GOST 28147-89 is a standard symmetric encryption based on a Feistel network.



The structure of the algorithm consists of three levels:

   encryption modes - simple replacement, application range, imposing a range of feedback and authentication code generation;
   cycles - 32-З, 32-Р and 16-З, is a repetition of the main step;
   main step, a function that takes a 64-bit block of text and one of the eight 32-bit encryption key elements, and uses the replacement table (8x16 matrix of 4-bit values), and returns encrypted block.


Task

Implement the main step of this encryption algorithm.




class GOST{
var [const] // Central Bank of Russian Federation S-Boxes
  k8=Data(0,Int, 4, 10,  9,  2, 13,  8,  0, 14,  6, 11,  1, 12,  7, 15,  5,  3),
  k7=Data(0,Int,14, 11,  4, 12,  6, 13, 15, 10,  2,  3,  8,  1,  0,  7,  5,  9),
  k6=Data(0,Int, 5,  8,  1, 13, 10,  3,  4,  2, 14, 15, 12,  7,  6,  0,  9, 11),
  k5=Data(0,Int, 7, 13, 10,  1,  0,  8,  9, 15, 14,  4,  6, 12, 11,  2,  5,  3),
  k4=Data(0,Int, 6, 12,  7,  1,  5, 15, 13,  8,  4, 10,  9, 14,  0,  3, 11,  2),
  k3=Data(0,Int, 4, 11, 10,  0,  7,  2,  1, 13,  3,  6,  8,  5,  9, 12, 15, 14),
  k2=Data(0,Int,13, 11,  4,  1,  3, 15,  5,  9,  0, 10, 14,  7,  6,  8,  2, 12),
  k1=Data(0,Int, 1, 15, 13,  0,  5,  7, 10,  4,  9,  2,  3, 14,  6, 11,  8, 12);
 
fcn generate(ka,kb)
   { (0).pump(256,Data,'wrap(i){ kb[i/0x10]*0x10 + ka[i%0x10] }) }
var [const] k87=generate(k8,k7), k65=generate(k6,k5),
	    k43=generate(k4,k3), k21=generate(k2,k1);
 
fcn f(x){ // int --> int
   x3,x2,x1,x0:=x.toLittleEndian(4);
   x=k87[x3] + k65[x2]*0x|100 + k43[x1]*0x1|0000 + k21[x0]*0x100|0000;
   x.shiftLeft(11) + x.shiftRight(21); // roll left 11 bits, leaving bits on top
}
 
fcn mainStep(input,key){ // input is stream of bytes, little endian 32 bit words
   r:=Data();
   foreach idx in ([0..input.len()-1,8]){
      w0:=input.toLittleEndian(idx,  4);
      w1:=input.toLittleEndian(idx+4,4);
      r.write(f(key+w0).bitXor(w1).toLittleEndian(4),w0.toLittleEndian(4));
   }
   r
}}