@@ -12,6 +12,7 @@ const Extensions = @import("extensions.zig");
const Extension = Extensions.Extension;
const Protocol = @import("protocol.zig");
const State = @import("state.zig");
const Handshake = @import("handshake.zig");
var csprng = std.Random.ChaCha.init([_]u8{0} ** 32);
@@ -38,7 +39,7 @@ const TLSRecord = struct {
kind: union(ContentType) {
change_cipher_spec: []const u8,
alert: Alert,
handshake: Handshake,
handshake: Handshake.Handshake,
application_data: []const u8,
},
@@ -80,7 +81,7 @@ const TLSRecord = struct {
.kind = switch (fragtype) {
.change_cipher_spec => .{ .change_cipher_spec = unreachable },
.alert => .{ .alert = try Alert.unpack(fragbuff) },
.handshake => .{ .handshake = try Handshake.unpack(fragbuff, sess) },
.handshake => .{ .handshake = try Handshake.Handshake.unpack(fragbuff, sess) },
.application_data => .{ .application_data = unreachable },
},
};
@@ -91,8 +92,7 @@ const TLSRecord = struct {
};
pub const Random = extern struct {
unix_time: u32,
random_bytes: [28]u8,
random_bytes: [32]u8,
};
comptime {
@@ -101,106 +101,6 @@ comptime {
pub const SessionID = [32]u8;
pub const Compression = enum(u8) {
null = 0,
};
pub const ClientHello = struct {
version: Protocol.Version,
random: Random,
session_id: SessionID,
ciphers: []const CipherSuite = &[0]CipherSuite{},
compression: Compression,
extensions: []const Extension = &[0]Extension{},
pub const SupportedExtensions = [_]type{
Extensions.SupportedGroups,
Extensions.SignatureAlgorithms,
};
pub const SupportedSuiteList = [_]CipherSuite{
CipherSuites.TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
CipherSuites.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
CipherSuites.TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
CipherSuites.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuites.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuites.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuites.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
CipherSuites.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuites.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
CipherSuites.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuites.TLS_DH_DSS_WITH_AES_128_CBC_SHA,
CipherSuites.TLS_DH_RSA_WITH_AES_128_CBC_SHA,
CipherSuites.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
CipherSuites.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuites.TLS_DH_DSS_WITH_AES_256_CBC_SHA,
CipherSuites.TLS_DH_RSA_WITH_AES_256_CBC_SHA,
CipherSuites.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
CipherSuites.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuites.TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
CipherSuites.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
CipherSuites.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
CipherSuites.TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
CipherSuites.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
};
pub const length = @sizeOf(ClientHello);
pub fn init() ClientHello {
var hello = ClientHello{
.version = .{ .major = 3, .minor = 3 },
.random = .{
.unix_time = @truncate(@abs(std.time.timestamp())),
.random_bytes = undefined,
},
.session_id = [_]u8{0} ** 32,
.ciphers = &SupportedSuiteList,
.compression = .null,
};
csprng.fill(&hello.random.random_bytes);
csprng.fill(&hello.session_id);
return hello;
}
pub fn pack(ch: ClientHello, buffer: []u8) !usize {
var fba = fixedBufferStream(buffer);
var w = fba.writer().any();
try w.writeByte(ch.version.major);
try w.writeByte(ch.version.minor);
try w.writeStruct(ch.random);
try w.writeByte(0);
//try w.writeByte(ch.session_id.len);
//try w.writeAll(&ch.session_id);
const c_count: u16 = @truncate(ch.ciphers.len);
try w.writeInt(u16, c_count * 2, std.builtin.Endian.big);
for (ch.ciphers) |cipher| {
try w.writeByte(cipher[0]);
try w.writeByte(cipher[1]);
}
try w.writeByte(1);
try w.writeByte(@intFromEnum(ch.compression));
var e_count: u16 = 0;
var extension_buffer: [0x1000]u8 = undefined;
inline for (SupportedExtensions) |extension| {
var extt = extension{};
var ext = extt.extension();
e_count += @truncate(ext.pack(extension_buffer[e_count..]));
}
try w.writeInt(u16, e_count, std.builtin.Endian.big);
try w.writeAll(extension_buffer[0..e_count]);
return fba.pos;
}
pub fn unpack(buffer: []const u8, _: *State) !ClientHello {
_ = buffer;
unreachable;
}
};
const RecordProto = struct {};
pub const ConnectionEnd = struct {};
pub const PRFAlgorithm = struct {};
@@ -322,59 +222,12 @@ const GenericAEADCipher = struct {};
const HelloRequest = struct {};
const HandshakeType = enum(u8) {
hello_request = 0,
client_hello = 1,
server_hello = 2,
certificate = 11,
server_key_exchange = 12,
certificate_request = 13,
server_hello_done = 14,
certificate_verify = 15,
client_key_exchange = 16,
finished = 20,
pub fn fromByte(kind: u8) !HandshakeType {
return switch (kind) {
0 => .hello_request,
1 => .client_hello,
2 => .server_hello,
11 => .certificate,
12 => .server_key_exchange,
13 => .certificate_request,
14 => .server_hello_done,
15 => .certificate_verify,
16 => .client_key_exchange,
20 => .finished,
else => unreachable,
};
}
pub fn toType(kind: HandshakeType) type {
return switch (kind) {
inline .hello_request => unreachable,
inline .client_hello => ClientHello,
inline .server_hello => ServerHello,
inline .certificate => Certificate,
inline .server_key_exchange => ServerKeyExchange,
inline .certificate_request => CertificateRequest,
inline .server_hello_done => ServerHelloDone,
.certificate_verify,
.client_key_exchange,
.finished,
=> unreachable,
else => return error.UnknownHandshakeType,
};
}
};
const ServerHello = struct {
pub const ServerHello = struct {
version: Protocol.Version,
random: Random,
session_id: SessionID,
cipher: CipherSuite,
compression: Compression,
compression: Handshake.Compression,
extensions: []const Extension,
pub fn unpack(buffer: []const u8, _: *State) !ServerHello {
@@ -386,7 +239,6 @@ const ServerHello = struct {
.minor = try r.readByte(),
};
var random = Random{
.unix_time = try r.readInt(u32, std.builtin.Endian.big),
.random_bytes = undefined,
};
try r.readNoEof(&random.random_bytes);
@@ -426,7 +278,7 @@ const ServerHello = struct {
}
};
const Certificate = struct {
pub const Certificate = struct {
buffer: []const u8,
session: *State,
@@ -438,7 +290,7 @@ const Certificate = struct {
}
};
const ServerKeyExchange = struct {
pub const ServerKeyExchange = struct {
buffer: []const u8,
cipher: *const Cipher,
@@ -461,7 +313,7 @@ const ServerKeyExchange = struct {
}
};
const CertificateRequest = struct {
pub const CertificateRequest = struct {
buffer: []const u8,
session: *State,
@@ -473,7 +325,7 @@ const CertificateRequest = struct {
}
};
const ServerHelloDone = struct {
pub const ServerHelloDone = struct {
buffer: []const u8,
session: *State,
@@ -493,7 +345,7 @@ const ClientECDH = struct {
point: []u8 = &[0]u8{},
};
const ClientKeyExchange = struct {
pub const ClientKeyExchange = struct {
/// RFC 4492
pve: enum(u8) {
/// Provided in the client cert
@@ -524,76 +376,6 @@ const ClientKeyExchange = struct {
const Finished = struct {};
fn handshakeFromHeader(kind: HandshakeType) type {
return Handshake(kind);
}
const Handshakes = union(HandshakeType) {
hello_request: void,
client_hello: ClientHello,
server_hello: ServerHello,
certificate: Certificate,
server_key_exchange: ServerKeyExchange,
certificate_request: CertificateRequest,
server_hello_done: ServerHelloDone,
certificate_verify: void,
client_key_exchange: ClientKeyExchange,
finished: void,
};
const Handshake = struct {
msg_type: HandshakeType,
_length: u24 = 0, // unused
body: Handshakes,
pub fn wrap(any: anytype) !Handshake {
const kind = @TypeOf(any);
return switch (kind) {
ClientHello => .{
.msg_type = .client_hello,
.body = .{ .client_hello = any },
},
ClientKeyExchange => .{
.msg_type = .client_key_exchange,
.body = .{ .client_key_exchange = any },
},
else => comptime unreachable,
};
}
pub fn pack(hs: Handshake, buffer: []u8) !usize {
var fba = fixedBufferStream(buffer);
var w = fba.writer().any();
const len = switch (hs.body) {
.client_hello => |ch| try ch.pack(buffer[4..]),
.client_key_exchange => |cke| try cke.pack(buffer[4..]),
else => unreachable,
};
std.debug.assert(len < std.math.maxInt(u24));
try w.writeByte(@intFromEnum(hs.msg_type));
try w.writeInt(u24, @truncate(len), std.builtin.Endian.big);
return len + 4;
}
pub fn unpack(buffer: []const u8, sess: *State) !Handshake {
const hs_type = try HandshakeType.fromByte(buffer[0]);
const hsbuf = buffer[4..];
return .{
.msg_type = hs_type,
.body = switch (hs_type) {
.client_hello => .{ .client_hello = try ClientHello.unpack(hsbuf, sess) },
.server_hello => .{ .server_hello = try ServerHello.unpack(hsbuf, sess) },
.certificate => .{ .certificate = try Certificate.unpack(hsbuf, sess) },
.server_key_exchange => .{ .server_key_exchange = try ServerKeyExchange.unpack(hsbuf, sess) },
.certificate_request => .{ .certificate_request = try CertificateRequest.unpack(hsbuf, sess) },
.server_hello_done => .{ .server_hello_done = try ServerHelloDone.unpack(hsbuf, sess) },
else => unreachable,
},
};
}
};
const HashAlgorithm = enum(u8) {
none = 0,
md5 = 1,
@@ -619,10 +401,10 @@ const SignatureAndHashAlgorithm = struct {
test "Handshake ClientHello" {
var buffer = [_]u8{0} ** 0x400;
const client_hello = ClientHello.init();
const client_hello = Handshake.ClientHello.init();
const record = TLSRecord{
.kind = .{
.handshake = try Handshake.wrap(client_hello),
.handshake = try Handshake.Handshake.wrap(client_hello),
},
};
@@ -632,10 +414,10 @@ test "Handshake ClientHello" {
fn startHandshake(conn: std.net.Stream) !void {
var buffer = [_]u8{0} ** 0x1000;
const client_hello = ClientHello.init();
const client_hello = Handshake.ClientHello.init();
const record = TLSRecord{
.kind = .{
.handshake = try Handshake.wrap(client_hello),
.handshake = try Handshake.Handshake.wrap(client_hello),
},
};
@@ -705,7 +487,7 @@ fn completeClient(conn: std.net.Stream) !void {
const cke = try ClientKeyExchange.init();
const cke_record = TLSRecord{
.kind = .{
.handshake = try Handshake.wrap(cke),
.handshake = try Handshake.Handshake.wrap(cke),
},
};
@@ -847,9 +629,9 @@ test "mock server response" {
//print("CKE: {any}\n", .{buffer[0..43]});
}
const CipherSuite = [2]u8;
pub const CipherSuite = [2]u8;
const CipherSuites = struct {
pub const CipherSuites = struct {
pub const TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: CipherSuite = .{ 0x00, 0x6B };
pub const TLS_DH_RSA_WITH_AES_256_CBC_SHA256: CipherSuite = .{ 0x00, 0x69 };
pub const TLS_DH_DSS_WITH_AES_128_CBC_SHA256: CipherSuite = .{ 0x00, 0x3E };