2012-09-21 23:24:55 +01:00
/* In-software asymmetric public-key crypto subtype
*
* See Documentation / crypto / asymmetric - keys . txt
*
* Copyright ( C ) 2012 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
# define pr_fmt(fmt) "PKEY: "fmt
# include <linux/module.h>
# include <linux/export.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/seq_file.h>
2016-03-03 21:49:27 +00:00
# include <linux/scatterlist.h>
2012-09-21 23:24:55 +01:00
# include <keys/asymmetric-subtype.h>
2016-02-02 10:08:53 -08:00
# include <crypto/public_key.h>
2016-03-03 21:49:27 +00:00
# include <crypto/akcipher.h>
2012-09-21 23:24:55 +01:00
2017-11-15 16:38:45 +00:00
MODULE_DESCRIPTION ( " In-software asymmetric public-key subtype " ) ;
MODULE_AUTHOR ( " Red Hat, Inc. " ) ;
2012-09-21 23:24:55 +01:00
MODULE_LICENSE ( " GPL " ) ;
/*
* Provide a part of a description of the key for / proc / keys .
*/
static void public_key_describe ( const struct key * asymmetric_key ,
struct seq_file * m )
{
2015-10-21 14:04:48 +01:00
struct public_key * key = asymmetric_key - > payload . data [ asym_crypto ] ;
2012-09-21 23:24:55 +01:00
if ( key )
2016-03-03 21:49:27 +00:00
seq_printf ( m , " %s.%s " , key - > id_type , key - > pkey_algo ) ;
2012-09-21 23:24:55 +01:00
}
/*
* Destroy a public key algorithm key .
*/
2016-04-06 16:13:33 +01:00
void public_key_free ( struct public_key * key )
2012-09-21 23:24:55 +01:00
{
2016-04-06 16:13:33 +01:00
if ( key ) {
2016-02-02 10:08:53 -08:00
kfree ( key - > key ) ;
2016-04-06 16:13:33 +01:00
kfree ( key ) ;
}
}
EXPORT_SYMBOL_GPL ( public_key_free ) ;
/*
* Destroy a public key algorithm key .
*/
static void public_key_destroy ( void * payload0 , void * payload3 )
{
public_key_free ( payload0 ) ;
public_key_signature_free ( payload3 ) ;
2012-09-21 23:24:55 +01:00
}
2018-10-09 17:47:23 +01:00
/*
* Determine the crypto algorithm name .
*/
static
int software_key_determine_akcipher ( const char * encoding ,
const char * hash_algo ,
const struct public_key * pkey ,
char alg_name [ CRYPTO_MAX_ALG_NAME ] )
{
int n ;
if ( strcmp ( encoding , " pkcs1 " ) = = 0 ) {
/* The data wangled by the RSA algorithm is typically padded
* and encoded in some manner , such as EMSA - PKCS1 - 1 _5 [ RFC3447
* sec 8.2 ] .
*/
if ( ! hash_algo )
n = snprintf ( alg_name , CRYPTO_MAX_ALG_NAME ,
" pkcs1pad(%s) " ,
pkey - > pkey_algo ) ;
else
n = snprintf ( alg_name , CRYPTO_MAX_ALG_NAME ,
" pkcs1pad(%s,%s) " ,
pkey - > pkey_algo , hash_algo ) ;
return n > = CRYPTO_MAX_ALG_NAME ? - EINVAL : 0 ;
}
if ( strcmp ( encoding , " raw " ) = = 0 ) {
strcpy ( alg_name , pkey - > pkey_algo ) ;
return 0 ;
}
return - ENOPKG ;
}
/*
* Query information about a key .
*/
static int software_key_query ( const struct kernel_pkey_params * params ,
struct kernel_pkey_query * info )
{
struct crypto_akcipher * tfm ;
struct public_key * pkey = params - > key - > payload . data [ asym_crypto ] ;
char alg_name [ CRYPTO_MAX_ALG_NAME ] ;
int ret , len ;
ret = software_key_determine_akcipher ( params - > encoding ,
params - > hash_algo ,
pkey , alg_name ) ;
if ( ret < 0 )
return ret ;
tfm = crypto_alloc_akcipher ( alg_name , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
2018-10-09 17:47:31 +01:00
if ( pkey - > key_is_private )
ret = crypto_akcipher_set_priv_key ( tfm ,
pkey - > key , pkey - > keylen ) ;
else
ret = crypto_akcipher_set_pub_key ( tfm ,
pkey - > key , pkey - > keylen ) ;
2018-10-09 17:47:23 +01:00
if ( ret < 0 )
goto error_free_tfm ;
len = crypto_akcipher_maxsize ( tfm ) ;
info - > key_size = len * 8 ;
info - > max_data_size = len ;
info - > max_sig_size = len ;
info - > max_enc_size = len ;
info - > max_dec_size = len ;
2018-10-09 17:47:38 +01:00
info - > supported_ops = ( KEYCTL_SUPPORTS_ENCRYPT |
KEYCTL_SUPPORTS_VERIFY ) ;
if ( pkey - > key_is_private )
info - > supported_ops | = ( KEYCTL_SUPPORTS_DECRYPT |
KEYCTL_SUPPORTS_SIGN ) ;
2018-10-09 17:47:23 +01:00
ret = 0 ;
error_free_tfm :
crypto_free_akcipher ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
return ret ;
}
2018-10-09 17:47:38 +01:00
/*
* Do encryption , decryption and signing ops .
*/
static int software_key_eds_op ( struct kernel_pkey_params * params ,
const void * in , void * out )
{
const struct public_key * pkey = params - > key - > payload . data [ asym_crypto ] ;
struct akcipher_request * req ;
struct crypto_akcipher * tfm ;
struct crypto_wait cwait ;
struct scatterlist in_sg , out_sg ;
char alg_name [ CRYPTO_MAX_ALG_NAME ] ;
int ret ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
ret = software_key_determine_akcipher ( params - > encoding ,
params - > hash_algo ,
pkey , alg_name ) ;
if ( ret < 0 )
return ret ;
tfm = crypto_alloc_akcipher ( alg_name , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
req = akcipher_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req )
goto error_free_tfm ;
if ( pkey - > key_is_private )
ret = crypto_akcipher_set_priv_key ( tfm ,
pkey - > key , pkey - > keylen ) ;
else
ret = crypto_akcipher_set_pub_key ( tfm ,
pkey - > key , pkey - > keylen ) ;
if ( ret )
goto error_free_req ;
sg_init_one ( & in_sg , in , params - > in_len ) ;
sg_init_one ( & out_sg , out , params - > out_len ) ;
akcipher_request_set_crypt ( req , & in_sg , & out_sg , params - > in_len ,
params - > out_len ) ;
crypto_init_wait ( & cwait ) ;
akcipher_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP ,
crypto_req_done , & cwait ) ;
/* Perform the encryption calculation. */
switch ( params - > op ) {
case kernel_pkey_encrypt :
ret = crypto_akcipher_encrypt ( req ) ;
break ;
case kernel_pkey_decrypt :
ret = crypto_akcipher_decrypt ( req ) ;
break ;
case kernel_pkey_sign :
ret = crypto_akcipher_sign ( req ) ;
break ;
default :
BUG ( ) ;
}
ret = crypto_wait_req ( ret , & cwait ) ;
if ( ret = = 0 )
ret = req - > dst_len ;
error_free_req :
akcipher_request_free ( req ) ;
error_free_tfm :
crypto_free_akcipher ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
return ret ;
}
2012-09-21 23:24:55 +01:00
/*
* Verify a signature using a public key .
*/
2016-02-02 10:08:53 -08:00
int public_key_verify_signature ( const struct public_key * pkey ,
2013-08-30 16:15:30 +01:00
const struct public_key_signature * sig )
2012-09-21 23:24:55 +01:00
{
2017-10-18 08:00:40 +01:00
struct crypto_wait cwait ;
2016-03-03 21:49:27 +00:00
struct crypto_akcipher * tfm ;
struct akcipher_request * req ;
struct scatterlist sig_sg , digest_sg ;
2018-10-09 17:47:23 +01:00
char alg_name [ CRYPTO_MAX_ALG_NAME ] ;
2016-03-03 21:49:27 +00:00
void * output ;
unsigned int outlen ;
2017-12-08 15:13:29 +00:00
int ret ;
2016-03-03 21:49:27 +00:00
pr_devel ( " ==>%s() \n " , __func__ ) ;
2016-02-02 10:08:53 -08:00
BUG_ON ( ! pkey ) ;
2013-08-30 16:15:30 +01:00
BUG_ON ( ! sig ) ;
2016-02-02 10:08:53 -08:00
BUG_ON ( ! sig - > s ) ;
2012-09-21 23:24:55 +01:00
2018-10-09 17:47:23 +01:00
ret = software_key_determine_akcipher ( sig - > encoding ,
sig - > hash_algo ,
pkey , alg_name ) ;
if ( ret < 0 )
return ret ;
2016-03-03 21:49:27 +00:00
tfm = crypto_alloc_akcipher ( alg_name , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
2017-12-08 15:13:29 +00:00
ret = - ENOMEM ;
2016-03-03 21:49:27 +00:00
req = akcipher_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req )
goto error_free_tfm ;
2018-10-09 17:47:31 +01:00
if ( pkey - > key_is_private )
ret = crypto_akcipher_set_priv_key ( tfm ,
pkey - > key , pkey - > keylen ) ;
else
ret = crypto_akcipher_set_pub_key ( tfm ,
pkey - > key , pkey - > keylen ) ;
2016-03-03 21:49:27 +00:00
if ( ret )
goto error_free_req ;
2016-12-13 09:26:18 +00:00
ret = - ENOMEM ;
2016-03-03 21:49:27 +00:00
outlen = crypto_akcipher_maxsize ( tfm ) ;
output = kmalloc ( outlen , GFP_KERNEL ) ;
if ( ! output )
goto error_free_req ;
sg_init_one ( & sig_sg , sig - > s , sig - > s_size ) ;
sg_init_one ( & digest_sg , output , outlen ) ;
akcipher_request_set_crypt ( req , & sig_sg , & digest_sg , sig - > s_size ,
outlen ) ;
2017-10-18 08:00:40 +01:00
crypto_init_wait ( & cwait ) ;
2016-03-03 21:49:27 +00:00
akcipher_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP ,
2017-10-18 08:00:40 +01:00
crypto_req_done , & cwait ) ;
2016-03-03 21:49:27 +00:00
/* Perform the verification calculation. This doesn't actually do the
* verification , but rather calculates the hash expected by the
* signature and returns that to us .
*/
2017-10-18 08:00:40 +01:00
ret = crypto_wait_req ( crypto_akcipher_verify ( req ) , & cwait ) ;
2017-12-08 15:13:29 +00:00
if ( ret )
2016-03-03 21:49:27 +00:00
goto out_free_output ;
2012-09-21 23:24:55 +01:00
2016-03-03 21:49:27 +00:00
/* Do the actual verification step. */
if ( req - > dst_len ! = sig - > digest_size | |
memcmp ( sig - > digest , output , sig - > digest_size ) ! = 0 )
ret = - EKEYREJECTED ;
2012-09-21 23:24:55 +01:00
2016-03-03 21:49:27 +00:00
out_free_output :
kfree ( output ) ;
error_free_req :
akcipher_request_free ( req ) ;
error_free_tfm :
crypto_free_akcipher ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
2017-12-08 15:13:29 +00:00
if ( WARN_ON_ONCE ( ret > 0 ) )
ret = - EINVAL ;
2016-03-03 21:49:27 +00:00
return ret ;
2013-08-30 16:15:30 +01:00
}
EXPORT_SYMBOL_GPL ( public_key_verify_signature ) ;
static int public_key_verify_signature_2 ( const struct key * key ,
const struct public_key_signature * sig )
{
2015-10-21 14:04:48 +01:00
const struct public_key * pk = key - > payload . data [ asym_crypto ] ;
2013-08-30 16:15:30 +01:00
return public_key_verify_signature ( pk , sig ) ;
2012-09-21 23:24:55 +01:00
}
/*
* Public key algorithm asymmetric key subtype
*/
struct asymmetric_key_subtype public_key_subtype = {
. owner = THIS_MODULE ,
. name = " public_key " ,
2014-09-02 13:52:10 +01:00
. name_len = sizeof ( " public_key " ) - 1 ,
2012-09-21 23:24:55 +01:00
. describe = public_key_describe ,
. destroy = public_key_destroy ,
2018-10-09 17:47:23 +01:00
. query = software_key_query ,
2018-10-09 17:47:38 +01:00
. eds_op = software_key_eds_op ,
2013-08-30 16:15:30 +01:00
. verify_signature = public_key_verify_signature_2 ,
2012-09-21 23:24:55 +01:00
} ;
EXPORT_SYMBOL_GPL ( public_key_subtype ) ;