#include "aesxts.h"
#include <sys/types.h>
#include <string.h>
#include <libkern/libkern.h>
int
aes_encrypt_key(const uint8_t *key, int key_len, aesedp_encrypt_ctx cx[1]);
int
aes_decrypt_key(const uint8_t *key, int key_len, aesedp_decrypt_ctx cx[1]);
int
aes_encrypt(const uint8_t *Plaintext, uint8_t *Ciphertext, aesedp_encrypt_ctx *ctx);
int
aes_decrypt(const uint8_t *Ciphertext, uint8_t *Plaintext, aesedp_decrypt_ctx *ctx);
enum {
CRYPT_OK=0,
CRYPT_ERROR=1,
CRYPT_INVALID_KEYSIZE=3,
CRYPT_INVALID_ARG=16,
};
static int
aesedp_keysize(int *keysize)
{
switch (*keysize) {
case 16:
case 24:
case 32:
return CRYPT_OK;
default:
return CRYPT_INVALID_KEYSIZE;
}
}
static int
aesedp_setup(const uint8_t *key, int keylen, int num_rounds __unused, aesedp_ctx *skey)
{
aesedp_ctx *ctx = (aesedp_ctx *) skey;
int retval;
if((retval = aesedp_keysize(&keylen)) != CRYPT_OK) return retval;
if((retval = aes_encrypt_key(key, keylen, &ctx->encrypt)) != CRYPT_OK) return CRYPT_ERROR;
if((retval = aes_decrypt_key(key, keylen, &ctx->decrypt)) != CRYPT_OK) return CRYPT_ERROR;
return CRYPT_OK;
}
#ifdef ZZZNEVER
static int
aesedp_ecb_encrypt(const uint8_t *pt, uint8_t *ct, aesedp_ctx *skey)
{
aesedp_ctx *ctx = (aesedp_ctx *) skey;
return aes_encrypt(pt, ct, &ctx->encrypt);
}
static int
aesedp_ecb_decrypt(const uint8_t *ct, uint8_t *pt, aesedp_ctx *skey)
{
return aes_decrypt(ct, pt, &skey->decrypt);
}
#endif
static void
aesedp_done(aesedp_ctx *skey __unused)
{
}
uint32_t
xts_start(uint32_t cipher, const uint8_t *IV __unused, const uint8_t *key1, int keylen,
const uint8_t *key2, int tweaklen __unused, uint32_t num_rounds, uint32_t options __unused, symmetric_xts *xts)
{
uint32_t err;
if((key1 == NULL)|| (key2 == NULL) || (xts == NULL)) return CRYPT_INVALID_ARG;
if ((err = aesedp_setup(key1, keylen, num_rounds, &xts->key1)) != 0) {
return err;
}
if ((err = aesedp_setup(key2, keylen, num_rounds, &xts->key2)) != 0) {
return err;
}
xts->cipher = cipher;
return err;
}
#if defined __x86_64__ || defined __i386__
extern void xts_mult_x(uint8_t *I);
#else
static void xts_mult_x(uint8_t *I)
{
uint32_t x;
uint8_t t, tt;
for (x = t = 0; x < 16; x++) {
tt = I[x] >> 7;
I[x] = ((I[x] << 1) | t) & 0xFF;
t = tt;
}
if (tt) {
I[0] ^= 0x87;
}
}
#endif
#if defined __x86_64__ || defined __i386__
extern int tweak_crypt(const uint8_t *P, uint8_t *C, uint8_t *T, aesedp_encrypt_ctx *ctx);
extern int tweak_crypt_group(const uint8_t *P, uint8_t *C, uint8_t *T, aesedp_encrypt_ctx *ctx, uint32_t lim);
#else
static int tweak_crypt(const uint8_t *P, uint8_t *C, uint8_t *T, aesedp_encrypt_ctx *ctx)
{
uint32_t x;
uint32_t err;
for (x = 0; x < 16; x += sizeof(uint64_t)) {
*((uint64_t*)&C[x]) = *((uint64_t*)&P[x]) ^ *((uint64_t*)&T[x]);
}
if ((err = aes_encrypt(C, C, ctx)) != CRYPT_OK) {
return CRYPT_INVALID_KEYSIZE;
}
for (x = 0; x < 16; x += sizeof(uint64_t)) {
*((uint64_t*)&C[x]) ^= *((uint64_t*)&T[x]);
}
xts_mult_x(T);
return CRYPT_OK;
}
#endif
int xts_encrypt(
const uint8_t *pt, unsigned long ptlen,
uint8_t *ct,
const uint8_t *tweak,
symmetric_xts *xts)
{
aesedp_encrypt_ctx *encrypt_ctx = &xts->key1.encrypt;
uint8_t PP[16], CC[16], T[16];
uint32_t i, m, mo, lim;
uint32_t err;
if((pt == NULL) || (ct == NULL)|| (tweak == NULL) || (xts == NULL)) return 1;
m = ptlen >> 4;
mo = ptlen & 15;
if (m == 0) {
return CRYPT_INVALID_ARG;
}
if ((err = aes_encrypt(tweak, T, &xts->key2.encrypt)) != 0) {
return CRYPT_INVALID_KEYSIZE;
}
if (mo == 0) {
lim = m;
} else {
lim = m - 1;
}
#if defined __x86_64__ || defined __i386__
if (lim>0) {
err = tweak_crypt_group(pt, ct, T, encrypt_ctx, lim);
ct += (lim<<4);
pt += (lim<<4);
}
#else
for (i = 0; i < lim; i++) {
err = tweak_crypt(pt, ct, T, encrypt_ctx);
ct += 16;
pt += 16;
}
#endif
if (mo > 0) {
if ((err = tweak_crypt(pt, CC, T, encrypt_ctx)) != 0) {
return err;
}
for (i = 0; i < mo; i++) {
PP[i] = pt[16+i];
ct[16+i] = CC[i];
}
for (; i < 16; i++) {
PP[i] = CC[i];
}
if ((err = tweak_crypt(PP, ct, T, encrypt_ctx)) != 0) {
return err;
}
}
return err;
}
#if defined __x86_64__ || defined __i386__
extern int tweak_uncrypt(const uint8_t *C, uint8_t *P, uint8_t *T, aesedp_decrypt_ctx *ctx);
extern int tweak_uncrypt_group(const uint8_t *C, uint8_t *P, uint8_t *T, aesedp_decrypt_ctx *ctx, uint32_t lim);
#else
static int tweak_uncrypt(const uint8_t *C, uint8_t *P, uint8_t *T, aesedp_decrypt_ctx *ctx)
{
uint32_t x;
uint32_t err;
for (x = 0; x < 16; x += sizeof(uint64_t)) {
*((uint64_t*)&P[x]) = *((uint64_t*)&C[x]) ^ *((uint64_t*)&T[x]);
}
err = aes_decrypt(P, P, ctx);
for (x = 0; x < 16; x += sizeof(uint64_t)) {
*((uint64_t*)&P[x]) ^= *((uint64_t*)&T[x]);
}
xts_mult_x(T);
return err;
}
#endif
int xts_decrypt(
const uint8_t *ct, unsigned long ptlen,
uint8_t *pt,
const uint8_t *tweak,
symmetric_xts *xts)
{
aesedp_decrypt_ctx *decrypt_ctx = &xts->key1.decrypt;
uint8_t PP[16], CC[16], T[16];
uint32_t i, m, mo, lim;
uint32_t err;
if((pt == NULL) || (ct == NULL)|| (tweak == NULL) || (xts == NULL)) return 1;
m = ptlen >> 4;
mo = ptlen & 15;
if (m == 0) {
return CRYPT_INVALID_ARG;
}
if ((err = aes_encrypt(tweak, T, &xts->key2.encrypt)) != 0) {
return CRYPT_INVALID_KEYSIZE;
}
if (mo == 0) {
lim = m;
} else {
lim = m - 1;
}
#if defined __x86_64__ || defined __i386__
if (lim>0) {
err = tweak_uncrypt_group(ct, pt, T, decrypt_ctx, lim);
ct += (lim<<4);
pt += (lim<<4);
}
#else
for (i = 0; i < lim; i++) {
err = tweak_uncrypt(ct, pt, T, decrypt_ctx);
ct += 16;
pt += 16;
}
#endif
if (mo > 0) {
memcpy(CC, T, 16);
xts_mult_x(CC);
if ((err = tweak_uncrypt(ct, PP, CC, decrypt_ctx)) != CRYPT_OK) {
return err;
}
for (i = 0; i < mo; i++) {
CC[i] = ct[16+i];
pt[16+i] = PP[i];
}
for (; i < 16; i++) {
CC[i] = PP[i];
}
if ((err = tweak_uncrypt(CC, pt, T, decrypt_ctx)) != CRYPT_OK) {
return err;
}
}
return CRYPT_OK;
}
void xts_done(symmetric_xts *xts)
{
if(xts == NULL) return;
aesedp_done(&xts->key1);
aesedp_done(&xts->key2);
}