srctree

Gregory Mullen parent 15aaa251 2f5a639a
can recieve and pretend to parse a server handshake

src/root.zig added: 335, removed: 150, total 185
@@ -35,7 +35,6 @@ const ContentType = enum(u8) {
application_data = 23,
 
pub fn fromByte(from: u8) !ContentType {
print("from {}\n", .{from});
return switch (from) {
20 => .change_cipher_spec,
21 => .alert,
@@ -74,31 +73,51 @@ const TLSRecord = struct {
return record.packFragment(buffer);
}
 
pub fn unpackFragment(buffer: []const u8, sess: SessionState) !TLSRecord {
pub fn unpackFragment(buffer: []const u8, sess: *SessionState) !TLSRecord {
var fba = fixedBufferStream(buffer);
var r = fba.reader().any();
 
const fragtype = try ContentType.fromByte(try r.readByte());
const version = ProtocolVersion{
.major = try r.readByte(),
.minor = try r.readByte(),
};
const length = try r.readInt(u16, std.builtin.Endian.big);
 
if (length > buffer[5..].len) return error.IncompleteFragment;
const fragbuff = buffer[5..][0..length];
 
return .{
.version = .{
.major = try r.readByte(),
.minor = try r.readByte(),
},
.length = try r.readInt(u16, std.builtin.Endian.big),
.version = version,
.length = length,
.kind = switch (fragtype) {
.change_cipher_spec => .{ .change_cipher_spec = unreachable },
.alert => .{ .alert = try Alert.unpack(buffer[5..]) },
.handshake => .{ .handshake = try Handshake.unpack(buffer[5..], sess) },
.alert => .{ .alert = try Alert.unpack(fragbuff) },
.handshake => .{ .handshake = try Handshake.unpack(fragbuff, sess) },
.application_data => .{ .application_data = unreachable },
},
};
}
pub fn unpack(buffer: []const u8, sess: SessionState) !TLSRecord {
pub fn unpack(buffer: []const u8, sess: *SessionState) !TLSRecord {
return try unpackFragment(buffer, sess);
}
};
 
pub const Random = extern struct {
unix_time: u32,
random_bytes: [28]u8,
};
 
comptime {
std.debug.assert(@sizeOf(Random) == 32);
}
 
pub const SessionID = [32]u8;
 
pub const Compression = enum(u8) {
null = 0,
};
 
pub const ClientHello = struct {
version: ProtocolVersion,
random: Random,
@@ -140,19 +159,6 @@ pub const ClientHello = struct {
};
pub const length = @sizeOf(ClientHello);
 
pub const SessionID = [32]u8;
pub const Compression = enum(u8) {
null = 0,
};
 
pub const Random = extern struct {
unix_time: u32,
random_bytes: [28]u8,
};
comptime {
std.debug.assert(@sizeOf(Random) == 32);
}
 
pub fn init() ClientHello {
var hello = ClientHello{
.version = .{ .major = 3, .minor = 3 },
@@ -202,7 +208,7 @@ pub const ClientHello = struct {
return fba.pos;
}
 
pub fn unpack(buffer: []const u8, _: SessionState) !ClientHello {
pub fn unpack(buffer: []const u8, _: *SessionState) !ClientHello {
_ = buffer;
unreachable;
}
@@ -212,26 +218,25 @@ const RecordProto = struct {};
const ConnectionEnd = struct {};
const PRFAlgorithm = struct {};
const BulkCipherAlgorithm = struct {};
//const CipherType = struct {};
const MACAlgorithm = struct {};
const CompressionMethod = struct {};
 
const SessionState = struct {
entity: ConnectionEnd,
prf_algorithm: PRFAlgorithm,
bulk_cipher_algorithm: BulkCipherAlgorithm,
cipher_type: CipherType,
enc_key_length: u8,
block_length: u8,
fixed_iv_length: u8,
record_iv_length: u8,
mac_algorithm: MACAlgorithm,
mac_length: u8,
mac_key_length: u8,
compression_algorithm: CompressionMethod,
master_secret: [48]u8,
client_random: [32]u8,
server_random: [32]u8,
entity: ConnectionEnd = .{},
prf_algorithm: PRFAlgorithm = .{},
bulk_cipher_algorithm: BulkCipherAlgorithm = .{},
cipher_type: ?CipherType = null,
enc_key_length: u8 = 0,
block_length: u8 = 0,
fixed_iv_length: u8 = 0,
record_iv_length: u8 = 0,
mac_algorithm: MACAlgorithm = .{},
mac_length: u8 = 0,
mac_key_length: u8 = 0,
compression_algorithm: CompressionMethod = .{},
master_secret: [48]u8 = std.mem.zeroes([48]u8),
client_random: [32]u8 = std.mem.zeroes([32]u8),
server_random: [32]u8 = std.mem.zeroes([32]u8),
};
 
//
@@ -248,38 +253,6 @@ const SessionState = struct {
// application layer traffic during handshakes subsequent to the first
// one on a connection.
 
const TLSPlaintext = struct {
pub fn init(frag: anytype) TLSPlaintext {
switch (@TypeOf(frag)) {
ClientHello => {
return .{
.type = .handshake,
.length = @TypeOf(frag).length,
.fragment = .{ .client_handshake = frag },
};
},
else => comptime unreachable,
}
}
 
pub fn send(plain: TLSPlaintext, w: *std.io.AnyWriter) !void {
var buffer: [0xfff]u8 = undefined;
var fba = std.io.fixedBufferStream(&buffer);
var frag = fba.writer().any();
 
switch (plain.fragment) {
.client_hello => try frag.write(plain.fragment),
else => unreachable,
}
const fragment = fba.getWritten();
_ = try w.write(&[1].{plain.type});
_ = try w.write(plain.version);
std.debug.assert(fragment.len < 0xFFFF);
try w.writeInt(u16, @truncate(fragment.len), std.builtin.Endian.big);
try w.writeAll(fragment);
}
};
 
const CipherType = enum {
stream,
block,
@@ -338,11 +311,11 @@ const HandshakeType = enum(u8) {
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,
.server_key_exchange,
.certificate_request,
.server_hello_done,
.certificate_verify,
.client_key_exchange,
.finished,
@@ -353,14 +326,109 @@ const HandshakeType = enum(u8) {
};
 
const ServerHello = struct {
pub fn unpack(_: []const u8, _: SessionState) !ServerHello {
unreachable;
version: ProtocolVersion,
random: Random,
session_id: SessionID,
ciphers: [1]CipherSuite,
compression: Compression,
extensions: []const Extension,
 
pub fn unpack(buffer: []const u8, _: *SessionState) !ServerHello {
var fba = fixedBufferStream(buffer);
const r = fba.reader().any();
 
const version = ProtocolVersion{
.major = try r.readByte(),
.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);
 
const session_size = try r.readByte();
var session_id: [32]u8 = [_]u8{0} ** 32;
try r.readNoEof(session_id[0..session_size]);
 
// cipers
//const cbytes: u16 = try r.readInt(u16, std.builtin.Endian.big);
const cipher: [1][2]u8 = [1][2]u8{.{
try r.readByte(),
try r.readByte(),
}};
 
// compression
if (try r.readByte() != 0) return error.InvalidCompression;
 
// extensions
if (r.readInt(u16, std.builtin.Endian.big)) |extbytes| {
var extbuffer: [0x1000]u8 = undefined;
try r.readNoEof(extbuffer[0..extbytes]);
} else |err| switch (err) {
error.EndOfStream => print("server hello readerror {}\n", .{err}),
else => return err,
}
 
return .{
.version = version,
.random = random,
.session_id = session_id,
.ciphers = cipher,
.compression = .null,
.extensions = &[0]Extension{},
};
}
};
const Certificate = struct {};
const ServerKeyExchange = struct {};
const CertificateRequest = struct {};
const ServerHelloDone = struct {};
 
const Certificate = struct {
buffer: []const u8,
session: *SessionState,
 
pub fn unpack(buffer: []const u8, sess: *SessionState) !Certificate {
return .{
.buffer = buffer,
.session = sess,
};
}
};
 
const ServerKeyExchange = struct {
buffer: []const u8,
session: *SessionState,
 
pub fn unpack(buffer: []const u8, sess: *SessionState) !ServerKeyExchange {
return .{
.buffer = buffer,
.session = sess,
};
}
};
 
const CertificateRequest = struct {
buffer: []const u8,
session: *SessionState,
 
pub fn unpack(buffer: []const u8, sess: *SessionState) !CertificateRequest {
return .{
.buffer = buffer,
.session = sess,
};
}
};
 
const ServerHelloDone = struct {
buffer: []const u8,
session: *SessionState,
 
pub fn unpack(buffer: []const u8, sess: *SessionState) !ServerHelloDone {
return .{
.buffer = buffer,
.session = sess,
};
}
};
 
const CertificateVerify = struct {};
const ClientKeyExchange = struct {};
const Finished = struct {};
@@ -373,28 +441,15 @@ const Handshakes = union(HandshakeType) {
hello_request: void,
client_hello: ClientHello,
server_hello: ServerHello,
certificate: void,
server_key_exchange: void,
certificate_request: void,
server_hello_done: void,
certificate: Certificate,
server_key_exchange: ServerKeyExchange,
certificate_request: CertificateRequest,
server_hello_done: ServerHelloDone,
certificate_verify: void,
client_key_exchange: void,
finished: void,
};
 
// {
// .hello_request => HelloRequest,
// .client_hello => ClientHello,
// .server_hello => ServerHello,
// .certificate => Certificate,
// .server_key_exchange => ServerKeyExchange,
// .certificate_request => CertificateRequest,
// .server_hello_done => ServerHelloDone,
// .certificate_verify => CertificateVerify,
// .client_key_exchange => ClientKeyExchange,
// .finished => Finished,
// },
 
const Handshake = struct {
msg_type: HandshakeType,
_length: u24 = 0, // unused
@@ -427,13 +482,18 @@ const Handshake = struct {
return len + 4;
}
 
pub fn unpack(buffer: []const u8, sess: SessionState) !Handshake {
pub fn unpack(buffer: []const u8, sess: *SessionState) !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(buffer[4..], sess) },
.server_hello => .{ .server_hello = try ServerHello.unpack(buffer[4..], sess) },
.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,
},
};
@@ -463,50 +523,6 @@ const SignatureAndHashAlgorithm = struct {
signature: SignatureAlgorithm,
};
 
fn tlsHandshake(conn: net.Stream) !void {
var buffer = [_]u8{0} ** 0x400;
const client_handshake = ClientHello.init();
const record = TLSRecord{
.kind = .{
.handshake = try Handshake.wrap(client_handshake),
},
};
 
const len = try record.pack(&buffer);
const dout = try conn.write(buffer[0..len]);
_ = dout;
 
//print("data count {}\n", .{dout});
if (true) print("data out {any}\n", .{buffer[0..len]});
var server_hello: [0xff]u8 = undefined;
const s_hello_read = try conn.read(&server_hello);
if (s_hello_read == 0) return error.InvalidSHello;
 
const server_msg = server_hello[0..s_hello_read];
 
const session: SessionState = undefined;
 
print("server data: {any}\n", .{server_msg});
const tlsr = try TLSRecord.unpack(server_msg, session);
 
switch (tlsr.kind) {
.alert => |a| {
try std.testing.expect(a.description != .decode_error);
print("ALERT {}\n", .{a});
},
else => |na| print("non alert message {}\n", .{na}),
}
 
print("server data: {any}\n", .{tlsr});
 
//try conn.writeAll(&client_fin);
try std.testing.expect(s_hello_read > 7);
}
 
fn tls(conn: net.Stream) !void {
try tlsHandshake(conn);
}
 
test "Handshake ClientHello" {
var buffer = [_]u8{0} ** 0x400;
const client_hello = ClientHello.init();
@@ -525,8 +541,177 @@ test "tls" {
print("unable to resolve address because {}\n", .{err});
return err;
};
const con = try net.tcpConnectToAddress(addr);
try tls(con);
const conn = try net.tcpConnectToAddress(addr);
 
var buffer = [_]u8{0} ** 0x1000;
const client_handshake = ClientHello.init();
const record = TLSRecord{
.kind = .{
.handshake = try Handshake.wrap(client_handshake),
},
};
 
const len = try record.pack(&buffer);
const dout = try conn.write(buffer[0..len]);
_ = dout;
 
//print("data count {}\n", .{dout});
if (false) print("data out {any}\n", .{buffer[0..len]});
var server_hello: [0x1000]u8 = undefined;
const s_hello_read = try conn.read(&server_hello);
if (s_hello_read == 0) return error.InvalidSHello;
 
const server_msg = server_hello[0..s_hello_read];
if (false) print("server data: {any}\n", .{server_msg});
 
//const session: SessionState = undefined;
 
//const tlsr = try TLSRecord.unpack(server_msg, session);
 
//switch (tlsr.kind) {
// .alert => |a| {
// try std.testing.expect(a.description != .decode_error);
// print("ALERT {}\n", .{a});
// },
// else => |na| print("non alert message {}\n", .{na}),
//}
 
//if (false) print("server data: {any}\n", .{tlsr});
 
//try conn.writeAll(&client_fin);
try std.testing.expect(s_hello_read > 7);
}
 
test "mock server response" {
// 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,
22, 3, 3, 2, 64,
11, 0, 2, 60,
0, 2, 57, 0, 2, 54, 48, 130, 2, 50, 48,
130, 1, 184, 160, 3, 2, 1, 2, 2, 20, 104,
254, 206, 87, 13, 243, 60, 147, 173, 38,
100, 66, 220, 129, 46, 24, 54, 59, 37,
114, 48, 10, 6, 8, 42, 134, 72, 206,
61, 4, 3, 2, 48, 80, 49, 11, 48, 9, 6,
3, 85, 4, 6, 19, 2, 85, 83, 49, 19, 48,
17, 6, 3, 85, 4, 8, 12, 10, 83, 111, 109,
101, 45, 83, 116, 97, 116, 101, 49, 13,
48, 11, 6, 3, 85, 4, 10, 12, 4, 110, 117,
108, 108, 49, 13, 48, 11, 6, 3, 85, 4,
11, 12, 4, 110, 117, 108, 108, 49, 14, 48,
12, 6, 3, 85, 4, 3, 12, 5, 116, 111, 119,
101, 114, 48, 30, 23, 13, 50, 51, 49, 48,
49, 48, 48, 49, 53, 53, 53, 55, 90, 23,
13, 50, 54, 49, 48, 48, 57, 48, 49, 53,
53, 53, 55, 90, 48, 80, 49, 11, 48, 9, 6,
3, 85, 4, 6, 19, 2, 85, 83, 49, 19, 48, 17,
6, 3, 85, 4, 8, 12, 10, 83, 111, 109, 101,
45, 83, 116, 97, 116, 101, 49, 13, 48, 11,
6, 3, 85, 4, 10, 12, 4, 110, 117, 108, 108,
49, 13, 48, 11, 6, 3, 85, 4, 11, 12, 4, 110,
117, 108, 108, 49, 14, 48, 12, 6, 3, 85, 4,
3, 12, 5, 116, 111, 119, 101, 114, 48, 118,
48, 16, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6,
5, 43, 129, 4, 0, 34, 3, 98, 0, 4, 198, 26,
57, 146, 22, 113, 142, 223, 208, 19, 112, 123,
87, 194, 131, 225, 118, 55, 148, 113, 62, 196,
158, 171, 34, 127, 140, 90, 235, 113, 221, 120,
251, 141, 38, 234, 151, 224, 155, 183, 70, 192,
129, 180, 116, 145, 238, 132, 71, 21, 10, 72,
24, 104, 229, 26, 155, 129, 59, 117, 223, 165,
200, 108, 233, 105, 103, 2, 98, 118, 249, 192,
114, 123, 82, 234, 152, 130, 17, 91, 38, 234,
87, 243, 52, 240, 72, 160, 35, 196, 25, 129, 201,
97, 112, 57, 163, 83, 48, 81, 48, 29, 6, 3,
85, 29, 14, 4, 22, 4, 20, 48, 133, 244, 80,
200, 17, 185, 240, 93, 162, 90, 225, 53, 62,
231, 234, 47, 38, 117, 207, 48, 31, 6, 3,
85, 29, 35, 4, 24, 48, 22, 128, 20, 48, 133,
244, 80, 200, 17, 185, 240, 93, 162, 90,
225, 53, 62, 231, 234, 47, 38, 117, 207, 48,
15, 6, 3, 85, 29, 19, 1, 1, 255, 4, 5, 48, 3,
1, 1, 255, 48, 10, 6, 8, 42, 134, 72, 206, 61,
4, 3, 2, 3, 104, 0, 48, 101, 2, 48, 9, 248,
206, 200, 89, 165, 36, 63, 181, 5, 145, 31,
138, 207, 12, 223, 57, 189, 65, 152, 99, 77,
182, 90, 47, 152, 227, 196, 144, 166, 147, 97,
44, 115, 138, 229, 212, 211, 251, 38, 221, 161,
225, 1, 88, 215, 83, 179, 2, 49, 0, 130, 255,
220, 17, 165, 227, 46, 238, 140, 122, 122, 18,
139, 47, 181, 191, 223, 154, 13, 62, 100, 0,
83, 84, 100, 92, 116, 99, 214, 144, 164, 102,
191, 190, 74, 81, 133, 80, 68, 166, 135, 147,
190, 189, 222, 192, 200, 113,
22, 3, 3, 0, 146,
12, 0, 0, 142,
3, 0, 29, 32, 79, 189, 85, 166,
190, 168, 238, 144, 215, 56, 187, 13, 64, 120,
156, 134, 33, 146, 225, 213, 111, 241, 251, 212,
105, 110, 68, 224, 176, 203, 101, 109, 4, 3, 0,
102, 48, 100, 2, 48, 109, 172, 43, 76, 16, 12,
149, 10, 199, 222, 137, 93, 199, 188, 15, 217,
191, 67, 9, 108, 228, 27, 224, 5, 85, 161, 180,
167, 89, 54, 134, 99, 44, 174, 71, 201, 99, 10,
175, 46, 207, 252, 5, 47, 74, 248, 80, 185, 2,
48, 25, 189, 150, 104, 207, 122, 9, 91, 186,
103, 172, 61, 128, 204, 245, 193, 119, 30, 202,
187, 75, 203, 45, 40, 49, 6, 51, 234, 98, 94,
191, 167, 187, 71, 12, 181, 172, 187, 203, 71,
91, 167, 76, 160, 224, 48, 135, 35,
22, 3, 3, 0, 142,
13, 0, 0, 138,
3, 1, 2, 64, 0, 46, 4, 3, 5, 3, 6, 3, 8, 7, 8, 8,
8, 26, 8, 27, 8, 28, 8, 9, 8, 10, 8, 11, 8, 4, 8,
5, 8, 6, 4, 1, 5, 1, 6, 1, 3, 3, 3, 1, 3, 2, 4, 2,
5, 2, 6, 2, 0, 84, 0, 82, 48, 80, 49, 11, 48, 9,
6, 3, 85, 4, 6, 19, 2, 85, 83, 49, 19, 48, 17, 6,
3, 85, 4, 8, 12, 10, 83, 111, 109, 101, 45, 83,
116, 97, 116, 101, 49, 13, 48, 11, 6, 3, 85, 4,
10, 12, 4, 110, 117, 108, 108, 49, 13, 48, 11, 6,
3, 85, 4, 11, 12, 4, 110, 117, 108, 108, 49, 14,
48, 12, 6, 3, 85, 4, 3, 12, 5, 116, 111, 119, 101, 114,
22, 3, 3, 0, 4,
14, 0, 0, 0
 
 
};
// zig fmt: on
 
var session = SessionState{};
var next_block: []const u8 = &server_data;
 
while (next_block.len > 0) {
const tlsr = try TLSRecord.unpack(next_block, &session);
print("mock {}\n", .{tlsr.length});
next_block = next_block[tlsr.length + 5 ..];
 
switch (tlsr.kind) {
.change_cipher_spec, .alert, .application_data => return error.UnexpectedResponse,
.handshake => |hs| {
switch (hs.body) {
.server_hello => |hello| {
print("server hello {}\n", .{@TypeOf(hello)});
},
.certificate => |cert| {
print("server cert {}\n", .{@TypeOf(cert)});
},
.server_key_exchange => |keyex| {
print("server keyex {}\n", .{@TypeOf(keyex)});
},
.certificate_request => |req| {
print("server req {}\n", .{@TypeOf(req)});
},
.server_hello_done => |done| {
print("server done {}\n", .{@TypeOf(done)});
},
else => return error.UnexpectedHandshake,
}
},
}
}
}
 
const CipherSuite = [2]u8;
@@ -631,7 +816,7 @@ const CipherSuites = struct {
// TLS_DH_anon_WITH_AES_128_CBC_SHA DH_anon AES_128_CBC SHA
// TLS_DH_DSS_WITH_AES_256_CBC_SHA DH_DSS AES_256_CBC SHA
// TLS_DH_RSA_WITH_AES_256_CBC_SHA DH_RSA AES_256_CBC SHA
// TLS_DHE_DSS_WITH_AES_256_CBC_SHA DHE_DSS AES_256_CBC SHA
// TLS_DHE_DSS_WITH_AES_256_CBC_SHA DHE_DSS AE4, 3, 2, 3S_256_CBC SHA
// TLS_DHE_RSA_WITH_AES_256_CBC_SHA DHE_RSA AES_256_CBC SHA
// TLS_DH_anon_WITH_AES_256_CBC_SHA DH_anon AES_256_CBC SHA
// TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256