blob: eb96b593186135e7e0815add2d419ad843c268d6 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2015, Linaro Limited
* All rights reserved.
* Copyright (c) 2001-2007, Tom St Denis
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
*/
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
*
* LibTomCrypt is a library that provides various cryptographic
* algorithms in a highly modular and flexible manner.
*
* The library is free for all purposes without any express
* guarantee it works.
*
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
*/
/*
* AES cipher for ARMv8 with Crypto Extensions
*
* Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
*/
#include <tomcrypt_private.h>
#include "tomcrypt_arm_neon.h"
typedef unsigned int u32;
typedef unsigned char u8;
/* Prototypes for assembly functions */
uint32_t ce_aes_sub(uint32_t in);
void ce_aes_invert(void *dst, void *src);
void ce_aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds,
int blocks, int first);
void ce_aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds,
int blocks, int first);
void ce_aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds,
int blocks, u8 iv[]);
void ce_aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds,
int blocks, u8 iv[]);
void ce_aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds,
int blocks, u8 ctr[], int first);
void ce_aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds,
int blocks, u8 const rk2[], u8 iv[]);
void ce_aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds,
int blocks, u8 const rk2[], u8 iv[]);
struct aes_block {
u8 b[16];
};
static inline u32 ror32(u32 val, u32 shift)
{
return (val >> shift) | (val << (32 - shift));
}
int rijndael_setup(const unsigned char *key, int keylen, int num_rounds,
symmetric_key *skey)
{
/* The AES key schedule round constants */
static u8 const rcon[] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36,
};
u32 kwords = keylen / sizeof(u32);
struct aes_block *key_enc, *key_dec;
struct tomcrypt_arm_neon_state state;
unsigned int i, j;
void *p;
LTC_ARGCHK(key);
LTC_ARGCHK(skey);
if (keylen != 16 && keylen != 24 && keylen != 32)
return CRYPT_INVALID_KEYSIZE;
if (num_rounds != 0 && num_rounds != (10 + ((keylen/8)-2)*2))
return CRYPT_INVALID_ROUNDS;
num_rounds = 10 + ((keylen/8)-2)*2;
skey->rijndael.Nr = num_rounds;
memcpy(skey->rijndael.eK, key, keylen);
tomcrypt_arm_neon_enable(&state);
for (i = 0; i < sizeof(rcon); i++) {
u32 *rki;
u32 *rko;
p = skey->rijndael.eK;
rki = (u32 *)p + (i * kwords);
rko = rki + kwords;
rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8)
^ rcon[i] ^ rki[0];
rko[1] = rko[0] ^ rki[1];
rko[2] = rko[1] ^ rki[2];
rko[3] = rko[2] ^ rki[3];
if (keylen == 24) {
if (i >= 7)
break;
rko[4] = rko[3] ^ rki[4];
rko[5] = rko[4] ^ rki[5];
} else if (keylen == 32) {
if (i >= 6)
break;
rko[4] = ce_aes_sub(rko[3]) ^ rki[4];
rko[5] = rko[4] ^ rki[5];
rko[6] = rko[5] ^ rki[6];
rko[7] = rko[6] ^ rki[7];
}
}
/*
* Generate the decryption keys for the Equivalent Inverse Cipher.
* This involves reversing the order of the round keys, and applying
* the Inverse Mix Columns transformation on all but the first and
* the last one.
*/
p = skey->rijndael.eK;
key_enc = (struct aes_block *)p;
p = skey->rijndael.dK;
key_dec = (struct aes_block *)p;
j = num_rounds;
key_dec[0] = key_enc[j];
for (i = 1, j--; j > 0; i++, j--)
ce_aes_invert(key_dec + i, key_enc + j);
key_dec[i] = key_enc[0];
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
void rijndael_done(symmetric_key *skey)
{
}
int rijndael_keysize(int *keysize)
{
LTC_ARGCHK(keysize);
if (*keysize < 16)
return CRYPT_INVALID_KEYSIZE;
else if (*keysize < 24)
*keysize = 16;
else if (*keysize < 32)
*keysize = 24;
else
*keysize = 32;
return CRYPT_OK;
}
static int aes_ecb_encrypt_nblocks(const unsigned char *pt, unsigned char *ct,
unsigned long blocks,
const symmetric_key *skey)
{
struct tomcrypt_arm_neon_state state;
u8 *rk;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(skey);
Nr = skey->rijndael.Nr;
rk = (u8 *)skey->rijndael.eK;
tomcrypt_arm_neon_enable(&state);
ce_aes_ecb_encrypt(ct, pt, rk, Nr, blocks, 1);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
static int aes_ecb_decrypt_nblocks(const unsigned char *ct, unsigned char *pt,
unsigned long blocks,
const symmetric_key *skey)
{
struct tomcrypt_arm_neon_state state;
u8 *rk;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(skey);
Nr = skey->rijndael.Nr;
rk = (u8 *)skey->rijndael.dK;
tomcrypt_arm_neon_enable(&state);
ce_aes_ecb_decrypt(pt, ct, rk, Nr, blocks, 1);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct,
const symmetric_key *skey)
{
return aes_ecb_encrypt_nblocks(pt, ct, 1, skey);
}
int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt,
const symmetric_key *skey)
{
return aes_ecb_decrypt_nblocks(ct, pt, 1, skey);
}
static int aes_cbc_encrypt_nblocks(const unsigned char *pt, unsigned char *ct,
unsigned long blocks, unsigned char *IV,
symmetric_key *skey)
{
struct tomcrypt_arm_neon_state state;
u8 *rk;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(IV);
LTC_ARGCHK(skey);
Nr = skey->rijndael.Nr;
rk = (u8 *)skey->rijndael.eK;
tomcrypt_arm_neon_enable(&state);
ce_aes_cbc_encrypt(ct, pt, rk, Nr, blocks, IV);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
static int aes_cbc_decrypt_nblocks(const unsigned char *ct, unsigned char *pt,
unsigned long blocks, unsigned char *IV,
symmetric_key *skey)
{
struct tomcrypt_arm_neon_state state;
u8 *rk;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(IV);
LTC_ARGCHK(skey);
Nr = skey->rijndael.Nr;
rk = (u8 *)skey->rijndael.dK;
tomcrypt_arm_neon_enable(&state);
ce_aes_cbc_decrypt(pt, ct, rk, Nr, blocks, IV);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
static int aes_ctr_encrypt_nblocks(const unsigned char *pt, unsigned char *ct,
unsigned long blocks, unsigned char *IV,
int mode, symmetric_key *skey)
{
struct tomcrypt_arm_neon_state state;
u8 *rk;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(IV);
LTC_ARGCHK(skey);
if (mode == CTR_COUNTER_LITTLE_ENDIAN) {
/* Accelerated algorithm supports big endian only */
return CRYPT_ERROR;
}
Nr = skey->rijndael.Nr;
rk = (u8 *)skey->rijndael.eK;
tomcrypt_arm_neon_enable(&state);
ce_aes_ctr_encrypt(ct, pt, rk, Nr, blocks, IV, 1);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
static int aes_xts_encrypt_nblocks(const unsigned char *pt, unsigned char *ct,
unsigned long blocks, unsigned char *tweak,
const symmetric_key *skey1,
const symmetric_key *skey2)
{
struct tomcrypt_arm_neon_state state;
u8 *rk1, *rk2;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(tweak);
LTC_ARGCHK(skey1);
LTC_ARGCHK(skey2);
LTC_ARGCHK(skey1->rijndael.Nr == skey2->rijndael.Nr);
Nr = skey1->rijndael.Nr;
rk1 = (u8 *)skey1->rijndael.eK;
rk2 = (u8 *)skey2->rijndael.eK;
tomcrypt_arm_neon_enable(&state);
ce_aes_xts_encrypt(ct, pt, rk1, Nr, blocks, rk2, tweak);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
static int aes_xts_decrypt_nblocks(const unsigned char *ct, unsigned char *pt,
unsigned long blocks, unsigned char *tweak,
const symmetric_key *skey1,
const symmetric_key *skey2)
{
struct tomcrypt_arm_neon_state state;
u8 *rk1, *rk2;
int Nr;
LTC_ARGCHK(pt);
LTC_ARGCHK(ct);
LTC_ARGCHK(tweak);
LTC_ARGCHK(skey1);
LTC_ARGCHK(skey2);
LTC_ARGCHK(skey1->rijndael.Nr == skey2->rijndael.Nr);
Nr = skey1->rijndael.Nr;
rk1 = (u8 *)skey1->rijndael.dK;
rk2 = (u8 *)skey2->rijndael.eK;
tomcrypt_arm_neon_enable(&state);
ce_aes_xts_decrypt(pt, ct, rk1, Nr, blocks, rk2, tweak);
tomcrypt_arm_neon_disable(&state);
return CRYPT_OK;
}
const struct ltc_cipher_descriptor aes_desc = {
.name = "aes",
.ID = 6,
.min_key_length = 16,
.max_key_length = 32,
.block_length = 16,
.default_rounds = 10,
.setup = rijndael_setup,
.ecb_encrypt = rijndael_ecb_encrypt,
.ecb_decrypt = rijndael_ecb_decrypt,
.done = rijndael_done,
.keysize = rijndael_keysize,
.accel_ecb_encrypt = aes_ecb_encrypt_nblocks,
.accel_ecb_decrypt = aes_ecb_decrypt_nblocks,
.accel_cbc_encrypt = aes_cbc_encrypt_nblocks,
.accel_cbc_decrypt = aes_cbc_decrypt_nblocks,
.accel_ctr_encrypt = aes_ctr_encrypt_nblocks,
.accel_xts_encrypt = aes_xts_encrypt_nblocks,
.accel_xts_decrypt = aes_xts_decrypt_nblocks,
};