/*---------------------------------------------------------------------------*/
/* cipher.cpp                                                                */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

#include <platform.h>
#include "../ilib/cipher.h"


/*---------------------------------------------------------------------------*/
/* Encryptions functions, copied from OpenSSL source code.                   */
/*---------------------------------------------------------------------------*/


namespace _openssl {

#if !defined(_MSC_VER) && defined(__x86_64)
// compiler optimization flag tree-slp-vectorize causes traps on x86_64 with GCC 13.2
// in function aes_encrypt_key_256
// so we disable the optimization for now
#pragma GCC push_options
#pragma GCC optimize ("O3", "no-tree-slp-vectorize")
#endif

#define register

    /*
     * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
     *
     * Licensed under the OpenSSL license (the "License").  You may not use
     * this file except in compliance with the License.  You can obtain a copy
     * in the file LICENSE in the source distribution or at
     * https://www.openssl.org/source/license.html
     */

    /* openssl-3.3.2v\include\openssl\des.h */
# ifdef  __cplusplus
    extern "C" {
# endif

    typedef unsigned int DES_LONG;

# ifdef OPENSSL_BUILD_SHLIBCRYPTO
#  undef OPENSSL_EXTERN
#  define OPENSSL_EXTERN OPENSSL_EXPORT
# endif

    typedef unsigned char DES_cblock[8];
    typedef /* const */ unsigned char const_DES_cblock[8];
    /*
     * With "const", gcc 2.8.1 on Solaris thinks that DES_cblock * and
     * const_DES_cblock * are incompatible pointer types.
     */

    typedef struct DES_ks {
        union {
            DES_cblock cblock;
            /*
             * make sure things are correct size on machines with 8 byte longs
             */
            DES_LONG deslong[2];
        } ks[16];
    } DES_key_schedule;

# define DES_KEY_SZ      (sizeof(DES_cblock))
# define DES_SCHEDULE_SZ (sizeof(DES_key_schedule))

# define DES_ENCRYPT     1
# define DES_DECRYPT     0

# define DES_CBC_MODE    0
# define DES_PCBC_MODE   1

# define DES_ecb2_encrypt(i,o,k1,k2,e) \
        DES_ecb3_encrypt((i),(o),(k1),(k2),(k1),(e))

# define DES_ede2_cbc_encrypt(i,o,l,k1,k2,iv,e) \
        DES_ede3_cbc_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(e))

# define DES_ede2_cfb64_encrypt(i,o,l,k1,k2,iv,n,e) \
        DES_ede3_cfb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n),(e))

# define DES_ede2_ofb64_encrypt(i,o,l,k1,k2,iv,n) \
        DES_ede3_ofb64_encrypt((i),(o),(l),(k1),(k2),(k1),(iv),(n))

# define DES_fixup_key_parity DES_set_odd_parity

    /* openssl-3.3.2v\crypto\spr.h */

    const DES_LONG DES_SPtrans[8][64] = {
        {
            /* nibble 0 */
            0x02080800L, 0x00080000L, 0x02000002L, 0x02080802L,
            0x02000000L, 0x00080802L, 0x00080002L, 0x02000002L,
            0x00080802L, 0x02080800L, 0x02080000L, 0x00000802L,
            0x02000802L, 0x02000000L, 0x00000000L, 0x00080002L,
            0x00080000L, 0x00000002L, 0x02000800L, 0x00080800L,
            0x02080802L, 0x02080000L, 0x00000802L, 0x02000800L,
            0x00000002L, 0x00000800L, 0x00080800L, 0x02080002L,
            0x00000800L, 0x02000802L, 0x02080002L, 0x00000000L,
            0x00000000L, 0x02080802L, 0x02000800L, 0x00080002L,
            0x02080800L, 0x00080000L, 0x00000802L, 0x02000800L,
            0x02080002L, 0x00000800L, 0x00080800L, 0x02000002L,
            0x00080802L, 0x00000002L, 0x02000002L, 0x02080000L,
            0x02080802L, 0x00080800L, 0x02080000L, 0x02000802L,
            0x02000000L, 0x00000802L, 0x00080002L, 0x00000000L,
            0x00080000L, 0x02000000L, 0x02000802L, 0x02080800L,
            0x00000002L, 0x02080002L, 0x00000800L, 0x00080802L,
        },
        {
            /* nibble 1 */
            0x40108010L, 0x00000000L, 0x00108000L, 0x40100000L,
            0x40000010L, 0x00008010L, 0x40008000L, 0x00108000L,
            0x00008000L, 0x40100010L, 0x00000010L, 0x40008000L,
            0x00100010L, 0x40108000L, 0x40100000L, 0x00000010L,
            0x00100000L, 0x40008010L, 0x40100010L, 0x00008000L,
            0x00108010L, 0x40000000L, 0x00000000L, 0x00100010L,
            0x40008010L, 0x00108010L, 0x40108000L, 0x40000010L,
            0x40000000L, 0x00100000L, 0x00008010L, 0x40108010L,
            0x00100010L, 0x40108000L, 0x40008000L, 0x00108010L,
            0x40108010L, 0x00100010L, 0x40000010L, 0x00000000L,
            0x40000000L, 0x00008010L, 0x00100000L, 0x40100010L,
            0x00008000L, 0x40000000L, 0x00108010L, 0x40008010L,
            0x40108000L, 0x00008000L, 0x00000000L, 0x40000010L,
            0x00000010L, 0x40108010L, 0x00108000L, 0x40100000L,
            0x40100010L, 0x00100000L, 0x00008010L, 0x40008000L,
            0x40008010L, 0x00000010L, 0x40100000L, 0x00108000L,
        },
        {
            /* nibble 2 */
            0x04000001L, 0x04040100L, 0x00000100L, 0x04000101L,
            0x00040001L, 0x04000000L, 0x04000101L, 0x00040100L,
            0x04000100L, 0x00040000L, 0x04040000L, 0x00000001L,
            0x04040101L, 0x00000101L, 0x00000001L, 0x04040001L,
            0x00000000L, 0x00040001L, 0x04040100L, 0x00000100L,
            0x00000101L, 0x04040101L, 0x00040000L, 0x04000001L,
            0x04040001L, 0x04000100L, 0x00040101L, 0x04040000L,
            0x00040100L, 0x00000000L, 0x04000000L, 0x00040101L,
            0x04040100L, 0x00000100L, 0x00000001L, 0x00040000L,
            0x00000101L, 0x00040001L, 0x04040000L, 0x04000101L,
            0x00000000L, 0x04040100L, 0x00040100L, 0x04040001L,
            0x00040001L, 0x04000000L, 0x04040101L, 0x00000001L,
            0x00040101L, 0x04000001L, 0x04000000L, 0x04040101L,
            0x00040000L, 0x04000100L, 0x04000101L, 0x00040100L,
            0x04000100L, 0x00000000L, 0x04040001L, 0x00000101L,
            0x04000001L, 0x00040101L, 0x00000100L, 0x04040000L,
        },
        {
            /* nibble 3 */
            0x00401008L, 0x10001000L, 0x00000008L, 0x10401008L,
            0x00000000L, 0x10400000L, 0x10001008L, 0x00400008L,
            0x10401000L, 0x10000008L, 0x10000000L, 0x00001008L,
            0x10000008L, 0x00401008L, 0x00400000L, 0x10000000L,
            0x10400008L, 0x00401000L, 0x00001000L, 0x00000008L,
            0x00401000L, 0x10001008L, 0x10400000L, 0x00001000L,
            0x00001008L, 0x00000000L, 0x00400008L, 0x10401000L,
            0x10001000L, 0x10400008L, 0x10401008L, 0x00400000L,
            0x10400008L, 0x00001008L, 0x00400000L, 0x10000008L,
            0x00401000L, 0x10001000L, 0x00000008L, 0x10400000L,
            0x10001008L, 0x00000000L, 0x00001000L, 0x00400008L,
            0x00000000L, 0x10400008L, 0x10401000L, 0x00001000L,
            0x10000000L, 0x10401008L, 0x00401008L, 0x00400000L,
            0x10401008L, 0x00000008L, 0x10001000L, 0x00401008L,
            0x00400008L, 0x00401000L, 0x10400000L, 0x10001008L,
            0x00001008L, 0x10000000L, 0x10000008L, 0x10401000L,
        },
        {
            /* nibble 4 */
            0x08000000L, 0x00010000L, 0x00000400L, 0x08010420L,
            0x08010020L, 0x08000400L, 0x00010420L, 0x08010000L,
            0x00010000L, 0x00000020L, 0x08000020L, 0x00010400L,
            0x08000420L, 0x08010020L, 0x08010400L, 0x00000000L,
            0x00010400L, 0x08000000L, 0x00010020L, 0x00000420L,
            0x08000400L, 0x00010420L, 0x00000000L, 0x08000020L,
            0x00000020L, 0x08000420L, 0x08010420L, 0x00010020L,
            0x08010000L, 0x00000400L, 0x00000420L, 0x08010400L,
            0x08010400L, 0x08000420L, 0x00010020L, 0x08010000L,
            0x00010000L, 0x00000020L, 0x08000020L, 0x08000400L,
            0x08000000L, 0x00010400L, 0x08010420L, 0x00000000L,
            0x00010420L, 0x08000000L, 0x00000400L, 0x00010020L,
            0x08000420L, 0x00000400L, 0x00000000L, 0x08010420L,
            0x08010020L, 0x08010400L, 0x00000420L, 0x00010000L,
            0x00010400L, 0x08010020L, 0x08000400L, 0x00000420L,
            0x00000020L, 0x00010420L, 0x08010000L, 0x08000020L,
        },
        {
            /* nibble 5 */
            0x80000040L, 0x00200040L, 0x00000000L, 0x80202000L,
            0x00200040L, 0x00002000L, 0x80002040L, 0x00200000L,
            0x00002040L, 0x80202040L, 0x00202000L, 0x80000000L,
            0x80002000L, 0x80000040L, 0x80200000L, 0x00202040L,
            0x00200000L, 0x80002040L, 0x80200040L, 0x00000000L,
            0x00002000L, 0x00000040L, 0x80202000L, 0x80200040L,
            0x80202040L, 0x80200000L, 0x80000000L, 0x00002040L,
            0x00000040L, 0x00202000L, 0x00202040L, 0x80002000L,
            0x00002040L, 0x80000000L, 0x80002000L, 0x00202040L,
            0x80202000L, 0x00200040L, 0x00000000L, 0x80002000L,
            0x80000000L, 0x00002000L, 0x80200040L, 0x00200000L,
            0x00200040L, 0x80202040L, 0x00202000L, 0x00000040L,
            0x80202040L, 0x00202000L, 0x00200000L, 0x80002040L,
            0x80000040L, 0x80200000L, 0x00202040L, 0x00000000L,
            0x00002000L, 0x80000040L, 0x80002040L, 0x80202000L,
            0x80200000L, 0x00002040L, 0x00000040L, 0x80200040L,
        },
        {
            /* nibble 6 */
            0x00004000L, 0x00000200L, 0x01000200L, 0x01000004L,
            0x01004204L, 0x00004004L, 0x00004200L, 0x00000000L,
            0x01000000L, 0x01000204L, 0x00000204L, 0x01004000L,
            0x00000004L, 0x01004200L, 0x01004000L, 0x00000204L,
            0x01000204L, 0x00004000L, 0x00004004L, 0x01004204L,
            0x00000000L, 0x01000200L, 0x01000004L, 0x00004200L,
            0x01004004L, 0x00004204L, 0x01004200L, 0x00000004L,
            0x00004204L, 0x01004004L, 0x00000200L, 0x01000000L,
            0x00004204L, 0x01004000L, 0x01004004L, 0x00000204L,
            0x00004000L, 0x00000200L, 0x01000000L, 0x01004004L,
            0x01000204L, 0x00004204L, 0x00004200L, 0x00000000L,
            0x00000200L, 0x01000004L, 0x00000004L, 0x01000200L,
            0x00000000L, 0x01000204L, 0x01000200L, 0x00004200L,
            0x00000204L, 0x00004000L, 0x01004204L, 0x01000000L,
            0x01004200L, 0x00000004L, 0x00004004L, 0x01004204L,
            0x01000004L, 0x01004200L, 0x01004000L, 0x00004004L,
        },
        {
            /* nibble 7 */
            0x20800080L, 0x20820000L, 0x00020080L, 0x00000000L,
            0x20020000L, 0x00800080L, 0x20800000L, 0x20820080L,
            0x00000080L, 0x20000000L, 0x00820000L, 0x00020080L,
            0x00820080L, 0x20020080L, 0x20000080L, 0x20800000L,
            0x00020000L, 0x00820080L, 0x00800080L, 0x20020000L,
            0x20820080L, 0x20000080L, 0x00000000L, 0x00820000L,
            0x20000000L, 0x00800000L, 0x20020080L, 0x20800080L,
            0x00800000L, 0x00020000L, 0x20820000L, 0x00000080L,
            0x00800000L, 0x00020000L, 0x20000080L, 0x20820080L,
            0x00020080L, 0x20000000L, 0x00000000L, 0x00820000L,
            0x20800080L, 0x20020080L, 0x20020000L, 0x00800080L,
            0x20820000L, 0x00000080L, 0x00800080L, 0x20020000L,
            0x20820080L, 0x00800000L, 0x20800000L, 0x20000080L,
            0x00820000L, 0x00020080L, 0x20020080L, 0x20800000L,
            0x00000080L, 0x20820000L, 0x00820080L, 0x00000000L,
            0x20000000L, 0x20800080L, 0x00020000L, 0x00820080L,
        }
    };

    /* openssl-3.3.2v\crypto\des\des_local.h */

    # define ITERATIONS 16
    # define HALF_ITERATIONS 8

    # define c2l(c,l)        (l =((DES_LONG)(*((c)++)))    , \
                             l|=((DES_LONG)(*((c)++)))<< 8L, \
                             l|=((DES_LONG)(*((c)++)))<<16L, \
                             l|=((DES_LONG)(*((c)++)))<<24L)

    /* NOTE - c is not incremented as per c2l */
    # define c2ln(c,l1,l2,n) { \
                            c+=n; \
                            l1=l2=0; \
                            switch (n) { \
                            case 8: l2 =((DES_LONG)(*(--(c))))<<24L; \
                            /* fall thru */                          \
                            case 7: l2|=((DES_LONG)(*(--(c))))<<16L; \
                            /* fall thru */                          \
                            case 6: l2|=((DES_LONG)(*(--(c))))<< 8L; \
                            /* fall thru */                          \
                            case 5: l2|=((DES_LONG)(*(--(c))));      \
                            /* fall thru */                          \
                            case 4: l1 =((DES_LONG)(*(--(c))))<<24L; \
                            /* fall thru */                          \
                            case 3: l1|=((DES_LONG)(*(--(c))))<<16L; \
                            /* fall thru */                          \
                            case 2: l1|=((DES_LONG)(*(--(c))))<< 8L; \
                            /* fall thru */                          \
                            case 1: l1|=((DES_LONG)(*(--(c))));      \
                                    } \
                            }

    # define l2c(l,c)        (*((c)++)=(unsigned char)(((l)     )&0xff), \
                             *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
                             *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
                             *((c)++)=(unsigned char)(((l)>>24L)&0xff))

    /*
     * replacements for htonl and ntohl since I have no idea what to do when
     * faced with machines with 8 byte longs.
     */

    # define n2l(c,l)        (l =((DES_LONG)(*((c)++)))<<24L, \
                             l|=((DES_LONG)(*((c)++)))<<16L, \
                             l|=((DES_LONG)(*((c)++)))<< 8L, \
                             l|=((DES_LONG)(*((c)++))))

    # define l2n(l,c)        (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
                             *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
                             *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
                             *((c)++)=(unsigned char)(((l)     )&0xff))

    /* NOTE - c is not incremented as per l2c */
    # define l2cn(l1,l2,c,n) { \
                            c+=n; \
                            switch (n) { \
                            case 8: *(--(c))=(unsigned char)(((l2)>>24L)&0xff); \
                            /* fall thru */                                     \
                            case 7: *(--(c))=(unsigned char)(((l2)>>16L)&0xff); \
                            /* fall thru */                                     \
                            case 6: *(--(c))=(unsigned char)(((l2)>> 8L)&0xff); \
                            /* fall thru */                                     \
                            case 5: *(--(c))=(unsigned char)(((l2)     )&0xff); \
                            /* fall thru */                                     \
                            case 4: *(--(c))=(unsigned char)(((l1)>>24L)&0xff); \
                            /* fall thru */                                     \
                            case 3: *(--(c))=(unsigned char)(((l1)>>16L)&0xff); \
                            /* fall thru */                                     \
                            case 2: *(--(c))=(unsigned char)(((l1)>> 8L)&0xff); \
                            /* fall thru */                                     \
                            case 1: *(--(c))=(unsigned char)(((l1)     )&0xff); \
                                    } \
                            }

    # if defined(_MSC_VER)
    #  define ROTATE(a,n)     (_lrotr(a,n))
    # elif defined(__ICC)
    #  define ROTATE(a,n)     (_rotr(a,n))
    # elif defined(__GNUC__) && __GNUC__>=2 && !defined(__STRICT_ANSI__) && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) && !defined(PEDANTIC)
    #  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
    #   define ROTATE(a,n)   ({ register unsigned int ret;   \
                                    asm ("rorl %1,%0"       \
                                            : "=r"(ret)     \
                                            : "I"(n),"0"(a) \
                                            : "cc");        \
                               ret;                         \
                            })
    #  endif
    # endif
    # ifndef ROTATE
    #  define ROTATE(a,n)     (((a)>>(n))+((a)<<(32-(n))))
    # endif

    /*
     * Don't worry about the LOAD_DATA() stuff, that is used by fcrypt() to add
     * it's little bit to the front
     */

    # ifdef DES_FCRYPT

    #  define LOAD_DATA_tmp(R,S,u,t,E0,E1) \
            { DES_LONG tmp; LOAD_DATA(R,S,u,t,E0,E1,tmp); }

    #  define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
            t=R^(R>>16L); \
            u=t&E0; t&=E1; \
            tmp=(u<<16); u^=R^s[S  ]; u^=tmp; \
            tmp=(t<<16); t^=R^s[S+1]; t^=tmp
    # else
    #  define LOAD_DATA_tmp(a,b,c,d,e,f) LOAD_DATA(a,b,c,d,e,f,g)
    #  define LOAD_DATA(R,S,u,t,E0,E1,tmp) \
            u=R^s[S  ]; \
            t=R^s[S+1]
    # endif

    /*
     * It recently occurred to me that 0^0^0^0^0^0^0 == 0, so there is no reason
     * to not xor all the sub items together.  This potentially saves a register
     * since things can be xored directly into L
     */

    # define D_ENCRYPT(LL,R,S) { \
            LOAD_DATA_tmp(R,S,u,t,E0,E1); \
            t=ROTATE(t,4); \
            LL^= \
                DES_SPtrans[0][(u>> 2L)&0x3f]^ \
                DES_SPtrans[2][(u>>10L)&0x3f]^ \
                DES_SPtrans[4][(u>>18L)&0x3f]^ \
                DES_SPtrans[6][(u>>26L)&0x3f]^ \
                DES_SPtrans[1][(t>> 2L)&0x3f]^ \
                DES_SPtrans[3][(t>>10L)&0x3f]^ \
                DES_SPtrans[5][(t>>18L)&0x3f]^ \
                DES_SPtrans[7][(t>>26L)&0x3f]; }

            /*-
             * IP and FP
             * The problem is more of a geometric problem that random bit fiddling.
             0  1  2  3  4  5  6  7      62 54 46 38 30 22 14  6
             8  9 10 11 12 13 14 15      60 52 44 36 28 20 12  4
            16 17 18 19 20 21 22 23      58 50 42 34 26 18 10  2
            24 25 26 27 28 29 30 31  to  56 48 40 32 24 16  8  0

            32 33 34 35 36 37 38 39      63 55 47 39 31 23 15  7
            40 41 42 43 44 45 46 47      61 53 45 37 29 21 13  5
            48 49 50 51 52 53 54 55      59 51 43 35 27 19 11  3
            56 57 58 59 60 61 62 63      57 49 41 33 25 17  9  1

            The output has been subject to swaps of the form
            0 1 -> 3 1 but the odd and even bits have been put into
            2 3    2 0
            different words.  The main trick is to remember that
            t=((l>>size)^r)&(mask);
            r^=t;
            l^=(t<<size);
            can be used to swap and move bits between words.

            So l =  0  1  2  3  r = 16 17 18 19
                    4  5  6  7      20 21 22 23
                    8  9 10 11      24 25 26 27
                   12 13 14 15      28 29 30 31
            becomes (for size == 2 and mask == 0x3333)
               t =   2^16  3^17 -- --   l =  0  1 16 17  r =  2  3 18 19
                     6^20  7^21 -- --        4  5 20 21       6  7 22 23
                    10^24 11^25 -- --        8  9 24 25      10 11 24 25
                    14^28 15^29 -- --       12 13 28 29      14 15 28 29

            Thanks for hints from Richard Outerbridge - he told me IP&FP
            could be done in 15 xor, 10 shifts and 5 ands.
            When I finally started to think of the problem in 2D
            I first got ~42 operations without xors.  When I remembered
            how to use xors :-) I got it to its final state.
            */
    # define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
            (b)^=(t),\
            (a)^=((t)<<(n)))

    # undef IP
    # define IP(l,r) \
            { \
            register DES_LONG tt; \
            PERM_OP(r,l,tt, 4,0x0f0f0f0fL); \
            PERM_OP(l,r,tt,16,0x0000ffffL); \
            PERM_OP(r,l,tt, 2,0x33333333L); \
            PERM_OP(l,r,tt, 8,0x00ff00ffL); \
            PERM_OP(r,l,tt, 1,0x55555555L); \
            }

    # define FP(l,r) \
            { \
            register DES_LONG tt; \
            PERM_OP(l,r,tt, 1,0x55555555L); \
            PERM_OP(r,l,tt, 8,0x00ff00ffL); \
            PERM_OP(l,r,tt, 2,0x33333333L); \
            PERM_OP(r,l,tt,16,0x0000ffffL); \
            PERM_OP(l,r,tt, 4,0x0f0f0f0fL); \
            }

    /* openssl-3.3.2v\crypto\des\set_key.c */

    /*-
     * set_key.c v 1.4 eay 24/9/91
     * 1.4 Speed up by 400% :-)
     * 1.3 added register declarations.
     * 1.2 unrolled make_key_sched a bit more
     * 1.1 added norm_expand_bits
     * 1.0 First working version
     */

    # define OSSL_NELEM(x)    (sizeof(x)/sizeof((x)[0]))

    static unsigned int constant_time_msb(unsigned int a)
    {
        return 0 - (a >> (sizeof(a) * 8 - 1));
    }

    static unsigned int constant_time_is_zero(unsigned int a)
    {
        return constant_time_msb(~a & (a - 1));
    }

    static unsigned int constant_time_eq(unsigned int a, unsigned int b)
    {
        return constant_time_is_zero(a ^ b);
    }

    static unsigned char constant_time_eq_8(unsigned int a, unsigned int b)
    {
        return (unsigned char)constant_time_eq(a, b);
    }

    static const unsigned char odd_parity[256] = {
        1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
        16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
        32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
        49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
        64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
        81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
        97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110,
        110,
        112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127,
        127,
        128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143,
        143,
        145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158,
        158,
        161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174,
        174,
        176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191,
        191,
        193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206,
        206,
        208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223,
        223,
        224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239,
        239,
        241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254,
        254
    };

    void DES_set_odd_parity(DES_cblock *key)
    {
        unsigned int i;

        for (i = 0; i < DES_KEY_SZ; i++)
            (*key)[i] = odd_parity[(*key)[i]];
    }

    /*
     * Check that a key has the correct parity.
     * Return 1 if parity is okay and 0 if not.
     */
    int DES_check_key_parity(const_DES_cblock *key)
    {
        unsigned int i;
        unsigned char res = 0377, b;

        for (i = 0; i < DES_KEY_SZ; i++) {
            b = (*key)[i];
            b ^= b >> 4;
            b ^= b >> 2;
            b ^= b >> 1;
            res &= constant_time_eq_8(b & 1, 1);
        }
        return (int)(res & 1);
    }

    /*-
     * Weak and semi weak keys as taken from
     * %A D.W. Davies
     * %A W.L. Price
     * %T Security for Computer Networks
     * %I John Wiley & Sons
     * %D 1984
     */
    static const DES_cblock weak_keys[] = {
        /* weak keys */
        {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
        {0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE},
        {0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
        {0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1},
        /* semi-weak keys */
        {0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE},
        {0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01},
        {0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1},
        {0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E},
        {0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1},
        {0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01},
        {0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE},
        {0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E},
        {0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E},
        {0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01},
        {0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
        {0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1}
    };

    /*
     * Check for weak keys.
     * Return 1 if the key is weak and 0 otherwise.
     */
    int DES_is_weak_key(const_DES_cblock *key)
    {
        unsigned int i, res = 0;
        int j;

        for (i = 0; i < OSSL_NELEM(weak_keys); i++) {
            j = memcmp(weak_keys[i], key, sizeof(DES_cblock));
            res |= constant_time_is_zero((unsigned int)j);
        }
        return (int)(res & 1);
    }

    /*-
     * NOW DEFINED IN des_local.h
     * See ecb_encrypt.c for a pseudo description of these macros.
     * #define PERM_OP(a,b,t,n,m) ((t)=((((a)>>(n))^(b))&(m)),\
     *      (b)^=(t),\
     *      (a)=((a)^((t)<<(n))))
     */

    #define HPERM_OP(a,t,n,m) ((t)=((((a)<<(16-(n)))^(a))&(m)),\
            (a)=(a)^(t)^(t>>(16-(n))))

    static const DES_LONG des_skb[8][64] = {
        {
         /* for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
         0x00000000L, 0x00000010L, 0x20000000L, 0x20000010L,
         0x00010000L, 0x00010010L, 0x20010000L, 0x20010010L,
         0x00000800L, 0x00000810L, 0x20000800L, 0x20000810L,
         0x00010800L, 0x00010810L, 0x20010800L, 0x20010810L,
         0x00000020L, 0x00000030L, 0x20000020L, 0x20000030L,
         0x00010020L, 0x00010030L, 0x20010020L, 0x20010030L,
         0x00000820L, 0x00000830L, 0x20000820L, 0x20000830L,
         0x00010820L, 0x00010830L, 0x20010820L, 0x20010830L,
         0x00080000L, 0x00080010L, 0x20080000L, 0x20080010L,
         0x00090000L, 0x00090010L, 0x20090000L, 0x20090010L,
         0x00080800L, 0x00080810L, 0x20080800L, 0x20080810L,
         0x00090800L, 0x00090810L, 0x20090800L, 0x20090810L,
         0x00080020L, 0x00080030L, 0x20080020L, 0x20080030L,
         0x00090020L, 0x00090030L, 0x20090020L, 0x20090030L,
         0x00080820L, 0x00080830L, 0x20080820L, 0x20080830L,
         0x00090820L, 0x00090830L, 0x20090820L, 0x20090830L,
         },
        {
         /* for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 */
         0x00000000L, 0x02000000L, 0x00002000L, 0x02002000L,
         0x00200000L, 0x02200000L, 0x00202000L, 0x02202000L,
         0x00000004L, 0x02000004L, 0x00002004L, 0x02002004L,
         0x00200004L, 0x02200004L, 0x00202004L, 0x02202004L,
         0x00000400L, 0x02000400L, 0x00002400L, 0x02002400L,
         0x00200400L, 0x02200400L, 0x00202400L, 0x02202400L,
         0x00000404L, 0x02000404L, 0x00002404L, 0x02002404L,
         0x00200404L, 0x02200404L, 0x00202404L, 0x02202404L,
         0x10000000L, 0x12000000L, 0x10002000L, 0x12002000L,
         0x10200000L, 0x12200000L, 0x10202000L, 0x12202000L,
         0x10000004L, 0x12000004L, 0x10002004L, 0x12002004L,
         0x10200004L, 0x12200004L, 0x10202004L, 0x12202004L,
         0x10000400L, 0x12000400L, 0x10002400L, 0x12002400L,
         0x10200400L, 0x12200400L, 0x10202400L, 0x12202400L,
         0x10000404L, 0x12000404L, 0x10002404L, 0x12002404L,
         0x10200404L, 0x12200404L, 0x10202404L, 0x12202404L,
         },
        {
         /* for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 */
         0x00000000L, 0x00000001L, 0x00040000L, 0x00040001L,
         0x01000000L, 0x01000001L, 0x01040000L, 0x01040001L,
         0x00000002L, 0x00000003L, 0x00040002L, 0x00040003L,
         0x01000002L, 0x01000003L, 0x01040002L, 0x01040003L,
         0x00000200L, 0x00000201L, 0x00040200L, 0x00040201L,
         0x01000200L, 0x01000201L, 0x01040200L, 0x01040201L,
         0x00000202L, 0x00000203L, 0x00040202L, 0x00040203L,
         0x01000202L, 0x01000203L, 0x01040202L, 0x01040203L,
         0x08000000L, 0x08000001L, 0x08040000L, 0x08040001L,
         0x09000000L, 0x09000001L, 0x09040000L, 0x09040001L,
         0x08000002L, 0x08000003L, 0x08040002L, 0x08040003L,
         0x09000002L, 0x09000003L, 0x09040002L, 0x09040003L,
         0x08000200L, 0x08000201L, 0x08040200L, 0x08040201L,
         0x09000200L, 0x09000201L, 0x09040200L, 0x09040201L,
         0x08000202L, 0x08000203L, 0x08040202L, 0x08040203L,
         0x09000202L, 0x09000203L, 0x09040202L, 0x09040203L,
         },
        {
         /* for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 */
         0x00000000L, 0x00100000L, 0x00000100L, 0x00100100L,
         0x00000008L, 0x00100008L, 0x00000108L, 0x00100108L,
         0x00001000L, 0x00101000L, 0x00001100L, 0x00101100L,
         0x00001008L, 0x00101008L, 0x00001108L, 0x00101108L,
         0x04000000L, 0x04100000L, 0x04000100L, 0x04100100L,
         0x04000008L, 0x04100008L, 0x04000108L, 0x04100108L,
         0x04001000L, 0x04101000L, 0x04001100L, 0x04101100L,
         0x04001008L, 0x04101008L, 0x04001108L, 0x04101108L,
         0x00020000L, 0x00120000L, 0x00020100L, 0x00120100L,
         0x00020008L, 0x00120008L, 0x00020108L, 0x00120108L,
         0x00021000L, 0x00121000L, 0x00021100L, 0x00121100L,
         0x00021008L, 0x00121008L, 0x00021108L, 0x00121108L,
         0x04020000L, 0x04120000L, 0x04020100L, 0x04120100L,
         0x04020008L, 0x04120008L, 0x04020108L, 0x04120108L,
         0x04021000L, 0x04121000L, 0x04021100L, 0x04121100L,
         0x04021008L, 0x04121008L, 0x04021108L, 0x04121108L,
         },
        {
         /* for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 */
         0x00000000L, 0x10000000L, 0x00010000L, 0x10010000L,
         0x00000004L, 0x10000004L, 0x00010004L, 0x10010004L,
         0x20000000L, 0x30000000L, 0x20010000L, 0x30010000L,
         0x20000004L, 0x30000004L, 0x20010004L, 0x30010004L,
         0x00100000L, 0x10100000L, 0x00110000L, 0x10110000L,
         0x00100004L, 0x10100004L, 0x00110004L, 0x10110004L,
         0x20100000L, 0x30100000L, 0x20110000L, 0x30110000L,
         0x20100004L, 0x30100004L, 0x20110004L, 0x30110004L,
         0x00001000L, 0x10001000L, 0x00011000L, 0x10011000L,
         0x00001004L, 0x10001004L, 0x00011004L, 0x10011004L,
         0x20001000L, 0x30001000L, 0x20011000L, 0x30011000L,
         0x20001004L, 0x30001004L, 0x20011004L, 0x30011004L,
         0x00101000L, 0x10101000L, 0x00111000L, 0x10111000L,
         0x00101004L, 0x10101004L, 0x00111004L, 0x10111004L,
         0x20101000L, 0x30101000L, 0x20111000L, 0x30111000L,
         0x20101004L, 0x30101004L, 0x20111004L, 0x30111004L,
         },
        {
         /* for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 */
         0x00000000L, 0x08000000L, 0x00000008L, 0x08000008L,
         0x00000400L, 0x08000400L, 0x00000408L, 0x08000408L,
         0x00020000L, 0x08020000L, 0x00020008L, 0x08020008L,
         0x00020400L, 0x08020400L, 0x00020408L, 0x08020408L,
         0x00000001L, 0x08000001L, 0x00000009L, 0x08000009L,
         0x00000401L, 0x08000401L, 0x00000409L, 0x08000409L,
         0x00020001L, 0x08020001L, 0x00020009L, 0x08020009L,
         0x00020401L, 0x08020401L, 0x00020409L, 0x08020409L,
         0x02000000L, 0x0A000000L, 0x02000008L, 0x0A000008L,
         0x02000400L, 0x0A000400L, 0x02000408L, 0x0A000408L,
         0x02020000L, 0x0A020000L, 0x02020008L, 0x0A020008L,
         0x02020400L, 0x0A020400L, 0x02020408L, 0x0A020408L,
         0x02000001L, 0x0A000001L, 0x02000009L, 0x0A000009L,
         0x02000401L, 0x0A000401L, 0x02000409L, 0x0A000409L,
         0x02020001L, 0x0A020001L, 0x02020009L, 0x0A020009L,
         0x02020401L, 0x0A020401L, 0x02020409L, 0x0A020409L,
         },
        {
         /* for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 */
         0x00000000L, 0x00000100L, 0x00080000L, 0x00080100L,
         0x01000000L, 0x01000100L, 0x01080000L, 0x01080100L,
         0x00000010L, 0x00000110L, 0x00080010L, 0x00080110L,
         0x01000010L, 0x01000110L, 0x01080010L, 0x01080110L,
         0x00200000L, 0x00200100L, 0x00280000L, 0x00280100L,
         0x01200000L, 0x01200100L, 0x01280000L, 0x01280100L,
         0x00200010L, 0x00200110L, 0x00280010L, 0x00280110L,
         0x01200010L, 0x01200110L, 0x01280010L, 0x01280110L,
         0x00000200L, 0x00000300L, 0x00080200L, 0x00080300L,
         0x01000200L, 0x01000300L, 0x01080200L, 0x01080300L,
         0x00000210L, 0x00000310L, 0x00080210L, 0x00080310L,
         0x01000210L, 0x01000310L, 0x01080210L, 0x01080310L,
         0x00200200L, 0x00200300L, 0x00280200L, 0x00280300L,
         0x01200200L, 0x01200300L, 0x01280200L, 0x01280300L,
         0x00200210L, 0x00200310L, 0x00280210L, 0x00280310L,
         0x01200210L, 0x01200310L, 0x01280210L, 0x01280310L,
         },
        {
         /* for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 */
         0x00000000L, 0x04000000L, 0x00040000L, 0x04040000L,
         0x00000002L, 0x04000002L, 0x00040002L, 0x04040002L,
         0x00002000L, 0x04002000L, 0x00042000L, 0x04042000L,
         0x00002002L, 0x04002002L, 0x00042002L, 0x04042002L,
         0x00000020L, 0x04000020L, 0x00040020L, 0x04040020L,
         0x00000022L, 0x04000022L, 0x00040022L, 0x04040022L,
         0x00002020L, 0x04002020L, 0x00042020L, 0x04042020L,
         0x00002022L, 0x04002022L, 0x00042022L, 0x04042022L,
         0x00000800L, 0x04000800L, 0x00040800L, 0x04040800L,
         0x00000802L, 0x04000802L, 0x00040802L, 0x04040802L,
         0x00002800L, 0x04002800L, 0x00042800L, 0x04042800L,
         0x00002802L, 0x04002802L, 0x00042802L, 0x04042802L,
         0x00000820L, 0x04000820L, 0x00040820L, 0x04040820L,
         0x00000822L, 0x04000822L, 0x00040822L, 0x04040822L,
         0x00002820L, 0x04002820L, 0x00042820L, 0x04042820L,
         0x00002822L, 0x04002822L, 0x00042822L, 0x04042822L,
         }
    };

    //int DES_set_key(const_DES_cblock *key, DES_key_schedule *schedule)
    //{
    //    if (DES_check_key) {
    //        return DES_set_key_checked(key, schedule);
    //    } else {
    //        DES_set_key_unchecked(key, schedule);
    //        return 0;
    //    }
    //}

    /*-
     * return 0 if key parity is odd (correct),
     * return -1 if key parity error,
     * return -2 if illegal weak key.
     */

    void DES_set_key_unchecked(const_DES_cblock *key, DES_key_schedule *schedule)
    {
        static const int shifts2[16] =
            { 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0 };
        register DES_LONG c, d, t, s, t2;
        register const unsigned char *in;
        register DES_LONG *k;
        register int i;

    #ifdef OPENBSD_DEV_CRYPTO
        memcpy(schedule->key, key, sizeof(schedule->key));
        schedule->session = NULL;
    #endif
        k = &schedule->ks->deslong[0];
        in = &(*key)[0];

        c2l(in, c);
        c2l(in, d);

        /*
         * do PC1 in 47 simple operations. Thanks to John Fletcher
         * for the inspiration.
         */
        PERM_OP(d, c, t, 4, 0x0f0f0f0fL);
        HPERM_OP(c, t, -2, 0xcccc0000L);
        HPERM_OP(d, t, -2, 0xcccc0000L);
        PERM_OP(d, c, t, 1, 0x55555555L);
        PERM_OP(c, d, t, 8, 0x00ff00ffL);
        PERM_OP(d, c, t, 1, 0x55555555L);
        d = (((d & 0x000000ffL) << 16L) | (d & 0x0000ff00L) |
             ((d & 0x00ff0000L) >> 16L) | ((c & 0xf0000000L) >> 4L));
        c &= 0x0fffffffL;

        for (i = 0; i < ITERATIONS; i++) {
            if (shifts2[i]) {
                c = ((c >> 2L) | (c << 26L));
                d = ((d >> 2L) | (d << 26L));
            } else {
                c = ((c >> 1L) | (c << 27L));
                d = ((d >> 1L) | (d << 27L));
            }
            c &= 0x0fffffffL;
            d &= 0x0fffffffL;
            /*
             * could be a few less shifts but I am to lazy at this point in time
             * to investigate
             */
            s = des_skb[0][(c) & 0x3f] |
                des_skb[1][((c >> 6L) & 0x03) | ((c >> 7L) & 0x3c)] |
                des_skb[2][((c >> 13L) & 0x0f) | ((c >> 14L) & 0x30)] |
                des_skb[3][((c >> 20L) & 0x01) | ((c >> 21L) & 0x06) |
                           ((c >> 22L) & 0x38)];
            t = des_skb[4][(d) & 0x3f] |
                des_skb[5][((d >> 7L) & 0x03) | ((d >> 8L) & 0x3c)] |
                des_skb[6][(d >> 15L) & 0x3f] |
                des_skb[7][((d >> 21L) & 0x0f) | ((d >> 22L) & 0x30)];

            /* table contained 0213 4657 */
            t2 = ((t << 16L) | (s & 0x0000ffffL)) & 0xffffffffL;
            *(k++) = ROTATE(t2, 30) & 0xffffffffL;

            t2 = ((s >> 16L) | (t & 0xffff0000L));
            *(k++) = ROTATE(t2, 26) & 0xffffffffL;
        }
    }

    int DES_set_key_checked(const_DES_cblock * key, DES_key_schedule * schedule)
    {
        if (!DES_check_key_parity(key))
            return -1;
        if (DES_is_weak_key(key))
            return -2;
        DES_set_key_unchecked(key, schedule);
        return 0;
    }

    //int DES_key_sched(const_DES_cblock *key, DES_key_schedule *schedule)
    //{
    //    return DES_set_key(key, schedule);
    //}

    /* openssl-3.3.2v\crypto\des\des_enc.c */

    void DES_encrypt1(DES_LONG * data, DES_key_schedule * ks, int enc)
    {
        register DES_LONG l, r, t, u;
        register DES_LONG * s;

        r = data[0];
        l = data[1];

        IP(r, l);
        /*
         * Things have been modified so that the initial rotate is done outside
         * the loop.  This required the DES_SPtrans values in sp.h to be rotated
         * 1 bit to the right. One perl script later and things have a 5% speed
         * up on a sparc2. Thanks to Richard Outerbridge for pointing this out.
         */
         /* clear the top bits on machines with 8byte longs */
         /* shift left by 2 */
        r = ROTATE(r, 29) & 0xffffffffL;
        l = ROTATE(l, 29) & 0xffffffffL;

        s = ks->ks->deslong;
        /*
         * I don't know if it is worth the effort of loop unrolling the inner
         * loop
         */
        if (enc) {
            D_ENCRYPT(l, r, 0);     /* 1 */
            D_ENCRYPT(r, l, 2);     /* 2 */
            D_ENCRYPT(l, r, 4);     /* 3 */
            D_ENCRYPT(r, l, 6);     /* 4 */
            D_ENCRYPT(l, r, 8);     /* 5 */
            D_ENCRYPT(r, l, 10);    /* 6 */
            D_ENCRYPT(l, r, 12);    /* 7 */
            D_ENCRYPT(r, l, 14);    /* 8 */
            D_ENCRYPT(l, r, 16);    /* 9 */
            D_ENCRYPT(r, l, 18);    /* 10 */
            D_ENCRYPT(l, r, 20);    /* 11 */
            D_ENCRYPT(r, l, 22);    /* 12 */
            D_ENCRYPT(l, r, 24);    /* 13 */
            D_ENCRYPT(r, l, 26);    /* 14 */
            D_ENCRYPT(l, r, 28);    /* 15 */
            D_ENCRYPT(r, l, 30);    /* 16 */
        }
        else {
            D_ENCRYPT(l, r, 30);    /* 16 */
            D_ENCRYPT(r, l, 28);    /* 15 */
            D_ENCRYPT(l, r, 26);    /* 14 */
            D_ENCRYPT(r, l, 24);    /* 13 */
            D_ENCRYPT(l, r, 22);    /* 12 */
            D_ENCRYPT(r, l, 20);    /* 11 */
            D_ENCRYPT(l, r, 18);    /* 10 */
            D_ENCRYPT(r, l, 16);    /* 9 */
            D_ENCRYPT(l, r, 14);    /* 8 */
            D_ENCRYPT(r, l, 12);    /* 7 */
            D_ENCRYPT(l, r, 10);    /* 6 */
            D_ENCRYPT(r, l, 8);     /* 5 */
            D_ENCRYPT(l, r, 6);     /* 4 */
            D_ENCRYPT(r, l, 4);     /* 3 */
            D_ENCRYPT(l, r, 2);     /* 2 */
            D_ENCRYPT(r, l, 0);     /* 1 */
        }

        /* rotate and clear the top bits on machines with 8byte longs */
        l = ROTATE(l, 3) & 0xffffffffL;
        r = ROTATE(r, 3) & 0xffffffffL;

        FP(r, l);
        data[0] = l;
        data[1] = r;
        l = r = t = u = 0;
    }

    void DES_encrypt2(DES_LONG * data, DES_key_schedule * ks, int enc)
    {
        register DES_LONG l, r, t, u;
        register DES_LONG * s;

        r = data[0];
        l = data[1];

        /*
         * Things have been modified so that the initial rotate is done outside
         * the loop.  This required the DES_SPtrans values in sp.h to be rotated
         * 1 bit to the right. One perl script later and things have a 5% speed
         * up on a sparc2. Thanks to Richard Outerbridge for pointing this out.
         */
         /* clear the top bits on machines with 8byte longs */
        r = ROTATE(r, 29) & 0xffffffffL;
        l = ROTATE(l, 29) & 0xffffffffL;

        s = ks->ks->deslong;
        /*
         * I don't know if it is worth the effort of loop unrolling the inner
         * loop
         */
        if (enc) {
            D_ENCRYPT(l, r, 0);     /* 1 */
            D_ENCRYPT(r, l, 2);     /* 2 */
            D_ENCRYPT(l, r, 4);     /* 3 */
            D_ENCRYPT(r, l, 6);     /* 4 */
            D_ENCRYPT(l, r, 8);     /* 5 */
            D_ENCRYPT(r, l, 10);    /* 6 */
            D_ENCRYPT(l, r, 12);    /* 7 */
            D_ENCRYPT(r, l, 14);    /* 8 */
            D_ENCRYPT(l, r, 16);    /* 9 */
            D_ENCRYPT(r, l, 18);    /* 10 */
            D_ENCRYPT(l, r, 20);    /* 11 */
            D_ENCRYPT(r, l, 22);    /* 12 */
            D_ENCRYPT(l, r, 24);    /* 13 */
            D_ENCRYPT(r, l, 26);    /* 14 */
            D_ENCRYPT(l, r, 28);    /* 15 */
            D_ENCRYPT(r, l, 30);    /* 16 */
        }
        else {
            D_ENCRYPT(l, r, 30);    /* 16 */
            D_ENCRYPT(r, l, 28);    /* 15 */
            D_ENCRYPT(l, r, 26);    /* 14 */
            D_ENCRYPT(r, l, 24);    /* 13 */
            D_ENCRYPT(l, r, 22);    /* 12 */
            D_ENCRYPT(r, l, 20);    /* 11 */
            D_ENCRYPT(l, r, 18);    /* 10 */
            D_ENCRYPT(r, l, 16);    /* 9 */
            D_ENCRYPT(l, r, 14);    /* 8 */
            D_ENCRYPT(r, l, 12);    /* 7 */
            D_ENCRYPT(l, r, 10);    /* 6 */
            D_ENCRYPT(r, l, 8);     /* 5 */
            D_ENCRYPT(l, r, 6);     /* 4 */
            D_ENCRYPT(r, l, 4);     /* 3 */
            D_ENCRYPT(l, r, 2);     /* 2 */
            D_ENCRYPT(r, l, 0);     /* 1 */
        }
        /* rotate and clear the top bits on machines with 8byte longs */
        data[0] = ROTATE(l, 3) & 0xffffffffL;
        data[1] = ROTATE(r, 3) & 0xffffffffL;
        l = r = t = u = 0;
    }

    void DES_encrypt3(DES_LONG * data, DES_key_schedule * ks1,
        DES_key_schedule * ks2, DES_key_schedule * ks3)
    {
        register DES_LONG l, r;

        l = data[0];
        r = data[1];
        IP(l, r);
        data[0] = l;
        data[1] = r;
        DES_encrypt2((DES_LONG *)data, ks1, DES_ENCRYPT);
        DES_encrypt2((DES_LONG *)data, ks2, DES_DECRYPT);
        DES_encrypt2((DES_LONG *)data, ks3, DES_ENCRYPT);
        l = data[0];
        r = data[1];
        FP(r, l);
        data[0] = l;
        data[1] = r;
    }

    void DES_decrypt3(DES_LONG * data, DES_key_schedule * ks1,
        DES_key_schedule * ks2, DES_key_schedule * ks3)
    {
        register DES_LONG l, r;

        l = data[0];
        r = data[1];
        IP(l, r);
        data[0] = l;
        data[1] = r;
        DES_encrypt2((DES_LONG *)data, ks3, DES_DECRYPT);
        DES_encrypt2((DES_LONG *)data, ks2, DES_ENCRYPT);
        DES_encrypt2((DES_LONG *)data, ks1, DES_DECRYPT);
        l = data[0];
        r = data[1];
        FP(r, l);
        data[0] = l;
        data[1] = r;
    }

#ifndef DES_DEFAULT_OPTIONS

# undef CBC_ENC_C__DONT_UPDATE_IV
//# include "ncbc_enc.c"          /* DES_ncbc_encrypt */

    void DES_ede3_cbc_encrypt(const unsigned char * input, unsigned char * output,
        long length, DES_key_schedule * ks1,
        DES_key_schedule * ks2, DES_key_schedule * ks3,
        DES_cblock * ivec, int enc)
    {
        register DES_LONG tin0, tin1;
        register DES_LONG tout0, tout1, xor0, xor1;
        register const unsigned char * in;
        unsigned char * out;
        register long l = length;
        DES_LONG tin[2];
        unsigned char * iv;

        in = input;
        out = output;
        iv = &(*ivec)[0];

        if (enc) {
            c2l(iv, tout0);
            c2l(iv, tout1);
            for (l -= 8; l >= 0; l -= 8) {
                c2l(in, tin0);
                c2l(in, tin1);
                tin0 ^= tout0;
                tin1 ^= tout1;

                tin[0] = tin0;
                tin[1] = tin1;
                DES_encrypt3((DES_LONG *)tin, ks1, ks2, ks3);
                tout0 = tin[0];
                tout1 = tin[1];

                l2c(tout0, out);
                l2c(tout1, out);
            }
            if (l != -8) {
                c2ln(in, tin0, tin1, l + 8);
                tin0 ^= tout0;
                tin1 ^= tout1;

                tin[0] = tin0;
                tin[1] = tin1;
                DES_encrypt3((DES_LONG *)tin, ks1, ks2, ks3);
                tout0 = tin[0];
                tout1 = tin[1];

                l2c(tout0, out);
                l2c(tout1, out);
            }
            iv = &(*ivec)[0];
            l2c(tout0, iv);
            l2c(tout1, iv);
        }
        else {
            register DES_LONG t0, t1;

            c2l(iv, xor0);
            c2l(iv, xor1);
            for (l -= 8; l >= 0; l -= 8) {
                c2l(in, tin0);
                c2l(in, tin1);

                t0 = tin0;
                t1 = tin1;

                tin[0] = tin0;
                tin[1] = tin1;
                DES_decrypt3((DES_LONG *)tin, ks1, ks2, ks3);
                tout0 = tin[0];
                tout1 = tin[1];

                tout0 ^= xor0;
                tout1 ^= xor1;
                l2c(tout0, out);
                l2c(tout1, out);
                xor0 = t0;
                xor1 = t1;
            }
            if (l != -8) {
                c2l(in, tin0);
                c2l(in, tin1);

                t0 = tin0;
                t1 = tin1;

                tin[0] = tin0;
                tin[1] = tin1;
                DES_decrypt3((DES_LONG *)tin, ks1, ks2, ks3);
                tout0 = tin[0];
                tout1 = tin[1];

                tout0 ^= xor0;
                tout1 ^= xor1;
                l2cn(tout0, tout1, out, l + 8);
                xor0 = t0;
                xor1 = t1;
            }

            iv = &(*ivec)[0];
            l2c(xor0, iv);
            l2c(xor1, iv);
        }
        tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
        tin[0] = tin[1] = 0;
    }

#endif                          /* DES_DEFAULT_OPTIONS */

    /* openssl-3.3.2v\crypto\des\ecb_enc.c */

    void DES_ecb_encrypt(const_DES_cblock * input, DES_cblock * output,
        DES_key_schedule * ks, int enc)
    {
        register DES_LONG l;
        DES_LONG ll[2];
        const unsigned char * in = &(*input)[0];
        unsigned char * out = &(*output)[0];

        c2l(in, l);
        ll[0] = l;
        c2l(in, l);
        ll[1] = l;
        DES_encrypt1(ll, ks, enc);
        l = ll[0];
        l2c(l, out);
        l = ll[1];
        l2c(l, out);
        l = ll[0] = ll[1] = 0;
    }

    /* openssl-3.3.2v\crypto\des\ncbc_enc.c */

#ifdef CBC_ENC_C__DONT_UPDATE_IV
    void DES_cbc_encrypt(const unsigned char * in, unsigned char * out, long length,
        DES_key_schedule * _schedule, DES_cblock * ivec, int enc)
#else
    void DES_ncbc_encrypt(const unsigned char * in, unsigned char * out,
        long length, DES_key_schedule * _schedule,
        DES_cblock * ivec, int enc)
#endif
    {
        register DES_LONG tin0, tin1;
        register DES_LONG tout0, tout1, xor0, xor1;
        register long l = length;
        DES_LONG tin[2];
        unsigned char * iv;

        iv = &(*ivec)[0];

        if (enc) {
            c2l(iv, tout0);
            c2l(iv, tout1);
            for (l -= 8; l >= 0; l -= 8) {
                c2l(in, tin0);
                c2l(in, tin1);
                tin0 ^= tout0;
                tin[0] = tin0;
                tin1 ^= tout1;
                tin[1] = tin1;
                DES_encrypt1((DES_LONG *)tin, _schedule, DES_ENCRYPT);
                tout0 = tin[0];
                l2c(tout0, out);
                tout1 = tin[1];
                l2c(tout1, out);
            }
            if (l != -8) {
                c2ln(in, tin0, tin1, l + 8);
                tin0 ^= tout0;
                tin[0] = tin0;
                tin1 ^= tout1;
                tin[1] = tin1;
                DES_encrypt1((DES_LONG *)tin, _schedule, DES_ENCRYPT);
                tout0 = tin[0];
                l2c(tout0, out);
                tout1 = tin[1];
                l2c(tout1, out);
            }
#ifndef CBC_ENC_C__DONT_UPDATE_IV
            iv = &(*ivec)[0];
            l2c(tout0, iv);
            l2c(tout1, iv);
#endif
        }
        else {
            c2l(iv, xor0);
            c2l(iv, xor1);
            for (l -= 8; l >= 0; l -= 8) {
                c2l(in, tin0);
                tin[0] = tin0;
                c2l(in, tin1);
                tin[1] = tin1;
                DES_encrypt1((DES_LONG *)tin, _schedule, DES_DECRYPT);
                tout0 = tin[0] ^ xor0;
                tout1 = tin[1] ^ xor1;
                l2c(tout0, out);
                l2c(tout1, out);
                xor0 = tin0;
                xor1 = tin1;
            }
            if (l != -8) {
                c2l(in, tin0);
                tin[0] = tin0;
                c2l(in, tin1);
                tin[1] = tin1;
                DES_encrypt1((DES_LONG *)tin, _schedule, DES_DECRYPT);
                tout0 = tin[0] ^ xor0;
                tout1 = tin[1] ^ xor1;
                l2cn(tout0, tout1, out, l + 8);
#ifndef CBC_ENC_C__DONT_UPDATE_IV
                xor0 = tin0;
                xor1 = tin1;
#endif
            }
#ifndef CBC_ENC_C__DONT_UPDATE_IV
            iv = &(*ivec)[0];
            l2c(xor0, iv);
            l2c(xor1, iv);
#endif
        }
        tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
        tin[0] = tin[1] = 0;
    }



# ifdef  __cplusplus
}
# endif

    // RC2 encryption
    // openssl-3.3.2v\crypto\rc2
    /*
     * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
     *
     * Licensed under the OpenSSL license (the "License").  You may not use
     * this file except in compliance with the License.  You can obtain a copy
     * in the file LICENSE in the source distribution or at
     * https://www.openssl.org/source/license.html
     */

# define RC2_ENCRYPT     1
# define RC2_DECRYPT     0

# define RC2_BLOCK       8
# define RC2_KEY_LENGTH  16

    typedef unsigned int RC2_INT;

    typedef struct rc2_key_st {
        RC2_INT data[64];
    } RC2_KEY;

    static const unsigned char key_table[256] = {
        0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79,
        0x4a, 0xa0, 0xd8, 0x9d, 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e,
        0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, 0x17, 0x9a, 0x59, 0xf5,
        0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32,
        0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22,
        0x5c, 0x6b, 0x4e, 0x82, 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c,
        0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, 0x12, 0x75, 0xca, 0x1f,
        0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26,
        0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b,
        0xbc, 0x94, 0x43, 0x03, 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7,
        0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, 0x08, 0xe8, 0xea, 0xde,
        0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a,
        0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e,
        0x04, 0x18, 0xa4, 0xec, 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc,
        0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, 0x99, 0x7c, 0x3a, 0x85,
        0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31,
        0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10,
        0x67, 0x6c, 0xba, 0xc9, 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c,
        0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, 0x0d, 0x38, 0x34, 0x1b,
        0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e,
        0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68,
        0xfe, 0x7f, 0xc1, 0xad,
    };

#if defined(_MSC_VER) && defined(_ARM_)
# pragma optimize("g",off)
#endif

    /*
     * It has come to my attention that there are 2 versions of the RC2 key
     * schedule.  One which is normal, and anther which has a hook to use a
     * reduced key length. BSAFE uses the 'retarded' version.  What I previously
     * shipped is the same as specifying 1024 for the 'bits' parameter.  Bsafe
     * uses a version where the bits parameter is the same as len*8
     */
    void RC2_set_key(RC2_KEY * key, int len, const unsigned char * data, int bits)
    {
        int i, j;
        unsigned char * k;
        RC2_INT * ki;
        unsigned int c, d;

        k = (unsigned char *)&(key->data[0]);
        *k = 0;                     /* for if there is a zero length key */

        if (len > 128)
            len = 128;
        if (bits <= 0)
            bits = 1024;
        if (bits > 1024)
            bits = 1024;

        for (i = 0; i < len; i++)
            k[i] = data[i];

        /* expand table */
        d = k[len - 1];
        j = 0;
        for (i = len; i < 128; i++, j++) {
            d = key_table[(k[j] + d) & 0xff];
            k[i] = d;
        }

        /* hmm.... key reduction to 'bits' bits */

        j = (bits + 7) >> 3;
        i = 128 - j;
        c = (0xff >> (-bits & 0x07));

        d = key_table[k[i] & c];
        k[i] = d;
        while (i--) {
            d = key_table[k[i + j] ^ d];
            k[i] = d;
        }

        /* copy from bytes into RC2_INT's */
        ki = &(key->data[63]);
        for (i = 127; i >= 0; i -= 2)
            *(ki--) = ((k[i] << 8) | k[i - 1]) & 0xffff;
    }

#if defined(_MSC_VER)
# pragma optimize("",on)
#endif

    void RC2_encrypt(unsigned long *d, RC2_KEY *key)
    {
        int i, n;
        register RC2_INT *p0, *p1;
        register RC2_INT x0, x1, x2, x3, t;
        unsigned long l;

        l = d[0];
        x0 = (RC2_INT) l & 0xffff;
        x1 = (RC2_INT) (l >> 16L);
        l = d[1];
        x2 = (RC2_INT) l & 0xffff;
        x3 = (RC2_INT) (l >> 16L);

        n = 3;
        i = 5;

        p0 = p1 = &(key->data[0]);
        for (;;) {
            t = (x0 + (x1 & ~x3) + (x2 & x3) + *(p0++)) & 0xffff;
            x0 = (t << 1) | (t >> 15);
            t = (x1 + (x2 & ~x0) + (x3 & x0) + *(p0++)) & 0xffff;
            x1 = (t << 2) | (t >> 14);
            t = (x2 + (x3 & ~x1) + (x0 & x1) + *(p0++)) & 0xffff;
            x2 = (t << 3) | (t >> 13);
            t = (x3 + (x0 & ~x2) + (x1 & x2) + *(p0++)) & 0xffff;
            x3 = (t << 5) | (t >> 11);

            if (--i == 0) {
                if (--n == 0)
                    break;
                i = (n == 2) ? 6 : 5;

                x0 += p1[x3 & 0x3f];
                x1 += p1[x0 & 0x3f];
                x2 += p1[x1 & 0x3f];
                x3 += p1[x2 & 0x3f];
            }
        }

        d[0] =
            (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L);
        d[1] =
            (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L);
    }

    void RC2_decrypt(unsigned long *d, RC2_KEY *key)
    {
        int i, n;
        register RC2_INT *p0, *p1;
        register RC2_INT x0, x1, x2, x3, t;
        unsigned long l;

        l = d[0];
        x0 = (RC2_INT) l & 0xffff;
        x1 = (RC2_INT) (l >> 16L);
        l = d[1];
        x2 = (RC2_INT) l & 0xffff;
        x3 = (RC2_INT) (l >> 16L);

        n = 3;
        i = 5;

        p0 = &(key->data[63]);
        p1 = &(key->data[0]);
        for (;;) {
            t = ((x3 << 11) | (x3 >> 5)) & 0xffff;
            x3 = (t - (x0 & ~x2) - (x1 & x2) - *(p0--)) & 0xffff;
            t = ((x2 << 13) | (x2 >> 3)) & 0xffff;
            x2 = (t - (x3 & ~x1) - (x0 & x1) - *(p0--)) & 0xffff;
            t = ((x1 << 14) | (x1 >> 2)) & 0xffff;
            x1 = (t - (x2 & ~x0) - (x3 & x0) - *(p0--)) & 0xffff;
            t = ((x0 << 15) | (x0 >> 1)) & 0xffff;
            x0 = (t - (x1 & ~x3) - (x2 & x3) - *(p0--)) & 0xffff;

            if (--i == 0) {
                if (--n == 0)
                    break;
                i = (n == 2) ? 6 : 5;

                x3 = (x3 - p1[x2 & 0x3f]) & 0xffff;
                x2 = (x2 - p1[x1 & 0x3f]) & 0xffff;
                x1 = (x1 - p1[x0 & 0x3f]) & 0xffff;
                x0 = (x0 - p1[x3 & 0x3f]) & 0xffff;
            }
        }

        d[0] =
            (unsigned long)(x0 & 0xffff) | ((unsigned long)(x1 & 0xffff) << 16L);
        d[1] =
            (unsigned long)(x2 & 0xffff) | ((unsigned long)(x3 & 0xffff) << 16L);
    }

    void RC2_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
                         RC2_KEY *ks, unsigned char *iv, int encrypt)
    {
        register unsigned long tin0, tin1;
        register unsigned long tout0, tout1, xor0, xor1;
        register long l = length;
        unsigned long tin[2];

        if (encrypt) {
            c2l(iv, tout0);
            c2l(iv, tout1);
            iv -= 8;
            for (l -= 8; l >= 0; l -= 8) {
                c2l(in, tin0);
                c2l(in, tin1);
                tin0 ^= tout0;
                tin1 ^= tout1;
                tin[0] = tin0;
                tin[1] = tin1;
                RC2_encrypt(tin, ks);
                tout0 = tin[0];
                l2c(tout0, out);
                tout1 = tin[1];
                l2c(tout1, out);
            }
            if (l != -8) {
                c2ln(in, tin0, tin1, l + 8);
                tin0 ^= tout0;
                tin1 ^= tout1;
                tin[0] = tin0;
                tin[1] = tin1;
                RC2_encrypt(tin, ks);
                tout0 = tin[0];
                l2c(tout0, out);
                tout1 = tin[1];
                l2c(tout1, out);
            }
            l2c(tout0, iv);
            l2c(tout1, iv);
        } else {
            c2l(iv, xor0);
            c2l(iv, xor1);
            iv -= 8;
            for (l -= 8; l >= 0; l -= 8) {
                c2l(in, tin0);
                tin[0] = tin0;
                c2l(in, tin1);
                tin[1] = tin1;
                RC2_decrypt(tin, ks);
                tout0 = tin[0] ^ xor0;
                tout1 = tin[1] ^ xor1;
                l2c(tout0, out);
                l2c(tout1, out);
                xor0 = tin0;
                xor1 = tin1;
            }
            if (l != -8) {
                c2l(in, tin0);
                tin[0] = tin0;
                c2l(in, tin1);
                tin[1] = tin1;
                RC2_decrypt(tin, ks);
                tout0 = tin[0] ^ xor0;
                tout1 = tin[1] ^ xor1;
                l2cn(tout0, tout1, out, l + 8);
                xor0 = tin0;
                xor1 = tin1;
            }
            l2c(xor0, iv);
            l2c(xor1, iv);
        }
        tin0 = tin1 = tout0 = tout1 = xor0 = xor1 = 0;
        tin[0] = tin[1] = 0;
    }

/*-
 * RC2 as implemented frm a posting from
 * Newsgroups: sci.crypt
 * Subject: Specification for Ron Rivests Cipher No.2
 * Message-ID: <4fk39f$f70@net.auckland.ac.nz>
 * Date: 11 Feb 1996 06:45:03 GMT
 */

    void RC2_ecb_encrypt(const unsigned char *in, unsigned char *out, RC2_KEY *ks,
                         int encrypt)
    {
        unsigned long l, d[2];

        c2l(in, l);
        d[0] = l;
        c2l(in, l);
        d[1] = l;
        if (encrypt)
            RC2_encrypt(d, ks);
        else
            RC2_decrypt(d, ks);
        l = d[0];
        l2c(l, out);
        l = d[1];
        l2c(l, out);
        l = d[0] = d[1] = 0;
    }

    // RC4 encryption
    // openssl-3.3.2v\crypto\rc4
    /*
     * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
     *
     * Licensed under the OpenSSL license (the "License").  You may not use
     * this file except in compliance with the License.  You can obtain a copy
     * in the file LICENSE in the source distribution or at
     * https://www.openssl.org/source/license.html
     */

#define RC4_INT dword

    typedef struct rc4_key_st
    {
        RC4_INT x, y;
        RC4_INT data[256];
    } RC4_KEY;

    void RC4_set_key(RC4_KEY *key, int len, const unsigned char *data)
    {
        register RC4_INT tmp;
        register int id1, id2;
        register RC4_INT *d;
        unsigned int i;

        d = &(key->data[0]);
        key->x = 0;
        key->y = 0;
        id1 = id2 = 0;

    #define SK_LOOP(d,n) { \
                    tmp=d[(n)]; \
                    id2 = (data[id1] + tmp + id2) & 0xff; \
                    if (++id1 == len) id1=0; \
                    d[(n)]=d[id2]; \
                    d[id2]=tmp; }

        for (i = 0; i < 256; i++)
            d[i] = i;
        for (i = 0; i < 256; i += 4) {
            SK_LOOP(d, i + 0);
            SK_LOOP(d, i + 1);
            SK_LOOP(d, i + 2);
            SK_LOOP(d, i + 3);
        }
    }

    /*-
     * RC4 as implemented from a posting from
     * Newsgroups: sci.crypt
     * Subject: RC4 Algorithm revealed.
     * Message-ID: <sternCvKL4B.Hyy@netcom.com>
     * Date: Wed, 14 Sep 1994 06:35:31 GMT
     */

    void RC4(RC4_KEY *key, size_t len, const unsigned char *indata,
             unsigned char *outdata)
    {
        register RC4_INT *d;
        register RC4_INT x, y, tx, ty;
        size_t i;

        x = key->x;
        y = key->y;
        d = key->data;

    #define LOOP(in,out) \
                    x=((x+1)&0xff); \
                    tx=d[x]; \
                    y=(tx+y)&0xff; \
                    d[x]=ty=d[y]; \
                    d[y]=tx; \
                    (out) = d[(tx+ty)&0xff]^ (in);

        i = len >> 3;
        if (i) {
            for (;;) {
                LOOP(indata[0], outdata[0]);
                LOOP(indata[1], outdata[1]);
                LOOP(indata[2], outdata[2]);
                LOOP(indata[3], outdata[3]);
                LOOP(indata[4], outdata[4]);
                LOOP(indata[5], outdata[5]);
                LOOP(indata[6], outdata[6]);
                LOOP(indata[7], outdata[7]);
                indata += 8;
                outdata += 8;
                if (--i == 0)
                    break;
            }
        }
        i = len & 0x07;
        if (i) {
            for (;;) {
                LOOP(indata[0], outdata[0]);
                if (--i == 0)
                    break;
                LOOP(indata[1], outdata[1]);
                if (--i == 0)
                    break;
                LOOP(indata[2], outdata[2]);
                if (--i == 0)
                    break;
                LOOP(indata[3], outdata[3]);
                if (--i == 0)
                    break;
                LOOP(indata[4], outdata[4]);
                if (--i == 0)
                    break;
                LOOP(indata[5], outdata[5]);
                if (--i == 0)
                    break;
                LOOP(indata[6], outdata[6]);
                if (--i == 0)
                    break;
            }
        }
        key->x = x;
        key->y = y;
    }

#undef register
}

/*
The implementation of AES in software is an adapted version of http://brgladman.org/oldsite/cryptography_technology/rijndael/aessrc.zip

Copyright (c) 1998-2006, Brian Gladman, Worcester, UK. All rights reserved.
LICENSE TERMS
The free distribution and use of this software in both source and binary
form is allowed (with or without changes) provided that:
  1. distributions of this source code include the above copyright
     notice, this list of conditions and the following disclaimer;
  2. distributions in binary form include the above copyright
     notice, this list of conditions and the following disclaimer
     in the documentation and/or other associated materials;
  3. the copyright holder's name is not used to endorse products
     built using this software without specific written permission.
ALTERNATIVELY, provided that this notice is retained in full, this product
may be distributed under the terms of the GNU General Public License (GPL),
in which case the provisions of the GPL apply INSTEAD OF those given above.
DISCLAIMER
This software is provided 'as is' with no explicit or implied warranties
in respect of its properties, including, but not limited to, correctness
and/or fitness for purpose.
*/

namespace _innovaphone_aes {

    typedef union
    {   
        dword l;
        byte b[4];
    } aes_inf;

     #define KS_LENGTH 60

    typedef struct
    {   
#if defined ARMV7_CRYPT || defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        __attribute__((aligned(16)))
#endif
                                     dword ks[KS_LENGTH];
        aes_inf inf;
    }  aes_encrypt_ctx;

    typedef struct
    {   
#if defined ARMV7_CRYPT || defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        __attribute__((aligned(16)))
#endif
                                     dword ks[KS_LENGTH];
        aes_inf inf;
    }  aes_decrypt_ctx;

    #define AES_BLOCK_SIZE  16  /* the AES block size in bytes          */
    #define RC_LENGTH   (5 * (AES_BLOCK_SIZE / 4 - 2))

    #define BFR_BLOCKS      8
    #define BFR_LENGTH  (BFR_BLOCKS * AES_BLOCK_SIZE)

 
#if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
    extern int aesni_set_key_glue(void* raw_context, const byte* in_key, unsigned int key_len);
    extern void aesni_enc_glue(void* raw_context, byte* out, const byte* in);
    extern void aesni_dec_glue(void* raw_context, byte* out, const byte* in);
    extern void aesni_ecb_enc_glue(void* raw_context, byte* out, const byte* in, size_t len);
    extern void aesni_ecb_dec_glue(void* raw_context, byte* out, const byte* in, size_t len);
    extern bool has_cpu_aesni;
#endif


#if defined ARMV7_CRYPT
    static int aesbs_setkey(const byte* in_key, unsigned int key_len, void* context);
    extern "C" void aesbs_ecb_encrypt(unsigned char* out, unsigned char const* in, const dword * rk, int rounds, int blocks);
    extern "C" void aesbs_ecb_decrypt(unsigned char* out, unsigned char const* in, const dword * rk, int rounds, int blocks);
    extern "C" void aesbs_cbc_decrypt(unsigned char* obuf, const unsigned char* ibuf, unsigned char* rk, int rounds, int blocks, unsigned char* iv);
    extern "C" void aesbs_ctr_encrypt(unsigned char* out, const unsigned char* const in, unsigned char* const rk, int rounds, int blocks, unsigned char* ctr, unsigned char* final);
    struct aesbs_ctx {
        int	rounds;
        byte	 __attribute__((aligned(16)))  rk[13 * (8 * 16) + 32];  // !!!__aligned(AES_BLOCK_SIZE);
    };
#else 
    static unsigned int t_rc[256];
    static unsigned int t_fn[4][256];
    static unsigned int t_fl[4][256];
    static unsigned int t_im[4][256];
    static unsigned int t_in[4][256];
    static unsigned int t_il[4][256];

    #define N_COLS           4  /* the number of columns in the state   */

    #define WPOLY   0x011b
    #define BPOLY     0x1b
    #define f2(x)   ((x<<1) ^ (((x>>7) & 1) * WPOLY))
    #define f4(x)   ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY))
    #define f8(x)   ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \
                            ^ (((x>>5) & 4) * WPOLY))
    #define f3(x)   (f2(x) ^ x)
    #define f9(x)   (f8(x) ^ x)
    #define fb(x)   (f8(x) ^ f2(x) ^ x)
    #define fd(x)   (f8(x) ^ f4(x) ^ x)
    #define fe(x)   (f8(x) ^ f4(x) ^ f2(x))

    #define fwd_affine(x) (w = (unsigned int)x, w ^= (w<<1)^(w<<2)^(w<<3)^(w<<4), 0x63^(unsigned char)(w^(w>>8)))
    #define inv_affine(x) (w = (unsigned int)x, w = (w<<1)^(w<<3)^(w<<6), 0x05^(unsigned char)(w^(w>>8)))

    #define vf1(x,r,c)  (x)
    #define rf1(r,c)    (r)
    #define rf2(r,c)    ((8+r-c)&3)
    #define bval(x,n)   ((unsigned char)((x) >> (8 * (n))))
    #define upr(x,n)    (((unsigned int)(x) << (8 * (n))) | ((unsigned int)(x) >> (32 - 8 * (n))))

    #define four_tables(x,tab,vf,rf,c) \
     (  tab[0][bval(vf(x,0,c),rf(0,c))] \
      ^ tab[1][bval(vf(x,1,c),rf(1,c))] \
      ^ tab[2][bval(vf(x,2,c),rf(2,c))] \
      ^ tab[3][bval(vf(x,3,c),rf(3,c))])

    #define t_set(m,n) t_##m##n
    #define t_use(m,n) t_##m##n
    #define ls_box(x,c) four_tables(x,t_use(f,l),vf1,rf2,c)

    #define key_ofs     0
    #define rnd_key(n)  (kp + n * N_COLS)

    #define bytes2word(b0, b1, b2, b3) (((unsigned int)(b3) << 24) | ((unsigned int)(b2) << 16) | ((unsigned int)(b1) << 8) | (b0))

    #define word_in(x,c) bytes2word(((const unsigned char*)(x)+4*c)[0], ((const unsigned char*)(x)+4*c)[1], ((const unsigned char*)(x)+4*c)[2], ((const unsigned char*)(x)+4*c)[3])
    #define word_out(x,c,v) { ((unsigned char*)(x)+4*c)[0] = bval(v,0); ((unsigned char*)(x)+4*c)[1] = bval(v,1); \
                              ((unsigned char*)(x)+4*c)[2] = bval(v,2); ((unsigned char*)(x)+4*c)[3] = bval(v,3); }

    #define ke4(k,i) \
    {   k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \
        k[4*(i)+5] = ss[1] ^= ss[0]; \
        k[4*(i)+6] = ss[2] ^= ss[1]; \
        k[4*(i)+7] = ss[3] ^= ss[2]; \
    }

    #define kef6(k,i) \
    {   k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \
        k[6*(i)+ 7] = ss[1] ^= ss[0]; \
        k[6*(i)+ 8] = ss[2] ^= ss[1]; \
        k[6*(i)+ 9] = ss[3] ^= ss[2]; \
    }

    #define ke6(k,i) \
    {   kef6(k,i); \
        k[6*(i)+10] = ss[4] ^= ss[3]; \
        k[6*(i)+11] = ss[5] ^= ss[4]; \
    }

    #define kef8(k,i) \
    {   k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \
        k[8*(i)+ 9] = ss[1] ^= ss[0]; \
        k[8*(i)+10] = ss[2] ^= ss[1]; \
        k[8*(i)+11] = ss[3] ^= ss[2]; \
    }

    #define ke8(k,i) \
    {   kef8(k,i); \
        k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); \
        k[8*(i)+13] = ss[5] ^= ss[4]; \
        k[8*(i)+14] = ss[6] ^= ss[5]; \
        k[8*(i)+15] = ss[7] ^= ss[6]; \
    }

    static void aes_encrypt_key_256(const unsigned char *key, aes_encrypt_ctx cx[1])
    {   
        dword ss[8];

        cx->ks[0] = ss[0] = word_in(key, 0);
        cx->ks[1] = ss[1] = word_in(key, 1);
        cx->ks[2] = ss[2] = word_in(key, 2);
        cx->ks[3] = ss[3] = word_in(key, 3);
        cx->ks[4] = ss[4] = word_in(key, 4);
        cx->ks[5] = ss[5] = word_in(key, 5);
        cx->ks[6] = ss[6] = word_in(key, 6);
        cx->ks[7] = ss[7] = word_in(key, 7);

        ke8(cx->ks, 0); ke8(cx->ks, 1);
        ke8(cx->ks, 2); ke8(cx->ks, 3);
        ke8(cx->ks, 4); ke8(cx->ks, 5);
        kef8(cx->ks, 6);
        cx->inf.l = 0;
        cx->inf.b[0] = 14 * 16;
    }

    static void aes_encrypt_key_192(const unsigned char *key, aes_encrypt_ctx cx[1])
    {   
        dword ss[6];

        cx->ks[0] = ss[0] = word_in(key, 0);
        cx->ks[1] = ss[1] = word_in(key, 1);
        cx->ks[2] = ss[2] = word_in(key, 2);
        cx->ks[3] = ss[3] = word_in(key, 3);
        cx->ks[4] = ss[4] = word_in(key, 4);
        cx->ks[5] = ss[5] = word_in(key, 5);

        ke6(cx->ks, 0);  ke6(cx->ks, 1);
        ke6(cx->ks, 2);  ke6(cx->ks, 3);
        ke6(cx->ks, 4);  ke6(cx->ks, 5);
        ke6(cx->ks, 6);
        kef6(cx->ks, 7);
        cx->inf.l = 0;
        cx->inf.b[0] = 12 * 16;
    }

    static void aes_encrypt_key_128(const unsigned char *key, aes_encrypt_ctx cx[1])
    {   
        dword ss[4];

        cx->ks[0] = ss[0] = word_in(key, 0);
        cx->ks[1] = ss[1] = word_in(key, 1);
        cx->ks[2] = ss[2] = word_in(key, 2);
        cx->ks[3] = ss[3] = word_in(key, 3);

        ke4(cx->ks, 0);  ke4(cx->ks, 1);
        ke4(cx->ks, 2);  ke4(cx->ks, 3);
        ke4(cx->ks, 4);  ke4(cx->ks, 5);
        ke4(cx->ks, 6);  ke4(cx->ks, 7);
        ke4(cx->ks, 8);

        ke4(cx->ks, 9);
        cx->inf.l = 0;
        cx->inf.b[0] = 10 * 16;
    }

#define v(n,i)  ((n) - (i) + 2 * ((i) & 3))
#define inv_mcol(x)       four_tables(x,t_use(i,m),vf1,rf1,0)
#define ff(x)   inv_mcol(x)

#define k4e(k,i) \
    {   k[v(40, (4 * (i)) + 4)] = ss[0] ^= ls_box(ss[3], 3) ^ t_use(r, c)[i]; \
    k[v(40, (4 * (i)) + 5)] = ss[1] ^= ss[0]; \
    k[v(40, (4 * (i)) + 6)] = ss[2] ^= ss[1]; \
    k[v(40, (4 * (i)) + 7)] = ss[3] ^= ss[2]; \
    }

#define kdf4(k,i) \
    {   ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \
    ss[1] = ss[1] ^ ss[3]; \
    ss[2] = ss[2] ^ ss[3]; \
    ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \
    ss[i % 4] ^= ss[4]; \
    ss[4] ^= k[v(40, (4 * (i)))];   k[v(40, (4 * (i)) + 4)] = ff(ss[4]); \
    ss[4] ^= k[v(40, (4 * (i)) + 1)]; k[v(40, (4 * (i)) + 5)] = ff(ss[4]); \
    ss[4] ^= k[v(40, (4 * (i)) + 2)]; k[v(40, (4 * (i)) + 6)] = ff(ss[4]); \
    ss[4] ^= k[v(40, (4 * (i)) + 3)]; k[v(40, (4 * (i)) + 7)] = ff(ss[4]); \
    }

#define kd4(k,i) \
    {   ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; \
    ss[i % 4] ^= ss[4]; ss[4] = ff(ss[4]); \
    k[v(40, (4 * (i)) + 4)] = ss[4] ^= k[v(40, (4 * (i)))]; \
    k[v(40, (4 * (i)) + 5)] = ss[4] ^= k[v(40, (4 * (i)) + 1)]; \
    k[v(40, (4 * (i)) + 6)] = ss[4] ^= k[v(40, (4 * (i)) + 2)]; \
    k[v(40, (4 * (i)) + 7)] = ss[4] ^= k[v(40, (4 * (i)) + 3)]; \
    }

#define kdl4(k,i) \
    {   ss[4] = ls_box(ss[(i + 3) % 4], 3) ^ t_use(r, c)[i]; ss[i % 4] ^= ss[4]; \
    k[v(40, (4 * (i)) + 4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \
    k[v(40, (4 * (i)) + 5)] = ss[1] ^ ss[3]; \
    k[v(40, (4 * (i)) + 6)] = ss[0]; \
    k[v(40, (4 * (i)) + 7)] = ss[1]; \
    }

    static void aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1])
    {
        dword ss[5];

        cx->ks[v(40, (0))] = ss[0] = word_in(key, 0);
        cx->ks[v(40, (1))] = ss[1] = word_in(key, 1);
        cx->ks[v(40, (2))] = ss[2] = word_in(key, 2);
        cx->ks[v(40, (3))] = ss[3] = word_in(key, 3);

        kdf4(cx->ks, 0); kd4(cx->ks, 1);
        kd4(cx->ks, 2); kd4(cx->ks, 3);
        kd4(cx->ks, 4); kd4(cx->ks, 5);
        kd4(cx->ks, 6); kd4(cx->ks, 7);
        kd4(cx->ks, 8); kdl4(cx->ks, 9);

        cx->inf.l = 0;
        cx->inf.b[0] = 10 * 16;
    }

#define k6ef(k,i) \
    {   k[v(48, (6 * (i)) + 6)] = ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
    k[v(48, (6 * (i)) + 7)] = ss[1] ^= ss[0]; \
    k[v(48, (6 * (i)) + 8)] = ss[2] ^= ss[1]; \
    k[v(48, (6 * (i)) + 9)] = ss[3] ^= ss[2]; \
    }

#define k6e(k,i) \
    {   k6ef(k, i); \
    k[v(48, (6 * (i)) + 10)] = ss[4] ^= ss[3]; \
    k[v(48, (6 * (i)) + 11)] = ss[5] ^= ss[4]; \
    }

#define kdf6(k,i) \
    {   ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; k[v(48, (6 * (i)) + 6)] = ff(ss[0]); \
    ss[1] ^= ss[0]; k[v(48, (6 * (i)) + 7)] = ff(ss[1]); \
    ss[2] ^= ss[1]; k[v(48, (6 * (i)) + 8)] = ff(ss[2]); \
    ss[3] ^= ss[2]; k[v(48, (6 * (i)) + 9)] = ff(ss[3]); \
    ss[4] ^= ss[3]; k[v(48, (6 * (i)) + 10)] = ff(ss[4]); \
    ss[5] ^= ss[4]; k[v(48, (6 * (i)) + 11)] = ff(ss[5]); \
    }

#define kd6(k,i) \
    {   ss[6] = ls_box(ss[5], 3) ^ t_use(r, c)[i]; \
    ss[0] ^= ss[6]; ss[6] = ff(ss[6]); k[v(48, (6 * (i)) + 6)] = ss[6] ^= k[v(48, (6 * (i)))]; \
    ss[1] ^= ss[0]; k[v(48, (6 * (i)) + 7)] = ss[6] ^= k[v(48, (6 * (i)) + 1)]; \
    ss[2] ^= ss[1]; k[v(48, (6 * (i)) + 8)] = ss[6] ^= k[v(48, (6 * (i)) + 2)]; \
    ss[3] ^= ss[2]; k[v(48, (6 * (i)) + 9)] = ss[6] ^= k[v(48, (6 * (i)) + 3)]; \
    ss[4] ^= ss[3]; k[v(48, (6 * (i)) + 10)] = ss[6] ^= k[v(48, (6 * (i)) + 4)]; \
    ss[5] ^= ss[4]; k[v(48, (6 * (i)) + 11)] = ss[6] ^= k[v(48, (6 * (i)) + 5)]; \
    }

#define kdl6(k,i) \
    {   ss[0] ^= ls_box(ss[5], 3) ^ t_use(r, c)[i]; k[v(48, (6 * (i)) + 6)] = ss[0]; \
    ss[1] ^= ss[0]; k[v(48, (6 * (i)) + 7)] = ss[1]; \
    ss[2] ^= ss[1]; k[v(48, (6 * (i)) + 8)] = ss[2]; \
    ss[3] ^= ss[2]; k[v(48, (6 * (i)) + 9)] = ss[3]; \
    }

    static void aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1])
    {
        dword ss[7];

        cx->ks[v(48, (0))] = ss[0] = word_in(key, 0);
        cx->ks[v(48, (1))] = ss[1] = word_in(key, 1);
        cx->ks[v(48, (2))] = ss[2] = word_in(key, 2);
        cx->ks[v(48, (3))] = ss[3] = word_in(key, 3);
        ss[4] = word_in(key, 4);
        ss[5] = word_in(key, 5);
        cx->ks[v(48, (4))] = ff(ss[4]);
        cx->ks[v(48, (5))] = ff(ss[5]);
        kdf6(cx->ks, 0); kd6(cx->ks, 1);
        kd6(cx->ks, 2);  kd6(cx->ks, 3);
        kd6(cx->ks, 4);  kd6(cx->ks, 5);
        kd6(cx->ks, 6); kdl6(cx->ks, 7);
        cx->inf.l = 0;
        cx->inf.b[0] = 12 * 16;
    }

#define k8ef(k,i) \
    {   k[v(56, (8 * (i)) + 8)] = ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
    k[v(56, (8 * (i)) + 9)] = ss[1] ^= ss[0]; \
    k[v(56, (8 * (i)) + 10)] = ss[2] ^= ss[1]; \
    k[v(56, (8 * (i)) + 11)] = ss[3] ^= ss[2]; \
    }

#define k8e(k,i) \
    {   k8ef(k, i); \
    k[v(56, (8 * (i)) + 12)] = ss[4] ^= ls_box(ss[3], 0); \
    k[v(56, (8 * (i)) + 13)] = ss[5] ^= ss[4]; \
    k[v(56, (8 * (i)) + 14)] = ss[6] ^= ss[5]; \
    k[v(56, (8 * (i)) + 15)] = ss[7] ^= ss[6]; \
    }

#define kdf8(k,i) \
    {   ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; k[v(56, (8 * (i)) + 8)] = ff(ss[0]); \
    ss[1] ^= ss[0]; k[v(56, (8 * (i)) + 9)] = ff(ss[1]); \
    ss[2] ^= ss[1]; k[v(56, (8 * (i)) + 10)] = ff(ss[2]); \
    ss[3] ^= ss[2]; k[v(56, (8 * (i)) + 11)] = ff(ss[3]); \
    ss[4] ^= ls_box(ss[3], 0); k[v(56, (8 * (i)) + 12)] = ff(ss[4]); \
    ss[5] ^= ss[4]; k[v(56, (8 * (i)) + 13)] = ff(ss[5]); \
    ss[6] ^= ss[5]; k[v(56, (8 * (i)) + 14)] = ff(ss[6]); \
    ss[7] ^= ss[6]; k[v(56, (8 * (i)) + 15)] = ff(ss[7]); \
    }

#define kd8(k,i) \
    {   ss[8] = ls_box(ss[7], 3) ^ t_use(r, c)[i]; \
    ss[0] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56, (8 * (i)) + 8)] = ss[8] ^= k[v(56, (8 * (i)))]; \
    ss[1] ^= ss[0]; k[v(56, (8 * (i)) + 9)] = ss[8] ^= k[v(56, (8 * (i)) + 1)]; \
    ss[2] ^= ss[1]; k[v(56, (8 * (i)) + 10)] = ss[8] ^= k[v(56, (8 * (i)) + 2)]; \
    ss[3] ^= ss[2]; k[v(56, (8 * (i)) + 11)] = ss[8] ^= k[v(56, (8 * (i)) + 3)]; \
    ss[8] = ls_box(ss[3], 0); \
    ss[4] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56, (8 * (i)) + 12)] = ss[8] ^= k[v(56, (8 * (i)) + 4)]; \
    ss[5] ^= ss[4]; k[v(56, (8 * (i)) + 13)] = ss[8] ^= k[v(56, (8 * (i)) + 5)]; \
    ss[6] ^= ss[5]; k[v(56, (8 * (i)) + 14)] = ss[8] ^= k[v(56, (8 * (i)) + 6)]; \
    ss[7] ^= ss[6]; k[v(56, (8 * (i)) + 15)] = ss[8] ^= k[v(56, (8 * (i)) + 7)]; \
    }

#define kdl8(k,i) \
    {   ss[0] ^= ls_box(ss[7], 3) ^ t_use(r, c)[i]; k[v(56, (8 * (i)) + 8)] = ss[0]; \
    ss[1] ^= ss[0]; k[v(56, (8 * (i)) + 9)] = ss[1]; \
    ss[2] ^= ss[1]; k[v(56, (8 * (i)) + 10)] = ss[2]; \
    ss[3] ^= ss[2]; k[v(56, (8 * (i)) + 11)] = ss[3]; \
    }

    static void aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1])
    {
        dword ss[9];
        cx->ks[v(56, (0))] = ss[0] = word_in(key, 0);
        cx->ks[v(56, (1))] = ss[1] = word_in(key, 1);
        cx->ks[v(56, (2))] = ss[2] = word_in(key, 2);
        cx->ks[v(56, (3))] = ss[3] = word_in(key, 3);
        ss[4] = word_in(key, 4);
        ss[5] = word_in(key, 5);
        ss[6] = word_in(key, 6);
        ss[7] = word_in(key, 7);
        cx->ks[v(56, (4))] = ff(ss[4]);
        cx->ks[v(56, (5))] = ff(ss[5]);
        cx->ks[v(56, (6))] = ff(ss[6]);
        cx->ks[v(56, (7))] = ff(ss[7]);
        kdf8(cx->ks, 0); kd8(cx->ks, 1);
        kd8(cx->ks, 2);  kd8(cx->ks, 3);
        kd8(cx->ks, 4);  kd8(cx->ks, 5);
        kdl8(cx->ks, 6);
        cx->inf.l = 0;
        cx->inf.b[0] = 14 * 16;
    }

    static void aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1])
    {
        switch (key_len)
        {
        case 16: case 128: aes_encrypt_key_128(key, cx); return;
        case 24: case 192: aes_encrypt_key_192(key, cx); return;
        case 32: case 256: aes_encrypt_key_256(key, cx); return;
        }
    }

    static void aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1])
    {
        switch (key_len)
        {
        case 16: case 128: aes_decrypt_key128(key, cx); return;
        case 24: case 192: aes_decrypt_key192(key, cx); return;
        case 32: case 256: aes_decrypt_key256(key, cx); return;
        }
    }

    static void increase_iv(byte * iv)
    {
        for (int i = 15; i >= 0; i--)
        {
            if (++iv[i] != 0) return;
        }
    }

    #define s(x,c) x[c]
    #define si(y,x,k,c) (s(y,c) = word_in(x, c) ^ (k)[c])
    #define so(y,x,c)   word_out(y, c, s(x,c))

    #define l_copy(y, x)    s(y,0) = s(x,0); s(y,1) = s(x,1); \
                            s(y,2) = s(x,2); s(y,3) = s(x,3);
    #define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3)
    #define state_out(y,x)  so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3)
    #define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3)

    #define locals(y,x)     x[4]={0},y[4]
    #define fwd_var(x,r,c)\
     ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
     : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\
     : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
     :          ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2)))
    #define fwd_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c))
    #define fwd_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,l),fwd_var,rf1,c))
    #define inv_var(x,r,c)\
     ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\
     : r == 1 ? ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))\
     : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\
     :          ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0)))
    #define inv_rnd(y,x,k,c)    (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,n),inv_var,rf1,c))
    #define inv_lrnd(y,x,k,c)   (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,l),inv_var,rf1,c))
#endif

    static void inno_aes_encrypt(const unsigned char* in, unsigned char* out, const aes_encrypt_ctx cx[1])
    {
#if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (_innovaphone_aes::has_cpu_aesni) {
            void* ctx1 = (void*)cx;
            _innovaphone_aes::aesni_ecb_enc_glue(ctx1, out, in, 16);
            return;
        }
#endif

#if defined ARMV7_CRYPT   
        const aesbs_ctx* ctx1 = (const aesbs_ctx*)cx;
        aesbs_ecb_encrypt(out, in, (const dword*)ctx1->rk, ctx1->rounds, 1);
#else
        unsigned int locals(b0, b1);
        const unsigned int *kp;

        kp = cx->ks;
        state_in(b0, in, kp);

        {   
            unsigned int rnd;
            for(rnd = 0; rnd < (unsigned int)((cx->inf.b[0] >> 4) - 1); ++rnd)
            {
                kp += N_COLS;
                round(fwd_rnd, b1, b0, kp);
                l_copy(b0, b1);
            }
            kp += N_COLS;
            round(fwd_lrnd, b0, b1, kp);
        }
        state_out(out, b0);
#endif
    }

    static void inno_aes_decrypt(const unsigned char* in, unsigned char* out, const aes_decrypt_ctx cx[1])
    {
#if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            void* ctx1 = (void*)cx;
            _innovaphone_aes::aesni_ecb_dec_glue(ctx1, out, in, 16);
            return;
        }
#endif

#if defined ARMV7_CRYPT   
        const aesbs_ctx* ctx1 = (const aesbs_ctx*)cx;
        aesbs_ecb_decrypt(out, in, (const dword*)ctx1->rk, ctx1->rounds, 1);
#else
        
        unsigned int locals(b0, b1);
        const unsigned int *kp;

        if( cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16 ) return;

        kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0);
        state_in(b0, in, kp);

        {   
            unsigned int rnd;
            for(rnd = 0; rnd < (unsigned int)((cx->inf.b[0] >> 4) - 1); ++rnd)
            {
                kp = rnd_key(1);
                round(inv_rnd, b1, b0, kp);
                l_copy(b0, b1);
            }
            kp = rnd_key(1);
            round(inv_lrnd, b0, b1, kp);
        }
        state_out(out, b0);
#endif
    }
#if defined ARMV8_CRYPT   
    extern "C" void aes_v8_ecb_encrypt(unsigned char const *in, unsigned char *out, int len, const dword *rk, int enc, int rounds);
#endif

    static void aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, int len, const aes_encrypt_ctx ctx[1] )
    {   

        if(len & (AES_BLOCK_SIZE - 1)) return;
#if defined ARMV8_CRYPT 
        unsigned int rounds= (unsigned int) (ctx->inf.b[0] >> 4);
        aes_v8_ecb_encrypt(ibuf, obuf, len, (const dword*)ctx, 1,rounds);
#elif defined ARMV7_CRYPT 
        const aesbs_ctx* ctx1 = (const aesbs_ctx*)ctx;
        aesbs_ecb_encrypt(obuf, ibuf, (const dword*)ctx1->rk, ctx1->rounds, len >> 4);
#else
        int nb = len >> 4;
        while(nb--)
        {
            inno_aes_encrypt(ibuf, obuf, ctx);
            ibuf += AES_BLOCK_SIZE;
            obuf += AES_BLOCK_SIZE;
        }
#endif
    }

    static void aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, int len, const aes_decrypt_ctx ctx[1])
    {   

        if(len & (AES_BLOCK_SIZE - 1)) return;
#if defined ARMV8_CRYPT
        unsigned int rounds= (unsigned int) (ctx->inf.b[0] >> 4);
        aes_v8_ecb_encrypt(ibuf, obuf, len, (const dword*)ctx, 0,rounds);
#elif defined ARMV7_CRYPT 
        const aesbs_ctx* ctx1 = (const aesbs_ctx*)ctx;
        aesbs_ecb_decrypt(obuf, ibuf, (const dword*)ctx1->rk, ctx1->rounds, len >> 4);
#else
        int nb = len >> 4;
        while(nb--)
        {
            inno_aes_decrypt(ibuf, obuf, ctx);
            ibuf += AES_BLOCK_SIZE;
            obuf += AES_BLOCK_SIZE;
        }
#endif
    }

    static void aes_reset_mode(aes_encrypt_ctx ctx[1])
    {
#if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            return;
        }
#endif

#if defined ARMV7_CRYPT
#else
        ctx->inf.b[2] = 0;
#endif
    }

    static void aes_reset_mode(aes_decrypt_ctx ctx[1])
    {
#if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            return;
    }
#endif

#if defined ARMV7_CRYPT
#else
        ctx->inf.b[2] = 0;
#endif
    }

    static void aes_ctr_encrypt(const unsigned char* ibuf, unsigned char* obuf, int len, unsigned char* cbuf, aes_encrypt_ctx ctx[1])
    {
#if defined ARMV7_CRYPT 
        if (!len) return;
        int remainder = len % 16;
        int complete = len - remainder;
        byte tmp[16];
        const aesbs_ctx* ctx1 = (const aesbs_ctx*)ctx;
        aesbs_ctr_encrypt(obuf, ibuf, (byte*)ctx1->rk, ctx1->rounds, complete >> 4, cbuf, remainder ? tmp : 0);
        if (remainder) {
            for (int i = 0; i < remainder; i++) obuf[complete + i] = ibuf[complete + i] ^ tmp[i];
        }
#else
        unsigned char* ip;
        int i, blen, bPos = (int)(ctx->inf.b[2]);

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            bPos = 0;
    }
#   endif

        unsigned char buf[BFR_LENGTH];

        if(bPos) {
            memcpy(buf, cbuf, AES_BLOCK_SIZE);
            aes_ecb_encrypt(buf, buf, AES_BLOCK_SIZE, ctx);
            while(bPos < AES_BLOCK_SIZE && len)
                *obuf++ = *ibuf++ ^ buf[bPos++], --len;
            if(len) 
                increase_iv(cbuf), bPos = 0;
        }

        while(len) {
            blen = (len > BFR_LENGTH ? BFR_LENGTH : len), len -= blen;

            for(i = 0, ip = buf; i < (blen >> 4); ++i)
            {
                memcpy(ip, cbuf, AES_BLOCK_SIZE);
                increase_iv(cbuf);
                ip += AES_BLOCK_SIZE;
            }

            if(blen & (AES_BLOCK_SIZE - 1))
                memcpy(ip, cbuf, AES_BLOCK_SIZE), i++;

            aes_ecb_encrypt(buf, buf, i * AES_BLOCK_SIZE, ctx);

            i = 0; ip = buf;
                while(i + AES_BLOCK_SIZE <= blen)
                {
                    obuf[ 0] = ibuf[ 0] ^ ip[ 0]; obuf[ 1] = ibuf[ 1] ^ ip[ 1];
                    obuf[ 2] = ibuf[ 2] ^ ip[ 2]; obuf[ 3] = ibuf[ 3] ^ ip[ 3];
                    obuf[ 4] = ibuf[ 4] ^ ip[ 4]; obuf[ 5] = ibuf[ 5] ^ ip[ 5];
                    obuf[ 6] = ibuf[ 6] ^ ip[ 6]; obuf[ 7] = ibuf[ 7] ^ ip[ 7];
                    obuf[ 8] = ibuf[ 8] ^ ip[ 8]; obuf[ 9] = ibuf[ 9] ^ ip[ 9];
                    obuf[10] = ibuf[10] ^ ip[10]; obuf[11] = ibuf[11] ^ ip[11];
                    obuf[12] = ibuf[12] ^ ip[12]; obuf[13] = ibuf[13] ^ ip[13];
                    obuf[14] = ibuf[14] ^ ip[14]; obuf[15] = ibuf[15] ^ ip[15];
                    i += AES_BLOCK_SIZE;
                    ip += AES_BLOCK_SIZE;
                    ibuf += AES_BLOCK_SIZE;
                    obuf += AES_BLOCK_SIZE;
                }
            while(i++ < blen) {
                *obuf++ = *ibuf++ ^ ip[bPos++];
            }
        }

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            return;
        }
#   endif

        ctx->inf.b[2] = bPos;
#endif
    }

    static void aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, int len, unsigned char *iv, const aes_encrypt_ctx ctx[1])
    {
        int nb = len >> 4;
        //ASSERT(!(len & (AES_BLOCK_SIZE - 1)), "aes_cbc_encrypt - invalid block size");

        while (nb--)
        {
            iv[0] ^= ibuf[0]; iv[1] ^= ibuf[1];
            iv[2] ^= ibuf[2]; iv[3] ^= ibuf[3];
            iv[4] ^= ibuf[4]; iv[5] ^= ibuf[5];
            iv[6] ^= ibuf[6]; iv[7] ^= ibuf[7];
            iv[8] ^= ibuf[8]; iv[9] ^= ibuf[9];
            iv[10] ^= ibuf[10]; iv[11] ^= ibuf[11];
            iv[12] ^= ibuf[12]; iv[13] ^= ibuf[13];
            iv[14] ^= ibuf[14]; iv[15] ^= ibuf[15];
            inno_aes_encrypt(iv, iv, ctx);
            memcpy(obuf, iv, AES_BLOCK_SIZE);
            ibuf += AES_BLOCK_SIZE;
            obuf += AES_BLOCK_SIZE;
        }
    }

    static void aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, int len, unsigned char *iv, const aes_decrypt_ctx ctx[1])
    {
        unsigned char tmp[AES_BLOCK_SIZE];
        int nb = len >> 4;
        //ASSERT(!(len & (AES_BLOCK_SIZE - 1)), "aes_cbc_decrypt - invalid block size");

        while (nb--)
        {
            memcpy(tmp, ibuf, AES_BLOCK_SIZE);
            inno_aes_decrypt(ibuf, obuf, ctx);
            obuf[0] ^= iv[0]; obuf[1] ^= iv[1];
            obuf[2] ^= iv[2]; obuf[3] ^= iv[3];
            obuf[4] ^= iv[4]; obuf[5] ^= iv[5];
            obuf[6] ^= iv[6]; obuf[7] ^= iv[7];
            obuf[8] ^= iv[8]; obuf[9] ^= iv[9];
            obuf[10] ^= iv[10]; obuf[11] ^= iv[11];
            obuf[12] ^= iv[12]; obuf[13] ^= iv[13];
            obuf[14] ^= iv[14]; obuf[15] ^= iv[15];
            memcpy(iv, tmp, AES_BLOCK_SIZE);
            ibuf += AES_BLOCK_SIZE;
            obuf += AES_BLOCK_SIZE;
        }
    }

    static void aes_cfb_encrypt(const unsigned char* ibuf, unsigned char* obuf, int len, unsigned char* iv, aes_encrypt_ctx ctx[1])
    {
        int cnt = 0;
#if defined ARMV7_CRYPT
        int b_pos = 0;
#else
        int b_pos = (int)ctx->inf.b[2];

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            b_pos = 0;
        }
#   endif

#endif
        int nb;

        if (b_pos)           /* complete any partial block   */
        {
            while (b_pos < AES_BLOCK_SIZE && cnt < len)
                *obuf++ = iv[b_pos++] ^= *ibuf++, cnt++;

            b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
        }

        if ((nb = (len - cnt) >> 4) != 0)    /* process whole blocks */
        {
            while (cnt + AES_BLOCK_SIZE <= len)
            {
                //assert(b_pos == 0);
                inno_aes_encrypt(iv, iv, ctx);
                obuf[0] = iv[0] ^= ibuf[0]; obuf[1] = iv[1] ^= ibuf[1];
                obuf[2] = iv[2] ^= ibuf[2]; obuf[3] = iv[3] ^= ibuf[3];
                obuf[4] = iv[4] ^= ibuf[4]; obuf[5] = iv[5] ^= ibuf[5];
                obuf[6] = iv[6] ^= ibuf[6]; obuf[7] = iv[7] ^= ibuf[7];
                obuf[8] = iv[8] ^= ibuf[8]; obuf[9] = iv[9] ^= ibuf[9];
                obuf[10] = iv[10] ^= ibuf[10]; obuf[11] = iv[11] ^= ibuf[11];
                obuf[12] = iv[12] ^= ibuf[12]; obuf[13] = iv[13] ^= ibuf[13];
                obuf[14] = iv[14] ^= ibuf[14]; obuf[15] = iv[15] ^= ibuf[15];
                ibuf += AES_BLOCK_SIZE;
                obuf += AES_BLOCK_SIZE;
                cnt += AES_BLOCK_SIZE;
            }
        }

        while (cnt < len)
        {
            if (!b_pos)
                aes_ecb_encrypt(iv, iv, AES_BLOCK_SIZE, ctx);

            while (cnt < len && b_pos < AES_BLOCK_SIZE)
                *obuf++ = iv[b_pos++] ^= *ibuf++, cnt++;

            b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
        }

#if defined ARMV7_CRYPT
#else

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            return;
        }
#   endif

        ctx->inf.b[2] = b_pos;
#endif
    }

    static void aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, int len, unsigned char *iv, aes_encrypt_ctx ctx[1])
    {
        int cnt = 0;
#if defined ARMV7_CRYPT
        int b_pos = 0;
#else
        int b_pos = (int)ctx->inf.b[2];

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            b_pos = 0;
        }
#   endif

#endif
        int nb;

        if (b_pos)           /* complete any partial block   */
        {
            unsigned char t;

            while (b_pos < AES_BLOCK_SIZE && cnt < len)
                t = *ibuf++, *obuf++ = t ^ iv[b_pos], iv[b_pos++] = t, cnt++;

            b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
        }

        if ((nb = (len - cnt) >> 4) != 0)    /* process whole blocks */
        {
            while (cnt + AES_BLOCK_SIZE <= len)
            {
                unsigned char t;
                //assert(b_pos == 0);
                inno_aes_encrypt(iv, iv, ctx);
                t = ibuf[0], obuf[0] = t ^ iv[0], iv[0] = t;
                t = ibuf[1], obuf[1] = t ^ iv[1], iv[1] = t;
                t = ibuf[2], obuf[2] = t ^ iv[2], iv[2] = t;
                t = ibuf[3], obuf[3] = t ^ iv[3], iv[3] = t;
                t = ibuf[4], obuf[4] = t ^ iv[4], iv[4] = t;
                t = ibuf[5], obuf[5] = t ^ iv[5], iv[5] = t;
                t = ibuf[6], obuf[6] = t ^ iv[6], iv[6] = t;
                t = ibuf[7], obuf[7] = t ^ iv[7], iv[7] = t;
                t = ibuf[8], obuf[8] = t ^ iv[8], iv[8] = t;
                t = ibuf[9], obuf[9] = t ^ iv[9], iv[9] = t;
                t = ibuf[10], obuf[10] = t ^ iv[10], iv[10] = t;
                t = ibuf[11], obuf[11] = t ^ iv[11], iv[11] = t;
                t = ibuf[12], obuf[12] = t ^ iv[12], iv[12] = t;
                t = ibuf[13], obuf[13] = t ^ iv[13], iv[13] = t;
                t = ibuf[14], obuf[14] = t ^ iv[14], iv[14] = t;
                t = ibuf[15], obuf[15] = t ^ iv[15], iv[15] = t;
                ibuf += AES_BLOCK_SIZE;
                obuf += AES_BLOCK_SIZE;
                cnt += AES_BLOCK_SIZE;
            }
        }

        while (cnt < len)
        {
            unsigned char t;

            if (!b_pos)
                aes_ecb_encrypt(iv, iv, AES_BLOCK_SIZE, ctx);

            while (cnt < len && b_pos < AES_BLOCK_SIZE)
                t = *ibuf++, *obuf++ = t ^ iv[b_pos], iv[b_pos++] = t, cnt++;

            b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos);
        }

#if defined ARMV7_CRYPT
#else

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (has_cpu_aesni) {
            return;
        }
#   endif

        ctx->inf.b[2] = b_pos;
#endif
    }

#if !defined ARMV7_CRYPT

    static unsigned char hibit(const unsigned int x)
    {   
        unsigned char r = (unsigned char)((x >> 1) | (x >> 2));

        r |= (r >> 2);
        r |= (r >> 4);
        return (r + 1) >> 1;
    }
    static unsigned char fi(const unsigned char x)
    {   
        unsigned char p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0;

        if(x < 2) return x;

        for(;;)
        {
            if(!n1) return v1;

            while(n2 >= n1)
            {
                n2 /= n1; p2 ^= p1 * n2; v2 ^= v1 * n2; n2 = hibit(p2);
            }

            if(!n2) return v2;

            while(n1 >= n2)
            {
                n1 /= n2; p1 ^= p2 * n1; v1 ^= v2 * n1; n1 = hibit(p1);
            }
        }
    }
    static void gen_tables(void)
    {   
        unsigned int i, w;
        static bool init_tables = false;

        if(init_tables) return;

        for(i = 0, w = 1; i < RC_LENGTH; ++i)
        {
            t_set(r,c)[i] = bytes2word(w, 0, 0, 0);
            w = f2(w);
        }

        for(i = 0; i < 256; ++i)
        {   
            unsigned char b;

            b = fwd_affine(fi((unsigned char)i));
            w = bytes2word(f2(b), b, b, f3(b));

            t_set(f,n)[0][i] = w;
            t_set(f,n)[1][i] = upr(w,1);
            t_set(f,n)[2][i] = upr(w,2);
            t_set(f,n)[3][i] = upr(w,3);

            w = bytes2word(b, 0, 0, 0);

            t_set(f,l)[0][i] = w;
            t_set(f,l)[1][i] = upr(w,1);
            t_set(f,l)[2][i] = upr(w,2);
            t_set(f,l)[3][i] = upr(w,3);

            b = fi(inv_affine((unsigned char)i));
            w = bytes2word(fe(b), f9(b), fd(b), fb(b));

            t_set(i,m)[0][b] = w;
            t_set(i,m)[1][b] = upr(w,1);
            t_set(i,m)[2][b] = upr(w,2);
            t_set(i,m)[3][b] = upr(w,3);

            t_set(i,n)[0][i] = w;
            t_set(i,n)[1][i] = upr(w,1);
            t_set(i,n)[2][i] = upr(w,2);
            t_set(i,n)[3][i] = upr(w,3);

            w = bytes2word(b, 0, 0, 0);

            t_set(i,l)[0][i] = w;
            t_set(i,l)[1][i] = upr(w,1);
            t_set(i,l)[2][i] = upr(w,2);
            t_set(i,l)[3][i] = upr(w,3);
        }
        init_tables = true;
    }
#endif

    /* (MSC) adapted GCM implementation from https://github.com/mko-x/SharedAES-GCM/blob/master/Sources/gcm.c */

    /******************************************************************************
    * THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
    *
    * This is a simple and straightforward implementation of AES-GCM authenticated
    * encryption. The focus of this work was correctness & accuracy. It is written
    * in straight 'C' without any particular focus upon optimization or speed. It
    * should be endian (memory byte order) neutral since the few places that care
    * are handled explicitly.
    *
    * This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
    *
    * It is intended for general purpose use, but was written in support of GRC's
    * reference implementation of the SQRL (Secure Quick Reliable Login) client.
    *
    * See:    http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
    *         http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
    *         gcm/gcm-revised-spec.pdf
    *
    * NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
    * REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
    *******************************************************************************/

#ifndef GET_UINT32_BE
#define GET_UINT32_BE(n,b,i) { (n) = ((dword)(b)[(i)] << 24) | ((dword)(b)[(i) + 1] << 16) | ((dword)(b)[(i) + 2] << 8)| ((dword)(b)[(i) + 3]); }
#endif

#ifndef PUT_UINT32_BE
#define PUT_UINT32_BE(n,b,i) { (b)[(i)] = (byte)((n) >> 24); (b)[(i) + 1] = (byte) ((n) >> 16); (b)[(i) + 2] = (byte)((n) >>  8); (b)[(i) + 3] = (byte)((n)); }
#endif

    static const ulong64 last4[16] = { 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0 };

    static void GCM_INIT(ulong64 * HH, ulong64 * HL, const byte * h) {
        int i, j;
        ulong64 hi, lo;
        ulong64 vl, vh;

        GET_UINT32_BE(hi, h, 0);
        GET_UINT32_BE(lo, h, 4);
        vh = (ulong64)hi << 32 | lo;
        
        GET_UINT32_BE(hi, h, 8);
        GET_UINT32_BE(lo, h, 12);
        vl = (ulong64)hi << 32 | lo;
        
        HL[8] = vl;
        HH[8] = vh;
        HH[0] = 0;
        HL[0] = 0;

        for (i = 4; i > 0; i >>= 1)
        {
            dword T = (vl & 1) * 0xe1000000U;
            vl = (vh << 63) | (vl >> 1);
            vh = (vh >> 1) ^ ((ulong64)T << 32);
            HL[i] = vl;
            HH[i] = vh;
        }

        for (i = 2; i <= 8; i *= 2)
        {
            ulong64 *HiL = HL + i, *HiH = HH + i;
            vh = *HiH;
            vl = *HiL;
            for (j = 1; j < i; j++)
            {
                HiH[j] = vh ^ HH[j];
                HiL[j] = vl ^ HL[j];
            }
        }
    }
    
    static void GCM_UPDATE(byte * x, const ulong64 * HH, const ulong64 * HL, const byte * y, int l)
    {
        int i;
        byte lo, hi, rem;
        ulong64 zh, zl;

        for (i = 0; i < l; i++) x[i] ^= y[i];

        lo = x[15] & 0xf;
        hi = x[15] >> 4;
        zh = HH[lo];
        zl = HL[lo];
        rem = (byte)zl & 0xf;
        zl = (zh << 60) | (zl >> 4);
        zh = (zh >> 4);
        zh ^= (ulong64)last4[rem] << 48;
        zh ^= HH[hi];
        zl ^= HL[hi];

        for (i = 14; i >= 0; i--) {
            lo = x[i] & 0xf;
            hi = x[i] >> 4;
            rem = (byte)zl & 0xf;
            zl = (zh << 60) | (zl >> 4);
            zh = (zh >> 4);
            zh ^= (ulong64)last4[rem] << 48;
            zh ^= HH[lo];
            zl ^= HL[lo];
            rem = (byte)zl & 0xf;
            zl = (zh << 60) | (zl >> 4);
            zh = (zh >> 4);
            zh ^= (ulong64)last4[rem] << 48;
            zh ^= HH[hi];
            zl ^= HL[hi];
        }

        PUT_UINT32_BE(zh >> 32, x, 0);
        PUT_UINT32_BE(zh, x, 4);
        PUT_UINT32_BE(zl >> 32, x, 8);
        PUT_UINT32_BE(zl, x, 12);
    }

    /* (MSC) end of adapted GCM implementation from https://github.com/mko-x/SharedAES-GCM/blob/master/Sources/gcm.c */

#define GCM_AES _innovaphone_aes::inno_aes_encrypt
#define GCM_XOR(_a_,_b_,_c_,_l_) for(unsigned _i_ = 0; _i_ < (_l_); _i_++) (_a_)[_i_] = (_b_)[_i_] ^ (_c_)[_i_];

    static void aes_gcm_encrypt(const byte * aad, dword aad_len, const byte * in, byte * out, dword len, const byte * ivec, byte * tag, const aes_encrypt_ctx * ctx)
    {
        byte iv[16];
        memcpy(iv, ivec, 12);
        ulong64 HH[16];
        ulong64 HL[16];
        byte tmp[16];
        dword l;
        // derive key
        memset(tag, 0, 16);
        GCM_AES(tag, tmp, ctx);
        GCM_INIT(HH, HL, tmp);
        // AAD
        l = 0;
        while (l + 16 <= aad_len) {
            GCM_UPDATE(tag, HH, HL, aad + l, 16);
            l += 16;
        }
        if (l < aad_len) {
            GCM_UPDATE(tag, HH, HL, aad + l, aad_len - l);
        }
        // PT
        l = 0;
        WRITE_DWORD_NET(iv + 12, 1);
        while (l + 16 <= len) {
            WRITE_DWORD_NET(iv + 12, READ_DWORD_NET(iv + 12) + 1);
            GCM_AES(iv, tmp, ctx);
            GCM_XOR(out + l, in + l, tmp, 16);
            GCM_UPDATE(tag, HH, HL, out + l, 16);
            l += 16;
        }
        if (l < len) {
            WRITE_DWORD_NET(iv + 12, READ_DWORD_NET(iv + 12) + 1);
            GCM_AES(iv, tmp, ctx);
            GCM_XOR(out + l, in + l, tmp, len - l);
            GCM_UPDATE(tag, HH, HL, out + l, len - l);
        }
        // length field
        WRITE_DWORD_NET(tmp, 0);
        WRITE_DWORD_NET(tmp + 4, aad_len * 8);
        WRITE_DWORD_NET(tmp + 8, 0);
        WRITE_DWORD_NET(tmp + 12, len * 8);
        GCM_UPDATE(tag, HH, HL, tmp, 16);
        // finish
        WRITE_DWORD_NET(iv + 12, 1);
        GCM_AES(iv, tmp, ctx);
        GCM_XOR(tag, tag, tmp, 16);
    }

    static bool aes_gcm_decrypt(const byte * aad, dword aad_len, const byte * in, byte * out, dword len, const byte * ivec, byte * tag_check, const aes_encrypt_ctx * ctx)
    {
        byte tag[16];
        byte iv[16];
        memcpy(iv, ivec, 12);
        ulong64 HH[16];
        ulong64 HL[16];
        byte tmp[16];
        dword l;
        // derive key
        memset(tag, 0, 16);
        GCM_AES(tag, tmp, ctx);
        GCM_INIT(HH, HL, tmp);
        // AAD
        l = 0;
        while (l + 16 <= aad_len) {
            GCM_UPDATE(tag, HH, HL, aad + l, 16);
            l += 16;
        }
        if (l < aad_len) {
            GCM_UPDATE(tag, HH, HL, aad + l, aad_len - l);
        }
        // CT
        l = 0;
        WRITE_DWORD_NET(iv + 12, 1);
        while (l + 16 <= len) {
            GCM_UPDATE(tag, HH, HL, in + l, 16);
            WRITE_DWORD_NET(iv + 12, READ_DWORD_NET(iv + 12) + 1);
            GCM_AES(iv, tmp, ctx);
            GCM_XOR(out + l, in + l, tmp, 16);
            l += 16;
        }
        if (l < len) {
            GCM_UPDATE(tag, HH, HL, in + l, len - l);
            WRITE_DWORD_NET(iv + 12, READ_DWORD_NET(iv + 12) + 1);
            GCM_AES(iv, tmp, ctx);
            GCM_XOR(out + l, in + l, tmp, len - l);
        }
        // length fields
        WRITE_DWORD_NET(tmp, 0);
        WRITE_DWORD_NET(tmp + 4, aad_len * 8);
        WRITE_DWORD_NET(tmp + 8, 0);
        WRITE_DWORD_NET(tmp + 12, len * 8);
        GCM_UPDATE(tag, HH, HL, tmp, 16);
        // finish
        WRITE_DWORD_NET(iv + 12, 1);
        GCM_AES(iv, tmp, ctx);
        GCM_XOR(tag, tag, tmp, 16);
        return !memcmp(tag, tag_check, 16);
    }

    static int aes_cts_encrypt(const byte * in, byte * out, int len, byte * iv, const aes_encrypt_ctx * aes_ctx) {
        if (len <= 16) {
            memset(out, 0, 16);
            memcpy(out, in, len);
            aes_cbc_encrypt(in, out, 16, iv, aes_ctx);
            return 16;
        }

        dword remlen = len % 16;

        if (remlen) {
            dword padlen = remlen ? (16 - remlen) : 0;
            byte temp[32];

            memcpy(temp, &in[len - remlen - 16], remlen + 16);
            memset(&temp[remlen + 16], 0, padlen);
            aes_cbc_encrypt(in, out, len - remlen - 16, iv, aes_ctx);
            aes_cbc_encrypt(temp, temp, 32, iv, aes_ctx);
            memcpy(&out[len - remlen - 16], &temp[16], 16);
            memcpy(&out[len - remlen], temp, remlen);
        }
        else {
            aes_cbc_encrypt(in, out, len - 32, iv, aes_ctx);
            aes_cbc_encrypt(&in[len - 32], &out[len - 16], 16, iv, aes_ctx);
            aes_cbc_encrypt(&in[len - 16], &out[len - 32], 16, iv, aes_ctx);
        }

        return len;
    }

    static int aes_cts_decrypt(const byte * in, byte * out, int len, byte * iv, const aes_decrypt_ctx * aes_ctx) {
        if (len <= 16) {
            memset(out, 0, 16);
            memcpy(out, in, len);
            aes_cbc_decrypt(in, out, 16, iv, aes_ctx);
            return 16;
        }

        dword remlen = len % 16;

        if (remlen) {
            //dword padlen = remlen ? (16 - remlen) : 0;
            byte temp[16];
            aes_ecb_decrypt(&in[len - remlen - 16], temp, 16, aes_ctx);
            memcpy(temp, &in[len - remlen], remlen);
            aes_cbc_decrypt(in, out, len - remlen - 16, iv, aes_ctx);
            aes_cbc_decrypt(temp, &out[len - remlen - 16], 16, iv, aes_ctx);
            aes_cbc_decrypt(&in[len - remlen - 16], temp, 16, iv, aes_ctx);
            memcpy(&out[len - remlen], temp, remlen);
        }
        else {
            aes_cbc_decrypt(in, out, len - 32, iv, aes_ctx);
            aes_cbc_decrypt(&in[len - 16], &out[len - 32], 16, iv, aes_ctx);
            aes_cbc_decrypt(&in[len - 32], &out[len - 16], 16, iv, aes_ctx);
        }

        return len;
    }
}

/*---------------------------------------------------------------------------*/
/* Helper functions to do the encryption                                     */
/*---------------------------------------------------------------------------*/

// The code to prepare the key was found here: http://www.innovation.ch/personal/ronald/ntlm.html

static void des_set_key(const byte * key, _openssl::DES_key_schedule * schedule)
{
    _openssl::DES_cblock tmpkey;

    tmpkey[0] = key[0];
    tmpkey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1);
    tmpkey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2);
    tmpkey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3);
    tmpkey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4);
    tmpkey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5);
    tmpkey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6);
    tmpkey[7] = (key[6] << 1) & 0xFF;

    _openssl::DES_set_odd_parity(&tmpkey);
    _openssl::DES_set_key_unchecked(&tmpkey, schedule);
}

static void des_set_key_plain(const byte * key, _openssl::DES_key_schedule * schedule)
{
    _openssl::DES_cblock tmpkey;

    for (unsigned i = 0; i < sizeof(tmpkey); i++)
        tmpkey[i] = key[i];

    _openssl::DES_set_odd_parity(&tmpkey);
    _openssl::DES_set_key_unchecked(&tmpkey, schedule);
}

/*---------------------------------------------------------------------------*/
/* Class Cipher                                                              */
/*---------------------------------------------------------------------------*/

USING_NAMESPACE

#if !defined ARMV7_CRYPT
static bool AesTablesInitialized = false;
static void GenerateAesTables(cipher_t type)
{
    if(AesTablesInitialized) return;
    AesTablesInitialized = true;
    _innovaphone_aes::gen_tables();
}
#endif

Cipher::Cipher()
{
    type = CIPHER_UNDEFINED;
}

void Cipher::Init(cipher_t type, const byte * key, int keyLen, bool encrypt, bool makekey)
{
    this->type = type;
    this->encrypt = encrypt;
    switch (type) {
    case CIPHER_DES:
        if (makekey) {
            des_set_key(key, (_openssl::DES_key_schedule*)encrypt_ctx);
        }
        else {
            des_set_key_plain(key, (_openssl::DES_key_schedule*)encrypt_ctx);
        }
        break;
    case CIPHER_3DES:
        if (makekey) {
            des_set_key(key, (_openssl::DES_key_schedule*)encrypt_ctx);
            des_set_key(key + 7, (_openssl::DES_key_schedule*)(encrypt_ctx + 32));
            des_set_key(key + 14, (_openssl::DES_key_schedule*)(encrypt_ctx + 64));
        }
        else {
            des_set_key_plain(key, (_openssl::DES_key_schedule*)encrypt_ctx);
            des_set_key_plain(key + 8, (_openssl::DES_key_schedule*)(encrypt_ctx + 32));
            des_set_key_plain(key + 16, (_openssl::DES_key_schedule*)(encrypt_ctx + 64));
        }
        break;
    case CIPHER_AES:
        ASSERT(keyLen == 16 || keyLen == 24 || keyLen == 32 || keyLen == 128 || keyLen == 192 || keyLen == 256, "Cipher::Init(CHIPHER_AES): unsupported keyLen. Valid values are 16, 24, 32, 128, 192, 256");
#if defined ARMV7_CRYPT
        aesbs_setkey(key, keyLen, (_innovaphone_aes::aes_encrypt_ctx*)encrypt_ctx); // !!! bs_context
        aesbs_setkey(key, keyLen, (_innovaphone_aes::aes_decrypt_ctx*)decrypt_ctx);
#else

#   if defined IA32_CRYPT_AESNI || defined IA64_CRYPT_AESNI
        if (_innovaphone_aes::has_cpu_aesni) {
            (void) _innovaphone_aes::aesni_set_key_glue((void*)encrypt_ctx, key, keyLen);
            (void) _innovaphone_aes::aesni_set_key_glue((void*)decrypt_ctx, key, keyLen);
            break;
        }
#   endif

        GenerateAesTables(type);
        _innovaphone_aes::aes_encrypt_key(key, keyLen, (_innovaphone_aes::aes_encrypt_ctx*)encrypt_ctx);
        _innovaphone_aes::aes_decrypt_key(key, keyLen, (_innovaphone_aes::aes_decrypt_ctx*)decrypt_ctx);
#endif
        break;
    case CIPHER_RC2:
        _openssl::RC2_set_key((_openssl::RC2_KEY *)encrypt_ctx, keyLen, (const unsigned char*)key, keyLen * 8);
        break;
    case CIPHER_RC4:
        _openssl::RC4_set_key((_openssl::RC4_KEY *)encrypt_ctx, keyLen, key);
        break;
    default:
        ASSERT(false, "unsupported cipher");
        break;
    }
}

void Cipher::Block(const byte * in, byte * out)
{
    switch (type) {
    case CIPHER_DES:
        _openssl::DES_ecb_encrypt((_openssl::const_DES_cblock *)in, (_openssl::DES_cblock *)out, (_openssl::DES_key_schedule *)encrypt_ctx, encrypt ? DES_ENCRYPT : DES_DECRYPT);
        break;
    case CIPHER_AES:
        if (encrypt) _innovaphone_aes::aes_ecb_encrypt(in, out, 16, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        else _innovaphone_aes::aes_ecb_decrypt(in, out, 16, (_innovaphone_aes::aes_decrypt_ctx *)decrypt_ctx);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
}

void Cipher::Crypt(const byte * in, byte * out, int len)
{
    switch (type) {
    case CIPHER_AES:
        ASSERT(len % 16 == 0, "Input length is not a multipe of the AES block size of 16 bytes");

        if (encrypt) _innovaphone_aes::aes_ecb_encrypt(in, out, len, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        else  _innovaphone_aes::aes_ecb_decrypt(in, out, len, (_innovaphone_aes::aes_decrypt_ctx *)decrypt_ctx);
        break;
    case CIPHER_RC4:
        _openssl::RC4((_openssl::RC4_KEY *)encrypt_ctx, len, in, out);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
}

void Cipher::CtrCrypt(const byte * in, byte * out, int len, byte * iv)
{
    switch (type) {
    case CIPHER_AES:
        _innovaphone_aes::aes_ctr_encrypt(in, out, len, iv, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
}

void Cipher::CfbCrypt(const byte * in, byte * out, int len, byte * iv)
{
    switch (type) {
    case CIPHER_AES:
        if (encrypt) _innovaphone_aes::aes_cfb_encrypt(in, out, len, iv, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        else _innovaphone_aes::aes_cfb_decrypt(in, out, len, iv, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
}

void Cipher::CbcCrypt(const byte * in, byte * out, int len, byte * iv)
{
    switch (type) {
    case CIPHER_DES:
        ASSERT(len % 8 == 0, "Input length is not a multipe of the DES block size of 8 bytes");
        _openssl::DES_ncbc_encrypt(in, out, len, (_openssl::DES_key_schedule *)encrypt_ctx, (_openssl::const_DES_cblock *)iv, encrypt ? DES_ENCRYPT : DES_DECRYPT);
        break;
    case CIPHER_3DES:
        ASSERT(len % 8 == 0, "Input length is not a multipe of the DES block size of 8 bytes");
        _openssl::DES_ede3_cbc_encrypt(in, out, len, (_openssl::DES_key_schedule *)(encrypt_ctx), (_openssl::DES_key_schedule *)(encrypt_ctx + 32), (_openssl::DES_key_schedule *)(encrypt_ctx + 64), (_openssl::const_DES_cblock *)iv, encrypt ? DES_ENCRYPT : DES_DECRYPT);
        break;
    case CIPHER_AES:
        if (encrypt) _innovaphone_aes::aes_cbc_encrypt(in, out, len, iv, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        else _innovaphone_aes::aes_cbc_decrypt(in, out, len, iv, (_innovaphone_aes::aes_decrypt_ctx *)decrypt_ctx);
        break;
    case CIPHER_RC2:
        _openssl::RC2_cbc_encrypt(in, out, len, (_openssl::RC2_KEY *)encrypt_ctx, iv, encrypt ? RC2_ENCRYPT: RC2_DECRYPT);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
}

int Cipher::CtsCrypt(const byte * in, byte * out, int len, byte * iv)
{
    switch (type) {
    case CIPHER_AES:
        if (encrypt) return _innovaphone_aes::aes_cts_encrypt(in, out, len, iv, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        else return _innovaphone_aes::aes_cts_decrypt(in, out, len, iv, (_innovaphone_aes::aes_decrypt_ctx *)decrypt_ctx);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        return 0;
        break;
    }
}

bool Cipher::GcmCrypt(const byte * aad, int aad_len, const byte * in, byte * out, int len, byte * iv, byte * tag)
{
    switch (type) {
    case CIPHER_AES:
        if (encrypt) _innovaphone_aes::aes_gcm_encrypt(aad, aad_len, in, out, len, iv, tag, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        else return _innovaphone_aes::aes_gcm_decrypt(aad, aad_len, in, out, len, iv, tag, (_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
    return true;
}

void Cipher::Reset(void)
{
    switch (type) {
    case CIPHER_AES:
        _innovaphone_aes::aes_reset_mode((_innovaphone_aes::aes_encrypt_ctx *)encrypt_ctx);
        _innovaphone_aes::aes_reset_mode((_innovaphone_aes::aes_decrypt_ctx *)decrypt_ctx);
        break;
    default:
        ASSERT(false, "Not implemented for cipher");
        break;
    }
}




#if defined ARMV7_CRYPT  // AES code to init the aesbs context
namespace _innovaphone_aes {
extern "C" void aesbs_convert_key(byte * out, dword * rk, int rounds);

//----------------
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2017-2019 Linaro Ltd <ard.biesheuvel@linaro.org>
 */

#define AES_KEYSIZE_192 24
#define AES_KEYSIZE_256 32

#define get_unaligned_le32(x)  READ_DWORD_LE(x)
#define put_unaligned_le32(x,y) WRITE_DWORD_LE(y,x)

#define AES_MAX_KEYLENGTH	(15 * 16)
#define AES_MAX_KEYLENGTH_dword	(AES_MAX_KEYLENGTH / sizeof(dword))

struct crypto_aes_ctx {
    dword __attribute__((aligned(16)))  key_enc[AES_MAX_KEYLENGTH_dword];
    dword __attribute__((aligned(16)))  key_dec[AES_MAX_KEYLENGTH_dword];
    dword key_length;
};


static inline dword ror32(dword word, unsigned int shift)
{
    return (word >> (shift & 31)) | (word << ((-shift) & 31));
}

 /*
  * Emit the sbox as volatile const to prevent the compiler from doing
  * constant folding on sbox references involving fixed indexes.
  */
static volatile const byte aes_sbox[] = {
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
    0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
    0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
    0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
    0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
    0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
    0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
    0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
    0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
    0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
    0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16,
};

static volatile const byte aes_inv_sbox[] = {
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
    0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
    0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
    0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
    0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
    0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
    0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
    0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
    0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
    0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
    0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
    0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d,
};


static dword mul_by_x(dword w)
{
    dword x = w & 0x7f7f7f7f;
    dword y = w & 0x80808080;

    /* multiply by polynomial 'x' (0b10) in GF(2^8) */
    return (x << 1) ^ (y >> 7) * 0x1b;
}

static dword mul_by_x2(dword w)
{
    dword x = w & 0x3f3f3f3f;
    dword y = w & 0x80808080;
    dword z = w & 0x40404040;

    /* multiply by polynomial 'x^2' (0b100) in GF(2^8) */
    return (x << 2) ^ (y >> 7) * 0x36 ^ (z >> 6) * 0x1b;
}

static dword mix_columns(dword x)
{
    /*
     * Perform the following matrix multiplication in GF(2^8)
     *
     * | 0x2 0x3 0x1 0x1 |   | x[0] |
     * | 0x1 0x2 0x3 0x1 |   | x[1] |
     * | 0x1 0x1 0x2 0x3 | x | x[2] |
     * | 0x3 0x1 0x1 0x2 |   | x[3] |
     */
    dword y = mul_by_x(x) ^ ror32(x, 16);

    return y ^ ror32(x ^ y, 8);
}

static dword inv_mix_columns(dword x)
{
    /*
     * Perform the following matrix multiplication in GF(2^8)
     *
     * | 0xe 0xb 0xd 0x9 |   | x[0] |
     * | 0x9 0xe 0xb 0xd |   | x[1] |
     * | 0xd 0x9 0xe 0xb | x | x[2] |
     * | 0xb 0xd 0x9 0xe |   | x[3] |
     *
     * which can conveniently be reduced to
     *
     * | 0x2 0x3 0x1 0x1 |   | 0x5 0x0 0x4 0x0 |   | x[0] |
     * | 0x1 0x2 0x3 0x1 |   | 0x0 0x5 0x0 0x4 |   | x[1] |
     * | 0x1 0x1 0x2 0x3 | x | 0x4 0x0 0x5 0x0 | x | x[2] |
     * | 0x3 0x1 0x1 0x2 |   | 0x0 0x4 0x0 0x5 |   | x[3] |
     */
    dword y = mul_by_x2(x);

    return mix_columns(x ^ y ^ ror32(y, 16));
}

static inline dword subshift(dword in[], int pos)
{
    return (aes_sbox[in[pos] & 0xff]) ^
        (aes_sbox[(in[(pos + 1) % 4] >> 8) & 0xff] << 8) ^
        (aes_sbox[(in[(pos + 2) % 4] >> 16) & 0xff] << 16) ^
        (aes_sbox[(in[(pos + 3) % 4] >> 24) & 0xff] << 24);
}

static inline dword inv_subshift(dword in[], int pos)
{
    return (aes_inv_sbox[in[pos] & 0xff]) ^
        (aes_inv_sbox[(in[(pos + 3) % 4] >> 8) & 0xff] << 8) ^
        (aes_inv_sbox[(in[(pos + 2) % 4] >> 16) & 0xff] << 16) ^
        (aes_inv_sbox[(in[(pos + 1) % 4] >> 24) & 0xff] << 24);
}

static dword subw(dword in)
{
    return (aes_sbox[in & 0xff]) ^
        (aes_sbox[(in >> 8) & 0xff] << 8) ^
        (aes_sbox[(in >> 16) & 0xff] << 16) ^
        (aes_sbox[(in >> 24) & 0xff] << 24);
}

/**
 * aes_expandkey - Expands the AES key as described in FIPS-197
 * @ctx:	The location where the computed key will be stored.
 * @in_key:	The supplied key.
 * @key_len:	The length of the supplied key.
 *
 * Returns 0 on success. The function fails only if an invalid key size (or
 * pointer) is supplied.
 * The expanded key size is 240 bytes (max of 14 rounds with a unique 16 bytes
 * key schedule plus a 16 bytes key which is used before the first round).
 * The decryption key is prepared for the "Equivalent Inverse Cipher" as
 * described in FIPS-197. The first slot (16 bytes) of each key (enc or dec) is
 * for the initial combination, the second slot for the first round and so on.
 */
int aes_expandkey(struct crypto_aes_ctx* ctx, const byte* in_key, unsigned int key_len)
{
    dword kwords = key_len / sizeof(dword);
    dword rc, i, j;

    ctx->key_length = key_len;

    for (i = 0; i < kwords; i++)
        ctx->key_enc[i] = get_unaligned_le32(in_key + i * sizeof(dword));

    for (i = 0, rc = 1; i < 10; i++, rc = mul_by_x(rc)) {
        dword* rki = ctx->key_enc + (i * kwords);
        dword* rko = rki + kwords;

        rko[0] = ror32(subw(rki[kwords - 1]), 8) ^ rc ^ rki[0];
        rko[1] = rko[0] ^ rki[1];
        rko[2] = rko[1] ^ rki[2];
        rko[3] = rko[2] ^ rki[3];

        if (key_len == AES_KEYSIZE_192) {
            if (i >= 7)
                break;
            rko[4] = rko[3] ^ rki[4];
            rko[5] = rko[4] ^ rki[5];
        }
        else if (key_len == AES_KEYSIZE_256) {
            if (i >= 6)
                break;
            rko[4] = subw(rko[3]) ^ rki[4];
            rko[5] = rko[4] ^ rki[5];
            rko[6] = rko[5] ^ rki[6];
            rko[7] = rko[6] ^ rki[7];
        }
    }

    /*
     * Generate the decryption keys for the Equivalent Inverse Cipher.
     * This involves reversing the order of the round keys, and applying
     * the Inverse Mix Columns transformation to all but the first and
     * the last one.
     */
    ctx->key_dec[0] = ctx->key_enc[key_len + 24];
    ctx->key_dec[1] = ctx->key_enc[key_len + 25];
    ctx->key_dec[2] = ctx->key_enc[key_len + 26];
    ctx->key_dec[3] = ctx->key_enc[key_len + 27];

    for (i = 4, j = key_len + 20; j > 0; i += 4, j -= 4) {
        ctx->key_dec[i] = inv_mix_columns(ctx->key_enc[j]);
        ctx->key_dec[i + 1] = inv_mix_columns(ctx->key_enc[j + 1]);
        ctx->key_dec[i + 2] = inv_mix_columns(ctx->key_enc[j + 2]);
        ctx->key_dec[i + 3] = inv_mix_columns(ctx->key_enc[j + 3]);
    }

    ctx->key_dec[i] = ctx->key_enc[0];
    ctx->key_dec[i + 1] = ctx->key_enc[1];
    ctx->key_dec[i + 2] = ctx->key_enc[2];
    ctx->key_dec[i + 3] = ctx->key_enc[3];

    return 0;
}

static int aesbs_setkey(const byte* in_key, unsigned int key_len, void*context)

{
    struct aesbs_ctx* ctx = (struct aesbs_ctx *) context;
    struct crypto_aes_ctx rk;
    int err;
    err = aes_expandkey(&rk, in_key, key_len);
    if (err)
        return err;

    ctx->rounds = 6 + key_len / 4;

    // convert: out,in,rounds 
    // in: 2 keys, round 0 and 1

    aesbs_convert_key(ctx->rk, rk.key_enc, ctx->rounds);

    return 0;
}

#if !defined(_MSC_VER) && defined(__x86_64)
#pragma GCC pop_options
#endif
}
#endif

