aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-06-21 14:43:52 -0400
committerJack Lloyd <[email protected]>2017-06-29 12:38:37 -0400
commit6ac870e6d81d98420a102661a27ad9b521da86f5 (patch)
tree0341a30bbf1003614e81e54f98b0a08105b6d9d1
parent8b0986310ae9fdf7fa93e28e2820d818cc954cdd (diff)
Add SM2 signature scheme
From https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02 This is a contribution from Ribose Inc (@riboseinc).
-rw-r--r--src/build-data/oids.txt6
-rw-r--r--src/build-data/policy/bsi.txt2
-rw-r--r--src/cli/speed.cpp32
-rw-r--r--src/lib/asn1/oids.cpp10
-rw-r--r--src/lib/ffi/ffi.cpp56
-rw-r--r--src/lib/ffi/ffi.h9
-rw-r--r--src/lib/pubkey/ec_group/ec_group.cpp4
-rw-r--r--src/lib/pubkey/ec_group/ec_named.cpp10
-rw-r--r--src/lib/pubkey/pk_algs.cpp20
-rw-r--r--src/lib/pubkey/sm2/info.txt13
-rw-r--r--src/lib/pubkey/sm2/sm2.cpp234
-rw-r--r--src/lib/pubkey/sm2/sm2.h98
-rw-r--r--src/tests/data/hash/sm3.vec5
-rw-r--r--src/tests/data/pubkey/sm2_sig.vec14
-rw-r--r--src/tests/test_ffi.cpp92
-rw-r--r--src/tests/test_sm2.cpp68
16 files changed, 671 insertions, 2 deletions
diff --git a/src/build-data/oids.txt b/src/build-data/oids.txt
index 41e599e29..1ee09583b 100644
--- a/src/build-data/oids.txt
+++ b/src/build-data/oids.txt
@@ -16,6 +16,10 @@
1.2.840.10045.2.1 = ECDSA
1.3.132.1.12 = ECDH
+1.2.156.10197.1.301.1 = SM2_Sig
+1.2.156.10197.1.301.2 = SM2_Kex
+1.2.156.10197.1.301.3 = SM2_Enc
+
# ecgPublicKey (see https://www.teletrust.de/projekte/oid/)
1.3.36.3.3.2.5.2.1 = ECGDSA
@@ -236,6 +240,8 @@
1.2.840.10045.3.1.6 = x962_p239v3
1.2.840.10045.3.1.7 = secp256r1
+1.2.156.10197.1.301 = sm2p256v1
+
1.3.36.3.3.2.8.1.1.1 = brainpool160r1
1.3.36.3.3.2.8.1.1.3 = brainpool192r1
1.3.36.3.3.2.8.1.1.5 = brainpool224r1
diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt
index ae722d4c1..011168463 100644
--- a/src/build-data/policy/bsi.txt
+++ b/src/build-data/policy/bsi.txt
@@ -92,6 +92,7 @@ noekeon_simd
seed
serpent
serpent_simd
+sm4
threefish
threefish_avx2
twofish
@@ -128,6 +129,7 @@ mce
mceies
rfc6979
newhope
+sm2
# pk_pad
#eme_pkcs1 // needed for tls
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp
index 296b4b07a..cae11e52f 100644
--- a/src/cli/speed.cpp
+++ b/src/cli/speed.cpp
@@ -112,6 +112,10 @@
#include <botan/xmss.h>
#endif
+#if defined(BOTAN_HAS_SM2)
+ #include <botan/sm2.h>
+#endif
+
#if defined(BOTAN_HAS_NEWHOPE) && defined(BOTAN_HAS_CHACHA)
#include <botan/newhope.h>
#include <botan/chacha.h>
@@ -499,6 +503,12 @@ class Speed final : public Command
bench_ecdsa(ecc_groups, provider, msec);
}
#endif
+#if defined(BOTAN_HAS_SM2)
+ else if(algo == "SM2")
+ {
+ bench_sm2(ecc_groups, provider, msec);
+ }
+#endif
#if defined(BOTAN_HAS_ECKCDSA)
else if(algo == "ECKCDSA")
{
@@ -1387,6 +1397,28 @@ class Speed final : public Command
}
#endif
+#if defined(BOTAN_HAS_SM2)
+ void bench_sm2(const std::vector<std::string>& groups,
+ const std::string& provider,
+ std::chrono::milliseconds msec)
+ {
+ for(std::string grp : groups)
+ {
+ const std::string nm = "SM2-" + grp;
+
+ Timer keygen_timer(nm, provider, "keygen");
+
+ std::unique_ptr<Botan::Private_Key> key(keygen_timer.run([&]
+ {
+ return new Botan::SM2_Signature_PrivateKey(rng(), Botan::EC_Group(grp));
+ }));
+
+ output() << Timer::result_string_ops(keygen_timer);
+ bench_pk_sig(*key, nm, provider, "SM3", msec);
+ }
+ }
+#endif
+
#if defined(BOTAN_HAS_ECGDSA)
void bench_ecgdsa(const std::vector<std::string>& groups,
const std::string& provider,
diff --git a/src/lib/asn1/oids.cpp b/src/lib/asn1/oids.cpp
index 794f0f6f0..1fcbe456d 100644
--- a/src/lib/asn1/oids.cpp
+++ b/src/lib/asn1/oids.cpp
@@ -1,7 +1,7 @@
/*
* OID maps
*
-* This file was automatically generated by ./src/scripts/oids.py on 2017-06-12
+* This file was automatically generated by ./src/scripts/oids.py on 2017-06-22
*
* All manual edits to this file will be lost. Edit the script
* then regenerate this source file.
@@ -19,6 +19,10 @@ std::string lookup(const OID& oid)
{
const std::string oid_str = oid.as_string();
if(oid_str == "1.0.14888.3.0.5") return "ECKCDSA";
+ if(oid_str == "1.2.156.10197.1.301") return "sm2p256v1";
+ if(oid_str == "1.2.156.10197.1.301.1") return "SM2_Sig";
+ if(oid_str == "1.2.156.10197.1.301.2") return "SM2_Kex";
+ if(oid_str == "1.2.156.10197.1.301.3") return "SM2_Enc";
if(oid_str == "1.2.156.10197.1.401") return "SM3";
if(oid_str == "1.2.156.10197.1.504") return "RSA/EMSA3(SM3)";
if(oid_str == "1.2.250.1.223.101.256.1") return "frp256v1";
@@ -327,6 +331,9 @@ OID lookup(const std::string& name)
if(name == "SHA-512-256") return OID("2.16.840.1.101.3.4.2.6");
if(name == "SHAKE-128") return OID("2.16.840.1.101.3.4.2.11");
if(name == "SHAKE-256") return OID("2.16.840.1.101.3.4.2.12");
+ if(name == "SM2_Enc") return OID("1.2.156.10197.1.301.3");
+ if(name == "SM2_Kex") return OID("1.2.156.10197.1.301.2");
+ if(name == "SM2_Sig") return OID("1.2.156.10197.1.301.1");
if(name == "SM3") return OID("1.2.156.10197.1.401");
if(name == "Serpent/CBC") return OID("1.3.6.1.4.1.25258.3.1");
if(name == "Serpent/GCM") return OID("1.3.6.1.4.1.25258.3.101");
@@ -388,6 +395,7 @@ OID lookup(const std::string& name)
if(name == "secp256r1") return OID("1.2.840.10045.3.1.7");
if(name == "secp384r1") return OID("1.3.132.0.34");
if(name == "secp521r1") return OID("1.3.132.0.35");
+ if(name == "sm2p256v1") return OID("1.2.156.10197.1.301");
if(name == "x962_p192v2") return OID("1.2.840.10045.3.1.2");
if(name == "x962_p192v3") return OID("1.2.840.10045.3.1.3");
if(name == "x962_p239v1") return OID("1.2.840.10045.3.1.4");
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 38e3ba425..d99569778 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -45,6 +45,10 @@
#include <botan/ecdsa.h>
#endif
+#if defined(BOTAN_HAS_SM2)
+ #include <botan/sm2.h>
+#endif
+
#if defined(BOTAN_HAS_ECC_PUBLIC_KEY_CRYPTO)
#include <botan/ecc_key.h>
#endif
@@ -1797,6 +1801,32 @@ int botan_pubkey_load_ecdsa(botan_pubkey_t* key,
#endif
}
+int botan_pubkey_load_sm2(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name)
+ {
+#if defined(BOTAN_HAS_SM2)
+ std::unique_ptr<Botan::SM2_Signature_PublicKey> p_key;
+ try
+ {
+ if(!pubkey_load_ec(p_key, safe_get(public_x), safe_get(public_y), curve_name))
+ {
+ *key = new botan_pubkey_struct(p_key.release());
+ return 0;
+ }
+ }
+ catch(std::exception& exn)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, exn.what());
+ }
+ return -1;
+#else
+ BOTAN_UNUSED(key, public_x, public_y, curve_name);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
int botan_pubkey_load_ecdh(botan_pubkey_t* key,
const botan_mp_t public_x,
const botan_mp_t public_y,
@@ -1847,6 +1877,32 @@ int botan_privkey_load_ecdsa(botan_privkey_t* key,
return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
#endif
}
+
+int botan_privkey_load_sm2(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name)
+ {
+#if defined(BOTAN_HAS_SM2)
+ std::unique_ptr<Botan::SM2_Signature_PrivateKey> p_key;
+ try
+ {
+ if(!privkey_load_ec(p_key, safe_get(scalar), curve_name))
+ {
+ *key = new botan_privkey_struct(p_key.release());
+ return 0;
+ }
+ }
+ catch(std::exception& exn)
+ {
+ log_exception(BOTAN_CURRENT_FUNCTION, exn.what());
+ }
+ return -1;
+#else
+ BOTAN_UNUSED(key, scalar, curve_name);
+ return BOTAN_FFI_ERROR_NOT_IMPLEMENTED;
+#endif
+ }
+
int botan_privkey_load_ecdh(botan_privkey_t* key,
const botan_mp_t scalar,
const char* curve_name)
diff --git a/src/lib/ffi/ffi.h b/src/lib/ffi/ffi.h
index a179e5fa4..4a7723974 100644
--- a/src/lib/ffi/ffi.h
+++ b/src/lib/ffi/ffi.h
@@ -882,6 +882,15 @@ BOTAN_DLL int botan_privkey_load_ecdh(botan_privkey_t* key,
const botan_mp_t scalar,
const char* curve_name);
+BOTAN_DLL int botan_pubkey_load_sm2(botan_pubkey_t* key,
+ const botan_mp_t public_x,
+ const botan_mp_t public_y,
+ const char* curve_name);
+
+BOTAN_DLL int botan_privkey_load_sm2(botan_privkey_t* key,
+ const botan_mp_t scalar,
+ const char* curve_name);
+
/*
* Public Key Encryption
*/
diff --git a/src/lib/pubkey/ec_group/ec_group.cpp b/src/lib/pubkey/ec_group/ec_group.cpp
index c8d464a22..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();
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/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..45a51b4cc
--- /dev/null
+++ b/src/lib/pubkey/sm2/sm2.cpp
@@ -0,0 +1,234 @@
+/*
+* SM2
+* (C) 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>
+
+#include <iostream>
+#include <botan/hex.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());
+
+ #if 0
+ std::cout << "Ent0 " << (int)get_byte(0, uid_len) << "\n";
+ std::cout << "Ent1 " << (int)get_byte(1, uid_len) << "\n";
+ std::cout << "ID " << user_id << "\n";
+ std::cout << "A = " << Botan::hex_encode(BigInt::encode_1363(domain.get_curve().get_a(), p_bytes)) << "\n";
+ std::cout << "B = " << Botan::hex_encode(BigInt::encode_1363(domain.get_curve().get_b(), p_bytes)) << "\n";
+ std::cout << "xG = " << Botan::hex_encode(BigInt::encode_1363(domain.get_base_point().get_affine_x(), p_bytes)) << "\n";
+ std::cout << "yG = " << Botan::hex_encode(BigInt::encode_1363(domain.get_base_point().get_affine_y(), p_bytes)) << "\n";
+ std::cout << "xP = " << Botan::hex_encode(BigInt::encode_1363(pubkey.get_affine_x(), p_bytes)) << "\n";
+ std::cout << "yP = " << Botan::hex_encode(BigInt::encode_1363(pubkey.get_affine_y(), p_bytes)) << "\n";
+ std::cout << "ZA = " << hex_encode(za) << "\n";
+ #endif
+
+ 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
diff --git a/src/tests/data/hash/sm3.vec b/src/tests/data/hash/sm3.vec
index 2dd4710bc..f5dee3ccb 100644
--- a/src/tests/data/hash/sm3.vec
+++ b/src/tests/data/hash/sm3.vec
@@ -1,5 +1,10 @@
[SM3]
+# From ZA computation in https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02
+
+In = 0090414C494345313233405941484F4F2E434F4D787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E49863E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A20AE4C7798AA0F119471BEE11825BE46202BB79E2A5844495E97C04FF4DF2548A7C0240F88F1CD4E16352A73C17B7F16F07353E53A176D684A9FE0C6BB798E857
+Out = F4A38489E32B45B6F876E3AC2168CA392362DC8F23459C1D1146FC3DBFB7BC9A
+
# https://tools.ietf.org/html/draft-shen-sm3-hash-01
In = 616263
Out = 66C7F0F462EEEDD9D1F2D46BDC10E4E24167C4875CF2F7A2297DA02B8F4BA8E0
diff --git a/src/tests/data/pubkey/sm2_sig.vec b/src/tests/data/pubkey/sm2_sig.vec
new file mode 100644
index 000000000..d3356cceb
--- /dev/null
+++ b/src/tests/data/pubkey/sm2_sig.vec
@@ -0,0 +1,14 @@
+
+P = 0x8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3
+A = 0x787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498
+B = 0x63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A
+xG = 0x421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D
+yG = 0x0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2
+Order = 0x8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7
+Cofactor = 1
+
+Msg = 6D65737361676520646967657374
+x = 0x128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263
+Nonce = 6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F
+Signature = 40F1EC59F793D9F49E09DCEF49130D4194F79FB1EED2CAA55BACDB49C4E755D16FC6DAC32C5D5CF10C77DFB20F7C2EB667A457872FB09EC56327A67EC7DEEBE7
diff --git a/src/tests/test_ffi.cpp b/src/tests/test_ffi.cpp
index e9d9b6e20..bf5fc0e9c 100644
--- a/src/tests/test_ffi.cpp
+++ b/src/tests/test_ffi.cpp
@@ -383,6 +383,10 @@ class FFI_Unit_Tests : public Test
results.push_back(ffi_test_ecdh(rng));
#endif
+#if defined(BOTAN_HAS_SM2)
+ results.push_back(ffi_test_sm2(rng));
+#endif
+
#if defined(BOTAN_HAS_MCELIECE)
results.push_back(ffi_test_mceliece(rng));
#endif
@@ -1280,6 +1284,94 @@ class FFI_Unit_Tests : public Test
return result;
}
+ Test::Result ffi_test_sm2(botan_rng_t rng)
+ {
+ Test::Result result("FFI SM2");
+ static const char* kCurve = "sm2p256v1";
+ const std::string sm2_ident = "SM2 Ident Field";
+ botan_privkey_t priv;
+ botan_pubkey_t pub;
+ botan_privkey_t loaded_privkey;
+ botan_pubkey_t loaded_pubkey;
+
+ REQUIRE_FFI_OK(botan_privkey_create, (&priv, "SM2_Sig", kCurve, rng));
+ TEST_FFI_OK(botan_privkey_export_pubkey, (&pub, priv));
+ ffi_test_pubkey_export(result, pub, priv, rng);
+
+ // Check key load functions
+ botan_mp_t private_scalar, public_x, public_y;
+ botan_mp_init(&private_scalar);
+ botan_mp_init(&public_x);
+ botan_mp_init(&public_y);
+
+ TEST_FFI_OK(botan_privkey_get_field, (private_scalar, priv, "x"));
+ TEST_FFI_OK(botan_pubkey_get_field, (public_x, pub, "public_x"));
+ TEST_FFI_OK(botan_pubkey_get_field, (public_y, pub, "public_y"));
+ TEST_FFI_OK(botan_privkey_load_sm2, (&loaded_privkey, private_scalar, kCurve));
+ TEST_FFI_OK(botan_pubkey_load_sm2, (&loaded_pubkey, public_x, public_y, kCurve));
+ TEST_FFI_OK(botan_privkey_check_key, (loaded_privkey, rng, 0));
+ TEST_FFI_OK(botan_pubkey_check_key, (loaded_pubkey, rng, 0));
+
+ char namebuf[32] = { 0 };
+ size_t name_len = sizeof(namebuf);
+
+ TEST_FFI_OK(botan_pubkey_algo_name, (pub, &namebuf[0], &name_len));
+ result.test_eq(namebuf, namebuf, "SM2_Sig");
+
+ std::vector<uint8_t> message(1280), signature;
+ TEST_FFI_OK(botan_rng_get, (rng, message.data(), message.size()));
+ botan_pk_op_sign_t signer;
+ if(TEST_FFI_OK(botan_pk_op_sign_create, (&signer, loaded_privkey, sm2_ident.c_str(), 0)))
+ {
+ // TODO: break input into multiple calls to update
+ TEST_FFI_OK(botan_pk_op_sign_update, (signer, message.data(), message.size()));
+
+ signature.resize(96); // TODO: no way to derive this from API
+ size_t sig_len = signature.size();
+ TEST_FFI_OK(botan_pk_op_sign_finish, (signer, rng, signature.data(), &sig_len));
+ signature.resize(sig_len);
+
+ TEST_FFI_OK(botan_pk_op_sign_destroy, (signer));
+ }
+
+ botan_pk_op_verify_t verifier;
+
+ if(signature.size() > 0 && TEST_FFI_OK(botan_pk_op_verify_create, (&verifier, pub, sm2_ident.c_str(), 0)))
+ {
+ TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+ TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+ // TODO: randomize this
+ signature[0] ^= 1;
+ TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+ TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+ message[0] ^= 1;
+ TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+ TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+ signature[0] ^= 1;
+ TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+ TEST_FFI_FAIL("bad signature", botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+ message[0] ^= 1;
+ TEST_FFI_OK(botan_pk_op_verify_update, (verifier, message.data(), message.size()));
+ TEST_FFI_OK(botan_pk_op_verify_finish, (verifier, signature.data(), signature.size()));
+
+ TEST_FFI_OK(botan_pk_op_verify_destroy, (verifier));
+ }
+
+ TEST_FFI_OK(botan_mp_destroy, (private_scalar));
+ TEST_FFI_OK(botan_mp_destroy, (public_x));
+ TEST_FFI_OK(botan_mp_destroy, (public_y));
+ TEST_FFI_OK(botan_pubkey_destroy, (pub));
+ TEST_FFI_OK(botan_privkey_destroy, (priv));
+ TEST_FFI_OK(botan_privkey_destroy, (loaded_privkey));
+ TEST_FFI_OK(botan_pubkey_destroy, (loaded_pubkey));
+
+ return result;
+ }
+
Test::Result ffi_test_ecdh(botan_rng_t rng)
{
Test::Result result("FFI ECDH");
diff --git a/src/tests/test_sm2.cpp b/src/tests/test_sm2.cpp
new file mode 100644
index 000000000..0361ea3a3
--- /dev/null
+++ b/src/tests/test_sm2.cpp
@@ -0,0 +1,68 @@
+/*
+* (C) 2017 Ribose Inc
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+#include "test_rng.h"
+
+#if defined(BOTAN_HAS_SM2)
+ #include <botan/sm2.h>
+ #include "test_pubkey.h"
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_SM2)
+
+namespace {
+
+class SM2_Signature_KAT_Tests : public PK_Signature_Generation_Test
+ {
+ public:
+ SM2_Signature_KAT_Tests()
+ : PK_Signature_Generation_Test(
+ "SM2",
+ "pubkey/sm2_sig.vec",
+ "P,A,B,xG,yG,Order,Cofactor,Ident,Msg,x,Nonce,Signature",
+ "") {}
+
+ virtual std::string default_padding(const VarMap& vars) const
+ {
+ return get_req_str(vars, "Ident");
+ }
+
+ Botan::RandomNumberGenerator* test_rng(const std::vector<uint8_t>& nonce) const override
+ {
+ return new Fixed_Output_Position_RNG(nonce, 1);
+ }
+
+ std::unique_ptr<Botan::Private_Key> load_private_key(const VarMap& vars) override
+ {
+ // group params
+ const BigInt p = get_req_bn(vars, "P");
+ const BigInt a = get_req_bn(vars, "A");
+ const BigInt b = get_req_bn(vars, "B");
+ const BigInt xG = get_req_bn(vars, "xG");
+ const BigInt yG = get_req_bn(vars, "yG");
+ const BigInt order = get_req_bn(vars, "Order");
+ const BigInt cofactor = get_req_bn(vars, "Cofactor");
+ const BigInt x = get_req_bn(vars, "x");
+
+ Botan::CurveGFp curve(p, a, b);
+ Botan::PointGFp base_point(curve, xG, yG);
+ Botan::EC_Group domain(curve, base_point, order, cofactor);
+
+ Botan::Null_RNG null_rng;
+ std::unique_ptr<Botan::Private_Key> key(new Botan::SM2_Signature_PrivateKey(null_rng, domain, x));
+ return key;
+ }
+ };
+
+}
+
+BOTAN_REGISTER_TEST("sm2_sig", SM2_Signature_KAT_Tests);
+#endif
+
+}