output valid TLV

This commit is contained in:
Michele Balistreri 2020-06-23 07:49:18 +03:00
parent 1b7b196901
commit abcc05f0a3
No known key found for this signature in database
GPG Key ID: E9567DA33A4F791A
4 changed files with 42 additions and 46 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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) {

View File

@ -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);