diff --git a/src/main/java/org/cryptomator/cryptolib/common/MasterkeyHubAccess.java b/src/main/java/org/cryptomator/cryptolib/common/MasterkeyHubAccess.java
deleted file mode 100644
index be0fef0..0000000
--- a/src/main/java/org/cryptomator/cryptolib/common/MasterkeyHubAccess.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.cryptomator.cryptolib.common;
-
-import com.google.common.io.BaseEncoding;
-import org.cryptomator.cryptolib.api.Masterkey;
-import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
-import org.cryptomator.cryptolib.ecies.EncryptedMessage;
-import org.cryptomator.cryptolib.ecies.ECIntegratedEncryptionScheme;
-
-import javax.crypto.AEADBadTagException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.Arrays;
-
-public class MasterkeyHubAccess {
-
- private static final BaseEncoding BASE64_URL = BaseEncoding.base64Url().omitPadding();
-
- private MasterkeyHubAccess() {
- }
-
- /**
- * Decrypts a masterkey retrieved from Cryptomator Hub
- *
- * @param devicePrivateKey Private key of the device this ciphertext is intended for
- * @param encodedCiphertext The encrypted masterkey
- * @param encodedEphPubKey The ephemeral public key to be used to derive a secret shared between message sender and this device
- * @return The decrypted masterkey
- * @throws MasterkeyLoadingFailedException If the parameters don't match and decryption fails
- */
- public static Masterkey decryptMasterkey(ECPrivateKey devicePrivateKey, String encodedCiphertext, String encodedEphPubKey) throws MasterkeyLoadingFailedException {
- byte[] cleartext = new byte[0];
- try {
- EncryptedMessage message = decode(encodedCiphertext, encodedEphPubKey);
- cleartext = ECIntegratedEncryptionScheme.HUB.decrypt(devicePrivateKey, message);
- return new Masterkey(cleartext);
- } catch (IllegalArgumentException | AEADBadTagException e) {
- throw new MasterkeyLoadingFailedException("Key and ciphertext don't match", e);
- } finally {
- Arrays.fill(cleartext, (byte) 0x00);
- }
- }
-
- private static EncryptedMessage decode(String encodedCiphertext, String encodedEphPubKey) throws IllegalArgumentException {
- byte[] ciphertext = BASE64_URL.decode(encodedCiphertext);
- byte[] keyBytes = BASE64_URL.decode(encodedEphPubKey);
- try {
- PublicKey key = KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(keyBytes));
- if (key instanceof ECPublicKey) {
- return new EncryptedMessage((ECPublicKey) key, ciphertext);
- } else {
- throw new IllegalArgumentException("Key not an EC public key.");
- }
- } catch (InvalidKeySpecException e) {
- throw new IllegalArgumentException("Invalid license public key", e);
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException(e);
- }
- }
-
-}
diff --git a/src/main/java/org/cryptomator/cryptolib/ecies/AuthenticatedEncryption.java b/src/main/java/org/cryptomator/cryptolib/ecies/AuthenticatedEncryption.java
deleted file mode 100644
index 654ff14..0000000
--- a/src/main/java/org/cryptomator/cryptolib/ecies/AuthenticatedEncryption.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import javax.crypto.AEADBadTagException;
-
-public interface AuthenticatedEncryption {
-
- /**
- * AES-GCM with a 96 bit nonce taken from a the shared secret.
- *
- * Since the secret is derived via ECDH with an ephemeral key, the nonce is guaranteed to be unique.
- */
- AuthenticatedEncryption GCM_WITH_SECRET_NONCE = new GcmWithSecretNonce();
-
- /**
- * @return number of bytes required during {@link #encrypt(byte[], byte[])} and {@link #decrypt(byte[], byte[])}
- */
- int requiredSecretBytes();
-
- /**
- * Encrypts the given plaintext
- *
- * @param secret secret data required for encryption, such as (but not limited to) a key
- * @param plaintext The data to encrypt
- * @return The encrypted data, including all required information for authentication, such as a tag
- */
- byte[] encrypt(byte[] secret, byte[] plaintext);
-
- /**
- * Encrypts the given ciphertext
- *
- * @param secret secret data required for encryption, such as (but not limited to) a key
- * @param ciphertext The data to decrypt
- * @return The decrypted data
- * @throws AEADBadTagException In case of an authentication failure (including wrong secret
)
- */
- byte[] decrypt(byte[] secret, byte[] ciphertext) throws AEADBadTagException;
-}
diff --git a/src/main/java/org/cryptomator/cryptolib/ecies/ECIntegratedEncryptionScheme.java b/src/main/java/org/cryptomator/cryptolib/ecies/ECIntegratedEncryptionScheme.java
deleted file mode 100644
index 1df67f1..0000000
--- a/src/main/java/org/cryptomator/cryptolib/ecies/ECIntegratedEncryptionScheme.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import org.cryptomator.cryptolib.common.Destroyables;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.KeyAgreement;
-import java.security.InvalidKeyException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.util.Arrays;
-
-public class ECIntegratedEncryptionScheme {
-
- /**
- * The ECIES used in Cryptomator Hub:
- *
- * - To be used with {@link org.cryptomator.cryptolib.common.P384KeyPair P-384 EC keys}
- * - Use ANSI X9.63 KDF with SHA-256 to derive a 352 bit shared secret
- * - Cut shared secret into 256 bit key + 96 bit nonce used for AES-GCM to encrypt/decrypt
- *
- */
- public static final ECIntegratedEncryptionScheme HUB = new ECIntegratedEncryptionScheme(AuthenticatedEncryption.GCM_WITH_SECRET_NONCE, KeyDerivationFunction.ANSI_X963_SHA256_KDF);
-
- private final AuthenticatedEncryption ae;
- private final KeyDerivationFunction kdf;
-
- public ECIntegratedEncryptionScheme(AuthenticatedEncryption ae, KeyDerivationFunction kdf) {
- this.ae = ae;
- this.kdf = kdf;
- }
-
- public EncryptedMessage encrypt(KeyPairGenerator ephKeyGen, ECPublicKey receiverPublicKey, byte[] plaintext) {
- KeyPair ephKeyPair = ephKeyGen.generateKeyPair();
- try {
- if (ephKeyPair.getPrivate() instanceof ECPrivateKey) {
- assert ephKeyPair.getPublic() instanceof ECPublicKey;
- byte[] ciphertext = encrypt((ECPrivateKey) ephKeyPair.getPrivate(), receiverPublicKey, plaintext);
- return new EncryptedMessage((ECPublicKey) ephKeyPair.getPublic(), ciphertext);
- } else {
- throw new IllegalArgumentException("key generator didn't create EC key pair");
- }
- } finally {
- Destroyables.destroySilently(ephKeyPair.getPrivate());
- }
- }
-
- public byte[] decrypt(ECPrivateKey receiverPrivateKey, EncryptedMessage encryptedMessage) throws AEADBadTagException {
- return decrypt(receiverPrivateKey, encryptedMessage.getEphPublicKey(), encryptedMessage.getCiphertext());
- }
-
- // visible for testing
- byte[] encrypt(ECPrivateKey ephPrivateKey, ECPublicKey receiverPublicKey, byte[] plaintext) {
- byte[] secret = ecdhAndKdf(ephPrivateKey, receiverPublicKey, ae.requiredSecretBytes());
- return ae.encrypt(secret, plaintext);
- }
-
- // visible for testing
- byte[] decrypt(ECPrivateKey receiverPrivateKey, ECPublicKey ephPublicKey, byte[] plaintext) throws AEADBadTagException {
- byte[] secret = ecdhAndKdf(receiverPrivateKey, ephPublicKey, ae.requiredSecretBytes());
- return ae.decrypt(secret, plaintext);
- }
-
- private byte[] ecdhAndKdf(ECPrivateKey privateKey, ECPublicKey publicKey, int numBytes) {
- byte[] sharedSecret = new byte[0];
- try {
- KeyAgreement keyAgreement = createKeyAgreement();
- keyAgreement.init(privateKey);
- keyAgreement.doPhase(publicKey, true);
- sharedSecret = keyAgreement.generateSecret();
- return kdf.deriveKey(sharedSecret, numBytes);
- } catch (InvalidKeyException e) {
- throw new IllegalArgumentException("Invalid keys", e);
- } finally {
- Arrays.fill(sharedSecret, (byte) 0x00);
- }
- }
-
- private static KeyAgreement createKeyAgreement() {
- try {
- return KeyAgreement.getInstance("ECDH");
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("ECDH not supported");
- }
- }
-
-
-}
diff --git a/src/main/java/org/cryptomator/cryptolib/ecies/EncryptedMessage.java b/src/main/java/org/cryptomator/cryptolib/ecies/EncryptedMessage.java
deleted file mode 100644
index 08657bc..0000000
--- a/src/main/java/org/cryptomator/cryptolib/ecies/EncryptedMessage.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import java.security.interfaces.ECPublicKey;
-
-public class EncryptedMessage {
-
- private final ECPublicKey ephPublicKey;
- private final byte[] ciphertext;
-
- public EncryptedMessage(ECPublicKey ephPublicKey, byte[] ciphertext) {
- this.ephPublicKey = ephPublicKey;
- this.ciphertext = ciphertext;
- }
-
- public ECPublicKey getEphPublicKey() {
- return ephPublicKey;
- }
-
- public byte[] getCiphertext() {
- return ciphertext;
- }
-
-}
diff --git a/src/main/java/org/cryptomator/cryptolib/ecies/GcmWithSecretNonce.java b/src/main/java/org/cryptomator/cryptolib/ecies/GcmWithSecretNonce.java
deleted file mode 100644
index 093af59..0000000
--- a/src/main/java/org/cryptomator/cryptolib/ecies/GcmWithSecretNonce.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import com.google.common.base.Throwables;
-import org.cryptomator.cryptolib.common.CipherSupplier;
-import org.cryptomator.cryptolib.common.DestroyableSecretKey;
-import org.cryptomator.cryptolib.common.ObjectPool;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.spec.GCMParameterSpec;
-import java.util.Arrays;
-
-class GcmWithSecretNonce implements AuthenticatedEncryption {
-
- private static final int GCM_KEY_SIZE = 32;
- private static final int GCM_TAG_SIZE = 16;
- private static final int GCM_NONCE_SIZE = 12; // 96 bit IVs strongly recommended for GCM
-
- @Override
- public int requiredSecretBytes() {
- return GCM_KEY_SIZE + GCM_NONCE_SIZE;
- }
-
- @Override
- public byte[] encrypt(byte[] secret, byte[] plaintext) {
- try (DestroyableSecretKey key = new DestroyableSecretKey(secret, 0, GCM_KEY_SIZE, "AES")) {
- byte[] nonce = Arrays.copyOfRange(secret, GCM_KEY_SIZE, GCM_KEY_SIZE + GCM_NONCE_SIZE);
- try (ObjectPool.Lease cipher = CipherSupplier.AES_GCM.encryptionCipher(key, new GCMParameterSpec(GCM_TAG_SIZE * Byte.SIZE, nonce))) {
- return cipher.get().doFinal(plaintext);
- }
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- throw new IllegalStateException("Unexpected exception during GCM decryption.", e);
- }
- }
-
- @Override
- public byte[] decrypt(byte[] secret, byte[] ciphertext) throws AEADBadTagException {
- try (DestroyableSecretKey key = new DestroyableSecretKey(secret, 0, GCM_KEY_SIZE, "AES")) {
- byte[] nonce = Arrays.copyOfRange(secret, GCM_KEY_SIZE, GCM_KEY_SIZE + GCM_NONCE_SIZE);
- try (ObjectPool.Lease cipher = CipherSupplier.AES_GCM.decryptionCipher(key, new GCMParameterSpec(GCM_TAG_SIZE * Byte.SIZE, nonce))) {
- return cipher.get().doFinal(ciphertext);
- }
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- Throwables.throwIfInstanceOf(e, AEADBadTagException.class);
- throw new IllegalStateException("Unexpected exception during GCM decryption.", e);
- }
- }
-}
diff --git a/src/main/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunction.java b/src/main/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunction.java
deleted file mode 100644
index 0b8ecf0..0000000
--- a/src/main/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunction.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import org.cryptomator.cryptolib.common.MessageDigestSupplier;
-import org.cryptomator.cryptolib.common.ObjectPool;
-
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.DigestException;
-import java.security.MessageDigest;
-import java.util.Arrays;
-
-@FunctionalInterface
-public interface KeyDerivationFunction {
-
- KeyDerivationFunction ANSI_X963_SHA256_KDF = (sharedSecret, keyDataLen) -> ansiX963sha256Kdf(sharedSecret, new byte[0], keyDataLen);
-
- /**
- * Derives a key of desired length
- *
- * @param sharedSecret A shared secret
- * @param keyDataLen Desired key length (in bytes)
- * @return key data
- */
- byte[] deriveKey(byte[] sharedSecret, int keyDataLen);
-
- /**
- * Performs ANSI-X9.63-KDF with SHA-256
- *
- * @param sharedSecret A shared secret
- * @param sharedInfo Additional authenticated data
- * @param keyDataLen Desired key length (in bytes)
- * @return key data
- */
- static byte[] ansiX963sha256Kdf(byte[] sharedSecret, byte[] sharedInfo, int keyDataLen) {
- // max input length is 2^64 - 1, see https://doi.org/10.6028/NIST.SP.800-56Cr2, Table 1
- int hashLen = 32; // fixed digest length for SHA-256 in bytes
-
- // These two checks must be performed according to spec. However with 32 bit integers, we can't exceed any limits anyway:
- assert BigInteger.valueOf(4L + sharedSecret.length + sharedInfo.length).compareTo(BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE)) < 0 : "input larger than hashmaxlen";
- assert keyDataLen < (1L << 32 - 1) * hashLen : "keyDataLen larger than hashLen × (2^32 − 1)";
-
- ByteBuffer counter = ByteBuffer.allocate(Integer.BYTES);
- assert ByteOrder.BIG_ENDIAN.equals(counter.order());
- int n = (keyDataLen + hashLen - 1) / hashLen;
- byte[] buffer = new byte[n * hashLen];
- try (ObjectPool.Lease sha256 = MessageDigestSupplier.SHA256.instance()) {
- for (int i = 0; i < n; i++) {
- sha256.get().update(sharedSecret);
- counter.clear();
- counter.putInt(i + 1);
- counter.flip();
- sha256.get().update(counter);
- sha256.get().update(sharedInfo);
- sha256.get().digest(buffer, i * hashLen, hashLen);
- }
- return Arrays.copyOf(buffer, keyDataLen);
- } catch (DigestException e) {
- throw new IllegalStateException("Invalid digest output buffer offset", e);
- } finally {
- Arrays.fill(buffer, (byte) 0x00);
- }
- }
-
-}
diff --git a/src/test/java/org/cryptomator/cryptolib/common/MasterkeyHubAccessTest.java b/src/test/java/org/cryptomator/cryptolib/common/MasterkeyHubAccessTest.java
deleted file mode 100644
index 7b779ef..0000000
--- a/src/test/java/org/cryptomator/cryptolib/common/MasterkeyHubAccessTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.cryptomator.cryptolib.common;
-
-import com.google.common.io.BaseEncoding;
-import org.cryptomator.cryptolib.api.Masterkey;
-import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Arrays;
-
-public class MasterkeyHubAccessTest {
-
- private ECPrivateKey devicePrivateKey;
-
- @BeforeEach
- public void setup() throws NoSuchAlgorithmException, InvalidKeySpecException {
- byte[] keyBytes = BaseEncoding.base64Url().decode("ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDDzj9mBnoqoYTO0wQDvM2iyI2wrNe468US1mHMjdJcKWGGvky4pMexIvmvmDsZLdsY");
- this.devicePrivateKey = (ECPrivateKey) KeyFactory.getInstance("EC").generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
- }
-
- @Test
- @DisplayName("decryptMasterkey(...)")
- public void testDecrypt() {
- String ephPk = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEkcv3x-hkCnb8Kr8TNfaLpD4q64ZqPn4p1yuM8r2r16h6f6mG01kFBp2EoY575bCcmT54PxiDFkf3KKqHXFjZwBhdm6zMp22l37ZlmKyHG96vkB7Rh6qFyzEhSQ_nvl2G";
- String ciphertext = "KQ48XS6ziW3tS7SMLR5sc2o_Y80OR4SS_htHpk8SHn4KrqI07EtDFFbNJ9AcNOazSu3TXrml--t_bEXprfnPqa3MlBvmPUVBcwUFJPDTR9Y";
-
- Masterkey masterkey = MasterkeyHubAccess.decryptMasterkey(devicePrivateKey, ciphertext, ephPk);
-
- byte[] expectedKey = new byte[64];
- Arrays.fill(expectedKey, 0, 32, (byte) 0x55);
- Arrays.fill(expectedKey, 32, 64, (byte) 0x77);
- Assertions.assertArrayEquals(expectedKey, masterkey.getEncoded());
- }
-
- @Test
- @DisplayName("decryptMasterkey(...) with tampered ephemeral public key")
- public void testDecryptWithInvalidEphemeralPublicKey() {
- String ephPk = "mHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEkcv3x-hkCnb8Kr8TNfaLpD4q64ZqPn4p1yuM8r2r16h6f6mG01kFBp2EoY575bCcmT54PxiDFkf3KKqHXFjZwBhdm6zMp22l37ZlmKyHG96vkB7Rh6qFyzEhSQ_nvl2G";
- String ciphertext = "KQ48XS6ziW3tS7SMLR5sc2o_Y80OR4SS_htHpk8SHn4KrqI07EtDFFbNJ9AcNOazSu3TXrml--t_bEXprfnPqa3MlBvmPUVBcwUFJPDTR9Y";
-
- Assertions.assertThrows(MasterkeyLoadingFailedException.class, () -> {
- MasterkeyHubAccess.decryptMasterkey(devicePrivateKey, ciphertext, ephPk);
- });
- }
-
- @Test
- @DisplayName("decryptMasterkey(...) with tampered ciphertext")
- public void testDecryptWithInvalidCiphertext() {
- String ephPk = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEkcv3x-hkCnb8Kr8TNfaLpD4q64ZqPn4p1yuM8r2r16h6f6mG01kFBp2EoY575bCcmT54PxiDFkf3KKqHXFjZwBhdm6zMp22l37ZlmKyHG96vkB7Rh6qFyzEhSQ_nvl2G";
- String ciphertext = "kQ48XS6ziW3tS7SMLR5sc2o_Y80OR4SS_htHpk8SHn4KrqI07EtDFFbNJ9AcNOazSu3TXrml--t_bEXprfnPqa3MlBvmPUVBcwUFJPDTR9Y";
-
- Assertions.assertThrows(MasterkeyLoadingFailedException.class, () -> {
- MasterkeyHubAccess.decryptMasterkey(devicePrivateKey, ciphertext, ephPk);
- });
- }
-
- @Test
- @DisplayName("decryptMasterkey(...) with invalid device key")
- public void testDecryptWithInvalidDeviceKey() {
- String ephPk = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEkcv3x-hkCnb8Kr8TNfaLpD4q64ZqPn4p1yuM8r2r16h6f6mG01kFBp2EoY575bCcmT54PxiDFkf3KKqHXFjZwBhdm6zMp22l37ZlmKyHG96vkB7Rh6qFyzEhSQ_nvl2G";
- String ciphertext = "KQ48XS6ziW3tS7SMLR5sc2o_Y80OR4SS_htHpk8SHn4KrqI07EtDFFbNJ9AcNOazSu3TXrml--t_bEXprfnPqa3MlBvmPUVBcwUFJPDTR9Y";
- ECPrivateKey wrongKey = P384KeyPair.generate().getPrivate();
-
- Assertions.assertThrows(MasterkeyLoadingFailedException.class, () -> {
- MasterkeyHubAccess.decryptMasterkey(wrongKey, ciphertext, ephPk);
- });
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/cryptomator/cryptolib/ecies/ECIntegratedEncryptionSchemeTest.java b/src/test/java/org/cryptomator/cryptolib/ecies/ECIntegratedEncryptionSchemeTest.java
deleted file mode 100644
index df836a5..0000000
--- a/src/test/java/org/cryptomator/cryptolib/ecies/ECIntegratedEncryptionSchemeTest.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.KeyAgreement;
-import java.nio.charset.StandardCharsets;
-import java.security.InvalidKeyException;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.util.Arrays;
-
-public class ECIntegratedEncryptionSchemeTest {
-
- @Nested
- @DisplayName("With null encryption scheme...")
- public class WithIdentityCipher {
-
- private AuthenticatedEncryption ae;
- private KeyDerivationFunction kdf;
- private ECIntegratedEncryptionScheme ecies;
- private KeyPair ephemeral;
- private KeyPair receiver;
- private byte[] expectedSharedSecret;
- private byte[] derivedSecret;
-
- @BeforeEach
- public void setup() throws AEADBadTagException, NoSuchAlgorithmException, InvalidKeyException {
- this.ephemeral = KeyPairGenerator.getInstance("EC").generateKeyPair();
- this.receiver = KeyPairGenerator.getInstance("EC").generateKeyPair();
- KeyAgreement ecdh = KeyAgreement.getInstance("ECDH");
- ecdh.init(ephemeral.getPrivate());
- ecdh.doPhase(receiver.getPublic(), true);
- this.expectedSharedSecret = ecdh.generateSecret();
- this.ae = Mockito.mock(AuthenticatedEncryption.class);
- this.kdf = Mockito.mock(KeyDerivationFunction.class);
- this.ecies = new ECIntegratedEncryptionScheme(ae, kdf);
- this.derivedSecret = new byte[32];
- Arrays.fill(derivedSecret, (byte) 0xAA);
-
- // set up null encryption
- Mockito.doReturn(32).when(ae).requiredSecretBytes();
- Mockito.doAnswer(invocation -> invocation.getArgument(1)).when(ae).encrypt(Mockito.any(), Mockito.any());
- Mockito.doAnswer(invocation -> invocation.getArgument(1)).when(ae).decrypt(Mockito.any(), Mockito.any());
-
- // set up null KDF
- Mockito.doReturn(derivedSecret).when(kdf).deriveKey(expectedSharedSecret, 32);
- }
-
- @Test
- public void testEncryptWithInvalidKey() {
- ECPrivateKey invalidSk = Mockito.mock(ECPrivateKey.class);
- ECPublicKey validPk = (ECPublicKey) receiver.getPublic();
- Mockito.doReturn("WRONG").when(invalidSk).getAlgorithm();
-
- Assertions.assertThrows(IllegalArgumentException.class, () -> {
- ecies.encrypt(invalidSk, validPk, new byte[42]);
- });
- }
-
- @Test
- public void testEncrypt() {
- byte[] cleartext = "secret message".getBytes(StandardCharsets.UTF_8);
-
- byte[] ciphertext = ecies.encrypt((ECPrivateKey) ephemeral.getPrivate(), (ECPublicKey) receiver.getPublic(), cleartext);
-
- Assertions.assertArrayEquals(cleartext, ciphertext);
- Mockito.verify(kdf).deriveKey(Mockito.any(), Mockito.eq(32));
- Mockito.verify(ae).encrypt(derivedSecret, cleartext);
- }
-
- @Test
- public void testDecrypt() throws AEADBadTagException {
- byte[] ciphertext = "secret message".getBytes(StandardCharsets.UTF_8);
-
- byte[] cleartext = ecies.decrypt((ECPrivateKey) receiver.getPrivate(), (ECPublicKey) ephemeral.getPublic(), ciphertext);
-
- Assertions.assertArrayEquals(ciphertext, cleartext);
- Mockito.verify(kdf).deriveKey(Mockito.any(), Mockito.eq(32));
- Mockito.verify(ae).decrypt(derivedSecret, cleartext);
- }
-
- }
-
- @Nested
- @DisplayName("With Cryptomator Hub encryption scheme...")
- public class WithHubScheme {
-
- private ECIntegratedEncryptionScheme ecies = ECIntegratedEncryptionScheme.HUB;
- private KeyPairGenerator keyGen;
- private KeyPair receiverKeyPair;
-
- @BeforeEach
- public void setup() throws NoSuchAlgorithmException {
- this.keyGen = KeyPairGenerator.getInstance("EC");
- this.receiverKeyPair = keyGen.generateKeyPair();
- }
-
- @Test
- @DisplayName("encrypt(...)")
- public void testEncrypt() {
- byte[] cleartext = "hello world".getBytes(StandardCharsets.UTF_8);
- EncryptedMessage msg = ecies.encrypt(keyGen, (ECPublicKey) receiverKeyPair.getPublic(), cleartext);
- Assertions.assertNotNull(msg.getCiphertext());
- Assertions.assertNotNull(msg.getEphPublicKey());
- }
-
- @Test
- @DisplayName("encrypt(...) with invalid keygen")
- public void testEncryptWithInvalidKeyGen() throws NoSuchAlgorithmException {
- KeyPairGenerator rsaKeyGen = KeyPairGenerator.getInstance("RSA");
- byte[] cleartext = "hello world".getBytes(StandardCharsets.UTF_8);
- ECPublicKey receiverPublicKey = (ECPublicKey) receiverKeyPair.getPublic();
-
- Assertions.assertThrows(IllegalArgumentException.class, () -> {
- ecies.encrypt(rsaKeyGen, receiverPublicKey, cleartext);
- });
- }
-
- @Nested
- @DisplayName("With Cryptomator Hub encryption scheme...")
- public class WithEncryptedMessage {
-
- private byte[] expectedCleartext = "hello world".getBytes(StandardCharsets.UTF_8);
- private EncryptedMessage encryptedMessage;
-
- @BeforeEach
- public void setup() throws NoSuchAlgorithmException {
- byte[] cleartext = "hello world".getBytes(StandardCharsets.UTF_8);
- this.encryptedMessage = ecies.encrypt(keyGen, (ECPublicKey) receiverKeyPair.getPublic(), cleartext);
- }
-
- @Test
- @DisplayName("decrypt(...)")
- public void testDecrypt() throws AEADBadTagException {
- byte[] cleartext = ecies.decrypt((ECPrivateKey) receiverKeyPair.getPrivate(), encryptedMessage);
- Assertions.assertArrayEquals(expectedCleartext, cleartext);
- }
-
- @Test
- @DisplayName("decrypt(...) with invalid key")
- public void testDecryptWithInvalidKey() {
- ECPrivateKey wrongPrivateKey = (ECPrivateKey) keyGen.generateKeyPair().getPrivate();
- Assertions.assertThrows(AEADBadTagException.class, () -> {
- ecies.decrypt(wrongPrivateKey, encryptedMessage);
- });
- }
-
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/cryptomator/cryptolib/ecies/GcmWithSecretNonceTest.java b/src/test/java/org/cryptomator/cryptolib/ecies/GcmWithSecretNonceTest.java
deleted file mode 100644
index 48cb923..0000000
--- a/src/test/java/org/cryptomator/cryptolib/ecies/GcmWithSecretNonceTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import org.cryptomator.cryptolib.common.CipherSupplier;
-import org.cryptomator.cryptolib.common.GcmTestHelper;
-import org.cryptomator.cryptolib.common.ObjectPool;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.converter.ConvertWith;
-import org.junit.jupiter.params.provider.CsvSource;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.Cipher;
-
-/**
- * Test vectors from https://csrc.nist.rip/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
- */
-public class GcmWithSecretNonceTest {
-
- private final GcmWithSecretNonce ae = new GcmWithSecretNonce();
-
- @BeforeEach
- public void setup() {
- // reset cipher state to avoid InvalidAlgorithmParameterExceptions due to IV-reuse
- GcmTestHelper.reset((mode, key, params) -> {
- try (ObjectPool.Lease cipher = CipherSupplier.AES_GCM.encryptionCipher(key, params)) {
- cipher.get();
- }
- });
- }
-
- @Test
- public void testRequiredSecretBytes() {
- Assertions.assertEquals(44, ae.requiredSecretBytes());
- }
-
- @ParameterizedTest
- @CsvSource(value = {
- "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, , 530f8afbc74536b9a963b4f1c4cb738b", // test case 13
- "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000, cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919", // test case 14
- "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308cafebabefacedbaddecaf888, d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255, 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015adb094dac5d93471bdec1a502270e3cc6c", // test case 15
- })
- public void testEncrypt(@ConvertWith(HexConverter.class) byte[] secret, @ConvertWith(HexConverter.class) byte[] plaintext, @ConvertWith(HexConverter.class) byte[] expectedCiphertext) {
- byte[] ciphertext = ae.encrypt(secret, plaintext);
- Assertions.assertArrayEquals(expectedCiphertext, ciphertext);
- }
-
- @ParameterizedTest
- @CsvSource(value = {
- "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, , 530f8afbc74536b9a963b4f1c4cb738b", // test case 13
- "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000, cea7403d4d606b6e074ec5d3baf39d18d0d1c8a799996bf0265b98b5d48ab919", // test case 14
- "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308cafebabefacedbaddecaf888, d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255, 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015adb094dac5d93471bdec1a502270e3cc6c", // test case 15
- })
- public void testDecrypt(@ConvertWith(HexConverter.class) byte[] secret, @ConvertWith(HexConverter.class) byte[] expectedPlaintext, @ConvertWith(HexConverter.class) byte[] ciphertext) throws AEADBadTagException {
- byte[] plaintext = ae.decrypt(secret, ciphertext);
- Assertions.assertArrayEquals(expectedPlaintext, plaintext);
- }
-
-
- @ParameterizedTest
- @CsvSource(value = {
- "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000001, 530f8afbc74536b9a963b4f1c4cb738b",
- "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000, 530f8afbc74536b9a963b4f1c4cb738b",
- })
- public void testDecryptInvalid(@ConvertWith(HexConverter.class) byte[] secret, @ConvertWith(HexConverter.class) byte[] ciphertext) {
- Assertions.assertThrows(AEADBadTagException.class, () -> {
- ae.decrypt(secret, ciphertext);
- });
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/cryptomator/cryptolib/ecies/HexConverter.java b/src/test/java/org/cryptomator/cryptolib/ecies/HexConverter.java
deleted file mode 100644
index 2daf59e..0000000
--- a/src/test/java/org/cryptomator/cryptolib/ecies/HexConverter.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import com.google.common.io.BaseEncoding;
-import org.junit.jupiter.api.extension.ParameterContext;
-import org.junit.jupiter.params.converter.ArgumentConversionException;
-import org.junit.jupiter.params.converter.ArgumentConverter;
-
-class HexConverter implements ArgumentConverter {
-
- @Override
- public byte[] convert(Object source, ParameterContext context) throws ArgumentConversionException {
- if (source == null) {
- return new byte[0];
- } else if (source instanceof String) {
- return BaseEncoding.base16().lowerCase().decode((String) source);
- } else {
- return null;
- }
- }
-}
diff --git a/src/test/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunctionTest.java b/src/test/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunctionTest.java
deleted file mode 100644
index 6395dc9..0000000
--- a/src/test/java/org/cryptomator/cryptolib/ecies/KeyDerivationFunctionTest.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.cryptomator.cryptolib.ecies;
-
-import com.google.common.io.BaseEncoding;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.converter.ConvertWith;
-import org.junit.jupiter.params.provider.CsvSource;
-
-public class KeyDerivationFunctionTest {
-
- @DisplayName("ANSI-X9.63-KDF")
- @ParameterizedTest
- @CsvSource(value = {
- "96c05619d56c328ab95fe84b18264b08725b85e33fd34f08, , 16, 443024c3dae66b95e6f5670601558f71",
- "96f600b73ad6ac5629577eced51743dd2c24c21b1ac83ee4, , 16, b6295162a7804f5667ba9070f82fa522",
- "22518b10e70f2a3f243810ae3254139efbee04aa57c7af7d, 75eef81aa3041e33b80971203d2c0c52, 128, c498af77161cc59f2962b9a713e2b215152d139766ce34a776df11866a69bf2e52a13d9c7c6fc878c50c5ea0bc7b00e0da2447cfd874f6cf92f30d0097111485500c90c3af8b487872d04685d14c8d1dc8d7fa08beb0ce0ababc11f0bd496269142d43525a78e5bc79a17f59676a5706dc54d54d4d1f0bd7e386128ec26afc21",
- "7e335afa4b31d772c0635c7b0e06f26fcd781df947d2990a, d65a4812733f8cdbcdfb4b2f4c191d87, 128, c0bd9e38a8f9de14c2acd35b2f3410c6988cf02400543631e0d6a4c1d030365acbf398115e51aaddebdc9590664210f9aa9fed770d4c57edeafa0b8c14f93300865251218c262d63dadc47dfa0e0284826793985137e0a544ec80abf2fdf5ab90bdaea66204012efe34971dc431d625cd9a329b8217cc8fd0d9f02b13f2f6b0b",
- })
- // test vectors from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/components/800-135testvectors/ansx963_2001.zip
- public void testAnsiX963sha256(@ConvertWith(HexConverter.class) byte[] sharedSecret, @ConvertWith(HexConverter.class) byte[] sharedInfo, int outLen, @ConvertWith(HexConverter.class) byte[] expectedResult) {
- byte[] result = KeyDerivationFunction.ansiX963sha256Kdf(sharedSecret, sharedInfo, outLen);
- Assertions.assertArrayEquals(expectedResult, result);
- }
-
- @DisplayName("kdf.deriveKey()")
- @Test
- public void testDeriveKey() {
- byte[] secret = BaseEncoding.base16().lowerCase().decode("96c05619d56c328ab95fe84b18264b08725b85e33fd34f08");
-
- byte[] result = KeyDerivationFunction.ANSI_X963_SHA256_KDF.deriveKey(secret, 16);
-
- byte[] expected = BaseEncoding.base16().lowerCase().decode("443024c3dae66b95e6f5670601558f71");
- Assertions.assertArrayEquals(expected, result);
- }
-
-}
\ No newline at end of file