Skip to content

Commit

Permalink
apply promote operation to specific subkeys present on yubikey only
Browse files Browse the repository at this point in the history
  • Loading branch information
Valodim committed May 16, 2015
1 parent f554cc9 commit c1e7fcf
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
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;
Expand Down Expand Up @@ -104,7 +105,7 @@ public void testPromote() throws Exception {
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());

Expand All @@ -130,7 +131,7 @@ public void testPromoteDivert() throws Exception {

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());

Expand All @@ -147,4 +148,40 @@ public void testPromoteDivert() throws Exception {

}
}

@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());
}
}

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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;
Expand All @@ -34,6 +35,7 @@
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.
Expand All @@ -50,7 +52,7 @@ public PromoteKeyOperation(Context context, ProviderHelper providerHelper,
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);
Expand All @@ -65,8 +67,24 @@ public PromoteKeyResult execute(long masterKeyId, byte[] cardAid) {
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);
Expand Down Expand Up @@ -106,4 +124,13 @@ public PromoteKeyResult execute(long masterKeyId, byte[] cardAid) {

}

static private Integer naiveIndexOf(long[] haystack, long needle) {
for (int i = 0; i < haystack.length; i++) {
if (needle == haystack[i]) {
return i;
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,11 @@ public static enum LogType {

// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -127,7 +128,11 @@ public CanonicalizedPublicKey getPublicKey() {
}

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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,22 @@ public UncachedKeyRing createDummySecretRing () {
}

/** 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);

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
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;
Expand Down Expand Up @@ -185,6 +184,7 @@ public static IOType fromInt(int n) {
// 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";
Expand Down Expand Up @@ -476,10 +476,12 @@ this, new ProviderHelper(this), this,
// 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
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> {
Expand Down Expand Up @@ -154,6 +156,11 @@ public void handleMessage(Message message) {
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
Expand Down Expand Up @@ -219,7 +226,7 @@ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

}

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;
Expand Down
3 changes: 3 additions & 0 deletions OpenKeychain/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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 -->
Expand Down

0 comments on commit c1e7fcf

Please sign in to comment.