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

This commit is contained in:
Andrew Kelley
2026-01-05 20:42:37 -08:00
parent 81a35a86ea
commit 1f1381a866
45 changed files with 514 additions and 336 deletions
+2 -1
View File
@@ -1217,11 +1217,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u
}
fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {
const io = d.comp.io;
const random_bytes_count = 12;
const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
var random_bytes: [random_bytes_count]u8 = undefined;
std.crypto.random.bytes(&random_bytes);
io.random(&random_bytes);
var random_name: [sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&random_name, &random_bytes);
+49 -16
View File
@@ -78,7 +78,7 @@ pub const Csprng = struct {
pub const seed_len = std.Random.DefaultCsprng.secret_seed_length;
pub fn isInitialized(c: *const Csprng) bool {
return c.rng.offset == std.math.maxInt(usize);
return c.rng.offset != std.math.maxInt(usize);
}
};
@@ -2395,11 +2395,11 @@ fn dirCreateDirPath(
status = .created;
} else |err| switch (err) {
error.PathAlreadyExists => {
// stat the file and return an error if it's not a directory
// this is important because otherwise a dangling symlink
// could cause an infinite loop
const fstat = try dirStatFile(t, dir, component.path, .{});
if (fstat.kind != .directory) return error.NotDir;
// It is important to return an error if it's not a directory
// because otherwise a dangling symlink could cause an infinite
// loop.
const kind = try filePathKind(t, dir, component.path);
if (kind != .directory) return error.NotDir;
},
error.FileNotFound => |e| {
component = it.previous() orelse return e;
@@ -2740,6 +2740,35 @@ fn dirStatFileWasi(
}
}
fn filePathKind(t: *Threaded, dir: Dir, sub_path: []const u8) !File.Kind {
if (native_os == .linux) {
var path_buffer: [posix.PATH_MAX]u8 = undefined;
const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
const linux = std.os.linux;
const syscall: Syscall = try .start();
while (true) {
var statx = std.mem.zeroes(linux.Statx);
switch (linux.errno(linux.statx(dir.handle, sub_path_posix, 0, .{ .TYPE = true }, &statx))) {
.SUCCESS => {
syscall.finish();
if (!statx.mask.TYPE) return error.Unexpected;
return statxKind(statx.mode);
},
.INTR => {
try syscall.checkCancel();
continue;
},
.NOMEM => return syscall.fail(error.SystemResources),
else => |err| return syscall.unexpectedErrno(err),
}
}
}
const stat = try dirStatFile(t, dir, sub_path, .{});
return stat.kind;
}
fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
const t: *Threaded = @ptrCast(@alignCast(userdata));
@@ -12310,16 +12339,7 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
.nlink = stx.nlink,
.size = stx.size,
.permissions = .fromMode(stx.mode),
.kind = switch (stx.mode & std.os.linux.S.IFMT) {
std.os.linux.S.IFDIR => .directory,
std.os.linux.S.IFCHR => .character_device,
std.os.linux.S.IFBLK => .block_device,
std.os.linux.S.IFREG => .file,
std.os.linux.S.IFIFO => .named_pipe,
std.os.linux.S.IFLNK => .sym_link,
std.os.linux.S.IFSOCK => .unix_domain_socket,
else => .unknown,
},
.kind = statxKind(stx.mode),
.atime = if (!stx.mask.ATIME) null else .{
.nanoseconds = @intCast(@as(i128, stx.atime.sec) * std.time.ns_per_s + stx.atime.nsec),
},
@@ -12328,6 +12348,19 @@ fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
};
}
fn statxKind(stx_mode: u16) File.Kind {
return switch (stx_mode & std.os.linux.S.IFMT) {
std.os.linux.S.IFDIR => .directory,
std.os.linux.S.IFCHR => .character_device,
std.os.linux.S.IFBLK => .block_device,
std.os.linux.S.IFREG => .file,
std.os.linux.S.IFIFO => .named_pipe,
std.os.linux.S.IFLNK => .sym_link,
std.os.linux.S.IFSOCK => .unix_domain_socket,
else => .unknown,
};
}
fn statFromPosix(st: *const posix.Stat) File.Stat {
const atime = st.atime();
const mtime = st.mtime();
+3 -3
View File
@@ -275,7 +275,7 @@ test "listen on a unix socket, send bytes, receive bytes" {
const io = testing.io;
const socket_path = try generateFileName("socket.unix");
const socket_path = try generateFileName(io, "socket.unix");
defer testing.allocator.free(socket_path);
const socket_addr = try net.UnixAddress.init(socket_path);
@@ -308,11 +308,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
}
fn generateFileName(base_name: []const u8) ![]const u8 {
fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {
const random_bytes_count = 12;
const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
var random_bytes: [12]u8 = undefined;
std.crypto.random.bytes(&random_bytes);
io.random(&random_bytes);
var sub_path: [sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });
+20 -5
View File
@@ -565,13 +565,28 @@ test "tasks spawned in group after Group.cancel are canceled" {
try group.concurrent(io, global.waitThenSpawn, .{ io, &group });
}
test "CSPRNG" {
test "random" {
const io = testing.io;
var random = io.random();
var a: u64 = undefined;
var b: u64 = undefined;
var c: u64 = undefined;
io.random(@ptrCast(&a));
io.random(@ptrCast(&b));
io.random(@ptrCast(&c));
const a = random.int(u64);
const b = random.int(u64);
const c = random.int(u64);
try std.testing.expect(a ^ b ^ c != 0);
}
test "randomSecure" {
const io = testing.io;
var buf_a: [50]u8 = undefined;
var buf_b: [50]u8 = undefined;
try io.randomSecure(&buf_a);
try io.randomSecure(&buf_b);
// If this test fails the chance is significantly higher that there is a bug than
// that two sets of 50 bytes were equal.
try expect(!mem.eql(u8, &buf_a, &buf_b));
}
+1 -1
View File
@@ -38,7 +38,7 @@ pub const IoSource = struct {
pub fn interface(this: *const @This()) std.Random {
return .{
.ptr = this,
.ptr = @constCast(this),
.fillFn = fill,
};
}
+2 -1
View File
@@ -436,8 +436,9 @@ fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void {
}
test "CSPRNG" {
const io = std.testing.io;
var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
std.crypto.random.bytes(&secret_seed);
io.random(&secret_seed);
var csprng = DefaultCsprng.init(secret_seed);
const random = csprng.random();
const a = random.int(u64);
+4
View File
@@ -303,6 +303,9 @@ test {
_ = dh.X25519;
_ = kem.kyber_d00;
_ = kem.hybrid;
_ = kem.kyber_d00;
_ = kem.ml_kem;
_ = ecc.Curve25519;
_ = ecc.Edwards25519;
@@ -340,6 +343,7 @@ test {
_ = sign.Ed25519;
_ = sign.ecdsa;
_ = sign.mldsa;
_ = stream.chacha.ChaCha20IETF;
_ = stream.chacha.ChaCha12IETF;
+32 -23
View File
@@ -333,12 +333,10 @@ pub const Ed25519 = struct {
}
/// Generate a new, random key pair.
///
/// `crypto.random.bytes` must be supported by the target.
pub fn generate() KeyPair {
pub fn generate(io: std.Io) KeyPair {
var random_seed: [seed_length]u8 = undefined;
while (true) {
crypto.random.bytes(&random_seed);
io.random(&random_seed);
return generateDeterministic(random_seed) catch {
@branchHint(.unlikely);
continue;
@@ -389,18 +387,21 @@ pub const Ed25519 = struct {
/// Create a Signer, that can be used for incremental signing.
/// Note that the signature is not deterministic.
/// The noise parameter, if set, should be something unique for each message,
/// such as a random nonce, or a counter.
pub fn signer(key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
pub fn signer(
key_pair: KeyPair,
/// If set, should be something unique for each message, such as a
/// random nonce, or a counter.
noise: ?[noise_length]u8,
/// Filled with cryptographically secure randomness.
entropy: *const [noise_length]u8,
) (IdentityElementError || KeyMismatchError || NonCanonicalError || WeakPublicKeyError)!Signer {
if (!mem.eql(u8, &key_pair.secret_key.publicKeyBytes(), &key_pair.public_key.toBytes())) {
return error.KeyMismatch;
}
const scalar_and_prefix = key_pair.secret_key.scalarAndPrefix();
var h = Sha512.init(.{});
h.update(&scalar_and_prefix.prefix);
var noise2: [noise_length]u8 = undefined;
crypto.random.bytes(&noise2);
h.update(&noise2);
h.update(entropy);
if (noise) |*z| {
h.update(z);
}
@@ -420,7 +421,7 @@ pub const Ed25519 = struct {
};
/// Verify several signatures in a single operation, much faster than verifying signatures one-by-one
pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {
pub fn verifyBatch(io: std.Io, comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void {
var r_batch: [count]CompressedScalar = undefined;
var s_batch: [count]CompressedScalar = undefined;
var a_batch: [count]Curve = undefined;
@@ -454,7 +455,7 @@ pub const Ed25519 = struct {
var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
for (&z_batch) |*z| {
crypto.random.bytes(z[0..16]);
io.random(z[0..16]);
@memset(z[16..], 0);
}
@@ -587,12 +588,14 @@ test "signature" {
}
test "batch verification" {
const io = std.testing.io;
for (0..16) |_| {
const key_pair = Ed25519.KeyPair.generate();
const key_pair = Ed25519.KeyPair.generate(io);
var msg1: [32]u8 = undefined;
var msg2: [32]u8 = undefined;
crypto.random.bytes(&msg1);
crypto.random.bytes(&msg2);
io.random(&msg1);
io.random(&msg2);
const sig1 = try key_pair.sign(&msg1, null);
const sig2 = try key_pair.sign(&msg2, null);
var signature_batch = [_]Ed25519.BatchElement{
@@ -607,10 +610,10 @@ test "batch verification" {
.public_key = key_pair.public_key,
},
};
try Ed25519.verifyBatch(2, signature_batch);
try Ed25519.verifyBatch(io, 2, signature_batch);
signature_batch[1].sig = sig1;
try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(signature_batch.len, signature_batch));
try std.testing.expectError(error.SignatureVerificationFailed, Ed25519.verifyBatch(io, signature_batch.len, signature_batch));
}
}
@@ -718,14 +721,15 @@ test "test vectors" {
}
test "with blind keys" {
const io = std.testing.io;
const BlindKeyPair = Ed25519.key_blinding.BlindKeyPair;
// Create a standard Ed25519 key pair
const kp = Ed25519.KeyPair.generate();
const kp = Ed25519.KeyPair.generate(io);
// Create a random blinding seed
var blind: [32]u8 = undefined;
crypto.random.bytes(&blind);
io.random(&blind);
// Blind the key pair
const blind_kp = try BlindKeyPair.init(kp, blind, "ctx");
@@ -741,9 +745,12 @@ test "with blind keys" {
}
test "signatures with streaming" {
const kp = Ed25519.KeyPair.generate();
const io = std.testing.io;
const kp = Ed25519.KeyPair.generate(io);
var signer = try kp.signer(null);
var entropy: [Ed25519.noise_length]u8 = undefined;
io.random(&entropy);
var signer = try kp.signer(null, &entropy);
signer.update("mes");
signer.update("sage");
const sig = signer.finalize();
@@ -757,7 +764,8 @@ test "signatures with streaming" {
}
test "key pair from secret key" {
const kp = Ed25519.KeyPair.generate();
const io = std.testing.io;
const kp = Ed25519.KeyPair.generate(io);
const kp2 = try Ed25519.KeyPair.fromSecretKey(kp.secret_key);
try std.testing.expectEqualSlices(u8, &kp.secret_key.toBytes(), &kp2.secret_key.toBytes());
try std.testing.expectEqualSlices(u8, &kp.public_key.toBytes(), &kp2.public_key.toBytes());
@@ -788,7 +796,8 @@ test "cofactored vs cofactorless verification" {
}
test "regular signature verifies with both verify and verifyStrict" {
const kp = Ed25519.KeyPair.generate();
const io = std.testing.io;
const kp = Ed25519.KeyPair.generate(io);
const msg = "test message";
const sig = try kp.sign(msg, null);
try sig.verify(msg, kp.public_key);
+5 -3
View File
@@ -575,10 +575,11 @@ test "packing/unpacking" {
}
test "point addition/subtraction" {
const io = std.testing.io;
var s1: [32]u8 = undefined;
var s2: [32]u8 = undefined;
crypto.random.bytes(&s1);
crypto.random.bytes(&s2);
io.random(&s1);
io.random(&s2);
const p = try Edwards25519.basePoint.clampedMul(s1);
const q = try Edwards25519.basePoint.clampedMul(s2);
const r = p.add(q).add(q).sub(q).sub(q);
@@ -622,9 +623,10 @@ test "implicit reduction of invalid scalars" {
}
test "subgroup check" {
const io = std.testing.io;
for (0..100) |_| {
var p = Edwards25519.basePoint;
const s = Edwards25519.scalar.random();
const s = Edwards25519.scalar.random(io);
p = try p.mulPublic(s);
try p.rejectUnexpectedSubgroup();
}
+7 -6
View File
@@ -101,8 +101,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar) CompressedScalar {
}
/// Return a random scalar < L
pub fn random() CompressedScalar {
return Scalar.random().toBytes();
pub fn random(io: std.Io) CompressedScalar {
return Scalar.random(io).toBytes();
}
/// A scalar in unpacked representation
@@ -560,10 +560,10 @@ pub const Scalar = struct {
}
/// Return a random scalar < L.
pub fn random() Scalar {
pub fn random(io: std.Io) Scalar {
var s: [64]u8 = undefined;
while (true) {
crypto.random.bytes(&s);
io.random(&s);
const n = Scalar.fromBytes64(s);
if (!n.isZero()) {
return n;
@@ -879,8 +879,9 @@ test "scalar field inversion" {
}
test "random scalar" {
const s1 = random();
const s2 = random();
const io = std.testing.io;
const s1 = random(io);
const s2 = random(io);
try std.testing.expect(!mem.eql(u8, &s1, &s2));
}
+2 -2
View File
@@ -41,10 +41,10 @@ pub const X25519 = struct {
}
/// Generate a new, random key pair.
pub fn generate() KeyPair {
pub fn generate(io: std.Io) KeyPair {
var random_seed: [seed_length]u8 = undefined;
while (true) {
crypto.random.bytes(&random_seed);
io.random(&random_seed);
return generateDeterministic(random_seed) catch {
@branchHint(.unlikely);
continue;
+1 -1
View File
@@ -533,7 +533,7 @@ const PhcFormatHasher = struct {
if (params.secret != null or params.ad != null) return HasherError.InvalidEncoding;
var salt: [default_salt_len]u8 = undefined;
crypto.random.bytes(&salt);
io.random(&salt);
var hash: [default_hash_len]u8 = undefined;
try kdf(allocator, &hash, password, &salt, params, mode, io);
+72 -39
View File
@@ -17,7 +17,7 @@ const HasherError = pwhash.HasherError;
const EncodingError = phc_format.Error;
const Error = pwhash.Error;
const salt_length: usize = 16;
pub const salt_length: usize = 16;
const salt_str_length: usize = 22;
const ct_str_length: usize = 31;
const ct_length: usize = 24;
@@ -426,7 +426,7 @@ pub const Params = struct {
fn bcryptWithTruncation(
password: []const u8,
salt: [salt_length]u8,
salt: *const [salt_length]u8,
params: Params,
) [dk_length]u8 {
var state = State{};
@@ -435,13 +435,13 @@ fn bcryptWithTruncation(
@memcpy(password_buf[0..trimmed_len], password[0..trimmed_len]);
password_buf[trimmed_len] = 0;
const passwordZ = password_buf[0 .. trimmed_len + 1];
state.expand(salt[0..], passwordZ);
state.expand(salt, passwordZ);
const rounds: u64 = @as(u64, 1) << params.rounds_log;
var k: u64 = 0;
while (k < rounds) : (k += 1) {
state.expand0(passwordZ);
state.expand0(salt[0..]);
state.expand0(salt);
}
crypto.secureZero(u8, &password_buf);
@@ -467,7 +467,7 @@ fn bcryptWithTruncation(
/// For key derivation, use `bcrypt.pbkdf()` or `bcrypt.opensshKdf()` instead.
pub fn bcrypt(
password: []const u8,
salt: [salt_length]u8,
salt: *const [salt_length]u8,
params: Params,
) [dk_length]u8 {
if (password.len <= 72 or params.silently_truncate_password) {
@@ -475,7 +475,7 @@ pub fn bcrypt(
}
var pre_hash: [HmacSha512.mac_length]u8 = undefined;
HmacSha512.create(&pre_hash, password, &salt);
HmacSha512.create(&pre_hash, password, salt);
const Encoder = crypt_format.Codec.Encoder;
var pre_hash_b64: [Encoder.calcSize(pre_hash.len)]u8 = undefined;
@@ -623,16 +623,16 @@ const crypt_format = struct {
fn strHashInternal(
password: []const u8,
salt: [salt_length]u8,
salt: *const [salt_length]u8,
params: Params,
) [hash_length]u8 {
var dk = bcrypt(password, salt, params);
var salt_str: [salt_str_length]u8 = undefined;
_ = Codec.Encoder.encode(salt_str[0..], salt[0..]);
_ = Codec.Encoder.encode(&salt_str, salt);
var ct_str: [ct_str_length]u8 = undefined;
_ = Codec.Encoder.encode(ct_str[0..], dk[0..]);
_ = Codec.Encoder.encode(&ct_str, dk[0..]);
var s_buf: [hash_length]u8 = undefined;
const s = fmt.bufPrint(
@@ -657,21 +657,20 @@ const PhcFormatHasher = struct {
hash: BinValue(dk_length),
};
/// Return a non-deterministic hash of the password encoded as a PHC-format string
/// Return a non-deterministic hash of the password encoded as a PHC-format string.
fn create(
password: []const u8,
params: Params,
buf: []u8,
/// Filled with cryptographically secure entropy.
salt: *const [salt_length]u8,
) HasherError![]const u8 {
var salt: [salt_length]u8 = undefined;
crypto.random.bytes(&salt);
const hash = bcrypt(password, salt, params);
return phc_format.serialize(HashResult{
.alg_id = alg_id,
.r = params.rounds_log,
.salt = try BinValue(salt_length).fromSlice(&salt),
.salt = try BinValue(salt_length).fromSlice(salt),
.hash = try BinValue(dk_length).fromSlice(&hash),
}, buf);
}
@@ -688,11 +687,11 @@ const PhcFormatHasher = struct {
if (hash_result.salt.len != salt_length or hash_result.hash.len != dk_length)
return HasherError.InvalidEncoding;
const params = Params{
const params: Params = .{
.rounds_log = hash_result.r,
.silently_truncate_password = silently_truncate_password,
};
const hash = bcrypt(password, hash_result.salt.buf, params);
const hash = bcrypt(password, &hash_result.salt.buf, params);
const expected_hash = hash_result.hash.constSlice();
if (!mem.eql(u8, &hash, expected_hash)) return HasherError.PasswordVerificationFailed;
@@ -709,12 +708,11 @@ const CryptFormatHasher = struct {
password: []const u8,
params: Params,
buf: []u8,
/// Filled with cryptographically secure entropy.
salt: *const [salt_length]u8,
) HasherError![]const u8 {
if (buf.len < pwhash_str_length) return HasherError.NoSpaceLeft;
var salt: [salt_length]u8 = undefined;
crypto.random.bytes(&salt);
const hash = crypt_format.strHashInternal(password, salt, params);
@memcpy(buf[0..hash.len], &hash);
@@ -736,9 +734,9 @@ const CryptFormatHasher = struct {
const salt_str = str[7..][0..salt_str_length];
var salt: [salt_length]u8 = undefined;
crypt_format.Codec.Decoder.decode(salt[0..], salt_str[0..]) catch return HasherError.InvalidEncoding;
crypt_format.Codec.Decoder.decode(&salt, salt_str) catch return HasherError.InvalidEncoding;
const wanted_s = crypt_format.strHashInternal(password, salt, .{
const wanted_s = crypt_format.strHashInternal(password, &salt, .{
.rounds_log = rounds_log,
.silently_truncate_password = silently_truncate_password,
});
@@ -756,21 +754,28 @@ pub const HashOptions = struct {
encoding: pwhash.Encoding,
};
/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key stretching function.
/// bcrypt is a computationally expensive and cache-hard function, explicitly designed to slow down exhaustive searches.
/// Compute a hash of a password using 2^rounds_log rounds of the bcrypt key
/// stretching function.
///
/// The function returns a string that includes all the parameters required for verification.
/// bcrypt is a computationally expensive and cache-hard function, explicitly
/// designed to slow down exhaustive searches.
///
/// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes.
/// If this is an issue for your application, set the `silently_truncate_password` option to `false`.
/// The function returns a string that includes all the parameters required for
/// verification.
///
/// By design, bcrypt silently truncates passwords to 72 bytes. If this is an
/// issue for your application, set the `silently_truncate_password` option to
/// `false`.
pub fn strHash(
password: []const u8,
options: HashOptions,
out: []u8,
/// Filled with cryptographically secure entropy.
salt: *const [salt_length]u8,
) Error![]const u8 {
switch (options.encoding) {
.phc => return PhcFormatHasher.create(password, options.params, out),
.crypt => return CryptFormatHasher.create(password, options.params, out),
.phc => return PhcFormatHasher.create(password, options.params, out, salt),
.crypt => return CryptFormatHasher.create(password, options.params, out, salt),
}
}
@@ -796,8 +801,9 @@ pub fn strVerify(
}
test "bcrypt codec" {
const io = testing.io;
var salt: [salt_length]u8 = undefined;
crypto.random.bytes(&salt);
io.random(&salt);
var salt_str: [salt_str_length]u8 = undefined;
_ = crypt_format.Codec.Encoder.encode(salt_str[0..], salt[0..]);
var salt2: [salt_length]u8 = undefined;
@@ -806,14 +812,20 @@ test "bcrypt codec" {
}
test "bcrypt crypt format" {
var hash_options = HashOptions{
const io = testing.io;
var hash_options: HashOptions = .{
.params = .{ .rounds_log = 5, .silently_truncate_password = false },
.encoding = .crypt,
};
var verify_options = VerifyOptions{ .silently_truncate_password = false };
var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
var buf: [hash_length]u8 = undefined;
const s = try strHash("password", hash_options, &buf);
const s = s: {
var salt: [salt_length]u8 = undefined;
io.random(&salt);
break :s try strHash("password", hash_options, &buf, &salt);
};
try testing.expect(mem.startsWith(u8, s, crypt_format.prefix));
try strVerify(s, "password", verify_options);
@@ -823,7 +835,11 @@ test "bcrypt crypt format" {
);
var long_buf: [hash_length]u8 = undefined;
var long_s = try strHash("password" ** 100, hash_options, &long_buf);
var long_s = s: {
var salt: [salt_length]u8 = undefined;
io.random(&salt);
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
};
try testing.expect(mem.startsWith(u8, long_s, crypt_format.prefix));
try strVerify(long_s, "password" ** 100, verify_options);
@@ -834,7 +850,11 @@ test "bcrypt crypt format" {
hash_options.params.silently_truncate_password = true;
verify_options.silently_truncate_password = true;
long_s = try strHash("password" ** 100, hash_options, &long_buf);
long_s = s: {
var salt: [salt_length]u8 = undefined;
io.random(&salt);
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
};
try strVerify(long_s, "password" ** 101, verify_options);
try strVerify(
@@ -845,15 +865,20 @@ test "bcrypt crypt format" {
}
test "bcrypt phc format" {
var hash_options = HashOptions{
const io = testing.io;
var hash_options: HashOptions = .{
.params = .{ .rounds_log = 5, .silently_truncate_password = false },
.encoding = .phc,
};
var verify_options = VerifyOptions{ .silently_truncate_password = false };
var verify_options: VerifyOptions = .{ .silently_truncate_password = false };
const prefix = "$bcrypt$";
var buf: [hash_length * 2]u8 = undefined;
const s = try strHash("password", hash_options, &buf);
const s = s: {
var salt: [salt_length]u8 = undefined;
io.random(&salt);
break :s try strHash("password", hash_options, &buf, &salt);
};
try testing.expect(mem.startsWith(u8, s, prefix));
try strVerify(s, "password", verify_options);
@@ -863,7 +888,11 @@ test "bcrypt phc format" {
);
var long_buf: [hash_length * 2]u8 = undefined;
var long_s = try strHash("password" ** 100, hash_options, &long_buf);
var long_s = s: {
var salt: [salt_length]u8 = undefined;
io.random(&salt);
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
};
try testing.expect(mem.startsWith(u8, long_s, prefix));
try strVerify(long_s, "password" ** 100, verify_options);
@@ -874,7 +903,11 @@ test "bcrypt phc format" {
hash_options.params.silently_truncate_password = true;
verify_options.silently_truncate_password = true;
long_s = try strHash("password" ** 100, hash_options, &long_buf);
long_s = s: {
var salt: [salt_length]u8 = undefined;
io.random(&salt);
break :s try strHash("password" ** 100, hash_options, &long_buf, &salt);
};
try strVerify(long_s, "password" ** 101, verify_options);
try strVerify(
+17 -11
View File
@@ -323,10 +323,10 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
}
/// Generate a new, random key pair.
pub fn generate() KeyPair {
pub fn generate(io: std.Io) KeyPair {
var random_seed: [seed_length]u8 = undefined;
while (true) {
crypto.random.bytes(&random_seed);
io.random(&random_seed);
return generateDeterministic(random_seed) catch {
@branchHint(.unlikely);
continue;
@@ -417,12 +417,13 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type {
test "Basic operations over EcdsaP384Sha384" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const io = testing.io;
const Scheme = EcdsaP384Sha384;
const kp = Scheme.KeyPair.generate();
const kp = Scheme.KeyPair.generate(io);
const msg = "test";
var noise: [Scheme.noise_length]u8 = undefined;
crypto.random.bytes(&noise);
io.random(&noise);
const sig = try kp.sign(msg, noise);
try sig.verify(msg, kp.public_key);
@@ -433,12 +434,13 @@ test "Basic operations over EcdsaP384Sha384" {
test "Basic operations over Secp256k1" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const io = testing.io;
const Scheme = EcdsaSecp256k1Sha256oSha256;
const kp = Scheme.KeyPair.generate();
const kp = Scheme.KeyPair.generate(io);
const msg = "test";
var noise: [Scheme.noise_length]u8 = undefined;
crypto.random.bytes(&noise);
io.random(&noise);
const sig = try kp.sign(msg, noise);
try sig.verify(msg, kp.public_key);
@@ -449,12 +451,13 @@ test "Basic operations over Secp256k1" {
test "Basic operations over EcdsaP384Sha256" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const io = testing.io;
const Scheme = Ecdsa(crypto.ecc.P384, crypto.hash.sha2.Sha256);
const kp = Scheme.KeyPair.generate();
const kp = Scheme.KeyPair.generate(io);
const msg = "test";
var noise: [Scheme.noise_length]u8 = undefined;
crypto.random.bytes(&noise);
io.random(&noise);
const sig = try kp.sign(msg, noise);
try sig.verify(msg, kp.public_key);
@@ -502,8 +505,10 @@ test "Verifying a existing signature with EcdsaP384Sha256" {
test "Prehashed message operations" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const io = testing.io;
const Scheme = EcdsaP256Sha256;
const kp = Scheme.KeyPair.generate();
const kp = Scheme.KeyPair.generate(io);
const msg = "test message for prehashed signing";
const Hash = crypto.hash.sha2.Sha256;
@@ -518,7 +523,7 @@ test "Prehashed message operations" {
try testing.expectError(error.SignatureVerificationFailed, sig.verifyPrehashed(bad_hash, kp.public_key));
var noise: [Scheme.noise_length]u8 = undefined;
crypto.random.bytes(&noise);
io.random(&noise);
const sig_with_noise = try kp.signPrehashed(msg_hash, noise);
try sig_with_noise.verifyPrehashed(msg_hash, kp.public_key);
@@ -1628,8 +1633,9 @@ fn tvTry(comptime Scheme: type, vector: TestVector) !void {
test "Sec1 encoding/decoding" {
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
const io = testing.io;
const Scheme = EcdsaP384Sha384;
const kp = Scheme.KeyPair.generate();
const kp = Scheme.KeyPair.generate(io);
const pk = kp.public_key;
const pk_compressed_sec1 = pk.toCompressedSec1();
const pk_recovered1 = try Scheme.PublicKey.fromSec1(&pk_compressed_sec1);
+74 -61
View File
@@ -174,43 +174,56 @@ pub fn HybridKem(comptime params: Params) type {
return .{ .bytes = buf.* };
}
/// Generates a shared secret and encapsulates it for the public key.
/// If `seed` is `null`, uses random bytes from `std.crypto.random`.
/// If `seed` is set, encapsulation is deterministic (for testing only).
pub fn encaps(self: PublicKey, seed: ?[]const u8) !EncapsulatedSecret {
const pq_nek = params.PqKem.PublicKey.encoded_length;
const ek_pq = try params.PqKem.PublicKey.fromBytes(self.bytes[0..pq_nek]);
const ek_t = self.bytes[pq_nek..][0..params.Group.element_length];
/// Generates a shared secret, encapsulated for the public key,
/// using random bytes.
///
/// This is recommended over `encapsDeterministic`.
pub fn encaps(pk: PublicKey, io: std.Io) !EncapsulatedSecret {
var seed_pq: [32]u8 = undefined;
io.random(&seed_pq);
var seed_t: [32]u8 = undefined;
io.random(&seed_t);
var seed_t_expanded: [params.Group.seed_length]u8 = try expandRandomnessSeed(seed_t);
return encapsInner(pk, &seed_pq, &seed_t_expanded);
}
/// Generates a shared secret, encapsulated for the public key,
/// using the provided seed.
///
/// Calling `encaps` instead is recommended.
pub fn encapsDeterministic(pk: PublicKey, seed: []const u8) !EncapsulatedSecret {
if (seed.len < 32) return error.InsufficientRandomness;
var seed_pq: [32]u8 = seed[0..32].*;
var seed_t_expanded: [params.Group.seed_length]u8 = undefined;
if (seed) |r| {
if (r.len < 32) return error.InsufficientRandomness;
seed_pq = r[0..32].*;
const t_randomness = r[32..];
const t_randomness = seed[32..];
if (t_randomness.len < params.Group.seed_length) {
// Provided randomness is shorter than seed_length, use it directly
// (test vectors provide just enough for randomScalar)
@memcpy(seed_t_expanded[0..t_randomness.len], t_randomness);
// Pad the rest with zeros if needed (shouldn't be used by randomScalar)
if (t_randomness.len < params.Group.seed_length) {
// Provided randomness is shorter than seed_length, use it directly
// (test vectors provide just enough for randomScalar)
@memcpy(seed_t_expanded[0..t_randomness.len], t_randomness);
// Pad the rest with zeros if needed (shouldn't be used by randomScalar)
if (t_randomness.len < params.Group.seed_length) {
@memset(seed_t_expanded[t_randomness.len..], 0);
}
} else {
// Full randomness provided
@memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]);
@memset(seed_t_expanded[t_randomness.len..], 0);
}
} else {
crypto.random.bytes(&seed_pq);
var seed_t: [32]u8 = undefined;
crypto.random.bytes(&seed_t);
seed_t_expanded = try expandRandomnessSeed(seed_t);
// Full randomness provided
@memcpy(&seed_t_expanded, t_randomness[0..params.Group.seed_length]);
}
const pq_encap = ek_pq.encaps(seed_pq);
const sk_e = try params.Group.randomScalar(&seed_t_expanded);
return encapsInner(pk, &seed_pq, &seed_t_expanded);
}
fn encapsInner(
pk: PublicKey,
seed_pq: *[32]u8,
seed_t_expanded: *[params.Group.seed_length]u8,
) !EncapsulatedSecret {
const pq_nek = params.PqKem.PublicKey.encoded_length;
const ek_pq = try params.PqKem.PublicKey.fromBytes(pk.bytes[0..pq_nek]);
const ek_t = pk.bytes[pq_nek..][0..params.Group.element_length];
const pq_encap = ek_pq.encapsDeterministic(seed_pq);
const sk_e = try params.Group.randomScalar(seed_t_expanded);
const ct_t_point = try params.Group.mulBase(sk_e);
const ct_t = if (is_nist_curve) params.Group.encodePoint(ct_t_point) else ct_t_point;
@@ -280,9 +293,9 @@ pub fn HybridKem(comptime params: Params) type {
}
/// Generates a new random key pair.
pub fn generate() !KeyPair {
pub fn generate(io: std.Io) !KeyPair {
var seed: [params.Nseed]u8 = undefined;
crypto.random.bytes(&seed);
io.random(&seed);
return generateDeterministic(seed);
}
};
@@ -386,7 +399,7 @@ test "MLKEM768-X25519 basic round trip" {
var enc_seed: [64]u8 = undefined;
@memset(&enc_seed, 0x43);
const encap_result = try kp.public_key.encaps(&enc_seed);
const encap_result = try kp.public_key.encapsDeterministic(&enc_seed);
const ss_decap = try kp.secret_key.decaps(&encap_result.ciphertext);
try testing.expectEqualSlices(u8, &encap_result.shared_secret, &ss_decap);
@@ -408,7 +421,7 @@ test "MLKEM768-X25519 test vector 0" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -432,7 +445,7 @@ test "MLKEM768-X25519 test vector 1" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -456,7 +469,7 @@ test "MLKEM768-X25519 test vector 2" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -480,7 +493,7 @@ test "MLKEM768-X25519 test vector 3" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -504,7 +517,7 @@ test "MLKEM768-X25519 test vector 4" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -528,7 +541,7 @@ test "MLKEM768-X25519 test vector 5" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -552,7 +565,7 @@ test "MLKEM768-X25519 test vector 6" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -576,7 +589,7 @@ test "MLKEM768-X25519 test vector 7" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -600,7 +613,7 @@ test "MLKEM768-X25519 test vector 8" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -624,7 +637,7 @@ test "MLKEM768-X25519 test vector 9" {
const kp = try MlKem768X25519.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -648,7 +661,7 @@ test "MLKEM768-P256 test vector 0" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -672,7 +685,7 @@ test "MLKEM768-P256 test vector 1" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -696,7 +709,7 @@ test "MLKEM768-P256 test vector 2" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -720,7 +733,7 @@ test "MLKEM768-P256 test vector 3" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -744,7 +757,7 @@ test "MLKEM768-P256 test vector 4" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -768,7 +781,7 @@ test "MLKEM768-P256 test vector 5" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -792,7 +805,7 @@ test "MLKEM768-P256 test vector 6" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -816,7 +829,7 @@ test "MLKEM768-P256 test vector 7" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -840,7 +853,7 @@ test "MLKEM768-P256 test vector 8" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -864,7 +877,7 @@ test "MLKEM768-P256 test vector 9" {
const kp = try MlKem768P256.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -888,7 +901,7 @@ test "MLKEM1024-P384 test vector 0" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -912,7 +925,7 @@ test "MLKEM1024-P384 test vector 1" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -936,7 +949,7 @@ test "MLKEM1024-P384 test vector 2" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -960,7 +973,7 @@ test "MLKEM1024-P384 test vector 3" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -984,7 +997,7 @@ test "MLKEM1024-P384 test vector 4" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -1008,7 +1021,7 @@ test "MLKEM1024-P384 test vector 5" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -1032,7 +1045,7 @@ test "MLKEM1024-P384 test vector 6" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -1056,7 +1069,7 @@ test "MLKEM1024-P384 test vector 7" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
@@ -1080,7 +1093,7 @@ test "MLKEM1024-P384 test vector 8" {
const kp = try MlKem1024P384.KeyPair.generateDeterministic(seed);
try testing.expectEqualSlices(u8, &expected_ek, &kp.public_key.toBytes());
const enc_result = try kp.public_key.encaps(&randomness);
const enc_result = try kp.public_key.encapsDeterministic(&randomness);
try testing.expectEqualSlices(u8, &expected_ct, &enc_result.ciphertext);
try testing.expectEqualSlices(u8, &expected_ss, &enc_result.shared_secret);
+10 -9
View File
@@ -2019,12 +2019,9 @@ fn MLDSAImpl(comptime p: Params) type {
secret_key: SecretKey,
/// Generate a new random key pair.
/// This uses the system's cryptographically secure random number generator.
///
/// `crypto.random.bytes` must be supported by the target.
pub fn generate() KeyPair {
pub fn generate(io: std.Io) KeyPair {
var seed: [Self.seed_length]u8 = undefined;
crypto.random.bytes(&seed);
io.random(&seed);
return generateDeterministic(seed) catch unreachable;
}
@@ -3198,8 +3195,9 @@ test "ML-DSA-87 KAT test vector 0" {
}
test "KeyPair API - generate and sign" {
const io = std.testing.io;
// Test the new KeyPair API with random generation
const kp = MLDSA44.KeyPair.generate();
const kp = MLDSA44.KeyPair.generate(io);
const msg = "Test message for KeyPair API";
// Sign with deterministic mode (no noise)
@@ -3222,8 +3220,9 @@ test "KeyPair API - generateDeterministic" {
}
test "KeyPair API - fromSecretKey" {
const io = std.testing.io;
// Generate a key pair
const kp1 = MLDSA44.KeyPair.generate();
const kp1 = MLDSA44.KeyPair.generate(io);
// Derive public key from secret key
const kp2 = try MLDSA44.KeyPair.fromSecretKey(kp1.secret_key);
@@ -3235,8 +3234,9 @@ test "KeyPair API - fromSecretKey" {
}
test "Signature verification with noise" {
const io = std.testing.io;
// Test signing with randomness (hedged signatures)
const kp = MLDSA65.KeyPair.generate();
const kp = MLDSA65.KeyPair.generate(io);
const msg = "Message to be signed with randomness";
// Create some noise
@@ -3250,8 +3250,9 @@ test "Signature verification with noise" {
}
test "Signature verification failure" {
const io = std.testing.io;
// Test that invalid signatures are rejected
const kp = MLDSA44.KeyPair.generate();
const kp = MLDSA44.KeyPair.generate(io);
const msg = "Original message";
const sig = try kp.sign(msg, null);
+29 -20
View File
@@ -244,32 +244,41 @@ fn Kyber(comptime p: Params) type {
/// Size of a serialized representation of the key, in bytes.
pub const encoded_length = InnerPk.encoded_length;
/// Generates a shared secret, and encapsulates it for the public key.
/// If `seed` is `null`, a random seed is used. This is recommended.
/// If `seed` is set, encapsulation is deterministic.
pub fn encaps(pk: PublicKey, seed_: ?[encaps_seed_length]u8) EncapsulatedSecret {
/// Generates a shared secret, encapsulated for the public key,
/// using random bytes.
///
/// This is recommended over `encapsDeterministic`.
pub fn encaps(pk: PublicKey, io: std.Io) EncapsulatedSecret {
var m: [inner_plaintext_length]u8 = undefined;
io.random(&m);
return encapsInner(pk, &m);
}
if (seed_) |seed| {
if (p.ml_kem) {
@memcpy(&m, &seed);
} else {
// m = H(seed)
sha3.Sha3_256.hash(&seed, &m, .{});
}
/// Generates a shared secret, encapsulated for the public key,
/// using the provided seed.
///
/// Calling `encaps` instead is recommended.
pub fn encapsDeterministic(pk: PublicKey, seed: *const [encaps_seed_length]u8) EncapsulatedSecret {
var m: [inner_plaintext_length]u8 = undefined;
if (p.ml_kem) {
@memcpy(&m, seed);
} else {
crypto.random.bytes(&m);
// m = H(seed)
sha3.Sha3_256.hash(seed, &m, .{});
}
return encapsInner(pk, &m);
}
fn encapsInner(pk: PublicKey, m: *[inner_plaintext_length]u8) EncapsulatedSecret {
// (K', r) = G(m ‖ H(pk))
var kr: [inner_plaintext_length + h_length]u8 = undefined;
var g = sha3.Sha3_512.init(.{});
g.update(&m);
g.update(m);
g.update(&pk.hpk);
g.final(&kr);
// c = innerEncrypt(pk, m, r)
const ct = pk.pk.encrypt(&m, kr[32..64]);
const ct = pk.pk.encrypt(m, kr[32..64]);
if (p.ml_kem) {
return EncapsulatedSecret{
@@ -398,10 +407,10 @@ fn Kyber(comptime p: Params) type {
}
/// Generate a new, random key pair.
pub fn generate() KeyPair {
pub fn generate(io: std.Io) KeyPair {
var random_seed: [seed_length]u8 = undefined;
while (true) {
crypto.random.bytes(&random_seed);
io.random(&random_seed);
return generateDeterministic(random_seed) catch {
@branchHint(.unlikely);
continue;
@@ -1634,15 +1643,15 @@ test "Test happy flow" {
}
inline for (modes) |mode| {
for (0..10) |i| {
seed[0] = @as(u8, @intCast(i));
seed[0] = @intCast(i);
const kp = try mode.KeyPair.generateDeterministic(seed);
const sk = try mode.SecretKey.fromBytes(&kp.secret_key.toBytes());
try testing.expectEqual(sk, kp.secret_key);
const pk = try mode.PublicKey.fromBytes(&kp.public_key.toBytes());
try testing.expectEqual(pk, kp.public_key);
for (0..10) |j| {
seed[1] = @as(u8, @intCast(j));
const e = pk.encaps(seed[0..32].*);
seed[1] = @intCast(j);
const e = pk.encapsDeterministic(seed[0..32]);
try testing.expectEqual(e.shared_secret, try sk.decaps(&e.ciphertext));
}
}
@@ -1695,7 +1704,7 @@ fn testNistKat(mode: type, hash: []const u8) !void {
g2.fill(kseed[32..64]);
g2.fill(&eseed);
const kp = try mode.KeyPair.generateDeterministic(kseed);
const e = kp.public_key.encaps(eseed);
const e = kp.public_key.encapsDeterministic(&eseed);
const ss2 = try kp.secret_key.decaps(&e.ciphertext);
try testing.expectEqual(ss2, e.shared_secret);
try fw.writer.print("pk = {X}\n", .{&kp.public_key.toBytes()});
+2 -2
View File
@@ -122,8 +122,8 @@ pub const P256 = struct {
}
/// Return a random point.
pub fn random() P256 {
const n = scalar.random(.little);
pub fn random(io: std.Io) P256 {
const n = scalar.random(io, .little);
return basePoint.mul(n, .little) catch unreachable;
}
+4 -4
View File
@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
}
/// Return a random scalar
pub fn random(endian: std.builtin.Endian) CompressedScalar {
return Scalar.random().toBytes(endian);
pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
return Scalar.random(io).toBytes(endian);
}
/// A scalar in unpacked representation.
@@ -170,10 +170,10 @@ pub const Scalar = struct {
}
/// Return a random scalar < L.
pub fn random() Scalar {
pub fn random(io: std.Io) Scalar {
var s: [48]u8 = undefined;
while (true) {
crypto.random.bytes(&s);
io.random(&s);
const n = Scalar.fromBytes48(s, .little);
if (!n.isZero()) {
return n;
+2 -2
View File
@@ -122,8 +122,8 @@ pub const P384 = struct {
}
/// Return a random point.
pub fn random() P384 {
const n = scalar.random(.little);
pub fn random(io: std.Io) P384 {
const n = scalar.random(io, .little);
return basePoint.mul(n, .little) catch unreachable;
}
+4 -4
View File
@@ -63,8 +63,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
}
/// Return a random scalar
pub fn random(endian: std.builtin.Endian) CompressedScalar {
return Scalar.random().toBytes(endian);
pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
return Scalar.random(io).toBytes(endian);
}
/// A scalar in unpacked representation.
@@ -159,10 +159,10 @@ pub const Scalar = struct {
}
/// Return a random scalar < L.
pub fn random() Scalar {
pub fn random(io: std.Io) Scalar {
var s: [64]u8 = undefined;
while (true) {
crypto.random.bytes(&s);
io.random(&s);
const n = Scalar.fromBytes64(s, .little);
if (!n.isZero()) {
return n;
+2 -2
View File
@@ -175,8 +175,8 @@ pub const Secp256k1 = struct {
}
/// Return a random point.
pub fn random() Secp256k1 {
const n = scalar.random(.little);
pub fn random(io: std.Io) Secp256k1 {
const n = scalar.random(io, .little);
return basePoint.mul(n, .little) catch unreachable;
}
+4 -4
View File
@@ -68,8 +68,8 @@ pub fn sub(a: CompressedScalar, b: CompressedScalar, endian: std.builtin.Endian)
}
/// Return a random scalar
pub fn random(endian: std.builtin.Endian) CompressedScalar {
return Scalar.random().toBytes(endian);
pub fn random(io: std.Io, endian: std.builtin.Endian) CompressedScalar {
return Scalar.random(io).toBytes(endian);
}
/// A scalar in unpacked representation.
@@ -170,10 +170,10 @@ pub const Scalar = struct {
}
/// Return a random scalar < L.
pub fn random() Scalar {
pub fn random(io: std.Io) Scalar {
var s: [48]u8 = undefined;
while (true) {
crypto.random.bytes(&s);
io.random(&s);
const n = Scalar.fromBytes48(s, .little);
if (!n.isZero()) {
return n;
+11 -6
View File
@@ -5,8 +5,9 @@ const testing = std.testing;
const P256 = @import("../p256.zig").P256;
test "p256 ECDH key exchange" {
const dha = P256.scalar.random(.little);
const dhb = P256.scalar.random(.little);
const io = testing.io;
const dha = P256.scalar.random(io, .little);
const dhb = P256.scalar.random(io, .little);
const dhA = try P256.basePoint.mul(dha, .little);
const dhB = try P256.basePoint.mul(dhb, .little);
const shareda = try dhA.mul(dhb, .little);
@@ -66,28 +67,32 @@ test "p256 test vectors - doubling" {
}
test "p256 compressed sec1 encoding/decoding" {
const p = P256.random();
const io = testing.io;
const p = P256.random(io);
const s = p.toCompressedSec1();
const q = try P256.fromSec1(&s);
try testing.expect(p.equivalent(q));
}
test "p256 uncompressed sec1 encoding/decoding" {
const p = P256.random();
const io = testing.io;
const p = P256.random(io);
const s = p.toUncompressedSec1();
const q = try P256.fromSec1(&s);
try testing.expect(p.equivalent(q));
}
test "p256 public key is the neutral element" {
const io = testing.io;
const n = P256.scalar.Scalar.zero.toBytes(.little);
const p = P256.random();
const p = P256.random(io);
try testing.expectError(error.IdentityElement, p.mul(n, .little));
}
test "p256 public key is the neutral element (public verification)" {
const io = testing.io;
const n = P256.scalar.Scalar.zero.toBytes(.little);
const p = P256.random();
const p = P256.random(io);
try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
}
+11 -6
View File
@@ -5,8 +5,9 @@ const testing = std.testing;
const P384 = @import("../p384.zig").P384;
test "p384 ECDH key exchange" {
const dha = P384.scalar.random(.little);
const dhb = P384.scalar.random(.little);
const io = testing.io;
const dha = P384.scalar.random(io, .little);
const dhb = P384.scalar.random(io, .little);
const dhA = try P384.basePoint.mul(dha, .little);
const dhB = try P384.basePoint.mul(dhb, .little);
const shareda = try dhA.mul(dhb, .little);
@@ -67,7 +68,8 @@ test "p384 test vectors - doubling" {
}
test "p384 compressed sec1 encoding/decoding" {
const p = P384.random();
const io = testing.io;
const p = P384.random(io);
const s0 = p.toUncompressedSec1();
const s = p.toCompressedSec1();
try testing.expectEqualSlices(u8, s0[1..49], s[1..49]);
@@ -76,21 +78,24 @@ test "p384 compressed sec1 encoding/decoding" {
}
test "p384 uncompressed sec1 encoding/decoding" {
const p = P384.random();
const io = testing.io;
const p = P384.random(io);
const s = p.toUncompressedSec1();
const q = try P384.fromSec1(&s);
try testing.expect(p.equivalent(q));
}
test "p384 public key is the neutral element" {
const io = testing.io;
const n = P384.scalar.Scalar.zero.toBytes(.little);
const p = P384.random();
const p = P384.random(io);
try testing.expectError(error.IdentityElement, p.mul(n, .little));
}
test "p384 public key is the neutral element (public verification)" {
const io = testing.io;
const n = P384.scalar.Scalar.zero.toBytes(.little);
const p = P384.random();
const p = P384.random(io);
try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
}
+14 -8
View File
@@ -5,8 +5,9 @@ const testing = std.testing;
const Secp256k1 = @import("../secp256k1.zig").Secp256k1;
test "secp256k1 ECDH key exchange" {
const dha = Secp256k1.scalar.random(.little);
const dhb = Secp256k1.scalar.random(.little);
const io = testing.io;
const dha = Secp256k1.scalar.random(io, .little);
const dhb = Secp256k1.scalar.random(io, .little);
const dhA = try Secp256k1.basePoint.mul(dha, .little);
const dhB = try Secp256k1.basePoint.mul(dhb, .little);
const shareda = try dhA.mul(dhb, .little);
@@ -15,8 +16,9 @@ test "secp256k1 ECDH key exchange" {
}
test "secp256k1 ECDH key exchange including public multiplication" {
const dha = Secp256k1.scalar.random(.little);
const dhb = Secp256k1.scalar.random(.little);
const io = testing.io;
const dha = Secp256k1.scalar.random(io, .little);
const dhb = Secp256k1.scalar.random(io, .little);
const dhA = try Secp256k1.basePoint.mul(dha, .little);
const dhB = try Secp256k1.basePoint.mulPublic(dhb, .little);
const shareda = try dhA.mul(dhb, .little);
@@ -77,28 +79,32 @@ test "secp256k1 test vectors - doubling" {
}
test "secp256k1 compressed sec1 encoding/decoding" {
const p = Secp256k1.random();
const io = testing.io;
const p = Secp256k1.random(io);
const s = p.toCompressedSec1();
const q = try Secp256k1.fromSec1(&s);
try testing.expect(p.equivalent(q));
}
test "secp256k1 uncompressed sec1 encoding/decoding" {
const p = Secp256k1.random();
const io = testing.io;
const p = Secp256k1.random(io);
const s = p.toUncompressedSec1();
const q = try Secp256k1.fromSec1(&s);
try testing.expect(p.equivalent(q));
}
test "secp256k1 public key is the neutral element" {
const io = testing.io;
const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);
const p = Secp256k1.random();
const p = Secp256k1.random(io);
try testing.expectError(error.IdentityElement, p.mul(n, .little));
}
test "secp256k1 public key is the neutral element (public verification)" {
const io = testing.io;
const n = Secp256k1.scalar.Scalar.zero.toBytes(.little);
const p = Secp256k1.random();
const p = Secp256k1.random(io);
try testing.expectError(error.IdentityElement, p.mulPublic(n, .little));
}
+19 -15
View File
@@ -533,9 +533,9 @@ pub const SealedBox = struct {
/// Encrypt a message `m` for a recipient whose public key is `public_key`.
/// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added.
pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {
pub fn seal(io: std.Io, c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void {
debug.assert(c.len == m.len + seal_length);
var ekp = KeyPair.generate();
var ekp = KeyPair.generate(io);
const nonce = createNonce(ekp.public_key, public_key);
c[0..public_length].* = ekp.public_key;
try Box.seal(c[Box.public_length..], m, nonce, public_key, ekp.secret_key);
@@ -573,29 +573,31 @@ test "(x)salsa20" {
}
test "xsalsa20poly1305" {
const io = std.testing.io;
var msg: [100]u8 = undefined;
var msg2: [msg.len]u8 = undefined;
var c: [msg.len]u8 = undefined;
var key: [XSalsa20Poly1305.key_length]u8 = undefined;
var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
crypto.random.bytes(&msg);
crypto.random.bytes(&key);
crypto.random.bytes(&nonce);
io.random(&msg);
io.random(&key);
io.random(&nonce);
XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
}
test "xsalsa20poly1305 secretbox" {
const io = std.testing.io;
var msg: [100]u8 = undefined;
var msg2: [msg.len]u8 = undefined;
var key: [XSalsa20Poly1305.key_length]u8 = undefined;
var nonce: [Box.nonce_length]u8 = undefined;
var boxed: [msg.len + Box.tag_length]u8 = undefined;
crypto.random.bytes(&msg);
crypto.random.bytes(&key);
crypto.random.bytes(&nonce);
io.random(&msg);
io.random(&key);
io.random(&nonce);
SecretBox.seal(boxed[0..], msg[0..], nonce, key);
try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
@@ -604,15 +606,16 @@ test "xsalsa20poly1305 secretbox" {
test "xsalsa20poly1305 box" {
if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299
const io = std.testing.io;
var msg: [100]u8 = undefined;
var msg2: [msg.len]u8 = undefined;
var nonce: [Box.nonce_length]u8 = undefined;
var boxed: [msg.len + Box.tag_length]u8 = undefined;
crypto.random.bytes(&msg);
crypto.random.bytes(&nonce);
io.random(&msg);
io.random(&nonce);
const kp1 = Box.KeyPair.generate();
const kp2 = Box.KeyPair.generate();
const kp1 = Box.KeyPair.generate(io);
const kp2 = Box.KeyPair.generate(io);
try Box.seal(boxed[0..], msg[0..], nonce, kp1.public_key, kp2.secret_key);
try Box.open(msg2[0..], boxed[0..], nonce, kp2.public_key, kp1.secret_key);
}
@@ -620,13 +623,14 @@ test "xsalsa20poly1305 box" {
test "xsalsa20poly1305 sealedbox" {
if (builtin.cpu.has(.riscv, .v) and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/24299
const io = std.testing.io;
var msg: [100]u8 = undefined;
var msg2: [msg.len]u8 = undefined;
var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
crypto.random.bytes(&msg);
io.random(&msg);
const kp = Box.KeyPair.generate();
try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);
const kp = Box.KeyPair.generate(io);
try SealedBox.seal(io, boxed[0..], msg[0..], kp.public_key);
try SealedBox.open(msg2[0..], boxed[0..], kp);
}
+5 -6
View File
@@ -20,7 +20,7 @@ const Error = pwhash.Error;
const max_size = math.maxInt(usize);
const max_int = max_size >> 1;
const default_salt_len = 32;
pub const default_salt_len = 32;
const default_hash_len = 32;
const max_salt_len = 64;
const max_hash_len = 64;
@@ -417,10 +417,9 @@ const PhcFormatHasher = struct {
password: []const u8,
params: Params,
buf: []u8,
/// Filled with cryptographically secure entropy.
salt: []const u8,
) HasherError![]const u8 {
var salt: [default_salt_len]u8 = undefined;
crypto.random.bytes(&salt);
var hash: [default_hash_len]u8 = undefined;
try kdf(allocator, &hash, password, &salt, params);
@@ -466,9 +465,9 @@ const CryptFormatHasher = struct {
password: []const u8,
params: Params,
buf: []u8,
/// Filled with cryptographically secure entropy.
salt_bin: []const u8,
) HasherError![]const u8 {
var salt_bin: [default_salt_len]u8 = undefined;
crypto.random.bytes(&salt_bin);
const salt = crypt_format.saltFromBin(salt_bin.len, salt_bin);
var hash: [default_hash_len]u8 = undefined;
+12 -11
View File
@@ -180,24 +180,24 @@ pub fn declassify(ptr: anytype) void {
}
test eql {
const random = std.crypto.random;
const io = std.testing.io;
const expect = std.testing.expect;
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
random.bytes(a[0..]);
random.bytes(b[0..]);
io.random(&a);
io.random(&b);
try expect(!eql([100]u8, a, b));
a = b;
try expect(eql([100]u8, a, b));
}
test "eql (vectors)" {
const random = std.crypto.random;
const io = std.testing.io;
const expect = std.testing.expect;
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
random.bytes(a[0..]);
random.bytes(b[0..]);
io.random(&a);
io.random(&b);
const v1: @Vector(100, u8) = a;
const v2: @Vector(100, u8) = b;
try expect(!eql(@Vector(100, u8), v1, v2));
@@ -220,9 +220,10 @@ test compare {
}
test "add and sub" {
const io = std.testing.io;
const expectEqual = std.testing.expectEqual;
const expectEqualSlices = std.testing.expectEqualSlices;
const random = std.crypto.random;
const len = 32;
var a: [len]u8 = undefined;
var b: [len]u8 = undefined;
@@ -230,8 +231,8 @@ test "add and sub" {
const zero = [_]u8{0} ** len;
var iterations: usize = 100;
while (iterations != 0) : (iterations -= 1) {
random.bytes(&a);
random.bytes(&b);
io.random(&a);
io.random(&b);
const endian = if (iterations % 2 == 0) Endian.big else Endian.little;
_ = sub(u8, &a, &b, &c, endian); // a-b
_ = add(u8, &c, &b, &c, endian); // (a-b)+b
@@ -243,11 +244,11 @@ test "add and sub" {
}
test classify {
const random = std.crypto.random;
const io = std.testing.io;
const expect = std.testing.expect;
var secret: [32]u8 = undefined;
random.bytes(&secret);
io.random(&secret);
// Input of the hash function is marked as secret
classify(&secret);
+7 -7
View File
@@ -109,7 +109,7 @@ pub const Options = struct {
read_buffer: []u8,
/// Cryptographically secure random bytes. The pointer is not captured; data is only
/// read during `init`.
entropy: *const [176]u8,
entropy: *const [240]u8,
/// Current time according to the wall clock / calendar, in seconds.
realtime_now_seconds: i64,
@@ -200,7 +200,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
var server_hello_rand: [32]u8 = undefined;
const legacy_session_id = options.entropy[32..64].*;
var key_share = KeyShare.init(options.entropy[64..176].*) catch |err| switch (err) {
var key_share = KeyShare.init(options.entropy[64..240]) catch |err| switch (err) {
// Only possible to happen if the seed is all zeroes.
error.IdentityElement => return error.InsufficientEntropy,
};
@@ -1330,12 +1330,12 @@ const KeyShare = struct {
crypto.dh.X25519.shared_length,
);
fn init(seed: [112]u8) error{IdentityElement}!KeyShare {
fn init(seed: *const [176]u8) error{IdentityElement}!KeyShare {
return .{
.ml_kem768_kp = .generate(),
.secp256r1_kp = try .generateDeterministic(seed[0..32].*),
.secp384r1_kp = try .generateDeterministic(seed[32..80].*),
.x25519_kp = try .generateDeterministic(seed[80..112].*),
.ml_kem768_kp = try .generateDeterministic(seed[0..64].*),
.secp256r1_kp = try .generateDeterministic(seed[64..96].*),
.secp384r1_kp = try .generateDeterministic(seed[96..144].*),
.x25519_kp = try .generateDeterministic(seed[144..176].*),
.sk_buf = undefined,
.sk_len = 0,
};
+1 -1
View File
@@ -1761,7 +1761,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
const io = testing.io;
var random_bytes: [12]u8 = undefined;
std.crypto.random.bytes(&random_bytes);
io.random(&random_bytes);
var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
_ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);
+2 -2
View File
@@ -321,8 +321,8 @@ pub const Connection = struct {
assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);
@memcpy(host_buffer, remote_host.bytes);
const tls: *Tls = @ptrCast(base);
var random_buffer: [176]u8 = undefined;
std.crypto.random.bytes(&random_buffer);
var random_buffer: [240]u8 = undefined;
io.random(&random_buffer);
tls.* = .{
.connection = .{
.client = client,
+5 -7
View File
@@ -531,13 +531,11 @@ pub fn prepareArea(area: []u8) usize {
};
}
/// The main motivation for the size chosen here is that this is how much ends up being requested for
/// the thread-local variables of the `std.crypto.random` implementation. I'm not sure why it ends up
/// being so much; the struct itself is only 64 bytes. I think it has to do with being page-aligned
/// and LLVM or LLD is not smart enough to lay out the TLS data in a space-conserving way. Anyway, I
/// think it's fine because it's less than 3 pages of memory, and putting it in the ELF like this is
/// equivalent to moving the `mmap` call below into the kernel, avoiding syscall overhead.
var main_thread_area_buffer: [0x2100]u8 align(page_size_min) = undefined;
/// The main motivation for the size chosen here is to be larger than total
/// amount of thread-local variables for most programs. Putting this allocation
/// in the ELF like this is equivalent to moving the `mmap` call below into the
/// kernel, avoiding syscall overhead.
var main_thread_area_buffer: [0x1000]u8 align(page_size_min) = undefined;
/// Computes the layout of the static TLS area, allocates the area, initializes all of its fields,
/// and assigns the architecture-specific value to the TP register.
-10
View File
@@ -33,16 +33,6 @@ test "check WASI CWD" {
}
}
test "getrandom" {
var buf_a: [50]u8 = undefined;
var buf_b: [50]u8 = undefined;
try posix.getrandom(&buf_a);
try posix.getrandom(&buf_b);
// If this test fails the chance is significantly higher that there is a bug than
// that two sets of 50 bytes were equal.
try expect(!mem.eql(u8, &buf_a, &buf_b));
}
test "getuid" {
if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
_ = posix.getuid();
+1 -1
View File
@@ -631,7 +631,7 @@ pub const TmpDir = struct {
pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {
comptime assert(builtin.is_test);
var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
std.crypto.random.bytes(&random_bytes);
io.random(&random_bytes);
var sub_path: [TmpDir.sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
+13 -4
View File
@@ -2942,7 +2942,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
.none => |none| {
assert(none.tmp_artifact_directory == null);
none.tmp_artifact_directory = d: {
tmp_dir_rand_int = std.crypto.random.int(u64);
io.random(@ptrCast(&tmp_dir_rand_int));
const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
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
// Compile the artifacts to a temporary directory.
whole.tmp_artifact_directory = d: {
tmp_dir_rand_int = std.crypto.random.int(u64);
io.random(@ptrCast(&tmp_dir_rand_int));
const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(tmp_dir_rand_int);
const path = try comp.dirs.local_cache.join(arena, &.{tmp_dir_sub_path});
const handle = comp.dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{}) catch |err| {
@@ -5759,7 +5759,11 @@ pub fn translateC(
const gpa = comp.gpa;
const io = comp.io;
const tmp_basename = std.fmt.hex(std.crypto.random.int(u64));
const tmp_basename = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r std.fmt.hex(x);
};
const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
const cache_dir = comp.dirs.local_cache.handle;
var cache_tmp_dir = try cache_dir.createDirPathOpen(io, tmp_sub_path, .{});
@@ -6889,8 +6893,13 @@ fn spawnZigRc(
}
pub fn tmpFilePath(comp: Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
const io = comp.io;
const rand_int = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
const s = fs.path.sep_str;
const rand_int = std.crypto.random.int(u64);
if (comp.dirs.local_cache.path) |p| {
return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
} else {
+2 -2
View File
@@ -14,9 +14,9 @@ pub const Fingerprint = packed struct(u64) {
id: u32,
checksum: u32,
pub fn generate(name: []const u8) Fingerprint {
pub fn generate(rng: std.Random, name: []const u8) Fingerprint {
return .{
.id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff),
.id = rng.intRangeLessThan(u32, 1, 0xffffffff),
.checksum = std.hash.Crc32.hash(name),
};
}
+13 -3
View File
@@ -494,7 +494,11 @@ fn runResource(
const eb = &f.error_bundle;
const s = fs.path.sep_str;
const cache_root = f.job_queue.global_cache;
const rand_int = std.crypto.random.int(u64);
const rand_int = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(rand_int);
const package_sub_path = blk: {
@@ -690,7 +694,9 @@ fn loadManifest(f: *Fetch, pkg_root: Cache.Path) RunError!void {
return error.FetchFailed;
}
f.manifest = try Manifest.parse(arena, ast.*, .{
const rng: std.Random.IoSource = .{ .io = io };
f.manifest = try Manifest.parse(arena, ast.*, rng.interface(), .{
.allow_missing_paths_field = f.allow_missing_paths_field,
.allow_missing_fingerprint = f.allow_missing_fingerprint,
.allow_name_string = f.allow_name_string,
@@ -1305,7 +1311,11 @@ fn unzip(
zip_path[prefix.len + random_len ..].* = suffix.*;
var zip_file = while (true) {
const random_integer = std.crypto.random.int(u64);
const random_integer = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
zip_path[prefix.len..][0..random_len].* = std.fmt.hex(random_integer);
break cache_root.handle.createFile(io, &zip_path, .{
+5 -5
View File
@@ -57,7 +57,7 @@ pub const ParseOptions = struct {
pub const Error = Allocator.Error;
pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {
pub fn parse(gpa: Allocator, ast: Ast, rng: std.Random, options: ParseOptions) Error!Manifest {
const main_node_index = ast.nodeData(.root).node;
var arena_instance = std.heap.ArenaAllocator.init(gpa);
@@ -87,7 +87,7 @@ pub fn parse(gpa: Allocator, ast: Ast, options: ParseOptions) Error!Manifest {
defer p.dependencies.deinit(gpa);
defer p.paths.deinit(gpa);
p.parseRoot(main_node_index) catch |err| switch (err) {
p.parseRoot(main_node_index, rng) catch |err| switch (err) {
error.ParseFailure => assert(p.errors.items.len > 0),
else => |e| return e,
};
@@ -157,7 +157,7 @@ const Parse = struct {
const InnerError = error{ ParseFailure, OutOfMemory };
fn parseRoot(p: *Parse, node: Ast.Node.Index) !void {
fn parseRoot(p: *Parse, node: Ast.Node.Index, rng: std.Random) !void {
const ast = p.ast;
const main_token = ast.nodeMainToken(node);
@@ -217,13 +217,13 @@ const Parse = struct {
if (fingerprint) |n| {
if (!n.validate(p.name)) {
return fail(p, main_token, "invalid fingerprint: 0x{x}; if this is a new or forked package, use this value: 0x{x}", .{
n.int(), Package.Fingerprint.generate(p.name).int(),
n.int(), Package.Fingerprint.generate(rng, p.name).int(),
});
}
p.id = n.id;
} else if (!p.allow_missing_fingerprint) {
try appendError(p, main_token, "missing top-level 'fingerprint' field; suggested value: 0x{x}", .{
Package.Fingerprint.generate(p.name).int(),
Package.Fingerprint.generate(rng, p.name).int(),
});
} else {
p.id = 0;
+6 -1
View File
@@ -616,8 +616,13 @@ pub const File = struct {
// it will return ETXTBSY. So instead, we copy the file, atomically rename it
// over top of the exe path, and then proceed normally. This changes the inode,
// avoiding the error.
const random_integer = r: {
var x: u32 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{
emit.sub_path, std.crypto.random.int(u32),
emit.sub_path, random_integer,
});
defer gpa.free(tmp_sub_path);
try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, io, .{});
+5 -1
View File
@@ -1636,7 +1636,11 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
const err = switch (first_err) {
error.NameTooLong => err: {
const s = fs.path.sep_str;
const rand_int = std.crypto.random.int(u64);
const rand_int = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp";
const rsp_file = try comp.dirs.local_cache.handle.createFile(io, rsp_path, .{});
+19 -12
View File
@@ -3395,7 +3395,7 @@ fn buildOutputType(
// "-" is stdin. Dump it to a real file.
const sep = fs.path.sep_str;
const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{
std.crypto.random.int(u64), ext.canonicalName(target),
randInt(io, u64), ext.canonicalName(target),
});
try dirs.local_cache.handle.createDirPath(io, "tmp");
@@ -4433,7 +4433,7 @@ fn runOrTest(
try argv.append(exe_path);
if (arg_mode == .zig_test) {
try argv.append(
try std.fmt.allocPrint(arena, "--seed=0x{x}", .{std.crypto.random.int(u32)}),
try std.fmt.allocPrint(arena, "--seed=0x{x}", .{randInt(io, u32)}),
);
}
} else {
@@ -4763,7 +4763,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
const cwd_basename = fs.path.basename(cwd_path);
const sanitized_root_name = try sanitizeExampleName(arena, cwd_basename);
const fingerprint: Package.Fingerprint = .generate(sanitized_root_name);
const rng: std.Random.IoSource = .{ .io = io };
const fingerprint: Package.Fingerprint = .generate(rng.interface(), sanitized_root_name);
switch (template) {
.example => {
@@ -4919,7 +4920,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
try child_argv.appendSlice(&.{
"--seed",
try std.fmt.allocPrint(arena, "0x{x}", .{std.crypto.random.int(u32)}),
try std.fmt.allocPrint(arena, "0x{x}", .{randInt(io, u32)}),
});
const argv_index_seed = child_argv.items.len - 1;
@@ -4937,7 +4938,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8,
// the strategy is to choose a temporary file name ahead of time, and then
// read this file in the parent to obtain the results, in the case the child
// exits with code 3.
const results_tmp_file_nonce = std.fmt.hex(std.crypto.random.int(u64));
const results_tmp_file_nonce = std.fmt.hex(randInt(io, u64));
try child_argv.append("-Z" ++ results_tmp_file_nonce);
var color: Color = .auto;
@@ -7223,7 +7224,7 @@ fn createDependenciesModule(
) !*Package.Module {
// Atomically create the file in a directory named after the hash of its contents.
const basename = "dependencies.zig";
const rand_int = std.crypto.random.int(u64);
const rand_int = randInt(io, u64);
const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
{
var tmp_dir = try dirs.local_cache.handle.createDirPathOpen(io, tmp_dir_sub_path, .{});
@@ -7339,6 +7340,8 @@ fn loadManifest(
io: Io,
options: LoadManifestOptions,
) !struct { Package.Manifest, Ast } {
const rng: std.Random.IoSource = .{ .io = io };
const manifest_bytes = while (true) {
break options.dir.readFileAllocOptions(
io,
@@ -7360,15 +7363,13 @@ fn loadManifest(
, .{
options.root_name,
build_options.version,
Package.Fingerprint.generate(options.root_name).int(),
Package.Fingerprint.generate(rng.interface(), options.root_name).int(),
}) catch |e| {
fatal("unable to write {s}: {s}", .{ Package.Manifest.basename, @errorName(e) });
fatal("unable to write {s}: {t}", .{ Package.Manifest.basename, e });
};
continue;
},
else => |e| fatal("unable to load {s}: {s}", .{
Package.Manifest.basename, @errorName(e),
}),
else => |e| fatal("unable to load {s}: {t}", .{ Package.Manifest.basename, e }),
};
};
var ast = try Ast.parse(gpa, manifest_bytes, .zon);
@@ -7379,7 +7380,7 @@ fn loadManifest(
process.exit(2);
}
var manifest = try Package.Manifest.parse(gpa, ast, .{});
var manifest = try Package.Manifest.parse(gpa, ast, rng.interface(), .{});
errdefer manifest.deinit(gpa);
if (manifest.errors.len > 0) {
@@ -7632,3 +7633,9 @@ fn setThreadLimit(n: usize) void {
threaded_impl_ptr.setAsyncLimit(limit);
threaded_impl_ptr.concurrent_limit = limit;
}
fn randInt(io: Io, comptime T: type) T {
var x: T = undefined;
io.random(@ptrCast(&x));
return x;
}
+2 -1
View File
@@ -10,7 +10,8 @@ pub fn main(init: std.process.Init) !void {
try out.writeAll("Welcome to the Guess Number Game in Zig.\n");
const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;
var rng: std.Random.IoSource = .{ .io = init.io };
const answer = rng.interface().intRangeLessThan(u8, 0, 100) + 1;
while (true) {
try out.writeAll("\nGuess a number between 1 and 100: ");
+4 -3
View File
@@ -78,9 +78,10 @@ pub fn main(init: std.process.Init) !void {
const code = try parseManifest(arena, source_bytes);
const source = stripManifest(source_bytes);
const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{
cache_root, std.crypto.random.int(u64),
});
var random_integer: u64 = undefined;
io.random(@ptrCast(&random_integer));
const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{ cache_root, random_integer });
Dir.cwd().createDirPath(io, tmp_dir_path) catch |err|
fatal("unable to create tmp dir '{s}': {t}", .{ tmp_dir_path, err });
defer Dir.cwd().deleteTree(io, tmp_dir_path) catch |err| std.log.err("unable to delete '{s}': {t}", .{
+9 -4
View File
@@ -100,7 +100,7 @@ pub fn main(init: std.process.Init) !void {
const prog_node = std.Progress.start(io, .{});
defer prog_node.end();
const rand_int = std.crypto.random.int(u64);
const rand_int = rand64(io);
const tmp_dir_path = "tmp_" ++ std.fmt.hex(rand_int);
var tmp_dir = try Dir.cwd().createDirPathOpen(io, tmp_dir_path, .{});
defer {
@@ -452,20 +452,19 @@ const Eval = struct {
std.debug.assert(eval.target.backend == .sema);
return;
};
const io = eval.io;
const binary_path = switch (eval.target.backend) {
.sema => unreachable,
.selfhosted, .llvm => emitted_path,
.cbe => bin: {
const rand_int = std.crypto.random.int(u64);
const rand_int = rand64(io);
const out_bin_name = "./out_" ++ std.fmt.hex(rand_int);
try eval.buildCOutput(emitted_path, out_bin_name, prog_node);
break :bin out_bin_name;
},
};
const io = eval.io;
var argv_buf: [2][]const u8 = undefined;
const argv: []const []const u8, const is_foreign: bool = sw: switch (std.zig.system.getExternalExecutor(
io,
@@ -957,3 +956,9 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {
.msg = message,
};
}
fn rand64(io: Io) u64 {
var x: u64 = undefined;
io.random(@ptrCast(&x));
return x;
}