Merge pull request #643 from open-keychain/v/dev

wrap bouncycastle objects and don't use them in android-related code
This commit is contained in:
Dominik Schürmann 2014-06-03 13:06:49 +02:00
commit 84d03dd514
57 changed files with 2734 additions and 1797 deletions

View File

@ -1,6 +1,7 @@
# Design Notes on Open-Keychain
This document contains notes on the software design of open keychain.
This document contains notes on the software design of open keychain. Points
with a * are yet to be implemented.
## Database design

View File

@ -1,9 +1,11 @@
apply plugin: 'android'
apply plugin: 'android-test'
sourceSets {
testLocal {
androidTest {
java.srcDir file('src/test/java')
resources.srcDir file('src/test/resources')
// configure the set of classes for JUnit tests
// include '**/*Test.class'
}
}
@ -23,24 +25,25 @@ dependencies {
compile project(':extern:spongycastle:prov')
compile project(':extern:AppMsg:library')
// Dependencies for the `testLocal` task, make sure to list all your global dependencies here as well
testLocalCompile 'junit:junit:4.11'
testLocalCompile 'org.robolectric:robolectric:2.2'
testLocalCompile 'com.google.android:android:4.1.1.4'
testLocalCompile 'com.android.support:support-v4:19.1.0'
testLocalCompile 'com.android.support:appcompat-v7:19.1.0'
testLocalCompile project(':extern:openpgp-api-lib')
testLocalCompile project(':extern:openkeychain-api-lib')
testLocalCompile project(':extern:html-textview')
testLocalCompile project(':extern:StickyListHeaders:library')
testLocalCompile project(':extern:AndroidBootstrap:AndroidBootstrap')
testLocalCompile project(':extern:zxing-qr-code')
testLocalCompile project(':extern:zxing-android-integration')
testLocalCompile project(':extern:spongycastle:core')
testLocalCompile project(':extern:spongycastle:pg')
testLocalCompile project(':extern:spongycastle:pkix')
testLocalCompile project(':extern:spongycastle:prov')
testLocalCompile project(':extern:AppMsg:library')
// Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well
androidTestCompile 'junit:junit:4.10'
androidTestCompile 'org.robolectric:robolectric:2.1.+'
androidTestCompile 'com.squareup:fest-android:1.0.+'
androidTestCompile 'com.google.android:android:4.1.1.4'
androidTestCompile 'com.android.support:support-v4:19.1.0'
androidTestCompile 'com.android.support:appcompat-v7:19.1.0'
androidTestCompile project(':extern:openpgp-api-lib')
androidTestCompile project(':extern:openkeychain-api-lib')
androidTestCompile project(':extern:html-textview')
androidTestCompile project(':extern:StickyListHeaders:library')
androidTestCompile project(':extern:AndroidBootstrap:AndroidBootstrap')
androidTestCompile project(':extern:zxing-qr-code')
androidTestCompile project(':extern:zxing-android-integration')
androidTestCompile project(':extern:spongycastle:core')
androidTestCompile project(':extern:spongycastle:pg')
androidTestCompile project(':extern:spongycastle:pkix')
androidTestCompile project(':extern:spongycastle:prov')
androidTestCompile project(':extern:AppMsg:library')
}
android {
@ -92,20 +95,6 @@ android {
}
}
task localTest(type: Test, dependsOn: assemble) {
testClassesDir = sourceSets.testLocal.output.classesDir
android.sourceSets.main.java.srcDirs.each { dir ->
def buildDir = dir.getAbsolutePath().split("\\" + File.separator)
buildDir = (buildDir[0..(buildDir.length - 4)] + ['build', 'classes', 'debug']).join("\\" + File.separator)
sourceSets.testLocal.compileClasspath += files(buildDir)
sourceSets.testLocal.runtimeClasspath += files(buildDir)
}
classpath = sourceSets.testLocal.runtimeClasspath
}
// NOTE: This disables Lint!
tasks.whenTaskAdded { task ->
if (task.name.equals("lint")) {
@ -113,5 +102,3 @@ tasks.whenTaskAdded { task ->
}
}
// NOTE: tests disabled!
//check.dependsOn localTest

View File

@ -0,0 +1,12 @@
package tests;
import org.junit.Assert;
import org.junit.Test;
public class SomeTest {
@Test
public void willFail() {
// stub
// Assert.assertThat();
}
}

View File

@ -31,6 +31,7 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@ -51,14 +52,15 @@ public class ExportHelper {
public void deleteKey(Uri dataUri, Handler deleteHandler) {
try {
long masterKeyId = new ProviderHelper(mActivity).extractOrGetMasterKeyId(dataUri);
long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
} catch (ProviderHelper.NotFoundException e) {
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}

View File

@ -21,18 +21,10 @@ import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
@ -55,8 +47,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean mSelected;
private byte[] mBytes = new byte[]{};
public int describeContents() {
return 0;
}
@ -74,8 +64,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0));
dest.writeByte((byte) (mSelected ? 1 : 0));
dest.writeInt(mBytes.length);
dest.writeByteArray(mBytes);
dest.writeString(mExtraData);
}
@ -94,8 +82,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1;
vr.mSelected = source.readByte() == 1;
vr.mBytes = new byte[source.readInt()];
source.readByteArray(vr.mBytes);
vr.mExtraData = source.readString();
return vr;
@ -110,14 +96,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
return keyIdHex;
}
public byte[] getBytes() {
return mBytes;
}
public void setBytes(byte[] bytes) {
this.mBytes = bytes;
}
public boolean isSelected() {
return mSelected;
}
@ -233,53 +211,25 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
* Constructor based on key object, used for import from NFC, QR Codes, files
*/
@SuppressWarnings("unchecked")
public ImportKeysListEntry(Context context, PGPKeyRing pgpKeyRing) {
// save actual key object into entry, used to import it later
try {
this.mBytes = pgpKeyRing.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "IOException on pgpKeyRing.getEncoded()", e);
}
public ImportKeysListEntry(Context context, UncachedKeyRing ring) {
// selected is default
this.mSelected = true;
if (pgpKeyRing instanceof PGPSecretKeyRing) {
secretKey = true;
} else {
secretKey = false;
}
PGPPublicKey key = pgpKeyRing.getPublicKey();
secretKey = ring.isSecret();
UncachedPublicKey key = ring.getPublicKey();
userIds = new ArrayList<String>();
for (String userId : new IterableIterator<String>(key.getUserIDs())) {
userIds.add(userId);
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignaturesForID(userId))) {
if (sig.getHashedSubPackets() != null
&& sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
try {
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), key);
if (sig.verifyCertification(userId, key)) {
mPrimaryUserId = userId;
}
} catch (Exception e) {
// nothing bad happens, the key is just not considered the primary key id
}
}
mPrimaryUserId = key.getPrimaryUserId();
userIds = key.getUnorderedUserIds();
}
}
// if there was no user id flagged as primary, use the first one
if (mPrimaryUserId == null) {
mPrimaryUserId = userIds.get(0);
}
this.keyId = key.getKeyID();
this.keyId = key.getKeyId();
this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
this.revoked = key.isRevoked();
this.revoked = key.maybeRevoked();
this.fingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
this.bitStrength = key.getBitStrength();
final int algorithm = key.getAlgorithm();

View File

@ -0,0 +1,51 @@
package org.sufficientlysecure.keychain.keyimport;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import java.io.IOException;
/** This is a trivial wrapper around UncachedKeyRing which implements Parcelable. It exists
* for the sole purpose of keeping spongycastle and android imports in separate packages.
*/
public class ParcelableKeyRing implements Parcelable {
final byte[] mBytes;
final String mExpectedFingerprint;
public ParcelableKeyRing(byte[] bytes) {
mBytes = bytes;
mExpectedFingerprint = null;
}
public ParcelableKeyRing(byte[] bytes, String expectedFingerprint) {
mBytes = bytes;
mExpectedFingerprint = expectedFingerprint;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeByteArray(mBytes);
dest.writeString(mExpectedFingerprint);
}
public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() {
public ParcelableKeyRing createFromParcel(final Parcel source) {
return new ParcelableKeyRing(source.createByteArray());
}
public ParcelableKeyRing[] newArray(final int size) {
return new ParcelableKeyRing[size];
}
};
public int describeContents() {
return 0;
}
public UncachedKeyRing getUncachedKeyRing() throws PgpGeneralException, IOException {
return UncachedKeyRing.decodeFromData(mBytes);
}
}

View File

@ -0,0 +1,36 @@
package org.sufficientlysecure.keychain.pgp;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
/** An abstract KeyRing.
*
* This is an abstract class for all KeyRing constructs. It serves as a common
* denominator of available information, two implementations wrapping the same
* keyring should in all cases agree on the output of all methods described
* here.
*
* @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
* @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
*
*/
public abstract class KeyRing {
abstract public long getMasterKeyId() throws PgpGeneralException;
abstract public String getPrimaryUserId() throws PgpGeneralException;
abstract public boolean isRevoked() throws PgpGeneralException;
abstract public boolean canCertify() throws PgpGeneralException;
abstract public long getEncryptId() throws PgpGeneralException;
abstract public boolean hasEncrypt() throws PgpGeneralException;
abstract public long getSignId() throws PgpGeneralException;
abstract public boolean hasSign() throws PgpGeneralException;
abstract public int getVerified() throws PgpGeneralException;
}

View File

@ -21,49 +21,25 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
public class PgpConversionHelper {
/**
* Convert from byte[] to PGPKeyRing
*
* @param keysBytes
* @return
*/
public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
PGPKeyRing keyRing = null;
try {
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
Log.e(Constants.TAG, "No keys given!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
}
return keyRing;
}
/**
* Convert from byte[] to ArrayList<PGPSecretKey>
*
* @param keysBytes
* @return
*/
public static ArrayList<PGPSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
public static ArrayList<UncachedSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
Object obj = null;
ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
try {
while ((obj = factory.nextObject()) != null) {
PGPSecretKey secKey = null;
@ -72,7 +48,7 @@ public class PgpConversionHelper {
if (secKey == null) {
Log.e(Constants.TAG, "No keys given!");
}
keys.add(secKey);
keys.add(new UncachedSecretKey(secKey));
} else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
PGPSecretKeyRing keyRing = null;
keyRing = (PGPSecretKeyRing) obj;
@ -82,7 +58,7 @@ public class PgpConversionHelper {
@SuppressWarnings("unchecked")
Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
while (itr.hasNext()) {
keys.add(itr.next());
keys.add(new UncachedSecretKey(itr.next()));
}
}
}
@ -100,7 +76,7 @@ public class PgpConversionHelper {
* @param keyBytes
* @return
*/
public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
public static UncachedSecretKey BytesToPGPSecretKey(byte[] keyBytes) {
PGPObjectFactory factory = new PGPObjectFactory(keyBytes);
Object obj = null;
try {
@ -121,80 +97,7 @@ public class PgpConversionHelper {
secKey = keyRing.getSecretKey();
}
return secKey;
}
/**
* Convert from byte[] to PGPSignature
*
* @param sigBytes
* @return
*/
public static PGPSignature BytesToPGPSignature(byte[] sigBytes) {
PGPObjectFactory factory = new PGPObjectFactory(sigBytes);
PGPSignatureList signatures = null;
try {
if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
Log.e(Constants.TAG, "No signatures given!");
return null;
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
return null;
}
return signatures.get(0);
}
/**
* Convert from ArrayList<PGPSecretKey> to byte[]
*
* @param keys
* @return
*/
public static byte[] PGPSecretKeyArrayListToBytes(ArrayList<PGPSecretKey> keys) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
for (PGPSecretKey key : keys) {
try {
key.encode(os);
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting ArrayList<PGPSecretKey> to byte[]!", e);
}
}
return os.toByteArray();
}
/**
* Convert from PGPSecretKey to byte[]
*
* @param key
* @return
*/
public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) {
try {
return key.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Encoding failed", e);
return null;
}
}
/**
* Convert from PGPSecretKeyRing to byte[]
*
* @param keyRing
* @return
*/
public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) {
try {
return keyRing.getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Encoding failed", e);
return null;
}
return new UncachedSecretKey(secKey);
}
}

View File

@ -18,10 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
import android.net.Uri;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
@ -31,29 +28,19 @@ import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPOnePassSignatureList;
import org.spongycastle.openpgp.PGPPBEEncryptedData;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
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.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
@ -67,7 +54,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
@ -248,7 +234,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
PGPSecretKey secretEncryptionKey = null;
WrappedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@ -260,15 +246,12 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
long masterKeyId;
PGPSecretKeyRing secretKeyRing;
WrappedSecretKeyRing secretKeyRing;
try {
// get master key id for this encryption key id
masterKeyId = mProviderHelper.getMasterKeyId(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
);
// get actual keyring object based on master key id
secretKeyRing = mProviderHelper.getPGPSecretKeyRing(masterKeyId);
secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
);
} catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop
continue;
@ -278,13 +261,14 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
}
/* secret key exists in database! */
long masterKeyId = secretEncryptionKey.getRing().getMasterKeyId();
// allow only specific keys for decryption?
if (mAllowedKeyIds != null) {
@ -359,23 +343,17 @@ public class PgpDecryptVerify {
} else if (asymmetricPacketFound) {
currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
PGPPrivateKey privateKey;
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mPassphrase.toCharArray());
privateKey = secretEncryptionKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
throw new WrongPassphraseException();
}
if (privateKey == null) {
if (!secretEncryptionKey.unlock(mPassphrase)) {
throw new WrongPassphraseException();
}
} catch(PgpGeneralException e) {
throw new KeyExtractionException();
}
currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
@ -388,10 +366,10 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
PGPOnePassSignature signature = null;
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
PGPPublicKey signatureKey = null;
int signatureIndex = -1;
WrappedPublicKeyRing signingRing = null;
WrappedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@ -403,6 +381,8 @@ public class PgpDecryptVerify {
currentProgress += 10;
}
PGPOnePassSignature signature = null;
if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
@ -410,19 +390,13 @@ public class PgpDecryptVerify {
// go through all signatures
// and find out for which signature we have a key in our database
Long masterKeyId = null;
String primaryUserId = null;
for (int i = 0; i < sigList.size(); ++i) {
try {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(sigList.get(i).getKeyID()));
Map<String, Object> data = mProviderHelper.getGenericData(uri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
ProviderHelper.FIELD_TYPE_STRING }
long sigKeyId = sigList.get(i).getKeyID();
signingRing = mProviderHelper.getWrappedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
primaryUserId = (String) data.get(KeyRings.USER_ID);
signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@ -430,43 +404,24 @@ public class PgpDecryptVerify {
}
}
if (masterKeyId != null) {
if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
PGPPublicKeyRing publicKeyRing = null;
try {
publicKeyRing = mProviderHelper
.getPGPPublicKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
// can't happen
}
// get the subkey which has been used to generate this signature
signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
signatureResultBuilder.userId(primaryUserId);
signatureResultBuilder.keyId(masterKeyId);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
signatureResultBuilder.userId(signingRing.getPrimaryUserId());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
// get certification status of this key
boolean isSignatureKeyCertified;
try {
Object data = mProviderHelper.getGenericData(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
isSignatureKeyCertified = ((Long) data > 0);
} catch (ProviderHelper.NotFoundException e) {
isSignatureKeyCertified = false;
}
signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@ -541,7 +496,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
boolean validKeyBinding = verifyKeyBinding(messageSignature, signatureKey);
boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@ -617,22 +572,19 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
WrappedPublicKeyRing signingRing = null;
WrappedPublicKey signingKey = null;
int signatureIndex = -1;
// go through all signatures
// and find out for which signature we have a key in our database
Long masterKeyId = null;
String primaryUserId = null;
int signatureIndex = 0;
for (int i = 0; i < sigList.size(); ++i) {
try {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(sigList.get(i).getKeyID()));
Map<String, Object> data = mProviderHelper.getGenericData(uri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
ProviderHelper.FIELD_TYPE_STRING }
long sigKeyId = sigList.get(i).getKeyID();
signingRing = mProviderHelper.getWrappedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
primaryUserId = (String) data.get(KeyRings.USER_ID);
signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@ -641,44 +593,25 @@ public class PgpDecryptVerify {
}
PGPSignature signature = null;
PGPPublicKey signatureKey = null;
if (masterKeyId != null) {
if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
PGPPublicKeyRing publicKeyRing = null;
try {
publicKeyRing = mProviderHelper
.getPGPPublicKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
// can't happen
}
// get the subkey which has been used to generate this signature
signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
signatureResultBuilder.userId(primaryUserId);
signatureResultBuilder.keyId(masterKeyId);
signatureResultBuilder.keyId(signingRing.getMasterKeyId());
try {
signatureResultBuilder.userId(signingRing.getPrimaryUserId());
} catch(PgpGeneralException e) {
Log.d(Constants.TAG, "No primary user id in key " + signingRing.getMasterKeyId());
}
signatureResultBuilder.signatureKeyCertified(signingRing.getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
// get certification status of this key
boolean isSignatureKeyCertified;
try {
Object data = mProviderHelper.getGenericData(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
isSignatureKeyCertified = ((Long) data > 0);
} catch (ProviderHelper.NotFoundException e) {
isSignatureKeyCertified = false;
}
signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@ -710,7 +643,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
boolean validKeyBinding = verifyKeyBinding(signature, signatureKey);
boolean validKeyBinding = signingRing.verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@ -722,113 +655,6 @@ public class PgpDecryptVerify {
return result;
}
private boolean verifyKeyBinding(PGPSignature signature, PGPPublicKey signatureKey) {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
PGPPublicKey mKey = null;
try {
PGPPublicKeyRing signKeyRing = mProviderHelper.getPGPPublicKeyRingWithKeyId(
signatureKeyId);
mKey = signKeyRing.getPublicKey();
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found");
}
if (signature.getKeyID() != mKey.getKeyID()) {
validKeyBinding = verifyKeyBinding(mKey, signatureKey);
} else { //if the key used to make the signature was the master key, no need to check binding sigs
validKeyBinding = true;
}
return validKeyBinding;
}
private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
boolean validSubkeyBinding = false;
boolean validTempSubkeyBinding = false;
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
if (sig.getKeyID() == masterPublicKey.getKeyID() &&
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterPublicKey);
validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
if (validTempSubkeyBinding) {
validSubkeyBinding = true;
}
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
}
}
}
return (validSubkeyBinding & validPrimaryKeyBinding);
}
private boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
return false;
}
for (int j = 0; j < eSigList.size(); ++j) {
PGPSignature emSig = eSigList.get(j);
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
}
}
}
return validPrimaryKeyBinding;
}
/**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
*

View File

@ -24,20 +24,14 @@ import android.os.Environment;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.util.Log;
@ -62,7 +56,6 @@ public class PgpImportExport {
private ProviderHelper mProviderHelper;
public static final int RETURN_OK = 0;
public static final int RETURN_ERROR = -1;
public static final int RETURN_BAD = -2;
public static final int RETURN_UPDATED = 1;
@ -100,12 +93,12 @@ public class PgpImportExport {
}
}
public boolean uploadKeyRingToServer(HkpKeyserver server, PGPPublicKeyRing keyring) {
public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
aos = new ArmoredOutputStream(bos);
aos.write(keyring.getEncoded());
keyring.encode(aos);
aos.close();
String armoredKey = bos.toString("UTF-8");
@ -133,7 +126,7 @@ public class PgpImportExport {
/**
* Imports keys from given data. If keyIds is given only those are imported
*/
public Bundle importKeyRings(List<ImportKeysListEntry> entries)
public Bundle importKeyRings(List<ParcelableKeyRing> entries)
throws PgpGeneralException, PGPException, IOException {
Bundle returnData = new Bundle();
@ -144,37 +137,26 @@ public class PgpImportExport {
int badKeys = 0;
int position = 0;
try {
for (ImportKeysListEntry entry : entries) {
Object obj = PgpConversionHelper.BytesToPGPKeyRing(entry.getBytes());
for (ParcelableKeyRing entry : entries) {
try {
UncachedKeyRing key = entry.getUncachedKeyRing();
if (obj instanceof PGPKeyRing) {
PGPKeyRing keyring = (PGPKeyRing) obj;
mProviderHelper.savePublicKeyRing(key);
/*switch(status) {
case RETURN_UPDATED: oldKeys++; break;
case RETURN_OK: newKeys++; break;
case RETURN_BAD: badKeys++; break;
}*/
// TODO proper import feedback
newKeys += 1;
int status = storeKeyRingInCache(keyring);
if (status == RETURN_ERROR) {
throw new PgpGeneralException(
mContext.getString(R.string.error_saving_keys));
}
// update the counts to display to the user at the end
if (status == RETURN_UPDATED) {
++oldKeys;
} else if (status == RETURN_OK) {
++newKeys;
} else if (status == RETURN_BAD) {
++badKeys;
}
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
position++;
updateProgress(position / entries.size() * 100, 100);
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Encountered bad key on import!", e);
++badKeys;
}
} catch (Exception e) {
Log.e(Constants.TAG, "Exception on parsing key file!", e);
// update progress
position++;
updateProgress(position / entries.size() * 100, 100);
}
returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys);
@ -211,9 +193,11 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
PGPPublicKeyRing publicKeyRing = mProviderHelper.getPGPPublicKeyRing(pubKeyMasterId);
WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId)
);
publicKeyRing.encode(arOutStream);
ring.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: inform user?
@ -237,7 +221,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId);
WrappedSecretKeyRing secretKeyRing =
mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
@ -259,53 +244,4 @@ public class PgpImportExport {
return returnData;
}
@SuppressWarnings("unchecked")
public int storeKeyRingInCache(PGPKeyRing keyring) {
int status = RETURN_ERROR;
try {
if (keyring instanceof PGPSecretKeyRing) {
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
boolean save = true;
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
secretKeyRing.getSecretKeys())) {
if (!testSecretKey.isMasterKey()) {
if (testSecretKey.isPrivateKeyEmpty()) {
// this is bad, something is very wrong...
save = false;
status = RETURN_BAD;
}
}
}
if (save) {
// TODO: preserve certifications
// (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
PGPPublicKeyRing newPubRing = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(
secretKeyRing.getPublicKeys())) {
if (newPubRing == null) {
newPubRing = new PGPPublicKeyRing(key.getEncoded(),
new JcaKeyFingerprintCalculator());
}
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
}
if (newPubRing != null) {
mProviderHelper.saveKeyRing(newPubRing);
}
mProviderHelper.saveKeyRing(secretKeyRing);
status = RETURN_OK;
}
} else if (keyring instanceof PGPPublicKeyRing) {
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
mProviderHelper.saveKeyRing(publicKeyRing);
status = RETURN_OK;
}
} catch (IOException e) {
status = RETURN_ERROR;
}
return status;
}
}

View File

@ -52,14 +52,12 @@ public class PgpKeyHelper {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
@Deprecated
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
public static Date getCreationDate(PGPSecretKey key) {
return key.getPublicKey().getCreationTime();
}
@Deprecated
public static Date getExpiryDate(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
if (key.getValidDays() == 0) {
@ -73,185 +71,6 @@ public class PgpKeyHelper {
return calendar.getTime();
}
public static Date getExpiryDate(PGPSecretKey key) {
return getExpiryDate(key.getPublicKey());
}
public static boolean isExpired(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
Date expiryDate = getExpiryDate(key);
Date now = new Date();
if (now.compareTo(creationDate) >= 0
&& (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
return false;
}
return true;
}
@SuppressWarnings("unchecked")
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
long cnt = 0;
if (keyRing == null) {
return null;
}
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (cnt == num) {
return key;
}
cnt++;
}
return null;
}
@SuppressWarnings("unchecked")
private static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (isEncryptionKey(key)) {
encryptKeys.add(key);
}
}
return encryptKeys;
}
@SuppressWarnings("unchecked")
private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (isSigningKey(key)) {
signingKeys.add(key);
}
}
return signingKeys;
}
@SuppressWarnings("unchecked")
private static Vector<PGPSecretKey> getCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (isCertificationKey(key)) {
signingKeys.add(key);
}
}
return signingKeys;
}
private static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
PGPPublicKey masterKey = null;
for (int i = 0; i < encryptKeys.size(); ++i) {
PGPPublicKey key = encryptKeys.get(i);
if (!isExpired(key) && !key.isRevoked()) {
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
private static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
PGPSecretKey masterKey = null;
for (int i = 0; i < signingKeys.size(); ++i) {
PGPSecretKey key = signingKeys.get(i);
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
PGPSecretKey masterKey = null;
for (int i = 0; i < signingKeys.size(); ++i) {
PGPSecretKey key = signingKeys.get(i);
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
public static PGPPublicKey getFirstEncryptSubkey(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
Log.e(Constants.TAG, "encryptKeys is null!");
return null;
}
return encryptKeys.get(0);
}
public static PGPSecretKey getFirstCertificationSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableCertificationKeys(keyRing);
if (signingKeys.size() == 0) {
return null;
}
return signingKeys.get(0);
}
public static PGPSecretKey getFirstSigningSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
if (signingKeys.size() == 0) {
return null;
}
return signingKeys.get(0);
}
public static int getKeyUsage(PGPSecretKey key) {
return getKeyUsage(key.getPublicKey());
}
@SuppressWarnings("unchecked")
private static int getKeyUsage(PGPPublicKey key) {
int usage = 0;
if (key.getVersion() >= 4) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null) {
usage |= hashed.getKeyFlags();
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null) {
usage |= unhashed.getKeyFlags();
}
}
}
return usage;
}
@SuppressWarnings("unchecked")
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
@ -293,10 +112,6 @@ public class PgpKeyHelper {
return false;
}
public static boolean isEncryptionKey(PGPSecretKey key) {
return isEncryptionKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
@ -328,10 +143,6 @@ public class PgpKeyHelper {
return false;
}
public static boolean isSigningKey(PGPSecretKey key) {
return isSigningKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
public static boolean isCertificationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
@ -358,48 +169,6 @@ public class PgpKeyHelper {
return false;
}
public static boolean isAuthenticationKey(PGPSecretKey key) {
return isAuthenticationKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
public static boolean isAuthenticationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
}
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
return true;
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
return true;
}
}
return false;
}
public static boolean isCertificationKey(PGPSecretKey key) {
return isCertificationKey(key.getPublicKey());
}
public static String getAlgorithmInfo(Context context, PGPPublicKey key) {
return getAlgorithmInfo(context, key.getAlgorithm(), key.getBitStrength());
}
public static String getAlgorithmInfo(Context context, PGPSecretKey key) {
return getAlgorithmInfo(context, key.getPublicKey());
}
/**
* TODO: Only used in HkpKeyServer. Get rid of this one!
*/

View File

@ -48,8 +48,8 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes;
@ -63,6 +63,7 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
@ -124,7 +125,7 @@ public class PgpKeyOperation {
*/
// TODO: key flags?
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
public byte[] createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
@ -188,43 +189,23 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor);
try {
return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, isMasterKey, keyEncryptor).getEncoded();
} catch(IOException e) {
throw new PgpGeneralMsgIdException(R.string.error_encoding);
}
}
public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase,
String newPassphrase)
throws IOException, PGPException, NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
keyRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return newKeyRing;
}
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
SaveKeyringParcel saveParcel)
public Pair<UncachedKeyRing,UncachedKeyRing> buildNewSecretKey(
OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = saveParcel.keysUsages.get(0);
boolean canSign;
String mainUserId = saveParcel.userIds.get(0);
PGPSecretKey masterKey = saveParcel.keys.get(0);
PGPSecretKey masterKey = saveParcel.keys.get(0).getSecretKeyExternal();
// this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
@ -299,7 +280,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 40 * (i - 1) / (saveParcel.keys.size() - 1), 100);
PGPSecretKey subKey = saveParcel.keys.get(i);
PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
@ -357,17 +338,19 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
return new Pair(new UncachedKeyRing(secretKeyRing), new UncachedKeyRing(publicKeyRing));
}
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR,
PGPPublicKeyRing pKR,
SaveKeyringParcel saveParcel)
public Pair<UncachedKeyRing, UncachedKeyRing> buildSecretKey(WrappedSecretKeyRing wmKR,
WrappedPublicKeyRing wpKR,
OldSaveKeyringParcel saveParcel)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
PGPSecretKeyRing mKR = wmKR.getRing();
PGPPublicKeyRing pKR = wpKR.getRing();
updateProgress(R.string.progress_building_key, 0, 100);
PGPSecretKey masterKey = saveParcel.keys.get(0);
if (saveParcel.oldPassphrase == null) {
saveParcel.oldPassphrase = "";
@ -399,12 +382,12 @@ public class PgpKeyOperation {
*/
if (saveParcel.deletedKeys != null) {
for (PGPSecretKey dKey : saveParcel.deletedKeys) {
mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey);
for (UncachedSecretKey dKey : saveParcel.deletedKeys) {
mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey.getSecretKeyExternal());
}
}
masterKey = mKR.getSecretKey();
PGPSecretKey masterKey = mKR.getSecretKey();
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
int usageId = saveParcel.keysUsages.get(0);
@ -564,7 +547,7 @@ public class PgpKeyOperation {
for (int i = 1; i < saveParcel.keys.size(); ++i) {
updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
if (saveParcel.moddedKeys[i]) {
PGPSecretKey subKey = saveParcel.keys.get(i);
PGPSecretKey subKey = saveParcel.keys.get(i).getSecretKeyExternal();
PGPPublicKey subPublicKey = subKey.getPublicKey();
PBESecretKeyDecryptor keyDecryptor2;
@ -667,7 +650,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@ -678,7 +661,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>(
publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
@ -686,10 +669,287 @@ public class PgpKeyOperation {
*/
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR);
return new Pair<UncachedKeyRing,UncachedKeyRing>(new UncachedKeyRing(pKR),
new UncachedKeyRing(mKR));
}
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
PGPPublicKeyRing pKR,
SaveKeyringParcel saveParcel,
String passphrase)
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
updateProgress(R.string.progress_building_key, 0, 100);
// sort these, so we can use binarySearch later on
Arrays.sort(saveParcel.revokeSubKeys);
Arrays.sort(saveParcel.revokeUserIds);
/*
* What's gonna happen here:
*
* 1. Unlock private key
*
* 2. Create new secret key ring
*
* 3. Copy subkeys
* - Generate revocation if requested
* - Copy old cert, or generate new if change requested
*
* 4. Generate and add new subkeys
*
* 5. Copy user ids
* - Generate revocation if requested
* - Copy old cert, or generate new if primary user id status changed
*
* 6. Add new user ids
*
* 7. Generate PublicKeyRing from SecretKeyRing
*
* 8. Return pair (PublicKeyRing,SecretKeyRing)
*
*/
// 1. Unlock private key
updateProgress(R.string.progress_building_key, 0, 100);
PGPPublicKey masterPublicKey = sKR.getPublicKey();
PGPPrivateKey masterPrivateKey; {
PGPSecretKey masterKey = sKR.getSecretKey();
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
}
// 2. Create new secret key ring
updateProgress(R.string.progress_certifying_master_key, 20, 100);
// Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff
// we want to do manually. Instead, we simply use a list of secret keys.
ArrayList<PGPSecretKey> secretKeys = new ArrayList<PGPSecretKey>();
ArrayList<PGPPublicKey> publicKeys = new ArrayList<PGPPublicKey>();
// 3. Copy subkeys
// - Generate revocation if requested
// - Copy old cert, or generate new if change requested
for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
PGPPublicKey pKey = sKey.getPublicKey();
if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) {
// add revocation signature to key, if there is none yet
if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) {
// generate revocation signature
}
}
if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) {
// change subkey flags?
SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID());
// remove old subkey binding signature(s?)
for (PGPSignature sig : new IterableIterator<PGPSignature>(
pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) {
pKey = PGPPublicKey.removeCertification(pKey, sig);
}
// generate and add new signature
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
sKey, pKey, change.mFlags, change.mExpiry, passphrase);
pKey = PGPPublicKey.addCertification(pKey, sig);
}
secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey));
publicKeys.add(pKey);
}
// 4. Generate and add new subkeys
// TODO
// 5. Copy user ids
for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
// - Copy old cert, or generate new if primary user id status changed
boolean certified = false, revoked = false;
for (PGPSignature sig : new IterableIterator<PGPSignature>(
masterPublicKey.getSignaturesForID(userId))) {
// We know there are only revocation and certification types in here.
switch(sig.getSignatureType()) {
case PGPSignature.CERTIFICATION_REVOCATION:
revoked = true;
continue;
case PGPSignature.DEFAULT_CERTIFICATION:
case PGPSignature.NO_CERTIFICATION:
case PGPSignature.CASUAL_CERTIFICATION:
case PGPSignature.POSITIVE_CERTIFICATION:
// Already got one? Remove this one, then.
if (certified) {
masterPublicKey = PGPPublicKey.removeCertification(
masterPublicKey, userId, sig);
continue;
}
boolean primary = userId.equals(saveParcel.changePrimaryUserId);
// Generate a new one under certain circumstances
if (saveParcel.changePrimaryUserId != null &&
sig.getHashedSubPackets().isPrimaryUserID() != primary) {
PGPSignature cert = generateUserIdSignature(
masterPrivateKey, masterPublicKey, userId, primary);
PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
certified = true;
}
}
// - Generate revocation if requested
if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) {
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
}
// 6. Add new user ids
for(String userId : saveParcel.addUserIds) {
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId));
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
}
// 7. Generate PublicKeyRing from SecretKeyRing
updateProgress(R.string.progress_building_master_key, 30, 100);
PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys);
// Copy all non-self uid certificates
for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
// - Copy old cert, or generate new if primary user id status changed
boolean certified = false, revoked = false;
for (PGPSignature sig : new IterableIterator<PGPSignature>(
masterPublicKey.getSignaturesForID(userId))) {
}
}
for (PGPPublicKey newKey : publicKeys) {
PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
for (PGPSignature sig : new IterableIterator<PGPSignature>(
oldKey.getSignatures())) {
}
}
// If requested, set new passphrase
if (saveParcel.newPassphrase != null) {
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
// Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
saveParcel.newPassphrase.toCharArray());
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
}
// 8. Return pair (PublicKeyRing,SecretKeyRing)
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
}
private static PGPSignature generateUserIdSignature(
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
subHashedPacketsGen.setPrimaryUserID(false, primary);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
}
private static PGPSignature generateRevocationSignature(
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
}
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
PGPSecretKey sKey, PGPPublicKey pKey,
int flags, Long expiry, String passphrase)
throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
// date for signing
Date todayDate = new Date();
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
// If this key can sign, we need a primary key binding signature
if ((flags & KeyFlags.SIGN_DATA) != 0) {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
passphrase.toCharArray());
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
// cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
unhashedPacketsGen.setEmbeddedSignature(false, certification);
}
PGPSignatureSubpacketGenerator hashedPacketsGen;
{
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
hashedPacketsGen.setSignatureCreationTime(false, todayDate);
hashedPacketsGen.setKeyFlags(false, flags);
}
if (expiry != null) {
Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
creationDate.setTime(pKey.getCreationTime());
// note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
// here we purposefully ignore partial days in each date - long type has
// no fractional part!
long numDays = (expiry / 86400000) -
(creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0) {
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
}
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0);
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
pKey.getAlgorithm(), PGPUtil.SHA1)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
sGen.setHashedSubpackets(hashedPacketsGen.generate());
sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
return sGen.generateCertification(masterPublicKey, pKey);
}
/**
* Certify the given pubkeyid with the given masterkeyid.
*

View File

@ -25,25 +25,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@ -277,20 +266,17 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
PGPSecretKey signingKey = null;
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
String signingUserId = null;
WrappedSecretKey signingKey = null;
if (enableSignature) {
WrappedSecretKeyRing signingKeyRing;
try {
signingKeyRing = mProviderHelper.getPGPSecretKeyRing(mSignatureMasterKeyId);
signingUserId = (String) mProviderHelper.getUnifiedData(mSignatureMasterKeyId,
KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
signingKey = PgpKeyHelper.getFirstSigningSubkey(signingKeyRing);
if (signingKey == null) {
try {
signingKey = signingKeyRing.getSigningSubKey();
} catch(PgpGeneralException e) {
throw new NoSigningKeyException();
}
@ -300,10 +286,9 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
try {
signingKey.unlock(mSignaturePassphrase);
} catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
}
@ -331,13 +316,12 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
PGPPublicKeyRing keyRing = mProviderHelper.getPGPPublicKeyRing(id);
PGPPublicKey key = PgpKeyHelper.getFirstEncryptSubkey(keyRing);
if (key != null) {
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
new JcePublicKeyKeyEncryptionMethodGenerator(key);
cPk.addMethod(pubKeyEncryptionGenerator);
}
WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
WrappedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
@ -351,29 +335,18 @@ public class PgpSignEncrypt {
if (enableSignature) {
updateProgress(R.string.progress_preparing_signature, 10, 100);
// content signer based on signing key algorithm and chosen hash algorithm
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
int signatureType;
if (mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
}
if (mSignatureForceV3) {
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(signatureType, signaturePrivateKey);
} else {
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, signingUserId);
signatureGenerator.setHashedSubpackets(spGen.generate());
try {
boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
if (mSignatureForceV3) {
signatureV3Generator = signingKey.getV3SignatureGenerator(
mSignatureHashAlgorithm,cleartext);
} else {
signatureGenerator = signingKey.getSignatureGenerator(
mSignatureHashAlgorithm, cleartext);
}
} catch (PgpGeneralException e) {
// TODO throw correct type of exception (which shouldn't be PGPException)
throw new KeyExtractionException();
}
}

View File

@ -0,0 +1,171 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/** Wrapper around PGPKeyRing class, to be constructed from bytes.
*
* This class and its relatives UncachedPublicKey and UncachedSecretKey are
* used to move around pgp key rings in non crypto related (UI, mostly) code.
* It should be used for simple inspection only until it saved in the database,
* all actual crypto operations should work with WrappedKeyRings exclusively.
*
* This class is also special in that it can hold either the PGPPublicKeyRing
* or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
* treated equally for most purposes in UI code. It is up to the programmer to
* take care of the differences.
*
* @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
* @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
* @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
*
*/
public class UncachedKeyRing {
final PGPKeyRing mRing;
final boolean mIsSecret;
UncachedKeyRing(PGPKeyRing ring) {
mRing = ring;
mIsSecret = ring instanceof PGPSecretKeyRing;
}
public long getMasterKeyId() {
return mRing.getPublicKey().getKeyID();
}
/* TODO don't use this */
@Deprecated
public PGPKeyRing getRing() {
return mRing;
}
public UncachedPublicKey getPublicKey() {
return new UncachedPublicKey(mRing.getPublicKey());
}
public Iterator<UncachedPublicKey> getPublicKeys() {
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
return new Iterator<UncachedPublicKey>() {
public void remove() {
it.remove();
}
public UncachedPublicKey next() {
return new UncachedPublicKey(it.next());
}
public boolean hasNext() {
return it.hasNext();
}
};
}
/** Returns the dynamic (though final) property if this is a secret keyring or not. */
public boolean isSecret() {
return mIsSecret;
}
public byte[] getEncoded() throws IOException {
return mRing.getEncoded();
}
public byte[] getFingerprint() {
return mRing.getPublicKey().getFingerprint();
}
public static UncachedKeyRing decodePublicFromData(byte[] data)
throws PgpGeneralException, IOException {
UncachedKeyRing ring = decodeFromData(data);
if(ring.isSecret()) {
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
}
return ring;
}
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(data));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
return new UncachedKeyRing((PGPKeyRing) obj);
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
public static List<UncachedKeyRing> fromStream(InputStream stream)
throws PgpGeneralException, IOException {
PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
// go through all objects in this block
Object obj;
while ((obj = objectFactory.nextObject()) != null) {
Log.d(Constants.TAG, "Found class: " + obj.getClass());
if (obj instanceof PGPKeyRing) {
result.add(new UncachedKeyRing((PGPKeyRing) obj));
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
}
return result;
}
public void encodeArmored(OutputStream out, String version) throws IOException {
ArmoredOutputStream aos = new ArmoredOutputStream(out);
aos.setHeader("Version", version);
aos.write(mRing.getEncoded());
aos.close();
}
public ArrayList<Long> getAvailableSubkeys() {
if(!isSecret()) {
throw new RuntimeException("Tried to find available subkeys from non-secret keys. " +
"This is a programming error and should never happen!");
}
ArrayList<Long> result = new ArrayList<Long>();
// then, mark exactly the keys we have available
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) {
result.add(sub.getKeyID());
}
}
return result;
}
}

View File

@ -0,0 +1,197 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
public class UncachedPublicKey {
protected final PGPPublicKey mPublicKey;
private Integer mCacheUsage = null;
public UncachedPublicKey(PGPPublicKey key) {
mPublicKey = key;
}
public long getKeyId() {
return mPublicKey.getKeyID();
}
/** The revocation signature is NOT checked here, so this may be false! */
public boolean maybeRevoked() {
return mPublicKey.isRevoked();
}
public Date getCreationTime() {
return mPublicKey.getCreationTime();
}
public Date getExpiryTime() {
Date creationDate = getCreationTime();
if (mPublicKey.getValidDays() == 0) {
// no expiry
return null;
}
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
calendar.add(Calendar.DATE, mPublicKey.getValidDays());
return calendar.getTime();
}
public boolean isExpired() {
Date creationDate = mPublicKey.getCreationTime();
Date expiryDate = mPublicKey.getValidSeconds() > 0
? new Date(creationDate.getTime() + mPublicKey.getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
public boolean isMasterKey() {
return mPublicKey.isMasterKey();
}
public int getAlgorithm() {
return mPublicKey.getAlgorithm();
}
public int getBitStrength() {
return mPublicKey.getBitStrength();
}
public String getPrimaryUserId() {
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignaturesForID(userId))) {
if (sig.getHashedSubPackets() != null
&& sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.PRIMARY_USER_ID)) {
try {
// make sure it's actually valid
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), mPublicKey);
if (sig.verifyCertification(userId, mPublicKey)) {
return userId;
}
} catch (Exception e) {
// nothing bad happens, the key is just not considered the primary key id
}
}
}
}
return null;
}
public ArrayList<String> getUnorderedUserIds() {
ArrayList<String> userIds = new ArrayList<String>();
for (String userId : new IterableIterator<String>(mPublicKey.getUserIDs())) {
userIds.add(userId);
}
return userIds;
}
public boolean isElGamalEncrypt() {
return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT;
}
public boolean isDSA() {
return getAlgorithm() == PGPPublicKey.DSA;
}
@SuppressWarnings("unchecked")
public int getKeyUsage() {
if(mCacheUsage == null) {
mCacheUsage = 0;
if (mPublicKey.getVersion() >= 4) {
for (PGPSignature sig : new IterableIterator<PGPSignature>(mPublicKey.getSignatures())) {
if (mPublicKey.isMasterKey() && sig.getKeyID() != mPublicKey.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null) {
mCacheUsage |= hashed.getKeyFlags();
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null) {
mCacheUsage |= unhashed.getKeyFlags();
}
}
}
}
return mCacheUsage;
}
public boolean canAuthenticate() {
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.AUTHENTICATION) != 0;
}
public boolean canCertify() {
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.CERTIFY_OTHER) != 0;
}
public boolean canEncrypt() {
if (!mPublicKey.isEncryptionKey()) {
return false;
}
// special cases
if (mPublicKey.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
return true;
}
return mPublicKey.getVersion() <= 3 ||
(getKeyUsage() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0;
}
public boolean canSign() {
// special case
if (mPublicKey.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
return true;
}
return mPublicKey.getVersion() <= 3 || (getKeyUsage() & KeyFlags.SIGN_DATA) != 0;
}
public byte[] getFingerprint() {
return mPublicKey.getFingerprint();
}
// TODO This method should have package visibility - no access outside the pgp package!
// (It's still used in ProviderHelper at this point)
public PGPPublicKey getPublicKey() {
return mPublicKey;
}
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
return new Iterator<WrappedSignature>() {
public void remove() {
it.remove();
}
public WrappedSignature next() {
return new WrappedSignature(it.next());
}
public boolean hasNext() {
return it.hasNext();
}
};
}
}

View File

@ -0,0 +1,33 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPSecretKey;
import java.io.IOException;
import java.io.OutputStream;
public class UncachedSecretKey extends UncachedPublicKey {
public static final int CERTIFY_OTHER = KeyFlags.CERTIFY_OTHER;
public static final int SIGN_DATA = KeyFlags.SIGN_DATA;
public static final int ENCRYPT_COMMS = KeyFlags.ENCRYPT_COMMS;
public static final int ENCRYPT_STORAGE = KeyFlags.ENCRYPT_STORAGE;
public static final int AUTHENTICATION = KeyFlags.AUTHENTICATION;
final PGPSecretKey mSecretKey;
public UncachedSecretKey(PGPSecretKey secretKey) {
super(secretKey.getPublicKey());
mSecretKey = secretKey;
}
@Deprecated
public PGPSecretKey getSecretKeyExternal() {
return mSecretKey;
}
public void encodeSecretKey(OutputStream os) throws IOException {
mSecretKey.encode(os);
}
}

View File

@ -0,0 +1,97 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException;
import java.io.OutputStream;
/** A generic wrapped PGPKeyRing object.
*
* This class provides implementations for all basic getters which both
* PublicKeyRing and SecretKeyRing have in common. To make the wrapped keyring
* class typesafe in implementing subclasses, the field is stored in the
* implementing class, providing properly typed access through the getRing
* getter method.
*
*/
public abstract class WrappedKeyRing extends KeyRing {
private final boolean mHasAnySecret;
private final int mVerified;
WrappedKeyRing(boolean hasAnySecret, int verified) {
mHasAnySecret = hasAnySecret;
mVerified = verified;
}
public long getMasterKeyId() {
return getRing().getPublicKey().getKeyID();
}
public boolean hasAnySecret() {
return mHasAnySecret;
}
public int getVerified() {
return mVerified;
}
public String getPrimaryUserId() throws PgpGeneralException {
return (String) getRing().getPublicKey().getUserIDs().next();
};
public boolean isRevoked() throws PgpGeneralException {
// Is the master key revoked?
return getRing().getPublicKey().isRevoked();
}
public boolean canCertify() throws PgpGeneralException {
return getRing().getPublicKey().isEncryptionKey();
}
public long getEncryptId() throws PgpGeneralException {
for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
if(PgpKeyHelper.isEncryptionKey(key)) {
return key.getKeyID();
}
}
throw new PgpGeneralException("No valid encryption key found!");
}
public boolean hasEncrypt() throws PgpGeneralException {
try {
getEncryptId();
return true;
} catch(PgpGeneralException e) {
return false;
}
}
public long getSignId() throws PgpGeneralException {
for(PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
if(PgpKeyHelper.isSigningKey(key)) {
return key.getKeyID();
}
}
throw new PgpGeneralException("No valid signing key found!");
}
public boolean hasSign() throws PgpGeneralException {
try {
getSignId();
return true;
} catch (PgpGeneralException e) {
return false;
}
}
public void encode(OutputStream stream) throws IOException {
getRing().encode(stream);
}
abstract PGPKeyRing getRing();
}

View File

@ -0,0 +1,39 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.util.IterableIterator;
/** Wrapper for a PGPPublicKey.
*
* The methods implemented in this class are a thin layer over
* UncachedPublicKey. The difference between the two classes is that objects of
* this class can only be obtained from a WrappedKeyRing, and that it stores a
* back reference to its parent as well. A method which works with
* WrappedPublicKey is therefore guaranteed to work on a KeyRing which is
* stored in the database.
*
*/
public class WrappedPublicKey extends UncachedPublicKey {
// this is the parent key ring
final KeyRing mRing;
WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
super(key);
mRing = ring;
}
public IterableIterator<String> getUserIds() {
return new IterableIterator<String>(mPublicKey.getUserIDs());
}
public KeyRing getKeyRing() {
return mRing;
}
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
}
}

View File

@ -0,0 +1,192 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Iterator;
public class WrappedPublicKeyRing extends WrappedKeyRing {
private PGPPublicKeyRing mRing;
private final byte[] mPubKey;
public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
super(hasAnySecret, verified);
mPubKey = blob;
}
PGPPublicKeyRing getRing() {
if(mRing == null) {
PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
PGPKeyRing keyRing = null;
try {
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
Log.e(Constants.TAG, "No keys given!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
}
mRing = (PGPPublicKeyRing) keyRing;
}
return mRing;
}
public void encode(ArmoredOutputStream stream) throws IOException {
getRing().encode(stream);
}
public WrappedPublicKey getSubkey() {
return new WrappedPublicKey(this, getRing().getPublicKey());
}
public WrappedPublicKey getSubkey(long id) {
return new WrappedPublicKey(this, getRing().getPublicKey(id));
}
/** Getter that returns the subkey that should be used for signing. */
WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
if(key != null) {
WrappedPublicKey cKey = new WrappedPublicKey(this, key);
if(!cKey.canEncrypt()) {
throw new PgpGeneralException("key error");
}
return cKey;
}
// TODO handle with proper exception
throw new PgpGeneralException("no encryption key available");
}
public boolean verifySubkeyBinding(WrappedPublicKey cachedSubkey) {
boolean validSubkeyBinding = false;
boolean validTempSubkeyBinding = false;
boolean validPrimaryKeyBinding = false;
PGPPublicKey masterKey = getRing().getPublicKey();
PGPPublicKey subKey = cachedSubkey.getPublicKey();
// Is this the master key? Match automatically, then.
if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {
return true;
}
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = subKey.getSignatures();
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
if (sig.getKeyID() == masterKey.getKeyID() &&
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterKey);
validTempSubkeyBinding = sig.verifyCertification(masterKey, subKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
if (validTempSubkeyBinding) {
validSubkeyBinding = true;
}
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterKey, subKey);
if (validPrimaryKeyBinding) {
break;
}
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterKey, subKey);
if (validPrimaryKeyBinding) {
break;
}
}
}
}
return validSubkeyBinding && validPrimaryKeyBinding;
}
static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
return false;
}
for (int j = 0; j < eSigList.size(); ++j) {
PGPSignature emSig = eSigList.get(j);
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
}
}
}
return validPrimaryKeyBinding;
}
public IterableIterator<WrappedPublicKey> iterator() {
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public WrappedPublicKey next() {
return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
}
@Override
public void remove() {
it.remove();
}
});
}
}

View File

@ -0,0 +1,200 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
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.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
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 java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.List;
/** Wrapper for a PGPSecretKey.
*
* This object can only be obtained from a WrappedSecretKeyRing, and stores a
* back reference to its parent.
*
* This class represents known secret keys which are stored in the database.
* All "crypto operations using a known secret key" should be implemented in
* this class, to ensure on type level that these operations are performed on
* properly imported secret keys only.
*
*/
public class WrappedSecretKey extends WrappedPublicKey {
private final PGPSecretKey mSecretKey;
private PGPPrivateKey mPrivateKey = null;
WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
super(ring, key.getPublicKey());
mSecretKey = key;
}
public WrappedSecretKeyRing getRing() {
return (WrappedSecretKeyRing) mRing;
}
/** Returns the wrapped PGPSecretKeyRing.
* This function is for compatibility only, should not be used anymore and will be removed
*/
@Deprecated
public PGPSecretKey getKeyExternal() {
return mSecretKey;
}
public boolean unlock(String passphrase) throws PgpGeneralException {
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
return false;
}
if(mPrivateKey == null) {
throw new PgpGeneralException("error extracting key");
}
return true;
}
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext)
throws PgpGeneralException {
if(mPrivateKey == null) {
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);
int signatureType;
if (cleartext) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
}
try {
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, mPrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, mRing.getPrimaryUserId());
signatureGenerator.setHashedSubpackets(spGen.generate());
return signatureGenerator;
} catch(PGPException e) {
throw new PgpGeneralException("Error initializing signature!", e);
}
}
public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext)
throws PgpGeneralException {
if(mPrivateKey == null) {
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);
int signatureType;
if (cleartext) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
}
try {
PGPV3SignatureGenerator signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(signatureType, mPrivateKey);
return signatureV3Generator;
} catch(PGPException e) {
throw new PgpGeneralException("Error initializing signature!", e);
}
}
public PublicKeyDataDecryptorFactory getDecryptorFactory() {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
return new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
}
/**
* Certify the given pubkeyid with the given masterkeyid.
*
* @param publicKeyRing Keyring to add certification to.
* @param userIds User IDs to certify, must not be null or empty
* @return A keyring with added certifications
*/
public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
// create a signatureGenerator from the supplied masterKeyId and passphrase
PGPSignatureGenerator signatureGenerator;
{
// TODO: SHA256 fixed?
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
mSecretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
}
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
}
// get the master subkey (which we certify for)
PGPPublicKey publicKey = publicKeyRing.getSubkey().getPublicKey();
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
}
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
return new UncachedKeyRing(ring);
}
static class PrivateKeyNotUnlockedException extends RuntimeException {
// this exception is a programming error which happens when an operation which requires
// the private key is called without a previous call to unlock()
}
public UncachedSecretKey getUncached() {
return new UncachedSecretKey(mSecretKey);
}
}

View File

@ -0,0 +1,141 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.NoSuchProviderException;
import java.util.Iterator;
public class WrappedSecretKeyRing extends WrappedKeyRing {
private PGPSecretKeyRing mRing;
public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
{
super(isRevoked, verified);
PGPObjectFactory factory = new PGPObjectFactory(blob);
PGPKeyRing keyRing = null;
try {
if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
Log.e(Constants.TAG, "No keys given!");
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
}
mRing = (PGPSecretKeyRing) keyRing;
}
PGPSecretKeyRing getRing() {
return mRing;
}
public WrappedSecretKey getSubKey() {
return new WrappedSecretKey(this, mRing.getSecretKey());
}
public WrappedSecretKey getSubKey(long id) {
return new WrappedSecretKey(this, mRing.getSecretKey(id));
}
/** Getter that returns the subkey that should be used for signing. */
WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
PGPSecretKey key = mRing.getSecretKey(getSignId());
if(key != null) {
WrappedSecretKey cKey = new WrappedSecretKey(this, key);
if(!cKey.canSign()) {
throw new PgpGeneralException("key error");
}
return cKey;
}
// TODO handle with proper exception
throw new PgpGeneralException("no signing key available");
}
public boolean hasPassphrase() {
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
for (Iterator keys = mRing.getSecretKeys(); keys.hasNext(); ) {
secretKey = (PGPSecretKey) keys.next();
if (!secretKey.isPrivateKeyEmpty()) {
foundValidKey = true;
break;
}
}
if(!foundValidKey) {
return false;
}
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider("SC").build("".toCharArray());
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
return testKey == null;
} catch(PGPException e) {
// this means the crc check failed -> passphrase required
return true;
}
}
public UncachedKeyRing changeSecretKeyPassphrase(String oldPassphrase,
String newPassphrase)
throws IOException, PGPException, NoSuchProviderException {
if (oldPassphrase == null) {
oldPassphrase = "";
}
if (newPassphrase == null) {
newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
mRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(mRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
return new UncachedKeyRing(newKeyRing);
}
public IterableIterator<WrappedSecretKey> iterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public WrappedSecretKey next() {
return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
}
@Override
public void remove() {
it.remove();
}
});
}
public UncachedKeyRing getUncached() {
return new UncachedKeyRing(mRing);
}
}

View File

@ -0,0 +1,161 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Date;
/** OpenKeychain wrapper around PGPSignature objects.
*
* This is a mostly simple wrapper around a single bouncycastle PGPSignature
* object. It exposes high level getters for all relevant information, methods
* for verification of various signatures (uid binding, subkey binding, generic
* bytes), and a static method for construction from bytes.
*
*/
public class WrappedSignature {
public static final int DEFAULT_CERTIFICATION = PGPSignature.DEFAULT_CERTIFICATION;
public static final int NO_CERTIFICATION = PGPSignature.NO_CERTIFICATION;
public static final int CASUAL_CERTIFICATION = PGPSignature.CASUAL_CERTIFICATION;
public static final int POSITIVE_CERTIFICATION = PGPSignature.POSITIVE_CERTIFICATION;
public static final int CERTIFICATION_REVOCATION = PGPSignature.CERTIFICATION_REVOCATION;
final PGPSignature mSig;
protected WrappedSignature(PGPSignature sig) {
mSig = sig;
}
public long getKeyId() {
return mSig.getKeyID();
}
public int getSignatureType() {
return mSig.getSignatureType();
}
public int getKeyAlgorithm() {
return mSig.getKeyAlgorithm();
}
public Date getCreationTime() {
return mSig.getCreationTime();
}
public byte[] getEncoded() throws IOException {
return mSig.getEncoded();
}
public boolean isRevocation() {
return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
}
public boolean isPrimaryUserId() {
return mSig.getHashedSubPackets().isPrimaryUserID();
}
public String getRevocationReason() throws PgpGeneralException {
if(!isRevocation()) {
throw new PgpGeneralException("Not a revocation signature.");
}
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
SignatureSubpacketTags.REVOCATION_REASON);
// For some reason, this is missing in SignatureSubpacketInputStream:146
if (!(p instanceof RevocationReason)) {
p = new RevocationReason(false, p.getData());
}
return ((RevocationReason) p).getRevocationDescription();
}
public void init(WrappedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
public void init(UncachedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
protected void init(PGPPublicKey key) throws PgpGeneralException {
try {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
mSig.init(contentVerifierBuilderProvider, key);
} catch(PGPException e) {
throw new PgpGeneralException(e);
}
}
public void update(byte[] data, int offset, int length) throws PgpGeneralException {
try {
mSig.update(data, offset, length);
} catch(SignatureException e) {
throw new PgpGeneralException(e);
}
}
public void update(byte data) throws PgpGeneralException {
try {
mSig.update(data);
} catch(SignatureException e) {
throw new PgpGeneralException(e);
}
}
public boolean verify() throws PgpGeneralException {
try {
return mSig.verify();
} catch(SignatureException e) {
throw new PgpGeneralException(e);
} catch(PGPException e) {
throw new PgpGeneralException(e);
}
}
protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
try {
return mSig.verifyCertification(uid, key);
} catch (SignatureException e) {
throw new PgpGeneralException("Error!", e);
} catch (PGPException e) {
throw new PgpGeneralException("Error!", e);
}
}
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
public static WrappedSignature fromBytes(byte[] data) {
PGPObjectFactory factory = new PGPObjectFactory(data);
PGPSignatureList signatures = null;
try {
if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
Log.e(Constants.TAG, "No signatures given!");
return null;
}
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
return null;
}
return new WrappedSignature(signatures.get(0));
}
}

View File

@ -27,4 +27,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message, Throwable cause) {
super(message, cause);
}
public PgpGeneralException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,161 @@
package org.sufficientlysecure.keychain.provider;
import android.net.Uri;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.Log;
/** This implementation of KeyRing provides a cached view of PublicKeyRing
* objects based on database queries exclusively.
*
* This class should be used where only few points of data but no actual
* cryptographic operations are required about a PublicKeyRing which is already
* in the database. This happens commonly in UI code, where parsing of a PGP
* key for examination would be a very expensive operation.
*
* Each getter method is implemented using a more or less expensive database
* query, while object construction is (almost) free. A common pattern is
* mProviderHelper.getCachedKeyRing(uri).getterMethod()
*
* TODO Ensure that the values returned here always match the ones returned by
* the parsed KeyRing!
*
*/
public class CachedPublicKeyRing extends KeyRing {
final ProviderHelper mProviderHelper;
final Uri mUri;
public CachedPublicKeyRing(ProviderHelper providerHelper, Uri uri) {
mProviderHelper = providerHelper;
mUri = uri;
}
public long getMasterKeyId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID, ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data;
} catch (ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
/**
* Find the master key id related to a given query. The id will either be extracted from the
* query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
*/
public long extractOrGetMasterKeyId() throws PgpGeneralException {
// try extracting from the uri first
String firstSegment = mUri.getPathSegments().get(1);
if (!firstSegment.equals("find")) try {
return Long.parseLong(firstSegment);
} catch (NumberFormatException e) {
// didn't work? oh well.
Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
}
return getMasterKeyId();
}
public String getPrimaryUserId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_STRING);
return (String) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean isRevoked() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean canCertify() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public long getEncryptId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean hasEncrypt() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public long getSignId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean hasSign() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public int getVerified() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Integer) data;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
public boolean hasAnySecret() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
KeychainContract.KeyRings.MASTER_KEY_ID,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
}
}

View File

@ -110,6 +110,8 @@ public class KeychainContract {
public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign";
public static final String PUBKEY_DATA = "pubkey_data";
public static final String PRIVKEY_DATA = "privkey_data";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@ -123,6 +125,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
public static Uri buildGenericKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
}
public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
@ -131,20 +137,24 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).build();
}
public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
public static Uri buildUnifiedKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId))
.appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingUri(Uri uri) {
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1))
.appendPath(PATH_UNIFIED).build();
}
public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build();
return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
.appendPath(PATH_BY_EMAIL).appendPath(email).build();
}
public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) {
return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build();
public static Uri buildUnifiedKeyRingsFindBySubkeyUri(long subkey) {
return CONTENT_URI.buildUpon().appendPath(PATH_FIND)
.appendPath(PATH_BY_SUBKEY).appendPath(Long.toString(subkey)).build();
}
}

View File

@ -23,11 +23,9 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
@ -256,6 +254,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}.getReadableDatabase();
Cursor cursor = null;
ProviderHelper providerHelper = new ProviderHelper(context);
try {
// we insert in two steps: first, all public keys that have secret keys
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
@ -266,14 +266,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0);
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
ProviderHelper providerHelper = new ProviderHelper(context);
if (ring instanceof PGPPublicKeyRing)
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
else if (ring instanceof PGPSecretKeyRing)
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
else {
Log.e(Constants.TAG, "Unknown blob data type!");
try {
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
providerHelper.savePublicKeyRing(ring);
} catch(PgpGeneralException e) {
Log.e(Constants.TAG, "Error decoding keyring blob!");
}
}
}
@ -293,14 +290,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
byte[] data = cursor.getBlob(0);
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
ProviderHelper providerHelper = new ProviderHelper(context);
if (ring instanceof PGPPublicKeyRing) {
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
} else if (ring instanceof PGPSecretKeyRing) {
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
} else {
Log.e(Constants.TAG, "Unknown blob data type!");
try {
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
providerHelper.savePublicKeyRing(ring);
} catch(PgpGeneralException e) {
Log.e(Constants.TAG, "Error decoding keyring blob!");
}
}
}

View File

@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
public class KeychainProvider extends ContentProvider {
@ -242,45 +243,39 @@ public class KeychainProvider extends ContentProvider {
HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID);
projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE);
projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);
projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE);
projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY);
projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN);
projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);
projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT);
projectionMap.put(KeyRings.CAN_SIGN, Tables.KEYS + "." + Keys.CAN_SIGN);
projectionMap.put(KeyRings.CREATION, Tables.KEYS + "." + Keys.CREATION);
projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY);
projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
projectionMap.put(KeyRings.HAS_SECRET, KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.PUBKEY_DATA,
Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
+ " AS " + KeyRings.PUBKEY_DATA);
projectionMap.put(KeyRings.PRIVKEY_DATA,
Tables.KEY_RINGS_SECRET + "." + KeyRingData.KEY_RING_DATA
+ " AS " + KeyRings.PRIVKEY_DATA);
projectionMap.put(KeyRings.HAS_SECRET, Tables.KEYS + "." + KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.HAS_ANY_SECRET,
"(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET
+ " WHERE " + Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ ")) AS " + KeyRings.HAS_ANY_SECRET);
projectionMap.put(KeyRings.HAS_ENCRYPT,
"(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k"
+" WHERE k." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND k." + Keys.IS_REVOKED + " = 0"
+ " AND k." + Keys.CAN_ENCRYPT + " = 1"
+ " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")) AS " + KeyRings.HAS_ENCRYPT);
"kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN,
"(EXISTS (SELECT * FROM " + Tables.KEYS + " AS k"
+" WHERE k." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND k." + Keys.IS_REVOKED + " = 0"
+ " AND k." + Keys.HAS_SECRET + " = 1"
+ " AND k." + Keys.CAN_SIGN + " = 1"
+ " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")) AS " + KeyRings.HAS_SIGN);
"kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
qb.setProjectionMap(projectionMap);
// Need this as list so we can search in it
List<String> plist = Arrays.asList(projection);
qb.setTables(
Tables.KEYS
+ " INNER JOIN " + Tables.USER_IDS + " ON ("
@ -295,6 +290,37 @@ public class KeychainProvider extends ContentProvider {
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED
+ " = " + Certs.VERIFIED_SECRET
+ ")"
// fairly expensive joins following, only do when requested
+ (plist.contains(KeyRings.PUBKEY_DATA) ?
" INNER JOIN " + Tables.KEY_RINGS_PUBLIC + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
+ Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID
+ ")" : "")
+ (plist.contains(KeyRings.PRIVKEY_DATA) ?
" LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
+ Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ ")" : "")
+ (plist.contains(KeyRings.HAS_ENCRYPT) ?
" LEFT JOIN " + Tables.KEYS + " AS kE ON ("
+"kE." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND kE." + Keys.IS_REVOKED + " = 0"
+ " AND kE." + Keys.CAN_ENCRYPT + " = 1"
+ " AND ( kE." + Keys.EXPIRY + " IS NULL OR kE." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")" : "")
+ (plist.contains(KeyRings.HAS_SIGN) ?
" LEFT JOIN " + Tables.KEYS + " AS kS ON ("
+"kS." + Keys.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND kS." + Keys.IS_REVOKED + " = 0"
+ " AND kS." + Keys.CAN_SIGN + " = 1"
+ " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")" : "")
);
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates
@ -595,7 +621,7 @@ public class KeychainProvider extends ContentProvider {
case KEY_RING_CERTS:
// we replace here, keeping only the latest signature
// TODO this would be better handled in saveKeyRing directly!
// TODO this would be better handled in savePublicKeyRing directly!
db.replaceOrThrow(Tables.CERTS, null, values);
keyId = values.getAsLong(Certs.MASTER_KEY_ID);
break;
@ -618,7 +644,7 @@ public class KeychainProvider extends ContentProvider {
}
if(keyId != null) {
uri = KeyRings.buildGenericKeyRingUri(keyId.toString());
uri = KeyRings.buildGenericKeyRingUri(keyId);
rowUri = uri;
}

View File

@ -23,26 +23,20 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@ -56,14 +50,12 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ProviderHelper {
@ -141,47 +133,29 @@ public class ProviderHelper {
public HashMap<String, Object> getUnifiedData(long masterKeyId, String[] proj, int[] types)
throws NotFoundException {
return getGenericData(KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types);
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
}
/**
* Find the master key id related to a given query. The id will either be extracted from the
* query, which should work for all specific /key_rings/ queries, or will be queried if it can't.
*/
public long extractOrGetMasterKeyId(Uri queryUri)
throws NotFoundException {
// try extracting from the uri first
String firstSegment = queryUri.getPathSegments().get(1);
if (!firstSegment.equals("find")) try {
return Long.parseLong(firstSegment);
} catch (NumberFormatException e) {
// didn't work? oh well.
Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
}
return getMasterKeyId(queryUri);
}
public long getMasterKeyId(Uri queryUri) throws NotFoundException {
Object data = getGenericData(queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
if (data != null) {
return (Long) data;
} else {
throw new NotFoundException();
}
}
public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
null, null, null);
LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
LongSparseArray<UncachedPublicKey> result =
new LongSparseArray<UncachedPublicKey>(cursor.getCount());
try {
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
byte[] data = cursor.getBlob(1);
if (data != null) {
result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
try {
result.put(masterKeyId,
UncachedKeyRing.decodePublicFromData(data).getPublicKey());
} catch(PgpGeneralException e) {
Log.e(Constants.TAG, "Error parsing keyring, skipping.");
} catch(IOException e) {
Log.e(Constants.TAG, "IO error, skipping keyring");
}
}
} while (cursor.moveToNext());
} finally {
@ -193,57 +167,74 @@ public class ProviderHelper {
return result;
}
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri);
if (result.size() == 0) {
throw new NotFoundException("PGPKeyRing object not found!");
} else {
return result.valueAt(0);
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) {
return new CachedPublicKeyRing(this, queryUri);
}
public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException {
return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
}
public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException {
return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false);
}
public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException {
return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
}
public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException {
return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true);
}
private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{
// we pick from cache only information that is not easily available from keyrings
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
// and of course, ring data
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
}, null, null, null
);
try {
if (cursor != null && cursor.moveToFirst()) {
boolean hasAnySecret = cursor.getInt(0) > 0;
int verified = cursor.getInt(1);
byte[] blob = cursor.getBlob(2);
if(secret &! hasAnySecret) {
throw new NotFoundException("Secret key not available!");
}
return secret
? new WrappedSecretKeyRing(blob, hasAnySecret, verified)
: new WrappedPublicKeyRing(blob, hasAnySecret, verified);
} else {
throw new NotFoundException("Key not found!");
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
long masterKeyId = getMasterKeyId(uri);
return getPGPPublicKeyRing(masterKeyId);
}
public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
long masterKeyId = getMasterKeyId(uri);
return getPGPSecretKeyRing(masterKeyId);
}
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
return (PGPPublicKeyRing) getPGPKeyRing(queryUri);
}
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
return (PGPSecretKeyRing) getPGPKeyRing(queryUri);
}
/**
* Saves PGPPublicKeyRing with its keys and userIds in DB
*/
@SuppressWarnings("unchecked")
public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException {
PGPPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyID();
public void savePublicKeyRing(UncachedKeyRing keyRing) throws IOException {
if (keyRing.isSecret()) {
throw new RuntimeException("Tried to save secret keyring as public! " +
"This is a bug, please file a bug report.");
}
UncachedPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyId();
// IF there is a secret key, preserve it!
PGPSecretKeyRing secretRing = null;
UncachedKeyRing secretRing = null;
try {
secretRing = getPGPSecretKeyRing(masterKeyId);
secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
} catch (NotFoundException e) {
Log.e(Constants.TAG, "key not found!");
}
@ -266,36 +257,38 @@ public class ProviderHelper {
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
++rank;
}
// get a list of owned secret keys, for verification filtering
LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
LongSparseArray<UncachedPublicKey> allKeyRings =
getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri());
// special case: available secret keys verify themselves!
if (secretRing != null)
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
if (secretRing != null) {
allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
}
// classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved.
List<UserIdItem> uids = new ArrayList<UserIdItem>();
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
for (String userId : new IterableIterator<String>(
masterKey.getUnorderedUserIds().iterator())) {
UserIdItem item = new UserIdItem();
uids.add(item);
item.userId = userId;
// look through signatures for this specific key
for (PGPSignature cert : new IterableIterator<PGPSignature>(
masterKey.getSignaturesForID(userId))) {
long certId = cert.getKeyID();
for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
masterKey.getSignaturesForId(userId))) {
long certId = cert.getKeyId();
try {
// self signature
if (certId == masterKeyId) {
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
if (!cert.verifyCertification(userId, masterKey)) {
cert.init(masterKey);
if (!cert.verifySignature(masterKey, userId)) {
// not verified?! dang! TODO notify user? this is kinda serious...
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
continue;
@ -304,31 +297,22 @@ public class ProviderHelper {
if (item.selfCert == null ||
item.selfCert.getCreationTime().before(cert.getCreationTime())) {
item.selfCert = cert;
item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID();
item.isRevoked =
cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
item.isPrimary = cert.isPrimaryUserId();
item.isRevoked = cert.isRevocation();
}
}
// verify signatures from known private keys
if (allKeyRings.indexOfKey(certId) >= 0) {
// mark them as verified
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
allKeyRings.get(certId).getPublicKey());
if (cert.verifyCertification(userId, masterKey)) {
cert.init(allKeyRings.get(certId));
if (cert.verifySignature(masterKey, userId)) {
item.trustedCerts.add(cert);
}
}
} catch (SignatureException e) {
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Signature verification failed! "
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
+ " from "
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
} catch (PGPException e) {
Log.e(Constants.TAG, "Signature verification failed! "
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
+ " from "
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e);
}
}
}
@ -365,7 +349,7 @@ public class ProviderHelper {
// Save the saved keyring (if any)
if (secretRing != null) {
saveKeyRing(secretRing);
saveSecretKeyRing(secretRing);
}
}
@ -374,8 +358,8 @@ public class ProviderHelper {
String userId;
boolean isPrimary = false;
boolean isRevoked = false;
PGPSignature selfCert;
List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>();
WrappedSignature selfCert;
List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
@Override
public int compareTo(UserIdItem o) {
@ -395,8 +379,13 @@ public class ProviderHelper {
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database!
*/
public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException {
long masterKeyId = keyRing.getPublicKey().getKeyID();
public void saveSecretKeyRing(UncachedKeyRing keyRing) throws IOException {
if (!keyRing.isSecret()) {
throw new RuntimeException("Tried to save publkc keyring as secret! " +
"This is a bug, please file a bug report.");
}
long masterKeyId = keyRing.getMasterKeyId();
{
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
@ -408,14 +397,10 @@ public class ProviderHelper {
values.put(Keys.HAS_SECRET, 1);
// then, mark exactly the keys we have available
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.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) {
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
Long.toString(sub.getKeyID())
});
}
for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
Long.toString(sub)
});
}
// this implicitly leaves all keys which were not in the secret key ring
// with has_secret = 0
@ -436,39 +421,39 @@ public class ProviderHelper {
/**
* Saves (or updates) a pair of public and secret KeyRings in the database
*/
public void saveKeyRing(PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException {
long masterKeyId = pubRing.getPublicKey().getKeyID();
public void saveKeyRing(UncachedKeyRing pubRing, UncachedKeyRing secRing) throws IOException {
long masterKeyId = pubRing.getPublicKey().getKeyId();
// delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below)
// delete secret keyring (so it isn't unnecessarily saved by public-savePublicKeyRing below)
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
// save public keyring
saveKeyRing(pubRing);
saveKeyRing(privRing);
savePublicKeyRing(pubRing);
saveSecretKeyRing(secRing);
}
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private ContentProviderOperation
buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException {
buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
ContentValues values = new ContentValues();
values.put(Keys.MASTER_KEY_ID, masterKeyId);
values.put(Keys.RANK, rank);
values.put(Keys.KEY_ID, key.getKeyID());
values.put(Keys.KEY_ID, key.getKeyId());
values.put(Keys.KEY_SIZE, key.getBitStrength());
values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint());
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
values.put(Keys.IS_REVOKED, key.isRevoked());
values.put(Keys.CAN_CERTIFY, key.canCertify());
values.put(Keys.CAN_SIGN, key.canSign());
values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
values.put(Keys.IS_REVOKED, key.maybeRevoked());
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
Date expiryDate = key.getExpiryTime();
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
@ -482,11 +467,12 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private ContentProviderOperation
buildCertOperations(long masterKeyId, int rank, PGPSignature cert, int verified) throws IOException {
buildCertOperations(long masterKeyId, int rank, WrappedSignature cert, int verified)
throws IOException {
ContentValues values = new ContentValues();
values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank);
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyId());
values.put(Certs.TYPE, cert.getSignatureType());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
values.put(Certs.VERIFIED, verified);
@ -514,23 +500,11 @@ public class ProviderHelper {
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
private String getKeyRingAsArmoredString(byte[] data) throws IOException {
Object keyRing = null;
if (data != null) {
keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
}
private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
aos.setHeader("Version", PgpHelper.getFullVersion(mContext));
if (keyRing instanceof PGPSecretKeyRing) {
aos.write(((PGPSecretKeyRing) keyRing).getEncoded());
} else if (keyRing instanceof PGPPublicKeyRing) {
aos.write(((PGPPublicKeyRing) keyRing).getEncoded());
}
aos.close();
keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
@ -539,77 +513,12 @@ public class ProviderHelper {
}
public String getKeyRingAsArmoredString(Uri uri)
throws NotFoundException, IOException {
throws NotFoundException, IOException, PgpGeneralException {
byte[] data = (byte[]) getGenericData(
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
return getKeyRingAsArmoredString(data);
}
/**
* TODO: currently not used, but will be needed to upload many keys at once!
*
* @param masterKeyIds
* @return
* @throws IOException
*/
public ArrayList<String> getKeyRingsAsArmoredString(long[] masterKeyIds)
throws IOException {
ArrayList<String> output = new ArrayList<String>();
if (masterKeyIds == null || masterKeyIds.length == 0) {
Log.e(Constants.TAG, "No master keys given!");
return output;
}
// Build a cursor for the selected masterKeyIds
Cursor cursor;
{
String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
for (int i = 0; i < masterKeyIds.length; ++i) {
if (i != 0) {
inMasterKeyList += ", ";
}
inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]);
}
inMasterKeyList += ")";
cursor = mContentResolver.query(KeyRingData.buildPublicKeyRingUri(), new String[]{
KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
}, inMasterKeyList, null, null);
}
try {
if (cursor != null) {
int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
if (cursor.moveToFirst()) {
do {
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
byte[] data = cursor.getBlob(dataCol);
// get actual keyring data blob and write it to ByteArrayOutputStream
try {
output.add(getKeyRingAsArmoredString(data));
} catch (IOException e) {
Log.e(Constants.TAG, "IOException", e);
}
} while (cursor.moveToNext());
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
if (output.size() > 0) {
return output;
} else {
return null;
}
}
public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);

View File

@ -419,14 +419,14 @@ public class OpenPgpService extends RemoteService {
try {
// try to find key, throws NotFoundException if not in db!
mProviderHelper.getPGPPublicKeyRing(masterKeyId);
mProviderHelper.getWrappedPublicKeyRing(masterKeyId);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
// also return PendingIntent that opens the key view activity
Intent intent = new Intent(getBaseContext(), ViewKeyActivity.class);
intent.setData(KeyRings.buildGenericKeyRingUri(Long.toString(masterKeyId)));
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId));
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
intent,

View File

@ -33,6 +33,7 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.ui.EditKeyActivity;
@ -179,9 +180,10 @@ public class AccountSettingsFragment extends Fragment implements
// select newly created key
try {
long masterKeyId = new ProviderHelper(getActivity())
.extractOrGetMasterKeyId(data.getData());
.getCachedPublicKeyRing(data.getData())
.extractOrGetMasterKeyId();
mSelectKeyFragment.selectKey(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}

View File

@ -26,29 +26,25 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@ -60,7 +56,6 @@ import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@ -497,7 +492,7 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_SAVE_KEYRING.equals(action)) {
try {
/* Input */
SaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
OldSaveKeyringParcel saveParcel = data.getParcelable(SAVE_KEYRING_PARCEL);
String oldPassphrase = saveParcel.oldPassphrase;
String newPassphrase = saveParcel.newPassphrase;
boolean canSign = true;
@ -510,33 +505,36 @@ public class KeychainIntentService extends IntentService
newPassphrase = oldPassphrase;
}
long masterKeyId = saveParcel.keys.get(0).getKeyID();
long masterKeyId = saveParcel.keys.get(0).getKeyId();
/* Operation */
ProviderHelper providerHelper = new ProviderHelper(this);
if (!canSign) {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
oldPassphrase, newPassphrase);
setProgress(R.string.progress_building_key, 0, 100);
WrappedSecretKeyRing keyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
UncachedKeyRing newKeyRing =
keyRing.changeSecretKeyPassphrase(oldPassphrase, newPassphrase);
setProgress(R.string.progress_saving_key_ring, 50, 100);
providerHelper.saveKeyRing(keyRing);
providerHelper.saveSecretKeyRing(newKeyRing);
setProgress(R.string.progress_done, 100, 100);
} else {
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
PgpKeyOperation.Pair<PGPSecretKeyRing, PGPPublicKeyRing> pair;
try {
PGPSecretKeyRing privkey = providerHelper.getPGPSecretKeyRing(masterKeyId);
PGPPublicKeyRing pubkey = providerHelper.getPGPPublicKeyRing(masterKeyId);
WrappedSecretKeyRing seckey = providerHelper.getWrappedSecretKeyRing(masterKeyId);
WrappedPublicKeyRing pubkey = providerHelper.getWrappedPublicKeyRing(masterKeyId);
pair = keyOperations.buildSecretKey(privkey, pubkey, saveParcel); // edit existing
PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
keyOperations.buildSecretKey(seckey, pubkey, saveParcel); // edit existing
setProgress(R.string.progress_saving_key_ring, 90, 100);
providerHelper.saveKeyRing(pair.first, pair.second);
} catch (ProviderHelper.NotFoundException e) {
pair = keyOperations.buildNewSecretKey(saveParcel); //new Keyring
PgpKeyOperation.Pair<UncachedKeyRing,UncachedKeyRing> pair =
keyOperations.buildNewSecretKey(saveParcel); //new Keyring
// save the pair
setProgress(R.string.progress_saving_key_ring, 90, 100);
providerHelper.saveKeyRing(pair.first, pair.second);
}
setProgress(R.string.progress_saving_key_ring, 90, 100);
// save the pair
providerHelper.saveKeyRing(pair.second, pair.first);
setProgress(R.string.progress_done, 100, 100);
}
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
@ -556,13 +554,11 @@ public class KeychainIntentService extends IntentService
/* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
passphrase, masterKey);
byte[] newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
/* Output */
Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY,
PgpConversionHelper.PGPSecretKeyToBytes(newKey));
resultData.putByteArray(RESULT_NEW_KEY, newKey);
OtherHelper.logDebugBundle(resultData, "resultData");
@ -575,7 +571,6 @@ public class KeychainIntentService extends IntentService
try {
/* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */
@ -588,24 +583,28 @@ public class KeychainIntentService extends IntentService
keysTotal);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey masterKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buf;
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, true);
newKeys.add(masterKey);
keyUsageList.add(KeyFlags.CERTIFY_OTHER);
os.write(buf);
keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
keysCreated++;
setProgress(keysCreated, keysTotal);
PGPSecretKey subKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false);
newKeys.add(subKey);
keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
os.write(buf);
keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
keysCreated++;
setProgress(keysCreated, keysTotal);
subKey = keyOperations.createKey(Constants.choice.algorithm.rsa,
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false);
newKeys.add(subKey);
keyUsageList.add(KeyFlags.SIGN_DATA);
os.write(buf);
keyUsageList.add(UncachedSecretKey.SIGN_DATA);
keysCreated++;
setProgress(keysCreated, keysTotal);
@ -613,10 +612,8 @@ public class KeychainIntentService extends IntentService
// for sign
/* Output */
Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY,
PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
resultData.putByteArray(RESULT_NEW_KEY, os.toByteArray());
resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData");
@ -648,12 +645,10 @@ public class KeychainIntentService extends IntentService
}
} else if (ACTION_IMPORT_KEYRING.equals(action)) {
try {
List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
Bundle resultData = new Bundle();
List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
resultData = pgpImportExport.importKeyRings(entries);
Bundle resultData = pgpImportExport.importKeyRings(entries);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
@ -727,15 +722,12 @@ public class KeychainIntentService extends IntentService
HkpKeyserver server = new HkpKeyserver(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this);
PGPPublicKeyRing keyring = (PGPPublicKeyRing) providerHelper.getPGPKeyRing(dataUri);
if (keyring != null) {
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri);
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server,
(PGPPublicKeyRing) keyring);
if (!uploaded) {
throw new PgpGeneralException("Unable to export key to selected server");
}
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
if (!uploaded) {
throw new PgpGeneralException("Unable to export key to selected server");
}
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
@ -747,40 +739,21 @@ public class KeychainIntentService extends IntentService
try {
KeybaseKeyserver server = new KeybaseKeyserver();
ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) {
// the keybase handle is in userId(1)
String keybaseId = entry.getExtraData();
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
// create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null;
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null) {
if (obj instanceof PGPKeyRing) {
downloadedKey = (PGPKeyRing) obj;
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
}
// save key bytes in entry object for doing the
// actual import afterwards
entry.setBytes(downloadedKey.getEncoded());
keyRings.add(new ParcelableKeyRing(downloadedKeyBytes));
}
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@ -794,11 +767,12 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) {
try {
ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
// this downloads the keys and places them into the ImportKeysListEntry entries
String keyServer = data.getString(DOWNLOAD_KEY_SERVER);
HkpKeyserver server = new HkpKeyserver(keyServer);
ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
for (ImportKeysListEntry entry : entries) {
// if available use complete fingerprint for get request
byte[] downloadedKeyBytes;
@ -808,49 +782,15 @@ public class KeychainIntentService extends IntentService
downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
}
// create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null;
BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// get first object in block
Object obj;
if ((obj = objectFactory.nextObject()) != null) {
if (obj instanceof PGPKeyRing) {
downloadedKey = (PGPKeyRing) obj;
} else {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
}
// verify downloaded key by comparing fingerprints
if (entry.getFingerprintHex() != null) {
String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
downloadedKey.getPublicKey().getFingerprint());
if (downloadedKeyFp.equals(entry.getFingerprintHex())) {
Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
"the requested fingerprint!");
} else {
throw new PgpGeneralException("fingerprint of downloaded key is " +
"NOT the same as the requested fingerprint!");
}
}
// save key bytes in entry object for doing the
// actual import afterwards
entry.setBytes(downloadedKey.getEncoded());
keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
}
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@ -877,29 +817,18 @@ public class KeychainIntentService extends IntentService
}
ProviderHelper providerHelper = new ProviderHelper(this);
PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPPublicKeyRing publicRing = providerHelper.getPGPPublicKeyRing(pubKeyId);
PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
PGPSecretKeyRing secretKeyRing = null;
try {
secretKeyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: throw exception here!
WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId);
WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
WrappedSecretKey certificationKey = secretKeyRing.getSubKey();
if(!certificationKey.unlock(signaturePassphrase)) {
throw new PgpGeneralException("Error extracting key (bad passphrase?)");
}
PGPSecretKey certificationKey = PgpKeyHelper.getFirstCertificationSubkey(secretKeyRing);
publicKey = keyOperation.certifyKey(certificationKey, publicKey,
userIds, signaturePassphrase);
publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
// store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
int retval = pgpImportExport.storeKeyRingInCache(publicRing);
if (retval != PgpImportExport.RETURN_OK && retval != PgpImportExport.RETURN_UPDATED) {
throw new PgpGeneralException("Failed to store signed key in local cache");
}
providerHelper.savePublicKeyRing(newRing);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
} catch (Exception e) {
sendErrorToHandler(e);
}

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
/** Class for parcelling data between ui and services.
* This class is outdated and scheduled for removal, pending a rewrite of the
* EditKeyActivity and save keyring routines.
*/
@Deprecated
public class OldSaveKeyringParcel implements Parcelable {
public ArrayList<String> userIds;
public ArrayList<String> originalIDs;
public ArrayList<String> deletedIDs;
public boolean[] newIDs;
public boolean primaryIDChanged;
public boolean[] moddedKeys;
public ArrayList<UncachedSecretKey> deletedKeys;
public ArrayList<Calendar> keysExpiryDates;
public ArrayList<Integer> keysUsages;
public String newPassphrase;
public String oldPassphrase;
public boolean[] newKeys;
public ArrayList<UncachedSecretKey> keys;
public String originalPrimaryID;
public OldSaveKeyringParcel() {}
private OldSaveKeyringParcel(Parcel source) {
userIds = (ArrayList<String>) source.readSerializable();
originalIDs = (ArrayList<String>) source.readSerializable();
deletedIDs = (ArrayList<String>) source.readSerializable();
newIDs = source.createBooleanArray();
primaryIDChanged = source.readByte() != 0;
moddedKeys = source.createBooleanArray();
byte[] tmp = source.createByteArray();
if (tmp == null) {
deletedKeys = null;
} else {
deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
}
keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
keysUsages = source.readArrayList(Integer.class.getClassLoader());
newPassphrase = source.readString();
oldPassphrase = source.readString();
newKeys = source.createBooleanArray();
keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
originalPrimaryID = source.readString();
}
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeSerializable(userIds); //might not be the best method to store.
destination.writeSerializable(originalIDs);
destination.writeSerializable(deletedIDs);
destination.writeBooleanArray(newIDs);
destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
destination.writeBooleanArray(moddedKeys);
destination.writeByteArray(encodeArrayList(deletedKeys));
destination.writeSerializable(keysExpiryDates);
destination.writeList(keysUsages);
destination.writeString(newPassphrase);
destination.writeString(oldPassphrase);
destination.writeBooleanArray(newKeys);
destination.writeByteArray(encodeArrayList(keys));
destination.writeString(originalPrimaryID);
}
public static final Creator<OldSaveKeyringParcel> CREATOR = new Creator<OldSaveKeyringParcel>() {
public OldSaveKeyringParcel createFromParcel(final Parcel source) {
return new OldSaveKeyringParcel(source);
}
public OldSaveKeyringParcel[] newArray(final int size) {
return new OldSaveKeyringParcel[size];
}
};
private static byte[] encodeArrayList(ArrayList<UncachedSecretKey> list) {
if(list.isEmpty()) {
return null;
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
for(UncachedSecretKey key : new IterableIterator<UncachedSecretKey>(list.iterator())) {
try {
key.encodeSecretKey(os);
} catch (IOException e) {
Log.e(Constants.TAG, "Error while converting ArrayList<UncachedSecretKey> to byte[]!", e);
}
}
return os.toByteArray();
}
@Override
public int describeContents() {
return 0;
}
}

View File

@ -34,20 +34,14 @@ import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
import java.util.Iterator;
/**
* This service runs in its own process, but is available to all other processes as the main
@ -163,81 +157,47 @@ public class PassphraseCacheService extends Service {
* @return
*/
private String getCachedPassphraseImpl(long keyId) {
Log.d(TAG, "getCachedPassphraseImpl() get masterKeyId for " + keyId);
// passphrase for symmetric encryption?
if (keyId == Constants.key.symmetric) {
Log.d(TAG, "getCachedPassphraseImpl() for symmetric encryption");
String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
if (cachedPassphrase == null) {
return null;
}
addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase);
return cachedPassphrase;
}
// try to get master key id which is used as an identifier for cached passphrases
long masterKeyId = keyId;
if (masterKeyId != Constants.key.symmetric) {
try {
masterKeyId = new ProviderHelper(this).getMasterKeyId(
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)));
} catch (ProviderHelper.NotFoundException e) {
return null;
}
}
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
// get cached passphrase
String cachedPassphrase = mPassphraseCache.get(masterKeyId);
if (cachedPassphrase == null) {
// if key has no passphrase -> cache and return empty passphrase
if (!hasPassphrase(this, masterKeyId)) {
try {
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + keyId);
WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
// no passphrase needed? just add empty string and return it, then
if (!key.hasPassphrase()) {
Log.d(Constants.TAG, "Key has no passphrase! Caches and returns empty passphrase!");
addCachedPassphrase(this, masterKeyId, "");
addCachedPassphrase(this, keyId, "");
return "";
} else {
}
// get cached passphrase
String cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) {
Log.d(TAG, "Passphrase not (yet) cached, returning null");
// not really an error, just means the passphrase is not cached but not empty either
return null;
}
}
// set it again to reset the cache life cycle
Log.d(TAG, "Cache passphrase again when getting it!");
addCachedPassphrase(this, masterKeyId, cachedPassphrase);
return cachedPassphrase;
}
// set it again to reset the cache life cycle
Log.d(TAG, "Cache passphrase again when getting it!");
addCachedPassphrase(this, keyId, cachedPassphrase);
return cachedPassphrase;
public static boolean hasPassphrase(PGPSecretKeyRing secretKeyRing) {
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
for (Iterator keys = secretKeyRing.getSecretKeys(); keys.hasNext(); ) {
secretKey = (PGPSecretKey) keys.next();
if (!secretKey.isPrivateKeyEmpty()) {
foundValidKey = true;
break;
}
}
if(!foundValidKey) {
return false;
}
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider("SC").build("".toCharArray());
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
return testKey == null;
} catch(PGPException e) {
// this means the crc check failed -> passphrase required
return true;
}
}
/**
* Checks if key has a passphrase.
*
* @param secretKeyId
* @return true if it has a passphrase
*/
public static boolean hasPassphrase(Context context, long secretKeyId) {
// check if the key has no passphrase
try {
PGPSecretKeyRing secRing = new ProviderHelper(context).getPGPSecretKeyRing(secretKeyId);
return hasPassphrase(secRing);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
Log.e(TAG, "Passphrase for unknown key was requested!");
return null;
}
return true;
}
/**

View File

@ -1,92 +1,101 @@
/*
* Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.service;
import android.os.Parcel;
import android.os.Parcelable;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import java.util.ArrayList;
import java.util.Calendar;
import java.io.Serializable;
import java.util.HashMap;
/** This class is a a transferable representation for a collection of changes
* to be done on a keyring.
*
* This class should include all types of operations supported in the backend.
*
* All changes are done in a differential manner. Besides the two key
* identification attributes, all attributes may be null, which indicates no
* change to the keyring. This is also the reason why boxed values are used
* instead of primitives in the subclasses.
*
* Application of operations in the backend should be fail-fast, which means an
* error in any included operation (for example revocation of a non-existent
* subkey) will cause the operation as a whole to fail.
*/
public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> userIds;
public ArrayList<String> originalIDs;
public ArrayList<String> deletedIDs;
public boolean[] newIDs;
public boolean primaryIDChanged;
public boolean[] moddedKeys;
public ArrayList<PGPSecretKey> deletedKeys;
public ArrayList<Calendar> keysExpiryDates;
public ArrayList<Integer> keysUsages;
// the master key id to be edited
private final long mMasterKeyId;
// the key fingerprint, for safety
private final byte[] mFingerprint;
public String newPassphrase;
public String oldPassphrase;
public boolean[] newKeys;
public ArrayList<PGPSecretKey> keys;
public String originalPrimaryID;
public SaveKeyringParcel() {}
public String[] addUserIds;
public SubkeyAdd[] addSubKeys;
private SaveKeyringParcel(Parcel source) {
userIds = (ArrayList<String>) source.readSerializable();
originalIDs = (ArrayList<String>) source.readSerializable();
deletedIDs = (ArrayList<String>) source.readSerializable();
newIDs = source.createBooleanArray();
primaryIDChanged = source.readByte() != 0;
moddedKeys = source.createBooleanArray();
byte[] tmp = source.createByteArray();
if (tmp == null) {
deletedKeys = null;
} else {
deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
public HashMap<Long, SubkeyChange> changeSubKeys;
public String changePrimaryUserId;
public String[] revokeUserIds;
public long[] revokeSubKeys;
public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {
mMasterKeyId = masterKeyId;
mFingerprint = fingerprint;
}
// performance gain for using Parcelable here would probably be negligible,
// use Serializable instead.
public static class SubkeyAdd implements Serializable {
public final int mAlgorithm;
public final int mKeysize;
public final int mFlags;
public final Long mExpiry;
public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
mAlgorithm = algorithm;
mKeysize = keysize;
mFlags = flags;
mExpiry = expiry;
}
keysExpiryDates = (ArrayList<Calendar>) source.readSerializable();
keysUsages = source.readArrayList(Integer.class.getClassLoader());
newPassphrase = source.readString();
oldPassphrase = source.readString();
newKeys = source.createBooleanArray();
keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
originalPrimaryID = source.readString();
}
public static class SubkeyChange implements Serializable {
public final long mKeyId;
public final Integer mFlags;
public final Long mExpiry;
public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId;
mFlags = flags;
mExpiry = expiry;
}
}
public SaveKeyringParcel(Parcel source) {
mMasterKeyId = source.readLong();
mFingerprint = source.createByteArray();
addUserIds = source.createStringArray();
addSubKeys = (SubkeyAdd[]) source.readSerializable();
changeSubKeys = (HashMap<Long,SubkeyChange>) source.readSerializable();
changePrimaryUserId = source.readString();
revokeUserIds = source.createStringArray();
revokeSubKeys = source.createLongArray();
}
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeSerializable(userIds); //might not be the best method to store.
destination.writeSerializable(originalIDs);
destination.writeSerializable(deletedIDs);
destination.writeBooleanArray(newIDs);
destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
destination.writeBooleanArray(moddedKeys);
byte[] tmp = null;
if (deletedKeys.size() != 0) {
tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys);
}
destination.writeByteArray(tmp);
destination.writeSerializable(keysExpiryDates);
destination.writeList(keysUsages);
destination.writeString(newPassphrase);
destination.writeString(oldPassphrase);
destination.writeBooleanArray(newKeys);
destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
destination.writeString(originalPrimaryID);
destination.writeLong(mMasterKeyId);
destination.writeByteArray(mFingerprint);
destination.writeStringArray(addUserIds);
destination.writeSerializable(addSubKeys);
destination.writeSerializable(changeSubKeys);
destination.writeString(changePrimaryUserId);
destination.writeStringArray(revokeUserIds);
destination.writeLongArray(revokeSubKeys);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@ -103,4 +112,5 @@ public class SaveKeyringParcel implements Parcelable {
public int describeContents() {
return 0;
}
}

View File

@ -43,7 +43,6 @@ import android.widget.TextView;
import com.devspark.appmsg.AppMsg;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@ -51,7 +50,6 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@ -222,54 +220,22 @@ public class CertifyKeyActivity extends ActionBarActivity implements
* handles the UI bits of the signing process on the UI thread
*/
private void initiateSigning() {
try {
PGPPublicKeyRing pubring = new ProviderHelper(this).getPGPPublicKeyRing(mPubKeyId);
// if we have already signed this key, dont bother doing it again
boolean alreadySigned = false;
/* todo: reconsider this at a later point when certs are in the db
@SuppressWarnings("unchecked")
Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
while (itr.hasNext()) {
PGPSignature sig = itr.next();
if (sig.getKeyID() == mMasterKeyId) {
alreadySigned = true;
break;
}
}
*/
if (!alreadySigned) {
/*
* get the user's passphrase for this key (if required)
*/
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
PassphraseDialogFragment.show(this, mMasterKeyId,
new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
startSigning();
}
}
// get the user's passphrase for this key (if required)
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
PassphraseDialogFragment.show(this, mMasterKeyId,
new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
startSigning();
}
);
// bail out; need to wait until the user has entered the passphrase before trying again
return;
} else {
startSigning();
}
} else {
AppMsg.makeText(this, R.string.key_has_already_been_certified, AppMsg.STYLE_ALERT)
.show();
setResult(RESULT_CANCELED);
finish();
}
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
});
// bail out; need to wait until the user has entered the passphrase before trying again
return;
} else {
startSigning();
}
}

View File

@ -19,7 +19,6 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@ -44,21 +43,22 @@ import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
@ -67,7 +67,6 @@ import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
import org.sufficientlysecure.keychain.ui.widget.SectionView;
import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@ -89,8 +88,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// EDIT
private Uri mDataUri;
private PGPSecretKeyRing mKeyRing = null;
private SectionView mUserIdsView;
private SectionView mKeysView;
@ -106,7 +103,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
private CheckBox mNoPassphrase;
Vector<String> mUserIds;
Vector<PGPSecretKey> mKeys;
Vector<UncachedSecretKey> mKeys;
Vector<Integer> mKeysUsages;
boolean mMasterCanSign = true;
@ -159,7 +156,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
);
mUserIds = new Vector<String>();
mKeys = new Vector<PGPSecretKey>();
mKeys = new Vector<UncachedSecretKey>();
mKeysUsages = new Vector<Integer>();
// Catch Intents opened from other apps
@ -240,7 +237,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
// get new key from data bundle returned from service
Bundle data = message.getData();
ArrayList<PGPSecretKey> newKeys =
ArrayList<UncachedSecretKey> newKeys =
PgpConversionHelper.BytesToPGPSecretKeyList(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
@ -288,18 +285,18 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Log.d(Constants.TAG, "uri: " + mDataUri);
try {
Uri secretUri = KeychainContract.KeyRingData.buildSecretKeyRingUri(mDataUri);
mKeyRing = (PGPSecretKeyRing) new ProviderHelper(this).getPGPKeyRing(secretUri);
Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri);
PGPSecretKey masterKey = mKeyRing.getSecretKey();
mMasterCanSign = PgpKeyHelper.isCertificationKey(mKeyRing.getSecretKey());
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mKeyRing.getSecretKeys())) {
mKeys.add(key);
mKeysUsages.add(-1); // get usage when view is created
mMasterCanSign = keyRing.getSubKey().canCertify();
for (WrappedSecretKey key : keyRing.iterator()) {
// Turn into uncached instance
mKeys.add(key.getUncached());
mKeysUsages.add(key.getKeyUsage()); // get usage when view is created
}
boolean isSet = false;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
for (String userId : keyRing.getSubKey().getUserIds()) {
Log.d(Constants.TAG, "Added userId " + userId);
if (!isSet) {
isSet = true;
@ -314,7 +311,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
buildLayout(false);
mCurrentPassphrase = "";
mIsPassphraseSet = PassphraseCacheService.hasPassphrase(mKeyRing);
mIsPassphraseSet = keyRing.hasPassphrase();
if (!mIsPassphraseSet) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
@ -432,7 +429,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
if (mKeysView.getEditors().getChildCount() == 0) {
return 0;
}
return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID();
return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId();
}
public boolean isPassphraseSet() {
@ -556,7 +553,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
SaveKeyringParcel saveParams = new SaveKeyringParcel();
OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel();
saveParams.userIds = getUserIds(mUserIdsView);
saveParams.originalIDs = mUserIdsView.getOriginalIDs();
saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
@ -572,7 +569,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
saveParams.keys = getKeys(mKeysView);
saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
// fill values for this action
Bundle data = new Bundle();
data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
@ -591,8 +587,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
Intent data = new Intent();
// return uri pointing to new created key
Uri uri = KeychainContract.KeyRings.buildGenericKeyRingUri(
String.valueOf(getMasterKeyId()));
Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId());
data.setData(uri);
setResult(RESULT_OK, data);
@ -690,8 +685,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener
* @param keysView
* @return
*/
private ArrayList<PGPSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
ViewGroup keyEditors = keysView.getEditors();

View File

@ -30,11 +30,11 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
@ -144,20 +144,17 @@ public class EncryptAsymmetricFragment extends Fragment {
*/
private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds,
ProviderHelper providerHelper) {
// TODO all of this works under the assumption that the first suitable subkey is always used!
// not sure if we need to distinguish between different subkeys here?
if (preselectedSignatureKeyId != 0) {
// TODO: don't use bouncy castle objects!
try {
PGPSecretKeyRing keyRing = providerHelper.getPGPSecretKeyRingWithKeyId(
preselectedSignatureKeyId);
PGPSecretKey masterKey = keyRing.getSecretKey();
if (masterKey != null) {
PGPSecretKey signKey = PgpKeyHelper.getFirstSigningSubkey(keyRing);
if (signKey != null) {
setSignatureKeyId(masterKey.getKeyID());
}
CachedPublicKeyRing keyring =
providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(preselectedSignatureKeyId));
if(keyring.hasAnySecret()) {
setSignatureKeyId(keyring.getMasterKeyId());
}
} catch (ProviderHelper.NotFoundException e) {
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
@ -165,14 +162,13 @@ public class EncryptAsymmetricFragment extends Fragment {
if (preselectedEncryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
// TODO One query per selected key?! wtf
try {
long id = providerHelper.getMasterKeyId(
long id = providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(preselectedEncryptionKeyIds[i]))
);
preselectedEncryptionKeyIds[i])
).getMasterKeyId();
goodIds.add(id);
} catch (ProviderHelper.NotFoundException e) {
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}

View File

@ -42,6 +42,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
@ -415,8 +416,8 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
// fill values for this action
Bundle data = new Bundle();
// get selected key entries
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
// get DATA from selected key entries
ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -442,7 +443,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, mListFragment.getKeyServer());
// get selected key entries
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -466,7 +467,7 @@ public class ImportKeysActivity extends ActionBarActivity implements ActionBar.O
Bundle data = new Bundle();
// get selected key entries
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedData();
ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);

View File

@ -23,6 +23,7 @@ import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.util.LongSparseArray;
import android.view.View;
import android.widget.ListView;
@ -31,6 +32,7 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@ -67,6 +69,8 @@ public class ImportKeysListFragment extends ListFragment implements
private static final int LOADER_ID_SERVER_QUERY = 1;
private static final int LOADER_ID_KEYBASE = 2;
private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
public byte[] getKeyBytes() {
return mKeyBytes;
}
@ -91,8 +95,16 @@ public class ImportKeysListFragment extends ListFragment implements
return mAdapter.getData();
}
public ArrayList<ImportKeysListEntry> getSelectedData() {
return mAdapter.getSelectedData();
public ArrayList<ParcelableKeyRing> getSelectedData() {
ArrayList<ParcelableKeyRing> result = new ArrayList<ParcelableKeyRing>();
for(ImportKeysListEntry entry : getSelectedEntries()) {
result.add(mCachedKeyData.get(entry.getKeyId()));
}
return result;
}
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
return mAdapter.getSelectedEntries();
}
/**
@ -120,8 +132,7 @@ public class ImportKeysListFragment extends ListFragment implements
mActivity = getActivity();
// Give some text to display if there is no data. In a real
// application this would come from a resource.
// Give some text to display if there is no data.
setEmptyText(mActivity.getString(R.string.error_nothing_import));
// Create an empty adapter we will use to display the loaded data.
@ -252,11 +263,15 @@ public class ImportKeysListFragment extends ListFragment implements
Exception error = data.getError();
// free old cached key data
mCachedKeyData = null;
switch (loader.getId()) {
case LOADER_ID_BYTES:
if (error == null) {
// No error
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
} else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
AppMsg.makeText(getActivity(), R.string.error_import_file_no_content,
AppMsg.STYLE_ALERT).show();

View File

@ -317,7 +317,7 @@ public class KeyListFragment extends LoaderFragment
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent viewIntent = new Intent(getActivity(), ViewKeyActivity.class);
viewIntent.setData(
KeyRings.buildGenericKeyRingUri(Long.toString(mAdapter.getMasterKeyId(position))));
KeyRings.buildGenericKeyRingUri(mAdapter.getMasterKeyId(position)));
startActivity(viewIntent);
}

View File

@ -84,7 +84,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position);
Uri result = KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
Uri result = KeyRings.buildGenericKeyRingUri(masterKeyId);
// return data to activity, which results in finishing it
mActivity.afterListSelection(result);

View File

@ -132,7 +132,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan
//For AppSettingsFragment
public void selectKey(long masterKeyId) {
Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(masterKeyId);
mReceivedUri = buildUri;
getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
}

View File

@ -32,24 +32,17 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.security.SignatureException;
import java.util.Date;
public class ViewCertActivity extends ActionBarActivity
@ -146,32 +139,25 @@ public class ViewCertActivity extends ActionBarActivity
mCertifierUid.setText(R.string.unknown_uid);
}
PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA));
WrappedSignature sig = WrappedSignature.fromBytes(data.getBlob(INDEX_DATA));
try {
ProviderHelper providerHelper = new ProviderHelper(this);
PGPKeyRing signeeRing = providerHelper.getPGPKeyRing(
KeychainContract.KeyRingData.buildPublicKeyRingUri(
Long.toString(data.getLong(INDEX_MASTER_KEY_ID)))
);
PGPKeyRing signerRing = providerHelper.getPGPKeyRing(
KeychainContract.KeyRingData.buildPublicKeyRingUri(
Long.toString(sig.getKeyID()))
);
WrappedPublicKeyRing signeeRing =
providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
WrappedPublicKeyRing signerRing =
providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
try {
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME), signerRing.getPublicKey());
if (sig.verifyCertification(signeeUid, signeeRing.getPublicKey())) {
sig.init(signerRing.getSubkey());
if (sig.verifySignature(signeeRing.getSubkey(), signeeUid)) {
mStatus.setText(R.string.cert_verify_ok);
mStatus.setTextColor(getResources().getColor(R.color.bbutton_success));
} else {
mStatus.setText(R.string.cert_verify_failed);
mStatus.setTextColor(getResources().getColor(R.color.alert));
}
} catch (SignatureException e) {
mStatus.setText(R.string.cert_verify_error);
mStatus.setTextColor(getResources().getColor(R.color.alert));
} catch (PGPException e) {
} catch (PgpGeneralException e) {
mStatus.setText(R.string.cert_verify_error);
mStatus.setTextColor(getResources().getColor(R.color.alert));
}
@ -185,29 +171,26 @@ public class ViewCertActivity extends ActionBarActivity
mRowReason.setVisibility(View.GONE);
switch (data.getInt(INDEX_TYPE)) {
case PGPSignature.DEFAULT_CERTIFICATION:
case WrappedSignature.DEFAULT_CERTIFICATION:
mType.setText(R.string.cert_default);
break;
case PGPSignature.NO_CERTIFICATION:
case WrappedSignature.NO_CERTIFICATION:
mType.setText(R.string.cert_none);
break;
case PGPSignature.CASUAL_CERTIFICATION:
case WrappedSignature.CASUAL_CERTIFICATION:
mType.setText(R.string.cert_casual);
break;
case PGPSignature.POSITIVE_CERTIFICATION:
case WrappedSignature.POSITIVE_CERTIFICATION:
mType.setText(R.string.cert_positive);
break;
case PGPSignature.CERTIFICATION_REVOCATION: {
case WrappedSignature.CERTIFICATION_REVOCATION: {
mType.setText(R.string.cert_revoke);
if (sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) {
SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket(
SignatureSubpacketTags.REVOCATION_REASON);
// For some reason, this is missing in SignatureSubpacketInputStream:146
if (!(p instanceof RevocationReason)) {
p = new RevocationReason(false, p.getData());
if (sig.isRevocation()) {
try {
mReason.setText(sig.getRevocationReason());
} catch(PgpGeneralException e) {
mReason.setText(R.string.none);
}
String reason = ((RevocationReason) p).getRevocationDescription();
mReason.setText(reason);
mRowReason.setVisibility(View.VISIBLE);
}
break;
@ -223,14 +206,11 @@ public class ViewCertActivity extends ActionBarActivity
try {
ProviderHelper providerHelper = new ProviderHelper(ViewCertActivity.this);
long signerMasterKeyId = providerHelper.getMasterKeyId(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mCertifierKeyId))
);
viewIntent.setData(KeyRings.buildGenericKeyRingUri(
Long.toString(signerMasterKeyId))
);
long signerMasterKeyId = providerHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mCertifierKeyId)).getMasterKeyId();
viewIntent.setData(KeyRings.buildGenericKeyRingUri(signerMasterKeyId));
startActivity(viewIntent);
} catch (ProviderHelper.NotFoundException e) {
} catch (PgpGeneralException e) {
// TODO notify user of this, maybe offer download?
Log.e(Constants.TAG, "key not found!", e);
}

View File

@ -326,10 +326,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
try {
Uri blobUri =
KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri);
mNfcKeyringBytes = mProviderHelper.getPGPKeyRing(
blobUri).getEncoded();
} catch (IOException e) {
Log.e(Constants.TAG, "Error parsing keyring", e);
mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData(
blobUri,
KeychainContract.KeyRingData.KEY_RING_DATA,
ProviderHelper.FIELD_TYPE_BLOB);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}

View File

@ -33,10 +33,10 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.TextView;
import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@ -227,19 +227,19 @@ public class ViewKeyCertsFragment extends LoaderFragment
wSignerKeyId.setText(signerKeyId);
switch (cursor.getInt(mIndexType)) {
case PGPSignature.DEFAULT_CERTIFICATION: // 0x10
case WrappedSignature.DEFAULT_CERTIFICATION: // 0x10
wSignStatus.setText(R.string.cert_default);
break;
case PGPSignature.NO_CERTIFICATION: // 0x11
case WrappedSignature.NO_CERTIFICATION: // 0x11
wSignStatus.setText(R.string.cert_none);
break;
case PGPSignature.CASUAL_CERTIFICATION: // 0x12
case WrappedSignature.CASUAL_CERTIFICATION: // 0x12
wSignStatus.setText(R.string.cert_casual);
break;
case PGPSignature.POSITIVE_CERTIFICATION: // 0x13
case WrappedSignature.POSITIVE_CERTIFICATION: // 0x13
wSignStatus.setText(R.string.cert_positive);
break;
case PGPSignature.CERTIFICATION_REVOCATION: // 0x30
case WrappedSignature.CERTIFICATION_REVOCATION: // 0x30
wSignStatus.setText(R.string.cert_revoke);
break;
}

View File

@ -32,7 +32,9 @@ import android.widget.ListView;
import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@ -235,14 +237,16 @@ public class ViewKeyMainFragment extends LoaderFragment implements
return;
}
try {
long keyId = new ProviderHelper(getActivity()).extractOrGetMasterKeyId(dataUri);
long keyId = new ProviderHelper(getActivity())
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId};
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
} catch (ProviderHelper.NotFoundException e) {
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}

View File

@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
@ -190,6 +191,9 @@ public class ViewKeyShareFragment extends LoaderFragment implements
}
startActivity(Intent.createChooser(sendIntent, title));
}
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "error processing key!", e);
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
} catch (IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();

View File

@ -32,6 +32,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Highlighter;
@ -83,7 +84,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
return mData;
}
public ArrayList<ImportKeysListEntry> getSelectedData() {
public ArrayList<ImportKeysListEntry> getSelectedEntries() {
ArrayList<ImportKeysListEntry> selectedData = new ArrayList<ImportKeysListEntry>();
for (ImportKeysListEntry entry : mData) {
if (entry.isSelected()) {

View File

@ -19,19 +19,19 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.util.LongSparseArray;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
@ -56,6 +56,7 @@ public class ImportKeysListLoader
final InputData mInputData;
ArrayList<ImportKeysListEntry> mData = new ArrayList<ImportKeysListEntry>();
LongSparseArray<ParcelableKeyRing> mParcelableRings = new LongSparseArray<ParcelableKeyRing>();
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
public ImportKeysListLoader(Context context, InputData inputData) {
@ -107,6 +108,10 @@ public class ImportKeysListLoader
super.deliverResult(data);
}
public LongSparseArray<ParcelableKeyRing> getParcelableRings() {
return mParcelableRings;
}
/**
* Reads all PGPKeyRing objects from input
*
@ -116,7 +121,6 @@ public class ImportKeysListLoader
private void generateListOfKeyrings(InputData inputData) {
boolean isEmpty = true;
int nonPgpCounter = 0;
PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream());
@ -129,28 +133,18 @@ public class ImportKeysListLoader
// read all available blocks... (asc files can contain many blocks with BEGIN END)
while (bufferedInput.available() > 0) {
isEmpty = false;
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
// go through all objects in this block
Object obj;
while ((obj = objectFactory.nextObject()) != null) {
Log.d(Constants.TAG, "Found class: " + obj.getClass());
if (obj instanceof PGPKeyRing) {
PGPKeyRing newKeyring = (PGPKeyRing) obj;
addToData(newKeyring);
} else {
Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
nonPgpCounter++;
}
// todo deal with non-keyring objects?
List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
for(UncachedKeyRing key : rings) {
ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);
mData.add(item);
mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded()));
isEmpty = false;
}
}
} catch (Exception e) {
Log.e(Constants.TAG, "Exception on parsing key file!", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
nonPgpCounter = 0;
}
if (isEmpty) {
@ -158,16 +152,6 @@ public class ImportKeysListLoader
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, new FileHasNoContent());
}
if (nonPgpCounter > 0) {
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, new NonPgpPart(nonPgpCounter));
}
}
private void addToData(PGPKeyRing keyring) {
ImportKeysListEntry item = new ImportKeysListEntry(getContext(), keyring);
mData.add(item);
}
}

View File

@ -41,17 +41,12 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.Log;
@ -106,8 +101,12 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
long secretKeyId) throws PgpGeneralException {
// check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
if (!PassphraseCacheService.hasPassphrase(context, secretKeyId)) {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
try {
if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
}
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException("Error: Key not found!", e);
}
}
@ -139,18 +138,24 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
alert.setTitle(R.string.title_authentication);
final PGPSecretKey secretKey;
final String userId;
final WrappedSecretKeyRing secretRing;
String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) {
secretKey = null;
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
secretRing = null;
} else {
try {
ProviderHelper helper = new ProviderHelper(activity);
secretKey = helper.getPGPSecretKeyRing(secretKeyId).getSecretKey();
userId = (String) helper.getUnifiedData(secretKeyId,
KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
secretRing = helper.getWrappedSecretKeyRing(secretKeyId);
// yes the inner try/catch block is necessary, otherwise the final variable
// above can't be statically verified to have been set in all cases because
// the catch clause doesn't return.
try {
userId = secretRing.getPrimaryUserId();
} catch (PgpGeneralException e) {
userId = null;
}
} catch (ProviderHelper.NotFoundException e) {
alert.setTitle(R.string.title_key_not_found);
alert.setMessage(getString(R.string.key_not_found, secretKeyId));
@ -179,76 +184,59 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
@Override
public void onClick(DialogInterface dialog, int id) {
dismiss();
long curKeyIndex = 1;
boolean keyOK = true;
String passphrase = mPassphraseEditText.getText().toString();
long keyId;
PGPSecretKey clickSecretKey = secretKey;
if (clickSecretKey != null) {
while (keyOK) {
if (clickSecretKey != null) { // check again for loop
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
passphrase.toCharArray());
PGPPrivateKey testKey = clickSecretKey
.extractPrivateKey(keyDecryptor);
if (testKey == null) {
if (!clickSecretKey.isMasterKey()) {
Toast.makeText(activity,
R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return;
} else {
try {
clickSecretKey = PgpKeyHelper.getKeyNum(new ProviderHelper(activity)
.getPGPSecretKeyRingWithKeyId(secretKeyId),
curKeyIndex
);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
curKeyIndex++; // does post-increment work like C?
continue;
}
} else {
keyOK = false;
}
} catch (PGPException e) {
Toast.makeText(activity, R.string.wrong_passphrase,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return;
}
} else {
Toast.makeText(activity, R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return; // ran out of keys to try
}
}
keyId = secretKey.getKeyID();
} else {
keyId = Constants.key.symmetric;
// Early breakout if we are dealing with a symmetric key
if (secretRing == null) {
PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric, passphrase);
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
sendMessageToHandler(MESSAGE_OKAY, data);
return;
}
WrappedSecretKey unlockedSecretKey = null;
for(WrappedSecretKey clickSecretKey : secretRing.iterator()) {
try {
boolean unlocked = clickSecretKey.unlock(passphrase);
if (unlocked) {
unlockedSecretKey = clickSecretKey;
break;
}
} catch (PgpGeneralException e) {
Toast.makeText(activity, R.string.error_could_not_extract_private_key,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return; // ran out of keys to try
}
}
// Means we got an exception every time
if (unlockedSecretKey == null) {
Toast.makeText(activity, R.string.wrong_passphrase,
Toast.LENGTH_SHORT).show();
sendMessageToHandler(MESSAGE_CANCEL);
return;
}
long masterKeyId = secretRing.getMasterKeyId();
// cache the new passphrase
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
PassphraseCacheService.addCachedPassphrase(activity, keyId, passphrase);
if (!keyOK && clickSecretKey.getKeyID() != keyId) {
PassphraseCacheService.addCachedPassphrase(activity, clickSecretKey.getKeyID(),
passphrase);
PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase);
if (unlockedSecretKey.getKeyId() != masterKeyId) {
PassphraseCacheService.addCachedPassphrase(
activity, unlockedSecretKey.getKeyId(), passphrase);
}
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
sendMessageToHandler(MESSAGE_OKAY, data);
}
});

View File

@ -38,22 +38,18 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Vector;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey;
private UncachedSecretKey mKey;
private EditorListener mEditorListener = null;
@ -208,7 +204,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
}
public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
public void setValue(UncachedSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key;
mIsMasterKey = isMasterKey;
@ -216,13 +212,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mDeleteButton.setVisibility(View.INVISIBLE);
}
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key));
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyID());
mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(getContext(), key.getAlgorithm()));
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(key.getKeyId());
mKeyId.setText(keyIdStr);
Vector<Choice> choices = new Vector<Choice>();
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
boolean isElGamalKey = (key.isElGamalEncrypt());
boolean isDSAKey = (key.isDSA());
if (isElGamalKey) {
mChkSign.setVisibility(View.INVISIBLE);
TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
@ -248,38 +243,45 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mIsNewKey = isNewKey;
if (isNewKey) {
mUsage = usage;
mChkCertify.setChecked((usage & KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER);
mChkSign.setChecked((usage & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA);
mChkEncrypt.setChecked(((usage & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) ||
((usage & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE));
mChkAuthenticate.setChecked((usage & KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION);
mChkCertify.setChecked(
(usage & UncachedSecretKey.CERTIFY_OTHER) == UncachedSecretKey.CERTIFY_OTHER);
mChkSign.setChecked(
(usage & UncachedSecretKey.SIGN_DATA) == UncachedSecretKey.SIGN_DATA);
mChkEncrypt.setChecked(
((usage & UncachedSecretKey.ENCRYPT_COMMS) == UncachedSecretKey.ENCRYPT_COMMS) ||
((usage & UncachedSecretKey.ENCRYPT_STORAGE) == UncachedSecretKey.ENCRYPT_STORAGE));
mChkAuthenticate.setChecked(
(usage & UncachedSecretKey.AUTHENTICATION) == UncachedSecretKey.AUTHENTICATION);
} else {
mUsage = PgpKeyHelper.getKeyUsage(key);
mUsage = key.getKeyUsage();
mOriginalUsage = mUsage;
if (key.isMasterKey()) {
mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key));
mChkCertify.setChecked(key.canCertify());
}
mChkSign.setChecked(PgpKeyHelper.isSigningKey(key));
mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key));
mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key));
mChkSign.setChecked(key.canSign());
mChkEncrypt.setChecked(key.canEncrypt());
mChkAuthenticate.setChecked(key.canAuthenticate());
}
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(PgpKeyHelper.getCreationDate(key));
setCreatedDate(cal);
cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
{
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(key.getCreationTime());
setCreatedDate(cal);
}
Date expiryDate = key.getExpiryTime();
if (expiryDate == null) {
setExpiryDate(null);
} else {
cal.setTime(PgpKeyHelper.getExpiryDate(key));
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
cal.setTime(expiryDate);
setExpiryDate(cal);
mOriginalExpiryDate = cal;
}
}
public PGPSecretKey getValue() {
public UncachedSecretKey getValue() {
return mKey;
}
@ -320,16 +322,16 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public int getUsage() {
mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) |
(mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0);
mUsage = (mUsage & ~KeyFlags.SIGN_DATA) |
(mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0);
mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) |
(mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0);
mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) |
(mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0);
mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) |
(mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0);
mUsage = (mUsage & ~UncachedSecretKey.CERTIFY_OTHER) |
(mChkCertify.isChecked() ? UncachedSecretKey.CERTIFY_OTHER : 0);
mUsage = (mUsage & ~UncachedSecretKey.SIGN_DATA) |
(mChkSign.isChecked() ? UncachedSecretKey.SIGN_DATA : 0);
mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_COMMS) |
(mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_COMMS : 0);
mUsage = (mUsage & ~UncachedSecretKey.ENCRYPT_STORAGE) |
(mChkEncrypt.isChecked() ? UncachedSecretKey.ENCRYPT_STORAGE : 0);
mUsage = (mUsage & ~UncachedSecretKey.AUTHENTICATION) |
(mChkAuthenticate.isChecked() ? UncachedSecretKey.AUTHENTICATION : 0);
return mUsage;
}

View File

@ -35,10 +35,9 @@ import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@ -63,7 +62,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
private int mNewKeySize;
private boolean mOldItemDeleted = false;
private ArrayList<String> mDeletedIDs = new ArrayList<String>();
private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
private ArrayList<UncachedSecretKey> mDeletedKeys = new ArrayList<UncachedSecretKey>();
private boolean mCanBeEdited = true;
private ActionBarActivity mActivity;
@ -227,7 +226,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
return mDeletedIDs;
}
public ArrayList<PGPSecretKey> getDeletedKeys() {
public ArrayList<UncachedSecretKey> getDeletedKeys() {
return mDeletedKeys;
}
@ -325,7 +324,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
this.updateEditorsVisible();
}
public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) {
public void setKeys(Vector<UncachedSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != TYPE_KEY) {
return;
}
@ -358,9 +357,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
String passphrase;
if (mEditors.getChildCount() > 0) {
PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
UncachedSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
passphrase = PassphraseCacheService
.getCachedPassphrase(mActivity, masterKey.getKeyID());
.getCachedPassphrase(mActivity, masterKey.getKeyId());
isMasterKey = false;
} else {
passphrase = "";
@ -395,7 +394,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
// get new key from data bundle returned from service
Bundle data = message.getData();
PGPSecretKey newKey = (PGPSecretKey) PgpConversionHelper
UncachedSecretKey newKey = PgpConversionHelper
.BytesToPGPSecretKey(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
addGeneratedKeyToView(newKey);
@ -413,14 +412,14 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mActivity.startService(intent);
}
private void addGeneratedKeyToView(PGPSecretKey newKey) {
private void addGeneratedKeyToView(UncachedSecretKey newKey) {
// add view with new key
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false);
view.setEditorListener(SectionView.this);
int usage = 0;
if (mEditors.getChildCount() == 0) {
usage = PGPKeyFlags.CAN_CERTIFY;
usage = UncachedSecretKey.CERTIFY_OTHER;
}
view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view);

View File

@ -517,6 +517,7 @@
<string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string>
<string name="can_sign_not">cannot sign</string>
<string name="error_encoding">Encoding error</string>
<string name="error_no_encrypt_subkey">No encryption subkey available!</string>
</resources>

View File

@ -1,46 +0,0 @@
package org.sufficientlysecure.keychain;
import org.junit.Before;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.sufficientlysecure.keychain.pgp.*;
import org.spongycastle.openpgp.*;
@RunWith(RobolectricGradleTestRunner.class)
public class PgpKeyOperationTest {
PGPSecretKey key;
@Before
public void setUp() throws Exception {
/* Input */
int algorithm = Id.choice.algorithm.dsa;
String passphrase = "swag";
int keysize = 2048;
boolean masterKey = true;
/* Operation */
PgpKeyOperation keyOperations = new PgpKeyOperation(null);
key = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);
System.err.println("initialized, test key: " + PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
}
@After
public void tearDown() {
}
@Test
public void createTest() {
}
@Test
public void certifyKey() {
System.err.println("swag");
}
}

View File

@ -1,23 +0,0 @@
package org.sufficientlysecure.keychain;
import org.junit.runners.model.InitializationError;
import org.robolectric.AndroidManifest;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.res.Fs;
import org.robolectric.res.FsFile;
import org.sufficientlysecure.keychain.KeychainApplication;
public class RobolectricGradleTestRunner extends RobolectricTestRunner {
public RobolectricGradleTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override protected AndroidManifest getAppManifest(Config config) {
String myAppPath = KeychainApplication.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String manifestPath = myAppPath + "../../../src/main/AndroidManifest.xml";
return createAppManifest(Fs.fileFromPath(manifestPath));
}
}

View File

@ -5,6 +5,7 @@ buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:0.10.0'
classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.9.+'
}
}

2
extern/spongycastle vendored

@ -1 +1 @@
Subproject commit eb2c35bd0602d05a65c10c86b9c9834ebd1a81c6
Subproject commit 2c47e5fca2a820a4fd584066871bed993f1c3919