srctree

Gregory Mullen parent e406f965 7a54a41d
move encrypt to cipher.zig

src/cipher.zig added: 140, removed: 77, total 63
@@ -14,6 +14,7 @@ const chacha_t = hscipherT(ChaCha20Poly1305, std.crypto.hash.sha2.Sha256);
const hkdfExpandLabel = std.crypto.tls.hkdfExpandLabel;
 
const ChaCha20 = ChaCha20Poly1305;
const Sha1 = std.crypto.auth.hmac.HmacSha1;
const Sha256 = std.crypto.auth.hmac.sha2.HmacSha256;
const Sha384 = std.crypto.auth.hmac.sha2.HmacSha384;
 
@@ -24,8 +25,89 @@ suite: union(enum) {
ecc: EllipticCurve,
aes: AnyAES,
} = .{ .invalid = {} },
key_xhg: KeyExchange = .{},
sequence: u72 = 0, // u72 is chacha20 specific :/
 
pub fn encrypt(c: *Cipher, clear_text: []const u8, cipher_text: []u8) !usize {
var l_clear: [0x1000]u8 = undefined;
@memcpy(l_clear[0..clear_text.len], clear_text);
switch (c.suite) {
.ecc => {
const empty: [0]u8 = undefined;
const encrypted_body = cipher_text;
std.crypto.aead.chacha_poly.ChaCha20Poly1305.encrypt(
encrypted_body,
encrypted_body[0..16],
clear_text,
&empty,
c.suite.ecc.material.cli_iv,
c.suite.ecc.material.cli_key,
);
return error.NotImplemented;
},
.aes => |aes| {
var len: usize = clear_text.len;
{
var mac_buf: [0x1000]u8 = undefined;
var mac_fba = fixedBufferStream(&mac_buf);
var mac_w = mac_fba.writer().any();
try mac_w.writeInt(u64, @truncate(c.sequence), .big);
try mac_w.writeAll(&[_]u8{ 22, 3, 3 });
try mac_w.writeInt(u16, @truncate(len), .big);
try mac_w.writeAll(clear_text);
const mac_len = try mac_fba.getPos();
 
const mac_out: *[48]u8 = l_clear[len..][0..48];
const mac_text = mac_buf[0..mac_len];
std.crypto.auth.hmac.sha2.HmacSha384.create(mac_out, mac_text, &aes.material.cli_mac);
len += 48;
}
 
var aes_ctx = std.crypto.core.aes.Aes256.initEnc(aes.material.cli_key);
const add = 16 - (len % 16);
if (add != 0) {
@memset(l_clear[len..][0..add], @truncate(add - 1));
}
len += add;
if (cipher_text.len < len + aes.material.cli_iv.len)
return error.NoSpaceLeft;
var fba = fixedBufferStream(cipher_text);
var w = fba.writer().any();
try w.writeAll(aes.material.cli_iv[0..]);
 
var xord: [16]u8 = aes.material.cli_iv;
for (0..len / 16) |i| {
var clear: [16]u8 = l_clear[i * 16 ..][0..16].*;
var cipher: [16]u8 = undefined;
var xclear: [16]u8 = undefined;
for (xclear[0..], clear[0..], xord[0..]) |*xc, cl, xr| xc.* = cl ^ xr;
aes_ctx.encrypt(cipher[0..], xclear[0..]);
@memcpy(xord[0..16], cipher[0..16]);
try w.writeAll(cipher[0..]);
}
return try fba.getPos();
},
else => return error.SuiteNotImplmented,
}
comptime unreachable;
}
 
pub fn decrypt(c: *Cipher, cipher_text: []const u8, clear: []u8) ![]const u8 {
_ = cipher_text;
switch (c.suite) {
.ecc => |ecc| {
_ = ecc;
},
.aes => |aes| {
_ = aes;
},
else => unreachable,
}
return clear;
}
 
pub const KeyExchange = struct {};
 
pub fn Material(comptime ENC: anytype, comptime HMAC: anytype) type {
return struct {
pub const Self = @This();
@@ -37,9 +119,6 @@ pub fn Material(comptime ENC: anytype, comptime HMAC: anytype) type {
 
pub const PRF = Sha384;
 
//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,
@@ -160,6 +239,16 @@ pub const Suites = enum(u16) {
else => unreachable,
};
}
 
pub fn toType(s: Suites) type {
return switch (s) {
.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 => Material(AES(256, CBC), Sha384),
.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => Material(AES(256, CBC), Sha1),
.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => Material(ECC, Sha256),
.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => Material(ECC, Sha256),
else => comptime unreachable,
};
}
};
 
pub const AnyAES = struct {
@@ -364,6 +453,13 @@ pub const ExplicitPrime = struct {};
pub const ExplicitChar2 = struct {};
pub const NamedCurve = struct {};
 
pub const ECC = struct {
pub const BIT_SIZE = 265;
pub const MODE = ChaCha20;
pub const key_length = ChaCha20.key_length;
pub const nonce_length = ChaCha20.nonce_length;
};
 
pub const EllipticCurve = struct {
curve: Curves = .{ .invalid = {} },
 
 
src/context.zig added: 140, removed: 77, total 63
@@ -14,6 +14,7 @@ const SessionID = root.SessionID;
 
pub const ConnCtx = @This();
 
session_encrypted: bool = false,
cipher: Cipher = .{},
cli_random: ?[32]u8 = null,
srv_random: ?[32]u8 = null,
 
src/handshake.zig added: 140, removed: 77, total 63
@@ -288,7 +288,7 @@ pub const Type = enum(u8) {
15 => .certificate_verify,
16 => .client_key_exchange,
20 => .finished,
else => unreachable,
else => return error.InvalidHandshakeType,
};
}
 
 
src/root.zig added: 140, removed: 77, total 63
@@ -35,13 +35,14 @@ const ContentType = enum(u8) {
 
pub const TLSRecord = struct {
version: Protocol.Version = Protocol.TLSv1_2,
/// Length is the Header + Fragment.len
length: u16 = 0,
kind: union(ContentType) {
change_cipher_spec: void, // This is const packet
alert: Alert,
handshake: Handshake.Handshake,
application_data: []const u8,
},
} = undefined,
 
pub fn packFragment(record: TLSRecord, buffer: []u8, ctx: *ConnCtx) !usize {
return switch (record.kind) {
@@ -67,69 +68,19 @@ pub const TLSRecord = struct {
}
 
pub fn encrypt(record: TLSRecord, buffer: []u8, ctx: *ConnCtx) !usize {
var fba = fixedBufferStream(buffer);
try fba.seekBy(5); // Record header
var w = fba.writer().any();
var clear: [0x1000]u8 = undefined;
const len = try record.packFragment(&clear, ctx);
 
var clear_text: [0x1000]u8 = undefined;
var len = try record.packFragment(&clear_text, ctx);
 
switch (ctx.cipher.suite) {
.ecc => {
const empty: [0]u8 = undefined;
const encrypted_body = buffer[5..];
std.crypto.aead.chacha_poly.ChaCha20Poly1305.encrypt(
encrypted_body[0..len],
encrypted_body[len..][0..16],
clear_text[0..len],
&empty,
ctx.cipher.suite.ecc.material.cli_iv,
ctx.cipher.suite.ecc.material.cli_key,
);
},
.aes => |aes| {
{
var mac_buf: [0x200]u8 = undefined;
var mac_fba = fixedBufferStream(&mac_buf);
var mac_w = mac_fba.writer().any();
try mac_w.writeInt(u64, @truncate(ctx.cipher.sequence), .big);
try mac_w.writeAll(&[_]u8{ 22, 3, 3 });
try mac_w.writeInt(u16, @truncate(len), .big);
try mac_w.writeAll(clear_text[0..len]);
const mac_len = try mac_fba.getPos();
 
const mac_out: *[48]u8 = clear_text[len..][0..48];
const mac_text = mac_buf[0..mac_len];
std.crypto.auth.hmac.sha2.HmacSha384.create(mac_out, mac_text, &aes.material.cli_mac);
len += 48;
}
 
var aes_ctx = std.crypto.core.aes.Aes256.initEnc(aes.material.cli_key);
const add = 16 - (len % 16);
if (add != 0) {
@memset(clear_text[len..][0..add], @truncate(add - 1));
}
len += add;
try w.writeAll(aes.material.cli_iv[0..]);
 
var xord: [16]u8 = aes.material.cli_iv;
for (0..len / 16) |i| {
var clear: [16]u8 = clear_text[i * 16 ..][0..16].*;
var cipher: [16]u8 = undefined;
var xclear: [16]u8 = undefined;
for (xclear[0..], clear[0..], xord[0..]) |*xc, c, xr| xc.* = c ^ xr;
aes_ctx.encrypt(cipher[0..], xclear[0..]);
@memcpy(xord[0..16], cipher[0..16]);
try w.writeAll(cipher[0..]);
}
},
else => unreachable,
}
const enc_len = try fba.getPos() - 5;
const enc_len = try ctx.cipher.encrypt(clear[0..len], buffer[5..]);
return try record.packHeader(buffer, enc_len);
}
 
pub fn unpackFragment(buffer: []const u8, sess: *ConnCtx) !TLSRecord {
pub fn decryptFragment(cipher: []const u8, clear: []u8, ctx: *ConnCtx) ![]const u8 {
if (true) unreachable;
return try ctx.cipher.decrypt(cipher, clear);
}
 
pub fn unpackFragment(buffer: []const u8, ctx: *ConnCtx) !TLSRecord {
var fba = fixedBufferStream(buffer);
var r = fba.reader().any();
 
@@ -140,24 +91,31 @@ pub const TLSRecord = struct {
};
const length = try r.readInt(u16, std.builtin.Endian.big);
 
var decrypted: [0x1000]u8 = undefined;
if (length > buffer[5..].len) return error.IncompleteFragment;
const fragbuff = buffer[5..][0..length];
const fragbuf: []const u8 = if (ctx.session_encrypted)
try decryptFragment(buffer[5..][0..length], decrypted[0..length], ctx)
else
buffer[5..][0..length];
 
return .{
.version = version,
.length = length,
.length = length + 5, // Record header size
.kind = switch (fragtype) {
.change_cipher_spec => .{
.change_cipher_spec = if (fragbuff[0] != 1) return error.InvalidCCSPacket else {},
.change_cipher_spec = if (fragbuf[0] != 1) return error.InvalidCCSPacket else {},
},
.alert => .{ .alert = try Alert.unpack(fragbuf) },
.handshake => .{
.handshake = try Handshake.Handshake.unpack(fragbuf, ctx),
},
.alert => .{ .alert = try Alert.unpack(fragbuff) },
.handshake => .{ .handshake = try Handshake.Handshake.unpack(fragbuff, sess) },
.application_data => .{ .application_data = unreachable },
},
};
}
pub fn unpack(buffer: []const u8, sess: *ConnCtx) !TLSRecord {
return try unpackFragment(buffer, sess);
 
pub fn unpack(buffer: []const u8, ctx: *ConnCtx) !TLSRecord {
return try unpackFragment(buffer, ctx);
}
};
 
@@ -233,7 +191,7 @@ fn buildServer(data: []const u8, ctx: *ConnCtx) !void {
if (false) print("server block\n{any}\n", .{next_block});
const tlsr = try TLSRecord.unpack(next_block, ctx);
 
next_block = next_block[tlsr.length + 5 ..];
next_block = next_block[tlsr.length..];
 
switch (tlsr.kind) {
.change_cipher_spec, .alert, .application_data => return error.UnexpectedResponse,
@@ -322,7 +280,15 @@ fn completeClient(conn: std.net.Stream, ctx: *ConnCtx) !void {
const num2 = try conn.read(&r_buf);
if (false) print("sin: {any}\n", .{r_buf[0..num2]});
const sin2 = try TLSRecord.unpack(r_buf[0..num2], ctx);
if (false) print("server thing {}\n", .{sin2});
if (true) print("server thing {}\n", .{sin2});
 
if (num2 > sin2.length) {
const n_buf = r_buf[sin2.length..];
const sin3 = try TLSRecord.unpack(n_buf, ctx);
if (true) print("server thing {}\n", .{sin3});
} else {
//const num3 = try conn.read(&r_buf);
}
 
ctx.raze();
}