diff --git a/pom.xml b/pom.xml index 2cd2baec6..d990427e2 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,8 @@ com.github.serceman,com.github.jnr,org.ow2.asm,net.java.dev.jna,org.apache.jackrabbit,org.apache.httpcomponents,de.swiesend,org.purejava,com.github.hypfvieh - 2.1.0-beta11 + 2.1.0-beta2 + 2.1.0-beta12 1.0.0-rc1 1.0.0-beta2 1.0.0-beta2 @@ -58,6 +59,11 @@ + + org.cryptomator + cryptolib + ${cryptomator.cryptolib.version} + org.cryptomator cryptofs @@ -130,13 +136,6 @@ ${commons-lang3.version} - - - org.bouncycastle - bcpkix-jdk15on - ${bouncycastle.version} - - org.eclipse.jetty jetty-server diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index b8e899845..552226462 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -4,6 +4,7 @@ import org.cryptomator.integrations.tray.TrayIntegrationProvider; import org.cryptomator.integrations.uiappearance.UiAppearanceProvider; module org.cryptomator.desktop { + requires org.cryptomator.cryptolib; requires org.cryptomator.cryptofs; requires org.cryptomator.frontend.dokany; requires org.cryptomator.frontend.fuse; @@ -25,8 +26,6 @@ module org.cryptomator.desktop { requires com.tobiasdiez.easybind; requires dagger; requires org.slf4j; - requires org.bouncycastle.provider; - requires org.bouncycastle.pkix; requires org.apache.commons.lang3; requires org.eclipse.jetty.server; requires org.eclipse.jetty.webapp; diff --git a/src/main/java/org/cryptomator/common/settings/DeviceKey.java b/src/main/java/org/cryptomator/common/settings/DeviceKey.java new file mode 100644 index 000000000..aba0bb849 --- /dev/null +++ b/src/main/java/org/cryptomator/common/settings/DeviceKey.java @@ -0,0 +1,104 @@ +package org.cryptomator.common.settings; + +import com.google.common.base.Preconditions; +import com.google.common.base.Suppliers; +import com.google.common.io.BaseEncoding; +import org.cryptomator.common.Environment; +import org.cryptomator.common.keychain.KeychainManager; +import org.cryptomator.cryptolib.common.P384KeyPair; +import org.cryptomator.cryptolib.common.Pkcs12Exception; +import org.cryptomator.integrations.keychain.KeychainAccessException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.io.IOException; +import java.nio.CharBuffer; +import java.nio.file.Files; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.UUID; +import java.util.function.Supplier; + +@Singleton +public class DeviceKey { + + private static final Logger LOG = LoggerFactory.getLogger(DeviceKey.class); + private static final String KEYCHAIN_KEY = "cryptomator-device-p12"; + + private final KeychainManager keychainManager; + private final Environment env; + private final SecureRandom csprng; + private final Supplier keyPairSupplier; + + @Inject + public DeviceKey(KeychainManager keychainManager, Environment env, SecureRandom csprng) { + this.keychainManager = keychainManager; + this.env = env; + this.csprng = csprng; + this.keyPairSupplier = Suppliers.memoize(this::loadOrCreate); + } + + public P384KeyPair get() throws DeviceKeyRetrievalException { + Preconditions.checkState(keychainManager.isSupported()); + return keyPairSupplier.get(); + } + + private P384KeyPair loadOrCreate() throws DeviceKeyRetrievalException { + char[] passphrase = null; + try { + passphrase = keychainManager.loadPassphrase(KEYCHAIN_KEY); + if (passphrase != null) { + return loadExistingKeyPair(passphrase); + } else { + passphrase = randomPassword(); + keychainManager.storePassphrase(KEYCHAIN_KEY, CharBuffer.wrap(passphrase)); + return createAndStoreNewKeyPair(passphrase); + } + } catch (KeychainAccessException e) { + throw new DeviceKeyRetrievalException("Failed to access system keychain", e); + } catch (Pkcs12Exception | IOException e) { + throw new DeviceKeyRetrievalException("Failed to access .p12 file", e); + } finally { + if (passphrase != null) { + Arrays.fill(passphrase, '\0'); + } + } + } + + private P384KeyPair loadExistingKeyPair(char[] passphrase) throws IOException { + var p12File = env.getP12Path() // + .filter(Files::isRegularFile) // + .findFirst() // + .orElseThrow(() -> new DeviceKeyRetrievalException("Missing .p12 file")); + LOG.debug("Loading existing device key from {}", p12File); + return P384KeyPair.load(p12File, passphrase); + } + + private P384KeyPair createAndStoreNewKeyPair(char[] passphrase) throws IOException { + var p12File = env.getP12Path() // + .findFirst() // + .orElseThrow(() -> new DeviceKeyRetrievalException("No path for .p12 file configured")); + var keyPair = P384KeyPair.generate(); + LOG.debug("Store new device key to {}", p12File); + keyPair.store(p12File, passphrase); + return keyPair; + } + + private char[] randomPassword() { + // this is a fast & easy attempt to create a random string: + var uuid = new UUID(csprng.nextLong(), csprng.nextLong()); + return uuid.toString().toCharArray(); + } + + public static class DeviceKeyRetrievalException extends RuntimeException { + private DeviceKeyRetrievalException(String message) { + super(message); + } + private DeviceKeyRetrievalException(String message, Throwable cause) { + super(message, cause); + } + } + +} diff --git a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 627793d6e..578b90969 100644 --- a/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -182,7 +182,7 @@ public class CreateNewVaultPasswordController implements FxController { // 2. initialize vault: try { - MasterkeyLoader loader = ignored -> masterkey.clone(); + MasterkeyLoader loader = ignored -> masterkey.copy(); CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties().withCipherCombo(CryptorProvider.Scheme.SIV_CTRMAC).withKeyLoader(loader).build(); CryptoFileSystemProvider.initialize(path, fsProps, DEFAULT_KEY_ID); diff --git a/src/main/java/org/cryptomator/ui/common/FxmlFile.java b/src/main/java/org/cryptomator/ui/common/FxmlFile.java index 84ef8983f..8d96dd216 100644 --- a/src/main/java/org/cryptomator/ui/common/FxmlFile.java +++ b/src/main/java/org/cryptomator/ui/common/FxmlFile.java @@ -15,7 +15,6 @@ public enum FxmlFile { HEALTH_START_FAIL("/fxml/health_start_fail.fxml"), // HEALTH_CHECK_LIST("/fxml/health_check_list.fxml"), // HUB_AUTH_FLOW("/fxml/hub_auth_flow.fxml"), // - HUB_P12("/fxml/hub_p12.fxml"), // HUB_RECEIVE_KEY("/fxml/hub_receive_key.fxml"), // HUB_REGISTER_DEVICE("/fxml/hub_register_device.fxml"), // LOCK_FORCED("/fxml/lock_forced.fxml"), // diff --git a/src/main/java/org/cryptomator/ui/health/CheckExecutor.java b/src/main/java/org/cryptomator/ui/health/CheckExecutor.java index 5b14bd17c..a9ee9a17f 100644 --- a/src/main/java/org/cryptomator/ui/health/CheckExecutor.java +++ b/src/main/java/org/cryptomator/ui/health/CheckExecutor.java @@ -67,7 +67,7 @@ public class CheckExecutor { @Override protected Void call() throws Exception { - try (var masterkeyClone = masterkey.clone(); // + try (var masterkeyClone = masterkey.copy(); // var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) { c.getHealthCheck().check(vaultPath, vaultConfig, masterkeyClone, cryptor, diagnosis -> { Platform.runLater(() -> c.getResults().add(Result.create(diagnosis))); diff --git a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java index 841a8f5c4..3dc91e33b 100644 --- a/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java +++ b/src/main/java/org/cryptomator/ui/health/ResultFixApplier.java @@ -50,7 +50,7 @@ class ResultFixApplier { public void fix(DiagnosticResult diagnosis) { Preconditions.checkArgument(diagnosis.getSeverity() == DiagnosticResult.Severity.WARN, "Unfixable result"); - try (var masterkeyClone = masterkey.clone(); // + try (var masterkeyClone = masterkey.copy(); // var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) { diagnosis.fix(vaultPath, vaultConfig, masterkeyClone, cryptor); } catch (Exception e) { diff --git a/src/main/java/org/cryptomator/ui/health/StartController.java b/src/main/java/org/cryptomator/ui/health/StartController.java index ebba001b5..ec980b9f0 100644 --- a/src/main/java/org/cryptomator/ui/health/StartController.java +++ b/src/main/java/org/cryptomator/ui/health/StartController.java @@ -84,7 +84,7 @@ public class StartController implements FxController { try (var masterkey = keyLoadingStrategy.loadKey(unverifiedCfg.getKeyId())) { var verifiedCfg = unverifiedCfg.verify(masterkey.getEncoded(), unverifiedCfg.allegedVaultVersion()); vaultConfigRef.set(verifiedCfg); - var old = masterkeyRef.getAndSet(masterkey.clone()); + var old = masterkeyRef.getAndSet(masterkey.copy()); if (old != null) { old.destroy(); } diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/EciesHelper.java b/src/main/java/org/cryptomator/ui/keyloading/hub/EciesHelper.java deleted file mode 100644 index b7dac36ba..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/EciesHelper.java +++ /dev/null @@ -1,135 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import com.google.common.base.Preconditions; -import com.google.common.io.BaseEncoding; -import org.cryptomator.cryptolib.api.Masterkey; -import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; -import org.cryptomator.cryptolib.common.CipherSupplier; -import org.cryptomator.cryptolib.common.DestroyableSecretKey; - -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyAgreement; -import javax.crypto.spec.GCMParameterSpec; -import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.DigestException; -import java.security.InvalidKeyException; -import java.security.KeyPair; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.util.Arrays; - -class EciesHelper { - - 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 - - private EciesHelper() {} - - public static Masterkey decryptMasterkey(KeyPair deviceKey, EciesParams eciesParams) throws MasterkeyLoadingFailedException { - var sharedSecret = ecdhAndKdf(deviceKey.getPrivate(), eciesParams.getEphemeralPublicKey(), GCM_KEY_SIZE + GCM_NONCE_SIZE); - var cleartext = new byte[0]; - try (var kek = new DestroyableSecretKey(sharedSecret, 0, GCM_KEY_SIZE, "AES")) { - var nonce = Arrays.copyOfRange(sharedSecret, GCM_KEY_SIZE, GCM_KEY_SIZE + GCM_NONCE_SIZE); - var cipher = CipherSupplier.AES_GCM.forDecryption(kek, new GCMParameterSpec(GCM_TAG_SIZE * Byte.SIZE, nonce)); - cleartext = cipher.doFinal(eciesParams.getCiphertext()); - return new Masterkey(cleartext); - } catch (AEADBadTagException e) { - throw new MasterkeyLoadingFailedException("Unsuitable KEK to decrypt encrypted masterkey", e); - } catch (IllegalBlockSizeException | BadPaddingException e) { - throw new IllegalStateException("Unexpected exception during GCM decryption.", e); - } finally { - Arrays.fill(sharedSecret, (byte) 0x00); - Arrays.fill(cleartext, (byte) 0x00); - } - } - - /** - * Computes a shared secret using ECDH key agreement and derives a key. - * - * @param privateKey Recipient's EC private key - * @param publicKey Sender's EC public key - * @param numBytes Number of bytes requested form KDF - * @return A derived secret key - */ - // visible for testing - static byte[] ecdhAndKdf(PrivateKey privateKey, PublicKey publicKey, int numBytes) { - Preconditions.checkArgument(privateKey instanceof ECPrivateKey, "expected ECPrivateKey"); - Preconditions.checkArgument(publicKey instanceof ECPublicKey, "expected ECPublicKey"); - byte[] sharedSecret = new byte[0]; - try { - var keyAgreement = createKeyAgreement(); - keyAgreement.init(privateKey); - keyAgreement.doPhase(publicKey, true); - sharedSecret = keyAgreement.generateSecret(); - return kdf(sharedSecret, new byte[0], numBytes); - } catch (InvalidKeyException e) { - throw new IllegalArgumentException("Invalid keys", e); - } finally { - Arrays.fill(sharedSecret, (byte) 0x00); - } - } - - /** - * 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 - */ - // visible for testing - static byte[] kdf(byte[] sharedSecret, byte[] sharedInfo, int keyDataLen) { - MessageDigest digest = sha256(); // max input length is 2^64 - 1, see https://doi.org/10.6028/NIST.SP.800-56Cr2, Table 1 - int hashLen = digest.getDigestLength(); - - // These two checks must be performed according to spec. However with 32 bit integers, we can't exceed any limits anyway: - assert BigInteger.valueOf(sharedSecret.length + sharedInfo.length + 4).compareTo(BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE)) < 0: "input larger than hashmaxlen"; - assert keyDataLen < (2L << 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 { - for (int i = 0; i < n; i++) { - digest.update(sharedSecret); - counter.clear(); - counter.putInt(i + 1); - counter.flip(); - digest.update(counter); - digest.update(sharedInfo); - digest.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); - } - } - - private static MessageDigest sha256() { - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Every implementation of the Java platform is required to support SHA-256."); - } - } - - 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/ui/keyloading/hub/EciesParams.java b/src/main/java/org/cryptomator/ui/keyloading/hub/EciesParams.java index 5e25b37f7..8b0519906 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/EciesParams.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/EciesParams.java @@ -1,43 +1,14 @@ package org.cryptomator.ui.keyloading.hub; -import com.google.common.io.BaseEncoding; - -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; - /** * ECIES parameters required to decrypt the masterkey: *
    *
  • m Encrypted Masterkey (base64url-encoded ciphertext)
  • *
  • epk Ephemeral Public Key (base64url-encoded SPKI format)
  • *
- * + *

* No separate tag required, since we use GCM for encryption. */ record EciesParams(String m, String epk) { - public byte[] getCiphertext() { - return BaseEncoding.base64Url().decode(m()); - } - - public ECPublicKey getEphemeralPublicKey() { - try { - byte[] keyBytes = BaseEncoding.base64Url().decode(epk()); - PublicKey key = KeyFactory.getInstance("EC").generatePublic(new X509EncodedKeySpec(keyBytes)); - if (key instanceof ECPublicKey k) { - return k; - } 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/ui/keyloading/hub/HubKeyLoadingModule.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java index ad2c5c02b..f9ade2585 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingModule.java @@ -46,12 +46,6 @@ public abstract class HubKeyLoadingModule { } } - @Provides - @KeyLoadingScoped - static AtomicReference provideKeyPair() { - return new AtomicReference<>(); - } - @Provides @Named("bearerToken") @KeyLoadingScoped @@ -83,13 +77,6 @@ public abstract class HubKeyLoadingModule { @StringKey(HubKeyLoadingStrategy.SCHEME_HUB_HTTPS) abstract KeyLoadingStrategy bindHubKeyLoadingStrategyToHubHttps(HubKeyLoadingStrategy strategy); - @Provides - @FxmlScene(FxmlFile.HUB_P12) - @KeyLoadingScoped - static Scene provideHubP12LoadingScene(@KeyLoading FxmlLoaderFactory fxmlLoaders) { - return fxmlLoaders.createScene(FxmlFile.HUB_P12); - } - @Provides @FxmlScene(FxmlFile.HUB_AUTH_FLOW) @KeyLoadingScoped @@ -97,7 +84,6 @@ public abstract class HubKeyLoadingModule { return fxmlLoaders.createScene(FxmlFile.HUB_AUTH_FLOW); } - @Provides @FxmlScene(FxmlFile.HUB_RECEIVE_KEY) @KeyLoadingScoped @@ -112,22 +98,6 @@ public abstract class HubKeyLoadingModule { return fxmlLoaders.createScene(FxmlFile.HUB_REGISTER_DEVICE); } - - @Binds - @IntoMap - @FxControllerKey(P12Controller.class) - abstract FxController bindP12Controller(P12Controller controller); - - @Binds - @IntoMap - @FxControllerKey(P12LoadController.class) - abstract FxController bindP12LoadController(P12LoadController controller); - - @Binds - @IntoMap - @FxControllerKey(P12CreateController.class) - abstract FxController bindP12CreateController(P12CreateController controller); - @Binds @IntoMap @FxControllerKey(AuthFlowController.class) diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java index b3b213b34..83338ed06 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/HubKeyLoadingStrategy.java @@ -2,10 +2,12 @@ package org.cryptomator.ui.keyloading.hub; import com.google.common.base.Preconditions; import dagger.Lazy; +import org.cryptomator.common.settings.DeviceKey; import org.cryptomator.common.vaults.Vault; import org.cryptomator.cryptolib.api.Masterkey; import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; import org.cryptomator.cryptolib.common.Destroyables; +import org.cryptomator.cryptolib.common.MasterkeyHubAccess; import org.cryptomator.ui.common.FxmlFile; import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.UserInteractionLock; @@ -30,17 +32,17 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy { static final String SCHEME_HUB_HTTPS = SCHEME_PREFIX + "https"; private final Stage window; - private final Lazy p12LoadingScene; + private final Lazy authFlowScene; private final UserInteractionLock userInteraction; - private final AtomicReference keyPairRef; + private final DeviceKey deviceKey; private final AtomicReference eciesParams; @Inject - public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_P12) Lazy p12LoadingScene, UserInteractionLock userInteraction, AtomicReference keyPairRef, AtomicReference eciesParams) { + public HubKeyLoadingStrategy(@KeyLoading Stage window, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene, UserInteractionLock userInteraction, DeviceKey deviceKey, AtomicReference eciesParams) { this.window = window; - this.p12LoadingScene = p12LoadingScene; + this.authFlowScene = authFlowScene; this.userInteraction = userInteraction; - this.keyPairRef = keyPairRef; + this.deviceKey = deviceKey; this.eciesParams = eciesParams; } @@ -48,28 +50,23 @@ public class HubKeyLoadingStrategy implements KeyLoadingStrategy { public Masterkey loadKey(URI keyId) throws MasterkeyLoadingFailedException { Preconditions.checkArgument(keyId.getScheme().startsWith(SCHEME_PREFIX)); try { + var keyPair = deviceKey.get(); return switch (auth()) { - case SUCCESS -> EciesHelper.decryptMasterkey(keyPairRef.get(), eciesParams.get()); + case SUCCESS -> MasterkeyHubAccess.decryptMasterkey(keyPair.getPrivate(), eciesParams.get().m(), eciesParams.get().epk()); case FAILED -> throw new MasterkeyLoadingFailedException("failed to load keypair"); case CANCELLED -> throw new UnlockCancelledException("User cancelled auth workflow"); }; + } catch (DeviceKey.DeviceKeyRetrievalException e) { + throw new MasterkeyLoadingFailedException("Failed to create or load device key pair", e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new UnlockCancelledException("Loading interrupted", e); } } - @Override - public void cleanup(boolean unlockedSuccessfully) { - var keyPair = keyPairRef.getAndSet(null); - if (keyPair != null) { - Destroyables.destroySilently(keyPair.getPrivate()); - } - } - private HubKeyLoadingModule.HubLoadingResult auth() throws InterruptedException { Platform.runLater(() -> { - window.setScene(p12LoadingScene.get()); + window.setScene(authFlowScene.get()); window.show(); Window owner = window.getOwner(); if (owner != null) { diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12AccessHelper.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12AccessHelper.java deleted file mode 100644 index 99bd7d3fe..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12AccessHelper.java +++ /dev/null @@ -1,108 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.X509Certificate; -import java.security.spec.ECGenParameterSpec; - -class P12AccessHelper { - - private static final String EC_ALG = "EC"; - private static final String EC_CURVE_NAME = "secp384r1"; - private static final String SIGNATURE_ALG = "SHA256withECDSA"; - private static final String KEYSTORE_ALIAS_KEY = "key"; - private static final String KEYSTORE_ALIAS_CERT = "crt"; - - private P12AccessHelper() {} - - /** - * Creates a new key pair and stores it in PKCS#12 format at the given path. - * - * @param p12File The path of the .p12 file - * @param pw The password to protect the key material - * @throws IOException In case of I/O errors - * @throws MasterkeyLoadingFailedException If any cryptographic operation fails - */ - public static KeyPair createNew(Path p12File, char[] pw) throws IOException, MasterkeyLoadingFailedException { - try { - var keyPair = getKeyPairGenerator().generateKeyPair(); - var keyStore = getKeyStore(); - keyStore.load(null, pw); - var cert = X509Helper.createSelfSignedCert(keyPair, SIGNATURE_ALG); - var chain = new X509Certificate[]{cert}; - keyStore.setKeyEntry(KEYSTORE_ALIAS_KEY, keyPair.getPrivate(), pw, chain); - keyStore.setCertificateEntry(KEYSTORE_ALIAS_CERT, cert); - var tmpFile = p12File.resolveSibling(p12File.getFileName().toString() + ".tmp"); - try (var out = Files.newOutputStream(tmpFile, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) { - keyStore.store(out, pw); - } - Files.move(tmpFile, p12File, StandardCopyOption.REPLACE_EXISTING); - return keyPair; - } catch (GeneralSecurityException e) { - throw new MasterkeyLoadingFailedException("Failed to store PKCS12 file.", e); - } - } - - /** - * Loads a key pair from a PKCS#12 file located at the given path. - * - * @param p12File The path of the .p12 file - * @param pw The password to protect the key material - * @throws IOException In case of I/O errors - * @throws InvalidPassphraseException If the supplied password is incorrect - * @throws MasterkeyLoadingFailedException If any cryptographic operation fails - */ - public static KeyPair loadExisting(Path p12File, char[] pw) throws IOException, InvalidPassphraseException, MasterkeyLoadingFailedException { - try (var in = Files.newInputStream(p12File, StandardOpenOption.READ)) { - var keyStore = getKeyStore(); - keyStore.load(in, pw); - var sk = (PrivateKey) keyStore.getKey(KEYSTORE_ALIAS_KEY, pw); - var pk = keyStore.getCertificate(KEYSTORE_ALIAS_CERT).getPublicKey(); - return new KeyPair(pk, sk); - } catch (UnrecoverableKeyException e) { - throw new InvalidPassphraseException(); - } catch (IOException e) { - if (e.getCause() instanceof UnrecoverableKeyException) { - throw new InvalidPassphraseException(); - } else { - throw e; - } - } catch (GeneralSecurityException e) { - throw new MasterkeyLoadingFailedException("Failed to load PKCS12 file.", e); - } - } - - private static KeyPairGenerator getKeyPairGenerator() { - try { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(EC_ALG); - keyGen.initialize(new ECGenParameterSpec(EC_CURVE_NAME)); - return keyGen; - } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { - throw new IllegalStateException(EC_CURVE_NAME + " curve not supported"); - } - } - - private static KeyStore getKeyStore() { - try { - return KeyStore.getInstance("PKCS12"); - } catch (KeyStoreException e) { - throw new IllegalStateException("Every implementation of the Java platform is required to support PKCS12."); - } - } - -} diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java deleted file mode 100644 index e9a70dc8d..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12Controller.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import org.cryptomator.common.Environment; -import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.common.UserInteractionLock; -import org.cryptomator.ui.keyloading.KeyLoading; -import org.cryptomator.ui.keyloading.KeyLoadingScoped; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; -import java.nio.file.Files; - -@KeyLoadingScoped -public class P12Controller implements FxController { - - private static final Logger LOG = LoggerFactory.getLogger(P12Controller.class); - - private final Stage window; - private final Environment env; - private final UserInteractionLock userInteraction; - - @Inject - public P12Controller(@KeyLoading Stage window, Environment env, UserInteractionLock userInteraction) { - this.window = window; - this.env = env; - this.userInteraction = userInteraction; - this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); - } - - private void windowClosed(WindowEvent windowEvent) { - // if not already interacted, mark this workflow as cancelled: - if (userInteraction.awaitingInteraction().get()) { - LOG.debug("P12 loading cancelled by user."); - userInteraction.interacted(HubKeyLoadingModule.HubLoadingResult.CANCELLED); - } - } - - /* Getter/Setter */ - - public boolean isP12Present() { - return env.getP12Path().anyMatch(Files::isRegularFile); - } - -} diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java deleted file mode 100644 index 3f98e62ca..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12CreateController.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import com.google.common.base.Preconditions; -import dagger.Lazy; -import org.cryptomator.common.Environment; -import org.cryptomator.cryptolib.common.Destroyables; -import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.common.FxmlFile; -import org.cryptomator.ui.common.FxmlScene; -import org.cryptomator.ui.common.NewPasswordController; -import org.cryptomator.ui.keyloading.KeyLoading; -import org.cryptomator.ui.keyloading.KeyLoadingScoped; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanExpression; -import javafx.beans.binding.ObjectBinding; -import javafx.beans.binding.ObjectExpression; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.ContentDisplay; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; -import java.io.IOException; -import java.nio.file.Path; -import java.security.KeyPair; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - -@KeyLoadingScoped -public class P12CreateController implements FxController { - - private static final Logger LOG = LoggerFactory.getLogger(P12LoadController.class); - - private final Stage window; - private final Environment env; - private final AtomicReference keyPairRef; - private final Lazy authFlowScene; - - private final BooleanProperty userInteractionDisabled = new SimpleBooleanProperty(); - private final ObjectBinding unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, userInteractionDisabled); - private final BooleanProperty readyToCreate = new SimpleBooleanProperty(); - - public NewPasswordController newPasswordController; - - @Inject - public P12CreateController(@KeyLoading Stage window, Environment env, AtomicReference keyPairRef, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene) { - this.window = window; - this.env = env; - this.keyPairRef = keyPairRef; - this.authFlowScene = authFlowScene; - this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); - } - - @FXML - public void initialize() { - readyToCreate.bind(newPasswordController.goodPasswordProperty()); - newPasswordController.passwordField.requestFocus(); - } - - @FXML - public void cancel() { - window.close(); - } - - private void windowClosed(WindowEvent windowEvent) { - newPasswordController.passwordField.wipe(); - newPasswordController.reenterField.wipe(); - } - - @FXML - public void create() { - Preconditions.checkState(newPasswordController.goodPasswordProperty().get()); - char[] pw = newPasswordController.passwordField.copyChars(); - try { - Path p12File = env.getP12Path().findFirst().orElseThrow(IllegalStateException::new); - var keyPair = P12AccessHelper.createNew(p12File, pw); - setKeyPair(keyPair); - LOG.debug("Created .p12 file {}", p12File); - window.setScene(authFlowScene.get()); - } catch (IOException e) { - LOG.error("Failed to load .p12 file.", e); - // TODO - } finally { - Arrays.fill(pw, '\0'); - } - } - - private void setKeyPair(KeyPair keyPair) { - var oldKeyPair = keyPairRef.getAndSet(keyPair); - if (oldKeyPair != null) { - Destroyables.destroySilently(oldKeyPair.getPrivate()); - } - } - /* Getter/Setter */ - - - public BooleanExpression userInteractionDisabledProperty() { - return userInteractionDisabled; - } - - public boolean isUserInteractionDisabled() { - return userInteractionDisabled.get(); - } - - public ObjectExpression unlockButtonContentDisplayProperty() { - return unlockButtonContentDisplay; - } - - public ContentDisplay getUnlockButtonContentDisplay() { - return userInteractionDisabled.get() ? ContentDisplay.LEFT : ContentDisplay.TEXT_ONLY; - } - - public BooleanProperty readyToCreateProperty() { - return readyToCreate; - } - - public boolean isReadyToCreate() { - return readyToCreate.get(); - } - -} diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java deleted file mode 100644 index 24af08384..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/P12LoadController.java +++ /dev/null @@ -1,118 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import dagger.Lazy; -import org.cryptomator.common.Environment; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.common.Destroyables; -import org.cryptomator.ui.common.Animations; -import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.common.FxmlFile; -import org.cryptomator.ui.common.FxmlScene; -import org.cryptomator.ui.controls.NiceSecurePasswordField; -import org.cryptomator.ui.keyloading.KeyLoading; -import org.cryptomator.ui.keyloading.KeyLoadingScoped; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanExpression; -import javafx.beans.binding.ObjectBinding; -import javafx.beans.binding.ObjectExpression; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.ContentDisplay; -import javafx.stage.Stage; -import javafx.stage.WindowEvent; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.security.KeyPair; -import java.util.Arrays; -import java.util.concurrent.atomic.AtomicReference; - -@KeyLoadingScoped -public class P12LoadController implements FxController { - - private static final Logger LOG = LoggerFactory.getLogger(P12LoadController.class); - - private final Stage window; - private final Environment env; - private final AtomicReference keyPairRef; - private final Lazy authFlowScene; - private final BooleanProperty userInteractionDisabled = new SimpleBooleanProperty(); - private final ObjectBinding unlockButtonContentDisplay = Bindings.createObjectBinding(this::getUnlockButtonContentDisplay, userInteractionDisabled); - - public NiceSecurePasswordField passwordField; - - @Inject - public P12LoadController(@KeyLoading Stage window, Environment env, AtomicReference keyPairRef, @FxmlScene(FxmlFile.HUB_AUTH_FLOW) Lazy authFlowScene) { - this.window = window; - this.env = env; - this.keyPairRef = keyPairRef; - this.authFlowScene = authFlowScene; - this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); - } - - @FXML - public void initialize() { - passwordField.requestFocus(); - } - - @FXML - public void cancel() { - window.close(); - } - - private void windowClosed(WindowEvent windowEvent) { - passwordField.wipe(); - } - - @FXML - public void load() { - char[] pw = passwordField.copyChars(); - try { - Path p12File = env.getP12Path().filter(Files::isRegularFile).findFirst().orElseThrow(IllegalStateException::new); - var keyPair = P12AccessHelper.loadExisting(p12File, pw); - setKeyPair(keyPair); - LOG.debug("Loaded .p12 file {}", p12File); - window.setScene(authFlowScene.get()); - } catch (InvalidPassphraseException e) { - LOG.warn("Invalid passphrase entered for .p12 file"); - Animations.createShakeWindowAnimation(window).playFromStart(); - // TODO - } catch (IOException e) { - LOG.error("Failed to load .p12 file.", e); - // TODO - } finally { - Arrays.fill(pw, '\0'); - } - } - - private void setKeyPair(KeyPair keyPair) { - var oldKeyPair = keyPairRef.getAndSet(keyPair); - if (oldKeyPair != null) { - Destroyables.destroySilently(oldKeyPair.getPrivate()); - } - } - - /* Getter/Setter */ - - public BooleanExpression userInteractionDisabledProperty() { - return userInteractionDisabled; - } - - public boolean isUserInteractionDisabled() { - return userInteractionDisabled.get(); - } - - public ObjectExpression unlockButtonContentDisplayProperty() { - return unlockButtonContentDisplay; - } - - public ContentDisplay getUnlockButtonContentDisplay() { - return userInteractionDisabled.get() ? ContentDisplay.LEFT : ContentDisplay.TEXT_ONLY; - } -} diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java index 9707981ec..295aa9244 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/ReceiveKeyController.java @@ -59,7 +59,7 @@ public class ReceiveKeyController implements FxController { private final HttpClient httpClient; @Inject - public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, AtomicReference keyPairRef, @Named("bearerToken") AtomicReference tokenRef, AtomicReference eciesParamsRef, UserInteractionLock result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, ErrorComponent.Builder errorComponent) { + public ReceiveKeyController(@KeyLoading Vault vault, ExecutorService executor, @KeyLoading Stage window, @Named("bearerToken") AtomicReference tokenRef, AtomicReference eciesParamsRef, UserInteractionLock result, @FxmlScene(FxmlFile.HUB_REGISTER_DEVICE) Lazy registerDeviceScene, ErrorComponent.Builder errorComponent) { this.window = window; this.bearerToken = Objects.requireNonNull(tokenRef.get()); this.eciesParamsRef = eciesParamsRef; diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java b/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java index 68654a632..ba2c308c7 100644 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java +++ b/src/main/java/org/cryptomator/ui/keyloading/hub/RegisterDeviceController.java @@ -1,6 +1,8 @@ package org.cryptomator.ui.keyloading.hub; import com.google.common.io.BaseEncoding; +import org.cryptomator.common.settings.DeviceKey; +import org.cryptomator.cryptolib.common.P384KeyPair; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.common.UserInteractionLock; import org.cryptomator.ui.keyloading.KeyLoading; @@ -25,16 +27,16 @@ public class RegisterDeviceController implements FxController { private final Application application; private final Stage window; private final HubConfig hubConfig; - private final KeyPair keyPair; + private final P384KeyPair keyPair; private final UserInteractionLock result; private final String verificationCode; @Inject - public RegisterDeviceController(Application application, SecureRandom csprng, @KeyLoading Stage window, HubConfig hubConfig, AtomicReference keyPairRef, UserInteractionLock result) { + public RegisterDeviceController(Application application, SecureRandom csprng, @KeyLoading Stage window, HubConfig hubConfig, DeviceKey deviceKey, UserInteractionLock result) { this.application = application; this.window = window; this.hubConfig = hubConfig; - this.keyPair = Objects.requireNonNull(keyPairRef.get()); + this.keyPair = Objects.requireNonNull(deviceKey.get()); this.result = result; this.window.addEventHandler(WindowEvent.WINDOW_HIDING, this::windowClosed); this.verificationCode = String.format("%06d", csprng.nextInt(1_000_000)); diff --git a/src/main/java/org/cryptomator/ui/keyloading/hub/X509Helper.java b/src/main/java/org/cryptomator/ui/keyloading/hub/X509Helper.java deleted file mode 100644 index d9b7b953a..000000000 --- a/src/main/java/org/cryptomator/ui/keyloading/hub/X509Helper.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; -import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; -import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.math.BigInteger; -import java.security.KeyPair; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.cert.CertificateFactory; -import java.security.cert.X509Certificate; -import java.sql.Date; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.UUID; - -class X509Helper { - - private static final X500Name ISSUER = new X500Name("CN=Cryptomator"); - private static final X500Name SUBJECT = new X500Name("CN=Self Signed Cert"); - private static final ASN1ObjectIdentifier ASN1_SUBJECT_KEY_ID = new ASN1ObjectIdentifier("2.5.29.14"); - - private X509Helper() {} - - /** - * Creates a self-signed X509Certificate containing the public key and signed with the private key of a given key pair. - * - * @param keyPair A key pair - * @param signatureAlg A signature algorithm suited for the given key pair (see available algorithms) - * @return A self-signed X509Certificate - * @throws CertificateException If certificate generation failed, e.g. because of unsupported algorithms - */ - public static X509Certificate createSelfSignedCert(KeyPair keyPair, String signatureAlg) throws CertificateException { - try { - X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( // - ISSUER, // - randomSerialNo(), // - Date.from(Instant.now()), // - Date.from(Instant.now().plus(3650, ChronoUnit.DAYS)), // - SUBJECT, // - keyPair.getPublic()); - certificateBuilder.addExtension(ASN1_SUBJECT_KEY_ID, false, getX509ExtensionUtils().createSubjectKeyIdentifier(keyPair.getPublic())); - var signer = new JcaContentSignerBuilder(signatureAlg).build(keyPair.getPrivate()); - var cert = certificateBuilder.build(signer); - try (InputStream in = new ByteArrayInputStream(cert.getEncoded())) { - return (X509Certificate) getCertFactory().generateCertificate(in); - } - } catch (IOException | OperatorCreationException e) { - throw new CertificateException(e); - } - } - - private static BigInteger randomSerialNo() { - return BigInteger.valueOf(UUID.randomUUID().getMostSignificantBits()); - } - - private static JcaX509ExtensionUtils getX509ExtensionUtils() { - try { - return new JcaX509ExtensionUtils(); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("Every implementation of the Java platform is required to support SHA-1."); - } - } - - private static CertificateFactory getCertFactory() { - try { - return CertificateFactory.getInstance("X.509"); - } catch (CertificateException e) { - throw new IllegalStateException("Every implementation of the Java platform is required to support X.509."); - } - } - -} diff --git a/src/main/resources/fxml/hub_p12.fxml b/src/main/resources/fxml/hub_p12.fxml deleted file mode 100644 index 505e2b8a6..000000000 --- a/src/main/resources/fxml/hub_p12.fxml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/main/resources/fxml/hub_p12_create.fxml b/src/main/resources/fxml/hub_p12_create.fxml deleted file mode 100644 index a4897cbc9..000000000 --- a/src/main/resources/fxml/hub_p12_create.fxml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/fxml/hub_p12_load.fxml b/src/main/resources/fxml/hub_p12_load.fxml deleted file mode 100644 index 98fde1dd0..000000000 --- a/src/main/resources/fxml/hub_p12_load.fxml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/license/THIRD-PARTY.txt b/src/main/resources/license/THIRD-PARTY.txt index 0ed1b3cff..6f06bbb39 100644 --- a/src/main/resources/license/THIRD-PARTY.txt +++ b/src/main/resources/license/THIRD-PARTY.txt @@ -11,7 +11,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. -Cryptomator uses 46 third-party dependencies under the following licenses: +Cryptomator uses 43 third-party dependencies under the following licenses: Apache License v2.0: - jffi (com.github.jnr:jffi:1.2.23 - http://github.com/jnr/jffi) - jnr-a64asm (com.github.jnr:jnr-a64asm:1.0.0 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-a64asm) @@ -44,10 +44,6 @@ Cryptomator uses 46 third-party dependencies under the following licenses: - asm-commons (org.ow2.asm:asm-commons:7.1 - http://asm.ow2.org/) - asm-tree (org.ow2.asm:asm-tree:7.1 - http://asm.ow2.org/) - asm-util (org.ow2.asm:asm-util:7.1 - http://asm.ow2.org/) - Bouncy Castle Licence: - - Bouncy Castle PKIX, CMS, EAC, TSP, PKCS, OCSP, CMP, and CRMF APIs (org.bouncycastle:bcpkix-jdk15on:1.69 - https://www.bouncycastle.org/java.html) - - Bouncy Castle Provider (org.bouncycastle:bcprov-jdk15on:1.69 - https://www.bouncycastle.org/java.html) - - Bouncy Castle ASN.1 Extension and Utility APIs (org.bouncycastle:bcutil-jdk15on:1.69 - https://www.bouncycastle.org/java.html) Eclipse Public License - Version 1.0: - Jetty :: Servlet API and Schemas for JPMS and OSGi (org.eclipse.jetty.toolchain:jetty-servlet-api:4.0.6 - https://eclipse.org/jetty/jetty-servlet-api) Eclipse Public License - Version 2.0: diff --git a/src/test/java/org/cryptomator/ui/keyloading/hub/EciesHelperTest.java b/src/test/java/org/cryptomator/ui/keyloading/hub/EciesHelperTest.java deleted file mode 100644 index 0bb432608..000000000 --- a/src/test/java/org/cryptomator/ui/keyloading/hub/EciesHelperTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import com.google.common.io.BaseEncoding; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.extension.ParameterContext; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.converter.ArgumentConversionException; -import org.junit.jupiter.params.converter.ArgumentConverter; -import org.junit.jupiter.params.converter.ConvertWith; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.ValueSource; - -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; - -public class EciesHelperTest { - - @DisplayName("ECDH + KDF") - @ParameterizedTest - @ValueSource(ints = {16, 32, 44, 128}) - public void testEcdhAndKdf(int len) throws NoSuchAlgorithmException { - var alice = KeyPairGenerator.getInstance("EC").generateKeyPair(); - var bob = KeyPairGenerator.getInstance("EC").generateKeyPair(); - - byte[] result1 = EciesHelper.ecdhAndKdf(alice.getPrivate(), bob.getPublic(), len); - byte[] result2 = EciesHelper.ecdhAndKdf(bob.getPrivate(), alice.getPublic(), len); - - Assertions.assertArrayEquals(result1, result2); - } - - @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 testKdf(@ConvertWith(HexConverter.class) byte[] sharedSecret, @ConvertWith(HexConverter.class) byte[] sharedInfo, int outLen, @ConvertWith(HexConverter.class) byte[] expectedResult) { - byte[] result = EciesHelper.kdf(sharedSecret, sharedInfo, outLen); - Assertions.assertArrayEquals(expectedResult, result); - } - - public static 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 s) { - return BaseEncoding.base16().lowerCase().decode(s); - } else { - return null; - } - } - } - -} \ No newline at end of file diff --git a/src/test/java/org/cryptomator/ui/keyloading/hub/P12AccessHelperTest.java b/src/test/java/org/cryptomator/ui/keyloading/hub/P12AccessHelperTest.java deleted file mode 100644 index a92eb40b3..000000000 --- a/src/test/java/org/cryptomator/ui/keyloading/hub/P12AccessHelperTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -public class P12AccessHelperTest { - - @Test - public void testCreate(@TempDir Path tmpDir) throws IOException { - var p12File = tmpDir.resolve("test.p12"); - - var keyPair = P12AccessHelper.createNew(p12File, "asd".toCharArray()); - - Assertions.assertNotNull(keyPair); - Assertions.assertTrue(Files.exists(p12File)); - } - - @Nested - public class ExistingFile { - - private Path p12File; - - @BeforeEach - public void setup(@TempDir Path tmpDir) throws IOException { - p12File = tmpDir.resolve("test.p12"); - P12AccessHelper.createNew(p12File, "foo".toCharArray()); - } - - @Test - public void testLoadWithWrongPassword() { - Assertions.assertThrows(InvalidPassphraseException.class, () -> { - P12AccessHelper.loadExisting(p12File, "bar".toCharArray()); - }); - } - - @Test - public void testLoad() throws IOException { - var keyPair = P12AccessHelper.loadExisting(p12File, "foo".toCharArray()); - - Assertions.assertNotNull(keyPair); - } - } - -} \ No newline at end of file diff --git a/src/test/java/org/cryptomator/ui/keyloading/hub/X509HelperTest.java b/src/test/java/org/cryptomator/ui/keyloading/hub/X509HelperTest.java deleted file mode 100644 index 79abc82f4..000000000 --- a/src/test/java/org/cryptomator/ui/keyloading/hub/X509HelperTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.cryptomator.ui.keyloading.hub; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPairGenerator; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.spec.ECGenParameterSpec; - -public class X509HelperTest { - - @Test - public void testCreateCert() throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException, InvalidAlgorithmParameterException { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(new ECGenParameterSpec("secp256r1")); - var keyPair = keyGen.generateKeyPair(); - var cert = X509Helper.createSelfSignedCert(keyPair, "SHA256withECDSA"); - Assertions.assertNotNull(cert); - } - -} \ No newline at end of file