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.BCPGOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPCompressedDataGenerator;
import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
@ -38,11 +39,13 @@ 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;
@ -70,6 +73,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase;
private boolean mEncryptToSigner;
private boolean mCleartextInput;
private String mNfcData;
private static byte[] NEW_LINE;
@ -100,6 +104,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 +127,7 @@ public class PgpSignEncrypt {
private String mSignaturePassphrase = null;
private boolean mEncryptToSigner = false;
private boolean mCleartextInput = false;
private String mNfcData = null;
public Builder(ProviderHelper providerHelper, String versionHeader, InputData data, OutputStream outStream) {
this.mProviderHelper = providerHelper;
@ -130,7 +136,7 @@ public class PgpSignEncrypt {
this.mOutStream = outStream;
}
public Builder setProgressable(Progressable progressable) {
public Builder setProgressable(Progressable progressable) {
mProgressable = progressable;
return this;
}
@ -170,6 +176,12 @@ public class PgpSignEncrypt {
return this;
}
/**
* Generate old V3 signatures
*
* @param signatureForceV3
* @return
*/
public Builder setSignatureForceV3(boolean signatureForceV3) {
mSignatureForceV3 = signatureForceV3;
return this;
@ -200,6 +212,11 @@ public class PgpSignEncrypt {
return this;
}
public Builder setNfcData(String nfcData) {
mNfcData = nfcData;
return this;
}
public PgpSignEncrypt build() {
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
*/
public void execute()
throws IOException, PGPException, NoSuchProviderException,
NoSuchAlgorithmException, SignatureException, KeyExtractionException, NoSigningKeyException, NoPassphraseException {
NoSuchAlgorithmException, SignatureException, KeyExtractionException, NoSigningKeyException, NoPassphraseException, NeedNfcDataException {
boolean enableSignature = mSignatureMasterKeyId != Constants.key.none;
boolean enableEncryption = ((mEncryptionMasterKeyIds != null && mEncryptionMasterKeyIds.length > 0)
@ -255,16 +286,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 +297,7 @@ public class PgpSignEncrypt {
}
try {
signingKey = signingKeyRing.getSigningSubKey();
} catch(PgpGeneralException e) {
} catch (PgpGeneralException e) {
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 */
PGPSignatureGenerator signatureGenerator = null;
PGPV3SignatureGenerator signatureV3Generator = null;
if (enableSignature) {
if (enableSignature && !useNfc) {
updateProgress(R.string.progress_preparing_signature, 10, 100);
try {
boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
if (mSignatureForceV3) {
signatureV3Generator = signingKey.getV3SignatureGenerator(
mSignatureHashAlgorithm,cleartext);
mSignatureHashAlgorithm, cleartext);
} else {
signatureGenerator = signingKey.getSignatureGenerator(
mSignatureHashAlgorithm, cleartext);
@ -349,12 +377,39 @@ public class PgpSignEncrypt {
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;
OutputStream pOut;
OutputStream pOut = null;
OutputStream encryptionOut = null;
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 */
encryptionOut = cPk.open(out, new byte[1 << 16]);
@ -488,7 +543,7 @@ public class PgpSignEncrypt {
Log.e(Constants.TAG, "not supported!");
}
if (enableSignature) {
if (enableSignature && !useNfc) {
updateProgress(R.string.progress_generating_signature, 95, 100);
if (mSignatureForceV3) {
signatureV3Generator.generate().encode(pOut);
@ -507,7 +562,7 @@ public class PgpSignEncrypt {
encryptionOut.close();
}
if (mEnableAsciiArmorOutput) {
if (mEnableAsciiArmorOutput && !useNfc) {
armorOut.close();
}

View File

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

View File

@ -197,4 +197,9 @@ public class WrappedSecretKey extends WrappedPublicKey {
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.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, 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
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;
}
String nfcData = data.getStringExtra(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);
@ -202,6 +225,10 @@ public class OpenPgpService extends RemoteService {
throw new Exception(getString(R.string.error_no_signature_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 +239,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 +310,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;
}
@ -317,6 +345,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 +405,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 +440,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()));

View File

@ -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) {

View File

@ -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) {

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