diff --git a/src/crypto.zig b/src/crypto.zig index 921b0d0..5f6c7ad 100644 --- a/src/crypto.zig +++ b/src/crypto.zig @@ -1,14 +1,14 @@ ///////////////// Imports // -const AES = @import("crypto/aes.zig"); -const ED25519 = @import("crypto/ed25519.zig"); -const HKDF = @import("crypto/hkdf.zig"); -const HMAC = @import("crypto/hmac.zig"); -const SHA256 = @import("crypto/sha256.zig"); -const SHA512 = @import("crypto/sha512.zig"); -const X25519= @import("crypto/x25519.zig"); -const RANDOM = @import("crypto/random.zig"); +pub const Aes = @import("crypto/aes.zig"); +pub const Ed25519 = @import("crypto/ed25519.zig"); +pub const Hkdf = @import("crypto/hkdf.zig"); +pub const Hmac = @import("crypto/hmac.zig"); +pub const Sha256 = @import("crypto/sha256.zig"); +pub const Sha512 = @import("crypto/sha512.zig"); +pub const X25519= @import("crypto/x25519.zig"); +pub const Random = @import("crypto/random.zig"); ///////////////// Errors @@ -25,25 +25,25 @@ pub const CryptoError = error { // pub const PartialEngine = struct { - aes_cbc256: ? AES.CBC256.Implementation = null, - ed25519: ? ED25519.Implementation = null, - hkdf: ? HKDF.Implementation = null, - hmac: ? HMAC.Implementation = null, - sha256: ? SHA256.Implementation = null, - sha512: ? SHA512.Implementation = null, + aes_cbc256: ? Aes.CBC256.Implementation = null, + ed25519: ? Ed25519.Implementation = null, + hkdf: ? Hkdf.Implementation = null, + hmac: ? Hmac.Implementation = null, + sha256: ? Sha256.Implementation = null, + sha512: ? Sha512.Implementation = null, x25519: ? X25519.Implementation = null, - random: ? RANDOM.Implementation = null, + random: ? Random.Implementation = null, }; pub const Engine = struct { - aes_cbc256: AES.CBC256.Implementation, - ed25519: ED25519.Implementation, - hkdf: HKDF.Implementation, - hmac: HMAC.Implementation, - sha256: SHA256.Implementation, - sha512: SHA512.Implementation, + aes_cbc256: Aes.CBC256.Implementation, + ed25519: Ed25519.Implementation, + hkdf: Hkdf.Implementation, + hmac: Hmac.Implementation, + sha256: Sha256.Implementation, + sha512: Sha512.Implementation, x25519: X25519.Implementation, - random: RANDOM.Implementation, + random: Random.Implementation, }; @@ -56,14 +56,14 @@ pub const Engine = struct { pub fn resolveEngine(comptime provider: PartialEngine) Engine { return .{ - .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, + .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, + .random = provider.random orelse Random.defaultImplementation, }; } diff --git a/src/crypto/ed25519.zig b/src/crypto/ed25519.zig index 889f713..79940ec 100644 --- a/src/crypto/ed25519.zig +++ b/src/crypto/ed25519.zig @@ -2,40 +2,42 @@ const CryptoError = @import("../crypto.zig").CryptoError; // Constants and default implementation const Ed25519 = @import("std").crypto.sign.Ed25519; +const secureZero = @import("std").crypto.secureZero; // Random number generation const rngProvider = @import("random.zig").Implementation; -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; +// Keypair type +pub const KeyPair = Ed25519.KeyPair; + +pub const secret_key_length: comptime_int = Ed25519.SecretKey.encoded_length; +pub const public_key_length: comptime_int = Ed25519.PublicKey.encoded_length; +pub const signature_length : comptime_int = Ed25519.Signature.encoded_length; +pub const seed_length : comptime_int = Ed25519.Signature.encoded_length; pub const Implementation = struct { - 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, + generateKeys: *const fn (destination: *KeyPair, rng: rngProvider) CryptoError!void, + sign: *const fn (key: *const [secret_key_length]u8, data: []const u8, signature_out: *const [signature_length]u8) CryptoError!void, + verify: *const fn (key: *const [public_key_length]u8, signature: *const [signature_length]u8, data: []const u8) CryptoError!void, }; -pub fn generateKeys(pubkey_buffer: *[public_key_size]u8, privkey_buffer: *[secret_key_size]u8, rng: rngProvider) !void { +pub fn generateKeys (destination: *KeyPair, rng: rngProvider) !void { - var seed_buffer: [Ed25519.KeyPair.seed_length]u8 = undefined; + var seed_buffer: [KeyPair.seed_length]u8 = undefined; + defer secureZero(u8, &seed_buffer); 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); + destination.* = KeyPair.generateDeterministic(seed_buffer) catch return CryptoError.FailedKeyGeneration; } -pub fn sign(key: *const [secret_key_size]u8, data: []const u8, signature_out: *const [signature_size]u8) !void { +pub fn sign (key: *const [secret_key_length]u8, data: []const u8, signature_out: *const [signature_length]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 { +pub fn verify (key: *const [public_key_length]u8, signature: *const [signature_length]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; diff --git a/src/crypto/x25519.zig b/src/crypto/x25519.zig index 0d756e8..a21bd2d 100644 --- a/src/crypto/x25519.zig +++ b/src/crypto/x25519.zig @@ -1,13 +1,38 @@ -//TODO +// Errors +const CryptoError = @import("../crypto.zig").CryptoError; +// Constants and default implementation +const X25519 = @import("std").crypto.dh.X25519; +const Ed25519 = @import("std").crypto.sign.Ed25519; +const secureZero = @import("std").crypto.secureZero; +// Random number generation +const rngProvider = @import("random.zig").Implementation; -const X25519 = @import("std").std.crypto.dh.X25519; +// Keypair type +pub const KeyPair = X25519.KeyPair; + +pub const secret_key_length: comptime_int = X25519.secret_length; +pub const public_key_length: comptime_int = X25519.public_length; +pub const seed_length: comptime_int = X25519.seed_length; pub const Implementation = struct { + generateKeys: *const fn (destination: *KeyPair, rng: rngProvider) CryptoError!void, + fromEd25519: *const fn (ed25519_keypair: *Ed25519.KeyPair, x25519_keypair: *KeyPair) CryptoError!void, }; +fn generateKeys (destination: *KeyPair, rng: rngProvider) !void { + + var seed_buffer: [seed_length]u8 = undefined; + defer secureZero(u8, &seed_buffer); + rng.generate(&seed_buffer); + destination.* = X25519.KeyPair.generateDeterministic(seed_buffer) catch return CryptoError.FailedKeyGeneration; +} + +fn fromEd25519 (ed25519_keypair: *Ed25519.KeyPair, x25519_keypair: *KeyPair) CryptoError!void { + x25519_keypair.* = X25519.KeyPair.fromEd25519(ed25519_keypair.*) catch return CryptoError.InvalidKey; +} + 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 + .generateKeys = generateKeys, + .fromEd25519 = fromEd25519, }; diff --git a/src/identity.zig b/src/identity.zig index dee9100..dc398d6 100644 --- a/src/identity.zig +++ b/src/identity.zig @@ -1,7 +1,7 @@ ///////////////// Imports // -const CryptoEngine = @import("crypto.zig").Engine; +const Crypto = @import("crypto.zig"); ///////////////// Constants // @@ -13,25 +13,38 @@ const CryptoEngine = @import("crypto.zig").Engine; // // TODO -pub fn Identity(comptime crypto_engine: CryptoEngine) type { - _ = crypto_engine; +pub fn resolveIdentity(comptime crypto_engine: Crypto.Engine) type { return struct { + const Self = @This(); + //////// Fields // - publicKey: []const u8, - privateKey: []const u8, - signature: []const u8, - hash: []const u8, + keys: Crypto.Ed25519.KeyPair, + signatureKeys: Crypto.X25519.KeyPair, + hash: [Crypto.Sha256.hash_len]u8, //////// Functions // - pub fn generate() !Identity { - return .{ - // .privateKey = crypto_engine.ed25519.generateKey(...), - // .signature = crypto_engine.x25519. - }; + pub fn new() !Self { + const result: Self = undefined; + crypto_engine.ed25519.generateKeys(result.keys, crypto_engine.random); + crypto_engine.x25519.fromEd25519(result.keys, result.signatureKeys, crypto_engine.random); + result.update_hash(); + return result; + } + + pub fn generate(self: *Self) !void { + try crypto_engine.ed25519.generateKeys(&self.keys, crypto_engine.random); + try crypto_engine.x25519.fromEd25519(&self.keys, &self.signatureKeys); + self.update_hash(); + } + + pub fn update_hash(self: *Self) void { + //WARNING PUBLIC KEY SHOULD BE TRUNCATED + // We should define the truncated hash length somewhere + crypto_engine.sha256.hash(&self.keys.public_key.bytes, &self.hash); } }; } @@ -46,3 +59,49 @@ pub fn Identity(comptime crypto_engine: CryptoEngine) type { const std = @import("std"); const expect = std.testing.expect; +const memeql = std.mem.eql; + +const testPattern: u8 = 0b10101010; +const pubkey_size = Crypto.Ed25519.public_key_length; +const privkey_size = Crypto.Ed25519.secret_key_length; + +// Fills out with alternating 0s and 1s +fn fillPattern(out: []u8) void { + for(out) |*byte| { + byte.* = testPattern; + } +} + +pub fn generateFakeKeys(destination: *Crypto.Ed25519.KeyPair, rng: Crypto.Random.Implementation) Crypto.CryptoError!void { + _ = rng; + fillPattern(&destination.public_key.bytes); + fillPattern(&destination.secret_key.bytes); +} + +test "Basic identity generation" { + + const fake_rng: Crypto.Random.Implementation = .{ + .generate = fillPattern, + }; + const fake_ed25519: Crypto.Ed25519.Implementation = .{ + .generateKeys = generateFakeKeys, + .sign = undefined, + .verify = undefined, + }; + const crypto_engine = comptime Crypto.resolveEngine(.{ + .random = fake_rng, + .ed25519 = fake_ed25519, + }); + + const Identity = resolveIdentity(crypto_engine); + var new_id: Identity = undefined; + try new_id.generate(); + + const expected_secret_key = [_]u8{testPattern} ** new_id.keys.secret_key.bytes.len; + // const expected_signature_key = [_]u8{testPattern} ** new_id.signatureKeys.secret_key.len; + try expect(memeql(u8, &new_id.keys.secret_key.bytes, &expected_secret_key)); + // try expect(memeql(u8, &new_id.signatureKeys.secret_key, &expected_signature_key)); + + // try expect(memeql(u8, new_id.hash, testPattern)); + +}