[[insns-vsm4k, Vector SM4 Key Expansion]]
= vsm4k.vi

Synopsis::
Vector SM4 KeyExpansion

Mnemonic::
vsm4k.vi vd, vs2, uimm

Encoding::
[wavedrom, , svg]
....
{reg:[
{bits: 7, name: 'OP-P'},
{bits: 5, name: 'vd'},
{bits: 3, name: 'OPMVV'},
{bits: 5, name: 'uimm'},
{bits: 5, name: 'vs2'},
{bits: 1, name: '1'},
{bits: 6, name: '100001'},
]}
....
Reserved Encodings::
* `SEW` is any value other than 32

Arguments::

[%autowidth]
[%header,cols="4,2,2,2,2,2"]
|===
|Register
|Direction
|EGW
|EGS
|EEW
|Definition

| uimm | input  | -    | - | -  | Round group (rnd)
| Vs2  | input  | 128  | 4 | 32 | Current 4 round keys rK[0:3]
| Vd   | output | 128  | 4 | 32 | Next 4 round keys rK'[0:3]
|===

Description::
Four rounds of the SM4 Key Expansion are performed.

Four round keys are read in as a 4-element group from `vs2`. Each of the next four round keys are generated
by iteratively XORing the last three round keys with a constant that is indexed by the Round Group Number,
performing a byte-wise substitution, and then performing XORs between rotated versions of this value
and the corresponding current round key. 

The Round group number (`rnd`) comes from `uimm[2:0]`; the bits in `uimm[4:3]` are ignored.
Round group numbers range from 0 to 7 and indicate which
group of four round keys are being generated. Round Keys range from 0-31.
For example, if `rnd`=1, then round keys 4, 5, 6, and 7 are being generated.

//  vs2 = {rK[i-4], rK[i-3],rK[i-2], rK[i-1]} // last 4 round keys
//  rnd = 0 to 7; // group of 4 rounds
//  vd (out) = {rK[i], rK[i+1],rK[i+2], rK[i+3]} // next 4 rounds keys

// Each of the 32 rounds consumes the last 4 32-bit keys along with a round constant and
// produces the next 32-bit key.


[NOTE]
====
Software needs to generate the initial round keys. This is done by XORing the 128-bit encryption key with
the system parameters: FK[0:3]
====

.System Parameters
[%autowidth]
[%header,cols="^2,^2"]
|===
|FK
|constant

| 0 | A3B1BAC6
| 1 | 56AA3350 
| 2 | 677D9197
| 3 | B27022DC
|===


////
.System Parameters
[%autowidth]
[%header,cols="^2,^2"]
|===
|FK
|constant

| 0 | A3B1BAC6
| 1 | 56AA3350 
| 2 | 677D9197
| 3 | B27022DC
|===
////

//  MK = {MK[0], MK[1], MK[2], MK[3]} // Encryption Key
//  rK[-4,-1] = K[0:3] = MK[0:3] ^ FK[0:3]


// The round keys are rK[0] to rK[31]
//  B = (rK[i-3] XOR rK[i-2] XOR rK[i-1] XOR CK[round]); +
//  S = subBytes(B); + 
//  rK[i]= rK[i-4] XOR S XOR ROTL13(S) XOR ROTR23(S); +
//
// The round constants and the S-box are described below and can be found at https://datatracker.ietf.org/doc/id/// draft-crypto-sm4-00

[NOTE]
====
Implementation Hint

The round constants (CK) can be generated on the fly fairly cheaply.
If the bytes of the constants are assigned an incrementing index from 0 to 127, the value of each byte is equal to its index multiplied by 7 modulo 256.
Since the results are all limited to 8 bits, the modulo operation occurs for free:

	B[n] = n + 2n + 4n;
       = 8n + ~n + 1;
====

// This instruction only returns the generated keys to the same element group as the source.
// If it is desired to have the same key in all vector groups, either the input vector groups
// need to contain the same values, or the output from a particular group needs to be "broadcast"
// to the other groups using an instruction such as vrgather.

// The number of element groups to be processed is `vl`/`EGS`.
// `vl` must be set to the number of `SEW=32` elements to be processed and
// therefore must be a multiple of `EGS=4`. +
// Likewise, `vstart` must be a multiple of `EGS=4`.

////
.System Parameters
[%autowidth]
[%header,cols="^2,^2"]
|===
|FK
|constant

| 0 | A3B1BAC6
| 1 | 56AA3350 
| 2 | 677D9197
| 3 | B27022DC
|===
////

Operation::
[source,sail]
--

function clause execute (vsm4k(uimm, vs2)) = {
  if(LMUL*VLEN < EGW)  then {
    handle_illegal();  // illegal instruction exception
    RETIRE_FAIL
  } else {

  eg_len = (vl/EGS)
  eg_start = (vstart/EGS)
  
  let B : bits(32) = 0;
  let S : bits(32) = 0;
  let rk4 : bits(32) = 0;
  let rk5 : bits(32) = 0;
  let rk6 : bits(32) = 0;
  let rk7 : bits(32) = 0;
  let rnd : bits(3) = uimm[2:0]; // Lower 3 bits

  foreach (i from eg_start to eg_len-1) {
    let (rk3 @ rk2 @ rk1 @ rk0) : bits(128) = get_velem(vs2, 128, i);
    
    B = rk1 ^ rk2 ^ rk3 ^ ck(4 * rnd);
    S = sm4_subword(B);
    rk4 = ROUND_KEY(rk0, S);

    B = rk2 ^ rk3 ^ rk4 ^ ck(4 * rnd + 1);
    S = sm4_subword(B);
    rk5 = ROUND_KEY(rk1, S);

    B = rk3 ^ rk4 ^ rk5 ^ ck(4 * rnd + 2);
    S = sm4_subword(B);
    rk6 = ROUND_KEY(rk2, S);

    B = rk4 ^ rk5 ^ rk6 ^ ck(4 * rnd + 3);
    S = sm4_subword(B);
    rk7 = ROUND_KEY(rk3, S);

    // Update the destination register.
   set_velem(vd, EGW=128, i, (rk7 @ rk6 @ rk5 @ rk4));
  }
  RETIRE_SUCCESS
  }
}

val round_key : bits(32) -> bits(32)
function ROUND_KEY(X, S) = ((X) ^ ((S) ^ ROL32((S), 13) ^ ROL32((S), 23)))

// SM4 Constant Key (CK)
let ck : list(bits(32)) = [|
	0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
	0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
	0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
	0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
	0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
	0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
	0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
	0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
  |]
};


--

Included in::
<<zvks>>, <<zvksc>>, <<zvksed>>, <<zvksg>>
