| author | |
| committer | |
| log | 8a00bd4ce6a3f7f61dc93ed8fabfee47f32dadc5 |
| tree | 4645d6413534443546a3e9dda122614156942157 |
| parent | 94be75a94fe643d8424c0225c63a2a60f12a97a0 |
| signature |
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 #210027 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, |
| 247 | 247 | ||
| 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 hashing | 252 | /// As in RFC 8032, an Ed25519 public key is generated by hashing |
| 251 | /// the secret key using the SHA-512 function, and interpreting the | 253 | /// 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 | } |
| 276 | 273 | ||
| 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 pair | 290 | /// 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 with | 292 | /// 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 { |
| 492 | test "key pair creation" { | 504 | test "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" { |
| 501 | test "signature" { | 513 | test "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); |
| 505 | 517 | ||
| 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" { |
| 513 | test "batch verification" { | 525 | test "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; |
| 646 | 658 | ||
| 647 | // Create a standard Ed25519 key pair | 659 | // Create a standard Ed25519 key pair |
| 648 | const kp = try Ed25519.KeyPair.create(null); | 660 | const kp = Ed25519.KeyPair.generate(); |
| 649 | 661 | ||
| 650 | // Create a random blinding seed | 662 | // 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 | } |
| 666 | 678 | ||
| 667 | test "signatures with streaming" { | 679 | test "signatures with streaming" { |
| 668 | const kp = try Ed25519.KeyPair.create(null); | 680 | const kp = Ed25519.KeyPair.generate(); |
| 669 | 681 | ||
| 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 | } |
| 682 | 694 | ||
| 683 | test "key pair from secret key" { | 695 | test "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, |
| 31 | 31 | ||
| 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 | } |
| 44 | 42 | ||
| 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 pair | 55 | /// 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 | } |
| 172 | 182 | ||
| 173 | test "edwards25519 -> curve25519 map" { | 183 | test "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{ |
| 140 | 140 | ||
| 141 | pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { | 141 | pub 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(); |
| 144 | 144 | ||
| 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 |
| 163 | 163 | ||
| 164 | pub fn benchmarkSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { | 164 | pub 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); |
| 168 | 168 | ||
| 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 |
| 187 | 187 | ||
| 188 | pub fn benchmarkBatchSignatureVerification(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { | 188 | pub 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); |
| 192 | 192 | ||
| 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 | }; |
| 220 | 220 | ||
| 221 | pub fn benchmarkKem(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 { | 221 | pub 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(); |
| 223 | 223 | ||
| 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 | } |
| 240 | 240 | ||
| 241 | pub fn benchmarkKemDecaps(comptime Kem: anytype, comptime kems_count: comptime_int) !u64 { | 241 | pub 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(); |
| 243 | 243 | ||
| 244 | const e = key_pair.public_key.encaps(null); | 244 | const e = key_pair.public_key.encaps(null); |
| 245 | 245 | ||
| ... | @@ -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, |
| 298 | 298 | ||
| 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 | } |
| 313 | 308 | ||
| 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; |
| 388 | 395 | ||
| 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"; |
| 392 | 399 | ||
| 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; |
| 404 | 411 | ||
| 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"; |
| 408 | 415 | ||
| 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; |
| 420 | 427 | ||
| 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"; |
| 424 | 431 | ||
| 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; |
| 894 | 901 | ||
| 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, |
| 372 | 372 | ||
| 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].*; |
| 384 | 379 | ||
| ... | @@ -399,6 +394,18 @@ fn Kyber(comptime p: Params) type { | ... | @@ -399,6 +394,18 @@ fn Kyber(comptime p: Params) type { |
| 399 | 394 | ||
| 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 | }; |
| 403 | 410 | ||
| 404 | // Size of plaintexts of the in | 411 | // 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); |
| 609 | 609 | ||
| 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); |
| 621 | 621 | ||
| 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 { |
| 1649 | 1649 | ||
| 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 | }; |