Certificates.Fuzz: expand corpus diversity + drop completed assessment doc (audit follow-up #2)

Expanded the Certificates fuzz corpus from 9 seeds to 21 (+ 12) across
all five categories. The security assessment doc flagged the corpus as
"adequate but limited diversity" -- this commit closes the gap.

Generator (Fuzzing/Opc.Ua.Certificates.Fuzz.Tools/Certificates.Testcases.cs)
now produces, in addition to the original RSA application cert / CRL /
CSR / PEM / extension seeds:

  X509Cert/   certificate-expired.der        RSA cert with NotAfter in past
              certificate-selfsigned.der     RSA cert with no separate issuer
              certificate-ecc.der            P-256 ECC cert with ECC issuer

  X509CRL/    crl-multi-revoked.der          CRL listing 3 revoked entries
              crl-empty.der                  CRL with no revoked entries

  X509CSR/    request-ecc.der                ECC-signed PKCS#10 CSR
              request-multi-san.der          CSR with 4 SAN entries

  PEM/        certificate-ecc.pem            ECC PEM cert
              private-key-ecc.pem            ECC PEM private key

  ASN1/       nested-sequence.der            SEQUENCE containing inner SEQUENCE + OCTET STRING
              large-integer.der              256-bit BigInteger (past AsnReader fast paths)
              subject-alt-name-many.der      SAN with 9 entries (5 DNS + 4 IP)

Exercises code paths the original corpus missed:
- ECDsa decode branches in cert + PEM + PKCS#10 parsers
- CRL revoked-list loop with multiple entries + empty-list boundary
- Validity-window logic for expired cert
- Self-signed (no chain) decode path
- Multi-entry SAN decode loop
- AsnReader.ReadInteger multi-limb branch (past 32 / 64-bit fast paths)
- Nested OCTET STRING containing further DER content

A dedicated ECC issuer ("CN=Fuzzing Test ECC Root") is constructed
because CertificateBuilder.CreateForECDsa() signs with the issuer's
ECDsa private key, so an RSA-only issuer is rejected.

Verified (net10.0):
  - Opc.Ua.Certificates.Fuzz.Tests: 460 passed (up from 220), 0 failed.
    The doubling reflects the 12 new seeds * the harness's
    Fuzz*Assets/FuzzGoodTestcases theory expansion across the 20 cert
    targets.
  - Opc.Ua.Encoders.Fuzz.Tests baseline: 4131/4131 passed.
  - Opc.Ua.Network.Fuzz.Tests baseline: 330/330 passed.
  - Combined: 4921 passed, 0 failed.

DER seed files are committed via `git add -f` because the repo-wide
`*.der` gitignore would otherwise exclude them (matches the original
F2 commit's precedent of force-adding cert test seeds).

Side effect: deleted plans/security-assessment.md. The doc was an
untracked working-copy audit summary that has now had every item
addressed:
  - 7.1 #1 (MUST FIX, parser bound): commit 77441468
  - 7.2 #2 (corpus diversity):       this commit
  - 7.2 #3 (IOORE whitelist):        commit 80832f17
  - 7.3 future work:                 explicitly out-of-scope, tracked
                                     in the PR description instead.

Audit ref: section 7.2 #2 of the security assessment doc.
This commit is contained in:
Marc Schier
2026-06-11 12:16:21 +02:00
parent 80832f1706
commit eba7fc64ab
18 changed files with 198 additions and 43 deletions
@@ -0,0 +1 @@
 �������������������������������
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICVTCCAfugAwIBAgIKYvusxca3VI3UgzAKBggqhkjOPQQDAjA5MRcwFQYDVQQK
Ew5PUEMgRm91bmRhdGlvbjEeMBwGA1UEAxMVRnV6emluZyBUZXN0IEVDQyBSb290
MB4XDTI2MDYxMDEwMTAxOFoXDTI2MDYyMTEwMTAxOFowNDEXMBUGA1UEChMOT1BD
IEZvdW5kYXRpb24xGTAXBgNVBAMTEEZ1enppbmcgVGVzdCBFQ0MwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAAST+hLKMNeX8RHhHzqiFaJwIDeXN9pqxrBnIjABB8TQ
LQcuChVDfvKPsbXxPdA0MtlcYKhd2BfSQgrrSJpuEWVfo4HvMIHsMAwGA1UdEwEB
/wQCMAAwHQYDVR0OBBYEFIEKGI8SMS/38Mv6tzteKPF0fjXOMHQGA1UdIwRtMGuA
FB4nQQHCqSJQ+4rm2JULxAjiTRaUoT2kOzA5MRcwFQYDVQQKEw5PUEMgRm91bmRh
dGlvbjEeMBwGA1UEAxMVRnV6emluZyBUZXN0IEVDQyBSb290ghR9hdEvvFzBuK/k
kqJwx/AREzG6WDAOBgNVHQ8BAf8EBAMCA8gwNwYDVR0RBDAwLoYhdXJuOm9wY2Zv
dW5kYXRpb24ub3JnOmZ1enppbmc6ZWNjgglsb2NhbGhvc3QwCgYIKoZIzj0EAwID
SAAwRQIgXJi8Jdaq0DzrwMrAu1GG2q98D6aSfvKOtvZ0BzCYy1kCIQCagVXXupku
/qdEVzdaTC9ZQfY3nXR5hmFGQyLgcQUmug==
-----END CERTIFICATE-----
@@ -1,24 +1,24 @@
-----BEGIN CERTIFICATE-----
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
rAuaTOpGC6VkvC9d5rX7MtmiuxzBAMfA+ZnA
DQEBCwUAA4IBAQAvw105dzRd3kT2f1yQJThC3J5qO+ZjygPj26Vo9tUTvjJdyzFD
5xEiUl5KIG2zr9MhyXS/hvJd/7M9/QgZxPYBtrkzLK5f8223eLZ1nSKEhp0Ijq8K
foP6Ryf3YpfsBJN1yoRxqh1PBfiYTSk5zXyTM5T4E8CGGCeAUXW1yfVE83WqxTgF
7vU8Nf3ctOinq/FRTF6MseC3mRjT6WuK8qX1Z8kKvAN0y+bvalzcMP1qNS/uinrn
oQT6nZ+Wis34jHdKas+0CPp36ON2ge+b3eFvpykmNqbifgNc+nbzZyTwhCQnQKEX
h08/4ydC8EgN9ZBqpO41grqox+R4UT3SmItd
-----END CERTIFICATE-----
@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIGiAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsh14JVtycxaZATYO
DNP9a378mCN+AXKoK1Nssgw69MCgCgYIKoZIzj0DAQehRANCAAST+hLKMNeX8RHh
HzqiFaJwIDeXN9pqxrBnIjABB8TQLQcuChVDfvKPsbXxPdA0MtlcYKhd2BfSQgrr
SJpuEWVfoA0wCwYDVR0PMQQDAgCA
-----END PRIVATE KEY-----
@@ -1,28 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDgf0mle3Ww+8Zt
5bksTBcj61ARPtExUQ1LM7eDUXsR8A4RJ9/D7LXDiMBSgWilZoMF625USwd3cIaR
/s+kUMWu9CKfMP8mCzyN+RiYcT0FOoutS7cAtnaMfZpY1mktZiif3jnccG9U+Epi
2wv56iheuzHZ8WSzMnDWLkVgqpmk3TwJMQMg898MG8W3wkUEfQusTmWQYfg/S5Fq
VVklRhtXY8C2mTPyOIn9ssKm1O/+FltUjpYGL28AeKJJHL22BFWPkdj3qVc4tpg5
FRqL88d+oL7hNzLt3X9ngmd5TS/gRXfqsh3ZZWSorJCypeBfvnIk1nqkVl0TN5Wn
JfnaORXxAgMBAAECggEADA7KJdk7uQXM4po0F+z+US+SXVrCYaO40xE0ChmWQqBi
IQnDOwK7wITXz5ajyD55SxgaFOzyubeRgsXs4SZ1d7AlK+e1zJVN61I1v/pVDLLY
TbaDXcZvF5ynbZ4WiBm0lOGaufxP/oBXfnfFbObmxC/Jvar5faZ36QkTMN344qHl
iliLFMCQI0fPf4IzPz+vQU42ERLq6YYeHRwrOE89b28/Q+hfGBR+bmIP6n4gJNZh
JgQ6XNvjsntrC9JIo6zzxF21Hw0tgt9MxS/4ueyplzrxhVAeIS7we8uzt3mHffUn
NvLWzYVVQL/wfzLtvLJxBIG+6ZdTAY9BhqEUWvV/AQKBgQD/CXmhiyopXNoJrNgS
EAyUr808QhUnuAtvlAv80y5YP64IGQTeA/fJhVWwgvb5vxKMTIMANfB+ucWKleQ5
XjALEQAGl0jbX2RyyqQYbYfzMEF+ei/mtviuESdOb2khmHi9yIoaEU+29N16PY9s
qAp2qcJqvDXzyrASRxwt5kk5vwKBgQDhWErEsN5MrYPcF5dhZRlNBf92KGkIpEQ7
M8sVA15Tetw3OPOJGQvqJt+FlzWf6+wZxCnP6o4GgPIb0/8sow8YXLeII7NaGY2E
hfN8vpxyRw985JUCz4sYs7ljR8OMkoQ0mUeYs2uV/L2K4jhrmW9+d2Lw0+QmT1i+
SudAmsi8TwKBgHfHv3R7o+WhUlZQ83ooR4BdwQqYDs+ExPo2khrAJHgWEGqFNaAA
RRREpyYrnEtXziJOnQgwTS93mKuqEnJPJklMRrLRwsolV0wCmGdQqhUxiNEaGf1F
2i4GqGtSMBIzRiH8WcA8eS/4eTy8ROxHUALDYCg5FS3W+DLdawKzsZiJAoGAMql0
9c+Fyttfv8WWBRGDpKtqiZgANS8pH94/FBRTypTemZO2POZipVvj+NbL+Y5C4HAX
rmTfVVr8E4GP5usp46JEqNjN473G8TpkSXFZHuAW/U8uqRkb/ou5bDagIiR2kJS9
YflEgM33lxfQ82/lfxs8JUSeEHbaTr3sS7wA0UUCgYEArTuyBWisr66B1h6z+9KG
6aEljrOF3yu+fyKNheT94qq6y7D1oeKZ50WtPnAShdrV9cPl1OUuXKxfc8tIpPvo
G1JESPA7XgDXJcHOe/lzpjhUaAEqIorxvcurVgScLFUqkZfJ3GJDtnTSlq2702ld
W/QO0g+GTeCU349b5k4SODQ=
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDhAap7R9i2M8P8
33UBRXi6fFAbGm49dUTVsR+TzHRJMIysWB/gSLtZjZjbHY6/fz6K6Q52OTv1iIOH
J2ktnzuewUN/0qxIePA2shm/3/lNjida9XNV716lN8StyBLJBkG7OXjI9OnXilxr
0Igu297oRJFxpRodd5OP/6rCeEUwafprPp8siOuXclYP7OKiEdU84fqloV7yaeqA
yFO7l/8ltLGIJkpd4kBZ4Zi/LoRK8BGbOGQnUfLTppgsY554ZWwCKdXeX8B0SXrF
S6zY2QQebpfur/Rd0ONhJwXZy/o4Wjz0j5AqHDn9A6T3OjvwjH8bL+25xlOOK/qu
130f5h5dAgMBAAECggEAd0vF0sUd/kRl5h2n0Hu/7cfZRPV3e3hAfbgCUP92rBNk
kj/K7sRBKNLtN0dtltRlGYtFUqM3yg+OBMt9dWXWW26GVjAhje8pFSbKhjO+30K+
VPiMJQTbvmdaDPVmGFUL5rMq+gj4LoI6TCNtAMMsZSNtpb5uoS7z+sYICEFyFZ8s
WKr3tXNVReZ5MvOFj2EQCZhYenSHCnocJw2dNa6/MaKqItSrVhne0deDhOYTx/IZ
e38IiKaSgfWFX8GbINT/yUBMoVNSi0hnqNQnZptvCibmF8bRN7GnHD0OXJETHtXd
MGw9v4MpFVbDLG9mPid9K/IwyCDrA2+iNBvQHUixlQKBgQDuoc7W5vrKT44GK+mx
gCpfphceVXQTlSPqOw9r1Jwor5J6zIgID4SGpebwPMKTt04QQD04kDpyxE61QTr9
fKBLNpe4OBEYmbEcbzjmogH+IjuCIuWDdcZJX+Gk0M7rtFg0uq7+1EtxOUZgLw5I
LvDzB70jRfn+u7takx6sgPi7UwKBgQDxYfyWh8RZi4caxH8FlWhq8CaIzbZizs5g
g2t4l1zvfHah/kf5+wuIn2Jnwq1MsWJD+ccAH8BmZRYoeThFX0o0FsVIxk4JqxUQ
iINN7viE0gkaY3AE7Id5uoMMXr5U7YxQODditTYPu4LLzEz379zuhJMIf9qK0tF7
Vrp8V8A5jwKBgCD2rxjWjVw3AxptJHoFh1TxH45sJX2zA2QOxoGRvASOxEA8Uqoy
Yo8rCayz5/j5wEnUZeCTY8+IuIqnORvdBFN/dhPXgt0QFfwkjQqm5HmThv7iDSxM
r5hhK01WHs9GeUzHYPcIomdAjy0oMuPbwCMCNalSuEbs7eOEIR+ZLULLAoGBAKsH
5tUx9aoM9vm8aNQn/QhoiKU+PRDdv9UayTd06GC4K8a0SYOSoaV4aV1jzy8cMKhB
fwNTfBwRXBXpbjmZPxaRW9Ao0np/QjmowICj5msV92U/N8wFXCigQuKs7ud2OYlN
i9Dg3SFgl1iXtaGnzz8hqJzJGqxqnAtsdE5OTuF5AoGBANlFoxtCTFS5CYYHl8MG
XTfXA5hGrw0DlszuUj/uSHlkWbxS8ft+7KH5d9vuF3MI6W1MQeRtWDtSofSLDxnz
JYo9A12qVP1suPvHecyNXpOiBEY4t+GTfH1Z0478BAj7kT7WB4ZGsmt16+ug252M
y3A9pEIS7Ia3/nftz26s0wex
-----END PRIVATE KEY-----
@@ -73,6 +73,62 @@ namespace Opc.Ua.Fuzzing
byte[] certificateDer = applicationCertificate.RawData;
WriteTestcase(workPath, "X509Cert", "certificate.der", certificateDer);
// Expired RSA certificate: NotAfter in the past — exercises validity-window logic.
using Certificate expiredCertificate = CertificateBuilder
.Create("CN=Fuzzing Test Expired, O=OPC Foundation")
.SetNotBefore(DateTime.UtcNow.AddDays(-30))
.SetNotAfter(DateTime.UtcNow.AddDays(-1))
.AddExtension(
new X509SubjectAltNameExtension(
"urn:opcfoundation.org:fuzzing:expired",
["localhost"]))
.SetIssuer(issuerCertificate)
.SetRSAKeySize(2048)
.CreateForRSA();
WriteTestcase(workPath, "X509Cert", "certificate-expired.der", expiredCertificate.RawData);
// Self-signed RSA certificate: no separate issuer — exercises the
// self-signature decoder path that skips chain construction.
using Certificate selfSignedCertificate = CertificateBuilder
.Create("CN=Fuzzing Test SelfSigned, O=OPC Foundation")
.SetNotBefore(DateTime.UtcNow.AddDays(-1))
.SetNotAfter(DateTime.UtcNow.AddDays(10))
.AddExtension(
new X509SubjectAltNameExtension(
"urn:opcfoundation.org:fuzzing:selfsigned",
["localhost", "127.0.0.1", "::1"]))
.SetRSAKeySize(2048)
.CreateForRSA();
WriteTestcase(workPath, "X509Cert", "certificate-selfsigned.der", selfSignedCertificate.RawData);
// ECC P-256 application certificate + matching PEM private key.
// Exercises the ECDsa decode branches in cert + PEM parsers.
// A dedicated ECC issuer is required because CertificateBuilder.CreateForECDsa
// signs with the issuer's ECDsa private key — an RSA-only issuer is rejected.
using Certificate eccIssuerCertificate = CertificateBuilder
.Create("CN=Fuzzing Test ECC Root, O=OPC Foundation")
.SetNotBefore(DateTime.UtcNow.AddDays(-1))
.SetNotAfter(DateTime.UtcNow.AddDays(30))
.SetCAConstraint()
.SetECCurve(ECCurve.NamedCurves.nistP256)
.CreateForECDsa();
ICertificateBuilder eccApplicationBuilder = CertificateBuilder
.Create("CN=Fuzzing Test ECC, O=OPC Foundation")
.SetNotBefore(DateTime.UtcNow.AddDays(-1))
.SetNotAfter(DateTime.UtcNow.AddDays(10))
.AddExtension(
new X509SubjectAltNameExtension(
"urn:opcfoundation.org:fuzzing:ecc",
["localhost"]));
eccApplicationBuilder.SetIssuer(eccIssuerCertificate);
using Certificate eccApplicationCertificate = eccApplicationBuilder
.SetECCurve(ECCurve.NamedCurves.nistP256)
.CreateForECDsa();
WriteTestcase(workPath, "X509Cert", "certificate-ecc.der", eccApplicationCertificate.RawData);
// CRL with a single revoked entry (original).
var crlBuilder = CrlBuilder
.Create(issuerCertificate.SubjectName)
.SetThisUpdate(DateTime.UtcNow.AddDays(-1))
@@ -83,17 +139,58 @@ namespace Opc.Ua.Fuzzing
byte[] crlDer = crl.RawData;
WriteTestcase(workPath, "X509CRL", "crl.der", crlDer);
// CRL with multiple revoked entries (exercises the revoked-list loop in EnsureDecoded).
var crlMultiBuilder = CrlBuilder
.Create(issuerCertificate.SubjectName)
.SetThisUpdate(DateTime.UtcNow.AddDays(-2))
.SetNextUpdate(DateTime.UtcNow.AddDays(14))
.AddRevokedCertificate(applicationCertificate)
.AddRevokedCertificate(expiredCertificate)
.AddRevokedCertificate(selfSignedCertificate)
.AddCRLExtension(new X509CrlNumberExtension(new BigInteger(42)));
IX509CRL crlMulti = crlMultiBuilder.CreateForRSA(issuerCertificate);
WriteTestcase(workPath, "X509CRL", "crl-multi-revoked.der", crlMulti.RawData);
// Empty CRL (no revoked entries) — boundary case for the loop.
var crlEmptyBuilder = CrlBuilder
.Create(issuerCertificate.SubjectName)
.SetThisUpdate(DateTime.UtcNow.AddDays(-1))
.SetNextUpdate(DateTime.UtcNow.AddDays(7))
.AddCRLExtension(new X509CrlNumberExtension(BigInteger.Zero));
IX509CRL crlEmpty = crlEmptyBuilder.CreateForRSA(issuerCertificate);
WriteTestcase(workPath, "X509CRL", "crl-empty.der", crlEmpty.RawData);
// RSA CSR (original).
byte[] csrDer = DefaultCertificateFactory.Instance.CreateSigningRequest(
applicationCertificate,
["localhost"]);
WriteTestcase(workPath, "X509CSR", "request.der", csrDer);
// ECC CSR — exercises the ECDsa-signed PKCS#10 decode branch.
byte[] eccCsrDer = DefaultCertificateFactory.Instance.CreateSigningRequest(
eccApplicationCertificate,
["localhost"]);
WriteTestcase(workPath, "X509CSR", "request-ecc.der", eccCsrDer);
// CSR with multiple SAN entries — exercises the extension-request decode loop.
byte[] csrMultiSanDer = DefaultCertificateFactory.Instance.CreateSigningRequest(
applicationCertificate,
["localhost", "127.0.0.1", "::1", "fuzz.example.com"]);
WriteTestcase(workPath, "X509CSR", "request-multi-san.der", csrMultiSanDer);
byte[] certificatePem = PEMWriter.ExportCertificateAsPEM(applicationCertificate);
WriteTestcase(workPath, "PEM", "certificate.pem", certificatePem);
byte[] privateKeyPem = PEMWriter.ExportPrivateKeyAsPEM(applicationCertificate);
WriteTestcase(workPath, "PEM", "private-key.pem", privateKeyPem);
// ECC PEM cert + key.
byte[] eccCertificatePem = PEMWriter.ExportCertificateAsPEM(eccApplicationCertificate);
WriteTestcase(workPath, "PEM", "certificate-ecc.pem", eccCertificatePem);
byte[] eccPrivateKeyPem = PEMWriter.ExportPrivateKeyAsPEM(eccApplicationCertificate);
WriteTestcase(workPath, "PEM", "private-key-ecc.pem", eccPrivateKeyPem);
byte[] subjectAltName = new X509SubjectAltNameExtension(
"urn:opcfoundation.org:fuzzing",
["localhost", "127.0.0.1"]).RawData;
@@ -107,6 +204,16 @@ namespace Opc.Ua.Fuzzing
WriteTestcase(workPath, "ASN1", "crl-number.der", crlNumber);
WriteTestcase(workPath, "ASN1", "sequence.der", CreateSequenceSeed());
// Additional ASN.1 boundary seeds — exercise the AsnReader entry points
// with structural variants beyond the canonical extension layouts.
WriteTestcase(workPath, "ASN1", "nested-sequence.der", CreateNestedSequenceSeed());
WriteTestcase(workPath, "ASN1", "large-integer.der", CreateLargeIntegerSeed());
WriteTestcase(workPath, "ASN1", "subject-alt-name-many.der",
new X509SubjectAltNameExtension(
"urn:opcfoundation.org:fuzzing:many",
["host-a", "host-b", "host-c", "host-d", "host-e",
"127.0.0.1", "::1", "10.0.0.1", "10.0.0.2"]).RawData);
FuzzableCode.FuzzX509CRLCore(crlDer);
FuzzableCode.FuzzX509SubjectAltNameExtensionCore(subjectAltName);
FuzzableCode.FuzzX509AuthorityKeyIdentifierExtensionCore(authorityKeyIdentifier);
@@ -140,5 +247,30 @@ namespace Opc.Ua.Fuzzing
writer.PopSequence();
return writer.Encode();
}
private static byte[] CreateNestedSequenceSeed()
{
var writer = new AsnWriter(AsnEncodingRules.DER);
writer.PushSequence();
writer.WriteInteger(2);
writer.PushSequence();
writer.WriteObjectIdentifier(X509SubjectAltNameExtension.SubjectAltName2Oid);
writer.PushOctetString();
writer.WriteInteger(0x7fffffff);
writer.PopOctetString();
writer.PopSequence();
writer.PopSequence();
return writer.Encode();
}
private static byte[] CreateLargeIntegerSeed()
{
var writer = new AsnWriter(AsnEncodingRules.DER);
// 256-bit BigInteger — exercises AsnReader.ReadInteger past the
// 32 / 64-bit fast paths into the multi-limb branch.
var big = (BigInteger.One << 255) - BigInteger.One;
writer.WriteInteger(big);
return writer.Encode();
}
}
}