aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-10-04 02:45:54 -0400
committerGitHub <noreply@github.com>2020-10-04 02:45:54 -0400
commit5fda4fe4c80011aa1a440aecdeaa281f846c7a0d (patch)
tree6e201c0e72cc67d4143280c3c7833a8157a5f5a1 /lib/std
parent538d485782629a358c2dd0f8e34d74813e3c9526 (diff)
parentd75d6e7f774c6236eb11a5a6b0277561a3b42a22 (diff)
downloadzig-5fda4fe4c80011aa1a440aecdeaa281f846c7a0d.tar.gz
zig-5fda4fe4c80011aa1a440aecdeaa281f846c7a0d.zip
Merge pull request #6454 from jedisct1/hkdf
std/crypto: implement the HKDF construction
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/crypto.zig85
-rw-r--r--lib/std/crypto/hkdf.zig66
2 files changed, 111 insertions, 40 deletions
diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig
index fa69d51d4d..36eca6700a 100644
--- a/lib/std/crypto.zig
+++ b/lib/std/crypto.zig
@@ -4,6 +4,48 @@
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
+/// Authenticated Encryption with Associated Data
+pub const aead = struct {
+ const chacha20 = @import("crypto/chacha20.zig");
+
+ pub const Gimli = @import("crypto/gimli.zig").Aead;
+ pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
+ pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
+ pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
+ pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
+};
+
+/// Authentication (MAC) functions.
+pub const auth = struct {
+ pub const hmac = @import("crypto/hmac.zig");
+ pub const siphash = @import("crypto/siphash.zig");
+};
+
+/// 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;
+
+ /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations.
+ ///
+ /// These modes are designed to be building blocks for higher-level constructions, and should generally not be used directly by applications, as they may not provide the expected properties and security guarantees.
+ ///
+ /// Most applications may want to use AEADs instead.
+ pub const modes = @import("crypto/modes.zig");
+};
+
+/// Diffie-Hellman key exchange functions.
+pub const dh = struct {
+ pub const X25519 = @import("crypto/25519/x25519.zig").X25519;
+};
+
+/// 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;
+};
+
/// Hash functions.
pub const hash = struct {
pub const Md5 = @import("crypto/md5.zig").Md5;
@@ -15,21 +57,9 @@ pub const hash = struct {
pub const Gimli = @import("crypto/gimli.zig").Hash;
};
-/// Authentication (MAC) functions.
-pub const auth = struct {
- pub const hmac = @import("crypto/hmac.zig");
- pub const siphash = @import("crypto/siphash.zig");
-};
-
-/// Authenticated Encryption with Associated Data
-pub const aead = struct {
- const chacha20 = @import("crypto/chacha20.zig");
-
- pub const Gimli = @import("crypto/gimli.zig").Aead;
- pub const ChaCha20Poly1305 = chacha20.Chacha20Poly1305;
- pub const XChaCha20Poly1305 = chacha20.XChacha20Poly1305;
- pub const AEGIS128L = @import("crypto/aegis.zig").AEGIS128L;
- pub const AEGIS256 = @import("crypto/aegis.zig").AEGIS256;
+/// Key derivation functions.
+pub const kdf = struct {
+ pub const hkdf = @import("crypto/hkdf.zig");
};
/// MAC functions requiring single-use secret keys.
@@ -57,31 +87,6 @@ pub const pwhash = struct {
pub const pbkdf2 = @import("crypto/pbkdf2.zig").pbkdf2;
};
-/// 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;
-
- /// Modes are generic compositions to construct encryption/decryption functions from block ciphers and permutations.
- ///
- /// These modes are designed to be building blocks for higher-level constructions, and should generally not be used directly by applications, as they may not provide the expected properties and security guarantees.
- ///
- /// Most applications may want to use AEADs instead.
- pub const modes = @import("crypto/modes.zig");
-};
-
-/// 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;
diff --git a/lib/std/crypto/hkdf.zig b/lib/std/crypto/hkdf.zig
new file mode 100644
index 0000000000..7ac3603637
--- /dev/null
+++ b/lib/std/crypto/hkdf.zig
@@ -0,0 +1,66 @@
+const std = @import("../std.zig");
+const assert = std.debug.assert;
+const hmac = std.crypto.auth.hmac;
+const mem = std.mem;
+
+/// HKDF-SHA256
+pub const HkdfSha256 = Hkdf(hmac.sha2.HmacSha256);
+
+/// HKDF-SHA512
+pub const HkdfSha512 = Hkdf(hmac.sha2.HmacSha512);
+
+/// The Hkdf construction takes some source of initial keying material and
+/// derives one or more uniform keys from it.
+pub fn Hkdf(comptime Hmac: type) type {
+ return struct {
+ /// Return a master key from a salt and initial keying material.
+ fn extract(salt: []const u8, ikm: []const u8) [Hmac.mac_length]u8 {
+ var prk: [Hmac.mac_length]u8 = undefined;
+ Hmac.create(&prk, ikm, salt);
+ return prk;
+ }
+
+ /// Derive a subkey from a master key `prk` and a subkey description `ctx`.
+ fn expand(out: []u8, ctx: []const u8, prk: [Hmac.mac_length]u8) void {
+ assert(out.len < Hmac.mac_length * 255); // output size is too large for the Hkdf construction
+ var i: usize = 0;
+ var counter = [1]u8{1};
+ while (i + Hmac.mac_length <= out.len) : (i += Hmac.mac_length) {
+ var st = Hmac.init(&prk);
+ if (i != 0) {
+ st.update(out[i - Hmac.mac_length ..][0..Hmac.mac_length]);
+ }
+ st.update(ctx);
+ st.update(&counter);
+ st.final(out[i..][0..Hmac.mac_length]);
+ counter[0] += 1;
+ }
+ const left = out.len % Hmac.mac_length;
+ if (left > 0) {
+ var st = Hmac.init(&prk);
+ if (i != 0) {
+ st.update(out[i - Hmac.mac_length ..][0..Hmac.mac_length]);
+ }
+ st.update(ctx);
+ st.update(&counter);
+ var tmp: [Hmac.mac_length]u8 = undefined;
+ st.final(tmp[0..Hmac.mac_length]);
+ mem.copy(u8, out[i..][0..left], tmp[0..left]);
+ }
+ }
+ };
+}
+
+const htest = @import("test.zig");
+
+test "Hkdf" {
+ const ikm = [_]u8{0x0b} ** 22;
+ const salt = [_]u8{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c };
+ const context = [_]u8{ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
+ const kdf = HkdfSha256;
+ const prk = kdf.extract(&salt, &ikm);
+ htest.assertEqual("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5", &prk);
+ var out: [42]u8 = undefined;
+ kdf.expand(&out, &context, prk);
+ htest.assertEqual("3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865", &out);
+}