aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java51
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java57
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java18
5 files changed, 132 insertions, 14 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index a965d4819..42e59b3bc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -49,6 +49,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.Date;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -140,6 +141,11 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
// It means the passphrase is empty
return SecretKeyType.PASSPHRASE_EMPTY;
} catch (PGPException e) {
+ HashMap<String,String> notation = getRing().getLocalNotationData();
+ if (notation.containsKey("unlock.pin@sufficientlysecure.org")
+ && "1".equals(notation.get("unlock.pin@sufficientlysecure.org"))) {
+ return SecretKeyType.PIN;
+ }
// Otherwise, it's just a regular ol' passphrase
return SecretKeyType.PASSPHRASE;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index e20155cc6..eb589c3f9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -26,6 +26,7 @@ import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
@@ -36,6 +37,7 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -130,4 +132,16 @@ public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
});
}
+ public HashMap<String,String> getLocalNotationData() {
+ HashMap<String,String> result = new HashMap<String,String>();
+ Iterator<PGPSignature> it = getRing().getPublicKey().getKeySignatures();
+ while (it.hasNext()) {
+ WrappedSignature sig = new WrappedSignature(it.next());
+ if (sig.isLocal()) {
+ result.putAll(sig.getNotation());
+ }
+ }
+ return result;
+ }
+
}
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 c125165a8..c8ce349f8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -34,6 +34,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.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
@@ -877,7 +878,8 @@ public class PgpKeyOperation {
log.add(LogType.MSG_MF_PASSPHRASE, indent);
indent += 1;
- sKR = applyNewUnlock(sKR, masterPublicKey, passphrase, saveParcel.mNewUnlock, log, indent);
+ sKR = applyNewUnlock(sKR, masterPublicKey, masterPrivateKey,
+ passphrase, saveParcel.mNewUnlock, log, indent);
if (sKR == null) {
// The error has been logged above, just return a bad state
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
@@ -909,19 +911,62 @@ public class PgpKeyOperation {
private static PGPSecretKeyRing applyNewUnlock(
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,
+ PGPPrivateKey masterPrivateKey,
String passphrase,
ChangeUnlockParcel newUnlock,
OperationLog log, int indent) throws PGPException {
if (newUnlock.mNewPassphrase != null) {
- return applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
+ sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPassphrase, log, indent);
+
+ // add packet with EMPTY notation data (updates old one, but will be stripped later)
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ { // set subpackets
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setExportable(false, false);
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ }
+ sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
+ PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR,
+ PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
+
+ return sKR;
+ }
+
+ if (newUnlock.mNewPin != null) {
+ sKR = applyNewPassphrase(sKR, masterPublicKey, passphrase, newUnlock.mNewPin, log, indent);
+
+ // add packet with EMPTY notation data (updates old one, but will be stripped later)
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ { // set subpackets
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen.setExportable(false, false);
+ hashedPacketsGen.setNotationData(false, true, "unlock.pin@sufficientlysecure.org", "1");
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ }
+ sGen.init(PGPSignature.DIRECT_KEY, masterPrivateKey);
+ PGPSignature emptySig = sGen.generateCertification(masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, emptySig);
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR,
+ PGPSecretKey.replacePublicKey(sKR.getSecretKey(), masterPublicKey));
+
+ return sKR;
}
throw new UnsupportedOperationException("PIN passphrases not yet implemented!");
}
-
private static PGPSecretKeyRing applyNewPassphrase(
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,
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 b343c779a..5e5a28e83 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -41,7 +41,6 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Utf8Util;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -302,6 +301,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = masterKey;
PGPSignature revocation = null;
+ PGPSignature notation = null;
for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
@@ -318,9 +318,9 @@ public class UncachedKeyRing {
}
WrappedSignature cert = new WrappedSignature(zert);
- if (type != PGPSignature.KEY_REVOCATION) {
+ if (type != PGPSignature.KEY_REVOCATION && type != PGPSignature.DIRECT_KEY) {
// Unknown type, just remove
- log.add(LogType.MSG_KC_REVOKE_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
+ log.add(LogType.MSG_KC_BAD_TYPE, indent, "0x" + Integer.toString(type, 16));
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
continue;
@@ -334,14 +334,6 @@ public class UncachedKeyRing {
continue;
}
- if (cert.isLocal()) {
- // Remove revocation certs with "local" flag
- log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
- modified = PGPPublicKey.removeCertification(modified, zert);
- badCerts += 1;
- continue;
- }
-
try {
cert.init(masterKey);
if (!cert.verifySignature(masterKey)) {
@@ -357,6 +349,39 @@ public class UncachedKeyRing {
continue;
}
+ // special case: direct key signatures!
+ if (cert.getSignatureType() == PGPSignature.DIRECT_KEY) {
+ // must be local, otherwise strip!
+ if (!cert.isLocal()) {
+ log.add(LogType.MSG_KC_BAD_TYPE, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ badCerts += 1;
+ continue;
+ }
+
+ // first notation? fine then.
+ if (notation == null) {
+ notation = zert;
+ // more notations? at least one is superfluous, then.
+ } else if (notation.getCreationTime().before(zert.getCreationTime())) {
+ log.add(LogType.MSG_KC_NOTATION_DUP, indent);
+ modified = PGPPublicKey.removeCertification(modified, notation);
+ redundantCerts += 1;
+ notation = zert;
+ } else {
+ log.add(LogType.MSG_KC_NOTATION_DUP, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ redundantCerts += 1;
+ }
+ continue;
+ } else if (cert.isLocal()) {
+ // Remove revocation certs with "local" flag
+ log.add(LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
+ modified = PGPPublicKey.removeCertification(modified, zert);
+ badCerts += 1;
+ continue;
+ }
+
// first revocation? fine then.
if (revocation == null) {
revocation = zert;
@@ -373,6 +398,16 @@ public class UncachedKeyRing {
}
}
+ // If we have a notation packet, check if there is even any data in it?
+ if (notation != null) {
+ // If there isn't, might as well strip it
+ if (new WrappedSignature(notation).getNotation().isEmpty()) {
+ log.add(LogType.MSG_KC_NOTATION_EMPTY, indent);
+ modified = PGPPublicKey.removeCertification(modified, notation);
+ redundantCerts += 1;
+ }
+ }
+
ArrayList<String> processedUserIds = new ArrayList<String>();
for (byte[] rawUserId : new IterableIterator<byte[]>(masterKey.getRawUserIDs())) {
String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId);
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 93afb987a..c395ca52d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.SignatureSubpacket;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.Exportable;
+import org.spongycastle.bcpg.sig.NotationData;
import org.spongycastle.bcpg.sig.Revocable;
import org.spongycastle.bcpg.sig.RevocationReason;
import org.spongycastle.openpgp.PGPException;
@@ -37,6 +38,7 @@ import java.io.IOException;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashMap;
/** OpenKeychain wrapper around PGPSignature objects.
*
@@ -239,4 +241,20 @@ public class WrappedSignature {
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(SignatureSubpacketTags.EXPORTABLE);
return ! ((Exportable) p).isExportable();
}
+
+ public HashMap<String,String> getNotation() {
+ HashMap<String,String> result = new HashMap<String,String>();
+
+ // If there is any notation data
+ if (mSig.getHashedSubPackets() != null
+ && mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.NOTATION_DATA)) {
+ // Iterate over notation data
+ for (NotationData data : mSig.getHashedSubPackets().getNotationDataOccurrences()) {
+ result.put(data.getNotationName(), data.getNotationValue());
+ }
+ }
+
+ return result;
+ }
+
}