std.crypto.tls.Certificate: explicit error set for verify

This commit is contained in:
Andrew Kelley 2022-12-22 20:19:25 -07:00
parent 7cb535d4b5
commit 642a8b05c3
4 changed files with 38 additions and 10 deletions

View File

@ -116,9 +116,23 @@ pub const Parsed = struct {
return p.slice(p.message_slice); return p.slice(p.message_slice);
} }
pub const VerifyError = error{
CertificateIssuerMismatch,
CertificateNotYetValid,
CertificateExpired,
CertificateSignatureAlgorithmUnsupported,
CertificateSignatureAlgorithmMismatch,
CertificateFieldHasInvalidLength,
CertificateFieldHasWrongDataType,
CertificatePublicKeyInvalid,
CertificateSignatureInvalidLength,
CertificateSignatureInvalid,
CertificateSignatureUnsupportedBitCount,
};
/// This function checks the time validity for the subject only. Checking /// This function checks the time validity for the subject only. Checking
/// the issuer's time validity is out of scope. /// the issuer's time validity is out of scope.
pub fn verify(parsed_subject: Parsed, parsed_issuer: Parsed) !void { pub fn verify(parsed_subject: Parsed, parsed_issuer: Parsed) VerifyError!void {
// Check that the subject's issuer name matches the issuer's // Check that the subject's issuer name matches the issuer's
// subject name. // subject name.
if (!mem.eql(u8, parsed_subject.issuer(), parsed_issuer.subject())) { if (!mem.eql(u8, parsed_subject.issuer(), parsed_issuer.subject())) {
@ -452,11 +466,19 @@ fn verifyRsa(
hash_der ++ hash_der ++
msg_hashed; msg_hashed;
const public_key = try rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop); const public_key = rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop) catch |err| switch (err) {
const em_dec = try rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key, rsa.poop); error.OutOfMemory => @panic("TODO don't heap allocate"),
};
const em_dec = rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key, rsa.poop) catch |err| switch (err) {
error.OutOfMemory => @panic("TODO don't heap allocate"),
error.MessageTooLong => unreachable,
error.NegativeIntoUnsigned => @panic("TODO make RSA not emit this error"),
error.TargetTooSmall => @panic("TODO make RSA not emit this error"),
error.BufferTooSmall => @panic("TODO make RSA not emit this error"),
};
if (!mem.eql(u8, &em, &em_dec)) { if (!mem.eql(u8, &em, &em_dec)) {
try std.testing.expectEqualSlices(u8, &em, &em_dec);
return error.CertificateSignatureInvalid; return error.CertificateSignatureInvalid;
} }
}, },

View File

@ -9,13 +9,19 @@
map: std.HashMapUnmanaged(der.Element.Slice, u32, MapContext, std.hash_map.default_max_load_percentage) = .{}, map: std.HashMapUnmanaged(der.Element.Slice, u32, MapContext, std.hash_map.default_max_load_percentage) = .{},
bytes: std.ArrayListUnmanaged(u8) = .{}, bytes: std.ArrayListUnmanaged(u8) = .{},
pub fn verify(cb: Bundle, subject: Certificate.Parsed) !void { pub const VerifyError = Certificate.Parsed.VerifyError || error{
const bytes_index = cb.find(subject.issuer()) orelse return error.IssuerNotFound; CertificateIssuerNotFound,
};
pub fn verify(cb: Bundle, subject: Certificate.Parsed) VerifyError!void {
const bytes_index = cb.find(subject.issuer()) orelse return error.CertificateIssuerNotFound;
const issuer_cert: Certificate = .{ const issuer_cert: Certificate = .{
.buffer = cb.bytes.items, .buffer = cb.bytes.items,
.index = bytes_index, .index = bytes_index,
}; };
const issuer = try issuer_cert.parse(); // Every certificate in the bundle is pre-parsed before adding it, ensuring
// that parsing will succeed here.
const issuer = issuer_cert.parse() catch unreachable;
try subject.verify(issuer); try subject.verify(issuer);
} }

View File

@ -111,7 +111,7 @@ pub const Element = struct {
}; };
}; };
pub const ParseElementError = error{CertificateHasFieldWithInvalidLength}; pub const ParseElementError = error{CertificateFieldHasInvalidLength};
pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element { pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element {
var i = index; var i = index;
@ -131,7 +131,7 @@ pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element {
const len_size = @truncate(u7, size_byte); const len_size = @truncate(u7, size_byte);
if (len_size > @sizeOf(u32)) { if (len_size > @sizeOf(u32)) {
return error.CertificateHasFieldWithInvalidLength; return error.CertificateFieldHasInvalidLength;
} }
const end_i = i + len_size; const end_i = i + len_size;

View File

@ -470,7 +470,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
handshake_state = .trust_chain_established; handshake_state = .trust_chain_established;
break :cert; break :cert;
} else |err| switch (err) { } else |err| switch (err) {
error.IssuerNotFound => {}, error.CertificateIssuerNotFound => {},
else => |e| { else => |e| {
std.debug.print("unable to validate cert against system root CAs: {s}\n", .{ std.debug.print("unable to validate cert against system root CAs: {s}\n", .{
@errorName(e), @errorName(e),