diff --git a/content/en/docs/a-warm-welcome-to-asn1-and-der.md b/content/en/docs/a-warm-welcome-to-asn1-and-der.md index 9b0eb66d5..3f42bd7dc 100644 --- a/content/en/docs/a-warm-welcome-to-asn1-and-der.md +++ b/content/en/docs/a-warm-welcome-to-asn1-and-der.md @@ -1,7 +1,7 @@ --- title: A Warm Welcome to ASN.1 and DER slug: a-warm-welcome-to-asn1-and-der -lastmod: 2021-03-21 +lastmod: 2026-09-04 --- This document provides a gentle introduction to the data structures and formats that define the certificates used in HTTPS. It should be @@ -118,8 +118,7 @@ representing things like an RSA modulus, which is much bigger than an int64 (like 22048 big). Technically there is a maximum integer in DER but it's extraordinarily large: The length of any DER field can be expressed as a series of up to 126 bytes. So the biggest INTEGER you can -represent in DER is 256(2\*\*1008)-1. For a truly unbounded INTEGER you'd -have to encode in BER, which allows indefinitely-long fields. +represent in DER is 256(2\*\*1008)-1. Strings ------- @@ -187,10 +186,10 @@ at 7am in New York City (UTC-5) and at 12pm in UTC. Since UTCTime is ambiguous as to whether it's the 1900's or 2000's, [RFC 5280 clarifies](https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1) that -it represents dates from 1950 to 2050. RFC 5280 also requires that the +it represents dates from 1950 through 2049. RFC 5280 also requires that the "Z" timezone must be used and seconds must be included. -GeneralizedTime supports dates after 2050 through the simple expedient +GeneralizedTime supports dates from 2050 onward through the simple expedient of representing the year with four digits. It also allows fractional seconds (weirdly, with either a comma or a full stop as the decimal separator). RFC 5280 forbids fractional seconds and requires the "Z." @@ -480,9 +479,9 @@ mean the same thing across all ASN.1 modules. These tags all happen to be under 31 (0x1F), and that's for a good reason: Bits 8, 7, and 6 (the high bits of the tag byte) are used to encode extra -information, so any universal tag numbers higher than 31 would need to +information, so any universal tag numbers of 31 or higher would need to use the "high tag number" form, which takes extra bytes. There are a -small handful of universal tags higher than 31, but they're quite rare. +small handful of universal tags of 31 or higher, but they're quite rare. The two tags marked with a `*` are always encoded as 0x30 or 0x31, because bit 6 is used to indicate whether a field is Constructed vs @@ -543,8 +542,8 @@ APPLICATION: ```asn1 Point ::= SEQUENCE { - x [APPLICATION 0] INTEGER OPTIONAL, - y [APPLICATION 1] INTEGER OPTIONAL + x [APPLICATION 0] IMPLICIT INTEGER OPTIONAL, + y [APPLICATION 1] IMPLICIT INTEGER OPTIONAL } ``` @@ -554,11 +553,15 @@ itself: ```asn1 Point ::= SEQUENCE { - x [0] INTEGER OPTIONAL, - y [1] INTEGER OPTIONAL + x [0] IMPLICIT INTEGER OPTIONAL, + y [1] IMPLICIT INTEGER OPTIONAL } ``` +(The IMPLICIT keyword is explained under [EXPLICIT vs IMPLICIT](#explicit-vs-implicit) +below. It matters here: without it the default is EXPLICIT, which produces a +different, longer encoding.) + So now, to encode a Point with just an x coordinate of 9, instead of encoding x as a UNIVERSAL INTEGER, you'd set bits 8 and 7 of the encoded tag to (1, 0) to indicate the context specific class, and set the low @@ -804,7 +807,7 @@ However, that could also be encoded as: 11111111 10000000 (== decimal -128, but an invalid encoding) Expanding that out, it's -215 + 214 + 213 + 212 + 211 + 210 + 29 + 28 + 27 == -27 == -128. Note that the 1 in "10000000" was a sign bit in the -single-byte encoding, but means 27 in the two-byte encoding. +single-byte encoding, but means 27 in the two-byte encoding. This is a generic transform: For any negative number encoded as BER (or DER) you could prefix it with 11111111 and get the same number. This is called @@ -1001,7 +1004,8 @@ DEFAULT fields MUST be omitted from DER encoding if they have the default value. In BER, a SET may be encoded in any order. In DER, a SET must be encoded -in ascending order by the serialized value of each element. +in ascending order by the tag of each element (first by class, then by tag +number). SET OF encoding --------------- @@ -1139,7 +1143,7 @@ field](#length). Certificates are almost always more than 127 bytes, so the leng field has to use the long form of the length. That means the first byte will be 0x80 + N, where N is the number of length bytes to follow. N is almost always 2, since that's how many bytes it takes to encode lengths -from 128 to 65535, and almost all certificates have lengths in that +from 256 to 65535, and almost all certificates have lengths in that range. So now we know that the first two bytes of the DER encoding of a