lib: crypto: Plumb in the Intel AES instructions.
authorJeremy Allison <jra@samba.org>
Wed, 6 Sep 2017 18:40:02 +0000 (11:40 -0700)
committerJeremy Allison <jra@samba.org>
Thu, 7 Sep 2017 00:01:08 +0000 (02:01 +0200)
Causes:

AES_set_encrypt_key()
AES_set_decrypt_key()
AES_encrypt()
AES_decrypt()

to probe for the Intel AES instructions at runtime (only once)
and then call the hardware implementations if so, otherwise
fall back to the software implementations.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13008

Based on original work by Justin Maggard <jmaggard@netgear.com>

Signed-off-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
lib/crypto/aes.c
lib/crypto/aes.h
lib/crypto/aesni.h [new file with mode: 0644]
lib/crypto/wscript_build

index 8e6d8418f16607e62aeb6d9a10ea4c6e7984ae27..c226ac1b3df26677e0ec3621103bcfaae737f450 100644 (file)
 #ifdef SAMBA_RIJNDAEL
 #include "rijndael-alg-fst.h"
 
+#if defined(HAVE_AESNI_INTEL)
+
+/*
+ * NB. HAVE_AESNI_INTEL is only defined if -lang-asm is
+ * available.
+ */
+
+static inline void __cpuid(unsigned int where[4], unsigned int leaf)
+{
+       asm volatile("cpuid" :
+                       "=a" (where[0]),
+                       "=b" (where[1]),
+                       "=c" (where[2]),
+                       "=d" (where[3]): "a" (leaf));
+}
+
+/*
+ * has_intel_aes_instructions()
+ * return true if supports AES-NI and false if doesn't
+ */
+static bool has_intel_aes_instructions(void)
+{
+       static int has_aes_instructions = -1;
+       unsigned int cpuid_results[4];
+
+       if (has_aes_instructions != -1) {
+               return (bool)has_aes_instructions;
+       }
+
+       __cpuid(cpuid_results, 0);
+       /*
+        *        MSB         LSB
+        *  EBX = 'u' 'n' 'e' 'G'
+        *  EDX = 'I' 'e' 'n' 'i'
+        *  ECX = 'l' 'e' 't' 'n'
+        */
+       if (memcmp((unsigned char *)&cpuid_results[1], "Genu", 4) != 0 ||
+                       memcmp((unsigned char *)&cpuid_results[3],
+                               "ineI", 4) != 0 ||
+                       memcmp((unsigned char *)&cpuid_results[2],
+                               "ntel", 4) != 0) {
+               has_aes_instructions = 0;
+               return (bool)has_aes_instructions;
+       }
+
+       __cpuid(cpuid_results, 1);
+       has_aes_instructions = !!(cpuid_results[2] & (1 << 25));
+       return (bool)has_aes_instructions;
+}
+
+/*
+ * Macro to ensure the AES key schedule starts on a 16 byte boundary.
+ */
+
+#define SET_ACC_CTX(k) \
+       do {    \
+               (k)->u.aes_ni.acc_ctx =  \
+               (struct crypto_aes_ctx *)(((unsigned long)(k)->u.aes_ni._acc_ctx + 15) & ~0xfUL); \
+       } while (0)
+
+/*
+ * The next 4 functions call the Intel AES hardware implementations
+ * of:
+ *
+ * AES_set_encrypt_key()
+ * AES_set_decrypt_key()
+ * AES_encrypt()
+ * AES_decrypt()
+ */
+
+static int AES_set_encrypt_key_aesni(const unsigned char *userkey,
+                               const int bits,
+                               AES_KEY *key)
+{
+       SET_ACC_CTX(key);
+       return aesni_set_key(key->u.aes_ni.acc_ctx, userkey, bits/8);
+}
+
+static int AES_set_decrypt_key_aesni(const unsigned char *userkey,
+                               const int bits,
+                               AES_KEY *key)
+{
+       SET_ACC_CTX(key);
+       return aesni_set_key(key->u.aes_ni.acc_ctx, userkey, bits/8);
+}
+
+static void AES_encrypt_aesni(const unsigned char *in,
+                               unsigned char *out,
+                               const AES_KEY *key)
+{
+       aesni_enc(key->u.aes_ni.acc_ctx, out, in);
+}
+
+static void AES_decrypt_aesni(const unsigned char *in,
+                               unsigned char *out,
+                               const AES_KEY *key)
+{
+       aesni_dec(key->u.aes_ni.acc_ctx, out, in);
+}
+#else /* defined(HAVE_AESNI_INTEL) */
+
+/*
+ * Dummy implementations if no Intel AES instructions present.
+ * Only has_intel_aes_instructions() will ever be called.
+*/
+
+static bool has_intel_aes_instructions(void)
+{
+       return false;
+}
+
+static int AES_set_encrypt_key_aesni(const unsigned char *userkey,
+                               const int bits,
+                               AES_KEY *key)
+{
+       return -1;
+}
+
+static int AES_set_decrypt_key_aesni(const unsigned char *userkey,
+                               const int bits,
+                               AES_KEY *key)
+{
+       return -1;
+}
+
+static void AES_encrypt_aesni(const unsigned char *in,
+                               unsigned char *out,
+                               const AES_KEY *key)
+{
+       abort();
+}
+
+static void AES_decrypt_aesni(const unsigned char *in,
+                               unsigned char *out,
+                               const AES_KEY *key)
+{
+       abort();
+}
+#endif /* defined(HAVE_AENI_INTEL) */
+
 /*
  * The next 4 functions are the pure software implementations
  * of:
@@ -88,31 +228,41 @@ AES_decrypt_rj(const unsigned char *in, unsigned char *out, const AES_KEY *key)
  *
  * If the hardware instructions don't exist, fall back to the software
  * versions.
- *
- * Currently only use the software implementations.
  */
 
 int
 AES_set_encrypt_key(const unsigned char *userkey, const int bits, AES_KEY *key)
 {
+       if (has_intel_aes_instructions()) {
+               return AES_set_encrypt_key_aesni(userkey, bits, key);
+       }
        return AES_set_encrypt_key_rj(userkey, bits, key);
 }
 
 int
 AES_set_decrypt_key(const unsigned char *userkey, const int bits, AES_KEY *key)
 {
+       if (has_intel_aes_instructions()) {
+               return AES_set_decrypt_key_aesni(userkey, bits, key);
+       }
        return AES_set_decrypt_key_rj(userkey, bits, key);
 }
 
 void
 AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)
 {
+       if (has_intel_aes_instructions()) {
+               return AES_encrypt_aesni(in, out, key);
+       }
        return AES_encrypt_rj(in, out, key);
 }
 
 void
 AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key)
 {
+       if (has_intel_aes_instructions()) {
+               return AES_decrypt_aesni(in, out, key);
+       }
        return AES_decrypt_rj(in, out, key);
 }
 
index 0ab0a3767578c7ee67e1bab77221828faf41ebbd..00bfa3e26cea8b6011458e32c00b4cd5142e543d 100644 (file)
@@ -36,6 +36,8 @@
 #ifndef LIB_CRYPTO_AES_H
 #define LIB_CRYPTO_AES_H 1
 
+#include "aesni.h"
+
 #define SAMBA_RIJNDAEL 1
 #define SAMBA_AES_CBC_ENCRYPT 1
 #define SAMBA_AES_CFB8_ENCRYPT 1
@@ -67,6 +69,7 @@ struct aes_key_rj {
 typedef struct aes_key {
        union {
                struct aes_key_rj aes_rj;
+               struct crypto_aesni_ctx aes_ni;
        } u;
 } AES_KEY;
 
diff --git a/lib/crypto/aesni.h b/lib/crypto/aesni.h
new file mode 100644 (file)
index 0000000..13d09d2
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008, Intel Corp.
+ *    Author: Huang Ying <ying.huang@intel.com>
+ *            Vinodh Gopal <vinodh.gopal@intel.com>
+ *            Kahraman Akdemir
+ *
+ * Ported x86_64 version to x86:
+ *    Author: Mathias Krause <minipli@googlemail.com>
+ *
+ * Modified for use in Samba by Justin Maggard <jmaggard@netgear.com>
+ * and Jeremy Allison <jra@samba.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef LIB_CRYPTO_AESNI_H
+#define LIB_CRYPTO_AESNI_H 1
+
+#if defined(HAVE_AESNI_INTEL)
+
+#define AES_MAX_KEYLENGTH      (15 * 16)
+#define AES_MAX_KEYLENGTH_U32  (AES_MAX_KEYLENGTH / sizeof(uint32_t))
+
+/*
+ * Please ensure that the first two fields are 16-byte aligned
+ * relative to the start of the structure, i.e., don't move them!
+ */
+struct crypto_aes_ctx {
+       uint32_t key_enc[AES_MAX_KEYLENGTH_U32];
+       uint32_t key_dec[AES_MAX_KEYLENGTH_U32];
+       uint32_t key_length;
+};
+
+struct crypto_aesni_ctx {
+       uint8_t _acc_ctx[sizeof(struct crypto_aes_ctx) + 16];
+       struct crypto_aes_ctx *acc_ctx;
+};
+
+/*
+ * These next 4 functions are actually implemented
+ * in the assembly language file:
+ * third_party/aesni-intel/aesni-intel_asm.c
+ */
+
+int aesni_set_key(struct crypto_aes_ctx *ctx,
+               const uint8_t *in_key,
+               unsigned int key_len);
+void aesni_enc(struct crypto_aes_ctx *ctx, uint8_t *dst, const uint8_t *src);
+void aesni_dec(struct crypto_aes_ctx *ctx, uint8_t *dst, const uint8_t *src);
+
+#else /* #if defined(HAVE_AESNI_INTEL) */
+
+/*
+ * We need a dummy definition of struct crypto_aesni_ctx to allow compiles.
+ */
+
+struct crypto_aesni_ctx {
+       int dummy;
+};
+
+#endif /* #if defined(HAVE_AESNI_INTEL) */
+
+#endif /* LIB_CRYPTO_AESNI_H */
index d1f152ebcf1b9e825c56d26421088ea495c1bab9..f3257d8d1ed1e9916242bd13adda7bf52dd247d9 100644 (file)
@@ -11,6 +11,9 @@ elif bld.CONFIG_SET('HAVE_SYS_MD5_H') and bld.CONFIG_SET('HAVE_LIBMD'):
 elif not bld.CONFIG_SET('HAVE_SYS_MD5_H') and not bld.CONFIG_SET('HAVE_COMMONCRYPTO_COMMONDIGEST_H'):
        extra_source += ' md5.c'
 
+if bld.CONFIG_SET("HAVE_AESNI_INTEL"):
+        extra_deps += ' aesni-intel'
+
 bld.SAMBA_SUBSYSTEM('LIBCRYPTO',
         source='''crc32.c hmacmd5.c md4.c arcfour.c sha256.c sha512.c hmacsha256.c
         aes.c rijndael-alg-fst.c aes_cmac_128.c aes_ccm_128.c aes_gcm_128.c