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/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 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. diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNat.java b/applet/src/main/java/opencrypto/jcmathlib/BigNat.java index a4849a8d..35a2fa38 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(); } @@ -236,6 +235,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.isOne()) + return; + if (!OperationSupport.getInstance().RSA_SQ && exp.isTwo()) { + 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; @@ -399,47 +404,47 @@ 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. */ 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.BN_A; + 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.zero(); - tmp.lock(); - tmp.setSize(p.length()); - tmp.zero(); - - while (!tmp.equals(q)) { - s.increment(); - // TODO replace with modMult(s, q, p) - tmp.setSizeToMax(false); - tmp.clone(s); - tmp.mult(q); - tmp.mod(p); - tmp.shrink(); + short s = 0; + while (!q.isOdd()) { + ++s; + q.shiftRight((short) 1); } - tmp.unlock(); - s.unlock(); // 2. Find the first quadratic non-residue z by brute-force search exp.lock(); @@ -451,23 +456,92 @@ 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(); - exp.copy(q); - q.unlock(); + + // 3. Compute the first candidate + exp.clone(q); exp.increment(); exp.shiftRight((short) 1); + t.lock(); + t.clone(this); + t.modExp(q, p); + + if (t.isZero()) { + z.unlock(); + t.unlock(); + exp.unlock(); + q.unlock(); + zero(); + return; + } + mod(p); modExp(exp, p); exp.unlock(); + + if (t.isOne()) { + z.unlock(); + t.unlock(); + q.unlock(); + return; + } + + // 4. Search for further candidates + z.modExp(q, p); + q.unlock(); + + while(true) { + tmp.lock(); + tmp.clone(t); + short i = 0; + + do { + tmp.modSq(p); + ++i; + } while (!tmp.isOne()); + + tmp.unlock(); + + b.lock(); + b.clone(z); + s -= i; + --s; + + tmp.lock(); + tmp.setSize((short) 1); + tmp.setValue((byte) 1); + while(s != 0) { + tmp.shiftLeft((short) 1); + --s; + } + b.modExp(tmp, p); + tmp.unlock(); + s = i; + z.clone(b); + z.modSq(p); + t.modMult(z, p); + modMult(b, p); + b.unlock(); + + if(t.isZero()) { + zero(); + break; + } + if(t.isOne()) { + break; + } + } + z.unlock(); + t.unlock(); } } diff --git a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java index c607cc9a..3b77f080 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java +++ b/applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java @@ -250,6 +250,18 @@ public boolean isOne() { 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. */ @@ -456,7 +468,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 +492,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. * @@ -490,7 +535,7 @@ public void shiftRight(short bits) { */ public void remainderDivide(BigNatInternal divisor, BigNatInternal quotient) { if (quotient != null) { - quotient.zero(); + quotient.setSizeToMax(true); } short divisorIndex = divisor.offset; @@ -543,6 +588,10 @@ public void remainderDivide(BigNatInternal divisor, BigNatInternal quotient) { divisionRound++; divisorShift--; } + + if (quotient != null) { + quotient.shrink(); + } } /** diff --git a/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java b/applet/src/main/java/opencrypto/jcmathlib/ECCurve.java index e3638da8..3623447b 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; @@ -15,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; @@ -32,8 +32,22 @@ 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) { - KEY_BIT_LENGTH = (short) (p.length * 8); + public ECCurve(byte[] p, byte[] a, byte[] b, byte[] G, byte[] r, short k, ResourceManager rm) { + 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) { + short tmp = (short) (p[i] & 0xff); + while (tmp != (short) 0x00) { + tmp >>= (short) 1; + ++bits; + } + break; + } + } + } + KEY_BIT_LENGTH = bits; POINT_SIZE = (short) G.length; COORD_SIZE = (short) ((short) (G.length - 1) / 2); @@ -42,6 +56,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); @@ -56,8 +71,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). */ @@ -90,16 +105,21 @@ 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(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((short) 1); + pubKey.setK(OperationSupport.getInstance().EC_SET_COFACTOR ? k : (short) 1); - keyPair.genKeyPair(); + 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/ECPoint.java b/applet/src/main/java/opencrypto/jcmathlib/ECPoint.java index b07c7843..3563062a 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(); + } } /** @@ -420,8 +434,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 +472,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; @@ -475,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); @@ -487,48 +504,91 @@ 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); - len = scalar.copyToByteArray(resultBuffer, (short) 0); - curve.disposablePriv.setS(resultBuffer, (short) 0, len); - 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); + denominator.unlock(); + + // 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(); } /** @@ -545,12 +605,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) @@ -584,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; } /** @@ -599,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; @@ -613,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); @@ -624,6 +688,7 @@ private void fromX(BigNat x) { y.unlock(); setW(pointBuffer, (short) 0, curve.POINT_SIZE); rm.unlock(pointBuffer); + return true; } /** diff --git a/applet/src/main/java/opencrypto/jcmathlib/Example.java b/applet/src/main/java/opencrypto/jcmathlib/Example.java index 0ca37a32..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 ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r, 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/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/OperationSupport.java b/applet/src/main/java/opencrypto/jcmathlib/OperationSupport.java index 2c25e2f4..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; @@ -33,6 +34,10 @@ 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; + public boolean EC_HW_X_ECDSA = true; private OperationSupport() { } @@ -51,12 +56,15 @@ 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; // required by Wei25519 + // EC_HW_X_ECDSA = false; // required by Wei25519 break; case GD60: RSA_PUB = true; @@ -83,10 +91,12 @@ 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; EC_HW_XY = true; + EC_PRECISE_BITLENGTH = false; break; default: break; diff --git a/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java b/applet/src/main/java/opencrypto/jcmathlib/ResourceManager.java index c281a316..8718e0ed 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; @@ -39,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; @@ -68,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)); @@ -91,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); @@ -148,6 +157,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/UnitTests.java b/applet/src/main/java/opencrypto/jcmathlib/UnitTests.java index 211cb58e..729dbc00 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; @@ -47,6 +41,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; @@ -91,10 +86,6 @@ public class UnitTests extends Applet { ECPoint point1; ECPoint point2; - byte[] customG; - ECCurve customCurve; - ECPoint customPoint; - BigNat bn1; BigNat bn2; BigNat bn3; @@ -120,17 +111,13 @@ 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 ECCurve(SecP256r1.p, SecP256r1.a, SecP256r1.b, SecP256r1.G, SecP256r1.r, SecP256r1.k, 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); @@ -248,6 +235,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; @@ -354,9 +344,6 @@ void updateAfterReset() { if (curve != null) { curve.updateAfterReset(); } - if (customCurve != null) { - customCurve.updateAfterReset(); - } if (rm != null) { rm.refreshAfterReset(); rm.unlockAll(); @@ -366,10 +353,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); } @@ -532,6 +519,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); @@ -713,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); diff --git a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java index d9213030..4db871f4 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256k1.java @@ -1,6 +1,8 @@ package opencrypto.jcmathlib; public class SecP256k1 { + 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 dab86fd2..8662e12e 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP256r1.java @@ -1,6 +1,8 @@ package opencrypto.jcmathlib; public class SecP256r1 { + 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 09e07ca8..f52934d0 100644 --- a/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/SecP512r1.java @@ -1,6 +1,8 @@ package opencrypto.jcmathlib; public class SecP512r1 { + 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, 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..1a2853d2 --- /dev/null +++ b/applet/src/main/java/opencrypto/jcmathlib/curves/Wei25519.java @@ -0,0 +1,72 @@ +package opencrypto.jcmathlib; + +public class Wei25519 { + 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 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"); diff --git a/applet/src/test/java/tests/JCMathLibTest.java b/applet/src/test/java/tests/JCMathLibTest.java index 4fd992d6..7b08e7cf 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); @@ -359,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)); @@ -406,7 +432,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 +453,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 +470,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 +486,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 +502,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 +519,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 +536,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 +694,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(); 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"}