diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..92322c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +target/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..971ad2d --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2021-2024 Ralph Plawetzki +Copyright (c) 2024-2024 Hatter Jiang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 1e8211f..0e6ae47 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,58 @@ -# card-cryptomator +# tinyencrypt-cryptomator +> This project is fork from https://github.com/purejava/keepassxc-cryptomator , add GnuPG support + +[![GitHub Release](https://img.shields.io/github/v/release/jht5945/gnupg-cryptomator)](https://github.com/jht5945/gnupg-cryptomator/releases) +[![License](https://img.shields.io/github/license/jht5945/gnupg-cryptomator.svg)](https://github.com/jht5945/gnupg-cryptomator/blob/master/LICENSE) + +Plug-in for Cryptomator to store vault passwords with card-cli encryption. + +# Build Project + +Requirement: + +* JDK 17 or later +* Maven 3.8.4 or later + +```shell +mvn package +``` + +Copy packaged plugin from `target/card-cryptomator-$VERSION.jar` to Cryptomator plugins directory. +> Usage reference [Wiki](https://github.com/purejava/keepassxc-cryptomator/wiki) + +# Configuration + +Configure file location: + +* `/etc/cryptomator/card_config.json` +* `~/.config/cryptomator/card_config.json` + +```json +{ +} +``` + +# Documentation + +For documentation please take a look at the [Wiki](https://github.com/purejava/keepassxc-cryptomator/wiki). + +Plugin location: + +| OS | Default Dir | +| ---- | ---- | +| Mac | `~/Library/Application Support/Cryptomator/Plugins` | +| Linux | `~/.local/share/Cryptomator/plugins` | +| Windows | `%homepath%\AppData\Roaming\Cryptomator\Plugins` | + +# How it works? + +This plugin use card-cli encrypt and decrypt passwords. + +# Copyright + +Copyright (C) 2021-2024 Ralph Plawetzki
+Copyright (C) 2024-2024 Hatter Jiang + +The Cryptomator logo is Copyright (C) of https://cryptomator.org/
+The KeePassXC logo is Copyright (C) of https://keepassxc.org/ diff --git a/build.json b/build.json new file mode 100644 index 0000000..e07661d --- /dev/null +++ b/build.json @@ -0,0 +1,7 @@ +{ + "java": "17", + "builder": { + "name": "maven", + "version": "3.8.4" + } +} diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml new file mode 100644 index 0000000..8443f3a --- /dev/null +++ b/dependency-reduced-pom.xml @@ -0,0 +1,275 @@ + + + 4.0.0 + me.hatter + card-cryptomator + card-cryptomator + 1.0.0 + Plug-in for Cryptomator to store vault passwords with card-cli encryption. + https://git.hatter.ink/hatter/card-cryptomator + + + Ralph Plawetzki + ralph@purejava.org + https://github.com/purejava + +1 + + + Hatter Jiang + jht5945@gmail.com + https://github.com/jht5945 + +8 + + + + + MIT License + https://opensource.org/licenses/MIT + + + + scm:git:git@github.com:jht5945/tinyencrypt-cryptomator.git + scm:git:git@github.com:jht5945/tinyencrypt-cryptomator.git + git@github.com:jht5945/tinyencrypt-cryptomator.git + + + + + + maven-clean-plugin + 3.3.2 + + + maven-resources-plugin + 3.3.1 + + false + + + + maven-compiler-plugin + 3.13.0 + + + maven-surefire-plugin + 3.3.0 + + + maven-jar-plugin + 3.4.1 + + true + + + + maven-install-plugin + 3.1.2 + + + maven-deploy-plugin + 3.1.2 + + + maven-site-plugin + 4.0.0-M15 + + + maven-project-info-reports-plugin + 3.6.0 + + + + + + maven-compiler-plugin + 3.13.0 + + 17 + 17 + + + + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + maven-javadoc-plugin + 3.7.0 + + + attach-javadocs + + jar + + + false + + + + + + maven-shade-plugin + 3.6.0 + + + package + + shade + + + + + + maven-gpg-plugin + 3.2.4 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.7.0 + true + + ossrh + https://oss.sonatype.org/ + false + + + + maven-surefire-plugin + 3.3.0 + + + org.junit.jupiter + junit-jupiter-engine + 5.10.2 + + + + + + + + sign + + + + maven-gpg-plugin + 3.2.4 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + + + + + + org.slf4j + slf4j-simple + 2.0.13 + test + + + org.junit.jupiter + junit-jupiter-api + 5.10.2 + test + + + opentest4j + org.opentest4j + + + junit-platform-commons + org.junit.platform + + + apiguardian-api + org.apiguardian + + + + + org.junit.jupiter + junit-jupiter-engine + 5.10.2 + test + + + junit-platform-engine + org.junit.platform + + + apiguardian-api + org.apiguardian + + + + + org.junit.jupiter + junit-jupiter + 5.10.2 + test + + + junit-jupiter-params + org.junit.jupiter + + + + + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + + 1.3.1 + 2.0.13 + 2.11.0 + UTF-8 + 5.10.2 + 1.2.5 + + diff --git a/keepassxc-cryptomator.png b/keepassxc-cryptomator.png new file mode 100644 index 0000000..03b1e69 Binary files /dev/null and b/keepassxc-cryptomator.png differ diff --git a/keepassxc-cryptomator.svg b/keepassxc-cryptomator.svg new file mode 100644 index 0000000..78e6796 --- /dev/null +++ b/keepassxc-cryptomator.svg @@ -0,0 +1,258 @@ + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6260d78 --- /dev/null +++ b/pom.xml @@ -0,0 +1,286 @@ + + + 4.0.0 + me.hatter + card-cryptomator + 1.0.0 + + card-cryptomator + Plug-in for Cryptomator to store vault passwords with card-cli encryption. + https://git.hatter.ink/hatter/card-cryptomator + + + + MIT License + https://opensource.org/licenses/MIT + + + + + + Ralph Plawetzki + ralph@purejava.org + +1 + + https://github.com/purejava + + + Hatter Jiang + jht5945@gmail.com + +8 + + https://github.com/jht5945 + + + + + scm:git:git@github.com:jht5945/tinyencrypt-cryptomator.git + scm:git:git@github.com:jht5945/tinyencrypt-cryptomator.git + git@github.com:jht5945/tinyencrypt-cryptomator.git + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + UTF-8 + + + 1.3.1 + 1.2.5 + 2.11.0 + 2.0.13 + 5.10.2 + + + + + org.cryptomator + integrations-api + ${api.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + com.google.code.gson + gson + ${gson.version} + + + org.purejava + keepassxc-proxy-access + ${keepassxc-proxy.version} + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + + + + + + maven-clean-plugin + 3.3.2 + + + maven-resources-plugin + 3.3.1 + + false + + + + maven-compiler-plugin + 3.13.0 + + + maven-surefire-plugin + 3.3.0 + + + maven-jar-plugin + 3.4.1 + + true + + + + maven-install-plugin + 3.1.2 + + + maven-deploy-plugin + 3.1.2 + + + maven-site-plugin + 4.0.0-M15 + + + maven-project-info-reports-plugin + 3.6.0 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 17 + 17 + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.7.0 + + + attach-javadocs + + jar + + + false + + + + + + maven-shade-plugin + 3.6.0 + + + package + + shade + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.4 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.7.0 + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.3.0 + + + org.junit.jupiter + junit-jupiter-engine + 5.10.2 + + + + + + + + sign + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.4 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + + + + diff --git a/src/main/java/me/hatter/integrations/card/CardAccessProvider.java b/src/main/java/me/hatter/integrations/card/CardAccessProvider.java new file mode 100644 index 0000000..55e22ab --- /dev/null +++ b/src/main/java/me/hatter/integrations/card/CardAccessProvider.java @@ -0,0 +1,80 @@ +package me.hatter.integrations.card; + +import org.cryptomator.integrations.keychain.KeychainAccessException; +import org.cryptomator.integrations.keychain.KeychainAccessProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author hatterjiang + */ +public class CardAccessProvider implements KeychainAccessProvider { + + private static final Logger LOG = LoggerFactory.getLogger(CardAccessProvider.class); + + private CardConfig cardConfig; + + public CardAccessProvider() { + try { + cardConfig = Utils.loadCardConfig(); + if (!Utils.checkCardCliReady(cardConfig)) { + LOG.error("Check card-cli command failed"); + cardConfig = null; + } + } catch (KeychainAccessException e) { + cardConfig = null; + LOG.error("Load card-cli config failed", e); + } + } + + @Override + public String displayName() { + return "CardCli"; + } + + @Override + public boolean isSupported() { + return cardConfig != null; + } + + @Override + public boolean isLocked() { + // No lock status + return false; + } + + @Override + public void storePassphrase(String vault, CharSequence password) throws KeychainAccessException { + storePassphrase(vault, "Vault", password); + } + + @Override + public void storePassphrase(String vault, String name, CharSequence password) throws KeychainAccessException { + LOG.info("Store password for: " + vault + " / " + name); + Utils.storePassword(cardConfig, vault, name, password); + } + + @Override + public char[] loadPassphrase(String vault) throws KeychainAccessException { + LOG.info("Load password for: " + vault); + final String password = Utils.loadPassword(cardConfig, vault); + return password.toCharArray(); + } + + @Override + public void deletePassphrase(String vault) throws KeychainAccessException { + LOG.info("Delete password for: " + vault); + Utils.deletePassword(cardConfig, vault); + } + + @Override + public void changePassphrase(String vault, CharSequence password) throws KeychainAccessException { + changePassphrase(vault, "Vault", password); + } + + @Override + public void changePassphrase(String vault, String name, CharSequence password) throws KeychainAccessException { + LOG.info("Change password for: " + vault + " / " + name); + Utils.storePassword(cardConfig, vault, name, password); + } +} diff --git a/src/main/java/me/hatter/integrations/card/CardConfig.java b/src/main/java/me/hatter/integrations/card/CardConfig.java new file mode 100644 index 0000000..c8e1e29 --- /dev/null +++ b/src/main/java/me/hatter/integrations/card/CardConfig.java @@ -0,0 +1,21 @@ +package me.hatter.integrations.card; + +/** + * card-cli config + * + * @author hatterjiang + */ +public class CardConfig { + /** + * OPTIONAL, Encrypt key base path, default "~/.config/cryptomator/card_keys/" + */ + private String encryptKeyBasePath; + + public String getEncryptKeyBasePath() { + return encryptKeyBasePath; + } + + public void setEncryptKeyBasePath(String encryptKeyBasePath) { + this.encryptKeyBasePath = encryptKeyBasePath; + } +} diff --git a/src/main/java/me/hatter/integrations/card/CardHmacDecryptResult.java b/src/main/java/me/hatter/integrations/card/CardHmacDecryptResult.java new file mode 100644 index 0000000..8bed4cf --- /dev/null +++ b/src/main/java/me/hatter/integrations/card/CardHmacDecryptResult.java @@ -0,0 +1,13 @@ +package me.hatter.integrations.card; + +public class CardHmacDecryptResult { + private String plaintext; + + public String getPlaintext() { + return plaintext; + } + + public void setPlaintext(String plaintext) { + this.plaintext = plaintext; + } +} diff --git a/src/main/java/me/hatter/integrations/card/CardHmacEncryptResult.java b/src/main/java/me/hatter/integrations/card/CardHmacEncryptResult.java new file mode 100644 index 0000000..e0be3ed --- /dev/null +++ b/src/main/java/me/hatter/integrations/card/CardHmacEncryptResult.java @@ -0,0 +1,13 @@ +package me.hatter.integrations.card; + +public class CardHmacEncryptResult { + private String ciphertext; + + public String getCiphertext() { + return ciphertext; + } + + public void setCiphertext(String ciphertext) { + this.ciphertext = ciphertext; + } +} diff --git a/src/main/java/me/hatter/integrations/card/Utils.java b/src/main/java/me/hatter/integrations/card/Utils.java new file mode 100644 index 0000000..97efd04 --- /dev/null +++ b/src/main/java/me/hatter/integrations/card/Utils.java @@ -0,0 +1,285 @@ +package me.hatter.integrations.card; + +import com.google.gson.Gson; +import org.apache.commons.lang3.StringUtils; +import org.cryptomator.integrations.keychain.KeychainAccessException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author hatterjiang + */ +public class Utils { + private static final Logger LOG = LoggerFactory.getLogger(Utils.class); + private static final String USER_HOME = System.getProperty("user.home"); + private static final String DEFAULT_CARD_COMMAND = new File(USER_HOME, "bin/card-cli").getAbsolutePath(); + private static final File CARD_CONFIG_FILE1 = new File("/etc/cryptomator/card_config.json"); + private static final File CARD_CONFIG_FILE2 = new File(USER_HOME, ".config/cryptomator/card_config.json"); + private static final File DEFAULT_ENCRYPTION_KEY_BASE_PATH = new File(USER_HOME, ".config/cryptomator/card_keys/"); + + public static boolean isCheckPassphraseStored() { + final StackTraceElement stack = getCallerStackTrace(); + if (stack != null) { + return "isPassphraseStored".equals(stack.getMethodName()); + } + return false; + } + + public static StackTraceElement getCallerStackTrace() { + // org.cryptomator.common.keychain.KeychainManager :: isPassphraseStored + final StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + for (int i = 0; i < stackTraceElements.length; i++) { + final StackTraceElement stack = stackTraceElements[i]; + if ("org.cryptomator.common.keychain.KeychainManager".equals(stack.getClassName())) { + return stack; + } + } + return null; + } + + public static boolean checkCardCliReady(CardConfig cardConfig) { + if (cardConfig == null) { + return false; + } + try { + final UtilsCommandResult versionResult = runCardCli(cardConfig, null, "-V"); + if (versionResult.getExitValue() == 0) { + return true; + } + LOG.warn("Check card-cli not success: " + versionResult); + return false; + } catch (KeychainAccessException e) { + LOG.warn("Check card-cli failed", e); + return false; + } + } + + public static CardConfig loadCardConfig() throws KeychainAccessException { + final File configFile = getCardConfigFile(); + if (configFile == null) { + return new CardConfig(); + } + final String configJson = readFile(configFile); + final CardConfig cardConfig; + try { + cardConfig = new Gson().fromJson(configJson, CardConfig.class); + } catch (Exception e) { + throw new KeychainAccessException("Parse card-cli config file: " + configFile + " failed", e); + } + return cardConfig; + } + + public static void deletePassword(CardConfig cardConfig, String vault) { + final File keyFile = getKeyFile(cardConfig, vault); + if (keyFile.exists() && keyFile.isFile()) { + keyFile.delete(); + } + } + + public static String loadPassword(CardConfig cardConfig, String vault) throws KeychainAccessException { + final File keyFile = getKeyFile(cardConfig, vault); + if (isCheckPassphraseStored()) { + LOG.info("Check passphrase stored: " + vault + ", exists: " + keyFile.exists()); + if (keyFile.exists()) { + // this is only for check passphrase stored + return "123456"; + } + } + if (!keyFile.isFile()) { + throw new KeychainAccessException("Password key file: " + keyFile + " not found"); + } + final String encryptedKey = readFile(keyFile); + final byte[] password = decrypt(cardConfig, encryptedKey); + return new String(password, StandardCharsets.UTF_8); + } + + public static void storePassword(CardConfig cardConfig, String vault, String name, CharSequence password) throws KeychainAccessException { + final String encryptedPassword = encrypt(cardConfig, password.toString().getBytes(StandardCharsets.UTF_8), name); + final File keyFile = getKeyFile(cardConfig, vault); + writeFile(keyFile, encryptedPassword); + } + + private static File getKeyFile(CardConfig cardConfig, String vault) { + final StringBuilder sb = new StringBuilder(vault.length()); + for (char c : vault.toCharArray()) { + if ((c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || (c == '-' || c == '.')) { + sb.append(c); + } else if (c == '_') { + sb.append("__"); + } else { + sb.append('_'); + final String hex = Integer.toHexString(c); + if (hex.length() % 2 != 0) { + sb.append('0'); + } + sb.append(hex); + } + } + return new File(getEncryptKeyBasePath(cardConfig), sb.toString()); + } + + private static String readFile(File file) throws KeychainAccessException { + final StringBuilder sb = new StringBuilder((int) file.length()); + try (final BufferedReader reader = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) { + for (int b; ((b = reader.read()) != -1); ) { + sb.append((char) b); + } + return sb.toString(); + } catch (IOException e) { + throw new KeychainAccessException("Read file: " + file + " failed", e); + } + } + + private static void writeFile(File file, String content) throws KeychainAccessException { + try (FileOutputStream fos = new FileOutputStream(file)) { + fos.write(content.getBytes(StandardCharsets.UTF_8)); + } catch (Exception e) { + throw new KeychainAccessException("Write file: " + file + " failed", e); + } + } + + private static byte[] decrypt(CardConfig cardConfig, String input) throws KeychainAccessException { + final UtilsCommandResult decryptResult = runCardCli( + cardConfig, + null, + "hmac-decrypt", + "--ciphertext", input, + "--auto-pbe", + "--json" + ); + if (decryptResult.getExitValue() != 0) { + throw new KeychainAccessException("card-cli decrypt failed: " + decryptResult); + } + final String resultString = new String(decryptResult.getStdout(), StandardCharsets.UTF_8); + final CardHmacDecryptResult result = new Gson().fromJson(resultString, CardHmacDecryptResult.class); + return Base64.getDecoder().decode(result.getPlaintext()); + } + + private static String encrypt(CardConfig cardConfig, byte[] input, String name) throws KeychainAccessException { + final UtilsCommandResult encryptResult = runCardCli( + cardConfig, + null, + "hmac-encrypt", + "--plaintext", Base64.getEncoder().encodeToString(input), + "--with-pbe-encrypt", + "--pbe-iteration", "1000000", + "--json" + ); + if (encryptResult.getExitValue() != 0) { + throw new KeychainAccessException("card-cli encrypt failed: " + encryptResult); + } + final String resultString = new String(encryptResult.getStdout(), StandardCharsets.UTF_8); + final CardHmacEncryptResult result = new Gson().fromJson(resultString, CardHmacEncryptResult.class); + return result.getCiphertext(); + } + + private static UtilsCommandResult runCardCli(CardConfig cardConfig, byte[] input, String... arguments) throws KeychainAccessException { + final List commands = new ArrayList<>(); + commands.add(DEFAULT_CARD_COMMAND); + if ((arguments == null) || (arguments.length == 0)) { + throw new KeychainAccessException("card-cli not arguments"); + } + commands.addAll(Arrays.asList(arguments)); + try { + final ProcessBuilder processBuilder = new ProcessBuilder(commands); + final Process process = processBuilder.start(); + + // ----- STD IN ----- + final AtomicReference inThreadException = new AtomicReference<>(); + final Thread inThread = new Thread(() -> { + if ((input != null) && (input.length > 0)) { + try (OutputStream processIn = process.getOutputStream()) { + processIn.write(input); + } catch (IOException e) { + inThreadException.set(e); + } + } + }); + inThread.setDaemon(true); + inThread.setName("card-cli-stdin"); + + // ----- STD OUT ----- + final AtomicReference outThreadException = new AtomicReference<>(); + final ByteArrayOutputStream outBaos = new ByteArrayOutputStream(); + final Thread outThread = getThread(process.getInputStream(), outBaos, outThreadException, "card-cli-stdout"); + // ----- STD ERR ----- + final AtomicReference errThreadException = new AtomicReference<>(); + final ByteArrayOutputStream errBaos = new ByteArrayOutputStream(); + final Thread errThread = getThread(process.getErrorStream(), errBaos, errThreadException, "card-cli-stderr"); + + inThread.start(); + outThread.start(); + errThread.start(); + + inThread.join(); + if (inThreadException.get() != null) { + throw inThreadException.get(); + } + outThread.join(); + if (outThreadException.get() != null) { + throw outThreadException.get(); + } + errThread.join(); + if (errThreadException.get() != null) { + throw errThreadException.get(); + } + final int exitValue = process.waitFor(); + + return new UtilsCommandResult(exitValue, outBaos.toByteArray(), errBaos.toByteArray()); + } catch (Exception e) { + throw new KeychainAccessException("Run card-cli command failed: " + commands, e); + } + } + + private static Thread getThread(InputStream is, ByteArrayOutputStream outBaos, AtomicReference outThreadException, String name) { + final Thread outThread = new Thread(() -> { + int b; + try { + while ((b = is.read()) != -1) { + outBaos.write(b); + } + } catch (IOException e) { + outThreadException.set(e); + } + }); + outThread.setDaemon(true); + outThread.setName(name); + return outThread; + } + + private static File getEncryptKeyBasePath(CardConfig cardConfig) { + final File encryptKeyBase; + if ((cardConfig != null) && StringUtils.isNoneEmpty(cardConfig.getEncryptKeyBasePath())) { + encryptKeyBase = new File(cardConfig.getEncryptKeyBasePath()); + } else { + encryptKeyBase = DEFAULT_ENCRYPTION_KEY_BASE_PATH; + } + if (encryptKeyBase.isDirectory()) { + return encryptKeyBase; + } + LOG.info("Make dirs: " + encryptKeyBase); + encryptKeyBase.mkdirs(); + return encryptKeyBase; + } + + private static File getCardConfigFile() throws KeychainAccessException { + for (File configFile : Arrays.asList(CARD_CONFIG_FILE1, CARD_CONFIG_FILE2)) { + LOG.info("Check config file: " + configFile + ": " + Arrays.asList(configFile.exists(), configFile.isFile())); + if (configFile.exists() && configFile.isFile()) { + return configFile; + } + } + return null; + } +} diff --git a/src/main/java/me/hatter/integrations/card/UtilsCommandResult.java b/src/main/java/me/hatter/integrations/card/UtilsCommandResult.java new file mode 100644 index 0000000..c168f03 --- /dev/null +++ b/src/main/java/me/hatter/integrations/card/UtilsCommandResult.java @@ -0,0 +1,40 @@ +package me.hatter.integrations.card; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +/** + * @author hatterjiang + */ +public class UtilsCommandResult { + private final int exitValue; + private final byte[] stdout; + private final byte[] stderr; + + public UtilsCommandResult(int exitValue, byte[] stdout, byte[] stderr) { + this.exitValue = exitValue; + this.stdout = stdout; + this.stderr = stderr; + } + + public int getExitValue() { + return exitValue; + } + + public byte[] getStdout() { + return stdout; + } + + public byte[] getStderr() { + return stderr; + } + + @Override + public String toString() { + return "CommandResult{" + + "exitValue=" + exitValue + + ", stdout=" + Arrays.toString(stdout) + " (" + new String(stdout, StandardCharsets.UTF_8) + ")" + + ", stderr=" + Arrays.toString(stderr) + " (" + new String(stderr, StandardCharsets.UTF_8) + ")" + + '}'; + } +} diff --git a/src/main/resources/META-INF/services/org.cryptomator.integrations.keychain.KeychainAccessProvider b/src/main/resources/META-INF/services/org.cryptomator.integrations.keychain.KeychainAccessProvider new file mode 100644 index 0000000..14c758c --- /dev/null +++ b/src/main/resources/META-INF/services/org.cryptomator.integrations.keychain.KeychainAccessProvider @@ -0,0 +1 @@ +me.hatter.integrations.card.CardAccessProvider \ No newline at end of file diff --git a/src/test/java/me/hatter/integrations/card/KeePassXCAccessTest.java b/src/test/java/me/hatter/integrations/card/KeePassXCAccessTest.java new file mode 100644 index 0000000..0c14b61 --- /dev/null +++ b/src/test/java/me/hatter/integrations/card/KeePassXCAccessTest.java @@ -0,0 +1,20 @@ +package me.hatter.integrations.card; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit test for simple KeePassXCAccess. + */ +public class KeePassXCAccessTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +}