mirror of
https://github.com/status-im/status-keycard.git
synced 2025-02-10 20:46:36 +00:00
extend LOAD KEY with seed loading support
This commit is contained in:
parent
97d195e6b5
commit
8424f262df
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user