authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2024-11-19 18:05:09+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-11-19 18:05:09+01:00
log8a00bd4ce6a3f7f61dc93ed8fabfee47f32dadc5
tree4645d6413534443546a3e9dda122614156942157
parent94be75a94fe643d8424c0225c63a2a60f12a97a0
signaturebadge-check Signed by PGP key B5690EEEBB952194

std.crypto: make the key pair API creation consistent (#21955)

Our key pair creation API was ugly and inconsistent between ecdsa keys and other keys. The same `generate()` function can now be used to generate key pairs, and that function cannot fail. For deterministic keys, a `generateDeterministic()` function is available for all key types. Fix comments and compilation of the benchmark by the way. Fixes #21002

7 files changed, 102 insertions(+), 66 deletions(-)

lib/std/crypto/25519/ed25519.zig+29-17
...@@ -245,7 +245,9 @@ pub const Ed25519 = struct {...@@ -245,7 +245,9 @@ pub const Ed25519 = struct {
245 /// Secret scalar.245 /// Secret scalar.
246 secret_key: SecretKey,246 secret_key: SecretKey,
247247
248 /// Derive a key pair from an optional secret seed.248 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
249 ///
250 /// Except in tests, applications should generally call `generate()` instead of this function.
249 ///251 ///
250 /// As in RFC 8032, an Ed25519 public key is generated by hashing252 /// As in RFC 8032, an Ed25519 public key is generated by hashing
251 /// the secret key using the SHA-512 function, and interpreting the253 /// the secret key using the SHA-512 function, and interpreting the
...@@ -253,20 +255,15 @@ pub const Ed25519 = struct {...@@ -253,20 +255,15 @@ pub const Ed25519 = struct {
253 ///255 ///
254 /// For this reason, an EdDSA secret key is commonly called a seed,256 /// For this reason, an EdDSA secret key is commonly called a seed,
255 /// from which the actual secret is derived.257 /// from which the actual secret is derived.
256 pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair {258 pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
257 const ss = seed orelse ss: {
258 var random_seed: [seed_length]u8 = undefined;
259 crypto.random.bytes(&random_seed);
260 break :ss random_seed;
261 };
262 var az: [Sha512.digest_length]u8 = undefined;259 var az: [Sha512.digest_length]u8 = undefined;
263 var h = Sha512.init(.{});260 var h = Sha512.init(.{});
264 h.update(&ss);261 h.update(&seed);
265 h.final(&az);262 h.final(&az);
266 const pk_p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement;263 const pk_p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement;
267 const pk_bytes = pk_p.toBytes();264 const pk_bytes = pk_p.toBytes();
268 var sk_bytes: [SecretKey.encoded_length]u8 = undefined;265 var sk_bytes: [SecretKey.encoded_length]u8 = undefined;
269 sk_bytes[0..ss.len].* = ss;266 sk_bytes[0..seed_length].* = seed;
270 sk_bytes[seed_length..].* = pk_bytes;267 sk_bytes[seed_length..].* = pk_bytes;
271 return KeyPair{268 return KeyPair{
272 .public_key = PublicKey.fromBytes(pk_bytes) catch unreachable,269 .public_key = PublicKey.fromBytes(pk_bytes) catch unreachable,
...@@ -274,7 +271,22 @@ pub const Ed25519 = struct {...@@ -274,7 +271,22 @@ pub const Ed25519 = struct {
274 };271 };
275 }272 }
276273
277 /// Create a KeyPair from a secret key.274 /// Generate a new, random key pair.
275 ///
276 /// `crypto.random.bytes` must be supported by the target.
277 pub fn generate() KeyPair {
278 var random_seed: [seed_length]u8 = undefined;
279 while (true) {
280 crypto.random.bytes(&random_seed);
281 return generateDeterministic(random_seed) catch {
282 @branchHint(.unlikely);
283 continue;
284 };
285 }
286 }
287
288 /// Create a key pair from an existing secret key.
289 ///
278 /// Note that with EdDSA, storing the seed, and recovering the key pair290 /// Note that with EdDSA, storing the seed, and recovering the key pair
279 /// from it is recommended over storing the entire secret key.291 /// from it is recommended over storing the entire secret key.
280 /// The seed of an exiting key pair can be obtained with292 /// The seed of an exiting key pair can be obtained with
...@@ -285,7 +297,7 @@ pub const Ed25519 = struct {...@@ -285,7 +297,7 @@ pub const Ed25519 = struct {
285 // With runtime safety, we can still afford checking that the public key is correct.297 // With runtime safety, we can still afford checking that the public key is correct.
286 if (std.debug.runtime_safety) {298 if (std.debug.runtime_safety) {
287 const pk_p = try Curve.fromBytes(secret_key.publicKeyBytes());299 const pk_p = try Curve.fromBytes(secret_key.publicKeyBytes());
288 const recomputed_kp = try create(secret_key.seed());300 const recomputed_kp = try generateDeterministic(secret_key.seed());
289 debug.assert(mem.eql(u8, &recomputed_kp.public_key.toBytes(), &pk_p.toBytes()));301 debug.assert(mem.eql(u8, &recomputed_kp.public_key.toBytes(), &pk_p.toBytes()));
290 }302 }
291 return KeyPair{303 return KeyPair{
...@@ -492,7 +504,7 @@ pub const Ed25519 = struct {...@@ -492,7 +504,7 @@ pub const Ed25519 = struct {
492test "key pair creation" {504test "key pair creation" {
493 var seed: [32]u8 = undefined;505 var seed: [32]u8 = undefined;
494 _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");506 _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
495 const key_pair = try Ed25519.KeyPair.create(seed);507 const key_pair = try Ed25519.KeyPair.generateDeterministic(seed);
496 var buf: [256]u8 = undefined;508 var buf: [256]u8 = undefined;
497 try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.secret_key.toBytes())}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");509 try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.secret_key.toBytes())}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
498 try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.public_key.toBytes())}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");510 try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.public_key.toBytes())}), "2D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
...@@ -501,7 +513,7 @@ test "key pair creation" {...@@ -501,7 +513,7 @@ test "key pair creation" {
501test "signature" {513test "signature" {
502 var seed: [32]u8 = undefined;514 var seed: [32]u8 = undefined;
503 _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");515 _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
504 const key_pair = try Ed25519.KeyPair.create(seed);516 const key_pair = try Ed25519.KeyPair.generateDeterministic(seed);
505517
506 const sig = try key_pair.sign("test", null);518 const sig = try key_pair.sign("test", null);
507 var buf: [128]u8 = undefined;519 var buf: [128]u8 = undefined;
...@@ -513,7 +525,7 @@ test "signature" {...@@ -513,7 +525,7 @@ test "signature" {
513test "batch verification" {525test "batch verification" {
514 var i: usize = 0;526 var i: usize = 0;
515 while (i < 100) : (i += 1) {527 while (i < 100) : (i += 1) {
516 const key_pair = try Ed25519.KeyPair.create(null);528 const key_pair = Ed25519.KeyPair.generate();
517 var msg1: [32]u8 = undefined;529 var msg1: [32]u8 = undefined;
518 var msg2: [32]u8 = undefined;530 var msg2: [32]u8 = undefined;
519 crypto.random.bytes(&msg1);531 crypto.random.bytes(&msg1);
...@@ -645,7 +657,7 @@ test "with blind keys" {...@@ -645,7 +657,7 @@ test "with blind keys" {
645 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;657 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;
646658
647 // Create a standard Ed25519 key pair659 // Create a standard Ed25519 key pair
648 const kp = try Ed25519.KeyPair.create(null);660 const kp = Ed25519.KeyPair.generate();
649661
650 // Create a random blinding seed662 // Create a random blinding seed
651 var blind: [32]u8 = undefined;663 var blind: [32]u8 = undefined;
...@@ -665,7 +677,7 @@ test "with blind keys" {...@@ -665,7 +677,7 @@ test "with blind keys" {
665}677}
666678
667test "signatures with streaming" {679test "signatures with streaming" {
668 const kp = try Ed25519.KeyPair.create(null);680 const kp = Ed25519.KeyPair.generate();
669681
670 var signer = try kp.signer(null);682 var signer = try kp.signer(null);
671 signer.update("mes");683 signer.update("mes");
...@@ -681,7 +693,7 @@ test "signatures with streaming" {...@@ -681,7 +693,7 @@ test "signatures with streaming" {
681}693}
682694
683test "key pair from secret key" {695test "key pair from secret key" {
684 const kp = try Ed25519.KeyPair.create(null);696 const kp = Ed25519.KeyPair.generate();
685 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);697 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);
686 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());698 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());
687 try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());699 try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());
lib/std/crypto/25519/x25519.zig+20-10
...@@ -29,19 +29,29 @@ pub const X25519 = struct {...@@ -29,19 +29,29 @@ pub const X25519 = struct {
29 /// Secret part.29 /// Secret part.
30 secret_key: [secret_length]u8,30 secret_key: [secret_length]u8,
3131
32 /// Create a new key pair using an optional seed.32 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
33 pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair {33 ///
34 const sk = seed orelse sk: {34 /// Except in tests, applications should generally call `generate()` instead of this function.
35 var random_seed: [seed_length]u8 = undefined;35 pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
36 crypto.random.bytes(&random_seed);36 const kp = KeyPair{
37 break :sk random_seed;37 .public_key = try X25519.recoverPublicKey(seed),
38 .secret_key = seed,
38 };39 };
39 var kp: KeyPair = undefined;
40 kp.secret_key = sk;
41 kp.public_key = try X25519.recoverPublicKey(sk);
42 return kp;40 return kp;
43 }41 }
4442
43 /// Generate a new, random key pair.
44 pub fn generate() KeyPair {
45 var random_seed: [seed_length]u8 = undefined;
46 while (true) {
47 crypto.random.bytes(&random_seed);
48 return generateDeterministic(random_seed) catch {
49 @branchHint(.unlikely);
50 continue;
51 };
52 }
53 }
54
45 /// Create a key pair from an Ed25519 key pair55 /// Create a key pair from an Ed25519 key pair
46 pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair {56 pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair {
47 const seed = ed25519_key_pair.secret_key.seed();57 const seed = ed25519_key_pair.secret_key.seed();
...@@ -171,7 +181,7 @@ test "rfc7748 1,000,000 iterations" {...@@ -171,7 +181,7 @@ test "rfc7748 1,000,000 iterations" {
171}181}
172182
173test "edwards25519 -> curve25519 map" {183test "edwards25519 -> curve25519 map" {
174 const ed_kp = try crypto.sign.Ed25519.KeyPair.create([_]u8{0x42} ** 32);184 const ed_kp = try crypto.sign.Ed25519.KeyPair.generateDeterministic([_]u8{0x42} ** 32);
175 const mont_kp = try X25519.KeyPair.fromEd25519(ed_kp);185 const mont_kp = try X25519.KeyPair.fromEd25519(ed_kp);
176 try htest.assertEqual("90e7595fc89e52fdfddce9c6a43d74dbf6047025ee0462d2d172e8b6a2841d6e", &mont_kp.secret_key);186 try htest.assertEqual("90e7595fc89e52fdfddce9c6a43d74dbf6047025ee0462d2d172e8b6a2841d6e", &mont_kp.secret_key);
177 try htest.assertEqual("cc4f2cdb695dd766f34118eb67b98652fed1d8bc49c330b119bbfa8a64989378", &mont_kp.public_key);187 try htest.assertEqual("cc4f2cdb695dd766f34118eb67b98652fed1d8bc49c330b119bbfa8a64989378", &mont_kp.public_key);
lib/std/crypto/benchmark.zig+7-7
...@@ -140,7 +140,7 @@ const signatures = [_]Crypto{...@@ -140,7 +140,7 @@ const signatures = [_]Crypto{
140140
141pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {141pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
142 const msg = [_]u8{0} ** 64;142 const msg = [_]u8{0} ** 64;
143 const key_pair = try Signature.KeyPair.create(null);143 const key_pair = Signature.KeyPair.generate();
144144
145 var timer = try Timer.start();145 var timer = try Timer.start();
146 const start = timer.lap();146 const start = timer.lap();
...@@ -163,7 +163,7 @@ const signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .na...@@ -163,7 +163,7 @@ const signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .na
163163
164pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {164pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
165 const msg = [_]u8{0} ** 64;165 const msg = [_]u8{0} ** 64;
166 const key_pair = try Signature.KeyPair.create(null);166 const key_pair = Signature.KeyPair.generate();
167 const sig = try key_pair.sign(&msg, null);167 const sig = try key_pair.sign(&msg, null);
168168
169 var timer = try Timer.start();169 var timer = try Timer.start();
...@@ -187,7 +187,7 @@ const batch_signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed2551...@@ -187,7 +187,7 @@ const batch_signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed2551
187187
188pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {188pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
189 const msg = [_]u8{0} ** 64;189 const msg = [_]u8{0} ** 64;
190 const key_pair = try Signature.KeyPair.create(null);190 const key_pair = Signature.KeyPair.generate();
191 const sig = try key_pair.sign(&msg, null);191 const sig = try key_pair.sign(&msg, null);
192192
193 var batch: [64]Signature.BatchElement = undefined;193 var batch: [64]Signature.BatchElement = undefined;
...@@ -219,7 +219,7 @@ const kems = [_]Crypto{...@@ -219,7 +219,7 @@ const kems = [_]Crypto{
219};219};
220220
221pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {221pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {
222 const key_pair = try Kem.KeyPair.create(null);222 const key_pair = Kem.KeyPair.generate();
223223
224 var timer = try Timer.start();224 var timer = try Timer.start();
225 const start = timer.lap();225 const start = timer.lap();
...@@ -239,7 +239,7 @@ pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u...@@ -239,7 +239,7 @@ pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u
239}239}
240240
241pub fn benchmarkKemDecaps(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {241pub fn benchmarkKemDecaps(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 {
242 const key_pair = try Kem.KeyPair.create(null);242 const key_pair = Kem.KeyPair.generate();
243243
244 const e = key_pair.public_key.encaps(null);244 const e = key_pair.public_key.encaps(null);
245245
...@@ -266,7 +266,7 @@ pub fn benchmarkKemKeyGen(comptime Kem: anytype, comptime kems_count: comptime_i...@@ -266,7 +266,7 @@ pub fn benchmarkKemKeyGen(comptime Kem: anytype, comptime kems_count: comptime_i
266 {266 {
267 var i: usize = 0;267 var i: usize = 0;
268 while (i < kems_count) : (i += 1) {268 while (i < kems_count) : (i += 1) {
269 const key_pair = try Kem.KeyPair.create(null);269 const key_pair = Kem.KeyPair.generate();
270 mem.doNotOptimizeAway(&key_pair);270 mem.doNotOptimizeAway(&key_pair);
271 }271 }
272 }272 }
...@@ -409,7 +409,7 @@ fn benchmarkPwhash(...@@ -409,7 +409,7 @@ fn benchmarkPwhash(
409 comptime count: comptime_int,409 comptime count: comptime_int,
410) !f64 {410) !f64 {
411 const password = "testpass" ** 2;411 const password = "testpass" ** 2;
412 const opts = .{412 const opts = ty.HashOptions{
413 .allocator = allocator,413 .allocator = allocator,
414 .params = @as(*const ty.Params, @ptrCast(@alignCast(params))).*,414 .params = @as(*const ty.Params, @ptrCast(@alignCast(params))).*,
415 .encoding = .phc,415 .encoding = .phc,
lib/std/crypto/ecdsa.zig+20-13
...@@ -296,21 +296,28 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -296,21 +296,28 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
296 /// Secret scalar.296 /// Secret scalar.
297 secret_key: SecretKey,297 secret_key: SecretKey,
298298
299 /// Create a new random key pair. `crypto.random.bytes` must be supported for the target.299 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
300 pub fn generate() IdentityElementError!KeyPair {300 ///
301 var random_seed: [seed_length]u8 = undefined;301 /// Except in tests, applications should generally call `generate()` instead of this function.
302 crypto.random.bytes(&random_seed);302 pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
303 return create(random_seed);
304 }
305
306 /// Create a new key pair. The seed must be secret and indistinguishable from random.
307 pub fn create(seed: [seed_length]u8) IdentityElementError!KeyPair {
308 const h = [_]u8{0x00} ** Hash.digest_length;303 const h = [_]u8{0x00} ** Hash.digest_length;
309 const k0 = [_]u8{0x01} ** SecretKey.encoded_length;304 const k0 = [_]u8{0x01} ** SecretKey.encoded_length;
310 const secret_key = deterministicScalar(h, k0, seed).toBytes(.big);305 const secret_key = deterministicScalar(h, k0, seed).toBytes(.big);
311 return fromSecretKey(SecretKey{ .bytes = secret_key });306 return fromSecretKey(SecretKey{ .bytes = secret_key });
312 }307 }
313308
309 /// Generate a new, random key pair.
310 pub fn generate() KeyPair {
311 var random_seed: [seed_length]u8 = undefined;
312 while (true) {
313 crypto.random.bytes(&random_seed);
314 return generateDeterministic(random_seed) catch {
315 @branchHint(.unlikely);
316 continue;
317 };
318 }
319 }
320
314 /// Return the public key corresponding to the secret key.321 /// Return the public key corresponding to the secret key.
315 pub fn fromSecretKey(secret_key: SecretKey) IdentityElementError!KeyPair {322 pub fn fromSecretKey(secret_key: SecretKey) IdentityElementError!KeyPair {
316 const public_key = try Curve.basePoint.mul(secret_key.bytes, .big);323 const public_key = try Curve.basePoint.mul(secret_key.bytes, .big);
...@@ -387,7 +394,7 @@ test "Basic operations over EcdsaP384Sha384" {...@@ -387,7 +394,7 @@ test "Basic operations over EcdsaP384Sha384" {
387 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;394 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
388395
389 const Scheme = EcdsaP384Sha384;396 const Scheme = EcdsaP384Sha384;
390 const kp = try Scheme.KeyPair.generate();397 const kp = Scheme.KeyPair.generate();
391 const msg = "test";398 const msg = "test";
392399
393 var noise: [Scheme.noise_length]u8 = undefined;400 var noise: [Scheme.noise_length]u8 = undefined;
...@@ -403,7 +410,7 @@ test "Basic operations over Secp256k1" {...@@ -403,7 +410,7 @@ test "Basic operations over Secp256k1" {
403 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;410 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
404411
405 const Scheme = EcdsaSecp256k1Sha256oSha256;412 const Scheme = EcdsaSecp256k1Sha256oSha256;
406 const kp = try Scheme.KeyPair.generate();413 const kp = Scheme.KeyPair.generate();
407 const msg = "test";414 const msg = "test";
408415
409 var noise: [Scheme.noise_length]u8 = undefined;416 var noise: [Scheme.noise_length]u8 = undefined;
...@@ -419,7 +426,7 @@ test "Basic operations over EcdsaP384Sha256" {...@@ -419,7 +426,7 @@ test "Basic operations over EcdsaP384Sha256" {
419 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;426 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
420427
421 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);428 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
422 const kp = try Scheme.KeyPair.generate();429 const kp = Scheme.KeyPair.generate();
423 const msg = "test";430 const msg = "test";
424431
425 var noise: [Scheme.noise_length]u8 = undefined;432 var noise: [Scheme.noise_length]u8 = undefined;
...@@ -893,7 +900,7 @@ test "Sec1 encoding/decoding" {...@@ -893,7 +900,7 @@ test "Sec1 encoding/decoding" {
893 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;900 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
894901
895 const Scheme = EcdsaP384Sha384;902 const Scheme = EcdsaP384Sha384;
896 const kp = try Scheme.KeyPair.generate();903 const kp = Scheme.KeyPair.generate();
897 const pk = kp.public_key;904 const pk = kp.public_key;
898 const pk_compressed_sec1 = pk.toCompressedSec1();905 const pk_compressed_sec1 = pk.toCompressedSec1();
899 const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);906 const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);
lib/std/crypto/ml_kem.zig+18-11
...@@ -370,15 +370,10 @@ fn Kyber(comptime p: Params) type {...@@ -370,15 +370,10 @@ fn Kyber(comptime p: Params) type {
370 secret_key: SecretKey,370 secret_key: SecretKey,
371 public_key: PublicKey,371 public_key: PublicKey,
372372
373 /// Create a new key pair.373 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
374 /// If seed is null, a random seed will be generated.374 ///
375 /// If a seed is provided, the key pair will be deterministic.375 /// Except in tests, applications should generally call `generate()` instead of this function.
376 pub fn create(seed_: ?[seed_length]u8) !KeyPair {376 pub fn generateDeterministic(seed: [seed_length]u8) !KeyPair {
377 const seed = seed_ orelse sk: {
378 var random_seed: [seed_length]u8 = undefined;
379 crypto.random.bytes(&random_seed);
380 break :sk random_seed;
381 };
382 var ret: KeyPair = undefined;377 var ret: KeyPair = undefined;
383 ret.secret_key.z = seed[inner_seed_length..seed_length].*;378 ret.secret_key.z = seed[inner_seed_length..seed_length].*;
384379
...@@ -399,6 +394,18 @@ fn Kyber(comptime p: Params) type {...@@ -399,6 +394,18 @@ fn Kyber(comptime p: Params) type {
399394
400 return ret;395 return ret;
401 }396 }
397
398 /// Generate a new, random key pair.
399 pub fn generate() KeyPair {
400 var random_seed: [seed_length]u8 = undefined;
401 while (true) {
402 crypto.random.bytes(&random_seed);
403 return generateDeterministic(random_seed) catch {
404 @branchHint(.unlikely);
405 continue;
406 };
407 }
408 }
402 };409 };
403410
404 // Size of plaintexts of the in411 // Size of plaintexts of the in
...@@ -1698,7 +1705,7 @@ test "Test happy flow" {...@@ -1698,7 +1705,7 @@ test "Test happy flow" {
1698 inline for (modes) |mode| {1705 inline for (modes) |mode| {
1699 for (0..10) |i| {1706 for (0..10) |i| {
1700 seed[0] = @as(u8, @intCast(i));1707 seed[0] = @as(u8, @intCast(i));
1701 const kp = try mode.KeyPair.create(seed);1708 const kp = try mode.KeyPair.generateDeterministic(seed);
1702 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());1709 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());
1703 try testing.expectEqual(sk, kp.secret_key);1710 try testing.expectEqual(sk, kp.secret_key);
1704 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());1711 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());
...@@ -1745,7 +1752,7 @@ test "NIST KAT test" {...@@ -1745,7 +1752,7 @@ test "NIST KAT test" {
1745 g2.fill(kseed[0..32]);1752 g2.fill(kseed[0..32]);
1746 g2.fill(kseed[32..64]);1753 g2.fill(kseed[32..64]);
1747 g2.fill(&eseed);1754 g2.fill(&eseed);
1748 const kp = try mode.KeyPair.create(kseed);1755 const kp = try mode.KeyPair.generateDeterministic(kseed);
1749 const e = kp.public_key.encaps(eseed);1756 const e = kp.public_key.encaps(eseed);
1750 const ss2 = try kp.secret_key.decaps(&e.ciphertext);1757 const ss2 = try kp.secret_key.decaps(&e.ciphertext);
1751 try testing.expectEqual(ss2, e.shared_secret);1758 try testing.expectEqual(ss2, e.shared_secret);
lib/std/crypto/salsa20.zig+4-4
...@@ -535,7 +535,7 @@ pub const SealedBox = struct {...@@ -535,7 +535,7 @@ pub const SealedBox = struct {
535 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.535 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.
536 pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {536 pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {
537 debug.assert(c.len == m.len + seal_length);537 debug.assert(c.len == m.len + seal_length);
538 var ekp = try KeyPair.create(null);538 var ekp = KeyPair.generate();
539 const nonce = createNonce(ekp.public_key, public_key);539 const nonce = createNonce(ekp.public_key, public_key);
540 c[0..public_length].* = ekp.public_key;540 c[0..public_length].* = ekp.public_key;
541 try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key);541 try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key);
...@@ -607,8 +607,8 @@ test "xsalsa20poly1305 box" {...@@ -607,8 +607,8 @@ test "xsalsa20poly1305 box" {
607 crypto.random.bytes(&msg);607 crypto.random.bytes(&msg);
608 crypto.random.bytes(&nonce);608 crypto.random.bytes(&nonce);
609609
610 const kp1 = try Box.KeyPair.create(null);610 const kp1 = Box.KeyPair.generate();
611 const kp2 = try Box.KeyPair.create(null);611 const kp2 = Box.KeyPair.generate();
612 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);612 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);
613 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);613 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);
614}614}
...@@ -619,7 +619,7 @@ test "xsalsa20poly1305 sealedbox" {...@@ -619,7 +619,7 @@ test "xsalsa20poly1305 sealedbox" {
619 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;619 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
620 crypto.random.bytes(&msg);620 crypto.random.bytes(&msg);
621621
622 const kp = try Box.KeyPair.create(null);622 const kp = Box.KeyPair.generate();
623 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);623 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);
624 try SealedBox.open(msg2[0..], boxed[0..], kp);624 try SealedBox.open(msg2[0..], boxed[0..], kp);
625}625}
lib/std/crypto/tls/Client.zig+4-4
...@@ -1649,10 +1649,10 @@ const KeyShare = struct {...@@ -1649,10 +1649,10 @@ const KeyShare = struct {
16491649
1650 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {1650 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {
1651 return .{1651 return .{
1652 .ml_kem768_kp = try .create(null),1652 .ml_kem768_kp = .generate(),
1653 .secp256r1_kp = try .create(seed[0..32].*),1653 .secp256r1_kp = try .generateDeterministic(seed[0..32].*),
1654 .secp384r1_kp = try .create(seed[32..80].*),1654 .secp384r1_kp = try .generateDeterministic(seed[32..80].*),
1655 .x25519_kp = try .create(seed[80..112].*),1655 .x25519_kp = try .generateDeterministic(seed[80..112].*),
1656 .sk_buf = undefined,1656 .sk_buf = undefined,
1657 .sk_len = 0,1657 .sk_len = 0,
1658 };1658 };