mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 23:52:31 +00:00
fe93332ba2
* implement vector comparison * implement reduce for bool vectors * fix `@memcpy` bug * enable passing std tests
81 lines
2.7 KiB
Zig
81 lines
2.7 KiB
Zig
const std = @import("../std.zig");
|
||
const sha2 = std.crypto.hash.sha2;
|
||
|
||
/// The composition of two hash functions: H1 o H2, with the same API as regular hash functions.
|
||
///
|
||
/// The security level of a hash cascade doesn't exceed the security level of the weakest function.
|
||
///
|
||
/// However, Merkle–Damgård constructions such as SHA-256 are vulnerable to length-extension attacks,
|
||
/// where under some conditions, `H(x||e)` can be efficiently computed without knowing `x`.
|
||
/// The composition of two hash functions is a common defense against such attacks.
|
||
///
|
||
/// This is not necessary with modern hash functions, such as SHA-3, BLAKE2 and BLAKE3.
|
||
pub fn Composition(comptime H1: type, comptime H2: type) type {
|
||
return struct {
|
||
const Self = @This();
|
||
|
||
H1: H1,
|
||
H2: H2,
|
||
|
||
/// The length of the hash output, in bytes.
|
||
pub const digest_length = H1.digest_length;
|
||
/// The block length, in bytes.
|
||
pub const block_length = H1.block_length;
|
||
|
||
/// Options for both hashes.
|
||
pub const Options = struct {
|
||
/// Options for H1.
|
||
H1: H1.Options = .{},
|
||
/// Options for H2.
|
||
H2: H2.Options = .{},
|
||
};
|
||
|
||
/// Initialize the hash composition with the given options.
|
||
pub fn init(options: Options) Self {
|
||
return Self{ .H1 = H1.init(options.H1), .H2 = H2.init(options.H2) };
|
||
}
|
||
|
||
/// Compute H1(H2(b)).
|
||
pub fn hash(b: []const u8, out: *[digest_length]u8, options: Options) void {
|
||
var d = Self.init(options);
|
||
d.update(b);
|
||
d.final(out);
|
||
}
|
||
|
||
/// Add content to the hash.
|
||
pub fn update(d: *Self, b: []const u8) void {
|
||
d.H2.update(b);
|
||
}
|
||
|
||
/// Compute the final hash for the accumulated content: H1(H2(b)).
|
||
pub fn final(d: *Self, out: *[digest_length]u8) void {
|
||
var H2_digest: [H2.digest_length]u8 = undefined;
|
||
d.H2.final(&H2_digest);
|
||
d.H1.update(&H2_digest);
|
||
d.H1.final(out);
|
||
}
|
||
};
|
||
}
|
||
|
||
/// SHA-256(SHA-256())
|
||
pub const Sha256oSha256 = Composition(sha2.Sha256, sha2.Sha256);
|
||
/// SHA-384(SHA-384())
|
||
pub const Sha384oSha384 = Composition(sha2.Sha384, sha2.Sha384);
|
||
/// SHA-512(SHA-512())
|
||
pub const Sha512oSha512 = Composition(sha2.Sha512, sha2.Sha512);
|
||
|
||
test "Hash composition" {
|
||
const Sha256 = sha2.Sha256;
|
||
const msg = "test";
|
||
|
||
var out: [Sha256oSha256.digest_length]u8 = undefined;
|
||
Sha256oSha256.hash(msg, &out, .{});
|
||
|
||
var t: [Sha256.digest_length]u8 = undefined;
|
||
Sha256.hash(msg, &t, .{});
|
||
var out2: [Sha256.digest_length]u8 = undefined;
|
||
Sha256.hash(&t, &out2, .{});
|
||
|
||
try std.testing.expectEqualSlices(u8, &out, &out2);
|
||
}
|