srctree

Gregory Mullen parent efbdf76b dd31f73a
start implementing aes cbc 256

src/cipher.zig added: 200, removed: 22, total 178
@@ -20,11 +20,19 @@ pub const Cipher = @This();
suite: union(enum) {
invalid: void,
ecc: EllipticCurve,
aes: AnyAES,
} = .{ .invalid = {} },
sequence: u72 = 0, // this is chacha20 specific :/
 
pub fn Material(comptime enc: anytype, comptime hmac: anytype) type {
return struct {
pub const Self = @This();
 
//cli_random: [32]u8 = undefined,
//srv_random: [32]u8 = undefined,
 
srv_pub_key: [32]u8 = undefined,
 
premaster: [X25519.shared_length]u8 = [_]u8{0} ** X25519.shared_length,
master: [48]u8 = undefined,
cli_mac: [hmac.mac_length]u8,
@@ -33,6 +41,21 @@ pub fn Material(comptime enc: anytype, comptime hmac: anytype) type {
srv_key: [enc.key_length]u8,
cli_iv: [enc.nonce_length]u8,
srv_iv: [enc.nonce_length]u8,
 
pub fn build(self: *Self, key_material: []const u8) void {
var index: usize = 0;
self.cli_mac = key_material[index..][0..hmac.mac_length].*;
index += hmac.mac_length;
self.srv_mac = key_material[index..][0..hmac.mac_length].*;
index += hmac.mac_length;
self.cli_key = key_material[index..][0..enc.key_length].*;
index += enc.key_length;
self.srv_key = key_material[index..][0..enc.key_length].*;
index += enc.key_length;
self.cli_iv = key_material[index..][0..enc.nonce_length].*;
index += enc.nonce_length;
self.srv_iv = key_material[index..][0..enc.nonce_length].*;
}
};
}
 
@@ -97,6 +120,110 @@ pub const Suites = enum(u16) {
}
};
 
pub const AnyAES = struct {
material: Material(AES(CBC), Sha256),
/// srv is copied, and will not zero any arguments
pub fn init(srv: [X25519.public_length]u8) !EllipticCurve {
return .{
.srv_material = try X25519.KeyPair.create(srv),
};
}
 
fn packNamed(_: EllipticCurve, _: []u8) !usize {}
fn buildKeyMaterial(ctx: *ConnCtx) !Material(AES(CBC), Sha256) {
var aes = &ctx.cipher.suite.aes;
 
//const our_seckey = ctx.cipher.suite.aes.material.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 seed = "master secret" ++ ctx.cli_random.? ++ ctx.srv_random.?;
//var left = std.crypto.auth.hmac.sha2.HmacSha256.init(ctx.cipher.suite.ecc.premaster);
 
var pre_left: [32]u8 = undefined;
var pre_right: [32]u8 = undefined;
Sha256.create(&pre_left, seed, &aes.material.premaster);
Sha256.create(&pre_right, &pre_left, &aes.material.premaster);
var left: [32]u8 = undefined;
var right: [32]u8 = undefined;
Sha256.create(&left, pre_left ++ seed, &aes.material.premaster);
Sha256.create(&right, pre_right ++ seed, &aes.material.premaster);
 
aes.material.master = left ++ right[0..16].*;
 
{
const key_seed = "key expansion" ++ ctx.cli_random.? ++ ctx.srv_random.?;
var first: [32]u8 = undefined;
Sha256.create(&first, key_seed, &aes.material.master);
var second: [32]u8 = undefined;
Sha256.create(&second, &first, &aes.material.master);
var third: [32]u8 = undefined;
Sha256.create(&third, &second, &aes.material.master);
var forth: [32]u8 = undefined;
Sha256.create(&forth, &third, &aes.material.master);
var fifth: [32]u8 = undefined;
Sha256.create(&fifth, &forth, &aes.material.master);
 
var p_first: [32]u8 = undefined;
Sha256.create(&p_first, first ++ key_seed, &aes.material.master);
var p_second: [32]u8 = undefined;
Sha256.create(&p_second, second ++ key_seed, &aes.material.master);
var p_third: [32]u8 = undefined;
Sha256.create(&p_third, third ++ key_seed, &aes.material.master);
var p_forth: [32]u8 = undefined;
Sha256.create(&p_forth, forth ++ key_seed, &aes.material.master);
var p_fifth: [32]u8 = undefined;
Sha256.create(&p_fifth, fifth ++ key_seed, &aes.material.master);
const final = p_first ++ p_second ++ p_third ++ p_forth ++ p_fifth;
 
aes.material.build(&final);
}
return aes.material;
}
 
fn unpackCBC(buffer: []const u8, ctx: *ConnCtx) !void {
var fba = fixedBufferStream(buffer);
const r = fba.reader().any();
//const name = try r.readInt(u16, .big);
//ctx.cipher.suite.aes.srv_dh = undefined;
const peer_key = &ctx.cipher.suite.aes.material.srv_pub_key;
try r.readNoEof(peer_key);
 
// TODO verify signature
ctx.cipher.suite.aes.material = try buildKeyMaterial(ctx);
}
 
pub fn unpackKeyExchange(buffer: []const u8, ctx: *ConnCtx) !void {
if (ctx.cipher.suite != .aes) unreachable;
//var fba = fixedBufferStream(buffer);
//const r = fba.reader().any();
 
//const curve_type = try CurveType.fromByte(try r.readByte());
//switch (curve_type) {
// .named_curve => try unpackNamed(buffer[1..], ctx),
// else => return error.UnsupportedCurve,
//}
return unpackCBC(buffer, ctx);
}
};
 
pub const CBC = struct {
pub const key_length = 32;
pub const nonce_length = 0;
};
 
//pub const AESType = enum {
// AES_128_CBC_SHA,
//};
 
pub fn AES(comptime T: type) type {
return struct {
pub const Kind = T;
pub const key_length = T.key_length;
pub const nonce_length = T.nonce_length;
};
}
 
pub const EllipticCurve = struct {
curve: Curves = .{ .invalid = {} },
 
@@ -178,7 +305,7 @@ pub const EllipticCurve = struct {
const peer_key = &ctx.cipher.suite.ecc.srv_dh.?.public_key;
material.premaster = try X25519.scalarmult(our_seckey, peer_key.*);
 
const seed = "master secret" ++ ctx.our_random ++ ctx.peer_random.?;
const seed = "master secret" ++ ctx.cli_random.? ++ ctx.srv_random.?;
//var left = std.crypto.auth.hmac.sha2.HmacSha256.init(ctx.cipher.suite.ecc.premaster);
 
var pre_left: [32]u8 = undefined;
@@ -193,7 +320,7 @@ pub const EllipticCurve = struct {
material.master = left ++ right[0..16].*;
 
{
const key_seed = "key expansion" ++ ctx.peer_random.? ++ ctx.our_random;
const key_seed = "key expansion" ++ ctx.cli_random.? ++ ctx.srv_random.?;
var first: [32]u8 = undefined;
Sha256.create(&first, key_seed, &material.master);
var second: [32]u8 = undefined;
 
src/context.zig added: 200, removed: 22, total 178
@@ -1,5 +1,6 @@
// TODO refactor more
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const root = @import("root.zig");
const Cipher = @import("cipher.zig");
@@ -14,8 +15,8 @@ const SessionID = root.SessionID;
pub const ConnCtx = @This();
 
cipher: Cipher = .{},
our_random: [32]u8,
peer_random: ?[32]u8 = null,
cli_random: ?[32]u8 = null,
srv_random: ?[32]u8 = null,
session_id: ?SessionID = null,
entity: ConnectionEnd = .{},
prf_algorithm: PRFAlgorithm = .{},
@@ -25,13 +26,24 @@ compression_algorithm: CompressionMethod = null,
// I hate this protocol
handshake_record: std.ArrayList(u8),
 
pub fn initClient(a: std.mem.Allocator) ConnCtx {
pub fn initClient(a: Allocator) ConnCtx {
var rand: [32]u8 = undefined;
var csprng = std.Random.ChaCha.init([_]u8{0} ** 32);
csprng.fill(&rand);
 
return .{
.our_random = rand,
.cli_random = rand,
.handshake_record = std.ArrayList(u8).init(a),
};
}
 
pub fn initCrypto(a: Allocator) ConnCtx {
var rand: [32]u8 = undefined;
var csprng = std.Random.ChaCha.init([_]u8{0} ** 32);
csprng.fill(&rand);
 
return .{
.cli_random = rand,
.handshake_record = std.ArrayList(u8).init(a),
};
}
 
src/handshake.zig added: 200, removed: 22, total 178
@@ -53,15 +53,15 @@ pub const ClientHello = struct {
};
 
pub const SupportedSuiteList = [_]Cipher.Suites{
Cipher.Suites.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
Cipher.Suites.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
};
 
pub const length = @sizeOf(ClientHello);
 
pub fn init(ctx: ConnCtx) ClientHello {
var hello = ClientHello{
.random = ctx.our_random,
.random = ctx.cli_random.?,
.session_id = [_]u8{0} ** 32,
};
 
@@ -170,8 +170,8 @@ pub const ServerHello = struct {
};
std.debug.assert(version.major == 3 and version.minor == 3);
 
ctx.peer_random = undefined;
try r.readNoEof(&ctx.peer_random.?);
ctx.srv_random = undefined;
try r.readNoEof(&ctx.srv_random.?);
 
const session_size = try r.readByte();
var session_id: [32]u8 = [_]u8{0} ** 32;
@@ -188,6 +188,14 @@ pub const ServerHello = struct {
=> {
ctx.cipher.suite = .{ .ecc = undefined };
},
.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
=> {
ctx.cipher.suite = .{ .aes = undefined };
},
//else => ctx.cipher.suite = .{ .ecc = undefined },
else => unreachable,
}
@@ -217,7 +225,11 @@ pub const ServerKeyExchange = struct {
/// Will modify sess with supplied
pub fn unpack(buffer: []const u8, ctx: *ConnCtx) !ServerKeyExchange {
switch (ctx.cipher.suite) {
.ecc => try Cipher.EllipticCurve.unpackKeyExchange(buffer, ctx),
.ecc => {
ctx.cipher.suite.ecc.cli_dh = try Cipher.X25519.KeyPair.create(null);
try Cipher.EllipticCurve.unpackKeyExchange(buffer, ctx);
},
.aes => try Cipher.AnyAES.unpackKeyExchange(buffer, ctx),
else => unreachable,
}
return .{
 
src/root.zig added: 200, removed: 22, total 178
@@ -164,10 +164,11 @@ test "Handshake ClientHello" {
_ = len;
}
 
fn startHandshake(conn: std.net.Stream) !ConnCtx {
fn startHandshakeCustomSuites(conn: std.net.Stream, suites: []const Cipher.Suites) !ConnCtx {
var buffer = [_]u8{0} ** 0x1000;
var ctx = ConnCtx.initClient(std.testing.allocator);
const client_hello = Handshake.ClientHello.init(ctx);
var client_hello = Handshake.ClientHello.init(ctx);
client_hello.ciphers = suites;
const record = TLSRecord{
.kind = .{
.handshake = try Handshake.Handshake.wrap(client_hello),
@@ -182,6 +183,14 @@ fn startHandshake(conn: std.net.Stream) !ConnCtx {
return ctx;
}
 
fn startHandshake(conn: std.net.Stream) !ConnCtx {
return startHandshakeCustomSuites(conn, &[_]Cipher.Suites{
.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
});
}
 
/// Forgive me, I'm tired
fn readServer(conn: std.net.Stream, server: []u8) !usize {
const s_read = try conn.read(server);
@@ -212,10 +221,9 @@ fn buildServer(data: []const u8, ctx: *ConnCtx) !void {
.server_hello => |hello| {
if (false) print("server hello {}\n", .{@TypeOf(hello)});
if (false) print("srv selected suite {any}\n", .{ctx.cipher});
if (ctx.cipher.suite != .ecc) {
return error.UnexpectedCipherSuite;
}
ctx.cipher.suite.ecc.cli_dh = try Cipher.X25519.KeyPair.create(null);
//if (ctx.cipher.suite != .ecc) {
// return error.UnexpectedCipherSuite;
//}
},
.certificate => |cert| {
if (false) print("server cert {}\n", .{@TypeOf(cert)});
@@ -351,6 +359,7 @@ test "probe" {
}
 
test "tls" {
if (false) return error.SkipZigTest;
const addr = net.Address.resolveIp(TESTING_IP, TESTING_PORT) catch |err| {
print("unable to resolve address because {}\n", .{err});
return err;
@@ -364,6 +373,24 @@ test "tls" {
try completeClient(conn, &ctx);
}
 
test "cbc" {
const addr = net.Address.resolveIp(TESTING_IP, TESTING_PORT) catch |err| {
print("unable to resolve address because {}\n", .{err});
return err;
};
const conn = try net.tcpConnectToAddress(addr);
 
var ctx = try startHandshakeCustomSuites(conn, &[_]Cipher.Suites{
.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
});
errdefer ctx.handshake_record.deinit();
var server: [0x1000]u8 = undefined;
const l = try readServer(conn, &server);
try buildServer(server[0..l], &ctx);
std.debug.assert(ctx.cipher.suite == .aes);
try completeClient(conn, &ctx);
}
 
test "mock server response" {
if (true) return error.SkipZigTest;