diff options
author | Matthias Gierlings <[email protected]> | 2016-10-08 20:45:53 +0200 |
---|---|---|
committer | Matthias Gierlings <[email protected]> | 2016-11-11 12:52:26 +0100 |
commit | 8b06b4fe5fbe189c7d5250becb189bf2b87b9013 (patch) | |
tree | c73fbb3bf4eddcddf1d3f37983799d80204e8f2c /src/lib/pubkey/xmss/xmss_privatekey.cpp | |
parent | 618f890fd7ede74c728612ca8bc590c72ee353f1 (diff) |
Added Extended Hash-Based Signatures (XMSS)
[1] XMSS: Extended Hash-Based Signatures,
draft-itrf-cfrg-xmss-hash-based-signatures-06
Release: July 2016.
https://datatracker.ietf.org/doc/
draft-irtf-cfrg-xmss-hash-based-signatures/?include_text=1
Provides XMSS_PublicKey and XMSS_PrivateKey classes as well as implementations
for the Botan interfaces PK_Ops::Signature and PK_Ops::Verification. XMSS has
been integrated into the Botan test bench, signature generation and verification
can be tested independently by invoking "botan-test xmss_sign" and
"botan-test xmss_verify"
- Some headers that are not required to be exposed to users of the library have
to be declared as public in `info.txt`. Declaring those headers private will
cause the amalgamation build to fail. The following headers have been
declared public inside `info.txt`, even though they are only intended for
internal use:
* atomic.h
* xmss_hash.h
* xmss_index_registry.h
* xmss_address.h
* xmss_common_ops.h
* xmss_tools.h
* xmss_wots_parameters.h
* xmss_wots_privatekey.h
* xmss_wots_publickey.h
- XMSS_Verification_Operation Requires the "randomness" parameter out of the
XMSS signature. "Randomness" is part of the prefix that is hashed *before*
the message. Since the signature is unknown till sign() is called, all
message content has to be buffered. For large messages this can be
inconvenient or impossible.
**Possible solution**: Change PK_Ops::Verification interface to take
the signature as constructor argument, and provide a setter method to be able
to update reuse the instance on multiple signatures. Make sign a parameterless
member call. This solution requires interface changes in botan.
**Suggested workaround** for signing large messages is to not sign the message
itself, but to precompute the message hash manually using Botan::HashFunctio
and sign the message hash instead of the message itself.
- Some of the available test vectors for the XMSS signature verification have
been commented out in order to reduce testbench runtime.
Diffstat (limited to 'src/lib/pubkey/xmss/xmss_privatekey.cpp')
-rw-r--r-- | src/lib/pubkey/xmss/xmss_privatekey.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/lib/pubkey/xmss/xmss_privatekey.cpp b/src/lib/pubkey/xmss/xmss_privatekey.cpp new file mode 100644 index 000000000..18d712a5f --- /dev/null +++ b/src/lib/pubkey/xmss/xmss_privatekey.cpp @@ -0,0 +1,188 @@ +/** + * XMSS Private Key + * An XMSS: Extended Hash-Based Siganture private key. + * The XMSS private key does not support the X509 and PKCS7 standard. Instead + * the raw format described in [1] is used. + * + * [1] XMSS: Extended Hash-Based Signatures, + * draft-itrf-cfrg-xmss-hash-based-signatures-06 + * Release: July 2016. + * https://datatracker.ietf.org/doc/ + * draft-irtf-cfrg-xmss-hash-based-signatures/?include_text=1 + * + * (C) 2016 Matthias Gierlings + * + * Botan is released under the Simplified BSD License (see license.txt) + **/ + +#include <botan/internal/xmss_signature_operation.h> +#include <botan/xmss_privatekey.h> + +namespace Botan { + +XMSS_PrivateKey::XMSS_PrivateKey(const secure_vector<byte>& raw_key) + : XMSS_PublicKey(raw_key), + 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()) + { + BOTAN_ASSERT(sizeof(size_t) >= ceil( + static_cast<float>(XMSS_PublicKey::m_xmss_params.tree_height()) / 8.f), + "System type \"size_t\" not big enough to support" + " leaf index."); + + if(raw_key.size() != size()) + { + throw Integrity_Failure("Invalid XMSS private key size detected."); + } + + // 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(uint64_t); + + for(auto& i = begin; i != end; i++) + unused_leaf = ((unused_leaf << 8) | *i); + + if(unused_leaf >= (1ull << (XMSS_PublicKey::m_xmss_params.tree_height() - 1))) + { + throw Integrity_Failure("XMSS private key leaf index out of " + "bounds."); + } + + begin = end; + end = begin + XMSS_PublicKey::m_xmss_params.element_size(); + m_prf.clear(); + m_prf.reserve(XMSS_PublicKey::m_xmss_params.element_size()); + std::copy(begin, end, std::back_inserter(m_prf)); + + begin = end; + end = begin + m_wots_params.element_size(); + m_wots_priv_key.set_private_seed(secure_vector<byte>(begin, end)); + set_unused_leaf_index(static_cast<size_t>(unused_leaf)); + } + +XMSS_PrivateKey::XMSS_PrivateKey( + XMSS_Parameters::xmss_algorithm_t xmss_algo_id, + RandomNumberGenerator& rng) + : XMSS_PublicKey(xmss_algo_id, rng), + XMSS_Common_Ops(xmss_algo_id), + m_wots_priv_key(XMSS_PublicKey::m_xmss_params.ots_oid(), + public_seed(), + rng), + m_prf(rng.random_vec(XMSS_PublicKey::m_xmss_params.element_size())), + m_index_reg(XMSS_Index_Registry::get_instance()) + { + XMSS_Address adrs; + set_root(tree_hash(0, + XMSS_PublicKey::m_xmss_params.tree_height(), + adrs)); + } + +secure_vector<byte> +XMSS_PrivateKey::tree_hash(size_t start_idx, + size_t target_node_height, + XMSS_Address& adrs) + { + const secure_vector<byte>& seed = this->public_seed(); + + BOTAN_ASSERT((start_idx % (1 << target_node_height)) == 0, + "Start index must be divisible by 2^{target node height}."); + + std::vector<secure_vector<byte>> nodes( + XMSS_PublicKey::m_xmss_params.tree_height() + 1, + secure_vector<byte>(XMSS_PublicKey::m_xmss_params.element_size())); + + // node stack, holds all nodes on stack and one extra "pending" node. This + // temporary node referred to as "node" in the XMSS standard document stays + // a pending element, meaning it is not regarded as element on the stack + // until level is increased. + std::vector<byte> node_levels(XMSS_PublicKey::m_xmss_params.tree_height() + 1); + + byte level = 0; + XMSS_WOTS_PublicKey pk(m_wots_priv_key.wots_parameters().oid(), seed); + + size_t last_idx = static_cast<size_t>(1 << target_node_height) + start_idx; + for(size_t i = start_idx; i < last_idx; i++) + { + adrs.set_type(XMSS_Address::Type::OTS_Hash_Address); + adrs.set_ots_address(i); + this->wots_private_key().generate_public_key( + pk, + // getWOTS_SK(SK, s + i), reference implementation uses adrs + // instead of zero padded index s + i. + this->wots_private_key()[adrs], + adrs); + adrs.set_type(XMSS_Address::Type::LTree_Address); + adrs.set_ltree_address(i); + create_l_tree(nodes[level], pk, adrs, seed); + node_levels[level] = 0; + + adrs.set_type(XMSS_Address::Type::Hash_Tree_Address); + adrs.set_tree_height(0); + adrs.set_tree_index(i); + + while(level > 0 && node_levels[level] == + node_levels[level - 1]) + { + adrs.set_tree_index(((adrs.get_tree_index() - 1) >> 1)); + randomize_tree_hash(nodes[level - 1], + nodes[level - 1], + nodes[level], + adrs, + seed); + node_levels[level - 1]++; + level--; //Pop stack top element + adrs.set_tree_height(adrs.get_tree_height() + 1); + } + level++; //push temporary node to stack + } + return nodes[level - 1]; + } + +std::shared_ptr<Atomic<size_t>> +XMSS_PrivateKey::recover_global_leaf_index() const + { + BOTAN_ASSERT(m_wots_priv_key.private_seed().size() == + XMSS_PublicKey::m_xmss_params.element_size() && + m_prf.size() == XMSS_PublicKey::m_xmss_params.element_size(), + "Trying to retrieve index for partially initialized " + "key."); + return m_index_reg.get(m_wots_priv_key.private_seed(), + m_prf); + } + +secure_vector<byte> XMSS_PrivateKey::raw_private_key() const + { + std::vector<byte> pk { raw_public_key() }; + secure_vector<byte> result(pk.begin(), pk.end()); + result.reserve(size()); + + for(int i = 7; i >= 0; i--) + { + result.push_back( + static_cast<byte>( + static_cast<uint64_t>(unused_leaf_index()) >> 8 * i)); + } + + std::copy(m_prf.begin(), m_prf.end(), std::back_inserter(result)); + std::copy(m_wots_priv_key.private_seed().begin(), + m_wots_priv_key.private_seed().end(), + std::back_inserter(result)); + + return result; + } + +std::unique_ptr<PK_Ops::Signature> +XMSS_PrivateKey::create_signature_op(RandomNumberGenerator&, + const std::string&, + const std::string& provider) const + { + if(provider == "base" || provider.empty()) + return std::unique_ptr<PK_Ops::Signature>( + new XMSS_Signature_Operation(*this)); + + throw Provider_Not_Found(algo_name(), provider); + } + +} |