mirror of
https://github.com/status-im/status-keycard.git
synced 2025-01-12 14:54:54 +00:00
output valid TLV
This commit is contained in:
parent
1b7b196901
commit
abcc05f0a3
@ -3,9 +3,13 @@ package im.status.keycard;
|
||||
import javacard.framework.*;
|
||||
import javacard.security.*;
|
||||
|
||||
import static javacard.framework.ISO7816.OFFSET_P2;
|
||||
|
||||
public class CashApplet extends Applet {
|
||||
private static final short SIGN_OUT_OFF = ISO7816.OFFSET_CDATA + MessageDigest.LENGTH_SHA_256;
|
||||
private static final byte TLV_PUB_DATA = (byte) 0x82;
|
||||
private static final byte SIGN_P2_ECDSA = 0x00;
|
||||
private static final byte SIGN_P2_SCHNORR = 0x01;
|
||||
|
||||
private KeyPair keypair;
|
||||
private ECPublicKey publicKey;
|
||||
@ -120,6 +124,20 @@ public class CashApplet extends Applet {
|
||||
private void sign(APDU apdu) {
|
||||
byte[] apduBuffer = apdu.getBuffer();
|
||||
|
||||
boolean schnorr;
|
||||
|
||||
switch(apduBuffer[OFFSET_P2]) {
|
||||
case SIGN_P2_ECDSA:
|
||||
schnorr = false;
|
||||
break;
|
||||
case SIGN_P2_SCHNORR:
|
||||
schnorr = true;
|
||||
break;
|
||||
default:
|
||||
ISOException.throwIt(ISO7816.SW_WRONG_P1P2);
|
||||
return;
|
||||
}
|
||||
|
||||
apduBuffer[SIGN_OUT_OFF] = KeycardApplet.TLV_SIGNATURE_TEMPLATE;
|
||||
apduBuffer[(short) (SIGN_OUT_OFF + 3)] = KeycardApplet.TLV_PUB_KEY;
|
||||
short outLen = apduBuffer[(short) (SIGN_OUT_OFF + 4)] = Crypto.KEY_PUB_SIZE;
|
||||
@ -129,8 +147,12 @@ public class CashApplet extends Applet {
|
||||
outLen += 5;
|
||||
short sigOff = (short) (SIGN_OUT_OFF + outLen);
|
||||
|
||||
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
||||
outLen += crypto.fixS(apduBuffer, sigOff);
|
||||
if (schnorr) {
|
||||
outLen += secp256k1.signSchnorr(privateKey, apduBuffer, (short) (SIGN_OUT_OFF + 5), apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
||||
} else {
|
||||
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
||||
outLen += crypto.fixS(apduBuffer, sigOff);
|
||||
}
|
||||
|
||||
apduBuffer[(short) (SIGN_OUT_OFF + 1)] = (byte) 0x81;
|
||||
apduBuffer[(short) (SIGN_OUT_OFF + 2)] = (byte) (outLen - 3);
|
||||
|
@ -1166,7 +1166,6 @@ public class KeycardApplet extends Applet {
|
||||
short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen);
|
||||
|
||||
if (schnorr) {
|
||||
//TODO: output BER-TLV!
|
||||
outLen += secp256k1.signSchnorr(signingKey, apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5), apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
||||
} else {
|
||||
signature.init(signingKey, Signature.MODE_SIGN);
|
||||
|
@ -56,6 +56,9 @@ public class SECP256k1 {
|
||||
static final short SECP256K1_KEY_SIZE = 256;
|
||||
static final short SECP256K1_BYTE_SIZE = (short) (SECP256K1_KEY_SIZE / 8);
|
||||
|
||||
|
||||
static final byte TLV_SCHNORR_SIGNATURE = (byte) 0x8f;
|
||||
|
||||
static final short SCHNORR_K_OUT_OFF = (short) 0;
|
||||
static final short SCHNORR_E_OUT_OFF = (short) (32 + SCHNORR_K_OUT_OFF);
|
||||
static final short SCHNORR_E_32_OFF = (short) (64 + SCHNORR_E_OUT_OFF);
|
||||
@ -63,9 +66,8 @@ public class SECP256k1 {
|
||||
static final short SCHNORR_D_32_OFF = (short) (64 + SCHNORR_D_OUT_OFF);
|
||||
static final short SCHNORR_TMP1_OUT_OFF = (short) (96 + SCHNORR_D_OUT_OFF);
|
||||
static final short SCHNORR_TMP1_32_OFF = (short) (64 + SCHNORR_TMP1_OUT_OFF);
|
||||
static final short SCHNORR_TMP2_OUT_OFF = (short) (96 + SCHNORR_TMP1_OUT_OFF);
|
||||
|
||||
static final short TMP_LEN = 416;
|
||||
static final short TMP_LEN = 320;
|
||||
|
||||
private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5
|
||||
|
||||
@ -77,8 +79,7 @@ public class SECP256k1 {
|
||||
private RSAPublicKey pow2;
|
||||
private Cipher multCipher;
|
||||
|
||||
static final byte[] CONST_TWO = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02};
|
||||
|
||||
static final byte[] CONST_TWO = { 0x02 };
|
||||
private byte[] tmp;
|
||||
|
||||
/**
|
||||
@ -162,6 +163,10 @@ public class SECP256k1 {
|
||||
}
|
||||
|
||||
short signSchnorr(ECPrivateKey privKey, byte[] pubKey, short pubOff, byte[] data, short dataOff, short dataLen, byte[] output, short outOff) {
|
||||
output[outOff++] = TLV_SCHNORR_SIGNATURE;
|
||||
output[outOff++] = (byte) 0x81;
|
||||
output[outOff++] = (byte) (Crypto.KEY_PUB_SIZE + 66);;
|
||||
|
||||
crypto.random.generateData(tmp, SCHNORR_K_OUT_OFF, SECP256K1_BYTE_SIZE);
|
||||
Util.arrayFillNonAtomic(tmp, SCHNORR_E_OUT_OFF, (short)(TMP_LEN - SCHNORR_E_OUT_OFF), (byte) 0x00);
|
||||
|
||||
@ -173,24 +178,24 @@ public class SECP256k1 {
|
||||
|
||||
tmp[(short)(SCHNORR_TMP1_32_OFF - 1)] = (byte) crypto.add256(tmp, SCHNORR_E_32_OFF, tmp, SCHNORR_D_32_OFF, tmp, SCHNORR_TMP1_32_OFF);
|
||||
multCipher.doFinal(tmp, SCHNORR_TMP1_OUT_OFF, (short) 96, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||
multCipher.doFinal(tmp, SCHNORR_D_OUT_OFF, (short) 96, tmp, SCHNORR_TMP2_OUT_OFF);
|
||||
crypto.sub768(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_TMP2_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||
multCipher.doFinal(tmp, SCHNORR_E_OUT_OFF, (short) 96, tmp, SCHNORR_TMP2_OUT_OFF);
|
||||
crypto.sub768(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_TMP2_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||
multCipher.doFinal(tmp, SCHNORR_D_OUT_OFF, (short) 96, tmp, SCHNORR_D_OUT_OFF);
|
||||
crypto.sub768(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_D_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||
multCipher.doFinal(tmp, SCHNORR_E_OUT_OFF, (short) 96, tmp, SCHNORR_E_OUT_OFF);
|
||||
crypto.sub768(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_E_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||
|
||||
short res, res2;
|
||||
|
||||
for (short i = (short) 95; i >= 0; i--) {
|
||||
res = (short) ((short) (tmp[(short)(SCHNORR_TMP1_OUT_OFF + i)] & 0xff) >> 1);
|
||||
res2 = (short) ((short) (tmp[(short)(SCHNORR_TMP1_OUT_OFF + i - 1)] & 0xff) << 7);
|
||||
tmp[(short)(SCHNORR_TMP1_OUT_OFF + i)] = (byte) (short) (res | res2);
|
||||
tmp[(short)(SCHNORR_TMP1_OUT_OFF + i)] = (byte) ((short) (res | res2));
|
||||
}
|
||||
|
||||
tmp[SCHNORR_TMP1_OUT_OFF] &= (byte) 0x7f;
|
||||
|
||||
add256to768(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_K_OUT_OFF, tmp, SCHNORR_TMP2_OUT_OFF);
|
||||
Util.arrayCopyNonAtomic(tmp, (short) (SCHNORR_TMP2_OUT_OFF + 30), output, (short) (outOff + Crypto.KEY_PUB_SIZE), (short) 66);
|
||||
return (short) (Crypto.KEY_PUB_SIZE + 66);
|
||||
add256to768(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_K_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||
Util.arrayCopyNonAtomic(tmp, (short) (SCHNORR_TMP1_OUT_OFF + 30), output, (short) (outOff + Crypto.KEY_PUB_SIZE), (short) 66);
|
||||
return (short) (3 + Crypto.KEY_PUB_SIZE + 66);
|
||||
}
|
||||
|
||||
short add256to768(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
||||
|
@ -1060,40 +1060,10 @@ public class KeycardTest {
|
||||
assertEquals(0x6A88, response.getSw());
|
||||
}
|
||||
|
||||
private void schnorrTest() throws Exception {
|
||||
byte[] m = new byte[32];
|
||||
SecureRandom.getInstanceStrong().nextBytes(m);
|
||||
MessageDigest dg = MessageDigest.getInstance("SHA256");
|
||||
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||
|
||||
byte[] sk = new byte[32];
|
||||
SecureRandom.getInstanceStrong().nextBytes(sk);
|
||||
BigInteger d = new BigInteger(1, sk);
|
||||
ECPoint P = ecSpec.getG().multiply(d);
|
||||
|
||||
byte[] _k = new byte[32];
|
||||
SecureRandom.getInstanceStrong().nextBytes(_k);
|
||||
BigInteger k = new BigInteger(1, _k);
|
||||
ECPoint R = ecSpec.getG().multiply(k);
|
||||
|
||||
dg.update(R.getEncoded(false));
|
||||
dg.update(P.getEncoded(false));
|
||||
dg.update(m);
|
||||
BigInteger e = new BigInteger(1, dg.digest());
|
||||
|
||||
BigInteger s = e.multiply(d).add(k);
|
||||
|
||||
ECPoint R2 = ecSpec.getG().multiply(s).subtract(P.multiply(e));
|
||||
|
||||
assertTrue(R.equals(R2));
|
||||
}
|
||||
|
||||
private void verifySchnorr(byte[] m, byte[] sig) throws Exception {
|
||||
schnorrTest();
|
||||
|
||||
byte[] p = extractPublicKeyFromSignature(sig);
|
||||
|
||||
int off = sig[4] + 5;
|
||||
int off = sig[4] + 5 + 3;
|
||||
byte[] rawSig = Arrays.copyOfRange(sig, off, sig.length);
|
||||
|
||||
byte[] r = Arrays.copyOf(rawSig, 65);
|
||||
|
Loading…
x
Reference in New Issue
Block a user