Crypto: Implement blinding (proposal 123)

Add sig type 11 for blinded keys
This commit is contained in:
zzz
2019-02-19 13:12:26 +00:00
parent af46e48563
commit b3c5974693
5 changed files with 169 additions and 34 deletions

View File

@ -19,6 +19,7 @@ import net.i2p.data.SigningPublicKey;
public final class Blinding {
private static final SigType TYPE = SigType.EdDSA_SHA512_Ed25519;
private static final SigType TYPER = SigType.RedDSA_SHA512_Ed25519;
private Blinding() {}
@ -26,17 +27,19 @@ public final class Blinding {
* Only for SigType EdDSA_SHA512_Ed25519.
*
* @param key must be SigType EdDSA_SHA512_Ed25519
* @param alpha the secret data
* @throws UnsupportedOperationException unless supported
* @param alpha must be SigType RedDSA_SHA512_Ed25519
* @return SigType RedDSA_SHA512_Ed25519
* @throws UnsupportedOperationException unless supported SigTypes
* @throws IllegalArgumentException on bad inputs
*/
public static SigningPublicKey blind(SigningPublicKey key, SigningPrivateKey alpha) {
if (key.getType() != TYPE && alpha.getType() != TYPE)
if (key.getType() != TYPE || alpha.getType() != TYPER)
throw new UnsupportedOperationException();
try {
EdDSAPublicKey jk = SigUtil.toJavaEdDSAKey(key);
EdDSAPrivateKey ajk = SigUtil.toJavaEdDSAKey(alpha);
EdDSAPublicKey bjk = EdDSABlinding.blind(jk, ajk);
return SigUtil.fromJavaKey(bjk, TYPE);
return SigUtil.fromJavaKey(bjk, TYPER);
} catch (GeneralSecurityException gse) {
throw new IllegalArgumentException(gse);
}
@ -46,17 +49,19 @@ public final class Blinding {
* Only for SigType EdDSA_SHA512_Ed25519.
*
* @param key must be SigType EdDSA_SHA512_Ed25519
* @param alpha the secret data
* @throws UnsupportedOperationException unless supported
* @param alpha must be SigType RedDSA_SHA512_Ed25519
* @return SigType RedDSA_SHA512_Ed25519
* @throws UnsupportedOperationException unless supported SigTypes
* @throws IllegalArgumentException on bad inputs
*/
public static SigningPrivateKey blind(SigningPrivateKey key, SigningPrivateKey alpha) {
if (key.getType() != TYPE && alpha.getType() != TYPE)
if (key.getType() != TYPE || alpha.getType() != TYPER)
throw new UnsupportedOperationException();
try {
EdDSAPrivateKey jk = SigUtil.toJavaEdDSAKey(key);
EdDSAPrivateKey ajk = SigUtil.toJavaEdDSAKey(alpha);
EdDSAPrivateKey bjk = EdDSABlinding.blind(jk, ajk);
return SigUtil.fromJavaKey(bjk, TYPE);
return SigUtil.fromJavaKey(bjk, TYPER);
} catch (GeneralSecurityException gse) {
throw new IllegalArgumentException(gse);
}
@ -65,12 +70,14 @@ public final class Blinding {
/**
* Only for SigType EdDSA_SHA512_Ed25519.
*
* @param key must be SigType EdDSA_SHA512_Ed25519
* @param alpha the secret data
* @throws UnsupportedOperationException unless supported
* @param key must be SigType RedDSA_SHA512_Ed25519
* @param alpha must be SigType RedDSA_SHA512_Ed25519
* @return SigType EdDSA_SHA512_Ed25519
* @throws UnsupportedOperationException unless supported SigTypes
* @throws IllegalArgumentException on bad inputs
*/
public static SigningPrivateKey unblind(SigningPrivateKey key, SigningPrivateKey alpha) {
if (key.getType() != TYPE && alpha.getType() != TYPE)
if (key.getType() != TYPER || alpha.getType() != TYPER)
throw new UnsupportedOperationException();
try {
EdDSAPrivateKey bjk = SigUtil.toJavaEdDSAKey(key);
@ -81,22 +88,39 @@ public final class Blinding {
throw new IllegalArgumentException(gse);
}
}
/******
public static void main(String args[]) throws Exception {
SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
SigningPublicKey pub = (SigningPublicKey) keys[0];
SigningPrivateKey priv = (SigningPrivateKey) keys[1];
byte[] b = new byte[32];
byte[] b = new byte[64];
net.i2p.I2PAppContext.getGlobalContext().random().nextBytes(b);
Hash h = new Hash(b);
SigningPublicKey bpub = blind(pub, h);
SigningPrivateKey bpriv = blind(priv, h);
SigningPublicKey bpub2 = bpriv.toPublic();
boolean ok = bpub2.equals(bpub);
System.out.println("Blinding test passed? " + ok);
SigningPrivateKey priv2 = unblind(bpriv, h);
ok = priv2.equals(priv);
System.out.println("Unblinding test passed? " + ok);
b = EdDSABlinding.reduce(b);
SigningPrivateKey alpha = new SigningPrivateKey(TYPER, b);
SigningPublicKey bpub = null;
try {
bpub = blind(pub, alpha);
} catch (Exception e) {
System.out.println("Blinding pubkey test failed");
e.printStackTrace();
}
SigningPrivateKey bpriv = null;
try {
bpriv = blind(priv, alpha);
} catch (Exception e) {
System.out.println("Blinding privkey test failed");
e.printStackTrace();
}
if (bpub != null && bpriv != null) {
SigningPublicKey bpub2 = bpriv.toPublic();
boolean ok = bpub2.equals(bpub);
System.out.println("Blinding test passed? " + ok);
// unimplemented
//SigningPrivateKey priv2 = unblind(bpriv, alpha);
//ok = priv2.equals(priv);
//System.out.println("Unblinding test passed? " + ok);
}
}
******/
}

View File

@ -65,6 +65,17 @@ public enum SigType {
EdDSA_SHA512_Ed25519ph(8, 32, 32, 64, 64, SigAlgo.EdDSA, "SHA-512", "NonewithEdDSA",
EdDSANamedCurveTable.getByName("ed25519-sha-512"), "1.3.101.101", "0.9.25"),
// 9 and 10 reserved for GOST - see proposal 134
/**
* Blinded version of EdDSA, use for encrypted LS2
* Pubkey 32 bytes; privkey 32 bytes; hash 64 bytes; sig 64 bytes
*
* @since 0.9.39
*/
RedDSA_SHA512_Ed25519(11, 32, 32, 64, 64, SigAlgo.EdDSA, "SHA-512", "SHA512withEdDSA",
EdDSANamedCurveTable.getByName("ed25519-sha-512"), "1.3.101.101", "0.9.39"),
;
// TESTING....................
@ -295,6 +306,8 @@ public enum SigType {
return EdDSA_SHA512_Ed25519;
if (uc.equals("EDDSA_SHA512_ED25519PH"))
return EdDSA_SHA512_Ed25519ph;
if (uc.equals("REDDSA_SHA512_ED25519"))
return RedDSA_SHA512_Ed25519;
return valueOf(uc);
} catch (IllegalArgumentException iae) {
try {

View File

@ -369,8 +369,16 @@ public final class SigUtil {
private static EdDSAPrivateKey cvtToJavaEdDSAKey(SigningPrivateKey pk)
throws GeneralSecurityException {
try {
return new EdDSAPrivateKey(new EdDSAPrivateKeySpec(
pk.getData(), (EdDSAParameterSpec) pk.getType().getParams()));
EdDSAParameterSpec paramspec = (EdDSAParameterSpec) pk.getType().getParams();
EdDSAPrivateKeySpec pkspec;
SigType type = pk.getType();
if (type == SigType.EdDSA_SHA512_Ed25519)
pkspec = new EdDSAPrivateKeySpec(pk.getData(), paramspec);
else if (type == SigType.RedDSA_SHA512_Ed25519)
pkspec = new EdDSAPrivateKeySpec(pk.getData(), null, paramspec);
else
throw new InvalidKeyException();
return new EdDSAPrivateKey(pkspec);
} catch (IllegalArgumentException iae) {
throw new InvalidKeyException(iae);
}
@ -389,7 +397,14 @@ public final class SigUtil {
*/
public static SigningPrivateKey fromJavaKey(EdDSAPrivateKey pk, SigType type)
throws GeneralSecurityException {
return new SigningPrivateKey(type, pk.getSeed());
byte[] data;
if (type == SigType.EdDSA_SHA512_Ed25519)
data = pk.getSeed();
else if (type == SigType.RedDSA_SHA512_Ed25519)
data = pk.geta();
else
throw new IllegalArgumentException();
return new SigningPrivateKey(type, data);
}
public static DSAPublicKey toJavaDSAKey(SigningPublicKey pk)

View File

@ -1,5 +1,16 @@
package net.i2p.crypto.eddsa;
import java.math.BigInteger;
import net.i2p.crypto.eddsa.math.Field;
import net.i2p.crypto.eddsa.math.GroupElement;
import net.i2p.crypto.eddsa.math.ScalarOps;
import net.i2p.crypto.eddsa.math.bigint.BigIntegerLittleEndianEncoding;
import net.i2p.crypto.eddsa.math.bigint.BigIntegerScalarOps;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
/**
* Utilities for Blinding EdDSA keys.
* PRELIMINARY - Subject to change - see proposal 123
@ -8,6 +19,10 @@ package net.i2p.crypto.eddsa;
*/
public final class EdDSABlinding {
private static final byte[] ONE = Utils.hexToBytes("0100000000000000000000000000000000000000000000000000000000000000");
private static final Field FIELD = EdDSANamedCurveTable.getByName("Ed25519").getCurve().getField();
private static final BigInteger ORDER = new BigInteger("2").pow(252).add(new BigInteger("27742317777372353535851937790883648493"));
private EdDSABlinding() {}
/**
@ -18,8 +33,19 @@ public final class EdDSABlinding {
* @throws UnsupportedOperationException unless supported
*/
public static EdDSAPublicKey blind(EdDSAPublicKey key, EdDSAPrivateKey alpha) {
// TODO, test only
return key;
GroupElement a = key.getA();
GroupElement aa = alpha.getA();
// Add the two public keys together.
GroupElement d = a.add(aa.toCached());
EdDSAPublicKeySpec pubKey = new EdDSAPublicKeySpec(d, key.getParams());
EdDSAPublicKey rv = new EdDSAPublicKey(pubKey);
//System.out.println("Adding pubkey\n" +
// net.i2p.util.HexDump.dump(key.getAbyte()) +
// "\nplus privkey\n" +
// net.i2p.util.HexDump.dump(alpha.geta()) +
// "\nequals\n" +
// net.i2p.util.HexDump.dump(rv.getAbyte()));
return rv;
}
/**
@ -30,19 +56,46 @@ public final class EdDSABlinding {
* @throws UnsupportedOperationException unless supported
*/
public static EdDSAPrivateKey blind(EdDSAPrivateKey key, EdDSAPrivateKey alpha) {
// TODO, test only
return key;
byte[] a = key.geta();
byte[] aa = alpha.geta();
Field f = key.getParams().getCurve().getField();
BigIntegerLittleEndianEncoding enc = new BigIntegerLittleEndianEncoding();
enc.setField(f);
ScalarOps sc = new BigIntegerScalarOps(f, ORDER);
// Add the two private keys together.
// just for now, since we don't have a pure add.
byte[] d = sc.multiplyAndAdd(ONE, a, aa);
//System.out.println("Adding privkeys\n" +
// net.i2p.util.HexDump.dump(a) +
// "\nplus\n" +
// net.i2p.util.HexDump.dump(aa) +
// "\nequals\n" +
// net.i2p.util.HexDump.dump(d));
EdDSAPrivateKeySpec privKey = new EdDSAPrivateKeySpec(d, null, key.getParams());
return new EdDSAPrivateKey(privKey);
}
/**
* Only for SigType EdDSA_SHA512_Ed25519.
* Unimplemented, probably not needed except for testing.
*
* @param key must be SigType EdDSA_SHA512_Ed25519
* @param alpha generated from hash of secret data
* @throws UnsupportedOperationException unless supported
*/
public static EdDSAPrivateKey unblind(EdDSAPrivateKey key, EdDSAPrivateKey alpha) {
// TODO, test only
return key;
throw new UnsupportedOperationException();
}
/**
* Use to generate alpha
*
* @param b 64 bytes little endian of random
* @return 32 bytes little endian mod l
*/
public static byte[] reduce(byte[] b) {
if (b.length != 64)
throw new IllegalArgumentException();
ScalarOps sc = new BigIntegerScalarOps(FIELD, ORDER);
return sc.reduce(b);
}
}

View File

@ -83,11 +83,41 @@ public class EdDSAPrivateKeySpec implements KeySpec {
A = spec.getB().scalarMultiply(a);
}
/**
* No validation of any parameters other than a.
* getSeed() and getH() will return null if this constructor is used.
*
* @param a must be "clamped" (for Ed) or reduced mod l (for Red)
* @param A if null, will be derived from a.
* @throws IllegalArgumentException if a not clamped or reduced
* @since 0.9.39
*/
public EdDSAPrivateKeySpec(byte[] a, GroupElement A, EdDSAParameterSpec spec) {
this(null, null, a, A, spec);
}
/**
* No validation of any parameters other than a.
*
* @param seed may be null
* @param h may be null
* @param a must be "clamped" (for Ed) or reduced mod l (for Red)
* @param A if null, will be derived from a.
* @throws IllegalArgumentException if a not clamped or reduced
*/
public EdDSAPrivateKeySpec(byte[] seed, byte[] h, byte[] a, GroupElement A, EdDSAParameterSpec spec) {
/**
// TODO if we move RedDSA to a different spec
int bd8m1 = (spec.getCurve().getField().getb() / 8) - 1;
if ((a[0] & 0x07) != 0 ||
(a[bd8m1] & 0xc0) != 0x40)
throw new IllegalArgumentException("a not clamped: a[0]=0x" + Integer.toString(a[0] & 0xff, 16) +
" a[31]=0x" + Integer.toString(a[31] & 0xff, 16));
**/
this.seed = seed;
this.h = h;
this.a = a;
this.A = A;
this.A = (A != null) ? A : spec.getB().scalarMultiply(a);
this.spec = spec;
}