goocsp/pkg/ocsp/ocsp.go

895 lines
27 KiB
Go
Raw Permalink Normal View History

2022-03-28 19:28:41 +00:00
/*
Copyright 2022 CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// 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"
2022-03-29 16:20:28 +00:00
_ "crypto/sha1" //nolint:gosec
_ "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})
var idPKIXOCSPExtendedRevoke = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 9})
2022-03-29 16:20:28 +00:00
// ResponseStatus contains the result of an OCSP request. See https://tools.ietf.org/html/rfc6960#section-2.3
type ResponseStatus int
const (
2022-03-29 16:20:28 +00:00
Success ResponseStatus = 0 // response has valid confirmations
Malformed ResponseStatus = 1 // illegal confirmation request
InternalError ResponseStatus = 2 // internal error in issuer
TryLater ResponseStatus = 3 // try again later
// Status code four is unused in OCSP. See https://tools.ietf.org/html/rfc6960#section-4.2.1
SignatureRequired ResponseStatus = 5 // must sign the request
Unauthorized ResponseStatus = 6 // request unauthorized
)
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
}
2022-03-29 16:20:28 +00:00
type ocspResponseASN1 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
ResponseExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"`
}
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
2022-03-29 16:20:28 +00:00
//nolint:cyclop
func signingParamsForPublicKey(
pub interface{},
requestedSigAlgo x509.SignatureAlgorithm,
) (crypto.Hash, pkix.AlgorithmIdentifier, error) {
var (
pubType x509.PublicKeyAlgorithm
hashFunc crypto.Hash
sigAlgo pkix.AlgorithmIdentifier
err error
)
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 {
2022-03-29 16:20:28 +00:00
return 0, pkix.AlgorithmIdentifier{}, err
}
if requestedSigAlgo == 0 {
2022-03-29 16:20:28 +00:00
return hashFunc, sigAlgo, nil
}
found := false
2022-03-29 16:20:28 +00:00
for _, details := range signatureAlgorithmDetails {
if details.algo == requestedSigAlgo {
if details.pubKeyAlgo != pubType {
2022-03-29 16:20:28 +00:00
return 0, pkix.AlgorithmIdentifier{}, errors.New(
"x509: requested SignatureAlgorithm does not match private key type",
)
}
2022-03-29 16:20:28 +00:00
sigAlgo.Algorithm, hashFunc = details.oid, details.hash
if hashFunc == 0 {
2022-03-29 16:20:28 +00:00
return 0, pkix.AlgorithmIdentifier{}, errors.New(
"x509: cannot sign with hash function requested",
)
}
2022-03-29 16:20:28 +00:00
found = true
2022-03-29 16:20:28 +00:00
break
}
}
if !found {
2022-03-29 16:20:28 +00:00
return 0, pkix.AlgorithmIdentifier{}, errors.New("x509: unknown SignatureAlgorithm")
}
2022-03-29 16:20:28 +00:00
return hashFunc, sigAlgo, nil
}
// 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
}
}
2022-03-29 16:20:28 +00:00
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
}
}
2022-03-29 16:20:28 +00:00
return crypto.Hash(0)
}
func getOIDFromHashAlgorithm(target crypto.Hash) asn1.ObjectIdentifier {
for hash, oid := range hashOIDs {
if hash == target {
return oid
}
}
2022-03-29 16:20:28 +00:00
return nil
}
// 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
// Revoked means that the certificate has been deliberately revoked.
Revoked
// Unknown means that the OCSP responder doesn't know about the certificate.
Unknown
// 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.
2022-03-29 16:20:28 +00:00
// ServerFailed
)
// 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")
}
2022-03-29 16:20:28 +00:00
request, err := 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,
},
},
},
},
})
2022-03-29 16:20:28 +00:00
if err != nil {
return nil, fmt.Errorf("could not marshal OCSP request ASN.1: %w", err)
}
return request, nil
}
// Response represents an OCSP response containing a single SingleResponse. See
// RFC 6960.
type Response struct {
// 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
// SupportExtendedRevoke is used to notify the requester that extended revoke
// is supported as specified in
// https://www.rfc-editor.org/rfc/rfc6960.html#section-4.4.8
SupportExtendedRevoke bool
}
// 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.
2022-03-29 16:20:28 +00:00
// var (
// MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01}
// InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02}
// TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03}
// SigRequiredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05}
2022-03-29 16:20:28 +00:00
// 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 {
2022-03-29 16:20:28 +00:00
if err := issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature); err != nil {
return fmt.Errorf("could not check signature: %w", err)
}
return nil
}
// 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
2022-03-29 16:20:28 +00:00
rest, err := asn1.Unmarshal(bytes, &req)
if err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not unmarshal OCSP request ASN.1: %w", err)
}
2022-03-29 16:20:28 +00:00
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")
}
2022-03-29 16:20:28 +00:00
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.
2022-03-29 16:20:28 +00:00
//
//nolint:gocognit, cyclop
func ParseResponseForCert(bytes []byte, cert, issuer *x509.Certificate) (*Response, error) {
2022-03-29 16:20:28 +00:00
var resp ocspResponseASN1
rest, err := asn1.Unmarshal(bytes, &resp)
2022-03-29 16:20:28 +00:00
if err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not unmarshal OCSP response ASN.1: %w", err)
}
2022-03-29 16:20:28 +00:00
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
2022-03-29 16:20:28 +00:00
rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp)
if err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not unmarshal basic response ASN.1: %w", err)
}
2022-03-29 16:20:28 +00:00
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
2022-03-29 16:20:28 +00:00
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
2022-03-29 16:20:28 +00:00
break
}
}
if !match {
return nil, ParseError("no response matching the supplied certificate")
}
}
ret := &Response{
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
2022-03-29 16:20:28 +00:00
if rest, err := asn1.Unmarshal(rawResponderID.Bytes, &rdn); err != nil || len(rest) != 0 {
return nil, ParseError("invalid responder name")
}
2022-03-29 16:20:28 +00:00
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")
}
2022-03-29 16:20:28 +00:00
if len(basicResp.Certificates) > 0 { //nolint:nestif
// 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 {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not parse signer certificate: %w", err)
}
if err := ret.CheckSignatureFrom(ret.Certificate); err != nil {
return nil, ParseError("bad signature on embedded certificate: " + err.Error())
}
if issuer != nil {
2022-03-29 16:20:28 +00:00
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
2022-03-29 16:20:28 +00:00
break
}
}
2022-03-29 16:20:28 +00:00
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
}
2022-03-29 16:20:28 +00:00
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
}
2022-03-29 16:20:28 +00:00
h := opts.hash().New()
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
2022-03-29 16:20:28 +00:00
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not unmarshal public key info ASN.1: %w", 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,
}
2022-03-29 16:20:28 +00:00
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 puplate 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.
2022-03-29 16:20:28 +00:00
//
//nolint:cyclop
func CreateResponse(
issuer, responderCert *x509.Certificate,
template Response,
priv crypto.Signer,
) ([]byte, error) {
var publicKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}
2022-03-29 16:20:28 +00:00
if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not unmarshal public key info ASN.1: %w", err)
}
if template.IssuerHash == 0 {
template.IssuerHash = crypto.SHA1
}
2022-03-29 16:20:28 +00:00
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)
}
2022-03-29 16:20:28 +00:00
h := template.IssuerHash.New()
2022-03-29 16:20:28 +00:00
h.Write(publicKeyInfo.PublicKey.RightAlign())
issuerKeyHash := h.Sum(nil)
h.Reset()
h.Write(issuer.RawSubject)
2022-03-29 16:20:28 +00:00
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,
}
var extensions []pkix.Extension
if template.SupportExtendedRevoke {
extensions = append(
extensions,
pkix.Extension{Id: idPKIXOCSPExtendedRevoke, Value: asn1.NullBytes},
)
}
tbsResponseData := responseData{
Version: 0,
RawResponderID: rawResponderID,
ProducedAt: time.Now().Truncate(time.Minute).UTC(),
Responses: []singleResponse{innerResponse},
ResponseExtensions: extensions,
}
tbsResponseDataDER, err := asn1.Marshal(tbsResponseData)
if err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not marshal response data: %w", err)
}
hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm)
if err != nil {
return nil, err
}
responseHash := hashFunc.New()
responseHash.Write(tbsResponseDataDER)
2022-03-29 16:20:28 +00:00
signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc)
if err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("signing failed: %w", 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},
}
}
2022-03-29 16:20:28 +00:00
responseDER, err := asn1.Marshal(response)
if err != nil {
2022-03-29 16:20:28 +00:00
return nil, fmt.Errorf("could not marshal OCSP data ASN.1: %w", err)
}
2022-03-29 16:20:28 +00:00
responseStructureDER, err := asn1.Marshal(ocspResponseASN1{
Status: asn1.Enumerated(Success),
Response: responseBytes{
ResponseType: idPKIXOCSPBasic,
Response: responseDER,
},
})
2022-03-29 16:20:28 +00:00
if err != nil {
return nil, fmt.Errorf("could not marshal OSCP response ASN.1: %w", err)
}
return responseStructureDER, nil
}