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) {
|
||||
crypto = new Crypto();
|
||||
secp256k1 = new SECP256k1();
|
||||
secp256k1 = new SECP256k1(crypto);
|
||||
|
||||
keypair = new KeyPair(KeyPair.ALG_EC_FP, SECP256k1.SECP256K1_KEY_SIZE);
|
||||
publicKey = (ECPublicKey) keypair.getPublic();
|
||||
|
|
|
@ -36,7 +36,7 @@ public class Crypto {
|
|||
private Signature hmacSHA512;
|
||||
private HMACKey hmacKey;
|
||||
|
||||
private byte[] hmacBlock;
|
||||
protected byte[] hmacBlock;
|
||||
|
||||
Crypto() {
|
||||
random = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM);
|
||||
|
@ -150,7 +150,7 @@ public class Crypto {
|
|||
* @param out 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) {
|
||||
hmacKey.setKey(key, keyOff, keyLen);
|
||||
hmacSHA512.init(hmacKey, Signature.MODE_SIGN);
|
||||
|
@ -186,7 +186,7 @@ public class Crypto {
|
|||
* @param out 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)) {
|
||||
sub256(out, outOff, n, nOff, out, outOff);
|
||||
}
|
||||
|
@ -201,7 +201,7 @@ public class Crypto {
|
|||
* @param bOff the offset of the b operand
|
||||
* @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;
|
||||
for (short i = 0 ; i < 32; i++) {
|
||||
ai = (short)(a[(short)(aOff + i)] & 0x00ff);
|
||||
|
@ -222,7 +222,7 @@ public class Crypto {
|
|||
* @param aOff the offset of the a operand
|
||||
* @return true if a is 0, false otherwise
|
||||
*/
|
||||
private boolean isZero256(byte[] a, short aOff) {
|
||||
boolean isZero256(byte[] a, short aOff) {
|
||||
boolean isZero = true;
|
||||
|
||||
for (short i = 0; i < (byte) 32; i++) {
|
||||
|
@ -246,7 +246,7 @@ public class Crypto {
|
|||
* @param outOff the offset in the output buffer
|
||||
* @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;
|
||||
for (short i = 31 ; i >= 0 ; i--) {
|
||||
outI = (short) ((short)(a[(short)(aOff + i)] & 0xFF) + (short)(b[(short)(bOff + i)] & 0xFF) + outI);
|
||||
|
@ -267,7 +267,7 @@ public class Crypto {
|
|||
* @param outOff the offset in the output buffer
|
||||
* @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;
|
||||
|
||||
for (short i = 31 ; i >= 0 ; i--) {
|
||||
|
@ -278,4 +278,27 @@ public class Crypto {
|
|||
|
||||
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.security.*;
|
||||
import javacardx.crypto.Cipher;
|
||||
|
||||
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.
|
||||
|
@ -68,6 +68,9 @@ public class KeycardApplet extends Applet {
|
|||
static final byte SIGN_P1_DERIVE_AND_MAKE_CURRENT = 0x02;
|
||||
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_DERIVE = 0x01;
|
||||
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) {
|
||||
crypto = new Crypto();
|
||||
secp256k1 = new SECP256k1();
|
||||
secp256k1 = new SECP256k1(crypto);
|
||||
|
||||
uid = new byte[UID_LENGTH];
|
||||
crypto.random.generateData(uid, (short) 0, UID_LENGTH);
|
||||
|
@ -1104,6 +1107,20 @@ public class KeycardApplet extends Applet {
|
|||
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;
|
||||
|
||||
if (usePinless && !secureChannel.isOpen()) {
|
||||
|
@ -1148,10 +1165,13 @@ public class KeycardApplet extends Applet {
|
|||
outLen += 5;
|
||||
short sigOff = (short) (SecureChannel.SC_OUT_OFFSET + outLen);
|
||||
|
||||
if (schnorr) {
|
||||
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);
|
||||
|
||||
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 + 2)] = (byte) (outLen - 3);
|
||||
|
@ -1212,7 +1232,7 @@ public class KeycardApplet extends Applet {
|
|||
|
||||
boolean publicOnly;
|
||||
|
||||
switch (apduBuffer[ISO7816.OFFSET_P2]) {
|
||||
switch (apduBuffer[OFFSET_P2]) {
|
||||
case EXPORT_KEY_P2_PRIVATE_AND_PUBLIC:
|
||||
publicOnly = false;
|
||||
break;
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package im.status.keycard;
|
||||
|
||||
import javacard.framework.JCSystem;
|
||||
import javacard.framework.Util;
|
||||
import javacard.security.ECKey;
|
||||
import javacard.security.ECPrivateKey;
|
||||
import javacard.security.KeyAgreement;
|
||||
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
|
||||
|
@ -49,20 +54,52 @@ public class SECP256k1 {
|
|||
static final byte SECP256K1_K = (byte)0x01;
|
||||
|
||||
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 KeyAgreement ecPointMultiplier;
|
||||
private Crypto crypto;
|
||||
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.
|
||||
*/
|
||||
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.tmpECPrivateKey = (ECPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, SECP256K1_KEY_SIZE, false);
|
||||
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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -123,4 +160,69 @@ public class SECP256k1 {
|
|||
ecPointMultiplier.init(privateKey);
|
||||
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.spec.ECParameterSpec;
|
||||
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.junit.jupiter.api.*;
|
||||
import org.web3j.crypto.*;
|
||||
|
@ -1006,6 +1008,16 @@ public class KeycardTest {
|
|||
response = cmdSet.sign(hash);
|
||||
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
|
||||
String currentPath = new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()).toString();
|
||||
String updatedPath = new KeyPath(currentPath + "/2").toString();
|
||||
|
@ -1048,6 +1060,67 @@ 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));
|
||||
|
||||
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 {
|
||||
Signature signature = Signature.getInstance("SHA256withECDSA", "BC");
|
||||
assertEquals(0x9000, response.getSw());
|
||||
|
|
Loading…
Reference in New Issue