Engage GHASH for ARMv8.
[openssl.git] / crypto / modes / gcm128.c
index c2a2d5e6d9041a632b6a4db05aca62c87ed0aff4..19cbcf5572cb9494062a5bed49d3e3ec0905a347 100644 (file)
@@ -148,6 +148,7 @@ static void gcm_gmult_8bit(u64 Xi[2], const u128 Htable[256])
        const u8 *xi = (const u8 *)Xi+15;
        size_t rem, n = *xi;
        const union { long one; char little; } is_endian = {1};
+       __fips_constseg
        static const size_t rem_8bit[256] = {
                PACK(0x0000), PACK(0x01C2), PACK(0x0384), PACK(0x0246),
                PACK(0x0708), PACK(0x06CA), PACK(0x048C), PACK(0x054E),
@@ -326,6 +327,7 @@ static void gcm_init_4bit(u128 Htable[16], u64 H[2])
 }
 
 #ifndef GHASH_ASM
+__fips_constseg
 static const size_t rem_4bit[16] = {
        PACK(0x0000), PACK(0x1C20), PACK(0x3840), PACK(0x2460),
        PACK(0x7080), PACK(0x6CA0), PACK(0x48C0), PACK(0x54E0),
@@ -461,6 +463,7 @@ static void gcm_ghash_4bit(u64 Xi[2],const u128 Htable[16],
      */ 
     u128 Hshr4[16];    /* Htable shifted right by 4 bits */
     u8   Hshl4[16];    /* Htable shifted left  by 4 bits */
+    __fips_constseg
     static const unsigned short rem_8bit[256] = {
        0x0000, 0x01C2, 0x0384, 0x0246, 0x0708, 0x06CA, 0x048C, 0x054E,
        0x0E10, 0x0FD2, 0x0D94, 0x0C56, 0x0918, 0x08DA, 0x0A9C, 0x0B5E,
@@ -642,27 +645,71 @@ static void gcm_gmult_1bit(u64 Xi[2],const u64 H[2])
 
 #endif
 
-#if    TABLE_BITS==4 && defined(GHASH_ASM) && !defined(I386_ONLY) && \
+#if    TABLE_BITS==4 && (defined(GHASH_ASM) || defined(OPENSSL_CPUID_OBJ))
+# if   !defined(I386_ONLY) && \
        (defined(__i386)        || defined(__i386__)    || \
         defined(__x86_64)      || defined(__x86_64__)  || \
         defined(_M_IX86)       || defined(_M_AMD64)    || defined(_M_X64))
-# define GHASH_ASM_X86_OR_64
+#  define GHASH_ASM_X86_OR_64
+#  define GCM_FUNCREF_4BIT
 extern unsigned int OPENSSL_ia32cap_P[2];
 
 void gcm_init_clmul(u128 Htable[16],const u64 Xi[2]);
 void gcm_gmult_clmul(u64 Xi[2],const u128 Htable[16]);
 void gcm_ghash_clmul(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
 
-# if   defined(__i386) || defined(__i386__) || defined(_M_IX86)
-#  define GHASH_ASM_X86
+#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
+# define gcm_init_avx  gcm_init_clmul
+# define gcm_gmult_avx gcm_gmult_clmul
+# define gcm_ghash_avx gcm_ghash_clmul
+#else
+void gcm_init_avx(u128 Htable[16],const u64 Xi[2]);
+void gcm_gmult_avx(u64 Xi[2],const u128 Htable[16]);
+void gcm_ghash_avx(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
+#endif
+
+#  if  defined(__i386) || defined(__i386__) || defined(_M_IX86)
+#   define GHASH_ASM_X86
 void gcm_gmult_4bit_mmx(u64 Xi[2],const u128 Htable[16]);
 void gcm_ghash_4bit_mmx(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
 
 void gcm_gmult_4bit_x86(u64 Xi[2],const u128 Htable[16]);
 void gcm_ghash_4bit_x86(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
+#  endif
+# elif defined(__arm__) || defined(__arm) || defined(__aarch64__)
+#  include "arm_arch.h"
+#  if __ARM_ARCH__>=7
+#   define GHASH_ASM_ARM
+#   define GCM_FUNCREF_4BIT
+#   define PMULL_CAPABLE       (OPENSSL_armcap_P & ARMV8_PMULL)
+#   if defined(__arm__) || defined(__arm)
+#    define NEON_CAPABLE       (OPENSSL_armcap_P & ARMV7_NEON)
+#   endif
+void gcm_init_neon(u128 Htable[16],const u64 Xi[2]);
+void gcm_gmult_neon(u64 Xi[2],const u128 Htable[16]);
+void gcm_ghash_neon(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
+void gcm_init_v8(u128 Htable[16],const u64 Xi[2]);
+void gcm_gmult_v8(u64 Xi[2],const u128 Htable[16]);
+void gcm_ghash_v8(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
+#  endif
+# elif defined(__sparc__) || defined(__sparc)
+#  include "sparc_arch.h"
+#  define GHASH_ASM_SPARC
+#  define GCM_FUNCREF_4BIT
+extern unsigned int OPENSSL_sparcv9cap_P[];
+void gcm_init_vis3(u128 Htable[16],const u64 Xi[2]);
+void gcm_gmult_vis3(u64 Xi[2],const u128 Htable[16]);
+void gcm_ghash_vis3(u64 Xi[2],const u128 Htable[16],const u8 *inp,size_t len);
 # endif
+#endif
 
-# define GCM_FUNCREF_4BIT
+#ifdef GCM_FUNCREF_4BIT
+# undef  GCM_MUL
+# define GCM_MUL(ctx,Xi)       (*gcm_gmult_p)(ctx->Xi.u,ctx->Htable)
+# ifdef GHASH
+#  undef  GHASH
+#  define GHASH(ctx,in,len)    (*gcm_ghash_p)(ctx->Xi.u,ctx->Htable,in,len)
+# endif
 #endif
 
 void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx,void *key,block128_f block)
@@ -695,16 +742,27 @@ void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx,void *key,block128_f block)
 #elif  TABLE_BITS==4
 # if   defined(GHASH_ASM_X86_OR_64)
 #  if  !defined(GHASH_ASM_X86) || defined(OPENSSL_IA32_SSE2)
-       if (OPENSSL_ia32cap_P[1]&(1<<1)) {
-               gcm_init_clmul(ctx->Htable,ctx->H.u);
-               ctx->gmult = gcm_gmult_clmul;
-               ctx->ghash = gcm_ghash_clmul;
+       if (OPENSSL_ia32cap_P[0]&(1<<24) &&     /* check FXSR bit */
+           OPENSSL_ia32cap_P[1]&(1<<1) ) {     /* check PCLMULQDQ bit */
+               if (((OPENSSL_ia32cap_P[1]>>22)&0x41)==0x41) {  /* AVX+MOVBE */
+                       gcm_init_avx(ctx->Htable,ctx->H.u);
+                       ctx->gmult = gcm_gmult_avx;
+                       ctx->ghash = gcm_ghash_avx;
+               } else {
+                       gcm_init_clmul(ctx->Htable,ctx->H.u);
+                       ctx->gmult = gcm_gmult_clmul;
+                       ctx->ghash = gcm_ghash_clmul;
+               }
                return;
        }
 #  endif
        gcm_init_4bit(ctx->Htable,ctx->H.u);
 #  if  defined(GHASH_ASM_X86)                  /* x86 only */
-       if (OPENSSL_ia32cap_P[0]&(1<<23)) {
+#   if defined(OPENSSL_IA32_SSE2)
+       if (OPENSSL_ia32cap_P[0]&(1<<25)) {     /* check SSE bit */
+#   else
+       if (OPENSSL_ia32cap_P[0]&(1<<23)) {     /* check MMX bit */
+#   endif
                ctx->gmult = gcm_gmult_4bit_mmx;
                ctx->ghash = gcm_ghash_4bit_mmx;
        } else {
@@ -715,6 +773,36 @@ void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx,void *key,block128_f block)
        ctx->gmult = gcm_gmult_4bit;
        ctx->ghash = gcm_ghash_4bit;
 #  endif
+# elif defined(GHASH_ASM_ARM)
+#  ifdef PMULL_CAPABLE
+       if (PMULL_CAPABLE) {
+               gcm_init_v8(ctx->Htable,ctx->H.u);
+               ctx->gmult = gcm_gmult_v8;
+               ctx->ghash = gcm_ghash_v8;
+       } else
+#  endif
+#  ifdef NEON_CAPABLE
+       if (NEON_CAPABLE) {
+               gcm_init_neon(ctx->Htable,ctx->H.u);
+               ctx->gmult = gcm_gmult_neon;
+               ctx->ghash = gcm_ghash_neon;
+       } else
+#  endif
+       {
+               gcm_init_4bit(ctx->Htable,ctx->H.u);
+               ctx->gmult = gcm_gmult_4bit;
+               ctx->ghash = gcm_ghash_4bit;
+       }
+# elif defined(GHASH_ASM_SPARC)
+       if (OPENSSL_sparcv9cap_P[0] & SPARCV9_VIS3) {
+               gcm_init_vis3(ctx->Htable,ctx->H.u);
+               ctx->gmult = gcm_gmult_vis3;
+               ctx->ghash = gcm_ghash_vis3;
+       } else {
+               gcm_init_4bit(ctx->Htable,ctx->H.u);
+               ctx->gmult = gcm_gmult_4bit;
+               ctx->ghash = gcm_ghash_4bit;
+       }
 # else
        gcm_init_4bit(ctx->Htable,ctx->H.u);
 # endif
@@ -726,7 +814,7 @@ void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx,const unsigned char *iv,size_t len)
        const union { long one; char little; } is_endian = {1};
        unsigned int ctr;
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 #endif
 
        ctx->Yi.u[0]  = 0;
@@ -778,7 +866,11 @@ void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx,const unsigned char *iv,size_t len)
                GCM_MUL(ctx,Yi);
 
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctr = BSWAP4(ctx->Yi.d[3]);
+#else
                        ctr = GETU32(ctx->Yi.c+12);
+#endif
                else
                        ctr = ctx->Yi.d[3];
        }
@@ -786,7 +878,11 @@ void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx,const unsigned char *iv,size_t len)
        (*ctx->block)(ctx->Yi.c,ctx->EK0.c,ctx->key);
        ++ctr;
        if (is_endian.little)
+#ifdef BSWAP4
+               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                PUTU32(ctx->Yi.c+12,ctr);
+#endif
        else
                ctx->Yi.d[3] = ctr;
 }
@@ -797,10 +893,10 @@ int CRYPTO_gcm128_aad(GCM128_CONTEXT *ctx,const unsigned char *aad,size_t len)
        unsigned int n;
        u64 alen = ctx->len.u[0];
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 # ifdef GHASH
-       void (*gcm_ghash_4bit)(u64 Xi[2],const u128 Htable[16],
-                               const u8 *inp,size_t len) = ctx->ghash;
+       void (*gcm_ghash_p)(u64 Xi[2],const u128 Htable[16],
+                               const u8 *inp,size_t len)       = ctx->ghash;
 # endif
 #endif
 
@@ -855,12 +951,14 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
        const union { long one; char little; } is_endian = {1};
        unsigned int n, ctr;
        size_t i;
-       u64 mlen = ctx->len.u[1];
+       u64        mlen  = ctx->len.u[1];
+       block128_f block = ctx->block;
+       void      *key   = ctx->key;
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 # ifdef GHASH
-       void (*gcm_ghash_4bit)(u64 Xi[2],const u128 Htable[16],
-                               const u8 *inp,size_t len) = ctx->ghash;
+       void (*gcm_ghash_p)(u64 Xi[2],const u128 Htable[16],
+                               const u8 *inp,size_t len)       = ctx->ghash;
 # endif
 #endif
 
@@ -879,7 +977,11 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
        }
 
        if (is_endian.little)
+#ifdef BSWAP4
+               ctr = BSWAP4(ctx->Yi.d[3]);
+#else
                ctr = GETU32(ctx->Yi.c+12);
+#endif
        else
                ctr = ctx->Yi.d[3];
 
@@ -907,15 +1009,21 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
                    size_t j=GHASH_CHUNK;
 
                    while (j) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       size_t *out_t=(size_t *)out;
+                       const size_t *in_t=(const size_t *)in;
+
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
-                       for (i=0; i<16; i+=sizeof(size_t))
-                               *(size_t *)(out+i) =
-                               *(size_t *)(in+i)^*(size_t *)(ctx->EKi.c+i);
+                       for (i=0; i<16/sizeof(size_t); ++i)
+                               out_t[i] = in_t[i] ^ ctx->EKi.t[i];
                        out += 16;
                        in  += 16;
                        j   -= 16;
@@ -927,15 +1035,21 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
                    size_t j=i;
 
                    while (len>=16) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       size_t *out_t=(size_t *)out;
+                       const size_t *in_t=(const size_t *)in;
+
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
-                       for (i=0; i<16; i+=sizeof(size_t))
-                               *(size_t *)(out+i) =
-                               *(size_t *)(in+i)^*(size_t *)(ctx->EKi.c+i);
+                       for (i=0; i<16/sizeof(size_t); ++i)
+                               out_t[i] = in_t[i] ^ ctx->EKi.t[i];
                        out += 16;
                        in  += 16;
                        len -= 16;
@@ -944,16 +1058,22 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
                }
 #else
                while (len>=16) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       size_t *out_t=(size_t *)out;
+                       const size_t *in_t=(const size_t *)in;
+
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
-                       for (i=0; i<16; i+=sizeof(size_t))
-                               *(size_t *)(ctx->Xi.c+i) ^=
-                               *(size_t *)(out+i) =
-                               *(size_t *)(in+i)^*(size_t *)(ctx->EKi.c+i);
+                       for (i=0; i<16/sizeof(size_t); ++i)
+                               ctx->Xi.t[i] ^=
+                               out_t[i] = in_t[i]^ctx->EKi.t[i];
                        GCM_MUL(ctx,Xi);
                        out += 16;
                        in  += 16;
@@ -961,10 +1081,14 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
                }
 #endif
                if (len) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
                        while (len--) {
@@ -979,10 +1103,14 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
 #endif
        for (i=0;i<len;++i) {
                if (n==0) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
                }
@@ -1003,12 +1131,14 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
        const union { long one; char little; } is_endian = {1};
        unsigned int n, ctr;
        size_t i;
-       u64 mlen = ctx->len.u[1];
+       u64        mlen  = ctx->len.u[1];
+       block128_f block = ctx->block;
+       void      *key   = ctx->key;
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 # ifdef GHASH
-       void (*gcm_ghash_4bit)(u64 Xi[2],const u128 Htable[16],
-                               const u8 *inp,size_t len) = ctx->ghash;
+       void (*gcm_ghash_p)(u64 Xi[2],const u128 Htable[16],
+                               const u8 *inp,size_t len)       = ctx->ghash;
 # endif
 #endif
 
@@ -1024,7 +1154,11 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
        }
 
        if (is_endian.little)
+#ifdef BSWAP4
+               ctr = BSWAP4(ctx->Yi.d[3]);
+#else
                ctr = GETU32(ctx->Yi.c+12);
+#endif
        else
                ctr = ctx->Yi.d[3];
 
@@ -1055,15 +1189,21 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
 
                    GHASH(ctx,in,GHASH_CHUNK);
                    while (j) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       size_t *out_t=(size_t *)out;
+                       const size_t *in_t=(const size_t *)in;
+
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
-                       for (i=0; i<16; i+=sizeof(size_t))
-                               *(size_t *)(out+i) =
-                               *(size_t *)(in+i)^*(size_t *)(ctx->EKi.c+i);
+                       for (i=0; i<16/sizeof(size_t); ++i)
+                               out_t[i] = in_t[i]^ctx->EKi.t[i];
                        out += 16;
                        in  += 16;
                        j   -= 16;
@@ -1073,15 +1213,21 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
                if ((i = (len&(size_t)-16))) {
                    GHASH(ctx,in,i);
                    while (len>=16) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       size_t *out_t=(size_t *)out;
+                       const size_t *in_t=(const size_t *)in;
+
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
-                       for (i=0; i<16; i+=sizeof(size_t))
-                               *(size_t *)(out+i) =
-                               *(size_t *)(in+i)^*(size_t *)(ctx->EKi.c+i);
+                       for (i=0; i<16/sizeof(size_t); ++i)
+                               out_t[i] = in_t[i]^ctx->EKi.t[i];
                        out += 16;
                        in  += 16;
                        len -= 16;
@@ -1089,16 +1235,23 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
                }
 #else
                while (len>=16) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       size_t *out_t=(size_t *)out;
+                       const size_t *in_t=(const size_t *)in;
+
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
-                       for (i=0; i<16; i+=sizeof(size_t)) {
-                               size_t c = *(size_t *)(in+i);
-                               *(size_t *)(out+i) = c^*(size_t *)(ctx->EKi.c+i);
-                               *(size_t *)(ctx->Xi.c+i) ^= c;
+                       for (i=0; i<16/sizeof(size_t); ++i) {
+                               size_t c = in[i];
+                               out[i] = c^ctx->EKi.t[i];
+                               ctx->Xi.t[i] ^= c;
                        }
                        GCM_MUL(ctx,Xi);
                        out += 16;
@@ -1107,10 +1260,14 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
                }
 #endif
                if (len) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
                        while (len--) {
@@ -1128,10 +1285,14 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
        for (i=0;i<len;++i) {
                u8 c;
                if (n==0) {
-                       (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+                       (*block)(ctx->Yi.c,ctx->EKi.c,key);
                        ++ctr;
                        if (is_endian.little)
+#ifdef BSWAP4
+                               ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                                PUTU32(ctx->Yi.c+12,ctr);
+#endif
                        else
                                ctx->Yi.d[3] = ctr;
                }
@@ -1154,12 +1315,13 @@ int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
        const union { long one; char little; } is_endian = {1};
        unsigned int n, ctr;
        size_t i;
-       u64 mlen = ctx->len.u[1];
+       u64   mlen = ctx->len.u[1];
+       void *key  = ctx->key;
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 # ifdef GHASH
-       void (*gcm_ghash_4bit)(u64 Xi[2],const u128 Htable[16],
-                               const u8 *inp,size_t len) = ctx->ghash;
+       void (*gcm_ghash_p)(u64 Xi[2],const u128 Htable[16],
+                               const u8 *inp,size_t len)       = ctx->ghash;
 # endif
 #endif
 
@@ -1175,7 +1337,11 @@ int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
        }
 
        if (is_endian.little)
+#ifdef BSWAP4
+               ctr = BSWAP4(ctx->Yi.d[3]);
+#else
                ctr = GETU32(ctx->Yi.c+12);
+#endif
        else
                ctr = ctx->Yi.d[3];
 
@@ -1194,10 +1360,14 @@ int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
        }
 #if defined(GHASH) && !defined(OPENSSL_SMALL_FOOTPRINT)
        while (len>=GHASH_CHUNK) {
-               (*stream)(in,out,GHASH_CHUNK/16,ctx->key,ctx->Yi.c);
+               (*stream)(in,out,GHASH_CHUNK/16,key,ctx->Yi.c);
                ctr += GHASH_CHUNK/16;
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                        PUTU32(ctx->Yi.c+12,ctr);
+#endif
                else
                        ctx->Yi.d[3] = ctr;
                GHASH(ctx,out,GHASH_CHUNK);
@@ -1209,10 +1379,14 @@ int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
        if ((i = (len&(size_t)-16))) {
                size_t j=i/16;
 
-               (*stream)(in,out,j,ctx->key,ctx->Yi.c);
+               (*stream)(in,out,j,key,ctx->Yi.c);
                ctr += (unsigned int)j;
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                        PUTU32(ctx->Yi.c+12,ctr);
+#endif
                else
                        ctx->Yi.d[3] = ctr;
                in  += i;
@@ -1229,10 +1403,14 @@ int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
 #endif
        }
        if (len) {
-               (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+               (*ctx->block)(ctx->Yi.c,ctx->EKi.c,key);
                ++ctr;
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                        PUTU32(ctx->Yi.c+12,ctr);
+#endif
                else
                        ctx->Yi.d[3] = ctr;
                while (len--) {
@@ -1252,12 +1430,13 @@ int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
        const union { long one; char little; } is_endian = {1};
        unsigned int n, ctr;
        size_t i;
-       u64 mlen = ctx->len.u[1];
+       u64   mlen = ctx->len.u[1];
+       void *key  = ctx->key;
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 # ifdef GHASH
-       void (*gcm_ghash_4bit)(u64 Xi[2],const u128 Htable[16],
-                               const u8 *inp,size_t len) = ctx->ghash;
+       void (*gcm_ghash_p)(u64 Xi[2],const u128 Htable[16],
+                               const u8 *inp,size_t len)       = ctx->ghash;
 # endif
 #endif
 
@@ -1273,7 +1452,11 @@ int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
        }
 
        if (is_endian.little)
+#ifdef BSWAP4
+               ctr = BSWAP4(ctx->Yi.d[3]);
+#else
                ctr = GETU32(ctx->Yi.c+12);
+#endif
        else
                ctr = ctx->Yi.d[3];
 
@@ -1295,10 +1478,14 @@ int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
 #if defined(GHASH) && !defined(OPENSSL_SMALL_FOOTPRINT)
        while (len>=GHASH_CHUNK) {
                GHASH(ctx,in,GHASH_CHUNK);
-               (*stream)(in,out,GHASH_CHUNK/16,ctx->key,ctx->Yi.c);
+               (*stream)(in,out,GHASH_CHUNK/16,key,ctx->Yi.c);
                ctr += GHASH_CHUNK/16;
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                        PUTU32(ctx->Yi.c+12,ctr);
+#endif
                else
                        ctx->Yi.d[3] = ctr;
                out += GHASH_CHUNK;
@@ -1321,10 +1508,14 @@ int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
                j   = i/16;
                in -= i;
 #endif
-               (*stream)(in,out,j,ctx->key,ctx->Yi.c);
+               (*stream)(in,out,j,key,ctx->Yi.c);
                ctr += (unsigned int)j;
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                        PUTU32(ctx->Yi.c+12,ctr);
+#endif
                else
                        ctx->Yi.d[3] = ctr;
                out += i;
@@ -1332,10 +1523,14 @@ int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
                len -= i;
        }
        if (len) {
-               (*ctx->block)(ctx->Yi.c,ctx->EKi.c,ctx->key);
+               (*ctx->block)(ctx->Yi.c,ctx->EKi.c,key);
                ++ctr;
                if (is_endian.little)
+#ifdef BSWAP4
+                       ctx->Yi.d[3] = BSWAP4(ctr);
+#else
                        PUTU32(ctx->Yi.c+12,ctr);
+#endif
                else
                        ctx->Yi.d[3] = ctr;
                while (len--) {
@@ -1357,10 +1552,10 @@ int CRYPTO_gcm128_finish(GCM128_CONTEXT *ctx,const unsigned char *tag,
        u64 alen = ctx->len.u[0]<<3;
        u64 clen = ctx->len.u[1]<<3;
 #ifdef GCM_FUNCREF_4BIT
-       void (*gcm_gmult_4bit)(u64 Xi[2],const u128 Htable[16]) = ctx->gmult;
+       void (*gcm_gmult_p)(u64 Xi[2],const u128 Htable[16])    = ctx->gmult;
 #endif
 
-       if (ctx->mres)
+       if (ctx->mres || ctx->ares)
                GCM_MUL(ctx,Xi);
 
        if (is_endian.little) {
@@ -1631,6 +1826,46 @@ static const u8  IV18[]={0x93,0x13,0x22,0x5d,0xf8,0x84,0x06,0xe5,0x55,0x90,0x9c,0
                        0xa2,0x41,0x89,0x97,0x20,0x0e,0xf8,0x2e,0x44,0xae,0x7e,0x3f},
                T18[]= {0xa4,0x4a,0x82,0x66,0xee,0x1c,0x8e,0xb0,0xc8,0xb5,0xd4,0xcf,0x5a,0xe9,0xf1,0x9a};
 
+/* Test Case 19 */
+#define K19 K1
+#define P19 P1
+#define IV19 IV1
+#define C19 C1
+static const u8 A19[]= {0xd9,0x31,0x32,0x25,0xf8,0x84,0x06,0xe5,0xa5,0x59,0x09,0xc5,0xaf,0xf5,0x26,0x9a,
+                       0x86,0xa7,0xa9,0x53,0x15,0x34,0xf7,0xda,0x2e,0x4c,0x30,0x3d,0x8a,0x31,0x8a,0x72,
+                       0x1c,0x3c,0x0c,0x95,0x95,0x68,0x09,0x53,0x2f,0xcf,0x0e,0x24,0x49,0xa6,0xb5,0x25,
+                       0xb1,0x6a,0xed,0xf5,0xaa,0x0d,0xe6,0x57,0xba,0x63,0x7b,0x39,0x1a,0xaf,0xd2,0x55,
+                       0x52,0x2d,0xc1,0xf0,0x99,0x56,0x7d,0x07,0xf4,0x7f,0x37,0xa3,0x2a,0x84,0x42,0x7d,
+                       0x64,0x3a,0x8c,0xdc,0xbf,0xe5,0xc0,0xc9,0x75,0x98,0xa2,0xbd,0x25,0x55,0xd1,0xaa,
+                       0x8c,0xb0,0x8e,0x48,0x59,0x0d,0xbb,0x3d,0xa7,0xb0,0x8b,0x10,0x56,0x82,0x88,0x38,
+                       0xc5,0xf6,0x1e,0x63,0x93,0xba,0x7a,0x0a,0xbc,0xc9,0xf6,0x62,0x89,0x80,0x15,0xad},
+               T19[]= {0x5f,0xea,0x79,0x3a,0x2d,0x6f,0x97,0x4d,0x37,0xe6,0x8e,0x0c,0xb8,0xff,0x94,0x92};
+
+/* Test Case 20 */
+#define K20 K1
+#define A20 A1
+static const u8 IV20[64]={0xff,0xff,0xff,0xff},        /* this results in 0xff in counter LSB */
+               P20[288],
+               C20[]= {0x56,0xb3,0x37,0x3c,0xa9,0xef,0x6e,0x4a,0x2b,0x64,0xfe,0x1e,0x9a,0x17,0xb6,0x14,
+                       0x25,0xf1,0x0d,0x47,0xa7,0x5a,0x5f,0xce,0x13,0xef,0xc6,0xbc,0x78,0x4a,0xf2,0x4f,
+                       0x41,0x41,0xbd,0xd4,0x8c,0xf7,0xc7,0x70,0x88,0x7a,0xfd,0x57,0x3c,0xca,0x54,0x18,
+                       0xa9,0xae,0xff,0xcd,0x7c,0x5c,0xed,0xdf,0xc6,0xa7,0x83,0x97,0xb9,0xa8,0x5b,0x49,
+                       0x9d,0xa5,0x58,0x25,0x72,0x67,0xca,0xab,0x2a,0xd0,0xb2,0x3c,0xa4,0x76,0xa5,0x3c,
+                       0xb1,0x7f,0xb4,0x1c,0x4b,0x8b,0x47,0x5c,0xb4,0xf3,0xf7,0x16,0x50,0x94,0xc2,0x29,
+                       0xc9,0xe8,0xc4,0xdc,0x0a,0x2a,0x5f,0xf1,0x90,0x3e,0x50,0x15,0x11,0x22,0x13,0x76,
+                       0xa1,0xcd,0xb8,0x36,0x4c,0x50,0x61,0xa2,0x0c,0xae,0x74,0xbc,0x4a,0xcd,0x76,0xce,
+                       0xb0,0xab,0xc9,0xfd,0x32,0x17,0xef,0x9f,0x8c,0x90,0xbe,0x40,0x2d,0xdf,0x6d,0x86,
+                       0x97,0xf4,0xf8,0x80,0xdf,0xf1,0x5b,0xfb,0x7a,0x6b,0x28,0x24,0x1e,0xc8,0xfe,0x18,
+                       0x3c,0x2d,0x59,0xe3,0xf9,0xdf,0xff,0x65,0x3c,0x71,0x26,0xf0,0xac,0xb9,0xe6,0x42,
+                       0x11,0xf4,0x2b,0xae,0x12,0xaf,0x46,0x2b,0x10,0x70,0xbe,0xf1,0xab,0x5e,0x36,0x06,
+                       0x87,0x2c,0xa1,0x0d,0xee,0x15,0xb3,0x24,0x9b,0x1a,0x1b,0x95,0x8f,0x23,0x13,0x4c,
+                       0x4b,0xcc,0xb7,0xd0,0x32,0x00,0xbc,0xe4,0x20,0xa2,0xf8,0xeb,0x66,0xdc,0xf3,0x64,
+                       0x4d,0x14,0x23,0xc1,0xb5,0x69,0x90,0x03,0xc1,0x3e,0xce,0xf4,0xbf,0x38,0xa3,0xb6,
+                       0x0e,0xed,0xc3,0x40,0x33,0xba,0xc1,0x90,0x27,0x83,0xdc,0x6d,0x89,0xe2,0xe7,0x74,
+                       0x18,0x8a,0x43,0x9c,0x7e,0xbc,0xc0,0x67,0x2d,0xbd,0xa4,0xdd,0xcf,0xb2,0x79,0x46,
+                       0x13,0xb0,0xbe,0x41,0x31,0x5e,0xf7,0x78,0x70,0x8a,0x70,0xee,0x7d,0x75,0x16,0x5c},
+               T20[]= {0x8b,0x30,0x7f,0x6b,0x33,0x28,0x6d,0x0a,0xb0,0x26,0xa9,0xed,0x3f,0xe1,0xe8,0x5f};
+
 #define TEST_CASE(n)   do {                                    \
        u8 out[sizeof(P##n)];                                   \
        AES_set_encrypt_key(K##n,sizeof(K##n)*8,&key);          \
@@ -1675,6 +1910,8 @@ int main()
        TEST_CASE(16);
        TEST_CASE(17);
        TEST_CASE(18);
+       TEST_CASE(19);
+       TEST_CASE(20);
 
 #ifdef OPENSSL_CPUID_OBJ
        {
@@ -1705,11 +1942,16 @@ int main()
                        ctr_t/(double)sizeof(buf),
                        (gcm_t-ctr_t)/(double)sizeof(buf));
 #ifdef GHASH
-       GHASH(&ctx,buf.c,sizeof(buf));
+       {
+       void (*gcm_ghash_p)(u64 Xi[2],const u128 Htable[16],
+                               const u8 *inp,size_t len)       = ctx.ghash;
+
+       GHASH((&ctx),buf.c,sizeof(buf));
        start = OPENSSL_rdtsc();
-       for (i=0;i<100;++i) GHASH(&ctx,buf.c,sizeof(buf));
+       for (i=0;i<100;++i) GHASH((&ctx),buf.c,sizeof(buf));
        gcm_t = OPENSSL_rdtsc() - start;
        printf("%.2f\n",gcm_t/(double)sizeof(buf)/(double)i);
+       }
 #endif
        }
 #endif