/**
  * The Elliptic Curve Diffe-Hellman Key Agreement Scheme as specified in ANSI X9.63 and IEEE P1363
  * In ECKAS-DH1 (the Elliptic Curve Key Agreement Scheme, Diffie-Hellman 1), each party combines
  * its own private key with the other party�fs public key to calculate a shared secret key which
  * can then be used as the key for a symmetric encryption algorithm such as AES. Other (public or
  * private) information known to both parties may be used as key derivation parameters to ensure
  * that a dierent secret key is generated every session. This key agreement scheme is described
  * in more detail in section 9.2 of the IEEEP1363 standard. This Calculates a 128 bit secret key
  * from EC domain parameters dp, private key s, public key Wi and key derivation parameter P (an
  * octet string). s belongs to one party, Wi belongs to the other and dp and P are common to both
  * of them.
  *
  * @param dp The EC domain parameters.
  * @param s The EC private key.
  * @param Wi The EC public key.
  * @param P The key derivation parameter.
  * @return
  */
 public static BigInteger ECKAS_DH1(ECDomainParameters dp, BigInteger s, ECPoint Wi, int[] P) {
   Fq z = ECSVDP_DH(dp, s, Wi);
   int[] Z = Utils.FE2OSP(z);
   int[] k = KDF2(Z, 16, P); // 128 bits
   BigInteger K = Utils.OS2IP(k);
   return K;
 }
  /**
   * The Advanced Encryption Symmetric (AES) encryption algorithm in Cipher Block Chaining (CBC)
   * mode with a null initialization vector.
   *
   * <p>The AES implementation used is the public domain optimised Java implementation of the
   * Rijndael (AES) block cipher by Paul Barreto. (see
   * com.dragongate_technologies.borZoi.internal.Rijndael)
   *
   * <p>Encrypt an octet string M with key KB of length keysize.
   *
   * @param KB the key
   * @param M the plaintext to be encrypted
   * @param keysize can be 128, 192 or 256 bits
   * @return the encrypted cipher text
   */
  public static int[] AES_CBC_IV0_Encrypt(int[] KB, int[] M, int keysize) {
    int padLen = 16 - (M.length % 16);
    int k = ((M.length + 1) / 16);
    if ((M.length + 1) % 16 != 0) k++;

    int P1[] = new int[1];
    P1[0] = padLen;
    int P2[] = new int[padLen];
    for (int l = 0; l < padLen; l++) {
      P2[l] = P1[0];
    }

    int T[] = Utils.concatenate(M, P2);
    int C[] = new int[16];
    int U[] = new int[16];

    for (int i = 1; i <= k; i++) {
      for (int j = 0; j < 16; j++) {
        if (i == 1) U[j] = T[(i - 1) * 16 + j];
        else U[j] = T[(i - 1) * 16 + j] ^ C[(i - 2) * 16 + j];
      }
      if (i == 1) C = Utils.toIntArray(Enc(Utils.toByteArray(U), Utils.toByteArray(KB), keysize));
      else
        C =
            Utils.concatenate(
                C, Utils.toIntArray(Enc(Utils.toByteArray(U), Utils.toByteArray(KB), keysize)));
    }
    return C;
  }
  /**
   * Key Derivation Function 2 (KDF2) from the IEEE P1363a draft standard. It generates a secret key
   * of length oLen bits from shared secret Z and key derivation parameter P.
   */
  public static int[] KDF2(int[] Z, int oLen, int[] P) {
    // Note:
    // oLen cannot be > hbits * (2^32-1) bits, because the size of an
    // int is 32 bits
    int[] K = new int[0];
    int[] CB = new int[1];
    int cThreshold = (oLen / 160);
    if (oLen % 160 != 0) cThreshold++;
    try {
      MessageDigest sha = MessageDigest.getInstance("SHA");

      // sha.update(data);
      // byte[] hash = sha.digest(data);
      for (byte i = 1; i <= cThreshold; i++) {
        CB[0] = i;
        sha.update(Utils.toByteArray(Utils.concatenate(Z, CB, P)));
        K = Utils.concatenate(K, Utils.revIntArray(Utils.toIntArray(sha.digest())));
        // K = Utils.concatenate(K, Utils.toIntArray(sha.digest()));
        sha.reset(); // not needed after diget()
      }
    } catch (NoSuchAlgorithmException e) {
    }
    // return Utils.resize (Utils.revIntArray(K), oLen);
    return Utils.resize(K, oLen);
  }
  /**
   * The Advanced Encryption Symmetric (AES) decryption algorithm in Cipher Block Chaining (CBC)
   * mode with a null initialization vector.
   *
   * <p>The AES implementation used is the public domain optimised Java implementation of the
   * Rijndael (AES) block cipher by Paul Barreto. (see
   * com.dragongate_technologies.borZoi.internal.Rijndael)
   *
   * <p>Decrypt an octet string C with key KB of length keysize.
   *
   * @param KB the key
   * @param C the ciphertext to be decrypted
   * @param keysize can be 128, 192 or 256 bits
   * @return the decrypted plain text
   */
  public static int[] AES_CBC_IV0_Decrypt(int[] KB, int[] C, int keysize) {
    if (C.length % 16 != 0) {
      throw (new RuntimeException("AES_CBC_IV0_Decrypt: C.length not a multiple of 16"));
    } else if (C.length < 16) {
      throw (new RuntimeException("AES_CBC_IV0_Decrypt: C.length < 16"));
    }
    int k = ((C.length + 1) / 16);

    int T[] = new int[C.length];
    int U[] = new int[16];
    int CI[] = new int[16];
    for (int i = 1; i <= k; i++) {
      for (int n = 0; n < 16; n++) {
        CI[n] = C[(i - 1) * 16 + n];
      }
      U = Utils.toIntArray(Dec(Utils.toByteArray(CI), Utils.toByteArray(KB), keysize));
      for (int j = 0; j < 16; j++) {
        if (i > 1) T[(i - 1) * 16 + j] = U[j] ^ C[(i - 2) * 16 + j];
        else T[j] = U[j];
      }
    }

    int padLen = T[(k * 16) - 1];
    if (padLen < 1) {
      throw (new RuntimeException("AES_CBC_IV0_Decrypt: padLen < 1"));
    } else if (padLen > 16) {
      throw (new RuntimeException("AES_CBC_IV0_Decrypt: padLen(" + padLen + ")>16"));
    }
    for (int l = 1; l < padLen; l++) {
      if (T[(k * 16) - 1 - l] != padLen) {
        throw (new RuntimeException("AES_CBC_IV0_Decrypt: OCTET != padLen"));
      }
    }
    int M[] = new int[T.length - padLen];
    for (int m = 0; m < T.length - padLen; m++) {
      M[m] = T[m];
    }
    return M;
  }
  /**
   * MAC1 as described in the IEEE P1363 standard. It computes a HMAC message authentication code
   * tag from secret key KB and message M.
   */
  public static int[] MAC1(int[] K, int[] M) {
    int[] HH = new int[0];
    try {
      int i;
      int[] KK;
      MessageDigest sha = MessageDigest.getInstance("SHA");
      // SHA1 Blocksize B = 512
      if (K.length > (8 * 512)) {
        sha.update(Utils.toByteArray(K));
        // kkLen = 20 octets, 160 bits
        KK = Utils.revIntArray(Utils.toIntArray(sha.digest()));
      } else KK = K;
      int[] P = new int[512 - KK.length];
      for (i = 0; i < P.length; i++) {
        P[i] = 0;
      }
      int[] K0 = Utils.concatenate(KK, P);
      int[] iPad = new int[512];
      for (i = 0; i < iPad.length; i++) {
        iPad[i] = 0x36;
      }
      int[] oPad = new int[512];
      for (i = 0; i < oPad.length; i++) {
        oPad[i] = 0x54;
      }
      sha.reset();
      sha.update(Utils.toByteArray(Utils.concatenate(Utils.xor(K0, iPad), M)));
      int[] H = Utils.revIntArray(Utils.toIntArray(sha.digest()));
      sha.reset();
      sha.update(Utils.toByteArray(Utils.concatenate(Utils.xor(K0, oPad), H)));
      HH = Utils.revIntArray(Utils.toIntArray(sha.digest()));
    } catch (NoSuchAlgorithmException e) {
    }

    return HH;
  }