content: package im.actor.runtime.crypto.primitives.kuznechik;
  
  // Disabling Bounds checks for speeding up calculations
  
  /*-[
  #define J2OBJC_DISABLE_ALL_CHECKS 1
  ]-*/
  
  /**
   * Mathematical methods for Kuzhechik encryption
   * <p>
   * Ported by Steven Kite (steve@actor.im) from
   * https://github.com/mjosaarinen/kuznechik/blob/master/kuznechik_8bit.c
   * Multiplication optimization from
   * http://www.cs.utsa.edu/~wagner/laws/FFM.html
   */
  class KuznechikMath {
  
      // poly multiplication mod p(x) = x^8 + x^7 + x^6 + x + 1
      // totally not constant time
      public static byte kuz_mul_gf256(byte x, byte y) {
          // uint8_t z;
          // z = 0;
          byte z = 0;
  
          // while (y) {
          while ((y & 0xFF) != 0) {
              // if (y & 1)
              if ((y & 1) != 0) {
                  // z ^= x;
                  z ^= x;
              }
  
              // x = (x << 1) ^ (x & 0x80 ? 0xC3 : 0x00);
              x = (byte) (((x & 0xFF) << 1) ^ ((x & 0x80) != 0 ? 0xC3 : 0x00));
              // y >>= 1;
              y = (byte) ((y & 0xFF) >> 1);
          }
  
          // return z;
          return z;
      }
  
      // Fast implementation of multiplication in GF(2^8) on x^8 + x^7 + x^6 + x + 1
      // Implemented with
      public static byte kuz_mul_gf256_fast(byte a, byte b) {
          if (a == 0 || b == 0) return 0;
          int t = (KuznechikTables.gf256_L[(a & 0xff)] & 0xff) + (KuznechikTables.gf256_L[(b & 0xff)] & 0xff);
          if (t > 255) t = t - 255;
          return KuznechikTables.gf256_E[(t & 0xff)];
      }
  
      // linear operation l
      // static void kuz_l(w128_t *w)
      public static void kuz_l(Kuz128 w) {
          // 16 rounds
          for (int j = 0; j < 16; j++) {
              // An LFSR with 16 elements from GF(2^8)
              // x = w->b[15];	// since lvec[15] = 1
              byte x = w.getB()[15];
  
              for (int i = 14; i >= 0; i--) {
                  // w->b[i + 1] = w->b[i];
                  w.getB()[i + 1] = w.getB()[i];
                  // x ^= kuz_mul_gf256(w->b[i], kuz_lvec[i]);
                  x ^= kuz_mul_gf256_fast(w.getB()[i], KuznechikTables.kuz_lvec[i]);
              }
              w.getB()[0] = x;
          }
      }
  
      // inverse of linear operation l
      public static void kuz_l_inv(Kuz128 w) {
  
          // 16 rounds
          for (int j = 0; j < 16; j++) {
  
              // x = w->b[0];
              byte x = w.getB()[0];
  
              for (int i = 0; i < 15; i++) {
  
                  // w->b[i] = w->b[i + 1];
                  w.getB()[i] = w.getB()[i + 1];
  
                  // x ^= kuz_mul_gf256(w->b[i], kuz_lvec[i]);
                  x ^= kuz_mul_gf256_fast(w.getB()[i], KuznechikTables.kuz_lvec[i]);
              }
  
              // w->b[15] = x;
              w.getB()[15] = x;
          }
      }
  }
  