diff options
author | René Korthaus <[email protected]> | 2019-10-19 07:20:50 +0200 |
---|---|---|
committer | René Korthaus <[email protected]> | 2019-10-21 16:56:24 +0200 |
commit | 84b8d3eeee5aaf29046091c0e675cfeb8c6434b8 (patch) | |
tree | ab56ae27ad340b1dc61eeaaffa25995fce8d30af | |
parent | cb34802ed24b0963ed5a0180236cabd593268987 (diff) |
Add support for XMSS X.509 certificates
-rw-r--r-- | src/build-data/oids.txt | 8 | ||||
-rw-r--r-- | src/lib/asn1/oid_maps.cpp | 8 | ||||
-rw-r--r-- | src/lib/pubkey/xmss/xmss_parameters.cpp | 2 | ||||
-rw-r--r-- | src/lib/pubkey/xmss/xmss_privatekey.cpp | 29 | ||||
-rw-r--r-- | src/lib/pubkey/xmss/xmss_privatekey.h | 1 | ||||
-rw-r--r-- | src/lib/pubkey/xmss/xmss_publickey.cpp | 35 | ||||
-rw-r--r-- | src/lib/pubkey/xmss/xmss_publickey.h | 32 | ||||
-rw-r--r-- | src/lib/x509/x509_dn_ub.cpp | 2 | ||||
-rw-r--r-- | src/lib/x509/x509_obj.cpp | 12 | ||||
-rwxr-xr-x | src/scripts/oids.py | 2 | ||||
-rw-r--r-- | src/tests/test_x509_path.cpp | 38 |
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 } |