diff --git a/CHANGES.md b/CHANGES.md index ec815915a2..65031b89a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,14 @@ OpenSSL 3.0 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx] + * Validation of SM2 keys has been separated from the validation of regular EC + keys, allowing to improve the SM2 validation process to reject loaded private + keys that are not conforming to the SM2 ISO standard. + In particular, a private scalar `k` outside the range `1 <= k < n-1` is now + correctly rejected. + + *Nicola Tuveri* + * Behavior of the `pkey` app is changed, when using the `-check` or `-pubcheck` switches: a validation failure triggers an early exit, returning a failure exit status to the parent process. diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index 5440e47093..4e36fc3394 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -3103,6 +3103,7 @@ SM2_R_INVALID_DIGEST:102:invalid digest SM2_R_INVALID_DIGEST_TYPE:103:invalid digest type SM2_R_INVALID_ENCODING:104:invalid encoding SM2_R_INVALID_FIELD:105:invalid field +SM2_R_INVALID_PRIVATE_KEY:113:invalid private key SM2_R_NO_PARAMETERS_SET:109:no parameters set SM2_R_USER_ID_TOO_LARGE:106:user id too large SSL_R_ALGORITHM_FETCH_FAILED:295:algorithm fetch failed diff --git a/crypto/sm2/build.info b/crypto/sm2/build.info index 402a76cc5d..a50d08d0bc 100644 --- a/crypto/sm2/build.info +++ b/crypto/sm2/build.info @@ -1,5 +1,5 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]=\ - sm2_sign.c sm2_crypt.c sm2_err.c + sm2_sign.c sm2_crypt.c sm2_err.c sm2_key.c diff --git a/crypto/sm2/sm2_err.c b/crypto/sm2/sm2_err.c index 60509e14d1..ab9c094a9d 100644 --- a/crypto/sm2/sm2_err.c +++ b/crypto/sm2/sm2_err.c @@ -28,6 +28,8 @@ static const ERR_STRING_DATA SM2_str_reasons[] = { "invalid digest type"}, {ERR_PACK(ERR_LIB_SM2, 0, SM2_R_INVALID_ENCODING), "invalid encoding"}, {ERR_PACK(ERR_LIB_SM2, 0, SM2_R_INVALID_FIELD), "invalid field"}, + {ERR_PACK(ERR_LIB_SM2, 0, SM2_R_INVALID_PRIVATE_KEY), + "invalid private key"}, {ERR_PACK(ERR_LIB_SM2, 0, SM2_R_NO_PARAMETERS_SET), "no parameters set"}, {ERR_PACK(ERR_LIB_SM2, 0, SM2_R_USER_ID_TOO_LARGE), "user id too large"}, {0, NULL} diff --git a/crypto/sm2/sm2_key.c b/crypto/sm2/sm2_key.c new file mode 100644 index 0000000000..5182d01058 --- /dev/null +++ b/crypto/sm2/sm2_key.c @@ -0,0 +1,49 @@ +/* + * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include "crypto/sm2err.h" +#include "crypto/sm2.h" +#include /* EC_KEY and EC_GROUP functions */ + +/* + * SM2 key generation is implemented within ec_generate_key() in + * crypto/ec/ec_key.c + */ + +int sm2_key_private_check(const EC_KEY *eckey) +{ + int ret = 0; + BIGNUM *max = NULL; + const EC_GROUP *group = NULL; + const BIGNUM *priv_key = NULL, *order = NULL; + + if (eckey == NULL + || (group = EC_KEY_get0_group(eckey)) == NULL + || (priv_key = EC_KEY_get0_private_key(eckey)) == NULL + || (order = EC_GROUP_get0_order(group)) == NULL ) { + ERR_raise(ERR_LIB_SM2, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + /* range of SM2 private key is [1, n-1) */ + max = BN_dup(order); + if (max == NULL || !BN_sub_word(max, 1)) + goto end; + if (BN_cmp(priv_key, BN_value_one()) < 0 + || BN_cmp(priv_key, max) >= 0) { + ERR_raise(ERR_LIB_SM2, SM2_R_INVALID_PRIVATE_KEY); + goto end; + } + ret = 1; + + end: + BN_free(max); + return ret; +} diff --git a/include/crypto/sm2.h b/include/crypto/sm2.h index fe87c84bba..e442e7aec7 100644 --- a/include/crypto/sm2.h +++ b/include/crypto/sm2.h @@ -17,6 +17,8 @@ # include +int sm2_key_private_check(const EC_KEY *eckey); + /* The default user id as specified in GM/T 0009-2012 */ # define SM2_DEFAULT_USERID "1234567812345678" diff --git a/include/crypto/sm2err.h b/include/crypto/sm2err.h index f8fabcb74e..fe081ddba8 100644 --- a/include/crypto/sm2err.h +++ b/include/crypto/sm2err.h @@ -61,6 +61,7 @@ int err_load_SM2_strings_int(void); # define SM2_R_INVALID_DIGEST_TYPE 103 # define SM2_R_INVALID_ENCODING 104 # define SM2_R_INVALID_FIELD 105 +# define SM2_R_INVALID_PRIVATE_KEY 113 # define SM2_R_NO_PARAMETERS_SET 109 # define SM2_R_USER_ID_TOO_LARGE 106 diff --git a/providers/implementations/keymgmt/build.info b/providers/implementations/keymgmt/build.info index 75f61a6de1..f434a720bc 100644 --- a/providers/implementations/keymgmt/build.info +++ b/providers/implementations/keymgmt/build.info @@ -1,7 +1,6 @@ # We make separate GOAL variables for each algorithm, to make it easy to # switch each to the Legacy provider when needed. -$EC_GOAL=../../libimplementations.a $ECX_GOAL=../../libimplementations.a $KDF_GOAL=../../libimplementations.a @@ -14,7 +13,8 @@ IF[{- !$disabled{dsa} -}] SOURCE[../../libnonfips.a]=dsa_kmgmt.c ENDIF IF[{- !$disabled{ec} -}] - SOURCE[$EC_GOAL]=ec_kmgmt.c + SOURCE[../../libfips.a]=ec_kmgmt.c + SOURCE[../../libnonfips.a]=ec_kmgmt.c ENDIF IF[{- !$disabled{asm} -}] diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c index 7e3fadc580..ac7094490e 100644 --- a/providers/implementations/keymgmt/ec_kmgmt.c +++ b/providers/implementations/keymgmt/ec_kmgmt.c @@ -27,7 +27,12 @@ #include "prov/providercommonerr.h" #include "prov/provider_ctx.h" #include "internal/param_build_set.h" -#include "crypto/sm2.h" + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +# include "crypto/sm2.h" +# endif +#endif static OSSL_FUNC_keymgmt_new_fn ec_newdata; static OSSL_FUNC_keymgmt_gen_init_fn ec_gen_init; @@ -50,13 +55,16 @@ 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 +#ifndef FIPS_MODULE +# 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; +static OSSL_FUNC_keymgmt_validate_fn sm2_validate; +# endif #endif #define EC_DEFAULT_MD "SHA256" @@ -76,7 +84,8 @@ const char *ec_query_operation_name(int operation_id) return NULL; } -#ifndef OPENSSL_NO_SM2 +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 static const char *sm2_query_operation_name(int operation_id) { @@ -86,6 +95,7 @@ const char *sm2_query_operation_name(int operation_id) } return NULL; } +# endif #endif /* @@ -364,12 +374,14 @@ int ec_import(void *keydata, int selection, const OSSL_PARAM params[]) return common_import(keydata, selection, params, 0); } -#ifndef OPENSSL_NO_SM2 +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 static int sm2_import(void *keydata, int selection, const OSSL_PARAM params[]) { return common_import(keydata, selection, params, 1); } +# endif #endif static @@ -746,7 +758,8 @@ int ec_set_params(void *key, const OSSL_PARAM params[]) return ec_key_otherparams_fromdata(eck, params); } -#ifndef OPENSSL_NO_SM2 +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 static int sm2_get_params(void *key, OSSL_PARAM params[]) { @@ -782,6 +795,40 @@ const OSSL_PARAM *sm2_settable_params(ossl_unused void *provctx) { return sm2_known_settable_params; } + +static +int sm2_validate(const void *keydata, int selection) +{ + const EC_KEY *eck = keydata; + int ok = 0; + BN_CTX *ctx = NULL; + + if (!ossl_prov_is_running()) + return 0; + + ctx = BN_CTX_new_ex(ec_key_get_libctx(eck)); + if (ctx == NULL) + return 0; + + if ((selection & EC_POSSIBLE_SELECTIONS) != 0) + ok = 1; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && EC_GROUP_check(EC_KEY_get0_group(eck), ctx); + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && ec_key_public_check(eck, ctx); + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && sm2_key_private_check(eck); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + ok = ok && ec_key_pairwise_check(eck, ctx); + + BN_CTX_free(ctx); + return ok; +} +# endif #endif static @@ -1084,7 +1131,8 @@ err: return NULL; } -#ifndef OPENSSL_NO_SM2 +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 /* * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation */ @@ -1130,6 +1178,7 @@ err: EC_KEY_free(ec); return NULL; } +# endif #endif static void ec_gen_cleanup(void *genctx) @@ -1195,7 +1244,8 @@ const OSSL_DISPATCH ossl_ec_keymgmt_functions[] = { { 0, NULL } }; -#ifndef OPENSSL_NO_SM2 +#ifndef FIPS_MODULE +# 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 }, @@ -1213,7 +1263,7 @@ const OSSL_DISPATCH sm2_keymgmt_functions[] = { { 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_VALIDATE, (void (*)(void))sm2_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 }, @@ -1222,4 +1272,5 @@ const OSSL_DISPATCH sm2_keymgmt_functions[] = { (void (*)(void))sm2_query_operation_name }, { 0, NULL } }; +# endif #endif