srctree

Gregory Mullen parent 6cd1daca 64eddc2a
move probe into it's own step `zig build probe`

build.zig added: 160, removed: 115, total 45
@@ -12,10 +12,14 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
});
 
const nihssl = b.addModule("nihssl", .{
.root_source_file = .{ .path = "src/root.zig" },
.target = target,
.optimize = optimize,
});
 
b.installArtifact(lib);
 
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.root_source_file = .{ .path = "src/root.zig" },
.target = target,
@@ -24,9 +28,17 @@ pub fn build(b: *std.Build) void {
 
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
 
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
 
const probe = b.addExecutable(.{
.name = "probe",
.root_source_file = .{ .path = "utils/probe.zig" },
.target = target,
.optimize = optimize,
});
probe.root_module.addImport("nihssl", nihssl);
const run_probe = b.addRunArtifact(probe);
const probe_step = b.step("probe", "Probe hosts for supported suites");
probe_step.dependOn(&run_probe.step);
}
 
src/cipher.zig added: 160, removed: 115, total 45
@@ -472,6 +472,8 @@ pub const EllipticCurve = struct {
const r = fba.reader().any();
const name = try r.readInt(u16, .big);
if (name != 0x001d) return error.UnknownCurveName;
const key_len = try r.readByte();
std.debug.assert(key_len == 32);
ctx.cipher.suite.ecc.srv_dh = undefined;
const peer_key = &ctx.cipher.suite.ecc.srv_dh.?.public_key;
try r.readNoEof(peer_key);
 
src/extensions.zig added: 160, removed: 115, total 45
@@ -137,18 +137,18 @@ pub const SignatureAlgorithms = struct {
//SignatureAlgorithm; enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), }
 
const testable = [_]u16{
0x0101,
0x0102,
0x0201,
0x0202,
0x0203,
0x0204,
0x0205,
0x0301,
0x0302,
0x0303,
0x0304,
0x0305,
//0x0101,
//0x0102,
//0x0201,
//0x0202,
//0x0203,
//0x0204,
//0x0205,
//0x0301,
//0x0302,
//0x0303,
//0x0304,
//0x0305,
0x0401,
0x0402,
0x0403,
 
src/handshake.zig added: 160, removed: 115, total 45
@@ -207,7 +207,7 @@ pub const ServerHello = struct {
ctx.cipher.suite = .{ .aes = undefined };
},
//else => ctx.cipher.suite = .{ .ecc = undefined },
else => unreachable,
else => return error.UnsupportedSuite,
}
 
// compression
 
src/root.zig added: 160, removed: 115, total 45
@@ -8,13 +8,13 @@ const asBytes = std.mem.asBytes;
const TESTING_IP = "127.0.0.1";
const TESTING_PORT = 4433;
 
const Alert = @import("alert.zig");
pub const Alert = @import("alert.zig");
const Extensions = @import("extensions.zig");
const Extension = Extensions.Extension;
const Protocol = @import("protocol.zig");
const ConnCtx = @import("context.zig");
const Handshake = @import("handshake.zig");
const Cipher = @import("cipher.zig");
pub const Protocol = @import("protocol.zig");
pub const ConnCtx = @import("context.zig");
pub const Handshake = @import("handshake.zig");
pub const Cipher = @import("cipher.zig");
 
const PRINT_DEBUG = true;
 
@@ -35,7 +35,7 @@ const ContentType = enum(u8) {
}
};
 
const TLSRecord = struct {
pub const TLSRecord = struct {
version: Protocol.Version = Protocol.TLSv1_2,
length: u16 = 0,
kind: union(ContentType) {
@@ -69,15 +69,16 @@ const TLSRecord = struct {
}
 
pub fn encrypt(record: TLSRecord, buffer: []u8, ctx: *ConnCtx) !usize {
var clear_buffer: [0x1000]u8 = undefined;
var len = try record.packFragment(&clear_buffer, ctx);
var fba = fixedBufferStream(buffer);
try fba.seekBy(5); // Record header
var w = fba.writer().any();
 
var clear_text: [0x1000]u8 = undefined;
var len = try record.packFragment(&clear_text, ctx);
 
print("clear buffer {}\n", .{
std.fmt.fmtSliceHexLower(clear_buffer[0..len]),
std.fmt.fmtSliceHexLower(clear_text[0..len]),
});
//print("material iv {any}\n", .{ctx.cipher.suite.ecc.material.cli_iv});
//for (buffer[5..][0..12], ctx.cipher.suite.ecc.material.cli_iv, asBytes(&ctx.cipher.sequence)[4..]) |*dst, iv, seq|
// dst.* = iv ^ seq;
 
switch (ctx.cipher.suite) {
.ecc => {
@@ -86,53 +87,51 @@ const TLSRecord = struct {
std.crypto.aead.chacha_poly.ChaCha20Poly1305.encrypt(
encrypted_body[0..len],
encrypted_body[len..][0..16],
clear_buffer[0..len],
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 fba = fixedBufferStream(&mac_buf);
var w = fba.writer().any();
try w.writeInt(u64, @truncate(ctx.cipher.sequence), .big);
try w.writeAll(clear_buffer[0..len]);
{
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(clear_text[0..len]);
 
const mac_out: *[48]u8 = clear_buffer[len..][0..48];
std.crypto.auth.hmac.sha2.HmacSha384.create(mac_out, mac_buf[0 .. 8 + len], &aes.material.cli_mac);
print("mac buf {any}\n", .{mac_buf[0 .. 8 + len]});
len += 48;
const mac_out: *[48]u8 = clear_text[len..][0..48];
const mac_text = mac_buf[0 .. 8 + len];
std.crypto.auth.hmac.sha2.HmacSha384.create(mac_out, mac_text, &aes.material.cli_mac);
print("mac buf {any}\n", .{mac_text});
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_buffer[len..][0..add], 0);
@memset(clear_text[len..][0..add], 0);
}
len += add;
@memcpy(buffer[5..][0..16], aes.material.cli_iv[0..]);
const encrypted_body = buffer[5..][16..];
try w.writeAll(aes.material.cli_iv[0..]);
 
var xord: [16]u8 = aes.material.cli_iv;
print("xor pre {any}\n", .{xord});
for (0..len / 16) |i| {
var clear: [16]u8 = clear_buffer[i * 16 ..][0..16].*;
const cipher: *[16]u8 = encrypted_body[i * 16 ..][0..16];
var clear: [16]u8 = clear_text[i * 16 ..][0..16].*;
var cipher: [16]u8 = undefined;
for (clear[0..], xord[0..]) |*c, xr| c.* ^= xr;
aes_ctx.encrypt(cipher, clear[0..]);
aes_ctx.encrypt(cipher[0..], clear[0..]);
@memcpy(xord[0..16], cipher[0..16]);
print("{} {any}\n {any}\n", .{ i, clear, cipher });
try w.writeAll(cipher[0..]);
}
len += 16; // IV
},
else => unreachable,
}
 
//print("biv {any}\n", .{buffer[5..][0..12]});
//print("encrypted {any}\n", .{encrypted_body[0..len]});
//print("tag {any}\n", .{encrypted_body[len .. len + 16]});
 
return try record.packHeader(buffer, len);
const enc_len = try fba.getPos();
return try record.packHeader(buffer, enc_len);
}
 
pub fn unpackFragment(buffer: []const u8, sess: *ConnCtx) !TLSRecord {
@@ -358,62 +357,6 @@ fn fullHandshake(conn: std.net.Stream) !void {
try completeClient(conn, &ctx);
}
 
fn probe(conn: std.net.Stream, target: Cipher.Suites) !void {
var buffer = [_]u8{0} ** 0x1000;
var ctx = ConnCtx.initClient(std.testing.allocator);
var client_hello = Handshake.ClientHello.init(ctx);
client_hello.ciphers = &[1]Cipher.Suites{target};
const record = TLSRecord{
.kind = .{
.handshake = try Handshake.Handshake.wrap(client_hello),
},
};
 
const len = try record.pack(&buffer, &ctx);
_ = try conn.write(buffer[0..len]);
 
var server: [0x1000]u8 = undefined;
const s_read = try conn.read(&server);
if (s_read == 0) return error.InvalidSHello;
 
const server_msg = server[0..s_read];
if (s_read <= 7) {
print("suite {} is unsupported\n", .{target});
return;
}
print("FOUND {}\n", .{target});
 
const tlsr = try TLSRecord.unpack(server_msg, &ctx);
 
switch (tlsr.kind) {
.change_cipher_spec, .alert, .application_data => return error.UnexpectedResponse,
.handshake => |hs| {
switch (hs.body) {
else => return,
}
},
}
}
 
test "probe" {
if (true) return error.SkipZigTest;
for (
[_][]const u8{ "127.0.0.1", "144.126.209.12" },
[_]u16{ 4433, 443 },
) |IP, PORT| {
print("{s} :: {}\n", .{ IP, PORT });
for (std.meta.tags(Cipher.Suites)) |target| {
const addr = net.Address.resolveIp(IP, PORT) catch |err| {
print("unable to resolve address because {}\n", .{err});
return err;
};
const conn = try net.tcpConnectToAddress(addr);
defer conn.close();
try probe(conn, target);
}
}
}
 
test "tls" {
if (true) return error.SkipZigTest;
const addr = net.Address.resolveIp(TESTING_IP, TESTING_PORT) catch |err| {
 
filename was Deleted added: 160, removed: 115, total 45
@@ -0,0 +1,88 @@
const std = @import("std");
const nihssl = @import("nihssl");
const Allocator = std.mem.Allocator;
const net = std.net;
const print = std.debug.print;
 
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
 
for (
[_][]const u8{ "127.0.0.1", "144.126.209.12" },
[_]u16{ 4433, 443 },
) |IP, PORT| {
print("{s} :: {}\n", .{ IP, PORT });
for (std.meta.tags(nihssl.Cipher.Suites)) |target| {
const addr = net.Address.resolveIp(IP, PORT) catch |err| {
print("unable to resolve address because {}\n", .{err});
return err;
};
const conn = try net.tcpConnectToAddress(addr);
defer conn.close();
probe(alloc, conn, target) catch |err| switch (err) {
error.UnsupportedSuite => {},
else => return err,
};
}
}
}
 
fn probe(a: Allocator, conn: std.net.Stream, target: nihssl.Cipher.Suites) !void {
var buffer = [_]u8{0} ** 0x1000;
var ctx = nihssl.ConnCtx.initClient(a);
var client_hello = nihssl.Handshake.ClientHello.init(ctx);
client_hello.ciphers = &[1]nihssl.Cipher.Suites{target};
const record = nihssl.TLSRecord{
.kind = .{
.handshake = try nihssl.Handshake.Handshake.wrap(client_hello),
},
};
 
const len = try record.pack(&buffer, &ctx);
_ = try conn.write(buffer[0..len]);
 
var server: [0x1000]u8 = undefined;
const s_read = try conn.read(&server);
if (s_read == 0) return error.InvalidSHello;
 
const server_msg = server[0..s_read];
if (s_read <= 7) {
print("suite {} is unsupported\n", .{target});
return;
}
print("FOUND {}\n", .{target});
 
const tlsr = try nihssl.TLSRecord.unpack(server_msg, &ctx);
 
switch (tlsr.kind) {
.change_cipher_spec, .alert, .application_data => return error.UnexpectedResponse,
.handshake => |hs| {
switch (hs.body) {
else => return,
}
},
}
}
 
test "probe" {
for (
[_][]const u8{ "178.62.242.62", "127.0.0.1", "144.126.209.12" },
[_]u16{ 443, 4433, 443 },
) |IP, PORT| {
print("{s} :: {}\n", .{ IP, PORT });
for (std.meta.tags(nihssl.Cipher.Suites)) |target| {
const addr = net.Address.resolveIp(IP, PORT) catch |err| {
print("unable to resolve address because {}\n", .{err});
return err;
};
const conn = try net.tcpConnectToAddress(addr);
defer conn.close();
probe(conn, target) catch |err| switch (err) {
error.UnsupportedSuite => {},
else => return err,
};
}
}
}