@@ -26,7 +26,11 @@ suite: union(enum) {
ecc: EllipticCurve,
aes: AnyAES,
} = .{ .invalid = {} },
key_xhg: KeyExchange = .{},
key_xchg: union(enum) {
invalid: void,
ecdh: ECDH,
dh: void,
} = .{ .invalid = {} },
sequence: u72 = 0, // u72 is chacha20 specific :/
pub fn encrypt(c: *Cipher, clear_text: []const u8, cipher_text: []u8) !usize {
@@ -88,21 +92,76 @@ pub fn encrypt(c: *Cipher, clear_text: []const u8, cipher_text: []u8) !usize {
comptime unreachable;
}
pub fn decrypt(c: *Cipher, cipher_text: []const u8, clear: []u8) ![]const u8 {
_ = cipher_text;
pub fn decrypt(c: *Cipher, cipher_text_: []const u8, clear_txt: []u8) !usize {
var cipher_txt = cipher_text_;
switch (c.suite) {
.ecc => |ecc| {
_ = ecc;
unreachable;
},
.aes => |aes| {
_ = aes;
var aes_ctx = std.crypto.core.aes.Aes256.initDec(aes.material.srv_key);
var fba = fixedBufferStream(clear_txt);
var w = fba.writer().any();
var xord: [16]u8 = cipher_txt[0..16].*;
cipher_txt = cipher_txt[16..];
std.debug.assert(cipher_txt.len % 16 == 0);
for (0..(cipher_txt.len - 16) / 16) |_| {
var xclear: [16]u8 = undefined;
aes_ctx.decrypt(xclear[0..], cipher_txt[0..16]);
cipher_txt = cipher_txt[16..];
var clear: [16]u8 = undefined;
for (clear[0..], xclear[0..], xord[0..]) |*cl, xc, xr| cl.* = xc ^ xr;
@memcpy(xord[0..16], cipher_txt[0..16]);
try w.writeAll(clear[0..]);
}
// TODO compute and verify mac
//var len: usize = clear_text.len;
//{
// const mac_out: *[48]u8 = l_clear[len..][0..48];
// var mac = HmacSha384.init(&aes.material.cli_mac);
// mac.update(std.mem.asBytes(&nativeToBig(u64, @truncate(c.sequence))));
// mac.update(&[_]u8{ 22, 3, 3 });
// mac.update(std.mem.asBytes(&nativeToBig(u16, @truncate(len))));
// mac.update(clear_text);
// mac.final(mac_out);
// len += 48;
//}
return try fba.getPos() - 48;
},
else => unreachable,
}
return clear;
return clear_txt;
}
pub const KeyExchange = struct {};
pub const ECDH = struct {
curve: union(enum) {
x25519: struct {
cli_dh: ?X25519.KeyPair = null,
srv_dh: ?X25519.KeyPair = null,
},
} = .{},
pub fn packClient(ecdh: ECDH, buffer: []u8) !usize {
var fba = fixedBufferStream(buffer);
const w = fba.writer().any();
switch (ecdh.curve) {
.x25519 => |x| {
try w.writeByte(@truncate(x.cli_dh.?.public_key.len));
try w.writeAll(&x.cli_dh.?.public_key);
return 1 + x.cli_dh.?.public_key.len;
},
}
}
//pub fn packKeyExchange(dh: ECDH, buffer: []u8) !usize { }
//pub fn pack(dh: ECDH, buffer: []u8) !usize { }
};
pub fn Material(comptime ENC: anytype, comptime HMAC: anytype) type {
return struct {
@@ -115,9 +174,6 @@ pub fn Material(comptime ENC: anytype, comptime HMAC: anytype) type {
pub const PRF = HmacSha384;
srv_pub_key: [32]u8 = undefined,
premaster: [X25519.shared_length]u8 = [_]u8{0} ** X25519.shared_length,
master: [48]u8 = undefined,
cli_mac: [MacLength]u8,
srv_mac: [MacLength]u8,
@@ -254,9 +310,6 @@ pub const AnyAES = struct {
cbc: CBC,
},
cli_dh: ?X25519.KeyPair = null,
srv_dh: ?X25519.KeyPair = null,
/// srv is copied, and will not zero any arguments
pub fn init(srv: [X25519.public_length]u8) !EllipticCurve {
return .{
@@ -280,32 +333,6 @@ pub const AnyAES = struct {
return 48;
}
fn packClientECDHE(aes: AnyAES, buffer: []u8) !usize {
var fba = fixedBufferStream(buffer);
const w = fba.writer().any();
try w.writeByte(@truncate(aes.cli_dh.?.public_key.len));
try w.writeAll(&aes.cli_dh.?.public_key);
return 1 + aes.cli_dh.?.public_key.len;
}
fn packClientDHE(aes: AnyAES, buffer: []u8) !usize {
var fba = fixedBufferStream(buffer);
const w = fba.writer().any();
try w.writeByte(1); // PublicValueEncoding.explicit
_ = aes;
// struct {
// select (PublicValueEncoding) {
// case implicit: struct { };
// case explicit: opaque dh_Yc<1..2^16-1>;
// } dh_public;
// } ClientDiffieHellmanPublic;
// dh_Yc
// The client's Diffie-Hellman public value (Yc).
return 0;
}
/// TODO move this into the CBC struct
fn packCBC(aes: AnyAES, buffer: []u8) !usize {
//var fba = fixedBufferStream(buffer);
@@ -315,19 +342,11 @@ pub const AnyAES = struct {
//w.writeAll(encrypted_premaster_secret);
}
pub fn packKeyExchange(aes: AnyAES, buffer: []u8) !usize {
return switch (aes.block) {
.cbc => aes.packCBC(buffer),
// LOL sorry future me!
//else => return error.NotImplemented,
};
}
fn buildKeyMaterial(ctx: *ConnCtx) !Material(AES(256, CBC), HmacSha384) {
var aes = &ctx.cipher.suite.aes;
const our_seckey = ctx.cipher.suite.aes.cli_dh.?.secret_key;
const peer_key = &ctx.cipher.suite.aes.srv_dh.?.public_key;
const our_seckey = ctx.cipher.key_xchg.ecdh.curve.x25519.cli_dh.?.secret_key;
const peer_key = &ctx.cipher.key_xchg.ecdh.curve.x25519.srv_dh.?.public_key;
const premaster = try X25519.scalarmult(our_seckey, peer_key.*);
const seed = "master secret" ++ ctx.cli_random.? ++ ctx.srv_random.?;
@@ -349,7 +368,7 @@ pub const AnyAES = struct {
//var fba = fixedBufferStream(buffer);
//const r = fba.reader().any();
////const name = try r.readInt(u16, .big);
////ctx.cipher.suite.aes.srv_dh = undefined;
////ctx.cipher.key_xhg.group.x25519.srv_dh = undefined;
//const peer_key = &ctx.cipher.suite.aes.material.srv_pub_key;
//try r.readNoEof(peer_key);
@@ -364,11 +383,11 @@ pub const AnyAES = struct {
if (name != 0x001d) return error.UnknownCurveName;
const key_len = try r.readByte();
std.debug.assert(key_len == 32);
ctx.cipher.suite.aes.srv_dh = undefined;
try r.readNoEof(&ctx.cipher.suite.aes.srv_dh.?.public_key);
ctx.cipher.key_xchg.ecdh.curve.x25519.srv_dh = undefined;
try r.readNoEof(&ctx.cipher.key_xchg.ecdh.curve.x25519.srv_dh.?.public_key);
// TODO verify signature
ctx.cipher.suite.aes.cli_dh = try Cipher.X25519.KeyPair.create(null);
ctx.cipher.key_xchg.ecdh.curve.x25519.cli_dh = try Cipher.X25519.KeyPair.create(null);
ctx.cipher.suite.aes.material = try buildKeyMaterial(ctx);
}
@@ -457,11 +476,9 @@ pub const ECC = struct {
};
pub const EllipticCurve = struct {
curve: Curves = .{ .invalid = {} },
cli_dh: ?X25519.KeyPair = null,
srv_dh: ?X25519.KeyPair = null,
curve: enum {
named_curve,
},
material: Material(ChaCha20, Sha256) = undefined,
/// srv is copied, and will not zero any arguments
@@ -500,28 +517,26 @@ pub const EllipticCurve = struct {
pub fn packKeyExchange(ecc: EllipticCurve, buffer: []u8) !usize {
return switch (ecc.curve) {
.named_curve => try ecc.packNamed(buffer),
// LOL sorry future me!
else => return try ecc.packNamed(buffer),
};
}
fn buildKeyMaterial(ctx: *ConnCtx) !Material(ChaCha20, Sha256) {
var material: Material(ChaCha20, Sha256) = ctx.cipher.suite.ecc.material;
const our_seckey = ctx.cipher.suite.ecc.cli_dh.?.secret_key;
const peer_key = &ctx.cipher.suite.ecc.srv_dh.?.public_key;
material.premaster = try X25519.scalarmult(our_seckey, peer_key.*);
const our_seckey = ctx.cipher.key_xchg.ecdh.curve.x25519.cli_dh.?.secret_key;
const peer_key = &ctx.cipher.key_xchg.ecdh.curve.x25519.srv_dh.?.public_key;
const premaster = try X25519.scalarmult(our_seckey, peer_key.*);
const seed = "master secret" ++ ctx.cli_random.? ++ ctx.srv_random.?;
var pre_left: [32]u8 = undefined;
var pre_right: [32]u8 = undefined;
Sha256.create(&pre_left, seed, &material.premaster);
Sha256.create(&pre_right, &pre_left, &material.premaster);
Sha256.create(&pre_left, seed, &premaster);
Sha256.create(&pre_right, &pre_left, &premaster);
var left: [32]u8 = undefined;
var right: [32]u8 = undefined;
Sha256.create(&left, pre_left ++ seed, &material.premaster);
Sha256.create(&right, pre_right ++ seed, &material.premaster);
Sha256.create(&left, pre_left ++ seed, &premaster);
Sha256.create(&right, pre_right ++ seed, &premaster);
material.master = left ++ right[0..16].*;
@@ -568,13 +583,13 @@ pub const EllipticCurve = struct {
if (name != 0x001d) return error.UnknownCurveName;
const key_len = try r.readByte();
std.debug.assert(key_len == 32);
ctx.cipher.suite.ecc.srv_dh = undefined;
const peer_key = &ctx.cipher.suite.ecc.srv_dh.?.public_key;
ctx.cipher.key_xchg.ecdh.curve.x25519.srv_dh = undefined;
const peer_key = &ctx.cipher.key_xchg.ecdh.curve.x25519.srv_dh.?.public_key;
try r.readNoEof(peer_key);
// TODO verify signature
ctx.cipher.suite.ecc.cli_dh = try Cipher.X25519.KeyPair.create(null);
ctx.cipher.key_xchg.ecdh.curve.x25519.cli_dh = try Cipher.X25519.KeyPair.create(null);
ctx.cipher.suite.ecc.material = try buildKeyMaterial(ctx);
}