aarch64 support.

This is an initial cut at aarch64 support. I have only qemu to test it
however—hopefully hardware will be coming soon.

This also affects 32-bit ARM in that aarch64 chips can run 32-bit code
and we would like to be able to take advantage of the crypto operations
even in 32-bit mode. AES and GHASH should Just Work in this case: the
-armx.pl files can be built for either 32- or 64-bit mode based on the
flavour argument given to the Perl script.

SHA-1 and SHA-256 don't work like this however because they've never
support for multiple implementations, thus BoringSSL built for 32-bit
won't use the SHA instructions on an aarch64 chip.

No dedicated ChaCha20 or Poly1305 support yet.

Change-Id: Ib275bc4894a365c8ec7c42f4e91af6dba3bd686c
Reviewed-on: https://boringssl-review.googlesource.com/2801
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/cipher/e_aes.c b/crypto/cipher/e_aes.c
index 03cef73..0b8b4b9 100644
--- a/crypto/cipher/e_aes.c
+++ b/crypto/cipher/e_aes.c
@@ -103,14 +103,33 @@
 }
 #endif
 
-#elif !defined(OPENSSL_NO_ASM) && defined(OPENSSL_ARM)
+#elif !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64))
 #include "../arm_arch.h"
-#if __ARM_ARCH__ >= 7
+
+#if defined(OPENSSL_ARM)
 #define BSAES
 static char bsaes_capable(void) {
   return CRYPTO_is_NEON_capable();
 }
-#endif  /* __ARM_ARCH__ >= 7 */
+#endif
+
+#define HWAES
+static char hwaes_capable(void) {
+  return (OPENSSL_armcap_P & ARMV8_AES) != 0;
+}
+
+int aes_v8_set_encrypt_key(const uint8_t *user_key, const int bits,
+                           AES_KEY *key);
+int aes_v8_set_decrypt_key(const uint8_t *user_key, const int bits,
+                           AES_KEY *key);
+void aes_v8_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+void aes_v8_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key);
+void aes_v8_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                        const AES_KEY *key, uint8_t *ivec, const int enc);
+void aes_v8_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t len,
+                                 const AES_KEY *key, const uint8_t ivec[16]);
+
 #endif  /* OPENSSL_ARM */
 
 #if defined(BSAES)
@@ -174,6 +193,41 @@
 }
 #endif
 
+#if !defined(HWAES)
+/* If HWAES isn't defined then we provide dummy functions for each of the hwaes
+ * functions. */
+int hwaes_capable() {
+  return 0;
+}
+
+int aes_v8_set_encrypt_key(const uint8_t *user_key, int bits,
+                           AES_KEY *key) {
+  abort();
+}
+
+int aes_v8_set_decrypt_key(const uint8_t *user_key, int bits, AES_KEY *key) {
+  abort();
+}
+
+void aes_v8_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  abort();
+}
+
+void aes_v8_decrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key) {
+  abort();
+}
+
+void aes_v8_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
+                        const AES_KEY *key, uint8_t *ivec, int enc) {
+  abort();
+}
+
+void aes_v8_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t len,
+                                 const AES_KEY *key, const uint8_t ivec[16]) {
+  abort();
+}
+#endif
+
 #if !defined(OPENSSL_NO_ASM) && \
     (defined(OPENSSL_X86_64) || defined(OPENSSL_X86))
 int aesni_set_encrypt_key(const uint8_t *userKey, int bits, AES_KEY *key);
@@ -227,7 +281,14 @@
 
   mode = ctx->cipher->flags & EVP_CIPH_MODE_MASK;
   if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
-    if (bsaes_capable() && mode == EVP_CIPH_CBC_MODE) {
+    if (hwaes_capable()) {
+      ret = aes_v8_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
+      dat->block = (block128_f)aes_v8_decrypt;
+      dat->stream.cbc = NULL;
+      if (mode == EVP_CIPH_CBC_MODE) {
+        dat->stream.cbc = (cbc128_f)aes_v8_cbc_encrypt;
+      }
+    } else if (bsaes_capable() && mode == EVP_CIPH_CBC_MODE) {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
       dat->block = (block128_f)AES_decrypt;
       dat->stream.cbc = (cbc128_f)bsaes_cbc_encrypt;
@@ -242,6 +303,15 @@
       dat->stream.cbc =
           mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
     }
+  } else if (hwaes_capable()) {
+    ret = aes_v8_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
+    dat->block = (block128_f)aes_v8_encrypt;
+    dat->stream.cbc = NULL;
+    if (mode == EVP_CIPH_CBC_MODE) {
+      dat->stream.cbc = (cbc128_f)aes_v8_cbc_encrypt;
+    } else if (mode == EVP_CIPH_CTR_MODE) {
+      dat->stream.ctr = (ctr128_f)aes_v8_ctr32_encrypt_blocks;
+    }
   } else if (bsaes_capable() && mode == EVP_CIPH_CTR_MODE) {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
     dat->block = (block128_f)AES_encrypt;
@@ -316,6 +386,12 @@
 
 static ctr128_f aes_gcm_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx,
                                 const uint8_t *key, size_t key_len) {
+  if (hwaes_capable()) {
+    aes_v8_set_encrypt_key(key, key_len * 8, aes_key);
+    CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)aes_v8_encrypt);
+    return (ctr128_f)aes_v8_ctr32_encrypt_blocks;
+  }
+
   if (bsaes_capable()) {
     AES_set_encrypt_key(key, key_len * 8, aes_key);
     CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)AES_encrypt);
@@ -1274,6 +1350,8 @@
 int EVP_has_aes_hardware(void) {
 #if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
   return aesni_capable() && crypto_gcm_clmul_enabled();
+#elif defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)
+  return hwaes_capable() && (OPENSSL_armcap_P & ARMV8_PMULL);
 #else
   return 0;
 #endif