mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-11 20:35:50 +00:00
49ed8cb93a
has been tested on this checkin. Added make target "rijndael_tables", which will generate rijndael32.tab.
783 lines
25 KiB
C
783 lines
25 KiB
C
/*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Netscape security libraries.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU General Public License Version 2 or later (the
|
|
* "GPL"), in which case the provisions of the GPL are applicable
|
|
* instead of those above. If you wish to allow use of your
|
|
* version of this file only under the terms of the GPL and not to
|
|
* allow others to use your version of this file under the MPL,
|
|
* indicate your decision by deleting the provisions above and
|
|
* replace them with the notice and other provisions required by
|
|
* the GPL. If you do not delete the provisions above, a recipient
|
|
* may use your version of this file under either the MPL or the
|
|
* GPL.
|
|
*
|
|
* $Id: rijndael.c,v 1.1 2000/12/19 23:43:09 mcgreer%netscape.com Exp $
|
|
*/
|
|
|
|
#include "prerr.h"
|
|
#include "secerr.h"
|
|
|
|
#include "prtypes.h"
|
|
#include "blapi.h"
|
|
#include "rijndael.h"
|
|
|
|
#ifndef RIJNDAEL_NO_TABLES
|
|
/* includes S**-1, Rcon, T0, T1, T2, T3, T0**-1, T1**-1, T2**-1, T3**-1 */
|
|
#include "rijndael32.tab"
|
|
#endif
|
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
#define SBOX(b) ((PRUint8)_T3[b])
|
|
#else
|
|
#define SBOX(b) ((PRUint8)_T1[b])
|
|
#endif
|
|
#define SBOXINV(b) (_SInv[b])
|
|
|
|
#ifndef RIJNDAEL_NO_TABLES
|
|
/* index the tables directly */
|
|
#define T0(i) _T0[i]
|
|
#define T1(i) _T1[i]
|
|
#define T2(i) _T2[i]
|
|
#define T3(i) _T3[i]
|
|
#define TInv0(i) _TInv0[i]
|
|
#define TInv1(i) _TInv1[i]
|
|
#define TInv2(i) _TInv2[i]
|
|
#define TInv3(i) _TInv3[i]
|
|
#define ME(i) _ME[i]
|
|
#define M9(i) _M9[i]
|
|
#define MB(i) _MB[i]
|
|
#define MD(i) _MD[i]
|
|
#define IMXC0(b) _IMXC0[b]
|
|
#define IMXC1(b) _IMXC1[b]
|
|
#define IMXC2(b) _IMXC2[b]
|
|
#define IMXC3(b) _IMXC3[b]
|
|
#else
|
|
/* generate the tables on the fly */
|
|
/* XXX not finished, just here for fun */
|
|
#define XTIME(a) \
|
|
((a & 0x80) ? ((a << 1) ^ 0x1b) : (a << 1))
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
#define WORD4(b0, b1, b2, b3) \
|
|
(((b3) << 24) | ((b2) << 16) | ((b1) << 8) | (b0))
|
|
#else
|
|
#define WORD4(b0, b1, b2, b3) \
|
|
(((b0) << 24) | ((b1) << 16) | ((b2) << 8) | (b3))
|
|
#endif
|
|
#define T0(i) \
|
|
(WORD4( XTIME(SBOX(i)), SBOX(i), SBOX(i), XTIME(SBOX(i)) ^ SBOX(i) ))
|
|
#define T1(i) \
|
|
(WORD4( XTIME(SBOX(i)) ^ SBOX(i), XTIME(SBOX(i)), SBOX(i), SBOX(i) ))
|
|
#define T2(i) \
|
|
(WORD4( SBOX(i), XTIME(SBOX(i)) ^ SBOX(i), XTIME(SBOX(i)), SBOX(i) ))
|
|
#define T3(i) \
|
|
(WORD4( SBOX(i), SBOX(i), XTIME(SBOX(i)) ^ SBOX(i), XTIME(SBOX(i)) ))
|
|
#endif
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Stuff related to the Rijndael key schedule
|
|
*
|
|
*************************************************************************/
|
|
|
|
#define SUBBYTE(w) \
|
|
((SBOX((w >> 24) & 0xff) << 24) | \
|
|
(SBOX((w >> 16) & 0xff) << 16) | \
|
|
(SBOX((w >> 8) & 0xff) << 8) | \
|
|
(SBOX((w ) & 0xff) ))
|
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
#define ROTBYTE(b) \
|
|
((b >> 8) | (b << 24))
|
|
#else
|
|
#define ROTBYTE(b) \
|
|
((b << 8) | (b >> 24))
|
|
#endif
|
|
|
|
/* rijndael_key_expansion7
|
|
*
|
|
* Generate the expanded key from the key input by the user.
|
|
* XXX
|
|
* Nk == 7 (224 key bits) is a weird case. Since Nk > 6, an added SubByte
|
|
* transformation is done periodically. The period is every 4 bytes, and
|
|
* since 7%4 != 0 this happens at different times for each key word (unlike
|
|
* Nk == 8 where it happens twice in every key word, in the same positions).
|
|
* For now, I'm implementing this case "dumbly", w/o any unrolling.
|
|
*/
|
|
static SECStatus
|
|
rijndael_key_expansion7(AESContext *cx, unsigned char *key, unsigned int Nk)
|
|
{
|
|
int i;
|
|
PRUint32 *W;
|
|
PRUint32 *pW;
|
|
PRUint32 tmp;
|
|
W = cx->expandedKey;
|
|
/* 1. the first Nk words contain the cipher key */
|
|
memcpy(W, key, Nk * 4);
|
|
i = Nk;
|
|
/* 2. loop until full expanded key is obtained */
|
|
pW = W + i - 1;
|
|
for (; i < cx->Nb * (cx->Nr + 1); ++i) {
|
|
tmp = *pW++;
|
|
if (i % Nk == 0)
|
|
tmp = SUBBYTE(ROTBYTE(tmp)) ^ Rcon[i / Nk - 1];
|
|
else if (i % Nk == 4)
|
|
tmp = SUBBYTE(tmp);
|
|
*pW = W[i - Nk] ^ tmp;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* rijndael_key_expansion
|
|
*
|
|
* Generate the expanded key from the key input by the user.
|
|
*/
|
|
static SECStatus
|
|
rijndael_key_expansion(AESContext *cx, unsigned char *key, unsigned int Nk)
|
|
{
|
|
int i;
|
|
PRUint32 *W;
|
|
PRUint32 *pW;
|
|
PRUint32 tmp;
|
|
int round_key_words = cx->Nb * (cx->Nr + 1);
|
|
if (Nk == 7)
|
|
return rijndael_key_expansion7(cx, key, Nk);
|
|
W = cx->expandedKey;
|
|
/* The first Nk words contain the input cipher key */
|
|
memcpy(W, key, Nk * 4);
|
|
i = Nk;
|
|
pW = W + i - 1;
|
|
/* Loop over all sets of Nk words, except the last */
|
|
while (i < round_key_words - Nk) {
|
|
tmp = *pW++;
|
|
tmp = SUBBYTE(ROTBYTE(tmp)) ^ Rcon[i / Nk - 1];
|
|
*pW = W[i++ - Nk] ^ tmp;
|
|
tmp = *pW++; *pW = W[i++ - Nk] ^ tmp;
|
|
tmp = *pW++; *pW = W[i++ - Nk] ^ tmp;
|
|
tmp = *pW++; *pW = W[i++ - Nk] ^ tmp;
|
|
if (Nk == 4)
|
|
continue;
|
|
switch (Nk) {
|
|
case 8: tmp = *pW++; tmp = SUBBYTE(tmp); *pW = W[i++ - Nk] ^ tmp;
|
|
case 7: tmp = *pW++; *pW = W[i++ - Nk] ^ tmp;
|
|
case 6: tmp = *pW++; *pW = W[i++ - Nk] ^ tmp;
|
|
case 5: tmp = *pW++; *pW = W[i++ - Nk] ^ tmp;
|
|
}
|
|
}
|
|
/* Generate the last word */
|
|
tmp = *pW++;
|
|
tmp = SUBBYTE(ROTBYTE(tmp)) ^ Rcon[i / Nk - 1];
|
|
*pW = W[i++ - Nk] ^ tmp;
|
|
/* There may be overflow here, if Nk % (Nb * (Nr + 1)) > 0. However,
|
|
* since the above loop generated all but the last Nk key words, there
|
|
* is no more need for the SubByte transformation.
|
|
*/
|
|
if (Nk < 8) {
|
|
for (; i < round_key_words; ++i) {
|
|
tmp = *pW++;
|
|
*pW = W[i - Nk] ^ tmp;
|
|
}
|
|
} else {
|
|
/* except in the case when Nk == 8. Then one more SubByte may have
|
|
* to be performed, at i % Nk == 4.
|
|
*/
|
|
for (; i < round_key_words; ++i) {
|
|
tmp = *pW++;
|
|
if (i % Nk == 4)
|
|
tmp = SUBBYTE(tmp);
|
|
*pW = W[i - Nk] ^ tmp;
|
|
}
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* rijndael_invkey_expansion
|
|
*
|
|
* Generate the expanded key for the inverse cipher from the key input by
|
|
* the user.
|
|
*/
|
|
static SECStatus
|
|
rijndael_invkey_expansion(AESContext *cx, unsigned char *key, unsigned int Nk)
|
|
{
|
|
int r;
|
|
PRUint32 *roundkeyw;
|
|
PRUint8 *b;
|
|
int Nb = cx->Nb;
|
|
/* begins like usual key expansion ... */
|
|
if (rijndael_key_expansion(cx, key, Nk) != SECSuccess)
|
|
return SECFailure;
|
|
/* ... but has the additional step of InvMixColumn,
|
|
* excepting the first and last round keys.
|
|
*/
|
|
roundkeyw = cx->expandedKey + cx->Nb;
|
|
for (r=1; r<cx->Nr; ++r) {
|
|
/* each key word, roundkeyw, represents a column in the key
|
|
* matrix. Each column is multiplied by the InvMixColumn matrix.
|
|
* [ 0E 0B 0D 09 ] [ b0 ]
|
|
* [ 09 0E 0B 0D ] * [ b1 ]
|
|
* [ 0D 09 0E 0B ] [ b2 ]
|
|
* [ 0B 0D 09 0E ] [ b3 ]
|
|
*/
|
|
b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^ IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^ IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^ IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^ IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
if (Nb <= 4)
|
|
continue;
|
|
switch (Nb) {
|
|
case 8: b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^
|
|
IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
case 7: b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^
|
|
IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
case 6: b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^
|
|
IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
case 5: b = (PRUint8 *)roundkeyw;
|
|
*roundkeyw++ = IMXC0(b[0]) ^ IMXC1(b[1]) ^
|
|
IMXC2(b[2]) ^ IMXC3(b[3]);
|
|
}
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
/**************************************************************************
|
|
*
|
|
* Stuff related to Rijndael encryption/decryption, optimized for
|
|
* a 128-bit blocksize.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#ifdef IS_LITTLE_ENDIAN
|
|
#define BYTE0WORD(w) ((w) & 0x000000ff)
|
|
#define BYTE1WORD(w) ((w) & 0x0000ff00)
|
|
#define BYTE2WORD(w) ((w) & 0x00ff0000)
|
|
#define BYTE3WORD(w) ((w) & 0xff000000)
|
|
#else
|
|
#define BYTE0WORD(w) ((w) & 0xff000000)
|
|
#define BYTE1WORD(w) ((w) & 0x00ff0000)
|
|
#define BYTE2WORD(w) ((w) & 0x0000ff00)
|
|
#define BYTE3WORD(w) ((w) & 0x000000ff)
|
|
#endif
|
|
|
|
#define COLUMN_0(array) *((PRUint32 *)(array ))
|
|
#define COLUMN_1(array) *((PRUint32 *)(array + 4))
|
|
#define COLUMN_2(array) *((PRUint32 *)(array + 8))
|
|
#define COLUMN_3(array) *((PRUint32 *)(array + 12))
|
|
#define COLUMN_4(array) *((PRUint32 *)(array + 16))
|
|
#define COLUMN_5(array) *((PRUint32 *)(array + 20))
|
|
#define COLUMN_6(array) *((PRUint32 *)(array + 24))
|
|
#define COLUMN_7(array) *((PRUint32 *)(array + 28))
|
|
|
|
#define STATE_BYTE(i) clone[i]
|
|
|
|
static SECStatus
|
|
rijndael_encryptBlock128(AESContext *cx,
|
|
unsigned char *output,
|
|
unsigned char *input)
|
|
{
|
|
int r, extra_cols;
|
|
PRUint32 *roundkeyw;
|
|
PRUint8 clone[RIJNDAEL_MAX_STATE_SIZE];
|
|
extra_cols = cx->Nb;
|
|
roundkeyw = cx->expandedKey;
|
|
/* Step 1: Add Round Key 0 to initial state */
|
|
COLUMN_0(clone) = COLUMN_0(input) ^ *roundkeyw++;
|
|
COLUMN_1(clone) = COLUMN_1(input) ^ *roundkeyw++;
|
|
COLUMN_2(clone) = COLUMN_2(input) ^ *roundkeyw++;
|
|
COLUMN_3(clone) = COLUMN_3(input) ^ *roundkeyw++;
|
|
/* Step 2: Loop over rounds [1..NR-1] */
|
|
for (r=1; r<cx->Nr; ++r) {
|
|
/* Do ShiftRow, ByteSub, and MixColumn all at once */
|
|
COLUMN_0(output) = T0(STATE_BYTE(0)) ^
|
|
T1(STATE_BYTE(5)) ^
|
|
T2(STATE_BYTE(10)) ^
|
|
T3(STATE_BYTE(15));
|
|
COLUMN_1(output) = T0(STATE_BYTE(4)) ^
|
|
T1(STATE_BYTE(9)) ^
|
|
T2(STATE_BYTE(14)) ^
|
|
T3(STATE_BYTE(3));
|
|
COLUMN_2(output) = T0(STATE_BYTE(8)) ^
|
|
T1(STATE_BYTE(13)) ^
|
|
T2(STATE_BYTE(2)) ^
|
|
T3(STATE_BYTE(7));
|
|
COLUMN_3(output) = T0(STATE_BYTE(12)) ^
|
|
T1(STATE_BYTE(1)) ^
|
|
T2(STATE_BYTE(6)) ^
|
|
T3(STATE_BYTE(11));
|
|
/* Round key addition */
|
|
COLUMN_0(clone) = COLUMN_0(output) ^ *roundkeyw++;
|
|
COLUMN_1(clone) = COLUMN_1(output) ^ *roundkeyw++;
|
|
COLUMN_2(clone) = COLUMN_2(output) ^ *roundkeyw++;
|
|
COLUMN_3(clone) = COLUMN_3(output) ^ *roundkeyw++;
|
|
}
|
|
/* Step 3: Do the last round */
|
|
/* Final round does not employ MixColumn */
|
|
COLUMN_0(output) = ((BYTE0WORD(T2(STATE_BYTE(0)))) |
|
|
(BYTE1WORD(T3(STATE_BYTE(5)))) |
|
|
(BYTE2WORD(T0(STATE_BYTE(10)))) |
|
|
(BYTE3WORD(T1(STATE_BYTE(15))))) ^
|
|
*roundkeyw++;
|
|
COLUMN_1(output) = ((BYTE0WORD(T2(STATE_BYTE(4)))) |
|
|
(BYTE1WORD(T3(STATE_BYTE(9)))) |
|
|
(BYTE2WORD(T0(STATE_BYTE(14)))) |
|
|
(BYTE3WORD(T1(STATE_BYTE(3))))) ^
|
|
*roundkeyw++;
|
|
COLUMN_2(output) = ((BYTE0WORD(T2(STATE_BYTE(8)))) |
|
|
(BYTE1WORD(T3(STATE_BYTE(13)))) |
|
|
(BYTE2WORD(T0(STATE_BYTE(2)))) |
|
|
(BYTE3WORD(T1(STATE_BYTE(7))))) ^
|
|
*roundkeyw++;
|
|
COLUMN_3(output) = ((BYTE0WORD(T2(STATE_BYTE(12)))) |
|
|
(BYTE1WORD(T3(STATE_BYTE(1)))) |
|
|
(BYTE2WORD(T0(STATE_BYTE(6)))) |
|
|
(BYTE3WORD(T1(STATE_BYTE(11))))) ^
|
|
*roundkeyw++;
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
rijndael_decryptBlock128(AESContext *cx,
|
|
unsigned char *output,
|
|
unsigned char *input)
|
|
{
|
|
int r, extra_cols;
|
|
PRUint32 *roundkeyw;
|
|
PRUint8 clone[RIJNDAEL_MAX_STATE_SIZE];
|
|
extra_cols = cx->Nb;
|
|
roundkeyw = cx->expandedKey + cx->Nb * cx->Nr + 3;
|
|
/* reverse the final key addition */
|
|
COLUMN_3(clone) = COLUMN_3(input) ^ *roundkeyw--;
|
|
COLUMN_2(clone) = COLUMN_2(input) ^ *roundkeyw--;
|
|
COLUMN_1(clone) = COLUMN_1(input) ^ *roundkeyw--;
|
|
COLUMN_0(clone) = COLUMN_0(input) ^ *roundkeyw--;
|
|
/* Loop over rounds in reverse [NR..1] */
|
|
for (r=cx->Nr; r>1; --r) {
|
|
/* Invert the (InvByteSub*InvMixColumn)(InvShiftRow(state)) */
|
|
COLUMN_0(output) = TInv0(STATE_BYTE(0)) ^
|
|
TInv1(STATE_BYTE(13)) ^
|
|
TInv2(STATE_BYTE(10)) ^
|
|
TInv3(STATE_BYTE(7));
|
|
COLUMN_1(output) = TInv0(STATE_BYTE(4)) ^
|
|
TInv1(STATE_BYTE(1)) ^
|
|
TInv2(STATE_BYTE(14)) ^
|
|
TInv3(STATE_BYTE(11));
|
|
COLUMN_2(output) = TInv0(STATE_BYTE(8)) ^
|
|
TInv1(STATE_BYTE(5)) ^
|
|
TInv2(STATE_BYTE(2)) ^
|
|
TInv3(STATE_BYTE(15));
|
|
COLUMN_3(output) = TInv0(STATE_BYTE(12)) ^
|
|
TInv1(STATE_BYTE(9)) ^
|
|
TInv2(STATE_BYTE(6)) ^
|
|
TInv3(STATE_BYTE(3));
|
|
/* Invert the key addition step */
|
|
COLUMN_3(clone) = COLUMN_3(output) ^ *roundkeyw--;
|
|
COLUMN_2(clone) = COLUMN_2(output) ^ *roundkeyw--;
|
|
COLUMN_1(clone) = COLUMN_1(output) ^ *roundkeyw--;
|
|
COLUMN_0(clone) = COLUMN_0(output) ^ *roundkeyw--;
|
|
}
|
|
/* inverse sub */
|
|
clone[ 0] = SBOXINV(clone[ 0]);
|
|
clone[ 1] = SBOXINV(clone[ 1]);
|
|
clone[ 2] = SBOXINV(clone[ 2]);
|
|
clone[ 3] = SBOXINV(clone[ 3]);
|
|
clone[ 4] = SBOXINV(clone[ 4]);
|
|
clone[ 5] = SBOXINV(clone[ 5]);
|
|
clone[ 6] = SBOXINV(clone[ 6]);
|
|
clone[ 7] = SBOXINV(clone[ 7]);
|
|
clone[ 8] = SBOXINV(clone[ 8]);
|
|
clone[ 9] = SBOXINV(clone[ 9]);
|
|
clone[10] = SBOXINV(clone[10]);
|
|
clone[11] = SBOXINV(clone[11]);
|
|
clone[12] = SBOXINV(clone[12]);
|
|
clone[13] = SBOXINV(clone[13]);
|
|
clone[14] = SBOXINV(clone[14]);
|
|
clone[15] = SBOXINV(clone[15]);
|
|
/* final key addition */
|
|
COLUMN_3(output) = COLUMN_3(clone) ^ *roundkeyw--;
|
|
COLUMN_2(output) = COLUMN_2(clone) ^ *roundkeyw--;
|
|
COLUMN_1(output) = COLUMN_1(clone) ^ *roundkeyw--;
|
|
COLUMN_0(output) = COLUMN_0(clone) ^ *roundkeyw--;
|
|
return SECSuccess;
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Stuff related to general Rijndael encryption/decryption, for blocksizes
|
|
* greater than 128 bits.
|
|
*
|
|
* XXX This code is currently untested! So far, AES specs have only been
|
|
* released for 128 bit blocksizes. This will be tested, but for now
|
|
* only the code above has been tested using known values.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#define COLUMN(array, j) *((PRUint32 *)(array + j))
|
|
|
|
SECStatus
|
|
rijndael_encryptBlock(AESContext *cx,
|
|
unsigned char *output,
|
|
unsigned char *input)
|
|
{
|
|
int j, r, Nb;
|
|
int c2, c3;
|
|
PRUint32 *roundkeyw;
|
|
PRUint8 clone[RIJNDAEL_MAX_STATE_SIZE];
|
|
Nb = cx->Nb;
|
|
roundkeyw = cx->expandedKey;
|
|
/* Step 1: Add Round Key 0 to initial state */
|
|
for (j=0; j<4*Nb; j+=4) {
|
|
COLUMN(clone, j) = COLUMN(input, j) ^ *roundkeyw++;
|
|
}
|
|
/* Step 2: Loop over rounds [1..NR-1] */
|
|
for (r=1; r<cx->Nr; ++r) {
|
|
for (j=0; j<Nb; ++j) {
|
|
COLUMN(output, j) = T0(STATE_BYTE(4* j )) ^
|
|
T1(STATE_BYTE(4*((j+ 1)%Nb)+1)) ^
|
|
T2(STATE_BYTE(4*((j+c2)%Nb)+2)) ^
|
|
T3(STATE_BYTE(4*((j+c3)%Nb)+3));
|
|
}
|
|
for (j=0; j<4*Nb; j+=4) {
|
|
COLUMN(clone, j) = COLUMN(output, j) ^ *roundkeyw++;
|
|
}
|
|
}
|
|
/* Step 3: Do the last round */
|
|
/* Final round does not employ MixColumn */
|
|
for (j=0; j<Nb; ++j) {
|
|
COLUMN(output, j) = ((BYTE0WORD(T2(STATE_BYTE(4* j )))) |
|
|
(BYTE1WORD(T3(STATE_BYTE(4*(j+ 1)%Nb)+1))) |
|
|
(BYTE2WORD(T0(STATE_BYTE(4*(j+c2)%Nb)+2))) |
|
|
(BYTE3WORD(T1(STATE_BYTE(4*(j+c3)%Nb)+3)))) ^
|
|
*roundkeyw++;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
rijndael_decryptBlock(AESContext *cx,
|
|
unsigned char *output,
|
|
unsigned char *input)
|
|
{
|
|
int j, r, Nb;
|
|
int c2, c3;
|
|
PRUint32 *roundkeyw;
|
|
PRUint8 clone[RIJNDAEL_MAX_STATE_SIZE];
|
|
Nb = cx->Nb;
|
|
roundkeyw = cx->expandedKey + cx->Nb * cx->Nr + 3;
|
|
/* reverse key addition */
|
|
for (j=4*Nb; j>=0; j-=4) {
|
|
COLUMN(clone, j) = COLUMN(input, j) ^ *roundkeyw--;
|
|
}
|
|
/* Loop over rounds in reverse [NR..1] */
|
|
for (r=cx->Nr; r>1; --r) {
|
|
/* Invert the (InvByteSub*InvMixColumn)(InvShiftRow(state)) */
|
|
for (j=0; j<Nb; ++j) {
|
|
COLUMN(output, 4*j) = TInv0(STATE_BYTE(4* j )) ^
|
|
TInv1(STATE_BYTE(4*(j+Nb- 1)%Nb)+1) ^
|
|
TInv2(STATE_BYTE(4*(j+Nb-c2)%Nb)+2) ^
|
|
TInv3(STATE_BYTE(4*(j+Nb-c3)%Nb)+3);
|
|
}
|
|
/* Invert the key addition step */
|
|
for (j=4*Nb; j>=0; j-=4) {
|
|
COLUMN(clone, j) = COLUMN(input, j) ^ *roundkeyw--;
|
|
}
|
|
}
|
|
/* inverse sub */
|
|
for (j=0; j<4*Nb; ++j) {
|
|
clone[j] = SBOXINV(clone[j]);
|
|
}
|
|
/* final key addition */
|
|
for (j=4*Nb; j>=0; j-=4) {
|
|
COLUMN(clone, j) = COLUMN(input, j) ^ *roundkeyw--;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Rijndael modes of operation (ECB and CBC)
|
|
*
|
|
*************************************************************************/
|
|
|
|
static SECStatus
|
|
rijndael_encryptECB(AESContext *cx, unsigned char *output,
|
|
unsigned int *outputLen, unsigned int maxOutputLen,
|
|
unsigned char *input, unsigned int inputLen, int blocksize)
|
|
{
|
|
SECStatus rv;
|
|
AESBlockFunc *encryptor;
|
|
encryptor = (blocksize == 16) ? &rijndael_encryptBlock128 :
|
|
&rijndael_encryptBlock;
|
|
while (inputLen > 0) {
|
|
rv = (*encryptor)(cx, output, input);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
output += blocksize;
|
|
input += blocksize;
|
|
inputLen -= blocksize;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
rijndael_encryptCBC(AESContext *cx, unsigned char *output,
|
|
unsigned int *outputLen, unsigned int maxOutputLen,
|
|
unsigned char *input, unsigned int inputLen, int blocksize)
|
|
{
|
|
int j;
|
|
SECStatus rv;
|
|
AESBlockFunc *encryptor;
|
|
unsigned char *lastblock;
|
|
lastblock = cx->iv;
|
|
encryptor = (blocksize == 16) ? &rijndael_encryptBlock128 :
|
|
&rijndael_encryptBlock;
|
|
while (inputLen > 0) {
|
|
/* XOR with the last block (IV if first block) */
|
|
for (j=0; j<blocksize; ++j)
|
|
input[j] ^= lastblock[j];
|
|
/* encrypt */
|
|
rv = (*encryptor)(cx, output, input);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
/* move to the next block */
|
|
lastblock = output;
|
|
output += blocksize;
|
|
input += blocksize;
|
|
inputLen -= blocksize;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
rijndael_decryptECB(AESContext *cx, unsigned char *output,
|
|
unsigned int *outputLen, unsigned int maxOutputLen,
|
|
unsigned char *input, unsigned int inputLen, int blocksize)
|
|
{
|
|
SECStatus rv;
|
|
AESBlockFunc *decryptor;
|
|
*outputLen = inputLen;
|
|
decryptor = (blocksize == 16) ? &rijndael_decryptBlock128 :
|
|
&rijndael_decryptBlock;
|
|
while (inputLen > 0) {
|
|
rv = (*decryptor)(cx, output, input);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
output += blocksize;
|
|
input += blocksize;
|
|
inputLen -= blocksize;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
rijndael_decryptCBC(AESContext *cx, unsigned char *output,
|
|
unsigned int *outputLen, unsigned int maxOutputLen,
|
|
unsigned char *input, unsigned int inputLen, int blocksize)
|
|
{
|
|
SECStatus rv;
|
|
AESBlockFunc *decryptor;
|
|
unsigned char *in, *out;
|
|
int j;
|
|
decryptor = (blocksize == 16) ? &rijndael_decryptBlock128 :
|
|
&rijndael_decryptBlock;
|
|
in = input + (inputLen - blocksize);
|
|
out = output + (inputLen - blocksize);
|
|
while (inputLen > 0) {
|
|
rv = (*decryptor)(cx, out, in);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
if (in == input) {
|
|
for (j=0; j<blocksize; ++j)
|
|
out[j] ^= cx->iv[j];
|
|
} else {
|
|
for (j=0; j<blocksize; ++j)
|
|
out[j] ^= in[j - blocksize];
|
|
}
|
|
out -= blocksize;
|
|
in -= blocksize;
|
|
inputLen -= blocksize;
|
|
}
|
|
return SECSuccess;
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* BLAPI Interface functions
|
|
*
|
|
* The following functions implement the encryption routines defined in
|
|
* BLAPI for the AES cipher, Rijndael.
|
|
*
|
|
***********************************************************************/
|
|
|
|
/* AES_CreateContext
|
|
*
|
|
* create a new context for Rijndael operations
|
|
*/
|
|
AESContext *
|
|
AES_CreateContext(unsigned char *key, unsigned char *iv, int mode, int encrypt,
|
|
unsigned int keysize, unsigned int blocksize)
|
|
{
|
|
AESContext *cx;
|
|
unsigned int Nk;
|
|
/* According to Rijndael AES Proposal, section 12.1, block and key
|
|
* lengths between 128 and 256 bits are supported, as long as the
|
|
* length in bytes is divisible by 4.
|
|
*/
|
|
if (key == NULL ||
|
|
keysize < 16 || keysize > 32 || keysize % 4 != 0 ||
|
|
blocksize < 16 || blocksize > 32 || blocksize % 4 != 0) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
if (mode != NSS_AES && mode != NSS_AES_CBC) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
if (mode == NSS_AES_CBC && iv == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return NULL;
|
|
}
|
|
cx = PORT_ZNew(AESContext);
|
|
if (!cx) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
return NULL;
|
|
}
|
|
/* Nb = (block size in bits) / 32 */
|
|
cx->Nb = blocksize / 4;
|
|
/* Nk = (key size in bits) / 32 */
|
|
Nk = keysize / 4;
|
|
/* Obtain number of rounds from "table" */
|
|
cx->Nr = RIJNDAEL_NUM_ROUNDS(Nk, cx->Nb);
|
|
/* copy in the iv, if neccessary */
|
|
if (mode == NSS_AES_CBC) {
|
|
cx->iv = PORT_ZAlloc(blocksize);
|
|
if (!cx->iv) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
memcpy(cx->iv, iv, blocksize);
|
|
cx->worker = (encrypt) ? &rijndael_encryptCBC : &rijndael_decryptCBC;
|
|
} else {
|
|
cx->worker = (encrypt) ? &rijndael_encryptECB : &rijndael_decryptECB;
|
|
}
|
|
/* Allocate memory for the expanded key */
|
|
cx->expandedKey = PORT_ZNewArray(PRUint32, cx->Nb * (cx->Nr + 1));
|
|
if (!cx->expandedKey) {
|
|
PORT_SetError(SEC_ERROR_NO_MEMORY);
|
|
goto cleanup;
|
|
}
|
|
/* Generate expanded key */
|
|
if (encrypt) {
|
|
if (rijndael_key_expansion(cx, key, Nk) != SECSuccess)
|
|
goto cleanup;
|
|
} else {
|
|
if (rijndael_invkey_expansion(cx, key, Nk) != SECSuccess)
|
|
goto cleanup;
|
|
}
|
|
return cx;
|
|
cleanup:
|
|
if (cx->expandedKey)
|
|
PORT_ZFree(cx->expandedKey, cx->Nb * (cx->Nr + 1));
|
|
PORT_ZFree(cx, sizeof *cx);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* AES_DestroyContext
|
|
*
|
|
* Zero an AES cipher context. If freeit is true, also free the pointer
|
|
* to the context.
|
|
*/
|
|
void
|
|
AES_DestroyContext(AESContext *cx, PRBool freeit)
|
|
{
|
|
PORT_ZFree(cx->expandedKey, cx->Nb * (cx->Nr + 1));
|
|
memset(cx, 0, sizeof *cx);
|
|
if (freeit)
|
|
PORT_Free(cx);
|
|
}
|
|
|
|
/*
|
|
* AES_Encrypt
|
|
*
|
|
* Encrypt an arbitrary-length buffer. The output buffer must already be
|
|
* allocated to at least inputLen.
|
|
*/
|
|
SECStatus
|
|
AES_Encrypt(AESContext *cx, unsigned char *output,
|
|
unsigned int *outputLen, unsigned int maxOutputLen,
|
|
unsigned char *input, unsigned int inputLen)
|
|
{
|
|
int blocksize;
|
|
/* Check args */
|
|
if (cx == NULL || output == NULL || input == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
blocksize = 4 * cx->Nb;
|
|
if (inputLen % blocksize != 0) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
if (maxOutputLen < inputLen) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
*outputLen = inputLen;
|
|
return (*cx->worker)(cx, output, outputLen, maxOutputLen,
|
|
input, inputLen, blocksize);
|
|
}
|
|
|
|
/*
|
|
* AES_Decrypt
|
|
*
|
|
* Decrypt and arbitrary-length buffer. The output buffer must already be
|
|
* allocated to at least inputLen.
|
|
*/
|
|
SECStatus
|
|
AES_Decrypt(AESContext *cx, unsigned char *output,
|
|
unsigned int *outputLen, unsigned int maxOutputLen,
|
|
unsigned char *input, unsigned int inputLen)
|
|
{
|
|
int blocksize;
|
|
/* Check args */
|
|
if (cx == NULL || output == NULL || input == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
blocksize = 4 * cx->Nb;
|
|
if (inputLen % blocksize != 0) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
if (maxOutputLen < inputLen) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return SECFailure;
|
|
}
|
|
*outputLen = inputLen;
|
|
return (*cx->worker)(cx, output, outputLen, maxOutputLen,
|
|
input, inputLen, blocksize);
|
|
}
|