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