blob: df9d19b6eea34436b1e65ceba8cfb360bf67d6a0 [file]
// Copyright 2026 The BoringSSL Authors
//
// 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
//
// https://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.
//go:build ignore
// This script is called without any arguments to re-generate all of the *.pem
// files in the script's directory.
package main
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/sha256"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"fmt"
"math/big"
"os"
"os/exec"
"time"
"golang.org/x/crypto/cryptobyte"
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
)
var (
nextSerial int64 = 1
certDate = time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
certExpire = time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC)
revokeDate = time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC)
thisDate = time.Date(2017, 3, 1, 0, 0, 0, 0, time.UTC)
producedDate = time.Date(2017, 3, 2, 0, 0, 0, 0, time.UTC)
verifyDate = time.Date(2017, 3, 5, 0, 0, 0, 0, time.UTC)
nextDate = time.Date(2017, 6, 1, 0, 0, 0, 0, time.UTC)
oidSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
oidSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
oidSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
oidBasicOCSPResponse = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}
oidCommonName = asn1.ObjectIdentifier{2, 5, 4, 3}
)
type certInfo struct {
der []byte
cert *x509.Certificate
key *rsa.PrivateKey
issuer *certInfo
serialBytes []byte
}
func makeNameDER(commonName string) []byte {
var b cryptobyte.Builder
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1(cbasn1.SET, func(b *cryptobyte.Builder) {
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1ObjectIdentifier(oidCommonName)
b.AddASN1(cbasn1.UTF8String, func(b *cryptobyte.Builder) {
b.AddBytes([]byte(commonName))
})
})
})
})
return b.BytesOrPanic()
}
func loadKey(path string) *rsa.PrivateKey {
data, err := os.ReadFile(path)
if err != nil {
panic(err)
}
block, _ := pem.Decode(data)
if block == nil || block.Type != "PRIVATE KEY" {
panic("failed to decode PEM key: " + path)
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
return key.(*rsa.PrivateKey)
}
type certOptions struct {
name string
keyPath string
signer *certInfo
ocsp bool
extraExtensions []pkix.Extension
notBefore time.Time
notAfter time.Time
}
func createCertWithOptions(opts certOptions) *certInfo {
privKey := loadKey(opts.keyPath)
subjectDER := makeNameDER(opts.name)
serial := nextSerial
nextSerial++
notBefore := opts.notBefore
if notBefore.IsZero() {
notBefore = certDate
}
notAfter := opts.notAfter
if notAfter.IsZero() {
notAfter = certExpire
}
tmpl := &x509.Certificate{
SerialNumber: big.NewInt(serial),
RawSubject: subjectDER,
NotBefore: notBefore,
NotAfter: notAfter,
SignatureAlgorithm: x509.SHA256WithRSA,
ExtraExtensions: opts.extraExtensions,
}
if opts.ocsp {
tmpl.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageOCSPSigning}
}
var issuerTmpl *x509.Certificate
var issuerKey *rsa.PrivateKey
if opts.signer != nil {
issuerTmpl = opts.signer.cert
issuerKey = opts.signer.key
} else {
issuerTmpl = tmpl
issuerKey = privKey
}
derBytes, err := x509.CreateCertificate(rand.Reader, tmpl, issuerTmpl, &privKey.PublicKey, issuerKey)
if err != nil {
panic(err)
}
parsedCert, err := x509.ParseCertificate(derBytes)
if err != nil {
panic(err)
}
info := &certInfo{
der: derBytes,
cert: parsedCert,
key: privKey,
serialBytes: []byte{byte(serial)},
}
if opts.signer != nil {
info.issuer = opts.signer
} else {
info.issuer = info
}
return info
}
func createCert(name, keyPath string, signer *certInfo, ocsp bool) *certInfo {
return createCertWithOptions(certOptions{
name: name,
keyPath: keyPath,
signer: signer,
ocsp: ocsp,
})
}
// createCertWithRawSerial creates a copy of baseCert with its serialNumber INTEGER
// replaced by rawSerialBytes, and re-signs the certificate with its issuer's key.
func createCertWithRawSerial(baseCert *certInfo, rawSerialBytes []byte) *certInfo {
s := cryptobyte.String(baseCert.der)
var certSeq, tbsSeq cryptobyte.String
if !s.ReadASN1(&certSeq, cbasn1.SEQUENCE) ||
!certSeq.ReadASN1(&tbsSeq, cbasn1.SEQUENCE) {
panic("failed to parse Certificate DER")
}
// Read version [0] EXPLICIT TLV raw bytes
var versionTLV cryptobyte.String
if !tbsSeq.ReadASN1Element(&versionTLV, cbasn1.Tag(0).ContextSpecific().Constructed()) {
panic("failed to read version from tbsCertificate")
}
// Skip original serialNumber INTEGER
var oldSerial cryptobyte.String
if !tbsSeq.ReadASN1(&oldSerial, cbasn1.INTEGER) {
panic("failed to read serialNumber from tbsCertificate")
}
restOfTBS := []byte(tbsSeq)
var newTBSBuilder cryptobyte.Builder
newTBSBuilder.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddBytes(versionTLV)
b.AddASN1(cbasn1.INTEGER, func(b *cryptobyte.Builder) {
b.AddBytes(rawSerialBytes)
})
b.AddBytes(restOfTBS)
})
newTBS := newTBSBuilder.BytesOrPanic()
digest := sha256.Sum256(newTBS)
sig, err := rsa.SignPKCS1v15(nil, baseCert.issuer.key, crypto.SHA256, digest[:])
if err != nil {
panic(err)
}
var newCertBuilder cryptobyte.Builder
newCertBuilder.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddBytes(newTBS)
b.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1ObjectIdentifier(oidSHA256WithRSA)
b.AddASN1NULL()
})
b.AddASN1BitString(sig)
})
newDER := newCertBuilder.BytesOrPanic()
return &certInfo{
der: newDER,
cert: baseCert.cert,
key: baseCert.key,
issuer: baseCert.issuer,
serialBytes: rawSerialBytes,
}
}
func extractSPKBitStringBytes(spkiDER []byte) []byte {
s := cryptobyte.String(spkiDER)
var seq, alg cryptobyte.String
var spk asn1.BitString
if !s.ReadASN1(&seq, cbasn1.SEQUENCE) ||
!seq.ReadASN1(&alg, cbasn1.SEQUENCE) ||
!seq.ReadASN1BitString(&spk) {
panic("failed to parse SubjectPublicKeyInfo")
}
return spk.Bytes
}
type responderID struct {
byName []byte
byKey []byte
}
func getNameResponderID(c *certInfo) responderID {
return responderID{byName: c.cert.RawSubject}
}
func getKeyHashResponderID(c *certInfo) responderID {
spkBytes := extractSPKBitStringBytes(c.cert.RawSubjectPublicKeyInfo)
keyHash := sha1.Sum(spkBytes)
return responderID{byKey: keyHash[:]}
}
func addExtensions(b *cryptobyte.Builder, tag uint8, exts []pkix.Extension) {
if len(exts) == 0 {
return
}
b.AddASN1(cbasn1.Tag(tag).ContextSpecific().Constructed(), func(child *cryptobyte.Builder) {
child.AddASN1(cbasn1.SEQUENCE, func(extsB *cryptobyte.Builder) {
for _, ext := range exts {
extsB.AddASN1(cbasn1.SEQUENCE, func(extB *cryptobyte.Builder) {
extB.AddASN1ObjectIdentifier(ext.Id)
if ext.Critical {
extB.AddASN1(cbasn1.BOOLEAN, func(crit *cryptobyte.Builder) {
crit.AddUint8(0xff)
})
}
extB.AddASN1(cbasn1.OCTET_STRING, func(value *cryptobyte.Builder) {
value.AddBytes(ext.Value)
})
})
}
})
})
}
type certStatus int
const (
statusGood certStatus = 0
statusRevoked certStatus = 1
statusUnknown certStatus = 2
)
type singleResponse struct {
cert *certInfo
status certStatus
reason *int
thisUpdate time.Time
nextUpdate *time.Time
revokeTime *time.Time
extensions []pkix.Extension
// trailingDataInStatus causes the CertStatus to include extra data
// in its contents.
trailingDataInStatus bool
}
func marshalSingleResponse(b *cryptobyte.Builder, resp singleResponse) {
issuer := resp.cert.issuer
nameHash := sha1.Sum(issuer.cert.RawSubject)
keyHash := sha1.Sum(extractSPKBitStringBytes(issuer.cert.RawSubjectPublicKeyInfo))
b.AddASN1(cbasn1.SEQUENCE, func(respB *cryptobyte.Builder) {
respB.AddASN1(cbasn1.SEQUENCE, func(certID *cryptobyte.Builder) {
certID.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1ObjectIdentifier(oidSHA1)
})
certID.AddASN1(cbasn1.OCTET_STRING, func(b *cryptobyte.Builder) {
b.AddBytes(nameHash[:])
})
certID.AddASN1(cbasn1.OCTET_STRING, func(b *cryptobyte.Builder) {
b.AddBytes(keyHash[:])
})
certID.AddASN1(cbasn1.INTEGER, func(b *cryptobyte.Builder) {
b.AddBytes(resp.cert.serialBytes)
})
})
switch resp.status {
case statusGood:
respB.AddASN1(cbasn1.Tag(0).ContextSpecific(), func(b *cryptobyte.Builder) {
if resp.trailingDataInStatus {
b.AddBytes([]byte("invalid"))
}
})
case statusRevoked:
revTime := revokeDate
if resp.revokeTime != nil {
revTime = *resp.revokeTime
}
respB.AddASN1(cbasn1.Tag(1).ContextSpecific().Constructed(), func(revInfo *cryptobyte.Builder) {
revInfo.AddASN1GeneralizedTime(revTime)
if resp.reason != nil {
revInfo.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(child *cryptobyte.Builder) {
child.AddASN1Enum(int64(*resp.reason))
})
}
if resp.trailingDataInStatus {
revInfo.AddBytes([]byte("invalid"))
}
})
case statusUnknown:
respB.AddASN1(cbasn1.Tag(2).ContextSpecific(), func(b *cryptobyte.Builder) {
if resp.trailingDataInStatus {
b.AddBytes([]byte("invalid"))
}
})
}
thisUpdate := resp.thisUpdate
if thisUpdate.IsZero() {
thisUpdate = thisDate
}
respB.AddASN1GeneralizedTime(thisUpdate)
// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL
if resp.nextUpdate != nil {
respB.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
b.AddASN1GeneralizedTime(*resp.nextUpdate)
})
}
// singleExtensions [1] EXPLICIT Extensions OPTIONAL
addExtensions(respB, 1, resp.extensions)
})
}
const (
responseStatusSuccessful = 0
responseStatusMalformedRequest = 1
responseStatusInternalError = 2
responseStatusTryLater = 3
// 4 is not allocated.
responseStatusSigRequired = 5
responseStatusUnauthorized = 6
)
type ocspResponse struct {
signer *certInfo
responseStatus int
// responseType, if set, overrides the responseType from id-pkix-ocsp-basic
responseType asn1.ObjectIdentifier
// version, if set, explicitly encodes a version number.
version *int
responderID responderID
producedAt time.Time
responses []singleResponse
extensions []pkix.Extension
sigAlg x509.SignatureAlgorithm
// signature, if set, overrides the signature.
signature []byte
certs []*certInfo
// invalidResponseData, if true, causes the tbsResponseData to be invalid.
invalidResponseData bool
}
func marshalBasicOCSPResponse(b *cryptobyte.Builder, resp *ocspResponse) {
sigAlg := resp.sigAlg
if sigAlg == 0 {
sigAlg = x509.SHA1WithRSA
}
var sigAlgOID asn1.ObjectIdentifier
var hashFunc crypto.Hash
switch sigAlg {
case x509.SHA1WithRSA:
sigAlgOID = oidSHA1WithRSA
hashFunc = crypto.SHA1
case x509.SHA256WithRSA:
sigAlgOID = oidSHA256WithRSA
hashFunc = crypto.SHA256
default:
panic(fmt.Sprintf("unrecognized signature algorithm: %s", sigAlg))
}
var tbsBuilder cryptobyte.Builder
if resp.invalidResponseData {
tbsBuilder.AddASN1(cbasn1.OCTET_STRING, func(b *cryptobyte.Builder) {
b.AddBytes([]byte("invalid"))
})
} else {
tbsBuilder.AddASN1(cbasn1.SEQUENCE, func(respData *cryptobyte.Builder) {
if resp.version != nil {
respData.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
b.AddASN1Int64(int64(*resp.version))
})
}
responderID := resp.responderID
if responderID.byKey != nil {
respData.AddASN1(cbasn1.Tag(2).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
b.AddASN1(cbasn1.OCTET_STRING, func(b *cryptobyte.Builder) {
b.AddBytes(responderID.byKey)
})
})
} else {
respData.AddASN1(cbasn1.Tag(1).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
if responderID.byName != nil {
b.AddBytes(responderID.byName)
} else {
// Default to the signer.
b.AddBytes(resp.signer.cert.RawSubject)
}
})
}
producedAt := resp.producedAt
if producedAt.IsZero() {
producedAt = producedDate
}
respData.AddASN1GeneralizedTime(producedAt)
respData.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
for _, r := range resp.responses {
marshalSingleResponse(b, r)
}
})
addExtensions(respData, 1, resp.extensions)
})
}
tbsBytes := tbsBuilder.BytesOrPanic()
b.AddASN1(cbasn1.SEQUENCE, func(basicResp *cryptobyte.Builder) {
basicResp.AddBytes(tbsBytes)
basicResp.AddASN1(cbasn1.SEQUENCE, func(b *cryptobyte.Builder) {
b.AddASN1ObjectIdentifier(sigAlgOID)
b.AddASN1NULL()
})
signature := resp.signature
if signature == nil {
h := hashFunc.New()
h.Write(tbsBytes)
var err error
signature, err = rsa.SignPKCS1v15(nil, resp.signer.key, hashFunc, h.Sum(nil))
if err != nil {
panic(err)
}
}
basicResp.AddASN1BitString(signature)
if len(resp.certs) > 0 {
basicResp.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(certsWrapper *cryptobyte.Builder) {
certsWrapper.AddASN1(cbasn1.SEQUENCE, func(certs *cryptobyte.Builder) {
for _, c := range resp.certs {
certs.AddBytes(c.der)
}
})
})
}
})
}
func createOCSPResponse(resp *ocspResponse) []byte {
var b cryptobyte.Builder
b.AddASN1(cbasn1.SEQUENCE, func(respB *cryptobyte.Builder) {
respB.AddASN1Enum(int64(resp.responseStatus))
if resp.responseStatus != responseStatusSuccessful {
return
}
respB.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(child *cryptobyte.Builder) {
child.AddASN1(cbasn1.SEQUENCE, func(respBytes *cryptobyte.Builder) {
responseType := resp.responseType
if len(responseType) == 0 {
responseType = oidBasicOCSPResponse
}
respBytes.AddASN1ObjectIdentifier(responseType)
respBytes.AddASN1(cbasn1.OCTET_STRING, func(respWrapper *cryptobyte.Builder) {
marshalBasicOCSPResponse(respWrapper, resp)
})
})
})
})
return b.BytesOrPanic()
}
func createOCSPRequest(issuer, cert *certInfo) []byte {
nameHash := sha1.Sum(issuer.cert.RawSubject)
keyHash := sha1.Sum(extractSPKBitStringBytes(issuer.cert.RawSubjectPublicKeyInfo))
var b cryptobyte.Builder
b.AddASN1(cbasn1.SEQUENCE, func(ocspReq *cryptobyte.Builder) {
ocspReq.AddASN1(cbasn1.SEQUENCE, func(tbsReq *cryptobyte.Builder) {
tbsReq.AddASN1(cbasn1.SEQUENCE, func(reqList *cryptobyte.Builder) {
reqList.AddASN1(cbasn1.SEQUENCE, func(req *cryptobyte.Builder) {
req.AddASN1(cbasn1.SEQUENCE, func(reqCert *cryptobyte.Builder) {
reqCert.AddASN1(cbasn1.SEQUENCE, func(hashAlg *cryptobyte.Builder) {
hashAlg.AddASN1ObjectIdentifier(oidSHA1)
hashAlg.AddASN1NULL()
})
reqCert.AddASN1(cbasn1.OCTET_STRING, func(child *cryptobyte.Builder) {
child.AddBytes(nameHash[:])
})
reqCert.AddASN1(cbasn1.OCTET_STRING, func(child *cryptobyte.Builder) {
child.AddBytes(keyHash[:])
})
reqCert.AddASN1(cbasn1.INTEGER, func(child *cryptobyte.Builder) {
child.AddBytes(cert.serialBytes)
})
})
})
})
})
})
return b.BytesOrPanic()
}
func store(filename, description string, ca, cert *certInfo, dataDER []byte) {
ocspRequestDER := createOCSPRequest(ca, cert)
var buf bytes.Buffer
buf.WriteString(description)
buf.WriteString("\n")
if err := pem.Encode(&buf, &pem.Block{Type: "OCSP RESPONSE", Bytes: dataDER}); err != nil {
panic(err)
}
if err := pem.Encode(&buf, &pem.Block{Type: "CA CERTIFICATE", Bytes: ca.der}); err != nil {
panic(err)
}
buf.WriteString("\n")
if err := pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: cert.der}); err != nil {
panic(err)
}
buf.WriteString("\n")
if err := pem.Encode(&buf, &pem.Block{Type: "OCSP REQUEST", Bytes: ocspRequestDER}); err != nil {
panic(err)
}
if err := os.WriteFile(filename+".pem", buf.Bytes(), 0644); err != nil {
panic(err)
}
}
func main() {
rootCA := createCert("Test CA", "root.key", nil, false)
ca := createCert("Test Intermediate CA", "intermediate.key", rootCA, false)
caLink := createCert("Test OCSP Signer", "ocsp_signer.key", ca, true)
caBadLink := createCert("Test False OCSP Signer", "bad_ocsp_signer.key", ca, false)
cert := createCert("Test Cert", "cert.key", ca, false)
junkCert := createCert("Random Cert", "cert2.key", nil, false)
caCriticalExtLink := createCertWithOptions(certOptions{
name: "Test OCSP Signer",
keyPath: "ocsp_signer.key",
signer: ca,
ocsp: true,
extraExtensions: []pkix.Extension{
{Id: asn1.ObjectIdentifier{1, 2, 3, 4}, Critical: true, Value: []byte("DEADBEEF")},
},
})
ca2 := createCert("Test Intermediate CA 2", "intermediate.key", rootCA, false)
ca2Link := createCert("Test OCSP Signer", "ocsp_signer.key", ca2, true)
caExpiredLink := createCertWithOptions(certOptions{
name: "Test OCSP Signer",
keyPath: "ocsp_signer.key",
signer: ca,
ocsp: true,
notBefore: certDate,
notAfter: verifyDate.Add(-1 * 24 * time.Hour),
})
create := func(resp ocspResponse) []byte {
if resp.signer == nil {
resp.signer = ca
}
if resp.responses == nil {
resp.responses = []singleResponse{{
cert: cert,
status: statusGood,
}}
}
return createOCSPResponse(&resp)
}
store(
"no_response",
"No SingleResponses attached to the response",
ca, cert,
create(ocspResponse{responses: []singleResponse{}}),
)
store(
"malformed_request",
"Has a status of MALFORMED_REQUEST",
ca, cert,
create(ocspResponse{responseStatus: responseStatusMalformedRequest}),
)
store(
"bad_status",
"Has an invalid status larger than the defined Status enumeration",
ca, cert,
create(ocspResponse{responseStatus: 17}),
)
store(
"bad_ocsp_type",
"Has an invalid OCSP OID",
ca, cert,
create(ocspResponse{responseType: asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 2}}),
)
store(
"bad_signature",
"Has an invalid signature",
ca, cert,
create(ocspResponse{signature: []byte{0xde, 0xad, 0xbe, 0xef}}),
)
store(
"ocsp_sign_direct",
"Signed directly by the issuer",
ca, cert,
create(ocspResponse{signer: ca, certs: []*certInfo{}}),
)
store(
"ocsp_sign_indirect",
"Signed indirectly through an intermediate",
ca, cert,
create(ocspResponse{signer: caLink, certs: []*certInfo{caLink}}),
)
store(
"ocsp_sign_indirect_missing",
"Signed indirectly through a missing intermediate",
ca, cert,
create(ocspResponse{signer: caLink, certs: []*certInfo{}}),
)
store(
"ocsp_sign_bad_indirect",
"Signed through an intermediate without the correct key usage",
ca, cert,
create(ocspResponse{signer: caBadLink, certs: []*certInfo{caBadLink}}),
)
store(
"ocsp_sign_bad_indirect_critical_extension",
"Signed through an intermediate with an unknown critical extension",
ca, cert,
create(ocspResponse{signer: caCriticalExtLink, certs: []*certInfo{caCriticalExtLink}}),
)
store(
"ocsp_sign_bad_indirect_wrong_issuer",
"Signed through an intermediate issued by a differently named CA with the same key",
ca, cert,
create(ocspResponse{signer: ca2Link, certs: []*certInfo{ca2Link}}),
)
store(
"ocsp_sign_bad_indirect_expired",
"Signed through an intermediate that is not valid at the verification time",
ca, cert,
create(ocspResponse{signer: caExpiredLink, certs: []*certInfo{caExpiredLink}}),
)
store(
"ocsp_extra_certs",
"Includes extra certs",
ca, cert,
create(ocspResponse{signer: ca, certs: []*certInfo{ca, caLink}}),
)
store(
"has_version",
"Includes a default version V1",
ca, cert,
// v1 is encoded with value zero.
create(ocspResponse{version: new(0)}),
)
store(
"responder_name",
"Uses byName to identify the signer",
ca, cert,
create(ocspResponse{responderID: getNameResponderID(ca)}),
)
store(
"responder_id",
"Uses byKey to identify the signer",
ca, cert,
create(ocspResponse{responderID: getKeyHashResponderID(ca)}),
)
store(
"has_extension",
"Includes an x509v3 extension",
ca, cert,
create(ocspResponse{
extensions: []pkix.Extension{
{Id: asn1.ObjectIdentifier{1, 2, 3, 4}, Value: []byte("DEADBEEF")},
},
}),
)
store(
"good_response",
"Is a valid response for the cert",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
},
}),
)
store(
"good_response_sha256",
"Is a valid response for the cert with a SHA256 signature",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
},
sigAlg: x509.SHA256WithRSA,
}),
)
store(
"good_response_next_update",
"Is a valid response for the cert until nextUpdate",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
nextUpdate: new(nextDate),
},
},
}),
)
store(
"revoke_response",
"Is a REVOKE response for the cert",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusRevoked},
},
}),
)
store(
"revoke_response_reason",
"Is a REVOKE response for the cert with a reason",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusRevoked,
revokeTime: new(revokeDate),
reason: new(1), // keyCompromise
},
},
}),
)
store(
"unknown_response",
"Is an UNKNOWN response for the cert",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusUnknown},
},
}),
)
store(
"multiple_response",
"Has multiple responses for the cert",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
{cert: cert, status: statusUnknown},
},
}),
)
store(
"other_response",
"Is a response for a different cert",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: junkCert, status: statusGood},
{cert: junkCert, status: statusRevoked},
},
}),
)
store(
"has_single_extension",
"Has an extension in the SingleResponse",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
extensions: []pkix.Extension{
{Id: asn1.ObjectIdentifier{1, 2, 3, 4}, Value: []byte("DEADBEEF")},
},
},
},
}),
)
store(
"has_critical_single_extension",
"Has a critical extension in the SingleResponse",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
extensions: []pkix.Extension{
{Id: asn1.ObjectIdentifier{1, 2, 3, 4}, Critical: true, Value: []byte("DEADBEEF")},
},
},
},
}),
)
store(
"has_critical_response_extension",
"Has a critical extension in the ResponseData",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
},
extensions: []pkix.Extension{
{Id: asn1.ObjectIdentifier{1, 2, 3, 4}, Critical: true, Value: []byte("DEADBEEF")},
},
}),
)
store(
"has_critical_ct_extension",
"Has a critical CT extension in the SingleResponse",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
extensions: []pkix.Extension{
{Id: asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 5}, Critical: true, Value: []byte("DEADBEEF")},
},
},
},
}),
)
store(
"missing_response",
"Missing a response for the cert",
ca, cert,
create(ocspResponse{responseStatus: responseStatusSuccessful, responses: []singleResponse{}}),
)
store(
"stale_response",
"nextUpdate is before the current time",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
thisUpdate: verifyDate.Add(-2 * 24 * time.Hour),
nextUpdate: new(verifyDate.Add(-1 * 24 * time.Hour)),
},
},
}),
)
store(
"future_response",
"thisUpdate is after the current time",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
thisUpdate: verifyDate.Add(1 * 24 * time.Hour),
},
},
}),
)
store(
"old_response",
"thisUpdate is over a week before the current time",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{
cert: cert,
status: statusGood,
thisUpdate: verifyDate.Add(-8 * 24 * time.Hour),
},
},
}),
)
store(
"produced_early_response",
"producedAt is before the cert's notBefore",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
},
producedAt: certDate.Add(-1 * 24 * time.Hour),
}),
)
store(
"produced_late_response",
"producedAt is after the cert's notAfter",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
},
producedAt: certExpire.Add(1 * 24 * time.Hour),
}),
)
store(
"invalid_response",
"OCSPResponse cannot be parsed",
ca, cert,
[]byte("invalid"),
)
store(
"invalid_response_data",
"ResponseData cannot be parsed",
ca, cert,
create(ocspResponse{invalidResponseData: true}),
)
store(
"multiple_response_good_revoked",
"Has both a good and a revoked response for the cert",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood},
{cert: cert, status: statusRevoked},
},
}),
)
certInvalidSerial := createCertWithRawSerial(cert, []byte{0x00, 0x05})
store(
"good_response_invalid_serial",
`Is a valid response for the cert, but the cert has an incorrectly encoded
serial number. Until we fix the cert parser (https://crbug.com/533048005), it
is prudent (though not critical) for every acceptable serial number to be
representable in OCSP.`,
ca, certInvalidSerial,
create(ocspResponse{
responses: []singleResponse{
{cert: certInvalidSerial, status: statusGood},
},
}),
)
store(
"good_response_invalid_status",
"Is a good response but the status is encoded wrong",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusGood, trailingDataInStatus: true},
},
}),
)
store(
"revoke_response_invalid_status",
"Is a revoked response but the status is encoded wrong",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusRevoked, trailingDataInStatus: true},
},
}),
)
store(
"unknown_response_invalid_status",
"Is an unknown response but the status is encoded wrong",
ca, cert,
create(ocspResponse{
responses: []singleResponse{
{cert: cert, status: statusUnknown, trailingDataInStatus: true},
},
}),
)
cmd := exec.Command("python3", "annotate_test_data.py")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}