srctree

Gregory Mullen parent 769787b5 55ff1aa9
send fake CKE and valid CCS

src/alert.zig added: 118, removed: 50, total 68
@@ -1,3 +1,4 @@
const std = @import("std");
pub const Alert = @This();
 
level: Level,
@@ -15,6 +16,13 @@ pub fn unpack(buffer: []const u8) !Alert {
};
}
 
pub fn format(self: Alert, comptime _: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
try out.print("Alert from peer ({s}) {s}\n", .{
@tagName(self.level),
@tagName(self.description),
});
}
 
pub const Level = enum(u8) {
warning = 1,
fatal = 2,
 
src/handshake.zig added: 118, removed: 50, total 68
@@ -27,6 +27,7 @@ const Extensions = @import("extensions.zig");
const Extension = Extensions.Extension;
 
const fixedBufferStream = std.io.fixedBufferStream;
const print = std.debug.print;
 
var csprng = std.Random.ChaCha.init([_]u8{0} ** 32);
 
@@ -49,30 +50,10 @@ pub const ClientHello = struct {
};
 
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,
//CipherSuites.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
//CipherSuites.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
};
pub const length = @sizeOf(ClientHello);
 
@@ -102,8 +83,7 @@ pub const ClientHello = struct {
//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);
try w.writeInt(u16, @truncate(ch.ciphers.len * 2), std.builtin.Endian.big);
for (ch.ciphers) |cipher| {
try w.writeByte(cipher[0]);
try w.writeByte(cipher[1]);
@@ -116,7 +96,7 @@ pub const ClientHello = struct {
inline for (SupportedExtensions) |extension| {
var extt = extension{};
var ext = extt.extension();
e_count += @truncate(ext.pack(extension_buffer[e_count..]));
e_count += @truncate(try ext.pack(extension_buffer[e_count..]));
}
try w.writeInt(u16, e_count, std.builtin.Endian.big);
try w.writeAll(extension_buffer[0..e_count]);
@@ -159,6 +139,23 @@ pub const ClientKeyExchange = struct {
}
};
 
pub const Finished = struct {
pub fn pack(_: Finished, buffer: []u8) !usize {
var fba = fixedBufferStream(buffer);
var w = fba.writer().any();
 
const iv = [0x10]u8{
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
};
try w.writeAll(&iv);
const encrypted = [_]u8{0} ** 0x30;
 
try w.writeAll(&encrypted);
return 0x40;
}
};
 
/// Server Section
pub const ServerHello = struct {
version: Protocol.Version,
@@ -169,6 +166,7 @@ pub const ServerHello = struct {
extensions: []const Extension,
 
pub fn unpack(buffer: []const u8, _: *State) !ServerHello {
print("buffer:: {any}\n", .{buffer});
var fba = fixedBufferStream(buffer);
const r = fba.reader().any();
 
@@ -200,7 +198,7 @@ pub const ServerHello = struct {
var extbuffer: [0x1000]u8 = undefined;
try r.readNoEof(extbuffer[0..extbytes]);
} else |err| switch (err) {
error.EndOfStream => std.debug.print("server hello readerror {}\n", .{err}),
error.EndOfStream => if (false) print("SrvHelo no extensions\n", .{}),
else => return err,
}
 
@@ -338,7 +336,7 @@ const Handshakes = union(Type) {
server_hello_done: ServerHelloDone,
certificate_verify: void,
client_key_exchange: ClientKeyExchange,
finished: void,
finished: Finished,
};
 
pub const Handshake = struct {
@@ -357,6 +355,10 @@ pub const Handshake = struct {
.msg_type = .client_key_exchange,
.body = .{ .client_key_exchange = any },
},
Finished => .{
.msg_type = .finished,
.body = .{ .finished = any },
},
else => comptime unreachable,
};
}
@@ -367,6 +369,7 @@ pub const Handshake = struct {
const len = switch (hs.body) {
.client_hello => |ch| try ch.pack(buffer[4..]),
.client_key_exchange => |cke| try cke.pack(buffer[4..]),
.finished => |fin| try fin.pack(buffer[4..]),
else => unreachable,
};
std.debug.assert(len < std.math.maxInt(u24));
 
src/root.zig added: 118, removed: 50, total 68
@@ -37,7 +37,7 @@ const TLSRecord = struct {
version: Protocol.Version = Protocol.TLSv1_2,
length: u16 = 0,
kind: union(ContentType) {
change_cipher_spec: []const u8,
change_cipher_spec: void, // This is const packet
alert: Alert,
handshake: Handshake.Handshake,
application_data: []const u8,
@@ -47,6 +47,7 @@ const TLSRecord = struct {
var fba = fixedBufferStream(buffer);
const len = switch (record.kind) {
.handshake => |ch| try ch.pack(buffer[5..]),
.change_cipher_spec => ChangeCipherSpec.pack(buffer[5..]),
else => unreachable,
};
var w = fba.writer().any();
@@ -79,7 +80,7 @@ const TLSRecord = struct {
.version = version,
.length = length,
.kind = switch (fragtype) {
.change_cipher_spec => .{ .change_cipher_spec = unreachable },
.change_cipher_spec => .{ .change_cipher_spec = if (fragbuff[0] != 1) return error.InvalidCCSPacket else {} },
.alert => .{ .alert = try Alert.unpack(fragbuff) },
.handshake => .{ .handshake = try Handshake.Handshake.unpack(fragbuff, sess) },
.application_data => .{ .application_data = unreachable },
@@ -157,11 +158,10 @@ pub const EllipticCurveCipher = struct {
const key_material = try std.crypto.dh.X25519.KeyPair.create(empty);
 
//try w.writeByte(@intFromEnum(cke.pve));
try w.writeByte(0);
//try w.writeInt(u16, @truncate(key_material.public_key.len), std.builtin.Endian.big);
try w.writeInt(u8, @truncate(key_material.public_key.len), std.builtin.Endian.big);
try w.writeAll(&key_material.public_key);
return 2 + key_material.public_key.len;
return 1 + key_material.public_key.len;
}
 
pub fn packKeyExchange(ecc: EllipticCurveCipher, buffer: []u8) !usize {
@@ -176,10 +176,10 @@ pub const EllipticCurveCipher = struct {
const r = fba.reader().any();
 
const curve_type = try CurveType.fromByte(try r.readByte());
print("named curve {} {}\n", .{ try r.readByte(), try r.readByte() });
print("full buffer {any}\n", .{buffer[0..4]});
print("full buffer {any}\n", .{buffer[4..][0..32]});
print("full buffer {any}\n", .{buffer[36..]});
//print("named curve {} {}\n", .{ try r.readByte(), try r.readByte() });
//print("full buffer {any}\n", .{buffer[0..4]});
//print("full buffer {any}\n", .{buffer[4..][0..32]});
//print("full buffer {any}\n", .{buffer[36..]});
return .{
.curve = switch (curve_type) {
.named_curve => .{ .named_curve = .{} },
@@ -239,7 +239,6 @@ const ClientECDH = struct {
// 1..255
point: []u8 = &[0]u8{},
};
const Finished = struct {};
 
const HashAlgorithm = enum(u8) {
none = 0,
@@ -264,6 +263,17 @@ const SignatureAndHashAlgorithm = struct {
signature: SignatureAlgorithm,
};
 
pub const ChangeCipherSpec = struct {
pub fn unpack(_: []const u8) !ChangeCipherSpec {
unreachable;
}
 
pub fn pack(buffer: []u8) usize {
buffer[0] = 0x01;
return 1;
}
};
 
test "Handshake ClientHello" {
var buffer = [_]u8{0} ** 0x400;
const client_hello = Handshake.ClientHello.init();
@@ -293,14 +303,14 @@ fn startHandshake(conn: std.net.Stream) !void {
}
 
/// Forgive me, I'm tired
fn getServer(conn: std.net.Stream) !void {
var server_hello: [0x1000]u8 = undefined;
const s_hello_read = try conn.read(&server_hello);
if (s_hello_read == 0) return error.InvalidSHello;
fn readServer(conn: std.net.Stream, server: []u8) !usize {
const s_read = try conn.read(server);
if (s_read == 0) return error.InvalidSHello;
 
const server_msg = server_hello[0..s_hello_read];
const server_msg = server[0..s_read];
if (false) print("server data: {any}\n", .{server_msg});
try std.testing.expect(s_hello_read > 7);
try std.testing.expect(s_read > 7);
return s_read;
}
 
fn buildServer(data: []const u8) !void {
@@ -308,6 +318,7 @@ fn buildServer(data: []const u8) !void {
var next_block: []const u8 = data;
 
while (next_block.len > 0) {
if (false) print("server block\n{any}\n", .{next_block});
const tlsr = try TLSRecord.unpack(next_block, &session);
if (false) print("mock {}\n", .{tlsr.length});
next_block = next_block[tlsr.length + 5 ..];
@@ -318,10 +329,12 @@ fn buildServer(data: []const u8) !void {
switch (hs.body) {
.server_hello => |hello| {
print("server hello {}\n", .{@TypeOf(hello)});
print("srv selected suite {any}\n", .{hello.cipher});
print("test selected suite {any}\n", .{CipherSuites.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256});
if (std.mem.eql(
u8,
&hello.cipher,
&CipherSuites.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
&CipherSuites.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
)) {
session.cipher.suite = .{ .ecc = .{} };
} else {
@@ -357,15 +370,50 @@ fn completeClient(conn: std.net.Stream) !void {
};
 
const cke_len = try cke_record.pack(&buffer);
try std.testing.expectEqual(43, cke_len);
try std.testing.expectEqual(42, cke_len);
print("CKE: {any}\n", .{buffer[0..cke_len]});
const ckeout = try conn.write(buffer[0..cke_len]);
if (true) print("cke delivered, {}\n", .{ckeout});
 
var r_buf: [0x1000]u8 = undefined;
var session = State{};
if (false) { // check for alerts
const num = try conn.read(&r_buf);
print("sin: {any}\n", .{r_buf[0..num]});
const sin = try TLSRecord.unpack(r_buf[0..num], &session);
print("server thing {}\n", .{sin});
}
 
const ccs_record = TLSRecord{
.kind = .{ .change_cipher_spec = {} },
};
const ccs_len = try ccs_record.pack(&buffer);
const ccsout = try conn.write(buffer[0..ccs_len]);
try std.testing.expectEqual(6, ccsout);
 
const fin = Handshake.Finished{};
//const fin_len = try fin.pack(&buffer);
const fin_record = TLSRecord{
.kind = .{
.handshake = try Handshake.Handshake.wrap(fin),
},
};
const fin_len = try fin_record.pack(&buffer);
print("fin: {any}\n", .{buffer[0..fin_len]});
const finout = try conn.write(buffer[0..fin_len]);
if (true) print("fin delivered, {}\n", .{finout});
 
const num2 = try conn.read(&r_buf);
print("sin: {any}\n", .{r_buf[0..num2]});
const sin2 = try TLSRecord.unpack(r_buf[0..num2], &session);
print("server thing {}\n", .{sin2});
}
 
fn fullHandshake(conn: std.net.Stream) !void {
try startHandshake(conn);
try getServer(conn);
var server: [0x1000]u8 = undefined;
const l = try readServer(conn, &server);
try buildServer(server[0..l]);
try completeClient(conn);
}
 
@@ -377,16 +425,25 @@ test "tls" {
const conn = try net.tcpConnectToAddress(addr);
 
try startHandshake(conn);
try getServer(conn);
var server: [0x1000]u8 = undefined;
const l = try readServer(conn, &server);
try buildServer(server[0..l]);
try completeClient(conn);
}
 
test "mock server response" {
if (true) return error.SkipZigTest;
// zig fmt: off
const server_data = [_]u8{
22, 3, 3, 0, 74,
2, 0, 0, 70,
3, 3, 75, 127, 236, 41, 6, 185, 127, 156, 38, 101, 41, 80, 93, 16, 140, 154, 60, 40, 250, 248, 115, 110, 115, 15, 68, 79, 87, 78, 71, 82, 68, 1, 32, 24, 187, 143, 225, 245, 127, 101, 130, 182, 200, 134, 201, 74, 38, 128, 15, 14, 35, 146, 216, 106, 109, 225, 72, 177, 41, 225, 227, 146, 101, 101, 10, 204, 169, 0,
3, 3, 75, 127, 236, 41, 6, 185, 127, 156, 38, 101,
41, 80, 93, 16, 140, 154, 60, 40, 250, 248, 115,
110, 115, 15, 68, 79, 87, 78, 71, 82, 68, 1, 32,
24, 187, 143, 225, 245, 127, 101, 130, 182, 200,
134, 201, 74, 38, 128, 15, 14, 35, 146, 216, 106,
109, 225, 72, 177, 41, 225, 227, 146, 101, 101,
10, 204, 169, 0,
22, 3, 3, 2, 64,
11, 0, 2, 60,
0, 2, 57, 0, 2, 54, 48, 130, 2, 50, 48,