zig

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

blob 3da4269b (46318B) - Raw


      1 buffer: []const u8,
      2 index: u32,
      3 
      4 pub const Bundle = @import("Certificate/Bundle.zig");
      5 
      6 pub const Version = enum { v1, v2, v3 };
      7 
      8 pub const Algorithm = enum {
      9     sha1WithRSAEncryption,
     10     sha224WithRSAEncryption,
     11     sha256WithRSAEncryption,
     12     sha384WithRSAEncryption,
     13     sha512WithRSAEncryption,
     14     ecdsa_with_SHA224,
     15     ecdsa_with_SHA256,
     16     ecdsa_with_SHA384,
     17     ecdsa_with_SHA512,
     18     md2WithRSAEncryption,
     19     md5WithRSAEncryption,
     20 
     21     pub const map = std.ComptimeStringMap(Algorithm, .{
     22         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05 }, .sha1WithRSAEncryption },
     23         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B }, .sha256WithRSAEncryption },
     24         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C }, .sha384WithRSAEncryption },
     25         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D }, .sha512WithRSAEncryption },
     26         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E }, .sha224WithRSAEncryption },
     27         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01 }, .ecdsa_with_SHA224 },
     28         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }, .ecdsa_with_SHA256 },
     29         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03 }, .ecdsa_with_SHA384 },
     30         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04 }, .ecdsa_with_SHA512 },
     31         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x02 }, .md2WithRSAEncryption },
     32         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04 }, .md5WithRSAEncryption },
     33     });
     34 
     35     pub fn Hash(comptime algorithm: Algorithm) type {
     36         return switch (algorithm) {
     37             .sha1WithRSAEncryption => crypto.hash.Sha1,
     38             .ecdsa_with_SHA224, .sha224WithRSAEncryption => crypto.hash.sha2.Sha224,
     39             .ecdsa_with_SHA256, .sha256WithRSAEncryption => crypto.hash.sha2.Sha256,
     40             .ecdsa_with_SHA384, .sha384WithRSAEncryption => crypto.hash.sha2.Sha384,
     41             .ecdsa_with_SHA512, .sha512WithRSAEncryption => crypto.hash.sha2.Sha512,
     42             .md2WithRSAEncryption => @compileError("unimplemented"),
     43             .md5WithRSAEncryption => crypto.hash.Md5,
     44         };
     45     }
     46 };
     47 
     48 pub const AlgorithmCategory = enum {
     49     rsaEncryption,
     50     X9_62_id_ecPublicKey,
     51 
     52     pub const map = std.ComptimeStringMap(AlgorithmCategory, .{
     53         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }, .rsaEncryption },
     54         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01 }, .X9_62_id_ecPublicKey },
     55     });
     56 };
     57 
     58 pub const Attribute = enum {
     59     commonName,
     60     serialNumber,
     61     countryName,
     62     localityName,
     63     stateOrProvinceName,
     64     streetAddress,
     65     organizationName,
     66     organizationalUnitName,
     67     postalCode,
     68     organizationIdentifier,
     69     pkcs9_emailAddress,
     70     domainComponent,
     71 
     72     pub const map = std.ComptimeStringMap(Attribute, .{
     73         .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
     74         .{ &[_]u8{ 0x55, 0x04, 0x05 }, .serialNumber },
     75         .{ &[_]u8{ 0x55, 0x04, 0x06 }, .countryName },
     76         .{ &[_]u8{ 0x55, 0x04, 0x07 }, .localityName },
     77         .{ &[_]u8{ 0x55, 0x04, 0x08 }, .stateOrProvinceName },
     78         .{ &[_]u8{ 0x55, 0x04, 0x09 }, .streetAddress },
     79         .{ &[_]u8{ 0x55, 0x04, 0x0A }, .organizationName },
     80         .{ &[_]u8{ 0x55, 0x04, 0x0B }, .organizationalUnitName },
     81         .{ &[_]u8{ 0x55, 0x04, 0x11 }, .postalCode },
     82         .{ &[_]u8{ 0x55, 0x04, 0x61 }, .organizationIdentifier },
     83         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01 }, .pkcs9_emailAddress },
     84         .{ &[_]u8{ 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 }, .domainComponent },
     85     });
     86 };
     87 
     88 pub const NamedCurve = enum {
     89     secp384r1,
     90     secp521r1,
     91     X9_62_prime256v1,
     92 
     93     pub const map = std.ComptimeStringMap(NamedCurve, .{
     94         .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 },
     95         .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x23 }, .secp521r1 },
     96         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 },
     97     });
     98 
     99     pub fn Curve(comptime curve: NamedCurve) type {
    100         return switch (curve) {
    101             .X9_62_prime256v1 => crypto.ecc.P256,
    102             .secp384r1 => crypto.ecc.P384,
    103             .secp521r1 => @compileError("unimplemented"),
    104         };
    105     }
    106 };
    107 
    108 pub const ExtensionId = enum {
    109     subject_key_identifier,
    110     key_usage,
    111     private_key_usage_period,
    112     subject_alt_name,
    113     issuer_alt_name,
    114     basic_constraints,
    115     crl_number,
    116     certificate_policies,
    117     authority_key_identifier,
    118     msCertsrvCAVersion,
    119     commonName,
    120     ext_key_usage,
    121     crl_distribution_points,
    122     info_access,
    123     entrustVersInfo,
    124     enroll_certtype,
    125     pe_logotype,
    126     netscape_cert_type,
    127     netscape_comment,
    128 
    129     pub const map = std.ComptimeStringMap(ExtensionId, .{
    130         .{ &[_]u8{ 0x55, 0x04, 0x03 }, .commonName },
    131         .{ &[_]u8{ 0x55, 0x1D, 0x01 }, .authority_key_identifier },
    132         .{ &[_]u8{ 0x55, 0x1D, 0x07 }, .subject_alt_name },
    133         .{ &[_]u8{ 0x55, 0x1D, 0x0E }, .subject_key_identifier },
    134         .{ &[_]u8{ 0x55, 0x1D, 0x0F }, .key_usage },
    135         .{ &[_]u8{ 0x55, 0x1D, 0x0A }, .basic_constraints },
    136         .{ &[_]u8{ 0x55, 0x1D, 0x10 }, .private_key_usage_period },
    137         .{ &[_]u8{ 0x55, 0x1D, 0x11 }, .subject_alt_name },
    138         .{ &[_]u8{ 0x55, 0x1D, 0x12 }, .issuer_alt_name },
    139         .{ &[_]u8{ 0x55, 0x1D, 0x13 }, .basic_constraints },
    140         .{ &[_]u8{ 0x55, 0x1D, 0x14 }, .crl_number },
    141         .{ &[_]u8{ 0x55, 0x1D, 0x1F }, .crl_distribution_points },
    142         .{ &[_]u8{ 0x55, 0x1D, 0x20 }, .certificate_policies },
    143         .{ &[_]u8{ 0x55, 0x1D, 0x23 }, .authority_key_identifier },
    144         .{ &[_]u8{ 0x55, 0x1D, 0x25 }, .ext_key_usage },
    145         .{ &[_]u8{ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x15, 0x01 }, .msCertsrvCAVersion },
    146         .{ &[_]u8{ 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01 }, .info_access },
    147         .{ &[_]u8{ 0x2A, 0x86, 0x48, 0x86, 0xF6, 0x7D, 0x07, 0x41, 0x00 }, .entrustVersInfo },
    148         .{ &[_]u8{ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x14, 0x02 }, .enroll_certtype },
    149         .{ &[_]u8{ 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0c }, .pe_logotype },
    150         .{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x01 }, .netscape_cert_type },
    151         .{ &[_]u8{ 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x01, 0x0d }, .netscape_comment },
    152     });
    153 };
    154 
    155 pub const GeneralNameTag = enum(u5) {
    156     otherName = 0,
    157     rfc822Name = 1,
    158     dNSName = 2,
    159     x400Address = 3,
    160     directoryName = 4,
    161     ediPartyName = 5,
    162     uniformResourceIdentifier = 6,
    163     iPAddress = 7,
    164     registeredID = 8,
    165     _,
    166 };
    167 
    168 pub const Parsed = struct {
    169     certificate: Certificate,
    170     issuer_slice: Slice,
    171     subject_slice: Slice,
    172     common_name_slice: Slice,
    173     signature_slice: Slice,
    174     signature_algorithm: Algorithm,
    175     pub_key_algo: PubKeyAlgo,
    176     pub_key_slice: Slice,
    177     message_slice: Slice,
    178     subject_alt_name_slice: Slice,
    179     validity: Validity,
    180     version: Version,
    181 
    182     pub const PubKeyAlgo = union(AlgorithmCategory) {
    183         rsaEncryption: void,
    184         X9_62_id_ecPublicKey: NamedCurve,
    185     };
    186 
    187     pub const Validity = struct {
    188         not_before: u64,
    189         not_after: u64,
    190     };
    191 
    192     pub const Slice = der.Element.Slice;
    193 
    194     pub fn slice(p: Parsed, s: Slice) []const u8 {
    195         return p.certificate.buffer[s.start..s.end];
    196     }
    197 
    198     pub fn issuer(p: Parsed) []const u8 {
    199         return p.slice(p.issuer_slice);
    200     }
    201 
    202     pub fn subject(p: Parsed) []const u8 {
    203         return p.slice(p.subject_slice);
    204     }
    205 
    206     pub fn commonName(p: Parsed) []const u8 {
    207         return p.slice(p.common_name_slice);
    208     }
    209 
    210     pub fn signature(p: Parsed) []const u8 {
    211         return p.slice(p.signature_slice);
    212     }
    213 
    214     pub fn pubKey(p: Parsed) []const u8 {
    215         return p.slice(p.pub_key_slice);
    216     }
    217 
    218     pub fn pubKeySigAlgo(p: Parsed) []const u8 {
    219         return p.slice(p.pub_key_signature_algorithm_slice);
    220     }
    221 
    222     pub fn message(p: Parsed) []const u8 {
    223         return p.slice(p.message_slice);
    224     }
    225 
    226     pub fn subjectAltName(p: Parsed) []const u8 {
    227         return p.slice(p.subject_alt_name_slice);
    228     }
    229 
    230     pub const VerifyError = error{
    231         CertificateIssuerMismatch,
    232         CertificateNotYetValid,
    233         CertificateExpired,
    234         CertificateSignatureAlgorithmUnsupported,
    235         CertificateSignatureAlgorithmMismatch,
    236         CertificateFieldHasInvalidLength,
    237         CertificateFieldHasWrongDataType,
    238         CertificatePublicKeyInvalid,
    239         CertificateSignatureInvalidLength,
    240         CertificateSignatureInvalid,
    241         CertificateSignatureUnsupportedBitCount,
    242         CertificateSignatureNamedCurveUnsupported,
    243     };
    244 
    245     /// This function verifies:
    246     ///  * That the subject's issuer is indeed the provided issuer.
    247     ///  * The time validity of the subject.
    248     ///  * The signature.
    249     pub fn verify(parsed_subject: Parsed, parsed_issuer: Parsed, now_sec: i64) VerifyError!void {
    250         // Check that the subject's issuer name matches the issuer's
    251         // subject name.
    252         if (!mem.eql(u8, parsed_subject.issuer(), parsed_issuer.subject())) {
    253             return error.CertificateIssuerMismatch;
    254         }
    255 
    256         if (now_sec < parsed_subject.validity.not_before)
    257             return error.CertificateNotYetValid;
    258         if (now_sec > parsed_subject.validity.not_after)
    259             return error.CertificateExpired;
    260 
    261         switch (parsed_subject.signature_algorithm) {
    262             inline .sha1WithRSAEncryption,
    263             .sha224WithRSAEncryption,
    264             .sha256WithRSAEncryption,
    265             .sha384WithRSAEncryption,
    266             .sha512WithRSAEncryption,
    267             => |algorithm| return verifyRsa(
    268                 algorithm.Hash(),
    269                 parsed_subject.message(),
    270                 parsed_subject.signature(),
    271                 parsed_issuer.pub_key_algo,
    272                 parsed_issuer.pubKey(),
    273             ),
    274 
    275             inline .ecdsa_with_SHA224,
    276             .ecdsa_with_SHA256,
    277             .ecdsa_with_SHA384,
    278             .ecdsa_with_SHA512,
    279             => |algorithm| return verify_ecdsa(
    280                 algorithm.Hash(),
    281                 parsed_subject.message(),
    282                 parsed_subject.signature(),
    283                 parsed_issuer.pub_key_algo,
    284                 parsed_issuer.pubKey(),
    285             ),
    286 
    287             .md2WithRSAEncryption, .md5WithRSAEncryption => {
    288                 return error.CertificateSignatureAlgorithmUnsupported;
    289             },
    290         }
    291     }
    292 
    293     pub const VerifyHostNameError = error{
    294         CertificateHostMismatch,
    295         CertificateFieldHasInvalidLength,
    296     };
    297 
    298     pub fn verifyHostName(parsed_subject: Parsed, host_name: []const u8) VerifyHostNameError!void {
    299         // If the Subject Alternative Names extension is present, this is
    300         // what to check. Otherwise, only the common name is checked.
    301         const subject_alt_name = parsed_subject.subjectAltName();
    302         if (subject_alt_name.len == 0) {
    303             if (checkHostName(host_name, parsed_subject.commonName())) {
    304                 return;
    305             } else {
    306                 return error.CertificateHostMismatch;
    307             }
    308         }
    309 
    310         const general_names = try der.Element.parse(subject_alt_name, 0);
    311         var name_i = general_names.slice.start;
    312         while (name_i < general_names.slice.end) {
    313             const general_name = try der.Element.parse(subject_alt_name, name_i);
    314             name_i = general_name.slice.end;
    315             switch (@intToEnum(GeneralNameTag, @enumToInt(general_name.identifier.tag))) {
    316                 .dNSName => {
    317                     const dns_name = subject_alt_name[general_name.slice.start..general_name.slice.end];
    318                     if (checkHostName(host_name, dns_name)) return;
    319                 },
    320                 else => {},
    321             }
    322         }
    323 
    324         return error.CertificateHostMismatch;
    325     }
    326 
    327     // Check hostname according to RFC2818 specification:
    328     //
    329     // If more than one identity of a given type is present in
    330     // the certificate (e.g., more than one DNSName name, a match in any one
    331     // of the set is considered acceptable.) Names may contain the wildcard
    332     // character * which is considered to match any single domain name
    333     // component or component fragment. E.g., *.a.com matches foo.a.com but
    334     // not bar.foo.a.com. f*.com matches foo.com but not bar.com.
    335     fn checkHostName(host_name: []const u8, dns_name: []const u8) bool {
    336         if (mem.eql(u8, dns_name, host_name)) {
    337             return true; // exact match
    338         }
    339 
    340         var it_host = std.mem.splitScalar(u8, host_name, '.');
    341         var it_dns = std.mem.splitScalar(u8, dns_name, '.');
    342 
    343         const len_match = while (true) {
    344             const host = it_host.next();
    345             const dns = it_dns.next();
    346 
    347             if (host == null or dns == null) {
    348                 break host == null and dns == null;
    349             }
    350 
    351             // If not a wildcard and they dont
    352             // match then there is no match.
    353             if (mem.eql(u8, dns.?, "*") == false and mem.eql(u8, dns.?, host.?) == false) {
    354                 return false;
    355             }
    356         };
    357 
    358         // If the components are not the same
    359         // length then there is no match.
    360         return len_match;
    361     }
    362 };
    363 
    364 test "Parsed.checkHostName" {
    365     const expectEqual = std.testing.expectEqual;
    366 
    367     try expectEqual(true, Parsed.checkHostName("ziglang.org", "ziglang.org"));
    368     try expectEqual(true, Parsed.checkHostName("bar.ziglang.org", "*.ziglang.org"));
    369     try expectEqual(false, Parsed.checkHostName("foo.bar.ziglang.org", "*.ziglang.org"));
    370     try expectEqual(false, Parsed.checkHostName("ziglang.org", "zig*.org"));
    371     try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org"));
    372 }
    373 
    374 pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
    375 
    376 pub fn parse(cert: Certificate) ParseError!Parsed {
    377     const cert_bytes = cert.buffer;
    378     const certificate = try der.Element.parse(cert_bytes, cert.index);
    379     const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start);
    380     const version_elem = try der.Element.parse(cert_bytes, tbs_certificate.slice.start);
    381     const version = try parseVersion(cert_bytes, version_elem);
    382     const serial_number = if (@bitCast(u8, version_elem.identifier) == 0xa0)
    383         try der.Element.parse(cert_bytes, version_elem.slice.end)
    384     else
    385         version_elem;
    386     // RFC 5280, section 4.1.2.3:
    387     // "This field MUST contain the same algorithm identifier as
    388     // the signatureAlgorithm field in the sequence Certificate."
    389     const tbs_signature = try der.Element.parse(cert_bytes, serial_number.slice.end);
    390     const issuer = try der.Element.parse(cert_bytes, tbs_signature.slice.end);
    391     const validity = try der.Element.parse(cert_bytes, issuer.slice.end);
    392     const not_before = try der.Element.parse(cert_bytes, validity.slice.start);
    393     const not_before_utc = try parseTime(cert, not_before);
    394     const not_after = try der.Element.parse(cert_bytes, not_before.slice.end);
    395     const not_after_utc = try parseTime(cert, not_after);
    396     const subject = try der.Element.parse(cert_bytes, validity.slice.end);
    397 
    398     const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end);
    399     const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start);
    400     const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start);
    401     const pub_key_algo_tag = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem);
    402     var pub_key_algo: Parsed.PubKeyAlgo = undefined;
    403     switch (pub_key_algo_tag) {
    404         .rsaEncryption => {
    405             pub_key_algo = .{ .rsaEncryption = {} };
    406         },
    407         .X9_62_id_ecPublicKey => {
    408             // RFC 5480 Section 2.1.1.1 Named Curve
    409             // ECParameters ::= CHOICE {
    410             //   namedCurve         OBJECT IDENTIFIER
    411             //   -- implicitCurve   NULL
    412             //   -- specifiedCurve  SpecifiedECDomain
    413             // }
    414             const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end);
    415             const named_curve = try parseNamedCurve(cert_bytes, params_elem);
    416             pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve };
    417         },
    418     }
    419     const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end);
    420     const pub_key = try parseBitString(cert, pub_key_elem);
    421 
    422     var common_name = der.Element.Slice.empty;
    423     var name_i = subject.slice.start;
    424     while (name_i < subject.slice.end) {
    425         const rdn = try der.Element.parse(cert_bytes, name_i);
    426         var rdn_i = rdn.slice.start;
    427         while (rdn_i < rdn.slice.end) {
    428             const atav = try der.Element.parse(cert_bytes, rdn_i);
    429             var atav_i = atav.slice.start;
    430             while (atav_i < atav.slice.end) {
    431                 const ty_elem = try der.Element.parse(cert_bytes, atav_i);
    432                 const val = try der.Element.parse(cert_bytes, ty_elem.slice.end);
    433                 atav_i = val.slice.end;
    434                 const ty = parseAttribute(cert_bytes, ty_elem) catch |err| switch (err) {
    435                     error.CertificateHasUnrecognizedObjectId => continue,
    436                     else => |e| return e,
    437                 };
    438                 switch (ty) {
    439                     .commonName => common_name = val.slice,
    440                     else => {},
    441                 }
    442             }
    443             rdn_i = atav.slice.end;
    444         }
    445         name_i = rdn.slice.end;
    446     }
    447 
    448     const sig_algo = try der.Element.parse(cert_bytes, tbs_certificate.slice.end);
    449     const algo_elem = try der.Element.parse(cert_bytes, sig_algo.slice.start);
    450     const signature_algorithm = try parseAlgorithm(cert_bytes, algo_elem);
    451     const sig_elem = try der.Element.parse(cert_bytes, sig_algo.slice.end);
    452     const signature = try parseBitString(cert, sig_elem);
    453 
    454     // Extensions
    455     var subject_alt_name_slice = der.Element.Slice.empty;
    456     ext: {
    457         if (version == .v1)
    458             break :ext;
    459 
    460         if (pub_key_info.slice.end >= tbs_certificate.slice.end)
    461             break :ext;
    462 
    463         const outer_extensions = try der.Element.parse(cert_bytes, pub_key_info.slice.end);
    464         if (outer_extensions.identifier.tag != .bitstring)
    465             break :ext;
    466 
    467         const extensions = try der.Element.parse(cert_bytes, outer_extensions.slice.start);
    468 
    469         var ext_i = extensions.slice.start;
    470         while (ext_i < extensions.slice.end) {
    471             const extension = try der.Element.parse(cert_bytes, ext_i);
    472             ext_i = extension.slice.end;
    473             const oid_elem = try der.Element.parse(cert_bytes, extension.slice.start);
    474             const ext_id = parseExtensionId(cert_bytes, oid_elem) catch |err| switch (err) {
    475                 error.CertificateHasUnrecognizedObjectId => continue,
    476                 else => |e| return e,
    477             };
    478             const critical_elem = try der.Element.parse(cert_bytes, oid_elem.slice.end);
    479             const ext_bytes_elem = if (critical_elem.identifier.tag != .boolean)
    480                 critical_elem
    481             else
    482                 try der.Element.parse(cert_bytes, critical_elem.slice.end);
    483             switch (ext_id) {
    484                 .subject_alt_name => subject_alt_name_slice = ext_bytes_elem.slice,
    485                 else => continue,
    486             }
    487         }
    488     }
    489 
    490     return .{
    491         .certificate = cert,
    492         .common_name_slice = common_name,
    493         .issuer_slice = issuer.slice,
    494         .subject_slice = subject.slice,
    495         .signature_slice = signature,
    496         .signature_algorithm = signature_algorithm,
    497         .message_slice = .{ .start = certificate.slice.start, .end = tbs_certificate.slice.end },
    498         .pub_key_algo = pub_key_algo,
    499         .pub_key_slice = pub_key,
    500         .validity = .{
    501             .not_before = not_before_utc,
    502             .not_after = not_after_utc,
    503         },
    504         .subject_alt_name_slice = subject_alt_name_slice,
    505         .version = version,
    506     };
    507 }
    508 
    509 pub fn verify(subject: Certificate, issuer: Certificate, now_sec: i64) !void {
    510     const parsed_subject = try subject.parse();
    511     const parsed_issuer = try issuer.parse();
    512     return parsed_subject.verify(parsed_issuer, now_sec);
    513 }
    514 
    515 pub fn contents(cert: Certificate, elem: der.Element) []const u8 {
    516     return cert.buffer[elem.slice.start..elem.slice.end];
    517 }
    518 
    519 pub const ParseBitStringError = error{ CertificateFieldHasWrongDataType, CertificateHasInvalidBitString };
    520 
    521 pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
    522     if (elem.identifier.tag != .bitstring) return error.CertificateFieldHasWrongDataType;
    523     if (cert.buffer[elem.slice.start] != 0) return error.CertificateHasInvalidBitString;
    524     return .{ .start = elem.slice.start + 1, .end = elem.slice.end };
    525 }
    526 
    527 pub const ParseTimeError = error{ CertificateTimeInvalid, CertificateFieldHasWrongDataType };
    528 
    529 /// Returns number of seconds since epoch.
    530 pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
    531     const bytes = cert.contents(elem);
    532     switch (elem.identifier.tag) {
    533         .utc_time => {
    534             // Example: "YYMMDD000000Z"
    535             if (bytes.len != 13)
    536                 return error.CertificateTimeInvalid;
    537             if (bytes[12] != 'Z')
    538                 return error.CertificateTimeInvalid;
    539 
    540             return Date.toSeconds(.{
    541                 .year = @as(u16, 2000) + try parseTimeDigits(bytes[0..2].*, 0, 99),
    542                 .month = try parseTimeDigits(bytes[2..4].*, 1, 12),
    543                 .day = try parseTimeDigits(bytes[4..6].*, 1, 31),
    544                 .hour = try parseTimeDigits(bytes[6..8].*, 0, 23),
    545                 .minute = try parseTimeDigits(bytes[8..10].*, 0, 59),
    546                 .second = try parseTimeDigits(bytes[10..12].*, 0, 59),
    547             });
    548         },
    549         .generalized_time => {
    550             // Examples:
    551             // "19920521000000Z"
    552             // "19920622123421Z"
    553             // "19920722132100.3Z"
    554             if (bytes.len < 15)
    555                 return error.CertificateTimeInvalid;
    556             return Date.toSeconds(.{
    557                 .year = try parseYear4(bytes[0..4]),
    558                 .month = try parseTimeDigits(bytes[4..6].*, 1, 12),
    559                 .day = try parseTimeDigits(bytes[6..8].*, 1, 31),
    560                 .hour = try parseTimeDigits(bytes[8..10].*, 0, 23),
    561                 .minute = try parseTimeDigits(bytes[10..12].*, 0, 59),
    562                 .second = try parseTimeDigits(bytes[12..14].*, 0, 59),
    563             });
    564         },
    565         else => return error.CertificateFieldHasWrongDataType,
    566     }
    567 }
    568 
    569 const Date = struct {
    570     /// example: 1999
    571     year: u16,
    572     /// range: 1 to 12
    573     month: u8,
    574     /// range: 1 to 31
    575     day: u8,
    576     /// range: 0 to 59
    577     hour: u8,
    578     /// range: 0 to 59
    579     minute: u8,
    580     /// range: 0 to 59
    581     second: u8,
    582 
    583     /// Convert to number of seconds since epoch.
    584     pub fn toSeconds(date: Date) u64 {
    585         var sec: u64 = 0;
    586 
    587         {
    588             var year: u16 = 1970;
    589             while (year < date.year) : (year += 1) {
    590                 const days: u64 = std.time.epoch.getDaysInYear(year);
    591                 sec += days * std.time.epoch.secs_per_day;
    592             }
    593         }
    594 
    595         {
    596             const is_leap = std.time.epoch.isLeapYear(date.year);
    597             var month: u4 = 1;
    598             while (month < date.month) : (month += 1) {
    599                 const days: u64 = std.time.epoch.getDaysInMonth(
    600                     @intToEnum(std.time.epoch.YearLeapKind, @boolToInt(is_leap)),
    601                     @intToEnum(std.time.epoch.Month, month),
    602                 );
    603                 sec += days * std.time.epoch.secs_per_day;
    604             }
    605         }
    606 
    607         sec += (date.day - 1) * @as(u64, std.time.epoch.secs_per_day);
    608         sec += date.hour * @as(u64, 60 * 60);
    609         sec += date.minute * @as(u64, 60);
    610         sec += date.second;
    611 
    612         return sec;
    613     }
    614 };
    615 
    616 pub fn parseTimeDigits(nn: @Vector(2, u8), min: u8, max: u8) !u8 {
    617     const zero: @Vector(2, u8) = .{ '0', '0' };
    618     const mm: @Vector(2, u8) = .{ 10, 1 };
    619     const result = @reduce(.Add, (nn -% zero) *% mm);
    620     if (result < min) return error.CertificateTimeInvalid;
    621     if (result > max) return error.CertificateTimeInvalid;
    622     return result;
    623 }
    624 
    625 test parseTimeDigits {
    626     const expectEqual = std.testing.expectEqual;
    627     try expectEqual(@as(u8, 0), try parseTimeDigits("00".*, 0, 99));
    628     try expectEqual(@as(u8, 99), try parseTimeDigits("99".*, 0, 99));
    629     try expectEqual(@as(u8, 42), try parseTimeDigits("42".*, 0, 99));
    630 
    631     const expectError = std.testing.expectError;
    632     try expectError(error.CertificateTimeInvalid, parseTimeDigits("13".*, 1, 12));
    633     try expectError(error.CertificateTimeInvalid, parseTimeDigits("00".*, 1, 12));
    634 }
    635 
    636 pub fn parseYear4(text: *const [4]u8) !u16 {
    637     const nnnn: @Vector(4, u16) = .{ text[0], text[1], text[2], text[3] };
    638     const zero: @Vector(4, u16) = .{ '0', '0', '0', '0' };
    639     const mmmm: @Vector(4, u16) = .{ 1000, 100, 10, 1 };
    640     const result = @reduce(.Add, (nnnn -% zero) *% mmmm);
    641     if (result > 9999) return error.CertificateTimeInvalid;
    642     return result;
    643 }
    644 
    645 test parseYear4 {
    646     const expectEqual = std.testing.expectEqual;
    647     try expectEqual(@as(u16, 0), try parseYear4("0000"));
    648     try expectEqual(@as(u16, 9999), try parseYear4("9999"));
    649     try expectEqual(@as(u16, 1988), try parseYear4("1988"));
    650 
    651     const expectError = std.testing.expectError;
    652     try expectError(error.CertificateTimeInvalid, parseYear4("999b"));
    653     try expectError(error.CertificateTimeInvalid, parseYear4("crap"));
    654 }
    655 
    656 pub fn parseAlgorithm(bytes: []const u8, element: der.Element) ParseEnumError!Algorithm {
    657     return parseEnum(Algorithm, bytes, element);
    658 }
    659 
    660 pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) ParseEnumError!AlgorithmCategory {
    661     return parseEnum(AlgorithmCategory, bytes, element);
    662 }
    663 
    664 pub fn parseAttribute(bytes: []const u8, element: der.Element) ParseEnumError!Attribute {
    665     return parseEnum(Attribute, bytes, element);
    666 }
    667 
    668 pub fn parseNamedCurve(bytes: []const u8, element: der.Element) ParseEnumError!NamedCurve {
    669     return parseEnum(NamedCurve, bytes, element);
    670 }
    671 
    672 pub fn parseExtensionId(bytes: []const u8, element: der.Element) ParseEnumError!ExtensionId {
    673     return parseEnum(ExtensionId, bytes, element);
    674 }
    675 
    676 pub const ParseEnumError = error{ CertificateFieldHasWrongDataType, CertificateHasUnrecognizedObjectId };
    677 
    678 fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) ParseEnumError!E {
    679     if (element.identifier.tag != .object_identifier)
    680         return error.CertificateFieldHasWrongDataType;
    681     const oid_bytes = bytes[element.slice.start..element.slice.end];
    682     return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;
    683 }
    684 
    685 pub const ParseVersionError = error{ UnsupportedCertificateVersion, CertificateFieldHasInvalidLength };
    686 
    687 pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionError!Version {
    688     if (@bitCast(u8, version_elem.identifier) != 0xa0)
    689         return .v1;
    690 
    691     if (version_elem.slice.end - version_elem.slice.start != 3)
    692         return error.CertificateFieldHasInvalidLength;
    693 
    694     const encoded_version = bytes[version_elem.slice.start..version_elem.slice.end];
    695 
    696     if (mem.eql(u8, encoded_version, "\x02\x01\x02")) {
    697         return .v3;
    698     } else if (mem.eql(u8, encoded_version, "\x02\x01\x01")) {
    699         return .v2;
    700     } else if (mem.eql(u8, encoded_version, "\x02\x01\x00")) {
    701         return .v1;
    702     }
    703 
    704     return error.UnsupportedCertificateVersion;
    705 }
    706 
    707 fn verifyRsa(
    708     comptime Hash: type,
    709     message: []const u8,
    710     sig: []const u8,
    711     pub_key_algo: Parsed.PubKeyAlgo,
    712     pub_key: []const u8,
    713 ) !void {
    714     if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch;
    715     const pk_components = try rsa.PublicKey.parseDer(pub_key);
    716     const exponent = pk_components.exponent;
    717     const modulus = pk_components.modulus;
    718     if (exponent.len > modulus.len) return error.CertificatePublicKeyInvalid;
    719     if (sig.len != modulus.len) return error.CertificateSignatureInvalidLength;
    720 
    721     const hash_der = switch (Hash) {
    722         crypto.hash.Sha1 => [_]u8{
    723             0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
    724             0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
    725         },
    726         crypto.hash.sha2.Sha224 => [_]u8{
    727             0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    728             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
    729             0x00, 0x04, 0x1c,
    730         },
    731         crypto.hash.sha2.Sha256 => [_]u8{
    732             0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    733             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
    734             0x00, 0x04, 0x20,
    735         },
    736         crypto.hash.sha2.Sha384 => [_]u8{
    737             0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    738             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
    739             0x00, 0x04, 0x30,
    740         },
    741         crypto.hash.sha2.Sha512 => [_]u8{
    742             0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
    743             0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
    744             0x00, 0x04, 0x40,
    745         },
    746         else => @compileError("unreachable"),
    747     };
    748 
    749     var msg_hashed: [Hash.digest_length]u8 = undefined;
    750     Hash.hash(message, &msg_hashed, .{});
    751 
    752     var rsa_mem_buf: [512 * 64]u8 = undefined;
    753     var fba = std.heap.FixedBufferAllocator.init(&rsa_mem_buf);
    754     const ally = fba.allocator();
    755 
    756     switch (modulus.len) {
    757         inline 128, 256, 512 => |modulus_len| {
    758             const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3;
    759             const em: [modulus_len]u8 =
    760                 [2]u8{ 0, 1 } ++
    761                 ([1]u8{0xff} ** ps_len) ++
    762                 [1]u8{0} ++
    763                 hash_der ++
    764                 msg_hashed;
    765 
    766             const public_key = rsa.PublicKey.fromBytes(exponent, modulus, ally) catch |err| switch (err) {
    767                 error.OutOfMemory => unreachable, // rsa_mem_buf is big enough
    768             };
    769             const em_dec = rsa.encrypt(modulus_len, sig[0..modulus_len].*, public_key, ally) catch |err| switch (err) {
    770                 error.OutOfMemory => unreachable, // rsa_mem_buf is big enough
    771 
    772                 error.MessageTooLong => unreachable,
    773                 error.NegativeIntoUnsigned => @panic("TODO make RSA not emit this error"),
    774                 error.TargetTooSmall => @panic("TODO make RSA not emit this error"),
    775                 error.BufferTooSmall => @panic("TODO make RSA not emit this error"),
    776             };
    777 
    778             if (!mem.eql(u8, &em, &em_dec)) {
    779                 return error.CertificateSignatureInvalid;
    780             }
    781         },
    782         else => {
    783             return error.CertificateSignatureUnsupportedBitCount;
    784         },
    785     }
    786 }
    787 
    788 fn verify_ecdsa(
    789     comptime Hash: type,
    790     message: []const u8,
    791     encoded_sig: []const u8,
    792     pub_key_algo: Parsed.PubKeyAlgo,
    793     sec1_pub_key: []const u8,
    794 ) !void {
    795     const sig_named_curve = switch (pub_key_algo) {
    796         .X9_62_id_ecPublicKey => |named_curve| named_curve,
    797         else => return error.CertificateSignatureAlgorithmMismatch,
    798     };
    799 
    800     switch (sig_named_curve) {
    801         .secp521r1 => {
    802             return error.CertificateSignatureNamedCurveUnsupported;
    803         },
    804         inline .X9_62_prime256v1,
    805         .secp384r1,
    806         => |curve| {
    807             const Ecdsa = crypto.sign.ecdsa.Ecdsa(curve.Curve(), Hash);
    808             const sig = Ecdsa.Signature.fromDer(encoded_sig) catch |err| switch (err) {
    809                 error.InvalidEncoding => return error.CertificateSignatureInvalid,
    810             };
    811             const pub_key = Ecdsa.PublicKey.fromSec1(sec1_pub_key) catch |err| switch (err) {
    812                 error.InvalidEncoding => return error.CertificateSignatureInvalid,
    813                 error.NonCanonical => return error.CertificateSignatureInvalid,
    814                 error.NotSquare => return error.CertificateSignatureInvalid,
    815             };
    816             sig.verify(message, pub_key) catch |err| switch (err) {
    817                 error.IdentityElement => return error.CertificateSignatureInvalid,
    818                 error.NonCanonical => return error.CertificateSignatureInvalid,
    819                 error.SignatureVerificationFailed => return error.CertificateSignatureInvalid,
    820             };
    821         },
    822     }
    823 }
    824 
    825 const std = @import("../std.zig");
    826 const crypto = std.crypto;
    827 const mem = std.mem;
    828 const Certificate = @This();
    829 
    830 pub const der = struct {
    831     pub const Class = enum(u2) {
    832         universal,
    833         application,
    834         context_specific,
    835         private,
    836     };
    837 
    838     pub const PC = enum(u1) {
    839         primitive,
    840         constructed,
    841     };
    842 
    843     pub const Identifier = packed struct(u8) {
    844         tag: Tag,
    845         pc: PC,
    846         class: Class,
    847     };
    848 
    849     pub const Tag = enum(u5) {
    850         boolean = 1,
    851         integer = 2,
    852         bitstring = 3,
    853         octetstring = 4,
    854         null = 5,
    855         object_identifier = 6,
    856         sequence = 16,
    857         sequence_of = 17,
    858         utc_time = 23,
    859         generalized_time = 24,
    860         _,
    861     };
    862 
    863     pub const Element = struct {
    864         identifier: Identifier,
    865         slice: Slice,
    866 
    867         pub const Slice = struct {
    868             start: u32,
    869             end: u32,
    870 
    871             pub const empty: Slice = .{ .start = 0, .end = 0 };
    872         };
    873 
    874         pub const ParseElementError = error{CertificateFieldHasInvalidLength};
    875 
    876         pub fn parse(bytes: []const u8, index: u32) ParseElementError!Element {
    877             var i = index;
    878             const identifier = @bitCast(Identifier, bytes[i]);
    879             i += 1;
    880             const size_byte = bytes[i];
    881             i += 1;
    882             if ((size_byte >> 7) == 0) {
    883                 return .{
    884                     .identifier = identifier,
    885                     .slice = .{
    886                         .start = i,
    887                         .end = i + size_byte,
    888                     },
    889                 };
    890             }
    891 
    892             const len_size = @truncate(u7, size_byte);
    893             if (len_size > @sizeOf(u32)) {
    894                 return error.CertificateFieldHasInvalidLength;
    895             }
    896 
    897             const end_i = i + len_size;
    898             var long_form_size: u32 = 0;
    899             while (i < end_i) : (i += 1) {
    900                 long_form_size = (long_form_size << 8) | bytes[i];
    901             }
    902 
    903             return .{
    904                 .identifier = identifier,
    905                 .slice = .{
    906                     .start = i,
    907                     .end = i + long_form_size,
    908                 },
    909             };
    910         }
    911     };
    912 };
    913 
    914 test {
    915     _ = Bundle;
    916 }
    917 
    918 /// TODO: replace this with Frank's upcoming RSA implementation. the verify
    919 /// function won't have the possibility of failure - it will either identify a
    920 /// valid signature or an invalid signature.
    921 /// This code is borrowed from https://github.com/shiguredo/tls13-zig
    922 /// which is licensed under the Apache License Version 2.0, January 2004
    923 /// http://www.apache.org/licenses/
    924 /// The code has been modified.
    925 pub const rsa = struct {
    926     const BigInt = std.math.big.int.Managed;
    927 
    928     pub const PSSSignature = struct {
    929         pub fn fromBytes(comptime modulus_len: usize, msg: []const u8) [modulus_len]u8 {
    930             var result = [1]u8{0} ** modulus_len;
    931             std.mem.copyForwards(u8, &result, msg);
    932             return result;
    933         }
    934 
    935         pub fn verify(comptime modulus_len: usize, sig: [modulus_len]u8, msg: []const u8, public_key: PublicKey, comptime Hash: type, allocator: std.mem.Allocator) !void {
    936             const mod_bits = try countBits(public_key.n.toConst(), allocator);
    937             const em_dec = try encrypt(modulus_len, sig, public_key, allocator);
    938 
    939             try EMSA_PSS_VERIFY(msg, &em_dec, mod_bits - 1, Hash.digest_length, Hash, allocator);
    940         }
    941 
    942         fn EMSA_PSS_VERIFY(msg: []const u8, em: []const u8, emBit: usize, sLen: usize, comptime Hash: type, allocator: std.mem.Allocator) !void {
    943             // TODO
    944             // 1.   If the length of M is greater than the input limitation for
    945             //      the hash function (2^61 - 1 octets for SHA-1), output
    946             //      "inconsistent" and stop.
    947 
    948             // emLen = \ceil(emBits/8)
    949             const emLen = ((emBit - 1) / 8) + 1;
    950             std.debug.assert(emLen == em.len);
    951 
    952             // 2.   Let mHash = Hash(M), an octet string of length hLen.
    953             var mHash: [Hash.digest_length]u8 = undefined;
    954             Hash.hash(msg, &mHash, .{});
    955 
    956             // 3.   If emLen < hLen + sLen + 2, output "inconsistent" and stop.
    957             if (emLen < Hash.digest_length + sLen + 2) {
    958                 return error.InvalidSignature;
    959             }
    960 
    961             // 4.   If the rightmost octet of EM does not have hexadecimal value
    962             //      0xbc, output "inconsistent" and stop.
    963             if (em[em.len - 1] != 0xbc) {
    964                 return error.InvalidSignature;
    965             }
    966 
    967             // 5.   Let maskedDB be the leftmost emLen - hLen - 1 octets of EM,
    968             //      and let H be the next hLen octets.
    969             const maskedDB = em[0..(emLen - Hash.digest_length - 1)];
    970             const h = em[(emLen - Hash.digest_length - 1)..(emLen - 1)];
    971 
    972             // 6.   If the leftmost 8emLen - emBits bits of the leftmost octet in
    973             //      maskedDB are not all equal to zero, output "inconsistent" and
    974             //      stop.
    975             const zero_bits = emLen * 8 - emBit;
    976             var mask: u8 = maskedDB[0];
    977             var i: usize = 0;
    978             while (i < 8 - zero_bits) : (i += 1) {
    979                 mask = mask >> 1;
    980             }
    981             if (mask != 0) {
    982                 return error.InvalidSignature;
    983             }
    984 
    985             // 7.   Let dbMask = MGF(H, emLen - hLen - 1).
    986             const mgf_len = emLen - Hash.digest_length - 1;
    987             var mgf_out = try allocator.alloc(u8, ((mgf_len - 1) / Hash.digest_length + 1) * Hash.digest_length);
    988             defer allocator.free(mgf_out);
    989             var dbMask = try MGF1(mgf_out, h, mgf_len, Hash, allocator);
    990 
    991             // 8.   Let DB = maskedDB \xor dbMask.
    992             i = 0;
    993             while (i < dbMask.len) : (i += 1) {
    994                 dbMask[i] = maskedDB[i] ^ dbMask[i];
    995             }
    996 
    997             // 9.   Set the leftmost 8emLen - emBits bits of the leftmost octet
    998             //      in DB to zero.
    999             i = 0;
   1000             mask = 0;
   1001             while (i < 8 - zero_bits) : (i += 1) {
   1002                 mask = mask << 1;
   1003                 mask += 1;
   1004             }
   1005             dbMask[0] = dbMask[0] & mask;
   1006 
   1007             // 10.  If the emLen - hLen - sLen - 2 leftmost octets of DB are not
   1008             //      zero or if the octet at position emLen - hLen - sLen - 1 (the
   1009             //      leftmost position is "position 1") does not have hexadecimal
   1010             //      value 0x01, output "inconsistent" and stop.
   1011             if (dbMask[mgf_len - sLen - 2] != 0x00) {
   1012                 return error.InvalidSignature;
   1013             }
   1014 
   1015             if (dbMask[mgf_len - sLen - 1] != 0x01) {
   1016                 return error.InvalidSignature;
   1017             }
   1018 
   1019             // 11.  Let salt be the last sLen octets of DB.
   1020             const salt = dbMask[(mgf_len - sLen)..];
   1021 
   1022             // 12.  Let
   1023             //         M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
   1024             //      M' is an octet string of length 8 + hLen + sLen with eight
   1025             //      initial zero octets.
   1026             var m_p = try allocator.alloc(u8, 8 + Hash.digest_length + sLen);
   1027             defer allocator.free(m_p);
   1028             std.mem.copyForwards(u8, m_p, &([_]u8{0} ** 8));
   1029             std.mem.copyForwards(u8, m_p[8..], &mHash);
   1030             std.mem.copyForwards(u8, m_p[(8 + Hash.digest_length)..], salt);
   1031 
   1032             // 13.  Let H' = Hash(M'), an octet string of length hLen.
   1033             var h_p: [Hash.digest_length]u8 = undefined;
   1034             Hash.hash(m_p, &h_p, .{});
   1035 
   1036             // 14.  If H = H', output "consistent".  Otherwise, output
   1037             //      "inconsistent".
   1038             if (!std.mem.eql(u8, h, &h_p)) {
   1039                 return error.InvalidSignature;
   1040             }
   1041         }
   1042 
   1043         fn MGF1(out: []u8, seed: []const u8, len: usize, comptime Hash: type, allocator: std.mem.Allocator) ![]u8 {
   1044             var counter: usize = 0;
   1045             var idx: usize = 0;
   1046             var c: [4]u8 = undefined;
   1047 
   1048             var hash = try allocator.alloc(u8, seed.len + c.len);
   1049             defer allocator.free(hash);
   1050             std.mem.copyForwards(u8, hash, seed);
   1051             var hashed: [Hash.digest_length]u8 = undefined;
   1052 
   1053             while (idx < len) {
   1054                 c[0] = @intCast(u8, (counter >> 24) & 0xFF);
   1055                 c[1] = @intCast(u8, (counter >> 16) & 0xFF);
   1056                 c[2] = @intCast(u8, (counter >> 8) & 0xFF);
   1057                 c[3] = @intCast(u8, counter & 0xFF);
   1058 
   1059                 std.mem.copyForwards(u8, hash[seed.len..], &c);
   1060                 Hash.hash(hash, &hashed, .{});
   1061 
   1062                 std.mem.copyForwards(u8, out[idx..], &hashed);
   1063                 idx += hashed.len;
   1064 
   1065                 counter += 1;
   1066             }
   1067 
   1068             return out[0..len];
   1069         }
   1070     };
   1071 
   1072     pub const PublicKey = struct {
   1073         n: BigInt,
   1074         e: BigInt,
   1075 
   1076         pub fn deinit(self: *PublicKey) void {
   1077             self.n.deinit();
   1078             self.e.deinit();
   1079         }
   1080 
   1081         pub fn fromBytes(pub_bytes: []const u8, modulus_bytes: []const u8, allocator: std.mem.Allocator) !PublicKey {
   1082             var _n = try BigInt.init(allocator);
   1083             errdefer _n.deinit();
   1084             try setBytes(&_n, modulus_bytes, allocator);
   1085 
   1086             var _e = try BigInt.init(allocator);
   1087             errdefer _e.deinit();
   1088             try setBytes(&_e, pub_bytes, allocator);
   1089 
   1090             return .{
   1091                 .n = _n,
   1092                 .e = _e,
   1093             };
   1094         }
   1095 
   1096         pub fn parseDer(pub_key: []const u8) !struct { modulus: []const u8, exponent: []const u8 } {
   1097             const pub_key_seq = try der.Element.parse(pub_key, 0);
   1098             if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType;
   1099             const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start);
   1100             if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
   1101             const exponent_elem = try der.Element.parse(pub_key, modulus_elem.slice.end);
   1102             if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType;
   1103             // Skip over meaningless zeroes in the modulus.
   1104             const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end];
   1105             const modulus_offset = for (modulus_raw, 0..) |byte, i| {
   1106                 if (byte != 0) break i;
   1107             } else modulus_raw.len;
   1108             return .{
   1109                 .modulus = modulus_raw[modulus_offset..],
   1110                 .exponent = pub_key[exponent_elem.slice.start..exponent_elem.slice.end],
   1111             };
   1112         }
   1113     };
   1114 
   1115     fn encrypt(comptime modulus_len: usize, msg: [modulus_len]u8, public_key: PublicKey, allocator: std.mem.Allocator) ![modulus_len]u8 {
   1116         var m = try BigInt.init(allocator);
   1117         defer m.deinit();
   1118 
   1119         try setBytes(&m, &msg, allocator);
   1120 
   1121         if (m.order(public_key.n) != .lt) {
   1122             return error.MessageTooLong;
   1123         }
   1124 
   1125         var e = try BigInt.init(allocator);
   1126         defer e.deinit();
   1127 
   1128         try pow_montgomery(&e, &m, &public_key.e, &public_key.n, allocator);
   1129 
   1130         var res: [modulus_len]u8 = undefined;
   1131 
   1132         try toBytes(&res, &e, allocator);
   1133 
   1134         return res;
   1135     }
   1136 
   1137     fn setBytes(r: *BigInt, bytes: []const u8, allocator: std.mem.Allocator) !void {
   1138         try r.set(0);
   1139         var tmp = try BigInt.init(allocator);
   1140         defer tmp.deinit();
   1141         for (bytes) |b| {
   1142             try r.shiftLeft(r, 8);
   1143             try tmp.set(b);
   1144             try r.add(r, &tmp);
   1145         }
   1146     }
   1147 
   1148     fn pow_montgomery(r: *BigInt, a: *const BigInt, x: *const BigInt, n: *const BigInt, allocator: std.mem.Allocator) !void {
   1149         var bin_raw: [512]u8 = undefined;
   1150         try toBytes(&bin_raw, x, allocator);
   1151 
   1152         var i: usize = 0;
   1153         while (bin_raw[i] == 0x00) : (i += 1) {}
   1154         const bin = bin_raw[i..];
   1155 
   1156         try r.set(1);
   1157         var r1 = try BigInt.init(allocator);
   1158         defer r1.deinit();
   1159         try BigInt.copy(&r1, a.toConst());
   1160         i = 0;
   1161         while (i < bin.len * 8) : (i += 1) {
   1162             if (((bin[i / 8] >> @intCast(u3, (7 - (i % 8)))) & 0x1) == 0) {
   1163                 try BigInt.mul(&r1, r, &r1);
   1164                 try mod(&r1, &r1, n, allocator);
   1165                 try BigInt.sqr(r, r);
   1166                 try mod(r, r, n, allocator);
   1167             } else {
   1168                 try BigInt.mul(r, r, &r1);
   1169                 try mod(r, r, n, allocator);
   1170                 try BigInt.sqr(&r1, &r1);
   1171                 try mod(&r1, &r1, n, allocator);
   1172             }
   1173         }
   1174     }
   1175 
   1176     fn toBytes(out: []u8, a: *const BigInt, allocator: std.mem.Allocator) !void {
   1177         const Error = error{
   1178             BufferTooSmall,
   1179         };
   1180 
   1181         var mask = try BigInt.initSet(allocator, 0xFF);
   1182         defer mask.deinit();
   1183         var tmp = try BigInt.init(allocator);
   1184         defer tmp.deinit();
   1185 
   1186         var a_copy = try BigInt.init(allocator);
   1187         defer a_copy.deinit();
   1188         try a_copy.copy(a.toConst());
   1189 
   1190         // Encoding into big-endian bytes
   1191         var i: usize = 0;
   1192         while (i < out.len) : (i += 1) {
   1193             try tmp.bitAnd(&a_copy, &mask);
   1194             const b = try tmp.to(u8);
   1195             out[out.len - i - 1] = b;
   1196             try a_copy.shiftRight(&a_copy, 8);
   1197         }
   1198 
   1199         if (!a_copy.eqZero()) {
   1200             return Error.BufferTooSmall;
   1201         }
   1202     }
   1203 
   1204     fn mod(rem: *BigInt, a: *const BigInt, n: *const BigInt, allocator: std.mem.Allocator) !void {
   1205         var q = try BigInt.init(allocator);
   1206         defer q.deinit();
   1207 
   1208         try BigInt.divFloor(&q, rem, a, n);
   1209     }
   1210 
   1211     fn countBits(a: std.math.big.int.Const, allocator: std.mem.Allocator) !usize {
   1212         var i: usize = 0;
   1213         var a_copy = try BigInt.init(allocator);
   1214         defer a_copy.deinit();
   1215         try a_copy.copy(a);
   1216 
   1217         while (!a_copy.eqZero()) {
   1218             try a_copy.shiftRight(&a_copy, 1);
   1219             i += 1;
   1220         }
   1221 
   1222         return i;
   1223     }
   1224 };