From 287a1d87db5d649b01d6193bd9d07e909f08094c Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Thu, 27 Aug 2015 11:19:40 -0400 Subject: [PATCH] ocsp: use asn1.Enumerated for revocation reason In the initial patch enabling generation of OCSP responses, the Reason field in the revokedInfo struct used for serializing responses was set to type int. That type maps to the ASN.1 type INTEGER, not ENUMERATED, as required by RFC 6960. As a result, if you serialize an OCSP resonse with the Reason field populated, then it will be rejected as malformed by compliant OCSP parsers. This patch changes the type of the Reason field in revokedInfo to asn1.Enumerated. It leaves the RevocationReason field in the public Response struct as int, and converts between the two. The patch also adds constant for the defined revocation reasons. Change-Id: I97205319503f447cde12d9a0bb0bd1a8db7a66ee Reviewed-on: https://go-review.googlesource.com/13964 Reviewed-by: Adam Langley --- ocsp/ocsp.go | 24 ++++++++++++++++++++---- ocsp/ocsp_test.go | 6 +++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/ocsp/ocsp.go b/ocsp/ocsp.go index 1850e1b..99120b5 100644 --- a/ocsp/ocsp.go +++ b/ocsp/ocsp.go @@ -94,8 +94,8 @@ type singleResponse struct { } type revokedInfo struct { - RevocationTime time.Time `asn1:"generalized"` - Reason int `asn1:"explicit,tag:0,optional"` + RevocationTime time.Time `asn1:"generalized"` + Reason asn1.Enumerated `asn1:"explicit,tag:0,optional"` } var ( @@ -230,6 +230,7 @@ func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash { // This is the exposed reflection of the internal OCSP structures. +// The status values that can be expressed in OCSP. See RFC 6960. const ( // Good means that the certificate is valid. Good = iota @@ -241,6 +242,21 @@ const ( ServerFailed = iota ) +// The enumerated reasons for revoking a certificate. See RFC 5280. +const ( + Unspecified = iota + KeyCompromise = iota + CACompromise = iota + AffiliationChanged = iota + Superseded = iota + CessationOfOperation = iota + CertificateHold = iota + _ = iota + RemoveFromCRL = iota + PrivilegeWithdrawn = iota + AACompromise = iota +) + // Request represents an OCSP request. See RFC 2560. type Request struct { HashAlgorithm crypto.Hash @@ -399,7 +415,7 @@ func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) { default: ret.Status = Revoked ret.RevokedAt = r.Revoked.RevocationTime - ret.RevocationReason = r.Revoked.Reason + ret.RevocationReason = int(r.Revoked.Reason) } ret.ProducedAt = basicResp.TBSResponseData.ProducedAt @@ -530,7 +546,7 @@ func CreateResponse(issuer, responderCert *x509.Certificate, template Response, case Revoked: innerResponse.Revoked = revokedInfo{ RevocationTime: template.RevokedAt.UTC(), - Reason: template.RevocationReason, + Reason: asn1.Enumerated(template.RevocationReason), } } diff --git a/ocsp/ocsp_test.go b/ocsp/ocsp_test.go index 5ea9eb2..d55682f 100644 --- a/ocsp/ocsp_test.go +++ b/ocsp/ocsp_test.go @@ -26,9 +26,9 @@ func TestOCSPDecode(t *testing.T) { } expected := Response{ - Status: 0, + Status: Good, SerialNumber: big.NewInt(0x1d0fa), - RevocationReason: 0, + RevocationReason: Unspecified, ThisUpdate: time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC), NextUpdate: time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC), } @@ -171,7 +171,7 @@ func TestOCSPResponse(t *testing.T) { ThisUpdate: thisUpdate, NextUpdate: nextUpdate, RevokedAt: thisUpdate, - RevocationReason: 1, // keyCompromise + RevocationReason: KeyCompromise, Certificate: responder, }