zig/lib/std/crypto/hash_composition.zig
Jacob Young fe93332ba2 x86_64: implement enough to pass unicode tests
* implement vector comparison
 * implement reduce for bool vectors
 * fix `@memcpy` bug
 * enable passing std tests
2023-10-23 22:42:18 -04:00

81 lines
2.7 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, MerkleDamgå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);
}