Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,19 +152,19 @@ 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
-o OUTPUT, --output OUTPUT
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
Expand Down
2 changes: 1 addition & 1 deletion applet/build.gradle
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
132 changes: 103 additions & 29 deletions applet/src/main/java/opencrypto/jcmathlib/BigNat.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public void divide(BigNat other) {
tmp.lock();
tmp.clone(this);
tmp.remainderDivide(other, this);
copy(tmp);
tmp.unlock();
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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();
}
}
53 changes: 51 additions & 2 deletions applet/src/main/java/opencrypto/jcmathlib/BigNatInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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
Expand All @@ -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.
*
Expand All @@ -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;
Expand Down Expand Up @@ -543,6 +588,10 @@ public void remainderDivide(BigNatInternal divisor, BigNatInternal quotient) {
divisionRound++;
divisorShift--;
}

if (quotient != null) {
quotient.shrink();
}
}

/**
Expand Down
Loading
Loading