mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 11:18:24 +00:00
fix(mls): check LeafNode lifetimes against the policy's clock
The vendored ts-mls KeyPackages are valid 2026-09-17 to 2026-10-03, and MlsGroup checked lifetimes against wall time, so CordnLifecycleInteropTest and KotlinArtifactProducerTest started failing on 2026-10-04 - and the pre-push hook with them, for every push. MlsGroupPolicy gains now(), defaulting to TimeUtils.now(); the two lifetime checks (UpdatePath leaves and Add proposals) read it. The fixture tests run under CordnGroupPolicy with the clock pinned inside the fixtures' window, since ts-mls signed those lifetimes and they cannot be extended here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S7FuNBSKiyVecARSoE4B9P
This commit is contained in:
@@ -2001,7 +2001,7 @@ class MlsGroup private constructor(
|
|||||||
// the signer authorized.
|
// the signer authorized.
|
||||||
val lifetime = updatePath.leafNode.lifetime
|
val lifetime = updatePath.leafNode.lifetime
|
||||||
if (lifetime != null) {
|
if (lifetime != null) {
|
||||||
val now = TimeUtils.now()
|
val now = policy.now()
|
||||||
require(now >= lifetime.notBefore && now <= lifetime.notAfter) {
|
require(now >= lifetime.notBefore && now <= lifetime.notAfter) {
|
||||||
"LeafNode lifetime expired or not yet valid in UpdatePath"
|
"LeafNode lifetime expired or not yet valid in UpdatePath"
|
||||||
}
|
}
|
||||||
@@ -2809,7 +2809,7 @@ class MlsGroup private constructor(
|
|||||||
// Validate lifetime
|
// Validate lifetime
|
||||||
val lifetime = leafNode.lifetime
|
val lifetime = leafNode.lifetime
|
||||||
if (lifetime != null) {
|
if (lifetime != null) {
|
||||||
val now = TimeUtils.now()
|
val now = policy.now()
|
||||||
require(now >= lifetime.notBefore && now <= lifetime.notAfter) {
|
require(now >= lifetime.notBefore && now <= lifetime.notAfter) {
|
||||||
"KeyPackage lifetime expired or not yet valid"
|
"KeyPackage lifetime expired or not yet valid"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.mls.group
|
|||||||
import com.vitorpamplona.quartz.mls.messages.CommitResult
|
import com.vitorpamplona.quartz.mls.messages.CommitResult
|
||||||
import com.vitorpamplona.quartz.mls.tree.Capabilities
|
import com.vitorpamplona.quartz.mls.tree.Capabilities
|
||||||
import com.vitorpamplona.quartz.mls.tree.Extension
|
import com.vitorpamplona.quartz.mls.tree.Extension
|
||||||
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The application's rules about who in a group may do what.
|
* The application's rules about who in a group may do what.
|
||||||
@@ -128,6 +129,14 @@ interface MlsGroupPolicy {
|
|||||||
*/
|
*/
|
||||||
val knownExtensionTypes: Set<Int> get() = emptySet()
|
val knownExtensionTypes: Set<Int> get() = emptySet()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The clock, in unix seconds, that LeafNode lifetimes (RFC 9420 §7.2) are
|
||||||
|
* checked against. Wall time in production; a test pins it to a moment
|
||||||
|
* inside a recorded fixture's validity window, so fixtures signed by
|
||||||
|
* another implementation don't expire out from under the suite.
|
||||||
|
*/
|
||||||
|
fun now(): Long = TimeUtils.now()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/** RFC 9420 exactly as written: any member may commit anything valid. */
|
/** RFC 9420 exactly as written: any member may commit anything valid. */
|
||||||
val Permissive: MlsGroupPolicy = object : MlsGroupPolicy {}
|
val Permissive: MlsGroupPolicy = object : MlsGroupPolicy {}
|
||||||
|
|||||||
+1
-1
@@ -260,7 +260,7 @@ class CordnLifecycleInteropTest {
|
|||||||
val group =
|
val group =
|
||||||
MlsGroup.create(
|
MlsGroup.create(
|
||||||
identity = CordnCredential.of(alice).identity,
|
identity = CordnCredential.of(alice).identity,
|
||||||
policy = CordnGroupPolicy,
|
policy = TsMlsFixtures.cordnPolicy,
|
||||||
initialExtensions = listOf(CordnGroupMetadata(name = "from Kotlin").toExtension()),
|
initialExtensions = listOf(CordnGroupMetadata(name = "from Kotlin").toExtension()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -83,7 +83,7 @@ class KotlinArtifactProducerTest {
|
|||||||
val group =
|
val group =
|
||||||
MlsGroup.create(
|
MlsGroup.create(
|
||||||
identity = CordnCredential.of(alice).identity,
|
identity = CordnCredential.of(alice).identity,
|
||||||
policy = CordnGroupPolicy,
|
policy = TsMlsFixtures.cordnPolicy,
|
||||||
initialExtensions = listOf(metadata.toExtension()),
|
initialExtensions = listOf(metadata.toExtension()),
|
||||||
)
|
)
|
||||||
write("k-alice.pk", alice)
|
write("k-alice.pk", alice)
|
||||||
|
|||||||
+15
@@ -20,9 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.quartz.cordn.interop
|
package com.vitorpamplona.quartz.cordn.interop
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.cordn.groups.CordnGroupPolicy
|
||||||
import com.vitorpamplona.quartz.mls.codec.TlsReader
|
import com.vitorpamplona.quartz.mls.codec.TlsReader
|
||||||
import com.vitorpamplona.quartz.mls.codec.TlsWriter
|
import com.vitorpamplona.quartz.mls.codec.TlsWriter
|
||||||
import com.vitorpamplona.quartz.mls.crypto.Ed25519
|
import com.vitorpamplona.quartz.mls.crypto.Ed25519
|
||||||
|
import com.vitorpamplona.quartz.mls.group.MlsGroupPolicy
|
||||||
import com.vitorpamplona.quartz.mls.messages.KeyPackageBundle
|
import com.vitorpamplona.quartz.mls.messages.KeyPackageBundle
|
||||||
import com.vitorpamplona.quartz.mls.messages.MlsKeyPackage
|
import com.vitorpamplona.quartz.mls.messages.MlsKeyPackage
|
||||||
import kotlin.io.encoding.Base64
|
import kotlin.io.encoding.Base64
|
||||||
@@ -43,6 +45,19 @@ object TsMlsFixtures {
|
|||||||
fun b64(name: String): ByteArray = Base64.decode(text(name))
|
fun b64(name: String): ByteArray = Base64.decode(text(name))
|
||||||
|
|
||||||
fun hex(name: String): ByteArray = text(name).chunked(2).map { it.toInt(16).toByte() }.toByteArray()
|
fun hex(name: String): ByteArray = text(name).chunked(2).map { it.toInt(16).toByte() }.toByteArray()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2026-09-21T09:46:40Z, inside the fixtures' KeyPackage lifetime
|
||||||
|
* (2026-09-17T03:34:16Z to 2026-10-03T08:34:16Z). ts-mls signed those
|
||||||
|
* lifetimes, so they cannot be extended here; the clock moves instead.
|
||||||
|
*/
|
||||||
|
const val NOW: Long = 1_790_000_000L
|
||||||
|
|
||||||
|
/** [CordnGroupPolicy] with its clock pinned to [NOW]. */
|
||||||
|
val cordnPolicy: MlsGroupPolicy =
|
||||||
|
object : MlsGroupPolicy by CordnGroupPolicy {
|
||||||
|
override fun now(): Long = NOW
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user