Skip to content

Commit

Permalink
introduce separate aes_desc
Browse files Browse the repository at this point in the history
`aes_desc` and `aes_enc_desc` now do auto-detection of the best suitable
AES implementation for the platform.

Signed-off-by: Steffen Jaeckel <[email protected]>
  • Loading branch information
sjaeckel committed Jun 21, 2023
1 parent 2cd52e1 commit 5446c12
Show file tree
Hide file tree
Showing 17 changed files with 310 additions and 104 deletions.
23 changes: 16 additions & 7 deletions doc/crypt.tex
Original file line number Diff line number Diff line change
Expand Up @@ -614,7 +614,10 @@ \subsection{Simple Encryption Demonstration}
\hline SAFER+ & saferp\_desc &16 & 16, 24, 32 & 8, 12, 16 & 4 \\
\hline AES & aes\_desc & 16 & 16, 24, 32 & 10, 12, 14 & 6 \\
& aes\_enc\_desc & 16 & 16, 24, 32 & 10, 12, 14 & 6 \\
& aesni\_desc & 16 & 16, 24, 32 & 10, 12, 14 & 6 \\
& rijndael\_desc & 16 & 16, 24, 32 & 10, 12, 14 & 6 \\
& rijndael\_enc\_desc & 16 & 16, 24, 32 & 10, 12, 14 & 6 \\
\hline AES & aesni\_desc & 16 & 16, 24, 32 & 10, 12, 14 & 6 \\
(only on x86 with SSE4.1) &&&&& \\
\hline Twofish & twofish\_desc & 16 & 16, 24, 32 & 16 & 7 \\
\hline DES & des\_desc & 8 & 8 & 16 & 13 \\
\hline 3DES (EDE mode) & des3\_desc & 8 & 16, 24 & 16 & 14 \\
Expand All @@ -640,24 +643,30 @@ \subsection{Notes}
\begin{small}
\begin{enumerate}
\item
For AES, (also known as Rijndael) there are four descriptors which complicate issues a little. The descriptors
rijndael\_desc and rijndael\_enc\_desc provide the cipher named \textit{rijndael}. The descriptors aes\_desc and
aes\_enc\_desc provide the cipher name \textit{aes}. Functionally both \textit{rijndael} and \textit{aes} are the same cipher. The
For AES, (also known as Rijndael) there are multiple descriptors which complicate issues a little. As of FIXME-version-next
the library also integrates hardware-accelerated AES operations (e.g. AES-NI on amd64 with SSE4.1). Therefor the functionality of
the descriptors for the AES algorithm has changed. The rijndael\_desc and rijndael\_enc\_desc descriptors provide the cipher
named \textit{rijndael} which contains the software implementation of the algorithm. The descriptors aes\_desc and aes\_enc\_desc
provide the cipher named \textit{aes} and implement an auto-detection mechanism that chooses between the software-only \textit{rijndael}
and the hardware-accelerated implementation.

Functionally both \textit{rijndael} and \textit{aes} are the same cipher. The
only difference is when you call find\_cipher() you have to pass the correct name. The cipher descriptors with \textit{enc}
in the middle (e.g. rijndael\_enc\_desc) are related to an implementation of Rijndael with only the encryption routine
and tables. The decryption and self--test function pointers of both \textit{encrypt only} descriptors are set to \textbf{NULL} and
should not be called.

The \textit{encrypt only} descriptors are useful for applications that only use the encryption function of the cipher. Algorithms such
as EAX, PMAC and OMAC only require the encryption function. So far this \textit{encrypt only} functionality has only been implemented for
Rijndael as it makes the most sense for this cipher.
as EAX, PMAC and OMAC or the CTR mode only require the encryption function. So far this \textit{encrypt only} functionality has only
been implemented for Rijndael as it makes the most sense for this cipher.

\item
Note that for \textit{DES} and \textit{3DES} they use 8 and 24 byte keys but only 7 and 21 [respectively] bytes of the keys are in
fact used for the purposes of encryption. My suggestion is just to use random 8/24 byte keys instead of trying to make a 8/24
byte string from the real 7/21 byte key.

For \textit{3DES} exists a two-key mode, that can be initialized by calling the setup function with a \textit{keylen} of 16. This results in the re-usage of key \textit{K1} as key \textit{K3}. This mode has been specified as \textit{Keying Option 2} in FIPS 46-3.
For \textit{3DES} exists a two-key mode, that can be initialized by calling the setup function with a \textit{keylen} of 16.
This results in the re-usage of key \textit{K1} as key \textit{K3}. This mode has been specified as \textit{Keying Option 2} in FIPS 46-3.

\item
Note that \textit{Twofish} has additional configuration options (Figure \ref{fig:twofishopts}) that take place at build time. These options are found in
Expand Down
2 changes: 1 addition & 1 deletion helper.pl
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ sub process_makefiles {
my @t = qw();
find({ no_chdir => 1, wanted => sub { push @t, $_ if $_ =~ /(common|no_prng|_tests?|test).c$/ } }, 'tests');

my @o = sort ('src/ciphers/aes/aes_enc.o', map { my $x = $_; $x =~ s/\.c$/.o/; $x } @c);
my @o = sort ('src/ciphers/aes/aes_enc.o', 'src/ciphers/aes/aes_enc_desc.o', map { my $x = $_; $x =~ s/\.c$/.o/; $x } @c);
my $var_o = prepare_variable("OBJECTS", @o);
my $var_h = prepare_variable("HEADERS_PUB", (sort @h));
(my $var_obj = $var_o) =~ s/\.o\b/.obj/sg;
Expand Down
5 changes: 5 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ LTC_EXTRALIBS += $(EXTRALIBS)

#AES comes in two flavours... enc+dec and enc
src/ciphers/aes/aes_enc.o: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
ifneq ($V,1)
@echo " * ${CC} $@" ${silent_echo}
endif
${silent} ${CC} ${LTC_CFLAGS} -DENCRYPT_ONLY -c $< -o $@
src/ciphers/aes/aes_enc_desc.o: src/ciphers/aes/aes_desc.c
ifneq ($V,1)
@echo " * ${CC} $@" ${silent_echo}
endif
Expand Down
2 changes: 2 additions & 0 deletions makefile.mingw
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ default: $(LIBMAIN_S)
#SPECIAL: AES comes in two flavours - enc+dec and enc-only
src/ciphers/aes/aes_enc.o: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
$(CC) $(LTC_CFLAGS) -DENCRYPT_ONLY -c src/ciphers/aes/aes.c -o src/ciphers/aes/aes_enc.o
src/ciphers/aes/aes_enc_desc.o: src/ciphers/aes/aes.c
$(CC) $(LTC_CFLAGS) -DENCRYPT_ONLY -c src/ciphers/aes/aes_desc.c -o src/ciphers/aes/aes_enc_desc.o

#SPECIAL: these are the rules to make certain object files
src/ciphers/aes/aes.o: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
Expand Down
2 changes: 2 additions & 0 deletions makefile.msvc
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ default: $(LIBMAIN_S)
#SPECIAL: AES comes in two flavours - enc+dec and enc-only
src/ciphers/aes/aes_enc.obj: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
$(CC) $(LTC_CFLAGS) /DENCRYPT_ONLY /c src/ciphers/aes/aes.c /Fosrc/ciphers/aes/aes_enc.obj
src/ciphers/aes/aes_enc_desc.obj: src/ciphers/aes/aes_desc.c
$(CC) $(LTC_CFLAGS) /DENCRYPT_ONLY /c src/ciphers/aes/aes_desc.c /Fosrc/ciphers/aes/aes_enc_desc.obj

#SPECIAL: these are the rules to make certain object files
src/ciphers/aes/aes.obj: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
Expand Down
2 changes: 2 additions & 0 deletions makefile.shared
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ endif
#ciphers come in two flavours... enc+dec and enc
src/ciphers/aes/aes_enc.o: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
$(LTCOMPILE) $(LTC_CFLAGS) $(CPPFLAGS) $(LTC_LDFLAGS) -DENCRYPT_ONLY -c src/ciphers/aes/aes.c -o src/ciphers/aes/aes_enc.o
src/ciphers/aes/aes_enc_desc.o: src/ciphers/aes/aes_desc.c
$(LTCOMPILE) $(LTC_CFLAGS) $(CPPFLAGS) $(LTC_LDFLAGS) -DENCRYPT_ONLY -c src/ciphers/aes/aes_desc.c -o src/ciphers/aes/aes_enc_desc.o

.c.o:
$(LTCOMPILE) $(LTC_CFLAGS) $(CPPFLAGS) $(LTC_LDFLAGS) -o $@ -c $<
Expand Down
2 changes: 2 additions & 0 deletions makefile.unix
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ default: $(LIBMAIN_S)
#SPECIAL: AES comes in two flavours - enc+dec and enc-only
src/ciphers/aes/aes_enc.o: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
$(CC) $(LTC_CFLAGS) -DENCRYPT_ONLY -c src/ciphers/aes/aes.c -o src/ciphers/aes/aes_enc.o
src/ciphers/aes/aes_enc_desc.o: src/ciphers/aes/aes_desc.c
$(CC) $(LTC_CFLAGS) -DENCRYPT_ONLY -c src/ciphers/aes/aes_desc.c -o src/ciphers/aes/aes_enc_desc.o

#SPECIAL: these are the rules to make certain object files
src/ciphers/aes/aes.o: src/ciphers/aes/aes.c src/ciphers/aes/aes_tab.c
Expand Down
1 change: 1 addition & 0 deletions makefile_include.mk
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ src/hashes/sha2/sha512_224.o: src/hashes/sha2/sha512.c src/hashes/sha2/sha512_22
src/hashes/sha2/sha512_256.o: src/hashes/sha2/sha512.c src/hashes/sha2/sha512_256.c
src/hashes/whirl/whirl.o: src/hashes/whirl/whirl.c src/hashes/whirl/whirltab.c


$(DOBJECTS): LTC_CFLAGS := -Itests $(LTC_CFLAGS)
$(TOBJECTS): LTC_CFLAGS := -Itests $(LTC_CFLAGS)

Expand Down
18 changes: 0 additions & 18 deletions src/ciphers/aes/aes.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,6 @@ const struct ltc_cipher_descriptor rijndael_desc =
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

const struct ltc_cipher_descriptor aes_desc =
{
"aes",
6,
16, 32, 16, 10,
SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

#else

#define SETUP rijndael_enc_setup
Expand All @@ -69,15 +60,6 @@ const struct ltc_cipher_descriptor rijndael_enc_desc =
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

const struct ltc_cipher_descriptor aes_enc_desc =
{
"aes",
6,
16, 32, 16, 10,
SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

#endif

#define LTC_AES_TAB_C
Expand Down
244 changes: 244 additions & 0 deletions src/ciphers/aes/aes_desc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */

/* Auto-detection of AES implementation by Steffen Jaeckel */
/**
@file aes_desc.c
Run-time detection of correct AES implementation
*/

#include "tomcrypt_private.h"

#if defined(LTC_RIJNDAEL)

#ifndef ENCRYPT_ONLY

#define AES_SETUP aes_setup
#define AES_ENC aes_ecb_encrypt
#define AES_DEC aes_ecb_decrypt
#define AES_DONE aes_done
#define AES_TEST aes_test
#define AES_KS aes_keysize

const struct ltc_cipher_descriptor aes_desc =
{
"aes",
6,
16, 32, 16, 10,
AES_SETUP, AES_ENC, AES_DEC, AES_TEST, AES_DONE, AES_KS,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

#else

#define AES_SETUP aes_enc_setup
#define AES_ENC aes_enc_ecb_encrypt
#define AES_DONE aes_enc_done
#define AES_KS aes_enc_keysize

const struct ltc_cipher_descriptor aes_enc_desc =
{
"aes",
6,
16, 32, 16, 10,
AES_SETUP, AES_ENC, NULL, NULL, AES_DONE, AES_KS,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

#endif

/* Code partially borrowed from https://software.intel.com/content/www/us/en/develop/articles/intel-sha-extensions.html */
#if defined(LTC_HAS_AES_NI)
static LTC_INLINE int s_aesni_is_supported(void)
{
static int initialized = 0, is_supported = 0;

if (initialized == 0) {
int a, b, c, d;

/* Look for CPUID.1.0.ECX[25]
* EAX = 1, ECX = 0
*/
a = 1;
c = 0;

asm volatile ("cpuid"
:"=a"(a), "=b"(b), "=c"(c), "=d"(d)
:"a"(a), "c"(c)
);

is_supported = ((c >> 25) & 1);
initialized = 1;
}

return is_supported;
}

#ifndef ENCRYPT_ONLY
int aesni_is_supported(void)
{
return s_aesni_is_supported();
}
#endif
#endif

/**
Initialize the AES (Rijndael) block cipher
@param key The symmetric key you wish to pass
@param keylen The key length in bytes
@param num_rounds The number of rounds desired (0 for default)
@param skey The key in as scheduled by this function.
@return CRYPT_OK if successful
*/
int AES_SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey)
{
#ifdef LTC_HAS_AES_NI
if (s_aesni_is_supported()) {
return aesni_setup(key, keylen, num_rounds, skey);
}
#endif
/* Last resort, software AES */
return rijndael_setup(key, keylen, num_rounds, skey);
}

/**
Encrypts a block of text with AES
@param pt The input plaintext (16 bytes)
@param ct The output ciphertext (16 bytes)
@param skey The key as scheduled
@return CRYPT_OK if successful
*/
int AES_ENC(const unsigned char *pt, unsigned char *ct, const symmetric_key *skey)
{
#ifdef LTC_HAS_AES_NI
if (s_aesni_is_supported()) {
return aesni_ecb_encrypt(pt, ct, skey);
}
#endif
return rijndael_ecb_encrypt(pt, ct, skey);
}


/**
Decrypts a block of text with AES
@param ct The input ciphertext (16 bytes)
@param pt The output plaintext (16 bytes)
@param skey The key as scheduled
@return CRYPT_OK if successful
*/
int AES_DEC(const unsigned char *ct, unsigned char *pt, const symmetric_key *skey)
{
#ifdef LTC_HAS_AES_NI
if (s_aesni_is_supported()) {
return aesni_ecb_decrypt(ct, pt, skey);
}
#endif
return rijndael_ecb_decrypt(ct, pt, skey);
}

/**
Performs a self-test of the AES block cipher
@return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled
*/
int AES_TEST(void)
{
#ifndef LTC_TEST
return CRYPT_NOP;
#else
int err;
static const struct {
int keylen;
unsigned char key[32], pt[16], ct[16];
} tests[] = {
{ 16,
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
{ 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a }
}, {
24,
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 },
{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
{ 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0,
0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91 }
}, {
32,
{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff },
{ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 }
}
};

symmetric_key key;
unsigned char tmp[2][16];
int i, y;

for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) {
zeromem(&key, sizeof(key));
if ((err = aes_setup(tests[i].key, tests[i].keylen, 0, &key)) != CRYPT_OK) {
return err;
}

aes_ecb_encrypt(tests[i].pt, tmp[0], &key);
aes_ecb_decrypt(tmp[0], tmp[1], &key);
if (compare_testvector(tmp[0], 16, tests[i].ct, 16, "AES Encrypt", i) ||
compare_testvector(tmp[1], 16, tests[i].pt, 16, "AES Decrypt", i)) {
return CRYPT_FAIL_TESTVECTOR;
}

/* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */
for (y = 0; y < 16; y++) tmp[0][y] = 0;
for (y = 0; y < 1000; y++) aes_ecb_encrypt(tmp[0], tmp[0], &key);
for (y = 0; y < 1000; y++) aes_ecb_decrypt(tmp[0], tmp[0], &key);
for (y = 0; y < 16; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR;
}
return CRYPT_OK;
#endif
}


/** Terminate the context
@param skey The scheduled key
*/
void AES_DONE(symmetric_key *skey)
{
LTC_UNUSED_PARAM(skey);
}


/**
Gets suitable key size
@param keysize [in/out] The length of the recommended key (in bytes). This function will store the suitable size back in this variable.
@return CRYPT_OK if the input key size is acceptable.
*/
int AES_KS(int *keysize)
{
LTC_ARGCHK(keysize != NULL);

if (*keysize < 16) {
return CRYPT_INVALID_KEYSIZE;
}
if (*keysize < 24) {
*keysize = 16;
return CRYPT_OK;
}
if (*keysize < 32) {
*keysize = 24;
return CRYPT_OK;
}
*keysize = 32;
return CRYPT_OK;
}

#endif

Loading

0 comments on commit 5446c12

Please sign in to comment.