Skip to content

Commit

Permalink
Add AESNI support from libcperciva
Browse files Browse the repository at this point in the history
* crypto_aes_aesni is the actual AESNI support.

* crypto_aes is a shim layer which calls crypto_aes_aesni or OpenSSL,
  depending on the CPU features and compiler support.

* cpusupport/Build detects compiler support for CPUID and AESNI
  intrinsics.

* cpusupport_x86_aesni detects whether the CPU supports AESNI.

None of this is hooked into the tarsnap build or used yet.
  • Loading branch information
cperciva committed Jul 6, 2015
1 parent 016bd3f commit 45efe0c
Show file tree
Hide file tree
Showing 10 changed files with 615 additions and 1 deletion.
3 changes: 2 additions & 1 deletion libcperciva/COPYRIGHT
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
The code and documentation in this directory ("libcperciva") is distributed
under the following terms:

Copyright 2005-2013 Colin Percival. All rights reserved.
Copyright 2005-2014 Colin Percival. All rights reserved.
Copyright 2014 Sean Kelly. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
Expand Down
13 changes: 13 additions & 0 deletions libcperciva/cpusupport/Build/cpusupport-X86-AESNI.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <wmmintrin.h>

static char a[16];

int main(void)
{
__m128i x, y;

x = _mm_loadu_si128((__m128i *)a);
y = _mm_aesenc_si128(x, x);
_mm_storeu_si128((__m128i *)a, y);
return (a[0]);
}
8 changes: 8 additions & 0 deletions libcperciva/cpusupport/Build/cpusupport-X86-CPUID.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <cpuid.h>

int main(void)
{
unsigned int a, b, c, d;

return __get_cpuid(0, &a, &b, &c, &d);
}
37 changes: 37 additions & 0 deletions libcperciva/cpusupport/Build/cpusupport.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Should be sourced by `command -p sh mk-h.sh` from within a Makefile
# Standard output should be written to cpusupport-config.h, which is both a
# C header file defining CPUSUPPORT_ARCH_FEATURE macros and sourceable sh
# code which sets CFLAGS_ARCH_FEATURE environment variables.
feature() {
ARCH=$1
FEATURE=$2
shift 2;
printf "Checking if compiler supports $ARCH $FEATURE feature..." 1>&2
for CFLAG in "$@"; do
if ${CC} ${CFLAGS} -D_POSIX_C_SOURCE=200809L ${CFLAG} \
cpusupport-$ARCH-$FEATURE.c 2>/dev/null; then
rm -f a.out
break;
fi
CFLAG=NOTSUPPORTED;
done
case $CFLAG in
NOTSUPPORTED)
echo " no" 1>&2
;;
"")
echo " yes" 1>&2
echo "#define CPUSUPPORT_${ARCH}_${FEATURE}"
;;
*)
echo " yes, via $CFLAG" 1>&2
echo "#define CPUSUPPORT_${ARCH}_${FEATURE}"
echo "#ifdef cpusupport_dummy"
echo "export CFLAGS_${ARCH}_${FEATURE}=\"${CFLAG}\""
echo "#endif"
;;
esac
}

feature X86 CPUID ""
feature X86 AESNI "" "-maes" "-maes -Wno-cast-align" "-maes -Wno-missing-prototypes -Wno-cast-qual"
63 changes: 63 additions & 0 deletions libcperciva/cpusupport/cpusupport.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef _CPUSUPPORT_H_
#define _CPUSUPPORT_H_

/*
* To enable support for non-portable CPU features at compile time, one or
* more CPUSUPPORT_ARCH_FEATURE macros should be defined. This can be done
* directly on the compiler command line; or a file can be created with the
* necessary #define lines and then -D CPUSUPPORT_CONFIG_FILE=cpuconfig.h
* (or similar) can be provided to include that file here.
*/
#ifdef CPUSUPPORT_CONFIG_FILE
#include CPUSUPPORT_CONFIG_FILE
#endif

/*
* The CPUSUPPORT_FEATURE macro declares the necessary variables and
* functions for detecting CPU feature support at run time. The function
* defined in the macro acts to cache the result of the ..._detect function
* using the ..._present and ..._init variables.
*/
#define CPUSUPPORT_FEATURE(arch, feature) \
extern int cpusupport_ ## arch ## _ ## feature ## _present; \
extern int cpusupport_ ## arch ## _ ## feature ## _init; \
int cpusupport_ ## arch ## _ ## feature ## _detect(void); \
\
static inline int \
cpusupport_ ## arch ## _ ## feature(void) \
{ \
\
if (cpusupport_ ## arch ## _ ## feature ## _present) \
return (1); \
else if (cpusupport_ ## arch ## _ ## feature ## _init) \
return (0); \
cpusupport_ ## arch ## _ ## feature ## _present = \
cpusupport_ ## arch ##_ ## feature ## _detect(); \
cpusupport_ ## arch ## _ ## feature ## _init = 1; \
return (cpusupport_ ## arch ## _ ## feature ## _present); \
} \
struct cpusupport_ ## arch ## _ ## feature ## _dummy

/*
* CPUSUPPORT_FEATURE_DECL(arch, feature):
* Macro which defines variables and provides a function declaration for
* detecting the presence of "feature" on the "arch" architecture. The
* function body following this macro expansion must return nonzero if the
* feature is present, or zero if the feature is not present or the detection
* fails for any reason.
*/
#define CPUSUPPORT_FEATURE_DECL(arch, feature) \
int cpusupport_ ## arch ## _ ## feature ## _present = 0; \
int cpusupport_ ## arch ## _ ## feature ## _init = 0; \
int \
cpusupport_ ## arch ## _ ## feature ## _detect(void)

/*
* Any features listed here must have associated C files compiled and linked
* in, since the macro references symbols which must be defined. Projects
* which do not need to detect certain CPU features may wish to remove lines
* from this list so that the associated C files can be omitted.
*/
CPUSUPPORT_FEATURE(x86, aesni);

#endif /* !_CPUSUPPORT_H_ */
30 changes: 30 additions & 0 deletions libcperciva/cpusupport/cpusupport_x86_aesni.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "cpusupport.h"

#ifdef CPUSUPPORT_X86_CPUID
#include <cpuid.h>
#endif

#define CPUID_AESNI_BIT (1 << 25)

CPUSUPPORT_FEATURE_DECL(x86, aesni)
{
#ifdef CPUSUPPORT_X86_CPUID
unsigned int eax, ebx, ecx, edx;

/* Check if CPUID supports the level we need. */
if (!__get_cpuid(0, &eax, &ebx, &ecx, &edx))
goto unsupported;
if (eax < 1)
goto unsupported;

/* Ask about CPU features. */
if (!__get_cpuid(1, &eax, &ebx, &ecx, &edx))
goto unsupported;

/* Return the relevant feature bit. */
return (ecx & CPUID_AESNI_BIT);

unsupported:
#endif
return (0);
}
165 changes: 165 additions & 0 deletions libcperciva/crypto/crypto_aes.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/aes.h>

#include "cpusupport.h"
#include "crypto_aes_aesni.h"
#include "warnp.h"

#include "crypto_aes.h"

/**
* This represents either an AES_KEY or a struct crypto_aes_key_aesni; we
* know which it is based on whether we're using AESNI code or not. As such,
* it's just an opaque pointer; but declaring it as a named structure type
* prevents type-mismatch bugs in upstream code.
*/
struct crypto_aes_key;

#ifdef CPUSUPPORT_X86_AESNI
/* Test whether OpenSSL and AESNI code produce the same AES ciphertext. */
static int
aesnitest(uint8_t ptext[16], uint8_t * key, size_t keylen)
{
AES_KEY kexp_openssl;
void * kexp_aesni;
uint8_t ctext_openssl[16];
uint8_t ctext_aesni[16];

/* Expand the key. */
AES_set_encrypt_key(key, keylen * 8, &kexp_openssl);
if ((kexp_aesni = crypto_aes_key_expand_aesni(key, keylen)) == NULL)
goto err0;

/* Encrypt the block. */
AES_encrypt(ptext, ctext_openssl, &kexp_openssl);
crypto_aes_encrypt_block_aesni(ptext, ctext_aesni, kexp_aesni);

/* Free the AESNI expanded key. */
crypto_aes_key_free_aesni(kexp_aesni);

/* Do the outputs match? */
return (memcmp(ctext_openssl, ctext_aesni, 16));

err0:
/* Failure! */
return (-1);
}

/* Should we use AESNI? */
static int
useaesni(void)
{
static int aesnigood = -1;
uint8_t key[32];
uint8_t ptext[16];
size_t i;

/* If we haven't decided which code to use yet, decide now. */
while (aesnigood == -1) {
/* Default to OpenSSL. */
aesnigood = 0;

/* If the CPU doesn't claim to support AESNI, stop here. */
if (!cpusupport_x86_aesni())
break;

/* Test cases: key is 0x00010203..., ptext is 0x00112233... */
for (i = 0; i < 16; i++)
ptext[i] = 0x11 * i;
for (i = 0; i < 32; i++)
key[i] = i;

/* Test that AESNI and OpenSSL produce the same results. */
if (aesnitest(ptext, key, 16) || aesnitest(ptext, key, 32)) {
warn0("Disabling AESNI due to failed self-test");
break;
}

/* AESNI works; use it. */
aesnigood = 1;
}

return (aesnigood);
}
#endif /* CPUSUPPORT_X86_AESNI */

/**
* crypto_aes_key_expand(key, len):
* Expand the ${len}-byte AES key ${key} into a structure which can be passed
* to crypto_aes_encrypt_block. The length must be 16 or 32.
*/
struct crypto_aes_key *
crypto_aes_key_expand(const uint8_t * key, size_t len)
{
AES_KEY * kexp;

/* Sanity-check. */
assert((len == 16) || (len == 32));

#ifdef CPUSUPPORT_X86_AESNI
/* Use AESNI if we can. */
if (useaesni())
return (crypto_aes_key_expand_aesni(key, len));
#endif

/* Allocate structure. */
if ((kexp = malloc(sizeof(AES_KEY))) == NULL)
goto err0;

/* Expand the key. */
AES_set_encrypt_key(key, len * 8, kexp);

/* Success! */
return ((void *)kexp);

err0:
/* Failure! */
return (NULL);
}

/**
* crypto_aes_encrypt_block(in, out, key):
* Using the expanded AES key ${key}, encrypt the block ${in} and write the
* resulting ciphertext to ${out}.
*/
void
crypto_aes_encrypt_block(const uint8_t * in, uint8_t * out,
const struct crypto_aes_key * key)
{

#ifdef CPUSUPPORT_X86_AESNI
if (useaesni()) {
crypto_aes_encrypt_block_aesni(in, out, (const void *)key);
return;
}
#endif

/* Get AES to do the work. */
AES_encrypt(in, out, (const void *)key);
}

/**
* crypto_aes_key_free(key):
* Free the expanded AES key ${key}.
*/
void
crypto_aes_key_free(struct crypto_aes_key * key)
{

#ifdef CPUSUPPORT_X86_AESNI
if (useaesni()) {
crypto_aes_key_free_aesni((void *)key);
return;
}
#endif

/* Attempt to zero the expanded key. */
memset(key, 0, sizeof(AES_KEY));

/* Free the key. */
free(key);
}
31 changes: 31 additions & 0 deletions libcperciva/crypto/crypto_aes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef _CRYPTO_AES_H_
#define _CRYPTO_AES_H_

#include <stddef.h>
#include <stdint.h>

/* Opaque structure. */
struct crypto_aes_key;

/**
* crypto_aes_key_expand(key, len):
* Expand the ${len}-byte AES key ${key} into a structure which can be passed
* to crypto_aes_encrypt_block. The length must be 16 or 32.
*/
struct crypto_aes_key * crypto_aes_key_expand(const uint8_t *, size_t);

/**
* crypto_aes_encrypt_block(in, out, key):
* Using the expanded AES key ${key}, encrypt the block ${in} and write the
* resulting ciphertext to ${out}.
*/
void crypto_aes_encrypt_block(const uint8_t *, uint8_t *,
const struct crypto_aes_key *);

/**
* crypto_aes_key_free(key):
* Free the expanded AES key ${key}.
*/
void crypto_aes_key_free(struct crypto_aes_key *);

#endif /* !_CRYPTO_AES_H_ */
Loading

0 comments on commit 45efe0c

Please sign in to comment.