working dh aes flow

This commit is contained in:
David Allemang
2024-03-04 20:45:42 -05:00
parent 12ab3391e8
commit 4fc1436431

View File

@@ -10,12 +10,12 @@ const Encryptor = struct {
kp: DH.KeyPair, kp: DH.KeyPair,
pub fn init() !Self { pub fn init() !Self {
const self = Self{}; var self: Self = undefined;
var seed: [DH.seed_length]u8 = undefined; var seed: [DH.seed_length]u8 = undefined;
std.crypto.random.bytes(&seed); std.crypto.random.bytes(&seed);
self.kp = try DH.KeyPair.create(seed); self.kp = try DH.KeyPair.create(seed);
@memset(seed, 0); @memset(&seed, 0);
return self; return self;
} }
@@ -27,11 +27,12 @@ const Peer = struct {
public_key: [DH.public_length]u8, public_key: [DH.public_length]u8,
shared_key: [AES.key_length]u8, shared_key: [AES.key_length]u8,
pub fn init(me: DH.KeyPair, peer_key: [DH.public_length]u8) Self { pub fn init(me: DH.KeyPair, peer_key: [DH.public_length]u8) !Self {
const self = Self{ .public_key = peer_key }; var self: Self = undefined;
self.public_key = peer_key;
const secret = try DH.scalarmult(me.secret_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; return self;
} }
@@ -46,8 +47,10 @@ const Peer = struct {
} }
/// Caller must free result /// 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, ally: std.mem.Allocator,
nonce: [AES.nonce_length]u8, nonce: [AES.nonce_length]u8,
tag: [AES.tag_length]u8, tag: [AES.tag_length]u8,
cipher_text: []u8, cipher_text: []u8,
}; };
pub fn main() !void { pub fn main() !void {
const me = try Encryptor.init(); const bob = try Encryptor.init();
const him = try Encryptor.init(); const alice = try Encryptor.init();
const my_peer = Peer.init(me, him.kp.public_key); std.debug.print(
const his_peer = Peer.init(him, me.kp.public_key); "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; const bob_peer = try Peer.init(bob.kp, alice.kp.public_key);
std.crypto.random.bytes(&seed); const alice_peer = try Peer.init(alice.kp, bob.kp.public_key);
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(
std.debug.print("{any}\n{any}\n\n", .{ kp2.public_key, kp2.secret_key }); "Bob deduces {s}\n",
.{std.fmt.bytesToHex(bob_peer.shared_key, .lower)},
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); std.debug.print(
"Alice deduces {s}\n",
var key: [aead.aes_gcm.Aes256Gcm.key_length]u8 = undefined; .{std.fmt.bytesToHex(alice_peer.shared_key, .lower)},
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(.{}){}; 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 ally = arena.allocator();
const ad: []const u8 = "foo";
// see https://crypto.stackexchange.com/a/5818 // bob send a message to alice
// the nonce does not need to be random, it can be a counter const msg = "Hello World!";
// 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); std.debug.print(
defer ally.free(c); "Bob sending message '{s}' ({s})\n",
.{ msg, std.fmt.bytesToHex(msg, .lower) },
);
@memset(c, 0); const pkt = try bob_peer.encrypt(ally, msg);
var tag: [aead.aes_gcm.Aes256Gcm.tag_length]u8 = undefined; defer ally.free(pkt.cipher_text);
std.debug.print("c: {any}\n", .{c}); std.debug.print(
aead.aes_gcm.Aes256Gcm.encrypt(c, &tag, m, ad, iv, key); "Packet: {s} {s} {s}\n",
std.debug.print("{any} {any} {any}\n", .{ c, tag, iv }); .{
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) // 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 list.append(42);
try std.testing.expectEqual(@as(i32, 42), list.pop()); 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;
}