Skip to content

Commit 2de2f6d

Browse files
rolandshoemakergopherbot
authored andcommitted
crypto/x509: add new CRL parser, deprecate old one
Adds a new, cryptobyte based, CRL parser, which returns a x509.RevocaitonList, rather than a pkix.CertificateList. This allows us to return much more detailed information, as well as leaving open the option of adding further information since RevocationList is not a direct ASN.1 representation like pkix.CertificateList. Additionally a new method is added to RevocationList, CheckSignatureFrom, which is analogous to the method with the same name on Certificate, which properly checks that the signature is from an issuing certiifcate. This change also deprecates a number of older CRL related functions and types, which have been replaced with the new functionality introduced in this change: * crypto/x509.ParseCRL * crypto/x509.ParseDERCRL * crypto/x509.CheckCRLSignature * crypto/x509/pkix.CertificateList * crypto/x509/pkix.TBSCertificateList Fixes golang#50674 Change-Id: I27dc219e39bef09a396e666b4fccaa32578fd913 Reviewed-on: https://go-review.googlesource.com/c/go/+/390834 Reviewed-by: Damien Neil <dneil@google.com> Trust: Roland Shoemaker <roland@golang.org> Run-TryBot: Roland Shoemaker <roland@golang.org> Auto-Submit: Roland Shoemaker <roland@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent dbb52cc commit 2de2f6d

File tree

5 files changed

+366
-55
lines changed

5 files changed

+366
-55
lines changed

api/next/50674.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pkg crypto/x509, func ParseRevocationList([]uint8) (*RevocationList, error) #50674
2+
pkg crypto/x509, method (*RevocationList) CheckSignatureFrom(*Certificate) error #50674
3+
pkg crypto/x509, type RevocationList struct, AuthorityKeyId []uint8 #50674
4+
pkg crypto/x509, type RevocationList struct, Extensions []pkix.Extension #50674
5+
pkg crypto/x509, type RevocationList struct, Issuer pkix.Name #50674
6+
pkg crypto/x509, type RevocationList struct, Raw []uint8 #50674
7+
pkg crypto/x509, type RevocationList struct, RawIssuer []uint8 #50674
8+
pkg crypto/x509, type RevocationList struct, RawTBSRevocationList []uint8 #50674
9+
pkg crypto/x509, type RevocationList struct, Signature []uint8 #50674

src/crypto/x509/parser.go

Lines changed: 178 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -164,53 +164,29 @@ func parseAI(der cryptobyte.String) (pkix.AlgorithmIdentifier, error) {
164164
return ai, nil
165165
}
166166

167-
func parseValidity(der cryptobyte.String) (time.Time, time.Time, error) {
168-
extract := func() (time.Time, error) {
169-
var t time.Time
170-
switch {
171-
case der.PeekASN1Tag(cryptobyte_asn1.UTCTime):
172-
// TODO(rolandshoemaker): once #45411 is fixed, the following code
173-
// should be replaced with a call to der.ReadASN1UTCTime.
174-
var utc cryptobyte.String
175-
if !der.ReadASN1(&utc, cryptobyte_asn1.UTCTime) {
176-
return t, errors.New("x509: malformed UTCTime")
177-
}
178-
s := string(utc)
179-
180-
formatStr := "0601021504Z0700"
181-
var err error
182-
t, err = time.Parse(formatStr, s)
183-
if err != nil {
184-
formatStr = "060102150405Z0700"
185-
t, err = time.Parse(formatStr, s)
186-
}
187-
if err != nil {
188-
return t, err
189-
}
190-
191-
if serialized := t.Format(formatStr); serialized != s {
192-
return t, errors.New("x509: malformed UTCTime")
193-
}
194-
195-
if t.Year() >= 2050 {
196-
// UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
197-
t = t.AddDate(-100, 0, 0)
198-
}
199-
case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime):
200-
if !der.ReadASN1GeneralizedTime(&t) {
201-
return t, errors.New("x509: malformed GeneralizedTime")
202-
}
203-
default:
204-
return t, errors.New("x509: unsupported time format")
167+
func parseTime(der *cryptobyte.String) (time.Time, error) {
168+
var t time.Time
169+
switch {
170+
case der.PeekASN1Tag(cryptobyte_asn1.UTCTime):
171+
if !der.ReadASN1UTCTime(&t) {
172+
return t, errors.New("x509: malformed UTCTime")
205173
}
206-
return t, nil
174+
case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime):
175+
if !der.ReadASN1GeneralizedTime(&t) {
176+
return t, errors.New("x509: malformed GeneralizedTime")
177+
}
178+
default:
179+
return t, errors.New("x509: unsupported time format")
207180
}
181+
return t, nil
182+
}
208183

209-
notBefore, err := extract()
184+
func parseValidity(der cryptobyte.String) (time.Time, time.Time, error) {
185+
notBefore, err := parseTime(&der)
210186
if err != nil {
211187
return time.Time{}, time.Time{}, err
212188
}
213-
notAfter, err := extract()
189+
notAfter, err := parseTime(&der)
214190
if err != nil {
215191
return time.Time{}, time.Time{}, err
216192
}
@@ -1011,3 +987,164 @@ func ParseCertificates(der []byte) ([]*Certificate, error) {
1011987
}
1012988
return certs, nil
1013989
}
990+
991+
// The X.509 standards confusingly 1-indexed the version names, but 0-indexed
992+
// the actual encoded version, so the version for X.509v2 is 1.
993+
const x509v2Version = 1
994+
995+
// ParseRevocationList parses a X509 v2 Certificate Revocation List from the given
996+
// ASN.1 DER data.
997+
func ParseRevocationList(der []byte) (*RevocationList, error) {
998+
rl := &RevocationList{}
999+
1000+
input := cryptobyte.String(der)
1001+
// we read the SEQUENCE including length and tag bytes so that
1002+
// we can populate RevocationList.Raw, before unwrapping the
1003+
// SEQUENCE so it can be operated on
1004+
if !input.ReadASN1Element(&input, cryptobyte_asn1.SEQUENCE) {
1005+
return nil, errors.New("x509: malformed certificate")
1006+
}
1007+
rl.Raw = input
1008+
if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
1009+
return nil, errors.New("x509: malformed certificate")
1010+
}
1011+
1012+
var tbs cryptobyte.String
1013+
// do the same trick again as above to extract the raw
1014+
// bytes for Certificate.RawTBSCertificate
1015+
if !input.ReadASN1Element(&tbs, cryptobyte_asn1.SEQUENCE) {
1016+
return nil, errors.New("x509: malformed tbs certificate")
1017+
}
1018+
rl.RawTBSRevocationList = tbs
1019+
if !tbs.ReadASN1(&tbs, cryptobyte_asn1.SEQUENCE) {
1020+
return nil, errors.New("x509: malformed tbs certificate")
1021+
}
1022+
1023+
var version int
1024+
if !tbs.PeekASN1Tag(cryptobyte_asn1.INTEGER) {
1025+
return nil, errors.New("x509: unsupported crl version")
1026+
}
1027+
if !tbs.ReadASN1Integer(&version) {
1028+
return nil, errors.New("x509: malformed crl")
1029+
}
1030+
if version != x509v2Version {
1031+
return nil, fmt.Errorf("x509: unsupported crl version: %d", version)
1032+
}
1033+
1034+
var sigAISeq cryptobyte.String
1035+
if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
1036+
return nil, errors.New("x509: malformed signature algorithm identifier")
1037+
}
1038+
// Before parsing the inner algorithm identifier, extract
1039+
// the outer algorithm identifier and make sure that they
1040+
// match.
1041+
var outerSigAISeq cryptobyte.String
1042+
if !input.ReadASN1(&outerSigAISeq, cryptobyte_asn1.SEQUENCE) {
1043+
return nil, errors.New("x509: malformed algorithm identifier")
1044+
}
1045+
if !bytes.Equal(outerSigAISeq, sigAISeq) {
1046+
return nil, errors.New("x509: inner and outer signature algorithm identifiers don't match")
1047+
}
1048+
sigAI, err := parseAI(sigAISeq)
1049+
if err != nil {
1050+
return nil, err
1051+
}
1052+
rl.SignatureAlgorithm = getSignatureAlgorithmFromAI(sigAI)
1053+
1054+
var signature asn1.BitString
1055+
if !input.ReadASN1BitString(&signature) {
1056+
return nil, errors.New("x509: malformed signature")
1057+
}
1058+
rl.Signature = signature.RightAlign()
1059+
1060+
var issuerSeq cryptobyte.String
1061+
if !tbs.ReadASN1Element(&issuerSeq, cryptobyte_asn1.SEQUENCE) {
1062+
return nil, errors.New("x509: malformed issuer")
1063+
}
1064+
rl.RawIssuer = issuerSeq
1065+
issuerRDNs, err := parseName(issuerSeq)
1066+
if err != nil {
1067+
return nil, err
1068+
}
1069+
rl.Issuer.FillFromRDNSequence(issuerRDNs)
1070+
1071+
rl.ThisUpdate, err = parseTime(&tbs)
1072+
if err != nil {
1073+
return nil, err
1074+
}
1075+
if tbs.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime) || tbs.PeekASN1Tag(cryptobyte_asn1.UTCTime) {
1076+
rl.NextUpdate, err = parseTime(&tbs)
1077+
if err != nil {
1078+
return nil, err
1079+
}
1080+
}
1081+
1082+
if tbs.PeekASN1Tag(cryptobyte_asn1.SEQUENCE) {
1083+
var revokedSeq cryptobyte.String
1084+
if !tbs.ReadASN1(&revokedSeq, cryptobyte_asn1.SEQUENCE) {
1085+
return nil, errors.New("x509: malformed crl")
1086+
}
1087+
for !revokedSeq.Empty() {
1088+
var certSeq cryptobyte.String
1089+
if !revokedSeq.ReadASN1(&certSeq, cryptobyte_asn1.SEQUENCE) {
1090+
return nil, errors.New("x509: malformed crl")
1091+
}
1092+
rc := pkix.RevokedCertificate{}
1093+
rc.SerialNumber = new(big.Int)
1094+
if !certSeq.ReadASN1Integer(rc.SerialNumber) {
1095+
return nil, errors.New("x509: malformed serial number")
1096+
}
1097+
rc.RevocationTime, err = parseTime(&certSeq)
1098+
if err != nil {
1099+
return nil, err
1100+
}
1101+
var extensions cryptobyte.String
1102+
var present bool
1103+
if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.SEQUENCE) {
1104+
return nil, errors.New("x509: malformed extensions")
1105+
}
1106+
if present {
1107+
if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) {
1108+
return nil, errors.New("x509: malformed extensions")
1109+
}
1110+
for !extensions.Empty() {
1111+
var extension cryptobyte.String
1112+
if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) {
1113+
return nil, errors.New("x509: malformed extension")
1114+
}
1115+
ext, err := parseExtension(extension)
1116+
if err != nil {
1117+
return nil, err
1118+
}
1119+
rc.Extensions = append(rc.Extensions, ext)
1120+
}
1121+
}
1122+
1123+
rl.RevokedCertificates = append(rl.RevokedCertificates, rc)
1124+
}
1125+
}
1126+
1127+
var extensions cryptobyte.String
1128+
var present bool
1129+
if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) {
1130+
return nil, errors.New("x509: malformed extensions")
1131+
}
1132+
if present {
1133+
if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) {
1134+
return nil, errors.New("x509: malformed extensions")
1135+
}
1136+
for !extensions.Empty() {
1137+
var extension cryptobyte.String
1138+
if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) {
1139+
return nil, errors.New("x509: malformed extension")
1140+
}
1141+
ext, err := parseExtension(extension)
1142+
if err != nil {
1143+
return nil, err
1144+
}
1145+
rl.Extensions = append(rl.Extensions, ext)
1146+
}
1147+
}
1148+
1149+
return rl, nil
1150+
}

src/crypto/x509/pkix/pkix.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ func (certList *CertificateList) HasExpired(now time.Time) bool {
296296

297297
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
298298
// 5280, section 5.1.
299+
//
300+
// Deprecated: x509.RevocationList should be used instead.
299301
type TBSCertificateList struct {
300302
Raw asn1.RawContent
301303
Version int `asn1:"optional,default:0"`
@@ -309,6 +311,8 @@ type TBSCertificateList struct {
309311

310312
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
311313
// 5280, section 5.1.
314+
//
315+
// Deprecated: x509.RevocationList should be used instead.
312316
type RevokedCertificate struct {
313317
SerialNumber *big.Int
314318
RevocationTime time.Time

src/crypto/x509/x509.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,8 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
880880
}
881881

882882
// CheckCRLSignature checks that the signature in crl is from c.
883+
//
884+
// Deprecated: Use RevocationList.CheckSignatureFrom instead.
883885
func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) error {
884886
algo := getSignatureAlgorithmFromAI(crl.SignatureAlgorithm)
885887
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
@@ -1607,6 +1609,8 @@ var pemType = "X509 CRL"
16071609
// encoded CRLs will appear where they should be DER encoded, so this function
16081610
// will transparently handle PEM encoding as long as there isn't any leading
16091611
// garbage.
1612+
//
1613+
// Deprecated: Use ParseRevocationList instead.
16101614
func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error) {
16111615
if bytes.HasPrefix(crlBytes, pemCRLPrefix) {
16121616
block, _ := pem.Decode(crlBytes)
@@ -1618,6 +1622,8 @@ func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error) {
16181622
}
16191623

16201624
// ParseDERCRL parses a DER encoded CRL from the given bytes.
1625+
//
1626+
// Deprecated: Use ParseRevocationList instead.
16211627
func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) {
16221628
certList := new(pkix.CertificateList)
16231629
if rest, err := asn1.Unmarshal(derBytes, certList); err != nil {
@@ -1631,7 +1637,7 @@ func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) {
16311637
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
16321638
// contains the given list of revoked certificates.
16331639
//
1634-
// Note: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
1640+
// Deprecated: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
16351641
// To generate a standards compliant CRL, use CreateRevocationList instead.
16361642
func (c *Certificate) CreateCRL(rand io.Reader, priv any, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
16371643
key, ok := priv.(crypto.Signer)
@@ -2073,6 +2079,14 @@ func (c *CertificateRequest) CheckSignature() error {
20732079
// RevocationList contains the fields used to create an X.509 v2 Certificate
20742080
// Revocation list with CreateRevocationList.
20752081
type RevocationList struct {
2082+
Raw []byte
2083+
RawTBSRevocationList []byte
2084+
RawIssuer []byte
2085+
2086+
Issuer pkix.Name
2087+
AuthorityKeyId []byte
2088+
2089+
Signature []byte
20762090
// SignatureAlgorithm is used to determine the signature algorithm to be
20772091
// used when signing the CRL. If 0 the default algorithm for the signing
20782092
// key will be used.
@@ -2087,13 +2101,19 @@ type RevocationList struct {
20872101
// which should be a monotonically increasing sequence number for a given
20882102
// CRL scope and CRL issuer.
20892103
Number *big.Int
2104+
20902105
// ThisUpdate is used to populate the thisUpdate field in the CRL, which
20912106
// indicates the issuance date of the CRL.
20922107
ThisUpdate time.Time
20932108
// NextUpdate is used to populate the nextUpdate field in the CRL, which
20942109
// indicates the date by which the next CRL will be issued. NextUpdate
20952110
// must be greater than ThisUpdate.
20962111
NextUpdate time.Time
2112+
2113+
// Extensions contains raw X.509 extensions. When creating a CRL,
2114+
// the Extensions field is ignored, see ExtraExtensions.
2115+
Extensions []pkix.Extension
2116+
20972117
// ExtraExtensions contains any additional extensions to add directly to
20982118
// the CRL.
20992119
ExtraExtensions []pkix.Extension
@@ -2207,3 +2227,22 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
22072227
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
22082228
})
22092229
}
2230+
2231+
// CheckSignatureFrom verifies that the signature on rl is a valid signature
2232+
// from issuer.
2233+
func (rl *RevocationList) CheckSignatureFrom(parent *Certificate) error {
2234+
if parent.Version == 3 && !parent.BasicConstraintsValid ||
2235+
parent.BasicConstraintsValid && !parent.IsCA {
2236+
return ConstraintViolationError{}
2237+
}
2238+
2239+
if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCRLSign == 0 {
2240+
return ConstraintViolationError{}
2241+
}
2242+
2243+
if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm {
2244+
return ErrUnsupportedAlgorithm
2245+
}
2246+
2247+
return parent.CheckSignature(rl.SignatureAlgorithm, rl.RawTBSRevocationList, rl.Signature)
2248+
}

0 commit comments

Comments
 (0)