Skip to content

Commit

Permalink
Move unlocking to session.
Browse files Browse the repository at this point in the history
  • Loading branch information
ylangisc committed Nov 29, 2024
1 parent 2cce2bd commit fd90c67
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import ch.cyberduck.core.sds.io.swagger.client.model.UserUserPublicKey;
import ch.cyberduck.core.sds.triplecrypt.TripleCryptConverter;
import ch.cyberduck.core.sds.triplecrypt.TripleCryptExceptionMappingService;
import ch.cyberduck.core.sds.triplecrypt.TripleCryptKeyPair;
import ch.cyberduck.core.shared.ThreadPoolSchedulerFeature;

import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -103,7 +102,7 @@ protected List<UserFileKeySetRequest> operate(final PasswordCallback callback) t
for(FileFileKeys fileKey : files.get(item.getFileId())) {
final EncryptedFileKey encryptedFileKey = TripleCryptConverter.toCryptoEncryptedFileKey(fileKey.getFileKeyContainer());
final UserKeyPairContainer keyPairForDecryption = session.getKeyPairForFileKey(encryptedFileKey.getVersion());
final Credentials passphrase = new TripleCryptKeyPair(session.getHost()).unlock(callback, TripleCryptConverter.toCryptoUserKeyPair(keyPairForDecryption));
final Credentials passphrase = session.unlockTripleCryptKeyPair(callback, TripleCryptConverter.toCryptoUserKeyPair(keyPairForDecryption));
for(UserUserPublicKey userPublicKey : userPublicKeys.get(item.getUserId())) {
final EncryptedFileKey fk = this.encryptFileKey(
TripleCryptConverter.toCryptoUserPrivateKey(keyPairForDecryption.getPrivateKeyContainer()),
Expand Down
65 changes: 45 additions & 20 deletions dracoon/src/main/java/ch/cyberduck/core/sds/SDSSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,8 @@
* GNU General Public License for more details.
*/

import ch.cyberduck.core.ConnectionTimeoutFactory;
import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.DefaultIOExceptionMappingService;
import ch.cyberduck.core.ExpiringObjectHolder;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostKeyCallback;
import ch.cyberduck.core.HostUrlProvider;
import ch.cyberduck.core.ListService;
import ch.cyberduck.core.LocaleFactory;
import ch.cyberduck.core.LoginCallback;
import ch.cyberduck.core.PreferencesUseragentProvider;
import ch.cyberduck.core.Scheme;
import ch.cyberduck.core.UrlProvider;
import ch.cyberduck.core.Version;
import ch.cyberduck.core.*;
import ch.cyberduck.core.cache.LRUCache;
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.InteroperabilityException;
import ch.cyberduck.core.exception.LoginCanceledException;
Expand Down Expand Up @@ -100,6 +88,8 @@
import com.dracoon.sdk.crypto.error.UnknownVersionException;
import com.dracoon.sdk.crypto.model.EncryptedFileKey;
import com.dracoon.sdk.crypto.model.UserKeyPair;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;

import static ch.cyberduck.core.oauth.OAuth2AuthorizationService.CYBERDUCK_REDIRECT_URI;

Expand All @@ -124,6 +114,9 @@ public class SDSSession extends HttpSession<SDSApiClient> {
private final ExpiringObjectHolder<UserKeyPairContainer> keyPairDeprecated
= new ExpiringObjectHolder<>(preferences.getLong("sds.encryption.keys.ttl"));

private final ExpiringObjectHolder<Credentials> keyPairPassphrase
= new ExpiringObjectHolder<>(preferences.getLong("sds.encryption.keys.ttl"));

private final ExpiringObjectHolder<SystemDefaults> systemDefaults
= new ExpiringObjectHolder<>(preferences.getLong("sds.useracount.ttl"));

Expand All @@ -136,6 +129,14 @@ public class SDSSession extends HttpSession<SDSApiClient> {
private final ExpiringObjectHolder<SoftwareVersionData> softwareVersion
= new ExpiringObjectHolder<>(preferences.getLong("sds.useracount.ttl"));

private final LRUCache<UserKeyPair, Credentials> keyPairPassphrases
= LRUCache.build(new RemovalListener<UserKeyPair, Credentials>() {
@Override
public void onRemoval(final RemovalNotification<UserKeyPair, Credentials> notification) {
//
}
}, 2, preferences.getLong("sds.encryption.keys.ttl"), false);

private UserKeyPair.Version requiredKeyPairVersion;

private final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(this);
Expand Down Expand Up @@ -188,7 +189,7 @@ public void process(final HttpRequest request, final HttpContext context) throws
}
configuration.setServiceUnavailableRetryStrategy(new CustomServiceUnavailableRetryStrategy(host,

new ExecutionCountServiceUnavailableRetryStrategy(new PreconditionFailedResponseInterceptor(host, authorizationService, prompt),
new ExecutionCountServiceUnavailableRetryStrategy(new PreconditionFailedResponseInterceptor(host, authorizationService, prompt),
new OAuth2ErrorResponseInterceptor(host, authorizationService))));
if(new HostPreferences(host).getBoolean("sds.limit.requests.enable")) {
configuration.addInterceptorLast(new RateLimitingHttpRequestInterceptor(new DefaultHttpRateLimiter(
Expand Down Expand Up @@ -315,6 +316,30 @@ private boolean isNewCryptoAvailable() throws BackgroundException {
return false;
}

public Credentials unlockTripleCryptKeyPair(final PasswordCallback callback, final UserKeyPair keypair) throws BackgroundException {
if(!keyPairPassphrases.contains(keypair)) {
try {
keyPairPassphrases.put(keypair, new TripleCryptKeyPair().unlock(callback, host, keypair));
}
catch(CryptoException e) {
throw new TripleCryptExceptionMappingService().map(e);
}
}
return keyPairPassphrases.get(keypair);
}

public Credentials unlockTripleCryptKeyPair(final PasswordCallback callback, final UserKeyPair keypair, final String passphrase) throws BackgroundException {
if(!keyPairPassphrases.contains(keypair)) {
try {
keyPairPassphrases.put(keypair, new TripleCryptKeyPair().unlock(callback, host, keypair, passphrase));
}
catch(CryptoException e) {
throw new TripleCryptExceptionMappingService().map(e);
}
}
return keyPairPassphrases.get(keypair);
}

protected void unlockTripleCryptKeyPair(final LoginCallback prompt, final UserAccountWrapper user,
final UserKeyPair.Version requiredKeyPairVersion) throws BackgroundException {
try {
Expand All @@ -336,14 +361,14 @@ protected void unlockTripleCryptKeyPair(final LoginCallback prompt, final UserAc
final UserKeyPairContainer deprecated = new UserApi(client).requestUserKeyPair(StringUtils.EMPTY, UserKeyPair.Version.RSA2048.getValue(), null);
final UserKeyPair keypair = TripleCryptConverter.toCryptoUserKeyPair(deprecated);
log.debug("Attempt to unlock deprecated private key {}", keypair.getUserPrivateKey());
deprecatedCredentials = new TripleCryptKeyPair(host).unlock(prompt, keypair);
deprecatedCredentials = this.unlockTripleCryptKeyPair(prompt, keypair);
keyPairDeprecated.set(deprecated);
}
if(!migrated) {
final UserKeyPairContainer deprecated = new UserApi(client).requestUserKeyPair(StringUtils.EMPTY, UserKeyPair.Version.RSA2048.getValue(), null);
final UserKeyPair keypair = TripleCryptConverter.toCryptoUserKeyPair(deprecated);
log.debug("Attempt to unlock and migrate deprecated private key {}", keypair.getUserPrivateKey());
deprecatedCredentials = new TripleCryptKeyPair(host).unlock(prompt, keypair);
deprecatedCredentials = this.unlockTripleCryptKeyPair(prompt, keypair);
final UserKeyPair newPair = Crypto.generateUserKeyPair(requiredKeyPairVersion, deprecatedCredentials.getPassword().toCharArray());
final CreateKeyPairRequest request = new CreateKeyPairRequest();
request.setPreviousPrivateKey(deprecated.getPrivateKeyContainer());
Expand All @@ -361,15 +386,15 @@ protected void unlockTripleCryptKeyPair(final LoginCallback prompt, final UserAc
if(deprecatedCredentials != null) {
log.debug("Attempt to unlock private key with passphrase from deprecated private key {}", keypair.getUserPrivateKey());
if(Crypto.checkUserKeyPair(keypair, deprecatedCredentials.getPassword().toCharArray())) {
new TripleCryptKeyPair(host).unlock(prompt, keypair, deprecatedCredentials.getPassword());
this.unlockTripleCryptKeyPair(prompt, keypair, deprecatedCredentials.getPassword());
}
else {
new TripleCryptKeyPair(host).unlock(prompt, keypair);
this.unlockTripleCryptKeyPair(prompt, keypair);
}
}
else {
log.debug("Attempt to unlock private key {}", keypair.getUserPrivateKey());
new TripleCryptKeyPair(host).unlock(prompt, keypair);
this.unlockTripleCryptKeyPair(prompt, keypair);
}
}
catch(CryptoException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import ch.cyberduck.core.sds.io.swagger.client.model.UserKeyPairContainer;
import ch.cyberduck.core.sds.triplecrypt.TripleCryptConverter;
import ch.cyberduck.core.sds.triplecrypt.TripleCryptExceptionMappingService;
import ch.cyberduck.core.sds.triplecrypt.TripleCryptKeyPair;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
Expand Down Expand Up @@ -119,7 +118,7 @@ public DescriptiveUrl toDownloadUrl(final Path file, final Sharee sharee, Create
final EncryptedFileKey encFileKey = TripleCryptConverter.toCryptoEncryptedFileKey(key);
final UserKeyPairContainer keyPairContainer = session.getKeyPairForFileKey(encFileKey.getVersion());
final UserKeyPair userKeyPair = TripleCryptConverter.toCryptoUserKeyPair(keyPairContainer);
final Credentials passphrase = new TripleCryptKeyPair(bookmark).unlock(callback, userKeyPair);
final Credentials passphrase = session.unlockTripleCryptKeyPair(callback, userKeyPair);

final PlainFileKey plainFileKey = Crypto.decryptFileKey(encFileKey, userKeyPair.getUserPrivateKey(), passphrase.getPassword().toCharArray());
// encrypt file key with a new key pair
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.DescriptiveUrl;
import ch.cyberduck.core.ExpiringObjectHolder;
import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostPasswordStore;
import ch.cyberduck.core.LocaleFactory;
Expand All @@ -28,7 +27,6 @@
import ch.cyberduck.core.exception.BackgroundException;
import ch.cyberduck.core.exception.LocalAccessDeniedException;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.preferences.HostPreferences;
import ch.cyberduck.core.shared.DefaultUrlProvider;
import ch.cyberduck.core.vault.VaultCredentials;

Expand All @@ -47,57 +45,45 @@ public class TripleCryptKeyPair {

private final HostPasswordStore keychain = PasswordStoreFactory.get();

private final Host host;

private final ExpiringObjectHolder<Credentials> cache;

public TripleCryptKeyPair(final Host host) {
this.host = host;
this.cache = new ExpiringObjectHolder<>(new HostPreferences(host).getLong("sds.encryption.keys.ttl"));
}

public Credentials unlock(final PasswordCallback callback, final UserKeyPair keypair) throws CryptoException, BackgroundException {
final String passphrase = keychain.getPassword(toServiceName(host, keypair.getUserPublicKey().getVersion()), toAccountName(host));
return this.unlock(callback, keypair, passphrase);
public Credentials unlock(final PasswordCallback callback, final Host bookmark, final UserKeyPair keypair) throws CryptoException, BackgroundException {
final String passphrase = keychain.getPassword(toServiceName(bookmark, keypair.getUserPublicKey().getVersion()), toAccountName(bookmark));
return this.unlock(callback, bookmark, keypair, passphrase);
}

public Credentials unlock(final PasswordCallback callback, final UserKeyPair keypair, final String passphrase) throws CryptoException, LoginCanceledException {
return this.unlock(callback, keypair, passphrase, LocaleFactory.localizedString("Enter your decryption password to access encrypted data rooms.", "SDS"));
public Credentials unlock(final PasswordCallback callback, final Host bookmark, final UserKeyPair keypair, final String passphrase) throws CryptoException, LoginCanceledException {
return this.unlock(callback, bookmark, keypair, passphrase, LocaleFactory.localizedString("Enter your decryption password to access encrypted data rooms.", "SDS"));
}

private Credentials unlock(final PasswordCallback callback, final UserKeyPair keypair, String passphrase, final String message) throws LoginCanceledException, CryptoException {
if(cache.get() == null) {
final Credentials credentials;
if(null == passphrase) {
credentials = callback.prompt(host, LocaleFactory.localizedString("Decryption password required", "SDS"), message,
new LoginOptions()
.icon(host.getProtocol().disk())
);
if(credentials.getPassword() == null) {
throw new LoginCanceledException();
}
}
else {
credentials = new VaultCredentials(passphrase).withSaved(false);
private Credentials unlock(final PasswordCallback callback, final Host bookmark, final UserKeyPair keypair, String passphrase, final String message) throws LoginCanceledException, CryptoException {
final Credentials credentials;
if(null == passphrase) {
credentials = callback.prompt(bookmark, LocaleFactory.localizedString("Decryption password required", "SDS"), message,
new LoginOptions()
.icon(bookmark.getProtocol().disk())
);
if(credentials.getPassword() == null) {
throw new LoginCanceledException();
}
if(!Crypto.checkUserKeyPair(keypair, credentials.getPassword().toCharArray())) {
return this.unlock(callback, keypair, null, String.format("%s. %s", LocaleFactory.localizedString("Invalid passphrase", "Credentials"), LocaleFactory.localizedString("Enter your decryption password to access encrypted data rooms.", "SDS")));
}
else {
if(credentials.isSaved()) {
log.info("Save encryption password for {}", host);
try {
keychain.addPassword(toServiceName(host, keypair.getUserPublicKey().getVersion()),
toAccountName(host), credentials.getPassword());
}
catch(LocalAccessDeniedException e) {
log.error("Failure {} saving credentials for {} in password store", e, host);
}
}
else {
credentials = new VaultCredentials(passphrase).withSaved(false);
}
if(!Crypto.checkUserKeyPair(keypair, credentials.getPassword().toCharArray())) {
return this.unlock(callback, bookmark, keypair, null, String.format("%s. %s", LocaleFactory.localizedString("Invalid passphrase", "Credentials"), LocaleFactory.localizedString("Enter your decryption password to access encrypted data rooms.", "SDS")));
}
else {
if(credentials.isSaved()) {
log.info("Save encryption password for {}", bookmark);
try {
keychain.addPassword(toServiceName(bookmark, keypair.getUserPublicKey().getVersion()),
toAccountName(bookmark), credentials.getPassword());
}
catch(LocalAccessDeniedException e) {
log.error("Failure {} saving credentials for {} in password store", e, bookmark);
}
cache.set(credentials);
}
return credentials;
}
return cache.get();
}

protected static String toServiceName(final Host bookmark, final UserKeyPair.Version version) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public InputStream read(final Path file, final TransferStatus status, final Conn
private Credentials unlock(final ConnectionCallback callback, final UserKeyPair userKeyPair) throws CryptoException, BackgroundException {
final Credentials passphrase;
try {
passphrase = new TripleCryptKeyPair(session.getHost()).unlock(callback, userKeyPair);
passphrase = session.unlockTripleCryptKeyPair(callback, userKeyPair);
}
catch(LoginCanceledException e) {
throw new AccessDeniedException(LocaleFactory.localizedString("Decryption password required", "SDS"), e);
Expand Down

0 comments on commit fd90c67

Please sign in to comment.