SM2
SM2 is a four-part standard for public key algorithms specified by China. This page will show you how to use SM2 classes and supply field notes when cutting-in some SM2 algorithms. The notes are significant because we lack some information.
The information is missing because (1) we don't have access to some documents (paywall?), (2) some documents are written in Chinese (we are fluent English), (3) English documents are incomplete (like draft-shen-sm2-ecdsa), and (4) the reference implementation has some unexpected features (bugs?).
Related pages are SM3 and SM4, which are the library's implementation of the Chinese hash and block cipher.
Field Notes
The following details some of our field notes during our cut-in of SM2. All the commands below were run form the root directory of the reference code after cloning. The URL for the reference code is https://github.com/guanzhi/GmSSL.git
.
Information
The information and documents we had access to:
- SM2 Public Key Algorithms - Internet Draft by IETF. This is the main English document with examples, but it is missing information and has bad links to normative references.
- Public Key Cryptographic Algorithm SM2 Based on Elliptic Curves - Wayback machine. It appears to provide the domain parameters for all the curves for F(p) and F(2m), but it is written in Chinese.
- SM2 Algorithms Parameters - Wayback machine. This is a one page document with domain parameters for a 256-bit prime curve, but it is written in Chinese.
- The GmSSL Project - Appears to be the official website for SM2, SM3, SM4, SM5 and SM9, but it is written in Chinese.
- The GmSSL Project Website | OIDs - The object identifiers for things like SM2 EC parameters, SM2 public keys, and various algorithm identifiers.
- The GmSSL Project Source Code - Appears to be the reference implementation based on OpenSSL 1.1.0. It is written in Chinese, but computer source code is a universal language :)
- The GmSSL Project Source Code | OIDs - The object identifiers used in the actual code.
gmssl Test Program
The gmssl
test program seems to be the main output artifact from the reference implementation. It appears to be the openssl
program with a different name. To build it:
$ git clone https://github.com/guanzhi/GmSSL.git $ cd GmSSL $ ./config ... $ make -j 8
Don't bother with make test
since it results in a bunch of failures.
Once built here are the locations of output artifacts:
GmSSL$ find . -name gmssl ./go/gmssl ./apps/gmssl GmSSL$ find . -name 'libcrypto*' ./libcrypto.a ./util/libcrypto.num ./libcrypto.so.1.1 ./libcrypto.pc ./libcrypto.so GmSSL$ find . -name 'libssl*' ./libssl.so.1.1 ./util/libssl.num ./libssl.pc ./libssl.a ./libssl.so
To run the program you have to twiddle with library paths:
$ LD_LIBRARY_PATH=. apps/gmssl version GmSSL 2.0 - OpenSSL 1.1.0d 26 Jan 2017
A script makes it easier to run the program without the LD_LIBRARY_PREFIX
:
$ cat gmssl #!/usr/bin/env bash LD_LIBRARY_PATH=".:$LD_LIBRARY_PATH" OPENSSL_CONF="apps/openssl.cnf" apps/gmssl "$@" $ chmod +x gmssl
Domain Parameters
It appears the primary or main domain parameters are the following.
p = FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF a = FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC b = 28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93 n = FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123 Gx = 32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7 Gy = BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0
The parameter is listed SM2 Algorithms Parameters, and it surfaces under the gmssl
test program for curve sm2p256v1
:
$ LD_LIBRARY_PATH=. apps/gmssl ecparam -name sm2p256v1 -param_enc explicit -outform DER > params.der $ dumpasn1 ... 0 224: SEQUENCE { 3 1: INTEGER 1 6 44: SEQUENCE { 8 7: OBJECT IDENTIFIER '1 2 840 10045 1 1' 17 33: INTEGER : 00 FF FF FF FE FF FF FF FF FF FF FF FF FF FF FF : FF FF FF FF FF 00 00 00 00 FF FF FF FF FF FF FF : FF : } 52 68: SEQUENCE { 54 32: OCTET STRING : FF FF FF FE FF FF FF FF FF FF FF FF FF FF FF FF : FF FF FF FF 00 00 00 00 FF FF FF FF FF FF FF FC 88 32: OCTET STRING : 28 E9 FA 9E 9D 9F 5E 34 4D 5A 9E 4B CF 65 09 A7 : F3 97 89 F5 15 AB 8F 92 DD BC BD 41 4D 94 0E 93 : } 122 65: OCTET STRING : 04 32 C4 AE 2C 1F 19 81 19 5F 99 04 46 6A 39 C9 : 94 8F E3 0B BF F2 66 0B E1 71 5A 45 89 33 4C 74 : C7 BC 37 36 A2 F4 F6 77 9C 59 BD CE E3 6B 69 21 : 53 D0 A9 87 7C C6 2A 47 40 02 DF 32 E5 21 39 F0 : A0 189 33: INTEGER : 00 FF FF FF FE FF FF FF FF FF FF FF FF FF FF FF : FF 72 03 DF 6B 21 C6 05 2B 53 BB F4 09 39 D5 41 : 23 224 1: INTEGER 1 : }
sm2p256v1 OID
Based on domain parameter testing it looks we need to map two different OIDs to the parameters listed above. First, we need to map the OID for sm2encrypt_recommendedParameters
, and second we need to map the OID for sm2p256v1
.
We map both OIDs to the same parameter set in eccrypto.cpp
. The library lacks the notion of an alias, so the parameters are duplicated. The compiler's string pooling should fold the parameters.
// this array must be sorted by OID static const EcRecommendedParameters<ECP> rec[] = { EcRecommendedParameters<ECP>(ASN1::sm2p256v1(), "FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF", "FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC", "28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93", "04" "32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7" "BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0", "FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123", 1), EcRecommendedParameters<ECP>(ASN1::sm2encrypt_recommendedParameters(), "FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF", "FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC", "28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93", "04" "32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7" "BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0", "FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123", 1), ... };
The OIDs for other domain parameters and curves listed in Public Key Cryptographic Algorithm SM2 Based on Elliptic Curves seems to be missing.
gmssl OIDs
A complete list of NIDs and OIDs used by the test program can be found with:
$ grep -IR NID_sm2 | grep -E '*\.c' java/GmSSL.c: NID_sm2sign, java/GmSSL.c: case NID_sm2sign: java/GmSSL.c: if (!EVP_PKEY_CTX_set_ec_scheme(pkctx, OBJ_txt2nid(alg) == NID_sm2sign ? java/GmSSL.c: if (!EVP_PKEY_CTX_set_ec_scheme(pkctx, OBJ_txt2nid(alg) == NID_sm2sign ? java/GmSSL.c: NID_sm2encrypt_with_sm3, java/GmSSL.c: NID_sm2encrypt_with_sha1, java/GmSSL.c: NID_sm2encrypt_with_sha256, java/GmSSL.c: NID_sm2encrypt_with_sha512, java/GmSSL.c: case NID_sm2encrypt_with_sm3: java/GmSSL.c: case NID_sm2encrypt_with_sha1: java/GmSSL.c: case NID_sm2encrypt_with_sha256: java/GmSSL.c: case NID_sm2encrypt_with_sha512: java/GmSSL.c: NID_sm2exchange, java/GmSSL.c: case NID_sm2exchange: ssl/t1_lib.c: {NID_sm2p256v1, 128, TLS_CURVE_PRIME}, /* sm2p256v1 (30) */ ssl/t1_lib.c: return NID_sm2p256v1; ssl/t1_lib.c: default_nid = NID_sm2sign_with_sm3; ssl/t1_lib.c: default_nid = NID_sm2sign_with_sm3; ssl/statem/statem_lib.c: (EC_KEY *)EVP_PKEY_get0(pk))) == NID_sm2p256v1) { test/sm9test.c: int curve_id = NID_sm2p256v1; //FIXME test/bfibetest.c: int curve_id = NID_sm2p256v1; test/bb1ibetest.c: int curve_id = NID_sm2p256v1;//FIXME crypto/ec/ec_curve.c: {NID_sm2p256v1, &_EC_SM2_PRIME_256V1.h, 0, crypto/gmapi/gmapi_skf_ec.c: if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_skf_ec.c: if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_skf_ec.c: if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_skf_ec.c: if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_sgd.c: {NID_sm2sign, SGD_SM2_1}, crypto/gmapi/gmapi_sgd.c: {NID_sm2exchange, SGD_SM2_2}, crypto/gmapi/gmapi_sgd.c: {NID_sm2encrypt, SGD_SM2_3} crypto/gmapi/gmapi_sdf_ec.c: if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_sdf_ec.c: if (!(ret = EC_KEY_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_sdf_ec.c: if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1))) { crypto/gmapi/gmapi_sdf_ec.c: if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1)) crypto/gmapi/gmapi_sdf_ec.c: if (!(group = EC_GROUP_new_by_curve_name(NID_sm2p256v1)) crypto/evp/m_sm3.c: NID_sm2sign_with_sm3, apps/speed.c: NID_sm2p256v1