Commit 6f9ea9eaef

Frank Denis <github@pureftpd.org>
2020-08-19 16:21:05
Breaking: sort std/crypto functions into categories
Instead of having all primitives and constructions share the same namespace, they are now organized by category and function family. Types within the same category are expected to share the exact same API.
1 parent 1a4059e
lib/std/build/write_file.zig
@@ -58,7 +58,7 @@ pub const WriteFileStep = struct {
         // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b
         // directly and construct the path, and no "cache hit" detection happens; the files
         // are always written.
-        var hash = std.crypto.Blake2b384.init();
+        var hash = std.crypto.hash.blake2.Blake2b384.init();
 
         // Random bytes to make WriteFileStep unique. Refresh this with
         // new random bytes when WriteFileStep implementation is modified
lib/std/crypto/25519/ed25519.zig
@@ -6,7 +6,7 @@
 const std = @import("std");
 const fmt = std.fmt;
 const mem = std.mem;
-const Sha512 = std.crypto.Sha512;
+const Sha512 = std.crypto.hash.sha2.Sha512;
 
 /// Ed25519 (EdDSA) signatures.
 pub const Ed25519 = struct {
lib/std/crypto/benchmark.zig
@@ -22,16 +22,16 @@ const Crypto = struct {
 };
 
 const hashes = [_]Crypto{
-    Crypto{ .ty = crypto.Md5, .name = "md5" },
-    Crypto{ .ty = crypto.Sha1, .name = "sha1" },
-    Crypto{ .ty = crypto.Sha256, .name = "sha256" },
-    Crypto{ .ty = crypto.Sha512, .name = "sha512" },
-    Crypto{ .ty = crypto.Sha3_256, .name = "sha3-256" },
-    Crypto{ .ty = crypto.Sha3_512, .name = "sha3-512" },
-    Crypto{ .ty = crypto.gimli.Hash, .name = "gimli-hash" },
-    Crypto{ .ty = crypto.Blake2s256, .name = "blake2s" },
-    Crypto{ .ty = crypto.Blake2b512, .name = "blake2b" },
-    Crypto{ .ty = crypto.Blake3, .name = "blake3" },
+    Crypto{ .ty = crypto.hash.Md5, .name = "md5" },
+    Crypto{ .ty = crypto.hash.Sha1, .name = "sha1" },
+    Crypto{ .ty = crypto.hash.sha2.Sha256, .name = "sha256" },
+    Crypto{ .ty = crypto.hash.sha2.Sha512, .name = "sha512" },
+    Crypto{ .ty = crypto.hash.sha3.Sha3_256, .name = "sha3-256" },
+    Crypto{ .ty = crypto.hash.sha3.Sha3_512, .name = "sha3-512" },
+    Crypto{ .ty = crypto.hash.Gimli, .name = "gimli-hash" },
+    Crypto{ .ty = crypto.hash.blake2.Blake2s256, .name = "blake2s" },
+    Crypto{ .ty = crypto.hash.blake2.Blake2b512, .name = "blake2b" },
+    Crypto{ .ty = crypto.hash.Blake3, .name = "blake3" },
 };
 
 pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64 {
@@ -55,10 +55,11 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
 }
 
 const macs = [_]Crypto{
-    Crypto{ .ty = crypto.Poly1305, .name = "poly1305" },
-    Crypto{ .ty = crypto.HmacMd5, .name = "hmac-md5" },
-    Crypto{ .ty = crypto.HmacSha1, .name = "hmac-sha1" },
-    Crypto{ .ty = crypto.HmacSha256, .name = "hmac-sha256" },
+    Crypto{ .ty = crypto.onetimeauth.Poly1305, .name = "poly1305" },
+    Crypto{ .ty = crypto.auth.HmacMd5, .name = "hmac-md5" },
+    Crypto{ .ty = crypto.auth.HmacSha1, .name = "hmac-sha1" },
+    Crypto{ .ty = crypto.auth.sha2.HmacSha256, .name = "hmac-sha256" },
+    Crypto{ .ty = crypto.auth.sha2.HmacSha512, .name = "hmac-sha512" },
 };
 
 pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
@@ -84,7 +85,7 @@ pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
     return throughput;
 }
 
-const exchanges = [_]Crypto{Crypto{ .ty = crypto.X25519, .name = "x25519" }};
+const exchanges = [_]Crypto{Crypto{ .ty = crypto.dh.X25519, .name = "x25519" }};
 
 pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_count: comptime_int) !u64 {
     std.debug.assert(DhKeyExchange.minimum_key_length >= DhKeyExchange.secret_length);
@@ -111,7 +112,7 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
     return throughput;
 }
 
-const signatures = [_]Crypto{Crypto{ .ty = crypto.Ed25519, .name = "ed25519" }};
+const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }};
 
 pub fn benchmarkSignatures(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 {
     var seed: [Signature.seed_length]u8 = undefined;
lib/std/crypto/chacha20.zig
@@ -12,7 +12,7 @@ const assert = std.debug.assert;
 const testing = std.testing;
 const builtin = @import("builtin");
 const maxInt = std.math.maxInt;
-const Poly1305 = std.crypto.Poly1305;
+const Poly1305 = std.crypto.onetimeauth.Poly1305;
 
 const QuarterRound = struct {
     a: usize,
@@ -137,56 +137,60 @@ fn keyToWords(key: [32]u8) [8]u32 {
 ///
 /// ChaCha20 is self-reversing. To decrypt just run the cipher with the same
 /// counter, nonce, and key.
-pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {
-    assert(in.len >= out.len);
-    assert((in.len >> 6) + counter <= maxInt(u32));
-
-    var c: [4]u32 = undefined;
-    c[0] = counter;
-    c[1] = mem.readIntLittle(u32, nonce[0..4]);
-    c[2] = mem.readIntLittle(u32, nonce[4..8]);
-    c[3] = mem.readIntLittle(u32, nonce[8..12]);
-    chaCha20_internal(out, in, keyToWords(key), c);
-}
+pub const ChaCha20IETF = struct {
+    pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [12]u8) void {
+        assert(in.len >= out.len);
+        assert((in.len >> 6) + counter <= maxInt(u32));
+
+        var c: [4]u32 = undefined;
+        c[0] = counter;
+        c[1] = mem.readIntLittle(u32, nonce[0..4]);
+        c[2] = mem.readIntLittle(u32, nonce[4..8]);
+        c[3] = mem.readIntLittle(u32, nonce[8..12]);
+        chaCha20_internal(out, in, keyToWords(key), c);
+    }
+};
 
 /// This is the original ChaCha20 before RFC 7539, which recommends using the
 /// orgininal version on applications such as disk or file encryption that might
 /// exceed the 256 GiB limit of the 96-bit nonce version.
-pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {
-    assert(in.len >= out.len);
-    assert(counter +% (in.len >> 6) >= counter);
-
-    var cursor: usize = 0;
-    const k = keyToWords(key);
-    var c: [4]u32 = undefined;
-    c[0] = @truncate(u32, counter);
-    c[1] = @truncate(u32, counter >> 32);
-    c[2] = mem.readIntLittle(u32, nonce[0..4]);
-    c[3] = mem.readIntLittle(u32, nonce[4..8]);
-
-    const block_size = (1 << 6);
-    // The full block size is greater than the address space on a 32bit machine
-    const big_block = if (@sizeOf(usize) > 4) (block_size << 32) else maxInt(usize);
-
-    // first partial big block
-    if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
-        chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c);
-        cursor = big_block - cursor;
-        c[1] += 1;
-        if (comptime @sizeOf(usize) > 4) {
-            // A big block is giant: 256 GiB, but we can avoid this limitation
-            var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
-            var i: u32 = 0;
-            while (remaining_blocks > 0) : (remaining_blocks -= 1) {
-                chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
-                c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this.
-                cursor += big_block;
+pub const ChaCha20With64BitNonce = struct {
+    pub fn xor(out: []u8, in: []const u8, counter: u64, key: [32]u8, nonce: [8]u8) void {
+        assert(in.len >= out.len);
+        assert(counter +% (in.len >> 6) >= counter);
+
+        var cursor: usize = 0;
+        const k = keyToWords(key);
+        var c: [4]u32 = undefined;
+        c[0] = @truncate(u32, counter);
+        c[1] = @truncate(u32, counter >> 32);
+        c[2] = mem.readIntLittle(u32, nonce[0..4]);
+        c[3] = mem.readIntLittle(u32, nonce[4..8]);
+
+        const block_size = (1 << 6);
+        // The full block size is greater than the address space on a 32bit machine
+        const big_block = if (@sizeOf(usize) > 4) (block_size << 32) else maxInt(usize);
+
+        // first partial big block
+        if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
+            chaCha20_internal(out[cursor..big_block], in[cursor..big_block], k, c);
+            cursor = big_block - cursor;
+            c[1] += 1;
+            if (comptime @sizeOf(usize) > 4) {
+                // A big block is giant: 256 GiB, but we can avoid this limitation
+                var remaining_blocks: u32 = @intCast(u32, (in.len / big_block));
+                var i: u32 = 0;
+                while (remaining_blocks > 0) : (remaining_blocks -= 1) {
+                    chaCha20_internal(out[cursor .. cursor + big_block], in[cursor .. cursor + big_block], k, c);
+                    c[1] += 1; // upper 32-bit of counter, generic chaCha20_internal() doesn't know about this.
+                    cursor += big_block;
+                }
             }
         }
-    }
 
-    chaCha20_internal(out[cursor..], in[cursor..], k, c);
-}
+        chaCha20_internal(out[cursor..], in[cursor..], k, c);
+    }
+};
 
 // https://tools.ietf.org/html/rfc7539#section-2.4.2
 test "crypto.chacha20 test vector sunscreen" {
@@ -221,12 +225,12 @@ test "crypto.chacha20 test vector sunscreen" {
         0, 0, 0, 0,
     };
 
-    chaCha20IETF(result[0..], input[0..], 1, key, nonce);
+    ChaCha20IETF.xor(result[0..], input[0..], 1, key, nonce);
     testing.expectEqualSlices(u8, &expected_result, &result);
 
     // Chacha20 is self-reversing.
     var plaintext: [114]u8 = undefined;
-    chaCha20IETF(plaintext[0..], result[0..], 1, key, nonce);
+    ChaCha20IETF.xor(plaintext[0..], result[0..], 1, key, nonce);
     testing.expect(mem.order(u8, input, &plaintext) == .eq);
 }
 
@@ -261,7 +265,7 @@ test "crypto.chacha20 test vector 1" {
     };
     const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
 
-    chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
+    ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
     testing.expectEqualSlices(u8, &expected_result, &result);
 }
 
@@ -295,7 +299,7 @@ test "crypto.chacha20 test vector 2" {
     };
     const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0 };
 
-    chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
+    ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
     testing.expectEqualSlices(u8, &expected_result, &result);
 }
 
@@ -329,7 +333,7 @@ test "crypto.chacha20 test vector 3" {
     };
     const nonce = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 1 };
 
-    chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
+    ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
     testing.expectEqualSlices(u8, &expected_result, &result);
 }
 
@@ -363,7 +367,7 @@ test "crypto.chacha20 test vector 4" {
     };
     const nonce = [_]u8{ 1, 0, 0, 0, 0, 0, 0, 0 };
 
-    chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
+    ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
     testing.expectEqualSlices(u8, &expected_result, &result);
 }
 
@@ -435,21 +439,21 @@ test "crypto.chacha20 test vector 5" {
         0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
     };
 
-    chaCha20With64BitNonce(result[0..], input[0..], 0, key, nonce);
+    ChaCha20With64BitNonce.xor(result[0..], input[0..], 0, key, nonce);
     testing.expectEqualSlices(u8, &expected_result, &result);
 }
 
 pub const chacha20poly1305_tag_size = 16;
 
-pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
+fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
     assert(ciphertext.len >= plaintext.len);
 
     // derive poly1305 key
     var polyKey = [_]u8{0} ** 32;
-    chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce);
+    ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce);
 
     // encrypt plaintext
-    chaCha20IETF(ciphertext[0..plaintext.len], plaintext, 1, key, nonce);
+    ChaCha20IETF.xor(ciphertext[0..plaintext.len], plaintext, 1, key, nonce);
 
     // construct mac
     var mac = Poly1305.init(polyKey[0..]);
@@ -472,18 +476,18 @@ pub fn chacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_ta
     mac.final(tag);
 }
 
-pub fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
+fn chacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) void {
     return chacha20poly1305SealDetached(ciphertextAndTag[0..plaintext.len], ciphertextAndTag[plaintext.len..][0..chacha20poly1305_tag_size], plaintext, data, key, nonce);
 }
 
 /// Verifies and decrypts an authenticated message produced by chacha20poly1305SealDetached.
-pub fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
+fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
     // split ciphertext and tag
     assert(dst.len >= ciphertext.len);
 
     // derive poly1305 key
     var polyKey = [_]u8{0} ** 32;
-    chaCha20IETF(polyKey[0..], polyKey[0..], 0, key, nonce);
+    ChaCha20IETF.xor(polyKey[0..], polyKey[0..], 0, key, nonce);
 
     // construct mac
     var mac = Poly1305.init(polyKey[0..]);
@@ -519,11 +523,11 @@ pub fn chacha20poly1305OpenDetached(dst: []u8, ciphertext: []const u8, tag: *con
     }
 
     // decrypt ciphertext
-    chaCha20IETF(dst[0..ciphertext.len], ciphertext, 1, key, nonce);
+    ChaCha20IETF.xor(dst[0..ciphertext.len], ciphertext, 1, key, nonce);
 }
 
 /// Verifies and decrypts an authenticated message produced by chacha20poly1305Seal.
-pub fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
+fn chacha20poly1305Open(dst: []u8, ciphertextAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [12]u8) !void {
     if (ciphertextAndTag.len < chacha20poly1305_tag_size) {
         return error.InvalidMessage;
     }
@@ -562,31 +566,33 @@ fn extend(key: [32]u8, nonce: [24]u8) struct { key: [32]u8, nonce: [12]u8 } {
     };
 }
 
-pub fn xChaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void {
-    const extended = extend(key, nonce);
-    chaCha20IETF(out, in, counter, extended.key, extended.nonce);
-}
+pub const XChaCha20IETF = struct {
+    pub fn xor(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce: [24]u8) void {
+        const extended = extend(key, nonce);
+        ChaCha20IETF.xor(out, in, counter, extended.key, extended.nonce);
+    }
+};
 
 pub const xchacha20poly1305_tag_size = 16;
 
-pub fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
+fn xchacha20poly1305SealDetached(ciphertext: []u8, tag: *[chacha20poly1305_tag_size]u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
     const extended = extend(key, nonce);
     return chacha20poly1305SealDetached(ciphertext, tag, plaintext, data, extended.key, extended.nonce);
 }
 
-pub fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
+fn xchacha20poly1305Seal(ciphertextAndTag: []u8, plaintext: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) void {
     const extended = extend(key, nonce);
     return chacha20poly1305Seal(ciphertextAndTag, plaintext, data, extended.key, extended.nonce);
 }
 
 /// Verifies and decrypts an authenticated message produced by xchacha20poly1305SealDetached.
-pub fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
+fn xchacha20poly1305OpenDetached(plaintext: []u8, ciphertext: []const u8, tag: *const [chacha20poly1305_tag_size]u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
     const extended = extend(key, nonce);
     return try chacha20poly1305OpenDetached(plaintext, ciphertext, tag, data, extended.key, extended.nonce);
 }
 
 /// Verifies and decrypts an authenticated message produced by xchacha20poly1305Seal.
-pub fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
+fn xchacha20poly1305Open(ciphertextAndTag: []u8, msgAndTag: []const u8, data: []const u8, key: [32]u8, nonce: [24]u8) !void {
     const extended = extend(key, nonce);
     return try chacha20poly1305Open(ciphertextAndTag, msgAndTag, data, extended.key, extended.nonce);
 }
@@ -714,7 +720,7 @@ test "crypto.xchacha20" {
     const input = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
     {
         var ciphertext: [input.len]u8 = undefined;
-        xChaCha20IETF(ciphertext[0..], input[0..], 0, key, nonce);
+        XChaCha20IETF.xor(ciphertext[0..], input[0..], 0, key, nonce);
         var buf: [2 * ciphertext.len]u8 = undefined;
         testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{ciphertext}), "E0A1BCF939654AFDBDC1746EC49832647C19D891F0D1A81FC0C1703B4514BDEA584B512F6908C2C5E9DD18D5CBC1805DE5803FE3B9CA5F193FB8359E91FAB0C3BB40309A292EB1CF49685C65C4A3ADF4F11DB0CD2B6B67FBC174BC2E860E8F769FD3565BBFAD1C845E05A0FED9BE167C240D");
     }
lib/std/crypto/gimli.zig
@@ -109,17 +109,21 @@ pub const Hash = struct {
     state: State,
     buf_off: usize,
 
+    pub const block_length = State.RATE;
+
     const Self = @This();
 
     pub fn init() Self {
         return Self{
-            .state = State{
-                .data = [_]u32{0} ** (State.BLOCKBYTES / 4),
-            },
+            .state = State{ .data = [_]u32{0} ** (State.BLOCKBYTES / 4) },
             .buf_off = 0,
         };
     }
 
+    pub fn reset(self: *Self) void {
+        self.* = init();
+    }
+
     /// Also known as 'absorb'
     pub fn update(self: *Self, data: []const u8) void {
         const buf = self.state.toSlice();
lib/std/crypto/hmac.zig
@@ -8,10 +8,19 @@ const crypto = std.crypto;
 const debug = std.debug;
 const mem = std.mem;
 
-pub const HmacMd5 = Hmac(crypto.Md5);
-pub const HmacSha1 = Hmac(crypto.Sha1);
-pub const HmacSha256 = Hmac(crypto.Sha256);
-pub const HmacBlake2s256 = Hmac(crypto.Blake2s256);
+pub const HmacMd5 = Hmac(crypto.hash.legacy.Md5);
+pub const HmacSha1 = Hmac(crypto.hash.legacy.Sha1);
+
+pub const sha2 = struct {
+    pub const HmacSha224 = Hmac(crypto.hash.sha2.Sha224);
+    pub const HmacSha256 = Hmac(crypto.hash.sha2.Sha256);
+    pub const HmacSha384 = Hmac(crypto.hash.sha2.Sha384);
+    pub const HmacSha512 = Hmac(crypto.hash.sha2.Sha512);
+};
+
+pub const blake2 = struct {
+    pub const HmacBlake2s256 = Hmac(crypto.hash.blake2.Blake2s256);
+};
 
 pub fn Hmac(comptime Hash: type) type {
     return struct {
@@ -95,10 +104,10 @@ test "hmac sha1" {
 }
 
 test "hmac sha256" {
-    var out: [HmacSha256.mac_length]u8 = undefined;
-    HmacSha256.create(out[0..], "", "");
+    var out: [sha2.HmacSha256.mac_length]u8 = undefined;
+    sha2.HmacSha256.create(out[0..], "", "");
     htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
 
-    HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
+    sha2.HmacSha256.create(out[0..], "The quick brown fox jumps over the lazy dog", "key");
     htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
 }
lib/std/crypto/md5.zig
@@ -32,6 +32,9 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar
     };
 }
 
+/// The MD5 function is now considered cryptographically broken.
+/// Namely, it is trivial to find multiple inputs producing the same hash.
+/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3.
 pub const Md5 = struct {
     const Self = @This();
     pub const block_length = 64;
@@ -44,18 +47,21 @@ pub const Md5 = struct {
     total_len: u64,
 
     pub fn init() Self {
-        var d: Self = undefined;
-        d.reset();
-        return d;
+        return Self{
+            .s = [_]u32{
+                0x67452301,
+                0xEFCDAB89,
+                0x98BADCFE,
+                0x10325476,
+            },
+            .buf = undefined,
+            .buf_len = 0,
+            .total_len = 0,
+        };
     }
 
-    pub fn reset(d: *Self) void {
-        d.s[0] = 0x67452301;
-        d.s[1] = 0xEFCDAB89;
-        d.s[2] = 0x98BADCFE;
-        d.s[3] = 0x10325476;
-        d.buf_len = 0;
-        d.total_len = 0;
+    pub fn reset(self: *Self) void {
+        self.* = init();
     }
 
     pub fn hash(b: []const u8, out: []u8) void {
lib/std/crypto/sha1.zig
@@ -29,6 +29,9 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam {
     };
 }
 
+/// The SHA-1 function is now considered cryptographically broken.
+/// Namely, it is feasible to find multiple inputs producing the same hash.
+/// For a fast-performing, cryptographically secure hash function, see SHA512/256, BLAKE2 or BLAKE3.
 pub const Sha1 = struct {
     const Self = @This();
     pub const block_length = 64;
@@ -41,19 +44,22 @@ pub const Sha1 = struct {
     total_len: u64,
 
     pub fn init() Self {
-        var d: Self = undefined;
-        d.reset();
-        return d;
+        return Self{
+            .s = [_]u32{
+                0x67452301,
+                0xEFCDAB89,
+                0x98BADCFE,
+                0x10325476,
+                0xC3D2E1F0,
+            },
+            .buf = undefined,
+            .buf_len = 0,
+            .total_len = 0,
+        };
     }
 
-    pub fn reset(d: *Self) void {
-        d.s[0] = 0x67452301;
-        d.s[1] = 0xEFCDAB89;
-        d.s[2] = 0x98BADCFE;
-        d.s[3] = 0x10325476;
-        d.s[4] = 0xC3D2E1F0;
-        d.buf_len = 0;
-        d.total_len = 0;
+    pub fn reset(self: *Self) void {
+        self.* = init();
     }
 
     pub fn hash(b: []const u8, out: []u8) void {
lib/std/crypto/sha2.zig
@@ -93,22 +93,25 @@ fn Sha2_32(comptime params: Sha2Params32) type {
         total_len: u64,
 
         pub fn init() Self {
-            var d: Self = undefined;
-            d.reset();
-            return d;
+            return Self{
+                .s = [_]u32{
+                    params.iv0,
+                    params.iv1,
+                    params.iv2,
+                    params.iv3,
+                    params.iv4,
+                    params.iv5,
+                    params.iv6,
+                    params.iv7,
+                },
+                .buf = undefined,
+                .buf_len = 0,
+                .total_len = 0,
+            };
         }
 
-        pub fn reset(d: *Self) void {
-            d.s[0] = params.iv0;
-            d.s[1] = params.iv1;
-            d.s[2] = params.iv2;
-            d.s[3] = params.iv3;
-            d.s[4] = params.iv4;
-            d.s[5] = params.iv5;
-            d.s[6] = params.iv6;
-            d.s[7] = params.iv7;
-            d.buf_len = 0;
-            d.total_len = 0;
+        pub fn reset(self: *Self) void {
+            self.* = init();
         }
 
         pub fn hash(b: []const u8, out: []u8) void {
lib/std/crypto/sha3.zig
@@ -27,14 +27,14 @@ fn Keccak(comptime bits: usize, comptime delim: u8) type {
 
         pub fn init() Self {
             var d: Self = undefined;
-            d.reset();
-            return d;
-        }
-
-        pub fn reset(d: *Self) void {
             mem.set(u8, d.s[0..], 0);
             d.offset = 0;
             d.rate = 200 - (bits / 4);
+            return d;
+        }
+
+        pub fn reset(self: *Self) void {
+            self.* = init();
         }
 
         pub fn hash(b: []const u8, out: []u8) void {
lib/std/bloom_filter.zig
@@ -158,7 +158,7 @@ pub fn BloomFilter(
 }
 
 fn hashFunc(out: []u8, Ki: usize, in: []const u8) void {
-    var st = std.crypto.gimli.Hash.init();
+    var st = std.crypto.hash.Gimli.init();
     st.update(std.mem.asBytes(&Ki));
     st.update(in);
     st.final(out);
lib/std/cache_hash.zig
@@ -4,7 +4,7 @@
 // The MIT license requires this copyright notice to be included in all copies
 // and substantial portions of the software.
 const std = @import("std.zig");
-const Blake3 = std.crypto.Blake3;
+const Blake3 = std.crypto.hash.Blake3;
 const fs = std.fs;
 const base64 = std.base64;
 const ArrayList = std.ArrayList;
lib/std/crypto.zig
@@ -3,60 +3,68 @@
 // This file is part of [zig](https://ziglang.org/), which is MIT licensed.
 // The MIT license requires this copyright notice to be included in all copies
 // and substantial portions of the software.
-pub const Md5 = @import("crypto/md5.zig").Md5;
-pub const Sha1 = @import("crypto/sha1.zig").Sha1;
-
-const sha2 = @import("crypto/sha2.zig");
-pub const Sha224 = sha2.Sha224;
-pub const Sha256 = sha2.Sha256;
-pub const Sha384 = sha2.Sha384;
-pub const Sha512 = sha2.Sha512;
-
-const sha3 = @import("crypto/sha3.zig");
-pub const Sha3_224 = sha3.Sha3_224;
-pub const Sha3_256 = sha3.Sha3_256;
-pub const Sha3_384 = sha3.Sha3_384;
-pub const Sha3_512 = sha3.Sha3_512;
-
-pub const gimli = @import("crypto/gimli.zig");
-
-const blake2 = @import("crypto/blake2.zig");
-pub const Blake2s224 = blake2.Blake2s224;
-pub const Blake2s256 = blake2.Blake2s256;
-pub const Blake2b384 = blake2.Blake2b384;
-pub const Blake2b512 = blake2.Blake2b512;
-
-pub const Blake3 = @import("crypto/blake3.zig").Blake3;
-
-const hmac = @import("crypto/hmac.zig");
-pub const HmacMd5 = hmac.HmacMd5;
-pub const HmacSha1 = hmac.HmacSha1;
-pub const HmacSha256 = hmac.HmacSha256;
-pub const HmacBlake2s256 = hmac.HmacBlake2s256;
-
-pub const chacha20 = @import("crypto/chacha20.zig");
-pub const chaCha20IETF = chacha20.chaCha20IETF;
-pub const chaCha20With64BitNonce = chacha20.chaCha20With64BitNonce;
-pub const xChaCha20IETF = chacha20.xChaCha20IETF;
-
-pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
-
-const import_aes = @import("crypto/aes.zig");
-pub const AES128 = import_aes.AES128;
-pub const AES256 = import_aes.AES256;
-
-pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
-pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519;
-pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519;
-pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
-pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255;
 
+/// Hash functions.
+pub const hash = struct {
+    pub const Md5 = @import("crypto/md5.zig").Md5;
+    pub const Sha1 = @import("crypto/sha1.zig").Sha1;
+    pub const sha2 = @import("crypto/sha2.zig");
+    pub const sha3 = @import("crypto/sha3.zig");
+    pub const blake2 = @import("crypto/blake2.zig");
+    pub const Blake3 = @import("crypto/blake3.zig").Blake3;
+    pub const Gimli = @import("crypto/gimli.zig").Hash;
+};
+
+/// Authentication (MAC) functions.
+pub const auth = struct {
+    pub const hmac = @import("crypto/hmac.zig");
+};
+
+/// Authenticated Encryption with Associated Data
 pub const aead = struct {
-    pub const Gimli = gimli.Aead;
+    const chacha20 = @import("crypto/chacha20.zig");
+
+    pub const Gimli = @import("crypto/gimli.zig").Aead;
     pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
     pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
 };
 
+/// MAC functions requiring single-use secret keys.
+pub const onetimeauth = struct {
+    pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305;
+};
+
+/// Core functions, that should rarely be used directly by applications.
+pub const core = struct {
+    pub const aes = @import("crypto/aes.zig");
+    pub const Gimli = @import("crypto/gimli.zig").State;
+};
+
+/// Elliptic-curve arithmetic.
+pub const ecc = struct {
+    pub const Curve25519 = @import("crypto/25519/curve25519.zig").Curve25519;
+    pub const Edwards25519 = @import("crypto/25519/edwards25519.zig").Edwards25519;
+    pub const Ristretto255 = @import("crypto/25519/ristretto255.zig").Ristretto255;
+};
+
+/// Diffie-Hellman key exchange functions.
+pub const dh = struct {
+    pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
+};
+
+/// Digital signature functions.
+pub const sign = struct {
+    pub const Ed25519 = @import("crypto/25519/ed25519.zig").Ed25519;
+};
+
+/// Stream ciphers. These do not provide any kind of authentication.
+/// Most applications should be using AEAD constructions instead of stream ciphers directly.
+pub const stream = struct {
+    pub const ChaCha20IETF = @import("crypto/chacha20.zig").ChaCha20IETF;
+    pub const XChaCha20IETF = @import("crypto/chacha20.zig").XChaCha20IETF;
+    pub const ChaCha20With64BitNonce = @import("crypto/chacha20.zig").ChaCha20With64BitNonce;
+};
+
 const std = @import("std.zig");
 pub const randomBytes = std.os.getrandom;
 
@@ -83,16 +91,21 @@ test "crypto" {
 
 test "issue #4532: no index out of bounds" {
     const types = [_]type{
-        Md5,
-        Sha1,
-        Sha224,
-        Sha256,
-        Sha384,
-        Sha512,
-        Blake2s224,
-        Blake2s256,
-        Blake2b384,
-        Blake2b512,
+        hash.Md5,
+        hash.Sha1,
+        hash.sha2.Sha224,
+        hash.sha2.Sha256,
+        hash.sha2.Sha384,
+        hash.sha2.Sha512,
+        hash.sha3.Sha3_224,
+        hash.sha3.Sha3_256,
+        hash.sha3.Sha3_384,
+        hash.sha3.Sha3_512,
+        hash.blake2.Blake2s224,
+        hash.blake2.Blake2s256,
+        hash.blake2.Blake2b384,
+        hash.blake2.Blake2b512,
+        hash.Gimli,
     };
 
     inline for (types) |Hasher| {
lib/std/rand.zig
@@ -737,12 +737,12 @@ test "xoroshiro sequence" {
 // CSPRNG
 pub const Gimli = struct {
     random: Random,
-    state: std.crypto.gimli.State,
+    state: std.crypto.core.Gimli,
 
     pub fn init(init_s: u64) Gimli {
         var self = Gimli{
             .random = Random{ .fillFn = fill },
-            .state = std.crypto.gimli.State{
+            .state = std.crypto.core.Gimli{
                 .data = [_]u32{0} ** (std.crypto.gimli.State.BLOCKBYTES / 4),
             },
         };
lib/std/zig.zig
@@ -26,7 +26,7 @@ pub fn hashSrc(src: []const u8) SrcHash {
         std.mem.copy(u8, &out, src);
         std.mem.set(u8, out[src.len..], 0);
     } else {
-        std.crypto.Blake3.hash(src, &out);
+        std.crypto.hash.Blake3.hash(src, &out);
     }
     return out;
 }
tools/process_headers.zig
@@ -313,7 +313,7 @@ pub fn main() !void {
     var max_bytes_saved: usize = 0;
     var total_bytes: usize = 0;
 
-    var hasher = std.crypto.Sha256.init();
+    var hasher = std.crypto.hash.sha2.Sha256.init();
 
     for (libc_targets) |libc_target| {
         const dest_target = DestTarget{