github.com/zmap/zcrypto@v0.0.0-20240512203510-0fef58d9a9db/x509/crl_parser.go (about) 1 package x509 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "math/big" 8 "time" 9 "unicode/utf16" 10 "unicode/utf8" 11 12 "github.com/zmap/zcrypto/cryptobyte" 13 cryptobyte_asn1 "github.com/zmap/zcrypto/cryptobyte/asn1" 14 "github.com/zmap/zcrypto/encoding/asn1" 15 "github.com/zmap/zcrypto/x509/pkix" 16 ) 17 18 const x509v2Version = 1 19 20 // RevokedCertificate represents an entry in the revokedCertificates sequence of 21 // a CRL. 22 // STARTBLOCK: This type does not exist in upstream. 23 type RevokedCertificate struct { 24 // Raw contains the raw bytes of the revokedCertificates entry. It is set when 25 // parsing a CRL; it is ignored when generating a CRL. 26 Raw []byte 27 28 // SerialNumber represents the serial number of a revoked certificate. It is 29 // both used when creating a CRL and populated when parsing a CRL. It MUST NOT 30 // be nil. 31 SerialNumber *big.Int 32 // RevocationTime represents the time at which the certificate was revoked. It 33 // is both used when creating a CRL and populated when parsing a CRL. It MUST 34 // NOT be nil. 35 RevocationTime time.Time 36 // ReasonCode represents the reason for revocation, using the integer enum 37 // values specified in RFC 5280 Section 5.3.1. When creating a CRL, a value of 38 // nil or zero will result in the reasonCode extension being omitted. When 39 // parsing a CRL, a value of nil represents a no reasonCode extension, while a 40 // value of 0 represents a reasonCode extension containing enum value 0 (this 41 // SHOULD NOT happen, but can and does). 42 ReasonCode *int 43 44 // Extensions contains raw X.509 extensions. When creating a CRL, the 45 // Extensions field is ignored, see ExtraExtensions. 46 Extensions []pkix.Extension 47 // ExtraExtensions contains any additional extensions to add directly to the 48 // revokedCertificate entry. It is up to the caller to ensure that this field 49 // does not contain any extensions which duplicate extensions created by this 50 // package (currently, the reasonCode extension). The ExtraExtensions field is 51 // not populated when parsing a CRL, see Extensions. 52 ExtraExtensions []pkix.Extension 53 } 54 55 // ENDBLOCK 56 57 // ParseRevocationList parses a X509 v2 Certificate Revocation List from the given 58 // ASN.1 DER data. 59 func ParseRevocationList(der []byte) (*RevocationList, error) { 60 rl := &RevocationList{} 61 62 input := cryptobyte.String(der) 63 // we read the SEQUENCE including length and tag bytes so that 64 // we can populate RevocationList.Raw, before unwrapping the 65 // SEQUENCE so it can be operated on 66 if !input.ReadASN1Element(&input, cryptobyte_asn1.SEQUENCE) { 67 return nil, errors.New("x509: malformed crl") 68 } 69 rl.Raw = input 70 if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) { 71 return nil, errors.New("x509: malformed crl") 72 } 73 74 var tbs cryptobyte.String 75 // do the same trick again as above to extract the raw 76 // bytes for Certificate.RawTBSCertificate 77 if !input.ReadASN1Element(&tbs, cryptobyte_asn1.SEQUENCE) { 78 return nil, errors.New("x509: malformed tbs crl") 79 } 80 rl.RawTBSRevocationList = tbs 81 if !tbs.ReadASN1(&tbs, cryptobyte_asn1.SEQUENCE) { 82 return nil, errors.New("x509: malformed tbs crl") 83 } 84 85 var version int 86 if !tbs.PeekASN1Tag(cryptobyte_asn1.INTEGER) { 87 return nil, errors.New("x509: unsupported crl version") 88 } 89 if !tbs.ReadASN1Integer(&version) { 90 return nil, errors.New("x509: malformed crl") 91 } 92 if version != x509v2Version { 93 return nil, fmt.Errorf("x509: unsupported crl version: %d", version) 94 } 95 96 var sigAISeq cryptobyte.String 97 if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) { 98 return nil, errors.New("x509: malformed signature algorithm identifier") 99 } 100 // Before parsing the inner algorithm identifier, extract 101 // the outer algorithm identifier and make sure that they 102 // match. 103 var outerSigAISeq cryptobyte.String 104 if !input.ReadASN1(&outerSigAISeq, cryptobyte_asn1.SEQUENCE) { 105 return nil, errors.New("x509: malformed algorithm identifier") 106 } 107 if !bytes.Equal(outerSigAISeq, sigAISeq) { 108 return nil, errors.New("x509: inner and outer signature algorithm identifiers don't match") 109 } 110 sigAI, err := parseAI(sigAISeq) 111 if err != nil { 112 return nil, err 113 } 114 rl.SignatureAlgorithm = getSignatureAlgorithmFromAI(sigAI) 115 116 var signature asn1.BitString 117 if !input.ReadASN1BitString(&signature) { 118 return nil, errors.New("x509: malformed signature") 119 } 120 rl.Signature = signature.RightAlign() 121 122 var issuerSeq cryptobyte.String 123 if !tbs.ReadASN1Element(&issuerSeq, cryptobyte_asn1.SEQUENCE) { 124 return nil, errors.New("x509: malformed issuer") 125 } 126 rl.RawIssuer = issuerSeq 127 issuerRDNs, err := parseName(issuerSeq) 128 if err != nil { 129 return nil, err 130 } 131 rl.Issuer.FillFromRDNSequence(issuerRDNs) 132 133 rl.ThisUpdate, err = parseTime(&tbs) 134 if err != nil { 135 return nil, err 136 } 137 if tbs.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime) || tbs.PeekASN1Tag(cryptobyte_asn1.UTCTime) { 138 rl.NextUpdate, err = parseTime(&tbs) 139 if err != nil { 140 return nil, err 141 } 142 } 143 144 if tbs.PeekASN1Tag(cryptobyte_asn1.SEQUENCE) { 145 // NOTE: The block does not exist in upstream. 146 rcs := make([]RevokedCertificate, 0) 147 // ENDBLOCK 148 var revokedSeq cryptobyte.String 149 if !tbs.ReadASN1(&revokedSeq, cryptobyte_asn1.SEQUENCE) { 150 return nil, errors.New("x509: malformed crl") 151 } 152 for !revokedSeq.Empty() { 153 var certSeq cryptobyte.String 154 // NOTE: The block is different from upstream. Upstream: ReadASN1 155 if !revokedSeq.ReadASN1Element(&certSeq, cryptobyte_asn1.SEQUENCE) { 156 // ENDBLOCK 157 return nil, errors.New("x509: malformed crl") 158 } 159 rc := RevokedCertificate{Raw: certSeq} 160 if !certSeq.ReadASN1(&certSeq, cryptobyte_asn1.SEQUENCE) { 161 return nil, errors.New("x509: malformed crl") 162 } 163 rc.SerialNumber = new(big.Int) 164 if !certSeq.ReadASN1Integer(rc.SerialNumber) { 165 return nil, errors.New("x509: malformed serial number") 166 } 167 rc.RevocationTime, err = parseTime(&certSeq) 168 if err != nil { 169 return nil, err 170 } 171 var extensions cryptobyte.String 172 var present bool 173 if !certSeq.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.SEQUENCE) { 174 return nil, errors.New("x509: malformed extensions") 175 } 176 if present { 177 for !extensions.Empty() { 178 var extension cryptobyte.String 179 if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) { 180 return nil, errors.New("x509: malformed extension") 181 } 182 ext, err := parseExtension(extension) 183 if err != nil { 184 return nil, err 185 } 186 // STARTBLOCK: This block does not exist in upstream. 187 if ext.Id.Equal(oidExtensionReasonCode) { 188 val := cryptobyte.String(ext.Value) 189 rc.ReasonCode = new(int) 190 if !val.ReadASN1Enum(rc.ReasonCode) { 191 return nil, fmt.Errorf("x509: malformed reasonCode extension") 192 } 193 } 194 // ENDBLOCK 195 rc.Extensions = append(rc.Extensions, ext) 196 } 197 } 198 // STARTBLOCK: The block does not exist in upstream. 199 rcs = append(rcs, rc) 200 // ENDBLOCK 201 } 202 rl.RevokedCertificates = rcs 203 } 204 205 var extensions cryptobyte.String 206 var present bool 207 if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) { 208 return nil, errors.New("x509: malformed extensions") 209 } 210 if present { 211 if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) { 212 return nil, errors.New("x509: malformed extensions") 213 } 214 for !extensions.Empty() { 215 var extension cryptobyte.String 216 if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) { 217 return nil, errors.New("x509: malformed extension") 218 } 219 ext, err := parseExtension(extension) 220 if err != nil { 221 return nil, err 222 } 223 if ext.Id.Equal(oidExtensionAuthorityKeyId) { 224 rl.AuthorityKeyId = ext.Value 225 } else if ext.Id.Equal(oidExtensionCRLNumber) { 226 value := cryptobyte.String(ext.Value) 227 rl.Number = new(big.Int) 228 if !value.ReadASN1Integer(rl.Number) { 229 return nil, errors.New("x509: malformed crl number") 230 } 231 } 232 rl.Extensions = append(rl.Extensions, ext) 233 } 234 } 235 236 return rl, nil 237 } 238 239 // isPrintable reports whether the given b is in the ASN.1 PrintableString set. 240 // This is a simplified version of encoding/asn1.isPrintable. 241 func isPrintable(b byte) bool { 242 return 'a' <= b && b <= 'z' || 243 'A' <= b && b <= 'Z' || 244 '0' <= b && b <= '9' || 245 '\'' <= b && b <= ')' || 246 '+' <= b && b <= '/' || 247 b == ' ' || 248 b == ':' || 249 b == '=' || 250 b == '?' || 251 // This is technically not allowed in a PrintableString. 252 // However, x509 certificates with wildcard strings don't 253 // always use the correct string type so we permit it. 254 b == '*' || 255 // This is not technically allowed either. However, not 256 // only is it relatively common, but there are also a 257 // handful of CA certificates that contain it. At least 258 // one of which will not expire until 2027. 259 b == '&' 260 } 261 262 // parseASN1String parses the ASN.1 string types T61String, PrintableString, 263 // UTF8String, BMPString, IA5String, and NumericString. This is mostly copied 264 // from the respective encoding/asn1.parse... methods, rather than just 265 // increasing the API surface of that package. 266 func parseASN1String(tag cryptobyte_asn1.Tag, value []byte) (string, error) { 267 switch tag { 268 case cryptobyte_asn1.T61String: 269 return string(value), nil 270 case cryptobyte_asn1.PrintableString: 271 for _, b := range value { 272 if !isPrintable(b) { 273 return "", errors.New("invalid PrintableString") 274 } 275 } 276 return string(value), nil 277 case cryptobyte_asn1.UTF8String: 278 if !utf8.Valid(value) { 279 return "", errors.New("invalid UTF-8 string") 280 } 281 return string(value), nil 282 case cryptobyte_asn1.Tag(asn1.TagBMPString): 283 if len(value)%2 != 0 { 284 return "", errors.New("invalid BMPString") 285 } 286 287 // Strip terminator if present. 288 if l := len(value); l >= 2 && value[l-1] == 0 && value[l-2] == 0 { 289 value = value[:l-2] 290 } 291 292 s := make([]uint16, 0, len(value)/2) 293 for len(value) > 0 { 294 s = append(s, uint16(value[0])<<8+uint16(value[1])) 295 value = value[2:] 296 } 297 298 return string(utf16.Decode(s)), nil 299 case cryptobyte_asn1.IA5String: 300 s := string(value) 301 if isIA5String(s) != nil { 302 return "", errors.New("invalid IA5String") 303 } 304 return s, nil 305 case cryptobyte_asn1.Tag(asn1.TagNumericString): 306 for _, b := range value { 307 if !('0' <= b && b <= '9' || b == ' ') { 308 return "", errors.New("invalid NumericString") 309 } 310 } 311 return string(value), nil 312 } 313 return "", fmt.Errorf("unsupported string type: %v", tag) 314 } 315 316 // parseName parses a DER encoded Name as defined in RFC 5280. We may 317 // want to export this function in the future for use in crypto/tls. 318 func parseName(raw cryptobyte.String) (*pkix.RDNSequence, error) { 319 if !raw.ReadASN1(&raw, cryptobyte_asn1.SEQUENCE) { 320 return nil, errors.New("x509: invalid RDNSequence") 321 } 322 323 var rdnSeq pkix.RDNSequence 324 for !raw.Empty() { 325 var rdnSet pkix.RelativeDistinguishedNameSET 326 var set cryptobyte.String 327 if !raw.ReadASN1(&set, cryptobyte_asn1.SET) { 328 return nil, errors.New("x509: invalid RDNSequence") 329 } 330 for !set.Empty() { 331 var atav cryptobyte.String 332 if !set.ReadASN1(&atav, cryptobyte_asn1.SEQUENCE) { 333 return nil, errors.New("x509: invalid RDNSequence: invalid attribute") 334 } 335 var attr pkix.AttributeTypeAndValue 336 if !atav.ReadASN1ObjectIdentifier(&attr.Type) { 337 return nil, errors.New("x509: invalid RDNSequence: invalid attribute type") 338 } 339 var rawValue cryptobyte.String 340 var valueTag cryptobyte_asn1.Tag 341 if !atav.ReadAnyASN1(&rawValue, &valueTag) { 342 return nil, errors.New("x509: invalid RDNSequence: invalid attribute value") 343 } 344 var err error 345 attr.Value, err = parseASN1String(valueTag, rawValue) 346 if err != nil { 347 return nil, fmt.Errorf("x509: invalid RDNSequence: invalid attribute value: %s", err) 348 } 349 rdnSet = append(rdnSet, attr) 350 } 351 352 rdnSeq = append(rdnSeq, rdnSet) 353 } 354 355 return &rdnSeq, nil 356 } 357 358 func parseAI(der cryptobyte.String) (pkix.AlgorithmIdentifier, error) { 359 ai := pkix.AlgorithmIdentifier{} 360 if !der.ReadASN1ObjectIdentifier(&ai.Algorithm) { 361 return ai, errors.New("x509: malformed OID") 362 } 363 if der.Empty() { 364 return ai, nil 365 } 366 var params cryptobyte.String 367 var tag cryptobyte_asn1.Tag 368 if !der.ReadAnyASN1Element(¶ms, &tag) { 369 return ai, errors.New("x509: malformed parameters") 370 } 371 ai.Parameters.Tag = int(tag) 372 ai.Parameters.FullBytes = params 373 return ai, nil 374 } 375 376 func parseTime(der *cryptobyte.String) (time.Time, error) { 377 var t time.Time 378 switch { 379 case der.PeekASN1Tag(cryptobyte_asn1.UTCTime): 380 if !der.ReadASN1UTCTime(&t) { 381 return t, errors.New("x509: malformed UTCTime") 382 } 383 case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime): 384 if !der.ReadASN1GeneralizedTime(&t) { 385 return t, errors.New("x509: malformed GeneralizedTime") 386 } 387 default: 388 return t, errors.New("x509: unsupported time format") 389 } 390 return t, nil 391 } 392 393 func parseExtension(der cryptobyte.String) (pkix.Extension, error) { 394 var ext pkix.Extension 395 if !der.ReadASN1ObjectIdentifier(&ext.Id) { 396 return ext, errors.New("x509: malformed extension OID field") 397 } 398 if der.PeekASN1Tag(cryptobyte_asn1.BOOLEAN) { 399 if !der.ReadASN1Boolean(&ext.Critical) { 400 return ext, errors.New("x509: malformed extension critical field") 401 } 402 } 403 var val cryptobyte.String 404 if !der.ReadASN1(&val, cryptobyte_asn1.OCTET_STRING) { 405 return ext, errors.New("x509: malformed extension value field") 406 } 407 ext.Value = val 408 return ext, nil 409 }