token-import: use entire fingerprint for promote operation

This commit is contained in:
Vincent Breitmoser 2017-09-11 14:54:06 +02:00
parent 10eeb5672b
commit 863651918e
11 changed files with 80 additions and 89 deletions

View file

@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context;
@ -71,30 +72,24 @@ public class PromoteKeyOperation extends BaseReadWriteOperation<PromoteKeyringPa
CanonicalizedPublicKeyRing pubRing =
mKeyRepository.getCanonicalizedPublicKeyRing(promoteKeyringParcel.getMasterKeyId());
long[] subKeyIds = promoteKeyringParcel.getSubKeyIds();
if (subKeyIds == null) {
List<byte[]> fingerprints = promoteKeyringParcel.getFingerprints();
if (fingerprints == null) {
log.add(LogType.MSG_PR_ALL, 1);
} else {
// sort for binary search
for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) {
long subKeyId = key.getKeyId();
// we ignore key ids from empty fingerprints here
if (subKeyId == 0L) {
continue;
}
if (naiveIndexOf(subKeyIds, subKeyId) != null) {
log.add(LogType.MSG_PR_SUBKEY_MATCH, 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
if (naiveArraySearch(fingerprints, key.getFingerprint())) {
log.add(LogType.MSG_PR_SUBKEY_MATCH, 1, KeyFormattingUtils.convertKeyIdToHex(subKeyId));
} else {
log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1,
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1, KeyFormattingUtils.convertKeyIdToHex(subKeyId));
}
}
}
// create divert-to-card secret key from public key
promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.getCardAid(), subKeyIds);
promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.getCardAid(), fingerprints);
} catch (NotFoundException e) {
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
@ -134,13 +129,13 @@ public class PromoteKeyOperation extends BaseReadWriteOperation<PromoteKeyringPa
}
static private Integer naiveIndexOf(long[] haystack, long needle) {
for (int i = 0; i < haystack.length; i++) {
if (needle == haystack[i]) {
return i;
static private boolean naiveArraySearch(List<byte[]> searchElements, byte[] needle) {
for (byte[] searchElement : searchElements) {
if (Arrays.equals(needle, searchElement)) {
return true;
}
}
return null;
return false;
}
}

View file

@ -20,7 +20,9 @@ package org.sufficientlysecure.keychain.pgp;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import android.support.annotation.Nullable;
@ -32,6 +34,7 @@ import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
@ -153,19 +156,19 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
}
/** Create a dummy secret ring from this key */
public UncachedKeyRing createDivertSecretRing (byte[] cardAid, long[] subKeyIds) {
public UncachedKeyRing createDivertSecretRing(byte[] cardAid, List<byte[]> subKeyFingerprints) {
PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), cardAid);
if (subKeyIds == null) {
if (subKeyFingerprints == 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) {
PGPSecretKey key = secRing.getSecretKey(subKeyId);
if (key != null) {
for (byte[] subKeyFingerprint : subKeyFingerprints) {
PGPSecretKey key = secRing.getSecretKey(KeyFormattingUtils.convertFingerprintToKeyId(subKeyFingerprint));
if (key != null && Arrays.equals(subKeyFingerprint, key.getPublicKey().getFingerprint())) {
newRing = PGPSecretKeyRing.insertSecretKey(newRing, key);
}
}

View file

@ -32,6 +32,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeSet;
@ -171,7 +172,7 @@ public class UncachedKeyRing {
}
public boolean containsKeyWithAnyFingerprint(byte[]... expectedFingerprints) {
public boolean containsKeyWithAnyFingerprint(List<byte[]> expectedFingerprints) {
Iterator<UncachedPublicKey> publicKeys = getPublicKeys();
while (publicKeys.hasNext()) {

View file

@ -979,22 +979,21 @@ public class SecurityTokenHelper {
}
public SecurityTokenInfo getTokenInfo() throws IOException {
byte[] fingerprints = getFingerprints();
byte[] rawFingerprints = getFingerprints();
byte[] fpSign = new byte[20];
byte[] fpDecrypt = new byte[20];
byte[] fpAuth = new byte[20];
ByteBuffer buf = ByteBuffer.wrap(fingerprints);
buf.get(fpSign);
buf.get(fpDecrypt);
buf.get(fpAuth);
byte[][] fingerprints = new byte[rawFingerprints.length / 20][];
ByteBuffer buf = ByteBuffer.wrap(rawFingerprints);
for (int i = 0; i < rawFingerprints.length / 20; i++) {
fingerprints[i] = new byte[20];
buf.get(fingerprints[i]);
}
byte[] aid = getAid();
String userId = getUserId();
String url = getUrl();
byte[] pwInfo = getPwStatusBytes();
return SecurityTokenInfo.create(fpSign, fpDecrypt, fpAuth, aid, userId, url, pwInfo[4], pwInfo[6]);
return SecurityTokenInfo.create(fingerprints, aid, userId, url, pwInfo[4], pwInfo[6]);
}
private static class LazyHolder {

View file

@ -1,7 +1,9 @@
package org.sufficientlysecure.keychain.securitytoken;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.os.Parcelable;
import android.support.annotation.Nullable;
@ -16,12 +18,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
public abstract class SecurityTokenInfo implements Parcelable {
private static final byte[] EMPTY_ARRAY = new byte[20];
@Nullable
public abstract byte[] getFingerprintSign();
@Nullable
public abstract byte[] getFingerprintDecrypt();
@Nullable
public abstract byte[] getFingerprintAuth();
public abstract List<byte[]> getFingerprints();
@Nullable
public abstract byte[] getAid();
@Nullable
@ -31,23 +28,19 @@ public abstract class SecurityTokenInfo implements Parcelable {
public abstract int getVerifyRetries();
public abstract int getVerifyAdminRetries();
public byte[][] getAllFingerprints() {
byte[][] fingerprints = new byte[3][];
fingerprints[0] = getFingerprintSign();
fingerprints[1] = getFingerprintDecrypt();
fingerprints[2] = getFingerprintAuth();
return fingerprints;
}
public boolean isEmpty() {
return Arrays.equals(EMPTY_ARRAY, getFingerprintSign()) && Arrays.equals(EMPTY_ARRAY, getFingerprintDecrypt()) &&
Arrays.equals(EMPTY_ARRAY, getFingerprintAuth());
return getFingerprints().isEmpty();
}
public static SecurityTokenInfo create(byte[] fpSign, byte[] fpDecrypt, byte[] fpAuth,
byte[] aid, String userId, String url, int verifyRetries, int verifyAdminRetries) {
return new AutoValue_SecurityTokenInfo(fpSign, fpDecrypt, fpAuth, aid,
userId, url, verifyRetries, verifyAdminRetries);
public static SecurityTokenInfo create(byte[][] fingerprints, byte[] aid, String userId, String url,
int verifyRetries, int verifyAdminRetries) {
ArrayList<byte[]> fingerprintList = new ArrayList<>(fingerprints.length);
for (byte[] fingerprint : fingerprints) {
if (!Arrays.equals(EMPTY_ARRAY, fingerprint)) {
fingerprintList.add(fingerprint);
}
}
return new AutoValue_SecurityTokenInfo(fingerprintList, aid, userId, url, verifyRetries, verifyAdminRetries);
}
public static SecurityTokenInfo newInstanceDebugKeyserver() {
@ -55,8 +48,8 @@ public abstract class SecurityTokenInfo implements Parcelable {
throw new UnsupportedOperationException("This operation is only available in debug builds!");
}
return SecurityTokenInfo.create(
KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a"),
new byte[20], new byte[20], Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3);
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("1efdb4845ca242ca6977fddb1f788094fd3b430a") },
Hex.decode("010203040506"), "yubinu2@mugenguild.com", null, 3, 3);
}
public static SecurityTokenInfo newInstanceDebugUri() {
@ -64,9 +57,8 @@ public abstract class SecurityTokenInfo implements Parcelable {
throw new UnsupportedOperationException("This operation is only available in debug builds!");
}
return SecurityTokenInfo.create(
KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E"),
new byte[20], new byte[20], Hex.decode("010203040506"),
"yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3);
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 3, 3);
}
public static SecurityTokenInfo newInstanceDebugLocked() {
@ -74,9 +66,8 @@ public abstract class SecurityTokenInfo implements Parcelable {
throw new UnsupportedOperationException("This operation is only available in debug builds!");
}
return SecurityTokenInfo.create(
KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E"),
new byte[20], new byte[20], Hex.decode("010203040506"),
"yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3);
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 3);
}
public static SecurityTokenInfo newInstanceDebugLockedHard() {
@ -84,9 +75,8 @@ public abstract class SecurityTokenInfo implements Parcelable {
throw new UnsupportedOperationException("This operation is only available in debug builds!");
}
return SecurityTokenInfo.create(
KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E"),
new byte[20], new byte[20], Hex.decode("010203040506"),
"yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0);
new byte[][] { KeyFormattingUtils.convertFingerprintHexFingerprint("4700BA1AC417ABEF3CC7765AD686905837779C3E") },
Hex.decode("010203040506"), "yubinu2@mugenguild.com", "http://valodim.stratum0.net/mryubinu2.asc", 0, 0);
}
}

View file

@ -20,11 +20,12 @@
package org.sufficientlysecure.keychain.service;
import java.util.List;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import com.google.auto.value.AutoValue;
import org.sufficientlysecure.keychain.securitytoken.SecurityTokenInfo;
@AutoValue
@ -35,10 +36,10 @@ public abstract class PromoteKeyringParcel implements Parcelable {
public abstract byte[] getCardAid();
@Nullable
@SuppressWarnings("mutable")
public abstract long[] getSubKeyIds();
public abstract List<byte[]> getFingerprints();
public static PromoteKeyringParcel createPromoteKeyringParcel(long keyRingId, byte[] cardAid,
@Nullable long[] subKeyIds) {
return new AutoValue_PromoteKeyringParcel(keyRingId, cardAid, subKeyIds);
@Nullable List<byte[]> fingerprints) {
return new AutoValue_PromoteKeyringParcel(keyRingId, cardAid, fingerprints);
}
}

View file

@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.ui.token;
import java.util.List;
import android.net.Uri;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
@ -79,7 +81,7 @@ class ManageSecurityTokenContract {
void hideAction();
void operationImportKey(byte[] importKeyData);
void operationPromote(long masterKeyId, byte[] cardAid, long[] subKeyIds);
void operationPromote(long masterKeyId, byte[] cardAid, List<byte[]> fingerprints);
void operationResetSecurityToken();
void operationChangePinSecurityToken(String adminPin, String newPin);

View file

@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.ui.token;
import java.util.List;
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
@ -277,12 +279,13 @@ public class ManageSecurityTokenFragment extends Fragment implements ManageSecur
}
@Override
public void operationPromote(long masterKeyId, byte[] cardAid, long[] subkeys) {
public void operationPromote(long masterKeyId, byte[] cardAid, List<byte[]> fingerprints) {
if (currentImportKeyringParcel != null) {
throw new IllegalStateException("Cannot trigger import operation twice!");
}
currentPromoteKeyringParcel = PromoteKeyringParcel.createPromoteKeyringParcel(masterKeyId, cardAid, subkeys);
currentPromoteKeyringParcel = PromoteKeyringParcel.createPromoteKeyringParcel(
masterKeyId, cardAid, fingerprints);
cryptoPromoteOperationHelper.setOperationMinimumDelay(1000L);
cryptoPromoteOperationHelper.cryptoOperation();
}

View file

@ -38,7 +38,6 @@ import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.KeyRetr
import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.KeyserverRetrievalLoader;
import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.LocalKeyLookupLoader;
import org.sufficientlysecure.keychain.ui.token.PublicKeyRetrievalLoader.UriKeyRetrievalLoader;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.PermissionsUtil;
@ -207,13 +206,13 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
public Loader<KeyRetrievalResult> onCreateLoader(int id, Bundle args) {
switch (id) {
case LOADER_LOCAL:
return new LocalKeyLookupLoader(context, tokenInfo.getAllFingerprints());
return new LocalKeyLookupLoader(context, tokenInfo.getFingerprints());
case LOADER_URI:
return new UriKeyRetrievalLoader(context, tokenInfo.getUrl(), tokenInfo.getAllFingerprints());
return new UriKeyRetrievalLoader(context, tokenInfo.getUrl(), tokenInfo.getFingerprints());
case LOADER_KEYSERVER:
return new KeyserverRetrievalLoader(context, tokenInfo.getAllFingerprints());
return new KeyserverRetrievalLoader(context, tokenInfo.getFingerprints());
case LOADER_CONTENT_URI:
return new ContentUriRetrievalLoader(context, tokenInfo.getAllFingerprints(),
return new ContentUriRetrievalLoader(context, tokenInfo.getFingerprints(),
args.<Uri>getParcelable(ARG_CONTENT_URI));
}
throw new IllegalArgumentException("called with unknown loader id!");
@ -286,10 +285,7 @@ class ManageSecurityTokenPresenter implements ManageSecurityTokenMvpPresenter {
}
private void promoteKeyWithTokenInfo(Long masterKeyId) {
long signKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(tokenInfo.getFingerprintSign());
long decryptKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(tokenInfo.getFingerprintDecrypt());
long authKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(tokenInfo.getFingerprintAuth());
view.operationPromote(masterKeyId, tokenInfo.getAid(), new long[] { signKeyId, decryptKeyId, authKeyId });
view.operationPromote(masterKeyId, tokenInfo.getAid(), tokenInfo.getFingerprints());
}
@Override

View file

@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.token;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
@ -64,10 +65,10 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
private KeyRetrievalResult cachedResult;
protected final byte[][] fingerprints;
protected final List<byte[]> fingerprints;
private PublicKeyRetrievalLoader(Context context, byte[][] fingerprints) {
private PublicKeyRetrievalLoader(Context context, List<byte[]> fingerprints) {
super(context);
this.fingerprints = fingerprints;
@ -94,7 +95,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
static class LocalKeyLookupLoader extends PublicKeyRetrievalLoader {
private final KeyRepository keyRepository;
LocalKeyLookupLoader(Context context, byte[][] fingerprints) {
LocalKeyLookupLoader(Context context, List<byte[]> fingerprints) {
super(context, fingerprints);
this.keyRepository = KeyRepository.createDatabaseInteractor(context);
@ -158,7 +159,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
static class UriKeyRetrievalLoader extends PublicKeyRetrievalLoader {
private final String tokenUri;
UriKeyRetrievalLoader(Context context, String tokenUri, byte[][] fingerprints) {
UriKeyRetrievalLoader(Context context, String tokenUri, List<byte[]> fingerprints) {
super(context, fingerprints);
this.tokenUri = tokenUri;
@ -211,7 +212,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
}
static class KeyserverRetrievalLoader extends PublicKeyRetrievalLoader {
KeyserverRetrievalLoader(Context context, byte[][] fingerprints) {
KeyserverRetrievalLoader(Context context, List<byte[]> fingerprints) {
super(context, fingerprints);
}
@ -228,7 +229,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
log.add(LogType.MSG_RET_KS_START, 0);
String keyString = keyserverClient.get(
"0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprints[0]), parcelableProxy);
"0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprints.get(0)), parcelableProxy);
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(keyString.getBytes());
if (!keyRing.containsKeyWithAnyFingerprint(fingerprints)) {
@ -255,7 +256,7 @@ public abstract class PublicKeyRetrievalLoader extends AsyncTaskLoader<KeyRetrie
private final ContentResolver contentResolver;
private final Uri uri;
ContentUriRetrievalLoader(Context context, byte[][] fingerprints, Uri uri) {
ContentUriRetrievalLoader(Context context, List<byte[]> fingerprints, Uri uri) {
super(context, fingerprints);
this.uri = uri;

View file

@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.operations;
import java.io.PrintStream;
import java.security.Security;
import java.util.Arrays;
import java.util.Iterator;
import org.bouncycastle.bcpg.sig.KeyFlags;
@ -164,9 +165,8 @@ public class PromoteKeyOperationTest {
long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1);
PromoteKeyResult result = op.execute(
PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid, new long[] {
keyId
}), null);
PromoteKeyringParcel.createPromoteKeyringParcel(mStaticRing.getMasterKeyId(), aid,
Arrays.asList(mStaticRing.getPublicKey(keyId).getFingerprint())), null);
Assert.assertTrue("promotion must succeed", result.success());