aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRené Korthaus <[email protected]>2019-10-19 07:20:50 +0200
committerRené Korthaus <[email protected]>2019-10-21 16:56:24 +0200
commit84b8d3eeee5aaf29046091c0e675cfeb8c6434b8 (patch)
treeab56ae27ad340b1dc61eeaaffa25995fce8d30af
parentcb34802ed24b0963ed5a0180236cabd593268987 (diff)
Add support for XMSS X.509 certificates
-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.cpp29
-rw-r--r--src/lib/pubkey/xmss/xmss_privatekey.h1
-rw-r--r--src/lib/pubkey/xmss/xmss_publickey.cpp35
-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
-rw-r--r--src/tests/test_x509_path.cpp38
11 files changed, 135 insertions, 34 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..7f6cc5f6f 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,26 @@
namespace Botan {
-XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<uint8_t>& raw_key)
- : XMSS_PublicKey(unlock(raw_key)),
+namespace {
+
+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 +59,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..4db7a2232 100644
--- a/src/lib/pubkey/xmss/xmss_publickey.cpp
+++ b/src/lib/pubkey/xmss/xmss_publickey.cpp
@@ -16,26 +16,47 @@
#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 {
+
+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/tests/test_x509_path.cpp b/src/tests/test_x509_path.cpp
index 8715f3f54..49e8783f0 100644
--- a/src/tests/test_x509_path.cpp
+++ b/src/tests/test_x509_path.cpp
@@ -118,7 +118,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();
@@ -895,6 +895,42 @@ class Path_Validation_With_OCSP_Tests final : public Test
BOTAN_REGISTER_TEST("x509_path_with_ocsp", Path_Validation_With_OCSP_Tests);
+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
}