2007-11-26 17:24:11 +03:00
/*
* GCM : Galois / Counter Mode .
*
* Copyright ( c ) 2007 Nokia Siemens Networks - Mikko Herranen < mh1 @ iki . fi >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation .
*/
# include <crypto/gf128mul.h>
2007-12-17 10:33:17 +03:00
# include <crypto/internal/aead.h>
2007-12-07 14:26:11 +03:00
# include <crypto/internal/skcipher.h>
2009-08-06 09:34:26 +04:00
# include <crypto/internal/hash.h>
2007-12-07 13:52:49 +03:00
# include <crypto/scatterwalk.h>
2009-08-06 09:34:26 +04:00
# include <crypto/hash.h>
# include "internal.h"
2007-12-17 16:42:08 +03:00
# include <linux/completion.h>
2007-11-26 17:24:11 +03:00
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/slab.h>
struct gcm_instance_ctx {
2007-12-07 14:26:11 +03:00
struct crypto_skcipher_spawn ctr ;
2009-08-06 09:34:26 +04:00
struct crypto_ahash_spawn ghash ;
2007-11-26 17:24:11 +03:00
} ;
struct crypto_gcm_ctx {
struct crypto_ablkcipher * ctr ;
2009-08-06 09:34:26 +04:00
struct crypto_ahash * ghash ;
2007-11-26 17:24:11 +03:00
} ;
2007-12-17 10:33:17 +03:00
struct crypto_rfc4106_ctx {
struct crypto_aead * child ;
u8 nonce [ 4 ] ;
} ;
2010-01-17 13:52:11 +03:00
struct crypto_rfc4543_ctx {
struct crypto_aead * child ;
u8 nonce [ 4 ] ;
} ;
struct crypto_rfc4543_req_ctx {
u8 auth_tag [ 16 ] ;
struct scatterlist cipher [ 1 ] ;
struct scatterlist payload [ 2 ] ;
struct scatterlist assoc [ 2 ] ;
struct aead_request subreq ;
} ;
2007-11-26 17:24:11 +03:00
struct crypto_gcm_ghash_ctx {
2009-08-06 09:34:26 +04:00
unsigned int cryptlen ;
struct scatterlist * src ;
2009-11-16 16:52:22 +03:00
void ( * complete ) ( struct aead_request * req , int err ) ;
2007-11-26 17:24:11 +03:00
} ;
struct crypto_gcm_req_priv_ctx {
u8 auth_tag [ 16 ] ;
2007-12-04 11:17:50 +03:00
u8 iauth_tag [ 16 ] ;
2007-12-17 16:42:08 +03:00
struct scatterlist src [ 2 ] ;
struct scatterlist dst [ 2 ] ;
2009-08-06 09:34:26 +04:00
struct crypto_gcm_ghash_ctx ghash_ctx ;
union {
struct ahash_request ahreq ;
struct ablkcipher_request abreq ;
} u ;
2007-11-26 17:24:11 +03:00
} ;
2007-12-17 16:42:08 +03:00
struct crypto_gcm_setkey_result {
int err ;
struct completion completion ;
} ;
2009-08-06 09:34:26 +04:00
static void * gcm_zeroes ;
2007-12-12 14:16:38 +03:00
static inline struct crypto_gcm_req_priv_ctx * crypto_gcm_reqctx (
struct aead_request * req )
{
unsigned long align = crypto_aead_alignmask ( crypto_aead_reqtfm ( req ) ) ;
return ( void * ) PTR_ALIGN ( ( u8 * ) aead_request_ctx ( req ) , align + 1 ) ;
}
2007-12-17 16:42:08 +03:00
static void crypto_gcm_setkey_done ( struct crypto_async_request * req , int err )
2007-11-26 17:24:11 +03:00
{
2007-12-17 16:42:08 +03:00
struct crypto_gcm_setkey_result * result = req - > data ;
2007-11-26 17:24:11 +03:00
2007-12-17 16:42:08 +03:00
if ( err = = - EINPROGRESS )
return ;
result - > err = err ;
complete ( & result - > completion ) ;
2007-11-26 17:24:11 +03:00
}
static int crypto_gcm_setkey ( struct crypto_aead * aead , const u8 * key ,
unsigned int keylen )
{
struct crypto_gcm_ctx * ctx = crypto_aead_ctx ( aead ) ;
2009-08-06 09:34:26 +04:00
struct crypto_ahash * ghash = ctx - > ghash ;
2007-11-26 17:24:11 +03:00
struct crypto_ablkcipher * ctr = ctx - > ctr ;
2007-12-17 16:42:08 +03:00
struct {
be128 hash ;
u8 iv [ 8 ] ;
struct crypto_gcm_setkey_result result ;
struct scatterlist sg [ 1 ] ;
struct ablkcipher_request req ;
} * data ;
int err ;
2007-11-26 17:24:11 +03:00
crypto_ablkcipher_clear_flags ( ctr , CRYPTO_TFM_REQ_MASK ) ;
crypto_ablkcipher_set_flags ( ctr , crypto_aead_get_flags ( aead ) &
CRYPTO_TFM_REQ_MASK ) ;
err = crypto_ablkcipher_setkey ( ctr , key , keylen ) ;
if ( err )
2007-12-17 16:42:08 +03:00
return err ;
2007-11-26 17:24:11 +03:00
crypto_aead_set_flags ( aead , crypto_ablkcipher_get_flags ( ctr ) &
CRYPTO_TFM_RES_MASK ) ;
2007-12-17 16:42:08 +03:00
data = kzalloc ( sizeof ( * data ) + crypto_ablkcipher_reqsize ( ctr ) ,
GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
init_completion ( & data - > result . completion ) ;
sg_init_one ( data - > sg , & data - > hash , sizeof ( data - > hash ) ) ;
ablkcipher_request_set_tfm ( & data - > req , ctr ) ;
ablkcipher_request_set_callback ( & data - > req , CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG ,
crypto_gcm_setkey_done ,
& data - > result ) ;
ablkcipher_request_set_crypt ( & data - > req , data - > sg , data - > sg ,
sizeof ( data - > hash ) , data - > iv ) ;
err = crypto_ablkcipher_encrypt ( & data - > req ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY ) {
err = wait_for_completion_interruptible (
& data - > result . completion ) ;
if ( ! err )
err = data - > result . err ;
}
2007-11-26 17:24:11 +03:00
if ( err )
goto out ;
2009-08-06 09:34:26 +04:00
crypto_ahash_clear_flags ( ghash , CRYPTO_TFM_REQ_MASK ) ;
crypto_ahash_set_flags ( ghash , crypto_aead_get_flags ( aead ) &
CRYPTO_TFM_REQ_MASK ) ;
err = crypto_ahash_setkey ( ghash , ( u8 * ) & data - > hash , sizeof ( be128 ) ) ;
crypto_aead_set_flags ( aead , crypto_ahash_get_flags ( ghash ) &
CRYPTO_TFM_RES_MASK ) ;
2007-11-26 17:24:11 +03:00
2007-12-17 16:42:08 +03:00
out :
kfree ( data ) ;
2007-11-26 17:24:11 +03:00
return err ;
}
2007-12-17 10:33:17 +03:00
static int crypto_gcm_setauthsize ( struct crypto_aead * tfm ,
unsigned int authsize )
{
switch ( authsize ) {
case 4 :
case 8 :
case 12 :
case 13 :
case 14 :
case 15 :
case 16 :
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2007-12-17 16:42:08 +03:00
static void crypto_gcm_init_crypt ( struct ablkcipher_request * ablk_req ,
struct aead_request * req ,
unsigned int cryptlen )
2007-11-26 17:24:11 +03:00
{
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
struct crypto_gcm_ctx * ctx = crypto_aead_ctx ( aead ) ;
2007-12-12 14:16:38 +03:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
2007-12-17 16:42:08 +03:00
struct scatterlist * dst ;
__be32 counter = cpu_to_be32 ( 1 ) ;
memset ( pctx - > auth_tag , 0 , sizeof ( pctx - > auth_tag ) ) ;
memcpy ( req - > iv + 12 , & counter , 4 ) ;
sg_init_table ( pctx - > src , 2 ) ;
sg_set_buf ( pctx - > src , pctx - > auth_tag , sizeof ( pctx - > auth_tag ) ) ;
scatterwalk_sg_chain ( pctx - > src , 2 , req - > src ) ;
dst = pctx - > src ;
if ( req - > src ! = req - > dst ) {
sg_init_table ( pctx - > dst , 2 ) ;
sg_set_buf ( pctx - > dst , pctx - > auth_tag , sizeof ( pctx - > auth_tag ) ) ;
scatterwalk_sg_chain ( pctx - > dst , 2 , req - > dst ) ;
dst = pctx - > dst ;
}
2007-11-26 17:24:11 +03:00
ablkcipher_request_set_tfm ( ablk_req , ctx - > ctr ) ;
2007-12-17 16:42:08 +03:00
ablkcipher_request_set_crypt ( ablk_req , pctx - > src , dst ,
cryptlen + sizeof ( pctx - > auth_tag ) ,
req - > iv ) ;
2009-08-06 09:34:26 +04:00
}
static inline unsigned int gcm_remain ( unsigned int len )
{
len & = 0xfU ;
return len ? 16 - len : 0 ;
}
static void gcm_hash_len_done ( struct crypto_async_request * areq , int err ) ;
static void gcm_hash_final_done ( struct crypto_async_request * areq , int err ) ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
static int gcm_hash_update ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx ,
crypto_completion_t complete ,
struct scatterlist * src ,
unsigned int len )
{
struct ahash_request * ahreq = & pctx - > u . ahreq ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
ahash_request_set_callback ( ahreq , aead_request_flags ( req ) ,
complete , req ) ;
ahash_request_set_crypt ( ahreq , src , NULL , len ) ;
return crypto_ahash_update ( ahreq ) ;
2007-11-26 17:24:11 +03:00
}
2009-08-06 09:34:26 +04:00
static int gcm_hash_remain ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx ,
unsigned int remain ,
crypto_completion_t complete )
2007-11-26 17:24:11 +03:00
{
2009-08-06 09:34:26 +04:00
struct ahash_request * ahreq = & pctx - > u . ahreq ;
ahash_request_set_callback ( ahreq , aead_request_flags ( req ) ,
complete , req ) ;
sg_init_one ( pctx - > src , gcm_zeroes , remain ) ;
ahash_request_set_crypt ( ahreq , pctx - > src , NULL , remain ) ;
return crypto_ahash_update ( ahreq ) ;
}
static int gcm_hash_len ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx )
{
struct ahash_request * ahreq = & pctx - > u . ahreq ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
u128 lengths ;
lengths . a = cpu_to_be64 ( req - > assoclen * 8 ) ;
lengths . b = cpu_to_be64 ( gctx - > cryptlen * 8 ) ;
memcpy ( pctx - > iauth_tag , & lengths , 16 ) ;
sg_init_one ( pctx - > src , pctx - > iauth_tag , 16 ) ;
ahash_request_set_callback ( ahreq , aead_request_flags ( req ) ,
gcm_hash_len_done , req ) ;
ahash_request_set_crypt ( ahreq , pctx - > src ,
NULL , sizeof ( lengths ) ) ;
return crypto_ahash_update ( ahreq ) ;
}
static int gcm_hash_final ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx )
{
struct ahash_request * ahreq = & pctx - > u . ahreq ;
ahash_request_set_callback ( ahreq , aead_request_flags ( req ) ,
gcm_hash_final_done , req ) ;
ahash_request_set_crypt ( ahreq , NULL , pctx - > iauth_tag , 0 ) ;
return crypto_ahash_final ( ahreq ) ;
}
2009-11-16 16:52:22 +03:00
static void __gcm_hash_final_done ( struct aead_request * req , int err )
2009-08-06 09:34:26 +04:00
{
2007-12-12 14:16:38 +03:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
2009-08-06 09:34:26 +04:00
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
if ( ! err )
crypto_xor ( pctx - > auth_tag , pctx - > iauth_tag , 16 ) ;
2007-11-26 17:24:11 +03:00
2009-11-16 16:52:22 +03:00
gctx - > complete ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
static void gcm_hash_final_done ( struct crypto_async_request * areq , int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
2009-11-16 16:52:22 +03:00
__gcm_hash_final_done ( req , err ) ;
}
static void __gcm_hash_len_done ( struct aead_request * req , int err )
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
if ( ! err ) {
err = gcm_hash_final ( req , pctx ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
}
2009-11-16 16:52:22 +03:00
__gcm_hash_final_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
static void gcm_hash_len_done ( struct crypto_async_request * areq , int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
2009-11-16 16:52:22 +03:00
__gcm_hash_len_done ( req , err ) ;
}
static void __gcm_hash_crypt_remain_done ( struct aead_request * req , int err )
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
if ( ! err ) {
err = gcm_hash_len ( req , pctx ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
}
2009-11-16 16:52:22 +03:00
__gcm_hash_len_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
static void gcm_hash_crypt_remain_done ( struct crypto_async_request * areq ,
int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
2009-11-16 16:52:22 +03:00
__gcm_hash_crypt_remain_done ( req , err ) ;
}
static void __gcm_hash_crypt_done ( struct aead_request * req , int err )
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
unsigned int remain ;
if ( ! err ) {
remain = gcm_remain ( gctx - > cryptlen ) ;
BUG_ON ( ! remain ) ;
err = gcm_hash_remain ( req , pctx , remain ,
gcm_hash_crypt_remain_done ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
}
2009-11-16 16:52:22 +03:00
__gcm_hash_crypt_remain_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
static void gcm_hash_crypt_done ( struct crypto_async_request * areq , int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
2009-11-16 16:52:22 +03:00
__gcm_hash_crypt_done ( req , err ) ;
}
static void __gcm_hash_assoc_remain_done ( struct aead_request * req , int err )
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
crypto_completion_t complete ;
unsigned int remain = 0 ;
if ( ! err & & gctx - > cryptlen ) {
remain = gcm_remain ( gctx - > cryptlen ) ;
complete = remain ? gcm_hash_crypt_done :
gcm_hash_crypt_remain_done ;
err = gcm_hash_update ( req , pctx , complete ,
gctx - > src , gctx - > cryptlen ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
}
if ( remain )
2009-11-16 16:52:22 +03:00
__gcm_hash_crypt_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
else
2009-11-16 16:52:22 +03:00
__gcm_hash_crypt_remain_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
static void gcm_hash_assoc_remain_done ( struct crypto_async_request * areq ,
int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
2009-11-16 16:52:22 +03:00
__gcm_hash_assoc_remain_done ( req , err ) ;
}
static void __gcm_hash_assoc_done ( struct aead_request * req , int err )
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
unsigned int remain ;
if ( ! err ) {
remain = gcm_remain ( req - > assoclen ) ;
BUG_ON ( ! remain ) ;
err = gcm_hash_remain ( req , pctx , remain ,
gcm_hash_assoc_remain_done ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
}
2009-11-16 16:52:22 +03:00
__gcm_hash_assoc_remain_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
static void gcm_hash_assoc_done ( struct crypto_async_request * areq , int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
2009-11-16 16:52:22 +03:00
__gcm_hash_assoc_done ( req , err ) ;
}
static void __gcm_hash_init_done ( struct aead_request * req , int err )
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
crypto_completion_t complete ;
unsigned int remain = 0 ;
if ( ! err & & req - > assoclen ) {
remain = gcm_remain ( req - > assoclen ) ;
complete = remain ? gcm_hash_assoc_done :
gcm_hash_assoc_remain_done ;
err = gcm_hash_update ( req , pctx , complete ,
req - > assoc , req - > assoclen ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
}
if ( remain )
2009-11-16 16:52:22 +03:00
__gcm_hash_assoc_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
else
2009-11-16 16:52:22 +03:00
__gcm_hash_assoc_remain_done ( req , err ) ;
}
static void gcm_hash_init_done ( struct crypto_async_request * areq , int err )
{
struct aead_request * req = areq - > data ;
__gcm_hash_init_done ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
static int gcm_hash ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx )
{
struct ahash_request * ahreq = & pctx - > u . ahreq ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
struct crypto_gcm_ctx * ctx = crypto_tfm_ctx ( req - > base . tfm ) ;
unsigned int remain ;
crypto_completion_t complete ;
int err ;
ahash_request_set_tfm ( ahreq , ctx - > ghash ) ;
ahash_request_set_callback ( ahreq , aead_request_flags ( req ) ,
gcm_hash_init_done , req ) ;
err = crypto_ahash_init ( ahreq ) ;
if ( err )
return err ;
remain = gcm_remain ( req - > assoclen ) ;
complete = remain ? gcm_hash_assoc_done : gcm_hash_assoc_remain_done ;
err = gcm_hash_update ( req , pctx , complete , req - > assoc , req - > assoclen ) ;
if ( err )
return err ;
if ( remain ) {
err = gcm_hash_remain ( req , pctx , remain ,
gcm_hash_assoc_remain_done ) ;
if ( err )
return err ;
}
remain = gcm_remain ( gctx - > cryptlen ) ;
complete = remain ? gcm_hash_crypt_done : gcm_hash_crypt_remain_done ;
err = gcm_hash_update ( req , pctx , complete , gctx - > src , gctx - > cryptlen ) ;
if ( err )
return err ;
if ( remain ) {
err = gcm_hash_remain ( req , pctx , remain ,
gcm_hash_crypt_remain_done ) ;
if ( err )
return err ;
}
err = gcm_hash_len ( req , pctx ) ;
if ( err )
return err ;
err = gcm_hash_final ( req , pctx ) ;
if ( err )
return err ;
return 0 ;
}
static void gcm_enc_copy_hash ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx )
{
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
u8 * auth_tag = pctx - > auth_tag ;
2007-11-26 17:24:11 +03:00
2007-12-04 11:17:50 +03:00
scatterwalk_map_and_copy ( auth_tag , req - > dst , req - > cryptlen ,
crypto_aead_authsize ( aead ) , 1 ) ;
}
2009-11-16 16:52:22 +03:00
static void gcm_enc_hash_done ( struct aead_request * req , int err )
2007-12-04 11:17:50 +03:00
{
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
2007-12-04 11:17:50 +03:00
if ( ! err )
2009-08-06 09:34:26 +04:00
gcm_enc_copy_hash ( req , pctx ) ;
2007-12-04 11:17:50 +03:00
2007-11-26 17:24:11 +03:00
aead_request_complete ( req , err ) ;
}
2009-11-16 16:52:22 +03:00
static void gcm_encrypt_done ( struct crypto_async_request * areq , int err )
2009-08-06 09:34:26 +04:00
{
struct aead_request * req = areq - > data ;
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
if ( ! err ) {
err = gcm_hash ( req , pctx ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
2009-11-16 16:52:22 +03:00
else if ( ! err ) {
crypto_xor ( pctx - > auth_tag , pctx - > iauth_tag , 16 ) ;
gcm_enc_copy_hash ( req , pctx ) ;
}
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
aead_request_complete ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2007-11-26 17:24:11 +03:00
static int crypto_gcm_encrypt ( struct aead_request * req )
{
2007-12-12 14:16:38 +03:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
2009-08-06 09:34:26 +04:00
struct ablkcipher_request * abreq = & pctx - > u . abreq ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
2007-12-17 16:42:08 +03:00
int err ;
crypto_gcm_init_crypt ( abreq , req , req - > cryptlen ) ;
ablkcipher_request_set_callback ( abreq , aead_request_flags ( req ) ,
2009-08-06 09:34:26 +04:00
gcm_encrypt_done , req ) ;
gctx - > src = req - > dst ;
gctx - > cryptlen = req - > cryptlen ;
gctx - > complete = gcm_enc_hash_done ;
2007-11-26 17:24:11 +03:00
2007-12-17 16:42:08 +03:00
err = crypto_ablkcipher_encrypt ( abreq ) ;
2007-11-26 17:24:11 +03:00
if ( err )
return err ;
2009-08-06 09:34:26 +04:00
err = gcm_hash ( req , pctx ) ;
if ( err )
return err ;
crypto_xor ( pctx - > auth_tag , pctx - > iauth_tag , 16 ) ;
gcm_enc_copy_hash ( req , pctx ) ;
return 0 ;
2007-11-26 17:24:11 +03:00
}
2009-08-06 09:34:26 +04:00
static int crypto_gcm_verify ( struct aead_request * req ,
struct crypto_gcm_req_priv_ctx * pctx )
2007-12-17 16:42:08 +03:00
{
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
u8 * auth_tag = pctx - > auth_tag ;
u8 * iauth_tag = pctx - > iauth_tag ;
unsigned int authsize = crypto_aead_authsize ( aead ) ;
unsigned int cryptlen = req - > cryptlen - authsize ;
2009-08-06 09:34:26 +04:00
crypto_xor ( auth_tag , iauth_tag , 16 ) ;
2007-12-17 16:42:08 +03:00
scatterwalk_map_and_copy ( iauth_tag , req - > src , cryptlen , authsize , 0 ) ;
return memcmp ( iauth_tag , auth_tag , authsize ) ? - EBADMSG : 0 ;
}
2009-08-06 09:34:26 +04:00
static void gcm_decrypt_done ( struct crypto_async_request * areq , int err )
2007-11-26 17:24:11 +03:00
{
2007-12-17 16:42:08 +03:00
struct aead_request * req = areq - > data ;
2009-08-06 09:34:26 +04:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
2007-12-17 16:42:08 +03:00
if ( ! err )
2009-08-06 09:34:26 +04:00
err = crypto_gcm_verify ( req , pctx ) ;
2007-12-17 16:42:08 +03:00
aead_request_complete ( req , err ) ;
2007-11-26 17:24:11 +03:00
}
2009-11-16 16:52:22 +03:00
static void gcm_dec_hash_done ( struct aead_request * req , int err )
2009-08-06 09:34:26 +04:00
{
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
struct ablkcipher_request * abreq = & pctx - > u . abreq ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
if ( ! err ) {
ablkcipher_request_set_callback ( abreq , aead_request_flags ( req ) ,
gcm_decrypt_done , req ) ;
crypto_gcm_init_crypt ( abreq , req , gctx - > cryptlen ) ;
err = crypto_ablkcipher_decrypt ( abreq ) ;
if ( err = = - EINPROGRESS | | err = = - EBUSY )
return ;
2009-11-16 16:52:22 +03:00
else if ( ! err )
err = crypto_gcm_verify ( req , pctx ) ;
2009-08-06 09:34:26 +04:00
}
2009-11-16 16:52:22 +03:00
aead_request_complete ( req , err ) ;
2009-08-06 09:34:26 +04:00
}
2007-11-26 17:24:11 +03:00
static int crypto_gcm_decrypt ( struct aead_request * req )
{
2007-12-04 11:17:50 +03:00
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
2007-12-12 14:16:38 +03:00
struct crypto_gcm_req_priv_ctx * pctx = crypto_gcm_reqctx ( req ) ;
2009-08-06 09:34:26 +04:00
struct ablkcipher_request * abreq = & pctx - > u . abreq ;
struct crypto_gcm_ghash_ctx * gctx = & pctx - > ghash_ctx ;
2007-12-04 11:17:50 +03:00
unsigned int authsize = crypto_aead_authsize ( aead ) ;
2009-08-06 09:34:26 +04:00
unsigned int cryptlen = req - > cryptlen ;
2007-11-26 17:24:11 +03:00
int err ;
2007-12-04 11:17:50 +03:00
if ( cryptlen < authsize )
2007-11-26 17:24:11 +03:00
return - EINVAL ;
2007-12-04 11:17:50 +03:00
cryptlen - = authsize ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
gctx - > src = req - > src ;
gctx - > cryptlen = cryptlen ;
gctx - > complete = gcm_dec_hash_done ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
err = gcm_hash ( req , pctx ) ;
if ( err )
return err ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
ablkcipher_request_set_callback ( abreq , aead_request_flags ( req ) ,
gcm_decrypt_done , req ) ;
crypto_gcm_init_crypt ( abreq , req , cryptlen ) ;
2007-12-17 16:42:08 +03:00
err = crypto_ablkcipher_decrypt ( abreq ) ;
if ( err )
return err ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
return crypto_gcm_verify ( req , pctx ) ;
2007-11-26 17:24:11 +03:00
}
static int crypto_gcm_init_tfm ( struct crypto_tfm * tfm )
{
struct crypto_instance * inst = ( void * ) tfm - > __crt_alg ;
struct gcm_instance_ctx * ictx = crypto_instance_ctx ( inst ) ;
struct crypto_gcm_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_ablkcipher * ctr ;
2009-08-06 09:34:26 +04:00
struct crypto_ahash * ghash ;
2007-11-26 17:24:11 +03:00
unsigned long align ;
int err ;
2009-08-06 09:34:26 +04:00
ghash = crypto_spawn_ahash ( & ictx - > ghash ) ;
if ( IS_ERR ( ghash ) )
return PTR_ERR ( ghash ) ;
2007-12-07 14:26:11 +03:00
ctr = crypto_spawn_skcipher ( & ictx - > ctr ) ;
2007-11-26 17:24:11 +03:00
err = PTR_ERR ( ctr ) ;
if ( IS_ERR ( ctr ) )
2009-08-06 09:34:26 +04:00
goto err_free_hash ;
2007-11-26 17:24:11 +03:00
ctx - > ctr = ctr ;
2009-08-06 09:34:26 +04:00
ctx - > ghash = ghash ;
2007-11-26 17:24:11 +03:00
2007-12-12 14:16:38 +03:00
align = crypto_tfm_alg_alignmask ( tfm ) ;
2007-11-26 17:24:11 +03:00
align & = ~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ;
2007-12-06 09:59:53 +03:00
tfm - > crt_aead . reqsize = align +
2009-08-06 09:34:26 +04:00
offsetof ( struct crypto_gcm_req_priv_ctx , u ) +
max ( sizeof ( struct ablkcipher_request ) +
crypto_ablkcipher_reqsize ( ctr ) ,
sizeof ( struct ahash_request ) +
crypto_ahash_reqsize ( ghash ) ) ;
2007-11-26 17:24:11 +03:00
return 0 ;
2009-08-06 09:34:26 +04:00
err_free_hash :
crypto_free_ahash ( ghash ) ;
return err ;
2007-11-26 17:24:11 +03:00
}
static void crypto_gcm_exit_tfm ( struct crypto_tfm * tfm )
{
struct crypto_gcm_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
2009-08-06 09:34:26 +04:00
crypto_free_ahash ( ctx - > ghash ) ;
2007-11-26 17:24:11 +03:00
crypto_free_ablkcipher ( ctx - > ctr ) ;
}
2007-12-07 15:31:10 +03:00
static struct crypto_instance * crypto_gcm_alloc_common ( struct rtattr * * tb ,
const char * full_name ,
2009-08-06 09:34:26 +04:00
const char * ctr_name ,
const char * ghash_name )
2007-11-26 17:24:11 +03:00
{
2007-12-07 15:31:10 +03:00
struct crypto_attr_type * algt ;
2007-11-26 17:24:11 +03:00
struct crypto_instance * inst ;
struct crypto_alg * ctr ;
2009-08-06 09:34:26 +04:00
struct crypto_alg * ghash_alg ;
struct ahash_alg * ghash_ahash_alg ;
2007-11-26 17:24:11 +03:00
struct gcm_instance_ctx * ctx ;
int err ;
2007-12-07 15:31:10 +03:00
algt = crypto_get_attr_type ( tb ) ;
err = PTR_ERR ( algt ) ;
if ( IS_ERR ( algt ) )
2007-11-26 17:24:11 +03:00
return ERR_PTR ( err ) ;
2007-12-07 15:31:10 +03:00
if ( ( algt - > type ^ CRYPTO_ALG_TYPE_AEAD ) & algt - > mask )
return ERR_PTR ( - EINVAL ) ;
2007-11-26 17:24:11 +03:00
2009-08-06 09:34:26 +04:00
ghash_alg = crypto_find_alg ( ghash_name , & crypto_ahash_type ,
CRYPTO_ALG_TYPE_HASH ,
CRYPTO_ALG_TYPE_AHASH_MASK ) ;
err = PTR_ERR ( ghash_alg ) ;
if ( IS_ERR ( ghash_alg ) )
return ERR_PTR ( err ) ;
err = - ENOMEM ;
2007-12-07 14:26:11 +03:00
inst = kzalloc ( sizeof ( * inst ) + sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! inst )
2009-08-06 09:34:26 +04:00
goto out_put_ghash ;
2007-11-26 17:24:11 +03:00
2007-12-07 14:26:11 +03:00
ctx = crypto_instance_ctx ( inst ) ;
2009-08-06 09:34:26 +04:00
ghash_ahash_alg = container_of ( ghash_alg , struct ahash_alg , halg . base ) ;
err = crypto_init_ahash_spawn ( & ctx - > ghash , & ghash_ahash_alg - > halg ,
inst ) ;
if ( err )
goto err_free_inst ;
2007-12-07 14:26:11 +03:00
crypto_set_skcipher_spawn ( & ctx - > ctr , inst ) ;
err = crypto_grab_skcipher ( & ctx - > ctr , ctr_name , 0 ,
crypto_requires_sync ( algt - > type ,
algt - > mask ) ) ;
if ( err )
2009-08-06 09:34:26 +04:00
goto err_drop_ghash ;
2007-12-07 14:26:11 +03:00
ctr = crypto_skcipher_spawn_alg ( & ctx - > ctr ) ;
2007-11-26 17:24:11 +03:00
2007-12-07 15:31:10 +03:00
/* We only support 16-byte blocks. */
2007-12-07 14:26:11 +03:00
if ( ctr - > cra_ablkcipher . ivsize ! = 16 )
2007-12-07 15:31:10 +03:00
goto out_put_ctr ;
/* Not a stream cipher? */
err = - EINVAL ;
if ( ctr - > cra_blocksize ! = 1 )
2007-11-26 17:24:11 +03:00
goto out_put_ctr ;
err = - ENAMETOOLONG ;
2007-12-07 15:31:10 +03:00
if ( snprintf ( inst - > alg . cra_driver_name , CRYPTO_MAX_ALG_NAME ,
2009-08-06 09:34:26 +04:00
" gcm_base(%s,%s) " , ctr - > cra_driver_name ,
ghash_alg - > cra_driver_name ) > =
2007-12-07 15:31:10 +03:00
CRYPTO_MAX_ALG_NAME )
2007-12-07 14:26:11 +03:00
goto out_put_ctr ;
2007-11-26 17:24:11 +03:00
2007-12-07 15:31:10 +03:00
memcpy ( inst - > alg . cra_name , full_name , CRYPTO_MAX_ALG_NAME ) ;
2007-12-07 14:26:11 +03:00
inst - > alg . cra_flags = CRYPTO_ALG_TYPE_AEAD ;
inst - > alg . cra_flags | = ctr - > cra_flags & CRYPTO_ALG_ASYNC ;
2007-11-26 17:24:11 +03:00
inst - > alg . cra_priority = ctr - > cra_priority ;
2007-12-07 15:31:10 +03:00
inst - > alg . cra_blocksize = 1 ;
2007-12-12 14:16:38 +03:00
inst - > alg . cra_alignmask = ctr - > cra_alignmask | ( __alignof__ ( u64 ) - 1 ) ;
2007-11-26 17:24:11 +03:00
inst - > alg . cra_type = & crypto_aead_type ;
2007-12-17 16:42:08 +03:00
inst - > alg . cra_aead . ivsize = 16 ;
2007-12-02 10:49:21 +03:00
inst - > alg . cra_aead . maxauthsize = 16 ;
2007-11-26 17:24:11 +03:00
inst - > alg . cra_ctxsize = sizeof ( struct crypto_gcm_ctx ) ;
inst - > alg . cra_init = crypto_gcm_init_tfm ;
inst - > alg . cra_exit = crypto_gcm_exit_tfm ;
inst - > alg . cra_aead . setkey = crypto_gcm_setkey ;
2007-12-17 10:33:17 +03:00
inst - > alg . cra_aead . setauthsize = crypto_gcm_setauthsize ;
2007-11-26 17:24:11 +03:00
inst - > alg . cra_aead . encrypt = crypto_gcm_encrypt ;
inst - > alg . cra_aead . decrypt = crypto_gcm_decrypt ;
out :
2009-08-06 09:34:26 +04:00
crypto_mod_put ( ghash_alg ) ;
2007-11-26 17:24:11 +03:00
return inst ;
2007-12-07 14:26:11 +03:00
out_put_ctr :
crypto_drop_skcipher ( & ctx - > ctr ) ;
2009-08-06 09:34:26 +04:00
err_drop_ghash :
crypto_drop_ahash ( & ctx - > ghash ) ;
2007-11-26 17:24:11 +03:00
err_free_inst :
kfree ( inst ) ;
2009-08-06 09:34:26 +04:00
out_put_ghash :
2007-11-26 17:24:11 +03:00
inst = ERR_PTR ( err ) ;
goto out ;
}
2007-12-07 15:31:10 +03:00
static struct crypto_instance * crypto_gcm_alloc ( struct rtattr * * tb )
{
int err ;
const char * cipher_name ;
char ctr_name [ CRYPTO_MAX_ALG_NAME ] ;
char full_name [ CRYPTO_MAX_ALG_NAME ] ;
cipher_name = crypto_attr_alg_name ( tb [ 1 ] ) ;
err = PTR_ERR ( cipher_name ) ;
if ( IS_ERR ( cipher_name ) )
return ERR_PTR ( err ) ;
if ( snprintf ( ctr_name , CRYPTO_MAX_ALG_NAME , " ctr(%s) " , cipher_name ) > =
CRYPTO_MAX_ALG_NAME )
return ERR_PTR ( - ENAMETOOLONG ) ;
if ( snprintf ( full_name , CRYPTO_MAX_ALG_NAME , " gcm(%s) " , cipher_name ) > =
CRYPTO_MAX_ALG_NAME )
return ERR_PTR ( - ENAMETOOLONG ) ;
2009-08-06 09:34:26 +04:00
return crypto_gcm_alloc_common ( tb , full_name , ctr_name , " ghash " ) ;
2007-12-07 15:31:10 +03:00
}
2007-11-26 17:24:11 +03:00
static void crypto_gcm_free ( struct crypto_instance * inst )
{
struct gcm_instance_ctx * ctx = crypto_instance_ctx ( inst ) ;
2007-12-07 14:26:11 +03:00
crypto_drop_skcipher ( & ctx - > ctr ) ;
2009-08-06 09:34:26 +04:00
crypto_drop_ahash ( & ctx - > ghash ) ;
2007-11-26 17:24:11 +03:00
kfree ( inst ) ;
}
static struct crypto_template crypto_gcm_tmpl = {
. name = " gcm " ,
. alloc = crypto_gcm_alloc ,
. free = crypto_gcm_free ,
. module = THIS_MODULE ,
} ;
2007-12-07 15:31:10 +03:00
static struct crypto_instance * crypto_gcm_base_alloc ( struct rtattr * * tb )
{
int err ;
const char * ctr_name ;
2009-08-06 09:34:26 +04:00
const char * ghash_name ;
2007-12-07 15:31:10 +03:00
char full_name [ CRYPTO_MAX_ALG_NAME ] ;
ctr_name = crypto_attr_alg_name ( tb [ 1 ] ) ;
err = PTR_ERR ( ctr_name ) ;
if ( IS_ERR ( ctr_name ) )
return ERR_PTR ( err ) ;
2009-08-06 09:34:26 +04:00
ghash_name = crypto_attr_alg_name ( tb [ 2 ] ) ;
err = PTR_ERR ( ghash_name ) ;
if ( IS_ERR ( ghash_name ) )
return ERR_PTR ( err ) ;
if ( snprintf ( full_name , CRYPTO_MAX_ALG_NAME , " gcm_base(%s,%s) " ,
ctr_name , ghash_name ) > = CRYPTO_MAX_ALG_NAME )
2007-12-07 15:31:10 +03:00
return ERR_PTR ( - ENAMETOOLONG ) ;
2009-08-06 09:34:26 +04:00
return crypto_gcm_alloc_common ( tb , full_name , ctr_name , ghash_name ) ;
2007-12-07 15:31:10 +03:00
}
static struct crypto_template crypto_gcm_base_tmpl = {
. name = " gcm_base " ,
. alloc = crypto_gcm_base_alloc ,
. free = crypto_gcm_free ,
. module = THIS_MODULE ,
} ;
2007-12-17 10:33:17 +03:00
static int crypto_rfc4106_setkey ( struct crypto_aead * parent , const u8 * key ,
unsigned int keylen )
{
struct crypto_rfc4106_ctx * ctx = crypto_aead_ctx ( parent ) ;
struct crypto_aead * child = ctx - > child ;
int err ;
if ( keylen < 4 )
return - EINVAL ;
keylen - = 4 ;
memcpy ( ctx - > nonce , key + keylen , 4 ) ;
crypto_aead_clear_flags ( child , CRYPTO_TFM_REQ_MASK ) ;
crypto_aead_set_flags ( child , crypto_aead_get_flags ( parent ) &
CRYPTO_TFM_REQ_MASK ) ;
err = crypto_aead_setkey ( child , key , keylen ) ;
crypto_aead_set_flags ( parent , crypto_aead_get_flags ( child ) &
CRYPTO_TFM_RES_MASK ) ;
return err ;
}
static int crypto_rfc4106_setauthsize ( struct crypto_aead * parent ,
unsigned int authsize )
{
struct crypto_rfc4106_ctx * ctx = crypto_aead_ctx ( parent ) ;
switch ( authsize ) {
case 8 :
case 12 :
case 16 :
break ;
default :
return - EINVAL ;
}
return crypto_aead_setauthsize ( ctx - > child , authsize ) ;
}
static struct aead_request * crypto_rfc4106_crypt ( struct aead_request * req )
{
struct aead_request * subreq = aead_request_ctx ( req ) ;
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
struct crypto_rfc4106_ctx * ctx = crypto_aead_ctx ( aead ) ;
struct crypto_aead * child = ctx - > child ;
u8 * iv = PTR_ALIGN ( ( u8 * ) ( subreq + 1 ) + crypto_aead_reqsize ( child ) ,
crypto_aead_alignmask ( child ) + 1 ) ;
memcpy ( iv , ctx - > nonce , 4 ) ;
memcpy ( iv + 4 , req - > iv , 8 ) ;
aead_request_set_tfm ( subreq , child ) ;
aead_request_set_callback ( subreq , req - > base . flags , req - > base . complete ,
req - > base . data ) ;
aead_request_set_crypt ( subreq , req - > src , req - > dst , req - > cryptlen , iv ) ;
aead_request_set_assoc ( subreq , req - > assoc , req - > assoclen ) ;
return subreq ;
}
static int crypto_rfc4106_encrypt ( struct aead_request * req )
{
req = crypto_rfc4106_crypt ( req ) ;
return crypto_aead_encrypt ( req ) ;
}
static int crypto_rfc4106_decrypt ( struct aead_request * req )
{
req = crypto_rfc4106_crypt ( req ) ;
return crypto_aead_decrypt ( req ) ;
}
static int crypto_rfc4106_init_tfm ( struct crypto_tfm * tfm )
{
struct crypto_instance * inst = ( void * ) tfm - > __crt_alg ;
struct crypto_aead_spawn * spawn = crypto_instance_ctx ( inst ) ;
struct crypto_rfc4106_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_aead * aead ;
unsigned long align ;
aead = crypto_spawn_aead ( spawn ) ;
if ( IS_ERR ( aead ) )
return PTR_ERR ( aead ) ;
ctx - > child = aead ;
align = crypto_aead_alignmask ( aead ) ;
align & = ~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ;
tfm - > crt_aead . reqsize = sizeof ( struct aead_request ) +
ALIGN ( crypto_aead_reqsize ( aead ) ,
crypto_tfm_ctx_alignment ( ) ) +
align + 16 ;
return 0 ;
}
static void crypto_rfc4106_exit_tfm ( struct crypto_tfm * tfm )
{
struct crypto_rfc4106_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
crypto_free_aead ( ctx - > child ) ;
}
static struct crypto_instance * crypto_rfc4106_alloc ( struct rtattr * * tb )
{
struct crypto_attr_type * algt ;
struct crypto_instance * inst ;
struct crypto_aead_spawn * spawn ;
struct crypto_alg * alg ;
const char * ccm_name ;
int err ;
algt = crypto_get_attr_type ( tb ) ;
err = PTR_ERR ( algt ) ;
if ( IS_ERR ( algt ) )
return ERR_PTR ( err ) ;
if ( ( algt - > type ^ CRYPTO_ALG_TYPE_AEAD ) & algt - > mask )
return ERR_PTR ( - EINVAL ) ;
ccm_name = crypto_attr_alg_name ( tb [ 1 ] ) ;
err = PTR_ERR ( ccm_name ) ;
if ( IS_ERR ( ccm_name ) )
return ERR_PTR ( err ) ;
inst = kzalloc ( sizeof ( * inst ) + sizeof ( * spawn ) , GFP_KERNEL ) ;
if ( ! inst )
return ERR_PTR ( - ENOMEM ) ;
spawn = crypto_instance_ctx ( inst ) ;
crypto_set_aead_spawn ( spawn , inst ) ;
err = crypto_grab_aead ( spawn , ccm_name , 0 ,
crypto_requires_sync ( algt - > type , algt - > mask ) ) ;
if ( err )
goto out_free_inst ;
alg = crypto_aead_spawn_alg ( spawn ) ;
err = - EINVAL ;
/* We only support 16-byte blocks. */
if ( alg - > cra_aead . ivsize ! = 16 )
goto out_drop_alg ;
/* Not a stream cipher? */
if ( alg - > cra_blocksize ! = 1 )
goto out_drop_alg ;
err = - ENAMETOOLONG ;
if ( snprintf ( inst - > alg . cra_name , CRYPTO_MAX_ALG_NAME ,
" rfc4106(%s) " , alg - > cra_name ) > = CRYPTO_MAX_ALG_NAME | |
snprintf ( inst - > alg . cra_driver_name , CRYPTO_MAX_ALG_NAME ,
" rfc4106(%s) " , alg - > cra_driver_name ) > =
CRYPTO_MAX_ALG_NAME )
goto out_drop_alg ;
inst - > alg . cra_flags = CRYPTO_ALG_TYPE_AEAD ;
inst - > alg . cra_flags | = alg - > cra_flags & CRYPTO_ALG_ASYNC ;
inst - > alg . cra_priority = alg - > cra_priority ;
inst - > alg . cra_blocksize = 1 ;
inst - > alg . cra_alignmask = alg - > cra_alignmask ;
inst - > alg . cra_type = & crypto_nivaead_type ;
inst - > alg . cra_aead . ivsize = 8 ;
inst - > alg . cra_aead . maxauthsize = 16 ;
inst - > alg . cra_ctxsize = sizeof ( struct crypto_rfc4106_ctx ) ;
inst - > alg . cra_init = crypto_rfc4106_init_tfm ;
inst - > alg . cra_exit = crypto_rfc4106_exit_tfm ;
inst - > alg . cra_aead . setkey = crypto_rfc4106_setkey ;
inst - > alg . cra_aead . setauthsize = crypto_rfc4106_setauthsize ;
inst - > alg . cra_aead . encrypt = crypto_rfc4106_encrypt ;
inst - > alg . cra_aead . decrypt = crypto_rfc4106_decrypt ;
inst - > alg . cra_aead . geniv = " seqiv " ;
out :
return inst ;
out_drop_alg :
crypto_drop_aead ( spawn ) ;
out_free_inst :
kfree ( inst ) ;
inst = ERR_PTR ( err ) ;
goto out ;
}
static void crypto_rfc4106_free ( struct crypto_instance * inst )
{
crypto_drop_spawn ( crypto_instance_ctx ( inst ) ) ;
kfree ( inst ) ;
}
static struct crypto_template crypto_rfc4106_tmpl = {
. name = " rfc4106 " ,
. alloc = crypto_rfc4106_alloc ,
. free = crypto_rfc4106_free ,
. module = THIS_MODULE ,
} ;
2010-01-17 13:52:11 +03:00
static inline struct crypto_rfc4543_req_ctx * crypto_rfc4543_reqctx (
struct aead_request * req )
{
unsigned long align = crypto_aead_alignmask ( crypto_aead_reqtfm ( req ) ) ;
return ( void * ) PTR_ALIGN ( ( u8 * ) aead_request_ctx ( req ) , align + 1 ) ;
}
static int crypto_rfc4543_setkey ( struct crypto_aead * parent , const u8 * key ,
unsigned int keylen )
{
struct crypto_rfc4543_ctx * ctx = crypto_aead_ctx ( parent ) ;
struct crypto_aead * child = ctx - > child ;
int err ;
if ( keylen < 4 )
return - EINVAL ;
keylen - = 4 ;
memcpy ( ctx - > nonce , key + keylen , 4 ) ;
crypto_aead_clear_flags ( child , CRYPTO_TFM_REQ_MASK ) ;
crypto_aead_set_flags ( child , crypto_aead_get_flags ( parent ) &
CRYPTO_TFM_REQ_MASK ) ;
err = crypto_aead_setkey ( child , key , keylen ) ;
crypto_aead_set_flags ( parent , crypto_aead_get_flags ( child ) &
CRYPTO_TFM_RES_MASK ) ;
return err ;
}
static int crypto_rfc4543_setauthsize ( struct crypto_aead * parent ,
unsigned int authsize )
{
struct crypto_rfc4543_ctx * ctx = crypto_aead_ctx ( parent ) ;
if ( authsize ! = 16 )
return - EINVAL ;
return crypto_aead_setauthsize ( ctx - > child , authsize ) ;
}
/* this is the same as crypto_authenc_chain */
static void crypto_rfc4543_chain ( struct scatterlist * head ,
struct scatterlist * sg , int chain )
{
if ( chain ) {
head - > length + = sg - > length ;
sg = scatterwalk_sg_next ( sg ) ;
}
if ( sg )
scatterwalk_sg_chain ( head , 2 , sg ) ;
else
sg_mark_end ( head ) ;
}
static struct aead_request * crypto_rfc4543_crypt ( struct aead_request * req ,
int enc )
{
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
struct crypto_rfc4543_ctx * ctx = crypto_aead_ctx ( aead ) ;
struct crypto_rfc4543_req_ctx * rctx = crypto_rfc4543_reqctx ( req ) ;
struct aead_request * subreq = & rctx - > subreq ;
struct scatterlist * dst = req - > dst ;
struct scatterlist * cipher = rctx - > cipher ;
struct scatterlist * payload = rctx - > payload ;
struct scatterlist * assoc = rctx - > assoc ;
unsigned int authsize = crypto_aead_authsize ( aead ) ;
unsigned int assoclen = req - > assoclen ;
struct page * dstp ;
u8 * vdst ;
u8 * iv = PTR_ALIGN ( ( u8 * ) ( rctx + 1 ) + crypto_aead_reqsize ( ctx - > child ) ,
crypto_aead_alignmask ( ctx - > child ) + 1 ) ;
memcpy ( iv , ctx - > nonce , 4 ) ;
memcpy ( iv + 4 , req - > iv , 8 ) ;
/* construct cipher/plaintext */
if ( enc )
memset ( rctx - > auth_tag , 0 , authsize ) ;
else
scatterwalk_map_and_copy ( rctx - > auth_tag , dst ,
req - > cryptlen - authsize ,
authsize , 0 ) ;
sg_init_one ( cipher , rctx - > auth_tag , authsize ) ;
/* construct the aad */
dstp = sg_page ( dst ) ;
vdst = PageHighMem ( dstp ) ? NULL : page_address ( dstp ) + dst - > offset ;
sg_init_table ( payload , 2 ) ;
sg_set_buf ( payload , req - > iv , 8 ) ;
crypto_rfc4543_chain ( payload , dst , vdst = = req - > iv + 8 ) ;
assoclen + = 8 + req - > cryptlen - ( enc ? 0 : authsize ) ;
sg_init_table ( assoc , 2 ) ;
sg_set_page ( assoc , sg_page ( req - > assoc ) , req - > assoc - > length ,
req - > assoc - > offset ) ;
crypto_rfc4543_chain ( assoc , payload , 0 ) ;
aead_request_set_tfm ( subreq , ctx - > child ) ;
aead_request_set_callback ( subreq , req - > base . flags , req - > base . complete ,
req - > base . data ) ;
aead_request_set_crypt ( subreq , cipher , cipher , enc ? 0 : authsize , iv ) ;
aead_request_set_assoc ( subreq , assoc , assoclen ) ;
return subreq ;
}
static int crypto_rfc4543_encrypt ( struct aead_request * req )
{
struct crypto_aead * aead = crypto_aead_reqtfm ( req ) ;
struct crypto_rfc4543_req_ctx * rctx = crypto_rfc4543_reqctx ( req ) ;
struct aead_request * subreq ;
int err ;
subreq = crypto_rfc4543_crypt ( req , 1 ) ;
err = crypto_aead_encrypt ( subreq ) ;
if ( err )
return err ;
scatterwalk_map_and_copy ( rctx - > auth_tag , req - > dst , req - > cryptlen ,
crypto_aead_authsize ( aead ) , 1 ) ;
return 0 ;
}
static int crypto_rfc4543_decrypt ( struct aead_request * req )
{
req = crypto_rfc4543_crypt ( req , 0 ) ;
return crypto_aead_decrypt ( req ) ;
}
static int crypto_rfc4543_init_tfm ( struct crypto_tfm * tfm )
{
struct crypto_instance * inst = ( void * ) tfm - > __crt_alg ;
struct crypto_aead_spawn * spawn = crypto_instance_ctx ( inst ) ;
struct crypto_rfc4543_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_aead * aead ;
unsigned long align ;
aead = crypto_spawn_aead ( spawn ) ;
if ( IS_ERR ( aead ) )
return PTR_ERR ( aead ) ;
ctx - > child = aead ;
align = crypto_aead_alignmask ( aead ) ;
align & = ~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ;
tfm - > crt_aead . reqsize = sizeof ( struct crypto_rfc4543_req_ctx ) +
ALIGN ( crypto_aead_reqsize ( aead ) ,
crypto_tfm_ctx_alignment ( ) ) +
align + 16 ;
return 0 ;
}
static void crypto_rfc4543_exit_tfm ( struct crypto_tfm * tfm )
{
struct crypto_rfc4543_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
crypto_free_aead ( ctx - > child ) ;
}
static struct crypto_instance * crypto_rfc4543_alloc ( struct rtattr * * tb )
{
struct crypto_attr_type * algt ;
struct crypto_instance * inst ;
struct crypto_aead_spawn * spawn ;
struct crypto_alg * alg ;
const char * ccm_name ;
int err ;
algt = crypto_get_attr_type ( tb ) ;
err = PTR_ERR ( algt ) ;
if ( IS_ERR ( algt ) )
return ERR_PTR ( err ) ;
if ( ( algt - > type ^ CRYPTO_ALG_TYPE_AEAD ) & algt - > mask )
return ERR_PTR ( - EINVAL ) ;
ccm_name = crypto_attr_alg_name ( tb [ 1 ] ) ;
err = PTR_ERR ( ccm_name ) ;
if ( IS_ERR ( ccm_name ) )
return ERR_PTR ( err ) ;
inst = kzalloc ( sizeof ( * inst ) + sizeof ( * spawn ) , GFP_KERNEL ) ;
if ( ! inst )
return ERR_PTR ( - ENOMEM ) ;
spawn = crypto_instance_ctx ( inst ) ;
crypto_set_aead_spawn ( spawn , inst ) ;
err = crypto_grab_aead ( spawn , ccm_name , 0 ,
crypto_requires_sync ( algt - > type , algt - > mask ) ) ;
if ( err )
goto out_free_inst ;
alg = crypto_aead_spawn_alg ( spawn ) ;
err = - EINVAL ;
/* We only support 16-byte blocks. */
if ( alg - > cra_aead . ivsize ! = 16 )
goto out_drop_alg ;
/* Not a stream cipher? */
if ( alg - > cra_blocksize ! = 1 )
goto out_drop_alg ;
err = - ENAMETOOLONG ;
if ( snprintf ( inst - > alg . cra_name , CRYPTO_MAX_ALG_NAME ,
" rfc4543(%s) " , alg - > cra_name ) > = CRYPTO_MAX_ALG_NAME | |
snprintf ( inst - > alg . cra_driver_name , CRYPTO_MAX_ALG_NAME ,
" rfc4543(%s) " , alg - > cra_driver_name ) > =
CRYPTO_MAX_ALG_NAME )
goto out_drop_alg ;
inst - > alg . cra_flags = CRYPTO_ALG_TYPE_AEAD ;
inst - > alg . cra_flags | = alg - > cra_flags & CRYPTO_ALG_ASYNC ;
inst - > alg . cra_priority = alg - > cra_priority ;
inst - > alg . cra_blocksize = 1 ;
inst - > alg . cra_alignmask = alg - > cra_alignmask ;
inst - > alg . cra_type = & crypto_nivaead_type ;
inst - > alg . cra_aead . ivsize = 8 ;
inst - > alg . cra_aead . maxauthsize = 16 ;
inst - > alg . cra_ctxsize = sizeof ( struct crypto_rfc4543_ctx ) ;
inst - > alg . cra_init = crypto_rfc4543_init_tfm ;
inst - > alg . cra_exit = crypto_rfc4543_exit_tfm ;
inst - > alg . cra_aead . setkey = crypto_rfc4543_setkey ;
inst - > alg . cra_aead . setauthsize = crypto_rfc4543_setauthsize ;
inst - > alg . cra_aead . encrypt = crypto_rfc4543_encrypt ;
inst - > alg . cra_aead . decrypt = crypto_rfc4543_decrypt ;
inst - > alg . cra_aead . geniv = " seqiv " ;
out :
return inst ;
out_drop_alg :
crypto_drop_aead ( spawn ) ;
out_free_inst :
kfree ( inst ) ;
inst = ERR_PTR ( err ) ;
goto out ;
}
static void crypto_rfc4543_free ( struct crypto_instance * inst )
{
crypto_drop_spawn ( crypto_instance_ctx ( inst ) ) ;
kfree ( inst ) ;
}
static struct crypto_template crypto_rfc4543_tmpl = {
. name = " rfc4543 " ,
. alloc = crypto_rfc4543_alloc ,
. free = crypto_rfc4543_free ,
. module = THIS_MODULE ,
} ;
2007-11-26 17:24:11 +03:00
static int __init crypto_gcm_module_init ( void )
{
2007-12-07 15:31:10 +03:00
int err ;
2009-08-06 09:34:26 +04:00
gcm_zeroes = kzalloc ( 16 , GFP_KERNEL ) ;
if ( ! gcm_zeroes )
return - ENOMEM ;
2007-12-07 15:31:10 +03:00
err = crypto_register_template ( & crypto_gcm_base_tmpl ) ;
if ( err )
goto out ;
err = crypto_register_template ( & crypto_gcm_tmpl ) ;
if ( err )
goto out_undo_base ;
2007-12-17 10:33:17 +03:00
err = crypto_register_template ( & crypto_rfc4106_tmpl ) ;
if ( err )
goto out_undo_gcm ;
2010-01-17 13:52:11 +03:00
err = crypto_register_template ( & crypto_rfc4543_tmpl ) ;
if ( err )
goto out_undo_rfc4106 ;
2009-08-06 09:34:26 +04:00
return 0 ;
2007-12-07 15:31:10 +03:00
2010-01-17 13:52:11 +03:00
out_undo_rfc4106 :
crypto_unregister_template ( & crypto_rfc4106_tmpl ) ;
2007-12-17 10:33:17 +03:00
out_undo_gcm :
crypto_unregister_template ( & crypto_gcm_tmpl ) ;
2007-12-07 15:31:10 +03:00
out_undo_base :
crypto_unregister_template ( & crypto_gcm_base_tmpl ) ;
2009-08-06 09:34:26 +04:00
out :
kfree ( gcm_zeroes ) ;
return err ;
2007-11-26 17:24:11 +03:00
}
static void __exit crypto_gcm_module_exit ( void )
{
2009-08-06 09:34:26 +04:00
kfree ( gcm_zeroes ) ;
2010-01-17 13:52:11 +03:00
crypto_unregister_template ( & crypto_rfc4543_tmpl ) ;
2007-12-17 10:33:17 +03:00
crypto_unregister_template ( & crypto_rfc4106_tmpl ) ;
2007-11-26 17:24:11 +03:00
crypto_unregister_template ( & crypto_gcm_tmpl ) ;
2007-12-07 15:31:10 +03:00
crypto_unregister_template ( & crypto_gcm_base_tmpl ) ;
2007-11-26 17:24:11 +03:00
}
module_init ( crypto_gcm_module_init ) ;
module_exit ( crypto_gcm_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Galois/Counter Mode " ) ;
MODULE_AUTHOR ( " Mikko Herranen <mh1@iki.fi> " ) ;
2007-12-07 15:31:10 +03:00
MODULE_ALIAS ( " gcm_base " ) ;
2007-12-17 10:33:17 +03:00
MODULE_ALIAS ( " rfc4106 " ) ;
2010-01-17 13:52:11 +03:00
MODULE_ALIAS ( " rfc4543 " ) ;