Commit bbc074252c
Changed files (6)
lib
std
lib/std/crypto/tls/Client.zig
@@ -1,5 +1,6 @@
const std = @import("../../std.zig");
const tls = std.crypto.tls;
+const Der = std.crypto.Der;
const Client = @This();
const net = std.net;
const mem = std.mem;
@@ -28,7 +29,7 @@ partially_read_len: u15,
eof: bool,
/// `host` is only borrowed during this function call.
-pub fn init(stream: net.Stream, host: []const u8) !Client {
+pub fn init(stream: net.Stream, ca_bundle: crypto.CertificateBundle, host: []const u8) !Client {
const host_len = @intCast(u16, host.len);
var random_buffer: [128]u8 = undefined;
@@ -392,7 +393,7 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
switch (cipher_params) {
inline else => |*p| p.transcript_hash.update(wrapped_handshake),
}
- var hs_i: usize = 0;
+ var hs_i: u32 = 0;
const cert_req_ctx_len = handshake[hs_i];
hs_i += 1;
if (cert_req_ctx_len != 0) return error.TlsIllegalParameter;
@@ -404,75 +405,47 @@ pub fn init(stream: net.Stream, host: []const u8) !Client {
hs_i += 3;
const end_cert = hs_i + cert_size;
- const certificate = try tls.Der.parseElement(handshake, &hs_i);
- {
- var cert_i: usize = 0;
- const tbs_certificate = try tls.Der.parseElement(certificate.contents, &cert_i);
- {
- var tbs_i: usize = 0;
- const version = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const serial_number = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const signature = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const issuer = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const validity = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const subject = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const subject_pub_key = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
- const extensions = try tls.Der.parseElement(tbs_certificate.contents, &tbs_i);
-
- // RFC 5280, section 4.1.2.3:
- // "This field MUST contain the same algorithm identifier as
- // the signatureAlgorithm field in the sequence Certificate."
- _ = signature;
-
- _ = issuer;
- _ = validity;
-
- std.debug.print("version: {any} '{}'\n", .{
- version.identifier, std.fmt.fmtSliceHexLower(version.contents),
- });
-
- std.debug.print("serial_number: {any} {}\n", .{
- serial_number.identifier,
- std.fmt.fmtSliceHexLower(serial_number.contents),
- });
+ const certificate = try Der.parseElement(handshake, hs_i);
+ const tbs_certificate = try Der.parseElement(handshake, certificate.start);
- std.debug.print("subject: {any} {}\n", .{
- subject.identifier,
- std.fmt.fmtSliceHexLower(subject.contents),
- });
-
- std.debug.print("subject pub key: {any} {}\n", .{
- subject_pub_key.identifier,
- std.fmt.fmtSliceHexLower(subject_pub_key.contents),
- });
-
- std.debug.print("extensions: {any} {}\n", .{
- extensions.identifier,
- std.fmt.fmtSliceHexLower(extensions.contents),
- });
- }
- const signature_algorithm = try tls.Der.parseElement(certificate.contents, &cert_i);
- const signature_value = try tls.Der.parseElement(certificate.contents, &cert_i);
-
- {
- var sa_i: usize = 0;
- const algorithm = try tls.Der.parseObjectId(signature_algorithm.contents, &sa_i);
- std.debug.print("cert has this signature algorithm: {any}\n", .{algorithm});
- //const parameters = try tls.Der.parseElement(signature_algorithm.contents, &sa_i);
- }
-
- std.debug.print("signature_value: {any} {d} bytes\n", .{
- signature_value.identifier, signature_value.contents.len,
- });
+ const version = try Der.parseElement(handshake, tbs_certificate.start);
+ if (@bitCast(u8, version.identifier) != 0xa0 or
+ !mem.eql(u8, handshake[version.start..version.end], "\x02\x01\x02"))
+ {
+ return error.UnsupportedCertificateVersion;
}
+ const serial_number = try Der.parseElement(handshake, version.end);
+ // RFC 5280, section 4.1.2.3:
+ // "This field MUST contain the same algorithm identifier as
+ // the signatureAlgorithm field in the sequence Certificate."
+ const signature = try Der.parseElement(handshake, serial_number.end);
+ const issuer = try Der.parseElement(handshake, signature.end);
+ const validity = try Der.parseElement(handshake, issuer.end);
+ const subject = try Der.parseElement(handshake, validity.end);
+ const subject_pub_key = try Der.parseElement(handshake, subject.end);
+ const extensions = try Der.parseElement(handshake, subject_pub_key.end);
+ _ = extensions;
+
+ const signature_algorithm = try Der.parseElement(handshake, tbs_certificate.end);
+ const signature_value = try Der.parseElement(handshake, signature_algorithm.end);
+ _ = signature_value;
+
+ const algorithm_elem = try Der.parseElement(handshake, signature_algorithm.start);
+ const algorithm = try Der.parseObjectId(handshake, algorithm_elem);
+ std.debug.print("cert has this signature algorithm: {any}\n", .{algorithm});
+ //const parameters = try Der.parseElement(signature_algorithm.contents, &sa_i);
+
hs_i = end_cert;
const total_ext_size = mem.readIntBig(u16, handshake[hs_i..][0..2]);
hs_i += 2;
hs_i += total_ext_size;
- std.debug.print("received certificate of size {d} bytes with {d} bytes of extensions\n", .{
- cert_size, total_ext_size,
+ const issuer_bytes = handshake[issuer.start..issuer.end];
+ const ca_cert = ca_bundle.find(issuer_bytes);
+
+ std.debug.print("received certificate of size {d} bytes with {d} bytes of extensions. ca_found={any}\n", .{
+ cert_size, total_ext_size, ca_cert != null,
});
}
},
lib/std/crypto/CertificateBundle.zig
@@ -0,0 +1,173 @@
+//! A set of certificates. Typically pre-installed on every operating system,
+//! these are "Certificate Authorities" used to validate SSL certificates.
+//! This data structure stores certificates in DER-encoded form, all of them
+//! concatenated together in the `bytes` array. The `map` field contains an
+//! index from the DER-encoded subject name to the index within `bytes`.
+
+map: std.HashMapUnmanaged(Key, u32, MapContext, std.hash_map.default_max_load_percentage) = .{},
+bytes: std.ArrayListUnmanaged(u8) = .{},
+
+pub const Key = struct {
+ subject_start: u32,
+ subject_end: u32,
+};
+
+/// The returned bytes become invalid after calling any of the rescan functions
+/// or add functions.
+pub fn find(cb: CertificateBundle, subject_name: []const u8) ?[]const u8 {
+ const Adapter = struct {
+ cb: CertificateBundle,
+
+ pub fn hash(ctx: @This(), k: []const u8) u64 {
+ _ = ctx;
+ return std.hash_map.hashString(k);
+ }
+
+ pub fn eql(ctx: @This(), a: []const u8, b_key: Key) bool {
+ const b = ctx.cb.bytes.items[b_key.subject_start..b_key.subject_end];
+ return mem.eql(u8, a, b);
+ }
+ };
+ const index = cb.map.getAdapted(subject_name, Adapter{ .cb = cb }) orelse return null;
+ return cb.bytes.items[index..];
+}
+
+pub fn deinit(cb: *CertificateBundle, gpa: Allocator) void {
+ cb.map.deinit(gpa);
+ cb.bytes.deinit(gpa);
+ cb.* = undefined;
+}
+
+/// Empties the set of certificates and then scans the host operating system
+/// file system standard locations for certificates.
+pub fn rescan(cb: *CertificateBundle, gpa: Allocator) !void {
+ switch (builtin.os.tag) {
+ .linux => return rescanLinux(cb, gpa),
+ else => @compileError("it is unknown where the root CA certificates live on this OS"),
+ }
+}
+
+pub fn rescanLinux(cb: *CertificateBundle, gpa: Allocator) !void {
+ var dir = fs.openIterableDirAbsolute("/etc/ssl/certs", .{}) catch |err| switch (err) {
+ error.FileNotFound => return,
+ else => |e| return e,
+ };
+ defer dir.close();
+
+ cb.bytes.clearRetainingCapacity();
+ cb.map.clearRetainingCapacity();
+
+ var it = dir.iterate();
+ while (try it.next()) |entry| {
+ switch (entry.kind) {
+ .File, .SymLink => {},
+ else => continue,
+ }
+
+ try addCertsFromFile(cb, gpa, dir.dir, entry.name);
+ }
+
+ cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
+}
+
+pub fn addCertsFromFile(
+ cb: *CertificateBundle,
+ gpa: Allocator,
+ dir: fs.Dir,
+ sub_file_path: []const u8,
+) !void {
+ var file = try dir.openFile(sub_file_path, .{});
+ defer file.close();
+
+ const size = try file.getEndPos();
+
+ // We borrow `bytes` as a temporary buffer for the base64-encoded data.
+ // This is possible by computing the decoded length and reserving the space
+ // for the decoded bytes first.
+ const decoded_size_upper_bound = size / 4 * 3;
+ try cb.bytes.ensureUnusedCapacity(gpa, decoded_size_upper_bound + size);
+ const end_reserved = cb.bytes.items.len + decoded_size_upper_bound;
+ const buffer = cb.bytes.allocatedSlice()[end_reserved..];
+ const end_index = try file.readAll(buffer);
+ const encoded_bytes = buffer[0..end_index];
+
+ const begin_marker = "-----BEGIN CERTIFICATE-----";
+ const end_marker = "-----END CERTIFICATE-----";
+
+ var start_index: usize = 0;
+ while (mem.indexOfPos(u8, encoded_bytes, start_index, begin_marker)) |begin_marker_start| {
+ const cert_start = begin_marker_start + begin_marker.len;
+ const cert_end = mem.indexOfPos(u8, encoded_bytes, cert_start, end_marker) orelse
+ return error.MissingEndCertificateMarker;
+ start_index = cert_end + end_marker.len;
+ const encoded_cert = mem.trim(u8, encoded_bytes[cert_start..cert_end], " \t\r\n");
+ const decoded_start = @intCast(u32, cb.bytes.items.len);
+ const dest_buf = cb.bytes.allocatedSlice()[decoded_start..];
+ cb.bytes.items.len += try base64.decode(dest_buf, encoded_cert);
+ const k = try key(cb, decoded_start);
+ try cb.map.putContext(gpa, k, decoded_start, .{ .cb = cb });
+ }
+}
+
+pub fn key(cb: *CertificateBundle, bytes_index: u32) !Key {
+ const bytes = cb.bytes.items;
+ const certificate = try Der.parseElement(bytes, bytes_index);
+ const tbs_certificate = try Der.parseElement(bytes, certificate.start);
+ const version = try Der.parseElement(bytes, tbs_certificate.start);
+ if (@bitCast(u8, version.identifier) != 0xa0 or
+ !mem.eql(u8, bytes[version.start..version.end], "\x02\x01\x02"))
+ {
+ return error.UnsupportedCertificateVersion;
+ }
+
+ const serial_number = try Der.parseElement(bytes, version.end);
+
+ // RFC 5280, section 4.1.2.3:
+ // "This field MUST contain the same algorithm identifier as
+ // the signatureAlgorithm field in the sequence Certificate."
+ const signature = try Der.parseElement(bytes, serial_number.end);
+ const issuer = try Der.parseElement(bytes, signature.end);
+ const validity = try Der.parseElement(bytes, issuer.end);
+ const subject = try Der.parseElement(bytes, validity.end);
+ //const subject_pub_key = try Der.parseElement(bytes, subject.end);
+ //const extensions = try Der.parseElement(bytes, subject_pub_key.end);
+
+ return .{
+ .subject_start = subject.start,
+ .subject_end = subject.end,
+ };
+}
+
+const builtin = @import("builtin");
+const std = @import("../std.zig");
+const fs = std.fs;
+const mem = std.mem;
+const Allocator = std.mem.Allocator;
+const Der = std.crypto.Der;
+const CertificateBundle = @This();
+
+const base64 = std.base64.standard.decoderWithIgnore(" \t\r\n");
+
+const MapContext = struct {
+ cb: *const CertificateBundle,
+
+ pub fn hash(ctx: MapContext, k: Key) u64 {
+ return std.hash_map.hashString(ctx.cb.bytes.items[k.subject_start..k.subject_end]);
+ }
+
+ pub fn eql(ctx: MapContext, a: Key, b: Key) bool {
+ const bytes = ctx.cb.bytes.items;
+ return mem.eql(
+ u8,
+ bytes[a.subject_start..a.subject_end],
+ bytes[b.subject_start..b.subject_end],
+ );
+ }
+};
+
+test {
+ var bundle: CertificateBundle = .{};
+ defer bundle.deinit(std.testing.allocator);
+
+ try bundle.rescan(std.testing.allocator);
+}
lib/std/crypto/Der.zig
@@ -0,0 +1,153 @@
+pub const Class = enum(u2) {
+ universal,
+ application,
+ context_specific,
+ private,
+};
+
+pub const PC = enum(u1) {
+ primitive,
+ constructed,
+};
+
+pub const Identifier = packed struct(u8) {
+ tag: Tag,
+ pc: PC,
+ class: Class,
+};
+
+pub const Tag = enum(u5) {
+ boolean = 1,
+ integer = 2,
+ bitstring = 3,
+ null = 5,
+ object_identifier = 6,
+ sequence = 16,
+ sequence_of = 17,
+ _,
+};
+
+pub const Oid = enum {
+ rsadsi,
+ pkcs,
+ rsaEncryption,
+ md2WithRSAEncryption,
+ md5WithRSAEncryption,
+ sha1WithRSAEncryption,
+ sha256WithRSAEncryption,
+ sha384WithRSAEncryption,
+ sha512WithRSAEncryption,
+ sha224WithRSAEncryption,
+ pbeWithMD2AndDES_CBC,
+ pbeWithMD5AndDES_CBC,
+ pkcs9_emailAddress,
+ md2,
+ md5,
+ rc4,
+ ecdsa_with_Recommended,
+ ecdsa_with_Specified,
+ ecdsa_with_SHA224,
+ ecdsa_with_SHA256,
+ ecdsa_with_SHA384,
+ ecdsa_with_SHA512,
+ X500,
+ X509,
+ commonName,
+ serialNumber,
+ countryName,
+ localityName,
+ stateOrProvinceName,
+ organizationName,
+ organizationalUnitName,
+ organizationIdentifier,
+
+ pub const map = std.ComptimeStringMap(Oid, .{
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D }, .rsadsi },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01 }, .pkcs },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x01 }, .pbeWithMD2AndDES_CBC },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x03 }, .pbeWithMD5AndDES_CBC },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x02 }, .md2 },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05 }, .md5 },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x03, 0x04 }, .rc4 },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x02 }, .ecdsa_with_Recommended },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03 }, .ecdsa_with_Specified },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
+ .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
+ .{ &[_]u8{0x55}, .X500 },
+ .{ &[_]u8{ 0x55, 0x04 }, .X509 },
+ .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
+ .{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber },
+ .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
+ .{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
+ .{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
+ .{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
+ .{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
+ .{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
+ });
+};
+
+pub const Element = struct {
+ identifier: Identifier,
+ start: u32,
+ end: u32,
+};
+
+pub const ParseElementError = error{CertificateHasFieldWithInvalidLength};
+
+pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element {
+ var i = index;
+ const identifier = @bitCast(Identifier, bytes[i]);
+ i += 1;
+ const size_byte = bytes[i];
+ i += 1;
+ if ((size_byte >> 7) == 0) {
+ return .{
+ .identifier = identifier,
+ .start = i,
+ .end = i + size_byte,
+ };
+ }
+
+ const len_size = @truncate(u7, size_byte);
+ if (len_size > @sizeOf(u32)) {
+ return error.CertificateHasFieldWithInvalidLength;
+ }
+
+ const end_i = i + len_size;
+ var long_form_size: u32 = 0;
+ while (i < end_i) : (i += 1) {
+ long_form_size = (long_form_size << 8) | bytes[i];
+ }
+
+ return .{
+ .identifier = identifier,
+ .start = i,
+ .end = i + long_form_size,
+ };
+}
+
+pub const ParseObjectIdError = error{
+ CertificateHasUnrecognizedObjectId,
+ CertificateFieldHasWrongDataType,
+} || ParseElementError;
+
+pub fn parseObjectId(bytes: []const u8, element: Element) ParseObjectIdError!Oid {
+ if (element.identifier.tag != .object_identifier)
+ return error.CertificateFieldHasWrongDataType;
+ return Oid.map.get(bytes[element.start..element.end]) orelse
+ return error.CertificateHasUnrecognizedObjectId;
+}
+
+const std = @import("../std.zig");
+const Der = @This();
lib/std/crypto/tls.zig
@@ -349,112 +349,3 @@ pub inline fn int3(x: u24) [3]u8 {
@truncate(u8, x),
};
}
-
-pub const Der = struct {
- pub const Class = enum(u2) {
- universal,
- application,
- context_specific,
- private,
- };
-
- pub const PC = enum(u1) {
- primitive,
- constructed,
- };
-
- pub const Identifier = packed struct(u8) {
- tag: Tag,
- pc: PC,
- class: Class,
- };
-
- pub const Tag = enum(u5) {
- boolean = 1,
- integer = 2,
- bitstring = 3,
- null = 5,
- object_identifier = 6,
- sequence = 16,
- _,
- };
-
- pub const Oid = enum {
- commonName,
- countryName,
- localityName,
- stateOrProvinceName,
- organizationName,
- organizationalUnitName,
- sha256WithRSAEncryption,
- sha384WithRSAEncryption,
- sha512WithRSAEncryption,
- sha224WithRSAEncryption,
-
- pub const map = std.ComptimeStringMap(Oid, .{
- .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
- .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
- .{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
- .{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
- .{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
- .{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
- .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
- .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
- .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
- .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
- });
- };
-
- pub const Element = struct {
- identifier: Identifier,
- contents: []const u8,
- };
-
- pub const ParseElementError = error{CertificateHasFieldWithInvalidLength};
-
- pub fn parseElement(bytes: []const u8, index: *usize) ParseElementError!Der.Element {
- var i = index.*;
- const identifier = @bitCast(Identifier, bytes[i]);
- i += 1;
- const size_byte = bytes[i];
- i += 1;
- if ((size_byte >> 7) == 0) {
- const contents = bytes[i..][0..size_byte];
- index.* = i + contents.len;
- return .{
- .identifier = identifier,
- .contents = contents,
- };
- }
-
- const len_size = @truncate(u7, size_byte);
- if (len_size > @sizeOf(usize)) {
- return error.CertificateHasFieldWithInvalidLength;
- }
-
- const end = i + len_size;
- var long_form_size: usize = 0;
- while (i < end) : (i += 1) {
- long_form_size = (long_form_size << 8) | bytes[i];
- }
-
- const contents = bytes[i..][0..long_form_size];
- index.* = i + contents.len;
-
- return .{
- .identifier = identifier,
- .contents = contents,
- };
- }
-
- pub const ParseObjectIdError = error{
- CertificateHasUnrecognizedObjectId,
- CertificateFieldHasWrongDataType,
- } || ParseElementError;
-
- pub fn parseObjectId(bytes: []const u8, index: *usize) ParseObjectIdError!Oid {
- const oid_element = try parseElement(bytes, index);
- if (oid_element.identifier.tag != .object_identifier) return error.CertificateFieldHasWrongDataType;
- return Oid.map.get(oid_element.contents) orelse return error.CertificateHasUnrecognizedObjectId;
- }
-};
lib/std/http/Client.zig
@@ -7,6 +7,7 @@ const Client = @This();
allocator: std.mem.Allocator,
headers: std.ArrayListUnmanaged(u8) = .{},
active_requests: usize = 0,
+ca_bundle: std.crypto.CertificateBundle = .{},
pub const Request = struct {
client: *Client,
@@ -102,7 +103,7 @@ pub fn request(client: *Client, options: Request.Options) !Request {
switch (options.protocol) {
.http => {},
.https => {
- req.tls_client = try std.crypto.tls.Client.init(req.stream, options.host);
+ req.tls_client = try std.crypto.tls.Client.init(req.stream, client.ca_bundle, options.host);
},
}
lib/std/crypto.zig
@@ -177,6 +177,8 @@ const std = @import("std.zig");
pub const errors = @import("crypto/errors.zig");
pub const tls = @import("crypto/tls.zig");
+pub const Der = @import("crypto/Der.zig");
+pub const CertificateBundle = @import("crypto/CertificateBundle.zig");
test {
_ = aead.aegis.Aegis128L;
@@ -266,6 +268,9 @@ test {
_ = utils;
_ = random;
_ = errors;
+ _ = tls;
+ _ = Der;
+ _ = CertificateBundle;
}
test "CSPRNG" {