Java implementation of Ed25519

Source: https://github.com/str4d/ed25519-java
Git commit: be161ee7c6da29129b5ec6c4739ec3a99114a846
License: Public domain
This commit is contained in:
str4d
2014-08-21 23:27:45 +00:00
parent 5041d819a9
commit a1cb00b5a3
26 changed files with 3774 additions and 0 deletions

View File

@ -0,0 +1,188 @@
package net.i2p.crypto.eddsa;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Arrays;
import net.i2p.crypto.eddsa.math.Curve;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.math.ScalarOps;
/**
* @author str4d
*
*/
public class EdDSAEngine extends Signature {
private MessageDigest digest;
private final ByteArrayOutputStream baos;
private EdDSAKey key;
/**
* No specific hash requested, allows any EdDSA key.
*/
public EdDSAEngine() {
super("EdDSA");
baos = new ByteArrayOutputStream(256);
}
/**
* Specific hash requested, only matching keys will be allowed.
* @param digest the hash algorithm that keys must have to sign or verify.
*/
public EdDSAEngine(MessageDigest digest) {
this();
this.digest = digest;
}
@Override
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
if (digest != null)
digest.reset();
baos.reset();
if (privateKey instanceof EdDSAPrivateKey) {
EdDSAPrivateKey privKey = (EdDSAPrivateKey) privateKey;
key = privKey;
if (digest == null) {
// Instantiate the digest from the key parameters
try {
digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm());
} catch (NoSuchAlgorithmException e) {
throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key.");
}
} else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
// Preparing for hash
// r = H(h_b,...,h_2b-1,M)
int b = privKey.getParams().getCurve().getField().getb();
digest.update(privKey.getH(), b/8, b/4 - b/8);
} else
throw new InvalidKeyException("cannot identify EdDSA private key.");
}
@Override
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
if (digest != null)
digest.reset();
baos.reset();
if (publicKey instanceof EdDSAPublicKey) {
key = (EdDSAPublicKey) publicKey;
if (digest == null) {
// Instantiate the digest from the key parameters
try {
digest = MessageDigest.getInstance(key.getParams().getHashAlgorithm());
} catch (NoSuchAlgorithmException e) {
throw new InvalidKeyException("cannot get required digest " + key.getParams().getHashAlgorithm() + " for private key.");
}
} else if (!key.getParams().getHashAlgorithm().equals(digest.getAlgorithm()))
throw new InvalidKeyException("Key hash algorithm does not match chosen digest");
} else
throw new InvalidKeyException("cannot identify EdDSA public key.");
}
@Override
protected void engineUpdate(byte b) throws SignatureException {
// We need to store the message because it is used in several hashes
// XXX Can this be done more efficiently?
baos.write(b);
}
@Override
protected void engineUpdate(byte[] b, int off, int len)
throws SignatureException {
// We need to store the message because it is used in several hashes
// XXX Can this be done more efficiently?
baos.write(b, off, len);
}
@Override
protected byte[] engineSign() throws SignatureException {
Curve curve = key.getParams().getCurve();
ScalarOps sc = key.getParams().getScalarOps();
byte[] a = ((EdDSAPrivateKey) key).geta();
byte[] message = baos.toByteArray();
// r = H(h_b,...,h_2b-1,M)
byte[] r = digest.digest(message);
// r mod l
// Reduces r from 64 bytes to 32 bytes
r = sc.reduce(r);
// R = rB
GroupElement R = key.getParams().getB().scalarMultiply(r);
byte[] Rbyte = R.toByteArray();
// S = (r + H(Rbar,Abar,M)*a) mod l
digest.update(Rbyte);
digest.update(((EdDSAPrivateKey) key).getAbyte());
byte[] h = digest.digest(message);
h = sc.reduce(h);
byte[] S = sc.multiplyAndAdd(h, a, r);
// R+S
int b = curve.getField().getb();
ByteBuffer out = ByteBuffer.allocate(b/4);
out.put(Rbyte).put(S);
return out.array();
}
@Override
protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
Curve curve = key.getParams().getCurve();
int b = curve.getField().getb();
if (sigBytes.length != b/4)
throw new SignatureException("signature length is wrong");
// R is first b/8 bytes of sigBytes, S is second b/8 bytes
digest.update(sigBytes, 0, b/8);
digest.update(((EdDSAPublicKey) key).getAbyte());
// h = H(Rbar,Abar,M)
byte[] message = baos.toByteArray();
byte[] h = digest.digest(message);
// h mod l
h = key.getParams().getScalarOps().reduce(h);
byte[] Sbyte = Arrays.copyOfRange(sigBytes, b/8, b/4);
// R = SB - H(Rbar,Abar,M)A
GroupElement R = key.getParams().getB().doubleScalarMultiplyVariableTime(
((EdDSAPublicKey) key).getNegativeA(), h, Sbyte);
// Variable time. This should be okay, because there are no secret
// values used anywhere in verification.
byte[] Rcalc = R.toByteArray();
for (int i = 0; i < Rcalc.length; i++) {
if (Rcalc[i] != sigBytes[i])
return false;
}
return true;
}
/**
* @deprecated replaced with <a href="#engineSetParameter(java.security.spec.AlgorithmParameterSpec)">
*/
@Override
protected void engineSetParameter(String param, Object value) {
throw new UnsupportedOperationException("engineSetParameter unsupported");
}
/**
* @deprecated
*/
@Override
protected Object engineGetParameter(String param) {
throw new UnsupportedOperationException("engineSetParameter unsupported");
}
}

View File

@ -0,0 +1,16 @@
package net.i2p.crypto.eddsa;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
/**
* Common interface for all EdDSA keys.
* @author str4d
*
*/
public interface EdDSAKey {
/**
* return a parameter specification representing the EdDSA domain
* parameters for the key.
*/
public EdDSAParameterSpec getParams();
}

View File

@ -0,0 +1,68 @@
package net.i2p.crypto.eddsa;
import java.security.PrivateKey;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
/**
* An EdDSA private key.
* @author str4d
*
*/
public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
private static final long serialVersionUID = 23495873459878957L;
private transient final byte[] seed;
private transient final byte[] h;
private transient final byte[] a;
private transient final GroupElement A;
private transient final byte[] Abyte;
private transient final EdDSAParameterSpec edDsaSpec;
public EdDSAPrivateKey(EdDSAPrivateKeySpec spec) {
this.seed = spec.getSeed();
this.h = spec.getH();
this.a = spec.geta();
this.A = spec.getA();
this.Abyte = this.A.toByteArray();
this.edDsaSpec = spec.getParams();
}
public String getAlgorithm() {
return "EdDSA";
}
public String getFormat() {
return "PKCS#8";
}
public byte[] getEncoded() {
// TODO Auto-generated method stub
return null;
}
public EdDSAParameterSpec getParams() {
return edDsaSpec;
}
public byte[] getSeed() {
return seed;
}
public byte[] getH() {
return h;
}
public byte[] geta() {
return a;
}
public GroupElement getA() {
return A;
}
public byte[] getAbyte() {
return Abyte;
}
}

View File

@ -0,0 +1,56 @@
package net.i2p.crypto.eddsa;
import java.security.PublicKey;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
/**
* An EdDSA public key.
* @author str4d
*
*/
public class EdDSAPublicKey implements EdDSAKey, PublicKey {
private static final long serialVersionUID = 9837459837498475L;
private final GroupElement A;
private final GroupElement Aneg;
private final byte[] Abyte;
private final EdDSAParameterSpec edDsaSpec;
public EdDSAPublicKey(EdDSAPublicKeySpec spec) {
this.A = spec.getA();
this.Aneg = spec.getNegativeA();
this.Abyte = this.A.toByteArray();
this.edDsaSpec = spec.getParams();
}
public String getAlgorithm() {
return "EdDSA";
}
public String getFormat() {
return "X.509";
}
public byte[] getEncoded() {
// TODO Auto-generated method stub
return null;
}
public EdDSAParameterSpec getParams() {
return edDsaSpec;
}
public GroupElement getA() {
return A;
}
public GroupElement getNegativeA() {
return Aneg;
}
public byte[] getAbyte() {
return Abyte;
}
}

View File

@ -0,0 +1,56 @@
package net.i2p.crypto.eddsa;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactorySpi;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
/**
* @author str4d
*
*/
public class KeyFactory extends KeyFactorySpi {
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
throws InvalidKeySpecException {
if (keySpec instanceof EdDSAPrivateKeySpec) {
return new EdDSAPrivateKey((EdDSAPrivateKeySpec) keySpec);
}
throw new InvalidKeySpecException("key spec not recognised");
}
protected PublicKey engineGeneratePublic(KeySpec keySpec)
throws InvalidKeySpecException {
if (keySpec instanceof EdDSAPublicKeySpec) {
return new EdDSAPublicKey((EdDSAPublicKeySpec) keySpec);
}
throw new InvalidKeySpecException("key spec not recognised");
}
@SuppressWarnings("unchecked")
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
throws InvalidKeySpecException {
if (keySpec.isAssignableFrom(EdDSAPublicKeySpec.class) && key instanceof EdDSAPublicKey) {
EdDSAPublicKey k = (EdDSAPublicKey) key;
if (k.getParams() != null) {
return (T) new EdDSAPublicKeySpec(k.getA(), k.getParams());
}
} else if (keySpec.isAssignableFrom(EdDSAPrivateKeySpec.class) && key instanceof EdDSAPrivateKey) {
EdDSAPrivateKey k = (EdDSAPrivateKey) key;
if (k.getParams() != null) {
return (T) new EdDSAPrivateKeySpec(k.getSeed(), k.getH(), k.geta(), k.getA(), k.getParams());
}
}
throw new InvalidKeySpecException("not implemented yet " + key + " " + keySpec);
}
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
throw new InvalidKeyException("No other EdDSA key providers known");
}
}

View File

@ -0,0 +1,86 @@
package net.i2p.crypto.eddsa;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidParameterException;
import java.security.KeyPair;
import java.security.KeyPairGeneratorSpi;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Hashtable;
import net.i2p.crypto.eddsa.spec.EdDSAGenParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
/**
* Default strength is 256
*/
public class KeyPairGenerator extends KeyPairGeneratorSpi {
private static final int DEFAULT_STRENGTH = 256;
private EdDSAParameterSpec edParams;
private SecureRandom random;
private boolean initialized;
private static final Hashtable<Integer, AlgorithmParameterSpec> edParameters;
static {
edParameters = new Hashtable<Integer, AlgorithmParameterSpec>();
edParameters.put(Integer.valueOf(DEFAULT_STRENGTH), new EdDSAGenParameterSpec(EdDSANamedCurveTable.CURVE_ED25519_SHA512));
}
public void initialize(int strength, SecureRandom random) {
AlgorithmParameterSpec edParams = edParameters.get(Integer.valueOf(strength));
if (edParams == null)
throw new InvalidParameterException("unknown key type.");
try {
initialize(edParams, random);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidParameterException("key type not configurable.");
}
}
@Override
public void initialize(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException {
if (params instanceof EdDSAParameterSpec) {
edParams = (EdDSAParameterSpec) params;
} else if (params instanceof EdDSAGenParameterSpec) {
edParams = createNamedCurveSpec(((EdDSAGenParameterSpec) params).getName());
} else
throw new InvalidAlgorithmParameterException("parameter object not a EdDSAParameterSpec");
this.random = random;
initialized = true;
}
public KeyPair generateKeyPair() {
if (!initialized)
initialize(DEFAULT_STRENGTH, new SecureRandom());
byte[] seed = new byte[edParams.getCurve().getField().getb()/8];
random.nextBytes(seed);
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(seed, edParams);
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(privKey.getA(), edParams);
return new KeyPair(new EdDSAPublicKey(pubKey), new EdDSAPrivateKey(privKey));
}
/**
* Create an EdDSANamedCurveSpec from the provided curve name. The current
* implementation fetches the pre-created curve spec from a table.
* @param curveName the EdDSA named curve.
* @return the specification for the named curve.
* @throws InvalidAlgorithmParameterException if the named curve is unknown.
*/
protected EdDSANamedCurveSpec createNamedCurveSpec(String curveName) throws InvalidAlgorithmParameterException {
EdDSANamedCurveSpec spec = EdDSANamedCurveTable.getByName(curveName);
if (spec == null) {
throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
}
return spec;
}
}

View File

@ -0,0 +1,54 @@
package net.i2p.crypto.eddsa;
/**
* @author str4d
*
*/
public class Utils {
/**
* Constant-time byte comparison.
* @return 1 if b and c are equal, 0 otherwise.
*/
public static int equal(int b, int c) {
int result = 0;
int xor = b ^ c;
for (int i = 0; i < 8; i++) {
result |= xor >> i;
}
return (result ^ 0x01) & 0x01;
}
/**
* Constant-time determine if byte is negative.
* @param b the byte to check.
* @return 1 if the byte is negative, 0 otherwise.
*/
public static int negative(int b) {
return (b >> 8) & 1;
}
/**
* Get the i'th bit of a byte array.
* @param h the byte array.
* @param i the bit index.
* @return 0 or 1, the value of the i'th bit in h
*/
public static int bit(byte[] h, int i) {
return (h[i/8] >> (i%8)) & 1;
}
/**
* Converts a hex string to bytes.
* @param s the hex string to be converted.
* @return the byte[]
*/
public static byte[] hexToBytes(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}

View File

@ -0,0 +1,12 @@
package net.i2p.crypto.eddsa.math;
import net.i2p.crypto.eddsa.Utils;
public class Constants {
public static final byte[] ZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
public static final byte[] ONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
public static final byte[] TWO = Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000");
public static final byte[] FOUR = Utils.hexToBytes("0400000000000000000000000000000000000000000000000000000000000000");
public static final byte[] FIVE = Utils.hexToBytes("0500000000000000000000000000000000000000000000000000000000000000");
public static final byte[] EIGHT = Utils.hexToBytes("0800000000000000000000000000000000000000000000000000000000000000");
}

View File

@ -0,0 +1,70 @@
package net.i2p.crypto.eddsa.math;
import java.io.Serializable;
/**
* A twisted Edwards curve.
* Points on the curve satisfy -x^2 + y^2 = 1 + d x^2y^2
* @author str4d
*
*/
public class Curve implements Serializable {
private static final long serialVersionUID = 4578920872509827L;
private final Field f;
private final FieldElement d;
private final FieldElement d2;
private final FieldElement I;
private final GroupElement zeroP2;
private final GroupElement zeroP3;
private final GroupElement zeroPrecomp;
public Curve(Field f, byte[] d, FieldElement I) {
this.f = f;
this.d = f.fromByteArray(d);
this.d2 = this.d.add(this.d);
this.I = I;
FieldElement zero = f.zero;
FieldElement one = f.one;
zeroP2 = GroupElement.p2(this, zero, one, one);
zeroP3 = GroupElement.p3(this, zero, one, one, zero);
zeroPrecomp = GroupElement.precomp(this, one, one, zero);
}
public Field getField() {
return f;
}
public FieldElement getD() {
return d;
}
public FieldElement get2D() {
return d2;
}
public FieldElement getI() {
return I;
}
public GroupElement getZero(GroupElement.Representation repr) {
switch (repr) {
case P2:
return zeroP2;
case P3:
return zeroP3;
case PRECOMP:
return zeroPrecomp;
default:
return null;
}
}
public GroupElement createPoint(byte[] P, boolean precompute) {
GroupElement ge = new GroupElement(this, P);
if (precompute)
ge.precompute(true);
return ge;
}
}

View File

@ -0,0 +1,39 @@
package net.i2p.crypto.eddsa.math;
/**
* Common interface for all (b-1)-bit encodings of elements
* of EdDSA finite fields.
* @author str4d
*
*/
public abstract class Encoding {
protected Field f;
public void setField(Field f) {
this.f = f;
}
/**
* Encode a FieldElement in its (b-1)-bit encoding.
* @return the (b-1)-bit encoding of this FieldElement.
*/
public abstract byte[] encode(FieldElement x);
/**
* Decode a FieldElement from its (b-1)-bit encoding.
* The highest bit is masked out.
* @param val the (b-1)-bit encoding of a FieldElement.
* @return the FieldElement represented by 'val'.
*/
public abstract FieldElement decode(byte[] in);
/**
* From the Ed25519 paper:
* x is negative if the (b-1)-bit encoding of x is lexicographically larger
* than the (b-1)-bit encoding of -x. If q is an odd prime and the encoding
* is the little-endian representation of {0, 1,..., q-1} then the negative
* elements of F_q are {1, 3, 5,..., q-2}.
* @return
*/
public abstract boolean isNegative(FieldElement x);
}

View File

@ -0,0 +1,88 @@
package net.i2p.crypto.eddsa.math;
import java.io.Serializable;
/**
* An EdDSA finite field. Includes several pre-computed values.
* @author str4d
*
*/
public class Field implements Serializable {
private static final long serialVersionUID = 8746587465875676L;
public final FieldElement zero;
public final FieldElement one;
public final FieldElement two;
public final FieldElement four;
public final FieldElement five;
public final FieldElement eight;
private final int b;
private final FieldElement q;
/**
* q-2
*/
private final FieldElement qm2;
/**
* (q-5) / 8
*/
private final FieldElement qm5d8;
private final Encoding enc;
public Field(int b, byte[] q, Encoding enc) {
this.b = b;
this.enc = enc;
this.enc.setField(this);
this.q = fromByteArray(q);
// Set up constants
zero = fromByteArray(Constants.ZERO);
one = fromByteArray(Constants.ONE);
two = fromByteArray(Constants.TWO);
four = fromByteArray(Constants.FOUR);
five = fromByteArray(Constants.FIVE);
eight = fromByteArray(Constants.EIGHT);
// Precompute values
qm2 = this.q.subtract(two);
qm5d8 = this.q.subtract(five).divide(eight);
}
public FieldElement fromByteArray(byte[] x) {
return enc.decode(x);
}
public int getb() {
return b;
}
public FieldElement getQ() {
return q;
}
public FieldElement getQm2() {
return qm2;
}
public FieldElement getQm5d8() {
return qm5d8;
}
public Encoding getEncoding(){
return enc;
}
@Override
public int hashCode() {
return q.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Field))
return false;
Field f = (Field) obj;
return b == f.b && q.equals(f.q);
}
}

View File

@ -0,0 +1,51 @@
package net.i2p.crypto.eddsa.math;
public abstract class FieldElement {
protected final Field f;
public FieldElement(Field f) {
this.f = f;
}
/**
* Encode a FieldElement in its (b-1)-bit encoding.
* @return the (b-1)-bit encoding of this FieldElement.
*/
public byte[] toByteArray() {
return f.getEncoding().encode(this);
}
public abstract boolean isNonZero();
public boolean isNegative() {
return f.getEncoding().isNegative(this);
}
public abstract FieldElement add(FieldElement val);
public FieldElement addOne() {
return add(f.one);
}
public abstract FieldElement subtract(FieldElement val);
public FieldElement subtractOne() {
return subtract(f.one);
}
public abstract FieldElement negate();
public FieldElement divide(FieldElement val) {
return multiply(val.invert());
}
public abstract FieldElement multiply(FieldElement val);
public abstract FieldElement square();
public abstract FieldElement squareAndDouble();
public abstract FieldElement invert();
public abstract FieldElement pow22523();
}

View File

@ -0,0 +1,681 @@
package net.i2p.crypto.eddsa.math;
import java.io.Serializable;
import net.i2p.crypto.eddsa.Utils;
/**
* A point (x,y) on an EdDSA curve.
* @author str4d
*
*/
public class GroupElement implements Serializable {
private static final long serialVersionUID = 2395879087349587L;
public enum Representation {
/** Projective: (X:Y:Z) satisfying x=X/Z, y=Y/Z */
P2,
/** Extended: (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT */
P3,
/** Completed: ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T */
P1P1,
/** Precomputed (Duif): (y+x,y-x,2dxy) */
PRECOMP,
/** Cached: (Y+X,Y-X,Z,2dT) */
CACHED
}
public static GroupElement p2(Curve curve, FieldElement X,
FieldElement Y, FieldElement Z) {
return new GroupElement(curve, Representation.P2, X, Y, Z, null);
}
public static GroupElement p3(Curve curve, FieldElement X,
FieldElement Y, FieldElement Z, FieldElement T) {
return new GroupElement(curve, Representation.P3, X, Y, Z, T);
}
public static GroupElement p1p1(Curve curve, FieldElement X,
FieldElement Y, FieldElement Z, FieldElement T) {
return new GroupElement(curve, Representation.P1P1, X, Y, Z, T);
}
public static GroupElement precomp(Curve curve, FieldElement ypx,
FieldElement ymx, FieldElement xy2d) {
return new GroupElement(curve, Representation.PRECOMP, ypx, ymx, xy2d, null);
}
public static GroupElement cached(Curve curve, FieldElement YpX,
FieldElement YmX, FieldElement Z, FieldElement T2d) {
return new GroupElement(curve, Representation.CACHED, YpX, YmX, Z, T2d);
}
/**
* Variable is package private only so that tests run.
*/
final Curve curve;
/**
* Variable is package private only so that tests run.
*/
final Representation repr;
/**
* Variable is package private only so that tests run.
*/
final FieldElement X;
/**
* Variable is package private only so that tests run.
*/
final FieldElement Y;
/**
* Variable is package private only so that tests run.
*/
final FieldElement Z;
/**
* Variable is package private only so that tests run.
*/
final FieldElement T;
/**
* Precomputed table for {@link GroupElement#scalarMultiply(byte[])},
* filled if necessary.
*
* Variable is package private only so that tests run.
*/
GroupElement[][] precmp;
/**
* Precomputed table for {@link GroupElement#doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])},
* filled if necessary.
*
* Variable is package private only so that tests run.
*/
GroupElement[] dblPrecmp;
public GroupElement(Curve curve, Representation repr, FieldElement X, FieldElement Y,
FieldElement Z, FieldElement T) {
this.curve = curve;
this.repr = repr;
this.X = X;
this.Y = Y;
this.Z = Z;
this.T = T;
}
public GroupElement(Curve curve, byte[] s) {
FieldElement x, y, yy, u, v, v3, vxx, check;
y = curve.getField().fromByteArray(s);
yy = y.square();
// u = y^2-1
u = yy.subtractOne();
// v = dy^2+1
v = yy.multiply(curve.getD()).addOne();
// v3 = v^3
v3 = v.square().multiply(v);
// x = (v3^2)vu, aka x = uv^7
x = v3.square().multiply(v).multiply(u);
// x = (uv^7)^((q-5)/8)
x = x.pow22523();
// x = uv^3(uv^7)^((q-5)/8)
x = v3.multiply(u).multiply(x);
vxx = x.square().multiply(v);
check = vxx.subtract(u); // vx^2-u
if (check.isNonZero()) {
check = vxx.add(u); // vx^2+u
if (check.isNonZero())
throw new IllegalArgumentException("not a valid GroupElement");
x = x.multiply(curve.getI());
}
if ((x.isNegative() ? 1 : 0) != Utils.bit(s, curve.getField().getb()-1)) {
x = x.negate();
}
this.curve = curve;
repr = Representation.P3;
X = x;
Y = y;
Z = curve.getField().one;
T = X.multiply(Y);
}
public byte[] toByteArray() {
switch (repr) {
case P2:
case P3:
FieldElement recip = Z.invert();
FieldElement x = X.multiply(recip);
FieldElement y = Y.multiply(recip);
byte[] s = y.toByteArray();
s[s.length-1] |= (x.isNegative() ? (byte) 0x80 : 0);
return s;
default:
return toP2().toByteArray();
}
}
public GroupElement toP2() {
return toRep(Representation.P2);
}
public GroupElement toP3() {
return toRep(Representation.P3);
}
public GroupElement toCached() {
return toRep(Representation.CACHED);
}
/**
* Convert a GroupElement from one Representation to another.<br>
* r = p<br>
* <br>
* Supported conversions:<br>
* - P3 -> P2<br>
* - P3 -> CACHED (1 multiply, 1 add, 1 subtract)<br>
* - P1P1 -> P2 (3 multiply)<br>
* - P1P1 -> P3 (4 multiply)
* @param rep The Representation to convert to.
* @return A new GroupElement in the given Representation.
*/
private GroupElement toRep(Representation repr) {
switch (this.repr) {
case P3:
switch (repr) {
case P2:
return p2(curve, X, Y, Z);
case CACHED:
return cached(curve, Y.add(X), Y.subtract(X), Z, T.multiply(curve.get2D()));
default:
throw new IllegalArgumentException();
}
case P1P1:
switch (repr) {
case P2:
return p2(curve, X.multiply(T), Y.multiply(Z), Z.multiply(T));
case P3:
return p3(curve, X.multiply(T), Y.multiply(Z), Z.multiply(T), X.multiply(Y));
default:
throw new IllegalArgumentException();
}
default:
throw new UnsupportedOperationException();
}
}
/**
* Precompute the tables for {@link GroupElement#scalarMultiply(byte[])}
* and {@link GroupElement#doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])}.
*
* @param precomputeSingle should the matrix for scalarMultiply() be precomputed?
*/
public synchronized void precompute(boolean precomputeSingle) {
GroupElement Bi;
if (precomputeSingle && precmp == null) {
// Precomputation for single scalar multiplication.
precmp = new GroupElement[32][8];
Bi = this;
for (int i = 0; i < 32; i++) {
GroupElement Bij = Bi;
for (int j = 0; j < 8; j++) {
FieldElement recip = Bij.Z.invert();
FieldElement x = Bij.X.multiply(recip);
FieldElement y = Bij.Y.multiply(recip);
precmp[i][j] = precomp(curve, y.add(x), y.subtract(x), x.multiply(y).multiply(curve.get2D()));
Bij = Bij.add(Bi.toCached()).toP3();
}
for (int k = 0; k < 8; k++) {
Bi = Bi.add(Bi.toCached()).toP3();
}
}
}
// Precomputation for double scalar multiplication.
// P,3P,5P,7P,9P,11P,13P,15P
if (dblPrecmp != null)
return;
dblPrecmp = new GroupElement[8];
Bi = this;
for (int i = 0; i < 8; i++) {
FieldElement recip = Bi.Z.invert();
FieldElement x = Bi.X.multiply(recip);
FieldElement y = Bi.Y.multiply(recip);
dblPrecmp[i] = precomp(curve, y.add(x), y.subtract(x), x.multiply(y).multiply(curve.get2D()));
// Bi = edwards(B,edwards(B,Bi))
Bi = add(add(Bi.toCached()).toP3().toCached()).toP3();
}
}
/**
* r = 2 * p
* @return The P1P1 representation
*/
public GroupElement dbl() {
switch (repr) {
case P2:
case P3: // Ignore T for P3 representation
FieldElement XX, YY, B, A, AA, Yn, Zn;
XX = X.square();
YY = Y.square();
B = Z.squareAndDouble();
A = X.add(Y);
AA = A.square();
Yn = YY.add(XX);
Zn = YY.subtract(XX);
return p1p1(curve, AA.subtract(Yn), Yn, Zn, B.subtract(Zn));
default:
throw new UnsupportedOperationException();
}
}
/**
* GroupElement addition using the twisted Edwards addition law with
* extended coordinates (Hisil2008).<br>
* r = p + q
* @param q the PRECOMP representation of the GroupElement to add.
* @return the P1P1 representation of the result.
*/
private GroupElement madd(GroupElement q) {
if (this.repr != Representation.P3)
throw new UnsupportedOperationException();
if (q.repr != Representation.PRECOMP)
throw new IllegalArgumentException();
FieldElement YpX, YmX, A, B, C, D;
YpX = Y.add(X);
YmX = Y.subtract(X);
A = YpX.multiply(q.X); // q->y+x
B = YmX.multiply(q.Y); // q->y-x
C = q.Z.multiply(T); // q->2dxy
D = Z.add(Z);
return p1p1(curve, A.subtract(B), A.add(B), D.add(C), D.subtract(C));
}
/**
* GroupElement subtraction using the twisted Edwards addition law with
* extended coordinates (Hisil2008).<br>
* r = p - q
* @param q the PRECOMP representation of the GroupElement to subtract.
* @return the P1P1 representation of the result.
*/
private GroupElement msub(GroupElement q) {
if (this.repr != Representation.P3)
throw new UnsupportedOperationException();
if (q.repr != Representation.PRECOMP)
throw new IllegalArgumentException();
FieldElement YpX, YmX, A, B, C, D;
YpX = Y.add(X);
YmX = Y.subtract(X);
A = YpX.multiply(q.Y); // q->y-x
B = YmX.multiply(q.X); // q->y+x
C = q.Z.multiply(T); // q->2dxy
D = Z.add(Z);
return p1p1(curve, A.subtract(B), A.add(B), D.subtract(C), D.add(C));
}
/**
* GroupElement addition using the twisted Edwards addition law with
* extended coordinates (Hisil2008).<br>
* r = p + q
* @param q the CACHED representation of the GroupElement to add.
* @return the P1P1 representation of the result.
*/
public GroupElement add(GroupElement q) {
if (this.repr != Representation.P3)
throw new UnsupportedOperationException();
if (q.repr != Representation.CACHED)
throw new IllegalArgumentException();
FieldElement YpX, YmX, A, B, C, ZZ, D;
YpX = Y.add(X);
YmX = Y.subtract(X);
A = YpX.multiply(q.X); // q->Y+X
B = YmX.multiply(q.Y); // q->Y-X
C = q.T.multiply(T); // q->2dT
ZZ = Z.multiply(q.Z);
D = ZZ.add(ZZ);
return p1p1(curve, A.subtract(B), A.add(B), D.add(C), D.subtract(C));
}
/**
* GroupElement subtraction using the twisted Edwards addition law with
* extended coordinates (Hisil2008).<br>
* r = p - q
* @param q the PRECOMP representation of the GroupElement to subtract.
* @return the P1P1 representation of the result.
*/
public GroupElement sub(GroupElement q) {
if (this.repr != Representation.P3)
throw new UnsupportedOperationException();
if (q.repr != Representation.CACHED)
throw new IllegalArgumentException();
FieldElement YpX, YmX, A, B, C, ZZ, D;
YpX = Y.add(X);
YmX = Y.subtract(X);
A = YpX.multiply(q.Y); // q->Y-X
B = YmX.multiply(q.X); // q->Y+X
C = q.T.multiply(T); // q->2dT
ZZ = Z.multiply(q.Z);
D = ZZ.add(ZZ);
return p1p1(curve, A.subtract(B), A.add(B), D.subtract(C), D.add(C));
}
public GroupElement negate() {
if (this.repr != Representation.P3)
throw new UnsupportedOperationException();
return curve.getZero(Representation.P3).sub(toCached()).toP3();
}
@Override
public int hashCode() {
// TODO
return 42;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof GroupElement))
return false;
GroupElement ge = (GroupElement) obj;
if (!this.repr.equals(ge.repr)) {
try {
ge = ge.toRep(this.repr);
} catch (Exception e) {
return false;
}
}
switch (this.repr) {
case P2:
case P3:
// Try easy way first
if (Z.equals(ge.Z))
return X.equals(ge.X) && Y.equals(ge.Y);
// X1/Z1 = X2/Z2 --> X1*Z2 = X2*Z1
FieldElement x1 = X.multiply(ge.Z);
FieldElement y1 = Y.multiply(ge.Z);
FieldElement x2 = ge.X.multiply(Z);
FieldElement y2 = ge.Y.multiply(Z);
return x1.equals(x2) && y1.equals(y2);
case P1P1:
return toP2().equals(ge);
case PRECOMP:
// Compare directly, PRECOMP is derived directly from x and y
return X.equals(ge.X) && Y.equals(ge.Y) && Z.equals(ge.Z);
case CACHED:
// Try easy way first
if (Z.equals(ge.Z))
return X.equals(ge.X) && Y.equals(ge.Y) && T.equals(ge.T);
// (Y+X)/Z = y+x etc.
FieldElement x3 = X.multiply(ge.Z);
FieldElement y3 = Y.multiply(ge.Z);
FieldElement t3 = T.multiply(ge.Z);
FieldElement x4 = ge.X.multiply(Z);
FieldElement y4 = ge.Y.multiply(Z);
FieldElement t4 = ge.T.multiply(Z);
return x3.equals(x4) && y3.equals(y4) && t3.equals(t4);
default:
return false;
}
}
/**
* Convert a to radix 16.
*
* Method is package private only so that tests run.
*
* @param a = a[0]+256*a[1]+...+256^31 a[31]
* @return 64 bytes, each between -8 and 7
*/
static byte[] toRadix16(byte[] a) {
byte[] e = new byte[64];
int i;
// Radix 16 notation
for (i = 0; i < 32; i++) {
e[2*i+0] = (byte) (a[i] & 15);
e[2*i+1] = (byte) ((a[i] >> 4) & 15);
}
/* each e[i] is between 0 and 15 */
/* e[63] is between 0 and 7 */
int carry = 0;
for (i = 0; i < 63; i++) {
e[i] += carry;
carry = e[i] + 8;
carry >>= 4;
e[i] -= carry << 4;
}
e[63] += carry;
/* each e[i] is between -8 and 7 */
return e;
}
/**
* Constant-time conditional move.
* Replaces this with u if b == 1.
* Replaces this with this if b == 0.
*
* Method is package private only so that tests run.
*
* @param u
* @param b in {0, 1}
* @return u if b == 1; this if b == 0; null otherwise.
*/
GroupElement cmov(GroupElement u, int b) {
GroupElement ret = null;
for (int i = 0; i < b; i++) {
// Only for b == 1
ret = u;
}
for (int i = 0; i < 1-b; i++) {
// Only for b == 0
ret = this;
}
return ret;
}
/**
* Look up 16^i r_i B in the precomputed table.
* No secret array indices, no secret branching.
* Constant time.
*
* Must have previously precomputed.
*
* Method is package private only so that tests run.
*
* @param pos = i/2 for i in {0, 2, 4,..., 62}
* @param b = r_i
* @return
*/
GroupElement select(int pos, int b) {
// Is r_i negative?
int bnegative = Utils.negative(b);
// |r_i|
int babs = b - (((-bnegative) & b) << 1);
// 16^i |r_i| B
GroupElement t = curve.getZero(Representation.PRECOMP)
.cmov(precmp[pos][0], Utils.equal(babs, 1))
.cmov(precmp[pos][1], Utils.equal(babs, 2))
.cmov(precmp[pos][2], Utils.equal(babs, 3))
.cmov(precmp[pos][3], Utils.equal(babs, 4))
.cmov(precmp[pos][4], Utils.equal(babs, 5))
.cmov(precmp[pos][5], Utils.equal(babs, 6))
.cmov(precmp[pos][6], Utils.equal(babs, 7))
.cmov(precmp[pos][7], Utils.equal(babs, 8));
// -16^i |r_i| B
GroupElement tminus = precomp(curve, t.Y, t.X, t.Z.negate());
// 16^i r_i B
return t.cmov(tminus, bnegative);
}
/**
* h = a * B where a = a[0]+256*a[1]+...+256^31 a[31] and
* B is this point. If its lookup table has not been precomputed, it
* will be at the start of the method (and cached for later calls).
* Constant time.
*
* Preconditions: (TODO: Check this applies here)
* a[31] <= 127
* @param a = a[0]+256*a[1]+...+256^31 a[31]
* @return
*/
public GroupElement scalarMultiply(byte[] a) {
GroupElement t;
int i;
byte[] e = toRadix16(a);
GroupElement h = curve.getZero(Representation.P3);
synchronized(this) {
// TODO: Get opinion from a crypto professional.
// This should in practice never be necessary, the only point that
// this should get called on is EdDSA's B.
//precompute();
for (i = 1; i < 64; i += 2) {
t = select(i/2, e[i]);
h = h.madd(t).toP3();
}
h = h.dbl().toP2().dbl().toP2().dbl().toP2().dbl().toP3();
for (i = 0; i < 64; i += 2) {
t = select(i/2, e[i]);
h = h.madd(t).toP3();
}
}
return h;
}
/**
* I don't really know what this method does.
*
* Method is package private only so that tests run.
*
* @param a 32 bytes
* @return 256 bytes
*/
static byte[] slide(byte[] a) {
byte[] r = new byte[256];
// put each bit of 'a' into a separate byte, 0 or 1
for (int i = 0; i < 256; ++i) {
r[i] = (byte) (1 & (a[i >> 3] >> (i & 7)));
}
for (int i = 0; i < 256; ++i) {
if (r[i] != 0) {
for (int b = 1; b <= 6 && i + b < 256; ++b) {
if (r[i + b] != 0) {
if (r[i] + (r[i + b] << b) <= 15) {
r[i] += r[i + b] << b;
r[i + b] = 0;
} else if (r[i] - (r[i + b] << b) >= -15) {
r[i] -= r[i + b] << b;
for (int k = i + b; k < 256; ++k) {
if (r[k] == 0) {
r[k] = 1;
break;
}
r[k] = 0;
}
} else
break;
}
}
}
}
return r;
}
/**
* r = a * A + b * B where a = a[0]+256*a[1]+...+256^31 a[31],
* b = b[0]+256*b[1]+...+256^31 b[31] and B is this point.
*
* A must have been previously precomputed.
*
* @param A in P3 representation.
* @param a = a[0]+256*a[1]+...+256^31 a[31]
* @param b = b[0]+256*b[1]+...+256^31 b[31]
* @return
*/
public GroupElement doubleScalarMultiplyVariableTime(GroupElement A, byte[] a, byte[] b) {
byte[] aslide = slide(a);
byte[] bslide = slide(b);
GroupElement r = curve.getZero(Representation.P2);
int i;
for (i = 255; i >= 0; --i) {
if (aslide[i] != 0 || bslide[i] != 0) break;
}
synchronized(this) {
// TODO: Get opinion from a crypto professional.
// This should in practice never be necessary, the only point that
// this should get called on is EdDSA's B.
//precompute();
for (; i >= 0; --i) {
GroupElement t = r.dbl();
if (aslide[i] > 0) {
t = t.toP3().madd(A.dblPrecmp[aslide[i]/2]);
} else if(aslide[i] < 0) {
t = t.toP3().msub(A.dblPrecmp[(-aslide[i])/2]);
}
if (bslide[i] > 0) {
t = t.toP3().madd(dblPrecmp[bslide[i]/2]);
} else if(bslide[i] < 0) {
t = t.toP3().msub(dblPrecmp[(-bslide[i])/2]);
}
r = t.toP2();
}
}
return r;
}
/**
* Verify that a point is on its curve.
* @param P The point to check.
* @return true if the point lies on its curve.
*/
public boolean isOnCurve() {
return isOnCurve(curve);
}
/**
* Verify that a point is on the curve.
* @param curve The curve to check.
* @return true if the point lies on the curve.
*/
public boolean isOnCurve(Curve curve) {
switch (repr) {
case P2:
case P3:
FieldElement recip = Z.invert();
FieldElement x = X.multiply(recip);
FieldElement y = Y.multiply(recip);
FieldElement xx = x.square();
FieldElement yy = y.square();
FieldElement dxxyy = curve.getD().multiply(xx).multiply(yy);
return curve.getField().one.add(dxxyy).add(xx).equals(yy);
default:
return toP2().isOnCurve(curve);
}
}
@Override
public String toString() {
return "[GroupElement\nX="+X+"\nY="+Y+"\nZ="+Z+"\nT="+T+"\n]";
}
}

View File

@ -0,0 +1,22 @@
package net.i2p.crypto.eddsa.math;
public interface ScalarOps {
/**
* Reduce the given scalar mod l.
* From the Ed25519 paper:
* Here we interpret 2b-bit strings in little-endian form as integers in
* {0, 1,..., 2^(2b)-1}.
* @param s
* @return s mod l
*/
public byte[] reduce(byte[] s);
/**
* r = (a * b + c) mod l
* @param a
* @param b
* @param c
* @return (a*b + c) mod l
*/
public byte[] multiplyAndAdd(byte[] a, byte[] b, byte[] c);
}

View File

@ -0,0 +1,113 @@
package net.i2p.crypto.eddsa.math.bigint;
import java.io.Serializable;
import java.math.BigInteger;
import net.i2p.crypto.eddsa.math.Field;
import net.i2p.crypto.eddsa.math.FieldElement;
/**
* A particular element of the field \Z/(2^255-19).
* @author str4d
*
*/
public class BigIntegerFieldElement extends FieldElement implements Serializable {
private static final long serialVersionUID = 4890398908392808L;
/**
* Variable is package private for encoding.
*/
final BigInteger bi;
public BigIntegerFieldElement(Field f, BigInteger bi) {
super(f);
this.bi = bi;
}
public boolean isNonZero() {
return !bi.equals(BigInteger.ZERO);
}
public FieldElement add(FieldElement val) {
return new BigIntegerFieldElement(f, bi.add(((BigIntegerFieldElement)val).bi)).mod(f.getQ());
}
@Override
public FieldElement addOne() {
return new BigIntegerFieldElement(f, bi.add(BigInteger.ONE)).mod(f.getQ());
}
public FieldElement subtract(FieldElement val) {
return new BigIntegerFieldElement(f, bi.subtract(((BigIntegerFieldElement)val).bi)).mod(f.getQ());
}
@Override
public FieldElement subtractOne() {
return new BigIntegerFieldElement(f, bi.subtract(BigInteger.ONE)).mod(f.getQ());
}
public FieldElement negate() {
return f.getQ().subtract(this);
}
@Override
public FieldElement divide(FieldElement val) {
return divide(((BigIntegerFieldElement)val).bi);
}
public FieldElement divide(BigInteger val) {
return new BigIntegerFieldElement(f, bi.divide(val)).mod(f.getQ());
}
public FieldElement multiply(FieldElement val) {
return new BigIntegerFieldElement(f, bi.multiply(((BigIntegerFieldElement)val).bi)).mod(f.getQ());
}
public FieldElement square() {
return multiply(this);
}
public FieldElement squareAndDouble() {
FieldElement sq = square();
return sq.add(sq);
}
public FieldElement invert() {
// Euler's theorem
//return modPow(f.getQm2(), f.getQ());
return new BigIntegerFieldElement(f, bi.modInverse(((BigIntegerFieldElement)f.getQ()).bi));
}
public FieldElement mod(FieldElement m) {
return new BigIntegerFieldElement(f, bi.mod(((BigIntegerFieldElement)m).bi));
}
public FieldElement modPow(FieldElement e, FieldElement m) {
return new BigIntegerFieldElement(f, bi.modPow(((BigIntegerFieldElement)e).bi, ((BigIntegerFieldElement)m).bi));
}
public FieldElement pow(FieldElement e){
return modPow(e, f.getQ());
}
public FieldElement pow22523(){
return pow(f.getQm5d8());
}
@Override
public int hashCode() {
return bi.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BigIntegerFieldElement))
return false;
BigIntegerFieldElement fe = (BigIntegerFieldElement) obj;
return bi.equals(fe.bi);
}
@Override
public String toString() {
return "[BigIntegerFieldElement val="+bi+"]";
}
}

View File

@ -0,0 +1,73 @@
package net.i2p.crypto.eddsa.math.bigint;
import java.io.Serializable;
import java.math.BigInteger;
import net.i2p.crypto.eddsa.math.Encoding;
import net.i2p.crypto.eddsa.math.Field;
import net.i2p.crypto.eddsa.math.FieldElement;
public class BigIntegerLittleEndianEncoding extends Encoding implements Serializable {
private static final long serialVersionUID = 3984579843759837L;
/**
* Mask where only the first b-1 bits are set.
*/
private BigInteger mask;
@Override
public void setField(Field f) {
super.setField(f);
mask = BigInteger.ONE.shiftLeft(f.getb()-1).subtract(BigInteger.ONE);
}
public byte[] encode(FieldElement x) {
return encode(((BigIntegerFieldElement)x).bi.and(mask));
}
/**
* Convert x to little endian.
* Constant time.
*
* @return array of length b/8
*/
public byte[] encode(BigInteger x) {
byte[] in = x.toByteArray();
byte[] out = new byte[f.getb()/8];
for (int i = 0; i < in.length; i++) {
out[i] = in[in.length-1-i];
}
for (int i = in.length; i < out.length; i++) {
out[i] = 0;
}
return out;
}
public FieldElement decode(byte[] in) {
if (in.length != f.getb()/8)
throw new IllegalArgumentException("Not a valid encoding");
return new BigIntegerFieldElement(f, toBigInteger(in).and(mask));
}
/**
* Convert in to big endian
*/
public BigInteger toBigInteger(byte[] in) {
byte[] out = new byte[in.length];
for (int i = 0; i < in.length; i++) {
out[i] = in[in.length-1-i];
}
return new BigInteger(1, out);
}
/**
* From the Ed25519 paper:
* x is negative if the (b-1)-bit encoding of x is lexicographically larger
* than the (b-1)-bit encoding of -x. If q is an odd prime and the encoding
* is the little-endian representation of {0, 1,..., q-1} then the negative
* elements of F_q are {1, 3, 5,..., q-2}.
* @return
*/
public boolean isNegative(FieldElement x) {
return ((BigIntegerFieldElement)x).bi.testBit(0);
}
}

View File

@ -0,0 +1,26 @@
package net.i2p.crypto.eddsa.math.bigint;
import java.math.BigInteger;
import net.i2p.crypto.eddsa.math.Field;
import net.i2p.crypto.eddsa.math.ScalarOps;
public class BigIntegerScalarOps implements ScalarOps {
private final BigInteger l;
private final BigIntegerLittleEndianEncoding enc;
public BigIntegerScalarOps(Field f, BigInteger l) {
this.l = l;
enc = new BigIntegerLittleEndianEncoding();
enc.setField(f);
}
public byte[] reduce(byte[] s) {
return enc.encode(enc.toBigInteger(s).mod(l));
}
public byte[] multiplyAndAdd(byte[] a, byte[] b, byte[] c) {
return enc.encode(enc.toBigInteger(a).multiply(enc.toBigInteger(b)).add(enc.toBigInteger(c)).mod(l));
}
}

View File

@ -0,0 +1,971 @@
package net.i2p.crypto.eddsa.math.ed25519;
import net.i2p.crypto.eddsa.TestUtils;
import net.i2p.crypto.eddsa.math.Field;
import net.i2p.crypto.eddsa.math.FieldElement;
/**
* An element t, entries t[0]...t[9], represents the integer
* t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
* Bounds on each t[i] vary depending on context.
*/
public class Ed25519FieldElement extends FieldElement {
/**
* Variable is package private for encoding.
*/
int[] t;
public Ed25519FieldElement(Field f, int[] t) {
super(f);
if (t.length != 10)
throw new IllegalArgumentException("Invalid radix-2^51 representation");
this.t = t;
}
private static final byte[] zero = new byte[32];
public boolean isNonZero() {
byte[] s = toByteArray();
int result = 0;
for (int i = 0; i < 32; i++) {
result |= s[i] ^ zero[i];
}
return result != 0;
}
/**
* h = f + g
* Can overlap h with f or g.
*
* Preconditions:
* |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
* |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*
* Postconditions:
* |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
*/
public FieldElement add(FieldElement val) {
int[] g = ((Ed25519FieldElement)val).t;
int f0 = t[0];
int f1 = t[1];
int f2 = t[2];
int f3 = t[3];
int f4 = t[4];
int f5 = t[5];
int f6 = t[6];
int f7 = t[7];
int f8 = t[8];
int f9 = t[9];
int g0 = g[0];
int g1 = g[1];
int g2 = g[2];
int g3 = g[3];
int g4 = g[4];
int g5 = g[5];
int g6 = g[6];
int g7 = g[7];
int g8 = g[8];
int g9 = g[9];
int h0 = f0 + g0;
int h1 = f1 + g1;
int h2 = f2 + g2;
int h3 = f3 + g3;
int h4 = f4 + g4;
int h5 = f5 + g5;
int h6 = f6 + g6;
int h7 = f7 + g7;
int h8 = f8 + g8;
int h9 = f9 + g9;
int[] h = new int[10];
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
return new Ed25519FieldElement(f, h);
}
/**
* h = f - g
* Can overlap h with f or g.
*
* Preconditions:
* |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
* |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*
* Postconditions:
* |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
**/
public FieldElement subtract(FieldElement val) {
int[] g = ((Ed25519FieldElement)val).t;
int f0 = t[0];
int f1 = t[1];
int f2 = t[2];
int f3 = t[3];
int f4 = t[4];
int f5 = t[5];
int f6 = t[6];
int f7 = t[7];
int f8 = t[8];
int f9 = t[9];
int g0 = g[0];
int g1 = g[1];
int g2 = g[2];
int g3 = g[3];
int g4 = g[4];
int g5 = g[5];
int g6 = g[6];
int g7 = g[7];
int g8 = g[8];
int g9 = g[9];
int h0 = f0 - g0;
int h1 = f1 - g1;
int h2 = f2 - g2;
int h3 = f3 - g3;
int h4 = f4 - g4;
int h5 = f5 - g5;
int h6 = f6 - g6;
int h7 = f7 - g7;
int h8 = f8 - g8;
int h9 = f9 - g9;
int[] h = new int[10];
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
return new Ed25519FieldElement(f, h);
}
/**
* h = -f
*
* Preconditions:
* |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*
* Postconditions:
* |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc.
*/
public FieldElement negate() {
int f0 = t[0];
int f1 = t[1];
int f2 = t[2];
int f3 = t[3];
int f4 = t[4];
int f5 = t[5];
int f6 = t[6];
int f7 = t[7];
int f8 = t[8];
int f9 = t[9];
int h0 = -f0;
int h1 = -f1;
int h2 = -f2;
int h3 = -f3;
int h4 = -f4;
int h5 = -f5;
int h6 = -f6;
int h7 = -f7;
int h8 = -f8;
int h9 = -f9;
int[] h = new int[10];
h[0] = h0;
h[1] = h1;
h[2] = h2;
h[3] = h3;
h[4] = h4;
h[5] = h5;
h[6] = h6;
h[7] = h7;
h[8] = h8;
h[9] = h9;
return new Ed25519FieldElement(f, h);
}
/**
* h = f * g Can overlap h with f or g.
*
* Preconditions: |f| bounded by
* 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. |g| bounded by
* 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
*
* Postconditions: |h| bounded by
* 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*
* Notes on implementation strategy:
*
* Using schoolbook multiplication. Karatsuba would save a little in some
* cost models.
*
* Most multiplications by 2 and 19 are 32-bit precomputations; cheaper than
* 64-bit postcomputations.
*
* There is one remaining multiplication by 19 in the carry chain; one *19
* precomputation can be merged into this, but the resulting data flow is
* considerably less clean.
*
* There are 12 carries below. 10 of them are 2-way parallelizable and
* vectorizable. Can get away with 11 carries, but then data flow is much
* deeper.
*
* With tighter constraints on inputs can squeeze carries into int32.
*/
public FieldElement multiply(FieldElement val) {
int[] g = ((Ed25519FieldElement)val).t;
int f0 = t[0];
int f1 = t[1];
int f2 = t[2];
int f3 = t[3];
int f4 = t[4];
int f5 = t[5];
int f6 = t[6];
int f7 = t[7];
int f8 = t[8];
int f9 = t[9];
int g0 = g[0];
int g1 = g[1];
int g2 = g[2];
int g3 = g[3];
int g4 = g[4];
int g5 = g[5];
int g6 = g[6];
int g7 = g[7];
int g8 = g[8];
int g9 = g[9];
int g1_19 = 19 * g1; /* 1.959375*2^29 */
int g2_19 = 19 * g2; /* 1.959375*2^30; still ok */
int g3_19 = 19 * g3;
int g4_19 = 19 * g4;
int g5_19 = 19 * g5;
int g6_19 = 19 * g6;
int g7_19 = 19 * g7;
int g8_19 = 19 * g8;
int g9_19 = 19 * g9;
int f1_2 = 2 * f1;
int f3_2 = 2 * f3;
int f5_2 = 2 * f5;
int f7_2 = 2 * f7;
int f9_2 = 2 * f9;
long f0g0 = f0 * (long) g0;
long f0g1 = f0 * (long) g1;
long f0g2 = f0 * (long) g2;
long f0g3 = f0 * (long) g3;
long f0g4 = f0 * (long) g4;
long f0g5 = f0 * (long) g5;
long f0g6 = f0 * (long) g6;
long f0g7 = f0 * (long) g7;
long f0g8 = f0 * (long) g8;
long f0g9 = f0 * (long) g9;
long f1g0 = f1 * (long) g0;
long f1g1_2 = f1_2 * (long) g1;
long f1g2 = f1 * (long) g2;
long f1g3_2 = f1_2 * (long) g3;
long f1g4 = f1 * (long) g4;
long f1g5_2 = f1_2 * (long) g5;
long f1g6 = f1 * (long) g6;
long f1g7_2 = f1_2 * (long) g7;
long f1g8 = f1 * (long) g8;
long f1g9_38 = f1_2 * (long) g9_19;
long f2g0 = f2 * (long) g0;
long f2g1 = f2 * (long) g1;
long f2g2 = f2 * (long) g2;
long f2g3 = f2 * (long) g3;
long f2g4 = f2 * (long) g4;
long f2g5 = f2 * (long) g5;
long f2g6 = f2 * (long) g6;
long f2g7 = f2 * (long) g7;
long f2g8_19 = f2 * (long) g8_19;
long f2g9_19 = f2 * (long) g9_19;
long f3g0 = f3 * (long) g0;
long f3g1_2 = f3_2 * (long) g1;
long f3g2 = f3 * (long) g2;
long f3g3_2 = f3_2 * (long) g3;
long f3g4 = f3 * (long) g4;
long f3g5_2 = f3_2 * (long) g5;
long f3g6 = f3 * (long) g6;
long f3g7_38 = f3_2 * (long) g7_19;
long f3g8_19 = f3 * (long) g8_19;
long f3g9_38 = f3_2 * (long) g9_19;
long f4g0 = f4 * (long) g0;
long f4g1 = f4 * (long) g1;
long f4g2 = f4 * (long) g2;
long f4g3 = f4 * (long) g3;
long f4g4 = f4 * (long) g4;
long f4g5 = f4 * (long) g5;
long f4g6_19 = f4 * (long) g6_19;
long f4g7_19 = f4 * (long) g7_19;
long f4g8_19 = f4 * (long) g8_19;
long f4g9_19 = f4 * (long) g9_19;
long f5g0 = f5 * (long) g0;
long f5g1_2 = f5_2 * (long) g1;
long f5g2 = f5 * (long) g2;
long f5g3_2 = f5_2 * (long) g3;
long f5g4 = f5 * (long) g4;
long f5g5_38 = f5_2 * (long) g5_19;
long f5g6_19 = f5 * (long) g6_19;
long f5g7_38 = f5_2 * (long) g7_19;
long f5g8_19 = f5 * (long) g8_19;
long f5g9_38 = f5_2 * (long) g9_19;
long f6g0 = f6 * (long) g0;
long f6g1 = f6 * (long) g1;
long f6g2 = f6 * (long) g2;
long f6g3 = f6 * (long) g3;
long f6g4_19 = f6 * (long) g4_19;
long f6g5_19 = f6 * (long) g5_19;
long f6g6_19 = f6 * (long) g6_19;
long f6g7_19 = f6 * (long) g7_19;
long f6g8_19 = f6 * (long) g8_19;
long f6g9_19 = f6 * (long) g9_19;
long f7g0 = f7 * (long) g0;
long f7g1_2 = f7_2 * (long) g1;
long f7g2 = f7 * (long) g2;
long f7g3_38 = f7_2 * (long) g3_19;
long f7g4_19 = f7 * (long) g4_19;
long f7g5_38 = f7_2 * (long) g5_19;
long f7g6_19 = f7 * (long) g6_19;
long f7g7_38 = f7_2 * (long) g7_19;
long f7g8_19 = f7 * (long) g8_19;
long f7g9_38 = f7_2 * (long) g9_19;
long f8g0 = f8 * (long) g0;
long f8g1 = f8 * (long) g1;
long f8g2_19 = f8 * (long) g2_19;
long f8g3_19 = f8 * (long) g3_19;
long f8g4_19 = f8 * (long) g4_19;
long f8g5_19 = f8 * (long) g5_19;
long f8g6_19 = f8 * (long) g6_19;
long f8g7_19 = f8 * (long) g7_19;
long f8g8_19 = f8 * (long) g8_19;
long f8g9_19 = f8 * (long) g9_19;
long f9g0 = f9 * (long) g0;
long f9g1_38 = f9_2 * (long) g1_19;
long f9g2_19 = f9 * (long) g2_19;
long f9g3_38 = f9_2 * (long) g3_19;
long f9g4_19 = f9 * (long) g4_19;
long f9g5_38 = f9_2 * (long) g5_19;
long f9g6_19 = f9 * (long) g6_19;
long f9g7_38 = f9_2 * (long) g7_19;
long f9g8_19 = f9 * (long) g8_19;
long f9g9_38 = f9_2 * (long) g9_19;
long h0 = f0g0+f1g9_38+f2g8_19+f3g7_38+f4g6_19+f5g5_38+f6g4_19+f7g3_38+f8g2_19+f9g1_38;
long h1 = f0g1+f1g0 +f2g9_19+f3g8_19+f4g7_19+f5g6_19+f6g5_19+f7g4_19+f8g3_19+f9g2_19;
long h2 = f0g2+f1g1_2 +f2g0 +f3g9_38+f4g8_19+f5g7_38+f6g6_19+f7g5_38+f8g4_19+f9g3_38;
long h3 = f0g3+f1g2 +f2g1 +f3g0 +f4g9_19+f5g8_19+f6g7_19+f7g6_19+f8g5_19+f9g4_19;
long h4 = f0g4+f1g3_2 +f2g2 +f3g1_2 +f4g0 +f5g9_38+f6g8_19+f7g7_38+f8g6_19+f9g5_38;
long h5 = f0g5+f1g4 +f2g3 +f3g2 +f4g1 +f5g0 +f6g9_19+f7g8_19+f8g7_19+f9g6_19;
long h6 = f0g6+f1g5_2 +f2g4 +f3g3_2 +f4g2 +f5g1_2 +f6g0 +f7g9_38+f8g8_19+f9g7_38;
long h7 = f0g7+f1g6 +f2g5 +f3g4 +f4g3 +f5g2 +f6g1 +f7g0 +f8g9_19+f9g8_19;
long h8 = f0g8+f1g7_2 +f2g6 +f3g5_2 +f4g4 +f5g3_2 +f6g2 +f7g1_2 +f8g0 +f9g9_38;
long h9 = f0g9+f1g8 +f2g7 +f3g6 +f4g5 +f5g4 +f6g3 +f7g2 +f8g1 +f9g0 ;
long carry0;
long carry1;
long carry2;
long carry3;
long carry4;
long carry5;
long carry6;
long carry7;
long carry8;
long carry9;
/*
|h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38))
i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8
|h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19))
i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9
*/
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
/* |h0| <= 2^25 */
/* |h4| <= 2^25 */
/* |h1| <= 1.71*2^59 */
/* |h5| <= 1.71*2^59 */
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
/* |h1| <= 2^24; from now on fits into int32 */
/* |h5| <= 2^24; from now on fits into int32 */
/* |h2| <= 1.41*2^60 */
/* |h6| <= 1.41*2^60 */
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
/* |h2| <= 2^25; from now on fits into int32 unchanged */
/* |h6| <= 2^25; from now on fits into int32 unchanged */
/* |h3| <= 1.71*2^59 */
/* |h7| <= 1.71*2^59 */
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
/* |h3| <= 2^24; from now on fits into int32 unchanged */
/* |h7| <= 2^24; from now on fits into int32 unchanged */
/* |h4| <= 1.72*2^34 */
/* |h8| <= 1.41*2^60 */
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
/* |h4| <= 2^25; from now on fits into int32 unchanged */
/* |h8| <= 2^25; from now on fits into int32 unchanged */
/* |h5| <= 1.01*2^24 */
/* |h9| <= 1.71*2^59 */
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
/* |h9| <= 2^24; from now on fits into int32 unchanged */
/* |h0| <= 1.1*2^39 */
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
/* |h0| <= 2^25; from now on fits into int32 unchanged */
/* |h1| <= 1.01*2^24 */
int[] h = new int[10];
h[0] = (int) h0;
h[1] = (int) h1;
h[2] = (int) h2;
h[3] = (int) h3;
h[4] = (int) h4;
h[5] = (int) h5;
h[6] = (int) h6;
h[7] = (int) h7;
h[8] = (int) h8;
h[9] = (int) h9;
return new Ed25519FieldElement(f, h);
}
/**
* h = f * f
* Can overlap h with f.
*
* Preconditions:
* |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
*
* Postconditions:
* |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*
* See {@link Ed25519FieldElement#multiply(FieldElement)} for discussion
* of implementation strategy.
*/
public FieldElement square() {
int f0 = t[0];
int f1 = t[1];
int f2 = t[2];
int f3 = t[3];
int f4 = t[4];
int f5 = t[5];
int f6 = t[6];
int f7 = t[7];
int f8 = t[8];
int f9 = t[9];
int f0_2 = 2 * f0;
int f1_2 = 2 * f1;
int f2_2 = 2 * f2;
int f3_2 = 2 * f3;
int f4_2 = 2 * f4;
int f5_2 = 2 * f5;
int f6_2 = 2 * f6;
int f7_2 = 2 * f7;
int f5_38 = 38 * f5; /* 1.959375*2^30 */
int f6_19 = 19 * f6; /* 1.959375*2^30 */
int f7_38 = 38 * f7; /* 1.959375*2^30 */
int f8_19 = 19 * f8; /* 1.959375*2^30 */
int f9_38 = 38 * f9; /* 1.959375*2^30 */
long f0f0 = f0 * (long) f0;
long f0f1_2 = f0_2 * (long) f1;
long f0f2_2 = f0_2 * (long) f2;
long f0f3_2 = f0_2 * (long) f3;
long f0f4_2 = f0_2 * (long) f4;
long f0f5_2 = f0_2 * (long) f5;
long f0f6_2 = f0_2 * (long) f6;
long f0f7_2 = f0_2 * (long) f7;
long f0f8_2 = f0_2 * (long) f8;
long f0f9_2 = f0_2 * (long) f9;
long f1f1_2 = f1_2 * (long) f1;
long f1f2_2 = f1_2 * (long) f2;
long f1f3_4 = f1_2 * (long) f3_2;
long f1f4_2 = f1_2 * (long) f4;
long f1f5_4 = f1_2 * (long) f5_2;
long f1f6_2 = f1_2 * (long) f6;
long f1f7_4 = f1_2 * (long) f7_2;
long f1f8_2 = f1_2 * (long) f8;
long f1f9_76 = f1_2 * (long) f9_38;
long f2f2 = f2 * (long) f2;
long f2f3_2 = f2_2 * (long) f3;
long f2f4_2 = f2_2 * (long) f4;
long f2f5_2 = f2_2 * (long) f5;
long f2f6_2 = f2_2 * (long) f6;
long f2f7_2 = f2_2 * (long) f7;
long f2f8_38 = f2_2 * (long) f8_19;
long f2f9_38 = f2 * (long) f9_38;
long f3f3_2 = f3_2 * (long) f3;
long f3f4_2 = f3_2 * (long) f4;
long f3f5_4 = f3_2 * (long) f5_2;
long f3f6_2 = f3_2 * (long) f6;
long f3f7_76 = f3_2 * (long) f7_38;
long f3f8_38 = f3_2 * (long) f8_19;
long f3f9_76 = f3_2 * (long) f9_38;
long f4f4 = f4 * (long) f4;
long f4f5_2 = f4_2 * (long) f5;
long f4f6_38 = f4_2 * (long) f6_19;
long f4f7_38 = f4 * (long) f7_38;
long f4f8_38 = f4_2 * (long) f8_19;
long f4f9_38 = f4 * (long) f9_38;
long f5f5_38 = f5 * (long) f5_38;
long f5f6_38 = f5_2 * (long) f6_19;
long f5f7_76 = f5_2 * (long) f7_38;
long f5f8_38 = f5_2 * (long) f8_19;
long f5f9_76 = f5_2 * (long) f9_38;
long f6f6_19 = f6 * (long) f6_19;
long f6f7_38 = f6 * (long) f7_38;
long f6f8_38 = f6_2 * (long) f8_19;
long f6f9_38 = f6 * (long) f9_38;
long f7f7_38 = f7 * (long) f7_38;
long f7f8_38 = f7_2 * (long) f8_19;
long f7f9_76 = f7_2 * (long) f9_38;
long f8f8_19 = f8 * (long) f8_19;
long f8f9_38 = f8 * (long) f9_38;
long f9f9_38 = f9 * (long) f9_38;
long h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
long h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
long h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
long h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
long h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
long h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
long h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
long h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
long h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
long h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
long carry0;
long carry1;
long carry2;
long carry3;
long carry4;
long carry5;
long carry6;
long carry7;
long carry8;
long carry9;
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
int[] h = new int[10];
h[0] = (int) h0;
h[1] = (int) h1;
h[2] = (int) h2;
h[3] = (int) h3;
h[4] = (int) h4;
h[5] = (int) h5;
h[6] = (int) h6;
h[7] = (int) h7;
h[8] = (int) h8;
h[9] = (int) h9;
return new Ed25519FieldElement(f, h);
}
/**
* h = 2 * f * f
* Can overlap h with f.
*
* Preconditions:
* |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc.
*
* Postconditions:
* |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc.
*
* See {@link Ed25519FieldElement#multiply(FieldElement)} for discussion
* of implementation strategy.
*/
public FieldElement squareAndDouble() {
int f0 = t[0];
int f1 = t[1];
int f2 = t[2];
int f3 = t[3];
int f4 = t[4];
int f5 = t[5];
int f6 = t[6];
int f7 = t[7];
int f8 = t[8];
int f9 = t[9];
int f0_2 = 2 * f0;
int f1_2 = 2 * f1;
int f2_2 = 2 * f2;
int f3_2 = 2 * f3;
int f4_2 = 2 * f4;
int f5_2 = 2 * f5;
int f6_2 = 2 * f6;
int f7_2 = 2 * f7;
int f5_38 = 38 * f5; /* 1.959375*2^30 */
int f6_19 = 19 * f6; /* 1.959375*2^30 */
int f7_38 = 38 * f7; /* 1.959375*2^30 */
int f8_19 = 19 * f8; /* 1.959375*2^30 */
int f9_38 = 38 * f9; /* 1.959375*2^30 */
long f0f0 = f0 * (long) f0;
long f0f1_2 = f0_2 * (long) f1;
long f0f2_2 = f0_2 * (long) f2;
long f0f3_2 = f0_2 * (long) f3;
long f0f4_2 = f0_2 * (long) f4;
long f0f5_2 = f0_2 * (long) f5;
long f0f6_2 = f0_2 * (long) f6;
long f0f7_2 = f0_2 * (long) f7;
long f0f8_2 = f0_2 * (long) f8;
long f0f9_2 = f0_2 * (long) f9;
long f1f1_2 = f1_2 * (long) f1;
long f1f2_2 = f1_2 * (long) f2;
long f1f3_4 = f1_2 * (long) f3_2;
long f1f4_2 = f1_2 * (long) f4;
long f1f5_4 = f1_2 * (long) f5_2;
long f1f6_2 = f1_2 * (long) f6;
long f1f7_4 = f1_2 * (long) f7_2;
long f1f8_2 = f1_2 * (long) f8;
long f1f9_76 = f1_2 * (long) f9_38;
long f2f2 = f2 * (long) f2;
long f2f3_2 = f2_2 * (long) f3;
long f2f4_2 = f2_2 * (long) f4;
long f2f5_2 = f2_2 * (long) f5;
long f2f6_2 = f2_2 * (long) f6;
long f2f7_2 = f2_2 * (long) f7;
long f2f8_38 = f2_2 * (long) f8_19;
long f2f9_38 = f2 * (long) f9_38;
long f3f3_2 = f3_2 * (long) f3;
long f3f4_2 = f3_2 * (long) f4;
long f3f5_4 = f3_2 * (long) f5_2;
long f3f6_2 = f3_2 * (long) f6;
long f3f7_76 = f3_2 * (long) f7_38;
long f3f8_38 = f3_2 * (long) f8_19;
long f3f9_76 = f3_2 * (long) f9_38;
long f4f4 = f4 * (long) f4;
long f4f5_2 = f4_2 * (long) f5;
long f4f6_38 = f4_2 * (long) f6_19;
long f4f7_38 = f4 * (long) f7_38;
long f4f8_38 = f4_2 * (long) f8_19;
long f4f9_38 = f4 * (long) f9_38;
long f5f5_38 = f5 * (long) f5_38;
long f5f6_38 = f5_2 * (long) f6_19;
long f5f7_76 = f5_2 * (long) f7_38;
long f5f8_38 = f5_2 * (long) f8_19;
long f5f9_76 = f5_2 * (long) f9_38;
long f6f6_19 = f6 * (long) f6_19;
long f6f7_38 = f6 * (long) f7_38;
long f6f8_38 = f6_2 * (long) f8_19;
long f6f9_38 = f6 * (long) f9_38;
long f7f7_38 = f7 * (long) f7_38;
long f7f8_38 = f7_2 * (long) f8_19;
long f7f9_76 = f7_2 * (long) f9_38;
long f8f8_19 = f8 * (long) f8_19;
long f8f9_38 = f8 * (long) f9_38;
long f9f9_38 = f9 * (long) f9_38;
long h0 = f0f0 +f1f9_76+f2f8_38+f3f7_76+f4f6_38+f5f5_38;
long h1 = f0f1_2+f2f9_38+f3f8_38+f4f7_38+f5f6_38;
long h2 = f0f2_2+f1f1_2 +f3f9_76+f4f8_38+f5f7_76+f6f6_19;
long h3 = f0f3_2+f1f2_2 +f4f9_38+f5f8_38+f6f7_38;
long h4 = f0f4_2+f1f3_4 +f2f2 +f5f9_76+f6f8_38+f7f7_38;
long h5 = f0f5_2+f1f4_2 +f2f3_2 +f6f9_38+f7f8_38;
long h6 = f0f6_2+f1f5_4 +f2f4_2 +f3f3_2 +f7f9_76+f8f8_19;
long h7 = f0f7_2+f1f6_2 +f2f5_2 +f3f4_2 +f8f9_38;
long h8 = f0f8_2+f1f7_4 +f2f6_2 +f3f5_4 +f4f4 +f9f9_38;
long h9 = f0f9_2+f1f8_2 +f2f7_2 +f3f6_2 +f4f5_2;
long carry0;
long carry1;
long carry2;
long carry3;
long carry4;
long carry5;
long carry6;
long carry7;
long carry8;
long carry9;
h0 += h0;
h1 += h1;
h2 += h2;
h3 += h3;
h4 += h4;
h5 += h5;
h6 += h6;
h7 += h7;
h8 += h8;
h9 += h9;
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
int[] h = new int[10];
h[0] = (int) h0;
h[1] = (int) h1;
h[2] = (int) h2;
h[3] = (int) h3;
h[4] = (int) h4;
h[5] = (int) h5;
h[6] = (int) h6;
h[7] = (int) h7;
h[8] = (int) h8;
h[9] = (int) h9;
return new Ed25519FieldElement(f, h);
}
public FieldElement invert() {
FieldElement t0, t1, t2, t3;
// z2 = z1^2^1
t0 = square();
for (int i = 1; i < 1; ++i) { // Don't remove this
t0 = t0.square();
}
// z8 = z2^2^2;
t1 = t0.square();
for (int i = 1; i < 2; ++i) {
t1 = t1.square();
}
// z9 = z1*z8
t1 = multiply(t1);
// z11 = z2*z9
t0 = t0.multiply(t1);
// z22 = z11^2^1
t2 = t0.square();
for (int i = 1; i < 1; ++i) { // Don't remove this
t2 = t2.square();
}
// z_5_0 = z9*z22
t1 = t1.multiply(t2);
// z_10_5 = z_5_0^2^5
t2 = t1.square();
for (int i = 1; i < 5; ++i) {
t2 = t2.square();
}
// z_10_0 = z_10_5*z_5_0
t1 = t2.multiply(t1);
// z_20_10 = z_10_0^2^10
t2 = t1.square();
for (int i = 1; i < 10; ++i) {
t2 = t2.square();
}
// z_20_0 = z_20_10*z_10_0
t2 = t2.multiply(t1);
// z_40_20 = z_20_0^2^20
t3 = t2.square();
for (int i = 1; i < 20; ++i) {
t3 = t3.square();
}
// z_40_0 = z_40_20*z_20_0
t2 = t3.multiply(t2);
// z_50_10 = z_40_0^2^10
t2 = t2.square();
for (int i = 1; i < 10; ++i) {
t2 = t2.square();
}
// z_50_0 = z_50_10*z_10_0
t1 = t2.multiply(t1);
// z_100_50 = z_50_0^2^50
t2 = t1.square();
for (int i = 1; i < 50; ++i) {
t2 = t2.square();
}
// z_100_0 = z_100_50*z_50_0
t2 = t2.multiply(t1);
// z_200_100 = z_100_0^2^100
t3 = t2.square();
for (int i = 1; i < 100; ++i) {
t3 = t3.square();
}
// z_200_0 = z_200_100*z_100_0
t2 = t3.multiply(t2);
// z_250_50 = z_200_0^2^50
t2 = t2.square();
for (int i = 1; i < 50; ++i) {
t2 = t2.square();
}
// z_250_0 = z_250_50*z_50_0
t1 = t2.multiply(t1);
// z_255_5 = z_250_0^2^5
t1 = t1.square();
for (int i = 1; i < 5; ++i) {
t1 = t1.square();
}
// z_255_21 = z_255_5*z11
return t1.multiply(t0);
}
public FieldElement pow22523() {
FieldElement t0, t1, t2;
// z2 = z1^2^1
t0 = square();
for (int i = 1; i < 1; ++i) { // Don't remove this
t0 = t0.square();
}
// z8 = z2^2^2;
t1 = t0.square();
for (int i = 1; i < 2; ++i) {
t1 = t1.square();
}
// z9 = z1*z8
t1 = multiply(t1);
// z11 = z2*z9
t0 = t0.multiply(t1);
// z22 = z11^2^1
t0 = t0.square();
for (int i = 1; i < 1; ++i) { // Don't remove this
t0 = t0.square();
}
// z_5_0 = z9*z22
t0 = t1.multiply(t0);
// z_10_5 = z_5_0^2^5
t1 = t0.square();
for (int i = 1; i < 5; ++i) {
t1 = t1.square();
}
// z_10_0 = z_10_5*z_5_0
t0 = t1.multiply(t0);
// z_20_10 = z_10_0^2^10
t1 = t0.square();
for (int i = 1; i < 10; ++i) {
t1 = t1.square();
}
// z_20_0 = z_20_10*z_10_0
t1 = t1.multiply(t0);
// z_40_20 = z_20_0^2^20
t2 = t1.square();
for (int i = 1; i < 20; ++i) {
t2 = t2.square();
}
// z_40_0 = z_40_20*z_20_0
t1 = t2.multiply(t1);
// z_50_10 = z_40_0^2^10
t1 = t1.square();
for (int i = 1; i < 10; ++i) {
t1 = t1.square();
}
// z_50_0 = z_50_10*z_10_0
t0 = t1.multiply(t0);
// z_100_50 = z_50_0^2^50
t1 = t0.square();
for (int i = 1; i < 50; ++i) {
t1 = t1.square();
}
// z_100_0 = z_100_50*z_50_0
t1 = t1.multiply(t0);
// z_200_100 = z_100_0^2^100
t2 = t1.square();
for (int i = 1; i < 100; ++i) {
t2 = t2.square();
}
// z_200_0 = z_200_100*z_100_0
t1 = t2.multiply(t1);
// z_250_50 = z_200_0^2^50
t1 = t1.square();
for (int i = 1; i < 50; ++i) {
t1 = t1.square();
}
// z_250_0 = z_250_50*z_50_0
t0 = t1.multiply(t0);
// z_252_2 = z_250_0^2^2
t0 = t0.square();
for (int i = 1; i < 2; ++i) {
t0 = t0.square();
}
// z_252_3 = z_252_2*z1
return multiply(t0);
}
@Override
public int hashCode() {
return t.hashCode(); // TODO should this be something else?
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Ed25519FieldElement))
return false;
Ed25519FieldElement fe = (Ed25519FieldElement) obj;
// XXX why does direct byte[] comparison fail?
// TODO should this be constant time?
return TestUtils.getHex(toByteArray()).equals(TestUtils.getHex(fe.toByteArray()));
}
@Override
public String toString() {
return "[Ed25519FieldElement val="+TestUtils.getHex(toByteArray())+"]";
}
}

View File

@ -0,0 +1,205 @@
package net.i2p.crypto.eddsa.math.ed25519;
import net.i2p.crypto.eddsa.math.Encoding;
import net.i2p.crypto.eddsa.math.FieldElement;
public class Ed25519LittleEndianEncoding extends Encoding {
/**
* Preconditions:<br>
* |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.<br><br>
*
* Write p=2^255-19; q=floor(h/p).<br>
* Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))).
* <br><br>
* Proof:<br>
* Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4.<br>
* Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4.
* <br><br>
* Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9).<br>
* Then 0 < y < 1.
* <br><br>
* Write r=h-pq.<br>
* Have 0 <= r <= p-1=2^255-20.<br>
* Thus 0 <= r+19(2^-255)r < r+19(2^-255)2^255 <= 2^255-1.
* <br><br>
* Write x=r+19(2^-255)r+y.<br>
* Then 0 < x < 2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q.
* <br><br>
* Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1))<br>
* so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q.
*/
public byte[] encode(FieldElement x) {
int[] h = ((Ed25519FieldElement)x).t;
int h0 = h[0];
int h1 = h[1];
int h2 = h[2];
int h3 = h[3];
int h4 = h[4];
int h5 = h[5];
int h6 = h[6];
int h7 = h[7];
int h8 = h[8];
int h9 = h[9];
int q;
int carry0;
int carry1;
int carry2;
int carry3;
int carry4;
int carry5;
int carry6;
int carry7;
int carry8;
int carry9;
q = (19 * h9 + (((int) 1) << 24)) >> 25;
q = (h0 + q) >> 26;
q = (h1 + q) >> 25;
q = (h2 + q) >> 26;
q = (h3 + q) >> 25;
q = (h4 + q) >> 26;
q = (h5 + q) >> 25;
q = (h6 + q) >> 26;
q = (h7 + q) >> 25;
q = (h8 + q) >> 26;
q = (h9 + q) >> 25;
/* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */
h0 += 19 * q;
/* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */
carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26;
carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25;
carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26;
carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25;
carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26;
carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25;
carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26;
carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25;
carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26;
carry9 = h9 >> 25; h9 -= carry9 << 25;
/* h10 = carry9 */
/*
Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20.
Have h0+...+2^230 h9 between 0 and 2^255-1;
evidently 2^255 h10-2^255 q = 0.
Goal: Output h0+...+2^230 h9.
*/
byte[] s = new byte[32];
s[0] = (byte) (h0 >> 0);
s[1] = (byte) (h0 >> 8);
s[2] = (byte) (h0 >> 16);
s[3] = (byte) ((h0 >> 24) | (h1 << 2));
s[4] = (byte) (h1 >> 6);
s[5] = (byte) (h1 >> 14);
s[6] = (byte) ((h1 >> 22) | (h2 << 3));
s[7] = (byte) (h2 >> 5);
s[8] = (byte) (h2 >> 13);
s[9] = (byte) ((h2 >> 21) | (h3 << 5));
s[10] = (byte) (h3 >> 3);
s[11] = (byte) (h3 >> 11);
s[12] = (byte) ((h3 >> 19) | (h4 << 6));
s[13] = (byte) (h4 >> 2);
s[14] = (byte) (h4 >> 10);
s[15] = (byte) (h4 >> 18);
s[16] = (byte) (h5 >> 0);
s[17] = (byte) (h5 >> 8);
s[18] = (byte) (h5 >> 16);
s[19] = (byte) ((h5 >> 24) | (h6 << 1));
s[20] = (byte) (h6 >> 7);
s[21] = (byte) (h6 >> 15);
s[22] = (byte) ((h6 >> 23) | (h7 << 3));
s[23] = (byte) (h7 >> 5);
s[24] = (byte) (h7 >> 13);
s[25] = (byte) ((h7 >> 21) | (h8 << 4));
s[26] = (byte) (h8 >> 4);
s[27] = (byte) (h8 >> 12);
s[28] = (byte) ((h8 >> 20) | (h9 << 6));
s[29] = (byte) (h9 >> 2);
s[30] = (byte) (h9 >> 10);
s[31] = (byte) (h9 >> 18);
return s;
}
private static long load_3(byte[] in, int offset) {
int result = in[offset++] & 0xff;
result |= (in[offset++] & 0xff) << 8;
result |= (in[offset] & 0xff) << 16;
return result;
}
private static long load_4(byte[] in, int offset) {
int result = in[offset++] & 0xff;
result |= (in[offset++] & 0xff) << 8;
result |= (in[offset++] & 0xff) << 16;
result |= in[offset] << 24;
return ((long)result) & 0xffffffffL;
}
/**
* Ignores top bit.
*/
public FieldElement decode(byte[] in) {
long h0 = load_4(in, 0);
long h1 = load_3(in, 4) << 6;
long h2 = load_3(in, 7) << 5;
long h3 = load_3(in, 10) << 3;
long h4 = load_3(in, 13) << 2;
long h5 = load_4(in, 16);
long h6 = load_3(in, 20) << 7;
long h7 = load_3(in, 23) << 5;
long h8 = load_3(in, 26) << 4;
long h9 = (load_3(in, 29) & 8388607) << 2;
long carry0;
long carry1;
long carry2;
long carry3;
long carry4;
long carry5;
long carry6;
long carry7;
long carry8;
long carry9;
carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
int[] h = new int[10];
h[0] = (int) h0;
h[1] = (int) h1;
h[2] = (int) h2;
h[3] = (int) h3;
h[4] = (int) h4;
h[5] = (int) h5;
h[6] = (int) h6;
h[7] = (int) h7;
h[8] = (int) h8;
h[9] = (int) h9;
return new Ed25519FieldElement(f, h);
}
/**
* Return true if x is in {1,3,5,...,q-2}<br>
* Return false if x is in {0,2,4,...,q-1}<br><br>
*
* Preconditions:<br>
* |x| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc.
* @return true if x is in {1,3,5,...,q-2}, false otherwise.
*/
public boolean isNegative(FieldElement x) {
byte[] s = encode(x);
return (s[0] & 1) != 0;
}
}

View File

@ -0,0 +1,617 @@
package net.i2p.crypto.eddsa.math.ed25519;
import net.i2p.crypto.eddsa.math.ScalarOps;
public class Ed25519ScalarOps implements ScalarOps {
private static long load_3(byte[] in, int offset) {
int result = in[offset++] & 0xff;
result |= (in[offset++] & 0xff) << 8;
result |= (in[offset] & 0xff) << 16;
return result;
}
private static long load_4(byte[] in, int offset) {
int result = in[offset++] & 0xff;
result |= (in[offset++] & 0xff) << 8;
result |= (in[offset++] & 0xff) << 16;
result |= in[offset] << 24;
return ((long)result) & 0xffffffffL;
}
/**
* Input:<br>
* s[0]+256*s[1]+...+256^63*s[63] = s<br><br>
*
* Output:<br>
* s[0]+256*s[1]+...+256^31*s[31] = s mod l<br>
* where l = 2^252 + 27742317777372353535851937790883648493.
*/
public byte[] reduce(byte[] s) {
long s0 = 2097151 & load_3(s, 0);
long s1 = 2097151 & (load_4(s, 2) >> 5);
long s2 = 2097151 & (load_3(s, 5) >> 2);
long s3 = 2097151 & (load_4(s, 7) >> 7);
long s4 = 2097151 & (load_4(s, 10) >> 4);
long s5 = 2097151 & (load_3(s, 13) >> 1);
long s6 = 2097151 & (load_4(s, 15) >> 6);
long s7 = 2097151 & (load_3(s, 18) >> 3);
long s8 = 2097151 & load_3(s, 21);
long s9 = 2097151 & (load_4(s, 23) >> 5);
long s10 = 2097151 & (load_3(s, 26) >> 2);
long s11 = 2097151 & (load_4(s, 28) >> 7);
long s12 = 2097151 & (load_4(s, 31) >> 4);
long s13 = 2097151 & (load_3(s, 34) >> 1);
long s14 = 2097151 & (load_4(s, 36) >> 6);
long s15 = 2097151 & (load_3(s, 39) >> 3);
long s16 = 2097151 & load_3(s, 42);
long s17 = 2097151 & (load_4(s, 44) >> 5);
long s18 = 2097151 & (load_3(s, 47) >> 2);
long s19 = 2097151 & (load_4(s, 49) >> 7);
long s20 = 2097151 & (load_4(s, 52) >> 4);
long s21 = 2097151 & (load_3(s, 55) >> 1);
long s22 = 2097151 & (load_4(s, 57) >> 6);
long s23 = (load_4(s, 60) >> 3);
long carry0;
long carry1;
long carry2;
long carry3;
long carry4;
long carry5;
long carry6;
long carry7;
long carry8;
long carry9;
long carry10;
long carry11;
long carry12;
long carry13;
long carry14;
long carry15;
long carry16;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s23 = 0;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s22 = 0;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s21 = 0;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s20 = 0;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s19 = 0;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
s18 = 0;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s17 = 0;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s16 = 0;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s15 = 0;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s14 = 0;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s13 = 0;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
byte[] result = new byte[32];
result[0] = (byte) (s0 >> 0);
result[1] = (byte) (s0 >> 8);
result[2] = (byte) ((s0 >> 16) | (s1 << 5));
result[3] = (byte) (s1 >> 3);
result[4] = (byte) (s1 >> 11);
result[5] = (byte) ((s1 >> 19) | (s2 << 2));
result[6] = (byte) (s2 >> 6);
result[7] = (byte) ((s2 >> 14) | (s3 << 7));
result[8] = (byte) (s3 >> 1);
result[9] = (byte) (s3 >> 9);
result[10] = (byte) ((s3 >> 17) | (s4 << 4));
result[11] = (byte) (s4 >> 4);
result[12] = (byte) (s4 >> 12);
result[13] = (byte) ((s4 >> 20) | (s5 << 1));
result[14] = (byte) (s5 >> 7);
result[15] = (byte) ((s5 >> 15) | (s6 << 6));
result[16] = (byte) (s6 >> 2);
result[17] = (byte) (s6 >> 10);
result[18] = (byte) ((s6 >> 18) | (s7 << 3));
result[19] = (byte) (s7 >> 5);
result[20] = (byte) (s7 >> 13);
result[21] = (byte) (s8 >> 0);
result[22] = (byte) (s8 >> 8);
result[23] = (byte) ((s8 >> 16) | (s9 << 5));
result[24] = (byte) (s9 >> 3);
result[25] = (byte) (s9 >> 11);
result[26] = (byte) ((s9 >> 19) | (s10 << 2));
result[27] = (byte) (s10 >> 6);
result[28] = (byte) ((s10 >> 14) | (s11 << 7));
result[29] = (byte) (s11 >> 1);
result[30] = (byte) (s11 >> 9);
result[31] = (byte) (s11 >> 17);
return result;
}
/**
* Input:<br>
* a[0]+256*a[1]+...+256^31*a[31] = a<br>
* b[0]+256*b[1]+...+256^31*b[31] = b<br>
* c[0]+256*c[1]+...+256^31*c[31] = c<br><br>
*
* Output:<br>
* result[0]+256*result[1]+...+256^31*result[31] = (ab+c) mod l<br>
* where l = 2^252 + 27742317777372353535851937790883648493.
*/
public byte[] multiplyAndAdd(byte[] a, byte[] b, byte[] c) {
long a0 = 2097151 & load_3(a, 0);
long a1 = 2097151 & (load_4(a, 2) >> 5);
long a2 = 2097151 & (load_3(a, 5) >> 2);
long a3 = 2097151 & (load_4(a, 7) >> 7);
long a4 = 2097151 & (load_4(a, 10) >> 4);
long a5 = 2097151 & (load_3(a, 13) >> 1);
long a6 = 2097151 & (load_4(a, 15) >> 6);
long a7 = 2097151 & (load_3(a, 18) >> 3);
long a8 = 2097151 & load_3(a, 21);
long a9 = 2097151 & (load_4(a, 23) >> 5);
long a10 = 2097151 & (load_3(a, 26) >> 2);
long a11 = (load_4(a, 28) >> 7);
long b0 = 2097151 & load_3(b, 0);
long b1 = 2097151 & (load_4(b, 2) >> 5);
long b2 = 2097151 & (load_3(b, 5) >> 2);
long b3 = 2097151 & (load_4(b, 7) >> 7);
long b4 = 2097151 & (load_4(b, 10) >> 4);
long b5 = 2097151 & (load_3(b, 13) >> 1);
long b6 = 2097151 & (load_4(b, 15) >> 6);
long b7 = 2097151 & (load_3(b, 18) >> 3);
long b8 = 2097151 & load_3(b, 21);
long b9 = 2097151 & (load_4(b, 23) >> 5);
long b10 = 2097151 & (load_3(b, 26) >> 2);
long b11 = (load_4(b, 28) >> 7);
long c0 = 2097151 & load_3(c, 0);
long c1 = 2097151 & (load_4(c, 2) >> 5);
long c2 = 2097151 & (load_3(c, 5) >> 2);
long c3 = 2097151 & (load_4(c, 7) >> 7);
long c4 = 2097151 & (load_4(c, 10) >> 4);
long c5 = 2097151 & (load_3(c, 13) >> 1);
long c6 = 2097151 & (load_4(c, 15) >> 6);
long c7 = 2097151 & (load_3(c, 18) >> 3);
long c8 = 2097151 & load_3(c, 21);
long c9 = 2097151 & (load_4(c, 23) >> 5);
long c10 = 2097151 & (load_3(c, 26) >> 2);
long c11 = (load_4(c, 28) >> 7);
long s0;
long s1;
long s2;
long s3;
long s4;
long s5;
long s6;
long s7;
long s8;
long s9;
long s10;
long s11;
long s12;
long s13;
long s14;
long s15;
long s16;
long s17;
long s18;
long s19;
long s20;
long s21;
long s22;
long s23;
long carry0;
long carry1;
long carry2;
long carry3;
long carry4;
long carry5;
long carry6;
long carry7;
long carry8;
long carry9;
long carry10;
long carry11;
long carry12;
long carry13;
long carry14;
long carry15;
long carry16;
long carry17;
long carry18;
long carry19;
long carry20;
long carry21;
long carry22;
s0 = c0 + a0*b0;
s1 = c1 + a0*b1 + a1*b0;
s2 = c2 + a0*b2 + a1*b1 + a2*b0;
s3 = c3 + a0*b3 + a1*b2 + a2*b1 + a3*b0;
s4 = c4 + a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0;
s5 = c5 + a0*b5 + a1*b4 + a2*b3 + a3*b2 + a4*b1 + a5*b0;
s6 = c6 + a0*b6 + a1*b5 + a2*b4 + a3*b3 + a4*b2 + a5*b1 + a6*b0;
s7 = c7 + a0*b7 + a1*b6 + a2*b5 + a3*b4 + a4*b3 + a5*b2 + a6*b1 + a7*b0;
s8 = c8 + a0*b8 + a1*b7 + a2*b6 + a3*b5 + a4*b4 + a5*b3 + a6*b2 + a7*b1 + a8*b0;
s9 = c9 + a0*b9 + a1*b8 + a2*b7 + a3*b6 + a4*b5 + a5*b4 + a6*b3 + a7*b2 + a8*b1 + a9*b0;
s10 = c10 + a0*b10 + a1*b9 + a2*b8 + a3*b7 + a4*b6 + a5*b5 + a6*b4 + a7*b3 + a8*b2 + a9*b1 + a10*b0;
s11 = c11 + a0*b11 + a1*b10 + a2*b9 + a3*b8 + a4*b7 + a5*b6 + a6*b5 + a7*b4 + a8*b3 + a9*b2 + a10*b1 + a11*b0;
s12 = a1*b11 + a2*b10 + a3*b9 + a4*b8 + a5*b7 + a6*b6 + a7*b5 + a8*b4 + a9*b3 + a10*b2 + a11*b1;
s13 = a2*b11 + a3*b10 + a4*b9 + a5*b8 + a6*b7 + a7*b6 + a8*b5 + a9*b4 + a10*b3 + a11*b2;
s14 = a3*b11 + a4*b10 + a5*b9 + a6*b8 + a7*b7 + a8*b6 + a9*b5 + a10*b4 + a11*b3;
s15 = a4*b11 + a5*b10 + a6*b9 + a7*b8 + a8*b7 + a9*b6 + a10*b5 + a11*b4;
s16 = a5*b11 + a6*b10 + a7*b9 + a8*b8 + a9*b7 + a10*b6 + a11*b5;
s17 = a6*b11 + a7*b10 + a8*b9 + a9*b8 + a10*b7 + a11*b6;
s18 = a7*b11 + a8*b10 + a9*b9 + a10*b8 + a11*b7;
s19 = a8*b11 + a9*b10 + a10*b9 + a11*b8;
s20 = a9*b11 + a10*b10 + a11*b9;
s21 = a10*b11 + a11*b10;
s22 = a11*b11;
s23 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry18 = (s18 + (1<<20)) >> 21; s19 += carry18; s18 -= carry18 << 21;
carry20 = (s20 + (1<<20)) >> 21; s21 += carry20; s20 -= carry20 << 21;
carry22 = (s22 + (1<<20)) >> 21; s23 += carry22; s22 -= carry22 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
carry17 = (s17 + (1<<20)) >> 21; s18 += carry17; s17 -= carry17 << 21;
carry19 = (s19 + (1<<20)) >> 21; s20 += carry19; s19 -= carry19 << 21;
carry21 = (s21 + (1<<20)) >> 21; s22 += carry21; s21 -= carry21 << 21;
s11 += s23 * 666643;
s12 += s23 * 470296;
s13 += s23 * 654183;
s14 -= s23 * 997805;
s15 += s23 * 136657;
s16 -= s23 * 683901;
s23 = 0;
s10 += s22 * 666643;
s11 += s22 * 470296;
s12 += s22 * 654183;
s13 -= s22 * 997805;
s14 += s22 * 136657;
s15 -= s22 * 683901;
s22 = 0;
s9 += s21 * 666643;
s10 += s21 * 470296;
s11 += s21 * 654183;
s12 -= s21 * 997805;
s13 += s21 * 136657;
s14 -= s21 * 683901;
s21 = 0;
s8 += s20 * 666643;
s9 += s20 * 470296;
s10 += s20 * 654183;
s11 -= s20 * 997805;
s12 += s20 * 136657;
s13 -= s20 * 683901;
s20 = 0;
s7 += s19 * 666643;
s8 += s19 * 470296;
s9 += s19 * 654183;
s10 -= s19 * 997805;
s11 += s19 * 136657;
s12 -= s19 * 683901;
s19 = 0;
s6 += s18 * 666643;
s7 += s18 * 470296;
s8 += s18 * 654183;
s9 -= s18 * 997805;
s10 += s18 * 136657;
s11 -= s18 * 683901;
s18 = 0;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry12 = (s12 + (1<<20)) >> 21; s13 += carry12; s12 -= carry12 << 21;
carry14 = (s14 + (1<<20)) >> 21; s15 += carry14; s14 -= carry14 << 21;
carry16 = (s16 + (1<<20)) >> 21; s17 += carry16; s16 -= carry16 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
carry13 = (s13 + (1<<20)) >> 21; s14 += carry13; s13 -= carry13 << 21;
carry15 = (s15 + (1<<20)) >> 21; s16 += carry15; s15 -= carry15 << 21;
s5 += s17 * 666643;
s6 += s17 * 470296;
s7 += s17 * 654183;
s8 -= s17 * 997805;
s9 += s17 * 136657;
s10 -= s17 * 683901;
s17 = 0;
s4 += s16 * 666643;
s5 += s16 * 470296;
s6 += s16 * 654183;
s7 -= s16 * 997805;
s8 += s16 * 136657;
s9 -= s16 * 683901;
s16 = 0;
s3 += s15 * 666643;
s4 += s15 * 470296;
s5 += s15 * 654183;
s6 -= s15 * 997805;
s7 += s15 * 136657;
s8 -= s15 * 683901;
s15 = 0;
s2 += s14 * 666643;
s3 += s14 * 470296;
s4 += s14 * 654183;
s5 -= s14 * 997805;
s6 += s14 * 136657;
s7 -= s14 * 683901;
s14 = 0;
s1 += s13 * 666643;
s2 += s13 * 470296;
s3 += s13 * 654183;
s4 -= s13 * 997805;
s5 += s13 * 136657;
s6 -= s13 * 683901;
s13 = 0;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = (s0 + (1<<20)) >> 21; s1 += carry0; s0 -= carry0 << 21;
carry2 = (s2 + (1<<20)) >> 21; s3 += carry2; s2 -= carry2 << 21;
carry4 = (s4 + (1<<20)) >> 21; s5 += carry4; s4 -= carry4 << 21;
carry6 = (s6 + (1<<20)) >> 21; s7 += carry6; s6 -= carry6 << 21;
carry8 = (s8 + (1<<20)) >> 21; s9 += carry8; s8 -= carry8 << 21;
carry10 = (s10 + (1<<20)) >> 21; s11 += carry10; s10 -= carry10 << 21;
carry1 = (s1 + (1<<20)) >> 21; s2 += carry1; s1 -= carry1 << 21;
carry3 = (s3 + (1<<20)) >> 21; s4 += carry3; s3 -= carry3 << 21;
carry5 = (s5 + (1<<20)) >> 21; s6 += carry5; s5 -= carry5 << 21;
carry7 = (s7 + (1<<20)) >> 21; s8 += carry7; s7 -= carry7 << 21;
carry9 = (s9 + (1<<20)) >> 21; s10 += carry9; s9 -= carry9 << 21;
carry11 = (s11 + (1<<20)) >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
carry11 = s11 >> 21; s12 += carry11; s11 -= carry11 << 21;
s0 += s12 * 666643;
s1 += s12 * 470296;
s2 += s12 * 654183;
s3 -= s12 * 997805;
s4 += s12 * 136657;
s5 -= s12 * 683901;
s12 = 0;
carry0 = s0 >> 21; s1 += carry0; s0 -= carry0 << 21;
carry1 = s1 >> 21; s2 += carry1; s1 -= carry1 << 21;
carry2 = s2 >> 21; s3 += carry2; s2 -= carry2 << 21;
carry3 = s3 >> 21; s4 += carry3; s3 -= carry3 << 21;
carry4 = s4 >> 21; s5 += carry4; s4 -= carry4 << 21;
carry5 = s5 >> 21; s6 += carry5; s5 -= carry5 << 21;
carry6 = s6 >> 21; s7 += carry6; s6 -= carry6 << 21;
carry7 = s7 >> 21; s8 += carry7; s7 -= carry7 << 21;
carry8 = s8 >> 21; s9 += carry8; s8 -= carry8 << 21;
carry9 = s9 >> 21; s10 += carry9; s9 -= carry9 << 21;
carry10 = s10 >> 21; s11 += carry10; s10 -= carry10 << 21;
byte[] result = new byte[32];
result[0] = (byte) (s0 >> 0);
result[1] = (byte) (s0 >> 8);
result[2] = (byte) ((s0 >> 16) | (s1 << 5));
result[3] = (byte) (s1 >> 3);
result[4] = (byte) (s1 >> 11);
result[5] = (byte) ((s1 >> 19) | (s2 << 2));
result[6] = (byte) (s2 >> 6);
result[7] = (byte) ((s2 >> 14) | (s3 << 7));
result[8] = (byte) (s3 >> 1);
result[9] = (byte) (s3 >> 9);
result[10] = (byte) ((s3 >> 17) | (s4 << 4));
result[11] = (byte) (s4 >> 4);
result[12] = (byte) (s4 >> 12);
result[13] = (byte) ((s4 >> 20) | (s5 << 1));
result[14] = (byte) (s5 >> 7);
result[15] = (byte) ((s5 >> 15) | (s6 << 6));
result[16] = (byte) (s6 >> 2);
result[17] = (byte) (s6 >> 10);
result[18] = (byte) ((s6 >> 18) | (s7 << 3));
result[19] = (byte) (s7 >> 5);
result[20] = (byte) (s7 >> 13);
result[21] = (byte) (s8 >> 0);
result[22] = (byte) (s8 >> 8);
result[23] = (byte) ((s8 >> 16) | (s9 << 5));
result[24] = (byte) (s9 >> 3);
result[25] = (byte) (s9 >> 11);
result[26] = (byte) ((s9 >> 19) | (s10 << 2));
result[27] = (byte) (s10 >> 6);
result[28] = (byte) ((s10 >> 14) | (s11 << 7));
result[29] = (byte) (s11 >> 1);
result[30] = (byte) (s11 >> 9);
result[31] = (byte) (s11 >> 17);
return result;
}
}

View File

@ -0,0 +1,21 @@
package net.i2p.crypto.eddsa.spec;
import java.security.spec.AlgorithmParameterSpec;
/**
* Implementation of AlgorithmParameterSpec that holds the name of a named
* EdDSA curve specification.
* @author str4d
*
*/
public class EdDSAGenParameterSpec implements AlgorithmParameterSpec {
private final String name;
public EdDSAGenParameterSpec(String stdName) {
name = stdName;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,24 @@
package net.i2p.crypto.eddsa.spec;
import net.i2p.crypto.eddsa.math.Curve;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.math.ScalarOps;
/**
* EdDSA Curve specification that can also be referred to by name.
* @author str4d
*
*/
public class EdDSANamedCurveSpec extends EdDSAParameterSpec {
private final String name;
public EdDSANamedCurveSpec(String name, Curve curve,
String hashAlgo, ScalarOps sc, GroupElement B) {
super(curve, hashAlgo, sc, B);
this.name = name;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,50 @@
package net.i2p.crypto.eddsa.spec;
import java.util.Hashtable;
import net.i2p.crypto.eddsa.Utils;
import net.i2p.crypto.eddsa.math.Curve;
import net.i2p.crypto.eddsa.math.Field;
import net.i2p.crypto.eddsa.math.ed25519.Ed25519LittleEndianEncoding;
import net.i2p.crypto.eddsa.math.ed25519.Ed25519ScalarOps;
/**
* The named EdDSA curves.
* @author str4d
*
*/
public class EdDSANamedCurveTable {
public static final String CURVE_ED25519_SHA512 = "ed25519-sha-512";
private static final Field ed25519field = new Field(
256, // b
Utils.hexToBytes("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"), // q
new Ed25519LittleEndianEncoding());
private static final Curve ed25519curve = new Curve(ed25519field,
Utils.hexToBytes("a3785913ca4deb75abd841414d0a700098e879777940c78c73fe6f2bee6c0352"), // d
ed25519field.fromByteArray(Utils.hexToBytes("b0a00e4a271beec478e42fad0618432fa7d7fb3d99004d2b0bdfc14f8024832b"))); // I
private static final EdDSANamedCurveSpec ed25519sha512 = new EdDSANamedCurveSpec(
CURVE_ED25519_SHA512,
ed25519curve,
"SHA-512", // H
new Ed25519ScalarOps(), // l
ed25519curve.createPoint( // B
Utils.hexToBytes("5866666666666666666666666666666666666666666666666666666666666666"),
true)); // Precompute tables for B
private static final Hashtable<String, EdDSANamedCurveSpec> curves = new Hashtable<String, EdDSANamedCurveSpec>();
public static void defineCurve(String name, EdDSANamedCurveSpec curve) {
curves.put(name, curve);
}
static {
defineCurve(CURVE_ED25519_SHA512, ed25519sha512);
}
public static EdDSANamedCurveSpec getByName(String name) {
return curves.get(name);
}
}

View File

@ -0,0 +1,60 @@
package net.i2p.crypto.eddsa.spec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import net.i2p.crypto.eddsa.math.Curve;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.math.ScalarOps;
import java.io.Serializable;
/**
* Parameter specification for an EdDSA algorithm.
* @author str4d
*
*/
public class EdDSAParameterSpec implements AlgorithmParameterSpec, Serializable {
private static final long serialVersionUID = 8274987108472012L;
private final Curve curve;
private final String hashAlgo;
private final ScalarOps sc;
private final GroupElement B;
/**
* @throws IllegalArgumentException if hash algorithm is unsupported or length is wrong
*/
public EdDSAParameterSpec(Curve curve, String hashAlgo,
ScalarOps sc, GroupElement B) {
try {
MessageDigest hash = MessageDigest.getInstance(hashAlgo);
// EdDSA hash function must produce 2b-bit output
if (curve.getField().getb()/4 != hash.getDigestLength())
throw new IllegalArgumentException("Hash output is not 2b-bit");
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Unsupported hash algorithm");
}
this.curve = curve;
this.hashAlgo = hashAlgo;
this.sc = sc;
this.B = B;
}
public Curve getCurve() {
return curve;
}
public String getHashAlgorithm() {
return hashAlgo;
}
public ScalarOps getScalarOps() {
return sc;
}
public GroupElement getB() {
return B;
}
}

View File

@ -0,0 +1,79 @@
package net.i2p.crypto.eddsa.spec;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import net.i2p.crypto.eddsa.math.GroupElement;
/**
* @author str4d
*
*/
public class EdDSAPrivateKeySpec implements KeySpec {
private final byte[] seed;
private final byte[] h;
private final byte[] a;
private final GroupElement A;
private final EdDSAParameterSpec spec;
/**
* @throws IllegalArgumentException if hash algorithm is unsupported
*/
public EdDSAPrivateKeySpec(byte[] seed, EdDSAParameterSpec spec) {
this.spec = spec;
this.seed = seed;
try {
MessageDigest hash = MessageDigest.getInstance(spec.getHashAlgorithm());
int b = spec.getCurve().getField().getb();
// H(k)
h = hash.digest(seed);
/*a = BigInteger.valueOf(2).pow(b-2);
for (int i=3;i<(b-2);i++) {
a = a.add(BigInteger.valueOf(2).pow(i).multiply(BigInteger.valueOf(Utils.bit(h,i))));
}*/
// Saves ~0.4ms per key when running signing tests.
// TODO: are these bitflips the same for any hash function?
h[0] &= 248;
h[(b/8)-1] &= 63;
h[(b/8)-1] |= 64;
a = Arrays.copyOfRange(h, 0, b/8);
A = spec.getB().scalarMultiply(a);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Unsupported hash algorithm");
}
}
public EdDSAPrivateKeySpec(byte[] seed, byte[] h, byte[] a, GroupElement A, EdDSAParameterSpec spec) {
this.seed = seed;
this.h = h;
this.a = a;
this.A = A;
this.spec = spec;
}
public byte[] getSeed() {
return seed;
}
public byte[] getH() {
return h;
}
public byte[] geta() {
return a;
}
public GroupElement getA() {
return A;
}
public EdDSAParameterSpec getParams() {
return spec;
}
}

View File

@ -0,0 +1,48 @@
package net.i2p.crypto.eddsa.spec;
import java.security.spec.KeySpec;
import net.i2p.crypto.eddsa.math.GroupElement;
/**
* @author str4d
*
*/
public class EdDSAPublicKeySpec implements KeySpec {
private final GroupElement A;
private final GroupElement Aneg;
private final EdDSAParameterSpec spec;
/**
* @throws IllegalArgumentException if key length is wrong
*/
public EdDSAPublicKeySpec(byte[] pk, EdDSAParameterSpec spec) {
if (pk.length != spec.getCurve().getField().getb()/8)
throw new IllegalArgumentException("public-key length is wrong");
this.A = new GroupElement(spec.getCurve(), pk);
// Precompute -A for use in verification.
this.Aneg = A.negate();
Aneg.precompute(false);
this.spec = spec;
}
public EdDSAPublicKeySpec(GroupElement A, EdDSAParameterSpec spec) {
this.A = A;
this.Aneg = A.negate();
Aneg.precompute(false);
this.spec = spec;
}
public GroupElement getA() {
return A;
}
public GroupElement getNegativeA() {
return Aneg;
}
public EdDSAParameterSpec getParams() {
return spec;
}
}