mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-11 03:25:05 -05:00
Merge branch 'master' into automatic-contact-discovery
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
This commit is contained in:
commit
cae0071342
@ -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
|
||||
|
@ -1,13 +1,17 @@
|
||||
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'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
|
||||
|
||||
compile 'com.android.support:support-v4:19.1.0'
|
||||
compile 'com.android.support:appcompat-v7:19.1.0'
|
||||
compile 'dnsjava:dnsjava:2.1.1'
|
||||
@ -24,24 +28,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.3'
|
||||
androidTestCompile 'com.squareup:fest-android:1.0.8'
|
||||
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 {
|
||||
@ -93,20 +98,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")) {
|
||||
@ -114,5 +105,3 @@ tasks.whenTaskAdded { task ->
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: tests disabled!
|
||||
//check.dependsOn localTest
|
||||
|
12
OpenKeychain/src/androidTest/java/tests/SomeTest.java
Normal file
12
OpenKeychain/src/androidTest/java/tests/SomeTest.java
Normal 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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -57,8 +49,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
|
||||
|
||||
private boolean mSelected;
|
||||
|
||||
private byte[] mBytes = new byte[]{};
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
@ -77,8 +67,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);
|
||||
dest.writeString(mOrigin);
|
||||
}
|
||||
@ -99,8 +87,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();
|
||||
vr.mOrigin = source.readString();
|
||||
|
||||
@ -116,14 +102,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;
|
||||
}
|
||||
@ -255,53 +233,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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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!
|
||||
*/
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -27,4 +27,7 @@ public class PgpGeneralException extends Exception {
|
||||
public PgpGeneralException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
public PgpGeneralException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -26,14 +26,6 @@ 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;
|
||||
@ -41,15 +33,19 @@ import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||
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;
|
||||
@ -61,7 +57,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;
|
||||
@ -498,7 +493,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;
|
||||
@ -511,33 +506,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);
|
||||
@ -557,13 +555,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");
|
||||
|
||||
@ -576,7 +572,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 */
|
||||
@ -589,24 +584,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);
|
||||
|
||||
@ -614,10 +613,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");
|
||||
@ -649,12 +646,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) {
|
||||
@ -728,15 +723,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);
|
||||
@ -746,9 +738,11 @@ public class KeychainIntentService extends IntentService
|
||||
} else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action) || ACTION_IMPORT_KEYBASE_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);
|
||||
|
||||
ArrayList<ParcelableKeyRing> keyRings = new ArrayList<ParcelableKeyRing>(entries.size());
|
||||
for (ImportKeysListEntry entry : entries) {
|
||||
|
||||
Keyserver server;
|
||||
@ -770,49 +764,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.equalsIgnoreCase(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);
|
||||
|
||||
@ -839,29 +799,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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Developers APG 1.x</h2>
|
||||
<ul>
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Getting started</h2>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
|
||||
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
17
OpenKeychain/src/main/res/raw-ar/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-ar/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
|
||||
|
||||
<h2>Support in OpenKeychain</h2>
|
||||
<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
|
||||
|
||||
<h2>Trust Model</h2>
|
||||
<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
|
||||
|
||||
<h2>Certifying keys</h2>
|
||||
<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Developers APG 1.x</h2>
|
||||
<ul>
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Getting started</h2>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
|
||||
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
17
OpenKeychain/src/main/res/raw-cs/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-cs/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
|
||||
|
||||
<h2>Support in OpenKeychain</h2>
|
||||
<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
|
||||
|
||||
<h2>Trust Model</h2>
|
||||
<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
|
||||
|
||||
<h2>Certifying keys</h2>
|
||||
<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,11 +19,12 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Entwickler APG 1.x</h2>
|
||||
<ul>
|
||||
<li>Thialfihar (Hauptentwickler)</li>
|
||||
<li>'Senecaso' (QR-Code, Schlüssel signtieren, Schlüssel hochladen)</li>
|
||||
<li>'Senecaso' (QR-Code, Schlüssel signieren, Schlüssel hochladen)</li>
|
||||
<li>Markus Doits</li>
|
||||
</ul>
|
||||
<h2>Bibliotheken</h2>
|
||||
|
@ -3,11 +3,11 @@
|
||||
<body>
|
||||
<h2>2.7</h2>
|
||||
<ul>
|
||||
<li>Purple! (Dominik, Vincent)</li>
|
||||
<li>New key view design (Dominik, Vincent)</li>
|
||||
<li>New flat Android buttons (Dominik, Vincent)</li>
|
||||
<li>API fixes (Dominik)</li>
|
||||
<li>Keybase.io import (Tim Bray)</li>
|
||||
<li>Lila! (Dominik, Vincent)</li>
|
||||
<li>Neues Schlüsselansicht Design (Dominik, Vincent)</li>
|
||||
<li>Neue flache Android Buttons (Dominik, Vincent)</li>
|
||||
<li>API Fehler behoben (Dominik)</li>
|
||||
<li>keybase.io importiert (Tim Bray)</li>
|
||||
</ul>
|
||||
<h2>2.6.1</h2>
|
||||
<ul>
|
||||
@ -15,51 +15,51 @@
|
||||
</ul>
|
||||
<h2>2.6</h2>
|
||||
<ul>
|
||||
<li>key certifications (thanks to Vincent Breitmoser)</li>
|
||||
<li>support for GnuPG partial secret keys (thanks to Vincent Breitmoser)</li>
|
||||
<li>new design for signature verification</li>
|
||||
<li>custom key length (thanks to Greg Witczak)</li>
|
||||
<li>fix share-functionality from other apps</li>
|
||||
<li>Schlüsselzertifikation (Dank an Vincent Breitmoser)</li>
|
||||
<li>Unterstützung für GnuPG teilweisegeheime Schlüssel (Dank an Vincent Breitmoser)</li>
|
||||
<li>Neues Design für Signaturverifikation</li>
|
||||
<li>Nutzerdefinierte Schlüssellänge (Dank an Greg Witczak)</li>
|
||||
<li>Fehler behoben bei der Teilen-Funktion von anderen Apps</li>
|
||||
</ul>
|
||||
<h2>2.5</h2>
|
||||
<ul>
|
||||
<li>fix decryption of symmetric pgp messages/files</li>
|
||||
<li>refactored edit key screen (thanks to Ash Hughes)</li>
|
||||
<li>new modern design for encrypt/decrypt screens</li>
|
||||
<li>OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)</li>
|
||||
<li>behoben: entschlüsseln von symetrischen pgp Nachrichten/Dateien</li>
|
||||
<li>umgestalteter Schlüssel bearbeiten Bildschirm (Dank an Ash Hughes)</li>
|
||||
<li>neues modernes Design für verschlüsselung/entschlüsselungs Bildschirme</li>
|
||||
<li>OpenPGP API Version 3 (mehrfache api accounts, interne fehlerbehebungen, schlüssel suche)</li>
|
||||
</ul>
|
||||
<h2>2.4</h2>
|
||||
<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
|
||||
Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
|
||||
<p>Danke an alle Bewerber beim Google Summer of Code 2014, die diese Version funktionsreich und fehlerfrei gemacht haben.
|
||||
Neben mehreren kleinen Updates sind eine beachtliche Anzahl von Updates von den folgenden Personen gemacht worden (in alphabetischer Reihenfolge):
|
||||
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
|
||||
<ul>
|
||||
<li>new unified key list</li>
|
||||
<li>colorized key fingerprint</li>
|
||||
<li>support for keyserver ports</li>
|
||||
<li>deactivate possibility to generate weak keys</li>
|
||||
<li>much more internal work on the API</li>
|
||||
<li>certify user ids</li>
|
||||
<li>keyserver query based on machine-readable output</li>
|
||||
<li>neue vereinheitlichte Schlüssel liste.</li>
|
||||
<li>eingefärbte Schlüssel fingerprints</li>
|
||||
<li>Unterstützung für Schlüsselserver </li>
|
||||
<li>deaktiviert die Möglichkeit schwache Schlüssel zu generieren.</li>
|
||||
<li>viel mehr interne Arbeit an der API</li>
|
||||
<li>zertifizieren von Benutzer IDs</li>
|
||||
<li>Schlüsselserver Anfragen basieren auf Maschinenlesbaren Ausgaben.</li>
|
||||
<li>lock navigation drawer on tablets</li>
|
||||
<li>suggestions for emails on creation of keys</li>
|
||||
<li>search in public key lists</li>
|
||||
<li>and much more improvements and fixes…</li>
|
||||
<li>suchen in öffentlichen Schlüssellisten</li>
|
||||
<li>und viele weitere Verbesserungen und Fehlerbehebungen...</li>
|
||||
</ul>
|
||||
<h2>2.3.1</h2>
|
||||
<ul>
|
||||
<li>hotfix for crash when upgrading from old versions</li>
|
||||
<li>schnelle Fehlerbehebung für Abstürze sobald man von einer alten Version geupdatet hat.</li>
|
||||
</ul>
|
||||
<h2>2.3</h2>
|
||||
<ul>
|
||||
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
|
||||
<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
|
||||
<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
|
||||
<li>querying keyservers directly from the import screen</li>
|
||||
<li>fix layout and dialog style on Android 2.2-3.0</li>
|
||||
<li>entfernung von unnötigen exporten von öffentlichen Schlüsseln, wenn man den geheimen Schlüssel exportiert. (Dank an Ash Hughes)</li>
|
||||
<li>Fehlerbehebung: Einstellen des Ablaufdatums von Schlüsseln (Dank an Ash Hughes)</li>
|
||||
<li>mehr interne Fehlerbehebungen wenn man Schlüssel bearbeitet (Dank an Ash Hughes)</li>
|
||||
<li>Suchzugriff auf die Schlüsselserver direkt vom Importieren Bildschirm</li>
|
||||
<li>Fehlerbehebung beim Layout und Dialogstil auf Android 2.2-3.0</li>
|
||||
<li>Absturz bei leeren Nutzer IDs behoben </li>
|
||||
<li>fix crash and empty lists when coming back from signing screen</li>
|
||||
<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
|
||||
<li>fix upload of key from signing screen</li>
|
||||
<li>Fehlerbehebung von Abstürzen und leeren Listen, wenn man vom signieren Bildschirm zurückkehrt.</li>
|
||||
<li>Bouncy Castle (Kryptographie Bibliothek) upgedatet von 1.47 auf 1.50 und vom Quellcode kompiliert.</li>
|
||||
<li>Fehlerbehebung vom hochladen von Schlüsseln vom signieren Bildschirm.</li>
|
||||
</ul>
|
||||
<h2>2.2</h2>
|
||||
<ul>
|
||||
@ -67,15 +67,15 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
|
||||
<li>Neus Design für die Liste der öffentlichen Schlüssel</li>
|
||||
<li>Neue Ansicht für öffentliche Schlüssel</li>
|
||||
<li>Fehler beim Schlüsselimport behoben</li>
|
||||
<li>key cross-certification (thanks to Ash Hughes)</li>
|
||||
<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
|
||||
<li>Schlüssel Cross-Zertifizierung (Dank an Ash Hughes)</li>
|
||||
<li>richtiges händling von UTF-8 Passwörtern (Danke an Ash Hughes)</li>
|
||||
<li>Erste Version mit neuen Sprachen (Danke an die Mitwirkenden bei Transifex)</li>
|
||||
<li>sharing of keys via QR Codes fixed and improved</li>
|
||||
<li>package signature verification for API</li>
|
||||
<li>teilen von schlüsseln per QR-Codes behoben und verbessert</li>
|
||||
<li>paket signatur verifizierung für die API</li>
|
||||
</ul>
|
||||
<h2>2.1.1</h2>
|
||||
<ul>
|
||||
<li>API Updates, preparation for K-9 Mail integration</li>
|
||||
<li>API Updates, vorbereitung für die K-9 Mail integration.</li>
|
||||
</ul>
|
||||
<h2>2.1</h2>
|
||||
<ul>
|
||||
@ -103,7 +103,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
|
||||
</ul>
|
||||
<h2>1.0.7</h2>
|
||||
<ul>
|
||||
<li>fixed problem with signature verification of texts with trailing newline</li>
|
||||
<li>Fehlerbehebung bei Problemen mit der Signatur verifizierung von Texten mit folgender neuer Zeile.</li>
|
||||
<li>weitere Optionen für die Time-to-live des Passphrasencaches (20, 40, 60 mins)</li>
|
||||
</ul>
|
||||
<h2>1.0.6</h2>
|
||||
@ -126,7 +126,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
|
||||
</ul>
|
||||
<h2>1.0.4</h2>
|
||||
<ul>
|
||||
<li>fixed another crash caused by some SDK bug with query builder</li>
|
||||
<li>Einen anderen crash behoben, verursacht von irgendeinen SDK bug mit dem query builder.</li>
|
||||
</ul>
|
||||
<h2>1.0.3</h2>
|
||||
<ul>
|
||||
@ -135,13 +135,13 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
|
||||
<h2>1.0.2</h2>
|
||||
<ul>
|
||||
<li>Filterbare Schlüsselliste</li>
|
||||
<li>smarter pre-selection of encryption keys</li>
|
||||
<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
|
||||
<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
|
||||
<li>leichtere vorauswahl von Verschlüsselungsschlüsseln</li>
|
||||
<li>neues Intent händling für VIEW und SEND, erlaubt es Dateien zu ver-/entschlüsseln außerhalb des Dateimanagers.</li>
|
||||
<li>Fehlerbehebungen und Extras (Schlüssel vorauswahl) für K-9 Mail, neue Beta Versionen verfügbar.</li>
|
||||
</ul>
|
||||
<h2>1.0.1</h2>
|
||||
<ul>
|
||||
<li>GMail account listing was broken in 1.0.0, fixed again</li>
|
||||
<li>GMail Account auflistung war nicht in Ordnung in 1.0.0, wieder behoben.</li>
|
||||
</ul>
|
||||
<h2>1.0.0</h2>
|
||||
<ul>
|
||||
@ -149,7 +149,7 @@ Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Pa
|
||||
<li>Unterstützung von mehr Filemanagern (einschließlich ASTRO)</li>
|
||||
<li>Slowenische Übersetzung</li>
|
||||
<li>Neue Datenbank, viel schneller, weniger Speicherbedarf</li>
|
||||
<li>defined Intents and content provider for other apps</li>
|
||||
<li>definierte intents und content provider für andere Apps</li>
|
||||
<li>Fehlerbehebungen</li>
|
||||
</ul>
|
||||
</body>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Wie werden Schlüssel empfangen</h2>
|
||||
<h2>Wie kann man Schlüssel empfangen</h2>
|
||||
<ol>
|
||||
<li>Gehen Sie zu Ihren Kontakten und öffnen Sie den Kontakt, den Sie teilen wollen.</li>
|
||||
<li>Halten sie die zwei Geräte Rückseitig aneinander (sie müssen sich fast berühren) und sie werden ein Vibrieren fühlen.</li>
|
||||
<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
|
||||
<li>Tippen sie die Karte an und der Inhalt wird dann auf Ihr Gerät geladen.</li>
|
||||
<li>Gehen Sie zu den Kontakten ihres Bekannten und öffnen Sie den Kontakt, den Sie teilen möchten.</li>
|
||||
<li>Halten Sie die zwei Geräte rückseitig aneinander (sodass sie sich fast berühren) und es wird vibrieren</li>
|
||||
<li>Nachdem es vibriert sehen sie wie sich der Inhalt des Gerätes ihres Bekannten von einer Warp-Geschwindigkeit Animation umgeben wird</li>
|
||||
<li>Wenn sie die Karte antippen, wird der Inhalt auf ihr Gerät übertragen</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
|
22
OpenKeychain/src/main/res/raw-de/help_start.html
Normal file
22
OpenKeychain/src/main/res/raw-de/help_start.html
Normal file
@ -0,0 +1,22 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Los gehts</h2>
|
||||
<p>Zuerst benötigen Sie einen geheimen privaten Schlüssel. Erstellen Sie einen Schlüssel über den Eintrag "Schlüssel" im Menu oder importieren Sie bestehende private Schlüssel. Anschließend können Sie die Schlüssel ihrer Freunde runterladen oder Sie über QR-Codes oder NFC austauschen.</p>
|
||||
|
||||
<p>Es wird empfohlen, dass Sie den <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> für ein verbessertes Dateihandling installieren, sowie den <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> installieren um erstellte QR-Codes zu scannen. Die Links führen entweder zu Google Play oder zu F-Droid zur Installation.</p>
|
||||
|
||||
<h2>Anwendungen</h2>
|
||||
<p>Etliche Anwendungen unterstützen OpenKeychain um private Kommunikation zu verschlüsseln/signieren:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychainUnterstützung in der aktuellenen <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha Version</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App zum senden von PGP-signierten Anfragen an einen Server um etwas zu öffnen oder zu schliessen, z.B. eine Tür.</p>
|
||||
|
||||
<h2>Ich habe einen Fehler in der OpenKeychain gefunden!</h2>
|
||||
<p>Bitte berichten Sie Bugs mithilfe des <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">Issue Trackers der OpenKeychain</a>.</p>
|
||||
|
||||
<h2>Unterstützen</h2>
|
||||
<p>Wenn Sie uns bei der Entwickelung von OpenKeychain durch das Beisteuern von Code helfen wollt, <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">schaut euch unsere kurze Anleitung auf Github an</a>.</p>
|
||||
|
||||
<h2>Übersetzungen</h2>
|
||||
<p>Hilf mit OpenKeychain zu übersetzten! Jeder kann auf <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain auf Transifex</a> mitmachen.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
17
OpenKeychain/src/main/res/raw-de/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-de/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>Das Web of Trust beschreibt den Teil von PGP der sich mit der Erstellung und dem Verwalten von Zertifikaten beschäftigt. Nutzer können so im Auge behalten zu wem ein bestimmter Public Key gehört, sowie diese Information teilen: Um die Privatsphäre von verschlüsselter Kommunikation zu gewährleisten ist es essentiell zu wissen ob der Public Key den man zum Verschlüsseln nutzt zu der Person gehört, die man erwartet.</p>
|
||||
|
||||
<h2>Support in OpenKeychain</h2>
|
||||
<p>Es gibt nur eine rudimentäre Unterstützung für das Web of Trust in OpenKeychain. Am Web of Trust System wird kontinuierlich gearbeitet und es wird in den kommenden Version überarbeitet.</p>
|
||||
|
||||
<h2>Vertrauens-Modell</h2>
|
||||
<p>Die Bewertung des Vertrauens basiert auf der Grundannahme, dass alle Schlüssel zu denen ein privater Schlüssel vorhanden ist, vertrauenswürdig sind. Öffentliche Schlüssel welche mindestens eine User ID beinhalten, die von einem Trusted Key verifiziert wurden, werden mit einem grünen Punkt in der Schlüsselliste gekennzeichnet. Es ist (noch) nicht möglich bestimmte Trust Level für Zertifikate anderer bekannter öffentlicher Schlüssel festzulegen.</p>
|
||||
|
||||
<h2>Schlüssel beglaubigen</h2>
|
||||
<p>Die Unterstützung für die Zertifizierung von Schlüsseln ist vorhanden. Nutzer IDs können separat zertifiziert werden. Es ist jedoch sowohl noch nicht möglich das Vertrauenslevel festzulegen, als auch lokale oder andere spezielle Zertifikate zu erstellen.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -2,10 +2,10 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<ol>
|
||||
<li>Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.</li>
|
||||
<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
|
||||
<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
|
||||
<li>Tap the card and the content will then load on the other person’s device.</li>
|
||||
<li>Stellen Sie sicher, dass NFC und Android Beam in den Einstellungen unter > Mehr > NFC eingeschaltet sind</li>
|
||||
<li>Halten sie die zwei Geräte rückseitig aneinander (sodass sie sich fast berühren) und es wird vibrieren</li>
|
||||
<li>Nachdem es vibriert sehen sie wie der Inhalt ihres Gerätes von einer Warp-Geschwindigkeit Animation umgeben wird</li>
|
||||
<li>Drücken Sie auf die Karte und der Inhalt wird auf das Gerät des Anderen geladen.</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Developers APG 1.x</h2>
|
||||
<ul>
|
||||
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Getting started</h2>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
|
||||
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
||||
|
17
OpenKeychain/src/main/res/raw-el/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-el/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
|
||||
|
||||
<h2>Support in OpenKeychain</h2>
|
||||
<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
|
||||
|
||||
<h2>Trust Model</h2>
|
||||
<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
|
||||
|
||||
<h2>Certifying keys</h2>
|
||||
<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Desarrolladores de APG 1.x</h2>
|
||||
<ul>
|
||||
|
@ -4,10 +4,10 @@
|
||||
<h2>2.7</h2>
|
||||
<ul>
|
||||
<li>Purple! (Dominik, Vincent)</li>
|
||||
<li>New key view design (Dominik, Vincent)</li>
|
||||
<li>New flat Android buttons (Dominik, Vincent)</li>
|
||||
<li>API fixes (Dominik)</li>
|
||||
<li>Keybase.io import (Tim Bray)</li>
|
||||
<li>Diseño de vista de nueva clave (Dominik, Vincent)</li>
|
||||
<li>Nuevos botones de Android planos (Dominik, Vincent)</li>
|
||||
<li>Reparaciones de la API (Dominik)</li>
|
||||
<li>Importación de Keybase.io (Tim Bray)</li>
|
||||
</ul>
|
||||
<h2>2.6.1</h2>
|
||||
<ul>
|
||||
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Primeros pasos</h2>
|
||||
<p>Primero necesita una clave privada (secreta) personal. Cree una mediante las opciones de menú en "Claves", o importe claves privadas existentes. En adelante puede descargar las claves de sus amigos, o intercambiarlas mediante códigos QR o vía NFC.</p>
|
||||
<p>Primero necesita una clave privada (secreta) personal. Cree una mediante las opciones de menú en "Claves", o importe claves privadas existentes. En adelante puede descargar las claves de sus amigos, o intercambiarlas mediante códigos QR o vía NFC.</p>
|
||||
|
||||
<p>Es recomendable que instales <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> para una mejor selección de archivos y <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> para escanear los códigos QR generados. Pulsando en los enlaces se abrirá Google Play o F-Droid.</p>
|
||||
|
||||
<h2>Aplicaciones</h2>
|
||||
<p>Varias aplicaciones soportan OpenKeychain para cifrar/firmar sus comunicaciones privadas:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversaciones</a>: Cliente Jabber/XMPP</p>
|
||||
<p><img src="apps_k9">K-9 Mail: ¡Soporte para OpenKeychain disponible en la actual <a href="https://github.com/k9mail/k-9/releases/tag/4.904">versión alfa</a>!</p>
|
||||
<p>Varias aplicaciones soportan OpenKeychain para cifrar/firmar sus comunicaciones privadas:<br><img src="apps_k9"><br>K-9 Mail: Soporte para OpenKeychain disponible en la actual ¡<a href="https://github.com/k9mail/k-9/releases/tag/4.904">versión alfa</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversaciones</a>: Cliente Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: Aplicación para enviar una petición firmada-con-PGP a un servidor para abrir o cerrar algo, ej: una puerta</p>
|
||||
|
||||
<h2>¡He encontrado un bug en OpenKeychain!</h2>
|
||||
<p>Por favor, informa de errores usando el <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">seguimiento de incidencias de OpenKeychain</a>.</p>
|
||||
|
17
OpenKeychain/src/main/res/raw-es/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-es/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>El Web of Trust (web de confianza) describe la parte de PGP que trata con la creación y catalogación de certificaciones. Proporciona mecanismos para ayudar al usuario a llevar un seguimiento de a quién pertenece una clave pública, y comparte esta información con otros. Para asegurar la privacidad de las comunicaciones cifradas, es esencial saber que la clave pública que usted cifra pertenece a la misma persona que usted cree.</p>
|
||||
|
||||
<h2>Soporte en OpenKeychain</h2>
|
||||
<p>Sólo hay soporte básico para Web of Trust (web de confianza) en OpenKeychain. Este es un duro trabajo que tenemos en marcha sujeto a cambios en versiones posteriores.</p>
|
||||
|
||||
<h2>Modelo de confianza</h2>
|
||||
<p>La evaluación de la confianza está basada en la simple asunción de que todas las claves que tienen disponibles claves privadas (secretas) son de confianza. Las claves públicas que contienen al menos una identificación de usuario certificada por una clave de confianza serán marcadas con un punto verde en los listados de claves. No es posible (aún) especificar niveles de confianza para certificados de otras claves públicas conocidas.</p>
|
||||
|
||||
<h2>Certificando claves</h2>
|
||||
<p>Está disponible el soporte para certificación de clave, y las identificaciones de usuario pueden ser certificadas individualmente. No es posible aún especificar el nivel de confianza o crear certificados locales y otros tipos especiales de certificados.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Developers APG 1.x</h2>
|
||||
<ul>
|
||||
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Getting started</h2>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
|
||||
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
||||
|
17
OpenKeychain/src/main/res/raw-et/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-et/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
|
||||
|
||||
<h2>Support in OpenKeychain</h2>
|
||||
<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
|
||||
|
||||
<h2>Trust Model</h2>
|
||||
<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
|
||||
|
||||
<h2>Certifying keys</h2>
|
||||
<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,24 +0,0 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>شروع کار</h2>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
||||
|
||||
<h2>هم بخشی کردن</h2>
|
||||
<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
|
||||
|
||||
<h2>ترجمه ها</h2>
|
||||
<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Les développeurs d'APG 1.x</h2>
|
||||
<ul>
|
||||
|
@ -3,11 +3,11 @@
|
||||
<body>
|
||||
<h2>2.7</h2>
|
||||
<ul>
|
||||
<li>Purple! (Dominik, Vincent)</li>
|
||||
<li>New key view design (Dominik, Vincent)</li>
|
||||
<li>New flat Android buttons (Dominik, Vincent)</li>
|
||||
<li>API fixes (Dominik)</li>
|
||||
<li>Keybase.io import (Tim Bray)</li>
|
||||
<li>Violet ! (Dominik, Vincent)</li>
|
||||
<li>Nouvelle présentation de la visualisation des clefs (Dominik, Vincent)</li>
|
||||
<li>Nouveaux boutons Android plats (Dominik, Vincent)</li>
|
||||
<li>Correctifs de l'API (Dominik)</li>
|
||||
<li>Importation de Keybase.io (Tim Bray)</li>
|
||||
</ul>
|
||||
<h2>2.6.1</h2>
|
||||
<ul>
|
||||
|
@ -4,12 +4,10 @@
|
||||
<h2>Commencer</h2>
|
||||
<p>Il vous faut d'abord une clef personnelle secrète. Créez-en une avec l'option du menu dans « Clefs » ou importer des clefs secrètes existantes. Ensuite vous pourrez télécharger les clefs de vos amis ou les échanger avec des codes QR ou par la NFC.</p>
|
||||
|
||||
<p>Il vous est recommendé d'installer le <a href="market://details?id=org.openintents.filemanager">gestionnaire de fichiers OI</a> pour sa fonction améliorée de séléction des fichiers et le <a href="market://details?id=com.google.zxing.client.android">lecteur de codes à barres</a> pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.</p>
|
||||
<p>Il vous est recommandé d'installer le <a href="market://details?id=org.openintents.filemanager">gestionnaire de fichiers OI</a> pour sa fonction améliorée de sélection des fichiers et le <a href="market://details?id=com.google.zxing.client.android">lecteur de codes à barres</a> pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Plusieurs applications prennent en charge OpenKeychain pour chiffrer/signer vos communications privées :</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a> : un client Jabber/XMPP</p>
|
||||
<p><img src="apps_k9">K-9 Mail : la prise en charge d'OpenKeychain est disponible dans la <a href="https://github.com/k9mail/k-9/releases/tag/4.904">version alpha</a> actuelle !</p>
|
||||
<p>Plusieurs applications prennent en charge OpenKeychain pour chiffrer/signer vos communications privées :<br><img src="apps_k9"><br>K-9 Mail : OpenKeychain est pris en charge dans la <a href="https://github.com/k9mail/k-9/releases/tag/4.904">version alpha</a>actuelle !<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a> : clilent Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a> : appli pour envoyer une demande signée par PGP à un serveur pour ouvrir ou fermer quelque chose, par exemple une porte</p>
|
||||
|
||||
<h2>J'ai trouvé un bogue dans OpenKeychain !</h2>
|
||||
<p>Veuillez rapporter le bogue en utilisant le <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">gestionnaire de bogue d'OpenKeychain</a>.</p>
|
||||
|
17
OpenKeychain/src/main/res/raw-fr/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-fr/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Toile confiance</h2>
|
||||
<p>La toile de confiance décrit la partie de PGP qui s'occupe de la création et du suivi des certifications. Elle fournit des mécanismes pour aider l'utilisateur à suivre à qui appartient une clef publique, et partager cette information avec les autres. Pour assurer la confidentialité d'une communication chiffrée, il est essentiel de savoir que la clef publique vers laquelle vous chiffrez appartient à la personne à qui vous croyez qu'elle appartient.</p>
|
||||
|
||||
<h2>Prise en charge dans OpenKeychain</h2>
|
||||
<p>OpenKeychain offre seulement une prise en charge de base de la toile de confiance. Un travail important est en cours et ceci pourrait changer dans les versions à venir.</p>
|
||||
|
||||
<h2>Modèle de confiance</h2>
|
||||
<p>L'évaluation de la confiance est fondée sur la simple supposition que toutes les clefs ayant des clefs secrètes disponibles sont de confiance. Les clefs publiques contenant au moins un ID d'utilisateur certifié par une clef de confiance seront marquées par un point vert dans le listage des clefs. Il n'est pas (encore) possible de spécifier les niveaux de confiance pour les certificats d'autres clefs publiques inconnues.</p>
|
||||
|
||||
<h2>Certifications des clefs</h2>
|
||||
<p>La prise en charge de la certification des clefs est disponible et les ID d'utilisateur peuvent être certifiées individuellement. Il n'est pas encore possible de spécifier le niveau de confiance ou de créer des certificats locaux et d'autres types spéciaux.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Sviluppatori APG 1.x</h2>
|
||||
<ul>
|
@ -3,11 +3,11 @@
|
||||
<body>
|
||||
<h2>2.7</h2>
|
||||
<ul>
|
||||
<li>Purple! (Dominik, Vincent)</li>
|
||||
<li>New key view design (Dominik, Vincent)</li>
|
||||
<li>New flat Android buttons (Dominik, Vincent)</li>
|
||||
<li>API fixes (Dominik)</li>
|
||||
<li>Keybase.io import (Tim Bray)</li>
|
||||
<li>Porpora! (Dominik, Vincent)</li>
|
||||
<li>Nuovo design della schermata chiavi (Dominik, Vincent)</li>
|
||||
<li>Nuovi pulsanti piatti Android (Dominik, Vincent)</li>
|
||||
<li>Correzioni API (Dominik)</li>
|
||||
<li>Importazione Keybase.io (Tim Bray)</li>
|
||||
</ul>
|
||||
<h2>2.6.1</h2>
|
||||
<ul>
|
||||
@ -31,7 +31,7 @@
|
||||
<h2>2.4</h2>
|
||||
<p>Grazie a tutti i partecipanti di Google Summer of Code 2014 che hanno reso questo rilascio ricco di caratteristiche e privo di bug!
|
||||
Oltre a numerose piccole correzioni, un notevole numero di patch sono state fatte dalle seguenti persone (in ordine alfabetico):
|
||||
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paolo Sarbinowski, Sreeram Boyapati, Vincent Breitmoser. </p>
|
||||
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paolo Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
|
||||
<ul>
|
||||
<li>nuova lista chiave unificata</li>
|
||||
<li>impronta chiave colorata</li>
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Per iniziare</h2>
|
||||
<p>In primo luogo è necessaria una chiave segreta personale. Creane una tramite il menu opzioni sotto la voce "Chiavi" o importa chiavi segrete già esistenti. Successivamente, è possibile scaricare le chiavi dei vostri amici o scambiarle con i codici QR o NFC.</p>
|
||||
<p>In primo luogo è necessaria una chiave segreta personale. Creane una tramite il menu opzioni sotto la voce "Chiavi" o importa chiavi segrete già esistenti. Successivamente, è possibile scaricare le chiavi dei vostri amici o scambiarle con i codici QR o NFC.</p>
|
||||
|
||||
<p>Si raccomanda di installare <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> per una migliore selezione dei file e <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> per scansionare i codici QR. I collegamenti verranno aperti in Google Play Store o F-Droid per l'installazione.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<h2>Applicazioni</h2>
|
||||
<p>Diverse applicazioni supportano OpenKeychain per codificare/firmare le tue comunicazioni private:<br><img src="apps_k9"><br>K-9 Mail: Supporto per OpenKeychain disponibile nella attuale <a href="https://github.com/k9mail/k-9/releases/tag/4.904">versione alpha</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Client Jabber/XMPP<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>App per inviare una richiesta firmata PGP a un server per aprire o chiudere qualcosa, ad esempio, una porta</a></p>
|
||||
|
||||
<h2>Ho trovato un bug in OpenKeychain!</h2>
|
||||
<p>Per favore riporta i bug usando il <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker di OpenKeychain</a>.</p>
|
17
OpenKeychain/src/main/res/raw-it/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-it/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Rete di Fiducia</h2>
|
||||
<p>La Rete di Fiducia descrive la parte del PGP che si occupa di creazione e gestione delle certificazioni. Essa fornisce meccanismi per aiutare l'utente a tenere traccia di chi appartiene una chiave pubblica, e condividere queste informazioni con gli altri; Per garantire la privacy della comunicazione crittografata, è essenziale sapere che la chiave pubblica per crittografare appartiene alla persona che si pensa.</p>
|
||||
|
||||
<h2>Supporto in OpenKeychain</h2>
|
||||
<p>Esiste solo un supporto di base per le Reti di Fiducia in OpenKeychain. Questo è un grosso lavoro in corso e soggetto a modifiche nei prossimi rilasci.</p>
|
||||
|
||||
<h2>Modello di Fiducia</h2>
|
||||
<p>La valutazione di fiducia si basa sul semplice presupposto che tutte le chiavi che hanno chiavi segrete disponibili sono attendibili. Le chiavi pubbliche che contengono almeno un id utente certificato da una chiave di fiducia saranno contrassegnati con un punto verde negli elenchi principali. Non è (ancora) possibile specificare livelli di attendibilità per i certificati di altre chiavi pubbliche conosciute.</p>
|
||||
|
||||
<h2>Chiavi di certificazione</h2>
|
||||
<p>Il supporto per la certificazione chiave è disponibile, e gli id utenti possono essere certificati singolarmente. Non è ancora possibile specificare il livello di fiducia o creare certificati locali e altro di tipo speciale.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>APG 1.xの開発者達</h2>
|
||||
<ul>
|
||||
|
@ -4,10 +4,10 @@
|
||||
<h2>2.7</h2>
|
||||
<ul>
|
||||
<li>Purple! (Dominik, Vincent)</li>
|
||||
<li>New key view design (Dominik, Vincent)</li>
|
||||
<li>New flat Android buttons (Dominik, Vincent)</li>
|
||||
<li>API fixes (Dominik)</li>
|
||||
<li>Keybase.io import (Tim Bray)</li>
|
||||
<li>新しい鍵のビューのデザイン (Dominik, Vincent)</li>
|
||||
<li>新しいフラットな Android ボタン (Dominik, Vincent)</li>
|
||||
<li>API のフィックス (Dominik)</li>
|
||||
<li>Keybase.io からのインポート (Tim Bray)</li>
|
||||
</ul>
|
||||
<h2>2.6.1</h2>
|
||||
<ul>
|
||||
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>入門</h2>
|
||||
<p>最初にあなたの個人用秘密鍵が必要になります。オプションメニューの"鍵"で生成するか、既存の秘密鍵をインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。</p>
|
||||
<p>最初にあなたの個人用秘密鍵が必要になります。オプションメニューの"鍵"で生成するか、既存の秘密鍵をインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。</p>
|
||||
|
||||
<p>ファイルの選択を拡張するには<a href="market://details?id=org.openintents.filemanager">OI File Manager</a>、<a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a>を生成したQRコードのスキャンのため、それぞれのインストールを必要とします。 リンクをクリックして、Google Play Store上かF-Droidからインストールしてください。</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<h2>アプリケーション</h2>
|
||||
<p>OpenKeychainはプライベートな通信での暗号化/署名をいくつかのアプリケーションでサポートしています:<br><img src="apps_k9"><br>K-9 Mail: 現在のOpenKeychain <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a> からサポートが有効です!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>やりとり</a>: Jabber/XMPP クライアント<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: このアプリはPGP署名されたリクエストをサーバに送り、なにかしらを開けたり閉じたりします、たとえばドアとか</p>
|
||||
|
||||
<h2>OpenKeychainでバグを見付けた!</h2>
|
||||
<p><a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">OpenKeychainのIssueトラッカー</a>を使ってバグレポートを送ってください。</p>
|
||||
|
17
OpenKeychain/src/main/res/raw-ja/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-ja/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>信頼の輪</h2>
|
||||
<p>信頼の輪はPGPが証明の作成と維持する一面を説明します。ユーザが公開鍵が属する者の追跡を維持し、他のユーザーとその情報を共有する手助けになるメカニズムを提供します。暗号化通信のプライバシーを確保するためには、あなたが暗号化したいという相手の公開鍵が必要な要素となります</p>
|
||||
|
||||
<h2>OpenKeychainでサポート</h2>
|
||||
<p>OpenKeychainで信頼の輪の基本のみサポートされます。とても重い作業を進めており今後やってくるリリースにおいて状態が変更されていきます。</p>
|
||||
|
||||
<h2>信頼モデル</h2>
|
||||
<p>信頼の評価はシンプルな仮定に基づいています、それはすべての鍵は秘密鍵が信頼できるというものです。公開鍵は信頼された鍵で検証された最低1つのユーザIDを含んでおり、それは鍵リストにおいて、緑でマークされます。ただしそれは他の既知の公開鍵の信頼レベルを特別なものには(まだ)しません。</p>
|
||||
|
||||
<h2>鍵の検証</h2>
|
||||
<p>鍵検証のサポートが提供されており、ユーザIDは個別に検証できます。ただしそれは他の既知の公開鍵の信頼レベルを特別なものにはまだしないか他の特別なタイプの証明です。</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>Paul Sarbinowski</li>
|
||||
<li>Sreeram Boyapati</li>
|
||||
<li>Vincent Breitmoser</li>
|
||||
<li>Tim Bray</li>
|
||||
</ul>
|
||||
<h2>Developers APG 1.x</h2>
|
||||
<ul>
|
||||
|
@ -2,14 +2,12 @@
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Getting started</h2>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
<p>First you need a personal secret key. Create one via the option menus in "Keys" or import existing secret keys. Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
|
||||
|
||||
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
|
||||
|
||||
<h2>Applications</h2>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:</p>
|
||||
<p><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations">Conversations</a>: Jabber/XMPP client</p>
|
||||
<p><img src="apps_k9">K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!</p>
|
||||
<p>Several applications support OpenKeychain to encrypt/sign your private communication:<br><img src="apps_k9"><br>K-9 Mail: OpenKeychain support available in current <a href="https://github.com/k9mail/k-9/releases/tag/4.904">alpha build</a>!<br><a href="market://details?id=eu.siacs.conversations"><img src="apps_conversations"><br>Conversations</a>: Jabber/XMPP client<br><a href="market://details?id=org.lf_net.pgpunlocker"><img src="apps_pgpauth"><br>PGPAuth</a>: App to send a PGP-signed request to a server to open or close something, e.g. a door</p>
|
||||
|
||||
<h2>I found a bug in OpenKeychain!</h2>
|
||||
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
|
||||
|
17
OpenKeychain/src/main/res/raw-ko/help_wot.html
Normal file
17
OpenKeychain/src/main/res/raw-ko/help_wot.html
Normal file
@ -0,0 +1,17 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>Web of Trust</h2>
|
||||
<p>The Web of Trust describes the part of PGP which deals with creation and bookkeeping of certifications. It provides mechanisms to help the user keep track of who a public key belongs to, and share this information with others; To ensure the privacy of encrypted communication, it is essential to know that the public key you encrypt to belongs to the person you think it does.</p>
|
||||
|
||||
<h2>Support in OpenKeychain</h2>
|
||||
<p>There is only basic support for Web of Trust in OpenKeychain. This is a heavy work in progress and subject to changes in upcoming releases.</p>
|
||||
|
||||
<h2>Trust Model</h2>
|
||||
<p>Trust evaluation is based on the simple assumption that all keys which have secret keys available are trusted. Public keys which contain at least one user id certified by a trusted key will be marked with a green dot in the key listings. It is not (yet) possible to specify trust levels for certificates of other known public keys.</p>
|
||||
|
||||
<h2>Certifying keys</h2>
|
||||
<p>Support for key certification is available, and user ids can be certified individually. It is not yet possible to specify the level of trust or create local and other special types of certificates.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -1,156 +0,0 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>2.7</h2>
|
||||
<ul>
|
||||
<li>Purple! (Dominik, Vincent)</li>
|
||||
<li>New key view design (Dominik, Vincent)</li>
|
||||
<li>New flat Android buttons (Dominik, Vincent)</li>
|
||||
<li>API fixes (Dominik)</li>
|
||||
<li>Keybase.io import (Tim Bray)</li>
|
||||
</ul>
|
||||
<h2>2.6.1</h2>
|
||||
<ul>
|
||||
<li>some fixes for regression bugs</li>
|
||||
</ul>
|
||||
<h2>2.6</h2>
|
||||
<ul>
|
||||
<li>key certifications (thanks to Vincent Breitmoser)</li>
|
||||
<li>support for GnuPG partial secret keys (thanks to Vincent Breitmoser)</li>
|
||||
<li>new design for signature verification</li>
|
||||
<li>custom key length (thanks to Greg Witczak)</li>
|
||||
<li>fix share-functionality from other apps</li>
|
||||
</ul>
|
||||
<h2>2.5</h2>
|
||||
<ul>
|
||||
<li>fix decryption of symmetric pgp messages/files</li>
|
||||
<li>refactored edit key screen (thanks to Ash Hughes)</li>
|
||||
<li>new modern design for encrypt/decrypt screens</li>
|
||||
<li>OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)</li>
|
||||
</ul>
|
||||
<h2>2.4</h2>
|
||||
<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
|
||||
Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
|
||||
Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
|
||||
<ul>
|
||||
<li>new unified key list</li>
|
||||
<li>colorized key fingerprint</li>
|
||||
<li>support for keyserver ports</li>
|
||||
<li>deactivate possibility to generate weak keys</li>
|
||||
<li>much more internal work on the API</li>
|
||||
<li>certify user ids</li>
|
||||
<li>keyserver query based on machine-readable output</li>
|
||||
<li>lock navigation drawer on tablets</li>
|
||||
<li>suggestions for emails on creation of keys</li>
|
||||
<li>search in public key lists</li>
|
||||
<li>and much more improvements and fixes…</li>
|
||||
</ul>
|
||||
<h2>2.3.1</h2>
|
||||
<ul>
|
||||
<li>hotfix for crash when upgrading from old versions</li>
|
||||
</ul>
|
||||
<h2>2.3</h2>
|
||||
<ul>
|
||||
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
|
||||
<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
|
||||
<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
|
||||
<li>querying keyservers directly from the import screen</li>
|
||||
<li>fix layout and dialog style on Android 2.2-3.0</li>
|
||||
<li>fix crash on keys with empty user ids</li>
|
||||
<li>fix crash and empty lists when coming back from signing screen</li>
|
||||
<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
|
||||
<li>fix upload of key from signing screen</li>
|
||||
</ul>
|
||||
<h2>2.2</h2>
|
||||
<ul>
|
||||
<li>new design with navigation drawer</li>
|
||||
<li>new public key list design</li>
|
||||
<li>new public key view</li>
|
||||
<li>bug fixes for importing of keys</li>
|
||||
<li>key cross-certification (thanks to Ash Hughes)</li>
|
||||
<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
|
||||
<li>first version with new languages (thanks to the contributors on Transifex)</li>
|
||||
<li>sharing of keys via QR Codes fixed and improved</li>
|
||||
<li>package signature verification for API</li>
|
||||
</ul>
|
||||
<h2>2.1.1</h2>
|
||||
<ul>
|
||||
<li>API Updates, preparation for K-9 Mail integration</li>
|
||||
</ul>
|
||||
<h2>2.1</h2>
|
||||
<ul>
|
||||
<li>lots of bug fixes</li>
|
||||
<li>new API for developers</li>
|
||||
<li>PRNG bug fix by Google</li>
|
||||
</ul>
|
||||
<h2>2.0</h2>
|
||||
<ul>
|
||||
<li>complete redesign</li>
|
||||
<li>share public keys via qr codes, nfc beam</li>
|
||||
<li>sign keys</li>
|
||||
<li>upload keys to server</li>
|
||||
<li>fixes import issues</li>
|
||||
<li>new AIDL API</li>
|
||||
</ul>
|
||||
<h2>1.0.8</h2>
|
||||
<ul>
|
||||
<li>basic keyserver support</li>
|
||||
<li>app2sd</li>
|
||||
<li>more choices for pass phrase cache: 1, 2, 4, 8, hours</li>
|
||||
<li>translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)</li>
|
||||
<li>bugfixes</li>
|
||||
<li>optimizations</li>
|
||||
</ul>
|
||||
<h2>1.0.7</h2>
|
||||
<ul>
|
||||
<li>fixed problem with signature verification of texts with trailing newline</li>
|
||||
<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
|
||||
</ul>
|
||||
<h2>1.0.6</h2>
|
||||
<ul>
|
||||
<li>account adding crash on Froyo fixed</li>
|
||||
<li>secure file deletion</li>
|
||||
<li>option to delete key file after import</li>
|
||||
<li>stream encryption/decryption (gallery, etc.)</li>
|
||||
<li>new options (language, force v3 signatures)</li>
|
||||
<li>interface changes</li>
|
||||
<li>bugfixes</li>
|
||||
</ul>
|
||||
<h2>1.0.5</h2>
|
||||
<ul>
|
||||
<li>German and Italian translation</li>
|
||||
<li>much smaller package, due to reduced BC sources</li>
|
||||
<li>new preferences GUI</li>
|
||||
<li>layout adjustment for localization</li>
|
||||
<li>signature bugfix</li>
|
||||
</ul>
|
||||
<h2>1.0.4</h2>
|
||||
<ul>
|
||||
<li>fixed another crash caused by some SDK bug with query builder</li>
|
||||
</ul>
|
||||
<h2>1.0.3</h2>
|
||||
<ul>
|
||||
<li>fixed crashes during encryption/signing and possibly key export</li>
|
||||
</ul>
|
||||
<h2>1.0.2</h2>
|
||||
<ul>
|
||||
<li>filterable key lists</li>
|
||||
<li>smarter pre-selection of encryption keys</li>
|
||||
<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
|
||||
<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
|
||||
</ul>
|
||||
<h2>1.0.1</h2>
|
||||
<ul>
|
||||
<li>GMail account listing was broken in 1.0.0, fixed again</li>
|
||||
</ul>
|
||||
<h2>1.0.0</h2>
|
||||
<ul>
|
||||
<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
|
||||
<li>support of more file managers (including ASTRO)</li>
|
||||
<li>Slovenian translation</li>
|
||||
<li>new database, much faster, less memory usage</li>
|
||||
<li>defined Intents and content provider for other apps</li>
|
||||
<li>bugfixes</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
@ -1,12 +0,0 @@
|
||||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<h2>How to receive keys</h2>
|
||||
<ol>
|
||||
<li>Go to your partners contacts and open the contact you want to share.</li>
|
||||
<li>Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.</li>
|
||||
<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
|
||||
<li>Tap the card and the content will then load on the your device.</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user