From 4fc1436431013af44b5926b84616ddfeac5febde Mon Sep 17 00:00:00 2001 From: David Allemang Date: Mon, 4 Mar 2024 20:45:42 -0500 Subject: [PATCH] working dh aes flow --- src/main.zig | 157 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 44 deletions(-) diff --git a/src/main.zig b/src/main.zig index f943360..97aa33b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -10,12 +10,12 @@ const Encryptor = struct { kp: DH.KeyPair, pub fn init() !Self { - const self = Self{}; + var self: Self = undefined; var seed: [DH.seed_length]u8 = undefined; std.crypto.random.bytes(&seed); self.kp = try DH.KeyPair.create(seed); - @memset(seed, 0); + @memset(&seed, 0); return self; } @@ -27,11 +27,12 @@ const Peer = struct { public_key: [DH.public_length]u8, shared_key: [AES.key_length]u8, - pub fn init(me: DH.KeyPair, peer_key: [DH.public_length]u8) Self { - const self = Self{ .public_key = peer_key }; + pub fn init(me: DH.KeyPair, peer_key: [DH.public_length]u8) !Self { + var self: Self = undefined; + self.public_key = peer_key; const secret = try DH.scalarmult(me.secret_key, peer_key); - SHA.hash(&secret, self.shared_key, .{}); + SHA.hash(&secret, &self.shared_key, .{}); return self; } @@ -46,8 +47,10 @@ const Peer = struct { } /// Caller must free result - pub fn decrypt(self: Self, ally: std.mem.Allocator, msg: []const u8) ![]const u8 { - + pub fn decrypt(self: Self, ally: std.mem.Allocator, pkt: Packet) ![]const u8 { + const msg: []u8 = try ally.alloc(u8, pkt.cipher_text.len); + try AES.decrypt(msg, pkt.cipher_text, pkt.tag, "", pkt.nonce, self.shared_key); + return msg; } }; @@ -55,57 +58,108 @@ const Packet = struct { ally: std.mem.Allocator, nonce: [AES.nonce_length]u8, tag: [AES.tag_length]u8, - cipher_text: []u8, }; pub fn main() !void { - const me = try Encryptor.init(); - const him = try Encryptor.init(); + const bob = try Encryptor.init(); + const alice = try Encryptor.init(); - const my_peer = Peer.init(me, him.kp.public_key); - const his_peer = Peer.init(him, me.kp.public_key); + std.debug.print( + "Bob sends {s}\n", + .{std.fmt.bytesToHex(bob.kp.public_key, .lower)}, + ); + std.debug.print( + "Alice sends {s}\n", + .{std.fmt.bytesToHex(alice.kp.public_key, .lower)}, + ); - var seed: [dh.X25519.seed_length]u8 = undefined; - std.crypto.random.bytes(&seed); - const kp1 = try dh.X25519.KeyPair.create(seed); - std.crypto.random.bytes(&seed); - const kp2 = try dh.X25519.KeyPair.create(seed); + const bob_peer = try Peer.init(bob.kp, alice.kp.public_key); + const alice_peer = try Peer.init(alice.kp, bob.kp.public_key); - std.debug.print("{any}\n{any}\n\n", .{ kp1.public_key, kp1.secret_key }); - std.debug.print("{any}\n{any}\n\n", .{ kp2.public_key, kp2.secret_key }); - - const sec_1 = try dh.X25519.scalarmult(kp1.secret_key, kp2.public_key); - const sec_2 = try dh.X25519.scalarmult(kp2.secret_key, kp1.public_key); - - var key: [aead.aes_gcm.Aes256Gcm.key_length]u8 = undefined; - std.crypto.hash.sha2.Sha256.hash(&sec_1, &key, .{}); - - std.debug.print("{any}\n", .{sec_1}); - std.debug.print("{any}\n", .{sec_2}); + std.debug.print( + "Bob deduces {s}\n", + .{std.fmt.bytesToHex(bob_peer.shared_key, .lower)}, + ); + std.debug.print( + "Alice deduces {s}\n", + .{std.fmt.bytesToHex(alice_peer.shared_key, .lower)}, + ); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const ally = gpa.allocator(); + var arena = std.heap.ArenaAllocator.init(gpa.allocator()); + defer arena.deinit(); - const m: []const u8 = "hello world!"; - const ad: []const u8 = "foo"; + const ally = arena.allocator(); - // see https://crypto.stackexchange.com/a/5818 - // the nonce does not need to be random, it can be a counter - // it can be sent in clear, or each end may deduce it - // but it ___MUST___ be unique and unrelated to the key - var iv: [aead.aes_gcm.Aes256Gcm.nonce_length]u8 = undefined; - std.crypto.random.bytes(&iv); + // bob send a message to alice + const msg = "Hello World!"; - const c: []u8 = try ally.alloc(u8, m.len); - defer ally.free(c); + std.debug.print( + "Bob sending message '{s}' ({s})\n", + .{ msg, std.fmt.bytesToHex(msg, .lower) }, + ); - @memset(c, 0); - var tag: [aead.aes_gcm.Aes256Gcm.tag_length]u8 = undefined; + const pkt = try bob_peer.encrypt(ally, msg); + defer ally.free(pkt.cipher_text); - std.debug.print("c: {any}\n", .{c}); - aead.aes_gcm.Aes256Gcm.encrypt(c, &tag, m, ad, iv, key); - std.debug.print("{any} {any} {any}\n", .{ c, tag, iv }); + std.debug.print( + "Packet: {s} {s} {s}\n", + .{ + try digest(ally, pkt.cipher_text, .lower), + try digest(ally, &pkt.nonce, .lower), + try digest(ally, &pkt.tag, .lower), + }, + ); + std.debug.assert(!std.mem.eql(u8, pkt.cipher_text, msg)); + + const rcv = try alice_peer.decrypt(ally, pkt); + defer ally.free(rcv); + std.debug.print( + "Alice received '{s}' ({s})\n", + .{ rcv, try digest(ally, rcv, .lower) }, + ); + + // var seed: [dh.X25519.seed_length]u8 = undefined; + // std.crypto.random.bytes(&seed); + // const kp1 = try dh.X25519.KeyPair.create(seed); + // std.crypto.random.bytes(&seed); + // const kp2 = try dh.X25519.KeyPair.create(seed); + // + // std.debug.print("{any}\n{any}\n\n", .{ kp1.public_key, kp1.secret_key }); + // std.debug.print("{any}\n{any}\n\n", .{ kp2.public_key, kp2.secret_key }); + // + // const sec_1 = try dh.X25519.scalarmult(kp1.secret_key, kp2.public_key); + // const sec_2 = try dh.X25519.scalarmult(kp2.secret_key, kp1.public_key); + // + // var key: [aead.aes_gcm.Aes256Gcm.key_length]u8 = undefined; + // std.crypto.hash.sha2.Sha256.hash(&sec_1, &key, .{}); + // + // std.debug.print("{any}\n", .{sec_1}); + // std.debug.print("{any}\n", .{sec_2}); + // + // var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + // const ally = gpa.allocator(); + // + // const m: []const u8 = "hello world!"; + // const ad: []const u8 = "foo"; + // + // // see https://crypto.stackexchange.com/a/5818 + // // the nonce does not need to be random, it can be a counter + // // it can be sent in clear, or each end may deduce it + // // but it ___MUST___ be unique and unrelated to the key + // var iv: [aead.aes_gcm.Aes256Gcm.nonce_length]u8 = undefined; + // std.crypto.random.bytes(&iv); + // + // const c: []u8 = try ally.alloc(u8, m.len); + // defer ally.free(c); + // + // @memset(c, 0); + // var tag: [aead.aes_gcm.Aes256Gcm.tag_length]u8 = undefined; + // + // std.debug.print("c: {any}\n", .{c}); + // aead.aes_gcm.Aes256Gcm.encrypt(c, &tag, m, ad, iv, key); + // std.debug.print("{any} {any} {any}\n", .{ c, tag, iv }); // aead.aes_gcm.Aes256Gcm.encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) @@ -132,3 +186,18 @@ test "simple test" { try list.append(42); try std.testing.expectEqual(@as(i32, 42), list.pop()); } + +/// caller takes ownership of result +fn digest(ally: std.mem.Allocator, data: []const u8, case: std.fmt.Case) ![]const u8 { + const charset = switch (case) { + .lower => "0123456789abcdef", + .upper => "0123456789ABCDEF", + }; + + var res = try ally.alloc(u8, data.len * 2); + for (data, 0..) |ch, idx| { + res[idx * 2 + 0] = charset[ch >> 4]; + res[idx * 2 + 1] = charset[ch & 0xf]; + } + return res; +}