/* rijndael-api.c - Rijndael encryption programming interface. * Author: Kees J. Bot * 3 Nov 2000 * Heavily based on the original API code by Antoon Bosselaers, * Vincent Rijmen, and Paulo Barreto, but with a different interface. * * Read this code top to bottom, not all comments are repeated. */ #include #include #include #include "rijndael-alg.h" #include "rijndael-api.h" /* Map a byte (?) address to a word address or vv. */ #define W(a) ((word32 *) (a)) #define B(a) ((word8 *) (a)) #if STRICT_ALIGN /* This machine checks alignment religiously. (The code is not proper with * respect to alignment. We need a compiler that doesn't muck about with byte * arrays that follow words in structs, and that places automatic variables * at word boundaries if not odd-sized. Most compilers are this nice.) */ #define aligned(a) (((unsigned) (a) & 3) == 0) #define aligned2(a1, a2) aligned((unsigned) (a1) | (unsigned) (a2)) static void blockcpy(void *dst, const void *src) { int i= 0; do { B(dst)[i+0] = B(src)[i+0]; B(dst)[i+1] = B(src)[i+1]; B(dst)[i+2] = B(src)[i+2]; B(dst)[i+3] = B(src)[i+3]; } while ((i += 4) < 16); } #else /* !STRICT_ALIGN */ /* This machine doesn't mind misaligned accesses much. */ #define aligned(a) ((void) (a), 1) #define aligned2(a1, a2) ((void) (a1), (void) (a2), 1) #if __GNUC__ __inline #endif static void blockcpy(void *dst, const void *src) { W(dst)[0] = W(src)[0]; W(dst)[1] = W(src)[1]; W(dst)[2] = W(src)[2]; W(dst)[3] = W(src)[3]; } #endif /* !STRICT_ALIGN */ #define between(a, c, z) ((unsigned) (c) - (a) <= (unsigned) (z) - (a)) int rijndael_makekey(rd_keyinstance *key, size_t keylen, const void *keymaterial) { word8 k[MAXKC][4]; /* Initialize key schedule: */ if (keylen == RD_KEY_HEX) { const word8 *kp; int c, b; kp= keymaterial; keylen= 0; for (;;) { c= *kp++; if (between('0', c, '9')) b= (c - '0' + 0x0) << 4; else if (between('a', c, 'f')) b= (c - 'a' + 0xa) << 4; else if (between('A', c, 'F')) b= (c - 'A' + 0xA) << 4; else break; c= *kp++; if (between('0', c, '9')) b |= (c - '0' + 0x0); else if (between('a', c, 'f')) b |= (c - 'a' + 0xa); else if (between('A', c, 'F')) b |= (c - 'A' + 0xA); else break; if (keylen >= 256/8) return RD_BAD_KEY_MAT; B(k)[keylen++] = b; } if (c != 0) return RD_BAD_KEY_MAT; if (keylen != 128/8 && keylen != 192/8 && keylen != 256/8) { return RD_BAD_KEY_MAT; } } else { if (keylen != 128/8 && keylen != 192/8 && keylen != 256/8) { return RD_BAD_KEY_MAT; } memcpy(k, keymaterial, keylen); } key->rounds= keylen * 8 / 32 + 6; rijndael_KeySched(k, key->encsched, key->rounds); memcpy(key->decsched, key->encsched, sizeof(key->decsched)); rijndael_KeyEncToDec(key->decsched, key->rounds); return 0; } ssize_t rijndael_ecb_encrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *dummyIV) { /* Encrypt blocks of data in Electronic Codebook mode. */ const word8 *inp= input; word8 *outp= output; size_t i, nr_blocks, extra; word32 in[4], out[4]; word8 t; /* Compute the number of whole blocks, and the extra bytes beyond the * last block. Those extra bytes, if any, are encrypted by stealing * enough bytes from the previous encrypted block to make a whole block. * This is done by encrypting the last block, exchanging the first few * encrypted bytes with the extra bytes, and encrypting the last whole * block again. */ nr_blocks= length / 16; if ((extra= (length % 16)) > 0) { if (nr_blocks == 0) return RD_BAD_BLOCK_LENGTH; nr_blocks--; } /* Encrypt a number of blocks. */ if (aligned2(inp, outp)) { for (i= 0; i < nr_blocks; i++) { rijndael_Encrypt(inp, outp, key->encsched, key->rounds); inp += 16; outp += 16; } } else { for (i= 0; i < nr_blocks; i++) { blockcpy(in, inp); rijndael_Encrypt(in, out, key->encsched, key->rounds); blockcpy(outp, out); inp += 16; outp += 16; } } /* Encrypt extra bytes by stealing from the last full block. */ if (extra > 0) { blockcpy(in, inp); rijndael_Encrypt(in, out, key->encsched, key->rounds); for (i= 0; i < extra; i++) { t= B(out)[i]; B(out)[i] = inp[16 + i]; outp[16 + i] = t; } rijndael_Encrypt(out, out, key->encsched, key->rounds); blockcpy(outp, out); } return length; } ssize_t rijndael_ecb_decrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *dummyIV) { /* Decrypt blocks of data in Electronic Codebook mode. */ const word8 *inp= input; word8 *outp= output; size_t i, nr_blocks, extra; word32 in[4], out[4]; word8 t; nr_blocks= length / 16; if ((extra= (length % 16)) > 0) { if (nr_blocks == 0) return RD_BAD_BLOCK_LENGTH; nr_blocks--; } /* Decrypt a number of blocks. */ if (aligned2(inp, outp)) { for (i= 0; i < nr_blocks; i++) { rijndael_Decrypt(inp, outp, key->decsched, key->rounds); inp += 16; outp += 16; } } else { for (i= 0; i < nr_blocks; i++) { blockcpy(in, inp); rijndael_Decrypt(in, out, key->decsched, key->rounds); blockcpy(outp, out); inp += 16; outp += 16; } } /* Decrypt extra bytes that stole from the last full block. */ if (extra > 0) { blockcpy(in, inp); rijndael_Decrypt(in, out, key->decsched, key->rounds); for (i= 0; i < extra; i++) { t= B(out)[i]; B(out)[i] = inp[16 + i]; outp[16 + i] = t; } rijndael_Decrypt(out, out, key->decsched, key->rounds); blockcpy(outp, out); } return length; } ssize_t rijndael_cbc_encrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *IV) { /* Encrypt blocks of data in Cypher Block Chaining mode. */ const word8 *inp= input; word8 *outp= output; size_t i, nr_blocks, extra; word32 in[4], out[4], iv[4], *ivp; word8 t; nr_blocks= length / 16; if ((extra= (length % 16)) > 0) { if (nr_blocks == 0) return RD_BAD_BLOCK_LENGTH; nr_blocks--; } /* Each input block is first XORed with the previous encryption result. * The "Initialization Vector" is used to XOR the first block with. * When done the last crypted block is stored back as the new IV to be * used for another call to this function. */ ivp= aligned(IV) ? IV : (blockcpy(iv, IV), iv); if (aligned2(inp, outp)) { for (i= 0; i < nr_blocks; i++) { in[0] = W(inp)[0] ^ ivp[0]; in[1] = W(inp)[1] ^ ivp[1]; in[2] = W(inp)[2] ^ ivp[2]; in[3] = W(inp)[3] ^ ivp[3]; rijndael_Encrypt(in, outp, key->encsched, key->rounds); ivp= W(outp); inp += 16; outp += 16; } } else { for (i= 0; i < nr_blocks; i++) { blockcpy(in, inp); in[0] ^= ivp[0]; in[1] ^= ivp[1]; in[2] ^= ivp[2]; in[3] ^= ivp[3]; rijndael_Encrypt(in, out, key->encsched, key->rounds); blockcpy(outp, out); ivp= out; inp += 16; outp += 16; } } if (extra > 0) { blockcpy(in, inp); in[0] ^= ivp[0]; in[1] ^= ivp[1]; in[2] ^= ivp[2]; in[3] ^= ivp[3]; rijndael_Encrypt(in, out, key->encsched, key->rounds); for (i= 0; i < extra; i++) { t= B(out)[i]; B(out)[i] ^= inp[16 + i]; outp[16 + i] = t; } rijndael_Encrypt(out, out, key->encsched, key->rounds); blockcpy(outp, out); ivp= out; } blockcpy(IV, ivp); /* Store last IV back. */ return length; } ssize_t rijndael_cbc_decrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *IV) { /* Decrypt blocks of data in Cypher Block Chaining mode. */ const word8 *inp= input; word8 *outp= output; size_t i, nr_blocks, extra; word32 in[4], out[4], iv[4]; word8 t; nr_blocks= length / 16; if ((extra= (length % 16)) > 0) { if (nr_blocks == 0) return RD_BAD_BLOCK_LENGTH; nr_blocks--; } blockcpy(iv, IV); if (aligned2(inp, outp)) { for (i= 0; i < nr_blocks; i++) { rijndael_Decrypt(inp, out, key->decsched, key->rounds); out[0] ^= iv[0]; out[1] ^= iv[1]; out[2] ^= iv[2]; out[3] ^= iv[3]; iv[0] = W(inp)[0]; iv[1] = W(inp)[1]; iv[2] = W(inp)[2]; iv[3] = W(inp)[3]; W(outp)[0] = out[0]; W(outp)[1] = out[1]; W(outp)[2] = out[2]; W(outp)[3] = out[3]; inp += 16; outp += 16; } } else { for (i= 0; i < nr_blocks; i++) { blockcpy(in, inp); rijndael_Decrypt(in, out, key->decsched, key->rounds); out[0] ^= iv[0]; out[1] ^= iv[1]; out[2] ^= iv[2]; out[3] ^= iv[3]; iv[0] = in[0]; iv[1] = in[1]; iv[2] = in[2]; iv[3] = in[3]; blockcpy(outp, out); inp += 16; outp += 16; } } if (extra > 0) { blockcpy(in, inp); blockcpy(IV, in); rijndael_Decrypt(in, out, key->decsched, key->rounds); for (i= 0; i < extra; i++) { t= B(out)[i] ^ inp[16 + i]; B(out)[i] = inp[16 + i]; outp[16 + i] = t; } rijndael_Decrypt(out, out, key->decsched, key->rounds); out[0] ^= iv[0]; out[1] ^= iv[1]; out[2] ^= iv[2]; out[3] ^= iv[3]; blockcpy(outp, out); } else { blockcpy(IV, iv); } return length; } ssize_t rijndael_cfb1_encrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *IV) { /* Encrypt blocks of data in Cypher Feedback mode, 1 bit at a time. */ const word8 *inp= input; word8 *outp= output; word8 t; size_t i; int b; word32 iv[4], civ[4]; blockcpy(iv, IV); for (i= 0; i < length; i++) { t= *inp++; for (b= 0; b < 8; b++) { rijndael_Encrypt(iv, civ, key->encsched, key->rounds); t ^= (B(civ)[0] & 0x80) >> b; B(iv)[ 0] = (B(iv)[ 0] << 1) | (B(iv)[ 1] >> 7); B(iv)[ 1] = (B(iv)[ 1] << 1) | (B(iv)[ 2] >> 7); B(iv)[ 2] = (B(iv)[ 2] << 1) | (B(iv)[ 3] >> 7); B(iv)[ 3] = (B(iv)[ 3] << 1) | (B(iv)[ 4] >> 7); B(iv)[ 4] = (B(iv)[ 4] << 1) | (B(iv)[ 5] >> 7); B(iv)[ 5] = (B(iv)[ 5] << 1) | (B(iv)[ 6] >> 7); B(iv)[ 6] = (B(iv)[ 6] << 1) | (B(iv)[ 7] >> 7); B(iv)[ 7] = (B(iv)[ 7] << 1) | (B(iv)[ 8] >> 7); B(iv)[ 8] = (B(iv)[ 8] << 1) | (B(iv)[ 9] >> 7); B(iv)[ 9] = (B(iv)[ 9] << 1) | (B(iv)[10] >> 7); B(iv)[10] = (B(iv)[10] << 1) | (B(iv)[11] >> 7); B(iv)[11] = (B(iv)[11] << 1) | (B(iv)[12] >> 7); B(iv)[12] = (B(iv)[12] << 1) | (B(iv)[13] >> 7); B(iv)[13] = (B(iv)[13] << 1) | (B(iv)[14] >> 7); B(iv)[14] = (B(iv)[14] << 1) | (B(iv)[15] >> 7); B(iv)[15] = (B(iv)[15] << 1) | ((t >> (7-b)) & 1); } *outp++ = t; } blockcpy(IV, iv); return length; } ssize_t rijndael_cfb1_decrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *IV) { /* Decrypt blocks of data in Cypher Feedback mode, 1 bit at a time. */ const word8 *inp= input; word8 *outp= output; word8 t; size_t i; int b; word32 iv[4], civ[4]; blockcpy(iv, IV); for (i= 0; i < length; i++) { t= *inp++; for (b= 0; b < 8; b++) { rijndael_Encrypt(iv, civ, key->encsched, key->rounds); B(iv)[ 0] = (B(iv)[ 0] << 1) | (B(iv)[ 1] >> 7); B(iv)[ 1] = (B(iv)[ 1] << 1) | (B(iv)[ 2] >> 7); B(iv)[ 2] = (B(iv)[ 2] << 1) | (B(iv)[ 3] >> 7); B(iv)[ 3] = (B(iv)[ 3] << 1) | (B(iv)[ 4] >> 7); B(iv)[ 4] = (B(iv)[ 4] << 1) | (B(iv)[ 5] >> 7); B(iv)[ 5] = (B(iv)[ 5] << 1) | (B(iv)[ 6] >> 7); B(iv)[ 6] = (B(iv)[ 6] << 1) | (B(iv)[ 7] >> 7); B(iv)[ 7] = (B(iv)[ 7] << 1) | (B(iv)[ 8] >> 7); B(iv)[ 8] = (B(iv)[ 8] << 1) | (B(iv)[ 9] >> 7); B(iv)[ 9] = (B(iv)[ 9] << 1) | (B(iv)[10] >> 7); B(iv)[10] = (B(iv)[10] << 1) | (B(iv)[11] >> 7); B(iv)[11] = (B(iv)[11] << 1) | (B(iv)[12] >> 7); B(iv)[12] = (B(iv)[12] << 1) | (B(iv)[13] >> 7); B(iv)[13] = (B(iv)[13] << 1) | (B(iv)[14] >> 7); B(iv)[14] = (B(iv)[14] << 1) | (B(iv)[15] >> 7); B(iv)[15] = (B(iv)[15] << 1) | ((t >> (7-b)) & 1); t ^= (B(civ)[0] & 0x80) >> b; } *outp++ = t; } blockcpy(IV, iv); return length; } ssize_t rijndael_cfb8_encrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *IV) { /* Encrypt blocks of data in Cypher Feedback mode, 8 bits at a time. */ const word8 *inp= input; word8 *outp= output; word8 t; size_t i; word32 iv[4], civ[4]; blockcpy(iv, IV); for (i= 0; i < length; i++) { t= *inp++; rijndael_Encrypt(iv, civ, key->encsched, key->rounds); t ^= B(civ)[0]; B(iv)[ 0] = B(iv)[ 1]; B(iv)[ 1] = B(iv)[ 2]; B(iv)[ 2] = B(iv)[ 3]; B(iv)[ 3] = B(iv)[ 4]; B(iv)[ 4] = B(iv)[ 5]; B(iv)[ 5] = B(iv)[ 6]; B(iv)[ 6] = B(iv)[ 7]; B(iv)[ 7] = B(iv)[ 8]; B(iv)[ 8] = B(iv)[ 9]; B(iv)[ 9] = B(iv)[10]; B(iv)[10] = B(iv)[11]; B(iv)[11] = B(iv)[12]; B(iv)[12] = B(iv)[13]; B(iv)[13] = B(iv)[14]; B(iv)[14] = B(iv)[15]; B(iv)[15] = t; *outp++ = t; } blockcpy(IV, iv); return length; } ssize_t rijndael_cfb8_decrypt(rd_keyinstance *key, const void *input, void *output, size_t length, void *IV) { /* Decrypt blocks of data in Cypher Feedback mode, 1 byte at a time. */ const word8 *inp= input; word8 *outp= output; word8 t; size_t i; word32 iv[4], civ[4]; blockcpy(iv, IV); for (i= 0; i < length; i++) { t= *inp++; rijndael_Encrypt(iv, civ, key->encsched, key->rounds); B(iv)[ 0] = B(iv)[ 1]; B(iv)[ 1] = B(iv)[ 2]; B(iv)[ 2] = B(iv)[ 3]; B(iv)[ 3] = B(iv)[ 4]; B(iv)[ 4] = B(iv)[ 5]; B(iv)[ 5] = B(iv)[ 6]; B(iv)[ 6] = B(iv)[ 7]; B(iv)[ 7] = B(iv)[ 8]; B(iv)[ 8] = B(iv)[ 9]; B(iv)[ 9] = B(iv)[10]; B(iv)[10] = B(iv)[11]; B(iv)[11] = B(iv)[12]; B(iv)[12] = B(iv)[13]; B(iv)[13] = B(iv)[14]; B(iv)[14] = B(iv)[15]; B(iv)[15] = t; t ^= B(civ)[0]; *outp++ = t; } blockcpy(IV, iv); return length; } ssize_t rijndael_pad(void *input, size_t length) { /* Adds at most one block of RFC-2040 style padding to the input to make * it a whole number of blocks for easier encryption. To be used if the * input may be less then one block in size, otherwise let the encryption * routines use cypher stealing. The input buffer should allow enough * space for the padding. The new length of the input is returned. */ word8 *inp= input; size_t padlen; /* Add padding up until the next block boundary. */ padlen= 16 - (length % 16); memset(inp + length, padlen, padlen); return length + padlen; } ssize_t rijndael_unpad(const void *input, size_t length) { /* Remove RFC-2040 style padding after decryption. The true length of * the input is returned, or the usual errors if the padding is incorrect. */ const word8 *inp= input; size_t i, padlen; if (length == 0 || (length % 16) != 0) return RD_BAD_BLOCK_LENGTH; padlen = inp[length-1]; if (padlen <= 0 || padlen > 16) return RD_BAD_DATA; for (i= 2; i <= padlen; i++) { if (inp[length-i] != padlen) return RD_BAD_DATA; } return length - padlen; } #ifdef INTERMEDIATE_VALUE_KAT void cipherEncryptUpdateRounds(rd_keyinstance *key, const void *input, void *output, int rounds) { /* Encrypt a block only a specified number of rounds. */ word8 block[4][4]; blockcpy(block, input); rijndaelEncryptRound(block, key->encsched, key->rounds, rounds); blockcpy(output, block); } void cipherDecryptUpdateRounds(rd_keyinstance *key, const void *input, void *output, int rounds) { /* Decrypt a block only a specified number of rounds. */ word8 block[4][4]; blockcpy(block, input); rijndaelDecryptRound(block, key->decsched, key->rounds, rounds); blockcpy(output, block); } #endif /* INTERMEDIATE_VALUE_KAT */ /* * $PchId: rijndael_api.c,v 1.2 2001/01/10 22:01:20 philip Exp $ */