diff options
Diffstat (limited to 'security/nss/lib/freebl/rijndael.c')
-rw-r--r-- | security/nss/lib/freebl/rijndael.c | 787 |
1 files changed, 787 insertions, 0 deletions
diff --git a/security/nss/lib/freebl/rijndael.c b/security/nss/lib/freebl/rijndael.c new file mode 100644 index 000000000..ffdd2be3b --- /dev/null +++ b/security/nss/lib/freebl/rijndael.c @@ -0,0 +1,787 @@ +/* + * 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$ + */ + +#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) +{ + unsigned 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) +{ + unsigned int i; + PRUint32 *W; + PRUint32 *pW; + PRUint32 tmp; + unsigned 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) +{ + unsigned 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, + const unsigned char *input) +{ + unsigned 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, + const 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 */ + output[ 0] = SBOXINV(clone[ 0]); + output[ 1] = SBOXINV(clone[13]); + output[ 2] = SBOXINV(clone[10]); + output[ 3] = SBOXINV(clone[ 7]); + output[ 4] = SBOXINV(clone[ 4]); + output[ 5] = SBOXINV(clone[ 1]); + output[ 6] = SBOXINV(clone[14]); + output[ 7] = SBOXINV(clone[11]); + output[ 8] = SBOXINV(clone[ 8]); + output[ 9] = SBOXINV(clone[ 5]); + output[10] = SBOXINV(clone[ 2]); + output[11] = SBOXINV(clone[15]); + output[12] = SBOXINV(clone[12]); + output[13] = SBOXINV(clone[ 9]); + output[14] = SBOXINV(clone[ 6]); + output[15] = SBOXINV(clone[ 3]); + /* final key addition */ + COLUMN_3(output) ^= *roundkeyw--; + COLUMN_2(output) ^= *roundkeyw--; + COLUMN_1(output) ^= *roundkeyw--; + COLUMN_0(output) ^= *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, + const unsigned char *input) +{ + unsigned int j, r, Nb; + unsigned 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, + const 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(output, j) ^ *roundkeyw--; + } + } + /* inverse sub */ + for (j=0; j<4*Nb; ++j) { + output[j] = SBOXINV(clone[j]); + } + /* final key addition */ + for (j=4*Nb; j>=0; j-=4) { + COLUMN(output, 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, + const 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, + const unsigned char *input, unsigned int inputLen, + int blocksize) +{ + int j; + SECStatus rv; + AESBlockFunc *encryptor; + unsigned char *lastblock; + unsigned char inblock[RIJNDAEL_MAX_STATE_SIZE * 8]; + 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) + inblock[j] = input[j] ^ lastblock[j]; + /* encrypt */ + rv = (*encryptor)(cx, output, inblock); + 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, + const 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, + const 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, + const 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, + const 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); +} |