2016-06-22 19:49:15 +03:00
/* ECDH key-agreement protocol
*
* Copyright ( c ) 2016 , Intel Corporation
* Authors : Salvator Benedetto < salvatore . benedetto @ intel . com >
*
* This program is free software ; you can redistribute it and / or
2017-05-25 10:18:03 +03:00
* modify it under the terms of the GNU General Public License
2016-06-22 19:49:15 +03:00
* as published by the Free Software Foundation ; either version
2017-05-25 10:18:03 +03:00
* 2 of the License , or ( at your option ) any later version .
2016-06-22 19:49:15 +03:00
*/
# include <linux/module.h>
# include <crypto/internal/kpp.h>
# include <crypto/kpp.h>
# include <crypto/ecdh.h>
# include <linux/scatterlist.h>
# include "ecc.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 ) ;
}
static unsigned int ecdh_supported_curve ( unsigned int curve_id )
{
switch ( curve_id ) {
2018-03-30 19:55:44 +03:00
case ECC_CURVE_NIST_P192 : return ECC_CURVE_NIST_P192_DIGITS ;
case ECC_CURVE_NIST_P256 : return ECC_CURVE_NIST_P256_DIGITS ;
2016-06-22 19:49:15 +03:00
default : return 0 ;
}
}
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 ;
unsigned int ndigits ;
if ( crypto_ecdh_decode_key ( buf , len , & params ) < 0 )
return - EINVAL ;
ndigits = ecdh_supported_curve ( params . curve_id ) ;
if ( ! ndigits )
return - EINVAL ;
ctx - > curve_id = params . curve_id ;
ctx - > ndigits = ndigits ;
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
if ( ecc_is_key_valid ( ctx - > curve_id , ctx - > ndigits ,
2017-05-25 10:18:05 +03:00
( const u64 * ) params . key , params . key_size ) < 0 )
2016-06-22 19:49:15 +03:00
return - EINVAL ;
memcpy ( ctx - > private_key , params . key , params . key_size ) ;
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 :
kzfree ( shared_secret ) ;
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
}
static struct kpp_alg ecdh = {
. set_secret = ecdh_set_secret ,
. generate_public_key = ecdh_compute_value ,
. compute_shared_secret = ecdh_compute_value ,
. max_size = ecdh_max_size ,
. base = {
. cra_name = " ecdh " ,
. cra_driver_name = " ecdh-generic " ,
. cra_priority = 100 ,
. cra_module = THIS_MODULE ,
. cra_ctxsize = sizeof ( struct ecdh_ctx ) ,
} ,
} ;
static int ecdh_init ( void )
{
return crypto_register_kpp ( & ecdh ) ;
}
static void ecdh_exit ( void )
{
crypto_unregister_kpp ( & ecdh ) ;
}
module_init ( ecdh_init ) ;
module_exit ( ecdh_exit ) ;
MODULE_ALIAS_CRYPTO ( " ecdh " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " ECDH generic algorithm " ) ;