From 22380c78b1f9fd714ea6c89d2b902846ccf2e4f9 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 7 Jan 2026 23:02:24 +0100 Subject: [PATCH 1/2] crypto.ed25519.Signer: get an std.io parameter rather than entropy This is consistent with what all other similar functions now do, and is less dangerous. --- lib/std/crypto/25519/ed25519.zig | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index b6f582839e..b0c7216230 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -392,16 +392,17 @@ pub const Ed25519 = struct { /// If set, should be something unique for each message, such as a /// random nonce, or a counter. noise: ?[noise_length]u8, - /// Filled with cryptographically secure randomness. - entropy: *const [noise_length]u8, + io: std.Io, ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer { if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) { return error.KeyMismatch; } const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix(); + var entropy: [noise_length]u8 = undefined; + io.random(&entropy); var h = Sha512.init(.{}); h.update(&scalar_and_prefix.prefix); - h.update(entropy); + h.update(&entropy); if (noise) |*z| { h.update(z); } @@ -748,9 +749,7 @@ test "signatures with streaming" { const io = std.testing.io; const kp = Ed25519.KeyPair.generate(io); - var entropy: [Ed25519.noise_length]u8 = undefined; - io.random(&entropy); - var signer = try kp.signer(null, &entropy); + var signer = try kp.signer(null, io); signer.update("mes"); signer.update("sage"); const sig = signer.finalize(); From cd8f0aa4ca174ae1d43232d20f27120bcd74e71f Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 7 Jan 2026 23:02:46 +0100 Subject: [PATCH 2/2] Add signerWithBaseNonce --- lib/std/crypto/25519/ed25519.zig | 47 ++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index b0c7216230..a224c70d90 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -385,6 +385,33 @@ pub const Ed25519 = struct { ); } + /// Create a signer that can be used for incremental signing, using a custom base nonce. + /// `base_nonce` must be unique for each signed message; otherwise, the secret key can + /// be trivially recovered by an attacker. + /// It can be generated using a cryptographically secure random number generator. + pub fn signerWithBaseNonce( + key_pair: KeyPair, + base_nonce: [32]u8, + /// If set, should be something unique for each message, such as a counter. + noise: ?[noise_length]u8, + ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer { + if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) { + return error.KeyMismatch; + } + const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix(); + var h = Sha512.init(.{}); + h.update(&scalar_and_prefix.prefix); + h.update(&base_nonce); + if (noise) |*z| { + h.update(z); + } + var nonce64: [64]u8 = undefined; + h.final(&nonce64); + const nonce = Curve.scalar.reduce64(nonce64); + + return Signer.init(scalar_and_prefix.scalar, nonce, key_pair.public_key); + } + /// Create a Signer, that can be used for incremental signing. /// Note that the signature is not deterministic. pub fn signer( @@ -394,23 +421,9 @@ pub const Ed25519 = struct { noise: ?[noise_length]u8, io: std.Io, ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer { - if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) { - return error.KeyMismatch; - } - const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix(); - var entropy: [noise_length]u8 = undefined; - io.random(&entropy); - var h = Sha512.init(.{}); - h.update(&scalar_and_prefix.prefix); - h.update(&entropy); - if (noise) |*z| { - h.update(z); - } - var nonce64: [64]u8 = undefined; - h.final(&nonce64); - const nonce = Curve.scalar.reduce64(nonce64); - - return Signer.init(scalar_and_prefix.scalar, nonce, key_pair.public_key); + var base_nonce: [32]u8 = undefined; + io.random(&base_nonce); + return key_pair.signerWithBaseNonce(base_nonce, noise); } };