Repository: wultra/powerauth-crypto (java)

Issue:
Crypto 3.0: Switch to byte array for counter in vault unlock
The `decryptVaultEncryptionKey() `and `encryptVaultEncryptionKey()` methods are marked as deprecated, however there is no available replacement for original version 2.0 logic which uses the counter. We should introduce a `byte[]` argument for the counter and switch to it, as it was done for other methods with numeric counter (for consistency reasons it is better to use `byte[]` than keeping original `long` type, although this method is only used for version 2.0 logic). The Crypto 3.0 vault encryption and decryption methods do not need a counter, so they are not affected. 

Deprecated methods:
https://github.com/wultra/powerauth-crypto/blob/features/crypto3/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/vault/PowerAuthClientVault.java#L55
https://github.com/wultra/powerauth-crypto/blob/features/crypto3/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/vault/PowerAuthServerVault.java#L56

Pull request:
Fix #211: Crypto 3.0: Switch to byte array for counter in vault unlock
Changes in this PR:
- Fix #211: Crypto 3.0: Switch to byte array for counter in vault unlock
- Key index used during key derivation uses byte array instead of long so that only one deriveSecretKey() method is used for both derivation using counter and using key

Patch:
diff --git a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/keyfactory/PowerAuthClientKeyFactory.java b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/keyfactory/PowerAuthClientKeyFactory.java
index 1d8c4236..23bb9888 100644
--- a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/keyfactory/PowerAuthClientKeyFactory.java
+++ b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/keyfactory/PowerAuthClientKeyFactory.java
@@ -158,7 +158,7 @@ public class PowerAuthClientKeyFactory {
      * Generate a signature key KEY_SIGNATURE_BIOMETRY from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey
      *            Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_SIGNATURE_BIOMETRY.
@@ -171,7 +171,7 @@ public class PowerAuthClientKeyFactory {
      * Generate a signature key KEY_SIGNATURE_KNOWLEDGE from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey
      *            Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_SIGNATURE_KNOWLEDGE.
@@ -184,7 +184,7 @@ public class PowerAuthClientKeyFactory {
      * Generate a signature key KEY_SIGNATURE_POSSESSION from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey
      *            Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_SIGNATURE_POSSESSION.
@@ -197,7 +197,7 @@ public class PowerAuthClientKeyFactory {
      * Generate a transport key KEY_ENCRYPTED_VAULT from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey
      *            Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_ENCRYPTED_VAULT.
@@ -210,7 +210,7 @@ public class PowerAuthClientKeyFactory {
      * Generate a transport key KEY_TRANSPORT from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey
      *            Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_TRANSPORT.
diff --git a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/vault/PowerAuthClientVault.java b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/vault/PowerAuthClientVault.java
index d032e15d..5564738f 100644
--- a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/vault/PowerAuthClientVault.java
+++ b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/client/vault/PowerAuthClientVault.java
@@ -41,8 +41,6 @@ public class PowerAuthClientVault {
      * Decrypts the vault encryption key KEY_ENCRYPTION_VAULT using a transport key
      * KEY_ENCRYPTION_VAULT_TRANSPORT.
      *
-     * @deprecated Use {@link #decryptVaultEncryptionKey(byte[], SecretKey)}
-     *
      * PowerAuth protocol version: 2.0
      *
      * @param cVaultEncryptionKey Encrypted vault encryption key KEY_ENCRYPTION_VAULT.
@@ -51,8 +49,7 @@ public class PowerAuthClientVault {
      * @return Original KEY_ENCRYPTION_VAULT
      * @throws InvalidKeyException In case invalid key is provided.
      */
-    @Deprecated
-    public SecretKey decryptVaultEncryptionKey(byte[] cVaultEncryptionKey, SecretKey masterTransportKey, long ctr) throws InvalidKeyException {
+    public SecretKey decryptVaultEncryptionKey(byte[] cVaultEncryptionKey, SecretKey masterTransportKey, byte[] ctr) throws InvalidKeyException {
         AESEncryptionUtils aes = new AESEncryptionUtils();
         CryptoProviderUtil keyConvertor = PowerAuthConfiguration.INSTANCE.getKeyConvertor();
         KeyGenerator keyGen = new KeyGenerator();
diff --git a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/enums/PowerAuthDerivedKey.java b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/enums/PowerAuthDerivedKey.java
index 1cce37b8..d512dee7 100644
--- a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/enums/PowerAuthDerivedKey.java
+++ b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/enums/PowerAuthDerivedKey.java
@@ -15,6 +15,7 @@
  */
 package io.getlime.security.powerauth.crypto.lib.enums;
 
+import java.nio.ByteBuffer;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -75,11 +76,11 @@ public enum PowerAuthDerivedKey {
     }
 
     /**
-     * Get the enum value (key index).
-     * @return Get the enum value (key index).
+     * Get the byte array value of key index.
+     * @return Get the byte array value of key index.
      */
-    public long getIndex() {
-        return index;
+    public byte[] getIndex() {
+        return ByteBuffer.allocate(16).putLong(0L).putLong(index).array();
     }
 
 }
\ No newline at end of file
diff --git a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/generator/KeyGenerator.java b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/generator/KeyGenerator.java
index 58afc167..e6537ea1 100644
--- a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/generator/KeyGenerator.java
+++ b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/lib/generator/KeyGenerator.java
@@ -23,7 +23,6 @@ import io.getlime.security.powerauth.provider.CryptoProviderUtil;
 import javax.crypto.*;
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
-import java.nio.ByteBuffer;
 import java.security.*;
 import java.security.spec.ECGenParameterSpec;
 import java.security.spec.InvalidKeySpecException;
@@ -145,19 +144,18 @@ public class KeyGenerator {
      * Derives a new secret key KEY_SHARED from a master secret key KEY_MASTER
      * based on following KDF:
      *
-     * BYTES = index, padded from left with 0x00, total 16 bytes
+     * BYTES = index, total 16 bytes
      * KEY_SHARED[BYTES] = AES(BYTES, KEY_MASTER)
      *
-     * @param secret A master shared key
-     * @param index An index of the key
+     * @param secret A master shared key.
+     * @param index A byte array index of the key.
      * @return A new derived key from a master key with given index.
      */
-    public SecretKey deriveSecretKey(SecretKey secret, long index) {
+    public SecretKey deriveSecretKey(SecretKey secret, byte[] index) {
         try {
             AESEncryptionUtils aes = new AESEncryptionUtils();
-            byte[] bytes = ByteBuffer.allocate(16).putLong(0L).putLong(index).array();
             byte[] iv = new byte[16];
-            byte[] encryptedBytes = aes.encrypt(bytes, iv, secret);
+            byte[] encryptedBytes = aes.encrypt(index, iv, secret);
             return PowerAuthConfiguration.INSTANCE.getKeyConvertor().convertBytesToSharedSecretKey(Arrays.copyOf(encryptedBytes, 16));
         } catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException ex) {
             Logger.getLogger(KeyGenerator.class.getName()).log(Level.SEVERE, null, ex);
diff --git a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/keyfactory/PowerAuthServerKeyFactory.java b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/keyfactory/PowerAuthServerKeyFactory.java
index 739295af..150a5c40 100644
--- a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/keyfactory/PowerAuthServerKeyFactory.java
+++ b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/keyfactory/PowerAuthServerKeyFactory.java
@@ -96,7 +96,7 @@ public class PowerAuthServerKeyFactory {
      * Generate a transport key KEY_ENCRYPTED_VAULT from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_ENCRYPTED_VAULT.
      */
@@ -126,7 +126,7 @@ public class PowerAuthServerKeyFactory {
      * Generate a signature key KEY_SIGNATURE_BIOMETRY from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_SIGNATURE_BIOMETRY.
      */
@@ -141,7 +141,7 @@ public class PowerAuthServerKeyFactory {
      * Generate a signature key KEY_SIGNATURE_KNOWLEDGE from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_SIGNATURE_KNOWLEDGE.
      */
@@ -156,7 +156,7 @@ public class PowerAuthServerKeyFactory {
      * Generate a signature key KEY_SIGNATURE_POSSESSION from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_SIGNATURE_POSSESSION.
      */
@@ -171,7 +171,7 @@ public class PowerAuthServerKeyFactory {
      * Generate a transport key KEY_TRANSPORT from master secret key
      * KEY_MASTER_SECRET using KDF.
      *
-     * @see KeyGenerator#deriveSecretKey(SecretKey, long)
+     * @see KeyGenerator#deriveSecretKey(SecretKey, byte[])
      * @param masterSecretKey Master secret key KEY_MASTER_SECRET.
      * @return An instance of signature key KEY_TRANSPORT.
      */
diff --git a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/vault/PowerAuthServerVault.java b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/vault/PowerAuthServerVault.java
index 0c2b2cf4..867ab39a 100644
--- a/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/vault/PowerAuthServerVault.java
+++ b/powerauth-java-crypto/src/main/java/io/getlime/security/powerauth/crypto/server/vault/PowerAuthServerVault.java
@@ -42,8 +42,6 @@ public class PowerAuthServerVault {
      * Return encrypted vault encryption key KEY_ENCRYPTION_VAULT using
      * a correct KEY_ENCRYPTION_VAULT_TRANSPORT.
      *
-     * @deprecated Use {@link #encryptVaultEncryptionKey(PrivateKey, PublicKey)}
-     *
      * PowerAuth protocol version: 2.0
      *
      * @param serverPrivateKey Server private key KEY_SERVER_PRIVATE
@@ -52,8 +50,7 @@ public class PowerAuthServerVault {
      * @return Encrypted vault encryption key.
      * @throws InvalidKeyException In case a provided key is incorrect.
      */
-    @Deprecated
-    public byte[] encryptVaultEncryptionKey(PrivateKey serverPrivateKey, PublicKey devicePublicKey, long ctr) throws InvalidKeyException {
+    public byte[] encryptVaultEncryptionKey(PrivateKey serverPrivateKey, PublicKey devicePublicKey, byte[] ctr) throws InvalidKeyException {
         try {
             KeyGenerator keyGenerator = new KeyGenerator();
             SecretKey keyMasterSecret = keyGenerator.computeSharedKey(serverPrivateKey, devicePublicKey);


Test patch:
diff --git a/powerauth-java-crypto/src/test/java/io/getlime/security/powerauth/crypto/vault/VaultTest.java b/powerauth-java-crypto/src/test/java/io/getlime/security/powerauth/crypto/vault/VaultTest.java
index 97e88e62..b20b7ce6 100644
--- a/powerauth-java-crypto/src/test/java/io/getlime/security/powerauth/crypto/vault/VaultTest.java
+++ b/powerauth-java-crypto/src/test/java/io/getlime/security/powerauth/crypto/vault/VaultTest.java
@@ -30,6 +30,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 import javax.crypto.SecretKey;
+import java.nio.ByteBuffer;
 import java.security.KeyPair;
 import java.security.PrivateKey;
 import java.security.Security;
@@ -38,35 +39,34 @@ import static org.junit.Assert.assertEquals;
 
 /**
  * Test the secure vault implementation.
- * 
- * @author Petr Dvorak
  *
+ * @author Petr Dvorak
  */
 public class VaultTest {
-	
-	/**
-	 * Register crypto providers.
-	 */
-	@Before
+
+    /**
+     * Register crypto providers.
+     */
+    @Before
     public void setUp() {
         // Add Bouncy Castle Security Provider
         Security.addProvider(new BouncyCastleProvider());
         PowerAuthConfiguration.INSTANCE.setKeyConvertor(CryptoProviderUtilFactory.getCryptoProviderUtils());
     }
-	
-	/**
-	 * Test the secure vault implementation.
+
+    /**
+     * Test the secure vault implementation.
      *
      * PowerAuth protocol version: 2.0
      *
-	 * @throws Exception In case the test fails.
-	 */
-	@Test
-	public void testVaultV2() throws Exception {
-		
-		System.out.println("# PowerAuth 2.0 Secure Vault");
+     * @throws Exception In case the test fails.
+     */
+    @Test
+    public void testVaultV2() throws Exception {
+
+        System.out.println("# PowerAuth 2.0 Secure Vault");
         System.out.println();
-        
+
         PowerAuthClientKeyFactory keyFactory = new PowerAuthClientKeyFactory();
 
         // Prepare test data
@@ -77,45 +77,46 @@ public class VaultTest {
         // Generate fake server and device keys
         KeyPair deviceKeyPair = keyGenerator.generateKeyPair();
         KeyPair serverKeyPair = keyGenerator.generateKeyPair();
-        
+
         // Deduce shared master secret keys
         SecretKey deviceMasterKey = keyGenerator.computeSharedKey(deviceKeyPair.getPrivate(), serverKeyPair.getPublic());
         SecretKey serverMasterKey = keyGenerator.computeSharedKey(serverKeyPair.getPrivate(), deviceKeyPair.getPublic());
         assertEquals(deviceMasterKey, serverMasterKey);
-        
+
         CryptoProviderUtil keyConvertor = PowerAuthConfiguration.INSTANCE.getKeyConvertor();
-        
+
         System.out.println("## Master Secret Key: " + BaseEncoding.base64().encode(keyConvertor.convertSharedSecretKeyToBytes(deviceMasterKey)));
-        
+
         // Deduce client vault encryption key and client / server master transport key
         SecretKey clientVaultEncryptionKey = keyFactory.generateServerEncryptedVaultKey(deviceMasterKey);
         System.out.println("## Vault Encryption Key: " + BaseEncoding.base64().encode(keyConvertor.convertSharedSecretKeyToBytes(clientVaultEncryptionKey)));
-        
+
         SecretKey clientTransportKey = keyGenerator.deriveSecretKey(deviceMasterKey, PowerAuthDerivedKey.TRANSPORT.getIndex());
         SecretKey serverTransportKey = keyGenerator.deriveSecretKey(serverMasterKey, PowerAuthDerivedKey.TRANSPORT.getIndex());
         assertEquals(clientTransportKey, serverTransportKey);
         System.out.println("## Master Transport Key: " + BaseEncoding.base64().encode(keyConvertor.convertSharedSecretKeyToBytes(clientTransportKey)));
-        
+
         // Encrypt device private key
         byte[] cDevicePrivateKey = clientVault.encryptDevicePrivateKey(deviceKeyPair.getPrivate(), clientVaultEncryptionKey);
-        
+
         // Get encrypted vault encryption key from the server
         for (long ctr = 0; ctr < 50; ctr++) {
 
-        	System.out.println();
+            System.out.println();
             System.out.println("## Counter: " + ctr);
-            
-        	byte[] cVaultEncryptionKey = serverVault.encryptVaultEncryptionKey(serverKeyPair.getPrivate(), deviceKeyPair.getPublic(), ctr);
+
+            byte[] ctrBytes = ByteBuffer.allocate(16).putLong(0L).putLong(ctr).array();
+            byte[] cVaultEncryptionKey = serverVault.encryptVaultEncryptionKey(serverKeyPair.getPrivate(), deviceKeyPair.getPublic(), ctrBytes);
             System.out.println("## cVaultEncryptionKey: " + BaseEncoding.base64().encode(cVaultEncryptionKey));
-            
-        	SecretKey vaultEncryptionKeyLocal = clientVault.decryptVaultEncryptionKey(cVaultEncryptionKey, clientTransportKey, ctr);
-        	assertEquals(clientVaultEncryptionKey, vaultEncryptionKeyLocal);
-        	
-        	PrivateKey devicePrivateKeyLocal = clientVault.decryptDevicePrivateKey(cDevicePrivateKey, vaultEncryptionKeyLocal);
-        	assertEquals(deviceKeyPair.getPrivate(), devicePrivateKeyLocal);
+
+            SecretKey vaultEncryptionKeyLocal = clientVault.decryptVaultEncryptionKey(cVaultEncryptionKey, clientTransportKey, ctrBytes);
+            assertEquals(clientVaultEncryptionKey, vaultEncryptionKeyLocal);
+
+            PrivateKey devicePrivateKeyLocal = clientVault.decryptDevicePrivateKey(cDevicePrivateKey, vaultEncryptionKeyLocal);
+            assertEquals(deviceKeyPair.getPrivate(), devicePrivateKeyLocal);
         }
 
-	}
+    }
 
     /**
      * Test the secure vault implementation.