remove DUPLICATE KEY command
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
package im.status.keycard.applet;
|
||||
|
||||
import im.status.keycard.io.APDUException;
|
||||
import im.status.keycard.io.CardChannel;
|
||||
import im.status.keycard.io.WrongPINException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Class helping with the card duplication process. Depending on the client's role, only some of the methods are relevant.
|
||||
*/
|
||||
public class CardDuplicator {
|
||||
private byte[] secret;
|
||||
private KeycardCommandSet cmdSet;
|
||||
private DuplicatorCallback cb;
|
||||
|
||||
private HashSet<byte[]> startedDuplication;
|
||||
private HashSet<byte[]> addedEntropy;
|
||||
private HashSet<byte[]> finishedDuplication;
|
||||
|
||||
/**
|
||||
* Creates a CardDuplicator object. Regardless of the role of the client, this object must be kept and used for the
|
||||
* entire duplication session. It cannot be reused for multiple sessions.
|
||||
*
|
||||
* @param cmdSet the CommandSet to use
|
||||
* @param cb the callback object for backups. This is needed only on the client performing steps requiring pairing
|
||||
* and authentication. Clients which only add entropy should pass null
|
||||
*/
|
||||
public CardDuplicator(KeycardCommandSet cmdSet, DuplicatorCallback cb) {
|
||||
this.cmdSet = cmdSet;
|
||||
this.cb = cb;
|
||||
this.startedDuplication = new HashSet<>();
|
||||
this.addedEntropy = new HashSet<>();
|
||||
this.finishedDuplication = new HashSet<>();
|
||||
|
||||
this.secret = new byte[32];
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.nextBytes(this.secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CardDuplicator object. Only suitable for clients performing the role of adding entropy.
|
||||
*
|
||||
* @param channel the APDU channel
|
||||
*/
|
||||
public CardDuplicator(CardChannel channel) {
|
||||
this(new KeycardCommandSet(channel), null);
|
||||
}
|
||||
|
||||
private ApplicationInfo selectAndCheck(HashSet<byte[]> processed) throws APDUException, IOException {
|
||||
ApplicationInfo appInfo = new ApplicationInfo(cmdSet.select().checkOK().getData());
|
||||
|
||||
if (!processed.add(appInfo.getInstanceUID())) {
|
||||
throw new IllegalStateException("The requested action has been already performed on this card");
|
||||
}
|
||||
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
private void preamble(HashSet<byte[]> processed) throws IOException, APDUException {
|
||||
ApplicationInfo appInfo = selectAndCheck(processed);
|
||||
|
||||
Pairing pairing = cb.getPairing(appInfo);
|
||||
|
||||
if (pairing == null) {
|
||||
throw new APDUException("The given card is not paired");
|
||||
}
|
||||
|
||||
cmdSet.setPairing(pairing);
|
||||
cmdSet.autoOpenSecureChannel();
|
||||
ApplicationStatus appStatus = new ApplicationStatus(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_APPLICATION).checkOK().getData());
|
||||
int remainingAttempts = appStatus.getPINRetryCount();
|
||||
|
||||
while(remainingAttempts > 0) {
|
||||
try {
|
||||
cmdSet.verifyPIN(cb.getPIN(appInfo, remainingAttempts)).checkAuthOK();
|
||||
break;
|
||||
} catch(WrongPINException e) {
|
||||
remainingAttempts = e.getRetryAttempts();
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingAttempts <= 0) {
|
||||
throw new APDUException("Card blocked");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts duplication session. Must be used on all cards taking part of in the duplication process.
|
||||
*
|
||||
* @param clientCount the number of clients which will be adding entropy for the key, including this one
|
||||
*
|
||||
* @throws IOException communication error
|
||||
* @throws APDUException unexpected card response
|
||||
* @throws IllegalStateException this card has already been used
|
||||
*/
|
||||
public void startDuplication(int clientCount) throws IOException, APDUException, IllegalStateException {
|
||||
preamble(startedDuplication);
|
||||
cmdSet.duplicateKeyStart(clientCount, secret).checkOK();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports key. Must be used on the card designated as the source for the duplication.
|
||||
*
|
||||
* @throws IOException communication error
|
||||
* @throws APDUException unexpected card response
|
||||
*/
|
||||
public byte[] exportKey() throws IOException, APDUException, IllegalStateException {
|
||||
preamble(finishedDuplication);
|
||||
return cmdSet.duplicateKeyExport().checkOK().getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports key. Must be used on all cards designated as the target for the duplication.
|
||||
*
|
||||
* @param key the key to import
|
||||
* @return the key UID
|
||||
* @throws IOException communication error
|
||||
* @throws APDUException unexpected card response
|
||||
* @throws IllegalStateException this card has already been used
|
||||
*/
|
||||
public byte[] importKey(byte[] key) throws IOException, APDUException, IllegalStateException {
|
||||
preamble(finishedDuplication);
|
||||
return cmdSet.duplicateKeyImport(key).checkOK().getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entropy. Must be used on all cards taking part in the backup process. Each client taking part must use this
|
||||
* exactly once, except for the client which started the backup.
|
||||
*
|
||||
* @throws IOException communication error
|
||||
* @throws APDUException unexpected card response
|
||||
* @throws IllegalStateException this card has already been used
|
||||
*/
|
||||
public void addEntropy() throws IOException, APDUException, IllegalStateException {
|
||||
selectAndCheck(addedEntropy);
|
||||
cmdSet.duplicateKeyAddEntropy(secret).checkOK();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package im.status.keycard.applet;
|
||||
|
||||
/**
|
||||
* Callback interface for duplication procedure.
|
||||
*/
|
||||
public interface DuplicatorCallback {
|
||||
/**
|
||||
* Must return the pairing for the current card, represented by the applicationInfo parameter. If no pairing
|
||||
* could be found, null must be returned.
|
||||
*
|
||||
* @param applicationInfo the application info template of the currently inserted card
|
||||
* @return the pairing info or null
|
||||
*/
|
||||
Pairing getPairing(ApplicationInfo applicationInfo);
|
||||
|
||||
/**
|
||||
* Must return the PIN for the current card. This method can prompt the user or return a cached value.
|
||||
*
|
||||
* @param applicationInfo the application info template of the currently inserted card
|
||||
* @param remainingAttempts the number of remaining PIN attempts
|
||||
* @return the PIN
|
||||
*/
|
||||
String getPIN(ApplicationInfo applicationInfo, int remainingAttempts);
|
||||
}
|
||||
@@ -28,7 +28,6 @@ public class KeycardCommandSet {
|
||||
static final byte INS_GENERATE_MNEMONIC = (byte) 0xD2;
|
||||
static final byte INS_REMOVE_KEY = (byte) 0xD3;
|
||||
static final byte INS_GENERATE_KEY = (byte) 0xD4;
|
||||
static final byte INS_DUPLICATE_KEY = (byte) 0xD5;
|
||||
static final byte INS_SIGN = (byte) 0xC0;
|
||||
static final byte INS_SET_PINLESS_PATH = (byte) 0xC1;
|
||||
static final byte INS_EXPORT_KEY = (byte) 0xC2;
|
||||
@@ -488,57 +487,6 @@ public class KeycardCommandSet {
|
||||
return secureChannel.transmit(apduChannel, generateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DUPLICATE KEY APDU. The P1 is set to 00, P2 to the entropy count and the data is the first entropy piece.
|
||||
* This starts a duplication session. Requires an open Secure Channel and authenticated PIN.
|
||||
*
|
||||
* @param entropyCount the number of entropy pieces to expect, including the one in this APDU
|
||||
* @param firstEntropy a random 32-byte number
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse duplicateKeyStart(int entropyCount, byte[] firstEntropy) throws IOException {
|
||||
APDUCommand duplicateKeyStart = secureChannel.protectedCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_START, entropyCount, firstEntropy);
|
||||
return secureChannel.transmit(apduChannel, duplicateKeyStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DUPLICATE KEY APDU. The P1 is set to 01 and the data is the entropy. This adds entropy and does not require
|
||||
* a Secure Channel or authenticated PIN.
|
||||
*
|
||||
* @param entropy a random 32-byte number
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse duplicateKeyAddEntropy(byte[] entropy) throws IOException {
|
||||
APDUCommand duplicateKeyAddEntropy = new APDUCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_ADD_ENTROPY, 0, secureChannel.oneShotEncrypt(entropy));
|
||||
return apduChannel.send(duplicateKeyAddEntropy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DUPLICATE KEY APDU. The P1 is set to 02. This exports the encrypted master key including chaining code.
|
||||
*
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse duplicateKeyExport() throws IOException {
|
||||
APDUCommand duplicateKeyExport = secureChannel.protectedCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_EXPORT, 0, new byte[0]);
|
||||
return secureChannel.transmit(apduChannel, duplicateKeyExport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DUPLICATE KEY APDU. The P1 is set to 03. This imports an encrypted master key including chaining code. The
|
||||
* response data contains the key UID of the imported key.
|
||||
*
|
||||
* @param key the key, exported from another card in the same duplication session.
|
||||
* @return the raw card response
|
||||
* @throws IOException communication error
|
||||
*/
|
||||
public APDUResponse duplicateKeyImport(byte[] key) throws IOException {
|
||||
APDUCommand duplicateKeyImport = secureChannel.protectedCommand(0x80, INS_DUPLICATE_KEY, DUPLICATE_KEY_P1_IMPORT, 0, key);
|
||||
return secureChannel.transmit(apduChannel, duplicateKeyImport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a SIGN APDU. This signs a precomputed hash that must be exactly 32-bytes long.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user