2018-10-09 19:48:02 +03:00
// SPDX-License-Identifier: GPL-2.0
# define pr_fmt(fmt) "ASYM-TPM: "fmt
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/export.h>
# include <linux/kernel.h>
# include <linux/seq_file.h>
# include <linux/scatterlist.h>
# include <linux/tpm.h>
2018-10-09 19:48:58 +03:00
# include <linux/tpm_command.h>
2018-10-09 19:48:25 +03:00
# include <crypto/akcipher.h>
2018-10-09 19:48:58 +03:00
# include <crypto/hash.h>
# include <crypto/sha.h>
2018-10-09 19:48:10 +03:00
# include <asm/unaligned.h>
2018-10-09 19:48:02 +03:00
# include <keys/asymmetric-subtype.h>
2019-10-16 08:14:54 +03:00
# include <keys/trusted_tpm.h>
2018-10-09 19:48:02 +03:00
# include <crypto/asym_tpm_subtype.h>
2018-10-09 19:49:20 +03:00
# include <crypto/public_key.h>
2018-10-09 19:48:02 +03:00
2018-10-09 19:48:58 +03:00
# define TPM_ORD_FLUSHSPECIFIC 186
# define TPM_ORD_LOADKEY2 65
2018-10-09 19:49:05 +03:00
# define TPM_ORD_UNBIND 30
2018-10-09 19:49:28 +03:00
# define TPM_ORD_SIGN 60
2018-10-09 19:48:58 +03:00
# define TPM_RT_KEY 0x00000001
/*
* Load a TPM key from the blob provided by userspace
*/
2019-10-16 08:14:53 +03:00
static int tpm_loadkey2 ( struct tpm_buf * tb ,
2018-10-09 19:48:58 +03:00
uint32_t keyhandle , unsigned char * keyauth ,
const unsigned char * keyblob , int keybloblen ,
uint32_t * newhandle )
{
unsigned char nonceodd [ TPM_NONCE_SIZE ] ;
unsigned char enonce [ TPM_NONCE_SIZE ] ;
unsigned char authdata [ SHA1_DIGEST_SIZE ] ;
uint32_t authhandle = 0 ;
unsigned char cont = 0 ;
uint32_t ordinal ;
int ret ;
ordinal = htonl ( TPM_ORD_LOADKEY2 ) ;
/* session for loading the key */
ret = oiap ( tb , & authhandle , enonce ) ;
if ( ret < 0 ) {
pr_info ( " oiap failed (%d) \n " , ret ) ;
return ret ;
}
/* generate odd nonce */
ret = tpm_get_random ( NULL , nonceodd , TPM_NONCE_SIZE ) ;
if ( ret < 0 ) {
pr_info ( " tpm_get_random failed (%d) \n " , ret ) ;
return ret ;
}
/* calculate authorization HMAC value */
ret = TSS_authhmac ( authdata , keyauth , SHA1_DIGEST_SIZE , enonce ,
nonceodd , cont , sizeof ( uint32_t ) , & ordinal ,
keybloblen , keyblob , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
/* build the request buffer */
2019-10-16 08:14:53 +03:00
tpm_buf_reset ( tb , TPM_TAG_RQU_AUTH1_COMMAND , TPM_ORD_LOADKEY2 ) ;
tpm_buf_append_u32 ( tb , keyhandle ) ;
tpm_buf_append ( tb , keyblob , keybloblen ) ;
tpm_buf_append_u32 ( tb , authhandle ) ;
tpm_buf_append ( tb , nonceodd , TPM_NONCE_SIZE ) ;
tpm_buf_append_u8 ( tb , cont ) ;
tpm_buf_append ( tb , authdata , SHA1_DIGEST_SIZE ) ;
2018-10-09 19:48:58 +03:00
ret = trusted_tpm_send ( tb - > data , MAX_BUF_SIZE ) ;
if ( ret < 0 ) {
pr_info ( " authhmac failed (%d) \n " , ret ) ;
return ret ;
}
ret = TSS_checkhmac1 ( tb - > data , ordinal , nonceodd , keyauth ,
SHA1_DIGEST_SIZE , 0 , 0 ) ;
if ( ret < 0 ) {
pr_info ( " TSS_checkhmac1 failed (%d) \n " , ret ) ;
return ret ;
}
* newhandle = LOAD32 ( tb - > data , TPM_DATA_OFFSET ) ;
return 0 ;
}
/*
* Execute the FlushSpecific TPM command
*/
2019-10-16 08:14:53 +03:00
static int tpm_flushspecific ( struct tpm_buf * tb , uint32_t handle )
2018-10-09 19:48:58 +03:00
{
2019-10-16 08:14:53 +03:00
tpm_buf_reset ( tb , TPM_TAG_RQU_COMMAND , TPM_ORD_FLUSHSPECIFIC ) ;
tpm_buf_append_u32 ( tb , handle ) ;
tpm_buf_append_u32 ( tb , TPM_RT_KEY ) ;
2018-10-09 19:48:58 +03:00
return trusted_tpm_send ( tb - > data , MAX_BUF_SIZE ) ;
}
2018-10-09 19:49:05 +03:00
/*
* Decrypt a blob provided by userspace using a specific key handle .
* The handle is a well known handle or previously loaded by e . g . LoadKey2
*/
2019-10-16 08:14:53 +03:00
static int tpm_unbind ( struct tpm_buf * tb ,
2018-10-09 19:49:05 +03:00
uint32_t keyhandle , unsigned char * keyauth ,
const unsigned char * blob , uint32_t bloblen ,
void * out , uint32_t outlen )
{
unsigned char nonceodd [ TPM_NONCE_SIZE ] ;
unsigned char enonce [ TPM_NONCE_SIZE ] ;
unsigned char authdata [ SHA1_DIGEST_SIZE ] ;
uint32_t authhandle = 0 ;
unsigned char cont = 0 ;
uint32_t ordinal ;
uint32_t datalen ;
int ret ;
ordinal = htonl ( TPM_ORD_UNBIND ) ;
datalen = htonl ( bloblen ) ;
/* session for loading the key */
ret = oiap ( tb , & authhandle , enonce ) ;
if ( ret < 0 ) {
pr_info ( " oiap failed (%d) \n " , ret ) ;
return ret ;
}
/* generate odd nonce */
ret = tpm_get_random ( NULL , nonceodd , TPM_NONCE_SIZE ) ;
if ( ret < 0 ) {
pr_info ( " tpm_get_random failed (%d) \n " , ret ) ;
return ret ;
}
/* calculate authorization HMAC value */
ret = TSS_authhmac ( authdata , keyauth , SHA1_DIGEST_SIZE , enonce ,
nonceodd , cont , sizeof ( uint32_t ) , & ordinal ,
sizeof ( uint32_t ) , & datalen ,
bloblen , blob , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
/* build the request buffer */
2019-10-16 08:14:53 +03:00
tpm_buf_reset ( tb , TPM_TAG_RQU_AUTH1_COMMAND , TPM_ORD_UNBIND ) ;
tpm_buf_append_u32 ( tb , keyhandle ) ;
tpm_buf_append_u32 ( tb , bloblen ) ;
tpm_buf_append ( tb , blob , bloblen ) ;
tpm_buf_append_u32 ( tb , authhandle ) ;
tpm_buf_append ( tb , nonceodd , TPM_NONCE_SIZE ) ;
tpm_buf_append_u8 ( tb , cont ) ;
tpm_buf_append ( tb , authdata , SHA1_DIGEST_SIZE ) ;
2018-10-09 19:49:05 +03:00
ret = trusted_tpm_send ( tb - > data , MAX_BUF_SIZE ) ;
if ( ret < 0 ) {
pr_info ( " authhmac failed (%d) \n " , ret ) ;
return ret ;
}
datalen = LOAD32 ( tb - > data , TPM_DATA_OFFSET ) ;
ret = TSS_checkhmac1 ( tb - > data , ordinal , nonceodd ,
keyauth , SHA1_DIGEST_SIZE ,
sizeof ( uint32_t ) , TPM_DATA_OFFSET ,
datalen , TPM_DATA_OFFSET + sizeof ( uint32_t ) ,
0 , 0 ) ;
if ( ret < 0 ) {
pr_info ( " TSS_checkhmac1 failed (%d) \n " , ret ) ;
return ret ;
}
memcpy ( out , tb - > data + TPM_DATA_OFFSET + sizeof ( uint32_t ) ,
min ( outlen , datalen ) ) ;
return datalen ;
}
2018-10-09 19:49:28 +03:00
/*
* Sign a blob provided by userspace ( that has had the hash function applied )
* using a specific key handle . The handle is assumed to have been previously
* loaded by e . g . LoadKey2 .
*
* Note that the key signature scheme of the used key should be set to
* TPM_SS_RSASSAPKCS1v15_DER . This allows the hashed input to be of any size
* up to key_length_in_bytes - 11 and not be limited to size 20 like the
* TPM_SS_RSASSAPKCS1v15_SHA1 signature scheme .
*/
2019-10-16 08:14:53 +03:00
static int tpm_sign ( struct tpm_buf * tb ,
2018-10-09 19:49:28 +03:00
uint32_t keyhandle , unsigned char * keyauth ,
const unsigned char * blob , uint32_t bloblen ,
void * out , uint32_t outlen )
{
unsigned char nonceodd [ TPM_NONCE_SIZE ] ;
unsigned char enonce [ TPM_NONCE_SIZE ] ;
unsigned char authdata [ SHA1_DIGEST_SIZE ] ;
uint32_t authhandle = 0 ;
unsigned char cont = 0 ;
uint32_t ordinal ;
uint32_t datalen ;
int ret ;
ordinal = htonl ( TPM_ORD_SIGN ) ;
datalen = htonl ( bloblen ) ;
/* session for loading the key */
ret = oiap ( tb , & authhandle , enonce ) ;
if ( ret < 0 ) {
pr_info ( " oiap failed (%d) \n " , ret ) ;
return ret ;
}
/* generate odd nonce */
ret = tpm_get_random ( NULL , nonceodd , TPM_NONCE_SIZE ) ;
if ( ret < 0 ) {
pr_info ( " tpm_get_random failed (%d) \n " , ret ) ;
return ret ;
}
/* calculate authorization HMAC value */
ret = TSS_authhmac ( authdata , keyauth , SHA1_DIGEST_SIZE , enonce ,
nonceodd , cont , sizeof ( uint32_t ) , & ordinal ,
sizeof ( uint32_t ) , & datalen ,
bloblen , blob , 0 , 0 ) ;
if ( ret < 0 )
return ret ;
/* build the request buffer */
2019-10-16 08:14:53 +03:00
tpm_buf_reset ( tb , TPM_TAG_RQU_AUTH1_COMMAND , TPM_ORD_SIGN ) ;
tpm_buf_append_u32 ( tb , keyhandle ) ;
tpm_buf_append_u32 ( tb , bloblen ) ;
tpm_buf_append ( tb , blob , bloblen ) ;
tpm_buf_append_u32 ( tb , authhandle ) ;
tpm_buf_append ( tb , nonceodd , TPM_NONCE_SIZE ) ;
tpm_buf_append_u8 ( tb , cont ) ;
tpm_buf_append ( tb , authdata , SHA1_DIGEST_SIZE ) ;
2018-10-09 19:49:28 +03:00
ret = trusted_tpm_send ( tb - > data , MAX_BUF_SIZE ) ;
if ( ret < 0 ) {
pr_info ( " authhmac failed (%d) \n " , ret ) ;
return ret ;
}
datalen = LOAD32 ( tb - > data , TPM_DATA_OFFSET ) ;
ret = TSS_checkhmac1 ( tb - > data , ordinal , nonceodd ,
keyauth , SHA1_DIGEST_SIZE ,
sizeof ( uint32_t ) , TPM_DATA_OFFSET ,
datalen , TPM_DATA_OFFSET + sizeof ( uint32_t ) ,
0 , 0 ) ;
if ( ret < 0 ) {
pr_info ( " TSS_checkhmac1 failed (%d) \n " , ret ) ;
return ret ;
}
memcpy ( out , tb - > data + TPM_DATA_OFFSET + sizeof ( uint32_t ) ,
min ( datalen , outlen ) ) ;
return datalen ;
}
2019-04-11 18:51:17 +03:00
/* Room to fit two u32 zeros for algo id and parameters length. */
# define SETKEY_PARAMS_SIZE (sizeof(u32) * 2)
2018-10-09 19:48:25 +03:00
/*
* Maximum buffer size for the BER / DER encoded public key . The public key
* is of the form SEQUENCE { INTEGER n , INTEGER e } where n is a maximum 2048
* bit key and e is usually 65537
* The encoding overhead is :
* - max 4 bytes for SEQUENCE
* - max 4 bytes for INTEGER n type / length
* - 257 bytes of n
* - max 2 bytes for INTEGER e type / length
* - 3 bytes of e
2019-04-11 18:51:17 +03:00
* - 4 + 4 of zeros for set_pub_key parameters ( SETKEY_PARAMS_SIZE )
2018-10-09 19:48:25 +03:00
*/
2019-04-11 18:51:17 +03:00
# define PUB_KEY_BUF_SIZE (4 + 4 + 257 + 2 + 3 + SETKEY_PARAMS_SIZE)
2018-10-09 19:48:25 +03:00
2018-10-09 19:48:02 +03:00
/*
* Provide a part of a description of the key for / proc / keys .
*/
static void asym_tpm_describe ( const struct key * asymmetric_key ,
struct seq_file * m )
{
struct tpm_key * tk = asymmetric_key - > payload . data [ asym_crypto ] ;
if ( ! tk )
return ;
seq_printf ( m , " TPM1.2/Blob " ) ;
}
static void asym_tpm_destroy ( void * payload0 , void * payload3 )
{
struct tpm_key * tk = payload0 ;
if ( ! tk )
return ;
kfree ( tk - > blob ) ;
tk - > blob_len = 0 ;
kfree ( tk ) ;
}
2018-10-09 19:48:25 +03:00
/* How many bytes will it take to encode the length */
static inline uint32_t definite_length ( uint32_t len )
{
if ( len < = 127 )
return 1 ;
if ( len < = 255 )
return 2 ;
return 3 ;
}
static inline uint8_t * encode_tag_length ( uint8_t * buf , uint8_t tag ,
uint32_t len )
{
* buf + + = tag ;
if ( len < = 127 ) {
buf [ 0 ] = len ;
return buf + 1 ;
}
if ( len < = 255 ) {
buf [ 0 ] = 0x81 ;
buf [ 1 ] = len ;
return buf + 2 ;
}
buf [ 0 ] = 0x82 ;
put_unaligned_be16 ( len , buf + 1 ) ;
return buf + 3 ;
}
static uint32_t derive_pub_key ( const void * pub_key , uint32_t len , uint8_t * buf )
{
uint8_t * cur = buf ;
uint32_t n_len = definite_length ( len ) + 1 + len + 1 ;
uint32_t e_len = definite_length ( 3 ) + 1 + 3 ;
uint8_t e [ 3 ] = { 0x01 , 0x00 , 0x01 } ;
/* SEQUENCE */
cur = encode_tag_length ( cur , 0x30 , n_len + e_len ) ;
/* INTEGER n */
cur = encode_tag_length ( cur , 0x02 , len + 1 ) ;
cur [ 0 ] = 0x00 ;
memcpy ( cur + 1 , pub_key , len ) ;
cur + = len + 1 ;
cur = encode_tag_length ( cur , 0x02 , sizeof ( e ) ) ;
memcpy ( cur , e , sizeof ( e ) ) ;
cur + = sizeof ( e ) ;
2019-04-11 18:51:17 +03:00
/* Zero parameters to satisfy set_pub_key ABI. */
memset ( cur , 0 , SETKEY_PARAMS_SIZE ) ;
2018-10-09 19:48:25 +03:00
return cur - buf ;
}
/*
* Determine the crypto algorithm name .
*/
static int determine_akcipher ( const char * encoding , const char * hash_algo ,
char alg_name [ CRYPTO_MAX_ALG_NAME ] )
{
if ( strcmp ( encoding , " pkcs1 " ) = = 0 ) {
2018-10-09 19:49:20 +03:00
if ( ! hash_algo ) {
strcpy ( alg_name , " pkcs1pad(rsa) " ) ;
return 0 ;
}
if ( snprintf ( alg_name , CRYPTO_MAX_ALG_NAME , " pkcs1pad(rsa,%s) " ,
hash_algo ) > = CRYPTO_MAX_ALG_NAME )
return - EINVAL ;
2018-10-09 19:48:25 +03:00
return 0 ;
}
if ( strcmp ( encoding , " raw " ) = = 0 ) {
strcpy ( alg_name , " rsa " ) ;
return 0 ;
}
return - ENOPKG ;
}
/*
* Query information about a key .
*/
static int tpm_key_query ( const struct kernel_pkey_params * params ,
struct kernel_pkey_query * info )
{
struct tpm_key * tk = params - > key - > payload . data [ asym_crypto ] ;
int ret ;
char alg_name [ CRYPTO_MAX_ALG_NAME ] ;
struct crypto_akcipher * tfm ;
uint8_t der_pub_key [ PUB_KEY_BUF_SIZE ] ;
uint32_t der_pub_key_len ;
int len ;
/* TPM only works on private keys, public keys still done in software */
ret = determine_akcipher ( params - > encoding , params - > hash_algo , alg_name ) ;
if ( ret < 0 )
return ret ;
tfm = crypto_alloc_akcipher ( alg_name , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
der_pub_key_len = derive_pub_key ( tk - > pub_key , tk - > pub_key_len ,
der_pub_key ) ;
ret = crypto_akcipher_set_pub_key ( tfm , der_pub_key , der_pub_key_len ) ;
if ( ret < 0 )
goto error_free_tfm ;
len = crypto_akcipher_maxsize ( tfm ) ;
info - > key_size = tk - > key_len ;
info - > max_data_size = tk - > key_len / 8 ;
info - > max_sig_size = len ;
info - > max_enc_size = len ;
info - > max_dec_size = tk - > key_len / 8 ;
2018-10-09 19:49:13 +03:00
info - > supported_ops = KEYCTL_SUPPORTS_ENCRYPT |
2018-10-09 19:49:20 +03:00
KEYCTL_SUPPORTS_DECRYPT |
2018-10-09 19:49:35 +03:00
KEYCTL_SUPPORTS_VERIFY |
KEYCTL_SUPPORTS_SIGN ;
2018-10-09 19:48:33 +03:00
2018-10-09 19:48:25 +03:00
ret = 0 ;
error_free_tfm :
crypto_free_akcipher ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
return ret ;
}
2018-10-09 19:48:33 +03:00
/*
* Encryption operation is performed with the public key . Hence it is done
* in software
*/
static int tpm_key_encrypt ( struct tpm_key * tk ,
struct kernel_pkey_params * params ,
const void * in , void * out )
{
char alg_name [ CRYPTO_MAX_ALG_NAME ] ;
struct crypto_akcipher * tfm ;
struct akcipher_request * req ;
struct crypto_wait cwait ;
struct scatterlist in_sg , out_sg ;
uint8_t der_pub_key [ PUB_KEY_BUF_SIZE ] ;
uint32_t der_pub_key_len ;
int ret ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
ret = determine_akcipher ( params - > encoding , params - > hash_algo , alg_name ) ;
if ( ret < 0 )
return ret ;
tfm = crypto_alloc_akcipher ( alg_name , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
der_pub_key_len = derive_pub_key ( tk - > pub_key , tk - > pub_key_len ,
der_pub_key ) ;
ret = crypto_akcipher_set_pub_key ( tfm , der_pub_key , der_pub_key_len ) ;
if ( ret < 0 )
goto error_free_tfm ;
2019-10-10 02:03:49 +03:00
ret = - ENOMEM ;
2018-10-09 19:48:33 +03:00
req = akcipher_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req )
goto error_free_tfm ;
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 ) ;
ret = crypto_akcipher_encrypt ( req ) ;
ret = crypto_wait_req ( ret , & cwait ) ;
if ( ret = = 0 )
ret = req - > dst_len ;
akcipher_request_free ( req ) ;
error_free_tfm :
crypto_free_akcipher ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
return ret ;
}
2018-10-09 19:49:13 +03:00
/*
* Decryption operation is performed with the private key in the TPM .
*/
static int tpm_key_decrypt ( struct tpm_key * tk ,
struct kernel_pkey_params * params ,
const void * in , void * out )
{
2019-10-16 08:14:53 +03:00
struct tpm_buf tb ;
2018-10-09 19:49:13 +03:00
uint32_t keyhandle ;
uint8_t srkauth [ SHA1_DIGEST_SIZE ] ;
uint8_t keyauth [ SHA1_DIGEST_SIZE ] ;
int r ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
if ( params - > hash_algo )
return - ENOPKG ;
if ( strcmp ( params - > encoding , " pkcs1 " ) )
return - ENOPKG ;
2019-10-16 08:14:53 +03:00
r = tpm_buf_init ( & tb , 0 , 0 ) ;
if ( r )
return r ;
2018-10-09 19:49:13 +03:00
/* TODO: Handle a non-all zero SRK authorization */
memset ( srkauth , 0 , sizeof ( srkauth ) ) ;
2019-10-16 08:14:53 +03:00
r = tpm_loadkey2 ( & tb , SRKHANDLE , srkauth ,
2018-10-09 19:49:13 +03:00
tk - > blob , tk - > blob_len , & keyhandle ) ;
if ( r < 0 ) {
pr_devel ( " loadkey2 failed (%d) \n " , r ) ;
goto error ;
}
/* TODO: Handle a non-all zero key authorization */
memset ( keyauth , 0 , sizeof ( keyauth ) ) ;
2019-10-16 08:14:53 +03:00
r = tpm_unbind ( & tb , keyhandle , keyauth ,
2018-10-09 19:49:13 +03:00
in , params - > in_len , out , params - > out_len ) ;
if ( r < 0 )
pr_devel ( " tpm_unbind failed (%d) \n " , r ) ;
2019-10-16 08:14:53 +03:00
if ( tpm_flushspecific ( & tb , keyhandle ) < 0 )
2018-10-09 19:49:13 +03:00
pr_devel ( " flushspecific failed (%d) \n " , r ) ;
error :
2019-10-16 08:14:53 +03:00
tpm_buf_destroy ( & tb ) ;
2018-10-09 19:49:13 +03:00
pr_devel ( " <==%s() = %d \n " , __func__ , r ) ;
return r ;
}
2018-10-09 19:49:35 +03:00
/*
* Hash algorithm OIDs plus ASN .1 DER wrappings [ RFC4880 sec 5.2 .2 ] .
*/
static const u8 digest_info_md5 [ ] = {
0x30 , 0x20 , 0x30 , 0x0c , 0x06 , 0x08 ,
0x2a , 0x86 , 0x48 , 0x86 , 0xf7 , 0x0d , 0x02 , 0x05 , /* OID */
0x05 , 0x00 , 0x04 , 0x10
} ;
static const u8 digest_info_sha1 [ ] = {
0x30 , 0x21 , 0x30 , 0x09 , 0x06 , 0x05 ,
0x2b , 0x0e , 0x03 , 0x02 , 0x1a ,
0x05 , 0x00 , 0x04 , 0x14
} ;
static const u8 digest_info_rmd160 [ ] = {
0x30 , 0x21 , 0x30 , 0x09 , 0x06 , 0x05 ,
0x2b , 0x24 , 0x03 , 0x02 , 0x01 ,
0x05 , 0x00 , 0x04 , 0x14
} ;
static const u8 digest_info_sha224 [ ] = {
0x30 , 0x2d , 0x30 , 0x0d , 0x06 , 0x09 ,
0x60 , 0x86 , 0x48 , 0x01 , 0x65 , 0x03 , 0x04 , 0x02 , 0x04 ,
0x05 , 0x00 , 0x04 , 0x1c
} ;
static const u8 digest_info_sha256 [ ] = {
0x30 , 0x31 , 0x30 , 0x0d , 0x06 , 0x09 ,
0x60 , 0x86 , 0x48 , 0x01 , 0x65 , 0x03 , 0x04 , 0x02 , 0x01 ,
0x05 , 0x00 , 0x04 , 0x20
} ;
static const u8 digest_info_sha384 [ ] = {
0x30 , 0x41 , 0x30 , 0x0d , 0x06 , 0x09 ,
0x60 , 0x86 , 0x48 , 0x01 , 0x65 , 0x03 , 0x04 , 0x02 , 0x02 ,
0x05 , 0x00 , 0x04 , 0x30
} ;
static const u8 digest_info_sha512 [ ] = {
0x30 , 0x51 , 0x30 , 0x0d , 0x06 , 0x09 ,
0x60 , 0x86 , 0x48 , 0x01 , 0x65 , 0x03 , 0x04 , 0x02 , 0x03 ,
0x05 , 0x00 , 0x04 , 0x40
} ;
static const struct asn1_template {
const char * name ;
const u8 * data ;
size_t size ;
} asn1_templates [ ] = {
# define _(X) { #X, digest_info_##X, sizeof(digest_info_##X) }
_ ( md5 ) ,
_ ( sha1 ) ,
_ ( rmd160 ) ,
_ ( sha256 ) ,
_ ( sha384 ) ,
_ ( sha512 ) ,
_ ( sha224 ) ,
{ NULL }
# undef _
} ;
static const struct asn1_template * lookup_asn1 ( const char * name )
{
const struct asn1_template * p ;
for ( p = asn1_templates ; p - > name ; p + + )
if ( strcmp ( name , p - > name ) = = 0 )
return p ;
return NULL ;
}
/*
* Sign operation is performed with the private key in the TPM .
*/
static int tpm_key_sign ( struct tpm_key * tk ,
struct kernel_pkey_params * params ,
const void * in , void * out )
{
2019-10-16 08:14:53 +03:00
struct tpm_buf tb ;
2018-10-09 19:49:35 +03:00
uint32_t keyhandle ;
uint8_t srkauth [ SHA1_DIGEST_SIZE ] ;
uint8_t keyauth [ SHA1_DIGEST_SIZE ] ;
void * asn1_wrapped = NULL ;
uint32_t in_len = params - > in_len ;
int r ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
if ( strcmp ( params - > encoding , " pkcs1 " ) )
return - ENOPKG ;
if ( params - > hash_algo ) {
const struct asn1_template * asn1 =
lookup_asn1 ( params - > hash_algo ) ;
if ( ! asn1 )
return - ENOPKG ;
/* request enough space for the ASN.1 template + input hash */
asn1_wrapped = kzalloc ( in_len + asn1 - > size , GFP_KERNEL ) ;
if ( ! asn1_wrapped )
return - ENOMEM ;
/* Copy ASN.1 template, then the input */
memcpy ( asn1_wrapped , asn1 - > data , asn1 - > size ) ;
memcpy ( asn1_wrapped + asn1 - > size , in , in_len ) ;
in = asn1_wrapped ;
in_len + = asn1 - > size ;
}
if ( in_len > tk - > key_len / 8 - 11 ) {
r = - EOVERFLOW ;
goto error_free_asn1_wrapped ;
}
2019-10-16 08:14:53 +03:00
r = tpm_buf_init ( & tb , 0 , 0 ) ;
if ( r )
2018-10-09 19:49:35 +03:00
goto error_free_asn1_wrapped ;
/* TODO: Handle a non-all zero SRK authorization */
memset ( srkauth , 0 , sizeof ( srkauth ) ) ;
2019-10-16 08:14:53 +03:00
r = tpm_loadkey2 ( & tb , SRKHANDLE , srkauth ,
2018-10-09 19:49:35 +03:00
tk - > blob , tk - > blob_len , & keyhandle ) ;
if ( r < 0 ) {
pr_devel ( " loadkey2 failed (%d) \n " , r ) ;
goto error_free_tb ;
}
/* TODO: Handle a non-all zero key authorization */
memset ( keyauth , 0 , sizeof ( keyauth ) ) ;
2019-10-16 08:14:53 +03:00
r = tpm_sign ( & tb , keyhandle , keyauth , in , in_len , out , params - > out_len ) ;
2018-10-09 19:49:35 +03:00
if ( r < 0 )
pr_devel ( " tpm_sign failed (%d) \n " , r ) ;
2019-10-16 08:14:53 +03:00
if ( tpm_flushspecific ( & tb , keyhandle ) < 0 )
2018-10-09 19:49:35 +03:00
pr_devel ( " flushspecific failed (%d) \n " , r ) ;
error_free_tb :
2019-10-16 08:14:53 +03:00
tpm_buf_destroy ( & tb ) ;
2018-10-09 19:49:35 +03:00
error_free_asn1_wrapped :
kfree ( asn1_wrapped ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , r ) ;
return r ;
}
2018-10-09 19:48:33 +03:00
/*
* Do encryption , decryption and signing ops .
*/
static int tpm_key_eds_op ( struct kernel_pkey_params * params ,
const void * in , void * out )
{
struct tpm_key * tk = params - > key - > payload . data [ asym_crypto ] ;
int ret = - EOPNOTSUPP ;
/* Perform the encryption calculation. */
switch ( params - > op ) {
case kernel_pkey_encrypt :
ret = tpm_key_encrypt ( tk , params , in , out ) ;
break ;
2018-10-09 19:49:13 +03:00
case kernel_pkey_decrypt :
ret = tpm_key_decrypt ( tk , params , in , out ) ;
break ;
2018-10-09 19:49:35 +03:00
case kernel_pkey_sign :
ret = tpm_key_sign ( tk , params , in , out ) ;
break ;
2018-10-09 19:48:33 +03:00
default :
BUG ( ) ;
}
return ret ;
}
2018-10-09 19:49:20 +03:00
/*
* Verify a signature using a public key .
*/
static int tpm_key_verify_signature ( const struct key * key ,
const struct public_key_signature * sig )
{
const struct tpm_key * tk = key - > payload . data [ asym_crypto ] ;
struct crypto_wait cwait ;
struct crypto_akcipher * tfm ;
struct akcipher_request * req ;
crypto: akcipher - new verify API for public key algorithms
Previous akcipher .verify() just `decrypts' (using RSA encrypt which is
using public key) signature to uncover message hash, which was then
compared in upper level public_key_verify_signature() with the expected
hash value, which itself was never passed into verify().
This approach was incompatible with EC-DSA family of algorithms,
because, to verify a signature EC-DSA algorithm also needs a hash value
as input; then it's used (together with a signature divided into halves
`r||s') to produce a witness value, which is then compared with `r' to
determine if the signature is correct. Thus, for EC-DSA, nor
requirements of .verify() itself, nor its output expectations in
public_key_verify_signature() wasn't sufficient.
Make improved .verify() call which gets hash value as input and produce
complete signature check without any output besides status.
Now for the top level verification only crypto_akcipher_verify() needs
to be called and its return value inspected.
Make sure that `digest' is in kmalloc'd memory (in place of `output`) in
{public,tpm}_key_verify_signature() as insisted by Herbert Xu, and will
be changed in the following commit.
Cc: David Howells <dhowells@redhat.com>
Cc: keyrings@vger.kernel.org
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Reviewed-by: Denis Kenzior <denkenz@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2019-04-11 18:51:15 +03:00
struct scatterlist src_sg [ 2 ] ;
2018-10-09 19:49:20 +03:00
char alg_name [ CRYPTO_MAX_ALG_NAME ] ;
uint8_t der_pub_key [ PUB_KEY_BUF_SIZE ] ;
uint32_t der_pub_key_len ;
int ret ;
pr_devel ( " ==>%s() \n " , __func__ ) ;
BUG_ON ( ! tk ) ;
BUG_ON ( ! sig ) ;
BUG_ON ( ! sig - > s ) ;
if ( ! sig - > digest )
return - ENOPKG ;
ret = determine_akcipher ( sig - > encoding , sig - > hash_algo , alg_name ) ;
if ( ret < 0 )
return ret ;
tfm = crypto_alloc_akcipher ( alg_name , 0 , 0 ) ;
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
der_pub_key_len = derive_pub_key ( tk - > pub_key , tk - > pub_key_len ,
der_pub_key ) ;
ret = crypto_akcipher_set_pub_key ( tfm , der_pub_key , der_pub_key_len ) ;
if ( ret < 0 )
goto error_free_tfm ;
ret = - ENOMEM ;
req = akcipher_request_alloc ( tfm , GFP_KERNEL ) ;
if ( ! req )
goto error_free_tfm ;
crypto: akcipher - new verify API for public key algorithms
Previous akcipher .verify() just `decrypts' (using RSA encrypt which is
using public key) signature to uncover message hash, which was then
compared in upper level public_key_verify_signature() with the expected
hash value, which itself was never passed into verify().
This approach was incompatible with EC-DSA family of algorithms,
because, to verify a signature EC-DSA algorithm also needs a hash value
as input; then it's used (together with a signature divided into halves
`r||s') to produce a witness value, which is then compared with `r' to
determine if the signature is correct. Thus, for EC-DSA, nor
requirements of .verify() itself, nor its output expectations in
public_key_verify_signature() wasn't sufficient.
Make improved .verify() call which gets hash value as input and produce
complete signature check without any output besides status.
Now for the top level verification only crypto_akcipher_verify() needs
to be called and its return value inspected.
Make sure that `digest' is in kmalloc'd memory (in place of `output`) in
{public,tpm}_key_verify_signature() as insisted by Herbert Xu, and will
be changed in the following commit.
Cc: David Howells <dhowells@redhat.com>
Cc: keyrings@vger.kernel.org
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Reviewed-by: Denis Kenzior <denkenz@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2019-04-11 18:51:15 +03:00
sg_init_table ( src_sg , 2 ) ;
sg_set_buf ( & src_sg [ 0 ] , sig - > s , sig - > s_size ) ;
2019-04-11 18:51:16 +03:00
sg_set_buf ( & src_sg [ 1 ] , sig - > digest , sig - > digest_size ) ;
crypto: akcipher - new verify API for public key algorithms
Previous akcipher .verify() just `decrypts' (using RSA encrypt which is
using public key) signature to uncover message hash, which was then
compared in upper level public_key_verify_signature() with the expected
hash value, which itself was never passed into verify().
This approach was incompatible with EC-DSA family of algorithms,
because, to verify a signature EC-DSA algorithm also needs a hash value
as input; then it's used (together with a signature divided into halves
`r||s') to produce a witness value, which is then compared with `r' to
determine if the signature is correct. Thus, for EC-DSA, nor
requirements of .verify() itself, nor its output expectations in
public_key_verify_signature() wasn't sufficient.
Make improved .verify() call which gets hash value as input and produce
complete signature check without any output besides status.
Now for the top level verification only crypto_akcipher_verify() needs
to be called and its return value inspected.
Make sure that `digest' is in kmalloc'd memory (in place of `output`) in
{public,tpm}_key_verify_signature() as insisted by Herbert Xu, and will
be changed in the following commit.
Cc: David Howells <dhowells@redhat.com>
Cc: keyrings@vger.kernel.org
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
Reviewed-by: Denis Kenzior <denkenz@gmail.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2019-04-11 18:51:15 +03:00
akcipher_request_set_crypt ( req , src_sg , NULL , sig - > s_size ,
sig - > digest_size ) ;
2018-10-09 19:49:20 +03:00
crypto_init_wait ( & cwait ) ;
akcipher_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_BACKLOG |
CRYPTO_TFM_REQ_MAY_SLEEP ,
crypto_req_done , & cwait ) ;
ret = crypto_wait_req ( crypto_akcipher_verify ( req ) , & cwait ) ;
akcipher_request_free ( req ) ;
error_free_tfm :
crypto_free_akcipher ( tfm ) ;
pr_devel ( " <==%s() = %d \n " , __func__ , ret ) ;
if ( WARN_ON_ONCE ( ret > 0 ) )
ret = - EINVAL ;
return ret ;
}
2018-10-09 19:48:10 +03:00
/*
* Parse enough information out of TPM_KEY structure :
* TPM_STRUCT_VER - > 4 bytes
* TPM_KEY_USAGE - > 2 bytes
* TPM_KEY_FLAGS - > 4 bytes
* TPM_AUTH_DATA_USAGE - > 1 byte
* TPM_KEY_PARMS - > variable
* UINT32 PCRInfoSize - > 4 bytes
* BYTE * - > PCRInfoSize bytes
* TPM_STORE_PUBKEY
* UINT32 encDataSize ;
* BYTE * - > encDataSize ;
*
* TPM_KEY_PARMS :
* TPM_ALGORITHM_ID - > 4 bytes
* TPM_ENC_SCHEME - > 2 bytes
* TPM_SIG_SCHEME - > 2 bytes
* UINT32 parmSize - > 4 bytes
* BYTE * - > variable
*/
static int extract_key_parameters ( struct tpm_key * tk )
{
const void * cur = tk - > blob ;
uint32_t len = tk - > blob_len ;
const void * pub_key ;
uint32_t sz ;
uint32_t key_len ;
if ( len < 11 )
return - EBADMSG ;
/* Ensure this is a legacy key */
if ( get_unaligned_be16 ( cur + 4 ) ! = 0x0015 )
return - EBADMSG ;
/* Skip to TPM_KEY_PARMS */
cur + = 11 ;
len - = 11 ;
if ( len < 12 )
return - EBADMSG ;
/* Make sure this is an RSA key */
if ( get_unaligned_be32 ( cur ) ! = 0x00000001 )
return - EBADMSG ;
/* Make sure this is TPM_ES_RSAESPKCSv15 encoding scheme */
if ( get_unaligned_be16 ( cur + 4 ) ! = 0x0002 )
return - EBADMSG ;
/* Make sure this is TPM_SS_RSASSAPKCS1v15_DER signature scheme */
if ( get_unaligned_be16 ( cur + 6 ) ! = 0x0003 )
return - EBADMSG ;
sz = get_unaligned_be32 ( cur + 8 ) ;
if ( len < sz + 12 )
return - EBADMSG ;
/* Move to TPM_RSA_KEY_PARMS */
len - = 12 ;
cur + = 12 ;
/* Grab the RSA key length */
key_len = get_unaligned_be32 ( cur ) ;
switch ( key_len ) {
case 512 :
case 1024 :
case 1536 :
case 2048 :
break ;
default :
return - EINVAL ;
}
/* Move just past TPM_KEY_PARMS */
cur + = sz ;
len - = sz ;
if ( len < 4 )
return - EBADMSG ;
sz = get_unaligned_be32 ( cur ) ;
if ( len < 4 + sz )
return - EBADMSG ;
/* Move to TPM_STORE_PUBKEY */
cur + = 4 + sz ;
len - = 4 + sz ;
/* Grab the size of the public key, it should jive with the key size */
sz = get_unaligned_be32 ( cur ) ;
if ( sz > 256 )
return - EINVAL ;
pub_key = cur + 4 ;
tk - > key_len = key_len ;
tk - > pub_key = pub_key ;
tk - > pub_key_len = sz ;
return 0 ;
}
2018-10-09 19:48:02 +03:00
/* Given the blob, parse it and load it into the TPM */
struct tpm_key * tpm_key_create ( const void * blob , uint32_t blob_len )
{
int r ;
struct tpm_key * tk ;
r = tpm_is_tpm2 ( NULL ) ;
if ( r < 0 )
goto error ;
/* We don't support TPM2 yet */
if ( r > 0 ) {
r = - ENODEV ;
goto error ;
}
r = - ENOMEM ;
tk = kzalloc ( sizeof ( struct tpm_key ) , GFP_KERNEL ) ;
if ( ! tk )
goto error ;
tk - > blob = kmemdup ( blob , blob_len , GFP_KERNEL ) ;
if ( ! tk - > blob )
goto error_memdup ;
tk - > blob_len = blob_len ;
2018-10-09 19:48:10 +03:00
r = extract_key_parameters ( tk ) ;
if ( r < 0 )
goto error_extract ;
2018-10-09 19:48:02 +03:00
return tk ;
2018-10-09 19:48:10 +03:00
error_extract :
kfree ( tk - > blob ) ;
tk - > blob_len = 0 ;
2018-10-09 19:48:02 +03:00
error_memdup :
kfree ( tk ) ;
error :
return ERR_PTR ( r ) ;
}
EXPORT_SYMBOL_GPL ( tpm_key_create ) ;
/*
* TPM - based asymmetric key subtype
*/
struct asymmetric_key_subtype asym_tpm_subtype = {
. owner = THIS_MODULE ,
. name = " asym_tpm " ,
. name_len = sizeof ( " asym_tpm " ) - 1 ,
. describe = asym_tpm_describe ,
. destroy = asym_tpm_destroy ,
2018-10-09 19:48:25 +03:00
. query = tpm_key_query ,
2018-10-09 19:48:33 +03:00
. eds_op = tpm_key_eds_op ,
2018-10-09 19:49:20 +03:00
. verify_signature = tpm_key_verify_signature ,
2018-10-09 19:48:02 +03:00
} ;
EXPORT_SYMBOL_GPL ( asym_tpm_subtype ) ;
MODULE_DESCRIPTION ( " TPM based asymmetric key subtype " ) ;
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;