diff --git a/core/java/src/net/i2p/crypto/Blinding.java b/core/java/src/net/i2p/crypto/Blinding.java index 661ba88fe3473ddd3a9541e5b6da67d2dfa26009..22be07a009b8b4eb389279b4c050112eaa7a6da2 100644 --- a/core/java/src/net/i2p/crypto/Blinding.java +++ b/core/java/src/net/i2p/crypto/Blinding.java @@ -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); + } } ******/ } diff --git a/core/java/src/net/i2p/crypto/SigType.java b/core/java/src/net/i2p/crypto/SigType.java index 5da9e22b0475adf6b20817d229328ed0f11b270c..7832c38604d084aef4cc597ba22bc8ea1fd7c88f 100644 --- a/core/java/src/net/i2p/crypto/SigType.java +++ b/core/java/src/net/i2p/crypto/SigType.java @@ -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 { diff --git a/core/java/src/net/i2p/crypto/SigUtil.java b/core/java/src/net/i2p/crypto/SigUtil.java index b19f63bef9db93587d7cdb971a9b4e161ee9c5e6..98376e8cba35ef4eb3ea4ff61b0876cf3a915fe2 100644 --- a/core/java/src/net/i2p/crypto/SigUtil.java +++ b/core/java/src/net/i2p/crypto/SigUtil.java @@ -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) diff --git a/core/java/src/net/i2p/crypto/eddsa/EdDSABlinding.java b/core/java/src/net/i2p/crypto/eddsa/EdDSABlinding.java index 5e4ccb457e3563c841db3299c7a0f1725abe6343..1438b7037855b87d5eb8feeb6bbec340c650a50c 100644 --- a/core/java/src/net/i2p/crypto/eddsa/EdDSABlinding.java +++ b/core/java/src/net/i2p/crypto/eddsa/EdDSABlinding.java @@ -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); } } diff --git a/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAPrivateKeySpec.java b/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAPrivateKeySpec.java index 2143eeab2e8ce634d87b7420cd399fb922dcbde9..1f583037aad12951a64699713cbf349c4fd9505b 100644 --- a/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAPrivateKeySpec.java +++ b/core/java/src/net/i2p/crypto/eddsa/spec/EdDSAPrivateKeySpec.java @@ -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; }