aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-05-14 15:37:55 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-05-27 13:56:30 +0200
commita53da491c09fc7db814d4c2358ffe5dc9fe888bc (patch)
treef2bcc862c883de89016f8eec437f9aa8e5d1f706 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
parent6415290b2d059752ebcfd74fa2c514aa5e5ef875 (diff)
downloadopen-keychain-a53da491c09fc7db814d4c2358ffe5dc9fe888bc.tar.gz
open-keychain-a53da491c09fc7db814d4c2358ffe5dc9fe888bc.tar.bz2
open-keychain-a53da491c09fc7db814d4c2358ffe5dc9fe888bc.zip
new savekeyring operation (mostly stub)
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java338
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java49
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKeyRing.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java77
9 files changed, 488 insertions, 72 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index 63e2ff2f2..ea3c72fd0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -21,8 +21,6 @@ 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;
@@ -122,26 +120,4 @@ public class PgpConversionHelper {
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);
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index e858012f5..268906037 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -257,7 +257,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);
@@ -287,18 +288,13 @@ public class PgpImportExport {
public int storeKeyRingInCache(UncachedKeyRing ring, UncachedKeyRing secretRing) {
int status;
try {
- // TODO make sure these are correctly typed!
- PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) ring.getRing();
- PGPSecretKeyRing secretKeyRing = null;
- if(secretRing != null) {
- secretKeyRing = (PGPSecretKeyRing) secretRing.getRing();
- }
+ UncachedSecretKeyRing secretKeyRing = null;
// see what type we have. we can either have a secret + public keyring, or just public
if (secretKeyRing != null) {
mProviderHelper.saveKeyRing(ring, secretRing);
status = RETURN_OK;
} else {
- mProviderHelper.saveKeyRing(publicKeyRing);
+ mProviderHelper.saveKeyRing(ring);
status = RETURN_OK;
}
} catch (IOException e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index f49e0af4b..3e296edb9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -35,6 +35,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
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.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
@@ -49,6 +50,8 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
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;
import java.io.IOException;
@@ -60,9 +63,11 @@ 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;
+import java.util.List;
import java.util.TimeZone;
/**
@@ -644,7 +649,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);
}
@@ -655,7 +660,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);
}
@@ -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.
* <p/>
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 06f890fb4..8bacb32c0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -1,8 +1,9 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
@@ -13,6 +14,8 @@ import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
import java.util.List;
import java.util.Vector;
@@ -26,6 +29,10 @@ public class UncachedKeyRing {
mIsSecret = ring instanceof PGPSecretKeyRing;
}
+ public long getMasterKeyId() {
+ return mRing.getPublicKey().getKeyID();
+ }
+
/* TODO don't use this */
@Deprecated
public PGPKeyRing getRing() {
@@ -36,6 +43,21 @@ public class UncachedKeyRing {
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() {
return mIsSecret;
}
@@ -50,6 +72,15 @@ public class UncachedKeyRing {
public static UncachedKeyRing decodePubkeyFromData(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) {
@@ -58,13 +89,14 @@ public class UncachedKeyRing {
// get first object in block
Object obj;
- if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPPublicKeyRing) {
- return new UncachedKeyRing((PGPPublicKeyRing) obj);
+ if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
+ // the constructor will take care of the public/secret part
+ return new UncachedKeyRing((PGPKeyRing) obj);
} else {
- throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
} 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;
}
+ public void encodeArmored(OutputStream out, String version) throws IOException {
+ ArmoredOutputStream aos = new ArmoredOutputStream(out);
+ aos.setHeader("Version", version);
+ aos.write(mRing.getEncoded());
+ aos.close();
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 5dbe4b316..e3db03bf6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -13,6 +13,7 @@ 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 {
@@ -173,8 +174,24 @@ public class UncachedPublicKey {
return mPublicKey.getFingerprint();
}
- // Note that this method has package visibility - no access outside the pgp package!
- PGPPublicKey getPublicKey() {
+ // 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();
+ }
+ };
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKeyRing.java
index bda9ebbcf..ca784fbde 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKeyRing.java
@@ -1,19 +1,33 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.S2K;
+import org.spongycastle.openpgp.PGPSecretKey;
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) {
- mSecretRing = secretRing;
+ super(secretRing);
}
- // Breaking the pattern here, for key import!
- // TODO reduce this from public to default visibility!
- public PGPSecretKeyRing getSecretKeyRing() {
- return mSecretRing;
+ public ArrayList<Long> getAvailableSubkeys() {
+ 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;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
index 94eb91420..6e7b2a49e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
@@ -5,6 +5,9 @@ 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;
+
public abstract class WrappedKeyRing extends KeyRing {
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();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
index c94b7dfba..656430969 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
@@ -120,4 +120,8 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
});
}
+ public UncachedSecretKeyRing getUncached() {
+ return new UncachedSecretKeyRing(mRing);
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index cdadbca7f..9f26439d2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -5,6 +5,7 @@ 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;
@@ -14,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.security.SignatureException;
+import java.util.Date;
public class WrappedSignature {
@@ -33,16 +35,57 @@ public class WrappedSignature {
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.getPublicKey());
+ mSig.init(contentVerifierBuilderProvider, key);
} catch(PGPException e) {
throw new PgpGeneralException(e);
}
@@ -74,30 +117,9 @@ public class WrappedSignature {
}
}
- public boolean isRevocation() {
- 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 {
+ protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
try {
- return mSig.verifyCertification(uid, key.getPublicKey());
+ return mSig.verifyCertification(uid, key);
} catch (SignatureException e) {
throw new PgpGeneralException("Error!", 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) {
PGPObjectFactory factory = new PGPObjectFactory(data);
PGPSignatureList signatures = null;