temp push
This commit is contained in:
parent
d4e971906e
commit
55fc4c7260
109
src/client.zig
109
src/client.zig
|
@ -1,10 +1,12 @@
|
|||
const std = @import("std");
|
||||
const read = @import("./qtshit/read.zig");
|
||||
const write = @import("./qtshit/write.zig");
|
||||
const range = @import("./qtshit/utils/RangeIter.zig").range;
|
||||
const QVariantType = @import("./qtshit/types/QVariant.zig").QVariant;
|
||||
const prettyPrintQVariant = @import("./qtshit/utils/prettyPrintQVariant.zig").prettyPrintQVariant;
|
||||
const freeQVariant = @import("./qtshit/utils/free/freeQVariant.zig").freeQVariant;
|
||||
const QVariantMapToQVariantList = @import("./qtshit/utils/QVariantMapToQVariantList.zig").QVariantMapToQVariantList;
|
||||
const tls = @import("./deps/iguanaTLS/src/main.zig");
|
||||
|
||||
fn dumpDebug(name: []const u8, list: std.ArrayList(u8)) !void {
|
||||
std.debug.print("dumpDebug list len {d}\n", .{list.items.len});
|
||||
|
@ -22,12 +24,84 @@ pub const Client = struct {
|
|||
allocator: *std.mem.Allocator,
|
||||
stream: *std.net.Stream,
|
||||
|
||||
pub var tlsAllowed = !true;
|
||||
pub var tlsConnected = !true;
|
||||
|
||||
pub const TLSStream = tls.Client(std.net.Stream.Reader, std.net.Stream.Writer, tls.ciphersuites.all, false);
|
||||
pub var tlsClient: TLSStream = undefined;
|
||||
|
||||
pub fn initTLS(s: *Client) !void {
|
||||
if (!tlsConnected and tlsAllowed) {
|
||||
var randBuf: [32]u8 = undefined;
|
||||
try std.os.getrandom(&randBuf);
|
||||
var rng = std.rand.DefaultCsprng.init(randBuf);
|
||||
|
||||
var rand = blk: {
|
||||
var seed: [std.rand.DefaultCsprng.secret_seed_length]u8 = undefined;
|
||||
try std.os.getrandom(&seed);
|
||||
break :blk &std.rand.DefaultCsprng.init(seed).random;
|
||||
};
|
||||
|
||||
tlsClient = try tls.client_connect(.{
|
||||
.rand = rand,
|
||||
.temp_allocator = s.allocator,
|
||||
.reader = s.stream.reader(),
|
||||
.writer = s.stream.writer(),
|
||||
.cert_verifier = .none,
|
||||
.ciphersuites = tls.ciphersuites.all,
|
||||
}, "quassel.owo.monster");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _writeFrame(s: *Client, writer: anytype, data: std.ArrayList(u8)) !void {
|
||||
try write.writeUInt(writer, @intCast(u32, data.items.len));
|
||||
try writer.writeAll(data.items);
|
||||
}
|
||||
|
||||
pub fn writeFrame(s: *Client, data: std.ArrayList(u8)) !void {
|
||||
try s.initTLS();
|
||||
if (tlsConnected) {
|
||||
var writer = tlsClient.writer();
|
||||
try s._writeFrame(writer, data);
|
||||
} else {
|
||||
var writer = s.stream.writer();
|
||||
try s._writeFrame(writer, data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _readFrame(s: *Client, reader: anytype) !std.ArrayList(u8) {
|
||||
var size = try read.readUInt(reader);
|
||||
var data = std.ArrayList(u8).init(s.allocator);
|
||||
var iter = range(u32, 0, size);
|
||||
while (iter.next()) |i| {
|
||||
const byte = try reader.readByte();
|
||||
try data.append(byte);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
pub fn readFrame(s: *Client) !QVariantType {
|
||||
try s.initTLS();
|
||||
var data: std.ArrayList(u8) = undefined;
|
||||
|
||||
if (tlsConnected) {
|
||||
var reader = tlsClient.reader();
|
||||
data = try s._readFrame(reader);
|
||||
} else {
|
||||
var reader = s.stream.reader();
|
||||
data = try s._readFrame(reader);
|
||||
}
|
||||
|
||||
var fBS = std.io.fixedBufferStream(data.items);
|
||||
return try read.readQVariant(fBS.reader(), s.allocator);
|
||||
}
|
||||
|
||||
pub fn handshake(s: *Client) !void {
|
||||
//const magic = 0x42b33f00;
|
||||
|
||||
//try write.writeUInt(s.stream.writer(), magic);
|
||||
//try write.writeUInt(s.stream.writer(), 0x80000002);
|
||||
try s.stream.writer().writeAll(&[_]u8{ 0x42, 0xb3, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00 });
|
||||
try s.stream.writer().writeAll(&[_]u8{ 0x42, 0xb3, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00 });
|
||||
|
||||
var flags = try read.readByte(s.stream.reader());
|
||||
var extra = try read.readShort(s.stream.reader());
|
||||
|
@ -36,13 +110,12 @@ pub const Client = struct {
|
|||
std.debug.print("Handshake: flags={d} extra={d} version={d} \n", .{ flags, extra, version });
|
||||
}
|
||||
pub fn quassel_init_packet(s: *Client) !void {
|
||||
var list = std.ArrayList(u8).init(s.allocator);
|
||||
var data = std.ArrayList(u8).init(s.allocator);
|
||||
|
||||
var map = std.StringHashMap(QVariantType).init(s.allocator);
|
||||
|
||||
try map.put("MsgType", .{ .String = "ClientInit" });
|
||||
try map.put("UseCompression", .{ .UInt = 0 });
|
||||
try map.put("UseSSL", .{ .UInt = 0 });
|
||||
try map.put("UseSsl", .{ .UInt = 1 });
|
||||
|
||||
try map.put("ProtocolVersion", .{ .UInt = 10 });
|
||||
try map.put("ClientVersion", .{ .String = "0.1 (quasselclient)" });
|
||||
|
@ -57,36 +130,36 @@ pub const Client = struct {
|
|||
|
||||
try map.put("FeatureList", .{ .QStringList = featureList.items });
|
||||
|
||||
try write.writeFrame(list.writer(), s.allocator, map);
|
||||
try dumpDebug("ClientInit.bin", list);
|
||||
try s.stream.writer().writeAll(list.items);
|
||||
try write.writeQVariant(data.writer(), s.allocator, .{
|
||||
.QVariantMap = map,
|
||||
});
|
||||
|
||||
//std.debug.print("\n\nInitPacket: \n", .{});
|
||||
try s.writeFrame(data);
|
||||
|
||||
//prettyPrintQVariant(.{ .QVariantMap = map }, 0);
|
||||
|
||||
try s.read_quassel_packet();
|
||||
var varient = try s.readFrame();
|
||||
tlsAllowed = varient.QVariantMap.get("SupportSsl").?.Byte == 1;
|
||||
prettyPrintQVariant(varient, 0);
|
||||
freeQVariant(varient, s.allocator);
|
||||
}
|
||||
|
||||
pub fn quassel_login(s: *Client, username: []const u8, password: []const u8) !void {
|
||||
var list = std.ArrayList(u8).init(s.allocator);
|
||||
var data = std.ArrayList(u8).init(s.allocator);
|
||||
|
||||
var map = std.StringHashMap(QVariantType).init(s.allocator);
|
||||
|
||||
try map.put("MsgType", .{ .String = "ClientLogin" });
|
||||
try map.put("User", .{ .String = username });
|
||||
try map.put("Password", .{ .String = password });
|
||||
|
||||
try write.writeFrame(list.writer(), s.allocator, map);
|
||||
try dumpDebug("ClientLogin.bin", list);
|
||||
try write.writeQVariant(data.writer(), s.allocator, .{
|
||||
.QVariantMap = map,
|
||||
});
|
||||
|
||||
try s.stream.writer().writeAll(list.items);
|
||||
try s.writeFrame(data);
|
||||
}
|
||||
|
||||
pub fn read_quassel_packet(s: *Client) !void {
|
||||
var size = read.readUInt(s.stream.reader());
|
||||
std.debug.print("\n\nQuassel Packet: \n", .{});
|
||||
var varient = try read.readQVariant(s.stream.reader(), s.allocator);
|
||||
var varient = try s.readFrame();
|
||||
prettyPrintQVariant(varient, 0);
|
||||
freeQVariant(varient, s.allocator);
|
||||
}
|
||||
|
|
1
src/deps/iguanaTLS/.gitattributes
vendored
Normal file
1
src/deps/iguanaTLS/.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.zig text=auto eol=lf
|
3
src/deps/iguanaTLS/.gitignore
vendored
Normal file
3
src/deps/iguanaTLS/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/zig-cache
|
||||
deps.zig
|
||||
gyro.lock
|
0
src/deps/iguanaTLS/.gitmodules
vendored
Normal file
0
src/deps/iguanaTLS/.gitmodules
vendored
Normal file
21
src/deps/iguanaTLS/LICENSE
Normal file
21
src/deps/iguanaTLS/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Alexandros Naskos
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
425
src/deps/iguanaTLS/bench/bench.zig
Normal file
425
src/deps/iguanaTLS/bench/bench.zig
Normal file
|
@ -0,0 +1,425 @@
|
|||
const std = @import("std");
|
||||
const tls = @import("tls");
|
||||
const use_gpa = @import("build_options").use_gpa;
|
||||
|
||||
pub const log_level = .debug;
|
||||
|
||||
const RecordingAllocator = struct {
|
||||
const Stats = struct {
|
||||
peak_allocated: usize = 0,
|
||||
total_allocated: usize = 0,
|
||||
total_deallocated: usize = 0,
|
||||
total_allocations: usize = 0,
|
||||
};
|
||||
|
||||
allocator: std.mem.Allocator = .{
|
||||
.allocFn = allocFn,
|
||||
.resizeFn = resizeFn,
|
||||
},
|
||||
base_allocator: *std.mem.Allocator,
|
||||
stats: Stats = .{},
|
||||
|
||||
fn allocFn(
|
||||
a: *std.mem.Allocator,
|
||||
len: usize,
|
||||
ptr_align: u29,
|
||||
len_align: u29,
|
||||
ret_addr: usize,
|
||||
) ![]u8 {
|
||||
const self = @fieldParentPtr(RecordingAllocator, "allocator", a);
|
||||
const mem = try self.base_allocator.allocFn(
|
||||
self.base_allocator,
|
||||
len,
|
||||
ptr_align,
|
||||
len_align,
|
||||
ret_addr,
|
||||
);
|
||||
|
||||
self.stats.total_allocations += 1;
|
||||
self.stats.total_allocated += mem.len;
|
||||
self.stats.peak_allocated = std.math.max(
|
||||
self.stats.peak_allocated,
|
||||
self.stats.total_allocated - self.stats.total_deallocated,
|
||||
);
|
||||
return mem;
|
||||
}
|
||||
|
||||
fn resizeFn(a: *std.mem.Allocator, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) !usize {
|
||||
const self = @fieldParentPtr(RecordingAllocator, "allocator", a);
|
||||
const actual_len = try self.base_allocator.resizeFn(
|
||||
self.base_allocator,
|
||||
buf,
|
||||
buf_align,
|
||||
new_len,
|
||||
len_align,
|
||||
ret_addr,
|
||||
);
|
||||
|
||||
if (actual_len == 0) {
|
||||
std.debug.assert(new_len == 0);
|
||||
self.stats.total_deallocated += buf.len;
|
||||
} else if (actual_len > buf.len) {
|
||||
self.stats.total_allocated += actual_len - buf.len;
|
||||
self.stats.peak_allocated = std.math.max(
|
||||
self.stats.peak_allocated,
|
||||
self.stats.total_allocated - self.stats.total_deallocated,
|
||||
);
|
||||
} else {
|
||||
self.stats.total_deallocated += buf.len - actual_len;
|
||||
}
|
||||
return actual_len;
|
||||
}
|
||||
};
|
||||
|
||||
const SinkWriter = blk: {
|
||||
const S = struct {};
|
||||
break :blk std.io.Writer(S, error{}, struct {
|
||||
fn f(_: S, buffer: []const u8) !usize {
|
||||
return buffer.len;
|
||||
}
|
||||
}.f);
|
||||
};
|
||||
|
||||
const ReplayingReaderState = struct {
|
||||
data: []const u8,
|
||||
};
|
||||
const ReplayingReader = std.io.Reader(*ReplayingReaderState, error{}, struct {
|
||||
fn f(self: *ReplayingReaderState, buffer: []u8) !usize {
|
||||
if (self.data.len < buffer.len)
|
||||
@panic("Not enoguh reader data!");
|
||||
std.mem.copy(u8, buffer, self.data[0..buffer.len]);
|
||||
self.data = self.data[buffer.len..];
|
||||
return buffer.len;
|
||||
}
|
||||
}.f);
|
||||
|
||||
const ReplayingRandom = struct {
|
||||
rand: std.rand.Random = .{ .fillFn = fillFn },
|
||||
data: []const u8,
|
||||
|
||||
fn fillFn(r: *std.rand.Random, buf: []u8) void {
|
||||
const self = @fieldParentPtr(ReplayingRandom, "rand", r);
|
||||
if (self.data.len < buf.len)
|
||||
@panic("Not enough random data!");
|
||||
std.mem.copy(u8, buf, self.data[0..buf.len]);
|
||||
self.data = self.data[buf.len..];
|
||||
}
|
||||
};
|
||||
|
||||
fn benchmark_run(
|
||||
comptime ciphersuites: anytype,
|
||||
comptime curves: anytype,
|
||||
gpa: *std.mem.Allocator,
|
||||
allocator: *std.mem.Allocator,
|
||||
running_time: f32,
|
||||
hostname: []const u8,
|
||||
port: u16,
|
||||
trust_anchors: tls.x509.TrustAnchorChain,
|
||||
reader_recording: []const u8,
|
||||
random_recording: []const u8,
|
||||
) !void {
|
||||
{
|
||||
const warmup_time_secs = std.math.max(0.5, running_time / 20);
|
||||
std.debug.print("Warming up for {d:.2} seconds...\n", .{warmup_time_secs});
|
||||
const warmup_time_ns = @floatToInt(i128, warmup_time_secs * std.time.ns_per_s);
|
||||
|
||||
var warmup_time_passed: i128 = 0;
|
||||
var timer = try std.time.Timer.start();
|
||||
while (warmup_time_passed < warmup_time_ns) {
|
||||
var rand = ReplayingRandom{
|
||||
.data = random_recording,
|
||||
};
|
||||
var reader_state = ReplayingReaderState{
|
||||
.data = reader_recording,
|
||||
};
|
||||
const reader = ReplayingReader{ .context = &reader_state };
|
||||
const writer = SinkWriter{ .context = .{} };
|
||||
|
||||
timer.reset();
|
||||
_ = try tls.client_connect(.{
|
||||
.rand = &rand.rand,
|
||||
.reader = reader,
|
||||
.writer = writer,
|
||||
.ciphersuites = ciphersuites,
|
||||
.curves = curves,
|
||||
.cert_verifier = .default,
|
||||
.temp_allocator = allocator,
|
||||
.trusted_certificates = trust_anchors.data.items,
|
||||
}, hostname);
|
||||
warmup_time_passed += timer.read();
|
||||
}
|
||||
}
|
||||
{
|
||||
std.debug.print("Benchmarking for {d:.2} seconds...\n", .{running_time});
|
||||
|
||||
const RunRecording = struct {
|
||||
time: i128,
|
||||
mem_stats: RecordingAllocator.Stats,
|
||||
};
|
||||
var run_recordings = std.ArrayList(RunRecording).init(gpa);
|
||||
|
||||
defer run_recordings.deinit();
|
||||
const bench_time_ns = @floatToInt(i128, running_time * std.time.ns_per_s);
|
||||
|
||||
var total_time_passed: i128 = 0;
|
||||
var iterations: usize = 0;
|
||||
var timer = try std.time.Timer.start();
|
||||
while (total_time_passed < bench_time_ns) : (iterations += 1) {
|
||||
var rand = ReplayingRandom{
|
||||
.data = random_recording,
|
||||
};
|
||||
var reader_state = ReplayingReaderState{
|
||||
.data = reader_recording,
|
||||
};
|
||||
const reader = ReplayingReader{ .context = &reader_state };
|
||||
const writer = SinkWriter{ .context = .{} };
|
||||
var recording_allocator = RecordingAllocator{ .base_allocator = allocator };
|
||||
|
||||
timer.reset();
|
||||
_ = try tls.client_connect(.{
|
||||
.rand = &rand.rand,
|
||||
.reader = reader,
|
||||
.writer = writer,
|
||||
.ciphersuites = ciphersuites,
|
||||
.curves = curves,
|
||||
.cert_verifier = .default,
|
||||
.temp_allocator = &recording_allocator.allocator,
|
||||
.trusted_certificates = trust_anchors.data.items,
|
||||
}, hostname);
|
||||
const runtime = timer.read();
|
||||
total_time_passed += runtime;
|
||||
|
||||
(try run_recordings.addOne()).* = .{
|
||||
.mem_stats = recording_allocator.stats,
|
||||
.time = runtime,
|
||||
};
|
||||
}
|
||||
|
||||
const total_time_secs = @intToFloat(f64, total_time_passed) / std.time.ns_per_s;
|
||||
const mean_time_ns = @divTrunc(total_time_passed, iterations);
|
||||
const mean_time_ms = @intToFloat(f64, mean_time_ns) * std.time.ms_per_s / std.time.ns_per_s;
|
||||
|
||||
const std_dev_ns = blk: {
|
||||
var acc: i128 = 0;
|
||||
for (run_recordings.items) |rec| {
|
||||
const dt = rec.time - mean_time_ns;
|
||||
acc += dt * dt;
|
||||
}
|
||||
break :blk std.math.sqrt(@divTrunc(acc, iterations));
|
||||
};
|
||||
const std_dev_ms = @intToFloat(f64, std_dev_ns) * std.time.ms_per_s / std.time.ns_per_s;
|
||||
|
||||
std.debug.print(
|
||||
\\Finished benchmarking.
|
||||
\\Total runtime: {d:.2} sec
|
||||
\\Iterations: {} ({d:.2} iterations/sec)
|
||||
\\Mean iteration time: {d:.2} ms
|
||||
\\Standard deviation: {d:.2} ms
|
||||
\\
|
||||
, .{
|
||||
total_time_secs,
|
||||
iterations,
|
||||
@intToFloat(f64, iterations) / total_time_secs,
|
||||
mean_time_ms,
|
||||
std_dev_ms,
|
||||
});
|
||||
|
||||
// (percentile/100) * (total number n + 1)
|
||||
std.sort.sort(RunRecording, run_recordings.items, {}, struct {
|
||||
fn f(_: void, lhs: RunRecording, rhs: RunRecording) bool {
|
||||
return lhs.time < rhs.time;
|
||||
}
|
||||
}.f);
|
||||
const percentiles = .{ 99.0, 90.0, 75.0, 50.0 };
|
||||
inline for (percentiles) |percentile| {
|
||||
if (percentile < iterations) {
|
||||
const idx = @floatToInt(usize, @intToFloat(f64, iterations + 1) * percentile / 100.0);
|
||||
std.debug.print(
|
||||
"{d:.0}th percentile value: {d:.2} ms\n",
|
||||
.{
|
||||
percentile,
|
||||
@intToFloat(f64, run_recordings.items[idx].time) * std.time.ms_per_s / std.time.ns_per_s,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const first_mem_stats = run_recordings.items[0].mem_stats;
|
||||
for (run_recordings.items[1..]) |rec| {
|
||||
std.debug.assert(std.meta.eql(first_mem_stats, rec.mem_stats));
|
||||
}
|
||||
|
||||
std.debug.print(
|
||||
\\Peak allocated memory: {Bi:.2},
|
||||
\\Total allocated memory: {Bi:.2},
|
||||
\\Number of allocations: {d},
|
||||
\\
|
||||
, .{
|
||||
first_mem_stats.peak_allocated,
|
||||
first_mem_stats.total_allocated,
|
||||
first_mem_stats.total_allocations,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn benchmark_run_with_ciphersuite(
|
||||
comptime ciphersuites: anytype,
|
||||
curve_str: []const u8,
|
||||
gpa: *std.mem.Allocator,
|
||||
allocator: *std.mem.Allocator,
|
||||
running_time: f32,
|
||||
hostname: []const u8,
|
||||
port: u16,
|
||||
trust_anchors: tls.x509.TrustAnchorChain,
|
||||
reader_recording: []const u8,
|
||||
random_recording: []const u8,
|
||||
) !void {
|
||||
if (std.mem.eql(u8, curve_str, "all")) {
|
||||
return try benchmark_run(
|
||||
ciphersuites,
|
||||
tls.curves.all,
|
||||
gpa,
|
||||
allocator,
|
||||
running_time,
|
||||
hostname,
|
||||
port,
|
||||
trust_anchors,
|
||||
reader_recording,
|
||||
random_recording,
|
||||
);
|
||||
}
|
||||
inline for (tls.curves.all) |curve| {
|
||||
if (std.mem.eql(u8, curve_str, curve.name)) {
|
||||
return try benchmark_run(
|
||||
ciphersuites,
|
||||
.{curve},
|
||||
gpa,
|
||||
allocator,
|
||||
running_time,
|
||||
hostname,
|
||||
port,
|
||||
trust_anchors,
|
||||
reader_recording,
|
||||
random_recording,
|
||||
);
|
||||
}
|
||||
}
|
||||
return error.InvalidCurve;
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const allocator = &gpa.allocator;
|
||||
|
||||
var args = std.process.args();
|
||||
std.debug.assert(args.skip());
|
||||
|
||||
const running_time = blk: {
|
||||
const maybe_arg = args.next(allocator) orelse return error.NoArguments;
|
||||
const arg = try maybe_arg;
|
||||
break :blk std.fmt.parseFloat(f32, arg) catch {
|
||||
std.log.crit("Running time is not a floating point number...", .{});
|
||||
return error.InvalidArg;
|
||||
};
|
||||
};
|
||||
|
||||
// Loop over all files, swap gpa with a fixed buffer allocator for the handhsake
|
||||
arg_loop: while (args.next(allocator)) |recorded_file_path_or_err| {
|
||||
const recorded_file_path = try recorded_file_path_or_err;
|
||||
defer allocator.free(recorded_file_path);
|
||||
|
||||
std.debug.print(
|
||||
\\============================================================
|
||||
\\{s}
|
||||
\\============================================================
|
||||
\\
|
||||
, .{std.fs.path.basename(recorded_file_path)});
|
||||
|
||||
const recorded_file = try std.fs.cwd().openFile(recorded_file_path, .{});
|
||||
defer recorded_file.close();
|
||||
|
||||
const ciphersuite_str_len = try recorded_file.reader().readByte();
|
||||
const ciphersuite_str = try allocator.alloc(u8, ciphersuite_str_len);
|
||||
defer allocator.free(ciphersuite_str);
|
||||
try recorded_file.reader().readNoEof(ciphersuite_str);
|
||||
|
||||
const curve_str_len = try recorded_file.reader().readByte();
|
||||
const curve_str = try allocator.alloc(u8, curve_str_len);
|
||||
defer allocator.free(curve_str);
|
||||
try recorded_file.reader().readNoEof(curve_str);
|
||||
|
||||
const hostname_len = try recorded_file.reader().readIntLittle(usize);
|
||||
const hostname = try allocator.alloc(u8, hostname_len);
|
||||
defer allocator.free(hostname);
|
||||
try recorded_file.reader().readNoEof(hostname);
|
||||
|
||||
const port = try recorded_file.reader().readIntLittle(u16);
|
||||
|
||||
const trust_anchors = blk: {
|
||||
const pem_file_path_len = try recorded_file.reader().readIntLittle(usize);
|
||||
const pem_file_path = try allocator.alloc(u8, pem_file_path_len);
|
||||
defer allocator.free(pem_file_path);
|
||||
try recorded_file.reader().readNoEof(pem_file_path);
|
||||
|
||||
const pem_file = try std.fs.cwd().openFile(pem_file_path, .{});
|
||||
defer pem_file.close();
|
||||
|
||||
const tas = try tls.x509.TrustAnchorChain.from_pem(allocator, pem_file.reader());
|
||||
std.debug.print("Read {} certificates.\n", .{tas.data.items.len});
|
||||
break :blk tas;
|
||||
};
|
||||
defer trust_anchors.deinit();
|
||||
|
||||
const reader_recording_len = try recorded_file.reader().readIntLittle(usize);
|
||||
const reader_recording = try allocator.alloc(u8, reader_recording_len);
|
||||
defer allocator.free(reader_recording);
|
||||
try recorded_file.reader().readNoEof(reader_recording);
|
||||
|
||||
const random_recording_len = try recorded_file.reader().readIntLittle(usize);
|
||||
const random_recording = try allocator.alloc(u8, random_recording_len);
|
||||
defer allocator.free(random_recording);
|
||||
try recorded_file.reader().readNoEof(random_recording);
|
||||
|
||||
const handshake_allocator = if (use_gpa)
|
||||
&gpa.allocator
|
||||
else
|
||||
&std.heap.ArenaAllocator.init(std.heap.page_allocator).allocator;
|
||||
|
||||
defer if (!use_gpa)
|
||||
@fieldParentPtr(std.heap.ArenaAllocator, "allocator", handshake_allocator).deinit();
|
||||
|
||||
if (std.mem.eql(u8, ciphersuite_str, "all")) {
|
||||
try benchmark_run_with_ciphersuite(
|
||||
tls.ciphersuites.all,
|
||||
curve_str,
|
||||
allocator,
|
||||
handshake_allocator,
|
||||
running_time,
|
||||
hostname,
|
||||
port,
|
||||
trust_anchors,
|
||||
reader_recording,
|
||||
random_recording,
|
||||
);
|
||||
continue :arg_loop;
|
||||
}
|
||||
inline for (tls.ciphersuites.all) |ciphersuite| {
|
||||
if (std.mem.eql(u8, ciphersuite_str, ciphersuite.name)) {
|
||||
try benchmark_run_with_ciphersuite(
|
||||
.{ciphersuite},
|
||||
curve_str,
|
||||
allocator,
|
||||
handshake_allocator,
|
||||
running_time,
|
||||
hostname,
|
||||
port,
|
||||
trust_anchors,
|
||||
reader_recording,
|
||||
random_recording,
|
||||
);
|
||||
continue :arg_loop;
|
||||
}
|
||||
}
|
||||
return error.InvalidCiphersuite;
|
||||
}
|
||||
}
|
33
src/deps/iguanaTLS/bench/build.zig
Normal file
33
src/deps/iguanaTLS/bench/build.zig
Normal file
|
@ -0,0 +1,33 @@
|
|||
const Builder = @import("std").build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const record_build = b.addExecutable("record_handshake", "record_handshake.zig");
|
||||
record_build.addPackagePath("tls", "../src/main.zig");
|
||||
record_build.setBuildMode(.Debug);
|
||||
record_build.install();
|
||||
|
||||
const use_gpa = b.option(
|
||||
bool,
|
||||
"use-gpa",
|
||||
"Use the general purpose allocator instead of an arena allocator",
|
||||
) orelse false;
|
||||
const bench_build = b.addExecutable("bench", "bench.zig");
|
||||
bench_build.addPackagePath("tls", "../src/main.zig");
|
||||
bench_build.setBuildMode(.ReleaseFast);
|
||||
bench_build.addBuildOption(bool, "use_gpa", use_gpa);
|
||||
bench_build.install();
|
||||
|
||||
const record_run_cmd = record_build.run();
|
||||
const bench_run_cmd = bench_build.run();
|
||||
record_run_cmd.step.dependOn(b.getInstallStep());
|
||||
bench_run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
record_run_cmd.addArgs(args);
|
||||
bench_run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const record_run_step = b.step("record-handshake", "Record a TLS handshake");
|
||||
const bench_run_step = b.step("bench", "Run the benchmark");
|
||||
record_run_step.dependOn(&record_run_cmd.step);
|
||||
bench_run_step.dependOn(&bench_run_cmd.step);
|
||||
}
|
249
src/deps/iguanaTLS/bench/record_handshake.zig
Normal file
249
src/deps/iguanaTLS/bench/record_handshake.zig
Normal file
|
@ -0,0 +1,249 @@
|
|||
const std = @import("std");
|
||||
const tls = @import("tls");
|
||||
|
||||
const RecordingRandom = struct {
|
||||
rand: std.rand.Random = .{
|
||||
.fillFn = fillFn,
|
||||
},
|
||||
base: *std.rand.Random,
|
||||
recorded: std.ArrayList(u8),
|
||||
|
||||
fn fillFn(r: *std.rand.Random, buf: []u8) void {
|
||||
const self = @fieldParentPtr(@This(), "rand", r);
|
||||
self.base.bytes(buf);
|
||||
self.recorded.writer().writeAll(buf) catch unreachable;
|
||||
}
|
||||
};
|
||||
|
||||
fn RecordingReaderState(comptime Base: type) type {
|
||||
return struct {
|
||||
base: Base,
|
||||
recorded: std.ArrayList(u8),
|
||||
|
||||
fn read(self: *@This(), buffer: []u8) !usize {
|
||||
var read_bytes = try self.base.read(buffer);
|
||||
if (read_bytes != 0) {
|
||||
try self.recorded.writer().writeAll(buffer[0..read_bytes]);
|
||||
}
|
||||
return read_bytes;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn RecordingReader(comptime Base: type) type {
|
||||
return std.io.Reader(
|
||||
*RecordingReaderState(Base),
|
||||
Base.Error || error{OutOfMemory},
|
||||
RecordingReaderState(Base).read,
|
||||
);
|
||||
}
|
||||
|
||||
fn record_handshake(
|
||||
comptime ciphersuites: anytype,
|
||||
comptime curves: anytype,
|
||||
allocator: *std.mem.Allocator,
|
||||
out_name: []const u8,
|
||||
hostname: []const u8,
|
||||
port: u16,
|
||||
pem_file_path: []const u8,
|
||||
) !void {
|
||||
// Read PEM file
|
||||
const pem_file = try std.fs.cwd().openFile(pem_file_path, .{});
|
||||
defer pem_file.close();
|
||||
|
||||
const trust_anchors = try tls.x509.TrustAnchorChain.from_pem(allocator, pem_file.reader());
|
||||
defer trust_anchors.deinit();
|
||||
std.log.info("Read {} certificates.", .{trust_anchors.data.items.len});
|
||||
|
||||
const sock = try std.net.tcpConnectToHost(allocator, hostname, port);
|
||||
defer sock.close();
|
||||
|
||||
var recording_reader_state = RecordingReaderState(@TypeOf(sock).Reader){
|
||||
.base = sock.reader(),
|
||||
.recorded = std.ArrayList(u8).init(allocator),
|
||||
};
|
||||
defer recording_reader_state.recorded.deinit();
|
||||
|
||||
var recording_random = RecordingRandom{
|
||||
.base = std.crypto.random,
|
||||
.recorded = std.ArrayList(u8).init(allocator),
|
||||
};
|
||||
defer recording_random.recorded.deinit();
|
||||
|
||||
const reader = RecordingReader(@TypeOf(sock).Reader){
|
||||
.context = &recording_reader_state,
|
||||
};
|
||||
std.log.info("Recording session `{s}`...", .{out_name});
|
||||
var client = try tls.client_connect(.{
|
||||
.rand = &recording_random.rand,
|
||||
.reader = reader,
|
||||
.writer = sock.writer(),
|
||||
.ciphersuites = ciphersuites,
|
||||
.curves = curves,
|
||||
.cert_verifier = .default,
|
||||
.temp_allocator = allocator,
|
||||
.trusted_certificates = trust_anchors.data.items,
|
||||
}, hostname);
|
||||
defer client.close_notify() catch {};
|
||||
|
||||
const out_file = try std.fs.cwd().createFile(out_name, .{});
|
||||
defer out_file.close();
|
||||
|
||||
if (ciphersuites.len > 1) {
|
||||
try out_file.writeAll(&[_]u8{ 0x3, 'a', 'l', 'l' });
|
||||
} else {
|
||||
try out_file.writer().writeIntLittle(u8, ciphersuites[0].name.len);
|
||||
try out_file.writeAll(ciphersuites[0].name);
|
||||
}
|
||||
if (curves.len > 1) {
|
||||
try out_file.writeAll(&[_]u8{ 0x3, 'a', 'l', 'l' });
|
||||
} else {
|
||||
try out_file.writer().writeIntLittle(u8, curves[0].name.len);
|
||||
try out_file.writeAll(curves[0].name);
|
||||
}
|
||||
try out_file.writer().writeIntLittle(usize, hostname.len);
|
||||
try out_file.writeAll(hostname);
|
||||
try out_file.writer().writeIntLittle(u16, port);
|
||||
try out_file.writer().writeIntLittle(usize, pem_file_path.len);
|
||||
try out_file.writeAll(pem_file_path);
|
||||
try out_file.writer().writeIntLittle(usize, recording_reader_state.recorded.items.len);
|
||||
try out_file.writeAll(recording_reader_state.recorded.items);
|
||||
try out_file.writer().writeIntLittle(usize, recording_random.recorded.items.len);
|
||||
try out_file.writeAll(recording_random.recorded.items);
|
||||
std.log.info("Session recorded.\n", .{});
|
||||
}
|
||||
|
||||
fn record_with_ciphersuite(
|
||||
comptime ciphersuites: anytype,
|
||||
allocator: *std.mem.Allocator,
|
||||
out_name: []const u8,
|
||||
curve_str: []const u8,
|
||||
hostname: []const u8,
|
||||
port: u16,
|
||||
pem_file_path: []const u8,
|
||||
) !void {
|
||||
if (std.mem.eql(u8, curve_str, "all")) {
|
||||
return try record_handshake(
|
||||
ciphersuites,
|
||||
tls.curves.all,
|
||||
allocator,
|
||||
out_name,
|
||||
hostname,
|
||||
port,
|
||||
pem_file_path,
|
||||
);
|
||||
}
|
||||
inline for (tls.curves.all) |curve| {
|
||||
if (std.mem.eql(u8, curve_str, curve.name)) {
|
||||
return try record_handshake(
|
||||
ciphersuites,
|
||||
.{curve},
|
||||
allocator,
|
||||
out_name,
|
||||
hostname,
|
||||
port,
|
||||
pem_file_path,
|
||||
);
|
||||
}
|
||||
}
|
||||
std.log.crit("Invalid curve `{s}`", .{curve_str});
|
||||
std.debug.warn("Available options:\n- all\n", .{});
|
||||
inline for (tls.curves.all) |curve| {
|
||||
std.debug.warn("- {s}\n", .{curve.name});
|
||||
}
|
||||
return error.InvalidArg;
|
||||
}
|
||||
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
pub fn main() !void {
|
||||
const allocator = &gpa.allocator;
|
||||
|
||||
var args = std.process.args();
|
||||
std.debug.assert(args.skip());
|
||||
|
||||
const pem_file_path = try (args.next(allocator) orelse {
|
||||
std.log.crit("Need PEM file path as first argument", .{});
|
||||
return error.NotEnoughArgs;
|
||||
});
|
||||
defer allocator.free(pem_file_path);
|
||||
|
||||
const ciphersuite_str = try (args.next(allocator) orelse {
|
||||
std.log.crit("Need ciphersuite as second argument", .{});
|
||||
return error.NotEnoughArgs;
|
||||
});
|
||||
defer allocator.free(ciphersuite_str);
|
||||
|
||||
const curve_str = try (args.next(allocator) orelse {
|
||||
std.log.crit("Need curve as third argument", .{});
|
||||
return error.NotEnoughArgs;
|
||||
});
|
||||
defer allocator.free(curve_str);
|
||||
|
||||
const hostname_port = try (args.next(allocator) orelse {
|
||||
std.log.crit("Need hostname:port as fourth argument", .{});
|
||||
return error.NotEnoughArgs;
|
||||
});
|
||||
defer allocator.free(hostname_port);
|
||||
|
||||
if (args.skip()) {
|
||||
std.log.crit("Need exactly four arguments", .{});
|
||||
return error.TooManyArgs;
|
||||
}
|
||||
|
||||
var hostname_parts = std.mem.split(hostname_port, ":");
|
||||
const hostname = hostname_parts.next().?;
|
||||
const port = std.fmt.parseUnsigned(
|
||||
u16,
|
||||
hostname_parts.next() orelse {
|
||||
std.log.crit("Hostname and port should be in `hostname:port` format", .{});
|
||||
return error.InvalidArg;
|
||||
},
|
||||
10,
|
||||
) catch {
|
||||
std.log.crit("Port is not a base 10 unsigned integer...", .{});
|
||||
return error.InvalidArg;
|
||||
};
|
||||
if (hostname_parts.next() != null) {
|
||||
std.log.crit("Hostname and port should be in `hostname:port` format", .{});
|
||||
return error.InvalidArg;
|
||||
}
|
||||
|
||||
const out_name = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}-{}.handshake", .{
|
||||
hostname,
|
||||
ciphersuite_str,
|
||||
curve_str,
|
||||
std.time.timestamp(),
|
||||
});
|
||||
defer allocator.free(out_name);
|
||||
|
||||
if (std.mem.eql(u8, ciphersuite_str, "all")) {
|
||||
return try record_with_ciphersuite(
|
||||
tls.ciphersuites.all,
|
||||
allocator,
|
||||
out_name,
|
||||
curve_str,
|
||||
hostname,
|
||||
port,
|
||||
pem_file_path,
|
||||
);
|
||||
}
|
||||
inline for (tls.ciphersuites.all) |ciphersuite| {
|
||||
if (std.mem.eql(u8, ciphersuite_str, ciphersuite.name)) {
|
||||
return try record_with_ciphersuite(
|
||||
.{ciphersuite},
|
||||
allocator,
|
||||
out_name,
|
||||
curve_str,
|
||||
hostname,
|
||||
port,
|
||||
pem_file_path,
|
||||
);
|
||||
}
|
||||
}
|
||||
std.log.crit("Invalid ciphersuite `{s}`", .{ciphersuite_str});
|
||||
std.debug.warn("Available options:\n- all\n", .{});
|
||||
inline for (tls.ciphersuites.all) |ciphersuite| {
|
||||
std.debug.warn("- {s}\n", .{ciphersuite.name});
|
||||
}
|
||||
return error.InvalidArg;
|
||||
}
|
14
src/deps/iguanaTLS/build.zig
Normal file
14
src/deps/iguanaTLS/build.zig
Normal file
|
@ -0,0 +1,14 @@
|
|||
const Builder = @import("std").build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const lib = b.addStaticLibrary("iguanaTLS", "src/main.zig");
|
||||
lib.setBuildMode(mode);
|
||||
lib.install();
|
||||
|
||||
var main_tests = b.addTest("src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
}
|
15
src/deps/iguanaTLS/gyro.zzz
Normal file
15
src/deps/iguanaTLS/gyro.zzz
Normal file
|
@ -0,0 +1,15 @@
|
|||
pkgs:
|
||||
iguanaTLS:
|
||||
version: 0.0.1
|
||||
author: alexnask
|
||||
description: "Minimal, experimental TLS 1.2 implementation in Zig"
|
||||
license: MIT
|
||||
source_url: "https://github.com/alexnask/iguanaTLS"
|
||||
|
||||
files:
|
||||
build.zig
|
||||
README.md
|
||||
LICENSE
|
||||
src/*.zig
|
||||
bench/*.zig
|
||||
test/*
|
631
src/deps/iguanaTLS/src/asn1.zig
Normal file
631
src/deps/iguanaTLS/src/asn1.zig
Normal file
|
@ -0,0 +1,631 @@
|
|||
const std = @import("std");
|
||||
const BigInt = std.math.big.int.Const;
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
// zig fmt: off
|
||||
pub const Tag = enum(u8) {
|
||||
bool = 0x01,
|
||||
int = 0x02,
|
||||
bit_string = 0x03,
|
||||
octet_string = 0x04,
|
||||
@"null" = 0x05,
|
||||
object_identifier = 0x06,
|
||||
utf8_string = 0x0c,
|
||||
printable_string = 0x13,
|
||||
ia5_string = 0x16,
|
||||
utc_time = 0x17,
|
||||
bmp_string = 0x1e,
|
||||
sequence = 0x30,
|
||||
set = 0x31,
|
||||
// Bogus value
|
||||
context_specific = 0xff,
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
pub const ObjectIdentifier = struct {
|
||||
data: [16]u32,
|
||||
len: u8,
|
||||
};
|
||||
|
||||
pub const BitString = struct {
|
||||
data: []const u8,
|
||||
bit_len: usize,
|
||||
};
|
||||
|
||||
pub const Value = union(Tag) {
|
||||
bool: bool,
|
||||
int: BigInt,
|
||||
bit_string: BitString,
|
||||
octet_string: []const u8,
|
||||
@"null",
|
||||
// @TODO Make this []u32, owned?
|
||||
object_identifier: ObjectIdentifier,
|
||||
utf8_string: []const u8,
|
||||
printable_string: []const u8,
|
||||
ia5_string: []const u8,
|
||||
utc_time: []const u8,
|
||||
bmp_string: []const u16,
|
||||
sequence: []const @This(),
|
||||
set: []const @This(),
|
||||
context_specific: struct {
|
||||
child: *const Value,
|
||||
number: u8,
|
||||
},
|
||||
|
||||
pub fn deinit(self: @This(), alloc: *Allocator) void {
|
||||
switch (self) {
|
||||
.int => |i| alloc.free(i.limbs),
|
||||
.bit_string => |bs| alloc.free(bs.data),
|
||||
.octet_string,
|
||||
.utf8_string,
|
||||
.printable_string,
|
||||
.ia5_string,
|
||||
.utc_time,
|
||||
=> |s| alloc.free(s),
|
||||
.bmp_string => |s| alloc.free(s),
|
||||
.sequence, .set => |s| {
|
||||
for (s) |c| {
|
||||
c.deinit(alloc);
|
||||
}
|
||||
alloc.free(s);
|
||||
},
|
||||
.context_specific => |cs| {
|
||||
cs.child.deinit(alloc);
|
||||
alloc.destroy(cs.child);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn formatInternal(
|
||||
self: Value,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
indents: usize,
|
||||
writer: anytype,
|
||||
) @TypeOf(writer).Error!void {
|
||||
try writer.writeByteNTimes(' ', indents);
|
||||
switch (self) {
|
||||
.bool => |b| try writer.print("BOOLEAN {}\n", .{b}),
|
||||
.int => |i| {
|
||||
try writer.writeAll("INTEGER ");
|
||||
try i.format(fmt, options, writer);
|
||||
try writer.writeByte('\n');
|
||||
},
|
||||
.bit_string => |bs| {
|
||||
try writer.print("BIT STRING ({} bits) ", .{bs.bit_len});
|
||||
const bits_to_show = std.math.min(8 * 3, bs.bit_len);
|
||||
const bytes = std.math.divCeil(usize, bits_to_show, 8) catch unreachable;
|
||||
|
||||
var bit_idx: usize = 0;
|
||||
var byte_idx: usize = 0;
|
||||
while (byte_idx < bytes) : (byte_idx += 1) {
|
||||
const byte = bs.data[byte_idx];
|
||||
var cur_bit_idx: u3 = 0;
|
||||
while (bit_idx < bits_to_show) {
|
||||
const mask = @as(u8, 0x80) >> cur_bit_idx;
|
||||
try writer.print("{}", .{@boolToInt(byte & mask == mask)});
|
||||
cur_bit_idx += 1;
|
||||
bit_idx += 1;
|
||||
if (cur_bit_idx == 7)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bits_to_show != bs.bit_len)
|
||||
try writer.writeAll("...");
|
||||
try writer.writeByte('\n');
|
||||
},
|
||||
.octet_string => |s| try writer.print("OCTET STRING ({} bytes) {X}\n", .{ s.len, s }),
|
||||
.@"null" => try writer.writeAll("NULL\n"),
|
||||
.object_identifier => |oid| {
|
||||
try writer.writeAll("OBJECT IDENTIFIER ");
|
||||
var i: u8 = 0;
|
||||
while (i < oid.len) : (i += 1) {
|
||||
if (i != 0) try writer.writeByte('.');
|
||||
try writer.print("{}", .{oid.data[i]});
|
||||
}
|
||||
try writer.writeByte('\n');
|
||||
},
|
||||
.utf8_string => |s| try writer.print("UTF8 STRING ({} bytes) {}\n", .{ s.len, s }),
|
||||
.printable_string => |s| try writer.print("PRINTABLE STRING ({} bytes) {}\n", .{ s.len, s }),
|
||||
.ia5_string => |s| try writer.print("IA5 STRING ({} bytes) {}\n", .{ s.len, s }),
|
||||
.utc_time => |s| try writer.print("UTC TIME {}\n", .{s}),
|
||||
.bmp_string => |s| try writer.print("BMP STRING ({} words) {}\n", .{
|
||||
s.len,
|
||||
@ptrCast([*]const u16, s.ptr)[0 .. s.len * 2],
|
||||
}),
|
||||
.sequence => |children| {
|
||||
try writer.print("SEQUENCE ({} elems)\n", .{children.len});
|
||||
for (children) |child| try child.formatInternal(fmt, options, indents + 2, writer);
|
||||
},
|
||||
.set => |children| {
|
||||
try writer.print("SET ({} elems)\n", .{children.len});
|
||||
for (children) |child| try child.formatInternal(fmt, options, indents + 2, writer);
|
||||
},
|
||||
.context_specific => |cs| {
|
||||
try writer.print("[{}]\n", .{cs.number});
|
||||
try cs.child.formatInternal(fmt, options, indents + 2, writer);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(self: Value, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
|
||||
try self.formatInternal(fmt, options, 0, writer);
|
||||
}
|
||||
};
|
||||
|
||||
/// Distinguished encoding rules
|
||||
pub const der = struct {
|
||||
pub fn DecodeError(comptime Reader: type) type {
|
||||
return Reader.Error || error{
|
||||
OutOfMemory,
|
||||
EndOfStream,
|
||||
InvalidLength,
|
||||
InvalidTag,
|
||||
InvalidContainerLength,
|
||||
DoesNotMatchSchema,
|
||||
};
|
||||
}
|
||||
|
||||
fn DERReaderState(comptime Reader: type) type {
|
||||
return struct {
|
||||
der_reader: Reader,
|
||||
length: usize,
|
||||
idx: usize = 0,
|
||||
};
|
||||
}
|
||||
|
||||
fn DERReader(comptime Reader: type) type {
|
||||
const S = struct {
|
||||
pub fn read(state: *DERReaderState(Reader), buffer: []u8) DecodeError(Reader)!usize {
|
||||
const out_bytes = std.math.min(buffer.len, state.length - state.idx);
|
||||
const res = try state.der_reader.readAll(buffer[0..out_bytes]);
|
||||
state.idx += res;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
return std.io.Reader(*DERReaderState(Reader), DecodeError(Reader), S.read);
|
||||
}
|
||||
|
||||
pub fn parse_schema(
|
||||
schema: anytype,
|
||||
captures: anytype,
|
||||
der_reader: anytype,
|
||||
) !void {
|
||||
const res = try parse_schema_tag_len_internal(null, null, schema, captures, der_reader);
|
||||
if (res != null) return error.DoesNotMatchSchema;
|
||||
}
|
||||
|
||||
pub fn parse_schema_tag_len(
|
||||
existing_tag_byte: ?u8,
|
||||
existing_length: ?usize,
|
||||
schema: anytype,
|
||||
captures: anytype,
|
||||
der_reader: anytype,
|
||||
) !void {
|
||||
const res = try parse_schema_tag_len_internal(
|
||||
existing_tag_byte,
|
||||
existing_length,
|
||||
schema,
|
||||
captures,
|
||||
der_reader,
|
||||
);
|
||||
if (res != null) return error.DoesNotMatchSchema;
|
||||
}
|
||||
|
||||
const TagLength = struct {
|
||||
tag: u8,
|
||||
length: usize,
|
||||
};
|
||||
|
||||
pub fn parse_schema_tag_len_internal(
|
||||
existing_tag_byte: ?u8,
|
||||
existing_length: ?usize,
|
||||
schema: anytype,
|
||||
captures: anytype,
|
||||
der_reader: anytype,
|
||||
) !?TagLength {
|
||||
const Reader = @TypeOf(der_reader);
|
||||
|
||||
const isEnumLit = comptime std.meta.trait.is(.EnumLiteral);
|
||||
comptime var tag_idx = 0;
|
||||
|
||||
const has_capture = comptime isEnumLit(@TypeOf(schema[tag_idx])) and schema[tag_idx] == .capture;
|
||||
if (has_capture) tag_idx += 2;
|
||||
|
||||
const is_optional = comptime isEnumLit(@TypeOf(schema[tag_idx])) and schema[tag_idx] == .optional;
|
||||
if (is_optional) tag_idx += 1;
|
||||
|
||||
const tag_literal = schema[tag_idx];
|
||||
comptime std.debug.assert(isEnumLit(@TypeOf(tag_literal)));
|
||||
|
||||
const tag_byte = existing_tag_byte orelse (der_reader.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => |e| return if (is_optional) null else error.EndOfStream,
|
||||
else => |e| return e,
|
||||
});
|
||||
|
||||
const length = existing_length orelse try parse_length(der_reader);
|
||||
if (tag_literal == .sequence_of) {
|
||||
if (tag_byte != @enumToInt(Tag.sequence)) {
|
||||
if (is_optional) return TagLength{ .tag = tag_byte, .length = length };
|
||||
return error.InvalidTag;
|
||||
}
|
||||
|
||||
var curr_tag_length: ?TagLength = null;
|
||||
const sub_schema = schema[tag_idx + 1];
|
||||
while (true) {
|
||||
if (curr_tag_length == null) {
|
||||
curr_tag_length = .{
|
||||
.tag = der_reader.readByte() catch |err| switch (err) {
|
||||
error.EndOfStream => {
|
||||
curr_tag_length = null;
|
||||
break;
|
||||
},
|
||||
else => |e| return e,
|
||||
},
|
||||
.length = try parse_length(der_reader),
|
||||
};
|
||||
}
|
||||
|
||||
curr_tag_length = parse_schema_tag_len_internal(
|
||||
curr_tag_length.?.tag,
|
||||
curr_tag_length.?.length,
|
||||
sub_schema,
|
||||
captures,
|
||||
der_reader,
|
||||
) catch |err| switch (err) {
|
||||
error.DoesNotMatchSchema => break,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
return curr_tag_length;
|
||||
} else if (tag_literal == .any) {
|
||||
if (!has_capture) {
|
||||
try der_reader.skipBytes(length, .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
var reader_state = DERReaderState(Reader){
|
||||
.der_reader = der_reader,
|
||||
.idx = 0,
|
||||
.length = length,
|
||||
};
|
||||
var reader = DERReader(@TypeOf(der_reader)){ .context = &reader_state };
|
||||
const capture_context = captures[schema[1] * 2];
|
||||
const capture_action = captures[schema[1] * 2 + 1];
|
||||
try capture_action(capture_context, tag_byte, length, reader);
|
||||
|
||||
// Skip remaining bytes
|
||||
try der_reader.skipBytes(reader_state.length - reader_state.idx, .{});
|
||||
return null;
|
||||
} else if (tag_literal == .context_specific) {
|
||||
const cs_number = schema[tag_idx + 1];
|
||||
if (tag_byte & 0xC0 == 0x80 and tag_byte - 0xa0 == cs_number) {
|
||||
if (!has_capture) {
|
||||
if (schema.len > tag_idx + 2) {
|
||||
return try parse_schema_tag_len_internal(null, null, schema[tag_idx + 2], captures, der_reader);
|
||||
}
|
||||
|
||||
try der_reader.skipBytes(length, .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
var reader_state = DERReaderState(Reader){
|
||||
.der_reader = der_reader,
|
||||
.idx = 0,
|
||||
.length = length,
|
||||
};
|
||||
var reader = DERReader(Reader){ .context = &reader_state };
|
||||
const capture_context = captures[schema[1] * 2];
|
||||
const capture_action = captures[schema[1] * 2 + 1];
|
||||
try capture_action(capture_context, tag_byte, length, reader);
|
||||
|
||||
// Skip remaining bytes
|
||||
try der_reader.skipBytes(reader_state.length - reader_state.idx, .{});
|
||||
return null;
|
||||
} else if (is_optional)
|
||||
return TagLength{ .tag = tag_byte, .length = length }
|
||||
else
|
||||
return error.DoesNotMatchSchema;
|
||||
}
|
||||
|
||||
const schema_tag: Tag = tag_literal;
|
||||
const actual_tag = std.meta.intToEnum(Tag, tag_byte) catch return error.InvalidTag;
|
||||
if (actual_tag != schema_tag) {
|
||||
if (is_optional) return TagLength{ .tag = tag_byte, .length = length };
|
||||
return error.DoesNotMatchSchema;
|
||||
}
|
||||
|
||||
const single_seq = schema_tag == .sequence and schema.len == 1;
|
||||
if ((!has_capture and schema_tag != .sequence) or (!has_capture and single_seq)) {
|
||||
try der_reader.skipBytes(length, .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (has_capture) {
|
||||
var reader_state = DERReaderState(Reader){
|
||||
.der_reader = der_reader,
|
||||
.idx = 0,
|
||||
.length = length,
|
||||
};
|
||||
var reader = DERReader(Reader){ .context = &reader_state };
|
||||
const capture_context = captures[schema[1] * 2];
|
||||
const capture_action = captures[schema[1] * 2 + 1];
|
||||
try capture_action(capture_context, tag_byte, length, reader);
|
||||
|
||||
// Skip remaining bytes
|
||||
try der_reader.skipBytes(reader_state.length - reader_state.idx, .{});
|
||||
return null;
|
||||
}
|
||||
|
||||
var cur_tag_length: ?TagLength = null;
|
||||
const sub_schemas = schema[tag_idx + 1];
|
||||
comptime var i = 0;
|
||||
inline while (i < sub_schemas.len) : (i += 1) {
|
||||
const curr_tag = if (cur_tag_length) |tl| tl.tag else null;
|
||||
const curr_length = if (cur_tag_length) |tl| tl.length else null;
|
||||
cur_tag_length = try parse_schema_tag_len_internal(curr_tag, curr_length, sub_schemas[i], captures, der_reader);
|
||||
}
|
||||
return cur_tag_length;
|
||||
}
|
||||
|
||||
pub const EncodedLength = struct {
|
||||
data: [@sizeOf(usize) + 1]u8,
|
||||
len: usize,
|
||||
|
||||
pub fn slice(self: @This()) []const u8 {
|
||||
if (self.len == 1) return self.data[0..1];
|
||||
return self.data[0 .. 1 + self.len];
|
||||
}
|
||||
};
|
||||
|
||||
pub fn encode_length(length: usize) EncodedLength {
|
||||
var enc = EncodedLength{ .data = undefined, .len = 0 };
|
||||
if (length < 128) {
|
||||
enc.data[0] = @truncate(u8, length);
|
||||
enc.len = 1;
|
||||
} else {
|
||||
const bytes_needed = @intCast(u8, std.math.divCeil(
|
||||
usize,
|
||||
std.math.log2_int_ceil(usize, length),
|
||||
8,
|
||||
) catch unreachable);
|
||||
enc.data[0] = bytes_needed | 0x80;
|
||||
mem.copy(
|
||||
u8,
|
||||
enc.data[1 .. bytes_needed + 1],
|
||||
mem.asBytes(&length)[0..bytes_needed],
|
||||
);
|
||||
if (std.builtin.target.cpu.arch.endian() != .Big) {
|
||||
mem.reverse(u8, enc.data[1 .. bytes_needed + 1]);
|
||||
}
|
||||
enc.len = bytes_needed;
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
fn parse_int_internal(alloc: *Allocator, bytes_read: *usize, der_reader: anytype) !BigInt {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
return try parse_int_with_length_internal(alloc, bytes_read, length, der_reader);
|
||||
}
|
||||
|
||||
pub fn parse_int(alloc: *Allocator, der_reader: anytype) !BigInt {
|
||||
var bytes: usize = undefined;
|
||||
return try parse_int_internal(alloc, &bytes, der_reader);
|
||||
}
|
||||
|
||||
pub fn parse_int_with_length(alloc: *Allocator, length: usize, der_reader: anytype) !BigInt {
|
||||
var read: usize = 0;
|
||||
return try parse_int_with_length_internal(alloc, &read, length, der_reader);
|
||||
}
|
||||
|
||||
fn parse_int_with_length_internal(alloc: *Allocator, bytes_read: *usize, length: usize, der_reader: anytype) !BigInt {
|
||||
const first_byte = try der_reader.readByte();
|
||||
if (first_byte == 0x0 and length > 1) {
|
||||
// Positive number with highest bit set to 1 in the rest.
|
||||
const limb_count = std.math.divCeil(usize, length - 1, @sizeOf(usize)) catch unreachable;
|
||||
const limbs = try alloc.alloc(usize, limb_count);
|
||||
std.mem.set(usize, limbs, 0);
|
||||
errdefer alloc.free(limbs);
|
||||
|
||||
var limb_ptr = @ptrCast([*]u8, limbs.ptr);
|
||||
try der_reader.readNoEof(limb_ptr[0 .. length - 1]);
|
||||
// We always reverse because the standard library big int expects little endian.
|
||||
mem.reverse(u8, limb_ptr[0 .. length - 1]);
|
||||
|
||||
bytes_read.* += length;
|
||||
return BigInt{ .limbs = limbs, .positive = true };
|
||||
}
|
||||
std.debug.assert(length != 0);
|
||||
// Write first_byte
|
||||
// Twos complement
|
||||
const limb_count = std.math.divCeil(usize, length, @sizeOf(usize)) catch unreachable;
|
||||
const limbs = try alloc.alloc(usize, limb_count);
|
||||
std.mem.set(usize, limbs, 0);
|
||||
errdefer alloc.free(limbs);
|
||||
|
||||
var limb_ptr = @ptrCast([*]u8, limbs.ptr);
|
||||
limb_ptr[0] = first_byte & ~@as(u8, 0x80);
|
||||
try der_reader.readNoEof(limb_ptr[1..length]);
|
||||
|
||||
// We always reverse because the standard library big int expects little endian.
|
||||
mem.reverse(u8, limb_ptr[0..length]);
|
||||
bytes_read.* += length;
|
||||
return BigInt{ .limbs = limbs, .positive = (first_byte & 0x80) == 0x00 };
|
||||
}
|
||||
|
||||
pub fn parse_length(der_reader: anytype) !usize {
|
||||
var bytes: usize = 0;
|
||||
return try parse_length_internal(&bytes, der_reader);
|
||||
}
|
||||
|
||||
fn parse_length_internal(bytes_read: *usize, der_reader: anytype) !usize {
|
||||
const first_byte = try der_reader.readByte();
|
||||
bytes_read.* += 1;
|
||||
if (first_byte & 0x80 == 0x00) {
|
||||
// 1 byte value
|
||||
return first_byte;
|
||||
}
|
||||
const length = @truncate(u7, first_byte);
|
||||
if (length > @sizeOf(usize))
|
||||
@panic("DER length does not fit in usize");
|
||||
|
||||
var res_buf = std.mem.zeroes([@sizeOf(usize)]u8);
|
||||
try der_reader.readNoEof(res_buf[0..length]);
|
||||
bytes_read.* += length;
|
||||
|
||||
if (std.builtin.target.cpu.arch.endian() != .Big) {
|
||||
mem.reverse(u8, res_buf[0..length]);
|
||||
}
|
||||
return mem.bytesToValue(usize, &res_buf);
|
||||
}
|
||||
|
||||
fn parse_value_with_tag_byte(
|
||||
tag_byte: u8,
|
||||
alloc: *Allocator,
|
||||
bytes_read: *usize,
|
||||
der_reader: anytype,
|
||||
) DecodeError(@TypeOf(der_reader))!Value {
|
||||
const tag = std.meta.intToEnum(Tag, tag_byte) catch {
|
||||
// tag starts with '0b10...', this is the context specific class.
|
||||
if (tag_byte & 0xC0 == 0x80) {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
var cur_read_bytes: usize = 0;
|
||||
var child = try alloc.create(Value);
|
||||
errdefer alloc.destroy(child);
|
||||
|
||||
child.* = try parse_value_internal(alloc, &cur_read_bytes, der_reader);
|
||||
if (cur_read_bytes != length)
|
||||
return error.InvalidContainerLength;
|
||||
bytes_read.* += length;
|
||||
return Value{ .context_specific = .{ .child = child, .number = tag_byte - 0xa0 } };
|
||||
}
|
||||
|
||||
return error.InvalidTag;
|
||||
};
|
||||
switch (tag) {
|
||||
.bool => {
|
||||
if ((try der_reader.readByte()) != 0x1)
|
||||
return error.InvalidLength;
|
||||
defer bytes_read.* += 2;
|
||||
return Value{ .bool = (try der_reader.readByte()) != 0x0 };
|
||||
},
|
||||
.int => return Value{ .int = try parse_int_internal(alloc, bytes_read, der_reader) },
|
||||
.bit_string => {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
const unused_bits = try der_reader.readByte();
|
||||
std.debug.assert(unused_bits < 8);
|
||||
const bit_count = (length - 1) * 8 - unused_bits;
|
||||
const bit_memory = try alloc.alloc(u8, std.math.divCeil(usize, bit_count, 8) catch unreachable);
|
||||
errdefer alloc.free(bit_memory);
|
||||
try der_reader.readNoEof(bit_memory[0 .. length - 1]);
|
||||
|
||||
bytes_read.* += length;
|
||||
return Value{ .bit_string = .{ .data = bit_memory, .bit_len = bit_count } };
|
||||
},
|
||||
.octet_string, .utf8_string, .printable_string, .utc_time, .ia5_string => {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
const str_mem = try alloc.alloc(u8, length);
|
||||
try der_reader.readNoEof(str_mem);
|
||||
bytes_read.* += length;
|
||||
return @as(Value, switch (tag) {
|
||||
.octet_string => .{ .octet_string = str_mem },
|
||||
.utf8_string => .{ .utf8_string = str_mem },
|
||||
.printable_string => .{ .printable_string = str_mem },
|
||||
.utc_time => .{ .utc_time = str_mem },
|
||||
.ia5_string => .{ .ia5_string = str_mem },
|
||||
else => unreachable,
|
||||
});
|
||||
},
|
||||
.@"null" => {
|
||||
std.debug.assert((try parse_length_internal(bytes_read, der_reader)) == 0x00);
|
||||
return .@"null";
|
||||
},
|
||||
.object_identifier => {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
const first_byte = try der_reader.readByte();
|
||||
var ret = Value{ .object_identifier = .{ .data = undefined, .len = 0 } };
|
||||
ret.object_identifier.data[0] = first_byte / 40;
|
||||
ret.object_identifier.data[1] = first_byte % 40;
|
||||
|
||||
var out_idx: u8 = 2;
|
||||
var i: usize = 0;
|
||||
while (i < length - 1) {
|
||||
var current_value: u32 = 0;
|
||||
var current_byte = try der_reader.readByte();
|
||||
i += 1;
|
||||
while (current_byte & 0x80 == 0x80) : (i += 1) {
|
||||
// Increase the base of the previous bytes
|
||||
current_value *= 128;
|
||||
// Add the current byte in base 128
|
||||
current_value += @as(u32, current_byte & ~@as(u8, 0x80)) * 128;
|
||||
current_byte = try der_reader.readByte();
|
||||
} else {
|
||||
current_value += current_byte;
|
||||
}
|
||||
ret.object_identifier.data[out_idx] = current_value;
|
||||
out_idx += 1;
|
||||
}
|
||||
ret.object_identifier.len = out_idx;
|
||||
std.debug.assert(out_idx <= 16);
|
||||
bytes_read.* += length;
|
||||
return ret;
|
||||
},
|
||||
.bmp_string => {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
const str_mem = try alloc.alloc(u16, @divExact(length, 2));
|
||||
errdefer alloc.free(str_mem);
|
||||
|
||||
for (str_mem) |*wide_char| {
|
||||
wide_char.* = try der_reader.readIntBig(u16);
|
||||
}
|
||||
bytes_read.* += length;
|
||||
return Value{ .bmp_string = str_mem };
|
||||
},
|
||||
.sequence, .set => {
|
||||
const length = try parse_length_internal(bytes_read, der_reader);
|
||||
var cur_read_bytes: usize = 0;
|
||||
var arr = std.ArrayList(Value).init(alloc);
|
||||
errdefer arr.deinit();
|
||||
|
||||
while (cur_read_bytes < length) {
|
||||
(try arr.addOne()).* = try parse_value_internal(alloc, &cur_read_bytes, der_reader);
|
||||
}
|
||||
if (cur_read_bytes != length)
|
||||
return error.InvalidContainerLength;
|
||||
bytes_read.* += length;
|
||||
|
||||
return @as(Value, switch (tag) {
|
||||
.sequence => .{ .sequence = arr.toOwnedSlice() },
|
||||
.set => .{ .set = arr.toOwnedSlice() },
|
||||
else => unreachable,
|
||||
});
|
||||
},
|
||||
.context_specific => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_value_internal(alloc: *Allocator, bytes_read: *usize, der_reader: anytype) DecodeError(@TypeOf(der_reader))!Value {
|
||||
const tag_byte = try der_reader.readByte();
|
||||
bytes_read.* += 1;
|
||||
return try parse_value_with_tag_byte(tag_byte, alloc, bytes_read, der_reader);
|
||||
}
|
||||
|
||||
pub fn parse_value(alloc: *Allocator, der_reader: anytype) DecodeError(@TypeOf(der_reader))!Value {
|
||||
var read: usize = 0;
|
||||
return try parse_value_internal(alloc, &read, der_reader);
|
||||
}
|
||||
};
|
||||
|
||||
test "der.parse_value" {
|
||||
const github_der = @embedFile("../test/github.der");
|
||||
var fbs = std.io.fixedBufferStream(github_der);
|
||||
|
||||
var arena = ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
_ = try der.parse_value(&arena.allocator, fbs.reader());
|
||||
}
|
617
src/deps/iguanaTLS/src/ciphersuites.zig
Normal file
617
src/deps/iguanaTLS/src/ciphersuites.zig
Normal file
|
@ -0,0 +1,617 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
usingnamespace @import("crypto.zig");
|
||||
const Chacha20Poly1305 = std.crypto.aead.chacha_poly.ChaCha20Poly1305;
|
||||
const Aes128Gcm = std.crypto.aead.aes_gcm.Aes128Gcm;
|
||||
|
||||
const main = @import("main.zig");
|
||||
const alert_byte_to_error = main.alert_byte_to_error;
|
||||
const record_tag_length = main.record_tag_length;
|
||||
const record_length = main.record_length;
|
||||
|
||||
pub const suites = struct {
|
||||
pub const ECDHE_RSA_Chacha20_Poly1305 = struct {
|
||||
pub const name = "ECDHE-RSA-CHACHA20-POLY1305";
|
||||
pub const tag = 0xCCA8;
|
||||
pub const key_exchange = .ecdhe;
|
||||
pub const hash = .sha256;
|
||||
|
||||
pub const Keys = struct {
|
||||
client_key: [32]u8,
|
||||
server_key: [32]u8,
|
||||
client_iv: [12]u8,
|
||||
server_iv: [12]u8,
|
||||
};
|
||||
|
||||
pub const State = union(enum) {
|
||||
none,
|
||||
in_record: struct {
|
||||
left: usize,
|
||||
context: ChaCha20Stream.BlockVec,
|
||||
idx: usize,
|
||||
buf: [64]u8,
|
||||
},
|
||||
};
|
||||
pub const default_state: State = .none;
|
||||
|
||||
pub fn raw_write(
|
||||
comptime buffer_size: usize,
|
||||
rand: *std.rand.Random,
|
||||
key_data: anytype,
|
||||
writer: anytype,
|
||||
prefix: [3]u8,
|
||||
seq: u64,
|
||||
buffer: []const u8,
|
||||
) !void {
|
||||
std.debug.assert(buffer.len <= buffer_size);
|
||||
try writer.writeAll(&prefix);
|
||||
try writer.writeIntBig(u16, @intCast(u16, buffer.len + 16));
|
||||
|
||||
var additional_data: [13]u8 = undefined;
|
||||
mem.writeIntBig(u64, additional_data[0..8], seq);
|
||||
additional_data[8..11].* = prefix;
|
||||
mem.writeIntBig(u16, additional_data[11..13], @intCast(u16, buffer.len));
|
||||
|
||||
var encrypted_data: [buffer_size]u8 = undefined;
|
||||
var tag_data: [16]u8 = undefined;
|
||||
|
||||
var nonce: [12]u8 = ([1]u8{0} ** 4) ++ ([1]u8{undefined} ** 8);
|
||||
mem.writeIntBig(u64, nonce[4..12], seq);
|
||||
for (nonce) |*n, i| {
|
||||
n.* ^= key_data.client_iv(@This())[i];
|
||||
}
|
||||
|
||||
Chacha20Poly1305.encrypt(
|
||||
encrypted_data[0..buffer.len],
|
||||
&tag_data,
|
||||
buffer,
|
||||
&additional_data,
|
||||
nonce,
|
||||
key_data.client_key(@This()).*,
|
||||
);
|
||||
try writer.writeAll(encrypted_data[0..buffer.len]);
|
||||
try writer.writeAll(&tag_data);
|
||||
}
|
||||
|
||||
pub fn check_verify_message(
|
||||
key_data: anytype,
|
||||
length: usize,
|
||||
reader: anytype,
|
||||
verify_message: [16]u8,
|
||||
) !bool {
|
||||
if (length != 32)
|
||||
return false;
|
||||
|
||||
var msg_in: [32]u8 = undefined;
|
||||
try reader.readNoEof(&msg_in);
|
||||
|
||||
const additional_data: [13]u8 = ([1]u8{0} ** 8) ++ [5]u8{ 0x16, 0x03, 0x03, 0x00, 0x10 };
|
||||
var decrypted: [16]u8 = undefined;
|
||||
Chacha20Poly1305.decrypt(
|
||||
&decrypted,
|
||||
msg_in[0..16],
|
||||
msg_in[16..].*,
|
||||
&additional_data,
|
||||
key_data.server_iv(@This()).*,
|
||||
key_data.server_key(@This()).*,
|
||||
) catch return false;
|
||||
|
||||
return mem.eql(u8, &decrypted, &verify_message);
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
comptime buf_size: usize,
|
||||
state: *State,
|
||||
key_data: anytype,
|
||||
reader: anytype,
|
||||
server_seq: *u64,
|
||||
buffer: []u8,
|
||||
) !usize {
|
||||
switch (state.*) {
|
||||
.none => {
|
||||
const tag_length = record_tag_length(reader) catch |err| switch (err) {
|
||||
error.EndOfStream => return 0,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (tag_length.length < 16)
|
||||
return error.ServerMalformedResponse;
|
||||
const len = tag_length.length - 16;
|
||||
|
||||
if ((tag_length.tag != 0x17 and tag_length.tag != 0x15) or
|
||||
(tag_length.tag == 0x15 and len != 2))
|
||||
{
|
||||
return error.ServerMalformedResponse;
|
||||
}
|
||||
|
||||
const curr_bytes = if (tag_length.tag == 0x15)
|
||||
2
|
||||
else
|
||||
std.math.min(std.math.min(len, buf_size), buffer.len);
|
||||
|
||||
var nonce: [12]u8 = ([1]u8{0} ** 4) ++ ([1]u8{undefined} ** 8);
|
||||
mem.writeIntBig(u64, nonce[4..12], server_seq.*);
|
||||
for (nonce) |*n, i| {
|
||||
n.* ^= key_data.server_iv(@This())[i];
|
||||
}
|
||||
|
||||
var c: [4]u32 = undefined;
|
||||
c[0] = 1;
|
||||
c[1] = mem.readIntLittle(u32, nonce[0..4]);
|
||||
c[2] = mem.readIntLittle(u32, nonce[4..8]);
|
||||
c[3] = mem.readIntLittle(u32, nonce[8..12]);
|
||||
const server_key = keyToWords(key_data.server_key(@This()).*);
|
||||
var context = ChaCha20Stream.initContext(server_key, c);
|
||||
var idx: usize = 0;
|
||||
var buf: [64]u8 = undefined;
|
||||
|
||||
if (tag_length.tag == 0x15) {
|
||||
var encrypted: [2]u8 = undefined;
|
||||
reader.readNoEof(&encrypted) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
var result: [2]u8 = undefined;
|
||||
ChaCha20Stream.chacha20Xor(
|
||||
&result,
|
||||
&encrypted,
|
||||
server_key,
|
||||
&context,
|
||||
&idx,
|
||||
&buf,
|
||||
);
|
||||
reader.skipBytes(16, .{}) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
server_seq.* += 1;
|
||||
// CloseNotify
|
||||
if (result[1] == 0)
|
||||
return 0;
|
||||
return alert_byte_to_error(result[1]);
|
||||
} else if (tag_length.tag == 0x17) {
|
||||
// Partially decrypt the data.
|
||||
var encrypted: [buf_size]u8 = undefined;
|
||||
const actually_read = try reader.read(encrypted[0..curr_bytes]);
|
||||
|
||||
ChaCha20Stream.chacha20Xor(
|
||||
buffer[0..actually_read],
|
||||
encrypted[0..actually_read],
|
||||
server_key,
|
||||
&context,
|
||||
&idx,
|
||||
&buf,
|
||||
);
|
||||
if (actually_read < len) {
|
||||
state.* = .{
|
||||
.in_record = .{
|
||||
.left = len - actually_read,
|
||||
.context = context,
|
||||
.idx = idx,
|
||||
.buf = buf,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// @TODO Verify Poly1305.
|
||||
reader.skipBytes(16, .{}) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
server_seq.* += 1;
|
||||
}
|
||||
return actually_read;
|
||||
} else unreachable;
|
||||
},
|
||||
.in_record => |*record_info| {
|
||||
const curr_bytes = std.math.min(std.math.min(buf_size, buffer.len), record_info.left);
|
||||
// Partially decrypt the data.
|
||||
var encrypted: [buf_size]u8 = undefined;
|
||||
const actually_read = try reader.read(encrypted[0..curr_bytes]);
|
||||
ChaCha20Stream.chacha20Xor(
|
||||
buffer[0..actually_read],
|
||||
encrypted[0..actually_read],
|
||||
keyToWords(key_data.server_key(@This()).*),
|
||||
&record_info.context,
|
||||
&record_info.idx,
|
||||
&record_info.buf,
|
||||
);
|
||||
|
||||
record_info.left -= actually_read;
|
||||
if (record_info.left == 0) {
|
||||
// @TODO Verify Poly1305.
|
||||
reader.skipBytes(16, .{}) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
state.* = .none;
|
||||
server_seq.* += 1;
|
||||
}
|
||||
return actually_read;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const ECDHE_RSA_AES128_GCM_SHA256 = struct {
|
||||
pub const name = "ECDHE-RSA-AES128-GCM-SHA256";
|
||||
pub const tag = 0xC02F;
|
||||
pub const key_exchange = .ecdhe;
|
||||
pub const hash = .sha256;
|
||||
|
||||
pub const Keys = struct {
|
||||
client_key: [16]u8,
|
||||
server_key: [16]u8,
|
||||
client_iv: [4]u8,
|
||||
server_iv: [4]u8,
|
||||
};
|
||||
|
||||
const Aes = std.crypto.core.aes.Aes128;
|
||||
pub const State = union(enum) {
|
||||
none,
|
||||
in_record: struct {
|
||||
left: usize,
|
||||
aes: @typeInfo(@TypeOf(Aes.initEnc)).Fn.return_type.?,
|
||||
// ctr state
|
||||
counterInt: u128,
|
||||
idx: usize,
|
||||
},
|
||||
};
|
||||
pub const default_state: State = .none;
|
||||
|
||||
pub fn check_verify_message(
|
||||
key_data: anytype,
|
||||
length: usize,
|
||||
reader: anytype,
|
||||
verify_message: [16]u8,
|
||||
) !bool {
|
||||
if (length != 40)
|
||||
return false;
|
||||
|
||||
var iv: [12]u8 = undefined;
|
||||
iv[0..4].* = key_data.server_iv(@This()).*;
|
||||
try reader.readNoEof(iv[4..12]);
|
||||
|
||||
var msg_in: [32]u8 = undefined;
|
||||
try reader.readNoEof(&msg_in);
|
||||
|
||||
const additional_data: [13]u8 = ([1]u8{0} ** 8) ++ [5]u8{ 0x16, 0x03, 0x03, 0x00, 0x10 };
|
||||
var decrypted: [16]u8 = undefined;
|
||||
Aes128Gcm.decrypt(
|
||||
&decrypted,
|
||||
msg_in[0..16],
|
||||
msg_in[16..].*,
|
||||
&additional_data,
|
||||
iv,
|
||||
key_data.server_key(@This()).*,
|
||||
) catch return false;
|
||||
|
||||
return mem.eql(u8, &decrypted, &verify_message);
|
||||
}
|
||||
|
||||
pub fn raw_write(
|
||||
comptime buffer_size: usize,
|
||||
rand: *std.rand.Random,
|
||||
key_data: anytype,
|
||||
writer: anytype,
|
||||
prefix: [3]u8,
|
||||
seq: u64,
|
||||
buffer: []const u8,
|
||||
) !void {
|
||||
std.debug.assert(buffer.len <= buffer_size);
|
||||
var iv: [12]u8 = undefined;
|
||||
iv[0..4].* = key_data.client_iv(@This()).*;
|
||||
rand.bytes(iv[4..12]);
|
||||
|
||||
var additional_data: [13]u8 = undefined;
|
||||
mem.writeIntBig(u64, additional_data[0..8], seq);
|
||||
additional_data[8..11].* = prefix;
|
||||
mem.writeIntBig(u16, additional_data[11..13], @intCast(u16, buffer.len));
|
||||
|
||||
try writer.writeAll(&prefix);
|
||||
try writer.writeIntBig(u16, @intCast(u16, buffer.len + 24));
|
||||
try writer.writeAll(iv[4..12]);
|
||||
|
||||
var encrypted_data: [buffer_size]u8 = undefined;
|
||||
var tag_data: [16]u8 = undefined;
|
||||
|
||||
Aes128Gcm.encrypt(
|
||||
encrypted_data[0..buffer.len],
|
||||
&tag_data,
|
||||
buffer,
|
||||
&additional_data,
|
||||
iv,
|
||||
key_data.client_key(@This()).*,
|
||||
);
|
||||
try writer.writeAll(encrypted_data[0..buffer.len]);
|
||||
try writer.writeAll(&tag_data);
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
comptime buf_size: usize,
|
||||
state: *State,
|
||||
key_data: anytype,
|
||||
reader: anytype,
|
||||
server_seq: *u64,
|
||||
buffer: []u8,
|
||||
) !usize {
|
||||
switch (state.*) {
|
||||
.none => {
|
||||
const tag_length = record_tag_length(reader) catch |err| switch (err) {
|
||||
error.EndOfStream => return 0,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (tag_length.length < 24)
|
||||
return error.ServerMalformedResponse;
|
||||
const len = tag_length.length - 24;
|
||||
|
||||
if ((tag_length.tag != 0x17 and tag_length.tag != 0x15) or
|
||||
(tag_length.tag == 0x15 and len != 2))
|
||||
{
|
||||
return error.ServerMalformedResponse;
|
||||
}
|
||||
|
||||
const curr_bytes = if (tag_length.tag == 0x15)
|
||||
2
|
||||
else
|
||||
std.math.min(std.math.min(len, buf_size), buffer.len);
|
||||
|
||||
var iv: [12]u8 = undefined;
|
||||
iv[0..4].* = key_data.server_iv(@This()).*;
|
||||
reader.readNoEof(iv[4..12]) catch |err| switch (err) {
|
||||
error.EndOfStream => return 0,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
const aes = Aes.initEnc(key_data.server_key(@This()).*);
|
||||
|
||||
var j: [16]u8 = undefined;
|
||||
mem.copy(u8, j[0..12], iv[0..]);
|
||||
mem.writeIntBig(u32, j[12..][0..4], 2);
|
||||
|
||||
var counterInt = mem.readInt(u128, &j, .Big);
|
||||
var idx: usize = 0;
|
||||
|
||||
if (tag_length.tag == 0x15) {
|
||||
var encrypted: [2]u8 = undefined;
|
||||
reader.readNoEof(&encrypted) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
var result: [2]u8 = undefined;
|
||||
ctr(
|
||||
@TypeOf(aes),
|
||||
aes,
|
||||
&result,
|
||||
&encrypted,
|
||||
&counterInt,
|
||||
&idx,
|
||||
.Big,
|
||||
);
|
||||
reader.skipBytes(16, .{}) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
server_seq.* += 1;
|
||||
// CloseNotify
|
||||
if (result[1] == 0)
|
||||
return 0;
|
||||
return alert_byte_to_error(result[1]);
|
||||
} else if (tag_length.tag == 0x17) {
|
||||
// Partially decrypt the data.
|
||||
var encrypted: [buf_size]u8 = undefined;
|
||||
const actually_read = try reader.read(encrypted[0..curr_bytes]);
|
||||
|
||||
ctr(
|
||||
@TypeOf(aes),
|
||||
aes,
|
||||
buffer[0..actually_read],
|
||||
encrypted[0..actually_read],
|
||||
&counterInt,
|
||||
&idx,
|
||||
.Big,
|
||||
);
|
||||
|
||||
if (actually_read < len) {
|
||||
state.* = .{
|
||||
.in_record = .{
|
||||
.left = len - actually_read,
|
||||
.aes = aes,
|
||||
.counterInt = counterInt,
|
||||
.idx = idx,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// @TODO Verify the message
|
||||
reader.skipBytes(16, .{}) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
server_seq.* += 1;
|
||||
}
|
||||
return actually_read;
|
||||
} else unreachable;
|
||||
},
|
||||
.in_record => |*record_info| {
|
||||
const curr_bytes = std.math.min(std.math.min(buf_size, buffer.len), record_info.left);
|
||||
// Partially decrypt the data.
|
||||
var encrypted: [buf_size]u8 = undefined;
|
||||
const actually_read = try reader.read(encrypted[0..curr_bytes]);
|
||||
|
||||
ctr(
|
||||
@TypeOf(record_info.aes),
|
||||
record_info.aes,
|
||||
buffer[0..actually_read],
|
||||
encrypted[0..actually_read],
|
||||
&record_info.counterInt,
|
||||
&record_info.idx,
|
||||
.Big,
|
||||
);
|
||||
record_info.left -= actually_read;
|
||||
if (record_info.left == 0) {
|
||||
// @TODO Verify Poly1305.
|
||||
reader.skipBytes(16, .{}) catch |err| switch (err) {
|
||||
error.EndOfStream => return error.ServerMalformedResponse,
|
||||
else => |e| return e,
|
||||
};
|
||||
state.* = .none;
|
||||
server_seq.* += 1;
|
||||
}
|
||||
return actually_read;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const all = &[_]type{ ECDHE_RSA_Chacha20_Poly1305, ECDHE_RSA_AES128_GCM_SHA256 };
|
||||
};
|
||||
|
||||
fn key_field_width(comptime T: type, comptime field: anytype) ?usize {
|
||||
if (!@hasField(T, @tagName(field)))
|
||||
return null;
|
||||
|
||||
const field_info = std.meta.fieldInfo(T, field);
|
||||
if (!comptime std.meta.trait.is(.Array)(field_info.field_type) or std.meta.Elem(field_info.field_type) != u8)
|
||||
@compileError("Field '" ++ field ++ "' of type '" ++ @typeName(T) ++ "' should be an array of u8.");
|
||||
|
||||
return @typeInfo(field_info.field_type).Array.len;
|
||||
}
|
||||
|
||||
pub fn key_data_size(comptime ciphersuites: anytype) usize {
|
||||
var max: usize = 0;
|
||||
for (ciphersuites) |cs| {
|
||||
const curr = (key_field_width(cs.Keys, .client_mac) orelse 0) +
|
||||
(key_field_width(cs.Keys, .server_mac) orelse 0) +
|
||||
key_field_width(cs.Keys, .client_key).? +
|
||||
key_field_width(cs.Keys, .server_key).? +
|
||||
key_field_width(cs.Keys, .client_iv).? +
|
||||
key_field_width(cs.Keys, .server_iv).?;
|
||||
if (curr > max)
|
||||
max = curr;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
pub fn KeyData(comptime ciphersuites: anytype) type {
|
||||
return struct {
|
||||
data: [key_data_size(ciphersuites)]u8,
|
||||
|
||||
pub fn client_mac(self: *@This(), comptime cs: type) *[key_field_width(cs.Keys, .client_mac) orelse 0]u8 {
|
||||
return self.data[0..comptime (key_field_width(cs.Keys, .client_mac) orelse 0)];
|
||||
}
|
||||
|
||||
pub fn server_mac(self: *@This(), comptime cs: type) *[key_field_width(cs.Keys, .server_mac) orelse 0]u8 {
|
||||
const start = key_field_width(cs.Keys, .client_mac) orelse 0;
|
||||
return self.data[start..][0..comptime (key_field_width(cs.Keys, .server_mac) orelse 0)];
|
||||
}
|
||||
|
||||
pub fn client_key(self: *@This(), comptime cs: type) *[key_field_width(cs.Keys, .client_key).?]u8 {
|
||||
const start = (key_field_width(cs.Keys, .client_mac) orelse 0) +
|
||||
(key_field_width(cs.Keys, .server_mac) orelse 0);
|
||||
return self.data[start..][0..comptime key_field_width(cs.Keys, .client_key).?];
|
||||
}
|
||||
|
||||
pub fn server_key(self: *@This(), comptime cs: type) *[key_field_width(cs.Keys, .server_key).?]u8 {
|
||||
const start = (key_field_width(cs.Keys, .client_mac) orelse 0) +
|
||||
(key_field_width(cs.Keys, .server_mac) orelse 0) +
|
||||
key_field_width(cs.Keys, .client_key).?;
|
||||
return self.data[start..][0..comptime key_field_width(cs.Keys, .server_key).?];
|
||||
}
|
||||
|
||||
pub fn client_iv(self: *@This(), comptime cs: type) *[key_field_width(cs.Keys, .client_iv).?]u8 {
|
||||
const start = (key_field_width(cs.Keys, .client_mac) orelse 0) +
|
||||
(key_field_width(cs.Keys, .server_mac) orelse 0) +
|
||||
key_field_width(cs.Keys, .client_key).? +
|
||||
key_field_width(cs.Keys, .server_key).?;
|
||||
return self.data[start..][0..comptime key_field_width(cs.Keys, .client_iv).?];
|
||||
}
|
||||
|
||||
pub fn server_iv(self: *@This(), comptime cs: type) *[key_field_width(cs.Keys, .server_iv).?]u8 {
|
||||
const start = (key_field_width(cs.Keys, .client_mac) orelse 0) +
|
||||
(key_field_width(cs.Keys, .server_mac) orelse 0) +
|
||||
key_field_width(cs.Keys, .client_key).? +
|
||||
key_field_width(cs.Keys, .server_key).? +
|
||||
key_field_width(cs.Keys, .client_iv).?;
|
||||
return self.data[start..][0..comptime key_field_width(cs.Keys, .server_iv).?];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn key_expansion(
|
||||
comptime ciphersuites: anytype,
|
||||
tag: u16,
|
||||
context: anytype,
|
||||
comptime next_32_bytes: anytype,
|
||||
) KeyData(ciphersuites) {
|
||||
var res: KeyData(ciphersuites) = undefined;
|
||||
inline for (ciphersuites) |cs| {
|
||||
if (cs.tag == tag) {
|
||||
var chunk: [32]u8 = undefined;
|
||||
next_32_bytes(context, 0, &chunk);
|
||||
comptime var chunk_idx = 1;
|
||||
comptime var data_cursor = 0;
|
||||
comptime var chunk_cursor = 0;
|
||||
|
||||
const fields = .{
|
||||
.client_mac, .server_mac,
|
||||
.client_key, .server_key,
|
||||
.client_iv, .server_iv,
|
||||
};
|
||||
inline for (fields) |field| {
|
||||
if (chunk_cursor == 32) {
|
||||
next_32_bytes(context, chunk_idx, &chunk);
|
||||
chunk_idx += 1;
|
||||
chunk_cursor = 0;
|
||||
}
|
||||
|
||||
const field_width = comptime (key_field_width(cs.Keys, field) orelse 0);
|
||||
const first_read = comptime std.math.min(32 - chunk_cursor, field_width);
|
||||
const second_read = field_width - first_read;
|
||||
|
||||
res.data[data_cursor..][0..first_read].* = chunk[chunk_cursor..][0..first_read].*;
|
||||
data_cursor += first_read;
|
||||
chunk_cursor += first_read;
|
||||
|
||||
if (second_read != 0) {
|
||||
next_32_bytes(context, chunk_idx, &chunk);
|
||||
chunk_idx += 1;
|
||||
res.data[data_cursor..][0..second_read].* = chunk[chunk_cursor..][0..second_read].*;
|
||||
data_cursor += second_read;
|
||||
chunk_cursor = second_read;
|
||||
comptime std.debug.assert(chunk_cursor != 32);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn ClientState(comptime ciphersuites: anytype) type {
|
||||
var fields: [ciphersuites.len]std.builtin.TypeInfo.UnionField = undefined;
|
||||
for (ciphersuites) |cs, i| {
|
||||
fields[i] = .{
|
||||
.name = cs.name,
|
||||
.field_type = cs.State,
|
||||
.alignment = if (@sizeOf(cs.State) > 0) @alignOf(cs.State) else 0,
|
||||
};
|
||||
}
|
||||
return @Type(.{
|
||||
.Union = .{
|
||||
.layout = .Extern,
|
||||
.tag_type = null,
|
||||
.fields = &fields,
|
||||
.decls = &[0]std.builtin.TypeInfo.Declaration{},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn client_state_default(comptime ciphersuites: anytype, tag: u16) ClientState(ciphersuites) {
|
||||
inline for (ciphersuites) |cs| {
|
||||
if (cs.tag == tag) {
|
||||
return @unionInit(ClientState(ciphersuites), cs.name, cs.default_state);
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
980
src/deps/iguanaTLS/src/crypto.zig
Normal file
980
src/deps/iguanaTLS/src/crypto.zig
Normal file
|
@ -0,0 +1,980 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
// TODO See stdlib, this is a modified non vectorized implementation
|
||||
pub const ChaCha20Stream = struct {
|
||||
const math = std.math;
|
||||
pub const BlockVec = [16]u32;
|
||||
|
||||
pub fn initContext(key: [8]u32, d: [4]u32) BlockVec {
|
||||
const c = "expand 32-byte k";
|
||||
const constant_le = comptime [4]u32{
|
||||
mem.readIntLittle(u32, c[0..4]),
|
||||
mem.readIntLittle(u32, c[4..8]),
|
||||
mem.readIntLittle(u32, c[8..12]),
|
||||
mem.readIntLittle(u32, c[12..16]),
|
||||
};
|
||||
return BlockVec{
|
||||
constant_le[0], constant_le[1], constant_le[2], constant_le[3],
|
||||
key[0], key[1], key[2], key[3],
|
||||
key[4], key[5], key[6], key[7],
|
||||
d[0], d[1], d[2], d[3],
|
||||
};
|
||||
}
|
||||
|
||||
const QuarterRound = struct {
|
||||
a: usize,
|
||||
b: usize,
|
||||
c: usize,
|
||||
d: usize,
|
||||
};
|
||||
|
||||
fn Rp(a: usize, b: usize, c: usize, d: usize) QuarterRound {
|
||||
return QuarterRound{
|
||||
.a = a,
|
||||
.b = b,
|
||||
.c = c,
|
||||
.d = d,
|
||||
};
|
||||
}
|
||||
|
||||
fn chacha20Core(x: *BlockVec, input: BlockVec) callconv(.Inline) void {
|
||||
x.* = input;
|
||||
|
||||
const rounds = comptime [_]QuarterRound{
|
||||
Rp(0, 4, 8, 12),
|
||||
Rp(1, 5, 9, 13),
|
||||
Rp(2, 6, 10, 14),
|
||||
Rp(3, 7, 11, 15),
|
||||
Rp(0, 5, 10, 15),
|
||||
Rp(1, 6, 11, 12),
|
||||
Rp(2, 7, 8, 13),
|
||||
Rp(3, 4, 9, 14),
|
||||
};
|
||||
|
||||
comptime var j: usize = 0;
|
||||
inline while (j < 20) : (j += 2) {
|
||||
inline for (rounds) |r| {
|
||||
x[r.a] +%= x[r.b];
|
||||
x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 16));
|
||||
x[r.c] +%= x[r.d];
|
||||
x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 12));
|
||||
x[r.a] +%= x[r.b];
|
||||
x[r.d] = math.rotl(u32, x[r.d] ^ x[r.a], @as(u32, 8));
|
||||
x[r.c] +%= x[r.d];
|
||||
x[r.b] = math.rotl(u32, x[r.b] ^ x[r.c], @as(u32, 7));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hashToBytes(out: *[64]u8, x: BlockVec) callconv(.Inline) void {
|
||||
var i: usize = 0;
|
||||
while (i < 4) : (i += 1) {
|
||||
mem.writeIntLittle(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0]);
|
||||
mem.writeIntLittle(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1]);
|
||||
mem.writeIntLittle(u32, out[16 * i + 8 ..][0..4], x[i * 4 + 2]);
|
||||
mem.writeIntLittle(u32, out[16 * i + 12 ..][0..4], x[i * 4 + 3]);
|
||||
}
|
||||
}
|
||||
|
||||
fn contextFeedback(x: *BlockVec, ctx: BlockVec) callconv(.Inline) void {
|
||||
var i: usize = 0;
|
||||
while (i < 16) : (i += 1) {
|
||||
x[i] +%= ctx[i];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Optimize this
|
||||
pub fn chacha20Xor(out: []u8, in: []const u8, key: [8]u32, ctx: *BlockVec, idx: *usize, buf: *[64]u8) void {
|
||||
var x: BlockVec = undefined;
|
||||
|
||||
const start_idx = idx.*;
|
||||
var i: usize = 0;
|
||||
while (i < in.len) {
|
||||
if (idx.* % 64 == 0) {
|
||||
if (idx.* != 0) {
|
||||
ctx.*[12] += 1;
|
||||
}
|
||||
chacha20Core(x[0..], ctx.*);
|
||||
contextFeedback(&x, ctx.*);
|
||||
hashToBytes(buf, x);
|
||||
}
|
||||
|
||||
out[i] = in[i] ^ buf[idx.* % 64];
|
||||
|
||||
i += 1;
|
||||
idx.* += 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn keyToWords(key: [32]u8) [8]u32 {
|
||||
var k: [8]u32 = undefined;
|
||||
var i: usize = 0;
|
||||
while (i < 8) : (i += 1) {
|
||||
k[i] = mem.readIntLittle(u32, key[i * 4 ..][0..4]);
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
// See std.crypto.core.modes.ctr
|
||||
/// This mode creates a key stream by encrypting an incrementing counter using a block cipher, and adding it to the source material.
|
||||
pub fn ctr(
|
||||
comptime BlockCipher: anytype,
|
||||
block_cipher: BlockCipher,
|
||||
dst: []u8,
|
||||
src: []const u8,
|
||||
counterInt: *u128,
|
||||
idx: *usize,
|
||||
endian: comptime std.builtin.Endian,
|
||||
) void {
|
||||
std.debug.assert(dst.len >= src.len);
|
||||
const block_length = BlockCipher.block_length;
|
||||
var cur_idx: usize = 0;
|
||||
|
||||
const offset = idx.* % block_length;
|
||||
if (offset != 0) {
|
||||
const part_len = std.math.min(block_length - offset, src.len);
|
||||
|
||||
var counter: [BlockCipher.block_length]u8 = undefined;
|
||||
mem.writeInt(u128, &counter, counterInt.*, endian);
|
||||
var pad = [_]u8{0} ** block_length;
|
||||
mem.copy(u8, pad[offset..], src[0..part_len]);
|
||||
block_cipher.xor(&pad, &pad, counter);
|
||||
mem.copy(u8, dst[0..part_len], pad[offset..][0..part_len]);
|
||||
cur_idx += part_len;
|
||||
idx.* += part_len;
|
||||
if (idx.* % block_length == 0)
|
||||
counterInt.* += 1;
|
||||
}
|
||||
|
||||
const start_idx = cur_idx;
|
||||
const remaining = src.len - cur_idx;
|
||||
cur_idx = 0;
|
||||
|
||||
const parallel_count = BlockCipher.block.parallel.optimal_parallel_blocks;
|
||||
const wide_block_length = parallel_count * 16;
|
||||
if (remaining >= wide_block_length) {
|
||||
var counters: [parallel_count * 16]u8 = undefined;
|
||||
while (cur_idx + wide_block_length <= remaining) : (cur_idx += wide_block_length) {
|
||||
comptime var j = 0;
|
||||
inline while (j < parallel_count) : (j += 1) {
|
||||
mem.writeInt(u128, counters[j * 16 .. j * 16 + 16], counterInt.*, endian);
|
||||
counterInt.* +%= 1;
|
||||
}
|
||||
block_cipher.xorWide(parallel_count, dst[start_idx..][cur_idx .. cur_idx + wide_block_length][0..wide_block_length], src[start_idx..][cur_idx .. cur_idx + wide_block_length][0..wide_block_length], counters);
|
||||
idx.* += wide_block_length;
|
||||
}
|
||||
}
|
||||
while (cur_idx + block_length <= remaining) : (cur_idx += block_length) {
|
||||
var counter: [BlockCipher.block_length]u8 = undefined;
|
||||
mem.writeInt(u128, &counter, counterInt.*, endian);
|
||||
counterInt.* +%= 1;
|
||||
block_cipher.xor(dst[start_idx..][cur_idx .. cur_idx + block_length][0..block_length], src[start_idx..][cur_idx .. cur_idx + block_length][0..block_length], counter);
|
||||
idx.* += block_length;
|
||||
}
|
||||
if (cur_idx < remaining) {
|
||||
std.debug.assert(idx.* % block_length == 0);
|
||||
var counter: [BlockCipher.block_length]u8 = undefined;
|
||||
mem.writeInt(u128, &counter, counterInt.*, endian);
|
||||
|
||||
var pad = [_]u8{0} ** block_length;
|
||||
mem.copy(u8, &pad, src[start_idx..][cur_idx..]);
|
||||
block_cipher.xor(&pad, &pad, counter);
|
||||
mem.copy(u8, dst[start_idx..][cur_idx..], pad[0 .. remaining - cur_idx]);
|
||||
|
||||
idx.* += remaining - cur_idx;
|
||||
if (idx.* % block_length == 0)
|
||||
counterInt.* +%= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Ported from BearSSL's ec_prime_i31 engine
|
||||
pub const ecc = struct {
|
||||
pub const SECP384R1 = struct {
|
||||
pub const point_len = 96;
|
||||
|
||||
const order = [point_len / 2]u8{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF,
|
||||
0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A,
|
||||
0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73,
|
||||
};
|
||||
|
||||
const P = [_]u32{
|
||||
0x0000018C, 0x7FFFFFFF, 0x00000001, 0x00000000,
|
||||
0x7FFFFFF8, 0x7FFFFFEF, 0x7FFFFFFF, 0x7FFFFFFF,
|
||||
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF,
|
||||
0x7FFFFFFF, 0x00000FFF,
|
||||
};
|
||||
const R2 = [_]u32{
|
||||
0x0000018C, 0x00000000, 0x00000080, 0x7FFFFE00,
|
||||
0x000001FF, 0x00000800, 0x00000000, 0x7FFFE000,
|
||||
0x00001FFF, 0x00008000, 0x00008000, 0x00000000,
|
||||
0x00000000, 0x00000000,
|
||||
};
|
||||
const B = [_]u32{
|
||||
0x0000018C, 0x6E666840, 0x070D0392, 0x5D810231,
|
||||
0x7651D50C, 0x17E218D6, 0x1B192002, 0x44EFE441,
|
||||
0x3A524E2B, 0x2719BA5F, 0x41F02209, 0x36C5643E,
|
||||
0x5813EFFE, 0x000008A5,
|
||||
};
|
||||
|
||||
const base_point = [point_len]u8{
|
||||
0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, 0x37,
|
||||
0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, 0x74,
|
||||
0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, 0x98,
|
||||
0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, 0x38,
|
||||
0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, 0x6C,
|
||||
0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, 0xB7,
|
||||
0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, 0x6F,
|
||||
0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, 0x29,
|
||||
0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, 0x7C,
|
||||
0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, 0xC0,
|
||||
0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, 0x9D,
|
||||
0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, 0x5F,
|
||||
};
|
||||
|
||||
comptime {
|
||||
std.debug.assert((P[0] - (P[0] >> 5) + 7) >> 2 == point_len + 1);
|
||||
}
|
||||
};
|
||||
|
||||
pub const SECP256R1 = struct {
|
||||
pub const point_len = 64;
|
||||
|
||||
const order = [point_len / 2]u8{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
|
||||
0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51,
|
||||
};
|
||||
|
||||
const P = [_]u32{
|
||||
0x00000108, 0x7FFFFFFF,
|
||||
0x7FFFFFFF, 0x7FFFFFFF,
|
||||
0x00000007, 0x00000000,
|
||||
0x00000000, 0x00000040,
|
||||
0x7FFFFF80, 0x000000FF,
|
||||
};
|
||||
const R2 = [_]u32{
|
||||
0x00000108, 0x00014000,
|
||||
0x00018000, 0x00000000,
|
||||
0x7FF40000, 0x7FEFFFFF,
|
||||
0x7FF7FFFF, 0x7FAFFFFF,
|
||||
0x005FFFFF, 0x00000000,
|
||||
};
|
||||
const B = [_]u32{
|
||||
0x00000108, 0x6FEE1803,
|
||||
0x6229C4BD, 0x21B139BE,
|
||||
0x327150AA, 0x3567802E,
|
||||
0x3F7212ED, 0x012E4355,
|
||||
0x782DD38D, 0x0000000E,
|
||||
};
|
||||
|
||||
const base_point = [point_len]u8{
|
||||
0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, 0x47,
|
||||
0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, 0xF2,
|
||||
0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, 0xA0,
|
||||
0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, 0x96,
|
||||
0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, 0x9B,
|
||||
0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, 0x16,
|
||||
0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, 0xCE,
|
||||
0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, 0xF5,
|
||||
};
|
||||
|
||||
comptime {
|
||||
std.debug.assert((P[0] - (P[0] >> 5) + 7) >> 2 == point_len + 1);
|
||||
}
|
||||
};
|
||||
|
||||
fn jacobian_len(comptime Curve: type) usize {
|
||||
return @divTrunc(Curve.order.len * 8 + 61, 31);
|
||||
}
|
||||
|
||||
fn Jacobian(comptime Curve: type) type {
|
||||
return [3][jacobian_len(Curve)]u32;
|
||||
}
|
||||
|
||||
fn zero_jacobian(comptime Curve: type) Jacobian(Curve) {
|
||||
var result = std.mem.zeroes(Jacobian(Curve));
|
||||
result[0][0] = Curve.P[0];
|
||||
result[1][0] = Curve.P[0];
|
||||
result[2][0] = Curve.P[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn scalarmult(
|
||||
comptime Curve: type,
|
||||
point: [Curve.point_len]u8,
|
||||
k: []const u8,
|
||||
) ![Curve.point_len]u8 {
|
||||
var P: Jacobian(Curve) = undefined;
|
||||
var res: u32 = decode_to_jacobian(Curve, &P, point);
|
||||
point_mul(Curve, &P, k);
|
||||
var out: [Curve.point_len]u8 = undefined;
|
||||
encode_from_jacobian(Curve, &out, P);
|
||||
if (res == 0)
|
||||
return error.MultiplicationFailed;
|
||||
return out;
|
||||
}
|
||||
|
||||
pub fn KeyPair(comptime Curve: type) type {
|
||||
return struct {
|
||||
public_key: [Curve.point_len]u8,
|
||||
secret_key: [Curve.point_len / 2]u8,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn make_key_pair(comptime Curve: type, rand_bytes: [Curve.point_len / 2]u8) KeyPair(Curve) {
|
||||
var key_bytes = rand_bytes;
|
||||
comptime var mask: u8 = 0xFF;
|
||||
comptime {
|
||||
while (mask >= Curve.order[0]) {
|
||||
mask >>= 1;
|
||||
}
|
||||
}
|
||||
key_bytes[0] &= mask;
|
||||
key_bytes[Curve.point_len / 2 - 1] |= 0x01;
|
||||
|
||||
return .{
|
||||
.secret_key = key_bytes,
|
||||
.public_key = scalarmult(Curve, Curve.base_point, &key_bytes) catch unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
fn jacobian_with_one_set(comptime Curve: type, comptime fields: [2][jacobian_len(Curve)]u32) Jacobian(Curve) {
|
||||
comptime const plen = (Curve.P[0] + 63) >> 5;
|
||||
return fields ++ [1][jacobian_len(Curve)]u32{
|
||||
[2]u32{ Curve.P[0], 1 } ++ ([1]u32{0} ** (plen - 2)),
|
||||
};
|
||||
}
|
||||
|
||||
fn encode_from_jacobian(comptime Curve: type, point: *[Curve.point_len]u8, P: Jacobian(Curve)) void {
|
||||
var Q = P;
|
||||
const T = comptime jacobian_with_one_set(Curve, [2][jacobian_len(Curve)]u32{ undefined, undefined });
|
||||
_ = run_code(Curve, &Q, T, &code.affine);
|
||||
encode_jacobian_part(point[0 .. Curve.point_len / 2], &Q[0]);
|
||||
encode_jacobian_part(point[Curve.point_len / 2 ..], &Q[1]);
|
||||
}
|
||||
|
||||
fn point_mul(comptime Curve: type, P: *Jacobian(Curve), x: []const u8) void {
|
||||
var P2 = P.*;
|
||||
point_double(Curve, &P2);
|
||||
var P3 = P.*;
|
||||
point_add(Curve, &P3, P2);
|
||||
var Q = zero_jacobian(Curve);
|
||||
var qz: u32 = 1;
|
||||
var xlen = x.len;
|
||||
var xidx: usize = 0;
|
||||
while (xlen > 0) : ({
|
||||
xlen -= 1;
|
||||
xidx += 1;
|
||||
}) {
|
||||
var k: u3 = 6;
|
||||
while (true) : (k -= 2) {
|
||||
point_double(Curve, &Q);
|
||||
point_double(Curve, &Q);
|
||||
var T = P.*;
|
||||
var U = Q;
|
||||
const bits = @as(u32, x[xidx] >> k) & 3;
|
||||
const bnz = NEQ(bits, 0);
|
||||
CCOPY(EQ(bits, 2), mem.asBytes(&T), mem.asBytes(&P2));
|
||||
CCOPY(EQ(bits, 3), mem.asBytes(&T), mem.asBytes(&P3));
|
||||
point_add(Curve, &U, T);
|
||||
CCOPY(bnz & qz, mem.asBytes(&Q), mem.asBytes(&T));
|
||||
CCOPY(bnz & ~qz, mem.asBytes(&Q), mem.asBytes(&U));
|
||||
qz &= ~bnz;
|
||||
|
||||
if (k == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
P.* = Q;
|
||||
}
|
||||
|
||||
fn point_double(comptime Curve: type, P: *Jacobian(Curve)) callconv(.Inline) void {
|
||||
_ = run_code(Curve, P, P.*, &code.double);
|
||||
}
|
||||
fn point_add(comptime Curve: type, P1: *Jacobian(Curve), P2: Jacobian(Curve)) callconv(.Inline) void {
|
||||
_ = run_code(Curve, P1, P2, &code._add);
|
||||
}
|
||||
|
||||
fn decode_to_jacobian(
|
||||
comptime Curve: type,
|
||||
out: *Jacobian(Curve),
|
||||
point: [Curve.point_len]u8,
|
||||
) u32 {
|
||||
out.* = zero_jacobian(Curve);
|
||||
var result = decode_mod(Curve, &out.*[0], point[0 .. Curve.point_len / 2].*);
|
||||
result &= decode_mod(Curve, &out.*[1], point[Curve.point_len / 2 ..].*);
|
||||
|
||||
const zlen = comptime ((Curve.P[0] + 63) >> 5);
|
||||
comptime std.debug.assert(zlen == @typeInfo(@TypeOf(Curve.R2)).Array.len);
|
||||
comptime std.debug.assert(zlen == @typeInfo(@TypeOf(Curve.B)).Array.len);
|
||||
|
||||
const Q = comptime jacobian_with_one_set(Curve, [2][jacobian_len(Curve)]u32{ Curve.R2, Curve.B });
|
||||
result &= ~run_code(Curve, out, Q, &code.check);
|
||||
return result;
|
||||
}
|
||||
|
||||
const code = struct {
|
||||
const P1x = 0;
|
||||
const P1y = 1;
|
||||
const P1z = 2;
|
||||
const P2x = 3;
|
||||
const P2y = 4;
|
||||
const P2z = 5;
|
||||
const Px = 0;
|
||||
const Py = 1;
|
||||
const Pz = 2;
|
||||
const t1 = 6;
|
||||
const t2 = 7;
|
||||
const t3 = 8;
|
||||
const t4 = 9;
|
||||
const t5 = 10;
|
||||
const t6 = 11;
|
||||
const t7 = 12;
|
||||
const t8 = 3;
|
||||
const t9 = 4;
|
||||
const t10 = 5;
|
||||
fn MSET(comptime d: u16, comptime a: u16) u16 {
|
||||
return 0x0000 + (d << 8) + (a << 4);
|
||||
}
|
||||
fn MADD(comptime d: u16, comptime a: u16) u16 {
|
||||
return 0x1000 + (d << 8) + (a << 4);
|
||||
}
|
||||
fn MSUB(comptime d: u16, comptime a: u16) u16 {
|
||||
return 0x2000 + (d << 8) + (a << 4);
|
||||
}
|
||||
fn MMUL(comptime d: u16, comptime a: u16, comptime b: u16) u16 {
|
||||
return 0x3000 + (d << 8) + (a << 4) + b;
|
||||
}
|
||||
fn MINV(comptime d: u16, comptime a: u16, comptime b: u16) u16 {
|
||||
return 0x4000 + (d << 8) + (a << 4) + b;
|
||||
}
|
||||
fn MTZ(comptime d: u16) u16 {
|
||||
return 0x5000 + (d << 8);
|
||||
}
|
||||
const ENDCODE = 0;
|
||||
|
||||
const check = [_]u16{
|
||||
// Convert x and y to Montgomery representation.
|
||||
MMUL(t1, P1x, P2x),
|
||||
MMUL(t2, P1y, P2x),
|
||||
MSET(P1x, t1),
|
||||
MSET(P1y, t2),
|
||||
// Compute x^3 in t1.
|
||||
MMUL(t2, P1x, P1x),
|
||||
MMUL(t1, P1x, t2),
|
||||
// Subtract 3*x from t1.
|
||||
MSUB(t1, P1x),
|
||||
MSUB(t1, P1x),
|
||||
MSUB(t1, P1x),
|
||||
// Add b.
|
||||
MADD(t1, P2y),
|
||||
// Compute y^2 in t2.
|
||||
MMUL(t2, P1y, P1y),
|
||||
// Compare y^2 with x^3 - 3*x + b; they must match.
|
||||
MSUB(t1, t2),
|
||||
MTZ(t1),
|
||||
// Set z to 1 (in Montgomery representation).
|
||||
MMUL(P1z, P2x, P2z),
|
||||
ENDCODE,
|
||||
};
|
||||
const double = [_]u16{
|
||||
// Compute z^2 (in t1).
|
||||
MMUL(t1, Pz, Pz),
|
||||
// Compute x-z^2 (in t2) and then x+z^2 (in t1).
|
||||
MSET(t2, Px),
|
||||
MSUB(t2, t1),
|
||||
MADD(t1, Px),
|
||||
// Compute m = 3*(x+z^2)*(x-z^2) (in t1).
|
||||
MMUL(t3, t1, t2),
|
||||
MSET(t1, t3),
|
||||
MADD(t1, t3),
|
||||
MADD(t1, t3),
|
||||
// Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3).
|
||||
MMUL(t3, Py, Py),
|
||||
MADD(t3, t3),
|
||||
MMUL(t2, Px, t3),
|
||||
MADD(t2, t2),
|
||||
// Compute x' = m^2 - 2*s.
|
||||
MMUL(Px, t1, t1),
|
||||
MSUB(Px, t2),
|
||||
MSUB(Px, t2),
|
||||
// Compute z' = 2*y*z.
|
||||
MMUL(t4, Py, Pz),
|
||||
MSET(Pz, t4),
|
||||
MADD(Pz, t4),
|
||||
// Compute y' = m*(s - x') - 8*y^4. Note that we already have
|
||||
// 2*y^2 in t3.
|
||||
MSUB(t2, Px),
|
||||
MMUL(Py, t1, t2),
|
||||
MMUL(t4, t3, t3),
|
||||
MSUB(Py, t4),
|
||||
MSUB(Py, t4),
|
||||
ENDCODE,
|
||||
};
|
||||
const _add = [_]u16{
|
||||
// Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3).
|
||||
MMUL(t3, P2z, P2z),
|
||||
MMUL(t1, P1x, t3),
|
||||
MMUL(t4, P2z, t3),
|
||||
MMUL(t3, P1y, t4),
|
||||
// Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4).
|
||||
MMUL(t4, P1z, P1z),
|
||||
MMUL(t2, P2x, t4),
|
||||
MMUL(t5, P1z, t4),
|
||||
MMUL(t4, P2y, t5),
|
||||
//Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4).
|
||||
MSUB(t2, t1),
|
||||
MSUB(t4, t3),
|
||||
// Report cases where r = 0 through the returned flag.
|
||||
MTZ(t4),
|
||||
// Compute u1*h^2 (in t6) and h^3 (in t5).
|
||||
MMUL(t7, t2, t2),
|
||||
MMUL(t6, t1, t7),
|
||||
MMUL(t5, t7, t2),
|
||||
// Compute x3 = r^2 - h^3 - 2*u1*h^2.
|
||||
// t1 and t7 can be used as scratch registers.
|
||||
MMUL(P1x, t4, t4),
|
||||
MSUB(P1x, t5),
|
||||
MSUB(P1x, t6),
|
||||
MSUB(P1x, t6),
|
||||
//Compute y3 = r*(u1*h^2 - x3) - s1*h^3.
|
||||
MSUB(t6, P1x),
|
||||
MMUL(P1y, t4, t6),
|
||||
MMUL(t1, t5, t3),
|
||||
MSUB(P1y, t1),
|
||||
//Compute z3 = h*z1*z2.
|
||||
MMUL(t1, P1z, P2z),
|
||||
MMUL(P1z, t1, t2),
|
||||
ENDCODE,
|
||||
};
|
||||
const affine = [_]u16{
|
||||
// Save z*R in t1.
|
||||
MSET(t1, P1z),
|
||||
// Compute z^3 in t2.
|
||||
MMUL(t2, P1z, P1z),
|
||||
MMUL(t3, P1z, t2),
|
||||
MMUL(t2, t3, P2z),
|
||||
// Invert to (1/z^3) in t2.
|
||||
MINV(t2, t3, t4),
|
||||
// Compute y.
|
||||
MSET(t3, P1y),
|
||||
MMUL(P1y, t2, t3),
|
||||
// Compute (1/z^2) in t3.
|
||||
MMUL(t3, t2, t1),
|
||||
// Compute x.
|
||||
MSET(t2, P1x),
|
||||
MMUL(P1x, t2, t3),
|
||||
ENDCODE,
|
||||
};
|
||||
};
|
||||
|
||||
fn decode_mod(
|
||||
comptime Curve: type,
|
||||
x: *[jacobian_len(Curve)]u32,
|
||||
src: [Curve.point_len / 2]u8,
|
||||
) u32 {
|
||||
const mlen = comptime ((Curve.P[0] + 31) >> 5);
|
||||
const tlen = comptime std.math.max(mlen << 2, Curve.point_len / 2) + 4;
|
||||
|
||||
var r: u32 = 0;
|
||||
var pass: usize = 0;
|
||||
while (pass < 2) : (pass += 1) {
|
||||
var v: usize = 1;
|
||||
var acc: u32 = 0;
|
||||
var acc_len: u32 = 0;
|
||||
|
||||
var u: usize = 0;
|
||||
while (u < tlen) : (u += 1) {
|
||||
const b = if (u < Curve.point_len / 2)
|
||||
@as(u32, src[Curve.point_len / 2 - 1 - u])
|
||||
else
|
||||
0;
|
||||
acc |= b << @truncate(u5, acc_len);
|
||||
acc_len += 8;
|
||||
if (acc_len >= 31) {
|
||||
const xw = acc & 0x7FFFFFFF;
|
||||
acc_len -= 31;
|
||||
acc = b >> @truncate(u5, 8 - acc_len);
|
||||
if (v <= mlen) {
|
||||
if (pass != 0) {
|
||||
x[v] = r & xw;
|
||||
} else {
|
||||
const cc = @bitCast(u32, CMP(xw, Curve.P[v]));
|
||||
r = MUX(EQ(cc, 0), r, cc);
|
||||
}
|
||||
} else if (pass == 0) {
|
||||
r = MUX(EQ(xw, 0), r, 1);
|
||||
}
|
||||
v += 1;
|
||||
}
|
||||
}
|
||||
r >>= 1;
|
||||
r |= (r << 1);
|
||||
}
|
||||
x[0] = Curve.P[0];
|
||||
return r & 1;
|
||||
}
|
||||
|
||||
fn run_code(
|
||||
comptime Curve: type,
|
||||
P1: *Jacobian(Curve),
|
||||
P2: Jacobian(Curve),
|
||||
comptime Code: []const u16,
|
||||
) u32 {
|
||||
comptime const jaclen = jacobian_len(Curve);
|
||||
|
||||
var t: [13][jaclen]u32 = undefined;
|
||||
var result: u32 = 1;
|
||||
|
||||
t[0..3].* = P1.*;
|
||||
t[3..6].* = P2;
|
||||
|
||||
comptime var u: usize = 0;
|
||||
inline while (true) : (u += 1) {
|
||||
comptime var op = Code[u];
|
||||
if (op == 0)
|
||||
break;
|
||||
comptime const d = (op >> 8) & 0x0F;
|
||||
comptime const a = (op >> 4) & 0x0F;
|
||||
comptime const b = op & 0x0F;
|
||||
op >>= 12;
|
||||
|
||||
switch (op) {
|
||||
0 => t[d] = t[a],
|
||||
1 => {
|
||||
var ctl = add(&t[d], &t[a], 1);
|
||||
ctl |= NOT(sub(&t[d], &Curve.P, 0));
|
||||
_ = sub(&t[d], &Curve.P, ctl);
|
||||
},
|
||||
2 => _ = add(&t[d], &Curve.P, sub(&t[d], &t[a], 1)),
|
||||
3 => montymul(&t[d], &t[a], &t[b], &Curve.P, 1),
|
||||
4 => {
|
||||
var tp: [Curve.point_len / 2]u8 = undefined;
|
||||
encode_jacobian_part(&tp, &Curve.P);
|
||||
tp[Curve.point_len / 2 - 1] -= 2;
|
||||
modpow(Curve, &t[d], tp, 1, &t[a], &t[b]);
|
||||
},
|
||||
else => result &= ~iszero(&t[d]),
|
||||
}
|
||||
}
|
||||
P1.* = t[0..3].*;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn MUL31(x: u32, y: u32) callconv(.Inline) u64 {
|
||||
return @as(u64, x) * @as(u64, y);
|
||||
}
|
||||
|
||||
fn MUL31_lo(x: u32, y: u32) callconv(.Inline) u32 {
|
||||
return (x *% y) & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
fn MUX(ctl: u32, x: u32, y: u32) callconv(.Inline) u32 {
|
||||
return y ^ (@bitCast(u32, -@bitCast(i32, ctl)) & (x ^ y));
|
||||
}
|
||||
fn NOT(ctl: u32) callconv(.Inline) u32 {
|
||||
return ctl ^ 1;
|
||||
}
|
||||
fn NEQ(x: u32, y: u32) callconv(.Inline) u32 {
|
||||
const q = x ^ y;
|
||||
return (q | @bitCast(u32, -@bitCast(i32, q))) >> 31;
|
||||
}
|
||||
fn EQ(x: u32, y: u32) callconv(.Inline) u32 {
|
||||
const q = x ^ y;
|
||||
return NOT((q | @bitCast(u32, -@bitCast(i32, q))) >> 31);
|
||||
}
|
||||
fn CMP(x: u32, y: u32) callconv(.Inline) i32 {
|
||||
return @bitCast(i32, GT(x, y)) | -@bitCast(i32, GT(y, x));
|
||||
}
|
||||
fn GT(x: u32, y: u32) callconv(.Inline) u32 {
|
||||
const z = y -% x;
|
||||
return (z ^ ((x ^ y) & (x ^ z))) >> 31;
|
||||
}
|
||||
fn LT(x: u32, y: u32) callconv(.Inline) u32 {
|
||||
return GT(y, x);
|
||||
}
|
||||
fn GE(x: u32, y: u32) callconv(.Inline) u32 {
|
||||
return NOT(GT(y, x));
|
||||
}
|
||||
|
||||
fn CCOPY(ctl: u32, dst: []u8, src: []const u8) void {
|
||||
for (src) |s, i| {
|
||||
dst[i] = @truncate(u8, MUX(ctl, s, dst[i]));
|
||||
}
|
||||
}
|
||||
|
||||
fn set_zero(out: [*]u32, bit_len: u32) callconv(.Inline) void {
|
||||
out[0] = bit_len;
|
||||
mem.set(u32, (out + 1)[0 .. (bit_len + 31) >> 5], 0);
|
||||
}
|
||||
|
||||
fn divrem(_hi: u32, _lo: u32, d: u32, r: *u32) u32 {
|
||||
var hi = _hi;
|
||||
var lo = _lo;
|
||||
var q: u32 = 0;
|
||||
const ch = EQ(hi, d);
|
||||
hi = MUX(ch, 0, hi);
|
||||
|
||||
var k: u5 = 31;
|
||||
while (k > 0) : (k -= 1) {
|
||||
const j = @truncate(u5, 32 - @as(u6, k));
|
||||
const w = (hi << j) | (lo >> k);
|
||||
const ctl = GE(w, d) | (hi >> k);
|
||||
const hi2 = (w -% d) >> j;
|
||||
const lo2 = lo -% (d << k);
|
||||
hi = MUX(ctl, hi2, hi);
|
||||
lo = MUX(ctl, lo2, lo);
|
||||
q |= ctl << k;
|
||||
}
|
||||
const cf = GE(lo, d) | hi;
|
||||
q |= cf;
|
||||
r.* = MUX(cf, lo -% d, lo);
|
||||
return q;
|
||||
}
|
||||
|
||||
fn div(hi: u32, lo: u32, d: u32) callconv(.Inline) u32 {
|
||||
var r: u32 = undefined;
|
||||
return divrem(hi, lo, d, &r);
|
||||
}
|
||||
|
||||
fn muladd_small(x: [*]u32, z: u32, m: [*]const u32) void {
|
||||
var a0: u32 = undefined;
|
||||
var a1: u32 = undefined;
|
||||
var b0: u32 = undefined;
|
||||
const mblr = @intCast(u5, m[0] & 31);
|
||||
const mlen = (m[0] + 31) >> 5;
|
||||
const hi = x[mlen];
|
||||
if (mblr == 0) {
|
||||
a0 = x[mlen];
|
||||
mem.copyBackwards(u32, (x + 2)[0 .. mlen - 1], (x + 1)[0 .. mlen - 1]);
|
||||
x[1] = z;
|
||||
a1 = x[mlen];
|
||||
b0 = m[mlen];
|
||||
} else {
|
||||
a0 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & 0x7FFFFFFF;
|
||||
mem.copyBackwards(u32, (x + 2)[0 .. mlen - 1], (x + 1)[0 .. mlen - 1]);
|
||||
x[1] = z;
|
||||
a1 = ((x[mlen] << (31 - mblr)) | (x[mlen - 1] >> mblr)) & 0x7FFFFFFF;
|
||||
b0 = ((m[mlen] << (31 - mblr)) | (m[mlen - 1] >> mblr)) & 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
const g = div(a0 >> 1, a1 | (a0 << 31), b0);
|
||||
const q = MUX(EQ(a0, b0), 0x7FFFFFFF, MUX(EQ(g, 0), 0, g -% 1));
|
||||
|
||||
var cc: u32 = 0;
|
||||
var tb: u32 = 1;
|
||||
var u: usize = 1;
|
||||
while (u <= mlen) : (u += 1) {
|
||||
const mw = m[u];
|
||||
const zl = MUL31(mw, q) + cc;
|
||||
cc = @truncate(u32, zl >> 31);
|
||||
const zw = @truncate(u32, zl) & 0x7FFFFFFF;
|
||||
const xw = x[u];
|
||||
var nxw = xw -% zw;
|
||||
cc += nxw >> 31;
|
||||
nxw &= 0x7FFFFFFF;
|
||||
x[u] = nxw;
|
||||
tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw));
|
||||
}
|
||||
|
||||
const over = GT(cc, hi);
|
||||
const under = ~over & (tb | LT(cc, hi));
|
||||
_ = add(x, m, over);
|
||||
_ = sub(x, m, under);
|
||||
}
|
||||
|
||||
fn to_monty(x: [*]u32, m: [*]const u32) void {
|
||||
const mlen = (m[0] + 31) >> 5;
|
||||
var k = mlen;
|
||||
while (k > 0) : (k -= 1) {
|
||||
muladd_small(x, 0, m);
|
||||
}
|
||||
}
|
||||
|
||||
fn modpow(
|
||||
comptime Curve: type,
|
||||
x: *[jacobian_len(Curve)]u32,
|
||||
e: [Curve.point_len / 2]u8,
|
||||
m0i: u32,
|
||||
t1: *[jacobian_len(Curve)]u32,
|
||||
t2: *[jacobian_len(Curve)]u32,
|
||||
) void {
|
||||
comptime const jaclen = jacobian_len(Curve);
|
||||
t1.* = x.*;
|
||||
to_monty(t1, &Curve.P);
|
||||
set_zero(x, Curve.P[0]);
|
||||
x[1] = 1;
|
||||
comptime const bitlen = (Curve.point_len / 2) << 3;
|
||||
var k: usize = 0;
|
||||
while (k < bitlen) : (k += 1) {
|
||||
const ctl = (e[Curve.point_len / 2 - 1 - (k >> 3)] >> (@truncate(u3, k & 7))) & 1;
|
||||
montymul(t2, x, t1, &Curve.P, m0i);
|
||||
CCOPY(ctl, mem.asBytes(x), mem.asBytes(t2));
|
||||
montymul(t2, t1, t1, &Curve.P, m0i);
|
||||
t1.* = t2.*;
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_jacobian_part(dst: []u8, x: [*]const u32) void {
|
||||
const xlen = (x[0] + 31) >> 5;
|
||||
|
||||
var buf = @ptrToInt(dst.ptr) + dst.len;
|
||||
var len: usize = dst.len;
|
||||
var k: usize = 1;
|
||||
var acc: u32 = 0;
|
||||
var acc_len: u5 = 0;
|
||||
while (len != 0) {
|
||||
const w = if (k <= xlen) x[k] else 0;
|
||||
k += 1;
|
||||
if (acc_len == 0) {
|
||||
acc = w;
|
||||
acc_len = 31;
|
||||
} else {
|
||||
const z = acc | (w << acc_len);
|
||||
acc_len -= 1;
|
||||
acc = w >> (31 - acc_len);
|
||||
if (len >= 4) {
|
||||
buf -= 4;
|
||||
len -= 4;
|
||||
mem.writeIntBig(u32, @intToPtr([*]u8, buf)[0..4], z);
|
||||
} else {
|
||||
switch (len) {
|
||||
3 => {
|
||||
@intToPtr(*u8, buf - 3).* = @truncate(u8, z >> 16);
|
||||
@intToPtr(*u8, buf - 2).* = @truncate(u8, z >> 8);
|
||||
},
|
||||
2 => @intToPtr(*u8, buf - 2).* = @truncate(u8, z >> 8),
|
||||
1 => {},
|
||||
else => unreachable,
|
||||
}
|
||||
@intToPtr(*u8, buf - 1).* = @truncate(u8, z);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn montymul(
|
||||
out: [*]u32,
|
||||
x: [*]const u32,
|
||||
y: [*]const u32,
|
||||
m: [*]const u32,
|
||||
m0i: u32,
|
||||
) void {
|
||||
const len = (m[0] + 31) >> 5;
|
||||
const len4 = len & ~@as(usize, 3);
|
||||
set_zero(out, m[0]);
|
||||
var dh: u32 = 0;
|
||||
var u: usize = 0;
|
||||
while (u < len) : (u += 1) {
|
||||
const xu = x[u + 1];
|
||||
const f = MUL31_lo(out[1] + MUL31_lo(x[u + 1], y[1]), m0i);
|
||||
|
||||
var r: u64 = 0;
|
||||
var v: usize = 0;
|
||||
while (v < len4) : (v += 4) {
|
||||
comptime var j = 1;
|
||||
inline while (j <= 4) : (j += 1) {
|
||||
const z = out[v + j] +% MUL31(xu, y[v + j]) +% MUL31(f, m[v + j]) +% r;
|
||||
r = z >> 31;
|
||||
out[v + j - 1] = @truncate(u32, z) & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
while (v < len) : (v += 1) {
|
||||
const z = out[v + 1] +% MUL31(xu, y[v + 1]) +% MUL31(f, m[v + 1]) +% r;
|
||||
r = z >> 31;
|
||||
out[v] = @truncate(u32, z) & 0x7FFFFFFF;
|
||||
}
|
||||
dh += @truncate(u32, r);
|
||||
out[len] = dh & 0x7FFFFFFF;
|
||||
dh >>= 31;
|
||||
}
|
||||
out[0] = m[0];
|
||||
const ctl = NEQ(dh, 0) | NOT(sub(out, m, 0));
|
||||
_ = sub(out, m, ctl);
|
||||
}
|
||||
|
||||
fn add(a: [*]u32, b: [*]const u32, ctl: u32) u32 {
|
||||
var u: usize = 1;
|
||||
var cc: u32 = 0;
|
||||
const m = (a[0] + 63) >> 5;
|
||||
while (u < m) : (u += 1) {
|
||||
const aw = a[u];
|
||||
const bw = b[u];
|
||||
const naw = aw +% bw +% cc;
|
||||
cc = naw >> 31;
|
||||
a[u] = MUX(ctl, naw & 0x7FFFFFFF, aw);
|
||||
}
|
||||
return cc;
|
||||
}
|
||||
|
||||
fn sub(a: [*]u32, b: [*]const u32, ctl: u32) u32 {
|
||||
var cc: u32 = 0;
|
||||
const m = (a[0] + 63) >> 5;
|
||||
var u: usize = 1;
|
||||
while (u < m) : (u += 1) {
|
||||
const aw = a[u];
|
||||
const bw = b[u];
|
||||
const naw = aw -% bw -% cc;
|
||||
cc = naw >> 31;
|
||||
a[u] = MUX(ctl, naw & 0x7FFFFFFF, aw);
|
||||
}
|
||||
return cc;
|
||||
}
|
||||
|
||||
fn iszero(arr: [*]const u32) u32 {
|
||||
const mlen = (arr[0] + 63) >> 5;
|
||||
var z: u32 = 0;
|
||||
var u: usize = mlen - 1;
|
||||
while (u > 0) : (u -= 1) {
|
||||
z |= arr[u];
|
||||
}
|
||||
return ~(z | @bitCast(u32, -@bitCast(i32, z))) >> 31;
|
||||
}
|
||||
};
|
||||
|
||||
test "elliptic curve functions with secp384r1 curve" {
|
||||
{
|
||||
// Decode to Jacobian then encode again with no operations
|
||||
var P: ecc.Jacobian(ecc.SECP384R1) = undefined;
|
||||
var res: u32 = ecc.decode_to_jacobian(ecc.SECP384R1, &P, ecc.SECP384R1.base_point);
|
||||
var out: [96]u8 = undefined;
|
||||
ecc.encode_from_jacobian(ecc.SECP384R1, &out, P);
|
||||
try std.testing.expectEqual(ecc.SECP384R1.base_point, out);
|
||||
|
||||
// Multiply by one, check that the result is still the base point
|
||||
mem.set(u8, &out, 0);
|
||||
ecc.point_mul(ecc.SECP384R1, &P, &[1]u8{1});
|
||||
ecc.encode_from_jacobian(ecc.SECP384R1, &out, P);
|
||||
try std.testing.expectEqual(ecc.SECP384R1.base_point, out);
|
||||
}
|
||||
|
||||
{
|
||||
// @TODO Remove this once std.crypto.rand works in .evented mode
|
||||
var rand = blk: {
|
||||
var seed: [std.rand.DefaultCsprng.secret_seed_length]u8 = undefined;
|
||||
try std.os.getrandom(&seed);
|
||||
break :blk &std.rand.DefaultCsprng.init(seed).random;
|
||||
};
|
||||
|
||||
// Derive a shared secret from a Diffie-Hellman key exchange
|
||||
var seed: [48]u8 = undefined;
|
||||
rand.bytes(&seed);
|
||||
const kp1 = ecc.make_key_pair(ecc.SECP384R1, seed);
|
||||
rand.bytes(&seed);
|
||||
const kp2 = ecc.make_key_pair(ecc.SECP384R1, seed);
|
||||
|
||||
const shared1 = try ecc.scalarmult(ecc.SECP384R1, kp1.public_key, &kp2.secret_key);
|
||||
const shared2 = try ecc.scalarmult(ecc.SECP384R1, kp2.public_key, &kp1.secret_key);
|
||||
try std.testing.expectEqual(shared1, shared2);
|
||||
}
|
||||
|
||||
// @TODO Add tests with known points.
|
||||
}
|
2034
src/deps/iguanaTLS/src/main.zig
Normal file
2034
src/deps/iguanaTLS/src/main.zig
Normal file
File diff suppressed because it is too large
Load diff
209
src/deps/iguanaTLS/src/pcks1-1_5.zig
Normal file
209
src/deps/iguanaTLS/src/pcks1-1_5.zig
Normal file
|
@ -0,0 +1,209 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const Sha224 = std.crypto.hash.sha2.Sha224;
|
||||
const Sha384 = std.crypto.hash.sha2.Sha384;
|
||||
const Sha512 = std.crypto.hash.sha2.Sha512;
|
||||
const Sha256 = std.crypto.hash.sha2.Sha256;
|
||||
|
||||
const x509 = @import("x509.zig");
|
||||
const SignatureAlgorithm = x509.Certificate.SignatureAlgorithm;
|
||||
const asn1 = @import("asn1.zig");
|
||||
|
||||
fn rsa_perform(
|
||||
allocator: *Allocator,
|
||||
modulus: std.math.big.int.Const,
|
||||
exponent: std.math.big.int.Const,
|
||||
base: []const u8,
|
||||
) !?std.math.big.int.Managed {
|
||||
// @TODO Better algorithm, make it faster.
|
||||
const curr_base_limbs = try allocator.alloc(
|
||||
usize,
|
||||
std.math.divCeil(usize, base.len, @sizeOf(usize)) catch unreachable,
|
||||
);
|
||||
const curr_base_limb_bytes = @ptrCast([*]u8, curr_base_limbs)[0..base.len];
|
||||
mem.copy(u8, curr_base_limb_bytes, base);
|
||||
mem.reverse(u8, curr_base_limb_bytes);
|
||||
var curr_base = (std.math.big.int.Mutable{
|
||||
.limbs = curr_base_limbs,
|
||||
.positive = true,
|
||||
.len = curr_base_limbs.len,
|
||||
}).toManaged(allocator);
|
||||
defer curr_base.deinit();
|
||||
|
||||
var curr_exponent = try exponent.toManaged(allocator);
|
||||
defer curr_exponent.deinit();
|
||||
var result = try std.math.big.int.Managed.initSet(allocator, @as(usize, 1));
|
||||
|
||||
// encrypted = signature ^ key.exponent MOD key.modulus
|
||||
while (curr_exponent.toConst().orderAgainstScalar(0) == .gt) {
|
||||
if (curr_exponent.isOdd()) {
|
||||
try result.ensureMulCapacity(result.toConst(), curr_base.toConst());
|
||||
try result.mul(result.toConst(), curr_base.toConst());
|
||||
try llmod(&result, modulus);
|
||||
}
|
||||
try curr_base.sqr(curr_base.toConst());
|
||||
try llmod(&curr_base, modulus);
|
||||
try curr_exponent.shiftRight(curr_exponent, 1);
|
||||
}
|
||||
|
||||
if (result.limbs.len * @sizeOf(usize) < base.len)
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
|
||||
// res = res mod N
|
||||
fn llmod(res: *std.math.big.int.Managed, n: std.math.big.int.Const) !void {
|
||||
var temp = try std.math.big.int.Managed.init(res.allocator);
|
||||
defer temp.deinit();
|
||||
try temp.divTrunc(res, res.toConst(), n);
|
||||
}
|
||||
|
||||
pub fn algorithm_prefix(signature_algorithm: SignatureAlgorithm) ?[]const u8 {
|
||||
return switch (signature_algorithm.hash) {
|
||||
.none, .md5, .sha1 => null,
|
||||
.sha224 => &[_]u8{
|
||||
0x30, 0x2d, 0x30, 0x0d, 0x06,
|
||||
0x09, 0x60, 0x86, 0x48, 0x01,
|
||||
0x65, 0x03, 0x04, 0x02, 0x04,
|
||||
0x05, 0x00, 0x04, 0x1c,
|
||||
},
|
||||
.sha256 => &[_]u8{
|
||||
0x30, 0x31, 0x30, 0x0d, 0x06,
|
||||
0x09, 0x60, 0x86, 0x48, 0x01,
|
||||
0x65, 0x03, 0x04, 0x02, 0x01,
|
||||
0x05, 0x00, 0x04, 0x20,
|
||||
},
|
||||
.sha384 => &[_]u8{
|
||||
0x30, 0x41, 0x30, 0x0d, 0x06,
|
||||
0x09, 0x60, 0x86, 0x48, 0x01,
|
||||
0x65, 0x03, 0x04, 0x02, 0x02,
|
||||
0x05, 0x00, 0x04, 0x30,
|
||||
},
|
||||
.sha512 => &[_]u8{
|
||||
0x30, 0x51, 0x30, 0x0d, 0x06,
|
||||
0x09, 0x60, 0x86, 0x48, 0x01,
|
||||
0x65, 0x03, 0x04, 0x02, 0x03,
|
||||
0x05, 0x00, 0x04, 0x40,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn sign(
|
||||
allocator: *Allocator,
|
||||
signature_algorithm: SignatureAlgorithm,
|
||||
hash: []const u8,
|
||||
private_key: x509.PrivateKey,
|
||||
) !?[]const u8 {
|
||||
// @TODO ECDSA signatures
|
||||
if (signature_algorithm.signature != .rsa or private_key != .rsa)
|
||||
return null;
|
||||
|
||||
const signature_length = private_key.rsa.modulus.len * @sizeOf(usize);
|
||||
var sig_buf = try allocator.alloc(u8, signature_length);
|
||||
defer allocator.free(sig_buf);
|
||||
const prefix = algorithm_prefix(signature_algorithm) orelse return null;
|
||||
const first_prefix_idx = sig_buf.len - hash.len - prefix.len;
|
||||
const first_hash_idx = sig_buf.len - hash.len;
|
||||
|
||||
// EM = 0x00 || 0x01 || PS || 0x00 || T
|
||||
sig_buf[0] = 0;
|
||||
sig_buf[1] = 1;
|
||||
mem.set(u8, sig_buf[2 .. first_prefix_idx - 1], 0xff);
|
||||
sig_buf[first_prefix_idx - 1] = 0;
|
||||
mem.copy(u8, sig_buf[first_prefix_idx..first_hash_idx], prefix);
|
||||
mem.copy(u8, sig_buf[first_hash_idx..], hash);
|
||||
|
||||
const modulus = std.math.big.int.Const{ .limbs = private_key.rsa.modulus, .positive = true };
|
||||
const exponent = std.math.big.int.Const{ .limbs = private_key.rsa.exponent, .positive = true };
|
||||
|
||||
var rsa_result = (try rsa_perform(allocator, modulus, exponent, sig_buf)) orelse return null;
|
||||
if (rsa_result.limbs.len * @sizeOf(usize) < signature_length) {
|
||||
rsa_result.deinit();
|
||||
return null;
|
||||
}
|
||||
|
||||
const enc_buf = @ptrCast([*]u8, rsa_result.limbs.ptr)[0..signature_length];
|
||||
mem.reverse(u8, enc_buf);
|
||||
return allocator.resize(
|
||||
enc_buf.ptr[0 .. rsa_result.limbs.len * @sizeOf(usize)],
|
||||
signature_length,
|
||||
) catch unreachable;
|
||||
}
|
||||
|
||||
pub fn verify_signature(
|
||||
allocator: *Allocator,
|
||||
signature_algorithm: SignatureAlgorithm,
|
||||
signature: asn1.BitString,
|
||||
hash: []const u8,
|
||||
public_key: x509.PublicKey,
|
||||
) !bool {
|
||||
// @TODO ECDSA algorithms
|
||||
if (public_key != .rsa or signature_algorithm.signature != .rsa) return false;
|
||||
const prefix = algorithm_prefix(signature_algorithm) orelse return false;
|
||||
|
||||
// RSA hash verification with PKCS 1 V1_5 padding
|
||||
const modulus = std.math.big.int.Const{ .limbs = public_key.rsa.modulus, .positive = true };
|
||||
const exponent = std.math.big.int.Const{ .limbs = public_key.rsa.exponent, .positive = true };
|
||||
if (modulus.bitCountAbs() != signature.bit_len)
|
||||
return false;
|
||||
|
||||
var rsa_result = (try rsa_perform(allocator, modulus, exponent, signature.data)) orelse return false;
|
||||
defer rsa_result.deinit();
|
||||
|
||||
if (rsa_result.limbs.len * @sizeOf(usize) < signature.data.len)
|
||||
return false;
|
||||
|
||||
const enc_buf = @ptrCast([*]u8, rsa_result.limbs.ptr)[0..signature.data.len];
|
||||
mem.reverse(u8, enc_buf);
|
||||
|
||||
if (enc_buf[0] != 0x00 or enc_buf[1] != 0x01)
|
||||
return false;
|
||||
if (!mem.endsWith(u8, enc_buf, hash))
|
||||
return false;
|
||||
if (!mem.endsWith(u8, enc_buf[0 .. enc_buf.len - hash.len], prefix))
|
||||
return false;
|
||||
if (enc_buf[enc_buf.len - hash.len - prefix.len - 1] != 0x00)
|
||||
return false;
|
||||
for (enc_buf[2 .. enc_buf.len - hash.len - prefix.len - 1]) |c| {
|
||||
if (c != 0xff) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn certificate_verify_signature(
|
||||
allocator: *Allocator,
|
||||
signature_algorithm: x509.Certificate.SignatureAlgorithm,
|
||||
signature: asn1.BitString,
|
||||
bytes: []const u8,
|
||||
public_key: x509.PublicKey,
|
||||
) !bool {
|
||||
// @TODO ECDSA algorithms
|
||||
if (public_key != .rsa or signature_algorithm.signature != .rsa) return false;
|
||||
|
||||
var hash_buf: [64]u8 = undefined;
|
||||
var hash: []u8 = undefined;
|
||||
|
||||
switch (signature_algorithm.hash) {
|
||||
// Deprecated hash algos
|
||||
.none, .md5, .sha1 => return false,
|
||||
.sha224 => {
|
||||
Sha224.hash(bytes, hash_buf[0..28], .{});
|
||||
hash = hash_buf[0..28];
|
||||
},
|
||||
.sha256 => {
|
||||
Sha256.hash(bytes, hash_buf[0..32], .{});
|
||||
hash = hash_buf[0..32];
|
||||
},
|
||||
.sha384 => {
|
||||
Sha384.hash(bytes, hash_buf[0..48], .{});
|
||||
hash = hash_buf[0..48];
|
||||
},
|
||||
.sha512 => {
|
||||
Sha512.hash(bytes, hash_buf[0..64], .{});
|
||||
hash = &hash_buf;
|
||||
},
|
||||
}
|
||||
return try verify_signature(allocator, signature_algorithm, signature, hash, public_key);
|
||||
}
|
1025
src/deps/iguanaTLS/src/x509.zig
Normal file
1025
src/deps/iguanaTLS/src/x509.zig
Normal file
File diff suppressed because it is too large
Load diff
20
src/deps/iguanaTLS/test/DSTRootCAX3.crt.pem
Normal file
20
src/deps/iguanaTLS/test/DSTRootCAX3.crt.pem
Normal file
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/
|
||||
MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
|
||||
DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow
|
||||
PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD
|
||||
Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O
|
||||
rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq
|
||||
OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b
|
||||
xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw
|
||||
7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD
|
||||
aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
|
||||
HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG
|
||||
SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69
|
||||
ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr
|
||||
AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz
|
||||
R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5
|
||||
JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo
|
||||
Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
|
||||
-----END CERTIFICATE-----
|
22
src/deps/iguanaTLS/test/DigiCertGlobalRootCA.crt.pem
Normal file
22
src/deps/iguanaTLS/test/DigiCertGlobalRootCA.crt.pem
Normal file
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
|
||||
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
|
||||
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
|
||||
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
|
||||
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
|
||||
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
|
||||
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
|
||||
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
|
||||
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
|
||||
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
|
||||
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
|
||||
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
|
||||
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
|
||||
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
61
src/deps/iguanaTLS/test/badssl.com-client.pem
Normal file
61
src/deps/iguanaTLS/test/badssl.com-client.pem
Normal file
|
@ -0,0 +1,61 @@
|
|||
Bag Attributes
|
||||
localKeyID: 41 C3 6C 33 C7 E3 36 DD EA 4A 1F C0 B7 23 B8 E6 9C DC D8 0F
|
||||
subject=C = US, ST = California, L = San Francisco, O = BadSSL, CN = BadSSL Client Certificate
|
||||
|
||||
issuer=C = US, ST = California, L = San Francisco, O = BadSSL, CN = BadSSL Client Root Certificate Authority
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEqDCCApCgAwIBAgIUK5Ns4y2CzosB/ZoFlaxjZqoBTIIwDQYJKoZIhvcNAQEL
|
||||
BQAwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM
|
||||
DVNhbiBGcmFuY2lzY28xDzANBgNVBAoMBkJhZFNTTDExMC8GA1UEAwwoQmFkU1NM
|
||||
IENsaWVudCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xOTExMjcwMDE5
|
||||
NTdaFw0yMTExMjYwMDE5NTdaMG8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxp
|
||||
Zm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMQ8wDQYDVQQKDAZCYWRTU0wx
|
||||
IjAgBgNVBAMMGUJhZFNTTCBDbGllbnQgQ2VydGlmaWNhdGUwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDHN18R6x5Oz+u6SOXLoxIscz5GHR6cDcCLgyPa
|
||||
x2XfXHdJs+h6fTy61WGM+aXEhR2SIwbj5997s34m0MsbvkJrFmn0LHK1fuTLCihE
|
||||
EmxGdCGZA9xrwxFYAkEjP7D8v7cAWRMipYF/JP7VU7xNUo+QSkZ0sOi9k6bNkABK
|
||||
L3+yP6PqAzsBoKIN5lN/YRLrppsDmk6nrRDo4R3CD+8JQl9quEoOmL22Pc/qpOjL
|
||||
1jgOIFSE5y3gwbzDlfCYoAL5V+by1vu0yJShTTK8oo5wvphcFfEHaQ9w5jFg2htd
|
||||
q99UER3BKuNDuL+zejqGQZCWb0Xsk8S5WBuX8l3Brrg5giqNAgMBAAGjLTArMAkG
|
||||
A1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgeAMAsGA1UdDwQEAwIF4DANBgkqhkiG
|
||||
9w0BAQsFAAOCAgEAZBauLzFSOijkDadcippr9C6laHebb0oRS54xAV70E9k5GxfR
|
||||
/E2EMuQ8X+miRUMXxKquffcDsSxzo2ac0flw94hDx3B6vJIYvsQx9Lzo95Im0DdT
|
||||
DkHFXhTlv2kjQwFVnEsWYwyGpHMTjanvNkO7sBP9p1bN1qTE3QAeyMZNKWJk5xPl
|
||||
U298ERar6tl3Z2Cl8mO6yLhrq4ba6iPGw08SENxzuAJW+n8r0rq7EU+bMg5spgT1
|
||||
CxExzG8Bb0f98ZXMklpYFogkcuH4OUOFyRodotrotm3iRbuvZNk0Zz7N5n1oLTPl
|
||||
bGPMwBcqaGXvK62NlaRkwjnbkPM4MYvREM0bbAgZD2GHyANBTso8bdWvhLvmoSjs
|
||||
FSqJUJp17AZ0x/ELWZd69v2zKW9UdPmw0evyVR19elh/7dmtF6wbewc4N4jxQnTq
|
||||
IItuhIWKWB9edgJz65uZ9ubQWjXoa+9CuWcV/1KxuKCbLHdZXiboLrKm4S1WmMYW
|
||||
d0sJm95H9mJzcLyhLF7iX2kK6K9ug1y02YCVXBC9WGZc2x6GMS7lDkXSkJFy3EWh
|
||||
CmfxkmFGwOgwKt3Jd1pF9ftcSEMhu4WcMgxi9vZr9OdkJLxmk033sVKI/hnkPaHw
|
||||
g0Y2YBH5v0xmi8sYU7weOcwynkjZARpUltBUQ0pWCF5uJsEB8uE8PPDD3c4=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAxzdfEeseTs/rukjly6MSLHM+Rh0enA3Ai4Mj2sdl31x3SbPo
|
||||
en08utVhjPmlxIUdkiMG4+ffe7N+JtDLG75CaxZp9CxytX7kywooRBJsRnQhmQPc
|
||||
a8MRWAJBIz+w/L+3AFkTIqWBfyT+1VO8TVKPkEpGdLDovZOmzZAASi9/sj+j6gM7
|
||||
AaCiDeZTf2ES66abA5pOp60Q6OEdwg/vCUJfarhKDpi9tj3P6qToy9Y4DiBUhOct
|
||||
4MG8w5XwmKAC+Vfm8tb7tMiUoU0yvKKOcL6YXBXxB2kPcOYxYNobXavfVBEdwSrj
|
||||
Q7i/s3o6hkGQlm9F7JPEuVgbl/Jdwa64OYIqjQIDAQABAoIBAFUQf7fW/YoJnk5c
|
||||
8kKRzyDL1Lt7k6Zu+NiZlqXEnutRQF5oQ8yJzXS5yH25296eOJI+AqMuT28ypZtN
|
||||
bGzcQOAZIgTxNcnp9Sf9nlPyyekLjY0Y6PXaxX0e+VFj0N8bvbiYUGNq6HCyC15r
|
||||
8uvRZRvnm04YfEj20zLTWkxTG+OwJ6ZNha1vfq8z7MG5JTsZbP0g7e/LrEb3wI7J
|
||||
Zu9yHQUzq23HhfhpmLN/0l89YLtOaS8WNq4QvKYgZapw/0G1wWoWW4Y2/UpAxZ9r
|
||||
cqTBWSpCSCCgyWjiNhPbSJWfe/9J2bcanITLcvCLlPWGAHy1wpo9iBH57y7S+7YS
|
||||
3yi7lgECgYEA8lwaRIChc38tmtQCNPtai/7uVDdeJe0uv8Jsg04FTF8KMYcD0V1g
|
||||
+T7rUPA+rTHwv8uAGLdzl4NW5Qryw18rDY+UivnaZkEdEsnlo3fc8MSQF78dDHCX
|
||||
nwmHfOmBnBoSbLl+W5ByHkJRHOnX+8qKq9ePNFUMf/hZNYuma9BCFBUCgYEA0m2p
|
||||
VDn12YdhFUUBIH91aD5cQIsBhkHFU4vqW4zBt6TsJpFciWbrBrTeRzeDou59aIsn
|
||||
zGBrLMykOY+EwwRku9KTVM4U791Z/NFbH89GqyUaicb4or+BXw5rGF8DmzSsDo0f
|
||||
ixJ9TVD5DmDi3c9ZQ7ljrtdSxPdA8kOoYPFsApkCgYEA08uZSPQAI6aoe/16UEK4
|
||||
Rk9qhz47kHlNuVZ27ehoyOzlQ5Lxyy0HacmKaxkILOLPuUxljTQEWAv3DAIdVI7+
|
||||
WMN41Fq0eVe9yIWXoNtGwUGFirsA77YVSm5RcN++3GQMZedUfUAl+juKFvJkRS4j
|
||||
MTkXdGw+mDa3/wsjTGSa2mECgYABO6NCWxSVsbVf6oeXKSgG9FaWCjp4DuqZErjM
|
||||
0IZSDSVVFIT2SSQXZffncuvSiJMziZ0yFV6LZKeRrsWYXu44K4Oxe4Oj5Cgi0xc1
|
||||
mIFRf2YoaIIMchLP+8Wk3ummfyiC7VDB/9m8Gj1bWDX8FrrvKqbq31gcz1YSFVNn
|
||||
PgLkAQKBgFzG8NdL8os55YcjBcOZMUs5QTKiQSyZM0Abab17k9JaqsU0jQtzeFsY
|
||||
FTiwh2uh6l4gdO/dGC/P0Vrp7F05NnO7oE4T+ojDzVQMnFpCBeL7x08GfUQkphEG
|
||||
m0Wqhhi8/24Sy934t5Txgkfoltg8ahkx934WjP6WWRnSAu+cf+vW
|
||||
-----END RSA PRIVATE KEY-----
|
BIN
src/deps/iguanaTLS/test/github.der
Normal file
BIN
src/deps/iguanaTLS/test/github.der
Normal file
Binary file not shown.
23
src/deps/iguanaTLS/test/github.pem
Normal file
23
src/deps/iguanaTLS/test/github.pem
Normal file
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
6
src/deps/iguanaTLS/zig.mod
Normal file
6
src/deps/iguanaTLS/zig.mod
Normal file
|
@ -0,0 +1,6 @@
|
|||
id: csbnipaad8n77buaszsnjvlmn6j173fl7pkprsctelswjywe
|
||||
name: iguanaTLS
|
||||
main: src/main.zig
|
||||
license: MIT
|
||||
description: Minimal, experimental TLS 1.2 implementation in Zig
|
||||
dependencies:
|
|
@ -111,10 +111,10 @@ pub fn prettyPrintQVariantMap(qvariantmap: std.StringHashMap(QVariant), indentLe
|
|||
var qMapIter = qvariantmap.iterator();
|
||||
while (qMapIter.next()) |v| {
|
||||
print_indent_level(indentLevel + 1);
|
||||
std.debug.print("Key: \"{s}\"\n", .{v.key});
|
||||
std.debug.print("Key: \"{s}\"\n", .{v.key_ptr.*});
|
||||
print_indent_level(indentLevel + 1);
|
||||
std.debug.print("Value:\n", .{});
|
||||
prettyPrintQVariant(v.value, indentLevel + 2);
|
||||
prettyPrintQVariant(v.value_ptr.*, indentLevel + 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue