Skip to content

Commit 8497fa1

Browse files
authored
Merge pull request #31 from ivmaykov/master
Constant-time GroupElement.cmov()
2 parents 3556096 + 48baa6d commit 8497fa1

File tree

6 files changed

+55
-15
lines changed

6 files changed

+55
-15
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>net.i2p.crypto</groupId>
55
<artifactId>eddsa</artifactId>
6-
<version>0.1.0</version>
6+
<version>0.2.0-SNAPSHOT</version>
77
<name>EdDSA-Java</name>
88
<packaging>bundle</packaging>
99
<description>Implementation of EdDSA in Java</description>

src/net/i2p/crypto/eddsa/math/FieldElement.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,7 @@ public FieldElement divide(FieldElement val) {
7070

7171
public abstract FieldElement pow22523();
7272

73+
public abstract FieldElement cmov(FieldElement val, final int b);
74+
7375
// Note: concrete subclasses must implement hashCode() and equals()
7476
}

src/net/i2p/crypto/eddsa/math/GroupElement.java

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public GroupElement(final Curve curve, final byte[] s) {
238238
y = curve.getField().fromByteArray(s);
239239
yy = y.square();
240240

241-
// u = y^2-1
241+
// u = y^2-1
242242
u = yy.subtractOne();
243243

244244
// v = dy^2+1
@@ -248,7 +248,7 @@ public GroupElement(final Curve curve, final byte[] s) {
248248
v3 = v.square().multiply(v);
249249

250250
// x = (v3^2)vu, aka x = uv^7
251-
x = v3.square().multiply(v).multiply(u);
251+
x = v3.square().multiply(v).multiply(u);
252252

253253
// x = (uv^7)^((q-5)/8)
254254
x = x.pow22523();
@@ -815,19 +815,10 @@ static byte[] toRadix16(final byte[] a) {
815815
*
816816
* @param u The group element to return if $b == 1$.
817817
* @param b in $\{0, 1\}$
818-
* @return $u$ if $b == 1$; this if $b == 0$; null otherwise.
818+
* @return $u$ if $b == 1$; this if $b == 0$. Results undefined if $b$ is not in $\{0, 1\}$.
819819
*/
820820
GroupElement cmov(final GroupElement u, final int b) {
821-
GroupElement ret = null;
822-
for (int i = 0; i < b; i++) {
823-
// Only for b == 1
824-
ret = u;
825-
}
826-
for (int i = 0; i < 1-b; i++) {
827-
// Only for b == 0
828-
ret = this;
829-
}
830-
return ret;
821+
return precomp(curve, X.cmov(u.X, b), Y.cmov(u.Y, b), Z.cmov(u.Z, b));
831822
}
832823

833824
/**
@@ -869,7 +860,7 @@ GroupElement select(final int pos, final int b) {
869860
/**
870861
* $h = a * B$ where $a = a[0]+256*a[1]+\dots+256^{31} a[31]$ and
871862
* $B$ is this point. If its lookup table has not been precomputed, it
872-
* will be at the start of the method (and cached for later calls).
863+
* will be at the start of the method (and cached for later calls).
873864
* Constant time.
874865
* <p>
875866
* Preconditions: (TODO: Check this applies here)

src/net/i2p/crypto/eddsa/math/bigint/BigIntegerFieldElement.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@ public FieldElement pow22523(){
104104
return pow(f.getQm5d8());
105105
}
106106

107+
@Override
108+
public FieldElement cmov(FieldElement val, int b) {
109+
// Not constant-time, but it doesn't really matter because none of the underlying BigInteger operations
110+
// are either, so there's not much point in trying hard here ...
111+
return b == 0 ? this : val;
112+
}
113+
107114
@Override
108115
public int hashCode() {
109116
return bi.hashCode();

src/net/i2p/crypto/eddsa/math/ed25519/Ed25519FieldElement.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,29 @@ public FieldElement pow22523() {
942942
return multiply(t0);
943943
}
944944

945+
/**
946+
* Constant-time conditional move. Well, actually it is a conditional copy.
947+
* Logic is inspired by the SUPERCOP implementation at:
948+
* https://github.com/floodyberry/supercop/blob/master/crypto_sign/ed25519/ref10/fe_cmov.c
949+
*
950+
* @param val the other field element.
951+
* @param b must be 0 or 1, otherwise results are undefined.
952+
* @return a copy of this if $b == 0$, or a copy of val if $b == 1$.
953+
*/
954+
@Override
955+
public FieldElement cmov(FieldElement val, int b) {
956+
Ed25519FieldElement that = (Ed25519FieldElement) val;
957+
b = -b;
958+
int[] result = new int[10];
959+
for (int i = 0; i < 10; i++) {
960+
result[i] = this.t[i];
961+
int x = this.t[i] ^ that.t[i];
962+
x &= b;
963+
result[i] ^= x;
964+
}
965+
return new Ed25519FieldElement(this.f, result);
966+
}
967+
945968
@Override
946969
public int hashCode() {
947970
return Arrays.hashCode(t);

test/net/i2p/crypto/eddsa/math/AbstractFieldElementTest.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ public void pow22523ReturnsCorrectResult() {
190190

191191
// endregion
192192

193+
// region cmov
194+
195+
@Test
196+
public void cmovReturnsCorrectResult() {
197+
final FieldElement zero = getZeroFieldElement();
198+
final FieldElement nz = getNonZeroFieldElement();
199+
final FieldElement f = getRandomFieldElement();
200+
201+
Assert.assertThat(zero.cmov(nz, 0), IsEqual.equalTo(zero));
202+
Assert.assertThat(zero.cmov(nz, 1), IsEqual.equalTo(nz));
203+
204+
Assert.assertThat(f.cmov(nz, 0), IsEqual.equalTo(f));
205+
Assert.assertThat(f.cmov(nz, 1), IsEqual.equalTo(nz));
206+
}
207+
208+
// endregion
209+
193210
// region hashCode / equals
194211

195212
@Test

0 commit comments

Comments
 (0)