From 036480a210f0a2439a1c8dfbd06d32180fb26f8c Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Mon, 13 Jan 2014 14:55:59 +0000 Subject: [PATCH 1/6] small tidy --- .../org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index e07c802b7..225167fce 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -378,7 +378,7 @@ public class PgpKeyOperation { keyFlags |= KeyFlags.SIGN_DATA; //cross-certify signing keys PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - subKey.getPublicKey().getAlgorithm(), PGPUtil.SHA1) + subPublicKey.getAlgorithm(), PGPUtil.SHA1) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); From 2b71b12b24e6f5c8f8193513ae0d81fb159d519d Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 14 Jan 2014 00:30:58 +0000 Subject: [PATCH 2/6] subkey binding check, start primary binding check --- .../keychain/pgp/PgpOperation.java | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java index 755537012..804d22187 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java @@ -34,6 +34,8 @@ import java.util.Iterator; import org.spongycastle.bcpg.ArmoredInputStream; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.SignatureSubpacket; +import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.openpgp.PGPCompressedData; import org.spongycastle.openpgp.PGPCompressedDataGenerator; import org.spongycastle.openpgp.PGPEncryptedData; @@ -56,6 +58,7 @@ import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.PGPV3SignatureGenerator; import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; @@ -887,7 +890,50 @@ public class PgpOperation { } while (lookAhead != -1); } - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); + boolean sig_isok = signature.verify(); + + //We should only do the next part if the singing key was a subkey - not the master key! + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext, + signatureKeyId); + PGPPublicKey mKey = null; + if (signKeyRing != null) { + mKey = PgpKeyHelper.getMasterKey(signKeyRing); + } + Iterator itr = signatureKey.getSignatures(); + + boolean subkeyBinding_isok = false; + boolean tmp_subkeyBinding_isok = false; + boolean primkeyBinding_isok = false; + while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong? + //gpg has an invalid subkey binding error on key import I think, but doesn't shout + //about keys without subkey signing. Can't get it to import a slightly broken one + //either, so we will err on bad subkey binding here. + PGPSignature sig = itr.next(); + if (sig.getKeyID() == mKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) { + //check and if ok, check primary key binding. + sig.init(contentVerifierBuilderProvider, mKey); + tmp_subkeyBinding_isok = sig.verifyCertification(mKey, signatureKey); + if (tmp_subkeyBinding_isok) + subkeyBinding_isok = true; + if (tmp_subkeyBinding_isok) { + PGPSignatureSubpacketVector hPkts = sig.getHashedSubPackets(); + PGPSignatureSubpacketVector uhPkts = sig.getUnhashedSubPackets(); + if (hPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { + SignatureSubpacket[] subsigpkts = hPkts.getSubpackets(SignatureSubpacketTags.EMBEDDED_SIGNATURE); + PGPSignature[] vals = new PGPSignature[subsigpkts.length]; + for (int i = 0; i < subsigpkts.length; i++) + { + vals[i] = (PGPSignature)subsigpkts[i]; + } + } + if (uhPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { + } + } + } + } + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & subkeyBinding_isok); updateProgress(R.string.progress_done, 100, 100); return returnData; From bb0baa815e224004a72bee5ff1fad46c530df5d8 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 14 Jan 2014 15:03:35 +0000 Subject: [PATCH 3/6] check primary bindings --- .../libs/scpg-jdk15on-1.47.0.3-SNAPSHOT.jar | Bin 279347 -> 279854 bytes .../keychain/pgp/PgpOperation.java | 73 +++++++++++------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/OpenPGP-Keychain/libs/scpg-jdk15on-1.47.0.3-SNAPSHOT.jar b/OpenPGP-Keychain/libs/scpg-jdk15on-1.47.0.3-SNAPSHOT.jar index 9d280222ec987aba4325b88e1e07b522a7eb9461..cb55406fad36d7f9723545cc2be39830079d53ad 100644 GIT binary patch delta 17431 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 delta 16835 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 itr = signatureKey.getSignatures(); + if (signature.getKeyID() != mKey.getKeyID()) { + Iterator itr = signatureKey.getSignatures(); - boolean subkeyBinding_isok = false; - boolean tmp_subkeyBinding_isok = false; - boolean primkeyBinding_isok = false; - while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong? - //gpg has an invalid subkey binding error on key import I think, but doesn't shout - //about keys without subkey signing. Can't get it to import a slightly broken one - //either, so we will err on bad subkey binding here. - PGPSignature sig = itr.next(); - if (sig.getKeyID() == mKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) { - //check and if ok, check primary key binding. - sig.init(contentVerifierBuilderProvider, mKey); - tmp_subkeyBinding_isok = sig.verifyCertification(mKey, signatureKey); - if (tmp_subkeyBinding_isok) - subkeyBinding_isok = true; - if (tmp_subkeyBinding_isok) { - PGPSignatureSubpacketVector hPkts = sig.getHashedSubPackets(); - PGPSignatureSubpacketVector uhPkts = sig.getUnhashedSubPackets(); - if (hPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { - SignatureSubpacket[] subsigpkts = hPkts.getSubpackets(SignatureSubpacketTags.EMBEDDED_SIGNATURE); - PGPSignature[] vals = new PGPSignature[subsigpkts.length]; - for (int i = 0; i < subsigpkts.length; i++) - { - vals[i] = (PGPSignature)subsigpkts[i]; - } - } - if (uhPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { + subkeyBinding_isok = false; + tmp_subkeyBinding_isok = false; + primkeyBinding_isok = false; + while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong? + //gpg has an invalid subkey binding error on key import I think, but doesn't shout + //about keys without subkey signing. Can't get it to import a slightly broken one + //either, so we will err on bad subkey binding here. + PGPSignature sig = itr.next(); + if (sig.getKeyID() == mKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) { + //check and if ok, check primary key binding. + sig.init(contentVerifierBuilderProvider, mKey); + tmp_subkeyBinding_isok = sig.verifyCertification(mKey, signatureKey); + if (tmp_subkeyBinding_isok) + subkeyBinding_isok = true; + if (tmp_subkeyBinding_isok) { + PGPSignatureSubpacketVector hPkts = sig.getHashedSubPackets(); + PGPSignatureSubpacketVector uhPkts = sig.getUnhashedSubPackets(); + if (uhPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { + PGPSignatureList eSigList = uhPkts.getEmbeddedSignatures(); + for (int j = 0; j < eSigList.size(); ++j) { + PGPSignature emSig = eSigList.get(j); + emSig.init(contentVerifierBuilderProvider, signatureKey); + primkeyBinding_isok = emSig.verifyCertification(mKey, signatureKey); + if (primkeyBinding_isok) + break; + } + } + if (hPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { + } } } } + } else { //if the key used to make the signature was the master key, no need to check binding sigs + subkeyBinding_isok = true; + primkeyBinding_isok = true; } - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & subkeyBinding_isok); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & subkeyBinding_isok & primkeyBinding_isok); updateProgress(R.string.progress_done, 100, 100); return returnData; From cd4a3dd2377d64f73da08711007e89c4733f9cef Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 14 Jan 2014 15:48:05 +0000 Subject: [PATCH 4/6] begin refactor --- .../keychain/pgp/PgpOperation.java | 54 ++++++++++++++----- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java index 6f0821fdc..1525c9462 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java @@ -927,20 +927,12 @@ public class PgpOperation { if (tmp_subkeyBinding_isok) subkeyBinding_isok = true; if (tmp_subkeyBinding_isok) { - PGPSignatureSubpacketVector hPkts = sig.getHashedSubPackets(); - PGPSignatureSubpacketVector uhPkts = sig.getUnhashedSubPackets(); - if (uhPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { - PGPSignatureList eSigList = uhPkts.getEmbeddedSignatures(); - for (int j = 0; j < eSigList.size(); ++j) { - PGPSignature emSig = eSigList.get(j); - emSig.init(contentVerifierBuilderProvider, signatureKey); - primkeyBinding_isok = emSig.verifyCertification(mKey, signatureKey); - if (primkeyBinding_isok) - break; - } - } - if (hPkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { - } + primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), mKey, signatureKey); + if (primkeyBinding_isok) + break; + primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), mKey, signatureKey); + if (primkeyBinding_isok) + break; } } } @@ -954,6 +946,40 @@ public class PgpOperation { return returnData; } + private boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) + { + boolean primkeyBinding_isok = false; + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureList eSigList; + + if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { + try { + eSigList = Pkts.getEmbeddedSignatures(); + } catch (IOException e) { + return false; + } catch (PGPException e) { + return false; + } + for (int j = 0; j < eSigList.size(); ++j) { + PGPSignature emSig = eSigList.get(j); + if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { + try { + emSig.init(contentVerifierBuilderProvider, signingPublicKey); + primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey); + if (primkeyBinding_isok) + break; + } catch (PGPException e) { + continue; + } catch (SignatureException e) { + continue; + } + } + } + } + return primkeyBinding_isok; + } + private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput, final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException { From c95a52c07041371f54d6315bddc8a60fcac69245 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 14 Jan 2014 22:12:57 +0000 Subject: [PATCH 5/6] refactor --- .../keychain/pgp/PgpOperation.java | 80 +++++++++++-------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java index 1525c9462..90934cbd9 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java @@ -897,9 +897,7 @@ public class PgpOperation { boolean sig_isok = signature.verify(); //Now check binding signatures - boolean subkeyBinding_isok = false; - boolean tmp_subkeyBinding_isok = false; - boolean primkeyBinding_isok = false; + boolean keyBinding_isok = false; signatureKeyId = signature.getKeyID(); String userId = null; @@ -910,42 +908,60 @@ public class PgpOperation { mKey = PgpKeyHelper.getMasterKey(signKeyRing); } if (signature.getKeyID() != mKey.getKeyID()) { - Iterator itr = signatureKey.getSignatures(); - - subkeyBinding_isok = false; - tmp_subkeyBinding_isok = false; - primkeyBinding_isok = false; - while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong? - //gpg has an invalid subkey binding error on key import I think, but doesn't shout - //about keys without subkey signing. Can't get it to import a slightly broken one - //either, so we will err on bad subkey binding here. - PGPSignature sig = itr.next(); - if (sig.getKeyID() == mKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) { - //check and if ok, check primary key binding. - sig.init(contentVerifierBuilderProvider, mKey); - tmp_subkeyBinding_isok = sig.verifyCertification(mKey, signatureKey); - if (tmp_subkeyBinding_isok) - subkeyBinding_isok = true; - if (tmp_subkeyBinding_isok) { - primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), mKey, signatureKey); - if (primkeyBinding_isok) - break; - primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), mKey, signatureKey); - if (primkeyBinding_isok) - break; - } - } - } + keyBinding_isok = verifyKeyBinding(mKey, signatureKey); } else { //if the key used to make the signature was the master key, no need to check binding sigs - subkeyBinding_isok = true; - primkeyBinding_isok = true; + keyBinding_isok = true; } - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & subkeyBinding_isok & primkeyBinding_isok); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok); updateProgress(R.string.progress_done, 100, 100); return returnData; } + private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) + { + boolean subkeyBinding_isok = false; + boolean tmp_subkeyBinding_isok = false; + boolean primkeyBinding_isok = false; + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + + Iterator itr = signingPublicKey.getSignatures(); + + subkeyBinding_isok = false; + tmp_subkeyBinding_isok = false; + primkeyBinding_isok = false; + while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong? + //gpg has an invalid subkey binding error on key import I think, but doesn't shout + //about keys without subkey signing. Can't get it to import a slightly broken one + //either, so we will err on bad subkey binding here. + PGPSignature sig = itr.next(); + if (sig.getKeyID() == masterPublicKey.getKeyID() && sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) { + //check and if ok, check primary key binding. + try { + sig.init(contentVerifierBuilderProvider, masterPublicKey); + tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey); + } catch (PGPException e) { + continue; + } catch (SignatureException e) { + continue; + } + + if (tmp_subkeyBinding_isok) + subkeyBinding_isok = true; + if (tmp_subkeyBinding_isok) { + primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey); + if (primkeyBinding_isok) + break; + primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey); + if (primkeyBinding_isok) + break; + } + } + } + return (subkeyBinding_isok & primkeyBinding_isok); + } + private boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) { boolean primkeyBinding_isok = false; From 0bca0a4b08fc0be62c64d7f8b8185cf6db620ead Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Wed, 15 Jan 2014 00:41:18 +0000 Subject: [PATCH 6/6] always check binding when verifying --- .../keychain/pgp/PgpOperation.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java index 90934cbd9..de1973702 100644 --- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpOperation.java @@ -764,11 +764,11 @@ public class PgpOperation { PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); PGPSignature messageSignature = signatureList.get(signatureIndex); - if (signature.verify(messageSignature)) { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, true); - } else { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); - } + + //Now check binding signatures + boolean keyBinding_isok = verifyKeyBinding(mContext, messageSignature, signatureKey); + boolean sig_isok = signature.verify(messageSignature); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok); } } @@ -897,9 +897,18 @@ public class PgpOperation { boolean sig_isok = signature.verify(); //Now check binding signatures - boolean keyBinding_isok = false; + boolean keyBinding_isok = verifyKeyBinding(mContext, signature, signatureKey); - signatureKeyId = signature.getKeyID(); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok); + + updateProgress(R.string.progress_done, 100, 100); + return returnData; + } + + public boolean verifyKeyBinding(Context mContext, PGPSignature signature, PGPPublicKey signatureKey) + { + long signatureKeyId = signature.getKeyID(); + boolean keyBinding_isok = false; String userId = null; PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext, signatureKeyId); @@ -912,13 +921,10 @@ public class PgpOperation { } else { //if the key used to make the signature was the master key, no need to check binding sigs keyBinding_isok = true; } - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok); - - updateProgress(R.string.progress_done, 100, 100); - return returnData; + return keyBinding_isok; } - private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) + public boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) { boolean subkeyBinding_isok = false; boolean tmp_subkeyBinding_isok = false;