extend LOAD KEY with seed loading support

This commit is contained in:
Michele Balistreri 2017-10-11 12:44:48 +03:00
parent 97d195e6b5
commit 8424f262df
5 changed files with 104 additions and 29 deletions

View File

@ -1,14 +1,24 @@
package im.status.wallet;
import javacard.security.CryptoException;
import javacard.security.KeyAgreement;
import javacard.security.MessageDigest;
import javacard.security.RandomData;
public class Crypto {
public static RandomData random;
public static MessageDigest sha256;
private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5
static RandomData random;
static MessageDigest sha256;
static KeyAgreement ecPointMultiplier;
static void init() {
try {
ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
} catch(CryptoException e) {
ecPointMultiplier = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
}
public static void init() {
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
sha256 = MessageDigest.getInstance(MessageDigest.ALG_SHA_256, false);
}

View File

@ -2,11 +2,8 @@ package im.status.wallet;
import javacard.security.ECKey;
import javacard.security.ECPrivateKey;
import javacard.security.KeyAgreement;
public class ECCurves {
static KeyAgreement ecPointMultiplier;
static final byte SECP256K1_FP[] = {
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,
@ -59,16 +56,13 @@ public class ECCurves {
}
/*
* This might or might not work on actual card. Does not work on simulator. KeyAgreement.ALG_EC_SVDP_DH_PLAIN_XY is actually
* the algorithm we need, but it has been implemented later. However some cards implement KeyAgreement.ALG_EC_SVDP_DH_PLAIN
* in the same way. If we cannot use KeyAgreement to multiply it is difficult to generate public keys on-card.
* This works only if KeyAgreement.ALG_EC_SVDP_DH_PLAIN_XY is implemented. Otherwise we get only X. This method must be
* extended to calculate Y from X (Y^2 = X(X^2+A)+B). Since we get two possible results we will need to use signature
* verification to check which one is the actual public key. In this case performance will be very slow, around 5s
* for a single multiplication.
*/
static short multiplyPoint(ECPrivateKey privateKey, byte[] point, short pointOff, short pointLen, byte[] out, short outOff) {
if (ecPointMultiplier == null) {
ecPointMultiplier = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
}
ecPointMultiplier.init(privateKey);
return ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
Crypto.ecPointMultiplier.init(privateKey);
return Crypto.ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
}
}

View File

@ -254,6 +254,7 @@ public class WalletApplet extends Applet {
break;
case LOAD_KEY_P1_SEED:
loadSeed(apduBuffer);
break;
default:
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
break;

View File

@ -11,6 +11,7 @@ import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import java.security.KeyPair;
import java.security.PrivateKey;
public class WalletAppletCommandSet {
public static final String APPLET_AID = "53746174757357616C6C6574417070";
@ -56,8 +57,30 @@ public class WalletAppletCommandSet {
return apduChannel.transmit(unblockPIN);
}
public ResponseAPDU loadKey(KeyPair keyPair) throws CardException {
byte[] publicKey = ((ECPublicKey) keyPair.getPublic()).getQ().getEncoded(false);
public ResponseAPDU loadKey(PrivateKey aPrivate, byte[] chainCode) throws CardException {
byte[] privateKey = ((ECPrivateKey) aPrivate).getD().toByteArray();
int privLen = privateKey.length;
int privOff = 0;
if(privateKey[0] == 0x00) {
privOff++;
privLen--;
}
byte[] data = new byte[chainCode.length + privLen];
System.arraycopy(privateKey, privOff, data, 0, privLen);
System.arraycopy(chainCode, 0, data, privLen, chainCode.length);
return loadKey(data, WalletApplet.LOAD_KEY_P1_SEED);
}
public ResponseAPDU loadKey(KeyPair ecKeyPair) throws CardException {
return loadKey(ecKeyPair, false, null);
}
public ResponseAPDU loadKey(KeyPair keyPair, boolean omitPublicKey, byte[] chainCode) throws CardException {
byte[] publicKey = omitPublicKey ? null : ((ECPublicKey) keyPair.getPublic()).getQ().getEncoded(false);
byte[] privateKey = ((ECPrivateKey) keyPair.getPrivate()).getD().toByteArray();
int privLen = privateKey.length;
@ -68,17 +91,51 @@ public class WalletAppletCommandSet {
privLen--;
}
byte[] data = new byte[publicKey.length + privLen + 6];
data[0] = (byte) 0xA1;
data[1] = (byte) (publicKey.length + privLen + 4);
data[2] = (byte) 0x80;
data[3] = (byte) publicKey.length;
System.arraycopy(publicKey, 0, data, 4, publicKey.length);
data[4 + publicKey.length] = (byte) 0x81;
data[5 + publicKey.length] = (byte) privLen;
System.arraycopy(privateKey, privOff, data, 6 + publicKey.length, privLen);
int off = 0;
int totalLength = publicKey == null ? 0 : (publicKey.length + 2);
totalLength += (privLen + 2);
totalLength += chainCode == null ? 0 : (chainCode.length + 2);
return loadKey(data, WalletApplet.LOAD_KEY_P1_EC);
if (totalLength > 127) {
totalLength += 3;
} else {
totalLength += 2;
}
byte[] data = new byte[totalLength];
data[off++] = (byte) 0xA1;
if (totalLength > 127) {
data[off++] = (byte) 0x81;
data[off++] = (byte) (totalLength - 3);
} else {
data[off++] = (byte) (totalLength - 2);
}
if (publicKey != null) {
data[off++] = (byte) 0x80;
data[off++] = (byte) publicKey.length;
System.arraycopy(publicKey, 0, data, off, publicKey.length);
off += publicKey.length;
}
data[off++] = (byte) 0x81;
data[off++] = (byte) privLen;
System.arraycopy(privateKey, privOff, data, off, privLen);
off += privLen;
byte p1;
if (chainCode != null) {
p1 = WalletApplet.LOAD_KEY_P1_EXT_EC;
data[off++] = (byte) 0x82;
data[off++] = (byte) chainCode.length;
System.arraycopy(chainCode, 0, data, off, chainCode.length);
} else {
p1 = WalletApplet.LOAD_KEY_P1_EC;
}
return loadKey(data, p1);
}
public ResponseAPDU loadKey(ECKeyPair ecKeyPair) throws CardException {

View File

@ -299,14 +299,27 @@ public class WalletAppletTest {
assertEquals(0x6A80, response.getSW());
}
byte[] chainCode = new byte[32];
new Random().nextBytes(chainCode);
// Correct LOAD KEY
response = cmdSet.loadKey(keyPair);
assertEquals(0x9000, response.getSW());
keyPair = g.generateKeyPair();
// Check replacing keys
response = cmdSet.loadKey(keyPair);
// Check extended key
response = cmdSet.loadKey(keyPair, false, chainCode);
assertEquals(0x9000, response.getSW());
// Check omitted public key
response = cmdSet.loadKey(keyPair, true, null);
assertEquals(0x9000, response.getSW());
response = cmdSet.loadKey(keyPair, true, chainCode);
assertEquals(0x9000, response.getSW());
// Check seed load
response = cmdSet.loadKey(keyPair.getPrivate(), chainCode);
assertEquals(0x9000, response.getSW());
}