Assembly Language Programming

November 19, 2013

GOST Encryption 28147-89 – Counter and Cipher Feedback Modes

The Javascript program in this post encrypts and decrypts data in either GOST Counter Encryption Mode or GOST Cipher Feedback Mode. Both of these modes are based on the ECB (Electronic Codebook) mode, which is described and implemented in the other post.

The program will accept input only as 32 bit (8 hex digits) little endian hexadecimal values:

- The 256 bit key is entered as the 32 bit little endian values X0 X1 X2 X3 X4 X5 X6 X7.

- The 64 bit initialisation vector S is entered as the 32 bit values S1 and S2.

- If M is the number of 64 bit blocks in the input plain text, then the plain text is entered 
  as (2 x M) 32 bit values. If the number of hex digits in the input string is not a multiple 
  of 16, then zeroes will be appended to the input string to make it so.

Related Post

Implementation of the GOST 28147-89 Electronic Codebook Mode (ECB):

GOST Encryption 28147-89 ECB

Disclaimer

I haven’t been able to find any test vectors for these modes of GOST, so I can’t guarantee the accuracy of the program.

GOST Counter Encryption Mode

The Counter Encryption Mode of GOST 28147-89 is specified in:

http://tools.ietf.org/html/rfc5830

In pseudo code, the specification is:

- S1 S2 is a 64 bit initialisation vector.

- Tp is the plain text, divided into M 64 bit blocks Tp(i).

- Tc is the output cipher text, divided into M 64 bit blocks Tc(i).

- N1 N2 N3 N4 CM3 CM4 CM5 are 32 bit little endian registers.

- Gc(i) is a 64 bit running key that is XORed into Tp(i).

- C1 and C2 are 32 bit constants defined as:

- C1 = 01 01 01 04

- C2 = 01 01 01 01

- ECB() is a function that encrypts a 64 bit block of data in ECB mode. 

- The pseudo code is then:

  ENCRYPT_GCTR(Tp S1 S2):

  N1 = S1

  N2 = S2

  N1 N2 = ECB(N1 N2)

  N3 = N1

  N4 = N2

  for(i=0;i<M;i++)
  {
   CM3 = N3 + C2

   CM4 = N4 + C1

   N1 = N3 = CM3

   N2 = N4 = CM4

   Gc(i) = N1 N2 = ECB(N1 N2)

   CM5 = Gc(i) xor Tp(i)

   Tc(i) = CM5 
  }

- This can be simplified to:

  ENCRYPT_GCTR(Tp S1 S2):

  N1 = S1

  N2 = S2

  N1 N2 = ECB(N1 N2)

  N3 = N1

  N4 = N2

  for(i=0;i<M;i++)
  {
   N1 = N3 = N3 + C2

   N2 = N4 = N4 + C1

   Gc(i) = N1 N2 = ECB(N1 N2)

   Tc(i) = Gc(i) xor Tp(i)
  }

- If encryption is Tc = ENCRYPT_GCTR(Tp S1 S2), then decryption is Tp = ENCRYPT_GCTR(Tc S1 S2).

GOST Cipher Feedback Encryption Mode

The Cipher Feedback Encryption Mode of GOST 28147-89 is specified in:

http://tools.ietf.org/html/rfc5830

In pseudo code, the specification is:

- Encryption:

  ENCRYPT_CFB(Tp S1 S2):

  N1 = S1

  N2 = S2

  for(i=0;i<M;i++)
  {
   Gc(i) = N1 N2 = ECB(N1 N2)

   Tc(i) = Gc(i) xor Tp(i)

   N1 N2 = Tc(i)
  }

- Decryption:

  DECRYPT_CFB(Tc S1 S2):

  N1 = S1

  N2 = S2

  for(i=0;i<M;i++)
  {
   Gc(i) = N1 N2 = ECB(N1 N2)

   Tp(i) = Gc(i) xor Tc(i)

   N1 N2 = Tc(i)
  }

The HTML/Javascript Code

To grab this code, select with the mouse, and copy and paste into a text file.

<html>

<head>

<title>GOST Encryption</title>

<style type="text/css">

textarea,input,p
{
 font-family: Lucida Console;

 font-size: 11pt;
}

</style>

</head>

<script type="text/javascript">

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

 // K_Arr is the s-box array: K8 K7 K6 K5 K4 K3 K2 K1

 var K_Arr = [];

 // the 256 bit key: X0 X1 X2 X3 X4 X5 X6 X7

 var X0;
 var X1;
 var X2;
 var X3;
 var X4;
 var X5;
 var X6;
 var X7;

 // the plain text input array

 var Tp_Arr = [];

 // the cipher text output array

 var Tc_Arr = [];

 // the 64 bit input block: N1 N2

 var N1;
 var N2;

 // the 64 bit initialisation vector: S1 S2

 var S1;
 var S2;

 // the constants C1 and C2

 var C1 = 0x01010104;

 var C2 = 0x01010101;

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Encrypt_ECB()
{
 // ===================================

 // for each round:

 // ===================================

 // CM1 = N1 + X(round)

 // R = K(CM1)

 // R = ROTL(R,11)

 // CM2 = R xor N2

 // ===================================

 // for all except the last round:

 // ===================================

 // N2 = N1

 // N1 = CM2

 // ===================================

 // for the last round:

 // ===================================

 // N2 = CM2

 // ===================================

 var CM1,CM2,R;

 var i;

 for(i=0;i<3;i++)
 {
  CM1 = N1 + X0;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X1;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X2;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X3;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X4;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X5;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X6;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;

  CM1 = N1 + X7;
  R = K_Sub(CM1);
  R = ROTL_11(R);
  CM2 = R ^ N2;

  N2 = N1;
  N1 = CM2;
 }

 CM1 = N1 + X7;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X6;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X5;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X4;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X3;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X2;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X1;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = N1;
 N1 = CM2;

 CM1 = N1 + X0;
 R = K_Sub(CM1);
 R = ROTL_11(R);
 CM2 = R ^ N2;

 N2 = CM2;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function K_Sub(w32)
{
 // do the s-box substitutions

 // the lower 4 bits in w32 go through K1

 // the upper 4 bits in w32 go through K8

 var tmp;

 var result = 0;

 for(var i=0;i<8;i++)
 {
  result = result >>> 4;

  // get the next 4 bits

  tmp = w32 & 0xf;

  // apply the substitution

  tmp = K_Arr[(i*16)+tmp] & 0xf;

  // update the result

  tmp = tmp << 28;

  result = result | tmp;

  w32 = w32 >>> 4;
 }

 return result & 0xffffffff;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function ROTL_11(w32)
{
 // left rotate w32 by 11 bits

 var tmp = (w32 << 11) | (w32 >>> 21);

 return tmp & 0xffffffff;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Read_Key()
{
 var str = document.getElementById("txtKey").value;

 // remove any non-hex characters

 str = StrToHexStr(str);

 // fix length to 64 hex digits

 // pad with zeroes

 while(str.length < 64) str += "0";

 if(str.length > 64) str = str.slice(0,64);

 // convert the key to an array of 32 bit words

 var X_Arr = [];

 X_Arr = HexStrToArray(str); 

 // initialise X0 .. X7

 X0 = X_Arr[0];
 X1 = X_Arr[1];
 X2 = X_Arr[2];
 X3 = X_Arr[3];
 X4 = X_Arr[4];
 X5 = X_Arr[5];
 X6 = X_Arr[6];
 X7 = X_Arr[7];

 // print X0 ... X7

 str = Print_Word(X0) + " " + Print_Word(X1) + " ";

 str += Print_Word(X2) + " " + Print_Word(X3) + " ";

 str += Print_Word(X4) + " " + Print_Word(X5) + " ";

 str += Print_Word(X6) + " " + Print_Word(X7);
 
 document.getElementById("txtKey").value = str;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Read_S()
{
 var str = document.getElementById("txtSVector").value;

 // remove any non-hex characters

 str = StrToHexStr(str);

 // fix length to 16 hex digits

 // pad with zeroes

 while(str.length < 16) str += "0";

 if(str.length > 16) str = str.slice(0,16);

 // convert the str to an array of 32 bit words

 var S = [];

 S = HexStrToArray(str); 

 // initialise S1 and S2

 S1 = S[0];

 S2 = S[1];

 // print S1 and S2

 str = Print_Word(S1) + " " + Print_Word(S2);
 
 document.getElementById("txtSVector").value = str;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Read_Input()
{
 var str = document.getElementById("txtInput").value;

 // remove any non-hex characters

 str = StrToHexStr(str);

 // fix length to a multiple of 16 hex digits (64 bits)

 // pad with zeroes

 while((str.length % 16) != 0) str += "0";

 // convert the str to an array of 32 bit words

 Tp_Arr = [];

 Tp_Arr = HexStrToArray(str); 

 Print_Tp();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function StrToHexStr(str)
{
 // remove all characters that are not hex digits:

 var ptn_not_hex = /[^0-9a-fA-F]{1,}/g; 

 str = str.replace(ptn_not_hex,"");

 return str;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function HexStrToArray(str)
{
 // convert the string to an array of 32 bit words:

 var hex_arr = [];

 for(var i=0;i<str.length/8;i++)
 {
  var tstr = str.substr(8*i,8);

  var tmp = parseInt(tstr,16);

  hex_arr[i] = tmp & 0xffffffff;
 }

 return hex_arr;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Default_S_Boxes()
{
 // load the default s-boxes

 GOST_34_11_94_S_Boxes();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function GOST_34_11_94_S_Boxes()
{
 // load the GOST 34.11-94 S-boxes

 K_Arr = [];

 var K8 = [0x1,0xF,0xD,0x0,0x5,0x7,0xA,0x4,0x9,0x2,0x3,0xE,0x6,0xB,0x8,0xC];
 var K7 = [0xD,0xB,0x4,0x1,0x3,0xF,0x5,0x9,0x0,0xA,0xE,0x7,0x6,0x8,0x2,0xC];
 var K6 = [0x4,0xB,0xA,0x0,0x7,0x2,0x1,0xD,0x3,0x6,0x8,0x5,0x9,0xC,0xF,0xE];
 var K5 = [0x6,0xC,0x7,0x1,0x5,0xF,0xD,0x8,0x4,0xA,0x9,0xE,0x0,0x3,0xB,0x2];
 var K4 = [0x7,0xD,0xA,0x1,0x0,0x8,0x9,0xF,0xE,0x4,0x6,0xC,0xB,0x2,0x5,0x3];
 var K3 = [0x5,0x8,0x1,0xD,0xA,0x3,0x4,0x2,0xE,0xF,0xC,0x7,0x6,0x0,0x9,0xB];
 var K2 = [0xE,0xB,0x4,0xC,0x6,0xD,0xF,0xA,0x2,0x3,0x8,0x1,0x0,0x7,0x5,0x9];
 var K1 = [0x4,0xA,0x9,0x2,0xD,0x8,0x0,0xE,0x6,0xB,0x1,0xC,0x7,0xF,0x5,0x3];

 K_Arr = K1.concat(K2,K3,K4,K5,K6,K7,K8);

 document.getElementById("cb_341194").checked=true;

 document.getElementById("cb_DES").checked=false;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function DES_S_Boxes()
{
 // load the DES S-boxes

 K_Arr = [];

 var K8 = [14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7];
 var K7 = [15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10];
 var K6 = [10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8];
 var K5 = [7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15];
 var K4 = [2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9];
 var K3 = [12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11];
 var K2 = [4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1];
 var K1 = [13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7];

 K_Arr = K1.concat(K2,K3,K4,K5,K6,K7,K8);

 document.getElementById("cb_341194").checked=false;

 document.getElementById("cb_DES").checked=true;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Set_GCTR()
{
 document.getElementById("cb_gctr").checked=true;

 document.getElementById("cb_cfb").checked=false;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Set_CFB()
{
 document.getElementById("cb_gctr").checked=false;

 document.getElementById("cb_cfb").checked=true;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Print_Word(w)
{
 // print a 32 bit word

 var str = "";

 str = (w>>>0).toString(16);
 
 while(str.length < 8) str = "0" + str;

 return str;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Print_Tp()
{
 // print the plain text

 var str = "";

 for(var i=0;i<Tp_Arr.length;i++)
 {
  if(i>0 && i%8==0) str += "\r\n";

  str += Print_Word(Tp_Arr[i]) + " ";
 }

 document.getElementById("txtInput").value = str;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Print_Tc()
{
 // print the cipher text

 var str = "";

 for(var i=0;i<Tc_Arr.length;i++)
 {
  if(i>0 && i%8==0) str += "\r\n";

  str += Print_Word(Tc_Arr[i]) + " ";
 }

 document.getElementById("txtOutput").value = str;
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Clear()
{
 document.getElementById("txtKey").value = "";

 document.getElementById("txtSVector").value = "";

 document.getElementById("txtInput").value = "";

 document.getElementById("txtOutput").value = "";

 Default_S_Boxes();

 Set_GCTR();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Encrypt()
{
 Read_Key();

 Read_S();

 Read_Input();

 if(document.getElementById("cb_gctr").checked == true)
 {
  Encrypt_GCTR();
 }
 else
 {
  Encrypt_CFB();
 }
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Decrypt()
{
 Read_Key();

 Read_S();

 Read_Input();

 if(document.getElementById("cb_gctr").checked == true)
 {
  Decrypt_GCTR();
 }
 else
 {
  Decrypt_CFB();
 }
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Encrypt_GCTR()
{
 // ===================================

 // Counter Mode:

 // ===================================

 // Tp is the plain text

 // Tc is the cipher text

 // Gc is the running key

 // ===================================

 // N1 = S1

 // N2 = S2

 // N1 N2 = ECB(N1 N2)

 // N3 = N1

 // N4 = N2

 // ===================================

 // for each 64 bit block of Tp:

 // ===================================

 // N1 = N3 = N3 + C2

 // N2 = N4 = N4 + C1

 // Gc(i) = N1 N2 = ECB(N1 N2)

 // Tc(i) = Gc(i) xor Tp(i)

 // ===================================

 Tc_Arr = [];

 N1 = S1;

 N2 = S2;

 Encrypt_ECB();

 var N3 = N1;

 var N4 = N2;

 for(var i=0;i<Tp_Arr.length;i+=2)
 {
  N1 = N3 = N3 + C2;

  N2 = N4 = N4 + C1;

  Encrypt_ECB();

  Tc_Arr[i] = Tp_Arr[i] ^ N1;

  Tc_Arr[i+1] = Tp_Arr[i+1] ^ N2;  
 }

 // print the cipher text

 Print_Tc();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Decrypt_GCTR()
{
 // ===================================

 // Counter Mode:

 // ===================================

 // Tp is the plain text

 // Tc is the cipher text

 // Gc is the running key

 // ===================================

 // N1 = S1

 // N2 = S2

 // N1 N2 = ECB(N1 N2)

 // N3 = N1

 // N4 = N2

 // ===================================

 // for each 64 bit block of Tc:

 // ===================================

 // N1 = N3 = N3 + C2

 // N2 = N4 = N4 + C1

 // Gc(i) = N1 N2 = ECB(N1 N2)

 // Tp(i) = Gc(i) xor Tc(i)

 // ===================================

 // Decrypt:

 // the cipher text Tc is in Tp_Arr

 // the plain text Tp is written to Tc_Arr

 // ===================================

 Tc_Arr = [];

 N1 = S1;

 N2 = S2;

 Encrypt_ECB();

 var N3 = N1;

 var N4 = N2;

 for(var i=0;i<Tp_Arr.length;i+=2)
 {
  N1 = N3 = N3 + C2;

  N2 = N4 = N4 + C1;

  Encrypt_ECB();

  Tc_Arr[i] = Tp_Arr[i] ^ N1;

  Tc_Arr[i+1] = Tp_Arr[i+1] ^ N2;  
 }

 // print the recovered plain text

 Print_Tc();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Encrypt_CFB()
{
 // ===================================

 // Cipher Feedback Mode:

 // ===================================

 // Tp is the plain text

 // Tc is the cipher text

 // Gc is the running key

 // ===================================

 // N1 = S1

 // N2 = S2

 // ===================================

 // for each 64 bit block of Tp:

 // ===================================

 // Gc(i) = N1 N2 = ECB(N1 N2)

 // Tc(i) = Gc(i) xor Tp(i)

 // N1 N2 = Tc(i)

 // ===================================

 Tc_Arr = [];

 N1 = S1;

 N2 = S2;

 for(var i=0;i<Tp_Arr.length;i+=2)
 {
  Encrypt_ECB();

  N1 = Tc_Arr[i] = Tp_Arr[i] ^ N1;

  N2 = Tc_Arr[i+1] = Tp_Arr[i+1] ^ N2;
 }

 // print the cipher text

 Print_Tc();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

function Decrypt_CFB()
{
 // ===================================

 // Cipher Feedback Mode:

 // ===================================

 // Tp is the plain text

 // Tc is the cipher text

 // Gc is the running key

 // ===================================

 // N1 = S1

 // N2 = S2

 // ===================================

 // for each 64 bit block of Tp:

 // ===================================

 // Gc(i) = N1 N2 = ECB(N1 N2)

 // Tp(i) = Gc(i) xor Tc(i)

 // N1 N2 = Tc(i)

 // ===================================

 // Decrypt:

 // the cipher text Tc is in Tp_Arr

 // the plain text Tp is written to Tc_Arr

 // ===================================

 Tc_Arr = [];

 N1 = S1;

 N2 = S2;

 for(var i=0;i<Tp_Arr.length;i+=2)
 {
  Encrypt_ECB();

  Tc_Arr[i] = Tp_Arr[i] ^ N1;

  Tc_Arr[i+1] = Tp_Arr[i+1] ^ N2;

  N1 = Tp_Arr[i];

  N2 = Tp_Arr[i+1];
 }

 // print the recovered plain text

 Print_Tc();
}

// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++

</script>

<body onload="Clear()">

<h2>GOST 28147-89 Encryption - Counter and Cipher Feedback Modes</h2>

<input type="button" value="Encrypt" onClick="Encrypt()">

<input type="button" value="Decrypt" onClick="Decrypt()">

<input type="button" value="Clear" onClick="Clear()">

<p>
Select the S-Box:
</p>

<p>
<input type="checkbox" id="cb_341194" onClick="GOST_34_11_94_S_Boxes()">34.11-94 S-Box
<input type="checkbox" id="cb_DES" onClick="DES_S_Boxes()">DES S-Box
</p>

<p>
Select the mode of encryption:
</p>

<p>
<input type="checkbox" id="cb_gctr" onClick="Set_GCTR()">Counter (GCTR)
<input type="checkbox" id="cb_cfb" onClick="Set_CFB()">Cipher Feedback (CFB)
</p>

<p>
Enter the 256 bit key (X0 X1 X2 X3 X4 X5 X6 X7) as 32 bit little endian values:
</p>

<textarea id="txtKey" rows="1" cols="80" spellcheck="false"></textarea>

<p>
Enter the 64 bit initialisation vector S1 S2 as 32 bit little endian values:
</p>

<textarea id="txtSVector" rows="1" cols="80" spellcheck="false"></textarea>

<p>
Enter the plain text Tp as 32 bit little endian values:
</p>

<textarea id="txtInput" rows="9" cols="80" spellcheck="false"></textarea>

<p>
The output cipher text Tc as 32 bit little endian values:
</p>

<textarea id="txtOutput" rows="9" cols="80" spellcheck="false"></textarea>

</body>

</html>
Advertisements

Blog at WordPress.com.

%d bloggers like this: