forked from I2P_Developers/i2p.i2p
Java implementation of Ed25519
Source: https://github.com/str4d/ed25519-java Git commit: be161ee7c6da29129b5ec6c4739ec3a99114a846 License: Public domain
This commit is contained in:
188
core/java/src/net/i2p/crypto/eddsa/EdDSAEngine.java
Normal file
188
core/java/src/net/i2p/crypto/eddsa/EdDSAEngine.java
Normal 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");
|
||||
}
|
||||
}
|
16
core/java/src/net/i2p/crypto/eddsa/EdDSAKey.java
Normal file
16
core/java/src/net/i2p/crypto/eddsa/EdDSAKey.java
Normal 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();
|
||||
}
|
68
core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
Normal file
68
core/java/src/net/i2p/crypto/eddsa/EdDSAPrivateKey.java
Normal 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;
|
||||
}
|
||||
}
|
56
core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java
Normal file
56
core/java/src/net/i2p/crypto/eddsa/EdDSAPublicKey.java
Normal 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;
|
||||
}
|
||||
}
|
56
core/java/src/net/i2p/crypto/eddsa/KeyFactory.java
Normal file
56
core/java/src/net/i2p/crypto/eddsa/KeyFactory.java
Normal 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");
|
||||
}
|
||||
}
|
86
core/java/src/net/i2p/crypto/eddsa/KeyPairGenerator.java
Normal file
86
core/java/src/net/i2p/crypto/eddsa/KeyPairGenerator.java
Normal 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;
|
||||
}
|
||||
}
|
54
core/java/src/net/i2p/crypto/eddsa/Utils.java
Normal file
54
core/java/src/net/i2p/crypto/eddsa/Utils.java
Normal 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;
|
||||
}
|
||||
}
|
12
core/java/src/net/i2p/crypto/eddsa/math/Constants.java
Normal file
12
core/java/src/net/i2p/crypto/eddsa/math/Constants.java
Normal 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");
|
||||
}
|
70
core/java/src/net/i2p/crypto/eddsa/math/Curve.java
Normal file
70
core/java/src/net/i2p/crypto/eddsa/math/Curve.java
Normal 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;
|
||||
}
|
||||
}
|
39
core/java/src/net/i2p/crypto/eddsa/math/Encoding.java
Normal file
39
core/java/src/net/i2p/crypto/eddsa/math/Encoding.java
Normal 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);
|
||||
}
|
88
core/java/src/net/i2p/crypto/eddsa/math/Field.java
Normal file
88
core/java/src/net/i2p/crypto/eddsa/math/Field.java
Normal 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);
|
||||
}
|
||||
}
|
51
core/java/src/net/i2p/crypto/eddsa/math/FieldElement.java
Normal file
51
core/java/src/net/i2p/crypto/eddsa/math/FieldElement.java
Normal 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();
|
||||
}
|
681
core/java/src/net/i2p/crypto/eddsa/math/GroupElement.java
Normal file
681
core/java/src/net/i2p/crypto/eddsa/math/GroupElement.java
Normal 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]";
|
||||
}
|
||||
}
|
22
core/java/src/net/i2p/crypto/eddsa/math/ScalarOps.java
Normal file
22
core/java/src/net/i2p/crypto/eddsa/math/ScalarOps.java
Normal 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);
|
||||
}
|
@ -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+"]";
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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())+"]";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user