Files

96 lines
3.5 KiB
Zig

//! A sequence of certificates, where each certificate is authenticated by the next certificate.
const Chain = @This();
store: ?crypt32.HCERTSTORE,
primary: ?*const crypt32.CERT_CONTEXT,
pub const empty: Chain = .{ .store = null, .primary = null };
pub fn deinit(chain: *Chain) void {
if (chain.primary) |primary| assert(crypt32.CertFreeCertificateContext(primary).toBool());
if (chain.store) |store| if (!crypt32.CertCloseStore(store, .{
.CHECK = std.debug.runtime_safety,
}).toBool()) std.os.windows.unexpectedError(std.os.windows.GetLastError()) catch unreachable;
chain.* = .empty;
}
pub fn addCert(chain: *Chain, cert: []const u8) std.Io.UnexpectedError!void {
const store = chain.store orelse store: {
const store = crypt32.CertOpenStore(
.MEMORY,
.{},
.NULL,
.{},
null,
) orelse return std.os.windows.unexpectedError(std.os.windows.GetLastError());
chain.store = store;
break :store store;
};
if (!crypt32.CertAddEncodedCertificateToStore(
store,
.{ .CERT = .ASN },
cert.ptr,
@intCast(cert.len),
.ALWAYS,
if (chain.primary) |_| null else &chain.primary,
).toBool()) return std.os.windows.unexpectedError(std.os.windows.GetLastError());
}
pub const VerifyError = error{
TlsCertificateNotVerified,
} || std.Io.UnexpectedError;
pub fn verify(chain: *const Chain, now: std.Io.Timestamp) VerifyError!void {
const now_win = @divFloor(now.nanoseconds - std.time.epoch.windows * std.time.ns_per_s, 100);
var cert_chain: *const crypt32.CERT_CHAIN.CONTEXT = undefined;
if (!crypt32.CertGetCertificateChain(
.CURRENT_USER,
chain.primary orelse return error.TlsCertificateNotVerified,
&.{
.dwLowDateTime = @bitCast(@as(i32, @truncate(now_win >> 0))),
.dwHighDateTime = @bitCast(@as(i32, @intCast(now_win >> 32))),
},
null,
&.{ .RequestedUsage = .{ .dwType = .AND, .Usage = .{
.cUsageIdentifier = ALLOWED_EKUS.len,
.rgpszUsageIdentifier = &ALLOWED_EKUS,
} } },
.{ .REVOCATION_CHECK_END_CERT = true, .REVOCATION_ACCUMULATIVE_TIMEOUT = true },
null,
&cert_chain,
).toBool()) return std.os.windows.unexpectedError(std.os.windows.GetLastError());
defer crypt32.CertFreeCertificateChain(cert_chain);
var status: crypt32.CERT_CHAIN.POLICY.STATUS = .{
.dwError = undefined,
.lChainIndex = undefined,
.lElementIndex = undefined,
.pvExtraPolicyStatus = undefined,
};
if (!crypt32.CertVerifyCertificateChainPolicy(
.SSL,
cert_chain,
&.{
.dwFlags = .{
.IGNORE_END_REV_UNKNOWN = true,
.IGNORE_CTL_SIGNER_REV_UNKNOWN = true,
.IGNORE_CA_REV_UNKNOWN = true,
.IGNORE_ROOT_REV_UNKNOWN = true,
},
.pvExtraPolicyPara = @constCast(&crypt32.HTTPSPolicyCallbackData{ .dwAuthType = .SERVER }),
},
&status,
).toBool()) return std.os.windows.unexpectedError(std.os.windows.GetLastError());
switch (status.dwError) {
.SUCCESS => return,
.CERT_E_UNTRUSTEDROOT => return error.TlsCertificateNotVerified,
else => |err| return std.os.windows.unexpectedError(err),
}
}
const ALLOWED_EKUS = [_][*:0]const u8{"1.3.6.1.5.5.7.3.1"};
const assert = std.debug.assert;
const builtin = @import("builtin");
const std = @import("std");
const crypt32 = std.os.windows.crypt32;