mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-30 14:52:41 +03:00
96 lines
3.5 KiB
Zig
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;
|