diff options
Diffstat (limited to 'src/lib/pubkey')
-rw-r--r-- | src/lib/pubkey/ec_group/ec_group.cpp | 10 | ||||
-rw-r--r-- | src/lib/pubkey/ec_group/ec_named.cpp | 10 | ||||
-rw-r--r-- | src/lib/pubkey/ed25519/ed25519_fe.h | 2 | ||||
-rw-r--r-- | src/lib/pubkey/pk_algs.cpp | 20 | ||||
-rw-r--r-- | src/lib/pubkey/sm2/info.txt | 13 | ||||
-rw-r--r-- | src/lib/pubkey/sm2/sm2.cpp | 218 | ||||
-rw-r--r-- | src/lib/pubkey/sm2/sm2.h | 98 |
7 files changed, 369 insertions, 2 deletions
diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp index e8a9672ab..831c41e37 100644 --- a/src/lib/pubkey/ec_group/ec_group.cpp +++ b/src/lib/pubkey/ec_group/ec_group.cpp @@ -21,7 +21,9 @@ EC_Group::EC_Group(const OID& domain_oid) const std::string pem = PEM_for_named_group(OIDS::lookup(domain_oid)); if(pem == "") - throw Lookup_Error("No ECC domain data for " + domain_oid.as_string()); + { + throw Lookup_Error("No ECC domain data for '" + domain_oid.as_string() + "'"); + } *this = EC_Group(pem); m_oid = domain_oid.as_string(); @@ -118,7 +120,13 @@ EC_Group::DER_encode(EC_Group_Encoding form) const .get_contents_unlocked(); } else if(form == EC_DOMPAR_ENC_OID) + { + if(get_oid().empty()) + { + throw Encoding_Error("Cannot encode EC_Group as OID because OID not set"); + } return DER_Encoder().encode(OID(get_oid())).get_contents_unlocked(); + } else if(form == EC_DOMPAR_ENC_IMPLICITCA) return DER_Encoder().encode_null().get_contents_unlocked(); else diff --git a/src/lib/pubkey/ec_group/ec_named.cpp b/src/lib/pubkey/ec_group/ec_named.cpp index 2aef0ad31..fc4a67fc6 100644 --- a/src/lib/pubkey/ec_group/ec_named.cpp +++ b/src/lib/pubkey/ec_group/ec_named.cpp @@ -266,6 +266,16 @@ std::string EC_Group::PEM_for_named_group(const std::string& name) "8f0XjAs61Y8QEm3ozkJDW1PcZ+FA0r+UH/3UWcbWVeECAQE=" "-----END EC PARAMETERS-----"; + if(name == "sm2p256v1") + return + "-----BEGIN EC PARAMETERS-----" + "MIHgAgEBMCwGByqGSM49AQECIQD////+/////////////////////wAAAAD/////" + "/////zBEBCD////+/////////////////////wAAAAD//////////AQgKOn6np2f" + "XjRNWp5Lz2UJp/OXifUVq4+S3by9QU2UDpMEQQQyxK4sHxmBGV+ZBEZqOcmUj+ML" + "v/JmC+FxWkWJM0x0x7w3NqL09necWb3O42tpIVPQqYd8xipHQALfMuUhOfCgAiEA" + "/////v///////////////3ID32shxgUrU7v0CTnVQSMCAQE=" + "-----END EC PARAMETERS-----"; + #if defined(BOTAN_HOUSE_ECC_CURVE_NAME) if(name == BOTAN_HOUSE_ECC_CURVE_NAME) return BOTAN_HOUSE_ECC_CURVE_PEM; diff --git a/src/lib/pubkey/ed25519/ed25519_fe.h b/src/lib/pubkey/ed25519/ed25519_fe.h index a7a3a8d8d..b82e2ebe2 100644 --- a/src/lib/pubkey/ed25519/ed25519_fe.h +++ b/src/lib/pubkey/ed25519/ed25519_fe.h @@ -16,7 +16,7 @@ namespace Botan { /** -* An element of the field \Z/(2^255-19) +* An element of the field \\Z/(2^255-19) */ class FE_25519 { diff --git a/src/lib/pubkey/pk_algs.cpp b/src/lib/pubkey/pk_algs.cpp index 45e88f2a3..34d659c8f 100644 --- a/src/lib/pubkey/pk_algs.cpp +++ b/src/lib/pubkey/pk_algs.cpp @@ -60,6 +60,10 @@ #include <botan/xmss.h> #endif +#if defined(BOTAN_HAS_SM2) + #include <botan/sm2.h> +#endif + #if defined(BOTAN_HAS_OPENSSL) #include <botan/internal/openssl.h> #endif @@ -134,6 +138,11 @@ load_public_key(const AlgorithmIdentifier& alg_id, return std::unique_ptr<Public_Key>(new GOST_3410_PublicKey(alg_id, key_bits)); #endif +#if defined(BOTAN_HAS_SM2) + if(alg_name == "SM2_Sig") + return std::unique_ptr<Public_Key>(new SM2_Signature_PublicKey(alg_id, key_bits)); +#endif + #if defined(BOTAN_HAS_XMSS) if(alg_name == "XMSS") return std::unique_ptr<Public_Key>(new XMSS_PublicKey(key_bits)); @@ -205,6 +214,11 @@ load_private_key(const AlgorithmIdentifier& alg_id, return std::unique_ptr<Private_Key>(new GOST_3410_PrivateKey(alg_id, key_bits)); #endif +#if defined(BOTAN_HAS_SM2) + if(alg_name == "SM2_Sig") + return std::unique_ptr<Private_Key>(new SM2_Signature_PrivateKey(alg_id, key_bits)); +#endif + #if defined(BOTAN_HAS_ELGAMAL) if(alg_name == "ElGamal") return std::unique_ptr<Private_Key>(new ElGamal_PrivateKey(alg_id, key_bits)); @@ -290,6 +304,7 @@ create_private_key(const std::string& alg_name, alg_name == "ECDH" || alg_name == "ECKCDSA" || alg_name == "ECGDSA" || + alg_name == "SM2_Sig" || alg_name == "GOST-34.10") { const EC_Group ec_group(params.empty() ? "secp256r1" : params); @@ -314,6 +329,11 @@ create_private_key(const std::string& alg_name, return std::unique_ptr<Private_Key>(new GOST_3410_PrivateKey(rng, ec_group)); #endif +#if defined(BOTAN_HAS_SM2) + if(alg_name == "SM2_Sig") + return std::unique_ptr<Private_Key>(new SM2_Signature_PrivateKey(rng, ec_group)); +#endif + #if defined(BOTAN_HAS_ECGDSA) if(alg_name == "ECGDSA") return std::unique_ptr<Private_Key>(new ECGDSA_PrivateKey(rng, ec_group)); diff --git a/src/lib/pubkey/sm2/info.txt b/src/lib/pubkey/sm2/info.txt new file mode 100644 index 000000000..e94500277 --- /dev/null +++ b/src/lib/pubkey/sm2/info.txt @@ -0,0 +1,13 @@ +<defines> +SM2 -> 20170621 +</defines> + +<requires> +asn1 +ec_group +ecc_key +keypair +numbertheory +rng +sm3 +</requires> diff --git a/src/lib/pubkey/sm2/sm2.cpp b/src/lib/pubkey/sm2/sm2.cpp new file mode 100644 index 000000000..5c5b50792 --- /dev/null +++ b/src/lib/pubkey/sm2/sm2.cpp @@ -0,0 +1,218 @@ +/* +* SM2 +* (C) 2017 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/sm2.h> +#include <botan/internal/pk_ops_impl.h> +#include <botan/keypair.h> +#include <botan/reducer.h> +#include <botan/hash.h> + +namespace Botan { + +bool SM2_Signature_PrivateKey::check_key(RandomNumberGenerator& rng, + bool strong) const + { + if(!public_point().on_the_curve()) + return false; + + if(!strong) + return true; + + return KeyPair::signature_consistency_check(rng, *this, "SM3"); + } + +SM2_Signature_PrivateKey::SM2_Signature_PrivateKey(const AlgorithmIdentifier& alg_id, + const secure_vector<uint8_t>& key_bits) : + EC_PrivateKey(alg_id, key_bits) + { + m_da_inv = inverse_mod(m_private_key + 1, domain().get_order()); + } + +SM2_Signature_PrivateKey::SM2_Signature_PrivateKey(RandomNumberGenerator& rng, + const EC_Group& domain, + const BigInt& x) : + EC_PrivateKey(rng, domain, x) + { + m_da_inv = inverse_mod(m_private_key + 1, domain.get_order()); + } + +namespace { + +std::vector<uint8_t> compute_za(HashFunction& hash, + const std::string& user_id, + const EC_Group& domain, + const PointGFp& pubkey) + { + if(user_id.size() >= 8192) + throw Invalid_Argument("SM2 user id too long to represent"); + + const uint16_t uid_len = static_cast<uint16_t>(8 * user_id.size()); + + hash.update(get_byte(0, uid_len)); + hash.update(get_byte(1, uid_len)); + hash.update(user_id); + + const size_t p_bytes = domain.get_curve().get_p().bytes(); + + hash.update(BigInt::encode_1363(domain.get_curve().get_a(), p_bytes)); + hash.update(BigInt::encode_1363(domain.get_curve().get_b(), p_bytes)); + hash.update(BigInt::encode_1363(domain.get_base_point().get_affine_x(), p_bytes)); + hash.update(BigInt::encode_1363(domain.get_base_point().get_affine_y(), p_bytes)); + hash.update(BigInt::encode_1363(pubkey.get_affine_x(), p_bytes)); + hash.update(BigInt::encode_1363(pubkey.get_affine_y(), p_bytes)); + + std::vector<uint8_t> za(hash.output_length()); + hash.final(za.data()); + + return za; + } + +/** +* SM2 signature operation +*/ +class SM2_Signature_Operation : public PK_Ops::Signature + { + public: + + SM2_Signature_Operation(const SM2_Signature_PrivateKey& sm2, + const std::string& ident) : + m_order(sm2.domain().get_order()), + m_base_point(sm2.domain().get_base_point(), m_order), + m_x(sm2.private_value()), + m_da_inv(sm2.get_da_inv()), + m_mod_order(m_order), + m_hash(HashFunction::create_or_throw("SM3")) + { + // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA) + m_za = compute_za(*m_hash, ident, sm2.domain(), sm2.public_point()); + m_hash->update(m_za); + } + + void update(const uint8_t msg[], size_t msg_len) override + { + m_hash->update(msg, msg_len); + } + + secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override; + + private: + const BigInt& m_order; + Blinded_Point_Multiply m_base_point; + const BigInt& m_x; + const BigInt& m_da_inv; + Modular_Reducer m_mod_order; + + std::vector<uint8_t> m_za; + std::unique_ptr<HashFunction> m_hash; + }; + +secure_vector<uint8_t> +SM2_Signature_Operation::sign(RandomNumberGenerator& rng) + { + const BigInt k = BigInt::random_integer(rng, 1, m_order); + + const PointGFp k_times_P = m_base_point.blinded_multiply(k, rng); + + const BigInt e = BigInt::decode(m_hash->final()); + const BigInt r = m_mod_order.reduce(k_times_P.get_affine_x() + e); + const BigInt s = m_mod_order.multiply(m_da_inv, (k - r*m_x)); + + // prepend ZA for next signature if any + m_hash->update(m_za); + + return BigInt::encode_fixed_length_int_pair(r, s, m_order.bytes()); + } + +/** +* SM2 verification operation +*/ +class SM2_Verification_Operation : public PK_Ops::Verification + { + public: + SM2_Verification_Operation(const SM2_Signature_PublicKey& sm2, + const std::string& ident) : + m_base_point(sm2.domain().get_base_point()), + m_public_point(sm2.public_point()), + m_order(sm2.domain().get_order()), + m_mod_order(m_order), + m_hash(HashFunction::create_or_throw("SM3")) + { + // ZA=H256(ENTLA || IDA || a || b || xG || yG || xA || yA) + m_za = compute_za(*m_hash, ident, sm2.domain(), sm2.public_point()); + m_hash->update(m_za); + } + + void update(const uint8_t msg[], size_t msg_len) override + { + m_hash->update(msg, msg_len); + } + + bool is_valid_signature(const uint8_t sig[], size_t sig_len) override; + private: + const PointGFp& m_base_point; + const PointGFp& m_public_point; + const BigInt& m_order; + // FIXME: should be offered by curve + Modular_Reducer m_mod_order; + std::vector<uint8_t> m_za; + std::unique_ptr<HashFunction> m_hash; + }; + +bool SM2_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len) + { + const BigInt e = BigInt::decode(m_hash->final()); + + // Update for next verification + m_hash->update(m_za); + + if(sig_len != m_order.bytes()*2) + return false; + + const BigInt r(sig, sig_len / 2); + const BigInt s(sig + sig_len / 2, sig_len / 2); + + if(r <= 0 || r >= m_order || s <= 0 || s >= m_order) + return false; + + const BigInt t = m_mod_order.reduce(r + s); + + if(t == 0) + return false; + + const PointGFp R = multi_exponentiate(m_base_point, s, m_public_point, t); + + // ??? + if(R.is_zero()) + return false; + + return (m_mod_order.reduce(R.get_affine_x() + e) == r); + } + +} + +std::unique_ptr<PK_Ops::Verification> +SM2_Signature_PublicKey::create_verification_op(const std::string& params, + const std::string& provider) const + { + if(provider == "base" || provider.empty()) + return std::unique_ptr<PK_Ops::Verification>(new SM2_Verification_Operation(*this, params)); + + throw Provider_Not_Found(algo_name(), provider); + } + +std::unique_ptr<PK_Ops::Signature> +SM2_Signature_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/, + const std::string& params, + const std::string& provider) const + { + if(provider == "base" || provider.empty()) + return std::unique_ptr<PK_Ops::Signature>(new SM2_Signature_Operation(*this, params)); + + throw Provider_Not_Found(algo_name(), provider); + } + +} diff --git a/src/lib/pubkey/sm2/sm2.h b/src/lib/pubkey/sm2/sm2.h new file mode 100644 index 000000000..a66654dbd --- /dev/null +++ b/src/lib/pubkey/sm2/sm2.h @@ -0,0 +1,98 @@ +/* +* SM2 +* (C) 2017 Ribose Inc +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SM2_KEY_H__ +#define BOTAN_SM2_KEY_H__ + +#include <botan/ecc_key.h> + +namespace Botan { + +/** +* This class represents SM2 Signature Keys +*/ +class BOTAN_DLL SM2_Signature_PublicKey : public virtual EC_PublicKey + { + public: + + /** + * Create a public key from a given public point. + * @param dom_par the domain parameters associated with this key + * @param public_point the public point defining this key + */ + SM2_Signature_PublicKey(const EC_Group& dom_par, + const PointGFp& public_point) : + EC_PublicKey(dom_par, public_point) {} + + /** + * Load a public key. + * @param alg_id the X.509 algorithm identifier + * @param key_bits DER encoded public key bits + */ + SM2_Signature_PublicKey(const AlgorithmIdentifier& alg_id, + const std::vector<uint8_t>& key_bits) : + EC_PublicKey(alg_id, key_bits) {} + + /** + * Get this keys algorithm name. + * @result this keys algorithm name + */ + std::string algo_name() const override { return "SM2_Sig"; } + + size_t message_parts() const override { return 2; } + + size_t message_part_size() const override + { return domain().get_order().bytes(); } + + std::unique_ptr<PK_Ops::Verification> + create_verification_op(const std::string& params, + const std::string& provider) const override; + protected: + SM2_Signature_PublicKey() = default; + }; + +/** +* This class represents SM2 Private Keys +*/ +class BOTAN_DLL SM2_Signature_PrivateKey : public SM2_Signature_PublicKey, + public EC_PrivateKey + { + public: + + /** + * Load a private key + * @param alg_id the X.509 algorithm identifier + * @param key_bits ECPrivateKey bits + */ + SM2_Signature_PrivateKey(const AlgorithmIdentifier& alg_id, + const secure_vector<uint8_t>& key_bits); + + /** + * Create a private key. + * @param rng a random number generator + * @param domain parameters to used for this key + * @param x the private key (if zero, generate a new random key) + */ + SM2_Signature_PrivateKey(RandomNumberGenerator& rng, + const EC_Group& domain, + const BigInt& x = 0); + + bool check_key(RandomNumberGenerator& rng, bool) const override; + + std::unique_ptr<PK_Ops::Signature> + create_signature_op(RandomNumberGenerator& rng, + const std::string& params, + const std::string& provider) const override; + + const BigInt& get_da_inv() const { return m_da_inv; } + private: + BigInt m_da_inv; + }; + +} + +#endif |