| ... | @@ -71,6 +71,16 @@ pub const Attribute = enum { | ... | @@ -71,6 +71,16 @@ pub const Attribute = enum { |
| 71 | }); | 71 | }); |
| 72 | }; | 72 | }; |
| 73 | | 73 | |
| | 74 | pub const NamedCurve = enum { |
| | 75 | secp384r1, |
| | 76 | X9_62_prime256v1, |
| | 77 | |
| | 78 | pub const map = std.ComptimeStringMap(NamedCurve, .{ |
| | 79 | .{ &[_]u8{ 0x2B, 0x81, 0x04, 0x00, 0x22 }, .secp384r1 }, |
| | 80 | .{ &[_]u8{ 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07 }, .X9_62_prime256v1 }, |
| | 81 | }); |
| | 82 | }; |
| | 83 | |
| 74 | pub const Parsed = struct { | 84 | pub const Parsed = struct { |
| 75 | certificate: Certificate, | 85 | certificate: Certificate, |
| 76 | issuer_slice: Slice, | 86 | issuer_slice: Slice, |
| ... | @@ -78,11 +88,16 @@ pub const Parsed = struct { | ... | @@ -78,11 +88,16 @@ pub const Parsed = struct { |
| 78 | common_name_slice: Slice, | 88 | common_name_slice: Slice, |
| 79 | signature_slice: Slice, | 89 | signature_slice: Slice, |
| 80 | signature_algorithm: Algorithm, | 90 | signature_algorithm: Algorithm, |
| 81 | pub_key_algo: AlgorithmCategory, | 91 | pub_key_algo: PubKeyAlgo, |
| 82 | pub_key_slice: Slice, | 92 | pub_key_slice: Slice, |
| 83 | message_slice: Slice, | 93 | message_slice: Slice, |
| 84 | validity: Validity, | 94 | validity: Validity, |
| 85 | | 95 | |
| | 96 | pub const PubKeyAlgo = union(AlgorithmCategory) { |
| | 97 | rsaEncryption: void, |
| | 98 | X9_62_id_ecPublicKey: NamedCurve, |
| | 99 | }; |
| | 100 | |
| 86 | pub const Validity = struct { | 101 | pub const Validity = struct { |
| 87 | not_before: u64, | 102 | not_before: u64, |
| 88 | not_after: u64, | 103 | not_after: u64, |
| ... | @@ -114,6 +129,10 @@ pub const Parsed = struct { | ... | @@ -114,6 +129,10 @@ pub const Parsed = struct { |
| 114 | return p.slice(p.pub_key_slice); | 129 | return p.slice(p.pub_key_slice); |
| 115 | } | 130 | } |
| 116 | | 131 | |
| | 132 | pub fn pubKeySigAlgo(p: Parsed) []const u8 { |
| | 133 | return p.slice(p.pub_key_signature_algorithm_slice); |
| | 134 | } |
| | 135 | |
| 117 | pub fn message(p: Parsed) []const u8 { | 136 | pub fn message(p: Parsed) []const u8 { |
| 118 | return p.slice(p.message_slice); | 137 | return p.slice(p.message_slice); |
| 119 | } | 138 | } |
| ... | @@ -130,6 +149,7 @@ pub const Parsed = struct { | ... | @@ -130,6 +149,7 @@ pub const Parsed = struct { |
| 130 | CertificateSignatureInvalidLength, | 149 | CertificateSignatureInvalidLength, |
| 131 | CertificateSignatureInvalid, | 150 | CertificateSignatureInvalid, |
| 132 | CertificateSignatureUnsupportedBitCount, | 151 | CertificateSignatureUnsupportedBitCount, |
| | 152 | CertificateSignatureNamedCurveUnsupported, |
| 133 | }; | 153 | }; |
| 134 | | 154 | |
| 135 | /// This function checks the time validity for the subject only. Checking | 155 | /// This function checks the time validity for the subject only. Checking |
| ... | @@ -160,56 +180,78 @@ pub const Parsed = struct { | ... | @@ -160,56 +180,78 @@ pub const Parsed = struct { |
| 160 | parsed_issuer.pub_key_algo, | 180 | parsed_issuer.pub_key_algo, |
| 161 | parsed_issuer.pubKey(), | 181 | parsed_issuer.pubKey(), |
| 162 | ), | 182 | ), |
| 163 | .ecdsa_with_SHA224, | 183 | |
| | 184 | inline .ecdsa_with_SHA224, |
| 164 | .ecdsa_with_SHA256, | 185 | .ecdsa_with_SHA256, |
| 165 | .ecdsa_with_SHA384, | 186 | .ecdsa_with_SHA384, |
| 166 | .ecdsa_with_SHA512, | 187 | .ecdsa_with_SHA512, |
| 167 | => { | 188 | => |algorithm| return verify_ecdsa( |
| 168 | return error.CertificateSignatureAlgorithmUnsupported; | 189 | algorithm.Hash(), |
| 169 | }, | 190 | parsed_subject.message(), |
| | 191 | parsed_subject.signature(), |
| | 192 | parsed_issuer.pub_key_algo, |
| | 193 | parsed_issuer.pubKey(), |
| | 194 | ), |
| 170 | } | 195 | } |
| 171 | } | 196 | } |
| 172 | }; | 197 | }; |
| 173 | | 198 | |
| 174 | pub fn parse(cert: Certificate) !Parsed { | 199 | pub fn parse(cert: Certificate) !Parsed { |
| 175 | const cert_bytes = cert.buffer; | 200 | const cert_bytes = cert.buffer; |
| 176 | const certificate = try der.parseElement(cert_bytes, cert.index); | 201 | const certificate = try der.Element.parse(cert_bytes, cert.index); |
| 177 | const tbs_certificate = try der.parseElement(cert_bytes, certificate.slice.start); | 202 | const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start); |
| 178 | const version = try der.parseElement(cert_bytes, tbs_certificate.slice.start); | 203 | const version = try der.Element.parse(cert_bytes, tbs_certificate.slice.start); |
| 179 | try checkVersion(cert_bytes, version); | 204 | try checkVersion(cert_bytes, version); |
| 180 | const serial_number = try der.parseElement(cert_bytes, version.slice.end); | 205 | const serial_number = try der.Element.parse(cert_bytes, version.slice.end); |
| 181 | // RFC 5280, section 4.1.2.3: | 206 | // RFC 5280, section 4.1.2.3: |
| 182 | // "This field MUST contain the same algorithm identifier as | 207 | // "This field MUST contain the same algorithm identifier as |
| 183 | // the signatureAlgorithm field in the sequence Certificate." | 208 | // the signatureAlgorithm field in the sequence Certificate." |
| 184 | const tbs_signature = try der.parseElement(cert_bytes, serial_number.slice.end); | 209 | const tbs_signature = try der.Element.parse(cert_bytes, serial_number.slice.end); |
| 185 | const issuer = try der.parseElement(cert_bytes, tbs_signature.slice.end); | 210 | const issuer = try der.Element.parse(cert_bytes, tbs_signature.slice.end); |
| 186 | const validity = try der.parseElement(cert_bytes, issuer.slice.end); | 211 | const validity = try der.Element.parse(cert_bytes, issuer.slice.end); |
| 187 | const not_before = try der.parseElement(cert_bytes, validity.slice.start); | 212 | const not_before = try der.Element.parse(cert_bytes, validity.slice.start); |
| 188 | const not_before_utc = try parseTime(cert, not_before); | 213 | const not_before_utc = try parseTime(cert, not_before); |
| 189 | const not_after = try der.parseElement(cert_bytes, not_before.slice.end); | 214 | const not_after = try der.Element.parse(cert_bytes, not_before.slice.end); |
| 190 | const not_after_utc = try parseTime(cert, not_after); | 215 | const not_after_utc = try parseTime(cert, not_after); |
| 191 | const subject = try der.parseElement(cert_bytes, validity.slice.end); | 216 | const subject = try der.Element.parse(cert_bytes, validity.slice.end); |
| 192 | | 217 | |
| 193 | const pub_key_info = try der.parseElement(cert_bytes, subject.slice.end); | 218 | const pub_key_info = try der.Element.parse(cert_bytes, subject.slice.end); |
| 194 | const pub_key_signature_algorithm = try der.parseElement(cert_bytes, pub_key_info.slice.start); | 219 | const pub_key_signature_algorithm = try der.Element.parse(cert_bytes, pub_key_info.slice.start); |
| 195 | const pub_key_algo_elem = try der.parseElement(cert_bytes, pub_key_signature_algorithm.slice.start); | 220 | const pub_key_algo_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.start); |
| 196 | const pub_key_algo = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem); | 221 | const pub_key_algo_tag = try parseAlgorithmCategory(cert_bytes, pub_key_algo_elem); |
| 197 | const pub_key_elem = try der.parseElement(cert_bytes, pub_key_signature_algorithm.slice.end); | 222 | var pub_key_algo: Parsed.PubKeyAlgo = undefined; |
| | 223 | switch (pub_key_algo_tag) { |
| | 224 | .rsaEncryption => { |
| | 225 | pub_key_algo = .{ .rsaEncryption = {} }; |
| | 226 | }, |
| | 227 | .X9_62_id_ecPublicKey => { |
| | 228 | // RFC 5480 Section 2.1.1.1 Named Curve |
| | 229 | // ECParameters ::= CHOICE { |
| | 230 | // namedCurve OBJECT IDENTIFIER |
| | 231 | // -- implicitCurve NULL |
| | 232 | // -- specifiedCurve SpecifiedECDomain |
| | 233 | // } |
| | 234 | const params_elem = try der.Element.parse(cert_bytes, pub_key_algo_elem.slice.end); |
| | 235 | const named_curve = try parseNamedCurve(cert_bytes, params_elem); |
| | 236 | pub_key_algo = .{ .X9_62_id_ecPublicKey = named_curve }; |
| | 237 | }, |
| | 238 | } |
| | 239 | const pub_key_elem = try der.Element.parse(cert_bytes, pub_key_signature_algorithm.slice.end); |
| 198 | const pub_key = try parseBitString(cert, pub_key_elem); | 240 | const pub_key = try parseBitString(cert, pub_key_elem); |
| 199 | | 241 | |
| 200 | var common_name = der.Element.Slice.empty; | 242 | var common_name = der.Element.Slice.empty; |
| 201 | var name_i = subject.slice.start; | 243 | var name_i = subject.slice.start; |
| 202 | //std.debug.print("subject name:\n", .{}); | 244 | //std.debug.print("subject name:\n", .{}); |
| 203 | while (name_i < subject.slice.end) { | 245 | while (name_i < subject.slice.end) { |
| 204 | const rdn = try der.parseElement(cert_bytes, name_i); | 246 | const rdn = try der.Element.parse(cert_bytes, name_i); |
| 205 | var rdn_i = rdn.slice.start; | 247 | var rdn_i = rdn.slice.start; |
| 206 | while (rdn_i < rdn.slice.end) { | 248 | while (rdn_i < rdn.slice.end) { |
| 207 | const atav = try der.parseElement(cert_bytes, rdn_i); | 249 | const atav = try der.Element.parse(cert_bytes, rdn_i); |
| 208 | var atav_i = atav.slice.start; | 250 | var atav_i = atav.slice.start; |
| 209 | while (atav_i < atav.slice.end) { | 251 | while (atav_i < atav.slice.end) { |
| 210 | const ty_elem = try der.parseElement(cert_bytes, atav_i); | 252 | const ty_elem = try der.Element.parse(cert_bytes, atav_i); |
| 211 | const ty = try parseAttribute(cert_bytes, ty_elem); | 253 | const ty = try parseAttribute(cert_bytes, ty_elem); |
| 212 | const val = try der.parseElement(cert_bytes, ty_elem.slice.end); | 254 | const val = try der.Element.parse(cert_bytes, ty_elem.slice.end); |
| 213 | //std.debug.print(" {s}: '{s}'\n", .{ | 255 | //std.debug.print(" {s}: '{s}'\n", .{ |
| 214 | // @tagName(ty), cert_bytes[val.slice.start..val.slice.end], | 256 | // @tagName(ty), cert_bytes[val.slice.start..val.slice.end], |
| 215 | //}); | 257 | //}); |
| ... | @@ -224,10 +266,10 @@ pub fn parse(cert: Certificate) !Parsed { | ... | @@ -224,10 +266,10 @@ pub fn parse(cert: Certificate) !Parsed { |
| 224 | name_i = rdn.slice.end; | 266 | name_i = rdn.slice.end; |
| 225 | } | 267 | } |
| 226 | | 268 | |
| 227 | const sig_algo = try der.parseElement(cert_bytes, tbs_certificate.slice.end); | 269 | const sig_algo = try der.Element.parse(cert_bytes, tbs_certificate.slice.end); |
| 228 | const algo_elem = try der.parseElement(cert_bytes, sig_algo.slice.start); | 270 | const algo_elem = try der.Element.parse(cert_bytes, sig_algo.slice.start); |
| 229 | const signature_algorithm = try parseAlgorithm(cert_bytes, algo_elem); | 271 | const signature_algorithm = try parseAlgorithm(cert_bytes, algo_elem); |
| 230 | const sig_elem = try der.parseElement(cert_bytes, sig_algo.slice.end); | 272 | const sig_elem = try der.Element.parse(cert_bytes, sig_algo.slice.end); |
| 231 | const signature = try parseBitString(cert, sig_elem); | 273 | const signature = try parseBitString(cert, sig_elem); |
| 232 | | 274 | |
| 233 | return .{ | 275 | return .{ |
| ... | @@ -391,45 +433,52 @@ test parseYear4 { | ... | @@ -391,45 +433,52 @@ test parseYear4 { |
| 391 | } | 433 | } |
| 392 | | 434 | |
| 393 | pub fn parseAlgorithm(bytes: []const u8, element: der.Element) !Algorithm { | 435 | pub fn parseAlgorithm(bytes: []const u8, element: der.Element) !Algorithm { |
| 394 | if (element.identifier.tag != .object_identifier) | 436 | return parseEnum(Algorithm, bytes, element); |
| 395 | return error.CertificateFieldHasWrongDataType; | | |
| 396 | const oid_bytes = bytes[element.slice.start..element.slice.end]; | | |
| 397 | return Algorithm.map.get(oid_bytes) orelse { | | |
| 398 | //std.debug.print("oid bytes: {}\n", .{std.fmt.fmtSliceHexLower(oid_bytes)}); | | |
| 399 | return error.CertificateHasUnrecognizedAlgorithm; | | |
| 400 | }; | | |
| 401 | } | 437 | } |
| 402 | | 438 | |
| 403 | pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) !AlgorithmCategory { | 439 | pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) !AlgorithmCategory { |
| 404 | if (element.identifier.tag != .object_identifier) | 440 | return parseEnum(AlgorithmCategory, bytes, element); |
| 405 | return error.CertificateFieldHasWrongDataType; | | |
| 406 | return AlgorithmCategory.map.get(bytes[element.slice.start..element.slice.end]) orelse | | |
| 407 | return error.CertificateHasUnrecognizedAlgorithmCategory; | | |
| 408 | } | 441 | } |
| 409 | | 442 | |
| 410 | pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute { | 443 | pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute { |
| | 444 | return parseEnum(Attribute, bytes, element); |
| | 445 | } |
| | 446 | |
| | 447 | pub fn parseNamedCurve(bytes: []const u8, element: der.Element) !NamedCurve { |
| | 448 | return parseEnum(NamedCurve, bytes, element); |
| | 449 | } |
| | 450 | |
| | 451 | fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) !E { |
| 411 | if (element.identifier.tag != .object_identifier) | 452 | if (element.identifier.tag != .object_identifier) |
| 412 | return error.CertificateFieldHasWrongDataType; | 453 | return error.CertificateFieldHasWrongDataType; |
| 413 | const oid_bytes = bytes[element.slice.start..element.slice.end]; | 454 | const oid_bytes = bytes[element.slice.start..element.slice.end]; |
| 414 | return Attribute.map.get(oid_bytes) orelse { | 455 | return E.map.get(oid_bytes) orelse { |
| 415 | //std.debug.print("attr: {}\n", .{std.fmt.fmtSliceHexLower(oid_bytes)}); | 456 | //std.debug.print("tag: {}\n", .{std.fmt.fmtSliceHexLower(oid_bytes)}); |
| 416 | return error.CertificateHasUnrecognizedAttribute; | 457 | return error.CertificateHasUnrecognizedObjectId; |
| 417 | }; | 458 | }; |
| 418 | } | 459 | } |
| 419 | | 460 | |
| | 461 | pub fn checkVersion(bytes: []const u8, version: der.Element) !void { |
| | 462 | if (@bitCast(u8, version.identifier) != 0xa0 or |
| | 463 | !mem.eql(u8, bytes[version.slice.start..version.slice.end], "\x02\x01\x02")) |
| | 464 | { |
| | 465 | return error.UnsupportedCertificateVersion; |
| | 466 | } |
| | 467 | } |
| | 468 | |
| 420 | fn verifyRsa( | 469 | fn verifyRsa( |
| 421 | comptime Hash: type, | 470 | comptime Hash: type, |
| 422 | message: []const u8, | 471 | message: []const u8, |
| 423 | sig: []const u8, | 472 | sig: []const u8, |
| 424 | pub_key_algo: AlgorithmCategory, | 473 | pub_key_algo: Parsed.PubKeyAlgo, |
| 425 | pub_key: []const u8, | 474 | pub_key: []const u8, |
| 426 | ) !void { | 475 | ) !void { |
| 427 | if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch; | 476 | if (pub_key_algo != .rsaEncryption) return error.CertificateSignatureAlgorithmMismatch; |
| 428 | const pub_key_seq = try der.parseElement(pub_key, 0); | 477 | const pub_key_seq = try der.Element.parse(pub_key, 0); |
| 429 | if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType; | 478 | if (pub_key_seq.identifier.tag != .sequence) return error.CertificateFieldHasWrongDataType; |
| 430 | const modulus_elem = try der.parseElement(pub_key, pub_key_seq.slice.start); | 479 | const modulus_elem = try der.Element.parse(pub_key, pub_key_seq.slice.start); |
| 431 | if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; | 480 | if (modulus_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; |
| 432 | const exponent_elem = try der.parseElement(pub_key, modulus_elem.slice.end); | 481 | const exponent_elem = try der.Element.parse(pub_key, modulus_elem.slice.end); |
| 433 | if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; | 482 | if (exponent_elem.identifier.tag != .integer) return error.CertificateFieldHasWrongDataType; |
| 434 | // Skip over meaningless zeroes in the modulus. | 483 | // Skip over meaningless zeroes in the modulus. |
| 435 | const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end]; | 484 | const modulus_raw = pub_key[modulus_elem.slice.start..modulus_elem.slice.end]; |
| ... | @@ -504,11 +553,39 @@ fn verifyRsa( | ... | @@ -504,11 +553,39 @@ fn verifyRsa( |
| 504 | } | 553 | } |
| 505 | } | 554 | } |
| 506 | | 555 | |
| 507 | pub fn checkVersion(bytes: []const u8, version: der.Element) !void { | 556 | fn verify_ecdsa( |
| 508 | if (@bitCast(u8, version.identifier) != 0xa0 or | 557 | comptime Hash: type, |
| 509 | !mem.eql(u8, bytes[version.slice.start..version.slice.end], "\x02\x01\x02")) | 558 | message: []const u8, |
| 510 | { | 559 | encoded_sig: []const u8, |
| 511 | return error.UnsupportedCertificateVersion; | 560 | pub_key_algo: Parsed.PubKeyAlgo, |
| | 561 | sec1_pub_key: []const u8, |
| | 562 | ) !void { |
| | 563 | const sig_named_curve = switch (pub_key_algo) { |
| | 564 | .X9_62_id_ecPublicKey => |named_curve| named_curve, |
| | 565 | else => return error.CertificateSignatureAlgorithmMismatch, |
| | 566 | }; |
| | 567 | |
| | 568 | switch (sig_named_curve) { |
| | 569 | .secp384r1 => { |
| | 570 | const P = crypto.ecc.P384; |
| | 571 | const Ecdsa = crypto.sign.ecdsa.Ecdsa(P, Hash); |
| | 572 | const sig = Ecdsa.Signature.fromDer(encoded_sig) catch |err| switch (err) { |
| | 573 | error.InvalidEncoding => return error.CertificateSignatureInvalid, |
| | 574 | }; |
| | 575 | const pub_key = Ecdsa.PublicKey.fromSec1(sec1_pub_key) catch |err| switch (err) { |
| | 576 | error.InvalidEncoding => return error.CertificateSignatureInvalid, |
| | 577 | error.NonCanonical => return error.CertificateSignatureInvalid, |
| | 578 | error.NotSquare => return error.CertificateSignatureInvalid, |
| | 579 | }; |
| | 580 | sig.verify(message, pub_key) catch |err| switch (err) { |
| | 581 | error.IdentityElement => return error.CertificateSignatureInvalid, |
| | 582 | error.NonCanonical => return error.CertificateSignatureInvalid, |
| | 583 | error.SignatureVerificationFailed => return error.CertificateSignatureInvalid, |
| | 584 | }; |
| | 585 | }, |
| | 586 | .X9_62_prime256v1 => { |
| | 587 | return error.CertificateSignatureNamedCurveUnsupported; |
| | 588 | }, |
| 512 | } | 589 | } |
| 513 | } | 590 | } |
| 514 | | 591 | |
| ... | @@ -559,45 +636,45 @@ pub const der = struct { | ... | @@ -559,45 +636,45 @@ pub const der = struct { |
| 559 | | 636 | |
| 560 | pub const empty: Slice = .{ .start = 0, .end = 0 }; | 637 | pub const empty: Slice = .{ .start = 0, .end = 0 }; |
| 561 | }; | 638 | }; |
| 562 | }; | | |
| 563 | | 639 | |
| 564 | pub const ParseElementError = error{CertificateFieldHasInvalidLength}; | 640 | pub const ParseError = error{CertificateFieldHasInvalidLength}; |
| | 641 | |
| | 642 | pub fn parse(bytes: []const u8, index: u32) ParseError!Element { |
| | 643 | var i = index; |
| | 644 | const identifier = @bitCast(Identifier, bytes[i]); |
| | 645 | i += 1; |
| | 646 | const size_byte = bytes[i]; |
| | 647 | i += 1; |
| | 648 | if ((size_byte >> 7) == 0) { |
| | 649 | return .{ |
| | 650 | .identifier = identifier, |
| | 651 | .slice = .{ |
| | 652 | .start = i, |
| | 653 | .end = i + size_byte, |
| | 654 | }, |
| | 655 | }; |
| | 656 | } |
| | 657 | |
| | 658 | const len_size = @truncate(u7, size_byte); |
| | 659 | if (len_size > @sizeOf(u32)) { |
| | 660 | return error.CertificateFieldHasInvalidLength; |
| | 661 | } |
| | 662 | |
| | 663 | const end_i = i + len_size; |
| | 664 | var long_form_size: u32 = 0; |
| | 665 | while (i < end_i) : (i += 1) { |
| | 666 | long_form_size = (long_form_size << 8) | bytes[i]; |
| | 667 | } |
| 565 | | 668 | |
| 566 | pub fn parseElement(bytes: []const u8, index: u32) ParseElementError!Element { | | |
| 567 | var i = index; | | |
| 568 | const identifier = @bitCast(Identifier, bytes[i]); | | |
| 569 | i += 1; | | |
| 570 | const size_byte = bytes[i]; | | |
| 571 | i += 1; | | |
| 572 | if ((size_byte >> 7) == 0) { | | |
| 573 | return .{ | 669 | return .{ |
| 574 | .identifier = identifier, | 670 | .identifier = identifier, |
| 575 | .slice = .{ | 671 | .slice = .{ |
| 576 | .start = i, | 672 | .start = i, |
| 577 | .end = i + size_byte, | 673 | .end = i + long_form_size, |
| 578 | }, | 674 | }, |
| 579 | }; | 675 | }; |
| 580 | } | 676 | } |
| 581 | | 677 | }; |
| 582 | const len_size = @truncate(u7, size_byte); | | |
| 583 | if (len_size > @sizeOf(u32)) { | | |
| 584 | return error.CertificateFieldHasInvalidLength; | | |
| 585 | } | | |
| 586 | | | |
| 587 | const end_i = i + len_size; | | |
| 588 | var long_form_size: u32 = 0; | | |
| 589 | while (i < end_i) : (i += 1) { | | |
| 590 | long_form_size = (long_form_size << 8) | bytes[i]; | | |
| 591 | } | | |
| 592 | | | |
| 593 | return .{ | | |
| 594 | .identifier = identifier, | | |
| 595 | .slice = .{ | | |
| 596 | .start = i, | | |
| 597 | .end = i + long_form_size, | | |
| 598 | }, | | |
| 599 | }; | | |
| 600 | } | | |
| 601 | }; | 678 | }; |
| 602 | | 679 | |
| 603 | test { | 680 | test { |