Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion test-tools/ossl_test_tools/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

import argparse

from . import crltest, pem
from . import crltest, ocsptest, pem


def main(argv=None):
Expand All @@ -25,6 +25,7 @@ def main(argv=None):
sub = parser.add_subparsers(dest="cmd", required=True)

crltest.register(sub)
ocsptest.register(sub)
pem.register(sub)

args = parser.parse_args(argv)
Expand Down
35 changes: 35 additions & 0 deletions test-tools/ossl_test_tools/ocsptest/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

"""Generators for the artifacts in test/ocsptest.c.

Each subcommand writes its outputs back into the C source file, replacing
the existing kXxx[] declarations by variable name.
"""

from pathlib import Path

from . import pki


def _all_cmd(args):
pki.build(args.source)
print(f"regenerated all OCSP artifacts in {args.source}")


def register(subparsers):
parent = subparsers.add_parser(
"ocsptest",
help="Regenerate artifacts in test/ocsptest.c.",
)
sub = parent.add_subparsers(dest="ocsptest_cmd", required=True)

pki.register(sub)

p = sub.add_parser("all", help="Run all OCSP artifact generators.")
p.add_argument("--source", type=Path, required=True, help="Path to ocsptest.c")
p.set_defaults(func=_all_cmd)
117 changes: 117 additions & 0 deletions test-tools/ossl_test_tools/ocsptest/pki.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved.
#
# Licensed under the Apache License 2.0 (the "License"). You may not use
# this file except in compliance with the License. You can obtain a copy
# in the file LICENSE in the source distribution or at
# https://www.openssl.org/source/license.html

"""PKI for test/ocsptest.c.

kOcspTestRoot self-signed root CA, trust anchor and OCSP responder
kOcspTestRootKey the root CA private key, used to sign OCSP responses
kOcspTestLeaf leaf issued by the root CA

The chain is flat (no intermediate) so the root is the leaf's issuer and
therefore its authorized OCSP responder. Validity windows are long because
the verification path checks OCSP response validity against the wall clock.
"""

import datetime
from pathlib import Path

from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.x509.oid import ExtendedKeyUsageOID

from .. import cert_util

UTC = datetime.timezone.utc
NOT_BEFORE = datetime.datetime(2026, 1, 1, 0, 0, 0, tzinfo=UTC)
NOT_AFTER = datetime.datetime(2126, 1, 1, 0, 0, 0, tzinfo=UTC)

ROOT_DN = cert_util.name(
"US", "California", "San Francisco",
"Example Corp", "Certificate Authority",
"Example Corp OCSP Test Root CA",
)
LEAF_DN = cert_util.name(
"US", "California", "San Francisco",
"Example Corp", "Web Services",
"ocsp-leaf.example.com",
)


def _ca_key_usage():
return x509.KeyUsage(
digital_signature=True, content_commitment=False,
key_encipherment=False, data_encipherment=False, key_agreement=False,
key_cert_sign=True, crl_sign=True,
encipher_only=False, decipher_only=False)


def _leaf_key_usage():
return x509.KeyUsage(
digital_signature=True, content_commitment=False,
key_encipherment=True, data_encipherment=False, key_agreement=False,
key_cert_sign=False, crl_sign=False,
encipher_only=False, decipher_only=False)


def build(source_path):
root_key = cert_util.new_rsa_key()
root = (
x509.CertificateBuilder()
.subject_name(ROOT_DN)
.issuer_name(ROOT_DN)
.public_key(root_key.public_key())
.serial_number(0x1)
.not_valid_before(NOT_BEFORE)
.not_valid_after(NOT_AFTER)
.add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
.add_extension(_ca_key_usage(), critical=True)
.add_extension(
x509.SubjectKeyIdentifier.from_public_key(root_key.public_key()),
critical=False)
.sign(private_key=root_key, algorithm=hashes.SHA256())
)

leaf_key = cert_util.new_rsa_key()
leaf = (
x509.CertificateBuilder()
.subject_name(LEAF_DN)
.issuer_name(ROOT_DN)
.public_key(leaf_key.public_key())
.serial_number(0x1000)
.not_valid_before(NOT_BEFORE)
.not_valid_after(NOT_AFTER)
.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
.add_extension(_leaf_key_usage(), critical=True)
.add_extension(
x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]), critical=False)
.add_extension(
x509.SubjectKeyIdentifier.from_public_key(leaf_key.public_key()),
critical=False)
.add_extension(cert_util.akid_from_cert(root), critical=False)
.add_extension(
x509.SubjectAlternativeName([x509.DNSName("ocsp-leaf.example.com")]),
critical=False)
.sign(private_key=root_key, algorithm=hashes.SHA256())
)

cert_util.update_cert_in_c(source_path, "kOcspTestRoot", root)
cert_util.update_key_in_c(source_path, "kOcspTestRootKey", root_key)
cert_util.update_cert_in_c(source_path, "kOcspTestLeaf", leaf)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes me a bit concerned for a few reasons:

  1. The updates that happen here don't seem to match the names provided. Running this:
uv run ossl-test-tools crltest all      --source /home/nhorman/git/openssl/test/crltest.c

Produces a diff in the crltest file that looks like this:

diff --git a/test/crltest.c b/test/crltest.c
index 3e06df10b7..5f185a8f78 100644
--- a/test/crltest.c
+++ b/test/crltest.c
@@ -90,21 +90,21 @@ static const char *kRoot2[] = {
     "MzYwMzA3MTIwMDAwWjCBizELMAkGA1UEBhMCVVMxDzANBgNVBAgMBk5ldmFkYTEN\n",
     "MAsGA1UEBwwEUmVubzEZMBcGA1UECgwQRXhhbXBsZSBBbHQgQ29ycDEeMBwGA1UE\n",
     "CwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSEwHwYDVQQDDBhFeGFtcGxlIEFsdCBD\n",
-    "b3JwIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTXGOg\n",
...
+    "b3JwIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDfg3jb\n",
...
@@ -810,20 +810,20 @@ static const char *kIndirectCRLIssuer[] = {
     "bGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xFTATBgNVBAoMDEV4YW1w\n",
     "bGUgQ29ycDEeMBwGA1UECwwVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MSkwJwYDVQQD\n",
     "DCBFeGFtcGxlIENvcnAgSW5kaXJlY3QgQ1JMIElzc3VlcjCCASIwDQYJKoZIhvcN\n",
-    "AQEBBQADggEPADCCAQoCggEBAKgcYFF3+Z/A12AMb3P9Isl959u4QpX/fx4d+A38\n",
...
+    "AQEBBQADggEPADCCAQoCggEBALnOrEYq5K/gMja6UvzOYY1LdQbLjhjCfDjbbhdo\n",
...

i.e. kOcspTestRoot, kOcspTestRootKey, and kOcspTestLeaf (none of which exist in the current crltest.c file) were not modified, but several other certificates/CRL's were, which seems unexpected

  1. We're modifiying source under scm here. While thats fine in and of itself, it seems like that might lead to inadvertent commits in future PR's. It might be nice, instead of modify the c source directly to use this utility to generate new pem files that tests like crltest.c (and others) could be modified to read.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not for crltest.c but for new ocsptest.c added in openssl/openssl#31828 .

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't be a big issue because it won't be most likely run unless there is a need to replace algorithm or do some modifications. It's really more for just in case situations so it's potentially easy to modify the cert if there is a need for that and also to show what's actually in it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, so perhaps thats my confusion, as the README that was previously added ahead of this PR indicates usage for this tool as follows:

## Use

    uv run ossl-test-tools crltest all      --source path/to/crltest.c
    uv run ossl-test-tools crltest indirect --source path/to/crltest.c
    uv run ossl-test-tools crltest alt-ta   --source path/to/crltest.c
    uv run ossl-test-tools crltest no-chain --source path/to/crltest.c

I ran that because, well, I don't know what I'm doing since this is a new tool for me, but despite your statement above that this is meant for a specific test, I would have expected running this command should have errored out indicating that the requested variables to change weren't found or some such.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But it does what it is asked to do. If you want to modify ocsptest you have to run uv run ossl-test-tools ocsptest ....

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah exactly the first param is the test to run. The README.md was a bit poor though so I just pushed update that makes it hopefully clearer.



def _cmd(args):
build(args.source)
print(f"updated kOcspTestRoot kOcspTestRootKey kOcspTestLeaf in {args.source}")


def register(sub):
p = sub.add_parser(
"pki",
help="Regenerate the root CA, root key, and leaf.",
)
p.add_argument("--source", type=Path, required=True, help="Path to ocsptest.c")
p.set_defaults(func=_cmd)