diff --git a/src/crypto.zig b/src/crypto.zig index e9813cd..921b0d0 100644 --- a/src/crypto.zig +++ b/src/crypto.zig @@ -11,11 +11,21 @@ const X25519= @import("crypto/x25519.zig"); const RANDOM = @import("crypto/random.zig"); +///////////////// Errors +// + +pub const CryptoError = error { + InvalidSignature, + FailedKeyGeneration, + InvalidKey, + NotImplemented +}; + ///////////////// Structs // pub const PartialEngine = struct { - aes_cbc256: ? AES.CBC256_Implementation = null, + aes_cbc256: ? AES.CBC256.Implementation = null, ed25519: ? ED25519.Implementation = null, hkdf: ? HKDF.Implementation = null, hmac: ? HMAC.Implementation = null, @@ -26,7 +36,7 @@ pub const PartialEngine = struct { }; pub const Engine = struct { - aes_cbc256: AES.CBC256_Implementation, + aes_cbc256: AES.CBC256.Implementation, ed25519: ED25519.Implementation, hkdf: HKDF.Implementation, hmac: HMAC.Implementation, @@ -45,18 +55,15 @@ pub const Engine = struct { // function and its sub-structs. pub fn resolveEngine(comptime provider: PartialEngine) Engine { - // Used by other engine components - const randomImpl = provider.random orelse RANDOM.defaultImplementation; - return .{ - .aes_cbc256 = provider.aes_cbc256 orelse AES.CBC256_DefaultImplementation, - .ed25519 = provider.ed25519 orelse ED25519.DefaultImplementation(randomImpl), - .hkdf = provider.hkdf orelse HKDF.DefaultImplementation, - .hmac = provider.hmac orelse HMAC.DefaultImplementation, - .sha256 = provider.sha256 orelse SHA256.DefaultImplementation, - .sha512 = provider.sha512 orelse SHA512.DefaultImplementation, - .x25519 = provider.x25519 orelse X25519.DefaultImplementation, - .random = randomImpl, + .aes_cbc256 = provider.aes_cbc256 orelse AES.CBC256.defaultImplementation, + .ed25519 = provider.ed25519 orelse ED25519.defaultImplementation, + .hkdf = provider.hkdf orelse HKDF.defaultImplementation, + .hmac = provider.hmac orelse HMAC.defaultImplementation, + .sha256 = provider.sha256 orelse SHA256.defaultImplementation, + .sha512 = provider.sha512 orelse SHA512.defaultImplementation, + .x25519 = provider.x25519 orelse X25519.defaultImplementation, + .random = provider.random orelse RANDOM.defaultImplementation, }; } @@ -69,9 +76,9 @@ const std = @import("std"); test "AES encryption defaults" { const crypto = resolveEngine(.{}); - - var data = [_]u8{ 1, 2, 3, 4 }; - const key = [_]u8{ 1, 2, 3, 4 }; - const iv = [_]u8{ 1, 2, 3, 4 }; - crypto.aes_cbc256.encrypt(&data, &key, &iv); + + var data: []u8 = undefined; + const key: [32]u8 = undefined; + const iv: [16]u8 = undefined; + crypto.aes_cbc256.encrypt(&key, &iv, &data); } diff --git a/src/crypto/aes.zig b/src/crypto/aes.zig index aceb2c9..1498678 100644 --- a/src/crypto/aes.zig +++ b/src/crypto/aes.zig @@ -1,27 +1,34 @@ -pub const CBC256_Implementation = struct { +pub const CBC256 = struct { + + pub const block_size = 16; + pub const key_size = 32; - pub const BLOCK_SIZE = 16; - pub const KEY_SIZE = 32; + pub const Implementation = struct { - // NOTE: data should be padded to make its length be a multiple of BLOCK_SIZE - encypt: *const fn (key: *const [KEY_SIZE]u8, iv: *const [BLOCK_SIZE]u8, data: *const []u8) void, - decrypt: *const fn (key: *const [KEY_SIZE]u8, iv: *const [BLOCK_SIZE]u8, data: *const []u8) void, -}; + // NOTE: data should be padded to make its length be a multiple of BLOCK_SIZE + encrypt: *const fn (key: *const [key_size]u8, iv: *const [block_size]u8, data: *const []u8) void, + decrypt: *const fn (key: *const [key_size]u8, iv: *const [block_size]u8, data: *const []u8) void, + }; -// TODO -pub const CBC256_DefaultImplementation = struct { + // TODO // Zig stdlib seems to provide no support for AES CBC265 // It may need some custom implementation or another library - pub fn encrypt(key: *const [32]u8, iv: *const [16]u8, data: *const []u8) !void { + fn encrypt(key: *const [key_size]u8, iv: *const [block_size]u8, data: *const []u8) void { _ = key; _ = iv; _ = data; } - pub fn decrypt(key: *const [32]u8, iv: *const [16]u8, data: *const []u8) !void { + fn decrypt(key: *const [key_size]u8, iv: *const [block_size]u8, data: *const []u8) void { _ = key; _ = iv; _ = data; } + + pub const defaultImplementation: Implementation = .{ + .decrypt = decrypt, + .encrypt = encrypt, + }; }; + diff --git a/src/crypto/ed25519.zig b/src/crypto/ed25519.zig index ff6d552..889f713 100644 --- a/src/crypto/ed25519.zig +++ b/src/crypto/ed25519.zig @@ -1,50 +1,51 @@ -pub const Implementation = struct { - - secret_key_size: comptime_int, - public_key_size: comptime_int, - signature_size: comptime_int, - - generateKeys: *const fn (pubkey_buffer: *[Implementation.public_key_size]u8, privkey_buffer: *[Implementation.secret_key_size]u8) void, - sign: *const fn (key: *const [Implementation.private_key_size]u8, data: *const []const u8, signature_out: *const [Implementation.signature_size]u8) void, - verify: *const fn (key: *const [Implementation.public_key_size]u8, data: *const []const u8, signature: *const [Implementation.signature_size]u8) void, -}; - +// Errors +const CryptoError = @import("../crypto.zig").CryptoError; +// Constants and default implementation +const Ed25519 = @import("std").crypto.sign.Ed25519; // Random number generation const rngProvider = @import("random.zig").Implementation; -pub fn defaultImplementation(comptime random: rngProvider) type { +pub const secret_key_size: comptime_int = Ed25519.SecretKey.encoded_length; +pub const public_key_size: comptime_int = Ed25519.PublicKey.encoded_length; +pub const signature_size : comptime_int = Ed25519.Signature.encoded_length; - return struct { - - const Ed25519 = @import("std").crypto.sign.Ed25519; +pub const Implementation = struct { - pub const secret_key_size = Ed25519.SecretKey.encoded_length; - pub const public_key_size = Ed25519.PublicKey.encoded_length; - pub const signature_size = Ed25519.Signature.encoded_length; + generateKeys: *const fn (pubkey_buffer: *[public_key_size]u8, privkey_buffer: *[secret_key_size]u8, rng: rngProvider) CryptoError!void, + sign: *const fn (key: *const [secret_key_size]u8, data: []const u8, signature_out: *const [signature_size]u8) CryptoError!void, + verify: *const fn (key: *const [public_key_size]u8, signature: *const [signature_size]u8, data: []const u8) CryptoError!void, +}; - pub fn generateKeys(pubkey_buffer: *[Ed25519.SecretKey.encoded_length]u8, privkey_buffer: *[Ed25519.SecretKey.encoded_length]u8) !void { - - const seed_buffer: [Ed25519.KeyPair.seed_length]u8 = undefined; - random.generate(&seed_buffer); - const keypair: Ed25519.KeyPair = try Ed25519.KeyPair.generateDeterministic(seed_buffer); - - - @memcpy(pubkey_buffer[0..public_key_size], keypair.public_key.bytes); - @memcpy(privkey_buffer[0..secret_key_size], keypair.secret_key.bytes); - } - - pub fn sign(key: *const [secret_key_size]u8, data: *const []const u8, signature_out: *const [signature_size]u8) !void { - _ = key; - _ = data; - _ = signature_out; - return error.NotImplemented; - } - - pub fn verify(signature: *const [signature_size]u8, data: *const []const u8) !void { - _ = signature; - _ = data; - return error.NotImplemented; - } - }; +pub fn generateKeys(pubkey_buffer: *[public_key_size]u8, privkey_buffer: *[secret_key_size]u8, rng: rngProvider) !void { + + var seed_buffer: [Ed25519.KeyPair.seed_length]u8 = undefined; + rng.generate(&seed_buffer); + const keypair: Ed25519.KeyPair = Ed25519.KeyPair.generateDeterministic(seed_buffer) catch return CryptoError.FailedKeyGeneration; + + + @memcpy(pubkey_buffer[0..public_key_size], &keypair.public_key.bytes); + @memcpy(privkey_buffer[0..secret_key_size], &keypair.secret_key.bytes); } + +pub fn sign(key: *const [secret_key_size]u8, data: []const u8, signature_out: *const [signature_size]u8) !void { + _ = key; + _ = data; + _ = signature_out; + return CryptoError.NotImplemented; +} + +pub fn verify(key: *const [public_key_size]u8, signature: *const [signature_size]u8, data: []const u8) !void { + // TODO maybe find a more efficient way to do that (excessive copies) + const sig = Ed25519.Signature.fromBytes(signature.*); + const pubkey = Ed25519.PublicKey.fromBytes(key.*) catch return CryptoError.InvalidKey; + + return sig.verify(data, pubkey) catch CryptoError.InvalidSignature; +} + + +pub const defaultImplementation: Implementation = .{ + .generateKeys = generateKeys, + .sign = sign, + .verify = verify, +}; diff --git a/src/crypto/hkdf.zig b/src/crypto/hkdf.zig index 05a93a8..d571a08 100644 --- a/src/crypto/hkdf.zig +++ b/src/crypto/hkdf.zig @@ -1,29 +1,30 @@ +const Sha256 = @import("std").crypto.hash.sha2.Sha256; +const Hmac = @import("std").crypto.auth.hmac.Hmac(Sha256); +const Hkdf = @import("std").crypto.kdf.hkdf.Hkdf(Hmac); + +pub const prk_length: comptime_int = Hkdf.prk_length; + + pub const Implementation = struct { - prk_length: comptime_int, - - expand: *const fn (out: []u8, ctx: []const u8, prk: *[Implementation.prk_length]u8) void, - extract: *const fn (out: *[Implementation.prk_length]u8, salt: []const u8, ikm: []const u8) void, + expand: *const fn (out: []u8, ctx: []const u8, prk: *[prk_length]u8) void, + extract: *const fn (out: *[prk_length]u8, salt: []const u8, ikm: []const u8) void, }; -pub const defaultImplementation = struct { - - const Sha256 = @import("std").crypto.hash.sha2.Sha256; - const HashImpl = Sha256; - const HmacImpl = @import("std").crypto.auth.hmac.Hmac(HashImpl); +fn expand(out: []u8, ctx: []const u8, prk: *[prk_length]u8) void { + // TODO enhance efficiency (excessive copies) + Hkdf.expand(out, ctx, prk.*); +} - const Hkdf = @import("std").crypto.kdf.hkdf.Hkdf(HmacImpl); +fn extract(out: *[prk_length]u8, salt: []const u8, ikm: []const u8) void { + // TODO enhance efficiency (excessive copies) + out.* = Hkdf.extract(salt, ikm); +} - pub const prk_length = Hkdf.prk_length; - - pub fn expand(out: []u8, ctx: []const u8, prk: *[prk_length]u8) void { - Hkdf.expand(out, ctx, prk); - } - - pub fn extract(out: *[prk_length]u8, salt: []const u8, ikm: []const u8) void { - out *= Hkdf.extract(salt, ikm); - } +pub const defaultImplementation: Implementation = .{ + .expand = expand, + .extract = extract, }; diff --git a/src/crypto/hmac.zig b/src/crypto/hmac.zig index 131bb20..96d19b1 100644 --- a/src/crypto/hmac.zig +++ b/src/crypto/hmac.zig @@ -1,20 +1,18 @@ +const Sha256 = @import("std").crypto.hash.sha2.Sha256; +const Hmac = @import("std").crypto.auth.hmac.Hmac(Sha256); + +pub const mac_length: comptime_int = Hmac.mac_length; + + pub const Implementation = struct { - mac_length: comptime_int, - - create: *const fn (out: *[Implementation.mac_length]u8, msg: []const u8, key: []const u8) void, + create: *const fn (out: *[mac_length]u8, msg: []const u8, key: []const u8) void, }; -pub const defaultImplementation = struct { - - const Sha256 = @import("std").crypto.hash.sha2.Sha256; +fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void { + Hmac.create(out, msg, key); +} - const HashImpl = Sha256; - const Hmac = @import("std").crypto.auth.hmac.Hmac(Sha256); - - pub const mac_length = Hmac.mac_length; - - pub fn create(out: *[mac_length]u8, msg: []const u8, key: []const u8) void { - Hmac.create(out, msg, key); - } +pub const defaultImplementation: Implementation = .{ + .create = create, }; diff --git a/src/crypto/random.zig b/src/crypto/random.zig index b4d99d9..57530bb 100644 --- a/src/crypto/random.zig +++ b/src/crypto/random.zig @@ -2,14 +2,14 @@ pub const Implementation = struct { generate: *const fn (out: []u8) void, }; -// TODO -pub const defaultImplementation = struct { - - pub fn generate(out: []u8) void { - // WARNING This is not secure at all, use only for testing purposes - for(out) |*byte| { - byte *= undefined; - } +fn generate(out: []u8) void { + // WARNING This is not secure at all, use only for testing purposes + for(out) |*byte| { + byte.* = undefined; } +} + +pub const defaultImplementation: Implementation = .{ + .generate = generate, }; diff --git a/src/crypto/sha256.zig b/src/crypto/sha256.zig index 0d913a1..34937e5 100644 --- a/src/crypto/sha256.zig +++ b/src/crypto/sha256.zig @@ -1,17 +1,16 @@ +const Sha256 = @import("std").crypto.hash.sha2.Sha256; + +pub const hash_len: comptime_int = Sha256.digest_length; + pub const Implementation = struct { - hash_length: comptime_int, - - hash: *const fn (buffer: []const u8, out: *[Implementation.hash_length]u8) void, + hash: *const fn (buffer: []const u8, out: *[hash_len]u8) void, }; -pub const defaultImplementation = struct { - - const Sha256 = @import("std").crypto.hash.sha2.Sha256; +pub fn hash(buffer: []const u8, out: *[hash_len]u8) void { + Sha256.hash(buffer, out, .{}); +} - pub const hash_len = Sha256.digest_length; - - pub fn hash(buffer: []const u8, out: *[hash_len]u8) !void { - Sha256.hash(buffer, out, .{}); - } +pub const defaultImplementation: Implementation = .{ + .hash = hash, }; diff --git a/src/crypto/sha512.zig b/src/crypto/sha512.zig index a69a88d..f42221b 100644 --- a/src/crypto/sha512.zig +++ b/src/crypto/sha512.zig @@ -1,18 +1,16 @@ +const Sha512 = @import("std").crypto.hash.sha2.Sha512; + +pub const hash_len: comptime_int = Sha512.digest_length; + pub const Implementation = struct { - hash: *const fn (buffer: []const u8, out: []u8) void, + hash: *const fn (buffer: []const u8, out: *[hash_len]u8) void, }; -pub const defaultImplementation = struct { - - const Sha512 = @import("std").crypto.sha2.Sha512; +pub fn hash(buffer: []const u8, out: *[hash_len]u8) void { + Sha512.hash(buffer, out, .{}); +} - const HASH_LEN = 64; // In bytes - - pub fn hash(buffer: []const u8, out: []u8) !void { - if (out.len < HASH_LEN) { - return error.BufferTooShort; - } - Sha512.hash(buffer, out, .{}); - } +pub const defaultImplementation: Implementation = .{ + .hash = hash, }; diff --git a/src/crypto/x25519.zig b/src/crypto/x25519.zig index f3f6607..0d756e8 100644 --- a/src/crypto/x25519.zig +++ b/src/crypto/x25519.zig @@ -1,10 +1,12 @@ +//TODO + +const X25519 = @import("std").std.crypto.dh.X25519; + pub const Implementation = struct { }; -pub const defaultImplementation = struct { - - const X25519 = @import("std").std.crypto.dh.X25519; +pub const defaultImplementation: Implementation = .{ // One problem, it uses the zig's io module, which is not supported yet as it depends on the target OS // It may need a custom implementation or some other library