remove eddsa submodule, remove bouncycastle, add xbib net security
This commit is contained in:
parent
1af5f2c928
commit
0618123c37
85 changed files with 158 additions and 11647 deletions
|
@ -4,7 +4,7 @@ plugins {
|
|||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = "${rootProject.property('gradle.wrapper.version')}"
|
||||
gradleVersion = libs.versions.gradle.get()
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
This work is derived from
|
||||
|
||||
https://github.com/str4d/ed25519-java/
|
||||
|
||||
released under Creative Commons Legal Code, CC0 1.0 Universal
|
|
@ -1,4 +0,0 @@
|
|||
dependencies {
|
||||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${project.property('junit.version')}"
|
||||
testImplementation "junit:junit:${project.property('junit4.version')}"
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
module org.xbib.eddsa {
|
||||
exports org.xbib.io.sshd.eddsa;
|
||||
exports org.xbib.io.sshd.eddsa.spec;
|
||||
provides java.security.Provider
|
||||
with org.xbib.io.sshd.eddsa.EdDSASecurityProvider;
|
||||
}
|
|
@ -1,455 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Curve;
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
import org.xbib.io.sshd.eddsa.math.ScalarOps;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
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.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Signing and verification for EdDSA.
|
||||
* <p>
|
||||
* The EdDSA sign and verify algorithms do not interact well with
|
||||
* the Java Signature API, as one or more update() methods must be
|
||||
* called before sign() or verify(). Using the standard API,
|
||||
* this implementation must copy and buffer all data passed in
|
||||
* via update().
|
||||
* </p><p>
|
||||
* This implementation offers two ways to avoid this copying,
|
||||
* but only if all data to be signed or verified is available
|
||||
* in a single byte array.
|
||||
* </p><p>
|
||||
* Option 1:
|
||||
* </p><ol>
|
||||
* <li>Call initSign() or initVerify() as usual.
|
||||
* </li><li>Call setParameter(ONE_SHOT_MODE)
|
||||
* </li><li>Call update(byte[]) or update(byte[], int, int) exactly once
|
||||
* </li><li>Call sign() or verify() as usual.
|
||||
* </li><li>If doing additional one-shot signs or verifies with this object, you must
|
||||
* call setParameter(ONE_SHOT_MODE) each time
|
||||
* </li></ol>
|
||||
* <p>
|
||||
* Option 2:
|
||||
* </p><ol>
|
||||
* <li>Call initSign() or initVerify() as usual.
|
||||
* </li><li>Call one of the signOneShot() or verifyOneShot() methods.
|
||||
* </li><li>If doing additional one-shot signs or verifies with this object,
|
||||
* just call signOneShot() or verifyOneShot() again.
|
||||
* </li></ol>
|
||||
*/
|
||||
public final class EdDSAEngine extends Signature {
|
||||
public static final String SIGNATURE_ALGORITHM = "NONEwithEdDSA";
|
||||
/**
|
||||
* To efficiently sign or verify data in one shot, pass this to setParameters()
|
||||
* after initSign() or initVerify() but BEFORE THE FIRST AND ONLY
|
||||
* update(data) or update(data, off, len). The data reference will be saved
|
||||
* and then used in sign() or verify() without copying the data.
|
||||
* Violate these rules and you will get a SignatureException.
|
||||
*/
|
||||
public static final AlgorithmParameterSpec ONE_SHOT_MODE = new OneShotSpec();
|
||||
private MessageDigest digest;
|
||||
private ByteArrayOutputStream baos;
|
||||
private EdDSAKey key;
|
||||
private boolean oneShotMode;
|
||||
private byte[] oneShotBytes;
|
||||
private int oneShotOffset;
|
||||
private int oneShotLength;
|
||||
|
||||
/**
|
||||
* No specific EdDSA-internal hash requested, allows any EdDSA key.
|
||||
*/
|
||||
public EdDSAEngine() {
|
||||
super(SIGNATURE_ALGORITHM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specific EdDSA-internal 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;
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
if (digest != null) {
|
||||
digest.reset();
|
||||
}
|
||||
if (baos != null) {
|
||||
baos.reset();
|
||||
}
|
||||
oneShotMode = false;
|
||||
oneShotBytes = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
|
||||
reset();
|
||||
if (privateKey instanceof EdDSAPrivateKey) {
|
||||
EdDSAPrivateKey privKey = (EdDSAPrivateKey) privateKey;
|
||||
key = privKey;
|
||||
if (digest == null) {
|
||||
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");
|
||||
digestInitSign(privKey);
|
||||
} else {
|
||||
throw new InvalidKeyException("cannot identify EdDSA private key: " + privateKey.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private void digestInitSign(EdDSAPrivateKey privKey) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
|
||||
reset();
|
||||
if (publicKey instanceof EdDSAPublicKey) {
|
||||
key = (EdDSAPublicKey) publicKey;
|
||||
if (digest == null) {
|
||||
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: " + publicKey.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SignatureException if in one-shot mode
|
||||
*/
|
||||
@Override
|
||||
protected void engineUpdate(byte b) throws SignatureException {
|
||||
if (oneShotMode)
|
||||
throw new SignatureException("unsupported in one-shot mode");
|
||||
if (baos == null)
|
||||
baos = new ByteArrayOutputStream(256);
|
||||
baos.write(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SignatureException if one-shot rules are violated
|
||||
*/
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len)
|
||||
throws SignatureException {
|
||||
if (oneShotMode) {
|
||||
if (oneShotBytes != null)
|
||||
throw new SignatureException("update() already called");
|
||||
oneShotBytes = b;
|
||||
oneShotOffset = off;
|
||||
oneShotLength = len;
|
||||
} else {
|
||||
if (baos == null)
|
||||
baos = new ByteArrayOutputStream(256);
|
||||
baos.write(b, off, len);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineSign() throws SignatureException {
|
||||
try {
|
||||
return x_engineSign();
|
||||
} finally {
|
||||
reset();
|
||||
// must leave the object ready to sign again with
|
||||
// the same key, as required by the API
|
||||
EdDSAPrivateKey privKey = (EdDSAPrivateKey) key;
|
||||
digestInitSign(privKey);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] x_engineSign() throws SignatureException {
|
||||
Curve curve = key.getParams().getCurve();
|
||||
ScalarOps sc = key.getParams().getScalarOps();
|
||||
byte[] a = ((EdDSAPrivateKey) key).geta();
|
||||
|
||||
byte[] message;
|
||||
int offset, length;
|
||||
if (oneShotMode) {
|
||||
if (oneShotBytes == null)
|
||||
throw new SignatureException("update() not called first");
|
||||
message = oneShotBytes;
|
||||
offset = oneShotOffset;
|
||||
length = oneShotLength;
|
||||
} else {
|
||||
if (baos == null)
|
||||
message = new byte[0];
|
||||
else
|
||||
message = baos.toByteArray();
|
||||
offset = 0;
|
||||
length = message.length;
|
||||
}
|
||||
// r = H(h_b,...,h_2b-1,M)
|
||||
digest.update(message, offset, length);
|
||||
byte[] r = digest.digest();
|
||||
|
||||
// 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());
|
||||
digest.update(message, offset, length);
|
||||
byte[] h = digest.digest();
|
||||
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 {
|
||||
try {
|
||||
return x_engineVerify(sigBytes);
|
||||
} finally {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean x_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;
|
||||
int offset, length;
|
||||
if (oneShotMode) {
|
||||
if (oneShotBytes == null)
|
||||
throw new SignatureException("update() not called first");
|
||||
message = oneShotBytes;
|
||||
offset = oneShotOffset;
|
||||
length = oneShotLength;
|
||||
} else {
|
||||
if (baos == null)
|
||||
message = new byte[0];
|
||||
else
|
||||
message = baos.toByteArray();
|
||||
offset = 0;
|
||||
length = message.length;
|
||||
}
|
||||
digest.update(message, offset, length);
|
||||
byte[] h = digest.digest();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* To efficiently sign all the data in one shot, if it is available,
|
||||
* use this method, which will avoid copying the data.
|
||||
* <p>
|
||||
* Same as:
|
||||
* <pre>
|
||||
* setParameter(ONE_SHOT_MODE)
|
||||
* update(data)
|
||||
* sig = sign()
|
||||
* </pre>
|
||||
*
|
||||
* @param data the message to be signed
|
||||
* @return the signature
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
public byte[] signOneShot(byte[] data) throws SignatureException {
|
||||
return signOneShot(data, 0, data.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* To efficiently sign all the data in one shot, if it is available,
|
||||
* use this method, which will avoid copying the data.
|
||||
* <p>
|
||||
* Same as:
|
||||
* <pre>
|
||||
* setParameter(ONE_SHOT_MODE)
|
||||
* update(data, off, len)
|
||||
* sig = sign()
|
||||
* </pre>
|
||||
*
|
||||
* @param data byte array containing the message to be signed
|
||||
* @param off the start of the message inside data
|
||||
* @param len the length of the message
|
||||
* @return the signature
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
public byte[] signOneShot(byte[] data, int off, int len) throws SignatureException {
|
||||
oneShotMode = true;
|
||||
update(data, off, len);
|
||||
return sign();
|
||||
}
|
||||
|
||||
/**
|
||||
* To efficiently verify all the data in one shot, if it is available,
|
||||
* use this method, which will avoid copying the data.
|
||||
* <p>
|
||||
* Same as:
|
||||
* <pre>
|
||||
* setParameter(ONE_SHOT_MODE)
|
||||
* update(data)
|
||||
* ok = verify(signature)
|
||||
* </pre>
|
||||
*
|
||||
* @param data the message that was signed
|
||||
* @param signature of the message
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
public boolean verifyOneShot(byte[] data, byte[] signature) throws SignatureException {
|
||||
return verifyOneShot(data, 0, data.length, signature, 0, signature.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* To efficiently verify all the data in one shot, if it is available,
|
||||
* use this method, which will avoid copying the data.
|
||||
* <p>
|
||||
* Same as:
|
||||
* <pre>
|
||||
* setParameter(ONE_SHOT_MODE)
|
||||
* update(data, off, len)
|
||||
* ok = verify(signature)
|
||||
* </pre>
|
||||
*
|
||||
* @param data byte array containing the message that was signed
|
||||
* @param off the start of the message inside data
|
||||
* @param len the length of the message
|
||||
* @param signature of the message
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature) throws SignatureException {
|
||||
return verifyOneShot(data, off, len, signature, 0, signature.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* To efficiently verify all the data in one shot, if it is available,
|
||||
* use this method, which will avoid copying the data.
|
||||
* <p>
|
||||
* Same as:
|
||||
* <pre>
|
||||
* setParameter(ONE_SHOT_MODE)
|
||||
* update(data)
|
||||
* ok = verify(signature, sigoff, siglen)
|
||||
* </pre>
|
||||
*
|
||||
* @param data the message that was signed
|
||||
* @param signature byte array containing the signature
|
||||
* @param sigoff the start of the signature
|
||||
* @param siglen the length of the signature
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
public boolean verifyOneShot(byte[] data, byte[] signature, int sigoff, int siglen) throws SignatureException {
|
||||
return verifyOneShot(data, 0, data.length, signature, sigoff, siglen);
|
||||
}
|
||||
|
||||
/**
|
||||
* To efficiently verify all the data in one shot, if it is available,
|
||||
* use this method, which will avoid copying the data.
|
||||
* <p>
|
||||
* Same as:
|
||||
* <pre>
|
||||
* setParameter(ONE_SHOT_MODE)
|
||||
* update(data, off, len)
|
||||
* ok = verify(signature, sigoff, siglen)
|
||||
* </pre>
|
||||
*
|
||||
* @param data byte array containing the message that was signed
|
||||
* @param off the start of the message inside data
|
||||
* @param len the length of the message
|
||||
* @param signature byte array containing the signature
|
||||
* @param sigoff the start of the signature
|
||||
* @param siglen the length of the signature
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureException if update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
public boolean verifyOneShot(byte[] data, int off, int len, byte[] signature, int sigoff, int siglen) throws SignatureException {
|
||||
oneShotMode = true;
|
||||
update(data, off, len);
|
||||
return verify(signature, sigoff, siglen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidAlgorithmParameterException if spec is ONE_SHOT_MODE and update() already called
|
||||
* @see #ONE_SHOT_MODE
|
||||
*/
|
||||
@Override
|
||||
protected void engineSetParameter(AlgorithmParameterSpec spec) throws InvalidAlgorithmParameterException {
|
||||
if (spec.equals(ONE_SHOT_MODE)) {
|
||||
if (oneShotBytes != null || (baos != null && baos.size() > 0))
|
||||
throw new InvalidAlgorithmParameterException("update() already called");
|
||||
oneShotMode = true;
|
||||
} else {
|
||||
super.engineSetParameter(spec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineSetParameter(String param, Object value) {
|
||||
throw new UnsupportedOperationException("engineSetParameter unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object engineGetParameter(String param) {
|
||||
throw new UnsupportedOperationException("engineSetParameter unsupported");
|
||||
}
|
||||
|
||||
private static class OneShotSpec implements AlgorithmParameterSpec {
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
|
||||
/**
|
||||
* Common interface for all EdDSA keys.
|
||||
*/
|
||||
public interface EdDSAKey {
|
||||
/**
|
||||
* The reported key algorithm for all EdDSA keys
|
||||
*/
|
||||
String KEY_ALGORITHM = "EdDSA";
|
||||
|
||||
/**
|
||||
* @return a parameter specification representing the EdDSA domain
|
||||
* parameters for the key.
|
||||
*/
|
||||
EdDSAParameterSpec getParams();
|
||||
}
|
|
@ -1,321 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An EdDSA private key.
|
||||
* <p>
|
||||
* Warning: Private key encoding is based on the current curdle WG draft,
|
||||
* and is subject to change. See getEncoded().
|
||||
* </p><p>
|
||||
* For compatibility with older releases, decoding supports both the old and new
|
||||
* draft specifications. See decode().
|
||||
* </p><p>
|
||||
* Ref: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
* </p><p>
|
||||
* Old Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
* </p>
|
||||
*/
|
||||
public class EdDSAPrivateKey implements EdDSAKey, PrivateKey {
|
||||
|
||||
// compatible with JDK 1.1
|
||||
private static final long serialVersionUID = 23495873459878957L;
|
||||
// OID 1.3.101.xxx
|
||||
private static final int OID_OLD = 100;
|
||||
private static final int OID_ED25519 = 112;
|
||||
private static final int OID_BYTE = 11;
|
||||
private static final int IDLEN_BYTE = 6;
|
||||
private final byte[] seed;
|
||||
private final byte[] h;
|
||||
private final byte[] a;
|
||||
private final GroupElement A;
|
||||
private final byte[] Abyte;
|
||||
private 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 EdDSAPrivateKey(PKCS8EncodedKeySpec spec) throws InvalidKeySpecException {
|
||||
this(new EdDSAPrivateKeySpec(decode(spec.getEncoded()),
|
||||
EdDSANamedCurveTable.getByName("Ed25519")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the private key bytes from the provided encoding.
|
||||
* <p>
|
||||
* This will decode data conforming to the current spec at
|
||||
* https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
* or as inferred from the old spec at
|
||||
* https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
|
||||
* </p><p>
|
||||
* Contrary to draft-ietf-curdle-pkix-04, it WILL accept a parameter value
|
||||
* of NULL, as it is required for interoperability with the default Java
|
||||
* keystore. Other implementations MUST NOT copy this behaviour from here
|
||||
* unless they also need to read keys from the default Java keystore.
|
||||
* </p><p>
|
||||
* This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
|
||||
* See also getEncoded().
|
||||
*
|
||||
* @return 32 bytes for Ed25519, throws for other curves
|
||||
*/
|
||||
private static byte[] decode(byte[] d) throws InvalidKeySpecException {
|
||||
try {
|
||||
//
|
||||
// Setup and OID check
|
||||
//
|
||||
int totlen = 48;
|
||||
int idlen = 5;
|
||||
int doid = d[OID_BYTE];
|
||||
if (doid == OID_OLD) {
|
||||
totlen = 49;
|
||||
idlen = 8;
|
||||
} else if (doid == OID_ED25519) {
|
||||
// Detect parameter value of NULL
|
||||
if (d[IDLEN_BYTE] == 7) {
|
||||
totlen = 50;
|
||||
idlen = 7;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
|
||||
//
|
||||
// Pre-decoding check
|
||||
//
|
||||
if (d.length != totlen) {
|
||||
throw new InvalidKeySpecException("invalid key spec length");
|
||||
}
|
||||
|
||||
//
|
||||
// Decoding
|
||||
//
|
||||
int idx = 0;
|
||||
if (d[idx++] != 0x30 ||
|
||||
d[idx++] != (totlen - 2) ||
|
||||
d[idx++] != 0x02 ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 0 ||
|
||||
d[idx++] != 0x30 ||
|
||||
d[idx++] != idlen ||
|
||||
d[idx++] != 0x06 ||
|
||||
d[idx++] != 3 ||
|
||||
d[idx++] != (1 * 40) + 3 ||
|
||||
d[idx++] != 101) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
idx++; // OID, checked above
|
||||
// parameters only with old OID
|
||||
if (doid == OID_OLD) {
|
||||
if (d[idx++] != 0x0a ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 1) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
} else {
|
||||
// Handle parameter value of NULL
|
||||
//
|
||||
// Quote https://tools.ietf.org/html/draft-ietf-curdle-pkix-04 :
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
// Regardless of the defect in the original 1997 syntax,
|
||||
// implementations MUST NOT accept a parameters value of NULL.
|
||||
//
|
||||
// But Java's default keystore puts it in (when decoding as
|
||||
// PKCS8 and then re-encoding to pass on), so we must accept it.
|
||||
if (idlen == 7) {
|
||||
if (d[idx++] != 0x05 ||
|
||||
d[idx++] != 0) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
}
|
||||
// PrivateKey wrapping the CurvePrivateKey
|
||||
if (d[idx++] != 0x04 ||
|
||||
d[idx++] != 34) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
}
|
||||
if (d[idx++] != 0x04 ||
|
||||
d[idx++] != 32) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
byte[] rv = new byte[32];
|
||||
System.arraycopy(d, idx, rv, 0, 32);
|
||||
return rv;
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
throw new InvalidKeySpecException(ioobe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return KEY_ALGORITHM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "PKCS#8";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key in its canonical encoding.
|
||||
* This implements the following specs:
|
||||
* <ul>
|
||||
* <li>General encoding: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04</li>
|
||||
* <li>Key encoding: https://tools.ietf.org/html/rfc8032</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This encodes the seed. It will return null if constructed from
|
||||
* a spec which was directly constructed from H, in which case seed is null.
|
||||
* </p><p>
|
||||
* For keys in older formats, decoding and then re-encoding is sufficient to
|
||||
* migrate them to the canonical encoding.
|
||||
* </p>
|
||||
* Relevant spec quotes:
|
||||
* <pre>
|
||||
* OneAsymmetricKey ::= SEQUENCE {
|
||||
* version Version,
|
||||
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
|
||||
* privateKey PrivateKey,
|
||||
* attributes [0] Attributes OPTIONAL,
|
||||
* ...,
|
||||
* [[2: publicKey [1] PublicKey OPTIONAL ]],
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* Version ::= INTEGER
|
||||
* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
|
||||
* PrivateKey ::= OCTET STRING
|
||||
* PublicKey ::= OCTET STRING
|
||||
* Attributes ::= SET OF Attribute
|
||||
* </pre>
|
||||
* <pre>
|
||||
* ... when encoding a OneAsymmetricKey object, the private key is wrapped
|
||||
* in a CurvePrivateKey object and wrapped by the OCTET STRING of the
|
||||
* 'privateKey' field.
|
||||
*
|
||||
* CurvePrivateKey ::= OCTET STRING
|
||||
* </pre>
|
||||
* <pre>
|
||||
* AlgorithmIdentifier ::= SEQUENCE {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
* }
|
||||
*
|
||||
* For all of the OIDs, the parameters MUST be absent.
|
||||
* </pre>
|
||||
* <pre>
|
||||
* id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
* </pre>
|
||||
*
|
||||
* @return 48 bytes for Ed25519, null for other curves
|
||||
*/
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
if (!edDsaSpec.equals(EdDSANamedCurveTable.getByName("Ed25519")))
|
||||
return null;
|
||||
if (seed == null)
|
||||
return null;
|
||||
int totlen = 16 + seed.length;
|
||||
byte[] rv = new byte[totlen];
|
||||
int idx = 0;
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = (byte) (totlen - 2);
|
||||
// version
|
||||
rv[idx++] = 0x02;
|
||||
rv[idx++] = 1;
|
||||
// v1 - no public key included
|
||||
rv[idx++] = 0;
|
||||
// Algorithm Identifier
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = 5;
|
||||
// OID
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
|
||||
rv[idx++] = 0x06;
|
||||
rv[idx++] = 3;
|
||||
rv[idx++] = (1 * 40) + 3;
|
||||
rv[idx++] = 101;
|
||||
rv[idx++] = (byte) OID_ED25519;
|
||||
// params - absent
|
||||
// PrivateKey
|
||||
rv[idx++] = 0x04; // octet string
|
||||
rv[idx++] = (byte) (2 + seed.length);
|
||||
// CurvePrivateKey
|
||||
rv[idx++] = 0x04; // octet string
|
||||
rv[idx++] = (byte) seed.length;
|
||||
// the key
|
||||
System.arraycopy(seed, 0, rv, idx, seed.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdDSAParameterSpec getParams() {
|
||||
return edDsaSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return will be null if constructed from a spec which was
|
||||
* directly constructed from H
|
||||
*/
|
||||
public byte[] getSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the hash of the seed
|
||||
*/
|
||||
public byte[] getH() {
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the private key
|
||||
*/
|
||||
public byte[] geta() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the public key
|
||||
*/
|
||||
public GroupElement getA() {
|
||||
return A;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the public key
|
||||
*/
|
||||
public byte[] getAbyte() {
|
||||
return Abyte;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof EdDSAPrivateKey))
|
||||
return false;
|
||||
EdDSAPrivateKey pk = (EdDSAPrivateKey) o;
|
||||
return Arrays.equals(seed, pk.getSeed()) &&
|
||||
edDsaSpec.equals(pk.getParams());
|
||||
}
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An EdDSA public key.
|
||||
* <p>
|
||||
* Warning: Public key encoding is is based on the current curdle WG draft,
|
||||
* and is subject to change. See getEncoded().
|
||||
* </p><p>
|
||||
* For compatibility with older releases, decoding supports both the old and new
|
||||
* draft specifications. See decode().
|
||||
* </p><p>
|
||||
* Ref: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
* </p><p>
|
||||
* Old Ref: https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04
|
||||
* </p>
|
||||
*/
|
||||
public class EdDSAPublicKey implements EdDSAKey, PublicKey {
|
||||
private static final long serialVersionUID = 9837459837498475L;
|
||||
// OID 1.3.101.xxx
|
||||
private static final int OID_OLD = 100;
|
||||
private static final int OID_ED25519 = 112;
|
||||
private static final int OID_BYTE = 8;
|
||||
private static final int IDLEN_BYTE = 3;
|
||||
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 EdDSAPublicKey(X509EncodedKeySpec spec) throws InvalidKeySpecException {
|
||||
this(new EdDSAPublicKeySpec(decode(spec.getEncoded()),
|
||||
EdDSANamedCurveTable.getByName("Ed25519")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the public key bytes from the provided encoding.
|
||||
* <p>
|
||||
* This will decode data conforming to the current spec at
|
||||
* https://tools.ietf.org/html/draft-ietf-curdle-pkix-04
|
||||
* or the old spec at
|
||||
* https://tools.ietf.org/html/draft-josefsson-pkix-eddsa-04.
|
||||
* </p><p>
|
||||
* Contrary to draft-ietf-curdle-pkix-04, it WILL accept a parameter value
|
||||
* of NULL, as it is required for interoperability with the default Java
|
||||
* keystore. Other implementations MUST NOT copy this behaviour from here
|
||||
* unless they also need to read keys from the default Java keystore.
|
||||
* </p><p>
|
||||
* This is really dumb for now. It does not use a general-purpose ASN.1 decoder.
|
||||
* See also getEncoded().
|
||||
* </p>
|
||||
*
|
||||
* @return 32 bytes for Ed25519, throws for other curves
|
||||
*/
|
||||
private static byte[] decode(byte[] d) throws InvalidKeySpecException {
|
||||
try {
|
||||
//
|
||||
// Setup and OID check
|
||||
//
|
||||
int totlen = 44;
|
||||
int idlen = 5;
|
||||
int doid = d[OID_BYTE];
|
||||
if (doid == OID_OLD) {
|
||||
totlen = 47;
|
||||
idlen = 8;
|
||||
} else if (doid == OID_ED25519) {
|
||||
// Detect parameter value of NULL
|
||||
if (d[IDLEN_BYTE] == 7) {
|
||||
totlen = 46;
|
||||
idlen = 7;
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
|
||||
//
|
||||
// Pre-decoding check
|
||||
//
|
||||
if (d.length != totlen) {
|
||||
throw new InvalidKeySpecException("invalid key spec length");
|
||||
}
|
||||
|
||||
//
|
||||
// Decoding
|
||||
//
|
||||
int idx = 0;
|
||||
if (d[idx++] != 0x30 ||
|
||||
d[idx++] != (totlen - 2) ||
|
||||
d[idx++] != 0x30 ||
|
||||
d[idx++] != idlen ||
|
||||
d[idx++] != 0x06 ||
|
||||
d[idx++] != 3 ||
|
||||
d[idx++] != (1 * 40) + 3 ||
|
||||
d[idx++] != 101) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
idx++; // OID, checked above
|
||||
// parameters only with old OID
|
||||
if (doid == OID_OLD) {
|
||||
if (d[idx++] != 0x0a ||
|
||||
d[idx++] != 1 ||
|
||||
d[idx++] != 1) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
} else {
|
||||
// Handle parameter value of NULL
|
||||
//
|
||||
// Quote https://tools.ietf.org/html/draft-ietf-curdle-pkix-04 :
|
||||
// For all of the OIDs, the parameters MUST be absent.
|
||||
// Regardless of the defect in the original 1997 syntax,
|
||||
// implementations MUST NOT accept a parameters value of NULL.
|
||||
//
|
||||
// But Java's default keystore puts it in (when decoding as
|
||||
// PKCS8 and then re-encoding to pass on), so we must accept it.
|
||||
if (idlen == 7) {
|
||||
if (d[idx++] != 0x05 ||
|
||||
d[idx++] != 0) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (d[idx++] != 0x03 ||
|
||||
d[idx++] != 33 ||
|
||||
d[idx++] != 0) {
|
||||
throw new InvalidKeySpecException("unsupported key spec");
|
||||
}
|
||||
byte[] rv = new byte[32];
|
||||
System.arraycopy(d, idx, rv, 0, 32);
|
||||
return rv;
|
||||
} catch (IndexOutOfBoundsException ioobe) {
|
||||
throw new InvalidKeySpecException(ioobe);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return KEY_ALGORITHM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "X.509";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key in its canonical encoding.
|
||||
* This implements the following specs:
|
||||
* <ul>
|
||||
* <li>General encoding: https://tools.ietf.org/html/draft-ietf-curdle-pkix-04</li>
|
||||
* <li>Key encoding: https://tools.ietf.org/html/rfc8032</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* For keys in older formats, decoding and then re-encoding is sufficient to
|
||||
* migrate them to the canonical encoding.
|
||||
* </p>
|
||||
* Relevant spec quotes:
|
||||
* <pre>
|
||||
* In the X.509 certificate, the subjectPublicKeyInfo field has the
|
||||
* SubjectPublicKeyInfo type, which has the following ASN.1 syntax:
|
||||
*
|
||||
* SubjectPublicKeyInfo ::= SEQUENCE {
|
||||
* algorithm AlgorithmIdentifier,
|
||||
* subjectPublicKey BIT STRING
|
||||
* }
|
||||
* </pre>
|
||||
* <pre>
|
||||
* AlgorithmIdentifier ::= SEQUENCE {
|
||||
* algorithm OBJECT IDENTIFIER,
|
||||
* parameters ANY DEFINED BY algorithm OPTIONAL
|
||||
* }
|
||||
*
|
||||
* For all of the OIDs, the parameters MUST be absent.
|
||||
* </pre>
|
||||
* <pre>
|
||||
* id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
* </pre>
|
||||
*
|
||||
* @return 44 bytes for Ed25519, null for other curves
|
||||
*/
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
if (!edDsaSpec.equals(EdDSANamedCurveTable.getByName("Ed25519")))
|
||||
return null;
|
||||
int totlen = 12 + Abyte.length;
|
||||
byte[] rv = new byte[totlen];
|
||||
int idx = 0;
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = (byte) (totlen - 2);
|
||||
// Algorithm Identifier
|
||||
// sequence
|
||||
rv[idx++] = 0x30;
|
||||
rv[idx++] = 5;
|
||||
// OID
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb540809%28v=vs.85%29.aspx
|
||||
rv[idx++] = 0x06;
|
||||
rv[idx++] = 3;
|
||||
rv[idx++] = (1 * 40) + 3;
|
||||
rv[idx++] = 101;
|
||||
rv[idx++] = (byte) OID_ED25519;
|
||||
// params - absent
|
||||
// the key
|
||||
rv[idx++] = 0x03; // bit string
|
||||
rv[idx++] = (byte) (1 + Abyte.length);
|
||||
rv[idx++] = 0; // number of trailing unused bits
|
||||
System.arraycopy(Abyte, 0, rv, idx, Abyte.length);
|
||||
return rv;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdDSAParameterSpec getParams() {
|
||||
return edDsaSpec;
|
||||
}
|
||||
|
||||
public GroupElement getA() {
|
||||
return A;
|
||||
}
|
||||
|
||||
public GroupElement getNegativeA() {
|
||||
return Aneg;
|
||||
}
|
||||
|
||||
public byte[] getAbyte() {
|
||||
return Abyte;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(Abyte);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof EdDSAPublicKey))
|
||||
return false;
|
||||
EdDSAPublicKey pk = (EdDSAPublicKey) o;
|
||||
return Arrays.equals(Abyte, pk.getAbyte()) &&
|
||||
edDsaSpec.equals(pk.getParams());
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
|
||||
/**
|
||||
* A security {@link Provider} that can be registered via {@link Security#addProvider(Provider)}.
|
||||
*/
|
||||
public class EdDSASecurityProvider extends Provider {
|
||||
|
||||
public static final String PROVIDER_NAME = "EdDSA";
|
||||
|
||||
private static final long serialVersionUID = 1210027906682292307L;
|
||||
|
||||
public EdDSASecurityProvider() {
|
||||
super(PROVIDER_NAME, 0.1, "xbib " + PROVIDER_NAME + " security provider wrapper");
|
||||
|
||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
setup();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected void setup() {
|
||||
// See https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html
|
||||
put("KeyFactory." + EdDSAKey.KEY_ALGORITHM, "org.xbib.io.sshd.eddsa.KeyFactory");
|
||||
put("KeyPairGenerator." + EdDSAKey.KEY_ALGORITHM, "org.xbib.io.sshd.eddsa.KeyPairGenerator");
|
||||
put("Signature." + EdDSAEngine.SIGNATURE_ALGORITHM, "org.xbib.io.sshd.eddsa.EdDSAEngine");
|
||||
|
||||
// OID Mappings
|
||||
// See section "Mapping from OID to name".
|
||||
// The Key* -> OID mappings correspond to the default algorithm in KeyPairGenerator.
|
||||
//
|
||||
// From draft-ieft-curdle-pkix-04:
|
||||
// id-Ed25519 OBJECT IDENTIFIER ::= { 1 3 101 112 }
|
||||
put("Alg.Alias.KeyFactory.1.3.101.112", EdDSAKey.KEY_ALGORITHM);
|
||||
put("Alg.Alias.KeyFactory.OID.1.3.101.112", EdDSAKey.KEY_ALGORITHM);
|
||||
put("Alg.Alias.KeyPairGenerator.1.3.101.112", EdDSAKey.KEY_ALGORITHM);
|
||||
put("Alg.Alias.KeyPairGenerator.OID.1.3.101.112", EdDSAKey.KEY_ALGORITHM);
|
||||
put("Alg.Alias.Signature.1.3.101.112", EdDSAEngine.SIGNATURE_ALGORITHM);
|
||||
put("Alg.Alias.Signature.OID.1.3.101.112", EdDSAEngine.SIGNATURE_ALGORITHM);
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
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 java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
/**
|
||||
*/
|
||||
public final class KeyFactory extends KeyFactorySpi {
|
||||
|
||||
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
if (keySpec instanceof EdDSAPrivateKeySpec) {
|
||||
return new EdDSAPrivateKey((EdDSAPrivateKeySpec) keySpec);
|
||||
}
|
||||
if (keySpec instanceof PKCS8EncodedKeySpec) {
|
||||
return new EdDSAPrivateKey((PKCS8EncodedKeySpec) keySpec);
|
||||
}
|
||||
throw new InvalidKeySpecException("key spec not recognised: " + keySpec.getClass());
|
||||
}
|
||||
|
||||
protected PublicKey engineGeneratePublic(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
if (keySpec instanceof EdDSAPublicKeySpec) {
|
||||
return new EdDSAPublicKey((EdDSAPublicKeySpec) keySpec);
|
||||
}
|
||||
if (keySpec instanceof X509EncodedKeySpec) {
|
||||
return new EdDSAPublicKey((X509EncodedKeySpec) keySpec);
|
||||
}
|
||||
throw new InvalidKeySpecException("key spec not recognised: " + keySpec.getClass());
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAGenParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Default keysize is 256 (Ed25519).
|
||||
*/
|
||||
public final class KeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
private static final int DEFAULT_KEYSIZE = 256;
|
||||
private static final Hashtable<Integer, AlgorithmParameterSpec> edParameters;
|
||||
|
||||
static {
|
||||
edParameters = new Hashtable<>();
|
||||
edParameters.put(256, new EdDSAGenParameterSpec("Ed25519"));
|
||||
}
|
||||
|
||||
private EdDSAParameterSpec edParams;
|
||||
private SecureRandom random;
|
||||
private boolean initialized;
|
||||
|
||||
public void initialize(int keysize, SecureRandom random) {
|
||||
AlgorithmParameterSpec edParams = edParameters.get(keysize);
|
||||
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_KEYSIZE, 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;
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
/**
|
||||
* Basic utilities for EdDSA.
|
||||
* Not for external use, not maintained as a public API.
|
||||
*/
|
||||
public class Utils {
|
||||
/**
|
||||
* Constant-time byte comparison.
|
||||
*
|
||||
* @param b a byte
|
||||
* @param c a byte
|
||||
* @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 byte[] comparison.
|
||||
*
|
||||
* @param b a byte[]
|
||||
* @param c a byte[]
|
||||
* @return 1 if b and c are equal, 0 otherwise.
|
||||
*/
|
||||
public static int equal(byte[] b, byte[] c) {
|
||||
int result = 0;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
result |= b[i] ^ c[i];
|
||||
}
|
||||
|
||||
return equal(result, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 >> 3] >> (i & 7)) & 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts bytes to a hex string.
|
||||
*
|
||||
* @param raw the byte[] to be converted.
|
||||
* @return the hex representation as a string.
|
||||
*/
|
||||
public static String bytesToHex(byte[] raw) {
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
final StringBuilder hex = new StringBuilder(2 * raw.length);
|
||||
for (final byte b : raw) {
|
||||
hex.append(Character.forDigit((b & 0xF0) >> 4, 16))
|
||||
.append(Character.forDigit((b & 0x0F), 16));
|
||||
}
|
||||
return hex.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
final 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");
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
//import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A twisted Edwards curve.
|
||||
* Points on the curve satisfy $-x^2 + y^2 = 1 + d x^2y^2$
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return f.hashCode() ^
|
||||
d.hashCode() ^
|
||||
I.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof Curve))
|
||||
return false;
|
||||
Curve c = (Curve) o;
|
||||
return f.equals(c.getField()) &&
|
||||
d.equals(c.getD()) &&
|
||||
I.equals(c.getI());
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
/**
|
||||
* Common interface for all $(b-1)$-bit encodings of elements of EdDSA finite fields.
|
||||
*/
|
||||
public abstract class Encoding {
|
||||
protected Field f;
|
||||
|
||||
public synchronized void setField(Field f) {
|
||||
if (this.f != null)
|
||||
throw new IllegalStateException("already set");
|
||||
this.f = f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a FieldElement in its $(b-1)$-bit encoding.
|
||||
*
|
||||
* @param x the FieldElement to encode
|
||||
* @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 in the $(b-1)$-bit encoding of a FieldElement.
|
||||
* @return the FieldElement represented by 'val'.
|
||||
*/
|
||||
public abstract FieldElement decode(byte[] in);
|
||||
|
||||
/**
|
||||
* From the Ed25519 paper:<br>
|
||||
* $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,\dots, q-1\}$ then the negative
|
||||
* elements of $F_q$ are $\{1, 3, 5,\dots, q-2\}$.
|
||||
*
|
||||
* @param x the FieldElement to check
|
||||
* @return true if negative
|
||||
*/
|
||||
public abstract boolean isNegative(FieldElement x);
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
/**
|
||||
* An EdDSA finite field. Includes several pre-computed values.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
//import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Note: concrete subclasses must implement hashCode() and equals()
|
||||
*/
|
||||
public abstract class FieldElement /*implements Serializable*/ {
|
||||
//private static final long serialVersionUID = 1239527465875676L;
|
||||
|
||||
protected final Field f;
|
||||
|
||||
public FieldElement(Field f) {
|
||||
if (null == f) {
|
||||
throw new IllegalArgumentException("field cannot be null");
|
||||
}
|
||||
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();
|
||||
|
||||
public abstract FieldElement cmov(FieldElement val, final int b);
|
||||
|
||||
// Note: concrete subclasses must implement hashCode() and equals()
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,27 +0,0 @@
|
|||
package org.xbib.io.sshd.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 the scalar to reduce
|
||||
* @return $s \bmod l$
|
||||
*/
|
||||
byte[] reduce(byte[] s);
|
||||
|
||||
/**
|
||||
* $r = (a * b + c) \bmod l$
|
||||
*
|
||||
* @param a a scalar
|
||||
* @param b a scalar
|
||||
* @param c a scalar
|
||||
* @return $(a*b + c) \bmod l$
|
||||
*/
|
||||
byte[] multiplyAndAdd(byte[] a, byte[] b, byte[] c);
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.bigint;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.FieldElement;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* A particular element of the field \Z/(2^255-19).
|
||||
*/
|
||||
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 FieldElement cmov(FieldElement val, int b) {
|
||||
// Not constant-time, but it doesn't really matter because none of the underlying BigInteger operations
|
||||
// are either, so there's not much point in trying hard here ...
|
||||
return b == 0 ? this : val;
|
||||
}
|
||||
|
||||
@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 + "]";
|
||||
}
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.bigint;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Encoding;
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.FieldElement;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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 synchronized 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.
|
||||
*
|
||||
* @param x the BigInteger value to encode
|
||||
* @return array of length $b/8$
|
||||
* @throws IllegalStateException if field not set
|
||||
*/
|
||||
public byte[] encode(BigInteger x) {
|
||||
if (f == null)
|
||||
throw new IllegalStateException("field not set");
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a FieldElement from its $(b-1)$-bit encoding.
|
||||
* The highest bit is masked out.
|
||||
*
|
||||
* @param in the $(b-1)$-bit encoding of a FieldElement.
|
||||
* @return the FieldElement represented by 'val'.
|
||||
* @throws IllegalStateException if field not set
|
||||
* @throws IllegalArgumentException if encoding is invalid
|
||||
*/
|
||||
public FieldElement decode(byte[] in) {
|
||||
if (f == null)
|
||||
throw new IllegalStateException("field not set");
|
||||
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
|
||||
*
|
||||
* @param in the $(b-1)$-bit encoding of a FieldElement.
|
||||
* @return the decoded value as a BigInteger
|
||||
*/
|
||||
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:<br>
|
||||
* $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,\dots, q-1\}$ then the negative
|
||||
* elements of $F_q$ are $\{1, 3, 5,\dots, q-2\}$.
|
||||
*
|
||||
* @return true if negative
|
||||
*/
|
||||
public boolean isNegative(FieldElement x) {
|
||||
return ((BigIntegerFieldElement) x).bi.testBit(0);
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
/**
|
||||
* EdDSA-Java by str4d
|
||||
* <p>
|
||||
* To the extent possible under law, the person who associated CC0 with
|
||||
* EdDSA-Java has waived all copyright and related or neighboring rights
|
||||
* to EdDSA-Java.
|
||||
* <p>
|
||||
* You should have received a copy of the CC0 legalcode along with this
|
||||
* work. If not, see <https://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
*/
|
||||
package org.xbib.io.sshd.eddsa.math.bigint;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.ScalarOps;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* Low-level, non-optimized implementation using BigIntegers for any curve.
|
||||
*/
|
||||
package org.xbib.io.sshd.eddsa.math.bigint;
|
File diff suppressed because it is too large
Load diff
|
@ -1,284 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.ed25519;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Encoding;
|
||||
import org.xbib.io.sshd.eddsa.math.FieldElement;
|
||||
|
||||
/**
|
||||
* Helper class for encoding/decoding from/to the 32 byte representation.
|
||||
* Reviewed/commented by Bloody Rookie (nemproject@gmx.de)
|
||||
*/
|
||||
public class Ed25519LittleEndianEncoding extends Encoding {
|
||||
static int load_3(byte[] in, int offset) {
|
||||
int result = in[offset++] & 0xff;
|
||||
result |= (in[offset++] & 0xff) << 8;
|
||||
result |= (in[offset] & 0xff) << 16;
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a given field element in its 32 byte representation. This is done in two steps:
|
||||
* <ol>
|
||||
* <li>Reduce the value of the field element modulo $p$.
|
||||
* <li>Convert the field element to the 32 byte representation.
|
||||
* </ol><p>
|
||||
* The idea for the modulo $p$ reduction algorithm is as follows:
|
||||
* </p>
|
||||
* <h2>Assumption:</h2>
|
||||
* <ul>
|
||||
* <li>$p = 2^{255} - 19$
|
||||
* <li>$h = h_0 + 2^{25} * h_1 + 2^{(26+25)} * h_2 + \dots + 2^{230} * h_9$ where $0 \le |h_i| \lt 2^{27}$ for all $i=0,\dots,9$.
|
||||
* <li>$h \cong r \mod p$, i.e. $h = r + q * p$ for some suitable $0 \le r \lt p$ and an integer $q$.
|
||||
* </ul><p>
|
||||
* Then $q = [2^{-255} * (h + 19 * 2^{-25} * h_9 + 1/2)]$ where $[x] = floor(x)$.
|
||||
* </p>
|
||||
* <h2>Proof:</h2>
|
||||
* <p>
|
||||
* We begin with some very raw estimation for the bounds of some expressions:
|
||||
* <p>
|
||||
* $$
|
||||
* \begin{equation}
|
||||
* |h| \lt 2^{230} * 2^{30} = 2^{260} \Rightarrow |r + q * p| \lt 2^{260} \Rightarrow |q| \lt 2^{10}. \\
|
||||
* \Rightarrow -1/4 \le a := 19^2 * 2^{-255} * q \lt 1/4. \\
|
||||
* |h - 2^{230} * h_9| = |h_0 + \dots + 2^{204} * h_8| \lt 2^{204} * 2^{30} = 2^{234}. \\
|
||||
* \Rightarrow -1/4 \le b := 19 * 2^{-255} * (h - 2^{230} * h_9) \lt 1/4
|
||||
* \end{equation}
|
||||
* $$
|
||||
* <p>
|
||||
* Therefore $0 \lt 1/2 - a - b \lt 1$.
|
||||
* <p>
|
||||
* Set $x := r + 19 * 2^{-255} * r + 1/2 - a - b$. Then:
|
||||
* <p>
|
||||
* $$
|
||||
* 0 \le x \lt 255 - 20 + 19 + 1 = 2^{255} \\
|
||||
* \Rightarrow 0 \le 2^{-255} * x \lt 1.
|
||||
* $$
|
||||
* <p>
|
||||
* Since $q$ is an integer we have
|
||||
* <p>
|
||||
* $$
|
||||
* [q + 2^{-255} * x] = q \quad (1)
|
||||
* $$
|
||||
* <p>
|
||||
* Have a closer look at $x$:
|
||||
* <p>
|
||||
* $$
|
||||
* \begin{align}
|
||||
* x &= h - q * (2^{255} - 19) + 19 * 2^{-255} * (h - q * (2^{255} - 19)) + 1/2 - 19^2 * 2^{-255} * q - 19 * 2^{-255} * (h - 2^{230} * h_9) \\
|
||||
* &= h - q * 2^{255} + 19 * q + 19 * 2^{-255} * h - 19 * q + 19^2 * 2^{-255} * q + 1/2 - 19^2 * 2^{-255} * q - 19 * 2^{-255} * h + 19 * 2^{-25} * h_9 \\
|
||||
* &= h + 19 * 2^{-25} * h_9 + 1/2 - q^{255}.
|
||||
* \end{align}
|
||||
* $$
|
||||
* <p>
|
||||
* Inserting the expression for $x$ into $(1)$ we get the desired expression for $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;
|
||||
|
||||
// Step 1:
|
||||
// Calculate q
|
||||
q = (19 * h9 + (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;
|
||||
|
||||
// r = h - q * p = h - 2^255 * q + 19 * q
|
||||
// First add 19 * q then discard the bit 255
|
||||
h0 += 19 * q;
|
||||
|
||||
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;
|
||||
|
||||
// Step 2 (straight forward conversion):
|
||||
byte[] s = new byte[32];
|
||||
s[0] = (byte) h0;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a given field element in its 10 byte $2^{25.5}$ representation.
|
||||
*
|
||||
* @param in The 32 byte representation.
|
||||
* @return The field element in its $2^{25.5}$ bit representation.
|
||||
*/
|
||||
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) & 0x7FFFFF) << 2;
|
||||
long carry0;
|
||||
long carry1;
|
||||
long carry2;
|
||||
long carry3;
|
||||
long carry4;
|
||||
long carry5;
|
||||
long carry6;
|
||||
long carry7;
|
||||
long carry8;
|
||||
long carry9;
|
||||
|
||||
// Remember: 2^255 congruent 19 modulo p
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the FieldElement negative in this encoding?
|
||||
* <p>
|
||||
* Return true if $x$ is in $\{1,3,5,\dots,q-2\}$<br>
|
||||
* Return false if $x$ is in $\{0,2,4,\dots,q-1\}$
|
||||
* <p>
|
||||
* Preconditions:
|
||||
* </p><ul>
|
||||
* <li>$|x|$ bounded by $1.1*2^{26},1.1*2^{25},1.1*2^{26},1.1*2^{25}$, etc.
|
||||
* </ul>
|
||||
*
|
||||
* @return true if $x$ is in $\{1,3,5,\dots,q-2\}$, false otherwise.
|
||||
*/
|
||||
public boolean isNegative(FieldElement x) {
|
||||
byte[] s = encode(x);
|
||||
return (s[0] & 1) != 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,913 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.ed25519;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.ScalarOps;
|
||||
|
||||
import static org.xbib.io.sshd.eddsa.math.ed25519.Ed25519LittleEndianEncoding.load_3;
|
||||
import static org.xbib.io.sshd.eddsa.math.ed25519.Ed25519LittleEndianEncoding.load_4;
|
||||
|
||||
/**
|
||||
* Class for reducing a huge integer modulo the group order q and
|
||||
* doing a combined multiply plus add plus reduce operation.
|
||||
* <p>
|
||||
* $q = 2^{252} + 27742317777372353535851937790883648493$.
|
||||
* <p>
|
||||
* Reviewed/commented by Bloody Rookie (nemproject@gmx.de)
|
||||
*/
|
||||
public class Ed25519ScalarOps implements ScalarOps {
|
||||
|
||||
/**
|
||||
* Reduction modulo the group order $q$.
|
||||
* <p>
|
||||
* Input:
|
||||
* $s[0]+256*s[1]+\dots+256^{63}*s[63] = s$
|
||||
* <p>
|
||||
* Output:
|
||||
* $s[0]+256*s[1]+\dots+256^{31}*s[31] = s \bmod q$
|
||||
* where $q = 2^{252} + 27742317777372353535851937790883648493$.
|
||||
*/
|
||||
public byte[] reduce(byte[] s) {
|
||||
// s0,..., s22 have 21 bits, s23 has 29 bits
|
||||
long s0 = 0x1FFFFF & load_3(s, 0);
|
||||
long s1 = 0x1FFFFF & (load_4(s, 2) >> 5);
|
||||
long s2 = 0x1FFFFF & (load_3(s, 5) >> 2);
|
||||
long s3 = 0x1FFFFF & (load_4(s, 7) >> 7);
|
||||
long s4 = 0x1FFFFF & (load_4(s, 10) >> 4);
|
||||
long s5 = 0x1FFFFF & (load_3(s, 13) >> 1);
|
||||
long s6 = 0x1FFFFF & (load_4(s, 15) >> 6);
|
||||
long s7 = 0x1FFFFF & (load_3(s, 18) >> 3);
|
||||
long s8 = 0x1FFFFF & load_3(s, 21);
|
||||
long s9 = 0x1FFFFF & (load_4(s, 23) >> 5);
|
||||
long s10 = 0x1FFFFF & (load_3(s, 26) >> 2);
|
||||
long s11 = 0x1FFFFF & (load_4(s, 28) >> 7);
|
||||
long s12 = 0x1FFFFF & (load_4(s, 31) >> 4);
|
||||
long s13 = 0x1FFFFF & (load_3(s, 34) >> 1);
|
||||
long s14 = 0x1FFFFF & (load_4(s, 36) >> 6);
|
||||
long s15 = 0x1FFFFF & (load_3(s, 39) >> 3);
|
||||
long s16 = 0x1FFFFF & load_3(s, 42);
|
||||
long s17 = 0x1FFFFF & (load_4(s, 44) >> 5);
|
||||
long s18 = 0x1FFFFF & (load_3(s, 47) >> 2);
|
||||
long s19 = 0x1FFFFF & (load_4(s, 49) >> 7);
|
||||
long s20 = 0x1FFFFF & (load_4(s, 52) >> 4);
|
||||
long s21 = 0x1FFFFF & (load_3(s, 55) >> 1);
|
||||
long s22 = 0x1FFFFF & (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;
|
||||
|
||||
/**
|
||||
* Lots of magic numbers :)
|
||||
* To understand what's going on below, note that
|
||||
*
|
||||
* (1) q = 2^252 + q0 where q0 = 27742317777372353535851937790883648493.
|
||||
* (2) s11 is the coefficient of 2^(11*21), s23 is the coefficient of 2^(^23*21) and 2^252 = 2^((23-11) * 21)).
|
||||
* (3) 2^252 congruent -q0 modulo q.
|
||||
* (4) -q0 = 666643 * 2^0 + 470296 * 2^21 + 654183 * 2^(2*21) - 997805 * 2^(3*21) + 136657 * 2^(4*21) - 683901 * 2^(5*21)
|
||||
*
|
||||
* Thus
|
||||
* s23 * 2^(23*11) = s23 * 2^(12*21) * 2^(11*21) = s3 * 2^252 * 2^(11*21) congruent
|
||||
* s23 * (666643 * 2^0 + 470296 * 2^21 + 654183 * 2^(2*21) - 997805 * 2^(3*21) + 136657 * 2^(4*21) - 683901 * 2^(5*21)) * 2^(11*21) modulo q =
|
||||
* s23 * (666643 * 2^(11*21) + 470296 * 2^(12*21) + 654183 * 2^(13*21) - 997805 * 2^(14*21) + 136657 * 2^(15*21) - 683901 * 2^(16*21)).
|
||||
*
|
||||
* The same procedure is then applied for s22,...,s18.
|
||||
*/
|
||||
s11 += s23 * 666643;
|
||||
s12 += s23 * 470296;
|
||||
s13 += s23 * 654183;
|
||||
s14 -= s23 * 997805;
|
||||
s15 += s23 * 136657;
|
||||
s16 -= s23 * 683901;
|
||||
// not used again
|
||||
//s23 = 0;
|
||||
|
||||
s10 += s22 * 666643;
|
||||
s11 += s22 * 470296;
|
||||
s12 += s22 * 654183;
|
||||
s13 -= s22 * 997805;
|
||||
s14 += s22 * 136657;
|
||||
s15 -= s22 * 683901;
|
||||
// not used again
|
||||
//s22 = 0;
|
||||
|
||||
s9 += s21 * 666643;
|
||||
s10 += s21 * 470296;
|
||||
s11 += s21 * 654183;
|
||||
s12 -= s21 * 997805;
|
||||
s13 += s21 * 136657;
|
||||
s14 -= s21 * 683901;
|
||||
// not used again
|
||||
//s21 = 0;
|
||||
|
||||
s8 += s20 * 666643;
|
||||
s9 += s20 * 470296;
|
||||
s10 += s20 * 654183;
|
||||
s11 -= s20 * 997805;
|
||||
s12 += s20 * 136657;
|
||||
s13 -= s20 * 683901;
|
||||
// not used again
|
||||
//s20 = 0;
|
||||
|
||||
s7 += s19 * 666643;
|
||||
s8 += s19 * 470296;
|
||||
s9 += s19 * 654183;
|
||||
s10 -= s19 * 997805;
|
||||
s11 += s19 * 136657;
|
||||
s12 -= s19 * 683901;
|
||||
// not used again
|
||||
//s19 = 0;
|
||||
|
||||
s6 += s18 * 666643;
|
||||
s7 += s18 * 470296;
|
||||
s8 += s18 * 654183;
|
||||
s9 -= s18 * 997805;
|
||||
s10 += s18 * 136657;
|
||||
s11 -= s18 * 683901;
|
||||
// not used again
|
||||
//s18 = 0;
|
||||
|
||||
/**
|
||||
* Time to reduce the coefficient in order not to get an overflow.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* Continue with above procedure.
|
||||
*/
|
||||
s5 += s17 * 666643;
|
||||
s6 += s17 * 470296;
|
||||
s7 += s17 * 654183;
|
||||
s8 -= s17 * 997805;
|
||||
s9 += s17 * 136657;
|
||||
s10 -= s17 * 683901;
|
||||
// not used again
|
||||
//s17 = 0;
|
||||
|
||||
s4 += s16 * 666643;
|
||||
s5 += s16 * 470296;
|
||||
s6 += s16 * 654183;
|
||||
s7 -= s16 * 997805;
|
||||
s8 += s16 * 136657;
|
||||
s9 -= s16 * 683901;
|
||||
// not used again
|
||||
//s16 = 0;
|
||||
|
||||
s3 += s15 * 666643;
|
||||
s4 += s15 * 470296;
|
||||
s5 += s15 * 654183;
|
||||
s6 -= s15 * 997805;
|
||||
s7 += s15 * 136657;
|
||||
s8 -= s15 * 683901;
|
||||
// not used again
|
||||
//s15 = 0;
|
||||
|
||||
s2 += s14 * 666643;
|
||||
s3 += s14 * 470296;
|
||||
s4 += s14 * 654183;
|
||||
s5 -= s14 * 997805;
|
||||
s6 += s14 * 136657;
|
||||
s7 -= s14 * 683901;
|
||||
// not used again
|
||||
//s14 = 0;
|
||||
|
||||
s1 += s13 * 666643;
|
||||
s2 += s13 * 470296;
|
||||
s3 += s13 * 654183;
|
||||
s4 -= s13 * 997805;
|
||||
s5 += s13 * 136657;
|
||||
s6 -= s13 * 683901;
|
||||
// not used again
|
||||
//s13 = 0;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
// set below
|
||||
//s12 = 0;
|
||||
|
||||
/**
|
||||
* Reduce coefficients again.
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
// set below
|
||||
//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;
|
||||
carry11 = s11 >> 21;
|
||||
s12 = carry11;
|
||||
s11 -= carry11 << 21;
|
||||
|
||||
// TODO-CR BR: Is it really needed to do it TWO times? (it doesn't hurt, just a question).
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
// not used again
|
||||
//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;
|
||||
|
||||
// s0, ..., s11 got 21 bits each.
|
||||
byte[] result = new byte[32];
|
||||
result[0] = (byte) s0;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* $(ab+c) \bmod q$
|
||||
* <p>
|
||||
* Input:
|
||||
* </p><ul>
|
||||
* <li>$a[0]+256*a[1]+\dots+256^{31}*a[31] = a$
|
||||
* <li>$b[0]+256*b[1]+\dots+256^{31}*b[31] = b$
|
||||
* <li>$c[0]+256*c[1]+\dots+256^{31}*c[31] = c$
|
||||
* </ul><p>
|
||||
* Output:
|
||||
* $result[0]+256*result[1]+\dots+256^{31}*result[31] = (ab+c) \bmod q$
|
||||
* where $q = 2^{252} + 27742317777372353535851937790883648493$.
|
||||
* <p>
|
||||
* See the comments in {@link #reduce(byte[])} for an explanation of the algorithm.
|
||||
*/
|
||||
public byte[] multiplyAndAdd(byte[] a, byte[] b, byte[] c) {
|
||||
long a0 = 0x1FFFFF & load_3(a, 0);
|
||||
long a1 = 0x1FFFFF & (load_4(a, 2) >> 5);
|
||||
long a2 = 0x1FFFFF & (load_3(a, 5) >> 2);
|
||||
long a3 = 0x1FFFFF & (load_4(a, 7) >> 7);
|
||||
long a4 = 0x1FFFFF & (load_4(a, 10) >> 4);
|
||||
long a5 = 0x1FFFFF & (load_3(a, 13) >> 1);
|
||||
long a6 = 0x1FFFFF & (load_4(a, 15) >> 6);
|
||||
long a7 = 0x1FFFFF & (load_3(a, 18) >> 3);
|
||||
long a8 = 0x1FFFFF & load_3(a, 21);
|
||||
long a9 = 0x1FFFFF & (load_4(a, 23) >> 5);
|
||||
long a10 = 0x1FFFFF & (load_3(a, 26) >> 2);
|
||||
long a11 = (load_4(a, 28) >> 7);
|
||||
long b0 = 0x1FFFFF & load_3(b, 0);
|
||||
long b1 = 0x1FFFFF & (load_4(b, 2) >> 5);
|
||||
long b2 = 0x1FFFFF & (load_3(b, 5) >> 2);
|
||||
long b3 = 0x1FFFFF & (load_4(b, 7) >> 7);
|
||||
long b4 = 0x1FFFFF & (load_4(b, 10) >> 4);
|
||||
long b5 = 0x1FFFFF & (load_3(b, 13) >> 1);
|
||||
long b6 = 0x1FFFFF & (load_4(b, 15) >> 6);
|
||||
long b7 = 0x1FFFFF & (load_3(b, 18) >> 3);
|
||||
long b8 = 0x1FFFFF & load_3(b, 21);
|
||||
long b9 = 0x1FFFFF & (load_4(b, 23) >> 5);
|
||||
long b10 = 0x1FFFFF & (load_3(b, 26) >> 2);
|
||||
long b11 = (load_4(b, 28) >> 7);
|
||||
long c0 = 0x1FFFFF & load_3(c, 0);
|
||||
long c1 = 0x1FFFFF & (load_4(c, 2) >> 5);
|
||||
long c2 = 0x1FFFFF & (load_3(c, 5) >> 2);
|
||||
long c3 = 0x1FFFFF & (load_4(c, 7) >> 7);
|
||||
long c4 = 0x1FFFFF & (load_4(c, 10) >> 4);
|
||||
long c5 = 0x1FFFFF & (load_3(c, 13) >> 1);
|
||||
long c6 = 0x1FFFFF & (load_4(c, 15) >> 6);
|
||||
long c7 = 0x1FFFFF & (load_3(c, 18) >> 3);
|
||||
long c8 = 0x1FFFFF & load_3(c, 21);
|
||||
long c9 = 0x1FFFFF & (load_4(c, 23) >> 5);
|
||||
long c10 = 0x1FFFFF & (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;
|
||||
// set below
|
||||
//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;
|
||||
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;
|
||||
// not used again
|
||||
//s23 = 0;
|
||||
|
||||
s10 += s22 * 666643;
|
||||
s11 += s22 * 470296;
|
||||
s12 += s22 * 654183;
|
||||
s13 -= s22 * 997805;
|
||||
s14 += s22 * 136657;
|
||||
s15 -= s22 * 683901;
|
||||
// not used again
|
||||
//s22 = 0;
|
||||
|
||||
s9 += s21 * 666643;
|
||||
s10 += s21 * 470296;
|
||||
s11 += s21 * 654183;
|
||||
s12 -= s21 * 997805;
|
||||
s13 += s21 * 136657;
|
||||
s14 -= s21 * 683901;
|
||||
// not used again
|
||||
//s21 = 0;
|
||||
|
||||
s8 += s20 * 666643;
|
||||
s9 += s20 * 470296;
|
||||
s10 += s20 * 654183;
|
||||
s11 -= s20 * 997805;
|
||||
s12 += s20 * 136657;
|
||||
s13 -= s20 * 683901;
|
||||
// not used again
|
||||
//s20 = 0;
|
||||
|
||||
s7 += s19 * 666643;
|
||||
s8 += s19 * 470296;
|
||||
s9 += s19 * 654183;
|
||||
s10 -= s19 * 997805;
|
||||
s11 += s19 * 136657;
|
||||
s12 -= s19 * 683901;
|
||||
// not used again
|
||||
//s19 = 0;
|
||||
|
||||
s6 += s18 * 666643;
|
||||
s7 += s18 * 470296;
|
||||
s8 += s18 * 654183;
|
||||
s9 -= s18 * 997805;
|
||||
s10 += s18 * 136657;
|
||||
s11 -= s18 * 683901;
|
||||
// not used again
|
||||
//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;
|
||||
// not used again
|
||||
//s17 = 0;
|
||||
|
||||
s4 += s16 * 666643;
|
||||
s5 += s16 * 470296;
|
||||
s6 += s16 * 654183;
|
||||
s7 -= s16 * 997805;
|
||||
s8 += s16 * 136657;
|
||||
s9 -= s16 * 683901;
|
||||
// not used again
|
||||
//s16 = 0;
|
||||
|
||||
s3 += s15 * 666643;
|
||||
s4 += s15 * 470296;
|
||||
s5 += s15 * 654183;
|
||||
s6 -= s15 * 997805;
|
||||
s7 += s15 * 136657;
|
||||
s8 -= s15 * 683901;
|
||||
// not used again
|
||||
//s15 = 0;
|
||||
|
||||
s2 += s14 * 666643;
|
||||
s3 += s14 * 470296;
|
||||
s4 += s14 * 654183;
|
||||
s5 -= s14 * 997805;
|
||||
s6 += s14 * 136657;
|
||||
s7 -= s14 * 683901;
|
||||
// not used again
|
||||
//s14 = 0;
|
||||
|
||||
s1 += s13 * 666643;
|
||||
s2 += s13 * 470296;
|
||||
s3 += s13 * 654183;
|
||||
s4 -= s13 * 997805;
|
||||
s5 += s13 * 136657;
|
||||
s6 -= s13 * 683901;
|
||||
// not used again
|
||||
//s13 = 0;
|
||||
|
||||
s0 += s12 * 666643;
|
||||
s1 += s12 * 470296;
|
||||
s2 += s12 * 654183;
|
||||
s3 -= s12 * 997805;
|
||||
s4 += s12 * 136657;
|
||||
s5 -= s12 * 683901;
|
||||
// set below
|
||||
//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;
|
||||
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;
|
||||
// set below
|
||||
//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;
|
||||
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;
|
||||
// not used again
|
||||
//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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
/**
|
||||
* Low-level, optimized implementation using Radix $2^{51}$ for Curve 25519.
|
||||
*/
|
||||
package org.xbib.io.sshd.eddsa.math.ed25519;
|
|
@ -1,6 +0,0 @@
|
|||
/**
|
||||
* Data structures that definie curves and fields, and the mathematical operaions on them.
|
||||
* Low-level implementation in bigint for any curve using BigIntegers,
|
||||
* and in ed25519 for Curve 25519 using Radix $2^{51}$.
|
||||
*/
|
||||
package org.xbib.io.sshd.eddsa.math;
|
|
@ -1,19 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
/**
|
||||
* Implementation of AlgorithmParameterSpec that holds the name of a named
|
||||
* EdDSA curve specification.
|
||||
*/
|
||||
public class EdDSAGenParameterSpec implements AlgorithmParameterSpec {
|
||||
private final String name;
|
||||
|
||||
public EdDSAGenParameterSpec(String stdName) {
|
||||
name = stdName;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Curve;
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
import org.xbib.io.sshd.eddsa.math.ScalarOps;
|
||||
|
||||
/**
|
||||
* EdDSA Curve specification that can also be referred to by name.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.math.Curve;
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.ed25519.Ed25519LittleEndianEncoding;
|
||||
import org.xbib.io.sshd.eddsa.math.ed25519.Ed25519ScalarOps;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The named EdDSA curves.
|
||||
*/
|
||||
public class EdDSANamedCurveTable {
|
||||
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 ed25519 = new EdDSANamedCurveSpec(
|
||||
"Ed25519",
|
||||
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>();
|
||||
|
||||
static {
|
||||
// RFC 8032
|
||||
defineCurve(ed25519);
|
||||
}
|
||||
|
||||
public static void defineCurve(EdDSANamedCurveSpec curve) {
|
||||
curves.put(curve.getName().toLowerCase(Locale.ENGLISH), curve);
|
||||
}
|
||||
|
||||
static void defineCurveAlias(String name, String alias) {
|
||||
EdDSANamedCurveSpec curve = curves.get(name.toLowerCase(Locale.ENGLISH));
|
||||
if (curve == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
curves.put(alias.toLowerCase(Locale.ENGLISH), curve);
|
||||
}
|
||||
|
||||
public static EdDSANamedCurveSpec getByName(String name) {
|
||||
return curves.get(name.toLowerCase(Locale.ENGLISH));
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.Curve;
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
import org.xbib.io.sshd.eddsa.math.ScalarOps;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
|
||||
//import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Parameter specification for an EdDSA algorithm.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @param curve the curve
|
||||
* @param hashAlgo the JCA string for the hash algorithm
|
||||
* @param sc the parameter L represented as ScalarOps
|
||||
* @param B the parameter 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the base (generator)
|
||||
*/
|
||||
public GroupElement getB() {
|
||||
return B;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashAlgo.hashCode() ^
|
||||
curve.hashCode() ^
|
||||
B.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
if (!(o instanceof EdDSAParameterSpec))
|
||||
return false;
|
||||
EdDSAParameterSpec s = (EdDSAParameterSpec) o;
|
||||
return hashAlgo.equals(s.getHashAlgorithm()) &&
|
||||
curve.equals(s.getCurve()) &&
|
||||
B.equals(s.getB());
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @param seed the private key
|
||||
* @param spec the parameter specification for this key
|
||||
* @throws IllegalArgumentException if seed length is wrong or hash algorithm is unsupported
|
||||
*/
|
||||
public EdDSAPrivateKeySpec(byte[] seed, EdDSAParameterSpec spec) {
|
||||
if (seed.length != spec.getCurve().getField().getb() / 8)
|
||||
throw new IllegalArgumentException("seed length is wrong");
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize directly from the hash.
|
||||
* getSeed() will return null if this constructor is used.
|
||||
*
|
||||
* @param spec the parameter specification for this key
|
||||
* @param h the private key
|
||||
* @throws IllegalArgumentException if hash length is wrong
|
||||
*/
|
||||
public EdDSAPrivateKeySpec(EdDSAParameterSpec spec, byte[] h) {
|
||||
if (h.length != spec.getCurve().getField().getb() / 4)
|
||||
throw new IllegalArgumentException("hash length is wrong");
|
||||
|
||||
this.seed = null;
|
||||
this.h = h;
|
||||
this.spec = spec;
|
||||
int b = spec.getCurve().getField().getb();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return will be null if constructed directly from the private key
|
||||
*/
|
||||
public byte[] getSeed() {
|
||||
return seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the hash
|
||||
*/
|
||||
public byte[] getH() {
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the private key
|
||||
*/
|
||||
public byte[] geta() {
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the public key
|
||||
*/
|
||||
public GroupElement getA() {
|
||||
return A;
|
||||
}
|
||||
|
||||
public EdDSAParameterSpec getParams() {
|
||||
return spec;
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.GroupElement;
|
||||
|
||||
import java.security.spec.KeySpec;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSAPublicKeySpec implements KeySpec {
|
||||
private final GroupElement A;
|
||||
private final GroupElement Aneg;
|
||||
private final EdDSAParameterSpec spec;
|
||||
|
||||
/**
|
||||
* @param pk the public key
|
||||
* @param spec the parameter specification for this key
|
||||
* @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;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
/**
|
||||
* Specifications for curves and keys, and a table for named curves.
|
||||
* Contains the following curves: Ed25519
|
||||
*/
|
||||
package org.xbib.io.sshd.eddsa.spec;
|
|
@ -1,54 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Ed25519TestVectors {
|
||||
public static class TestTuple {
|
||||
public static int numCases;
|
||||
public int caseNum;
|
||||
public byte[] seed;
|
||||
public byte[] pk;
|
||||
public byte[] message;
|
||||
public byte[] sig;
|
||||
|
||||
public TestTuple(String line) {
|
||||
caseNum = ++numCases;
|
||||
String[] x = line.split(":");
|
||||
seed = Utils.hexToBytes(x[0].substring(0, 64));
|
||||
pk = Utils.hexToBytes(x[1]);
|
||||
message = Utils.hexToBytes(x[2]);
|
||||
sig = Utils.hexToBytes(x[3].substring(0, 128));
|
||||
}
|
||||
}
|
||||
|
||||
public static Collection<TestTuple> testCases = getTestData("test.data");
|
||||
|
||||
public static Collection<TestTuple> getTestData(String fileName) {
|
||||
List<TestTuple> testCases = new ArrayList<TestTuple>();
|
||||
BufferedReader file = null;
|
||||
try {
|
||||
InputStream is = Ed25519TestVectors.class.getResourceAsStream(fileName);
|
||||
if (is == null)
|
||||
throw new IOException("Resource not found: " + fileName);
|
||||
file = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
while ((line = file.readLine()) != null) {
|
||||
testCases.add(new TestTuple(line));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (file != null) try { file.close(); } catch (IOException e) {}
|
||||
}
|
||||
return testCases;
|
||||
}
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSAEngineTest {
|
||||
static final byte[] TEST_SEED = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] TEST_PK = Utils.hexToBytes("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29");
|
||||
static final byte[] TEST_MSG = "This is a secret message".getBytes(Charset.forName("UTF-8"));
|
||||
static final byte[] TEST_MSG_SIG = Utils.hexToBytes("94825896c7075c31bcb81f06dba2bdcd9dcf16e79288d4b9f87c248215c8468d475f429f3de3b4a2cf67fe17077ae19686020364d6d4fa7a0174bab4a123ba0f");
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void testSign() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
//Signature sgr = Signature.getInstance("EdDSA", "I2P");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
|
||||
for (Ed25519TestVectors.TestTuple testCase : Ed25519TestVectors.testCases) {
|
||||
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(testCase.seed, spec);
|
||||
PrivateKey sKey = new EdDSAPrivateKey(privKey);
|
||||
sgr.initSign(sKey);
|
||||
|
||||
sgr.update(testCase.message);
|
||||
|
||||
assertThat("Test case " + testCase.caseNum + " failed",
|
||||
sgr.sign(), is(equalTo(testCase.sig)));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerify() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
//Signature sgr = Signature.getInstance("EdDSA", "I2P");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
for (Ed25519TestVectors.TestTuple testCase : Ed25519TestVectors.testCases) {
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(testCase.pk, spec);
|
||||
PublicKey vKey = new EdDSAPublicKey(pubKey);
|
||||
sgr.initVerify(vKey);
|
||||
|
||||
sgr.update(testCase.message);
|
||||
|
||||
assertThat("Test case " + testCase.caseNum + " failed",
|
||||
sgr.verify(testCase.sig), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that a wrong-length signature throws an IAE.
|
||||
*/
|
||||
@Test
|
||||
public void testVerifyWrongSigLength() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
//Signature sgr = Signature.getInstance("EdDSA", "I2P");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(TEST_PK, spec);
|
||||
PublicKey vKey = new EdDSAPublicKey(pubKey);
|
||||
sgr.initVerify(vKey);
|
||||
|
||||
sgr.update(TEST_MSG);
|
||||
|
||||
exception.expect(SignatureException.class);
|
||||
exception.expectMessage("signature length is wrong");
|
||||
sgr.verify(new byte[] {0});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignResetsForReuse() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(TEST_SEED, spec);
|
||||
PrivateKey sKey = new EdDSAPrivateKey(privKey);
|
||||
sgr.initSign(sKey);
|
||||
|
||||
// First usage
|
||||
sgr.update(new byte[] {0});
|
||||
sgr.sign();
|
||||
|
||||
// Second usage
|
||||
sgr.update(TEST_MSG);
|
||||
assertThat("Second sign failed", sgr.sign(), is(equalTo(TEST_MSG_SIG)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyResetsForReuse() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(TEST_PK, spec);
|
||||
PublicKey vKey = new EdDSAPublicKey(pubKey);
|
||||
sgr.initVerify(vKey);
|
||||
|
||||
// First usage
|
||||
sgr.update(new byte[] {0});
|
||||
sgr.verify(TEST_MSG_SIG);
|
||||
|
||||
// Second usage
|
||||
sgr.update(TEST_MSG);
|
||||
assertThat("Second verify failed", sgr.verify(TEST_MSG_SIG), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignOneShotMode() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(TEST_SEED, spec);
|
||||
PrivateKey sKey = new EdDSAPrivateKey(privKey);
|
||||
sgr.initSign(sKey);
|
||||
sgr.setParameter(EdDSAEngine.ONE_SHOT_MODE);
|
||||
|
||||
sgr.update(TEST_MSG);
|
||||
|
||||
assertThat("One-shot mode sign failed", sgr.sign(), is(equalTo(TEST_MSG_SIG)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyOneShotMode() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(TEST_PK, spec);
|
||||
PublicKey vKey = new EdDSAPublicKey(pubKey);
|
||||
sgr.initVerify(vKey);
|
||||
sgr.setParameter(EdDSAEngine.ONE_SHOT_MODE);
|
||||
|
||||
sgr.update(TEST_MSG);
|
||||
|
||||
assertThat("One-shot mode verify failed", sgr.verify(TEST_MSG_SIG), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignOneShotModeMultipleUpdates() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(TEST_SEED, spec);
|
||||
PrivateKey sKey = new EdDSAPrivateKey(privKey);
|
||||
sgr.initSign(sKey);
|
||||
sgr.setParameter(EdDSAEngine.ONE_SHOT_MODE);
|
||||
|
||||
sgr.update(TEST_MSG);
|
||||
|
||||
exception.expect(SignatureException.class);
|
||||
exception.expectMessage("update() already called");
|
||||
sgr.update(TEST_MSG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyOneShotModeMultipleUpdates() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(TEST_PK, spec);
|
||||
Signature sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
PublicKey vKey = new EdDSAPublicKey(pubKey);
|
||||
sgr.initVerify(vKey);
|
||||
sgr.setParameter(EdDSAEngine.ONE_SHOT_MODE);
|
||||
|
||||
sgr.update(TEST_MSG);
|
||||
|
||||
exception.expect(SignatureException.class);
|
||||
exception.expectMessage("update() already called");
|
||||
sgr.update(TEST_MSG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignOneShot() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(TEST_SEED, spec);
|
||||
EdDSAEngine sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
PrivateKey sKey = new EdDSAPrivateKey(privKey);
|
||||
sgr.initSign(sKey);
|
||||
|
||||
assertThat("signOneShot() failed", sgr.signOneShot(TEST_MSG), is(equalTo(TEST_MSG_SIG)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyOneShot() throws Exception {
|
||||
EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(TEST_PK, spec);
|
||||
EdDSAEngine sgr = new EdDSAEngine(MessageDigest.getInstance(spec.getHashAlgorithm()));
|
||||
PublicKey vKey = new EdDSAPublicKey(pubKey);
|
||||
sgr.initVerify(vKey);
|
||||
|
||||
assertThat("verifyOneShot() failed", sgr.verifyOneShot(TEST_MSG, TEST_MSG_SIG), is(true));
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSAPrivateKeyTest {
|
||||
/**
|
||||
* The example private key MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC
|
||||
* from https://tools.ietf.org/html/draft-ietf-curdle-pkix-04#section-10.3
|
||||
*/
|
||||
static final byte[] TEST_PRIVKEY = Utils.hexToBytes("302e020100300506032b657004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842");
|
||||
|
||||
static final byte[] TEST_PRIVKEY_NULL_PARAMS = Utils.hexToBytes("3030020100300706032b6570050004220420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842");
|
||||
static final byte[] TEST_PRIVKEY_OLD = Utils.hexToBytes("302f020100300806032b65640a01010420d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842");
|
||||
|
||||
@Test
|
||||
public void testDecodeAndEncode() throws Exception {
|
||||
// Decode
|
||||
PKCS8EncodedKeySpec encoded = new PKCS8EncodedKeySpec(TEST_PRIVKEY);
|
||||
EdDSAPrivateKey keyIn = new EdDSAPrivateKey(encoded);
|
||||
|
||||
// Encode
|
||||
EdDSAPrivateKeySpec decoded = new EdDSAPrivateKeySpec(
|
||||
keyIn.getSeed(),
|
||||
keyIn.getH(),
|
||||
keyIn.geta(),
|
||||
keyIn.getA(),
|
||||
keyIn.getParams());
|
||||
EdDSAPrivateKey keyOut = new EdDSAPrivateKey(decoded);
|
||||
|
||||
// Check
|
||||
assertThat(keyOut.getEncoded(), is(equalTo(TEST_PRIVKEY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeWithNullAndEncode() throws Exception {
|
||||
// Decode
|
||||
PKCS8EncodedKeySpec encoded = new PKCS8EncodedKeySpec(TEST_PRIVKEY_NULL_PARAMS);
|
||||
EdDSAPrivateKey keyIn = new EdDSAPrivateKey(encoded);
|
||||
|
||||
// Encode
|
||||
EdDSAPrivateKeySpec decoded = new EdDSAPrivateKeySpec(
|
||||
keyIn.getSeed(),
|
||||
keyIn.getH(),
|
||||
keyIn.geta(),
|
||||
keyIn.getA(),
|
||||
keyIn.getParams());
|
||||
EdDSAPrivateKey keyOut = new EdDSAPrivateKey(decoded);
|
||||
|
||||
// Check
|
||||
assertThat(keyOut.getEncoded(), is(equalTo(TEST_PRIVKEY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReEncodeOldEncoding() throws Exception {
|
||||
// Decode
|
||||
PKCS8EncodedKeySpec encoded = new PKCS8EncodedKeySpec(TEST_PRIVKEY_OLD);
|
||||
EdDSAPrivateKey keyIn = new EdDSAPrivateKey(encoded);
|
||||
|
||||
// Encode
|
||||
EdDSAPrivateKeySpec decoded = new EdDSAPrivateKeySpec(
|
||||
keyIn.getSeed(),
|
||||
keyIn.getH(),
|
||||
keyIn.geta(),
|
||||
keyIn.getA(),
|
||||
keyIn.getParams());
|
||||
EdDSAPrivateKey keyOut = new EdDSAPrivateKey(decoded);
|
||||
|
||||
// Check
|
||||
assertThat(keyOut.getEncoded(), is(equalTo(TEST_PRIVKEY)));
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSAPublicKeyTest {
|
||||
/**
|
||||
* The example public key MCowBQYDK2VwAyEAGb9ECWmEzf6FQbrBZ9w7lshQhqowtrbLDFw4rXAxZuE=
|
||||
* from https://tools.ietf.org/html/draft-ietf-curdle-pkix-04#section-10.1
|
||||
*/
|
||||
static final byte[] TEST_PUBKEY = Utils.hexToBytes("302a300506032b657003210019bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1");
|
||||
|
||||
static final byte[] TEST_PUBKEY_NULL_PARAMS = Utils.hexToBytes("302c300706032b6570050003210019bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1");
|
||||
static final byte[] TEST_PUBKEY_OLD = Utils.hexToBytes("302d300806032b65640a010103210019bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1");
|
||||
|
||||
@Test
|
||||
public void testDecodeAndEncode() throws Exception {
|
||||
// Decode
|
||||
X509EncodedKeySpec encoded = new X509EncodedKeySpec(TEST_PUBKEY);
|
||||
EdDSAPublicKey keyIn = new EdDSAPublicKey(encoded);
|
||||
|
||||
// Encode
|
||||
EdDSAPublicKeySpec decoded = new EdDSAPublicKeySpec(
|
||||
keyIn.getA(),
|
||||
keyIn.getParams());
|
||||
EdDSAPublicKey keyOut = new EdDSAPublicKey(decoded);
|
||||
|
||||
// Check
|
||||
assertThat(keyOut.getEncoded(), is(equalTo(TEST_PUBKEY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecodeWithNullAndEncode() throws Exception {
|
||||
// Decode
|
||||
X509EncodedKeySpec encoded = new X509EncodedKeySpec(TEST_PUBKEY_NULL_PARAMS);
|
||||
EdDSAPublicKey keyIn = new EdDSAPublicKey(encoded);
|
||||
|
||||
// Encode
|
||||
EdDSAPublicKeySpec decoded = new EdDSAPublicKeySpec(
|
||||
keyIn.getA(),
|
||||
keyIn.getParams());
|
||||
EdDSAPublicKey keyOut = new EdDSAPublicKey(decoded);
|
||||
|
||||
// Check
|
||||
assertThat(keyOut.getEncoded(), is(equalTo(TEST_PUBKEY)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReEncodeOldEncoding() throws Exception {
|
||||
// Decode
|
||||
X509EncodedKeySpec encoded = new X509EncodedKeySpec(TEST_PUBKEY_OLD);
|
||||
EdDSAPublicKey keyIn = new EdDSAPublicKey(encoded);
|
||||
|
||||
// Encode
|
||||
EdDSAPublicKeySpec decoded = new EdDSAPublicKeySpec(
|
||||
keyIn.getA(),
|
||||
keyIn.getParams());
|
||||
EdDSAPublicKey keyOut = new EdDSAPublicKey(decoded);
|
||||
|
||||
// Check
|
||||
assertThat(keyOut.getEncoded(), is(equalTo(TEST_PUBKEY)));
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Security;
|
||||
import java.security.Signature;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSASecurityProviderTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void canGetInstancesWhenProviderIsPresent() throws Exception {
|
||||
Security.addProvider(new EdDSASecurityProvider());
|
||||
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EdDSA", "EdDSA");
|
||||
KeyFactory keyFac = KeyFactory.getInstance("EdDSA", "EdDSA");
|
||||
Signature sgr = Signature.getInstance("NONEwithEdDSA", "EdDSA");
|
||||
|
||||
Security.removeProvider("EdDSA");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cannotGetInstancesWhenProviderIsNotPresent() throws Exception {
|
||||
exception.expect(NoSuchProviderException.class);
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EdDSA", "EdDSA");
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa;
|
||||
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* additional test by the NEM project team.
|
||||
*
|
||||
*/
|
||||
public class UtilsTest {
|
||||
private static final String hex1 = "3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29";
|
||||
private static final String hex2 = "47a3f5b71494bcd961f3a4e859a238d6eaf8e648746d2f56a89b5e236f98d45f";
|
||||
private static final String hex3 = "5fd396e4a2b5dc9078f57e3ab5a87c28fd128e5f78cc4a97f4122dc45f6e4bb9";
|
||||
private static final byte[] bytes1 = { 59, 106, 39, -68, -50, -74, -92, 45, 98, -93, -88, -48, 42, 111, 13, 115,
|
||||
101, 50, 21, 119, 29, -30, 67, -90, 58, -64, 72, -95, -117, 89, -38, 41 };
|
||||
private static final byte[] bytes2 = { 71, -93, -11, -73, 20, -108, -68, -39, 97, -13, -92, -24, 89, -94, 56, -42,
|
||||
-22, -8, -26, 72, 116, 109, 47, 86, -88, -101, 94, 35, 111, -104, -44, 95 };
|
||||
private static final byte[] bytes3 = { 95, -45, -106, -28, -94, -75, -36, -112, 120, -11, 126, 58, -75, -88, 124, 40,
|
||||
-3, 18, -114, 95, 120, -52, 74, -105, -12, 18, 45, -60, 95, 110, 75, -71 };
|
||||
|
||||
/**
|
||||
* Test method for {@link Utils#equal(int, int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testIntEqual() {
|
||||
assertThat(Utils.equal(0, 0), is(1));
|
||||
assertThat(Utils.equal(1, 1), is(1));
|
||||
assertThat(Utils.equal(1, 0), is(0));
|
||||
assertThat(Utils.equal(1, 127), is(0));
|
||||
assertThat(Utils.equal(-127, 127), is(0));
|
||||
assertThat(Utils.equal(-42, -42), is(1));
|
||||
assertThat(Utils.equal(255, 255), is(1));
|
||||
assertThat(Utils.equal(-255, -256), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsReturnsOneForEqualByteArrays() {
|
||||
final SecureRandom random = new SecureRandom();
|
||||
final byte[] bytes1 = new byte[32];
|
||||
final byte[] bytes2 = new byte[32];
|
||||
for (int i=0; i<100; i++) {
|
||||
random.nextBytes(bytes1);
|
||||
System.arraycopy(bytes1, 0, bytes2, 0, 32);
|
||||
Assert.assertThat(Utils.equal(bytes1, bytes2), IsEqual.equalTo(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsReturnsZeroForUnequalByteArrays() {
|
||||
final SecureRandom random = new SecureRandom();
|
||||
final byte[] bytes1 = new byte[32];
|
||||
final byte[] bytes2 = new byte[32];
|
||||
random.nextBytes(bytes1);
|
||||
for (int i=0; i<32; i++) {
|
||||
System.arraycopy(bytes1, 0, bytes2, 0, 32);
|
||||
bytes2[i] = (byte)(bytes2[i] ^ 0xff);
|
||||
Assert.assertThat(Utils.equal(bytes1, bytes2), IsEqual.equalTo(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link Utils#equal(byte[], byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testByteArrayEqual() {
|
||||
byte[] zero = new byte[32];
|
||||
byte[] one = new byte[32];
|
||||
one[0] = 1;
|
||||
|
||||
assertThat(Utils.equal(zero, zero), is(1));
|
||||
assertThat(Utils.equal(one, one), is(1));
|
||||
assertThat(Utils.equal(one, zero), is(0));
|
||||
assertThat(Utils.equal(zero, one), is(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link Utils#negative(int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testNegative() {
|
||||
assertThat(Utils.negative(0), is(0));
|
||||
assertThat(Utils.negative(1), is(0));
|
||||
assertThat(Utils.negative(-1), is(1));
|
||||
assertThat(Utils.negative(32), is(0));
|
||||
assertThat(Utils.negative(-100), is(1));
|
||||
assertThat(Utils.negative(127), is(0));
|
||||
assertThat(Utils.negative(-255), is(1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link Utils#bit(byte[], int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testBit() {
|
||||
assertThat(Utils.bit(new byte[] {0}, 0), is(0));
|
||||
assertThat(Utils.bit(new byte[] {8}, 3), is(1));
|
||||
assertThat(Utils.bit(new byte[] {1, 2, 3}, 9), is(1));
|
||||
assertThat(Utils.bit(new byte[] {1, 2, 3}, 15), is(0));
|
||||
assertThat(Utils.bit(new byte[] {1, 2, 3}, 16), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hexToBytesReturnsCorrectByteArray() {
|
||||
Assert.assertThat(Utils.hexToBytes(hex1), IsEqual.equalTo(bytes1));
|
||||
Assert.assertThat(Utils.hexToBytes(hex2), IsEqual.equalTo(bytes2));
|
||||
Assert.assertThat(Utils.hexToBytes(hex3), IsEqual.equalTo(bytes3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bytesToHexReturnsCorrectHexString() {
|
||||
Assert.assertThat(Utils.bytesToHex(bytes1), IsEqual.equalTo(hex1));
|
||||
Assert.assertThat(Utils.bytesToHex(bytes2), IsEqual.equalTo(hex2));
|
||||
Assert.assertThat(Utils.bytesToHex(bytes3), IsEqual.equalTo(hex3));
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.hamcrest.core.IsNot;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Tests rely on the BigInteger class.
|
||||
*/
|
||||
public abstract class AbstractFieldElementTest {
|
||||
|
||||
protected abstract FieldElement getRandomFieldElement();
|
||||
protected abstract BigInteger toBigInteger(FieldElement f);
|
||||
protected abstract BigInteger getQ();
|
||||
protected abstract Field getField();
|
||||
|
||||
protected abstract FieldElement getZeroFieldElement();
|
||||
protected abstract FieldElement getNonZeroFieldElement();
|
||||
|
||||
@Test
|
||||
public void isNonZeroReturnsFalseIfFieldElementIsZero() {
|
||||
final FieldElement f = getZeroFieldElement();
|
||||
Assert.assertThat(f.isNonZero(), IsEqual.equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNonZeroReturnsTrueIfFieldElementIsNonZero() {
|
||||
final FieldElement f = getNonZeroFieldElement();
|
||||
Assert.assertThat(f.isNonZero(), IsEqual.equalTo(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final FieldElement f2 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
final BigInteger b2 = toBigInteger(f2);
|
||||
|
||||
final FieldElement f3 = f1.add(f2);
|
||||
final BigInteger b3 = toBigInteger(f3).mod(getQ());
|
||||
Assert.assertThat(b3, IsEqual.equalTo(b1.add(b2).mod(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subtractReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final FieldElement f2 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
final BigInteger b2 = toBigInteger(f2);
|
||||
|
||||
final FieldElement f3 = f1.subtract(f2);
|
||||
final BigInteger b3 = toBigInteger(f3).mod(getQ());
|
||||
|
||||
Assert.assertThat(b3, IsEqual.equalTo(b1.subtract(b2).mod(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negateReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
|
||||
final FieldElement f2 = f1.negate();
|
||||
final BigInteger b2 = toBigInteger(f2).mod(getQ());
|
||||
|
||||
Assert.assertThat(b2, IsEqual.equalTo(b1.negate().mod(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiplyReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final FieldElement f2 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
final BigInteger b2 = toBigInteger(f2);
|
||||
|
||||
final FieldElement f3 = f1.multiply(f2);
|
||||
final BigInteger b3 = toBigInteger(f3).mod(getQ());
|
||||
|
||||
Assert.assertThat(b3, IsEqual.equalTo(b1.multiply(b2).mod(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void squareReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
|
||||
final FieldElement f2 = f1.square();
|
||||
final BigInteger b2 = toBigInteger(f2).mod(getQ());
|
||||
|
||||
Assert.assertThat(b2, IsEqual.equalTo(b1.multiply(b1).mod(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void squareAndDoubleReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
|
||||
final FieldElement f2 = f1.squareAndDouble();
|
||||
final BigInteger b2 = toBigInteger(f2).mod(getQ());
|
||||
|
||||
Assert.assertThat(b2, IsEqual.equalTo(b1.multiply(b1).multiply(new BigInteger("2")).mod(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invertReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
|
||||
final FieldElement f2 = f1.invert();
|
||||
final BigInteger b2 = toBigInteger(f2).mod(getQ());
|
||||
|
||||
Assert.assertThat(b2, IsEqual.equalTo(b1.modInverse(getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pow22523ReturnsCorrectResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final BigInteger b1 = toBigInteger(f1);
|
||||
|
||||
final FieldElement f2 = f1.pow22523();
|
||||
final BigInteger b2 = toBigInteger(f2).mod(getQ());
|
||||
|
||||
Assert.assertThat(b2, IsEqual.equalTo(b1.modPow(BigInteger.ONE.shiftLeft(252).subtract(new BigInteger("3")), getQ())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cmovReturnsCorrectResult() {
|
||||
final FieldElement zero = getZeroFieldElement();
|
||||
final FieldElement nz = getNonZeroFieldElement();
|
||||
final FieldElement f = getRandomFieldElement();
|
||||
|
||||
Assert.assertThat(zero.cmov(nz, 0), IsEqual.equalTo(zero));
|
||||
Assert.assertThat(zero.cmov(nz, 1), IsEqual.equalTo(nz));
|
||||
|
||||
Assert.assertThat(f.cmov(nz, 0), IsEqual.equalTo(f));
|
||||
Assert.assertThat(f.cmov(nz, 1), IsEqual.equalTo(nz));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsOnlyReturnsTrueForEquivalentObjects() {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final FieldElement f2 = getField().getEncoding().decode(f1.toByteArray());
|
||||
final FieldElement f3 = getRandomFieldElement();
|
||||
final FieldElement f4 = getRandomFieldElement();
|
||||
|
||||
Assert.assertThat(f1, IsEqual.equalTo(f2));
|
||||
Assert.assertThat(f1, IsNot.not(IsEqual.equalTo(f3)));
|
||||
Assert.assertThat(f1, IsNot.not(IsEqual.equalTo(f4)));
|
||||
Assert.assertThat(f3, IsNot.not(IsEqual.equalTo(f4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodesAreEqualForEquivalentObjects() {
|
||||
final FieldElement f1 = getRandomFieldElement();
|
||||
final FieldElement f2 = getField().getEncoding().decode(f1.toByteArray());
|
||||
final FieldElement f3 = getRandomFieldElement();
|
||||
final FieldElement f4 = getRandomFieldElement();
|
||||
|
||||
Assert.assertThat(f1.hashCode(), IsEqual.equalTo(f2.hashCode()));
|
||||
Assert.assertThat(f1.hashCode(), IsNot.not(IsEqual.equalTo(f3.hashCode())));
|
||||
Assert.assertThat(f1.hashCode(), IsNot.not(IsEqual.equalTo(f4.hashCode())));
|
||||
Assert.assertThat(f3.hashCode(), IsNot.not(IsEqual.equalTo(f4.hashCode())));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Based on the tests in checkparams.py from the Python Ed25519 implementation.
|
||||
*
|
||||
*/
|
||||
public class ConstantsTest {
|
||||
static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
static final Curve curve = ed25519.getCurve();
|
||||
|
||||
static final FieldElement ZERO = curve.getField().ZERO;
|
||||
static final FieldElement ONE = curve.getField().ONE;
|
||||
static final FieldElement TWO = curve.getField().TWO;
|
||||
|
||||
static final GroupElement P3_ZERO = GroupElement.p3(curve, ZERO, ONE, ONE, ZERO);
|
||||
|
||||
@Test
|
||||
public void testb() {
|
||||
int b = curve.getField().getb();
|
||||
assertThat(b, is(greaterThanOrEqualTo(10)));
|
||||
try {
|
||||
MessageDigest h = MessageDigest.getInstance(ed25519.getHashAlgorithm());
|
||||
assertThat(8 * h.getDigestLength(), is(equalTo(2 * b)));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*@Test
|
||||
public void testq() {
|
||||
FieldElement q = curve.getField().getQ();
|
||||
assertThat(TWO.modPow(q.subtractOne(), q), is(equalTo(ONE)));
|
||||
assertThat(q.mod(curve.getField().FOUR), is(equalTo(ONE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testl() {
|
||||
int b = curve.getField().getb();
|
||||
BigInteger l = ed25519.getL();
|
||||
assertThat(TWO.modPow(l.subtract(BigInteger.ONE), l), is(equalTo(ONE)));
|
||||
assertThat(l, is(greaterThanOrEqualTo(BigInteger.valueOf(2).pow(b-4))));
|
||||
assertThat(l, is(lessThanOrEqualTo(BigInteger.valueOf(2).pow(b-3))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testd() {
|
||||
FieldElement q = curve.getField().getQ();
|
||||
FieldElement qm1 = q.subtractOne();
|
||||
assertThat(curve.getD().modPow(qm1.divide(curve.getField().TWO), q), is(equalTo(qm1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testI() {
|
||||
FieldElement q = curve.getField().getQ();
|
||||
assertThat(curve.getI().modPow(curve.getField().TWO, q), is(equalTo(q.subtractOne())));
|
||||
}*/
|
||||
|
||||
@Test
|
||||
public void testB() {
|
||||
GroupElement B = ed25519.getB();
|
||||
assertThat(B.isOnCurve(curve), is(true));
|
||||
//assertThat(B.scalarMultiply(new BigIntegerLittleEndianEncoding().encode(ed25519.getL(), curve.getField().getb()/8)), is(equalTo(P3_ZERO)));
|
||||
}
|
||||
}
|
|
@ -1,865 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Ed25519TestVectors;
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.hamcrest.core.IsNot;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Additional tests by NEM project team.
|
||||
*
|
||||
*/
|
||||
public class GroupElementTest {
|
||||
static final byte[] BYTES_ZEROZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_ONEONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000080");
|
||||
static final byte[] BYTES_TENZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_ONETEN = Utils.hexToBytes("0a00000000000000000000000000000000000000000000000000000000000080");
|
||||
|
||||
static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
static final Curve curve = ed25519.getCurve();
|
||||
|
||||
static final FieldElement ZERO = curve.getField().ZERO;
|
||||
static final FieldElement ONE = curve.getField().ONE;
|
||||
static final FieldElement TWO = curve.getField().TWO;
|
||||
static final FieldElement TEN = curve.getField().fromByteArray(Utils.hexToBytes("0a00000000000000000000000000000000000000000000000000000000000000"));
|
||||
|
||||
static final GroupElement P2_ZERO = GroupElement.p2(curve, ZERO, ONE, ONE);
|
||||
|
||||
static final FieldElement[] PKR = new FieldElement[] {
|
||||
curve.getField().fromByteArray(Utils.hexToBytes("5849722e338aced7b50c7f0e9328f9a10c847b08e40af5c5b0577b0fd8984f15")),
|
||||
curve.getField().fromByteArray(Utils.hexToBytes("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29"))
|
||||
};
|
||||
static final byte[] BYTES_PKR = Utils.hexToBytes("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29");
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#p2(Curve, FieldElement, FieldElement, FieldElement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testP2() {
|
||||
final GroupElement t = GroupElement.p2(curve, ZERO, ONE, ONE);
|
||||
assertThat(t.curve, is(equalTo(curve)));
|
||||
assertThat(t.repr, is(GroupElement.Representation.P2));
|
||||
assertThat(t.X, is(ZERO));
|
||||
assertThat(t.Y, is(ONE));
|
||||
assertThat(t.Z, is(ONE));
|
||||
assertThat(t.T, is((FieldElement) null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#p3(Curve, FieldElement, FieldElement, FieldElement, FieldElement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testP3() {
|
||||
final GroupElement t = GroupElement.p3(curve, ZERO, ONE, ONE, ZERO);
|
||||
assertThat(t.curve, is(equalTo(curve)));
|
||||
assertThat(t.repr, is(GroupElement.Representation.P3));
|
||||
assertThat(t.X, is(ZERO));
|
||||
assertThat(t.Y, is(ONE));
|
||||
assertThat(t.Z, is(ONE));
|
||||
assertThat(t.T, is(ZERO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#p1p1(Curve, FieldElement, FieldElement, FieldElement, FieldElement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testP1p1() {
|
||||
final GroupElement t = GroupElement.p1p1(curve, ZERO, ONE, ONE, ONE);
|
||||
assertThat(t.curve, is(equalTo(curve)));
|
||||
assertThat(t.repr, is(GroupElement.Representation.P1P1));
|
||||
assertThat(t.X, is(ZERO));
|
||||
assertThat(t.Y, is(ONE));
|
||||
assertThat(t.Z, is(ONE));
|
||||
assertThat(t.T, is(ONE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#precomp(Curve, FieldElement, FieldElement, FieldElement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testPrecomp() {
|
||||
final GroupElement t = GroupElement.precomp(curve, ONE, ONE, ZERO);
|
||||
assertThat(t.curve, is(equalTo(curve)));
|
||||
assertThat(t.repr, is(GroupElement.Representation.PRECOMP));
|
||||
assertThat(t.X, is(ONE));
|
||||
assertThat(t.Y, is(ONE));
|
||||
assertThat(t.Z, is(ZERO));
|
||||
assertThat(t.T, is((FieldElement) null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#cached(Curve, FieldElement, FieldElement, FieldElement, FieldElement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCached() {
|
||||
final GroupElement t = GroupElement.cached(curve, ONE, ONE, ONE, ZERO);
|
||||
assertThat(t.curve, is(equalTo(curve)));
|
||||
assertThat(t.repr, is(GroupElement.Representation.CACHED));
|
||||
assertThat(t.X, is(ONE));
|
||||
assertThat(t.Y, is(ONE));
|
||||
assertThat(t.Z, is(ONE));
|
||||
assertThat(t.T, is(ZERO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#GroupElement(Curve, GroupElement.Representation, FieldElement, FieldElement, FieldElement, FieldElement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGroupElementCurveRepresentationFieldElementFieldElementFieldElementFieldElement() {
|
||||
final GroupElement t = new GroupElement(curve, GroupElement.Representation.P3, ZERO, ONE, ONE, ZERO);
|
||||
assertThat(t.curve, is(equalTo(curve)));
|
||||
assertThat(t.repr, is(GroupElement.Representation.P3));
|
||||
assertThat(t.X, is(ZERO));
|
||||
assertThat(t.Y, is(ONE));
|
||||
assertThat(t.Z, is(ONE));
|
||||
assertThat(t.T, is(ZERO));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link GroupElement#GroupElement(Curve, byte[])} and
|
||||
* {@link GroupElement#toByteArray()} against valid public keys.
|
||||
*/
|
||||
@Test
|
||||
public void testToAndFromByteArray() {
|
||||
GroupElement t;
|
||||
for (Ed25519TestVectors.TestTuple testCase : Ed25519TestVectors.testCases) {
|
||||
t = new GroupElement(curve, testCase.pk);
|
||||
assertThat("Test case " + testCase.caseNum + " failed",
|
||||
t.toByteArray(), is(equalTo(testCase.pk)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#GroupElement(Curve, byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testGroupElementByteArray() {
|
||||
final GroupElement t = new GroupElement(curve, BYTES_PKR);
|
||||
final GroupElement s = GroupElement.p3(curve, PKR[0], PKR[1], ONE, PKR[0].multiply(PKR[1]));
|
||||
assertThat(t, is(equalTo(s)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorUsingByteArrayReturnsExpectedResult() {
|
||||
for (int i=0; i<100; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
final byte[] bytes = g.toByteArray();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = new GroupElement(curve, bytes);
|
||||
final GroupElement h2 = MathUtils.toGroupElement(bytes);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h1, IsEqual.equalTo(h2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#toByteArray()}.
|
||||
* <p>
|
||||
* TODO 20141001 BR: why test with points which are not on the curve?
|
||||
*/
|
||||
@Test
|
||||
public void testToByteArray() {
|
||||
byte[] zerozero = GroupElement.p2(curve, ZERO, ZERO, ONE).toByteArray();
|
||||
assertThat(zerozero.length, is(equalTo(BYTES_ZEROZERO.length)));
|
||||
assertThat(zerozero, is(equalTo(BYTES_ZEROZERO)));
|
||||
|
||||
byte[] oneone = GroupElement.p2(curve, ONE, ONE, ONE).toByteArray();
|
||||
assertThat(oneone.length, is(equalTo(BYTES_ONEONE.length)));
|
||||
assertThat(oneone, is(equalTo(BYTES_ONEONE)));
|
||||
|
||||
byte[] tenzero = GroupElement.p2(curve, TEN, ZERO, ONE).toByteArray();
|
||||
assertThat(tenzero.length, is(equalTo(BYTES_TENZERO.length)));
|
||||
assertThat(tenzero, is(equalTo(BYTES_TENZERO)));
|
||||
|
||||
byte[] oneten = GroupElement.p2(curve, ONE, TEN, ONE).toByteArray();
|
||||
assertThat(oneten.length, is(equalTo(BYTES_ONETEN.length)));
|
||||
assertThat(oneten, is(equalTo(BYTES_ONETEN)));
|
||||
|
||||
byte[] pkr = GroupElement.p2(curve, PKR[0], PKR[1], ONE).toByteArray();
|
||||
assertThat(pkr.length, is(equalTo(BYTES_PKR.length)));
|
||||
assertThat(pkr, is(equalTo(BYTES_PKR)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toByteArrayReturnsExpectedResult() {
|
||||
for (int i=0; i<100; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final byte[] gBytes = g.toByteArray();
|
||||
final byte[] bytes = MathUtils.toByteArray(MathUtils.toBigInteger(g.getY()));
|
||||
if (MathUtils.toBigInteger(g.getX()).mod(new BigInteger("2")).equals(BigInteger.ONE)) {
|
||||
bytes[31] |= 0x80;
|
||||
}
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(Arrays.equals(gBytes, bytes), IsEqual.equalTo(true));
|
||||
}
|
||||
}
|
||||
|
||||
// region toX where X is the representation
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#toP2()}.
|
||||
*/
|
||||
@Test
|
||||
public void testToP2() {
|
||||
GroupElement p3zero = curve.getZero(GroupElement.Representation.P3);
|
||||
GroupElement t = p3zero.toP2();
|
||||
assertThat(t.repr, is(GroupElement.Representation.P2));
|
||||
assertThat(t.X, is(p3zero.X));
|
||||
assertThat(t.Y, is(p3zero.Y));
|
||||
assertThat(t.Z, is(p3zero.Z));
|
||||
assertThat(t.T, is((FieldElement) null));
|
||||
|
||||
GroupElement B = ed25519.getB();
|
||||
t = B.toP2();
|
||||
assertThat(t.repr, is(GroupElement.Representation.P2));
|
||||
assertThat(t.X, is(B.X));
|
||||
assertThat(t.Y, is(B.Y));
|
||||
assertThat(t.Z, is(B.Z));
|
||||
assertThat(t.T, is((FieldElement) null));
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toP2ThrowsIfGroupElementHasPrecompRepresentation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.PRECOMP);
|
||||
|
||||
// Assert:
|
||||
g.toP2();
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toP2ThrowsIfGroupElementHasCachedRepresentation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED);
|
||||
|
||||
// Assert:
|
||||
g.toP2();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toP2ReturnsExpectedResultIfGroupElementHasP2Representation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P2);
|
||||
|
||||
// Act:
|
||||
final GroupElement h = g.toP2();
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P2));
|
||||
Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX()));
|
||||
Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY()));
|
||||
Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ()));
|
||||
Assert.assertThat(h.getT(), IsEqual.equalTo(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toP2ReturnsExpectedResultIfGroupElementHasP3Representation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g.toP2();
|
||||
final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.P2);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h1, IsEqual.equalTo(h2));
|
||||
Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P2));
|
||||
Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getX()));
|
||||
Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY()));
|
||||
Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ()));
|
||||
Assert.assertThat(h1.getT(), IsEqual.equalTo(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toP2ReturnsExpectedResultIfGroupElementHasP1P1Representation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1);
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g.toP2();
|
||||
final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.P2);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h1, IsEqual.equalTo(h2));
|
||||
Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P2));
|
||||
Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getX().multiply(g.getT())));
|
||||
Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().multiply(g.getZ())));
|
||||
Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ().multiply(g.getT())));
|
||||
Assert.assertThat(h1.getT(), IsEqual.equalTo(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toP3ThrowsIfGroupElementHasP2Representation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P2);
|
||||
|
||||
// Assert:
|
||||
g.toP3();
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toP3ThrowsIfGroupElementHasPrecompRepresentation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.PRECOMP);
|
||||
|
||||
// Assert:
|
||||
g.toP3();
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toP3ThrowsIfGroupElementHasCachedRepresentation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED);
|
||||
|
||||
// Assert:
|
||||
g.toP3();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toP3ReturnsExpectedResultIfGroupElementHasP1P1Representation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1);
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g.toP3();
|
||||
final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.P3);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h1, IsEqual.equalTo(h2));
|
||||
Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P3));
|
||||
Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getX().multiply(g.getT())));
|
||||
Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().multiply(g.getZ())));
|
||||
Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ().multiply(g.getT())));
|
||||
Assert.assertThat(h1.getT(), IsEqual.equalTo(g.getX().multiply(g.getY())));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toP3ReturnsExpectedResultIfGroupElementHasP3Representation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h = g.toP3();
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.P3));
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX()));
|
||||
Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY()));
|
||||
Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ()));
|
||||
Assert.assertThat(h.getT(), IsEqual.equalTo(g.getT()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toCachedThrowsIfGroupElementHasP2Representation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P2);
|
||||
|
||||
// Assert:
|
||||
g.toCached();
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toCachedThrowsIfGroupElementHasPrecompRepresentation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.PRECOMP);
|
||||
|
||||
// Assert:
|
||||
g.toCached();
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void toCachedThrowsIfGroupElementHasP1P1Representation() {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.P1P1);
|
||||
|
||||
// Assert:
|
||||
g.toCached();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCachedReturnsExpectedResultIfGroupElementHasCachedRepresentation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.toRepresentation(MathUtils.getRandomGroupElement(), GroupElement.Representation.CACHED);
|
||||
|
||||
// Act:
|
||||
final GroupElement h = g.toCached();
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
Assert.assertThat(h.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.CACHED));
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
Assert.assertThat(h.getX(), IsEqual.equalTo(g.getX()));
|
||||
Assert.assertThat(h.getY(), IsEqual.equalTo(g.getY()));
|
||||
Assert.assertThat(h.getZ(), IsEqual.equalTo(g.getZ()));
|
||||
Assert.assertThat(h.getT(), IsEqual.equalTo(g.getT()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCachedReturnsExpectedResultIfGroupElementHasP3Representation() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g.toCached();
|
||||
final GroupElement h2 = MathUtils.toRepresentation(g, GroupElement.Representation.CACHED);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h1, IsEqual.equalTo(h2));
|
||||
Assert.assertThat(h1.getRepresentation(), IsEqual.equalTo(GroupElement.Representation.CACHED));
|
||||
Assert.assertThat(h1, IsEqual.equalTo(g));
|
||||
Assert.assertThat(h1.getX(), IsEqual.equalTo(g.getY().add(g.getX())));
|
||||
Assert.assertThat(h1.getY(), IsEqual.equalTo(g.getY().subtract(g.getX())));
|
||||
Assert.assertThat(h1.getZ(), IsEqual.equalTo(g.getZ()));
|
||||
Assert.assertThat(h1.getT(), IsEqual.equalTo(g.getT().multiply(curve.get2D())));
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#precompute(boolean)}.
|
||||
*/
|
||||
@Test
|
||||
public void testPrecompute() {
|
||||
GroupElement B = ed25519.getB();
|
||||
assertThat(B.precmp, is(equalTo(PrecomputationTestVectors.testPrecmp)));
|
||||
assertThat(B.dblPrecmp, is(equalTo(PrecomputationTestVectors.testDblPrecmp)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void precomputedTableContainsExpectedGroupElements() {
|
||||
// Arrange:
|
||||
GroupElement g = ed25519.getB();
|
||||
|
||||
// Act + Assert:
|
||||
for (int i = 0; i < 32; i++) {
|
||||
GroupElement h = g;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
Assert.assertThat(MathUtils.toRepresentation(h, GroupElement.Representation.PRECOMP), IsEqual.equalTo(ed25519.getB().precmp[i][j]));
|
||||
h = MathUtils.addGroupElements(h, g);
|
||||
}
|
||||
for (int k = 0; k < 8; k++) {
|
||||
g = MathUtils.addGroupElements(g, g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dblPrecomputedTableContainsExpectedGroupElements() {
|
||||
// Arrange:
|
||||
GroupElement g = ed25519.getB();
|
||||
GroupElement h = MathUtils.addGroupElements(g, g);
|
||||
|
||||
// Act + Assert:
|
||||
for (int i=0; i<8; i++) {
|
||||
Assert.assertThat(MathUtils.toRepresentation(g, GroupElement.Representation.PRECOMP), IsEqual.equalTo(ed25519.getB().dblPrecmp[i]));
|
||||
g = MathUtils.addGroupElements(g, h);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#dbl()}.
|
||||
*/
|
||||
@Test
|
||||
public void testDbl() {
|
||||
GroupElement B = ed25519.getB();
|
||||
// 2 * B = B + B
|
||||
assertThat(B.dbl(), is(equalTo(B.add(B.toCached()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dblReturnsExpectedResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g.dbl();
|
||||
final GroupElement h2 = MathUtils.doubleGroupElement(g);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h2, IsEqual.equalTo(h1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addingNeutralGroupElementDoesNotChangeGroupElement() {
|
||||
final GroupElement neutral = GroupElement.p3(curve, curve.getField().ZERO, curve.getField().ONE, curve.getField().ONE, curve.getField().ZERO);
|
||||
for (int i=0; i<1000; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g.add(neutral.toCached());
|
||||
final GroupElement h2 = neutral.add(g.toCached());
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(g, IsEqual.equalTo(h1));
|
||||
Assert.assertThat(g, IsEqual.equalTo(h2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addReturnsExpectedResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g1 = MathUtils.getRandomGroupElement();
|
||||
final GroupElement g2 = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g1.add(g2.toCached());
|
||||
final GroupElement h2 = MathUtils.addGroupElements(g1, g2);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h2, IsEqual.equalTo(h1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subReturnsExpectedResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g1 = MathUtils.getRandomGroupElement();
|
||||
final GroupElement g2 = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = g1.sub(g2.toCached());
|
||||
final GroupElement h2 = MathUtils.addGroupElements(g1, MathUtils.negateGroupElement(g2));
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h2, IsEqual.equalTo(h1));
|
||||
}
|
||||
}
|
||||
|
||||
// region hashCode / equals
|
||||
/**
|
||||
* Test method for {@link GroupElement#equals(Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testEqualsObject() {
|
||||
assertThat(GroupElement.p2(curve, ZERO, ONE, ONE),
|
||||
is(equalTo(P2_ZERO)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsOnlyReturnsTrueForEquivalentObjects() {
|
||||
// Arrange:
|
||||
final GroupElement g1 = MathUtils.getRandomGroupElement();
|
||||
final GroupElement g2 = MathUtils.toRepresentation(g1, GroupElement.Representation.P2);
|
||||
final GroupElement g3 = MathUtils.toRepresentation(g1, GroupElement.Representation.CACHED);
|
||||
final GroupElement g4 = MathUtils.toRepresentation(g1, GroupElement.Representation.P1P1);
|
||||
final GroupElement g5 = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Assert
|
||||
Assert.assertThat(g2, IsEqual.equalTo(g1));
|
||||
Assert.assertThat(g3, IsEqual.equalTo(g1));
|
||||
Assert.assertThat(g1, IsEqual.equalTo(g4));
|
||||
Assert.assertThat(g1, IsNot.not(IsEqual.equalTo(g5)));
|
||||
Assert.assertThat(g2, IsNot.not(IsEqual.equalTo(g5)));
|
||||
Assert.assertThat(g3, IsNot.not(IsEqual.equalTo(g5)));
|
||||
Assert.assertThat(g5, IsNot.not(IsEqual.equalTo(g4)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodesAreEqualForEquivalentObjects() {
|
||||
// Arrange:
|
||||
final GroupElement g1 = MathUtils.getRandomGroupElement();
|
||||
final GroupElement g2 = MathUtils.toRepresentation(g1, GroupElement.Representation.P2);
|
||||
final GroupElement g3 = MathUtils.toRepresentation(g1, GroupElement.Representation.P1P1);
|
||||
final GroupElement g4 = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Assert
|
||||
Assert.assertThat(g2.hashCode(), IsEqual.equalTo(g1.hashCode()));
|
||||
Assert.assertThat(g3.hashCode(), IsEqual.equalTo(g1.hashCode()));
|
||||
Assert.assertThat(g1.hashCode(), IsNot.not(IsEqual.equalTo(g4.hashCode())));
|
||||
Assert.assertThat(g2.hashCode(), IsNot.not(IsEqual.equalTo(g4.hashCode())));
|
||||
Assert.assertThat(g3.hashCode(), IsNot.not(IsEqual.equalTo(g4.hashCode())));
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
static final byte[] BYTES_ZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_ONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_42 = Utils.hexToBytes("2A00000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_1234567890 = Utils.hexToBytes("D202964900000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
static final byte[] RADIX16_ZERO = Utils.hexToBytes("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] RADIX16_ONE = Utils.hexToBytes("01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] RADIX16_42 = Utils.hexToBytes("FA030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#toRadix16(byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testToRadix16() {
|
||||
assertThat(GroupElement.toRadix16(BYTES_ZERO), is(RADIX16_ZERO));
|
||||
assertThat(GroupElement.toRadix16(BYTES_ONE), is(RADIX16_ONE));
|
||||
assertThat(GroupElement.toRadix16(BYTES_42), is(RADIX16_42));
|
||||
|
||||
byte[] from1234567890 = GroupElement.toRadix16(BYTES_1234567890);
|
||||
int total = 0;
|
||||
for (int i = 0; i < from1234567890.length; i++) {
|
||||
assertThat(from1234567890[i], is(greaterThanOrEqualTo((byte)-8)));
|
||||
assertThat(from1234567890[i], is(lessThanOrEqualTo((byte)8)));
|
||||
total += from1234567890[i] * Math.pow(16, i);
|
||||
}
|
||||
assertThat(total, is(1234567890));
|
||||
|
||||
byte[] pkrR16 = GroupElement.toRadix16(BYTES_PKR);
|
||||
for (int i = 0; i < pkrR16.length; i++) {
|
||||
assertThat(pkrR16[i], is(greaterThanOrEqualTo((byte)-8)));
|
||||
assertThat(pkrR16[i], is(lessThanOrEqualTo((byte)8)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#cmov(GroupElement, int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCmov() {
|
||||
GroupElement a = curve.getZero(GroupElement.Representation.PRECOMP);
|
||||
GroupElement b = GroupElement.precomp(curve, TWO, ZERO, TEN);
|
||||
assertThat(a.cmov(b, 0), is(equalTo(a)));
|
||||
assertThat(a.cmov(b, 1), is(equalTo(b)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#select(int, int)}.
|
||||
*/
|
||||
@Test
|
||||
public void testSelect() {
|
||||
GroupElement B = ed25519.getB();
|
||||
for (int i = 0; i < 32; i++) {
|
||||
// 16^i 0 B
|
||||
assertThat(i + ",0", B.select(i, 0),
|
||||
is(equalTo(GroupElement.precomp(curve, ONE, ONE, ZERO))));
|
||||
for (int j = 1; j < 8; j++) {
|
||||
// 16^i r_i B
|
||||
GroupElement t = B.select(i, j);
|
||||
assertThat(i + "," + j,
|
||||
t, is(equalTo(B.precmp[i][j-1])));
|
||||
// -16^i r_i B
|
||||
t = B.select(i, -j);
|
||||
GroupElement neg = GroupElement.precomp(curve,
|
||||
B.precmp[i][j-1].Y,
|
||||
B.precmp[i][j-1].X,
|
||||
B.precmp[i][j-1].Z.negate());
|
||||
assertThat(i + "," + -j,
|
||||
t, is(equalTo(neg)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region scalar multiplication
|
||||
/**
|
||||
* Test method for {@link GroupElement#scalarMultiply(byte[])}.
|
||||
* Test values generated with Python Ed25519 implementation.
|
||||
*/
|
||||
@Test
|
||||
public void testScalarMultiplyByteArray() {
|
||||
// Little-endian
|
||||
byte[] zero = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
byte[] one = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
|
||||
byte[] two = Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000");
|
||||
byte[] a = Utils.hexToBytes("d072f8dd9c07fa7bc8d22a4b325d26301ee9202f6db89aa7c3731529e37e437c");
|
||||
GroupElement A = new GroupElement(curve, Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66"));
|
||||
|
||||
assertThat("scalarMultiply(0) failed",
|
||||
ed25519.getB().scalarMultiply(zero), is(equalTo(curve.getZero(GroupElement.Representation.P3))));
|
||||
assertThat("scalarMultiply(1) failed",
|
||||
ed25519.getB().scalarMultiply(one), is(equalTo(ed25519.getB())));
|
||||
assertThat("scalarMultiply(2) failed",
|
||||
ed25519.getB().scalarMultiply(two), is(equalTo(ed25519.getB().dbl())));
|
||||
|
||||
assertThat("scalarMultiply(a) failed",
|
||||
ed25519.getB().scalarMultiply(a), is(equalTo(A)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarMultiplyBasePointWithZeroReturnsNeutralElement() {
|
||||
// Arrange:
|
||||
final GroupElement basePoint = ed25519.getB();
|
||||
|
||||
// Act:
|
||||
final GroupElement g = basePoint.scalarMultiply(curve.getField().ZERO.toByteArray());
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(curve.getZero(GroupElement.Representation.P3), IsEqual.equalTo(g));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarMultiplyBasePointWithOneReturnsBasePoint() {
|
||||
// Arrange:
|
||||
final GroupElement basePoint = ed25519.getB();
|
||||
|
||||
// Act:
|
||||
final GroupElement g = basePoint.scalarMultiply(curve.getField().ONE.toByteArray());
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(basePoint, IsEqual.equalTo(g));
|
||||
}
|
||||
|
||||
// This test is slow (~6s) due to math utils using an inferior algorithm to calculate the result.
|
||||
@Test
|
||||
public void scalarMultiplyBasePointReturnsExpectedResult() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement basePoint = ed25519.getB();
|
||||
final FieldElement f = MathUtils.getRandomFieldElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement g = basePoint.scalarMultiply(f.toByteArray());
|
||||
final GroupElement h = MathUtils.scalarMultiplyGroupElement(basePoint, f);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(g, IsEqual.equalTo(h));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoubleScalarMultiplyVariableTime() {
|
||||
// Little-endian
|
||||
byte[] zero = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
byte[] one = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
|
||||
byte[] two = Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000");
|
||||
byte[] a = Utils.hexToBytes("d072f8dd9c07fa7bc8d22a4b325d26301ee9202f6db89aa7c3731529e37e437c");
|
||||
GroupElement A = new GroupElement(curve, Utils.hexToBytes("d4cf8595571830644bd14af416954d09ab7159751ad9e0f7a6cbd92379e71a66"));
|
||||
GroupElement B = ed25519.getB();
|
||||
GroupElement geZero = curve.getZero(GroupElement.Representation.P3);
|
||||
geZero.precompute(false);
|
||||
|
||||
// 0 * GE(0) + 0 * GE(0) = GE(0)
|
||||
assertThat(geZero.doubleScalarMultiplyVariableTime(geZero, zero, zero),
|
||||
is(equalTo(geZero)));
|
||||
// 0 * GE(0) + 0 * B = GE(0)
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(geZero, zero, zero),
|
||||
is(equalTo(geZero)));
|
||||
// 1 * GE(0) + 0 * B = GE(0)
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(geZero, one, zero),
|
||||
is(equalTo(geZero)));
|
||||
// 1 * GE(0) + 1 * B = B
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(geZero, one, one),
|
||||
is(equalTo(B)));
|
||||
// 1 * B + 1 * B = 2 * B
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(B, one, one),
|
||||
is(equalTo(B.dbl())));
|
||||
// 1 * B + 2 * B = 3 * B
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(B, one, two),
|
||||
is(equalTo(B.dbl().toP3().add(B.toCached()))));
|
||||
// 2 * B + 2 * B = 4 * B
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(B, two, two),
|
||||
is(equalTo(B.dbl().toP3().dbl())));
|
||||
|
||||
// 0 * B + a * B = A
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(B, zero, a),
|
||||
is(equalTo(A)));
|
||||
// a * B + 0 * B = A
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(B, a, zero),
|
||||
is(equalTo(A)));
|
||||
// a * B + a * B = 2 * A
|
||||
assertThat(B.doubleScalarMultiplyVariableTime(B, a, a),
|
||||
is(equalTo(A.dbl())));
|
||||
}
|
||||
|
||||
// This test is slow (~6s) due to math utils using an inferior algorithm to calculate the result.
|
||||
@Test
|
||||
public void doubleScalarMultiplyVariableTimeReturnsExpectedResult() {
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement basePoint = ed25519.getB();
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
g.precompute(false);
|
||||
final FieldElement f1 = MathUtils.getRandomFieldElement();
|
||||
final FieldElement f2 = MathUtils.getRandomFieldElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = basePoint.doubleScalarMultiplyVariableTime(g, f2.toByteArray(), f1.toByteArray());
|
||||
final GroupElement h2 = MathUtils.doubleScalarMultiplyGroupElements(basePoint, f1, g, f2);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(h1, IsEqual.equalTo(h2));
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* Test method for {@link GroupElement#isOnCurve(Curve)}.
|
||||
*/
|
||||
@Test
|
||||
public void testIsOnCurve() {
|
||||
assertThat(P2_ZERO.isOnCurve(curve),
|
||||
is(true));
|
||||
assertThat(GroupElement.p2(curve, ZERO, ZERO, ONE).isOnCurve(curve),
|
||||
is(false));
|
||||
assertThat(GroupElement.p2(curve, ONE, ONE, ONE).isOnCurve(curve),
|
||||
is(false));
|
||||
assertThat(GroupElement.p2(curve, TEN, ZERO, ONE).isOnCurve(curve),
|
||||
is(false));
|
||||
assertThat(GroupElement.p2(curve, ONE, TEN, ONE).isOnCurve(curve),
|
||||
is(false));
|
||||
assertThat(GroupElement.p2(curve, PKR[0], PKR[1], ONE).isOnCurve(curve),
|
||||
is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isOnCurveReturnsTrueForPointsOnTheCurve() {
|
||||
for (int i=0; i<100; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(g.isOnCurve(), IsEqual.equalTo(true));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isOnCurveReturnsFalseForPointsNotOnTheCurve() {
|
||||
for (int i=0; i<100; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
final GroupElement h = GroupElement.p2(curve, g.getX(), g.getY(), g.getZ().multiply(curve.getField().TWO));
|
||||
|
||||
// Assert (can only fail for 5*Z^2=1):
|
||||
Assert.assertThat(h.isOnCurve(), IsEqual.equalTo(false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,472 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.math.ed25519.Ed25519FieldElement;
|
||||
import org.xbib.io.sshd.eddsa.math.ed25519.Ed25519LittleEndianEncoding;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Utility class to help with calculations.
|
||||
*/
|
||||
public class MathUtils {
|
||||
private static final int[] exponents = {0, 26, 26 + 25, 2*26 + 25, 2*26 + 2*25, 3*26 + 2*25, 3*26 + 3*25, 4*26 + 3*25, 4*26 + 4*25, 5*26 + 4*25};
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
private static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
private static final Curve curve = ed25519.getCurve();
|
||||
private static final BigInteger d = new BigInteger("-121665").multiply(new BigInteger("121666").modInverse(getQ()));
|
||||
private static final BigInteger groupOrder = BigInteger.ONE.shiftLeft(252).add(new BigInteger("27742317777372353535851937790883648493"));
|
||||
|
||||
/**
|
||||
* Gets q = 2^255 - 19 as BigInteger.
|
||||
*/
|
||||
public static BigInteger getQ() {
|
||||
return new BigInteger("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed", 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets group order = 2^252 + 27742317777372353535851937790883648493 as BigInteger.
|
||||
*/
|
||||
public static BigInteger getGroupOrder() {
|
||||
return groupOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying finite field with q=2^255 - 19 elements.
|
||||
*
|
||||
* @return The finite field.
|
||||
*/
|
||||
public static Field getField() {
|
||||
return new Field(
|
||||
256, // b
|
||||
Utils.hexToBytes("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"), // q
|
||||
new Ed25519LittleEndianEncoding());
|
||||
}
|
||||
|
||||
// region field element
|
||||
|
||||
/**
|
||||
* Converts a 2^25.5 bit representation to a BigInteger.
|
||||
* <p>
|
||||
* Value: 2^exponents[0] * t[0] + 2^exponents[1] * t[1] + ... + 2^exponents[9] * t[9]
|
||||
*
|
||||
* @param t The 2^25.5 bit representation.
|
||||
* @return The BigInteger.
|
||||
*/
|
||||
public static BigInteger toBigInteger(final int[] t) {
|
||||
BigInteger b = BigInteger.ZERO;
|
||||
for (int i=0; i<10; i++) {
|
||||
b = b.add(BigInteger.ONE.multiply(BigInteger.valueOf(t[i])).shiftLeft(exponents[i]));
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a 2^8 bit representation to a BigInteger.
|
||||
* <p>
|
||||
* Value: bytes[0] + 2^8 * bytes[1] + ...
|
||||
*
|
||||
* @param bytes The 2^8 bit representation.
|
||||
* @return The BigInteger.
|
||||
*/
|
||||
public static BigInteger toBigInteger(final byte[] bytes) {
|
||||
BigInteger b = BigInteger.ZERO;
|
||||
for (int i=0; i<bytes.length; i++) {
|
||||
b = b.add(BigInteger.ONE.multiply(BigInteger.valueOf(bytes[i] & 0xff)).shiftLeft(i * 8));
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a field element to a BigInteger.
|
||||
*
|
||||
* @param f The field element.
|
||||
* @return The BigInteger.
|
||||
*/
|
||||
public static BigInteger toBigInteger(final FieldElement f) {
|
||||
return toBigInteger(f.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BigInteger to a field element.
|
||||
*
|
||||
* @param b The BigInteger.
|
||||
* @return The field element.
|
||||
*/
|
||||
public static FieldElement toFieldElement(final BigInteger b) {
|
||||
return getField().getEncoding().decode(toByteArray(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BigInteger to a little endian 32 byte representation.
|
||||
*
|
||||
* @param b The BigInteger.
|
||||
* @return The 32 byte representation.
|
||||
*/
|
||||
public static byte[] toByteArray(final BigInteger b) {
|
||||
if (b.compareTo(BigInteger.ONE.shiftLeft(256)) >= 0) {
|
||||
throw new RuntimeException("only numbers < 2^256 are allowed");
|
||||
}
|
||||
final byte[] bytes = new byte[32];
|
||||
final byte[] original = b.toByteArray();
|
||||
|
||||
// Although b < 2^256, original can have length > 32 with some bytes set to 0.
|
||||
final int offset = original.length > 32? original.length - 32 : 0;
|
||||
for (int i=0; i<original.length - offset; i++) {
|
||||
bytes[original.length - i - offset - 1] = original[i + offset];
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces an integer in 2^8 bit representation modulo the group order and returns the result.
|
||||
*
|
||||
* @param bytes The integer in 2^8 bit representation.
|
||||
* @return The mod group order reduced integer.
|
||||
*/
|
||||
public static byte[] reduceModGroupOrder(final byte[] bytes) {
|
||||
final BigInteger b = toBigInteger(bytes).mod(groupOrder);
|
||||
return toByteArray(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates (a * b + c) mod group order and returns the result.
|
||||
* <p>
|
||||
* a, b and c are given in 2^8 bit representation.
|
||||
*
|
||||
* @param a The first integer.
|
||||
* @param b The second integer.
|
||||
* @param c The third integer.
|
||||
* @return The mod group order reduced result.
|
||||
*/
|
||||
public static byte[] multiplyAndAddModGroupOrder(final byte[] a, final byte[] b, final byte[] c) {
|
||||
final BigInteger result = toBigInteger(a).multiply(toBigInteger(b)).add(toBigInteger(c)).mod(groupOrder);
|
||||
return toByteArray(result);
|
||||
}
|
||||
|
||||
public static byte[] getRandomByteArray(final int length) {
|
||||
final byte[] bytes = new byte[length];
|
||||
random.nextBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random field element where |t[i]| <= 2^24 for 0 <= i <= 9.
|
||||
*
|
||||
* @return The field element.
|
||||
*/
|
||||
public static FieldElement getRandomFieldElement() {
|
||||
final int[] t = new int[10];
|
||||
for (int j=0; j<10; j++) {
|
||||
t[j] = random.nextInt(1 << 25) - (1 << 24);
|
||||
}
|
||||
return new Ed25519FieldElement(getField(), t);
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region group element
|
||||
|
||||
/**
|
||||
* Gets a random group element in P3 representation.
|
||||
*
|
||||
* @return The group element.
|
||||
*/
|
||||
public static GroupElement getRandomGroupElement() {
|
||||
final byte[] bytes = new byte[32];
|
||||
while (true) {
|
||||
try {
|
||||
random.nextBytes(bytes);
|
||||
return new GroupElement(curve, bytes);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// Will fail in about 87.5%, so try again.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a group element from a byte array.
|
||||
* <p>
|
||||
* Bit 0 to 254 are the affine y-coordinate, bit 255 is the sign of the affine x-coordinate.
|
||||
*
|
||||
* @param bytes the byte array.
|
||||
* @return The group element.
|
||||
*/
|
||||
public static GroupElement toGroupElement(final byte[] bytes) {
|
||||
final boolean shouldBeNegative = (bytes[31] >> 7) != 0;
|
||||
bytes[31] &= 0x7f;
|
||||
final BigInteger y = MathUtils.toBigInteger(bytes);
|
||||
|
||||
// x = sign(x) * sqrt((y^2 - 1) / (d * y^2 + 1))
|
||||
final BigInteger u = y.multiply(y).subtract(BigInteger.ONE).mod(getQ());
|
||||
final BigInteger v = d.multiply(y).multiply(y).add(BigInteger.ONE).mod(getQ());
|
||||
final BigInteger tmp = u.multiply(v.pow(7)).modPow(BigInteger.ONE.shiftLeft(252).subtract(new BigInteger("3")), getQ()).mod(getQ());
|
||||
BigInteger x = tmp.multiply(u).multiply(v.pow(3)).mod(getQ());
|
||||
if (!v.multiply(x).multiply(x).subtract(u).mod(getQ()).equals(BigInteger.ZERO)) {
|
||||
if (!v.multiply(x).multiply(x).add(u).mod(getQ()).equals(BigInteger.ZERO)) {
|
||||
throw new IllegalArgumentException("not a valid GroupElement");
|
||||
}
|
||||
x = x.multiply(toBigInteger(curve.getI())).mod(getQ());
|
||||
}
|
||||
final boolean isNegative = x.mod(new BigInteger("2")).equals(BigInteger.ONE);
|
||||
if ((shouldBeNegative && !isNegative) || (!shouldBeNegative && isNegative)) {
|
||||
x = x.negate().mod(getQ());
|
||||
}
|
||||
|
||||
return GroupElement.p3(curve, toFieldElement(x), toFieldElement(y), getField().ONE, toFieldElement(x.multiply(y).mod(getQ())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a group element from one representation to another.
|
||||
* This method is a helper used to test various methods in GroupElement.
|
||||
*
|
||||
* @param g The group element.
|
||||
* @param repr The desired representation.
|
||||
* @return The same group element in the new representation.
|
||||
*/
|
||||
public static GroupElement toRepresentation(final GroupElement g, final GroupElement.Representation repr) {
|
||||
BigInteger x;
|
||||
BigInteger y;
|
||||
final BigInteger gX = toBigInteger(g.getX().toByteArray());
|
||||
final BigInteger gY = toBigInteger(g.getY().toByteArray());
|
||||
final BigInteger gZ = toBigInteger(g.getZ().toByteArray());
|
||||
final BigInteger gT = null == g.getT()? null : toBigInteger(g.getT().toByteArray());
|
||||
|
||||
// Switch to affine coordinates.
|
||||
switch (g.getRepresentation()) {
|
||||
case P2:
|
||||
case P3:
|
||||
x = gX.multiply(gZ.modInverse(getQ())).mod(getQ());
|
||||
y = gY.multiply(gZ.modInverse(getQ())).mod(getQ());
|
||||
break;
|
||||
case P1P1:
|
||||
x = gX.multiply(gZ.modInverse(getQ())).mod(getQ());
|
||||
y = gY.multiply(gT.modInverse(getQ())).mod(getQ());
|
||||
break;
|
||||
case CACHED:
|
||||
x = gX.subtract(gY).multiply(gZ.multiply(new BigInteger("2")).modInverse(getQ())).mod(getQ());
|
||||
y = gX.add(gY).multiply(gZ.multiply(new BigInteger("2")).modInverse(getQ())).mod(getQ());
|
||||
break;
|
||||
case PRECOMP:
|
||||
x = gX.subtract(gY).multiply(new BigInteger("2").modInverse(getQ())).mod(getQ());
|
||||
y = gX.add(gY).multiply(new BigInteger("2").modInverse(getQ())).mod(getQ());
|
||||
break;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
// Now back to the desired representation.
|
||||
switch (repr) {
|
||||
case P2:
|
||||
return GroupElement.p2(
|
||||
curve,
|
||||
toFieldElement(x),
|
||||
toFieldElement(y),
|
||||
getField().ONE);
|
||||
case P3:
|
||||
return GroupElement.p3(
|
||||
curve,
|
||||
toFieldElement(x),
|
||||
toFieldElement(y),
|
||||
getField().ONE,
|
||||
toFieldElement(x.multiply(y).mod(getQ())));
|
||||
case P1P1:
|
||||
return GroupElement.p1p1(
|
||||
curve,
|
||||
toFieldElement(x),
|
||||
toFieldElement(y),
|
||||
getField().ONE,
|
||||
getField().ONE);
|
||||
case CACHED:
|
||||
return GroupElement.cached(
|
||||
curve,
|
||||
toFieldElement(y.add(x).mod(getQ())),
|
||||
toFieldElement(y.subtract(x).mod(getQ())),
|
||||
getField().ONE,
|
||||
toFieldElement(d.multiply(new BigInteger("2")).multiply(x).multiply(y).mod(getQ())));
|
||||
case PRECOMP:
|
||||
return GroupElement.precomp(
|
||||
curve,
|
||||
toFieldElement(y.add(x).mod(getQ())),
|
||||
toFieldElement(y.subtract(x).mod(getQ())),
|
||||
toFieldElement(d.multiply(new BigInteger("2")).multiply(x).multiply(y).mod(getQ())));
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two group elements and returns the result in P3 representation.
|
||||
* It uses BigInteger arithmetic and the affine representation.
|
||||
* This method is a helper used to test the projective group addition formulas in GroupElement.
|
||||
*
|
||||
* @param g1 The first group element.
|
||||
* @param g2 The second group element.
|
||||
* @return The result of the addition.
|
||||
*/
|
||||
public static GroupElement addGroupElements(final GroupElement g1, final GroupElement g2) {
|
||||
// Relying on a special representation of the group elements.
|
||||
if ((g1.getRepresentation() != GroupElement.Representation.P2 && g1.getRepresentation() != GroupElement.Representation.P3) ||
|
||||
(g2.getRepresentation() != GroupElement.Representation.P2 && g2.getRepresentation() != GroupElement.Representation.P3)) {
|
||||
throw new IllegalArgumentException("g1 and g2 must have representation P2 or P3");
|
||||
}
|
||||
|
||||
// Projective coordinates
|
||||
final BigInteger g1X = toBigInteger(g1.getX().toByteArray());
|
||||
final BigInteger g1Y = toBigInteger(g1.getY().toByteArray());
|
||||
final BigInteger g1Z = toBigInteger(g1.getZ().toByteArray());
|
||||
final BigInteger g2X = toBigInteger(g2.getX().toByteArray());
|
||||
final BigInteger g2Y = toBigInteger(g2.getY().toByteArray());
|
||||
final BigInteger g2Z = toBigInteger(g2.getZ().toByteArray());
|
||||
|
||||
// Affine coordinates
|
||||
final BigInteger g1x = g1X.multiply(g1Z.modInverse(getQ())).mod(getQ());
|
||||
final BigInteger g1y = g1Y.multiply(g1Z.modInverse(getQ())).mod(getQ());
|
||||
final BigInteger g2x = g2X.multiply(g2Z.modInverse(getQ())).mod(getQ());
|
||||
final BigInteger g2y = g2Y.multiply(g2Z.modInverse(getQ())).mod(getQ());
|
||||
|
||||
// Addition formula for affine coordinates. The formula is complete in our case.
|
||||
//
|
||||
// (x3, y3) = (x1, y1) + (x2, y2) where
|
||||
//
|
||||
// x3 = (x1 * y2 + x2 * y1) / (1 + d * x1 * x2 * y1 * y2) and
|
||||
// y3 = (x1 * x2 + y1 * y2) / (1 - d * x1 * x2 * y1 * y2) and
|
||||
// d = -121665/121666
|
||||
BigInteger dx1x2y1y2 = d.multiply(g1x).multiply(g2x).multiply(g1y).multiply(g2y).mod(getQ());
|
||||
BigInteger x3 = g1x.multiply(g2y).add(g2x.multiply(g1y))
|
||||
.multiply(BigInteger.ONE.add(dx1x2y1y2).modInverse(getQ())).mod(getQ());
|
||||
BigInteger y3 = g1x.multiply(g2x).add(g1y.multiply(g2y))
|
||||
.multiply(BigInteger.ONE.subtract(dx1x2y1y2).modInverse(getQ())).mod(getQ());
|
||||
BigInteger t3 = x3.multiply(y3).mod(getQ());
|
||||
|
||||
return GroupElement.p3(g1.getCurve(), toFieldElement(x3), toFieldElement(y3), getField().ONE, toFieldElement(t3));
|
||||
}
|
||||
|
||||
/**
|
||||
* Doubles a group element and returns the result in P3 representation.
|
||||
* It uses BigInteger arithmetic and the affine representation.
|
||||
* This method is a helper used to test the projective group doubling formula in GroupElement.
|
||||
*
|
||||
* @param g The group element.
|
||||
* @return g+g.
|
||||
*/
|
||||
public static GroupElement doubleGroupElement(final GroupElement g) {
|
||||
return addGroupElements(g, g);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scalar multiply the group element by the field element.
|
||||
*
|
||||
* @param g The group element.
|
||||
* @param f The field element.
|
||||
* @return The resulting group element.
|
||||
*/
|
||||
public static GroupElement scalarMultiplyGroupElement(final GroupElement g, final FieldElement f) {
|
||||
final byte[] bytes = f.toByteArray();
|
||||
GroupElement h = curve.getZero(GroupElement.Representation.P3);
|
||||
for (int i=254; i>=0; i--) {
|
||||
h = doubleGroupElement(h);
|
||||
if (Utils.bit(bytes, i) == 1) {
|
||||
h = addGroupElements(h, g);
|
||||
}
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates f1 * g1 + f2 * g2.
|
||||
*
|
||||
* @param g1 The first group element.
|
||||
* @param f1 The first multiplier.
|
||||
* @param g2 The second group element.
|
||||
* @param f2 The second multiplier.
|
||||
* @return The resulting group element.
|
||||
*/
|
||||
public static GroupElement doubleScalarMultiplyGroupElements(
|
||||
final GroupElement g1,
|
||||
final FieldElement f1,
|
||||
final GroupElement g2,
|
||||
final FieldElement f2) {
|
||||
final GroupElement h1 = scalarMultiplyGroupElement(g1, f1);
|
||||
final GroupElement h2 = scalarMultiplyGroupElement(g2, f2);
|
||||
return addGroupElements(h1, h2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates a group element.
|
||||
*
|
||||
* @param g The group element.
|
||||
* @return The negated group element.
|
||||
*/
|
||||
public static GroupElement negateGroupElement(final GroupElement g) {
|
||||
if (g.getRepresentation() != GroupElement.Representation.P3) {
|
||||
throw new IllegalArgumentException("g must have representation P3");
|
||||
}
|
||||
|
||||
return GroupElement.p3(g.getCurve(), g.getX().negate(), g.getY(), g.getZ(), g.getT().negate());
|
||||
}
|
||||
|
||||
// Start TODO BR: Remove when finished!
|
||||
@Test
|
||||
public void mathUtilsWorkAsExpected() {
|
||||
final GroupElement neutral = GroupElement.p3(curve, curve.getField().ZERO, curve.getField().ONE, curve.getField().ONE, curve.getField().ZERO);
|
||||
for (int i=0; i<1000; i++) {
|
||||
final GroupElement g = getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h1 = addGroupElements(g, neutral);
|
||||
final GroupElement h2 = addGroupElements(neutral, g);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(g, IsEqual.equalTo(h1));
|
||||
Assert.assertThat(g, IsEqual.equalTo(h2));
|
||||
}
|
||||
|
||||
for (int i=0; i<1000; i++) {
|
||||
GroupElement g = getRandomGroupElement();
|
||||
|
||||
// P3 -> P2.
|
||||
GroupElement h = toRepresentation(g, GroupElement.Representation.P2);
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
// P3 -> P1P1.
|
||||
h = toRepresentation(g, GroupElement.Representation.P1P1);
|
||||
Assert.assertThat(g, IsEqual.equalTo(h));
|
||||
|
||||
// P3 -> CACHED.
|
||||
h = toRepresentation(g, GroupElement.Representation.CACHED);
|
||||
Assert.assertThat(h, IsEqual.equalTo(g));
|
||||
|
||||
// P3 -> P2 -> P3.
|
||||
g = toRepresentation(g, GroupElement.Representation.P2);
|
||||
h = toRepresentation(g, GroupElement.Representation.P3);
|
||||
Assert.assertThat(g, IsEqual.equalTo(h));
|
||||
|
||||
// P3 -> P2 -> P1P1.
|
||||
g = toRepresentation(g, GroupElement.Representation.P2);
|
||||
h = toRepresentation(g, GroupElement.Representation.P1P1);
|
||||
Assert.assertThat(g, IsEqual.equalTo(h));
|
||||
}
|
||||
|
||||
for (int i=0; i<10; i++) {
|
||||
// Arrange:
|
||||
final GroupElement g = MathUtils.getRandomGroupElement();
|
||||
|
||||
// Act:
|
||||
final GroupElement h = MathUtils.scalarMultiplyGroupElement(g, curve.getField().ZERO);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(curve.getZero(GroupElement.Representation.P3), IsEqual.equalTo(h));
|
||||
}
|
||||
}
|
||||
// End TODO BR: Remove when finished!
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PrecomputationTestVectors {
|
||||
// Test files were generated using base.py and base2.py from ref10
|
||||
// (by printing hex(x%q) instead of the radix-255 representation).
|
||||
static GroupElement[][] testPrecmp = getPrecomputation("basePrecmp");
|
||||
static GroupElement[] testDblPrecmp = getDoublePrecomputation("baseDblPrecmp");
|
||||
|
||||
public static GroupElement[][] getPrecomputation(String fileName) {
|
||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Curve curve = ed25519.getCurve();
|
||||
Field field = curve.getField();
|
||||
GroupElement[][] precmp = new GroupElement[32][8];
|
||||
BufferedReader file = null;
|
||||
int row = 0, col = 0;
|
||||
try {
|
||||
InputStream is = PrecomputationTestVectors.class.getResourceAsStream(fileName);
|
||||
if (is == null)
|
||||
throw new IOException("Resource not found: " + fileName);
|
||||
file = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
while ((line = file.readLine()) != null) {
|
||||
if (line.equals(" },"))
|
||||
col += 1;
|
||||
else if (line.equals("},")) {
|
||||
col = 0;
|
||||
row += 1;
|
||||
} else if (line.startsWith(" { ")) {
|
||||
String ypxStr = line.substring(4, line.lastIndexOf(' '));
|
||||
FieldElement ypx = field.fromByteArray(
|
||||
Utils.hexToBytes(ypxStr));
|
||||
line = file.readLine();
|
||||
String ymxStr = line.substring(4, line.lastIndexOf(' '));
|
||||
FieldElement ymx = field.fromByteArray(
|
||||
Utils.hexToBytes(ymxStr));
|
||||
line = file.readLine();
|
||||
String xy2dStr = line.substring(4, line.lastIndexOf(' '));
|
||||
FieldElement xy2d = field.fromByteArray(
|
||||
Utils.hexToBytes(xy2dStr));
|
||||
precmp[row][col] = GroupElement.precomp(curve,
|
||||
ypx, ymx, xy2d);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (file != null) try { file.close(); } catch (IOException e) {}
|
||||
}
|
||||
return precmp;
|
||||
}
|
||||
|
||||
public static GroupElement[] getDoublePrecomputation(String fileName) {
|
||||
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
Curve curve = ed25519.getCurve();
|
||||
Field field = curve.getField();
|
||||
GroupElement[] dblPrecmp = new GroupElement[8];
|
||||
BufferedReader file = null;
|
||||
int row = 0;
|
||||
try {
|
||||
InputStream is = PrecomputationTestVectors.class.getResourceAsStream(fileName);
|
||||
if (is == null)
|
||||
throw new IOException("Resource not found: " + fileName);
|
||||
file = new BufferedReader(new InputStreamReader(is));
|
||||
String line;
|
||||
while ((line = file.readLine()) != null) {
|
||||
if (line.equals(" },")) {
|
||||
row += 1;
|
||||
} else if (line.startsWith(" { ")) {
|
||||
String ypxStr = line.substring(4, line.lastIndexOf(' '));
|
||||
FieldElement ypx = field.fromByteArray(
|
||||
Utils.hexToBytes(ypxStr));
|
||||
line = file.readLine();
|
||||
String ymxStr = line.substring(4, line.lastIndexOf(' '));
|
||||
FieldElement ymx = field.fromByteArray(
|
||||
Utils.hexToBytes(ymxStr));
|
||||
line = file.readLine();
|
||||
String xy2dStr = line.substring(4, line.lastIndexOf(' '));
|
||||
FieldElement xy2d = field.fromByteArray(
|
||||
Utils.hexToBytes(xy2dStr));
|
||||
dblPrecmp[row] = GroupElement.precomp(curve,
|
||||
ypx, ymx, xy2d);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (file != null) try { file.close(); } catch (IOException e) {}
|
||||
}
|
||||
return dblPrecmp;
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.bigint;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Random;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.FieldElement;
|
||||
import org.xbib.io.sshd.eddsa.math.MathUtils;
|
||||
import org.xbib.io.sshd.eddsa.math.AbstractFieldElementTest;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BigIntegerFieldElementTest extends AbstractFieldElementTest {
|
||||
static final byte[] BYTES_ZERO = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_ONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] BYTES_TEN = Utils.hexToBytes("0a00000000000000000000000000000000000000000000000000000000000000");
|
||||
|
||||
static final Field ed25519Field = new Field(
|
||||
256, // b
|
||||
Utils.hexToBytes("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"), // q
|
||||
new BigIntegerLittleEndianEncoding());
|
||||
|
||||
static final FieldElement ZERO = new BigIntegerFieldElement(ed25519Field, BigInteger.ZERO);
|
||||
static final FieldElement ONE = new BigIntegerFieldElement(ed25519Field, BigInteger.ONE);
|
||||
static final FieldElement TWO = new BigIntegerFieldElement(ed25519Field, BigInteger.valueOf(2));
|
||||
|
||||
protected FieldElement getRandomFieldElement() {
|
||||
BigInteger r;
|
||||
Random rnd = new Random();
|
||||
do {
|
||||
r = new BigInteger(255, rnd);
|
||||
} while (r.compareTo(getQ()) >= 0);
|
||||
return new BigIntegerFieldElement(ed25519Field, r);
|
||||
}
|
||||
|
||||
protected BigInteger toBigInteger(FieldElement f) {
|
||||
return ((BigIntegerFieldElement)f).bi;
|
||||
}
|
||||
|
||||
protected BigInteger getQ() {
|
||||
return MathUtils.getQ();
|
||||
}
|
||||
|
||||
protected Field getField() {
|
||||
return ed25519Field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link BigIntegerFieldElement#BigIntegerFieldElement(Field, BigInteger)}.
|
||||
*/
|
||||
@Test
|
||||
public void testFieldElementBigInteger() {
|
||||
assertThat(new BigIntegerFieldElement(ed25519Field, BigInteger.ZERO).bi, is(BigInteger.ZERO));
|
||||
assertThat(new BigIntegerFieldElement(ed25519Field, BigInteger.ONE).bi, is(BigInteger.ONE));
|
||||
assertThat(new BigIntegerFieldElement(ed25519Field, BigInteger.valueOf(2)).bi, is(BigInteger.valueOf(2)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link FieldElement#toByteArray()}.
|
||||
*/
|
||||
@Test
|
||||
public void testToByteArray() {
|
||||
byte[] zero = ZERO.toByteArray();
|
||||
assertThat(zero.length, is(equalTo(BYTES_ZERO.length)));
|
||||
assertThat(zero, is(equalTo(BYTES_ZERO)));
|
||||
|
||||
byte[] one = ONE.toByteArray();
|
||||
assertThat(one.length, is(equalTo(BYTES_ONE.length)));
|
||||
assertThat(one, is(equalTo(BYTES_ONE)));
|
||||
|
||||
byte[] ten = new BigIntegerFieldElement(ed25519Field, BigInteger.TEN).toByteArray();
|
||||
assertThat(ten.length, is(equalTo(BYTES_TEN.length)));
|
||||
assertThat(ten, is(equalTo(BYTES_TEN)));
|
||||
}
|
||||
|
||||
// region isNonZero
|
||||
|
||||
protected FieldElement getZeroFieldElement() {
|
||||
return ZERO;
|
||||
}
|
||||
|
||||
protected FieldElement getNonZeroFieldElement() {
|
||||
return TWO;
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
/**
|
||||
* Test method for {@link FieldElement#equals(Object)}.
|
||||
*/
|
||||
@Test
|
||||
public void testEqualsObject() {
|
||||
assertThat(new BigIntegerFieldElement(ed25519Field, BigInteger.ZERO), is(equalTo(ZERO)));
|
||||
assertThat(new BigIntegerFieldElement(ed25519Field, BigInteger.valueOf(1000)), is(equalTo(new BigIntegerFieldElement(ed25519Field, BigInteger.valueOf(1000)))));
|
||||
assertThat(ONE, is(not(equalTo(TWO))));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.bigint;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.ScalarOps;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class BigIntegerScalarOpsTest {
|
||||
|
||||
static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
static final Field ed25519Field = ed25519.getCurve().getField();
|
||||
|
||||
/**
|
||||
* Test method for {@link BigIntegerScalarOps#reduce(byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testReduce() {
|
||||
ScalarOps sc = new BigIntegerScalarOps(ed25519Field,
|
||||
new BigInteger("5"));
|
||||
assertThat(sc.reduce(new byte[] {7}),
|
||||
is(equalTo(Utils.hexToBytes("0200000000000000000000000000000000000000000000000000000000000000"))));
|
||||
|
||||
ScalarOps sc2 = new BigIntegerScalarOps(ed25519Field,
|
||||
new BigInteger("7237005577332262213973186563042994240857116359379907606001950938285454250989"));
|
||||
// Example from test case 1
|
||||
byte[] r = Utils.hexToBytes("b6b19cd8e0426f5983fa112d89a143aa97dab8bc5deb8d5b6253c928b65272f4044098c2a990039cde5b6a4818df0bfb6e40dc5dee54248032962323e701352d");
|
||||
assertThat(sc2.reduce(r), is(equalTo(Utils.hexToBytes("f38907308c893deaf244787db4af53682249107418afc2edc58f75ac58a07404"))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link BigIntegerScalarOps#multiplyAndAdd(byte[], byte[], byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testMultiplyAndAdd() {
|
||||
ScalarOps sc = new BigIntegerScalarOps(ed25519Field,
|
||||
new BigInteger("5"));
|
||||
assertThat(sc.multiplyAndAdd(new byte[] {7}, new byte[] {2}, new byte[] {5}),
|
||||
is(equalTo(Utils.hexToBytes("0400000000000000000000000000000000000000000000000000000000000000"))));
|
||||
|
||||
ScalarOps sc2 = new BigIntegerScalarOps(ed25519Field,
|
||||
new BigInteger("7237005577332262213973186563042994240857116359379907606001950938285454250989"));
|
||||
// Example from test case 1
|
||||
byte[] h = Utils.hexToBytes("86eabc8e4c96193d290504e7c600df6cf8d8256131ec2c138a3e7e162e525404");
|
||||
byte[] a = Utils.hexToBytes("307c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de94f");
|
||||
byte[] r = Utils.hexToBytes("f38907308c893deaf244787db4af53682249107418afc2edc58f75ac58a07404");
|
||||
byte[] S = Utils.hexToBytes("5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
|
||||
assertThat(sc2.multiplyAndAdd(h, a, r), is(equalTo(S)));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.ed25519;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.AbstractFieldElementTest;
|
||||
import org.xbib.io.sshd.eddsa.math.Field;
|
||||
import org.xbib.io.sshd.eddsa.math.FieldElement;
|
||||
import org.xbib.io.sshd.eddsa.math.MathUtils;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* Tests rely on the BigInteger class.
|
||||
*/
|
||||
public class Ed25519FieldElementTest extends AbstractFieldElementTest {
|
||||
|
||||
protected FieldElement getRandomFieldElement() {
|
||||
return MathUtils.getRandomFieldElement();
|
||||
}
|
||||
|
||||
protected BigInteger toBigInteger(FieldElement f) {
|
||||
return MathUtils.toBigInteger(f);
|
||||
}
|
||||
|
||||
protected BigInteger getQ() {
|
||||
return MathUtils.getQ();
|
||||
}
|
||||
|
||||
protected Field getField() {
|
||||
return MathUtils.getField();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canConstructFieldElementFromArrayWithCorrectLength() {
|
||||
new Ed25519FieldElement(MathUtils.getField(), new int[10]);
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void cannotConstructFieldElementFromArrayWithIncorrectLength() {
|
||||
new Ed25519FieldElement(MathUtils.getField(), new int[9]);
|
||||
}
|
||||
|
||||
@Test (expected = IllegalArgumentException.class)
|
||||
public void cannotConstructFieldElementWithoutField() {
|
||||
new Ed25519FieldElement(null, new int[9]);
|
||||
}
|
||||
|
||||
protected FieldElement getZeroFieldElement() {
|
||||
return new Ed25519FieldElement(MathUtils.getField(), new int[10]);
|
||||
}
|
||||
|
||||
protected FieldElement getNonZeroFieldElement() {
|
||||
final int[] t = new int[10];
|
||||
t[0] = 5;
|
||||
return new Ed25519FieldElement(MathUtils.getField(), t);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toStringReturnsCorrectRepresentation() {
|
||||
final byte[] bytes = new byte[32];
|
||||
for (int i=0; i<32; i++) {
|
||||
bytes[i] = (byte)(i+1);
|
||||
}
|
||||
final FieldElement f = MathUtils.getField().getEncoding().decode(bytes);
|
||||
|
||||
final String fAsString = f.toString();
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("[Ed25519FieldElement val=");
|
||||
for (byte b : bytes) {
|
||||
builder.append(String.format("%02x", b));
|
||||
}
|
||||
builder.append("]");
|
||||
|
||||
Assert.assertThat(fAsString, IsEqual.equalTo(builder.toString()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.ed25519;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.math.FieldElement;
|
||||
import org.xbib.io.sshd.eddsa.math.MathUtils;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.SecureRandom;
|
||||
|
||||
/**
|
||||
* Tests rely on the BigInteger class.
|
||||
*/
|
||||
public class Ed25519LittleEndianEncodingTest {
|
||||
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
@Test
|
||||
public void encodeReturnsCorrectByteArrayForSimpleFieldElements() {
|
||||
// Arrange:
|
||||
final int[] t1 = new int[10];
|
||||
final int[] t2 = new int[10];
|
||||
t2[0] = 1;
|
||||
final FieldElement fieldElement1 = new Ed25519FieldElement(MathUtils.getField(), t1);
|
||||
final FieldElement fieldElement2 = new Ed25519FieldElement(MathUtils.getField(), t2);
|
||||
|
||||
// Act:
|
||||
final byte[] bytes1 = MathUtils.getField().getEncoding().encode(fieldElement1);
|
||||
final byte[] bytes2 = MathUtils.getField().getEncoding().encode(fieldElement2);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(bytes1, IsEqual.equalTo(MathUtils.toByteArray(BigInteger.ZERO)));
|
||||
Assert.assertThat(bytes2, IsEqual.equalTo(MathUtils.toByteArray(BigInteger.ONE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeReturnsCorrectByteArray() {
|
||||
for (int i=0; i<10000; i++){
|
||||
// Arrange:
|
||||
final int[] t = new int[10];
|
||||
for (int j=0; j<10; j++) {
|
||||
t[j] = random.nextInt(1 << 28) - (1 << 27);
|
||||
}
|
||||
final FieldElement fieldElement1 = new Ed25519FieldElement(MathUtils.getField(), t);
|
||||
final BigInteger b = MathUtils.toBigInteger(t);
|
||||
|
||||
// Act:
|
||||
final byte[] bytes = MathUtils.getField().getEncoding().encode(fieldElement1);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(bytes, IsEqual.equalTo(MathUtils.toByteArray(b.mod(MathUtils.getQ()))));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeReturnsCorrectFieldElementForSimpleByteArrays() {
|
||||
// Arrange:
|
||||
final byte[] bytes1 = new byte[32];
|
||||
final byte[] bytes2 = new byte[32];
|
||||
bytes2[0] = 1;
|
||||
|
||||
// Act:
|
||||
final Ed25519FieldElement f1 = (Ed25519FieldElement)MathUtils.getField().getEncoding().decode(bytes1);
|
||||
final Ed25519FieldElement f2 = (Ed25519FieldElement)MathUtils.getField().getEncoding().decode(bytes2);
|
||||
final BigInteger b1 = MathUtils.toBigInteger(f1.t);
|
||||
final BigInteger b2 = MathUtils.toBigInteger(f2.t);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(b1, IsEqual.equalTo(BigInteger.ZERO));
|
||||
Assert.assertThat(b2, IsEqual.equalTo(BigInteger.ONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeReturnsCorrectFieldElement() {
|
||||
for (int i=0; i<10000; i++) {
|
||||
// Arrange:
|
||||
final byte[] bytes = new byte[32];
|
||||
random.nextBytes(bytes);
|
||||
bytes[31] = (byte)(bytes[31] & 0x7f);
|
||||
final BigInteger b1 = MathUtils.toBigInteger(bytes);
|
||||
|
||||
// Act:
|
||||
final Ed25519FieldElement f = (Ed25519FieldElement)MathUtils.getField().getEncoding().decode(bytes);
|
||||
final BigInteger b2 = MathUtils.toBigInteger(f.t).mod(MathUtils.getQ());
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(b2, IsEqual.equalTo(b1));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isNegativeReturnsCorrectResult() {
|
||||
for (int i=0; i<10000; i++) {
|
||||
// Arrange:
|
||||
final int[] t = new int[10];
|
||||
for (int j=0; j<10; j++) {
|
||||
t[j] = random.nextInt(1 << 28) - (1 << 27);
|
||||
}
|
||||
final boolean isNegative = MathUtils.toBigInteger(t).mod(MathUtils.getQ()).mod(new BigInteger("2")).equals(BigInteger.ONE);
|
||||
final FieldElement f = new Ed25519FieldElement(MathUtils.getField(), t);
|
||||
|
||||
// Assert:
|
||||
Assert.assertThat(MathUtils.getField().getEncoding().isNegative(f), IsEqual.equalTo(isNegative));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.math.ed25519;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
import org.xbib.io.sshd.eddsa.math.MathUtils;
|
||||
import org.hamcrest.core.IsEqual;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* Additional tests by the NEM project team.
|
||||
*
|
||||
*/
|
||||
public class Ed25519ScalarOpsTest {
|
||||
|
||||
private static final Ed25519ScalarOps scalarOps = new Ed25519ScalarOps();
|
||||
|
||||
/**
|
||||
* Test method for {@link org.xbib.io.sshd.eddsa.math.bigint.BigIntegerScalarOps#reduce(byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testReduce() {
|
||||
// Example from test case 1
|
||||
byte[] r = Utils.hexToBytes("b6b19cd8e0426f5983fa112d89a143aa97dab8bc5deb8d5b6253c928b65272f4044098c2a990039cde5b6a4818df0bfb6e40dc5dee54248032962323e701352d");
|
||||
assertThat(scalarOps.reduce(r), is(equalTo(Utils.hexToBytes("f38907308c893deaf244787db4af53682249107418afc2edc58f75ac58a07404"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reduceReturnsExpectedResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final byte[] bytes = MathUtils.getRandomByteArray(64);
|
||||
final byte[] reduced1 = scalarOps.reduce(bytes);
|
||||
final byte[] reduced2 = MathUtils.reduceModGroupOrder(bytes);
|
||||
Assert.assertThat(MathUtils.toBigInteger(reduced1).compareTo(MathUtils.getGroupOrder()), IsEqual.equalTo(-1));
|
||||
Assert.assertThat(MathUtils.toBigInteger(reduced1).compareTo(new BigInteger("-1")), IsEqual.equalTo(1));
|
||||
Assert.assertThat(reduced1, IsEqual.equalTo(reduced2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.xbib.io.sshd.eddsa.math.bigint.BigIntegerScalarOps#multiplyAndAdd(byte[], byte[], byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testMultiplyAndAdd() {
|
||||
byte[] h = Utils.hexToBytes("86eabc8e4c96193d290504e7c600df6cf8d8256131ec2c138a3e7e162e525404");
|
||||
byte[] a = Utils.hexToBytes("307c83864f2833cb427a2ef1c00a013cfdff2768d980c0a3a520f006904de94f");
|
||||
byte[] r = Utils.hexToBytes("f38907308c893deaf244787db4af53682249107418afc2edc58f75ac58a07404");
|
||||
byte[] S = Utils.hexToBytes("5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
|
||||
assertThat(scalarOps.multiplyAndAdd(h, a, r), is(equalTo(S)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiplyAndAddReturnsExpectedResult() {
|
||||
for (int i=0; i<1000; i++) {
|
||||
final byte[] bytes1 = MathUtils.getRandomByteArray(32);
|
||||
final byte[] bytes2 = MathUtils.getRandomByteArray(32);
|
||||
final byte[] bytes3 = MathUtils.getRandomByteArray(32);
|
||||
|
||||
final byte[] result1 = scalarOps.multiplyAndAdd(bytes1, bytes2, bytes3);
|
||||
final byte[] result2 = MathUtils.multiplyAndAddModGroupOrder(bytes1, bytes2, bytes3);
|
||||
|
||||
Assert.assertThat(MathUtils.toBigInteger(result1).compareTo(MathUtils.getGroupOrder()), IsEqual.equalTo(-1));
|
||||
Assert.assertThat(MathUtils.toBigInteger(result1).compareTo(new BigInteger("-1")), IsEqual.equalTo(1));
|
||||
Assert.assertThat(result1, IsEqual.equalTo(result2));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSANamedCurveTableTest {
|
||||
/**
|
||||
* Ensure curve names are case-inspecific
|
||||
*/
|
||||
@Test
|
||||
public void curveNamesAreCaseInspecific() {
|
||||
EdDSANamedCurveSpec mixed = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
EdDSANamedCurveSpec lower = EdDSANamedCurveTable.getByName("ed25519");
|
||||
EdDSANamedCurveSpec upper = EdDSANamedCurveTable.getByName("ED25519");
|
||||
|
||||
assertThat(lower, is(equalTo(mixed)));
|
||||
assertThat(upper, is(equalTo(mixed)));
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package org.xbib.io.sshd.eddsa.spec;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.xbib.io.sshd.eddsa.Utils;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EdDSAPrivateKeySpecTest {
|
||||
static final byte[] ZERO_SEED = Utils.hexToBytes("0000000000000000000000000000000000000000000000000000000000000000");
|
||||
static final byte[] ZERO_H = Utils.hexToBytes("5046adc1dba838867b2bbbfdd0c3423e58b57970b5267a90f57960924a87f1960a6a85eaa642dac835424b5d7c8d637c00408c7a73da672b7f498521420b6dd3");
|
||||
static final byte[] ZERO_PK = Utils.hexToBytes("3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29");
|
||||
|
||||
static final EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519");
|
||||
|
||||
@Rule
|
||||
public ExpectedException exception = ExpectedException.none();
|
||||
|
||||
/**
|
||||
* Test method for {@link EdDSAPrivateKeySpec#EdDSAPrivateKeySpec(byte[], EdDSAParameterSpec)}.
|
||||
*/
|
||||
@Test
|
||||
public void testEdDSAPrivateKeySpecFromSeed() {
|
||||
EdDSAPrivateKeySpec key = new EdDSAPrivateKeySpec(ZERO_SEED, ed25519);
|
||||
assertThat(key.getSeed(), is(equalTo(ZERO_SEED)));
|
||||
assertThat(key.getH(), is(equalTo(ZERO_H)));
|
||||
assertThat(key.getA().toByteArray(), is(equalTo(ZERO_PK)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incorrectSeedLengthThrows() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage("seed length is wrong");
|
||||
EdDSAPrivateKeySpec key = new EdDSAPrivateKeySpec(new byte[2], ed25519);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link EdDSAPrivateKeySpec#EdDSAPrivateKeySpec(EdDSAParameterSpec, byte[])}.
|
||||
*/
|
||||
@Test
|
||||
public void testEdDSAPrivateKeySpecFromH() {
|
||||
EdDSAPrivateKeySpec key = new EdDSAPrivateKeySpec(ed25519, ZERO_H);
|
||||
assertThat(key.getSeed(), is(nullValue()));
|
||||
assertThat(key.getH(), is(equalTo(ZERO_H)));
|
||||
assertThat(key.getA().toByteArray(), is(equalTo(ZERO_PK)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incorrectHashLengthThrows() {
|
||||
exception.expect(IllegalArgumentException.class);
|
||||
exception.expectMessage("hash length is wrong");
|
||||
EdDSAPrivateKeySpec key = new EdDSAPrivateKeySpec(ed25519, new byte[2]);
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
{ 853b8cf5c693bc2f190e8cfbc62d93cfc2423d6498480b2765bad4333a9dcf07 },
|
||||
{ 3e9140d70539109db3be40d1059f39fd098a8f683484c1a56712f898922ffd44 },
|
||||
{ 68aa7a870512c9ab9ec4aacc23e8d9268c5943ddcb7d1b5aa8650c9f687b116f },
|
||||
},
|
||||
{
|
||||
{ 3097ee4ca8b025af8a4b86e830845a023267019f02501bc1f4f8809a1b4e167a },
|
||||
{ 65d2fca4e81f61567dbac1e5fd53d33bbdd64b211af3318162da5b558715b92a },
|
||||
{ 89d8d00d3f93ae1462da351c222394584cdbf28c45e570d1c6b4b912af26285a },
|
||||
},
|
||||
{
|
||||
{ 33bba50844bc12a202ed5ec7c348508d44ecbf5a0ceb1bddeb06e246f1cc4529 },
|
||||
{ bad647a4c382917fb729274bd11400d587a064b81cf13ce3f3551beb737e4a15 },
|
||||
{ 85822a81f1dbbbbcfcd1bdd007080e272da7bd1b0b671bb49ab63b6b69beaa43 },
|
||||
},
|
||||
{
|
||||
{ bfa34e94d05c1a6bd2c09db33a357074492e54288252b2717e923c2869ea1b46 },
|
||||
{ b12132aa9a2c6fbaa723ba3b5321a06c3a2c19924f76ea9de017532e5ddd6e1d },
|
||||
{ a2b3b801c86d83f19aa43e05475f03b3f3ad7758ba419c52a7900f6a1cbb9f7a },
|
||||
},
|
||||
{
|
||||
{ 2f63a8a68a672e9bc546bc516f9e50a6b5f586c6c933b2ce597fdd8a33edb934 },
|
||||
{ 64809d037e216ef39b4120f5b681a09844b05ee708c6cb968f9cdcfa515ac049 },
|
||||
{ 1baf4590bfe8b4062fd219a7e883ffe216cfd49329fcf6aa068b001b0272c173 },
|
||||
},
|
||||
{
|
||||
{ de2a808a8400bf2f272e3002cffed9e50634701771843e11af8f6d54e2aa7542 },
|
||||
{ 48438649025b5f318183087769b3d63e95eb8d6a5575a0a37fc7d5298059ab18 },
|
||||
{ e98960fdc52c2bd8a4e48232a1b41e0322861ab59911314448f93db52255c63d },
|
||||
},
|
||||
{
|
||||
{ 6d7f00a222c270bfdbdebcb59ab384bf07ba07fb120e7a5341f246c3eed74f23 },
|
||||
{ 93bf7f323b016f506b6f779bc9ebfcae6859adaa32b2129da72460172d886702 },
|
||||
{ 78a32e7319a1605371d48ddfb1e6372433e5a791f837efa2637809aafda67b49 },
|
||||
},
|
||||
{
|
||||
{ a0eacf1303ccce246d249c188dc24886d0d4f2c1fabdbd2d2be72df11729e261 },
|
||||
{ 0bcf8c4686cd0b04d610992aa49b82d39251b20708300875bf5ed01842cdb543 },
|
||||
{ 16b5d09b2f769a5deede3f374eaf38eb7042d6937d5a2e0342d8e40a21611d51 },
|
||||
},
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,8 @@
|
|||
dependencies {
|
||||
api project(':files-api')
|
||||
api project(':files-ftp')
|
||||
testImplementation "org.mockftpserver:MockFtpServer:${project.property('mockftpserver.version')}"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}"
|
||||
testImplementation "org.mockito:mockito-core:${project.property('mockito.version')}"
|
||||
testImplementation "org.mockito:mockito-junit-jupiter:${project.property('mockito.version')}"
|
||||
testImplementation libs.mockftpserver
|
||||
testImplementation libs.junit.jupiter.params
|
||||
testImplementation libs.mockito.core
|
||||
testImplementation libs.mockito.junit.jupiter
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
dependencies {
|
||||
api project(':files-api')
|
||||
api project(':files-sftp')
|
||||
testImplementation "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}"
|
||||
testImplementation libs.net.security
|
||||
}
|
||||
|
|
|
@ -1,38 +1,24 @@
|
|||
package org.apache.sshd.fs.test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import org.apache.sshd.client.ClientBuilder;
|
||||
import org.apache.sshd.client.SshClient;
|
||||
import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.fs.SftpFileSystem;
|
||||
import org.apache.sshd.fs.SftpFileSystemProvider;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.io.sshd.eddsa.EdDSASecurityProvider;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPair;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.logging.Logger;
|
||||
import org.xbib.net.security.PrivateKeyReader;
|
||||
|
||||
public class SFTPFileSystemTest {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SFTPFileSystemTest.class.getName());
|
||||
|
||||
static {
|
||||
// load bouncy castle provider (and other security providers)
|
||||
for (Provider p : ServiceLoader.load(Provider.class)) {
|
||||
if (Security.getProvider(p.getName()) == null) {
|
||||
Security.addProvider(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void init() throws Exception {
|
||||
Map<String, String> env = new HashMap<>();
|
||||
|
@ -40,12 +26,8 @@ public class SFTPFileSystemTest {
|
|||
URI uri = URI.create("sftp://xbib.org");
|
||||
SshClient sshClient = ClientBuilder.builder().build();
|
||||
Path privateKey = Paths.get(System.getProperty("user.home") + "/.ssh/id_ed25519");
|
||||
KeyPairProvider keyPairProvider = new FileKeyPairProvider(privateKey);
|
||||
keyPairProvider.loadKeys(null).forEach(sshClient::addPublicKeyIdentity);
|
||||
for (String keyType : keyPairProvider.getKeyTypes(null)) {
|
||||
logger.info("found key type = " + keyType);
|
||||
}
|
||||
KeyPair keyPair = keyPairProvider.loadKey(null, "ssh-ed25519");
|
||||
PrivateKeyReader privateKeyReader = new PrivateKeyReader();
|
||||
KeyPair keyPair = privateKeyReader.generateFrom(Files.newInputStream(privateKey), null);
|
||||
sshClient.addPublicKeyIdentity(keyPair);
|
||||
sshClient.setNioWorkers(1);
|
||||
sshClient.start();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
dependencies {
|
||||
api project(':files-eddsa')
|
||||
implementation "org.bouncycastle:bcpkix-jdk15on:${project.property('bouncycastle.version')}"
|
||||
implementation libs.net.security
|
||||
}
|
||||
|
|
|
@ -78,10 +78,7 @@ module org.xbib.files.sftp {
|
|||
exports org.apache.sshd.common.util.net;
|
||||
exports org.apache.sshd.common.util.security;
|
||||
exports org.apache.sshd.common.util.security.eddsa;
|
||||
exports org.apache.sshd.common.util.security.bouncycastle;
|
||||
exports org.apache.sshd.common.util.threads;
|
||||
requires transitive org.xbib.eddsa;
|
||||
requires org.bouncycastle.pkix;
|
||||
requires org.bouncycastle.provider;
|
||||
requires org.xbib.net.security;
|
||||
requires java.logging;
|
||||
}
|
|
@ -125,7 +125,6 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder
|
|||
BuiltinSignatures.nistp521,
|
||||
BuiltinSignatures.ed25519,
|
||||
BuiltinSignatures.sk_ecdsa_sha2_nistp256,
|
||||
BuiltinSignatures.sk_ssh_ed25519,
|
||||
BuiltinSignatures.rsaSHA512,
|
||||
BuiltinSignatures.rsaSHA256,
|
||||
BuiltinSignatures.rsa));
|
||||
|
|
|
@ -71,13 +71,11 @@ import org.apache.sshd.common.config.keys.impl.ECDSAPublicKeyEntryDecoder;
|
|||
import org.apache.sshd.common.config.keys.impl.OpenSSHCertificateDecoder;
|
||||
import org.apache.sshd.common.config.keys.impl.RSAPublicKeyDecoder;
|
||||
import org.apache.sshd.common.config.keys.impl.SkECDSAPublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.digest.BuiltinDigests;
|
||||
import org.apache.sshd.common.digest.Digest;
|
||||
import org.apache.sshd.common.digest.DigestFactory;
|
||||
import org.apache.sshd.common.digest.DigestUtils;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.u2f.SkED25519PublicKey;
|
||||
import org.apache.sshd.common.u2f.SkEcdsaPublicKey;
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.MapEntryUtils.NavigableMapBuilder;
|
||||
|
@ -167,7 +165,6 @@ public final class KeyUtils {
|
|||
}
|
||||
if (SecurityUtils.isEDDSACurveSupported()) {
|
||||
registerPublicKeyEntryDecoder(SecurityUtils.getEDDSAPublicKeyEntryDecoder());
|
||||
registerPublicKeyEntryDecoder(SkED25519PublicKeyEntryDecoder.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1011,8 +1008,6 @@ public final class KeyUtils {
|
|||
} else if ((k1 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k1.getAlgorithm())
|
||||
&& (k2 != null) && SecurityUtils.EDDSA.equalsIgnoreCase(k2.getAlgorithm())) {
|
||||
return SecurityUtils.compareEDDSAPPublicKeys(k1, k2);
|
||||
} else if ((k1 instanceof SkED25519PublicKey) && (k2 instanceof SkED25519PublicKey)) {
|
||||
return compareSkEd25519Keys(SkED25519PublicKey.class.cast(k1), SkED25519PublicKey.class.cast(k2));
|
||||
} else {
|
||||
return false; // either key is null or not of same class
|
||||
}
|
||||
|
@ -1023,8 +1018,6 @@ public final class KeyUtils {
|
|||
return recoverRSAPublicKey((RSAPrivateKey) key);
|
||||
} else if (key instanceof DSAPrivateKey) {
|
||||
return recoverDSAPublicKey((DSAPrivateKey) key);
|
||||
} else if ((key != null) && SecurityUtils.EDDSA.equalsIgnoreCase(key.getAlgorithm())) {
|
||||
return SecurityUtils.recoverEDDSAPublicKey(key);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -1184,16 +1177,4 @@ public final class KeyUtils {
|
|||
&& compareECKeys(k1.getDelegatePublicKey(), k2.getDelegatePublicKey());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean compareSkEd25519Keys(SkED25519PublicKey k1, SkED25519PublicKey k2) {
|
||||
if (Objects.equals(k1, k2)) {
|
||||
return true;
|
||||
} else if (k1 == null || k2 == null) {
|
||||
return false; // both null is covered by Objects#equals
|
||||
} else {
|
||||
return Objects.equals(k1.getAppName(), k2.getAppName())
|
||||
&& Objects.equals(k1.isNoTouchRequired(), k2.isNoTouchRequired())
|
||||
&& SecurityUtils.compareEDDSAPPublicKeys(k1.getDelegatePublicKey(), k2.getDelegatePublicKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.sshd.common.config.keys.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.sshd.common.config.keys.KeyEntryResolver;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.apache.sshd.common.u2f.SkED25519PublicKey;
|
||||
import org.apache.sshd.common.util.security.eddsa.Ed25519PublicKeyDecoder;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class SkED25519PublicKeyEntryDecoder extends AbstractPublicKeyEntryDecoder<SkED25519PublicKey, PrivateKey> {
|
||||
public static final String KEY_TYPE = "sk-ssh-ed25519@openssh.com";
|
||||
public static final int MAX_APP_NAME_LENGTH = 1024;
|
||||
|
||||
public static final SkED25519PublicKeyEntryDecoder INSTANCE = new SkED25519PublicKeyEntryDecoder();
|
||||
|
||||
private static final String NO_TOUCH_REQUIRED_HEADER = "no-touch-required";
|
||||
|
||||
public SkED25519PublicKeyEntryDecoder() {
|
||||
super(SkED25519PublicKey.class, PrivateKey.class, Collections.singleton(KEY_TYPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkED25519PublicKey decodePublicKey(
|
||||
SessionContext session, String keyType, InputStream keyData, Map<String, String> headers)
|
||||
throws IOException, GeneralSecurityException {
|
||||
if (!KEY_TYPE.equals(keyType)) {
|
||||
throw new InvalidKeySpecException("Invalid keyType: " + keyType);
|
||||
}
|
||||
|
||||
boolean noTouchRequired = parseBooleanHeader(headers, NO_TOUCH_REQUIRED_HEADER, false);
|
||||
EdDSAPublicKey edDSAPublicKey
|
||||
= Ed25519PublicKeyDecoder.INSTANCE.decodePublicKey(session, KeyPairProvider.SSH_ED25519, keyData, headers);
|
||||
String appName = KeyEntryResolver.decodeString(keyData, MAX_APP_NAME_LENGTH);
|
||||
return new SkED25519PublicKey(appName, noTouchRequired, edDSAPublicKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkED25519PublicKey clonePublicKey(SkED25519PublicKey key) {
|
||||
if (key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SkED25519PublicKey(key.getAppName(), key.isNoTouchRequired(), key.getDelegatePublicKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encodePublicKey(OutputStream s, SkED25519PublicKey key) throws IOException {
|
||||
Objects.requireNonNull(key, "No public key provided");
|
||||
KeyEntryResolver.encodeString(s, KEY_TYPE);
|
||||
byte[] seed = Ed25519PublicKeyDecoder.getSeedValue(key.getDelegatePublicKey());
|
||||
KeyEntryResolver.writeRLEBytes(s, seed);
|
||||
KeyEntryResolver.encodeString(s, key.getAppName());
|
||||
return KEY_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateKey clonePrivateKey(PrivateKey key) {
|
||||
throw new UnsupportedOperationException("Private key operations are not supported for security keys.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyFactory getKeyFactoryInstance() {
|
||||
throw new UnsupportedOperationException("Private key operations are not supported for security keys.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair generateKeyPair(int keySize) {
|
||||
throw new UnsupportedOperationException("Private key operations are not supported for security keys.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPairGenerator getKeyPairGenerator() {
|
||||
throw new UnsupportedOperationException("Private key operations are not supported for security keys.");
|
||||
}
|
||||
}
|
|
@ -39,7 +39,6 @@ import org.apache.sshd.common.cipher.ECCurves;
|
|||
import org.apache.sshd.common.config.NamedFactoriesListParseResult;
|
||||
import org.apache.sshd.common.config.keys.KeyUtils;
|
||||
import org.apache.sshd.common.config.keys.impl.SkECDSAPublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.ValidateUtils;
|
||||
|
@ -246,17 +245,6 @@ public enum BuiltinSignatures implements SignatureFactory {
|
|||
return SecurityUtils.getEDDSASigner();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return SecurityUtils.isEDDSACurveSupported();
|
||||
}
|
||||
},
|
||||
sk_ssh_ed25519(SkED25519PublicKeyEntryDecoder.KEY_TYPE) {
|
||||
@Override
|
||||
public Signature create() {
|
||||
return new SignatureSkED25519();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return SecurityUtils.isEDDSACurveSupported();
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.sshd.common.signature;
|
||||
|
||||
import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
|
||||
public class SignatureSkED25519 extends AbstractSecurityKeySignature {
|
||||
|
||||
public static final String ALGORITHM = "ED25519-SK";
|
||||
|
||||
public SignatureSkED25519() {
|
||||
super(SkED25519PublicKeyEntryDecoder.KEY_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return ALGORITHM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSignatureKeyType() {
|
||||
return KeyPairProvider.SSH_ED25519;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Signature getDelegateSignature() {
|
||||
return SecurityUtils.getEDDSASigner();
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.sshd.common.u2f;
|
||||
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
|
||||
public class SkED25519PublicKey implements SecurityKeyPublicKey<EdDSAPublicKey> {
|
||||
|
||||
public static final String ALGORITHM = "ED25519-SK";
|
||||
|
||||
private static final long serialVersionUID = 4587115316266869640L;
|
||||
|
||||
private final String appName;
|
||||
private final boolean noTouchRequired;
|
||||
private final EdDSAPublicKey delegatePublicKey;
|
||||
|
||||
public SkED25519PublicKey(String appName, boolean noTouchRequired, EdDSAPublicKey delegatePublicKey) {
|
||||
this.appName = appName;
|
||||
this.noTouchRequired = noTouchRequired;
|
||||
this.delegatePublicKey = delegatePublicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return ALGORITHM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNoTouchRequired() {
|
||||
return noTouchRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EdDSAPublicKey getDelegatePublicKey() {
|
||||
return delegatePublicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()
|
||||
+ "[appName=" + getAppName()
|
||||
+ ", noTouchRequired=" + isNoTouchRequired()
|
||||
+ ", delegatePublicKey=" + getDelegatePublicKey()
|
||||
+ "]";
|
||||
}
|
||||
}
|
|
@ -60,8 +60,7 @@ public interface BufferPublicKeyParser<PUB extends PublicKey> {
|
|||
ECBufferPublicKeyParser.INSTANCE,
|
||||
SkECBufferPublicKeyParser.INSTANCE,
|
||||
ED25519BufferPublicKeyParser.INSTANCE,
|
||||
OpenSSHCertPublicKeyParser.INSTANCE,
|
||||
SkED25519BufferPublicKeyParser.INSTANCE));
|
||||
OpenSSHCertPublicKeyParser.INSTANCE));
|
||||
|
||||
/**
|
||||
* @param keyType The key type - e.g., "ssh-rsa", "ssh-dss"
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.sshd.common.util.buffer.keys;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
|
||||
import org.apache.sshd.common.config.keys.impl.SkED25519PublicKeyEntryDecoder;
|
||||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.u2f.SkED25519PublicKey;
|
||||
import org.apache.sshd.common.util.buffer.Buffer;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class SkED25519BufferPublicKeyParser extends AbstractBufferPublicKeyParser<SkED25519PublicKey> {
|
||||
public static final SkED25519BufferPublicKeyParser INSTANCE = new SkED25519BufferPublicKeyParser();
|
||||
|
||||
public SkED25519BufferPublicKeyParser() {
|
||||
super(SkED25519PublicKey.class, SkED25519PublicKeyEntryDecoder.KEY_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkED25519PublicKey getRawPublicKey(String keyType, Buffer buffer) throws GeneralSecurityException {
|
||||
// The "sk-ssh-ed25519@openssh.com" keytype has the same format as the "ssh-ed25519" keytype with an appname on
|
||||
// the end
|
||||
PublicKey publicKey = ED25519BufferPublicKeyParser.INSTANCE.getRawPublicKey(KeyPairProvider.SSH_ED25519, buffer);
|
||||
String appName = buffer.getString();
|
||||
return new SkED25519PublicKey(appName, false, (EdDSAPublicKey) publicKey);
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ package org.apache.sshd.common.util.security;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.file.Path;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
|
@ -72,8 +71,6 @@ import org.apache.sshd.common.session.SessionContext;
|
|||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.ValidateUtils;
|
||||
import org.apache.sshd.common.util.buffer.Buffer;
|
||||
import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleKeyPairResourceParser;
|
||||
import org.apache.sshd.common.util.security.bouncycastle.BouncyCastleRandomFactory;
|
||||
import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
|
||||
import org.apache.sshd.common.util.threads.ThreadUtils;
|
||||
|
||||
|
@ -127,17 +124,7 @@ public final class SecurityUtils {
|
|||
*/
|
||||
public static final String SECURITY_PROVIDER_REGISTRARS = "org.apache.sshd.security.registrars";
|
||||
public static final List<String> DEFAULT_SECURITY_PROVIDER_REGISTRARS = Collections.unmodifiableList(
|
||||
Arrays.asList(
|
||||
"org.apache.sshd.common.util.security.bouncycastle.BouncyCastleSecurityProviderRegistrar",
|
||||
"org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"));
|
||||
|
||||
/**
|
||||
* System property used to control whether to automatically register the {@code Bouncyastle} JCE provider
|
||||
*
|
||||
* @deprecated Please use "org.apache.sshd.security.provider.BC.enabled"
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle";
|
||||
Arrays.asList("org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderRegistrar"));
|
||||
|
||||
/**
|
||||
* System property used to control whether Elliptic Curves are supported or not. If not set then the support is
|
||||
|
@ -146,16 +133,6 @@ public final class SecurityUtils {
|
|||
*/
|
||||
public static final String ECC_SUPPORTED_PROP = "org.apache.sshd.eccSupport";
|
||||
|
||||
/**
|
||||
* System property used to decide whether EDDSA curves are supported or not (in addition or even in spite of
|
||||
* {@link #isEDDSACurveSupported()}). If not set or set to {@code true}, then the existence of the optional support
|
||||
* classes determines the support.
|
||||
*
|
||||
* @deprecated Please use "org.apache.sshd.security.provider.EdDSA.enabled&qupt;
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String EDDSA_SUPPORTED_PROP = "org.apache.sshd.eddsaSupport";
|
||||
|
||||
public static final String PROP_DEFAULT_SECURITY_PROVIDER = "org.apache.sshd.security.defaultProvider";
|
||||
|
||||
private static final AtomicInteger MIN_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
|
||||
|
@ -508,23 +485,11 @@ public final class SecurityUtils {
|
|||
return ids;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
public static KeyPairResourceParser getBouncycastleKeyPairResourceParser() {
|
||||
ValidateUtils.checkTrue(isBouncyCastleRegistered(), "BouncyCastle not registered");
|
||||
return BouncyCastleKeyPairResourceParser.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If {@link #isBouncyCastleRegistered()} then a {@link BouncyCastleRandomFactory} instance, otherwise a
|
||||
* {@link JceRandomFactory} one
|
||||
* @return a {@link JceRandomFactory} one
|
||||
*/
|
||||
public static RandomFactory getRandomFactory() {
|
||||
if (isBouncyCastleRegistered()) {
|
||||
return BouncyCastleRandomFactory.INSTANCE;
|
||||
} else {
|
||||
return JceRandomFactory.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////// ED25519 support ///////////////////////////////
|
||||
|
@ -545,7 +510,6 @@ public final class SecurityUtils {
|
|||
if (!isEDDSACurveSupported()) {
|
||||
throw new UnsupportedOperationException(EDDSA + " provider N/A");
|
||||
}
|
||||
|
||||
return EdDSASecurityProviderUtils.getEDDSAPublicKeyEntryDecoder();
|
||||
}
|
||||
|
||||
|
@ -585,14 +549,6 @@ public final class SecurityUtils {
|
|||
return isEDDSACurveSupported() ? EdDSASecurityProviderUtils.compareEDDSAPrivateKeys(k1, k2) : false;
|
||||
}
|
||||
|
||||
public static PublicKey recoverEDDSAPublicKey(PrivateKey key) throws GeneralSecurityException {
|
||||
if (!isEDDSACurveSupported()) {
|
||||
throw new NoSuchAlgorithmException(EDDSA + " provider not supported");
|
||||
}
|
||||
|
||||
return EdDSASecurityProviderUtils.recoverEDDSAPublicKey(key);
|
||||
}
|
||||
|
||||
public static PublicKey generateEDDSAPublicKey(String keyType, byte[] seed) throws GeneralSecurityException {
|
||||
if (!KeyPairProvider.SSH_ED25519.equals(keyType)) {
|
||||
throw new InvalidKeyException("Unsupported key type: " + keyType);
|
||||
|
@ -637,8 +593,6 @@ public final class SecurityUtils {
|
|||
throw new GeneralSecurityException("Full SSHD-440 implementation N/A");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static KeyPairResourceParser getKeyPairResourceParser() {
|
||||
KeyPairResourceParser parser;
|
||||
synchronized (KEYPAIRS_PARSER_HODLER) {
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.sshd.common.util.security.bouncycastle;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ProtocolException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.security.auth.login.CredentialException;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
|
||||
import org.apache.sshd.common.NamedResource;
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider;
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider.ResourceDecodeResult;
|
||||
import org.apache.sshd.common.config.keys.loader.AbstractKeyPairResourceParser;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.io.IoUtils;
|
||||
import org.apache.sshd.common.util.security.SecurityProviderRegistrar;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.bouncycastle.openssl.PEMDecryptorProvider;
|
||||
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class BouncyCastleKeyPairResourceParser extends AbstractKeyPairResourceParser {
|
||||
public static final List<String> BEGINNERS = Collections.unmodifiableList(
|
||||
Arrays.asList(
|
||||
"BEGIN RSA PRIVATE KEY",
|
||||
"BEGIN DSA PRIVATE KEY",
|
||||
"BEGIN EC PRIVATE KEY"));
|
||||
|
||||
public static final List<String> ENDERS = Collections.unmodifiableList(
|
||||
Arrays.asList(
|
||||
"END RSA PRIVATE KEY",
|
||||
"END DSA PRIVATE KEY",
|
||||
"END EC PRIVATE KEY"));
|
||||
|
||||
public static final BouncyCastleKeyPairResourceParser INSTANCE = new BouncyCastleKeyPairResourceParser();
|
||||
|
||||
public BouncyCastleKeyPairResourceParser() {
|
||||
super(BEGINNERS, ENDERS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<KeyPair> extractKeyPairs(
|
||||
SessionContext session, NamedResource resourceKey,
|
||||
String beginMarker, String endMarker,
|
||||
FilePasswordProvider passwordProvider,
|
||||
List<String> lines, Map<String, String> headers)
|
||||
throws IOException, GeneralSecurityException {
|
||||
StringBuilder writer = new StringBuilder(beginMarker.length() + endMarker.length() + lines.size() * 80);
|
||||
writer.append(beginMarker).append(IoUtils.EOL);
|
||||
lines.forEach(l -> writer.append(l).append(IoUtils.EOL));
|
||||
writer.append(endMarker).append(IoUtils.EOL);
|
||||
|
||||
String data = writer.toString();
|
||||
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
|
||||
try (InputStream bais = new ByteArrayInputStream(dataBytes)) {
|
||||
return extractKeyPairs(session, resourceKey, beginMarker, endMarker, passwordProvider, bais, headers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<KeyPair> extractKeyPairs(
|
||||
SessionContext session, NamedResource resourceKey,
|
||||
String beginMarker, String endMarker,
|
||||
FilePasswordProvider passwordProvider,
|
||||
InputStream stream, Map<String, String> headers)
|
||||
throws IOException, GeneralSecurityException {
|
||||
KeyPair kp = loadKeyPair(session, resourceKey, stream, passwordProvider);
|
||||
return (kp == null) ? Collections.emptyList() : Collections.singletonList(kp);
|
||||
}
|
||||
|
||||
public static KeyPair loadKeyPair(
|
||||
SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
|
||||
throws IOException, GeneralSecurityException {
|
||||
try (PEMParser r = new PEMParser(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||
Object o = r.readObject();
|
||||
|
||||
SecurityProviderRegistrar registrar = SecurityUtils.getRegisteredProvider(SecurityUtils.BOUNCY_CASTLE);
|
||||
if (registrar == null) {
|
||||
throw new NoSuchProviderException(SecurityUtils.BOUNCY_CASTLE + " registrar not available");
|
||||
}
|
||||
|
||||
JcaPEMKeyConverter pemConverter = new JcaPEMKeyConverter();
|
||||
if (registrar.isNamedProviderUsed()) {
|
||||
pemConverter.setProvider(registrar.getName());
|
||||
} else {
|
||||
pemConverter.setProvider(registrar.getSecurityProvider());
|
||||
}
|
||||
|
||||
if (o instanceof PEMEncryptedKeyPair) {
|
||||
if (provider == null) {
|
||||
throw new CredentialException("Missing password provider for encrypted resource=" + resourceKey);
|
||||
}
|
||||
|
||||
for (int retryIndex = 0;; retryIndex++) {
|
||||
String password = provider.getPassword(session, resourceKey, retryIndex);
|
||||
PEMKeyPair decoded;
|
||||
try {
|
||||
if (GenericUtils.isEmpty(password)) {
|
||||
throw new FailedLoginException("No password data for encrypted resource=" + resourceKey);
|
||||
}
|
||||
|
||||
JcePEMDecryptorProviderBuilder decryptorBuilder = new JcePEMDecryptorProviderBuilder();
|
||||
PEMDecryptorProvider pemDecryptor = decryptorBuilder.build(password.toCharArray());
|
||||
decoded = ((PEMEncryptedKeyPair) o).decryptKeyPair(pemDecryptor);
|
||||
} catch (IOException | GeneralSecurityException | RuntimeException e) {
|
||||
ResourceDecodeResult result
|
||||
= provider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, e);
|
||||
if (result == null) {
|
||||
result = ResourceDecodeResult.TERMINATE;
|
||||
}
|
||||
switch (result) {
|
||||
case TERMINATE:
|
||||
throw e;
|
||||
case RETRY:
|
||||
continue;
|
||||
case IGNORE:
|
||||
return null;
|
||||
default:
|
||||
throw new ProtocolException(
|
||||
"Unsupported decode attempt result (" + result + ") for " + resourceKey);
|
||||
}
|
||||
}
|
||||
|
||||
o = decoded;
|
||||
provider.handleDecodeAttemptResult(session, resourceKey, retryIndex, password, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (o instanceof PEMKeyPair) {
|
||||
return pemConverter.getKeyPair((PEMKeyPair) o);
|
||||
} else if (o instanceof KeyPair) {
|
||||
return (KeyPair) o;
|
||||
} else {
|
||||
throw new IOException("Failed to read " + resourceKey + " - unknown result object: " + o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.sshd.common.util.security.bouncycastle;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import org.apache.sshd.common.random.AbstractRandom;
|
||||
import org.apache.sshd.common.util.ValidateUtils;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.bouncycastle.crypto.prng.RandomGenerator;
|
||||
import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
|
||||
|
||||
/**
|
||||
* BouncyCastle <code>Random</code>. This pseudo random number generator uses the a very fast PRNG from BouncyCastle.
|
||||
* The JRE random will be used when creating a new generator to add some random data to the seed.
|
||||
*
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public final class BouncyCastleRandom extends AbstractRandom {
|
||||
public static final String NAME = SecurityUtils.BOUNCY_CASTLE;
|
||||
private final RandomGenerator random;
|
||||
|
||||
public BouncyCastleRandom() {
|
||||
ValidateUtils.checkTrue(SecurityUtils.isBouncyCastleRegistered(), "BouncyCastle not registered");
|
||||
this.random = new VMPCRandomGenerator();
|
||||
byte[] seed = new SecureRandom().generateSeed(8);
|
||||
this.random.addSeedMaterial(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(byte[] bytes, int start, int len) {
|
||||
this.random.nextBytes(bytes, start, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pseudo-random uniformly distributed {@code int} in the half-open range [0, n).
|
||||
*/
|
||||
@Override
|
||||
public int random(int n) {
|
||||
ValidateUtils.checkTrue(n > 0, "Limit must be positive: %d", n);
|
||||
if ((n & -n) == n) {
|
||||
return (int) ((n * (long) next(31)) >> 31);
|
||||
}
|
||||
|
||||
int bits;
|
||||
int val;
|
||||
do {
|
||||
bits = next(31);
|
||||
val = bits % n;
|
||||
} while (bits - val + (n - 1) < 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
private int next(int numBits) {
|
||||
int bytes = (numBits + 7) / 8;
|
||||
byte next[] = new byte[bytes];
|
||||
int ret = 0;
|
||||
random.nextBytes(next);
|
||||
for (int i = 0; i < bytes; i++) {
|
||||
ret = (next[i] & 0xFF) | (ret << 8);
|
||||
}
|
||||
return ret >>> (bytes * 8 - numBits);
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.sshd.common.util.security.bouncycastle;
|
||||
|
||||
import org.apache.sshd.common.random.AbstractRandomFactory;
|
||||
import org.apache.sshd.common.random.Random;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
|
||||
/**
|
||||
* Named factory for the BouncyCastle <code>Random</code>
|
||||
*/
|
||||
public final class BouncyCastleRandomFactory extends AbstractRandomFactory {
|
||||
public static final String NAME = "bouncycastle";
|
||||
public static final BouncyCastleRandomFactory INSTANCE = new BouncyCastleRandomFactory();
|
||||
|
||||
public BouncyCastleRandomFactory() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return SecurityUtils.isBouncyCastleRegistered();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Random create() {
|
||||
return new BouncyCastleRandom();
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.sshd.common.util.security.bouncycastle;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.Provider;
|
||||
import java.security.Signature;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.ReflectionUtils;
|
||||
import org.apache.sshd.common.util.security.AbstractSecurityProviderRegistrar;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.apache.sshd.common.util.threads.ThreadUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class BouncyCastleSecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
|
||||
// We want to use reflection API so as not to require BouncyCastle to be present in the classpath
|
||||
public static final String PROVIDER_CLASS = "org.bouncycastle.jce.provider.BouncyCastleProvider";
|
||||
// Do not define a static registrar instance to minimize class loading issues
|
||||
private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
|
||||
private final AtomicReference<String> allSupportHolder = new AtomicReference<>();
|
||||
|
||||
public BouncyCastleSecurityProviderRegistrar() {
|
||||
super(SecurityUtils.BOUNCY_CASTLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
if (!super.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
return this.getBooleanProperty(SecurityUtils.REGISTER_BOUNCY_CASTLE_PROP, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Provider getSecurityProvider() {
|
||||
try {
|
||||
return getOrCreateProvider(PROVIDER_CLASS);
|
||||
} catch (ReflectiveOperationException t) {
|
||||
Throwable e = GenericUtils.peelException(t);
|
||||
if (e instanceof RuntimeException) {
|
||||
throw (RuntimeException) e;
|
||||
}
|
||||
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultSecurityEntitySupportValue(Class<?> entityType) {
|
||||
String allValue = allSupportHolder.get();
|
||||
if (GenericUtils.length(allValue) > 0) {
|
||||
return allValue;
|
||||
}
|
||||
|
||||
String propName = getConfigurationPropertyName("supportAll");
|
||||
allValue = this.getStringProperty(propName, ALL_OPTIONS_VALUE);
|
||||
if (GenericUtils.isEmpty(allValue)) {
|
||||
allValue = NO_OPTIONS_VALUE;
|
||||
}
|
||||
|
||||
allSupportHolder.set(allValue);
|
||||
return allValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
|
||||
if (!isSupported()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some known values it does not support
|
||||
if (KeyPairGenerator.class.isAssignableFrom(entityType)
|
||||
|| KeyFactory.class.isAssignableFrom(entityType)) {
|
||||
if (Objects.compare(name, SecurityUtils.EDDSA, String.CASE_INSENSITIVE_ORDER) == 0) {
|
||||
return false;
|
||||
}
|
||||
} else if (Signature.class.isAssignableFrom(entityType)) {
|
||||
if (Objects.compare(name, SecurityUtils.CURVE_ED25519_SHA512, String.CASE_INSENSITIVE_ORDER) == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return super.isSecurityEntitySupported(entityType, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
Boolean supported;
|
||||
synchronized (supportHolder) {
|
||||
supported = supportHolder.get();
|
||||
if (supported != null) {
|
||||
return supported.booleanValue();
|
||||
}
|
||||
|
||||
ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
|
||||
supported = ReflectionUtils.isClassAvailable(cl, PROVIDER_CLASS);
|
||||
supportHolder.set(supported);
|
||||
}
|
||||
|
||||
return supported.booleanValue();
|
||||
}
|
||||
}
|
|
@ -42,12 +42,12 @@ import org.apache.sshd.common.util.io.der.ASN1Object;
|
|||
import org.apache.sshd.common.util.io.der.ASN1Type;
|
||||
import org.apache.sshd.common.util.io.der.DERParser;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAKey;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.net.security.eddsa.EdDSAKey;
|
||||
import org.xbib.net.security.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.net.security.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
|
@ -55,10 +55,10 @@ import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
|||
public class Ed25519PEMResourceKeyParser extends AbstractPEMResourceKeyPairParser {
|
||||
// TODO find out how the markers really look like for now provide something
|
||||
public static final String BEGIN_MARKER = "BEGIN EDDSA PRIVATE KEY";
|
||||
public static final List<String> BEGINNERS = Collections.unmodifiableList(Collections.singletonList(BEGIN_MARKER));
|
||||
public static final List<String> BEGINNERS = Collections.singletonList(BEGIN_MARKER);
|
||||
|
||||
public static final String END_MARKER = "END EDDSA PRIVATE KEY";
|
||||
public static final List<String> ENDERS = Collections.unmodifiableList(Collections.singletonList(END_MARKER));
|
||||
public static final List<String> ENDERS = Collections.singletonList(END_MARKER);
|
||||
|
||||
/**
|
||||
* @see <A HREF="https://tools.ietf.org/html/rfc8410#section-3">RFC8412 section 3</A>
|
||||
|
@ -174,7 +174,6 @@ public class Ed25519PEMResourceKeyParser extends AbstractPEMResourceKeyPairParse
|
|||
if (!SecurityUtils.isEDDSACurveSupported()) {
|
||||
throw new NoSuchAlgorithmException(SecurityUtils.EDDSA + " provider not supported");
|
||||
}
|
||||
|
||||
EdDSAParameterSpec params = EdDSANamedCurveTable.getByName(EdDSASecurityProviderUtils.CURVE_ED25519_SHA512);
|
||||
EdDSAPrivateKeySpec keySpec = new EdDSAPrivateKeySpec(seed, params);
|
||||
KeyFactory factory = SecurityUtils.getKeyFactory(SecurityUtils.EDDSA);
|
||||
|
|
|
@ -33,10 +33,10 @@ import org.apache.sshd.common.config.keys.impl.AbstractPublicKeyEntryDecoder;
|
|||
import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import org.xbib.net.security.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.net.security.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.apache.sshd.common.util.threads.ThreadUtils;
|
|||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegistrar {
|
||||
public static final String PROVIDER_CLASS = "org.xbib.io.sshd.eddsa.EdDSASecurityProvider";
|
||||
public static final String PROVIDER_CLASS = "org.xbib.net.security.eddsa.EdDSASecurityProvider";
|
||||
// Do not define a static registrar instance to minimize class loading issues
|
||||
private final AtomicReference<Boolean> supportHolder = new AtomicReference<>(null);
|
||||
|
||||
|
@ -45,12 +45,7 @@ public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegi
|
|||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
if (!super.isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For backward compatibility
|
||||
return this.getBooleanProperty(SecurityUtils.EDDSA_SUPPORTED_PROP, true);
|
||||
return super.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -93,7 +88,7 @@ public class EdDSASecurityProviderRegistrar extends AbstractSecurityProviderRegi
|
|||
}
|
||||
|
||||
ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(getClass());
|
||||
supported = ReflectionUtils.isClassAvailable(cl, "org.xbib.io.sshd.eddsa.EdDSAKey");
|
||||
supported = ReflectionUtils.isClassAvailable(cl, "org.xbib.net.security.eddsa.EdDSAKey");
|
||||
supportHolder.set(supported);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,14 @@ import org.apache.sshd.common.config.keys.PublicKeyEntryDecoder;
|
|||
import org.apache.sshd.common.util.ValidateUtils;
|
||||
import org.apache.sshd.common.util.buffer.Buffer;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAEngine;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAKey;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import org.xbib.net.security.eddsa.EdDSAEngine;
|
||||
import org.xbib.net.security.eddsa.EdDSAKey;
|
||||
import org.xbib.net.security.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.net.security.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
|
|
|
@ -39,12 +39,12 @@ import org.apache.sshd.common.session.SessionContext;
|
|||
import org.apache.sshd.common.util.GenericUtils;
|
||||
import org.apache.sshd.common.util.io.SecureByteArrayOutputStream;
|
||||
import org.apache.sshd.common.util.security.SecurityUtils;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.io.sshd.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import org.xbib.net.security.eddsa.EdDSAPrivateKey;
|
||||
import org.xbib.net.security.eddsa.EdDSAPublicKey;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSANamedCurveTable;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAParameterSpec;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import org.xbib.net.security.eddsa.spec.EdDSAPublicKeySpec;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.apache.sshd.common.keyprovider.KeyPairProvider;
|
|||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.apache.sshd.common.signature.AbstractSignature;
|
||||
import org.apache.sshd.common.util.ValidateUtils;
|
||||
import org.xbib.io.sshd.eddsa.EdDSAEngine;
|
||||
import org.xbib.net.security.eddsa.EdDSAEngine;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
|
@ -37,15 +37,12 @@ public class SignatureEd25519 extends AbstractSignature {
|
|||
@Override
|
||||
public boolean verify(SessionContext session, byte[] sig) throws Exception {
|
||||
byte[] data = sig;
|
||||
Map.Entry<String, byte[]> encoding
|
||||
= extractEncodedSignature(data, k -> KeyPairProvider.SSH_ED25519.equalsIgnoreCase(k));
|
||||
Map.Entry<String, byte[]> encoding = extractEncodedSignature(data, KeyPairProvider.SSH_ED25519::equalsIgnoreCase);
|
||||
if (encoding != null) {
|
||||
String keyType = encoding.getKey();
|
||||
ValidateUtils.checkTrue(
|
||||
KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
|
||||
ValidateUtils.checkTrue(KeyPairProvider.SSH_ED25519.equals(keyType), "Mismatched key type: %s", keyType);
|
||||
data = encoding.getValue();
|
||||
}
|
||||
|
||||
return doVerify(data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.sshd.common.util.security.xbib;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.apache.sshd.common.NamedResource;
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider;
|
||||
import org.apache.sshd.common.config.keys.loader.KeyPairResourceParser;
|
||||
import org.apache.sshd.common.session.SessionContext;
|
||||
import org.xbib.net.security.PrivateKeyReader;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
|
||||
*/
|
||||
public class XbibKeyPairResourceParser implements KeyPairResourceParser {
|
||||
|
||||
public static final XbibKeyPairResourceParser INSTANCE = new XbibKeyPairResourceParser();
|
||||
|
||||
public XbibKeyPairResourceParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExtractKeyPairs(NamedResource resourceKey, List<String> lines) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<KeyPair> loadKeyPairs(SessionContext session,
|
||||
NamedResource resourceKey,
|
||||
FilePasswordProvider passwordProvider,
|
||||
List<String> lines) throws IOException, GeneralSecurityException {
|
||||
PrivateKeyReader privateKeyReader = new PrivateKeyReader();
|
||||
InputStream inputStream = new ByteArrayInputStream(String.join("\n", lines).getBytes(StandardCharsets.US_ASCII));
|
||||
String password = passwordProvider.getPassword(session, resourceKey, 0);
|
||||
return Collections.singleton(privateKeyReader.generateFrom(inputStream, password));
|
||||
}
|
||||
}
|
|
@ -1,12 +1,5 @@
|
|||
group = org.xbib
|
||||
name = files
|
||||
version = 3.0.0
|
||||
version = 4.0.0
|
||||
|
||||
org.gradle.warning.mode = ALL
|
||||
gradle.wrapper.version = 7.5.1
|
||||
bouncycastle.version = 1.69
|
||||
log4j.version = 2.14.1
|
||||
mockftpserver.version = 2.7.1
|
||||
junit.version = 5.8.0
|
||||
junit4.version = 4.13.2
|
||||
mockito.version = 3.7.7
|
||||
|
|
|
@ -6,20 +6,19 @@ java {
|
|||
}
|
||||
|
||||
compileJava {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
compileTestJava {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes('Implementation-Title': project.name)
|
||||
attributes('Implementation-Version': project.version)
|
||||
attributes('Implementation-Vendor': 'Jörg Prante')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,9 +37,11 @@ artifacts {
|
|||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << '-Xlint:all'
|
||||
options.compilerArgs.add('-Xlint:all,-exports')
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
||||
javadoc {
|
||||
tasks.withType(Javadoc) {
|
||||
options.addStringOption('Xdoclint:none', '-quiet')
|
||||
options.encoding = 'UTF-8'
|
||||
}
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
|
||||
def junitVersion = project.hasProperty('junit.version')?project.property('junit.version'):'5.6.2'
|
||||
def hamcrestVersion = project.hasProperty('hamcrest.version')?project.property('hamcrest.version'):'2.2'
|
||||
|
||||
dependencies {
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitVersion}"
|
||||
testImplementation "org.hamcrest:hamcrest-library:${hamcrestVersion}"
|
||||
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}"
|
||||
testImplementation libs.junit.jupiter.api
|
||||
testImplementation libs.junit.jupiter.params
|
||||
testImplementation libs.hamcrest
|
||||
testRuntimeOnly libs.junit.jupiter.engine
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
|
||||
failFast = false
|
||||
testLogging {
|
||||
events 'STARTED', 'PASSED', 'FAILED', 'SKIPPED'
|
||||
|
@ -25,4 +20,5 @@ test {
|
|||
"${result.skippedTestCount} skipped"
|
||||
}
|
||||
}
|
||||
systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties'
|
||||
}
|
||||
|
|
|
@ -1,5 +1,39 @@
|
|||
pluginManagement {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral {
|
||||
metadataSources {
|
||||
mavenPom()
|
||||
artifact()
|
||||
ignoreGradleMetadataRedirection()
|
||||
}
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
version('gradle', '7.5.1')
|
||||
version('groovy', '3.0.10')
|
||||
version('junit', '5.9.1')
|
||||
version('net', '3.0.2')
|
||||
library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')
|
||||
library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit')
|
||||
library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit')
|
||||
library('junit-vintage-engine', 'org.junit.vintage', 'junit-vintage-engine').versionRef('junit')
|
||||
library('hamcrest', 'org.hamcrest', 'hamcrest-library').version('2.2')
|
||||
library('junit4', 'junit', 'junit').version('4.13.2')
|
||||
library('net-security', 'org.xbib', 'net-security').versionRef('net')
|
||||
library('mockftpserver', 'org.mockftpserver', 'MockFtpServer').version('2.7.1')
|
||||
library('mockito-core', 'org.mockito', 'mockito-core').version('3.7.7')
|
||||
library('mockito-junit-jupiter', 'org.mockito', 'mockito-junit-jupiter').version('3.7.7')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include 'files-api'
|
||||
include 'files-eddsa'
|
||||
include 'files-zlib'
|
||||
include 'files-ftp'
|
||||
include 'files-ftp-fs'
|
||||
|
|
Loading…
Reference in a new issue