2017-04-24 15:13:20 +03:00
/*
* ECDH helper functions - KPP wrappings
*
* Copyright ( C ) 2017 Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation ;
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS
* OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS .
* IN NO EVENT SHALL THE COPYRIGHT HOLDER ( S ) AND AUTHOR ( S ) BE LIABLE FOR ANY
* CLAIM , OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES , OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*
* ALL LIABILITY , INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS ,
* COPYRIGHTS , TRADEMARKS OR OTHER RIGHTS , RELATING TO USE OF THIS
* SOFTWARE IS DISCLAIMED .
*/
# include "ecdh_helper.h"
# include <linux/scatterlist.h>
# include <crypto/ecdh.h>
struct ecdh_completion {
struct completion completion ;
int err ;
} ;
static void ecdh_complete ( struct crypto_async_request * req , int err )
{
struct ecdh_completion * res = req - > data ;
if ( err = = - EINPROGRESS )
return ;
res - > err = err ;
complete ( & res - > completion ) ;
}
static inline void swap_digits ( u64 * in , u64 * out , unsigned int ndigits )
{
int i ;
for ( i = 0 ; i < ndigits ; i + + )
out [ i ] = __swab64 ( in [ ndigits - 1 - i ] ) ;
}
2017-09-28 17:14:51 +03:00
bool compute_ecdh_secret ( struct crypto_kpp * tfm , const u8 public_key [ 64 ] ,
const u8 private_key [ 32 ] , u8 secret [ 32 ] )
2017-04-24 15:13:20 +03:00
{
struct kpp_request * req ;
struct ecdh p ;
struct ecdh_completion result ;
struct scatterlist src , dst ;
2017-04-25 18:59:47 +03:00
u8 * tmp , * buf ;
2017-04-24 15:13:20 +03:00
unsigned int buf_len ;
int err = - ENOMEM ;
2017-04-25 18:59:47 +03:00
tmp = kmalloc ( 64 , GFP_KERNEL ) ;
if ( ! tmp )
return false ;
2017-04-24 15:13:20 +03:00
req = kpp_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req )
2017-09-28 17:14:51 +03:00
goto free_tmp ;
2017-04-24 15:13:20 +03:00
init_completion ( & result . completion ) ;
/* Security Manager Protocol holds digits in litte-endian order
* while ECC API expect big - endian data
*/
swap_digits ( ( u64 * ) private_key , ( u64 * ) tmp , 4 ) ;
p . key = ( char * ) tmp ;
p . key_size = 32 ;
/* Set curve_id */
p . curve_id = ECC_CURVE_NIST_P256 ;
buf_len = crypto_ecdh_key_len ( & p ) ;
buf = kmalloc ( buf_len , GFP_KERNEL ) ;
2017-05-22 09:42:28 +03:00
if ( ! buf )
2017-04-24 15:13:20 +03:00
goto free_req ;
2017-05-22 09:42:28 +03:00
2017-04-24 15:13:20 +03:00
crypto_ecdh_encode_key ( buf , buf_len , & p ) ;
/* Set A private Key */
err = crypto_kpp_set_secret ( tfm , ( void * ) buf , buf_len ) ;
if ( err )
goto free_all ;
swap_digits ( ( u64 * ) public_key , ( u64 * ) tmp , 4 ) ; /* x */
swap_digits ( ( u64 * ) & public_key [ 32 ] , ( u64 * ) & tmp [ 32 ] , 4 ) ; /* y */
sg_init_one ( & src , tmp , 64 ) ;
sg_init_one ( & dst , secret , 32 ) ;
kpp_request_set_input ( req , & src , 64 ) ;
kpp_request_set_output ( req , & dst , 32 ) ;
kpp_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG ,
ecdh_complete , & result ) ;
err = crypto_kpp_compute_shared_secret ( req ) ;
if ( err = = - EINPROGRESS ) {
wait_for_completion ( & result . completion ) ;
err = result . err ;
}
if ( err < 0 ) {
pr_err ( " alg: ecdh: compute shared secret failed. err %d \n " ,
err ) ;
goto free_all ;
}
swap_digits ( ( u64 * ) secret , ( u64 * ) tmp , 4 ) ;
memcpy ( secret , tmp , 32 ) ;
free_all :
kzfree ( buf ) ;
free_req :
kpp_request_free ( req ) ;
2017-04-25 18:59:47 +03:00
free_tmp :
kfree ( tmp ) ;
2017-04-24 15:13:20 +03:00
return ( err = = 0 ) ;
}
2017-09-28 17:14:51 +03:00
bool generate_ecdh_keys ( struct crypto_kpp * tfm , u8 public_key [ 64 ] ,
u8 private_key [ 32 ] )
2017-04-24 15:13:20 +03:00
{
struct kpp_request * req ;
struct ecdh p ;
struct ecdh_completion result ;
struct scatterlist dst ;
2017-04-25 18:59:47 +03:00
u8 * tmp , * buf ;
2017-04-24 15:13:20 +03:00
unsigned int buf_len ;
int err = - ENOMEM ;
const unsigned short max_tries = 16 ;
unsigned short tries = 0 ;
2017-04-25 18:59:47 +03:00
tmp = kmalloc ( 64 , GFP_KERNEL ) ;
if ( ! tmp )
return false ;
2017-04-24 15:13:20 +03:00
req = kpp_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req )
2017-09-28 17:14:51 +03:00
goto free_tmp ;
2017-04-24 15:13:20 +03:00
init_completion ( & result . completion ) ;
/* Set curve_id */
p . curve_id = ECC_CURVE_NIST_P256 ;
p . key_size = 32 ;
buf_len = crypto_ecdh_key_len ( & p ) ;
buf = kmalloc ( buf_len , GFP_KERNEL ) ;
2017-05-22 09:42:28 +03:00
if ( ! buf )
2017-04-24 15:13:20 +03:00
goto free_req ;
do {
if ( tries + + > = max_tries )
goto free_all ;
/* Set private Key */
p . key = ( char * ) private_key ;
crypto_ecdh_encode_key ( buf , buf_len , & p ) ;
err = crypto_kpp_set_secret ( tfm , buf , buf_len ) ;
if ( err )
goto free_all ;
sg_init_one ( & dst , tmp , 64 ) ;
2017-04-30 16:51:40 +03:00
kpp_request_set_input ( req , NULL , 0 ) ;
2017-04-24 15:13:20 +03:00
kpp_request_set_output ( req , & dst , 64 ) ;
kpp_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG ,
ecdh_complete , & result ) ;
err = crypto_kpp_generate_public_key ( req ) ;
if ( err = = - EINPROGRESS ) {
wait_for_completion ( & result . completion ) ;
err = result . err ;
}
/* Private key is not valid. Regenerate */
if ( err = = - EINVAL )
continue ;
if ( err < 0 )
goto free_all ;
else
break ;
} while ( true ) ;
/* Keys are handed back in little endian as expected by Security
* Manager Protocol
*/
swap_digits ( ( u64 * ) tmp , ( u64 * ) public_key , 4 ) ; /* x */
swap_digits ( ( u64 * ) & tmp [ 32 ] , ( u64 * ) & public_key [ 32 ] , 4 ) ; /* y */
swap_digits ( ( u64 * ) private_key , ( u64 * ) tmp , 4 ) ;
memcpy ( private_key , tmp , 32 ) ;
free_all :
kzfree ( buf ) ;
free_req :
kpp_request_free ( req ) ;
2017-04-25 18:59:47 +03:00
free_tmp :
kfree ( tmp ) ;
2017-04-24 15:13:20 +03:00
return ( err = = 0 ) ;
}