diff options
Diffstat (limited to 'src/ocb/ocb.c')
-rw-r--r-- | src/ocb/ocb.c | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/src/ocb/ocb.c b/src/ocb/ocb.c new file mode 100644 index 0000000..003eb5d --- /dev/null +++ b/src/ocb/ocb.c @@ -0,0 +1,436 @@ +/* + * ocb.c + * + * Author: Ted Krovetz (tdk@acm.org) + * History: 1 April 2000 - first release (TK) - version 0.9 + * + * OCB-AES-n reference code based on NIST submission "OCB Mode" + * (dated 1 April 2000), submitted by Phillip Rogaway, with + * auxiliary submitters Mihir Bellare, John Black, and Ted Krovetz. + * + * This code is freely available, and may be modified as desired. + * Please retain the authorship and change history. + * Note that OCB mode itself is patent pending. + * + * This code is NOT optimized for speed; it is only + * designed to clarify the algorithm and to provide a point + * of comparison for other implementations. + * + * Limitiations: Assumes a 4-byte integer type and and pointers that are + * 32-bit aligned. Acts on a byte string of at most 2^36-16 bytes. + * + * Rijndael source available at www.esat.kuleuven.ac.be/~rijmen/rijndael/ + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ocb.h" +#include "rijndael-alg-fst.h" +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#if (INT_MAX != 0x7fffffff) +#error -- Assumes 4-byte int +#endif + +/* + * This implementation precomputes L(-1), L(0), L(1), L(PRE_COMP_BLOCKS), + * where L(0) = L and L(-1) = L/x and L(i) = x*L(i) for i>0. + * Normally, one would select PRE_COMP_BLOCKS to be a small number + * (like 0-6) and compute any larger L(i) values "on the fly", when they + * are needed. This saves space in _keystruct and needn't adversely + * impact running time. But in this implementation, to keep things as + * simple as possible, we compute all the L(i)-values we might ever see. + */ +#define PRE_COMP_BLOCKS 31 /* Must be between 0 and 31 */ + +#define AES_ROUNDS (AES_KEY_BITLEN / 32 + 6) + +typedef unsigned char block[16]; + +struct _keystruct { + unsigned rek[4*(AES_ROUNDS+1)]; /* AES encryption key */ + unsigned rdk[4*(AES_ROUNDS+1)]; /* AES decryption key */ + unsigned tag_len; /* Sizeof tags to generate/validate */ + block L[PRE_COMP_BLOCKS+1]; /* Precomputed L(i) values, L[0] = L */ + block L_inv; /* Precomputed L/x value */ +}; + +/************************************************************************* + * xor_block + *************************************************************************/ +static void +xor_block(void *dst, void *src1, void *src2) +/* 128-bit xor: *dst = *src1 xor *src2. Pointers must be 32-bit aligned */ +{ + ((unsigned *)dst)[0] = ((unsigned *)src1)[0] ^ ((unsigned *)src2)[0]; + ((unsigned *)dst)[1] = ((unsigned *)src1)[1] ^ ((unsigned *)src2)[1]; + ((unsigned *)dst)[2] = ((unsigned *)src1)[2] ^ ((unsigned *)src2)[2]; + ((unsigned *)dst)[3] = ((unsigned *)src1)[3] ^ ((unsigned *)src2)[3]; +} + + +/************************************************************************* + * shift_left + *************************************************************************/ +static void +shift_left(unsigned char *x) +/* 128-bit shift-left by 1 bit: *x <<= 1. */ +{ + int i; + for (i = 0; i < 15; i++) { + x[i] = (x[i] << 1) | (x[i+1] & 0x80 ? 1 : 0); + } + x[15] = (x[15] << 1); +} + +/************************************************************************* + * shift_right + *************************************************************************/ +static void +shift_right(unsigned char *x) +/* 128-bit shift-right by 1 bit: *x >>= 1 */ +{ + int i; + for (i = 15; i > 0; i--) { + x[i] = (x[i] >> 1) | (x[i-1] & 1 ? 0x80u : 0); + } + x[0] = (x[0] >> 1); +} + +/************************************************************************* + * ntz + *************************************************************************/ +static int +ntz(unsigned i) +/* Count the number of trailing zeroes in integer i. */ +{ +#if (_MSC_VER && _M_IX86) /* Only non-C sop */ + __asm bsf eax, i +#elif (__GNUC__ && __i386__) + int rval; + asm volatile("bsf %1, %0" : "=r" (rval) : "g" (i)); + return rval; +#else + int rval = 0; + while ((i & 1) == 0) { + i >>= 1; + rval++; + } + return rval; +#endif +} + +/************************************************************************* + * ocb_aes_init + *************************************************************************/ +keystruct * /* Init'd keystruct or NULL */ +ocb_aes_init(void *enc_key, /* AES key */ + unsigned tag_len, /* Length of tags to be used */ + keystruct *key) /* OCB key structure. NULL means */ + /* Allocate/init new, non-NULL */ + /* means init existing structure */ +{ + unsigned char tmp[16] = {0,}; + unsigned first_bit, last_bit, i; + + if (key == NULL) + key = (keystruct *)malloc(sizeof(keystruct)); + if (key != NULL) { + memset(key, 0, sizeof(keystruct)); + + /* Initialize AES keys. (Note that if one is only going to + encrypt, key->rdk can be eliminated */ + rijndaelKeySetupEnc(key->rek, (unsigned char *)enc_key, + AES_KEY_BITLEN); + rijndaelKeySetupDec(key->rdk, (unsigned char *)enc_key, + AES_KEY_BITLEN); + + /* Precompute L[i]-values. L[0] is synonym of L */ + rijndaelEncrypt (key->rek, AES_ROUNDS, tmp, tmp); + for (i = 0; i <= PRE_COMP_BLOCKS; i++) { + memcpy(key->L + i, tmp, 16); /* Copy tmp to L[i] */ + first_bit = tmp[0] & 0x80u; /* and multiply tmp by x */ + shift_left(tmp); + if (first_bit) + tmp[15] ^= 0x87; + } + + /* Precompute L_inv = L . x^{-1} */ + memcpy(tmp, key->L, 16); + last_bit = tmp[15] & 0x01; + shift_right(tmp); + if (last_bit) { + tmp[0] ^= 0x80; + tmp[15] ^= 0x43; + } + memcpy(key->L_inv, tmp, 16); + + /* Set tag length used for this session */ + key->tag_len = tag_len; + } + + return key; +} + + +/************************************************************************* + * pmac_aes -- move to a separate file when everything final + *************************************************************************/ +void +pmac_aes (keystruct *key, /* Initialized key struct */ + void *in, /* Buffer for (incoming) message */ + unsigned in_len, /* Byte length of message */ + void *tag) /* 16-byte buffer for generated tag */ +{ + unsigned i; /* Block counter */ + unsigned char tmp[16]; /* temporary buffer */ + block *in_blk; /* Block-typed alias to in */ + block Offset; /* Offset (Z[i]) for current block */ + block checksum; /* Checksum for computing tag */ + + /* + * Initializations + */ + i = 1; /* Start with first block */ + in_blk = (block *)in - 1; /* Offset so in_blk[1] is first block. */ + memset(checksum, 0, 16); /* Initlize the checksum and */ + memset(Offset, 0, 16); /* current Offset to the zero block */ + + /* + * Process blocks 1 .. m-1. + */ + while (in_len > 16) { + + /* Update Offset (Z[i] from Z[i-1]) */ + xor_block(Offset, key->L + ntz(i), Offset); + + xor_block(tmp, Offset, in_blk + i); /* xor input block with Z[i] */ + + rijndaelEncrypt(key->rek, AES_ROUNDS, tmp, tmp); + + xor_block(checksum, checksum, tmp); /* Update checksum */ + + in_len -= 16; /* and the loop variables */ + i++; + } + + /* + * Process block m + */ + + if (in_len == 16) { /* full final block */ + xor_block(checksum, checksum, in_blk + i); + xor_block(checksum, checksum, key->L_inv); + } else { /* short final block */ + memset(tmp, 0, 16); + memcpy(tmp, in_blk + i, in_len); + tmp[in_len] = 0x80; + xor_block(checksum, checksum, tmp); + } + + rijndaelEncrypt(key->rek, AES_ROUNDS, checksum, (unsigned char *)tag); +} + +/************************************************************************* + * ocb_aes_encrypt + *************************************************************************/ +void +ocb_aes_encrypt(keystruct *key, /* Initialized key struct */ + void *nonce, /* 16-byte nonce */ + void *pt, /* Buffer for (incoming) plaintext */ + unsigned pt_len, /* Byte length of pt */ + void *ct, /* Buffer for (outgoing) ciphertext */ + void *tag) /* Buffer for generated tag */ +{ + unsigned i; /* Block counter */ + block tmp, tmp2; /* temporary buffers */ + block *pt_blk, *ct_blk; /* block-typed aliases for pt / ct */ + block Offset; /* Offset (Z[i]) for current block */ + block checksum; /* Checksum for computing tag */ + + /* + * Initializations + */ + i = 1; /* Start with first block */ + pt_blk = (block *)pt - 1; /* These are adjusted so, for example, */ + ct_blk = (block *)ct - 1; /* pt_blk[1] refers to the first block */ + memset(checksum, 0, 16); /* Zero the checksum */ + + /* Calculate R, aka Z[0] */ + xor_block(Offset, nonce, key->L); + rijndaelEncrypt (key->rek, AES_ROUNDS, Offset, Offset); + + /* + * Process blocks 1 .. m-1 + */ + while (pt_len > 16) { + + /* Update the Offset (Z[i] from Z[i-1]) */ + xor_block(Offset, key->L + ntz(i), Offset); + + /* xor the plaintext block block with Z[i] */ + xor_block(tmp, Offset, pt_blk + i); + + /* Encipher the block */ + rijndaelEncrypt (key->rek, AES_ROUNDS, tmp, tmp); + + /* xor Z[i] again, writing result to ciphertext pointer */ + xor_block(ct_blk + i, Offset, tmp); + + /* Update checksum */ + xor_block(checksum, checksum, pt_blk + i); + + /* Update loop variables */ + pt_len -= 16; + i++; + } + + /* + * Process block m + */ + + /* Update Offset (Z[m] from Z[m-1]) */ + xor_block(Offset, key->L + ntz(i), Offset); + + /* xor L . x^{-1} and Z[m] */ + xor_block(tmp, Offset, key->L_inv); + + /* Add in final block bit-length */ + tmp[15] ^= (pt_len << 3); + + rijndaelEncrypt (key->rek, AES_ROUNDS, tmp, tmp); + + /* xor 'pt' with block-cipher output, copy valid bytes to 'ct' */ + memcpy(tmp2, pt_blk + i, pt_len); + xor_block(tmp2, tmp2, tmp); + memcpy(ct_blk + i, tmp2, pt_len); + + /* Add to checksum the pt_len bytes of plaintext followed by */ + /* the last (16 - pt_len) bytes of block-cipher output */ + memcpy(tmp, pt_blk + i, pt_len); + xor_block(checksum, checksum, tmp); + + /* + * Calculate tag + */ + xor_block(checksum, checksum, Offset); + rijndaelEncrypt(key->rek, AES_ROUNDS, checksum, tmp); + memcpy(tag, tmp, key->tag_len); +} + + +/************************************************************************* + * ocb_aes_decrypt + *************************************************************************/ +int /* Returns 0 iff tag is incorrect */ +ocb_aes_decrypt(keystruct *key, /* Initialized key struct */ + void *nonce, /* 16-byte nonce */ + void *ct, /* Buffer for (incoming) ciphertext */ + unsigned ct_len, /* Byte length of ct */ + void *pt, /* Buffer for (outgoing) plaintext */ + void *tag) /* Tag to be verified */ +{ + unsigned i; /* Block counter */ + block tmp, tmp2; /* temporary buffers */ + block *ct_blk, *pt_blk; /* block-typed aliases for ct / pt */ + block Offset; /* Offset (Z[i]) for current block */ + block checksum; /* Checksum for computing tag */ + + /* + * Initializations + */ + i = 1; /* Start with first block */ + ct_blk = (block *)ct - 1; /* These are adjusted so, for example, */ + pt_blk = (block *)pt - 1; /* ct_blk[1] refers to the first block */ + + /* Zero checksum */ + memset(checksum, 0, 16); + + /* Calculate R, aka Z[0] */ + xor_block(Offset, nonce, key->L); + rijndaelEncrypt (key->rek, AES_ROUNDS, Offset, Offset); + + /* + * Process blocks 1 .. m-1 + */ + while (ct_len > 16) { + + /* Update Offset (Z[i] from Z[i-1]) */ + xor_block(Offset, key->L + ntz(i), Offset); + + /* xor ciphertext block with Z[i] */ + xor_block(tmp, Offset, ct_blk + i); + + /* Decipher the next block-cipher block */ + rijndaelDecrypt (key->rdk, AES_ROUNDS, tmp, tmp); + + /* xor Z[i] again, writing result to plaintext ponter */ + xor_block(pt_blk + i, Offset, tmp); + + /* Update checksum */ + xor_block(checksum, checksum, pt_blk + i); + + /* Update loop variables */ + ct_len -= 16; + i++; + } + + /* + * Process block m + */ + + /* Update Offset (Z[m] from Z[m-1]) */ + xor_block(Offset, key->L + ntz(i), Offset); + + /* xor L . x^{-1} and Z[m] */ + xor_block(tmp, Offset, key->L_inv); + + /* Add in final block bit-length */ + tmp[15] ^= (ct_len << 3); + + rijndaelEncrypt (key->rek, AES_ROUNDS, tmp, tmp); + + /* Form the final ciphertext block, C[m] */ + memset(tmp2, 0, 16); + memcpy(tmp2, ct_blk + i, ct_len); + xor_block(tmp, tmp2, tmp); + memcpy(pt_blk + i, tmp, ct_len); + + /* After the xor above, tmp will have ct_len bytes of plaintext */ + /* then (16 - ct_len) block-cipher bytes, perfect for checksum. */ + xor_block(checksum, checksum, tmp); + + /* + * Calculate tag + */ + xor_block(checksum, checksum, Offset); + rijndaelEncrypt(key->rek, AES_ROUNDS, checksum, tmp); + return (memcmp(tag, tmp, key->tag_len) == 0 ? 1 : 0); +} + + +/************************************************************************* + * ocb_done + *************************************************************************/ +keystruct * +ocb_done(keystruct *key) +{ + if (key) { + memset(key, 0, sizeof(keystruct)); + free(key); + } + return NULL; +} + |