aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules5
-rw-r--r--OpenKeychain/build.gradle2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java91
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java84
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java44
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java8
m---------extern/openpgp-card-nfc-lib0
m---------extern/spongycastle0
-rw-r--r--settings.gradle1
12 files changed, 215 insertions, 37 deletions
diff --git a/.gitmodules b/.gitmodules
index 687126e37..93ca9d49b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -30,4 +30,7 @@
url = https://github.com/open-keychain/KeybaseLib.git
[submodule "extern/minidns"]
path = extern/minidns
- url = https://github.com/open-keychain/minidns.git \ No newline at end of file
+ url = https://github.com/open-keychain/minidns.git
+[submodule "extern/openpgp-card-nfc-lib"]
+ path = extern/openpgp-card-nfc-lib
+ url = https://github.com/open-keychain/openpgp-card-nfc-lib.git \ No newline at end of file
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index 11fa54a57..aa1932c56 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -19,7 +19,7 @@ dependencies {
compile project(':extern:SuperToasts:supertoasts')
compile project(':extern:minidns')
compile project(':extern:KeybaseLib:Lib')
-
+ compile project(':extern:openpgp-card-nfc-lib:library')
}
android {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 1ba028006..20bb1f97c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -95,7 +95,7 @@ public class PgpKeyHelper {
}
/**
- * Converts fingerprint to hex (optional: with whitespaces after 4 characters)
+ * Converts fingerprint to hex
* <p/>
* Fingerprint is shown using lowercase characters. Studies have shown that humans can
* better differentiate between numbers and letters when letters are lowercase.
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 4cb92c368..cc34e8737 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -18,8 +18,10 @@
package org.sufficientlysecure.keychain.pgp;
+import org.openkeychain.nfc.NfcHandler;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
+import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
@@ -29,6 +31,7 @@ import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -38,16 +41,20 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.LinkedList;
/**
* This class uses a Builder pattern!
@@ -70,6 +77,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase;
private boolean mEncryptToSigner;
private boolean mCleartextInput;
+ private byte[] mNfcData;
private static byte[] NEW_LINE;
@@ -100,6 +108,7 @@ public class PgpSignEncrypt {
this.mSignaturePassphrase = builder.mSignaturePassphrase;
this.mEncryptToSigner = builder.mEncryptToSigner;
this.mCleartextInput = builder.mCleartextInput;
+ this.mNfcData = builder.mNfcData;
}
public static class Builder {
@@ -122,6 +131,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase = null;
private boolean mEncryptToSigner = false;
private boolean mCleartextInput = false;
+ private byte[] mNfcData = null;
public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper;
@@ -130,7 +140,7 @@ public class PgpSignEncrypt {
this.mOutStream = outStream;
}
- public Builder setProgressable(Progressable progressable) {
+ public Builder setProgressable(Progressable progressable) {
mProgressable = progressable;
return this;
}
@@ -170,6 +180,12 @@ public class PgpSignEncrypt {
return this;
}
+ /**
+ * Generate old V3 signatures
+ *
+ * @param signatureForceV3
+ * @return
+ */
public Builder setSignatureForceV3(boolean signatureForceV3) {
mSignatureForceV3 = signatureForceV3;
return this;
@@ -200,6 +216,11 @@ public class PgpSignEncrypt {
return this;
}
+ public Builder setNfcData(byte[] nfcData) {
+ mNfcData = nfcData;
+ return this;
+ }
+
public PgpSignEncrypt build() {
return new PgpSignEncrypt(this);
}
@@ -227,17 +248,36 @@ public class PgpSignEncrypt {
}
}
+ public static class WrongPassphraseException extends Exception {
+ public WrongPassphraseException() {
+ }
+ }
+
public static class NoSigningKeyException extends Exception {
public NoSigningKeyException() {
}
}
+ public static class NeedNfcDataException extends Exception {
+ public byte[] mData;
+
+ public NeedNfcDataException(byte[] data) {
+ mData = data;
+ }
+ }
+
+ // TODO: remove later
+ static String convertStreamToString(java.io.InputStream is) {
+ java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
+ return s.hasNext() ? s.next() : "";
+ }
+
/**
* Signs and/or encrypts data based on parameters of class
*/
public void execute()
throws IOException, PGPException, NoSuchProviderException,
- NoSuchAlgorithmException, SignatureException, KeyExtractionException, NoSigningKeyException, NoPassphraseException {
+ NoSuchAlgorithmException, SignatureException, KeyExtractionException, NoSigningKeyException, NoPassphraseException, NeedNfcDataException, WrongPassphraseException {
boolean enableSignature = mSignatureMasterKeyId != Constants.key.none;
boolean enableEncryption = ((mEncryptionMasterKeyIds != null && mEncryptionMasterKeyIds.length > 0)
@@ -255,16 +295,6 @@ public class PgpSignEncrypt {
mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mSignatureMasterKeyId;
}
- ArmoredOutputStream armorOut = null;
- OutputStream out;
- if (mEnableAsciiArmorOutput) {
- armorOut = new ArmoredOutputStream(mOutStream);
- armorOut.setHeader("Version", mVersionHeader);
- out = armorOut;
- } else {
- out = mOutStream;
- }
-
/* Get keys for signature generation for later usage */
WrappedSecretKey signingKey = null;
if (enableSignature) {
@@ -276,7 +306,7 @@ public class PgpSignEncrypt {
}
try {
signingKey = signingKeyRing.getSigningSubKey();
- } catch(PgpGeneralException e) {
+ } catch (PgpGeneralException e) {
throw new NoSigningKeyException();
}
@@ -287,10 +317,19 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
try {
- signingKey.unlock(mSignaturePassphrase);
+ if (!signingKey.unlock(mSignaturePassphrase)) {
+ throw new WrongPassphraseException();
+ }
} catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
+
+ // check if hash algo is supported
+ LinkedList<Integer> supported = signingKey.getSupportedHashAlgorithms();
+ if (!supported.contains(mSignatureHashAlgorithm)) {
+ // get most preferred
+ mSignatureHashAlgorithm = supported.getLast();
+ }
}
updateProgress(R.string.progress_preparing_streams, 5, 100);
@@ -339,10 +378,10 @@ public class PgpSignEncrypt {
boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
if (mSignatureForceV3) {
signatureV3Generator = signingKey.getV3SignatureGenerator(
- mSignatureHashAlgorithm,cleartext);
+ mSignatureHashAlgorithm, cleartext);
} else {
signatureGenerator = signingKey.getSignatureGenerator(
- mSignatureHashAlgorithm, cleartext);
+ mSignatureHashAlgorithm, cleartext, mNfcData);
}
} catch (PgpGeneralException e) {
// TODO throw correct type of exception (which shouldn't be PGPException)
@@ -350,10 +389,21 @@ public class PgpSignEncrypt {
}
}
+ ArmoredOutputStream armorOut = null;
+ OutputStream out;
+ if (mEnableAsciiArmorOutput) {
+ armorOut = new ArmoredOutputStream(mOutStream);
+ armorOut.setHeader("Version", mVersionHeader);
+ out = armorOut;
+ } else {
+ out = mOutStream;
+ }
+
PGPCompressedDataGenerator compressGen = null;
- OutputStream pOut;
+ OutputStream pOut = null;
OutputStream encryptionOut = null;
BCPGOutputStream bcpgOut;
+
if (enableEncryption) {
/* actual encryption */
@@ -493,7 +543,12 @@ public class PgpSignEncrypt {
if (mSignatureForceV3) {
signatureV3Generator.generate().encode(pOut);
} else {
- signatureGenerator.generate().encode(pOut);
+ try {
+ signatureGenerator.generate().encode(pOut);
+ } catch (NfcSyncPGPContentSignerBuilder.NfcInteractionNeeded e) {
+ // this secret key diverts to a OpenPGP card, throw exception with to-be-signed hash
+ throw new NeedNfcDataException(e.hashToSign);
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 6020fc084..5dc3c598e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -176,9 +176,12 @@ public class UncachedKeyRing {
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
((PGPSecretKeyRing) mRing).getSecretKeys())) {
S2K s2k = sub.getS2K();
- // Set to 1, except if the encryption type is GNU_DUMMY_S2K
- if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
+ // add key, except if the private key has been stripped (GNU extension)
+ if(s2k == null || (s2k.getType() == S2K.GNU_DUMMY_S2K
+ && s2k.getProtectionMode() != S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY)) {
result.add(sub.getKeyID());
+ } else {
+ Log.d(Constants.TAG, "S2K GNU extension!, mode: " + s2k.getProtectionMode());
}
}
return result;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
index 98ad2b706..141f2d5eb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
@@ -1,5 +1,8 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
+import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -12,19 +15,26 @@ import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
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.NfcSyncPGPContentSignerBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.util.IterableIterator;
+import org.sufficientlysecure.keychain.util.Log;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
/** Wrapper for a PGPSecretKey.
*
@@ -42,6 +52,11 @@ public class WrappedSecretKey extends WrappedPublicKey {
private final PGPSecretKey mSecretKey;
private PGPPrivateKey mPrivateKey = null;
+ private int mPrivateKeyState = PRIVATE_KEY_STATE_LOCKED;
+ private static int PRIVATE_KEY_STATE_LOCKED = 0;
+ private static int PRIVATE_KEY_STATE_UNLOCKED = 1;
+ private static int PRIVATE_KEY_STATE_DIVERT_TO_CARD = 2;
+
WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
super(ring, key.getPublicKey());
mSecretKey = key;
@@ -59,11 +74,23 @@ public class WrappedSecretKey extends WrappedPublicKey {
return mSecretKey;
}
+ /**
+ * Returns true on right passphrase
+ */
public boolean unlock(String passphrase) throws PgpGeneralException {
+ // handle keys on OpenPGP cards like they were unlocked
+ if (mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K
+ && mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
+ mPrivateKeyState = PRIVATE_KEY_STATE_DIVERT_TO_CARD;
+ return true;
+ }
+
+ // try to extract keys using the passphrase
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
+ mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED;
} catch (PGPException e) {
return false;
}
@@ -73,16 +100,46 @@ public class WrappedSecretKey extends WrappedPublicKey {
return true;
}
- public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext)
+ // TODO: just a hack currently
+ public LinkedList<Integer> getSupportedHashAlgorithms() {
+ LinkedList<Integer> supported = new LinkedList<Integer>();
+
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
+ // TODO: only works with SHA256 ?!
+ supported.add(HashAlgorithmTags.SHA256);
+ } else {
+ supported.add(HashAlgorithmTags.MD5);
+ supported.add(HashAlgorithmTags.RIPEMD160);
+ supported.add(HashAlgorithmTags.SHA1);
+ supported.add(HashAlgorithmTags.SHA224);
+ supported.add(HashAlgorithmTags.SHA256);
+ supported.add(HashAlgorithmTags.SHA384);
+ supported.add(HashAlgorithmTags.SHA512); // preferred is latest
+ }
+
+ return supported;
+ }
+
+ public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext,
+ byte[] nfcSignedHash)
throws PgpGeneralException {
- if(mPrivateKey == null) {
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
throw new PrivateKeyNotUnlockedException();
}
- // content signer based on signing key algorithm and chosen hash algorithm
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPContentSignerBuilder contentSignerBuilder;
+ if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
+ // use synchronous "NFC based" SignerBuilder
+ contentSignerBuilder = new NfcSyncPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), hashAlgo,
+ mSecretKey.getKeyID(), nfcSignedHash)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ } else {
+ // content signer based on signing key algorithm and chosen hash algorithm
+ contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ }
int signatureType;
if (cleartext) {
@@ -101,13 +158,15 @@ public class WrappedSecretKey extends WrappedPublicKey {
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch(PGPException e) {
+ // TODO: simply throw PGPException!
throw new PgpGeneralException("Error initializing signature!", e);
}
}
public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext)
throws PgpGeneralException {
- if(mPrivateKey == null) {
+ // TODO: divert to card missing
+ if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) {
throw new PrivateKeyNotUnlockedException();
}
@@ -134,7 +193,8 @@ public class WrappedSecretKey extends WrappedPublicKey {
}
public PublicKeyDataDecryptorFactory getDecryptorFactory() {
- if(mPrivateKey == null) {
+ // TODO: divert to card missing
+ if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) {
throw new PrivateKeyNotUnlockedException();
}
return new JcePublicKeyDataDecryptorFactoryBuilder()
@@ -152,7 +212,8 @@ public class WrappedSecretKey extends WrappedPublicKey {
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
- if(mPrivateKey == null) {
+ // TODO: divert to card missing
+ if (mPrivateKeyState != PRIVATE_KEY_STATE_UNLOCKED) {
throw new PrivateKeyNotUnlockedException();
}
@@ -197,4 +258,9 @@ public class WrappedSecretKey extends WrappedPublicKey {
return new UncachedSecretKey(mSecretKey);
}
+ // HACK
+ public PGPSecretKey getSecretKey() {
+ return mSecretKey;
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 62d6b5ad6..a00759180 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -28,6 +28,7 @@ import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
+import org.openkeychain.nfc.NfcActivity;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -135,7 +136,26 @@ public class OpenPgpService extends RemoteService {
return result;
}
- private Intent getPassphraseBundleIntent(Intent data, long keyId) {
+ private Intent getNfcIntent(Intent data, byte[] in) {
+ // build PendingIntent for Yubikey NFC operations
+ Intent intent = new Intent(getBaseContext(), NfcActivity.class);
+ intent.setAction(NfcActivity.ACTION_SIGN_HASH);
+ intent.putExtra(NfcActivity.EXTRA_NFC_DATA, in);
+ intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ // pass params through to activity that it can be returned again later to repeat pgp operation
+ intent.putExtra(NfcActivity.EXTRA_DATA, data);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ return result;
+ }
+
+ private Intent getPassphraseIntent(Intent data, long keyId) {
// build PendingIntent for passphrase input
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE);
@@ -167,10 +187,12 @@ public class OpenPgpService extends RemoteService {
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
+ byte[] nfcData = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DATA);
+
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
@@ -187,7 +209,8 @@ public class OpenPgpService extends RemoteService {
.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
.setSignatureForceV3(false)
.setSignatureMasterKeyId(accSettings.getKeyId())
- .setSignaturePassphrase(passphrase);
+ .setSignaturePassphrase(passphrase)
+ .setNfcData(nfcData);
// TODO: currently always assume cleartext input, no sign-only of binary currently!
builder.setCleartextInput(true);
@@ -200,8 +223,14 @@ public class OpenPgpService extends RemoteService {
throw new Exception(getString(R.string.error_could_not_extract_private_key));
} catch (PgpSignEncrypt.NoPassphraseException e) {
throw new Exception(getString(R.string.error_no_signature_passphrase));
+ } catch (PgpSignEncrypt.WrongPassphraseException e) {
+ throw new Exception(getString(R.string.error_wrong_passphrase));
} catch (PgpSignEncrypt.NoSigningKeyException e) {
throw new Exception(getString(R.string.error_no_signature_key));
+ } catch (PgpSignEncrypt.NeedNfcDataException e) {
+ // return PendingIntent to execute NFC activity
+ Intent nfcIntent = getNfcIntent(data, e.mData);
+ return nfcIntent;
}
} finally {
is.close();
@@ -212,6 +241,7 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result;
} catch (Exception e) {
+ Log.d(Constants.TAG, "signImpl", e);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
@@ -282,7 +312,7 @@ public class OpenPgpService extends RemoteService {
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
@@ -305,6 +335,8 @@ public class OpenPgpService extends RemoteService {
throw new Exception(getString(R.string.error_could_not_extract_private_key));
} catch (PgpSignEncrypt.NoPassphraseException e) {
throw new Exception(getString(R.string.error_no_signature_passphrase));
+ } catch (PgpSignEncrypt.WrongPassphraseException e) {
+ throw new Exception(getString(R.string.error_wrong_passphrase));
} catch (PgpSignEncrypt.NoSigningKeyException e) {
throw new Exception(getString(R.string.error_no_signature_key));
}
@@ -317,6 +349,7 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result;
} catch (Exception e) {
+ Log.d(Constants.TAG, "encryptAndSignImpl", e);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
@@ -376,7 +409,7 @@ public class OpenPgpService extends RemoteService {
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
// get PendingIntent for passphrase input, add it to given params and return to client
Intent passphraseBundle =
- getPassphraseBundleIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
+ getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
return passphraseBundle;
} else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
decryptVerifyResult.getStatus()) {
@@ -411,6 +444,7 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result;
} catch (Exception e) {
+ Log.d(Constants.TAG, "decryptAndVerifyImpl", e);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
index 8df341f9e..b37405304 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -17,9 +17,12 @@
package org.sufficientlysecure.keychain.remote.ui;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
@@ -34,6 +37,8 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.util.Log;
+import java.util.List;
+
public class AppSettingsActivity extends ActionBarActivity {
private Uri mAppUri;
@@ -90,6 +95,7 @@ public class AppSettingsActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
+ // disabled: breaks Yubikey NFC Foreground dispatching
private void startApp() {
Intent i;
PackageManager manager = getPackageManager();
@@ -97,6 +103,8 @@ public class AppSettingsActivity extends ActionBarActivity {
i = manager.getLaunchIntentForPackage(mAppSettings.getPackageName());
if (i == null)
throw new PackageManager.NameNotFoundException();
+ // start like the Android launcher would do
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
i.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(i);
} catch (PackageManager.NameNotFoundException e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 13d9b497f..774b9a0df 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -38,6 +38,7 @@ import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import android.support.v4.app.NotificationCompat;
+import org.spongycastle.bcpg.S2K;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@@ -198,6 +199,13 @@ public class PassphraseCacheService extends Service {
return "";
}
+ // TODO: HACK
+ if (key.getSecretKey().getSecretKey().getS2K().getType() == S2K.GNU_DUMMY_S2K
+ && key.getSecretKey().getSecretKey().getS2K().getProtectionMode() == 2) {
+ // NFC!
+ return "123456";
+ }
+
// get cached passphrase
CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) {
diff --git a/extern/openpgp-card-nfc-lib b/extern/openpgp-card-nfc-lib
new file mode 160000
+Subproject bedea7f2734451e7990d4971cb7f9f4e3cbe2fe
diff --git a/extern/spongycastle b/extern/spongycastle
-Subproject c142a844b680652adb751a193a4e4a926a4c080
+Subproject a68ebd1ffc5af880903b2905c17e4f6ac0f1398
diff --git a/settings.gradle b/settings.gradle
index d8802320c..e134c52a8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -13,3 +13,4 @@ include ':extern:AppMsg:library'
include ':extern:SuperToasts:supertoasts'
include ':extern:minidns'
include ':extern:KeybaseLib:Lib'
+include ':extern:openpgp-card-nfc-lib:library'