/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include "hitls_build.h"
#ifdef HITLS_CRYPTO_SM4

#include "crypt_arm.h"

#define VTMP0 V8
#define VTMP1 V9
#define VTMP2 V10

#define DATA0 V16

#define MaskV      v26
#define TAHMatV    v27
#define TALMatV    v28
#define ATAHMatV   v29
#define ATALMatV   v30
#define ANDMaskV   v31

#define MaskQ      q26
#define TAHMatQ    q27
#define TALMatQ    q28
#define ATAHMatQ   q29
#define ATALMatQ   q30
#define ANDMaskQ   q31

CRYPT_AARCH64_ARCH_CRYPTO

CRYPT_AARCH64_RODATA_SECTION
.p2align 4
#ifdef HITLS_BIG_ENDIAN
 .qtmp0:
    .quad 0x0101010101010101,0x0101010101010187
.Lsbox_magic:
    .quad 0x0306090c0f020508,0x0b0e0104070a0d00
    .quad 0x22581a6002783a40,0x62185a2042387a00
    .quad 0xc10bb67c4a803df7,0x15df62a89e54e923
    .quad 0x1407c6d56c7fbead,0xb9aa6b78c1d21300
    .quad 0xe383c1a1fe9edcbc,0x6404462679195b3b
    .quad 0x0f0f0f0f0f0f0f0f,0x0f0f0f0f0f0f0f0f
#else
 .qtmp0:
    .quad 0x0101010101010187,0x0101010101010101
.Lsbox_magic:
    .quad 0x0b0e0104070a0d00,0x0306090c0f020508
    .quad 0x62185a2042387a00,0x22581a6002783a40
    .quad 0x15df62a89e54e923,0xc10bb67c4a803df7
    .quad 0xb9aa6b78c1d21300,0x1407c6d56c7fbead
    .quad 0x6404462679195b3b,0xe383c1a1fe9edcbc
    .quad 0x0f0f0f0f0f0f0f0f,0x0f0f0f0f0f0f0f0f
#endif

.Lck:
    .long 0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269
    .long 0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9
    .long 0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249
    .long 0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9
    .long 0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229
    .long 0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299
    .long 0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209
    .long 0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
.Lfk:
    .long 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
.Lshuffles:
    .long 0x07060504, 0x0B0A0908, 0x0F0E0D0C, 0x03020100

#ifndef HITLS_BIG_ENDIAN
    #define REV32_EQ(DST, SRC)                             \
        rev32    DST.16b,DST.16b                           ;

#else
    #define REV32_EQ(DST, SRC)                             \
    /*rev32 eq is null in armeb  */                        ;

#endif

.macro LOAD_SBOX_MATRIX
    CRYPT_AARCH64_GET_RELA(x15, .Lsbox_magic)
    ldr MaskQ,      [x15]
    ldr TAHMatQ,    [x15, #16]
    ldr TALMatQ,    [x15, #32]
    ldr ATAHMatQ,   [x15, #48]
    ldr ATALMatQ,   [x15, #64]
    ldr ANDMaskQ,   [x15, #80]
.endm

.macro MUL_MATRIX X, HIGHERMAT, LOWERMAT, TMP
    ushr    \TMP\().16b, \X\().16b, 4
    and     \X\().16b, \X\().16b, ANDMaskV.16b
    tbl     \X\().16b, {\LOWERMAT\().16b}, \X\().16b
    tbl     \TMP\().16b, {\HIGHERMAT\().16b}, \TMP\().16b
    eor     \X\().16b, \X\().16b, \TMP\().16b
.endm



.text
#define USER_KEY x0
#define ROUND_KEY1 x1
#define ENC1 w2

#define POINTER1 x5
#define SCHEDULES x6
#define WTMP w7
#define ROUND_KEY2 w8

#define V_KEY v5
#define V_FK v6
#define V_MAP v7

/*
 * void vpsm4_ex_set_key(const unsigned char *userKey, SM4_KEY *key, int enc);
 * generate sm4 rounk key context
 *   USER_KEY => userKey; 
 *   ROUND_KEY1 => key ; 
 *   if encryption: ENC=>enc
 */
CRYPT_AARCH64_LOCAL_FUNC_START(vpsm4_ex_set_key)
AARCH64_PACIASP
    stp x29, x30, [sp, #-0x30]!
    stp d8, d9, [sp, #0x10]
    stp d10, d11, [sp, #0x20]

    ld1 {V_KEY.4s},[USER_KEY]
    LOAD_SBOX_MATRIX
    REV32_EQ(V_KEY,V_KEY)

    CRYPT_AARCH64_GET_RELA(POINTER1, .Lshuffles)
    ld1 {V_MAP.4s},[POINTER1]
    CRYPT_AARCH64_GET_RELA(POINTER1, .Lfk)
    ld1 {V_FK.4s},[POINTER1]
    eor V_KEY.16b,V_KEY.16b,V_FK.16b
    mov SCHEDULES,#32
    CRYPT_AARCH64_GET_RELA(POINTER1, .Lck)
    movi VTMP0.16b,#64
    cbnz ENC1,1f
    add ROUND_KEY1,ROUND_KEY1,124
1:  // loop
    mov WTMP,V_KEY.s[1]
    ldr ROUND_KEY2,[POINTER1],#4
    eor ROUND_KEY2,ROUND_KEY2,WTMP
    mov WTMP,V_KEY.s[2]
    eor ROUND_KEY2,ROUND_KEY2,WTMP
    mov WTMP,V_KEY.s[3]
    eor ROUND_KEY2,ROUND_KEY2,WTMP
    
    /* optimize sbox using AESE instruction */
    mov DATA0.s[0],ROUND_KEY2
    tbl VTMP0.16b, {DATA0.16b}, MaskV.16b
    MUL_MATRIX VTMP0, TAHMatV, TALMatV, VTMP2
    eor VTMP1.16b, VTMP1.16b, VTMP1.16b
    aese VTMP0.16b,VTMP1.16b
    MUL_MATRIX VTMP0, ATAHMatV, ATALMatV, VTMP2
    mov WTMP,VTMP0.s[0]
    
    /* linear transformation */
    eor ROUND_KEY2,WTMP,WTMP,ror #19
    eor ROUND_KEY2,ROUND_KEY2,WTMP,ror #9
    mov WTMP,V_KEY.s[0]
    eor ROUND_KEY2,ROUND_KEY2,WTMP
    mov V_KEY.s[0],ROUND_KEY2
    cbz ENC1,2f
    str ROUND_KEY2,[ROUND_KEY1],#4
    b 3f
2:  // set encrypt key
    str ROUND_KEY2,[ROUND_KEY1],#-4
3:  // final
    tbl V_KEY.16b,{V_KEY.16b},V_MAP.16b
    subs SCHEDULES,SCHEDULES,#1
    b.ne 1b
    /*clear register for temp key */
    eor V_KEY.16b, V_KEY.16b, V_KEY.16b
    eor ROUND_KEY2, ROUND_KEY2, ROUND_KEY2
    ldp d10, d11, [sp, #0x20]
    ldp d8, d9, [sp, #0x10]
    ldp x29, x30, [sp], #0x30
AARCH64_AUTIASP
    ret
CRYPT_AARCH64_LOCAL_FUNC_END(vpsm4_ex_set_key)

/*
 * void Vpsm4SetEncryptKey(const unsigned char *userKey, SM4_KEY *key);
 * generate SM4 encrypt round KEY context 
 * x0 => userKey; x1 => key
 */
CRYPT_AARCH64_FUNC_START(Vpsm4SetEncryptKey)
AARCH64_PACIASP
    stp x29,x30,[sp,#-16]!
    mov w2,1
    bl CRYPT_AARCH64_SYM(vpsm4_ex_set_key)
    ldp x29,x30,[sp],#16
AARCH64_AUTIASP
    ret
CRYPT_AARCH64_FUNC_END(Vpsm4SetEncryptKey)

/*
 * void Vpsm4SetDecryptKey(const unsigned char *userKey, SM4_KEY *key);
 * generate SM4 decryption round KEY context 
 * x0 => userKey; x1 => key
 */
CRYPT_AARCH64_FUNC_START(Vpsm4SetDecryptKey)
AARCH64_PACIASP
    stp x29,x30,[sp,#-16]!
    mov w2,0
    bl CRYPT_AARCH64_SYM(vpsm4_ex_set_key)
    ldp x29,x30,[sp],#16
AARCH64_AUTIASP
    ret
CRYPT_AARCH64_FUNC_END(Vpsm4SetDecryptKey)

#endif