From f3e36478494dd04b09efe024af5c864e613f0ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 12:17:54 +0200 Subject: [PATCH 01/20] feat: make curve definitions extend `ECCurve` --- applet/src/main/java/opencrypto/jcmathlib/ECCurve.java | 5 ++--- applet/src/main/java/opencrypto/jcmathlib/Example.java | 2 +- applet/src/main/java/opencrypto/jcmathlib/UnitTests.java | 2 +- .../main/java/opencrypto/jcmathlib/curves/SecP256k1.java | 6 +++++- .../main/java/opencrypto/jcmathlib/curves/SecP256r1.java | 6 +++++- .../main/java/opencrypto/jcmathlib/curves/SecP512r1.java | 6 +++++- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java index e3638da8..2a1acedf 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java @@ -1,7 +1,6 @@ package opencrypto.jcmathlib; import javacard.framework.JCSystem; -import javacard.framework.Util; import javacard.security.ECPrivateKey; import javacard.security.ECPublicKey; import javacard.security.KeyBuilder; @@ -56,8 +55,8 @@ public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, ResourceManager disposablePair = newKeyPair(null); disposablePriv = (ECPrivateKey) disposablePair.getPrivate(); disposablePub = (ECPublicKey) disposablePair.getPublic(); - } - + } + /** * Refresh critical information stored in RAM for performance reasons after a card reset (RAM was cleared). */ diff --git a/applet/src/main/java/opencrypto/jcmathlib/Example.java b/applet/src/main/java/opencrypto/jcmathlib/Example.java index 0ca37a32..029e9aa8 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/Example.java +++ b/applet/src/main/java/opencrypto/jcmathlib/Example.java @@ -59,7 +59,7 @@ public void initialize() { // Allocate resources for a required elliptic curve size (in bits) rm = new ResourceManager((short) 256); // Allocate SecP256r1 curve and two EC points on this curve - curve = new ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r, rm); + curve = new SecP256k1(rm); point1 = new ECPoint(curve); point2 = new ECPoint(curve); // Allocate two BigNats large enough to hold scalars of the elliptic curve diff --git a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index 211cb58e..041585eb 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java +++ b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java @@ -120,7 +120,7 @@ public void initialize() { // Pre-allocate test objects (no new allocation for every tested operation) - curve = new ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r, rm); + curve = new SecP256r1(rm); memoryInfoOffset = snapshotAvailableMemory((short) 3, memoryInfo, memoryInfoOffset); customG = new byte[(short) SecP256r1.G.length]; Util.arrayCopyNonAtomic(SecP256r1.G, (short) 0, customG, (short) 0, (short) SecP256r1.G.length); diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java index d9213030..9e9b8ade 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java @@ -1,6 +1,10 @@ package opencrypto.jcmathlib; -public class SecP256k1 { +public class SecP256k1 extends ECCurve { + public SecP256k1(ResourceManager rm) { + super(p, a, b, G, r, rm); + } + public final static byte[] p = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java index dab86fd2..2c33edca 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java @@ -1,6 +1,10 @@ package opencrypto.jcmathlib; -public class SecP256r1 { +public class SecP256r1 extends ECCurve { + public SecP256r1(ResourceManager rm) { + super(p, a, b, G, r, rm); + } + public final static byte[] p = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java index 09e07ca8..f794efe2 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java @@ -1,6 +1,10 @@ package opencrypto.jcmathlib; -public class SecP512r1 { +public class SecP512r1 extends ECCurve { + public SecP512r1(ResourceManager rm) { + super(p, a, b, G, r, rm); + } + public final static byte[] p = { (byte) 0xaa, (byte) 0xdd, (byte) 0x9d, (byte) 0xb8, (byte) 0xdb, (byte) 0xe9, (byte) 0xc4, (byte) 0x8b, From 785933310a4656db84eb1271645921bc479f62a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 12:21:31 +0200 Subject: [PATCH 02/20] refactor: remove `customCurve` from `UnitTests` --- .../java/opencrypto/jcmathlib/UnitTests.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index 041585eb..84e312e9 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java +++ b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java @@ -91,10 +91,6 @@ public class UnitTests extends Applet { ECPoint point1; ECPoint point2; - byte[] customG; - ECCurve customCurve; - ECPoint customPoint; - BigNat bn1; BigNat bn2; BigNat bn3; @@ -122,15 +118,11 @@ public void initialize() { // Pre-allocate test objects (no new allocation for every tested operation) curve = new SecP256r1(rm); memoryInfoOffset = snapshotAvailableMemory((short) 3, memoryInfo, memoryInfoOffset); - customG = new byte[(short) SecP256r1.G.length]; - Util.arrayCopyNonAtomic(SecP256r1.G, (short) 0, customG, (short) 0, (short) SecP256r1.G.length); - customCurve = new ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, customG, SecP256r1.r, rm); memoryInfoOffset = snapshotAvailableMemory((short) 5, memoryInfo, memoryInfoOffset); point1 = new ECPoint(curve); memoryInfoOffset = snapshotAvailableMemory((short) 6, memoryInfo, memoryInfoOffset); point2 = new ECPoint(curve); - customPoint = new ECPoint(customCurve); // Testing BigNat objects used in tests memoryInfoOffset = snapshotAvailableMemory((short) 7, memoryInfo, memoryInfoOffset); @@ -354,9 +346,6 @@ void updateAfterReset() { if (curve != null) { curve.updateAfterReset(); } - if (customCurve != null) { - customCurve.updateAfterReset(); - } if (rm != null) { rm.refreshAfterReset(); rm.unlockAll(); @@ -366,10 +355,10 @@ void updateAfterReset() { void testEcDbl(APDU apdu) { byte[] apduBuffer = apdu.getBuffer(); - customPoint.setW(apduBuffer, ISO7816.OFFSET_CDATA, customCurve.POINT_SIZE); - customPoint.makeDouble(); + point1.setW(apduBuffer, ISO7816.OFFSET_CDATA, curve.POINT_SIZE); + point1.makeDouble(); - short len = customPoint.getW(apduBuffer, (short) 0); + short len = point1.getW(apduBuffer, (short) 0); apdu.setOutgoingAndSend((short) 0, len); } From 176382692cc56bf7e4e2bcd5a5a66d6fbad7695f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 12:51:01 +0200 Subject: [PATCH 03/20] feat: add cofactor to `ECCurve` constructor --- applet/src/main/java/opencrypto/jcmathlib/ECCurve.java | 8 +++++--- .../main/java/opencrypto/jcmathlib/curves/SecP256k1.java | 4 +++- .../main/java/opencrypto/jcmathlib/curves/SecP256r1.java | 4 +++- .../main/java/opencrypto/jcmathlib/curves/SecP512r1.java | 4 +++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java index 2a1acedf..cd52c65b 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java @@ -14,6 +14,7 @@ public class ECCurve { public ResourceManager rm; public byte[] p, a, b, G, r; + public short k; public BigNat pBN, aBN, bBN, rBN; @@ -31,7 +32,7 @@ public class ECCurve { * @param G array with base point G * @param r array with r */ - public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, ResourceManager rm) { + public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, short k, ResourceManager rm) { KEY_BIT_LENGTH = (short) (p.length * 8); POINT_SIZE = (short) G.length; COORD_SIZE = (short) ((short) (G.length - 1) / 2); @@ -41,6 +42,7 @@ public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, ResourceManager this.b = b; this.G = G; this.r = r; + this.k = k; this.rm = rm; pBN = new BigNat(COORD_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_RESET, rm); @@ -89,14 +91,14 @@ KeyPair newKeyPair(KeyPair keyPair) { privKey.setB(b, (short) 0, (short) b.length); privKey.setG(G, (short) 0, (short) G.length); privKey.setR(r, (short) 0, (short) r.length); - privKey.setK((short) 1); + privKey.setK(k); pubKey.setFieldFP(p, (short) 0, (short) p.length); pubKey.setA(a, (short) 0, (short) a.length); pubKey.setB(b, (short) 0, (short) b.length); pubKey.setG(G, (short) 0, (short) G.length); pubKey.setR(r, (short) 0, (short) r.length); - pubKey.setK((short) 1); + pubKey.setK(k); keyPair.genKeyPair(); diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java index 9e9b8ade..f5fd84da 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java @@ -2,9 +2,11 @@ public class SecP256k1 extends ECCurve { public SecP256k1(ResourceManager rm) { - super(p, a, b, G, r, rm); + super(p, a, b, G, r, k, rm); } + public final static short k = 1; + public final static byte[] p = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java index 2c33edca..f08b1209 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java @@ -2,9 +2,11 @@ public class SecP256r1 extends ECCurve { public SecP256r1(ResourceManager rm) { - super(p, a, b, G, r, rm); + super(p, a, b, G, r, k, rm); } + public final static short k = 1; + public final static byte[] p = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java index f794efe2..d3554610 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java @@ -2,9 +2,11 @@ public class SecP512r1 extends ECCurve { public SecP512r1(ResourceManager rm) { - super(p, a, b, G, r, rm); + super(p, a, b, G, r, k, rm); } + public final static short k = 1; + public final static byte[] p = { (byte) 0xaa, (byte) 0xdd, (byte) 0x9d, (byte) 0xb8, (byte) 0xdb, (byte) 0xe9, (byte) 0xc4, (byte) 0x8b, From 2a38bf853b64f960d125da64d66f0c4e2d40e77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 12:52:43 +0200 Subject: [PATCH 04/20] refactor: simplify changing curve in tests --- applet/src/test/java/tests/JCMathLibTest.java | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/applet/src/test/java/tests/JCMathLibTest.java b/applet/src/test/java/tests/JCMathLibTest.java index 4fd992d6..b2954ec5 100644 --- a/applet/src/test/java/tests/JCMathLibTest.java +++ b/applet/src/test/java/tests/JCMathLibTest.java @@ -8,19 +8,18 @@ import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; -import opencrypto.jcmathlib.OperationSupport; -import opencrypto.jcmathlib.UnitTests; -import opencrypto.jcmathlib.SecP256r1; -import org.bouncycastle.jce.ECNamedCurveTable; +import opencrypto.jcmathlib.*; import org.bouncycastle.jce.interfaces.ECPublicKey; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.junit.jupiter.api.*; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; +import java.lang.Integer; import java.math.BigInteger; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -39,9 +38,22 @@ public class JCMathLibTest extends BaseTest { public static Map perfMap = new HashMap<>(); public static String atr; + public static ECParameterSpec CURVE_SPEC = null; + public static byte[] CURVE_P = SecP256r1.p; + public static byte[] CURVE_R = SecP256r1.r; + public static byte[] CURVE_A = SecP256r1.a; + public static byte[] CURVE_B = SecP256r1.b; + public static byte[] CURVE_G = SecP256r1.G; + public static short CURVE_K = SecP256r1.k; + public JCMathLibTest() throws Exception { this.setCardType(UnitTests.CARD_TYPE == OperationSupport.SIMULATOR ? CardType.JCARDSIMLOCAL : CardType.PHYSICAL); this.setSimulateStateful(true); + ECCurve curve = new ECCurve.Fp(new BigInteger(1, CURVE_P), new BigInteger(1, CURVE_A), new BigInteger(1, CURVE_B)); + BigInteger x = new BigInteger(1, Arrays.copyOfRange(CURVE_G, 1, CURVE_G.length / 2 + 1)); + BigInteger y = new BigInteger(1, Arrays.copyOfRange(CURVE_G, 1 + CURVE_G.length / 2, CURVE_G.length)); + CURVE_SPEC = new ECParameterSpec(curve, curve.createPoint(x, y), new BigInteger(1, CURVE_R), BigInteger.valueOf(CURVE_K)); + statefulCard = connect(); } @@ -102,9 +114,8 @@ public void eccNegation() throws Exception { @Test public void eccMultiplyGenerator() throws Exception { perfMap.put("eccMultiplyGenerator/INS_EC_MUL", new Long(-1)); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1"); - ECPoint point = ecSpec.getG(); - BigInteger scalar = randomBigNat(256); + ECPoint point = CURVE_SPEC.getG(); + BigInteger scalar = randomBigNat(BIGNAT_BIT_LENGTH); ECPoint result = point.multiply(scalar); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_EC_MUL, scalar.toByteArray().length, 0, Util.concat(scalar.toByteArray(), point.getEncoded(false))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -119,7 +130,7 @@ public void eccMultiplyGenerator() throws Exception { public void eccMultiplyRandom() throws Exception { perfMap.put("eccMultiplyRandom/INS_EC_MUL", new Long(-1)); ECPoint point = randECPoint(); - BigInteger scalar = randomBigNat(256); + BigInteger scalar = randomBigNat(BIGNAT_BIT_LENGTH); ECPoint result = point.multiply(scalar); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_EC_MUL, scalar.toByteArray().length, 0, Util.concat(scalar.toByteArray(), point.getEncoded(false))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -145,8 +156,7 @@ public void eccIsEqual() throws Exception { @Test public void eccDoubleGenerator() throws Exception { perfMap.put("eccDoubleGenerator/INS_EC_DBL", new Long(-1)); - ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1"); - ECPoint point = ecSpec.getG(); + ECPoint point = CURVE_SPEC.getG(); ECPoint doubled = point.add(point); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_EC_DBL, 0, 0, point.getEncoded(false)); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -206,7 +216,7 @@ public void eccMultRandomAndAdd() throws Exception { perfMap.put("eccMultRandomAndAdd/INS_EC_MUL_ADD", new Long(-1)); ECPoint point1 = randECPoint(); ECPoint point2 = randECPoint(); - BigInteger scalar = randomBigNat(256); + BigInteger scalar = randomBigNat(BIGNAT_BIT_LENGTH); ECPoint result = point1.multiply(scalar).add(point2); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_EC_MUL_ADD, scalar.toByteArray().length, 0, Util.concat(Util.concat(scalar.toByteArray(), point1.getEncoded(false)), point2.getEncoded(false))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -406,7 +416,7 @@ public void bigNatSetValue() throws Exception { public void bigNatModSqrt() throws Exception { perfMap.put("bigNatModSqrt/INS_BN_SQRT_MOD", new Long(-1)); BigInteger num = randomBigNat(BIGNAT_BIT_LENGTH); - BigInteger mod = new BigInteger(1, SecP256r1.p); + BigInteger mod = new BigInteger(1, CURVE_P); // Sample num until we get a quadratic residue while (!num.modPow(mod.subtract(BigInteger.valueOf(1)).divide(BigInteger.valueOf(2)), mod).equals(BigInteger.valueOf(1))) { num = randomBigNat(BIGNAT_BIT_LENGTH); @@ -427,7 +437,7 @@ public void bigNatModAdd() throws Exception { perfMap.put("bigNatModAdd/INS_BN_ADD_MOD", new Long(-1)); BigInteger num1 = randomBigNat(BIGNAT_BIT_LENGTH); BigInteger num2 = randomBigNat(BIGNAT_BIT_LENGTH); - BigInteger num3 = new BigInteger(1, SecP256r1.r); + BigInteger num3 = new BigInteger(1, CURVE_R); BigInteger result = (num1.add(num2)).mod(num3); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_ADD_MOD, Util.trimLeadingZeroes(num1.toByteArray()).length, Util.trimLeadingZeroes(num2.toByteArray()).length, Util.concat(Util.trimLeadingZeroes(num1.toByteArray()), Util.trimLeadingZeroes(num2.toByteArray()), Util.trimLeadingZeroes(num3.toByteArray()))); @@ -444,7 +454,7 @@ public void bigNatModSub() throws Exception { perfMap.put("bigNatModSub/INS_BN_SUB_MOD", new Long(-1)); BigInteger num1 = randomBigNat(BIGNAT_BIT_LENGTH); BigInteger num2 = randomBigNat(BIGNAT_BIT_LENGTH); - BigInteger num3 = new BigInteger(1, SecP256r1.r); + BigInteger num3 = new BigInteger(1, CURVE_R); BigInteger result = (num1.subtract(num2)).mod(num3); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_SUB_MOD, Util.trimLeadingZeroes(num1.toByteArray()).length, Util.trimLeadingZeroes(num2.toByteArray()).length, Util.concat(Util.trimLeadingZeroes(num1.toByteArray()), Util.trimLeadingZeroes(num2.toByteArray()), Util.trimLeadingZeroes(num3.toByteArray()))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -460,7 +470,7 @@ public void bigNatModMult() throws Exception { perfMap.put("bigNatModMult/INS_BN_MUL_MOD", new Long(-1)); BigInteger num1 = randomBigNat(BIGNAT_BIT_LENGTH); BigInteger num2 = randomBigNat(BIGNAT_BIT_LENGTH); - BigInteger num3 = new BigInteger(1, SecP256r1.r); + BigInteger num3 = new BigInteger(1, CURVE_R); BigInteger result = (num1.multiply(num2)).mod(num3); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_MUL_MOD, Util.trimLeadingZeroes(num1.toByteArray()).length, Util.trimLeadingZeroes(num2.toByteArray()).length, Util.concat(Util.trimLeadingZeroes(num1.toByteArray()), Util.trimLeadingZeroes(num2.toByteArray()), Util.trimLeadingZeroes(num3.toByteArray()))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -476,9 +486,9 @@ public void bigNatModExp() throws Exception { perfMap.put("bigNatModExp/INS_BN_EXP_MOD", new Long(-1)); // Test multiple configurations (to check for OperationSupport.RSA_KEY_REFRESH) for (int i = 0; i < 3; ++i) { - BigInteger base = randomBigNat(256); - BigInteger exp = randomBigNat(256); - BigInteger mod = new BigInteger(1, SecP256r1.r); + BigInteger base = randomBigNat(BIGNAT_BIT_LENGTH); + BigInteger exp = randomBigNat(BIGNAT_BIT_LENGTH); + BigInteger mod = new BigInteger(1, CURVE_R); BigInteger result = (base.modPow(exp, mod)); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_EXP_MOD, Util.trimLeadingZeroes(base.toByteArray()).length, Util.trimLeadingZeroes(exp.toByteArray()).length, Util.concat(Util.trimLeadingZeroes(base.toByteArray()), Util.trimLeadingZeroes(exp.toByteArray()), Util.trimLeadingZeroes(mod.toByteArray()))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -493,9 +503,9 @@ public void bigNatModExp() throws Exception { @Test public void bigNatModSq() throws Exception { perfMap.put("bigNatModSq/INS_BN_SQ_MOD", new Long(-1)); - BigInteger base = randomBigNat(256); + BigInteger base = randomBigNat(BIGNAT_BIT_LENGTH); BigInteger exp = BigInteger.valueOf(2); - BigInteger mod = new BigInteger(1, SecP256r1.r); + BigInteger mod = new BigInteger(1, CURVE_R); BigInteger result = (base.modPow(exp, mod)); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_SQ_MOD, Util.trimLeadingZeroes(base.toByteArray()).length, (short) 0, Util.concat(Util.trimLeadingZeroes(base.toByteArray()), Util.trimLeadingZeroes(mod.toByteArray()))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -510,7 +520,7 @@ public void bigNatModSq() throws Exception { public void bigNatModInv() throws Exception { perfMap.put("bigNatModInv/INS_BN_INV_MOD", new Long(-1)); BigInteger base = randomBigNat(BIGNAT_BIT_LENGTH); - BigInteger mod = new BigInteger(1, SecP256r1.r); + BigInteger mod = new BigInteger(1, CURVE_R); BigInteger result = base.modInverse(mod); CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_INV_MOD, Util.trimLeadingZeroes(base.toByteArray()).length, 0, Util.concat(Util.trimLeadingZeroes(base.toByteArray()), Util.trimLeadingZeroes(mod.toByteArray()))); ResponseAPDU resp = statefulCard.transmit(cmd); @@ -668,9 +678,8 @@ public static int bytesToInt(byte[] data) { public static ECPoint randECPoint() throws Exception { Security.addProvider(new BouncyCastleProvider()); - ECParameterSpec ecSpec_named = ECNamedCurveTable.getParameterSpec("secp256r1"); KeyPairGenerator kpg = KeyPairGenerator.getInstance("ECDSA", "BC"); - kpg.initialize(ecSpec_named); + kpg.initialize(CURVE_SPEC); KeyPair apair = kpg.generateKeyPair(); ECPublicKey apub = (ECPublicKey) apair.getPublic(); return apub.getQ(); From 84667fd12b5e6ed795879f38c35919d2efba00c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 17:46:52 +0200 Subject: [PATCH 05/20] feat: implement `shiftLeft` --- .../opencrypto/jcmathlib/BigNatInternal.java | 35 ++++++++++++++++++- .../java/opencrypto/jcmathlib/UnitTests.java | 14 ++++++++ applet/src/test/java/tests/JCMathLibTest.java | 16 +++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java index c607cc9a..f1d5f065 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java @@ -456,7 +456,7 @@ public void mult(BigNatInternal other) { * Right bit shift with carry * * @param bits number of bits to shift by - * @param carry XORed into the highest byte + * @param carry ORed into the highest byte */ protected void shiftRight(short bits, short carry) { // assumes 0 <= bits < 8 @@ -480,6 +480,39 @@ public void shiftRight(short bits) { shiftRight(bits, (short) 0); } + /** + * Left bit shift with carry + * + * @param bits number of bits to shift by + * @param carry ORed into the lowest byte + */ + protected void shiftLeft(short bits, short carry) { + // assumes 0 <= bits < 8 + short mask = (short) ((-1 << (8 - bits)) & 0xff); // highest `bits` bits set to 1 + for (short i = (short) (value.length - 1); i >= offset; --i) { + short current = (short) (value[i] & 0xff); + short previous = current; + current <<= bits; + value[i] = (byte) (current | carry); + carry = (short) (previous & mask); + carry >>= (8 - bits); + } + + if (carry != 0) { + setSize((short) (size + 1)); + value[offset] = (byte) carry; + } + } + + /** + * Right bit shift + * + * @param bits number of bits to shift by + */ + public void shiftLeft(short bits) { + shiftLeft(bits, (short) 0); + } + /** * Divide this by divisor and store the remained in this and quotient in quotient. * diff --git a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index 84e312e9..cbfda5c2 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java +++ b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java @@ -47,6 +47,7 @@ public class UnitTests extends Applet { public final static byte INS_BN_SQ = (byte) 0x26; public final static byte INS_BN_MUL_SCHOOL = (byte) 0x27; public final static byte INS_BN_SET_VALUE = (byte) 0x28; + public final static byte INS_BN_SHIFT_LEFT = (byte) 0x29; public final static byte INS_BN_ADD_MOD = (byte) 0x30; public final static byte INS_BN_SUB_MOD = (byte) 0x31; @@ -240,6 +241,9 @@ public void process(APDU apdu) { case INS_BN_SHIFT_RIGHT: testBnShiftRight(apdu, dataLen); break; + case INS_BN_SHIFT_LEFT: + testBnShiftLeft(apdu, dataLen); + break; case INS_BN_MUL_SCHOOL: testBnMulSchool(apdu, dataLen); break; @@ -521,6 +525,16 @@ void testBnShiftRight(APDU apdu, short dataLen) { apdu.setOutgoingAndSend((short) 0, len); } + void testBnShiftLeft(APDU apdu, short dataLen) { + byte[] apduBuffer = apdu.getBuffer(); + short p1 = (short) (apduBuffer[ISO7816.OFFSET_P1] & 0x00FF); + + bn1.fromByteArray(apduBuffer, ISO7816.OFFSET_CDATA, dataLen); + bn1.shiftLeft(p1); + short len = bn1.copyToByteArray(apduBuffer, (short) 0); + apdu.setOutgoingAndSend((short) 0, len); + } + void testBnMulSchool(APDU apdu, short dataLen) { byte[] apduBuffer = apdu.getBuffer(); short p1 = (short) (apduBuffer[ISO7816.OFFSET_P1] & 0x00FF); diff --git a/applet/src/test/java/tests/JCMathLibTest.java b/applet/src/test/java/tests/JCMathLibTest.java index b2954ec5..7b08e7cf 100644 --- a/applet/src/test/java/tests/JCMathLibTest.java +++ b/applet/src/test/java/tests/JCMathLibTest.java @@ -369,6 +369,22 @@ public void bigNatShiftRight() throws Exception { statefulCard.transmit(new CommandAPDU(APDU_CLEANUP)); } + @Test + public void bigNatShiftLeft() throws Exception { + perfMap.put("bigNatShiftLeft(8b)/INS_BN_SHIFT_LEFT", (long) -1); + for (int bits = 0; bits < 8; ++bits) { + BigInteger num1 = randomBigNat(BIGNAT_BIT_LENGTH); + BigInteger result = num1.shiftLeft(bits); + CommandAPDU cmd = new CommandAPDU(UnitTests.CLA_OC_UT, UnitTests.INS_BN_SHIFT_LEFT, bits, 0, num1.toByteArray()); + ResponseAPDU resp = statefulCard.transmit(cmd); + perfMap.put("bigNatShiftLeft(8b)/INS_BN_SHIFT_LEFT", statefulCard.getLastTransmitTime()); + + Assertions.assertEquals(ISO7816.SW_NO_ERROR & 0xffff, resp.getSW()); + Assertions.assertEquals(result, new BigInteger(1, resp.getData())); + } + statefulCard.transmit(new CommandAPDU(APDU_CLEANUP)); + } + @Test public void bigNatMultiplicationSlow() throws Exception { perfMap.put("bigNatMultiplicationSlow/INS_BN_MUL_SCHOOL", new Long(-1)); From 15619154145e16592db30edb94bdc53fbfcdff50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 18:19:48 +0200 Subject: [PATCH 06/20] feat: fully implement Tonelli-Shanks algorithm --- .../java/opencrypto/jcmathlib/BigNat.java | 114 ++++++++++++++---- 1 file changed, 89 insertions(+), 25 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java index a4849a8d..019477c3 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java @@ -405,41 +405,31 @@ public void modSq(BigNat mod) { */ public void modSqrt(BigNat p) { BigNat s = rm.BN_A; - BigNat exp = rm.BN_A; + BigNat exp = rm.BN_G; BigNat p1 = rm.BN_B; BigNat q = rm.BN_C; BigNat tmp = rm.BN_D; - BigNat z = rm.BN_E; + BigNat z = rm.EC_BN_E; // TODO temporary workaround to avoid allocating another BigNat + BigNat i = rm.BN_G; + BigNat t = rm.BN_B; + BigNat b = rm.BN_C; - // 1. By factoring out powers of 2, find Q and S such that p-1=Q2^S p-1=Q*2^S and Q is odd + // 1. Find Q and S such that p - 1 = Q * 2^S and Q is odd p1.lock(); p1.clone(p); p1.decrement(); - // Compute Q - q.lock(); - q.clone(p1); - q.shiftRight((short) 1); // Q /= 2 - - // Compute S s.lock(); - s.setSize(p.length()); + s.setSize((short) 1); // assumes S < 256 s.zero(); - tmp.lock(); - tmp.setSize(p.length()); - tmp.zero(); - while (!tmp.equals(q)) { + q.lock(); + q.clone(p1); + + while (!q.isOdd()) { s.increment(); - // TODO replace with modMult(s, q, p) - tmp.setSizeToMax(false); - tmp.clone(s); - tmp.mult(q); - tmp.mod(p); - tmp.shrink(); + q.shiftRight((short) 1); } - tmp.unlock(); - s.unlock(); // 2. Find the first quadratic non-residue z by brute-force search exp.lock(); @@ -451,23 +441,97 @@ public void modSqrt(BigNat p) { z.setSize(p.length()); z.setValue((byte) 1); tmp.lock(); + tmp.setSize(p.length()); tmp.setValue((byte) 1); while (!tmp.equals(p1)) { z.increment(); tmp.copy(z); - tmp.modExp(exp, p); + tmp.modExp(exp, p); // Euler's criterion } p1.unlock(); tmp.unlock(); - z.unlock(); + + // 3. Compute the first candidate exp.copy(q); - q.unlock(); exp.increment(); exp.shiftRight((short) 1); + t.lock(); + t.copy(this); + t.modExp(q, p); + + if (t.isZero()) { + z.unlock(); + s.unlock(); + t.unlock(); + exp.unlock(); + q.unlock(); + zero(); + return; + } + mod(p); modExp(exp, p); exp.unlock(); + + if (t.isOne()) { + z.unlock(); + s.unlock(); + t.unlock(); + q.unlock(); + return; + } + + // 4. Search for further candidates + z.modExp(q, p); + q.unlock(); + + while(true) { + tmp.lock(); + tmp.copy(t); + i.lock(); + i.setSize(p.length()); + i.zero(); + + do { + tmp.modSq(p); + i.increment(); + } while (!tmp.isOne()); + + tmp.unlock(); + + b.lock(); + b.copy(z); + s.subtract(i); + s.decrement(); + + tmp.lock(); + tmp.setValue((byte) 1); + while(!s.isZero()) { + tmp.shiftLeft((short) 1); + s.decrement(); + } + b.modExp(tmp, p); + tmp.unlock(); + s.copy(i); + i.unlock(); + z.copy(b); + z.modSq(p); + t.modMult(z, p); + modMult(b, p); + b.unlock(); + + if(t.isZero()) { + zero(); + break; + } + if(t.isOne()) { + break; + } + } + z.unlock(); + s.unlock(); + t.unlock(); } } From cee7cc750f951ca77f685f4f6ba6067aa73d4b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 19:10:56 +0200 Subject: [PATCH 07/20] fix: make scalar multiplication work with short scalars in simulator --- applet/src/main/java/opencrypto/jcmathlib/ECPoint.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java index b07c7843..988b38d9 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java @@ -420,8 +420,8 @@ private short multAndAddKA(BigNat scalar, ECPoint point, byte[] outBuffer, short rm.lock(pointBuffer); short len = getW(pointBuffer, (short) 0); curve.disposablePriv.setG(pointBuffer, (short) 0, len); - len = scalar.copyToByteArray(pointBuffer, (short) 0); - curve.disposablePriv.setS(pointBuffer, (short) 0, len); + scalar.prependZeros((short) curve.r.length, pointBuffer, (short) 0); + curve.disposablePriv.setS(pointBuffer, (short) 0, (short) curve.r.length); rm.ecAddKA.init(curve.disposablePriv); len = point.getW(pointBuffer, (short) 0); @@ -458,11 +458,11 @@ public short multXYKA(BigNat scalar, byte[] outBuffer, short outBufferOffset) { byte[] pointBuffer = rm.POINT_ARRAY_B; rm.lock(pointBuffer); - short len = scalar.copyToByteArray(pointBuffer, (short) 0); - curve.disposablePriv.setS(pointBuffer, (short) 0, len); + scalar.prependZeros((short) curve.r.length, pointBuffer, (short) 0); + curve.disposablePriv.setS(pointBuffer, (short) 0, (short) curve.r.length); rm.ecMultKA.init(curve.disposablePriv); - len = getW(pointBuffer, (short) 0); + short len = getW(pointBuffer, (short) 0); len = rm.ecMultKA.generateSecret(pointBuffer, (short) 0, len, outBuffer, outBufferOffset); rm.unlock(pointBuffer); return len; From 0362760703b616fad74024a1b1b1e9b1ddef7ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Wed, 26 Jul 2023 19:34:03 +0200 Subject: [PATCH 08/20] feat: add Wei25519 support --- .../java/opencrypto/jcmathlib/ECCurve.java | 23 +++++- .../java/opencrypto/jcmathlib/ECPoint.java | 26 +++++-- .../jcmathlib/OperationSupport.java | 5 ++ .../opencrypto/jcmathlib/ResourceManager.java | 4 + .../opencrypto/jcmathlib/curves/Wei25519.java | 76 +++++++++++++++++++ 5 files changed, 124 insertions(+), 10 deletions(-) create mode 100644 applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java index cd52c65b..9012c197 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java @@ -33,7 +33,21 @@ public class ECCurve { * @param r array with r */ public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, short k, ResourceManager rm) { - KEY_BIT_LENGTH = (short) (p.length * 8); + short bits = (short) (p.length * 8); + if (OperationSupport.getInstance().EC_PRECISE_BITLENGTH) { + for (short i = 0; i < (short) p.length; ++i) { + bits -= 8; + if (p[i] != (byte) 0x00) { + byte tmp = p[i]; + while (tmp != (byte) 0x00) { + tmp >>= (short) 1; + ++bits; + } + break; + } + } + } + KEY_BIT_LENGTH = bits; POINT_SIZE = (short) G.length; COORD_SIZE = (short) ((short) (G.length - 1) / 2); @@ -91,16 +105,17 @@ KeyPair newKeyPair(KeyPair keyPair) { privKey.setB(b, (short) 0, (short) b.length); privKey.setG(G, (short) 0, (short) G.length); privKey.setR(r, (short) 0, (short) r.length); - privKey.setK(k); + privKey.setK(OperationSupport.getInstance().EC_SET_COFACTOR ? k : (short) 1); pubKey.setFieldFP(p, (short) 0, (short) p.length); pubKey.setA(a, (short) 0, (short) a.length); pubKey.setB(b, (short) 0, (short) b.length); pubKey.setG(G, (short) 0, (short) G.length); pubKey.setR(r, (short) 0, (short) r.length); - pubKey.setK(k); + pubKey.setK(OperationSupport.getInstance().EC_SET_COFACTOR ? k : (short) 1); - keyPair.genKeyPair(); + privKey.setS(ResourceManager.CONST_ONE, (short) 0, (short) 1); + pubKey.setW(G, (short) 0, (short) G.length); return keyPair; } diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java index 988b38d9..bb66ecd4 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java @@ -47,7 +47,21 @@ public final void updatePointObjects() { * Generates new random point value. */ public void randomize() { - pointKeyPair.genKeyPair(); + if (OperationSupport.getInstance().EC_GEN) { + pointKeyPair.genKeyPair(); // Fails for some curves on some cards + } else { + BigNat tmp = rm.EC_BN_A; + rm.lock(rm.ARRAY_A); + rm.rng.generateData(rm.ARRAY_A, (short) 0, (short) (curve.KEY_BIT_LENGTH / 8 + 16)); + tmp.lock(); + tmp.fromByteArray(rm.ARRAY_A, (short) 0, (short) (curve.KEY_BIT_LENGTH / 8 + 16)); + tmp.mod(curve.rBN); + tmp.shrink(); + rm.unlock(rm.ARRAY_A); + point.setW(curve.G, (short) 0, (short) curve.G.length); + multiplication(tmp); + tmp.unlock(); + } } /** @@ -513,8 +527,8 @@ private void multX(BigNat scalar) { // Check if public point corresponds to the "secret" (i.e., our scalar) rm.lock(resultBuffer); - len = scalar.copyToByteArray(resultBuffer, (short) 0); - curve.disposablePriv.setS(resultBuffer, (short) 0, len); + scalar.prependZeros((short) curve.r.length, resultBuffer, (short) 0); + curve.disposablePriv.setS(resultBuffer, (short) 0, (short) curve.r.length); curve.disposablePub.setW(pointBuffer, (short) 0, curve.POINT_SIZE); if (!SignVerifyECDSA(curve.disposablePriv, curve.disposablePub, rm.verifyEcdsa, resultBuffer)) { // If verification fails, then pick the y2.lock(); @@ -545,12 +559,12 @@ private short multXKA(BigNat scalar, byte[] outBuffer, short outBufferOffset) { byte[] pointBuffer = rm.POINT_ARRAY_B; // NOTE: potential problem on real cards (j2e) - when small scalar is used (e.g., BigNat.TWO), operation sometimes freezes rm.lock(pointBuffer); - short len = scalar.copyToByteArray(pointBuffer, (short) 0); - curve.disposablePriv.setS(pointBuffer, (short) 0, len); + scalar.prependZeros((short) curve.r.length, pointBuffer, (short) 0); + curve.disposablePriv.setS(pointBuffer, (short) 0, (short) curve.r.length); rm.ecMultKA.init(curve.disposablePriv); - len = getW(pointBuffer, (short) 0); + short len = getW(pointBuffer, (short) 0); rm.ecMultKA.generateSecret(pointBuffer, (short) 0, len, outBuffer, outBufferOffset); rm.unlock(pointBuffer); // Return always length of whole coordinate X instead of len - some real cards returns shorter value equal to SHA-1 output size although PLAIN results is filled into buffer (GD60) diff --git a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java index 2c25e2f4..9c7bdb8c 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java +++ b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java @@ -33,6 +33,9 @@ public class OperationSupport { public boolean EC_HW_X = true; public boolean EC_HW_ADD = false; public boolean EC_SW_DOUBLE = false; + public boolean EC_PRECISE_BITLENGTH = true; + public boolean EC_SET_COFACTOR = false; + public boolean EC_GEN = true; private OperationSupport() { } @@ -51,12 +54,14 @@ public void setCard(short card_identifier) { EC_HW_XY = true; EC_HW_ADD = true; EC_SW_DOUBLE = true; + EC_PRECISE_BITLENGTH = false; break; case JCOP21: RSA_PUB = true; RSA_EXTRA_MOD = true; RSA_APPEND_MOD = true; EC_SW_DOUBLE = true; + EC_GEN = false; break; case GD60: RSA_PUB = true; diff --git a/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java b/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java index c281a316..e6f0908b 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java @@ -12,6 +12,7 @@ public class ResourceManager { public ObjectAllocator memAlloc; + RandomData rng; MessageDigest hashEngine; KeyAgreement ecMultKA; KeyAgreement ecAddKA; @@ -23,6 +24,7 @@ public class ResourceManager { byte[] ARRAY_A, ARRAY_B, POINT_ARRAY_A, POINT_ARRAY_B, HASH_ARRAY; + static byte[] CONST_ONE = {0x01}; static byte[] CONST_TWO = {0x02}; BigNat BN_WORD; @@ -148,6 +150,8 @@ else if (maxEcLength <= (short) 512) { expPub = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, MAX_EXP_BIT_LENGTH, false); expPriv = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, MAX_EXP_BIT_LENGTH, false); expCiph = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + + rng = RandomData.getInstance(RandomData.ALG_SECURE_RANDOM); } /** diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java b/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java new file mode 100644 index 00000000..a67f5dfa --- /dev/null +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java @@ -0,0 +1,76 @@ +package opencrypto.jcmathlib; + +public class Wei25519 extends ECCurve { + public Wei25519(ResourceManager rm) { + super(p, a, b, G, r, k, rm); + } + + public final static short k = 8; + + public final static byte[] p = { + (byte) 0x7f, (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, (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) 0xed + }; + + + public final static byte[] a = { + (byte) 0x2a, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0x98, + (byte) 0x49, (byte) 0x14, (byte) 0xa1, (byte) 0x44 + }; + + public final static byte[] b = { + (byte) 0x7b, (byte) 0x42, (byte) 0x5e, (byte) 0xd0, + (byte) 0x97, (byte) 0xb4, (byte) 0x25, (byte) 0xed, + (byte) 0x09, (byte) 0x7b, (byte) 0x42, (byte) 0x5e, + (byte) 0xd0, (byte) 0x97, (byte) 0xb4, (byte) 0x25, + (byte) 0xed, (byte) 0x09, (byte) 0x7b, (byte) 0x42, + (byte) 0x5e, (byte) 0xd0, (byte) 0x97, (byte) 0xb4, + (byte) 0x26, (byte) 0x0b, (byte) 0x5e, (byte) 0x9c, + (byte) 0x77, (byte) 0x10, (byte) 0xc8, (byte) 0x64 + }; + + public final static byte[] G = { + (byte) 0x04, + + (byte) 0x2a, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, (byte) 0xaa, + (byte) 0xaa, (byte) 0xad, (byte) 0x24, (byte) 0x5a, + + (byte) 0x20, (byte) 0xae, (byte) 0x19, (byte) 0xa1, + (byte) 0xb8, (byte) 0xa0, (byte) 0x86, (byte) 0xb4, + (byte) 0xe0, (byte) 0x1e, (byte) 0xdd, (byte) 0x2c, + (byte) 0x77, (byte) 0x48, (byte) 0xd1, (byte) 0x4c, + (byte) 0x92, (byte) 0x3d, (byte) 0x4d, (byte) 0x7e, + (byte) 0x6d, (byte) 0x7c, (byte) 0x61, (byte) 0xb2, + (byte) 0x29, (byte) 0xe9, (byte) 0xc5, (byte) 0xa2, + (byte) 0x7e, (byte) 0xce, (byte) 0xd3, (byte) 0xd9 + }; + + public final static byte[] r = { + (byte) 0x10, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x14, (byte) 0xde, (byte) 0xf9, (byte) 0xde, + (byte) 0xa2, (byte) 0xf7, (byte) 0x9c, (byte) 0xd6, + (byte) 0x58, (byte) 0x12, (byte) 0x63, (byte) 0x1a, + (byte) 0x5c, (byte) 0xf5, (byte) 0xd3, (byte) 0xed + }; +} \ No newline at end of file From c70f4ed1d86a4ebbdf7187e173f3a6d30bdab111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Fri, 28 Jul 2023 09:15:12 +0200 Subject: [PATCH 09/20] feat: support X-only multiplication without ECDSA parity checking --- .../java/opencrypto/jcmathlib/ECPoint.java | 103 +++++++++++++----- .../jcmathlib/OperationSupport.java | 4 +- 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java index bb66ecd4..7d69f22f 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java @@ -489,11 +489,14 @@ public short multXYKA(BigNat scalar, byte[] outBuffer, short outBufferOffset) { */ private void multX(BigNat scalar) { byte[] pointBuffer = rm.POINT_ARRAY_A; + byte[] pointBuffer2 = rm.POINT_ARRAY_B; byte[] resultBuffer = rm.ARRAY_A; BigNat x = rm.EC_BN_B; BigNat ySq = rm.EC_BN_C; - BigNat y1 = rm.EC_BN_D; - BigNat y2 = rm.EC_BN_B; + BigNat y = rm.EC_BN_D; + BigNat lambda = rm.EC_BN_E; + BigNat tmp = rm.EC_BN_F; + BigNat denominator = rm.EC_BN_D; rm.lock(pointBuffer); short len = multXKA(scalar, pointBuffer, (short) 0); @@ -501,48 +504,90 @@ private void multX(BigNat scalar) { x.fromByteArray(pointBuffer, (short) 0, len); rm.unlock(pointBuffer); - //Y^2 = X^3 + XA + B = x(x^2+A)+B + // Solve for Y in Weierstrass equation: Y^2 = X^3 + XA + B = x(x^2+A)+B ySq.lock(); ySq.clone(x); ySq.modExp(ResourceManager.TWO, curve.pBN); ySq.modAdd(curve.aBN, curve.pBN); ySq.modMult(x, curve.pBN); ySq.modAdd(curve.bBN, curve.pBN); - y1.lock(); - y1.clone(ySq); + y.lock(); + y.clone(ySq); ySq.unlock(); - y1.modSqrt(curve.pBN); + y.modSqrt(curve.pBN); - // Prepare for SignVerify + // Construct public key with rm.lock(pointBuffer); - getW(pointBuffer, (short) 0); - curve.disposablePriv.setG(pointBuffer, (short) 0, curve.POINT_SIZE); - curve.disposablePub.setG(pointBuffer, (short) 0, curve.POINT_SIZE); - - // Construct public key with pointBuffer[0] = 0x04; x.prependZeros(curve.COORD_SIZE, pointBuffer, (short) 1); x.unlock(); - y1.prependZeros(curve.COORD_SIZE, pointBuffer, (short) (1 + curve.COORD_SIZE)); - - // Check if public point corresponds to the "secret" (i.e., our scalar) - rm.lock(resultBuffer); - scalar.prependZeros((short) curve.r.length, resultBuffer, (short) 0); - curve.disposablePriv.setS(resultBuffer, (short) 0, (short) curve.r.length); - curve.disposablePub.setW(pointBuffer, (short) 0, curve.POINT_SIZE); - if (!SignVerifyECDSA(curve.disposablePriv, curve.disposablePub, rm.verifyEcdsa, resultBuffer)) { // If verification fails, then pick the - y2.lock(); - y2.clone(curve.pBN); // y_2 = p - y_1 - y2.modSub(y1, curve.pBN); - y2.copyToByteArray(pointBuffer, (short) (1 + curve.COORD_SIZE)); - y2.unlock(); - } - rm.unlock(resultBuffer); - y1.unlock(); + y.prependZeros(curve.COORD_SIZE, pointBuffer, (short) (1 + curve.COORD_SIZE)); + y.unlock(); + boolean negate; + if (OperationSupport.getInstance().EC_HW_X_ECDSA) { + rm.lock(pointBuffer2); + getW(pointBuffer2, (short) 0); + curve.disposablePriv.setG(pointBuffer2, (short) 0, curve.POINT_SIZE); + curve.disposablePub.setG(pointBuffer2, (short) 0, curve.POINT_SIZE); + rm.unlock(pointBuffer2); - setW(pointBuffer, (short) 0, curve.POINT_SIZE); + setW(pointBuffer, (short) 0, curve.POINT_SIZE); + + // Check if corresponds to the "secret" (i.e., our scalar) + rm.lock(resultBuffer); + scalar.prependZeros((short) curve.r.length, resultBuffer, (short) 0); + curve.disposablePriv.setS(resultBuffer, (short) 0, (short) curve.r.length); + curve.disposablePub.setW(pointBuffer, (short) 0, curve.POINT_SIZE); + negate = !SignVerifyECDSA(curve.disposablePriv, curve.disposablePub, rm.verifyEcdsa, resultBuffer); + rm.unlock(resultBuffer); + } else { + // Check that ( + P)_x == ((scalar + 1)P)_x + x.lock(); + rm.lock(resultBuffer); + scalar.increment(); + len = multXKA(scalar, resultBuffer, (short) 0); + x.fromByteArray(resultBuffer, (short) 0, len); + rm.unlock(resultBuffer); + scalar.decrement(); // keep the original + + rm.lock(pointBuffer2); + getW(pointBuffer2, (short) 0); + setW(pointBuffer, (short) 0, curve.POINT_SIZE); + + // y_1 - y_2 + lambda.lock(); + lambda.fromByteArray(pointBuffer2, (short) (1 + curve.COORD_SIZE), curve.COORD_SIZE); + tmp.lock(); + tmp.fromByteArray(pointBuffer, (short) (1 + curve.COORD_SIZE), curve.COORD_SIZE); + lambda.modSub(tmp, curve.pBN); + + // (x_1 - x_2)^-1 + denominator.lock(); + denominator.fromByteArray(pointBuffer2, (short) 1, curve.COORD_SIZE); + tmp.fromByteArray(pointBuffer, (short) 1, curve.COORD_SIZE); + denominator.modSub(tmp, curve.pBN); + denominator.modInv(curve.pBN); + + // λ = (y_1 - y_2)/(x_1 - x_2) + lambda.modMult(denominator, curve.pBN); + + // x_3 = λ^2 - x_1 - x_2 + lambda.modSq(curve.pBN); + tmp.fromByteArray(pointBuffer2, (short) 1, curve.COORD_SIZE); + lambda.modSub(tmp, curve.pBN); + tmp.fromByteArray(pointBuffer, (short) 1, curve.COORD_SIZE); + lambda.modSub(tmp, curve.pBN); + tmp.unlock(); + + // If + P != (scalar + 1)P, negate the point + negate = !lambda.equals(x); + lambda.unlock(); + x.unlock(); + } rm.unlock(pointBuffer); + if (negate) + negate(); } /** diff --git a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java index 9c7bdb8c..122bfffa 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java +++ b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java @@ -36,6 +36,7 @@ public class OperationSupport { public boolean EC_PRECISE_BITLENGTH = true; public boolean EC_SET_COFACTOR = false; public boolean EC_GEN = true; + public boolean EC_HW_X_ECDSA = true; private OperationSupport() { } @@ -61,7 +62,8 @@ public void setCard(short card_identifier) { RSA_EXTRA_MOD = true; RSA_APPEND_MOD = true; EC_SW_DOUBLE = true; - EC_GEN = false; + // EC_GEN = false; // required by Wei25519 + // EC_HW_X_ECDSA = false; // required by Wei25519 break; case GD60: RSA_PUB = true; From 15c30570366e21593b3722e1f0b5e5c922b3a6ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Fri, 28 Jul 2023 11:19:54 +0200 Subject: [PATCH 10/20] fix: keep compatibility with GD60 cards (revert c9892ff) and change `ECCurve` bitlength computation --- applet/src/main/java/opencrypto/jcmathlib/ECCurve.java | 4 ++-- applet/src/main/java/opencrypto/jcmathlib/Example.java | 2 +- applet/src/main/java/opencrypto/jcmathlib/UnitTests.java | 2 +- .../main/java/opencrypto/jcmathlib/curves/SecP256k1.java | 6 +----- .../main/java/opencrypto/jcmathlib/curves/SecP256r1.java | 6 +----- .../main/java/opencrypto/jcmathlib/curves/SecP512r1.java | 6 +----- .../src/main/java/opencrypto/jcmathlib/curves/Wei25519.java | 6 +----- 7 files changed, 8 insertions(+), 24 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java index 9012c197..d5887776 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java @@ -38,8 +38,8 @@ public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, short k, Resour for (short i = 0; i < (short) p.length; ++i) { bits -= 8; if (p[i] != (byte) 0x00) { - byte tmp = p[i]; - while (tmp != (byte) 0x00) { + short tmp = (short) (p[i] & 0xff); + while (tmp != (short) 0x00) { tmp >>= (short) 1; ++bits; } diff --git a/applet/src/main/java/opencrypto/jcmathlib/Example.java b/applet/src/main/java/opencrypto/jcmathlib/Example.java index 029e9aa8..565f617a 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/Example.java +++ b/applet/src/main/java/opencrypto/jcmathlib/Example.java @@ -59,7 +59,7 @@ public void initialize() { // Allocate resources for a required elliptic curve size (in bits) rm = new ResourceManager((short) 256); // Allocate SecP256r1 curve and two EC points on this curve - curve = new SecP256k1(rm); + curve = new ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r, SecP256r1.k, rm); point1 = new ECPoint(curve); point2 = new ECPoint(curve); // Allocate two BigNats large enough to hold scalars of the elliptic curve diff --git a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index cbfda5c2..c4f5ee9e 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java +++ b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java @@ -117,7 +117,7 @@ public void initialize() { // Pre-allocate test objects (no new allocation for every tested operation) - curve = new SecP256r1(rm); + curve = new ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r, SecP256r1.k, rm); memoryInfoOffset = snapshotAvailableMemory((short) 3, memoryInfo, memoryInfoOffset); memoryInfoOffset = snapshotAvailableMemory((short) 5, memoryInfo, memoryInfoOffset); diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java index f5fd84da..4db871f4 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java @@ -1,10 +1,6 @@ package opencrypto.jcmathlib; -public class SecP256k1 extends ECCurve { - public SecP256k1(ResourceManager rm) { - super(p, a, b, G, r, k, rm); - } - +public class SecP256k1 { public final static short k = 1; public final static byte[] p = { diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java index f08b1209..8662e12e 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java @@ -1,10 +1,6 @@ package opencrypto.jcmathlib; -public class SecP256r1 extends ECCurve { - public SecP256r1(ResourceManager rm) { - super(p, a, b, G, r, k, rm); - } - +public class SecP256r1 { public final static short k = 1; public final static byte[] p = { diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java index d3554610..f52934d0 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java @@ -1,10 +1,6 @@ package opencrypto.jcmathlib; -public class SecP512r1 extends ECCurve { - public SecP512r1(ResourceManager rm) { - super(p, a, b, G, r, k, rm); - } - +public class SecP512r1 { public final static short k = 1; public final static byte[] p = { diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java b/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java index a67f5dfa..1a2853d2 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java @@ -1,10 +1,6 @@ package opencrypto.jcmathlib; -public class Wei25519 extends ECCurve { - public Wei25519(ResourceManager rm) { - super(p, a, b, G, r, k, rm); - } - +public class Wei25519 { public final static short k = 8; public final static byte[] p = { From fdce9f2d7a48aede18d605a49c5bd7fc9044f292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Fri, 28 Jul 2023 11:45:14 +0200 Subject: [PATCH 11/20] feat: add `Wei25519` support for `SECORA` --- applet/src/main/java/opencrypto/jcmathlib/ECCurve.java | 8 ++++++-- .../main/java/opencrypto/jcmathlib/OperationSupport.java | 1 + applet/src/test/java/tests/BaseTest.java | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java index d5887776..3623447b 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java @@ -114,8 +114,12 @@ KeyPair newKeyPair(KeyPair keyPair) { pubKey.setR(r, (short) 0, (short) r.length); pubKey.setK(OperationSupport.getInstance().EC_SET_COFACTOR ? k : (short) 1); - privKey.setS(ResourceManager.CONST_ONE, (short) 0, (short) 1); - pubKey.setW(G, (short) 0, (short) G.length); + if (OperationSupport.getInstance().EC_GEN) { + keyPair.genKeyPair(); + } else { + privKey.setS(ResourceManager.CONST_ONE, (short) 0, (short) 1); + pubKey.setW(G, (short) 0, (short) G.length); + } return keyPair; } diff --git a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java index 122bfffa..8f117755 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java +++ b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java @@ -94,6 +94,7 @@ public void setCard(short card_identifier) { RSA_EXTRA_MOD = true; RSA_APPEND_MOD = true; EC_HW_XY = true; + EC_PRECISE_BITLENGTH = false; break; default: break; diff --git a/applet/src/test/java/tests/BaseTest.java b/applet/src/test/java/tests/BaseTest.java index 5a0ef525..c6e1e480 100644 --- a/applet/src/test/java/tests/BaseTest.java +++ b/applet/src/test/java/tests/BaseTest.java @@ -68,7 +68,7 @@ public CardManager connectRaw(byte[] installData) throws Exception { // System.setProperty("com.licel.jcardsim.randomdata.secure", "1"); runCfg.setTestCardType(cardType); - runCfg.setTargetReaderIndex(0); + runCfg.setTargetReaderIndex(2); if (cardType == CardType.REMOTE){ runCfg.setRemoteAddress("http://127.0.0.1:9901"); From c785f736824de81bd14a4f9a5619dab2e9eae5f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Fri, 28 Jul 2023 14:58:00 +0200 Subject: [PATCH 12/20] fix: full modSqrt functionality on `SECORA` --- .../java/opencrypto/jcmathlib/BigNat.java | 66 +++++++++---------- .../opencrypto/jcmathlib/BigNatInternal.java | 36 ++++------ .../jcmathlib/OperationSupport.java | 2 + 3 files changed, 45 insertions(+), 59 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java index 019477c3..8bbeca5b 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java @@ -45,7 +45,7 @@ public void gcd(BigNat other) { tmpOther.clone(other); // TODO: optimise? - while (!other.isZero()) { + while (!other.equals((byte) 0)) { tmp.clone(tmpOther); mod(tmpOther); tmpOther.clone(this); @@ -66,7 +66,7 @@ public boolean isCoprime(BigNat a, BigNat b) { tmp.clone(a); tmp.gcd(b); - boolean result = tmp.isOne(); + boolean result = tmp.equals((byte) 1); tmp.unlock(); return result; } @@ -112,7 +112,7 @@ public void sq() { * Computes this * other and stores the result into this. */ public void mult(BigNat other) { - if (OperationSupport.getInstance().RSA_CHECK_ONE && isOne()) { + if (OperationSupport.getInstance().RSA_CHECK_ONE && equals((byte) 1)) { clone(other); return; } @@ -236,6 +236,12 @@ private void modSqFixed() { public void modExp(BigNat exp, BigNat mod) { if (!OperationSupport.getInstance().RSA_EXP) ISOException.throwIt(ReturnCodes.SW_OPERATION_NOT_SUPPORTED); + if (OperationSupport.getInstance().RSA_CHECK_EXP_ONE && exp.equals((byte) 1)) + return; + if (!OperationSupport.getInstance().RSA_SQ && exp.equals((byte) 2)) { + modMult(this, mod); + return; + } BigNat tmpMod = rm.BN_F; // modExp is called from modSqrt => requires BN_F not being locked when modExp is called byte[] tmpBuffer = rm.ARRAY_A; @@ -343,7 +349,7 @@ public void modMult(BigNat other, BigNat mod) { BigNat tmp = rm.BN_D; BigNat result = rm.BN_E; - if (OperationSupport.getInstance().RSA_CHECK_ONE && isOne()) { + if (OperationSupport.getInstance().RSA_CHECK_ONE && equals((byte) 1)) { copy(other); return; } @@ -404,13 +410,11 @@ public void modSq(BigNat mod) { * the two roots) is stored to this. */ public void modSqrt(BigNat p) { - BigNat s = rm.BN_A; BigNat exp = rm.BN_G; BigNat p1 = rm.BN_B; BigNat q = rm.BN_C; BigNat tmp = rm.BN_D; - BigNat z = rm.EC_BN_E; // TODO temporary workaround to avoid allocating another BigNat - BigNat i = rm.BN_G; + BigNat z = rm.BN_A; BigNat t = rm.BN_B; BigNat b = rm.BN_C; @@ -419,15 +423,12 @@ public void modSqrt(BigNat p) { p1.clone(p); p1.decrement(); - s.lock(); - s.setSize((short) 1); // assumes S < 256 - s.zero(); - q.lock(); q.clone(p1); + short s = 0; while (!q.isOdd()) { - s.increment(); + ++s; q.shiftRight((short) 1); } @@ -453,17 +454,16 @@ public void modSqrt(BigNat p) { tmp.unlock(); // 3. Compute the first candidate - exp.copy(q); + exp.clone(q); exp.increment(); exp.shiftRight((short) 1); t.lock(); - t.copy(this); + t.clone(this); t.modExp(q, p); - if (t.isZero()) { + if (t.equals((byte) 0)) { z.unlock(); - s.unlock(); t.unlock(); exp.unlock(); q.unlock(); @@ -475,9 +475,8 @@ public void modSqrt(BigNat p) { modExp(exp, p); exp.unlock(); - if (t.isOne()) { + if (t.equals((byte) 1)) { z.unlock(); - s.unlock(); t.unlock(); q.unlock(); return; @@ -489,49 +488,46 @@ public void modSqrt(BigNat p) { while(true) { tmp.lock(); - tmp.copy(t); - i.lock(); - i.setSize(p.length()); - i.zero(); + tmp.clone(t); + short i = 0; do { tmp.modSq(p); - i.increment(); - } while (!tmp.isOne()); + ++i; + } while (!tmp.equals((byte) 1)); tmp.unlock(); b.lock(); - b.copy(z); - s.subtract(i); - s.decrement(); + b.clone(z); + s -= i; + --s; tmp.lock(); + tmp.setSize((short) 1); tmp.setValue((byte) 1); - while(!s.isZero()) { + while(s != 0) { tmp.shiftLeft((short) 1); - s.decrement(); + --s; } b.modExp(tmp, p); tmp.unlock(); - s.copy(i); - i.unlock(); - z.copy(b); + s = i; + z.clone(b); z.modSq(p); t.modMult(z, p); modMult(b, p); b.unlock(); - if(t.isZero()) { + if(t.equals((byte) 0)) { zero(); break; } - if(t.isOne()) { + if(t.equals((byte) 1)) { break; } } z.unlock(); - s.unlock(); t.unlock(); } } diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java index f1d5f065..59fd8a90 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java @@ -226,30 +226,6 @@ public void clone(BigNatInternal other) { setSize(other.size); } - /** - * Test equality with zero. - */ - public boolean isZero() { - for (short i = offset; i < value.length; i++) { - if (value[i] != 0) { - return false; // CTO - } - } - return true; - } - - /** - * Test equality with one. - */ - public boolean isOne() { - for (short i = offset; i < (short) (value.length - 1); i++) { - if (value[i] != 0) { - return false; // CTO - } - } - return value[(short) (value.length - 1)] == (byte) 0x01; - } - /** * Check if stored BigNat is odd. */ @@ -322,6 +298,18 @@ public boolean equals(BigNatInternal other) { return Util.arrayCompare(value, end, other.value, other.offset, other.size) == 0; } + /** + * Test equality with a byte. + */ + public boolean equals(byte b) { + for (short i = offset; i < (short) (value.length - 1); i++) { + if (value[i] != 0) { + return false; // CTO + } + } + return value[(short) (value.length - 1)] == b; + } + /** * Increment this BigNat. */ diff --git a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java index 8f117755..55826d7c 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java +++ b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java @@ -23,6 +23,7 @@ public class OperationSupport { public boolean RSA_SQ = true; public boolean RSA_PUB = false; public boolean RSA_CHECK_ONE = false; + public boolean RSA_CHECK_EXP_ONE = false; public boolean RSA_KEY_REFRESH = false; public boolean RSA_PREPEND_ZEROS = false; public boolean RSA_EXTRA_MOD = false; @@ -90,6 +91,7 @@ public void setCard(short card_identifier) { case SECORA: MIN_RSA_BIT_LENGTH = 1024; RSA_SQ = false; + RSA_CHECK_EXP_ONE = true; RSA_PUB = true; RSA_EXTRA_MOD = true; RSA_APPEND_MOD = true; From fe723cf5017ab6cf49aab2b7195ea1d941ff4f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Mon, 31 Jul 2023 09:31:10 +0200 Subject: [PATCH 13/20] fix: unlock variable in X-only multiplication without ECDSA --- applet/src/main/java/opencrypto/jcmathlib/ECPoint.java | 1 + 1 file changed, 1 insertion(+) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java index 7d69f22f..ce4e0c22 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java @@ -571,6 +571,7 @@ private void multX(BigNat scalar) { // λ = (y_1 - y_2)/(x_1 - x_2) lambda.modMult(denominator, curve.pBN); + denominator.unlock(); // x_3 = λ^2 - x_1 - x_2 lambda.modSq(curve.pBN); From a2dae16ebdbe059818cf04418b3e9209bae402de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Mon, 31 Jul 2023 09:36:14 +0200 Subject: [PATCH 14/20] feat: add `Wei25519` to package.py --- package.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.py b/package.py index af4389db..4d7e98f4 100644 --- a/package.py +++ b/package.py @@ -5,7 +5,7 @@ DIR = "." -CURVES = {"SecP256r1", "SecP256k1", "SecP512r1"} +CURVES = {"SecP256r1", "SecP256k1", "SecP512r1", "Wei25519"} FILTERED_FILES = {"UnitTests.java", "Example.java", "Integer.java"} From 4389aab920c2c25bb8619f7cf961c8f8406dc18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Sun, 29 Dec 2024 10:28:44 +0100 Subject: [PATCH 15/20] chore: remove extra comments --- applet/src/main/java/opencrypto/jcmathlib/UnitTests.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index c4f5ee9e..d9eef51d 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java +++ b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java @@ -18,12 +18,6 @@ */ public class UnitTests extends Applet { public final static short CARD_TYPE = OperationSupport.SIMULATOR; // TODO set your card - //public final static short CARD_TYPE = OperationSupport.JCOP21; - //public final static short CARD_TYPE = OperationSupport.SECORA; - //public final static short CARD_TYPE = OperationSupport.JCOP3_P60; - //public final static short CARD_TYPE = OperationSupport.JCOP4_P71; - //public final static short CARD_TYPE = OperationSupport.GD60; - //public final static short CARD_TYPE = OperationSupport.GD70; public final static byte CLA_OC_UT = (byte) 0xB0; public final static byte INS_CLEANUP = (byte) 0x03; From 82b54fc0ca9b07c9004ed5745e51d309513b6257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Sun, 29 Dec 2024 10:29:32 +0100 Subject: [PATCH 16/20] fix: integer division --- applet/src/main/java/opencrypto/jcmathlib/BigNat.java | 1 - .../src/main/java/opencrypto/jcmathlib/BigNatInternal.java | 6 +++++- applet/src/main/java/opencrypto/jcmathlib/UnitTests.java | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java index 8bbeca5b..4b864bb6 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java @@ -28,7 +28,6 @@ public void divide(BigNat other) { tmp.lock(); tmp.clone(this); tmp.remainderDivide(other, this); - copy(tmp); tmp.unlock(); } diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java index 59fd8a90..d1b9e08d 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java @@ -511,7 +511,7 @@ public void shiftLeft(short bits) { */ public void remainderDivide(BigNatInternal divisor, BigNatInternal quotient) { if (quotient != null) { - quotient.zero(); + quotient.setSizeToMax(true); } short divisorIndex = divisor.offset; @@ -564,6 +564,10 @@ public void remainderDivide(BigNatInternal divisor, BigNatInternal quotient) { divisionRound++; divisorShift--; } + + if (quotient != null) { + quotient.shrink(); + } } /** diff --git a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index d9eef51d..729dbc00 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java +++ b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java @@ -710,7 +710,9 @@ void testIntDiv(APDU apdu, short ignoredDataLen) { int1.fromByteArray(apduBuffer, ISO7816.OFFSET_CDATA, p1); int2.fromByteArray(apduBuffer, (short) (ISO7816.OFFSET_CDATA + p1), p1); + short size = int1.getSize(); int1.divide(int2); + int1.setSize(size); short len = int1.toByteArray(apduBuffer, (short) 0); apdu.setOutgoingAndSend((short) 0, len); From 39561a3c07889cc778684f778638d0f94d79b2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Sun, 29 Dec 2024 10:42:38 +0100 Subject: [PATCH 17/20] feat: enable using modular arithmetic with larger bit sizes --- .../opencrypto/jcmathlib/ObjectAllocator.java | 20 +++++++++---------- .../opencrypto/jcmathlib/ResourceManager.java | 13 +++++++++--- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/ObjectAllocator.java b/applet/src/main/java/opencrypto/jcmathlib/ObjectAllocator.java index fbda91fa..4c59c7b5 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ObjectAllocator.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ObjectAllocator.java @@ -69,20 +69,20 @@ public void setAllocatorsTradeoff() { setAllAllocatorsEEPROM(); // Put only the most perfromance relevant ones into RAM - ALLOCATOR_TYPE_ARRAY[ARRAY_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[ARRAY_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[BN_WORD] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[BN_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[ARRAY_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[ARRAY_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[BN_WORD] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[BN_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; ALLOCATOR_TYPE_ARRAY[BN_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[BN_C] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[BN_C] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; ALLOCATOR_TYPE_ARRAY[BN_D] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; ALLOCATOR_TYPE_ARRAY[BN_E] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; ALLOCATOR_TYPE_ARRAY[BN_F] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[BN_G] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[EC_BN_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[EC_BN_C] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[POINT_ARRAY_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; - ALLOCATOR_TYPE_ARRAY[POINT_ARRAY_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[BN_G] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[EC_BN_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[EC_BN_C] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[POINT_ARRAY_A] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; + // ALLOCATOR_TYPE_ARRAY[POINT_ARRAY_B] = JCSystem.MEMORY_TYPE_TRANSIENT_RESET; } /** diff --git a/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java b/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java index e6f0908b..8718e0ed 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java @@ -41,7 +41,14 @@ public class ResourceManager { public final short MAX_COORD_SIZE; public ResourceManager(short maxEcLength) { + this(maxEcLength, (short) 0); + } + + public ResourceManager(short maxEcLength, short maxRsaLength) { short min = OperationSupport.getInstance().MIN_RSA_BIT_LENGTH; + if (min < maxRsaLength) { + min = maxRsaLength; + } if (maxEcLength <= (short) 256) { MAX_EXP_BIT_LENGTH = (short) 512 < min ? min : (short) 512; MAX_SQ_BIT_LENGTH = (short) 768 < min ? min : (short) 768; @@ -70,9 +77,9 @@ else if (maxEcLength <= (short) 512) { memAlloc = new ObjectAllocator(); memAlloc.setAllAllocatorsRAM(); + // memAlloc.setAllAllocatorsEEPROM(); + // memAlloc.setAllocatorsTradeoff(); // if required, memory for helper objects and arrays can be in persistent memory to save RAM (or some tradeoff) - // ObjectAllocator.setAllAllocatorsEEPROM(); - // ObjectAllocator.setAllocatorsTradeoff(); ARRAY_A = memAlloc.allocateByteArray(MAX_SQ_LENGTH, memAlloc.getAllocatorType(ObjectAllocator.ARRAY_A)); @@ -93,7 +100,7 @@ else if (maxEcLength <= (short) 512) { BN_B = new BigNat(MAX_BIGNAT_SIZE, memAlloc.getAllocatorType(ObjectAllocator.BN_B), this); BN_C = new BigNat(MAX_BIGNAT_SIZE, memAlloc.getAllocatorType(ObjectAllocator.BN_C), this); BN_D = new BigNat(MAX_BIGNAT_SIZE, memAlloc.getAllocatorType(ObjectAllocator.BN_D), this); - BN_E = new BigNat(MAX_BIGNAT_SIZE, memAlloc.getAllocatorType(ObjectAllocator.BN_E), this); + BN_E = new BigNat((short) (2 * MAX_BIGNAT_SIZE), memAlloc.getAllocatorType(ObjectAllocator.BN_E), this); BN_F = new BigNat(MAX_SQ_LENGTH, memAlloc.getAllocatorType(ObjectAllocator.BN_F), this); BN_G = new BigNat(MAX_SQ_LENGTH, memAlloc.getAllocatorType(ObjectAllocator.BN_G), this); From e71e49327a5cd924c1bd74a87c7af6b6cc5a28c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Sun, 29 Dec 2024 10:58:46 +0100 Subject: [PATCH 18/20] feat: add quadratic residuosity check --- .../java/opencrypto/jcmathlib/BigNat.java | 37 +++++++++----- .../opencrypto/jcmathlib/BigNatInternal.java | 48 ++++++++++++++----- .../java/opencrypto/jcmathlib/ECPoint.java | 11 +++-- 3 files changed, 70 insertions(+), 26 deletions(-) diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java index 4b864bb6..35a2fa38 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java @@ -44,7 +44,7 @@ public void gcd(BigNat other) { tmpOther.clone(other); // TODO: optimise? - while (!other.equals((byte) 0)) { + while (!other.isZero()) { tmp.clone(tmpOther); mod(tmpOther); tmpOther.clone(this); @@ -65,7 +65,7 @@ public boolean isCoprime(BigNat a, BigNat b) { tmp.clone(a); tmp.gcd(b); - boolean result = tmp.equals((byte) 1); + boolean result = tmp.isOne(); tmp.unlock(); return result; } @@ -111,7 +111,7 @@ public void sq() { * Computes this * other and stores the result into this. */ public void mult(BigNat other) { - if (OperationSupport.getInstance().RSA_CHECK_ONE && equals((byte) 1)) { + if (OperationSupport.getInstance().RSA_CHECK_ONE && isOne()) { clone(other); return; } @@ -235,9 +235,9 @@ private void modSqFixed() { public void modExp(BigNat exp, BigNat mod) { if (!OperationSupport.getInstance().RSA_EXP) ISOException.throwIt(ReturnCodes.SW_OPERATION_NOT_SUPPORTED); - if (OperationSupport.getInstance().RSA_CHECK_EXP_ONE && exp.equals((byte) 1)) + if (OperationSupport.getInstance().RSA_CHECK_EXP_ONE && exp.isOne()) return; - if (!OperationSupport.getInstance().RSA_SQ && exp.equals((byte) 2)) { + if (!OperationSupport.getInstance().RSA_SQ && exp.isTwo()) { modMult(this, mod); return; } @@ -348,7 +348,7 @@ public void modMult(BigNat other, BigNat mod) { BigNat tmp = rm.BN_D; BigNat result = rm.BN_E; - if (OperationSupport.getInstance().RSA_CHECK_ONE && equals((byte) 1)) { + if (OperationSupport.getInstance().RSA_CHECK_ONE && isOne()) { copy(other); return; } @@ -404,6 +404,21 @@ public void modSq(BigNat mod) { } } + /** + * Checks whether this BigNat is a quadratic residue modulo p. + * @param p modulo + */ + public boolean isQuadraticResidue(BigNat p) { + BigNat tmp = rm.BN_A; + BigNat exp = rm.BN_B; + tmp.clone(this); + exp.clone(p); + exp.decrement(); + exp.shiftRight((short) 1); + tmp.modExp(exp, p); + return tmp.isOne(); + } + /** * Computes square root of provided BigNat which MUST be prime using Tonelli Shanks Algorithm. The result (one of * the two roots) is stored to this. @@ -461,7 +476,7 @@ public void modSqrt(BigNat p) { t.clone(this); t.modExp(q, p); - if (t.equals((byte) 0)) { + if (t.isZero()) { z.unlock(); t.unlock(); exp.unlock(); @@ -474,7 +489,7 @@ public void modSqrt(BigNat p) { modExp(exp, p); exp.unlock(); - if (t.equals((byte) 1)) { + if (t.isOne()) { z.unlock(); t.unlock(); q.unlock(); @@ -493,7 +508,7 @@ public void modSqrt(BigNat p) { do { tmp.modSq(p); ++i; - } while (!tmp.equals((byte) 1)); + } while (!tmp.isOne()); tmp.unlock(); @@ -518,11 +533,11 @@ public void modSqrt(BigNat p) { modMult(b, p); b.unlock(); - if(t.equals((byte) 0)) { + if(t.isZero()) { zero(); break; } - if(t.equals((byte) 1)) { + if(t.isOne()) { break; } } diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java index d1b9e08d..3b77f080 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java @@ -226,6 +226,42 @@ public void clone(BigNatInternal other) { setSize(other.size); } + /** + * Test equality with zero. + */ + public boolean isZero() { + for (short i = offset; i < value.length; i++) { + if (value[i] != 0) { + return false; // CTO + } + } + return true; + } + + /** + * Test equality with one. + */ + public boolean isOne() { + for (short i = offset; i < (short) (value.length - 1); i++) { + if (value[i] != 0) { + return false; // CTO + } + } + return value[(short) (value.length - 1)] == (byte) 0x01; + } + + /** + * Test equality with two. + */ + public boolean isTwo() { + for (short i = offset; i < (short) (value.length - 1); i++) { + if (value[i] != 0) { + return false; // CTO + } + } + return value[(short) (value.length - 1)] == (byte) 0x02; + } + /** * Check if stored BigNat is odd. */ @@ -298,18 +334,6 @@ public boolean equals(BigNatInternal other) { return Util.arrayCompare(value, end, other.value, other.offset, other.size) == 0; } - /** - * Test equality with a byte. - */ - public boolean equals(byte b) { - for (short i = offset; i < (short) (value.length - 1); i++) { - if (value[i] != 0) { - return false; // CTO - } - } - return value[(short) (value.length - 1)] == b; - } - /** * Increment this BigNat. */ diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java index ce4e0c22..3563062a 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java +++ b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java @@ -644,14 +644,15 @@ public void negate() { * @param xOffset offset in the byte array * @param xLen length of the X coordinate */ - public void fromX(byte[] xCoord, short xOffset, short xLen) { + public boolean fromX(byte[] xCoord, short xOffset, short xLen) { BigNat x = rm.EC_BN_F; x.lock(); x.setSize(xLen); x.fromByteArray(xCoord, xOffset, xLen); - fromX(x); + boolean result = fromX(x); x.unlock(); + return result; } /** @@ -659,7 +660,7 @@ public void fromX(byte[] xCoord, short xOffset, short xLen) { * * @param x the x coordinate */ - private void fromX(BigNat x) { + private boolean fromX(BigNat x) { BigNat ySq = rm.EC_BN_C; BigNat y = rm.EC_BN_D; byte[] pointBuffer = rm.POINT_ARRAY_A; @@ -673,6 +674,9 @@ private void fromX(BigNat x) { ySq.modAdd(curve.bBN, curve.pBN); y.lock(); y.clone(ySq); + if (!y.isQuadraticResidue(curve.pBN)) { + return false; + } ySq.unlock(); y.modSqrt(curve.pBN); @@ -684,6 +688,7 @@ private void fromX(BigNat x) { y.unlock(); setW(pointBuffer, (short) 0, curve.POINT_SIZE); rm.unlock(pointBuffer); + return true; } /** From 871dae7509ab634d176061f809645cfa20163c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Sun, 29 Dec 2024 13:10:21 +0100 Subject: [PATCH 19/20] chore: update package.py usage example --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d3fedbe1..1844e22a 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ options: -h, --help show this help message and exit -d DIR, --dir DIR Directory to package -k, --keep-locks Keep locks - -c {SecP256k1,SecP256r1,SecP512r1} [{SecP256k1,SecP256r1,SecP512r1} ...], --curves {SecP256k1,SecP256r1,SecP512r1} [{SecP256k1,SecP256r1,SecP512r1} ...] + -c {SecP256k1,SecP256r1,SecP512r1,Wei25519} [{SecP256k1,SecP256r1,SecP512r1,Wei25519} ...], --curves {SecP256k1,SecP256r1,SecP512r1,Wei25519} [{SecP256k1,SecP256r1,SecP512r1,Wei25519} ...] Curves to include -p PACKAGE, --package PACKAGE Package name @@ -160,11 +160,11 @@ options: Output file ``` -For example, to bundle JCMathLib for your applet `test` in which you use curve `SecP256k1`, use the following. The +For example, to bundle JCMathLib for your applet `test` in which you use curve `SecP256r1`, use the following. The output will be stored in `jcmathlib.java` file. ``` -$ python package.py -p test -c SecP256k1 -o jcmathlib.java +$ python package.py -p test -c SecP256r1 -o jcmathlib.java ``` ## Community From cd8b577af908cfe990041c227733b032a30b7821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=C3=ADn=20Dufka?= Date: Sun, 29 Dec 2024 13:20:09 +0100 Subject: [PATCH 20/20] chore: bump version to 2.1 --- CITATION.cff | 2 +- applet/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index 4cadb53c..21bf5c9e 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -8,7 +8,7 @@ authors: - family-names: "Dufka" given-names: "Antonin" title: "JCMathLib" -version: 2.0 +version: 2.1 url: "https://github.com/OpenCryptoProject/JCMathLib" preferred-citation: type: conference-paper diff --git a/applet/build.gradle b/applet/build.gradle index 3ee48f70..182a703b 100644 --- a/applet/build.gradle +++ b/applet/build.gradle @@ -1,5 +1,5 @@ group 'test-applet' -version '2.0' +version '2.1' // Buildscript configuration for the javacard-gradle plugin. // Do not modify this particular block. Dependencies for the project are lower.