authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-09 09:38:44+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-09 09:38:44+01:00
log7c0b42ba0c8a52018ad6c5c498786bc560fcc50c
treebcccdeb8ecddccc508b1b9f4de4695c1c3627514
parent27039a014c591aff8ab50fd3e757a803bc116e8e
parentcd8f0aa4ca174ae1d43232d20f27120bcd74e71f

Merge pull request 'crypto.ed25519.Signer: get an std.io parameter rather than entropy' (#30736) from jedisct1/zig:edsigned into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30736 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

1 files changed, 23 insertions(+), 11 deletions(-)

lib/std/crypto/25519/ed25519.zig+23-11
...@@ -385,15 +385,15 @@ pub const Ed25519 = struct {...@@ -385,15 +385,15 @@ pub const Ed25519 = struct {
385 );385 );
386 }386 }
387387
388 /// Create a Signer, that can be used for incremental signing.388 /// Create a signer that can be used for incremental signing, using a custom base nonce.
389 /// Note that the signature is not deterministic.389 /// `base_nonce` must be unique for each signed message; otherwise, the secret key can
390 pub fn signer(390 /// be trivially recovered by an attacker.
391 /// It can be generated using a cryptographically secure random number generator.
392 pub fn signerWithBaseNonce(
391 key_pair: KeyPair,393 key_pair: KeyPair,
392 /// If set, should be something unique for each message, such as a394 base_nonce: [32]u8,
393 /// random nonce, or a counter.395 /// If set, should be something unique for each message, such as a counter.
394 noise: ?[noise_length]u8,396 noise: ?[noise_length]u8,
395 /// Filled with cryptographically secure randomness.
396 entropy: *const [noise_length]u8,
397 ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {397 ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
398 if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {398 if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
399 return error.KeyMismatch;399 return error.KeyMismatch;
...@@ -401,7 +401,7 @@ pub const Ed25519 = struct {...@@ -401,7 +401,7 @@ pub const Ed25519 = struct {
401 const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();401 const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
402 var h = Sha512.init(.{});402 var h = Sha512.init(.{});
403 h.update(&scalar_and_prefix.prefix);403 h.update(&scalar_and_prefix.prefix);
404 h.update(entropy);404 h.update(&base_nonce);
405 if (noise) |*z| {405 if (noise) |*z| {
406 h.update(z);406 h.update(z);
407 }407 }
...@@ -411,6 +411,20 @@ pub const Ed25519 = struct {...@@ -411,6 +411,20 @@ pub const Ed25519 = struct {
411411
412 return Signer.init(scalar_and_prefix.scalar, nonce, key_pair.public_key);412 return Signer.init(scalar_and_prefix.scalar, nonce, key_pair.public_key);
413 }413 }
414
415 /// Create a Signer, that can be used for incremental signing.
416 /// Note that the signature is not deterministic.
417 pub fn signer(
418 key_pair: KeyPair,
419 /// If set, should be something unique for each message, such as a
420 /// random nonce, or a counter.
421 noise: ?[noise_length]u8,
422 io: std.Io,
423 ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
424 var base_nonce: [32]u8 = undefined;
425 io.random(&base_nonce);
426 return key_pair.signerWithBaseNonce(base_nonce, noise);
427 }
414 };428 };
415429
416 /// A (signature, message, public_key) tuple for batch verification430 /// A (signature, message, public_key) tuple for batch verification
...@@ -748,9 +762,7 @@ test "signatures with streaming" {...@@ -748,9 +762,7 @@ test "signatures with streaming" {
748 const io = std.testing.io;762 const io = std.testing.io;
749 const kp = Ed25519.KeyPair.generate(io);763 const kp = Ed25519.KeyPair.generate(io);
750764
751 var entropy: [Ed25519.noise_length]u8 = undefined;765 var signer = try kp.signer(null, io);
752 io.random(&entropy);
753 var signer = try kp.signer(null, &entropy);
754 signer.update("mes");766 signer.update("mes");
755 signer.update("sage");767 signer.update("sage");
756 const sig = signer.finalize();768 const sig = signer.finalize();