introduce minimize extra to ACTION_GET_KEY
This commit is contained in:
@@ -62,6 +62,10 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
||||
return getRing().getPublicKey().getFingerprint();
|
||||
}
|
||||
|
||||
public byte[] getRawPrimaryUserId() throws PgpKeyNotFoundException {
|
||||
return getPublicKey().getRawPrimaryUserId();
|
||||
}
|
||||
|
||||
public String getPrimaryUserId() throws PgpKeyNotFoundException {
|
||||
return getPublicKey().getPrimaryUserId();
|
||||
}
|
||||
@@ -136,6 +140,15 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
||||
}
|
||||
}
|
||||
|
||||
public long getSigningId() throws PgpKeyNotFoundException {
|
||||
for(CanonicalizedPublicKey key : publicKeyIterator()) {
|
||||
if (key.canSign() && key.isValid()) {
|
||||
return key.getKeyId();
|
||||
}
|
||||
}
|
||||
throw new PgpKeyNotFoundException("No valid signing key found!");
|
||||
}
|
||||
|
||||
public void encode(OutputStream stream) throws IOException {
|
||||
getRing().encode(stream);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
@@ -27,8 +34,6 @@ import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
||||
|
||||
@@ -84,6 +89,69 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns a minimized version of this key.
|
||||
*
|
||||
* The minimized version includes:
|
||||
* - the master key
|
||||
* - the current best signing key (if any)
|
||||
* - one encryption key (if any)
|
||||
* - the user id that matches the userIdToKeep parameter, or the primary user id if none matches
|
||||
* each with their most recent binding certificates
|
||||
*/
|
||||
public CanonicalizedPublicKeyRing minimize(@Nullable String userIdToKeep) throws IOException, PgpKeyNotFoundException {
|
||||
CanonicalizedPublicKey masterKey = getPublicKey();
|
||||
PGPPublicKey masterPubKey = masterKey.getPublicKey();
|
||||
boolean userIdStrippedOk = false;
|
||||
if (userIdToKeep != null) {
|
||||
try {
|
||||
masterPubKey = PGPPublicKeyUtils.keepOnlyUserId(masterPubKey, userIdToKeep);
|
||||
userIdStrippedOk = true;
|
||||
} catch (NoSuchElementException e) {
|
||||
// will be handled because userIdStrippedOk is false
|
||||
}
|
||||
}
|
||||
|
||||
if (!userIdStrippedOk) {
|
||||
byte[] rawPrimaryUserId = getRawPrimaryUserId();
|
||||
masterPubKey = PGPPublicKeyUtils.keepOnlyRawUserId(masterPubKey, rawPrimaryUserId);
|
||||
}
|
||||
|
||||
masterPubKey = PGPPublicKeyUtils.keepOnlySelfCertsForUserIds(masterPubKey);
|
||||
masterPubKey = PGPPublicKeyUtils.removeAllUserAttributes(masterPubKey);
|
||||
masterPubKey = PGPPublicKeyUtils.removeAllDirectKeyCerts(masterPubKey);
|
||||
|
||||
PGPPublicKeyRing resultRing = new PGPPublicKeyRing(masterPubKey.getEncoded(), new JcaKeyFingerprintCalculator());
|
||||
|
||||
Long encryptId;
|
||||
try {
|
||||
encryptId = getEncryptId();
|
||||
// only add if this key doesn't coincide with master key
|
||||
if (encryptId != getMasterKeyId()) {
|
||||
CanonicalizedPublicKey encryptKey = getPublicKey(encryptId);
|
||||
PGPPublicKey encryptPubKey = encryptKey.getPublicKey();
|
||||
resultRing = PGPPublicKeyRing.insertPublicKey(resultRing, encryptPubKey);
|
||||
}
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
// no encryption key: can't be reasonably minimized
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
long signingId = getSigningId();
|
||||
// only add if this key doesn't coincide with master or encryption key
|
||||
if (signingId != encryptId && signingId != getMasterKeyId()) {
|
||||
CanonicalizedPublicKey signingKey = getPublicKey(signingId);
|
||||
PGPPublicKey signingPubKey = signingKey.getPublicKey();
|
||||
resultRing = PGPPublicKeyRing.insertPublicKey(resultRing, signingPubKey);
|
||||
}
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
// no signing key: can't be reasonably minimized
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CanonicalizedPublicKeyRing(resultRing, getVerified());
|
||||
}
|
||||
|
||||
/** Create a dummy secret ring from this key */
|
||||
public UncachedKeyRing createDivertSecretRing (byte[] cardAid, long[] subKeyIds) {
|
||||
PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), cardAid);
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||
import org.sufficientlysecure.keychain.util.Utf8Util;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked") // BouncyCastle doesn't do generics here :(
|
||||
class PGPPublicKeyUtils {
|
||||
|
||||
static PGPPublicKey keepOnlyRawUserId(PGPPublicKey masterPublicKey, byte[] rawUserIdToKeep) {
|
||||
boolean elementToKeepFound = false;
|
||||
|
||||
Iterator<byte[]> it = masterPublicKey.getRawUserIDs();
|
||||
while (it.hasNext()) {
|
||||
byte[] rawUserId = it.next();
|
||||
if (Arrays.equals(rawUserId, rawUserIdToKeep)) {
|
||||
elementToKeepFound = true;
|
||||
} else {
|
||||
masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, rawUserId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!elementToKeepFound) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return masterPublicKey;
|
||||
}
|
||||
|
||||
static PGPPublicKey keepOnlyUserId(PGPPublicKey masterPublicKey, String userIdToKeep) {
|
||||
boolean elementToKeepFound = false;
|
||||
|
||||
Iterator<byte[]> it = masterPublicKey.getRawUserIDs();
|
||||
while (it.hasNext()) {
|
||||
byte[] rawUserId = it.next();
|
||||
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
|
||||
if (userId.contains(userIdToKeep)) {
|
||||
elementToKeepFound = true;
|
||||
} else {
|
||||
masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, rawUserId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!elementToKeepFound) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return masterPublicKey;
|
||||
}
|
||||
|
||||
static PGPPublicKey keepOnlySelfCertsForUserIds(PGPPublicKey masterPubKey) {
|
||||
long masterKeyId = masterPubKey.getKeyID();
|
||||
|
||||
Iterator<byte[]> it = masterPubKey.getRawUserIDs();
|
||||
while (it.hasNext()) {
|
||||
byte[] rawUserId = it.next();
|
||||
masterPubKey = keepOnlySelfCertsForRawUserId(masterPubKey, masterKeyId, rawUserId);
|
||||
}
|
||||
|
||||
return masterPubKey;
|
||||
}
|
||||
|
||||
private static PGPPublicKey keepOnlySelfCertsForRawUserId(
|
||||
PGPPublicKey masterPubKey, long masterKeyId, byte[] rawUserId) {
|
||||
Iterator<PGPSignature> it = masterPubKey.getSignaturesForID(rawUserId);
|
||||
while (it.hasNext()) {
|
||||
PGPSignature sig = it.next();
|
||||
if (sig.getKeyID() != masterKeyId) {
|
||||
masterPubKey = PGPPublicKey.removeCertification(masterPubKey, rawUserId, sig);
|
||||
}
|
||||
}
|
||||
return masterPubKey;
|
||||
}
|
||||
|
||||
static PGPPublicKey removeAllUserAttributes(PGPPublicKey masterPubKey) {
|
||||
Iterator<PGPUserAttributeSubpacketVector> it = masterPubKey.getUserAttributes();
|
||||
|
||||
while (it.hasNext()) {
|
||||
masterPubKey = PGPPublicKey.removeCertification(masterPubKey, it.next());
|
||||
}
|
||||
|
||||
return masterPubKey;
|
||||
}
|
||||
|
||||
static PGPPublicKey removeAllDirectKeyCerts(PGPPublicKey masterPubKey) {
|
||||
Iterator<PGPSignature> it = masterPubKey.getSignaturesOfType(PGPSignature.DIRECT_KEY);
|
||||
|
||||
while (it.hasNext()) {
|
||||
masterPubKey = PGPPublicKey.removeCertification(masterPubKey, it.next());
|
||||
}
|
||||
|
||||
return masterPubKey;
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,24 @@ public class UncachedPublicKey {
|
||||
*
|
||||
*/
|
||||
public String getPrimaryUserId() {
|
||||
byte[] found = getRawPrimaryUserId();
|
||||
if (found != null) {
|
||||
return Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(found);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the primary user id, as indicated by the public key's self certificates.
|
||||
*
|
||||
* This is an expensive operation, since potentially a lot of certificates (and revocations)
|
||||
* have to be checked, and even then the result is NOT guaranteed to be constant through a
|
||||
* canonicalization operation.
|
||||
*
|
||||
* Returns null if there is no primary user id (as indicated by certificates)
|
||||
*
|
||||
*/
|
||||
public byte[] getRawPrimaryUserId() {
|
||||
byte[] found = null;
|
||||
PGPSignature foundSig = null;
|
||||
// noinspection unchecked
|
||||
@@ -161,11 +179,7 @@ public class UncachedPublicKey {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found != null) {
|
||||
return Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(found);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -544,6 +544,11 @@ public class OpenPgpService extends Service {
|
||||
Intent result = new Intent();
|
||||
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
|
||||
|
||||
if (data.getBooleanExtra(OpenPgpApi.EXTRA_MINIMIZE, false)) {
|
||||
String userIdToKeep = data.getStringExtra(OpenPgpApi.EXTRA_MINIMIZE_USER_ID);
|
||||
keyRing = keyRing.minimize(userIdToKeep);
|
||||
}
|
||||
|
||||
boolean requestedKeyData = outputStream != null;
|
||||
if (requestedKeyData) {
|
||||
boolean requestAsciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, false);
|
||||
|
||||
Reference in New Issue
Block a user