📄 twofish.cpp
字号:
if( key_len < 0 || key_len > 32 )
{
/*
* This can only happen if a programmer didn't read the limitations
* on the key size.
*/
Twofish_fatal( "Twofish_prepare_key: illegal key length" );
/*
* A return statement just in case the fatal macro returns.
* The rest of the code assumes that key_len is in range, and would
* buffer-overflow if it wasn't.
*
* Why do we still use a programming language that has problems like
* buffer overflows, when these problems were solved in 1960 with
* the development of Algol? Have we not leared anything?
*/
return;
}
/* Pad the key with zeroes to the next suitable key length. */
memcpy( K, key, key_len );
memset( K+key_len, 0, sizeof(K)-key_len );
/*
* Compute kCycles: the number of key cycles used in the cipher.
* 2 for 128-bit keys, 3 for 192-bit keys, and 4 for 256-bit keys.
*/
kCycles = (key_len + 7) >> 3;
/* Handle the special case of very short keys: minimum 2 cycles. */
if( kCycles < 2 )
{
kCycles = 2;
}
/*
* From now on we just pretend to have 8*kCycles bytes of
* key material in K. This handles all the key size cases.
*/
/*
* We first compute the 40 expanded key words,
* formulas straight from the Twofish specifications.
*/
for( i=0; i<40; i+=2 )
{
/*
* Due to the byte spacing expected by the h() function
* we can pick the bytes directly from the key K.
* As we use bytes, we never have the little/big endian
* problem.
*
* Note that we apply the rotation function only to simple
* variables, as the rotation macro might evaluate its argument
* more than once.
*/
A = h( i , K , kCycles );
B = h( i+1, K+4, kCycles );
B = ROL32( B, 8 );
/* Compute and store the round keys. */
A += B;
B += A;
xkey->K[i] = A;
xkey->K[i+1] = ROL32( B, 9 );
}
/* Wipe variables that contained key material. */
A=B=0;
/*
* And now the dreaded RS multiplication that few seem to understand.
* The RS matrix is not random, and is specially designed to compute the
* RS matrix multiplication in a simple way.
*
* We work in the field GF(2)[x]/x^8+x^6+x^3+x^2+1. Note that this is a
* different field than used for the MDS matrix.
* (At least, it is a different representation because all GF(2^8)
* representations are equivalent in some form.)
*
* We take 8 consecutive bytes of the key and interpret them as
* a polynomial k_0 + k_1 y + k_2 y^2 + ... + k_7 y^7 where
* the k_i bytes are the key bytes and are elements of the finite field.
* We multiply this polynomial by y^4 and reduce it modulo
* y^4 + (x + 1/x)y^3 + (x)y^2 + (x + 1/x)y + 1.
* using straightforward polynomial modulo reduction.
* The coefficients of the result are the result of the RS
* matrix multiplication. When we wrote the Twofish specification,
* the original RS definition used the polynomials,
* but that requires much more mathematical knowledge.
* We were already using matrix multiplication in a finite field for
* the MDS matrix, so I re-wrote the RS operation as a matrix
* multiplication to reduce the difficulty of understanding it.
* Some implementors have not picked up on this simpler method of
* computing the RS operation, even though it is mentioned in the
* specifications.
*
* It is possible to perform these computations faster by using 32-bit
* word operations, but that is not portable and this is not a speed-
* critical area.
*
* We explained the 1/x computation when we did the MDS matrix.
*
* The S vector is stored in K[32..64].
* The S vector has to be reversed, so we loop cross-wise.
*
* Note the weird byte spacing of the S-vector, to match the even
* or odd key words arrays. See the discussion at the Hxx macros for
* details.
*/
kptr = K + 8*kCycles; /* Start at end of key */
sptr = K + 32; /* Start at start of S */
/* Loop over all key material */
while( kptr > K )
{
kptr -= 8;
/*
* Initialise the polynimial in sptr[0..12]
* The first four coefficients are 0 as we have to multiply by y^4.
* The next 8 coefficients are from the key material.
*/
memset( sptr, 0, 4 );
memcpy( sptr+4, kptr, 8 );
/*
* The 12 bytes starting at sptr are now the coefficients of
* the polynomial we need to reduce.
*/
/* Loop over the polynomial coefficients from high to low */
t = sptr+11;
/* Keep looping until polynomial is degree 3; */
while( t > sptr+3 )
{
/* Pick up the highest coefficient of the poly. */
b = *t;
/*
* Compute x and (x+1/x) times this coefficient.
* See the MDS matrix implementation for a discussion of
* multiplication by x and 1/x. We just use different
* constants here as we are in a
* different finite field representation.
*
* These two statements set
* bx = (x) * b
* bxx= (x + 1/x) * b
*/
bx = (Twofish_Byte)((b<<1) ^ rs_poly_const[ b>>7 ]);
bxx= (Twofish_Byte)((b>>1) ^ rs_poly_div_const[ b&1 ] ^ bx);
/*
* Subtract suitable multiple of
* y^4 + (x + 1/x)y^3 + (x)y^2 + (x + 1/x)y + 1
* from the polynomial, except that we don't bother
* updating t[0] as it will become zero anyway.
*/
t[-1] ^= bxx;
t[-2] ^= bx;
t[-3] ^= bxx;
t[-4] ^= b;
/* Go to the next coefficient. */
t--;
}
/* Go to next S-vector word, obeying the weird spacing rules. */
sptr += 8;
}
/* Wipe variables that contained key material. */
b = bx = bxx = 0;
/* And finally, we can compute the key-dependent S-boxes. */
fill_keyed_sboxes( &K[32], kCycles, xkey );
/* Wipe array that contained key material. */
memset( K, 0, sizeof( K ) );
}
/*
* We can now start on the actual encryption and decryption code.
* As these are often speed-critical we will use a lot of macros.
*/
/*
* The g() function is the heart of the round function.
* We have two versions of the g() function, one without an input
* rotation and one with.
* The pre-computed S-boxes make this pretty simple.
*/
#define g0(X,xkey) \
(xkey->s[0][b0(X)]^xkey->s[1][b1(X)]^xkey->s[2][b2(X)]^xkey->s[3][b3(X)])
#define g1(X,xkey) \
(xkey->s[0][b3(X)]^xkey->s[1][b0(X)]^xkey->s[2][b1(X)]^xkey->s[3][b2(X)])
/*
* A single round of Twofish. The A,B,C,D are the four state variables,
* T0 and T1 are temporaries, xkey is the expanded key, and r the
* round number.
*
* Note that this macro does not implement the swap at the end of the round.
*/
#define ENCRYPT_RND( A,B,C,D, T0, T1, xkey, r ) \
T0 = g0(A,xkey); T1 = g1(B,xkey);\
C ^= T0+T1+xkey->K[8+2*(r)]; C = ROR32(C,1);\
D = ROL32(D,1); D ^= T0+2*T1+xkey->K[8+2*(r)+1]
/*
* Encrypt a single cycle, consisting of two rounds.
* This avoids the swapping of the two halves.
* Parameter r is now the cycle number.
*/
#define ENCRYPT_CYCLE( A, B, C, D, T0, T1, xkey, r ) \
ENCRYPT_RND( A,B,C,D,T0,T1,xkey,2*(r) );\
ENCRYPT_RND( C,D,A,B,T0,T1,xkey,2*(r)+1 )
/* Full 16-round encryption */
#define ENCRYPT( A,B,C,D,T0,T1,xkey ) \
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 0 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 1 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 2 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 3 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 4 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 5 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 6 );\
ENCRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 7 )
/*
* A single round of Twofish for decryption. It differs from
* ENCRYTP_RND only because of the 1-bit rotations.
*/
#define DECRYPT_RND( A,B,C,D, T0, T1, xkey, r ) \
T0 = g0(A,xkey); T1 = g1(B,xkey);\
C = ROL32(C,1); C ^= T0+T1+xkey->K[8+2*(r)];\
D ^= T0+2*T1+xkey->K[8+2*(r)+1]; D = ROR32(D,1)
/*
* Decrypt a single cycle, consisting of two rounds.
* This avoids the swapping of the two halves.
* Parameter r is now the cycle number.
*/
#define DECRYPT_CYCLE( A, B, C, D, T0, T1, xkey, r ) \
DECRYPT_RND( A,B,C,D,T0,T1,xkey,2*(r)+1 );\
DECRYPT_RND( C,D,A,B,T0,T1,xkey,2*(r) )
/* Full 16-round decryption. */
#define DECRYPT( A,B,C,D,T0,T1, xkey ) \
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 7 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 6 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 5 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 4 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 3 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 2 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 1 );\
DECRYPT_CYCLE( A,B,C,D,T0,T1,xkey, 0 )
/*
* A macro to read the state from the plaintext and do the initial key xors.
* The koff argument allows us to use the same macro
* for the decryption which uses different key words at the start.
*/
#define GET_INPUT( src, A,B,C,D, xkey, koff ) \
A = GET32(src )^xkey->K[ koff]; B = GET32(src+ 4)^xkey->K[1+koff]; \
C = GET32(src+ 8)^xkey->K[2+koff]; D = GET32(src+12)^xkey->K[3+koff]
/*
* Similar macro to put the ciphertext in the output buffer.
* We xor the keys into the state variables before we use the PUT32
* macro as the macro might use its argument multiple times.
*/
#define PUT_OUTPUT( A,B,C,D, dst, xkey, koff ) \
A ^= xkey->K[ koff]; B ^= xkey->K[1+koff]; \
C ^= xkey->K[2+koff]; D ^= xkey->K[3+koff]; \
PUT32( A, dst ); PUT32( B, dst+ 4 ); \
PUT32( C, dst+8 ); PUT32( D, dst+12 )
/*
* Twofish block encryption
*
* Arguments:
* xkey expanded key array
* p 16 bytes of plaintext
* c 16 bytes in which to store the ciphertext
*/
void Twofish_encrypt( Twofish_key * xkey, Twofish_Byte p[16], Twofish_Byte c[16])
{
Twofish_UInt32 A,B,C,D,T0,T1; /* Working variables */
/* Get the four plaintext words xorred with the key */
GET_INPUT( p, A,B,C,D, xkey, 0 );
/* Do 8 cycles (= 16 rounds) */
ENCRYPT( A,B,C,D,T0,T1,xkey );
/* Store them with the final swap and the output whitening. */
PUT_OUTPUT( C,D,A,B, c, xkey, 4 );
}
/*
* Twofish block decryption.
*
* Arguments:
* xkey expanded key array
* p 16 bytes of plaintext
* c 16 bytes in which to store the ciphertext
*/
void Twofish_decrypt( Twofish_key * xkey, Twofish_Byte c[16], Twofish_Byte p[16])
{
Twofish_UInt32 A,B,C,D,T0,T1; /* Working variables */
/* Get the four plaintext words xorred with the key */
GET_INPUT( c, A,B,C,D, xkey, 4 );
/* Do 8 cycles (= 16 rounds) */
DECRYPT( A,B,C,D,T0,T1,xkey );
/* Store them with the final swap and the output whitening. */
PUT_OUTPUT( C,D,A,B, p, xkey, 0 );
}
/*
* Using the macros it is easy to make special routines for
* CBC mode, CTR mode etc. The only thing you might want to
* add is a XOR_PUT_OUTPUT which xors the outputs into the
* destinationa instead of overwriting the data. This requires
* a XOR_PUT32 macro as well, but that should all be trivial.
*
* I thought about including routines for the separate cipher
* modes here, but it is unclear which modes should be included,
* and each encryption or decryption routine takes up a lot of code space.
* Also, I don't have any test vectors for
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -