2022-05-20 21:14:54 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* POLYVAL : hash function for HCTR2 .
*
* Copyright ( c ) 2007 Nokia Siemens Networks - Mikko Herranen < mh1 @ iki . fi >
* Copyright ( c ) 2009 Intel Corp .
* Author : Huang Ying < ying . huang @ intel . com >
* Copyright 2021 Google LLC
*/
/*
* Code based on crypto / ghash - generic . c
*
* POLYVAL is a keyed hash function similar to GHASH . POLYVAL uses a different
* modulus for finite field multiplication which makes hardware accelerated
* implementations on little - endian machines faster . POLYVAL is used in the
* kernel to implement HCTR2 , but was originally specified for AES - GCM - SIV
* ( RFC 8452 ) .
*
* For more information see :
* Length - preserving encryption with HCTR2 :
* https : //eprint.iacr.org/2021/1441.pdf
* AES - GCM - SIV : Nonce Misuse - Resistant Authenticated Encryption :
* https : //datatracker.ietf.org/doc/html/rfc8452
*
* Like GHASH , POLYVAL is not a cryptographic hash function and should
* not be used outside of crypto modes explicitly designed to use POLYVAL .
*
* This implementation uses a convenient trick involving the GHASH and POLYVAL
* fields . This trick allows multiplication in the POLYVAL field to be
* implemented by using multiplication in the GHASH field as a subroutine . An
* element of the POLYVAL field can be converted to an element of the GHASH
* field by computing x * REVERSE ( a ) , where REVERSE reverses the byte - ordering of
* a . Similarly , an element of the GHASH field can be converted back to the
* POLYVAL field by computing REVERSE ( x ^ { - 1 } * a ) . For more information , see :
* https : //datatracker.ietf.org/doc/html/rfc8452#appendix-A
*
* By using this trick , we do not need to implement the POLYVAL field for the
* generic implementation .
*
* Warning : this generic implementation is not intended to be used in practice
* and is not constant time . For practical use , a hardware accelerated
* implementation of POLYVAL should be used instead .
*
*/
# include <asm/unaligned.h>
# include <crypto/algapi.h>
# include <crypto/gf128mul.h>
# include <crypto/polyval.h>
# include <crypto/internal/hash.h>
# include <linux/crypto.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
struct polyval_tfm_ctx {
struct gf128mul_4k * gf128 ;
} ;
struct polyval_desc_ctx {
union {
u8 buffer [ POLYVAL_BLOCK_SIZE ] ;
be128 buffer128 ;
} ;
u32 bytes ;
} ;
static void copy_and_reverse ( u8 dst [ POLYVAL_BLOCK_SIZE ] ,
const u8 src [ POLYVAL_BLOCK_SIZE ] )
{
u64 a = get_unaligned ( ( const u64 * ) & src [ 0 ] ) ;
u64 b = get_unaligned ( ( const u64 * ) & src [ 8 ] ) ;
put_unaligned ( swab64 ( a ) , ( u64 * ) & dst [ 8 ] ) ;
put_unaligned ( swab64 ( b ) , ( u64 * ) & dst [ 0 ] ) ;
}
2022-05-20 21:14:59 +03:00
/*
* Performs multiplication in the POLYVAL field using the GHASH field as a
* subroutine . This function is used as a fallback for hardware accelerated
* implementations when simd registers are unavailable .
*
* Note : This function is not used for polyval - generic , instead we use the 4 k
* lookup table implementation for finite field multiplication .
*/
void polyval_mul_non4k ( u8 * op1 , const u8 * op2 )
{
be128 a , b ;
// Assume one argument is in Montgomery form and one is not.
copy_and_reverse ( ( u8 * ) & a , op1 ) ;
copy_and_reverse ( ( u8 * ) & b , op2 ) ;
gf128mul_x_lle ( & a , & a ) ;
gf128mul_lle ( & a , & b ) ;
copy_and_reverse ( op1 , ( u8 * ) & a ) ;
}
EXPORT_SYMBOL_GPL ( polyval_mul_non4k ) ;
/*
* Perform a POLYVAL update using non4k multiplication . This function is used
* as a fallback for hardware accelerated implementations when simd registers
* are unavailable .
*
* Note : This function is not used for polyval - generic , instead we use the 4 k
* lookup table implementation of finite field multiplication .
*/
void polyval_update_non4k ( const u8 * key , const u8 * in ,
size_t nblocks , u8 * accumulator )
{
while ( nblocks - - ) {
crypto_xor ( accumulator , in , POLYVAL_BLOCK_SIZE ) ;
polyval_mul_non4k ( accumulator , key ) ;
in + = POLYVAL_BLOCK_SIZE ;
}
}
EXPORT_SYMBOL_GPL ( polyval_update_non4k ) ;
2022-05-20 21:14:54 +03:00
static int polyval_setkey ( struct crypto_shash * tfm ,
const u8 * key , unsigned int keylen )
{
struct polyval_tfm_ctx * ctx = crypto_shash_ctx ( tfm ) ;
be128 k ;
if ( keylen ! = POLYVAL_BLOCK_SIZE )
return - EINVAL ;
gf128mul_free_4k ( ctx - > gf128 ) ;
BUILD_BUG_ON ( sizeof ( k ) ! = POLYVAL_BLOCK_SIZE ) ;
copy_and_reverse ( ( u8 * ) & k , key ) ;
gf128mul_x_lle ( & k , & k ) ;
ctx - > gf128 = gf128mul_init_4k_lle ( & k ) ;
memzero_explicit ( & k , POLYVAL_BLOCK_SIZE ) ;
if ( ! ctx - > gf128 )
return - ENOMEM ;
return 0 ;
}
static int polyval_init ( struct shash_desc * desc )
{
struct polyval_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
memset ( dctx , 0 , sizeof ( * dctx ) ) ;
return 0 ;
}
static int polyval_update ( struct shash_desc * desc ,
const u8 * src , unsigned int srclen )
{
struct polyval_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
const struct polyval_tfm_ctx * ctx = crypto_shash_ctx ( desc - > tfm ) ;
u8 * pos ;
u8 tmp [ POLYVAL_BLOCK_SIZE ] ;
int n ;
if ( dctx - > bytes ) {
n = min ( srclen , dctx - > bytes ) ;
pos = dctx - > buffer + dctx - > bytes - 1 ;
dctx - > bytes - = n ;
srclen - = n ;
while ( n - - )
* pos - - ^ = * src + + ;
if ( ! dctx - > bytes )
gf128mul_4k_lle ( & dctx - > buffer128 , ctx - > gf128 ) ;
}
while ( srclen > = POLYVAL_BLOCK_SIZE ) {
copy_and_reverse ( tmp , src ) ;
crypto_xor ( dctx - > buffer , tmp , POLYVAL_BLOCK_SIZE ) ;
gf128mul_4k_lle ( & dctx - > buffer128 , ctx - > gf128 ) ;
src + = POLYVAL_BLOCK_SIZE ;
srclen - = POLYVAL_BLOCK_SIZE ;
}
if ( srclen ) {
dctx - > bytes = POLYVAL_BLOCK_SIZE - srclen ;
pos = dctx - > buffer + POLYVAL_BLOCK_SIZE - 1 ;
while ( srclen - - )
* pos - - ^ = * src + + ;
}
return 0 ;
}
static int polyval_final ( struct shash_desc * desc , u8 * dst )
{
struct polyval_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
const struct polyval_tfm_ctx * ctx = crypto_shash_ctx ( desc - > tfm ) ;
if ( dctx - > bytes )
gf128mul_4k_lle ( & dctx - > buffer128 , ctx - > gf128 ) ;
copy_and_reverse ( dst , dctx - > buffer ) ;
return 0 ;
}
static void polyval_exit_tfm ( struct crypto_tfm * tfm )
{
struct polyval_tfm_ctx * ctx = crypto_tfm_ctx ( tfm ) ;
gf128mul_free_4k ( ctx - > gf128 ) ;
}
static struct shash_alg polyval_alg = {
. digestsize = POLYVAL_DIGEST_SIZE ,
. init = polyval_init ,
. update = polyval_update ,
. final = polyval_final ,
. setkey = polyval_setkey ,
. descsize = sizeof ( struct polyval_desc_ctx ) ,
. base = {
. cra_name = " polyval " ,
. cra_driver_name = " polyval-generic " ,
. cra_priority = 100 ,
. cra_blocksize = POLYVAL_BLOCK_SIZE ,
. cra_ctxsize = sizeof ( struct polyval_tfm_ctx ) ,
. cra_module = THIS_MODULE ,
. cra_exit = polyval_exit_tfm ,
} ,
} ;
static int __init polyval_mod_init ( void )
{
return crypto_register_shash ( & polyval_alg ) ;
}
static void __exit polyval_mod_exit ( void )
{
crypto_unregister_shash ( & polyval_alg ) ;
}
subsys_initcall ( polyval_mod_init ) ;
module_exit ( polyval_mod_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " POLYVAL hash function " ) ;
MODULE_ALIAS_CRYPTO ( " polyval " ) ;
MODULE_ALIAS_CRYPTO ( " polyval-generic " ) ;