2015-05-21 10:11:15 +03:00
/*
* echainiv : Encrypted Chain IV Generator
*
2016-09-07 13:42:08 +03:00
* This generator generates an IV based on a sequence number by multiplying
* it with a salt and then encrypting it with the same key as used to encrypt
2015-05-21 10:11:15 +03:00
* the plain text . This algorithm requires that the block size be equal
* to the IV size . It is mainly useful for CBC .
*
* This generator can only be used by algorithms where authentication
* is performed after encryption ( i . e . , authenc ) .
*
* Copyright ( c ) 2015 Herbert Xu < herbert @ gondor . apana . org . au >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation ; either version 2 of the License , or ( at your option )
* any later version .
*
*/
2015-05-27 09:37:33 +03:00
# include <crypto/internal/geniv.h>
2015-05-21 10:11:15 +03:00
# include <crypto/scatterwalk.h>
2016-07-12 08:17:43 +03:00
# include <crypto/skcipher.h>
2015-05-21 10:11:15 +03:00
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
2016-09-07 13:42:08 +03:00
# include <linux/slab.h>
2015-05-21 10:11:15 +03:00
# include <linux/string.h>
static int echainiv_encrypt ( struct aead_request * req )
{
struct crypto_aead * geniv = crypto_aead_reqtfm ( req ) ;
2015-08-13 12:29:01 +03:00
struct aead_geniv_ctx * ctx = crypto_aead_ctx ( geniv ) ;
2015-05-21 10:11:15 +03:00
struct aead_request * subreq = aead_request_ctx ( req ) ;
2016-09-07 13:42:08 +03:00
__be64 nseqno ;
u64 seqno ;
2015-05-21 10:11:15 +03:00
u8 * info ;
2015-05-23 10:41:54 +03:00
unsigned int ivsize = crypto_aead_ivsize ( geniv ) ;
2015-05-21 10:11:15 +03:00
int err ;
2015-05-23 10:41:54 +03:00
if ( req - > cryptlen < ivsize )
return - EINVAL ;
2015-08-13 12:29:01 +03:00
aead_request_set_tfm ( subreq , ctx - > child ) ;
2015-05-21 10:11:15 +03:00
info = req - > iv ;
if ( req - > src ! = req - > dst ) {
2016-07-12 08:17:43 +03:00
SKCIPHER_REQUEST_ON_STACK ( nreq , ctx - > sknull ) ;
2015-05-21 10:11:15 +03:00
2016-07-12 08:17:43 +03:00
skcipher_request_set_tfm ( nreq , ctx - > sknull ) ;
skcipher_request_set_callback ( nreq , req - > base . flags ,
NULL , NULL ) ;
skcipher_request_set_crypt ( nreq , req - > src , req - > dst ,
req - > assoclen + req - > cryptlen ,
NULL ) ;
err = crypto_skcipher_encrypt ( nreq ) ;
2015-05-21 10:11:15 +03:00
if ( err )
return err ;
}
2016-09-07 13:42:08 +03:00
aead_request_set_callback ( subreq , req - > base . flags ,
req - > base . complete , req - > base . data ) ;
2015-05-21 10:11:15 +03:00
aead_request_set_crypt ( subreq , req - > dst , req - > dst ,
2015-07-09 02:17:20 +03:00
req - > cryptlen , info ) ;
aead_request_set_ad ( subreq , req - > assoclen ) ;
2015-05-21 10:11:15 +03:00
2016-09-07 13:42:08 +03:00
memcpy ( & nseqno , info + ivsize - 8 , 8 ) ;
seqno = be64_to_cpu ( nseqno ) ;
memset ( info , 0 , ivsize ) ;
2015-05-21 10:11:15 +03:00
scatterwalk_map_and_copy ( info , req - > dst , req - > assoclen , ivsize , 1 ) ;
2016-09-07 13:42:08 +03:00
do {
u64 a ;
memcpy ( & a , ctx - > salt + ivsize - 8 , 8 ) ;
a | = 1 ;
a * = seqno ;
memcpy ( info + ivsize - 8 , & a , 8 ) ;
} while ( ( ivsize - = 8 ) ) ;
return crypto_aead_encrypt ( subreq ) ;
2015-05-21 10:11:15 +03:00
}
static int echainiv_decrypt ( struct aead_request * req )
{
struct crypto_aead * geniv = crypto_aead_reqtfm ( req ) ;
2015-08-13 12:29:01 +03:00
struct aead_geniv_ctx * ctx = crypto_aead_ctx ( geniv ) ;
2015-05-21 10:11:15 +03:00
struct aead_request * subreq = aead_request_ctx ( req ) ;
crypto_completion_t compl ;
void * data ;
2015-05-23 10:41:54 +03:00
unsigned int ivsize = crypto_aead_ivsize ( geniv ) ;
2015-07-09 02:17:20 +03:00
if ( req - > cryptlen < ivsize )
2015-05-23 10:41:54 +03:00
return - EINVAL ;
2015-05-21 10:11:15 +03:00
2015-08-13 12:29:01 +03:00
aead_request_set_tfm ( subreq , ctx - > child ) ;
2015-05-21 10:11:15 +03:00
compl = req - > base . complete ;
data = req - > base . data ;
aead_request_set_callback ( subreq , req - > base . flags , compl , data ) ;
aead_request_set_crypt ( subreq , req - > src , req - > dst ,
req - > cryptlen - ivsize , req - > iv ) ;
2015-05-23 10:41:57 +03:00
aead_request_set_ad ( subreq , req - > assoclen + ivsize ) ;
2015-05-21 10:11:15 +03:00
scatterwalk_map_and_copy ( req - > iv , req - > src , req - > assoclen , ivsize , 0 ) ;
return crypto_aead_decrypt ( subreq ) ;
}
2015-05-23 10:41:52 +03:00
static int echainiv_aead_create ( struct crypto_template * tmpl ,
struct rtattr * * tb )
2015-05-21 10:11:15 +03:00
{
struct aead_instance * inst ;
2015-05-23 10:41:52 +03:00
int err ;
2015-05-21 10:11:15 +03:00
2015-05-23 10:41:52 +03:00
inst = aead_geniv_alloc ( tmpl , tb , 0 , 0 ) ;
2015-05-21 10:11:15 +03:00
if ( IS_ERR ( inst ) )
2015-05-23 10:41:52 +03:00
return PTR_ERR ( inst ) ;
2015-05-21 10:11:15 +03:00
2015-05-23 10:41:52 +03:00
err = - EINVAL ;
2016-09-07 13:42:08 +03:00
if ( inst - > alg . ivsize & ( sizeof ( u64 ) - 1 ) | | ! inst - > alg . ivsize )
2015-05-23 10:41:52 +03:00
goto free_inst ;
2015-05-21 10:11:15 +03:00
2015-06-03 09:49:24 +03:00
inst - > alg . encrypt = echainiv_encrypt ;
2015-05-21 10:11:15 +03:00
inst - > alg . decrypt = echainiv_decrypt ;
2015-08-13 12:29:01 +03:00
inst - > alg . init = aead_init_geniv ;
inst - > alg . exit = aead_exit_geniv ;
2015-05-21 10:11:15 +03:00
2015-08-13 12:29:01 +03:00
inst - > alg . base . cra_ctxsize = sizeof ( struct aead_geniv_ctx ) ;
2015-05-27 09:37:34 +03:00
inst - > alg . base . cra_ctxsize + = inst - > alg . ivsize ;
2015-05-21 10:11:15 +03:00
2015-07-09 02:17:20 +03:00
inst - > free = aead_geniv_free ;
2015-05-23 10:41:52 +03:00
err = aead_register_instance ( tmpl , inst ) ;
if ( err )
goto free_inst ;
2015-05-21 10:11:15 +03:00
out :
2015-05-23 10:41:52 +03:00
return err ;
free_inst :
aead_geniv_free ( inst ) ;
goto out ;
2015-05-21 10:11:15 +03:00
}
static void echainiv_free ( struct crypto_instance * inst )
{
aead_geniv_free ( aead_instance ( inst ) ) ;
}
static struct crypto_template echainiv_tmpl = {
. name = " echainiv " ,
2015-06-21 14:11:50 +03:00
. create = echainiv_aead_create ,
2015-05-21 10:11:15 +03:00
. free = echainiv_free ,
. module = THIS_MODULE ,
} ;
static int __init echainiv_module_init ( void )
{
return crypto_register_template ( & echainiv_tmpl ) ;
}
static void __exit echainiv_module_exit ( void )
{
crypto_unregister_template ( & echainiv_tmpl ) ;
}
module_init ( echainiv_module_init ) ;
module_exit ( echainiv_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Encrypted Chain IV Generator " ) ;
MODULE_ALIAS_CRYPTO ( " echainiv " ) ;