summaryrefslogtreecommitdiff
path: root/src/ocb/ocb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ocb/ocb.c')
-rw-r--r--src/ocb/ocb.c436
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;
+}
+