mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-23 17:22:16 -05:00
apply promote operation to specific subkeys present on yubikey only
This commit is contained in:
parent
71818934ca
commit
158263f255
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -558,8 +558,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
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -996,8 +996,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 -->
|
||||
|
Loading…
Reference in New Issue
Block a user