2015-04-21 16:23:47 -07:00
/*
* linux / fs / f2fs / crypto_key . c
*
* Copied from linux / fs / f2fs / crypto_key . c
*
* Copyright ( C ) 2015 , Google , Inc .
*
* This contains encryption key functions for f2fs
*
* Written by Michael Halcrow , Ildar Muslukhov , and Uday Savagaonkar , 2015.
*/
# include <keys/encrypted-type.h>
# include <keys/user-type.h>
# include <linux/random.h>
# include <linux/scatterlist.h>
# include <uapi/linux/keyctl.h>
# include <crypto/hash.h>
# include <linux/f2fs_fs.h>
# include "f2fs.h"
# include "xattr.h"
static void derive_crypt_complete ( struct crypto_async_request * req , int rc )
{
struct f2fs_completion_result * ecr = req - > data ;
if ( rc = = - EINPROGRESS )
return ;
ecr - > res = rc ;
complete ( & ecr - > completion ) ;
}
/**
* f2fs_derive_key_aes ( ) - Derive a key using AES - 128 - ECB
* @ deriving_key : Encryption key used for derivatio .
* @ source_key : Source key to which to apply derivation .
* @ derived_key : Derived key .
*
* Return : Zero on success ; non - zero otherwise .
*/
static int f2fs_derive_key_aes ( char deriving_key [ F2FS_AES_128_ECB_KEY_SIZE ] ,
char source_key [ F2FS_AES_256_XTS_KEY_SIZE ] ,
char derived_key [ F2FS_AES_256_XTS_KEY_SIZE ] )
{
int res = 0 ;
struct ablkcipher_request * req = NULL ;
DECLARE_F2FS_COMPLETION_RESULT ( ecr ) ;
struct scatterlist src_sg , dst_sg ;
struct crypto_ablkcipher * tfm = crypto_alloc_ablkcipher ( " ecb(aes) " , 0 ,
0 ) ;
if ( IS_ERR ( tfm ) ) {
res = PTR_ERR ( tfm ) ;
tfm = NULL ;
goto out ;
}
crypto_ablkcipher_set_flags ( tfm , CRYPTO_TFM_REQ_WEAK_KEY ) ;
req = ablkcipher_request_alloc ( tfm , GFP_NOFS ) ;
if ( ! req ) {
res = - ENOMEM ;
goto out ;
}
ablkcipher_request_set_callback ( req ,
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP ,
derive_crypt_complete , & ecr ) ;
res = crypto_ablkcipher_setkey ( tfm , deriving_key ,
F2FS_AES_128_ECB_KEY_SIZE ) ;
if ( res < 0 )
goto out ;
sg_init_one ( & src_sg , source_key , F2FS_AES_256_XTS_KEY_SIZE ) ;
sg_init_one ( & dst_sg , derived_key , F2FS_AES_256_XTS_KEY_SIZE ) ;
ablkcipher_request_set_crypt ( req , & src_sg , & dst_sg ,
F2FS_AES_256_XTS_KEY_SIZE , NULL ) ;
res = crypto_ablkcipher_encrypt ( req ) ;
if ( res = = - EINPROGRESS | | res = = - EBUSY ) {
wait_for_completion ( & ecr . completion ) ;
res = ecr . res ;
}
out :
if ( req )
ablkcipher_request_free ( req ) ;
if ( tfm )
crypto_free_ablkcipher ( tfm ) ;
return res ;
}
2015-05-19 22:26:54 -07:00
static void f2fs_free_crypt_info ( struct f2fs_crypt_info * ci )
2015-04-21 16:23:47 -07:00
{
if ( ! ci )
return ;
2015-06-26 17:28:55 +02:00
key_put ( ci - > ci_keyring_key ) ;
2015-04-21 16:23:47 -07:00
crypto_free_ablkcipher ( ci - > ci_ctfm ) ;
2015-05-12 13:26:54 -07:00
kmem_cache_free ( f2fs_crypt_info_cachep , ci ) ;
2015-05-19 22:26:54 -07:00
}
void f2fs_free_encryption_info ( struct inode * inode , struct f2fs_crypt_info * ci )
{
struct f2fs_inode_info * fi = F2FS_I ( inode ) ;
struct f2fs_crypt_info * prev ;
if ( ci = = NULL )
ci = ACCESS_ONCE ( fi - > i_crypt_info ) ;
if ( ci = = NULL )
return ;
prev = cmpxchg ( & fi - > i_crypt_info , ci , NULL ) ;
if ( prev ! = ci )
return ;
f2fs_free_crypt_info ( ci ) ;
2015-04-21 16:23:47 -07:00
}
int _f2fs_get_encryption_info ( struct inode * inode )
{
struct f2fs_inode_info * fi = F2FS_I ( inode ) ;
struct f2fs_crypt_info * crypt_info ;
char full_key_descriptor [ F2FS_KEY_DESC_PREFIX_SIZE +
( F2FS_KEY_DESCRIPTOR_SIZE * 2 ) + 1 ] ;
struct key * keyring_key = NULL ;
struct f2fs_encryption_key * master_key ;
struct f2fs_encryption_context ctx ;
2015-10-21 14:04:48 +01:00
const struct user_key_payload * ukp ;
2015-05-19 22:26:54 -07:00
struct crypto_ablkcipher * ctfm ;
const char * cipher_str ;
char raw_key [ F2FS_MAX_KEY_SIZE ] ;
char mode ;
2015-04-21 16:23:47 -07:00
int res ;
2015-05-15 15:37:24 -07:00
res = f2fs_crypto_initialize ( ) ;
if ( res )
return res ;
2015-05-19 22:26:54 -07:00
retry :
crypt_info = ACCESS_ONCE ( fi - > i_crypt_info ) ;
if ( crypt_info ) {
if ( ! crypt_info - > ci_keyring_key | |
key_validate ( crypt_info - > ci_keyring_key ) = = 0 )
2015-04-21 16:23:47 -07:00
return 0 ;
2015-05-19 22:26:54 -07:00
f2fs_free_encryption_info ( inode , crypt_info ) ;
goto retry ;
2015-04-21 16:23:47 -07:00
}
res = f2fs_getxattr ( inode , F2FS_XATTR_INDEX_ENCRYPTION ,
F2FS_XATTR_NAME_ENCRYPTION_CONTEXT ,
& ctx , sizeof ( ctx ) , NULL ) ;
if ( res < 0 )
return res ;
else if ( res ! = sizeof ( ctx ) )
return - EINVAL ;
res = 0 ;
2015-05-12 13:26:54 -07:00
crypt_info = kmem_cache_alloc ( f2fs_crypt_info_cachep , GFP_NOFS ) ;
2015-04-21 16:23:47 -07:00
if ( ! crypt_info )
return - ENOMEM ;
crypt_info - > ci_flags = ctx . flags ;
crypt_info - > ci_data_mode = ctx . contents_encryption_mode ;
crypt_info - > ci_filename_mode = ctx . filenames_encryption_mode ;
crypt_info - > ci_ctfm = NULL ;
2015-05-19 22:26:54 -07:00
crypt_info - > ci_keyring_key = NULL ;
2015-04-21 16:23:47 -07:00
memcpy ( crypt_info - > ci_master_key , ctx . master_key_descriptor ,
sizeof ( crypt_info - > ci_master_key ) ) ;
if ( S_ISREG ( inode - > i_mode ) )
2015-05-19 22:26:54 -07:00
mode = crypt_info - > ci_data_mode ;
2015-04-21 16:23:47 -07:00
else if ( S_ISDIR ( inode - > i_mode ) | | S_ISLNK ( inode - > i_mode ) )
2015-05-19 22:26:54 -07:00
mode = crypt_info - > ci_filename_mode ;
2015-05-12 13:33:00 -07:00
else
2015-04-21 16:23:47 -07:00
BUG ( ) ;
2015-05-12 13:33:00 -07:00
2015-05-19 22:26:54 -07:00
switch ( mode ) {
case F2FS_ENCRYPTION_MODE_AES_256_XTS :
cipher_str = " xts(aes) " ;
break ;
case F2FS_ENCRYPTION_MODE_AES_256_CTS :
cipher_str = " cts(cbc(aes)) " ;
break ;
default :
printk_once ( KERN_WARNING
" f2fs: unsupported key mode %d (ino %u) \n " ,
mode , ( unsigned ) inode - > i_ino ) ;
res = - ENOKEY ;
goto out ;
}
2015-04-21 16:23:47 -07:00
memcpy ( full_key_descriptor , F2FS_KEY_DESC_PREFIX ,
F2FS_KEY_DESC_PREFIX_SIZE ) ;
sprintf ( full_key_descriptor + F2FS_KEY_DESC_PREFIX_SIZE ,
" %*phN " , F2FS_KEY_DESCRIPTOR_SIZE ,
ctx . master_key_descriptor ) ;
full_key_descriptor [ F2FS_KEY_DESC_PREFIX_SIZE +
( 2 * F2FS_KEY_DESCRIPTOR_SIZE ) ] = ' \0 ' ;
keyring_key = request_key ( & key_type_logon , full_key_descriptor , NULL ) ;
if ( IS_ERR ( keyring_key ) ) {
res = PTR_ERR ( keyring_key ) ;
keyring_key = NULL ;
goto out ;
}
2015-05-19 22:26:54 -07:00
crypt_info - > ci_keyring_key = keyring_key ;
2016-02-05 19:19:01 -08:00
if ( keyring_key - > type ! = & key_type_logon ) {
printk_once ( KERN_WARNING " f2fs: key type must be logon \n " ) ;
res = - ENOKEY ;
goto out ;
}
2015-10-21 14:04:48 +01:00
ukp = user_key_payload ( keyring_key ) ;
2015-04-21 16:23:47 -07:00
if ( ukp - > datalen ! = sizeof ( struct f2fs_encryption_key ) ) {
res = - EINVAL ;
goto out ;
}
master_key = ( struct f2fs_encryption_key * ) ukp - > data ;
BUILD_BUG_ON ( F2FS_AES_128_ECB_KEY_SIZE ! =
F2FS_KEY_DERIVATION_NONCE_SIZE ) ;
2016-02-05 19:19:01 -08:00
if ( master_key - > size ! = F2FS_AES_256_XTS_KEY_SIZE ) {
printk_once ( KERN_WARNING
" f2fs: key size incorrect: %d \n " ,
master_key - > size ) ;
res = - ENOKEY ;
goto out ;
}
2015-04-21 16:23:47 -07:00
res = f2fs_derive_key_aes ( ctx . nonce , master_key - > raw ,
2015-05-19 22:26:54 -07:00
raw_key ) ;
if ( res )
goto out ;
ctfm = crypto_alloc_ablkcipher ( cipher_str , 0 , 0 ) ;
if ( ! ctfm | | IS_ERR ( ctfm ) ) {
res = ctfm ? PTR_ERR ( ctfm ) : - ENOMEM ;
printk ( KERN_DEBUG
" %s: error %d (inode %u) allocating crypto tfm \n " ,
__func__ , res , ( unsigned ) inode - > i_ino ) ;
goto out ;
2015-04-21 16:23:47 -07:00
}
2015-05-19 22:26:54 -07:00
crypt_info - > ci_ctfm = ctfm ;
crypto_ablkcipher_clear_flags ( ctfm , ~ 0 ) ;
crypto_tfm_set_flags ( crypto_ablkcipher_tfm ( ctfm ) ,
CRYPTO_TFM_REQ_WEAK_KEY ) ;
res = crypto_ablkcipher_setkey ( ctfm , raw_key ,
f2fs_encryption_key_size ( mode ) ) ;
if ( res )
goto out ;
memzero_explicit ( raw_key , sizeof ( raw_key ) ) ;
if ( cmpxchg ( & fi - > i_crypt_info , NULL , crypt_info ) ! = NULL ) {
f2fs_free_crypt_info ( crypt_info ) ;
goto retry ;
}
return 0 ;
out :
if ( res = = - ENOKEY & & ! S_ISREG ( inode - > i_mode ) )
res = 0 ;
f2fs_free_crypt_info ( crypt_info ) ;
memzero_explicit ( raw_key , sizeof ( raw_key ) ) ;
2015-04-21 16:23:47 -07:00
return res ;
}
int f2fs_has_encryption_key ( struct inode * inode )
{
struct f2fs_inode_info * fi = F2FS_I ( inode ) ;
return ( fi - > i_crypt_info ! = NULL ) ;
}