new savekeyring operation (mostly stub)

This commit is contained in:
Vincent Breitmoser 2014-05-14 15:37:55 +02:00
parent 6415290b2d
commit a53da491c0
16 changed files with 595 additions and 356 deletions

View File

@ -21,8 +21,6 @@ import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -122,26 +120,4 @@ public class PgpConversionHelper {
return new UncachedSecretKey(secKey); return new UncachedSecretKey(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);
}
} }

View File

@ -257,7 +257,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100); updateProgress(progress * 100 / masterKeyIdsSize, 100);
try { try {
PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId); WrappedSecretKeyRing secretKeyRing =
mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream); secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e); Log.e(Constants.TAG, "key not found!", e);
@ -287,18 +288,13 @@ public class PgpImportExport {
public int storeKeyRingInCache(UncachedKeyRing ring, UncachedKeyRing secretRing) { public int storeKeyRingInCache(UncachedKeyRing ring, UncachedKeyRing secretRing) {
int status; int status;
try { try {
// TODO make sure these are correctly typed! UncachedSecretKeyRing secretKeyRing = null;
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) ring.getRing();
PGPSecretKeyRing secretKeyRing = null;
if(secretRing != null) {
secretKeyRing = (PGPSecretKeyRing) secretRing.getRing();
}
// see what type we have. we can either have a secret + public keyring, or just public // see what type we have. we can either have a secret + public keyring, or just public
if (secretKeyRing != null) { if (secretKeyRing != null) {
mProviderHelper.saveKeyRing(ring, secretRing); mProviderHelper.saveKeyRing(ring, secretRing);
status = RETURN_OK; status = RETURN_OK;
} else { } else {
mProviderHelper.saveKeyRing(publicKeyRing); mProviderHelper.saveKeyRing(ring);
status = RETURN_OK; status = RETURN_OK;
} }
} catch (IOException e) { } catch (IOException e) {

View File

@ -35,6 +35,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
@ -49,6 +50,8 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel; import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.Primes;
import java.io.IOException; import java.io.IOException;
@ -60,9 +63,11 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
@ -644,7 +649,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) { for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>( for(PGPSignature sig : new IterableIterator<PGPSignature>(
secretKeyRing.getPublicKey().getSignaturesForID(uid))) { secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " + Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
} }
@ -655,7 +660,7 @@ public class PgpKeyOperation {
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) { for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
for(PGPSignature sig : new IterableIterator<PGPSignature>( for(PGPSignature sig : new IterableIterator<PGPSignature>(
publicKeyRing.getPublicKey().getSignaturesForID(uid))) { publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
Log.d(Constants.TAG, "sig: " + Log.d(Constants.TAG, "sig: " +
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid); PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
} }
@ -668,6 +673,335 @@ public class PgpKeyOperation {
} }
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.
*
* @param certificationKey Certifying key
* @param publicKey public key to certify
* @param userIds User IDs to certify, must not be null or empty
* @param passphrase Passphrase of the secret key
* @return A keyring with added certifications
*/
public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey,
List<String> userIds, String passphrase)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
// create a signatureGenerator from the supplied masterKeyId and passphrase
PGPSignatureGenerator signatureGenerator;
{
if (certificationKey == null) {
throw new PgpGeneralMsgIdException(R.string.error_no_signature_key);
}
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key);
}
// TODO: SHA256 fixed?
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
}
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
}
// 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);
}
return publicKey;
}
/** /**
* Simple static subclass that stores two values. * Simple static subclass that stores two values.
* <p/> * <p/>

View File

@ -1,8 +1,9 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
@ -13,6 +14,8 @@ import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Vector; import java.util.Vector;
@ -26,6 +29,10 @@ public class UncachedKeyRing {
mIsSecret = ring instanceof PGPSecretKeyRing; mIsSecret = ring instanceof PGPSecretKeyRing;
} }
public long getMasterKeyId() {
return mRing.getPublicKey().getKeyID();
}
/* TODO don't use this */ /* TODO don't use this */
@Deprecated @Deprecated
public PGPKeyRing getRing() { public PGPKeyRing getRing() {
@ -36,6 +43,21 @@ public class UncachedKeyRing {
return new UncachedPublicKey(mRing.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();
}
};
}
public boolean isSecret() { public boolean isSecret() {
return mIsSecret; return mIsSecret;
} }
@ -50,6 +72,15 @@ public class UncachedKeyRing {
public static UncachedKeyRing decodePubkeyFromData(byte[] data) public static UncachedKeyRing decodePubkeyFromData(byte[] data)
throws PgpGeneralException, IOException { 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 = BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(data)); new BufferedInputStream(new ByteArrayInputStream(data));
if (bufferedInput.available() > 0) { if (bufferedInput.available() > 0) {
@ -58,13 +89,14 @@ public class UncachedKeyRing {
// get first object in block // get first object in block
Object obj; Object obj;
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPPublicKeyRing) { if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
return new UncachedKeyRing((PGPPublicKeyRing) obj); // the constructor will take care of the public/secret part
return new UncachedKeyRing((PGPKeyRing) obj);
} else { } else {
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!"); throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
} }
} else { } else {
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!"); throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
} }
} }
@ -89,4 +121,11 @@ public class UncachedKeyRing {
return result; 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();
}
} }

View File

@ -13,6 +13,7 @@ import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List; import java.util.List;
public class UncachedPublicKey { public class UncachedPublicKey {
@ -173,8 +174,24 @@ public class UncachedPublicKey {
return mPublicKey.getFingerprint(); return mPublicKey.getFingerprint();
} }
// Note that this method has package visibility - no access outside the pgp package! // TODO This method should have package visibility - no access outside the pgp package!
PGPPublicKey getPublicKey() { // (It's still used in ProviderHelper at this point)
public PGPPublicKey getPublicKey() {
return mPublicKey; return mPublicKey;
} }
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
return new Iterator<WrappedSignature>() {
public void remove() {
it.remove();
}
public WrappedSignature next() {
return new WrappedSignature(it.next());
}
public boolean hasNext() {
return it.hasNext();
}
};
}
} }

View File

@ -1,19 +1,33 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.util.IterableIterator;
public class UncachedSecretKeyRing { import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
final PGPSecretKeyRing mSecretRing; public class UncachedSecretKeyRing extends UncachedKeyRing {
UncachedSecretKeyRing(PGPSecretKeyRing secretRing) { UncachedSecretKeyRing(PGPSecretKeyRing secretRing) {
mSecretRing = secretRing; super(secretRing);
} }
// Breaking the pattern here, for key import! public ArrayList<Long> getAvailableSubkeys() {
// TODO reduce this from public to default visibility! ArrayList<Long> result = new ArrayList<Long>();
public PGPSecretKeyRing getSecretKeyRing() { // then, mark exactly the keys we have available
return mSecretRing; for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
((PGPSecretKeyRing) mRing).getSecretKeys())) {
S2K s2k = sub.getS2K();
// Set to 1, except if the encryption type is GNU_DUMMY_S2K
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
result.add(sub.getKeyID());
}
}
return result;
} }
} }

View File

@ -5,6 +5,9 @@ import org.spongycastle.openpgp.PGPPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException;
import java.io.OutputStream;
public abstract class WrappedKeyRing extends KeyRing { public abstract class WrappedKeyRing extends KeyRing {
private final boolean mHasAnySecret; private final boolean mHasAnySecret;
@ -76,6 +79,10 @@ public abstract class WrappedKeyRing extends KeyRing {
} }
} }
public void encode(OutputStream stream) throws IOException {
getRing().encode(stream);
}
abstract PGPKeyRing getRing(); abstract PGPKeyRing getRing();
} }

View File

@ -120,4 +120,8 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
}); });
} }
public UncachedSecretKeyRing getUncached() {
return new UncachedSecretKeyRing(mRing);
}
} }

View File

@ -5,6 +5,7 @@ import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.RevocationReason; import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
@ -14,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException; import java.io.IOException;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Date;
public class WrappedSignature { public class WrappedSignature {
@ -33,16 +35,57 @@ public class WrappedSignature {
return mSig.getKeyID(); return mSig.getKeyID();
} }
public int getSignatureType() {
return mSig.getSignatureType();
}
public int getKeyAlgorithm() { public int getKeyAlgorithm() {
return mSig.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 { 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 { try {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider() new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
mSig.init(contentVerifierBuilderProvider, key.getPublicKey()); mSig.init(contentVerifierBuilderProvider, key);
} catch(PGPException e) { } catch(PGPException e) {
throw new PgpGeneralException(e); throw new PgpGeneralException(e);
} }
@ -74,30 +117,9 @@ public class WrappedSignature {
} }
} }
public boolean isRevocation() { protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
}
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();
}
/** Verify a signature for this pubkey, after it has been initialized by the signer using
* initSignature(). This method should probably move into a wrapped PGPSignature class
* at some point.
*/
public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
try { try {
return mSig.verifyCertification(uid, key.getPublicKey()); return mSig.verifyCertification(uid, key);
} catch (SignatureException e) { } catch (SignatureException e) {
throw new PgpGeneralException("Error!", e); throw new PgpGeneralException("Error!", e);
} catch (PGPException e) { } catch (PGPException e) {
@ -105,6 +127,13 @@ public class WrappedSignature {
} }
} }
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) { public static WrappedSignature fromBytes(byte[] data) {
PGPObjectFactory factory = new PGPObjectFactory(data); PGPObjectFactory factory = new PGPObjectFactory(data);
PGPSignatureList signatures = null; PGPSignatureList signatures = null;

View File

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

View File

@ -23,30 +23,21 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.OperationApplicationException; import android.content.OperationApplicationException;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri; import android.net.Uri;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.util.LongSparseArray; 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.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedSecretKeyRing;
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.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
@ -60,7 +51,6 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.security.SignatureException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
@ -147,19 +137,26 @@ public class ProviderHelper {
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types); return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
} }
@Deprecated private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri, Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
null, null, null); null, null, null);
LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount()); LongSparseArray<UncachedPublicKey> result =
new LongSparseArray<UncachedPublicKey>(cursor.getCount());
try { try {
if (cursor != null && cursor.moveToFirst()) do { if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0); long masterKeyId = cursor.getLong(0);
byte[] data = cursor.getBlob(1); byte[] data = cursor.getBlob(1);
if (data != null) { if (data != null) {
result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data)); try {
result.put(masterKeyId,
UncachedKeyRing.decodePubkeyFromData(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()); } while (cursor.moveToNext());
} finally { } finally {
@ -194,12 +191,13 @@ public class ProviderHelper {
private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException { private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri, Cursor cursor = mContentResolver.query(queryUri,
new String[] { new String[]{
// we pick from cache only information that is not easily available from keyrings // we pick from cache only information that is not easily available from keyrings
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED, KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
// and of course, ring data // and of course, ring data
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
}, null, null, null); }, null, null, null
);
try { try {
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
@ -219,37 +217,18 @@ public class ProviderHelper {
} }
} }
@Deprecated
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);
}
}
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
@Deprecated
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 * Saves PGPPublicKeyRing with its keys and userIds in DB
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException { public void saveKeyRing(UncachedKeyRing keyRing) throws IOException {
PGPPublicKey masterKey = keyRing.getPublicKey(); UncachedPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyID(); long masterKeyId = masterKey.getKeyId();
// IF there is a secret key, preserve it! // IF there is a secret key, preserve it!
PGPSecretKeyRing secretRing = null; UncachedSecretKeyRing secretRing = null;
try { try {
secretRing = getPGPSecretKeyRing(masterKeyId); secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
} catch (NotFoundException e) { } catch (NotFoundException e) {
Log.e(Constants.TAG, "key not found!"); Log.e(Constants.TAG, "key not found!");
} }
@ -272,36 +251,38 @@ public class ProviderHelper {
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0; 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)); operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
++rank; ++rank;
} }
// get a list of owned secret keys, for verification filtering // 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! // special case: available secret keys verify themselves!
if (secretRing != null) if (secretRing != null) {
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing); allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
}
// classify and order user ids. primary are moved to the front, revoked to the back, // classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved. // otherwise the order in the keyfile is preserved.
List<UserIdItem> uids = new ArrayList<UserIdItem>(); 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(); UserIdItem item = new UserIdItem();
uids.add(item); uids.add(item);
item.userId = userId; item.userId = userId;
// look through signatures for this specific key // look through signatures for this specific key
for (PGPSignature cert : new IterableIterator<PGPSignature>( for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
masterKey.getSignaturesForID(userId))) { masterKey.getSignaturesForId(userId))) {
long certId = cert.getKeyID(); long certId = cert.getKeyId();
try { try {
// self signature // self signature
if (certId == masterKeyId) { if (certId == masterKeyId) {
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( cert.init(masterKey);
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey); if (!cert.verifySignature(masterKey, userId)) {
if (!cert.verifyCertification(userId, masterKey)) {
// not verified?! dang! TODO notify user? this is kinda serious... // not verified?! dang! TODO notify user? this is kinda serious...
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!"); Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
continue; continue;
@ -310,31 +291,22 @@ public class ProviderHelper {
if (item.selfCert == null || if (item.selfCert == null ||
item.selfCert.getCreationTime().before(cert.getCreationTime())) { item.selfCert.getCreationTime().before(cert.getCreationTime())) {
item.selfCert = cert; item.selfCert = cert;
item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID(); item.isPrimary = cert.isPrimaryUserId();
item.isRevoked = item.isRevoked = cert.isRevocation();
cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
} }
} }
// verify signatures from known private keys // verify signatures from known private keys
if (allKeyRings.indexOfKey(certId) >= 0) { if (allKeyRings.indexOfKey(certId) >= 0) {
// mark them as verified cert.init(allKeyRings.get(certId));
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider( if (cert.verifySignature(masterKey, userId)) {
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
allKeyRings.get(certId).getPublicKey());
if (cert.verifyCertification(userId, masterKey)) {
item.trustedCerts.add(cert); item.trustedCerts.add(cert);
} }
} }
} catch (SignatureException e) { } catch (PgpGeneralException e) {
Log.e(Constants.TAG, "Signature verification failed! " Log.e(Constants.TAG, "Signature verification failed! "
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID()) + PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
+ " from " + " from "
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e); + 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);
} }
} }
} }
@ -380,8 +352,8 @@ public class ProviderHelper {
String userId; String userId;
boolean isPrimary = false; boolean isPrimary = false;
boolean isRevoked = false; boolean isRevoked = false;
PGPSignature selfCert; WrappedSignature selfCert;
List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>(); List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
@Override @Override
public int compareTo(UserIdItem o) { public int compareTo(UserIdItem o) {
@ -401,18 +373,8 @@ public class ProviderHelper {
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
* is already in the database! * is already in the database!
*/ */
public void saveKeyRing(UncachedSecretKeyRing wrappedRing) throws IOException { public void saveKeyRing(UncachedSecretKeyRing keyRing) throws IOException {
// TODO split up getters long masterKeyId = keyRing.getMasterKeyId();
PGPSecretKeyRing keyRing = wrappedRing.getSecretKeyRing();
saveKeyRing(keyRing);
}
/**
* 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.getSecretKey().getKeyID();
{ {
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
@ -424,14 +386,10 @@ public class ProviderHelper {
values.put(Keys.HAS_SECRET, 1); values.put(Keys.HAS_SECRET, 1);
// then, mark exactly the keys we have available // then, mark exactly the keys we have available
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) { for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
S2K s2k = sub.getS2K(); mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
// Set to 1, except if the encryption type is GNU_DUMMY_S2K Long.toString(sub)
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) { });
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
Long.toString(sub.getKeyID())
});
}
} }
// this implicitly leaves all keys which were not in the secret key ring // this implicitly leaves all keys which were not in the secret key ring
// with has_secret = 0 // with has_secret = 0
@ -449,11 +407,6 @@ public class ProviderHelper {
} }
public void saveKeyRing(UncachedKeyRing ring) throws IOException {
PGPPublicKeyRing pubRing = (PGPPublicKeyRing) ring.getRing();
saveKeyRing(pubRing);
}
/** /**
* Saves (or updates) a pair of public and secret KeyRings in the database * Saves (or updates) a pair of public and secret KeyRings in the database
*/ */
@ -464,32 +417,32 @@ public class ProviderHelper {
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null); mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
// save public keyring // save public keyring
saveKeyRing((PGPPublicKeyRing) pubRing.getRing()); saveKeyRing(pubRing);
saveKeyRing((PGPSecretKeyRing) secRing.getRing()); saveKeyRing(secRing);
} }
/** /**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/ */
private ContentProviderOperation private ContentProviderOperation
buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException { buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(Keys.MASTER_KEY_ID, masterKeyId); values.put(Keys.MASTER_KEY_ID, masterKeyId);
values.put(Keys.RANK, rank); 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.KEY_SIZE, key.getBitStrength());
values.put(Keys.ALGORITHM, key.getAlgorithm()); values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint()); values.put(Keys.FINGERPRINT, key.getFingerprint());
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key))); values.put(Keys.CAN_CERTIFY, key.canCertify());
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key))); values.put(Keys.CAN_SIGN, key.canSign());
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key)); values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
values.put(Keys.IS_REVOKED, key.isRevoked()); values.put(Keys.IS_REVOKED, key.maybeRevoked());
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
Date expiryDate = PgpKeyHelper.getExpiryDate(key); Date expiryDate = key.getExpiryTime();
if (expiryDate != null) { if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
} }
@ -503,11 +456,12 @@ public class ProviderHelper {
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/ */
private ContentProviderOperation 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(); ContentValues values = new ContentValues();
values.put(Certs.MASTER_KEY_ID, masterKeyId); values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank); 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.TYPE, cert.getSignatureType());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000); values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
values.put(Certs.VERIFIED, verified); values.put(Certs.VERIFIED, verified);
@ -535,23 +489,11 @@ public class ProviderHelper {
return ContentProviderOperation.newInsert(uri).withValues(values).build(); return ContentProviderOperation.newInsert(uri).withValues(values).build();
} }
private String getKeyRingAsArmoredString(byte[] data) throws IOException { private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
Object keyRing = null; UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
if (data != null) {
keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
}
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = new ArmoredOutputStream(bos); keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
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();
String armoredKey = bos.toString("UTF-8"); String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey); Log.d(Constants.TAG, "armoredKey:" + armoredKey);
@ -560,77 +502,12 @@ public class ProviderHelper {
} }
public String getKeyRingAsArmoredString(Uri uri) public String getKeyRingAsArmoredString(Uri uri)
throws NotFoundException, IOException { throws NotFoundException, IOException, PgpGeneralException {
byte[] data = (byte[]) getGenericData( byte[] data = (byte[]) getGenericData(
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB); uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
return getKeyRingAsArmoredString(data); 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() { public ArrayList<String> getRegisteredApiApps() {
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null); Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);

View File

@ -26,16 +26,13 @@ import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey; import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
@ -594,21 +591,21 @@ public class KeychainIntentService extends IntentService
buf = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, true); 4096, passphrase, true);
os.write(buf); os.write(buf);
keyUsageList.add(KeyFlags.CERTIFY_OTHER); keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
buf = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false); 4096, passphrase, false);
os.write(buf); os.write(buf);
keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE); keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
buf = keyOperations.createKey(Constants.choice.algorithm.rsa, buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
4096, passphrase, false); 4096, passphrase, false);
os.write(buf); os.write(buf);
keyUsageList.add(KeyFlags.SIGN_DATA); keyUsageList.add(UncachedSecretKey.SIGN_DATA);
keysCreated++; keysCreated++;
setProgress(keysCreated, keysTotal); setProgress(keysCreated, keysTotal);
@ -749,23 +746,15 @@ public class KeychainIntentService extends IntentService
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes(); byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
// create PGPKeyRing object based on downloaded armored key // create PGPKeyRing object based on downloaded armored key
PGPKeyRing downloadedKey = null; UncachedKeyRing downloadedKey = null;
BufferedInputStream bufferedInput = BufferedInputStream bufferedInput =
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes)); new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
if (bufferedInput.available() > 0) { if (bufferedInput.available() > 0) {
InputStream in = PGPUtil.getDecoderStream(bufferedInput); List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
PGPObjectFactory objectFactory = new PGPObjectFactory(in); if(rings.isEmpty()) {
throw new PgpGeneralException("No keys in result!");
// 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!");
}
} }
downloadedKey = rings.get(0);
} }
// save key bytes in entry object for doing the // save key bytes in entry object for doing the

View File

@ -34,12 +34,6 @@ import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.v4.util.LongSparseArray; 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.Constants;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
@ -48,7 +42,6 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import java.util.Date; 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 * This service runs in its own process, but is available to all other processes as the main
@ -191,7 +184,8 @@ public class PassphraseCacheService extends Service {
// get cached passphrase // get cached passphrase
String cachedPassphrase = mPassphraseCache.get(keyId); String cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) { if (cachedPassphrase == null) {
// this is an error 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; return null;
} }
@ -206,44 +200,6 @@ public class PassphraseCacheService extends Service {
} }
} }
@Deprecated
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
*/
@Deprecated
public static boolean hasPassphrase(Context context, long secretKeyId)
throws ProviderHelper.NotFoundException {
return new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase();
}
/** /**
* Register BroadcastReceiver that is unregistered when service is destroyed. This * Register BroadcastReceiver that is unregistered when service is destroyed. This
* BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout * BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout

View File

@ -13,7 +13,8 @@ import java.util.HashMap;
* *
* All changes are done in a differential manner. Besides the two key * All changes are done in a differential manner. Besides the two key
* identification attributes, all attributes may be null, which indicates no * identification attributes, all attributes may be null, which indicates no
* change to the keyring. * 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 * 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 * error in any included operation (for example revocation of a non-existent
@ -45,10 +46,12 @@ public class SaveKeyringParcel implements Parcelable {
// performance gain for using Parcelable here would probably be negligible, // performance gain for using Parcelable here would probably be negligible,
// use Serializable instead. // use Serializable instead.
public static class SubkeyAdd implements Serializable { public static class SubkeyAdd implements Serializable {
public final int mAlgorithm;
public final int mKeysize; public final int mKeysize;
public final int mFlags; public final int mFlags;
public final Long mExpiry; public final Long mExpiry;
public SubkeyAdd(int keysize, int flags, long expiry) { public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
mAlgorithm = algorithm;
mKeysize = keysize; mKeysize = keysize;
mFlags = flags; mFlags = flags;
mExpiry = expiry; mExpiry = expiry;
@ -59,7 +62,7 @@ public class SaveKeyringParcel implements Parcelable {
public final long mKeyId; public final long mKeyId;
public final Integer mFlags; public final Integer mFlags;
public final Long mExpiry; public final Long mExpiry;
public SubkeyChange(long keyId, int flags, long expiry) { public SubkeyChange(long keyId, Integer flags, Long expiry) {
mKeyId = keyId; mKeyId = keyId;
mFlags = flags; mFlags = flags;
mExpiry = expiry; mExpiry = expiry;

View File

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

View File

@ -102,7 +102,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// check if secret key has a passphrase // check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) { if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
try { try {
if (new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) { if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!"); throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
} }
} catch(ProviderHelper.NotFoundException e) { } catch(ProviderHelper.NotFoundException e) {