commit 7c0b42ba0c8a52018ad6c5c498786bc560fcc50c (tree)
parent 27039a014c591aff8ab50fd3e757a803bc116e8e
Author: Andrew Kelley <andrew@ziglang.org>
Date: Fri, 9 Jan 2026 09:38:44 +0100
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>
Diffstat:
1 file changed, 23 insertions(+), 11 deletions(-)
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig
@@ -385,15 +385,15 @@ pub const Ed25519 = struct {
);
}
- /// Create a Signer, that can be used for incremental signing.
- /// Note that the signature is not deterministic.
- pub fn signer(
+ /// 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,
- /// If set, should be something unique for each message, such as a
- /// random nonce, or a counter.
+ base_nonce: [32]u8,
+ /// If set, should be something unique for each message, such as a counter.
noise: ?[noise_length]u8,
- /// Filled with cryptographically secure randomness.
- entropy: *const [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;
@@ -401,7 +401,7 @@ pub const Ed25519 = struct {
const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
var h = Sha512.init(.{});
h.update(&scalar_and_prefix.prefix);
- h.update(entropy);
+ h.update(&base_nonce);
if (noise) |*z| {
h.update(z);
}
@@ -411,6 +411,20 @@ pub const Ed25519 = struct {
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(
+ key_pair: KeyPair,
+ /// If set, should be something unique for each message, such as a
+ /// random nonce, or a counter.
+ noise: ?[noise_length]u8,
+ io: std.Io,
+ ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
+ var base_nonce: [32]u8 = undefined;
+ io.random(&base_nonce);
+ return key_pair.signerWithBaseNonce(base_nonce, noise);
+ }
};
/// A (signature, message, public_key) tuple for batch verification
@@ -748,9 +762,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();