crypto/x509: Added RFC 5280, section 4.2.1.14 to parseCertificate and buildExtensions
Support for CRL Distribution Points

R=golang-dev, agl, bradfitz
CC=golang-dev
https://golang.org/cl/10258043
diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go
index d789e5c..7dea7b6 100644
--- a/src/pkg/crypto/x509/x509.go
+++ b/src/pkg/crypto/x509/x509.go
@@ -472,6 +472,9 @@
 	PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical.
 	PermittedDNSDomains         []string
 
+	// CRL Distribution Points
+	CRLDistributionPoints []string
+
 	PolicyIdentifiers []asn1.ObjectIdentifier
 }
 
@@ -659,6 +662,18 @@
 	Name string `asn1:"tag:2,optional,ia5"`
 }
 
+// RFC 5280, 4.2.1.14
+type distributionPoint struct {
+	DistributionPoint distributionPointName `asn1:"optional,tag:0"`
+	Reason            asn1.BitString        `asn1:"optional,tag:1"`
+	CRLIssuer         asn1.RawValue         `asn1:"optional,tag:2"`
+}
+
+type distributionPointName struct {
+	FullName     asn1.RawValue    `asn1:"optional,tag:0"`
+	RelativeName pkix.RDNSequence `asn1:"optional,tag:1"`
+}
+
 func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) {
 	asn1Data := keyData.PublicKey.RightAlign()
 	switch algo {
@@ -896,6 +911,39 @@
 				}
 				continue
 
+			case 31:
+				// RFC 5280, 4.2.1.14
+
+				// CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+				//
+				// DistributionPoint ::= SEQUENCE {
+				//     distributionPoint       [0]     DistributionPointName OPTIONAL,
+				//     reasons                 [1]     ReasonFlags OPTIONAL,
+				//     cRLIssuer               [2]     GeneralNames OPTIONAL }
+				//
+				// DistributionPointName ::= CHOICE {
+				//     fullName                [0]     GeneralNames,
+				//     nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
+
+				var cdp []distributionPoint
+				_, err := asn1.Unmarshal(e.Value, &cdp)
+				if err != nil {
+					return nil, err
+				}
+
+				for _, dp := range cdp {
+					var n asn1.RawValue
+					_, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n)
+					if err != nil {
+						return nil, err
+					}
+
+					if n.Tag == 6 {
+						out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes))
+					}
+				}
+				continue
+
 			case 35:
 				// RFC 5280, 4.2.1.1
 				var a authKeyId
@@ -1011,18 +1059,19 @@
 }
 
 var (
-	oidExtensionSubjectKeyId        = []int{2, 5, 29, 14}
-	oidExtensionKeyUsage            = []int{2, 5, 29, 15}
-	oidExtensionExtendedKeyUsage    = []int{2, 5, 29, 37}
-	oidExtensionAuthorityKeyId      = []int{2, 5, 29, 35}
-	oidExtensionBasicConstraints    = []int{2, 5, 29, 19}
-	oidExtensionSubjectAltName      = []int{2, 5, 29, 17}
-	oidExtensionCertificatePolicies = []int{2, 5, 29, 32}
-	oidExtensionNameConstraints     = []int{2, 5, 29, 30}
+	oidExtensionSubjectKeyId          = []int{2, 5, 29, 14}
+	oidExtensionKeyUsage              = []int{2, 5, 29, 15}
+	oidExtensionExtendedKeyUsage      = []int{2, 5, 29, 37}
+	oidExtensionAuthorityKeyId        = []int{2, 5, 29, 35}
+	oidExtensionBasicConstraints      = []int{2, 5, 29, 19}
+	oidExtensionSubjectAltName        = []int{2, 5, 29, 17}
+	oidExtensionCertificatePolicies   = []int{2, 5, 29, 32}
+	oidExtensionNameConstraints       = []int{2, 5, 29, 30}
+	oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31}
 )
 
 func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) {
-	ret = make([]pkix.Extension, 8 /* maximum number of elements. */)
+	ret = make([]pkix.Extension, 9 /* maximum number of elements. */)
 	n := 0
 
 	if template.KeyUsage != 0 {
@@ -1147,6 +1196,28 @@
 		n++
 	}
 
+	if len(template.CRLDistributionPoints) > 0 {
+		ret[n].Id = oidExtensionCRLDistributionPoints
+
+		var crlDp []distributionPoint
+		for _, name := range template.CRLDistributionPoints {
+			rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)})
+
+			dp := distributionPoint{
+				DistributionPoint: distributionPointName{
+					FullName: asn1.RawValue{Tag: 0, Class: 2, Bytes: rawFullName},
+				},
+			}
+			crlDp = append(crlDp, dp)
+		}
+
+		ret[n].Value, err = asn1.Marshal(crlDp)
+		if err != nil {
+			return
+		}
+		n++
+	}
+
 	// Adding another extension here? Remember to update the maximum number
 	// of elements in the make() at the top of the function.
 
diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go
index 123b1cf..08dd09f 100644
--- a/src/pkg/crypto/x509/x509_test.go
+++ b/src/pkg/crypto/x509/x509_test.go
@@ -336,6 +336,8 @@
 
 			PolicyIdentifiers:   []asn1.ObjectIdentifier{[]int{1, 2, 3}},
 			PermittedDNSDomains: []string{".example.com", "example.com"},
+
+			CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"},
 		}
 
 		derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv)
@@ -386,6 +388,10 @@
 			t.Errorf("%s: SAN IPs differ from template. Got %v, want %v", test.name, cert.IPAddresses, template.IPAddresses)
 		}
 
+		if !reflect.DeepEqual(cert.CRLDistributionPoints, template.CRLDistributionPoints) {
+			t.Errorf("%s: CRL distribution points differ from template. Got %v, want %v", test.name, cert.CRLDistributionPoints, template.CRLDistributionPoints)
+		}
+
 		if test.checkSig {
 			err = cert.CheckSignatureFrom(cert)
 			if err != nil {