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/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 4b80e149bac..d0e58e17973 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 @@ -2015,6 +2016,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..439d099e320 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,44 @@ 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; + word32 hdrSz = 1 + SetLength(len, NULL); + word32 i = 0; + + a = wolfSSL_ASN1_INTEGER_new(); + if (a == NULL) + return NULL; + + /* Make sure there is space for the data, ASN.1 type and length. */ + if (wolfssl_asn1_integer_require_len(a, (int)(len + hdrSz), 0) != 1) { + wolfSSL_ASN1_INTEGER_free(a); + return NULL; + } + + a->data[i++] = ASN_INTEGER; + i += SetLength(len, a->data + i); + 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..2857d6d44bb --- /dev/null +++ b/src/ssl_tsp.c @@ -0,0 +1,2370 @@ +/* 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) + +#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) { + 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]; + byte buf[256]; + + /* 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; + } + /* 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, (int)sizeof(buf))) > 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); + + return ok; +} + +/* 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. + */ +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; + byte reqDer[2048]; + 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; + } + + /* Read the DER request from the BIO. */ + n = wolfSSL_BIO_read(req_bio, reqDer, (int)sizeof(reqDer)); + if (n <= 0) { + WOLFSSL_MSG("Failed to read TS request from BIO"); + return NULL; + } + reqSz = (word32)n; + + /* Decode the request and start a TSTInfo. */ + if ((wc_TspRequest_Decode(&req, reqDer, reqSz) != 0) || + (wc_TspTstInfo_Init(&tst) != 0)) { + return NULL; + } + + /* 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); + 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..e9eb67cbd51 --- /dev/null +++ b/tests/api/test_ossl_tsp.c @@ -0,0 +1,1853 @@ +/* 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()) { + ExpectIntEQ(num->length, 2 + (int)sizeof(tsOsslSerial)); + ExpectBufEQ(num->data + 2, 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()) { + ExpectIntEQ(num->length, 2 + (int)sizeof(tsOsslNonce)); + ExpectBufEQ(num->data + 2, 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); + } + + /* Hashing message data is not supported. */ + 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); + + /* Hashing message data is not supported. */ + 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..5a16a12d50c --- /dev/null +++ b/tests/api/test_tsp.c @@ -0,0 +1,3485 @@ +/* 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: 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 + }; + 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)); + + /* 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..663e1d776db 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 @@ -3998,6 +4000,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..e55fc496fcd --- /dev/null +++ b/wolfcrypt/src/asn_tsp.c @@ -0,0 +1,1439 @@ +/* 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 digits are not checked for valid ranges - values + * compare as strings. + * + * @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; + } + } + /* 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 18d7a339cd5..ae076d4d242 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 e95bba5ae5e..84a156c9a87 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)) @@ -67582,6 +67597,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/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..023906673ff --- /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) /* not supported */ +#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 */