@Test
  public void testImportSymantec() throws Exception {

    // symantec pgp desktop exports secret keys without self certificates. we don't support
    // those on their own, but if they are imported together with their public key (or if
    // the public key is already known), the self certs info will be merged in as a special
    // case.
    UncachedKeyRing seckey = readRingFromResource("/test-keys/symantec_secret.asc");
    UncachedKeyRing pubkey = readRingFromResource("/test-keys/symantec_public.asc");

    SaveKeyringResult result;

    // insert secret, this should fail because of missing self-cert
    result =
        new ProviderHelper(RuntimeEnvironment.application)
            .saveSecretKeyRing(seckey, new ProgressScaler());
    Assert.assertFalse("secret keyring import before pubring import should fail", result.success());

    // insert pubkey, then seckey - both should succeed
    result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(pubkey);
    Assert.assertTrue("public keyring import should succeed", result.success());
    result =
        new ProviderHelper(RuntimeEnvironment.application)
            .saveSecretKeyRing(seckey, new ProgressScaler());
    Assert.assertTrue(
        "secret keyring import after pubring import should succeed", result.success());
  }
  @Test
  public void testImportCooperPair() throws Exception {

    // insert two keys with same long key id, make sure the second one gets rejected either way!
    UncachedKeyRing first =
        readRingFromResource("/test-keys/cooperpair/9E669861368BCA0BE42DAF7DDDA252EBB8EBE1AF.asc");
    UncachedKeyRing second =
        readRingFromResource("/test-keys/cooperpair/A55120427374F3F7AA5F1166DDA252EBB8EBE1AF.asc");

    SaveKeyringResult result;

    // insert both keys, second should fail
    result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(first);
    Assert.assertTrue("first keyring import should succeed", result.success());
    result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(second);
    Assert.assertFalse("second keyring import should fail", result.success());

    new KeychainDatabase(RuntimeEnvironment.application).clearDatabase();

    // and the other way around
    result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(second);
    Assert.assertTrue("first keyring import should succeed", result.success());
    result = new ProviderHelper(RuntimeEnvironment.application).savePublicKeyRing(first);
    Assert.assertFalse("second keyring import should fail", result.success());
  }
  @Test
  /**
   * Tests a master key which may sign, but is stripped. In this case, if there is a different
   * subkey available which can sign, that one should be selected.
   */
  public void testImportStrippedFlags() throws Exception {

    UncachedKeyRing key = readRingFromResource("/test-keys/stripped_flags.asc");
    long masterKeyId = key.getMasterKeyId();

    SaveKeyringResult result;

    result = mProviderHelper.saveSecretKeyRing(key, new ProgressScaler());
    Assert.assertTrue("import of keyring should succeed", result.success());

    long signId;
    {
      CanonicalizedSecretKeyRing ring = mProviderHelper.getCanonicalizedSecretKeyRing(masterKeyId);
      Assert.assertTrue("master key should have sign flag", ring.getPublicKey().canSign());
      Assert.assertTrue("master key should have encrypt flag", ring.getPublicKey().canEncrypt());

      signId = ring.getSecretSignId();
      Assert.assertNotEquals("encrypt id should not be 0", 0, signId);
      Assert.assertNotEquals(
          "encrypt key should be different from master key", masterKeyId, signId);
    }

    {
      CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(masterKeyId);
      Assert.assertEquals(
          "signing key should be same id cached as uncached", signId, ring.getSecretSignId());
    }
  }
  @Test
  public void testImportBadEncodedUserId() throws Exception {

    UncachedKeyRing key = readRingFromResource("/test-keys/bad_user_id_encoding.asc");
    long keyId = key.getMasterKeyId();

    SaveKeyringResult result;

    result = mProviderHelper.savePublicKeyRing(key, new ProgressScaler(), null);
    Assert.assertTrue("import of keyring should succeed", result.success());

    CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing(keyId);
    boolean found = false;
    byte[] badUserId =
        Hex.decode(
            "436c61757320467261656e6b656c203c436c6175732e4672e46e6b656c4068616c696661782e727774682d61616368656e2e64653e");
    for (byte[] rawUserId :
        new IterableIterator<byte[]>(ring.getUnorderedRawUserIds().iterator())) {
      if (Arrays.equals(rawUserId, badUserId)) {
        found = true;
      }
    }

    Assert.assertTrue("import of the badly encoded user id should succeed", found);
  }
  @NonNull
  public OperationResult execute(ChangeUnlockParcel unlockParcel, CryptoInputParcel cryptoInput) {
    OperationResult.OperationLog log = new OperationResult.OperationLog();
    log.add(OperationResult.LogType.MSG_ED, 0);

    if (unlockParcel == null || unlockParcel.mMasterKeyId == null) {
      log.add(OperationResult.LogType.MSG_ED_ERROR_NO_PARCEL, 1);
      return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
    }

    // Perform actual modification
    PgpEditKeyResult modifyResult;
    {
      PgpKeyOperation keyOperations =
          new PgpKeyOperation(new ProgressScaler(mProgressable, 0, 70, 100));

      try {
        log.add(
            OperationResult.LogType.MSG_ED_FETCHING,
            1,
            KeyFormattingUtils.convertKeyIdToHex(unlockParcel.mMasterKeyId));

        CanonicalizedSecretKeyRing secRing =
            mProviderHelper.getCanonicalizedSecretKeyRing(unlockParcel.mMasterKeyId);
        modifyResult = keyOperations.modifyKeyRingPassphrase(secRing, cryptoInput, unlockParcel);

        if (modifyResult.isPending()) {
          // obtain original passphrase from user
          log.add(modifyResult, 1);
          return new EditKeyResult(log, modifyResult);
        }
      } catch (ProviderHelper.NotFoundException e) {
        log.add(OperationResult.LogType.MSG_ED_ERROR_KEY_NOT_FOUND, 2);
        return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
      }
    }

    log.add(modifyResult, 1);

    if (!modifyResult.success()) {
      // error is already logged by modification
      return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
    }

    // Cannot cancel from here on out!
    mProgressable.setPreventCancel();

    // It's a success, so this must be non-null now
    UncachedKeyRing ring = modifyResult.getRing();

    SaveKeyringResult saveResult =
        mProviderHelper.saveSecretKeyRing(ring, new ProgressScaler(mProgressable, 70, 95, 100));
    log.add(saveResult, 1);

    // If the save operation didn't succeed, exit here
    if (!saveResult.success()) {
      return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
    }

    updateProgress(R.string.progress_done, 100, 100);
    log.add(OperationResult.LogType.MSG_ED_SUCCESS, 0);
    return new EditKeyResult(EditKeyResult.RESULT_OK, log, ring.getMasterKeyId());
  }
  @Test
  public void testImportDivertToCard() throws Exception {

    UncachedKeyRing sec = readRingFromResource("/test-keys/divert_to_card_sec.asc");
    long keyId = sec.getMasterKeyId();

    SaveKeyringResult result;

    result = mProviderHelper.saveSecretKeyRing(sec, new ProgressScaler());
    Assert.assertTrue("import of secret keyring should succeed", result.success());

    // make sure both the CanonicalizedSecretKeyRing as well as the CachedPublicKeyRing correctly
    // indicate the secret key type
    CachedPublicKeyRing cachedRing = mProviderHelper.getCachedPublicKeyRing(keyId);
    CanonicalizedSecretKeyRing secRing = mProviderHelper.getCanonicalizedSecretKeyRing(keyId);

    Iterator<CanonicalizedSecretKey> it = secRing.secretKeyIterator().iterator();

    { // first subkey
      Assert.assertTrue("keyring should have 3 subkeys (1)", it.hasNext());
      CanonicalizedSecretKey key = it.next();
      Assert.assertEquals(
          "first subkey should be of type sign+certify",
          KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA,
          (int) key.getKeyUsage());
      Assert.assertEquals(
          "first subkey should be divert-to-card",
          SecretKeyType.DIVERT_TO_CARD,
          key.getSecretKeyType());
      Assert.assertTrue("canCertify() should be true", key.canCertify());
      Assert.assertTrue("canSign() should be true", key.canSign());

      // cached
      Assert.assertEquals(
          "all subkeys from CachedPublicKeyRing should be divert-to-key",
          SecretKeyType.DIVERT_TO_CARD,
          cachedRing.getSecretKeyType(key.getKeyId()));
    }

    { // second subkey
      Assert.assertTrue("keyring should have 3 subkeys (2)", it.hasNext());
      CanonicalizedSecretKey key = it.next();
      Assert.assertEquals(
          "second subkey should be of type authenticate",
          KeyFlags.AUTHENTICATION,
          (int) key.getKeyUsage());
      Assert.assertEquals(
          "second subkey should be divert-to-card",
          SecretKeyType.DIVERT_TO_CARD,
          key.getSecretKeyType());
      Assert.assertTrue("canAuthenticate() should be true", key.canAuthenticate());

      // cached
      Assert.assertEquals(
          "all subkeys from CachedPublicKeyRing should be divert-to-key",
          SecretKeyType.DIVERT_TO_CARD,
          cachedRing.getSecretKeyType(key.getKeyId()));
    }

    { // third subkey
      Assert.assertTrue("keyring should have 3 subkeys (3)", it.hasNext());
      CanonicalizedSecretKey key = it.next();
      Assert.assertEquals(
          "first subkey should be of type encrypt (both types)",
          KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE,
          (int) key.getKeyUsage());
      Assert.assertEquals(
          "third subkey should be divert-to-card",
          SecretKeyType.DIVERT_TO_CARD,
          key.getSecretKeyType());
      Assert.assertTrue("canEncrypt() should be true", key.canEncrypt());

      // cached
      Assert.assertEquals(
          "all subkeys from CachedPublicKeyRing should be divert-to-key",
          SecretKeyType.DIVERT_TO_CARD,
          cachedRing.getSecretKeyType(key.getKeyId()));
    }

    Assert.assertFalse("keyring should have 3 subkeys (4)", it.hasNext());
  }