I have a functioning implementation of the NIST SPECK block cipher that I wish to modify to support a tweak. While I am aware that there are a number of papers proposing secure methods of turning a block cipher into a tweakable one, all the ones I have read involve some kind of universal hash function, requiring a second key that is then applied to the tweak as part of the mode.
While these modes have been proven secure by their respective author(s) under the assumption that both the block cipher and the hash function themselves are secure, they do add a fair amount of extra processing when considering small MCU targets as they almost always involve multiplication in the field GF (2^n). Given that the tweak can be up to 128-bits in size, this multiplication can add a considerable amount of work for a small MCU.
Upon closer look at the details of the SPECK cipher, I believe I have found a very simple and efficient modification that will do the trick.
I have provided below some pseudo code that clearly expresses the standard SPECK algorithm and below it, the modifications I propose.
Understand that I am not asking to validate code, but to validate the proposed changes to the cryptographic algorithm as expressed by the code.
The top two thirds of the code shows the standard algorithm for block encryption/decryption. I have omitted the generation of the expanded keys as this part is the same for both constructions.
The bottom third shows where the proposed changes are injected.
The idea is to double the number of rounds but two effectively have two different expanded keys, each one dependent on a part of the tweak.
The XOR of the expanded key with a round constant, followed by multiplication by a prime (must be odd) is effectively a miniature 'full-state absorb and squeeze' sponge function acting as a hash function to generate two separate expanded key streams. Each individual expanded key is then XOR'd with its respective tweak, making the expanded key schedule totally dependent on each part of the tweak.
Note that the tweak is not being applied to the data block, but to the permutation function where it modifies which permutation from a family of permutations will be used.
My question is : Given this proposed modification, is this a secure tweak block cipher?
//==========================================================================//
// Following parameters are determined based on which SPECK variant is used //
// //
// NR = Number of rounds //
// //
// ALPHA & BETA = Rotation constants //
// //
// U = Unsigned type : One of { uint16_t , uint32_t , uint64_t } //
// //
// key_e [NR] = Expanded key generated from secret key //
// //
//==========================================================================//
#include <bit> // std::rotr & std::rotl //
void permute_enc (U & lhs , U & rhs , U const key)
{
lhs = (std::rotr <U> (lhs , ALPHA) + rhs) ^ key ;
rhs = std::rotl <U> (rhs , BETA ) ^ lhs ;
}
void permute_dec (U & lhs , U & rhs , U const key)
{
rhs = std::rotr <U> (rhs ^ lhs , BETA <U>) ;
lhs = std::rotl <U> ((lhs ^ key) - rhs , ALPHA <U>) ;
}
//=[ Normal Block Cipher : Standard ]=======================================//
void encrypt (U const key_e [NR] , U block [2]) const noexcept
{
for (int i = 0 ; i < NR ; i ++)
{
permute_enc (block [1] , block [0] , key_e [i]) ;
}
}
void decrypt (U const key_e [NR] , U block [2]) const noexcept
{
for (int i = NR - 1 ; i >= 0 ; i --)
{
permute_dec (block [1] , block [0] , key_e [i]) ;
}
}
//=[ Tweak Block Cipher : Proposed ]========================================//
U const RC_0 = // A randomly chosen round constant
U const RC_1 = // Second one like above such that RC_0 != RC_1
U const PRIME_0 = // A randomly chosen prime number different for each SPECK variant
U const PRIME_1 = // Second one like above such that PRIME_0 != PRIME_1
void encrypt (U const key_e [NR] , U block [2] , U const tweak [2]) const noexcept
{
for (int i = 0 ; i < NR ; i ++)
{
permute_enc (block [1] , block [0] , (PRIME_0 * (key_e [i] ^ RC_0)) ^ tweak [0]) ;
permute_enc (block [1] , block [0] , (PRIME_1 * (key_e [i] ^ RC_1)) ^ tweak [1]) ;
}
}
void decrypt (U const key_e [NR] , U block [2] , U const tweak [2]) const noexcept
{
for (int NR - 1 = 0 ; i >= 0 ; i --)
{
permute_dec (block [1] , block [0] , (PRIME_1 * (key_e [i] ^ RC_1)) ^ tweak [1]) ;
permute_dec (block [1] , block [0] , (PRIME_0 * (key_e [i] ^ RC_0)) ^ tweak [0]) ;
}
}