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 {
245245 /// Secret scalar.
246246 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.
249251 ///
250252 /// As in RFC 8032, an Ed25519 public key is generated by hashing
251253 /// the secret key using the SHA-512 function, and interpreting the
......@@ -253,20 +255,15 @@ pub const Ed25519 = struct {
253255 ///
254256 /// For this reason, an EdDSA secret key is commonly called a seed,
255257 /// from which the actual secret is derived.
256 pub fn create(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 };
258 pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
262259 var az: [Sha512.digest_length]u8 = undefined;
263260 var h = Sha512.init(.{});
264 h.update(&ss);
261 h.update(&seed);
265262 h.final(&az);
266263 const pk_p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement;
267264 const pk_bytes = pk_p.toBytes();
268265 var sk_bytes: [SecretKey.encoded_length]u8 = undefined;
269 sk_bytes[0..ss.len].* = ss;
266 sk_bytes[0..seed_length].* = seed;
270267 sk_bytes[seed_length..].* = pk_bytes;
271268 return KeyPair{
272269 .public_key = PublicKey.fromBytes(pk_bytes) catch unreachable,
......@@ -274,7 +271,22 @@ pub const Ed25519 = struct {
274271 };
275272 }
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 ///
278290 /// Note that with EdDSA, storing the seed, and recovering the key pair
279291 /// from it is recommended over storing the entire secret key.
280292 /// The seed of an exiting key pair can be obtained with
......@@ -285,7 +297,7 @@ pub const Ed25519 = struct {
285297 // With runtime safety, we can still afford checking that the public key is correct.
286298 if (std.debug.runtime_safety) {
287299 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());
289301 debug.assert(mem.eql(u8, &recomputed_kp.public_key.toBytes(), &pk_p.toBytes()));
290302 }
291303 return KeyPair{
......@@ -492,7 +504,7 @@ pub const Ed25519 = struct {
492504test "key pair creation" {
493505 var seed: [32]u8 = undefined;
494506 _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
495 const key_pair = try Ed25519.KeyPair.create(seed);
507 const key_pair = try Ed25519.KeyPair.generateDeterministic(seed);
496508 var buf: [256]u8 = undefined;
497509 try std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{s}", .{std.fmt.fmtSliceHexUpper(&key_pair.secret_key.toBytes())}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083");
498510 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" {
501513test "signature" {
502514 var seed: [32]u8 = undefined;
503515 _ = 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
506518 const sig = try key_pair.sign("test", null);
507519 var buf: [128]u8 = undefined;
......@@ -513,7 +525,7 @@ test "signature" {
513525test "batch verification" {
514526 var i: usize = 0;
515527 while (i < 100) : (i += 1) {
516 const key_pair = try Ed25519.KeyPair.create(null);
528 const key_pair = Ed25519.KeyPair.generate();
517529 var msg1: [32]u8 = undefined;
518530 var msg2: [32]u8 = undefined;
519531 crypto.random.bytes(&msg1);
......@@ -645,7 +657,7 @@ test "with blind keys" {
645657 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;
646658
647659 // Create a standard Ed25519 key pair
648 const kp = try Ed25519.KeyPair.create(null);
660 const kp = Ed25519.KeyPair.generate();
649661
650662 // Create a random blinding seed
651663 var blind: [32]u8 = undefined;
......@@ -665,7 +677,7 @@ test "with blind keys" {
665677}
666678
667679test "signatures with streaming" {
668 const kp = try Ed25519.KeyPair.create(null);
680 const kp = Ed25519.KeyPair.generate();
669681
670682 var signer = try kp.signer(null);
671683 signer.update("mes");
......@@ -681,7 +693,7 @@ test "signatures with streaming" {
681693}
682694
683695test "key pair from secret key" {
684 const kp = try Ed25519.KeyPair.create(null);
696 const kp = Ed25519.KeyPair.generate();
685697 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);
686698 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());
687699 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 {
2929 /// Secret part.
3030 secret_key: [secret_length]u8,
3131
32 /// Create a new key pair using an optional seed.
33 pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair {
34 const sk = seed orelse sk: {
35 var random_seed: [seed_length]u8 = undefined;
36 crypto.random.bytes(&random_seed);
37 break :sk random_seed;
32 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
33 ///
34 /// Except in tests, applications should generally call `generate()` instead of this function.
35 pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
36 const kp = KeyPair{
37 .public_key = try X25519.recoverPublicKey(seed),
38 .secret_key = seed,
3839 };
39 var kp: KeyPair = undefined;
40 kp.secret_key = sk;
41 kp.public_key = try X25519.recoverPublicKey(sk);
4240 return kp;
4341 }
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
4555 /// Create a key pair from an Ed25519 key pair
4656 pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair {
4757 const seed = ed25519_key_pair.secret_key.seed();
......@@ -171,7 +181,7 @@ test "rfc7748 1,000,000 iterations" {
171181}
172182
173183test "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);
175185 const mont_kp = try X25519.KeyPair.fromEd25519(ed_kp);
176186 try htest.assertEqual("90e7595fc89e52fdfddce9c6a43d74dbf6047025ee0462d2d172e8b6a2841d6e", &mont_kp.secret_key);
177187 try htest.assertEqual("cc4f2cdb695dd766f34118eb67b98652fed1d8bc49c330b119bbfa8a64989378", &mont_kp.public_key);
lib/std/crypto/benchmark.zig+7-7
......@@ -140,7 +140,7 @@ const signatures = [_]Crypto{
140140
141141pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
142142 const msg = [_]u8{0} ** 64;
143 const key_pair = try Signature.KeyPair.create(null);
143 const key_pair = Signature.KeyPair.generate();
144144
145145 var timer = try Timer.start();
146146 const start = timer.lap();
......@@ -163,7 +163,7 @@ const signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .na
163163
164164pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
165165 const msg = [_]u8{0} ** 64;
166 const key_pair = try Signature.KeyPair.create(null);
166 const key_pair = Signature.KeyPair.generate();
167167 const sig = try key_pair.sign(&msg, null);
168168
169169 var timer = try Timer.start();
......@@ -187,7 +187,7 @@ const batch_signature_verifications = [_]Crypto{Crypto{ .ty = crypto.sign.Ed2551
187187
188188pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
189189 const msg = [_]u8{0} ** 64;
190 const key_pair = try Signature.KeyPair.create(null);
190 const key_pair = Signature.KeyPair.generate();
191191 const sig = try key_pair.sign(&msg, null);
192192
193193 var batch: [64]Signature.BatchElement = undefined;
......@@ -219,7 +219,7 @@ const kems = [_]Crypto{
219219};
220220
221221pub 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
224224 var timer = try Timer.start();
225225 const start = timer.lap();
......@@ -239,7 +239,7 @@ pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u
239239}
240240
241241pub 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
244244 const e = key_pair.public_key.encaps(null);
245245
......@@ -266,7 +266,7 @@ pub fn benchmarkKemKeyGen(comptime Kem: anytype, comptime kems_count: comptime_i
266266 {
267267 var i: usize = 0;
268268 while (i < kems_count) : (i += 1) {
269 const key_pair = try Kem.KeyPair.create(null);
269 const key_pair = Kem.KeyPair.generate();
270270 mem.doNotOptimizeAway(&key_pair);
271271 }
272272 }
......@@ -409,7 +409,7 @@ fn benchmarkPwhash(
409409 comptime count: comptime_int,
410410) !f64 {
411411 const password = "testpass" ** 2;
412 const opts = .{
412 const opts = ty.HashOptions{
413413 .allocator = allocator,
414414 .params = @as(*const ty.Params, @ptrCast(@alignCast(params))).*,
415415 .encoding = .phc,
lib/std/crypto/ecdsa.zig+20-13
......@@ -296,21 +296,28 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
296296 /// Secret scalar.
297297 secret_key: SecretKey,
298298
299 /// Create a new random key pair. `crypto.random.bytes` must be supported for the target.
300 pub fn generate() IdentityElementError!KeyPair {
301 var random_seed: [seed_length]u8 = undefined;
302 crypto.random.bytes(&random_seed);
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 {
299 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
300 ///
301 /// Except in tests, applications should generally call `generate()` instead of this function.
302 pub fn generateDeterministic(seed: [seed_length]u8) IdentityElementError!KeyPair {
308303 const h = [_]u8{0x00} ** Hash.digest_length;
309304 const k0 = [_]u8{0x01} ** SecretKey.encoded_length;
310305 const secret_key = deterministicScalar(h, k0, seed).toBytes(.big);
311306 return fromSecretKey(SecretKey{ .bytes = secret_key });
312307 }
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
314321 /// Return the public key corresponding to the secret key.
315322 pub fn fromSecretKey(secret_key: SecretKey) IdentityElementError!KeyPair {
316323 const public_key = try Curve.basePoint.mul(secret_key.bytes, .big);
......@@ -387,7 +394,7 @@ test "Basic operations over EcdsaP384Sha384" {
387394 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
388395
389396 const Scheme = EcdsaP384Sha384;
390 const kp = try Scheme.KeyPair.generate();
397 const kp = Scheme.KeyPair.generate();
391398 const msg = "test";
392399
393400 var noise: [Scheme.noise_length]u8 = undefined;
......@@ -403,7 +410,7 @@ test "Basic operations over Secp256k1" {
403410 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
404411
405412 const Scheme = EcdsaSecp256k1Sha256oSha256;
406 const kp = try Scheme.KeyPair.generate();
413 const kp = Scheme.KeyPair.generate();
407414 const msg = "test";
408415
409416 var noise: [Scheme.noise_length]u8 = undefined;
......@@ -419,7 +426,7 @@ test "Basic operations over EcdsaP384Sha256" {
419426 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
420427
421428 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
422 const kp = try Scheme.KeyPair.generate();
429 const kp = Scheme.KeyPair.generate();
423430 const msg = "test";
424431
425432 var noise: [Scheme.noise_length]u8 = undefined;
......@@ -893,7 +900,7 @@ test "Sec1 encoding/decoding" {
893900 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
894901
895902 const Scheme = EcdsaP384Sha384;
896 const kp = try Scheme.KeyPair.generate();
903 const kp = Scheme.KeyPair.generate();
897904 const pk = kp.public_key;
898905 const pk_compressed_sec1 = pk.toCompressedSec1();
899906 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 {
370370 secret_key: SecretKey,
371371 public_key: PublicKey,
372372
373 /// Create a new key pair.
374 /// If seed is null, a random seed will be generated.
375 /// If a seed is provided, the key pair will be deterministic.
376 pub fn create(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 };
373 /// Deterministically derive a key pair from a cryptograpically secure secret seed.
374 ///
375 /// Except in tests, applications should generally call `generate()` instead of this function.
376 pub fn generateDeterministic(seed: [seed_length]u8) !KeyPair {
382377 var ret: KeyPair = undefined;
383378 ret.secret_key.z = seed[inner_seed_length..seed_length].*;
384379
......@@ -399,6 +394,18 @@ fn Kyber(comptime p: Params) type {
399394
400395 return ret;
401396 }
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 }
402409 };
403410
404411 // Size of plaintexts of the in
......@@ -1698,7 +1705,7 @@ test "Test happy flow" {
16981705 inline for (modes) |mode| {
16991706 for (0..10) |i| {
17001707 seed[0] = @as(u8, @intCast(i));
1701 const kp = try mode.KeyPair.create(seed);
1708 const kp = try mode.KeyPair.generateDeterministic(seed);
17021709 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());
17031710 try testing.expectEqual(sk, kp.secret_key);
17041711 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());
......@@ -1745,7 +1752,7 @@ test "NIST KAT test" {
17451752 g2.fill(kseed[0..32]);
17461753 g2.fill(kseed[32..64]);
17471754 g2.fill(&eseed);
1748 const kp = try mode.KeyPair.create(kseed);
1755 const kp = try mode.KeyPair.generateDeterministic(kseed);
17491756 const e = kp.public_key.encaps(eseed);
17501757 const ss2 = try kp.secret_key.decaps(&e.ciphertext);
17511758 try testing.expectEqual(ss2, e.shared_secret);
lib/std/crypto/salsa20.zig+4-4
......@@ -535,7 +535,7 @@ pub const SealedBox = struct {
535535 /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.
536536 pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {
537537 debug.assert(c.len == m.len + seal_length);
538 var ekp = try KeyPair.create(null);
538 var ekp = KeyPair.generate();
539539 const nonce = createNonce(ekp.public_key, public_key);
540540 c[0..public_length].* = ekp.public_key;
541541 try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key);
......@@ -607,8 +607,8 @@ test "xsalsa20poly1305 box" {
607607 crypto.random.bytes(&msg);
608608 crypto.random.bytes(&nonce);
609609
610 const kp1 = try Box.KeyPair.create(null);
611 const kp2 = try Box.KeyPair.create(null);
610 const kp1 = Box.KeyPair.generate();
611 const kp2 = Box.KeyPair.generate();
612612 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);
613613 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);
614614}
......@@ -619,7 +619,7 @@ test "xsalsa20poly1305 sealedbox" {
619619 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
620620 crypto.random.bytes(&msg);
621621
622 const kp = try Box.KeyPair.create(null);
622 const kp = Box.KeyPair.generate();
623623 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);
624624 try SealedBox.open(msg2[0..], boxed[0..], kp);
625625}
lib/std/crypto/tls/Client.zig+4-4
......@@ -1649,10 +1649,10 @@ const KeyShare = struct {
16491649
16501650 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {
16511651 return .{
1652 .ml_kem768_kp = try .create(null),
1653 .secp256r1_kp = try .create(seed[0..32].*),
1654 .secp384r1_kp = try .create(seed[32..80].*),
1655 .x25519_kp = try .create(seed[80..112].*),
1652 .ml_kem768_kp = .generate(),
1653 .secp256r1_kp = try .generateDeterministic(seed[0..32].*),
1654 .secp384r1_kp = try .generateDeterministic(seed[32..80].*),
1655 .x25519_kp = try .generateDeterministic(seed[80..112].*),
16561656 .sk_buf = undefined,
16571657 .sk_len = 0,
16581658 };