mirror of https://github.com/containers/podman.git
				
				
				
			
		
			
				
	
	
		
			794 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			794 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
| // Copyright 2013 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses
 | |
| // are signed messages attesting to the validity of a certificate for a small
 | |
| // period of time. This is used to manage revocation for X.509 certificates.
 | |
| package ocsp // import "golang.org/x/crypto/ocsp"
 | |
| 
 | |
| import (
 | |
| 	"crypto"
 | |
| 	"crypto/ecdsa"
 | |
| 	"crypto/elliptic"
 | |
| 	"crypto/rand"
 | |
| 	"crypto/rsa"
 | |
| 	_ "crypto/sha1"
 | |
| 	_ "crypto/sha256"
 | |
| 	_ "crypto/sha512"
 | |
| 	"crypto/x509"
 | |
| 	"crypto/x509/pkix"
 | |
| 	"encoding/asn1"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"math/big"
 | |
| 	"strconv"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1})
 | |
| 
 | |
| // ResponseStatus contains the result of an OCSP request. See
 | |
| // https://tools.ietf.org/html/rfc6960#section-2.3
 | |
| type ResponseStatus int
 | |
| 
 | |
| const (
 | |
| 	Success       ResponseStatus = 0
 | |
| 	Malformed     ResponseStatus = 1
 | |
| 	InternalError ResponseStatus = 2
 | |
| 	TryLater      ResponseStatus = 3
 | |
| 	// Status code four is unused in OCSP. See
 | |
| 	// https://tools.ietf.org/html/rfc6960#section-4.2.1
 | |
| 	SignatureRequired ResponseStatus = 5
 | |
| 	Unauthorized      ResponseStatus = 6
 | |
| )
 | |
| 
 | |
| func (r ResponseStatus) String() string {
 | |
| 	switch r {
 | |
| 	case Success:
 | |
| 		return "success"
 | |
| 	case Malformed:
 | |
| 		return "malformed"
 | |
| 	case InternalError:
 | |
| 		return "internal error"
 | |
| 	case TryLater:
 | |
| 		return "try later"
 | |
| 	case SignatureRequired:
 | |
| 		return "signature required"
 | |
| 	case Unauthorized:
 | |
| 		return "unauthorized"
 | |
| 	default:
 | |
| 		return "unknown OCSP status: " + strconv.Itoa(int(r))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ResponseError is an error that may be returned by ParseResponse to indicate
 | |
| // that the response itself is an error, not just that it's indicating that a
 | |
| // certificate is revoked, unknown, etc.
 | |
| type ResponseError struct {
 | |
| 	Status ResponseStatus
 | |
| }
 | |
| 
 | |
| func (r ResponseError) Error() string {
 | |
| 	return "ocsp: error from server: " + r.Status.String()
 | |
| }
 | |
| 
 | |
| // These are internal structures that reflect the ASN.1 structure of an OCSP
 | |
| // response. See RFC 2560, section 4.2.
 | |
| 
 | |
| type certID struct {
 | |
| 	HashAlgorithm pkix.AlgorithmIdentifier
 | |
| 	NameHash      []byte
 | |
| 	IssuerKeyHash []byte
 | |
| 	SerialNumber  *big.Int
 | |
| }
 | |
| 
 | |
| // https://tools.ietf.org/html/rfc2560#section-4.1.1
 | |
| type ocspRequest struct {
 | |
| 	TBSRequest tbsRequest
 | |
| }
 | |
| 
 | |
| type tbsRequest struct {
 | |
| 	Version       int              `asn1:"explicit,tag:0,default:0,optional"`
 | |
| 	RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"`
 | |
| 	RequestList   []request
 | |
| }
 | |
| 
 | |
| type request struct {
 | |
| 	Cert certID
 | |
| }
 | |
| 
 | |
| type responseASN1 struct {
 | |
| 	Status   asn1.Enumerated
 | |
| 	Response responseBytes `asn1:"explicit,tag:0,optional"`
 | |
| }
 | |
| 
 | |
| type responseBytes struct {
 | |
| 	ResponseType asn1.ObjectIdentifier
 | |
| 	Response     []byte
 | |
| }
 | |
| 
 | |
| type basicResponse struct {
 | |
| 	TBSResponseData    responseData
 | |
| 	SignatureAlgorithm pkix.AlgorithmIdentifier
 | |
| 	Signature          asn1.BitString
 | |
| 	Certificates       []asn1.RawValue `asn1:"explicit,tag:0,optional"`
 | |
| }
 | |
| 
 | |
| type responseData struct {
 | |
| 	Raw            asn1.RawContent
 | |
| 	Version        int `asn1:"optional,default:0,explicit,tag:0"`
 | |
| 	RawResponderID asn1.RawValue
 | |
| 	ProducedAt     time.Time `asn1:"generalized"`
 | |
| 	Responses      []singleResponse
 | |
| }
 | |
| 
 | |
| type singleResponse struct {
 | |
| 	CertID           certID
 | |
| 	Good             asn1.Flag        `asn1:"tag:0,optional"`
 | |
| 	Revoked          revokedInfo      `asn1:"tag:1,optional"`
 | |
| 	Unknown          asn1.Flag        `asn1:"tag:2,optional"`
 | |
| 	ThisUpdate       time.Time        `asn1:"generalized"`
 | |
| 	NextUpdate       time.Time        `asn1:"generalized,explicit,tag:0,optional"`
 | |
| 	SingleExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"`
 | |
| }
 | |
| 
 | |
| type revokedInfo struct {
 | |
| 	RevocationTime time.Time       `asn1:"generalized"`
 | |
| 	Reason         asn1.Enumerated `asn1:"explicit,tag:0,optional"`
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	oidSignatureMD2WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
 | |
| 	oidSignatureMD5WithRSA      = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
 | |
| 	oidSignatureSHA1WithRSA     = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
 | |
| 	oidSignatureSHA256WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
 | |
| 	oidSignatureSHA384WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
 | |
| 	oidSignatureSHA512WithRSA   = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
 | |
| 	oidSignatureDSAWithSHA1     = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
 | |
| 	oidSignatureDSAWithSHA256   = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
 | |
| 	oidSignatureECDSAWithSHA1   = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
 | |
| 	oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
 | |
| 	oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
 | |
| 	oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
 | |
| )
 | |
| 
 | |
| var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{
 | |
| 	crypto.SHA1:   asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}),
 | |
| 	crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}),
 | |
| 	crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}),
 | |
| 	crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}),
 | |
| }
 | |
| 
 | |
| // TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
 | |
| var signatureAlgorithmDetails = []struct {
 | |
| 	algo       x509.SignatureAlgorithm
 | |
| 	oid        asn1.ObjectIdentifier
 | |
| 	pubKeyAlgo x509.PublicKeyAlgorithm
 | |
| 	hash       crypto.Hash
 | |
| }{
 | |
| 	{x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
 | |
| 	{x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
 | |
| 	{x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
 | |
| 	{x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
 | |
| 	{x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
 | |
| 	{x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
 | |
| 	{x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
 | |
| 	{x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
 | |
| 	{x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
 | |
| 	{x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
 | |
| 	{x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
 | |
| 	{x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
 | |
| }
 | |
| 
 | |
| // TODO(rlb): This is also from crypto/x509, so same comment as AGL's below
 | |
| func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
 | |
| 	var pubType x509.PublicKeyAlgorithm
 | |
| 
 | |
| 	switch pub := pub.(type) {
 | |
| 	case *rsa.PublicKey:
 | |
| 		pubType = x509.RSA
 | |
| 		hashFunc = crypto.SHA256
 | |
| 		sigAlgo.Algorithm = oidSignatureSHA256WithRSA
 | |
| 		sigAlgo.Parameters = asn1.RawValue{
 | |
| 			Tag: 5,
 | |
| 		}
 | |
| 
 | |
| 	case *ecdsa.PublicKey:
 | |
| 		pubType = x509.ECDSA
 | |
| 
 | |
| 		switch pub.Curve {
 | |
| 		case elliptic.P224(), elliptic.P256():
 | |
| 			hashFunc = crypto.SHA256
 | |
| 			sigAlgo.Algorithm = oidSignatureECDSAWithSHA256
 | |
| 		case elliptic.P384():
 | |
| 			hashFunc = crypto.SHA384
 | |
| 			sigAlgo.Algorithm = oidSignatureECDSAWithSHA384
 | |
| 		case elliptic.P521():
 | |
| 			hashFunc = crypto.SHA512
 | |
| 			sigAlgo.Algorithm = oidSignatureECDSAWithSHA512
 | |
| 		default:
 | |
| 			err = errors.New("x509: unknown elliptic curve")
 | |
| 		}
 | |
| 
 | |
| 	default:
 | |
| 		err = errors.New("x509: only RSA and ECDSA keys supported")
 | |
| 	}
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if requestedSigAlgo == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	found := false
 | |
| 	for _, details := range signatureAlgorithmDetails {
 | |
| 		if details.algo == requestedSigAlgo {
 | |
| 			if details.pubKeyAlgo != pubType {
 | |
| 				err = errors.New("x509: requested SignatureAlgorithm does not match private key type")
 | |
| 				return
 | |
| 			}
 | |
| 			sigAlgo.Algorithm, hashFunc = details.oid, details.hash
 | |
| 			if hashFunc == 0 {
 | |
| 				err = errors.New("x509: cannot sign with hash function requested")
 | |
| 				return
 | |
| 			}
 | |
| 			found = true
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if !found {
 | |
| 		err = errors.New("x509: unknown SignatureAlgorithm")
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // TODO(agl): this is taken from crypto/x509 and so should probably be exported
 | |
| // from crypto/x509 or crypto/x509/pkix.
 | |
| func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm {
 | |
| 	for _, details := range signatureAlgorithmDetails {
 | |
| 		if oid.Equal(details.oid) {
 | |
| 			return details.algo
 | |
| 		}
 | |
| 	}
 | |
| 	return x509.UnknownSignatureAlgorithm
 | |
| }
 | |
| 
 | |
| // TODO(rlb): This is not taken from crypto/x509, but it's of the same general form.
 | |
| func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash {
 | |
| 	for hash, oid := range hashOIDs {
 | |
| 		if oid.Equal(target) {
 | |
| 			return hash
 | |
| 		}
 | |
| 	}
 | |
| 	return crypto.Hash(0)
 | |
| }
 | |
| 
 | |
| func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier {
 | |
| 	for hash, oid := range hashOIDs {
 | |
| 		if hash == target {
 | |
| 			return oid
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // This is the exposed reflection of the internal OCSP structures.
 | |
| 
 | |
| // The status values that can be expressed in OCSP. See RFC 6960.
 | |
| // These are used for the Response.Status field.
 | |
| const (
 | |
| 	// Good means that the certificate is valid.
 | |
| 	Good = 0
 | |
| 	// Revoked means that the certificate has been deliberately revoked.
 | |
| 	Revoked = 1
 | |
| 	// Unknown means that the OCSP responder doesn't know about the certificate.
 | |
| 	Unknown = 2
 | |
| 	// ServerFailed is unused and was never used (see
 | |
| 	// https://go-review.googlesource.com/#/c/18944). ParseResponse will
 | |
| 	// return a ResponseError when an error response is parsed.
 | |
| 	ServerFailed = 3
 | |
| )
 | |
| 
 | |
| // The enumerated reasons for revoking a certificate. See RFC 5280.
 | |
| const (
 | |
| 	Unspecified          = 0
 | |
| 	KeyCompromise        = 1
 | |
| 	CACompromise         = 2
 | |
| 	AffiliationChanged   = 3
 | |
| 	Superseded           = 4
 | |
| 	CessationOfOperation = 5
 | |
| 	CertificateHold      = 6
 | |
| 
 | |
| 	RemoveFromCRL      = 8
 | |
| 	PrivilegeWithdrawn = 9
 | |
| 	AACompromise       = 10
 | |
| )
 | |
| 
 | |
| // Request represents an OCSP request. See RFC 6960.
 | |
| type Request struct {
 | |
| 	HashAlgorithm  crypto.Hash
 | |
| 	IssuerNameHash []byte
 | |
| 	IssuerKeyHash  []byte
 | |
| 	SerialNumber   *big.Int
 | |
| }
 | |
| 
 | |
| // Marshal marshals the OCSP request to ASN.1 DER encoded form.
 | |
| func (req *Request) Marshal() ([]byte, error) {
 | |
| 	hashAlg := getOIDFromHashAlgorithm(req.HashAlgorithm)
 | |
| 	if hashAlg == nil {
 | |
| 		return nil, errors.New("Unknown hash algorithm")
 | |
| 	}
 | |
| 	return asn1.Marshal(ocspRequest{
 | |
| 		tbsRequest{
 | |
| 			Version: 0,
 | |
| 			RequestList: []request{
 | |
| 				{
 | |
| 					Cert: certID{
 | |
| 						pkix.AlgorithmIdentifier{
 | |
| 							Algorithm:  hashAlg,
 | |
| 							Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
 | |
| 						},
 | |
| 						req.IssuerNameHash,
 | |
| 						req.IssuerKeyHash,
 | |
| 						req.SerialNumber,
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // Response represents an OCSP response containing a single SingleResponse. See
 | |
| // RFC 6960.
 | |
| type Response struct {
 | |
| 	Raw []byte
 | |
| 
 | |
| 	// Status is one of {Good, Revoked, Unknown}
 | |
| 	Status                                        int
 | |
| 	SerialNumber                                  *big.Int
 | |
| 	ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time
 | |
| 	RevocationReason                              int
 | |
| 	Certificate                                   *x509.Certificate
 | |
| 	// TBSResponseData contains the raw bytes of the signed response. If
 | |
| 	// Certificate is nil then this can be used to verify Signature.
 | |
| 	TBSResponseData    []byte
 | |
| 	Signature          []byte
 | |
| 	SignatureAlgorithm x509.SignatureAlgorithm
 | |
| 
 | |
| 	// IssuerHash is the hash used to compute the IssuerNameHash and IssuerKeyHash.
 | |
| 	// Valid values are crypto.SHA1, crypto.SHA256, crypto.SHA384, and crypto.SHA512.
 | |
| 	// If zero, the default is crypto.SHA1.
 | |
| 	IssuerHash crypto.Hash
 | |
| 
 | |
| 	// RawResponderName optionally contains the DER-encoded subject of the
 | |
| 	// responder certificate. Exactly one of RawResponderName and
 | |
| 	// ResponderKeyHash is set.
 | |
| 	RawResponderName []byte
 | |
| 	// ResponderKeyHash optionally contains the SHA-1 hash of the
 | |
| 	// responder's public key. Exactly one of RawResponderName and
 | |
| 	// ResponderKeyHash is set.
 | |
| 	ResponderKeyHash []byte
 | |
| 
 | |
| 	// Extensions contains raw X.509 extensions from the singleExtensions field
 | |
| 	// of the OCSP response. When parsing certificates, this can be used to
 | |
| 	// extract non-critical extensions that are not parsed by this package. When
 | |
| 	// marshaling OCSP responses, the Extensions field is ignored, see
 | |
| 	// ExtraExtensions.
 | |
| 	Extensions []pkix.Extension
 | |
| 
 | |
| 	// ExtraExtensions contains extensions to be copied, raw, into any marshaled
 | |
| 	// OCSP response (in the singleExtensions field). Values override any
 | |
| 	// extensions that would otherwise be produced based on the other fields. The
 | |
| 	// ExtraExtensions field is not populated when parsing certificates, see
 | |
| 	// Extensions.
 | |
| 	ExtraExtensions []pkix.Extension
 | |
| }
 | |
| 
 | |
| // These are pre-serialized error responses for the various non-success codes
 | |
| // defined by OCSP. The Unauthorized code in particular can be used by an OCSP
 | |
| // responder that supports only pre-signed responses as a response to requests
 | |
| // for certificates with unknown status. See RFC 5019.
 | |
| var (
 | |
| 	MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
 | |
| 	InternalErrorErrorResponse    = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
 | |
| 	TryLaterErrorResponse         = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
 | |
| 	SigRequredErrorResponse       = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
 | |
| 	UnauthorizedErrorResponse     = []byte{0x30, 0x03, 0x0A, 0x01, 0x06}
 | |
| )
 | |
| 
 | |
| // CheckSignatureFrom checks that the signature in resp is a valid signature
 | |
| // from issuer. This should only be used if resp.Certificate is nil. Otherwise,
 | |
| // the OCSP response contained an intermediate certificate that created the
 | |
| // signature. That signature is checked by ParseResponse and only
 | |
| // resp.Certificate remains to be validated.
 | |
| func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error {
 | |
| 	return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature)
 | |
| }
 | |
| 
 | |
| // ParseError results from an invalid OCSP response.
 | |
| type ParseError string
 | |
| 
 | |
| func (p ParseError) Error() string {
 | |
| 	return string(p)
 | |
| }
 | |
| 
 | |
| // ParseRequest parses an OCSP request in DER form. It only supports
 | |
| // requests for a single certificate. Signed requests are not supported.
 | |
| // If a request includes a signature, it will result in a ParseError.
 | |
| func ParseRequest(bytes []byte) (*Request, error) {
 | |
| 	var req ocspRequest
 | |
| 	rest, err := asn1.Unmarshal(bytes, &req)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if len(rest) > 0 {
 | |
| 		return nil, ParseError("trailing data in OCSP request")
 | |
| 	}
 | |
| 
 | |
| 	if len(req.TBSRequest.RequestList) == 0 {
 | |
| 		return nil, ParseError("OCSP request contains no request body")
 | |
| 	}
 | |
| 	innerRequest := req.TBSRequest.RequestList[0]
 | |
| 
 | |
| 	hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm)
 | |
| 	if hashFunc == crypto.Hash(0) {
 | |
| 		return nil, ParseError("OCSP request uses unknown hash function")
 | |
| 	}
 | |
| 
 | |
| 	return &Request{
 | |
| 		HashAlgorithm:  hashFunc,
 | |
| 		IssuerNameHash: innerRequest.Cert.NameHash,
 | |
| 		IssuerKeyHash:  innerRequest.Cert.IssuerKeyHash,
 | |
| 		SerialNumber:   innerRequest.Cert.SerialNumber,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // ParseResponse parses an OCSP response in DER form. The response must contain
 | |
| // only one certificate status. To parse the status of a specific certificate
 | |
| // from a response which may contain multiple statuses, use ParseResponseForCert
 | |
| // instead.
 | |
| //
 | |
| // If the response contains an embedded certificate, then that certificate will
 | |
| // be used to verify the response signature. If the response contains an
 | |
| // embedded certificate and issuer is not nil, then issuer will be used to verify
 | |
| // the signature on the embedded certificate.
 | |
| //
 | |
| // If the response does not contain an embedded certificate and issuer is not
 | |
| // nil, then issuer will be used to verify the response signature.
 | |
| //
 | |
| // Invalid responses and parse failures will result in a ParseError.
 | |
| // Error responses will result in a ResponseError.
 | |
| func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) {
 | |
| 	return ParseResponseForCert(bytes, nil, issuer)
 | |
| }
 | |
| 
 | |
| // ParseResponseForCert acts identically to ParseResponse, except it supports
 | |
| // parsing responses that contain multiple statuses. If the response contains
 | |
| // multiple statuses and cert is not nil, then ParseResponseForCert will return
 | |
| // the first status which contains a matching serial, otherwise it will return an
 | |
| // error. If cert is nil, then the first status in the response will be returned.
 | |
| func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) {
 | |
| 	var resp responseASN1
 | |
| 	rest, err := asn1.Unmarshal(bytes, &resp)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if len(rest) > 0 {
 | |
| 		return nil, ParseError("trailing data in OCSP response")
 | |
| 	}
 | |
| 
 | |
| 	if status := ResponseStatus(resp.Status); status != Success {
 | |
| 		return nil, ResponseError{status}
 | |
| 	}
 | |
| 
 | |
| 	if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) {
 | |
| 		return nil, ParseError("bad OCSP response type")
 | |
| 	}
 | |
| 
 | |
| 	var basicResp basicResponse
 | |
| 	rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if len(rest) > 0 {
 | |
| 		return nil, ParseError("trailing data in OCSP response")
 | |
| 	}
 | |
| 
 | |
| 	if n := len(basicResp.TBSResponseData.Responses); n == 0 || cert == nil && n > 1 {
 | |
| 		return nil, ParseError("OCSP response contains bad number of responses")
 | |
| 	}
 | |
| 
 | |
| 	var singleResp singleResponse
 | |
| 	if cert == nil {
 | |
| 		singleResp = basicResp.TBSResponseData.Responses[0]
 | |
| 	} else {
 | |
| 		match := false
 | |
| 		for _, resp := range basicResp.TBSResponseData.Responses {
 | |
| 			if cert.SerialNumber.Cmp(resp.CertID.SerialNumber) == 0 {
 | |
| 				singleResp = resp
 | |
| 				match = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !match {
 | |
| 			return nil, ParseError("no response matching the supplied certificate")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	ret := &Response{
 | |
| 		Raw:                bytes,
 | |
| 		TBSResponseData:    basicResp.TBSResponseData.Raw,
 | |
| 		Signature:          basicResp.Signature.RightAlign(),
 | |
| 		SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm),
 | |
| 		Extensions:         singleResp.SingleExtensions,
 | |
| 		SerialNumber:       singleResp.CertID.SerialNumber,
 | |
| 		ProducedAt:         basicResp.TBSResponseData.ProducedAt,
 | |
| 		ThisUpdate:         singleResp.ThisUpdate,
 | |
| 		NextUpdate:         singleResp.NextUpdate,
 | |
| 	}
 | |
| 
 | |
| 	// Handle the ResponderID CHOICE tag. ResponderID can be flattened into
 | |
| 	// TBSResponseData once https://go-review.googlesource.com/34503 has been
 | |
| 	// released.
 | |
| 	rawResponderID := basicResp.TBSResponseData.RawResponderID
 | |
| 	switch rawResponderID.Tag {
 | |
| 	case 1: // Name
 | |
| 		var rdn pkix.RDNSequence
 | |
| 		if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &rdn); err != nil || len(rest) != 0 {
 | |
| 			return nil, ParseError("invalid responder name")
 | |
| 		}
 | |
| 		ret.RawResponderName = rawResponderID.Bytes
 | |
| 	case 2: // KeyHash
 | |
| 		if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &ret.ResponderKeyHash); err != nil || len(rest) != 0 {
 | |
| 			return nil, ParseError("invalid responder key hash")
 | |
| 		}
 | |
| 	default:
 | |
| 		return nil, ParseError("invalid responder id tag")
 | |
| 	}
 | |
| 
 | |
| 	if len(basicResp.Certificates) > 0 {
 | |
| 		// Responders should only send a single certificate (if they
 | |
| 		// send any) that connects the responder's certificate to the
 | |
| 		// original issuer. We accept responses with multiple
 | |
| 		// certificates due to a number responders sending them[1], but
 | |
| 		// ignore all but the first.
 | |
| 		//
 | |
| 		// [1] https://github.com/golang/go/issues/21527
 | |
| 		ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
 | |
| 			return nil, ParseError("bad signature on embedded certificate: " + err.Error())
 | |
| 		}
 | |
| 
 | |
| 		if issuer != nil {
 | |
| 			if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil {
 | |
| 				return nil, ParseError("bad OCSP signature: " + err.Error())
 | |
| 			}
 | |
| 		}
 | |
| 	} else if issuer != nil {
 | |
| 		if err := ret.CheckSignatureFrom(issuer); err != nil {
 | |
| 			return nil, ParseError("bad OCSP signature: " + err.Error())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, ext := range singleResp.SingleExtensions {
 | |
| 		if ext.Critical {
 | |
| 			return nil, ParseError("unsupported critical extension")
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for h, oid := range hashOIDs {
 | |
| 		if singleResp.CertID.HashAlgorithm.Algorithm.Equal(oid) {
 | |
| 			ret.IssuerHash = h
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if ret.IssuerHash == 0 {
 | |
| 		return nil, ParseError("unsupported issuer hash algorithm")
 | |
| 	}
 | |
| 
 | |
| 	switch {
 | |
| 	case bool(singleResp.Good):
 | |
| 		ret.Status = Good
 | |
| 	case bool(singleResp.Unknown):
 | |
| 		ret.Status = Unknown
 | |
| 	default:
 | |
| 		ret.Status = Revoked
 | |
| 		ret.RevokedAt = singleResp.Revoked.RevocationTime
 | |
| 		ret.RevocationReason = int(singleResp.Revoked.Reason)
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 | |
| 
 | |
| // RequestOptions contains options for constructing OCSP requests.
 | |
| type RequestOptions struct {
 | |
| 	// Hash contains the hash function that should be used when
 | |
| 	// constructing the OCSP request. If zero, SHA-1 will be used.
 | |
| 	Hash crypto.Hash
 | |
| }
 | |
| 
 | |
| func (opts *RequestOptions) hash() crypto.Hash {
 | |
| 	if opts == nil || opts.Hash == 0 {
 | |
| 		// SHA-1 is nearly universally used in OCSP.
 | |
| 		return crypto.SHA1
 | |
| 	}
 | |
| 	return opts.Hash
 | |
| }
 | |
| 
 | |
| // CreateRequest returns a DER-encoded, OCSP request for the status of cert. If
 | |
| // opts is nil then sensible defaults are used.
 | |
| func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) {
 | |
| 	hashFunc := opts.hash()
 | |
| 
 | |
| 	// OCSP seems to be the only place where these raw hash identifiers are
 | |
| 	// used. I took the following from
 | |
| 	// http://msdn.microsoft.com/en-us/library/ff635603.aspx
 | |
| 	_, ok := hashOIDs[hashFunc]
 | |
| 	if !ok {
 | |
| 		return nil, x509.ErrUnsupportedAlgorithm
 | |
| 	}
 | |
| 
 | |
| 	if !hashFunc.Available() {
 | |
| 		return nil, x509.ErrUnsupportedAlgorithm
 | |
| 	}
 | |
| 	h := opts.hash().New()
 | |
| 
 | |
| 	var publicKeyInfo struct {
 | |
| 		Algorithm pkix.AlgorithmIdentifier
 | |
| 		PublicKey asn1.BitString
 | |
| 	}
 | |
| 	if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	h.Write(publicKeyInfo.PublicKey.RightAlign())
 | |
| 	issuerKeyHash := h.Sum(nil)
 | |
| 
 | |
| 	h.Reset()
 | |
| 	h.Write(issuer.RawSubject)
 | |
| 	issuerNameHash := h.Sum(nil)
 | |
| 
 | |
| 	req := &Request{
 | |
| 		HashAlgorithm:  hashFunc,
 | |
| 		IssuerNameHash: issuerNameHash,
 | |
| 		IssuerKeyHash:  issuerKeyHash,
 | |
| 		SerialNumber:   cert.SerialNumber,
 | |
| 	}
 | |
| 	return req.Marshal()
 | |
| }
 | |
| 
 | |
| // CreateResponse returns a DER-encoded OCSP response with the specified contents.
 | |
| // The fields in the response are populated as follows:
 | |
| //
 | |
| // The responder cert is used to populate the responder's name field, and the
 | |
| // certificate itself is provided alongside the OCSP response signature.
 | |
| //
 | |
| // The issuer cert is used to populate the IssuerNameHash and IssuerKeyHash fields.
 | |
| //
 | |
| // The template is used to populate the SerialNumber, Status, RevokedAt,
 | |
| // RevocationReason, ThisUpdate, and NextUpdate fields.
 | |
| //
 | |
| // If template.IssuerHash is not set, SHA1 will be used.
 | |
| //
 | |
| // The ProducedAt date is automatically set to the current date, to the nearest minute.
 | |
| func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) {
 | |
| 	var publicKeyInfo struct {
 | |
| 		Algorithm pkix.AlgorithmIdentifier
 | |
| 		PublicKey asn1.BitString
 | |
| 	}
 | |
| 	if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if template.IssuerHash == 0 {
 | |
| 		template.IssuerHash = crypto.SHA1
 | |
| 	}
 | |
| 	hashOID := getOIDFromHashAlgorithm(template.IssuerHash)
 | |
| 	if hashOID == nil {
 | |
| 		return nil, errors.New("unsupported issuer hash algorithm")
 | |
| 	}
 | |
| 
 | |
| 	if !template.IssuerHash.Available() {
 | |
| 		return nil, fmt.Errorf("issuer hash algorithm %v not linked into binary", template.IssuerHash)
 | |
| 	}
 | |
| 	h := template.IssuerHash.New()
 | |
| 	h.Write(publicKeyInfo.PublicKey.RightAlign())
 | |
| 	issuerKeyHash := h.Sum(nil)
 | |
| 
 | |
| 	h.Reset()
 | |
| 	h.Write(issuer.RawSubject)
 | |
| 	issuerNameHash := h.Sum(nil)
 | |
| 
 | |
| 	innerResponse := singleResponse{
 | |
| 		CertID: certID{
 | |
| 			HashAlgorithm: pkix.AlgorithmIdentifier{
 | |
| 				Algorithm:  hashOID,
 | |
| 				Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */},
 | |
| 			},
 | |
| 			NameHash:      issuerNameHash,
 | |
| 			IssuerKeyHash: issuerKeyHash,
 | |
| 			SerialNumber:  template.SerialNumber,
 | |
| 		},
 | |
| 		ThisUpdate:       template.ThisUpdate.UTC(),
 | |
| 		NextUpdate:       template.NextUpdate.UTC(),
 | |
| 		SingleExtensions: template.ExtraExtensions,
 | |
| 	}
 | |
| 
 | |
| 	switch template.Status {
 | |
| 	case Good:
 | |
| 		innerResponse.Good = true
 | |
| 	case Unknown:
 | |
| 		innerResponse.Unknown = true
 | |
| 	case Revoked:
 | |
| 		innerResponse.Revoked = revokedInfo{
 | |
| 			RevocationTime: template.RevokedAt.UTC(),
 | |
| 			Reason:         asn1.Enumerated(template.RevocationReason),
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	rawResponderID := asn1.RawValue{
 | |
| 		Class:      2, // context-specific
 | |
| 		Tag:        1, // Name (explicit tag)
 | |
| 		IsCompound: true,
 | |
| 		Bytes:      responderCert.RawSubject,
 | |
| 	}
 | |
| 	tbsResponseData := responseData{
 | |
| 		Version:        0,
 | |
| 		RawResponderID: rawResponderID,
 | |
| 		ProducedAt:     time.Now().Truncate(time.Minute).UTC(),
 | |
| 		Responses:      []singleResponse{innerResponse},
 | |
| 	}
 | |
| 
 | |
| 	tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	responseHash := hashFunc.New()
 | |
| 	responseHash.Write(tbsResponseDataDER)
 | |
| 	signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	response := basicResponse{
 | |
| 		TBSResponseData:    tbsResponseData,
 | |
| 		SignatureAlgorithm: signatureAlgorithm,
 | |
| 		Signature: asn1.BitString{
 | |
| 			Bytes:     signature,
 | |
| 			BitLength: 8 * len(signature),
 | |
| 		},
 | |
| 	}
 | |
| 	if template.Certificate != nil {
 | |
| 		response.Certificates = []asn1.RawValue{
 | |
| 			{FullBytes: template.Certificate.Raw},
 | |
| 		}
 | |
| 	}
 | |
| 	responseDER, err := asn1.Marshal(response)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return asn1.Marshal(responseASN1{
 | |
| 		Status: asn1.Enumerated(Success),
 | |
| 		Response: responseBytes{
 | |
| 			ResponseType: idPKIXOCSPBasic,
 | |
| 			Response:     responseDER,
 | |
| 		},
 | |
| 	})
 | |
| }
 |