Академический Документы
Профессиональный Документы
Культура Документы
2 - CodeProject Page 1 of 57
Cipher EX V1.2
John Underhill, 25 Dec 2014 CPOL
4.94 (28 votes)
Introduction
What follows is the product of my study of several encryption
algorithms. I decided to write this library, out of a desire to
learn more about them, and encryption in general. I have in
the past adapted classes from popular libraries like Mono and
Bouncy Castle, but this time I wanted to write my own
implementations, ones that were optimized for the C#
language, and possibly faster, and more flexible than these
popular C# versions. As I was writing the base classes, I also
began thinking about various attack vectors, and how they
might be mitigated, and also how the existing primitives
might be improved upon from a security perspective.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 2 of 57
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 3 of 57
Library Components
The library contains the following components, as it evolves,
some will be added, some removed, and when possible,
changes will be made to improve upon performance, security
and documentation.
Encryption Engines
Base Algorithms
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 4 of 57
(256-1)
also makes brute force attacks practically impossible; 2
(1536-1)
compared to a minimum of 2 iterations. Another
advantage of the HX ciphers is that the number of diffusion
rounds, (transformation cycles within the rounds function), are
configurable independent of the initial key size.
Super Ciphers
Cipher Modes
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 5 of 57
function of the CBC mode. The project also includes ECB and
CFB modes that are not currently implemented.
Padding
Hash Algorithms
MAC
Queuing
Security
Utilities
Overview
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 6 of 57
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 7 of 57
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb0(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 8 of 57
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 9 of 57
The HX Ciphers
The three base ciphers all have something in common; they all
use the working key in a similar way; to change or 'whiten' the
state values to create a unique output, other than that, they
do not interact with the actual computational processes used
to transform the state. What that means is that how that
cipher key is expanded, (so long as it is done in a secure way),
does not directly impact the data transformation. Creating
that expanded key using a more secure means, like a hash
function, can increase the overall security of the cipher itself.
The other advantage is the key size; the minimum key size for
an HX cipher is the block size of the hash function (SHA512 =
128 bytes) + the IKM, or the HMAC key material (64 bytes). So
the key for these ciphers is a minimum of 192 bytes (1536
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 10 of 57
Super Ciphers
Super Ciphers have been around for a while, and there are a
number of different implementations of software that double,
or even triple encrypt an input using different ciphers and
keys. There are also implementations that use multiple
instances of the same cipher, (think Triple-DES). This is done
to extend key size and make the output more resistant to
some forms of Linear and Differential cryptanalysis. The
problem with this approach is that it is subject to a 'meet in
the middle attack'. Some theoretical models project that
decryption could be performed with little more computational
energy than brute forcing only one of the cipher instances.
One way to mitigate this attack, is instead of encrypting
successively with different cryptographic instances, it makes
more sense to combine the primitives at the rounds
processing level.
// rijndael round
R0 = T0[C0 >> 24] ^ T1[(byte)(C1 >> 16)] ^ T2[(byte)(C2
>> 8)] ^ T3[(byte)C3] ^ _exKey[keyCtr++];
R1 = T0[C1 >> 24] ^ T1[(byte)(C2 >> 16)] ^ T2[(byte)(C3
>> 8)] ^ T3[(byte)C0] ^ _exKey[keyCtr++];
R2 = T0[C2 >> 24] ^ T1[(byte)(C3 >> 16)] ^ T2[(byte)(C0
>> 8)] ^ T3[(byte)C1] ^ _exKey[keyCtr++];
R3 = T0[C3 >> 24] ^ T1[(byte)(C0 >> 16)] ^ T2[(byte)(C1
>> 8)] ^ T3[(byte)C2] ^ _exKey[keyCtr++];
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 11 of 57
Benchmarks
Speed tests were performed on an AMD ASD-3600 Quad-
Core, 4GB RAM (3.47 GB usable), compiled Release/Any CPU
on Windows 7.
Test is a transform of a byte array in a Monte Carlo method.
Sizes are in MBs (1000000 bytes). Time format sec.ms, key
sizes in bits. Rate is MB per minute.
HX series will have similar times, as they use the
same transformation engines.
CTR mode and CBC decrypt are run in parallel mode. CBC
encrypt is in linear (single processor) mode.
Highest rate was RDX with a 128 bit key: 6.052 GB per minute!
Block Ciphers
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 12 of 57
Stream Ciphers
RDX (Rijndael)
RDX is an implementation of the Rijndael encryption
algorithm, the same one used in the AES standard. What I
have done is to extend Rijndael so that it now accepts the
longer key length (512 bits). The extended key length
provides more security against attacks that attempt to brute
force the key, and also adds eight more rounds of diffusion.
The increased number of rounds brings the total from 14
rounds with a 256 bit key, to 22 rounds with the 512 bit key
size. These added passes through the transform further
disperse the input through row and column transpositions,
and XOR’s with a longer expanded key array.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 13 of 57
// rounds calculation
if (Nk == 16)
Nr = 22;
else if (Nb == 8 || Nk == 8)
Nr = 14;
else if (Nk == 6)
Nr = 12;
else
Nr = 10;
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 14 of 57
temp = SubByte(temp);
}
}
else
{
if (i % Nk == 0)
{
// round the key
UInt32 rot = (UInt32)((temp << 8) |
((temp >> 24) & 0xff));
// subbyte step
temp = SubByte(rot) ^ Rcon[i / Nk];
}
// step ik + 4
else if (Nk > 6 && (i % Nk) == 4)
{
temp = SubByte(temp);
}
}
// w[i-Nk] ^ w[i]
_exKey[i] = (UInt32)_exKey[i - Nk] ^ temp;
}
// inverse cipher
if (!Encryption)
{
// reverse key
for (int i = 0, k = keySize - Nb; i < k; i +=
Nb, k -= Nb)
{
for (int j = 0; j < Nb; j++)
{
UInt32 temp = _exKey[i + j];
_exKey[i + j] = _exKey[k + j];
_exKey[k + j] = temp;
}
}
// sbox inversion
for (int i = Nb; i < keySize - Nb; i++)
{
_exKey[i] = IT0[SBox[(_exKey[i] >> 24)]] ^
IT1[SBox[(byte)(_exKey[i] >> 16)]] ^
IT2[SBox[(byte)(_exKey[i] >> 8)]] ^
IT3[SBox[(byte)_exKey[i]]];
}
}
this.IsInitialized = true;
}
if (Nk == 16) Nr = 22
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 15 of 57
number of 32 bit words in the block size. A 512 bit user key
with a 16 byte block size will generate 92 working keys, or 184
working keys with a 32 byte block. A 256 bit key generates
60 or 120 working keys. A better dispersion ratio of user key
to expanded key size is achieved with the larger 512 bit key;
bytes(32 byte key: 32/240 and 32/480, 64 byte key: 64/368
and 64/736).
With a 512 bit key; Nk = 16, which would double the interval
between these additional dispersal steps, and create a weaker
expanded key. I have compensated for this by maintaining the
same intervals in the dispersion pattern; just as with a 256 bit
key every 8 keys, the Rcon ^ SubByte step executes, with the
SubByte step at the same alternating offset 4 interval.
Rijndael Transform:
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 16 of 57
// Round 0
R0 = (UInt32)((Input[InOffset] << 24) | (Input
[InOffset + 1] << 16) | (Input[InOffset + 2] << 8) |
Input[InOffset + 3]) ^ _exKey[keyCtr++];
R1 = (UInt32)((Input[InOffset + 4] << 24) | (Input
[InOffset + 5] << 16) | (Input[InOffset + 6] << 8) |
Input[InOffset + 7]) ^ _exKey[keyCtr++];
R2 = (UInt32)((Input[InOffset + 8] << 24) | (Input
[InOffset + 9] << 16) | (Input[InOffset + 10] << 8) |
Input[InOffset + 11]) ^ _exKey[keyCtr++];
R3 = (UInt32)((Input[InOffset + 12] << 24) | (Input
[InOffset + 13] << 16) | (Input[InOffset + 14] << 8) |
Input[InOffset + 15]) ^ _exKey[keyCtr++];
// Round 1
C0 = T0[R0 >> 24] ^ T1[(byte)(R1 >> 16)] ^ T2[(byte)
(R2 >> 8)] ^ T3[(byte)R3] ^ _exKey[keyCtr++];
C1 = T0[R1 >> 24] ^ T1[(byte)(R2 >> 16)] ^ T2[(byte)
(R3 >> 8)] ^ T3[(byte)R0] ^ _exKey[keyCtr++];
C2 = T0[R2 >> 24] ^ T1[(byte)(R3 >> 16)] ^ T2[(byte)
(R0 >> 8)] ^ T3[(byte)R1] ^ _exKey[keyCtr++];
C3 = T0[R3 >> 24] ^ T1[(byte)(R0 >> 16)] ^ T2[(byte)
(R1 >> 8)] ^ T3[(byte)R2] ^ _exKey[keyCtr++];
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 17 of 57
// Final Round
Output[OutOffset] = (byte)(SBox[C0 >> 24] ^ (byte)
(_exKey[keyCtr] >> 24));
Output[OutOffset + 1] = (byte)(SBox[(byte)(C1 >>
16)] ^ (byte)(_exKey[keyCtr] >> 16));
Output[OutOffset + 2] = (byte)(SBox[(byte)(C2 >> 8)]
^ (byte)(_exKey[keyCtr] >> 8));
Output[OutOffset + 3] = (byte)(SBox[(byte)C3] ^
(byte)_exKey[keyCtr++]);
As you can see this version uses the byte oriented approach
and is optimized by combining the SubBytes and
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 18 of 57
// Round 0
R0 = (UInt32)((Input[InOffset] << 24) | (Input
[InOffset + 1] << 16) | (Input[InOffset + 2] << 8) |
Input[InOffset + 3]) ^ _exKey[keyCtr++];
R1 = (UInt32)((Input[InOffset + 4] << 24) | (Input
[InOffset + 5] << 16) | (Input[InOffset + 6] << 8) |
Input[InOffset + 7]) ^ _exKey[keyCtr++];
R2 = (UInt32)((Input[InOffset + 8] << 24) | (Input
[InOffset + 9] << 16) | (Input[InOffset + 10] << 8) |
Input[InOffset + 11]) ^ _exKey[keyCtr++];
R3 = (UInt32)((Input[InOffset + 12] << 24) | (Input
[InOffset + 13] << 16) | (Input[InOffset + 14] << 8) |
Input[InOffset + 15]) ^ _exKey[keyCtr++];
R4 = (UInt32)((Input[InOffset + 16] << 24) | (Input
[InOffset + 17] << 16) | (Input[InOffset + 18] << 8) |
Input[InOffset + 19]) ^ _exKey[keyCtr++];
R5 = (UInt32)((Input[InOffset + 20] << 24) | (Input
[InOffset + 21] << 16) | (Input[InOffset + 22] << 8) |
Input[InOffset + 23]) ^ _exKey[keyCtr++];
R6 = (UInt32)((Input[InOffset + 24] << 24) | (Input
[InOffset + 25] << 16) | (Input[InOffset + 26] << 8) |
Input[InOffset + 27]) ^ _exKey[keyCtr++];
R7 = (UInt32)((Input[InOffset + 28] << 24) | (Input
[InOffset + 29] << 16) | (Input[InOffset + 30] << 8) |
Input[InOffset + 31]) ^ _exKey[keyCtr++];
// Round 1
C0 = T0[R0 >> 24] ^ T1[(byte)(R1 >> 16)] ^ T2[(byte)
(R3 >> 8)] ^ T3[(byte)R4] ^ _exKey[keyCtr++];
C1 = T0[R1 >> 24] ^ T1[(byte)(R2 >> 16)] ^ T2[(byte)
(R4 >> 8)] ^ T3[(byte)R5] ^ _exKey[keyCtr++];
C2 = T0[R2 >> 24] ^ T1[(byte)(R3 >> 16)] ^ T2[(byte)
(R5 >> 8)] ^ T3[(byte)R6] ^ _exKey[keyCtr++];
C3 = T0[R3 >> 24] ^ T1[(byte)(R4 >> 16)] ^ T2[(byte)
(R6 >> 8)] ^ T3[(byte)R7] ^ _exKey[keyCtr++];
C4 = T0[R4 >> 24] ^ T1[(byte)(R5 >> 16)] ^ T2[(byte)
(R7 >> 8)] ^ T3[(byte)R0] ^ _exKey[keyCtr++];
C5 = T0[R5 >> 24] ^ T1[(byte)(R6 >> 16)] ^ T2[(byte)
(R0 >> 8)] ^ T3[(byte)R1] ^ _exKey[keyCtr++];
C6 = T0[R6 >> 24] ^ T1[(byte)(R7 >> 16)] ^ T2[(byte)
(R1 >> 8)] ^ T3[(byte)R2] ^ _exKey[keyCtr++];
C7 = T0[R7 >> 24] ^ T1[(byte)(R0 >> 16)] ^ T2[(byte)
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 19 of 57
// rounds loop
while (keyCtr < _exKey.Length - 8)
{
R0 = T0[C0 >> 24] ^ T1[(byte)(C1 >> 16)] ^ T2
[(byte)(C3 >> 8)] ^ T3[(byte)C4] ^ _exKey[keyCtr++];
R1 = T0[C1 >> 24] ^ T1[(byte)(C2 >> 16)] ^ T2
[(byte)(C4 >> 8)] ^ T3[(byte)C5] ^ _exKey[keyCtr++];
R2 = T0[C2 >> 24] ^ T1[(byte)(C3 >> 16)] ^ T2
[(byte)(C5 >> 8)] ^ T3[(byte)C6] ^ _exKey[keyCtr++];
R3 = T0[C3 >> 24] ^ T1[(byte)(C4 >> 16)] ^ T2
[(byte)(C6 >> 8)] ^ T3[(byte)C7] ^ _exKey[keyCtr++];
R4 = T0[C4 >> 24] ^ T1[(byte)(C5 >> 16)] ^ T2
[(byte)(C7 >> 8)] ^ T3[(byte)C0] ^ _exKey[keyCtr++];
R5 = T0[C5 >> 24] ^ T1[(byte)(C6 >> 16)] ^ T2
[(byte)(C0 >> 8)] ^ T3[(byte)C1] ^ _exKey[keyCtr++];
R6 = T0[C6 >> 24] ^ T1[(byte)(C7 >> 16)] ^ T2
[(byte)(C1 >> 8)] ^ T3[(byte)C2] ^ _exKey[keyCtr++];
R7 = T0[C7 >> 24] ^ T1[(byte)(C0 >> 16)] ^ T2
[(byte)(C2 >> 8)] ^ T3[(byte)C3] ^ _exKey[keyCtr++];
// Final Round
Output[OutOffset] = (byte)(SBox[C0 >> 24] ^ (byte)
(_exKey[keyCtr] >> 24));
Output[OutOffset + 1] = (byte)(SBox[(byte)(C1 >>
16)] ^ (byte)(_exKey[keyCtr] >> 16));
Output[OutOffset + 2] = (byte)(SBox[(byte)(C3 >> 8)]
^ (byte)(_exKey[keyCtr] >> 8));
Output[OutOffset + 3] = (byte)(SBox[(byte)C4] ^
(byte)_exKey[keyCtr++]);
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 20 of 57
"This standard explicitly defines the allowed values for the key
length (Nk), block size (Nb), and number of rounds (Nr).
However, future reaffirmations of this standard could include
changes or additions to the allowed values for those
parameters. Therefore, implementers may choose to design
their AES implementations with future flexibility in mind."
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 21 of 57
API
Properties:
Get/Set Unit block size of internal cipher
int BlockSize { get; set; }
Public Methods:
Constructor: Initialize the class
BlockSize: Algorithm input block size
public RDX(int BlockSize)
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 22 of 57
RSX
RSX is a hybrid of the Rijndael and Serpent encryption
algorithms. Most encryption algorithms can be thought of as
having two main parts; the key schedule, and
the transformation algorithm. The key schedule takes a small
amount of initial entropy, (the user key), and expands it into a
larger working array that is used in the rounds function.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 23 of 57
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 24 of 57
if (padSize == 16)
{
// create 32 byte key pre-key
// step 2: rotate k into w(k) ints
for (int i = 8; i < 16; i++)
Wp[i] = RotateLeft((uint)(Wp[i - 8] ^ Wp[i
- 5] ^ Wp[i - 3] ^ Wp[i - 1] ^ PHI ^ (i - 8)), 11);
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 25 of 57
// last rounds
Sb3(ref Wk[ct++], ref Wk[ct++], ref Wk[ct++], ref Wk
[ct++]);
Sb2(ref Wk[ct++], ref Wk[ct++], ref Wk[ct++], ref Wk
[ct++]);
Sb1(ref Wk[ct++], ref Wk[ct++], ref Wk[ct++], ref Wk
[ct++]);
Sb0(ref Wk[ct++], ref Wk[ct++], ref Wk[ct++], ref Wk
[ct++]);
Sb7(ref Wk[ct++], ref Wk[ct++], ref Wk[ct++], ref Wk
[ct++]);
Sb6(ref Wk[ct++], ref Wk[ct++], ref Wk[ct++], ref Wk
[ct++]);
return Wk;
}
RHX
The Key Schedule
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 26 of 57
// hkdf input
byte[] hkdfKey = new byte[IKM_SIZE];
byte[] hkdfSalt = new byte[saltSize];
// inverse cipher
if (!Encryption)
{
// reverse key
for (int i = 0, k = keySize - Nb; i < k; i +=
Nb, k -= Nb)
{
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 27 of 57
this.IsInitialized = true;
return exKey;
}
Now, some people might balk at the size of the key, but what
is 192 bytes by todays standards? My 256 GB memory stick
could hold 1,333,333,333 keys, a single 5 terabyte drive could
hold multiple keys for every person on earth.. with wire
speeds and storage capabilities growing constantly, what is
the point in keeping keys so small, particularly when a larger
key, generated with a cryptographically strong method, can
dramatically increase the security of the cipher?
SPX (Serpent)
This is an implementation of the Serpent block cipher. Just as
with Rijndael, I strove to create a more flexible diffusion
engine and key schedule. I modified the key schedule so that
it can produce the required number of working keys when
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 28 of 57
if (padSize == 16)
{
// 32 byte key
// step 2: rotate k into w(k) ints
for (int i = 8; i < 16; i++)
Wp[i] = RotateLeft((Wp[i - 8] ^ Wp[i - 5] ^
Wp[i - 3] ^ Wp[i - 1] ^ PHI ^ (i - 8)), 11);
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 29 of 57
// last round
Sb3(ref Wk[cnt++], ref Wk[cnt++], ref Wk[cnt++],
ref Wk[cnt]);
return Wk;
}
As you can see, this is similar to the key schedule in RSX, and
in fact their output is equivalent at the byte level. This
schedule however calculates the working key to a size
required by Serpents rounds processing.
SHX
SHX, just like RHX uses an HKDF generator to expand the user
supplied key into a working key integer array. It also takes a
user defined number of rounds between 32 (the normal
number of rounds), all the way up to 128 rounds in 8 round
sets. A round count of 40 or 48 is more than sufficient, as
theoretical attacks to date are only able to break up to 12
rounds and would require an enormous amount of memory
and processing power.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 30 of 57
// input round
Int32 R0 = BytesToWord(Input, InOffset + 12);
Int32 R1 = BytesToWord(Input, InOffset + 8);
Int32 R2 = BytesToWord(Input, InOffset + 4);
Int32 R3 = BytesToWord(Input, InOffset);
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb1(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb2(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
;
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb3(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb4(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb5(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 31 of 57
R3 ^= _exKey[keyCtr++];
Sb6(ref R0, ref R1, ref R2, ref R3);
LinearTransform(ref R0, ref R1, ref R2, ref R3);
R0 ^= _exKey[keyCtr++];
R1 ^= _exKey[keyCtr++];
R2 ^= _exKey[keyCtr++];
R3 ^= _exKey[keyCtr++];
Sb7(ref R0, ref R1, ref R2, ref R3);
// last round
WordToBytes(_exKey[keyCtr++] ^ R0, Output,
OutOffset + 12);
WordToBytes(_exKey[keyCtr++] ^ R1, Output, OutOffset
+ 8);
WordToBytes(_exKey[keyCtr++] ^ R2, Output, OutOffset
+ 4);
WordToBytes(_exKey[keyCtr] ^ R3, Output, OutOffset);
}
TFX (Twofish)
This was an interesting cipher, with methods like a keyed
S-Box and a complex algebraic description, quite a bit
different from Sepent or Rijndael. As such, it required more
consideration in how an extended key size could be
implemented. What I did was similar to the other ciphers;
to use patterns from the existing function, and extend those
patterns in a way that best leverages the larger cipher key
while maintaining a consistancy the original design.
keyCtr = 0;
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 32 of 57
Y0 = Y1 = Y2 = Y3 = keyCtr;
// 512 key
if (Key.Length == 64)
{
Y0 = (byte)Q1[Y0] ^ sbKey[28];
Y1 = (byte)Q0[Y1] ^ sbKey[29];
Y2 = (byte)Q0[Y2] ^ sbKey[30];
Y3 = (byte)Q1[Y3] ^ sbKey[31];
Y0 = (byte)Q1[Y0] ^ sbKey[24];
Y1 = (byte)Q1[Y1] ^ sbKey[25];
Y2 = (byte)Q0[Y2] ^ sbKey[26];
Y3 = (byte)Q0[Y3] ^ sbKey[27];
Y0 = (byte)Q0[Y0] ^ sbKey[20];
Y1 = (byte)Q1[Y1] ^ sbKey[21];
Y2 = (byte)Q1[Y2] ^ sbKey[22];
Y3 = (byte)Q0[Y3] ^ sbKey[23];
Y0 = (byte)Q0[Y0] ^ sbKey[16];
Y1 = (byte)Q0[Y1] ^ sbKey[17];
Y2 = (byte)Q1[Y2] ^ sbKey[18];
Y3 = (byte)Q1[Y3] ^ sbKey[19];
}
// 256 key
if (Key.Length > 24)
{
Y0 = (byte)Q1[Y0] ^ sbKey[12];
Y1 = (byte)Q0[Y1] ^ sbKey[13];
Y2 = (byte)Q0[Y2] ^ sbKey[14];
Y3 = (byte)Q1[Y3] ^ sbKey[15];
}
// 192 key
if (Key.Length > 16)
{
Y0 = (byte)Q1[Y0] ^ sbKey[8];
Y1 = (byte)Q1[Y1] ^ sbKey[9];
Y2 = (byte)Q0[Y2] ^ sbKey[10];
Y3 = (byte)Q0[Y3] ^ sbKey[11];
}
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 33 of 57
// key processed
this.IsInitialized = true;
return wK;
}
The first loop in the method copies the key material into two
arrays okm and ekm, used in the main while loop as working
key material. This is the first extension of the function. With a
256 bit key, the 32 bytes are copied into two 4 member
integer arrays. With a 512 bit key, these two integer arrays are
extended to 8 member arrays, using the full width of the user
supplied key material. The S-Box key sbKey, is extended as
well, from 16 to 32 bytes after undergoing an MDS (Maximum
Distance Separable) like transformation through MDSEncode
().
At the top of the main loop, the working key is created; using
the F32 function. The keyed S-Box member is then calculated
using a lookup into one of two key dependant s-boxes (QO
and Q1), XORd with a member of sbKey, the S-Box keying
material. This is the second extension of the function. The key
length clause determines how many times this shifting
permutation of Q s-box products and s-box keys occurs. With
a 256 bit key it happens three times, with the last stage
creating four S-Box keys by passing the XORd product of the
Q s-box lookups through an MDS matrix. With a 512 bit key,
you can see that the permutation has been appended by 16
bytes, adding some the additional entropy of the longer
cipher key, and using the same pattern of alternating key
dependant s-box lookups.
THX
The transform for both THX and TFX are identical. So no
matter how the working keys are generated, or what size they
are, the algebraic formula used by a round is the same.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 34 of 57
X2 ^= T0 + T1 + _exKey[keyCtr++];
X2 = (Int32)((UInt32)X2 >> 1) | X2 << 31;
X3 = (X3 << 1 | (Int32)((UInt32)X3 >> 31)) ^ (T0
+ 2 * T1 + _exKey[keyCtr++]);
T0 = Fe0(X2);
T1 = Fe3(X3);
X0 ^= T0 + T1 + _exKey[keyCtr++];
X0 = (Int32)((UInt32)X0 >> 1) | X0 << 31;
X1 = (X1 << 1 | (Int32)((UInt32)X1 >> 31)) ^ (T0
+ 2 * T1 + _exKey[keyCtr++]);
}
keyCtr = 4;
WordToBytes(X2 ^ _exKey[keyCtr++], Output,
OutOffset);
WordToBytes(X3 ^ _exKey[keyCtr++], Output, OutOffset
+ 4);
WordToBytes(X0 ^ _exKey[keyCtr++], Output, OutOffset
+ 8);
WordToBytes(X1 ^ _exKey[keyCtr], Output, OutOffset
+ 12);
}
RSM
This is Rijndael and Serpent merged within the rounds
function. The key scheduler uses HKDF, and is similar to an HX
series implementation. The transform combines the two
ciphers in the rounds processing loop. First a round of
Serpent; which is a pass through one of eight bit slicing
S-Boxes and a linear transform, then a full round of Rijndael,
where the working key is added to the state.
// Round 0
R0 = (UInt32)((Input[InOffset] << 24) | (Input
[InOffset + 1] << 16) | (Input[InOffset + 2] << 8) |
Input[InOffset + 3]) ^ _exKey[keyCtr++];
R1 = (UInt32)((Input[InOffset + 4] << 24) | (Input
[InOffset + 5] << 16) | (Input[InOffset + 6] << 8) |
Input[InOffset + 7]) ^ _exKey[keyCtr++];
R2 = (UInt32)((Input[InOffset + 8] << 24) | (Input
[InOffset + 9] << 16) | (Input[InOffset + 10] << 8) |
Input[InOffset + 11]) ^ _exKey[keyCtr++];
R3 = (UInt32)((Input[InOffset + 12] << 24) | (Input
[InOffset + 13] << 16) | (Input[InOffset + 14] << 8) |
Input[InOffset + 15]) ^ _exKey[keyCtr++];
// Round 1
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 35 of 57
// rijndael round
R0 = T0[C0 >> 24] ^ T1[(byte)(C1 >> 16)] ^ T2
[(byte)(C2 >> 8)] ^ T3[(byte)C3] ^ _exKey[keyCtr++];
R1 = T0[C1 >> 24] ^ T1[(byte)(C2 >> 16)] ^ T2
[(byte)(C3 >> 8)] ^ T3[(byte)C0] ^ _exKey[keyCtr++];
R2 = T0[C2 >> 24] ^ T1[(byte)(C3 >> 16)] ^ T2
[(byte)(C0 >> 8)] ^ T3[(byte)C1] ^ _exKey[keyCtr++];
R3 = T0[C3 >> 24] ^ T1[(byte)(C0 >> 16)] ^ T2
[(byte)(C1 >> 8)] ^ T3[(byte)C2] ^ _exKey[keyCtr++];
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 36 of 57
// Final Round
Output[OutOffset] = (byte)(SBox[C0 >> 24] ^ (byte)
(_exKey[keyCtr] >> 24));
Output[OutOffset + 1] = (byte)(SBox[(byte)(C1 >>
16)] ^ (byte)(_exKey[keyCtr] >> 16));
Output[OutOffset + 2] = (byte)(SBox[(byte)(C2 >> 8)]
^ (byte)(_exKey[keyCtr] >> 8));
Output[OutOffset + 3] = (byte)(SBox[(byte)C3] ^
(byte)_exKey[keyCtr++]);
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 37 of 57
TSM
This is Twofish and Serpent merged during rounds processing.
Is an invertible cipher that combines the ciphers in 4 round
loop cycles; two rounds of each:
keyCtr = 8;
int index = 0;
// twofish round
T0 = Fe0(X0);
T1 = Fe3(X1);
X2 ^= T0 + T1 + _exKey[keyCtr++];
X2 = (Int32)((UInt32)X2 >> 1) | X2 << 31;
X3 = (X3 << 1 | (Int32)((UInt32)X3 >> 31)) ^ (T0
+ 2 * T1 + _exKey[keyCtr++]);
// serpent round
SuperBox(index++, ref X0, ref X1, ref X2, ref
X3);
LinearTransform(ref X0, ref X1, ref X2, ref X3);
// twofish round
T0 = Fe0(X2);
T1 = Fe3(X3);
X0 ^= T0 + T1 + _exKey[keyCtr++];
X0 = (Int32)((UInt32)X0 >> 1) | X0 << 31;
X1 = (X1 << 1 | (Int32)((UInt32)X1 >> 31)) ^ (T0
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 38 of 57
+ 2 * T1 + _exKey[keyCtr++]);
if (index > 7)
index = 0;
}
keyCtr = 4;
WordToBytes(X2 ^ _exKey[keyCtr++], Output,
OutOffset);
WordToBytes(X3 ^ _exKey[keyCtr++], Output, OutOffset
+ 4);
WordToBytes(X0 ^ _exKey[keyCtr++], Output, OutOffset
+ 8);
WordToBytes(X1 ^ _exKey[keyCtr], Output, OutOffset
+ 12);
}
Fusion
I think this is my favorite cipher in the library. If I had the
cure for baldness on my computer, this is what I would use to
encrypt it.. Fusion is a parallelized stream cipher; it it encrypts
a random 128 bit counter to create a key stream, used to
transform input data. The pseudo random generator used to
create the key stream is a combination of the Rijndael and
Twofish ciphers:
keyCtr = 8;
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 39 of 57
// twofish round
M0 = Fe0(X0);
M1 = Fe3(X1);
X2 ^= M0 + M1 + _exKey[keyCtr++];
X2 = (Int32)((UInt32)X2 >> 1) | X2 << 31;
X3 = (X3 << 1 | (Int32)((UInt32)X3 >> 31)) ^ (M0
+ 2 * M1 + _exKey[keyCtr++]);
M0 = Fe0(X6);
M1 = Fe3(X7);
X4 ^= M0 + M1 + _exKey[keyCtr++];
X4 = (Int32)((UInt32)X4 >> 1) | X4 << 31;
X5 = (X5 << 1 | (Int32)((UInt32)X5 >> 31)) ^ (M0
+ 2 * M1 + _exKey[keyCtr++]);
}
keyCtr = 4;
WordToBytes(X2 ^ _exKey[keyCtr++], Output,
OutOffset);
WordToBytes(X3 ^ _exKey[keyCtr++], Output, OutOffset
+ 4);
WordToBytes(X0 ^ _exKey[keyCtr++], Output, OutOffset
+ 8);
WordToBytes(X1 ^ _exKey[keyCtr], Output, OutOffset
+ 12);
}
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 40 of 57
// copy state
Buffer.BlockCopy(_State, 0, stateTemp, 0, stateLen
* 4);
DCS
DCS is a stream cipher that uses two Rijndael streams in an
AES configuration; that is a 256 bit key and 128 bit block size.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 41 of 57
Initialization
The Init() method tests for weak and invalid keys; this is
because DCS requires a strong key, one that does not contain
repeating sequences or high numbers of repeating bytes. Keys
should be created with a hash function or PRNG as
demonstrated in the class: Crypto\Helpers\KeyGenerator.cs.
// copy seed
Buffer.BlockCopy(Seed, 0, _seedBuffer, 0,
_seedBuffer.Length);
if (keyBuffer1.SequenceEqual(keyBuffer2))
throw new ArgumentException("Bad seed! Seed
material is a repeating sequence.");
if (_ctrBuffer1.SequenceEqual(_ctrBuffer2))
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 42 of 57
Random Generation
// copy to output
if (i != lastBlock)
{
// copy transform to output
Buffer.BlockCopy(randBlock1, 0, outputData,
i, BLOCK_SIZE);
}
else
{
// copy last block
int finalSize = (Size % BLOCK_SIZE) == 0 ?
BLOCK_SIZE : (Size % BLOCK_SIZE);
Buffer.BlockCopy(randBlock1, 0, outputData,
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 43 of 57
i, finalSize);
}
// increment counters
Increment(Ctr1);
Increment(Ctr2);
}
return outputData;
}
Parallel Processing
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 44 of 57
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 45 of 57
variables
Buffer.BlockCopy(counters[count - 1], 0,
_ctrBuffer1, 0, _ctrBuffer1.Length);
Buffer.BlockCopy(counters[dimensions - 1], 0,
_ctrBuffer2, 0, _ctrBuffer2.Length);
}
}
API
Properties:
Get/Set Automatic processor parallelization
public bool IsParallel { get; set; }
Public Methods:
Constructor: Initialize the class
public DCS()
Example Implementation
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 46 of 57
/// <summary>
/// Get a random seed value
/// </summary>
/// <returns>64 bytes of p-rand</returns>
internal static byte[] GetSeed64()
{
byte[] data = GetRngBytes(256);
byte[] key = GetRngBytes(64);
/// <summary>
/// Get a random seed value using an SHA3-512 HMAC
/// </summary>
/// <returns>64 bytes of p-rand</returns>
internal static byte[] GetSeed64Ng()
{
byte[] data = GetRngBytes(144); // 2x block per
Nist sp800-90b
byte[] key = GetRngBytes(64); // key size per
rfc 2104
Headers
Key Header
Both the key and message contain headers that provide some
information to the encryptor. The key header contains fields
that are used by the encryptor to determine settings;
algorithm, cipher mode, padding scheme and block size. It
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 47 of 57
[Serializable]
[StructLayout(LayoutKind.Sequential)]
internal struct KeyHeaderStruct
{
internal Int32 Engine;
internal Int32 KeySize;
internal Int32 IvSize;
internal Int32 CipherMode;
internal Int32 PaddingMode;
internal Int32 BlockSize;
internal Int32 RoundCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
16)]
internal byte[] KeyID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
16)]
internal byte[] ExtRandom;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
64)]
internal byte[] MessageKey;
Message Header
The message header contains the identity field of the key
used to encrypt the message, a 16 byte field that contains the
encrypted file extension, and a 64 byte value that stores the
HMAC hash of the cipher-text.
[Serializable]
[StructLayout(LayoutKind.Sequential)]
internal struct MessageHeaderStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
16)]
internal byte[] MessageID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
16)]
public byte[] Extension;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
64)]
internal byte[] MessageHash;
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 48 of 57
Message Authentication
Creating a Checksum
if (bytesRead > 0)
hmac.BlockUpdate(buffer, 0, bytesRead);
hmac.DoFinal(chkSum, 0);
}
return chkSum;
}
}
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 49 of 57
Verifying a File
To test the file, the hash key is extracted from the key file, a
new HMAC hash code is calculated, and the two are
compared for equality.
Processing RDX/RSX
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 50 of 57
if (bytesTotal % this.ProgressInterval
== 0)
CalculateProgress(bytesTotal);
}
if (bytesRead > 0)
{
if (bytesRead < this.BlockSize)
Padding.AddPadding(inputBuffer,
(int)bytesRead);
this.Cipher.Transform(inputBuffer,
outputBuffer);
outputWriter.Write(outputBuffer);
CalculateProgress(bytesTotal +
bytesRead);
}
}
}
}
Processing DCS
if (Header != null)
outputWriter.Write(Header.ToArray());
else
inputReader.BaseStream.Position =
MessageHeader.GetHeaderSize;
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 51 of 57
if (bytesTotal %
this.ProgressInterval == 0)
CalculateProgress(bytesTotal);
}
if (bytesRead > 0)
{
outputBuffer = new byte[bytesRead];
dcs.Transform(inputBuffer,
outputBuffer);
outputWriter.Write(outputBuffer);
CalculateProgress(bytesTotal +
bytesRead);
}
}
}
}
}
With this example of DCS the input size is set to 4096 bytes.
Depending on if encryption or decryption is being used, the
message header is either written to the output array or the
input file pointer is moved to the end of the header. The input
is then transformed through the while loop. Because this is a
stream cipher, no padding is required, and the last data
segment is simply transformed and written to the output file.
if (Header != null)
outputWriter.Write(Header.ToArray());
else
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 52 of 57
inputReader.BaseStream.Position =
MessageHeader.GetHeaderSize;
if (bytesTotal % this.ProgressInterval
== 0)
CalculateProgress(bytesTotal);
}
if (bytesRead > 0)
{
outputBuffer = new byte[blockSize];
this.Cipher.Transform(inputBuffer,
outputBuffer);
outputWriter.Write(outputBuffer, 0,
(int)bytesRead);
CalculateProgress(bytesTotal +
bytesRead);
}
}
}
}
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 53 of 57
Tests
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 54 of 57
Updates
• November 21, 2004: Added SHX, RHX, SPX, ChaCha and
Salsa implementations
• November 23: Various performance optimizations
added.
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 55 of 57
Conclusion
In the spring of 1946 work on the ENIAC computer was
completed. Newspapers around the globe heralded it as the
“Super Brain” and the “Einstein Machine”. It was the most
powerful computer ever built; and had more than 17,000
vacuum tubes, 7,200 crystal diodes, 1,500 relays, 70,000
resistors, 10,000 capacitors and around 5 million hand-
soldered joints. It weighed 30 tons, took up 1800 square feet,
and consumed 150 kW of power. It was capable of calculating
an astounding 385 multiplication operations per second.
Imagine that you were one of the designers, out for a few
pints with fellow engineers and scientists, and you proposed
that in just 25 years, anyone could walk into a Sears store and
buy a 10 dollar portable calculator with thousands of times
the computational power and speed. I think you would have
been greeted with much skepticism; ‘impossible’, ‘infeasible’,
‘transistors and circuit pathways cannot be made that small’..
and you would have been subjected to a barrage of scientific
theories positing that such a thing could never happen.. at
least, not for a hundred years or so..
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 56 of 57
Cheers,
John
License
This article, along with any associated source code and files, is
licensed under The Code Project Open License (CPOL)
Share
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014
Cipher EX V1.2 - CodeProject Page 57 of 57
John Underhill
Network Administrator vtdev.com
Canada
Permalink | Advertise | Privacy | Terms of Use | Mobile Article Copyright 2014 by John Underhill
Web04 | 2.8.141223.1 | Last Updated 25 Dec 2014 Everything else Copyright © CodeProject, 1999-2014
http://www.codeproject.com/Articles/828477/Cipher-EX-V?display=Print 26.12.2014