aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-05-17 00:35:10 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2015-05-17 00:35:10 +0200
commitc1e7fcf02455b7a03c86bb78efe39684396c15f8 (patch)
tree40e89ce3bccf981f698862beccb7685a6b55cbdd
parentf554cc9c931f46f1017c2a34fbb776c456e9e344 (diff)
downloadopen-keychain-c1e7fcf02455b7a03c86bb78efe39684396c15f8.tar.gz
open-keychain-c1e7fcf02455b7a03c86bb78efe39684396c15f8.tar.bz2
open-keychain-c1e7fcf02455b7a03c86bb78efe39684396c15f8.zip
apply promote operation to specific subkeys present on yubikey only
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java31
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java9
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml3
8 files changed, 108 insertions, 11 deletions
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
index 617b5762c..2ca904656 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java
@@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils;
@@ -104,7 +105,7 @@ public class PromoteKeyOperationTest {
PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application,
new ProviderHelper(Robolectric.application), null, null);
- PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), null);
+ PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), null, null);
Assert.assertTrue("promotion must succeed", result.success());
@@ -130,7 +131,7 @@ public class PromoteKeyOperationTest {
byte[] aid = Hex.decode("D2760001240102000000012345670000");
- PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid);
+ PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid, null);
Assert.assertTrue("promotion must succeed", result.success());
@@ -147,4 +148,40 @@ public class PromoteKeyOperationTest {
}
}
+
+ @Test
+ public void testPromoteDivertSpecific() throws Exception {
+ PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application,
+ new ProviderHelper(Robolectric.application), null, null);
+
+ byte[] aid = Hex.decode("D2760001240102000000012345670000");
+
+ // only promote the first, rest stays dummy
+ long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1);
+
+ PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid, new long[] {
+ keyId
+ });
+
+ Assert.assertTrue("promotion must succeed", result.success());
+
+ {
+ CanonicalizedSecretKeyRing ring = new ProviderHelper(Robolectric.application)
+ .getCanonicalizedSecretKeyRing(mStaticRing.getMasterKeyId());
+
+ for (CanonicalizedSecretKey key : ring.secretKeyIterator()) {
+ if (key.getKeyId() == keyId) {
+ Assert.assertEquals("subkey must be divert-to-card",
+ SecretKeyType.DIVERT_TO_CARD, key.getSecretKeyType());
+ Assert.assertArrayEquals("subkey must have correct iv",
+ aid, key.getIv());
+ } else {
+ Assert.assertEquals("some subkeys must be gnu dummy",
+ SecretKeyType.GNU_DUMMY, key.getSecretKeyType());
+ }
+ }
+
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
index ef08b0b77..558756378 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java
@@ -25,6 +25,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
@@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ProgressScaler;
+import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
/** An operation which promotes a public key ring to a secret one.
@@ -50,7 +52,7 @@ public class PromoteKeyOperation extends BaseOperation {
super(context, providerHelper, progressable, cancelled);
}
- public PromoteKeyResult execute(long masterKeyId, byte[] cardAid) {
+ public PromoteKeyResult execute(long masterKeyId, byte[] cardAid, long[] subKeyIds) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_PR, 0);
@@ -65,8 +67,24 @@ public class PromoteKeyOperation extends BaseOperation {
CanonicalizedPublicKeyRing pubRing =
mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
+ if (subKeyIds == null) {
+ log.add(LogType.MSG_PR_ALL, 1);
+ } else {
+ // sort for binary search
+ for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) {
+ long subKeyId = key.getKeyId();
+ if (naiveIndexOf(subKeyIds, subKeyId) != null) {
+ log.add(LogType.MSG_PR_SUBKEY_MATCH, 1,
+ KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ } else {
+ log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1,
+ KeyFormattingUtils.convertKeyIdToHex(subKeyId));
+ }
+ }
+ }
+
// create divert-to-card secret key from public key
- promotedRing = pubRing.createDivertSecretRing(cardAid);
+ promotedRing = pubRing.createDivertSecretRing(cardAid, subKeyIds);
} catch (NotFoundException e) {
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
@@ -106,4 +124,13 @@ public class PromoteKeyOperation extends BaseOperation {
}
+ static private Integer naiveIndexOf(long[] haystack, long needle) {
+ for (int i = 0; i < haystack.length; i++) {
+ if (needle == haystack[i]) {
+ return i;
+ }
+ }
+ return null;
+ }
+
}
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 7f36aeb08..f7b1ca0b4 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
@@ -566,8 +566,11 @@ public abstract class OperationResult implements Parcelable {
// promote key
MSG_PR (LogLevel.START, R.string.msg_pr),
+ MSG_PR_ALL (LogLevel.DEBUG, R.string.msg_pr_all),
MSG_PR_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_pr_error_key_not_found),
MSG_PR_FETCHING (LogLevel.DEBUG, R.string.msg_pr_fetching),
+ MSG_PR_SUBKEY_MATCH (LogLevel.DEBUG, R.string.msg_pr_subkey_match),
+ MSG_PR_SUBKEY_NOMATCH (LogLevel.WARN, R.string.msg_pr_subkey_nomatch),
MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success),
// messages used in UI code
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index 4adacaf23..432ba23e9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -19,6 +19,7 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.IterableIterator;
@@ -127,7 +128,11 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
}
public CanonicalizedPublicKey getPublicKey(long id) {
- return new CanonicalizedPublicKey(this, getRing().getPublicKey(id));
+ PGPPublicKey pubKey = getRing().getPublicKey(id);
+ if (pubKey == null) {
+ return null;
+ }
+ return new CanonicalizedPublicKey(this, pubKey);
}
public byte[] getEncoded() throws IOException {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 8432b8f9f..68fd4a428 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -103,9 +103,22 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
}
/** Create a dummy secret ring from this key */
- public UncachedKeyRing createDivertSecretRing (byte[] cardAid) {
+ public UncachedKeyRing createDivertSecretRing (byte[] cardAid, long[] subKeyIds) {
PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), cardAid);
- return new UncachedKeyRing(secRing);
+
+ if (subKeyIds == null) {
+ return new UncachedKeyRing(secRing);
+ }
+
+ // if only specific subkeys should be promoted, construct a
+ // stripped dummy, then move divert-to-card keys over
+ PGPSecretKeyRing newRing = PGPSecretKeyRing.constructDummyFromPublic(getRing());
+ for (long subKeyId : subKeyIds) {
+ newRing = PGPSecretKeyRing.insertSecretKey(newRing, secRing.getSecretKey(subKeyId));
+ }
+
+ return new UncachedKeyRing(newRing);
+
}
} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index e0509ac9b..71e149672 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -65,7 +65,6 @@ import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -185,6 +184,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// promote key
public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
public static final String PROMOTE_CARD_AID = "promote_card_aid";
+ public static final String PROMOTE_SUBKEY_IDS = "promote_fingerprints";
// consolidate
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
@@ -476,10 +476,12 @@ public class KeychainIntentService extends IntentService implements Progressable
// Input
long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
+ long[] subKeyIds = data.getLongArray(PROMOTE_SUBKEY_IDS);
// Operation
- PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled);
- PromoteKeyResult result = op.execute(keyRingId, cardAid);
+ PromoteKeyOperation op = new PromoteKeyOperation(
+ this, providerHelper, this, mActionCanceled);
+ PromoteKeyResult result = op.execute(keyRingId, cardAid, subKeyIds);
// Result
sendMessageToHandler(MessageStatus.OKAY, result);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
index 99ac73800..ecd351965 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java
@@ -45,6 +45,8 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+
public class ViewKeyYubiKeyFragment extends Fragment
implements LoaderCallbacks<Cursor> {
@@ -154,6 +156,11 @@ public class ViewKeyYubiKeyFragment extends Fragment
Bundle data = new Bundle();
data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
data.putByteArray(KeychainIntentService.PROMOTE_CARD_AID, mCardAid);
+ long[] subKeyIds = new long[mFingerprints.length];
+ for (int i = 0; i < subKeyIds.length; i++) {
+ subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]);
+ }
+ data.putLongArray(KeychainIntentService.PROMOTE_SUBKEY_IDS, subKeyIds);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
// Create a new Messenger for the communication back
@@ -219,7 +226,7 @@ public class ViewKeyYubiKeyFragment extends Fragment
}
- public Integer naiveIndexOf(byte[][] haystack, byte[] needle) {
+ static private Integer naiveIndexOf(byte[][] haystack, byte[] needle) {
for (int i = 0; i < haystack.length; i++) {
if (Arrays.equals(needle, haystack[i])) {
return i;
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index f5f58d01e..45e286595 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -1005,8 +1005,11 @@
<!-- Promote key -->
<string name="msg_pr">"Promoting public key to secret key"</string>
+ <string name="msg_pr_all">"Promoting all subkeysp"</string>
<string name="msg_pr_error_key_not_found">"Key not found!"</string>
<string name="msg_pr_fetching">"Fetching key to modify (%s)"</string>
+ <string name="msg_pr_subkey_match">"Promoting subkey: %s"</string>
+ <string name="msg_pr_subkey_nomatch">"Subkey not on Yubikey: %s"</string>
<string name="msg_pr_success">"Key successfully promoted"</string>
<!-- Other messages used in OperationLogs -->