diff --git a/.github/workflows/no-malloc.yml b/.github/workflows/no-malloc.yml
index d6364534c1e..2fce3599b7a 100644
--- a/.github/workflows/no-malloc.yml
+++ b/.github/workflows/no-malloc.yml
@@ -74,6 +74,14 @@ jobs:
"--enable-curve448", "--enable-mlkem", "--enable-staticmemory",
"CFLAGS=-DWOLFSSL_NO_MALLOC -pedantic -Wdeclaration-after-statement -Wnull-dereference -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"],
"check": false,
+ "run": [["./wolfcrypt/test/testwolfcrypt"]]},
+ {"name": "tps-staticmemory", "minutes": 0.8,
+ "configure": ["--enable-ecc", "--enable-rsa", "--enable-keygen",
+ "--enable-ed25519", "--enable-curve25519", "--enable-ed448",
+ "--enable-curve448", "--enable-mlkem", "--enable-tsp",
+ "--enable-staticmemory",
+ "CFLAGS=-DWOLFSSL_NO_MALLOC -pedantic -Wdeclaration-after-statement -Wnull-dereference -DTEST_LIBWOLFSSL_SOURCES_INCLUSION_SEQUENCE"],
+ "check": false,
"run": [["./wolfcrypt/test/testwolfcrypt"]]}
]
EOF
diff --git a/.github/workflows/os-check.yml b/.github/workflows/os-check.yml
index a6b9b945940..6ca2f132c47 100644
--- a/.github/workflows/os-check.yml
+++ b/.github/workflows/os-check.yml
@@ -306,6 +306,37 @@ jobs:
{"name": "pkcs7", "minutes": 1.3,
"comment": "PKCS#7 without RSA-PSS",
"configure": ["--enable-pkcs7"]},
+ {"name": "tsp", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol",
+ "configure": ["--enable-tsp"]},
+ {"name": "tsp-openssl", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol with OpenSSL compat",
+ "configure": ["--enable-tsp", "--enable-opensslall"]},
+ {"name": "tsp-no-ecc", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol without ECC",
+ "configure": ["--enable-tsp", "--disable-ecc"]},
+ {"name": "tsp-no-rsa", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol without RSA",
+ "configure": ["--enable-tsp", "--disable-rsa"]},
+ {"name": "tsp-smallstack", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol Small Stack",
+ "configure": ["--enable-tsp", "CPPFLAGS=-DWOLFSSL_SMALL_STACK"]},
+ {"name": "tsp-min-hash-str", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol Minimum 128-bit hash strength",
+ "configure": ["--enable-tsp",
+ "CPPFLAGS=-DWC_TSP_MIN_HASH_STRENGTH_BITS=128"]},
+ {"name": "tsp-requester", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol Requester",
+ "configure": ["--enable-tsp", "--enable-opensslall",
+ "CPPFLAGS=-DWOLFSSL_TSP_REQUESTER"]},
+ {"name": "tsp-responder", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol Responder",
+ "configure": ["--enable-tsp", "--enable-opensslall",
+ "CPPFLAGS=-DWOLFSSL_TSP_RESPONDER"]},
+ {"name": "tsp-verifier", "minutes": 1.3,
+ "comment": "Time-Stamp Protocol Verifier",
+ "configure": ["--enable-tsp", "--enable-opensslall",
+ "CPPFLAGS=-DWOLFSSL_TSP_VERIFIER"]},
{"name": "no-tls-cryptocb-aesgcm-setkey-free", "minutes": 1.3,
"configure": ["--disable-tls", "--enable-cryptocb", "--enable-aesgcm",
"CPPFLAGS=-DWOLF_CRYPTO_CB_AES_SETKEY -DWOLF_CRYPTO_CB_FREE"]},
diff --git a/.gitignore b/.gitignore
index 693d9d5a843..a021acea6d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -72,6 +72,9 @@ examples/sctp/sctp-client-dtls
examples/asn1/asn1
examples/pem/pem
examples/ocsp_responder/ocsp_responder
+examples/tsp/tsp_query
+examples/tsp/tsp_reply
+examples/tsp/tsp_verify
server_ready
snifftest
output
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ecbf163321..c0e2d83a69b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2021,6 +2021,9 @@ endif()
set(WOLFSSL_PKCS7_HELP_STRING "Enable PKCS7 (default: disabled)")
add_option(WOLFSSL_PKCS7 ${WOLFSSL_PKCS7_HELP_STRING} "no" "yes;no")
+set(WOLFSSL_TSP_HELP_STRING "Enable RFC 3161 Time-Stamp Protocol (default: disabled)")
+add_option(WOLFSSL_TSP ${WOLFSSL_TSP_HELP_STRING} "no" "yes;no")
+
set(WOLFSSL_TPM_HELP_STRING "Enable wolfTPM options (default: disabled)")
add_option(WOLFSSL_TPM ${WOLFSSL_TPM_HELP_STRING} "no" "yes;no")
@@ -2412,6 +2415,12 @@ if(WOLFSSL_AESCFB)
endif()
+if(WOLFSSL_TSP)
+ list(APPEND WOLFSSL_DEFINITIONS "-DWOLFSSL_TSP")
+ # Requires PKCS7 for time-stamp token creation and verification
+ override_cache(WOLFSSL_PKCS7 "yes")
+endif()
+
if(WOLFSSL_PKCS7)
list(APPEND WOLFSSL_DEFINITIONS "-DHAVE_PKCS7")
override_cache(WOLFSSL_AESKEYWRAP "yes")
@@ -2965,6 +2974,8 @@ if(WOLFSSL_EXAMPLES)
tests/api/test_asn.c
tests/api/test_pkcs7.c
tests/api/test_pkcs12.c
+ tests/api/test_tsp.c
+ tests/api/test_ossl_tsp.c
tests/api/test_pwdbased.c
tests/api/test_ossl_asn1.c
tests/api/test_ossl_bio.c
diff --git a/IDE/INTIME-RTOS/libwolfssl.vcxproj b/IDE/INTIME-RTOS/libwolfssl.vcxproj
index 28671f46d6d..4c238d4ac57 100644
--- a/IDE/INTIME-RTOS/libwolfssl.vcxproj
+++ b/IDE/INTIME-RTOS/libwolfssl.vcxproj
@@ -87,6 +87,7 @@
+
diff --git a/IDE/INTIME-RTOS/wolfssl-lib.vcxproj b/IDE/INTIME-RTOS/wolfssl-lib.vcxproj
index 4d711e21023..9b09873a845 100644
--- a/IDE/INTIME-RTOS/wolfssl-lib.vcxproj
+++ b/IDE/INTIME-RTOS/wolfssl-lib.vcxproj
@@ -185,6 +185,7 @@
+
diff --git a/IDE/WIN10/wolfssl-fips.vcxproj b/IDE/WIN10/wolfssl-fips.vcxproj
index 2ae14cc3b16..4d9cfb995a6 100644
--- a/IDE/WIN10/wolfssl-fips.vcxproj
+++ b/IDE/WIN10/wolfssl-fips.vcxproj
@@ -285,6 +285,7 @@
+
diff --git a/certs/include.am b/certs/include.am
index 6afa36129be..97d8bfe75a7 100644
--- a/certs/include.am
+++ b/certs/include.am
@@ -37,6 +37,14 @@ EXTRA_DIST += \
certs/client-ca-cert.pem \
certs/dh2048.pem \
certs/server-cert.pem \
+ certs/tsa-bad-ku-cert.pem \
+ certs/tsa-extra-eku-cert.pem \
+ certs/tsa-chain-cert.pem \
+ certs/tsa-chain-key.pem \
+ certs/tsa-cert.pem \
+ certs/tsa-ecc-cert.pem \
+ certs/tsa-ecc-key.pem \
+ certs/tsa-key.pem \
certs/server-ecc.pem \
certs/server-ecc-self.pem \
certs/server-ecc-comp.pem \
@@ -119,6 +127,14 @@ EXTRA_DIST += \
certs/ecc-keyPub.der \
certs/server-key.der \
certs/server-cert.der \
+ certs/tsa-key.der \
+ certs/tsa-cert.der \
+ certs/tsa-ecc-key.der \
+ certs/tsa-ecc-cert.der \
+ certs/tsa-bad-ku-cert.der \
+ certs/tsa-extra-eku-cert.der \
+ certs/tsa-chain-cert.der \
+ certs/tsa-chain-key.der \
certs/server-ecc-comp.der \
certs/server-ecc.der \
certs/server-ecc-self.der \
diff --git a/certs/renewcerts.sh b/certs/renewcerts.sh
index 397b989d44b..18520e4b93c 100755
--- a/certs/renewcerts.sh
+++ b/certs/renewcerts.sh
@@ -39,6 +39,16 @@
# aia/multi-aia-cert.pem
# aia/overflow-aia-cert.pem
# sia/timestamping-sia-cert.pem
+# tsa-cert.pem
+# tsa-cert.der
+# tsa-ecc-cert.pem
+# tsa-ecc-cert.der
+# tsa-bad-ku-cert.pem
+# tsa-bad-ku-cert.der
+# tsa-extra-eku-cert.pem
+# tsa-extra-eku-cert.der
+# tsa-chain-cert.pem
+# tsa-chain-cert.der
# updates the following crls:
# crl/cliCrl.pem
# crl/crl.pem
@@ -216,6 +226,112 @@ run_renewcerts(){
echo "End of section"
echo "---------------------------------------------------------------------"
+ ############################################################
+ ######## update the self-signed (2048-bit) tsa-cert.pem ###
+ ############################################################
+ echo "Updating 2048-bit tsa-cert.pem"
+ echo ""
+ openssl req -new -key tsa-key.pem -config ./renewcerts/wolfssl.cnf -nodes -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out tsa-cert.csr
+ check_result $? "Step 1"
+
+ openssl x509 -req -in tsa-cert.csr -days 1000 -extfile ./renewcerts/wolfssl.cnf -extensions tsa_cert -signkey tsa-key.pem -out tsa-cert.pem
+ check_result $? "Step 2"
+ rm tsa-cert.csr
+
+ openssl x509 -in tsa-cert.pem -text > tmp.pem
+ check_result $? "Step 3"
+ mv tmp.pem tsa-cert.pem
+
+ openssl x509 -in tsa-cert.pem -outform der -out tsa-cert.der
+ check_result $? "Step 4"
+ echo "End of section"
+ echo "---------------------------------------------------------------------"
+
+ ############################################################
+ ## update the intermediate-issued tsa-chain-cert.pem ######
+ ############################################################
+ echo "Updating 2048-bit tsa-chain-cert.pem"
+ echo ""
+ openssl req -new -key tsa-chain-key.pem -config ./renewcerts/wolfssl.cnf -nodes -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-chain-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out tsa-chain-cert.csr
+ check_result $? "Step 1"
+
+ openssl x509 -req -in tsa-chain-cert.csr -days 1000 -extfile ./renewcerts/wolfssl.cnf -extensions tsa_cert -CA intermediate/ca-int-cert.pem -CAkey intermediate/ca-int-key.pem -CAcreateserial -out tsa-chain-cert.pem
+ check_result $? "Step 2"
+ rm tsa-chain-cert.csr
+ rm -f intermediate/ca-int-cert.srl
+
+ openssl x509 -in tsa-chain-cert.pem -text > tmp.pem
+ check_result $? "Step 3"
+ mv tmp.pem tsa-chain-cert.pem
+
+ openssl x509 -in tsa-chain-cert.pem -outform der -out tsa-chain-cert.der
+ check_result $? "Step 4"
+ echo "End of section"
+ echo "---------------------------------------------------------------------"
+
+ ############################################################
+ ########## update the self-signed tsa-ecc-cert.pem ########
+ ############################################################
+ echo "Updating tsa-ecc-cert.pem"
+ echo ""
+ openssl req -new -key tsa-ecc-key.pem -config ./renewcerts/wolfssl.cnf -nodes -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-ECC/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out tsa-ecc-cert.csr
+ check_result $? "Step 1"
+
+ openssl x509 -req -in tsa-ecc-cert.csr -days 1000 -extfile ./renewcerts/wolfssl.cnf -extensions tsa_cert -signkey tsa-ecc-key.pem -out tsa-ecc-cert.pem
+ check_result $? "Step 2"
+ rm tsa-ecc-cert.csr
+
+ openssl x509 -in tsa-ecc-cert.pem -text > tmp.pem
+ check_result $? "Step 3"
+ mv tmp.pem tsa-ecc-cert.pem
+
+ openssl x509 -in tsa-ecc-cert.pem -outform der -out tsa-ecc-cert.der
+ check_result $? "Step 4"
+ echo "End of section"
+ echo "---------------------------------------------------------------------"
+
+ ############################################################
+ ## update the self-signed (2048-bit) tsa-bad-ku-cert.pem ##
+ ############################################################
+ echo "Updating 2048-bit tsa-bad-ku-cert.pem"
+ echo ""
+ openssl req -new -key tsa-key.pem -config ./renewcerts/wolfssl.cnf -nodes -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-bad-ku-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out tsa-bad-ku-cert.csr
+ check_result $? "Step 1"
+
+ openssl x509 -req -in tsa-bad-ku-cert.csr -days 1000 -extfile ./renewcerts/wolfssl.cnf -extensions tsa_bad_ku_cert -signkey tsa-key.pem -out tsa-bad-ku-cert.pem
+ check_result $? "Step 2"
+ rm tsa-bad-ku-cert.csr
+
+ openssl x509 -in tsa-bad-ku-cert.pem -text > tmp.pem
+ check_result $? "Step 3"
+ mv tmp.pem tsa-bad-ku-cert.pem
+
+ openssl x509 -in tsa-bad-ku-cert.pem -outform der -out tsa-bad-ku-cert.der
+ check_result $? "Step 4"
+ echo "End of section"
+ echo "---------------------------------------------------------------------"
+
+ ###############################################################
+ ## update the self-signed (2048-bit) tsa-extra-eku-cert.pem ##
+ ###############################################################
+ echo "Updating 2048-bit tsa-extra-eku-cert.pem"
+ echo ""
+ openssl req -new -key tsa-key.pem -config ./renewcerts/wolfssl.cnf -nodes -subj "/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-extra-eku-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" -out tsa-extra-eku-cert.csr
+ check_result $? "Step 1"
+
+ openssl x509 -req -in tsa-extra-eku-cert.csr -days 1000 -extfile ./renewcerts/wolfssl.cnf -extensions tsa_extra_eku_cert -signkey tsa-key.pem -out tsa-extra-eku-cert.pem
+ check_result $? "Step 2"
+ rm tsa-extra-eku-cert.csr
+
+ openssl x509 -in tsa-extra-eku-cert.pem -text > tmp.pem
+ check_result $? "Step 3"
+ mv tmp.pem tsa-extra-eku-cert.pem
+
+ openssl x509 -in tsa-extra-eku-cert.pem -outform der -out tsa-extra-eku-cert.der
+ check_result $? "Step 4"
+ echo "End of section"
+ echo "---------------------------------------------------------------------"
+
############################################################
#### update the self-signed (1024-bit) client-cert.pem #####
############################################################
diff --git a/certs/renewcerts/wolfssl.cnf b/certs/renewcerts/wolfssl.cnf
index 70453f9369f..106abeb6b5d 100644
--- a/certs/renewcerts/wolfssl.cnf
+++ b/certs/renewcerts/wolfssl.cnf
@@ -452,3 +452,30 @@ DNS.1 = www.example.org
URI.1 = https://www.wolfssl.com/
otherName.2 = 2.16.840.1.101.3.6.6;FORMAT:HEX,OCT:D1:38:10:D8:28:AF:2C:10:84:35:15:A1:68:58:28:AF:02:10:86:A2:84:E7:39:C3:EB
+
+# TSA certificate extensions - RFC 3161 time-stamping only
+[ tsa_cert ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:false
+subjectAltName = DNS:tsa.wolfssl.com
+keyUsage = critical, digitalSignature
+extendedKeyUsage = critical, timeStamping
+
+# TSA certificate extensions with wrong key usage - for failure testing
+[ tsa_bad_ku_cert ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:false
+keyUsage = critical, keyEncipherment
+extendedKeyUsage = critical, timeStamping
+
+# TSA certificate extensions with an extra (non-timeStamping) extended key
+# usage - for failure testing. The extra purpose is an unrecognized OID so the
+# time-stamping bit is still the only one set; rejection relies on the count.
+[ tsa_extra_eku_cert ]
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+basicConstraints = CA:false
+keyUsage = critical, digitalSignature
+extendedKeyUsage = critical, timeStamping, 1.3.6.1.4.1.99999.1
diff --git a/certs/tsa-bad-ku-cert.der b/certs/tsa-bad-ku-cert.der
new file mode 100644
index 00000000000..fffb9d9afd1
Binary files /dev/null and b/certs/tsa-bad-ku-cert.der differ
diff --git a/certs/tsa-bad-ku-cert.pem b/certs/tsa-bad-ku-cert.pem
new file mode 100644
index 00000000000..31756dfe10c
--- /dev/null
+++ b/certs/tsa-bad-ku-cert.pem
@@ -0,0 +1,93 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 7d:ef:e7:fe:70:74:73:af:df:71:6b:d3:ba:fb:d0:4b:a3:50:26:d3
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-bad-ku-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Validity
+ Not Before: Jun 4 20:35:37 2026 GMT
+ Not After : Feb 28 20:35:37 2029 GMT
+ Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-bad-ku-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:93:74:96:f0:0c:fd:d7:2a:5a:a0:a1:6e:b4:63:
+ b7:f4:e6:8b:77:8f:49:f7:a9:0c:b3:3d:5a:60:a8:
+ b8:67:75:b8:d0:ce:ba:ce:65:e5:64:a2:d0:c8:d0:
+ 2c:5b:a8:a5:d3:3f:2c:46:07:e6:b1:b7:a5:65:f8:
+ 9f:89:e1:93:bc:49:cd:55:fd:9b:a1:1e:1e:f7:99:
+ 07:4a:1c:c8:b6:88:a2:5e:77:a5:3f:79:2f:d3:43:
+ 41:f6:cf:ff:78:30:89:12:b1:0c:3f:f5:2f:62:e6:
+ 71:33:02:70:d9:42:1b:e4:07:9c:9d:c7:5e:5b:4e:
+ 93:8f:6b:0e:fe:5b:7e:02:6f:8d:e8:7d:e9:aa:1d:
+ 12:f2:6b:a4:55:61:59:e0:3a:38:b0:14:41:9d:bb:
+ c6:e0:6a:9d:35:a8:08:f2:3c:29:b9:a7:b2:38:47:
+ f6:70:1a:b8:d2:d5:c9:48:f0:ee:60:1d:14:77:8a:
+ 2c:38:90:29:7f:42:e4:dd:92:0a:8d:03:88:44:0a:
+ f0:b6:14:bd:e5:11:50:94:26:fb:3e:ab:17:69:3d:
+ a6:45:c7:ad:86:41:66:e6:53:e5:dc:d8:4a:04:c7:
+ af:fb:a7:df:99:77:57:d9:25:7c:67:73:3d:ee:60:
+ f3:63:2d:68:0f:97:da:68:d5:3d:e5:06:92:82:dd:
+ 90:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ C0:19:81:BB:D6:3D:BB:41:E8:22:1A:08:50:AD:57:FB:E5:B5:EA:9D
+ X509v3 Authority Key Identifier:
+ keyid:C0:19:81:BB:D6:3D:BB:41:E8:22:1A:08:50:AD:57:FB:E5:B5:EA:9D
+ DirName:/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-bad-ku-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com
+ serial:7D:EF:E7:FE:70:74:73:AF:DF:71:6B:D3:BA:FB:D0:4B:A3:50:26:D3
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage: critical
+ Key Encipherment
+ X509v3 Extended Key Usage: critical
+ Time Stamping
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 1c:b5:98:0a:98:28:90:9f:7c:ff:d9:d4:3b:92:3d:b3:7c:d6:
+ 9b:b4:88:59:0d:cd:58:7a:6a:ad:f0:6f:f3:ed:5e:57:d7:14:
+ 3e:64:50:1c:11:33:ab:62:09:f1:8f:58:15:93:25:20:ed:f3:
+ cb:ed:a3:d2:21:7c:c2:02:14:fa:61:cf:20:13:8a:6b:f1:95:
+ 81:94:e8:d7:fd:24:13:fb:87:e3:2c:fb:4e:d7:ce:46:ab:fd:
+ 21:bc:93:b8:0d:88:2a:76:b6:02:f9:ef:58:fc:36:b5:11:5a:
+ 07:62:0d:a3:1a:e6:77:a7:28:b2:0e:c6:b1:a4:66:52:99:11:
+ 90:11:4a:d7:98:e5:9f:b1:e7:99:fe:a6:66:66:5e:1e:52:bb:
+ b8:e7:bd:e9:95:d4:03:47:de:c7:cd:f7:58:67:af:12:57:28:
+ 33:a7:34:ef:74:2f:6c:67:43:29:6d:57:80:b4:2d:67:21:2a:
+ 52:41:97:1d:2d:af:2c:c7:1f:c8:20:6d:9a:79:82:f7:a6:3b:
+ 97:5b:1a:bc:f4:2a:d9:df:a6:45:db:a2:c1:83:5a:39:ef:f9:
+ 6f:f7:14:42:30:0f:52:71:6e:6b:05:19:ca:51:4e:a0:f1:4a:
+ ba:6f:95:46:3d:ea:1a:ab:e3:37:bd:30:ba:b9:b5:30:fd:97:
+ e4:7a:49:d4
+-----BEGIN CERTIFICATE-----
+MIIE8zCCA9ugAwIBAgIUfe/n/nB0c6/fcWvTuvvQS6NQJtMwDQYJKoZIhvcNAQEL
+BQAwgZgxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC
+b3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMRgwFgYDVQQLDA9UU0EtYmFkLWt1LTIw
+NDgxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5m
+b0B3b2xmc3NsLmNvbTAeFw0yNjA2MDQyMDM1MzdaFw0yOTAyMjgyMDM1MzdaMIGY
+MQswCQYDVQQGEwJVUzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1h
+bjEQMA4GA1UECgwHd29sZlNTTDEYMBYGA1UECwwPVFNBLWJhZC1rdS0yMDQ4MRgw
+FgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29s
+ZnNzbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTdJbwDP3X
+KlqgoW60Y7f05ot3j0n3qQyzPVpgqLhndbjQzrrOZeVkotDI0CxbqKXTPyxGB+ax
+t6Vl+J+J4ZO8Sc1V/ZuhHh73mQdKHMi2iKJed6U/eS/TQ0H2z/94MIkSsQw/9S9i
+5nEzAnDZQhvkB5ydx15bTpOPaw7+W34Cb43ofemqHRLya6RVYVngOjiwFEGdu8bg
+ap01qAjyPCm5p7I4R/ZwGrjS1clI8O5gHRR3iiw4kCl/QuTdkgqNA4hECvC2FL3l
+EVCUJvs+qxdpPaZFx62GQWbmU+Xc2EoEx6/7p9+Zd1fZJXxncz3uYPNjLWgPl9po
+1T3lBpKC3ZAlAgMBAAGjggExMIIBLTAdBgNVHQ4EFgQUwBmBu9Y9u0HoIhoIUK1X
+++W16p0wgdgGA1UdIwSB0DCBzYAUwBmBu9Y9u0HoIhoIUK1X++W16p2hgZ6kgZsw
+gZgxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3pl
+bWFuMRAwDgYDVQQKDAd3b2xmU1NMMRgwFgYDVQQLDA9UU0EtYmFkLWt1LTIwNDgx
+GDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3
+b2xmc3NsLmNvbYIUfe/n/nB0c6/fcWvTuvvQS6NQJtMwCQYDVR0TBAIwADAOBgNV
+HQ8BAf8EBAMCBSAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQEL
+BQADggEBABy1mAqYKJCffP/Z1DuSPbN81pu0iFkNzVh6aq3wb/PtXlfXFD5kUBwR
+M6tiCfGPWBWTJSDt88vto9IhfMICFPphzyATimvxlYGU6Nf9JBP7h+Ms+07Xzkar
+/SG8k7gNiCp2tgL571j8NrURWgdiDaMa5nenKLIOxrGkZlKZEZARSteY5Z+x55n+
+pmZmXh5Su7jnvemV1ANH3sfN91hnrxJXKDOnNO90L2xnQyltV4C0LWchKlJBlx0t
+ryzHH8ggbZp5gvemO5dbGrz0KtnfpkXbosGDWjnv+W/3FEIwD1JxbmsFGcpRTqDx
+SrpvlUY96hqr4ze9MLq5tTD9l+R6SdQ=
+-----END CERTIFICATE-----
diff --git a/certs/tsa-cert.der b/certs/tsa-cert.der
new file mode 100644
index 00000000000..64f05c0f87d
Binary files /dev/null and b/certs/tsa-cert.der differ
diff --git a/certs/tsa-cert.pem b/certs/tsa-cert.pem
new file mode 100644
index 00000000000..42350394ac4
--- /dev/null
+++ b/certs/tsa-cert.pem
@@ -0,0 +1,95 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 27:a1:f0:c9:99:87:1d:e9:ca:22:54:48:43:86:ef:5d:31:15:f5:5f
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Validity
+ Not Before: Jun 4 20:44:10 2026 GMT
+ Not After : Feb 28 20:44:10 2029 GMT
+ Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:93:74:96:f0:0c:fd:d7:2a:5a:a0:a1:6e:b4:63:
+ b7:f4:e6:8b:77:8f:49:f7:a9:0c:b3:3d:5a:60:a8:
+ b8:67:75:b8:d0:ce:ba:ce:65:e5:64:a2:d0:c8:d0:
+ 2c:5b:a8:a5:d3:3f:2c:46:07:e6:b1:b7:a5:65:f8:
+ 9f:89:e1:93:bc:49:cd:55:fd:9b:a1:1e:1e:f7:99:
+ 07:4a:1c:c8:b6:88:a2:5e:77:a5:3f:79:2f:d3:43:
+ 41:f6:cf:ff:78:30:89:12:b1:0c:3f:f5:2f:62:e6:
+ 71:33:02:70:d9:42:1b:e4:07:9c:9d:c7:5e:5b:4e:
+ 93:8f:6b:0e:fe:5b:7e:02:6f:8d:e8:7d:e9:aa:1d:
+ 12:f2:6b:a4:55:61:59:e0:3a:38:b0:14:41:9d:bb:
+ c6:e0:6a:9d:35:a8:08:f2:3c:29:b9:a7:b2:38:47:
+ f6:70:1a:b8:d2:d5:c9:48:f0:ee:60:1d:14:77:8a:
+ 2c:38:90:29:7f:42:e4:dd:92:0a:8d:03:88:44:0a:
+ f0:b6:14:bd:e5:11:50:94:26:fb:3e:ab:17:69:3d:
+ a6:45:c7:ad:86:41:66:e6:53:e5:dc:d8:4a:04:c7:
+ af:fb:a7:df:99:77:57:d9:25:7c:67:73:3d:ee:60:
+ f3:63:2d:68:0f:97:da:68:d5:3d:e5:06:92:82:dd:
+ 90:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ C0:19:81:BB:D6:3D:BB:41:E8:22:1A:08:50:AD:57:FB:E5:B5:EA:9D
+ X509v3 Authority Key Identifier:
+ keyid:C0:19:81:BB:D6:3D:BB:41:E8:22:1A:08:50:AD:57:FB:E5:B5:EA:9D
+ DirName:/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com
+ serial:27:A1:F0:C9:99:87:1D:E9:CA:22:54:48:43:86:EF:5D:31:15:F5:5F
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Alternative Name:
+ DNS:tsa.wolfssl.com
+ X509v3 Key Usage: critical
+ Digital Signature
+ X509v3 Extended Key Usage: critical
+ Time Stamping
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 1a:c2:d0:93:e8:65:f7:77:2c:62:b2:9c:e0:64:ec:18:96:5e:
+ 89:ba:58:25:12:a9:f7:27:da:70:4a:b7:6d:0e:86:6d:61:5c:
+ 73:9e:55:46:f1:6e:f4:06:b2:08:e5:8f:93:bf:ab:b4:60:dd:
+ 95:d5:89:c4:16:fa:c4:c6:e5:1f:31:4a:75:cb:11:f5:16:5c:
+ c9:19:8b:bc:80:2a:61:c0:12:8e:71:59:ba:1e:5a:c6:6b:62:
+ e7:f0:a9:f2:cd:81:2d:41:37:13:59:3a:6f:63:09:b6:5e:78:
+ 80:d3:18:72:89:90:51:fa:6f:1d:99:43:fd:5f:17:28:19:0b:
+ 48:14:87:46:09:29:e7:d3:79:04:7e:46:af:30:eb:e9:89:0c:
+ 7f:26:b3:c4:66:c7:37:9a:8b:42:bb:98:21:73:1a:fe:22:2d:
+ 80:f6:78:8c:d4:80:a8:5c:b5:cb:e6:a8:b7:fb:98:cd:5d:d1:
+ e4:37:be:36:cf:97:c1:d0:a9:f2:64:90:dc:b8:57:44:c2:e8:
+ 95:50:18:1d:0d:8a:ea:9f:12:0b:e4:1e:78:9e:f5:88:5b:22:
+ 00:a8:06:aa:af:1a:2b:0c:3a:2c:f1:0a:cd:0a:af:0e:86:e5:
+ 5c:34:e7:c7:64:48:39:9b:c4:60:40:41:2f:bd:65:20:66:54:
+ f2:37:5c:7b
+-----BEGIN CERTIFICATE-----
+MIIE+jCCA+KgAwIBAgIUJ6HwyZmHHenKIlRIQ4bvXTEV9V8wDQYJKoZIhvcNAQEL
+BQAwgZExCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC
+b3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMREwDwYDVQQLDAhUU0EtMjA0ODEYMBYG
+A1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZz
+c2wuY29tMB4XDTI2MDYwNDIwNDQxMFoXDTI5MDIyODIwNDQxMFowgZExCzAJBgNV
+BAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYD
+VQQKDAd3b2xmU1NMMREwDwYDVQQLDAhUU0EtMjA0ODEYMBYGA1UEAwwPd3d3Lndv
+bGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk3SW8Az91ypaoKFutGO39OaLd49J
+96kMsz1aYKi4Z3W40M66zmXlZKLQyNAsW6il0z8sRgfmsbelZfifieGTvEnNVf2b
+oR4e95kHShzItoiiXnelP3kv00NB9s//eDCJErEMP/UvYuZxMwJw2UIb5Aecncde
+W06Tj2sO/lt+Am+N6H3pqh0S8mukVWFZ4Do4sBRBnbvG4GqdNagI8jwpuaeyOEf2
+cBq40tXJSPDuYB0Ud4osOJApf0Lk3ZIKjQOIRArwthS95RFQlCb7PqsXaT2mRcet
+hkFm5lPl3NhKBMev+6ffmXdX2SV8Z3M97mDzYy1oD5faaNU95QaSgt2QJQIDAQAB
+o4IBRjCCAUIwHQYDVR0OBBYEFMAZgbvWPbtB6CIaCFCtV/vlteqdMIHRBgNVHSME
+gckwgcaAFMAZgbvWPbtB6CIaCFCtV/vlteqdoYGXpIGUMIGRMQswCQYDVQQGEwJV
+UzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjEQMA4GA1UECgwH
+d29sZlNTTDERMA8GA1UECwwIVFNBLTIwNDgxGDAWBgNVBAMMD3d3dy53b2xmc3Ns
+LmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIUJ6HwyZmHHenK
+IlRIQ4bvXTEV9V8wCQYDVR0TBAIwADAaBgNVHREEEzARgg90c2Eud29sZnNzbC5j
+b20wDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA0GCSqG
+SIb3DQEBCwUAA4IBAQAawtCT6GX3dyxispzgZOwYll6JulglEqn3J9pwSrdtDoZt
+YVxznlVG8W70BrII5Y+Tv6u0YN2V1YnEFvrExuUfMUp1yxH1FlzJGYu8gCphwBKO
+cVm6HlrGa2Ln8KnyzYEtQTcTWTpvYwm2XniA0xhyiZBR+m8dmUP9XxcoGQtIFIdG
+CSnn03kEfkavMOvpiQx/JrPEZsc3motCu5ghcxr+Ii2A9niM1ICoXLXL5qi3+5jN
+XdHkN742z5fB0KnyZJDcuFdEwuiVUBgdDYrqnxIL5B54nvWIWyIAqAaqrxorDDos
+8QrNCq8OhuVcNOfHZEg5m8RgQEEvvWUgZlTyN1x7
+-----END CERTIFICATE-----
diff --git a/certs/tsa-chain-cert.der b/certs/tsa-chain-cert.der
new file mode 100644
index 00000000000..9157423ec33
Binary files /dev/null and b/certs/tsa-chain-cert.der differ
diff --git a/certs/tsa-chain-cert.pem b/certs/tsa-chain-cert.pem
new file mode 100644
index 00000000000..62eee3d327a
--- /dev/null
+++ b/certs/tsa-chain-cert.pem
@@ -0,0 +1,95 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 75:ad:1d:25:bd:c8:57:1f:44:1e:4f:c1:d8:6f:3a:98:2e:01:25:7e
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Washington, L=Seattle, O=wolfSSL, OU=Development, CN=wolfSSL Intermediate CA, emailAddress=info@wolfssl.com
+ Validity
+ Not Before: Jun 16 01:06:26 2026 GMT
+ Not After : Mar 12 01:06:26 2029 GMT
+ Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-chain-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:b2:bb:66:ac:49:bb:8c:5d:e8:ee:ff:55:12:b9:
+ 6e:b0:e8:9a:77:35:7a:b8:65:85:62:a6:17:1b:27:
+ 25:8c:59:bb:1e:e6:bb:ae:09:55:7d:ee:53:fd:ff:
+ 56:99:9d:0c:1e:31:48:bd:42:69:2c:b2:0a:37:68:
+ 5b:8f:33:ff:c5:a0:e0:e2:1e:6c:f6:4f:90:b4:73:
+ 3d:13:fe:e4:ed:b0:1f:b8:5e:f0:ce:4d:51:0d:e6:
+ be:2b:7d:a4:44:6b:38:75:83:13:0d:41:d2:69:3f:
+ 29:7c:f1:57:29:52:f3:d1:2c:40:0e:c7:be:64:73:
+ d4:9e:06:a4:67:50:57:03:ef:8e:6a:c9:06:8a:e3:
+ c3:c3:eb:cb:6f:0f:3a:26:b9:ee:20:28:30:47:5c:
+ ec:57:98:18:01:f1:d7:91:ca:15:67:11:5e:e7:03:
+ 72:87:19:52:bf:a1:41:47:34:94:98:88:58:fc:ca:
+ 41:d5:71:7e:75:b5:e4:95:4d:5e:be:7e:5c:6e:4e:
+ 48:7a:a2:cd:14:0a:bb:fa:f8:70:8c:32:9d:29:7a:
+ 77:ab:55:7e:a8:5f:b8:2d:8e:62:5d:51:2e:8e:17:
+ a5:6e:38:c1:05:87:74:c1:55:6e:4f:8a:4a:51:f6:
+ 6c:d8:ac:cf:c0:8e:fa:4b:5c:74:16:39:18:1a:29:
+ 75:67
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 60:3A:6A:61:84:57:4F:4B:52:94:50:57:7E:1F:F4:CC:78:22:1B:44
+ X509v3 Authority Key Identifier:
+ keyid:EF:69:E0:F7:D5:1D:E6:99:EC:DC:6D:D0:F7:E2:B9:5C:64:71:83:35
+ DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com
+ serial:10:00
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Alternative Name:
+ DNS:tsa.wolfssl.com
+ X509v3 Key Usage: critical
+ Digital Signature
+ X509v3 Extended Key Usage: critical
+ Time Stamping
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 1e:4a:86:fa:ab:13:9d:97:5c:00:70:53:4c:71:cf:f8:8e:61:
+ 0d:08:e3:47:8e:67:0b:2c:31:c6:0b:b1:fc:23:6d:f5:76:e9:
+ 7f:c1:d3:80:1f:8f:c8:0a:83:6e:ee:51:1f:57:b3:70:4e:f3:
+ a1:54:af:34:29:ae:37:60:da:8b:da:95:f9:f6:e1:16:56:57:
+ b8:3e:7e:74:a8:6f:a7:d2:27:de:5c:56:b3:1a:79:bc:4c:28:
+ e9:0b:99:fc:a7:3a:ad:83:79:3d:6f:0d:35:af:40:98:fc:ef:
+ a7:09:0d:b9:9a:d7:2c:00:a9:26:c5:af:af:69:e7:b0:8f:c0:
+ af:d8:fd:6c:50:4b:45:c7:2b:cf:8e:f1:20:4a:63:d2:3a:d4:
+ fc:f8:64:d6:c7:02:40:4b:1a:cd:a2:81:03:d9:96:b2:61:f0:
+ 5d:c3:18:73:a3:a4:5e:c9:4d:19:91:19:23:83:aa:46:5d:f8:
+ a7:90:5f:0b:1e:db:72:4e:f8:7e:be:4d:4c:db:5b:61:15:7f:
+ 61:12:28:35:5b:ac:0a:0c:ae:f4:12:7c:65:30:92:29:73:ab:
+ 73:2c:8f:db:62:2f:9e:fc:2b:21:a5:66:b5:7c:f0:fa:5c:b1:
+ 57:8a:9c:dc:7c:ec:a6:3f:92:29:4a:45:88:f0:3c:da:cd:c4:
+ 11:e2:a8:56
+-----BEGIN CERTIFICATE-----
+MIIE/zCCA+egAwIBAgIUda0dJb3IVx9EHk/B2G86mC4BJX4wDQYJKoZIhvcNAQEL
+BQAwgZ8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQH
+DAdTZWF0dGxlMRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVu
+dDEgMB4GA1UEAwwXd29sZlNTTCBJbnRlcm1lZGlhdGUgQ0ExHzAdBgkqhkiG9w0B
+CQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjYwNjE2MDEwNjI2WhcNMjkwMzEyMDEw
+NjI2WjCBlzELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcM
+B0JvemVtYW4xEDAOBgNVBAoMB3dvbGZTU0wxFzAVBgNVBAsMDlRTQS1jaGFpbi0y
+MDQ4MRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGlu
+Zm9Ad29sZnNzbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCy
+u2asSbuMXeju/1USuW6w6Jp3NXq4ZYViphcbJyWMWbse5ruuCVV97lP9/1aZnQwe
+MUi9Qmkssgo3aFuPM//FoODiHmz2T5C0cz0T/uTtsB+4XvDOTVEN5r4rfaREazh1
+gxMNQdJpPyl88VcpUvPRLEAOx75kc9SeBqRnUFcD745qyQaK48PD68tvDzomue4g
+KDBHXOxXmBgB8deRyhVnEV7nA3KHGVK/oUFHNJSYiFj8ykHVcX51teSVTV6+flxu
+Tkh6os0UCrv6+HCMMp0penerVX6oX7gtjmJdUS6OF6VuOMEFh3TBVW5PikpR9mzY
+rM/AjvpLXHQWORgaKXVnAgMBAAGjggE3MIIBMzAdBgNVHQ4EFgQUYDpqYYRXT0tS
+lFBXfh/0zHgiG0QwgcIGA1UdIwSBujCBt4AU72ng99Ud5pns3G3Q9+K5XGRxgzWh
+gZqkgZcwgZQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQH
+DAdCb3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGlu
+ZzEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZv
+QHdvbGZzc2wuY29tggIQADAJBgNVHRMEAjAAMBoGA1UdEQQTMBGCD3RzYS53b2xm
+c3NsLmNvbTAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw
+DQYJKoZIhvcNAQELBQADggEBAB5KhvqrE52XXABwU0xxz/iOYQ0I40eOZwssMcYL
+sfwjbfV26X/B04Afj8gKg27uUR9Xs3BO86FUrzQprjdg2ovalfn24RZWV7g+fnSo
+b6fSJ95cVrMaebxMKOkLmfynOq2DeT1vDTWvQJj876cJDbma1ywAqSbFr69p57CP
+wK/Y/WxQS0XHK8+O8SBKY9I61Pz4ZNbHAkBLGs2igQPZlrJh8F3DGHOjpF7JTRmR
+GSODqkZd+KeQXwse23JO+H6+TUzbW2EVf2ESKDVbrAoMrvQSfGUwkilzq3Msj9ti
+L578KyGlZrV88PpcsVeKnNx87KY/kilKRYjwPNrNxBHiqFY=
+-----END CERTIFICATE-----
diff --git a/certs/tsa-chain-key.der b/certs/tsa-chain-key.der
new file mode 100644
index 00000000000..5c320a19ce8
Binary files /dev/null and b/certs/tsa-chain-key.der differ
diff --git a/certs/tsa-chain-key.pem b/certs/tsa-chain-key.pem
new file mode 100644
index 00000000000..68f94fea440
--- /dev/null
+++ b/certs/tsa-chain-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyu2asSbuMXeju
+/1USuW6w6Jp3NXq4ZYViphcbJyWMWbse5ruuCVV97lP9/1aZnQweMUi9Qmkssgo3
+aFuPM//FoODiHmz2T5C0cz0T/uTtsB+4XvDOTVEN5r4rfaREazh1gxMNQdJpPyl8
+8VcpUvPRLEAOx75kc9SeBqRnUFcD745qyQaK48PD68tvDzomue4gKDBHXOxXmBgB
+8deRyhVnEV7nA3KHGVK/oUFHNJSYiFj8ykHVcX51teSVTV6+flxuTkh6os0UCrv6
++HCMMp0penerVX6oX7gtjmJdUS6OF6VuOMEFh3TBVW5PikpR9mzYrM/AjvpLXHQW
+ORgaKXVnAgMBAAECggEAVOJFt+topBhxqRggujzRAjnmKll+yBaHC7vf22hzH735
+7YoddmE+dgl7YHUlFt2MRMaZSjFBLBX+XiQ038UNYzmttBZJH43YJqtYRafX576u
+wextJz13EkgU5yjLnCbj8INox/IL0SpLNOiVwa2A2EXQwnRAywpr3wU+jUaNnPMb
+Pp7vHOJgTYKGN+YY8gpYNkcrzOoZvwEt+pbc4VoJ9pyxLBAWuZfofJ9hACw/WxPR
+HMrFajkLA3zvKBtCVhaccVeLHBzR0e9cLigdhp3m4qPIyQpTiHOqCD3pmQw9aU1j
+7/cei345NkfIf/M0bkXxdGpTLVNchs9cSlVU5uQhmQKBgQDwOYZq2NvQa5zW9VLi
+XcD2NpQCB8tOrTcYf6DLeIUbI2D9ZbXTiqArAGQy9/j6z5QuDWxUA/lzOismPRJu
+erSw7Wqdl7i4DT5Q9Dr52jN8jTkM/RG0II+JZjwu5iOL0tf8/w9b0HUi2PusBLKe
+0CVt8dvcUIL2Ioh0F7hsCoaHMwKBgQC+eBumJpk5tbyJnTk7Jy2bDnCO10DTUuyV
+PsiCk52QvefVvcuSF5ocnUdsIFtpCIfkwVMrSos9JSd66qV/JzLN8QcYv8ttsWf3
+Z5gVh05Lz7JfvjheyUHSesOhxzltKIBrOLqV1gsAkUPyxai6MV2WDk5G7neqOh4n
+pAPwiBLI/QKBgDDYoZpsShYRK60R6S6aPbhS1Lms+AwhcIiMuxdkhDxGb1xXKCfB
+klvsEnPCtF/bgZfzpslWnYxukYOO+4Z3cPJg9ELjLO5P0xIG908CrWFwHd+kPctQ
+q58WqLoolaXC06RgALF8q7TQRixuMBvW2yWF/lzICjkeQHzKrfdaFIy9AoGBAK8R
+muhXJhODVe5vUwFp+2NAHHlOpMkYuVhcwtQydmtqAkPWFd0MUJzoe5OJEjwymSXM
+BHQQKndjRSyXrNJ45xuf5VP8RjFnFRa0Y3e2TGMmXt/d6dZFynh6WoLCqagJyC/F
+jsrWWHqHCxuETpgFc+3O7GgKHHecv8bT3MMjb5DJAoGBAONLdeZuHrtAEuYqBZdi
+iC8+YA96wOhMvULuWHxweBcJ8tLhEagcLuabLAQWrExFw+FdZ90IVyPuMzD3TTf0
+VwZaDrzGplkxqfLB/8sbxL6G3+/r3Zrq0PHV+vhRyebEKbu8fKhIUPnATlcABDC8
+kWRmM97sbQNJ68efcfq0C5GJ
+-----END PRIVATE KEY-----
diff --git a/certs/tsa-ecc-cert.der b/certs/tsa-ecc-cert.der
new file mode 100644
index 00000000000..ebd6606f9b0
Binary files /dev/null and b/certs/tsa-ecc-cert.der differ
diff --git a/certs/tsa-ecc-cert.pem b/certs/tsa-ecc-cert.pem
new file mode 100644
index 00000000000..c3245bfa150
--- /dev/null
+++ b/certs/tsa-ecc-cert.pem
@@ -0,0 +1,64 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 19:47:b6:5a:9a:23:a8:ff:c0:13:80:9b:3d:af:92:ee:0d:d7:58:de
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-ECC, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Validity
+ Not Before: Jun 5 10:09:40 2026 GMT
+ Not After : Mar 1 10:09:40 2029 GMT
+ Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-ECC, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (256 bit)
+ pub:
+ 04:ce:3e:24:12:01:46:68:1d:11:7d:cd:d9:8b:63:
+ 4e:f9:cc:36:06:b6:75:33:72:57:1c:12:7f:fa:5f:
+ 77:83:68:fc:18:64:8b:75:d4:53:88:93:e7:64:38:
+ 23:ef:19:c9:67:95:48:0f:7e:ed:f5:81:61:8c:66:
+ c9:e3:ae:c8:12
+ ASN1 OID: prime256v1
+ NIST CURVE: P-256
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ AB:E6:D5:D9:8A:31:CB:10:23:82:16:CE:20:1F:80:E4:D5:87:5A:20
+ X509v3 Authority Key Identifier:
+ keyid:AB:E6:D5:D9:8A:31:CB:10:23:82:16:CE:20:1F:80:E4:D5:87:5A:20
+ DirName:/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-ECC/CN=www.wolfssl.com/emailAddress=info@wolfssl.com
+ serial:19:47:B6:5A:9A:23:A8:FF:C0:13:80:9B:3D:AF:92:EE:0D:D7:58:DE
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Alternative Name:
+ DNS:tsa.wolfssl.com
+ X509v3 Key Usage: critical
+ Digital Signature
+ X509v3 Extended Key Usage: critical
+ Time Stamping
+ Signature Algorithm: ecdsa-with-SHA256
+ Signature Value:
+ 30:45:02:20:2b:51:26:56:e1:e1:1f:b9:ad:03:07:46:fa:7b:
+ 34:d0:2a:f5:11:c8:12:0c:06:32:23:29:6f:1b:a1:18:87:9a:
+ 02:21:00:d9:e6:a9:01:b6:cb:22:f8:28:5f:91:a5:bd:9c:e7:
+ f6:24:5b:d5:5e:7d:1d:a1:65:12:ca:aa:eb:48:fa:d5:15
+-----BEGIN CERTIFICATE-----
+MIIDazCCAxGgAwIBAgIUGUe2WpojqP/AE4CbPa+S7g3XWN4wCgYIKoZIzj0EAwIw
+gZAxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3pl
+bWFuMRAwDgYDVQQKDAd3b2xmU1NMMRAwDgYDVQQLDAdUU0EtRUNDMRgwFgYDVQQD
+DA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5j
+b20wHhcNMjYwNjA1MTAwOTQwWhcNMjkwMzAxMTAwOTQwWjCBkDELMAkGA1UEBhMC
+VVMxEDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xEDAOBgNVBAoM
+B3dvbGZTU0wxEDAOBgNVBAsMB1RTQS1FQ0MxGDAWBgNVBAMMD3d3dy53b2xmc3Ns
+LmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTBZMBMGByqGSM49
+AgEGCCqGSM49AwEHA0IABM4+JBIBRmgdEX3N2YtjTvnMNga2dTNyVxwSf/pfd4No
+/Bhki3XUU4iT52Q4I+8ZyWeVSA9+7fWBYYxmyeOuyBKjggFFMIIBQTAdBgNVHQ4E
+FgQUq+bV2YoxyxAjghbOIB+A5NWHWiAwgdAGA1UdIwSByDCBxYAUq+bV2YoxyxAj
+ghbOIB+A5NWHWiChgZakgZMwgZAxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250
+YW5hMRAwDgYDVQQHDAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMRAwDgYDVQQL
+DAdUU0EtRUNDMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0B
+CQEWEGluZm9Ad29sZnNzbC5jb22CFBlHtlqaI6j/wBOAmz2vku4N11jeMAkGA1Ud
+EwQCMAAwGgYDVR0RBBMwEYIPdHNhLndvbGZzc2wuY29tMA4GA1UdDwEB/wQEAwIH
+gDAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAKBggqhkjOPQQDAgNIADBFAiArUSZW
+4eEfua0DB0b6ezTQKvURyBIMBjIjKW8boRiHmgIhANnmqQG2yyL4KF+Rpb2c5/Yk
+W9VefR2hZRLKqutI+tUV
+-----END CERTIFICATE-----
diff --git a/certs/tsa-ecc-key.der b/certs/tsa-ecc-key.der
new file mode 100644
index 00000000000..2ee5113b536
Binary files /dev/null and b/certs/tsa-ecc-key.der differ
diff --git a/certs/tsa-ecc-key.pem b/certs/tsa-ecc-key.pem
new file mode 100644
index 00000000000..df12bb6ea06
--- /dev/null
+++ b/certs/tsa-ecc-key.pem
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIHN8XsCdrSslanbYhpsRH4P7rwxT4PXttp+4zE/Ovz1doAoGCCqGSM49
+AwEHoUQDQgAEzj4kEgFGaB0Rfc3Zi2NO+cw2BrZ1M3JXHBJ/+l93g2j8GGSLddRT
+iJPnZDgj7xnJZ5VID37t9YFhjGbJ467IEg==
+-----END EC PRIVATE KEY-----
diff --git a/certs/tsa-extra-eku-cert.der b/certs/tsa-extra-eku-cert.der
new file mode 100644
index 00000000000..6b6f6ba6f02
Binary files /dev/null and b/certs/tsa-extra-eku-cert.der differ
diff --git a/certs/tsa-extra-eku-cert.pem b/certs/tsa-extra-eku-cert.pem
new file mode 100644
index 00000000000..53d05a2a320
--- /dev/null
+++ b/certs/tsa-extra-eku-cert.pem
@@ -0,0 +1,93 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 2e:66:3c:01:89:1b:3a:75:8a:86:40:99:fd:af:c6:7b:b1:24:7f:8e
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-extra-eku-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Validity
+ Not Before: Jun 11 10:38:14 2026 GMT
+ Not After : Mar 7 10:38:14 2029 GMT
+ Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=TSA-extra-eku-2048, CN=www.wolfssl.com, emailAddress=info@wolfssl.com
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2048 bit)
+ Modulus:
+ 00:93:74:96:f0:0c:fd:d7:2a:5a:a0:a1:6e:b4:63:
+ b7:f4:e6:8b:77:8f:49:f7:a9:0c:b3:3d:5a:60:a8:
+ b8:67:75:b8:d0:ce:ba:ce:65:e5:64:a2:d0:c8:d0:
+ 2c:5b:a8:a5:d3:3f:2c:46:07:e6:b1:b7:a5:65:f8:
+ 9f:89:e1:93:bc:49:cd:55:fd:9b:a1:1e:1e:f7:99:
+ 07:4a:1c:c8:b6:88:a2:5e:77:a5:3f:79:2f:d3:43:
+ 41:f6:cf:ff:78:30:89:12:b1:0c:3f:f5:2f:62:e6:
+ 71:33:02:70:d9:42:1b:e4:07:9c:9d:c7:5e:5b:4e:
+ 93:8f:6b:0e:fe:5b:7e:02:6f:8d:e8:7d:e9:aa:1d:
+ 12:f2:6b:a4:55:61:59:e0:3a:38:b0:14:41:9d:bb:
+ c6:e0:6a:9d:35:a8:08:f2:3c:29:b9:a7:b2:38:47:
+ f6:70:1a:b8:d2:d5:c9:48:f0:ee:60:1d:14:77:8a:
+ 2c:38:90:29:7f:42:e4:dd:92:0a:8d:03:88:44:0a:
+ f0:b6:14:bd:e5:11:50:94:26:fb:3e:ab:17:69:3d:
+ a6:45:c7:ad:86:41:66:e6:53:e5:dc:d8:4a:04:c7:
+ af:fb:a7:df:99:77:57:d9:25:7c:67:73:3d:ee:60:
+ f3:63:2d:68:0f:97:da:68:d5:3d:e5:06:92:82:dd:
+ 90:25
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ C0:19:81:BB:D6:3D:BB:41:E8:22:1A:08:50:AD:57:FB:E5:B5:EA:9D
+ X509v3 Authority Key Identifier:
+ keyid:C0:19:81:BB:D6:3D:BB:41:E8:22:1A:08:50:AD:57:FB:E5:B5:EA:9D
+ DirName:/C=US/ST=Montana/L=Bozeman/O=wolfSSL/OU=TSA-extra-eku-2048/CN=www.wolfssl.com/emailAddress=info@wolfssl.com
+ serial:2E:66:3C:01:89:1B:3A:75:8A:86:40:99:FD:AF:C6:7B:B1:24:7F:8E
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage: critical
+ Digital Signature
+ X509v3 Extended Key Usage: critical
+ Time Stamping, 1.3.6.1.4.1.99999.1
+ Signature Algorithm: sha256WithRSAEncryption
+ Signature Value:
+ 68:67:cb:3b:2a:55:c2:5f:be:62:c5:21:5e:a8:cd:d7:74:1b:
+ 2e:2f:33:76:0f:0d:bb:11:75:1b:bf:1f:05:da:21:6e:a6:82:
+ 26:f2:35:5c:ac:6d:04:86:b6:99:e9:c5:a0:23:48:af:14:ae:
+ bc:ae:8f:84:41:a8:14:a3:fc:69:39:9f:50:4e:6e:b9:81:f4:
+ d8:cc:df:a2:f9:ba:0b:79:67:7a:8f:2e:75:1f:7f:f2:57:74:
+ 4a:4c:bd:db:1c:16:fa:e9:38:c2:7a:8d:64:ea:c2:c3:34:93:
+ 54:73:07:8e:cc:95:44:05:9a:dd:a1:ae:b3:94:11:12:b6:1f:
+ 47:dd:a7:1c:ff:6d:06:b3:aa:d5:da:dd:54:a7:e8:c1:87:6f:
+ 37:ee:ef:a3:05:eb:08:10:32:7f:ad:02:2f:aa:ca:e3:80:06:
+ bb:2c:c3:df:c8:99:fc:c1:b5:21:f8:85:42:90:20:2d:06:33:
+ fd:c1:d1:57:8a:e8:82:d1:dd:c2:3e:78:fa:62:da:15:5f:4c:
+ 04:36:06:e5:76:22:9c:e6:b0:b8:b9:2c:70:aa:16:b4:b4:20:
+ 43:d9:0d:4d:16:81:55:b3:26:b1:32:99:2b:27:a6:c7:44:24:
+ b8:15:73:2b:e1:fa:b0:26:dc:e6:70:ec:12:00:5d:0f:1f:2d:
+ 2f:50:26:3f
+-----BEGIN CERTIFICATE-----
+MIIFBzCCA++gAwIBAgIULmY8AYkbOnWKhkCZ/a/Ge7Ekf44wDQYJKoZIhvcNAQEL
+BQAwgZsxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC
+b3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMRswGQYDVQQLDBJUU0EtZXh0cmEtZWt1
+LTIwNDgxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQ
+aW5mb0B3b2xmc3NsLmNvbTAeFw0yNjA2MTExMDM4MTRaFw0yOTAzMDcxMDM4MTRa
+MIGbMQswCQYDVQQGEwJVUzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96
+ZW1hbjEQMA4GA1UECgwHd29sZlNTTDEbMBkGA1UECwwSVFNBLWV4dHJhLWVrdS0y
+MDQ4MRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGlu
+Zm9Ad29sZnNzbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCT
+dJbwDP3XKlqgoW60Y7f05ot3j0n3qQyzPVpgqLhndbjQzrrOZeVkotDI0CxbqKXT
+PyxGB+axt6Vl+J+J4ZO8Sc1V/ZuhHh73mQdKHMi2iKJed6U/eS/TQ0H2z/94MIkS
+sQw/9S9i5nEzAnDZQhvkB5ydx15bTpOPaw7+W34Cb43ofemqHRLya6RVYVngOjiw
+FEGdu8bgap01qAjyPCm5p7I4R/ZwGrjS1clI8O5gHRR3iiw4kCl/QuTdkgqNA4hE
+CvC2FL3lEVCUJvs+qxdpPaZFx62GQWbmU+Xc2EoEx6/7p9+Zd1fZJXxncz3uYPNj
+LWgPl9po1T3lBpKC3ZAlAgMBAAGjggE/MIIBOzAdBgNVHQ4EFgQUwBmBu9Y9u0Ho
+IhoIUK1X++W16p0wgdsGA1UdIwSB0zCB0IAUwBmBu9Y9u0HoIhoIUK1X++W16p2h
+gaGkgZ4wgZsxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQH
+DAdCb3plbWFuMRAwDgYDVQQKDAd3b2xmU1NMMRswGQYDVQQLDBJUU0EtZXh0cmEt
+ZWt1LTIwNDgxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJ
+ARYQaW5mb0B3b2xmc3NsLmNvbYIULmY8AYkbOnWKhkCZ/a/Ge7Ekf44wCQYDVR0T
+BAIwADAOBgNVHQ8BAf8EBAMCB4AwIQYDVR0lAQH/BBcwFQYIKwYBBQUHAwgGCSsG
+AQQBho0fATANBgkqhkiG9w0BAQsFAAOCAQEAaGfLOypVwl++YsUhXqjN13QbLi8z
+dg8NuxF1G78fBdohbqaCJvI1XKxtBIa2menFoCNIrxSuvK6PhEGoFKP8aTmfUE5u
+uYH02Mzfovm6C3lneo8udR9/8ld0Sky92xwW+uk4wnqNZOrCwzSTVHMHjsyVRAWa
+3aGus5QRErYfR92nHP9tBrOq1drdVKfowYdvN+7vowXrCBAyf60CL6rK44AGuyzD
+38iZ/MG1IfiFQpAgLQYz/cHRV4rogtHdwj54+mLaFV9MBDYG5XYinOawuLkscKoW
+tLQgQ9kNTRaBVbMmsTKZKyemx0QkuBVzK+H6sCbc5nDsEgBdDx8tL1AmPw==
+-----END CERTIFICATE-----
diff --git a/certs/tsa-key.der b/certs/tsa-key.der
new file mode 100644
index 00000000000..c5d57dac629
Binary files /dev/null and b/certs/tsa-key.der differ
diff --git a/certs/tsa-key.pem b/certs/tsa-key.pem
new file mode 100644
index 00000000000..2c77f34690d
--- /dev/null
+++ b/certs/tsa-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAk3SW8Az91ypaoKFutGO39OaLd49J96kMsz1aYKi4Z3W40M66
+zmXlZKLQyNAsW6il0z8sRgfmsbelZfifieGTvEnNVf2boR4e95kHShzItoiiXnel
+P3kv00NB9s//eDCJErEMP/UvYuZxMwJw2UIb5AecncdeW06Tj2sO/lt+Am+N6H3p
+qh0S8mukVWFZ4Do4sBRBnbvG4GqdNagI8jwpuaeyOEf2cBq40tXJSPDuYB0Ud4os
+OJApf0Lk3ZIKjQOIRArwthS95RFQlCb7PqsXaT2mRcethkFm5lPl3NhKBMev+6ff
+mXdX2SV8Z3M97mDzYy1oD5faaNU95QaSgt2QJQIDAQABAoIBAARHDRE1/Jp5zuWQ
+WU2gA5hLTyoZkjE+NQacFduY+WR9nJi+GTA0nNogWQMs+s8b44JEd7cpB9m0Sfbd
+Jq+mx2ozuo0EjY0NJSPUoMqsbsF7fDkA/95erchTT/4QxvNIdn7Ve6qn+lxu8pi4
+Dj2si8eVKvIZdFTHa5jCUniG1JnNxwRVAgC0vFWEUyPIwFHyPon7ruP1YdxMsaEN
+JW5Gi7QnKGLvTAceFfIKIAFTArFKAfE5KFV3ksBHjS21Yehr4ngqvMTuQWpCvaPp
+cMBAvMHH6t62naaibcFSh+qsLGHQWjfq7jXh0FtQbnvcW7z4yXQs4boOiLm1ug1p
+T1qnJxECgYEAz2m4+VJNkG+FobHIdjQ5EejH6JBlhSMbStlxB3H1RmQzRRFhrd2N
+Cnr7XPKRp7hkuMnbJX7oky/Vb72/WsJz8FazEgIRh+L7g1JLJh842R8lsap90UO5
+b44Up1WNwsJ29iVi0t0fTMwWhCjLZAiKslCtGa8YXSpZj9ChjHWtBPkCgYEAtf9L
+GVx9yrJjN7NYD8/WFim5E182/xsoDqQbBcxQlf49b/HdsW9H/TBSnlLHfv1yCZkc
+8PY3dUuBGai8UYPOPqq6mjs5c9WsVgXcAzdCWZltIFoBb0p8yEWPFZxxMG5nytWZ
+TbWKZOSZ2QhvCUYn+hHZnhP6tGWhCJ2xbTOcK40CgYA63Z6J5DnvTDd49KQYKCoq
+Uw6pipHFf3k3fQ7/NfCO0dFbQNugJMjquIyujImaOFMdvuxbb/FCFMlWtVuhvp55
+D6Iy8jNXhawsUSbS4vmXZaelDOY4higS6Rgjhbx+EgMBSQsLHYbMnP+m8o0HDwWO
+Jid6qp8XkfVpQ6UV4DACwQKBgQCWNtx5VknNH2ec9b3dbyG4sT88qf3umS96xiA3
+rOdmpa131B2y8bJtW5EVdCWMvwt8uI+WouIrQeKQlyC39x4nwyq5WeCVpurTJYru
+bJGq+mODrEY64TQU6LSsla8m1jl8xMf/x7MuizAgXkGnWextDoabXsoyUx+SyPVE
+uLBRkQKBgQCopTVwcup0wurvNqNzFAeSuKRwHFDffAHNcTY0u30qyyv1JyJ+Fr6J
+z39jw93hEPohHGEeDVMSvHTk3nFil+Du+z+yP7NbRHsoctizjnGgTOMKZHl+O+9P
+iwFFJCZv9qKG29nkfOOk57GPRLvIAij+ioywExeyaDWY/lAY0JsF0g==
+-----END RSA PRIVATE KEY-----
diff --git a/cmake/functions.cmake b/cmake/functions.cmake
index c803f072a23..8ec279bda55 100644
--- a/cmake/functions.cmake
+++ b/cmake/functions.cmake
@@ -329,6 +329,9 @@ function(generate_build_flags)
if(WOLFSSL_PKCS7 OR WOLFSSL_USER_SETTINGS)
set(BUILD_PKCS7 "yes" PARENT_SCOPE)
endif()
+ if(WOLFSSL_TSP OR WOLFSSL_USER_SETTINGS)
+ set(BUILD_TSP "yes" PARENT_SCOPE)
+ endif()
set(BUILD_HASHFLAGS ${WOLFSSL_HASHFLAGS} PARENT_SCOPE)
set(BUILD_LINUX_KM ${WOLFSSL_LINUX_KM} PARENT_SCOPE)
set(BUILD_NO_LIBRARY ${WOLFSSL_NO_LIBRARY} PARENT_SCOPE)
@@ -1088,6 +1091,10 @@ function(generate_lib_src_list LIB_SOURCES)
list(APPEND LIB_SOURCES wolfcrypt/src/pkcs7.c)
endif()
+ if(BUILD_TSP)
+ list(APPEND LIB_SOURCES wolfcrypt/src/tsp.c)
+ endif()
+
if(BUILD_SRP)
list(APPEND LIB_SOURCES wolfcrypt/src/srp.c)
endif()
diff --git a/configure.ac b/configure.ac
index 2a58545fcc6..f3780737123 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8528,6 +8528,28 @@ then
ENABLED_PKCS7=yes
fi
+# RFC 3161 Time-Stamp Protocol (TSP)
+AC_ARG_ENABLE([tsp],
+ [AS_HELP_STRING([--enable-tsp],[Enable RFC 3161 Time-Stamp Protocol (default: disabled)])],
+ [ ENABLED_TSP=$enableval ],
+ [ ENABLED_TSP=no ]
+ )
+
+if test "x$ENABLED_TSP" = "xyes"
+then
+ # TSP ASN.1 encoding/decoding is only implemented with the template parser.
+ if test "$ASN_IMPL" = "original"
+ then
+ AC_MSG_ERROR([--enable-tsp is not supported with --enable-asn=original.])
+ fi
+ AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_TSP"
+ # Requires PKCS7 for time-stamp token creation and verification
+ if test "x$ENABLED_PKCS7" = "xno"
+ then
+ ENABLED_PKCS7="yes"
+ fi
+fi
+
# wolfSSH Options
AC_ARG_ENABLE([wolfssh],
[AS_HELP_STRING([--enable-wolfssh],[Enable wolfSSH options (default: disabled)])],
@@ -12372,6 +12394,7 @@ AM_CONDITIONAL([BUILD_HEAPMATH],[test "x$ENABLED_HEAPMATH" = "xyes" || test "x$E
AM_CONDITIONAL([BUILD_EXAMPLE_SERVERS],[test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_LEANTLS" = "xno"])
AM_CONDITIONAL([BUILD_EXAMPLE_CLIENTS],[test "x$ENABLED_EXAMPLES" = "xyes"])
AM_CONDITIONAL([BUILD_EXAMPLE_ASN1],[test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_ASN_PRINT" = "xyes" && test "$ENABLED_ASN" != "no"])
+AM_CONDITIONAL([BUILD_EXAMPLE_TSP],[test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_TSP" = "xyes"])
AM_CONDITIONAL([BUILD_OCSP_RESPONDER],[test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_OCSP_RESPONDER" = "xyes"])
AM_CONDITIONAL([BUILD_TESTS],[test "x$ENABLED_EXAMPLES" = "xyes"])
AM_CONDITIONAL([BUILD_THREADED_EXAMPLES],[test "x$ENABLED_SINGLETHREADED" = "xno" && test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_LEANTLS" = "xno"])
@@ -12413,6 +12436,7 @@ AM_CONDITIONAL([BUILD_TRUST_PEER_CERT],[test "x$ENABLED_TRUSTED_PEER_CERT" = "xy
AM_CONDITIONAL([BUILD_PKI],[test "x$ENABLED_PKI" = "xyes"])
AM_CONDITIONAL([BUILD_DES3],[test "x$ENABLED_DES3" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_PKCS7],[test "x$ENABLED_PKCS7" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
+AM_CONDITIONAL([BUILD_TSP],[test "x$ENABLED_TSP" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_SMIME],[test "x$ENABLED_SMIME" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_HASHFLAGS],[test "x$ENABLED_HASHFLAGS" = "xyes"])
AM_CONDITIONAL([BUILD_LINUXKM],[test "$ENABLED_LINUXKM" = "yes"])
@@ -12941,6 +12965,7 @@ echo " * Keying Material Exporter: $ENABLED_KEYING_MATERIAL"
echo " * All TLS Extensions: $ENABLED_TLSX"
echo " * S/MIME: $ENABLED_SMIME"
echo " * PKCS#7: $ENABLED_PKCS7"
+echo " * TSP: $ENABLED_TSP"
echo " * PKCS#8: $ENABLED_PKCS8"
echo " * PKCS#11: $ENABLED_PKCS11"
echo " * PKCS#12: $ENABLED_PKCS12"
diff --git a/doc/dox_comments/header_files/doxygen_groups.h b/doc/dox_comments/header_files/doxygen_groups.h
index 1f308964f91..b84a3e49099 100644
--- a/doc/dox_comments/header_files/doxygen_groups.h
+++ b/doc/dox_comments/header_files/doxygen_groups.h
@@ -263,6 +263,7 @@
\defgroup MD4 Algorithms - MD4
\defgroup MD5 Algorithms - MD5
\defgroup PKCS7 Algorithms - PKCS7
+ \defgroup TSP Time-Stamp Protocol (RFC 3161)
\defgroup PKCS11 Algorithms - PKCS11
\defgroup Password Algorithms - Password Based
\defgroup Poly1305 Algorithms - Poly1305
diff --git a/doc/dox_comments/header_files/tsp.h b/doc/dox_comments/header_files/tsp.h
new file mode 100644
index 00000000000..6f157b731d6
--- /dev/null
+++ b/doc/dox_comments/header_files/tsp.h
@@ -0,0 +1,1795 @@
+/*!
+ \ingroup TSP
+
+ \brief This function initializes a TimeStampReq structure for encoding.
+ All fields are cleared and the version is set to 1. Set the message
+ imprint with the hash algorithm and hash of the data to be time-stamped
+ before encoding.
+
+ \return 0 Returned on successfully initializing the request.
+ \return BAD_FUNC_ARG Returned when req is NULL.
+
+ \param [out] req Pointer to the TspRequest structure to initialize.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ // hash the data to be time-stamped into hash
+
+ wc_TspRequest_Init(&req);
+ req.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(req.imprint.hash, hash, sizeof(hash));
+ req.imprint.hashSz = (word32)sizeof(hash);
+ req.certReq = 1;
+ \endcode
+
+ \sa wc_TspRequest_SetHashType
+ \sa wc_TspRequest_Encode
+ \sa wc_TspRequest_Decode
+*/
+int wc_TspRequest_Init(TspRequest* req);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the message imprint hash algorithm and hash size
+ of a TimeStampReq from a hash type. After calling, fill
+ req->imprint.hash with the digest of the data to be time-stamped.
+
+ \return 0 Returned on successfully setting the hash algorithm.
+ \return BAD_FUNC_ARG Returned when req is NULL.
+ \return HASH_TYPE_E Returned when the hash algorithm is not available.
+ \return BUFFER_E Returned when the digest is too big for the message
+ imprint.
+
+ \param [in,out] req Pointer to the TspRequest structure to update.
+ \param [in] hashType Hash algorithm to use - e.g. WC_HASH_TYPE_SHA256.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ // hash the data to be time-stamped into hash
+
+ wc_TspRequest_Init(&req);
+ wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256);
+ XMEMCPY(req.imprint.hash, hash, sizeof(hash));
+ req.certReq = 1;
+ \endcode
+
+ \sa wc_TspRequest_Init
+ \sa wc_TspRequest_GetHashType
+ \sa wc_TspRequest_Encode
+*/
+int wc_TspRequest_SetHashType(TspRequest* req, enum wc_HashType hashType);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the message imprint hash type of a TimeStampReq.
+ It maps the hash algorithm OID of the message imprint to a hash type. This
+ is useful after decoding a request to learn which hash algorithm to use on
+ the data to be time-stamped.
+
+ \return 0 Returned on successfully getting the hash type.
+ \return BAD_FUNC_ARG Returned when req or hashType is NULL.
+ \return HASH_TYPE_E Returned when the message imprint hash algorithm is
+ not a recognized hash.
+
+ \param [in] req Pointer to the TspRequest structure to query.
+ \param [out] hashType Set to the hash algorithm of the message imprint.
+
+ _Example_
+ \code
+ TspRequest req;
+ enum wc_HashType hashType;
+
+ // decode req from a received request
+ if (wc_TspRequest_GetHashType(&req, &hashType) != 0) {
+ // hash algorithm not supported
+ }
+ \endcode
+
+ \sa wc_TspRequest_SetHashType
+ \sa wc_TspRequest_Decode
+*/
+int wc_TspRequest_GetHashType(const TspRequest* req,
+ enum wc_HashType* hashType);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the message imprint hash of a TimeStampReq. The
+ hash is copied into the caller's buffer.
+
+ \return 0 Returned on successfully getting the hash.
+ \return BAD_FUNC_ARG Returned when req, hash or hashSz is NULL.
+ \return BUFFER_E Returned when the buffer is too small for the hash.
+
+ \param [in] req Pointer to the TspRequest structure to query.
+ \param [out] hash Buffer to hold the hash.
+ \param [in,out] hashSz On in, the length of the buffer in bytes. On out,
+ the length of the hash in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ word32 hashSz = (word32)sizeof(hash);
+
+ // decode req from a received request
+ if (wc_TspRequest_GetHash(&req, hash, &hashSz) != 0) {
+ // buffer too small
+ }
+ \endcode
+
+ \sa wc_TspRequest_SetHash
+*/
+int wc_TspRequest_GetHash(const TspRequest* req, byte* hash, word32* hashSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the message imprint hash of a TimeStampReq. The
+ hash and its length are copied into the message imprint. Set the hash
+ algorithm separately with wc_TspRequest_SetHashType().
+
+ \return 0 Returned on successfully setting the hash.
+ \return BAD_FUNC_ARG Returned when req or hash is NULL or hashSz is 0.
+ \return BUFFER_E Returned when hashSz is too big for the message imprint.
+
+ \param [in,out] req Pointer to the TspRequest structure to update.
+ \param [in] hash Hash of the data to be time-stamped.
+ \param [in] hashSz Length of the hash in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ // hash the data to be time-stamped into hash
+
+ wc_TspRequest_Init(&req);
+ wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256);
+ wc_TspRequest_SetHash(&req, hash, sizeof(hash));
+ \endcode
+
+ \sa wc_TspRequest_GetHash
+ \sa wc_TspRequest_SetHashType
+*/
+int wc_TspRequest_SetHash(TspRequest* req, const byte* hash, word32 hashSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the nonce of a TimeStampReq. The nonce is copied
+ into the caller's buffer. A length of 0 means no nonce is set.
+
+ \return 0 Returned on successfully getting the nonce.
+ \return BAD_FUNC_ARG Returned when req, nonce or nonceSz is NULL.
+ \return BUFFER_E Returned when the buffer is too small for the nonce.
+
+ \param [in] req Pointer to the TspRequest structure to query.
+ \param [out] nonce Buffer to hold the nonce.
+ \param [in,out] nonceSz On in, the length of the buffer in bytes. On out,
+ the length of the nonce in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte nonce[32];
+ word32 nonceSz = (word32)sizeof(nonce);
+
+ // decode req from a received request
+ if (wc_TspRequest_GetNonce(&req, nonce, &nonceSz) != 0) {
+ // buffer too small
+ }
+ \endcode
+
+ \sa wc_TspRequest_SetNonce
+*/
+int wc_TspRequest_GetNonce(const TspRequest* req, byte* nonce, word32* nonceSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the nonce of a TimeStampReq. The nonce is a
+ big-endian number that must not have a leading zero byte to encode -
+ leading zero bytes are stripped, keeping at least one byte so an all-zero
+ nonce becomes the number zero.
+
+ \return 0 Returned on successfully setting the nonce.
+ \return BAD_FUNC_ARG Returned when req or nonce is NULL or nonceSz is 0.
+ \return BUFFER_E Returned when nonceSz is too big for the nonce field.
+
+ \param [in,out] req Pointer to the TspRequest structure to update.
+ \param [in] nonce Nonce as a big-endian number.
+ \param [in] nonceSz Length of the nonce in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte nonce[16];
+ // fill nonce with random bytes
+
+ wc_TspRequest_Init(&req);
+ wc_TspRequest_SetNonce(&req, nonce, sizeof(nonce));
+ \endcode
+
+ \sa wc_TspRequest_GetNonce
+*/
+int wc_TspRequest_SetNonce(TspRequest* req, const byte* nonce, word32 nonceSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function generates a random nonce for a TimeStampReq from a
+ random number generator. A convenience over generating random bytes and
+ calling wc_TspRequest_SetNonce(). The nonce is a minimal positive INTEGER:
+ the top bit of the first byte is cleared so it is positive and the first
+ byte is made non-zero so there is no leading zero byte.
+
+ \return 0 Returned on successfully generating the nonce.
+ \return BAD_FUNC_ARG Returned when req or rng is NULL or sz is 0.
+ \return BUFFER_E Returned when sz is too big for the nonce field.
+ \return Other Returned, negative, on random number generation failure.
+
+ \param [in,out] req Pointer to the TspRequest structure to update.
+ \param [in] rng Random number generator.
+ \param [in] sz Length of the nonce to generate in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ WC_RNG rng;
+
+ wc_InitRng(&rng);
+ wc_TspRequest_Init(&req);
+ if (wc_TspRequest_GenerateNonce(&req, &rng, 16) != 0) {
+ // error generating nonce
+ }
+ \endcode
+
+ \sa wc_TspRequest_SetNonce
+ \sa wc_TspRequest_GetNonce
+*/
+int wc_TspRequest_GenerateNonce(TspRequest* req, WC_RNG* rng, word32 sz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the TSA policy of a TimeStampReq, copying it into
+ the caller's buffer. The policy is the content of an OBJECT IDENTIFIER. A
+ length of 0 means no policy is set.
+
+ \return 0 Returned on successfully getting the policy.
+ \return BAD_FUNC_ARG Returned when req, policy or policySz is NULL.
+ \return BUFFER_E Returned when the buffer is too small for the policy.
+
+ \param [in] req Pointer to the TspRequest structure.
+ \param [out] policy Buffer to hold the policy.
+ \param [in,out] policySz On in, length of the buffer in bytes. On out,
+ length of the policy in bytes.
+
+ _Example_
+ \code
+ TspRequest req; // decoded request
+ byte policy[32];
+ word32 policySz = sizeof(policy);
+
+ if (wc_TspRequest_GetPolicy(&req, policy, &policySz) != 0) {
+ // error getting policy
+ }
+ \endcode
+
+ \sa wc_TspRequest_SetPolicy
+*/
+int wc_TspRequest_GetPolicy(const TspRequest* req, byte* policy,
+ word32* policySz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the TSA policy of a TimeStampReq. The policy is
+ the content of an OBJECT IDENTIFIER - the bytes after the type and length.
+ It is copied into the request.
+
+ \return 0 Returned on successfully setting the policy.
+ \return BAD_FUNC_ARG Returned when req or policy is NULL or policySz is 0.
+ \return BUFFER_E Returned when policySz is too big for the policy field.
+
+ \param [in,out] req Pointer to the TspRequest structure to update.
+ \param [in] policy Policy as OBJECT IDENTIFIER content.
+ \param [in] policySz Length of the policy in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ // OBJECT IDENTIFIER content for the policy
+ static const byte policy[] = { 0x2b, 0x06, 0x01, 0x04, 0x01 };
+
+ wc_TspRequest_Init(&req);
+ wc_TspRequest_SetPolicy(&req, policy, sizeof(policy));
+ \endcode
+
+ \sa wc_TspRequest_GetPolicy
+*/
+int wc_TspRequest_SetPolicy(TspRequest* req, const byte* policy,
+ word32 policySz);
+
+/*!
+ \ingroup TSP
+
+ \brief This macro gets the certReq flag of a TimeStampReq. A non-zero
+ value means the requester asked for the TSA certificate to be included
+ in the response.
+
+ \return The certReq flag: non-zero when the TSA certificate is requested,
+ 0 otherwise.
+
+ \param [in] req Pointer to the TspRequest structure to query.
+
+ _Example_
+ \code
+ TspRequest req;
+ // decode req from a received request
+
+ if (wc_TspRequest_GetCertReq(&req)) {
+ // include the TSA certificate in the response
+ }
+ \endcode
+
+ \sa wc_TspRequest_SetCertReq
+*/
+#define wc_TspRequest_GetCertReq(req) ((req)->certReq)
+
+/*!
+ \ingroup TSP
+
+ \brief This macro sets the certReq flag of a TimeStampReq. A non-zero
+ value requests the TSA certificate to be included in the response. Any
+ non-zero value is normalized to 1.
+
+ \param [in,out] req Pointer to the TspRequest structure to update.
+ \param [in] val Non-zero to request the TSA certificate, 0 otherwise.
+
+ _Example_
+ \code
+ TspRequest req;
+
+ wc_TspRequest_Init(&req);
+ wc_TspRequest_SetCertReq(&req, 1);
+ \endcode
+
+ \sa wc_TspRequest_GetCertReq
+*/
+#define wc_TspRequest_SetCertReq(req, val) \
+ ((req)->certReq = (byte)((val) != 0))
+
+/*!
+ \ingroup TSP
+
+ \brief This function encodes a TimeStampReq as DER - RFC 3161, 2.4.1.
+ The message imprint is required. The policy, nonce and certReq fields
+ are encoded when set. The nonce is a big-endian number and must not
+ have a leading zero byte.
+
+ \return 0 Returned on successfully encoding the request.
+ \return BAD_FUNC_ARG Returned when req or outSz is NULL, the message
+ imprint hash is not set, a field is too long for its array or the nonce
+ has a leading zero byte.
+ \return BUFFER_E Returned when out is not NULL and the encoding is
+ longer than outSz.
+ \return ASN_UNKNOWN_OID_E Returned when the hash algorithm is not
+ recognized.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] req Pointer to the TspRequest structure to encode.
+ \param [out] out Buffer to hold the encoding. May be NULL to get the
+ length.
+ \param [in,out] outSz On in, the length of the buffer in bytes. On out,
+ the length of the encoding in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte der[512];
+ word32 derSz = (word32)sizeof(der);
+
+ // initialize req and set the message imprint
+ if (wc_TspRequest_Encode(&req, der, &derSz) != 0) {
+ // error encoding request
+ }
+ // send der to the TSA
+ \endcode
+
+ \sa wc_TspRequest_Init
+ \sa wc_TspRequest_Decode
+*/
+int wc_TspRequest_Encode(const TspRequest* req, byte* out, word32* outSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function decodes a DER encoded TimeStampReq - RFC 3161,
+ 2.4.1. All fields are copied into the structure - the input buffer is
+ not referenced after return. Only version 1 is supported - a request
+ with another version fails to decode. Any policy OID is accepted - the
+ caller checks it is one it supports. The hash algorithm is not checked
+ to be available - the caller checks it is usable. Requests with
+ extensions are not supported and fail to decode.
+
+ \return 0 Returned on successfully decoding the request.
+ \return BAD_FUNC_ARG Returned when req or input is NULL or inSz is 0.
+ \return ASN_PARSE_E Returned when the encoding is invalid, the hash is
+ empty or extensions are present.
+ \return ASN_VERSION_E Returned when the version is not supported.
+ \return ASN_UNKNOWN_OID_E Returned when the hash algorithm OID check
+ fails.
+ \return BUFFER_E Returned when the hash is longer than
+ WC_TSP_MAX_HASH_SZ bytes, the policy is longer than MAX_OID_SZ bytes or
+ the nonce is longer than MAX_TS_NONCE_SZ bytes.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [out] req Pointer to the TspRequest structure to fill.
+ \param [in] input Buffer holding the DER encoding.
+ \param [in] inSz Length of the data in the buffer in bytes.
+
+ _Example_
+ \code
+ TspRequest req;
+ byte* der; // DER encoded request received
+ word32 derSz; // length of DER encoded request
+
+ if (wc_TspRequest_Decode(&req, der, derSz) != 0) {
+ // error decoding request
+ }
+ \endcode
+
+ \sa wc_TspRequest_Encode
+ \sa wc_TspTstInfo_CheckRequest
+*/
+int wc_TspRequest_Decode(TspRequest* req, const byte* input, word32 inSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function initializes a TSTInfo structure for encoding. All
+ fields are cleared and the version is set to 1. The policy, message
+ imprint, serial number and time are required for encoding.
+
+ \return 0 Returned on successfully initializing the TSTInfo.
+ \return BAD_FUNC_ARG Returned when tstInfo is NULL.
+
+ \param [out] tstInfo Pointer to the TspTstInfo structure to initialize.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+
+ wc_TspTstInfo_Init(&tst);
+ // set policy, imprint and serial number; NULL genTime is current time
+ \endcode
+
+ \sa wc_TspTstInfo_Encode
+ \sa wc_TspTstInfo_SignWithPkcs7
+*/
+int wc_TspTstInfo_Init(TspTstInfo* tstInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the serial number of a TSTInfo. A reference is
+ returned - the serial number is not copied and is valid while the TSTInfo
+ references it.
+
+ \return 0 Returned on successfully getting the serial number.
+ \return BAD_FUNC_ARG Returned when tstInfo, serial or serialSz is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [out] serial Serial number as a big-endian number.
+ \param [out] serialSz Length of the serial number in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst; // decoded TSTInfo
+ const byte* serial;
+ word32 serialSz;
+
+ if (wc_TspTstInfo_GetSerial(&tst, &serial, &serialSz) != 0) {
+ // error getting serial number
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetSerial
+*/
+int wc_TspTstInfo_GetSerial(const TspTstInfo* tstInfo, const byte** serial,
+ word32* serialSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the serial number of a TSTInfo. The serial number
+ is a big-endian number that is referenced, not copied, and must remain
+ available while the TSTInfo is used. Leading zero bytes are stripped,
+ keeping at least one byte, so it has no leading zero byte and encodes - an
+ all-zero serial number becomes zero.
+
+ \return 0 Returned on successfully setting the serial number.
+ \return BAD_FUNC_ARG Returned when tstInfo or serial is NULL or serialSz
+ is 0.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] serial Serial number as a big-endian number.
+ \param [in] serialSz Length of the serial number in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ static const byte serial[] = { 0x01, 0x02, 0x03, 0x04 };
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetSerial(&tst, serial, sizeof(serial));
+ \endcode
+
+ \sa wc_TspTstInfo_GetSerial
+*/
+int wc_TspTstInfo_SetSerial(TspTstInfo* tstInfo, const byte* serial,
+ word32 serialSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the TSA policy of a TSTInfo. A reference is
+ returned - the policy is not copied and is valid while the TSTInfo
+ references it. A length of 0 means no policy is present.
+
+ \return 0 Returned on successfully getting the policy.
+ \return BAD_FUNC_ARG Returned when tstInfo, policy or policySz is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [out] policy Policy as OBJECT IDENTIFIER content.
+ \param [out] policySz Length of the policy in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst; // decoded TSTInfo
+ const byte* policy;
+ word32 policySz;
+
+ if (wc_TspTstInfo_GetPolicy(&tst, &policy, &policySz) != 0) {
+ // error getting policy
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetPolicy
+*/
+int wc_TspTstInfo_GetPolicy(const TspTstInfo* tstInfo, const byte** policy,
+ word32* policySz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the TSA policy of a TSTInfo. The policy is the
+ content of an OBJECT IDENTIFIER - it is referenced, not copied, and must
+ remain available while the TSTInfo is used.
+
+ \return 0 Returned on successfully setting the policy.
+ \return BAD_FUNC_ARG Returned when tstInfo or policy is NULL or policySz
+ is 0.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] policy Policy as OBJECT IDENTIFIER content.
+ \param [in] policySz Length of the policy in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ static const byte policy[] = { 0x2b, 0x06, 0x01, 0x04, 0x01 };
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetPolicy(&tst, policy, sizeof(policy));
+ \endcode
+
+ \sa wc_TspTstInfo_GetPolicy
+*/
+int wc_TspTstInfo_SetPolicy(TspTstInfo* tstInfo, const byte* policy,
+ word32 policySz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the message imprint of a TSTInfo - the hash and
+ hash algorithm of the time-stamped data. Each output is optional - pass
+ NULL to not retrieve it. The hash references the TSTInfo.
+
+ \return 0 Returned on successfully getting the message imprint.
+ \return BAD_FUNC_ARG Returned when tstInfo is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [out] hashOID Hash algorithm OID sum. May be NULL.
+ \param [out] hash Hash of the time-stamped data. May be NULL.
+ \param [out] hashSz Length of the hash in bytes. May be NULL.
+
+ _Example_
+ \code
+ TspTstInfo tst; // decoded TSTInfo
+ word32 hashOID;
+ const byte* hash;
+ word32 hashSz;
+
+ if (wc_TspTstInfo_GetMsgImprint(&tst, &hashOID, &hash, &hashSz) != 0) {
+ // error getting message imprint
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetMsgImprint
+*/
+int wc_TspTstInfo_GetMsgImprint(const TspTstInfo* tstInfo, word32* hashOID,
+ const byte** hash, word32* hashSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the message imprint of a TSTInfo. The hash is the
+ digest of the data being time-stamped - it is copied into the TSTInfo. The
+ hash and algorithm are typically those of the request.
+
+ \return 0 Returned on successfully setting the message imprint.
+ \return BAD_FUNC_ARG Returned when tstInfo or hash is NULL or hashSz is 0.
+ \return BUFFER_E Returned when hashSz is too big for the message imprint.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] hashOID Hash algorithm OID sum: SHA256h, etc.
+ \param [in] hash Hash of the data to time-stamp.
+ \param [in] hashSz Length of the hash in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ byte hash[32]; // SHA-256 of the data, e.g. from the request
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, hash, sizeof(hash));
+ \endcode
+
+ \sa wc_TspTstInfo_GetMsgImprint
+*/
+int wc_TspTstInfo_SetMsgImprint(TspTstInfo* tstInfo, word32 hashOID,
+ const byte* hash, word32 hashSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the time of the time-stamp of a TSTInfo as a
+ GeneralizedTime string of RFC 3161 - "YYYYMMDDhhmmss[.s...]Z". A reference
+ is returned - the string is not copied and is valid while the TSTInfo
+ references it.
+
+ \return 0 Returned on successfully getting the time.
+ \return BAD_FUNC_ARG Returned when tstInfo, genTime or genTimeSz is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [out] genTime Time as a GeneralizedTime string.
+ \param [out] genTimeSz Length of the string in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst; // decoded TSTInfo
+ const byte* genTime;
+ word32 genTimeSz;
+
+ if (wc_TspTstInfo_GetGenTime(&tst, &genTime, &genTimeSz) != 0) {
+ // error getting time
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetGenTime
+ \sa wc_TspTstInfo_GetGenTimeAsTime
+*/
+int wc_TspTstInfo_GetGenTime(const TspTstInfo* tstInfo, const byte** genTime,
+ word32* genTimeSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the time of the time-stamp of a TSTInfo. The
+ genTime is a GeneralizedTime string of RFC 3161 - it is referenced, not
+ copied, and must remain available while the TSTInfo is used. The syntax is
+ checked on encode. Leave unset to use the current time on encode.
+
+ \return 0 Returned on successfully setting the time.
+ \return BAD_FUNC_ARG Returned when tstInfo or genTime is NULL or genTimeSz
+ is 0.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] genTime Time as a GeneralizedTime string.
+ \param [in] genTimeSz Length of the string in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ static const byte genTime[] = "20260625120000Z";
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetGenTime(&tst, genTime, sizeof(genTime) - 1);
+ \endcode
+
+ \sa wc_TspTstInfo_GetGenTime
+ \sa wc_TspTstInfo_SetGenTimeAsTime
+*/
+int wc_TspTstInfo_SetGenTime(TspTstInfo* tstInfo, const byte* genTime,
+ word32 genTimeSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the accuracy of the time of a TSTInfo - the
+ seconds, milliseconds and microseconds the genTime may be off by. Each
+ output is optional - pass NULL to not retrieve it. A value of 0 means that
+ part of the accuracy is not present.
+
+ \return 0 Returned on successfully getting the accuracy.
+ \return BAD_FUNC_ARG Returned when tstInfo is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [out] seconds Accuracy in seconds. May be NULL.
+ \param [out] millis Accuracy in milliseconds. May be NULL.
+ \param [out] micros Accuracy in microseconds. May be NULL.
+
+ _Example_
+ \code
+ TspTstInfo tst; // decoded TSTInfo
+ word32 seconds;
+ word16 millis;
+ word16 micros;
+
+ if (wc_TspTstInfo_GetAccuracy(&tst, &seconds, &millis, µs) != 0) {
+ // error getting accuracy
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetAccuracy
+*/
+int wc_TspTstInfo_GetAccuracy(const TspTstInfo* tstInfo, word32* seconds,
+ word16* millis, word16* micros);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the accuracy of the time of a TSTInfo - how far
+ the genTime may be off. A value of 0 for a part means it is not present.
+ Milliseconds and microseconds must be 1..999 - checked on encode.
+
+ \return 0 Returned on successfully setting the accuracy.
+ \return BAD_FUNC_ARG Returned when tstInfo is NULL.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] seconds Accuracy in seconds.
+ \param [in] millis Accuracy in milliseconds.
+ \param [in] micros Accuracy in microseconds.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetAccuracy(&tst, 1, 0, 0); // accurate to one second
+ \endcode
+
+ \sa wc_TspTstInfo_GetAccuracy
+*/
+int wc_TspTstInfo_SetAccuracy(TspTstInfo* tstInfo, word32 seconds,
+ word16 millis, word16 micros);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the nonce of a TSTInfo. A reference is returned -
+ the nonce is not copied and is valid while the TSTInfo references it. A
+ length of 0 means no nonce is present.
+
+ \return 0 Returned on successfully getting the nonce.
+ \return BAD_FUNC_ARG Returned when tstInfo, nonce or nonceSz is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [out] nonce Nonce as a big-endian number.
+ \param [out] nonceSz Length of the nonce in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst; // decoded TSTInfo
+ const byte* nonce;
+ word32 nonceSz;
+
+ if (wc_TspTstInfo_GetNonce(&tst, &nonce, &nonceSz) != 0) {
+ // error getting nonce
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetNonce
+*/
+int wc_TspTstInfo_GetNonce(const TspTstInfo* tstInfo, const byte** nonce,
+ word32* nonceSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the nonce of a TSTInfo. The nonce is referenced,
+ not copied, and must remain available while the TSTInfo is used. It must
+ match the request's nonce. Leading zero bytes are stripped, keeping at
+ least one byte, so it has no leading zero byte and encodes.
+
+ \return 0 Returned on successfully setting the nonce.
+ \return BAD_FUNC_ARG Returned when tstInfo or nonce is NULL or nonceSz is 0.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] nonce Nonce as a big-endian number.
+ \param [in] nonceSz Length of the nonce in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ const byte* nonce; // the request's nonce
+ word32 nonceSz;
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetNonce(&tst, nonce, nonceSz);
+ \endcode
+
+ \sa wc_TspTstInfo_GetNonce
+*/
+int wc_TspTstInfo_SetNonce(TspTstInfo* tstInfo, const byte* nonce,
+ word32 nonceSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the values of a TSTInfo to respond to a request.
+ A convenience for a TSA building a response: it echoes the request's
+ message imprint (copied) and nonce (referenced), and sets the TSA's policy,
+ serial number and time. Initialize the TSTInfo with wc_TspTstInfo_Init()
+ first. The request, policy, serial and genTime buffers are referenced - not
+ copied, except the imprint - and must remain available while the TSTInfo is
+ used.
+
+ \return 0 Returned on successfully setting the values.
+ \return BAD_FUNC_ARG Returned when tstInfo, req, policy or serial is NULL or
+ policySz or serialSz is 0.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to set.
+ \param [in] req Decoded request being time-stamped.
+ \param [in] policy TSA policy as OBJECT IDENTIFIER content.
+ \param [in] policySz Length of the policy in bytes.
+ \param [in] serial Serial number of the time-stamp - big-endian, no leading
+ zero byte.
+ \param [in] serialSz Length of the serial number in bytes.
+ \param [in] genTime Time of the time-stamp as a GeneralizedTime string.
+ NULL to use the current time on encode.
+ \param [in] genTimeSz Length of genTime in bytes - 0 when NULL.
+
+ _Example_
+ \code
+ TspRequest req; // decoded request
+ TspTstInfo tst;
+ static const byte policy[] = { 0x2b, 0x06, 0x01, 0x04, 0x01 };
+ static const byte serial[] = { 0x01 };
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetFromRequest(&tst, &req, policy, sizeof(policy),
+ serial, sizeof(serial), NULL, 0);
+ \endcode
+
+ \sa wc_TspTstInfo_Init
+ \sa wc_TspTstInfo_SignWithPkcs7
+*/
+int wc_TspTstInfo_SetFromRequest(TspTstInfo* tstInfo, const TspRequest* req,
+ const byte* policy, word32 policySz, const byte* serial, word32 serialSz,
+ const byte* genTime, word32 genTimeSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function encodes a TSTInfo as DER - RFC 3161, 2.4.2. The
+ policy, message imprint and serial number are required. When genTime is
+ NULL the current time is used - requires a real time clock and is not
+ available with NO_ASN_TIME, USER_TIME or TIME_OVERRIDES. The TSTInfo is
+ the content that a TSA signs into a time-stamp token - see
+ wc_TspTstInfo_SignWithPkcs7() which encodes and signs in one call.
+
+ \return 0 Returned on successfully encoding the TSTInfo.
+ \return BAD_FUNC_ARG Returned when tstInfo or outSz is NULL, a required
+ field is not set or empty, the hash is too long, the genTime is not a
+ valid GeneralizedTime, the tsa is empty, the serial number or nonce is
+ empty or has a leading zero byte or accuracy millis or micros is out of
+ range.
+ \return BUFFER_E Returned when out is not NULL and the encoding is
+ longer than outSz.
+ \return ASN_UNKNOWN_OID_E Returned when the hash algorithm is not
+ recognized.
+ \return ASN_TIME_E Returned when getting the current time failed.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure to encode.
+ \param [out] out Buffer to hold the encoding. May be NULL to get the
+ length.
+ \param [in,out] outSz On in, the length of the buffer in bytes. On out,
+ the length of the encoding in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ byte der[512];
+ word32 derSz = (word32)sizeof(der);
+
+ // initialize tst and set required fields
+ if (wc_TspTstInfo_Encode(&tst, der, &derSz) != 0) {
+ // error encoding TSTInfo
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_Init
+ \sa wc_TspTstInfo_Decode
+ \sa wc_TspTstInfo_SignWithPkcs7
+*/
+int wc_TspTstInfo_Encode(const TspTstInfo* tstInfo, byte* out, word32* outSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function decodes a DER encoded TSTInfo - RFC 3161, 2.4.2.
+ Pointers in the structure reference into the input buffer - the buffer
+ must remain available while the structure is in use. The message
+ imprint hash is copied. TSTInfos with extensions are not supported and
+ fail to decode. See wc_TspTstInfo_VerifyWithPKCS7() which verifies a token and
+ decodes its TSTInfo.
+
+ \return 0 Returned on successfully decoding the TSTInfo.
+ \return BAD_FUNC_ARG Returned when tstInfo or input is NULL or inSz
+ is 0.
+ \return ASN_PARSE_E Returned when the encoding is invalid, the hash is
+ empty, accuracy millis or micros is out of range, the genTime is not a
+ valid GeneralizedTime or extensions are present.
+ \return ASN_UNKNOWN_OID_E Returned when the hash algorithm OID check
+ fails.
+ \return BUFFER_E Returned when the hash is longer than
+ WC_TSP_MAX_HASH_SZ bytes.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [out] tstInfo Pointer to the TspTstInfo structure to fill.
+ \param [in] input Buffer holding the DER encoding.
+ \param [in] inSz Length of the data in the buffer in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ byte* der; // DER encoded TSTInfo
+ word32 derSz; // length of DER encoded TSTInfo
+
+ if (wc_TspTstInfo_Decode(&tst, der, derSz) != 0) {
+ // error decoding TSTInfo
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_Encode
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+*/
+int wc_TspTstInfo_Decode(TspTstInfo* tstInfo, const byte* input, word32 inSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function checks the genTime of a TSTInfo is close enough to
+ the current time - RFC 3161, 2.4.2: the requester verifies the genTime
+ is within an acceptable period of the local trusted time. Any fraction
+ of a second in the genTime is ignored. Requires a real time clock and
+ is not available with NO_ASN_TIME, USER_TIME or TIME_OVERRIDES.
+
+ \return 0 Returned when the genTime is within the acceptable period.
+ \return BAD_FUNC_ARG Returned when tstInfo or its genTime is NULL.
+ \return ASN_PARSE_E Returned when the genTime string is not valid.
+ \return ASN_TIME_E Returned when getting the current time failed.
+ \return TSP_VERIFY_E Returned when the genTime is outside the
+ acceptable period.
+
+ \param [in] tstInfo Pointer to the decoded TspTstInfo structure from a
+ response.
+ \param [in] tolerance Acceptable time around the current time in
+ seconds.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ // verify token and decode TSTInfo into tst
+
+ // accept a time-stamp within five minutes of the current time
+ if (wc_TspTstInfo_CheckGenTime(&tst, 300) != 0) {
+ // time-stamp is not fresh
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+ \sa wc_TspTstInfo_CheckRequest
+*/
+int wc_TspTstInfo_CheckGenTime(const TspTstInfo* tstInfo, word32 tolerance);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the time of the time-stamp of a TSTInfo as a
+ time_t. The genTime GeneralizedTime string of RFC 3161 is parsed - any
+ fraction of a second is ignored. The time is UTC. Not available with
+ NO_ASN_TIME.
+
+ \return 0 Returned on successfully getting the time.
+ \return BAD_FUNC_ARG Returned when tstInfo, its genTime or t is NULL.
+ \return ASN_PARSE_E Returned when the genTime string is not valid.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure to query.
+ \param [out] t Time of the time-stamp as seconds since the Unix epoch.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ time_t t;
+ // verify token and decode TSTInfo into tst
+
+ if (wc_TspTstInfo_GetGenTimeAsTime(&tst, &t) != 0) {
+ // genTime is not valid
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SetGenTimeAsTime
+ \sa wc_TspTstInfo_GetGenTime
+*/
+int wc_TspTstInfo_GetGenTimeAsTime(const TspTstInfo* tstInfo, time_t* t);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the time of the time-stamp of a TSTInfo from a
+ time_t. The time is formatted as a GeneralizedTime string of RFC 3161
+ into the caller's buffer and referenced - the buffer must remain
+ available while the TSTInfo is used and be at least
+ ASN_GENERALIZED_TIME_SIZE bytes. The time is treated as UTC. Not
+ available with NO_ASN_TIME.
+
+ \return 0 Returned on successfully setting the time.
+ \return BAD_FUNC_ARG Returned when tstInfo or buf is NULL.
+ \return BUFFER_E Returned when bufSz is too small for the GeneralizedTime
+ string.
+ \return ASN_TIME_E Returned when the time could not be converted.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] t Time of the time-stamp as seconds since the Unix epoch.
+ \param [out] buf Buffer to hold the formatted GeneralizedTime.
+ \param [in] bufSz Length of buffer in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ byte buf[ASN_GENERALIZED_TIME_SIZE];
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetGenTimeAsTime(&tst, wc_Time(NULL), buf, sizeof(buf));
+ \endcode
+
+ \sa wc_TspTstInfo_GetGenTimeAsTime
+ \sa wc_TspTstInfo_SetGenTime
+*/
+int wc_TspTstInfo_SetGenTimeAsTime(TspTstInfo* tstInfo, time_t t, byte* buf,
+ word32 bufSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the TSA name of a TSTInfo. The tsa - the DER
+ encoding of a GeneralName - is referenced, not copied, and is valid
+ while the TSTInfo references it. A length of 0 means no TSA name is
+ present.
+
+ \return 0 Returned on successfully getting the TSA name.
+ \return BAD_FUNC_ARG Returned when tstInfo, tsa or tsaSz is NULL.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure to query.
+ \param [out] tsa TSA name as the DER encoding of a GeneralName.
+ \param [out] tsaSz Length of the TSA name in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ const byte* tsa = NULL;
+ word32 tsaSz = 0;
+ // verify token and decode TSTInfo into tst
+
+ wc_TspTstInfo_GetTsa(&tst, &tsa, &tsaSz);
+ \endcode
+
+ \sa wc_TspTstInfo_SetTsa
+*/
+int wc_TspTstInfo_GetTsa(const TspTstInfo* tstInfo, const byte** tsa,
+ word32* tsaSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the TSA name of a TSTInfo. The tsa is the DER
+ encoding of a GeneralName - it is referenced, not copied, and must
+ remain available while the TSTInfo is used.
+
+ \return 0 Returned on successfully setting the TSA name.
+ \return BAD_FUNC_ARG Returned when tstInfo or tsa is NULL or tsaSz is 0.
+
+ \param [in,out] tstInfo Pointer to the TspTstInfo structure to update.
+ \param [in] tsa TSA name as the DER encoding of a GeneralName.
+ \param [in] tsaSz Length of the TSA name in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ // DER encoding of a GeneralName for the TSA
+ static const byte tsa[] = { 0x82, 0x03, 't', 's', 'a' };
+
+ wc_TspTstInfo_Init(&tst);
+ wc_TspTstInfo_SetTsa(&tst, tsa, sizeof(tsa));
+ \endcode
+
+ \sa wc_TspTstInfo_GetTsa
+*/
+int wc_TspTstInfo_SetTsa(TspTstInfo* tstInfo, const byte* tsa, word32 tsaSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function initializes a TimeStampResp structure for
+ encoding. All fields are cleared - the status is granted.
+
+ \return 0 Returned on successfully initializing the response.
+ \return BAD_FUNC_ARG Returned when resp is NULL.
+
+ \param [out] resp Pointer to the TspResponse structure to initialize.
+
+ _Example_
+ \code
+ TspResponse resp;
+
+ wc_TspResponse_Init(&resp);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ // set the token created with wc_TspTstInfo_SignWithPkcs7()
+ \endcode
+
+ \sa wc_TspResponse_Encode
+ \sa wc_TspResponse_Decode
+*/
+int wc_TspResponse_Init(TspResponse* resp);
+
+/*!
+ \ingroup TSP
+
+ \brief This function encodes a TimeStampResp as DER - RFC 3161, 2.4.2.
+ The status string, when set, is encoded as a PKIFreeText with one
+ UTF8String. The failure information, when not zero, is encoded as a
+ BIT STRING of the WC_TSP_FAIL_* flags. The token, when set, is the
+ complete DER encoding from wc_TspTstInfo_SignWithPkcs7().
+
+ \return 0 Returned on successfully encoding the response.
+ \return BAD_FUNC_ARG Returned when resp or outSz is NULL.
+ \return BUFFER_E Returned when out is not NULL and the encoding is
+ longer than outSz.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] resp Pointer to the TspResponse structure to encode.
+ \param [out] out Buffer to hold the encoding. May be NULL to get the
+ length.
+ \param [in,out] outSz On in, the length of the buffer in bytes. On out,
+ the length of the encoding in bytes.
+
+ _Example_
+ \code
+ TspResponse resp;
+ byte der[4096];
+ word32 derSz = (word32)sizeof(der);
+
+ wc_TspResponse_Init(&resp);
+ resp.status = WC_TSP_PKISTATUS_REJECTION;
+ resp.failInfo = WC_TSP_FAIL_BAD_ALG;
+ if (wc_TspResponse_Encode(&resp, der, &derSz) != 0) {
+ // error encoding response
+ }
+ \endcode
+
+ \sa wc_TspResponse_Init
+ \sa wc_TspTstInfo_SignWithPkcs7
+ \sa wc_TspResponse_Decode
+*/
+int wc_TspResponse_Encode(const TspResponse* resp, byte* out, word32* outSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function decodes a DER encoded TimeStampResp - RFC 3161,
+ 2.4.2. Pointers in the structure reference into the input buffer - the
+ buffer must remain available while the structure is in use. The TSTInfo
+ of the token is not validated or decoded - see wc_TspTstInfo_VerifyWithPKCS7().
+
+ \return 0 Returned on successfully decoding the response.
+ \return BAD_FUNC_ARG Returned when resp or input is NULL or inSz is 0.
+ \return ASN_PARSE_E Returned when the encoding is invalid.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [out] resp Pointer to the TspResponse structure to fill.
+ \param [in] input Buffer holding the DER encoding.
+ \param [in] inSz Length of the data in the buffer in bytes.
+
+ _Example_
+ \code
+ TspResponse resp;
+ byte* der; // DER encoded response received
+ word32 derSz; // length of DER encoded response
+
+ if (wc_TspResponse_Decode(&resp, der, derSz) != 0) {
+ // error decoding response
+ }
+ if ((resp.status != WC_TSP_PKISTATUS_GRANTED) &&
+ (resp.status != WC_TSP_PKISTATUS_GRANTED_WITH_MODS)) {
+ // time-stamp not granted - see resp.failInfo
+ }
+ \endcode
+
+ \sa wc_TspResponse_Encode
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+*/
+int wc_TspResponse_Decode(TspResponse* resp, const byte* input, word32 inSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function gets the status information of a TimeStampResp. Each
+ output is optional - pass NULL to not retrieve it. The status string and
+ failure information are present only when set; a NULL status string and a
+ failure information of 0 mean they are absent.
+
+ \return 0 Returned on successfully getting the status information.
+ \return BAD_FUNC_ARG Returned when resp is NULL.
+
+ \param [in] resp Pointer to the TspResponse structure to query.
+ \param [out] status PKIStatus value - see TspPkiStatus. May be NULL.
+ \param [out] str Status text, UTF-8 encoded with no NUL terminator. May be
+ NULL.
+ \param [out] strSz Length of the status text in bytes. May be NULL.
+ \param [out] failInfo Failure information - WC_TSP_FAIL_* flags. May be
+ NULL.
+
+ _Example_
+ \code
+ TspResponse resp;
+ word32 status = 0;
+ const byte* str = NULL;
+ word32 strSz = 0;
+ word32 failInfo = 0;
+ // decode resp from a received response
+
+ wc_TspResponse_GetStatus(&resp, &status, &str, &strSz, &failInfo);
+ \endcode
+
+ \sa wc_TspResponse_SetStatus
+ \sa wc_TspStatus_ToString
+ \sa wc_TspFailInfo_ToString
+*/
+int wc_TspResponse_GetStatus(const TspResponse* resp, word32* status,
+ const byte** str, word32* strSz, word32* failInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function sets the status information of a TimeStampResp. The
+ status string, when not NULL, is referenced - not copied - and must
+ remain available while the response is used; a NULL string clears the
+ status text. The failure information is the WC_TSP_FAIL_* flags or 0 when
+ not present.
+
+ \return 0 Returned on successfully setting the status information.
+ \return BAD_FUNC_ARG Returned when resp is NULL.
+
+ \param [in,out] resp Pointer to the TspResponse structure to update.
+ \param [in] status PKIStatus value - see TspPkiStatus.
+ \param [in] str Status text, UTF-8 encoded with no NUL terminator. May be
+ NULL.
+ \param [in] strSz Length of the status text in bytes.
+ \param [in] failInfo Failure information - WC_TSP_FAIL_* flags or 0.
+
+ _Example_
+ \code
+ TspResponse resp;
+
+ wc_TspResponse_Init(&resp);
+ wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_REJECTION, NULL, 0,
+ WC_TSP_FAIL_BAD_ALG);
+ \endcode
+
+ \sa wc_TspResponse_GetStatus
+*/
+int wc_TspResponse_SetStatus(TspResponse* resp, word32 status,
+ const byte* str, word32 strSz, word32 failInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function returns a human-readable string for a PKIStatus
+ value - see TspPkiStatus. An unknown value returns a generic string.
+
+ \return A NUL-terminated, human-readable string for the status. Never
+ NULL.
+
+ \param [in] status PKIStatus value - see TspPkiStatus.
+
+ _Example_
+ \code
+ word32 status = 0;
+ // get status from a decoded response
+
+ printf("status: %s\n", wc_TspStatus_ToString(status));
+ \endcode
+
+ \sa wc_TspResponse_GetStatus
+ \sa wc_TspFailInfo_ToString
+*/
+const char* wc_TspStatus_ToString(word32 status);
+
+/*!
+ \ingroup TSP
+
+ \brief This function returns a human-readable string for a single
+ PKIFailureInfo flag - a WC_TSP_FAIL_* value. An unknown value returns a
+ generic string.
+
+ \return A NUL-terminated, human-readable string for the failure
+ information flag. Never NULL.
+
+ \param [in] failInfo A single WC_TSP_FAIL_* flag.
+
+ _Example_
+ \code
+ word32 failInfo = WC_TSP_FAIL_BAD_ALG;
+
+ printf("failure: %s\n", wc_TspFailInfo_ToString(failInfo));
+ \endcode
+
+ \sa wc_TspResponse_GetStatus
+ \sa wc_TspStatus_ToString
+*/
+const char* wc_TspFailInfo_ToString(word32 failInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function checks the TSTInfo of a response against the
+ request sent - RFC 3161, 2.4.2. The version is checked, the message
+ imprint must be the same and, when in the request, the nonce and policy
+ must be matched. The nonce is compared exactly. The genTime and the
+ token's signature are not validated here - see
+ wc_TspTstInfo_VerifyWithPKCS7() and wc_TspTstInfo_CheckGenTime().
+
+ \return 0 Returned when the TSTInfo matches the request.
+ \return BAD_FUNC_ARG Returned when tstInfo or req is NULL.
+ \return ASN_VERSION_E Returned when the version is not supported.
+ \return TSP_VERIFY_E Returned when a field of the TSTInfo does not
+ match the request.
+
+ \param [in] tstInfo Pointer to the decoded TspTstInfo structure from
+ the response.
+ \param [in] req Pointer to the TspRequest structure sent.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ TspRequest req; // the request sent
+ // verify token and decode TSTInfo into tst
+
+ if (wc_TspTstInfo_CheckRequest(&tst, &req) != 0) {
+ // token is not for the request
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+ \sa wc_TspTstInfo_CheckGenTime
+ \sa wc_TspTstInfo_CheckTsaName
+*/
+int wc_TspTstInfo_CheckRequest(const TspTstInfo* tstInfo, const TspRequest* req);
+
+/*!
+ \ingroup TSP
+
+ \brief This function checks the TSA name of a TSTInfo is the expected
+ name. The TSA name must be present and be the same encoding as the
+ expected name - the DER encodings of the GeneralNames are compared
+ exactly. The TSA name is also checked against the signer's certificate
+ in wc_TspTstInfo_VerifyWithPKCS7() when present.
+
+ \return 0 Returned when the TSA name is the expected name.
+ \return BAD_FUNC_ARG Returned when tstInfo or tsa is NULL or tsaSz
+ is 0.
+ \return TSP_VERIFY_E Returned when the TSA name is not present or does
+ not match the expected name.
+
+ \param [in] tstInfo Pointer to the decoded TspTstInfo structure from
+ the response.
+ \param [in] tsa Expected name as a DER encoding of a GeneralName.
+ \param [in] tsaSz Length of the expected name in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst;
+ // dNSName GeneralName: tsa.wolfssl.com
+ static const byte name[] = {
+ 0x82, 0x0f, 't', 's', 'a', '.', 'w', 'o', 'l', 'f',
+ 's', 's', 'l', '.', 'c', 'o', 'm'
+ };
+ // verify token and decode TSTInfo into tst
+
+ if (wc_TspTstInfo_CheckTsaName(&tst, name, (word32)sizeof(name)) != 0) {
+ // token is not from the expected TSA
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+ \sa wc_TspTstInfo_CheckRequest
+*/
+int wc_TspTstInfo_CheckTsaName(const TspTstInfo* tstInfo, const byte* tsa,
+ word32 tsaSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function verifies the message imprint of a TSTInfo against the
+ original data. It hashes the data with the TSTInfo's message imprint hash
+ algorithm and compares the result to the imprint hash - confirming the
+ time-stamp is over the given data. The caller does not need to hash the
+ data first.
+
+ \return 0 Returned when the hash of the data matches the message imprint.
+ \return BAD_FUNC_ARG Returned when tstInfo or data is NULL.
+ \return HASH_TYPE_E Returned when the imprint's hash algorithm is not
+ supported.
+ \return TSP_VERIFY_E Returned when the hash of the data does not match the
+ message imprint.
+ \return Other Returned, negative, on a hashing failure.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure.
+ \param [in] data Data that was time-stamped.
+ \param [in] dataSz Length of the data in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst; // verified TSTInfo from a token
+ byte* data; // the original data
+ word32 dataSz;
+
+ if (wc_TspTstInfo_VerifyData(&tst, data, dataSz) != 0) {
+ // time-stamp is not over this data
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+ \sa wc_TspResponse_VerifyData
+*/
+int wc_TspTstInfo_VerifyData(const TspTstInfo* tstInfo, const byte* data,
+ word32 dataSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function creates a time-stamp token signed with the TSA's
+ certificate and private key. A convenience wrapper around
+ wc_TspTstInfo_SignWithPkcs7() that creates and disposes of the PKCS7
+ object. The TSA's certificate is included in the token.
+
+ \return 0 Returned on successfully creating the token.
+ \return BAD_FUNC_ARG Returned when a pointer argument is NULL, a length is
+ 0 or the key type is not supported.
+ \return HASH_TYPE_E Returned when the hash algorithm is not available.
+ \return BUFFER_E Returned when the encoding is longer than outSz.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure to encode and sign.
+ \param [in] cert DER encoded certificate of the TSA.
+ \param [in] certSz Length of the certificate in bytes.
+ \param [in] key DER encoded private key of the TSA.
+ \param [in] keySz Length of the private key in bytes.
+ \param [in] keyType Type of the private key - WC_PK_TYPE_RSA or
+ WC_PK_TYPE_ECDSA_SIGN.
+ \param [in] hashType Hash algorithm for the signature - e.g.
+ WC_HASH_TYPE_SHA256.
+ \param [in] rng Random number generator.
+ \param [out] out Buffer to hold the encoding.
+ \param [in,out] outSz On in, the length of the buffer in bytes. On out, the
+ length of the encoding in bytes.
+
+ _Example_
+ \code
+ TspTstInfo tst; // TSTInfo set from the request
+ byte token[2048];
+ word32 tokenSz = (word32)sizeof(token);
+ WC_RNG rng;
+
+ wc_InitRng(&rng);
+ if (wc_TspTstInfo_Sign(&tst, cert, certSz, key, keySz, WC_PK_TYPE_RSA,
+ WC_HASH_TYPE_SHA256, &rng, token, &tokenSz) != 0) {
+ // error creating token
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_SignWithPkcs7
+ \sa wc_TspTstInfo_SetFromRequest
+*/
+int wc_TspTstInfo_Sign(const TspTstInfo* tstInfo,
+ const byte* cert, word32 certSz, const byte* key, word32 keySz,
+ enum wc_PkType keyType, enum wc_HashType hashType, WC_RNG* rng,
+ byte* out, word32* outSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function creates a time-stamp token - a CMS SignedData with
+ the encoded TSTInfo as content of type id-ct-TSTInfo. The PKCS7 object
+ must be initialized with the certificate and private key of the TSA,
+ and the hash algorithm, encryption algorithm and RNG set. A
+ SigningCertificateV2 signed attribute identifying the TSA's certificate
+ is added as required by RFC 3161, 2.4.2. The TSA's certificate is
+ included in the token - when the request did not set certReq set the
+ PKCS7 object's noCerts field so that it is not.
+
+ \return 0 Returned on successfully creating the token.
+ \return BAD_FUNC_ARG Returned when tstInfo, pkcs7, out or outSz is NULL
+ or the signer's certificate is not set.
+ \return BUFFER_E Returned when the encoding is longer than outSz.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] tstInfo Pointer to the TspTstInfo structure to encode and
+ sign.
+ \param [in] pkcs7 Pointer to the PKCS7 object with the signer
+ configured.
+ \param [out] out Buffer to hold the encoding.
+ \param [in,out] outSz On in, the length of the buffer in bytes. On out,
+ the length of the encoding in bytes.
+
+ _Example_
+ \code
+ wc_PKCS7* pkcs7;
+ WC_RNG rng;
+ TspTstInfo tst;
+ byte token[4096];
+ word32 tokenSz = (word32)sizeof(token);
+
+ // initialize rng and tst
+ pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ wc_PKCS7_InitWithCert(pkcs7, tsaCert, tsaCertSz);
+ pkcs7->rng = &rng;
+ pkcs7->hashOID = SHA256h;
+ pkcs7->encryptOID = RSAk;
+ pkcs7->privateKey = tsaKey;
+ pkcs7->privateKeySz = tsaKeySz;
+ if (wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz) != 0) {
+ // error creating token
+ }
+ wc_PKCS7_Free(pkcs7);
+ \endcode
+
+ \sa wc_TspTstInfo_Init
+ \sa wc_TspResponse_Encode
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+*/
+int wc_TspTstInfo_SignWithPkcs7(const TspTstInfo* tstInfo, wc_PKCS7* pkcs7,
+ byte* out, word32* outSz);
+
+/*!
+ \ingroup TSP
+
+ \brief This function verifies a time-stamp token and decodes the
+ TSTInfo content. The token must have a single SignerInfo and content of
+ type id-ct-TSTInfo. The signature of the CMS SignedData is verified
+ with the certificates in the token - when the token does not include
+ certificates, initialize the PKCS7 object with the TSA's certificate.
+ The signer's certificate must have a critical extended key usage of
+ time-stamping only, a key usage that is signing only when present and
+ be the certificate identified by the ESS signing certificate attribute.
+ Only the certHash of the first ESSCertID(v2) of the attribute is
+ checked. The TSA name of the TSTInfo, when present, must correspond to
+ a subject name of the signer's certificate. Trust in the TSA's
+ certificate must be established by the caller. Define
+ WC_TSP_MIN_HASH_STRENGTH_BITS to require a minimum security strength of
+ the hash algorithms used.
+
+ Pointers in tstInfo reference the content of the PKCS7 object - the
+ PKCS7 object and the token buffer must remain available while tstInfo
+ is in use.
+
+ \return 0 Returned on successfully verifying the token.
+ \return BAD_FUNC_ARG Returned when pkcs7 or token is NULL or tokenSz
+ is 0.
+ \return PKCS7_OID_E Returned when the content is not a TSTInfo.
+ \return EXTKEYUSAGE_E Returned when the signer's extended key usage is
+ not critical or not time-stamping only.
+ \return KEYUSAGE_E Returned when the signer's key usage is not for
+ signing only.
+ \return TSP_VERIFY_E Returned when the token does not have exactly one
+ SignerInfo, no signing certificate attribute is found or it does not
+ match the signer's certificate or the TSA name does not match the
+ signer's certificate.
+ \return HASH_TYPE_E Returned when a hash algorithm is not available or
+ below WC_TSP_MIN_HASH_STRENGTH_BITS.
+ \return ASN_PARSE_E Returned when an encoding is invalid.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] pkcs7 Pointer to an initialized PKCS7 object.
+ \param [in,out] token Buffer holding the DER encoding of the token.
+ \param [in] tokenSz Length of the data in the buffer in bytes.
+ \param [out] tstInfo Pointer to the TspTstInfo structure to fill. May
+ be NULL.
+
+ _Example_
+ \code
+ wc_PKCS7* pkcs7;
+ TspResponse resp;
+ TspTstInfo tst;
+ // decode response into resp and check status is granted
+
+ pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ wc_PKCS7_InitWithCert(pkcs7, NULL, 0);
+ if (wc_TspTstInfo_VerifyWithPKCS7(pkcs7, (byte*)resp.token, resp.tokenSz,
+ &tst) != 0) {
+ // token did not verify
+ }
+ // check tst against the request and establish trust in the signer
+ wc_PKCS7_Free(pkcs7); // tst references pkcs7 - free after use
+ \endcode
+
+ \sa wc_TspResponse_Decode
+ \sa wc_TspTstInfo_CheckRequest
+ \sa wc_TspTstInfo_CheckGenTime
+ \sa wc_TspTstInfo_CheckTsaName
+*/
+int wc_TspTstInfo_VerifyWithPKCS7(wc_PKCS7* pkcs7, byte* token, word32 tokenSz,
+ TspTstInfo* tstInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function verifies the time-stamp token of a response and
+ decodes its TSTInfo content. A convenience wrapper around
+ wc_TspTstInfo_VerifyWithPKCS7() that manages the PKCS7 object. The response
+ must be granted and have a token. When cert is not NULL, the signer must
+ be that trusted TSA certificate; the certificate is also used to verify
+ the signature when the token does not include the signer's certificate.
+ When cert is NULL the token must include the signer's certificate and
+ trust must be established by other means.
+
+ Pointers in tstInfo reference the token of the response - the response
+ and its token buffer must remain available while tstInfo is in use.
+
+ \return 0 Returned on successfully verifying the response.
+ \return BAD_FUNC_ARG Returned when resp is NULL.
+ \return TSP_VERIFY_E Returned when the response was not granted, has no
+ token, the token does not verify or the signer is not the trusted TSA
+ certificate.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] resp Pointer to the TspResponse structure with a token to
+ verify.
+ \param [in] cert DER encoded certificate of the trusted TSA. May be NULL
+ when the token includes the signer's certificate.
+ \param [in] certSz Length of the certificate in bytes.
+ \param [out] tstInfo Pointer to the TspTstInfo structure to fill. May be
+ NULL.
+
+ _Example_
+ \code
+ TspResponse resp;
+ TspTstInfo tst;
+ // decode resp from a received response
+
+ if (wc_TspResponse_Verify(&resp, tsaCert, tsaCertSz, &tst) != 0) {
+ // response did not verify
+ }
+ \endcode
+
+ \sa wc_TspTstInfo_VerifyWithPKCS7
+ \sa wc_TspTstInfo_CheckRequest
+ \sa wc_TspTstInfo_CheckGenTime
+*/
+int wc_TspResponse_Verify(TspResponse* resp, const byte* cert, word32 certSz,
+ TspTstInfo* tstInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function verifies the time-stamp token of a response, trusting
+ the signer via a certificate manager. A convenience wrapper around
+ wc_TspTstInfo_VerifyWithPKCS7() that manages the PKCS7 object. The response
+ must be granted and have a token. The token's signature is verified and the
+ signer's certificate checked, then the signer's certificate is verified to
+ chain to a trusted CA in the manager. The token must include the signer's
+ certificate - the manager must hold the trust anchor and any intermediate
+ CAs needed to build the chain. Certificates carried in the token are used
+ to verify the token's signature but are not trusted as CAs.
+
+ Pointers in tstInfo reference the token of the response - the response and
+ its token buffer must remain available while tstInfo is in use.
+
+ \return 0 Returned on successfully verifying the response.
+ \return BAD_FUNC_ARG Returned when resp or cm is NULL.
+ \return TSP_VERIFY_E Returned when the response was not granted, has no
+ token, the token does not verify or the signer does not chain to a trusted
+ CA.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] resp Pointer to the TspResponse structure with a token to
+ verify.
+ \param [in] cm WOLFSSL_CERT_MANAGER with the trusted CAs - passed as a void
+ pointer to avoid an SSL layer dependency.
+ \param [out] tstInfo Pointer to the TspTstInfo structure to fill. May be
+ NULL.
+
+ _Example_
+ \code
+ TspResponse resp;
+ TspTstInfo tst;
+ WOLFSSL_CERT_MANAGER* cm; // loaded with the trust anchor and intermediates
+ // decode resp from a received response
+
+ if (wc_TspResponse_VerifyWithCm(&resp, cm, &tst) != 0) {
+ // response did not verify
+ }
+ \endcode
+
+ \sa wc_TspResponse_Verify
+ \sa wc_TspResponse_VerifyData
+*/
+int wc_TspResponse_VerifyWithCm(TspResponse* resp, void* cm,
+ TspTstInfo* tstInfo);
+
+/*!
+ \ingroup TSP
+
+ \brief This function verifies the time-stamp token of a response and that
+ it is over the given data. A convenience over wc_TspResponse_Verify() that
+ also confirms the time-stamp is over the data - hashing the data with the
+ token's message imprint algorithm and comparing to the imprint. The caller
+ does not hash the data.
+
+ \return 0 Returned on successfully verifying the response and data.
+ \return BAD_FUNC_ARG Returned when resp or data is NULL.
+ \return TSP_VERIFY_E Returned when the token does not verify or the data
+ does not match the message imprint.
+ \return HASH_TYPE_E Returned when the imprint's hash algorithm is not
+ supported.
+ \return MEMORY_E Returned on dynamic memory allocation failure.
+
+ \param [in] resp Pointer to the TspResponse structure with a token to
+ verify.
+ \param [in] cert DER encoded certificate of the trusted TSA. May be NULL -
+ see wc_TspResponse_Verify().
+ \param [in] certSz Length of the certificate in bytes.
+ \param [in] data Data that was time-stamped.
+ \param [in] dataSz Length of the data in bytes.
+ \param [out] tstInfo Pointer to the TspTstInfo structure to fill. May be
+ NULL.
+
+ _Example_
+ \code
+ TspResponse resp;
+ byte* data; // the original data
+ word32 dataSz;
+ // decode resp from a received response
+
+ if (wc_TspResponse_VerifyData(&resp, tsaCert, tsaCertSz, data, dataSz,
+ NULL) != 0) {
+ // response did not verify or is not over this data
+ }
+ \endcode
+
+ \sa wc_TspResponse_Verify
+ \sa wc_TspTstInfo_VerifyData
+*/
+int wc_TspResponse_VerifyData(TspResponse* resp, const byte* cert,
+ word32 certSz, const byte* data, word32 dataSz, TspTstInfo* tstInfo);
diff --git a/examples/include.am b/examples/include.am
index 6235d147f83..2a14caf730d 100644
--- a/examples/include.am
+++ b/examples/include.am
@@ -11,5 +11,6 @@ include examples/sctp/include.am
include examples/configs/include.am
include examples/asn1/include.am
include examples/pem/include.am
+include examples/tsp/include.am
include examples/ocsp_responder/include.am
EXTRA_DIST += examples/README.md
diff --git a/examples/tsp/include.am b/examples/tsp/include.am
new file mode 100644
index 00000000000..cce8ec1bc96
--- /dev/null
+++ b/examples/tsp/include.am
@@ -0,0 +1,21 @@
+# vim:ft=automake
+# included from Top Level Makefile.am
+# All paths should be given relative to the root
+
+
+if BUILD_EXAMPLE_TSP
+noinst_PROGRAMS += examples/tsp/tsp_query examples/tsp/tsp_reply \
+ examples/tsp/tsp_verify
+
+examples_tsp_tsp_query_SOURCES = examples/tsp/tsp_query.c
+examples_tsp_tsp_query_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
+examples_tsp_tsp_query_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
+
+examples_tsp_tsp_reply_SOURCES = examples/tsp/tsp_reply.c
+examples_tsp_tsp_reply_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
+examples_tsp_tsp_reply_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
+
+examples_tsp_tsp_verify_SOURCES = examples/tsp/tsp_verify.c
+examples_tsp_tsp_verify_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
+examples_tsp_tsp_verify_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
+endif
diff --git a/examples/tsp/tsp_query.c b/examples/tsp/tsp_query.c
new file mode 100644
index 00000000000..b9974230618
--- /dev/null
+++ b/examples/tsp/tsp_query.c
@@ -0,0 +1,232 @@
+/* tsp_query.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/* Time-Stamp Protocol (RFC 3161) example: create a request.
+ *
+ * tsp_query
+ * Hash the file with SHA-256 and write a time-stamp request.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#ifndef WOLFSSL_USER_SETTINGS
+ #include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ (!defined(NO_RSA) || defined(HAVE_ECC)) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && !defined(NO_FILESYSTEM) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+
+/* Number of random bytes in a nonce. */
+#define TSP_NUM_SZ 8
+
+/* Maximum size of an encoded time-stamp request - a hash imprint, nonce and
+ * a few small fields. */
+#ifndef WC_TSP_MAX_REQ_SZ
+ #define WC_TSP_MAX_REQ_SZ 512
+#endif
+
+/* Size of the buffer used to hash a file a chunk at a time. */
+#ifndef WC_TSP_HASH_CHUNK_SZ
+ #define WC_TSP_HASH_CHUNK_SZ 256
+#endif
+
+/* Local variables larger than 63 bytes - big buffers and big structures - are
+ * kept off the stack. They are allocated from the heap, unless dynamic memory
+ * is not available (WOLFSSL_NO_MALLOC) in which case they go on the stack.
+ * WOLFSSL_SMALL_STACK uses the heap path; it never co-exists with
+ * WOLFSSL_NO_MALLOC. 'name' is always used as a pointer.
+ *
+ * TSP_DECL declares, TSP_ALLOC allocates (running 'fail' on failure) and
+ * TSP_FREE releases an array of 'cnt' items of 'type' (cnt is 1 for a single
+ * object). */
+#ifdef WOLFSSL_NO_MALLOC
+ #define TSP_DECL(type, name, cnt) type name[cnt]
+ #define TSP_ALLOC(type, name, cnt, fail) WC_DO_NOTHING
+ #define TSP_FREE(name) WC_DO_NOTHING
+#else
+ #define TSP_DECL(type, name, cnt) type* name = NULL
+ #define TSP_ALLOC(type, name, cnt, fail) \
+ do { \
+ (name) = (type*)XMALLOC(sizeof(type) * (cnt), NULL, \
+ DYNAMIC_TYPE_TMP_BUFFER); \
+ if ((name) == NULL) { \
+ fail; \
+ } \
+ } while (0)
+ #define TSP_FREE(name) \
+ XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER)
+#endif
+
+/* Hash a file with SHA-256 a chunk at a time so the whole file need not be
+ * held in memory. */
+static int tsp_hash_file(const char* name, byte* hash)
+{
+ int ret = -1;
+ FILE* f = NULL;
+ TSP_DECL(wc_Sha256, sha, 1);
+ TSP_DECL(byte, buf, WC_TSP_HASH_CHUNK_SZ);
+ size_t n;
+
+ TSP_ALLOC(wc_Sha256, sha, 1, goto done);
+ TSP_ALLOC(byte, buf, WC_TSP_HASH_CHUNK_SZ, goto done);
+
+ f = fopen(name, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s\n", name);
+ goto done;
+ }
+ if (wc_InitSha256(sha) == 0) {
+ ret = 0;
+ while ((n = fread(buf, 1, WC_TSP_HASH_CHUNK_SZ, f)) > 0) {
+ if (wc_Sha256Update(sha, buf, (word32)n) != 0) {
+ ret = -1;
+ break;
+ }
+ }
+ if ((ret == 0) && (ferror(f) != 0))
+ ret = -1;
+ if (ret == 0)
+ ret = (wc_Sha256Final(sha, hash) == 0) ? 0 : -1;
+ wc_Sha256Free(sha);
+ }
+
+done:
+ if (f != NULL)
+ fclose(f);
+ TSP_FREE(sha);
+ TSP_FREE(buf);
+ return ret;
+}
+
+/* Write a buffer to a file. */
+static int tsp_write_file(const char* name, const byte* data, word32 sz)
+{
+ int ret = -1;
+ FILE* f;
+
+ f = fopen(name, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s\n", name);
+ return -1;
+ }
+ if (fwrite(data, 1, sz, f) == sz) {
+ ret = 0;
+ }
+ fclose(f);
+ return ret;
+}
+
+/* Create a time-stamp request for the file. */
+static int tsp_query(const char* dataFile, const char* reqFile)
+{
+ int ret = 1;
+ int r;
+ WC_RNG rng;
+ int rngInit = 0;
+ TSP_DECL(TspRequest, req, 1);
+ TSP_DECL(byte, enc, WC_TSP_MAX_REQ_SZ);
+ word32 encSz = WC_TSP_MAX_REQ_SZ;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ byte nonce[TSP_NUM_SZ];
+
+ TSP_ALLOC(TspRequest, req, 1, goto done);
+ TSP_ALLOC(byte, enc, WC_TSP_MAX_REQ_SZ, goto done);
+
+ /* Hash the data to be time-stamped - the TSA never sees the data. */
+ r = wc_TspRequest_Init(req);
+ if (r != 0)
+ goto done;
+ /* Set the message imprint hash algorithm, then its value. */
+ r = wc_TspRequest_SetHashType(req, WC_HASH_TYPE_SHA256);
+ if (r != 0)
+ goto done;
+ if (tsp_hash_file(dataFile, hash) != 0)
+ goto done;
+ r = wc_TspRequest_SetHash(req, hash, (word32)sizeof(hash));
+ if (r != 0)
+ goto done;
+
+ /* Random nonce to tie the response to this request - SetNonce strips
+ * any leading zero bytes so the nonce is encodable. */
+ if (wc_InitRng(&rng) != 0)
+ goto done;
+ rngInit = 1;
+ if (wc_RNG_GenerateBlock(&rng, nonce, (word32)sizeof(nonce)) != 0)
+ goto done;
+ r = wc_TspRequest_SetNonce(req, nonce, (word32)sizeof(nonce));
+ if (r != 0)
+ goto done;
+ /* Ask for the TSA's certificate to be included in the token. */
+ req->certReq = 1;
+
+ r = wc_TspRequest_Encode(req, enc, &encSz);
+ if (r != 0) {
+ fprintf(stderr, "encode request failed: %s\n", wc_GetErrorString(r));
+ goto done;
+ }
+ if (tsp_write_file(reqFile, enc, encSz) != 0)
+ goto done;
+ printf("Wrote %u byte time-stamp request to %s\n", encSz, reqFile);
+ ret = 0;
+
+done:
+ if (rngInit)
+ wc_FreeRng(&rng);
+ TSP_FREE(req);
+ TSP_FREE(enc);
+ return ret;
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s \n", argv[0]);
+ return 1;
+ }
+ return tsp_query(argv[1], argv[2]);
+}
+
+#else
+
+int main(void)
+{
+#ifdef NO_FILESYSTEM
+ fprintf(stderr, "NO_FILESYSTEM is defined\n");
+#else
+ fprintf(stderr, "Build wolfSSL with ./configure --enable-tsp\n");
+#endif
+ return 1;
+}
+
+#endif
diff --git a/examples/tsp/tsp_reply.c b/examples/tsp/tsp_reply.c
new file mode 100644
index 00000000000..772468d41c8
--- /dev/null
+++ b/examples/tsp/tsp_reply.c
@@ -0,0 +1,346 @@
+/* tsp_reply.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/* Time-Stamp Protocol (RFC 3161) example: act as a TSA and reply.
+ *
+ * tsp_reply
+ * [rsa|ecc]
+ * Write a signed response for the request.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#ifndef WOLFSSL_USER_SETTINGS
+ #include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ (!defined(NO_RSA) || defined(HAVE_ECC)) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && !defined(NO_FILESYSTEM) && \
+ defined(WOLFSSL_TSP_RESPONDER)
+
+/* TSA policy of this example: 1.3.6.1.4.1.999.1. */
+static const byte tsaPolicy[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+};
+
+/* Number of random bytes in a serial number. */
+#define TSP_NUM_SZ 8
+
+/* Maximum size of a file read into memory: a DER request, certificate or key,
+ * and the time-stamp token written. Big enough for an RSA-2048 credential and
+ * a typical time-stamp token. */
+#ifndef WC_TSP_MAX_FILE_SZ
+ #define WC_TSP_MAX_FILE_SZ 8192
+#endif
+
+/* Local variables larger than 63 bytes - big buffers and big structures - are
+ * kept off the stack. They are allocated from the heap, unless dynamic memory
+ * is not available (WOLFSSL_NO_MALLOC) in which case they go on the stack.
+ * WOLFSSL_SMALL_STACK uses the heap path; it never co-exists with
+ * WOLFSSL_NO_MALLOC. 'name' is always used as a pointer.
+ *
+ * TSP_DECL declares, TSP_ALLOC allocates (running 'fail' on failure) and
+ * TSP_FREE releases an array of 'cnt' items of 'type' (cnt is 1 for a single
+ * object). */
+#ifdef WOLFSSL_NO_MALLOC
+ #define TSP_DECL(type, name, cnt) type name[cnt]
+ #define TSP_ALLOC(type, name, cnt, fail) WC_DO_NOTHING
+ #define TSP_FREE(name) WC_DO_NOTHING
+#else
+ #define TSP_DECL(type, name, cnt) type* name = NULL
+ #define TSP_ALLOC(type, name, cnt, fail) \
+ do { \
+ (name) = (type*)XMALLOC(sizeof(type) * (cnt), NULL, \
+ DYNAMIC_TYPE_TMP_BUFFER); \
+ if ((name) == NULL) { \
+ fail; \
+ } \
+ } while (0)
+ #define TSP_FREE(name) \
+ XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER)
+#endif
+
+/* Read a whole file into the caller's buffer. Fails when the file is larger
+ * than maxSz so that no dynamic allocation of the file size is needed. */
+static int tsp_read_file(const char* name, byte* data, word32 maxSz,
+ word32* sz)
+{
+ int ret = -1;
+ FILE* f;
+ long len;
+
+ f = fopen(name, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s\n", name);
+ return -1;
+ }
+ if ((fseek(f, 0, SEEK_END) == 0) && ((len = ftell(f)) >= 0) &&
+ (fseek(f, 0, SEEK_SET) == 0)) {
+ /* Compare as unsigned long so a length above 4GB is not truncated by
+ * a word32 cast to a small value that slips under maxSz. */
+ if ((unsigned long)len > maxSz) {
+ fprintf(stderr, "%s is too big (%ld > %u)\n", name, len, maxSz);
+ }
+ else if (fread(data, 1, (size_t)len, f) == (size_t)len) {
+ *sz = (word32)len;
+ ret = 0;
+ }
+ }
+ fclose(f);
+ return ret;
+}
+
+/* Write a buffer to a file. */
+static int tsp_write_file(const char* name, const byte* data, word32 sz)
+{
+ int ret = -1;
+ FILE* f;
+
+ f = fopen(name, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s\n", name);
+ return -1;
+ }
+ if (fwrite(data, 1, sz, f) == sz) {
+ ret = 0;
+ }
+ fclose(f);
+ return ret;
+}
+
+/* Generate a random number without leading zero bytes. */
+static int tsp_rand_num(WC_RNG* rng, byte* num, word32 sz, word32* outSz)
+{
+ word32 i;
+
+ if (wc_RNG_GenerateBlock(rng, num, sz) != 0)
+ return -1;
+ /* Skip leading zero bytes - keep at least one byte. */
+ for (i = 0; (i < sz - 1) && (num[i] == 0x00); i++);
+ if (num[i] == 0x00)
+ num[i] = 0x01;
+ memmove(num, num + i, sz - i);
+ *outSz = sz - i;
+ return 0;
+}
+
+/* Write a response to the file. */
+static int tsp_write_response(const char* name, const TspResponse* resp)
+{
+ int ret = 1;
+ int r;
+ TSP_DECL(byte, enc, WC_TSP_MAX_FILE_SZ);
+ word32 encSz = WC_TSP_MAX_FILE_SZ;
+
+ TSP_ALLOC(byte, enc, WC_TSP_MAX_FILE_SZ, return 1);
+
+ r = wc_TspResponse_Encode(resp, enc, &encSz);
+ if (r != 0) {
+ fprintf(stderr, "encode response failed: %s\n", wc_GetErrorString(r));
+ goto done;
+ }
+ if (tsp_write_file(name, enc, encSz) != 0)
+ goto done;
+ printf("Wrote %u byte time-stamp response to %s\n", encSz, name);
+ ret = 0;
+
+done:
+ TSP_FREE(enc);
+ return ret;
+}
+
+/* Write a rejection response with failure information. */
+static int tsp_reject(const char* name, word32 failInfo, const char* text)
+{
+ TspResponse resp;
+
+ (void)wc_TspResponse_Init(&resp);
+ (void)wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_REJECTION,
+ (const byte*)text, (word32)strlen(text), failInfo);
+
+ printf("Rejecting request: %s\n", text);
+ return tsp_write_response(name, &resp);
+}
+
+/* Act as a TSA - create a response for the request. */
+static int tsp_reply(const char* reqFile, const char* certFile,
+ const char* keyFile, const char* respFile, const char* keyType)
+{
+ int ret = 1;
+ int r;
+ WC_RNG rng;
+ int rngInit = 0;
+ TSP_DECL(TspRequest, req, 1);
+ TSP_DECL(TspTstInfo, tst, 1);
+ TspResponse resp;
+ TSP_DECL(byte, reqDer, WC_TSP_MAX_FILE_SZ);
+ word32 reqDerSz = 0;
+ TSP_DECL(byte, cert, WC_TSP_MAX_FILE_SZ);
+ word32 certSz = 0;
+ TSP_DECL(byte, key, WC_TSP_MAX_FILE_SZ);
+ word32 keySz = 0;
+ byte serial[TSP_NUM_SZ];
+ word32 serialSz = 0;
+ TSP_DECL(byte, token, WC_TSP_MAX_FILE_SZ);
+ word32 tokenSz = WC_TSP_MAX_FILE_SZ;
+
+ TSP_ALLOC(TspRequest, req, 1, goto done);
+ TSP_ALLOC(TspTstInfo, tst, 1, goto done);
+ TSP_ALLOC(byte, reqDer, WC_TSP_MAX_FILE_SZ, goto done);
+ TSP_ALLOC(byte, cert, WC_TSP_MAX_FILE_SZ, goto done);
+ TSP_ALLOC(byte, key, WC_TSP_MAX_FILE_SZ, goto done);
+ TSP_ALLOC(byte, token, WC_TSP_MAX_FILE_SZ, goto done);
+
+ /* Load the request and the TSA's credentials. */
+ if ((tsp_read_file(reqFile, reqDer, WC_TSP_MAX_FILE_SZ, &reqDerSz) != 0) ||
+ (tsp_read_file(certFile, cert, WC_TSP_MAX_FILE_SZ, &certSz) != 0) ||
+ (tsp_read_file(keyFile, key, WC_TSP_MAX_FILE_SZ, &keySz) != 0)) {
+ goto done;
+ }
+
+ /* A request that does not parse is rejected. */
+ r = wc_TspRequest_Decode(req, reqDer, reqDerSz);
+ if (r != 0) {
+ ret = tsp_reject(respFile, WC_TSP_FAIL_BAD_DATA_FORMAT,
+ "request could not be parsed");
+ goto done;
+ }
+ /* The hash algorithm of the imprint must be one the TSA supports. */
+ if (wc_HashGetDigestSize(wc_OidGetHash((int)req->imprint.hashAlgOID)) !=
+ (int)req->imprint.hashSz) {
+ ret = tsp_reject(respFile, WC_TSP_FAIL_BAD_ALG,
+ "hash algorithm not supported");
+ goto done;
+ }
+
+ if (wc_InitRng(&rng) != 0)
+ goto done;
+ rngInit = 1;
+
+ /* Fill in the TSTInfo for the request. */
+ r = wc_TspTstInfo_Init(tst);
+ if (r != 0)
+ goto done;
+ tst->policy = tsaPolicy;
+ tst->policySz = (word32)sizeof(tsaPolicy);
+ /* Time-stamp the imprint as it was sent. */
+ tst->imprint = req->imprint;
+ /* Random serial number - a real TSA must ensure uniqueness across
+ * restarts. */
+ if (tsp_rand_num(&rng, serial, (word32)sizeof(serial), &serialSz) != 0)
+ goto done;
+ /* Leading zero bytes are stripped so the serial number encodes. */
+ if (wc_TspTstInfo_SetSerial(tst, serial, serialSz) != 0)
+ goto done;
+ /* genTime of NULL - the current time is used. */
+ tst->accuracy.seconds = 1;
+ /* The nonce must be returned when it was in the request. */
+ if (req->nonceSz != 0) {
+ tst->nonce = req->nonce;
+ tst->nonceSz = req->nonceSz;
+ }
+
+ /* Sign the TSTInfo to make a time-stamp token. This example always
+ * includes the TSA's certificate in the token. */
+ {
+ enum wc_PkType keyPkType;
+
+#ifndef HAVE_ECC
+ (void)keyType;
+#endif
+#ifdef HAVE_ECC
+ if ((keyType != NULL) && (strcmp(keyType, "ecc") == 0)) {
+ keyPkType = WC_PK_TYPE_ECDSA_SIGN;
+ }
+ else
+#endif
+ {
+#ifndef NO_RSA
+ keyPkType = WC_PK_TYPE_RSA;
+#else
+ /* RSA not available - fall back to ECC. */
+ keyPkType = WC_PK_TYPE_ECDSA_SIGN;
+#endif
+ }
+ r = wc_TspTstInfo_Sign(tst, cert, certSz, key, keySz,
+ keyPkType, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz);
+ }
+ if (r != 0) {
+ fprintf(stderr, "create token failed: %s\n", wc_GetErrorString(r));
+ goto done;
+ }
+
+ /* Put the token in a granted response. */
+ r = wc_TspResponse_Init(&resp);
+ if (r != 0)
+ goto done;
+ (void)wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_GRANTED, NULL, 0, 0);
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ ret = tsp_write_response(respFile, &resp);
+
+done:
+ if (rngInit)
+ wc_FreeRng(&rng);
+ TSP_FREE(req);
+ TSP_FREE(tst);
+ TSP_FREE(reqDer);
+ TSP_FREE(cert);
+ TSP_FREE(key);
+ TSP_FREE(token);
+ return ret;
+}
+
+int main(int argc, char* argv[])
+{
+ if ((argc != 5) && (argc != 6)) {
+ fprintf(stderr, "usage: %s "
+ " [rsa|ecc]\n", argv[0]);
+ return 1;
+ }
+ return tsp_reply(argv[1], argv[2], argv[3], argv[4],
+ (argc == 6) ? argv[5] : NULL);
+}
+
+#else
+
+int main(void)
+{
+#ifdef NO_FILESYSTEM
+ fprintf(stderr, "NO_FILESYSTEM is defined\n");
+#else
+ fprintf(stderr, "Build wolfSSL with ./configure --enable-tsp\n");
+#endif
+ return 1;
+}
+
+#endif
diff --git a/examples/tsp/tsp_verify.c b/examples/tsp/tsp_verify.c
new file mode 100644
index 00000000000..f91f4e7d06e
--- /dev/null
+++ b/examples/tsp/tsp_verify.c
@@ -0,0 +1,302 @@
+/* tsp_verify.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/* Time-Stamp Protocol (RFC 3161) example: verify a response.
+ *
+ * tsp_verify
+ * Verify a response against the data, the request sent and the
+ * trusted TSA certificate.
+ */
+
+#ifdef HAVE_CONFIG_H
+ #include
+#endif
+
+#ifndef WOLFSSL_USER_SETTINGS
+ #include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ (!defined(NO_RSA) || defined(HAVE_ECC)) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && !defined(NO_FILESYSTEM) && \
+ defined(WOLFSSL_TSP_VERIFIER)
+
+/* Maximum size of a file read into memory: a DER request, response or
+ * certificate. Big enough for an RSA-2048 credential and a typical
+ * time-stamp token. */
+#ifndef WC_TSP_MAX_FILE_SZ
+ #define WC_TSP_MAX_FILE_SZ 8192
+#endif
+
+/* Size of the buffer used to hash a file a chunk at a time. */
+#ifndef WC_TSP_HASH_CHUNK_SZ
+ #define WC_TSP_HASH_CHUNK_SZ 256
+#endif
+
+/* Local variables larger than 63 bytes - big buffers and big structures - are
+ * kept off the stack. They are allocated from the heap, unless dynamic memory
+ * is not available (WOLFSSL_NO_MALLOC) in which case they go on the stack.
+ * WOLFSSL_SMALL_STACK uses the heap path; it never co-exists with
+ * WOLFSSL_NO_MALLOC. 'name' is always used as a pointer.
+ *
+ * TSP_DECL declares, TSP_ALLOC allocates (running 'fail' on failure) and
+ * TSP_FREE releases an array of 'cnt' items of 'type' (cnt is 1 for a single
+ * object). */
+#ifdef WOLFSSL_NO_MALLOC
+ #define TSP_DECL(type, name, cnt) type name[cnt]
+ #define TSP_ALLOC(type, name, cnt, fail) WC_DO_NOTHING
+ #define TSP_FREE(name) WC_DO_NOTHING
+#else
+ #define TSP_DECL(type, name, cnt) type* name = NULL
+ #define TSP_ALLOC(type, name, cnt, fail) \
+ do { \
+ (name) = (type*)XMALLOC(sizeof(type) * (cnt), NULL, \
+ DYNAMIC_TYPE_TMP_BUFFER); \
+ if ((name) == NULL) { \
+ fail; \
+ } \
+ } while (0)
+ #define TSP_FREE(name) \
+ XFREE((name), NULL, DYNAMIC_TYPE_TMP_BUFFER)
+#endif
+
+/* Read a whole file into the caller's buffer. Fails when the file is larger
+ * than maxSz so that no dynamic allocation of the file size is needed. */
+static int tsp_read_file(const char* name, byte* data, word32 maxSz,
+ word32* sz)
+{
+ int ret = -1;
+ FILE* f;
+ long len;
+
+ f = fopen(name, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s\n", name);
+ return -1;
+ }
+ if ((fseek(f, 0, SEEK_END) == 0) && ((len = ftell(f)) >= 0) &&
+ (fseek(f, 0, SEEK_SET) == 0)) {
+ /* Compare as unsigned long so a length above 4GB is not truncated by
+ * a word32 cast to a small value that slips under maxSz. */
+ if ((unsigned long)len > maxSz) {
+ fprintf(stderr, "%s is too big (%ld > %u)\n", name, len, maxSz);
+ }
+ else if (fread(data, 1, (size_t)len, f) == (size_t)len) {
+ *sz = (word32)len;
+ ret = 0;
+ }
+ }
+ fclose(f);
+ return ret;
+}
+
+/* Hash a file with SHA-256 a chunk at a time so the whole file need not be
+ * held in memory. */
+static int tsp_hash_file(const char* name, byte* hash)
+{
+ int ret = -1;
+ FILE* f = NULL;
+ TSP_DECL(wc_Sha256, sha, 1);
+ TSP_DECL(byte, buf, WC_TSP_HASH_CHUNK_SZ);
+ size_t n;
+
+ TSP_ALLOC(wc_Sha256, sha, 1, goto done);
+ TSP_ALLOC(byte, buf, WC_TSP_HASH_CHUNK_SZ, goto done);
+
+ f = fopen(name, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open %s\n", name);
+ goto done;
+ }
+ if (wc_InitSha256(sha) == 0) {
+ ret = 0;
+ while ((n = fread(buf, 1, WC_TSP_HASH_CHUNK_SZ, f)) > 0) {
+ if (wc_Sha256Update(sha, buf, (word32)n) != 0) {
+ ret = -1;
+ break;
+ }
+ }
+ if ((ret == 0) && (ferror(f) != 0))
+ ret = -1;
+ if (ret == 0)
+ ret = (wc_Sha256Final(sha, hash) == 0) ? 0 : -1;
+ wc_Sha256Free(sha);
+ }
+
+done:
+ if (f != NULL)
+ fclose(f);
+ TSP_FREE(sha);
+ TSP_FREE(buf);
+ return ret;
+}
+
+/* Check a response was granted - print the status when not. */
+static int tsp_check_granted(const TspResponse* resp)
+{
+ word32 status = 0;
+ const byte* statusText = NULL;
+ word32 statusTextSz = 0;
+ word32 failInfo = 0;
+
+ wc_TspResponse_GetStatus(resp, &status, &statusText, &statusTextSz,
+ &failInfo);
+ if ((status == WC_TSP_PKISTATUS_GRANTED) ||
+ (status == WC_TSP_PKISTATUS_GRANTED_WITH_MODS)) {
+ return 0;
+ }
+
+ fprintf(stderr, "time-stamp not granted: status %lu\n",
+ (unsigned long)status);
+ if (statusText != NULL) {
+ fprintf(stderr, " status text: %.*s\n", (int)statusTextSz,
+ statusText);
+ }
+ if (failInfo != 0) {
+ fprintf(stderr, " failure information: 0x%08lx\n",
+ (unsigned long)failInfo);
+ }
+ return 1;
+}
+
+/* Verify a response against the data and the request sent. */
+static int tsp_verify(const char* dataFile, const char* reqFile,
+ const char* respFile, const char* certFile)
+{
+ int ret = 1;
+ int r;
+ TSP_DECL(TspRequest, req, 1);
+ TspResponse resp;
+ TSP_DECL(TspTstInfo, tst, 1);
+ TSP_DECL(byte, reqDer, WC_TSP_MAX_FILE_SZ);
+ word32 reqDerSz = 0;
+ TSP_DECL(byte, respDer, WC_TSP_MAX_FILE_SZ);
+ word32 respDerSz = 0;
+ TSP_DECL(byte, cert, WC_TSP_MAX_FILE_SZ);
+ word32 certSz = 0;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ enum wc_HashType hashType = WC_HASH_TYPE_NONE;
+
+ TSP_ALLOC(TspRequest, req, 1, goto done);
+ TSP_ALLOC(TspTstInfo, tst, 1, goto done);
+ TSP_ALLOC(byte, reqDer, WC_TSP_MAX_FILE_SZ, goto done);
+ TSP_ALLOC(byte, respDer, WC_TSP_MAX_FILE_SZ, goto done);
+ TSP_ALLOC(byte, cert, WC_TSP_MAX_FILE_SZ, goto done);
+
+ /* Load the request, response and trusted TSA certificate. */
+ if ((tsp_read_file(reqFile, reqDer, WC_TSP_MAX_FILE_SZ, &reqDerSz) != 0) ||
+ (tsp_read_file(respFile, respDer, WC_TSP_MAX_FILE_SZ,
+ &respDerSz) != 0) ||
+ (tsp_read_file(certFile, cert, WC_TSP_MAX_FILE_SZ, &certSz) != 0)) {
+ goto done;
+ }
+
+ /* Check the request was for this data - hash the data file again. */
+ r = wc_TspRequest_Decode(req, reqDer, reqDerSz);
+ if (r != 0) {
+ fprintf(stderr, "decode request failed: %s\n", wc_GetErrorString(r));
+ goto done;
+ }
+ /* This example only hashes data with SHA-256 - the request must match. */
+ r = wc_TspRequest_GetHashType(req, &hashType);
+ if (r == 0)
+ r = tsp_hash_file(dataFile, hash);
+ if ((r != 0) || (hashType != WC_HASH_TYPE_SHA256) ||
+ (req->imprint.hashSz != (word32)sizeof(hash)) ||
+ (memcmp(req->imprint.hash, hash, sizeof(hash)) != 0)) {
+ fprintf(stderr, "request is not for this data\n");
+ goto done;
+ }
+
+ /* The time-stamp must have been granted. */
+ r = wc_TspResponse_Decode(&resp, respDer, respDerSz);
+ if (r != 0) {
+ fprintf(stderr, "decode response failed: %s\n", wc_GetErrorString(r));
+ goto done;
+ }
+ if (tsp_check_granted(&resp) != 0) {
+ goto done;
+ }
+
+ /* Verify the token against the trusted TSA certificate - the signer must
+ * be that certificate. The certificate is also used to verify tokens that
+ * do not include it. */
+ r = wc_TspResponse_Verify(&resp, cert, certSz, tst);
+ if (r != 0) {
+ fprintf(stderr, "token verification failed: %s\n",
+ wc_GetErrorString(r));
+ goto done;
+ }
+
+ /* Check the TSTInfo is for the request - imprint, nonce and policy. */
+ r = wc_TspTstInfo_CheckRequest(tst, req);
+ if (r != 0) {
+ fprintf(stderr, "token does not match request: %s\n",
+ wc_GetErrorString(r));
+ goto done;
+ }
+
+ /* tst references the response's token - use before respDer is freed. */
+ printf("Verification: OK\n");
+ printf(" Time: %.*s\n", (int)tst->genTimeSz, tst->genTime);
+ ret = 0;
+
+done:
+ TSP_FREE(req);
+ TSP_FREE(tst);
+ TSP_FREE(reqDer);
+ TSP_FREE(respDer);
+ TSP_FREE(cert);
+ return ret;
+}
+
+int main(int argc, char* argv[])
+{
+ if (argc != 5) {
+ fprintf(stderr, "usage: %s "
+ "\n", argv[0]);
+ return 1;
+ }
+ return tsp_verify(argv[1], argv[2], argv[3], argv[4]);
+}
+
+#else
+
+int main(void)
+{
+#ifdef NO_FILESYSTEM
+ fprintf(stderr, "NO_FILESYSTEM is defined\n");
+#else
+ fprintf(stderr, "Build wolfSSL with ./configure --enable-tsp\n");
+#endif
+ return 1;
+}
+
+#endif
diff --git a/gencertbuf.pl b/gencertbuf.pl
index a3573d6d480..47a7f52e45f 100755
--- a/gencertbuf.pl
+++ b/gencertbuf.pl
@@ -29,6 +29,8 @@
[ "./certs/server-ecc-comp.der", "serv_ecc_comp_der_256" ],
[ "./certs/server-ecc-rsa.der", "serv_ecc_rsa_der_256" ],
[ "./certs/server-ecc.der", "serv_ecc_der_256" ],
+ [ "./certs/tsa-ecc-key.der", "tsa_ecc_key_der_256" ],
+ [ "./certs/tsa-ecc-cert.der", "tsa_ecc_cert_der_256" ],
[ "./certs/ca-ecc-key.der", "ca_ecc_key_der_256" ],
[ "./certs/ca-ecc-cert.der", "ca_ecc_cert_der_256" ],
[ "./certs/ca-ecc384-key.der", "ca_ecc_key_der_384" ],
@@ -87,7 +89,11 @@
[ "./certs/ca-cert.der", "ca_cert_der_2048" ],
[ "./certs/ca-cert-chain.der", "ca_cert_chain_der" ],
[ "./certs/server-key.der", "server_key_der_2048" ],
- [ "./certs/server-cert.der", "server_cert_der_2048" ]
+ [ "./certs/server-cert.der", "server_cert_der_2048" ],
+ [ "./certs/tsa-key.der", "tsa_key_der_2048" ],
+ [ "./certs/tsa-cert.der", "tsa_cert_der_2048" ],
+ [ "./certs/tsa-bad-ku-cert.der", "tsa_bad_ku_cert_der_2048" ],
+ [ "./certs/tsa-extra-eku-cert.der", "tsa_extra_eku_cert_der_2048" ]
);
# 3072-bit certs/keys to be converted
diff --git a/scripts/asn1_oid_sum.pl b/scripts/asn1_oid_sum.pl
index 8df6545f022..04f79235db5 100755
--- a/scripts/asn1_oid_sum.pl
+++ b/scripts/asn1_oid_sum.pl
@@ -1209,6 +1209,7 @@ sub print_footer {
my @p7t_compressed_data = ( 1, 2, 840, 113549, 1, 9, 16, 1, 9 );
my @p7t_firmware_pkg_data = ( 1, 2, 840, 113549, 1, 9, 16, 1, 16 );
my @p7t_auth_env_data = ( 1, 2, 840, 113549, 1, 9, 16, 1, 23 );
+my @p7t_tstinfo_data = ( 1, 2, 840, 113549, 1, 9, 16, 1, 4 );
my @p7t_encrypted_key_package = ( 2, 16, 840, 1, 101, 2, 1, 2, 78, 2 );
my @pkcs7_types = (
@@ -1222,6 +1223,7 @@ sub print_footer {
{ name => "ENCRYPTED_DATA", oid => \@p7t_encrypted_data },
{ name => "FIRMWARE_PKG_DATA", oid => \@p7t_firmware_pkg_data },
{ name => "AUTH_ENVELOPED_DATA", oid => \@p7t_auth_env_data },
+ { name => "TSTINFO_DATA", oid => \@p7t_tstinfo_data },
{ name => "ENCRYPTED_KEY_PACKAGE", oid => \@p7t_encrypted_key_package },
);
diff --git a/scripts/include.am b/scripts/include.am
index 38dc071466d..f19dfc73529 100644
--- a/scripts/include.am
+++ b/scripts/include.am
@@ -124,6 +124,8 @@ EXTRA_DIST+= scripts/multi-msg-record.py
dist_noinst_SCRIPTS+= scripts/pem.test
+dist_noinst_SCRIPTS+= scripts/tsp.test
+
EXTRA_DIST += scripts/sniffer-static-rsa.pcap \
scripts/sniffer-ipv6.pcap \
scripts/sniffer-tls13-dh.pcap \
diff --git a/scripts/tsp.test b/scripts/tsp.test
new file mode 100755
index 00000000000..2f4c95d28d9
--- /dev/null
+++ b/scripts/tsp.test
@@ -0,0 +1,130 @@
+#!/usr/bin/env bash
+
+# tsp.test
+# Time-Stamp Protocol (RFC 3161) tests using the ts example tools, with
+# OpenSSL interoperability when an openssl binary is available.
+
+TSP_QUERY=./examples/tsp/tsp_query
+TSP_REPLY=./examples/tsp/tsp_reply
+TSP_VERIFY=./examples/tsp/tsp_verify
+RESULT=0
+
+# Check the examples were built and are usable - can't test without them.
+# A feature-disabled stub prints a build hint rather than a usage line.
+if [ ! -x "$TSP_QUERY" ] || [ ! -x "$TSP_REPLY" ] || [ ! -x "$TSP_VERIFY" ]; then
+ echo "ts examples not found -- skipping tsp.test."
+ exit 77
+fi
+# The round trip exercises every role, so all three tools must be usable. A
+# single-role build (e.g. --enable-tsp with only the responder) leaves the
+# others as feature-disabled stubs - skip rather than fail in that case.
+for tool in "$TSP_QUERY" "$TSP_REPLY" "$TSP_VERIFY"; do
+ if ! "$tool" 2>&1 | grep -q "usage:"; then
+ echo "ts examples not usable (need requester, responder and verifier"\
+ "roles) -- skipping tsp.test."
+ exit 77
+ fi
+done
+
+SRC_DIR="$(dirname "$0")/.."
+CERTS_DIR="${SRC_DIR}/certs"
+if [ ! -f "${CERTS_DIR}/tsa-cert.der" ]; then
+ echo "TSA certificate not found at ${CERTS_DIR} -- skipping tsp.test."
+ exit 77
+fi
+
+WORK_DIR=./tsp_test.$$
+mkdir "$WORK_DIR" || exit 1
+
+cleanup() {
+ rm -rf "$WORK_DIR"
+}
+trap cleanup EXIT
+
+fail() {
+ echo "FAIL: $1"
+ RESULT=1
+}
+
+pass() {
+ echo "PASS: $1"
+}
+
+DATA="$WORK_DIR/data.txt"
+REQ="$WORK_DIR/request.tsq"
+RESP="$WORK_DIR/response.tsr"
+echo "data to be time-stamped" > "$DATA"
+
+# Choose a TSA credential this build supports: RSA when available,
+# otherwise the ECC credential (e.g. a --disable-rsa build).
+CERT="${CERTS_DIR}/tsa-cert.der"
+KEY="${CERTS_DIR}/tsa-key.der"
+CAFILE="${CERTS_DIR}/tsa-cert.pem"
+KEYTYPE=
+"$TSP_QUERY" "$DATA" "$REQ" >/dev/null 2>&1
+if ! "$TSP_REPLY" "$REQ" "$CERT" "$KEY" "$RESP" >/dev/null 2>&1; then
+ CERT="${CERTS_DIR}/tsa-ecc-cert.der"
+ KEY="${CERTS_DIR}/tsa-ecc-key.der"
+ CAFILE="${CERTS_DIR}/tsa-ecc-cert.pem"
+ KEYTYPE=ecc
+fi
+
+# wolfSSL round trip: query -> reply -> verify.
+"$TSP_QUERY" "$DATA" "$REQ" >/dev/null &&
+"$TSP_REPLY" "$REQ" "$CERT" "$KEY" "$RESP" $KEYTYPE >/dev/null &&
+"$TSP_VERIFY" "$DATA" "$REQ" "$RESP" "$CERT" \
+ >/dev/null
+if [ $? -eq 0 ]; then
+ pass "wolfSSL round trip"
+else
+ fail "wolfSSL round trip"
+fi
+
+# Tampered data must not verify.
+echo "tampered" >> "$DATA"
+if "$TSP_VERIFY" "$DATA" "$REQ" "$RESP" "$CERT" \
+ >/dev/null 2>&1; then
+ fail "tampered data detected"
+else
+ pass "tampered data detected"
+fi
+echo "data to be time-stamped" > "$DATA"
+
+# A request that does not parse gets a rejection response.
+head -c 20 "$REQ" > "$WORK_DIR/bad.tsq"
+"$TSP_REPLY" "$WORK_DIR/bad.tsq" "$CERT" \
+ "$KEY" "$WORK_DIR/reject.tsr" $KEYTYPE >/dev/null
+if [ $? -eq 0 ] && [ -f "$WORK_DIR/reject.tsr" ]; then
+ pass "rejection response written"
+else
+ fail "rejection response written"
+fi
+
+# OpenSSL interoperability - skipped without an openssl supporting ts.
+if openssl ts -query -data "$DATA" -sha256 -cert \
+ -out "$WORK_DIR/ossl.tsq" >/dev/null 2>&1; then
+ # OpenSSL verifies a wolfSSL response.
+ if openssl ts -verify -queryfile "$REQ" -in "$RESP" \
+ -CAfile "$CAFILE" >/dev/null 2>&1; then
+ pass "OpenSSL verifies wolfSSL response"
+ else
+ fail "OpenSSL verifies wolfSSL response"
+ fi
+
+ # wolfSSL answers an OpenSSL request and OpenSSL verifies it.
+ "$TSP_REPLY" "$WORK_DIR/ossl.tsq" "$CERT" \
+ "$KEY" "$WORK_DIR/ossl.tsr" $KEYTYPE >/dev/null &&
+ "$TSP_VERIFY" "$DATA" "$WORK_DIR/ossl.tsq" "$WORK_DIR/ossl.tsr" \
+ "$CERT" >/dev/null &&
+ openssl ts -verify -data "$DATA" -in "$WORK_DIR/ossl.tsr" \
+ -CAfile "$CAFILE" >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ pass "wolfSSL answers OpenSSL request"
+ else
+ fail "wolfSSL answers OpenSSL request"
+ fi
+else
+ echo "openssl ts not usable -- skipping interoperability tests."
+fi
+
+exit $RESULT
diff --git a/src/include.am b/src/include.am
index ad8060f1f58..0194e847454 100644
--- a/src/include.am
+++ b/src/include.am
@@ -25,6 +25,7 @@ EXTRA_DIST += src/ssl_api_dtls.c
EXTRA_DIST += src/ssl_api_ext.c
EXTRA_DIST += src/ssl_api_pk.c
EXTRA_DIST += src/ssl_asn1.c
+EXTRA_DIST += src/ssl_tsp.c
EXTRA_DIST += src/ssl_bn.c
EXTRA_DIST += src/ssl_certman.c
EXTRA_DIST += src/ssl_crypto.c
@@ -2024,6 +2025,10 @@ if BUILD_PKCS7
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/pkcs7.c
endif
+if BUILD_TSP
+src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/tsp.c
+endif
+
if BUILD_SRP
src_libwolfssl@LIBSUFFIX@_la_SOURCES += wolfcrypt/src/srp.c
endif
diff --git a/src/ssl.c b/src/ssl.c
index c2a5827c9dd..51c25829da4 100644
--- a/src/ssl.c
+++ b/src/ssl.c
@@ -455,6 +455,9 @@ WC_RNG* wolfssl_make_rng(WC_RNG* rng, int* local)
#include "src/ssl_asn1.c"
#endif /* OPENSSL_EXTRA_NO_ASN1 */
+#define WOLFSSL_SSL_TSP_INCLUDED
+#include "src/ssl_tsp.c"
+
#define WOLFSSL_PK_INCLUDED
#include "src/pk.c"
diff --git a/src/ssl_asn1.c b/src/ssl_asn1.c
index 2b7c25f01b5..18013c46bb5 100644
--- a/src/ssl_asn1.c
+++ b/src/ssl_asn1.c
@@ -758,6 +758,44 @@ void wolfSSL_ASN1_BIT_STRING_free(WOLFSSL_ASN1_BIT_STRING* bitStr)
XFREE(bitStr, NULL, DYNAMIC_TYPE_OPENSSL);
}
+/* Copy data into an ASN.1 BIT_STRING object.
+ *
+ * Any existing data is disposed of and a copy of the supplied data is made.
+ *
+ * @param [in, out] bitStr ASN.1 BIT_STRING object.
+ * @param [in] data Data to copy in. May be NULL when len is 0.
+ * @param [in] len Length of data in bytes.
+ * @return 1 on success.
+ * @return 0 when bitStr is NULL, len is negative, data is NULL with a
+ * positive length, or dynamic memory allocation fails.
+ */
+int wolfSSL_ASN1_BIT_STRING_set1(WOLFSSL_ASN1_BIT_STRING* bitStr,
+ const unsigned char* data, int len)
+{
+ byte* tmp = NULL;
+
+ /* Validate parameters. */
+ if ((bitStr == NULL) || (len < 0) || ((data == NULL) && (len > 0))) {
+ return 0;
+ }
+
+ /* Make a copy of the data when there is any. */
+ if (len > 0) {
+ tmp = (byte*)XMALLOC((size_t)len, NULL, DYNAMIC_TYPE_OPENSSL);
+ if (tmp == NULL) {
+ return 0;
+ }
+ XMEMCPY(tmp, data, (size_t)len);
+ }
+
+ /* Dispose of any old data and store the copy. */
+ XFREE(bitStr->data, NULL, DYNAMIC_TYPE_OPENSSL);
+ bitStr->data = tmp;
+ bitStr->length = len;
+
+ return 1;
+}
+
/* Get the value of the bit from the ASN.1 BIT_STRING at specified index.
*
* A NULL object a value of 0 for the bit at all indices.
@@ -1134,6 +1172,50 @@ static int wolfssl_asn1_integer_require_len(WOLFSSL_ASN1_INTEGER* a, int len,
return ret;
}
+#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ defined(WOLFSSL_TSP_VERIFIER)
+/* Create an ASN.1 INTEGER object holding a big-endian number in DER form.
+ *
+ * The data is the ASN.1 type and length followed by the number as supplied.
+ *
+ * @param [in] val Big-endian encoding of number.
+ * @param [in] len Length of number in bytes.
+ * @return ASN.1 INTEGER object on success.
+ * @return NULL on failure.
+ */
+static WOLFSSL_ASN1_INTEGER* wolfssl_asn1_integer_new_buf(
+ const unsigned char* val, word32 len)
+{
+ WOLFSSL_ASN1_INTEGER* a;
+ /* Pad with a leading 0x00 when the top bit is set so the DER INTEGER stays
+ * positive - the wc layer supplies the magnitude without this pad. */
+ word32 pad = ((len > 0) && (val[0] & 0x80)) ? 1 : 0;
+ word32 hdrSz = 1 + SetLength(len + pad, NULL);
+ word32 i = 0;
+
+ a = wolfSSL_ASN1_INTEGER_new();
+ if (a == NULL)
+ return NULL;
+
+ /* Make sure there is space for the data, pad, ASN.1 type and length. */
+ if (wolfssl_asn1_integer_require_len(a, (int)(len + pad + hdrSz), 0) != 1) {
+ wolfSSL_ASN1_INTEGER_free(a);
+ return NULL;
+ }
+
+ a->data[i++] = ASN_INTEGER;
+ i += SetLength(len + pad, a->data + i);
+ if (pad == 1) {
+ a->data[i++] = 0x00;
+ }
+ XMEMCPY(a->data + i, val, len);
+ a->length = (int)(len + i);
+ a->type = WOLFSSL_V_ASN1_INTEGER;
+
+ return a;
+}
+#endif /* OPENSSL_EXTRA && WOLFSSL_TSP && HAVE_PKCS7 && WOLFSSL_TSP_VERIFIER */
+
/* Duplicate the ASN.1 INTEGER object into a newly allocated one.
*
* @param [in] src ASN.1 INTEGER object to copy.
diff --git a/src/ssl_tsp.c b/src/ssl_tsp.c
new file mode 100644
index 00000000000..a0bba68e58d
--- /dev/null
+++ b/src/ssl_tsp.c
@@ -0,0 +1,2414 @@
+/* ssl_tsp.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+#include
+
+#include
+
+#if !defined(WOLFSSL_SSL_TSP_INCLUDED)
+ #ifndef WOLFSSL_IGNORE_FILE_WARN
+ #warning ssl_tsp.c does not need to be compiled separately from ssl.c
+ #endif
+#else
+
+#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ defined(WOLFSSL_TSP_VERIFIER)
+
+/* The TSP OpenSSL compat layer uses the OpenSSL ASN.1 helpers (e.g.
+ * wolfssl_asn1_integer_new_buf) from ssl_asn1.c, which is omitted when
+ * OPENSSL_EXTRA_NO_ASN1 is defined. */
+#ifdef OPENSSL_EXTRA_NO_ASN1
+#error "TSP OpenSSL compat layer requires the OpenSSL ASN.1 API \
+(OPENSSL_EXTRA_NO_ASN1 not supported with WOLFSSL_TSP)"
+#endif
+
+#include
+#include
+#include
+
+/* Time-Stamp Protocol (RFC 3161) compatibility layer.
+ *
+ * Requester side only: create and encode requests, decode responses and
+ * verify time-stamp tokens. Implemented with the wc_Tsp API.
+ *
+ * The wolfCrypt Tsp data structure (TspRequest, TspTstInfo, TspResponse) is
+ * embedded in the compatibility object and is the single source of truth.
+ * Loading and storing - including d2i and i2d - go through the wc_Tsp API
+ * and operate on the embedded structure directly.
+ *
+ * OpenSSL-style sub-objects (ASN1_INTEGER, ASN1_OBJECT, ...) are not stored.
+ * A getter that returns one creates it from the wc data on first use, caches
+ * it on the parent and frees it when the parent is freed - the returned
+ * pointer is valid for the life of the parent and must not be freed by the
+ * caller. A setter copies the supplied object's value into the wc data and
+ * discards any cached view so it is rebuilt on the next get.
+ */
+
+/* MessageImprint - hash algorithm and hash of the data being time-stamped.
+ * RFC 3161, 2.4.1. A field of TS_REQ and TS_TST_INFO, and a standalone object
+ * (TS_MSG_IMPRINT_new). */
+struct WOLFSSL_TS_MSG_IMPRINT {
+ /* Source of truth. Points to miStore when standalone, or to the imprint
+ * embedded in the parent (TS_REQ/TS_TST_INFO) when a field of one. */
+ TspMessageImprint* mi;
+ /* Imprint storage used only when the object is standalone. */
+ TspMessageImprint miStore;
+ /* Hash algorithm view (X509_ALGOR) - owned, built from mi on first get. */
+ WOLFSSL_X509_ALGOR* algo;
+ /* Hashed message view (OCTET STRING) - owned, built from mi on first
+ * get; its data references mi's hash. */
+ WOLFSSL_ASN1_STRING* msg;
+};
+
+/* TimeStampReq - the request sent to a TSA. RFC 3161, 2.4.1.
+ * Writable: built with the setters then encoded, or decoded from DER. */
+struct WOLFSSL_TS_REQ {
+ /* Source of truth - fully owns its data (no references into a buffer). */
+ TspRequest req;
+ /* Message imprint view - owned, built from req.imprint on first get. */
+ WOLFSSL_TS_MSG_IMPRINT* msgImprint;
+ /* Policy OID view - owned, built from req.policy on first get. */
+ WOLFSSL_ASN1_OBJECT* policy;
+ /* Nonce view (INTEGER) - owned, built from req.nonce on first get. */
+ WOLFSSL_ASN1_INTEGER* nonce;
+};
+
+/* Accuracy - bound on the error of the time in a TSTInfo. RFC 3161, 2.4.2.
+ * A read-only view returned by TS_TST_INFO_get_accuracy(). */
+struct WOLFSSL_TS_ACCURACY {
+ /* References the accuracy embedded in the parent TS_TST_INFO. */
+ const TspAccuracy* acc;
+ /* Part views (INTEGER) - owned, built from acc on first get. A part that
+ * is zero is treated as absent and its getter returns NULL. */
+ WOLFSSL_ASN1_INTEGER* seconds;
+ WOLFSSL_ASN1_INTEGER* millis;
+ WOLFSSL_ASN1_INTEGER* micros;
+};
+
+/* PKIStatusInfo - status of a time-stamp response. RFC 3161, 2.4.2.
+ * A read-only view returned by TS_RESP_get_status_info(). */
+struct WOLFSSL_TS_STATUS_INFO {
+ /* References the response embedded in the parent TS_RESP. */
+ const TspResponse* resp;
+ /* PKIStatus view (INTEGER) - owned, built from resp on first get. */
+ WOLFSSL_ASN1_INTEGER* status;
+ /* PKIFailureInfo view (BIT STRING) - owned, built on first get; NULL when
+ * no failure information is present. */
+ WOLFSSL_ASN1_BIT_STRING* failInfo;
+ /* PKIFreeText view - a stack holding only the first UTF8String (all the
+ * wc layer exposes). Owned; textStr references the string in resp's der. */
+ WOLFSSL_STACK* text;
+ WOLFSSL_ASN1_STRING* textStr;
+};
+
+/* TSTInfo - the time-stamp token content signed by the TSA. RFC 3161, 2.4.2.
+ * Read-only: decoded from DER (or extracted from a token); i2d re-emits der. */
+struct WOLFSSL_TS_TST_INFO {
+ /* The DER encoding - owned. The wc TSTInfo references into it. */
+ unsigned char* der;
+ word32 derSz;
+ /* Source of truth - decoded from der and references into it. */
+ TspTstInfo tst;
+ /* Policy OID view - owned, built from tst.policy on first get. */
+ WOLFSSL_ASN1_OBJECT* policy;
+ /* Message imprint view - owned, built from tst.imprint on first get. */
+ WOLFSSL_TS_MSG_IMPRINT* msgImprint;
+ /* Serial number view (INTEGER) - owned, built from tst on first get. */
+ WOLFSSL_ASN1_INTEGER* serial;
+ /* Time view (GeneralizedTime) - owned, built from tst on first get. */
+ WOLFSSL_ASN1_TIME* time;
+ /* Accuracy view - owned, built on first get; NULL when not present. */
+ WOLFSSL_TS_ACCURACY* accuracy;
+ /* Nonce view (INTEGER) - owned, built on first get; NULL when absent. */
+ WOLFSSL_ASN1_INTEGER* nonce;
+ /* TSA name view (GeneralName) - owned, built on first get; NULL when
+ * absent. */
+ WOLFSSL_GENERAL_NAME* tsa;
+};
+
+/* TimeStampResp - the response returned by a TSA. RFC 3161, 2.4.2.
+ * Read-only: decoded from DER; i2d re-emits der. */
+struct WOLFSSL_TS_RESP {
+ /* The DER encoding - owned. The wc response references into it. */
+ unsigned char* der;
+ word32 derSz;
+ /* Source of truth - decoded from der and references into it. */
+ TspResponse resp;
+ /* Status info view - owned, built on first get. */
+ WOLFSSL_TS_STATUS_INFO* statusInfo;
+ /* TSTInfo of the token - owned, created on first get by verifying the
+ * token and decoding its content; NULL when there is no valid token. */
+ WOLFSSL_TS_TST_INFO* tstInfo;
+};
+
+/* Verification context - expected values and checks for verifying a response
+ * or token. The OpenSSL TS_VERIFY_CTX; has no wc counterpart. */
+struct WOLFSSL_TS_VERIFY_CTX {
+ /* TS_VFY_* flags selecting which checks to perform. */
+ unsigned int flags;
+ /* Trusted certificate store for the signer check - owned. May be NULL. */
+ WOLFSSL_X509_STORE* store;
+ /* Expected message imprint hash - owned. */
+ unsigned char* imprint;
+ word32 imprintSz;
+ /* Expected nonce as the wc value bytes. Absent when nonceSz is 0. */
+ unsigned char nonce[MAX_TS_NONCE_SZ];
+ word32 nonceSz;
+ /* Expected policy as the OID content bytes. Absent when policySz is 0. */
+ unsigned char policy[MAX_OID_SZ];
+ word32 policySz;
+ /* Data to hash and check against the imprint for TS_VFY_DATA - owned,
+ * freed on cleanup like OpenSSL. NULL when not set. */
+ WOLFSSL_BIO* data;
+};
+
+/******************************************************************************
+ * Field helpers
+ *****************************************************************************/
+
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_RESPONDER)
+/* Get the number data and length out of an ASN1_INTEGER.
+ *
+ * Used for the unsigned big-endian numbers of TSP: a request's nonce
+ * (requester) and a response's serial number (responder).
+ *
+ * @param [in] a ASN1_INTEGER object.
+ * @param [out] data Big-endian encoding of number.
+ * @param [out] len Length of number in bytes.
+ * @return 1 on success.
+ * @return 0 on failure or when the number is negative.
+ */
+static int ts_asn1_integer_get(const WOLFSSL_ASN1_INTEGER* a,
+ const unsigned char** data, word32* len)
+{
+ word32 idx = 1;
+ int length = 0;
+
+ if ((a == NULL) || (a->length < 3) || (a->data[0] != ASN_INTEGER))
+ return 0;
+ /* The number is an unsigned big-endian value. The data holds the
+ * magnitude; the sign is carried separately - reject a negative value
+ * rather than use its magnitude as if positive. */
+ if (a->negative || (a->type == WOLFSSL_V_ASN1_NEG_INTEGER))
+ return 0;
+ if (GetLength(a->data, &idx, &length, (word32)a->length) < 0)
+ return 0;
+
+ *data = a->data + idx;
+ *len = (word32)length;
+ return 1;
+}
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_RESPONDER */
+
+/* Return the encoding in the i2d output parameter style.
+ *
+ * @param [in] der DER encoding.
+ * @param [in] derSz Length of encoding in bytes.
+ * @param [in, out] pp Output buffer pointer. May be NULL. When pointer
+ * to NULL, a buffer is allocated and not advanced.
+ * Otherwise written to and advanced.
+ * @return Length of encoding on success.
+ * @return -1 on failure.
+ */
+static int ts_i2d(const unsigned char* der, word32 derSz, unsigned char** pp)
+{
+ if (der == NULL)
+ return -1;
+ if (pp == NULL)
+ return (int)derSz;
+
+ if (*pp == NULL) {
+ *pp = (unsigned char*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_OPENSSL);
+ if (*pp == NULL)
+ return -1;
+ XMEMCPY(*pp, der, derSz);
+ }
+ else {
+ XMEMCPY(*pp, der, derSz);
+ *pp += derSz;
+ }
+ return (int)derSz;
+}
+
+/* Get the length of the complete ASN.1 item at the front of the buffer.
+ *
+ * @param [in] der Buffer holding DER encoding.
+ * @param [in] len Length of data in buffer in bytes.
+ * @return Length of ASN.1 item on success.
+ * @return 0 on failure.
+ */
+static word32 ts_msg_len(const unsigned char* der, long len)
+{
+ word32 idx = 0;
+ int length = 0;
+
+ if ((der == NULL) || (len <= 0))
+ return 0;
+ /* Outer item is a SEQUENCE - return the total length of the item. */
+ if (GetSequence(der, &idx, &length, (word32)len) < 0)
+ return 0;
+ return idx + (word32)length;
+}
+
+/* Create an ASN1_INTEGER holding a small number.
+ *
+ * @param [in] v Value of number.
+ * @return ASN1_INTEGER object on success.
+ * @return NULL on failure.
+ */
+static WOLFSSL_ASN1_INTEGER* ts_asn1_integer_new_num(long v)
+{
+ WOLFSSL_ASN1_INTEGER* a = wolfSSL_ASN1_INTEGER_new();
+ if ((a != NULL) && (wolfSSL_ASN1_INTEGER_set(a, v) != 1)) {
+ wolfSSL_ASN1_INTEGER_free(a);
+ a = NULL;
+ }
+ return a;
+}
+
+/******************************************************************************
+ * TS_MSG_IMPRINT
+ *****************************************************************************/
+
+/* Create a message imprint object that views an imprint.
+ *
+ * @param [in] mi Imprint to view. When NULL the object's own storage is
+ * used.
+ * @return Message imprint object on success.
+ * @return NULL on failure.
+ */
+static WOLFSSL_TS_MSG_IMPRINT* ts_msg_imprint_new(TspMessageImprint* mi)
+{
+ WOLFSSL_TS_MSG_IMPRINT* a;
+
+ a = (WOLFSSL_TS_MSG_IMPRINT*)XMALLOC(sizeof(WOLFSSL_TS_MSG_IMPRINT),
+ NULL, DYNAMIC_TYPE_OPENSSL);
+ if (a != NULL) {
+ XMEMSET(a, 0, sizeof(WOLFSSL_TS_MSG_IMPRINT));
+ a->mi = (mi != NULL) ? mi : &a->miStore;
+ }
+
+ return a;
+}
+
+/* Create a new message imprint.
+ *
+ * @return Message imprint object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_MSG_IMPRINT* wolfSSL_TS_MSG_IMPRINT_new(void)
+{
+ return ts_msg_imprint_new(NULL);
+}
+
+/* Dispose of a message imprint.
+ *
+ * @param [in, out] a Message imprint object. May be NULL.
+ */
+void wolfSSL_TS_MSG_IMPRINT_free(WOLFSSL_TS_MSG_IMPRINT* a)
+{
+ if (a != NULL) {
+ if (a->algo != NULL)
+ wolfSSL_X509_ALGOR_free(a->algo);
+ if (a->msg != NULL)
+ wolfSSL_ASN1_STRING_free(a->msg);
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Set the hash algorithm of a message imprint.
+ *
+ * The algorithm is copied as its OID sum.
+ *
+ * @param [in, out] a Message imprint object.
+ * @param [in] alg Hash algorithm.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_MSG_IMPRINT_set_algo(WOLFSSL_TS_MSG_IMPRINT* a,
+ WOLFSSL_X509_ALGOR* alg)
+{
+ int nid;
+ word32 oidSum;
+
+ if ((a == NULL) || (alg == NULL) || (alg->algorithm == NULL))
+ return 0;
+ /* No work when setting the algorithm onto itself. */
+ if (alg == a->algo)
+ return 1;
+
+ /* Convert the algorithm to a hash OID sum for the wc imprint.
+ * nid2oid returns (word32)-1 when the NID is not a known hash. */
+ nid = wolfSSL_OBJ_obj2nid(alg->algorithm);
+ oidSum = (word32)nid2oid(nid, oidHashType);
+ if ((oidSum == 0) || (oidSum == (word32)-1))
+ return 0;
+ a->mi->hashAlgOID = oidSum;
+
+ /* Discard any cached view so it is rebuilt from the wc imprint. */
+ if (a->algo != NULL) {
+ wolfSSL_X509_ALGOR_free(a->algo);
+ a->algo = NULL;
+ }
+
+ return 1;
+}
+
+/* Get the hash algorithm of a message imprint.
+ *
+ * @param [in] a Message imprint object.
+ * @return Hash algorithm of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+WOLFSSL_X509_ALGOR* wolfSSL_TS_MSG_IMPRINT_get_algo(WOLFSSL_TS_MSG_IMPRINT* a)
+{
+ WOLFSSL_X509_ALGOR* alg;
+
+ if (a == NULL)
+ return NULL;
+
+ if (a->algo != NULL)
+ return a->algo;
+
+ alg = wolfSSL_X509_ALGOR_new();
+ if (alg == NULL)
+ return NULL;
+ /* Algorithm OBJECT IDENTIFIER from the wc imprint's OID sum. */
+ alg->algorithm = wolfSSL_OBJ_nid2obj(oid2nid(a->mi->hashAlgOID,
+ oidHashType));
+ if (alg->algorithm == NULL) {
+ wolfSSL_X509_ALGOR_free(alg);
+ return NULL;
+ }
+ a->algo = alg;
+
+ return a->algo;
+}
+
+/* Set the hashed message of a message imprint.
+ *
+ * @param [in, out] a Message imprint object.
+ * @param [in] d Hash of message.
+ * @param [in] len Length of hash in bytes.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_MSG_IMPRINT_set_msg(WOLFSSL_TS_MSG_IMPRINT* a,
+ unsigned char* d, int len)
+{
+ if ((a == NULL) || (d == NULL) || (len <= 0) ||
+ (len > WC_TSP_MAX_HASH_SZ))
+ return 0;
+
+ XMEMCPY(a->mi->hash, d, (size_t)len);
+ a->mi->hashSz = (word32)len;
+
+ /* Discard any cached view so it is rebuilt from the wc imprint. */
+ if (a->msg != NULL) {
+ wolfSSL_ASN1_STRING_free(a->msg);
+ a->msg = NULL;
+ }
+
+ return 1;
+}
+
+/* Get the hashed message of a message imprint.
+ *
+ * @param [in] a Message imprint object.
+ * @return Hashed message of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+WOLFSSL_ASN1_STRING* wolfSSL_TS_MSG_IMPRINT_get_msg(WOLFSSL_TS_MSG_IMPRINT* a)
+{
+ if (a == NULL)
+ return NULL;
+
+ if (a->msg == NULL) {
+ WOLFSSL_ASN1_STRING* s = wolfSSL_ASN1_STRING_new();
+ if (s == NULL)
+ return NULL;
+ /* Reference the hash in the wc imprint. */
+ s->data = (char*)a->mi->hash;
+ s->length = (int)a->mi->hashSz;
+ s->type = WOLFSSL_V_ASN1_OCTET_STRING;
+ s->isDynamic = 0;
+ a->msg = s;
+ }
+
+ return a->msg;
+}
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/******************************************************************************
+ * TS_REQ
+ *****************************************************************************/
+
+/* Create a new time-stamp request.
+ *
+ * @return TS_REQ object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_REQ* wolfSSL_TS_REQ_new(void)
+{
+ WOLFSSL_TS_REQ* a;
+
+ a = (WOLFSSL_TS_REQ*)XMALLOC(sizeof(WOLFSSL_TS_REQ), NULL,
+ DYNAMIC_TYPE_OPENSSL);
+ if (a != NULL) {
+ XMEMSET(a, 0, sizeof(WOLFSSL_TS_REQ));
+ if (wc_TspRequest_Init(&a->req) != 0) {
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ a = NULL;
+ }
+ }
+
+ return a;
+}
+
+/* Dispose of a time-stamp request.
+ *
+ * @param [in, out] a TS_REQ object. May be NULL.
+ */
+void wolfSSL_TS_REQ_free(WOLFSSL_TS_REQ* a)
+{
+ if (a != NULL) {
+ wolfSSL_TS_MSG_IMPRINT_free(a->msgImprint);
+ wolfSSL_ASN1_OBJECT_free(a->policy);
+ wolfSSL_ASN1_INTEGER_free(a->nonce);
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Set the version of a time-stamp request - must be 1.
+ *
+ * @param [in, out] a TS_REQ object.
+ * @param [in] version Version of request.
+ * @return 1 on success.
+ * @return 0 when a is NULL.
+ */
+int wolfSSL_TS_REQ_set_version(WOLFSSL_TS_REQ* a, long version)
+{
+ if (a == NULL)
+ return 0;
+ a->req.version = (byte)version;
+ return 1;
+}
+
+/* Get the version of a time-stamp request.
+ *
+ * @param [in] a TS_REQ object.
+ * @return Version of request.
+ * @return 0 when a is NULL.
+ */
+long wolfSSL_TS_REQ_get_version(const WOLFSSL_TS_REQ* a)
+{
+ if (a == NULL)
+ return 0;
+ return (long)a->req.version;
+}
+
+/* Set the message imprint of a time-stamp request.
+ *
+ * The message imprint is copied.
+ *
+ * @param [in, out] a TS_REQ object.
+ * @param [in] msgImprint Message imprint object.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_REQ_set_msg_imprint(WOLFSSL_TS_REQ* a,
+ WOLFSSL_TS_MSG_IMPRINT* msgImprint)
+{
+ if ((a == NULL) || (msgImprint == NULL))
+ return 0;
+ /* No work when setting the message imprint onto itself. */
+ if (msgImprint == a->msgImprint)
+ return 1;
+
+ /* Copy the imprint into the wc request. */
+ XMEMCPY(&a->req.imprint, msgImprint->mi, sizeof(TspMessageImprint));
+
+ /* Discard any cached view so it is rebuilt from the wc request. */
+ wolfSSL_TS_MSG_IMPRINT_free(a->msgImprint);
+ a->msgImprint = NULL;
+
+ return 1;
+}
+
+/* Get the message imprint of a time-stamp request.
+ *
+ * @param [in] a TS_REQ object.
+ * @return Message imprint of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+WOLFSSL_TS_MSG_IMPRINT* wolfSSL_TS_REQ_get_msg_imprint(WOLFSSL_TS_REQ* a)
+{
+ if (a == NULL)
+ return NULL;
+ if (a->msgImprint == NULL)
+ a->msgImprint = ts_msg_imprint_new(&a->req.imprint);
+ return a->msgImprint;
+}
+
+/* Set the TSA policy of a time-stamp request.
+ *
+ * The policy is copied.
+ *
+ * @param [in, out] a TS_REQ object.
+ * @param [in] policy TSA policy ID.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_REQ_set_policy_id(WOLFSSL_TS_REQ* a,
+ const WOLFSSL_ASN1_OBJECT* policy)
+{
+ const unsigned char* oid;
+ word32 oidSz;
+ word32 idx = 1;
+ int len = 0;
+
+ if ((a == NULL) || (policy == NULL) || (policy->obj == NULL))
+ return 0;
+
+ /* The object's OID may be full DER (OBJECT IDENTIFIER tag, length then
+ * content) or bare content - the wc request holds the content. */
+ oid = policy->obj;
+ oidSz = policy->objSz;
+ if ((oidSz >= 2) && (oid[0] == ASN_OBJECT_ID) &&
+ (GetLength(oid, &idx, &len, oidSz) >= 0) &&
+ (idx + (word32)len == oidSz)) {
+ oid += idx;
+ oidSz = (word32)len;
+ }
+ if (oidSz > MAX_OID_SZ)
+ return 0;
+
+ XMEMCPY(a->req.policy, oid, oidSz);
+ a->req.policySz = oidSz;
+
+ /* Discard any cached view so it is rebuilt from the wc request. */
+ wolfSSL_ASN1_OBJECT_free(a->policy);
+ a->policy = NULL;
+
+ return 1;
+}
+
+/* Get the TSA policy of a time-stamp request.
+ *
+ * @param [in] a TS_REQ object.
+ * @return TSA policy ID of the object - do not free.
+ * @return NULL when a is NULL or no policy set.
+ */
+WOLFSSL_ASN1_OBJECT* wolfSSL_TS_REQ_get_policy_id(WOLFSSL_TS_REQ* a)
+{
+ if ((a == NULL) || (a->req.policySz == 0))
+ return NULL;
+ /* Build the object from the OID content in the wc request. */
+ if (a->policy == NULL) {
+ const unsigned char* p = a->req.policy;
+ a->policy = wolfSSL_c2i_ASN1_OBJECT(NULL, &p, (long)a->req.policySz);
+ }
+ return a->policy;
+}
+
+/* Set the nonce of a time-stamp request.
+ *
+ * The nonce is copied. It must be a non-negative INTEGER - a negative nonce
+ * is rejected.
+ *
+ * @param [in, out] a TS_REQ object.
+ * @param [in] nonce Nonce to send.
+ * @return 1 on success.
+ * @return 0 on failure or when nonce is negative.
+ */
+int wolfSSL_TS_REQ_set_nonce(WOLFSSL_TS_REQ* a,
+ const WOLFSSL_ASN1_INTEGER* nonce)
+{
+ const unsigned char* data = NULL;
+ word32 len = 0;
+
+ if ((a == NULL) || (nonce == NULL))
+ return 0;
+ /* Get the value bytes out of the ASN.1 INTEGER and store in the wc
+ * request - leading zero bytes are stripped by wc_TspRequest_SetNonce. */
+ if (ts_asn1_integer_get(nonce, &data, &len) != 1)
+ return 0;
+ if (wc_TspRequest_SetNonce(&a->req, data, len) != 0)
+ return 0;
+
+ /* Discard any cached view so it is rebuilt from the wc request. */
+ if (a->nonce != NULL) {
+ wolfSSL_ASN1_INTEGER_free(a->nonce);
+ a->nonce = NULL;
+ }
+
+ return 1;
+}
+
+/* Get the nonce of a time-stamp request.
+ *
+ * @param [in] a TS_REQ object.
+ * @return Nonce of the object - do not free.
+ * @return NULL when a is NULL or no nonce set.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_REQ_get_nonce(const WOLFSSL_TS_REQ* a)
+{
+ WOLFSSL_TS_REQ* req = (WOLFSSL_TS_REQ*)a;
+
+ if ((a == NULL) || (a->req.nonceSz == 0))
+ return NULL;
+ if (req->nonce == NULL)
+ req->nonce = wolfssl_asn1_integer_new_buf(a->req.nonce, a->req.nonceSz);
+ return req->nonce;
+}
+
+/* Set whether the TSA's certificate is requested in the response.
+ *
+ * @param [in, out] a TS_REQ object.
+ * @param [in] certReq Request certificate when non-zero.
+ * @return 1 on success.
+ * @return 0 when a is NULL.
+ */
+int wolfSSL_TS_REQ_set_cert_req(WOLFSSL_TS_REQ* a, int certReq)
+{
+ if (a == NULL)
+ return 0;
+ a->req.certReq = (byte)(certReq != 0);
+ return 1;
+}
+
+/* Get whether the TSA's certificate is requested in the response.
+ *
+ * @param [in] a TS_REQ object.
+ * @return 1 when certificate requested.
+ * @return 0 when not requested or a is NULL.
+ */
+int wolfSSL_TS_REQ_get_cert_req(const WOLFSSL_TS_REQ* a)
+{
+ if (a == NULL)
+ return 0;
+ return a->req.certReq;
+}
+
+/* Encode a time-stamp request.
+ *
+ * @param [in] a TS_REQ object.
+ * @param [in, out] pp Output buffer pointer. May be NULL. When pointer to
+ * NULL, a buffer is allocated and not advanced.
+ * Otherwise written to and advanced.
+ * @return Length of encoding on success.
+ * @return -1 on failure.
+ */
+int wolfSSL_i2d_TS_REQ(const WOLFSSL_TS_REQ* a, unsigned char** pp)
+{
+ int ret = -1;
+ unsigned char* der = NULL;
+ word32 derSz = 0;
+
+ WOLFSSL_ENTER("wolfSSL_i2d_TS_REQ");
+
+ if (a == NULL)
+ return -1;
+
+ /* Encode the wc request into a new buffer and return in i2d style. */
+ if (wc_TspRequest_Encode(&a->req, NULL, &derSz) != 0) {
+ return -1;
+ }
+ if (pp == NULL)
+ return (int)derSz;
+
+ der = *pp;
+ if (der == NULL) {
+ der = (unsigned char*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+ if (der == NULL)
+ return -1;
+ if (wc_TspRequest_Encode(&a->req, der, &derSz) == 0) {
+ ret = (int)derSz;
+ *pp = (*pp != der) ? der : der + derSz;
+ }
+ else if (der != *pp)
+ XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL);
+
+ return ret;
+}
+
+/* Decode a time-stamp request.
+ *
+ * @param [in, out] a TS_REQ object pointer. May be NULL. Any object
+ * pointed to is freed and replaced.
+ * @param [in, out] pp Pointer to DER encoding - advanced past the
+ * request.
+ * @param [in] length Length of data in buffer in bytes.
+ * @return TS_REQ object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_REQ* wolfSSL_d2i_TS_REQ(WOLFSSL_TS_REQ** a,
+ const unsigned char** pp, long length)
+{
+ WOLFSSL_TS_REQ* ret;
+ word32 sz;
+
+ WOLFSSL_ENTER("wolfSSL_d2i_TS_REQ");
+
+ if ((pp == NULL) || (*pp == NULL))
+ return NULL;
+ sz = ts_msg_len(*pp, length);
+ if (sz == 0)
+ return NULL;
+
+ ret = wolfSSL_TS_REQ_new();
+ if (ret == NULL)
+ return NULL;
+
+ /* Decode straight into the wc request. */
+ if (wc_TspRequest_Decode(&ret->req, *pp, sz) != 0) {
+ wolfSSL_TS_REQ_free(ret);
+ return NULL;
+ }
+
+ *pp += sz;
+ if (a != NULL) {
+ wolfSSL_TS_REQ_free(*a);
+ *a = ret;
+ }
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+/******************************************************************************
+ * TS_STATUS_INFO and TS_ACCURACY
+ *****************************************************************************/
+
+/* Get the PKIStatus of a status info.
+ *
+ * @param [in] a TS_STATUS_INFO object.
+ * @return Status of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_STATUS_INFO_get0_status(
+ const WOLFSSL_TS_STATUS_INFO* a)
+{
+ WOLFSSL_TS_STATUS_INFO* info = (WOLFSSL_TS_STATUS_INFO*)a;
+
+ if (a == NULL)
+ return NULL;
+ if (info->status == NULL)
+ info->status = ts_asn1_integer_new_num((long)a->resp->status);
+ return info->status;
+}
+
+/* Get the failure information of a status info.
+ *
+ * @param [in] a TS_STATUS_INFO object.
+ * @return Failure information of the object - do not free.
+ * @return NULL when a is NULL or no failure information.
+ */
+const WOLFSSL_ASN1_BIT_STRING* wolfSSL_TS_STATUS_INFO_get0_failure_info(
+ const WOLFSSL_TS_STATUS_INFO* a)
+{
+ WOLFSSL_TS_STATUS_INFO* info = (WOLFSSL_TS_STATUS_INFO*)a;
+
+ if ((a == NULL) || (a->resp->failInfo == 0))
+ return NULL;
+
+ if (info->failInfo == NULL) {
+ WOLFSSL_ASN1_BIT_STRING* bs = wolfSSL_ASN1_BIT_STRING_new();
+ unsigned char b[4];
+ word32 i;
+ int n = 0;
+
+ if (bs == NULL)
+ return NULL;
+ /* Big-endian bytes of number - trailing zero bytes not encoded. */
+ for (i = 0; i < 4; i++) {
+ b[i] = (unsigned char)(a->resp->failInfo >> (8 * (3 - i)));
+ if (b[i] != 0) {
+ n = (int)i + 1;
+ }
+ }
+ if (wolfSSL_ASN1_BIT_STRING_set1(bs, b, n) != 1) {
+ wolfSSL_ASN1_BIT_STRING_free(bs);
+ return NULL;
+ }
+ bs->type = ASN_BIT_STRING;
+ info->failInfo = bs;
+ }
+
+ return info->failInfo;
+}
+
+/* Get the status string (PKIFreeText) of a status info.
+ *
+ * Only the first UTF8String of the PKIFreeText is in the stack.
+ *
+ * @param [in] a TS_STATUS_INFO object.
+ * @return Stack of ASN1_UTF8STRINGs - do not free.
+ * @return NULL when a is NULL or no status string present.
+ */
+const WOLF_STACK_OF(WOLFSSL_ASN1_STRING)* wolfSSL_TS_STATUS_INFO_get0_text(
+ const WOLFSSL_TS_STATUS_INFO* a)
+{
+ WOLFSSL_TS_STATUS_INFO* info = (WOLFSSL_TS_STATUS_INFO*)a;
+
+ if ((a == NULL) || (a->resp->statusString == NULL))
+ return NULL;
+
+ if (info->text == NULL) {
+ WOLFSSL_ASN1_STRING* str;
+ WOLFSSL_STACK* sk;
+
+ str = wolfSSL_ASN1_STRING_new();
+ if (str == NULL)
+ return NULL;
+ /* Reference the first UTF8String in the response's der. */
+ str->type = WOLFSSL_V_ASN1_UTF8STRING;
+ str->data = (char*)a->resp->statusString;
+ str->length = (int)a->resp->statusStringSz;
+ str->isDynamic = 0;
+
+ sk = (WOLFSSL_STACK*)XMALLOC(sizeof(WOLFSSL_STACK), NULL,
+ DYNAMIC_TYPE_OPENSSL);
+ if (sk == NULL) {
+ wolfSSL_ASN1_STRING_free(str);
+ return NULL;
+ }
+ XMEMSET(sk, 0, sizeof(WOLFSSL_STACK));
+ sk->num = 1;
+ sk->type = STACK_TYPE_NULL;
+ sk->data.generic = str;
+
+ info->textStr = str;
+ info->text = sk;
+ }
+
+ return info->text;
+}
+
+/* Get the seconds of an accuracy.
+ *
+ * @param [in] a TS_ACCURACY object.
+ * @return Seconds of the object - do not free.
+ * @return NULL when a is NULL or seconds not present.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_ACCURACY_get_seconds(
+ const WOLFSSL_TS_ACCURACY* a)
+{
+ WOLFSSL_TS_ACCURACY* acc = (WOLFSSL_TS_ACCURACY*)a;
+
+ if ((a == NULL) || (a->acc->seconds == 0))
+ return NULL;
+ if (acc->seconds == NULL)
+ acc->seconds = ts_asn1_integer_new_num((long)a->acc->seconds);
+ return acc->seconds;
+}
+
+/* Get the milliseconds of an accuracy.
+ *
+ * @param [in] a TS_ACCURACY object.
+ * @return Milliseconds of the object - do not free.
+ * @return NULL when a is NULL or milliseconds not present.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_ACCURACY_get_millis(
+ const WOLFSSL_TS_ACCURACY* a)
+{
+ WOLFSSL_TS_ACCURACY* acc = (WOLFSSL_TS_ACCURACY*)a;
+
+ if ((a == NULL) || (a->acc->millis == 0))
+ return NULL;
+ if (acc->millis == NULL)
+ acc->millis = ts_asn1_integer_new_num((long)a->acc->millis);
+ return acc->millis;
+}
+
+/* Get the microseconds of an accuracy.
+ *
+ * @param [in] a TS_ACCURACY object.
+ * @return Microseconds of the object - do not free.
+ * @return NULL when a is NULL or microseconds not present.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_ACCURACY_get_micros(
+ const WOLFSSL_TS_ACCURACY* a)
+{
+ WOLFSSL_TS_ACCURACY* acc = (WOLFSSL_TS_ACCURACY*)a;
+
+ if ((a == NULL) || (a->acc->micros == 0))
+ return NULL;
+ if (acc->micros == NULL)
+ acc->micros = ts_asn1_integer_new_num((long)a->acc->micros);
+ return acc->micros;
+}
+
+/* Dispose of an accuracy view.
+ *
+ * @param [in, out] a TS_ACCURACY object. May be NULL.
+ */
+static void ts_accuracy_free(WOLFSSL_TS_ACCURACY* a)
+{
+ if (a != NULL) {
+ wolfSSL_ASN1_INTEGER_free(a->seconds);
+ wolfSSL_ASN1_INTEGER_free(a->millis);
+ wolfSSL_ASN1_INTEGER_free(a->micros);
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Dispose of a status info view.
+ *
+ * @param [in, out] a TS_STATUS_INFO object. May be NULL.
+ */
+static void ts_status_info_free(WOLFSSL_TS_STATUS_INFO* a)
+{
+ if (a != NULL) {
+ wolfSSL_ASN1_INTEGER_free(a->status);
+ wolfSSL_ASN1_BIT_STRING_free(a->failInfo);
+ wolfSSL_ASN1_STRING_free(a->textStr);
+ XFREE(a->text, NULL, DYNAMIC_TYPE_OPENSSL);
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/******************************************************************************
+ * TS_TST_INFO
+ *****************************************************************************/
+
+/* Dispose of a TSTInfo.
+ *
+ * @param [in, out] a TS_TST_INFO object. May be NULL.
+ */
+void wolfSSL_TS_TST_INFO_free(WOLFSSL_TS_TST_INFO* a)
+{
+ if (a != NULL) {
+ XFREE(a->der, NULL, DYNAMIC_TYPE_OPENSSL);
+ wolfSSL_ASN1_OBJECT_free(a->policy);
+ wolfSSL_TS_MSG_IMPRINT_free(a->msgImprint);
+ wolfSSL_ASN1_INTEGER_free(a->serial);
+ wolfSSL_ASN1_TIME_free(a->time);
+ ts_accuracy_free(a->accuracy);
+ wolfSSL_ASN1_INTEGER_free(a->nonce);
+ wolfSSL_GENERAL_NAME_free(a->tsa);
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Create a TS_TST_INFO from the DER encoding.
+ *
+ * @param [in] der DER encoding of TSTInfo.
+ * @param [in] sz Length of encoding in bytes.
+ * @return TS_TST_INFO object on success.
+ * @return NULL on failure.
+ */
+static WOLFSSL_TS_TST_INFO* ts_tst_info_from_der(const unsigned char* der,
+ word32 sz)
+{
+ WOLFSSL_TS_TST_INFO* ret;
+
+ ret = (WOLFSSL_TS_TST_INFO*)XMALLOC(sizeof(WOLFSSL_TS_TST_INFO), NULL,
+ DYNAMIC_TYPE_OPENSSL);
+ if (ret == NULL)
+ return NULL;
+ XMEMSET(ret, 0, sizeof(WOLFSSL_TS_TST_INFO));
+
+ /* Keep a copy of the encoding - the wc TSTInfo references into it. */
+ ret->der = (unsigned char*)XMALLOC(sz, NULL, DYNAMIC_TYPE_OPENSSL);
+ if (ret->der == NULL) {
+ XFREE(ret, NULL, DYNAMIC_TYPE_OPENSSL);
+ return NULL;
+ }
+ XMEMCPY(ret->der, der, sz);
+ ret->derSz = sz;
+
+ /* Decode straight into the wc TSTInfo. */
+ if (wc_TspTstInfo_Decode(&ret->tst, ret->der, sz) != 0) {
+ wolfSSL_TS_TST_INFO_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* Decode a TSTInfo.
+ *
+ * @param [in, out] a TS_TST_INFO object pointer. May be NULL. Any
+ * object pointed to is freed and replaced.
+ * @param [in, out] pp Pointer to DER encoding - advanced past the
+ * TSTInfo.
+ * @param [in] length Length of data in buffer in bytes.
+ * @return TS_TST_INFO object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_TST_INFO* wolfSSL_d2i_TS_TST_INFO(WOLFSSL_TS_TST_INFO** a,
+ const unsigned char** pp, long length)
+{
+ WOLFSSL_TS_TST_INFO* ret;
+ word32 sz;
+
+ WOLFSSL_ENTER("wolfSSL_d2i_TS_TST_INFO");
+
+ if ((pp == NULL) || (*pp == NULL))
+ return NULL;
+ sz = ts_msg_len(*pp, length);
+ if (sz == 0)
+ return NULL;
+
+ ret = ts_tst_info_from_der(*pp, sz);
+ if (ret == NULL)
+ return NULL;
+
+ *pp += sz;
+ if (a != NULL) {
+ wolfSSL_TS_TST_INFO_free(*a);
+ *a = ret;
+ }
+ return ret;
+}
+
+/* Encode a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @param [in, out] pp Output buffer pointer. May be NULL. When pointer to
+ * NULL, a buffer is allocated and not advanced.
+ * Otherwise written to and advanced.
+ * @return Length of encoding on success.
+ * @return -1 on failure.
+ */
+int wolfSSL_i2d_TS_TST_INFO(const WOLFSSL_TS_TST_INFO* a, unsigned char** pp)
+{
+ if (a == NULL)
+ return -1;
+ return ts_i2d(a->der, a->derSz, pp);
+}
+
+/* Get the version of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return Version of TSTInfo.
+ * @return 0 when a is NULL.
+ */
+long wolfSSL_TS_TST_INFO_get_version(const WOLFSSL_TS_TST_INFO* a)
+{
+ if (a == NULL)
+ return 0;
+ return (long)a->tst.version;
+}
+
+/* Get the TSA policy of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return TSA policy ID of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+WOLFSSL_ASN1_OBJECT* wolfSSL_TS_TST_INFO_get_policy_id(WOLFSSL_TS_TST_INFO* a)
+{
+ if (a == NULL)
+ return NULL;
+ /* Build the object from the OID content in the wc TSTInfo. */
+ if ((a->policy == NULL) && (a->tst.policy != NULL)) {
+ const unsigned char* p = a->tst.policy;
+ a->policy = wolfSSL_c2i_ASN1_OBJECT(NULL, &p, (long)a->tst.policySz);
+ }
+ return a->policy;
+}
+
+/* Get the message imprint of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return Message imprint of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+WOLFSSL_TS_MSG_IMPRINT* wolfSSL_TS_TST_INFO_get_msg_imprint(
+ WOLFSSL_TS_TST_INFO* a)
+{
+ if (a == NULL)
+ return NULL;
+ if (a->msgImprint == NULL)
+ a->msgImprint = ts_msg_imprint_new(&a->tst.imprint);
+ return a->msgImprint;
+}
+
+/* Get the serial number of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return Serial number of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_TST_INFO_get_serial(
+ const WOLFSSL_TS_TST_INFO* a)
+{
+ WOLFSSL_TS_TST_INFO* tst = (WOLFSSL_TS_TST_INFO*)a;
+
+ if (a == NULL)
+ return NULL;
+ if ((tst->serial == NULL) && (a->tst.serial != NULL)) {
+ tst->serial = wolfssl_asn1_integer_new_buf(a->tst.serial,
+ a->tst.serialSz);
+ }
+ return tst->serial;
+}
+
+/* Get the time of the time-stamp of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return Time of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+const WOLFSSL_ASN1_GENERALIZEDTIME* wolfSSL_TS_TST_INFO_get_time(
+ const WOLFSSL_TS_TST_INFO* a)
+{
+ WOLFSSL_TS_TST_INFO* tst = (WOLFSSL_TS_TST_INFO*)a;
+
+ if (a == NULL)
+ return NULL;
+ if ((tst->time == NULL) && (a->tst.genTime != NULL) &&
+ (a->tst.genTimeSz < (word32)CTC_DATE_SIZE)) {
+ WOLFSSL_ASN1_TIME* t = wolfSSL_ASN1_TIME_new();
+ if (t != NULL) {
+ t->type = WOLFSSL_V_ASN1_GENERALIZEDTIME;
+ t->length = (int)a->tst.genTimeSz;
+ XMEMCPY(t->data, a->tst.genTime, a->tst.genTimeSz);
+ }
+ tst->time = t;
+ }
+ return tst->time;
+}
+
+/* Get the accuracy of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return Accuracy of the object - do not free.
+ * @return NULL when a is NULL or accuracy not present.
+ */
+WOLFSSL_TS_ACCURACY* wolfSSL_TS_TST_INFO_get_accuracy(WOLFSSL_TS_TST_INFO* a)
+{
+ if (a == NULL)
+ return NULL;
+ /* Accuracy is absent when all parts are zero. */
+ if ((a->tst.accuracy.seconds == 0) && (a->tst.accuracy.millis == 0) &&
+ (a->tst.accuracy.micros == 0))
+ return NULL;
+
+ if (a->accuracy == NULL) {
+ WOLFSSL_TS_ACCURACY* acc = (WOLFSSL_TS_ACCURACY*)XMALLOC(
+ sizeof(WOLFSSL_TS_ACCURACY), NULL, DYNAMIC_TYPE_OPENSSL);
+ if (acc == NULL)
+ return NULL;
+ XMEMSET(acc, 0, sizeof(WOLFSSL_TS_ACCURACY));
+ acc->acc = &a->tst.accuracy;
+ a->accuracy = acc;
+ }
+
+ return a->accuracy;
+}
+
+/* Get whether time-stamps from the TSA are strictly ordered.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return 1 when strictly ordered.
+ * @return 0 when not ordered or a is NULL.
+ */
+int wolfSSL_TS_TST_INFO_get_ordering(const WOLFSSL_TS_TST_INFO* a)
+{
+ if (a == NULL)
+ return 0;
+ return a->tst.ordering;
+}
+
+/* Get the nonce of a TSTInfo.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return Nonce of the object - do not free.
+ * @return NULL when a is NULL or no nonce present.
+ */
+const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_TST_INFO_get_nonce(
+ const WOLFSSL_TS_TST_INFO* a)
+{
+ WOLFSSL_TS_TST_INFO* tst = (WOLFSSL_TS_TST_INFO*)a;
+
+ if ((a == NULL) || (a->tst.nonce == NULL))
+ return NULL;
+ if (tst->nonce == NULL)
+ tst->nonce = wolfssl_asn1_integer_new_buf(a->tst.nonce, a->tst.nonceSz);
+ return tst->nonce;
+}
+
+/* Get the TSA name of a TSTInfo as a GeneralName.
+ *
+ * Builds a GENERAL_NAME from the TSTInfo's tsa field on the first call and
+ * caches it on the object. directoryName, dNSName, rfc822Name and
+ * uniformResourceIdentifier forms are supported.
+ *
+ * @param [in] a TS_TST_INFO object.
+ * @return GeneralName of the TSA - owned by the object, do not free.
+ * @return NULL when a is NULL, no TSA name is present or the form is not
+ * supported.
+ */
+WOLFSSL_GENERAL_NAME* wolfSSL_TS_TST_INFO_get_tsa(WOLFSSL_TS_TST_INFO* a)
+{
+ WOLFSSL_GENERAL_NAME* gn;
+ const byte* tsa;
+ word32 idx = 1;
+ int len = 0;
+ byte tag;
+
+ if ((a == NULL) || (a->tst.tsa == NULL) || (a->tst.tsaSz == 0))
+ return NULL;
+ /* Return the cached view when already built. */
+ if (a->tsa != NULL)
+ return a->tsa;
+
+ tsa = a->tst.tsa;
+ tag = tsa[0];
+ /* The GeneralName content follows the tag and length at tsa[idx]. */
+ if (GetLength(tsa, &idx, &len, a->tst.tsaSz) < 0)
+ return NULL;
+
+ gn = wolfSSL_GENERAL_NAME_new();
+ if (gn == NULL)
+ return NULL;
+
+ if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE)) {
+ /* directoryName [4] - the content is a Name (SEQUENCE). */
+ const unsigned char* p = tsa + idx;
+ WOLFSSL_X509_NAME* name = wolfSSL_d2i_X509_NAME(NULL,
+ (unsigned char**)&p, len);
+
+ if (name == NULL) {
+ wolfSSL_GENERAL_NAME_free(gn);
+ return NULL;
+ }
+ /* Replace the default IA5 string with the directory name. */
+ wolfSSL_ASN1_STRING_free(gn->d.ia5);
+ gn->type = WOLFSSL_GEN_DIRNAME;
+ gn->d.dirn = name;
+ }
+ else if ((tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) ||
+ (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) ||
+ (tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE))) {
+ /* rfc822Name [1], dNSName [2] or uniformResourceIdentifier [6] - an
+ * IA5String. The CHOICE number matches the GENERAL_NAME type and
+ * uses the default IA5 string of the new GeneralName. */
+ if (wolfSSL_ASN1_STRING_set(gn->d.ia5, tsa + idx, len) != 1) {
+ wolfSSL_GENERAL_NAME_free(gn);
+ return NULL;
+ }
+ gn->type = (int)(tag & ~ASN_CONTEXT_SPECIFIC);
+ }
+ else {
+ /* Unsupported GeneralName form. */
+ wolfSSL_GENERAL_NAME_free(gn);
+ return NULL;
+ }
+
+ a->tsa = gn;
+ return gn;
+}
+
+/******************************************************************************
+ * TS_RESP
+ *****************************************************************************/
+
+/* Dispose of a time-stamp response.
+ *
+ * @param [in, out] a TS_RESP object. May be NULL.
+ */
+void wolfSSL_TS_RESP_free(WOLFSSL_TS_RESP* a)
+{
+ if (a != NULL) {
+ XFREE(a->der, NULL, DYNAMIC_TYPE_OPENSSL);
+ ts_status_info_free(a->statusInfo);
+ wolfSSL_TS_TST_INFO_free(a->tstInfo);
+ XFREE(a, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Decode a time-stamp response.
+ *
+ * @param [in, out] a TS_RESP object pointer. May be NULL. Any object
+ * pointed to is freed and replaced.
+ * @param [in, out] pp Pointer to DER encoding - advanced past the
+ * response.
+ * @param [in] length Length of data in buffer in bytes.
+ * @return TS_RESP object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_RESP* wolfSSL_d2i_TS_RESP(WOLFSSL_TS_RESP** a,
+ const unsigned char** pp, long length)
+{
+ WOLFSSL_TS_RESP* ret;
+ word32 sz;
+
+ WOLFSSL_ENTER("wolfSSL_d2i_TS_RESP");
+
+ if ((pp == NULL) || (*pp == NULL))
+ return NULL;
+ sz = ts_msg_len(*pp, length);
+ if (sz == 0)
+ return NULL;
+
+ ret = (WOLFSSL_TS_RESP*)XMALLOC(sizeof(WOLFSSL_TS_RESP), NULL,
+ DYNAMIC_TYPE_OPENSSL);
+ if (ret == NULL)
+ return NULL;
+ XMEMSET(ret, 0, sizeof(WOLFSSL_TS_RESP));
+
+ /* Keep a copy of the encoding - the wc response references into it. */
+ ret->der = (unsigned char*)XMALLOC(sz, NULL, DYNAMIC_TYPE_OPENSSL);
+ if (ret->der == NULL) {
+ wolfSSL_TS_RESP_free(ret);
+ return NULL;
+ }
+
+ XMEMCPY(ret->der, *pp, sz);
+ ret->derSz = sz;
+ if (wc_TspResponse_Decode(&ret->resp, ret->der, sz) != 0) {
+ wolfSSL_TS_RESP_free(ret);
+ return NULL;
+ }
+
+ *pp += sz;
+ if (a != NULL) {
+ wolfSSL_TS_RESP_free(*a);
+ *a = ret;
+ }
+ return ret;
+}
+
+/* Encode a time-stamp response.
+ *
+ * @param [in] a TS_RESP object.
+ * @param [in, out] pp Output buffer pointer. May be NULL. When pointer to
+ * NULL, a buffer is allocated and not advanced.
+ * Otherwise written to and advanced.
+ * @return Length of encoding on success.
+ * @return -1 on failure.
+ */
+int wolfSSL_i2d_TS_RESP(const WOLFSSL_TS_RESP* a, unsigned char** pp)
+{
+ if (a == NULL)
+ return -1;
+ return ts_i2d(a->der, a->derSz, pp);
+}
+
+/* Get the status info of a time-stamp response.
+ *
+ * @param [in] a TS_RESP object.
+ * @return Status info of the object - do not free.
+ * @return NULL when a is NULL.
+ */
+WOLFSSL_TS_STATUS_INFO* wolfSSL_TS_RESP_get_status_info(WOLFSSL_TS_RESP* a)
+{
+ if (a == NULL)
+ return NULL;
+
+ if (a->statusInfo == NULL) {
+ WOLFSSL_TS_STATUS_INFO* info = (WOLFSSL_TS_STATUS_INFO*)XMALLOC(
+ sizeof(WOLFSSL_TS_STATUS_INFO), NULL, DYNAMIC_TYPE_OPENSSL);
+ if (info == NULL)
+ return NULL;
+ XMEMSET(info, 0, sizeof(WOLFSSL_TS_STATUS_INFO));
+ info->resp = &a->resp;
+ a->statusInfo = info;
+ }
+
+ return a->statusInfo;
+}
+
+/* Get the TSTInfo of the token of a time-stamp response.
+ *
+ * The token's signature must verify for the TSTInfo to be available.
+ *
+ * @param [in] a TS_RESP object.
+ * @return TSTInfo of the object - do not free.
+ * @return NULL when a is NULL or there is no valid token.
+ */
+WOLFSSL_TS_TST_INFO* wolfSSL_TS_RESP_get_tst_info(WOLFSSL_TS_RESP* a)
+{
+ if (a == NULL)
+ return NULL;
+
+ /* Get the TSTInfo out of the token on first use. */
+ if ((a->tstInfo == NULL) && (a->resp.token != NULL)) {
+ wc_PKCS7* pkcs7;
+
+ pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ if (pkcs7 != NULL) {
+ TspTstInfo tst;
+
+ if ((wc_PKCS7_InitWithCert(pkcs7, NULL, 0) == 0) &&
+ (wc_TspTstInfo_VerifyWithPKCS7(pkcs7, (byte*)a->resp.token,
+ a->resp.tokenSz, &tst) == 0)) {
+ a->tstInfo = ts_tst_info_from_der(pkcs7->content,
+ pkcs7->contentSz);
+ }
+ wc_PKCS7_Free(pkcs7);
+ }
+ }
+
+ return a->tstInfo;
+}
+
+/******************************************************************************
+ * TS_VERIFY_CTX
+ *****************************************************************************/
+
+/* Create a new verification context.
+ *
+ * @return TS_VERIFY_CTX object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_VERIFY_CTX* wolfSSL_TS_VERIFY_CTX_new(void)
+{
+ WOLFSSL_TS_VERIFY_CTX* ctx;
+
+ ctx = (WOLFSSL_TS_VERIFY_CTX*)XMALLOC(sizeof(WOLFSSL_TS_VERIFY_CTX),
+ NULL, DYNAMIC_TYPE_OPENSSL);
+ if (ctx != NULL) {
+ XMEMSET(ctx, 0, sizeof(WOLFSSL_TS_VERIFY_CTX));
+ }
+
+ return ctx;
+}
+
+/* Free the items of a verification context.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object. May be NULL.
+ */
+void wolfSSL_TS_VERIFY_CTX_cleanup(WOLFSSL_TS_VERIFY_CTX* ctx)
+{
+ if (ctx != NULL) {
+ wolfSSL_X509_STORE_free(ctx->store);
+ XFREE(ctx->imprint, NULL, DYNAMIC_TYPE_OPENSSL);
+ wolfSSL_BIO_free(ctx->data);
+ XMEMSET(ctx, 0, sizeof(WOLFSSL_TS_VERIFY_CTX));
+ }
+}
+
+/* Dispose of a verification context.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object. May be NULL.
+ */
+void wolfSSL_TS_VERIFY_CTX_free(WOLFSSL_TS_VERIFY_CTX* ctx)
+{
+ if (ctx != NULL) {
+ wolfSSL_TS_VERIFY_CTX_cleanup(ctx);
+ XFREE(ctx, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Set the checks to perform when verifying.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object.
+ * @param [in] flags TS_VFY_* flags of checks to perform.
+ * @return Flags set.
+ * @return 0 when ctx is NULL.
+ */
+int wolfSSL_TS_VERIFY_CTX_set_flags(WOLFSSL_TS_VERIFY_CTX* ctx, int flags)
+{
+ if (ctx == NULL)
+ return 0;
+ ctx->flags = (unsigned int)flags;
+ return (int)ctx->flags;
+}
+
+/* Add to the checks to perform when verifying.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object.
+ * @param [in] flags TS_VFY_* flags of checks to add.
+ * @return Flags set.
+ * @return 0 when ctx is NULL.
+ */
+int wolfSSL_TS_VERIFY_CTX_add_flags(WOLFSSL_TS_VERIFY_CTX* ctx, int flags)
+{
+ if (ctx == NULL)
+ return 0;
+ ctx->flags |= (unsigned int)flags;
+ return (int)ctx->flags;
+}
+
+/* Set the expected hash of the message imprint.
+ *
+ * Takes ownership of imprint - disposed of when the context is.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object.
+ * @param [in] imprint Allocated hash of message.
+ * @param [in] len Length of hash in bytes.
+ * @return The imprint set.
+ * @return NULL when ctx is NULL.
+ */
+unsigned char* wolfSSL_TS_VERIFY_CTX_set_imprint(WOLFSSL_TS_VERIFY_CTX* ctx,
+ unsigned char* imprint, long len)
+{
+ if (ctx == NULL)
+ return NULL;
+ XFREE(ctx->imprint, NULL, DYNAMIC_TYPE_OPENSSL);
+ /* Takes ownership of imprint. */
+ ctx->imprint = imprint;
+ ctx->imprintSz = (word32)len;
+ return ctx->imprint;
+}
+
+/* Set the data to hash and check against the message imprint for TS_VFY_DATA.
+ *
+ * Takes ownership of the BIO - freed when the context is cleaned up, matching
+ * OpenSSL. The data is read and hashed during verification.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object.
+ * @param [in] b BIO to read the time-stamped data from. May be NULL.
+ * @return The BIO set.
+ * @return NULL when ctx is NULL.
+ */
+WOLFSSL_BIO* wolfSSL_TS_VERIFY_CTX_set_data(WOLFSSL_TS_VERIFY_CTX* ctx,
+ WOLFSSL_BIO* b)
+{
+ if (ctx == NULL)
+ return NULL;
+ if (ctx->data != b) {
+ /* Replace and take ownership of the new BIO. */
+ wolfSSL_BIO_free(ctx->data);
+ ctx->data = b;
+ }
+ return ctx->data;
+}
+
+/* Set the store of trusted certificates to check the signer against.
+ *
+ * Takes ownership of store - disposed of when the context is.
+ *
+ * @param [in, out] ctx TS_VERIFY_CTX object.
+ * @param [in] store Store of trusted certificates. May be NULL.
+ * @return The store set.
+ * @return NULL when ctx is NULL or clearing the store.
+ */
+WOLFSSL_X509_STORE* wolfSSL_TS_VERIFY_CTX_set_store(
+ WOLFSSL_TS_VERIFY_CTX* ctx, WOLFSSL_X509_STORE* store)
+{
+ if (ctx == NULL)
+ return NULL;
+ wolfSSL_X509_STORE_free(ctx->store);
+ /* Takes ownership of store. */
+ ctx->store = store;
+ return ctx->store;
+}
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Fill a verification context from the request sent.
+ *
+ * The message imprint's hash, nonce and policy are copied and all checks
+ * but the TSA name are enabled.
+ *
+ * @param [in] req TS_REQ object sent.
+ * @param [in, out] ctx TS_VERIFY_CTX object to fill. When NULL a new
+ * object is created.
+ * @return TS_VERIFY_CTX object on success.
+ * @return NULL on failure.
+ */
+WOLFSSL_TS_VERIFY_CTX* wolfSSL_TS_REQ_to_TS_VERIFY_CTX(WOLFSSL_TS_REQ* req,
+ WOLFSSL_TS_VERIFY_CTX* ctx)
+{
+ WOLFSSL_TS_VERIFY_CTX* ret = ctx;
+
+ WOLFSSL_ENTER("wolfSSL_TS_REQ_to_TS_VERIFY_CTX");
+
+ if (req == NULL)
+ return NULL;
+ /* A message imprint hash is required to verify against. */
+ if (req->req.imprint.hashSz == 0)
+ return NULL;
+ if (ret == NULL)
+ ret = wolfSSL_TS_VERIFY_CTX_new();
+ if (ret == NULL)
+ return NULL;
+
+ /* Replace - not accumulate - the checks when reusing a context. */
+ ret->flags = WOLFSSL_TS_VFY_ALL_IMPRINT & ~WOLFSSL_TS_VFY_TSA_NAME;
+
+ /* Copy the message imprint's hash of the request. */
+ XFREE(ret->imprint, NULL, DYNAMIC_TYPE_OPENSSL);
+ ret->imprintSz = req->req.imprint.hashSz;
+ ret->imprint = (unsigned char*)XMALLOC(ret->imprintSz, NULL,
+ DYNAMIC_TYPE_OPENSSL);
+ if (ret->imprint == NULL) {
+ /* Keep a reused context consistent - no imprint means no size. */
+ ret->imprintSz = 0;
+ if (ctx == NULL)
+ wolfSSL_TS_VERIFY_CTX_free(ret);
+ return NULL;
+ }
+
+ XMEMCPY(ret->imprint, req->req.imprint.hash, ret->imprintSz);
+ /* Copy the nonce - cleared to absent when the request has none. */
+ ret->nonceSz = req->req.nonceSz;
+ if (ret->nonceSz != 0) {
+ XMEMCPY(ret->nonce, req->req.nonce, ret->nonceSz);
+ }
+ /* Copy the policy - cleared to absent when the request has none. */
+ ret->policySz = req->req.policySz;
+ if (ret->policySz != 0) {
+ XMEMCPY(ret->policy, req->req.policy, ret->policySz);
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+/* Check the token signer's certificate chains to a trust anchor in the store.
+ *
+ * The certificates carried in the token are used as untrusted intermediate
+ * certificates so a signer issued by an intermediate CA verifies - matching
+ * OpenSSL's TS_RESP_verify. The store is not modified: any intermediates added
+ * to the cert manager while building the chain are unloaded by
+ * wolfSSL_X509_verify_cert().
+ *
+ * @param [in] store Trust store holding the trusted anchors.
+ * @param [in] pkcs7 PKCS7 that verified the token - holds the signer
+ * certificate and the token's certificates.
+ * @return 1 when the signer's certificate is trusted.
+ * @return 0 when it is not trusted or on error.
+ */
+static int ts_verify_signer_trusted(WOLFSSL_X509_STORE* store, wc_PKCS7* pkcs7)
+{
+ int trusted = 0;
+ WOLFSSL_X509* leaf = NULL;
+ WOLFSSL_X509_STORE_CTX* storeCtx = NULL;
+ WOLF_STACK_OF(WOLFSSL_X509)* inter = NULL;
+ const byte* p;
+ int i;
+
+ /* The signer's certificate is the leaf to verify. */
+ p = pkcs7->verifyCert;
+ leaf = wolfSSL_d2i_X509(NULL, &p, (int)pkcs7->verifyCertSz);
+ /* The token's other certificates are untrusted intermediates. */
+ inter = wolfSSL_sk_X509_new_null();
+ if ((leaf == NULL) || (inter == NULL)) {
+ goto done;
+ }
+ for (i = 0; i < MAX_PKCS7_CERTS; i++) {
+ WOLFSSL_X509* x;
+
+ if ((pkcs7->cert[i] == NULL) || (pkcs7->certSz[i] == 0)) {
+ continue;
+ }
+ /* The signer leaf is verified separately - do not add it again. */
+ if ((pkcs7->certSz[i] == pkcs7->verifyCertSz) &&
+ (XMEMCMP(pkcs7->cert[i], pkcs7->verifyCert,
+ pkcs7->verifyCertSz) == 0)) {
+ continue;
+ }
+ p = pkcs7->cert[i];
+ x = wolfSSL_d2i_X509(NULL, &p, (int)pkcs7->certSz[i]);
+ if (x == NULL) {
+ goto done;
+ }
+ if (wolfSSL_sk_X509_push(inter, x) <= 0) {
+ wolfSSL_X509_free(x);
+ goto done;
+ }
+ }
+
+ /* Build and verify the chain from the leaf through the intermediates to a
+ * trust anchor in the store. */
+ storeCtx = wolfSSL_X509_STORE_CTX_new();
+ if ((storeCtx != NULL) &&
+ (wolfSSL_X509_STORE_CTX_init(storeCtx, store, leaf, inter) ==
+ WOLFSSL_SUCCESS) &&
+ (wolfSSL_X509_verify_cert(storeCtx) == WOLFSSL_SUCCESS)) {
+ trusted = 1;
+ }
+
+done:
+ wolfSSL_X509_STORE_CTX_free(storeCtx);
+ wolfSSL_sk_X509_pop_free(inter, wolfSSL_X509_free);
+ wolfSSL_X509_free(leaf);
+ return trusted;
+}
+
+/* Hash the data read from a BIO with the token's message imprint algorithm
+ * and compare to the imprint - the TS_VFY_DATA check. The data is hashed
+ * incrementally so it need not be held in memory all at once.
+ *
+ * @param [in] bio BIO to read the time-stamped data from.
+ * @param [in] tst Decoded TSTInfo with the expected message imprint.
+ * @return 1 when the hash of the data matches the imprint.
+ * @return 0 otherwise or on error.
+ */
+static int ts_check_data(WOLFSSL_BIO* bio, const TspTstInfo* tst)
+{
+ int ok = 0;
+ int failed = 0;
+ int n;
+ enum wc_HashType hashType;
+ int digestSz;
+ wc_HashAlg hash;
+ byte digest[WC_MAX_DIGEST_SIZE];
+#define TSP_CHECK_DATA_BUF_SZ 256
+#ifdef WOLFSSL_SMALL_STACK
+ byte* buf = NULL;
+#else
+ byte buf[TSP_CHECK_DATA_BUF_SZ];
+#endif
+
+ /* Hash algorithm and digest size of the message imprint. */
+ hashType = wc_OidGetHash((int)tst->imprint.hashAlgOID);
+ digestSz = wc_HashGetDigestSize(hashType);
+ if ((digestSz <= 0) || (tst->imprint.hashSz != (word32)digestSz)) {
+ return 0;
+ }
+
+ if (wc_HashInit(&hash, hashType) != 0) {
+ return 0;
+ }
+
+#ifdef WOLFSSL_SMALL_STACK
+ buf = (byte*)XMALLOC(TSP_CHECK_DATA_BUF_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (buf == NULL) {
+ wc_HashFree(&hash, hashType);
+ return 0;
+ }
+#endif
+ /* Hash the data in chunks until the BIO is exhausted. A non-positive read
+ * ends the data: a wolfSSL memory BIO signals end-of-data with a negative
+ * return (BIO_read cannot distinguish it from an I/O error), and OpenSSL's
+ * own imprint check likewise stops on any non-positive read. */
+ while ((n = wolfSSL_BIO_read(bio, buf, TSP_CHECK_DATA_BUF_SZ)) > 0) {
+ if (wc_HashUpdate(&hash, hashType, buf, (word32)n) != 0) {
+ failed = 1;
+ break;
+ }
+ }
+ if ((!failed) && (wc_HashFinal(&hash, hashType, digest) == 0) &&
+ (XMEMCMP(digest, tst->imprint.hash, (word32)digestSz) == 0)) {
+ ok = 1;
+ }
+ wc_HashFree(&hash, hashType);
+
+#ifdef WOLFSSL_SMALL_STACK
+ XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+
+ return ok;
+}
+#undef TSP_CHECK_DATA_BUF_SZ
+
+/* Verify a time-stamp token against the expected values in the context.
+ *
+ * The token's signature, content type and signer's certificate are always
+ * checked - the signer must be trusted when a store is set. The version,
+ * message imprint, nonce and policy are checked as flagged.
+ *
+ * @param [in] ctx TS_VERIFY_CTX object with checks and expected values.
+ * @param [in] token DER encoding of time-stamp token - CMS SignedData.
+ * @param [in] tokenSz Length of encoding in bytes.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+static int ts_verify_token(WOLFSSL_TS_VERIFY_CTX* ctx, unsigned char* token,
+ word32 tokenSz)
+{
+ int ret = 0;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+
+ /* Verify the signature of the token and validity for time-stamping. */
+ pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ if (pkcs7 == NULL)
+ return 0;
+ if ((wc_PKCS7_InitWithCert(pkcs7, NULL, 0) != 0) ||
+ (wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tst) != 0)) {
+ goto cleanup;
+ }
+
+ /* When signature/signer verification is requested a trust store is
+ * required - without one the signer is only checked against the cert
+ * embedded in the token, which establishes no trust. Fail closed. */
+ if ((ctx->flags & (WOLFSSL_TS_VFY_SIGNATURE | WOLFSSL_TS_VFY_SIGNER)) &&
+ ((ctx->store == NULL) || (ctx->store->cm == NULL))) {
+ WOLFSSL_MSG("TS signer verification requested but no trust store set");
+ goto cleanup;
+ }
+ /* Check the signer's certificate is trusted when a store is set, building
+ * a chain through any intermediate certificates carried in the token. */
+ if ((ctx->store != NULL) && (ctx->store->cm != NULL) &&
+ (!ts_verify_signer_trusted(ctx->store, pkcs7))) {
+ WOLFSSL_MSG("TS signer's certificate is not trusted");
+ goto cleanup;
+ }
+
+ /* Only version 1 supported. */
+ if ((ctx->flags & WOLFSSL_TS_VFY_VERSION) &&
+ (tst.version != WC_TSP_VERSION)) {
+ WOLFSSL_MSG("TS version is not 1");
+ goto cleanup;
+ }
+ /* Check the message imprint's hash is the expected. */
+ if ((ctx->flags & WOLFSSL_TS_VFY_IMPRINT) &&
+ ((ctx->imprint == NULL) ||
+ (tst.imprint.hashSz != ctx->imprintSz) ||
+ (XMEMCMP(tst.imprint.hash, ctx->imprint,
+ ctx->imprintSz) != 0))) {
+ WOLFSSL_MSG("TS message imprint doesn't match");
+ goto cleanup;
+ }
+ /* Check the data, when provided, hashes to the message imprint. The data
+ * is read from the BIO and hashed with the token's imprint algorithm. */
+ if ((ctx->flags & WOLFSSL_TS_VFY_DATA) &&
+ ((ctx->data == NULL) || (!ts_check_data(ctx->data, &tst)))) {
+ WOLFSSL_MSG("TS data doesn't match the message imprint");
+ goto cleanup;
+ }
+ /* Check the nonce is the expected when set. */
+ if ((ctx->flags & WOLFSSL_TS_VFY_NONCE) && (ctx->nonceSz != 0) &&
+ ((tst.nonce == NULL) || (tst.nonceSz != ctx->nonceSz) ||
+ (XMEMCMP(tst.nonce, ctx->nonce, tst.nonceSz) != 0))) {
+ WOLFSSL_MSG("TS nonce doesn't match");
+ goto cleanup;
+ }
+ /* Check the policy is the expected when set. */
+ if ((ctx->flags & WOLFSSL_TS_VFY_POLICY) && (ctx->policySz != 0) &&
+ ((tst.policy == NULL) || (tst.policySz != ctx->policySz) ||
+ (XMEMCMP(tst.policy, ctx->policy, tst.policySz) != 0))) {
+ WOLFSSL_MSG("TS policy doesn't match");
+ goto cleanup;
+ }
+
+ ret = 1;
+cleanup:
+ wc_PKCS7_Free(pkcs7);
+ return ret;
+}
+
+/* Verify a time-stamp response.
+ *
+ * The time-stamp must have been granted and the token is verified against
+ * the context - see ts_verify_token().
+ *
+ * @param [in] ctx TS_VERIFY_CTX object with checks and expected
+ * values.
+ * @param [in] response TS_RESP object to verify.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_RESP_verify_response(WOLFSSL_TS_VERIFY_CTX* ctx,
+ WOLFSSL_TS_RESP* response)
+{
+ WOLFSSL_ENTER("wolfSSL_TS_RESP_verify_response");
+
+ if ((ctx == NULL) || (response == NULL))
+ return 0;
+
+ /* Time-stamp must have been granted. */
+ if ((response->resp.status != WC_TSP_PKISTATUS_GRANTED) &&
+ (response->resp.status != WC_TSP_PKISTATUS_GRANTED_WITH_MODS)) {
+ WOLFSSL_MSG("TS status is not granted");
+ return 0;
+ }
+ if (response->resp.token == NULL) {
+ WOLFSSL_MSG("TS response has no time-stamp token");
+ return 0;
+ }
+
+ return ts_verify_token(ctx, (byte*)response->resp.token,
+ response->resp.tokenSz);
+}
+
+#ifdef OPENSSL_ALL
+/* Verify a time-stamp token.
+ *
+ * The token's encoding, kept by d2i_PKCS7, is verified against the context -
+ * see ts_verify_token().
+ *
+ * @param [in] ctx TS_VERIFY_CTX object with checks and expected values.
+ * @param [in] token WOLFSSL_PKCS7 object holding a time-stamp token - the
+ * extended object returned by d2i_PKCS7(), which keeps the
+ * DER encoding needed here.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_RESP_verify_token(WOLFSSL_TS_VERIFY_CTX* ctx,
+ WOLFSSL_PKCS7* token)
+{
+ WOLFSSL_ENTER("wolfSSL_TS_RESP_verify_token");
+
+ if ((ctx == NULL) || (token == NULL) || (token->data == NULL) ||
+ (token->len <= 0)) {
+ return 0;
+ }
+
+ return ts_verify_token(ctx, token->data, (word32)token->len);
+}
+#endif /* OPENSSL_ALL */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/******************************************************************************
+ * TS_RESP_CTX - responder context for creating time-stamp responses.
+ *****************************************************************************/
+
+struct WOLFSSL_TS_RESP_CTX {
+ /* Signer's certificate as DER - owned. */
+ unsigned char* certDer;
+ word32 certSz;
+ /* Signer's private key as DER - owned. */
+ unsigned char* keyDer;
+ word32 keySz;
+ /* Private key type - WC_PK_TYPE_RSA or WC_PK_TYPE_ECDSA_SIGN. */
+ enum wc_PkType keyType;
+ /* Signature hash algorithm. */
+ enum wc_HashType hashType;
+ /* Default TSA policy as OBJECT IDENTIFIER content - owned. */
+ unsigned char* policy;
+ word32 policySz;
+ /* Serial number callback and its data. */
+ WOLFSSL_TS_serial_cb serialCb;
+ void* serialCbData;
+ /* Time callback and its data - when NULL the current time is used. */
+ WOLFSSL_TS_time_cb timeCb;
+ void* timeCbData;
+ /* Accuracy of the time-stamp. */
+ int accSecs;
+ int accMillis;
+ int accMicros;
+ /* TS_* flags - e.g. TS_ORDERING. */
+ int flags;
+};
+
+/* Create a responder context.
+ *
+ * @return TS_RESP_CTX object on success.
+ * @return NULL on allocation failure.
+ */
+WOLFSSL_TS_RESP_CTX* wolfSSL_TS_RESP_CTX_new(void)
+{
+ WOLFSSL_TS_RESP_CTX* ctx;
+
+ WOLFSSL_ENTER("wolfSSL_TS_RESP_CTX_new");
+
+ ctx = (WOLFSSL_TS_RESP_CTX*)XMALLOC(sizeof(WOLFSSL_TS_RESP_CTX), NULL,
+ DYNAMIC_TYPE_OPENSSL);
+ if (ctx != NULL) {
+ XMEMSET(ctx, 0, sizeof(WOLFSSL_TS_RESP_CTX));
+ /* Default to SHA-256 for the signature. */
+ ctx->hashType = WC_HASH_TYPE_SHA256;
+ }
+ return ctx;
+}
+
+/* Dispose of a responder context.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object. May be NULL.
+ */
+void wolfSSL_TS_RESP_CTX_free(WOLFSSL_TS_RESP_CTX* ctx)
+{
+ if (ctx != NULL) {
+ XFREE(ctx->certDer, NULL, DYNAMIC_TYPE_OPENSSL);
+ XFREE(ctx->keyDer, NULL, DYNAMIC_TYPE_OPENSSL);
+ XFREE(ctx->policy, NULL, DYNAMIC_TYPE_OPENSSL);
+ XFREE(ctx, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+}
+
+/* Set the signer's certificate.
+ *
+ * The certificate is copied as DER - the caller keeps ownership of signer.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] signer Signer's certificate.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_RESP_CTX_set_signer_cert(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_X509* signer)
+{
+ int derSz;
+ unsigned char* der = NULL;
+
+ if ((ctx == NULL) || (signer == NULL))
+ return 0;
+
+ /* Encode the certificate to DER. */
+ derSz = wolfSSL_i2d_X509(signer, &der);
+ if ((derSz <= 0) || (der == NULL))
+ return 0;
+
+ XFREE(ctx->certDer, NULL, DYNAMIC_TYPE_OPENSSL);
+ ctx->certDer = der;
+ ctx->certSz = (word32)derSz;
+ return 1;
+}
+
+/* Set the signer's private key.
+ *
+ * The key is copied as DER - the caller keeps ownership of key.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] key Signer's private key - RSA or ECDSA.
+ * @return 1 on success.
+ * @return 0 on failure or an unsupported key type.
+ */
+int wolfSSL_TS_RESP_CTX_set_signer_key(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_EVP_PKEY* key)
+{
+ int derSz;
+ int baseId;
+ unsigned char* der = NULL;
+ enum wc_PkType keyType;
+
+ if ((ctx == NULL) || (key == NULL))
+ return 0;
+
+ /* Map the key type to the signature algorithm. */
+ baseId = wolfSSL_EVP_PKEY_base_id(key);
+ if (baseId == WC_EVP_PKEY_RSA) {
+ keyType = WC_PK_TYPE_RSA;
+ }
+#ifdef HAVE_ECC
+ else if (baseId == WC_EVP_PKEY_EC) {
+ keyType = WC_PK_TYPE_ECDSA_SIGN;
+ }
+#endif
+ else {
+ WOLFSSL_MSG("TS signer key type not supported");
+ return 0;
+ }
+
+ /* Encode the private key to DER. */
+ derSz = wolfSSL_i2d_PrivateKey(key, &der);
+ if ((derSz <= 0) || (der == NULL))
+ return 0;
+
+ XFREE(ctx->keyDer, NULL, DYNAMIC_TYPE_OPENSSL);
+ ctx->keyDer = der;
+ ctx->keySz = (word32)derSz;
+ ctx->keyType = keyType;
+ return 1;
+}
+
+/* Set the message digest used for the signature.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] md Message digest.
+ * @return 1 on success.
+ * @return 0 on failure or an unsupported digest.
+ */
+int wolfSSL_TS_RESP_CTX_set_signer_digest(WOLFSSL_TS_RESP_CTX* ctx,
+ const WOLFSSL_EVP_MD* md)
+{
+ int hashType = 0;
+ int hashSz = 0;
+
+ if ((ctx == NULL) || (md == NULL))
+ return 0;
+ if (wolfSSL_EVP_get_hashinfo(md, &hashType, &hashSz) != WOLFSSL_SUCCESS)
+ return 0;
+
+ ctx->hashType = (enum wc_HashType)hashType;
+ return 1;
+}
+
+/* Set the default TSA policy.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] policy Policy as an OBJECT IDENTIFIER.
+ * @return 1 on success.
+ * @return 0 on failure.
+ */
+int wolfSSL_TS_RESP_CTX_set_def_policy(WOLFSSL_TS_RESP_CTX* ctx,
+ const WOLFSSL_ASN1_OBJECT* policy)
+{
+ const unsigned char* oid;
+ word32 oidSz;
+ word32 idx = 1;
+ int len = 0;
+ unsigned char* copy;
+
+ if ((ctx == NULL) || (policy == NULL) || (policy->obj == NULL))
+ return 0;
+
+ /* The object may be full DER or bare content - store the content. */
+ oid = policy->obj;
+ oidSz = policy->objSz;
+ if ((oidSz >= 2) && (oid[0] == ASN_OBJECT_ID) &&
+ (GetLength(oid, &idx, &len, oidSz) >= 0) &&
+ (idx + (word32)len == oidSz)) {
+ oid += idx;
+ oidSz = (word32)len;
+ }
+ if ((oidSz == 0) || (oidSz > MAX_OID_SZ))
+ return 0;
+
+ copy = (unsigned char*)XMALLOC(oidSz, NULL, DYNAMIC_TYPE_OPENSSL);
+ if (copy == NULL)
+ return 0;
+ XMEMCPY(copy, oid, oidSz);
+
+ XFREE(ctx->policy, NULL, DYNAMIC_TYPE_OPENSSL);
+ ctx->policy = copy;
+ ctx->policySz = oidSz;
+ return 1;
+}
+
+/* Set the callback that supplies a serial number for each response.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] cb Serial number callback.
+ * @param [in] data Data passed to the callback.
+ * @return 1 on success.
+ * @return 0 when ctx is NULL.
+ */
+int wolfSSL_TS_RESP_CTX_set_serial_cb(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_TS_serial_cb cb, void* data)
+{
+ if (ctx == NULL)
+ return 0;
+ ctx->serialCb = cb;
+ ctx->serialCbData = data;
+ return 1;
+}
+
+/* Set the callback that supplies the time for each response.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] cb Time callback.
+ * @param [in] data Data passed to the callback.
+ * @return 1 on success.
+ * @return 0 when ctx is NULL.
+ */
+int wolfSSL_TS_RESP_CTX_set_time_cb(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_TS_time_cb cb, void* data)
+{
+ if (ctx == NULL)
+ return 0;
+ ctx->timeCb = cb;
+ ctx->timeCbData = data;
+ return 1;
+}
+
+/* Set the accuracy of the time-stamp.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] secs Accuracy in seconds.
+ * @param [in] millis Accuracy in milliseconds - 0..999.
+ * @param [in] micros Accuracy in microseconds - 0..999.
+ * @return 1 on success.
+ * @return 0 when ctx is NULL.
+ */
+int wolfSSL_TS_RESP_CTX_set_accuracy(WOLFSSL_TS_RESP_CTX* ctx, int secs,
+ int millis, int micros)
+{
+ if (ctx == NULL)
+ return 0;
+ ctx->accSecs = secs;
+ ctx->accMillis = millis;
+ ctx->accMicros = micros;
+ return 1;
+}
+
+/* Add flags to the responder context.
+ *
+ * @param [in, out] ctx TS_RESP_CTX object.
+ * @param [in] flags TS_* flags to add.
+ * @return 1 on success.
+ * @return 0 when ctx is NULL.
+ */
+int wolfSSL_TS_RESP_CTX_add_flags(WOLFSSL_TS_RESP_CTX* ctx, int flags)
+{
+ if (ctx == NULL)
+ return 0;
+ ctx->flags |= flags;
+ return 1;
+}
+
+/* Create a time-stamp response for a request.
+ *
+ * Reads the DER request from the BIO, builds a granted TSTInfo from the
+ * request and the context's configured values, signs it into a time-stamp
+ * token and returns the response. The signer's certificate is included in the
+ * token. A serial number callback must be set; a time callback is optional -
+ * the current time is used when not set.
+ *
+ * @param [in] ctx TS_RESP_CTX object.
+ * @param [in] req_bio BIO holding the DER encoded TimeStampReq.
+ * @return TS_RESP object on success - free with TS_RESP_free().
+ * @return NULL on failure.
+ */
+/* Maximum size of a TS request read from the BIO. */
+#define TSP_RESP_REQ_DER_SZ 2048
+
+WOLFSSL_TS_RESP* wolfSSL_TS_RESP_create_response(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_BIO* req_bio)
+{
+ int ok = 0;
+ int n;
+ int rngInit = 0;
+ word32 reqSz = 0;
+ WC_RNG rng;
+ TspRequest req;
+ TspTstInfo tst;
+ TspResponse resp;
+ WOLFSSL_TS_RESP* tsResp = NULL;
+ WOLFSSL_ASN1_INTEGER* serialInt = NULL;
+ const unsigned char* serial = NULL;
+ word32 serialSz = 0;
+#ifndef NO_ASN_TIME
+ byte genTime[ASN_GENERALIZED_TIME_SIZE];
+#endif
+ const byte* genTimePtr = NULL;
+ word32 genTimeSz = 0;
+#ifdef WOLFSSL_SMALL_STACK
+ byte* reqDer = NULL;
+#else
+ byte reqDer[TSP_RESP_REQ_DER_SZ];
+#endif
+ byte* token = NULL;
+ word32 tokenSz = 0;
+ const unsigned char* p;
+
+ WOLFSSL_ENTER("wolfSSL_TS_RESP_create_response");
+
+ /* The signer, key, policy and a serial callback are required. */
+ if ((ctx == NULL) || (req_bio == NULL) || (ctx->certDer == NULL) ||
+ (ctx->keyDer == NULL) || (ctx->policy == NULL) ||
+ (ctx->serialCb == NULL)) {
+ WOLFSSL_MSG("TS_RESP_CTX is not fully configured");
+ return NULL;
+ }
+
+#ifdef WOLFSSL_SMALL_STACK
+ reqDer = (byte*)XMALLOC(TSP_RESP_REQ_DER_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (reqDer == NULL)
+ return NULL;
+#endif
+
+ /* Read the DER request from the BIO. */
+ n = wolfSSL_BIO_read(req_bio, reqDer, TSP_RESP_REQ_DER_SZ);
+ if (n <= 0) {
+ WOLFSSL_MSG("Failed to read TS request from BIO");
+ goto cleanup;
+ }
+ reqSz = (word32)n;
+
+ /* Decode the request and start a TSTInfo. */
+ if ((wc_TspRequest_Decode(&req, reqDer, reqSz) != 0) ||
+ (wc_TspTstInfo_Init(&tst) != 0)) {
+ goto cleanup;
+ }
+
+ /* Get a serial number from the callback - a non-negative INTEGER whose
+ * magnitude bytes are the serial number. */
+ serialInt = ctx->serialCb(ctx, ctx->serialCbData);
+ if (ts_asn1_integer_get(serialInt, &serial, &serialSz) != 1)
+ goto cleanup;
+
+ /* Get the time from the callback. Without time support (NO_ASN_TIME) the
+ * callback's time cannot be formatted as a GeneralizedTime, and the
+ * encoder cannot supply the current time either, so a response cannot be
+ * created - see the genTime encode path which returns an error. */
+#ifndef NO_ASN_TIME
+ if (ctx->timeCb != NULL) {
+ long sec = 0;
+ long usec = 0;
+
+ if (ctx->timeCb(ctx, ctx->timeCbData, &sec, &usec) != 1)
+ goto cleanup;
+ if (wc_TspTstInfo_SetGenTimeAsTime(&tst, (time_t)sec, genTime,
+ (word32)sizeof(genTime)) != 0) {
+ goto cleanup;
+ }
+ genTimePtr = genTime;
+ genTimeSz = tst.genTimeSz;
+ }
+#endif
+
+ /* Set the TSTInfo from the request and the configured values. The genTime
+ * is NULL when no time callback - the encoder uses the current time. */
+ if (wc_TspTstInfo_SetFromRequest(&tst, &req, ctx->policy, ctx->policySz,
+ serial, serialSz, genTimePtr, genTimeSz) != 0) {
+ goto cleanup;
+ }
+ /* Apply accuracy and ordering from the context. */
+ if ((ctx->accSecs != 0) || (ctx->accMillis != 0) ||
+ (ctx->accMicros != 0)) {
+ (void)wc_TspTstInfo_SetAccuracy(&tst, (word32)ctx->accSecs,
+ (word16)ctx->accMillis, (word16)ctx->accMicros);
+ }
+ if (ctx->flags & WOLFSSL_TS_ORDERING) {
+ tst.ordering = 1;
+ }
+
+ /* Sign the TSTInfo into a token - includes the signer's certificate. */
+ if (wc_InitRng(&rng) != 0)
+ goto cleanup;
+ rngInit = 1;
+ tokenSz = ctx->certSz + 2048;
+ token = (byte*)XMALLOC(tokenSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (token == NULL)
+ goto cleanup;
+ if (wc_TspTstInfo_Sign(&tst, ctx->certDer, ctx->certSz, ctx->keyDer,
+ ctx->keySz, ctx->keyType, ctx->hashType, &rng, token,
+ &tokenSz) != 0) {
+ goto cleanup;
+ }
+
+ /* Wrap the token in a granted response, encode it and load it into a
+ * TS_RESP so the getters work. */
+ if (wc_TspResponse_Init(&resp) != 0)
+ goto cleanup;
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ {
+ byte* respDer;
+ word32 respDerSz = 0;
+
+ /* Get the encoded length, allocate and encode. */
+ if (wc_TspResponse_Encode(&resp, NULL, &respDerSz) != 0)
+ goto cleanup;
+ respDer = (byte*)XMALLOC(respDerSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ if (respDer == NULL)
+ goto cleanup;
+ if (wc_TspResponse_Encode(&resp, respDer, &respDerSz) == 0) {
+ p = respDer;
+ tsResp = wolfSSL_d2i_TS_RESP(NULL, &p, (long)respDerSz);
+ if (tsResp != NULL)
+ ok = 1;
+ }
+ XFREE(respDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+ }
+
+cleanup:
+ if (!ok) {
+ wolfSSL_TS_RESP_free(tsResp);
+ tsResp = NULL;
+ }
+ if (rngInit)
+ wc_FreeRng(&rng);
+ XFREE(token, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#ifdef WOLFSSL_SMALL_STACK
+ XFREE(reqDer, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+ wolfSSL_ASN1_INTEGER_free(serialInt);
+ WOLFSSL_LEAVE("wolfSSL_TS_RESP_create_response", ok);
+ return tsResp;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#endif /* OPENSSL_EXTRA && WOLFSSL_TSP && HAVE_PKCS7 */
+
+#endif /* WOLFSSL_SSL_TSP_INCLUDED */
diff --git a/src/x509.c b/src/x509.c
index fd87b1da4aa..dda47add3b9 100644
--- a/src/x509.c
+++ b/src/x509.c
@@ -3704,7 +3704,7 @@ static int wolfSSL_ASN1_STRING_into_old_ext_fmt(WOLFSSL_ASN1_STRING *asn1str,
ret = DecodeExtKeyUsage((const byte*)asn1str->data, asn1str->length,
&extExtKeyUsageSrc, &extExtKeyUsageSz, &extExtKeyUsageCount,
- &extExtKeyUsage, &extExtKeyUsageSsh);
+ &extExtKeyUsage, &extExtKeyUsageSsh, NULL);
if (ret != 0)
return ret;
diff --git a/tests/api.c b/tests/api.c
index 32b90b9a079..99c4e4808dc 100644
--- a/tests/api.c
+++ b/tests/api.c
@@ -245,11 +245,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -34912,6 +34914,9 @@ TEST_CASE testCases[] = {
/* ASN */
TEST_ASN_DECLS,
+ /* Time-Stamp Protocol (RFC 3161) */
+ TEST_TSP_DECLS,
+
/* LMS, and RFC 9802 (HSS/LMS and XMSS/XMSS^MT in X.509) */
TEST_LMS_XMSS_DECLS,
@@ -35061,6 +35066,7 @@ TEST_CASE testCases[] = {
/* X509 tests */
TEST_OSSL_X509_DECLS,
+ TEST_OSSL_TSP_DECLS,
TEST_OSSL_X509_NAME_DECLS,
TEST_OSSL_X509_EXT_DECLS,
TEST_OSSL_X509_PK_DECLS,
diff --git a/tests/api/include.am b/tests/api/include.am
index 3d204763c6d..fcb9720114c 100644
--- a/tests/api/include.am
+++ b/tests/api/include.am
@@ -68,6 +68,8 @@ tests_unit_test_SOURCES += tests/api/test_session.c
tests_unit_test_SOURCES += tests/api/test_x509.c
# ASN
tests_unit_test_SOURCES += tests/api/test_asn.c
+# Time-Stamp Protocol (RFC 3161)
+tests_unit_test_SOURCES += tests/api/test_tsp.c
# PKCS#7
tests_unit_test_SOURCES += tests/api/test_pkcs7.c
# PKCS#12
@@ -76,6 +78,7 @@ tests_unit_test_SOURCES += tests/api/test_pkcs12.c
tests_unit_test_SOURCES += tests/api/test_pwdbased.c
# OpenSSL ASN.1
tests_unit_test_SOURCES += tests/api/test_ossl_asn1.c
+tests_unit_test_SOURCES += tests/api/test_ossl_tsp.c
# OpenSSL BN
tests_unit_test_SOURCES += tests/api/test_ossl_bn.c
# OpenSSL BIO
@@ -178,10 +181,12 @@ EXTRA_DIST += tests/api/test_tls.h
EXTRA_DIST += tests/api/test_session.h
EXTRA_DIST += tests/api/test_x509.h
EXTRA_DIST += tests/api/test_asn.h
+EXTRA_DIST += tests/api/test_tsp.h
EXTRA_DIST += tests/api/test_pkcs7.h
EXTRA_DIST += tests/api/test_pkcs12.h
EXTRA_DIST += tests/api/test_pwdbased.h
EXTRA_DIST += tests/api/test_ossl_asn1.h
+EXTRA_DIST += tests/api/test_ossl_tsp.h
EXTRA_DIST += tests/api/test_ossl_bn.h
EXTRA_DIST += tests/api/test_ossl_bio.h
EXTRA_DIST += tests/api/test_ossl_dgst.h
diff --git a/tests/api/test_ossl_tsp.c b/tests/api/test_ossl_tsp.c
new file mode 100644
index 00000000000..6be9ae2c92f
--- /dev/null
+++ b/tests/api/test_ossl_tsp.c
@@ -0,0 +1,1857 @@
+/* test_ossl_tsp.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+#include
+
+#include
+#include
+
+#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TSP) && \
+ defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
+ !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ #define TEST_OSSL_TSP
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+#endif
+
+#ifdef TEST_OSSL_TSP
+
+/* Hash of message - content is not checked against an algorithm. */
+static const byte tsOsslHash[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+};
+#ifdef WOLFSSL_TSP_RESPONDER
+/* 1.3.6.1.4.1.999.1 - test TSA policy. */
+static const byte tsOsslPolicy[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+};
+#endif /* WOLFSSL_TSP_RESPONDER */
+/* Nonce with top bit set to check INTEGER encoding. */
+static const byte tsOsslNonce[] = {
+ 0xc3, 0x5a, 0x10, 0x42, 0x77, 0x08, 0x99, 0x01
+};
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Serial number of test time-stamp. */
+static const byte tsOsslSerial[] = { 0x9a, 0x33 };
+/* Time of test time-stamp. */
+static const byte tsOsslGenTime[] = "20260605120000Z";
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+/* Options controlling the test TimeStampResp built by
+ * test_tsp_create_resp_ex(). */
+typedef struct TsRespOpts {
+ int withNonce; /* Include the nonce in the TSTInfo. */
+ int withMicros; /* Include a microseconds accuracy. */
+ int ordering; /* Set the ordering flag of the TSTInfo. */
+ int noAccuracy; /* Omit the accuracy from the TSTInfo. */
+ byte status; /* PKIStatus to put on the response. */
+} TsRespOpts;
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Create a TimeStampResp with a token signed by the test TSA. */
+static int test_tsp_create_resp_ex(byte* out, word32* outSz,
+ const TsRespOpts* opts)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ tst.policy = tsOsslPolicy;
+ tst.policySz = (word32)sizeof(tsOsslPolicy);
+ tst.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(tst.imprint.hash, tsOsslHash, sizeof(tsOsslHash));
+ tst.imprint.hashSz = (word32)sizeof(tsOsslHash);
+ tst.serial = tsOsslSerial;
+ tst.serialSz = (word32)sizeof(tsOsslSerial);
+ tst.genTime = tsOsslGenTime;
+ tst.genTimeSz = (word32)sizeof(tsOsslGenTime) - 1;
+ if (!opts->noAccuracy) {
+ tst.accuracy.seconds = 1;
+ tst.accuracy.millis = 500;
+ if (opts->withMicros) {
+ tst.accuracy.micros = 250;
+ }
+ }
+ tst.ordering = (byte)(opts->ordering != 0);
+ if (opts->withNonce) {
+ tst.nonce = tsOsslNonce;
+ tst.nonceSz = (word32)sizeof(tsOsslNonce);
+ }
+
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048), 0);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->rng = &rng;
+ pkcs7->hashOID = SHA256h;
+ pkcs7->encryptOID = RSAk;
+ pkcs7->privateKey = (byte*)tsa_key_der_2048;
+ pkcs7->privateKeySz = sizeof_tsa_key_der_2048;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = opts->status;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, out, outSz), 0);
+
+ wc_FreeRng(&rng);
+ return EXPECT_RESULT();
+}
+
+/* Create a granted TimeStampResp with a token signed by the test TSA. */
+static int test_tsp_create_resp(byte* out, word32* outSz, int withNonce)
+{
+ TsRespOpts opts;
+ XMEMSET(&opts, 0, sizeof(opts));
+ opts.withNonce = withNonce;
+ opts.status = WC_TSP_PKISTATUS_GRANTED;
+ return test_tsp_create_resp_ex(out, outSz, &opts);
+}
+
+/* Create a granted TimeStampResp whose token is signed by an intermediate-
+ * issued TSA certificate. The token carries both the signer and the
+ * intermediate CA so a verifier holding only the root can build the chain. */
+static int test_tsp_create_resp_chain(byte* out, word32* outSz)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ TspResponse resp;
+ byte token[4096];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ tst.policy = tsOsslPolicy;
+ tst.policySz = (word32)sizeof(tsOsslPolicy);
+ tst.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(tst.imprint.hash, tsOsslHash, sizeof(tsOsslHash));
+ tst.imprint.hashSz = (word32)sizeof(tsOsslHash);
+ tst.serial = tsOsslSerial;
+ tst.serialSz = (word32)sizeof(tsOsslSerial);
+ tst.genTime = tsOsslGenTime;
+ tst.genTimeSz = (word32)sizeof(tsOsslGenTime) - 1;
+ tst.nonce = tsOsslNonce;
+ tst.nonceSz = (word32)sizeof(tsOsslNonce);
+
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ /* The signer is the intermediate-issued TSA leaf. */
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)tsa_chain_cert_der_2048,
+ sizeof_tsa_chain_cert_der_2048), 0);
+ /* Carry the intermediate CA in the token for chain building. */
+ ExpectIntEQ(wc_PKCS7_AddCertificate(pkcs7, (byte*)ca_int_cert_der_2048,
+ sizeof_ca_int_cert_der_2048), 0);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->rng = &rng;
+ pkcs7->hashOID = SHA256h;
+ pkcs7->encryptOID = RSAk;
+ pkcs7->privateKey = (byte*)tsa_chain_key_der_2048;
+ pkcs7->privateKeySz = sizeof_tsa_chain_key_der_2048;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, out, outSz), 0);
+
+ wc_FreeRng(&rng);
+ return EXPECT_RESULT();
+}
+
+/* Attach a trust store holding the test TSA certificate to a verification
+ * context so the token signer can be anchored - without a store the verifier
+ * fails closed. Each context owns its store, so a fresh store is built per
+ * call. Returns TEST_SUCCESS on success. */
+static int test_tsp_trust_ctx(WOLFSSL_TS_VERIFY_CTX* ctx)
+{
+ EXPECT_DECLS;
+ WOLFSSL_X509_STORE* store = NULL;
+ WOLFSSL_X509* caX509 = NULL;
+ const unsigned char* cp = tsa_cert_der_2048;
+
+ ExpectNotNull(caX509 = wolfSSL_d2i_X509(NULL, &cp,
+ sizeof_tsa_cert_der_2048));
+ ExpectNotNull(store = wolfSSL_X509_STORE_new());
+ ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, caX509), 1);
+ wolfSSL_X509_free(caX509);
+ if (EXPECT_SUCCESS()) {
+ ExpectNotNull(TS_VERIFY_CTX_set_store(ctx, store));
+ }
+ else {
+ wolfSSL_X509_STORE_free(store);
+ }
+ return EXPECT_RESULT();
+}
+
+/* Serial number callback for the TS_RESP_CTX - returns a fixed serial. The
+ * response creation takes ownership of the returned ASN1_INTEGER. */
+static WOLFSSL_ASN1_INTEGER* test_tsp_serial_cb(WOLFSSL_TS_RESP_CTX* ctx,
+ void* data)
+{
+ WOLFSSL_ASN1_INTEGER* serial = wolfSSL_ASN1_INTEGER_new();
+
+ (void)ctx;
+ (void)data;
+ if (serial != NULL) {
+ wolfSSL_ASN1_INTEGER_set(serial, 0x1234);
+ }
+ return serial;
+}
+
+/* Serial callback returning a negative INTEGER - a serial number must be a
+ * non-negative number, so creating a response with this must fail. */
+static WOLFSSL_ASN1_INTEGER* test_tsp_serial_cb_neg(WOLFSSL_TS_RESP_CTX* ctx,
+ void* data)
+{
+ WOLFSSL_ASN1_INTEGER* serial = wolfSSL_ASN1_INTEGER_new();
+
+ (void)ctx;
+ (void)data;
+ if (serial != NULL) {
+ serial->data[0] = ASN_INTEGER;
+ serial->data[1] = 0x01;
+ serial->data[2] = 0x05;
+ serial->length = 3;
+ serial->negative = 1;
+ serial->type = WOLFSSL_V_ASN1_NEG_INTEGER;
+ }
+ return serial;
+}
+
+/* Time callback for the TS_RESP_CTX - returns a fixed time. */
+static int test_tsp_time_cb(WOLFSSL_TS_RESP_CTX* ctx, void* data, long* sec,
+ long* usec)
+{
+ (void)ctx;
+ (void)data;
+ /* 2026-06-04 12:00:00 UTC. */
+ *sec = 1780920000L;
+ if (usec != NULL)
+ *usec = 0;
+ return 1;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+/* Create a TS_REQ matching the test time-stamps. */
+static WOLFSSL_TS_REQ* test_tsp_create_req(void)
+{
+ EXPECT_DECLS;
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_MSG_IMPRINT* imprint = NULL;
+ WOLFSSL_X509_ALGOR* algo = NULL;
+ WOLFSSL_ASN1_INTEGER* nonce = NULL;
+
+ ExpectNotNull(req = TS_REQ_new());
+ ExpectIntEQ(TS_REQ_set_version(req, 1), 1);
+
+ /* Hash algorithm and message hash. */
+ ExpectNotNull(imprint = TS_MSG_IMPRINT_new());
+ ExpectNotNull(algo = X509_ALGOR_new());
+ if (EXPECT_SUCCESS()) {
+ ASN1_OBJECT_free(algo->algorithm);
+ algo->algorithm = OBJ_nid2obj(NID_sha256);
+ }
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(imprint, algo), 1);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(imprint, (unsigned char*)tsOsslHash,
+ (int)sizeof(tsOsslHash)), 1);
+ ExpectIntEQ(TS_REQ_set_msg_imprint(req, imprint), 1);
+
+ /* Nonce. */
+ ExpectNotNull(nonce = ASN1_INTEGER_new());
+ if (EXPECT_SUCCESS()) {
+ word32 i;
+ nonce->data[0] = ASN_INTEGER;
+ nonce->data[1] = (unsigned char)sizeof(tsOsslNonce);
+ for (i = 0; i < (word32)sizeof(tsOsslNonce); i++)
+ nonce->data[2 + i] = tsOsslNonce[i];
+ nonce->length = 2 + (int)sizeof(tsOsslNonce);
+ }
+ ExpectIntEQ(TS_REQ_set_nonce(req, nonce), 1);
+ ExpectIntEQ(TS_REQ_set_cert_req(req, 1), 1);
+
+ ASN1_INTEGER_free(nonce);
+ X509_ALGOR_free(algo);
+ TS_MSG_IMPRINT_free(imprint);
+
+ if (!EXPECT_SUCCESS()) {
+ TS_REQ_free(req);
+ req = NULL;
+ }
+ return req;
+}
+
+#endif /* TEST_OSSL_TSP */
+
+int test_wolfSSL_TS_REQ(void)
+{
+ EXPECT_DECLS;
+#ifdef TEST_OSSL_TSP
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_REQ* reqDec = NULL;
+ WOLFSSL_TS_MSG_IMPRINT* imprint = NULL;
+ unsigned char* der = NULL;
+ unsigned char buf[256];
+ unsigned char* p;
+ const unsigned char* cp;
+ int derSz = 0;
+ TspRequest wcReq;
+
+ ExpectNotNull(req = test_tsp_create_req());
+
+ /* Get length of encoding only. */
+ ExpectIntGT(derSz = i2d_TS_REQ(req, NULL), 0);
+ /* Allocating encode. */
+ ExpectIntEQ(i2d_TS_REQ(req, &der), derSz);
+ ExpectNotNull(der);
+ /* Encode into buffer - pointer moved on. */
+ p = buf;
+ ExpectIntEQ(i2d_TS_REQ(req, &p), derSz);
+ if (EXPECT_SUCCESS()) {
+ ExpectPtrEq(p, buf + derSz);
+ ExpectBufEQ(buf, der, derSz);
+ }
+
+ /* Check the encoding decodes at the wc level. */
+ ExpectIntEQ(wc_TspRequest_Decode(&wcReq, buf, (word32)derSz), 0);
+ ExpectIntEQ(wcReq.version, 1);
+ ExpectIntEQ(wcReq.imprint.hashAlgOID, SHA256h);
+ ExpectIntEQ(wcReq.certReq, 1);
+ ExpectIntEQ(wcReq.nonceSz, (word32)sizeof(tsOsslNonce));
+ ExpectBufEQ(wcReq.nonce, tsOsslNonce, (int)sizeof(tsOsslNonce));
+
+ /* Decode and check fields. */
+ cp = buf;
+ ExpectNotNull(reqDec = d2i_TS_REQ(NULL, &cp, derSz));
+ if (EXPECT_SUCCESS()) {
+ ExpectPtrEq(cp, buf + derSz);
+ }
+ ExpectIntEQ(TS_REQ_get_version(reqDec), 1);
+ ExpectIntEQ(TS_REQ_get_cert_req(reqDec), 1);
+ ExpectNotNull(TS_REQ_get_nonce(reqDec));
+ ExpectNull(TS_REQ_get_policy_id(reqDec));
+ ExpectNotNull(imprint = TS_REQ_get_msg_imprint(reqDec));
+ if (EXPECT_SUCCESS()) {
+ WOLFSSL_X509_ALGOR* algo = NULL;
+ WOLFSSL_ASN1_STRING* msg = NULL;
+
+ ExpectNotNull(algo = TS_MSG_IMPRINT_get_algo(imprint));
+ if (algo != NULL) {
+ ExpectIntEQ(OBJ_obj2nid(algo->algorithm), NID_sha256);
+ }
+ ExpectNotNull(msg = TS_MSG_IMPRINT_get_msg(imprint));
+ ExpectIntEQ(ASN1_STRING_length(msg), (int)sizeof(tsOsslHash));
+ if (msg != NULL) {
+ ExpectBufEQ(ASN1_STRING_data(msg), tsOsslHash,
+ (int)sizeof(tsOsslHash));
+ }
+ }
+
+ /* Decoding into an existing object frees and replaces it. The return is
+ * not assigned back to reqDec: d2i updates it through the &reqDec argument
+ * on success and leaves it pointing at the still-owned old object on
+ * failure - assigning a NULL return would orphan that object (a leak the
+ * memory-failure tests detect). */
+ cp = buf;
+ ExpectNotNull(d2i_TS_REQ(&reqDec, &cp, derSz));
+ ExpectIntEQ(TS_REQ_get_version(reqDec), 1);
+
+ /* Bad arguments. */
+ ExpectIntEQ(i2d_TS_REQ(NULL, NULL), -1);
+ cp = buf;
+ ExpectNull(d2i_TS_REQ(NULL, &cp, 2));
+ ExpectNull(d2i_TS_REQ(NULL, NULL, derSz));
+
+ /* Setting fields onto themselves does no work. */
+ ExpectIntEQ(TS_REQ_set_msg_imprint(req, TS_REQ_get_msg_imprint(req)), 1);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(TS_REQ_get_msg_imprint(req),
+ TS_MSG_IMPRINT_get_algo(TS_REQ_get_msg_imprint(req))), 1);
+
+ /* set_nonce rejects a malformed ASN1_INTEGER (no DER value bytes). */
+ {
+ WOLFSSL_ASN1_INTEGER* badNonce = NULL;
+ ExpectNotNull(badNonce = ASN1_INTEGER_new());
+ ExpectIntEQ(TS_REQ_set_nonce(req, badNonce), 0);
+ ASN1_INTEGER_free(badNonce);
+ }
+ /* i2d of a request with no message imprint fails to encode. */
+ {
+ WOLFSSL_TS_REQ* emptyReq = NULL;
+ ExpectNotNull(emptyReq = TS_REQ_new());
+ ExpectIntEQ(i2d_TS_REQ(emptyReq, NULL), -1);
+ TS_REQ_free(emptyReq);
+ }
+ /* A well-framed SEQUENCE that is not a valid TimeStampReq fails to
+ * decode (passes the outer length check but not wc_TspRequest_Decode). */
+ {
+ static const byte badReq[] = { 0x30, 0x03, 0x02, 0x01, 0x01 };
+ const unsigned char* bp = badReq;
+ ExpectNull(d2i_TS_REQ(NULL, &bp, (long)sizeof(badReq)));
+ }
+ /* set_algo rejects an algorithm OID that is not a known hash. */
+ {
+ WOLFSSL_TS_MSG_IMPRINT* mi = NULL;
+ WOLFSSL_X509_ALGOR* algo = NULL;
+ ExpectNotNull(mi = TS_MSG_IMPRINT_new());
+ ExpectNotNull(algo = X509_ALGOR_new());
+ if (EXPECT_SUCCESS()) {
+ ASN1_OBJECT_free(algo->algorithm);
+ algo->algorithm = OBJ_nid2obj(NID_commonName);
+ }
+ ExpectNotNull(algo->algorithm);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(mi, algo), 0);
+ X509_ALGOR_free(algo);
+ TS_MSG_IMPRINT_free(mi);
+ }
+ /* set_nonce rejects a nonce value longer than the maximum. */
+ {
+ WOLFSSL_ASN1_INTEGER bigNonce;
+ unsigned char nbuf[2 + MAX_TS_NONCE_SZ + 1];
+ XMEMSET(&bigNonce, 0, sizeof(bigNonce));
+ nbuf[0] = 0x02; /* INTEGER */
+ nbuf[1] = (unsigned char)(MAX_TS_NONCE_SZ + 1); /* > max, no pad */
+ XMEMSET(nbuf + 2, 0x55, MAX_TS_NONCE_SZ + 1);
+ bigNonce.data = nbuf;
+ bigNonce.length = 2 + MAX_TS_NONCE_SZ + 1;
+ ExpectIntEQ(TS_REQ_set_nonce(req, &bigNonce), 0);
+ }
+
+ XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL);
+ TS_REQ_free(reqDec);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_REQ_long_nonce(void)
+{
+ EXPECT_DECLS;
+#ifdef TEST_OSSL_TSP
+ WOLFSSL_TS_REQ* reqDec = NULL;
+ const WOLFSSL_ASN1_INTEGER* nonce = NULL;
+ TspRequest req;
+ byte longNonce[MAX_TS_NONCE_SZ];
+ byte enc[384];
+ word32 encSz = (word32)sizeof(enc);
+ byte buf[384];
+ unsigned char* p;
+ const unsigned char* cp;
+ word32 i;
+
+ /* A maximum length nonce - longer than an embedded ASN1_INTEGER. */
+ for (i = 0; i < (word32)sizeof(longNonce); i++)
+ longNonce[i] = (byte)(i + 1);
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ req.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(req.imprint.hash, tsOsslHash, sizeof(tsOsslHash));
+ req.imprint.hashSz = (word32)sizeof(tsOsslHash);
+ XMEMCPY(req.nonce, longNonce, sizeof(longNonce));
+ req.nonceSz = (word32)sizeof(longNonce);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz), 0);
+
+ /* Decode and check the nonce round trips. */
+ cp = enc;
+ ExpectNotNull(reqDec = d2i_TS_REQ(NULL, &cp, (long)encSz));
+ ExpectNotNull(nonce = TS_REQ_get_nonce(reqDec));
+ if (EXPECT_SUCCESS()) {
+ /* Type, one length byte then the number. */
+ ExpectIntEQ(nonce->length, 2 + (int)sizeof(longNonce));
+ ExpectBufEQ(nonce->data + 2, longNonce, (int)sizeof(longNonce));
+ }
+ p = buf;
+ ExpectIntEQ(i2d_TS_REQ(reqDec, &p), (int)encSz);
+ ExpectBufEQ(buf, enc, (int)encSz);
+
+ /* A negative nonce is rejected - the magnitude must not be used as if
+ * unsigned. */
+ {
+ WOLFSSL_TS_REQ* req2 = NULL;
+ WOLFSSL_ASN1_INTEGER* neg = NULL;
+
+ ExpectNotNull(req2 = TS_REQ_new());
+ ExpectNotNull(neg = ASN1_INTEGER_new());
+ if (EXPECT_SUCCESS()) {
+ neg->data[0] = ASN_INTEGER;
+ neg->data[1] = 0x01;
+ neg->data[2] = 0x05;
+ neg->length = 3;
+ neg->negative = 1;
+ neg->type = WOLFSSL_V_ASN1_NEG_INTEGER;
+ }
+ ExpectIntEQ(TS_REQ_set_nonce(req2, neg), 0);
+ /* The same magnitude as a positive nonce is accepted. */
+ if (EXPECT_SUCCESS()) {
+ neg->negative = 0;
+ neg->type = WOLFSSL_V_ASN1_INTEGER;
+ }
+ ExpectIntEQ(TS_REQ_set_nonce(req2, neg), 1);
+ /* A malformed length encoding is rejected. */
+ if (EXPECT_SUCCESS()) {
+ neg->data[1] = 0x82; /* long form: two length bytes follow... */
+ neg->length = 3; /* ...but the encoding is truncated. */
+ }
+ ExpectIntEQ(TS_REQ_set_nonce(req2, neg), 0);
+
+ ASN1_INTEGER_free(neg);
+ TS_REQ_free(req2);
+ }
+
+ TS_REQ_free(reqDec);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_TST_INFO* tstInfo = NULL;
+ WOLFSSL_TS_TST_INFO* tstInfoDec = NULL;
+ WOLFSSL_TS_MSG_IMPRINT* imprint = NULL;
+ WOLFSSL_TS_ACCURACY* accuracy = NULL;
+ const WOLFSSL_ASN1_INTEGER* num = NULL;
+ const WOLFSSL_ASN1_GENERALIZEDTIME* genTime = NULL;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ unsigned char* der = NULL;
+ const unsigned char* cp;
+ int derSz = 0;
+
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 1), TEST_SUCCESS);
+
+ /* Decode the response. */
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ /* Granted - no failure information or status string. */
+ ExpectIntEQ(ASN1_INTEGER_get(TS_STATUS_INFO_get0_status(
+ TS_RESP_get_status_info(resp))), TS_STATUS_GRANTED);
+ ExpectNull(TS_STATUS_INFO_get0_failure_info(
+ TS_RESP_get_status_info(resp)));
+ ExpectNull(TS_STATUS_INFO_get0_text(TS_RESP_get_status_info(resp)));
+
+ /* Rejection with a PKIFreeText of two strings - first one exposed. */
+ {
+ static const byte rejDer[] = {
+ 0x30, 0x12, 0x30, 0x10,
+ 0x02, 0x01, 0x02,
+ 0x30, 0x0b,
+ 0x0c, 0x03, 'a', 'b', 'c',
+ 0x0c, 0x04, 'd', 'e', 'f', 'g'
+ };
+ WOLFSSL_TS_RESP* rej = NULL;
+ const WOLF_STACK_OF(WOLFSSL_ASN1_STRING)* text = NULL;
+ WOLFSSL_ASN1_STRING* str = NULL;
+
+ cp = rejDer;
+ ExpectNotNull(rej = d2i_TS_RESP(NULL, &cp, (long)sizeof(rejDer)));
+ ExpectIntEQ(ASN1_INTEGER_get(TS_STATUS_INFO_get0_status(
+ TS_RESP_get_status_info(rej))), TS_STATUS_REJECTION);
+ ExpectNotNull(text = TS_STATUS_INFO_get0_text(
+ TS_RESP_get_status_info(rej)));
+ ExpectIntEQ(sk_ASN1_UTF8STRING_num(text), 1);
+ ExpectNotNull(str = (WOLFSSL_ASN1_STRING*)sk_ASN1_UTF8STRING_value(
+ text, 0));
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(ASN1_STRING_length(str), 3);
+ ExpectBufEQ(ASN1_STRING_data(str), "abc", 3);
+ }
+ TS_RESP_free(rej);
+ }
+
+ /* Encode is the same. */
+ ExpectIntEQ(derSz = i2d_TS_RESP(resp, &der), (int)respDerSz);
+ if (EXPECT_SUCCESS()) {
+ ExpectBufEQ(der, respDer, derSz);
+ }
+ XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL);
+ der = NULL;
+ /* i2d length-only (NULL) and into a caller buffer (pointer advances). */
+ {
+ unsigned char* wbuf = NULL;
+ unsigned char* q;
+ ExpectIntEQ(i2d_TS_RESP(resp, NULL), (int)respDerSz);
+ ExpectNotNull(wbuf = (unsigned char*)XMALLOC((size_t)respDerSz, NULL,
+ DYNAMIC_TYPE_OPENSSL));
+ if (wbuf != NULL) {
+ q = wbuf;
+ ExpectIntEQ(i2d_TS_RESP(resp, &q), (int)respDerSz);
+ ExpectPtrEq(q, wbuf + respDerSz);
+ ExpectBufEQ(wbuf, respDer, (int)respDerSz);
+ }
+ XFREE(wbuf, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+
+ /* Check the TSTInfo of the token. */
+ ExpectNotNull(tstInfo = TS_RESP_get_tst_info(resp));
+ ExpectIntEQ(TS_TST_INFO_get_version(tstInfo), 1);
+ ExpectNotNull(TS_TST_INFO_get_policy_id(tstInfo));
+ ExpectIntEQ(TS_TST_INFO_get_ordering(tstInfo), 0);
+ /* Serial number. */
+ ExpectNotNull(num = TS_TST_INFO_get_serial(tstInfo));
+ if (EXPECT_SUCCESS()) {
+ /* Top bit of first byte set - 0x00 pad keeps the INTEGER positive. */
+ ExpectIntEQ(num->length, 3 + (int)sizeof(tsOsslSerial));
+ ExpectIntEQ(num->data[2], 0x00);
+ ExpectBufEQ(num->data + 3, tsOsslSerial, (int)sizeof(tsOsslSerial));
+ }
+ /* Time of time-stamp. */
+ ExpectNotNull(genTime = TS_TST_INFO_get_time(tstInfo));
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(genTime->length, (int)sizeof(tsOsslGenTime) - 1);
+ ExpectBufEQ(genTime->data, tsOsslGenTime,
+ (int)sizeof(tsOsslGenTime) - 1);
+ }
+ /* Accuracy. */
+ ExpectNotNull(accuracy = TS_TST_INFO_get_accuracy(tstInfo));
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(ASN1_INTEGER_get(TS_ACCURACY_get_seconds(accuracy)), 1);
+ ExpectIntEQ(ASN1_INTEGER_get(TS_ACCURACY_get_millis(accuracy)), 500);
+ ExpectNull(TS_ACCURACY_get_micros(accuracy));
+ }
+ /* Nonce. */
+ ExpectNotNull(num = TS_TST_INFO_get_nonce(tstInfo));
+ if (EXPECT_SUCCESS()) {
+ /* Top bit of first byte set - 0x00 pad keeps the INTEGER positive. */
+ ExpectIntEQ(num->length, 3 + (int)sizeof(tsOsslNonce));
+ ExpectIntEQ(num->data[2], 0x00);
+ ExpectBufEQ(num->data + 3, tsOsslNonce, (int)sizeof(tsOsslNonce));
+ }
+ /* Message imprint. */
+ ExpectNotNull(imprint = TS_TST_INFO_get_msg_imprint(tstInfo));
+ if (EXPECT_SUCCESS()) {
+ WOLFSSL_X509_ALGOR* algo = NULL;
+ WOLFSSL_ASN1_STRING* msg = NULL;
+
+ ExpectNotNull(algo = TS_MSG_IMPRINT_get_algo(imprint));
+ if (algo != NULL) {
+ ExpectIntEQ(OBJ_obj2nid(algo->algorithm), NID_sha256);
+ }
+ ExpectNotNull(msg = TS_MSG_IMPRINT_get_msg(imprint));
+ if (msg != NULL) {
+ ExpectBufEQ(ASN1_STRING_data(msg), tsOsslHash,
+ (int)sizeof(tsOsslHash));
+ }
+ }
+
+ /* Encode and decode the TSTInfo. */
+ ExpectIntGT(derSz = i2d_TS_TST_INFO(tstInfo, &der), 0);
+ cp = der;
+ ExpectNotNull(tstInfoDec = d2i_TS_TST_INFO(NULL, &cp, derSz));
+ ExpectIntEQ(TS_TST_INFO_get_version(tstInfoDec), 1);
+ /* i2d length-only (NULL) and into a caller buffer (pointer advances). */
+ {
+ unsigned char* wbuf = NULL;
+ unsigned char* q;
+ ExpectIntEQ(i2d_TS_TST_INFO(tstInfo, NULL), derSz);
+ ExpectNotNull(wbuf = (unsigned char*)XMALLOC((size_t)derSz, NULL,
+ DYNAMIC_TYPE_OPENSSL));
+ if (wbuf != NULL) {
+ q = wbuf;
+ ExpectIntEQ(i2d_TS_TST_INFO(tstInfo, &q), derSz);
+ ExpectPtrEq(q, wbuf + derSz);
+ }
+ XFREE(wbuf, NULL, DYNAMIC_TYPE_OPENSSL);
+ }
+ /* Decoding into an existing TSTInfo frees and replaces it. The return is
+ * not assigned back - d2i updates tstInfoDec through the &tstInfoDec
+ * argument and leaves it valid on failure (see d2i_TS_REQ above). */
+ cp = der;
+ ExpectNotNull(d2i_TS_TST_INFO(&tstInfoDec, &cp, derSz));
+ ExpectIntEQ(TS_TST_INFO_get_version(tstInfoDec), 1);
+ XFREE(der, NULL, DYNAMIC_TYPE_OPENSSL);
+
+ /* Decoding into an existing response frees and replaces it (tstInfo and
+ * the other views above reference resp and must not be used afterward).
+ * The return is not assigned back - d2i updates resp through &resp and
+ * leaves it valid on failure (see d2i_TS_REQ above). */
+ cp = respDer;
+ ExpectNotNull(d2i_TS_RESP(&resp, &cp, (long)respDerSz));
+
+ TS_TST_INFO_free(tstInfoDec);
+ TS_RESP_free(resp);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_verify_response(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ WOLFSSL_X509_STORE* store = NULL;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+ unsigned char* imprint = NULL;
+
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 1), TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+
+ /* Verification context out of the request sent. */
+ ExpectNotNull(req = test_tsp_create_req());
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ /* No trust store set - the signer cannot be anchored so verification
+ * fails closed even though the token's signature is valid. */
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ /* An empty store - the signer's certificate is not trusted - fails. */
+ ExpectNotNull(store = wolfSSL_X509_STORE_new());
+ ExpectNotNull(TS_VERIFY_CTX_set_store(ctx, store));
+ store = NULL;
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ /* Trust the signer's certificate - verification succeeds. */
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+
+ /* Check a different message imprint fails. The trusted store stays set so
+ * the imprint check - not the signer check - is what rejects it. */
+ ExpectNotNull(imprint = (unsigned char*)XMALLOC(sizeof(tsOsslHash), NULL,
+ DYNAMIC_TYPE_OPENSSL));
+ if (EXPECT_SUCCESS()) {
+ XMEMCPY(imprint, tsOsslHash, sizeof(tsOsslHash));
+ imprint[0] ^= 0x80;
+ ExpectNotNull(TS_VERIFY_CTX_set_imprint(ctx, imprint,
+ (long)sizeof(tsOsslHash)));
+ imprint = NULL;
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+ }
+
+ /* Data check enabled but no data BIO set - verification fails. */
+ TS_VERIFY_CTX_add_flags(ctx, TS_VFY_DATA);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ ctx = NULL;
+
+ /* Check a response without a nonce fails the nonce check. */
+ respDerSz = (word32)sizeof(respDer);
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 0), TEST_SUCCESS);
+ TS_RESP_free(resp);
+ resp = NULL;
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ /* Trust the signer so the nonce check - not the signer check - rejects. */
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+ TS_VERIFY_CTX_free(ctx);
+ ctx = NULL;
+ TS_RESP_free(resp);
+ resp = NULL;
+
+ /* A granted response with no time-stamp token fails verification. */
+ {
+ static const byte grantedNoTokenDer[] = {
+ 0x30, 0x05, /* TimeStampResp */
+ 0x30, 0x03, /* PKIStatusInfo */
+ 0x02, 0x01, 0x00 /* status granted (0) - no token */
+ };
+ cp = grantedNoTokenDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp,
+ (long)sizeof(grantedNoTokenDer)));
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+ }
+ TS_VERIFY_CTX_free(ctx);
+ ctx = NULL;
+ TS_RESP_free(resp);
+ resp = NULL;
+
+ /* A granted response whose token signature is corrupt fails to verify -
+ * exercises the wc_TspTstInfo_VerifyWithPKCS7 failure path. The flipped byte
+ * is well inside the trailing RSA signature so the response framing still
+ * decodes but the signature does not verify. */
+ respDerSz = (word32)sizeof(respDer);
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 1), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ respDer[respDerSz - 16] ^= 0xFF;
+ }
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ TS_RESP_free(resp);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_verify_response_chain(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ WOLFSSL_X509_STORE* store = NULL;
+ WOLFSSL_X509* rootX509 = NULL;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+
+ /* A response whose token is signed by an intermediate-issued TSA and
+ * carries the intermediate certificate. */
+ ExpectIntEQ(test_tsp_create_resp_chain(respDer, &respDerSz), TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+
+ ExpectNotNull(req = test_tsp_create_req());
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+
+ /* Trusting only the intermediate-issued leaf's root is enough: the token
+ * carries the intermediate, so the signer chains leaf -> intermediate ->
+ * root and verifies. */
+ cp = ca_cert_der_2048;
+ ExpectNotNull(rootX509 = wolfSSL_d2i_X509(NULL, &cp,
+ sizeof_ca_cert_der_2048));
+ ExpectNotNull(store = wolfSSL_X509_STORE_new());
+ ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, rootX509), 1);
+ wolfSSL_X509_free(rootX509);
+ rootX509 = NULL;
+ /* set_store takes ownership, but a failed Expect above short-circuits it -
+ * free the store in that case so an allocation-failure path does not leak
+ * the store. */
+ if (EXPECT_SUCCESS()) {
+ ExpectNotNull(TS_VERIFY_CTX_set_store(ctx, store));
+ }
+ else {
+ wolfSSL_X509_STORE_free(store);
+ }
+ store = NULL;
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+
+ /* An empty store does not trust the chain - verification fails. */
+ ExpectNotNull(store = wolfSSL_X509_STORE_new());
+ ExpectNotNull(TS_VERIFY_CTX_set_store(ctx, store));
+ store = NULL;
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ TS_RESP_free(resp);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_VerifyWithCm(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ TspResponse resp;
+ TspTstInfo tst;
+ WOLFSSL_CERT_MANAGER* cm = NULL;
+ WOLFSSL_CERT_MANAGER* emptyCm = NULL;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+
+ /* A response whose token is signed by the intermediate-issued TSA. */
+ ExpectIntEQ(test_tsp_create_resp_chain(respDer, &respDerSz), TEST_SUCCESS);
+ ExpectIntEQ(wc_TspResponse_Decode(&resp, respDer, respDerSz), 0);
+
+ /* A manager trusting the root and holding the intermediate CA - the
+ * signer (issued by the intermediate) chains to the trusted root. */
+ ExpectNotNull(cm = wolfSSL_CertManagerNew());
+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_cert_der_2048,
+ (long)sizeof_ca_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
+ WOLFSSL_SUCCESS);
+ ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, ca_int_cert_der_2048,
+ (long)sizeof_ca_int_cert_der_2048, WOLFSSL_FILETYPE_ASN1),
+ WOLFSSL_SUCCESS);
+ ExpectIntEQ(wc_TspResponse_VerifyWithCm(&resp, cm, &tst), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspResponse_VerifyWithCm(NULL, cm, &tst),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspResponse_VerifyWithCm(&resp, NULL, &tst),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* An empty manager does not trust the signer. */
+ ExpectNotNull(emptyCm = wolfSSL_CertManagerNew());
+ ExpectIntEQ(wc_TspResponse_VerifyWithCm(&resp, emptyCm, &tst),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+
+ /* The TSTInfo is optional. */
+ ExpectIntEQ(wc_TspResponse_VerifyWithCm(&resp, cm, NULL), 0);
+
+ wolfSSL_CertManagerFree(cm);
+ wolfSSL_CertManagerFree(emptyCm);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_verify_data(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ WOLFSSL_BIO* bio = NULL;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ TspResponse wcResp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ static const byte data[] = "wolfSSL RFC 3161 time-stamp data";
+ byte dataHash[WC_SHA256_DIGEST_SIZE];
+ const unsigned char* cp;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* The hash of the data is the token's message imprint. */
+ ExpectIntEQ(wc_Sha256Hash(data, (word32)sizeof(data) - 1, dataHash), 0);
+
+ /* Build a granted response over the hash of the data. */
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ tst.policy = tsOsslPolicy;
+ tst.policySz = (word32)sizeof(tsOsslPolicy);
+ tst.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(tst.imprint.hash, dataHash, sizeof(dataHash));
+ tst.imprint.hashSz = (word32)sizeof(dataHash);
+ tst.serial = tsOsslSerial;
+ tst.serialSz = (word32)sizeof(tsOsslSerial);
+ tst.genTime = tsOsslGenTime;
+ tst.genTimeSz = (word32)sizeof(tsOsslGenTime) - 1;
+
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048), 0);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->rng = &rng;
+ pkcs7->hashOID = SHA256h;
+ pkcs7->encryptOID = RSAk;
+ pkcs7->privateKey = (byte*)tsa_key_der_2048;
+ pkcs7->privateKeySz = sizeof_tsa_key_der_2048;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ ExpectIntEQ(wc_TspResponse_Init(&wcResp), 0);
+ wcResp.status = WC_TSP_PKISTATUS_GRANTED;
+ wcResp.token = token;
+ wcResp.tokenSz = tokenSz;
+ ExpectIntEQ(wc_TspResponse_Encode(&wcResp, respDer, &respDerSz), 0);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+
+ /* Verify against the data - the library hashes it and checks the imprint,
+ * so the caller does not pre-compute the hash. */
+ ExpectNotNull(ctx = TS_VERIFY_CTX_new());
+ ExpectIntEQ(TS_VERIFY_CTX_set_flags(ctx, TS_VFY_DATA | TS_VFY_SIGNER),
+ TS_VFY_DATA | TS_VFY_SIGNER);
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectNotNull(bio = BIO_new_mem_buf(data, (int)sizeof(data) - 1));
+ ExpectNotNull(TS_VERIFY_CTX_set_data(ctx, bio));
+ bio = NULL; /* The context owns the BIO now. */
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+
+ /* Different data does not hash to the imprint - verification fails. */
+ ExpectNotNull(bio = BIO_new_mem_buf("not the time-stamped data", 25));
+ ExpectNotNull(TS_VERIFY_CTX_set_data(ctx, bio));
+ bio = NULL;
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ /* TS_VFY_DATA with no data set fails - clearing returns NULL. */
+ ExpectNull(TS_VERIFY_CTX_set_data(ctx, NULL));
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ TS_RESP_free(resp);
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_TST_INFO_get_tsa(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ WOLFSSL_TS_TST_INFO* tstInfo = NULL;
+ WOLFSSL_GENERAL_NAME* gn = NULL;
+ byte der[512];
+ word32 derSz = (word32)sizeof(der);
+ const unsigned char* cp;
+ /* GeneralName dNSName [2] "tsa.wolfssl.com". */
+ static const byte tsaName[] = {
+ 0x82, 0x0f, 't', 's', 'a', '.', 'w', 'o', 'l', 'f', 's', 's', 'l',
+ '.', 'c', 'o', 'm'
+ };
+
+ /* Build a TSTInfo carrying a dNSName TSA name and encode it. */
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ tst.policy = tsOsslPolicy;
+ tst.policySz = (word32)sizeof(tsOsslPolicy);
+ tst.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(tst.imprint.hash, tsOsslHash, sizeof(tsOsslHash));
+ tst.imprint.hashSz = (word32)sizeof(tsOsslHash);
+ tst.serial = tsOsslSerial;
+ tst.serialSz = (word32)sizeof(tsOsslSerial);
+ tst.genTime = tsOsslGenTime;
+ tst.genTimeSz = (word32)sizeof(tsOsslGenTime) - 1;
+ tst.tsa = tsaName;
+ tst.tsaSz = (word32)sizeof(tsaName);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, der, &derSz), 0);
+
+ cp = der;
+ ExpectNotNull(tstInfo = d2i_TS_TST_INFO(NULL, &cp, (long)derSz));
+
+ /* get_tsa builds the GeneralName - a dNSName with the expected value. */
+ ExpectNotNull(gn = TS_TST_INFO_get_tsa(tstInfo));
+ if (gn != NULL) {
+ ExpectIntEQ(gn->type, GEN_DNS);
+ ExpectIntEQ(ASN1_STRING_length(gn->d.dNSName),
+ (int)sizeof(tsaName) - 2);
+ ExpectIntEQ(XMEMCMP(ASN1_STRING_data(gn->d.dNSName), tsaName + 2,
+ sizeof(tsaName) - 2), 0);
+ }
+ /* A second get returns the same cached object. */
+ ExpectPtrEq(TS_TST_INFO_get_tsa(tstInfo), gn);
+
+ /* NULL argument returns NULL. */
+ ExpectNull(TS_TST_INFO_get_tsa(NULL));
+
+ TS_TST_INFO_free(tstInfo);
+ tstInfo = NULL;
+
+ /* A directoryName [4] form is returned as a GEN_DIRNAME. The name is
+ * RDNSequence { RDN { commonName "ts" } }. */
+ {
+ static const byte dirName[] = {
+ 0xa4, 0x0f, 0x30, 0x0d, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x02, 't', 's'
+ };
+
+ derSz = (word32)sizeof(der);
+ tst.tsa = dirName;
+ tst.tsaSz = (word32)sizeof(dirName);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, der, &derSz), 0);
+ cp = der;
+ ExpectNotNull(tstInfo = d2i_TS_TST_INFO(NULL, &cp, (long)derSz));
+ ExpectNotNull(gn = TS_TST_INFO_get_tsa(tstInfo));
+ if (gn != NULL) {
+ ExpectIntEQ(gn->type, GEN_DIRNAME);
+ }
+ TS_TST_INFO_free(tstInfo);
+ tstInfo = NULL;
+ }
+
+ /* An unsupported GeneralName form - iPAddress [7] - returns NULL. */
+ {
+ static const byte ipName[] = {
+ 0x87, 0x04, 0x7f, 0x00, 0x00, 0x01
+ };
+
+ derSz = (word32)sizeof(der);
+ tst.tsa = ipName;
+ tst.tsaSz = (word32)sizeof(ipName);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, der, &derSz), 0);
+ cp = der;
+ ExpectNotNull(tstInfo = d2i_TS_TST_INFO(NULL, &cp, (long)derSz));
+ ExpectNull(TS_TST_INFO_get_tsa(tstInfo));
+ TS_TST_INFO_free(tstInfo);
+ }
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_CTX(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_RESP_CTX* ctx = NULL;
+ WOLFSSL_X509* signer = NULL;
+ WOLFSSL_EVP_PKEY* key = NULL;
+ WOLFSSL_ASN1_OBJECT* policy = NULL;
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_BIO* reqBio = NULL;
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_TST_INFO* tstInfo = NULL;
+ WOLFSSL_TS_MSG_IMPRINT* imprint = NULL;
+ unsigned char* reqDer = NULL;
+ int reqDerSz = 0;
+ const unsigned char* cp;
+ /* 1.3.6.1.4.1.999.1 - the test TSA policy as an OID object. */
+ static const byte policyObj[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+ };
+
+ /* Load the TSA signer certificate and key. */
+ cp = tsa_cert_der_2048;
+ ExpectNotNull(signer = wolfSSL_d2i_X509(NULL, &cp, sizeof_tsa_cert_der_2048));
+ cp = tsa_key_der_2048;
+ ExpectNotNull(key = wolfSSL_d2i_PrivateKey(EVP_PKEY_RSA, NULL, &cp,
+ (long)sizeof_tsa_key_der_2048));
+
+ /* Build the responder context. */
+ ExpectNotNull(ctx = TS_RESP_CTX_new());
+ ExpectIntEQ(TS_RESP_CTX_set_signer_cert(ctx, signer), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_key(ctx, key), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_digest(ctx, EVP_sha256()), 1);
+ if (EXPECT_SUCCESS()) {
+ const unsigned char* pp = policyObj;
+ policy = wolfSSL_c2i_ASN1_OBJECT(NULL, &pp, (long)sizeof(policyObj));
+ }
+ ExpectNotNull(policy);
+ ExpectIntEQ(TS_RESP_CTX_set_def_policy(ctx, policy), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_serial_cb(ctx, test_tsp_serial_cb, NULL), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_accuracy(ctx, 1, 0, 0), 1);
+
+ /* A request from a client. */
+ ExpectNotNull(req = test_tsp_create_req());
+ ExpectIntGT(reqDerSz = i2d_TS_REQ(req, &reqDer), 0);
+ ExpectNotNull(reqBio = BIO_new_mem_buf(reqDer, reqDerSz));
+
+ /* Create the response. */
+ ExpectNotNull(resp = TS_RESP_create_response(ctx, reqBio));
+
+ /* The response is granted and the TSTInfo echoes the request imprint. */
+ ExpectNotNull(tstInfo = TS_RESP_get_tst_info(resp));
+ ExpectIntEQ(TS_TST_INFO_get_version(tstInfo), 1);
+ ExpectNotNull(imprint = TS_TST_INFO_get_msg_imprint(tstInfo));
+ if (imprint != NULL) {
+ ExpectBufEQ(ASN1_STRING_data(TS_MSG_IMPRINT_get_msg(imprint)),
+ tsOsslHash, (int)sizeof(tsOsslHash));
+ }
+
+ TS_RESP_free(resp);
+ resp = NULL;
+ BIO_free(reqBio);
+ reqBio = NULL;
+
+ /* Re-create with a time callback and the ordering flag set. */
+ ExpectIntEQ(TS_RESP_CTX_set_time_cb(ctx, test_tsp_time_cb, NULL), 1);
+ ExpectIntEQ(TS_RESP_CTX_add_flags(ctx, TS_ORDERING), 1);
+ ExpectNotNull(reqBio = BIO_new_mem_buf(reqDer, reqDerSz));
+ ExpectNotNull(resp = TS_RESP_create_response(ctx, reqBio));
+ ExpectNotNull(tstInfo = TS_RESP_get_tst_info(resp));
+ ExpectIntEQ(TS_TST_INFO_get_ordering(tstInfo), 1);
+
+ /* Bad arguments - create with NULL, and each setter rejects a NULL ctx. */
+ ExpectNull(TS_RESP_create_response(NULL, reqBio));
+ ExpectNull(TS_RESP_create_response(ctx, NULL));
+ ExpectIntEQ(TS_RESP_CTX_set_signer_cert(NULL, signer), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_cert(ctx, NULL), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_key(NULL, key), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_key(ctx, NULL), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_digest(NULL, EVP_sha256()), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_digest(ctx, NULL), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_def_policy(NULL, policy), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_def_policy(ctx, NULL), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_serial_cb(NULL, test_tsp_serial_cb, NULL), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_time_cb(NULL, test_tsp_time_cb, NULL), 0);
+ ExpectIntEQ(TS_RESP_CTX_set_accuracy(NULL, 1, 0, 0), 0);
+ ExpectIntEQ(TS_RESP_CTX_add_flags(NULL, TS_ORDERING), 0);
+
+ /* A context missing the serial callback cannot create a response. */
+ {
+ WOLFSSL_TS_RESP_CTX* ctx2 = NULL;
+ WOLFSSL_BIO* bio2 = NULL;
+
+ ExpectNotNull(ctx2 = TS_RESP_CTX_new());
+ ExpectIntEQ(TS_RESP_CTX_set_signer_cert(ctx2, signer), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_key(ctx2, key), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_def_policy(ctx2, policy), 1);
+ ExpectNotNull(bio2 = BIO_new_mem_buf(reqDer, reqDerSz));
+ ExpectNull(TS_RESP_create_response(ctx2, bio2));
+ BIO_free(bio2);
+ TS_RESP_CTX_free(ctx2);
+ }
+
+ /* A malformed request does not decode - no response is created. */
+ {
+ static const byte badReq[] = { 0x30, 0x03, 0x02, 0x01, 0x01 };
+ WOLFSSL_BIO* badBio = NULL;
+
+ ExpectNotNull(badBio = BIO_new_mem_buf(badReq, (int)sizeof(badReq)));
+ ExpectNull(TS_RESP_create_response(ctx, badBio));
+ BIO_free(badBio);
+ }
+
+ /* A serial callback returning a negative INTEGER is rejected - no response
+ * is created. */
+ {
+ WOLFSSL_BIO* negBio = NULL;
+
+ ExpectIntEQ(TS_RESP_CTX_set_serial_cb(ctx, test_tsp_serial_cb_neg,
+ NULL), 1);
+ ExpectNotNull(negBio = BIO_new_mem_buf(reqDer, reqDerSz));
+ ExpectNull(TS_RESP_create_response(ctx, negBio));
+ BIO_free(negBio);
+ /* Restore the valid serial callback. */
+ ExpectIntEQ(TS_RESP_CTX_set_serial_cb(ctx, test_tsp_serial_cb, NULL), 1);
+ }
+
+ TS_RESP_free(resp);
+ BIO_free(reqBio);
+ XFREE(reqDer, NULL, DYNAMIC_TYPE_OPENSSL);
+ TS_REQ_free(req);
+ ASN1_OBJECT_free(policy);
+ TS_RESP_CTX_free(ctx);
+ wolfSSL_EVP_PKEY_free(key);
+ wolfSSL_X509_free(signer);
+
+ /* An ECDSA signer is also supported. */
+#ifdef HAVE_ECC
+ {
+ WOLFSSL_TS_RESP_CTX* eccCtx = NULL;
+ WOLFSSL_X509* eccSigner = NULL;
+ WOLFSSL_EVP_PKEY* eccKey = NULL;
+ WOLFSSL_ASN1_OBJECT* eccPolicy = NULL;
+ WOLFSSL_TS_REQ* eccReq = NULL;
+ WOLFSSL_BIO* eccBio = NULL;
+ WOLFSSL_TS_RESP* eccResp = NULL;
+ unsigned char* eccReqDer = NULL;
+ int eccReqDerSz = 0;
+
+ cp = tsa_ecc_cert_der_256;
+ ExpectNotNull(eccSigner = wolfSSL_d2i_X509(NULL, &cp,
+ sizeof_tsa_ecc_cert_der_256));
+ cp = tsa_ecc_key_der_256;
+ ExpectNotNull(eccKey = wolfSSL_d2i_PrivateKey(EVP_PKEY_EC, NULL, &cp,
+ (long)sizeof_tsa_ecc_key_der_256));
+
+ ExpectNotNull(eccCtx = TS_RESP_CTX_new());
+ ExpectIntEQ(TS_RESP_CTX_set_signer_cert(eccCtx, eccSigner), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_signer_key(eccCtx, eccKey), 1);
+ if (EXPECT_SUCCESS()) {
+ const unsigned char* pp = policyObj;
+ eccPolicy = wolfSSL_c2i_ASN1_OBJECT(NULL, &pp,
+ (long)sizeof(policyObj));
+ }
+ ExpectIntEQ(TS_RESP_CTX_set_def_policy(eccCtx, eccPolicy), 1);
+ ExpectIntEQ(TS_RESP_CTX_set_serial_cb(eccCtx, test_tsp_serial_cb,
+ NULL), 1);
+
+ ExpectNotNull(eccReq = test_tsp_create_req());
+ ExpectIntGT(eccReqDerSz = i2d_TS_REQ(eccReq, &eccReqDer), 0);
+ ExpectNotNull(eccBio = BIO_new_mem_buf(eccReqDer, eccReqDerSz));
+ ExpectNotNull(eccResp = TS_RESP_create_response(eccCtx, eccBio));
+
+ TS_RESP_free(eccResp);
+ BIO_free(eccBio);
+ XFREE(eccReqDer, NULL, DYNAMIC_TYPE_OPENSSL);
+ TS_REQ_free(eccReq);
+ ASN1_OBJECT_free(eccPolicy);
+ TS_RESP_CTX_free(eccCtx);
+ wolfSSL_EVP_PKEY_free(eccKey);
+ wolfSSL_X509_free(eccSigner);
+ }
+#endif /* HAVE_ECC */
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_verify_token(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(OPENSSL_ALL) && \
+ defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ WOLFSSL_PKCS7* token = NULL;
+ TspResponse wcResp;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 1), TEST_SUCCESS);
+ /* Get the time-stamp token out of the response. d2i_PKCS7 returns the
+ * extended WOLFSSL_PKCS7 object that TS_RESP_verify_token requires. */
+ ExpectIntEQ(wc_TspResponse_Decode(&wcResp, respDer, respDerSz), 0);
+ cp = wcResp.token;
+ ExpectNotNull(token = (WOLFSSL_PKCS7*)d2i_PKCS7(NULL, &cp,
+ (int)wcResp.tokenSz));
+
+ /* Verification context out of the request sent. */
+ ExpectNotNull(req = test_tsp_create_req());
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+
+ /* Bad arguments. */
+ ExpectIntEQ(TS_RESP_verify_token(NULL, token), 0);
+ ExpectIntEQ(TS_RESP_verify_token(ctx, NULL), 0);
+
+ /* Trust the signer's certificate so verification can be anchored. */
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_token(ctx, token), 1);
+
+ /* Data check enabled but no data BIO set - verification fails. */
+ TS_VERIFY_CTX_add_flags(ctx, TS_VFY_DATA);
+ ExpectIntEQ(TS_RESP_verify_token(ctx, token), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ TS_REQ_free(req);
+ PKCS7_free((PKCS7*)token);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_REQ_policy_id(void)
+{
+ EXPECT_DECLS;
+#ifdef TEST_OSSL_TSP
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_REQ* reqDec = NULL;
+ WOLFSSL_ASN1_OBJECT* policy = NULL;
+ unsigned char buf[256];
+ unsigned char* p;
+ const unsigned char* cp;
+ int derSz = 0;
+
+ ExpectNotNull(req = test_tsp_create_req());
+ /* No policy set on a fresh request. */
+ ExpectNull(TS_REQ_get_policy_id(req));
+
+ ExpectNotNull(policy = OBJ_nid2obj(NID_sha256));
+
+ /* Bad arguments. */
+ ExpectIntEQ(TS_REQ_set_policy_id(NULL, policy), 0);
+ ExpectIntEQ(TS_REQ_set_policy_id(req, NULL), 0);
+ /* A policy OID content longer than MAX_OID_SZ is rejected. The bytes do
+ * not start with an OBJECT IDENTIFIER tag, so they are taken as content. */
+ {
+ WOLFSSL_ASN1_OBJECT bigPolicy;
+ unsigned char bigOid[MAX_OID_SZ + 1];
+ XMEMSET(&bigPolicy, 0, sizeof(bigPolicy));
+ XMEMSET(bigOid, 0x2a, sizeof(bigOid));
+ bigPolicy.obj = bigOid;
+ bigPolicy.objSz = (unsigned int)sizeof(bigOid);
+ ExpectIntEQ(TS_REQ_set_policy_id(req, &bigPolicy), 0);
+ }
+
+ /* Set the policy and read it back. */
+ ExpectIntEQ(TS_REQ_set_policy_id(req, policy), 1);
+ ExpectIntEQ(OBJ_obj2nid(TS_REQ_get_policy_id(req)), NID_sha256);
+
+ /* The policy is encoded and round trips through decode. */
+ ExpectIntGT(derSz = i2d_TS_REQ(req, NULL), 0);
+ p = buf;
+ ExpectIntEQ(i2d_TS_REQ(req, &p), derSz);
+ cp = buf;
+ ExpectNotNull(reqDec = d2i_TS_REQ(NULL, &cp, derSz));
+ ExpectIntEQ(OBJ_obj2nid(TS_REQ_get_policy_id(reqDec)), NID_sha256);
+
+ ASN1_OBJECT_free(policy);
+ TS_REQ_free(reqDec);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_VERIFY_CTX(void)
+{
+ EXPECT_DECLS;
+#ifdef TEST_OSSL_TSP
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+
+ ExpectNotNull(ctx = TS_VERIFY_CTX_new());
+
+ /* set_flags returns the new flag set; add_flags ORs more in. */
+ ExpectIntEQ(TS_VERIFY_CTX_set_flags(ctx, TS_VFY_VERSION), TS_VFY_VERSION);
+ ExpectIntEQ(TS_VERIFY_CTX_add_flags(ctx, TS_VFY_NONCE),
+ TS_VFY_VERSION | TS_VFY_NONCE);
+ /* set_flags replaces - not ORs - the flags. */
+ ExpectIntEQ(TS_VERIFY_CTX_set_flags(ctx, TS_VFY_IMPRINT), TS_VFY_IMPRINT);
+
+ /* Bad arguments. */
+ ExpectIntEQ(TS_VERIFY_CTX_set_flags(NULL, TS_VFY_VERSION), 0);
+ ExpectIntEQ(TS_VERIFY_CTX_add_flags(NULL, TS_VFY_VERSION), 0);
+ ExpectNull(TS_VERIFY_CTX_set_store(NULL, NULL));
+ ExpectNull(TS_VERIFY_CTX_set_imprint(NULL, NULL, 0));
+ ExpectNull(TS_REQ_to_TS_VERIFY_CTX(NULL, NULL));
+ ExpectIntEQ(TS_RESP_verify_response(NULL, NULL), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ /* Freeing NULL is safe. */
+ TS_VERIFY_CTX_free(NULL);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_STATUS_INFO_failure_info(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_RESP* resp = NULL;
+ const WOLFSSL_ASN1_BIT_STRING* failInfo = NULL;
+ TspResponse wcResp;
+ byte respDer[64];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+ /* BAD_ALG (top bit) plus SYSTEM_FAILURE (bit 25) - spans four bytes so
+ * the trailing non-zero byte is kept. */
+ static const byte expFailInfo[] = { 0x80, 0x00, 0x00, 0x40 };
+
+ /* Build a rejection response with failure information. */
+ ExpectIntEQ(wc_TspResponse_Init(&wcResp), 0);
+ wcResp.status = WC_TSP_PKISTATUS_REJECTION;
+ wcResp.failInfo = WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_SYSTEM_FAILURE;
+ ExpectIntEQ(wc_TspResponse_Encode(&wcResp, respDer, &respDerSz), 0);
+
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectIntEQ(ASN1_INTEGER_get(TS_STATUS_INFO_get0_status(
+ TS_RESP_get_status_info(resp))), TS_STATUS_REJECTION);
+
+ /* Failure information is exposed as a BIT STRING. */
+ ExpectNotNull(failInfo = TS_STATUS_INFO_get0_failure_info(
+ TS_RESP_get_status_info(resp)));
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(failInfo->length, (int)sizeof(expFailInfo));
+ ExpectBufEQ(failInfo->data, expFailInfo, (int)sizeof(expFailInfo));
+ }
+
+ TS_RESP_free(resp);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_accuracy_ordering(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_TST_INFO* tstInfo = NULL;
+ WOLFSSL_TS_ACCURACY* accuracy = NULL;
+ TsRespOpts opts;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+
+ /* A token with a microseconds accuracy and the ordering flag set. */
+ XMEMSET(&opts, 0, sizeof(opts));
+ opts.status = WC_TSP_PKISTATUS_GRANTED;
+ opts.withMicros = 1;
+ opts.ordering = 1;
+ ExpectIntEQ(test_tsp_create_resp_ex(respDer, &respDerSz, &opts),
+ TEST_SUCCESS);
+
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectNotNull(tstInfo = TS_RESP_get_tst_info(resp));
+
+ /* Ordering flag is reported as set. */
+ ExpectIntEQ(TS_TST_INFO_get_ordering(tstInfo), 1);
+
+ /* Microseconds accuracy is present alongside seconds and milliseconds. */
+ ExpectNotNull(accuracy = TS_TST_INFO_get_accuracy(tstInfo));
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(ASN1_INTEGER_get(TS_ACCURACY_get_seconds(accuracy)), 1);
+ ExpectIntEQ(ASN1_INTEGER_get(TS_ACCURACY_get_millis(accuracy)), 500);
+ ExpectNotNull(TS_ACCURACY_get_micros(accuracy));
+ ExpectIntEQ(ASN1_INTEGER_get(TS_ACCURACY_get_micros(accuracy)), 250);
+ }
+
+ TS_RESP_free(resp);
+ resp = NULL;
+
+ /* A token with no accuracy at all - the accuracy is optional and
+ * TS_TST_INFO_get_accuracy reports it absent. */
+ respDerSz = (word32)sizeof(respDer);
+ XMEMSET(&opts, 0, sizeof(opts));
+ opts.status = WC_TSP_PKISTATUS_GRANTED;
+ opts.noAccuracy = 1;
+ ExpectIntEQ(test_tsp_create_resp_ex(respDer, &respDerSz, &opts),
+ TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectNotNull(tstInfo = TS_RESP_get_tst_info(resp));
+ ExpectNull(TS_TST_INFO_get_accuracy(tstInfo));
+
+ TS_RESP_free(resp);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_verify_status(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ TsRespOpts opts;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+
+ ExpectNotNull(req = test_tsp_create_req());
+
+ /* "Granted with mods" is an accepted status. */
+ XMEMSET(&opts, 0, sizeof(opts));
+ opts.status = WC_TSP_PKISTATUS_GRANTED_WITH_MODS;
+ opts.withNonce = 1;
+ ExpectIntEQ(test_tsp_create_resp_ex(respDer, &respDerSz, &opts),
+ TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+ TS_VERIFY_CTX_free(ctx);
+ ctx = NULL;
+ TS_RESP_free(resp);
+ resp = NULL;
+
+ /* A rejection is not granted - verification fails on status. */
+ respDerSz = (word32)sizeof(respDer);
+ XMEMSET(&opts, 0, sizeof(opts));
+ opts.status = WC_TSP_PKISTATUS_REJECTION;
+ opts.withNonce = 1;
+ ExpectIntEQ(test_tsp_create_resp_ex(respDer, &respDerSz, &opts),
+ TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ TS_VERIFY_CTX_free(ctx);
+ TS_RESP_free(resp);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_RESP_verify_policy(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ WOLFSSL_ASN1_OBJECT* otherPolicy = NULL;
+ WOLFSSL_ASN1_OBJECT policy;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+
+ /* An ASN1_OBJECT referencing the raw OID content of the test policy. */
+ XMEMSET(&policy, 0, sizeof(policy));
+ policy.obj = tsOsslPolicy;
+ policy.objSz = (unsigned int)sizeof(tsOsslPolicy);
+
+ /* A granted response carrying the test TSA policy. */
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 1), TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+
+ /* Request with the matching policy - context carries it and the
+ * TS_VFY_POLICY check passes. */
+ ExpectNotNull(req = test_tsp_create_req());
+ ExpectIntEQ(TS_REQ_set_policy_id(req, &policy), 1);
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+ TS_VERIFY_CTX_free(ctx);
+ ctx = NULL;
+
+ /* A different policy on the request fails the policy check. The signer is
+ * trusted so the policy check - not the signer check - rejects it. */
+ ExpectNotNull(otherPolicy = OBJ_nid2obj(NID_sha256));
+ ExpectIntEQ(TS_REQ_set_policy_id(req, otherPolicy), 1);
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 0);
+
+ ASN1_OBJECT_free(otherPolicy);
+ TS_VERIFY_CTX_free(ctx);
+ TS_RESP_free(resp);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_VERIFY_CTX_cleanup(void)
+{
+ EXPECT_DECLS;
+#if defined(TEST_OSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_TS_RESP* resp = NULL;
+ WOLFSSL_TS_VERIFY_CTX* ctx = NULL;
+ WOLFSSL_X509_STORE* store = NULL;
+ WOLFSSL_X509* caX509 = NULL;
+ byte respDer[4096];
+ word32 respDerSz = (word32)sizeof(respDer);
+ const unsigned char* cp;
+
+ ExpectIntEQ(test_tsp_create_resp(respDer, &respDerSz, 1), TEST_SUCCESS);
+ cp = respDer;
+ ExpectNotNull(resp = d2i_TS_RESP(NULL, &cp, (long)respDerSz));
+
+ /* A fully populated context - imprint, nonce, policy and a store. */
+ ExpectNotNull(req = test_tsp_create_req());
+ ExpectNotNull(ctx = TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ cp = tsa_cert_der_2048;
+ ExpectNotNull(caX509 = wolfSSL_d2i_X509(NULL, &cp,
+ sizeof_tsa_cert_der_2048));
+ ExpectNotNull(store = wolfSSL_X509_STORE_new());
+ ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, caX509), 1);
+ wolfSSL_X509_free(caX509);
+ caX509 = NULL;
+ /* set_store takes ownership, but a failed Expect above short-circuits it -
+ * free the store in that case so an allocation-failure path does not leak
+ * the store. */
+ if (EXPECT_SUCCESS()) {
+ ExpectNotNull(TS_VERIFY_CTX_set_store(ctx, store));
+ }
+ else {
+ wolfSSL_X509_STORE_free(store);
+ }
+ store = NULL;
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+
+ /* Cleanup frees the owned store, imprint and nonce and resets state. */
+ TS_VERIFY_CTX_cleanup(ctx);
+
+ /* The context can be filled and used again - exercises the reuse of an
+ * existing context by TS_REQ_to_TS_VERIFY_CTX. Cleanup dropped the store,
+ * so trust the signer again. */
+ ExpectPtrEq(TS_REQ_to_TS_VERIFY_CTX(req, ctx), ctx);
+ ExpectIntEQ(test_tsp_trust_ctx(ctx), TEST_SUCCESS);
+ ExpectIntEQ(TS_RESP_verify_response(ctx, resp), 1);
+
+ TS_VERIFY_CTX_free(ctx);
+ /* Cleaning up NULL is safe. */
+ TS_VERIFY_CTX_cleanup(NULL);
+
+ TS_RESP_free(resp);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wolfSSL_TS_bad_args(void)
+{
+ EXPECT_DECLS;
+#ifdef TEST_OSSL_TSP
+ /* Scalar getters return 0 on NULL. */
+ ExpectIntEQ(TS_REQ_get_version(NULL), 0);
+ ExpectIntEQ(TS_REQ_get_cert_req(NULL), 0);
+ ExpectIntEQ(TS_TST_INFO_get_version(NULL), 0);
+ ExpectIntEQ(TS_TST_INFO_get_ordering(NULL), 0);
+
+ /* Pointer getters return NULL on NULL. */
+ ExpectNull(TS_REQ_get_msg_imprint(NULL));
+ ExpectNull(TS_REQ_get_policy_id(NULL));
+ ExpectNull(TS_REQ_get_nonce(NULL));
+ ExpectNull(TS_MSG_IMPRINT_get_algo(NULL));
+ ExpectNull(TS_MSG_IMPRINT_get_msg(NULL));
+ ExpectNull(TS_TST_INFO_get_policy_id(NULL));
+ ExpectNull(TS_TST_INFO_get_msg_imprint(NULL));
+ ExpectNull(TS_TST_INFO_get_serial(NULL));
+ ExpectNull(TS_TST_INFO_get_time(NULL));
+ ExpectNull(TS_TST_INFO_get_accuracy(NULL));
+ ExpectNull(TS_TST_INFO_get_nonce(NULL));
+ ExpectNull(TS_ACCURACY_get_seconds(NULL));
+ ExpectNull(TS_ACCURACY_get_millis(NULL));
+ ExpectNull(TS_ACCURACY_get_micros(NULL));
+ ExpectNull(TS_STATUS_INFO_get0_status(NULL));
+ ExpectNull(TS_STATUS_INFO_get0_failure_info(NULL));
+ ExpectNull(TS_STATUS_INFO_get0_text(NULL));
+ ExpectNull(TS_RESP_get_status_info(NULL));
+ ExpectNull(TS_RESP_get_tst_info(NULL));
+
+ /* Setters return 0 on NULL. */
+ ExpectIntEQ(TS_REQ_set_version(NULL, 1), 0);
+ ExpectIntEQ(TS_REQ_set_cert_req(NULL, 1), 0);
+ ExpectIntEQ(TS_REQ_set_msg_imprint(NULL, NULL), 0);
+ ExpectIntEQ(TS_REQ_set_nonce(NULL, NULL), 0);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(NULL, NULL), 0);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(NULL, NULL, 0), 0);
+
+ /* Encoders return -1 and decoders return NULL on NULL. */
+ ExpectIntEQ(i2d_TS_RESP(NULL, NULL), -1);
+ ExpectIntEQ(i2d_TS_TST_INFO(NULL, NULL), -1);
+ ExpectNull(d2i_TS_RESP(NULL, NULL, 0));
+ ExpectNull(d2i_TS_TST_INFO(NULL, NULL, 0));
+
+ /* Well-framed SEQUENCEs that are not valid TSTInfo / TimeStampResp pass
+ * the outer length check but fail the wc decode. */
+ {
+ /* SEQUENCE { INTEGER 1 } - not a TSTInfo. */
+ static const byte badTst[] = { 0x30, 0x03, 0x02, 0x01, 0x01 };
+ /* SEQUENCE { INTEGER 0 } - PKIStatusInfo must be a SEQUENCE. */
+ static const byte badResp[] = { 0x30, 0x03, 0x02, 0x01, 0x00 };
+ const unsigned char* cp;
+
+ cp = badTst;
+ ExpectNull(d2i_TS_TST_INFO(NULL, &cp, (long)sizeof(badTst)));
+ cp = badResp;
+ ExpectNull(d2i_TS_RESP(NULL, &cp, (long)sizeof(badResp)));
+
+ /* A non-positive length has no item to decode. */
+ cp = badTst;
+ ExpectNull(d2i_TS_REQ(NULL, &cp, 0));
+ cp = badTst;
+ ExpectNull(d2i_TS_TST_INFO(NULL, &cp, -1));
+ cp = badResp;
+ ExpectNull(d2i_TS_RESP(NULL, &cp, 0));
+ }
+
+ /* Setters reject invalid sub-objects even when the parent is valid. */
+ {
+ WOLFSSL_TS_MSG_IMPRINT* mi = NULL;
+ WOLFSSL_X509_ALGOR* algo = NULL;
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_ASN1_OBJECT policy;
+ byte hash[WC_TSP_MAX_HASH_SZ + 1];
+
+ XMEMSET(hash, 0, sizeof(hash));
+
+ /* set_msg rejects NULL data, a non-positive length and an oversize
+ * hash. */
+ ExpectNotNull(mi = TS_MSG_IMPRINT_new());
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(mi, NULL, sizeof(hash)), 0);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(mi, hash, 0), 0);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(mi, hash, WC_TSP_MAX_HASH_SZ + 1), 0);
+ /* set_algo rejects an algorithm carrying no OID. */
+ ExpectNotNull(algo = X509_ALGOR_new()); /* algo->algorithm is NULL */
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(mi, algo), 0);
+ X509_ALGOR_free(algo);
+ TS_MSG_IMPRINT_free(mi);
+
+ /* Request setters reject NULL sub-objects on an otherwise valid
+ * request, and a policy object with no OID content. */
+ ExpectNotNull(req = TS_REQ_new());
+ ExpectIntEQ(TS_REQ_set_msg_imprint(req, NULL), 0);
+ ExpectIntEQ(TS_REQ_set_nonce(req, NULL), 0);
+ XMEMSET(&policy, 0, sizeof(policy)); /* policy.obj is NULL */
+ ExpectIntEQ(TS_REQ_set_policy_id(req, &policy), 0);
+ TS_REQ_free(req);
+
+ /* A request with no message imprint cannot make a verify context. */
+ ExpectNotNull(req = TS_REQ_new());
+ ExpectNull(TS_REQ_to_TS_VERIFY_CTX(req, NULL));
+ TS_REQ_free(req);
+ }
+
+ /* Freeing a NULL object is a safe no-op. */
+ TS_MSG_IMPRINT_free(NULL);
+ TS_REQ_free(NULL);
+ TS_TST_INFO_free(NULL);
+ TS_RESP_free(NULL);
+#ifdef WOLFSSL_TSP_RESPONDER
+ TS_RESP_CTX_free(NULL);
+#endif
+#endif
+ return EXPECT_RESULT();
+}
+
+/* A getter builds an OpenSSL view from the embedded wc data and caches it on
+ * the parent; a second get returns the same cached object; changing the wc
+ * data with a setter discards the cached view so the next get rebuilds it. */
+int test_wolfSSL_TS_view_cache(void)
+{
+ EXPECT_DECLS;
+#ifdef TEST_OSSL_TSP
+ WOLFSSL_TS_MSG_IMPRINT* mi = NULL;
+ WOLFSSL_X509_ALGOR* algo = NULL;
+ WOLFSSL_X509_ALGOR* gotAlgo = NULL;
+ WOLFSSL_ASN1_STRING* gotMsg = NULL;
+ WOLFSSL_TS_REQ* req = NULL;
+ WOLFSSL_ASN1_INTEGER* nonce = NULL;
+ byte hash[32];
+
+ XMEMSET(hash, 0x5a, sizeof(hash));
+
+ /* Build an imprint with an algorithm and message hash. */
+ ExpectNotNull(mi = TS_MSG_IMPRINT_new());
+ ExpectNotNull(algo = X509_ALGOR_new());
+ if (EXPECT_SUCCESS()) {
+ ASN1_OBJECT_free(algo->algorithm);
+ algo->algorithm = OBJ_nid2obj(NID_sha256);
+ }
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(mi, algo), 1);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(mi, hash, (int)sizeof(hash)), 1);
+
+ /* First get builds and caches the view; a second get returns the same
+ * cached pointer rather than rebuilding. */
+ ExpectNotNull(gotAlgo = TS_MSG_IMPRINT_get_algo(mi));
+ ExpectPtrEq(TS_MSG_IMPRINT_get_algo(mi), gotAlgo);
+ ExpectNotNull(gotMsg = TS_MSG_IMPRINT_get_msg(mi));
+ ExpectPtrEq(TS_MSG_IMPRINT_get_msg(mi), gotMsg);
+
+ /* Setting a new value discards the stale cached views. The next get
+ * rebuilds a fresh object. */
+ ExpectIntEQ(TS_MSG_IMPRINT_set_algo(mi, algo), 1);
+ ExpectIntEQ(TS_MSG_IMPRINT_set_msg(mi, hash, (int)sizeof(hash)), 1);
+ ExpectNotNull(TS_MSG_IMPRINT_get_algo(mi));
+ ExpectNotNull(TS_MSG_IMPRINT_get_msg(mi));
+
+ X509_ALGOR_free(algo);
+ TS_MSG_IMPRINT_free(mi);
+
+ /* The request's nonce view is likewise cached and invalidated on set. */
+ ExpectNotNull(req = TS_REQ_new());
+ ExpectNotNull(nonce = ASN1_INTEGER_new());
+ if (EXPECT_SUCCESS()) {
+ nonce->data[0] = ASN_INTEGER;
+ nonce->data[1] = 4;
+ nonce->data[2] = 0x12;
+ nonce->data[3] = 0x34;
+ nonce->data[4] = 0x56;
+ nonce->data[5] = 0x78;
+ nonce->length = 6;
+ }
+ ExpectIntEQ(TS_REQ_set_nonce(req, nonce), 1);
+ ExpectNotNull(TS_REQ_get_nonce(req)); /* build the cached view */
+ ExpectIntEQ(TS_REQ_set_nonce(req, nonce), 1); /* discards the stale view */
+ ExpectNotNull(TS_REQ_get_nonce(req)); /* rebuilt */
+
+ ASN1_INTEGER_free(nonce);
+ TS_REQ_free(req);
+#endif
+ return EXPECT_RESULT();
+}
diff --git a/tests/api/test_ossl_tsp.h b/tests/api/test_ossl_tsp.h
new file mode 100644
index 00000000000..320af043aa6
--- /dev/null
+++ b/tests/api/test_ossl_tsp.h
@@ -0,0 +1,68 @@
+/* test_ossl_tsp.h
+ *
+ * Copyright (C) 2006-2026 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 3 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 WOLFCRYPT_TEST_OSSL_TSP_H
+#define WOLFCRYPT_TEST_OSSL_TSP_H
+
+#include
+
+int test_wolfSSL_TS_REQ(void);
+int test_wolfSSL_TS_REQ_long_nonce(void);
+int test_wolfSSL_TS_REQ_policy_id(void);
+int test_wolfSSL_TS_RESP(void);
+int test_wolfSSL_TS_RESP_accuracy_ordering(void);
+int test_wolfSSL_TS_STATUS_INFO_failure_info(void);
+int test_wolfSSL_TS_RESP_verify_response(void);
+int test_wolfSSL_TS_RESP_verify_response_chain(void);
+int test_wc_TspResponse_VerifyWithCm(void);
+int test_wolfSSL_TS_RESP_verify_data(void);
+int test_wolfSSL_TS_TST_INFO_get_tsa(void);
+int test_wolfSSL_TS_RESP_CTX(void);
+int test_wolfSSL_TS_RESP_verify_token(void);
+int test_wolfSSL_TS_RESP_verify_status(void);
+int test_wolfSSL_TS_RESP_verify_policy(void);
+int test_wolfSSL_TS_VERIFY_CTX(void);
+int test_wolfSSL_TS_VERIFY_CTX_cleanup(void);
+int test_wolfSSL_TS_bad_args(void);
+int test_wolfSSL_TS_view_cache(void);
+
+#define TEST_OSSL_TSP_DECLS \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_REQ), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_REQ_long_nonce), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_REQ_policy_id), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_accuracy_ordering),\
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_STATUS_INFO_failure_info), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_verify_response), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_verify_response_chain), \
+ TEST_DECL_GROUP("ossl_tsp", test_wc_TspResponse_VerifyWithCm), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_verify_data), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_TST_INFO_get_tsa), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_CTX), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_verify_token), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_verify_status), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_RESP_verify_policy), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_VERIFY_CTX), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_VERIFY_CTX_cleanup), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_bad_args), \
+ TEST_DECL_GROUP("ossl_tsp", test_wolfSSL_TS_view_cache)
+
+#endif /* WOLFCRYPT_TEST_OSSL_TSP_H */
diff --git a/tests/api/test_tsp.c b/tests/api/test_tsp.c
new file mode 100644
index 00000000000..1a2eb613a74
--- /dev/null
+++ b/tests/api/test_tsp.c
@@ -0,0 +1,3531 @@
+/* test_tsp.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+#include
+
+#include
+#include
+
+#ifdef WOLFSSL_TSP
+ #include
+ #include
+#ifdef HAVE_PKCS7
+ #include
+ #include
+ #include
+#endif
+#endif
+
+#ifdef WOLFSSL_TSP
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Hash of message - content is not checked against an algorithm. */
+static const byte tsHashedMsg[32] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+};
+
+/* Set a message imprint to SHA-256 and the test hash. */
+static void test_tsp_set_hash(TspMessageImprint* mi)
+{
+ mi->hashAlgOID = SHA256h;
+ XMEMCPY(mi->hash, tsHashedMsg, sizeof(tsHashedMsg));
+ mi->hashSz = (word32)sizeof(tsHashedMsg);
+}
+/* 1.3.6.1.4.1.999.1 - test TSA policy. */
+static const byte tsPolicy[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+};
+/* Nonce with top bit set to check INTEGER encoding. */
+static const byte tsNonce[] = {
+ 0xc3, 0x5a, 0x10, 0x42, 0x77, 0x08, 0x99, 0x01
+};
+#endif /* WOLFSSL_TSP_REQUESTER */
+#ifndef NO_SHA256
+#if defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Serial number with top bit set to check INTEGER encoding. */
+static const byte tsSerial[] = { 0x9a, 0x33 };
+/* Time of test time-stamp. */
+static const byte tsGenTime[] = "20260604120000Z";
+/* Name of TSA: dNSName GeneralName. */
+static const byte tsTsaName[] = { 0x82, 0x03, 't', 's', 'a' };
+#endif /* WOLFSSL_TSP_REQUESTER && WOLFSSL_TSP_RESPONDER */
+#ifdef WOLFSSL_TSP_REQUESTER
+/* DER encoding of minimal TimeStampReq: version 1 and SHA-256 message
+ * imprint of tsHashedMsg. */
+static const byte tsMinReqDer[] = {
+ 0x30, 0x36, /* TimeStampReq */
+ 0x02, 0x01, 0x01, /* version 1 */
+ 0x30, 0x31, /* messageImprint */
+ 0x30, 0x0d, /* hashAlgorithm */
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, /* sha256 */
+ 0x04, 0x02, 0x01,
+ 0x05, 0x00, /* NULL */
+ 0x04, 0x20, /* hashedMessage */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+};
+#endif /* WOLFSSL_TSP_REQUESTER */
+#endif
+
+#endif /* WOLFSSL_TSP */
+
+int test_wc_TspRequest_Init(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+
+ ExpectIntEQ(wc_TspRequest_Init(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ XMEMSET(&req, 0xa5, sizeof(TspRequest));
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ ExpectIntEQ(req.version, WC_TSP_VERSION);
+ ExpectIntEQ(req.imprint.hashSz, 0);
+ ExpectIntEQ(req.policySz, 0);
+ ExpectIntEQ(req.nonceSz, 0);
+ ExpectIntEQ(req.certReq, 0);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_SetHashType(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+
+ /* Bad argument. */
+ ExpectIntEQ(wc_TspRequest_SetHashType(NULL, WC_HASH_TYPE_SHA256),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Hash type that is not a usable algorithm. */
+ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_NONE),
+ WC_NO_ERR_TRACE(HASH_TYPE_E));
+
+ /* SHA-256 sets the algorithm OID and the digest size. */
+ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0);
+ ExpectIntEQ(req.imprint.hashAlgOID, SHA256h);
+ ExpectIntEQ(req.imprint.hashSz, WC_SHA256_DIGEST_SIZE);
+
+#ifdef WOLFSSL_SHA384
+ /* A different algorithm sets a different OID and size. */
+ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA384), 0);
+ ExpectIntEQ(req.imprint.hashAlgOID, SHA384h);
+ ExpectIntEQ(req.imprint.hashSz, WC_SHA384_DIGEST_SIZE);
+#endif
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_GetHashType(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+ enum wc_HashType hashType = WC_HASH_TYPE_NONE;
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA256), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspRequest_GetHashType(NULL, &hashType),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetHashType(&req, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Round trips with the algorithm that was set. */
+ ExpectIntEQ(wc_TspRequest_GetHashType(&req, &hashType), 0);
+ ExpectIntEQ(hashType, WC_HASH_TYPE_SHA256);
+
+#ifdef WOLFSSL_SHA384
+ ExpectIntEQ(wc_TspRequest_SetHashType(&req, WC_HASH_TYPE_SHA384), 0);
+ ExpectIntEQ(wc_TspRequest_GetHashType(&req, &hashType), 0);
+ ExpectIntEQ(hashType, WC_HASH_TYPE_SHA384);
+#endif
+
+ /* An OID that is not a known hash algorithm. */
+ req.imprint.hashAlgOID = 1;
+ ExpectIntEQ(wc_TspRequest_GetHashType(&req, &hashType),
+ WC_NO_ERR_TRACE(HASH_TYPE_E));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_GetSetHash(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+ byte hash[WC_SHA256_DIGEST_SIZE];
+ byte out[WC_SHA256_DIGEST_SIZE];
+ word32 outSz;
+
+ XMEMSET(hash, 0x5a, sizeof(hash));
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+
+ /* Set: bad arguments. */
+ ExpectIntEQ(wc_TspRequest_SetHash(NULL, hash, (word32)sizeof(hash)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_SetHash(&req, NULL, (word32)sizeof(hash)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Hash too big for the message imprint. */
+ ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, WC_TSP_MAX_HASH_SZ + 1),
+ WC_NO_ERR_TRACE(BUFFER_E));
+
+ /* Set the hash and length. */
+ ExpectIntEQ(wc_TspRequest_SetHash(&req, hash, (word32)sizeof(hash)), 0);
+ ExpectIntEQ(req.imprint.hashSz, (word32)sizeof(hash));
+
+ /* Get: bad arguments. */
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetHash(NULL, out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetHash(&req, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetHash(&req, out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Buffer too small. */
+ outSz = (word32)sizeof(hash) - 1;
+ ExpectIntEQ(wc_TspRequest_GetHash(&req, out, &outSz),
+ WC_NO_ERR_TRACE(BUFFER_E));
+
+ /* Get the hash back - round trips. */
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetHash(&req, out, &outSz), 0);
+ ExpectIntEQ(outSz, (word32)sizeof(hash));
+ ExpectBufEQ(out, hash, (int)sizeof(hash));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_GetSetNonce(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+ static const byte nonce[] = { 0x12, 0x34, 0x56, 0x78 };
+ static const byte padded[] = { 0x00, 0x00, 0x12, 0x34 };
+ static const byte zeros[] = { 0x00, 0x00, 0x00 };
+ byte out[16];
+ word32 outSz;
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+
+ /* Set: bad arguments. */
+ ExpectIntEQ(wc_TspRequest_SetNonce(NULL, nonce, (word32)sizeof(nonce)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_SetNonce(&req, NULL, (word32)sizeof(nonce)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_SetNonce(&req, nonce, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Nonce too big for the field. */
+ ExpectIntEQ(wc_TspRequest_SetNonce(&req, nonce, MAX_TS_NONCE_SZ + 1),
+ WC_NO_ERR_TRACE(BUFFER_E));
+
+ /* No leading zeros - kept as-is and round trips. */
+ ExpectIntEQ(wc_TspRequest_SetNonce(&req, nonce, (word32)sizeof(nonce)), 0);
+ ExpectIntEQ(req.nonceSz, (word32)sizeof(nonce));
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetNonce(&req, out, &outSz), 0);
+ ExpectIntEQ(outSz, (word32)sizeof(nonce));
+ ExpectBufEQ(out, nonce, (int)sizeof(nonce));
+
+ /* Leading zero bytes are stripped. */
+ ExpectIntEQ(wc_TspRequest_SetNonce(&req, padded, (word32)sizeof(padded)),
+ 0);
+ ExpectIntEQ(req.nonceSz, 2);
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetNonce(&req, out, &outSz), 0);
+ ExpectIntEQ(outSz, 2);
+ ExpectIntEQ(out[0], 0x12);
+ ExpectIntEQ(out[1], 0x34);
+
+ /* All zeros becomes a single zero byte. */
+ ExpectIntEQ(wc_TspRequest_SetNonce(&req, zeros, (word32)sizeof(zeros)), 0);
+ ExpectIntEQ(req.nonceSz, 1);
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetNonce(&req, out, &outSz), 0);
+ ExpectIntEQ(outSz, 1);
+ ExpectIntEQ(out[0], 0x00);
+
+ /* Get: bad arguments. */
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetNonce(NULL, out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetNonce(&req, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetNonce(&req, out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Buffer too small for the 1-byte nonce. */
+ outSz = 0;
+ ExpectIntEQ(wc_TspRequest_GetNonce(&req, out, &outSz),
+ WC_NO_ERR_TRACE(BUFFER_E));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspGenerateNonce(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ WC_RNG rng;
+ TspRequest req;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspRequest_GenerateNonce(NULL, &rng, 8),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GenerateNonce(&req, NULL, 8),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GenerateNonce(&req, &rng, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GenerateNonce(&req, &rng,
+ MAX_TS_NONCE_SZ + 1), WC_NO_ERR_TRACE(BUFFER_E));
+
+ /* Generates a minimal positive INTEGER nonce of the requested size. */
+ ExpectIntEQ(wc_TspRequest_GenerateNonce(&req, &rng, 8), 0);
+ ExpectIntEQ(req.nonceSz, 8);
+ ExpectIntLT(req.nonce[0], 0x80); /* positive - top bit clear */
+ ExpectIntGT(req.nonce[0], 0x00); /* minimal - non-zero leading byte */
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_GetSetPolicy(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+ /* 1.3.6.1.4.1.999.1 - OBJECT IDENTIFIER content. */
+ static const byte policy[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+ };
+ byte big[MAX_OID_SZ + 1];
+ byte out[MAX_OID_SZ];
+ word32 outSz;
+
+ XMEMSET(big, 0, sizeof(big));
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+
+ /* Set: bad arguments. */
+ ExpectIntEQ(wc_TspRequest_SetPolicy(NULL, policy, (word32)sizeof(policy)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_SetPolicy(&req, NULL, (word32)sizeof(policy)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_SetPolicy(&req, policy, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Policy too big for the field. */
+ ExpectIntEQ(wc_TspRequest_SetPolicy(&req, big, (word32)sizeof(big)),
+ WC_NO_ERR_TRACE(BUFFER_E));
+
+ /* Set and get round trips - content kept as-is, no stripping. */
+ ExpectIntEQ(wc_TspRequest_SetPolicy(&req, policy, (word32)sizeof(policy)),
+ 0);
+ ExpectIntEQ(req.policySz, (word32)sizeof(policy));
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetPolicy(&req, out, &outSz), 0);
+ ExpectIntEQ(outSz, (word32)sizeof(policy));
+ ExpectBufEQ(out, policy, (int)sizeof(policy));
+
+ /* Get: bad arguments. */
+ outSz = (word32)sizeof(out);
+ ExpectIntEQ(wc_TspRequest_GetPolicy(NULL, out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetPolicy(&req, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_GetPolicy(&req, out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Buffer too small for the policy. */
+ outSz = (word32)sizeof(policy) - 1;
+ ExpectIntEQ(wc_TspRequest_GetPolicy(&req, out, &outSz),
+ WC_NO_ERR_TRACE(BUFFER_E));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_GetSetCertReq(void)
+{
+ EXPECT_DECLS;
+ /* The requester both sets and gets certReq - runs in a requester build. */
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+
+ /* Defaults to not requesting the TSA certificate. */
+ ExpectIntEQ(wc_TspRequest_GetCertReq(&req), 0);
+
+ /* Set requests the certificate and round trips through the getter. */
+ wc_TspRequest_SetCertReq(&req, 1);
+ ExpectIntEQ(wc_TspRequest_GetCertReq(&req), 1);
+
+ /* Any non-zero value is normalized to 1. */
+ wc_TspRequest_SetCertReq(&req, 5);
+ ExpectIntEQ(wc_TspRequest_GetCertReq(&req), 1);
+ ExpectIntEQ(req.certReq, 1);
+
+ /* Clear the request. */
+ wc_TspRequest_SetCertReq(&req, 0);
+ ExpectIntEQ(wc_TspRequest_GetCertReq(&req), 0);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_Encode(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ TspRequest req;
+ byte enc[256];
+ word32 encSz;
+ word32 sz;
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ test_tsp_set_hash(&req.imprint);
+
+ /* Bad arguments. */
+ encSz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspRequest_Encode(NULL, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Message imprint required. */
+ req.imprint.hashSz = 0;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Hash too long. */
+ req.imprint.hashSz = WC_TSP_MAX_HASH_SZ + 1;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ req.imprint.hashSz = (word32)sizeof(tsHashedMsg);
+ /* Policy too long. */
+ req.policySz = MAX_OID_SZ + 1;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ req.policySz = 0;
+ /* Unknown hash algorithm. */
+ req.imprint.hashAlgOID = 1;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz),
+ WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E));
+ req.imprint.hashAlgOID = SHA256h;
+ /* Nonce with a leading zero byte - not allowed. */
+ {
+ static const byte paddedNonce[] = { 0x00, 0x13 };
+
+ XMEMCPY(req.nonce, paddedNonce, sizeof(paddedNonce));
+ req.nonceSz = (word32)sizeof(paddedNonce);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Nonce too long - not allowed. */
+ req.nonceSz = MAX_TS_NONCE_SZ + 1;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ req.nonceSz = 0;
+ }
+
+ /* Get length of encoding only. */
+ encSz = 0;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, NULL, &encSz), 0);
+ ExpectIntEQ(encSz, (word32)sizeof(tsMinReqDer));
+ /* Buffer too small. */
+ sz = encSz - 1;
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &sz),
+ WC_NO_ERR_TRACE(BUFFER_E));
+ /* Check minimal encoding against expected DER. */
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &sz), 0);
+ ExpectIntEQ(sz, (word32)sizeof(tsMinReqDer));
+ ExpectBufEQ(enc, tsMinReqDer, (int)sizeof(tsMinReqDer));
+
+ /* All optional fields included. */
+ XMEMCPY(req.policy, tsPolicy, sizeof(tsPolicy));
+ req.policySz = (word32)sizeof(tsPolicy);
+ XMEMCPY(req.nonce, tsNonce, sizeof(tsNonce));
+ req.nonceSz = (word32)sizeof(tsNonce);
+ req.certReq = 1;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &sz), 0);
+ ExpectIntGT(sz, (word32)sizeof(tsMinReqDer));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspRequest_Decode(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER)
+ /* Minimal TimeStampReq without NULL hash algorithm parameters. */
+ static const byte noNullDer[] = {
+ 0x30, 0x34,
+ 0x02, 0x01, 0x01,
+ 0x30, 0x2f,
+ 0x30, 0x0b,
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+ 0x04, 0x20,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+ };
+ TspRequest req;
+ TspRequest reqDec;
+ byte enc[256];
+ word32 sz;
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspRequest_Decode(NULL, tsMinReqDer,
+ (word32)sizeof(tsMinReqDer)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, NULL,
+ (word32)sizeof(tsMinReqDer)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, tsMinReqDer, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Minimal request. */
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, tsMinReqDer,
+ (word32)sizeof(tsMinReqDer)), 0);
+ ExpectIntEQ(reqDec.version, WC_TSP_VERSION);
+ ExpectIntEQ(reqDec.imprint.hashAlgOID, SHA256h);
+ ExpectIntEQ(reqDec.imprint.hashSz, (word32)sizeof(tsHashedMsg));
+ ExpectBufEQ(reqDec.imprint.hash, tsHashedMsg,
+ (int)sizeof(tsHashedMsg));
+ ExpectIntEQ(reqDec.policySz, 0);
+ ExpectIntEQ(reqDec.nonceSz, 0);
+ ExpectIntEQ(reqDec.certReq, 0);
+
+ /* Hash algorithm parameters not present. */
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, noNullDer,
+ (word32)sizeof(noNullDer)), 0);
+ ExpectIntEQ(reqDec.imprint.hashAlgOID, SHA256h);
+
+ /* Truncated encoding. */
+ ExpectIntLT(wc_TspRequest_Decode(&reqDec, tsMinReqDer,
+ (word32)sizeof(tsMinReqDer) - 1), 0);
+ /* Trailing data not allowed. */
+ XMEMCPY(enc, tsMinReqDer, sizeof(tsMinReqDer));
+ enc[sizeof(tsMinReqDer)] = 0x00;
+ ExpectIntLT(wc_TspRequest_Decode(&reqDec, enc,
+ (word32)sizeof(tsMinReqDer) + 1), 0);
+
+ /* Request with extensions is not supported. */
+ {
+ static const byte extsReqDer[] = {
+ 0x30, 0x45, /* TimeStampReq */
+ 0x02, 0x01, 0x01, /* version 1 */
+ 0x30, 0x31, /* messageImprint */
+ 0x30, 0x0d, /* hashAlgorithm */
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, /* sha256 */
+ 0x04, 0x02, 0x01,
+ 0x05, 0x00, /* NULL */
+ 0x04, 0x20, /* hashedMessage */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0xa0, 0x0d, /* extensions */
+ 0x30, 0x0b, 0x06, 0x02, 0x2a, 0x03, /* OID 1.2.3 */
+ 0x04, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05 /* value */
+ };
+
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, extsReqDer,
+ (word32)sizeof(extsReqDer)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ }
+
+ /* Request with an empty hash is invalid. */
+ {
+ static const byte emptyHashDer[] = {
+ 0x30, 0x16, /* TimeStampReq */
+ 0x02, 0x01, 0x01, /* version 1 */
+ 0x30, 0x11, /* messageImprint */
+ 0x30, 0x0d, /* hashAlgorithm */
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, /* sha256 */
+ 0x04, 0x02, 0x01,
+ 0x05, 0x00, /* NULL */
+ 0x04, 0x00 /* hashedMessage */
+ };
+
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, emptyHashDer,
+ (word32)sizeof(emptyHashDer)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ }
+
+ /* Request with an unsupported version is rejected - RFC 3161, 2.4.1. */
+ {
+ /* tsMinReqDer with the version INTEGER changed from 1 to 2. */
+ XMEMCPY(enc, tsMinReqDer, sizeof(tsMinReqDer));
+ enc[4] = 0x02; /* version = 2 */
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, enc,
+ (word32)sizeof(tsMinReqDer)), WC_NO_ERR_TRACE(ASN_VERSION_E));
+ enc[4] = 0x00; /* version = 0 */
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, enc,
+ (word32)sizeof(tsMinReqDer)), WC_NO_ERR_TRACE(ASN_VERSION_E));
+ }
+
+ /* Nonce of zero is one zero byte. */
+ {
+ static const byte zeroNonce[] = { 0x00 };
+
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ test_tsp_set_hash(&req.imprint);
+ XMEMCPY(req.nonce, zeroNonce, sizeof(zeroNonce));
+ req.nonceSz = (word32)sizeof(zeroNonce);
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &sz), 0);
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, enc, sz), 0);
+ ExpectIntEQ(reqDec.nonceSz, 1);
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(reqDec.nonce[0], 0x00);
+ }
+ }
+
+ /* Round trip all optional fields. */
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ test_tsp_set_hash(&req.imprint);
+ XMEMCPY(req.policy, tsPolicy, sizeof(tsPolicy));
+ req.policySz = (word32)sizeof(tsPolicy);
+ XMEMCPY(req.nonce, tsNonce, sizeof(tsNonce));
+ req.nonceSz = (word32)sizeof(tsNonce);
+ req.certReq = 1;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, enc, &sz), 0);
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, enc, sz), 0);
+ ExpectIntEQ(reqDec.policySz, (word32)sizeof(tsPolicy));
+ ExpectBufEQ(reqDec.policy, tsPolicy, (int)sizeof(tsPolicy));
+ ExpectIntEQ(reqDec.nonceSz, (word32)sizeof(tsNonce));
+ ExpectBufEQ(reqDec.nonce, tsNonce, (int)sizeof(tsNonce));
+ ExpectIntEQ(reqDec.certReq, 1);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_Init(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+
+ ExpectIntEQ(wc_TspTstInfo_Init(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ XMEMSET(&tst, 0xa5, sizeof(TspTstInfo));
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ ExpectIntEQ(tst.version, WC_TSP_VERSION);
+ ExpectNull(tst.policy);
+ ExpectNull(tst.serial);
+ ExpectNull(tst.genTime);
+ ExpectIntEQ(tst.accuracy.seconds, 0);
+ ExpectIntEQ(tst.ordering, 0);
+ ExpectNull(tst.nonce);
+ ExpectNull(tst.tsa);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_GetSetSerial(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ static const byte serial[] = { 0x9a, 0x33, 0x10 };
+ static const byte padded[] = { 0x00, 0x00, 0x9a, 0x33 };
+ static const byte zeros[] = { 0x00, 0x00, 0x00 };
+ const byte* out = NULL;
+ word32 outSz = 0;
+
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+
+ /* No serial on a freshly initialized TSTInfo - Get reports it absent. */
+ out = serial;
+ outSz = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(&tst, &out, &outSz), 0);
+ ExpectNull(out);
+ ExpectIntEQ(outSz, 0);
+
+ /* Set: bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_SetSerial(NULL, serial, (word32)sizeof(serial)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetSerial(&tst, NULL, (word32)sizeof(serial)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetSerial(&tst, serial, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* No leading zeros - referenced as-is and round trips. */
+ ExpectIntEQ(wc_TspTstInfo_SetSerial(&tst, serial, (word32)sizeof(serial)),
+ 0);
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, serial);
+ ExpectIntEQ(outSz, (word32)sizeof(serial));
+
+ /* Leading zero bytes are stripped - references past them. */
+ ExpectIntEQ(wc_TspTstInfo_SetSerial(&tst, padded, (word32)sizeof(padded)),
+ 0);
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, padded + 2);
+ ExpectIntEQ(outSz, 2);
+ ExpectIntEQ(out[0], 0x9a);
+ ExpectIntEQ(out[1], 0x33);
+
+ /* All zeros becomes a single zero byte. */
+ ExpectIntEQ(wc_TspTstInfo_SetSerial(&tst, zeros, (word32)sizeof(zeros)), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, zeros + 2);
+ ExpectIntEQ(outSz, 1);
+ ExpectIntEQ(out[0], 0x00);
+
+ /* Get: bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(NULL, &out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(&tst, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetSerial(&tst, &out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+#endif
+ return EXPECT_RESULT();
+}
+
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Fill a TSTInfo with the required fields. */
+static void test_tsp_set_tstinfo(TspTstInfo* tst)
+{
+ (void)wc_TspTstInfo_Init(tst);
+ tst->policy = tsPolicy;
+ tst->policySz = (word32)sizeof(tsPolicy);
+ test_tsp_set_hash(&tst->imprint);
+ tst->serial = tsSerial;
+ tst->serialSz = (word32)sizeof(tsSerial);
+ tst->genTime = tsGenTime;
+ tst->genTimeSz = (word32)sizeof(tsGenTime) - 1;
+}
+#endif
+
+int test_wc_TspTstInfo_Getters(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ const byte* out = NULL;
+ word32 outSz = 0;
+ word32 hashOID = 0;
+ word32 seconds = 0;
+ word16 millis = 0;
+ word16 micros = 0;
+
+ test_tsp_set_tstinfo(&tst);
+ tst.accuracy.seconds = 1;
+ tst.accuracy.millis = 500;
+ tst.accuracy.micros = 250;
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+ tst.tsa = tsTsaName;
+ tst.tsaSz = (word32)sizeof(tsTsaName);
+
+ /* Policy. */
+ ExpectIntEQ(wc_TspTstInfo_GetPolicy(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, tsPolicy);
+ ExpectIntEQ(outSz, (word32)sizeof(tsPolicy));
+ /* Message imprint. */
+ ExpectIntEQ(wc_TspTstInfo_GetMsgImprint(&tst, &hashOID, &out, &outSz), 0);
+ ExpectIntEQ(hashOID, SHA256h);
+ ExpectIntEQ(outSz, (word32)sizeof(tsHashedMsg));
+ ExpectBufEQ(out, tsHashedMsg, (int)sizeof(tsHashedMsg));
+ /* All message imprint outputs are optional. */
+ ExpectIntEQ(wc_TspTstInfo_GetMsgImprint(&tst, NULL, NULL, NULL), 0);
+ /* Time of the time-stamp. */
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, tsGenTime);
+ ExpectIntEQ(outSz, (word32)sizeof(tsGenTime) - 1);
+ /* Accuracy. */
+ ExpectIntEQ(wc_TspTstInfo_GetAccuracy(&tst, &seconds, &millis, µs), 0);
+ ExpectIntEQ(seconds, 1);
+ ExpectIntEQ(millis, 500);
+ ExpectIntEQ(micros, 250);
+ /* All accuracy outputs are optional. */
+ ExpectIntEQ(wc_TspTstInfo_GetAccuracy(&tst, NULL, NULL, NULL), 0);
+ /* Nonce. */
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, tsNonce);
+ ExpectIntEQ(outSz, (word32)sizeof(tsNonce));
+ /* TSA name. */
+ ExpectIntEQ(wc_TspTstInfo_GetTsa(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, tsTsaName);
+ ExpectIntEQ(outSz, (word32)sizeof(tsTsaName));
+
+ /* On a freshly initialized TSTInfo the optional fields are absent - the
+ * getters succeed and report empty values, not an error. */
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ out = tsPolicy;
+ outSz = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetPolicy(&tst, &out, &outSz), 0);
+ ExpectNull(out);
+ ExpectIntEQ(outSz, 0);
+ out = tsGenTime;
+ outSz = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(&tst, &out, &outSz), 0);
+ ExpectNull(out);
+ ExpectIntEQ(outSz, 0);
+ out = tsNonce;
+ outSz = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(&tst, &out, &outSz), 0);
+ ExpectNull(out);
+ ExpectIntEQ(outSz, 0);
+ out = tsTsaName;
+ outSz = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetTsa(&tst, &out, &outSz), 0);
+ ExpectNull(out);
+ ExpectIntEQ(outSz, 0);
+ hashOID = 99;
+ outSz = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetMsgImprint(&tst, &hashOID, &out, &outSz), 0);
+ ExpectIntEQ(hashOID, 0);
+ ExpectIntEQ(outSz, 0);
+ seconds = 99;
+ millis = 99;
+ micros = 99;
+ ExpectIntEQ(wc_TspTstInfo_GetAccuracy(&tst, &seconds, &millis, µs), 0);
+ ExpectIntEQ(seconds, 0);
+ ExpectIntEQ(millis, 0);
+ ExpectIntEQ(micros, 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_GetPolicy(NULL, &out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetPolicy(&tst, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetPolicy(&tst, &out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetMsgImprint(NULL, &hashOID, &out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(NULL, &out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(&tst, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(&tst, &out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetAccuracy(NULL, &seconds, &millis, µs),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(NULL, &out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(&tst, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(&tst, &out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetTsa(NULL, &out, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetTsa(&tst, NULL, &outSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetTsa(&tst, &out, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_Setters(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ /* 1.3.6.1.4.1.999.1 - OBJECT IDENTIFIER content. */
+ static const byte policy[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+ };
+ static const byte hash[] = { 0xde, 0xad, 0xbe, 0xef };
+ static const byte genTime[] = "20260610120000Z";
+ static const byte nonce[] = { 0x12, 0x34 };
+ /* Name of TSA: dNSName GeneralName. */
+ static const byte tsa[] = { 0x82, 0x03, 't', 's', 'a' };
+ static const byte padded[] = { 0x00, 0x00, 0x12, 0x34 };
+ byte bigHash[WC_TSP_MAX_HASH_SZ + 1];
+ const byte* out = NULL;
+ word32 outSz = 0;
+ word32 hashOID = 0;
+ word32 seconds = 0;
+ word16 millis = 0;
+ word16 micros = 0;
+
+ XMEMSET(bigHash, 0, sizeof(bigHash));
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+
+ /* Policy - referenced, round trips through the getter. */
+ ExpectIntEQ(wc_TspTstInfo_SetPolicy(NULL, policy, (word32)sizeof(policy)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetPolicy(&tst, NULL, (word32)sizeof(policy)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetPolicy(&tst, policy, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetPolicy(&tst, policy, (word32)sizeof(policy)),
+ 0);
+ ExpectIntEQ(wc_TspTstInfo_GetPolicy(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, policy);
+ ExpectIntEQ(outSz, (word32)sizeof(policy));
+
+ /* Message imprint - copied, too big rejected, round trips. */
+ ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(NULL, SHA256h, hash,
+ (word32)sizeof(hash)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, NULL,
+ (word32)sizeof(hash)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, hash, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, bigHash,
+ (word32)sizeof(bigHash)), WC_NO_ERR_TRACE(BUFFER_E));
+ ExpectIntEQ(wc_TspTstInfo_SetMsgImprint(&tst, SHA256h, hash,
+ (word32)sizeof(hash)), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetMsgImprint(&tst, &hashOID, &out, &outSz), 0);
+ ExpectIntEQ(hashOID, SHA256h);
+ ExpectIntEQ(outSz, (word32)sizeof(hash));
+ ExpectBufEQ(out, hash, (int)sizeof(hash));
+
+ /* Time of the time-stamp - referenced, round trips. */
+ ExpectIntEQ(wc_TspTstInfo_SetGenTime(NULL, genTime,
+ (word32)sizeof(genTime) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetGenTime(&tst, NULL,
+ (word32)sizeof(genTime) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetGenTime(&tst, genTime, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetGenTime(&tst, genTime,
+ (word32)sizeof(genTime) - 1), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, genTime);
+ ExpectIntEQ(outSz, (word32)sizeof(genTime) - 1);
+
+ /* Accuracy - values round trip. */
+ ExpectIntEQ(wc_TspTstInfo_SetAccuracy(NULL, 1, 500, 250),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetAccuracy(&tst, 1, 500, 250), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetAccuracy(&tst, &seconds, &millis, µs), 0);
+ ExpectIntEQ(seconds, 1);
+ ExpectIntEQ(millis, 500);
+ ExpectIntEQ(micros, 250);
+
+ /* Nonce - referenced, leading zeros stripped, round trips. */
+ ExpectIntEQ(wc_TspTstInfo_SetNonce(NULL, nonce, (word32)sizeof(nonce)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetNonce(&tst, NULL, (word32)sizeof(nonce)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetNonce(&tst, nonce, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetNonce(&tst, nonce, (word32)sizeof(nonce)), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, nonce);
+ ExpectIntEQ(outSz, (word32)sizeof(nonce));
+ ExpectIntEQ(wc_TspTstInfo_SetNonce(&tst, padded, (word32)sizeof(padded)),
+ 0);
+ ExpectIntEQ(wc_TspTstInfo_GetNonce(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, padded + 2);
+ ExpectIntEQ(outSz, 2);
+
+ /* TSA name - referenced, round trips. */
+ ExpectIntEQ(wc_TspTstInfo_SetTsa(NULL, tsa, (word32)sizeof(tsa)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetTsa(&tst, NULL, (word32)sizeof(tsa)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetTsa(&tst, tsa, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetTsa(&tst, tsa, (word32)sizeof(tsa)), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetTsa(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, tsa);
+ ExpectIntEQ(outSz, (word32)sizeof(tsa));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_Encode(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ byte enc[256];
+ word32 encSz;
+ word32 sz;
+
+ test_tsp_set_tstinfo(&tst);
+
+ /* Bad arguments. */
+ encSz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(NULL, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Required fields. */
+ tst.policy = NULL;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.policy = tsPolicy;
+ /* Empty policy - not allowed. */
+ tst.policySz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.policySz = (word32)sizeof(tsPolicy);
+ /* Empty genTime - not allowed. */
+ tst.genTimeSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* genTime must end in Z. */
+ tst.genTime = (const byte*)"20260604120000";
+ tst.genTimeSz = 14;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* genTime fraction of seconds must not have a trailing zero. */
+ tst.genTime = (const byte*)"20260604120000.50Z";
+ tst.genTimeSz = 18;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* genTime fraction of seconds must not be empty. */
+ tst.genTime = (const byte*)"20260604120000.Z";
+ tst.genTimeSz = 16;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* genTime with a fraction of seconds is valid. */
+ tst.genTime = (const byte*)"20260604120000.5Z";
+ tst.genTimeSz = 17;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz), 0);
+ encSz = (word32)sizeof(enc);
+ tst.genTime = tsGenTime;
+ tst.genTimeSz = (word32)sizeof(tsGenTime) - 1;
+ /* Empty TSA name - not allowed. */
+ tst.tsa = tsTsaName;
+ tst.tsaSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.tsa = NULL;
+ tst.imprint.hashSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Hash too long. */
+ tst.imprint.hashSz = WC_TSP_MAX_HASH_SZ + 1;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.imprint.hashSz = (word32)sizeof(tsHashedMsg);
+ tst.serial = NULL;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.serial = tsSerial;
+ /* Serial number and nonce with a leading zero byte - not allowed. */
+ {
+ static const byte paddedNum[] = { 0x00, 0x13 };
+
+ tst.serial = paddedNum;
+ tst.serialSz = (word32)sizeof(paddedNum);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Empty serial number - not allowed. */
+ tst.serialSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.serial = tsSerial;
+ tst.serialSz = (word32)sizeof(tsSerial);
+ tst.nonce = paddedNum;
+ tst.nonceSz = (word32)sizeof(paddedNum);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.nonce = NULL;
+ tst.nonceSz = 0;
+ }
+ /* Accuracy range. */
+ tst.accuracy.millis = 1000;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.accuracy.millis = 0;
+ tst.accuracy.micros = 1000;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ tst.accuracy.micros = 0;
+ /* Unknown hash algorithm. */
+ tst.imprint.hashAlgOID = 1;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &encSz),
+ WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E));
+ tst.imprint.hashAlgOID = SHA256h;
+
+ /* Get length of encoding only. */
+ encSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, NULL, &encSz), 0);
+ ExpectIntGT(encSz, 0);
+ /* Buffer too small. */
+ sz = encSz - 1;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz),
+ WC_NO_ERR_TRACE(BUFFER_E));
+ /* Minimal TSTInfo round trip. */
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(sz, encSz);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.accuracy.seconds, 0);
+ ExpectIntEQ(tstDec.accuracy.millis, 0);
+ ExpectIntEQ(tstDec.accuracy.micros, 0);
+ ExpectIntEQ(tstDec.ordering, 0);
+ ExpectNull(tstDec.nonce);
+ ExpectNull(tstDec.tsa);
+
+ /* Accuracy values that need 2 bytes and a leading zero byte. */
+ tst.accuracy.seconds = 70000;
+ tst.accuracy.millis = 128;
+ tst.accuracy.micros = 999;
+ /* All optional fields included. */
+ tst.ordering = 1;
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+ tst.tsa = tsTsaName;
+ tst.tsaSz = (word32)sizeof(tsTsaName);
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.accuracy.seconds, 70000);
+ ExpectIntEQ(tstDec.accuracy.millis, 128);
+ ExpectIntEQ(tstDec.accuracy.micros, 999);
+ ExpectIntEQ(tstDec.ordering, 1);
+
+ /* Accuracy with only seconds. */
+ tst.accuracy.seconds = 1;
+ tst.accuracy.millis = 0;
+ tst.accuracy.micros = 0;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.accuracy.seconds, 1);
+ ExpectIntEQ(tstDec.accuracy.millis, 0);
+ ExpectIntEQ(tstDec.accuracy.micros, 0);
+
+ /* Accuracy with only millis. */
+ tst.accuracy.seconds = 0;
+ tst.accuracy.millis = 500;
+ tst.accuracy.micros = 0;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.accuracy.seconds, 0);
+ ExpectIntEQ(tstDec.accuracy.millis, 500);
+ ExpectIntEQ(tstDec.accuracy.micros, 0);
+
+ /* Accuracy seconds needing a zero byte to be positive. */
+ tst.accuracy.seconds = 200;
+ tst.accuracy.millis = 0;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.accuracy.seconds, 200);
+ /* Accuracy seconds of the maximum encoding length. */
+ tst.accuracy.seconds = 0x80000000UL;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.accuracy.seconds, 0x80000000UL);
+
+#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES)
+ /* Use current time when genTime not set. */
+ tst.genTime = NULL;
+ tst.genTimeSz = 0;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntGE(tstDec.genTimeSz, 15);
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(tstDec.genTime[tstDec.genTimeSz - 1], 'Z');
+ }
+#endif
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_Decode(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ byte enc[256];
+ word32 sz;
+
+ test_tsp_set_tstinfo(&tst);
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_Decode(NULL, enc, sz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, NULL, sz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Truncated encoding. */
+ ExpectIntLT(wc_TspTstInfo_Decode(&tstDec, enc, sz - 1), 0);
+ /* Trailing data not allowed. */
+ enc[sz] = 0x00;
+ ExpectIntLT(wc_TspTstInfo_Decode(&tstDec, enc, sz + 1), 0);
+
+ /* TSTInfo with extensions is not supported. */
+ {
+ static const byte extsTstDer[] = {
+ 0x30, 0x62, /* TSTInfo */
+ 0x02, 0x01, 0x01, /* version 1 */
+ 0x06, 0x08, /* policy */
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01,
+ 0x30, 0x31, /* messageImprint */
+ 0x30, 0x0d, /* hashAlgorithm */
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, /* sha256 */
+ 0x04, 0x02, 0x01,
+ 0x05, 0x00, /* NULL */
+ 0x04, 0x20, /* hashedMessage */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x02, 0x02, 0x9a, 0x33, /* serialNumber */
+ 0x18, 0x0f, /* genTime */
+ '2', '0', '2', '6', '0', '6', '0', '4',
+ '1', '2', '0', '0', '0', '0', 'Z',
+ 0xa1, 0x0b, /* extensions */
+ 0x30, 0x09, 0x06, 0x02, 0x2a, 0x03, /* OID 1.2.3 */
+ 0x04, 0x03, 0x01, 0x02, 0x03 /* value */
+ };
+
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, extsTstDer,
+ (word32)sizeof(extsTstDer)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ }
+
+ /* Accuracy millis and micros must be 1..999 when present. */
+ {
+ static const byte accTstDer[] = {
+ 0x30, 0x5b, /* TSTInfo */
+ 0x02, 0x01, 0x01, /* version 1 */
+ 0x06, 0x08, /* policy */
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01,
+ 0x30, 0x31, /* messageImprint */
+ 0x30, 0x0d, /* hashAlgorithm */
+ 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, /* sha256 */
+ 0x04, 0x02, 0x01,
+ 0x05, 0x00, /* NULL */
+ 0x04, 0x20, /* hashedMessage */
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x02, 0x02, 0x9a, 0x33, /* serialNumber */
+ 0x18, 0x0f, /* genTime */
+ '2', '0', '2', '6', '0', '6', '0', '4',
+ '1', '2', '0', '0', '0', '0', 'Z',
+ 0x30, 0x04, /* accuracy */
+ 0x80, 0x02, 0x03, 0xe7 /* millis 999 */
+ };
+ byte accEnc[sizeof(accTstDer)];
+
+ XMEMCPY(accEnc, accTstDer, sizeof(accTstDer));
+ /* Maximum millis value decodes. */
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, accEnc,
+ (word32)sizeof(accEnc)), 0);
+ ExpectIntEQ(tstDec.accuracy.millis, 999);
+ /* millis of 1000 - out of range. */
+ accEnc[sizeof(accEnc) - 1] = 0xe8;
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, accEnc,
+ (word32)sizeof(accEnc)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ /* micros of 1000 - out of range. */
+ accEnc[sizeof(accEnc) - 4] = 0x81;
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, accEnc,
+ (word32)sizeof(accEnc)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ /* micros of zero - out of range. */
+ accEnc[sizeof(accEnc) - 2] = 0x00;
+ accEnc[sizeof(accEnc) - 1] = 0x00;
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, accEnc,
+ (word32)sizeof(accEnc)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ /* genTime not ending in Z - invalid. */
+ XMEMCPY(accEnc, accTstDer, sizeof(accTstDer));
+ accEnc[sizeof(accEnc) - 7] = '0';
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, accEnc,
+ (word32)sizeof(accEnc)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+ }
+
+ /* Check decoded fields. */
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.version, WC_TSP_VERSION);
+ ExpectIntEQ(tstDec.policySz, (word32)sizeof(tsPolicy));
+ ExpectBufEQ(tstDec.policy, tsPolicy, (int)sizeof(tsPolicy));
+ ExpectIntEQ(tstDec.imprint.hashAlgOID, SHA256h);
+ ExpectIntEQ(tstDec.imprint.hashSz, (word32)sizeof(tsHashedMsg));
+ ExpectBufEQ(tstDec.imprint.hash, tsHashedMsg,
+ (int)sizeof(tsHashedMsg));
+ ExpectIntEQ(tstDec.serialSz, (word32)sizeof(tsSerial));
+ ExpectBufEQ(tstDec.serial, tsSerial, (int)sizeof(tsSerial));
+ ExpectIntEQ(tstDec.genTimeSz, (word32)sizeof(tsGenTime) - 1);
+ ExpectBufEQ(tstDec.genTime, tsGenTime, (int)sizeof(tsGenTime) - 1);
+
+ /* Round trip the tsa field. */
+ tst.tsa = tsTsaName;
+ tst.tsaSz = (word32)sizeof(tsTsaName);
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+ ExpectIntEQ(tstDec.tsaSz, (word32)sizeof(tsTsaName));
+ ExpectBufEQ(tstDec.tsa, tsTsaName, (int)sizeof(tsTsaName));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_CheckGenTime(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
+ !defined(USER_TIME) && !defined(TIME_OVERRIDES) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ byte enc[256];
+ word32 sz = (word32)sizeof(enc);
+
+ /* TSTInfo with the current time. */
+ test_tsp_set_tstinfo(&tst);
+ tst.genTime = NULL;
+ tst.genTimeSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, enc, &sz), 0);
+ ExpectIntEQ(wc_TspTstInfo_Decode(&tstDec, enc, sz), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_CheckGenTime(NULL, 10),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_CheckGenTime(&tst, 10),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* The current time is within tolerance. */
+ ExpectIntEQ(wc_TspTstInfo_CheckGenTime(&tstDec, 10), 0);
+
+ /* A time in the past is not. */
+ tst.genTime = (const byte*)"19700101000000Z";
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_CheckGenTime(&tst, 60),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* A time in the future is not. */
+ tst.genTime = (const byte*)"20990101000000Z";
+ ExpectIntEQ(wc_TspTstInfo_CheckGenTime(&tst, 60),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* An invalid time string. */
+ tst.genTimeSz = 14;
+ ExpectIntEQ(wc_TspTstInfo_CheckGenTime(&tst, 60),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_GetSetGenTimeAsTime(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && !defined(NO_ASN_TIME) && \
+ defined(WOLFSSL_TSP_VERIFIER) && defined(WOLFSSL_TSP_RESPONDER)
+ TspTstInfo tst;
+ byte buf[ASN_GENERALIZED_TIME_SIZE];
+ const byte* out = NULL;
+ word32 outSz = 0;
+ time_t t = 0;
+
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+
+ /* Set: bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_SetGenTimeAsTime(NULL, 0, buf,
+ (word32)sizeof(buf)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetGenTimeAsTime(&tst, 0, NULL,
+ (word32)sizeof(buf)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Buffer too small for the GeneralizedTime string. */
+ ExpectIntEQ(wc_TspTstInfo_SetGenTimeAsTime(&tst, 0, buf,
+ ASN_GENERALIZED_TIME_SIZE - 1), WC_NO_ERR_TRACE(BUFFER_E));
+
+ /* The Unix epoch formats as a known GeneralizedTime and references buf. */
+ ExpectIntEQ(wc_TspTstInfo_SetGenTimeAsTime(&tst, 0, buf,
+ (word32)sizeof(buf)), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetGenTime(&tst, &out, &outSz), 0);
+ ExpectPtrEq(out, buf);
+ ExpectIntEQ(outSz, 15);
+ ExpectBufEQ(out, "19700101000000Z", 15);
+ /* Get round trips back to the time_t. */
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t), 0);
+ ExpectIntEQ((long)t, 0);
+
+ /* A later time round trips through set then get. */
+ ExpectIntEQ(wc_TspTstInfo_SetGenTimeAsTime(&tst, (time_t)1700000000, buf,
+ (word32)sizeof(buf)), 0);
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t), 0);
+ ExpectIntEQ((long)t, 1700000000L);
+
+ /* Get ignores a fraction of a second. */
+ tst.genTime = (const byte*)"19700101000000.5Z";
+ tst.genTimeSz = 17;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t), 0);
+ ExpectIntEQ((long)t, 0);
+
+ /* Get: bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(NULL, &t),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Get: an invalid time string. */
+ tst.genTime = (const byte*)"19700101000000";
+ tst.genTimeSz = 14;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ /* Get: out-of-range fields are rejected, not used to index a table.
+ * A month of 00 or 13-99 would otherwise read outside the month table. */
+ tst.genTime = (const byte*)"19700001000000Z"; /* month 00 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ tst.genTime = (const byte*)"19701301000000Z"; /* month 13 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ tst.genTime = (const byte*)"19709901000000Z"; /* month 99 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ tst.genTime = (const byte*)"19700100000000Z"; /* day 00 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ tst.genTime = (const byte*)"19700101240000Z"; /* hour 24 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ tst.genTime = (const byte*)"19700101006000Z"; /* minute 60 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ tst.genTime = (const byte*)"19700101000061Z"; /* second 61 */
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(ASN_PARSE_E));
+ /* A leap second (60) is accepted. */
+ tst.genTime = (const byte*)"19700101000060Z";
+ tst.genTimeSz = 15;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t), 0);
+ /* Get: no time present. */
+ tst.genTime = NULL;
+ tst.genTimeSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_GetGenTimeAsTime(&tst, &t),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Init(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_RESPONDER)
+ TspResponse resp;
+
+ ExpectIntEQ(wc_TspResponse_Init(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Setting the status of a NULL response is rejected. */
+ ExpectIntEQ(wc_TspResponse_SetStatus(NULL, WC_TSP_PKISTATUS_GRANTED, NULL,
+ 0, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ XMEMSET(&resp, 0xa5, sizeof(TspResponse));
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ ExpectIntEQ(resp.status, WC_TSP_PKISTATUS_GRANTED);
+ ExpectNull(resp.statusString);
+ ExpectIntEQ(resp.failInfo, 0);
+ ExpectNull(resp.token);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspGetSetStatus(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ static const char statusText[] = "rejected by policy";
+ TspResponse resp;
+ word32 status = 0;
+ const byte* str = NULL;
+ word32 strSz = 0;
+ word32 failInfo = 0;
+
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+
+ /* Bad arguments - NULL response. */
+ ExpectIntEQ(wc_TspResponse_GetStatus(NULL, &status, &str, &strSz,
+ &failInfo), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspResponse_SetStatus(NULL, WC_TSP_PKISTATUS_GRANTED, NULL,
+ 0, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Set status, string and failure information - string is assigned. */
+ ExpectIntEQ(wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_REJECTION,
+ (const byte*)statusText, (word32)XSTRLEN(statusText),
+ WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_SYSTEM_FAILURE), 0);
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(resp.status, WC_TSP_PKISTATUS_REJECTION);
+ ExpectPtrEq(resp.statusString, statusText);
+ ExpectIntEQ(resp.statusStringSz, (word32)XSTRLEN(statusText));
+ ExpectIntEQ(resp.failInfo,
+ WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_SYSTEM_FAILURE);
+ }
+
+ /* Get returns each value asked for. */
+ ExpectIntEQ(wc_TspResponse_GetStatus(&resp, &status, &str, &strSz,
+ &failInfo), 0);
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(status, WC_TSP_PKISTATUS_REJECTION);
+ ExpectPtrEq(str, statusText);
+ ExpectIntEQ(strSz, (word32)XSTRLEN(statusText));
+ ExpectIntEQ(failInfo, WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_SYSTEM_FAILURE);
+ }
+
+ /* All outputs are optional - NULLs retrieve nothing. */
+ ExpectIntEQ(wc_TspResponse_GetStatus(&resp, NULL, NULL, NULL, NULL), 0);
+
+ /* A NULL string clears the string and its length. */
+ ExpectIntEQ(wc_TspResponse_SetStatus(&resp, WC_TSP_PKISTATUS_GRANTED, NULL,
+ 5, 0), 0);
+ str = (const byte*)statusText;
+ strSz = 99;
+ ExpectIntEQ(wc_TspResponse_GetStatus(&resp, &status, &str, &strSz,
+ &failInfo), 0);
+ if (EXPECT_SUCCESS()) {
+ ExpectIntEQ(status, WC_TSP_PKISTATUS_GRANTED);
+ ExpectNull(str);
+ ExpectIntEQ(strSz, 0);
+ ExpectIntEQ(failInfo, 0);
+ }
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspStrings(void)
+{
+ EXPECT_DECLS;
+#ifdef WOLFSSL_TSP
+ /* Each PKIStatus has a description. */
+ ExpectStrEQ(wc_TspStatus_ToString(WC_TSP_PKISTATUS_GRANTED), "granted");
+ ExpectStrEQ(wc_TspStatus_ToString(WC_TSP_PKISTATUS_REJECTION), "rejection");
+ ExpectNotNull(wc_TspStatus_ToString(WC_TSP_PKISTATUS_GRANTED_WITH_MODS));
+ ExpectNotNull(wc_TspStatus_ToString(WC_TSP_PKISTATUS_WAITING));
+ ExpectNotNull(wc_TspStatus_ToString(WC_TSP_PKISTATUS_REVOCATION_WARNING));
+ ExpectNotNull(wc_TspStatus_ToString(
+ WC_TSP_PKISTATUS_REVOCATION_NOTIFICATION));
+ /* An unknown status still returns a string. */
+ ExpectStrEQ(wc_TspStatus_ToString(99), "unknown status");
+
+ /* Each PKIFailureInfo flag has a description. */
+ ExpectStrEQ(wc_TspFailInfo_ToString(WC_TSP_FAIL_SYSTEM_FAILURE),
+ "the request cannot be handled due to system failure");
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_BAD_ALG));
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_BAD_REQUEST));
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_BAD_DATA_FORMAT));
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_TIME_NOT_AVAILABLE));
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_UNACCEPTED_POLICY));
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_UNACCEPTED_EXTENSION));
+ ExpectNotNull(wc_TspFailInfo_ToString(WC_TSP_FAIL_ADD_INFO_NOT_AVAILABLE));
+ /* An unknown or empty failure information still returns a string. */
+ ExpectStrEQ(wc_TspFailInfo_ToString(0), "unknown failure information");
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Encode(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* All failure information flags. RFC 3161, 2.4.2. */
+ static const word32 failInfoFlags[] = {
+ WC_TSP_FAIL_BAD_ALG,
+ WC_TSP_FAIL_BAD_REQUEST,
+ WC_TSP_FAIL_BAD_DATA_FORMAT,
+ WC_TSP_FAIL_TIME_NOT_AVAILABLE,
+ WC_TSP_FAIL_UNACCEPTED_POLICY,
+ WC_TSP_FAIL_UNACCEPTED_EXTENSION,
+ WC_TSP_FAIL_ADD_INFO_NOT_AVAILABLE,
+ WC_TSP_FAIL_SYSTEM_FAILURE
+ };
+ /* Stand-in for a token - decoded as opaque DER. */
+ static const byte token[] = { 0x30, 0x03, 0x02, 0x01, 0x05 };
+ static const char statusText[] = "rejected by policy";
+ TspResponse resp;
+ TspResponse respDec;
+ byte enc[256];
+ word32 encSz;
+ word32 sz;
+ word32 i;
+
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+
+ /* Bad arguments. */
+ encSz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspResponse_Encode(NULL, enc, &encSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, enc, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Smallest response. */
+ encSz = 0;
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, NULL, &encSz), 0);
+ ExpectIntGT(encSz, 0);
+ /* Buffer too small. */
+ sz = encSz - 1;
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, enc, &sz),
+ WC_NO_ERR_TRACE(BUFFER_E));
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, enc, &sz), 0);
+ ExpectIntEQ(sz, encSz);
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, enc, sz), 0);
+ ExpectIntEQ(respDec.status, WC_TSP_PKISTATUS_GRANTED);
+ ExpectNull(respDec.statusString);
+ ExpectIntEQ(respDec.failInfo, 0);
+ ExpectNull(respDec.token);
+
+ /* Rejection with status string and each failure information flag. */
+ resp.status = WC_TSP_PKISTATUS_REJECTION;
+ resp.statusString = (const byte*)statusText;
+ resp.statusStringSz = (word32)XSTRLEN(statusText);
+ for (i = 0; i < (word32)(sizeof(failInfoFlags) / sizeof(*failInfoFlags));
+ i++) {
+ resp.failInfo = failInfoFlags[i];
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, enc, &sz), 0);
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, enc, sz), 0);
+ ExpectIntEQ(respDec.status, WC_TSP_PKISTATUS_REJECTION);
+ ExpectIntEQ(respDec.failInfo, failInfoFlags[i]);
+ ExpectIntEQ(respDec.statusStringSz, (word32)XSTRLEN(statusText));
+ ExpectBufEQ(respDec.statusString, statusText,
+ (int)XSTRLEN(statusText));
+ }
+ /* Multiple failure information flags. */
+ resp.failInfo = WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_BAD_REQUEST;
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, enc, &sz), 0);
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, enc, sz), 0);
+ ExpectIntEQ(respDec.failInfo,
+ WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_BAD_REQUEST);
+
+ /* Granted with a token. */
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.token = token;
+ resp.tokenSz = (word32)sizeof(token);
+ sz = (word32)sizeof(enc);
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, enc, &sz), 0);
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, enc, sz), 0);
+ ExpectIntEQ(respDec.status, WC_TSP_PKISTATUS_GRANTED);
+ ExpectIntEQ(respDec.tokenSz, (word32)sizeof(token));
+ ExpectBufEQ(respDec.token, token, (int)sizeof(token));
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Decode(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_VERIFIER)
+ /* Rejection with failure information of badRequest - OpenSSL style BIT
+ * STRING with unused bits. */
+ static const byte failInfoDer[] = {
+ 0x30, 0x09, 0x30, 0x07,
+ 0x02, 0x01, 0x02,
+ 0x03, 0x02, 0x05, 0x20
+ };
+ /* Rejection with a PKIFreeText holding two strings. */
+ static const byte twoStrDer[] = {
+ 0x30, 0x12, 0x30, 0x10,
+ 0x02, 0x01, 0x02,
+ 0x30, 0x0b,
+ 0x0c, 0x03, 'a', 'b', 'c',
+ 0x0c, 0x04, 'd', 'e', 'f', 'g'
+ };
+ /* Invalid: PKIFreeText must have at least one string. */
+ static const byte emptyStrDer[] = {
+ 0x30, 0x07, 0x30, 0x05,
+ 0x02, 0x01, 0x02,
+ 0x30, 0x00
+ };
+ /* Failure information with more bits than recognized - invalid. */
+ static const byte longFailDer[] = {
+ 0x30, 0x0d, 0x30, 0x0b,
+ 0x02, 0x01, 0x02,
+ 0x03, 0x06, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80
+ };
+ /* Present but empty failInfo BIT STRING - only the unused bits byte. */
+ static const byte emptyFailDer[] = {
+ 0x30, 0x08, 0x30, 0x06,
+ 0x02, 0x01, 0x02,
+ 0x03, 0x01, 0x00
+ };
+ TspResponse respDec;
+ byte enc[32];
+ word32 status = 0;
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspResponse_Decode(NULL, failInfoDer,
+ (word32)sizeof(failInfoDer)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Getting the status of a NULL response is rejected. */
+ ExpectIntEQ(wc_TspResponse_GetStatus(NULL, &status, NULL, NULL, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, NULL,
+ (word32)sizeof(failInfoDer)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, failInfoDer, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Failure information with unused bits in BIT STRING. */
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, failInfoDer,
+ (word32)sizeof(failInfoDer)), 0);
+ ExpectIntEQ(respDec.status, WC_TSP_PKISTATUS_REJECTION);
+ ExpectIntEQ(respDec.failInfo, WC_TSP_FAIL_BAD_REQUEST);
+ ExpectNull(respDec.statusString);
+ ExpectNull(respDec.token);
+
+ /* First string of PKIFreeText returned. */
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, twoStrDer,
+ (word32)sizeof(twoStrDer)), 0);
+ ExpectIntEQ(respDec.statusStringSz, 3);
+ ExpectBufEQ(respDec.statusString, "abc", 3);
+
+ /* Empty PKIFreeText invalid. */
+ ExpectIntLT(wc_TspResponse_Decode(&respDec, emptyStrDer,
+ (word32)sizeof(emptyStrDer)), 0);
+
+ /* Failure information bits past 31 are not supported. */
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, longFailDer,
+ (word32)sizeof(longFailDer)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+
+ /* Present but empty failInfo BIT STRING (only the unused bits byte) has no
+ * data bytes and is rejected - the shift over the encoded length is never
+ * reached, so it cannot shift a word32 by 32. */
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, emptyFailDer,
+ (word32)sizeof(emptyFailDer)), WC_NO_ERR_TRACE(ASN_PARSE_E));
+
+ /* Truncated encoding. */
+ ExpectIntLT(wc_TspResponse_Decode(&respDec, failInfoDer,
+ (word32)sizeof(failInfoDer) - 1), 0);
+ /* Trailing data not allowed. */
+ XMEMCPY(enc, failInfoDer, sizeof(failInfoDer));
+ enc[sizeof(failInfoDer)] = 0x00;
+ ExpectIntLT(wc_TspResponse_Decode(&respDec, enc,
+ (word32)sizeof(failInfoDer) + 1), 0);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_CheckRequest(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* Nonce with extra leading zero byte. */
+ static const byte paddedNonce[] = {
+ 0x00, 0xc3, 0x5a, 0x10, 0x42, 0x77, 0x08, 0x99, 0x01
+ };
+ static const byte otherNonce[] = { 0x01, 0x02 };
+ /* Nonce of zero in different number of bytes. */
+ static const byte zeroNonce1[] = { 0x00 };
+ static const byte zeroNonce2[] = { 0x00, 0x00 };
+ TspTstInfo tst;
+ TspRequest req;
+
+ /* Matching request and TSTInfo - no encoding required for check. */
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ test_tsp_set_hash(&req.imprint);
+ XMEMCPY(req.policy, tsPolicy, sizeof(tsPolicy));
+ req.policySz = (word32)sizeof(tsPolicy);
+ XMEMCPY(req.nonce, tsNonce, sizeof(tsNonce));
+ req.nonceSz = (word32)sizeof(tsNonce);
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+ tst.policy = tsPolicy;
+ tst.policySz = (word32)sizeof(tsPolicy);
+ test_tsp_set_hash(&tst.imprint);
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(NULL, &req),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req), 0);
+
+ /* Only version 1 supported. */
+ tst.version = 2;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(ASN_VERSION_E));
+ tst.version = WC_TSP_VERSION;
+
+ /* Hash algorithm different. */
+ tst.imprint.hashAlgOID = SHA256h + 1;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ tst.imprint.hashAlgOID = SHA256h;
+ /* Hash length different. */
+ tst.imprint.hashSz--;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ tst.imprint.hashSz++;
+ /* Hash different. */
+ tst.imprint.hash[0] ^= 0x80;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ tst.imprint.hash[0] ^= 0x80;
+
+ /* Nonce in request must be in TSTInfo. */
+ tst.nonce = NULL;
+ tst.nonceSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* Nonces compared exactly - same number with leading zero byte does not
+ * match. */
+ tst.nonce = paddedNonce;
+ tst.nonceSz = (word32)sizeof(paddedNonce);
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* Request's nonce with leading zero byte. */
+ XMEMCPY(req.nonce, paddedNonce, sizeof(paddedNonce));
+ req.nonceSz = (word32)sizeof(paddedNonce);
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* Nonce of zero with different lengths. */
+ XMEMCPY(req.nonce, zeroNonce2, sizeof(zeroNonce2));
+ req.nonceSz = (word32)sizeof(zeroNonce2);
+ tst.nonce = zeroNonce1;
+ tst.nonceSz = (word32)sizeof(zeroNonce1);
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ XMEMCPY(req.nonce, tsNonce, sizeof(tsNonce));
+ req.nonceSz = (word32)sizeof(tsNonce);
+ /* Nonce different. */
+ tst.nonce = otherNonce;
+ tst.nonceSz = (word32)sizeof(otherNonce);
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+ /* Nonce in TSTInfo and not request is allowed. */
+ req.nonceSz = 0;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req), 0);
+ XMEMCPY(req.nonce, tsNonce, sizeof(tsNonce));
+ req.nonceSz = (word32)sizeof(tsNonce);
+
+ /* Policy in request must be in TSTInfo. */
+ tst.policy = NULL;
+ tst.policySz = 0;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* Policy different. */
+ tst.policy = tsNonce;
+ tst.policySz = (word32)sizeof(tsNonce);
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ tst.policy = tsPolicy;
+ tst.policySz = (word32)sizeof(tsPolicy);
+ /* Policy not checked when not requested. */
+ req.policySz = 0;
+ ExpectIntEQ(wc_TspTstInfo_CheckRequest(&tst, &req), 0);
+#endif
+ return EXPECT_RESULT();
+}
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER) && \
+ (!defined(NO_RSA) || defined(HAVE_ECC))
+/* Create a PKCS7 object ready to sign with the certificate and key using the
+ * given encryption algorithm. */
+static int test_tsp_new_signer_ex(wc_PKCS7** pkcs7, WC_RNG* rng,
+ const byte* cert, word32 certSz, const byte* key, word32 keySz,
+ int encryptOID)
+{
+ EXPECT_DECLS;
+
+ ExpectNotNull(*pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(*pkcs7, (byte*)cert, certSz), 0);
+ if (EXPECT_SUCCESS()) {
+ (*pkcs7)->rng = rng;
+ (*pkcs7)->hashOID = SHA256h;
+ (*pkcs7)->encryptOID = encryptOID;
+ (*pkcs7)->privateKey = (byte*)key;
+ (*pkcs7)->privateKeySz = keySz;
+ }
+
+ return EXPECT_RESULT();
+}
+
+#ifndef NO_RSA
+/* Create a PKCS7 object ready to sign with the RSA certificate and key. */
+static int test_tsp_new_signer(wc_PKCS7** pkcs7, WC_RNG* rng, const byte* cert,
+ word32 certSz, const byte* key, word32 keySz)
+{
+ return test_tsp_new_signer_ex(pkcs7, rng, cert, certSz, key, keySz, RSAk);
+}
+
+/* Create a PKCS7 object ready to sign as the TSA. */
+static int test_tsp_new_tsa_signer(wc_PKCS7** pkcs7, WC_RNG* rng)
+{
+ return test_tsp_new_signer(pkcs7, rng, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048);
+}
+#endif /* !NO_RSA */
+
+/* Build a standard time-stamp token signed with the given certificate and key.
+ * Encapsulates the signing PKCS7 so verify tests need only their own object. */
+static int test_tsp_make_token(byte* token, word32* tokenSz, const byte* cert,
+ word32 certSz, const byte* key, word32 keySz, int encryptOID, WC_RNG* rng)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+
+ test_tsp_set_tstinfo(&tst);
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+
+ ExpectIntEQ(test_tsp_new_signer_ex(&pkcs7, rng, cert, certSz, key, keySz,
+ encryptOID), TEST_SUCCESS);
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+#endif
+
+int test_wc_TspTstInfo_SignWithPkcs7(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ test_tsp_set_tstinfo(&tst);
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, &rng), TEST_SUCCESS);
+
+ /* Bad arguments. */
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, NULL, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(NULL, pkcs7, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, NULL, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_SignWithPkcs7_create(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Create a token and check the TSTInfo it holds. */
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ RSAk, &rng), TEST_SUCCESS);
+ ExpectIntGT(tokenSz, 0);
+
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec), 0);
+ ExpectIntEQ(tstDec.version, WC_TSP_VERSION);
+ ExpectBufEQ(tstDec.imprint.hash, tsHashedMsg,
+ (int)sizeof(tsHashedMsg));
+ ExpectIntEQ(tstDec.nonceSz, (word32)sizeof(tsNonce));
+ ExpectBufEQ(tstDec.nonce, tsNonce, (int)sizeof(tsNonce));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_SignWithPkcs7_signer_required(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+ byte token[3072];
+ word32 tokenSz;
+
+ test_tsp_set_tstinfo(&tst);
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+
+ /* Signer's certificate required. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ wc_PKCS7_Free(pkcs7);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_SignWithPkcs7_hash_and_buffer(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ test_tsp_set_tstinfo(&tst);
+ tst.nonce = tsNonce;
+ tst.nonceSz = (word32)sizeof(tsNonce);
+
+ /* Hash algorithm of PKCS7 object must be usable. */
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, &rng), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->hashOID = 0;
+ }
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Output buffer too small - PKCS7 object fields are put back. */
+ if (EXPECT_SUCCESS()) {
+ pkcs7->hashOID = SHA256h;
+ }
+ tokenSz = 16;
+ ExpectIntLT(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz), 0);
+ if (EXPECT_SUCCESS()) {
+ ExpectNull(pkcs7->content);
+ ExpectIntEQ(pkcs7->contentSz, 0);
+ ExpectNull(pkcs7->signedAttribs);
+ ExpectIntEQ(pkcs7->signedAttribsSz, 0);
+ }
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_Sign(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ test_tsp_set_tstinfo(&tst);
+
+ /* Bad arguments. */
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(wc_TspTstInfo_Sign(NULL, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, NULL,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048, 0,
+ tsa_key_der_2048, sizeof_tsa_key_der_2048, WC_PK_TYPE_RSA,
+ WC_HASH_TYPE_SHA256, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, NULL, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, 0, WC_PK_TYPE_RSA,
+ WC_HASH_TYPE_SHA256, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, NULL, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, NULL, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, token, NULL),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Unsupported key type. */
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_DH, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ /* Hash algorithm not available. */
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_NONE, &rng, token, &tokenSz),
+ WC_NO_ERR_TRACE(HASH_TYPE_E));
+
+ /* Create an RSA-signed token and verify it against the TSA certificate. */
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz), 0);
+ ExpectIntGT(tokenSz, 0);
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, &tstDec), 0);
+ ExpectIntEQ(tstDec.version, WC_TSP_VERSION);
+
+#ifdef HAVE_ECC
+ /* Create an ECDSA-signed token and verify it. */
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_ecc_cert_der_256,
+ sizeof_tsa_ecc_cert_der_256, tsa_ecc_key_der_256,
+ sizeof_tsa_ecc_key_der_256, WC_PK_TYPE_ECDSA_SIGN,
+ WC_HASH_TYPE_SHA256, &rng, token, &tokenSz), 0);
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, tsa_ecc_cert_der_256,
+ sizeof_tsa_ecc_cert_der_256, &tstDec), 0);
+ ExpectIntEQ(tstDec.version, WC_TSP_VERSION);
+#endif
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Custom user attribute OID 1.2.5555 with a UTF8String value. */
+static const byte tspCustomOid[] = { 0x06, 0x03, 0x2a, 0xab, 0x33 };
+static const byte tspCustomValue[] = { 0x0c, 0x02, 'h', 'i' };
+
+/* Sign a standard TSTInfo token that also carries the custom user attribute.
+ * Encapsulates the signing PKCS7 so verify tests need only their own object. */
+static int test_tsp_make_attrib_token(byte* token, word32* tokenSz, WC_RNG* rng)
+{
+ EXPECT_DECLS;
+ PKCS7Attrib attrib;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+
+ XMEMSET(&attrib, 0, sizeof(attrib));
+ attrib.oid = tspCustomOid;
+ attrib.oidSz = (word32)sizeof(tspCustomOid);
+ attrib.value = tspCustomValue;
+ attrib.valueSz = (word32)sizeof(tspCustomValue);
+
+ test_tsp_set_tstinfo(&tst);
+
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, rng), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->signedAttribs = &attrib;
+ pkcs7->signedAttribsSz = 1;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+
+#ifdef WOLFSSL_SHA384
+/* Sign a standard TSTInfo token with SHA-384 so the hash algorithm is encoded
+ * in the SigningCertificateV2 attribute. Encapsulates the signing PKCS7. */
+static int test_tsp_make_sha384_token(byte* token, word32* tokenSz, WC_RNG* rng)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+
+ test_tsp_set_tstinfo(&tst);
+
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, rng), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->hashOID = SHA384h;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+#endif
+#endif
+
+int test_wc_TspTstInfo_SignWithPkcs7_attribs(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ PKCS7Attrib attrib;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ XMEMSET(&attrib, 0, sizeof(attrib));
+ attrib.oid = tspCustomOid;
+ attrib.oidSz = (word32)sizeof(tspCustomOid);
+ attrib.value = tspCustomValue;
+ attrib.valueSz = (word32)sizeof(tspCustomValue);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ test_tsp_set_tstinfo(&tst);
+
+ /* Create a token with a user's signed attribute as well. */
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, &rng), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->signedAttribs = &attrib;
+ pkcs7->signedAttribsSz = 1;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz), 0);
+ /* PKCS7 object's signed attributes are put back. */
+ if (EXPECT_SUCCESS()) {
+ ExpectPtrEq(pkcs7->signedAttribs, &attrib);
+ ExpectIntEQ(pkcs7->signedAttribsSz, 1);
+ }
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_SignWithPkcs7_attribs_verify(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* id-aa-signingCertificateV2: 1.2.840.113549.1.9.16.2.47. */
+ static const byte signCertV2Oid[] = {
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x2f
+ };
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+ byte attrValue[128];
+ word32 attrValueSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ ExpectIntEQ(test_tsp_make_attrib_token(token, &tokenSz, &rng),
+ TEST_SUCCESS);
+
+ /* Both the user's attribute and SigningCertificateV2 are in token. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL), 0);
+ attrValueSz = (word32)sizeof(attrValue);
+ ExpectIntEQ(wc_PKCS7_GetAttributeValue(pkcs7, tspCustomOid + 2,
+ (word32)sizeof(tspCustomOid) - 2, attrValue, &attrValueSz),
+ (int)sizeof(tspCustomValue));
+ ExpectBufEQ(attrValue, tspCustomValue, (int)sizeof(tspCustomValue));
+ attrValueSz = (word32)sizeof(attrValue);
+ ExpectIntGT(wc_PKCS7_GetAttributeValue(pkcs7, signCertV2Oid,
+ (word32)sizeof(signCertV2Oid), attrValue, &attrValueSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_SignWithPkcs7_attribs_sha384(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+#ifdef WOLFSSL_SHA384
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Not SHA-256: hash algorithm encoded in SigningCertificateV2. */
+ ExpectIntEQ(test_tsp_make_sha384_token(token, &tokenSz, &rng),
+ TEST_SUCCESS);
+
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+#endif
+ return EXPECT_RESULT();
+}
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Encode a CMS SignedData with DATA content - not a time-stamp token.
+ * Encapsulates the signing PKCS7 so verify tests need only their own object. */
+static int test_tsp_make_data_token(byte* token, word32* tokenSz, WC_RNG* rng)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ byte data[] = { 0x01, 0x02, 0x03, 0x04 };
+ int sz = 0;
+
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, rng), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->content = data;
+ pkcs7->contentSz = (word32)sizeof(data);
+ pkcs7->contentOID = DATA;
+ }
+ ExpectIntGT(sz = wc_PKCS7_EncodeSignedData(pkcs7, token, *tokenSz), 0);
+ if (EXPECT_SUCCESS()) {
+ *tokenSz = (word32)sz;
+ }
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+
+/* Build a standard time-stamp token that does not include any certificates.
+ * Encapsulates the signing PKCS7 so verify tests need only their own object. */
+static int test_tsp_make_nocerts_token(byte* token, word32* tokenSz,
+ WC_RNG* rng)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+
+ test_tsp_set_tstinfo(&tst);
+
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, rng), TEST_SUCCESS);
+ if (EXPECT_SUCCESS()) {
+ /* Do not include certificates in the token. */
+ pkcs7->noCerts = 1;
+ }
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+#endif
+
+int test_wc_TspTstInfo_VerifyWithPKCS7(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ RSAk, &rng), TEST_SUCCESS);
+
+ /* Bad arguments. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(NULL, token, tokenSz, &tstDec),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, NULL, tokenSz, &tstDec),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, 0, &tstDec),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* TSTInfo object is optional. */
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_modified(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ RSAk, &rng), TEST_SUCCESS);
+
+ /* Modified token does not verify. */
+ token[tokenSz - 5] ^= 0x80;
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntLT(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec), 0);
+ token[tokenSz - 5] ^= 0x80;
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_no_signer(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* Token without a SignerInfo does not verify. */
+ static const byte noSignerToken[] = {
+ 0x30, 0x25, /* ContentInfo */
+ 0x06, 0x09, /* signedData */
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02,
+ 0xa0, 0x18, /* content */
+ 0x30, 0x16, /* SignedData */
+ 0x02, 0x01, 0x03, /* version 3 */
+ 0x31, 0x00, /* digestAlgs */
+ 0x30, 0x0d, /* encapContent */
+ 0x06, 0x0b, /* id-ct-TSTInfo */
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x01, 0x04,
+ 0x31, 0x00 /* signerInfos */
+ };
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tstDec;
+ byte noSigner[sizeof(noSignerToken)];
+
+ XMEMCPY(noSigner, noSignerToken, sizeof(noSignerToken));
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, noSigner,
+ (word32)sizeof(noSigner), &tstDec),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ wc_PKCS7_Free(pkcs7);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_not_tst(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* CMS SignedData that is not a time-stamp token. */
+ ExpectIntEQ(test_tsp_make_data_token(token, &tokenSz, &rng), TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec),
+ WC_NO_ERR_TRACE(PKCS7_OID_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_bad_eku(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Token signed with a certificate not for time-stamping. */
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, client_cert_der_2048,
+ sizeof_client_cert_der_2048, client_key_der_2048,
+ sizeof_client_key_der_2048, RSAk, &rng), TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec),
+ WC_NO_ERR_TRACE(EXTKEYUSAGE_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_bad_ku(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Token signed with a time-stamping certificate with a key usage that
+ * is not signing. */
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_bad_ku_cert_der_2048,
+ sizeof_tsa_bad_ku_cert_der_2048, tsa_key_der_2048,
+ sizeof_tsa_key_der_2048, RSAk, &rng), TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec),
+ WC_NO_ERR_TRACE(KEYUSAGE_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_extra_eku(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Token signed with a time-stamping certificate that has an extra
+ * extended key usage - the extra purpose is an unrecognized OID so the
+ * time-stamping bit is still the only one set, but it is not the sole
+ * KeyPurposeId, so verification must reject it. */
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_extra_eku_cert_der_2048,
+ sizeof_tsa_extra_eku_cert_der_2048, tsa_key_der_2048,
+ sizeof_tsa_key_der_2048, RSAk, &rng), TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec),
+ WC_NO_ERR_TRACE(EXTKEYUSAGE_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_nocerts(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Token without certificates - certReq was FALSE in the request. */
+ ExpectIntEQ(test_tsp_make_nocerts_token(token, &tokenSz, &rng),
+ TEST_SUCCESS);
+ /* Cannot verify without the TSA's certificate. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntLT(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_nocerts_supplied(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Token without certificates - certReq was FALSE in the request. */
+ ExpectIntEQ(test_tsp_make_nocerts_token(token, &tokenSz, &rng),
+ TEST_SUCCESS);
+ /* Verifies when the TSA's certificate is supplied. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, (byte*)tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+/* Standard verify gate for the TspResponse_Verify split. */
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Build a token that includes the TSA's certificate and wrap it in a granted
+ * response. The signing PKCS7 is created and freed in the helper. */
+static int test_tsp_make_granted_resp(TspResponse* resp, byte* token,
+ word32* tokenSz, WC_RNG* rng)
+{
+ EXPECT_DECLS;
+
+ ExpectIntEQ(test_tsp_make_token(token, tokenSz, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ RSAk, rng), TEST_SUCCESS);
+ ExpectIntEQ(wc_TspResponse_Init(resp), 0);
+ if (EXPECT_SUCCESS()) {
+ resp->status = WC_TSP_PKISTATUS_GRANTED;
+ resp->token = token;
+ resp->tokenSz = *tokenSz;
+ }
+
+ return EXPECT_RESULT();
+}
+#endif
+
+int test_wc_TspResponse_Verify(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(test_tsp_make_granted_resp(&resp, token, &tokenSz, &rng),
+ TEST_SUCCESS);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspResponse_Verify(NULL, NULL, 0, &tstDec),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Verifies with the certificate in the token - no certificate needed.
+ * The returned TSTInfo references the response's token, which is still
+ * available after the call. */
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, NULL, 0, &tstDec), 0);
+ ExpectIntEQ(tstDec.version, 1);
+ ExpectIntEQ(tstDec.genTimeSz, (word32)sizeof(tsGenTime) - 1);
+ ExpectBufEQ(tstDec.genTime, tsGenTime, (int)sizeof(tsGenTime) - 1);
+ ExpectIntEQ(tstDec.serialSz, (word32)sizeof(tsSerial));
+ ExpectBufEQ(tstDec.serial, tsSerial, (int)sizeof(tsSerial));
+ /* The TSTInfo object is optional. */
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, NULL, 0, NULL), 0);
+
+ /* The signer matches the trusted TSA certificate. */
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, &tstDec), 0);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Verify_wrong_cert(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(test_tsp_make_granted_resp(&resp, token, &tokenSz, &rng),
+ TEST_SUCCESS);
+
+ /* A different trusted certificate is not the signer - the token is signed
+ * by the TSA but not the one trusted. This certificate is a different
+ * length to the signer's. */
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, client_cert_der_2048,
+ sizeof_client_cert_der_2048, &tstDec),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* A certificate of the same length as the signer's but with a different
+ * public key - the trust check is a byte comparison, not just length. */
+ {
+ byte badCert[sizeof_tsa_cert_der_2048];
+
+ XMEMCPY(badCert, tsa_cert_der_2048, sizeof(badCert));
+ /* Corrupt a byte of the public key. */
+ badCert[500] ^= 0xff;
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, badCert,
+ (word32)sizeof(badCert), &tstDec), WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ }
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Verify_status(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(test_tsp_make_granted_resp(&resp, token, &tokenSz, &rng),
+ TEST_SUCCESS);
+
+ /* A response that was not granted has no token to trust. */
+ resp.status = WC_TSP_PKISTATUS_REJECTION;
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, NULL, 0, &tstDec),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ resp.status = WC_TSP_PKISTATUS_GRANTED_WITH_MODS;
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, NULL, 0, &tstDec), 0);
+
+ /* A granted response with no token does not verify. */
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = NULL;
+ resp.tokenSz = 0;
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, NULL, 0, &tstDec),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Verify_modified(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ ExpectIntEQ(test_tsp_make_granted_resp(&resp, token, &tokenSz, &rng),
+ TEST_SUCCESS);
+
+ /* A modified token does not verify. */
+ if (EXPECT_SUCCESS()) {
+ token[tokenSz - 5] ^= 0x80;
+ }
+ ExpectIntLT(wc_TspResponse_Verify(&resp, NULL, 0, &tstDec), 0);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_Verify_nocerts(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* Token without certificates - certReq was FALSE in the request. */
+ ExpectIntEQ(test_tsp_make_nocerts_token(token, &tokenSz, &rng),
+ TEST_SUCCESS);
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+
+ /* Cannot verify without the TSA's certificate. */
+ ExpectIntLT(wc_TspResponse_Verify(&resp, NULL, 0, &tstDec), 0);
+ /* Verifies when the TSA's certificate is supplied. */
+ ExpectIntEQ(wc_TspResponse_Verify(&resp, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, &tstDec), 0);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspResponse_VerifyData(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+ static const byte data[] = "wolfSSL RFC 3161 time-stamp data";
+ byte dataHash[WC_SHA256_DIGEST_SIZE];
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* The hash of the data is the token's message imprint. */
+ ExpectIntEQ(wc_Sha256Hash(data, (word32)sizeof(data) - 1, dataHash), 0);
+
+ test_tsp_set_tstinfo(&tst);
+ tst.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(tst.imprint.hash, dataHash, sizeof(dataHash));
+ tst.imprint.hashSz = (word32)sizeof(dataHash);
+
+ /* A token signed by the TSA over the hash of the data. */
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, &rng), TEST_SUCCESS);
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspResponse_VerifyData(NULL, NULL, 0, data,
+ (word32)sizeof(data) - 1, &tstDec), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspResponse_VerifyData(&resp, NULL, 0, NULL, 0, &tstDec),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Verifies the token and that it is over the data - no hashing by the
+ * caller. */
+ ExpectIntEQ(wc_TspResponse_VerifyData(&resp, NULL, 0, data,
+ (word32)sizeof(data) - 1, &tstDec), 0);
+ ExpectIntEQ(tstDec.imprint.hashSz, (word32)sizeof(dataHash));
+ /* The TSTInfo object is optional. */
+ ExpectIntEQ(wc_TspResponse_VerifyData(&resp, NULL, 0, data,
+ (word32)sizeof(data) - 1, NULL), 0);
+
+ /* Different data does not match the message imprint. */
+ ExpectIntEQ(wc_TspResponse_VerifyData(&resp, NULL, 0,
+ (const byte*)"different data", 14, &tstDec),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+
+ /* wc_TspTstInfo_VerifyData directly - bad args and match/mismatch. */
+ ExpectIntEQ(wc_TspTstInfo_VerifyData(NULL, data, (word32)sizeof(data) - 1),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_VerifyData(&tstDec, NULL, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_VerifyData(&tstDec, data,
+ (word32)sizeof(data) - 1), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyData(&tstDec, (const byte*)"x", 1),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* An unknown imprint hash algorithm cannot be used to hash the data. */
+ {
+ TspTstInfo tstBad = tstDec;
+
+ tstBad.imprint.hashAlgOID = 0; /* not a known hash OID */
+ ExpectIntEQ(wc_TspTstInfo_VerifyData(&tstBad, data,
+ (word32)sizeof(data) - 1), WC_NO_ERR_TRACE(HASH_TYPE_E));
+ /* An imprint length that doesn't match the algorithm's digest. */
+ tstBad = tstDec;
+ tstBad.imprint.hashSz = 16; /* SHA-256 is 32 bytes */
+ ExpectIntEQ(wc_TspTstInfo_VerifyData(&tstBad, data,
+ (word32)sizeof(data) - 1), WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ }
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_SetFromRequest(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ WC_RNG rng;
+ TspRequest req;
+ TspRequest reqDec;
+ TspTstInfo tst;
+ TspResponse resp;
+ TspResponse respDec;
+ TspTstInfo tstDec;
+ byte reqDer[256];
+ word32 reqDerSz = (word32)sizeof(reqDer);
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+ byte respDer[3072];
+ word32 respDerSz = (word32)sizeof(respDer);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Requester: build and encode a request with an imprint and a nonce. */
+ ExpectIntEQ(wc_TspRequest_Init(&req), 0);
+ test_tsp_set_hash(&req.imprint);
+ XMEMCPY(req.nonce, tsNonce, sizeof(tsNonce));
+ req.nonceSz = (word32)sizeof(tsNonce);
+ ExpectIntEQ(wc_TspRequest_Encode(&req, reqDer, &reqDerSz), 0);
+
+ /* TSA: decode the request. */
+ ExpectIntEQ(wc_TspRequest_Decode(&reqDec, reqDer, reqDerSz), 0);
+
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_SetFromRequest(NULL, &reqDec, tsPolicy,
+ (word32)sizeof(tsPolicy), tsSerial, (word32)sizeof(tsSerial), tsGenTime,
+ (word32)sizeof(tsGenTime) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetFromRequest(&tst, NULL, tsPolicy,
+ (word32)sizeof(tsPolicy), tsSerial, (word32)sizeof(tsSerial), tsGenTime,
+ (word32)sizeof(tsGenTime) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetFromRequest(&tst, &reqDec, NULL, 0, tsSerial,
+ (word32)sizeof(tsSerial), tsGenTime, (word32)sizeof(tsGenTime) - 1),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_SetFromRequest(&tst, &reqDec, tsPolicy,
+ (word32)sizeof(tsPolicy), NULL, 0, tsGenTime,
+ (word32)sizeof(tsGenTime) - 1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* Set the TSTInfo from the request and the TSA's values - the request's
+ * message imprint and nonce are echoed. */
+ ExpectIntEQ(wc_TspTstInfo_SetFromRequest(&tst, &reqDec, tsPolicy,
+ (word32)sizeof(tsPolicy), tsSerial, (word32)sizeof(tsSerial), tsGenTime,
+ (word32)sizeof(tsGenTime) - 1), 0);
+ ExpectIntEQ(tst.imprint.hashSz, (word32)sizeof(tsHashedMsg));
+ ExpectBufEQ(tst.imprint.hash, tsHashedMsg, (int)sizeof(tsHashedMsg));
+ ExpectIntEQ(tst.nonceSz, (word32)sizeof(tsNonce));
+ ExpectIntEQ(tst.policySz, (word32)sizeof(tsPolicy));
+ ExpectIntEQ(tst.serialSz, (word32)sizeof(tsSerial));
+ ExpectIntEQ(tst.genTimeSz, (word32)sizeof(tsGenTime) - 1);
+
+ /* Sign the TSTInfo and wrap it in a granted response. */
+ ExpectIntEQ(wc_TspTstInfo_Sign(&tst, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
+ WC_PK_TYPE_RSA, WC_HASH_TYPE_SHA256, &rng, token, &tokenSz), 0);
+ ExpectIntEQ(wc_TspResponse_Init(&resp), 0);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ ExpectIntEQ(wc_TspResponse_Encode(&resp, respDer, &respDerSz), 0);
+
+ /* The response verifies and echoes the request's imprint and nonce. */
+ ExpectIntEQ(wc_TspResponse_Decode(&respDec, respDer, respDerSz), 0);
+ ExpectIntEQ(respDec.status, WC_TSP_PKISTATUS_GRANTED);
+ ExpectIntEQ(wc_TspResponse_Verify(&respDec, tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, &tstDec), 0);
+ ExpectBufEQ(tstDec.imprint.hash, tsHashedMsg, (int)sizeof(tsHashedMsg));
+ ExpectIntEQ(tstDec.nonceSz, (word32)sizeof(tsNonce));
+ ExpectBufEQ(tstDec.nonce, tsNonce, (int)sizeof(tsNonce));
+ ExpectIntEQ(tstDec.serialSz, (word32)sizeof(tsSerial));
+ ExpectBufEQ(tstDec.serial, tsSerial, (int)sizeof(tsSerial));
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Create a time-stamp token like CMS SignedData with the given signed
+ * attributes - no SigningCertificateV2 added. */
+static int test_tsp_token_with_attribs(WC_RNG* rng, PKCS7Attrib* attribs,
+ word32 attribsSz, byte* out, word32* outSz)
+{
+ EXPECT_DECLS;
+ /* id-ct-TSTInfo: 1.2.840.113549.1.9.16.1.4. */
+ static const byte tstInfoOid[] = {
+ 0x06, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x01, 0x04
+ };
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+ byte tstDer[256];
+ word32 tstDerSz = (word32)sizeof(tstDer);
+ int sz = 0;
+
+ test_tsp_set_tstinfo(&tst);
+ ExpectIntEQ(wc_TspTstInfo_Encode(&tst, tstDer, &tstDerSz), 0);
+
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, rng), TEST_SUCCESS);
+ ExpectIntEQ(wc_PKCS7_SetContentType(pkcs7, (byte*)tstInfoOid,
+ (word32)sizeof(tstInfoOid)), 0);
+ if (EXPECT_SUCCESS()) {
+ pkcs7->content = tstDer;
+ pkcs7->contentSz = tstDerSz;
+ pkcs7->signedAttribs = attribs;
+ pkcs7->signedAttribsSz = attribsSz;
+ }
+ ExpectIntGT(sz = wc_PKCS7_EncodeSignedData(pkcs7, out, *outSz), 0);
+ if (EXPECT_SUCCESS()) {
+ *outSz = (word32)sz;
+ }
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+#endif
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_no_attrib(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Signing certificate attribute must be present. */
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(test_tsp_token_with_attribs(&rng, NULL, 0, token, &tokenSz),
+ TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_bad_hash(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* id-aa-signingCertificateV2: 1.2.840.113549.1.9.16.2.47. */
+ static const byte signCertV2Oid[] = {
+ 0x06, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x2f
+ };
+ /* SigningCertificateV2 with a certHash that matches no certificate. */
+ static const byte badSignCertV2[] = {
+ 0x30, 0x26, 0x30, 0x24, 0x30, 0x22, 0x04, 0x20,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ PKCS7Attrib attrib;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Signing certificate attribute must match the signer's certificate. */
+ XMEMSET(&attrib, 0, sizeof(attrib));
+ attrib.oid = signCertV2Oid;
+ attrib.oidSz = (word32)sizeof(signCertV2Oid);
+ attrib.value = badSignCertV2;
+ attrib.valueSz = (word32)sizeof(badSignCertV2);
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(test_tsp_token_with_attribs(&rng, &attrib, 1, token, &tokenSz),
+ TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_bad_alg(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* id-aa-signingCertificateV2: 1.2.840.113549.1.9.16.2.47. */
+ static const byte signCertV2Oid[] = {
+ 0x06, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x2f
+ };
+ /* SigningCertificateV2 with a hash algorithm - 1.2.3.4 - that is not a
+ * hash. */
+ static const byte badAlgSignCertV2[] = {
+ 0x30, 0x0e, 0x30, 0x0c, 0x30, 0x0a,
+ 0x30, 0x05, 0x06, 0x03, 0x2a, 0x03, 0x04,
+ 0x04, 0x01, 0x00
+ };
+ PKCS7Attrib attrib;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Signing certificate attribute's hash algorithm must be available. */
+ XMEMSET(&attrib, 0, sizeof(attrib));
+ attrib.oid = signCertV2Oid;
+ attrib.oidSz = (word32)sizeof(signCertV2Oid);
+ attrib.value = badAlgSignCertV2;
+ attrib.valueSz = (word32)sizeof(badAlgSignCertV2);
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(test_tsp_token_with_attribs(&rng, &attrib, 1, token, &tokenSz),
+ TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL),
+ WC_NO_ERR_TRACE(HASH_TYPE_E));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_v1(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+#ifndef NO_SHA
+ /* id-aa-signingCertificate: 1.2.840.113549.1.9.16.2.12. */
+ static const byte signCertOid[] = {
+ 0x06, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x0c
+ };
+ byte signCertV1[8 + WC_SHA_DIGEST_SIZE];
+ PKCS7Attrib attrib;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ byte token[3072];
+ word32 tokenSz;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* SigningCertificate of ESS - RFC 2634 - with SHA-1 hash accepted. */
+ signCertV1[0] = 0x30;
+ signCertV1[1] = 0x1a;
+ signCertV1[2] = 0x30;
+ signCertV1[3] = 0x18;
+ signCertV1[4] = 0x30;
+ signCertV1[5] = 0x16;
+ signCertV1[6] = 0x04;
+ signCertV1[7] = 0x14;
+ ExpectIntEQ(wc_ShaHash(tsa_cert_der_2048, sizeof_tsa_cert_der_2048,
+ signCertV1 + 8), 0);
+ XMEMSET(&attrib, 0, sizeof(attrib));
+ attrib.oid = signCertOid;
+ attrib.oidSz = (word32)sizeof(signCertOid);
+ attrib.value = signCertV1;
+ attrib.valueSz = (word32)sizeof(signCertV1);
+ tokenSz = (word32)sizeof(token);
+ ExpectIntEQ(test_tsp_token_with_attribs(&rng, &attrib, 1, token, &tokenSz),
+ TEST_SUCCESS);
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+#if WC_TSP_MIN_HASH_STRENGTH_BITS > 80
+ /* SHA-1 of ESS SigningCertificate is below the minimum strength. */
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL),
+ WC_NO_ERR_TRACE(HASH_TYPE_E));
+#else
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, NULL), 0);
+#endif
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_ecc(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && defined(HAVE_ECC) && \
+ defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) && \
+ !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+
+ /* Sign the token with the ECC TSA. */
+ ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_ecc_cert_der_256,
+ sizeof_tsa_ecc_cert_der_256, tsa_ecc_key_der_256,
+ sizeof_tsa_ecc_key_der_256, ECDSAk, &rng), TEST_SUCCESS);
+
+ /* Verify the ECDSA signed token. */
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec), 0);
+ ExpectIntEQ(tstDec.version, WC_TSP_VERSION);
+ ExpectBufEQ(tstDec.imprint.hash, tsHashedMsg,
+ (int)sizeof(tsHashedMsg));
+ wc_PKCS7_Free(pkcs7);
+
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_CheckTsaName(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* Name of TSA: dNSName GeneralName. */
+ static const byte tsaName[] = { 0x82, 0x03, 't', 's', 'a' };
+ /* A different name of the same length. */
+ static const byte otherName[] = { 0x82, 0x03, 't', 's', 'b' };
+ TspTstInfo tst;
+
+ ExpectIntEQ(wc_TspTstInfo_Init(&tst), 0);
+
+ /* Bad arguments. */
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(NULL, tsaName,
+ (word32)sizeof(tsaName)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tst, NULL, (word32)sizeof(tsaName)),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tst, tsaName, 0),
+ WC_NO_ERR_TRACE(BAD_FUNC_ARG));
+
+ /* TSA name must be present when expected. */
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tst, tsaName,
+ (word32)sizeof(tsaName)), WC_NO_ERR_TRACE(TSP_VERIFY_E));
+
+ /* TSA name is the expected name. */
+ tst.tsa = tsaName;
+ tst.tsaSz = (word32)sizeof(tsaName);
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tst, tsaName,
+ (word32)sizeof(tsaName)), 0);
+ /* Expected name of a different length. */
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tst, tsaName,
+ (word32)sizeof(tsaName) - 1), WC_NO_ERR_TRACE(TSP_VERIFY_E));
+ /* Expected name with different data. */
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tst, otherName,
+ (word32)sizeof(otherName)), WC_NO_ERR_TRACE(TSP_VERIFY_E));
+#endif
+ return EXPECT_RESULT();
+}
+
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+/* Build a token whose TSTInfo carries the given TSA name. The signing PKCS7
+ * is created and freed here so the verifying caller news only its own. */
+static int test_tsp_make_tsa_name_token(WC_RNG* rng, const byte* tsa,
+ word32 tsaSz, byte* token, word32* tokenSz)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tst;
+
+ test_tsp_set_tstinfo(&tst);
+ tst.tsa = tsa;
+ tst.tsaSz = tsaSz;
+
+ ExpectIntEQ(test_tsp_new_tsa_signer(&pkcs7, rng), TEST_SUCCESS);
+ ExpectIntEQ(wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, tokenSz), 0);
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+
+/* Create and verify a token with a TSA name in the TSTInfo. */
+static int test_tsp_tsa_name(WC_RNG* rng, const byte* tsa, word32 tsaSz,
+ int expRet)
+{
+ EXPECT_DECLS;
+ wc_PKCS7* pkcs7 = NULL;
+ TspTstInfo tstDec;
+ byte token[3072];
+ word32 tokenSz = (word32)sizeof(token);
+
+ ExpectIntEQ(test_tsp_make_tsa_name_token(rng, tsa, tsaSz, token, &tokenSz),
+ TEST_SUCCESS);
+
+ ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
+ ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
+ ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec),
+ expRet);
+ if (expRet == 0) {
+ /* TSA name decoded is the expected name. */
+ ExpectIntEQ(wc_TspTstInfo_CheckTsaName(&tstDec, tsa, tsaSz), 0);
+ }
+ wc_PKCS7_Free(pkcs7);
+
+ return EXPECT_RESULT();
+}
+
+#if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
+/* Make a directoryName GeneralName with the subject of the certificate. */
+static int test_tsp_cert_dirname(const byte* certDer, word32 certDerSz,
+ byte* out, word32* outSz)
+{
+ EXPECT_DECLS;
+ DecodedCert cert;
+ word32 idx = 0;
+ word32 nameLen = 0;
+
+ wc_InitDecodedCert(&cert, certDer, certDerSz, NULL);
+ ExpectIntEQ(wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL), 0);
+ if (EXPECT_SUCCESS()) {
+ nameLen = (word32)cert.subjectRawLen;
+ }
+ ExpectNotNull(cert.subjectRaw);
+ ExpectIntLE(2 + 3 + 3 + nameLen, *outSz);
+ if (EXPECT_SUCCESS()) {
+ word32 seqLen = 2 + nameLen + ((nameLen >= 128) ? 1 : 0);
+
+ /* directoryName [4] of GeneralName - explicitly tagged Name. */
+ out[idx++] = ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | ASN_DIR_TYPE;
+ if (seqLen >= 128) {
+ out[idx++] = 0x81;
+ }
+ out[idx++] = (byte)seqLen;
+ out[idx++] = ASN_SEQUENCE | ASN_CONSTRUCTED;
+ if (nameLen >= 128) {
+ out[idx++] = 0x81;
+ }
+ out[idx++] = (byte)nameLen;
+ XMEMCPY(out + idx, cert.subjectRaw, nameLen);
+ *outSz = idx + nameLen;
+ }
+ wc_FreeDecodedCert(&cert);
+
+ return EXPECT_RESULT();
+}
+#endif
+#endif
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* dNSName of the TSA's certificate. */
+ static const byte goodDns[] = {
+ 0x82, 0x0f,
+ 't', 's', 'a', '.', 'w', 'o', 'l', 'f', 's', 's', 'l', '.', 'c',
+ 'o', 'm'
+ };
+ WC_RNG rng;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* TSA name in subject alternative names of certificate. */
+ ExpectIntEQ(test_tsp_tsa_name(&rng, goodDns, (word32)sizeof(goodDns), 0),
+ TEST_SUCCESS);
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_mismatch(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* dNSName not of the TSA's certificate. */
+ static const byte badDns[] = {
+ 0x82, 0x0f,
+ 't', 's', 'b', '.', 'w', 'o', 'l', 'f', 's', 's', 'l', '.', 'c',
+ 'o', 'm'
+ };
+ WC_RNG rng;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* TSA name not in subject alternative names of certificate. */
+ ExpectIntEQ(test_tsp_tsa_name(&rng, badDns, (word32)sizeof(badDns),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E)), TEST_SUCCESS);
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_unsupported(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* otherName - form of GeneralName not supported. */
+ static const byte otherName[] = { 0xa0, 0x02, 0x05, 0x00 };
+ WC_RNG rng;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* Form of GeneralName not supported. */
+ ExpectIntEQ(test_tsp_tsa_name(&rng, otherName, (word32)sizeof(otherName),
+ WC_NO_ERR_TRACE(TSP_VERIFY_E)), TEST_SUCCESS);
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_bad_enc(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
+ /* dNSName with a length longer than the data. */
+ static const byte badLen[] = { 0x82, 0x05, 't' };
+#if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
+ /* directoryName that does not hold a SEQUENCE. */
+ static const byte badDirName[] = { 0xa4, 0x02, 0x31, 0x00 };
+#endif
+ WC_RNG rng;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* Invalid GeneralName encoding. */
+ ExpectIntEQ(test_tsp_tsa_name(&rng, badLen, (word32)sizeof(badLen),
+ WC_NO_ERR_TRACE(ASN_PARSE_E)), TEST_SUCCESS);
+#if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
+ /* directoryName must hold a Name. */
+ ExpectIntEQ(test_tsp_tsa_name(&rng, badDirName, (word32)sizeof(badDirName),
+ WC_NO_ERR_TRACE(ASN_PARSE_E)), TEST_SUCCESS);
+#endif
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
+
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_dirname(void)
+{
+ EXPECT_DECLS;
+#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
+ !defined(NO_SHA256) && !defined(WC_NO_RNG) && \
+ defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER) && \
+ (!defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT))
+ byte dirName[256];
+ word32 dirNameSz;
+ WC_RNG rng;
+
+ ExpectIntEQ(wc_InitRng(&rng), 0);
+ /* TSA name is subject name of certificate. */
+ dirNameSz = (word32)sizeof(dirName);
+ ExpectIntEQ(test_tsp_cert_dirname(tsa_cert_der_2048,
+ sizeof_tsa_cert_der_2048, dirName, &dirNameSz), TEST_SUCCESS);
+ ExpectIntEQ(test_tsp_tsa_name(&rng, dirName, dirNameSz, 0), TEST_SUCCESS);
+ /* TSA name is subject name of another certificate. */
+ dirNameSz = (word32)sizeof(dirName);
+ ExpectIntEQ(test_tsp_cert_dirname(client_cert_der_2048,
+ sizeof_client_cert_der_2048, dirName, &dirNameSz), TEST_SUCCESS);
+ ExpectIntEQ(test_tsp_tsa_name(&rng, dirName, dirNameSz,
+ WC_NO_ERR_TRACE(TSP_VERIFY_E)), TEST_SUCCESS);
+ wc_FreeRng(&rng);
+#endif
+ return EXPECT_RESULT();
+}
diff --git a/tests/api/test_tsp.h b/tests/api/test_tsp.h
new file mode 100644
index 00000000000..9b579a55616
--- /dev/null
+++ b/tests/api/test_tsp.h
@@ -0,0 +1,148 @@
+/* test_tsp.h
+ *
+ * Copyright (C) 2006-2026 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 3 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 WOLFCRYPT_TEST_TSP_H
+#define WOLFCRYPT_TEST_TSP_H
+
+#include
+
+int test_wc_TspRequest_Init(void);
+int test_wc_TspRequest_SetHashType(void);
+int test_wc_TspRequest_GetHashType(void);
+int test_wc_TspRequest_GetSetHash(void);
+int test_wc_TspRequest_GetSetNonce(void);
+int test_wc_TspGenerateNonce(void);
+int test_wc_TspRequest_GetSetPolicy(void);
+int test_wc_TspRequest_GetSetCertReq(void);
+int test_wc_TspTstInfo_GetSetSerial(void);
+int test_wc_TspRequest_Encode(void);
+int test_wc_TspRequest_Decode(void);
+int test_wc_TspTstInfo_Init(void);
+int test_wc_TspTstInfo_Getters(void);
+int test_wc_TspTstInfo_Setters(void);
+int test_wc_TspTstInfo_Encode(void);
+int test_wc_TspTstInfo_Decode(void);
+int test_wc_TspTstInfo_CheckGenTime(void);
+int test_wc_TspTstInfo_GetSetGenTimeAsTime(void);
+int test_wc_TspResponse_Init(void);
+int test_wc_TspGetSetStatus(void);
+int test_wc_TspStrings(void);
+int test_wc_TspResponse_Encode(void);
+int test_wc_TspResponse_Decode(void);
+int test_wc_TspTstInfo_CheckRequest(void);
+int test_wc_TspTstInfo_CheckTsaName(void);
+int test_wc_TspTstInfo_SignWithPkcs7(void);
+int test_wc_TspTstInfo_SignWithPkcs7_create(void);
+int test_wc_TspTstInfo_SignWithPkcs7_signer_required(void);
+int test_wc_TspTstInfo_SignWithPkcs7_hash_and_buffer(void);
+int test_wc_TspTstInfo_Sign(void);
+int test_wc_TspTstInfo_SignWithPkcs7_attribs(void);
+int test_wc_TspTstInfo_SignWithPkcs7_attribs_verify(void);
+int test_wc_TspTstInfo_SignWithPkcs7_attribs_sha384(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_modified(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_no_signer(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_not_tst(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_bad_eku(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_bad_ku(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_extra_eku(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_nocerts(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_nocerts_supplied(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_no_attrib(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_bad_hash(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_bad_alg(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_ess_v1(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_mismatch(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_unsupported(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_bad_enc(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_dirname(void);
+int test_wc_TspTstInfo_VerifyWithPKCS7_ecc(void);
+int test_wc_TspResponse_Verify(void);
+int test_wc_TspResponse_Verify_wrong_cert(void);
+int test_wc_TspResponse_Verify_status(void);
+int test_wc_TspResponse_Verify_modified(void);
+int test_wc_TspResponse_Verify_nocerts(void);
+int test_wc_TspResponse_VerifyData(void);
+int test_wc_TspTstInfo_SetFromRequest(void);
+
+#define TEST_TSP_DECLS \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_Init), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_SetHashType), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_GetHashType), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_GetSetHash), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_GetSetNonce), \
+ TEST_DECL_GROUP("tsp", test_wc_TspGenerateNonce), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_GetSetPolicy), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_GetSetCertReq), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_Encode), \
+ TEST_DECL_GROUP("tsp", test_wc_TspRequest_Decode), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Init), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_GetSetSerial), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Getters), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Setters), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Encode), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Decode), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_CheckGenTime), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_GetSetGenTimeAsTime), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Init), \
+ TEST_DECL_GROUP("tsp", test_wc_TspGetSetStatus), \
+ TEST_DECL_GROUP("tsp", test_wc_TspStrings), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Encode), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Decode), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_CheckRequest), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_CheckTsaName), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_create), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_signer_required), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_hash_and_buffer), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Sign), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_attribs), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_attribs_verify), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_attribs_sha384), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_modified), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_no_signer), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_not_tst), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_bad_eku), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_bad_ku), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_extra_eku), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_nocerts), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_nocerts_supplied), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_ess_no_attrib), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_ess_bad_hash), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_ess_bad_alg), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_ess_v1), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_mismatch), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_unsupported), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_bad_enc), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_tsa_name_dirname), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_ecc), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Verify), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Verify_wrong_cert), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Verify_status), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Verify_modified), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_Verify_nocerts), \
+ TEST_DECL_GROUP("tsp", test_wc_TspResponse_VerifyData), \
+ TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SetFromRequest)
+
+#endif /* WOLFCRYPT_TEST_TSP_H */
diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c
index 7e855986f67..75ab9a759d6 100644
--- a/wolfcrypt/src/asn.c
+++ b/wolfcrypt/src/asn.c
@@ -774,6 +774,30 @@ static word32 SizeASN_Num(word32 n, int bits, byte tag)
return len;
}
+#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+/* Calculate the size of a DER encoded BIT STRING of a 32-bit word.
+ *
+ * Named bit string: bit 0 is the most significant bit of the word and
+ * trailing zero bits are not encoded.
+ *
+ * @param [in] n 32-bit word to be encoded.
+ * @return Number of bytes of the ASN.1 item.
+ */
+static word32 SizeASN_BitString32(word32 n)
+{
+ word32 len = 4;
+
+ /* Discover actual size by checking for trailing zero bytes. */
+ while ((len > 0) && ((n & 0xff) == 0)) {
+ n >>= 8;
+ len--;
+ }
+
+ /* Tag, length, unused bits byte and data. */
+ return 1 + 1 + 1 + len;
+}
+#endif
+
/* Calculate the size of the data in the constructed item based on the
* length of the ASN.1 items below.
*
@@ -855,9 +879,18 @@ int SizeASN_Items(const ASNItem* asn, ASNSetData *data, int count,
len = SizeASN_Num(data[i].data.u16, 16, asn[i].tag);
break;
#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
- /* Not used yet! */
case ASN_DATA_TYPE_WORD32:
- len = SizeASN_Num(data[i].data.u32, 32, asn[i].tag);
+ /* BIT_STRING is a named bit string in a 32-bit word. */
+ if (asn[i].tag == ASN_BIT_STRING) {
+ len = SizeASN_BitString32(data[i].data.u32);
+ }
+ else {
+ len = SizeASN_Num(data[i].data.u32, 32, asn[i].tag);
+ }
+ break;
+ /* Encoded as an INTEGER even when implicitly tagged. */
+ case ASN_DATA_TYPE_WORD32_INT:
+ len = SizeASN_Num(data[i].data.u32, 32, ASN_INTEGER);
break;
#endif
@@ -1021,6 +1054,44 @@ static void SetASN_Num(word32 n, int bits, byte* out, byte tag)
out[idx++] = (byte)(n >> j);
}
+#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+/* Create the DER encoding of a BIT STRING from a 32-bit word.
+ *
+ * Named bit string: bit 0 is the most significant bit of the word and
+ * trailing zero bits are not encoded.
+ *
+ * Assumes that the out buffer is large enough for encoding.
+ *
+ * @param [in] n 32-bit word to be encoded.
+ * @param [out] out Buffer to write encoding into - after tag byte.
+ */
+static void SetASN_BitString32(word32 n, byte* out)
+{
+ int j;
+ byte len = 4;
+ byte unusedBits = 0;
+ word32 idx = 3;
+
+ /* Discover actual size by checking for trailing zero bytes. */
+ while ((len > 0) && ((n & 0xff) == 0)) {
+ n >>= 8;
+ len--;
+ }
+ if (len > 0) {
+ /* Count trailing zero bits of last byte. */
+ while (((n >> unusedBits) & 0x01) == 0x00)
+ unusedBits++;
+ }
+
+ /* Length includes unused bits byte. */
+ out[1] = (byte)(1 + len);
+ out[2] = unusedBits;
+ /* Place in the required bytes of the word. */
+ for (j = 8 * (len - 1); j >= 0; j -= 8)
+ out[idx++] = (byte)(n >> j);
+}
+#endif
+
/* Creates the DER encoding of the ASN.1 items.
*
* Assumes the output buffer is large enough to hold encoding.
@@ -1082,9 +1153,18 @@ int SetASN_Items(const ASNItem* asn, ASNSetData *data, int count, byte* output)
SetASN_Num(data[i].data.u16, 16, out, asn[i].tag);
break;
#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
- /* Not used yet! */
case ASN_DATA_TYPE_WORD32:
- SetASN_Num(data[i].data.u32, 32, out, asn[i].tag);
+ /* BIT_STRING is a named bit string in a 32-bit word. */
+ if (asn[i].tag == ASN_BIT_STRING) {
+ SetASN_BitString32(data[i].data.u32, out);
+ }
+ else {
+ SetASN_Num(data[i].data.u32, 32, out, asn[i].tag);
+ }
+ break;
+ /* Encoded as an INTEGER even when implicitly tagged. */
+ case ASN_DATA_TYPE_WORD32_INT:
+ SetASN_Num(data[i].data.u32, 32, out, ASN_INTEGER);
break;
#endif
@@ -1446,7 +1526,8 @@ static int GetASN_StoreData(const ASNItem* asn, ASNGetData* data,
#endif
return ASN_PARSE_E;
}
- if (!zeroPadded && (input[idx] >= 0x80U)) {
+ if ((asn->tag != ASN_BIT_STRING) && (!zeroPadded) &&
+ (input[idx] >= 0x80U)) {
#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
WOLFSSL_MSG_VSNPRINTF("Unexpected negative INTEGER value");
#endif
@@ -2317,6 +2398,33 @@ void SetASN_Int16Bit(ASNSetData *dataASN, word16 num)
dataASN->data.u16 = num;
}
+#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+/* Setup an ASN data item to set a 32-bit number.
+ *
+ * @param [in] dataASN Dynamic ASN data item.
+ * @param [in] num 32-bit number to set.
+ */
+void SetASN_Int32Bit(ASNSetData *dataASN, word32 num)
+{
+ dataASN->dataType = ASN_DATA_TYPE_WORD32;
+ dataASN->data.u32 = num;
+}
+
+/* Setup an ASN data item to set a 32-bit number encoded as an INTEGER.
+ *
+ * For implicitly tagged INTEGERs - a zero byte is prepended to keep the
+ * number positive, as is done for INTEGER tagged items.
+ *
+ * @param [in] dataASN Dynamic ASN data item.
+ * @param [in] num 32-bit number to set.
+ */
+void SetASN_Int32BitInt(ASNSetData *dataASN, word32 num)
+{
+ dataASN->dataType = ASN_DATA_TYPE_WORD32_INT;
+ dataASN->data.u32 = num;
+}
+#endif
+
/* Setup an ASN data item to set the data in a buffer.
*
* @param [in] dataASN Dynamic ASN data item.
@@ -15239,7 +15347,8 @@ int ValidateGmtime(struct tm* inTime)
#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && \
!defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || \
- defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER))
+ defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER) || \
+ defined(WOLFSSL_TSP))
/* Set current time string, either UTC or GeneralizedTime.
* (void*) currTime should be a pointer to time_t, output is placed in buf.
*
@@ -20052,7 +20161,7 @@ enum {
int DecodeExtKeyUsage(const byte* input, word32 sz,
const byte **extExtKeyUsageSrc, word32 *extExtKeyUsageSz,
word32 *extExtKeyUsageCount, byte *extExtKeyUsage,
- byte *extExtKeyUsageSsh)
+ byte *extExtKeyUsageSsh, word32 *extExtKeyUsageOidCnt)
{
word32 idx = 0;
int length;
@@ -20071,6 +20180,8 @@ int DecodeExtKeyUsage(const byte* input, word32 sz,
*extExtKeyUsageCount = 0;
#endif
*extExtKeyUsage = 0;
+ if (extExtKeyUsageOidCnt != NULL)
+ *extExtKeyUsageOidCnt = 0;
#ifdef WOLFSSL_WOLFSSH
*extExtKeyUsageSsh = 0;
#endif
@@ -20134,6 +20245,11 @@ int DecodeExtKeyUsage(const byte* input, word32 sz,
(*extExtKeyUsageCount)++;
#endif
}
+
+ /* Count every KeyPurposeId consumed - recognized or not. */
+ if ((ret == 0) && (extExtKeyUsageOidCnt != NULL)) {
+ (*extExtKeyUsageOidCnt)++;
+ }
}
return ret;
@@ -20167,10 +20283,11 @@ static int DecodeExtKeyUsageInternal(const byte* input, word32 sz,
#endif
&cert->extExtKeyUsage,
#ifdef WOLFSSL_WOLFSSH
- &cert->extExtKeyUsageSsh
+ &cert->extExtKeyUsageSsh,
#else
- NULL
+ NULL,
#endif
+ &cert->extExtKeyUsageOidCnt
);
if (ret != 0)
@@ -39631,3 +39748,7 @@ const byte* AsnHashesGetHash(const AsnHashes* hashes, int hashAlg, int* size)
#endif /* WOLFSSL_SEP */
#undef ERROR_OUT
+
+/* Time-Stamp Protocol (TSP) encoding and decoding. RFC 3161. */
+#define WOLFSSL_ASN_TSP_INCLUDED
+#include "wolfcrypt/src/asn_tsp.c"
diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c
index 375da8eaf0b..fd352159e1e 100644
--- a/wolfcrypt/src/asn_orig.c
+++ b/wolfcrypt/src/asn_orig.c
@@ -3919,7 +3919,7 @@ int DecodeKeyUsage(const byte* input, word32 sz, word16 *extKeyUsage)
int DecodeExtKeyUsage(const byte* input, word32 sz,
const byte **extExtKeyUsageSrc, word32 *extExtKeyUsageSz,
word32 *extExtKeyUsageCount, byte *extExtKeyUsage,
- byte *extExtKeyUsageSsh)
+ byte *extExtKeyUsageSsh, word32 *extExtKeyUsageOidCnt)
{
word32 idx = 0, oid;
int length, ret;
@@ -3937,6 +3937,8 @@ int DecodeExtKeyUsage(const byte* input, word32 sz,
*extExtKeyUsageCount = 0;
#endif
*extExtKeyUsage = 0;
+ if (extExtKeyUsageOidCnt != NULL)
+ *extExtKeyUsageOidCnt = 0;
#ifdef WOLFSSL_WOLFSSH
*extExtKeyUsageSsh = 0;
#endif
@@ -3953,8 +3955,14 @@ int DecodeExtKeyUsage(const byte* input, word32 sz,
while (idx < (word32)sz) {
ret = GetObjectId(input, &idx, &oid, oidCertKeyUseType, sz);
- if (ret == WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E))
+ if (ret == WC_NO_ERR_TRACE(ASN_UNKNOWN_OID_E)) {
+ /* Unknown KeyPurposeId is still consumed - count it so the count
+ * matches the template parser (and every consumed OID). */
+ if (extExtKeyUsageOidCnt != NULL) {
+ (*extExtKeyUsageOidCnt)++;
+ }
continue;
+ }
else if (ret < 0)
return ret;
@@ -3998,6 +4006,11 @@ int DecodeExtKeyUsage(const byte* input, word32 sz,
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
(*extExtKeyUsageCount)++;
#endif
+
+ /* Count every KeyPurposeId consumed - recognized or not. */
+ if (extExtKeyUsageOidCnt != NULL) {
+ (*extExtKeyUsageOidCnt)++;
+ }
}
return 0;
diff --git a/wolfcrypt/src/asn_tsp.c b/wolfcrypt/src/asn_tsp.c
new file mode 100644
index 00000000000..0316d6f5451
--- /dev/null
+++ b/wolfcrypt/src/asn_tsp.c
@@ -0,0 +1,1454 @@
+/* asn_tsp.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/*
+ * DESCRIPTION
+ * This library provides encoding and decoding of Time-Stamp Protocol (TSP)
+ * messages: TimeStampReq, TimeStampResp and TSTInfo. RFC 3161.
+ *
+ * The TimeStampToken in a TimeStampResp is a CMS SignedData (RFC 5652) with
+ * content type id-ct-TSTInfo. Creation and verification of tokens is
+ * implemented using PKCS#7 APIs when available.
+ */
+
+#include
+
+#if !defined(WOLFSSL_ASN_TSP_INCLUDED)
+ #ifndef WOLFSSL_IGNORE_FILE_WARN
+ #warning asn_tsp.c does not need to be compiled separately from asn.c
+ #endif
+#else
+
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_ASN_TEMPLATE) && !defined(NO_ASN)
+
+#include
+#ifdef HAVE_PKCS7
+ #include
+ #include
+#endif
+
+/* ASN template for TimeStampReq.
+ * RFC 3161, 2.4.1 - Request Format
+ *
+ * Extensions are not supported - decoding a message with an extensions
+ * element fails as it matches no item.
+ */
+static const ASNItem tspReqASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* version */
+/* VER */ { 1, ASN_INTEGER, 0, 0, 0 },
+ /* messageImprint */
+/* MI_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 },
+ /* hashAlgorithm */
+/* MI_ALG_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 0 },
+/* MI_ALG_OID */ { 3, ASN_OBJECT_ID, 0, 0, 0 },
+/* MI_ALG_NULL */ { 3, ASN_TAG_NULL, 0, 0, 1 },
+ /* hashedMessage */
+/* MI_MSG */ { 2, ASN_OCTET_STRING, 0, 0, 0 },
+ /* reqPolicy */
+/* POLICY */ { 1, ASN_OBJECT_ID, 0, 0, 1 },
+ /* nonce */
+/* NONCE */ { 1, ASN_INTEGER, 0, 0, 1 },
+ /* certReq */
+/* CERTREQ */ { 1, ASN_BOOLEAN, 0, 0, 1 },
+};
+/* Named indices for tspReqASN. */
+enum {
+ TSPREQASN_IDX_SEQ = 0,
+ TSPREQASN_IDX_VER,
+ TSPREQASN_IDX_MI_SEQ,
+ TSPREQASN_IDX_MI_ALG_SEQ,
+ TSPREQASN_IDX_MI_ALG_OID,
+ TSPREQASN_IDX_MI_ALG_NULL,
+ TSPREQASN_IDX_MI_MSG,
+ TSPREQASN_IDX_POLICY,
+ TSPREQASN_IDX_NONCE,
+ TSPREQASN_IDX_CERTREQ
+};
+/* Number of items in ASN.1 template for TimeStampReq. */
+#define tspReqASN_Length (sizeof(tspReqASN) / sizeof(ASNItem))
+
+/* Check a number is encodable as given - no leading zero byte.
+ *
+ * The number is encoded as the content of an INTEGER - the encoder prepends
+ * a zero byte to keep the number positive when needed. A number with a
+ * leading zero byte would not round trip: the decoder returns the minimal
+ * form and comparisons against it are exact.
+ *
+ * Zero is one zero byte - other leading zero bytes are redundant.
+ *
+ * @param [in] data Big-endian number to check.
+ * @param [in] sz Length of number in bytes.
+ * @return 0 when the number is encodable.
+ * @return BAD_FUNC_ARG when the number is empty or has a leading zero byte.
+ */
+#define TspCheckNum(data, sz) \
+ ((((sz) == 0) || (((sz) > 1) && ((data)[0] == 0x00))) ? \
+ BAD_FUNC_ARG : 0)
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Encode a TimeStampReq.
+ *
+ * @param [in] req TimeStampReq object to encode.
+ * @param [out] out Buffer to hold encoding. May be NULL to get length.
+ * @param [in, out] outSz On in, length of buffer in bytes.
+ * On out, length of encoding in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or outSz is NULL, the message imprint hash
+ * is not set, a field is too long for its array or the nonce has a
+ * leading zero byte.
+ * @return BUFFER_E when out is not NULL and encoding is longer than outSz.
+ * @return ASN_UNKNOWN_OID_E when the hash algorithm is not recognized.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspRequest_Encode(const TspRequest* req, byte* out, word32* outSz)
+{
+ DECL_ASNSETDATA(dataASN, tspReqASN_Length);
+ int ret = 0;
+ word32 sz = 0;
+
+ WOLFSSL_ENTER("wc_TspRequest_Encode");
+
+ /* Validate parameters. */
+ if ((req == NULL) || (outSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* The message imprint is the only required field. */
+ if ((ret == 0) && ((req->imprint.hashSz == 0) ||
+ (req->imprint.hashSz > sizeof(req->imprint.hash)))) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* Policy, when set, must fit. */
+ if ((ret == 0) && (req->policySz > sizeof(req->policy))) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* Nonce, when set, must fit and be encodable as given. */
+ if ((ret == 0) && (req->nonceSz != 0)) {
+ if (req->nonceSz > sizeof(req->nonce)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else {
+ ret = TspCheckNum(req->nonce, req->nonceSz);
+ }
+ }
+
+ CALLOC_ASNSETDATA(dataASN, tspReqASN_Length, ret, NULL);
+
+ if (ret == 0) {
+ /* Version is 1 - only version defined. */
+ SetASN_Int8Bit(&dataASN[TSPREQASN_IDX_VER], WC_TSP_VERSION);
+ /* messageImprint - hash algorithm with NULL parameters and hash. */
+ SetASN_OID(&dataASN[TSPREQASN_IDX_MI_ALG_OID],
+ (int)req->imprint.hashAlgOID, oidHashType);
+ /* No encoding available for an unknown OID sum. */
+ if (dataASN[TSPREQASN_IDX_MI_ALG_OID].data.buffer.data == NULL) {
+ ret = ASN_UNKNOWN_OID_E;
+ }
+ }
+ if (ret == 0) {
+ /* Hash of the data to be time-stamped. */
+ SetASN_Buffer(&dataASN[TSPREQASN_IDX_MI_MSG], req->imprint.hash,
+ req->imprint.hashSz);
+ /* reqPolicy is optional. */
+ if (req->policySz != 0) {
+ SetASN_Buffer(&dataASN[TSPREQASN_IDX_POLICY], req->policy,
+ req->policySz);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspReqASN, 0,
+ TSPREQASN_IDX_POLICY, tspReqASN_Length);
+ }
+ /* nonce is optional. */
+ if (req->nonceSz != 0) {
+ SetASN_Buffer(&dataASN[TSPREQASN_IDX_NONCE], req->nonce,
+ req->nonceSz);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspReqASN, 0,
+ TSPREQASN_IDX_NONCE, tspReqASN_Length);
+ }
+ /* certReq defaults to FALSE - only encode when TRUE. */
+ if (req->certReq) {
+ SetASN_Boolean(&dataASN[TSPREQASN_IDX_CERTREQ], 1);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspReqASN, 0,
+ TSPREQASN_IDX_CERTREQ, tspReqASN_Length);
+ }
+ /* Calculate size of encoding. */
+ ret = SizeASN_Items(tspReqASN, dataASN, tspReqASN_Length, &sz);
+ }
+ /* Write out encoding when buffer supplied. */
+ if ((ret == 0) && (out != NULL)) {
+ /* Check buffer is big enough to hold encoding. */
+ if (sz > *outSz) {
+ ret = BUFFER_E;
+ }
+ /* Length written must be the length calculated. */
+ else if (SetASN_Items(tspReqASN, dataASN, tspReqASN_Length, out) !=
+ (int)sz) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ if (ret == 0) {
+ /* Return the length of the encoding. */
+ *outSz = sz;
+ }
+
+ FREE_ASNSETDATA(dataASN, NULL);
+ WOLFSSL_LEAVE("wc_TspRequest_Encode", ret);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_REQUESTER */
+/* Decode a TimeStampReq.
+ *
+ * All fields are copied - input is not referenced after return.
+ *
+ * @param [out] req TimeStampReq object to fill.
+ * @param [in] input Buffer holding DER encoding.
+ * @param [in] inSz Length of data in buffer in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or input is NULL or inSz is 0.
+ * @return ASN_PARSE_E when the encoding is invalid, the hash is empty or
+ * extensions are present.
+ * @return ASN_VERSION_E when the version is not supported.
+ * @return ASN_UNKNOWN_OID_E when the hash algorithm OID check fails.
+ * @return BUFFER_E when the hash is longer than WC_TSP_MAX_HASH_SZ bytes,
+ * the policy is longer than MAX_OID_SZ bytes or the nonce is
+ * longer than MAX_TS_NONCE_SZ bytes.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspRequest_Decode(TspRequest* req, const byte* input, word32 inSz)
+{
+ DECL_ASNGETDATA(dataASN, tspReqASN_Length);
+ int ret = 0;
+ word32 idx = 0;
+
+ WOLFSSL_ENTER("wc_TspRequest_Decode");
+
+ /* Validate parameters. */
+ if ((req == NULL) || (input == NULL) || (inSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ CALLOC_ASNGETDATA(dataASN, tspReqASN_Length, ret, NULL);
+
+ if (ret == 0) {
+ /* All fields empty - optional fields left empty when not present. */
+ XMEMSET(req, 0, sizeof(TspRequest));
+
+ /* Version is small - 1 is the only defined value. */
+ GetASN_Int8Bit(&dataASN[TSPREQASN_IDX_VER], &req->version);
+ /* Any policy accepted - copied into fixed size array. Caller
+ * checks it is one it supports. */
+ req->policySz = (word32)sizeof(req->policy);
+ GetASN_Buffer(&dataASN[TSPREQASN_IDX_POLICY], req->policy,
+ &req->policySz);
+ /* Hash algorithm OID checked against known hash OIDs - caller
+ * checks it is usable. */
+ GetASN_OID(&dataASN[TSPREQASN_IDX_MI_ALG_OID], oidHashType);
+ /* Hash copied into fixed size array - length checked. */
+ req->imprint.hashSz = (word32)sizeof(req->imprint.hash);
+ GetASN_Buffer(&dataASN[TSPREQASN_IDX_MI_MSG], req->imprint.hash,
+ &req->imprint.hashSz);
+ /* Nonce copied into fixed size array - length checked. */
+ req->nonceSz = (word32)sizeof(req->nonce);
+ GetASN_Buffer(&dataASN[TSPREQASN_IDX_NONCE], req->nonce,
+ &req->nonceSz);
+ /* certReq defaults to FALSE when not present. */
+ GetASN_Boolean(&dataASN[TSPREQASN_IDX_CERTREQ], &req->certReq);
+ /* Decode TimeStampReq. */
+ ret = GetASN_Items(tspReqASN, dataASN, tspReqASN_Length, 1, input,
+ &idx, inSz);
+ }
+ /* Check all data used - input is one complete message. */
+ if ((ret == 0) && (idx != inSz)) {
+ ret = ASN_PARSE_E;
+ }
+ /* Only version 1 defined - RFC 3161, 2.4.1. */
+ if ((ret == 0) && (req->version != WC_TSP_VERSION)) {
+ ret = ASN_VERSION_E;
+ }
+ /* Hash must not be empty. */
+ if ((ret == 0) && (req->imprint.hashSz == 0)) {
+ ret = ASN_PARSE_E;
+ }
+ if (ret == 0) {
+ /* messageImprint hash algorithm - hash already copied. */
+ req->imprint.hashAlgOID =
+ dataASN[TSPREQASN_IDX_MI_ALG_OID].data.oid.sum;
+ /* Optional fields already copied - length set to zero when not
+ * present. */
+ if (dataASN[TSPREQASN_IDX_POLICY].tag == 0) {
+ req->policySz = 0;
+ }
+ if (dataASN[TSPREQASN_IDX_NONCE].tag == 0) {
+ req->nonceSz = 0;
+ }
+ }
+
+ FREE_ASNGETDATA(dataASN, NULL);
+ WOLFSSL_LEAVE("wc_TspRequest_Decode", ret);
+ return ret;
+}
+
+
+/* Check a genTime is a valid GeneralizedTime of RFC 3161.
+ *
+ * RFC 3161, 2.4.2: "YYYYMMDDhhmmss[.s...]Z" - the seconds are always
+ * represented, the fraction of seconds has no trailing zeros and is not
+ * empty, and the time is Zulu with nothing following.
+ *
+ * The date and time fields are range-checked so the values are usable as a
+ * calendar time (e.g. a month outside 1..12 would index out of bounds when
+ * converting to a time_t).
+ *
+ * @param [in] t genTime string.
+ * @param [in] sz Length of string in bytes.
+ * @return 0 when the string is valid.
+ * @return ASN_PARSE_E when the string is not valid.
+ */
+WOLFSSL_LOCAL int TspCheckGenTimeSyntax(const byte* t, word32 sz)
+{
+ int ret = 0;
+ word32 i = 0;
+
+ /* Shortest form: "YYYYMMDDhhmmssZ". */
+ if (sz < 15) {
+ ret = ASN_PARSE_E;
+ }
+ /* Date and time are digits. */
+ for (i = 0; (ret == 0) && (i < 14); i++) {
+ if ((t[i] < '0') || (t[i] > '9')) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ /* Date and time fields must be in range. The month in particular is used
+ * to index a table when converting to a time_t, so an out-of-range value
+ * must not be accepted. */
+ if (ret == 0) {
+ int mon = (t[4] - '0') * 10 + (t[5] - '0');
+ int day = (t[6] - '0') * 10 + (t[7] - '0');
+ int hour = (t[8] - '0') * 10 + (t[9] - '0');
+ int min = (t[10] - '0') * 10 + (t[11] - '0');
+ int sec = (t[12] - '0') * 10 + (t[13] - '0');
+ if ((mon < 1) || (mon > 12) || (day < 1) || (day > 31) ||
+ (hour > 23) || (min > 59) || (sec > 60)) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ /* Optional fraction of seconds. */
+ if ((ret == 0) && (t[i] == '.')) {
+ /* Step over the digits of the fraction. */
+ for (i++; (i < sz - 1) && (t[i] >= '0') && (t[i] <= '9'); i++);
+ /* At least one digit and no trailing zero. RFC 3161, 2.4.2. */
+ if ((i == 15) || (t[i - 1] == '0')) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ /* Must be Zulu time and nothing after. */
+ if ((ret == 0) && ((i != sz - 1) || (t[i] != 'Z'))) {
+ ret = ASN_PARSE_E;
+ }
+
+ return ret;
+}
+
+/* ASN template for TSTInfo.
+ * RFC 3161, 2.4.2 - Response Format
+ *
+ * Extensions are not supported - decoding a message with an extensions
+ * element fails as it matches no item.
+ */
+static const ASNItem tspTstInfoASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* version */
+/* VER */ { 1, ASN_INTEGER, 0, 0, 0 },
+ /* policy */
+/* POLICY */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
+ /* messageImprint */
+/* MI_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 },
+ /* hashAlgorithm */
+/* MI_ALG_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 0 },
+/* MI_ALG_OID */ { 3, ASN_OBJECT_ID, 0, 0, 0 },
+/* MI_ALG_NULL */ { 3, ASN_TAG_NULL, 0, 0, 1 },
+ /* hashedMessage */
+/* MI_MSG */ { 2, ASN_OCTET_STRING, 0, 0, 0 },
+ /* serialNumber */
+/* SERIAL */ { 1, ASN_INTEGER, 0, 0, 0 },
+ /* genTime */
+/* GENTIME */ { 1, ASN_GENERALIZED_TIME, 0, 0, 0 },
+ /* accuracy */
+/* ACC_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 1 },
+ /* seconds */
+/* ACC_SEC */ { 2, ASN_INTEGER, 0, 0, 1 },
+ /* millis */
+/* ACC_MILLIS */ { 2, ASN_CONTEXT_SPECIFIC | 0, 0, 0, 1 },
+ /* micros */
+/* ACC_MICROS */ { 2, ASN_CONTEXT_SPECIFIC | 1, 0, 0, 1 },
+ /* ordering */
+/* ORDERING */ { 1, ASN_BOOLEAN, 0, 0, 1 },
+ /* nonce */
+/* NONCE */ { 1, ASN_INTEGER, 0, 0, 1 },
+ /* tsa */
+/* TSA */ { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 1 },
+};
+/* Named indices for tspTstInfoASN. */
+enum {
+ TSPTSTINFOASN_IDX_SEQ = 0,
+ TSPTSTINFOASN_IDX_VER,
+ TSPTSTINFOASN_IDX_POLICY,
+ TSPTSTINFOASN_IDX_MI_SEQ,
+ TSPTSTINFOASN_IDX_MI_ALG_SEQ,
+ TSPTSTINFOASN_IDX_MI_ALG_OID,
+ TSPTSTINFOASN_IDX_MI_ALG_NULL,
+ TSPTSTINFOASN_IDX_MI_MSG,
+ TSPTSTINFOASN_IDX_SERIAL,
+ TSPTSTINFOASN_IDX_GENTIME,
+ TSPTSTINFOASN_IDX_ACC_SEQ,
+ TSPTSTINFOASN_IDX_ACC_SEC,
+ TSPTSTINFOASN_IDX_ACC_MILLIS,
+ TSPTSTINFOASN_IDX_ACC_MICROS,
+ TSPTSTINFOASN_IDX_ORDERING,
+ TSPTSTINFOASN_IDX_NONCE,
+ TSPTSTINFOASN_IDX_TSA
+};
+/* Number of items in ASN.1 template for TSTInfo. */
+#define tspTstInfoASN_Length (sizeof(tspTstInfoASN) / sizeof(ASNItem))
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Encode a TSTInfo.
+ *
+ * When genTime is NULL, the current time is used. Not available when there
+ * is no real time clock - NO_ASN_TIME, USER_TIME or TIME_OVERRIDES.
+ *
+ * @param [in] tstInfo TSTInfo object to encode.
+ * @param [out] out Buffer to hold encoding. May be NULL to get
+ * length.
+ * @param [in, out] outSz On in, length of buffer in bytes.
+ * On out, length of encoding in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or outSz is NULL, a required field of
+ * tstInfo is not set or empty, the hash is too long, the genTime
+ * is not a valid GeneralizedTime, the tsa is empty, the serial
+ * number or nonce is empty or has a leading zero byte or accuracy
+ * millis or micros is out of range.
+ * @return BUFFER_E when out is not NULL and encoding is longer than outSz.
+ * @return ASN_UNKNOWN_OID_E when the hash algorithm is not recognized.
+ * @return ASN_TIME_E when getting the current time failed.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspTstInfo_Encode(const TspTstInfo* tstInfo, byte* out, word32* outSz)
+{
+ DECL_ASNSETDATA(dataASN, tspTstInfoASN_Length);
+ int ret = 0;
+ word32 sz = 0;
+#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES)
+ byte timeBuf[ASN_GENERALIZED_TIME_SIZE];
+#endif
+
+ WOLFSSL_ENTER("wc_TspTstInfo_Encode");
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (outSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* Policy, message imprint and serial number are required fields. */
+ if ((ret == 0) && ((tstInfo->policy == NULL) ||
+ (tstInfo->policySz == 0) ||
+ (tstInfo->imprint.hashSz == 0) ||
+ (tstInfo->imprint.hashSz > sizeof(tstInfo->imprint.hash)) ||
+ (tstInfo->serial == NULL))) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* genTime, when set, must be a valid GeneralizedTime of RFC 3161. */
+ if ((ret == 0) && (tstInfo->genTime != NULL) &&
+ (TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz)
+ != 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* TSA name, when set, must not be empty. */
+ if ((ret == 0) && (tstInfo->tsa != NULL) && (tstInfo->tsaSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* Serial number must be encodable as given. */
+ if (ret == 0) {
+ ret = TspCheckNum(tstInfo->serial, tstInfo->serialSz);
+ }
+ /* Nonce, when set, must be encodable as given. */
+ if ((ret == 0) && (tstInfo->nonce != NULL)) {
+ ret = TspCheckNum(tstInfo->nonce, tstInfo->nonceSz);
+ }
+ /* Accuracy millis and micros must be 1..999 when set. */
+ if ((ret == 0) && ((tstInfo->accuracy.millis > 999) ||
+ (tstInfo->accuracy.micros > 999))) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ CALLOC_ASNSETDATA(dataASN, tspTstInfoASN_Length, ret, NULL);
+
+ if (ret == 0) {
+ /* Version is 1 - only version defined. */
+ SetASN_Int8Bit(&dataASN[TSPTSTINFOASN_IDX_VER], WC_TSP_VERSION);
+ /* TSA policy. */
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_POLICY], tstInfo->policy,
+ tstInfo->policySz);
+ /* messageImprint - hash algorithm with NULL parameters and hash. */
+ SetASN_OID(&dataASN[TSPTSTINFOASN_IDX_MI_ALG_OID],
+ (int)tstInfo->imprint.hashAlgOID, oidHashType);
+ /* No encoding available for an unknown OID sum. */
+ if (dataASN[TSPTSTINFOASN_IDX_MI_ALG_OID].data.buffer.data == NULL) {
+ ret = ASN_UNKNOWN_OID_E;
+ }
+ }
+ if (ret == 0) {
+ /* Hash of the data time-stamped. */
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_MI_MSG],
+ tstInfo->imprint.hash, tstInfo->imprint.hashSz);
+ /* serialNumber. */
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_SERIAL], tstInfo->serial,
+ tstInfo->serialSz);
+ /* genTime - use current time when not provided. */
+ if (tstInfo->genTime != NULL) {
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_GENTIME],
+ tstInfo->genTime, tstInfo->genTimeSz);
+ }
+ else {
+ #if !defined(NO_ASN_TIME) && !defined(USER_TIME) && \
+ !defined(TIME_OVERRIDES)
+ /* Format the current time as a GeneralizedTime string. */
+ time_t now = wc_Time(0);
+ int len = GetFormattedTime_ex(&now, timeBuf, sizeof(timeBuf),
+ ASN_GENERALIZED_TIME);
+ if (len <= 0) {
+ ret = ASN_TIME_E;
+ }
+ else {
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_GENTIME], timeBuf,
+ (word32)len);
+ }
+ #else
+ /* No clock available - caller must provide the time. */
+ ret = BAD_FUNC_ARG;
+ #endif
+ }
+ }
+ if (ret == 0) {
+ /* accuracy is optional - not encoded when all fields are zero. */
+ if ((tstInfo->accuracy.seconds == 0) &&
+ (tstInfo->accuracy.millis == 0) &&
+ (tstInfo->accuracy.micros == 0)) {
+ SetASNItem_NoOutNode(dataASN, tspTstInfoASN,
+ TSPTSTINFOASN_IDX_ACC_SEQ, tspTstInfoASN_Length);
+ }
+ else {
+ /* Each field is optional - not encoded when zero. */
+ /* INTEGER - leading zero added by encoder when needed. */
+ if (tstInfo->accuracy.seconds != 0) {
+ SetASN_Int32Bit(&dataASN[TSPTSTINFOASN_IDX_ACC_SEC],
+ tstInfo->accuracy.seconds);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspTstInfoASN, 0,
+ TSPTSTINFOASN_IDX_ACC_SEC, tspTstInfoASN_Length);
+ }
+ /* Implicitly tagged INTEGERs. */
+ if (tstInfo->accuracy.millis != 0) {
+ SetASN_Int32BitInt(&dataASN[TSPTSTINFOASN_IDX_ACC_MILLIS],
+ tstInfo->accuracy.millis);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspTstInfoASN, 0,
+ TSPTSTINFOASN_IDX_ACC_MILLIS, tspTstInfoASN_Length);
+ }
+ if (tstInfo->accuracy.micros != 0) {
+ SetASN_Int32BitInt(&dataASN[TSPTSTINFOASN_IDX_ACC_MICROS],
+ tstInfo->accuracy.micros);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspTstInfoASN, 0,
+ TSPTSTINFOASN_IDX_ACC_MICROS, tspTstInfoASN_Length);
+ }
+ }
+ /* ordering defaults to FALSE - only encode when TRUE. */
+ if (tstInfo->ordering) {
+ SetASN_Boolean(&dataASN[TSPTSTINFOASN_IDX_ORDERING], 1);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspTstInfoASN, 0,
+ TSPTSTINFOASN_IDX_ORDERING, tspTstInfoASN_Length);
+ }
+ /* nonce is optional. */
+ if (tstInfo->nonce != NULL) {
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_NONCE], tstInfo->nonce,
+ tstInfo->nonceSz);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspTstInfoASN, 0,
+ TSPTSTINFOASN_IDX_NONCE, tspTstInfoASN_Length);
+ }
+ /* tsa is optional. */
+ if (tstInfo->tsa != NULL) {
+ SetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_TSA], tstInfo->tsa,
+ tstInfo->tsaSz);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspTstInfoASN, 0,
+ TSPTSTINFOASN_IDX_TSA, tspTstInfoASN_Length);
+ }
+ /* Calculate size of encoding. */
+ ret = SizeASN_Items(tspTstInfoASN, dataASN, tspTstInfoASN_Length,
+ &sz);
+ }
+ /* Write out encoding when buffer supplied. */
+ if ((ret == 0) && (out != NULL)) {
+ /* Check buffer is big enough to hold encoding. */
+ if (sz > *outSz) {
+ ret = BUFFER_E;
+ }
+ /* Length written must be the length calculated. */
+ else if (SetASN_Items(tspTstInfoASN, dataASN, tspTstInfoASN_Length,
+ out) != (int)sz) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ if (ret == 0) {
+ /* Return the length of the encoding. */
+ *outSz = sz;
+ }
+
+ FREE_ASNSETDATA(dataASN, NULL);
+ WOLFSSL_LEAVE("wc_TspTstInfo_Encode", ret);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+/* Decode a TSTInfo.
+ *
+ * Pointers in tstInfo reference into input - the buffer must remain
+ * available while tstInfo is in use. The message imprint hash is copied.
+ *
+ * @param [out] tstInfo TSTInfo object to fill.
+ * @param [in] input Buffer holding DER encoding.
+ * @param [in] inSz Length of data in buffer in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or input is NULL or inSz is 0.
+ * @return ASN_PARSE_E when the encoding is invalid, the hash is empty,
+ * accuracy millis or micros is out of range, the genTime is not a
+ * valid GeneralizedTime or extensions are present.
+ * @return ASN_UNKNOWN_OID_E when the hash algorithm OID check fails.
+ * @return BUFFER_E when the hash is longer than WC_TSP_MAX_HASH_SZ bytes.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspTstInfo_Decode(TspTstInfo* tstInfo, const byte* input, word32 inSz)
+{
+ DECL_ASNGETDATA(dataASN, tspTstInfoASN_Length);
+ int ret = 0;
+ word32 idx = 0;
+
+ WOLFSSL_ENTER("wc_TspTstInfo_Decode");
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (input == NULL) || (inSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ CALLOC_ASNGETDATA(dataASN, tspTstInfoASN_Length, ret, NULL);
+
+ if (ret == 0) {
+ /* All fields empty - optional fields left empty when not present. */
+ XMEMSET(tstInfo, 0, sizeof(TspTstInfo));
+
+ /* Version is small - 1 is the only defined value. */
+ GetASN_Int8Bit(&dataASN[TSPTSTINFOASN_IDX_VER], &tstInfo->version);
+ /* Any policy accepted - caller checks it is the one expected. */
+ GetASN_OID(&dataASN[TSPTSTINFOASN_IDX_POLICY], oidIgnoreType);
+ /* Hash algorithm OID checked against known hash OIDs - caller
+ * checks it is usable. */
+ GetASN_OID(&dataASN[TSPTSTINFOASN_IDX_MI_ALG_OID], oidHashType);
+ /* Hash copied into fixed size array - length checked. */
+ tstInfo->imprint.hashSz = (word32)sizeof(tstInfo->imprint.hash);
+ GetASN_Buffer(&dataASN[TSPTSTINFOASN_IDX_MI_MSG],
+ tstInfo->imprint.hash, &tstInfo->imprint.hashSz);
+ /* Accuracy fields default to zero when not present. Millis and
+ * micros are limited to 1..999 - 16 bits enough. */
+ GetASN_Int32Bit(&dataASN[TSPTSTINFOASN_IDX_ACC_SEC],
+ &tstInfo->accuracy.seconds);
+ GetASN_Int16Bit(&dataASN[TSPTSTINFOASN_IDX_ACC_MILLIS],
+ &tstInfo->accuracy.millis);
+ GetASN_Int16Bit(&dataASN[TSPTSTINFOASN_IDX_ACC_MICROS],
+ &tstInfo->accuracy.micros);
+ /* ordering defaults to FALSE when not present. */
+ GetASN_Boolean(&dataASN[TSPTSTINFOASN_IDX_ORDERING],
+ &tstInfo->ordering);
+ /* Decode TSTInfo. */
+ ret = GetASN_Items(tspTstInfoASN, dataASN, tspTstInfoASN_Length, 1,
+ input, &idx, inSz);
+ }
+ /* Check all data used - input is one complete message. */
+ if ((ret == 0) && (idx != inSz)) {
+ ret = ASN_PARSE_E;
+ }
+ /* Hash must not be empty. */
+ if ((ret == 0) && (tstInfo->imprint.hashSz == 0)) {
+ ret = ASN_PARSE_E;
+ }
+ /* Accuracy millis and micros, when present, must be 1..999. */
+ if ((ret == 0) &&
+ (((dataASN[TSPTSTINFOASN_IDX_ACC_MILLIS].tag != 0) &&
+ ((tstInfo->accuracy.millis == 0) ||
+ (tstInfo->accuracy.millis > 999))) ||
+ ((dataASN[TSPTSTINFOASN_IDX_ACC_MICROS].tag != 0) &&
+ ((tstInfo->accuracy.micros == 0) ||
+ (tstInfo->accuracy.micros > 999))))) {
+ ret = ASN_PARSE_E;
+ }
+ if (ret == 0) {
+ /* TSA policy referenced. */
+ GetASN_OIDData(&dataASN[TSPTSTINFOASN_IDX_POLICY], &tstInfo->policy,
+ &tstInfo->policySz);
+ /* messageImprint hash algorithm - hash already copied. */
+ tstInfo->imprint.hashAlgOID =
+ dataASN[TSPTSTINFOASN_IDX_MI_ALG_OID].data.oid.sum;
+ /* Serial number and time string referenced. */
+ GetASN_GetConstRef(&dataASN[TSPTSTINFOASN_IDX_SERIAL],
+ &tstInfo->serial, &tstInfo->serialSz);
+ GetASN_GetConstRef(&dataASN[TSPTSTINFOASN_IDX_GENTIME],
+ &tstInfo->genTime, &tstInfo->genTimeSz);
+ /* genTime must be a valid GeneralizedTime of RFC 3161. */
+ ret = TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz);
+ }
+ if (ret == 0) {
+ /* Optional fields - pointer left NULL when not present. */
+ if (GetASNItem_HaveIdx(dataASN[TSPTSTINFOASN_IDX_NONCE])) {
+ GetASN_GetConstRef(&dataASN[TSPTSTINFOASN_IDX_NONCE],
+ &tstInfo->nonce, &tstInfo->nonceSz);
+ }
+ if (GetASNItem_HaveIdx(dataASN[TSPTSTINFOASN_IDX_TSA])) {
+ GetASN_GetConstRef(&dataASN[TSPTSTINFOASN_IDX_TSA], &tstInfo->tsa,
+ &tstInfo->tsaSz);
+ }
+ }
+
+ FREE_ASNGETDATA(dataASN, NULL);
+ WOLFSSL_LEAVE("wc_TspTstInfo_Decode", ret);
+ return ret;
+}
+
+/* ASN template for decoding TimeStampResp.
+ * RFC 3161, 2.4.2 - Response Format
+ *
+ * PKIFreeText is a SEQUENCE OF UTF8String - not parsed by template.
+ */
+static const ASNItem tspRespASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* status */
+/* STAT_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 },
+ /* status */
+/* STAT */ { 2, ASN_INTEGER, 0, 0, 0 },
+ /* statusString */
+/* STAT_STR */ { 2, ASN_SEQUENCE, 1, 0, 1 },
+ /* failInfo */
+/* STAT_FAIL */ { 2, ASN_BIT_STRING, 0, 0, 1 },
+ /* timeStampToken */
+/* TOKEN */ { 1, ASN_SEQUENCE, 1, 0, 1 },
+};
+/* Named indices for tspRespASN. */
+enum {
+ TSPRESPASN_IDX_SEQ = 0,
+ TSPRESPASN_IDX_STAT_SEQ,
+ TSPRESPASN_IDX_STAT,
+ TSPRESPASN_IDX_STAT_STR,
+ TSPRESPASN_IDX_STAT_FAIL,
+ TSPRESPASN_IDX_TOKEN
+};
+/* Number of items in ASN.1 template for decoding TimeStampResp. */
+#define tspRespASN_Length (sizeof(tspRespASN) / sizeof(ASNItem))
+
+/* ASN template for encoding TimeStampResp.
+ * RFC 3161, 2.4.2 - Response Format
+ *
+ * statusString is encoded as a PKIFreeText with one UTF8String.
+ */
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+static const ASNItem tspRespEncASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* status */
+/* STAT_SEQ */ { 1, ASN_SEQUENCE, 1, 1, 0 },
+ /* status */
+/* STAT */ { 2, ASN_INTEGER, 0, 0, 0 },
+ /* statusString */
+/* STAT_STR_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 1 },
+/* STAT_STR */ { 3, ASN_UTF8STRING, 0, 0, 0 },
+ /* failInfo */
+/* STAT_FAIL */ { 2, ASN_BIT_STRING, 0, 0, 1 },
+ /* timeStampToken */
+/* TOKEN */ { 1, ASN_SEQUENCE, 1, 0, 1 },
+};
+/* Named indices for tspRespEncASN. */
+enum {
+ TSPRESPENCASN_IDX_SEQ = 0,
+ TSPRESPENCASN_IDX_STAT_SEQ,
+ TSPRESPENCASN_IDX_STAT,
+ TSPRESPENCASN_IDX_STAT_STR_SEQ,
+ TSPRESPENCASN_IDX_STAT_STR,
+ TSPRESPENCASN_IDX_STAT_FAIL,
+ TSPRESPENCASN_IDX_TOKEN
+};
+/* Number of items in ASN.1 template for encoding TimeStampResp. */
+#define tspRespEncASN_Length (sizeof(tspRespEncASN) / sizeof(ASNItem))
+
+/* ASN template for a UTF8String of a PKIFreeText.
+ * RFC 3161, 2.4.2.
+ */
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+static const ASNItem tspUtf8StrASN[] = {
+/* STR */ { 0, ASN_UTF8STRING, 0, 0, 0 },
+};
+/* Number of items in ASN.1 template for a UTF8String of a PKIFreeText. */
+#define tspUtf8StrASN_Length (sizeof(tspUtf8StrASN) / sizeof(ASNItem))
+
+/* Get a reference to the first UTF8String in a PKIFreeText.
+ *
+ * @param [in] input Content of PKIFreeText SEQUENCE.
+ * @param [in] inSz Length of content in bytes.
+ * @param [out] str First UTF8String's data.
+ * @param [out] strSz Length of first UTF8String's data in bytes.
+ * @return 0 on success.
+ * @return ASN_PARSE_E when the encoding is invalid.
+ */
+static int TspGetPkiFreeTextStr(const byte* input, word32 inSz,
+ const byte** str, word32* strSz)
+{
+ /* Template is small enough to declare data on the stack. */
+ ASNGetData dataASN[tspUtf8StrASN_Length];
+ int ret;
+ word32 idx = 0;
+
+ XMEMSET(dataASN, 0, sizeof(dataASN));
+
+ /* Decode first UTF8String - any others are ignored. */
+ ret = GetASN_Items(tspUtf8StrASN, dataASN, tspUtf8StrASN_Length, 0,
+ input, &idx, inSz);
+ if (ret == 0) {
+ /* Reference the string's data for the caller. */
+ GetASN_GetConstRef(&dataASN[0], str, strSz);
+ }
+
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Encode a TimeStampResp.
+ *
+ * @param [in] resp TimeStampResp object to encode.
+ * @param [out] out Buffer to hold encoding. May be NULL to get length.
+ * @param [in, out] outSz On in, length of buffer in bytes.
+ * On out, length of encoding in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp or outSz is NULL.
+ * @return BUFFER_E when out is not NULL and encoding is longer than outSz.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspResponse_Encode(const TspResponse* resp, byte* out, word32* outSz)
+{
+ DECL_ASNSETDATA(dataASN, tspRespEncASN_Length);
+ int ret = 0;
+ word32 sz = 0;
+
+ WOLFSSL_ENTER("wc_TspResponse_Encode");
+
+ /* Validate parameters. */
+ if ((resp == NULL) || (outSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ CALLOC_ASNSETDATA(dataASN, tspRespEncASN_Length, ret, NULL);
+
+ if (ret == 0) {
+ /* status. */
+ SetASN_Int8Bit(&dataASN[TSPRESPENCASN_IDX_STAT], resp->status);
+ /* statusString is optional - encoded as a PKIFreeText with one
+ * UTF8String. */
+ if (resp->statusString != NULL) {
+ SetASN_Buffer(&dataASN[TSPRESPENCASN_IDX_STAT_STR],
+ resp->statusString, resp->statusStringSz);
+ }
+ else {
+ SetASNItem_NoOutNode(dataASN, tspRespEncASN,
+ TSPRESPENCASN_IDX_STAT_STR_SEQ, tspRespEncASN_Length);
+ }
+ /* failInfo is optional - BIT STRING from the 32-bit flags word. */
+ if (resp->failInfo != 0) {
+ SetASN_Int32Bit(&dataASN[TSPRESPENCASN_IDX_STAT_FAIL],
+ resp->failInfo);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspRespEncASN, 0,
+ TSPRESPENCASN_IDX_STAT_FAIL, tspRespEncASN_Length);
+ }
+ /* timeStampToken is optional - complete DER encoding. */
+ if (resp->token != NULL) {
+ SetASN_ReplaceBuffer(&dataASN[TSPRESPENCASN_IDX_TOKEN],
+ resp->token, resp->tokenSz);
+ }
+ else {
+ SetASNItem_NoOutNode_ex(dataASN, tspRespEncASN, 0,
+ TSPRESPENCASN_IDX_TOKEN, tspRespEncASN_Length);
+ }
+
+ /* Calculate size of encoding. */
+ ret = SizeASN_Items(tspRespEncASN, dataASN, tspRespEncASN_Length,
+ &sz);
+ }
+ /* Write out encoding when buffer supplied. */
+ if ((ret == 0) && (out != NULL)) {
+ /* Check buffer is big enough to hold encoding. */
+ if (sz > *outSz) {
+ ret = BUFFER_E;
+ }
+ /* Length written must be the length calculated. */
+ else if (SetASN_Items(tspRespEncASN, dataASN, tspRespEncASN_Length,
+ out) != (int)sz) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ if (ret == 0) {
+ /* Return the length of the encoding. */
+ *outSz = sz;
+ }
+
+ FREE_ASNSETDATA(dataASN, NULL);
+ WOLFSSL_LEAVE("wc_TspResponse_Encode", ret);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+/* Decode a TimeStampResp.
+ *
+ * Pointers in resp reference into input - the buffer must remain available
+ * while resp is in use.
+ *
+ * The TSTInfo of the timeStampToken is not validated or decoded - see
+ * wc_TspTstInfo_VerifyWithPKCS7() and wc_TspTstInfo_Decode().
+ *
+ * @param [out] resp TimeStampResp object to fill.
+ * @param [in] input Buffer holding DER encoding.
+ * @param [in] inSz Length of data in buffer in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp or input is NULL or inSz is 0.
+ * @return ASN_PARSE_E when the encoding is invalid.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspResponse_Decode(TspResponse* resp, const byte* input, word32 inSz)
+{
+ DECL_ASNGETDATA(dataASN, tspRespASN_Length);
+ int ret = 0;
+ word32 idx = 0;
+
+ WOLFSSL_ENTER("wc_TspResponse_Decode");
+
+ /* Validate parameters. */
+ if ((resp == NULL) || (input == NULL) || (inSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ CALLOC_ASNGETDATA(dataASN, tspRespASN_Length, ret, NULL);
+
+ if (ret == 0) {
+ /* All fields empty - optional fields left empty when not present. */
+ XMEMSET(resp, 0, sizeof(TspResponse));
+
+ /* Status is small - 0 to 5 defined. */
+ GetASN_Int8Bit(&dataASN[TSPRESPASN_IDX_STAT], &resp->status);
+ /* failInfo BIT STRING data into the 32-bit flags word. */
+ GetASN_Int32Bit(&dataASN[TSPRESPASN_IDX_STAT_FAIL], &resp->failInfo);
+ /* Decode TimeStampResp. */
+ ret = GetASN_Items(tspRespASN, dataASN, tspRespASN_Length, 1, input,
+ &idx, inSz);
+ }
+ /* Check all data used - input is one complete message. */
+ if ((ret == 0) && (idx != inSz)) {
+ ret = ASN_PARSE_E;
+ }
+ /* statusString is optional - reference first UTF8String. */
+ if ((ret == 0) && GetASNItem_HaveIdx(dataASN[TSPRESPASN_IDX_STAT_STR])) {
+ const byte* freeText;
+ word32 freeTextSz;
+
+ /* Get the content of the PKIFreeText SEQUENCE and parse it. */
+ GetASN_GetConstRef(&dataASN[TSPRESPASN_IDX_STAT_STR], &freeText,
+ &freeTextSz);
+ ret = TspGetPkiFreeTextStr(freeText, freeTextSz, &resp->statusString,
+ &resp->statusStringSz);
+ }
+ if (ret == 0) {
+ /* failInfo is optional. Length includes unused bits byte.
+ * Shift up by the number of bytes not encoded - bit 0 of the named
+ * bit string is the most significant bit of 32. */
+ if (dataASN[TSPRESPASN_IDX_STAT_FAIL].tag != 0) {
+ resp->failInfo <<=
+ (8 * (5 - dataASN[TSPRESPASN_IDX_STAT_FAIL].length));
+ }
+ /* timeStampToken is optional - complete DER encoding referenced. */
+ if (GetASNItem_HaveIdx(dataASN[TSPRESPASN_IDX_TOKEN])) {
+ resp->token = GetASNItem_Addr(dataASN[TSPRESPASN_IDX_TOKEN],
+ input);
+ resp->tokenSz = GetASNItem_Length(dataASN[TSPRESPASN_IDX_TOKEN],
+ input);
+ }
+ }
+
+ FREE_ASNGETDATA(dataASN, NULL);
+ WOLFSSL_LEAVE("wc_TspResponse_Decode", ret);
+ return ret;
+}
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+#ifdef HAVE_PKCS7
+
+/* id-aa-signingCertificateV2: 1.2.840.113549.1.9.16.2.47. RFC 5035.
+ * Not static - also used by wc_TspTstInfo_SignWithPkcs7() in tsp.c (declared in
+ * tsp.h). */
+const byte tspSigningCertV2Oid[] = {
+ ASN_OBJECT_ID, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x2f
+};
+
+#ifdef WOLFSSL_TSP_VERIFIER
+#ifndef NO_SHA
+/* id-aa-signingCertificate: 1.2.840.113549.1.9.16.2.12. RFC 2634. */
+static const byte tspSigningCertOid[] = {
+ ASN_OBJECT_ID, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x0c
+};
+#endif
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+
+/* ASN template for SigningCertificateV2.
+ * RFC 5035, 3 - Attribute Certificate Definition
+ *
+ * First ESSCertIDv2 only - any issuerSerial, further ESSCertIDv2s and
+ * policies are skipped on decode and not encoded.
+ */
+static const ASNItem tspSignCertV2ASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* certs */
+/* CERTS */ { 1, ASN_SEQUENCE, 1, 1, 0 },
+ /* ESSCertIDv2 */
+/* CERTID */ { 2, ASN_SEQUENCE, 1, 1, 0 },
+ /* hashAlgorithm */
+/* HASH_SEQ */ { 3, ASN_SEQUENCE, 1, 1, 1 },
+/* HASH_OID */ { 4, ASN_OBJECT_ID, 0, 0, 0 },
+/* HASH_NULL */ { 4, ASN_TAG_NULL, 0, 0, 1 },
+ /* certHash */
+/* HASH */ { 3, ASN_OCTET_STRING, 0, 0, 0 },
+};
+/* Named indices for tspSignCertV2ASN. */
+enum {
+ TSPSIGNCERTV2ASN_IDX_SEQ = 0,
+ TSPSIGNCERTV2ASN_IDX_CERTS,
+ TSPSIGNCERTV2ASN_IDX_CERTID,
+ TSPSIGNCERTV2ASN_IDX_HASH_SEQ,
+ TSPSIGNCERTV2ASN_IDX_HASH_OID,
+ TSPSIGNCERTV2ASN_IDX_HASH_NULL,
+ TSPSIGNCERTV2ASN_IDX_HASH
+};
+/* Number of items in ASN.1 template for SigningCertificateV2. */
+#define tspSignCertV2ASN_Length (sizeof(tspSignCertV2ASN) / sizeof(ASNItem))
+
+#ifdef WOLFSSL_TSP_VERIFIER
+#ifndef NO_SHA
+/* ASN template for SigningCertificate.
+ * RFC 2634, 5.4 - Signing Certificate Attribute Definition
+ *
+ * First ESSCertID only - any issuerSerial, further ESSCertIDs and policies
+ * are skipped on decode.
+ */
+static const ASNItem tspSignCertASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* certs */
+/* CERTS */ { 1, ASN_SEQUENCE, 1, 1, 0 },
+ /* ESSCertID */
+/* CERTID */ { 2, ASN_SEQUENCE, 1, 1, 0 },
+ /* certHash */
+/* HASH */ { 3, ASN_OCTET_STRING, 0, 0, 0 },
+};
+/* Named indices for tspSignCertASN. */
+enum {
+ TSPSIGNCERTASN_IDX_SEQ = 0,
+ TSPSIGNCERTASN_IDX_CERTS,
+ TSPSIGNCERTASN_IDX_CERTID,
+ TSPSIGNCERTASN_IDX_HASH
+};
+/* Number of items in ASN.1 template for SigningCertificate. */
+#define tspSignCertASN_Length (sizeof(tspSignCertASN) / sizeof(ASNItem))
+#endif /* !NO_SHA */
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Encode a SigningCertificateV2 with the hash of the signer's certificate.
+ *
+ * @param [in] hashOID Hash algorithm OID sum - hash of token signing.
+ * @param [in] cert DER encoded certificate of signer.
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [out] out Buffer to hold encoding.
+ * @param [in, out] outSz On in, length of buffer in bytes.
+ * On out, length of encoding in bytes.
+ * @param [in] heap Dynamic memory allocation hint.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when the hash algorithm is not usable.
+ * @return BUFFER_E when encoding is longer than outSz.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int TspEncodeSigningCertV2(int hashOID, const byte* cert, word32 certSz,
+ byte* out, word32* outSz, void* heap)
+{
+ DECL_ASNSETDATA(dataASN, tspSignCertV2ASN_Length);
+ int ret = 0;
+ word32 sz = 0;
+ WC_DECLARE_VAR(digest, byte, WC_MAX_DIGEST_SIZE, heap);
+ enum wc_HashType hashType;
+ int hashSz = 0;
+
+ WC_ALLOC_VAR_EX(digest, byte, WC_MAX_DIGEST_SIZE, heap,
+ DYNAMIC_TYPE_DIGEST, return MEMORY_E);
+
+ /* Get the digest size to check hash algorithm is available. */
+ hashType = wc_OidGetHash(hashOID);
+ hashSz = wc_HashGetDigestSize(hashType);
+ if (hashSz <= 0) {
+ ret = BAD_FUNC_ARG;
+ }
+ if (ret == 0) {
+ /* Hash certificate of token signer. */
+ ret = wc_Hash(hashType, cert, certSz, digest, WC_MAX_DIGEST_SIZE);
+ }
+
+ CALLOC_ASNSETDATA(dataASN, tspSignCertV2ASN_Length, ret, heap);
+
+ if (ret == 0) {
+ /* SHA-256 is the default hash algorithm - not encoded. */
+ if (hashOID == SHA256h) {
+ SetASNItem_NoOutNode(dataASN, tspSignCertV2ASN,
+ TSPSIGNCERTV2ASN_IDX_HASH_SEQ, tspSignCertV2ASN_Length);
+ }
+ else {
+ SetASN_OID(&dataASN[TSPSIGNCERTV2ASN_IDX_HASH_OID], hashOID,
+ oidHashType);
+ /* No encoding available for an unknown OID sum. */
+ if (dataASN[TSPSIGNCERTV2ASN_IDX_HASH_OID].data.buffer.data ==
+ NULL) {
+ ret = ASN_UNKNOWN_OID_E;
+ }
+ /* No parameters with hash algorithm. */
+ SetASNItem_NoOutNode_ex(dataASN, tspSignCertV2ASN, 0,
+ TSPSIGNCERTV2ASN_IDX_HASH_NULL, tspSignCertV2ASN_Length);
+ }
+ }
+ if (ret == 0) {
+ /* certHash is the hash of the signer's certificate. */
+ SetASN_Buffer(&dataASN[TSPSIGNCERTV2ASN_IDX_HASH], digest,
+ (word32)hashSz);
+
+ /* Calculate size of encoding. */
+ ret = SizeASN_Items(tspSignCertV2ASN, dataASN,
+ tspSignCertV2ASN_Length, &sz);
+ }
+ /* Write out encoding when buffer supplied. */
+ if ((ret == 0) && (out != NULL)) {
+ /* Check buffer is big enough to hold encoding. */
+ if (sz > *outSz) {
+ ret = BUFFER_E;
+ }
+ /* Length written must be the length calculated. */
+ else if (SetASN_Items(tspSignCertV2ASN, dataASN,
+ tspSignCertV2ASN_Length, out) != (int)sz) {
+ ret = ASN_PARSE_E;
+ }
+ }
+ if (ret == 0) {
+ /* Return the length of the encoding. */
+ *outSz = sz;
+ }
+
+ FREE_ASNSETDATA(dataASN, heap);
+ WC_FREE_VAR_EX(digest, heap, DYNAMIC_TYPE_DIGEST);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+
+/* Find a decoded signed attribute by OID.
+ *
+ * @param [in] pkcs7 PKCS7 object with decoded signed attributes.
+ * @param [in] oid DER encoding of OBJECT IDENTIFIER.
+ * @param [in] oidSz Length of OBJECT IDENTIFIER in bytes.
+ * @return Decoded attribute when found.
+ * @return NULL when not found.
+ */
+static const PKCS7DecodedAttrib* TspFindAttrib(wc_PKCS7* pkcs7,
+ const byte* oid, word32 oidSz)
+{
+ const PKCS7DecodedAttrib* attrib;
+
+ /* Search the linked list for the OID. */
+ for (attrib = pkcs7->decodedAttrib; attrib != NULL;
+ attrib = attrib->next) {
+ if ((attrib->oidSz == oidSz) &&
+ (XMEMCMP(attrib->oid, oid, oidSz) == 0)) {
+ break;
+ }
+ }
+
+ return attrib;
+}
+
+/* Check the signing certificate attribute matches the signer's certificate.
+ *
+ * RFC 3161, 2.4.2: the signing certificate attribute of ESS must be present.
+ * SigningCertificateV2 of RFC 5816 also accepted. The hash of the signer's
+ * certificate must match the certHash of the first ESSCertID(v2).
+ *
+ * @param [in] pkcs7 PKCS7 object that verified the token.
+ * @return 0 on success.
+ * @return TSP_VERIFY_E when no signing certificate attribute is found or
+ * the certificate hash does not match.
+ * @return HASH_TYPE_E when the hash algorithm is not available.
+ * @return ASN_PARSE_E when the attribute encoding is invalid.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int TspCheckSigningCertAttr(wc_PKCS7* pkcs7)
+{
+ DECL_ASNGETDATA(dataASN, tspSignCertV2ASN_Length);
+ int ret = 0;
+ const PKCS7DecodedAttrib* attrib;
+ const byte* certHash = NULL;
+ word32 certHashSz = 0;
+ word32 hashOID = SHA256h;
+ enum wc_HashType hashType;
+ int hashSz = 0;
+ word32 idx = 0;
+ WC_DECLARE_VAR(digest, byte, WC_MAX_DIGEST_SIZE, pkcs7->heap);
+
+ WC_ALLOC_VAR_EX(digest, byte, WC_MAX_DIGEST_SIZE, pkcs7->heap,
+ DYNAMIC_TYPE_DIGEST, return MEMORY_E);
+
+ CALLOC_ASNGETDATA(dataASN, tspSignCertV2ASN_Length, ret, pkcs7->heap);
+
+ if (ret == 0) {
+ /* Look for SigningCertificateV2 first - RFC 5816. */
+ attrib = TspFindAttrib(pkcs7, tspSigningCertV2Oid,
+ (word32)sizeof(tspSigningCertV2Oid));
+ if (attrib != NULL) {
+ /* Any hash algorithm accepted - checked when hashing. */
+ GetASN_OID(&dataASN[TSPSIGNCERTV2ASN_IDX_HASH_OID],
+ oidIgnoreType);
+ /* Decode first ESSCertIDv2 of SigningCertificateV2. */
+ ret = GetASN_Items(tspSignCertV2ASN, dataASN,
+ tspSignCertV2ASN_Length, 0, attrib->value, &idx,
+ attrib->valueSz);
+ if (ret == 0) {
+ /* SHA-256 is the default hash algorithm. */
+ if (dataASN[TSPSIGNCERTV2ASN_IDX_HASH_OID].tag != 0) {
+ hashOID =
+ dataASN[TSPSIGNCERTV2ASN_IDX_HASH_OID].data.oid.sum;
+ }
+ GetASN_GetConstRef(&dataASN[TSPSIGNCERTV2ASN_IDX_HASH],
+ &certHash, &certHashSz);
+ }
+ }
+ else {
+ #ifndef NO_SHA
+ /* Fall back to SigningCertificate of ESS - RFC 2634. */
+ attrib = TspFindAttrib(pkcs7, tspSigningCertOid,
+ (word32)sizeof(tspSigningCertOid));
+ if (attrib != NULL) {
+ /* SHA-1 is the hash algorithm of ESSCertID. */
+ hashOID = SHAh;
+ /* Decode first ESSCertID of SigningCertificate. */
+ ret = GetASN_Items(tspSignCertASN, dataASN,
+ tspSignCertASN_Length, 0, attrib->value, &idx,
+ attrib->valueSz);
+ if (ret == 0) {
+ GetASN_GetConstRef(&dataASN[TSPSIGNCERTASN_IDX_HASH],
+ &certHash, &certHashSz);
+ }
+ }
+ else
+ #endif
+ {
+ /* The signing certificate attribute must be present. */
+ WOLFSSL_MSG("TSP token has no signing certificate attribute");
+ ret = TSP_VERIFY_E;
+ }
+ }
+ }
+ /* The hash algorithm must meet the minimum strength. */
+ if (ret == 0) {
+ ret = Tsp_CheckHashStrength(hashOID);
+ }
+ /* Compare against hash of the signer's certificate. */
+ if (ret == 0) {
+ /* Get the digest size to check hash algorithm is available. */
+ hashType = wc_OidGetHash((int)hashOID);
+ hashSz = wc_HashGetDigestSize(hashType);
+ if (hashSz <= 0) {
+ ret = HASH_TYPE_E;
+ }
+ }
+ if (ret == 0) {
+ /* Hash the certificate that verified the token. */
+ ret = wc_Hash(hashType, pkcs7->verifyCert, pkcs7->verifyCertSz,
+ digest, WC_MAX_DIGEST_SIZE);
+ }
+ /* certHash must be the hash of the signer's certificate. */
+ if ((ret == 0) && ((certHashSz != (word32)hashSz) ||
+ (XMEMCMP(certHash, digest, certHashSz) != 0))) {
+ WOLFSSL_MSG("TSP signing certificate attribute hash mismatch");
+ ret = TSP_VERIFY_E;
+ }
+
+ FREE_ASNGETDATA(dataASN, pkcs7->heap);
+ WC_FREE_VAR_EX(digest, pkcs7->heap, DYNAMIC_TYPE_DIGEST);
+ return ret;
+}
+
+/* ASN template for the SignedData of a TimeStampToken.
+ * RFC 5652, 5.1 - SignedData Type
+ *
+ * Parsed only as far as needed to find the signerInfos.
+ */
+static const ASNItem tspTokenASN[] = {
+/* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
+ /* contentType */
+/* TYPE */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
+ /* content */
+/* CONTENT */ { 1, ASN_CONTEXT_SPECIFIC | 0, 1, 1, 0 },
+ /* SignedData */
+/* SD_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 0 },
+ /* version */
+/* VER */ { 3, ASN_INTEGER, 0, 0, 0 },
+ /* digestAlgorithms */
+/* DIG_ALGS */ { 3, ASN_SET, 1, 0, 0 },
+ /* encapContentInfo */
+/* ENCAP */ { 3, ASN_SEQUENCE, 1, 0, 0 },
+ /* certificates */
+/* CERTS */ { 3, ASN_CONTEXT_SPECIFIC | 0, 1, 0, 1 },
+ /* crls */
+/* CRLS */ { 3, ASN_CONTEXT_SPECIFIC | 1, 1, 0, 1 },
+ /* signerInfos */
+/* SIGNERS */ { 3, ASN_SET, 1, 0, 0 },
+};
+/* Named indices for tspTokenASN. */
+enum {
+ TSPTOKENASN_IDX_SEQ = 0,
+ TSPTOKENASN_IDX_TYPE,
+ TSPTOKENASN_IDX_CONTENT,
+ TSPTOKENASN_IDX_SD_SEQ,
+ TSPTOKENASN_IDX_VER,
+ TSPTOKENASN_IDX_DIG_ALGS,
+ TSPTOKENASN_IDX_ENCAP,
+ TSPTOKENASN_IDX_CERTS,
+ TSPTOKENASN_IDX_CRLS,
+ TSPTOKENASN_IDX_SIGNERS
+};
+/* Number of items in ASN.1 template for the SignedData of a token. */
+#define tspTokenASN_Length (sizeof(tspTokenASN) / sizeof(ASNItem))
+
+/* Check the token has exactly one SignerInfo.
+ *
+ * RFC 3161, 2.4.2: the time-stamp token must contain a single SignerInfo.
+ *
+ * @param [in] token Buffer holding DER encoding of token.
+ * @param [in] tokenSz Length of data in buffer in bytes.
+ * @param [in] heap Dynamic memory allocation hint.
+ * @return 0 on success.
+ * @return ASN_PARSE_E when the encoding is invalid.
+ * @return TSP_VERIFY_E when there is not exactly one SignerInfo.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int TspCheckOneSignerInfo(const byte* token, word32 tokenSz, void* heap)
+{
+ DECL_ASNGETDATA(dataASN, tspTokenASN_Length);
+ int ret = 0;
+ word32 idx = 0;
+ const byte* signers = NULL;
+ word32 signersSz = 0;
+ int cnt = 0;
+
+ CALLOC_ASNGETDATA(dataASN, tspTokenASN_Length, ret, heap);
+
+ if (ret == 0) {
+ /* Parse down to the signerInfos. */
+ ret = GetASN_Items(tspTokenASN, dataASN, tspTokenASN_Length, 1,
+ token, &idx, tokenSz);
+ }
+ if (ret == 0) {
+ /* Count the SignerInfo SEQUENCEs in the SET. */
+ GetASN_GetConstRef(&dataASN[TSPTOKENASN_IDX_SIGNERS], &signers,
+ &signersSz);
+ idx = 0;
+ while ((ret == 0) && (idx < signersSz)) {
+ byte tag = 0;
+ int len = 0;
+
+ if ((GetASNTag(signers, &idx, &tag, signersSz) < 0) ||
+ (tag != (ASN_SEQUENCE | ASN_CONSTRUCTED)) ||
+ (GetLength(signers, &idx, &len, signersSz) < 0)) {
+ ret = ASN_PARSE_E;
+ }
+ else {
+ /* Step over the SignerInfo. */
+ idx += (word32)len;
+ cnt++;
+ }
+ }
+ }
+ if ((ret == 0) && (cnt != 1)) {
+ WOLFSSL_MSG("TSP token must have one SignerInfo");
+ ret = TSP_VERIFY_E;
+ }
+
+ FREE_ASNGETDATA(dataASN, heap);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif /* HAVE_PKCS7 */
+
+#endif /* WOLFSSL_TSP && WOLFSSL_ASN_TEMPLATE && !NO_ASN */
+
+#endif /* WOLFSSL_ASN_TSP_INCLUDED */
diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c
index 0f70a84cc8b..0cca67dc0cf 100644
--- a/wolfcrypt/src/error.c
+++ b/wolfcrypt/src/error.c
@@ -692,6 +692,9 @@ const char* wc_GetErrorString(int error)
case SLH_DSA_KAT_FIPS_E:
return "SLH-DSA Known Answer Test check FIPS error";
+ case TSP_VERIFY_E:
+ return "TSP token invalid or response doesn't match request error";
+
case SEQ_OVERFLOW_E:
return "Sequence counter would overflow";
diff --git a/wolfcrypt/src/include.am b/wolfcrypt/src/include.am
index 908c43984cd..ba3d3dfeadb 100644
--- a/wolfcrypt/src/include.am
+++ b/wolfcrypt/src/include.am
@@ -12,6 +12,7 @@ MAINTAINERCLEANFILES+= $(ASYNC_FILES)
EXTRA_DIST += wolfcrypt/src/misc.c
EXTRA_DIST += wolfcrypt/src/asn_orig.c
+EXTRA_DIST += wolfcrypt/src/asn_tsp.c
EXTRA_DIST += wolfcrypt/src/evp.c
EXTRA_DIST += wolfcrypt/src/evp_pk.c
EXTRA_DIST += wolfcrypt/src/asm.c
diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c
index b04504a0054..60ebea46a7a 100644
--- a/wolfcrypt/src/pkcs7.c
+++ b/wolfcrypt/src/pkcs7.c
@@ -599,6 +599,11 @@ static int wc_SetContentType(int pkcs7TypeOID, byte* output, word32 outputSz)
/* FirmwarePkgData (1.2.840.113549.1.9.16.1.16), RFC 4108 */
static const byte firmwarePkgData[] =
{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x10, 0x01, 0x10 };
+#ifdef WOLFSSL_TSP
+ /* id-ct-TSTInfo (1.2.840.113549.1.9.16.1.4), RFC 3161 */
+ static const byte tstInfoData[] =
+ { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x10, 0x01, 0x04 };
+#endif
#if defined(HAVE_LIBZ) && !defined(NO_PKCS7_COMPRESSED_DATA)
/* id-ct-compressedData (1.2.840.113549.1.9.16.1.9), RFC 3274 */
static const byte compressedData[] =
@@ -670,6 +675,13 @@ static int wc_SetContentType(int pkcs7TypeOID, byte* output, word32 outputSz)
typeName = firmwarePkgData;
break;
+#ifdef WOLFSSL_TSP
+ case TSTINFO_DATA:
+ typeSz = sizeof(tstInfoData);
+ typeName = tstInfoData;
+ break;
+#endif
+
#if !defined(NO_PWDBASED) && !defined(NO_SHA)
case PWRI_KEK_WRAP:
typeSz = sizeof(pwriKek);
diff --git a/wolfcrypt/src/tsp.c b/wolfcrypt/src/tsp.c
new file mode 100644
index 00000000000..d6d666e8558
--- /dev/null
+++ b/wolfcrypt/src/tsp.c
@@ -0,0 +1,2370 @@
+/* tsp.c
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/*
+ * DESCRIPTION
+ * This library provides the application interface to the Time-Stamp Protocol
+ * (TSP): TimeStampReq, TSTInfo and TimeStampResp setup and accessors, and
+ * verification helpers for a response. RFC 3161.
+ *
+ * The encoding and decoding of TSP messages, and creation and verification of
+ * time-stamp tokens (which use ASN.1 template machinery private to asn.c), are
+ * in asn_tsp.c.
+ */
+
+#include
+
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_ASN_TEMPLATE) && !defined(NO_ASN)
+
+#include
+#include
+#include
+#include
+#include
+#ifdef HAVE_PKCS7
+ #include
+#endif
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Initialize a TimeStampReq.
+ *
+ * @param [out] req TimeStampReq object.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req is NULL.
+ */
+int wc_TspRequest_Init(TspRequest* req)
+{
+ /* Validate parameter. */
+ if (req == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* All fields empty - optional fields not encoded. */
+ XMEMSET(req, 0, sizeof(TspRequest));
+ /* Only version 1 defined. */
+ req->version = WC_TSP_VERSION;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the message imprint hash type of a TimeStampReq.
+ *
+ * Maps the message imprint hash algorithm OID to a hash type. The OID may be
+ * one not recognized as a hash algorithm - e.g. after decoding a request from
+ * an unknown source.
+ *
+ * @param [in] req TimeStampReq object.
+ * @param [out] hashType Hash algorithm of the message imprint.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or hashType is NULL.
+ * @return HASH_TYPE_E when the hash algorithm is not a recognized hash.
+ */
+int wc_TspRequest_GetHashType(const TspRequest* req, enum wc_HashType* hashType)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (hashType == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ if (ret == 0) {
+ /* Map the OID sum to a hash type - NONE when not a known hash. */
+ *hashType = wc_OidGetHash((int)req->imprint.hashAlgOID);
+ if (*hashType == WC_HASH_TYPE_NONE) {
+ ret = HASH_TYPE_E;
+ }
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Set the message imprint hash algorithm of a TimeStampReq.
+ *
+ * Sets the hash algorithm OID and hash size from the hash type. The caller
+ * fills req->imprint.hash with the digest of the data to be time-stamped.
+ *
+ * @param [in, out] req TimeStampReq object.
+ * @param [in] hashType Hash algorithm to use - e.g. WC_HASH_TYPE_SHA256.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req is NULL.
+ * @return HASH_TYPE_E when the hash algorithm is not available.
+ * @return BUFFER_E when the digest is too big for the message imprint.
+ */
+int wc_TspRequest_SetHashType(TspRequest* req, enum wc_HashType hashType)
+{
+ int ret = 0;
+ int oid = 0;
+ int digestSz = 0;
+
+ /* Validate parameter. */
+ if (req == NULL) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ if (ret == 0) {
+ /* Map the hash type to its OID sum - negative when not available. */
+ oid = wc_HashGetOID(hashType);
+ if (oid <= 0) {
+ ret = HASH_TYPE_E;
+ }
+ }
+ if (ret == 0) {
+ /* The digest size is the length of the message imprint hash. */
+ digestSz = wc_HashGetDigestSize(hashType);
+ if (digestSz <= 0) {
+ ret = HASH_TYPE_E;
+ }
+ else if (digestSz > (int)sizeof(req->imprint.hash)) {
+ ret = BUFFER_E;
+ }
+ }
+ if (ret == 0) {
+ req->imprint.hashAlgOID = (word32)oid;
+ req->imprint.hashSz = (word32)digestSz;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the message imprint hash of a TimeStampReq.
+ *
+ * Copies the hash into the caller's buffer.
+ *
+ * @param [in] req TimeStampReq object.
+ * @param [out] hash Buffer to hold the hash.
+ * @param [in, out] hashSz On in, length of buffer in bytes.
+ * On out, length of the hash in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req, hash or hashSz is NULL.
+ * @return BUFFER_E when the buffer is too small for the hash.
+ */
+int wc_TspRequest_GetHash(const TspRequest* req, byte* hash, word32* hashSz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (hash == NULL) || (hashSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (*hashSz < req->imprint.hashSz) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ XMEMCPY(hash, req->imprint.hash, req->imprint.hashSz);
+ *hashSz = req->imprint.hashSz;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Set the message imprint hash of a TimeStampReq.
+ *
+ * Copies the hash and its length into the message imprint. The hash algorithm
+ * is set separately - see wc_TspRequest_SetHashType().
+ *
+ * @param [in, out] req TimeStampReq object.
+ * @param [in] hash Hash of the data to be time-stamped.
+ * @param [in] hashSz Length of hash in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or hash is NULL or hashSz is 0.
+ * @return BUFFER_E when hashSz is too big for the message imprint.
+ */
+int wc_TspRequest_SetHash(TspRequest* req, const byte* hash, word32 hashSz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (hash == NULL) || (hashSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (hashSz > sizeof(req->imprint.hash)) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ XMEMCPY(req->imprint.hash, hash, hashSz);
+ req->imprint.hashSz = hashSz;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the nonce of a TimeStampReq.
+ *
+ * Copies the nonce into the caller's buffer. A length of 0 means no nonce is
+ * set.
+ *
+ * @param [in] req TimeStampReq object.
+ * @param [out] nonce Buffer to hold the nonce.
+ * @param [in, out] nonceSz On in, length of buffer in bytes.
+ * On out, length of the nonce in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req, nonce or nonceSz is NULL.
+ * @return BUFFER_E when the buffer is too small for the nonce.
+ */
+int wc_TspRequest_GetNonce(const TspRequest* req, byte* nonce, word32* nonceSz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (nonce == NULL) || (nonceSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (*nonceSz < req->nonceSz) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ XMEMCPY(nonce, req->nonce, req->nonceSz);
+ *nonceSz = req->nonceSz;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Set the nonce of a TimeStampReq.
+ *
+ * The nonce is a big-endian number that must not have a leading zero byte to
+ * encode. Leading zero bytes are stripped, keeping at least one byte so an
+ * all-zero nonce becomes the number zero.
+ *
+ * @param [in, out] req TimeStampReq object.
+ * @param [in] nonce Nonce as a big-endian number.
+ * @param [in] nonceSz Length of nonce in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or nonce is NULL or nonceSz is 0.
+ * @return BUFFER_E when nonceSz is too big for the nonce field.
+ */
+int wc_TspRequest_SetNonce(TspRequest* req, const byte* nonce, word32 nonceSz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (nonce == NULL) || (nonceSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (nonceSz > sizeof(req->nonce)) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ /* Strip leading zeros. */
+ while ((nonceSz > 1) && (nonce[0] == 0x00)) {
+ nonce++;
+ nonceSz--;
+ }
+ XMEMCPY(req->nonce, nonce, nonceSz);
+ req->nonceSz = nonceSz;
+ }
+
+ return ret;
+}
+
+#ifndef WC_NO_RNG
+/* Generate a random nonce for a TimeStampReq.
+ *
+ * A convenience over generating random bytes and calling
+ * wc_TspRequest_SetNonce(). The nonce is a minimal positive INTEGER: the top
+ * bit of the first byte is cleared so it is positive and the first byte is
+ * made non-zero so there is no leading zero byte to strip.
+ *
+ * @param [in, out] req TimeStampReq object.
+ * @param [in] rng Random number generator.
+ * @param [in] sz Length of nonce to generate in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or rng is NULL or sz is 0.
+ * @return BUFFER_E when sz is too big for the nonce field.
+ * @return Other negative value on random number generation failure.
+ */
+int wc_TspRequest_GenerateNonce(TspRequest* req, WC_RNG* rng, word32 sz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (rng == NULL) || (sz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (sz > sizeof(req->nonce)) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ ret = wc_RNG_GenerateBlock(rng, req->nonce, sz);
+ }
+ if (ret == 0) {
+ /* Make a minimal positive INTEGER: clear the sign bit and ensure a
+ * non-zero leading byte so there is no leading zero to strip. */
+ req->nonce[0] &= 0x7F;
+ if (req->nonce[0] == 0x00) {
+ req->nonce[0] = 0x01;
+ }
+ req->nonceSz = sz;
+ }
+
+ return ret;
+}
+#endif /* WC_NO_RNG */
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the TSA policy of a TimeStampReq.
+ *
+ * Copies the policy into the caller's buffer. A length of 0 means no policy is
+ * set.
+ *
+ * @param [in] req TimeStampReq object.
+ * @param [out] policy Buffer to hold the policy.
+ * @param [in, out] policySz On in, length of buffer in bytes.
+ * On out, length of the policy in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req, policy or policySz is NULL.
+ * @return BUFFER_E when the buffer is too small for the policy.
+ */
+int wc_TspRequest_GetPolicy(const TspRequest* req, byte* policy,
+ word32* policySz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (policy == NULL) || (policySz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (*policySz < req->policySz) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ XMEMCPY(policy, req->policy, req->policySz);
+ *policySz = req->policySz;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Set the TSA policy of a TimeStampReq.
+ *
+ * The policy is the content of an OBJECT IDENTIFIER - the bytes after the type
+ * and length. It is copied into the request.
+ *
+ * @param [in, out] req TimeStampReq object.
+ * @param [in] policy Policy as OBJECT IDENTIFIER content.
+ * @param [in] policySz Length of policy in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when req or policy is NULL or policySz is 0.
+ * @return BUFFER_E when policySz is too big for the policy field.
+ */
+int wc_TspRequest_SetPolicy(TspRequest* req, const byte* policy,
+ word32 policySz)
+{
+ int ret = 0;
+
+ /* Validate parameters. */
+ if ((req == NULL) || (policy == NULL) || (policySz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+ else if (policySz > sizeof(req->policy)) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ XMEMCPY(req->policy, policy, policySz);
+ req->policySz = policySz;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Initialize a TSTInfo.
+ *
+ * @param [out] tstInfo TSTInfo object.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo is NULL.
+ */
+int wc_TspTstInfo_Init(TspTstInfo* tstInfo)
+{
+ /* Validate parameter. */
+ if (tstInfo == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* All fields empty - optional fields not encoded. */
+ XMEMSET(tstInfo, 0, sizeof(TspTstInfo));
+ /* Only version 1 defined. */
+ tstInfo->version = WC_TSP_VERSION;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the serial number of a TSTInfo.
+ *
+ * Returns a reference to the serial number - it is not copied and is valid
+ * while the TSTInfo references it.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] serial Serial number as a big-endian number.
+ * @param [out] serialSz Length of serial number in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, serial or serialSz is NULL.
+ */
+int wc_TspTstInfo_GetSerial(const TspTstInfo* tstInfo, const byte** serial,
+ word32* serialSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (serial == NULL) || (serialSz == NULL)) {
+ return BAD_FUNC_ARG;
+ }
+
+ *serial = tstInfo->serial;
+ *serialSz = tstInfo->serialSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the serial number of a TSTInfo.
+ *
+ * The serial number is a big-endian number that is referenced - it is not
+ * copied and must remain available while the TSTInfo is used. Leading zero
+ * bytes are stripped, keeping at least one byte, so the serial number has no
+ * leading zero byte and encodes - an all-zero serial number becomes zero.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] serial Serial number as a big-endian number.
+ * @param [in] serialSz Length of serial number in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or serial is NULL or serialSz is 0.
+ */
+int wc_TspTstInfo_SetSerial(TspTstInfo* tstInfo, const byte* serial,
+ word32 serialSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (serial == NULL) || (serialSz == 0)) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* Strip leading zero bytes - keep at least one byte. */
+ while ((serialSz > 1) && (serial[0] == 0x00)) {
+ serial++;
+ serialSz--;
+ }
+ tstInfo->serial = serial;
+ tstInfo->serialSz = serialSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the TSA policy of a TSTInfo.
+ *
+ * Returns a reference to the policy - it is not copied and is valid while the
+ * TSTInfo references it. A length of 0 means no policy is present.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] policy Policy as OBJECT IDENTIFIER content.
+ * @param [out] policySz Length of policy in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, policy or policySz is NULL.
+ */
+int wc_TspTstInfo_GetPolicy(const TspTstInfo* tstInfo, const byte** policy,
+ word32* policySz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (policy == NULL) || (policySz == NULL)) {
+ return BAD_FUNC_ARG;
+ }
+
+ *policy = tstInfo->policy;
+ *policySz = tstInfo->policySz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the TSA policy of a TSTInfo.
+ *
+ * The policy is the content of an OBJECT IDENTIFIER - it is referenced, not
+ * copied, and must remain available while the TSTInfo is used.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] policy Policy as OBJECT IDENTIFIER content.
+ * @param [in] policySz Length of policy in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or policy is NULL or policySz is 0.
+ */
+int wc_TspTstInfo_SetPolicy(TspTstInfo* tstInfo, const byte* policy,
+ word32 policySz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (policy == NULL) || (policySz == 0)) {
+ return BAD_FUNC_ARG;
+ }
+
+ tstInfo->policy = policy;
+ tstInfo->policySz = policySz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the message imprint of a TSTInfo.
+ *
+ * The hash is the digest of the time-stamped data. Each output is optional -
+ * pass NULL to not retrieve it. The hash references the TSTInfo.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] hashOID Hash algorithm OID sum. May be NULL.
+ * @param [out] hash Hash of the time-stamped data. May be NULL.
+ * @param [out] hashSz Length of hash in bytes. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo is NULL.
+ */
+int wc_TspTstInfo_GetMsgImprint(const TspTstInfo* tstInfo, word32* hashOID,
+ const byte** hash, word32* hashSz)
+{
+ /* Validate parameter. */
+ if (tstInfo == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ if (hashOID != NULL) {
+ *hashOID = tstInfo->imprint.hashAlgOID;
+ }
+ if (hash != NULL) {
+ *hash = tstInfo->imprint.hash;
+ }
+ if (hashSz != NULL) {
+ *hashSz = tstInfo->imprint.hashSz;
+ }
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the message imprint of a TSTInfo.
+ *
+ * The hash is the digest of the data being time-stamped - it is copied into
+ * the TSTInfo. The hash and algorithm are typically those of the request.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] hashOID Hash algorithm OID sum: SHA256h, etc.
+ * @param [in] hash Hash of the data to time-stamp.
+ * @param [in] hashSz Length of hash in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or hash is NULL or hashSz is 0.
+ * @return BUFFER_E when hashSz is too big for the message imprint.
+ */
+int wc_TspTstInfo_SetMsgImprint(TspTstInfo* tstInfo, word32 hashOID,
+ const byte* hash, word32 hashSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (hash == NULL) || (hashSz == 0)) {
+ return BAD_FUNC_ARG;
+ }
+ if (hashSz > sizeof(tstInfo->imprint.hash)) {
+ return BUFFER_E;
+ }
+
+ tstInfo->imprint.hashAlgOID = hashOID;
+ XMEMCPY(tstInfo->imprint.hash, hash, hashSz);
+ tstInfo->imprint.hashSz = hashSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the time of the time-stamp of a TSTInfo.
+ *
+ * Returns a reference to the genTime as a GeneralizedTime string of RFC 3161:
+ * "YYYYMMDDhhmmss[.s...]Z" - not copied and valid while the TSTInfo references
+ * it.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] genTime Time as a GeneralizedTime string.
+ * @param [out] genTimeSz Length of string in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, genTime or genTimeSz is NULL.
+ */
+int wc_TspTstInfo_GetGenTime(const TspTstInfo* tstInfo, const byte** genTime,
+ word32* genTimeSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (genTime == NULL) || (genTimeSz == NULL)) {
+ return BAD_FUNC_ARG;
+ }
+
+ *genTime = tstInfo->genTime;
+ *genTimeSz = tstInfo->genTimeSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the time of the time-stamp of a TSTInfo.
+ *
+ * The genTime is a GeneralizedTime string of RFC 3161 - it is referenced, not
+ * copied, and must remain available while the TSTInfo is used. The syntax is
+ * checked on encode. Leave unset to use the current time on encode.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] genTime Time as a GeneralizedTime string.
+ * @param [in] genTimeSz Length of string in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or genTime is NULL or genTimeSz is 0.
+ */
+int wc_TspTstInfo_SetGenTime(TspTstInfo* tstInfo, const byte* genTime,
+ word32 genTimeSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (genTime == NULL) || (genTimeSz == 0)) {
+ return BAD_FUNC_ARG;
+ }
+
+ tstInfo->genTime = genTime;
+ tstInfo->genTimeSz = genTimeSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifndef NO_ASN_TIME
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Convert a broken-down UTC time to seconds since the Unix epoch.
+ *
+ * Computed directly rather than with mktime() - mktime() interprets the
+ * fields as local time and may overflow a 32-bit time_t in 2038.
+ *
+ * @param [in] year Year including century. e.g. 2026.
+ * @param [in] mon Month of year. 1-12.
+ * @param [in] day Day of month. 1-31.
+ * @param [in] hour Hour of day. 0-23.
+ * @param [in] min Minute of hour. 0-59.
+ * @param [in] sec Second of minute. 0-60.
+ * @return Seconds since 00:00:00 UTC, 1 January 1970.
+ */
+static time_t TspGenTimeToUnix(int year, int mon, int day, int hour, int min,
+ int sec)
+{
+ /* Cumulative days before each month in a non-leap year. */
+ static const int monthDays[12] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+ };
+ /* Years contributing a leap day - exclude this year before March. */
+ int y = year - ((mon <= 2) ? 1 : 0);
+ int leapDays = y / 4 - y / 100 + y / 400 -
+ (1969 / 4 - 1969 / 100 + 1969 / 400);
+
+ return (time_t)(((((time_t)(year - 1970) * 365 + leapDays +
+ monthDays[mon - 1] + day - 1) * 24 + hour) * 60 + min) * 60 + sec);
+}
+
+/* Get the time of the time-stamp of a TSTInfo as a time_t.
+ *
+ * Parses the genTime GeneralizedTime string of RFC 3161 - any fraction of a
+ * second is ignored. The time is UTC.
+ *
+ * Not available when there is no time support - NO_ASN_TIME.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] t Time of the time-stamp as seconds since the Unix
+ * epoch.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, its genTime or t is NULL.
+ * @return ASN_PARSE_E when the genTime string is not valid.
+ */
+int wc_TspTstInfo_GetGenTimeAsTime(const TspTstInfo* tstInfo, time_t* t)
+{
+ int ret = 0;
+ const byte* g;
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (tstInfo->genTime == NULL) || (t == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ /* The genTime must be a valid GeneralizedTime to convert. */
+ if (ret == 0) {
+ ret = TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz);
+ }
+ if (ret == 0) {
+ /* Date and time digits checked by TspCheckGenTimeSyntax. */
+ g = tstInfo->genTime;
+ *t = TspGenTimeToUnix(
+ (g[0] - '0') * 1000 + (g[1] - '0') * 100 + (g[2] - '0') * 10 +
+ (g[3] - '0'),
+ (g[4] - '0') * 10 + (g[5] - '0'),
+ (g[6] - '0') * 10 + (g[7] - '0'),
+ (g[8] - '0') * 10 + (g[9] - '0'),
+ (g[10] - '0') * 10 + (g[11] - '0'),
+ (g[12] - '0') * 10 + (g[13] - '0'));
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the time of the time-stamp of a TSTInfo from a time_t.
+ *
+ * Formats the time as a GeneralizedTime string of RFC 3161 into the caller's
+ * buffer and references it - the buffer must remain available while the
+ * TSTInfo is used and be at least ASN_GENERALIZED_TIME_SIZE bytes. The time
+ * is treated as UTC.
+ *
+ * Not available when there is no time support - NO_ASN_TIME.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] t Time of the time-stamp as seconds since the Unix
+ * epoch.
+ * @param [out] buf Buffer to hold the formatted GeneralizedTime.
+ * @param [in] bufSz Length of buffer in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or buf is NULL.
+ * @return BUFFER_E when bufSz is too small for the GeneralizedTime string.
+ * @return ASN_TIME_E when the time could not be converted.
+ */
+int wc_TspTstInfo_SetGenTimeAsTime(TspTstInfo* tstInfo, time_t t, byte* buf,
+ word32 bufSz)
+{
+ int ret = 0;
+ int n = 0;
+ struct tm* ts = NULL;
+#ifdef NEED_TMP_TIME
+ struct tm tmpTimeStorage;
+ struct tm* tmpTime = &tmpTimeStorage;
+#else
+ struct tm* tmpTime = NULL;
+#endif
+ /* Needed in case XGMTIME does not use the tmpTime argument. */
+ (void)tmpTime;
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (buf == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* Buffer must hold "YYYYMMDDhhmmssZ" and a NUL from formatting. */
+ else if (bufSz < ASN_GENERALIZED_TIME_SIZE) {
+ ret = BUFFER_E;
+ }
+
+ if (ret == 0) {
+ /* Break the time down as UTC. */
+ ts = (struct tm*)XGMTIME(&t, tmpTime);
+ if ((ts == NULL) || ValidateGmtime(ts)) {
+ ret = ASN_TIME_E;
+ }
+ }
+ if (ret == 0) {
+ /* Format as a GeneralizedTime string of RFC 3161. */
+ n = XSNPRINTF((char*)buf, bufSz, "%04d%02d%02d%02d%02d%02dZ",
+ ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour,
+ ts->tm_min, ts->tm_sec);
+ /* Negative on error; >= bufSz when the time was truncated (e.g. a
+ * year beyond 9999 needs more than the 15 expected characters). */
+ if ((n < 0) || (n >= (int)bufSz)) {
+ ret = ASN_TIME_E;
+ }
+ }
+ if (ret == 0) {
+ tstInfo->genTime = buf;
+ /* Content length excludes the NUL terminator. */
+ tstInfo->genTimeSz = ASN_GENERALIZED_TIME_SIZE - 1;
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+#endif /* !NO_ASN_TIME */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the accuracy of the time of a TSTInfo.
+ *
+ * The accuracy is the seconds, milliseconds and microseconds the genTime may
+ * be off by. Each output is optional - pass NULL to not retrieve it. A value
+ * of 0 means that part of the accuracy is not present.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] seconds Accuracy in seconds. May be NULL.
+ * @param [out] millis Accuracy in milliseconds. May be NULL.
+ * @param [out] micros Accuracy in microseconds. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo is NULL.
+ */
+int wc_TspTstInfo_GetAccuracy(const TspTstInfo* tstInfo, word32* seconds,
+ word16* millis, word16* micros)
+{
+ /* Validate parameter. */
+ if (tstInfo == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ if (seconds != NULL) {
+ *seconds = tstInfo->accuracy.seconds;
+ }
+ if (millis != NULL) {
+ *millis = tstInfo->accuracy.millis;
+ }
+ if (micros != NULL) {
+ *micros = tstInfo->accuracy.micros;
+ }
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the accuracy of the time of a TSTInfo.
+ *
+ * The accuracy is how far the genTime may be off. A value of 0 for a part
+ * means it is not present. Milliseconds and microseconds must be 1..999 -
+ * checked on encode.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] seconds Accuracy in seconds.
+ * @param [in] millis Accuracy in milliseconds.
+ * @param [in] micros Accuracy in microseconds.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo is NULL.
+ */
+int wc_TspTstInfo_SetAccuracy(TspTstInfo* tstInfo, word32 seconds,
+ word16 millis, word16 micros)
+{
+ /* Validate parameter. */
+ if (tstInfo == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ tstInfo->accuracy.seconds = seconds;
+ tstInfo->accuracy.millis = millis;
+ tstInfo->accuracy.micros = micros;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the nonce of a TSTInfo.
+ *
+ * Returns a reference to the nonce - it is not copied and is valid while the
+ * TSTInfo references it. A length of 0 means no nonce is present.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] nonce Nonce as a big-endian number.
+ * @param [out] nonceSz Length of nonce in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, nonce or nonceSz is NULL.
+ */
+int wc_TspTstInfo_GetNonce(const TspTstInfo* tstInfo, const byte** nonce,
+ word32* nonceSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (nonce == NULL) || (nonceSz == NULL)) {
+ return BAD_FUNC_ARG;
+ }
+
+ *nonce = tstInfo->nonce;
+ *nonceSz = tstInfo->nonceSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the nonce of a TSTInfo.
+ *
+ * The nonce is referenced, not copied, and must remain available while the
+ * TSTInfo is used. It must match the request's nonce. Leading zero bytes are
+ * stripped, keeping at least one byte, so it has no leading zero byte and
+ * encodes - the request's decoded nonce is already in this form.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] nonce Nonce as a big-endian number.
+ * @param [in] nonceSz Length of nonce in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or nonce is NULL or nonceSz is 0.
+ */
+int wc_TspTstInfo_SetNonce(TspTstInfo* tstInfo, const byte* nonce,
+ word32 nonceSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (nonce == NULL) || (nonceSz == 0)) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* Strip leading zero bytes - keep at least one byte. */
+ while ((nonceSz > 1) && (nonce[0] == 0x00)) {
+ nonce++;
+ nonceSz--;
+ }
+ tstInfo->nonce = nonce;
+ tstInfo->nonceSz = nonceSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the TSA name of a TSTInfo.
+ *
+ * Returns a reference to the tsa - the DER encoding of a GeneralName - it is
+ * not copied and is valid while the TSTInfo references it. A length of 0
+ * means no TSA name is present.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [out] tsa TSA name as the DER encoding of a GeneralName.
+ * @param [out] tsaSz Length of TSA name in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, tsa or tsaSz is NULL.
+ */
+int wc_TspTstInfo_GetTsa(const TspTstInfo* tstInfo, const byte** tsa,
+ word32* tsaSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (tsa == NULL) || (tsaSz == NULL)) {
+ return BAD_FUNC_ARG;
+ }
+
+ *tsa = tstInfo->tsa;
+ *tsaSz = tstInfo->tsaSz;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the TSA name of a TSTInfo.
+ *
+ * The tsa is the DER encoding of a GeneralName - it is referenced, not
+ * copied, and must remain available while the TSTInfo is used.
+ *
+ * @param [in, out] tstInfo TSTInfo object.
+ * @param [in] tsa TSA name as the DER encoding of a GeneralName.
+ * @param [in] tsaSz Length of TSA name in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or tsa is NULL or tsaSz is 0.
+ */
+int wc_TspTstInfo_SetTsa(TspTstInfo* tstInfo, const byte* tsa, word32 tsaSz)
+{
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (tsa == NULL) || (tsaSz == 0)) {
+ return BAD_FUNC_ARG;
+ }
+
+ tstInfo->tsa = tsa;
+ tstInfo->tsaSz = tsaSz;
+
+ return 0;
+}
+
+/* Set the values of a TSTInfo to respond to a request.
+ *
+ * A convenience for a TSA building a response: echoes the request's message
+ * imprint (copied) and nonce (referenced), and sets the TSA's policy, serial
+ * number and time. The TSTInfo should be initialized with
+ * wc_TspTstInfo_Init() first. The request, policy, serial and genTime buffers
+ * are referenced - not copied (except the imprint) - and must remain available
+ * while the TSTInfo is used.
+ *
+ * @param [in, out] tstInfo TSTInfo object to set.
+ * @param [in] req Decoded request being time-stamped.
+ * @param [in] policy TSA policy as OBJECT IDENTIFIER content.
+ * @param [in] policySz Length of policy in bytes.
+ * @param [in] serial Serial number of the time-stamp - big-endian,
+ * no leading zero byte.
+ * @param [in] serialSz Length of serial in bytes.
+ * @param [in] genTime Time of the time-stamp as a GeneralizedTime
+ * string. NULL to use the current time on encode.
+ * @param [in] genTimeSz Length of genTime in bytes - 0 when NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo, req, policy or serial is NULL or
+ * policySz or serialSz is 0.
+ */
+int wc_TspTstInfo_SetFromRequest(TspTstInfo* tstInfo, const TspRequest* req,
+ const byte* policy, word32 policySz, const byte* serial, word32 serialSz,
+ const byte* genTime, word32 genTimeSz)
+{
+ int ret = 0;
+
+ /* Validate parameters - genTime is optional. */
+ if ((tstInfo == NULL) || (req == NULL) || (policy == NULL) ||
+ (policySz == 0) || (serial == NULL) || (serialSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ if (ret == 0) {
+ tstInfo->policy = policy;
+ tstInfo->policySz = policySz;
+ /* Echo the requester's message imprint - copies the embedded hash. */
+ tstInfo->imprint = req->imprint;
+ tstInfo->serial = serial;
+ tstInfo->serialSz = serialSz;
+ /* NULL genTime uses the current time when encoding. */
+ tstInfo->genTime = genTime;
+ tstInfo->genTimeSz = genTimeSz;
+ /* Echo the nonce when the request has one. */
+ if (req->nonceSz != 0) {
+ tstInfo->nonce = req->nonce;
+ tstInfo->nonceSz = req->nonceSz;
+ }
+ }
+
+ return ret;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES)
+#ifdef WOLFSSL_TSP_VERIFIER
+/* Check the genTime of a TSTInfo is close enough to the current time.
+ *
+ * RFC 3161, 2.4.2: the requester verifies the genTime is within an
+ * acceptable period of the local trusted time. GeneralizedTime strings of
+ * the same form compare as times - the bounds of the acceptable period are
+ * formatted and compared as strings. Any fraction of a second in the
+ * genTime is ignored.
+ *
+ * Not available when there is no real time clock - NO_ASN_TIME, USER_TIME
+ * or TIME_OVERRIDES.
+ *
+ * @param [in] tstInfo Decoded TSTInfo object from response.
+ * @param [in] tolerance Acceptable time around the current time in
+ * seconds.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or its genTime is NULL.
+ * @return ASN_PARSE_E when the genTime string is not valid.
+ * @return ASN_TIME_E when getting the current time failed.
+ * @return TSP_VERIFY_E when the genTime is outside the acceptable period.
+ */
+int wc_TspTstInfo_CheckGenTime(const TspTstInfo* tstInfo, word32 tolerance)
+{
+ int ret = 0;
+ time_t now;
+ time_t bound;
+ byte lo[ASN_GENERALIZED_TIME_SIZE];
+ byte hi[ASN_GENERALIZED_TIME_SIZE];
+
+ WOLFSSL_ENTER("wc_TspTstInfo_CheckGenTime");
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (tstInfo->genTime == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ /* The genTime must be a valid time to compare. */
+ if (ret == 0) {
+ ret = TspCheckGenTimeSyntax(tstInfo->genTime, tstInfo->genTimeSz);
+ }
+ if (ret == 0) {
+ /* Format the bounds of the acceptable period. */
+ now = wc_Time(0);
+ bound = now - (time_t)tolerance;
+ if (GetFormattedTime_ex(&bound, lo, sizeof(lo),
+ ASN_GENERALIZED_TIME) <= 0) {
+ ret = ASN_TIME_E;
+ }
+ bound = now + (time_t)tolerance;
+ if ((ret == 0) && (GetFormattedTime_ex(&bound, hi, sizeof(hi),
+ ASN_GENERALIZED_TIME) <= 0)) {
+ ret = ASN_TIME_E;
+ }
+ }
+ /* Compare the date and time digits - fraction of a second ignored. */
+ if ((ret == 0) && ((XMEMCMP(tstInfo->genTime, lo, 14) < 0) ||
+ (XMEMCMP(tstInfo->genTime, hi, 14) > 0))) {
+ WOLFSSL_MSG("TSP genTime is outside the acceptable period");
+ ret = TSP_VERIFY_E;
+ }
+
+ WOLFSSL_LEAVE("wc_TspTstInfo_CheckGenTime", ret);
+ return ret;
+}
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif /* !NO_ASN_TIME && !USER_TIME && !TIME_OVERRIDES */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+/* Check the TSTInfo of a response against the request sent.
+ *
+ * Checks the version, that the message imprint is the same and, when in the
+ * request, that the nonce and policy are matched. RFC 3161, 2.4.2.
+ *
+ * The genTime and the token's signature are not validated here.
+ *
+ * @param [in] tstInfo Decoded TSTInfo object from response.
+ * @param [in] req TimeStampReq object sent.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or req is NULL.
+ * @return ASN_VERSION_E when the version is not supported.
+ * @return TSP_VERIFY_E when a field of the TSTInfo does not match the
+ * request.
+ */
+int wc_TspTstInfo_CheckRequest(const TspTstInfo* tstInfo, const TspRequest* req)
+{
+ int ret = 0;
+
+ WOLFSSL_ENTER("wc_TspTstInfo_CheckRequest");
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (req == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ /* Only version 1 defined. */
+ if ((ret == 0) && (tstInfo->version != WC_TSP_VERSION)) {
+ ret = ASN_VERSION_E;
+ }
+ /* Message imprint must be the same as the request - same hash
+ * algorithm ... */
+ if ((ret == 0) &&
+ (tstInfo->imprint.hashAlgOID != req->imprint.hashAlgOID)) {
+ ret = TSP_VERIFY_E;
+ }
+ /* ... and same hash of data. */
+ if ((ret == 0) &&
+ ((tstInfo->imprint.hashSz != req->imprint.hashSz) ||
+ (XMEMCMP(tstInfo->imprint.hash, req->imprint.hash,
+ req->imprint.hashSz) != 0))) {
+ ret = TSP_VERIFY_E;
+ }
+ /* Nonce must be returned when in request - compared exactly. */
+ if ((ret == 0) && (req->nonceSz != 0) &&
+ ((tstInfo->nonce == NULL) ||
+ (tstInfo->nonceSz != req->nonceSz) ||
+ (XMEMCMP(tstInfo->nonce, req->nonce, req->nonceSz) != 0))) {
+ ret = TSP_VERIFY_E;
+ }
+ /* Policy must match when requested. */
+ if ((ret == 0) && (req->policySz != 0) &&
+ ((tstInfo->policy == NULL) ||
+ (tstInfo->policySz != req->policySz) ||
+ (XMEMCMP(tstInfo->policy, req->policy, req->policySz) != 0))) {
+ ret = TSP_VERIFY_E;
+ }
+
+ WOLFSSL_LEAVE("wc_TspTstInfo_CheckRequest", ret);
+ return ret;
+}
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+/* Check the TSA name of a TSTInfo is the expected name.
+ *
+ * The TSA name must be present and be the same encoding as the expected
+ * name - the DER encodings of the GeneralNames are compared exactly.
+ *
+ * @param [in] tstInfo Decoded TSTInfo object from response.
+ * @param [in] tsa Expected name: DER encoding of GeneralName.
+ * @param [in] tsaSz Length of expected name in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or tsa is NULL or tsaSz is 0.
+ * @return TSP_VERIFY_E when the TSA name is not present or does not match
+ * the expected name.
+ */
+int wc_TspTstInfo_CheckTsaName(const TspTstInfo* tstInfo, const byte* tsa,
+ word32 tsaSz)
+{
+ int ret = 0;
+
+ WOLFSSL_ENTER("wc_TspTstInfo_CheckTsaName");
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (tsa == NULL) || (tsaSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ /* TSA name must be present and exactly the expected encoding. */
+ if ((ret == 0) && ((tstInfo->tsa == NULL) || (tstInfo->tsaSz != tsaSz) ||
+ (XMEMCMP(tstInfo->tsa, tsa, tsaSz) != 0))) {
+ WOLFSSL_MSG("TSP TSA name is not the expected name");
+ ret = TSP_VERIFY_E;
+ }
+
+ WOLFSSL_LEAVE("wc_TspTstInfo_CheckTsaName", ret);
+ return ret;
+}
+
+/* Verify the message imprint of a TSTInfo against the original data.
+ *
+ * Hashes the data with the TSTInfo's message imprint hash algorithm and
+ * compares the result to the imprint hash - confirming the time-stamp is over
+ * the given data. The caller does not need to hash the data first.
+ *
+ * @param [in] tstInfo TSTInfo object.
+ * @param [in] data Data that was time-stamped.
+ * @param [in] dataSz Length of data in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when tstInfo or data is NULL.
+ * @return HASH_TYPE_E when the imprint's hash algorithm is not supported.
+ * @return TSP_VERIFY_E when the hash of the data does not match the imprint.
+ * @return Other negative value on hashing failure.
+ */
+int wc_TspTstInfo_VerifyData(const TspTstInfo* tstInfo, const byte* data,
+ word32 dataSz)
+{
+ int ret = 0;
+ enum wc_HashType hashType = WC_HASH_TYPE_NONE;
+ int digestSz = 0;
+ byte digest[WC_MAX_DIGEST_SIZE];
+
+ WOLFSSL_ENTER("wc_TspTstInfo_VerifyData");
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (data == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ if (ret == 0) {
+ /* Determine the hash algorithm of the message imprint. */
+ hashType = wc_OidGetHash((int)tstInfo->imprint.hashAlgOID);
+ digestSz = wc_HashGetDigestSize(hashType);
+ if (digestSz <= 0) {
+ ret = HASH_TYPE_E;
+ }
+ }
+ /* The imprint length must match the algorithm's digest size. */
+ if ((ret == 0) && (tstInfo->imprint.hashSz != (word32)digestSz)) {
+ ret = TSP_VERIFY_E;
+ }
+ if (ret == 0) {
+ /* Hash the data and compare to the message imprint. */
+ ret = wc_Hash(hashType, data, dataSz, digest, (word32)digestSz);
+ }
+ if ((ret == 0) && (XMEMCMP(digest, tstInfo->imprint.hash,
+ (word32)digestSz) != 0)) {
+ WOLFSSL_MSG("TSP data does not match the message imprint");
+ ret = TSP_VERIFY_E;
+ }
+
+ WOLFSSL_LEAVE("wc_TspTstInfo_VerifyData", ret);
+ return ret;
+}
+#endif /* WOLFSSL_TSP_VERIFIER */
+
+#ifdef HAVE_PKCS7
+
+/* id-ct-TSTInfo: 1.2.840.113549.1.9.16.1.4. RFC 3161 - the content type of
+ * a time-stamp token. Used by wc_TspTstInfo_SignWithPkcs7() and
+ * wc_TspTstInfo_VerifyWithPKCS7(). */
+static const byte tspTstInfoOid[] = {
+ ASN_OBJECT_ID, 0x0b,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x01, 0x04
+};
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Create a TimeStampToken signed with the TSA's certificate and private key.
+ *
+ * Convenience wrapper around wc_TspTstInfo_SignWithPkcs7() that creates and
+ * disposes of the PKCS7 object. The TSA's certificate is included in the
+ * token.
+ *
+ * @param [in] tstInfo TSTInfo object to encode and sign.
+ * @param [in] cert DER encoded certificate of the TSA.
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [in] key DER encoded private key of the TSA.
+ * @param [in] keySz Length of private key in bytes.
+ * @param [in] keyType Type of the private key - WC_PK_TYPE_RSA or
+ * WC_PK_TYPE_ECDSA_SIGN.
+ * @param [in] hashType Hash algorithm for the signature - e.g.
+ * WC_HASH_TYPE_SHA256.
+ * @param [in] rng Random number generator.
+ * @param [out] out Buffer to hold encoding.
+ * @param [in, out] outSz On in, length of buffer in bytes.
+ * On out, length of encoding in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when a pointer argument is NULL, a length is 0 or the
+ * key type is not supported.
+ * @return HASH_TYPE_E when the hash algorithm is not available.
+ * @return BUFFER_E when the encoding is longer than outSz.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspTstInfo_Sign(const TspTstInfo* tstInfo,
+ const byte* cert, word32 certSz, const byte* key, word32 keySz,
+ enum wc_PkType keyType, enum wc_HashType hashType, WC_RNG* rng,
+ byte* out, word32* outSz)
+{
+ int ret = 0;
+#ifdef WOLFSSL_NO_MALLOC
+ /* No dynamic memory - the PKCS7 object is on the stack. */
+ wc_PKCS7 pkcs7Obj;
+ wc_PKCS7* pkcs7 = &pkcs7Obj;
+#else
+ wc_PKCS7* pkcs7 = NULL;
+#endif
+ int hashOID = 0;
+ int encryptOID = 0;
+
+ WOLFSSL_ENTER("wc_TspTstInfo_Sign");
+
+#ifdef WOLFSSL_NO_MALLOC
+ /* Zero the stack object up front - an early error returns through the
+ * unconditional wc_PKCS7_Free below, which must see isDynamic 0 and all
+ * pointers NULL so it does not free the stack object or wild pointers. */
+ XMEMSET(pkcs7, 0, sizeof(pkcs7Obj));
+#endif
+
+ /* Validate parameters. */
+ if ((tstInfo == NULL) || (cert == NULL) || (certSz == 0) ||
+ (key == NULL) || (keySz == 0) || (rng == NULL) ||
+ (out == NULL) || (outSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ /* Map the key type to the signature algorithm OID. */
+ if (ret == 0) {
+ if (keyType == WC_PK_TYPE_RSA) {
+ encryptOID = RSAk;
+ }
+ #ifdef HAVE_ECC
+ else if (keyType == WC_PK_TYPE_ECDSA_SIGN) {
+ encryptOID = ECDSAk;
+ }
+ #endif
+ else {
+ WOLFSSL_MSG("TSP key type not supported");
+ ret = BAD_FUNC_ARG;
+ }
+ }
+ /* Map the hash type to its OID sum. */
+ if (ret == 0) {
+ hashOID = wc_HashGetOID(hashType);
+ if (hashOID <= 0) {
+ ret = HASH_TYPE_E;
+ }
+ }
+
+ if (ret == 0) {
+#ifdef WOLFSSL_NO_MALLOC
+ ret = wc_PKCS7_Init(pkcs7, NULL, INVALID_DEVID);
+#else
+ pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ if (pkcs7 == NULL) {
+ ret = MEMORY_E;
+ }
+#endif
+ }
+ if (ret == 0) {
+ ret = wc_PKCS7_InitWithCert(pkcs7, (byte*)cert, certSz);
+ }
+ if (ret == 0) {
+ /* Configure the signer and sign the TSTInfo. */
+ pkcs7->rng = rng;
+ pkcs7->hashOID = hashOID;
+ pkcs7->encryptOID = encryptOID;
+ pkcs7->privateKey = (byte*)key;
+ pkcs7->privateKeySz = keySz;
+
+ ret = wc_TspTstInfo_SignWithPkcs7(tstInfo, pkcs7, out, outSz);
+ }
+
+ wc_PKCS7_Free(pkcs7);
+ WOLFSSL_LEAVE("wc_TspTstInfo_Sign", ret);
+ return ret;
+}
+
+/* Maximum size of an encoded SigningCertificateV2: 4 headers of 4 bytes and
+ * a hash algorithm of 15 bytes. */
+#define TSP_MAX_SIGN_CERT_V2_SZ (4 * 4 + 15 + WC_MAX_DIGEST_SIZE)
+
+#ifdef WOLFSSL_NO_MALLOC
+/* Maximum size of an encoded TSTInfo when no dynamic memory. */
+#ifndef WC_TSP_MAX_TSTINFO_SZ
+ #define WC_TSP_MAX_TSTINFO_SZ 512
+#endif
+/* Maximum number of signed attributes, including SigningCertificateV2, when
+ * no dynamic memory. */
+#ifndef WC_TSP_MAX_SIGNED_ATTRIBS
+ #define WC_TSP_MAX_SIGNED_ATTRIBS 4
+#endif
+#endif
+
+/* Create a TimeStampToken - CMS SignedData with TSTInfo content.
+ *
+ * The PKCS7 object must be initialized with the certificate and private key
+ * of the TSA, and the hash algorithm and RNG set. A SigningCertificateV2
+ * signed attribute is added as required by RFC 3161, 2.4.2.
+ *
+ * The TSA's certificate is included in the token. When the request did not
+ * set certReq the certificates must not be included - RFC 3161, 2.4.1 -
+ * set the PKCS7 object's noCerts field.
+ *
+ * @param [in] tstInfo TSTInfo object to encode and sign.
+ * @param [in] pkcs7 PKCS7 object with signer configured.
+ * @param [out] out Buffer to hold encoding.
+ * @param [in, out] outSz On in, length of buffer in bytes.
+ * On out, length of encoding in bytes.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when pkcs7, tstInfo, out or outSz is NULL or the
+ * signer's certificate is not set or, when no dynamic memory, there
+ * are more signed attributes than WC_TSP_MAX_SIGNED_ATTRIBS.
+ * @return BUFFER_E when no dynamic memory and the encoded TSTInfo is longer
+ * than WC_TSP_MAX_TSTINFO_SZ.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspTstInfo_SignWithPkcs7(const TspTstInfo* tstInfo, wc_PKCS7* pkcs7,
+ byte* out, word32* outSz)
+{
+ int ret = 0;
+#ifdef WOLFSSL_NO_MALLOC
+ byte tstDer[WC_TSP_MAX_TSTINFO_SZ];
+ PKCS7Attrib attribs[WC_TSP_MAX_SIGNED_ATTRIBS];
+#else
+ byte* tstDer = NULL;
+ PKCS7Attrib* attribs = NULL;
+#endif
+ word32 tstDerSz = 0;
+ WC_DECLARE_VAR(signCert, byte, TSP_MAX_SIGN_CERT_V2_SZ, pkcs7->heap);
+ word32 signCertSz = TSP_MAX_SIGN_CERT_V2_SZ;
+ word32 cnt = 0;
+
+ WOLFSSL_ENTER("wc_TspTstInfo_SignWithPkcs7");
+
+ /* Validate parameters. */
+ if ((pkcs7 == NULL) || (tstInfo == NULL) || (out == NULL) ||
+ (outSz == NULL)) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* The signer's certificate is hashed into a signed attribute. */
+ if ((ret == 0) && ((pkcs7->singleCert == NULL) ||
+ (pkcs7->singleCertSz == 0))) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ if (ret == 0) {
+ WC_ALLOC_VAR_EX(signCert, byte, TSP_MAX_SIGN_CERT_V2_SZ, pkcs7->heap,
+ DYNAMIC_TYPE_TMP_BUFFER, ret = MEMORY_E);
+ }
+
+ /* Encode TSTInfo as the content. */
+#ifdef WOLFSSL_NO_MALLOC
+ if (ret == 0) {
+ /* Fixed size buffer on the stack. */
+ tstDerSz = (word32)sizeof(tstDer);
+ }
+#else
+ if (ret == 0) {
+ /* Get the length of the encoding to allocate. */
+ ret = wc_TspTstInfo_Encode(tstInfo, NULL, &tstDerSz);
+ }
+ if (ret == 0) {
+ tstDer = (byte*)XMALLOC(tstDerSz, pkcs7->heap,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ if (tstDer == NULL) {
+ ret = MEMORY_E;
+ }
+ }
+#endif
+ if (ret == 0) {
+ ret = wc_TspTstInfo_Encode(tstInfo, tstDer, &tstDerSz);
+ }
+ /* Hash of signer's certificate in signed attribute. */
+ if (ret == 0) {
+ ret = TspEncodeSigningCertV2(pkcs7->hashOID, pkcs7->singleCert,
+ pkcs7->singleCertSz, signCert, &signCertSz, pkcs7->heap);
+ }
+ /* Add SigningCertificateV2 to user's signed attributes. */
+ if (ret == 0) {
+ cnt = pkcs7->signedAttribsSz;
+#ifdef WOLFSSL_NO_MALLOC
+ /* Check fixed size array is big enough for one more. */
+ if (cnt + 1 > WC_TSP_MAX_SIGNED_ATTRIBS) {
+ ret = BAD_FUNC_ARG;
+ }
+#else
+ /* Allocate array for user's attributes and one more. */
+ attribs = (PKCS7Attrib*)XMALLOC((cnt + 1) * sizeof(PKCS7Attrib),
+ pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
+ if (attribs == NULL) {
+ ret = MEMORY_E;
+ }
+#endif
+ }
+ if (ret == 0) {
+ /* Copy in user's attributes and append SigningCertificateV2. */
+ if (cnt > 0) {
+ XMEMCPY(attribs, pkcs7->signedAttribs,
+ cnt * sizeof(PKCS7Attrib));
+ }
+ attribs[cnt].oid = tspSigningCertV2Oid;
+ attribs[cnt].oidSz = (word32)sizeof(tspSigningCertV2Oid);
+ attribs[cnt].value = signCert;
+ attribs[cnt].valueSz = signCertSz;
+ }
+ if (ret == 0) {
+ /* Sign TSTInfo keeping original PKCS7 object fields. */
+ byte* content = pkcs7->content;
+ word32 contentSz = pkcs7->contentSz;
+ int contentOID = pkcs7->contentOID;
+ byte contentType[MAX_OID_SZ];
+ word32 contentTypeSz = pkcs7->contentTypeSz;
+ PKCS7Attrib* signedAttribs = pkcs7->signedAttribs;
+ word32 signedAttribsSz = pkcs7->signedAttribsSz;
+
+ XMEMCPY(contentType, pkcs7->contentType, MAX_OID_SZ);
+
+ /* TSTInfo encoding is the content to be signed. */
+ pkcs7->content = tstDer;
+ pkcs7->contentSz = tstDerSz;
+ pkcs7->contentOID = TSTINFO_DATA;
+ pkcs7->signedAttribs = attribs;
+ pkcs7->signedAttribsSz = cnt + 1;
+
+ /* Content type written and put in signed attributes. */
+ ret = wc_PKCS7_SetContentType(pkcs7, (byte*)tspTstInfoOid,
+ (word32)sizeof(tspTstInfoOid));
+ if (ret == 0) {
+ /* Encode CMS SignedData - returns length of encoding. */
+ ret = wc_PKCS7_EncodeSignedData(pkcs7, out, *outSz);
+ }
+
+ /* Restore caller's PKCS7 object fields. */
+ pkcs7->content = content;
+ pkcs7->contentSz = contentSz;
+ pkcs7->contentOID = contentOID;
+ pkcs7->signedAttribs = signedAttribs;
+ pkcs7->signedAttribsSz = signedAttribsSz;
+ XMEMCPY(pkcs7->contentType, contentType, MAX_OID_SZ);
+ pkcs7->contentTypeSz = contentTypeSz;
+
+ if (ret > 0) {
+ /* Return the length of the encoding. */
+ *outSz = (word32)ret;
+ ret = 0;
+ }
+ else if (ret == 0) {
+ /* Zero length encoding is not valid. */
+ ret = BAD_STATE_E;
+ }
+ }
+
+#ifndef WOLFSSL_NO_MALLOC
+ XFREE(attribs, (pkcs7 != NULL) ? pkcs7->heap : NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ XFREE(tstDer, (pkcs7 != NULL) ? pkcs7->heap : NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+ WC_FREE_VAR_EX(signCert, (pkcs7 != NULL) ? pkcs7->heap : NULL,
+ DYNAMIC_TYPE_TMP_BUFFER);
+ WOLFSSL_LEAVE("wc_TspTstInfo_SignWithPkcs7", ret);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifdef WOLFSSL_TSP_VERIFIER
+
+/* Check a hash algorithm meets the minimum security strength.
+ *
+ * The collision resistance of a hash is half the digest length in bits -
+ * digest size in bytes * 4. With WC_TSP_MIN_HASH_STRENGTH_BITS of 0 any
+ * available hash algorithm is acceptable.
+ *
+ * This is a strength check, not an availability check: with the default
+ * WC_TSP_MIN_HASH_STRENGTH_BITS of 0 it returns 0 for an unavailable OID.
+ * Callers that go on to use the algorithm independently reject an unavailable
+ * hash via wc_HashGetDigestSize() - see TspCheckSigningCertAttr() and
+ * wc_TspTstInfo_VerifyData().
+ *
+ * Not static - also used by TspCheckSigningCertAttr() in asn_tsp.c (declared
+ * in tsp.h).
+ *
+ * @param [in] hashOID Hash algorithm OID sum.
+ * @return 0 when strong enough.
+ * @return HASH_TYPE_E when not available or below
+ * WC_TSP_MIN_HASH_STRENGTH_BITS.
+ */
+int Tsp_CheckHashStrength(word32 hashOID)
+{
+ int ret = 0;
+#if WC_TSP_MIN_HASH_STRENGTH_BITS > 0
+ int digestSz = wc_HashGetDigestSize(wc_OidGetHash((int)hashOID));
+
+ if ((digestSz <= 0) ||
+ ((digestSz * 4) < WC_TSP_MIN_HASH_STRENGTH_BITS)) {
+ WOLFSSL_MSG("TSP hash algorithm below minimum security strength");
+ ret = HASH_TYPE_E;
+ }
+#else
+ (void)hashOID;
+#endif
+ return ret;
+}
+
+/* Check the TSA name corresponds to a subject name of the signer's
+ * certificate.
+ *
+ * RFC 3161, 2.4.2: the tsa field, when present, must correspond to one of
+ * the subject names included in the certificate that is to be used to
+ * verify the token.
+ *
+ * A directoryName is checked against the subject name and other supported
+ * GeneralName forms against the subject alternative names.
+ *
+ * @param [in] dCert Decoded certificate of signer.
+ * @param [in] tsa DER encoding of GeneralName.
+ * @param [in] tsaSz Length of GeneralName in bytes.
+ * @return 0 on success.
+ * @return TSP_VERIFY_E when the name does not match the certificate or the
+ * form of name is not supported.
+ * @return ASN_PARSE_E when the encoding is invalid.
+ */
+static int Tsp_CheckTsaName(DecodedCert* dCert, const byte* tsa, word32 tsaSz)
+{
+ int ret = 0;
+ word32 idx = 0;
+ byte tag = 0;
+ int len = 0;
+
+ /* Get header of the one GeneralName. */
+ if ((GetASNTag(tsa, &idx, &tag, tsaSz) < 0) ||
+ (GetLength(tsa, &idx, &len, tsaSz) < 0) ||
+ (idx + (word32)len != tsaSz)) {
+ ret = ASN_PARSE_E;
+ }
+ /* directoryName [4] - explicitly tagged Name. */
+ else if (tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED |
+ ASN_DIR_TYPE)) {
+ #if !defined(IGNORE_NAME_CONSTRAINTS) || defined(WOLFSSL_CERT_EXT)
+ byte nameTag = 0;
+ int nameLen = 0;
+
+ /* Step into the Name to compare contents of SEQUENCE. */
+ if ((GetASNTag(tsa, &idx, &nameTag, tsaSz) < 0) ||
+ (nameTag != (ASN_SEQUENCE | ASN_CONSTRUCTED)) ||
+ (GetLength(tsa, &idx, &nameLen, tsaSz) < 0) ||
+ (idx + (word32)nameLen != tsaSz)) {
+ ret = ASN_PARSE_E;
+ }
+ /* Compare with the subject name of the signer's certificate. */
+ else if ((dCert->subjectRaw == NULL) ||
+ (nameLen != dCert->subjectRawLen) ||
+ (XMEMCMP(tsa + idx, dCert->subjectRaw,
+ (size_t)nameLen) != 0)) {
+ WOLFSSL_MSG("TSP TSA name doesn't match signer's subject");
+ ret = TSP_VERIFY_E;
+ }
+ #else
+ /* No raw subject name to compare against. */
+ WOLFSSL_MSG("TSP TSA name check requires raw subject name");
+ ret = TSP_VERIFY_E;
+ #endif
+ }
+ /* Name forms of the subject alternative names extension. */
+ else if ((tag == (ASN_CONTEXT_SPECIFIC | ASN_RFC822_TYPE)) ||
+ (tag == (ASN_CONTEXT_SPECIFIC | ASN_DNS_TYPE)) ||
+ (tag == (ASN_CONTEXT_SPECIFIC | ASN_URI_TYPE))) {
+ const DNS_entry* entry;
+ int type = (int)(tag & ~ASN_CONTEXT_SPECIFIC);
+
+ /* Compare against each subject alternative name of the form. */
+ ret = TSP_VERIFY_E;
+ for (entry = dCert->altNames; entry != NULL; entry = entry->next) {
+ if ((entry->type == type) && (entry->len == len) &&
+ (XMEMCMP(entry->name, tsa + idx, (size_t)len) == 0)) {
+ ret = 0;
+ break;
+ }
+ }
+ if (ret != 0) {
+ WOLFSSL_MSG("TSP TSA name not in signer's alternative names");
+ }
+ }
+ else {
+ /* Other forms of GeneralName are not supported. */
+ WOLFSSL_MSG("TSP TSA name form not supported");
+ ret = TSP_VERIFY_E;
+ }
+
+ return ret;
+}
+
+/* Check the signer's certificate is valid for time-stamping.
+ *
+ * RFC 3161, 2.3: the TSA's certificate must have an extended key usage of
+ * id-kp-timeStamping only and the extension must be critical. The key
+ * usage, when present, must only be for signing.
+ *
+ * The TSA name of the TSTInfo, when present, must correspond to a subject
+ * name of the certificate. RFC 3161, 2.4.2.
+ *
+ * @param [in] cert DER encoded certificate of signer.
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [in] tsa DER encoding of GeneralName from TSTInfo. May be NULL.
+ * @param [in] tsaSz Length of GeneralName in bytes.
+ * @param [in] heap Dynamic memory allocation hint.
+ * @return 0 on success.
+ * @return EXTKEYUSAGE_E when the extended key usage is not critical or not
+ * time-stamping only.
+ * @return KEYUSAGE_E when the key usage is not for signing only.
+ * @return TSP_VERIFY_E when the TSA name does not match the certificate.
+ * @return ASN_PARSE_E when an encoding is invalid.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+static int Tsp_CheckSignerCert(const byte* cert, word32 certSz,
+ const byte* tsa, word32 tsaSz, void* heap)
+{
+ int ret = 0;
+ WC_DECLARE_VAR(dCert, DecodedCert, 1, heap);
+
+ WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, heap, DYNAMIC_TYPE_DCERT,
+ return MEMORY_E);
+
+ /* Parse certificate for extensions and names - no chain verify. */
+ InitDecodedCert(dCert, cert, certSz, heap);
+ ret = ParseCertRelative(dCert, CERT_TYPE, NO_VERIFY, NULL, NULL);
+ if (ret == 0) {
+ /* Extended key usage must be critical and time-stamping only - the
+ * OID count catches an extra purpose whose OID is not recognized and
+ * so leaves no extra bit set in extExtKeyUsage. */
+ if ((!dCert->extExtKeyUsageSet) ||
+ (dCert->extExtKeyUsage != EXTKEYUSE_TIMESTAMP) ||
+ (dCert->extExtKeyUsageOidCnt != 1) ||
+ (!dCert->extExtKeyUsageCrit)) {
+ WOLFSSL_MSG("TSP signer's cert not for time-stamping only");
+ ret = EXTKEYUSAGE_E;
+ }
+ }
+ if (ret == 0) {
+ /* Key usage, when present, must be for signing only. */
+ if (dCert->extKeyUsageSet &&
+ (((dCert->extKeyUsage & (word16)~(KEYUSE_DIGITAL_SIG |
+ KEYUSE_CONTENT_COMMIT)) != 0) ||
+ ((dCert->extKeyUsage & (KEYUSE_DIGITAL_SIG |
+ KEYUSE_CONTENT_COMMIT)) == 0))) {
+ WOLFSSL_MSG("TSP signer's cert key usage not signing only");
+ ret = KEYUSAGE_E;
+ }
+ }
+ /* TSA name, when present, must correspond to the certificate. */
+ if ((ret == 0) && (tsa != NULL)) {
+ ret = Tsp_CheckTsaName(dCert, tsa, tsaSz);
+ }
+ FreeDecodedCert(dCert);
+
+ WC_FREE_VAR_EX(dCert, heap, DYNAMIC_TYPE_DCERT);
+ return ret;
+}
+
+/* Verify a TimeStampToken and decode the TSTInfo content.
+ *
+ * The PKCS7 object must be initialized. The signature of the CMS SignedData
+ * is verified with the certificates in the token. When the token does not
+ * include certificates - certReq was not set in the request - initialize
+ * the PKCS7 object with the TSA's certificate. Trust in the TSA's
+ * certificate must be established by the caller.
+ *
+ * The signer's certificate must be valid for time-stamping only -
+ * RFC 3161, 2.3 - and be the certificate identified by the signing
+ * certificate attribute - RFC 3161, 2.4.2. Only the certHash of the first
+ * ESSCertID(v2) of the attribute is checked - any issuerSerial and further
+ * certificate identifiers are not used. The TSA name of the TSTInfo, when
+ * present, must correspond to a subject name of the signer's certificate -
+ * RFC 3161, 2.4.2.
+ *
+ * Pointers in tstInfo reference the content of the PKCS7 object - the
+ * PKCS7 object and the token buffer must remain available while tstInfo is
+ * in use.
+ *
+ * @param [in] pkcs7 Initialized PKCS7 object.
+ * @param [in, out] token Buffer holding DER encoding of token.
+ * @param [in] tokenSz Length of data in buffer in bytes.
+ * @param [out] tstInfo TSTInfo object to fill. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when pkcs7 or token is NULL or tokenSz is 0.
+ * @return PKCS7_OID_E when the content is not a TSTInfo.
+ * @return EXTKEYUSAGE_E when the signer's extended key usage is not
+ * critical or not time-stamping only.
+ * @return KEYUSAGE_E when the signer's key usage is not for signing only.
+ * @return TSP_VERIFY_E when the token does not have exactly one
+ * SignerInfo, no signing certificate attribute is found or it does
+ * not match the signer's certificate or the TSA name does not
+ * match the signer's certificate.
+ * @return HASH_TYPE_E when the signing certificate attribute's hash
+ * algorithm is not available or a hash algorithm is below
+ * WC_TSP_MIN_HASH_STRENGTH_BITS.
+ * @return ASN_PARSE_E when an encoding is invalid.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspTstInfo_VerifyWithPKCS7(wc_PKCS7* pkcs7, byte* token, word32 tokenSz,
+ TspTstInfo* tstInfo)
+{
+ int ret = 0;
+ TspTstInfo tstDec;
+
+ WOLFSSL_ENTER("wc_TspTstInfo_VerifyWithPKCS7");
+
+ /* Validate parameters. */
+ if ((pkcs7 == NULL) || (token == NULL) || (tokenSz == 0)) {
+ ret = BAD_FUNC_ARG;
+ }
+
+ if (ret == 0) {
+ /* Token must have a single SignerInfo. */
+ ret = TspCheckOneSignerInfo(token, tokenSz, pkcs7->heap);
+ }
+ if (ret == 0) {
+ /* Verify signature of CMS SignedData. */
+ ret = wc_PKCS7_VerifySignedData(pkcs7, token, tokenSz);
+ }
+ /* Content type must be id-ct-TSTInfo. */
+ if ((ret == 0) && ((pkcs7->contentTypeSz != sizeof(tspTstInfoOid)) ||
+ (XMEMCMP(pkcs7->contentType, tspTstInfoOid,
+ sizeof(tspTstInfoOid)) != 0))) {
+ ret = PKCS7_OID_E;
+ }
+ /* The digest algorithm of the signature must meet the minimum
+ * strength. */
+ if (ret == 0) {
+ ret = Tsp_CheckHashStrength((word32)pkcs7->hashOID);
+ }
+ if (ret == 0) {
+ /* Decode the content as a TSTInfo - TSA name needed for checks. */
+ ret = wc_TspTstInfo_Decode(&tstDec, pkcs7->content, pkcs7->contentSz);
+ }
+ /* The hash algorithm of the imprint must meet the minimum strength. */
+ if (ret == 0) {
+ ret = Tsp_CheckHashStrength(tstDec.imprint.hashAlgOID);
+ }
+ /* Check the signer's certificate is valid for time-stamping. */
+ if (ret == 0) {
+ if (pkcs7->verifyCert == NULL) {
+ /* No certificate to check - must be in token. */
+ ret = TSP_VERIFY_E;
+ }
+ else {
+ ret = Tsp_CheckSignerCert(pkcs7->verifyCert, pkcs7->verifyCertSz,
+ tstDec.tsa, tstDec.tsaSz, pkcs7->heap);
+ }
+ }
+ /* Check the signing certificate attribute matches the signer. */
+ if (ret == 0) {
+ ret = TspCheckSigningCertAttr(pkcs7);
+ }
+ /* Return the decoded TSTInfo when requested. */
+ if ((ret == 0) && (tstInfo != NULL)) {
+ *tstInfo = tstDec;
+ }
+
+ WOLFSSL_LEAVE("wc_TspTstInfo_VerifyWithPKCS7", ret);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif /* HAVE_PKCS7 */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Initialize a TimeStampResp.
+ *
+ * @param [out] resp TimeStampResp object.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp is NULL.
+ */
+int wc_TspResponse_Init(TspResponse* resp)
+{
+ /* Validate parameter. */
+ if (resp == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* All fields empty - status of 0 is granted. */
+ XMEMSET(resp, 0, sizeof(TspResponse));
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+/* Get the status information of a TimeStampResp.
+ *
+ * Each output is optional - pass NULL to not retrieve a value. The status
+ * string references into the response and is valid while the response is.
+ *
+ * @param [in] resp TimeStampResp object.
+ * @param [out] status PKIStatus value. See TspPkiStatus. May be NULL.
+ * @param [out] str Status string - UTF-8, no NUL terminator. NULL when
+ * no status string present. May be NULL.
+ * @param [out] strSz Length of status string in bytes. May be NULL.
+ * @param [out] failInfo Failure information: WC_TSP_FAIL_* flags, 0 when not
+ * present. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp is NULL.
+ */
+int wc_TspResponse_GetStatus(const TspResponse* resp, word32* status,
+ const byte** str, word32* strSz, word32* failInfo)
+{
+ /* Validate parameter. */
+ if (resp == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* Return each value the caller asked for. */
+ if (status != NULL) {
+ *status = resp->status;
+ }
+ if (str != NULL) {
+ *str = resp->statusString;
+ }
+ if (strSz != NULL) {
+ *strSz = resp->statusStringSz;
+ }
+ if (failInfo != NULL) {
+ *failInfo = resp->failInfo;
+ }
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Set the status information of a TimeStampResp.
+ *
+ * The status string is assigned - it is not copied and must remain available
+ * while the response is used. Pass NULL to have no status string.
+ *
+ * @param [in, out] resp TimeStampResp object.
+ * @param [in] status PKIStatus value. See TspPkiStatus.
+ * @param [in] str Status string - UTF-8, no NUL terminator. May be
+ * NULL.
+ * @param [in] strSz Length of status string in bytes.
+ * @param [in] failInfo Failure information: WC_TSP_FAIL_* flags, 0 when
+ * none.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp is NULL.
+ */
+int wc_TspResponse_SetStatus(TspResponse* resp, word32 status, const byte* str,
+ word32 strSz, word32 failInfo)
+{
+ /* Validate parameter. */
+ if (resp == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ resp->status = (byte)status;
+ /* The status string is assigned, not copied. */
+ resp->statusString = str;
+ resp->statusStringSz = (str != NULL) ? strSz : 0;
+ resp->failInfo = failInfo;
+
+ return 0;
+}
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+/* Get a human-readable string for a PKIStatus value.
+ *
+ * @param [in] status PKIStatus value. See TspPkiStatus.
+ * @return Description of the status - a constant string, not to be freed.
+ */
+const char* wc_TspStatus_ToString(word32 status)
+{
+ switch (status) {
+ case WC_TSP_PKISTATUS_GRANTED:
+ return "granted";
+ case WC_TSP_PKISTATUS_GRANTED_WITH_MODS:
+ return "granted with modifications";
+ case WC_TSP_PKISTATUS_REJECTION:
+ return "rejection";
+ case WC_TSP_PKISTATUS_WAITING:
+ return "waiting";
+ case WC_TSP_PKISTATUS_REVOCATION_WARNING:
+ return "revocation warning";
+ case WC_TSP_PKISTATUS_REVOCATION_NOTIFICATION:
+ return "revocation notification";
+ default:
+ return "unknown status";
+ }
+}
+
+/* Get a human-readable string for a PKIFailureInfo flag.
+ *
+ * Expects a single WC_TSP_FAIL_* flag - the failure information of a response
+ * has at most one.
+ *
+ * @param [in] failInfo Failure information: a WC_TSP_FAIL_* flag.
+ * @return Description of the failure - a constant string, not to be freed.
+ */
+const char* wc_TspFailInfo_ToString(word32 failInfo)
+{
+ switch (failInfo) {
+ case WC_TSP_FAIL_BAD_ALG:
+ return "unrecognized or unsupported algorithm";
+ case WC_TSP_FAIL_BAD_REQUEST:
+ return "transaction not permitted or supported";
+ case WC_TSP_FAIL_BAD_DATA_FORMAT:
+ return "data submitted has the wrong format";
+ case WC_TSP_FAIL_TIME_NOT_AVAILABLE:
+ return "the TSA's time source is not available";
+ case WC_TSP_FAIL_UNACCEPTED_POLICY:
+ return "the requested TSA policy is not supported";
+ case WC_TSP_FAIL_UNACCEPTED_EXTENSION:
+ return "the requested extension is not supported";
+ case WC_TSP_FAIL_ADD_INFO_NOT_AVAILABLE:
+ return "the additional information is not available";
+ case WC_TSP_FAIL_SYSTEM_FAILURE:
+ return "the request cannot be handled due to system failure";
+ default:
+ return "unknown failure information";
+ }
+}
+
+#ifdef HAVE_PKCS7
+#ifdef WOLFSSL_TSP_VERIFIER
+/* Verify a signer's certificate chains to a trusted CA in a manager.
+ *
+ * @param [in] cert DER encoded signer certificate.
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [in] cm WOLFSSL_CERT_MANAGER with the trusted CAs - a void
+ * pointer to avoid an SSL layer dependency in wolfCrypt.
+ * @param [in] heap Dynamic memory allocation hint.
+ * @return 0 when the certificate chains to a trusted CA.
+ * @return TSP_VERIFY_E when it does not.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+static int Tsp_VerifyCertChain(const byte* cert, word32 certSz, void* cm,
+ void* heap)
+{
+ int ret;
+ WC_DECLARE_VAR(dCert, DecodedCert, 1, heap);
+
+ WC_ALLOC_VAR_EX(dCert, DecodedCert, 1, heap, DYNAMIC_TYPE_DCERT,
+ return MEMORY_E);
+
+ /* Parse and verify the certificate chains to a trusted CA in the
+ * manager. The manager must hold the trust anchor and any intermediate
+ * CAs needed - certificates carried in the token are not trust anchors. */
+ InitDecodedCert(dCert, cert, certSz, heap);
+ ret = ParseCertRelative(dCert, CERT_TYPE, VERIFY, cm, NULL);
+ FreeDecodedCert(dCert);
+
+ WC_FREE_VAR_EX(dCert, heap, DYNAMIC_TYPE_DCERT);
+
+ if (ret != 0) {
+ WOLFSSL_MSG("TSP signer's certificate is not trusted by the manager");
+ ret = TSP_VERIFY_E;
+ }
+ return ret;
+}
+
+/* Verify the time-stamp token of a TimeStampResp, establishing trust in the
+ * signer by either pinning a certificate or chaining to a certificate
+ * manager. Used by wc_TspResponse_Verify() and wc_TspResponse_VerifyWithCm().
+ *
+ * @param [in] resp TimeStampResp object with a token to verify.
+ * @param [in] cert DER encoded trusted TSA certificate to pin, or NULL.
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [in] cm Certificate manager to chain against, or NULL.
+ * @param [out] tstInfo TSTInfo object to fill. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp is NULL.
+ * @return TSP_VERIFY_E on a verification failure.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+static int TspResponse_Verify(TspResponse* resp, const byte* cert,
+ word32 certSz, void* cm, TspTstInfo* tstInfo)
+{
+ int ret = 0;
+#ifdef WOLFSSL_NO_MALLOC
+ /* No dynamic memory - the PKCS7 object is on the stack. */
+ wc_PKCS7 pkcs7Obj;
+ wc_PKCS7* pkcs7 = &pkcs7Obj;
+#else
+ wc_PKCS7* pkcs7 = NULL;
+#endif
+
+#ifdef WOLFSSL_NO_MALLOC
+ /* Zero the stack object up front - an early error returns through the
+ * unconditional wc_PKCS7_Free below, which must see isDynamic 0 and all
+ * pointers NULL so it does not free the stack object or wild pointers. */
+ XMEMSET(pkcs7, 0, sizeof(pkcs7Obj));
+#endif
+
+ /* Validate parameter. */
+ if (resp == NULL) {
+ ret = BAD_FUNC_ARG;
+ }
+ /* The time-stamp must have been granted. */
+ if ((ret == 0) && (resp->status != WC_TSP_PKISTATUS_GRANTED) &&
+ (resp->status != WC_TSP_PKISTATUS_GRANTED_WITH_MODS)) {
+ WOLFSSL_MSG("TSP response status is not granted");
+ ret = TSP_VERIFY_E;
+ }
+ /* A granted response has a token to verify. */
+ if ((ret == 0) && ((resp->token == NULL) || (resp->tokenSz == 0))) {
+ WOLFSSL_MSG("TSP response has no time-stamp token");
+ ret = TSP_VERIFY_E;
+ }
+
+ if (ret == 0) {
+#ifdef WOLFSSL_NO_MALLOC
+ ret = wc_PKCS7_Init(pkcs7, NULL, INVALID_DEVID);
+#else
+ pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID);
+ if (pkcs7 == NULL) {
+ ret = MEMORY_E;
+ }
+#endif
+ }
+ if (ret == 0) {
+ /* Initialize with the TSA's certificate - NULL when in the token. */
+ ret = wc_PKCS7_InitWithCert(pkcs7, (byte*)cert, certSz);
+ }
+ if (ret == 0) {
+ /* The token references the response - not modified by verify. */
+ ret = wc_TspTstInfo_VerifyWithPKCS7(pkcs7, (byte*)resp->token,
+ resp->tokenSz, tstInfo);
+ }
+ /* Establish trust in the signer. The token's signature was verified
+ * against the certificate in the token; trust pins that certificate to a
+ * known one or chains it to a trusted CA in the manager. */
+ if ((ret == 0) && (cm != NULL)) {
+ /* Chain the signer's certificate to a trusted CA. */
+ ret = Tsp_VerifyCertChain(pkcs7->verifyCert, pkcs7->verifyCertSz,
+ cm, pkcs7->heap);
+ }
+ else if ((ret == 0) && (cert != NULL) &&
+ ((pkcs7->verifyCertSz != certSz) ||
+ (pkcs7->verifyCert == NULL) ||
+ (XMEMCMP(pkcs7->verifyCert, cert, certSz) != 0))) {
+ /* Pin: the signer must be the given trusted certificate. */
+ WOLFSSL_MSG("TSP signer is not the trusted TSA");
+ ret = TSP_VERIFY_E;
+ }
+ /* tstInfo references pkcs7->content, which wc_PKCS7_Free releases - the
+ * verify may copy the eContent into PKCS7-owned memory. Re-point the
+ * references into the caller's token, which holds the same TSTInfo DER,
+ * so tstInfo stays valid after this function returns. */
+ if ((ret == 0) && (tstInfo != NULL) && (pkcs7->contentSz > 0)) {
+ const byte* c = pkcs7->content;
+ word32 off;
+ word32 matchOff = 0;
+ int matches = 0;
+
+ /* The content must appear exactly once in the response token so the
+ * references can be rebased unambiguously. Zero matches means the
+ * content is not contiguous in the token (e.g. a constructed OCTET
+ * STRING); more than one means a duplicated byte sequence that could
+ * rebase the references to the wrong - though in-bounds - location.
+ * Both are rejected rather than hand back possibly-wrong references. */
+ for (off = 0; off + pkcs7->contentSz <= resp->tokenSz; off++) {
+ if (XMEMCMP(resp->token + off, c, pkcs7->contentSz) == 0) {
+ matchOff = off;
+ if (++matches > 1) {
+ break;
+ }
+ }
+ }
+ if (matches != 1) {
+ WOLFSSL_MSG("TSP token content not found uniquely in response");
+ ret = TSP_VERIFY_E;
+ }
+ else {
+ const byte* tok = resp->token + matchOff;
+ if (tstInfo->policy != NULL)
+ tstInfo->policy = tok + (tstInfo->policy - c);
+ if (tstInfo->serial != NULL)
+ tstInfo->serial = tok + (tstInfo->serial - c);
+ if (tstInfo->genTime != NULL)
+ tstInfo->genTime = tok + (tstInfo->genTime - c);
+ if (tstInfo->nonce != NULL)
+ tstInfo->nonce = tok + (tstInfo->nonce - c);
+ if (tstInfo->tsa != NULL)
+ tstInfo->tsa = tok + (tstInfo->tsa - c);
+ }
+ }
+
+ /* On any error tstInfo may hold references into pkcs7->content, which
+ * wc_PKCS7_Free is about to release - clear them so a caller that ignores
+ * the return value is not handed dangling pointers. */
+ if ((ret != 0) && (tstInfo != NULL)) {
+ XMEMSET(tstInfo, 0, sizeof(*tstInfo));
+ }
+
+ wc_PKCS7_Free(pkcs7);
+ return ret;
+}
+
+/* Verify the time-stamp token of a TimeStampResp with the TSA's certificate.
+ *
+ * Convenience wrapper around wc_TspTstInfo_VerifyWithPKCS7() that creates and
+ * disposes of the PKCS7 object. The time-stamp must have been granted and the
+ * response must have a token. The token's signature is verified and the
+ * signer's certificate checked - see wc_TspTstInfo_VerifyWithPKCS7().
+ *
+ * When a certificate is given it is the trusted TSA - the signer of the token
+ * must be that certificate. This establishes trust by pinning the TSA's
+ * certificate. The certificate is also used to verify the signature when the
+ * token does not include the signer's certificate - certReq was not set in
+ * the request. When the certificate is NULL the token must include the
+ * signer's certificate and no trust is established - the caller must trust
+ * the signer by other means.
+ *
+ * Pointers in tstInfo reference the token of the response - the response and
+ * its token buffer must remain available while tstInfo is in use.
+ *
+ * @param [in] resp TimeStampResp object with a token to verify.
+ * @param [in] cert DER encoded certificate of the trusted TSA. May be
+ * NULL when the token includes the signer's certificate
+ * and trust is established by other means.
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [out] tstInfo TSTInfo object to fill. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp is NULL.
+ * @return TSP_VERIFY_E when the response was not granted, has no token, the
+ * token does not verify - see wc_TspTstInfo_VerifyWithPKCS7() - or the
+ * signer is not the trusted TSA certificate.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspResponse_Verify(TspResponse* resp, const byte* cert, word32 certSz,
+ TspTstInfo* tstInfo)
+{
+ int ret;
+
+ WOLFSSL_ENTER("wc_TspResponse_Verify");
+
+ /* Pin the signer to the given certificate - no certificate manager. */
+ ret = TspResponse_Verify(resp, cert, certSz, NULL, tstInfo);
+
+ WOLFSSL_LEAVE("wc_TspResponse_Verify", ret);
+ return ret;
+}
+
+/* Verify the time-stamp token of a TimeStampResp, trusting the signer via a
+ * certificate manager.
+ *
+ * Convenience wrapper around wc_TspTstInfo_VerifyWithPKCS7() that creates and
+ * disposes of the PKCS7 object. The time-stamp must have been granted and the
+ * response must have a token. The token's signature is verified and the
+ * signer's certificate checked - see wc_TspTstInfo_VerifyWithPKCS7() - then
+ * the signer's certificate is verified to chain to a trusted CA in the
+ * manager.
+ *
+ * The token must include the signer's certificate - the certificate manager
+ * must hold the trust anchor and any intermediate CAs needed to build the
+ * chain. Certificates carried in the token are used to verify the token's
+ * signature but are not trusted as CAs - load intermediate CAs into the
+ * manager to support a signer issued by an intermediate.
+ *
+ * Pointers in tstInfo reference the token of the response - the response and
+ * its token buffer must remain available while tstInfo is in use.
+ *
+ * @param [in] resp TimeStampResp object with a token to verify.
+ * @param [in] cm WOLFSSL_CERT_MANAGER with the trusted CAs - passed as
+ * a void pointer to avoid an SSL layer dependency.
+ * @param [out] tstInfo TSTInfo object to fill. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp or cm is NULL.
+ * @return TSP_VERIFY_E when the response was not granted, has no token, the
+ * token does not verify or the signer does not chain to a trusted CA.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspResponse_VerifyWithCm(TspResponse* resp, void* cm,
+ TspTstInfo* tstInfo)
+{
+ int ret;
+
+ WOLFSSL_ENTER("wc_TspResponse_VerifyWithCm");
+
+ /* A certificate manager is required to establish trust. */
+ if (cm == NULL) {
+ return BAD_FUNC_ARG;
+ }
+
+ /* Chain the signer's certificate to a trusted CA in the manager. */
+ ret = TspResponse_Verify(resp, NULL, 0, cm, tstInfo);
+
+ WOLFSSL_LEAVE("wc_TspResponse_VerifyWithCm", ret);
+ return ret;
+}
+
+/* Verify the time-stamp token of a TimeStampResp and that it is over the data.
+ *
+ * Convenience over wc_TspResponse_Verify() that also confirms the time-stamp
+ * is over the given data - hashing the data with the token's message imprint
+ * algorithm and comparing to the imprint. The caller does not hash the data.
+ *
+ * @param [in] resp TimeStampResp object with a token to verify.
+ * @param [in] cert DER encoded certificate of the trusted TSA. May be
+ * NULL - see wc_TspResponse_Verify().
+ * @param [in] certSz Length of certificate in bytes.
+ * @param [in] data Data that was time-stamped.
+ * @param [in] dataSz Length of data in bytes.
+ * @param [out] tstInfo TSTInfo object to fill. May be NULL.
+ * @return 0 on success.
+ * @return BAD_FUNC_ARG when resp or data is NULL.
+ * @return TSP_VERIFY_E when the token does not verify or the data does not
+ * match the message imprint.
+ * @return HASH_TYPE_E when the imprint's hash algorithm is not supported.
+ * @return MEMORY_E on dynamic memory allocation failure.
+ */
+int wc_TspResponse_VerifyData(TspResponse* resp, const byte* cert,
+ word32 certSz, const byte* data, word32 dataSz, TspTstInfo* tstInfo)
+{
+ int ret;
+ TspTstInfo tstLocal;
+
+ WOLFSSL_ENTER("wc_TspResponse_VerifyData");
+
+ /* Validate parameter - resp is checked by wc_TspResponse_Verify(). */
+ if (data == NULL) {
+ return BAD_FUNC_ARG;
+ }
+ /* A TSTInfo is needed for the data check - use a local when not wanted. */
+ if (tstInfo == NULL) {
+ tstInfo = &tstLocal;
+ }
+
+ /* Verify the response and its token. */
+ ret = wc_TspResponse_Verify(resp, cert, certSz, tstInfo);
+ /* Confirm the time-stamp is over the given data. */
+ if (ret == 0) {
+ ret = wc_TspTstInfo_VerifyData(tstInfo, data, dataSz);
+ }
+
+ WOLFSSL_LEAVE("wc_TspResponse_VerifyData", ret);
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif /* HAVE_PKCS7 */
+
+#endif /* WOLFSSL_TSP && WOLFSSL_ASN_TEMPLATE && !NO_ASN */
diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c
index 49deeaf0cec..3e95bfe810f 100644
--- a/wolfcrypt/test/test.c
+++ b/wolfcrypt/test/test.c
@@ -434,6 +434,9 @@ static const byte const_byte_array[] = "A+Gd\0\0\0";
#ifdef HAVE_PKCS7
#include
#endif
+#ifdef WOLFSSL_TSP
+ #include
+#endif
#ifdef HAVE_PKCS12
#include
#endif
@@ -1022,6 +1025,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t scrypt_test(void);
word32 keySz);
#endif
#endif
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_REQUESTER) && \
+ defined(WOLFSSL_TSP_RESPONDER)
+ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t tsp_test(void);
+#endif
#if !defined(NO_ASN_TIME) && !defined(NO_RSA) && defined(WOLFSSL_TEST_CERT) && \
!defined(NO_FILESYSTEM)
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cert_test(void);
@@ -3255,6 +3262,14 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
#endif
#endif
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_REQUESTER) && \
+ defined(WOLFSSL_TSP_RESPONDER)
+ if ( (ret = tsp_test()) != 0)
+ TEST_FAIL("TSP test failed!\n", ret);
+ else
+ TEST_PASS("TSP test passed!\n");
+#endif
+
#if defined(WOLFSSL_PUBLIC_MP) && \
((defined(WOLFSSL_SP_MATH_ALL) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
defined(USE_FAST_MATH))
@@ -67708,6 +67723,555 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t pkcs7signed_test(void)
#endif /* HAVE_PKCS7 */
+#if defined(WOLFSSL_TSP) && defined(WOLFSSL_TSP_REQUESTER) && \
+ defined(WOLFSSL_TSP_RESPONDER)
+
+#ifndef NO_SHA256
+ #define TSP_TEST_HASH_OID SHA256h
+ #define TSP_TEST_HASH_SZ 32
+#elif !defined(NO_SHA)
+ #define TSP_TEST_HASH_OID SHAh
+ #define TSP_TEST_HASH_SZ 20
+#endif
+
+#ifdef TSP_TEST_HASH_OID
+
+/* 1.3.6.1.4.1.999.1 - test TSA policy. */
+static const byte tspTestPolicy[] = {
+ 0x2b, 0x06, 0x01, 0x04, 0x01, 0x87, 0x67, 0x01
+};
+/* Nonce with top bit set to check INTEGER encoding. */
+static const byte tspTestNonce[] = {
+ 0xc3, 0x5a, 0x10, 0x42, 0x77, 0x08, 0x99, 0x01
+};
+/* Serial number with top bit set to check INTEGER encoding. */
+static const byte tspTestSerial[] = { 0x9a, 0x33 };
+/* Time of test time-stamp. */
+static const byte tspTestGenTime[] = "20260604120000Z";
+
+/* Test encoding and decoding of TimeStampReq. */
+static wc_test_ret_t tsp_req_test(byte* hashedMsg)
+{
+ wc_test_ret_t ret = 0;
+ int r;
+ TspRequest req;
+ TspRequest reqDec;
+ byte enc[256];
+ word32 encSz = 0;
+ word32 sz;
+
+ r = wc_TspRequest_Init(&req);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ req.imprint.hashAlgOID = TSP_TEST_HASH_OID;
+ XMEMCPY(req.imprint.hash, hashedMsg, TSP_TEST_HASH_SZ);
+ req.imprint.hashSz = TSP_TEST_HASH_SZ;
+ XMEMCPY(req.policy, tspTestPolicy, sizeof(tspTestPolicy));
+ req.policySz = sizeof(tspTestPolicy);
+ XMEMCPY(req.nonce, tspTestNonce, sizeof(tspTestNonce));
+ req.nonceSz = sizeof(tspTestNonce);
+ req.certReq = 1;
+
+ /* Get length of encoding. */
+ r = wc_TspRequest_Encode(&req, NULL, &encSz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if ((encSz == 0) || (encSz > sizeof(enc)))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ /* Check too small a buffer is rejected. */
+ sz = encSz - 1;
+ r = wc_TspRequest_Encode(&req, enc, &sz);
+ if (r != WC_NO_ERR_TRACE(BUFFER_E))
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ sz = sizeof(enc);
+ r = wc_TspRequest_Encode(&req, enc, &sz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (sz != encSz)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+ r = wc_TspRequest_Decode(&reqDec, enc, sz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (reqDec.version != WC_TSP_VERSION)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (reqDec.imprint.hashAlgOID != TSP_TEST_HASH_OID)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((reqDec.imprint.hashSz != TSP_TEST_HASH_SZ) ||
+ (XMEMCMP(reqDec.imprint.hash, hashedMsg,
+ TSP_TEST_HASH_SZ) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((reqDec.policySz != sizeof(tspTestPolicy)) ||
+ (XMEMCMP(reqDec.policy, tspTestPolicy,
+ sizeof(tspTestPolicy)) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((reqDec.nonceSz != sizeof(tspTestNonce)) ||
+ (XMEMCMP(reqDec.nonce, tspTestNonce, sizeof(tspTestNonce)) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (reqDec.certReq != 1)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+out:
+ return ret;
+}
+
+/* Test encoding and decoding of TSTInfo and checking against request. */
+static wc_test_ret_t tsp_tstinfo_test(byte* hashedMsg)
+{
+ wc_test_ret_t ret = 0;
+ int r;
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ TspRequest req;
+ byte enc[256];
+ word32 sz;
+
+ r = wc_TspTstInfo_Init(&tst);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ tst.policy = tspTestPolicy;
+ tst.policySz = sizeof(tspTestPolicy);
+ tst.imprint.hashAlgOID = TSP_TEST_HASH_OID;
+ XMEMCPY(tst.imprint.hash, hashedMsg, TSP_TEST_HASH_SZ);
+ tst.imprint.hashSz = TSP_TEST_HASH_SZ;
+ tst.serial = tspTestSerial;
+ tst.serialSz = sizeof(tspTestSerial);
+ tst.genTime = tspTestGenTime;
+ tst.genTimeSz = sizeof(tspTestGenTime) - 1;
+ /* millis 2 bytes encoded and micros has top bit set - zero byte added. */
+ tst.accuracy.seconds = 1;
+ tst.accuracy.millis = 500;
+ tst.accuracy.micros = 130;
+ tst.ordering = 1;
+ tst.nonce = tspTestNonce;
+ tst.nonceSz = sizeof(tspTestNonce);
+
+ sz = sizeof(enc);
+ r = wc_TspTstInfo_Encode(&tst, enc, &sz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+
+ r = wc_TspTstInfo_Decode(&tstDec, enc, sz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (tstDec.version != WC_TSP_VERSION)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.policySz != sizeof(tspTestPolicy)) ||
+ (XMEMCMP(tstDec.policy, tspTestPolicy,
+ sizeof(tspTestPolicy)) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (tstDec.imprint.hashAlgOID != TSP_TEST_HASH_OID)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.imprint.hashSz != TSP_TEST_HASH_SZ) ||
+ (XMEMCMP(tstDec.imprint.hash, hashedMsg,
+ TSP_TEST_HASH_SZ) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.serialSz != sizeof(tspTestSerial)) ||
+ (XMEMCMP(tstDec.serial, tspTestSerial,
+ sizeof(tspTestSerial)) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.genTimeSz != sizeof(tspTestGenTime) - 1) ||
+ (XMEMCMP(tstDec.genTime, tspTestGenTime,
+ sizeof(tspTestGenTime) - 1) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.accuracy.seconds != 1) || (tstDec.accuracy.millis != 500) ||
+ (tstDec.accuracy.micros != 130))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (tstDec.ordering != 1)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.nonceSz != sizeof(tspTestNonce)) ||
+ (XMEMCMP(tstDec.nonce, tspTestNonce, sizeof(tspTestNonce)) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (tstDec.tsa != NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+ /* Check TSTInfo against a matching request. */
+ r = wc_TspRequest_Init(&req);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ req.imprint.hashAlgOID = TSP_TEST_HASH_OID;
+ XMEMCPY(req.imprint.hash, hashedMsg, TSP_TEST_HASH_SZ);
+ req.imprint.hashSz = TSP_TEST_HASH_SZ;
+ XMEMCPY(req.policy, tspTestPolicy, sizeof(tspTestPolicy));
+ req.policySz = sizeof(tspTestPolicy);
+ XMEMCPY(req.nonce, tspTestNonce, sizeof(tspTestNonce));
+ req.nonceSz = sizeof(tspTestNonce);
+ r = wc_TspTstInfo_CheckRequest(&tstDec, &req);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ /* Check nonce mismatch is detected. */
+ XMEMCPY(req.nonce, tspTestSerial, sizeof(tspTestSerial));
+ req.nonceSz = sizeof(tspTestSerial);
+ r = wc_TspTstInfo_CheckRequest(&tstDec, &req);
+ if (r != WC_NO_ERR_TRACE(TSP_VERIFY_E))
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ XMEMCPY(req.nonce, tspTestNonce, sizeof(tspTestNonce));
+ req.nonceSz = sizeof(tspTestNonce);
+ /* Check hash mismatch is detected. */
+ req.imprint.hash[0] ^= 0x80;
+ r = wc_TspTstInfo_CheckRequest(&tstDec, &req);
+ req.imprint.hash[0] ^= 0x80;
+ if (r != WC_NO_ERR_TRACE(TSP_VERIFY_E))
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+
+out:
+ return ret;
+}
+
+/* Test encoding and decoding of TimeStampResp. */
+static wc_test_ret_t tsp_resp_test(void)
+{
+ wc_test_ret_t ret = 0;
+ int r;
+ TspResponse resp;
+ TspResponse respDec;
+ static const char statusText[] = "hash algorithm not supported";
+ byte enc[256];
+ word32 sz;
+
+ /* Rejection response with status string and failure information. */
+ r = wc_TspResponse_Init(&resp);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ resp.status = WC_TSP_PKISTATUS_REJECTION;
+ resp.statusString = (const byte*)statusText;
+ resp.statusStringSz = (word32)XSTRLEN(statusText);
+ resp.failInfo = WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_BAD_REQUEST;
+
+ sz = sizeof(enc);
+ r = wc_TspResponse_Encode(&resp, enc, &sz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+
+ r = wc_TspResponse_Decode(&respDec, enc, sz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (respDec.status != WC_TSP_PKISTATUS_REJECTION)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((respDec.statusStringSz != (word32)XSTRLEN(statusText)) ||
+ (XMEMCMP(respDec.statusString, statusText,
+ respDec.statusStringSz) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (respDec.failInfo != (WC_TSP_FAIL_BAD_ALG | WC_TSP_FAIL_BAD_REQUEST))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if (respDec.token != NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+out:
+ return ret;
+}
+
+#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
+ !defined(WC_NO_RNG)
+/* Test creating and verifying a TimeStampResp with a TimeStampToken. */
+static wc_test_ret_t tsp_token_test(void)
+{
+ wc_test_ret_t ret = 0;
+ int r;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ int rngInit = 0;
+ byte* tsaCert = NULL;
+ byte* tsaKey = NULL;
+ word32 tsaCertSz = FOURK_BUF;
+ word32 tsaKeySz = FOURK_BUF;
+ byte* token = NULL;
+ word32 tokenSz = FOURK_BUF;
+ byte* respEnc = NULL;
+ word32 respEncSz = FOURK_BUF;
+ TspRequest req;
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ TspResponse resp;
+ TspResponse respDec;
+ static const char msg[] = "wolfSSL time-stamped message";
+ byte digest[WC_SHA256_DIGEST_SIZE];
+
+ tsaCert = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ tsaKey = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ token = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ respEnc = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ if ((tsaCert == NULL) || (tsaKey == NULL) || (token == NULL) ||
+ (respEnc == NULL))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+ /* Load the TSA's certificate and key. */
+#ifdef USE_CERT_BUFFERS_2048
+ XMEMCPY(tsaCert, tsa_cert_der_2048, sizeof_tsa_cert_der_2048);
+ tsaCertSz = (word32)sizeof_tsa_cert_der_2048;
+ XMEMCPY(tsaKey, tsa_key_der_2048, sizeof_tsa_key_der_2048);
+ tsaKeySz = (word32)sizeof_tsa_key_der_2048;
+#elif !defined(NO_FILESYSTEM)
+ {
+ XFILE file;
+
+ file = XFOPEN(CERT_ROOT "tsa-cert.der", "rb");
+ if (file == XBADFILE)
+ ERROR_OUT(WC_TEST_RET_ENC_ERRNO, out);
+ tsaCertSz = (word32)XFREAD(tsaCert, 1, FOURK_BUF, file);
+ XFCLOSE(file);
+
+ file = XFOPEN(CERT_ROOT "tsa-key.der", "rb");
+ if (file == XBADFILE)
+ ERROR_OUT(WC_TEST_RET_ENC_ERRNO, out);
+ tsaKeySz = (word32)XFREAD(tsaKey, 1, FOURK_BUF, file);
+ XFCLOSE(file);
+ }
+#else
+ /* No TSA certificate available - skip test. */
+ goto out;
+#endif
+
+#ifndef HAVE_FIPS
+ r = wc_InitRng_ex(&rng, HEAP_HINT, devId);
+#else
+ r = wc_InitRng(&rng);
+#endif
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ rngInit = 1;
+
+ /* Requester creates a request for a time-stamp of a message hash. */
+ r = wc_Sha256Hash((const byte*)msg, (word32)XSTRLEN(msg), digest);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ r = wc_TspRequest_Init(&req);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ req.imprint.hashAlgOID = SHA256h;
+ XMEMCPY(req.imprint.hash, digest, sizeof(digest));
+ req.imprint.hashSz = (word32)sizeof(digest);
+ XMEMCPY(req.nonce, tspTestNonce, sizeof(tspTestNonce));
+ req.nonceSz = sizeof(tspTestNonce);
+ req.certReq = 1;
+
+ /* TSA creates the TSTInfo for the request. */
+ r = wc_TspTstInfo_Init(&tst);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ tst.policy = tspTestPolicy;
+ tst.policySz = sizeof(tspTestPolicy);
+ tst.imprint = req.imprint;
+ tst.serial = tspTestSerial;
+ tst.serialSz = sizeof(tspTestSerial);
+ tst.accuracy.seconds = 1;
+ tst.nonce = req.nonce;
+ tst.nonceSz = req.nonceSz;
+#if defined(NO_ASN_TIME) || defined(USER_TIME) || defined(TIME_OVERRIDES)
+ /* No current time available - set the time of the time-stamp. */
+ tst.genTime = tspTestGenTime;
+ tst.genTimeSz = sizeof(tspTestGenTime) - 1;
+#endif
+
+ /* TSA signs the TSTInfo to make a TimeStampToken. */
+ pkcs7 = wc_PKCS7_New(HEAP_HINT, devId);
+ if (pkcs7 == NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ r = wc_PKCS7_InitWithCert(pkcs7, tsaCert, tsaCertSz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ pkcs7->rng = &rng;
+ pkcs7->hashOID = SHA256h;
+ pkcs7->encryptOID = RSAk;
+ pkcs7->privateKey = tsaKey;
+ pkcs7->privateKeySz = tsaKeySz;
+ r = wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+
+ /* TSA puts the token in a granted response. */
+ r = wc_TspResponse_Init(&resp);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ resp.status = WC_TSP_PKISTATUS_GRANTED;
+ resp.token = token;
+ resp.tokenSz = tokenSz;
+ r = wc_TspResponse_Encode(&resp, respEnc, &respEncSz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+
+ /* Requester decodes the response. */
+ r = wc_TspResponse_Decode(&respDec, respEnc, respEncSz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (respDec.status != WC_TSP_PKISTATUS_GRANTED)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((respDec.token == NULL) || (respDec.tokenSz != tokenSz) ||
+ (XMEMCMP(respDec.token, token, tokenSz) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+ /* Requester verifies the token and checks it against the request. */
+ pkcs7 = wc_PKCS7_New(HEAP_HINT, devId);
+ if (pkcs7 == NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ r = wc_PKCS7_InitWithCert(pkcs7, NULL, 0);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ r = wc_TspTstInfo_VerifyWithPKCS7(pkcs7, (byte*)respDec.token, respDec.tokenSz,
+ &tstDec);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ r = wc_TspTstInfo_CheckRequest(&tstDec, &req);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (tstDec.genTimeSz < 15)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+ /* Check a token for a different message hash is detected. */
+ req.imprint.hash[0] ^= 0x80;
+ r = wc_TspTstInfo_CheckRequest(&tstDec, &req);
+ if (r != WC_NO_ERR_TRACE(TSP_VERIFY_E))
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+
+out:
+ if (pkcs7 != NULL)
+ wc_PKCS7_Free(pkcs7);
+ if (rngInit)
+ wc_FreeRng(&rng);
+ XFREE(respEnc, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ XFREE(token, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ XFREE(tsaKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ XFREE(tsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+#endif /* HAVE_PKCS7 && !NO_RSA && !NO_SHA256 && !WC_NO_RNG */
+
+#if defined(HAVE_PKCS7) && defined(HAVE_ECC) && \
+ defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) && \
+ !defined(WC_NO_RNG)
+/* Test creating and verifying a time-stamp token signed with ECDSA. */
+static wc_test_ret_t tsp_ecc_token_test(void)
+{
+ wc_test_ret_t ret = 0;
+ int r;
+ wc_PKCS7* pkcs7 = NULL;
+ WC_RNG rng;
+ int rngInit = 0;
+ TspTstInfo tst;
+ TspTstInfo tstDec;
+ byte* token = NULL;
+ word32 tokenSz = FOURK_BUF;
+ byte hashedMsg[TSP_TEST_HASH_SZ];
+ word32 i;
+
+ for (i = 0; i < (word32)sizeof(hashedMsg); i++)
+ hashedMsg[i] = (byte)(0x80 + i);
+
+ token = (byte*)XMALLOC(FOURK_BUF, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ if (token == NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+#ifndef HAVE_FIPS
+ r = wc_InitRng_ex(&rng, HEAP_HINT, devId);
+#else
+ r = wc_InitRng(&rng);
+#endif
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ rngInit = 1;
+
+ /* TSTInfo to be signed by the ECC TSA. */
+ r = wc_TspTstInfo_Init(&tst);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ tst.policy = tspTestPolicy;
+ tst.policySz = sizeof(tspTestPolicy);
+ tst.imprint.hashAlgOID = TSP_TEST_HASH_OID;
+ XMEMCPY(tst.imprint.hash, hashedMsg, TSP_TEST_HASH_SZ);
+ tst.imprint.hashSz = TSP_TEST_HASH_SZ;
+ tst.serial = tspTestSerial;
+ tst.serialSz = sizeof(tspTestSerial);
+#if defined(NO_ASN_TIME) || defined(USER_TIME) || defined(TIME_OVERRIDES)
+ /* No current time available - set the time of the time-stamp. */
+ tst.genTime = tspTestGenTime;
+ tst.genTimeSz = sizeof(tspTestGenTime) - 1;
+#endif
+
+ /* TSA signs the TSTInfo with its ECC key. */
+ pkcs7 = wc_PKCS7_New(HEAP_HINT, devId);
+ if (pkcs7 == NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ r = wc_PKCS7_InitWithCert(pkcs7, (byte*)tsa_ecc_cert_der_256,
+ sizeof_tsa_ecc_cert_der_256);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ pkcs7->rng = &rng;
+ pkcs7->hashOID = SHA256h;
+ pkcs7->encryptOID = ECDSAk;
+ pkcs7->privateKey = (byte*)tsa_ecc_key_der_256;
+ pkcs7->privateKeySz = sizeof_tsa_ecc_key_der_256;
+ r = wc_TspTstInfo_SignWithPkcs7(&tst, pkcs7, token, &tokenSz);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ wc_PKCS7_Free(pkcs7);
+ pkcs7 = NULL;
+
+ /* Requester verifies the token. */
+ pkcs7 = wc_PKCS7_New(HEAP_HINT, devId);
+ if (pkcs7 == NULL)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ r = wc_PKCS7_InitWithCert(pkcs7, NULL, 0);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ r = wc_TspTstInfo_VerifyWithPKCS7(pkcs7, token, tokenSz, &tstDec);
+ if (r != 0)
+ ERROR_OUT(WC_TEST_RET_ENC_EC(r), out);
+ if (tstDec.version != WC_TSP_VERSION)
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+ if ((tstDec.imprint.hashSz != TSP_TEST_HASH_SZ) ||
+ (XMEMCMP(tstDec.imprint.hash, hashedMsg, TSP_TEST_HASH_SZ) != 0))
+ ERROR_OUT(WC_TEST_RET_ENC_NC, out);
+
+out:
+ if (pkcs7 != NULL)
+ wc_PKCS7_Free(pkcs7);
+ if (rngInit)
+ wc_FreeRng(&rng);
+ XFREE(token, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+ return ret;
+}
+#endif /* HAVE_PKCS7 && HAVE_ECC && USE_CERT_BUFFERS_256 && !NO_SHA256 &&
+ * !WC_NO_RNG */
+
+#endif /* TSP_TEST_HASH_OID */
+
+WOLFSSL_TEST_SUBROUTINE wc_test_ret_t tsp_test(void)
+{
+ wc_test_ret_t ret = 0;
+#ifdef TSP_TEST_HASH_OID
+ byte hashedMsg[TSP_TEST_HASH_SZ];
+ word32 i;
+
+ WOLFSSL_ENTER("tsp_test");
+
+ for (i = 0; i < (word32)sizeof(hashedMsg); i++)
+ hashedMsg[i] = (byte)(0x80 + i);
+
+ ret = tsp_req_test(hashedMsg);
+ if (ret == 0)
+ ret = tsp_tstinfo_test(hashedMsg);
+ if (ret == 0)
+ ret = tsp_resp_test();
+#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \
+ !defined(WC_NO_RNG)
+ if (ret == 0)
+ ret = tsp_token_test();
+#endif
+#if defined(HAVE_PKCS7) && defined(HAVE_ECC) && \
+ defined(USE_CERT_BUFFERS_256) && !defined(NO_SHA256) && \
+ !defined(WC_NO_RNG)
+ if (ret == 0)
+ ret = tsp_ecc_token_test();
+#endif
+#endif /* TSP_TEST_HASH_OID */
+
+ return ret;
+}
+
+#endif /* WOLFSSL_TSP */
+
#if defined(WOLFSSL_PUBLIC_MP) && \
((defined(WOLFSSL_SP_MATH_ALL) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
defined(USE_FAST_MATH))
diff --git a/wolfssl-VS2022.vcxproj b/wolfssl-VS2022.vcxproj
index 81d32758e91..9c872ac266a 100644
--- a/wolfssl-VS2022.vcxproj
+++ b/wolfssl-VS2022.vcxproj
@@ -465,6 +465,7 @@
+
diff --git a/wolfssl.vcproj b/wolfssl.vcproj
index a7f12b57e78..af99fc97538 100644
--- a/wolfssl.vcproj
+++ b/wolfssl.vcproj
@@ -399,6 +399,10 @@
RelativePath=".\wolfcrypt\src\tfm.c"
>
+
+
diff --git a/wolfssl.vcxproj b/wolfssl.vcxproj
index 44c23ab74ee..08a467a1588 100644
--- a/wolfssl.vcxproj
+++ b/wolfssl.vcxproj
@@ -465,6 +465,7 @@
+
diff --git a/wolfssl/certs_test.h b/wolfssl/certs_test.h
index b438a23edce..097eab397df 100644
--- a/wolfssl/certs_test.h
+++ b/wolfssl/certs_test.h
@@ -2135,6 +2135,908 @@ static const unsigned char server_cert_der_2048[] =
};
#define sizeof_server_cert_der_2048 (sizeof(server_cert_der_2048))
+/* ./certs/tsa-key.der, 2048-bit */
+static const unsigned char tsa_key_der_2048[] =
+{
+ 0x30, 0x82, 0x04, 0xA4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0x93, 0x74, 0x96, 0xF0, 0x0C, 0xFD, 0xD7, 0x2A,
+ 0x5A, 0xA0, 0xA1, 0x6E, 0xB4, 0x63, 0xB7, 0xF4, 0xE6, 0x8B,
+ 0x77, 0x8F, 0x49, 0xF7, 0xA9, 0x0C, 0xB3, 0x3D, 0x5A, 0x60,
+ 0xA8, 0xB8, 0x67, 0x75, 0xB8, 0xD0, 0xCE, 0xBA, 0xCE, 0x65,
+ 0xE5, 0x64, 0xA2, 0xD0, 0xC8, 0xD0, 0x2C, 0x5B, 0xA8, 0xA5,
+ 0xD3, 0x3F, 0x2C, 0x46, 0x07, 0xE6, 0xB1, 0xB7, 0xA5, 0x65,
+ 0xF8, 0x9F, 0x89, 0xE1, 0x93, 0xBC, 0x49, 0xCD, 0x55, 0xFD,
+ 0x9B, 0xA1, 0x1E, 0x1E, 0xF7, 0x99, 0x07, 0x4A, 0x1C, 0xC8,
+ 0xB6, 0x88, 0xA2, 0x5E, 0x77, 0xA5, 0x3F, 0x79, 0x2F, 0xD3,
+ 0x43, 0x41, 0xF6, 0xCF, 0xFF, 0x78, 0x30, 0x89, 0x12, 0xB1,
+ 0x0C, 0x3F, 0xF5, 0x2F, 0x62, 0xE6, 0x71, 0x33, 0x02, 0x70,
+ 0xD9, 0x42, 0x1B, 0xE4, 0x07, 0x9C, 0x9D, 0xC7, 0x5E, 0x5B,
+ 0x4E, 0x93, 0x8F, 0x6B, 0x0E, 0xFE, 0x5B, 0x7E, 0x02, 0x6F,
+ 0x8D, 0xE8, 0x7D, 0xE9, 0xAA, 0x1D, 0x12, 0xF2, 0x6B, 0xA4,
+ 0x55, 0x61, 0x59, 0xE0, 0x3A, 0x38, 0xB0, 0x14, 0x41, 0x9D,
+ 0xBB, 0xC6, 0xE0, 0x6A, 0x9D, 0x35, 0xA8, 0x08, 0xF2, 0x3C,
+ 0x29, 0xB9, 0xA7, 0xB2, 0x38, 0x47, 0xF6, 0x70, 0x1A, 0xB8,
+ 0xD2, 0xD5, 0xC9, 0x48, 0xF0, 0xEE, 0x60, 0x1D, 0x14, 0x77,
+ 0x8A, 0x2C, 0x38, 0x90, 0x29, 0x7F, 0x42, 0xE4, 0xDD, 0x92,
+ 0x0A, 0x8D, 0x03, 0x88, 0x44, 0x0A, 0xF0, 0xB6, 0x14, 0xBD,
+ 0xE5, 0x11, 0x50, 0x94, 0x26, 0xFB, 0x3E, 0xAB, 0x17, 0x69,
+ 0x3D, 0xA6, 0x45, 0xC7, 0xAD, 0x86, 0x41, 0x66, 0xE6, 0x53,
+ 0xE5, 0xDC, 0xD8, 0x4A, 0x04, 0xC7, 0xAF, 0xFB, 0xA7, 0xDF,
+ 0x99, 0x77, 0x57, 0xD9, 0x25, 0x7C, 0x67, 0x73, 0x3D, 0xEE,
+ 0x60, 0xF3, 0x63, 0x2D, 0x68, 0x0F, 0x97, 0xDA, 0x68, 0xD5,
+ 0x3D, 0xE5, 0x06, 0x92, 0x82, 0xDD, 0x90, 0x25, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x04, 0x47, 0x0D,
+ 0x11, 0x35, 0xFC, 0x9A, 0x79, 0xCE, 0xE5, 0x90, 0x59, 0x4D,
+ 0xA0, 0x03, 0x98, 0x4B, 0x4F, 0x2A, 0x19, 0x92, 0x31, 0x3E,
+ 0x35, 0x06, 0x9C, 0x15, 0xDB, 0x98, 0xF9, 0x64, 0x7D, 0x9C,
+ 0x98, 0xBE, 0x19, 0x30, 0x34, 0x9C, 0xDA, 0x20, 0x59, 0x03,
+ 0x2C, 0xFA, 0xCF, 0x1B, 0xE3, 0x82, 0x44, 0x77, 0xB7, 0x29,
+ 0x07, 0xD9, 0xB4, 0x49, 0xF6, 0xDD, 0x26, 0xAF, 0xA6, 0xC7,
+ 0x6A, 0x33, 0xBA, 0x8D, 0x04, 0x8D, 0x8D, 0x0D, 0x25, 0x23,
+ 0xD4, 0xA0, 0xCA, 0xAC, 0x6E, 0xC1, 0x7B, 0x7C, 0x39, 0x00,
+ 0xFF, 0xDE, 0x5E, 0xAD, 0xC8, 0x53, 0x4F, 0xFE, 0x10, 0xC6,
+ 0xF3, 0x48, 0x76, 0x7E, 0xD5, 0x7B, 0xAA, 0xA7, 0xFA, 0x5C,
+ 0x6E, 0xF2, 0x98, 0xB8, 0x0E, 0x3D, 0xAC, 0x8B, 0xC7, 0x95,
+ 0x2A, 0xF2, 0x19, 0x74, 0x54, 0xC7, 0x6B, 0x98, 0xC2, 0x52,
+ 0x78, 0x86, 0xD4, 0x99, 0xCD, 0xC7, 0x04, 0x55, 0x02, 0x00,
+ 0xB4, 0xBC, 0x55, 0x84, 0x53, 0x23, 0xC8, 0xC0, 0x51, 0xF2,
+ 0x3E, 0x89, 0xFB, 0xAE, 0xE3, 0xF5, 0x61, 0xDC, 0x4C, 0xB1,
+ 0xA1, 0x0D, 0x25, 0x6E, 0x46, 0x8B, 0xB4, 0x27, 0x28, 0x62,
+ 0xEF, 0x4C, 0x07, 0x1E, 0x15, 0xF2, 0x0A, 0x20, 0x01, 0x53,
+ 0x02, 0xB1, 0x4A, 0x01, 0xF1, 0x39, 0x28, 0x55, 0x77, 0x92,
+ 0xC0, 0x47, 0x8D, 0x2D, 0xB5, 0x61, 0xE8, 0x6B, 0xE2, 0x78,
+ 0x2A, 0xBC, 0xC4, 0xEE, 0x41, 0x6A, 0x42, 0xBD, 0xA3, 0xE9,
+ 0x70, 0xC0, 0x40, 0xBC, 0xC1, 0xC7, 0xEA, 0xDE, 0xB6, 0x9D,
+ 0xA6, 0xA2, 0x6D, 0xC1, 0x52, 0x87, 0xEA, 0xAC, 0x2C, 0x61,
+ 0xD0, 0x5A, 0x37, 0xEA, 0xEE, 0x35, 0xE1, 0xD0, 0x5B, 0x50,
+ 0x6E, 0x7B, 0xDC, 0x5B, 0xBC, 0xF8, 0xC9, 0x74, 0x2C, 0xE1,
+ 0xBA, 0x0E, 0x88, 0xB9, 0xB5, 0xBA, 0x0D, 0x69, 0x4F, 0x5A,
+ 0xA7, 0x27, 0x11, 0x02, 0x81, 0x81, 0x00, 0xCF, 0x69, 0xB8,
+ 0xF9, 0x52, 0x4D, 0x90, 0x6F, 0x85, 0xA1, 0xB1, 0xC8, 0x76,
+ 0x34, 0x39, 0x11, 0xE8, 0xC7, 0xE8, 0x90, 0x65, 0x85, 0x23,
+ 0x1B, 0x4A, 0xD9, 0x71, 0x07, 0x71, 0xF5, 0x46, 0x64, 0x33,
+ 0x45, 0x11, 0x61, 0xAD, 0xDD, 0x8D, 0x0A, 0x7A, 0xFB, 0x5C,
+ 0xF2, 0x91, 0xA7, 0xB8, 0x64, 0xB8, 0xC9, 0xDB, 0x25, 0x7E,
+ 0xE8, 0x93, 0x2F, 0xD5, 0x6F, 0xBD, 0xBF, 0x5A, 0xC2, 0x73,
+ 0xF0, 0x56, 0xB3, 0x12, 0x02, 0x11, 0x87, 0xE2, 0xFB, 0x83,
+ 0x52, 0x4B, 0x26, 0x1F, 0x38, 0xD9, 0x1F, 0x25, 0xB1, 0xAA,
+ 0x7D, 0xD1, 0x43, 0xB9, 0x6F, 0x8E, 0x14, 0xA7, 0x55, 0x8D,
+ 0xC2, 0xC2, 0x76, 0xF6, 0x25, 0x62, 0xD2, 0xDD, 0x1F, 0x4C,
+ 0xCC, 0x16, 0x84, 0x28, 0xCB, 0x64, 0x08, 0x8A, 0xB2, 0x50,
+ 0xAD, 0x19, 0xAF, 0x18, 0x5D, 0x2A, 0x59, 0x8F, 0xD0, 0xA1,
+ 0x8C, 0x75, 0xAD, 0x04, 0xF9, 0x02, 0x81, 0x81, 0x00, 0xB5,
+ 0xFF, 0x4B, 0x19, 0x5C, 0x7D, 0xCA, 0xB2, 0x63, 0x37, 0xB3,
+ 0x58, 0x0F, 0xCF, 0xD6, 0x16, 0x29, 0xB9, 0x13, 0x5F, 0x36,
+ 0xFF, 0x1B, 0x28, 0x0E, 0xA4, 0x1B, 0x05, 0xCC, 0x50, 0x95,
+ 0xFE, 0x3D, 0x6F, 0xF1, 0xDD, 0xB1, 0x6F, 0x47, 0xFD, 0x30,
+ 0x52, 0x9E, 0x52, 0xC7, 0x7E, 0xFD, 0x72, 0x09, 0x99, 0x1C,
+ 0xF0, 0xF6, 0x37, 0x75, 0x4B, 0x81, 0x19, 0xA8, 0xBC, 0x51,
+ 0x83, 0xCE, 0x3E, 0xAA, 0xBA, 0x9A, 0x3B, 0x39, 0x73, 0xD5,
+ 0xAC, 0x56, 0x05, 0xDC, 0x03, 0x37, 0x42, 0x59, 0x99, 0x6D,
+ 0x20, 0x5A, 0x01, 0x6F, 0x4A, 0x7C, 0xC8, 0x45, 0x8F, 0x15,
+ 0x9C, 0x71, 0x30, 0x6E, 0x67, 0xCA, 0xD5, 0x99, 0x4D, 0xB5,
+ 0x8A, 0x64, 0xE4, 0x99, 0xD9, 0x08, 0x6F, 0x09, 0x46, 0x27,
+ 0xFA, 0x11, 0xD9, 0x9E, 0x13, 0xFA, 0xB4, 0x65, 0xA1, 0x08,
+ 0x9D, 0xB1, 0x6D, 0x33, 0x9C, 0x2B, 0x8D, 0x02, 0x81, 0x80,
+ 0x3A, 0xDD, 0x9E, 0x89, 0xE4, 0x39, 0xEF, 0x4C, 0x37, 0x78,
+ 0xF4, 0xA4, 0x18, 0x28, 0x2A, 0x2A, 0x53, 0x0E, 0xA9, 0x8A,
+ 0x91, 0xC5, 0x7F, 0x79, 0x37, 0x7D, 0x0E, 0xFF, 0x35, 0xF0,
+ 0x8E, 0xD1, 0xD1, 0x5B, 0x40, 0xDB, 0xA0, 0x24, 0xC8, 0xEA,
+ 0xB8, 0x8C, 0xAE, 0x8C, 0x89, 0x9A, 0x38, 0x53, 0x1D, 0xBE,
+ 0xEC, 0x5B, 0x6F, 0xF1, 0x42, 0x14, 0xC9, 0x56, 0xB5, 0x5B,
+ 0xA1, 0xBE, 0x9E, 0x79, 0x0F, 0xA2, 0x32, 0xF2, 0x33, 0x57,
+ 0x85, 0xAC, 0x2C, 0x51, 0x26, 0xD2, 0xE2, 0xF9, 0x97, 0x65,
+ 0xA7, 0xA5, 0x0C, 0xE6, 0x38, 0x86, 0x28, 0x12, 0xE9, 0x18,
+ 0x23, 0x85, 0xBC, 0x7E, 0x12, 0x03, 0x01, 0x49, 0x0B, 0x0B,
+ 0x1D, 0x86, 0xCC, 0x9C, 0xFF, 0xA6, 0xF2, 0x8D, 0x07, 0x0F,
+ 0x05, 0x8E, 0x26, 0x27, 0x7A, 0xAA, 0x9F, 0x17, 0x91, 0xF5,
+ 0x69, 0x43, 0xA5, 0x15, 0xE0, 0x30, 0x02, 0xC1, 0x02, 0x81,
+ 0x81, 0x00, 0x96, 0x36, 0xDC, 0x79, 0x56, 0x49, 0xCD, 0x1F,
+ 0x67, 0x9C, 0xF5, 0xBD, 0xDD, 0x6F, 0x21, 0xB8, 0xB1, 0x3F,
+ 0x3C, 0xA9, 0xFD, 0xEE, 0x99, 0x2F, 0x7A, 0xC6, 0x20, 0x37,
+ 0xAC, 0xE7, 0x66, 0xA5, 0xAD, 0x77, 0xD4, 0x1D, 0xB2, 0xF1,
+ 0xB2, 0x6D, 0x5B, 0x91, 0x15, 0x74, 0x25, 0x8C, 0xBF, 0x0B,
+ 0x7C, 0xB8, 0x8F, 0x96, 0xA2, 0xE2, 0x2B, 0x41, 0xE2, 0x90,
+ 0x97, 0x20, 0xB7, 0xF7, 0x1E, 0x27, 0xC3, 0x2A, 0xB9, 0x59,
+ 0xE0, 0x95, 0xA6, 0xEA, 0xD3, 0x25, 0x8A, 0xEE, 0x6C, 0x91,
+ 0xAA, 0xFA, 0x63, 0x83, 0xAC, 0x46, 0x3A, 0xE1, 0x34, 0x14,
+ 0xE8, 0xB4, 0xAC, 0x95, 0xAF, 0x26, 0xD6, 0x39, 0x7C, 0xC4,
+ 0xC7, 0xFF, 0xC7, 0xB3, 0x2E, 0x8B, 0x30, 0x20, 0x5E, 0x41,
+ 0xA7, 0x59, 0xEC, 0x6D, 0x0E, 0x86, 0x9B, 0x5E, 0xCA, 0x32,
+ 0x53, 0x1F, 0x92, 0xC8, 0xF5, 0x44, 0xB8, 0xB0, 0x51, 0x91,
+ 0x02, 0x81, 0x81, 0x00, 0xA8, 0xA5, 0x35, 0x70, 0x72, 0xEA,
+ 0x74, 0xC2, 0xEA, 0xEF, 0x36, 0xA3, 0x73, 0x14, 0x07, 0x92,
+ 0xB8, 0xA4, 0x70, 0x1C, 0x50, 0xDF, 0x7C, 0x01, 0xCD, 0x71,
+ 0x36, 0x34, 0xBB, 0x7D, 0x2A, 0xCB, 0x2B, 0xF5, 0x27, 0x22,
+ 0x7E, 0x16, 0xBE, 0x89, 0xCF, 0x7F, 0x63, 0xC3, 0xDD, 0xE1,
+ 0x10, 0xFA, 0x21, 0x1C, 0x61, 0x1E, 0x0D, 0x53, 0x12, 0xBC,
+ 0x74, 0xE4, 0xDE, 0x71, 0x62, 0x97, 0xE0, 0xEE, 0xFB, 0x3F,
+ 0xB2, 0x3F, 0xB3, 0x5B, 0x44, 0x7B, 0x28, 0x72, 0xD8, 0xB3,
+ 0x8E, 0x71, 0xA0, 0x4C, 0xE3, 0x0A, 0x64, 0x79, 0x7E, 0x3B,
+ 0xEF, 0x4F, 0x8B, 0x01, 0x45, 0x24, 0x26, 0x6F, 0xF6, 0xA2,
+ 0x86, 0xDB, 0xD9, 0xE4, 0x7C, 0xE3, 0xA4, 0xE7, 0xB1, 0x8F,
+ 0x44, 0xBB, 0xC8, 0x02, 0x28, 0xFE, 0x8A, 0x8C, 0xB0, 0x13,
+ 0x17, 0xB2, 0x68, 0x35, 0x98, 0xFE, 0x50, 0x18, 0xD0, 0x9B,
+ 0x05, 0xD2
+};
+#define sizeof_tsa_key_der_2048 (sizeof(tsa_key_der_2048))
+
+/* ./certs/tsa-cert.der, 2048-bit */
+static const unsigned char tsa_cert_der_2048[] =
+{
+ 0x30, 0x82, 0x04, 0xFA, 0x30, 0x82, 0x03, 0xE2, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x14, 0x27, 0xA1, 0xF0, 0xC9, 0x99,
+ 0x87, 0x1D, 0xE9, 0xCA, 0x22, 0x54, 0x48, 0x43, 0x86, 0xEF,
+ 0x5D, 0x31, 0x15, 0xF5, 0x5F, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x30, 0x81, 0x91, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E,
+ 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D,
+ 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C,
+ 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C,
+ 0x08, 0x54, 0x53, 0x41, 0x2D, 0x32, 0x30, 0x34, 0x38, 0x31,
+ 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F,
+ 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73,
+ 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01,
+ 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x1E,
+ 0x17, 0x0D, 0x32, 0x36, 0x30, 0x36, 0x30, 0x34, 0x32, 0x30,
+ 0x34, 0x34, 0x31, 0x30, 0x5A, 0x17, 0x0D, 0x32, 0x39, 0x30,
+ 0x32, 0x32, 0x38, 0x32, 0x30, 0x34, 0x34, 0x31, 0x30, 0x5A,
+ 0x30, 0x81, 0x91, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E,
+ 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D,
+ 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C,
+ 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C,
+ 0x08, 0x54, 0x53, 0x41, 0x2D, 0x32, 0x30, 0x34, 0x38, 0x31,
+ 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F,
+ 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73,
+ 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01,
+ 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x93, 0x74, 0x96, 0xF0, 0x0C, 0xFD, 0xD7, 0x2A, 0x5A,
+ 0xA0, 0xA1, 0x6E, 0xB4, 0x63, 0xB7, 0xF4, 0xE6, 0x8B, 0x77,
+ 0x8F, 0x49, 0xF7, 0xA9, 0x0C, 0xB3, 0x3D, 0x5A, 0x60, 0xA8,
+ 0xB8, 0x67, 0x75, 0xB8, 0xD0, 0xCE, 0xBA, 0xCE, 0x65, 0xE5,
+ 0x64, 0xA2, 0xD0, 0xC8, 0xD0, 0x2C, 0x5B, 0xA8, 0xA5, 0xD3,
+ 0x3F, 0x2C, 0x46, 0x07, 0xE6, 0xB1, 0xB7, 0xA5, 0x65, 0xF8,
+ 0x9F, 0x89, 0xE1, 0x93, 0xBC, 0x49, 0xCD, 0x55, 0xFD, 0x9B,
+ 0xA1, 0x1E, 0x1E, 0xF7, 0x99, 0x07, 0x4A, 0x1C, 0xC8, 0xB6,
+ 0x88, 0xA2, 0x5E, 0x77, 0xA5, 0x3F, 0x79, 0x2F, 0xD3, 0x43,
+ 0x41, 0xF6, 0xCF, 0xFF, 0x78, 0x30, 0x89, 0x12, 0xB1, 0x0C,
+ 0x3F, 0xF5, 0x2F, 0x62, 0xE6, 0x71, 0x33, 0x02, 0x70, 0xD9,
+ 0x42, 0x1B, 0xE4, 0x07, 0x9C, 0x9D, 0xC7, 0x5E, 0x5B, 0x4E,
+ 0x93, 0x8F, 0x6B, 0x0E, 0xFE, 0x5B, 0x7E, 0x02, 0x6F, 0x8D,
+ 0xE8, 0x7D, 0xE9, 0xAA, 0x1D, 0x12, 0xF2, 0x6B, 0xA4, 0x55,
+ 0x61, 0x59, 0xE0, 0x3A, 0x38, 0xB0, 0x14, 0x41, 0x9D, 0xBB,
+ 0xC6, 0xE0, 0x6A, 0x9D, 0x35, 0xA8, 0x08, 0xF2, 0x3C, 0x29,
+ 0xB9, 0xA7, 0xB2, 0x38, 0x47, 0xF6, 0x70, 0x1A, 0xB8, 0xD2,
+ 0xD5, 0xC9, 0x48, 0xF0, 0xEE, 0x60, 0x1D, 0x14, 0x77, 0x8A,
+ 0x2C, 0x38, 0x90, 0x29, 0x7F, 0x42, 0xE4, 0xDD, 0x92, 0x0A,
+ 0x8D, 0x03, 0x88, 0x44, 0x0A, 0xF0, 0xB6, 0x14, 0xBD, 0xE5,
+ 0x11, 0x50, 0x94, 0x26, 0xFB, 0x3E, 0xAB, 0x17, 0x69, 0x3D,
+ 0xA6, 0x45, 0xC7, 0xAD, 0x86, 0x41, 0x66, 0xE6, 0x53, 0xE5,
+ 0xDC, 0xD8, 0x4A, 0x04, 0xC7, 0xAF, 0xFB, 0xA7, 0xDF, 0x99,
+ 0x77, 0x57, 0xD9, 0x25, 0x7C, 0x67, 0x73, 0x3D, 0xEE, 0x60,
+ 0xF3, 0x63, 0x2D, 0x68, 0x0F, 0x97, 0xDA, 0x68, 0xD5, 0x3D,
+ 0xE5, 0x06, 0x92, 0x82, 0xDD, 0x90, 0x25, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xA3, 0x82, 0x01, 0x46, 0x30, 0x82, 0x01, 0x42,
+ 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04,
+ 0x14, 0xC0, 0x19, 0x81, 0xBB, 0xD6, 0x3D, 0xBB, 0x41, 0xE8,
+ 0x22, 0x1A, 0x08, 0x50, 0xAD, 0x57, 0xFB, 0xE5, 0xB5, 0xEA,
+ 0x9D, 0x30, 0x81, 0xD1, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04,
+ 0x81, 0xC9, 0x30, 0x81, 0xC6, 0x80, 0x14, 0xC0, 0x19, 0x81,
+ 0xBB, 0xD6, 0x3D, 0xBB, 0x41, 0xE8, 0x22, 0x1A, 0x08, 0x50,
+ 0xAD, 0x57, 0xFB, 0xE5, 0xB5, 0xEA, 0x9D, 0xA1, 0x81, 0x97,
+ 0xA4, 0x81, 0x94, 0x30, 0x81, 0x91, 0x31, 0x0B, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07,
+ 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F,
+ 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06,
+ 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66,
+ 0x53, 0x53, 0x4C, 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03, 0x55,
+ 0x04, 0x0B, 0x0C, 0x08, 0x54, 0x53, 0x41, 0x2D, 0x32, 0x30,
+ 0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F,
+ 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+ 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40,
+ 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F,
+ 0x6D, 0x82, 0x14, 0x27, 0xA1, 0xF0, 0xC9, 0x99, 0x87, 0x1D,
+ 0xE9, 0xCA, 0x22, 0x54, 0x48, 0x43, 0x86, 0xEF, 0x5D, 0x31,
+ 0x15, 0xF5, 0x5F, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1D, 0x13,
+ 0x04, 0x02, 0x30, 0x00, 0x30, 0x1A, 0x06, 0x03, 0x55, 0x1D,
+ 0x11, 0x04, 0x13, 0x30, 0x11, 0x82, 0x0F, 0x74, 0x73, 0x61,
+ 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63,
+ 0x6F, 0x6D, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01,
+ 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x16,
+ 0x06, 0x03, 0x55, 0x1D, 0x25, 0x01, 0x01, 0xFF, 0x04, 0x0C,
+ 0x30, 0x0A, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07,
+ 0x03, 0x08, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x01, 0x00, 0x1A, 0xC2, 0xD0, 0x93, 0xE8, 0x65, 0xF7, 0x77,
+ 0x2C, 0x62, 0xB2, 0x9C, 0xE0, 0x64, 0xEC, 0x18, 0x96, 0x5E,
+ 0x89, 0xBA, 0x58, 0x25, 0x12, 0xA9, 0xF7, 0x27, 0xDA, 0x70,
+ 0x4A, 0xB7, 0x6D, 0x0E, 0x86, 0x6D, 0x61, 0x5C, 0x73, 0x9E,
+ 0x55, 0x46, 0xF1, 0x6E, 0xF4, 0x06, 0xB2, 0x08, 0xE5, 0x8F,
+ 0x93, 0xBF, 0xAB, 0xB4, 0x60, 0xDD, 0x95, 0xD5, 0x89, 0xC4,
+ 0x16, 0xFA, 0xC4, 0xC6, 0xE5, 0x1F, 0x31, 0x4A, 0x75, 0xCB,
+ 0x11, 0xF5, 0x16, 0x5C, 0xC9, 0x19, 0x8B, 0xBC, 0x80, 0x2A,
+ 0x61, 0xC0, 0x12, 0x8E, 0x71, 0x59, 0xBA, 0x1E, 0x5A, 0xC6,
+ 0x6B, 0x62, 0xE7, 0xF0, 0xA9, 0xF2, 0xCD, 0x81, 0x2D, 0x41,
+ 0x37, 0x13, 0x59, 0x3A, 0x6F, 0x63, 0x09, 0xB6, 0x5E, 0x78,
+ 0x80, 0xD3, 0x18, 0x72, 0x89, 0x90, 0x51, 0xFA, 0x6F, 0x1D,
+ 0x99, 0x43, 0xFD, 0x5F, 0x17, 0x28, 0x19, 0x0B, 0x48, 0x14,
+ 0x87, 0x46, 0x09, 0x29, 0xE7, 0xD3, 0x79, 0x04, 0x7E, 0x46,
+ 0xAF, 0x30, 0xEB, 0xE9, 0x89, 0x0C, 0x7F, 0x26, 0xB3, 0xC4,
+ 0x66, 0xC7, 0x37, 0x9A, 0x8B, 0x42, 0xBB, 0x98, 0x21, 0x73,
+ 0x1A, 0xFE, 0x22, 0x2D, 0x80, 0xF6, 0x78, 0x8C, 0xD4, 0x80,
+ 0xA8, 0x5C, 0xB5, 0xCB, 0xE6, 0xA8, 0xB7, 0xFB, 0x98, 0xCD,
+ 0x5D, 0xD1, 0xE4, 0x37, 0xBE, 0x36, 0xCF, 0x97, 0xC1, 0xD0,
+ 0xA9, 0xF2, 0x64, 0x90, 0xDC, 0xB8, 0x57, 0x44, 0xC2, 0xE8,
+ 0x95, 0x50, 0x18, 0x1D, 0x0D, 0x8A, 0xEA, 0x9F, 0x12, 0x0B,
+ 0xE4, 0x1E, 0x78, 0x9E, 0xF5, 0x88, 0x5B, 0x22, 0x00, 0xA8,
+ 0x06, 0xAA, 0xAF, 0x1A, 0x2B, 0x0C, 0x3A, 0x2C, 0xF1, 0x0A,
+ 0xCD, 0x0A, 0xAF, 0x0E, 0x86, 0xE5, 0x5C, 0x34, 0xE7, 0xC7,
+ 0x64, 0x48, 0x39, 0x9B, 0xC4, 0x60, 0x40, 0x41, 0x2F, 0xBD,
+ 0x65, 0x20, 0x66, 0x54, 0xF2, 0x37, 0x5C, 0x7B
+};
+#define sizeof_tsa_cert_der_2048 (sizeof(tsa_cert_der_2048))
+
+/* ./certs/tsa-bad-ku-cert.der, 2048-bit */
+static const unsigned char tsa_bad_ku_cert_der_2048[] =
+{
+ 0x30, 0x82, 0x04, 0xF3, 0x30, 0x82, 0x03, 0xDB, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x14, 0x7D, 0xEF, 0xE7, 0xFE, 0x70,
+ 0x74, 0x73, 0xAF, 0xDF, 0x71, 0x6B, 0xD3, 0xBA, 0xFB, 0xD0,
+ 0x4B, 0xA3, 0x50, 0x26, 0xD3, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x30, 0x81, 0x98, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E,
+ 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D,
+ 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C,
+ 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C,
+ 0x0F, 0x54, 0x53, 0x41, 0x2D, 0x62, 0x61, 0x64, 0x2D, 0x6B,
+ 0x75, 0x2D, 0x32, 0x30, 0x34, 0x38, 0x31, 0x18, 0x30, 0x16,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77,
+ 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63,
+ 0x6F, 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86,
+ 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69,
+ 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73,
+ 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x32,
+ 0x36, 0x30, 0x36, 0x30, 0x34, 0x32, 0x30, 0x33, 0x35, 0x33,
+ 0x37, 0x5A, 0x17, 0x0D, 0x32, 0x39, 0x30, 0x32, 0x32, 0x38,
+ 0x32, 0x30, 0x33, 0x35, 0x33, 0x37, 0x5A, 0x30, 0x81, 0x98,
+ 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E,
+ 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07,
+ 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x18, 0x30,
+ 0x16, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x0F, 0x54, 0x53,
+ 0x41, 0x2D, 0x62, 0x61, 0x64, 0x2D, 0x6B, 0x75, 0x2D, 0x32,
+ 0x30, 0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F,
+ 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31,
+ 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F,
+ 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63,
+ 0x6F, 0x6D, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09,
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0x93, 0x74, 0x96, 0xF0, 0x0C,
+ 0xFD, 0xD7, 0x2A, 0x5A, 0xA0, 0xA1, 0x6E, 0xB4, 0x63, 0xB7,
+ 0xF4, 0xE6, 0x8B, 0x77, 0x8F, 0x49, 0xF7, 0xA9, 0x0C, 0xB3,
+ 0x3D, 0x5A, 0x60, 0xA8, 0xB8, 0x67, 0x75, 0xB8, 0xD0, 0xCE,
+ 0xBA, 0xCE, 0x65, 0xE5, 0x64, 0xA2, 0xD0, 0xC8, 0xD0, 0x2C,
+ 0x5B, 0xA8, 0xA5, 0xD3, 0x3F, 0x2C, 0x46, 0x07, 0xE6, 0xB1,
+ 0xB7, 0xA5, 0x65, 0xF8, 0x9F, 0x89, 0xE1, 0x93, 0xBC, 0x49,
+ 0xCD, 0x55, 0xFD, 0x9B, 0xA1, 0x1E, 0x1E, 0xF7, 0x99, 0x07,
+ 0x4A, 0x1C, 0xC8, 0xB6, 0x88, 0xA2, 0x5E, 0x77, 0xA5, 0x3F,
+ 0x79, 0x2F, 0xD3, 0x43, 0x41, 0xF6, 0xCF, 0xFF, 0x78, 0x30,
+ 0x89, 0x12, 0xB1, 0x0C, 0x3F, 0xF5, 0x2F, 0x62, 0xE6, 0x71,
+ 0x33, 0x02, 0x70, 0xD9, 0x42, 0x1B, 0xE4, 0x07, 0x9C, 0x9D,
+ 0xC7, 0x5E, 0x5B, 0x4E, 0x93, 0x8F, 0x6B, 0x0E, 0xFE, 0x5B,
+ 0x7E, 0x02, 0x6F, 0x8D, 0xE8, 0x7D, 0xE9, 0xAA, 0x1D, 0x12,
+ 0xF2, 0x6B, 0xA4, 0x55, 0x61, 0x59, 0xE0, 0x3A, 0x38, 0xB0,
+ 0x14, 0x41, 0x9D, 0xBB, 0xC6, 0xE0, 0x6A, 0x9D, 0x35, 0xA8,
+ 0x08, 0xF2, 0x3C, 0x29, 0xB9, 0xA7, 0xB2, 0x38, 0x47, 0xF6,
+ 0x70, 0x1A, 0xB8, 0xD2, 0xD5, 0xC9, 0x48, 0xF0, 0xEE, 0x60,
+ 0x1D, 0x14, 0x77, 0x8A, 0x2C, 0x38, 0x90, 0x29, 0x7F, 0x42,
+ 0xE4, 0xDD, 0x92, 0x0A, 0x8D, 0x03, 0x88, 0x44, 0x0A, 0xF0,
+ 0xB6, 0x14, 0xBD, 0xE5, 0x11, 0x50, 0x94, 0x26, 0xFB, 0x3E,
+ 0xAB, 0x17, 0x69, 0x3D, 0xA6, 0x45, 0xC7, 0xAD, 0x86, 0x41,
+ 0x66, 0xE6, 0x53, 0xE5, 0xDC, 0xD8, 0x4A, 0x04, 0xC7, 0xAF,
+ 0xFB, 0xA7, 0xDF, 0x99, 0x77, 0x57, 0xD9, 0x25, 0x7C, 0x67,
+ 0x73, 0x3D, 0xEE, 0x60, 0xF3, 0x63, 0x2D, 0x68, 0x0F, 0x97,
+ 0xDA, 0x68, 0xD5, 0x3D, 0xE5, 0x06, 0x92, 0x82, 0xDD, 0x90,
+ 0x25, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x82, 0x01, 0x31,
+ 0x30, 0x82, 0x01, 0x2D, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D,
+ 0x0E, 0x04, 0x16, 0x04, 0x14, 0xC0, 0x19, 0x81, 0xBB, 0xD6,
+ 0x3D, 0xBB, 0x41, 0xE8, 0x22, 0x1A, 0x08, 0x50, 0xAD, 0x57,
+ 0xFB, 0xE5, 0xB5, 0xEA, 0x9D, 0x30, 0x81, 0xD8, 0x06, 0x03,
+ 0x55, 0x1D, 0x23, 0x04, 0x81, 0xD0, 0x30, 0x81, 0xCD, 0x80,
+ 0x14, 0xC0, 0x19, 0x81, 0xBB, 0xD6, 0x3D, 0xBB, 0x41, 0xE8,
+ 0x22, 0x1A, 0x08, 0x50, 0xAD, 0x57, 0xFB, 0xE5, 0xB5, 0xEA,
+ 0x9D, 0xA1, 0x81, 0x9E, 0xA4, 0x81, 0x9B, 0x30, 0x81, 0x98,
+ 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E,
+ 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07,
+ 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x18, 0x30,
+ 0x16, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x0F, 0x54, 0x53,
+ 0x41, 0x2D, 0x62, 0x61, 0x64, 0x2D, 0x6B, 0x75, 0x2D, 0x32,
+ 0x30, 0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F,
+ 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31,
+ 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F,
+ 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63,
+ 0x6F, 0x6D, 0x82, 0x14, 0x7D, 0xEF, 0xE7, 0xFE, 0x70, 0x74,
+ 0x73, 0xAF, 0xDF, 0x71, 0x6B, 0xD3, 0xBA, 0xFB, 0xD0, 0x4B,
+ 0xA3, 0x50, 0x26, 0xD3, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1D,
+ 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0E, 0x06, 0x03, 0x55,
+ 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x05,
+ 0x20, 0x30, 0x16, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x01, 0x01,
+ 0xFF, 0x04, 0x0C, 0x30, 0x0A, 0x06, 0x08, 0x2B, 0x06, 0x01,
+ 0x05, 0x05, 0x07, 0x03, 0x08, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x1C, 0xB5, 0x98, 0x0A, 0x98,
+ 0x28, 0x90, 0x9F, 0x7C, 0xFF, 0xD9, 0xD4, 0x3B, 0x92, 0x3D,
+ 0xB3, 0x7C, 0xD6, 0x9B, 0xB4, 0x88, 0x59, 0x0D, 0xCD, 0x58,
+ 0x7A, 0x6A, 0xAD, 0xF0, 0x6F, 0xF3, 0xED, 0x5E, 0x57, 0xD7,
+ 0x14, 0x3E, 0x64, 0x50, 0x1C, 0x11, 0x33, 0xAB, 0x62, 0x09,
+ 0xF1, 0x8F, 0x58, 0x15, 0x93, 0x25, 0x20, 0xED, 0xF3, 0xCB,
+ 0xED, 0xA3, 0xD2, 0x21, 0x7C, 0xC2, 0x02, 0x14, 0xFA, 0x61,
+ 0xCF, 0x20, 0x13, 0x8A, 0x6B, 0xF1, 0x95, 0x81, 0x94, 0xE8,
+ 0xD7, 0xFD, 0x24, 0x13, 0xFB, 0x87, 0xE3, 0x2C, 0xFB, 0x4E,
+ 0xD7, 0xCE, 0x46, 0xAB, 0xFD, 0x21, 0xBC, 0x93, 0xB8, 0x0D,
+ 0x88, 0x2A, 0x76, 0xB6, 0x02, 0xF9, 0xEF, 0x58, 0xFC, 0x36,
+ 0xB5, 0x11, 0x5A, 0x07, 0x62, 0x0D, 0xA3, 0x1A, 0xE6, 0x77,
+ 0xA7, 0x28, 0xB2, 0x0E, 0xC6, 0xB1, 0xA4, 0x66, 0x52, 0x99,
+ 0x11, 0x90, 0x11, 0x4A, 0xD7, 0x98, 0xE5, 0x9F, 0xB1, 0xE7,
+ 0x99, 0xFE, 0xA6, 0x66, 0x66, 0x5E, 0x1E, 0x52, 0xBB, 0xB8,
+ 0xE7, 0xBD, 0xE9, 0x95, 0xD4, 0x03, 0x47, 0xDE, 0xC7, 0xCD,
+ 0xF7, 0x58, 0x67, 0xAF, 0x12, 0x57, 0x28, 0x33, 0xA7, 0x34,
+ 0xEF, 0x74, 0x2F, 0x6C, 0x67, 0x43, 0x29, 0x6D, 0x57, 0x80,
+ 0xB4, 0x2D, 0x67, 0x21, 0x2A, 0x52, 0x41, 0x97, 0x1D, 0x2D,
+ 0xAF, 0x2C, 0xC7, 0x1F, 0xC8, 0x20, 0x6D, 0x9A, 0x79, 0x82,
+ 0xF7, 0xA6, 0x3B, 0x97, 0x5B, 0x1A, 0xBC, 0xF4, 0x2A, 0xD9,
+ 0xDF, 0xA6, 0x45, 0xDB, 0xA2, 0xC1, 0x83, 0x5A, 0x39, 0xEF,
+ 0xF9, 0x6F, 0xF7, 0x14, 0x42, 0x30, 0x0F, 0x52, 0x71, 0x6E,
+ 0x6B, 0x05, 0x19, 0xCA, 0x51, 0x4E, 0xA0, 0xF1, 0x4A, 0xBA,
+ 0x6F, 0x95, 0x46, 0x3D, 0xEA, 0x1A, 0xAB, 0xE3, 0x37, 0xBD,
+ 0x30, 0xBA, 0xB9, 0xB5, 0x30, 0xFD, 0x97, 0xE4, 0x7A, 0x49,
+ 0xD4
+};
+#define sizeof_tsa_bad_ku_cert_der_2048 (sizeof(tsa_bad_ku_cert_der_2048))
+
+static const unsigned char tsa_extra_eku_cert_der_2048[] =
+{
+ 0x30, 0x82, 0x05, 0x07, 0x30, 0x82, 0x03, 0xEF, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x14, 0x2E, 0x66, 0x3C, 0x01, 0x89,
+ 0x1B, 0x3A, 0x75, 0x8A, 0x86, 0x40, 0x99, 0xFD, 0xAF, 0xC6,
+ 0x7B, 0xB1, 0x24, 0x7F, 0x8E, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x30, 0x81, 0x9B, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E,
+ 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D,
+ 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C,
+ 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C,
+ 0x12, 0x54, 0x53, 0x41, 0x2D, 0x65, 0x78, 0x74, 0x72, 0x61,
+ 0x2D, 0x65, 0x6B, 0x75, 0x2D, 0x32, 0x30, 0x34, 0x38, 0x31,
+ 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F,
+ 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73,
+ 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01,
+ 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x1E,
+ 0x17, 0x0D, 0x32, 0x36, 0x30, 0x36, 0x31, 0x31, 0x31, 0x30,
+ 0x33, 0x38, 0x31, 0x34, 0x5A, 0x17, 0x0D, 0x32, 0x39, 0x30,
+ 0x33, 0x30, 0x37, 0x31, 0x30, 0x33, 0x38, 0x31, 0x34, 0x5A,
+ 0x30, 0x81, 0x9B, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E,
+ 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03,
+ 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D,
+ 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C,
+ 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C,
+ 0x12, 0x54, 0x53, 0x41, 0x2D, 0x65, 0x78, 0x74, 0x72, 0x61,
+ 0x2D, 0x65, 0x6B, 0x75, 0x2D, 0x32, 0x30, 0x34, 0x38, 0x31,
+ 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F,
+ 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73,
+ 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01,
+ 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0x93, 0x74, 0x96, 0xF0, 0x0C, 0xFD, 0xD7, 0x2A, 0x5A,
+ 0xA0, 0xA1, 0x6E, 0xB4, 0x63, 0xB7, 0xF4, 0xE6, 0x8B, 0x77,
+ 0x8F, 0x49, 0xF7, 0xA9, 0x0C, 0xB3, 0x3D, 0x5A, 0x60, 0xA8,
+ 0xB8, 0x67, 0x75, 0xB8, 0xD0, 0xCE, 0xBA, 0xCE, 0x65, 0xE5,
+ 0x64, 0xA2, 0xD0, 0xC8, 0xD0, 0x2C, 0x5B, 0xA8, 0xA5, 0xD3,
+ 0x3F, 0x2C, 0x46, 0x07, 0xE6, 0xB1, 0xB7, 0xA5, 0x65, 0xF8,
+ 0x9F, 0x89, 0xE1, 0x93, 0xBC, 0x49, 0xCD, 0x55, 0xFD, 0x9B,
+ 0xA1, 0x1E, 0x1E, 0xF7, 0x99, 0x07, 0x4A, 0x1C, 0xC8, 0xB6,
+ 0x88, 0xA2, 0x5E, 0x77, 0xA5, 0x3F, 0x79, 0x2F, 0xD3, 0x43,
+ 0x41, 0xF6, 0xCF, 0xFF, 0x78, 0x30, 0x89, 0x12, 0xB1, 0x0C,
+ 0x3F, 0xF5, 0x2F, 0x62, 0xE6, 0x71, 0x33, 0x02, 0x70, 0xD9,
+ 0x42, 0x1B, 0xE4, 0x07, 0x9C, 0x9D, 0xC7, 0x5E, 0x5B, 0x4E,
+ 0x93, 0x8F, 0x6B, 0x0E, 0xFE, 0x5B, 0x7E, 0x02, 0x6F, 0x8D,
+ 0xE8, 0x7D, 0xE9, 0xAA, 0x1D, 0x12, 0xF2, 0x6B, 0xA4, 0x55,
+ 0x61, 0x59, 0xE0, 0x3A, 0x38, 0xB0, 0x14, 0x41, 0x9D, 0xBB,
+ 0xC6, 0xE0, 0x6A, 0x9D, 0x35, 0xA8, 0x08, 0xF2, 0x3C, 0x29,
+ 0xB9, 0xA7, 0xB2, 0x38, 0x47, 0xF6, 0x70, 0x1A, 0xB8, 0xD2,
+ 0xD5, 0xC9, 0x48, 0xF0, 0xEE, 0x60, 0x1D, 0x14, 0x77, 0x8A,
+ 0x2C, 0x38, 0x90, 0x29, 0x7F, 0x42, 0xE4, 0xDD, 0x92, 0x0A,
+ 0x8D, 0x03, 0x88, 0x44, 0x0A, 0xF0, 0xB6, 0x14, 0xBD, 0xE5,
+ 0x11, 0x50, 0x94, 0x26, 0xFB, 0x3E, 0xAB, 0x17, 0x69, 0x3D,
+ 0xA6, 0x45, 0xC7, 0xAD, 0x86, 0x41, 0x66, 0xE6, 0x53, 0xE5,
+ 0xDC, 0xD8, 0x4A, 0x04, 0xC7, 0xAF, 0xFB, 0xA7, 0xDF, 0x99,
+ 0x77, 0x57, 0xD9, 0x25, 0x7C, 0x67, 0x73, 0x3D, 0xEE, 0x60,
+ 0xF3, 0x63, 0x2D, 0x68, 0x0F, 0x97, 0xDA, 0x68, 0xD5, 0x3D,
+ 0xE5, 0x06, 0x92, 0x82, 0xDD, 0x90, 0x25, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xA3, 0x82, 0x01, 0x3F, 0x30, 0x82, 0x01, 0x3B,
+ 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04,
+ 0x14, 0xC0, 0x19, 0x81, 0xBB, 0xD6, 0x3D, 0xBB, 0x41, 0xE8,
+ 0x22, 0x1A, 0x08, 0x50, 0xAD, 0x57, 0xFB, 0xE5, 0xB5, 0xEA,
+ 0x9D, 0x30, 0x81, 0xDB, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04,
+ 0x81, 0xD3, 0x30, 0x81, 0xD0, 0x80, 0x14, 0xC0, 0x19, 0x81,
+ 0xBB, 0xD6, 0x3D, 0xBB, 0x41, 0xE8, 0x22, 0x1A, 0x08, 0x50,
+ 0xAD, 0x57, 0xFB, 0xE5, 0xB5, 0xEA, 0x9D, 0xA1, 0x81, 0xA1,
+ 0xA4, 0x81, 0x9E, 0x30, 0x81, 0x9B, 0x31, 0x0B, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07,
+ 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F,
+ 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06,
+ 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66,
+ 0x53, 0x53, 0x4C, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55,
+ 0x04, 0x0B, 0x0C, 0x12, 0x54, 0x53, 0x41, 0x2D, 0x65, 0x78,
+ 0x74, 0x72, 0x61, 0x2D, 0x65, 0x6B, 0x75, 0x2D, 0x32, 0x30,
+ 0x34, 0x38, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04,
+ 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F,
+ 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D,
+ 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40,
+ 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F,
+ 0x6D, 0x82, 0x14, 0x2E, 0x66, 0x3C, 0x01, 0x89, 0x1B, 0x3A,
+ 0x75, 0x8A, 0x86, 0x40, 0x99, 0xFD, 0xAF, 0xC6, 0x7B, 0xB1,
+ 0x24, 0x7F, 0x8E, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1D, 0x13,
+ 0x04, 0x02, 0x30, 0x00, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D,
+ 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
+ 0x30, 0x21, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x01, 0x01, 0xFF,
+ 0x04, 0x17, 0x30, 0x15, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05,
+ 0x05, 0x07, 0x03, 0x08, 0x06, 0x09, 0x2B, 0x06, 0x01, 0x04,
+ 0x01, 0x86, 0x8D, 0x1F, 0x01, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x68, 0x67, 0xCB, 0x3B, 0x2A,
+ 0x55, 0xC2, 0x5F, 0xBE, 0x62, 0xC5, 0x21, 0x5E, 0xA8, 0xCD,
+ 0xD7, 0x74, 0x1B, 0x2E, 0x2F, 0x33, 0x76, 0x0F, 0x0D, 0xBB,
+ 0x11, 0x75, 0x1B, 0xBF, 0x1F, 0x05, 0xDA, 0x21, 0x6E, 0xA6,
+ 0x82, 0x26, 0xF2, 0x35, 0x5C, 0xAC, 0x6D, 0x04, 0x86, 0xB6,
+ 0x99, 0xE9, 0xC5, 0xA0, 0x23, 0x48, 0xAF, 0x14, 0xAE, 0xBC,
+ 0xAE, 0x8F, 0x84, 0x41, 0xA8, 0x14, 0xA3, 0xFC, 0x69, 0x39,
+ 0x9F, 0x50, 0x4E, 0x6E, 0xB9, 0x81, 0xF4, 0xD8, 0xCC, 0xDF,
+ 0xA2, 0xF9, 0xBA, 0x0B, 0x79, 0x67, 0x7A, 0x8F, 0x2E, 0x75,
+ 0x1F, 0x7F, 0xF2, 0x57, 0x74, 0x4A, 0x4C, 0xBD, 0xDB, 0x1C,
+ 0x16, 0xFA, 0xE9, 0x38, 0xC2, 0x7A, 0x8D, 0x64, 0xEA, 0xC2,
+ 0xC3, 0x34, 0x93, 0x54, 0x73, 0x07, 0x8E, 0xCC, 0x95, 0x44,
+ 0x05, 0x9A, 0xDD, 0xA1, 0xAE, 0xB3, 0x94, 0x11, 0x12, 0xB6,
+ 0x1F, 0x47, 0xDD, 0xA7, 0x1C, 0xFF, 0x6D, 0x06, 0xB3, 0xAA,
+ 0xD5, 0xDA, 0xDD, 0x54, 0xA7, 0xE8, 0xC1, 0x87, 0x6F, 0x37,
+ 0xEE, 0xEF, 0xA3, 0x05, 0xEB, 0x08, 0x10, 0x32, 0x7F, 0xAD,
+ 0x02, 0x2F, 0xAA, 0xCA, 0xE3, 0x80, 0x06, 0xBB, 0x2C, 0xC3,
+ 0xDF, 0xC8, 0x99, 0xFC, 0xC1, 0xB5, 0x21, 0xF8, 0x85, 0x42,
+ 0x90, 0x20, 0x2D, 0x06, 0x33, 0xFD, 0xC1, 0xD1, 0x57, 0x8A,
+ 0xE8, 0x82, 0xD1, 0xDD, 0xC2, 0x3E, 0x78, 0xFA, 0x62, 0xDA,
+ 0x15, 0x5F, 0x4C, 0x04, 0x36, 0x06, 0xE5, 0x76, 0x22, 0x9C,
+ 0xE6, 0xB0, 0xB8, 0xB9, 0x2C, 0x70, 0xAA, 0x16, 0xB4, 0xB4,
+ 0x20, 0x43, 0xD9, 0x0D, 0x4D, 0x16, 0x81, 0x55, 0xB3, 0x26,
+ 0xB1, 0x32, 0x99, 0x2B, 0x27, 0xA6, 0xC7, 0x44, 0x24, 0xB8,
+ 0x15, 0x73, 0x2B, 0xE1, 0xFA, 0xB0, 0x26, 0xDC, 0xE6, 0x70,
+ 0xEC, 0x12, 0x00, 0x5D, 0x0F, 0x1F, 0x2D, 0x2F, 0x50, 0x26,
+ 0x3F
+};
+#define sizeof_tsa_extra_eku_cert_der_2048 (sizeof(tsa_extra_eku_cert_der_2048))
+
+/* ./certs/intermediate/ca-int-cert.der, 2048-bit */
+static const unsigned char ca_int_cert_der_2048[] =
+{
+ 0x30, 0x82, 0x04, 0x17, 0x30, 0x82, 0x02, 0xFF, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x02, 0x10, 0x00, 0x30, 0x0D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+ 0x05, 0x00, 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10,
+ 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07, 0x4D,
+ 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F, 0x7A,
+ 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x11, 0x30, 0x0F, 0x06, 0x03,
+ 0x55, 0x04, 0x0A, 0x0C, 0x08, 0x53, 0x61, 0x77, 0x74, 0x6F,
+ 0x6F, 0x74, 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+ 0x04, 0x0B, 0x0C, 0x0A, 0x43, 0x6F, 0x6E, 0x73, 0x75, 0x6C,
+ 0x74, 0x69, 0x6E, 0x67, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77,
+ 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D,
+ 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66,
+ 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E,
+ 0x63, 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x32, 0x35, 0x30,
+ 0x36, 0x32, 0x35, 0x31, 0x35, 0x35, 0x36, 0x32, 0x32, 0x5A,
+ 0x17, 0x0D, 0x34, 0x35, 0x30, 0x36, 0x32, 0x30, 0x31, 0x35,
+ 0x35, 0x36, 0x32, 0x32, 0x5A, 0x30, 0x81, 0x9F, 0x31, 0x0B,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x0C, 0x0A, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6E, 0x67, 0x74,
+ 0x6F, 0x6E, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x07, 0x0C, 0x07, 0x53, 0x65, 0x61, 0x74, 0x74, 0x6C, 0x65,
+ 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C,
+ 0x07, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x14,
+ 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x0B, 0x44,
+ 0x65, 0x76, 0x65, 0x6C, 0x6F, 0x70, 0x6D, 0x65, 0x6E, 0x74,
+ 0x31, 0x20, 0x30, 0x1E, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C,
+ 0x17, 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x20, 0x49,
+ 0x6E, 0x74, 0x65, 0x72, 0x6D, 0x65, 0x64, 0x69, 0x61, 0x74,
+ 0x65, 0x20, 0x43, 0x41, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09,
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16,
+ 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66,
+ 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x82, 0x01,
+ 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F,
+ 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xC3, 0xA2, 0x73, 0x5D, 0x21, 0x62, 0x20, 0xCE, 0x3A, 0x71,
+ 0x38, 0xA7, 0x94, 0xBB, 0xDB, 0x87, 0x04, 0x1C, 0x5A, 0x1B,
+ 0x9E, 0x4B, 0x0D, 0x3E, 0xCA, 0xF8, 0xA5, 0xF7, 0x0D, 0x6A,
+ 0xDC, 0x23, 0x90, 0x22, 0x6A, 0x2B, 0x58, 0x63, 0x4A, 0x28,
+ 0x6A, 0x48, 0xA8, 0xE7, 0x73, 0x1F, 0xA2, 0x55, 0xD8, 0x4D,
+ 0x02, 0x3B, 0xE2, 0xCB, 0x6B, 0xE2, 0x83, 0xC9, 0x51, 0x8F,
+ 0x77, 0xFD, 0xDC, 0x2D, 0x5D, 0x23, 0xB7, 0x23, 0x9A, 0x7E,
+ 0xB6, 0x29, 0x68, 0xE8, 0x2A, 0x4E, 0xA9, 0xFE, 0x32, 0x70,
+ 0x31, 0x9E, 0xF0, 0xEF, 0xEE, 0xF8, 0x8D, 0xE3, 0xFC, 0xF3,
+ 0xD7, 0x28, 0xDD, 0x7A, 0x1D, 0x9E, 0xAD, 0x23, 0x2B, 0xF1,
+ 0xA6, 0x7F, 0x34, 0x52, 0x29, 0x66, 0xD2, 0xE5, 0x64, 0x55,
+ 0x64, 0xD6, 0xDD, 0x4B, 0x41, 0x3B, 0x55, 0x83, 0x6E, 0xC0,
+ 0x11, 0x0E, 0x6E, 0x20, 0xC2, 0x16, 0x73, 0xEB, 0x30, 0xFF,
+ 0x09, 0x46, 0xBB, 0xE7, 0xCC, 0xC6, 0x03, 0x44, 0x41, 0x11,
+ 0xC6, 0xC1, 0x6C, 0x36, 0x2F, 0x4A, 0xF9, 0x91, 0x55, 0xCA,
+ 0x58, 0x5E, 0x37, 0xB8, 0x28, 0x10, 0x30, 0x89, 0x40, 0x96,
+ 0x77, 0xCF, 0x70, 0x66, 0xA4, 0x55, 0xFB, 0x69, 0x0B, 0xE7,
+ 0xD9, 0xB2, 0x33, 0x65, 0xDB, 0x72, 0x3A, 0x77, 0xB7, 0x2B,
+ 0x49, 0xFC, 0xB6, 0xCD, 0x58, 0x10, 0x8D, 0xAB, 0xAA, 0xCB,
+ 0x40, 0x45, 0x77, 0x02, 0x39, 0x18, 0xB3, 0x8F, 0x33, 0x01,
+ 0x48, 0x77, 0x50, 0xBE, 0x8E, 0x73, 0xA7, 0xDE, 0x36, 0xA0,
+ 0x49, 0x8E, 0x2C, 0x16, 0xAF, 0xB9, 0xFB, 0x42, 0x2D, 0x35,
+ 0x6A, 0xDB, 0x34, 0x37, 0xD5, 0x14, 0x59, 0x7D, 0x65, 0x72,
+ 0xE5, 0x8B, 0x65, 0x55, 0x4B, 0x20, 0x5E, 0x47, 0xF9, 0xF8,
+ 0x3A, 0xD3, 0x6C, 0xD9, 0x3A, 0xF5, 0xC7, 0x01, 0x46, 0x31,
+ 0xC3, 0x79, 0x9A, 0x18, 0xBE, 0x49, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xA3, 0x66, 0x30, 0x64, 0x30, 0x1D, 0x06, 0x03, 0x55,
+ 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xEF, 0x69, 0xE0, 0xF7,
+ 0xD5, 0x1D, 0xE6, 0x99, 0xEC, 0xDC, 0x6D, 0xD0, 0xF7, 0xE2,
+ 0xB9, 0x5C, 0x64, 0x71, 0x83, 0x35, 0x30, 0x1F, 0x06, 0x03,
+ 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x27,
+ 0x8E, 0x67, 0x11, 0x74, 0xC3, 0x26, 0x1D, 0x3F, 0xED, 0x33,
+ 0x63, 0xB3, 0xA4, 0xD8, 0x1D, 0x30, 0xE5, 0xE8, 0xD5, 0x30,
+ 0x12, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x01, 0x01, 0xFF, 0x04,
+ 0x08, 0x30, 0x06, 0x01, 0x01, 0xFF, 0x02, 0x01, 0x01, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04,
+ 0x04, 0x03, 0x02, 0x01, 0x86, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x03, 0x82, 0x01, 0x01, 0x00, 0x59, 0xDD, 0xC9, 0x25, 0x71,
+ 0x0B, 0xCD, 0x07, 0x26, 0x99, 0x78, 0x8C, 0x8B, 0x3A, 0xF2,
+ 0x78, 0x20, 0x8B, 0x01, 0x14, 0x29, 0x69, 0x53, 0x92, 0x4F,
+ 0x5F, 0x8D, 0xE0, 0xCE, 0xFA, 0xAF, 0x11, 0x44, 0x18, 0x25,
+ 0x3A, 0x93, 0x07, 0xB2, 0xB5, 0x17, 0xF4, 0x4E, 0xFF, 0xF1,
+ 0x59, 0xB8, 0x45, 0xEF, 0xE0, 0x2C, 0x81, 0x3D, 0xFA, 0xC5,
+ 0x3E, 0x90, 0x84, 0xB7, 0xFC, 0x41, 0xB0, 0x6C, 0x7D, 0xB8,
+ 0x27, 0x75, 0x3B, 0x60, 0x82, 0xFF, 0x68, 0x72, 0x1F, 0x5A,
+ 0x43, 0xD1, 0xE7, 0x92, 0x8B, 0x66, 0xCD, 0xDD, 0xFE, 0x40,
+ 0x39, 0xDE, 0xB5, 0x83, 0xD1, 0x6C, 0x55, 0x4C, 0xEC, 0xB8,
+ 0xCE, 0x64, 0x4A, 0x31, 0xA0, 0xA3, 0xB8, 0x5C, 0xAF, 0xBD,
+ 0xEA, 0xA6, 0x0F, 0xDA, 0xAB, 0xE8, 0x26, 0x3B, 0xDD, 0xDB,
+ 0x1D, 0xF2, 0x67, 0x8C, 0x04, 0x93, 0xB4, 0xAF, 0x27, 0xC1,
+ 0x82, 0x5E, 0xDE, 0x98, 0x37, 0x16, 0x1C, 0x66, 0x4B, 0xC5,
+ 0x7F, 0x3A, 0x00, 0x44, 0xD3, 0xA8, 0x2D, 0x6B, 0x0C, 0x0C,
+ 0x02, 0xB5, 0xB5, 0x5E, 0x24, 0x25, 0xCF, 0xEB, 0xC9, 0x54,
+ 0xDD, 0xEB, 0x9E, 0xD3, 0x07, 0x69, 0x76, 0xBF, 0x34, 0x2E,
+ 0xEB, 0x67, 0x57, 0x08, 0x1E, 0x1A, 0xFD, 0x5C, 0x90, 0xA2,
+ 0x0C, 0x78, 0x6C, 0x07, 0xA6, 0x11, 0xA5, 0x52, 0xEC, 0xED,
+ 0xE5, 0x7D, 0x4A, 0x1F, 0xFD, 0x98, 0x5A, 0xE3, 0x95, 0x85,
+ 0x11, 0xA5, 0x85, 0x31, 0xD1, 0x77, 0x25, 0x02, 0x2F, 0x5E,
+ 0x46, 0xF7, 0xD8, 0x78, 0xFB, 0x2D, 0x5A, 0xF9, 0x8E, 0x16,
+ 0xEC, 0x53, 0x9C, 0xDB, 0x1A, 0x92, 0x31, 0xB1, 0x4A, 0xBE,
+ 0x10, 0x74, 0xBD, 0xEB, 0x9E, 0xC3, 0xBF, 0x73, 0xE0, 0x9C,
+ 0x86, 0x3A, 0xED, 0x1E, 0xEA, 0x3E, 0xE8, 0x99, 0x93, 0xDA,
+ 0x3C, 0xD7, 0xEF, 0x73, 0x49, 0xC3, 0x75, 0xFD, 0x3D, 0x1B,
+ 0x6D
+};
+#define sizeof_ca_int_cert_der_2048 (sizeof(ca_int_cert_der_2048))
+
+/* ./certs/tsa-chain-key.der, 2048-bit */
+static const unsigned char tsa_chain_key_der_2048[] =
+{
+ 0x30, 0x82, 0x04, 0xA4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xB2, 0xBB, 0x66, 0xAC, 0x49, 0xBB, 0x8C, 0x5D,
+ 0xE8, 0xEE, 0xFF, 0x55, 0x12, 0xB9, 0x6E, 0xB0, 0xE8, 0x9A,
+ 0x77, 0x35, 0x7A, 0xB8, 0x65, 0x85, 0x62, 0xA6, 0x17, 0x1B,
+ 0x27, 0x25, 0x8C, 0x59, 0xBB, 0x1E, 0xE6, 0xBB, 0xAE, 0x09,
+ 0x55, 0x7D, 0xEE, 0x53, 0xFD, 0xFF, 0x56, 0x99, 0x9D, 0x0C,
+ 0x1E, 0x31, 0x48, 0xBD, 0x42, 0x69, 0x2C, 0xB2, 0x0A, 0x37,
+ 0x68, 0x5B, 0x8F, 0x33, 0xFF, 0xC5, 0xA0, 0xE0, 0xE2, 0x1E,
+ 0x6C, 0xF6, 0x4F, 0x90, 0xB4, 0x73, 0x3D, 0x13, 0xFE, 0xE4,
+ 0xED, 0xB0, 0x1F, 0xB8, 0x5E, 0xF0, 0xCE, 0x4D, 0x51, 0x0D,
+ 0xE6, 0xBE, 0x2B, 0x7D, 0xA4, 0x44, 0x6B, 0x38, 0x75, 0x83,
+ 0x13, 0x0D, 0x41, 0xD2, 0x69, 0x3F, 0x29, 0x7C, 0xF1, 0x57,
+ 0x29, 0x52, 0xF3, 0xD1, 0x2C, 0x40, 0x0E, 0xC7, 0xBE, 0x64,
+ 0x73, 0xD4, 0x9E, 0x06, 0xA4, 0x67, 0x50, 0x57, 0x03, 0xEF,
+ 0x8E, 0x6A, 0xC9, 0x06, 0x8A, 0xE3, 0xC3, 0xC3, 0xEB, 0xCB,
+ 0x6F, 0x0F, 0x3A, 0x26, 0xB9, 0xEE, 0x20, 0x28, 0x30, 0x47,
+ 0x5C, 0xEC, 0x57, 0x98, 0x18, 0x01, 0xF1, 0xD7, 0x91, 0xCA,
+ 0x15, 0x67, 0x11, 0x5E, 0xE7, 0x03, 0x72, 0x87, 0x19, 0x52,
+ 0xBF, 0xA1, 0x41, 0x47, 0x34, 0x94, 0x98, 0x88, 0x58, 0xFC,
+ 0xCA, 0x41, 0xD5, 0x71, 0x7E, 0x75, 0xB5, 0xE4, 0x95, 0x4D,
+ 0x5E, 0xBE, 0x7E, 0x5C, 0x6E, 0x4E, 0x48, 0x7A, 0xA2, 0xCD,
+ 0x14, 0x0A, 0xBB, 0xFA, 0xF8, 0x70, 0x8C, 0x32, 0x9D, 0x29,
+ 0x7A, 0x77, 0xAB, 0x55, 0x7E, 0xA8, 0x5F, 0xB8, 0x2D, 0x8E,
+ 0x62, 0x5D, 0x51, 0x2E, 0x8E, 0x17, 0xA5, 0x6E, 0x38, 0xC1,
+ 0x05, 0x87, 0x74, 0xC1, 0x55, 0x6E, 0x4F, 0x8A, 0x4A, 0x51,
+ 0xF6, 0x6C, 0xD8, 0xAC, 0xCF, 0xC0, 0x8E, 0xFA, 0x4B, 0x5C,
+ 0x74, 0x16, 0x39, 0x18, 0x1A, 0x29, 0x75, 0x67, 0x02, 0x03,
+ 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x54, 0xE2, 0x45,
+ 0xB7, 0xEB, 0x68, 0xA4, 0x18, 0x71, 0xA9, 0x18, 0x20, 0xBA,
+ 0x3C, 0xD1, 0x02, 0x39, 0xE6, 0x2A, 0x59, 0x7E, 0xC8, 0x16,
+ 0x87, 0x0B, 0xBB, 0xDF, 0xDB, 0x68, 0x73, 0x1F, 0xBD, 0xF9,
+ 0xED, 0x8A, 0x1D, 0x76, 0x61, 0x3E, 0x76, 0x09, 0x7B, 0x60,
+ 0x75, 0x25, 0x16, 0xDD, 0x8C, 0x44, 0xC6, 0x99, 0x4A, 0x31,
+ 0x41, 0x2C, 0x15, 0xFE, 0x5E, 0x24, 0x34, 0xDF, 0xC5, 0x0D,
+ 0x63, 0x39, 0xAD, 0xB4, 0x16, 0x49, 0x1F, 0x8D, 0xD8, 0x26,
+ 0xAB, 0x58, 0x45, 0xA7, 0xD7, 0xE7, 0xBE, 0xAE, 0xC1, 0xEC,
+ 0x6D, 0x27, 0x3D, 0x77, 0x12, 0x48, 0x14, 0xE7, 0x28, 0xCB,
+ 0x9C, 0x26, 0xE3, 0xF0, 0x83, 0x68, 0xC7, 0xF2, 0x0B, 0xD1,
+ 0x2A, 0x4B, 0x34, 0xE8, 0x95, 0xC1, 0xAD, 0x80, 0xD8, 0x45,
+ 0xD0, 0xC2, 0x74, 0x40, 0xCB, 0x0A, 0x6B, 0xDF, 0x05, 0x3E,
+ 0x8D, 0x46, 0x8D, 0x9C, 0xF3, 0x1B, 0x3E, 0x9E, 0xEF, 0x1C,
+ 0xE2, 0x60, 0x4D, 0x82, 0x86, 0x37, 0xE6, 0x18, 0xF2, 0x0A,
+ 0x58, 0x36, 0x47, 0x2B, 0xCC, 0xEA, 0x19, 0xBF, 0x01, 0x2D,
+ 0xFA, 0x96, 0xDC, 0xE1, 0x5A, 0x09, 0xF6, 0x9C, 0xB1, 0x2C,
+ 0x10, 0x16, 0xB9, 0x97, 0xE8, 0x7C, 0x9F, 0x61, 0x00, 0x2C,
+ 0x3F, 0x5B, 0x13, 0xD1, 0x1C, 0xCA, 0xC5, 0x6A, 0x39, 0x0B,
+ 0x03, 0x7C, 0xEF, 0x28, 0x1B, 0x42, 0x56, 0x16, 0x9C, 0x71,
+ 0x57, 0x8B, 0x1C, 0x1C, 0xD1, 0xD1, 0xEF, 0x5C, 0x2E, 0x28,
+ 0x1D, 0x86, 0x9D, 0xE6, 0xE2, 0xA3, 0xC8, 0xC9, 0x0A, 0x53,
+ 0x88, 0x73, 0xAA, 0x08, 0x3D, 0xE9, 0x99, 0x0C, 0x3D, 0x69,
+ 0x4D, 0x63, 0xEF, 0xF7, 0x1E, 0x8B, 0x7E, 0x39, 0x36, 0x47,
+ 0xC8, 0x7F, 0xF3, 0x34, 0x6E, 0x45, 0xF1, 0x74, 0x6A, 0x53,
+ 0x2D, 0x53, 0x5C, 0x86, 0xCF, 0x5C, 0x4A, 0x55, 0x54, 0xE6,
+ 0xE4, 0x21, 0x99, 0x02, 0x81, 0x81, 0x00, 0xF0, 0x39, 0x86,
+ 0x6A, 0xD8, 0xDB, 0xD0, 0x6B, 0x9C, 0xD6, 0xF5, 0x52, 0xE2,
+ 0x5D, 0xC0, 0xF6, 0x36, 0x94, 0x02, 0x07, 0xCB, 0x4E, 0xAD,
+ 0x37, 0x18, 0x7F, 0xA0, 0xCB, 0x78, 0x85, 0x1B, 0x23, 0x60,
+ 0xFD, 0x65, 0xB5, 0xD3, 0x8A, 0xA0, 0x2B, 0x00, 0x64, 0x32,
+ 0xF7, 0xF8, 0xFA, 0xCF, 0x94, 0x2E, 0x0D, 0x6C, 0x54, 0x03,
+ 0xF9, 0x73, 0x3A, 0x2B, 0x26, 0x3D, 0x12, 0x6E, 0x7A, 0xB4,
+ 0xB0, 0xED, 0x6A, 0x9D, 0x97, 0xB8, 0xB8, 0x0D, 0x3E, 0x50,
+ 0xF4, 0x3A, 0xF9, 0xDA, 0x33, 0x7C, 0x8D, 0x39, 0x0C, 0xFD,
+ 0x11, 0xB4, 0x20, 0x8F, 0x89, 0x66, 0x3C, 0x2E, 0xE6, 0x23,
+ 0x8B, 0xD2, 0xD7, 0xFC, 0xFF, 0x0F, 0x5B, 0xD0, 0x75, 0x22,
+ 0xD8, 0xFB, 0xAC, 0x04, 0xB2, 0x9E, 0xD0, 0x25, 0x6D, 0xF1,
+ 0xDB, 0xDC, 0x50, 0x82, 0xF6, 0x22, 0x88, 0x74, 0x17, 0xB8,
+ 0x6C, 0x0A, 0x86, 0x87, 0x33, 0x02, 0x81, 0x81, 0x00, 0xBE,
+ 0x78, 0x1B, 0xA6, 0x26, 0x99, 0x39, 0xB5, 0xBC, 0x89, 0x9D,
+ 0x39, 0x3B, 0x27, 0x2D, 0x9B, 0x0E, 0x70, 0x8E, 0xD7, 0x40,
+ 0xD3, 0x52, 0xEC, 0x95, 0x3E, 0xC8, 0x82, 0x93, 0x9D, 0x90,
+ 0xBD, 0xE7, 0xD5, 0xBD, 0xCB, 0x92, 0x17, 0x9A, 0x1C, 0x9D,
+ 0x47, 0x6C, 0x20, 0x5B, 0x69, 0x08, 0x87, 0xE4, 0xC1, 0x53,
+ 0x2B, 0x4A, 0x8B, 0x3D, 0x25, 0x27, 0x7A, 0xEA, 0xA5, 0x7F,
+ 0x27, 0x32, 0xCD, 0xF1, 0x07, 0x18, 0xBF, 0xCB, 0x6D, 0xB1,
+ 0x67, 0xF7, 0x67, 0x98, 0x15, 0x87, 0x4E, 0x4B, 0xCF, 0xB2,
+ 0x5F, 0xBE, 0x38, 0x5E, 0xC9, 0x41, 0xD2, 0x7A, 0xC3, 0xA1,
+ 0xC7, 0x39, 0x6D, 0x28, 0x80, 0x6B, 0x38, 0xBA, 0x95, 0xD6,
+ 0x0B, 0x00, 0x91, 0x43, 0xF2, 0xC5, 0xA8, 0xBA, 0x31, 0x5D,
+ 0x96, 0x0E, 0x4E, 0x46, 0xEE, 0x77, 0xAA, 0x3A, 0x1E, 0x27,
+ 0xA4, 0x03, 0xF0, 0x88, 0x12, 0xC8, 0xFD, 0x02, 0x81, 0x80,
+ 0x30, 0xD8, 0xA1, 0x9A, 0x6C, 0x4A, 0x16, 0x11, 0x2B, 0xAD,
+ 0x11, 0xE9, 0x2E, 0x9A, 0x3D, 0xB8, 0x52, 0xD4, 0xB9, 0xAC,
+ 0xF8, 0x0C, 0x21, 0x70, 0x88, 0x8C, 0xBB, 0x17, 0x64, 0x84,
+ 0x3C, 0x46, 0x6F, 0x5C, 0x57, 0x28, 0x27, 0xC1, 0x92, 0x5B,
+ 0xEC, 0x12, 0x73, 0xC2, 0xB4, 0x5F, 0xDB, 0x81, 0x97, 0xF3,
+ 0xA6, 0xC9, 0x56, 0x9D, 0x8C, 0x6E, 0x91, 0x83, 0x8E, 0xFB,
+ 0x86, 0x77, 0x70, 0xF2, 0x60, 0xF4, 0x42, 0xE3, 0x2C, 0xEE,
+ 0x4F, 0xD3, 0x12, 0x06, 0xF7, 0x4F, 0x02, 0xAD, 0x61, 0x70,
+ 0x1D, 0xDF, 0xA4, 0x3D, 0xCB, 0x50, 0xAB, 0x9F, 0x16, 0xA8,
+ 0xBA, 0x28, 0x95, 0xA5, 0xC2, 0xD3, 0xA4, 0x60, 0x00, 0xB1,
+ 0x7C, 0xAB, 0xB4, 0xD0, 0x46, 0x2C, 0x6E, 0x30, 0x1B, 0xD6,
+ 0xDB, 0x25, 0x85, 0xFE, 0x5C, 0xC8, 0x0A, 0x39, 0x1E, 0x40,
+ 0x7C, 0xCA, 0xAD, 0xF7, 0x5A, 0x14, 0x8C, 0xBD, 0x02, 0x81,
+ 0x81, 0x00, 0xAF, 0x11, 0x9A, 0xE8, 0x57, 0x26, 0x13, 0x83,
+ 0x55, 0xEE, 0x6F, 0x53, 0x01, 0x69, 0xFB, 0x63, 0x40, 0x1C,
+ 0x79, 0x4E, 0xA4, 0xC9, 0x18, 0xB9, 0x58, 0x5C, 0xC2, 0xD4,
+ 0x32, 0x76, 0x6B, 0x6A, 0x02, 0x43, 0xD6, 0x15, 0xDD, 0x0C,
+ 0x50, 0x9C, 0xE8, 0x7B, 0x93, 0x89, 0x12, 0x3C, 0x32, 0x99,
+ 0x25, 0xCC, 0x04, 0x74, 0x10, 0x2A, 0x77, 0x63, 0x45, 0x2C,
+ 0x97, 0xAC, 0xD2, 0x78, 0xE7, 0x1B, 0x9F, 0xE5, 0x53, 0xFC,
+ 0x46, 0x31, 0x67, 0x15, 0x16, 0xB4, 0x63, 0x77, 0xB6, 0x4C,
+ 0x63, 0x26, 0x5E, 0xDF, 0xDD, 0xE9, 0xD6, 0x45, 0xCA, 0x78,
+ 0x7A, 0x5A, 0x82, 0xC2, 0xA9, 0xA8, 0x09, 0xC8, 0x2F, 0xC5,
+ 0x8E, 0xCA, 0xD6, 0x58, 0x7A, 0x87, 0x0B, 0x1B, 0x84, 0x4E,
+ 0x98, 0x05, 0x73, 0xED, 0xCE, 0xEC, 0x68, 0x0A, 0x1C, 0x77,
+ 0x9C, 0xBF, 0xC6, 0xD3, 0xDC, 0xC3, 0x23, 0x6F, 0x90, 0xC9,
+ 0x02, 0x81, 0x81, 0x00, 0xE3, 0x4B, 0x75, 0xE6, 0x6E, 0x1E,
+ 0xBB, 0x40, 0x12, 0xE6, 0x2A, 0x05, 0x97, 0x62, 0x88, 0x2F,
+ 0x3E, 0x60, 0x0F, 0x7A, 0xC0, 0xE8, 0x4C, 0xBD, 0x42, 0xEE,
+ 0x58, 0x7C, 0x70, 0x78, 0x17, 0x09, 0xF2, 0xD2, 0xE1, 0x11,
+ 0xA8, 0x1C, 0x2E, 0xE6, 0x9B, 0x2C, 0x04, 0x16, 0xAC, 0x4C,
+ 0x45, 0xC3, 0xE1, 0x5D, 0x67, 0xDD, 0x08, 0x57, 0x23, 0xEE,
+ 0x33, 0x30, 0xF7, 0x4D, 0x37, 0xF4, 0x57, 0x06, 0x5A, 0x0E,
+ 0xBC, 0xC6, 0xA6, 0x59, 0x31, 0xA9, 0xF2, 0xC1, 0xFF, 0xCB,
+ 0x1B, 0xC4, 0xBE, 0x86, 0xDF, 0xEF, 0xEB, 0xDD, 0x9A, 0xEA,
+ 0xD0, 0xF1, 0xD5, 0xFA, 0xF8, 0x51, 0xC9, 0xE6, 0xC4, 0x29,
+ 0xBB, 0xBC, 0x7C, 0xA8, 0x48, 0x50, 0xF9, 0xC0, 0x4E, 0x57,
+ 0x00, 0x04, 0x30, 0xBC, 0x91, 0x64, 0x66, 0x33, 0xDE, 0xEC,
+ 0x6D, 0x03, 0x49, 0xEB, 0xC7, 0x9F, 0x71, 0xFA, 0xB4, 0x0B,
+ 0x91, 0x89
+};
+#define sizeof_tsa_chain_key_der_2048 (sizeof(tsa_chain_key_der_2048))
+
+/* ./certs/tsa-chain-cert.der, 2048-bit */
+static const unsigned char tsa_chain_cert_der_2048[] =
+{
+ 0x30, 0x82, 0x04, 0xFF, 0x30, 0x82, 0x03, 0xE7, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x14, 0x75, 0xAD, 0x1D, 0x25, 0xBD,
+ 0xC8, 0x57, 0x1F, 0x44, 0x1E, 0x4F, 0xC1, 0xD8, 0x6F, 0x3A,
+ 0x98, 0x2E, 0x01, 0x25, 0x7E, 0x30, 0x0D, 0x06, 0x09, 0x2A,
+ 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+ 0x30, 0x81, 0x9F, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11,
+ 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x57, 0x61, 0x73,
+ 0x68, 0x69, 0x6E, 0x67, 0x74, 0x6F, 0x6E, 0x31, 0x10, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x53, 0x65,
+ 0x61, 0x74, 0x74, 0x6C, 0x65, 0x31, 0x10, 0x30, 0x0E, 0x06,
+ 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C, 0x66,
+ 0x53, 0x53, 0x4C, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55,
+ 0x04, 0x0B, 0x0C, 0x0B, 0x44, 0x65, 0x76, 0x65, 0x6C, 0x6F,
+ 0x70, 0x6D, 0x65, 0x6E, 0x74, 0x31, 0x20, 0x30, 0x1E, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0C, 0x17, 0x77, 0x6F, 0x6C, 0x66,
+ 0x53, 0x53, 0x4C, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6D,
+ 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x20, 0x43, 0x41, 0x31,
+ 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F,
+ 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63,
+ 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x32, 0x36, 0x30, 0x36,
+ 0x31, 0x36, 0x30, 0x31, 0x30, 0x36, 0x32, 0x36, 0x5A, 0x17,
+ 0x0D, 0x32, 0x39, 0x30, 0x33, 0x31, 0x32, 0x30, 0x31, 0x30,
+ 0x36, 0x32, 0x36, 0x5A, 0x30, 0x81, 0x97, 0x31, 0x0B, 0x30,
+ 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53,
+ 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C,
+ 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10,
+ 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42,
+ 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77, 0x6F, 0x6C,
+ 0x66, 0x53, 0x53, 0x4C, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03,
+ 0x55, 0x04, 0x0B, 0x0C, 0x0E, 0x54, 0x53, 0x41, 0x2D, 0x63,
+ 0x68, 0x61, 0x69, 0x6E, 0x2D, 0x32, 0x30, 0x34, 0x38, 0x31,
+ 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F,
+ 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73,
+ 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01,
+ 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C,
+ 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x82,
+ 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01,
+ 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01,
+ 0x00, 0xB2, 0xBB, 0x66, 0xAC, 0x49, 0xBB, 0x8C, 0x5D, 0xE8,
+ 0xEE, 0xFF, 0x55, 0x12, 0xB9, 0x6E, 0xB0, 0xE8, 0x9A, 0x77,
+ 0x35, 0x7A, 0xB8, 0x65, 0x85, 0x62, 0xA6, 0x17, 0x1B, 0x27,
+ 0x25, 0x8C, 0x59, 0xBB, 0x1E, 0xE6, 0xBB, 0xAE, 0x09, 0x55,
+ 0x7D, 0xEE, 0x53, 0xFD, 0xFF, 0x56, 0x99, 0x9D, 0x0C, 0x1E,
+ 0x31, 0x48, 0xBD, 0x42, 0x69, 0x2C, 0xB2, 0x0A, 0x37, 0x68,
+ 0x5B, 0x8F, 0x33, 0xFF, 0xC5, 0xA0, 0xE0, 0xE2, 0x1E, 0x6C,
+ 0xF6, 0x4F, 0x90, 0xB4, 0x73, 0x3D, 0x13, 0xFE, 0xE4, 0xED,
+ 0xB0, 0x1F, 0xB8, 0x5E, 0xF0, 0xCE, 0x4D, 0x51, 0x0D, 0xE6,
+ 0xBE, 0x2B, 0x7D, 0xA4, 0x44, 0x6B, 0x38, 0x75, 0x83, 0x13,
+ 0x0D, 0x41, 0xD2, 0x69, 0x3F, 0x29, 0x7C, 0xF1, 0x57, 0x29,
+ 0x52, 0xF3, 0xD1, 0x2C, 0x40, 0x0E, 0xC7, 0xBE, 0x64, 0x73,
+ 0xD4, 0x9E, 0x06, 0xA4, 0x67, 0x50, 0x57, 0x03, 0xEF, 0x8E,
+ 0x6A, 0xC9, 0x06, 0x8A, 0xE3, 0xC3, 0xC3, 0xEB, 0xCB, 0x6F,
+ 0x0F, 0x3A, 0x26, 0xB9, 0xEE, 0x20, 0x28, 0x30, 0x47, 0x5C,
+ 0xEC, 0x57, 0x98, 0x18, 0x01, 0xF1, 0xD7, 0x91, 0xCA, 0x15,
+ 0x67, 0x11, 0x5E, 0xE7, 0x03, 0x72, 0x87, 0x19, 0x52, 0xBF,
+ 0xA1, 0x41, 0x47, 0x34, 0x94, 0x98, 0x88, 0x58, 0xFC, 0xCA,
+ 0x41, 0xD5, 0x71, 0x7E, 0x75, 0xB5, 0xE4, 0x95, 0x4D, 0x5E,
+ 0xBE, 0x7E, 0x5C, 0x6E, 0x4E, 0x48, 0x7A, 0xA2, 0xCD, 0x14,
+ 0x0A, 0xBB, 0xFA, 0xF8, 0x70, 0x8C, 0x32, 0x9D, 0x29, 0x7A,
+ 0x77, 0xAB, 0x55, 0x7E, 0xA8, 0x5F, 0xB8, 0x2D, 0x8E, 0x62,
+ 0x5D, 0x51, 0x2E, 0x8E, 0x17, 0xA5, 0x6E, 0x38, 0xC1, 0x05,
+ 0x87, 0x74, 0xC1, 0x55, 0x6E, 0x4F, 0x8A, 0x4A, 0x51, 0xF6,
+ 0x6C, 0xD8, 0xAC, 0xCF, 0xC0, 0x8E, 0xFA, 0x4B, 0x5C, 0x74,
+ 0x16, 0x39, 0x18, 0x1A, 0x29, 0x75, 0x67, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xA3, 0x82, 0x01, 0x37, 0x30, 0x82, 0x01, 0x33,
+ 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04,
+ 0x14, 0x60, 0x3A, 0x6A, 0x61, 0x84, 0x57, 0x4F, 0x4B, 0x52,
+ 0x94, 0x50, 0x57, 0x7E, 0x1F, 0xF4, 0xCC, 0x78, 0x22, 0x1B,
+ 0x44, 0x30, 0x81, 0xC2, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04,
+ 0x81, 0xBA, 0x30, 0x81, 0xB7, 0x80, 0x14, 0xEF, 0x69, 0xE0,
+ 0xF7, 0xD5, 0x1D, 0xE6, 0x99, 0xEC, 0xDC, 0x6D, 0xD0, 0xF7,
+ 0xE2, 0xB9, 0x5C, 0x64, 0x71, 0x83, 0x35, 0xA1, 0x81, 0x9A,
+ 0xA4, 0x81, 0x97, 0x30, 0x81, 0x94, 0x31, 0x0B, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x07,
+ 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31, 0x10, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07, 0x42, 0x6F,
+ 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x11, 0x30, 0x0F, 0x06,
+ 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x08, 0x53, 0x61, 0x77, 0x74,
+ 0x6F, 0x6F, 0x74, 0x68, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+ 0x55, 0x04, 0x0B, 0x0C, 0x0A, 0x43, 0x6F, 0x6E, 0x73, 0x75,
+ 0x6C, 0x74, 0x69, 0x6E, 0x67, 0x31, 0x18, 0x30, 0x16, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E,
+ 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F,
+ 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48,
+ 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E,
+ 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C,
+ 0x2E, 0x63, 0x6F, 0x6D, 0x82, 0x02, 0x10, 0x00, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x1D, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30,
+ 0x1A, 0x06, 0x03, 0x55, 0x1D, 0x11, 0x04, 0x13, 0x30, 0x11,
+ 0x82, 0x0F, 0x74, 0x73, 0x61, 0x2E, 0x77, 0x6F, 0x6C, 0x66,
+ 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x0E, 0x06,
+ 0x03, 0x55, 0x1D, 0x0F, 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03,
+ 0x02, 0x07, 0x80, 0x30, 0x16, 0x06, 0x03, 0x55, 0x1D, 0x25,
+ 0x01, 0x01, 0xFF, 0x04, 0x0C, 0x30, 0x0A, 0x06, 0x08, 0x2B,
+ 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x08, 0x30, 0x0D, 0x06,
+ 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x1E, 0x4A, 0x86,
+ 0xFA, 0xAB, 0x13, 0x9D, 0x97, 0x5C, 0x00, 0x70, 0x53, 0x4C,
+ 0x71, 0xCF, 0xF8, 0x8E, 0x61, 0x0D, 0x08, 0xE3, 0x47, 0x8E,
+ 0x67, 0x0B, 0x2C, 0x31, 0xC6, 0x0B, 0xB1, 0xFC, 0x23, 0x6D,
+ 0xF5, 0x76, 0xE9, 0x7F, 0xC1, 0xD3, 0x80, 0x1F, 0x8F, 0xC8,
+ 0x0A, 0x83, 0x6E, 0xEE, 0x51, 0x1F, 0x57, 0xB3, 0x70, 0x4E,
+ 0xF3, 0xA1, 0x54, 0xAF, 0x34, 0x29, 0xAE, 0x37, 0x60, 0xDA,
+ 0x8B, 0xDA, 0x95, 0xF9, 0xF6, 0xE1, 0x16, 0x56, 0x57, 0xB8,
+ 0x3E, 0x7E, 0x74, 0xA8, 0x6F, 0xA7, 0xD2, 0x27, 0xDE, 0x5C,
+ 0x56, 0xB3, 0x1A, 0x79, 0xBC, 0x4C, 0x28, 0xE9, 0x0B, 0x99,
+ 0xFC, 0xA7, 0x3A, 0xAD, 0x83, 0x79, 0x3D, 0x6F, 0x0D, 0x35,
+ 0xAF, 0x40, 0x98, 0xFC, 0xEF, 0xA7, 0x09, 0x0D, 0xB9, 0x9A,
+ 0xD7, 0x2C, 0x00, 0xA9, 0x26, 0xC5, 0xAF, 0xAF, 0x69, 0xE7,
+ 0xB0, 0x8F, 0xC0, 0xAF, 0xD8, 0xFD, 0x6C, 0x50, 0x4B, 0x45,
+ 0xC7, 0x2B, 0xCF, 0x8E, 0xF1, 0x20, 0x4A, 0x63, 0xD2, 0x3A,
+ 0xD4, 0xFC, 0xF8, 0x64, 0xD6, 0xC7, 0x02, 0x40, 0x4B, 0x1A,
+ 0xCD, 0xA2, 0x81, 0x03, 0xD9, 0x96, 0xB2, 0x61, 0xF0, 0x5D,
+ 0xC3, 0x18, 0x73, 0xA3, 0xA4, 0x5E, 0xC9, 0x4D, 0x19, 0x91,
+ 0x19, 0x23, 0x83, 0xAA, 0x46, 0x5D, 0xF8, 0xA7, 0x90, 0x5F,
+ 0x0B, 0x1E, 0xDB, 0x72, 0x4E, 0xF8, 0x7E, 0xBE, 0x4D, 0x4C,
+ 0xDB, 0x5B, 0x61, 0x15, 0x7F, 0x61, 0x12, 0x28, 0x35, 0x5B,
+ 0xAC, 0x0A, 0x0C, 0xAE, 0xF4, 0x12, 0x7C, 0x65, 0x30, 0x92,
+ 0x29, 0x73, 0xAB, 0x73, 0x2C, 0x8F, 0xDB, 0x62, 0x2F, 0x9E,
+ 0xFC, 0x2B, 0x21, 0xA5, 0x66, 0xB5, 0x7C, 0xF0, 0xFA, 0x5C,
+ 0xB1, 0x57, 0x8A, 0x9C, 0xDC, 0x7C, 0xEC, 0xA6, 0x3F, 0x92,
+ 0x29, 0x4A, 0x45, 0x88, 0xF0, 0x3C, 0xDA, 0xCD, 0xC4, 0x11,
+ 0xE2, 0xA8, 0x56
+};
+#define sizeof_tsa_chain_cert_der_2048 (sizeof(tsa_chain_cert_der_2048))
+
#endif /* USE_CERT_BUFFERS_2048 */
#ifdef USE_CERT_BUFFERS_3072
@@ -9401,6 +10303,119 @@ static const unsigned char serv_ecc_der_256[] =
};
#define sizeof_serv_ecc_der_256 (sizeof(serv_ecc_der_256))
+/* ./certs/tsa-ecc-key.der, ECC */
+static const unsigned char tsa_ecc_key_der_256[] =
+{
+ 0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x73, 0x7C, 0x5E,
+ 0xC0, 0x9D, 0xAD, 0x2B, 0x25, 0x6A, 0x76, 0xD8, 0x86, 0x9B,
+ 0x11, 0x1F, 0x83, 0xFB, 0xAF, 0x0C, 0x53, 0xE0, 0xF5, 0xED,
+ 0xB6, 0x9F, 0xB8, 0xCC, 0x4F, 0xCE, 0xBF, 0x3D, 0x5D, 0xA0,
+ 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01,
+ 0x07, 0xA1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xCE, 0x3E, 0x24,
+ 0x12, 0x01, 0x46, 0x68, 0x1D, 0x11, 0x7D, 0xCD, 0xD9, 0x8B,
+ 0x63, 0x4E, 0xF9, 0xCC, 0x36, 0x06, 0xB6, 0x75, 0x33, 0x72,
+ 0x57, 0x1C, 0x12, 0x7F, 0xFA, 0x5F, 0x77, 0x83, 0x68, 0xFC,
+ 0x18, 0x64, 0x8B, 0x75, 0xD4, 0x53, 0x88, 0x93, 0xE7, 0x64,
+ 0x38, 0x23, 0xEF, 0x19, 0xC9, 0x67, 0x95, 0x48, 0x0F, 0x7E,
+ 0xED, 0xF5, 0x81, 0x61, 0x8C, 0x66, 0xC9, 0xE3, 0xAE, 0xC8,
+ 0x12
+};
+#define sizeof_tsa_ecc_key_der_256 (sizeof(tsa_ecc_key_der_256))
+
+/* ./certs/tsa-ecc-cert.der, ECC */
+static const unsigned char tsa_ecc_cert_der_256[] =
+{
+ 0x30, 0x82, 0x03, 0x6B, 0x30, 0x82, 0x03, 0x11, 0xA0, 0x03,
+ 0x02, 0x01, 0x02, 0x02, 0x14, 0x19, 0x47, 0xB6, 0x5A, 0x9A,
+ 0x23, 0xA8, 0xFF, 0xC0, 0x13, 0x80, 0x9B, 0x3D, 0xAF, 0x92,
+ 0xEE, 0x0D, 0xD7, 0x58, 0xDE, 0x30, 0x0A, 0x06, 0x08, 0x2A,
+ 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x30, 0x81, 0x90,
+ 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+ 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E,
+ 0x61, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07,
+ 0x0C, 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07,
+ 0x77, 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x10, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x07, 0x54, 0x53,
+ 0x41, 0x2D, 0x45, 0x43, 0x43, 0x31, 0x18, 0x30, 0x16, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E,
+ 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F,
+ 0x6D, 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48,
+ 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E,
+ 0x66, 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C,
+ 0x2E, 0x63, 0x6F, 0x6D, 0x30, 0x1E, 0x17, 0x0D, 0x32, 0x36,
+ 0x30, 0x36, 0x30, 0x35, 0x31, 0x30, 0x30, 0x39, 0x34, 0x30,
+ 0x5A, 0x17, 0x0D, 0x32, 0x39, 0x30, 0x33, 0x30, 0x31, 0x31,
+ 0x30, 0x30, 0x39, 0x34, 0x30, 0x5A, 0x30, 0x81, 0x90, 0x31,
+ 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+ 0x55, 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04,
+ 0x08, 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61,
+ 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C,
+ 0x07, 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x10,
+ 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77,
+ 0x6F, 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x10, 0x30, 0x0E,
+ 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x07, 0x54, 0x53, 0x41,
+ 0x2D, 0x45, 0x43, 0x43, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03,
+ 0x55, 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77,
+ 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D,
+ 0x31, 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+ 0xF7, 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66,
+ 0x6F, 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E,
+ 0x63, 0x6F, 0x6D, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2A,
+ 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x06, 0x08, 0x2A, 0x86,
+ 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04,
+ 0xCE, 0x3E, 0x24, 0x12, 0x01, 0x46, 0x68, 0x1D, 0x11, 0x7D,
+ 0xCD, 0xD9, 0x8B, 0x63, 0x4E, 0xF9, 0xCC, 0x36, 0x06, 0xB6,
+ 0x75, 0x33, 0x72, 0x57, 0x1C, 0x12, 0x7F, 0xFA, 0x5F, 0x77,
+ 0x83, 0x68, 0xFC, 0x18, 0x64, 0x8B, 0x75, 0xD4, 0x53, 0x88,
+ 0x93, 0xE7, 0x64, 0x38, 0x23, 0xEF, 0x19, 0xC9, 0x67, 0x95,
+ 0x48, 0x0F, 0x7E, 0xED, 0xF5, 0x81, 0x61, 0x8C, 0x66, 0xC9,
+ 0xE3, 0xAE, 0xC8, 0x12, 0xA3, 0x82, 0x01, 0x45, 0x30, 0x82,
+ 0x01, 0x41, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04,
+ 0x16, 0x04, 0x14, 0xAB, 0xE6, 0xD5, 0xD9, 0x8A, 0x31, 0xCB,
+ 0x10, 0x23, 0x82, 0x16, 0xCE, 0x20, 0x1F, 0x80, 0xE4, 0xD5,
+ 0x87, 0x5A, 0x20, 0x30, 0x81, 0xD0, 0x06, 0x03, 0x55, 0x1D,
+ 0x23, 0x04, 0x81, 0xC8, 0x30, 0x81, 0xC5, 0x80, 0x14, 0xAB,
+ 0xE6, 0xD5, 0xD9, 0x8A, 0x31, 0xCB, 0x10, 0x23, 0x82, 0x16,
+ 0xCE, 0x20, 0x1F, 0x80, 0xE4, 0xD5, 0x87, 0x5A, 0x20, 0xA1,
+ 0x81, 0x96, 0xA4, 0x81, 0x93, 0x30, 0x81, 0x90, 0x31, 0x0B,
+ 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
+ 0x53, 0x31, 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x08,
+ 0x0C, 0x07, 0x4D, 0x6F, 0x6E, 0x74, 0x61, 0x6E, 0x61, 0x31,
+ 0x10, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x07,
+ 0x42, 0x6F, 0x7A, 0x65, 0x6D, 0x61, 0x6E, 0x31, 0x10, 0x30,
+ 0x0E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x07, 0x77, 0x6F,
+ 0x6C, 0x66, 0x53, 0x53, 0x4C, 0x31, 0x10, 0x30, 0x0E, 0x06,
+ 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x07, 0x54, 0x53, 0x41, 0x2D,
+ 0x45, 0x43, 0x43, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55,
+ 0x04, 0x03, 0x0C, 0x0F, 0x77, 0x77, 0x77, 0x2E, 0x77, 0x6F,
+ 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63, 0x6F, 0x6D, 0x31,
+ 0x1F, 0x30, 0x1D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7,
+ 0x0D, 0x01, 0x09, 0x01, 0x16, 0x10, 0x69, 0x6E, 0x66, 0x6F,
+ 0x40, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E, 0x63,
+ 0x6F, 0x6D, 0x82, 0x14, 0x19, 0x47, 0xB6, 0x5A, 0x9A, 0x23,
+ 0xA8, 0xFF, 0xC0, 0x13, 0x80, 0x9B, 0x3D, 0xAF, 0x92, 0xEE,
+ 0x0D, 0xD7, 0x58, 0xDE, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1D,
+ 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1A, 0x06, 0x03, 0x55,
+ 0x1D, 0x11, 0x04, 0x13, 0x30, 0x11, 0x82, 0x0F, 0x74, 0x73,
+ 0x61, 0x2E, 0x77, 0x6F, 0x6C, 0x66, 0x73, 0x73, 0x6C, 0x2E,
+ 0x63, 0x6F, 0x6D, 0x30, 0x0E, 0x06, 0x03, 0x55, 0x1D, 0x0F,
+ 0x01, 0x01, 0xFF, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30,
+ 0x16, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x01, 0x01, 0xFF, 0x04,
+ 0x0C, 0x30, 0x0A, 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05,
+ 0x07, 0x03, 0x08, 0x30, 0x0A, 0x06, 0x08, 0x2A, 0x86, 0x48,
+ 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45,
+ 0x02, 0x20, 0x2B, 0x51, 0x26, 0x56, 0xE1, 0xE1, 0x1F, 0xB9,
+ 0xAD, 0x03, 0x07, 0x46, 0xFA, 0x7B, 0x34, 0xD0, 0x2A, 0xF5,
+ 0x11, 0xC8, 0x12, 0x0C, 0x06, 0x32, 0x23, 0x29, 0x6F, 0x1B,
+ 0xA1, 0x18, 0x87, 0x9A, 0x02, 0x21, 0x00, 0xD9, 0xE6, 0xA9,
+ 0x01, 0xB6, 0xCB, 0x22, 0xF8, 0x28, 0x5F, 0x91, 0xA5, 0xBD,
+ 0x9C, 0xE7, 0xF6, 0x24, 0x5B, 0xD5, 0x5E, 0x7D, 0x1D, 0xA1,
+ 0x65, 0x12, 0xCA, 0xAA, 0xEB, 0x48, 0xFA, 0xD5, 0x15
+};
+#define sizeof_tsa_ecc_cert_der_256 (sizeof(tsa_ecc_cert_der_256))
+
/* ./certs/ca-ecc-key.der, ECC */
static const unsigned char ca_ecc_key_der_256[] =
{
diff --git a/wolfssl/openssl/include.am b/wolfssl/openssl/include.am
index 84e0dbb1f3c..941cf1b18d9 100644
--- a/wolfssl/openssl/include.am
+++ b/wolfssl/openssl/include.am
@@ -54,6 +54,7 @@ nobase_include_HEADERS+= \
wolfssl/openssl/ssl.h \
wolfssl/openssl/stack.h \
wolfssl/openssl/tls1.h \
+ wolfssl/openssl/ts.h \
wolfssl/openssl/txt_db.h \
wolfssl/openssl/ui.h \
wolfssl/openssl/x509.h \
diff --git a/wolfssl/openssl/ssl.h b/wolfssl/openssl/ssl.h
index 063500675e1..cdc8ea64adb 100644
--- a/wolfssl/openssl/ssl.h
+++ b/wolfssl/openssl/ssl.h
@@ -974,6 +974,7 @@ wolfSSL_X509_STORE_set_verify_cb((WOLFSSL_X509_STORE *)(s), (WOLFSSL_X509_STORE_
#define ASN1_BIT_STRING_free wolfSSL_ASN1_BIT_STRING_free
#define ASN1_BIT_STRING_get_bit wolfSSL_ASN1_BIT_STRING_get_bit
#define ASN1_BIT_STRING_set_bit wolfSSL_ASN1_BIT_STRING_set_bit
+#define ASN1_BIT_STRING_set1 wolfSSL_ASN1_BIT_STRING_set1
#define i2d_ASN1_BIT_STRING wolfSSL_i2d_ASN1_BIT_STRING
#define d2i_ASN1_BIT_STRING wolfSSL_d2i_ASN1_BIT_STRING
diff --git a/wolfssl/openssl/ts.h b/wolfssl/openssl/ts.h
new file mode 100644
index 00000000000..8b87a5995b9
--- /dev/null
+++ b/wolfssl/openssl/ts.h
@@ -0,0 +1,359 @@
+/* ts.h
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/* ts.h - Time-Stamp Protocol (RFC 3161) compatibility layer.
+ *
+ * Requester side only: create and encode requests, decode responses and
+ * verify time-stamp tokens. There is no TS_RESP_CTX - wolfSSL's wc_Tsp API
+ * is used to implement a TSA.
+ */
+
+#ifndef WOLFSSL_OPENSSL_TS_H_
+#define WOLFSSL_OPENSSL_TS_H_
+
+#include
+#include
+#include
+
+#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && \
+ defined(WOLFSSL_TSP_VERIFIER)
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+typedef struct WOLFSSL_TS_MSG_IMPRINT WOLFSSL_TS_MSG_IMPRINT;
+typedef struct WOLFSSL_TS_REQ WOLFSSL_TS_REQ;
+typedef struct WOLFSSL_TS_ACCURACY WOLFSSL_TS_ACCURACY;
+typedef struct WOLFSSL_TS_STATUS_INFO WOLFSSL_TS_STATUS_INFO;
+typedef struct WOLFSSL_TS_TST_INFO WOLFSSL_TS_TST_INFO;
+typedef struct WOLFSSL_TS_RESP WOLFSSL_TS_RESP;
+typedef struct WOLFSSL_TS_VERIFY_CTX WOLFSSL_TS_VERIFY_CTX;
+
+/* PKIStatus values. RFC 3161, 2.4.2. */
+#define WOLFSSL_TS_STATUS_GRANTED 0
+#define WOLFSSL_TS_STATUS_GRANTED_WITH_MODS 1
+#define WOLFSSL_TS_STATUS_REJECTION 2
+#define WOLFSSL_TS_STATUS_WAITING 3
+#define WOLFSSL_TS_STATUS_REVOCATION_WARNING 4
+#define WOLFSSL_TS_STATUS_REVOCATION_NOTIFICATION 5
+
+/* Verification flags. */
+#define WOLFSSL_TS_VFY_SIGNATURE (1u << 0)
+#define WOLFSSL_TS_VFY_VERSION (1u << 1)
+#define WOLFSSL_TS_VFY_POLICY (1u << 2)
+#define WOLFSSL_TS_VFY_IMPRINT (1u << 3)
+#define WOLFSSL_TS_VFY_DATA (1u << 4)
+#define WOLFSSL_TS_VFY_NONCE (1u << 5)
+#define WOLFSSL_TS_VFY_SIGNER (1u << 6)
+#define WOLFSSL_TS_VFY_TSA_NAME (1u << 7) /* needs name - not supported */
+
+#define WOLFSSL_TS_VFY_ALL_IMPRINT (WOLFSSL_TS_VFY_SIGNATURE | \
+ WOLFSSL_TS_VFY_VERSION | \
+ WOLFSSL_TS_VFY_POLICY | \
+ WOLFSSL_TS_VFY_IMPRINT | \
+ WOLFSSL_TS_VFY_NONCE | \
+ WOLFSSL_TS_VFY_SIGNER | \
+ WOLFSSL_TS_VFY_TSA_NAME)
+
+/* TS_RESP_CTX flags - OpenSSL values. */
+#define WOLFSSL_TS_TSA_NAME 0x01
+#define WOLFSSL_TS_ORDERING 0x02
+#define WOLFSSL_TS_ESS_CERT_ID_CHAIN 0x04
+
+/* Responder context for creating time-stamp responses. */
+typedef struct WOLFSSL_TS_RESP_CTX WOLFSSL_TS_RESP_CTX;
+
+/* Callback returning a new serial number as an ASN1_INTEGER - the response
+ * creation takes ownership of the returned object. */
+typedef WOLFSSL_ASN1_INTEGER* (*WOLFSSL_TS_serial_cb)(WOLFSSL_TS_RESP_CTX*,
+ void*);
+/* Callback filling the time in seconds since the epoch (and optionally
+ * microseconds). Returns 1 on success. */
+typedef int (*WOLFSSL_TS_time_cb)(WOLFSSL_TS_RESP_CTX*, void*, long* sec,
+ long* usec);
+
+/* TS_MSG_IMPRINT */
+WOLFSSL_API WOLFSSL_TS_MSG_IMPRINT* wolfSSL_TS_MSG_IMPRINT_new(void);
+WOLFSSL_API void wolfSSL_TS_MSG_IMPRINT_free(WOLFSSL_TS_MSG_IMPRINT* a);
+WOLFSSL_API int wolfSSL_TS_MSG_IMPRINT_set_algo(WOLFSSL_TS_MSG_IMPRINT* a,
+ WOLFSSL_X509_ALGOR* alg);
+WOLFSSL_API WOLFSSL_X509_ALGOR* wolfSSL_TS_MSG_IMPRINT_get_algo(
+ WOLFSSL_TS_MSG_IMPRINT* a);
+WOLFSSL_API int wolfSSL_TS_MSG_IMPRINT_set_msg(WOLFSSL_TS_MSG_IMPRINT* a,
+ unsigned char* d, int len);
+WOLFSSL_API WOLFSSL_ASN1_STRING* wolfSSL_TS_MSG_IMPRINT_get_msg(
+ WOLFSSL_TS_MSG_IMPRINT* a);
+
+/* TS_REQ */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API WOLFSSL_TS_REQ* wolfSSL_TS_REQ_new(void);
+WOLFSSL_API void wolfSSL_TS_REQ_free(WOLFSSL_TS_REQ* a);
+WOLFSSL_API int wolfSSL_TS_REQ_set_version(WOLFSSL_TS_REQ* a, long version);
+WOLFSSL_API long wolfSSL_TS_REQ_get_version(const WOLFSSL_TS_REQ* a);
+WOLFSSL_API int wolfSSL_TS_REQ_set_msg_imprint(WOLFSSL_TS_REQ* a,
+ WOLFSSL_TS_MSG_IMPRINT* msgImprint);
+WOLFSSL_API WOLFSSL_TS_MSG_IMPRINT* wolfSSL_TS_REQ_get_msg_imprint(
+ WOLFSSL_TS_REQ* a);
+WOLFSSL_API int wolfSSL_TS_REQ_set_policy_id(WOLFSSL_TS_REQ* a,
+ const WOLFSSL_ASN1_OBJECT* policy);
+WOLFSSL_API WOLFSSL_ASN1_OBJECT* wolfSSL_TS_REQ_get_policy_id(
+ WOLFSSL_TS_REQ* a);
+WOLFSSL_API int wolfSSL_TS_REQ_set_nonce(WOLFSSL_TS_REQ* a,
+ const WOLFSSL_ASN1_INTEGER* nonce);
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_REQ_get_nonce(
+ const WOLFSSL_TS_REQ* a);
+WOLFSSL_API int wolfSSL_TS_REQ_set_cert_req(WOLFSSL_TS_REQ* a, int certReq);
+WOLFSSL_API int wolfSSL_TS_REQ_get_cert_req(const WOLFSSL_TS_REQ* a);
+WOLFSSL_API int wolfSSL_i2d_TS_REQ(const WOLFSSL_TS_REQ* a,
+ unsigned char** pp);
+WOLFSSL_API WOLFSSL_TS_REQ* wolfSSL_d2i_TS_REQ(WOLFSSL_TS_REQ** a,
+ const unsigned char** pp, long length);
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+/* TS_STATUS_INFO */
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_STATUS_INFO_get0_status(
+ const WOLFSSL_TS_STATUS_INFO* a);
+WOLFSSL_API const WOLFSSL_ASN1_BIT_STRING*
+ wolfSSL_TS_STATUS_INFO_get0_failure_info(
+ const WOLFSSL_TS_STATUS_INFO* a);
+WOLFSSL_API const WOLF_STACK_OF(WOLFSSL_ASN1_STRING)*
+ wolfSSL_TS_STATUS_INFO_get0_text(const WOLFSSL_TS_STATUS_INFO* a);
+
+/* TS_ACCURACY */
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_ACCURACY_get_seconds(
+ const WOLFSSL_TS_ACCURACY* a);
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_ACCURACY_get_millis(
+ const WOLFSSL_TS_ACCURACY* a);
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_ACCURACY_get_micros(
+ const WOLFSSL_TS_ACCURACY* a);
+
+/* TS_TST_INFO */
+WOLFSSL_API void wolfSSL_TS_TST_INFO_free(WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API WOLFSSL_TS_TST_INFO* wolfSSL_d2i_TS_TST_INFO(
+ WOLFSSL_TS_TST_INFO** a, const unsigned char** pp, long length);
+WOLFSSL_API int wolfSSL_i2d_TS_TST_INFO(const WOLFSSL_TS_TST_INFO* a,
+ unsigned char** pp);
+WOLFSSL_API long wolfSSL_TS_TST_INFO_get_version(
+ const WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API WOLFSSL_ASN1_OBJECT* wolfSSL_TS_TST_INFO_get_policy_id(
+ WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API WOLFSSL_TS_MSG_IMPRINT* wolfSSL_TS_TST_INFO_get_msg_imprint(
+ WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_TST_INFO_get_serial(
+ const WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API const WOLFSSL_ASN1_GENERALIZEDTIME* wolfSSL_TS_TST_INFO_get_time(
+ const WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API WOLFSSL_TS_ACCURACY* wolfSSL_TS_TST_INFO_get_accuracy(
+ WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API int wolfSSL_TS_TST_INFO_get_ordering(
+ const WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API const WOLFSSL_ASN1_INTEGER* wolfSSL_TS_TST_INFO_get_nonce(
+ const WOLFSSL_TS_TST_INFO* a);
+WOLFSSL_API WOLFSSL_GENERAL_NAME* wolfSSL_TS_TST_INFO_get_tsa(
+ WOLFSSL_TS_TST_INFO* a);
+
+/* TS_RESP */
+WOLFSSL_API void wolfSSL_TS_RESP_free(WOLFSSL_TS_RESP* a);
+WOLFSSL_API WOLFSSL_TS_RESP* wolfSSL_d2i_TS_RESP(WOLFSSL_TS_RESP** a,
+ const unsigned char** pp, long length);
+WOLFSSL_API int wolfSSL_i2d_TS_RESP(const WOLFSSL_TS_RESP* a,
+ unsigned char** pp);
+WOLFSSL_API WOLFSSL_TS_STATUS_INFO* wolfSSL_TS_RESP_get_status_info(
+ WOLFSSL_TS_RESP* a);
+WOLFSSL_API WOLFSSL_TS_TST_INFO* wolfSSL_TS_RESP_get_tst_info(
+ WOLFSSL_TS_RESP* a);
+
+/* TS_VERIFY_CTX */
+WOLFSSL_API WOLFSSL_TS_VERIFY_CTX* wolfSSL_TS_VERIFY_CTX_new(void);
+WOLFSSL_API void wolfSSL_TS_VERIFY_CTX_free(WOLFSSL_TS_VERIFY_CTX* ctx);
+WOLFSSL_API void wolfSSL_TS_VERIFY_CTX_cleanup(WOLFSSL_TS_VERIFY_CTX* ctx);
+WOLFSSL_API int wolfSSL_TS_VERIFY_CTX_set_flags(WOLFSSL_TS_VERIFY_CTX* ctx,
+ int flags);
+WOLFSSL_API int wolfSSL_TS_VERIFY_CTX_add_flags(WOLFSSL_TS_VERIFY_CTX* ctx,
+ int flags);
+WOLFSSL_API unsigned char* wolfSSL_TS_VERIFY_CTX_set_imprint(
+ WOLFSSL_TS_VERIFY_CTX* ctx, unsigned char* imprint, long len);
+WOLFSSL_API WOLFSSL_X509_STORE* wolfSSL_TS_VERIFY_CTX_set_store(
+ WOLFSSL_TS_VERIFY_CTX* ctx, WOLFSSL_X509_STORE* store);
+WOLFSSL_API WOLFSSL_BIO* wolfSSL_TS_VERIFY_CTX_set_data(
+ WOLFSSL_TS_VERIFY_CTX* ctx, WOLFSSL_BIO* b);
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API WOLFSSL_TS_VERIFY_CTX* wolfSSL_TS_REQ_to_TS_VERIFY_CTX(
+ WOLFSSL_TS_REQ* req, WOLFSSL_TS_VERIFY_CTX* ctx);
+#endif /* WOLFSSL_TSP_REQUESTER */
+WOLFSSL_API int wolfSSL_TS_RESP_verify_response(WOLFSSL_TS_VERIFY_CTX* ctx,
+ WOLFSSL_TS_RESP* response);
+#ifdef OPENSSL_ALL
+WOLFSSL_API int wolfSSL_TS_RESP_verify_token(WOLFSSL_TS_VERIFY_CTX* ctx,
+ WOLFSSL_PKCS7* token);
+#endif
+
+/* TS_RESP_CTX - responder context for creating time-stamp responses. */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API WOLFSSL_TS_RESP_CTX* wolfSSL_TS_RESP_CTX_new(void);
+WOLFSSL_API void wolfSSL_TS_RESP_CTX_free(WOLFSSL_TS_RESP_CTX* ctx);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_signer_cert(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_X509* signer);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_signer_key(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_EVP_PKEY* key);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_signer_digest(
+ WOLFSSL_TS_RESP_CTX* ctx, const WOLFSSL_EVP_MD* md);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_def_policy(WOLFSSL_TS_RESP_CTX* ctx,
+ const WOLFSSL_ASN1_OBJECT* policy);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_serial_cb(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_TS_serial_cb cb, void* data);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_time_cb(WOLFSSL_TS_RESP_CTX* ctx,
+ WOLFSSL_TS_time_cb cb, void* data);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_set_accuracy(WOLFSSL_TS_RESP_CTX* ctx,
+ int secs, int millis, int micros);
+WOLFSSL_API int wolfSSL_TS_RESP_CTX_add_flags(WOLFSSL_TS_RESP_CTX* ctx,
+ int flags);
+WOLFSSL_API WOLFSSL_TS_RESP* wolfSSL_TS_RESP_create_response(
+ WOLFSSL_TS_RESP_CTX* ctx, WOLFSSL_BIO* req_bio);
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#ifndef OPENSSL_COEXIST
+
+typedef WOLFSSL_TS_MSG_IMPRINT TS_MSG_IMPRINT;
+typedef WOLFSSL_TS_REQ TS_REQ;
+typedef WOLFSSL_TS_ACCURACY TS_ACCURACY;
+typedef WOLFSSL_TS_STATUS_INFO TS_STATUS_INFO;
+typedef WOLFSSL_TS_TST_INFO TS_TST_INFO;
+typedef WOLFSSL_TS_RESP TS_RESP;
+typedef WOLFSSL_TS_VERIFY_CTX TS_VERIFY_CTX;
+
+#define TS_STATUS_GRANTED WOLFSSL_TS_STATUS_GRANTED
+#define TS_STATUS_GRANTED_WITH_MODS WOLFSSL_TS_STATUS_GRANTED_WITH_MODS
+#define TS_STATUS_REJECTION WOLFSSL_TS_STATUS_REJECTION
+#define TS_STATUS_WAITING WOLFSSL_TS_STATUS_WAITING
+#define TS_STATUS_REVOCATION_WARNING WOLFSSL_TS_STATUS_REVOCATION_WARNING
+#define TS_STATUS_REVOCATION_NOTIFICATION \
+ WOLFSSL_TS_STATUS_REVOCATION_NOTIFICATION
+
+#define TS_VFY_SIGNATURE WOLFSSL_TS_VFY_SIGNATURE
+#define TS_VFY_VERSION WOLFSSL_TS_VFY_VERSION
+#define TS_VFY_POLICY WOLFSSL_TS_VFY_POLICY
+#define TS_VFY_IMPRINT WOLFSSL_TS_VFY_IMPRINT
+#define TS_VFY_DATA WOLFSSL_TS_VFY_DATA
+#define TS_VFY_NONCE WOLFSSL_TS_VFY_NONCE
+#define TS_VFY_SIGNER WOLFSSL_TS_VFY_SIGNER
+#define TS_VFY_TSA_NAME WOLFSSL_TS_VFY_TSA_NAME
+#define TS_VFY_ALL_IMPRINT WOLFSSL_TS_VFY_ALL_IMPRINT
+
+#define TS_MSG_IMPRINT_new wolfSSL_TS_MSG_IMPRINT_new
+#define TS_MSG_IMPRINT_free wolfSSL_TS_MSG_IMPRINT_free
+#define TS_MSG_IMPRINT_set_algo wolfSSL_TS_MSG_IMPRINT_set_algo
+#define TS_MSG_IMPRINT_get_algo wolfSSL_TS_MSG_IMPRINT_get_algo
+#define TS_MSG_IMPRINT_set_msg wolfSSL_TS_MSG_IMPRINT_set_msg
+#define TS_MSG_IMPRINT_get_msg wolfSSL_TS_MSG_IMPRINT_get_msg
+
+#ifdef WOLFSSL_TSP_REQUESTER
+#define TS_REQ_new wolfSSL_TS_REQ_new
+#define TS_REQ_free wolfSSL_TS_REQ_free
+#define TS_REQ_set_version wolfSSL_TS_REQ_set_version
+#define TS_REQ_get_version wolfSSL_TS_REQ_get_version
+#define TS_REQ_set_msg_imprint wolfSSL_TS_REQ_set_msg_imprint
+#define TS_REQ_get_msg_imprint wolfSSL_TS_REQ_get_msg_imprint
+#define TS_REQ_set_policy_id wolfSSL_TS_REQ_set_policy_id
+#define TS_REQ_get_policy_id wolfSSL_TS_REQ_get_policy_id
+#define TS_REQ_set_nonce wolfSSL_TS_REQ_set_nonce
+#define TS_REQ_get_nonce wolfSSL_TS_REQ_get_nonce
+#define TS_REQ_set_cert_req wolfSSL_TS_REQ_set_cert_req
+#define TS_REQ_get_cert_req wolfSSL_TS_REQ_get_cert_req
+#define i2d_TS_REQ wolfSSL_i2d_TS_REQ
+#define d2i_TS_REQ wolfSSL_d2i_TS_REQ
+#endif /* WOLFSSL_TSP_REQUESTER */
+
+#define TS_STATUS_INFO_get0_status wolfSSL_TS_STATUS_INFO_get0_status
+#define TS_STATUS_INFO_get0_text wolfSSL_TS_STATUS_INFO_get0_text
+#define sk_ASN1_UTF8STRING_num wolfSSL_sk_num
+#define sk_ASN1_UTF8STRING_value wolfSSL_sk_value
+#define TS_STATUS_INFO_get0_failure_info \
+ wolfSSL_TS_STATUS_INFO_get0_failure_info
+
+#define TS_ACCURACY_get_seconds wolfSSL_TS_ACCURACY_get_seconds
+#define TS_ACCURACY_get_millis wolfSSL_TS_ACCURACY_get_millis
+#define TS_ACCURACY_get_micros wolfSSL_TS_ACCURACY_get_micros
+
+#define TS_TST_INFO_free wolfSSL_TS_TST_INFO_free
+#define d2i_TS_TST_INFO wolfSSL_d2i_TS_TST_INFO
+#define i2d_TS_TST_INFO wolfSSL_i2d_TS_TST_INFO
+#define TS_TST_INFO_get_version wolfSSL_TS_TST_INFO_get_version
+#define TS_TST_INFO_get_policy_id wolfSSL_TS_TST_INFO_get_policy_id
+#define TS_TST_INFO_get_msg_imprint wolfSSL_TS_TST_INFO_get_msg_imprint
+#define TS_TST_INFO_get_serial wolfSSL_TS_TST_INFO_get_serial
+#define TS_TST_INFO_get_time wolfSSL_TS_TST_INFO_get_time
+#define TS_TST_INFO_get_accuracy wolfSSL_TS_TST_INFO_get_accuracy
+#define TS_TST_INFO_get_ordering wolfSSL_TS_TST_INFO_get_ordering
+#define TS_TST_INFO_get_nonce wolfSSL_TS_TST_INFO_get_nonce
+#define TS_TST_INFO_get_tsa wolfSSL_TS_TST_INFO_get_tsa
+
+#define TS_RESP_free wolfSSL_TS_RESP_free
+#define d2i_TS_RESP wolfSSL_d2i_TS_RESP
+#define i2d_TS_RESP wolfSSL_i2d_TS_RESP
+#define TS_RESP_get_status_info wolfSSL_TS_RESP_get_status_info
+#define TS_RESP_get_tst_info wolfSSL_TS_RESP_get_tst_info
+
+#define TS_VERIFY_CTX_new wolfSSL_TS_VERIFY_CTX_new
+#define TS_VERIFY_CTX_free wolfSSL_TS_VERIFY_CTX_free
+#define TS_VERIFY_CTX_cleanup wolfSSL_TS_VERIFY_CTX_cleanup
+#define TS_VERIFY_CTX_set_flags wolfSSL_TS_VERIFY_CTX_set_flags
+#define TS_VERIFY_CTX_add_flags wolfSSL_TS_VERIFY_CTX_add_flags
+#define TS_VERIFY_CTX_set_imprint wolfSSL_TS_VERIFY_CTX_set_imprint
+#define TS_VERIFY_CTX_set_store wolfSSL_TS_VERIFY_CTX_set_store
+#define TS_VERIFY_CTX_set_data wolfSSL_TS_VERIFY_CTX_set_data
+#ifdef WOLFSSL_TSP_REQUESTER
+#define TS_REQ_to_TS_VERIFY_CTX wolfSSL_TS_REQ_to_TS_VERIFY_CTX
+#endif
+#define TS_RESP_verify_response wolfSSL_TS_RESP_verify_response
+#ifdef OPENSSL_ALL
+#define TS_RESP_verify_token wolfSSL_TS_RESP_verify_token
+#endif
+
+#define TS_TST_INFO_get_tsa wolfSSL_TS_TST_INFO_get_tsa
+
+#ifdef WOLFSSL_TSP_RESPONDER
+#define TS_RESP_CTX_new wolfSSL_TS_RESP_CTX_new
+#define TS_RESP_CTX_free wolfSSL_TS_RESP_CTX_free
+#define TS_RESP_CTX_set_signer_cert wolfSSL_TS_RESP_CTX_set_signer_cert
+#define TS_RESP_CTX_set_signer_key wolfSSL_TS_RESP_CTX_set_signer_key
+#define TS_RESP_CTX_set_signer_digest wolfSSL_TS_RESP_CTX_set_signer_digest
+#define TS_RESP_CTX_set_def_policy wolfSSL_TS_RESP_CTX_set_def_policy
+#define TS_RESP_CTX_set_serial_cb wolfSSL_TS_RESP_CTX_set_serial_cb
+#define TS_RESP_CTX_set_time_cb wolfSSL_TS_RESP_CTX_set_time_cb
+#define TS_RESP_CTX_set_accuracy wolfSSL_TS_RESP_CTX_set_accuracy
+#define TS_RESP_CTX_add_flags wolfSSL_TS_RESP_CTX_add_flags
+#define TS_RESP_create_response wolfSSL_TS_RESP_create_response
+#define TS_RESP_CTX WOLFSSL_TS_RESP_CTX
+#define TS_TSA_NAME WOLFSSL_TS_TSA_NAME
+#define TS_ORDERING WOLFSSL_TS_ORDERING
+#define TS_ESS_CERT_ID_CHAIN WOLFSSL_TS_ESS_CERT_ID_CHAIN
+#endif /* WOLFSSL_TSP_RESPONDER */
+
+#endif /* !OPENSSL_COEXIST */
+
+#ifdef __cplusplus
+ } /* extern "C" */
+#endif
+
+#endif /* OPENSSL_EXTRA && WOLFSSL_TSP && HAVE_PKCS7 */
+#endif /* WOLFSSL_OPENSSL_TS_H_ */
diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h
index 738828b127b..3b2d97f9ea7 100644
--- a/wolfssl/ssl.h
+++ b/wolfssl/ssl.h
@@ -5687,6 +5687,9 @@ WOLFSSL_API int wolfSSL_ASN1_BIT_STRING_get_bit(
const WOLFSSL_ASN1_BIT_STRING* str, int i);
WOLFSSL_API int wolfSSL_ASN1_BIT_STRING_set_bit(
WOLFSSL_ASN1_BIT_STRING* str, int pos, int val);
+WOLFSSL_API int wolfSSL_ASN1_BIT_STRING_set1(
+ WOLFSSL_ASN1_BIT_STRING* str,
+ const unsigned char* data, int len);
WOLFSSL_API int wolfSSL_i2d_ASN1_BIT_STRING(const WOLFSSL_ASN1_BIT_STRING* bstr,
unsigned char** pp);
WOLFSSL_API WOLFSSL_ASN1_BIT_STRING* wolfSSL_d2i_ASN1_BIT_STRING(
diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h
index 45993f0638a..4fac3130239 100644
--- a/wolfssl/wolfcrypt/asn.h
+++ b/wolfssl/wolfcrypt/asn.h
@@ -254,6 +254,11 @@ enum ASNItem_DataType {
ASN_DATA_TYPE_MP_POS_NEG = 10,
/* ASN.1 CHOICE. A 0 terminated list of tags that are valid. */
ASN_DATA_TYPE_CHOICE = 11
+#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+ /* 32-bit integer value encoded as an INTEGER's content even when the
+ * item is implicitly tagged. */
+ ,ASN_DATA_TYPE_WORD32_INT = 12
+#endif
};
/* A template entry describing an ASN.1 item. */
@@ -390,6 +395,10 @@ WOLFSSL_LOCAL void GetASN_OIDData(const ASNGetData * dataASN, const byte** data,
WOLFSSL_LOCAL void SetASN_Boolean(ASNSetData *dataASN, byte val);
WOLFSSL_LOCAL void SetASN_Int8Bit(ASNSetData *dataASN, byte num);
WOLFSSL_LOCAL void SetASN_Int16Bit(ASNSetData *dataASN, word16 num);
+#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+WOLFSSL_LOCAL void SetASN_Int32Bit(ASNSetData *dataASN, word32 num);
+WOLFSSL_LOCAL void SetASN_Int32BitInt(ASNSetData *dataASN, word32 num);
+#endif
WOLFSSL_LOCAL void SetASN_Buffer(ASNSetData *dataASN, const byte* data,
word32 length);
WOLFSSL_LOCAL void SetASN_ReplaceBuffer(ASNSetData *dataASN, const byte* data,
@@ -588,6 +597,33 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType);
(dataASN)->data.u16 = (num); \
} while (0)
+#ifdef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+/* Setup an ASN data item to set a 32-bit number.
+ *
+ * @param [in] dataASN Dynamic ASN data item.
+ * @param [in] num 32-bit number to set.
+ */
+#define SetASN_Int32Bit(dataASN, num) \
+ do { \
+ (dataASN)->dataType = ASN_DATA_TYPE_WORD32; \
+ (dataASN)->data.u32 = (num); \
+ } while (0)
+
+/* Setup an ASN data item to set a 32-bit number encoded as an INTEGER.
+ *
+ * For implicitly tagged INTEGERs - a zero byte is prepended to keep the
+ * number positive, as is done for INTEGER tagged items.
+ *
+ * @param [in] dataASN Dynamic ASN data item.
+ * @param [in] num 32-bit number to set.
+ */
+#define SetASN_Int32BitInt(dataASN, num) \
+ do { \
+ (dataASN)->dataType = ASN_DATA_TYPE_WORD32_INT; \
+ (dataASN)->data.u32 = (num); \
+ } while (0)
+#endif
+
/* Setup an ASN data item to set the data in a buffer.
*
* @param [in] dataASN Dynamic ASN data item.
@@ -723,6 +759,32 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType);
} \
while (0)
+/* Set the node, and when a header, all nodes below to not be encoded.
+ *
+ * A node that is not a header has no nodes below - no need to check
+ * further nodes at a lower depth.
+ *
+ * @param [in] dataASN Dynamic ASN data item.
+ * @param [in] asn ASN template item.
+ * @param [in] header Node is a header - nodes below are also set to not
+ * be encoded.
+ * @param [in] node Node which should not be encoded.
+ * @param [in] dataASNLen Number of items in dataASN.
+ */
+#define SetASNItem_NoOutNode_ex(dataASN, asn, header, node, dataASNLen) \
+ do { \
+ (dataASN)[node].noOut = 1; \
+ if (header) { \
+ int ii; \
+ for (ii = (node) + 1; ii < (int)(dataASNLen); ii++) { \
+ if ((asn)[ii].depth <= (asn)[node].depth) \
+ break; \
+ (dataASN)[ii].noOut = 1; \
+ } \
+ } \
+ } \
+ while (0)
+
/* Set the node and all nodes below to not be encoded.
*
* @param [in] dataASN Dynamic ASN data item.
@@ -732,16 +794,7 @@ WOLFSSL_LOCAL void SetASN_OID(ASNSetData *dataASN, int oid, int oidType);
* @param [in] dataASNLen Number of items in dataASN.
*/
#define SetASNItem_NoOutNode(dataASN, asn, node, dataASNLen) \
- do { \
- int ii; \
- (dataASN)[node].noOut = 1; \
- for (ii = (node) + 1; ii < (int)(dataASNLen); ii++) { \
- if ((asn)[ii].depth <= (asn)[node].depth) \
- break; \
- (dataASN)[ii].noOut = 1; \
- } \
- } \
- while (0)
+ SetASNItem_NoOutNode_ex(dataASN, asn, 1, node, dataASNLen)
#endif /* WOLFSSL_ASN_TEMPLATE */
@@ -1850,6 +1903,8 @@ struct DecodedCert {
byte policyConstSkip; /* Policy Constraints skip certs value */
word16 extKeyUsage; /* Key usage bitfield */
byte extExtKeyUsage; /* Extended Key usage bitfield */
+ word32 extExtKeyUsageOidCnt; /* Number of EKU KeyPurposeIds seen,
+ * recognized or not */
#ifdef WOLFSSL_WOLFSSH
byte extExtKeyUsageSsh; /* Extended Key Usage bitfield for SSH */
#endif /* WOLFSSL_WOLFSSH */
@@ -2471,7 +2526,7 @@ WOLFSSL_LOCAL int DecodeKeyUsage(const byte* input, word32 sz,
WOLFSSL_LOCAL int DecodeExtKeyUsage(const byte* input, word32 sz,
const byte **extExtKeyUsageSrc, word32 *extExtKeyUsageSz,
word32 *extExtKeyUsageCount, byte *extExtKeyUsage,
- byte *extExtKeyUsageSsh);
+ byte *extExtKeyUsageSsh, word32 *extExtKeyUsageOidCnt);
WOLFSSL_LOCAL int TryDecodeRPKToKey(DecodedCert* cert);
WOLFSSL_LOCAL int wc_GetPubX509(DecodedCert* cert, int verify, int* badDate);
@@ -2528,7 +2583,8 @@ WOLFSSL_LOCAL int GetTimeString(byte* date, int format, char* buf, int len,
#endif
#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && \
!defined(TIME_OVERRIDES) && (defined(OPENSSL_EXTRA) || \
- defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER))
+ defined(HAVE_PKCS7) || defined(HAVE_OCSP_RESPONDER) || \
+ defined(WOLFSSL_TSP))
WOLFSSL_LOCAL int GetFormattedTime(void* currTime, byte* buf, word32 len);
WOLFSSL_LOCAL int GetAsnTimeString(void* currTime, byte* buf, word32 len);
WOLFSSL_LOCAL int GetFormattedTime_ex(void* currTime, byte* buf, word32 len, byte format);
diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h
index 5b089f118b4..9bc5ff7bec9 100644
--- a/wolfssl/wolfcrypt/error-crypt.h
+++ b/wolfssl/wolfcrypt/error-crypt.h
@@ -328,8 +328,11 @@ enum wolfCrypt_ErrorCodes {
DRBG_SHA512_KAT_FIPS_E = -1017, /* SHA-512 DRBG KAT failure */
SLH_DSA_KAT_FIPS_E = -1018, /* SLH-DSA CAST KAT failure */
- WC_SPAN2_LAST_E = -1018, /* Update to indicate last used error code */
- WC_LAST_E = -1018, /* the last code used either here or in
+ TSP_VERIFY_E = -1019, /* TSP token invalid or response doesn't
+ * match request */
+
+ WC_SPAN2_LAST_E = -1019, /* Update to indicate last used error code */
+ WC_LAST_E = -1019, /* the last code used either here or in
* error-ssl.h */
WC_SPAN2_MIN_CODE_E = -1999, /* Last usable code in span 2 */
diff --git a/wolfssl/wolfcrypt/include.am b/wolfssl/wolfcrypt/include.am
index 9635e1a6cfd..b6936f73c38 100644
--- a/wolfssl/wolfcrypt/include.am
+++ b/wolfssl/wolfcrypt/include.am
@@ -7,6 +7,7 @@ nobase_include_HEADERS+= \
wolfssl/wolfcrypt/ascon.h \
wolfssl/wolfcrypt/asn.h \
wolfssl/wolfcrypt/asn_public.h \
+ wolfssl/wolfcrypt/tsp.h \
wolfssl/wolfcrypt/poly1305.h \
wolfssl/wolfcrypt/camellia.h \
wolfssl/wolfcrypt/cmac.h \
diff --git a/wolfssl/wolfcrypt/oid_sum.h b/wolfssl/wolfcrypt/oid_sum.h
index d56405c8247..c24dd97d17b 100644
--- a/wolfssl/wolfcrypt/oid_sum.h
+++ b/wolfssl/wolfcrypt/oid_sum.h
@@ -1786,6 +1786,8 @@ enum PKCS7_TYPES {
FIRMWARE_PKG_DATA = 685, /* 1.2.840.113549.1.9.16.1.16 */
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x01,0x17 */
AUTH_ENVELOPED_DATA = 692, /* 1.2.840.113549.1.9.16.1.23 */
+ /* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x01,0x04 */
+ TSTINFO_DATA = 673, /* 1.2.840.113549.1.9.16.1.4 */
/* 0x60,0x86,0x48,0x01,0x65,0x02,0x01,0x02,0x4e,0x02 */
ENCRYPTED_KEY_PACKAGE = 489 /* 2.16.840.1.101.2.1.2.78.2 */
#else
@@ -1809,6 +1811,8 @@ enum PKCS7_TYPES {
FIRMWARE_PKG_DATA = 0x70a68a32, /* 1.2.840.113549.1.9.16.1.16 */
/* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x01,0x17 */
AUTH_ENVELOPED_DATA = 0x70a18a32, /* 1.2.840.113549.1.9.16.1.23 */
+ /* 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x10,0x01,0x04 */
+ TSTINFO_DATA = 0x70b28a32, /* 1.2.840.113549.1.9.16.1.4 */
/* 0x60,0x86,0x48,0x01,0x65,0x02,0x01,0x02,0x4e,0x02 */
ENCRYPTED_KEY_PACKAGE = 0x034986b4 /* 2.16.840.1.101.2.1.2.78.2 */
#endif
diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h
index 9f699145847..f50cdce842a 100644
--- a/wolfssl/wolfcrypt/settings.h
+++ b/wolfssl/wolfcrypt/settings.h
@@ -3726,6 +3726,30 @@
#error "Attribute Certificate support requires the ASN.1 template feature."
#endif
+#if defined(WOLFSSL_TSP) && !defined(WOLFSSL_ASN_TEMPLATE)
+ #error "Time-Stamp Protocol support requires the ASN.1 template feature."
+#endif
+
+#ifdef WOLFSSL_TSP
+ /* Time-Stamp Protocol roles: requester (create requests, verify
+ * responses), verifier (verify tokens/responses only) and responder
+ * (read requests, create responses). When none is chosen, requester and
+ * responder are enabled. */
+ #if !defined(WOLFSSL_TSP_REQUESTER) && !defined(WOLFSSL_TSP_RESPONDER) && \
+ !defined(WOLFSSL_TSP_VERIFIER)
+ #define WOLFSSL_TSP_REQUESTER
+ #define WOLFSSL_TSP_RESPONDER
+ #endif
+ /* A requester also verifies the responses it receives. */
+ #if defined(WOLFSSL_TSP_REQUESTER) && !defined(WOLFSSL_TSP_VERIFIER)
+ #define WOLFSSL_TSP_VERIFIER
+ #endif
+
+ /* Encoding the accuracy seconds of a TSTInfo needs 32-bit numbers. */
+ #undef WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+ #define WOLFSSL_ASN_TEMPLATE_NEED_SET_INT32
+#endif
+
#if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
#undef WOLFSSL_ASN_ALL
#define WOLFSSL_ASN_ALL
diff --git a/wolfssl/wolfcrypt/tsp.h b/wolfssl/wolfcrypt/tsp.h
new file mode 100644
index 00000000000..4e596490694
--- /dev/null
+++ b/wolfssl/wolfcrypt/tsp.h
@@ -0,0 +1,456 @@
+/* tsp.h
+ *
+ * Copyright (C) 2006-2026 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 3 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
+ */
+
+/*!
+ \file wolfssl/wolfcrypt/tsp.h
+
+ Time-Stamp Protocol (TSP) message encoding and decoding. RFC 3161.
+*/
+
+#ifndef WOLF_CRYPT_TSP_H
+#define WOLF_CRYPT_TSP_H
+
+#include
+
+#ifdef WOLFSSL_TSP
+
+#include
+#include
+#ifdef HAVE_PKCS7
+ #include
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Version of TimeStampReq and TSTInfo supported. RFC 3161, 2.4.1 and 2.4.2. */
+#define WC_TSP_VERSION 1
+
+/* PKIStatus values. RFC 3161, 2.4.2. */
+enum TspPkiStatus {
+ WC_TSP_PKISTATUS_GRANTED = 0,
+ WC_TSP_PKISTATUS_GRANTED_WITH_MODS = 1,
+ WC_TSP_PKISTATUS_REJECTION = 2,
+ WC_TSP_PKISTATUS_WAITING = 3,
+ WC_TSP_PKISTATUS_REVOCATION_WARNING = 4,
+ WC_TSP_PKISTATUS_REVOCATION_NOTIFICATION = 5
+};
+
+/* PKIFailureInfo flags. RFC 3161, 2.4.2.
+ * The value is the BIT STRING's data as a big-endian number left-aligned to
+ * 32 bits - named bit n is bit (31 - n) of the value. */
+#define WC_TSP_FAIL_BAD_ALG 0x80000000UL /* bit 0 */
+#define WC_TSP_FAIL_BAD_REQUEST 0x20000000UL /* bit 2 */
+#define WC_TSP_FAIL_BAD_DATA_FORMAT 0x04000000UL /* bit 5 */
+#define WC_TSP_FAIL_TIME_NOT_AVAILABLE 0x00020000UL /* bit 14 */
+#define WC_TSP_FAIL_UNACCEPTED_POLICY 0x00010000UL /* bit 15 */
+#define WC_TSP_FAIL_UNACCEPTED_EXTENSION 0x00008000UL /* bit 16 */
+#define WC_TSP_FAIL_ADD_INFO_NOT_AVAILABLE 0x00004000UL /* bit 17 */
+#define WC_TSP_FAIL_SYSTEM_FAILURE 0x00000040UL /* bit 25 */
+
+/* Maximum length of the hash in a MessageImprint: SHA-512 is the longest
+ * hash supported. */
+#define WC_TSP_MAX_HASH_SZ 64
+
+/* Maximum length of a nonce in a TimeStampReq: OpenSSL sends 8 bytes. */
+#ifndef MAX_TS_NONCE_SZ
+ #define MAX_TS_NONCE_SZ 32
+#endif
+
+/* Minimum security strength in bits of the hash algorithms accepted when
+ * verifying a token - the collision resistance of a hash is half the
+ * digest length in bits. 0 means any available hash algorithm is accepted.
+ * e.g. 128 requires SHA-256 or longer. */
+#ifndef WC_TSP_MIN_HASH_STRENGTH_BITS
+ #define WC_TSP_MIN_HASH_STRENGTH_BITS 0
+#endif
+
+/* MessageImprint. RFC 3161, 2.4.1.
+ * Hash algorithm and hash of the data to be time-stamped. */
+typedef struct TspMessageImprint {
+ /* Hash algorithm OID sum: SHA256h, SHA384h, etc. On decode, checked
+ * against the known hash algorithm OIDs - an OID not known is accepted
+ * and the caller checks the sum is one it supports. */
+ word32 hashAlgOID;
+ /* Hash of message - copied into on decode. */
+ byte hash[WC_TSP_MAX_HASH_SZ];
+ /* Length of hash in bytes. */
+ word32 hashSz;
+} TspMessageImprint;
+
+/* Accuracy of time in TSTInfo. RFC 3161, 2.4.2.
+ * A value of 0 in a field means not present. */
+typedef struct TspAccuracy {
+ /* Accuracy in seconds. */
+ word32 seconds;
+ /* Accuracy in milliseconds: 1..999. */
+ word16 millis;
+ /* Accuracy in microseconds: 1..999. */
+ word16 micros;
+} TspAccuracy;
+
+/* TimeStampReq. RFC 3161, 2.4.1.
+ * Extensions are not supported - decode fails when present. */
+typedef struct TspRequest {
+ /* Version: must be 1. Set on decode - always 1 on encode. */
+ byte version;
+ /* Hash algorithm and hash of data to be time-stamped. */
+ TspMessageImprint imprint;
+ /* Optional TSA policy ID: content of OBJECT IDENTIFIER - copied into on
+ * decode. Not present when policySz is 0. */
+ byte policy[MAX_OID_SZ];
+ word32 policySz;
+ /* Optional nonce: big-endian number - must not have a leading zero
+ * byte, checked on encode. Copied into on decode. Not present when
+ * nonceSz is 0. */
+ byte nonce[MAX_TS_NONCE_SZ];
+ word32 nonceSz;
+ /* Request TSA certificate to be included in response when true. */
+ byte certReq;
+} TspRequest;
+
+/* TSTInfo. RFC 3161, 2.4.2.
+ * Content of the time-stamp token signed by the TSA.
+ * On decode, pointers reference into the message buffer - the buffer must
+ * remain available while the structure is in use.
+ * Extensions are not supported - decode fails when present. */
+typedef struct TspTstInfo {
+ /* Version: must be 1. Set on decode - always 1 on encode. */
+ byte version;
+ /* TSA policy ID: content of OBJECT IDENTIFIER. */
+ const byte* policy;
+ word32 policySz;
+ /* Hash algorithm and hash of data time-stamped. */
+ TspMessageImprint imprint;
+ /* Serial number of time-stamp: big-endian number, up to 160 bits -
+ * must not have a leading zero byte, checked on encode. */
+ const byte* serial;
+ word32 serialSz;
+ /* Time of time-stamp as a GeneralizedTime string of RFC 3161:
+ * "YYYYMMDDhhmmss[.s...]Z" - syntax checked on encode and decode.
+ * When NULL on encode, the current time is used (requires a real time
+ * clock - not available with NO_ASN_TIME). */
+ const byte* genTime;
+ word32 genTimeSz;
+ /* Optional accuracy of time. */
+ TspAccuracy accuracy;
+ /* Time-stamps from TSA are strictly ordered when true. */
+ byte ordering;
+ /* Optional nonce: big-endian number, must match request's nonce
+ * exactly - must not have a leading zero byte, checked on encode. NULL
+ * when not present. */
+ const byte* nonce;
+ word32 nonceSz;
+ /* Optional name of TSA: DER encoding of GeneralName. NULL when not
+ * present. */
+ const byte* tsa;
+ word32 tsaSz;
+} TspTstInfo;
+
+/* TimeStampResp. RFC 3161, 2.4.2.
+ * PKIStatusInfo and optional TimeStampToken.
+ * On decode, pointers reference into the message buffer - the buffer must
+ * remain available while the structure is in use. */
+typedef struct TspResponse {
+ /* PKIStatus value. See TspPkiStatus. */
+ byte status;
+ /* Optional status text, UTF-8 encoded, no NUL terminator.
+ * On encode, placed in a PKIFreeText with one UTF8String.
+ * On decode, references the first UTF8String of the PKIFreeText. */
+ const byte* statusString;
+ word32 statusStringSz;
+ /* Optional failure information: WC_TSP_FAIL_* flags. 0 when not
+ * present. */
+ word32 failInfo;
+ /* Optional time-stamp token: DER encoding of ContentInfo containing
+ * CMS SignedData with TSTInfo content. NULL when not present. */
+ const byte* token;
+ word32 tokenSz;
+} TspResponse;
+
+
+/* TimeStampReq encoding, decoding and operations. */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API int wc_TspRequest_Init(TspRequest* req);
+#endif /* WOLFSSL_TSP_REQUESTER */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API int wc_TspRequest_SetHashType(TspRequest* req,
+ enum wc_HashType hashType);
+#endif /* WOLFSSL_TSP_REQUESTER */
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspRequest_GetHashType(const TspRequest* req,
+ enum wc_HashType* hashType);
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspRequest_GetHash(const TspRequest* req, byte* hash,
+ word32* hashSz);
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API int wc_TspRequest_SetHash(TspRequest* req, const byte* hash,
+ word32 hashSz);
+#endif /* WOLFSSL_TSP_REQUESTER */
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspRequest_GetNonce(const TspRequest* req, byte* nonce,
+ word32* nonceSz);
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API int wc_TspRequest_SetNonce(TspRequest* req, const byte* nonce,
+ word32 nonceSz);
+#endif /* WOLFSSL_TSP_REQUESTER */
+#if defined(WOLFSSL_TSP_REQUESTER) && !defined(WC_NO_RNG)
+WOLFSSL_API int wc_TspRequest_GenerateNonce(TspRequest* req, WC_RNG* rng,
+ word32 sz);
+#endif /* WOLFSSL_TSP_REQUESTER && !WC_NO_RNG */
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspRequest_GetPolicy(const TspRequest* req, byte* policy,
+ word32* policySz);
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API int wc_TspRequest_SetPolicy(TspRequest* req, const byte* policy,
+ word32 policySz);
+#endif /* WOLFSSL_TSP_REQUESTER */
+/* The requester sets certReq and reads it back; the responder reads it from a
+ * received request. */
+#if defined(WOLFSSL_TSP_REQUESTER) || defined(WOLFSSL_TSP_RESPONDER)
+/* Get the certReq flag of a TimeStampReq.
+ *
+ * A non-zero value means the requester asked for the TSA certificate to be
+ * included in the response.
+ *
+ * @param [in] req TimeStampReq object.
+ * @return Non-zero when the TSA certificate is requested, 0 otherwise.
+ */
+#define wc_TspRequest_GetCertReq(req) ((req)->certReq)
+#endif /* WOLFSSL_TSP_REQUESTER || WOLFSSL_TSP_RESPONDER */
+#ifdef WOLFSSL_TSP_REQUESTER
+/* Set the certReq flag of a TimeStampReq.
+ *
+ * A non-zero value requests the TSA certificate to be included in the
+ * response. Any non-zero value is normalized to 1.
+ *
+ * @param [in, out] req TimeStampReq object.
+ * @param [in] val Non-zero to request the TSA certificate, 0 otherwise.
+ */
+#define wc_TspRequest_SetCertReq(req, val) \
+ ((req)->certReq = (byte)((val) != 0))
+#endif /* WOLFSSL_TSP_REQUESTER */
+#ifdef WOLFSSL_TSP_REQUESTER
+WOLFSSL_API int wc_TspRequest_Encode(const TspRequest* req, byte* out,
+ word32* outSz);
+#endif /* WOLFSSL_TSP_REQUESTER */
+WOLFSSL_API int wc_TspRequest_Decode(TspRequest* req, const byte* input,
+ word32 inSz);
+
+/* TSTInfo encoding, decoding and operations. */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_Init(TspTstInfo* tstInfo);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetSerial(const TspTstInfo* tstInfo,
+ const byte** serial, word32* serialSz);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetSerial(TspTstInfo* tstInfo,
+ const byte* serial, word32 serialSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetPolicy(const TspTstInfo* tstInfo,
+ const byte** policy, word32* policySz);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetPolicy(TspTstInfo* tstInfo,
+ const byte* policy, word32 policySz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetMsgImprint(const TspTstInfo* tstInfo,
+ word32* hashOID, const byte** hash, word32* hashSz);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetMsgImprint(TspTstInfo* tstInfo,
+ word32 hashOID, const byte* hash, word32 hashSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetGenTime(const TspTstInfo* tstInfo,
+ const byte** genTime, word32* genTimeSz);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetGenTime(TspTstInfo* tstInfo,
+ const byte* genTime, word32 genTimeSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#ifndef NO_ASN_TIME
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetGenTimeAsTime(const TspTstInfo* tstInfo,
+ time_t* t);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetGenTimeAsTime(TspTstInfo* tstInfo, time_t t,
+ byte* buf, word32 bufSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#endif /* !NO_ASN_TIME */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetAccuracy(const TspTstInfo* tstInfo,
+ word32* seconds, word16* millis, word16* micros);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetAccuracy(TspTstInfo* tstInfo,
+ word32 seconds, word16 millis, word16 micros);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetNonce(const TspTstInfo* tstInfo,
+ const byte** nonce, word32* nonceSz);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetNonce(TspTstInfo* tstInfo,
+ const byte* nonce, word32 nonceSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspTstInfo_GetTsa(const TspTstInfo* tstInfo,
+ const byte** tsa, word32* tsaSz);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetTsa(TspTstInfo* tstInfo, const byte* tsa,
+ word32 tsaSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+/* Set the TSTInfo's values to respond to a request - echoes the request's
+ * imprint and nonce and sets the TSA's policy, serial and time. */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_SetFromRequest(TspTstInfo* tstInfo,
+ const TspRequest* req, const byte* policy, word32 policySz,
+ const byte* serial, word32 serialSz, const byte* genTime,
+ word32 genTimeSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspTstInfo_Encode(const TspTstInfo* tstInfo, byte* out,
+ word32* outSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspTstInfo_Decode(TspTstInfo* tstInfo, const byte* input,
+ word32 inSz);
+#endif /* WOLFSSL_TSP_VERIFIER */
+#if !defined(NO_ASN_TIME) && !defined(USER_TIME) && !defined(TIME_OVERRIDES)
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspTstInfo_CheckGenTime(const TspTstInfo* tstInfo,
+ word32 tolerance);
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif
+/* Check TSTInfo from a response against the request sent. */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspTstInfo_CheckRequest(const TspTstInfo* tstInfo,
+ const TspRequest* req);
+#endif /* WOLFSSL_TSP_VERIFIER */
+/* Check the TSA name of a TSTInfo is the expected name. */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspTstInfo_CheckTsaName(const TspTstInfo* tstInfo,
+ const byte* tsa, word32 tsaSz);
+#endif /* WOLFSSL_TSP_VERIFIER */
+/* Verify the message imprint of a TSTInfo against the original data. */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspTstInfo_VerifyData(const TspTstInfo* tstInfo,
+ const byte* data, word32 dataSz);
+#endif /* WOLFSSL_TSP_VERIFIER */
+#ifdef HAVE_PKCS7
+/* Internal: id-aa-signingCertificateV2 attribute OID. Defined in asn_tsp.c;
+ * used by token creation (tsp.c) and the signing-certificate check
+ * (asn_tsp.c). */
+extern const byte tspSigningCertV2Oid[13];
+/* TimeStampToken creation (responder) using CMS SignedData. */
+#ifdef WOLFSSL_TSP_RESPONDER
+/* Create a token with the TSA's certificate and key - manages the PKCS7
+ * object. */
+WOLFSSL_API int wc_TspTstInfo_Sign(const TspTstInfo* tstInfo,
+ const byte* cert, word32 certSz, const byte* key, word32 keySz,
+ enum wc_PkType keyType, enum wc_HashType hashType, WC_RNG* rng,
+ byte* out, word32* outSz);
+WOLFSSL_API int wc_TspTstInfo_SignWithPkcs7(const TspTstInfo* tstInfo,
+ wc_PKCS7* pkcs7, byte* out, word32* outSz);
+/* Internal: encode a SigningCertificateV2 signed attribute - asn_tsp.c. */
+WOLFSSL_LOCAL int TspEncodeSigningCertV2(int hashOID, const byte* cert,
+ word32 certSz, byte* out, word32* outSz, void* heap);
+#endif /* WOLFSSL_TSP_RESPONDER */
+/* TimeStampToken verification (requester) using CMS SignedData. */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspTstInfo_VerifyWithPKCS7(wc_PKCS7* pkcs7, byte* token,
+ word32 tokenSz, TspTstInfo* tstInfo);
+/* Internal helper in tsp.c, also used by TspCheckSigningCertAttr (asn_tsp.c). */
+WOLFSSL_LOCAL int Tsp_CheckHashStrength(word32 hashOID);
+/* Internal helpers in asn_tsp.c used by wc_TspTstInfo_VerifyWithPKCS7 (tsp.c). */
+WOLFSSL_LOCAL int TspCheckOneSignerInfo(const byte* token, word32 tokenSz,
+ void* heap);
+WOLFSSL_LOCAL int TspCheckSigningCertAttr(wc_PKCS7* pkcs7);
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif /* HAVE_PKCS7 */
+
+/* TimeStampResp encoding, decoding and operations. */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspResponse_Init(TspResponse* resp);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspResponse_Encode(const TspResponse* resp, byte* out,
+ word32* outSz);
+#endif /* WOLFSSL_TSP_RESPONDER */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspResponse_Decode(TspResponse* resp, const byte* input,
+ word32 inSz);
+#endif /* WOLFSSL_TSP_VERIFIER */
+/* Get and set the status information of a TimeStampResp. */
+#if defined(WOLFSSL_TSP_RESPONDER) || defined(WOLFSSL_TSP_VERIFIER)
+WOLFSSL_API int wc_TspResponse_GetStatus(const TspResponse* resp,
+ word32* status, const byte** str, word32* strSz, word32* failInfo);
+#endif /* WOLFSSL_TSP_RESPONDER || WOLFSSL_TSP_VERIFIER */
+#ifdef WOLFSSL_TSP_RESPONDER
+WOLFSSL_API int wc_TspResponse_SetStatus(TspResponse* resp, word32 status,
+ const byte* str, word32 strSz, word32 failInfo);
+#endif /* WOLFSSL_TSP_RESPONDER */
+/* Human-readable strings for a PKIStatus and a PKIFailureInfo flag. */
+WOLFSSL_API const char* wc_TspStatus_ToString(word32 status);
+WOLFSSL_API const char* wc_TspFailInfo_ToString(word32 failInfo);
+#ifdef HAVE_PKCS7
+/* Verify a response's token with the TSA's certificate - manages the PKCS7
+ * object. */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspResponse_Verify(TspResponse* resp, const byte* cert,
+ word32 certSz, TspTstInfo* tstInfo);
+#endif /* WOLFSSL_TSP_VERIFIER */
+/* Verify a response's token, trusting the signer via a certificate manager
+ * (WOLFSSL_CERT_MANAGER, passed as void* to avoid an SSL layer dependency). */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspResponse_VerifyWithCm(TspResponse* resp, void* cm,
+ TspTstInfo* tstInfo);
+#endif /* WOLFSSL_TSP_VERIFIER */
+/* Verify a response's token and that it is over the given data. */
+#ifdef WOLFSSL_TSP_VERIFIER
+WOLFSSL_API int wc_TspResponse_VerifyData(TspResponse* resp, const byte* cert,
+ word32 certSz, const byte* data, word32 dataSz, TspTstInfo* tstInfo);
+#endif /* WOLFSSL_TSP_VERIFIER */
+#endif /* HAVE_PKCS7 */
+
+/* Internal: validate a genTime is a GeneralizedTime of RFC 3161. Shared by
+ * the message encoding/decoding (asn_tsp.c) and wc_TspTstInfo_CheckGenTime
+ * (tsp.c). */
+WOLFSSL_LOCAL int TspCheckGenTimeSyntax(const byte* t, word32 sz);
+
+#ifdef __cplusplus
+ } /* extern "C" */
+#endif
+
+#endif /* WOLFSSL_TSP */
+#endif /* WOLF_CRYPT_TSP_H */
diff --git a/wrapper/CSharp/wolfssl.vcxproj b/wrapper/CSharp/wolfssl.vcxproj
index 7a963cbd913..f7665e7778e 100644
--- a/wrapper/CSharp/wolfssl.vcxproj
+++ b/wrapper/CSharp/wolfssl.vcxproj
@@ -347,6 +347,7 @@
+