Crypto: RedDSAEngine and generateAlpha() for Encrypted LS2

This commit is contained in:
zzz
2019-02-20 15:00:54 +00:00
parent e34b646231
commit 17270b1502
4 changed files with 129 additions and 7 deletions

View File

@ -1,10 +1,16 @@
package net.i2p.crypto;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
import net.i2p.I2PAppContext;
import net.i2p.crypto.eddsa.EdDSABlinding;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
@ -20,6 +26,15 @@ public final class Blinding {
private static final SigType TYPE = SigType.EdDSA_SHA512_Ed25519;
private static final SigType TYPER = SigType.RedDSA_SHA512_Ed25519;
private static final String INFO = "i2pblinding1";
// following copied from RouterKeyGenerator
private static final String FORMAT = "yyyyMMdd";
private static final int LENGTH = FORMAT.length();
private static final SimpleDateFormat _fmt = new SimpleDateFormat(FORMAT, Locale.US);
static {
_fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
}
private Blinding() {}
@ -89,6 +104,40 @@ public final class Blinding {
}
}
/**
* Only for SigType EdDSA_SHA512_Ed25519.
*
* @param dest spk must be SigType EdDSA_SHA512_Ed25519
* @param secret may be null or zero-length
* @return SigType RedDSA_SHA512_Ed25519
* @throws UnsupportedOperationException unless supported SigTypes
* @throws IllegalArgumentException on bad inputs
* @since 0.9.39
*/
public static SigningPrivateKey generateAlpha(I2PAppContext ctx, Destination dest, String secret) {
long now = ctx.clock().now();
String modVal;
synchronized(_fmt) {
modVal = _fmt.format(now);
}
if (modVal.length() != LENGTH)
throw new IllegalStateException();
byte[] mod = DataHelper.getASCII(modVal);
byte[] data;
if (secret != null && secret.length() > 0) {
data = new byte[LENGTH + secret.length()];
System.arraycopy(mod, 0, data, 0, LENGTH);
System.arraycopy(DataHelper.getASCII(secret), 0, data, LENGTH, secret.length());
} else {
data = mod;
}
HKDF hkdf = new HKDF(ctx);
byte[] out = new byte[64];
hkdf.calculate(dest.getHash().getData(), data, INFO, out, out, 32);
byte[] b = EdDSABlinding.reduce(out);
return new SigningPrivateKey(TYPER, b);
}
/******
public static void main(String args[]) throws Exception {
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);

View File

@ -44,6 +44,7 @@ import java.security.interfaces.RSAKey;
import net.i2p.I2PAppContext;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAKey;
import net.i2p.crypto.eddsa.RedDSAEngine;
import net.i2p.data.Hash;
import net.i2p.data.Signature;
import net.i2p.data.SigningPrivateKey;
@ -520,7 +521,8 @@ public final class DSAEngine {
boolean rv;
if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
// take advantage of one-shot mode
EdDSAEngine jsig = new EdDSAEngine(type.getDigestInstance());
MessageDigest md = type.getDigestInstance();
EdDSAEngine jsig = (type == SigType.RedDSA_SHA512_Ed25519) ? new RedDSAEngine(md) : new EdDSAEngine(md);
jsig.initVerify(pubKey);
rv = jsig.verifyOneShot(data, offset, len, sigbytes);
} else {
@ -573,7 +575,7 @@ public final class DSAEngine {
if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
// take advantage of one-shot mode
// Ignore algo, EdDSAKey includes a hash specification.
EdDSAEngine jsig = new EdDSAEngine();
EdDSAEngine jsig = (type == SigType.RedDSA_SHA512_Ed25519) ? new RedDSAEngine() : new EdDSAEngine();
jsig.initVerify(pubKey);
rv = jsig.verifyOneShot(hash.getData(), sigbytes);
} else {
@ -621,7 +623,8 @@ public final class DSAEngine {
byte[] sigbytes;
if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
// take advantage of one-shot mode
EdDSAEngine jsig = new EdDSAEngine(type.getDigestInstance());
MessageDigest md = type.getDigestInstance();
EdDSAEngine jsig = (type == SigType.RedDSA_SHA512_Ed25519) ? new RedDSAEngine(md) : new EdDSAEngine(md);
jsig.initSign(privKey);
sigbytes = jsig.signOneShot(data, offset, len);
} else {
@ -669,7 +672,7 @@ public final class DSAEngine {
if (type.getBaseAlgorithm() == SigAlgo.EdDSA) {
// take advantage of one-shot mode
// Ignore algo, EdDSAKey includes a hash specification.
EdDSAEngine jsig = new EdDSAEngine();
EdDSAEngine jsig = (type == SigType.RedDSA_SHA512_Ed25519) ? new RedDSAEngine() : new EdDSAEngine();
jsig.initSign(privKey);
sigbytes = jsig.signOneShot(hash.getData());
} else {

View File

@ -55,10 +55,10 @@ import net.i2p.crypto.eddsa.math.ScalarOps;
* @author str4d
*
*/
public final class EdDSAEngine extends Signature {
public class EdDSAEngine extends Signature {
public static final String SIGNATURE_ALGORITHM = "NONEwithEdDSA";
private MessageDigest digest;
protected MessageDigest digest;
private ByteArrayOutputStream baos;
private EdDSAKey key;
private boolean oneShotMode;
@ -129,7 +129,7 @@ public final class EdDSAEngine extends Signature {
}
}
private void digestInitSign(EdDSAPrivateKey privKey) {
protected void digestInitSign(EdDSAPrivateKey privKey) {
// Preparing for hash
// r = H(h_b,...,h_2b-1,M)
int b = privKey.getParams().getCurve().getField().getb();

View File

@ -0,0 +1,70 @@
package net.i2p.crypto.eddsa;
import java.security.MessageDigest;
import net.i2p.util.RandomSource;
/**
* Signing and verification for REdDSA using SHA-512 and the Ed25519 curve.
* Ref: Zcash Protocol Specification, Version 2018.0-beta-33 [Overwinter+Sapling]
* Sections 4.1.6.1, 4.1.6.2, 5.4.6
*
*<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>
*
* @since 0.9.39
*/
public final class RedDSAEngine extends EdDSAEngine {
/**
* No specific EdDSA-internal hash requested, allows any EdDSA key.
*/
public RedDSAEngine() {
super();
}
/**
* 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 RedDSAEngine(MessageDigest digest) {
super(digest);
}
@Override
protected void digestInitSign(EdDSAPrivateKey privKey) {
// Preparing for hash
// r = H(T, pubkey, M)
byte[] t = new byte[digest.getDigestLength() + 16];
RandomSource.getInstance().nextBytes(t);
digest.update(t);
digest.update(privKey.getAbyte());
}
}