This commit is contained in:
Dominik Schürmann 2014-07-17 20:29:07 +02:00
parent 3cce00d61e
commit f2a6064e38
7 changed files with 133 additions and 25 deletions

View File

@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream; import org.spongycastle.bcpg.BCPGOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPCompressedDataGenerator; import org.spongycastle.openpgp.PGPCompressedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator; import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
@ -38,11 +39,13 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import java.security.SignatureException; import java.security.SignatureException;
@ -70,6 +73,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase; private String mSignaturePassphrase;
private boolean mEncryptToSigner; private boolean mEncryptToSigner;
private boolean mCleartextInput; private boolean mCleartextInput;
private String mNfcData;
private static byte[] NEW_LINE; private static byte[] NEW_LINE;
@ -100,6 +104,7 @@ public class PgpSignEncrypt {
this.mSignaturePassphrase = builder.mSignaturePassphrase; this.mSignaturePassphrase = builder.mSignaturePassphrase;
this.mEncryptToSigner = builder.mEncryptToSigner; this.mEncryptToSigner = builder.mEncryptToSigner;
this.mCleartextInput = builder.mCleartextInput; this.mCleartextInput = builder.mCleartextInput;
this.mNfcData = builder.mNfcData;
} }
public static class Builder { public static class Builder {
@ -122,6 +127,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase = null; private String mSignaturePassphrase = null;
private boolean mEncryptToSigner = false; private boolean mEncryptToSigner = false;
private boolean mCleartextInput = false; private boolean mCleartextInput = false;
private String mNfcData = null;
public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) { public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper; this.mProviderHelper = providerHelper;
@ -170,6 +176,12 @@ public class PgpSignEncrypt {
return this; return this;
} }
/**
* Generate old V3 signatures
*
* @param signatureForceV3
* @return
*/
public Builder setSignatureForceV3(boolean signatureForceV3) { public Builder setSignatureForceV3(boolean signatureForceV3) {
mSignatureForceV3 = signatureForceV3; mSignatureForceV3 = signatureForceV3;
return this; return this;
@ -200,6 +212,11 @@ public class PgpSignEncrypt {
return this; return this;
} }
public Builder setNfcData(String nfcData) {
mNfcData = nfcData;
return this;
}
public PgpSignEncrypt build() { public PgpSignEncrypt build() {
return new PgpSignEncrypt(this); return new PgpSignEncrypt(this);
} }
@ -232,12 +249,26 @@ public class PgpSignEncrypt {
} }
} }
public static class NeedNfcDataException extends Exception {
public String mData;
public NeedNfcDataException(String 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 * Signs and/or encrypts data based on parameters of class
*/ */
public void execute() public void execute()
throws IOException, PGPException, NoSuchProviderException, throws IOException, PGPException, NoSuchProviderException,
NoSuchAlgorithmException, SignatureException, KeyExtractionException, NoSigningKeyException, NoPassphraseException { NoSuchAlgorithmException, SignatureException, KeyExtractionException, NoSigningKeyException, NoPassphraseException, NeedNfcDataException {
boolean enableSignature = mSignatureMasterKeyId != Constants.key.none; boolean enableSignature = mSignatureMasterKeyId != Constants.key.none;
boolean enableEncryption = ((mEncryptionMasterKeyIds != null && mEncryptionMasterKeyIds.length > 0) boolean enableEncryption = ((mEncryptionMasterKeyIds != null && mEncryptionMasterKeyIds.length > 0)
@ -255,16 +286,6 @@ public class PgpSignEncrypt {
mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mSignatureMasterKeyId; 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 */ /* Get keys for signature generation for later usage */
WrappedSecretKey signingKey = null; WrappedSecretKey signingKey = null;
if (enableSignature) { if (enableSignature) {
@ -276,7 +297,7 @@ public class PgpSignEncrypt {
} }
try { try {
signingKey = signingKeyRing.getSigningSubKey(); signingKey = signingKeyRing.getSigningSubKey();
} catch(PgpGeneralException e) { } catch (PgpGeneralException e) {
throw new NoSigningKeyException(); throw new NoSigningKeyException();
} }
@ -329,17 +350,24 @@ public class PgpSignEncrypt {
} }
} }
// HACK
boolean useNfc = false;
if (signingKey.getSecretKey().getS2K().getType() == S2K.GNU_DUMMY_S2K
&& signingKey.getSecretKey().getS2K().getProtectionMode() == 2) {
useNfc = true;
}
/* Initialize signature generator object for later usage */ /* Initialize signature generator object for later usage */
PGPSignatureGenerator signatureGenerator = null; PGPSignatureGenerator signatureGenerator = null;
PGPV3SignatureGenerator signatureV3Generator = null; PGPV3SignatureGenerator signatureV3Generator = null;
if (enableSignature) { if (enableSignature && !useNfc) {
updateProgress(R.string.progress_preparing_signature, 10, 100); updateProgress(R.string.progress_preparing_signature, 10, 100);
try { try {
boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption; boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
if (mSignatureForceV3) { if (mSignatureForceV3) {
signatureV3Generator = signingKey.getV3SignatureGenerator( signatureV3Generator = signingKey.getV3SignatureGenerator(
mSignatureHashAlgorithm,cleartext); mSignatureHashAlgorithm, cleartext);
} else { } else {
signatureGenerator = signingKey.getSignatureGenerator( signatureGenerator = signingKey.getSignatureGenerator(
mSignatureHashAlgorithm, cleartext); mSignatureHashAlgorithm, cleartext);
@ -349,12 +377,39 @@ public class PgpSignEncrypt {
throw new KeyExtractionException(); throw new KeyExtractionException();
} }
} }
// else if (enableSignature && useNfc) {
//
// }
ArmoredOutputStream armorOut = null;
OutputStream out = null;
if (mEnableAsciiArmorOutput && !useNfc) {
armorOut = new ArmoredOutputStream(mOutStream);
armorOut.setHeader("Version", mVersionHeader);
out = armorOut;
} else {
out = mOutStream;
}
PGPCompressedDataGenerator compressGen = null; PGPCompressedDataGenerator compressGen = null;
OutputStream pOut; OutputStream pOut = null;
OutputStream encryptionOut = null; OutputStream encryptionOut = null;
BCPGOutputStream bcpgOut; BCPGOutputStream bcpgOut;
if (enableEncryption) {
// Barrier function!
if (useNfc && mNfcData == null) {
Log.d(Constants.TAG, "mNfcData is null");
String nfcData = convertStreamToString(mData.getInputStream());
Log.d(Constants.TAG, "nfcData: " + nfcData);
throw new NeedNfcDataException(nfcData);
}
if (useNfc) {
Log.d(Constants.TAG, "mNfcData: " + mNfcData);
out.write(mNfcData.getBytes());
out.flush();
} else if (enableEncryption) {
/* actual encryption */ /* actual encryption */
encryptionOut = cPk.open(out, new byte[1 << 16]); encryptionOut = cPk.open(out, new byte[1 << 16]);
@ -488,7 +543,7 @@ public class PgpSignEncrypt {
Log.e(Constants.TAG, "not supported!"); Log.e(Constants.TAG, "not supported!");
} }
if (enableSignature) { if (enableSignature && !useNfc) {
updateProgress(R.string.progress_generating_signature, 95, 100); updateProgress(R.string.progress_generating_signature, 95, 100);
if (mSignatureForceV3) { if (mSignatureForceV3) {
signatureV3Generator.generate().encode(pOut); signatureV3Generator.generate().encode(pOut);
@ -507,7 +562,7 @@ public class PgpSignEncrypt {
encryptionOut.close(); encryptionOut.close();
} }
if (mEnableAsciiArmorOutput) { if (mEnableAsciiArmorOutput && !useNfc) {
armorOut.close(); armorOut.close();
} }

View File

@ -179,6 +179,8 @@ public class UncachedKeyRing {
// Set to 1, except if the encryption type is GNU_DUMMY_S2K // Set to 1, except if the encryption type is GNU_DUMMY_S2K
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) { if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
result.add(sub.getKeyID()); result.add(sub.getKeyID());
} else {
Log.d(Constants.TAG, "S2K GNU extension!, mode: " + s2k.getProtectionMode());
} }
} }
return result; return result;

View File

@ -197,4 +197,9 @@ public class WrappedSecretKey extends WrappedPublicKey {
return new UncachedSecretKey(mSecretKey); return new UncachedSecretKey(mSecretKey);
} }
// HACK
public PGPSecretKey getSecretKey() {
return mSecretKey;
}
} }

View File

@ -28,6 +28,7 @@ import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openkeychain.nfc.NfcActivity;
import org.spongycastle.util.Arrays; import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
@ -135,7 +136,26 @@ public class OpenPgpService extends RemoteService {
return result; return result;
} }
private Intent getPassphraseBundleIntent(Intent data, long keyId) { private Intent getNfcIntent(Intent data, String in) {
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getBaseContext(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_SIGN);
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 // build PendingIntent for passphrase input
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE); intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE);
@ -167,10 +187,12 @@ public class OpenPgpService extends RemoteService {
} }
if (passphrase == null) { if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client // 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; return passphraseBundle;
} }
String nfcData = data.getStringExtra(OpenPgpApi.EXTRA_NFC_DATA);
// Get Input- and OutputStream from ParcelFileDescriptor // Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output); OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
@ -187,7 +209,8 @@ public class OpenPgpService extends RemoteService {
.setSignatureHashAlgorithm(accSettings.getHashAlgorithm()) .setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
.setSignatureForceV3(false) .setSignatureForceV3(false)
.setSignatureMasterKeyId(accSettings.getKeyId()) .setSignatureMasterKeyId(accSettings.getKeyId())
.setSignaturePassphrase(passphrase); .setSignaturePassphrase(passphrase)
.setNfcData(nfcData);
// TODO: currently always assume cleartext input, no sign-only of binary currently! // TODO: currently always assume cleartext input, no sign-only of binary currently!
builder.setCleartextInput(true); builder.setCleartextInput(true);
@ -202,6 +225,10 @@ public class OpenPgpService extends RemoteService {
throw new Exception(getString(R.string.error_no_signature_passphrase)); throw new Exception(getString(R.string.error_no_signature_passphrase));
} catch (PgpSignEncrypt.NoSigningKeyException e) { } catch (PgpSignEncrypt.NoSigningKeyException e) {
throw new Exception(getString(R.string.error_no_signature_key)); 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 { } finally {
is.close(); is.close();
@ -212,6 +239,7 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result; return result;
} catch (Exception e) { } catch (Exception e) {
Log.d(Constants.TAG, "signImpl", e);
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR, result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
@ -282,7 +310,7 @@ public class OpenPgpService extends RemoteService {
} }
if (passphrase == null) { if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client // 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; return passphraseBundle;
} }
@ -317,6 +345,7 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result; return result;
} catch (Exception e) { } catch (Exception e) {
Log.d(Constants.TAG, "encryptAndSignImpl", e);
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR, result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
@ -376,7 +405,7 @@ public class OpenPgpService extends RemoteService {
if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
// get PendingIntent for passphrase input, add it to given params and return to client // get PendingIntent for passphrase input, add it to given params and return to client
Intent passphraseBundle = Intent passphraseBundle =
getPassphraseBundleIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded()); getPassphraseIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
return passphraseBundle; return passphraseBundle;
} else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED == } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
decryptVerifyResult.getStatus()) { decryptVerifyResult.getStatus()) {
@ -411,6 +440,7 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
return result; return result;
} catch (Exception e) { } catch (Exception e) {
Log.d(Constants.TAG, "decryptAndVerifyImpl", e);
Intent result = new Intent(); Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR, result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));

View File

@ -17,9 +17,12 @@
package org.sufficientlysecure.keychain.remote.ui; package org.sufficientlysecure.keychain.remote.ui;
import android.content.ComponentName;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBar; 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.remote.AppSettings;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.util.List;
public class AppSettingsActivity extends ActionBarActivity { public class AppSettingsActivity extends ActionBarActivity {
private Uri mAppUri; private Uri mAppUri;
@ -90,6 +95,7 @@ public class AppSettingsActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
// disabled: breaks Yubikey NFC Foreground dispatching
private void startApp() { private void startApp() {
Intent i; Intent i;
PackageManager manager = getPackageManager(); PackageManager manager = getPackageManager();
@ -97,6 +103,8 @@ public class AppSettingsActivity extends ActionBarActivity {
i = manager.getLaunchIntentForPackage(mAppSettings.getPackageName()); i = manager.getLaunchIntentForPackage(mAppSettings.getPackageName());
if (i == null) if (i == null)
throw new PackageManager.NameNotFoundException(); 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); i.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(i); startActivity(i);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {

View File

@ -38,6 +38,7 @@ import android.os.RemoteException;
import android.support.v4.util.LongSparseArray; import android.support.v4.util.LongSparseArray;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import org.spongycastle.bcpg.S2K;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
@ -198,6 +199,13 @@ public class PassphraseCacheService extends Service {
return ""; 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 // get cached passphrase
CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId); CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) { if (cachedPassphrase == null) {

@ -1 +1 @@
Subproject commit 8e588506e0bfb5220f7b57ba42f46a73740c96c7 Subproject commit 56b4f0628a957088ec03a0c7c05c8e61c0f71472