2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2016-04-12 21:54:58 +03:00
/* Crypto operations using stored keys
*
* Copyright ( c ) 2016 , Intel Corporation
*/
# include <linux/slab.h>
# include <linux/uaccess.h>
2017-06-08 16:50:11 +03:00
# include <linux/scatterlist.h>
2016-08-19 21:39:09 +03:00
# include <linux/crypto.h>
# include <crypto/hash.h>
2017-06-08 16:50:11 +03:00
# include <crypto/kpp.h>
# include <crypto/dh.h>
2021-11-19 09:59:09 +03:00
# include <crypto/kdf_sp800108.h>
2016-04-12 21:54:58 +03:00
# include <keys/user-type.h>
# include "internal.h"
2022-02-21 15:10:50 +03:00
static ssize_t dh_data_from_key ( key_serial_t keyid , const void * * data )
2016-04-12 21:54:58 +03:00
{
struct key * key ;
key_ref_t key_ref ;
long status ;
ssize_t ret ;
key_ref = lookup_user_key ( keyid , 0 , KEY_NEED_READ ) ;
if ( IS_ERR ( key_ref ) ) {
ret = - ENOKEY ;
goto error ;
}
key = key_ref_to_ptr ( key_ref ) ;
ret = - EOPNOTSUPP ;
if ( key - > type = = & key_type_user ) {
down_read ( & key - > sem ) ;
status = key_validate ( key ) ;
if ( status = = 0 ) {
const struct user_key_payload * payload ;
2017-06-08 16:50:11 +03:00
uint8_t * duplicate ;
2016-04-12 21:54:58 +03:00
2017-03-01 18:11:23 +03:00
payload = user_key_payload_locked ( key ) ;
2016-04-12 21:54:58 +03:00
2017-06-08 16:50:11 +03:00
duplicate = kmemdup ( payload - > data , payload - > datalen ,
GFP_KERNEL ) ;
if ( duplicate ) {
* data = duplicate ;
2016-04-12 21:54:58 +03:00
ret = payload - > datalen ;
} else {
2017-06-08 16:50:11 +03:00
ret = - ENOMEM ;
2016-04-12 21:54:58 +03:00
}
}
up_read ( & key - > sem ) ;
}
key_put ( key ) ;
error :
return ret ;
}
2017-06-08 16:50:11 +03:00
static void dh_free_data ( struct dh * dh )
{
2020-08-07 09:18:13 +03:00
kfree_sensitive ( dh - > key ) ;
kfree_sensitive ( dh - > p ) ;
kfree_sensitive ( dh - > g ) ;
2017-06-08 16:50:11 +03:00
}
struct dh_completion {
struct completion completion ;
int err ;
} ;
static void dh_crypto_done ( struct crypto_async_request * req , int err )
{
struct dh_completion * compl = req - > data ;
if ( err = = - EINPROGRESS )
return ;
compl - > err = err ;
complete ( & compl - > completion ) ;
}
2021-11-19 09:59:09 +03:00
static int kdf_alloc ( struct crypto_shash * * hash , char * hashname )
2016-08-19 21:39:09 +03:00
{
struct crypto_shash * tfm ;
/* allocate synchronous hash */
tfm = crypto_alloc_shash ( hashname , 0 , 0 ) ;
if ( IS_ERR ( tfm ) ) {
pr_info ( " could not allocate digest TFM handle %s \n " , hashname ) ;
return PTR_ERR ( tfm ) ;
}
2021-11-19 09:59:09 +03:00
if ( crypto_shash_digestsize ( tfm ) = = 0 ) {
crypto_free_shash ( tfm ) ;
return - EINVAL ;
}
2016-08-19 21:39:09 +03:00
2021-11-19 09:59:09 +03:00
* hash = tfm ;
2016-08-19 21:39:09 +03:00
return 0 ;
}
2021-11-19 09:59:09 +03:00
static void kdf_dealloc ( struct crypto_shash * hash )
2016-08-19 21:39:09 +03:00
{
2021-11-19 09:59:09 +03:00
if ( hash )
crypto_free_shash ( hash ) ;
2016-08-19 21:39:09 +03:00
}
2021-11-19 09:59:09 +03:00
static int keyctl_dh_compute_kdf ( struct crypto_shash * hash ,
2016-08-19 21:39:09 +03:00
char __user * buffer , size_t buflen ,
2021-11-19 09:58:44 +03:00
uint8_t * kbuf , size_t kbuflen )
2016-08-19 21:39:09 +03:00
{
2021-11-19 09:59:09 +03:00
struct kvec kbuf_iov = { . iov_base = kbuf , . iov_len = kbuflen } ;
2016-08-19 21:39:09 +03:00
uint8_t * outbuf = NULL ;
int ret ;
2021-11-19 09:59:09 +03:00
size_t outbuf_len = roundup ( buflen , crypto_shash_digestsize ( hash ) ) ;
2016-08-19 21:39:09 +03:00
2018-04-24 23:26:38 +03:00
outbuf = kmalloc ( outbuf_len , GFP_KERNEL ) ;
2016-08-19 21:39:09 +03:00
if ( ! outbuf ) {
ret = - ENOMEM ;
goto err ;
}
2021-11-19 09:59:09 +03:00
ret = crypto_kdf108_ctr_generate ( hash , & kbuf_iov , 1 , outbuf , outbuf_len ) ;
2016-08-19 21:39:09 +03:00
if ( ret )
goto err ;
ret = buflen ;
if ( copy_to_user ( buffer , outbuf , buflen ) ! = 0 )
ret = - EFAULT ;
err :
2020-08-07 09:18:13 +03:00
kfree_sensitive ( outbuf ) ;
2016-08-19 21:39:09 +03:00
return ret ;
}
long __keyctl_dh_compute ( struct keyctl_dh_params __user * params ,
char __user * buffer , size_t buflen ,
struct keyctl_kdf_params * kdfcopy )
2016-04-12 21:54:58 +03:00
{
long ret ;
2017-06-08 16:50:11 +03:00
ssize_t dlen ;
int secretlen ;
int outlen ;
2016-04-12 21:54:58 +03:00
struct keyctl_dh_params pcopy ;
2017-06-08 16:50:11 +03:00
struct dh dh_inputs ;
struct scatterlist outsg ;
struct dh_completion compl ;
struct crypto_kpp * tfm ;
struct kpp_request * req ;
uint8_t * secret ;
uint8_t * outbuf ;
2021-11-19 09:59:09 +03:00
struct crypto_shash * hash = NULL ;
2016-04-12 21:54:58 +03:00
if ( ! params | | ( ! buffer & & buflen ) ) {
ret = - EINVAL ;
2017-06-08 16:50:11 +03:00
goto out1 ;
2016-04-12 21:54:58 +03:00
}
if ( copy_from_user ( & pcopy , params , sizeof ( pcopy ) ) ! = 0 ) {
ret = - EFAULT ;
2017-06-08 16:50:11 +03:00
goto out1 ;
2016-04-12 21:54:58 +03:00
}
2016-08-19 21:39:09 +03:00
if ( kdfcopy ) {
char * hashname ;
2017-07-13 15:16:56 +03:00
if ( memchr_inv ( kdfcopy - > __spare , 0 , sizeof ( kdfcopy - > __spare ) ) ) {
ret = - EINVAL ;
goto out1 ;
}
2016-08-19 21:39:09 +03:00
if ( buflen > KEYCTL_KDF_MAX_OUTPUT_LEN | |
kdfcopy - > otherinfolen > KEYCTL_KDF_MAX_OI_LEN ) {
ret = - EMSGSIZE ;
2017-06-08 16:50:11 +03:00
goto out1 ;
2016-08-19 21:39:09 +03:00
}
/* get KDF name string */
hashname = strndup_user ( kdfcopy - > hashname , CRYPTO_MAX_ALG_NAME ) ;
if ( IS_ERR ( hashname ) ) {
ret = PTR_ERR ( hashname ) ;
2017-06-08 16:50:11 +03:00
goto out1 ;
2016-08-19 21:39:09 +03:00
}
/* allocate KDF from the kernel crypto API */
2021-11-19 09:59:09 +03:00
ret = kdf_alloc ( & hash , hashname ) ;
2016-08-19 21:39:09 +03:00
kfree ( hashname ) ;
if ( ret )
2017-06-08 16:50:11 +03:00
goto out1 ;
2016-05-27 00:38:12 +03:00
}
2017-06-08 16:50:11 +03:00
memset ( & dh_inputs , 0 , sizeof ( dh_inputs ) ) ;
dlen = dh_data_from_key ( pcopy . prime , & dh_inputs . p ) ;
if ( dlen < 0 ) {
ret = dlen ;
goto out1 ;
2016-04-12 21:54:58 +03:00
}
2017-06-08 16:50:11 +03:00
dh_inputs . p_size = dlen ;
2016-04-12 21:54:58 +03:00
2017-06-08 16:50:11 +03:00
dlen = dh_data_from_key ( pcopy . base , & dh_inputs . g ) ;
if ( dlen < 0 ) {
ret = dlen ;
goto out2 ;
}
dh_inputs . g_size = dlen ;
2016-04-12 21:54:58 +03:00
2018-09-24 15:18:34 +03:00
dlen = dh_data_from_key ( pcopy . private , & dh_inputs . key ) ;
2017-06-08 16:50:11 +03:00
if ( dlen < 0 ) {
ret = dlen ;
goto out2 ;
2016-04-12 21:54:58 +03:00
}
2017-06-08 16:50:11 +03:00
dh_inputs . key_size = dlen ;
2016-04-12 21:54:58 +03:00
2017-06-08 16:50:11 +03:00
secretlen = crypto_dh_key_len ( & dh_inputs ) ;
secret = kmalloc ( secretlen , GFP_KERNEL ) ;
if ( ! secret ) {
ret = - ENOMEM ;
goto out2 ;
}
ret = crypto_dh_encode_key ( secret , secretlen , & dh_inputs ) ;
if ( ret )
goto out3 ;
2018-07-01 01:16:16 +03:00
tfm = crypto_alloc_kpp ( " dh " , 0 , 0 ) ;
2017-06-08 16:50:11 +03:00
if ( IS_ERR ( tfm ) ) {
ret = PTR_ERR ( tfm ) ;
goto out3 ;
}
ret = crypto_kpp_set_secret ( tfm , secret , secretlen ) ;
if ( ret )
goto out4 ;
outlen = crypto_kpp_maxsize ( tfm ) ;
if ( ! kdfcopy ) {
/*
* When not using a KDF , buflen 0 is used to read the
* required buffer length
*/
if ( buflen = = 0 ) {
ret = outlen ;
goto out4 ;
} else if ( outlen > buflen ) {
ret = - EOVERFLOW ;
goto out4 ;
}
2016-04-12 21:54:58 +03:00
}
2017-06-08 16:50:11 +03:00
outbuf = kzalloc ( kdfcopy ? ( outlen + kdfcopy - > otherinfolen ) : outlen ,
GFP_KERNEL ) ;
if ( ! outbuf ) {
2016-04-12 21:54:58 +03:00
ret = - ENOMEM ;
2017-06-08 16:50:11 +03:00
goto out4 ;
2016-04-12 21:54:58 +03:00
}
2017-06-08 16:50:11 +03:00
sg_init_one ( & outsg , outbuf , outlen ) ;
req = kpp_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req ) {
2016-04-12 21:54:58 +03:00
ret = - ENOMEM ;
2017-06-08 16:50:11 +03:00
goto out5 ;
2016-04-12 21:54:58 +03:00
}
2017-06-08 16:50:11 +03:00
kpp_request_set_input ( req , NULL , 0 ) ;
kpp_request_set_output ( req , & outsg , outlen ) ;
init_completion ( & compl . completion ) ;
kpp_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP ,
dh_crypto_done , & compl ) ;
2016-08-19 21:39:09 +03:00
/*
2017-06-08 16:50:11 +03:00
* For DH , generate_public_key and generate_shared_secret are
* the same calculation
2016-08-19 21:39:09 +03:00
*/
2017-06-08 16:50:11 +03:00
ret = crypto_kpp_generate_public_key ( req ) ;
if ( ret = = - EINPROGRESS ) {
wait_for_completion ( & compl . completion ) ;
ret = compl . err ;
if ( ret )
goto out6 ;
2016-08-19 21:39:09 +03:00
}
if ( kdfcopy ) {
2017-06-08 16:50:11 +03:00
/*
* Concatenate SP800 - 56 A otherinfo past DH shared secret - - the
* input to the KDF is ( DH shared secret | | otherinfo )
*/
if ( copy_from_user ( outbuf + req - > dst_len , kdfcopy - > otherinfo ,
kdfcopy - > otherinfolen ) ! = 0 ) {
2016-08-19 21:39:09 +03:00
ret = - EFAULT ;
2017-06-08 16:50:11 +03:00
goto out6 ;
}
2021-11-19 09:59:09 +03:00
ret = keyctl_dh_compute_kdf ( hash , buffer , buflen , outbuf ,
2021-11-19 09:58:44 +03:00
req - > dst_len + kdfcopy - > otherinfolen ) ;
2017-06-08 16:50:11 +03:00
} else if ( copy_to_user ( buffer , outbuf , req - > dst_len ) = = 0 ) {
ret = req - > dst_len ;
} else {
ret = - EFAULT ;
2016-08-19 21:39:09 +03:00
}
2016-04-12 21:54:58 +03:00
2017-06-08 16:50:11 +03:00
out6 :
kpp_request_free ( req ) ;
out5 :
2020-08-07 09:18:13 +03:00
kfree_sensitive ( outbuf ) ;
2017-06-08 16:50:11 +03:00
out4 :
crypto_free_kpp ( tfm ) ;
out3 :
2020-08-07 09:18:13 +03:00
kfree_sensitive ( secret ) ;
2017-06-08 16:50:11 +03:00
out2 :
dh_free_data ( & dh_inputs ) ;
out1 :
2021-11-19 09:59:09 +03:00
kdf_dealloc ( hash ) ;
2016-04-12 21:54:58 +03:00
return ret ;
}
2016-08-19 21:39:09 +03:00
long keyctl_dh_compute ( struct keyctl_dh_params __user * params ,
char __user * buffer , size_t buflen ,
struct keyctl_kdf_params __user * kdf )
{
struct keyctl_kdf_params kdfcopy ;
if ( ! kdf )
return __keyctl_dh_compute ( params , buffer , buflen , NULL ) ;
if ( copy_from_user ( & kdfcopy , kdf , sizeof ( kdfcopy ) ) ! = 0 )
return - EFAULT ;
return __keyctl_dh_compute ( params , buffer , buflen , & kdfcopy ) ;
}