/**
  * Test that the keys are correctly generated.
  *
  * @throws Exception When test fails.
  */
 @Test
 public void testGenerateKeys() throws Exception {
   CryptoProviderUtil keyConvertor = PowerAuthConfiguration.INSTANCE.getKeyConvertor();
   KeyGenerator keyGenerator = new KeyGenerator();
   KeyPair kp = keyGenerator.generateKeyPair();
   System.out.println(
       "Private Key: "
           + BaseEncoding.base64().encode(keyConvertor.convertPrivateKeyToBytes(kp.getPrivate())));
   System.out.println(
       "Public Key: "
           + BaseEncoding.base64().encode(keyConvertor.convertPublicKeyToBytes(kp.getPublic())));
 }
  /**
   * Encrypt the server public key using activation OTP and device public key. As a technical
   * component for public key encryption, an ephemeral private key is used (in order to deduce
   * ephemeral symmetric key using ECDH).
   *
   * @param serverPublicKey Server public key to be encrypted.
   * @param devicePublicKey Device public key used for encryption.
   * @param ephemeralPrivateKey Ephemeral private key.
   * @param activationOTP Activation OTP value.
   * @param activationIdShort Short activation ID.
   * @param activationNonce Activation nonce, used as an initialization vector for AES encryption.
   * @return Encrypted server public key.
   * @throws InvalidKeyException In case some of the provided keys is invalid.
   */
  public byte[] encryptServerPublicKey(
      PublicKey serverPublicKey,
      PublicKey devicePublicKey,
      PrivateKey ephemeralPrivateKey,
      String activationOTP,
      String activationIdShort,
      byte[] activationNonce)
      throws InvalidKeyException {
    try {

      // Convert public key to bytes
      byte[] serverPublicKeyBytes =
          PowerAuthConfiguration.INSTANCE
              .getKeyConvertor()
              .convertPublicKeyToBytes(serverPublicKey);

      // Generate symmetric keys
      KeyGenerator keyGenerator = new KeyGenerator();
      SecretKey ephemeralSymmetricKey =
          keyGenerator.computeSharedKey(ephemeralPrivateKey, devicePublicKey);

      byte[] activationIdShortBytes = activationIdShort.getBytes("UTF-8");
      SecretKey otpBasedSymmetricKey =
          keyGenerator.deriveSecretKeyFromPassword(activationOTP, activationIdShortBytes);

      // Encrypt the data
      AESEncryptionUtils aes = new AESEncryptionUtils();
      byte[] encryptedTMP =
          aes.encrypt(serverPublicKeyBytes, activationNonce, otpBasedSymmetricKey);
      byte[] encryptServerPublicKey =
          aes.encrypt(encryptedTMP, activationNonce, ephemeralSymmetricKey);
      return encryptServerPublicKey;

    } catch (IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException ex) {
      Logger.getLogger(PowerAuthServerActivation.class.getName()).log(Level.SEVERE, null, ex);
    }
    return null;
  }
  /**
   * Test of the complete activation process, orchestration between client and server.
   *
   * @throws Exception In case test fails
   */
  @Test
  public void testActivationProcess() throws Exception {

    System.out.println("TEST: Activation Process");

    // Prepare test data
    KeyGenerator keyGenerator = new KeyGenerator();
    PowerAuthClientActivation clientActivation = new PowerAuthClientActivation();
    PowerAuthServerActivation serverActivation = new PowerAuthServerActivation();
    KeyPair masterKeyPair = keyGenerator.generateKeyPair();

    // Generate master keypair
    PrivateKey masterPrivateKey = masterKeyPair.getPrivate();
    PublicKey masterPublicKey = masterKeyPair.getPublic();

    for (int i = 0; i < 20; i++) {

      // SERVER: Generate data for activation
      String activationId = serverActivation.generateActivationId();
      String activationIdShort = serverActivation.generateActivationIdShort();
      String activationOTP = serverActivation.generateActivationOTP();
      byte[] activationSignature =
          serverActivation.generateActivationSignature(
              activationIdShort, activationOTP, masterPrivateKey);
      KeyPair serverKeyPair = serverActivation.generateServerKeyPair();
      PrivateKey serverPrivateKey = serverKeyPair.getPrivate();
      PublicKey serverPublicKey = serverKeyPair.getPublic();

      // CLIENT: Verify activation signature
      boolean activationSignatureOK =
          clientActivation.verifyActivationDataSignature(
              activationIdShort, activationOTP, activationSignature, masterPublicKey);
      assertTrue(activationSignatureOK);

      // CLIENT: Generate and send public key
      KeyPair deviceKeyPair = clientActivation.generateDeviceKeyPair();
      KeyPair clientEphemeralKeyPair = keyGenerator.generateKeyPair();
      PrivateKey devicePrivateKey = deviceKeyPair.getPrivate();
      PublicKey devicePublicKey = deviceKeyPair.getPublic();
      byte[] clientNonce = clientActivation.generateActivationNonce();
      byte[] c_devicePublicKey =
          clientActivation.encryptDevicePublicKey(
              devicePublicKey,
              clientEphemeralKeyPair.getPrivate(),
              masterPublicKey,
              activationOTP,
              activationIdShort,
              clientNonce);

      // SERVER: Decrypt device public key
      PublicKey decryptedDevicePublicKey =
          serverActivation.decryptDevicePublicKey(
              c_devicePublicKey,
              activationIdShort,
              masterPrivateKey,
              clientEphemeralKeyPair.getPublic(),
              activationOTP,
              clientNonce);
      assertEquals(devicePublicKey, decryptedDevicePublicKey);

      // SERVER: Encrypt and send encrypted server public and it's signature
      KeyPair ephemeralKeyPair = keyGenerator.generateKeyPair();
      PrivateKey ephemeralPrivateKey = ephemeralKeyPair.getPrivate();
      PublicKey ephemeralPublicKey = ephemeralKeyPair.getPublic();
      byte[] serverNonce = serverActivation.generateActivationNonce();
      byte[] c_serverPublicKey =
          serverActivation.encryptServerPublicKey(
              serverPublicKey,
              devicePublicKey,
              ephemeralPrivateKey,
              activationOTP,
              activationIdShort,
              serverNonce);
      byte[] c_serverPublicKeySignature =
          serverActivation.computeServerDataSignature(
              activationId, c_serverPublicKey, masterPrivateKey);

      // CLIENT: Validate server public key signature and decrypt server public key
      boolean serverPublicKeySignatureOK =
          clientActivation.verifyServerDataSignature(
              activationId, c_serverPublicKey, c_serverPublicKeySignature, masterPublicKey);
      assertTrue(serverPublicKeySignatureOK);

      PublicKey decryptedServerPublicKey =
          clientActivation.decryptServerPublicKey(
              c_serverPublicKey,
              devicePrivateKey,
              ephemeralPublicKey,
              activationOTP,
              activationIdShort,
              serverNonce);
      assertEquals(serverPublicKey, decryptedServerPublicKey);

      // CLIENT and SERVER: Compute device public key fingerprint
      int devicePublicKeyFingerprintClient =
          clientActivation.computeDevicePublicKeyFingerprint(devicePublicKey);
      int devicePublicKeyFingerprintServer =
          serverActivation.computeDevicePublicKeyFingerprint(decryptedDevicePublicKey);
      assertEquals(devicePublicKeyFingerprintClient, devicePublicKeyFingerprintServer);

      // CLIENT and SERVER: Compute shared master secret
      SecretKey sharedMasterSecretDevice =
          keyGenerator.computeSharedKey(devicePrivateKey, serverPublicKey);
      SecretKey sharedMasterSecretServer =
          keyGenerator.computeSharedKey(serverPrivateKey, devicePublicKey);
      assertEquals(sharedMasterSecretDevice, sharedMasterSecretServer);
    }
  }