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 };