Commit 642a8b05c3

Andrew Kelley <andrew@ziglang.org>
2022-12-23 04:19:25
std.crypto.tls.Certificate: explicit error set for verify
1 parent 7cb535d
Changed files (4)
lib
lib/std/crypto/Certificate/Bundle.zig
@@ -9,13 +9,19 @@
 map: std.HashMapUnmanaged(der.Element.Slice, u32, MapContext, std.hash_map.default_max_load_percentage) = .{},
 bytes: std.ArrayListUnmanaged(u8) = .{},
 
-pub fn verify(cb: Bundle, subject: Certificate.Parsed) !void {
-    const bytes_index = cb.find(subject.issuer()) orelse return error.IssuerNotFound;
+pub const VerifyError = Certificate.Parsed.VerifyError || error{
+    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 = .{
         .buffer = cb.bytes.items,
         .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);
 }
 
lib/std/crypto/tls/Client.zig
@@ -470,7 +470,7 @@ pub fn init(stream: net.Stream, ca_bundle: Certificate.Bundle, host: []const u8)
                                             handshake_state = .trust_chain_established;
                                             break :cert;
                                         } else |err| switch (err) {
-                                            error.IssuerNotFound => {},
+                                            error.CertificateIssuerNotFound => {},
                                             else => |e| {
                                                 std.debug.print("unable to validate cert against system root CAs: {s}\n", .{
                                                     @errorName(e),
lib/std/crypto/Certificate.zig
@@ -116,9 +116,23 @@ pub const Parsed = struct {
         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
     /// 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
         // subject name.
         if (!mem.eql(u8, parsed_subject.issuer(), parsed_issuer.subject())) {
@@ -452,11 +466,19 @@ fn verifyRsa(
                 hash_der ++
                 msg_hashed;
 
-            const public_key = try rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop);
-            const em_dec = try rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key, rsa.poop);
+            const public_key = rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop) catch |err| switch (err) {
+                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)) {
-                try std.testing.expectEqualSlices(u8, &em, &em_dec);
                 return error.CertificateSignatureInvalid;
             }
         },
lib/std/crypto/der.zig
@@ -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 {
     var i = index;
@@ -131,7 +131,7 @@ pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element {
 
     const len_size = @truncate(u7, size_byte);
     if (len_size > @sizeOf(u32)) {
-        return error.CertificateHasFieldWithInvalidLength;
+        return error.CertificateFieldHasInvalidLength;
     }
 
     const end_i = i + len_size;