2015-06-01 14:44:00 +03:00
/*
* ChaCha20 - Poly1305 AEAD , RFC7539
*
* Copyright ( C ) 2015 Martin Willi
*
* 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 .
*/
# include <crypto/internal/aead.h>
# include <crypto/internal/hash.h>
# include <crypto/internal/skcipher.h>
# include <crypto/scatterwalk.h>
2015-07-16 20:14:00 +03:00
# include <crypto/chacha20.h>
2015-07-16 20:14:05 +03:00
# include <crypto/poly1305.h>
2015-06-01 14:44:00 +03:00
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include "internal.h"
# define CHACHAPOLY_IV_SIZE 12
struct chachapoly_instance_ctx {
struct crypto_skcipher_spawn chacha ;
struct crypto_ahash_spawn poly ;
unsigned int saltlen ;
} ;
struct chachapoly_ctx {
2016-07-12 08:17:40 +03:00
struct crypto_skcipher * chacha ;
2015-06-01 14:44:00 +03:00
struct crypto_ahash * poly ;
/* key bytes we use for the ChaCha20 IV */
unsigned int saltlen ;
u8 salt [ ] ;
} ;
struct poly_req {
/* zero byte padding for AD/ciphertext, as needed */
u8 pad [ POLY1305_BLOCK_SIZE ] ;
/* tail data with AD/ciphertext lengths */
struct {
__le64 assoclen ;
__le64 cryptlen ;
} tail ;
struct scatterlist src [ 1 ] ;
struct ahash_request req ; /* must be last member */
} ;
struct chacha_req {
u8 iv [ CHACHA20_IV_SIZE ] ;
struct scatterlist src [ 1 ] ;
2016-07-12 08:17:40 +03:00
struct skcipher_request req ; /* must be last member */
2015-06-01 14:44:00 +03:00
} ;
struct chachapoly_req_ctx {
2015-07-16 07:35:08 +03:00
struct scatterlist src [ 2 ] ;
struct scatterlist dst [ 2 ] ;
2015-06-16 12:34:16 +03:00
/* the key we generate for Poly1305 using Chacha20 */
u8 key [ POLY1305_KEY_SIZE ] ;
2015-06-01 14:44:00 +03:00
/* calculated Poly1305 tag */
u8 tag [ POLY1305_DIGEST_SIZE ] ;
/* length of data to en/decrypt, without ICV */
unsigned int cryptlen ;
2015-07-16 07:35:08 +03:00
/* Actual AD, excluding IV */
unsigned int assoclen ;
2015-06-01 14:44:00 +03:00
union {
struct poly_req poly ;
struct chacha_req chacha ;
} u ;
} ;
static inline void async_done_continue ( struct aead_request * req , int err ,
int ( * cont ) ( struct aead_request * ) )
{
if ( ! err )
err = cont ( req ) ;
if ( err ! = - EINPROGRESS & & err ! = - EBUSY )
aead_request_complete ( req , err ) ;
}
static void chacha_iv ( u8 * iv , struct aead_request * req , u32 icb )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
__le32 leicb = cpu_to_le32 ( icb ) ;
memcpy ( iv , & leicb , sizeof ( leicb ) ) ;
memcpy ( iv + sizeof ( leicb ) , ctx - > salt , ctx - > saltlen ) ;
memcpy ( iv + sizeof ( leicb ) + ctx - > saltlen , req - > iv ,
CHACHA20_IV_SIZE - sizeof ( leicb ) - ctx - > saltlen ) ;
}
static int poly_verify_tag ( struct aead_request * req )
{
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
u8 tag [ sizeof ( rctx - > tag ) ] ;
2015-07-16 07:35:08 +03:00
scatterwalk_map_and_copy ( tag , req - > src ,
req - > assoclen + rctx - > cryptlen ,
sizeof ( tag ) , 0 ) ;
2015-06-01 14:44:00 +03:00
if ( crypto_memneq ( tag , rctx - > tag , sizeof ( tag ) ) )
return - EBADMSG ;
return 0 ;
}
static int poly_copy_tag ( struct aead_request * req )
{
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
2015-07-16 07:35:08 +03:00
scatterwalk_map_and_copy ( rctx - > tag , req - > dst ,
req - > assoclen + rctx - > cryptlen ,
2015-06-01 14:44:00 +03:00
sizeof ( rctx - > tag ) , 1 ) ;
return 0 ;
}
static void chacha_decrypt_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_verify_tag ) ;
}
static int chacha_decrypt ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct chacha_req * creq = & rctx - > u . chacha ;
2015-07-16 07:35:08 +03:00
struct scatterlist * src , * dst ;
2015-06-01 14:44:00 +03:00
int err ;
2015-12-06 04:51:38 +03:00
if ( rctx - > cryptlen = = 0 )
goto skip ;
2015-06-01 14:44:00 +03:00
chacha_iv ( creq - > iv , req , 1 ) ;
2015-07-16 07:35:08 +03:00
sg_init_table ( rctx - > src , 2 ) ;
src = scatterwalk_ffwd ( rctx - > src , req - > src , req - > assoclen ) ;
dst = src ;
if ( req - > src ! = req - > dst ) {
sg_init_table ( rctx - > dst , 2 ) ;
dst = scatterwalk_ffwd ( rctx - > dst , req - > dst , req - > assoclen ) ;
}
2016-07-12 08:17:40 +03:00
skcipher_request_set_callback ( & creq - > req , aead_request_flags ( req ) ,
chacha_decrypt_done , req ) ;
skcipher_request_set_tfm ( & creq - > req , ctx - > chacha ) ;
skcipher_request_set_crypt ( & creq - > req , src , dst ,
rctx - > cryptlen , creq - > iv ) ;
err = crypto_skcipher_decrypt ( & creq - > req ) ;
2015-06-01 14:44:00 +03:00
if ( err )
return err ;
2015-12-06 04:51:38 +03:00
skip :
2015-06-01 14:44:00 +03:00
return poly_verify_tag ( req ) ;
}
static int poly_tail_continue ( struct aead_request * req )
{
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
if ( rctx - > cryptlen = = req - > cryptlen ) /* encrypting */
return poly_copy_tag ( req ) ;
return chacha_decrypt ( req ) ;
}
static void poly_tail_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_tail_continue ) ;
}
static int poly_tail ( struct aead_request * req )
{
2015-07-16 07:35:08 +03:00
struct crypto_aead * tfm = crypto_aead_reqtfm ( req ) ;
struct chachapoly_ctx * ctx = crypto_aead_ctx ( tfm ) ;
2015-06-01 14:44:00 +03:00
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct poly_req * preq = & rctx - > u . poly ;
__le64 len ;
int err ;
sg_init_table ( preq - > src , 1 ) ;
2015-07-16 07:35:08 +03:00
len = cpu_to_le64 ( rctx - > assoclen ) ;
2015-06-01 14:44:00 +03:00
memcpy ( & preq - > tail . assoclen , & len , sizeof ( len ) ) ;
len = cpu_to_le64 ( rctx - > cryptlen ) ;
memcpy ( & preq - > tail . cryptlen , & len , sizeof ( len ) ) ;
sg_set_buf ( preq - > src , & preq - > tail , sizeof ( preq - > tail ) ) ;
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
poly_tail_done , req ) ;
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
ahash_request_set_crypt ( & preq - > req , preq - > src ,
rctx - > tag , sizeof ( preq - > tail ) ) ;
err = crypto_ahash_finup ( & preq - > req ) ;
if ( err )
return err ;
return poly_tail_continue ( req ) ;
}
static void poly_cipherpad_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_tail ) ;
}
static int poly_cipherpad ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct poly_req * preq = & rctx - > u . poly ;
unsigned int padlen , bs = POLY1305_BLOCK_SIZE ;
int err ;
padlen = ( bs - ( rctx - > cryptlen % bs ) ) % bs ;
memset ( preq - > pad , 0 , sizeof ( preq - > pad ) ) ;
sg_init_table ( preq - > src , 1 ) ;
sg_set_buf ( preq - > src , & preq - > pad , padlen ) ;
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
poly_cipherpad_done , req ) ;
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
ahash_request_set_crypt ( & preq - > req , preq - > src , NULL , padlen ) ;
err = crypto_ahash_update ( & preq - > req ) ;
if ( err )
return err ;
return poly_tail ( req ) ;
}
static void poly_cipher_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_cipherpad ) ;
}
static int poly_cipher ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct poly_req * preq = & rctx - > u . poly ;
struct scatterlist * crypt = req - > src ;
int err ;
if ( rctx - > cryptlen = = req - > cryptlen ) /* encrypting */
crypt = req - > dst ;
2015-07-16 07:35:08 +03:00
sg_init_table ( rctx - > src , 2 ) ;
crypt = scatterwalk_ffwd ( rctx - > src , crypt , req - > assoclen ) ;
2015-06-01 14:44:00 +03:00
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
poly_cipher_done , req ) ;
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
ahash_request_set_crypt ( & preq - > req , crypt , NULL , rctx - > cryptlen ) ;
err = crypto_ahash_update ( & preq - > req ) ;
if ( err )
return err ;
return poly_cipherpad ( req ) ;
}
static void poly_adpad_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_cipher ) ;
}
static int poly_adpad ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct poly_req * preq = & rctx - > u . poly ;
unsigned int padlen , bs = POLY1305_BLOCK_SIZE ;
int err ;
2015-07-16 07:35:08 +03:00
padlen = ( bs - ( rctx - > assoclen % bs ) ) % bs ;
2015-06-01 14:44:00 +03:00
memset ( preq - > pad , 0 , sizeof ( preq - > pad ) ) ;
sg_init_table ( preq - > src , 1 ) ;
sg_set_buf ( preq - > src , preq - > pad , padlen ) ;
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
poly_adpad_done , req ) ;
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
ahash_request_set_crypt ( & preq - > req , preq - > src , NULL , padlen ) ;
err = crypto_ahash_update ( & preq - > req ) ;
if ( err )
return err ;
return poly_cipher ( req ) ;
}
static void poly_ad_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_adpad ) ;
}
static int poly_ad ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct poly_req * preq = & rctx - > u . poly ;
int err ;
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
poly_ad_done , req ) ;
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
2015-07-16 07:35:08 +03:00
ahash_request_set_crypt ( & preq - > req , req - > src , NULL , rctx - > assoclen ) ;
2015-06-01 14:44:00 +03:00
err = crypto_ahash_update ( & preq - > req ) ;
if ( err )
return err ;
return poly_adpad ( req ) ;
}
2015-06-16 12:34:16 +03:00
static void poly_setkey_done ( struct crypto_async_request * areq , int err )
2015-06-01 14:44:00 +03:00
{
async_done_continue ( areq - > data , err , poly_ad ) ;
}
2015-06-16 12:34:16 +03:00
static int poly_setkey ( struct aead_request * req )
2015-06-01 14:44:00 +03:00
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct poly_req * preq = & rctx - > u . poly ;
int err ;
2015-06-16 12:34:16 +03:00
sg_init_table ( preq - > src , 1 ) ;
sg_set_buf ( preq - > src , rctx - > key , sizeof ( rctx - > key ) ) ;
2015-06-01 14:44:00 +03:00
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
2015-06-16 12:34:16 +03:00
poly_setkey_done , req ) ;
2015-06-01 14:44:00 +03:00
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
2015-06-16 12:34:16 +03:00
ahash_request_set_crypt ( & preq - > req , preq - > src , NULL , sizeof ( rctx - > key ) ) ;
2015-06-01 14:44:00 +03:00
2015-06-16 12:34:16 +03:00
err = crypto_ahash_update ( & preq - > req ) ;
2015-06-01 14:44:00 +03:00
if ( err )
return err ;
return poly_ad ( req ) ;
}
2015-06-16 12:34:16 +03:00
static void poly_init_done ( struct crypto_async_request * areq , int err )
2015-06-01 14:44:00 +03:00
{
2015-06-16 12:34:16 +03:00
async_done_continue ( areq - > data , err , poly_setkey ) ;
}
static int poly_init ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
2015-06-01 14:44:00 +03:00
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
2015-06-16 12:34:16 +03:00
struct poly_req * preq = & rctx - > u . poly ;
2015-06-01 14:44:00 +03:00
int err ;
2015-06-16 12:34:16 +03:00
ahash_request_set_callback ( & preq - > req , aead_request_flags ( req ) ,
poly_init_done , req ) ;
ahash_request_set_tfm ( & preq - > req , ctx - > poly ) ;
2015-06-01 14:44:00 +03:00
2015-06-16 12:34:16 +03:00
err = crypto_ahash_init ( & preq - > req ) ;
2015-06-01 14:44:00 +03:00
if ( err )
return err ;
2015-06-16 12:34:16 +03:00
return poly_setkey ( req ) ;
2015-06-01 14:44:00 +03:00
}
static void poly_genkey_done ( struct crypto_async_request * areq , int err )
{
2015-06-16 12:34:16 +03:00
async_done_continue ( areq - > data , err , poly_init ) ;
2015-06-01 14:44:00 +03:00
}
static int poly_genkey ( struct aead_request * req )
{
2015-07-16 07:35:08 +03:00
struct crypto_aead * tfm = crypto_aead_reqtfm ( req ) ;
struct chachapoly_ctx * ctx = crypto_aead_ctx ( tfm ) ;
2015-06-01 14:44:00 +03:00
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct chacha_req * creq = & rctx - > u . chacha ;
int err ;
2015-07-16 07:35:08 +03:00
rctx - > assoclen = req - > assoclen ;
if ( crypto_aead_ivsize ( tfm ) = = 8 ) {
if ( rctx - > assoclen < 8 )
return - EINVAL ;
rctx - > assoclen - = 8 ;
}
2015-06-01 14:44:00 +03:00
sg_init_table ( creq - > src , 1 ) ;
2015-06-16 12:34:16 +03:00
memset ( rctx - > key , 0 , sizeof ( rctx - > key ) ) ;
sg_set_buf ( creq - > src , rctx - > key , sizeof ( rctx - > key ) ) ;
2015-06-01 14:44:00 +03:00
chacha_iv ( creq - > iv , req , 0 ) ;
2016-07-12 08:17:40 +03:00
skcipher_request_set_callback ( & creq - > req , aead_request_flags ( req ) ,
poly_genkey_done , req ) ;
skcipher_request_set_tfm ( & creq - > req , ctx - > chacha ) ;
skcipher_request_set_crypt ( & creq - > req , creq - > src , creq - > src ,
POLY1305_KEY_SIZE , creq - > iv ) ;
2015-06-01 14:44:00 +03:00
2016-07-12 08:17:40 +03:00
err = crypto_skcipher_decrypt ( & creq - > req ) ;
2015-06-01 14:44:00 +03:00
if ( err )
return err ;
2015-06-16 12:34:16 +03:00
return poly_init ( req ) ;
2015-06-01 14:44:00 +03:00
}
static void chacha_encrypt_done ( struct crypto_async_request * areq , int err )
{
async_done_continue ( areq - > data , err , poly_genkey ) ;
}
static int chacha_encrypt ( struct aead_request * req )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( crypto_aead_reqtfm ( req ) ) ;
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
struct chacha_req * creq = & rctx - > u . chacha ;
2015-07-16 07:35:08 +03:00
struct scatterlist * src , * dst ;
2015-06-01 14:44:00 +03:00
int err ;
2015-12-06 04:51:38 +03:00
if ( req - > cryptlen = = 0 )
goto skip ;
2015-06-01 14:44:00 +03:00
chacha_iv ( creq - > iv , req , 1 ) ;
2015-07-16 07:35:08 +03:00
sg_init_table ( rctx - > src , 2 ) ;
src = scatterwalk_ffwd ( rctx - > src , req - > src , req - > assoclen ) ;
dst = src ;
if ( req - > src ! = req - > dst ) {
sg_init_table ( rctx - > dst , 2 ) ;
dst = scatterwalk_ffwd ( rctx - > dst , req - > dst , req - > assoclen ) ;
}
2016-07-12 08:17:40 +03:00
skcipher_request_set_callback ( & creq - > req , aead_request_flags ( req ) ,
chacha_encrypt_done , req ) ;
skcipher_request_set_tfm ( & creq - > req , ctx - > chacha ) ;
skcipher_request_set_crypt ( & creq - > req , src , dst ,
req - > cryptlen , creq - > iv ) ;
err = crypto_skcipher_encrypt ( & creq - > req ) ;
2015-06-01 14:44:00 +03:00
if ( err )
return err ;
2015-12-06 04:51:38 +03:00
skip :
2015-06-01 14:44:00 +03:00
return poly_genkey ( req ) ;
}
static int chachapoly_encrypt ( struct aead_request * req )
{
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
rctx - > cryptlen = req - > cryptlen ;
/* encrypt call chain:
* - chacha_encrypt / done ( )
2015-06-16 12:34:16 +03:00
* - poly_genkey / done ( )
2015-06-01 14:44:00 +03:00
* - poly_init / done ( )
2015-06-16 12:34:16 +03:00
* - poly_setkey / done ( )
2015-06-01 14:44:00 +03:00
* - poly_ad / done ( )
* - poly_adpad / done ( )
* - poly_cipher / done ( )
* - poly_cipherpad / done ( )
* - poly_tail / done / continue ( )
* - poly_copy_tag ( )
*/
return chacha_encrypt ( req ) ;
}
static int chachapoly_decrypt ( struct aead_request * req )
{
struct chachapoly_req_ctx * rctx = aead_request_ctx ( req ) ;
rctx - > cryptlen = req - > cryptlen - POLY1305_DIGEST_SIZE ;
/* decrypt call chain:
2015-06-16 12:34:16 +03:00
* - poly_genkey / done ( )
2015-06-01 14:44:00 +03:00
* - poly_init / done ( )
2015-06-16 12:34:16 +03:00
* - poly_setkey / done ( )
2015-06-01 14:44:00 +03:00
* - poly_ad / done ( )
* - poly_adpad / done ( )
* - poly_cipher / done ( )
* - poly_cipherpad / done ( )
* - poly_tail / done / continue ( )
* - chacha_decrypt / done ( )
* - poly_verify_tag ( )
*/
return poly_genkey ( req ) ;
}
static int chachapoly_setkey ( struct crypto_aead * aead , const u8 * key ,
unsigned int keylen )
{
struct chachapoly_ctx * ctx = crypto_aead_ctx ( aead ) ;
int err ;
if ( keylen ! = ctx - > saltlen + CHACHA20_KEY_SIZE )
return - EINVAL ;
keylen - = ctx - > saltlen ;
memcpy ( ctx - > salt , key + keylen , ctx - > saltlen ) ;
2016-07-12 08:17:40 +03:00
crypto_skcipher_clear_flags ( ctx - > chacha , CRYPTO_TFM_REQ_MASK ) ;
crypto_skcipher_set_flags ( ctx - > chacha , crypto_aead_get_flags ( aead ) &
CRYPTO_TFM_REQ_MASK ) ;
2015-06-01 14:44:00 +03:00
2016-07-12 08:17:40 +03:00
err = crypto_skcipher_setkey ( ctx - > chacha , key , keylen ) ;
crypto_aead_set_flags ( aead , crypto_skcipher_get_flags ( ctx - > chacha ) &
CRYPTO_TFM_RES_MASK ) ;
2015-06-01 14:44:00 +03:00
return err ;
}
static int chachapoly_setauthsize ( struct crypto_aead * tfm ,
unsigned int authsize )
{
if ( authsize ! = POLY1305_DIGEST_SIZE )
return - EINVAL ;
return 0 ;
}
2015-07-16 07:35:08 +03:00
static int chachapoly_init ( struct crypto_aead * tfm )
2015-06-01 14:44:00 +03:00
{
2015-07-16 07:35:08 +03:00
struct aead_instance * inst = aead_alg_instance ( tfm ) ;
struct chachapoly_instance_ctx * ictx = aead_instance_ctx ( inst ) ;
struct chachapoly_ctx * ctx = crypto_aead_ctx ( tfm ) ;
2016-07-12 08:17:40 +03:00
struct crypto_skcipher * chacha ;
2015-06-01 14:44:00 +03:00
struct crypto_ahash * poly ;
unsigned long align ;
poly = crypto_spawn_ahash ( & ictx - > poly ) ;
if ( IS_ERR ( poly ) )
return PTR_ERR ( poly ) ;
2016-10-28 19:52:19 +03:00
chacha = crypto_spawn_skcipher ( & ictx - > chacha ) ;
2015-06-01 14:44:00 +03:00
if ( IS_ERR ( chacha ) ) {
crypto_free_ahash ( poly ) ;
return PTR_ERR ( chacha ) ;
}
ctx - > chacha = chacha ;
ctx - > poly = poly ;
ctx - > saltlen = ictx - > saltlen ;
2015-07-16 07:35:08 +03:00
align = crypto_aead_alignmask ( tfm ) ;
2015-06-01 14:44:00 +03:00
align & = ~ ( crypto_tfm_ctx_alignment ( ) - 1 ) ;
2015-07-16 07:35:08 +03:00
crypto_aead_set_reqsize (
tfm ,
align + offsetof ( struct chachapoly_req_ctx , u ) +
max ( offsetof ( struct chacha_req , req ) +
2016-07-12 08:17:40 +03:00
sizeof ( struct skcipher_request ) +
crypto_skcipher_reqsize ( chacha ) ,
2015-07-16 07:35:08 +03:00
offsetof ( struct poly_req , req ) +
sizeof ( struct ahash_request ) +
crypto_ahash_reqsize ( poly ) ) ) ;
2015-06-01 14:44:00 +03:00
return 0 ;
}
2015-07-16 07:35:08 +03:00
static void chachapoly_exit ( struct crypto_aead * tfm )
2015-06-01 14:44:00 +03:00
{
2015-07-16 07:35:08 +03:00
struct chachapoly_ctx * ctx = crypto_aead_ctx ( tfm ) ;
2015-06-01 14:44:00 +03:00
crypto_free_ahash ( ctx - > poly ) ;
2016-07-12 08:17:40 +03:00
crypto_free_skcipher ( ctx - > chacha ) ;
2015-06-01 14:44:00 +03:00
}
2015-07-16 07:35:08 +03:00
static void chachapoly_free ( struct aead_instance * inst )
{
struct chachapoly_instance_ctx * ctx = aead_instance_ctx ( inst ) ;
crypto_drop_skcipher ( & ctx - > chacha ) ;
crypto_drop_ahash ( & ctx - > poly ) ;
kfree ( inst ) ;
}
static int chachapoly_create ( struct crypto_template * tmpl , struct rtattr * * tb ,
const char * name , unsigned int ivsize )
2015-06-01 14:44:00 +03:00
{
struct crypto_attr_type * algt ;
2015-07-16 07:35:08 +03:00
struct aead_instance * inst ;
2016-07-12 08:17:40 +03:00
struct skcipher_alg * chacha ;
2015-06-01 14:44:00 +03:00
struct crypto_alg * poly ;
2015-07-16 07:35:08 +03:00
struct hash_alg_common * poly_hash ;
2015-06-01 14:44:00 +03:00
struct chachapoly_instance_ctx * ctx ;
const char * chacha_name , * poly_name ;
int err ;
if ( ivsize > CHACHAPOLY_IV_SIZE )
2015-07-16 07:35:08 +03:00
return - EINVAL ;
2015-06-01 14:44:00 +03:00
algt = crypto_get_attr_type ( tb ) ;
if ( IS_ERR ( algt ) )
2015-07-16 07:35:08 +03:00
return PTR_ERR ( algt ) ;
2015-06-01 14:44:00 +03:00
2015-08-13 12:29:06 +03:00
if ( ( algt - > type ^ CRYPTO_ALG_TYPE_AEAD ) & algt - > mask )
2015-07-16 07:35:08 +03:00
return - EINVAL ;
2015-06-01 14:44:00 +03:00
chacha_name = crypto_attr_alg_name ( tb [ 1 ] ) ;
if ( IS_ERR ( chacha_name ) )
2015-07-16 07:35:08 +03:00
return PTR_ERR ( chacha_name ) ;
2015-06-01 14:44:00 +03:00
poly_name = crypto_attr_alg_name ( tb [ 2 ] ) ;
if ( IS_ERR ( poly_name ) )
2015-07-16 07:35:08 +03:00
return PTR_ERR ( poly_name ) ;
2015-06-01 14:44:00 +03:00
poly = crypto_find_alg ( poly_name , & crypto_ahash_type ,
CRYPTO_ALG_TYPE_HASH ,
2016-07-12 08:17:40 +03:00
CRYPTO_ALG_TYPE_AHASH_MASK |
crypto_requires_sync ( algt - > type ,
algt - > mask ) ) ;
2015-06-01 14:44:00 +03:00
if ( IS_ERR ( poly ) )
2015-07-16 07:35:08 +03:00
return PTR_ERR ( poly ) ;
crypto: chacha20poly1305 - validate the digest size
If the rfc7539 template was instantiated with a hash algorithm with
digest size larger than 16 bytes (POLY1305_DIGEST_SIZE), then the digest
overran the 'tag' buffer in 'struct chachapoly_req_ctx', corrupting the
subsequent memory, including 'cryptlen'. This caused a crash during
crypto_skcipher_decrypt().
Fix it by, when instantiating the template, requiring that the
underlying hash algorithm has the digest size expected for Poly1305.
Reproducer:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int algfd, reqfd;
struct sockaddr_alg addr = {
.salg_type = "aead",
.salg_name = "rfc7539(chacha20,sha256)",
};
unsigned char buf[32] = { 0 };
algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(algfd, (void *)&addr, sizeof(addr));
setsockopt(algfd, SOL_ALG, ALG_SET_KEY, buf, sizeof(buf));
reqfd = accept(algfd, 0, 0);
write(reqfd, buf, 16);
read(reqfd, buf, 16);
}
Reported-by: syzbot <syzkaller@googlegroups.com>
Fixes: 71ebc4d1b27d ("crypto: chacha20poly1305 - Add a ChaCha20-Poly1305 AEAD construction, RFC7539")
Cc: <stable@vger.kernel.org> # v4.2+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2017-12-11 23:15:17 +03:00
poly_hash = __crypto_hash_alg_common ( poly ) ;
err = - EINVAL ;
if ( poly_hash - > digestsize ! = POLY1305_DIGEST_SIZE )
goto out_put_poly ;
2015-06-01 14:44:00 +03:00
err = - ENOMEM ;
inst = kzalloc ( sizeof ( * inst ) + sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! inst )
goto out_put_poly ;
2015-07-16 07:35:08 +03:00
ctx = aead_instance_ctx ( inst ) ;
2015-06-01 14:44:00 +03:00
ctx - > saltlen = CHACHAPOLY_IV_SIZE - ivsize ;
2015-07-16 07:35:08 +03:00
err = crypto_init_ahash_spawn ( & ctx - > poly , poly_hash ,
aead_crypto_instance ( inst ) ) ;
2015-06-01 14:44:00 +03:00
if ( err )
goto err_free_inst ;
2015-07-16 07:35:08 +03:00
crypto_set_skcipher_spawn ( & ctx - > chacha , aead_crypto_instance ( inst ) ) ;
2016-10-28 19:51:13 +03:00
err = crypto_grab_skcipher ( & ctx - > chacha , chacha_name , 0 ,
crypto_requires_sync ( algt - > type ,
algt - > mask ) ) ;
2015-06-01 14:44:00 +03:00
if ( err )
goto err_drop_poly ;
2016-07-12 08:17:40 +03:00
chacha = crypto_spawn_skcipher_alg ( & ctx - > chacha ) ;
2015-06-01 14:44:00 +03:00
err = - EINVAL ;
/* Need 16-byte IV size, including Initial Block Counter value */
2016-07-12 08:17:40 +03:00
if ( crypto_skcipher_alg_ivsize ( chacha ) ! = CHACHA20_IV_SIZE )
2015-06-01 14:44:00 +03:00
goto out_drop_chacha ;
/* Not a stream cipher? */
2016-07-12 08:17:40 +03:00
if ( chacha - > base . cra_blocksize ! = 1 )
2015-06-01 14:44:00 +03:00
goto out_drop_chacha ;
err = - ENAMETOOLONG ;
2015-07-16 07:35:08 +03:00
if ( snprintf ( inst - > alg . base . cra_name , CRYPTO_MAX_ALG_NAME ,
2015-06-01 14:44:00 +03:00
" %s(%s,%s) " , name , chacha_name ,
poly_name ) > = CRYPTO_MAX_ALG_NAME )
goto out_drop_chacha ;
2015-07-16 07:35:08 +03:00
if ( snprintf ( inst - > alg . base . cra_driver_name , CRYPTO_MAX_ALG_NAME ,
2016-07-12 08:17:40 +03:00
" %s(%s,%s) " , name , chacha - > base . cra_driver_name ,
2015-06-01 14:44:00 +03:00
poly - > cra_driver_name ) > = CRYPTO_MAX_ALG_NAME )
goto out_drop_chacha ;
2016-07-12 08:17:40 +03:00
inst - > alg . base . cra_flags = ( chacha - > base . cra_flags | poly - > cra_flags ) &
2015-07-16 07:35:08 +03:00
CRYPTO_ALG_ASYNC ;
2016-07-12 08:17:40 +03:00
inst - > alg . base . cra_priority = ( chacha - > base . cra_priority +
2015-07-16 07:35:08 +03:00
poly - > cra_priority ) / 2 ;
inst - > alg . base . cra_blocksize = 1 ;
2016-07-12 08:17:40 +03:00
inst - > alg . base . cra_alignmask = chacha - > base . cra_alignmask |
2015-07-16 07:35:08 +03:00
poly - > cra_alignmask ;
inst - > alg . base . cra_ctxsize = sizeof ( struct chachapoly_ctx ) +
ctx - > saltlen ;
inst - > alg . ivsize = ivsize ;
2016-07-12 08:17:40 +03:00
inst - > alg . chunksize = crypto_skcipher_alg_chunksize ( chacha ) ;
2015-07-16 07:35:08 +03:00
inst - > alg . maxauthsize = POLY1305_DIGEST_SIZE ;
inst - > alg . init = chachapoly_init ;
inst - > alg . exit = chachapoly_exit ;
inst - > alg . encrypt = chachapoly_encrypt ;
inst - > alg . decrypt = chachapoly_decrypt ;
inst - > alg . setkey = chachapoly_setkey ;
inst - > alg . setauthsize = chachapoly_setauthsize ;
inst - > free = chachapoly_free ;
err = aead_register_instance ( tmpl , inst ) ;
if ( err )
goto out_drop_chacha ;
out_put_poly :
2015-06-01 14:44:00 +03:00
crypto_mod_put ( poly ) ;
2015-07-16 07:35:08 +03:00
return err ;
2015-06-01 14:44:00 +03:00
out_drop_chacha :
crypto_drop_skcipher ( & ctx - > chacha ) ;
err_drop_poly :
crypto_drop_ahash ( & ctx - > poly ) ;
err_free_inst :
kfree ( inst ) ;
2015-07-16 07:35:08 +03:00
goto out_put_poly ;
2015-06-01 14:44:00 +03:00
}
2015-07-16 07:35:08 +03:00
static int rfc7539_create ( struct crypto_template * tmpl , struct rtattr * * tb )
2015-06-01 14:44:02 +03:00
{
2015-07-16 07:35:08 +03:00
return chachapoly_create ( tmpl , tb , " rfc7539 " , 12 ) ;
2015-06-01 14:44:02 +03:00
}
2015-07-16 07:35:08 +03:00
static int rfc7539esp_create ( struct crypto_template * tmpl , struct rtattr * * tb )
2015-06-01 14:44:00 +03:00
{
2015-07-16 07:35:08 +03:00
return chachapoly_create ( tmpl , tb , " rfc7539esp " , 8 ) ;
2015-06-01 14:44:00 +03:00
}
static struct crypto_template rfc7539_tmpl = {
. name = " rfc7539 " ,
2015-07-16 07:35:08 +03:00
. create = rfc7539_create ,
2015-06-01 14:44:00 +03:00
. module = THIS_MODULE ,
} ;
2015-06-01 14:44:02 +03:00
static struct crypto_template rfc7539esp_tmpl = {
. name = " rfc7539esp " ,
2015-07-16 07:35:08 +03:00
. create = rfc7539esp_create ,
2015-06-01 14:44:02 +03:00
. module = THIS_MODULE ,
} ;
2015-06-01 14:44:00 +03:00
static int __init chacha20poly1305_module_init ( void )
{
2015-06-01 14:44:02 +03:00
int err ;
err = crypto_register_template ( & rfc7539_tmpl ) ;
if ( err )
return err ;
err = crypto_register_template ( & rfc7539esp_tmpl ) ;
if ( err )
crypto_unregister_template ( & rfc7539_tmpl ) ;
return err ;
2015-06-01 14:44:00 +03:00
}
static void __exit chacha20poly1305_module_exit ( void )
{
2015-06-01 14:44:02 +03:00
crypto_unregister_template ( & rfc7539esp_tmpl ) ;
2015-06-01 14:44:00 +03:00
crypto_unregister_template ( & rfc7539_tmpl ) ;
}
module_init ( chacha20poly1305_module_init ) ;
module_exit ( chacha20poly1305_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Martin Willi <martin@strongswan.org> " ) ;
MODULE_DESCRIPTION ( " ChaCha20-Poly1305 AEAD " ) ;
MODULE_ALIAS_CRYPTO ( " rfc7539 " ) ;
2015-06-01 14:44:02 +03:00
MODULE_ALIAS_CRYPTO ( " rfc7539esp " ) ;