mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-26 13:01:34 +03:00
update API usage of std.crypto.random to io.random
This commit is contained in:
Vendored
+2
-1
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -38,7 +38,7 @@ pub const IoSource = struct {
|
||||
|
||||
pub fn interface(this: *const @This()) std.Random {
|
||||
return .{
|
||||
.ptr = this,
|
||||
.ptr = @constCast(this),
|
||||
.fillFn = fill,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
@@ -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()});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, .{
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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}", .{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user