]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add SM2 key management
authorPaul Yang <kaishen.yy@antfin.com>
Sun, 26 Jul 2020 15:25:49 +0000 (23:25 +0800)
committerMatt Caswell <matt@openssl.org>
Tue, 22 Sep 2020 07:17:57 +0000 (08:17 +0100)
Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/12536)

crypto/ec/ec_key.c
crypto/evp/pmeth_gn.c
include/openssl/ec.h
providers/defltprov.c
providers/implementations/include/prov/implementations.h
providers/implementations/keymgmt/ec_kmgmt.c
test/ecdsatest.c

index 75c3588a9549b15adc05133340a0b34d6fdf450f..89a28622bbf912b5af6c7506eeb682b27d3750dd 100644 (file)
@@ -260,10 +260,12 @@ static int ec_generate_key(EC_KEY *eckey, int pairwise_test)
 {
     int ok = 0;
     BIGNUM *priv_key = NULL;
-    const BIGNUM *order = NULL;
+    const BIGNUM *tmp = NULL;
+    BIGNUM *order = NULL;
     EC_POINT *pub_key = NULL;
     const EC_GROUP *group = eckey->group;
     BN_CTX *ctx = BN_CTX_secure_new_ex(eckey->libctx);
+    int sm2 = EC_KEY_get_flags(eckey) & EC_FLAG_SM2_RANGE ? 1 : 0;
 
     if (ctx == NULL)
         goto err;
@@ -281,8 +283,8 @@ static int ec_generate_key(EC_KEY *eckey, int pairwise_test)
      * stated in the security policy.
      */
 
-    order = EC_GROUP_get0_order(group);
-    if (order == NULL)
+    tmp = EC_GROUP_get0_order(group);
+    if (tmp == NULL)
         goto err;
 
     /*
@@ -293,6 +295,18 @@ static int ec_generate_key(EC_KEY *eckey, int pairwise_test)
      * 1 + rand[0..n-2] would effect the way that tests feed dummy entropy into
      * rand so the simpler backward compatible method has been used here.
      */
+
+    /* range of SM2 private key is [1, n-1) */
+    if (sm2) {
+        order = BN_new();
+        if (order == NULL || !BN_sub(order, tmp, BN_value_one()))
+            goto err;
+    } else {
+        order = BN_dup(tmp);
+        if (order == NULL)
+            goto err;
+    }
+
     do
         if (!BN_priv_rand_range_ex(priv_key, order, ctx))
             goto err;
@@ -340,6 +354,7 @@ err:
     EC_POINT_free(pub_key);
     BN_clear_free(priv_key);
     BN_CTX_free(ctx);
+    BN_free(order);
     return ok;
 }
 
index b8dad20abd1e436d98a2fe0735e17445817f7050..f370ad05fb6d999e34df04155a2881b1e2464896 100644 (file)
 #include "crypto/evp.h"
 #include "evp_local.h"
 
-#if !defined(FIPS_MODULE) && !defined(OPENSSL_NO_EC)
-# define TMP_SM2_HACK
-#endif
-
 /* TODO(3.0) remove when provider SM2 key generation is implemented */
 #ifdef TMP_SM2_HACK
 # include <openssl/ec.h>
index ed83e3bcb0a7cb11bb49686000b6ab40e7edbc58..04e648681bef621f6b30d8ff30e6dd9b4a29145b 100644 (file)
@@ -874,6 +874,7 @@ int ECPKParameters_print_fp(FILE *fp, const EC_GROUP *x, int off);
 #  define EC_FLAG_NON_FIPS_ALLOW  0x1
 #  define EC_FLAG_FIPS_CHECKED    0x2
 #  define EC_FLAG_COFACTOR_ECDH   0x1000
+#  define EC_FLAG_SM2_RANGE       0x4
 
 /**
  *  Creates a new EC_KEY object.
index 8f663affbbfb4134bc4ec040962b4cbb8ba0aec2..fabb56bfec130e6ea73e725d7fbd3a67597622ca 100644 (file)
@@ -413,6 +413,8 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
 #endif
 #ifndef OPENSSL_NO_CMAC
     { "CMAC", "provider=default", cmac_legacy_keymgmt_functions },
+#ifndef OPENSSL_NO_SM2
+    { "SM2", "provider=default", sm2_keymgmt_functions },
 #endif
     { NULL, NULL, NULL }
 };
index 5c5f47f12eb258926fcdc5bfb32b628c53875578..748ccf46fbc42f18824d8d85b1f09bb55ed75404 100644 (file)
@@ -280,6 +280,9 @@ extern const OSSL_DISPATCH ec_keymgmt_functions[];
 extern const OSSL_DISPATCH kdf_keymgmt_functions[];
 extern const OSSL_DISPATCH mac_legacy_keymgmt_functions[];
 extern const OSSL_DISPATCH cmac_legacy_keymgmt_functions[];
+#ifndef OPENSSL_NO_SM2
+extern const OSSL_DISPATCH sm2_keymgmt_functions[];
+#endif
 
 /* Key Exchange */
 extern const OSSL_DISPATCH dh_keyexch_functions[];
index 6e493caa3be33fb94b6fc423297827012169e62a..07cba1962e3d700cd98b10441c3c4cc8a684bead 100644 (file)
@@ -27,6 +27,7 @@
 #include "prov/providercommonerr.h"
 #include "prov/provider_ctx.h"
 #include "internal/param_build_set.h"
+#include "crypto/sm2.h"
 
 static OSSL_FUNC_keymgmt_new_fn ec_newdata;
 static OSSL_FUNC_keymgmt_gen_init_fn ec_gen_init;
@@ -49,10 +50,19 @@ static OSSL_FUNC_keymgmt_import_types_fn ec_import_types;
 static OSSL_FUNC_keymgmt_export_fn ec_export;
 static OSSL_FUNC_keymgmt_export_types_fn ec_export_types;
 static OSSL_FUNC_keymgmt_query_operation_name_fn ec_query_operation_name;
+#ifndef OPENSSL_NO_SM2
+static OSSL_FUNC_keymgmt_gen_fn sm2_gen;
+static OSSL_FUNC_keymgmt_get_params_fn sm2_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn sm2_gettable_params;
+static OSSL_FUNC_keymgmt_settable_params_fn sm2_settable_params;
+static OSSL_FUNC_keymgmt_import_fn sm2_import;
+static OSSL_FUNC_keymgmt_query_operation_name_fn sm2_query_operation_name;
+#endif
 
 #define EC_DEFAULT_MD "SHA256"
 #define EC_POSSIBLE_SELECTIONS                                                 \
     (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)
+#define SM2_DEFAULT_MD "SM3"
 
 static
 const char *ec_query_operation_name(int operation_id)
@@ -66,6 +76,53 @@ const char *ec_query_operation_name(int operation_id)
     return NULL;
 }
 
+#ifndef OPENSSL_NO_SM2
+static
+const char *sm2_query_operation_name(int operation_id)
+{
+    switch (operation_id) {
+    case OSSL_OP_SIGNATURE:
+        return "SM2";
+    }
+    return NULL;
+}
+#endif
+
+static ossl_inline
+int domparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl,
+                        OSSL_PARAM params[])
+{
+    const EC_GROUP *ecg;
+    int curve_nid;
+
+    if (ec == NULL)
+        return 0;
+
+    ecg = EC_KEY_get0_group(ec);
+    if (ecg == NULL)
+        return 0;
+
+    curve_nid = EC_GROUP_get_curve_name(ecg);
+
+    if (curve_nid == NID_undef) {
+        /* TODO(3.0): should we support explicit parameters curves? */
+        return 0;
+    } else {
+        /* named curve */
+        const char *curve_name = NULL;
+
+        if ((curve_name = ec_curve_nid2name(curve_nid)) == NULL)
+            return 0;
+        if (!ossl_param_build_set_utf8_string(tmpl, params,
+                                              OSSL_PKEY_PARAM_GROUP_NAME,
+                                              curve_name))
+
+            return 0;
+    }
+
+    return 1;
+}
+
 /*
  * Callers of key_to_params MUST make sure that domparams_to_params is also
  * called!
@@ -283,6 +340,7 @@ static
 int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
 {
     EC_KEY *ec = keydata;
+    const EC_GROUP *ecg = NULL;
     int ok = 1;
 
     if (!ossl_prov_is_running() || ec == NULL)
@@ -309,6 +367,61 @@ int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
 
     if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
         ok = ok && ec_group_fromdata(ec, params);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        int include_private =
+            selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+        ok = ok && ec_key_fromdata(ec, params, include_private);
+    }
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+        ok = ok && ec_key_otherparams_fromdata(ec, params);
+
+    return ok;
+}
+
+#ifndef OPENSSL_NO_SM2
+static
+int sm2_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+    EC_KEY *ec = keydata;
+    const EC_GROUP *ecg = NULL;
+    int ok = 1;
+
+    if (ec == NULL)
+        return 0;
+
+    /*
+     * In this implementation, we can export/import only keydata in the
+     * following combinations:
+     *   - domain parameters only
+     *   - public key with associated domain parameters (+optional other params)
+     *   - private key with associated public key and domain parameters
+     *         (+optional other params)
+     *
+     * This means:
+     *   - domain parameters must always be requested
+     *   - private key must be requested alongside public key
+     *   - other parameters must be requested only alongside a key
+     */
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
+        return 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
+        return 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0
+            && (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return 0;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        ok = ok && ec_key_domparams_fromdata(ec, params);
+
+    /* import the keys or domparams only on SM2 Curve */
+    if ((ecg = EC_KEY_get0_group(ec)) == NULL
+            || EC_GROUP_get_curve_name(ecg) != NID_sm2) {
+        return 0;
+    }
+
     if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
         int include_private =
             selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
@@ -320,6 +433,7 @@ int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
 
     return ok;
 }
+#endif
 
 static
 int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
@@ -679,6 +793,87 @@ int ec_set_params(void *key, const OSSL_PARAM params[])
     return ec_key_otherparams_fromdata(eck, params);
 }
 
+#ifndef OPENSSL_NO_SM2
+static
+int sm2_get_params(void *key, OSSL_PARAM params[])
+{
+    int ret;
+    EC_KEY *eck = key;
+    const EC_GROUP *ecg = NULL;
+    OSSL_PARAM *p;
+    unsigned char *pub_key = NULL;
+
+    ecg = EC_KEY_get0_group(eck);
+    if (ecg == NULL)
+        return 0;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+        && !OSSL_PARAM_set_int(p, ECDSA_size(eck)))
+        return 0;
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+        && !OSSL_PARAM_set_int(p, EC_GROUP_order_bits(ecg)))
+        return 0;
+
+    /* XXX:
+     * I dropped the support of OSSL_PKEY_PARAM_SECURITY_BITS since
+     * I didn't find definition of SM2 security bits so far. This could
+     * be supported if the definition is clear in the future.
+     */
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
+        && !OSSL_PARAM_set_utf8_string(p, SM2_DEFAULT_MD))
+        return 0;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_TLS_ENCODED_PT)) != NULL) {
+        BN_CTX *ctx = BN_CTX_new_ex(ec_key_get_libctx(key));
+
+        if (ctx == NULL)
+            return 0;
+        p->return_size = EC_POINT_point2oct(EC_KEY_get0_group(key),
+                                            EC_KEY_get0_public_key(key),
+                                            POINT_CONVERSION_UNCOMPRESSED,
+                                            p->data, p->return_size, ctx);
+        BN_CTX_free(ctx);
+        if (p->return_size == 0)
+            return 0;
+    }
+
+    ret = domparams_to_params(eck, NULL, params)
+          && key_to_params(eck, NULL, params, 1, &pub_key);
+    OPENSSL_free(pub_key);
+    return ret;
+}
+
+static const OSSL_PARAM sm2_known_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_TLS_ENCODED_PT, NULL, 0),
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0),
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *sm2_gettable_params(void)
+{
+    return sm2_known_gettable_params;
+}
+
+static const OSSL_PARAM sm2_known_settable_params[] = {
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_TLS_ENCODED_PT, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *sm2_settable_params(void)
+{
+    return sm2_known_settable_params;
+}
+#endif
+
 static
 int ec_validate(void *keydata, int selection)
 {
@@ -975,6 +1170,39 @@ err:
     return NULL;
 }
 
+#ifndef OPENSSL_NO_SM2
+/*
+ * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation
+ */
+static void *sm2_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+    struct ec_gen_ctx *gctx = genctx;
+    EC_KEY *ec = NULL;
+    int ret = 1;
+
+    if (gctx == NULL
+        || (ec = EC_KEY_new_with_libctx(gctx->libctx, NULL)) == NULL)
+        return NULL;
+
+    /* We must always assign a group, no matter what */
+    ret = ec_gen_assign_group(ec, gctx->gen_group);
+    /* Whether you want it or not, you get a keypair, not just one half */
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        /*
+         * For SM2, we need a new flag to indicate the 'generate' function
+         * to use a new range
+         */
+        EC_KEY_set_flags(ec, EC_FLAG_SM2_RANGE);
+        ret = ret && EC_KEY_generate_key(ec);
+    }
+
+    if (ret)
+        return ec;
+
+    return NULL;
+}
+#endif
+
 static void ec_gen_cleanup(void *genctx)
 {
     struct ec_gen_ctx *gctx = genctx;
@@ -1037,3 +1265,32 @@ const OSSL_DISPATCH ec_keymgmt_functions[] = {
       (void (*)(void))ec_query_operation_name },
     { 0, NULL }
 };
+
+#ifndef OPENSSL_NO_SM2
+const OSSL_DISPATCH sm2_keymgmt_functions[] = {
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ec_newdata },
+    { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))ec_gen_init },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+      (void (*)(void))ec_gen_set_template },
+    { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ec_gen_set_params },
+    { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+      (void (*)(void))ec_gen_settable_params },
+    { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sm2_gen },
+    { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ec_gen_cleanup },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ec_freedata },
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))sm2_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))sm2_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))ec_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))sm2_settable_params },
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ec_has },
+    { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ec_match },
+    { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ec_validate },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sm2_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ec_import_types },
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ec_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ec_export_types },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+      (void (*)(void))sm2_query_operation_name },
+    { 0, NULL }
+};
+#endif
index 471aaa184dd1cb063d1201d40f174adb6a5dc30d..8340d289124c9de57a6676bfd6ccafe0a5ffe994 100644 (file)
@@ -237,6 +237,20 @@ static int test_builtin(int n, int as)
         return 1;
     }
 
+    /*
+     * skip SM2 curve if 'as' is equal to EVP_PKEY_EC or, skip all curves
+     * except SM2 curve if 'as' is equal to EVP_PKEY_SM2
+     */
+    if (nid == NID_sm2 && as == EVP_PKEY_EC) {
+        TEST_info("skipped: EC key type unsupported for curve %s",
+                  OBJ_nid2sn(nid));
+        return 1;
+    } else if (nid != NID_sm2 && as == EVP_PKEY_SM2) {
+        TEST_info("skipped: SM2 key type unsupported for curve %s",
+                  OBJ_nid2sn(nid));
+        return 1;
+    }
+
     TEST_info("testing ECDSA for curve %s as %s key type", OBJ_nid2sn(nid),
               as == EVP_PKEY_EC ? "EC" : "SM2");