aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2019-10-25 05:31:06 -0400
committerJack Lloyd <[email protected]>2019-10-25 05:31:06 -0400
commitcc3caa20974e30b145a5e16135f85bf8a76a2233 (patch)
tree661ad6080317cb4b60f00eea282021255c952c73
parentfbbb4c211620e00b7c681a2281f815138a2024bd (diff)
parenta7b7fa47c7173b1c9e145cffe0ad1cbbc816f355 (diff)
Merge GH #2172 Add support for XMSS X.509 certs
-rw-r--r--src/build-data/oids.txt8
-rw-r--r--src/lib/asn1/oid_maps.cpp8
-rw-r--r--src/lib/pubkey/xmss/xmss_parameters.cpp2
-rw-r--r--src/lib/pubkey/xmss/xmss_privatekey.cpp30
-rw-r--r--src/lib/pubkey/xmss/xmss_privatekey.h1
-rw-r--r--src/lib/pubkey/xmss/xmss_publickey.cpp36
-rw-r--r--src/lib/pubkey/xmss/xmss_publickey.h32
-rw-r--r--src/lib/x509/x509_dn_ub.cpp2
-rw-r--r--src/lib/x509/x509_obj.cpp12
-rwxr-xr-xsrc/scripts/oids.py2
-rwxr-xr-xsrc/scripts/test_cli.py10
-rw-r--r--src/tests/data/x509/xmss/xmss_bouncycastle_sha256_10_root.pem64
-rw-r--r--src/tests/data/x509/xmss/xmss_isara_root.pem71
-rw-r--r--src/tests/test_x509_path.cpp48
14 files changed, 286 insertions, 40 deletions
diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt
index 063f52069..03ce80a84 100644
--- a/src/build-data/oids.txt
+++ b/src/build-data/oids.txt
@@ -13,10 +13,14 @@
1.3.6.1.4.1.3029.1.2.1 = ElGamal
1.3.6.1.4.1.25258.1.3 = McEliece
1.3.101.110 = Curve25519
-1.3.6.1.4.1.25258.1.5 = XMSS-draft6
-1.3.6.1.4.1.25258.1.8 = XMSS
1.3.101.112 = Ed25519
+# XMSS
+1.3.6.1.4.1.25258.1.5 = XMSS-draft6
+1.3.6.1.4.1.25258.1.8 = XMSS-draft12
+# draft-vangeest-x509-hash-sigs-03
+0.4.0.127.0.15.1.1.13.0 = XMSS
+
# X9.62 ecPublicKey, valid for ECDSA and ECDH (RFC 3279 sec 2.3.5)
1.2.840.10045.2.1 = ECDSA
1.3.132.1.12 = ECDH
diff --git a/src/lib/asn1/oid_maps.cpp b/src/lib/asn1/oid_maps.cpp
index 47d418271..d385dfd84 100644
--- a/src/lib/asn1/oid_maps.cpp
+++ b/src/lib/asn1/oid_maps.cpp
@@ -1,7 +1,7 @@
/*
* OID maps
*
-* This file was automatically generated by ./src/scripts/oids.py on 2019-08-03
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
*
* All manual edits to this file will be lost. Edit the script
* then regenerate this source file.
@@ -20,6 +20,7 @@ std::unordered_map<std::string, std::string> OIDS::load_oid2str_map()
{ "0.3.4401.5.3.1.9.26", "Camellia-192/GCM" },
{ "0.3.4401.5.3.1.9.46", "Camellia-256/GCM" },
{ "0.3.4401.5.3.1.9.6", "Camellia-128/GCM" },
+ { "0.4.0.127.0.15.1.1.13.0", "XMSS" },
{ "1.0.14888.3.0.5", "ECKCDSA" },
{ "1.2.156.10197.1.104.100", "SM4/OCB" },
{ "1.2.156.10197.1.104.2", "SM4/CBC" },
@@ -141,7 +142,7 @@ std::unordered_map<std::string, std::string> OIDS::load_oid2str_map()
{ "1.3.6.1.4.1.25258.1.3", "McEliece" },
{ "1.3.6.1.4.1.25258.1.5", "XMSS-draft6" },
{ "1.3.6.1.4.1.25258.1.6.1", "GOST-34.10-2012-256/EMSA1(SHA-256)" },
- { "1.3.6.1.4.1.25258.1.8", "XMSS" },
+ { "1.3.6.1.4.1.25258.1.8", "XMSS-draft12" },
{ "1.3.6.1.4.1.25258.3.1", "Serpent/CBC" },
{ "1.3.6.1.4.1.25258.3.101", "Serpent/GCM" },
{ "1.3.6.1.4.1.25258.3.102", "Twofish/GCM" },
@@ -470,7 +471,8 @@ std::unordered_map<std::string, OID> OIDS::load_str2oid_map()
{ "X520.StreetAddress", OID({2,5,4,9}) },
{ "X520.Surname", OID({2,5,4,4}) },
{ "X520.Title", OID({2,5,4,12}) },
- { "XMSS", OID({1,3,6,1,4,1,25258,1,8}) },
+ { "XMSS", OID({0,4,0,127,0,15,1,1,13,0}) },
+ { "XMSS-draft12", OID({1,3,6,1,4,1,25258,1,8}) },
{ "XMSS-draft6", OID({1,3,6,1,4,1,25258,1,5}) },
{ "brainpool160r1", OID({1,3,36,3,3,2,8,1,1,1}) },
{ "brainpool192r1", OID({1,3,36,3,3,2,8,1,1,3}) },
diff --git a/src/lib/pubkey/xmss/xmss_parameters.cpp b/src/lib/pubkey/xmss/xmss_parameters.cpp
index 933072da6..0654821fd 100644
--- a/src/lib/pubkey/xmss/xmss_parameters.cpp
+++ b/src/lib/pubkey/xmss/xmss_parameters.cpp
@@ -176,7 +176,7 @@ XMSS_Parameters::XMSS_Parameters(xmss_algorithm_t oid)
m_wots_oid = XMSS_WOTS_Parameters::ots_algorithm_t::WOTSP_SHAKE_512;
break;
default:
- throw Not_Implemented("Algorithm id does not match any known XMSS algorithm id.");
+ throw Not_Implemented("Algorithm id does not match any known XMSS algorithm id:" + std::to_string(oid));
break;
}
}
diff --git a/src/lib/pubkey/xmss/xmss_privatekey.cpp b/src/lib/pubkey/xmss/xmss_privatekey.cpp
index d4a353d3c..6a728ac72 100644
--- a/src/lib/pubkey/xmss/xmss_privatekey.cpp
+++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp
@@ -17,6 +17,7 @@
#include <botan/xmss_privatekey.h>
#include <botan/internal/xmss_signature_operation.h>
+#include <botan/ber_dec.h>
#if defined(BOTAN_HAS_THREAD_UTILS)
#include <botan/internal/thread_pool.h>
@@ -24,8 +25,27 @@
namespace Botan {
-XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& raw_key)
- : XMSS_PublicKey(unlock(raw_key)),
+namespace {
+
+// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
+secure_vector<uint8_t> extract_raw_key(const secure_vector<uint8_t>& key_bits)
+{
+ secure_vector<uint8_t> raw_key;
+ try
+ {
+ BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+ }
+ catch(Decoding_Error& e)
+ {
+ raw_key = key_bits;
+ }
+ return raw_key;
+}
+
+}
+
+XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& key_bits)
+ : XMSS_PublicKey(unlock(key_bits)),
XMSS_Common_Ops(XMSS_PublicKey::m_xmss_params.oid()),
m_wots_priv_key(m_wots_params.oid(), m_public_seed),
m_index_reg(XMSS_Index_Registry::get_instance())
@@ -40,12 +60,14 @@ XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& raw_key)
*/
static_assert(sizeof(size_t) >= 4, "size_t is big enough to support leaf index");
+ secure_vector<uint8_t> raw_key = extract_raw_key(key_bits);
+
if(raw_key.size() != XMSS_PrivateKey::size())
{
- throw Decoding_Error("Invalid XMSS private key size detected.");
+ throw Decoding_Error("Invalid XMSS private key size");
}
- // extract & copy unused leaf index from raw_key.
+ // extract & copy unused leaf index from raw_key
uint64_t unused_leaf = 0;
auto begin = (raw_key.begin() + XMSS_PublicKey::size());
auto end = raw_key.begin() + XMSS_PublicKey::size() + sizeof(uint32_t);
diff --git a/src/lib/pubkey/xmss/xmss_privatekey.h b/src/lib/pubkey/xmss/xmss_privatekey.h
index 2bfcbc82e..301fef04b 100644
--- a/src/lib/pubkey/xmss/xmss_privatekey.h
+++ b/src/lib/pubkey/xmss/xmss_privatekey.h
@@ -202,6 +202,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PrivateKey final : public virtual XMSS_PublicKe
secure_vector<uint8_t> private_key_bits() const override
{
+ return DER_Encoder().encode(raw_private_key(), OCTET_STRING).get_contents();
return raw_private_key();
}
diff --git a/src/lib/pubkey/xmss/xmss_publickey.cpp b/src/lib/pubkey/xmss/xmss_publickey.cpp
index 7c7c19fc1..43f7417cf 100644
--- a/src/lib/pubkey/xmss/xmss_publickey.cpp
+++ b/src/lib/pubkey/xmss/xmss_publickey.cpp
@@ -16,26 +16,48 @@
#include <botan/internal/xmss_verification_operation.h>
#include <botan/xmss_publickey.h>
+#include <botan/der_enc.h>
+#include <botan/ber_dec.h>
namespace Botan {
-XMSS_PublicKey::XMSS_PublicKey(const std::vector<uint8_t>& raw_key)
- : m_xmss_params(XMSS_PublicKey::deserialize_xmss_oid(raw_key)),
+namespace {
+
+// fall back to raw decoding for previous versions, which did not encode an OCTET STRING
+std::vector<uint8_t> extract_raw_key(const std::vector<uint8_t>& key_bits)
+{
+ std::vector<uint8_t> raw_key;
+ try
+ {
+ BER_Decoder(key_bits).decode(raw_key, OCTET_STRING);
+ }
+ catch(Decoding_Error& e)
+ {
+ raw_key = key_bits;
+ }
+ return raw_key;
+}
+
+}
+
+XMSS_PublicKey::XMSS_PublicKey(const std::vector<uint8_t>& key_bits)
+ : m_raw_key(extract_raw_key(key_bits)),
+ m_xmss_params(XMSS_PublicKey::deserialize_xmss_oid(m_raw_key)),
m_wots_params(m_xmss_params.ots_oid())
{
- if(raw_key.size() < XMSS_PublicKey::size())
+ if(m_raw_key.size() < XMSS_PublicKey::size())
{
- throw Decoding_Error("Invalid XMSS public key size detected.");
+ throw Decoding_Error("Invalid XMSS public key size detected");
}
- // extract & copy root from raw key.
+ // extract & copy root from raw key
m_root.clear();
m_root.reserve(m_xmss_params.element_size());
- auto begin = raw_key.begin() + sizeof(uint32_t);
+ auto begin = m_raw_key.begin() + sizeof(uint32_t);
auto end = begin + m_xmss_params.element_size();
std::copy(begin, end, std::back_inserter(m_root));
- // extract & copy public seed from raw key.
+ // extract & copy public seed from raw key
begin = end;
end = begin + m_xmss_params.element_size();
m_public_seed.clear();
diff --git a/src/lib/pubkey/xmss/xmss_publickey.h b/src/lib/pubkey/xmss/xmss_publickey.h
index ff56c1378..e055d3a3a 100644
--- a/src/lib/pubkey/xmss/xmss_publickey.h
+++ b/src/lib/pubkey/xmss/xmss_publickey.h
@@ -1,6 +1,7 @@
/*
* XMSS Public Key
* (C) 2016,2017 Matthias Gierlings
+ * (C) 2019 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
**/
@@ -29,8 +30,6 @@ class XMSS_Verification_Operation;
/**
* An XMSS: Extended Hash-Based Signature public key.
- * The XMSS public key does not support the X509 standard. Instead the
- * raw format described in [1] is used.
*
* [1] XMSS: Extended Hash-Based Signatures,
* Request for Comments: 8391
@@ -56,10 +55,14 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
m_public_seed(rng.random_vec(m_xmss_params.element_size())) {}
/**
- * Creates an XMSS public key from a byte sequence produced by
- * raw_private_key().
- **/
- XMSS_PublicKey(const std::vector<uint8_t>& raw_key);
+ * Loads a public key.
+ *
+ * Public key must be encoded as in RFC
+ * draft-vangeest-x509-hash-sigs-03.
+ *
+ * @param key_bits DER encoded public key bits
+ */
+ XMSS_PublicKey(const std::vector<uint8_t>& key_bits);
/**
* Creates a new XMSS public key for a chosen XMSS signature method as
@@ -121,7 +124,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
/**
* Retrieves the Winternitz One Time Signature (WOTS) method,
- * corrseponding to the chosen XMSS signature method.
+ * corresponding to the chosen XMSS signature method.
*
* @return XMSS WOTS signature method identifier.
**/
@@ -188,7 +191,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
AlgorithmIdentifier algorithm_identifier() const override
{
- return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_NULL_PARAM);
+ return AlgorithmIdentifier(get_oid(), AlgorithmIdentifier::USE_EMPTY_PARAM);
}
bool check_key(RandomNumberGenerator&, bool) const override
@@ -211,14 +214,16 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
}
/**
- * Returns a raw byte sequence as defined in [1].
- * This method acts as an alias for raw_public_key().
+ * Returns the encoded public key as defined in RFC
+ * draft-vangeest-x509-hash-sigs-03.
*
- * @return raw public key bits.
+ * @return encoded public key bits
**/
std::vector<uint8_t> public_key_bits() const override
{
- return raw_public_key();
+ std::vector<uint8_t> output;
+ DER_Encoder(output).encode(raw_public_key(), OCTET_STRING);
+ return output;
}
/**
@@ -233,7 +238,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
}
/**
- * Generates a non standardized byte sequence representing the XMSS
+ * Generates a byte sequence representing the XMSS
* public key, as defined in [1] (p. 23, "XMSS Public Key")
*
* @return 4-byte OID, followed by n-byte root node, followed by
@@ -242,6 +247,7 @@ class BOTAN_PUBLIC_API(2,0) XMSS_PublicKey : public virtual Public_Key
virtual std::vector<uint8_t> raw_public_key() const;
protected:
+ std::vector<uint8_t> m_raw_key;
XMSS_Parameters m_xmss_params;
XMSS_WOTS_Parameters m_wots_params;
secure_vector<uint8_t> m_root;
diff --git a/src/lib/x509/x509_dn_ub.cpp b/src/lib/x509/x509_dn_ub.cpp
index d8663124e..d41168b95 100644
--- a/src/lib/x509/x509_dn_ub.cpp
+++ b/src/lib/x509/x509_dn_ub.cpp
@@ -1,7 +1,7 @@
/*
* DN_UB maps: Upper bounds on the length of DN strings
*
-* This file was automatically generated by ./src/scripts/oids.py on 2019-08-04
+* This file was automatically generated by ./src/scripts/oids.py on 2019-10-21
*
* All manual edits to this file will be lost. Edit the script
* then regenerate this source file.
diff --git a/src/lib/x509/x509_obj.cpp b/src/lib/x509/x509_obj.cpp
index e6767ea98..644a1ca5e 100644
--- a/src/lib/x509/x509_obj.cpp
+++ b/src/lib/x509/x509_obj.cpp
@@ -192,7 +192,7 @@ Certificate_Status_Code X509_Object::verify_signature(const Public_Key& pub_key)
std::string padding;
if(sig_info.size() == 2)
padding = sig_info[1];
- else if(sig_info[0] == "Ed25519")
+ else if(sig_info[0] == "Ed25519" || sig_info[0] == "XMSS")
padding = "Pure";
else
return Certificate_Status_Code::SIGNATURE_ALGO_BAD_PARAMS;
@@ -313,6 +313,16 @@ std::string choose_sig_algo(AlgorithmIdentifier& sig_algo,
{
padding = "Pure";
}
+ else if(algo_name == "XMSS")
+ {
+ if(user_specified.empty() == true)
+ {
+ throw Invalid_Argument("XMSS requires padding scheme");
+ }
+ padding = user_specified;
+ sig_algo = AlgorithmIdentifier(OID::from_string("XMSS"), AlgorithmIdentifier::USE_EMPTY_PARAM);
+ return padding;
+ }
else
{
throw Invalid_Argument("Unknown X.509 signing key type: " + algo_name);
diff --git a/src/scripts/oids.py b/src/scripts/oids.py
index 7036dbab6..323b6efb5 100755
--- a/src/scripts/oids.py
+++ b/src/scripts/oids.py
@@ -256,7 +256,7 @@ bool sig_algo_and_pad_ok(const std::string algo, std::string padding)
def main(args = None):
- """ Print header files (oids.cpp, dn_ub.cpp) depending on the first argument and on srs/build-data/oids.txt
+ """ Print header files (oids.cpp, dn_ub.cpp) depending on the first argument and on src/build-data/oids.txt
Choose 'oids' to print oids.cpp, needs to be written to src/lib/asn1/oids.cpp
Choose 'dn_ub' to print dn_ub.cpp, needs to be written to src/lib/x509/X509_dn_ub.cpp
diff --git a/src/scripts/test_cli.py b/src/scripts/test_cli.py
index c0f8677d1..e4da5a898 100755
--- a/src/scripts/test_cli.py
+++ b/src/scripts/test_cli.py
@@ -361,27 +361,27 @@ def cli_xmss_sign_tests(tmp_dir):
test_cli("hash", ["--no-fsname", msg], "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855")
test_cli("keygen", ["--algo=XMSS", "--output=%s" % (priv_key)], "")
- test_cli("hash", ["--no-fsname", priv_key], "32397312E3FAC9D6396C55FEEFFF11EE195E2D2D5B34279D2544AF27763B0946")
+ test_cli("hash", ["--no-fsname", priv_key], "5B38F737BA41BE7F40433DB30EAEF7C41ABB0F7D9E7A09DEB5FDCE7B6811693F")
test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key, priv_key), "")
test_cli("fingerprint", ['--no-fsname', pub_key],
- "E2:BE:C8:6D:CF:4B:5D:67:AB:A1:C1:F8:36:79:D5:3B:D8:17:D5:E3:5B:BE:29:08:03:7E:6E:07:27:4E:16:46")
+ "B0:F4:98:6E:D8:4E:05:63:A1:D8:4B:37:61:5A:A0:41:78:7E:DE:0E:72:46:E0:A8:D6:CF:09:54:08:DA:A4:22")
# verify the key is updated after each signature:
test_cli("sign", [priv_key, msg, "--output=%s" % (sig1)], "")
test_cli("verify", [pub_key, msg, sig1], "Signature is valid")
test_cli("hash", ["--no-fsname", sig1], "04AF45451C7A9AF2D828E1AD6EC262E012436F4087C5DA6F32C689D781E597D0")
- test_cli("hash", ["--no-fsname", priv_key], "649E54D334F78A6AAAE34CFABF62121C74909D80E4DC2FA240A6EE1848526094")
+ test_cli("hash", ["--no-fsname", priv_key], "67929FAEC636E43DE828C1CD7E2D11CE7C3388CE90DD0A0F687C6627FFA850CD")
test_cli("sign", [priv_key, msg, "--output=%s" % (sig2)], "")
test_cli("verify", [pub_key, msg, sig2], "Signature is valid")
test_cli("hash", ["--no-fsname", sig2], "0785A6AD54CC7D01F2BE2BC6463A3EAA1159792E52210ED754992C5068E8F24F")
- test_cli("hash", ["--no-fsname", priv_key], "04483FA5367A7340F4BF6160FABD5742258009E05F9584E8D9732660B132608E")
+ test_cli("hash", ["--no-fsname", priv_key], "1940945D68B1CF54D79E05DD7913A4D0B4959183F1E12B81A4E43EF4E63FBD20")
# private key updates, public key is unchanged:
test_cli("pkcs8", "--pub-out --output=%s %s" % (pub_key2, priv_key), "")
test_cli("fingerprint", ['--no-fsname', pub_key2],
- "E2:BE:C8:6D:CF:4B:5D:67:AB:A1:C1:F8:36:79:D5:3B:D8:17:D5:E3:5B:BE:29:08:03:7E:6E:07:27:4E:16:46")
+ "B0:F4:98:6E:D8:4E:05:63:A1:D8:4B:37:61:5A:A0:41:78:7E:DE:0E:72:46:E0:A8:D6:CF:09:54:08:DA:A4:22")
def cli_pbkdf_tune_tests(_tmp_dir):
if not check_for_command("pbkdf_tune"):
diff --git a/src/tests/data/x509/xmss/xmss_bouncycastle_sha256_10_root.pem b/src/tests/data/x509/xmss/xmss_bouncycastle_sha256_10_root.pem
new file mode 100644
index 000000000..24db08b86
--- /dev/null
+++ b/src/tests/data/x509/xmss/xmss_bouncycastle_sha256_10_root.pem
@@ -0,0 +1,64 @@
+-----BEGIN CERTIFICATE-----
+MIILizCCAbGgAwIBAgIBATALBgkEAH8ADwEBDQAwgY8xCzAJBgNVBAYTAkFVMSgw
+JgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIwEAYDVQQH
+DAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBm
+ZWVkYmFjay1jcnlwdG9AYm91bmN5Y2FzdGxlLm9yZzAeFw0xOTEwMDgwNDQ0MjRa
+Fw0xOTEwMDgwNDQ2MDRaMIGPMQswCQYDVQQGEwJBVTEoMCYGA1UECgwfVGhlIExl
+Z2lvbiBvZiB0aGUgQm91bmN5IENhc3RsZTESMBAGA1UEBwwJTWVsYm91cm5lMREw
+DwYDVQQIDAhWaWN0b3JpYTEvMC0GCSqGSIb3DQEJARYgZmVlZGJhY2stY3J5cHRv
+QGJvdW5jeWNhc3RsZS5vcmcwVjALBgkEAH8ADwEBDQADRwAERAAAAAFWJwchaHXu
+HqYEWHM/sxvzb5uMNgsNnfzBWQ6ms4iX0CerRixLDoDVhhyVbQFyR2lzigXz1blz
+SbPHlBoWLRDiMAsGCQQAfwAPAQENAAOCCcUAAAAAAGScYlJ/B/oWlOBgnIGlnYhT
+EPDzn4sHDqic8HJOMMdkBsyGrR0XkqgRDnVI4PEctP8bndw7M327VGlM8Dpprcov
+lisvu7j8cV8J6Nksef/AxeIf5z+wsx0zKi7j/n1A3LkC1m3uclesJiifwfNzQYhu
+EFE87E6E7QJimr/6tzvU0jDOUB4Fcl/F3mze/c+vtR9xEjKdK2VC3Crftw8eo7jH
+kz1pz0JHHYCK2tKh9k3lYIqZMv0RQmfUT3HTQYf5nU+3gMmLUX3gDefZRVYTLX9T
+Q3/ctpbLkQ6KmW+8/3nD9X0d49C+pU6AyZHC+Ms7GN7igZhdPh8piI+0uUv1uKDv
+u+FZ/yLrLpQ9Ju/qISHXTc8q40ZXUAi77g1kWRaalXArY4j6JudIxJissWZt6w3q
+K0ouaygQQCjIydIYjKMRlf5k01basXgT8NyrVsZRl6zKvEO7HM5xgRwcB/2S5yO0
+Rec7anG2SBqL5EXRSTQQhX2cyw6tQU2zBfL6H37Gyf8ezPhdL19zHril2mQuOwgQ
+DQ/TsrU/+gfifn78JUDbdBoyflRQnrk0QDmKxOOTUnQXITDrC3773sIv2QUh3h4m
+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
+-----END CERTIFICATE-----
diff --git a/src/tests/data/x509/xmss/xmss_isara_root.pem b/src/tests/data/x509/xmss/xmss_isara_root.pem
new file mode 100644
index 000000000..07f3a6ab5
--- /dev/null
+++ b/src/tests/data/x509/xmss/xmss_isara_root.pem
@@ -0,0 +1,71 @@
+-----BEGIN CERTIFICATE-----
+MIIMwTCCAuegAwIBAgICH1AwCwYJBAB/AA8BAQ0AMIGeMQswCQYDVQQGEwJDQTEQ
+MA4GA1UECAwHT250YXJpbzERMA8GA1UEBwwIV2F0ZXJsb28xGjAYBgNVBAoMEUlT
+QVJBIENvcnBvcmF0aW9uMRcwFQYDVQQLDA5EZXZlbG9wZXIgVGVhbTENMAsGA1UE
+AwwEcm9vdDEmMCQGCSqGSIb3DQEJARYXcm9vdEBmYWtlLmlzYXJhY29ycC5jb20w
+HhcNMTkxMDA3MTg0MDAwWhcNMTkxMTA2MTg0MDAwWjCBnjELMAkGA1UEBhMCQ0Ex
+EDAOBgNVBAgMB09udGFyaW8xETAPBgNVBAcMCFdhdGVybG9vMRowGAYDVQQKDBFJ
+U0FSQSBDb3Jwb3JhdGlvbjEXMBUGA1UECwwORGV2ZWxvcGVyIFRlYW0xDTALBgNV
+BAMMBHJvb3QxJjAkBgkqhkiG9w0BCQEWF3Jvb3RAZmFrZS5pc2FyYWNvcnAuY29t
+MFYwCwYJBAB/AA8BAQ0AA0cABEQAAAABIp/dzWhnKoTmW30crhrq5TzXYp8k+7qK
+jk0x17pdgXMrvNkPrRUcK3Wgp/afgUAaGHlo9zuRckNBp7pPXGPmHaOCARMwggEP
+MB0GA1UdDgQWBBSYjbGo++onbH3AB7RVNQ9GrWGgHjCBzAYDVR0jBIHEMIHBgBSY
+jbGo++onbH3AB7RVNQ9GrWGgHqGBpKSBoTCBnjELMAkGA1UEBhMCQ0ExEDAOBgNV
+BAgMB09udGFyaW8xETAPBgNVBAcMCFdhdGVybG9vMRowGAYDVQQKDBFJU0FSQSBD
+b3Jwb3JhdGlvbjEXMBUGA1UECwwORGV2ZWxvcGVyIFRlYW0xDTALBgNVBAMMBHJv
+b3QxJjAkBgkqhkiG9w0BCQEWF3Jvb3RAZmFrZS5pc2FyYWNvcnAuY29tggIfUDAP
+BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjALBgkEAH8ADwEBDQADggnF
+AAAAAABDguB8TKHavz3/+LmBcG+TZGB9GzDMQJnINHW9egH4oB56LYdw49wBDcxo
++so3RLjOgaB2TBnR40QKowmTf8U4edk5IIfQbiwhQKLZC6vTHbiYD7cuLmtIzxx7
+12ko50ltsPRn/IXBq3JmNxDS0g78R2x6+V464C5PRETQ0SnRiY3yXNxXB9Lb2+sm
+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
+DeJ/1Y0=
+-----END CERTIFICATE-----
diff --git a/src/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp
index 8715f3f54..3e07832c3 100644
--- a/src/tests/test_x509_path.cpp
+++ b/src/tests/test_x509_path.cpp
@@ -28,7 +28,9 @@ namespace Botan_Tests {
namespace {
-#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+#if defined(BOTAN_HAS_X509_CERTIFICATES) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
+
+#if defined(BOTAN_HAS_RSA) && defined(BOTAN_HAS_EMSA_PKCS1)
std::map<std::string, std::string> read_results(const std::string& results_file, const char delim = ':')
{
@@ -118,7 +120,7 @@ class X509test_Path_Validation_Tests final : public Test
results.push_back(result);
}
- // test softfail
+ // test softfail
{
Test::Result result("X509test path validation softfail");
result.start_timer();
@@ -897,6 +899,48 @@ BOTAN_REGISTER_TEST("x509_path_with_ocsp", Path_Validation_With_OCSP_Tests);
#endif
+#if defined(BOTAN_HAS_XMSS_RFC8391)
+
+class XMSS_Path_Validation_Tests final : public Test
+ {
+ public:
+ Test::Result validate_self_signed(const std::string& name, const std::string& file)
+ {
+ Test::Result result(name);
+
+ Botan::Path_Validation_Restrictions restrictions;
+ auto self_signed = Botan::X509_Certificate(Test::data_dir() + "/x509/xmss/" + file);
+
+ auto cert_path = std::vector<std::shared_ptr<const Botan::X509_Certificate>>{
+ std::make_shared<const Botan::X509_Certificate>(self_signed)};
+ auto valid_time = Botan::calendar_point(2019, 10, 8, 4, 45, 0).to_std_timepoint();
+
+ auto status = Botan::PKIX::overall_status(Botan::PKIX::check_chain(cert_path, valid_time,
+ "", Botan::Usage_Type::UNSPECIFIED, restrictions.minimum_key_strength(), restrictions.trusted_hashes()));
+ result.test_eq("Cert validation status",
+ Botan::to_string(status), "Verified");
+ return result;
+ }
+
+ std::vector<Test::Result> run() override
+ {
+ if(Botan::has_filesystem_impl() == false)
+ {
+ return {Test::Result::Note("XMSS path validation",
+ "Skipping due to missing filesystem access")};
+ }
+
+ return {validate_self_signed("XMSS path validation with certificate created by ISARA corp", "xmss_isara_root.pem"),
+ validate_self_signed("XMSS path validation with certificate created by BouncyCastle", "xmss_bouncycastle_sha256_10_root.pem")};
+ }
+ };
+
+BOTAN_REGISTER_TEST("x509_path_xmss", XMSS_Path_Validation_Tests);
+
+#endif
+
+#endif
+
}
}