mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-27 11:12:15 -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;
|
||||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||||
|
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||||
import org.sufficientlysecure.keychain.util.TestingUtils;
|
import org.sufficientlysecure.keychain.util.TestingUtils;
|
||||||
@ -104,7 +105,7 @@ public class PromoteKeyOperationTest {
|
|||||||
PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application,
|
PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application,
|
||||||
new ProviderHelper(Robolectric.application), null, null);
|
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());
|
Assert.assertTrue("promotion must succeed", result.success());
|
||||||
|
|
||||||
@ -130,7 +131,7 @@ public class PromoteKeyOperationTest {
|
|||||||
|
|
||||||
byte[] aid = Hex.decode("D2760001240102000000012345670000");
|
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());
|
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.PgpEditKeyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
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.ui.util.KeyFormattingUtils;
|
||||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/** An operation which promotes a public key ring to a secret one.
|
/** 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);
|
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();
|
OperationLog log = new OperationLog();
|
||||||
log.add(LogType.MSG_PR, 0);
|
log.add(LogType.MSG_PR, 0);
|
||||||
@ -65,8 +67,24 @@ public class PromoteKeyOperation extends BaseOperation {
|
|||||||
CanonicalizedPublicKeyRing pubRing =
|
CanonicalizedPublicKeyRing pubRing =
|
||||||
mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
|
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
|
// create divert-to-card secret key from public key
|
||||||
promotedRing = pubRing.createDivertSecretRing(cardAid);
|
promotedRing = pubRing.createDivertSecretRing(cardAid, subKeyIds);
|
||||||
|
|
||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
|
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
|
// promote key
|
||||||
MSG_PR (LogLevel.START, R.string.msg_pr),
|
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_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_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),
|
MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success),
|
||||||
|
|
||||||
// messages used in UI code
|
// messages used in UI code
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
import org.spongycastle.openpgp.PGPKeyRing;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
|
|
||||||
@ -127,7 +128,11 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CanonicalizedPublicKey getPublicKey(long id) {
|
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 {
|
public byte[] getEncoded() throws IOException {
|
||||||
|
@ -103,9 +103,22 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Create a dummy secret ring from this key */
|
/** 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);
|
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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -65,7 +65,6 @@ import org.sufficientlysecure.keychain.util.FileHelper;
|
|||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -185,6 +184,7 @@ public class KeychainIntentService extends IntentService implements Progressable
|
|||||||
// promote key
|
// promote key
|
||||||
public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
|
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_CARD_AID = "promote_card_aid";
|
||||||
|
public static final String PROMOTE_SUBKEY_IDS = "promote_fingerprints";
|
||||||
|
|
||||||
// consolidate
|
// consolidate
|
||||||
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
|
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
|
||||||
@ -476,10 +476,12 @@ public class KeychainIntentService extends IntentService implements Progressable
|
|||||||
// Input
|
// Input
|
||||||
long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
|
long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
|
||||||
byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
|
byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
|
||||||
|
long[] subKeyIds = data.getLongArray(PROMOTE_SUBKEY_IDS);
|
||||||
|
|
||||||
// Operation
|
// Operation
|
||||||
PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled);
|
PromoteKeyOperation op = new PromoteKeyOperation(
|
||||||
PromoteKeyResult result = op.execute(keyRingId, cardAid);
|
this, providerHelper, this, mActionCanceled);
|
||||||
|
PromoteKeyResult result = op.execute(keyRingId, cardAid, subKeyIds);
|
||||||
|
|
||||||
// Result
|
// Result
|
||||||
sendMessageToHandler(MessageStatus.OKAY, 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.provider.KeychainContract.Keys;
|
||||||
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
import org.sufficientlysecure.keychain.service.KeychainIntentService;
|
||||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||||
|
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||||
|
|
||||||
|
|
||||||
public class ViewKeyYubiKeyFragment extends Fragment
|
public class ViewKeyYubiKeyFragment extends Fragment
|
||||||
implements LoaderCallbacks<Cursor> {
|
implements LoaderCallbacks<Cursor> {
|
||||||
@ -154,6 +156,11 @@ public class ViewKeyYubiKeyFragment extends Fragment
|
|||||||
Bundle data = new Bundle();
|
Bundle data = new Bundle();
|
||||||
data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
|
data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
|
||||||
data.putByteArray(KeychainIntentService.PROMOTE_CARD_AID, mCardAid);
|
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);
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
|
|
||||||
// Create a new Messenger for the communication back
|
// 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++) {
|
for (int i = 0; i < haystack.length; i++) {
|
||||||
if (Arrays.equals(needle, haystack[i])) {
|
if (Arrays.equals(needle, haystack[i])) {
|
||||||
return i;
|
return i;
|
||||||
|
@ -996,8 +996,11 @@
|
|||||||
|
|
||||||
<!-- Promote key -->
|
<!-- Promote key -->
|
||||||
<string name="msg_pr">"Promoting public key to secret key"</string>
|
<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_error_key_not_found">"Key not found!"</string>
|
||||||
<string name="msg_pr_fetching">"Fetching key to modify (%s)"</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>
|
<string name="msg_pr_success">"Key successfully promoted"</string>
|
||||||
|
|
||||||
<!-- Other messages used in OperationLogs -->
|
<!-- Other messages used in OperationLogs -->
|
||||||
|
Loading…
Reference in New Issue
Block a user