2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-06-22 19:49:15 +03:00
/* ECDH key-agreement protocol
*
* Copyright ( c ) 2016 , Intel Corporation
* Authors : Salvator Benedetto < salvatore . benedetto @ intel . com >
*/
# include <linux/module.h>
2021-10-20 13:35:35 +03:00
# include <crypto/internal/ecc.h>
2016-06-22 19:49:15 +03:00
# include <crypto/internal/kpp.h>
# include <crypto/kpp.h>
# include <crypto/ecdh.h>
# include <linux/scatterlist.h>
struct ecdh_ctx {
unsigned int curve_id ;
unsigned int ndigits ;
u64 private_key [ ECC_MAX_DIGITS ] ;
} ;
static inline struct ecdh_ctx * ecdh_get_ctx ( struct crypto_kpp * tfm )
{
return kpp_tfm_ctx ( tfm ) ;
}
2017-02-25 02:46:58 +03:00
static int ecdh_set_secret ( struct crypto_kpp * tfm , const void * buf ,
unsigned int len )
2016-06-22 19:49:15 +03:00
{
struct ecdh_ctx * ctx = ecdh_get_ctx ( tfm ) ;
struct ecdh params ;
2021-01-02 16:59:09 +03:00
if ( crypto_ecdh_decode_key ( buf , len , & params ) < 0 | |
2021-03-04 09:35:46 +03:00
params . key_size > sizeof ( u64 ) * ctx - > ndigits )
2016-06-22 19:49:15 +03:00
return - EINVAL ;
2017-05-30 17:52:48 +03:00
if ( ! params . key | | ! params . key_size )
return ecc_gen_privkey ( ctx - > curve_id , ctx - > ndigits ,
ctx - > private_key ) ;
2016-06-22 19:49:15 +03:00
memcpy ( ctx - > private_key , params . key , params . key_size ) ;
2020-11-24 13:47:19 +03:00
if ( ecc_is_key_valid ( ctx - > curve_id , ctx - > ndigits ,
ctx - > private_key , params . key_size ) < 0 ) {
memzero_explicit ( ctx - > private_key , params . key_size ) ;
return - EINVAL ;
}
2016-06-22 19:49:15 +03:00
return 0 ;
}
static int ecdh_compute_value ( struct kpp_request * req )
{
struct crypto_kpp * tfm = crypto_kpp_reqtfm ( req ) ;
struct ecdh_ctx * ctx = ecdh_get_ctx ( tfm ) ;
2017-07-20 10:37:39 +03:00
u64 * public_key ;
u64 * shared_secret = NULL ;
2016-06-22 19:49:15 +03:00
void * buf ;
2017-07-20 10:37:39 +03:00
size_t copied , nbytes , public_key_sz ;
int ret = - ENOMEM ;
2016-06-22 19:49:15 +03:00
nbytes = ctx - > ndigits < < ECC_DIGITS_TO_BYTES_SHIFT ;
2017-07-20 10:37:39 +03:00
/* Public part is a point thus it has both coordinates */
public_key_sz = 2 * nbytes ;
public_key = kmalloc ( public_key_sz , GFP_KERNEL ) ;
if ( ! public_key )
return - ENOMEM ;
2016-06-22 19:49:15 +03:00
if ( req - > src ) {
2017-07-20 10:37:39 +03:00
shared_secret = kmalloc ( nbytes , GFP_KERNEL ) ;
if ( ! shared_secret )
goto free_pubkey ;
2018-03-02 01:37:42 +03:00
/* from here on it's invalid parameters */
ret = - EINVAL ;
/* must have exactly two points to be on the curve */
if ( public_key_sz ! = req - > src_len )
goto free_all ;
copied = sg_copy_to_buffer ( req - > src ,
sg_nents_for_len ( req - > src ,
public_key_sz ) ,
public_key , public_key_sz ) ;
if ( copied ! = public_key_sz )
2017-07-20 10:37:39 +03:00
goto free_all ;
2016-06-22 19:49:15 +03:00
2016-06-24 09:20:22 +03:00
ret = crypto_ecdh_shared_secret ( ctx - > curve_id , ctx - > ndigits ,
2017-07-20 10:37:39 +03:00
ctx - > private_key , public_key ,
shared_secret ) ;
2016-06-22 19:49:15 +03:00
2017-07-20 10:37:39 +03:00
buf = shared_secret ;
2016-06-22 19:49:15 +03:00
} else {
2017-05-30 15:37:56 +03:00
ret = ecc_make_pub_key ( ctx - > curve_id , ctx - > ndigits ,
2017-07-20 10:37:39 +03:00
ctx - > private_key , public_key ) ;
buf = public_key ;
nbytes = public_key_sz ;
2016-06-22 19:49:15 +03:00
}
if ( ret < 0 )
2017-07-20 10:37:39 +03:00
goto free_all ;
2016-06-22 19:49:15 +03:00
2018-03-02 01:37:42 +03:00
/* might want less than we've got */
nbytes = min_t ( size_t , nbytes , req - > dst_len ) ;
copied = sg_copy_from_buffer ( req - > dst , sg_nents_for_len ( req - > dst ,
nbytes ) ,
buf , nbytes ) ;
2016-06-22 19:49:15 +03:00
if ( copied ! = nbytes )
2017-07-20 10:37:39 +03:00
ret = - EINVAL ;
2016-06-22 19:49:15 +03:00
2017-07-20 10:37:39 +03:00
/* fall through */
free_all :
2020-08-07 09:18:13 +03:00
kfree_sensitive ( shared_secret ) ;
2017-07-20 10:37:39 +03:00
free_pubkey :
kfree ( public_key ) ;
2016-06-22 19:49:15 +03:00
return ret ;
}
2017-05-25 10:18:10 +03:00
static unsigned int ecdh_max_size ( struct crypto_kpp * tfm )
2016-06-22 19:49:15 +03:00
{
struct ecdh_ctx * ctx = ecdh_get_ctx ( tfm ) ;
2017-05-25 10:18:10 +03:00
/* Public key is made of two coordinates, add one to the left shift */
return ctx - > ndigits < < ( ECC_DIGITS_TO_BYTES_SHIFT + 1 ) ;
2016-06-22 19:49:15 +03:00
}
2021-03-04 09:35:46 +03:00
static int ecdh_nist_p192_init_tfm ( struct crypto_kpp * tfm )
{
struct ecdh_ctx * ctx = ecdh_get_ctx ( tfm ) ;
ctx - > curve_id = ECC_CURVE_NIST_P192 ;
ctx - > ndigits = ECC_CURVE_NIST_P192_DIGITS ;
return 0 ;
}
static struct kpp_alg ecdh_nist_p192 = {
2016-06-22 19:49:15 +03:00
. set_secret = ecdh_set_secret ,
. generate_public_key = ecdh_compute_value ,
. compute_shared_secret = ecdh_compute_value ,
. max_size = ecdh_max_size ,
2021-03-04 09:35:46 +03:00
. init = ecdh_nist_p192_init_tfm ,
2016-06-22 19:49:15 +03:00
. base = {
2021-03-04 09:35:46 +03:00
. cra_name = " ecdh-nist-p192 " ,
2021-05-10 11:59:47 +03:00
. cra_driver_name = " ecdh-nist-p192-generic " ,
2016-06-22 19:49:15 +03:00
. cra_priority = 100 ,
. cra_module = THIS_MODULE ,
. cra_ctxsize = sizeof ( struct ecdh_ctx ) ,
} ,
} ;
2021-03-04 09:35:46 +03:00
static int ecdh_nist_p256_init_tfm ( struct crypto_kpp * tfm )
{
struct ecdh_ctx * ctx = ecdh_get_ctx ( tfm ) ;
ctx - > curve_id = ECC_CURVE_NIST_P256 ;
ctx - > ndigits = ECC_CURVE_NIST_P256_DIGITS ;
return 0 ;
}
static struct kpp_alg ecdh_nist_p256 = {
. set_secret = ecdh_set_secret ,
. generate_public_key = ecdh_compute_value ,
. compute_shared_secret = ecdh_compute_value ,
. max_size = ecdh_max_size ,
. init = ecdh_nist_p256_init_tfm ,
. base = {
. cra_name = " ecdh-nist-p256 " ,
2021-05-10 11:59:47 +03:00
. cra_driver_name = " ecdh-nist-p256-generic " ,
2021-03-04 09:35:46 +03:00
. cra_priority = 100 ,
. cra_module = THIS_MODULE ,
. cra_ctxsize = sizeof ( struct ecdh_ctx ) ,
} ,
} ;
2021-05-22 05:44:30 +03:00
static int ecdh_nist_p384_init_tfm ( struct crypto_kpp * tfm )
{
struct ecdh_ctx * ctx = ecdh_get_ctx ( tfm ) ;
ctx - > curve_id = ECC_CURVE_NIST_P384 ;
ctx - > ndigits = ECC_CURVE_NIST_P384_DIGITS ;
return 0 ;
}
static struct kpp_alg ecdh_nist_p384 = {
. set_secret = ecdh_set_secret ,
. generate_public_key = ecdh_compute_value ,
. compute_shared_secret = ecdh_compute_value ,
. max_size = ecdh_max_size ,
. init = ecdh_nist_p384_init_tfm ,
. base = {
. cra_name = " ecdh-nist-p384 " ,
. cra_driver_name = " ecdh-nist-p384-generic " ,
. cra_priority = 100 ,
. cra_module = THIS_MODULE ,
. cra_ctxsize = sizeof ( struct ecdh_ctx ) ,
} ,
} ;
2021-03-04 09:35:46 +03:00
static bool ecdh_nist_p192_registered ;
2016-06-22 19:49:15 +03:00
static int ecdh_init ( void )
{
2021-03-04 09:35:46 +03:00
int ret ;
2021-05-22 05:44:28 +03:00
/* NIST p192 will fail to register in FIPS mode */
2021-03-04 09:35:46 +03:00
ret = crypto_register_kpp ( & ecdh_nist_p192 ) ;
ecdh_nist_p192_registered = ret = = 0 ;
2021-05-22 05:44:29 +03:00
ret = crypto_register_kpp ( & ecdh_nist_p256 ) ;
if ( ret )
goto nist_p256_error ;
2021-05-22 05:44:30 +03:00
ret = crypto_register_kpp ( & ecdh_nist_p384 ) ;
if ( ret )
goto nist_p384_error ;
2021-05-22 05:44:29 +03:00
return 0 ;
2021-05-22 05:44:30 +03:00
nist_p384_error :
crypto_unregister_kpp ( & ecdh_nist_p256 ) ;
2021-05-22 05:44:29 +03:00
nist_p256_error :
if ( ecdh_nist_p192_registered )
crypto_unregister_kpp ( & ecdh_nist_p192 ) ;
return ret ;
2016-06-22 19:49:15 +03:00
}
static void ecdh_exit ( void )
{
2021-03-04 09:35:46 +03:00
if ( ecdh_nist_p192_registered )
crypto_unregister_kpp ( & ecdh_nist_p192 ) ;
crypto_unregister_kpp ( & ecdh_nist_p256 ) ;
2021-05-22 05:44:30 +03:00
crypto_unregister_kpp ( & ecdh_nist_p384 ) ;
2016-06-22 19:49:15 +03:00
}
2019-04-12 07:57:42 +03:00
subsys_initcall ( ecdh_init ) ;
2016-06-22 19:49:15 +03:00
module_exit ( ecdh_exit ) ;
MODULE_ALIAS_CRYPTO ( " ecdh " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " ECDH generic algorithm " ) ;