diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org')
9 files changed, 147 insertions, 15 deletions
| diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index dc45fabc3..606dd49d5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -359,6 +359,7 @@ public abstract class OperationResult implements Parcelable {          MSG_IS_SUBKEY_STRIPPED (LogLevel.DEBUG, R.string.msg_is_subkey_stripped),          MSG_IS_SUBKEY_DIVERT (LogLevel.DEBUG, R.string.msg_is_subkey_divert),          MSG_IS_SUBKEY_EMPTY (LogLevel.DEBUG, R.string.msg_is_subkey_empty), +        MSG_IS_SUBKEY_PIN (LogLevel.DEBUG, R.string.msg_is_subkey_pin),          MSG_IS_SUCCESS_IDENTICAL (LogLevel.OK, R.string.msg_is_success_identical),          MSG_IS_SUCCESS (LogLevel.OK, R.string.msg_is_success), @@ -370,13 +371,15 @@ public abstract class OperationResult implements Parcelable {          MSG_KC_ERROR_MASTER_ALGO (LogLevel.ERROR, R.string.msg_kc_error_master_algo),          MSG_KC_ERROR_DUP_KEY (LogLevel.ERROR, R.string.msg_kc_error_dup_key),          MSG_KC_MASTER (LogLevel.DEBUG, R.string.msg_kc_master), +        MSG_KC_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_bad_type),          MSG_KC_REVOKE_BAD_ERR (LogLevel.WARN, R.string.msg_kc_revoke_bad_err),          MSG_KC_REVOKE_BAD_LOCAL (LogLevel.WARN, R.string.msg_kc_revoke_bad_local),          MSG_KC_REVOKE_BAD_TIME (LogLevel.WARN, R.string.msg_kc_revoke_bad_time), -        MSG_KC_REVOKE_BAD_TYPE (LogLevel.WARN, R.string.msg_kc_revoke_bad_type),          MSG_KC_REVOKE_BAD_TYPE_UID (LogLevel.WARN, R.string.msg_kc_revoke_bad_type_uid),          MSG_KC_REVOKE_BAD (LogLevel.WARN, R.string.msg_kc_revoke_bad),          MSG_KC_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_revoke_dup), +        MSG_KC_NOTATION_DUP (LogLevel.DEBUG, R.string.msg_kc_notation_dup), +        MSG_KC_NOTATION_EMPTY (LogLevel.DEBUG, R.string.msg_kc_notation_empty),          MSG_KC_SUB (LogLevel.DEBUG, R.string.msg_kc_sub),          MSG_KC_SUB_BAD(LogLevel.WARN, R.string.msg_kc_sub_bad),          MSG_KC_SUB_BAD_ERR(LogLevel.WARN, R.string.msg_kc_sub_bad_err), 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; +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 05dc99c5e..6daa26cd3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -682,6 +682,11 @@ public class ProviderHelper {                                          KeyFormattingUtils.convertKeyIdToHex(id)                                  );                                  break; +                            case PIN: +                                log(LogType.MSG_IS_SUBKEY_PIN, +                                        KeyFormattingUtils.convertKeyIdToHex(id) +                                ); +                                break;                              case GNU_DUMMY:                                  log(LogType.MSG_IS_SUBKEY_STRIPPED,                                          KeyFormattingUtils.convertKeyIdToHex(id) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index f5d4e5bd4..810190fee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -254,6 +254,9 @@ public class SaveKeyringParcel implements Parcelable {          // A new pin to use. Must only contain [0-9]+          public final String mNewPin; +        public ChangeUnlockParcel(String newPassphrase) { +            this(newPassphrase, null); +        }          public ChangeUnlockParcel(String newPassphrase, String newPin) {              if (newPassphrase == null && newPin == null) {                  throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 4c65aa314..cd564551a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -208,6 +208,9 @@ public class PassphraseDialogActivity extends FragmentActivity {                          case PASSPHRASE:                              message = getString(R.string.passphrase_for, userId);                              break; +                        case PIN: +                            message = getString(R.string.pin_for, userId); +                            break;                          case DIVERT_TO_CARD:                              message = getString(R.string.yubikey_pin_for, userId);                              break; | 
