ocsp: add tests

pull/8408/head
Marco Oliverio 2025-01-31 18:33:34 +00:00
parent 3a3238eb9f
commit 2fe413d80f
10 changed files with 2079 additions and 1 deletions

View File

@ -2511,6 +2511,7 @@ if(WOLFSSL_EXAMPLES)
tests/api/test_ripemd.c
tests/api/test_hash.c
tests/api/test_ascon.c
tests/api/test_ocsp.c
tests/hash.c
tests/srp.c
tests/suites.c

View File

@ -36,4 +36,5 @@ EXTRA_DIST += \
certs/ocsp/test-response.der \
certs/ocsp/test-response-rsapss.der \
certs/ocsp/test-response-nointern.der \
certs/ocsp/test-multi-response.der
certs/ocsp/test-multi-response.der \
certs/ocsp/test-leaf-response.der

View File

@ -100,6 +100,16 @@ openssl ocsp -issuer ./root-ca-cert.pem -cert ./intermediate1-ca-cert.pem -cert
kill $PID
wait $PID
# Create a response DER buffer for testing leaf certificate
openssl ocsp -port 22221 -ndays 1000 -index \
./index-intermediate1-ca-issued-certs.txt -rsigner ocsp-responder-cert.pem \
-rkey ocsp-responder-key.pem -CA intermediate1-ca-cert.pem -partial_chain &
PID=$!
sleep 1 # Make sure server is ready
openssl ocsp -issuer ./intermediate1-ca-cert.pem -cert ./server1-cert.pem -url http://localhost:22221/ -respout test-leaf-response.der -noverify
kill $PID
wait $PID
# now start up a responder that signs using rsa-pss
openssl ocsp -port 22221 -ndays 1000 -index index-ca-and-intermediate-cas.txt -rsigner ocsp-responder-cert.pem -rkey ocsp-responder-key.pem -CA root-ca-cert.pem -rsigopt rsa_padding_mode:pss &

Binary file not shown.

View File

@ -301,6 +301,7 @@
#include <tests/api/test_hash.h>
#include <tests/api/test_ascon.h>
#include <tests/api/test_dtls.h>
#include <tests/api/test_ocsp.h>
#if !defined(NO_FILESYSTEM) && !defined(NO_CERTS) && !defined(NO_TLS) && \
!defined(NO_RSA) && !defined(SINGLE_THREADED) && \
@ -99372,6 +99373,9 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wolfSSL_SSLDisableRead),
TEST_DECL(test_wolfSSL_inject),
TEST_DECL(test_wolfSSL_dtls_cid_parse),
TEST_DECL(test_ocsp_status_callback),
TEST_DECL(test_ocsp_basic_verify),
TEST_DECL(test_ocsp_response_parsing),
/* This test needs to stay at the end to clean up any caches allocated. */
TEST_DECL(test_wolfSSL_Cleanup)
};

View File

@ -0,0 +1,415 @@
#!/usr/bin/env python3
"""
This is a simple generator of OCSP responses that will be used to test
wolfSSL OCSP implementation
"""
from pyasn1_modules import rfc6960
from pyasn1.codec.der.encoder import encode
from pyasn1.codec.der.decoder import decode
from pyasn1.type import univ, tag, useful, namedtype
from base64 import b64decode
from hashlib import sha1, sha256
from datetime import datetime
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography import x509
from cryptography.hazmat.backends import default_backend
WOLFSSL_OCSP_CERT_PATH = './certs/ocsp/'
def response_status(value: int) -> rfc6960.OCSPResponseStatus:
return rfc6960.OCSPResponseStatus(value)
def response_type() -> univ.ObjectIdentifier:
return rfc6960.id_pkix_ocsp_basic
sha256WithRSAEncryption = (1, 2, 840, 113549, 1, 1, 11)
sha1_alg_id = (1, 3, 14, 3, 2, 26)
def cert_id_sha1_alg_id() -> rfc6960.AlgorithmIdentifier:
return algorithm(sha1_alg_id)
def signature_algorithm() -> rfc6960.AlgorithmIdentifier:
return algorithm(sha256WithRSAEncryption)
def algorithm(value) -> rfc6960.AlgorithmIdentifier:
ai = rfc6960.AlgorithmIdentifier()
ai['algorithm'] = univ.ObjectIdentifier(value=value)
return ai
def cert_pem_to_der(cert_path: str) -> bytes:
beg_cert = '-----BEGIN CERTIFICATE-----'
end_cert = '-----END CERTIFICATE-----'
with open(cert_path, 'r') as f:
pem = f.read()
cert = pem.split(beg_cert)[1].split(end_cert)[0]
return b64decode(cert)
def certs(cert_path: list[str]) -> univ.SequenceOf | None:
if len(cert_path) == 0:
return None
certs = rfc6960.BasicOCSPResponse()['certs']
for cp in cert_path:
cert_der = cert_pem_to_der(cp)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
certs.append(cert)
return certs
def signature(bitstr: str) -> univ.BitString:
return univ.BitString(hexValue=bitstr)
def resp_id_by_name(cert_path: str) -> rfc6960.ResponderID:
cert_der = cert_pem_to_der(cert_path)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
subj = cert['tbsCertificate']['subject']
rid = rfc6960.ResponderID()
rdi_name = rid['byName']
rdi_name['rdnSequence'] = subj['rdnSequence']
return rid
def resp_id_by_key(cert_path: str) -> rfc6960.ResponderID:
cert_der = cert_pem_to_der(cert_path)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
key = get_key(cert)
key_hash = sha1(key.asOctets()).digest()
rid = rfc6960.ResponderID()
rid['byKey'] = rfc6960.KeyHash(value=key_hash).subtype(explicitTag=
tag.Tag(
tag.tagClassContext,
tag.tagFormatSimple,
2))
return rid
def get_key(cert: rfc6960.Certificate) -> univ.BitString:
return cert['tbsCertificate']['subjectPublicKeyInfo']['subjectPublicKey']
def get_name(cert: rfc6960.Certificate) -> rfc6960.Name:
return cert['tbsCertificate']['subject']
def cert_id_from_hash(issuer_name_hash: bytes, issuer_key_hash: bytes,
serial: int) -> rfc6960.CertID:
cert_id = rfc6960.CertID()
cert_id['hashAlgorithm'] = cert_id_sha1_alg_id()
cert_id['issuerNameHash'] = univ.OctetString(value=issuer_name_hash)
cert_id['issuerKeyHash'] = univ.OctetString(value=issuer_key_hash)
cert_id['serialNumber'] = rfc6960.CertificateSerialNumber(serial)
return cert_id
def cert_id(issuer_cert_path: str, serial: int) -> rfc6960.CertID:
issuer_cert = cert_pem_to_der(issuer_cert_path)
issuer, _ = decode(bytes(issuer_cert), asn1Spec=rfc6960.Certificate())
issuer_name = get_name(issuer)
issuer_key = get_key(issuer)
issuer_name_hash = sha1(encode(issuer_name)).digest()
issuer_key_hash = sha1(issuer_key.asOctets()).digest()
cert_id = rfc6960.CertID()
cert_id['hashAlgorithm'] = cert_id_sha1_alg_id()
cert_id['issuerNameHash'] = univ.OctetString(value=issuer_name_hash)
cert_id['issuerKeyHash'] = univ.OctetString(value=issuer_key_hash)
cert_id['serialNumber'] = rfc6960.CertificateSerialNumber(serial)
return cert_id
CERT_GOOD = 0
CERT_REVOKED = 1
CERT_UNKNOWN = 2
def cert_status(value: int) -> rfc6960.CertStatus:
cs = rfc6960.CertStatus()
if value == CERT_GOOD:
good = univ.Null('').subtype(implicitTag=tag.Tag(tag.tagClassContext,
tag.tagFormatSimple,
0))
cs['good'] = good
elif value == CERT_REVOKED:
revoked = rfc6960.RevokedInfo().subtype(implicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatSimple, 1))
revoked['revocationTime'] = useful.GeneralizedTime().fromDateTime(
datetime.now())
cs['revoked'] = revoked
return cs
def single_response(issuer_cert_path: str, serial: int,
status: int) -> rfc6960.SingleResponse:
cid = cert_id(issuer_cert_path, serial)
cs = cert_status(status)
sr = rfc6960.SingleResponse().clone()
sr.setComponentByName('certID', cid)
sr['certStatus'] = cs
sr['thisUpdate'] = useful.GeneralizedTime().fromDateTime(datetime.now())
return sr
def response_data(rid: rfc6960.ResponderID | None,
responses: list[rfc6960.SingleResponse]) -> rfc6960.ResponseData:
rd = rfc6960.ResponseData()
rd['version'] = rfc6960.Version('v1').subtype(explicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatSimple, 0))
if rid:
rd['responderID'] = rid
rd['producedAt'] = useful.GeneralizedTime().fromDateTime(datetime.now())
rs = univ.SequenceOf(componentType=rfc6960.SingleResponse())
rs.extend(responses)
rd['responses'] = rs
return rd
def read_key_der_from_pem(key_path: str) -> bytes:
with open(key_path, 'r') as f:
pem = f.readlines()
pem_start = [i for i, line in enumerate(pem) if '-----BEGIN' in line][0]
pem_end = [i for i, line in enumerate(pem) if '-----END' in line][0]
key = ''.join(pem[pem_start+1:pem_end])
return b64decode(key)
def basic_ocsp_response(rd: rfc6960.ResponseData, sig_alg:
rfc6960.AlgorithmIdentifier, sig: univ.BitString,
certs: univ.SequenceOf|None = None) -> rfc6960.BasicOCSPResponse:
br = rfc6960.BasicOCSPResponse()
br['tbsResponseData'] = rd
br['signatureAlgorithm'] = sig_alg
br['signature'] = sig
if certs is not None:
br['certs'] = certs
return br
def response_bytes(br: rfc6960.BasicOCSPResponse) -> rfc6960.ResponseBytes:
rb = rfc6960.ResponseBytes().subtype(explicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatConstructed, 0))
rb['responseType'] = response_type()
rb['response'] = encode(br)
return rb
def ocsp_response(status: rfc6960.OCSPResponseStatus,
response_bytes: rfc6960.ResponseBytes) -> rfc6960.OCSPResponse:
orsp = rfc6960.OCSPResponse()
orsp['responseStatus'] = status
orsp['responseBytes'] = response_bytes
return orsp
def get_priv_key(pem_path) -> rsa.RSAPrivateKey:
key_der = read_key_der_from_pem(pem_path)
private_key = serialization.load_der_private_key(
key_der,
password=None,
)
return private_key
def sign_repsonse_data(rd: rfc6960.ResponseData,
key: rsa.RSAPrivateKey) -> univ.BitString:
sig = key.sign(encode(rd), padding.PKCS1v15(), hashes.SHA256())
return univ.BitString(hexValue=sig.hex())
def get_pub_key(cert_path: str) -> rsa.RSAPublicKey:
with open(cert_path, 'rb') as f:
cert = f.read()
cert = x509.load_pem_x509_certificate(cert, default_backend())
return cert.public_key()
def test_signature(ocsp_resp_path: str, key: rsa.RSAPublicKey):
with open(ocsp_resp_path, 'rb') as f:
ocsp_resp = f.read()
ocsp_resp, _ = decode(ocsp_resp, asn1Spec=rfc6960.OCSPResponse())
response = ocsp_resp.getComponentByName(
'responseBytes').getComponentByName('response')
br, _ = decode(response, asn1Spec=rfc6960.BasicOCSPResponse())
rd = br.getComponentByName('tbsResponseData')
rd_hash = sha256(encode(rd)).digest()
di = rfc8017.DigestInfo()
di['digestAlgorithm'] = signature_algorithm()
di['digest'] = univ.OctetString(rd_hash)
sig = br.getComponentByName('signature')
key.verify(sig.asOctets(), encode(rd), padding.PKCS1v15(), hashes.SHA256())
def single_response_from_cert(cert_path: str,
status: int) -> rfc6960.SingleResponse:
cert_der = cert_pem_to_der(cert_path)
cert, _ = decode(bytes(cert_der), asn1Spec=rfc6960.Certificate())
serial = cert['tbsCertificate']['serialNumber']
issuer = cert['tbsCertificate']['issuer']
serialHash = sha1(serial.asOctets()).digest()
issuerHash = sha1(encode(issuer)).digest()
cid = cert_id_from_hash(issuerHash, serialHash, serial)
cs = cert_status(status)
sr = rfc6960.SingleResponse().clone()
sr.setComponentByName('certID', cid)
sr['certStatus'] = cs
sr['thisUpdate'] = useful.GeneralizedTime().fromDateTime(datetime.now())
return sr
RESPONSE_STATUS_GOOD = 0
def write_buffer(name: str, data: bytes, f):
f.write(f"unsigned char {name}[] = {{\n")
for i in range(0, len(data), 12):
f.write(" " + ", ".join(f"0x{b:02x}" for b in data[i:i+12]) + ",\n")
f.write("};\n\n")
def create_response(rd: dict) -> rfc6960.OCSPResponse:
"""create a response using definition in rd"""
cs = response_status(rd.get('response_status', RESPONSE_STATUS_GOOD))
sa = rd.get('signature_algorithm', signature_algorithm())
c = certs(rd.get('certs_path', []))
rid = None
if rd.get('responder_by_name') is not None:
rid = resp_id_by_name(
rd.get(
'responder_cert', WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'))
elif rd.get('responder_by_key', None) is not None:
rid = resp_id_by_key(
rd.get('responder_cert', WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'))
# implement responder byhash
responses = []
for entry in rd.get('responses', []):
if entry.get('certificate'):
sr = single_response_from_cert(entry['certificate'], entry['status'])
else:
sr = single_response(entry['issuer_cert'], entry['serial'], entry['status'])
responses.append(sr)
rd_data = response_data(rid, responses)
k = get_priv_key(rd.get('responder_key', WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem'))
s = sign_repsonse_data(rd_data, k)
br = basic_ocsp_response(rd_data, sa, s, c)
rb = response_bytes(br)
ocspr = ocsp_response(cs, rb)
return ocspr
def create_and_write_response(rd: dict, f):
ocspr = create_response(rd)
encoded_response = encode(ocspr)
write_buffer(rd['name'].replace('-', '_').replace('.', '_'), encoded_response, f)
def add_certificate(cert_path: str, f):
cert_der = cert_pem_to_der(cert_path)
write_buffer(cert_path.split('/')[-1].replace('-', '_').replace('.', '_'), cert_der, f)
class badOCSPResponse(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.OptionalNamedType('responseBytes', rfc6960.ResponseBytes().subtype(
explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0)))
)
def create_bad_response(rd: dict) -> bytes:
"""Creates a malformed OCSP response by removing the response status field"""
r = create_response(rd)
br = badOCSPResponse()
br['responseBytes'] = r['responseBytes']
return encode(br)
if __name__ == '__main__':
useful.GeneralizedTime._hasSubsecond = False
response_definitions = [
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'certs_path': [WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'],
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem',
'name': 'resp'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'certs_path': [WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem'],
'responder_by_key': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem',
'name': 'resp_rid_bykey',
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-key.pem',
'name': 'resp_nocert'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
},
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x02,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'root-ca-key.pem',
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'name': 'resp_multi'
},
{
'response_status': 0,
'signature_algorithm': signature_algorithm(),
'responder_by_name': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
},
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + '../ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'responder_key': WOLFSSL_OCSP_CERT_PATH + 'root-ca-key.pem',
'responder_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'name': 'resp_bad_noauth'
},
]
with open('./tests/api/ocsp_test_blobs.h', 'w') as f:
f.write(
"""/*
* This file is generated automatically by running ./tests/api/create_ocsp_test_blobs.py.
*/
""")
f.write("#ifndef OCSP_TEST_BLOBS_H\n")
f.write("#define OCSP_TEST_BLOBS_H\n\n")
for rd in response_definitions:
create_and_write_response(rd, f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + 'ocsp-responder-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + '../ca-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + '../server-cert.pem', f)
add_certificate(WOLFSSL_OCSP_CERT_PATH + 'intermediate1-ca-cert.pem', f)
br = create_bad_response({
'response_status': 0,
'responder_by_key': True,
'responses': [
{
'issuer_cert': WOLFSSL_OCSP_CERT_PATH + 'root-ca-cert.pem',
'serial': 0x01,
'status': CERT_GOOD
}
],
'name': 'resp_bad'
})
write_buffer('resp_bad', br, f)
f.write("#endif // OCSP_TEST_BLOBS_H\n")

View File

@ -14,6 +14,7 @@ tests_unit_test_SOURCES += tests/api/test_ripemd.c
tests_unit_test_SOURCES += tests/api/test_hash.c
tests_unit_test_SOURCES += tests/api/test_ascon.c
tests_unit_test_SOURCES += tests/api/test_dtls.c
tests_unit_test_SOURCES += tests/api/test_ocsp.c
endif
EXTRA_DIST += tests/api/api.h
EXTRA_DIST += tests/api/test_md5.h
@ -29,4 +30,7 @@ EXTRA_DIST += tests/api/test_ascon.h
EXTRA_DIST += tests/api/test_ascon.h
EXTRA_DIST += tests/api/test_ascon_kats.h
EXTRA_DIST += tests/api/test_dtls.h
EXTRA_DIST += tests/api/test_ocsp.h
EXTRA_DIST += tests/api/test_ocsp_test_blobs.h
EXTRA_DIST += tests/api/create_ocsp_test_blobs.py

View File

@ -0,0 +1,568 @@
/* ocsp.c
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if !defined(WOLFSSL_USER_SETTINGS) && !defined(WOLFSSL_NO_OPTIONS_H)
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <tests/api/test_ocsp.h>
#include <tests/api/test_ocsp_test_blobs.h>
#include <tests/unit.h>
#include <wolfssl/internal.h>
#include <wolfssl/ocsp.h>
#include <wolfssl/ssl.h>
#if defined(HAVE_OCSP)
struct ocsp_cb_ctx {
byte* response;
int responseSz;
};
struct test_conf {
unsigned char* resp;
int respSz;
unsigned char* ca0;
int ca0Sz;
unsigned char* ca1;
int ca1Sz;
unsigned char* targetCert;
int targetCertSz;
};
static int ocsp_cb(void* ctx, const char* url, int urlSz, unsigned char* req,
int reqSz, unsigned char** respBuf)
{
struct ocsp_cb_ctx* cb_ctx = (struct ocsp_cb_ctx*)ctx;
(void)url;
(void)urlSz;
(void)req;
(void)reqSz;
*respBuf = cb_ctx->response;
return cb_ctx->responseSz;
}
static int test_ocsp_response_with_cm(struct test_conf* c)
{
EXPECT_DECLS;
WOLFSSL_CERT_MANAGER* cm = NULL;
struct ocsp_cb_ctx cb_ctx;
int ret;
cm = wolfSSL_CertManagerNew();
ExpectPtrNE(cm, NULL);
ret = wolfSSL_CertManagerEnableOCSP(cm,
WOLFSSL_OCSP_URL_OVERRIDE | WOLFSSL_OCSP_NO_NONCE);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
ret = wolfSSL_CertManagerSetOCSPOverrideURL(cm, "http://foo.com");
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
cb_ctx.response = (byte*)c->resp;
cb_ctx.responseSz = c->respSz;
ret = wolfSSL_CertManagerSetOCSP_Cb(cm, ocsp_cb, NULL, (void*)&cb_ctx);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
/* add ca in cm */
if (c->ca0 != NULL) {
ret = wolfSSL_CertManagerLoadCABuffer(cm, c->ca0, c->ca0Sz,
WOLFSSL_FILETYPE_ASN1);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
}
if (c->ca1 != NULL) {
ret = wolfSSL_CertManagerLoadCABuffer(cm, c->ca1, c->ca1Sz,
WOLFSSL_FILETYPE_ASN1);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
}
/* check cert */
ret = wolfSSL_CertManagerCheckOCSP(cm, c->targetCert, c->targetCertSz);
wolfSSL_CertManagerFree(cm);
return ret;
}
int test_ocsp_response_parsing(void)
{
struct test_conf conf;
int ret;
EXPECT_DECLS;
conf.resp = (unsigned char*)resp;
conf.respSz = sizeof(resp);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = NULL;
conf.ca1Sz = 0;
conf.targetCert = intermediate1_ca_cert_pem;
conf.targetCertSz = sizeof(intermediate1_ca_cert_pem);
ret = test_ocsp_response_with_cm(&conf);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
conf.resp = (unsigned char*)resp_multi;
conf.respSz = sizeof(resp_multi);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = NULL;
conf.ca1Sz = 0;
conf.targetCert = intermediate1_ca_cert_pem;
conf.targetCertSz = sizeof(intermediate1_ca_cert_pem);
ret = test_ocsp_response_with_cm(&conf);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
conf.resp = (unsigned char*)resp_bad_noauth;
conf.respSz = sizeof(resp_bad_noauth);
conf.ca0 = root_ca_cert_pem;
conf.ca0Sz = sizeof(root_ca_cert_pem);
conf.ca1 = ca_cert_pem;
conf.ca1Sz = sizeof(ca_cert_pem);
conf.targetCert = server_cert_pem;
conf.targetCertSz = sizeof(server_cert_pem);
ret = test_ocsp_response_with_cm(&conf);
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
ExpectIntNE(ret, WOLFSSL_SUCCESS);
#else
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
#endif
return EXPECT_SUCCESS();
}
#else /* HAVE_OCSP */
int test_ocsp_response_parsing(void) { return TEST_SKIPPED; }
#endif /* HAVE_OCSP */
#if defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA))
static int test_ocsp_create_x509store(WOLFSSL_X509_STORE** store,
unsigned char* ca, int caSz)
{
EXPECT_DECLS;
WOLFSSL_X509* cert = NULL;
int ret;
*store = wolfSSL_X509_STORE_new();
ExpectPtrNE(*store, NULL);
cert = wolfSSL_X509_d2i(&cert, ca, caSz);
ExpectPtrNE(cert, NULL);
ret = wolfSSL_X509_STORE_add_cert(*store, cert);
wolfSSL_X509_free(cert);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
return EXPECT_RESULT();
}
static int test_create_stack_of_x509(WOLF_STACK_OF(WOLFSSL_X509) * *certs,
unsigned char* der, int derSz)
{
EXPECT_DECLS;
WOLFSSL_X509* cert = NULL;
int ret;
*certs = wolfSSL_sk_X509_new_null();
ExpectPtrNE(*certs, NULL);
cert = wolfSSL_X509_d2i(&cert, der, derSz);
ExpectPtrNE(cert, NULL);
ret = wolfSSL_sk_X509_push(*certs, cert);
ExpectIntEQ(ret, 1);
return EXPECT_RESULT();
}
int test_ocsp_basic_verify(void)
{
EXPECT_DECLS;
WOLF_STACK_OF(WOLFSSL_X509) * certs;
OcspResponse* response = NULL;
WOLFSSL_X509_STORE* store;
const unsigned char* ptr;
DecodedCert cert;
int ret;
wc_InitDecodedCert(&cert, ocsp_responder_cert_pem,
sizeof(ocsp_responder_cert_pem), NULL);
ret = wc_ParseCert(&cert, CERT_TYPE, 0, NULL);
ExpectIntEQ(ret, 0);
/* just decoding */
ptr = (const unsigned char*)resp;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp));
ExpectPtrNE(response, NULL);
ExpectIntEQ(response->responseStatus, 0);
ExpectIntEQ(response->responderIdType, OCSP_RESPONDER_ID_NAME);
ExpectBufEQ(response->responderId.nameHash, cert.subjectHash,
OCSP_DIGEST_SIZE);
wolfSSL_OCSP_RESPONSE_free(response);
/* responder Id by key hash */
ptr = (const unsigned char*)resp_rid_bykey;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_rid_bykey));
ExpectPtrNE(response, NULL);
ExpectIntEQ(response->responseStatus, 0);
ExpectIntEQ(response->responderIdType, OCSP_RESPONDER_ID_KEY);
ExpectBufEQ(response->responderId.keyHash, cert.subjectKeyHash,
OCSP_DIGEST_SIZE);
wc_FreeDecodedCert(&cert);
wolfSSL_OCSP_RESPONSE_free(response);
/* decoding with no embedded certificates */
ptr = (const unsigned char*)resp_nocert;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_nocert));
ExpectPtrNE(response, NULL);
ExpectIntEQ(response->responseStatus, 0);
wolfSSL_OCSP_RESPONSE_free(response);
/* decoding an invalid response */
ptr = (const unsigned char*)resp_bad;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_bad));
ExpectPtrEq(response, NULL);
ptr = (const unsigned char*)resp;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp));
ExpectPtrNE(response, NULL);
/* no verify signer certificate */
ret = wolfSSL_OCSP_basic_verify(response, NULL, NULL, OCSP_NOVERIFY);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
/* verify that the signature is checked */
response->sig[0] ^= 0xff;
ret = wolfSSL_OCSP_basic_verify(response, NULL, NULL, OCSP_NOVERIFY);
ExpectIntEQ(ret, WOLFSSL_FAILURE);
wolfSSL_OCSP_RESPONSE_free(response);
/* populate a store with root-ca-cert */
ret = test_ocsp_create_x509store(&store, root_ca_cert_pem,
sizeof(root_ca_cert_pem));
ExpectIntEQ(ret, TEST_SUCCESS);
/* populate a WOLF_STACK_OF(WOLFSSL_X509) with responder certificate */
ret = test_create_stack_of_x509(&certs, ocsp_responder_cert_pem,
sizeof(ocsp_responder_cert_pem));
ExpectIntEQ(ret, TEST_SUCCESS);
/* cert not embedded, cert in certs, validated using store */
ptr = (const unsigned char*)resp_nocert;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_nocert));
ExpectPtrNE(response, NULL);
ret = wolfSSL_OCSP_basic_verify(response, certs, store, 0);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
/* cert embedded, verified using store */
ptr = (const unsigned char*)resp;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp));
ExpectPtrNE(response, NULL);
ret = wolfSSL_OCSP_basic_verify(response, NULL, store, 0);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
/* make invalid signature */
response->sig[0] ^= 0xff;
ret = wolfSSL_OCSP_basic_verify(response, NULL, store, 0);
ExpectIntEQ(ret, WOLFSSL_FAILURE);
response->sig[0] ^= 0xff;
/* cert embedded and in certs, no store needed bc OCSP_TRUSTOTHER */
ret = wolfSSL_OCSP_basic_verify(response, certs, NULL, OCSP_TRUSTOTHER);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
/* this should also pass */
ret = wolfSSL_OCSP_basic_verify(response, certs, store, OCSP_NOINTERN);
;
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
/* this should not */
ret = wolfSSL_OCSP_basic_verify(response, NULL, store, OCSP_NOINTERN);
;
ExpectIntNE(ret, WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
/* cert not embedded, not certs */
ptr = (const unsigned char*)resp_nocert;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_nocert));
ExpectPtrNE(response, NULL);
ret = wolfSSL_OCSP_basic_verify(response, NULL, store, 0);
ExpectIntNE(ret, WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
wolfSSL_sk_X509_pop_free(certs, wolfSSL_X509_free);
wolfSSL_X509_STORE_free(store);
ret = test_ocsp_create_x509store(&store, root_ca_cert_pem,
sizeof(root_ca_cert_pem));
ExpectIntEQ(ret, TEST_SUCCESS);
ret = test_create_stack_of_x509(&certs, root_ca_cert_pem,
sizeof(root_ca_cert_pem));
ExpectIntEQ(ret, TEST_SUCCESS);
/* multiple responses in a ocsp response */
ptr = (const unsigned char*)resp_multi;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_multi));
ExpectPtrNE(response, NULL);
ret = wolfSSL_OCSP_basic_verify(response, certs, store, 0);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
/* cert in certs, cert verified on store, not authorized to verify all
* responses */
ptr = (const unsigned char*)resp_bad_noauth;
response = wolfSSL_d2i_OCSP_RESPONSE(NULL, &ptr, sizeof(resp_bad_noauth));
ExpectPtrNE(response, NULL);
ret = wolfSSL_OCSP_basic_verify(response, certs, store, 0);
#ifndef WOLFSSL_NO_OCSP_ISSUER_CHECK
ExpectIntEQ(ret, WOLFSSL_FAILURE);
#else
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
#endif
/* should pass with OCSP_NOCHECKS ...*/
ret = wolfSSL_OCSP_basic_verify(response, certs, store, OCSP_NOCHECKS);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
/* or with OSCP_TRUSTOTHER */
ret = wolfSSL_OCSP_basic_verify(response, certs, store, OCSP_TRUSTOTHER);
ExpectIntEQ(ret, WOLFSSL_SUCCESS);
wolfSSL_OCSP_RESPONSE_free(response);
wolfSSL_sk_X509_pop_free(certs, wolfSSL_X509_free);
wolfSSL_X509_STORE_free(store);
return EXPECT_RESULT();
}
#else
int test_ocsp_basic_verify(void) { return TEST_SKIPPED; }
#endif /* HAVE_OCSP && (OPENSSL_ALL || OPENSSL_EXTRA) */
#if defined(HAVE_OCSP) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(WOLFSSL_NO_TLS12) && \
(defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA))
struct _test_ocsp_status_callback_ctx {
byte* ocsp_resp;
int ocsp_resp_sz;
int invoked;
};
static int test_ocsp_status_callback_cb(WOLFSSL* ssl, void* ctx)
{
struct _test_ocsp_status_callback_ctx* _ctx =
(struct _test_ocsp_status_callback_ctx*)ctx;
byte* allocated;
_ctx->invoked++;
allocated = (byte*)XMALLOC(_ctx->ocsp_resp_sz, NULL, 0);
if (allocated == NULL)
return SSL_TLSEXT_ERR_ALERT_FATAL;
XMEMCPY(allocated, _ctx->ocsp_resp, _ctx->ocsp_resp_sz);
SSL_set_tlsext_status_ocsp_resp(ssl, allocated, _ctx->ocsp_resp_sz);
return SSL_TLSEXT_ERR_OK;
}
static int test_ocsp_status_callback_cb_noack(WOLFSSL* ssl, void* ctx)
{
struct _test_ocsp_status_callback_ctx* _ctx =
(struct _test_ocsp_status_callback_ctx*)ctx;
(void)ssl;
_ctx->invoked++;
return SSL_TLSEXT_ERR_NOACK;
}
static int test_ocsp_status_callback_cb_err(WOLFSSL* ssl, void* ctx)
{
struct _test_ocsp_status_callback_ctx* _ctx =
(struct _test_ocsp_status_callback_ctx*)ctx;
(void)ssl;
_ctx->invoked++;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
static int test_ocsp_status_callback_test_setup(
struct _test_ocsp_status_callback_ctx* cb_ctx,
struct test_ssl_memio_ctx* test_ctx, method_provider cm, method_provider sm)
{
int ret;
cb_ctx->invoked = 0;
XMEMSET(test_ctx, 0, sizeof(*test_ctx));
test_ctx->c_cb.caPemFile = "./certs/ocsp/root-ca-cert.pem";
test_ctx->s_cb.certPemFile = "./certs/ocsp/server1-cert.pem";
test_ctx->s_cb.keyPemFile = "./certs/ocsp/server1-key.pem";
test_ctx->c_cb.method = cm;
test_ctx->s_cb.method = sm;
ret = test_ssl_memio_setup(test_ctx);
wolfSSL_set_verify(test_ctx->c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
return ret;
}
static int test_ocsp_status_callback(void)
{
struct test_params {
method_provider c_method;
method_provider s_method;
};
const char* responseFile = "./certs/ocsp/test-leaf-response.der";
struct _test_ocsp_status_callback_ctx cb_ctx;
struct test_ssl_memio_ctx test_ctx;
int enable_client_ocsp;
int enable_must_staple;
XFILE f = XBADFILE;
byte data[4096];
unsigned int i;
EXPECT_DECLS;
struct test_params params[] = {
{wolfTLSv1_2_client_method, wolfTLSv1_2_server_method},
#if defined(WOLFSSL_TLS13)
{wolfTLSv1_3_client_method, wolfTLSv1_3_server_method},
#endif
#if defined(WOLFSSL_DTLS)
{wolfDTLSv1_2_client_method, wolfDTLSv1_2_server_method},
#endif
#if defined(WOLFSSL_DTLS13)
{wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method},
#endif
};
XMEMSET(&cb_ctx, 0, sizeof(cb_ctx));
f = XFOPEN(responseFile, "rb");
if (f == XBADFILE)
return -1;
cb_ctx.ocsp_resp_sz = (word32)XFREAD(data, 1, 4096, f);
if (f != XBADFILE) {
XFCLOSE(f);
f = XBADFILE;
}
cb_ctx.ocsp_resp = data;
for (i = 0; i < sizeof(params) / sizeof(params[0]); i++) {
for (enable_client_ocsp = 0; enable_client_ocsp <= 1;
enable_client_ocsp++) {
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
params[i].c_method, params[i].s_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb),
SSL_SUCCESS);
ExpectIntEQ(
SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
if (enable_client_ocsp) {
ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl,
WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
}
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, enable_client_ocsp ? 1 : 0);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
}
}
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
/* test client sending both OCSPv1 and OCSPv2/MultiOCSP */
/* StatusCb only supports OCSPv1 */
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb),
SSL_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
ExpectIntEQ(
wolfSSL_UseOCSPStaplingV2(test_ctx.c_ssl, WOLFSSL_CSR2_OCSP_MULTI, 0),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL), TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, 1);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
#endif /* defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) */
/* test cb returning NO_ACK, not acking the OCSP */
for (i = 0; i < sizeof(params) / sizeof(params[0]); i++) {
for (enable_must_staple = 0; enable_must_staple <= 1;
enable_must_staple++) {
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
params[i].c_method, params[i].s_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb_noack),
SSL_SUCCESS);
ExpectIntEQ(
SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(
wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
if (enable_must_staple)
ExpectIntEQ(wolfSSL_CTX_EnableOCSPMustStaple(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
enable_must_staple ? TEST_FAIL : TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, 1);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
}
}
/* test cb returning err aborting handshake */
for (i = 0; i < sizeof(params) / sizeof(params[0]); i++) {
for (enable_client_ocsp = 0; enable_client_ocsp <= 1;
enable_client_ocsp++) {
ExpectIntEQ(test_ocsp_status_callback_test_setup(&cb_ctx, &test_ctx,
params[i].c_method, params[i].s_method),
TEST_SUCCESS);
ExpectIntEQ(SSL_CTX_set_tlsext_status_cb(test_ctx.s_ctx,
test_ocsp_status_callback_cb_err),
SSL_SUCCESS);
ExpectIntEQ(
SSL_CTX_set_tlsext_status_arg(test_ctx.s_ctx, (void*)&cb_ctx),
SSL_SUCCESS);
if (enable_client_ocsp)
ExpectIntEQ(wolfSSL_CTX_EnableOCSPStapling(test_ctx.c_ctx),
WOLFSSL_SUCCESS);
ExpectIntEQ(
wolfSSL_UseOCSPStapling(test_ctx.c_ssl, WOLFSSL_CSR_OCSP, 0),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(test_ctx.c_ssl, WOLFSSL_VERIFY_DEFAULT, NULL);
ExpectIntEQ(test_ssl_memio_do_handshake(&test_ctx, 10, NULL),
enable_client_ocsp ? TEST_FAIL : TEST_SUCCESS);
ExpectIntEQ(cb_ctx.invoked, enable_client_ocsp ? 1 : 0);
test_ssl_memio_cleanup(&test_ctx);
if (!EXPECT_SUCCESS())
return EXPECT_RESULT();
}
}
return EXPECT_RESULT();
}
#else
int test_ocsp_status_callback(void) { return TEST_SKIPPED; }
#endif /* defined(HAVE_OCSP) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) \
&& defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(WOLFSSL_NO_TLS12) \
&& (defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA)) */

View File

@ -0,0 +1,29 @@
/* ocsp.h
*
* Copyright (C) 2006-2025 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifndef WOLFSSL_TEST_OCSP_H
#define WOLFSSL_TEST_OCSP_H
int test_ocsp_status_callback(void);
int test_ocsp_basic_verify(void);
int test_ocsp_response_parsing(void);
#endif /* WOLFSSL_TEST_OCSP_H */

File diff suppressed because it is too large Load Diff