mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-05 08:45:08 -05:00
Work on Yubikey decryption
This commit is contained in:
parent
dc39b58609
commit
518d7116e2
@ -36,7 +36,7 @@ import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcPublicKeyDataDecryptorFactoryBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
@ -82,21 +82,27 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
}
|
||||
|
||||
public enum SecretKeyType {
|
||||
UNAVAILABLE(0), GNU_DUMMY (1), PASSPHRASE (2), PASSPHRASE_EMPTY (3), DIVERT_TO_CARD (4);
|
||||
UNAVAILABLE(0), GNU_DUMMY(1), PASSPHRASE(2), PASSPHRASE_EMPTY(3), DIVERT_TO_CARD(4);
|
||||
|
||||
final int mNum;
|
||||
|
||||
SecretKeyType(int num) {
|
||||
mNum = num;
|
||||
}
|
||||
|
||||
public static SecretKeyType fromNum(int num) {
|
||||
switch (num) {
|
||||
case 1: return GNU_DUMMY;
|
||||
case 2: return PASSPHRASE;
|
||||
case 3: return PASSPHRASE_EMPTY;
|
||||
case 4: return DIVERT_TO_CARD;
|
||||
case 1:
|
||||
return GNU_DUMMY;
|
||||
case 2:
|
||||
return PASSPHRASE;
|
||||
case 3:
|
||||
return PASSPHRASE_EMPTY;
|
||||
case 4:
|
||||
return DIVERT_TO_CARD;
|
||||
// if this case happens, it's probably a check from a database value
|
||||
default: return UNAVAILABLE;
|
||||
default:
|
||||
return UNAVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,14 +256,14 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
public PublicKeyDataDecryptorFactory getDecryptorFactory() {
|
||||
public PublicKeyDataDecryptorFactory getDecryptorFactory(byte[] nfcDecryptedSessionKey) {
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
||||
throw new PrivateKeyNotUnlockedException();
|
||||
}
|
||||
|
||||
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
||||
return new NfcPublicKeyDataDecryptorFactoryBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
|
||||
return new NfcSyncPublicKeyDataDecryptorFactoryBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(nfcDecryptedSessionKey);
|
||||
} else {
|
||||
return new JcePublicKeyDataDecryptorFactoryBuilder()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
|
||||
|
@ -42,7 +42,7 @@ import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcPublicKeyDataDecryptorFactoryBuilder;
|
||||
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
@ -78,6 +78,7 @@ public class PgpDecryptVerify {
|
||||
private String mPassphrase;
|
||||
private Set<Long> mAllowedKeyIds;
|
||||
private boolean mDecryptMetadataOnly;
|
||||
private byte[] mDecryptedSessionKey;
|
||||
|
||||
private PgpDecryptVerify(Builder builder) {
|
||||
// private Constructor can only be called from Builder
|
||||
@ -91,6 +92,7 @@ public class PgpDecryptVerify {
|
||||
this.mPassphrase = builder.mPassphrase;
|
||||
this.mAllowedKeyIds = builder.mAllowedKeyIds;
|
||||
this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly;
|
||||
this.mDecryptedSessionKey = builder.mDecryptedSessionKey;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
@ -106,6 +108,7 @@ public class PgpDecryptVerify {
|
||||
private String mPassphrase = null;
|
||||
private Set<Long> mAllowedKeyIds = null;
|
||||
private boolean mDecryptMetadataOnly = false;
|
||||
private byte[] mDecryptedSessionKey = null;
|
||||
|
||||
public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache,
|
||||
InputData data, OutputStream outStream) {
|
||||
@ -148,6 +151,11 @@ public class PgpDecryptVerify {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNfcState(byte[] decryptedSessionKey) {
|
||||
mDecryptedSessionKey = decryptedSessionKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PgpDecryptVerify build() {
|
||||
return new PgpDecryptVerify(this);
|
||||
}
|
||||
@ -196,10 +204,12 @@ public class PgpDecryptVerify {
|
||||
}
|
||||
|
||||
public static class NeedNfcDataException extends Exception {
|
||||
public byte[] mDec;
|
||||
public byte[] mEncryptedSessionKey;
|
||||
public String mPassphrase;
|
||||
|
||||
public NeedNfcDataException(byte[] dec) {
|
||||
mDec = dec;
|
||||
public NeedNfcDataException(byte[] encryptedSessionKey, String passphrase) {
|
||||
mEncryptedSessionKey = encryptedSessionKey;
|
||||
mPassphrase = passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
@ -379,11 +389,12 @@ public class PgpDecryptVerify {
|
||||
currentProgress += 2;
|
||||
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
|
||||
|
||||
PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
|
||||
try {
|
||||
PublicKeyDataDecryptorFactory decryptorFactory
|
||||
= secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey);
|
||||
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
|
||||
} catch (NfcPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
|
||||
throw new NeedNfcDataException(e.dec);
|
||||
} catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) {
|
||||
throw new NeedNfcDataException(e.encryptedSessionKey, mPassphrase);
|
||||
}
|
||||
encryptedData = encryptedDataAsymmetric;
|
||||
} else {
|
||||
|
@ -177,7 +177,7 @@ public class OpenPgpService extends RemoteService {
|
||||
return result;
|
||||
}
|
||||
|
||||
private Intent getNfcDecryptIntent(Intent data, String pin, byte[] dec) {
|
||||
private Intent getNfcDecryptIntent(Intent data, String pin, byte[] encryptedSessionKey) {
|
||||
// build PendingIntent for Yubikey NFC operations
|
||||
Intent intent = new Intent(getBaseContext(), NfcActivity.class);
|
||||
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
|
||||
@ -185,7 +185,7 @@ public class OpenPgpService extends RemoteService {
|
||||
intent.putExtra(NfcActivity.EXTRA_DATA, data);
|
||||
intent.putExtra(NfcActivity.EXTRA_PIN, pin);
|
||||
|
||||
intent.putExtra(NfcActivity.EXTRA_NFC_DEC, dec);
|
||||
intent.putExtra(NfcActivity.EXTRA_NFC_ENC_SESSION_KEY, encryptedSessionKey);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||
intent,
|
||||
@ -432,7 +432,6 @@ public class OpenPgpService extends RemoteService {
|
||||
|
||||
Intent result = new Intent();
|
||||
try {
|
||||
|
||||
String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
|
||||
long inputLength = is.available();
|
||||
InputData inputData = new InputData(is, inputLength);
|
||||
@ -453,12 +452,15 @@ public class OpenPgpService extends RemoteService {
|
||||
inputData, os
|
||||
);
|
||||
|
||||
byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY);
|
||||
|
||||
// allow only private keys associated with accounts of this app
|
||||
// no support for symmetric encryption
|
||||
builder.setPassphrase(passphrase)
|
||||
.setAllowSymmetricDecryption(false)
|
||||
.setAllowedKeyIds(allowedKeyIds)
|
||||
.setDecryptMetadataOnly(decryptMetadataOnly);
|
||||
.setDecryptMetadataOnly(decryptMetadataOnly)
|
||||
.setNfcState(nfcDecryptedSessionKey);
|
||||
|
||||
PgpDecryptVerifyResult decryptVerifyResult;
|
||||
try {
|
||||
@ -478,7 +480,7 @@ public class OpenPgpService extends RemoteService {
|
||||
throw new Exception(getString(R.string.error_integrity_check_failed));
|
||||
} catch (PgpDecryptVerify.NeedNfcDataException e) {
|
||||
// return PendingIntent to execute NFC activity
|
||||
return getNfcDecryptIntent(data, passphrase, e.mDec);
|
||||
return getNfcDecryptIntent(data, e.mPassphrase, e.mEncryptedSessionKey);
|
||||
}
|
||||
|
||||
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
|
||||
@ -522,7 +524,6 @@ public class OpenPgpService extends RemoteService {
|
||||
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
if (os != null) {
|
||||
|
@ -33,12 +33,15 @@ import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@ -182,7 +185,20 @@ public class DecryptFileFragment extends DecryptFragment {
|
||||
returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
|
||||
|
||||
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
|
||||
showPassphraseDialogForFilename(decryptVerifyResult.getKeyIdPassphraseNeeded());
|
||||
try {
|
||||
// try to get passphrase from cache...
|
||||
String passphrase = PassphraseCacheService.getCachedPassphrase(
|
||||
getActivity(), decryptVerifyResult.getKeyIdPassphraseNeeded());
|
||||
|
||||
if (passphrase != null) {
|
||||
// try again with passphrase from cache
|
||||
decryptOriginalFilename(passphrase);
|
||||
} else {
|
||||
showPassphraseDialogForFilename(decryptVerifyResult.getKeyIdPassphraseNeeded());
|
||||
}
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
Log.e(Constants.TAG, "PassphraseCacheService.KeyNotFoundException", e);
|
||||
}
|
||||
} else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
|
||||
decryptVerifyResult.getStatus()) {
|
||||
showPassphraseDialogForFilename(Constants.key.symmetric);
|
||||
|
@ -186,6 +186,7 @@ public class EditKeyFragment extends LoaderFragment implements
|
||||
mSaveKeyringParcel.mMasterKeyId);
|
||||
} catch (PassphraseCacheService.KeyNotFoundException e) {
|
||||
Log.e(Constants.TAG, "Key not found!", e);
|
||||
Toast.makeText(getActivity(), R.string.error_no_secret_key_found, Toast.LENGTH_SHORT).show();
|
||||
getActivity().finish();
|
||||
return;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user