Unit tests that validate OCSP signatures leverage revocation signature algo (#17452)

- Add some unit tests around the OCSP response validation that we
   are using the proper signature algorithms.
 - Add in test cases as well to validate SHA384 and SHA512 requested hash support
This commit is contained in:
Steven Clark 2022-10-07 12:33:17 -04:00 committed by GitHub
parent 1f0cf558a9
commit f0bf670b0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 55 deletions

View File

@ -364,6 +364,7 @@ func lookupIssuerIds(sc *storageContext, optRevokedIssuer issuerID) ([]issuerID,
}
func doesRequestMatchIssuer(parsedBundle *certutil.ParsedCertBundle, req *ocsp.Request) (bool, error) {
// issuer name hashing taken from golang.org/x/crypto/ocsp.
var pkInfo struct {
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
@ -390,7 +391,7 @@ func genResponse(cfg *crlConfig, caBundle *certutil.ParsedCertBundle, info *ocsp
return nil, err
}
// x/ocsp lives outside of the standard library's crypto/x509 and includes
// x/crypto/ocsp lives outside of the standard library's crypto/x509 and includes
// ripped-off variants of many internal structures and functions. These
// lack support for PSS signatures altogether, so if we have revSigAlg
// that uses PSS, downgrade it to PKCS#1v1.5. This fixes the lack of

View File

@ -4,10 +4,6 @@ import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"fmt"
@ -362,40 +358,56 @@ func TestOcsp_MultipleMatchingIssuersOneWithoutSigningUsage(t *testing.T) {
require.Equal(t, testEnv.leafCertIssuer1.SerialNumber, ocspResp.SerialNumber)
require.Equal(t, rotatedCert, ocspResp.Certificate)
requireOcspSignatureAlgoForKey(t, rotatedCert.PublicKey, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, rotatedCert.PublicKey)
requireOcspSignatureAlgoForKey(t, rotatedCert.SignatureAlgorithm, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, rotatedCert)
}
func TestOcsp_ValidRequests(t *testing.T) {
type caKeyConf struct {
keyType string
keyBits int
sigBits int
}
t.Parallel()
type testArgs struct {
reqType string
caKeyType string
reqHash crypto.Hash
reqType string
keyConf caKeyConf
reqHash crypto.Hash
}
var tests []testArgs
for _, reqType := range []string{"get", "post"} {
for _, caKeyType := range []string{"rsa", "ec"} { // "ed25519" is not supported at the moment in x/crypto/ocsp
for _, requestHash := range []crypto.Hash{crypto.SHA1, crypto.SHA256} {
for _, keyConf := range []caKeyConf{
{"rsa", 0, 0},
{"rsa", 0, 384},
{"rsa", 0, 512},
{"ec", 0, 0},
{"ec", 521, 0},
} {
// "ed25519" is not supported at the moment in x/crypto/ocsp
for _, requestHash := range []crypto.Hash{crypto.SHA1, crypto.SHA256, crypto.SHA384, crypto.SHA512} {
tests = append(tests, testArgs{
reqType: reqType,
caKeyType: caKeyType,
reqHash: requestHash,
reqType: reqType,
keyConf: keyConf,
reqHash: requestHash,
})
}
}
}
for _, tt := range tests {
localTT := tt
testName := fmt.Sprintf("%s-%s-%s", localTT.reqType, localTT.caKeyType, localTT.reqHash)
testName := fmt.Sprintf("%s-%s-keybits-%d-sigbits-%d-reqHash-%s", localTT.reqType, localTT.keyConf.keyType,
localTT.keyConf.keyBits,
localTT.keyConf.sigBits,
localTT.reqHash)
t.Run(testName, func(t *testing.T) {
runOcspRequestTest(t, localTT.reqType, localTT.caKeyType, localTT.reqHash)
runOcspRequestTest(t, localTT.reqType, localTT.keyConf.keyType, localTT.keyConf.keyBits,
localTT.keyConf.sigBits, localTT.reqHash)
})
}
}
func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, requestHash crypto.Hash) {
b, s, testEnv := setupOcspEnv(t, caKeyType)
func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, caKeyBits int, caKeySigBits int, requestHash crypto.Hash) {
b, s, testEnv := setupOcspEnvWithCaKeyConfig(t, caKeyType, caKeyBits, caKeySigBits)
// Non-revoked cert
resp, err := sendOcspRequest(t, b, s, requestType, testEnv.leafCertIssuer1, testEnv.issuer1, requestHash)
@ -414,8 +426,8 @@ func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, requ
require.Equal(t, 0, ocspResp.RevocationReason)
require.Equal(t, testEnv.leafCertIssuer1.SerialNumber, ocspResp.SerialNumber)
requireOcspSignatureAlgoForKey(t, testEnv.issuer1.PublicKey, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer1.PublicKey)
requireOcspSignatureAlgoForKey(t, testEnv.issuer1.SignatureAlgorithm, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer1)
// Now revoke it
resp, err = CBWrite(b, s, "revoke", map[string]interface{}{
@ -439,8 +451,8 @@ func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, requ
require.Equal(t, 0, ocspResp.RevocationReason)
require.Equal(t, testEnv.leafCertIssuer1.SerialNumber, ocspResp.SerialNumber)
requireOcspSignatureAlgoForKey(t, testEnv.issuer1.PublicKey, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer1.PublicKey)
requireOcspSignatureAlgoForKey(t, testEnv.issuer1.SignatureAlgorithm, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer1)
// Request status for our second issuer
resp, err = sendOcspRequest(t, b, s, requestType, testEnv.leafCertIssuer2, testEnv.issuer2, requestHash)
@ -471,21 +483,12 @@ func runOcspRequestTest(t *testing.T, requestType string, caKeyType string, requ
fmt.Sprintf("the delta between thisUpdate %s and nextUpdate: %s should have been around: %s but was %s",
thisUpdate, nextUpdate, defaultCrlConfig.OcspExpiry, nextUpdateDiff))
requireOcspSignatureAlgoForKey(t, testEnv.issuer2.PublicKey, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer2.PublicKey)
requireOcspSignatureAlgoForKey(t, testEnv.issuer2.SignatureAlgorithm, ocspResp.SignatureAlgorithm)
requireOcspResponseSignedBy(t, ocspResp, testEnv.issuer2)
}
func requireOcspSignatureAlgoForKey(t *testing.T, key crypto.PublicKey, algorithm x509.SignatureAlgorithm) {
switch key.(type) {
case *rsa.PublicKey:
require.Equal(t, x509.SHA256WithRSA, algorithm)
case *ecdsa.PublicKey:
require.Equal(t, x509.ECDSAWithSHA256, algorithm)
case ed25519.PublicKey:
require.Equal(t, x509.PureEd25519, algorithm)
default:
t.Fatalf("unsupported public key type %T", key)
}
func requireOcspSignatureAlgoForKey(t *testing.T, expected x509.SignatureAlgorithm, actual x509.SignatureAlgorithm) {
require.Equal(t, expected.String(), actual.String())
}
type ocspTestEnv struct {
@ -503,6 +506,10 @@ type ocspTestEnv struct {
}
func setupOcspEnv(t *testing.T, keyType string) (*backend, logical.Storage, *ocspTestEnv) {
return setupOcspEnvWithCaKeyConfig(t, keyType, 0, 0)
}
func setupOcspEnvWithCaKeyConfig(t *testing.T, keyType string, caKeyBits int, caKeySigBits int) (*backend, logical.Storage, *ocspTestEnv) {
b, s := createBackendWithStorage(t)
var issuerCerts []*x509.Certificate
var leafCerts []*x509.Certificate
@ -511,9 +518,11 @@ func setupOcspEnv(t *testing.T, keyType string) (*backend, logical.Storage, *ocs
for i := 0; i < 2; i++ {
resp, err := CBWrite(b, s, "root/generate/internal", map[string]interface{}{
"key_type": keyType,
"ttl": "40h",
"common_name": "example-ocsp.com",
"key_type": keyType,
"key_bits": caKeyBits,
"signature_bits": caKeySigBits,
"ttl": "40h",
"common_name": "example-ocsp.com",
})
requireSuccessNonNilResponse(t, resp, err, "root/generate/internal")
requireFieldsSetInResp(t, resp, "issuer_id", "key_id")
@ -601,20 +610,7 @@ func generateRequest(t *testing.T, requestHash crypto.Hash, cert *x509.Certifica
return ocspRequestDer
}
func requireOcspResponseSignedBy(t *testing.T, ocspResp *ocsp.Response, key crypto.PublicKey) {
require.Contains(t, []x509.SignatureAlgorithm{x509.SHA256WithRSA, x509.ECDSAWithSHA256}, ocspResp.SignatureAlgorithm)
hasher := sha256.New()
hashAlgo := crypto.SHA256
hasher.Write(ocspResp.TBSResponseData)
hashData := hasher.Sum(nil)
switch typedKey := key.(type) {
case *rsa.PublicKey:
err := rsa.VerifyPKCS1v15(typedKey, hashAlgo, hashData, ocspResp.Signature)
require.NoError(t, err, "the ocsp response was not signed by the expected public rsa key.")
case *ecdsa.PublicKey:
verify := ecdsa.VerifyASN1(typedKey, hashData, ocspResp.Signature)
require.True(t, verify, "the certificate was not signed by the expected public ecdsa key.")
}
func requireOcspResponseSignedBy(t *testing.T, ocspResp *ocsp.Response, issuer *x509.Certificate) {
err := ocspResp.CheckSignatureFrom(issuer)
require.NoError(t, err, "Failed signature verification of ocsp response: %w", err)
}