2008-03-24 16:26:16 +03:00
/*
* CTS : Cipher Text Stealing mode
*
* COPYRIGHT ( c ) 2008
* The Regents of the University of Michigan
* ALL RIGHTS RESERVED
*
* Permission is granted to use , copy , create derivative works
* and redistribute this software and such derivative works
* for any purpose , so long as the name of The University of
* Michigan is not used in any advertising or publicity
* pertaining to the use of distribution of this software
* without specific , written prior authorization . If the
* above copyright notice or any other identification of the
* University of Michigan is included in any copy of any
* portion of this software , then the disclaimer below must
* also be included .
*
* THIS SOFTWARE IS PROVIDED AS IS , WITHOUT REPRESENTATION
* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
* PURPOSE , AND WITHOUT WARRANTY BY THE UNIVERSITY OF
* MICHIGAN OF ANY KIND , EITHER EXPRESS OR IMPLIED , INCLUDING
* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE . THE
* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
* FOR ANY DAMAGES , INCLUDING SPECIAL , INDIRECT , INCIDENTAL , OR
* CONSEQUENTIAL DAMAGES , WITH RESPECT TO ANY CLAIM ARISING
* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE , EVEN
* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGES .
*/
/* Derived from various:
* Copyright ( c ) 2006 Herbert Xu < herbert @ gondor . apana . org . au >
*/
/*
* This is the Cipher Text Stealing mode as described by
* Section 8 of rfc2040 and referenced by rfc3962 .
* rfc3962 includes errata information in its Appendix A .
*/
# include <crypto/algapi.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/log2.h>
# include <linux/module.h>
# include <linux/scatterlist.h>
# include <crypto/scatterwalk.h>
# include <linux/slab.h>
struct crypto_cts_ctx {
struct crypto_blkcipher * child ;
} ;
static int crypto_cts_setkey ( struct crypto_tfm * parent , const u8 * key ,
unsigned int keylen )
{
struct crypto_cts_ctx * ctx = crypto_tfm_ctx ( parent ) ;
struct crypto_blkcipher * child = ctx - > child ;
int err ;
crypto_blkcipher_clear_flags ( child , CRYPTO_TFM_REQ_MASK ) ;
crypto_blkcipher_set_flags ( child , crypto_tfm_get_flags ( parent ) &
CRYPTO_TFM_REQ_MASK ) ;
err = crypto_blkcipher_setkey ( child , key , keylen ) ;
crypto_tfm_set_flags ( parent , crypto_blkcipher_get_flags ( child ) &
CRYPTO_TFM_RES_MASK ) ;
return err ;
}
static int cts_cbc_encrypt ( struct crypto_cts_ctx * ctx ,
struct blkcipher_desc * desc ,
struct scatterlist * dst ,
struct scatterlist * src ,
unsigned int offset ,
unsigned int nbytes )
{
int bsize = crypto_blkcipher_blocksize ( desc - > tfm ) ;
u8 tmp [ bsize ] , tmp2 [ bsize ] ;
struct blkcipher_desc lcldesc ;
struct scatterlist sgsrc [ 1 ] , sgdst [ 1 ] ;
int lastn = nbytes - bsize ;
u8 iv [ bsize ] ;
u8 s [ bsize * 2 ] , d [ bsize * 2 ] ;
int err ;
if ( lastn < 0 )
return - EINVAL ;
2008-06-02 09:46:51 +04:00
sg_init_table ( sgsrc , 1 ) ;
sg_init_table ( sgdst , 1 ) ;
2008-03-24 16:26:16 +03:00
memset ( s , 0 , sizeof ( s ) ) ;
scatterwalk_map_and_copy ( s , src , offset , nbytes , 0 ) ;
memcpy ( iv , desc - > info , bsize ) ;
lcldesc . tfm = ctx - > child ;
lcldesc . info = iv ;
lcldesc . flags = desc - > flags ;
sg_set_buf ( & sgsrc [ 0 ] , s , bsize ) ;
sg_set_buf ( & sgdst [ 0 ] , tmp , bsize ) ;
err = crypto_blkcipher_encrypt_iv ( & lcldesc , sgdst , sgsrc , bsize ) ;
memcpy ( d + bsize , tmp , lastn ) ;
lcldesc . info = tmp ;
sg_set_buf ( & sgsrc [ 0 ] , s + bsize , bsize ) ;
sg_set_buf ( & sgdst [ 0 ] , tmp2 , bsize ) ;
err = crypto_blkcipher_encrypt_iv ( & lcldesc , sgdst , sgsrc , bsize ) ;
memcpy ( d , tmp2 , bsize ) ;
scatterwalk_map_and_copy ( d , dst , offset , nbytes , 1 ) ;
memcpy ( desc - > info , tmp2 , bsize ) ;
return err ;
}
static int crypto_cts_encrypt ( struct blkcipher_desc * desc ,
struct scatterlist * dst , struct scatterlist * src ,
unsigned int nbytes )
{
struct crypto_cts_ctx * ctx = crypto_blkcipher_ctx ( desc - > tfm ) ;
int bsize = crypto_blkcipher_blocksize ( desc - > tfm ) ;
int tot_blocks = ( nbytes + bsize - 1 ) / bsize ;
int cbc_blocks = tot_blocks > 2 ? tot_blocks - 2 : 0 ;
struct blkcipher_desc lcldesc ;
int err ;
lcldesc . tfm = ctx - > child ;
lcldesc . info = desc - > info ;
lcldesc . flags = desc - > flags ;
if ( tot_blocks = = 1 ) {
err = crypto_blkcipher_encrypt_iv ( & lcldesc , dst , src , bsize ) ;
} else if ( nbytes < = bsize * 2 ) {
err = cts_cbc_encrypt ( ctx , desc , dst , src , 0 , nbytes ) ;
} else {
/* do normal function for tot_blocks - 2 */
err = crypto_blkcipher_encrypt_iv ( & lcldesc , dst , src ,
cbc_blocks * bsize ) ;
if ( err = = 0 ) {
/* do cts for final two blocks */
err = cts_cbc_encrypt ( ctx , desc , dst , src ,
cbc_blocks * bsize ,
nbytes - ( cbc_blocks * bsize ) ) ;
}
}
return err ;
}
static int cts_cbc_decrypt ( struct crypto_cts_ctx * ctx ,
struct blkcipher_desc * desc ,
struct scatterlist * dst ,
struct scatterlist * src ,
unsigned int offset ,
unsigned int nbytes )
{
int bsize = crypto_blkcipher_blocksize ( desc - > tfm ) ;
u8 tmp [ bsize ] ;
struct blkcipher_desc lcldesc ;
struct scatterlist sgsrc [ 1 ] , sgdst [ 1 ] ;
int lastn = nbytes - bsize ;
u8 iv [ bsize ] ;
u8 s [ bsize * 2 ] , d [ bsize * 2 ] ;
int err ;
if ( lastn < 0 )
return - EINVAL ;
2008-06-02 09:46:51 +04:00
sg_init_table ( sgsrc , 1 ) ;
sg_init_table ( sgdst , 1 ) ;
2008-03-24 16:26:16 +03:00
scatterwalk_map_and_copy ( s , src , offset , nbytes , 0 ) ;
lcldesc . tfm = ctx - > child ;
lcldesc . info = iv ;
lcldesc . flags = desc - > flags ;
/* 1. Decrypt Cn-1 (s) to create Dn (tmp)*/
memset ( iv , 0 , sizeof ( iv ) ) ;
sg_set_buf ( & sgsrc [ 0 ] , s , bsize ) ;
sg_set_buf ( & sgdst [ 0 ] , tmp , bsize ) ;
err = crypto_blkcipher_decrypt_iv ( & lcldesc , sgdst , sgsrc , bsize ) ;
if ( err )
return err ;
/* 2. Pad Cn with zeros at the end to create C of length BB */
memset ( iv , 0 , sizeof ( iv ) ) ;
memcpy ( iv , s + bsize , lastn ) ;
/* 3. Exclusive-or Dn (tmp) with C (iv) to create Xn (tmp) */
crypto_xor ( tmp , iv , bsize ) ;
/* 4. Select the first Ln bytes of Xn (tmp) to create Pn */
memcpy ( d + bsize , tmp , lastn ) ;
/* 5. Append the tail (BB - Ln) bytes of Xn (tmp) to Cn to create En */
memcpy ( s + bsize + lastn , tmp + lastn , bsize - lastn ) ;
/* 6. Decrypt En to create Pn-1 */
memset ( iv , 0 , sizeof ( iv ) ) ;
sg_set_buf ( & sgsrc [ 0 ] , s + bsize , bsize ) ;
sg_set_buf ( & sgdst [ 0 ] , d , bsize ) ;
err = crypto_blkcipher_decrypt_iv ( & lcldesc , sgdst , sgsrc , bsize ) ;
/* XOR with previous block */
crypto_xor ( d , desc - > info , bsize ) ;
scatterwalk_map_and_copy ( d , dst , offset , nbytes , 1 ) ;
memcpy ( desc - > info , s , bsize ) ;
return err ;
}
static int crypto_cts_decrypt ( struct blkcipher_desc * desc ,
struct scatterlist * dst , struct scatterlist * src ,
unsigned int nbytes )
{
struct crypto_cts_ctx * ctx = crypto_blkcipher_ctx ( desc - > tfm ) ;
int bsize = crypto_blkcipher_blocksize ( desc - > tfm ) ;
int tot_blocks = ( nbytes + bsize - 1 ) / bsize ;
int cbc_blocks = tot_blocks > 2 ? tot_blocks - 2 : 0 ;
struct blkcipher_desc lcldesc ;
int err ;
lcldesc . tfm = ctx - > child ;
lcldesc . info = desc - > info ;
lcldesc . flags = desc - > flags ;
if ( tot_blocks = = 1 ) {
err = crypto_blkcipher_decrypt_iv ( & lcldesc , dst , src , bsize ) ;
} else if ( nbytes < = bsize * 2 ) {
err = cts_cbc_decrypt ( ctx , desc , dst , src , 0 , nbytes ) ;
} else {
/* do normal function for tot_blocks - 2 */
err = crypto_blkcipher_decrypt_iv ( & lcldesc , dst , src ,
cbc_blocks * bsize ) ;
if ( err = = 0 ) {
/* do cts for final two blocks */
err = cts_cbc_decrypt ( ctx , desc , dst , src ,
cbc_blocks * bsize ,
nbytes - ( cbc_blocks * bsize ) ) ;
}
}
return err ;
}
static int crypto_cts_init_tfm ( struct crypto_tfm * tfm )
{
struct crypto_instance * inst = ( void * ) tfm - > __crt_alg ;
struct crypto_spawn * spawn = crypto_instance_ctx ( inst ) ;
struct crypto_cts_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_blkcipher * cipher ;
cipher = crypto_spawn_blkcipher ( spawn ) ;
if ( IS_ERR ( cipher ) )
return PTR_ERR ( cipher ) ;
ctx - > child = cipher ;
return 0 ;
}
static void crypto_cts_exit_tfm ( struct crypto_tfm * tfm )
{
struct crypto_cts_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
crypto_free_blkcipher ( ctx - > child ) ;
}
static struct crypto_instance * crypto_cts_alloc ( struct rtattr * * tb )
{
struct crypto_instance * inst ;
struct crypto_alg * alg ;
int err ;
err = crypto_check_attr_type ( tb , CRYPTO_ALG_TYPE_BLKCIPHER ) ;
if ( err )
return ERR_PTR ( err ) ;
alg = crypto_attr_alg ( tb [ 1 ] , CRYPTO_ALG_TYPE_BLKCIPHER ,
CRYPTO_ALG_TYPE_MASK ) ;
err = PTR_ERR ( alg ) ;
if ( IS_ERR ( alg ) )
return ERR_PTR ( err ) ;
inst = ERR_PTR ( - EINVAL ) ;
if ( ! is_power_of_2 ( alg - > cra_blocksize ) )
goto out_put_alg ;
inst = crypto_alloc_instance ( " cts " , alg ) ;
if ( IS_ERR ( inst ) )
goto out_put_alg ;
inst - > alg . cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER ;
inst - > alg . cra_priority = alg - > cra_priority ;
inst - > alg . cra_blocksize = alg - > cra_blocksize ;
inst - > alg . cra_alignmask = alg - > cra_alignmask ;
inst - > alg . cra_type = & crypto_blkcipher_type ;
/* We access the data as u32s when xoring. */
inst - > alg . cra_alignmask | = __alignof__ ( u32 ) - 1 ;
inst - > alg . cra_blkcipher . ivsize = alg - > cra_blocksize ;
inst - > alg . cra_blkcipher . min_keysize = alg - > cra_blkcipher . min_keysize ;
inst - > alg . cra_blkcipher . max_keysize = alg - > cra_blkcipher . max_keysize ;
inst - > alg . cra_blkcipher . geniv = " seqiv " ;
inst - > alg . cra_ctxsize = sizeof ( struct crypto_cts_ctx ) ;
inst - > alg . cra_init = crypto_cts_init_tfm ;
inst - > alg . cra_exit = crypto_cts_exit_tfm ;
inst - > alg . cra_blkcipher . setkey = crypto_cts_setkey ;
inst - > alg . cra_blkcipher . encrypt = crypto_cts_encrypt ;
inst - > alg . cra_blkcipher . decrypt = crypto_cts_decrypt ;
out_put_alg :
crypto_mod_put ( alg ) ;
return inst ;
}
static void crypto_cts_free ( struct crypto_instance * inst )
{
crypto_drop_spawn ( crypto_instance_ctx ( inst ) ) ;
kfree ( inst ) ;
}
static struct crypto_template crypto_cts_tmpl = {
. name = " cts " ,
. alloc = crypto_cts_alloc ,
. free = crypto_cts_free ,
. module = THIS_MODULE ,
} ;
static int __init crypto_cts_module_init ( void )
{
return crypto_register_template ( & crypto_cts_tmpl ) ;
}
static void __exit crypto_cts_module_exit ( void )
{
crypto_unregister_template ( & crypto_cts_tmpl ) ;
}
module_init ( crypto_cts_module_init ) ;
module_exit ( crypto_cts_module_exit ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_DESCRIPTION ( " CTS-CBC CipherText Stealing for CBC " ) ;