From 99604f16859f7d5c59d45d05425851ca52d9d5f1 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sat, 15 Aug 2026 12:29:13 -0700 Subject: [PATCH] Discard nonce prefix `z` as sensitive material unnecessary for public key derivation. Document reason for clamping scalar. --- src/assembly/index.ts | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/assembly/index.ts b/src/assembly/index.ts index 549d116..be6705e 100644 --- a/src/assembly/index.ts +++ b/src/assembly/index.ts @@ -21,6 +21,13 @@ const MESSAGE_BUFFER = memory.data(32768) // crypto_hash function const blake2b = new Blake2b() +/** + * Clears the 3 least significant bits to guarantee multiple of 8 and prevent + * small-subgroup attacks based on Curve25519's cofactor of 8. Also sets bit 254 + * and clears bit 255 to guarantee a fixed bit length for constant time + * performance. + * @param k 32-byte scalar + */ //@ts-expect-error @inline function clamp (k: StaticArray): void { @@ -29,14 +36,23 @@ function clamp (k: StaticArray): void { k[31] |= 64 } -const crypto_derive_pk: StaticArray = new StaticArray(PUBLICKEY_BYTES) -function crypto_derive (sk: StaticArray, seed: StaticArray): void { - const pk = crypto_derive_pk - blake2b.init().update(changetype(seed), 32).digest(sk) - clamp(sk) - - ge_scalarmult_base_tobytes(pk, sk) - memory.copy(changetype(sk), changetype(pk), PUBLICKEY_BYTES) +const crypto_derive_a: StaticArray = new StaticArray(PRIVATEKEY_BYTES) +const crypto_derive_az: StaticArray = new StaticArray(SECRETKEY_BYTES) +/** + * Hash seed to 32-byte scalar `a`, clamp it, then point-multiply it by the + * Ed25519 base point. + * @param pk 32-byte output buffer for the compressed public key + * @param seed 32-byte input buffer for the securely-random secret seed value + */ +function crypto_derive (pk: StaticArray, seed: StaticArray): void { + const a = crypto_derive_a + const az = crypto_derive_az + blake2b.init().update(changetype(seed), 32).digest(az) + memory.copy(changetype(a), changetype(az), 32) + az.fill(0) + clamp(a) + ge_scalarmult_base_tobytes(pk, a) + a.fill(0) } const crypto_sign_az = new StaticArray(64) -- 2.52.0