first attempt
This commit is contained in:
parent
36de0fcc9c
commit
f1522fac81
|
@ -40,7 +40,7 @@ public class CashApplet extends Applet {
|
||||||
*/
|
*/
|
||||||
public CashApplet(byte[] bArray, short bOffset, byte bLength) {
|
public CashApplet(byte[] bArray, short bOffset, byte bLength) {
|
||||||
crypto = new Crypto();
|
crypto = new Crypto();
|
||||||
secp256k1 = new SECP256k1();
|
secp256k1 = new SECP256k1(crypto);
|
||||||
|
|
||||||
keypair = new KeyPair(KeyPair.ALG_EC_FP, SECP256k1.SECP256K1_KEY_SIZE);
|
keypair = new KeyPair(KeyPair.ALG_EC_FP, SECP256k1.SECP256K1_KEY_SIZE);
|
||||||
publicKey = (ECPublicKey) keypair.getPublic();
|
publicKey = (ECPublicKey) keypair.getPublic();
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class Crypto {
|
||||||
private Signature hmacSHA512;
|
private Signature hmacSHA512;
|
||||||
private HMACKey hmacKey;
|
private HMACKey hmacKey;
|
||||||
|
|
||||||
private byte[] hmacBlock;
|
protected byte[] hmacBlock;
|
||||||
|
|
||||||
Crypto() {
|
Crypto() {
|
||||||
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
|
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
|
||||||
|
@ -150,7 +150,7 @@ public class Crypto {
|
||||||
* @param out the output buffer
|
* @param out the output buffer
|
||||||
* @param outOff the offset in the output buffer
|
* @param outOff the offset in the output buffer
|
||||||
*/
|
*/
|
||||||
private void hmacSHA512(byte[] key, short keyOff, short keyLen, byte[] in, short inOff, short inLen, byte[] out, short outOff) {
|
void hmacSHA512(byte[] key, short keyOff, short keyLen, byte[] in, short inOff, short inLen, byte[] out, short outOff) {
|
||||||
if (hmacSHA512 != null) {
|
if (hmacSHA512 != null) {
|
||||||
hmacKey.setKey(key, keyOff, keyLen);
|
hmacKey.setKey(key, keyOff, keyLen);
|
||||||
hmacSHA512.init(hmacKey, Signature.MODE_SIGN);
|
hmacSHA512.init(hmacKey, Signature.MODE_SIGN);
|
||||||
|
@ -186,7 +186,7 @@ public class Crypto {
|
||||||
* @param out the output buffer
|
* @param out the output buffer
|
||||||
* @param outOff the offset in the output buffer
|
* @param outOff the offset in the output buffer
|
||||||
*/
|
*/
|
||||||
private void addm256(byte[] a, short aOff, byte[] b, short bOff, byte[] n, short nOff, byte[] out, short outOff) {
|
void addm256(byte[] a, short aOff, byte[] b, short bOff, byte[] n, short nOff, byte[] out, short outOff) {
|
||||||
if ((add256(a, aOff, b, bOff, out, outOff) != 0) || (ucmp256(out, outOff, n, nOff) > 0)) {
|
if ((add256(a, aOff, b, bOff, out, outOff) != 0) || (ucmp256(out, outOff, n, nOff) > 0)) {
|
||||||
sub256(out, outOff, n, nOff, out, outOff);
|
sub256(out, outOff, n, nOff, out, outOff);
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ public class Crypto {
|
||||||
* @param bOff the offset of the b operand
|
* @param bOff the offset of the b operand
|
||||||
* @return the comparison result
|
* @return the comparison result
|
||||||
*/
|
*/
|
||||||
private short ucmp256(byte[] a, short aOff, byte[] b, short bOff) {
|
short ucmp256(byte[] a, short aOff, byte[] b, short bOff) {
|
||||||
short ai, bi;
|
short ai, bi;
|
||||||
for (short i = 0 ; i < 32; i++) {
|
for (short i = 0 ; i < 32; i++) {
|
||||||
ai = (short)(a[(short)(aOff + i)] & 0x00ff);
|
ai = (short)(a[(short)(aOff + i)] & 0x00ff);
|
||||||
|
@ -222,7 +222,7 @@ public class Crypto {
|
||||||
* @param aOff the offset of the a operand
|
* @param aOff the offset of the a operand
|
||||||
* @return true if a is 0, false otherwise
|
* @return true if a is 0, false otherwise
|
||||||
*/
|
*/
|
||||||
private boolean isZero256(byte[] a, short aOff) {
|
boolean isZero256(byte[] a, short aOff) {
|
||||||
boolean isZero = true;
|
boolean isZero = true;
|
||||||
|
|
||||||
for (short i = 0; i < (byte) 32; i++) {
|
for (short i = 0; i < (byte) 32; i++) {
|
||||||
|
@ -246,11 +246,11 @@ public class Crypto {
|
||||||
* @param outOff the offset in the output buffer
|
* @param outOff the offset in the output buffer
|
||||||
* @return the carry of the addition
|
* @return the carry of the addition
|
||||||
*/
|
*/
|
||||||
private short add256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
short add256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
||||||
short outI = 0;
|
short outI = 0;
|
||||||
for (short i = 31 ; i >= 0 ; i--) {
|
for (short i = 31 ; i >= 0 ; i--) {
|
||||||
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) + (short)(b[(short)(bOff + i)] & 0xFF) + outI);
|
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) + (short)(b[(short)(bOff + i)] & 0xFF) + outI);
|
||||||
out[(short)(outOff + i)] = (byte)outI ;
|
out[(short)(outOff + i)] = (byte)outI;
|
||||||
outI = (short)(outI >> 8);
|
outI = (short)(outI >> 8);
|
||||||
}
|
}
|
||||||
return outI;
|
return outI;
|
||||||
|
@ -267,7 +267,7 @@ public class Crypto {
|
||||||
* @param outOff the offset in the output buffer
|
* @param outOff the offset in the output buffer
|
||||||
* @return the carry of the subtraction
|
* @return the carry of the subtraction
|
||||||
*/
|
*/
|
||||||
private short sub256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
short sub256(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
||||||
short outI = 0;
|
short outI = 0;
|
||||||
|
|
||||||
for (short i = 31 ; i >= 0 ; i--) {
|
for (short i = 31 ; i >= 0 ; i--) {
|
||||||
|
@ -278,4 +278,27 @@ public class Crypto {
|
||||||
|
|
||||||
return outI;
|
return outI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtraction of two 512-bit numbers.
|
||||||
|
*
|
||||||
|
* @param a the a operand
|
||||||
|
* @param aOff the offset of the a operand
|
||||||
|
* @param b the b operand
|
||||||
|
* @param bOff the offset of the b operand
|
||||||
|
* @param out the output buffer
|
||||||
|
* @param outOff the offset in the output buffer
|
||||||
|
* @return the carry of the subtraction
|
||||||
|
*/
|
||||||
|
short sub512(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
||||||
|
short outI = 0;
|
||||||
|
|
||||||
|
for (short i = 63 ; i >= 0 ; i--) {
|
||||||
|
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) - (short)(b[(short)(bOff + i)] & 0xFF) - outI);
|
||||||
|
out[(short)(outOff + i)] = (byte)outI ;
|
||||||
|
outI = (short)(((outI >> 8) != 0) ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outI;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ package im.status.keycard;
|
||||||
|
|
||||||
import javacard.framework.*;
|
import javacard.framework.*;
|
||||||
import javacard.security.*;
|
import javacard.security.*;
|
||||||
import javacardx.crypto.Cipher;
|
|
||||||
|
|
||||||
import static javacard.framework.ISO7816.OFFSET_P1;
|
import static javacard.framework.ISO7816.OFFSET_P1;
|
||||||
|
import static javacard.framework.ISO7816.OFFSET_P2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The applet's main class. All incoming commands a processed by this class.
|
* The applet's main class. All incoming commands a processed by this class.
|
||||||
|
@ -68,6 +68,9 @@ public class KeycardApplet extends Applet {
|
||||||
static final byte SIGN_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
|
static final byte SIGN_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
|
||||||
static final byte SIGN_P1_PINLESS = 0x03;
|
static final byte SIGN_P1_PINLESS = 0x03;
|
||||||
|
|
||||||
|
static final byte SIGN_P2_ECDSA = 0x00;
|
||||||
|
static final byte SIGN_P2_SCHNORR = 0x01;
|
||||||
|
|
||||||
static final byte EXPORT_KEY_P1_CURRENT = 0x00;
|
static final byte EXPORT_KEY_P1_CURRENT = 0x00;
|
||||||
static final byte EXPORT_KEY_P1_DERIVE = 0x01;
|
static final byte EXPORT_KEY_P1_DERIVE = 0x01;
|
||||||
static final byte EXPORT_KEY_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
|
static final byte EXPORT_KEY_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
|
||||||
|
@ -167,7 +170,7 @@ public class KeycardApplet extends Applet {
|
||||||
*/
|
*/
|
||||||
public KeycardApplet(byte[] bArray, short bOffset, byte bLength) {
|
public KeycardApplet(byte[] bArray, short bOffset, byte bLength) {
|
||||||
crypto = new Crypto();
|
crypto = new Crypto();
|
||||||
secp256k1 = new SECP256k1();
|
secp256k1 = new SECP256k1(crypto);
|
||||||
|
|
||||||
uid = new byte[UID_LENGTH];
|
uid = new byte[UID_LENGTH];
|
||||||
crypto.random.generateData(uid, (short) 0, UID_LENGTH);
|
crypto.random.generateData(uid, (short) 0, UID_LENGTH);
|
||||||
|
@ -1104,6 +1107,20 @@ public class KeycardApplet extends Applet {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
short len;
|
short len;
|
||||||
|
|
||||||
if (usePinless && !secureChannel.isOpen()) {
|
if (usePinless && !secureChannel.isOpen()) {
|
||||||
|
@ -1148,10 +1165,13 @@ public class KeycardApplet extends Applet {
|
||||||
outLen += 5;
|
outLen += 5;
|
||||||
short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen);
|
short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen);
|
||||||
|
|
||||||
signature.init(signingKey, Signature.MODE_SIGN);
|
if (schnorr) {
|
||||||
|
outLen += secp256k1.signSchnorr(signingKey, apduBuffer, (short) (SecureChannel.SC_OUT_OFFSET + 5), apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
||||||
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
} else {
|
||||||
outLen += crypto.fixS(apduBuffer, sigOff);
|
signature.init(signingKey, Signature.MODE_SIGN);
|
||||||
|
outLen += signature.signPreComputedHash(apduBuffer, ISO7816.OFFSET_CDATA, MessageDigest.LENGTH_SHA_256, apduBuffer, sigOff);
|
||||||
|
outLen += crypto.fixS(apduBuffer, sigOff);
|
||||||
|
}
|
||||||
|
|
||||||
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81;
|
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 1)] = (byte) 0x81;
|
||||||
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3);
|
apduBuffer[(short)(SecureChannel.SC_OUT_OFFSET + 2)] = (byte) (outLen - 3);
|
||||||
|
@ -1212,7 +1232,7 @@ public class KeycardApplet extends Applet {
|
||||||
|
|
||||||
boolean publicOnly;
|
boolean publicOnly;
|
||||||
|
|
||||||
switch (apduBuffer[ISO7816.OFFSET_P2]) {
|
switch (apduBuffer[OFFSET_P2]) {
|
||||||
case EXPORT_KEY_P2_PRIVATE_AND_PUBLIC:
|
case EXPORT_KEY_P2_PRIVATE_AND_PUBLIC:
|
||||||
publicOnly = false;
|
publicOnly = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
package im.status.keycard;
|
package im.status.keycard;
|
||||||
|
|
||||||
|
import javacard.framework.JCSystem;
|
||||||
|
import javacard.framework.Util;
|
||||||
import javacard.security.ECKey;
|
import javacard.security.ECKey;
|
||||||
import javacard.security.ECPrivateKey;
|
import javacard.security.ECPrivateKey;
|
||||||
import javacard.security.KeyAgreement;
|
import javacard.security.KeyAgreement;
|
||||||
import javacard.security.KeyBuilder;
|
import javacard.security.KeyBuilder;
|
||||||
|
import javacard.security.KeyPair;
|
||||||
|
import javacard.security.RSAPublicKey;
|
||||||
|
import javacardx.crypto.Cipher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods to work with the SECP256k1 curve. This class is not meant to be instantiated, but its init method
|
* Utility methods to work with the SECP256k1 curve. This class is not meant to be instantiated, but its init method
|
||||||
|
@ -49,20 +54,52 @@ public class SECP256k1 {
|
||||||
static final byte SECP256K1_K = (byte)0x01;
|
static final byte SECP256K1_K = (byte)0x01;
|
||||||
|
|
||||||
static final short SECP256K1_KEY_SIZE = 256;
|
static final short SECP256K1_KEY_SIZE = 256;
|
||||||
|
static final short SECP256K1_BYTE_SIZE = (short) (SECP256K1_KEY_SIZE / 8);
|
||||||
|
|
||||||
|
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) (32 + SCHNORR_E_OUT_OFF);
|
||||||
|
static final short SCHNORR_D_OUT_OFF = (short) (64 + SCHNORR_E_OUT_OFF);
|
||||||
|
static final short SCHNORR_D_32_OFF = (short) (32 + SCHNORR_D_OUT_OFF);
|
||||||
|
static final short SCHNORR_TMP1_OUT_OFF = (short) (64 + SCHNORR_D_OUT_OFF);
|
||||||
|
static final short SCHNORR_TMP1_32_OUT_OFF = (short) (32 + SCHNORR_TMP1_OUT_OFF);
|
||||||
|
static final short SCHNORR_TMP2_OUT_OFF = (short) (64 + SCHNORR_TMP1_OUT_OFF);
|
||||||
|
|
||||||
|
static final short TMP_LEN = 288;
|
||||||
|
|
||||||
private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5
|
private static final byte ALG_EC_SVDP_DH_PLAIN_XY = 6; // constant from JavaCard 3.0.5
|
||||||
|
|
||||||
|
|
||||||
private KeyAgreement ecPointMultiplier;
|
private KeyAgreement ecPointMultiplier;
|
||||||
|
private Crypto crypto;
|
||||||
ECPrivateKey tmpECPrivateKey;
|
ECPrivateKey tmpECPrivateKey;
|
||||||
|
|
||||||
|
private KeyPair multPair;
|
||||||
|
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, 0x02};
|
||||||
|
|
||||||
|
private byte[] tmp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocates objects needed by this class. Must be invoked during the applet installation exactly 1 time.
|
* Allocates objects needed by this class. Must be invoked during the applet installation exactly 1 time.
|
||||||
*/
|
*/
|
||||||
SECP256k1() {
|
SECP256k1(Crypto crypto) {
|
||||||
|
this.crypto = crypto;
|
||||||
|
|
||||||
|
this.tmp = JCSystem.makeTransientByteArray(TMP_LEN, JCSystem.CLEAR_ON_RESET);
|
||||||
|
|
||||||
this.ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
|
this.ecPointMultiplier = KeyAgreement.getInstance(ALG_EC_SVDP_DH_PLAIN_XY, false);
|
||||||
this.tmpECPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256K1_KEY_SIZE, false);
|
this.tmpECPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256K1_KEY_SIZE, false);
|
||||||
setCurveParameters(tmpECPrivateKey);
|
setCurveParameters(tmpECPrivateKey);
|
||||||
|
|
||||||
|
multPair = new KeyPair(KeyPair.ALG_RSA_CRT, KeyBuilder.LENGTH_RSA_512);
|
||||||
|
multPair.genKeyPair();
|
||||||
|
pow2 = (RSAPublicKey) multPair.getPublic();
|
||||||
|
pow2.setExponent(CONST_TWO, (short) 0, (short) CONST_TWO.length);
|
||||||
|
|
||||||
|
multCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
|
||||||
|
multCipher.init(pow2, Cipher.MODE_ENCRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,7 +140,7 @@ public class SECP256k1 {
|
||||||
* @return the length of the public key
|
* @return the length of the public key
|
||||||
*/
|
*/
|
||||||
short derivePublicKey(byte[] privateKey, short privOff, byte[] pubOut, short pubOff) {
|
short derivePublicKey(byte[] privateKey, short privOff, byte[] pubOut, short pubOff) {
|
||||||
tmpECPrivateKey.setS(privateKey, privOff, (short)(SECP256K1_KEY_SIZE/8));
|
tmpECPrivateKey.setS(privateKey, privOff, SECP256K1_BYTE_SIZE);
|
||||||
return derivePublicKey(tmpECPrivateKey, pubOut, pubOff);
|
return derivePublicKey(tmpECPrivateKey, pubOut, pubOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,4 +160,69 @@ public class SECP256k1 {
|
||||||
ecPointMultiplier.init(privateKey);
|
ecPointMultiplier.init(privateKey);
|
||||||
return ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
|
return ecPointMultiplier.generateSecret(point, pointOff, pointLen, out, outOff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
short signSchnorr(ECPrivateKey privKey, byte[] pubKey, short pubOff, byte[] data, short dataOff, short dataLen, byte[] output, short outOff) {
|
||||||
|
/*
|
||||||
|
The algorithm Sign(sk, m) is defined as:
|
||||||
|
Let d' = int(sk)
|
||||||
|
Fail if d' = 0 or d' ≥ n
|
||||||
|
Let P = d'⋅G
|
||||||
|
Let d = d' if has_square_y(P), otherwise let d = n - d' .
|
||||||
|
Let rand = hashBIPSchnorrDerive(bytes(d) || m).
|
||||||
|
Let k' = int(rand) mod n.
|
||||||
|
Fail if k' = 0.
|
||||||
|
Let R = k'⋅G.
|
||||||
|
Let k = k' if has_square_y(R), otherwise let k = n - k' .
|
||||||
|
Let e = int(hashBIPSchnorr(bytes(R) || bytes(P) || m)) mod n.
|
||||||
|
Return the signature bytes(R) || bytes((k + ed) mod n).
|
||||||
|
*/
|
||||||
|
//TODO: evaluate if mod must be really applied to the output of the RNG and SHA256, since the hash is statistically very unlikley to be higher than R
|
||||||
|
|
||||||
|
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) 0x0);
|
||||||
|
|
||||||
|
derivePublicKey(tmp, SCHNORR_K_OUT_OFF, output, outOff);
|
||||||
|
crypto.sha256.update(output, outOff, Crypto.KEY_PUB_SIZE);
|
||||||
|
crypto.sha256.update(pubKey, pubOff, Crypto.KEY_PUB_SIZE);
|
||||||
|
crypto.sha256.doFinal(data, dataOff, dataLen, tmp, SCHNORR_E_32_OFF);
|
||||||
|
privKey.getS(tmp, SCHNORR_D_32_OFF);
|
||||||
|
|
||||||
|
tmp[(short)(SCHNORR_TMP1_32_OUT_OFF - 1)] = (byte) crypto.add256(tmp, SCHNORR_E_32_OFF, tmp, SCHNORR_D_32_OFF, tmp, SCHNORR_TMP1_32_OUT_OFF);
|
||||||
|
multCipher.doFinal(tmp, SCHNORR_TMP1_OUT_OFF, (short) 64, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||||
|
multCipher.doFinal(tmp, SCHNORR_D_OUT_OFF, (short) 64, tmp, SCHNORR_TMP2_OUT_OFF);
|
||||||
|
crypto.sub512(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_TMP2_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||||
|
multCipher.doFinal(tmp, SCHNORR_E_OUT_OFF, (short) 64, tmp, SCHNORR_TMP2_OUT_OFF);
|
||||||
|
crypto.sub512(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_TMP2_OUT_OFF, tmp, SCHNORR_TMP1_OUT_OFF);
|
||||||
|
|
||||||
|
short res, res2;
|
||||||
|
|
||||||
|
for (short i = (short) 63; 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[SCHNORR_TMP1_OUT_OFF] &= (byte) 0x7f;
|
||||||
|
|
||||||
|
add256to512(tmp, SCHNORR_TMP1_OUT_OFF, tmp, SCHNORR_K_OUT_OFF, output, (short) (outOff + Crypto.KEY_PUB_SIZE));
|
||||||
|
|
||||||
|
return (short) (Crypto.KEY_PUB_SIZE + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
short add256to512(byte[] a, short aOff, byte[] b, short bOff, byte[] out, short outOff) {
|
||||||
|
short outI = 0;
|
||||||
|
|
||||||
|
for (short i = 63 ; i >= 32 ; i--) {
|
||||||
|
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) + (short)(b[(short)(bOff + 32 + i)] & 0xFF) + outI);
|
||||||
|
out[(short)(outOff + i)] = (byte)outI;
|
||||||
|
outI = (short)(outI >> 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (short i = 31 ; i >= 0 ; i--) {
|
||||||
|
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) + outI);
|
||||||
|
out[(short)(outOff + i)] = (byte)outI;
|
||||||
|
outI = (short)(outI >> 8);
|
||||||
|
}
|
||||||
|
return outI;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ import org.bitcoinj.crypto.HDKeyDerivation;
|
||||||
import org.bouncycastle.jce.ECNamedCurveTable;
|
import org.bouncycastle.jce.ECNamedCurveTable;
|
||||||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||||
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
import org.bouncycastle.jce.spec.ECPublicKeySpec;
|
||||||
|
import org.bouncycastle.math.ec.ECCurve;
|
||||||
|
import org.bouncycastle.math.ec.ECPoint;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.*;
|
||||||
import org.web3j.crypto.*;
|
import org.web3j.crypto.*;
|
||||||
|
@ -1006,6 +1008,16 @@ public class KeycardTest {
|
||||||
response = cmdSet.sign(hash);
|
response = cmdSet.sign(hash);
|
||||||
verifySignResp(data, response);
|
verifySignResp(data, response);
|
||||||
|
|
||||||
|
// Schnorr
|
||||||
|
APDUCommand sign = secureChannel.protectedCommand(0x80, 0xC0, 0x00, 0x01, hash);
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
response = secureChannel.transmit(sdkChannel, sign);
|
||||||
|
System.out.print("Schnorr time: ");
|
||||||
|
System.out.println(System.currentTimeMillis() - time);
|
||||||
|
response.checkOK();
|
||||||
|
|
||||||
|
verifySchnorr(hash, response.getData());
|
||||||
|
|
||||||
// Sign and derive
|
// Sign and derive
|
||||||
String currentPath = new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()).toString();
|
String currentPath = new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()).toString();
|
||||||
String updatedPath = new KeyPath(currentPath + "/2").toString();
|
String updatedPath = new KeyPath(currentPath + "/2").toString();
|
||||||
|
@ -1048,6 +1060,67 @@ public class KeycardTest {
|
||||||
assertEquals(0x6A88, response.getSw());
|
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));
|
||||||
|
|
||||||
|
System.out.println("Rt = " + Hex.toHexString(R.getEncoded(false)));
|
||||||
|
System.out.println("R2t = " + Hex.toHexString(R2.getEncoded(false)));
|
||||||
|
|
||||||
|
assertTrue(R.equals(R2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifySchnorr(byte[] m, byte[] sig) throws Exception {
|
||||||
|
schnorrTest();
|
||||||
|
|
||||||
|
byte[] p = extractPublicKeyFromSignature(sig);
|
||||||
|
|
||||||
|
int off = sig[4] + 5;
|
||||||
|
byte[] rawSig = Arrays.copyOfRange(sig, off, sig.length);
|
||||||
|
|
||||||
|
byte[] r = Arrays.copyOf(rawSig, 65);
|
||||||
|
|
||||||
|
System.out.println("p = " + Hex.toHexString(p));
|
||||||
|
System.out.println("r = " + Hex.toHexString(r));
|
||||||
|
System.out.println("s = " + Hex.toHexString(Arrays.copyOfRange(rawSig, 65, rawSig.length)));
|
||||||
|
|
||||||
|
MessageDigest dg = MessageDigest.getInstance("SHA256");
|
||||||
|
dg.update(r);
|
||||||
|
dg.update(p);
|
||||||
|
dg.update(m);
|
||||||
|
BigInteger e = new BigInteger(1, dg.digest());
|
||||||
|
|
||||||
|
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
|
||||||
|
ECPoint P = ecSpec.getCurve().decodePoint(p);
|
||||||
|
ECPoint G = ecSpec.getG();
|
||||||
|
BigInteger s = new BigInteger(1, Arrays.copyOfRange(rawSig, 65, rawSig.length));
|
||||||
|
s = s.mod(ecSpec.getCurve().getOrder());
|
||||||
|
ECPoint R = G.multiply(s).subtract(P.multiply(e));
|
||||||
|
System.out.println("R = " + Hex.toHexString(R.getEncoded(false)));
|
||||||
|
assertTrue(R.equals(ecSpec.getCurve().decodePoint(r)));
|
||||||
|
}
|
||||||
|
|
||||||
private void verifySignResp(byte[] data, APDUResponse response) throws Exception {
|
private void verifySignResp(byte[] data, APDUResponse response) throws Exception {
|
||||||
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
|
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
|
||||||
assertEquals(0x9000, response.getSw());
|
assertEquals(0x9000, response.getSw());
|
||||||
|
|
Loading…
Reference in New Issue