authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-05 20:42:37-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-07 11:03:36-08:00
log1f1381a866a110953f9950d8c0c6497a2567daa0
tree458e258f1ea98c691ebbd41b353c65b676b4ff8a
parent81a35a86ea9385016f371213a2b72b14902bb955

update API usage of std.crypto.random to io.random


45 files changed, 514 insertions(+), 336 deletions(-)

lib/compiler/aro/aro/Driver.zig+2-1
...@@ -1217,11 +1217,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u...@@ -1217,11 +1217,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u
1217}1217}
12181218
1219fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {1219fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {
1220 const io = d.comp.io;
1220 const random_bytes_count = 12;1221 const random_bytes_count = 12;
1221 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);1222 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
12221223
1223 var random_bytes: [random_bytes_count]u8 = undefined;1224 var random_bytes: [random_bytes_count]u8 = undefined;
1224 std.crypto.random.bytes(&random_bytes);1225 io.random(&random_bytes);
1225 var random_name: [sub_path_len]u8 = undefined;1226 var random_name: [sub_path_len]u8 = undefined;
1226 _ = std.fs.base64_encoder.encode(&random_name, &random_bytes);1227 _ = std.fs.base64_encoder.encode(&random_name, &random_bytes);
12271228
lib/std/Io/Threaded.zig+49-16
...@@ -78,7 +78,7 @@ pub const Csprng = struct {...@@ -78,7 +78,7 @@ pub const Csprng = struct {
78 pub const seed_len = std.Random.DefaultCsprng.secret_seed_length;78 pub const seed_len = std.Random.DefaultCsprng.secret_seed_length;
7979
80 pub fn isInitialized(c: *const Csprng) bool {80 pub fn isInitialized(c: *const Csprng) bool {
81 return c.rng.offset == std.math.maxInt(usize);81 return c.rng.offset != std.math.maxInt(usize);
82 }82 }
83};83};
8484
...@@ -2395,11 +2395,11 @@ fn dirCreateDirPath(...@@ -2395,11 +2395,11 @@ fn dirCreateDirPath(
2395 status = .created;2395 status = .created;
2396 } else |err| switch (err) {2396 } else |err| switch (err) {
2397 error.PathAlreadyExists => {2397 error.PathAlreadyExists => {
2398 // stat the file and return an error if it's not a directory2398 // It is important to return an error if it's not a directory
2399 // this is important because otherwise a dangling symlink2399 // because otherwise a dangling symlink could cause an infinite
2400 // could cause an infinite loop2400 // loop.
2401 const fstat = try dirStatFile(t, dir, component.path, .{});2401 const kind = try filePathKind(t, dir, component.path);
2402 if (fstat.kind != .directory) return error.NotDir;2402 if (kind != .directory) return error.NotDir;
2403 },2403 },
2404 error.FileNotFound => |e| {2404 error.FileNotFound => |e| {
2405 component = it.previous() orelse return e;2405 component = it.previous() orelse return e;
...@@ -2740,6 +2740,35 @@ fn dirStatFileWasi(...@@ -2740,6 +2740,35 @@ fn dirStatFileWasi(
2740 }2740 }
2741}2741}
27422742
2743fn filePathKind(t: *Threaded, dir: Dir, sub_path: []const u8) !File.Kind {
2744 if (native_os == .linux) {
2745 var path_buffer: [posix.PATH_MAX]u8 = undefined;
2746 const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
2747
2748 const linux = std.os.linux;
2749 const syscall: Syscall = try .start();
2750 while (true) {
2751 var statx = std.mem.zeroes(linux.Statx);
2752 switch (linux.errno(linux.statx(dir.handle, sub_path_posix, 0, .{ .TYPE = true }, &statx))) {
2753 .SUCCESS => {
2754 syscall.finish();
2755 if (!statx.mask.TYPE) return error.Unexpected;
2756 return statxKind(statx.mode);
2757 },
2758 .INTR => {
2759 try syscall.checkCancel();
2760 continue;
2761 },
2762 .NOMEM => return syscall.fail(error.SystemResources),
2763 else => |err| return syscall.unexpectedErrno(err),
2764 }
2765 }
2766 }
2767
2768 const stat = try dirStatFile(t, dir, sub_path, .{});
2769 return stat.kind;
2770}
2771
2743fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {2772fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
2744 const t: *Threaded = @ptrCast(@alignCast(userdata));2773 const t: *Threaded = @ptrCast(@alignCast(userdata));
27452774
...@@ -12310,16 +12339,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {...@@ -12310,16 +12339,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
12310 .nlink = stx.nlink,12339 .nlink = stx.nlink,
12311 .size = stx.size,12340 .size = stx.size,
12312 .permissions = .fromMode(stx.mode),12341 .permissions = .fromMode(stx.mode),
12313 .kind = switch (stx.mode & std.os.linux.S.IFMT) {12342 .kind = statxKind(stx.mode),
12314 std.os.linux.S.IFDIR => .directory,
12315 std.os.linux.S.IFCHR => .character_device,
12316 std.os.linux.S.IFBLK => .block_device,
12317 std.os.linux.S.IFREG => .file,
12318 std.os.linux.S.IFIFO => .named_pipe,
12319 std.os.linux.S.IFLNK => .sym_link,
12320 std.os.linux.S.IFSOCK => .unix_domain_socket,
12321 else => .unknown,
12322 },
12323 .atime = if (!stx.mask.ATIME) null else .{12343 .atime = if (!stx.mask.ATIME) null else .{
12324 .nanoseconds = @intCast(@as(i128, stx.atime.sec) * std.time.ns_per_s + stx.atime.nsec),12344 .nanoseconds = @intCast(@as(i128, stx.atime.sec) * std.time.ns_per_s + stx.atime.nsec),
12325 },12345 },
...@@ -12328,6 +12348,19 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {...@@ -12328,6 +12348,19 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
12328 };12348 };
12329}12349}
1233012350
12351fn statxKind(stx_mode: u16) File.Kind {
12352 return switch (stx_mode & std.os.linux.S.IFMT) {
12353 std.os.linux.S.IFDIR => .directory,
12354 std.os.linux.S.IFCHR => .character_device,
12355 std.os.linux.S.IFBLK => .block_device,
12356 std.os.linux.S.IFREG => .file,
12357 std.os.linux.S.IFIFO => .named_pipe,
12358 std.os.linux.S.IFLNK => .sym_link,
12359 std.os.linux.S.IFSOCK => .unix_domain_socket,
12360 else => .unknown,
12361 };
12362}
12363
12331fn statFromPosix(st: *const posix.Stat) File.Stat {12364fn statFromPosix(st: *const posix.Stat) File.Stat {
12332 const atime = st.atime();12365 const atime = st.atime();
12333 const mtime = st.mtime();12366 const mtime = st.mtime();
lib/std/Io/net/test.zig+3-3
...@@ -275,7 +275,7 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -275,7 +275,7 @@ test "listen on a unix socket, send bytes, receive bytes" {
275275
276 const io = testing.io;276 const io = testing.io;
277277
278 const socket_path = try generateFileName("socket.unix");278 const socket_path = try generateFileName(io, "socket.unix");
279 defer testing.allocator.free(socket_path);279 defer testing.allocator.free(socket_path);
280280
281 const socket_addr = try net.UnixAddress.init(socket_path);281 const socket_addr = try net.UnixAddress.init(socket_path);
...@@ -308,11 +308,11 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -308,11 +308,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
308 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);308 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
309}309}
310310
311fn generateFileName(base_name: []const u8) ![]const u8 {311fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {
312 const random_bytes_count = 12;312 const random_bytes_count = 12;
313 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);313 const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
314 var random_bytes: [12]u8 = undefined;314 var random_bytes: [12]u8 = undefined;
315 std.crypto.random.bytes(&random_bytes);315 io.random(&random_bytes);
316 var sub_path: [sub_path_len]u8 = undefined;316 var sub_path: [sub_path_len]u8 = undefined;
317 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);317 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
318 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });318 return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });
lib/std/Io/test.zig+20-5
...@@ -565,13 +565,28 @@ test "tasks spawned in group after Group.cancel are canceled" {...@@ -565,13 +565,28 @@ test "tasks spawned in group after Group.cancel are canceled" {
565 try group.concurrent(io, global.waitThenSpawn, .{ io, &group });565 try group.concurrent(io, global.waitThenSpawn, .{ io, &group });
566}566}
567567
568test "CSPRNG" {568test "random" {
569 const io = testing.io;569 const io = testing.io;
570570
571 var random = io.random();571 var a: u64 = undefined;
572 var b: u64 = undefined;
573 var c: u64 = undefined;
574
575 io.random(@ptrCast(&a));
576 io.random(@ptrCast(&b));
577 io.random(@ptrCast(&c));
572578
573 const a = random.int(u64);
574 const b = random.int(u64);
575 const c = random.int(u64);
576 try std.testing.expect(a ^ b ^ c != 0);579 try std.testing.expect(a ^ b ^ c != 0);
577}580}
581
582test "randomSecure" {
583 const io = testing.io;
584
585 var buf_a: [50]u8 = undefined;
586 var buf_b: [50]u8 = undefined;
587 try io.randomSecure(&buf_a);
588 try io.randomSecure(&buf_b);
589 // If this test fails the chance is significantly higher that there is a bug than
590 // that two sets of 50 bytes were equal.
591 try expect(!mem.eql(u8, &buf_a, &buf_b));
592}
lib/std/Random.zig+1-1
...@@ -38,7 +38,7 @@ pub const IoSource = struct {...@@ -38,7 +38,7 @@ pub const IoSource = struct {
3838
39 pub fn interface(this: *const @This()) std.Random {39 pub fn interface(this: *const @This()) std.Random {
40 return .{40 return .{
41 .ptr = this,41 .ptr = @constCast(this),
42 .fillFn = fill,42 .fillFn = fill,
43 };43 };
44 }44 }
lib/std/Random/test.zig+2-1
...@@ -436,8 +436,9 @@ fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void {...@@ -436,8 +436,9 @@ fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void {
436}436}
437437
438test "CSPRNG" {438test "CSPRNG" {
439 const io = std.testing.io;
439 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;440 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
440 std.crypto.random.bytes(&secret_seed);441 io.random(&secret_seed);
441 var csprng = DefaultCsprng.init(secret_seed);442 var csprng = DefaultCsprng.init(secret_seed);
442 const random = csprng.random();443 const random = csprng.random();
443 const a = random.int(u64);444 const a = random.int(u64);
lib/std/crypto.zig+4
...@@ -303,6 +303,9 @@ test {...@@ -303,6 +303,9 @@ test {
303 _ = dh.X25519;303 _ = dh.X25519;
304304
305 _ = kem.kyber_d00;305 _ = kem.kyber_d00;
306 _ = kem.hybrid;
307 _ = kem.kyber_d00;
308 _ = kem.ml_kem;
306309
307 _ = ecc.Curve25519;310 _ = ecc.Curve25519;
308 _ = ecc.Edwards25519;311 _ = ecc.Edwards25519;
...@@ -340,6 +343,7 @@ test {...@@ -340,6 +343,7 @@ test {
340343
341 _ = sign.Ed25519;344 _ = sign.Ed25519;
342 _ = sign.ecdsa;345 _ = sign.ecdsa;
346 _ = sign.mldsa;
343347
344 _ = stream.chacha.ChaCha20IETF;348 _ = stream.chacha.ChaCha20IETF;
345 _ = stream.chacha.ChaCha12IETF;349 _ = stream.chacha.ChaCha12IETF;
lib/std/crypto/25519/ed25519.zig+32-23
...@@ -333,12 +333,10 @@ pub const Ed25519 = struct {...@@ -333,12 +333,10 @@ pub const Ed25519 = struct {
333 }333 }
334334
335 /// Generate a new, random key pair.335 /// Generate a new, random key pair.
336 ///336 pub fn generate(io: std.Io) KeyPair {
337 /// `crypto.random.bytes` must be supported by the target.
338 pub fn generate() KeyPair {
339 var random_seed: [seed_length]u8 = undefined;337 var random_seed: [seed_length]u8 = undefined;
340 while (true) {338 while (true) {
341 crypto.random.bytes(&random_seed);339 io.random(&random_seed);
342 return generateDeterministic(random_seed) catch {340 return generateDeterministic(random_seed) catch {
343 @branchHint(.unlikely);341 @branchHint(.unlikely);
344 continue;342 continue;
...@@ -389,18 +387,21 @@ pub const Ed25519 = struct {...@@ -389,18 +387,21 @@ pub const Ed25519 = struct {
389387
390 /// Create a Signer, that can be used for incremental signing.388 /// Create a Signer, that can be used for incremental signing.
391 /// Note that the signature is not deterministic.389 /// Note that the signature is not deterministic.
392 /// The noise parameter, if set, should be something unique for each message,390 pub fn signer(
393 /// such as a random nonce, or a counter.391 key_pair: KeyPair,
394 pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {392 /// If set, should be something unique for each message, such as a
393 /// random nonce, or a counter.
394 noise: ?[noise_length]u8,
395 /// Filled with cryptographically secure randomness.
396 entropy: *const [noise_length]u8,
397 ) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
395 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())) {
396 return error.KeyMismatch;399 return error.KeyMismatch;
397 }400 }
398 const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();401 const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
399 var h = Sha512.init(.{});402 var h = Sha512.init(.{});
400 h.update(&scalar_and_prefix.prefix);403 h.update(&scalar_and_prefix.prefix);
401 var noise2: [noise_length]u8 = undefined;404 h.update(entropy);
402 crypto.random.bytes(&noise2);
403 h.update(&noise2);
404 if (noise) |*z| {405 if (noise) |*z| {
405 h.update(z);406 h.update(z);
406 }407 }
...@@ -420,7 +421,7 @@ pub const Ed25519 = struct {...@@ -420,7 +421,7 @@ pub const Ed25519 = struct {
420 };421 };
421422
422 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one423 /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
423 pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {424 pub fn verifyBatch(io: std.Io, comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {
424 var r_batch: [count]CompressedScalar = undefined;425 var r_batch: [count]CompressedScalar = undefined;
425 var s_batch: [count]CompressedScalar = undefined;426 var s_batch: [count]CompressedScalar = undefined;
426 var a_batch: [count]Curve = undefined;427 var a_batch: [count]Curve = undefined;
...@@ -454,7 +455,7 @@ pub const Ed25519 = struct {...@@ -454,7 +455,7 @@ pub const Ed25519 = struct {
454455
455 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;456 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
456 for (&z_batch) |*z| {457 for (&z_batch) |*z| {
457 crypto.random.bytes(z[0..16]);458 io.random(z[0..16]);
458 @memset(z[16..], 0);459 @memset(z[16..], 0);
459 }460 }
460461
...@@ -587,12 +588,14 @@ test "signature" {...@@ -587,12 +588,14 @@ test "signature" {
587}588}
588589
589test "batch verification" {590test "batch verification" {
591 const io = std.testing.io;
592
590 for (0..16) |_| {593 for (0..16) |_| {
591 const key_pair = Ed25519.KeyPair.generate();594 const key_pair = Ed25519.KeyPair.generate(io);
592 var msg1: [32]u8 = undefined;595 var msg1: [32]u8 = undefined;
593 var msg2: [32]u8 = undefined;596 var msg2: [32]u8 = undefined;
594 crypto.random.bytes(&msg1);597 io.random(&msg1);
595 crypto.random.bytes(&msg2);598 io.random(&msg2);
596 const sig1 = try key_pair.sign(&msg1, null);599 const sig1 = try key_pair.sign(&msg1, null);
597 const sig2 = try key_pair.sign(&msg2, null);600 const sig2 = try key_pair.sign(&msg2, null);
598 var signature_batch = [_]Ed25519.BatchElement{601 var signature_batch = [_]Ed25519.BatchElement{
...@@ -607,10 +610,10 @@ test "batch verification" {...@@ -607,10 +610,10 @@ test "batch verification" {
607 .public_key = key_pair.public_key,610 .public_key = key_pair.public_key,
608 },611 },
609 };612 };
610 try Ed25519.verifyBatch(2, signature_batch);613 try Ed25519.verifyBatch(io, 2, signature_batch);
611614
612 signature_batch[1].sig = sig1;615 signature_batch[1].sig = sig1;
613 try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(signature_batch.len, signature_batch));616 try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(io, signature_batch.len, signature_batch));
614 }617 }
615}618}
616619
...@@ -718,14 +721,15 @@ test "test vectors" {...@@ -718,14 +721,15 @@ test "test vectors" {
718}721}
719722
720test "with blind keys" {723test "with blind keys" {
724 const io = std.testing.io;
721 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;725 const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;
722726
723 // Create a standard Ed25519 key pair727 // Create a standard Ed25519 key pair
724 const kp = Ed25519.KeyPair.generate();728 const kp = Ed25519.KeyPair.generate(io);
725729
726 // Create a random blinding seed730 // Create a random blinding seed
727 var blind: [32]u8 = undefined;731 var blind: [32]u8 = undefined;
728 crypto.random.bytes(&blind);732 io.random(&blind);
729733
730 // Blind the key pair734 // Blind the key pair
731 const blind_kp = try BlindKeyPair.init(kp, blind, "ctx");735 const blind_kp = try BlindKeyPair.init(kp, blind, "ctx");
...@@ -741,9 +745,12 @@ test "with blind keys" {...@@ -741,9 +745,12 @@ test "with blind keys" {
741}745}
742746
743test "signatures with streaming" {747test "signatures with streaming" {
744 const kp = Ed25519.KeyPair.generate();748 const io = std.testing.io;
749 const kp = Ed25519.KeyPair.generate(io);
745750
746 var signer = try kp.signer(null);751 var entropy: [Ed25519.noise_length]u8 = undefined;
752 io.random(&entropy);
753 var signer = try kp.signer(null, &entropy);
747 signer.update("mes");754 signer.update("mes");
748 signer.update("sage");755 signer.update("sage");
749 const sig = signer.finalize();756 const sig = signer.finalize();
...@@ -757,7 +764,8 @@ test "signatures with streaming" {...@@ -757,7 +764,8 @@ test "signatures with streaming" {
757}764}
758765
759test "key pair from secret key" {766test "key pair from secret key" {
760 const kp = Ed25519.KeyPair.generate();767 const io = std.testing.io;
768 const kp = Ed25519.KeyPair.generate(io);
761 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);769 const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);
762 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());770 try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());
763 try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());771 try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());
...@@ -788,7 +796,8 @@ test "cofactored vs cofactorless verification" {...@@ -788,7 +796,8 @@ test "cofactored vs cofactorless verification" {
788}796}
789797
790test "regular signature verifies with both verify and verifyStrict" {798test "regular signature verifies with both verify and verifyStrict" {
791 const kp = Ed25519.KeyPair.generate();799 const io = std.testing.io;
800 const kp = Ed25519.KeyPair.generate(io);
792 const msg = "test message";801 const msg = "test message";
793 const sig = try kp.sign(msg, null);802 const sig = try kp.sign(msg, null);
794 try sig.verify(msg, kp.public_key);803 try sig.verify(msg, kp.public_key);
lib/std/crypto/25519/edwards25519.zig+5-3
...@@ -575,10 +575,11 @@ test "packing/unpacking" {...@@ -575,10 +575,11 @@ test "packing/unpacking" {
575}575}
576576
577test "point addition/subtraction" {577test "point addition/subtraction" {
578 const io = std.testing.io;
578 var s1: [32]u8 = undefined;579 var s1: [32]u8 = undefined;
579 var s2: [32]u8 = undefined;580 var s2: [32]u8 = undefined;
580 crypto.random.bytes(&s1);581 io.random(&s1);
581 crypto.random.bytes(&s2);582 io.random(&s2);
582 const p = try Edwards25519.basePoint.clampedMul(s1);583 const p = try Edwards25519.basePoint.clampedMul(s1);
583 const q = try Edwards25519.basePoint.clampedMul(s2);584 const q = try Edwards25519.basePoint.clampedMul(s2);
584 const r = p.add(q).add(q).sub(q).sub(q);585 const r = p.add(q).add(q).sub(q).sub(q);
...@@ -622,9 +623,10 @@ test "implicit reduction of invalid scalars" {...@@ -622,9 +623,10 @@ test "implicit reduction of invalid scalars" {
622}623}
623624
624test "subgroup check" {625test "subgroup check" {
626 const io = std.testing.io;
625 for (0..100) |_| {627 for (0..100) |_| {
626 var p = Edwards25519.basePoint;628 var p = Edwards25519.basePoint;
627 const s = Edwards25519.scalar.random();629 const s = Edwards25519.scalar.random(io);
628 p = try p.mulPublic(s);630 p = try p.mulPublic(s);
629 try p.rejectUnexpectedSubgroup();631 try p.rejectUnexpectedSubgroup();
630 }632 }
lib/std/crypto/25519/scalar.zig+7-6
...@@ -101,8 +101,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {...@@ -101,8 +101,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
101}101}
102102
103/// Return a random scalar < L103/// Return a random scalar < L
104pub fn random() CompressedScalar {104pub fn random(io: std.Io) CompressedScalar {
105 return Scalar.random().toBytes();105 return Scalar.random(io).toBytes();
106}106}
107107
108/// A scalar in unpacked representation108/// A scalar in unpacked representation
...@@ -560,10 +560,10 @@ pub const Scalar = struct {...@@ -560,10 +560,10 @@ pub const Scalar = struct {
560 }560 }
561561
562 /// Return a random scalar < L.562 /// Return a random scalar < L.
563 pub fn random() Scalar {563 pub fn random(io: std.Io) Scalar {
564 var s: [64]u8 = undefined;564 var s: [64]u8 = undefined;
565 while (true) {565 while (true) {
566 crypto.random.bytes(&s);566 io.random(&s);
567 const n = Scalar.fromBytes64(s);567 const n = Scalar.fromBytes64(s);
568 if (!n.isZero()) {568 if (!n.isZero()) {
569 return n;569 return n;
...@@ -879,8 +879,9 @@ test "scalar field inversion" {...@@ -879,8 +879,9 @@ test "scalar field inversion" {
879}879}
880880
881test "random scalar" {881test "random scalar" {
882 const s1 = random();882 const io = std.testing.io;
883 const s2 = random();883 const s1 = random(io);
884 const s2 = random(io);
884 try std.testing.expect(!mem.eql(u8, &s1, &s2));885 try std.testing.expect(!mem.eql(u8, &s1, &s2));
885}886}
886887
lib/std/crypto/25519/x25519.zig+2-2
...@@ -41,10 +41,10 @@ pub const X25519 = struct {...@@ -41,10 +41,10 @@ pub const X25519 = struct {
41 }41 }
4242
43 /// Generate a new, random key pair.43 /// Generate a new, random key pair.
44 pub fn generate() KeyPair {44 pub fn generate(io: std.Io) KeyPair {
45 var random_seed: [seed_length]u8 = undefined;45 var random_seed: [seed_length]u8 = undefined;
46 while (true) {46 while (true) {
47 crypto.random.bytes(&random_seed);47 io.random(&random_seed);
48 return generateDeterministic(random_seed) catch {48 return generateDeterministic(random_seed) catch {
49 @branchHint(.unlikely);49 @branchHint(.unlikely);
50 continue;50 continue;
lib/std/crypto/argon2.zig+1-1
...@@ -533,7 +533,7 @@ const PhcFormatHasher = struct {...@@ -533,7 +533,7 @@ const PhcFormatHasher = struct {
533 if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;533 if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;
534534
535 var salt: [default_salt_len]u8 = undefined;535 var salt: [default_salt_len]u8 = undefined;
536 crypto.random.bytes(&salt);536 io.random(&salt);
537537
538 var hash: [default_hash_len]u8 = undefined;538 var hash: [default_hash_len]u8 = undefined;
539 try kdf(allocator, &hash, password, &salt, params, mode, io);539 try kdf(allocator, &hash, password, &salt, params, mode, io);
lib/std/crypto/bcrypt.zig+72-39
...@@ -17,7 +17,7 @@ const HasherError = pwhash.HasherError;...@@ -17,7 +17,7 @@ const HasherError = pwhash.HasherError;
17const EncodingError = phc_format.Error;17const EncodingError = phc_format.Error;
18const Error = pwhash.Error;18const Error = pwhash.Error;
1919
20const salt_length: usize = 16;20pub const salt_length: usize = 16;
21const salt_str_length: usize = 22;21const salt_str_length: usize = 22;
22const ct_str_length: usize = 31;22const ct_str_length: usize = 31;
23const ct_length: usize = 24;23const ct_length: usize = 24;
...@@ -426,7 +426,7 @@ pub const Params = struct {...@@ -426,7 +426,7 @@ pub const Params = struct {
426426
427fn bcryptWithTruncation(427fn bcryptWithTruncation(
428 password: []const u8,428 password: []const u8,
429 salt: [salt_length]u8,429 salt: *const [salt_length]u8,
430 params: Params,430 params: Params,
431) [dk_length]u8 {431) [dk_length]u8 {
432 var state = State{};432 var state = State{};
...@@ -435,13 +435,13 @@ fn bcryptWithTruncation(...@@ -435,13 +435,13 @@ fn bcryptWithTruncation(
435 @memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]);435 @memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]);
436 password_buf[trimmed_len] = 0;436 password_buf[trimmed_len] = 0;
437 const passwordZ = password_buf[0 .. trimmed_len + 1];437 const passwordZ = password_buf[0 .. trimmed_len + 1];
438 state.expand(salt[0..], passwordZ);438 state.expand(salt, passwordZ);
439439
440 const rounds: u64 = @as(u64, 1) << params.rounds_log;440 const rounds: u64 = @as(u64, 1) << params.rounds_log;
441 var k: u64 = 0;441 var k: u64 = 0;
442 while (k < rounds) : (k += 1) {442 while (k < rounds) : (k += 1) {
443 state.expand0(passwordZ);443 state.expand0(passwordZ);
444 state.expand0(salt[0..]);444 state.expand0(salt);
445 }445 }
446 crypto.secureZero(u8, &password_buf);446 crypto.secureZero(u8, &password_buf);
447447
...@@ -467,7 +467,7 @@ fn bcryptWithTruncation(...@@ -467,7 +467,7 @@ fn bcryptWithTruncation(
467/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.467/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.
468pub fn bcrypt(468pub fn bcrypt(
469 password: []const u8,469 password: []const u8,
470 salt: [salt_length]u8,470 salt: *const [salt_length]u8,
471 params: Params,471 params: Params,
472) [dk_length]u8 {472) [dk_length]u8 {
473 if (password.len <= 72 or params.silently_truncate_password) {473 if (password.len <= 72 or params.silently_truncate_password) {
...@@ -475,7 +475,7 @@ pub fn bcrypt(...@@ -475,7 +475,7 @@ pub fn bcrypt(
475 }475 }
476476
477 var pre_hash: [HmacSha512.mac_length]u8 = undefined;477 var pre_hash: [HmacSha512.mac_length]u8 = undefined;
478 HmacSha512.create(&pre_hash, password, &salt);478 HmacSha512.create(&pre_hash, password, salt);
479479
480 const Encoder = crypt_format.Codec.Encoder;480 const Encoder = crypt_format.Codec.Encoder;
481 var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;481 var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;
...@@ -623,16 +623,16 @@ const crypt_format = struct {...@@ -623,16 +623,16 @@ const crypt_format = struct {
623623
624 fn strHashInternal(624 fn strHashInternal(
625 password: []const u8,625 password: []const u8,
626 salt: [salt_length]u8,626 salt: *const [salt_length]u8,
627 params: Params,627 params: Params,
628 ) [hash_length]u8 {628 ) [hash_length]u8 {
629 var dk = bcrypt(password, salt, params);629 var dk = bcrypt(password, salt, params);
630630
631 var salt_str: [salt_str_length]u8 = undefined;631 var salt_str: [salt_str_length]u8 = undefined;
632 _ = Codec.Encoder.encode(salt_str[0..], salt[0..]);632 _ = Codec.Encoder.encode(&salt_str, salt);
633633
634 var ct_str: [ct_str_length]u8 = undefined;634 var ct_str: [ct_str_length]u8 = undefined;
635 _ = Codec.Encoder.encode(ct_str[0..], dk[0..]);635 _ = Codec.Encoder.encode(&ct_str, dk[0..]);
636636
637 var s_buf: [hash_length]u8 = undefined;637 var s_buf: [hash_length]u8 = undefined;
638 const s = fmt.bufPrint(638 const s = fmt.bufPrint(
...@@ -657,21 +657,20 @@ const PhcFormatHasher = struct {...@@ -657,21 +657,20 @@ const PhcFormatHasher = struct {
657 hash: BinValue(dk_length),657 hash: BinValue(dk_length),
658 };658 };
659659
660 /// Return a non-deterministic hash of the password encoded as a PHC-format string660 /// Return a non-deterministic hash of the password encoded as a PHC-format string.
661 fn create(661 fn create(
662 password: []const u8,662 password: []const u8,
663 params: Params,663 params: Params,
664 buf: []u8,664 buf: []u8,
665 /// Filled with cryptographically secure entropy.
666 salt: *const [salt_length]u8,
665 ) HasherError![]const u8 {667 ) HasherError![]const u8 {
666 var salt: [salt_length]u8 = undefined;
667 crypto.random.bytes(&salt);
668
669 const hash = bcrypt(password, salt, params);668 const hash = bcrypt(password, salt, params);
670669
671 return phc_format.serialize(HashResult{670 return phc_format.serialize(HashResult{
672 .alg_id = alg_id,671 .alg_id = alg_id,
673 .r = params.rounds_log,672 .r = params.rounds_log,
674 .salt = try BinValue(salt_length).fromSlice(&salt),673 .salt = try BinValue(salt_length).fromSlice(salt),
675 .hash = try BinValue(dk_length).fromSlice(&hash),674 .hash = try BinValue(dk_length).fromSlice(&hash),
676 }, buf);675 }, buf);
677 }676 }
...@@ -688,11 +687,11 @@ const PhcFormatHasher = struct {...@@ -688,11 +687,11 @@ const PhcFormatHasher = struct {
688 if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)687 if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)
689 return HasherError.InvalidEncoding;688 return HasherError.InvalidEncoding;
690689
691 const params = Params{690 const params: Params = .{
692 .rounds_log = hash_result.r,691 .rounds_log = hash_result.r,
693 .silently_truncate_password = silently_truncate_password,692 .silently_truncate_password = silently_truncate_password,
694 };693 };
695 const hash = bcrypt(password, hash_result.salt.buf, params);694 const hash = bcrypt(password, &hash_result.salt.buf, params);
696 const expected_hash = hash_result.hash.constSlice();695 const expected_hash = hash_result.hash.constSlice();
697696
698 if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;697 if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;
...@@ -709,12 +708,11 @@ const CryptFormatHasher = struct {...@@ -709,12 +708,11 @@ const CryptFormatHasher = struct {
709 password: []const u8,708 password: []const u8,
710 params: Params,709 params: Params,
711 buf: []u8,710 buf: []u8,
711 /// Filled with cryptographically secure entropy.
712 salt: *const [salt_length]u8,
712 ) HasherError![]const u8 {713 ) HasherError![]const u8 {
713 if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;714 if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
714715
715 var salt: [salt_length]u8 = undefined;
716 crypto.random.bytes(&salt);
717
718 const hash = crypt_format.strHashInternal(password, salt, params);716 const hash = crypt_format.strHashInternal(password, salt, params);
719 @memcpy(buf[0..hash.len], &hash);717 @memcpy(buf[0..hash.len], &hash);
720718
...@@ -736,9 +734,9 @@ const CryptFormatHasher = struct {...@@ -736,9 +734,9 @@ const CryptFormatHasher = struct {
736734
737 const salt_str = str[7..][0..salt_str_length];735 const salt_str = str[7..][0..salt_str_length];
738 var salt: [salt_length]u8 = undefined;736 var salt: [salt_length]u8 = undefined;
739 crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding;737 crypt_format.Codec.Decoder.decode(&salt, salt_str) catch return HasherError.InvalidEncoding;
740738
741 const wanted_s = crypt_format.strHashInternal(password, salt, .{739 const wanted_s = crypt_format.strHashInternal(password, &salt, .{
742 .rounds_log = rounds_log,740 .rounds_log = rounds_log,
743 .silently_truncate_password = silently_truncate_password,741 .silently_truncate_password = silently_truncate_password,
744 });742 });
...@@ -756,21 +754,28 @@ pub const HashOptions = struct {...@@ -756,21 +754,28 @@ pub const HashOptions = struct {
756 encoding: pwhash.Encoding,754 encoding: pwhash.Encoding,
757};755};
758756
759/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.757/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key
760/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.758/// stretching function.
759///
760/// bcrypt is a computationally expensive and cache-hard function, explicitly
761/// designed to slow down exhaustive searches.
761///762///
762/// The function returns a string that includes all the parameters required for verification.763/// The function returns a string that includes all the parameters required for
764/// verification.
763///765///
764/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.766/// By design, bcrypt silently truncates passwords to 72 bytes. If this is an
765/// If this is an issue for your application, set the `silently_truncate_password` option to `false`.767/// issue for your application, set the `silently_truncate_password` option to
768/// `false`.
766pub fn strHash(769pub fn strHash(
767 password: []const u8,770 password: []const u8,
768 options: HashOptions,771 options: HashOptions,
769 out: []u8,772 out: []u8,
773 /// Filled with cryptographically secure entropy.
774 salt: *const [salt_length]u8,
770) Error![]const u8 {775) Error![]const u8 {
771 switch (options.encoding) {776 switch (options.encoding) {
772 .phc => return PhcFormatHasher.create(password, options.params, out),777 .phc => return PhcFormatHasher.create(password, options.params, out, salt),
773 .crypt => return CryptFormatHasher.create(password, options.params, out),778 .crypt => return CryptFormatHasher.create(password, options.params, out, salt),
774 }779 }
775}780}
776781
...@@ -796,8 +801,9 @@ pub fn strVerify(...@@ -796,8 +801,9 @@ pub fn strVerify(
796}801}
797802
798test "bcrypt codec" {803test "bcrypt codec" {
804 const io = testing.io;
799 var salt: [salt_length]u8 = undefined;805 var salt: [salt_length]u8 = undefined;
800 crypto.random.bytes(&salt);806 io.random(&salt);
801 var salt_str: [salt_str_length]u8 = undefined;807 var salt_str: [salt_str_length]u8 = undefined;
802 _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);808 _ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);
803 var salt2: [salt_length]u8 = undefined;809 var salt2: [salt_length]u8 = undefined;
...@@ -806,14 +812,20 @@ test "bcrypt codec" {...@@ -806,14 +812,20 @@ test "bcrypt codec" {
806}812}
807813
808test "bcrypt crypt format" {814test "bcrypt crypt format" {
809 var hash_options = HashOptions{815 const io = testing.io;
816
817 var hash_options: HashOptions = .{
810 .params = .{ .rounds_log = 5, .silently_truncate_password = false },818 .params = .{ .rounds_log = 5, .silently_truncate_password = false },
811 .encoding = .crypt,819 .encoding = .crypt,
812 };820 };
813 var verify_options = VerifyOptions{ .silently_truncate_password = false };821 var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
814822
815 var buf: [hash_length]u8 = undefined;823 var buf: [hash_length]u8 = undefined;
816 const s = try strHash("password", hash_options, &buf);824 const s = s: {
825 var salt: [salt_length]u8 = undefined;
826 io.random(&salt);
827 break :s try strHash("password", hash_options, &buf, &salt);
828 };
817829
818 try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));830 try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));
819 try strVerify(s, "password", verify_options);831 try strVerify(s, "password", verify_options);
...@@ -823,7 +835,11 @@ test "bcrypt crypt format" {...@@ -823,7 +835,11 @@ test "bcrypt crypt format" {
823 );835 );
824836
825 var long_buf: [hash_length]u8 = undefined;837 var long_buf: [hash_length]u8 = undefined;
826 var long_s = try strHash("password" ** 100, hash_options, &long_buf);838 var long_s = s: {
839 var salt: [salt_length]u8 = undefined;
840 io.random(&salt);
841 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
842 };
827843
828 try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));844 try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));
829 try strVerify(long_s, "password" ** 100, verify_options);845 try strVerify(long_s, "password" ** 100, verify_options);
...@@ -834,7 +850,11 @@ test "bcrypt crypt format" {...@@ -834,7 +850,11 @@ test "bcrypt crypt format" {
834850
835 hash_options.params.silently_truncate_password = true;851 hash_options.params.silently_truncate_password = true;
836 verify_options.silently_truncate_password = true;852 verify_options.silently_truncate_password = true;
837 long_s = try strHash("password" ** 100, hash_options, &long_buf);853 long_s = s: {
854 var salt: [salt_length]u8 = undefined;
855 io.random(&salt);
856 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
857 };
838 try strVerify(long_s, "password" ** 101, verify_options);858 try strVerify(long_s, "password" ** 101, verify_options);
839859
840 try strVerify(860 try strVerify(
...@@ -845,15 +865,20 @@ test "bcrypt crypt format" {...@@ -845,15 +865,20 @@ test "bcrypt crypt format" {
845}865}
846866
847test "bcrypt phc format" {867test "bcrypt phc format" {
848 var hash_options = HashOptions{868 const io = testing.io;
869 var hash_options: HashOptions = .{
849 .params = .{ .rounds_log = 5, .silently_truncate_password = false },870 .params = .{ .rounds_log = 5, .silently_truncate_password = false },
850 .encoding = .phc,871 .encoding = .phc,
851 };872 };
852 var verify_options = VerifyOptions{ .silently_truncate_password = false };873 var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
853 const prefix = "$bcrypt$";874 const prefix = "$bcrypt$";
854875
855 var buf: [hash_length * 2]u8 = undefined;876 var buf: [hash_length * 2]u8 = undefined;
856 const s = try strHash("password", hash_options, &buf);877 const s = s: {
878 var salt: [salt_length]u8 = undefined;
879 io.random(&salt);
880 break :s try strHash("password", hash_options, &buf, &salt);
881 };
857882
858 try testing.expect(mem.startsWith(u8, s, prefix));883 try testing.expect(mem.startsWith(u8, s, prefix));
859 try strVerify(s, "password", verify_options);884 try strVerify(s, "password", verify_options);
...@@ -863,7 +888,11 @@ test "bcrypt phc format" {...@@ -863,7 +888,11 @@ test "bcrypt phc format" {
863 );888 );
864889
865 var long_buf: [hash_length * 2]u8 = undefined;890 var long_buf: [hash_length * 2]u8 = undefined;
866 var long_s = try strHash("password" ** 100, hash_options, &long_buf);891 var long_s = s: {
892 var salt: [salt_length]u8 = undefined;
893 io.random(&salt);
894 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
895 };
867896
868 try testing.expect(mem.startsWith(u8, long_s, prefix));897 try testing.expect(mem.startsWith(u8, long_s, prefix));
869 try strVerify(long_s, "password" ** 100, verify_options);898 try strVerify(long_s, "password" ** 100, verify_options);
...@@ -874,7 +903,11 @@ test "bcrypt phc format" {...@@ -874,7 +903,11 @@ test "bcrypt phc format" {
874903
875 hash_options.params.silently_truncate_password = true;904 hash_options.params.silently_truncate_password = true;
876 verify_options.silently_truncate_password = true;905 verify_options.silently_truncate_password = true;
877 long_s = try strHash("password" ** 100, hash_options, &long_buf);906 long_s = s: {
907 var salt: [salt_length]u8 = undefined;
908 io.random(&salt);
909 break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
910 };
878 try strVerify(long_s, "password" ** 101, verify_options);911 try strVerify(long_s, "password" ** 101, verify_options);
879912
880 try strVerify(913 try strVerify(
lib/std/crypto/ecdsa.zig+17-11
...@@ -323,10 +323,10 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -323,10 +323,10 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
323 }323 }
324324
325 /// Generate a new, random key pair.325 /// Generate a new, random key pair.
326 pub fn generate() KeyPair {326 pub fn generate(io: std.Io) KeyPair {
327 var random_seed: [seed_length]u8 = undefined;327 var random_seed: [seed_length]u8 = undefined;
328 while (true) {328 while (true) {
329 crypto.random.bytes(&random_seed);329 io.random(&random_seed);
330 return generateDeterministic(random_seed) catch {330 return generateDeterministic(random_seed) catch {
331 @branchHint(.unlikely);331 @branchHint(.unlikely);
332 continue;332 continue;
...@@ -417,12 +417,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {...@@ -417,12 +417,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
417test "Basic operations over EcdsaP384Sha384" {417test "Basic operations over EcdsaP384Sha384" {
418 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;418 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
419419
420 const io = testing.io;
420 const Scheme = EcdsaP384Sha384;421 const Scheme = EcdsaP384Sha384;
421 const kp = Scheme.KeyPair.generate();422 const kp = Scheme.KeyPair.generate(io);
422 const msg = "test";423 const msg = "test";
423424
424 var noise: [Scheme.noise_length]u8 = undefined;425 var noise: [Scheme.noise_length]u8 = undefined;
425 crypto.random.bytes(&noise);426 io.random(&noise);
426 const sig = try kp.sign(msg, noise);427 const sig = try kp.sign(msg, noise);
427 try sig.verify(msg, kp.public_key);428 try sig.verify(msg, kp.public_key);
428429
...@@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" {...@@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" {
433test "Basic operations over Secp256k1" {434test "Basic operations over Secp256k1" {
434 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;435 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
435436
437 const io = testing.io;
436 const Scheme = EcdsaSecp256k1Sha256oSha256;438 const Scheme = EcdsaSecp256k1Sha256oSha256;
437 const kp = Scheme.KeyPair.generate();439 const kp = Scheme.KeyPair.generate(io);
438 const msg = "test";440 const msg = "test";
439441
440 var noise: [Scheme.noise_length]u8 = undefined;442 var noise: [Scheme.noise_length]u8 = undefined;
441 crypto.random.bytes(&noise);443 io.random(&noise);
442 const sig = try kp.sign(msg, noise);444 const sig = try kp.sign(msg, noise);
443 try sig.verify(msg, kp.public_key);445 try sig.verify(msg, kp.public_key);
444446
...@@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" {...@@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" {
449test "Basic operations over EcdsaP384Sha256" {451test "Basic operations over EcdsaP384Sha256" {
450 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;452 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
451453
454 const io = testing.io;
452 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);455 const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
453 const kp = Scheme.KeyPair.generate();456 const kp = Scheme.KeyPair.generate(io);
454 const msg = "test";457 const msg = "test";
455458
456 var noise: [Scheme.noise_length]u8 = undefined;459 var noise: [Scheme.noise_length]u8 = undefined;
457 crypto.random.bytes(&noise);460 io.random(&noise);
458 const sig = try kp.sign(msg, noise);461 const sig = try kp.sign(msg, noise);
459 try sig.verify(msg, kp.public_key);462 try sig.verify(msg, kp.public_key);
460463
...@@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" {...@@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" {
502test "Prehashed message operations" {505test "Prehashed message operations" {
503 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;506 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
504507
508 const io = testing.io;
509
505 const Scheme = EcdsaP256Sha256;510 const Scheme = EcdsaP256Sha256;
506 const kp = Scheme.KeyPair.generate();511 const kp = Scheme.KeyPair.generate(io);
507 const msg = "test message for prehashed signing";512 const msg = "test message for prehashed signing";
508513
509 const Hash = crypto.hash.sha2.Sha256;514 const Hash = crypto.hash.sha2.Sha256;
...@@ -518,7 +523,7 @@ test "Prehashed message operations" {...@@ -518,7 +523,7 @@ test "Prehashed message operations" {
518 try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key));523 try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key));
519524
520 var noise: [Scheme.noise_length]u8 = undefined;525 var noise: [Scheme.noise_length]u8 = undefined;
521 crypto.random.bytes(&noise);526 io.random(&noise);
522 const sig_with_noise = try kp.signPrehashed(msg_hash, noise);527 const sig_with_noise = try kp.signPrehashed(msg_hash, noise);
523 try sig_with_noise.verifyPrehashed(msg_hash, kp.public_key);528 try sig_with_noise.verifyPrehashed(msg_hash, kp.public_key);
524529
...@@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void {...@@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void {
1628test "Sec1 encoding/decoding" {1633test "Sec1 encoding/decoding" {
1629 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;1634 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
16301635
1636 const io = testing.io;
1631 const Scheme = EcdsaP384Sha384;1637 const Scheme = EcdsaP384Sha384;
1632 const kp = Scheme.KeyPair.generate();1638 const kp = Scheme.KeyPair.generate(io);
1633 const pk = kp.public_key;1639 const pk = kp.public_key;
1634 const pk_compressed_sec1 = pk.toCompressedSec1();1640 const pk_compressed_sec1 = pk.toCompressedSec1();
1635 const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);1641 const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);
lib/std/crypto/hybrid_kem.zig+74-61
...@@ -174,43 +174,56 @@ pub fn HybridKem(comptime params: Params) type {...@@ -174,43 +174,56 @@ pub fn HybridKem(comptime params: Params) type {
174 return .{ .bytes = buf.* };174 return .{ .bytes = buf.* };
175 }175 }
176176
177 /// Generates a shared secret and encapsulates it for the public key.177 /// Generates a shared secret, encapsulated for the public key,
178 /// If `seed` is `null`, uses random bytes from `std.crypto.random`.178 /// using random bytes.
179 /// If `seed` is set, encapsulation is deterministic (for testing only).179 ///
180 pub fn encaps(self: PublicKey, seed: ?[]const u8) !EncapsulatedSecret {180 /// This is recommended over `encapsDeterministic`.
181 const pq_nek = params.PqKem.PublicKey.encoded_length;181 pub fn encaps(pk: PublicKey, io: std.Io) !EncapsulatedSecret {
182 const ek_pq = try params.PqKem.PublicKey.fromBytes(self.bytes[0..pq_nek]);
183 const ek_t = self.bytes[pq_nek..][0..params.Group.element_length];
184
185 var seed_pq: [32]u8 = undefined;182 var seed_pq: [32]u8 = undefined;
186 var seed_t_expanded: [params.Group.seed_length]u8 = undefined;183 io.random(&seed_pq);
184 var seed_t: [32]u8 = undefined;
185 io.random(&seed_t);
186 var seed_t_expanded: [params.Group.seed_length]u8 = try expandRandomnessSeed(seed_t);
187 return encapsInner(pk, &seed_pq, &seed_t_expanded);
188 }
187189
188 if (seed) |r| {190 /// Generates a shared secret, encapsulated for the public key,
189 if (r.len < 32) return error.InsufficientRandomness;191 /// using the provided seed.
190 seed_pq = r[0..32].*;192 ///
193 /// Calling `encaps` instead is recommended.
194 pub fn encapsDeterministic(pk: PublicKey, seed: []const u8) !EncapsulatedSecret {
195 if (seed.len < 32) return error.InsufficientRandomness;
196 var seed_pq: [32]u8 = seed[0..32].*;
197 var seed_t_expanded: [params.Group.seed_length]u8 = undefined;
191198
192 const t_randomness = r[32..];199 const t_randomness = seed[32..];
200 if (t_randomness.len < params.Group.seed_length) {
201 // Provided randomness is shorter than seed_length, use it directly
202 // (test vectors provide just enough for randomScalar)
203 @memcpy(seed_t_expanded[0..t_randomness.len], t_randomness);
204 // Pad the rest with zeros if needed (shouldn't be used by randomScalar)
193 if (t_randomness.len < params.Group.seed_length) {205 if (t_randomness.len < params.Group.seed_length) {
194 // Provided randomness is shorter than seed_length, use it directly206 @memset(seed_t_expanded[t_randomness.len..], 0);
195 // (test vectors provide just enough for randomScalar)
196 @memcpy(seed_t_expanded[0..t_randomness.len], t_randomness);
197 // Pad the rest with zeros if needed (shouldn't be used by randomScalar)
198 if (t_randomness.len < params.Group.seed_length) {
199 @memset(seed_t_expanded[t_randomness.len..], 0);
200 }
201 } else {
202 // Full randomness provided
203 @memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]);
204 }207 }
205 } else {208 } else {
206 crypto.random.bytes(&seed_pq);209 // Full randomness provided
207 var seed_t: [32]u8 = undefined;210 @memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]);
208 crypto.random.bytes(&seed_t);
209 seed_t_expanded = try expandRandomnessSeed(seed_t);
210 }211 }
211212
212 const pq_encap = ek_pq.encaps(seed_pq);213 return encapsInner(pk, &seed_pq, &seed_t_expanded);
213 const sk_e = try params.Group.randomScalar(&seed_t_expanded);214 }
215
216 fn encapsInner(
217 pk: PublicKey,
218 seed_pq: *[32]u8,
219 seed_t_expanded: *[params.Group.seed_length]u8,
220 ) !EncapsulatedSecret {
221 const pq_nek = params.PqKem.PublicKey.encoded_length;
222 const ek_pq = try params.PqKem.PublicKey.fromBytes(pk.bytes[0..pq_nek]);
223 const ek_t = pk.bytes[pq_nek..][0..params.Group.element_length];
224
225 const pq_encap = ek_pq.encapsDeterministic(seed_pq);
226 const sk_e = try params.Group.randomScalar(seed_t_expanded);
214 const ct_t_point = try params.Group.mulBase(sk_e);227 const ct_t_point = try params.Group.mulBase(sk_e);
215 const ct_t = if (is_nist_curve) params.Group.encodePoint(ct_t_point) else ct_t_point;228 const ct_t = if (is_nist_curve) params.Group.encodePoint(ct_t_point) else ct_t_point;
216229
...@@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type {...@@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type {
280 }293 }
281294
282 /// Generates a new random key pair.295 /// Generates a new random key pair.
283 pub fn generate() !KeyPair {296 pub fn generate(io: std.Io) !KeyPair {
284 var seed: [params.Nseed]u8 = undefined;297 var seed: [params.Nseed]u8 = undefined;
285 crypto.random.bytes(&seed);298 io.random(&seed);
286 return generateDeterministic(seed);299 return generateDeterministic(seed);
287 }300 }
288 };301 };
...@@ -386,7 +399,7 @@ test "MLKEM768-X25519 basic round trip" {...@@ -386,7 +399,7 @@ test "MLKEM768-X25519 basic round trip" {
386 var enc_seed: [64]u8 = undefined;399 var enc_seed: [64]u8 = undefined;
387 @memset(&enc_seed, 0x43);400 @memset(&enc_seed, 0x43);
388401
389 const encap_result = try kp.public_key.encaps(&enc_seed);402 const encap_result = try kp.public_key.encapsDeterministic(&enc_seed);
390 const ss_decap = try kp.secret_key.decaps(&encap_result.ciphertext);403 const ss_decap = try kp.secret_key.decaps(&encap_result.ciphertext);
391404
392 try testing.expectEqualSlices(u8, &encap_result.shared_secret, &ss_decap);405 try testing.expectEqualSlices(u8, &encap_result.shared_secret, &ss_decap);
...@@ -408,7 +421,7 @@ test "MLKEM768-X25519 test vector 0" {...@@ -408,7 +421,7 @@ test "MLKEM768-X25519 test vector 0" {
408 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);421 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
409 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());422 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
410423
411 const enc_result = try kp.public_key.encaps(&randomness);424 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
412 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);425 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
413 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);426 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
414427
...@@ -432,7 +445,7 @@ test "MLKEM768-X25519 test vector 1" {...@@ -432,7 +445,7 @@ test "MLKEM768-X25519 test vector 1" {
432 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);445 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
433 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());446 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
434447
435 const enc_result = try kp.public_key.encaps(&randomness);448 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
436 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);449 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
437 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);450 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
438451
...@@ -456,7 +469,7 @@ test "MLKEM768-X25519 test vector 2" {...@@ -456,7 +469,7 @@ test "MLKEM768-X25519 test vector 2" {
456 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);469 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
457 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());470 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
458471
459 const enc_result = try kp.public_key.encaps(&randomness);472 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
460 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);473 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
461 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);474 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
462475
...@@ -480,7 +493,7 @@ test "MLKEM768-X25519 test vector 3" {...@@ -480,7 +493,7 @@ test "MLKEM768-X25519 test vector 3" {
480 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);493 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
481 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());494 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
482495
483 const enc_result = try kp.public_key.encaps(&randomness);496 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
484 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);497 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
485 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);498 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
486499
...@@ -504,7 +517,7 @@ test "MLKEM768-X25519 test vector 4" {...@@ -504,7 +517,7 @@ test "MLKEM768-X25519 test vector 4" {
504 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);517 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
505 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());518 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
506519
507 const enc_result = try kp.public_key.encaps(&randomness);520 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
508 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);521 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
509 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);522 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
510523
...@@ -528,7 +541,7 @@ test "MLKEM768-X25519 test vector 5" {...@@ -528,7 +541,7 @@ test "MLKEM768-X25519 test vector 5" {
528 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);541 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
529 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());542 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
530543
531 const enc_result = try kp.public_key.encaps(&randomness);544 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
532 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);545 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
533 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);546 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
534547
...@@ -552,7 +565,7 @@ test "MLKEM768-X25519 test vector 6" {...@@ -552,7 +565,7 @@ test "MLKEM768-X25519 test vector 6" {
552 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);565 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
553 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());566 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
554567
555 const enc_result = try kp.public_key.encaps(&randomness);568 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
556 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);569 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
557 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);570 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
558571
...@@ -576,7 +589,7 @@ test "MLKEM768-X25519 test vector 7" {...@@ -576,7 +589,7 @@ test "MLKEM768-X25519 test vector 7" {
576 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);589 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
577 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());590 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
578591
579 const enc_result = try kp.public_key.encaps(&randomness);592 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
580 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);593 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
581 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);594 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
582595
...@@ -600,7 +613,7 @@ test "MLKEM768-X25519 test vector 8" {...@@ -600,7 +613,7 @@ test "MLKEM768-X25519 test vector 8" {
600 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);613 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
601 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());614 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
602615
603 const enc_result = try kp.public_key.encaps(&randomness);616 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
604 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);617 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
605 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);618 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
606619
...@@ -624,7 +637,7 @@ test "MLKEM768-X25519 test vector 9" {...@@ -624,7 +637,7 @@ test "MLKEM768-X25519 test vector 9" {
624 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);637 const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
625 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());638 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
626639
627 const enc_result = try kp.public_key.encaps(&randomness);640 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
628 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);641 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
629 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);642 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
630643
...@@ -648,7 +661,7 @@ test "MLKEM768-P256 test vector 0" {...@@ -648,7 +661,7 @@ test "MLKEM768-P256 test vector 0" {
648 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);661 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
649 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());662 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
650663
651 const enc_result = try kp.public_key.encaps(&randomness);664 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
652 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);665 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
653 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);666 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
654667
...@@ -672,7 +685,7 @@ test "MLKEM768-P256 test vector 1" {...@@ -672,7 +685,7 @@ test "MLKEM768-P256 test vector 1" {
672 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);685 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
673 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());686 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
674687
675 const enc_result = try kp.public_key.encaps(&randomness);688 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
676 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);689 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
677 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);690 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
678691
...@@ -696,7 +709,7 @@ test "MLKEM768-P256 test vector 2" {...@@ -696,7 +709,7 @@ test "MLKEM768-P256 test vector 2" {
696 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);709 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
697 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());710 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
698711
699 const enc_result = try kp.public_key.encaps(&randomness);712 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
700 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);713 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
701 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);714 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
702715
...@@ -720,7 +733,7 @@ test "MLKEM768-P256 test vector 3" {...@@ -720,7 +733,7 @@ test "MLKEM768-P256 test vector 3" {
720 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);733 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
721 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());734 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
722735
723 const enc_result = try kp.public_key.encaps(&randomness);736 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
724 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);737 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
725 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);738 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
726739
...@@ -744,7 +757,7 @@ test "MLKEM768-P256 test vector 4" {...@@ -744,7 +757,7 @@ test "MLKEM768-P256 test vector 4" {
744 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);757 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
745 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());758 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
746759
747 const enc_result = try kp.public_key.encaps(&randomness);760 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
748 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);761 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
749 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);762 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
750763
...@@ -768,7 +781,7 @@ test "MLKEM768-P256 test vector 5" {...@@ -768,7 +781,7 @@ test "MLKEM768-P256 test vector 5" {
768 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);781 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
769 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());782 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
770783
771 const enc_result = try kp.public_key.encaps(&randomness);784 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
772 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);785 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
773 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);786 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
774787
...@@ -792,7 +805,7 @@ test "MLKEM768-P256 test vector 6" {...@@ -792,7 +805,7 @@ test "MLKEM768-P256 test vector 6" {
792 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);805 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
793 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());806 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
794807
795 const enc_result = try kp.public_key.encaps(&randomness);808 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
796 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);809 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
797 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);810 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
798811
...@@ -816,7 +829,7 @@ test "MLKEM768-P256 test vector 7" {...@@ -816,7 +829,7 @@ test "MLKEM768-P256 test vector 7" {
816 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);829 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
817 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());830 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
818831
819 const enc_result = try kp.public_key.encaps(&randomness);832 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
820 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);833 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
821 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);834 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
822835
...@@ -840,7 +853,7 @@ test "MLKEM768-P256 test vector 8" {...@@ -840,7 +853,7 @@ test "MLKEM768-P256 test vector 8" {
840 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);853 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
841 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());854 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
842855
843 const enc_result = try kp.public_key.encaps(&randomness);856 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
844 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);857 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
845 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);858 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
846859
...@@ -864,7 +877,7 @@ test "MLKEM768-P256 test vector 9" {...@@ -864,7 +877,7 @@ test "MLKEM768-P256 test vector 9" {
864 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);877 const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
865 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());878 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
866879
867 const enc_result = try kp.public_key.encaps(&randomness);880 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
868 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);881 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
869 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);882 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
870883
...@@ -888,7 +901,7 @@ test "MLKEM1024-P384 test vector 0" {...@@ -888,7 +901,7 @@ test "MLKEM1024-P384 test vector 0" {
888 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);901 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
889 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());902 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
890903
891 const enc_result = try kp.public_key.encaps(&randomness);904 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
892 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);905 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
893 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);906 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
894907
...@@ -912,7 +925,7 @@ test "MLKEM1024-P384 test vector 1" {...@@ -912,7 +925,7 @@ test "MLKEM1024-P384 test vector 1" {
912 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);925 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
913 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());926 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
914927
915 const enc_result = try kp.public_key.encaps(&randomness);928 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
916 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);929 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
917 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);930 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
918931
...@@ -936,7 +949,7 @@ test "MLKEM1024-P384 test vector 2" {...@@ -936,7 +949,7 @@ test "MLKEM1024-P384 test vector 2" {
936 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);949 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
937 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());950 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
938951
939 const enc_result = try kp.public_key.encaps(&randomness);952 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
940 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);953 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
941 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);954 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
942955
...@@ -960,7 +973,7 @@ test "MLKEM1024-P384 test vector 3" {...@@ -960,7 +973,7 @@ test "MLKEM1024-P384 test vector 3" {
960 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);973 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
961 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());974 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
962975
963 const enc_result = try kp.public_key.encaps(&randomness);976 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
964 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);977 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
965 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);978 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
966979
...@@ -984,7 +997,7 @@ test "MLKEM1024-P384 test vector 4" {...@@ -984,7 +997,7 @@ test "MLKEM1024-P384 test vector 4" {
984 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);997 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
985 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());998 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
986999
987 const enc_result = try kp.public_key.encaps(&randomness);1000 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
988 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1001 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
989 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1002 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
9901003
...@@ -1008,7 +1021,7 @@ test "MLKEM1024-P384 test vector 5" {...@@ -1008,7 +1021,7 @@ test "MLKEM1024-P384 test vector 5" {
1008 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1021 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1009 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1022 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10101023
1011 const enc_result = try kp.public_key.encaps(&randomness);1024 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1012 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1025 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1013 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1026 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10141027
...@@ -1032,7 +1045,7 @@ test "MLKEM1024-P384 test vector 6" {...@@ -1032,7 +1045,7 @@ test "MLKEM1024-P384 test vector 6" {
1032 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1045 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1033 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1046 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10341047
1035 const enc_result = try kp.public_key.encaps(&randomness);1048 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1036 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1049 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1037 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1050 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10381051
...@@ -1056,7 +1069,7 @@ test "MLKEM1024-P384 test vector 7" {...@@ -1056,7 +1069,7 @@ test "MLKEM1024-P384 test vector 7" {
1056 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1069 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1057 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1070 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10581071
1059 const enc_result = try kp.public_key.encaps(&randomness);1072 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1060 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1073 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1061 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1074 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10621075
...@@ -1080,7 +1093,7 @@ test "MLKEM1024-P384 test vector 8" {...@@ -1080,7 +1093,7 @@ test "MLKEM1024-P384 test vector 8" {
1080 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);1093 const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
1081 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());1094 try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
10821095
1083 const enc_result = try kp.public_key.encaps(&randomness);1096 const enc_result = try kp.public_key.encapsDeterministic(&randomness);
1084 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);1097 try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
1085 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);1098 try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
10861099
lib/std/crypto/ml_dsa.zig+10-9
...@@ -2019,12 +2019,9 @@ fn MLDSAImpl(comptime p: Params) type {...@@ -2019,12 +2019,9 @@ fn MLDSAImpl(comptime p: Params) type {
2019 secret_key: SecretKey,2019 secret_key: SecretKey,
20202020
2021 /// Generate a new random key pair.2021 /// Generate a new random key pair.
2022 /// This uses the system's cryptographically secure random number generator.2022 pub fn generate(io: std.Io) KeyPair {
2023 ///
2024 /// `crypto.random.bytes` must be supported by the target.
2025 pub fn generate() KeyPair {
2026 var seed: [Self.seed_length]u8 = undefined;2023 var seed: [Self.seed_length]u8 = undefined;
2027 crypto.random.bytes(&seed);2024 io.random(&seed);
2028 return generateDeterministic(seed) catch unreachable;2025 return generateDeterministic(seed) catch unreachable;
2029 }2026 }
20302027
...@@ -3198,8 +3195,9 @@ test "ML-DSA-87 KAT test vector 0" {...@@ -3198,8 +3195,9 @@ test "ML-DSA-87 KAT test vector 0" {
3198}3195}
31993196
3200test "KeyPair API - generate and sign" {3197test "KeyPair API - generate and sign" {
3198 const io = std.testing.io;
3201 // Test the new KeyPair API with random generation3199 // Test the new KeyPair API with random generation
3202 const kp = MLDSA44.KeyPair.generate();3200 const kp = MLDSA44.KeyPair.generate(io);
3203 const msg = "Test message for KeyPair API";3201 const msg = "Test message for KeyPair API";
32043202
3205 // Sign with deterministic mode (no noise)3203 // Sign with deterministic mode (no noise)
...@@ -3222,8 +3220,9 @@ test "KeyPair API - generateDeterministic" {...@@ -3222,8 +3220,9 @@ test "KeyPair API - generateDeterministic" {
3222}3220}
32233221
3224test "KeyPair API - fromSecretKey" {3222test "KeyPair API - fromSecretKey" {
3223 const io = std.testing.io;
3225 // Generate a key pair3224 // Generate a key pair
3226 const kp1 = MLDSA44.KeyPair.generate();3225 const kp1 = MLDSA44.KeyPair.generate(io);
32273226
3228 // Derive public key from secret key3227 // Derive public key from secret key
3229 const kp2 = try MLDSA44.KeyPair.fromSecretKey(kp1.secret_key);3228 const kp2 = try MLDSA44.KeyPair.fromSecretKey(kp1.secret_key);
...@@ -3235,8 +3234,9 @@ test "KeyPair API - fromSecretKey" {...@@ -3235,8 +3234,9 @@ test "KeyPair API - fromSecretKey" {
3235}3234}
32363235
3237test "Signature verification with noise" {3236test "Signature verification with noise" {
3237 const io = std.testing.io;
3238 // Test signing with randomness (hedged signatures)3238 // Test signing with randomness (hedged signatures)
3239 const kp = MLDSA65.KeyPair.generate();3239 const kp = MLDSA65.KeyPair.generate(io);
3240 const msg = "Message to be signed with randomness";3240 const msg = "Message to be signed with randomness";
32413241
3242 // Create some noise3242 // Create some noise
...@@ -3250,8 +3250,9 @@ test "Signature verification with noise" {...@@ -3250,8 +3250,9 @@ test "Signature verification with noise" {
3250}3250}
32513251
3252test "Signature verification failure" {3252test "Signature verification failure" {
3253 const io = std.testing.io;
3253 // Test that invalid signatures are rejected3254 // Test that invalid signatures are rejected
3254 const kp = MLDSA44.KeyPair.generate();3255 const kp = MLDSA44.KeyPair.generate(io);
3255 const msg = "Original message";3256 const msg = "Original message";
3256 const sig = try kp.sign(msg, null);3257 const sig = try kp.sign(msg, null);
32573258
lib/std/crypto/ml_kem.zig+29-20
...@@ -244,32 +244,41 @@ fn Kyber(comptime p: Params) type {...@@ -244,32 +244,41 @@ fn Kyber(comptime p: Params) type {
244 /// Size of a serialized representation of the key, in bytes.244 /// Size of a serialized representation of the key, in bytes.
245 pub const encoded_length = InnerPk.encoded_length;245 pub const encoded_length = InnerPk.encoded_length;
246246
247 /// Generates a shared secret, and encapsulates it for the public key.247 /// Generates a shared secret, encapsulated for the public key,
248 /// If `seed` is `null`, a random seed is used. This is recommended.248 /// using random bytes.
249 /// If `seed` is set, encapsulation is deterministic.249 ///
250 pub fn encaps(pk: PublicKey, seed_: ?[encaps_seed_length]u8) EncapsulatedSecret {250 /// This is recommended over `encapsDeterministic`.
251 pub fn encaps(pk: PublicKey, io: std.Io) EncapsulatedSecret {
251 var m: [inner_plaintext_length]u8 = undefined;252 var m: [inner_plaintext_length]u8 = undefined;
253 io.random(&m);
254 return encapsInner(pk, &m);
255 }
252256
253 if (seed_) |seed| {257 /// Generates a shared secret, encapsulated for the public key,
254 if (p.ml_kem) {258 /// using the provided seed.
255 @memcpy(&m, &seed);259 ///
256 } else {260 /// Calling `encaps` instead is recommended.
257 // m = H(seed)261 pub fn encapsDeterministic(pk: PublicKey, seed: *const [encaps_seed_length]u8) EncapsulatedSecret {
258 sha3.Sha3_256.hash(&seed, &m, .{});262 var m: [inner_plaintext_length]u8 = undefined;
259 }263 if (p.ml_kem) {
264 @memcpy(&m, seed);
260 } else {265 } else {
261 crypto.random.bytes(&m);266 // m = H(seed)
267 sha3.Sha3_256.hash(seed, &m, .{});
262 }268 }
269 return encapsInner(pk, &m);
270 }
263271
272 fn encapsInner(pk: PublicKey, m: *[inner_plaintext_length]u8) EncapsulatedSecret {
264 // (K', r) = G(m ‖ H(pk))273 // (K', r) = G(m ‖ H(pk))
265 var kr: [inner_plaintext_length + h_length]u8 = undefined;274 var kr: [inner_plaintext_length + h_length]u8 = undefined;
266 var g = sha3.Sha3_512.init(.{});275 var g = sha3.Sha3_512.init(.{});
267 g.update(&m);276 g.update(m);
268 g.update(&pk.hpk);277 g.update(&pk.hpk);
269 g.final(&kr);278 g.final(&kr);
270279
271 // c = innerEncrypt(pk, m, r)280 // c = innerEncrypt(pk, m, r)
272 const ct = pk.pk.encrypt(&m, kr[32..64]);281 const ct = pk.pk.encrypt(m, kr[32..64]);
273282
274 if (p.ml_kem) {283 if (p.ml_kem) {
275 return EncapsulatedSecret{284 return EncapsulatedSecret{
...@@ -398,10 +407,10 @@ fn Kyber(comptime p: Params) type {...@@ -398,10 +407,10 @@ fn Kyber(comptime p: Params) type {
398 }407 }
399408
400 /// Generate a new, random key pair.409 /// Generate a new, random key pair.
401 pub fn generate() KeyPair {410 pub fn generate(io: std.Io) KeyPair {
402 var random_seed: [seed_length]u8 = undefined;411 var random_seed: [seed_length]u8 = undefined;
403 while (true) {412 while (true) {
404 crypto.random.bytes(&random_seed);413 io.random(&random_seed);
405 return generateDeterministic(random_seed) catch {414 return generateDeterministic(random_seed) catch {
406 @branchHint(.unlikely);415 @branchHint(.unlikely);
407 continue;416 continue;
...@@ -1634,15 +1643,15 @@ test "Test happy flow" {...@@ -1634,15 +1643,15 @@ test "Test happy flow" {
1634 }1643 }
1635 inline for (modes) |mode| {1644 inline for (modes) |mode| {
1636 for (0..10) |i| {1645 for (0..10) |i| {
1637 seed[0] = @as(u8, @intCast(i));1646 seed[0] = @intCast(i);
1638 const kp = try mode.KeyPair.generateDeterministic(seed);1647 const kp = try mode.KeyPair.generateDeterministic(seed);
1639 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());1648 const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());
1640 try testing.expectEqual(sk, kp.secret_key);1649 try testing.expectEqual(sk, kp.secret_key);
1641 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());1650 const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());
1642 try testing.expectEqual(pk, kp.public_key);1651 try testing.expectEqual(pk, kp.public_key);
1643 for (0..10) |j| {1652 for (0..10) |j| {
1644 seed[1] = @as(u8, @intCast(j));1653 seed[1] = @intCast(j);
1645 const e = pk.encaps(seed[0..32].*);1654 const e = pk.encapsDeterministic(seed[0..32]);
1646 try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext));1655 try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext));
1647 }1656 }
1648 }1657 }
...@@ -1695,7 +1704,7 @@ fn testNistKat(mode: type, hash: []const u8) !void {...@@ -1695,7 +1704,7 @@ fn testNistKat(mode: type, hash: []const u8) !void {
1695 g2.fill(kseed[32..64]);1704 g2.fill(kseed[32..64]);
1696 g2.fill(&eseed);1705 g2.fill(&eseed);
1697 const kp = try mode.KeyPair.generateDeterministic(kseed);1706 const kp = try mode.KeyPair.generateDeterministic(kseed);
1698 const e = kp.public_key.encaps(eseed);1707 const e = kp.public_key.encapsDeterministic(&eseed);
1699 const ss2 = try kp.secret_key.decaps(&e.ciphertext);1708 const ss2 = try kp.secret_key.decaps(&e.ciphertext);
1700 try testing.expectEqual(ss2, e.shared_secret);1709 try testing.expectEqual(ss2, e.shared_secret);
1701 try fw.writer.print("pk = {X}\n", .{&kp.public_key.toBytes()});1710 try fw.writer.print("pk = {X}\n", .{&kp.public_key.toBytes()});
lib/std/crypto/pcurves/p256.zig+2-2
...@@ -122,8 +122,8 @@ pub const P256 = struct {...@@ -122,8 +122,8 @@ pub const P256 = struct {
122 }122 }
123123
124 /// Return a random point.124 /// Return a random point.
125 pub fn random() P256 {125 pub fn random(io: std.Io) P256 {
126 const n = scalar.random(.little);126 const n = scalar.random(io, .little);
127 return basePoint.mul(n, .little) catch unreachable;127 return basePoint.mul(n, .little) catch unreachable;
128 }128 }
129129
lib/std/crypto/pcurves/p256/scalar.zig+4-4
...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
68}68}
6969
70/// Return a random scalar70/// Return a random scalar
71pub fn random(endian: std.builtin.Endian) CompressedScalar {71pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
72 return Scalar.random().toBytes(endian);72 return Scalar.random(io).toBytes(endian);
73}73}
7474
75/// A scalar in unpacked representation.75/// A scalar in unpacked representation.
...@@ -170,10 +170,10 @@ pub const Scalar = struct {...@@ -170,10 +170,10 @@ pub const Scalar = struct {
170 }170 }
171171
172 /// Return a random scalar < L.172 /// Return a random scalar < L.
173 pub fn random() Scalar {173 pub fn random(io: std.Io) Scalar {
174 var s: [48]u8 = undefined;174 var s: [48]u8 = undefined;
175 while (true) {175 while (true) {
176 crypto.random.bytes(&s);176 io.random(&s);
177 const n = Scalar.fromBytes48(s, .little);177 const n = Scalar.fromBytes48(s, .little);
178 if (!n.isZero()) {178 if (!n.isZero()) {
179 return n;179 return n;
lib/std/crypto/pcurves/p384.zig+2-2
...@@ -122,8 +122,8 @@ pub const P384 = struct {...@@ -122,8 +122,8 @@ pub const P384 = struct {
122 }122 }
123123
124 /// Return a random point.124 /// Return a random point.
125 pub fn random() P384 {125 pub fn random(io: std.Io) P384 {
126 const n = scalar.random(.little);126 const n = scalar.random(io, .little);
127 return basePoint.mul(n, .little) catch unreachable;127 return basePoint.mul(n, .little) catch unreachable;
128 }128 }
129129
lib/std/crypto/pcurves/p384/scalar.zig+4-4
...@@ -63,8 +63,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)...@@ -63,8 +63,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
63}63}
6464
65/// Return a random scalar65/// Return a random scalar
66pub fn random(endian: std.builtin.Endian) CompressedScalar {66pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
67 return Scalar.random().toBytes(endian);67 return Scalar.random(io).toBytes(endian);
68}68}
6969
70/// A scalar in unpacked representation.70/// A scalar in unpacked representation.
...@@ -159,10 +159,10 @@ pub const Scalar = struct {...@@ -159,10 +159,10 @@ pub const Scalar = struct {
159 }159 }
160160
161 /// Return a random scalar < L.161 /// Return a random scalar < L.
162 pub fn random() Scalar {162 pub fn random(io: std.Io) Scalar {
163 var s: [64]u8 = undefined;163 var s: [64]u8 = undefined;
164 while (true) {164 while (true) {
165 crypto.random.bytes(&s);165 io.random(&s);
166 const n = Scalar.fromBytes64(s, .little);166 const n = Scalar.fromBytes64(s, .little);
167 if (!n.isZero()) {167 if (!n.isZero()) {
168 return n;168 return n;
lib/std/crypto/pcurves/secp256k1.zig+2-2
...@@ -175,8 +175,8 @@ pub const Secp256k1 = struct {...@@ -175,8 +175,8 @@ pub const Secp256k1 = struct {
175 }175 }
176176
177 /// Return a random point.177 /// Return a random point.
178 pub fn random() Secp256k1 {178 pub fn random(io: std.Io) Secp256k1 {
179 const n = scalar.random(.little);179 const n = scalar.random(io, .little);
180 return basePoint.mul(n, .little) catch unreachable;180 return basePoint.mul(n, .little) catch unreachable;
181 }181 }
182182
lib/std/crypto/pcurves/secp256k1/scalar.zig+4-4
...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)...@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
68}68}
6969
70/// Return a random scalar70/// Return a random scalar
71pub fn random(endian: std.builtin.Endian) CompressedScalar {71pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
72 return Scalar.random().toBytes(endian);72 return Scalar.random(io).toBytes(endian);
73}73}
7474
75/// A scalar in unpacked representation.75/// A scalar in unpacked representation.
...@@ -170,10 +170,10 @@ pub const Scalar = struct {...@@ -170,10 +170,10 @@ pub const Scalar = struct {
170 }170 }
171171
172 /// Return a random scalar < L.172 /// Return a random scalar < L.
173 pub fn random() Scalar {173 pub fn random(io: std.Io) Scalar {
174 var s: [48]u8 = undefined;174 var s: [48]u8 = undefined;
175 while (true) {175 while (true) {
176 crypto.random.bytes(&s);176 io.random(&s);
177 const n = Scalar.fromBytes48(s, .little);177 const n = Scalar.fromBytes48(s, .little);
178 if (!n.isZero()) {178 if (!n.isZero()) {
179 return n;179 return n;
lib/std/crypto/pcurves/tests/p256.zig+11-6
...@@ -5,8 +5,9 @@ const testing = std.testing;...@@ -5,8 +5,9 @@ const testing = std.testing;
5const P256 = @import("../p256.zig").P256;5const P256 = @import("../p256.zig").P256;
66
7test "p256 ECDH key exchange" {7test "p256 ECDH key exchange" {
8 const dha = P256.scalar.random(.little);8 const io = testing.io;
9 const dhb = P256.scalar.random(.little);9 const dha = P256.scalar.random(io, .little);
10 const dhb = P256.scalar.random(io, .little);
10 const dhA = try P256.basePoint.mul(dha, .little);11 const dhA = try P256.basePoint.mul(dha, .little);
11 const dhB = try P256.basePoint.mul(dhb, .little);12 const dhB = try P256.basePoint.mul(dhb, .little);
12 const shareda = try dhA.mul(dhb, .little);13 const shareda = try dhA.mul(dhb, .little);
...@@ -66,28 +67,32 @@ test "p256 test vectors - doubling" {...@@ -66,28 +67,32 @@ test "p256 test vectors - doubling" {
66}67}
6768
68test "p256 compressed sec1 encoding/decoding" {69test "p256 compressed sec1 encoding/decoding" {
69 const p = P256.random();70 const io = testing.io;
71 const p = P256.random(io);
70 const s = p.toCompressedSec1();72 const s = p.toCompressedSec1();
71 const q = try P256.fromSec1(&s);73 const q = try P256.fromSec1(&s);
72 try testing.expect(p.equivalent(q));74 try testing.expect(p.equivalent(q));
73}75}
7476
75test "p256 uncompressed sec1 encoding/decoding" {77test "p256 uncompressed sec1 encoding/decoding" {
76 const p = P256.random();78 const io = testing.io;
79 const p = P256.random(io);
77 const s = p.toUncompressedSec1();80 const s = p.toUncompressedSec1();
78 const q = try P256.fromSec1(&s);81 const q = try P256.fromSec1(&s);
79 try testing.expect(p.equivalent(q));82 try testing.expect(p.equivalent(q));
80}83}
8184
82test "p256 public key is the neutral element" {85test "p256 public key is the neutral element" {
86 const io = testing.io;
83 const n = P256.scalar.Scalar.zero.toBytes(.little);87 const n = P256.scalar.Scalar.zero.toBytes(.little);
84 const p = P256.random();88 const p = P256.random(io);
85 try testing.expectError(error.IdentityElement, p.mul(n, .little));89 try testing.expectError(error.IdentityElement, p.mul(n, .little));
86}90}
8791
88test "p256 public key is the neutral element (public verification)" {92test "p256 public key is the neutral element (public verification)" {
93 const io = testing.io;
89 const n = P256.scalar.Scalar.zero.toBytes(.little);94 const n = P256.scalar.Scalar.zero.toBytes(.little);
90 const p = P256.random();95 const p = P256.random(io);
91 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));96 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
92}97}
9398
lib/std/crypto/pcurves/tests/p384.zig+11-6
...@@ -5,8 +5,9 @@ const testing = std.testing;...@@ -5,8 +5,9 @@ const testing = std.testing;
5const P384 = @import("../p384.zig").P384;5const P384 = @import("../p384.zig").P384;
66
7test "p384 ECDH key exchange" {7test "p384 ECDH key exchange" {
8 const dha = P384.scalar.random(.little);8 const io = testing.io;
9 const dhb = P384.scalar.random(.little);9 const dha = P384.scalar.random(io, .little);
10 const dhb = P384.scalar.random(io, .little);
10 const dhA = try P384.basePoint.mul(dha, .little);11 const dhA = try P384.basePoint.mul(dha, .little);
11 const dhB = try P384.basePoint.mul(dhb, .little);12 const dhB = try P384.basePoint.mul(dhb, .little);
12 const shareda = try dhA.mul(dhb, .little);13 const shareda = try dhA.mul(dhb, .little);
...@@ -67,7 +68,8 @@ test "p384 test vectors - doubling" {...@@ -67,7 +68,8 @@ test "p384 test vectors - doubling" {
67}68}
6869
69test "p384 compressed sec1 encoding/decoding" {70test "p384 compressed sec1 encoding/decoding" {
70 const p = P384.random();71 const io = testing.io;
72 const p = P384.random(io);
71 const s0 = p.toUncompressedSec1();73 const s0 = p.toUncompressedSec1();
72 const s = p.toCompressedSec1();74 const s = p.toCompressedSec1();
73 try testing.expectEqualSlices(u8, s0[1..49], s[1..49]);75 try testing.expectEqualSlices(u8, s0[1..49], s[1..49]);
...@@ -76,21 +78,24 @@ test "p384 compressed sec1 encoding/decoding" {...@@ -76,21 +78,24 @@ test "p384 compressed sec1 encoding/decoding" {
76}78}
7779
78test "p384 uncompressed sec1 encoding/decoding" {80test "p384 uncompressed sec1 encoding/decoding" {
79 const p = P384.random();81 const io = testing.io;
82 const p = P384.random(io);
80 const s = p.toUncompressedSec1();83 const s = p.toUncompressedSec1();
81 const q = try P384.fromSec1(&s);84 const q = try P384.fromSec1(&s);
82 try testing.expect(p.equivalent(q));85 try testing.expect(p.equivalent(q));
83}86}
8487
85test "p384 public key is the neutral element" {88test "p384 public key is the neutral element" {
89 const io = testing.io;
86 const n = P384.scalar.Scalar.zero.toBytes(.little);90 const n = P384.scalar.Scalar.zero.toBytes(.little);
87 const p = P384.random();91 const p = P384.random(io);
88 try testing.expectError(error.IdentityElement, p.mul(n, .little));92 try testing.expectError(error.IdentityElement, p.mul(n, .little));
89}93}
9094
91test "p384 public key is the neutral element (public verification)" {95test "p384 public key is the neutral element (public verification)" {
96 const io = testing.io;
92 const n = P384.scalar.Scalar.zero.toBytes(.little);97 const n = P384.scalar.Scalar.zero.toBytes(.little);
93 const p = P384.random();98 const p = P384.random(io);
94 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));99 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
95}100}
96101
lib/std/crypto/pcurves/tests/secp256k1.zig+14-8
...@@ -5,8 +5,9 @@ const testing = std.testing;...@@ -5,8 +5,9 @@ const testing = std.testing;
5const Secp256k1 = @import("../secp256k1.zig").Secp256k1;5const Secp256k1 = @import("../secp256k1.zig").Secp256k1;
66
7test "secp256k1 ECDH key exchange" {7test "secp256k1 ECDH key exchange" {
8 const dha = Secp256k1.scalar.random(.little);8 const io = testing.io;
9 const dhb = Secp256k1.scalar.random(.little);9 const dha = Secp256k1.scalar.random(io, .little);
10 const dhb = Secp256k1.scalar.random(io, .little);
10 const dhA = try Secp256k1.basePoint.mul(dha, .little);11 const dhA = try Secp256k1.basePoint.mul(dha, .little);
11 const dhB = try Secp256k1.basePoint.mul(dhb, .little);12 const dhB = try Secp256k1.basePoint.mul(dhb, .little);
12 const shareda = try dhA.mul(dhb, .little);13 const shareda = try dhA.mul(dhb, .little);
...@@ -15,8 +16,9 @@ test "secp256k1 ECDH key exchange" {...@@ -15,8 +16,9 @@ test "secp256k1 ECDH key exchange" {
15}16}
1617
17test "secp256k1 ECDH key exchange including public multiplication" {18test "secp256k1 ECDH key exchange including public multiplication" {
18 const dha = Secp256k1.scalar.random(.little);19 const io = testing.io;
19 const dhb = Secp256k1.scalar.random(.little);20 const dha = Secp256k1.scalar.random(io, .little);
21 const dhb = Secp256k1.scalar.random(io, .little);
20 const dhA = try Secp256k1.basePoint.mul(dha, .little);22 const dhA = try Secp256k1.basePoint.mul(dha, .little);
21 const dhB = try Secp256k1.basePoint.mulPublic(dhb, .little);23 const dhB = try Secp256k1.basePoint.mulPublic(dhb, .little);
22 const shareda = try dhA.mul(dhb, .little);24 const shareda = try dhA.mul(dhb, .little);
...@@ -77,28 +79,32 @@ test "secp256k1 test vectors - doubling" {...@@ -77,28 +79,32 @@ test "secp256k1 test vectors - doubling" {
77}79}
7880
79test "secp256k1 compressed sec1 encoding/decoding" {81test "secp256k1 compressed sec1 encoding/decoding" {
80 const p = Secp256k1.random();82 const io = testing.io;
83 const p = Secp256k1.random(io);
81 const s = p.toCompressedSec1();84 const s = p.toCompressedSec1();
82 const q = try Secp256k1.fromSec1(&s);85 const q = try Secp256k1.fromSec1(&s);
83 try testing.expect(p.equivalent(q));86 try testing.expect(p.equivalent(q));
84}87}
8588
86test "secp256k1 uncompressed sec1 encoding/decoding" {89test "secp256k1 uncompressed sec1 encoding/decoding" {
87 const p = Secp256k1.random();90 const io = testing.io;
91 const p = Secp256k1.random(io);
88 const s = p.toUncompressedSec1();92 const s = p.toUncompressedSec1();
89 const q = try Secp256k1.fromSec1(&s);93 const q = try Secp256k1.fromSec1(&s);
90 try testing.expect(p.equivalent(q));94 try testing.expect(p.equivalent(q));
91}95}
9296
93test "secp256k1 public key is the neutral element" {97test "secp256k1 public key is the neutral element" {
98 const io = testing.io;
94 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);99 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);
95 const p = Secp256k1.random();100 const p = Secp256k1.random(io);
96 try testing.expectError(error.IdentityElement, p.mul(n, .little));101 try testing.expectError(error.IdentityElement, p.mul(n, .little));
97}102}
98103
99test "secp256k1 public key is the neutral element (public verification)" {104test "secp256k1 public key is the neutral element (public verification)" {
105 const io = testing.io;
100 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);106 const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);
101 const p = Secp256k1.random();107 const p = Secp256k1.random(io);
102 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));108 try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
103}109}
104110
lib/std/crypto/salsa20.zig+19-15
...@@ -533,9 +533,9 @@ pub const SealedBox = struct {...@@ -533,9 +533,9 @@ pub const SealedBox = struct {
533533
534 /// Encrypt a message `m` for a recipient whose public key is `public_key`.534 /// Encrypt a message `m` for a recipient whose public key is `public_key`.
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(io: std.Io, 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 = KeyPair.generate();538 var ekp = KeyPair.generate(io);
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);
...@@ -573,29 +573,31 @@ test "(x)salsa20" {...@@ -573,29 +573,31 @@ test "(x)salsa20" {
573}573}
574574
575test "xsalsa20poly1305" {575test "xsalsa20poly1305" {
576 const io = std.testing.io;
576 var msg: [100]u8 = undefined;577 var msg: [100]u8 = undefined;
577 var msg2: [msg.len]u8 = undefined;578 var msg2: [msg.len]u8 = undefined;
578 var c: [msg.len]u8 = undefined;579 var c: [msg.len]u8 = undefined;
579 var key: [XSalsa20Poly1305.key_length]u8 = undefined;580 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
580 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;581 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
581 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;582 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
582 crypto.random.bytes(&msg);583 io.random(&msg);
583 crypto.random.bytes(&key);584 io.random(&key);
584 crypto.random.bytes(&nonce);585 io.random(&nonce);
585586
586 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);587 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
587 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);588 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
588}589}
589590
590test "xsalsa20poly1305 secretbox" {591test "xsalsa20poly1305 secretbox" {
592 const io = std.testing.io;
591 var msg: [100]u8 = undefined;593 var msg: [100]u8 = undefined;
592 var msg2: [msg.len]u8 = undefined;594 var msg2: [msg.len]u8 = undefined;
593 var key: [XSalsa20Poly1305.key_length]u8 = undefined;595 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
594 var nonce: [Box.nonce_length]u8 = undefined;596 var nonce: [Box.nonce_length]u8 = undefined;
595 var boxed: [msg.len + Box.tag_length]u8 = undefined;597 var boxed: [msg.len + Box.tag_length]u8 = undefined;
596 crypto.random.bytes(&msg);598 io.random(&msg);
597 crypto.random.bytes(&key);599 io.random(&key);
598 crypto.random.bytes(&nonce);600 io.random(&nonce);
599601
600 SecretBox.seal(boxed[0..], msg[0..], nonce, key);602 SecretBox.seal(boxed[0..], msg[0..], nonce, key);
601 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);603 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
...@@ -604,15 +606,16 @@ test "xsalsa20poly1305 secretbox" {...@@ -604,15 +606,16 @@ test "xsalsa20poly1305 secretbox" {
604test "xsalsa20poly1305 box" {606test "xsalsa20poly1305 box" {
605 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299607 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299
606608
609 const io = std.testing.io;
607 var msg: [100]u8 = undefined;610 var msg: [100]u8 = undefined;
608 var msg2: [msg.len]u8 = undefined;611 var msg2: [msg.len]u8 = undefined;
609 var nonce: [Box.nonce_length]u8 = undefined;612 var nonce: [Box.nonce_length]u8 = undefined;
610 var boxed: [msg.len + Box.tag_length]u8 = undefined;613 var boxed: [msg.len + Box.tag_length]u8 = undefined;
611 crypto.random.bytes(&msg);614 io.random(&msg);
612 crypto.random.bytes(&nonce);615 io.random(&nonce);
613616
614 const kp1 = Box.KeyPair.generate();617 const kp1 = Box.KeyPair.generate(io);
615 const kp2 = Box.KeyPair.generate();618 const kp2 = Box.KeyPair.generate(io);
616 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);619 try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);
617 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);620 try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);
618}621}
...@@ -620,13 +623,14 @@ test "xsalsa20poly1305 box" {...@@ -620,13 +623,14 @@ test "xsalsa20poly1305 box" {
620test "xsalsa20poly1305 sealedbox" {623test "xsalsa20poly1305 sealedbox" {
621 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299624 if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299
622625
626 const io = std.testing.io;
623 var msg: [100]u8 = undefined;627 var msg: [100]u8 = undefined;
624 var msg2: [msg.len]u8 = undefined;628 var msg2: [msg.len]u8 = undefined;
625 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;629 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
626 crypto.random.bytes(&msg);630 io.random(&msg);
627631
628 const kp = Box.KeyPair.generate();632 const kp = Box.KeyPair.generate(io);
629 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);633 try SealedBox.seal(io, boxed[0..], msg[0..], kp.public_key);
630 try SealedBox.open(msg2[0..], boxed[0..], kp);634 try SealedBox.open(msg2[0..], boxed[0..], kp);
631}635}
632636
lib/std/crypto/scrypt.zig+5-6
...@@ -20,7 +20,7 @@ const Error = pwhash.Error;...@@ -20,7 +20,7 @@ const Error = pwhash.Error;
2020
21const max_size = math.maxInt(usize);21const max_size = math.maxInt(usize);
22const max_int = max_size >> 1;22const max_int = max_size >> 1;
23const default_salt_len = 32;23pub const default_salt_len = 32;
24const default_hash_len = 32;24const default_hash_len = 32;
25const max_salt_len = 64;25const max_salt_len = 64;
26const max_hash_len = 64;26const max_hash_len = 64;
...@@ -417,10 +417,9 @@ const PhcFormatHasher = struct {...@@ -417,10 +417,9 @@ const PhcFormatHasher = struct {
417 password: []const u8,417 password: []const u8,
418 params: Params,418 params: Params,
419 buf: []u8,419 buf: []u8,
420 /// Filled with cryptographically secure entropy.
421 salt: []const u8,
420 ) HasherError![]const u8 {422 ) HasherError![]const u8 {
421 var salt: [default_salt_len]u8 = undefined;
422 crypto.random.bytes(&salt);
423
424 var hash: [default_hash_len]u8 = undefined;423 var hash: [default_hash_len]u8 = undefined;
425 try kdf(allocator, &hash, password, &salt, params);424 try kdf(allocator, &hash, password, &salt, params);
426425
...@@ -466,9 +465,9 @@ const CryptFormatHasher = struct {...@@ -466,9 +465,9 @@ const CryptFormatHasher = struct {
466 password: []const u8,465 password: []const u8,
467 params: Params,466 params: Params,
468 buf: []u8,467 buf: []u8,
468 /// Filled with cryptographically secure entropy.
469 salt_bin: []const u8,
469 ) HasherError![]const u8 {470 ) HasherError![]const u8 {
470 var salt_bin: [default_salt_len]u8 = undefined;
471 crypto.random.bytes(&salt_bin);
472 const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);471 const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);
473472
474 var hash: [default_hash_len]u8 = undefined;473 var hash: [default_hash_len]u8 = undefined;
lib/std/crypto/timing_safe.zig+12-11
...@@ -180,24 +180,24 @@ pub fn declassify(ptr: anytype) void {...@@ -180,24 +180,24 @@ pub fn declassify(ptr: anytype) void {
180}180}
181181
182test eql {182test eql {
183 const random = std.crypto.random;183 const io = std.testing.io;
184 const expect = std.testing.expect;184 const expect = std.testing.expect;
185 var a: [100]u8 = undefined;185 var a: [100]u8 = undefined;
186 var b: [100]u8 = undefined;186 var b: [100]u8 = undefined;
187 random.bytes(a[0..]);187 io.random(&a);
188 random.bytes(b[0..]);188 io.random(&b);
189 try expect(!eql([100]u8, a, b));189 try expect(!eql([100]u8, a, b));
190 a = b;190 a = b;
191 try expect(eql([100]u8, a, b));191 try expect(eql([100]u8, a, b));
192}192}
193193
194test "eql (vectors)" {194test "eql (vectors)" {
195 const random = std.crypto.random;195 const io = std.testing.io;
196 const expect = std.testing.expect;196 const expect = std.testing.expect;
197 var a: [100]u8 = undefined;197 var a: [100]u8 = undefined;
198 var b: [100]u8 = undefined;198 var b: [100]u8 = undefined;
199 random.bytes(a[0..]);199 io.random(&a);
200 random.bytes(b[0..]);200 io.random(&b);
201 const v1: @Vector(100, u8) = a;201 const v1: @Vector(100, u8) = a;
202 const v2: @Vector(100, u8) = b;202 const v2: @Vector(100, u8) = b;
203 try expect(!eql(@Vector(100, u8), v1, v2));203 try expect(!eql(@Vector(100, u8), v1, v2));
...@@ -220,9 +220,10 @@ test compare {...@@ -220,9 +220,10 @@ test compare {
220}220}
221221
222test "add and sub" {222test "add and sub" {
223 const io = std.testing.io;
224
223 const expectEqual = std.testing.expectEqual;225 const expectEqual = std.testing.expectEqual;
224 const expectEqualSlices = std.testing.expectEqualSlices;226 const expectEqualSlices = std.testing.expectEqualSlices;
225 const random = std.crypto.random;
226 const len = 32;227 const len = 32;
227 var a: [len]u8 = undefined;228 var a: [len]u8 = undefined;
228 var b: [len]u8 = undefined;229 var b: [len]u8 = undefined;
...@@ -230,8 +231,8 @@ test "add and sub" {...@@ -230,8 +231,8 @@ test "add and sub" {
230 const zero = [_]u8{0} ** len;231 const zero = [_]u8{0} ** len;
231 var iterations: usize = 100;232 var iterations: usize = 100;
232 while (iterations != 0) : (iterations -= 1) {233 while (iterations != 0) : (iterations -= 1) {
233 random.bytes(&a);234 io.random(&a);
234 random.bytes(&b);235 io.random(&b);
235 const endian = if (iterations % 2 == 0) Endian.big else Endian.little;236 const endian = if (iterations % 2 == 0) Endian.big else Endian.little;
236 _ = sub(u8, &a, &b, &c, endian); // a-b237 _ = sub(u8, &a, &b, &c, endian); // a-b
237 _ = add(u8, &c, &b, &c, endian); // (a-b)+b238 _ = add(u8, &c, &b, &c, endian); // (a-b)+b
...@@ -243,11 +244,11 @@ test "add and sub" {...@@ -243,11 +244,11 @@ test "add and sub" {
243}244}
244245
245test classify {246test classify {
246 const random = std.crypto.random;247 const io = std.testing.io;
247 const expect = std.testing.expect;248 const expect = std.testing.expect;
248249
249 var secret: [32]u8 = undefined;250 var secret: [32]u8 = undefined;
250 random.bytes(&secret);251 io.random(&secret);
251252
252 // Input of the hash function is marked as secret253 // Input of the hash function is marked as secret
253 classify(&secret);254 classify(&secret);
lib/std/crypto/tls/Client.zig+7-7
...@@ -109,7 +109,7 @@ pub const Options = struct {...@@ -109,7 +109,7 @@ pub const Options = struct {
109 read_buffer: []u8,109 read_buffer: []u8,
110 /// Cryptographically secure random bytes. The pointer is not captured; data is only110 /// Cryptographically secure random bytes. The pointer is not captured; data is only
111 /// read during `init`.111 /// read during `init`.
112 entropy: *const [176]u8,112 entropy: *const [240]u8,
113 /// Current time according to the wall clock / calendar, in seconds.113 /// Current time according to the wall clock / calendar, in seconds.
114 realtime_now_seconds: i64,114 realtime_now_seconds: i64,
115115
...@@ -200,7 +200,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client...@@ -200,7 +200,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
200 var server_hello_rand: [32]u8 = undefined;200 var server_hello_rand: [32]u8 = undefined;
201 const legacy_session_id = options.entropy[32..64].*;201 const legacy_session_id = options.entropy[32..64].*;
202202
203 var key_share = KeyShare.init(options.entropy[64..176].*) catch |err| switch (err) {203 var key_share = KeyShare.init(options.entropy[64..240]) catch |err| switch (err) {
204 // Only possible to happen if the seed is all zeroes.204 // Only possible to happen if the seed is all zeroes.
205 error.IdentityElement => return error.InsufficientEntropy,205 error.IdentityElement => return error.InsufficientEntropy,
206 };206 };
...@@ -1330,12 +1330,12 @@ const KeyShare = struct {...@@ -1330,12 +1330,12 @@ const KeyShare = struct {
1330 crypto.dh.X25519.shared_length,1330 crypto.dh.X25519.shared_length,
1331 );1331 );
13321332
1333 fn init(seed: [112]u8) error{IdentityElement}!KeyShare {1333 fn init(seed: *const [176]u8) error{IdentityElement}!KeyShare {
1334 return .{1334 return .{
1335 .ml_kem768_kp = .generate(),1335 .ml_kem768_kp = try .generateDeterministic(seed[0..64].*),
1336 .secp256r1_kp = try .generateDeterministic(seed[0..32].*),1336 .secp256r1_kp = try .generateDeterministic(seed[64..96].*),
1337 .secp384r1_kp = try .generateDeterministic(seed[32..80].*),1337 .secp384r1_kp = try .generateDeterministic(seed[96..144].*),
1338 .x25519_kp = try .generateDeterministic(seed[80..112].*),1338 .x25519_kp = try .generateDeterministic(seed[144..176].*),
1339 .sk_buf = undefined,1339 .sk_buf = undefined,
1340 .sk_len = 0,1340 .sk_len = 0,
1341 };1341 };
lib/std/fs/test.zig+1-1
...@@ -1761,7 +1761,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1761,7 +1761,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1761 const io = testing.io;1761 const io = testing.io;
17621762
1763 var random_bytes: [12]u8 = undefined;1763 var random_bytes: [12]u8 = undefined;
1764 std.crypto.random.bytes(&random_bytes);1764 io.random(&random_bytes);
17651765
1766 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;1766 var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
1767 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);1767 _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);
lib/std/http/Client.zig+2-2
...@@ -321,8 +321,8 @@ pub const Connection = struct {...@@ -321,8 +321,8 @@ pub const Connection = struct {
321 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);321 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);
322 @memcpy(host_buffer, remote_host.bytes);322 @memcpy(host_buffer, remote_host.bytes);
323 const tls: *Tls = @ptrCast(base);323 const tls: *Tls = @ptrCast(base);
324 var random_buffer: [176]u8 = undefined;324 var random_buffer: [240]u8 = undefined;
325 std.crypto.random.bytes(&random_buffer);325 io.random(&random_buffer);
326 tls.* = .{326 tls.* = .{
327 .connection = .{327 .connection = .{
328 .client = client,328 .client = client,
lib/std/os/linux/tls.zig+5-7
...@@ -531,13 +531,11 @@ pub fn prepareArea(area: []u8) usize {...@@ -531,13 +531,11 @@ pub fn prepareArea(area: []u8) usize {
531 };531 };
532}532}
533533
534/// The main motivation for the size chosen here is that this is how much ends up being requested for534/// The main motivation for the size chosen here is to be larger than total
535/// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up535/// amount of thread-local variables for most programs. Putting this allocation
536/// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned536/// in the ELF like this is equivalent to moving the `mmap` call below into the
537/// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I537/// kernel, avoiding syscall overhead.
538/// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is538var main_thread_area_buffer: [0x1000]u8 align(page_size_min) = undefined;
539/// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
540var main_thread_area_buffer: [0x2100]u8 align(page_size_min) = undefined;
541539
542/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,540/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,
543/// and assigns the architecture-specific value to the TP register.541/// and assigns the architecture-specific value to the TP register.
lib/std/posix/test.zig-10
...@@ -33,16 +33,6 @@ test "check WASI CWD" {...@@ -33,16 +33,6 @@ test "check WASI CWD" {
33 }33 }
34}34}
3535
36test "getrandom" {
37 var buf_a: [50]u8 = undefined;
38 var buf_b: [50]u8 = undefined;
39 try posix.getrandom(&buf_a);
40 try posix.getrandom(&buf_b);
41 // If this test fails the chance is significantly higher that there is a bug than
42 // that two sets of 50 bytes were equal.
43 try expect(!mem.eql(u8, &buf_a, &buf_b));
44}
45
46test "getuid" {36test "getuid" {
47 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;37 if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
48 _ = posix.getuid();38 _ = posix.getuid();
lib/std/testing.zig+1-1
...@@ -631,7 +631,7 @@ pub const TmpDir = struct {...@@ -631,7 +631,7 @@ pub const TmpDir = struct {
631pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {631pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {
632 comptime assert(builtin.is_test);632 comptime assert(builtin.is_test);
633 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;633 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
634 std.crypto.random.bytes(&random_bytes);634 io.random(&random_bytes);
635 var sub_path: [TmpDir.sub_path_len]u8 = undefined;635 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
636 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);636 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
637637
src/Compilation.zig+13-4
...@@ -2942,7 +2942,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE...@@ -2942,7 +2942,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
2942 .none => |none| {2942 .none => |none| {
2943 assert(none.tmp_artifact_directory == null);2943 assert(none.tmp_artifact_directory == null);
2944 none.tmp_artifact_directory = d: {2944 none.tmp_artifact_directory = d: {
2945 tmp_dir_rand_int = std.crypto.random.int(u64);2945 io.random(@ptrCast(&tmp_dir_rand_int));
2946 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);2946 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
2947 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});2947 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
2948 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {2948 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
...@@ -3023,7 +3023,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE...@@ -3023,7 +3023,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
30233023
3024 // Compile the artifacts to a temporary directory.3024 // Compile the artifacts to a temporary directory.
3025 whole.tmp_artifact_directory = d: {3025 whole.tmp_artifact_directory = d: {
3026 tmp_dir_rand_int = std.crypto.random.int(u64);3026 io.random(@ptrCast(&tmp_dir_rand_int));
3027 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);3027 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
3028 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});3028 const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
3029 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {3029 const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
...@@ -5759,7 +5759,11 @@ pub fn translateC(...@@ -5759,7 +5759,11 @@ pub fn translateC(
57595759
5760 const gpa = comp.gpa;5760 const gpa = comp.gpa;
5761 const io = comp.io;5761 const io = comp.io;
5762 const tmp_basename = std.fmt.hex(std.crypto.random.int(u64));5762 const tmp_basename = r: {
5763 var x: u64 = undefined;
5764 io.random(@ptrCast(&x));
5765 break :r std.fmt.hex(x);
5766 };
5763 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;5767 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
5764 const cache_dir = comp.dirs.local_cache.handle;5768 const cache_dir = comp.dirs.local_cache.handle;
5765 var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{});5769 var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{});
...@@ -6889,8 +6893,13 @@ fn spawnZigRc(...@@ -6889,8 +6893,13 @@ fn spawnZigRc(
6889}6893}
68906894
6891pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {6895pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
6896 const io = comp.io;
6897 const rand_int = r: {
6898 var x: u64 = undefined;
6899 io.random(@ptrCast(&x));
6900 break :r x;
6901 };
6892 const s = fs.path.sep_str;6902 const s = fs.path.sep_str;
6893 const rand_int = std.crypto.random.int(u64);
6894 if (comp.dirs.local_cache.path) |p| {6903 if (comp.dirs.local_cache.path) |p| {
6895 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });6904 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
6896 } else {6905 } else {
src/Package.zig+2-2
...@@ -14,9 +14,9 @@ pub const Fingerprint = packed struct(u64) {...@@ -14,9 +14,9 @@ pub const Fingerprint = packed struct(u64) {
14 id: u32,14 id: u32,
15 checksum: u32,15 checksum: u32,
1616
17 pub fn generate(name: []const u8) Fingerprint {17 pub fn generate(rng: std.Random, name: []const u8) Fingerprint {
18 return .{18 return .{
19 .id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff),19 .id = rng.intRangeLessThan(u32, 1, 0xffffffff),
20 .checksum = std.hash.Crc32.hash(name),20 .checksum = std.hash.Crc32.hash(name),
21 };21 };
22 }22 }
src/Package/Fetch.zig+13-3
...@@ -494,7 +494,11 @@ fn runResource(...@@ -494,7 +494,11 @@ fn runResource(
494 const eb = &f.error_bundle;494 const eb = &f.error_bundle;
495 const s = fs.path.sep_str;495 const s = fs.path.sep_str;
496 const cache_root = f.job_queue.global_cache;496 const cache_root = f.job_queue.global_cache;
497 const rand_int = std.crypto.random.int(u64);497 const rand_int = r: {
498 var x: u64 = undefined;
499 io.random(@ptrCast(&x));
500 break :r x;
501 };
498 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(rand_int);502 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(rand_int);
499503
500 const package_sub_path = blk: {504 const package_sub_path = blk: {
...@@ -690,7 +694,9 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {...@@ -690,7 +694,9 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
690 return error.FetchFailed;694 return error.FetchFailed;
691 }695 }
692696
693 f.manifest = try Manifest.parse(arena, ast.*, .{697 const rng: std.Random.IoSource = .{ .io = io };
698
699 f.manifest = try Manifest.parse(arena, ast.*, rng.interface(), .{
694 .allow_missing_paths_field = f.allow_missing_paths_field,700 .allow_missing_paths_field = f.allow_missing_paths_field,
695 .allow_missing_fingerprint = f.allow_missing_fingerprint,701 .allow_missing_fingerprint = f.allow_missing_fingerprint,
696 .allow_name_string = f.allow_name_string,702 .allow_name_string = f.allow_name_string,
...@@ -1305,7 +1311,11 @@ fn unzip(...@@ -1305,7 +1311,11 @@ fn unzip(
1305 zip_path[prefix.len + random_len ..].* = suffix.*;1311 zip_path[prefix.len + random_len ..].* = suffix.*;
13061312
1307 var zip_file = while (true) {1313 var zip_file = while (true) {
1308 const random_integer = std.crypto.random.int(u64);1314 const random_integer = r: {
1315 var x: u64 = undefined;
1316 io.random(@ptrCast(&x));
1317 break :r x;
1318 };
1309 zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer);1319 zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer);
13101320
1311 break cache_root.handle.createFile(io, &zip_path, .{1321 break cache_root.handle.createFile(io, &zip_path, .{
src/Package/Manifest.zig+5-5
...@@ -57,7 +57,7 @@ pub const ParseOptions = struct {...@@ -57,7 +57,7 @@ pub const ParseOptions = struct {
5757
58pub const Error = Allocator.Error;58pub const Error = Allocator.Error;
5959
60pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {60pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) Error!Manifest {
61 const main_node_index = ast.nodeData(.root).node;61 const main_node_index = ast.nodeData(.root).node;
6262
63 var arena_instance = std.heap.ArenaAllocator.init(gpa);63 var arena_instance = std.heap.ArenaAllocator.init(gpa);
...@@ -87,7 +87,7 @@ pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {...@@ -87,7 +87,7 @@ pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {
87 defer p.dependencies.deinit(gpa);87 defer p.dependencies.deinit(gpa);
88 defer p.paths.deinit(gpa);88 defer p.paths.deinit(gpa);
8989
90 p.parseRoot(main_node_index) catch |err| switch (err) {90 p.parseRoot(main_node_index, rng) catch |err| switch (err) {
91 error.ParseFailure => assert(p.errors.items.len > 0),91 error.ParseFailure => assert(p.errors.items.len > 0),
92 else => |e| return e,92 else => |e| return e,
93 };93 };
...@@ -157,7 +157,7 @@ const Parse = struct {...@@ -157,7 +157,7 @@ const Parse = struct {
157157
158 const InnerError = error{ ParseFailure, OutOfMemory };158 const InnerError = error{ ParseFailure, OutOfMemory };
159159
160 fn parseRoot(p: *Parse, node: Ast.Node.Index) !void {160 fn parseRoot(p: *Parse, node: Ast.Node.Index, rng: std.Random) !void {
161 const ast = p.ast;161 const ast = p.ast;
162 const main_token = ast.nodeMainToken(node);162 const main_token = ast.nodeMainToken(node);
163163
...@@ -217,13 +217,13 @@ const Parse = struct {...@@ -217,13 +217,13 @@ const Parse = struct {
217 if (fingerprint) |n| {217 if (fingerprint) |n| {
218 if (!n.validate(p.name)) {218 if (!n.validate(p.name)) {
219 return fail(p, main_token, "invalid fingerprint: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{219 return fail(p, main_token, "invalid fingerprint: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{
220 n.int(), Package.Fingerprint.generate(p.name).int(),220 n.int(), Package.Fingerprint.generate(rng, p.name).int(),
221 });221 });
222 }222 }
223 p.id = n.id;223 p.id = n.id;
224 } else if (!p.allow_missing_fingerprint) {224 } else if (!p.allow_missing_fingerprint) {
225 try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{225 try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{
226 Package.Fingerprint.generate(p.name).int(),226 Package.Fingerprint.generate(rng, p.name).int(),
227 });227 });
228 } else {228 } else {
229 p.id = 0;229 p.id = 0;
src/link.zig+6-1
...@@ -616,8 +616,13 @@ pub const File = struct {...@@ -616,8 +616,13 @@ pub const File = struct {
616 // it will return ETXTBSY. So instead, we copy the file, atomically rename it616 // it will return ETXTBSY. So instead, we copy the file, atomically rename it
617 // over top of the exe path, and then proceed normally. This changes the inode,617 // over top of the exe path, and then proceed normally. This changes the inode,
618 // avoiding the error.618 // avoiding the error.
619 const random_integer = r: {
620 var x: u32 = undefined;
621 io.random(@ptrCast(&x));
622 break :r x;
623 };
619 const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{624 const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{
620 emit.sub_path, std.crypto.random.int(u32),625 emit.sub_path, random_integer,
621 });626 });
622 defer gpa.free(tmp_sub_path);627 defer gpa.free(tmp_sub_path);
623 try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, io, .{});628 try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, io, .{});
src/link/Lld.zig+5-1
...@@ -1636,7 +1636,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi...@@ -1636,7 +1636,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
1636 const err = switch (first_err) {1636 const err = switch (first_err) {
1637 error.NameTooLong => err: {1637 error.NameTooLong => err: {
1638 const s = fs.path.sep_str;1638 const s = fs.path.sep_str;
1639 const rand_int = std.crypto.random.int(u64);1639 const rand_int = r: {
1640 var x: u64 = undefined;
1641 io.random(@ptrCast(&x));
1642 break :r x;
1643 };
1640 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";1644 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
16411645
1642 const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{});1646 const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{});
src/main.zig+19-12
...@@ -3395,7 +3395,7 @@ fn buildOutputType(...@@ -3395,7 +3395,7 @@ fn buildOutputType(
3395 // "-" is stdin. Dump it to a real file.3395 // "-" is stdin. Dump it to a real file.
3396 const sep = fs.path.sep_str;3396 const sep = fs.path.sep_str;
3397 const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{3397 const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{
3398 std.crypto.random.int(u64), ext.canonicalName(target),3398 randInt(io, u64), ext.canonicalName(target),
3399 });3399 });
3400 try dirs.local_cache.handle.createDirPath(io, "tmp");3400 try dirs.local_cache.handle.createDirPath(io, "tmp");
34013401
...@@ -4433,7 +4433,7 @@ fn runOrTest(...@@ -4433,7 +4433,7 @@ fn runOrTest(
4433 try argv.append(exe_path);4433 try argv.append(exe_path);
4434 if (arg_mode == .zig_test) {4434 if (arg_mode == .zig_test) {
4435 try argv.append(4435 try argv.append(
4436 try std.fmt.allocPrint(arena, "--seed=0x{x}", .{std.crypto.random.int(u32)}),4436 try std.fmt.allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)}),
4437 );4437 );
4438 }4438 }
4439 } else {4439 } else {
...@@ -4763,7 +4763,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !...@@ -4763,7 +4763,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
4763 const cwd_basename = fs.path.basename(cwd_path);4763 const cwd_basename = fs.path.basename(cwd_path);
4764 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);4764 const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);
47654765
4766 const fingerprint: Package.Fingerprint = .generate(sanitized_root_name);4766 const rng: std.Random.IoSource = .{ .io = io };
4767 const fingerprint: Package.Fingerprint = .generate(rng.interface(), sanitized_root_name);
47674768
4768 switch (template) {4769 switch (template) {
4769 .example => {4770 .example => {
...@@ -4919,7 +4920,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -4919,7 +4920,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
49194920
4920 try child_argv.appendSlice(&.{4921 try child_argv.appendSlice(&.{
4921 "--seed",4922 "--seed",
4922 try std.fmt.allocPrint(arena, "0x{x}", .{std.crypto.random.int(u32)}),4923 try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}),
4923 });4924 });
4924 const argv_index_seed = child_argv.items.len - 1;4925 const argv_index_seed = child_argv.items.len - 1;
49254926
...@@ -4937,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,...@@ -4937,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
4937 // the strategy is to choose a temporary file name ahead of time, and then4938 // the strategy is to choose a temporary file name ahead of time, and then
4938 // read this file in the parent to obtain the results, in the case the child4939 // read this file in the parent to obtain the results, in the case the child
4939 // exits with code 3.4940 // exits with code 3.
4940 const results_tmp_file_nonce = std.fmt.hex(std.crypto.random.int(u64));4941 const results_tmp_file_nonce = std.fmt.hex(randInt(io, u64));
4941 try child_argv.append("-Z" ++ results_tmp_file_nonce);4942 try child_argv.append("-Z" ++ results_tmp_file_nonce);
49424943
4943 var color: Color = .auto;4944 var color: Color = .auto;
...@@ -7223,7 +7224,7 @@ fn createDependenciesModule(...@@ -7223,7 +7224,7 @@ fn createDependenciesModule(
7223) !*Package.Module {7224) !*Package.Module {
7224 // Atomically create the file in a directory named after the hash of its contents.7225 // Atomically create the file in a directory named after the hash of its contents.
7225 const basename = "dependencies.zig";7226 const basename = "dependencies.zig";
7226 const rand_int = std.crypto.random.int(u64);7227 const rand_int = randInt(io, u64);
7227 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);7228 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
7228 {7229 {
7229 var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{});7230 var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{});
...@@ -7339,6 +7340,8 @@ fn loadManifest(...@@ -7339,6 +7340,8 @@ fn loadManifest(
7339 io: Io,7340 io: Io,
7340 options: LoadManifestOptions,7341 options: LoadManifestOptions,
7341) !struct { Package.Manifest, Ast } {7342) !struct { Package.Manifest, Ast } {
7343 const rng: std.Random.IoSource = .{ .io = io };
7344
7342 const manifest_bytes = while (true) {7345 const manifest_bytes = while (true) {
7343 break options.dir.readFileAllocOptions(7346 break options.dir.readFileAllocOptions(
7344 io,7347 io,
...@@ -7360,15 +7363,13 @@ fn loadManifest(...@@ -7360,15 +7363,13 @@ fn loadManifest(
7360 , .{7363 , .{
7361 options.root_name,7364 options.root_name,
7362 build_options.version,7365 build_options.version,
7363 Package.Fingerprint.generate(options.root_name).int(),7366 Package.Fingerprint.generate(rng.interface(), options.root_name).int(),
7364 }) catch |e| {7367 }) catch |e| {
7365 fatal("unable to write {s}: {s}", .{ Package.Manifest.basename, @errorName(e) });7368 fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e });
7366 };7369 };
7367 continue;7370 continue;
7368 },7371 },
7369 else => |e| fatal("unable to load {s}: {s}", .{7372 else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }),
7370 Package.Manifest.basename, @errorName(e),
7371 }),
7372 };7373 };
7373 };7374 };
7374 var ast = try Ast.parse(gpa, manifest_bytes, .zon);7375 var ast = try Ast.parse(gpa, manifest_bytes, .zon);
...@@ -7379,7 +7380,7 @@ fn loadManifest(...@@ -7379,7 +7380,7 @@ fn loadManifest(
7379 process.exit(2);7380 process.exit(2);
7380 }7381 }
73817382
7382 var manifest = try Package.Manifest.parse(gpa, ast, .{});7383 var manifest = try Package.Manifest.parse(gpa, ast, rng.interface(), .{});
7383 errdefer manifest.deinit(gpa);7384 errdefer manifest.deinit(gpa);
73847385
7385 if (manifest.errors.len > 0) {7386 if (manifest.errors.len > 0) {
...@@ -7632,3 +7633,9 @@ fn setThreadLimit(n: usize) void {...@@ -7632,3 +7633,9 @@ fn setThreadLimit(n: usize) void {
7632 threaded_impl_ptr.setAsyncLimit(limit);7633 threaded_impl_ptr.setAsyncLimit(limit);
7633 threaded_impl_ptr.concurrent_limit = limit;7634 threaded_impl_ptr.concurrent_limit = limit;
7634}7635}
7636
7637fn randInt(io: Io, comptime T: type) T {
7638 var x: T = undefined;
7639 io.random(@ptrCast(&x));
7640 return x;
7641}
test/standalone/simple/guess_number/main.zig+2-1
...@@ -10,7 +10,8 @@ pub fn main(init: std.process.Init) !void {...@@ -10,7 +10,8 @@ pub fn main(init: std.process.Init) !void {
1010
11 try out.writeAll("Welcome to the Guess Number Game in Zig.\n");11 try out.writeAll("Welcome to the Guess Number Game in Zig.\n");
1212
13 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;13 var rng: std.Random.IoSource = .{ .io = init.io };
14 const answer = rng.interface().intRangeLessThan(u8, 0, 100) + 1;
1415
15 while (true) {16 while (true) {
16 try out.writeAll("\nGuess a number between 1 and 100: ");17 try out.writeAll("\nGuess a number between 1 and 100: ");
tools/doctest.zig+4-3
...@@ -78,9 +78,10 @@ pub fn main(init: std.process.Init) !void {...@@ -78,9 +78,10 @@ pub fn main(init: std.process.Init) !void {
78 const code = try parseManifest(arena, source_bytes);78 const code = try parseManifest(arena, source_bytes);
79 const source = stripManifest(source_bytes);79 const source = stripManifest(source_bytes);
8080
81 const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{81 var random_integer: u64 = undefined;
82 cache_root, std.crypto.random.int(u64),82 io.random(@ptrCast(&random_integer));
83 });83
84 const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{ cache_root, random_integer });
84 Dir.cwd().createDirPath(io, tmp_dir_path) catch |err|85 Dir.cwd().createDirPath(io, tmp_dir_path) catch |err|
85 fatal("unable to create tmp dir '{s}': {t}", .{ tmp_dir_path, err });86 fatal("unable to create tmp dir '{s}': {t}", .{ tmp_dir_path, err });
86 defer Dir.cwd().deleteTree(io, tmp_dir_path) catch |err| std.log.err("unable to delete '{s}': {t}", .{87 defer Dir.cwd().deleteTree(io, tmp_dir_path) catch |err| std.log.err("unable to delete '{s}': {t}", .{
tools/incr-check.zig+9-4
...@@ -100,7 +100,7 @@ pub fn main(init: std.process.Init) !void {...@@ -100,7 +100,7 @@ pub fn main(init: std.process.Init) !void {
100 const prog_node = std.Progress.start(io, .{});100 const prog_node = std.Progress.start(io, .{});
101 defer prog_node.end();101 defer prog_node.end();
102102
103 const rand_int = std.crypto.random.int(u64);103 const rand_int = rand64(io);
104 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);104 const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);
105 var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{});105 var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{});
106 defer {106 defer {
...@@ -452,20 +452,19 @@ const Eval = struct {...@@ -452,20 +452,19 @@ const Eval = struct {
452 std.debug.assert(eval.target.backend == .sema);452 std.debug.assert(eval.target.backend == .sema);
453 return;453 return;
454 };454 };
455 const io = eval.io;
455456
456 const binary_path = switch (eval.target.backend) {457 const binary_path = switch (eval.target.backend) {
457 .sema => unreachable,458 .sema => unreachable,
458 .selfhosted, .llvm => emitted_path,459 .selfhosted, .llvm => emitted_path,
459 .cbe => bin: {460 .cbe => bin: {
460 const rand_int = std.crypto.random.int(u64);461 const rand_int = rand64(io);
461 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);462 const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
462 try eval.buildCOutput(emitted_path, out_bin_name, prog_node);463 try eval.buildCOutput(emitted_path, out_bin_name, prog_node);
463 break :bin out_bin_name;464 break :bin out_bin_name;
464 },465 },
465 };466 };
466467
467 const io = eval.io;
468
469 var argv_buf: [2][]const u8 = undefined;468 var argv_buf: [2][]const u8 = undefined;
470 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(469 const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(
471 io,470 io,
...@@ -957,3 +956,9 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {...@@ -957,3 +956,9 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {
957 .msg = message,956 .msg = message,
958 };957 };
959}958}
959
960fn rand64(io: Io) u64 {
961 var x: u64 = undefined;
962 io.random(@ptrCast(&x));
963 return x;
964}