zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 4f34412b (27183B) - Raw


      1 buffer: []const u8,
      2 index: u32,
      3 
      4 pub const Bundle = @import("Certificate/Bundle.zig");
      5 
      6 pub const Algorithm = enum {
      7     sha1WithRSAEncryption,
      8     sha224WithRSAEncryption,
      9     sha256WithRSAEncryption,
     10     sha384WithRSAEncryption,
     11     sha512WithRSAEncryption,
     12     ecdsa_with_SHA224,
     13     ecdsa_with_SHA256,
     14     ecdsa_with_SHA384,
     15     ecdsa_with_SHA512,
     16 
     17     pub const map = std.ComptimeStringMap(Algorithm, .{
     18         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
     19         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
     20         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
     21         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
     22         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
     23         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
     24         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
     25         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
     26         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
     27     });
     28 
     29     pub fn Hash(comptime algorithm: Algorithm) type {
     30         return switch (algorithm) {
     31             .sha1WithRSAEncryption => crypto.hash.Sha1,
     32             .ecdsa_with_SHA224, .sha224WithRSAEncryption => crypto.hash.sha2.Sha224,
     33             .ecdsa_with_SHA256, .sha256WithRSAEncryption => crypto.hash.sha2.Sha256,
     34             .ecdsa_with_SHA384, .sha384WithRSAEncryption => crypto.hash.sha2.Sha384,
     35             .ecdsa_with_SHA512, .sha512WithRSAEncryption => crypto.hash.sha2.Sha512,
     36         };
     37     }
     38 };
     39 
     40 pub const AlgorithmCategory = enum {
     41     rsaEncryption,
     42     X9_62_id_ecPublicKey,
     43 
     44     pub const map = std.ComptimeStringMap(AlgorithmCategory, .{
     45         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
     46         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
     47     });
     48 };
     49 
     50 pub const Attribute = enum {
     51     commonName,
     52     serialNumber,
     53     countryName,
     54     localityName,
     55     stateOrProvinceName,
     56     organizationName,
     57     organizationalUnitName,
     58     organizationIdentifier,
     59     subject_alt_name,
     60     pkcs9_emailAddress,
     61 
     62     pub const map = std.ComptimeStringMap(Attribute, .{
     63         .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
     64         .{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber },
     65         .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
     66         .{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
     67         .{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
     68         .{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
     69         .{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
     70         .{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
     71         .{ &[_]u8{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
     72         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
     73     });
     74 };
     75 
     76 pub const Parsed = struct {
     77     certificate: Certificate,
     78     issuer_slice: Slice,
     79     subject_slice: Slice,
     80     common_name_slice: Slice,
     81     subject_alt_name_slice: Slice,
     82     signature_slice: Slice,
     83     signature_algorithm: Algorithm,
     84     pub_key_algo: AlgorithmCategory,
     85     pub_key_slice: Slice,
     86     message_slice: Slice,
     87     validity: Validity,
     88 
     89     pub const Validity = struct {
     90         not_before: u64,
     91         not_after: u64,
     92     };
     93 
     94     pub const Slice = der.Element.Slice;
     95 
     96     pub fn slice(p: Parsed, s: Slice) []const u8 {
     97         return p.certificate.buffer[s.start..s.end];
     98     }
     99 
    100     pub fn issuer(p: Parsed) []const u8 {
    101         return p.slice(p.issuer_slice);
    102     }
    103 
    104     pub fn subject(p: Parsed) []const u8 {
    105         return p.slice(p.subject_slice);
    106     }
    107 
    108     pub fn commonName(p: Parsed) []const u8 {
    109         return p.slice(p.common_name_slice);
    110     }
    111 
    112     pub fn subjectAltName(p: Parsed) []const u8 {
    113         return p.slice(p.subject_alt_name_slice);
    114     }
    115 
    116     pub fn signature(p: Parsed) []const u8 {
    117         return p.slice(p.signature_slice);
    118     }
    119 
    120     pub fn pubKey(p: Parsed) []const u8 {
    121         return p.slice(p.pub_key_slice);
    122     }
    123 
    124     pub fn message(p: Parsed) []const u8 {
    125         return p.slice(p.message_slice);
    126     }
    127 
    128     pub const VerifyError = error{
    129         CertificateIssuerMismatch,
    130         CertificateNotYetValid,
    131         CertificateExpired,
    132         CertificateSignatureAlgorithmUnsupported,
    133         CertificateSignatureAlgorithmMismatch,
    134         CertificateFieldHasInvalidLength,
    135         CertificateFieldHasWrongDataType,
    136         CertificatePublicKeyInvalid,
    137         CertificateSignatureInvalidLength,
    138         CertificateSignatureInvalid,
    139         CertificateSignatureUnsupportedBitCount,
    140     };
    141 
    142     /// This function checks the time validity for the subject only. Checking
    143     /// the issuer's time validity is out of scope.
    144     pub fn verify(parsed_subject: Parsed, parsed_issuer: Parsed) VerifyError!void {
    145         // Check that the subject's issuer name matches the issuer's
    146         // subject name.
    147         if (!mem.eql(u8, parsed_subject.issuer(), parsed_issuer.subject())) {
    148             return error.CertificateIssuerMismatch;
    149         }
    150 
    151         const now_sec = std.time.timestamp();
    152         if (now_sec < parsed_subject.validity.not_before)
    153             return error.CertificateNotYetValid;
    154         if (now_sec > parsed_subject.validity.not_after)
    155             return error.CertificateExpired;
    156 
    157         switch (parsed_subject.signature_algorithm) {
    158             inline .sha1WithRSAEncryption,
    159             .sha224WithRSAEncryption,
    160             .sha256WithRSAEncryption,
    161             .sha384WithRSAEncryption,
    162             .sha512WithRSAEncryption,
    163             => |algorithm| return verifyRsa(
    164                 algorithm.Hash(),
    165                 parsed_subject.message(),
    166                 parsed_subject.signature(),
    167                 parsed_issuer.pub_key_algo,
    168                 parsed_issuer.pubKey(),
    169             ),
    170             .ecdsa_with_SHA224,
    171             .ecdsa_with_SHA256,
    172             .ecdsa_with_SHA384,
    173             .ecdsa_with_SHA512,
    174             => {
    175                 return error.CertificateSignatureAlgorithmUnsupported;
    176             },
    177         }
    178     }
    179 };
    180 
    181 pub fn parse(cert: Certificate) !Parsed {
    182     const cert_bytes = cert.buffer;
    183     const certificate = try der.parseElement(cert_bytes, cert.index);
    184     const tbs_certificate = try der.parseElement(cert_bytes, certificate.slice.start);
    185     const version = try der.parseElement(cert_bytes, tbs_certificate.slice.start);
    186     try checkVersion(cert_bytes, version);
    187     const serial_number = try der.parseElement(cert_bytes, version.slice.end);
    188     // RFC 5280, section 4.1.2.3:
    189     // "This field MUST contain the same algorithm identifier as
    190     // the signatureAlgorithm field in the sequence Certificate."
    191     const tbs_signature = try der.parseElement(cert_bytes, serial_number.slice.end);
    192     const issuer = try der.parseElement(cert_bytes, tbs_signature.slice.end);
    193     const validity = try der.parseElement(cert_bytes, issuer.slice.end);
    194     const not_before = try der.parseElement(cert_bytes, validity.slice.start);
    195     const not_before_utc = try parseTime(cert, not_before);
    196     const not_after = try der.parseElement(cert_bytes, not_before.slice.end);
    197     const not_after_utc = try parseTime(cert, not_after);
    198     const subject = try der.parseElement(cert_bytes, validity.slice.end);
    199 
    200     const pub_key_info = try der.parseElement(cert_bytes, subject.slice.end);
    201     const pub_key_signature_algorithm = try der.parseElement(cert_bytes, pub_key_info.slice.start);
    202     const pub_key_algo_elem = try der.parseElement(cert_bytes, pub_key_signature_algorithm.slice.start);
    203     const pub_key_algo = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem);
    204     const pub_key_elem = try der.parseElement(cert_bytes, pub_key_signature_algorithm.slice.end);
    205     const pub_key = try parseBitString(cert, pub_key_elem);
    206 
    207     var common_name = der.Element.Slice.empty;
    208     var subject_alt_name = der.Element.Slice.empty;
    209     var name_i = subject.slice.start;
    210     //std.debug.print("subject name:\n", .{});
    211     while (name_i < subject.slice.end) {
    212         const rdn = try der.parseElement(cert_bytes, name_i);
    213         var rdn_i = rdn.slice.start;
    214         while (rdn_i < rdn.slice.end) {
    215             const atav = try der.parseElement(cert_bytes, rdn_i);
    216             var atav_i = atav.slice.start;
    217             while (atav_i < atav.slice.end) {
    218                 const ty_elem = try der.parseElement(cert_bytes, atav_i);
    219                 const ty = try parseAttribute(cert_bytes, ty_elem);
    220                 const val = try der.parseElement(cert_bytes, ty_elem.slice.end);
    221                 //std.debug.print(" {s}: '{s}'\n", .{
    222                 //    @tagName(ty), cert_bytes[val.slice.start..val.slice.end],
    223                 //});
    224                 switch (ty) {
    225                     .commonName => common_name = val.slice,
    226                     .subject_alt_name => subject_alt_name = val.slice,
    227                     else => {},
    228                 }
    229                 atav_i = val.slice.end;
    230             }
    231             rdn_i = atav.slice.end;
    232         }
    233         name_i = rdn.slice.end;
    234     }
    235 
    236     const sig_algo = try der.parseElement(cert_bytes, tbs_certificate.slice.end);
    237     const algo_elem = try der.parseElement(cert_bytes, sig_algo.slice.start);
    238     const signature_algorithm = try parseAlgorithm(cert_bytes, algo_elem);
    239     const sig_elem = try der.parseElement(cert_bytes, sig_algo.slice.end);
    240     const signature = try parseBitString(cert, sig_elem);
    241 
    242     return .{
    243         .certificate = cert,
    244         .common_name_slice = common_name,
    245         .subject_alt_name_slice = subject_alt_name,
    246         .issuer_slice = issuer.slice,
    247         .subject_slice = subject.slice,
    248         .signature_slice = signature,
    249         .signature_algorithm = signature_algorithm,
    250         .message_slice = .{ .start = certificate.slice.start, .end = tbs_certificate.slice.end },
    251         .pub_key_algo = pub_key_algo,
    252         .pub_key_slice = pub_key,
    253         .validity = .{
    254             .not_before = not_before_utc,
    255             .not_after = not_after_utc,
    256         },
    257     };
    258 }
    259 
    260 pub fn verify(subject: Certificate, issuer: Certificate) !void {
    261     const parsed_subject = try subject.parse();
    262     const parsed_issuer = try issuer.parse();
    263     return parsed_subject.verify(parsed_issuer);
    264 }
    265 
    266 pub fn contents(cert: Certificate, elem: der.Element) []const u8 {
    267     return cert.buffer[elem.slice.start..elem.slice.end];
    268 }
    269 
    270 pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
    271     if (elem.identifier.tag != .bitstring) return error.CertificateFieldHasWrongDataType;
    272     if (cert.buffer[elem.slice.start] != 0) return error.CertificateHasInvalidBitString;
    273     return .{ .start = elem.slice.start + 1, .end = elem.slice.end };
    274 }
    275 
    276 /// Returns number of seconds since epoch.
    277 pub fn parseTime(cert: Certificate, elem: der.Element) !u64 {
    278     const bytes = cert.contents(elem);
    279     switch (elem.identifier.tag) {
    280         .utc_time => {
    281             // Example: "YYMMDD000000Z"
    282             if (bytes.len != 13)
    283                 return error.CertificateTimeInvalid;
    284             if (bytes[12] != 'Z')
    285                 return error.CertificateTimeInvalid;
    286 
    287             return Date.toSeconds(.{
    288                 .year = @as(u16, 2000) + try parseTimeDigits(bytes[0..2].*, 0, 99),
    289                 .month = try parseTimeDigits(bytes[2..4].*, 1, 12),
    290                 .day = try parseTimeDigits(bytes[4..6].*, 1, 31),
    291                 .hour = try parseTimeDigits(bytes[6..8].*, 0, 23),
    292                 .minute = try parseTimeDigits(bytes[8..10].*, 0, 59),
    293                 .second = try parseTimeDigits(bytes[10..12].*, 0, 59),
    294             });
    295         },
    296         .generalized_time => {
    297             // Examples:
    298             // "19920521000000Z"
    299             // "19920622123421Z"
    300             // "19920722132100.3Z"
    301             if (bytes.len < 15)
    302                 return error.CertificateTimeInvalid;
    303             return Date.toSeconds(.{
    304                 .year = try parseYear4(bytes[0..4]),
    305                 .month = try parseTimeDigits(bytes[4..6].*, 1, 12),
    306                 .day = try parseTimeDigits(bytes[6..8].*, 1, 31),
    307                 .hour = try parseTimeDigits(bytes[8..10].*, 0, 23),
    308                 .minute = try parseTimeDigits(bytes[10..12].*, 0, 59),
    309                 .second = try parseTimeDigits(bytes[12..14].*, 0, 59),
    310             });
    311         },
    312         else => return error.CertificateFieldHasWrongDataType,
    313     }
    314 }
    315 
    316 const Date = struct {
    317     /// example: 1999
    318     year: u16,
    319     /// range: 1 to 12
    320     month: u8,
    321     /// range: 1 to 31
    322     day: u8,
    323     /// range: 0 to 59
    324     hour: u8,
    325     /// range: 0 to 59
    326     minute: u8,
    327     /// range: 0 to 59
    328     second: u8,
    329 
    330     /// Convert to number of seconds since epoch.
    331     pub fn toSeconds(date: Date) u64 {
    332         var sec: u64 = 0;
    333 
    334         {
    335             var year: u16 = 1970;
    336             while (year < date.year) : (year += 1) {
    337                 const days: u64 = std.time.epoch.getDaysInYear(year);
    338                 sec += days * std.time.epoch.secs_per_day;
    339             }
    340         }
    341 
    342         {
    343             const is_leap = std.time.epoch.isLeapYear(date.year);
    344             var month: u4 = 1;
    345             while (month < date.month) : (month += 1) {
    346                 const days: u64 = std.time.epoch.getDaysInMonth(
    347                     @intToEnum(std.time.epoch.YearLeapKind, @boolToInt(is_leap)),
    348                     @intToEnum(std.time.epoch.Month, month),
    349                 );
    350                 sec += days * std.time.epoch.secs_per_day;
    351             }
    352         }
    353 
    354         sec += (date.day - 1) * @as(u64, std.time.epoch.secs_per_day);
    355         sec += date.hour * @as(u64, 60 * 60);
    356         sec += date.minute * @as(u64, 60);
    357         sec += date.second;
    358 
    359         return sec;
    360     }
    361 };
    362 
    363 pub fn parseTimeDigits(nn: @Vector(2, u8), min: u8, max: u8) !u8 {
    364     const zero: @Vector(2, u8) = .{ '0', '0' };
    365     const mm: @Vector(2, u8) = .{ 10, 1 };
    366     const result = @reduce(.Add, (nn -% zero) *% mm);
    367     if (result < min) return error.CertificateTimeInvalid;
    368     if (result > max) return error.CertificateTimeInvalid;
    369     return result;
    370 }
    371 
    372 test parseTimeDigits {
    373     const expectEqual = std.testing.expectEqual;
    374     try expectEqual(@as(u8, 0), try parseTimeDigits("00".*, 0, 99));
    375     try expectEqual(@as(u8, 99), try parseTimeDigits("99".*, 0, 99));
    376     try expectEqual(@as(u8, 42), try parseTimeDigits("42".*, 0, 99));
    377 
    378     const expectError = std.testing.expectError;
    379     try expectError(error.CertificateTimeInvalid, parseTimeDigits("13".*, 1, 12));
    380     try expectError(error.CertificateTimeInvalid, parseTimeDigits("00".*, 1, 12));
    381 }
    382 
    383 pub fn parseYear4(text: *const [4]u8) !u16 {
    384     const nnnn: @Vector(4, u16) = .{ text[0], text[1], text[2], text[3] };
    385     const zero: @Vector(4, u16) = .{ '0', '0', '0', '0' };
    386     const mmmm: @Vector(4, u16) = .{ 1000, 100, 10, 1 };
    387     const result = @reduce(.Add, (nnnn -% zero) *% mmmm);
    388     if (result > 9999) return error.CertificateTimeInvalid;
    389     return result;
    390 }
    391 
    392 test parseYear4 {
    393     const expectEqual = std.testing.expectEqual;
    394     try expectEqual(@as(u16, 0), try parseYear4("0000"));
    395     try expectEqual(@as(u16, 9999), try parseYear4("9999"));
    396     try expectEqual(@as(u16, 1988), try parseYear4("1988"));
    397 
    398     const expectError = std.testing.expectError;
    399     try expectError(error.CertificateTimeInvalid, parseYear4("999b"));
    400     try expectError(error.CertificateTimeInvalid, parseYear4("crap"));
    401 }
    402 
    403 pub fn parseAlgorithm(bytes: []const u8, element: der.Element) !Algorithm {
    404     if (element.identifier.tag != .object_identifier)
    405         return error.CertificateFieldHasWrongDataType;
    406     const oid_bytes = bytes[element.slice.start..element.slice.end];
    407     return Algorithm.map.get(oid_bytes) orelse {
    408         //std.debug.print("oid bytes: {}\n", .{std.fmt.fmtSliceHexLower(oid_bytes)});
    409         return error.CertificateHasUnrecognizedAlgorithm;
    410     };
    411 }
    412 
    413 pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) !AlgorithmCategory {
    414     if (element.identifier.tag != .object_identifier)
    415         return error.CertificateFieldHasWrongDataType;
    416     return AlgorithmCategory.map.get(bytes[element.slice.start..element.slice.end]) orelse
    417         return error.CertificateHasUnrecognizedAlgorithmCategory;
    418 }
    419 
    420 pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute {
    421     if (element.identifier.tag != .object_identifier)
    422         return error.CertificateFieldHasWrongDataType;
    423     const oid_bytes = bytes[element.slice.start..element.slice.end];
    424     return Attribute.map.get(oid_bytes) orelse {
    425         //std.debug.print("attr: {}\n", .{std.fmt.fmtSliceHexLower(oid_bytes)});
    426         return error.CertificateHasUnrecognizedAttribute;
    427     };
    428 }
    429 
    430 fn verifyRsa(
    431     comptime Hash: type,
    432     message: []const u8,
    433     sig: []const u8,
    434     pub_key_algo: AlgorithmCategory,
    435     pub_key: []const u8,
    436 ) !void {
    437     if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch;
    438     const pub_key_seq = try der.parseElement(pub_key, 0);
    439     if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
    440     const modulus_elem = try der.parseElement(pub_key, pub_key_seq.slice.start);
    441     if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
    442     const exponent_elem = try der.parseElement(pub_key, modulus_elem.slice.end);
    443     if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
    444     // Skip over meaningless zeroes in the modulus.
    445     const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end];
    446     const modulus_offset = for (modulus_raw) |byte, i| {
    447         if (byte != 0) break i;
    448     } else modulus_raw.len;
    449     const modulus = modulus_raw[modulus_offset..];
    450     const exponent = pub_key[exponent_elem.slice.start..exponent_elem.slice.end];
    451     if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
    452     if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
    453 
    454     const hash_der = switch (Hash) {
    455         crypto.hash.Sha1 => [_]u8{
    456             0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
    457             0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
    458         },
    459         crypto.hash.sha2.Sha224 => [_]u8{
    460             0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    461             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
    462             0x00, 0x04, 0x1c,
    463         },
    464         crypto.hash.sha2.Sha256 => [_]u8{
    465             0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    466             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
    467             0x00, 0x04, 0x20,
    468         },
    469         crypto.hash.sha2.Sha384 => [_]u8{
    470             0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    471             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
    472             0x00, 0x04, 0x30,
    473         },
    474         crypto.hash.sha2.Sha512 => [_]u8{
    475             0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    476             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
    477             0x00, 0x04, 0x40,
    478         },
    479         else => @compileError("unreachable"),
    480     };
    481 
    482     var msg_hashed: [Hash.digest_length]u8 = undefined;
    483     Hash.hash(message, &msg_hashed, .{});
    484 
    485     switch (modulus.len) {
    486         inline 128, 256, 512 => |modulus_len| {
    487             const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3;
    488             const em: [modulus_len]u8 =
    489                 [2]u8{ 0, 1 } ++
    490                 ([1]u8{0xff} ** ps_len) ++
    491                 [1]u8{0} ++
    492                 hash_der ++
    493                 msg_hashed;
    494 
    495             const public_key = rsa.PublicKey.fromBytes(exponent, modulus, rsa.poop) catch |err| switch (err) {
    496                 error.OutOfMemory => @panic("TODO don't heap allocate"),
    497             };
    498             const em_dec = rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key, rsa.poop) catch |err| switch (err) {
    499                 error.OutOfMemory => @panic("TODO don't heap allocate"),
    500 
    501                 error.MessageTooLong => unreachable,
    502                 error.NegativeIntoUnsigned => @panic("TODO make RSA not emit this error"),
    503                 error.TargetTooSmall => @panic("TODO make RSA not emit this error"),
    504                 error.BufferTooSmall => @panic("TODO make RSA not emit this error"),
    505             };
    506 
    507             if (!mem.eql(u8, &em, &em_dec)) {
    508                 return error.CertificateSignatureInvalid;
    509             }
    510         },
    511         else => {
    512             return error.CertificateSignatureUnsupportedBitCount;
    513         },
    514     }
    515 }
    516 
    517 pub fn checkVersion(bytes: []const u8, version: der.Element) !void {
    518     if (@bitCast(u8, version.identifier) != 0xa0 or
    519         !mem.eql(u8, bytes[version.slice.start..version.slice.end], "\x02\x01\x02"))
    520     {
    521         return error.UnsupportedCertificateVersion;
    522     }
    523 }
    524 
    525 const std = @import("../std.zig");
    526 const crypto = std.crypto;
    527 const mem = std.mem;
    528 const Certificate = @This();
    529 
    530 pub const der = struct {
    531     pub const Class = enum(u2) {
    532         universal,
    533         application,
    534         context_specific,
    535         private,
    536     };
    537 
    538     pub const PC = enum(u1) {
    539         primitive,
    540         constructed,
    541     };
    542 
    543     pub const Identifier = packed struct(u8) {
    544         tag: Tag,
    545         pc: PC,
    546         class: Class,
    547     };
    548 
    549     pub const Tag = enum(u5) {
    550         boolean = 1,
    551         integer = 2,
    552         bitstring = 3,
    553         null = 5,
    554         object_identifier = 6,
    555         sequence = 16,
    556         sequence_of = 17,
    557         utc_time = 23,
    558         generalized_time = 24,
    559         _,
    560     };
    561 
    562     pub const Element = struct {
    563         identifier: Identifier,
    564         slice: Slice,
    565 
    566         pub const Slice = struct {
    567             start: u32,
    568             end: u32,
    569 
    570             pub const empty: Slice = .{ .start = 0, .end = 0 };
    571         };
    572     };
    573 
    574     pub const ParseElementError = error{CertificateFieldHasInvalidLength};
    575 
    576     pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element {
    577         var i = index;
    578         const identifier = @bitCast(Identifier, bytes[i]);
    579         i += 1;
    580         const size_byte = bytes[i];
    581         i += 1;
    582         if ((size_byte >> 7) == 0) {
    583             return .{
    584                 .identifier = identifier,
    585                 .slice = .{
    586                     .start = i,
    587                     .end = i + size_byte,
    588                 },
    589             };
    590         }
    591 
    592         const len_size = @truncate(u7, size_byte);
    593         if (len_size > @sizeOf(u32)) {
    594             return error.CertificateFieldHasInvalidLength;
    595         }
    596 
    597         const end_i = i + len_size;
    598         var long_form_size: u32 = 0;
    599         while (i < end_i) : (i += 1) {
    600             long_form_size = (long_form_size << 8) | bytes[i];
    601         }
    602 
    603         return .{
    604             .identifier = identifier,
    605             .slice = .{
    606                 .start = i,
    607                 .end = i + long_form_size,
    608             },
    609         };
    610     }
    611 };
    612 
    613 test {
    614     _ = Bundle;
    615 }
    616 
    617 /// TODO: replace this with Frank's upcoming RSA implementation. the verify
    618 /// function won't have the possibility of failure - it will either identify a
    619 /// valid signature or an invalid signature.
    620 /// This code is borrowed from https://github.com/shiguredo/tls13-zig
    621 /// which is licensed under the Apache License Version 2.0, January 2004
    622 /// http://www.apache.org/licenses/
    623 /// The code has been modified.
    624 const rsa = struct {
    625     const BigInt = std.math.big.int.Managed;
    626 
    627     const PublicKey = struct {
    628         n: BigInt,
    629         e: BigInt,
    630 
    631         pub fn deinit(self: *PublicKey) void {
    632             self.n.deinit();
    633             self.e.deinit();
    634         }
    635 
    636         pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8, allocator: std.mem.Allocator) !PublicKey {
    637             var _n = try BigInt.init(allocator);
    638             errdefer _n.deinit();
    639             try setBytes(&_n, modulus_bytes, allocator);
    640 
    641             var _e = try BigInt.init(allocator);
    642             errdefer _e.deinit();
    643             try setBytes(&_e, pub_bytes, allocator);
    644 
    645             return .{
    646                 .n = _n,
    647                 .e = _e,
    648             };
    649         }
    650     };
    651 
    652     fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey, allocator: std.mem.Allocator) ![modulus_len]u8 {
    653         var m = try BigInt.init(allocator);
    654         defer m.deinit();
    655 
    656         try setBytes(&m, &msg, allocator);
    657 
    658         if (m.order(public_key.n) != .lt) {
    659             return error.MessageTooLong;
    660         }
    661 
    662         var e = try BigInt.init(allocator);
    663         defer e.deinit();
    664 
    665         try pow_montgomery(&e, &m, &public_key.e, &public_key.n, allocator);
    666 
    667         var res: [modulus_len]u8 = undefined;
    668 
    669         try toBytes(&res, &e, allocator);
    670 
    671         return res;
    672     }
    673 
    674     fn setBytes(r: *BigInt, bytes: []const u8, allcator: std.mem.Allocator) !void {
    675         try r.set(0);
    676         var tmp = try BigInt.init(allcator);
    677         defer tmp.deinit();
    678         for (bytes) |b| {
    679             try r.shiftLeft(r, 8);
    680             try tmp.set(b);
    681             try r.add(r, &tmp);
    682         }
    683     }
    684 
    685     fn pow_montgomery(r: *BigInt, a: *const BigInt, x: *const BigInt, n: *const BigInt, allocator: std.mem.Allocator) !void {
    686         var bin_raw: [512]u8 = undefined;
    687         try toBytes(&bin_raw, x, allocator);
    688 
    689         var i: usize = 0;
    690         while (bin_raw[i] == 0x00) : (i += 1) {}
    691         const bin = bin_raw[i..];
    692 
    693         try r.set(1);
    694         var r1 = try BigInt.init(allocator);
    695         defer r1.deinit();
    696         try BigInt.copy(&r1, a.toConst());
    697         i = 0;
    698         while (i < bin.len * 8) : (i += 1) {
    699             if (((bin[i / 8] >> @intCast(u3, (7 - (i % 8)))) & 0x1) == 0) {
    700                 try BigInt.mul(&r1, r, &r1);
    701                 try mod(&r1, &r1, n, allocator);
    702                 try BigInt.sqr(r, r);
    703                 try mod(r, r, n, allocator);
    704             } else {
    705                 try BigInt.mul(r, r, &r1);
    706                 try mod(r, r, n, allocator);
    707                 try BigInt.sqr(&r1, &r1);
    708                 try mod(&r1, &r1, n, allocator);
    709             }
    710         }
    711     }
    712 
    713     fn toBytes(out: []u8, a: *const BigInt, allocator: std.mem.Allocator) !void {
    714         const Error = error{
    715             BufferTooSmall,
    716         };
    717 
    718         var mask = try BigInt.initSet(allocator, 0xFF);
    719         defer mask.deinit();
    720         var tmp = try BigInt.init(allocator);
    721         defer tmp.deinit();
    722 
    723         var a_copy = try BigInt.init(allocator);
    724         defer a_copy.deinit();
    725         try a_copy.copy(a.toConst());
    726 
    727         // Encoding into big-endian bytes
    728         var i: usize = 0;
    729         while (i < out.len) : (i += 1) {
    730             try tmp.bitAnd(&a_copy, &mask);
    731             const b = try tmp.to(u8);
    732             out[out.len - i - 1] = b;
    733             try a_copy.shiftRight(&a_copy, 8);
    734         }
    735 
    736         if (!a_copy.eqZero()) {
    737             return Error.BufferTooSmall;
    738         }
    739     }
    740 
    741     fn mod(rem: *BigInt, a: *const BigInt, n: *const BigInt, allocator: std.mem.Allocator) !void {
    742         var q = try BigInt.init(allocator);
    743         defer q.deinit();
    744 
    745         try BigInt.divFloor(&q, rem, a, n);
    746     }
    747 
    748     // TODO: flush the toilet
    749     const poop = std.heap.page_allocator;
    750 };