diff --git a/core/java/src/net/i2p/crypto/elgamal/ElGamalSigEngine.java b/core/java/src/net/i2p/crypto/elgamal/ElGamalSigEngine.java new file mode 100644 index 000000000..b44dc5fbe --- /dev/null +++ b/core/java/src/net/i2p/crypto/elgamal/ElGamalSigEngine.java @@ -0,0 +1,164 @@ +package net.i2p.crypto.elgamal; + +import java.io.ByteArrayOutputStream; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.util.Arrays; + +import net.i2p.crypto.SHA256Generator; +import net.i2p.crypto.SigUtil; +import net.i2p.util.NativeBigInteger; +import net.i2p.util.RandomSource; + +/** + * ElG signatures with SHA-256 + * + * ref: https://en.wikipedia.org/wiki/ElGamal_signature_scheme + * + * @since 0.9.25 + */ +public final class ElGamalSigEngine extends Signature { + private final MessageDigest digest; + private ElGamalKey key; + + /** + * No specific hash requested, allows any ElGamal key. + */ + public ElGamalSigEngine() { + this(SHA256Generator.getDigestInstance()); + } + + /** + * Specific hash requested, only matching keys will be allowed. + * @param digest the hash algorithm that keys must have to sign or verify. + */ + public ElGamalSigEngine(MessageDigest digest) { + super("ElGamal"); + this.digest = digest; + } + + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + digest.reset(); + if (privateKey instanceof ElGamalPrivateKey) { + ElGamalPrivateKey privKey = (ElGamalPrivateKey) privateKey; + key = privKey; + } else { + throw new InvalidKeyException("cannot identify ElGamal private key: " + privateKey.getClass()); + } + } + + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + digest.reset(); + if (publicKey instanceof ElGamalPublicKey) { + key = (ElGamalPublicKey) publicKey; + } else { + throw new InvalidKeyException("cannot identify ElGamal public key: " + publicKey.getClass()); + } + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + digest.update(b); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + digest.update(b, off, len); + } + + /** + * @return ASN.1 R,S + */ + @Override + protected byte[] engineSign() throws SignatureException { + BigInteger elgp = key.getParams().getP(); + BigInteger pm1 = elgp.subtract(BigInteger.ONE); + BigInteger elgg = key.getParams().getG(); + BigInteger x = ((ElGamalPrivateKey) key).getX(); + if (!(x instanceof NativeBigInteger)) + x = new NativeBigInteger(x); + byte[] data = digest.digest(); + + BigInteger k; + boolean ok; + do { + k = new BigInteger(2048, RandomSource.getInstance()); + ok = k.compareTo(pm1) == -1; + ok = ok && k.compareTo(BigInteger.ONE) == 1; + ok = ok && k.gcd(pm1).equals(BigInteger.ONE); + } while (!ok); + + BigInteger r = elgg.modPow(k, elgp); + BigInteger kinv = k.modInverse(pm1); + BigInteger h = new NativeBigInteger(1, data); + BigInteger s = (kinv.multiply(h.subtract(x.multiply(r)))).mod(pm1); + // todo if s == 0 go around again + + byte[] rv; + try { + rv = SigUtil.sigBytesToASN1(r, s); + } catch (IllegalArgumentException iae) { + throw new SignatureException("ASN1", iae); + } + return rv; + } + + /** + * @param sigBytes ASN.1 R,S + */ + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + BigInteger elgp = key.getParams().getP(); + BigInteger pm1 = elgp.subtract(BigInteger.ONE); + BigInteger elgg = key.getParams().getG(); + BigInteger y = ((ElGamalPublicKey) key).getY(); + if (!(y instanceof NativeBigInteger)) + y = new NativeBigInteger(y); + byte[] data = digest.digest(); + + try { + BigInteger[] rs = SigUtil.aSN1ToBigInteger(sigBytes, 256); + BigInteger r = rs[0]; + BigInteger s = rs[1]; + if (r.signum() != 1 || s.signum() != 1 || + r.compareTo(elgp) != -1 || s.compareTo(pm1) != -1) + return false; + NativeBigInteger h = new NativeBigInteger(1, data); + BigInteger modvalr = r.modPow(s, elgp); + BigInteger modvaly = y.modPow(r, elgp); + BigInteger modmulval = modvalr.multiply(modvaly).mod(elgp); + BigInteger v = elgg.modPow(h, elgp); + + boolean ok = v.compareTo(modmulval) == 0; + return ok; + } catch (RuntimeException e) { + throw new SignatureException("verify", e); + } + } + + /** + * @deprecated replaced with + */ + @Override + protected void engineSetParameter(String param, Object value) { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + @Override + protected Object engineGetParameter(String param) { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } +} diff --git a/core/java/src/net/i2p/crypto/provider/I2PProvider.java b/core/java/src/net/i2p/crypto/provider/I2PProvider.java index 4c096bc22..e64bb1d43 100644 --- a/core/java/src/net/i2p/crypto/provider/I2PProvider.java +++ b/core/java/src/net/i2p/crypto/provider/I2PProvider.java @@ -31,13 +31,24 @@ public final class I2PProvider extends Provider { private void setup() { // TODO: Implement SPIs for existing code + // However - + // quote + // http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/HowToImplAProvider.html + // + // If your provider is supplying encryption algorithms through the + // Cipher, KeyAgreement, KeyGenerator, Mac, or SecretKeyFactory classes, + // you will need to sign your JAR file so that the JCA can authenticate the code at runtime. + // If you are NOT providing an implementation of this type you can skip this step. + // //put("Cipher.AES", "net.i2p.crypto.provider.CipherSpi$aesCBC"); //put("Cipher.ElGamal", "net.i2p.crypto.provider.CipherSpi$elGamal"); //put("Mac.HmacMD5-I2P", "net.i2p.crypto.provider.MacSpi"); + put("MessageDigest.SHA-1", "net.i2p.crypto.SHA1"); //put("Signature.SHA1withDSA", "net.i2p.crypto.provider.SignatureSpi"); // EdDSA + // OID: 1.3.101.100 put("KeyFactory.EdDSA", "net.i2p.crypto.eddsa.KeyFactory"); put("KeyPairGenerator.EdDSA", "net.i2p.crypto.eddsa.KeyPairGenerator"); put("Signature.SHA512withEdDSA", "net.i2p.crypto.eddsa.EdDSAEngine"); @@ -50,5 +61,21 @@ public final class I2PProvider extends Provider { put("Alg.Alias.KeyPairGenerator.OID.1.3.101.100", "EdDSA"); put("Alg.Alias.Signature.1.3.101.100", "SHA512withEdDSA"); put("Alg.Alias.Signature.OID.1.3.101.100", "SHA512withEdDSA"); + // TODO Ed25519ph + // OID: 1.3.101.101 + + // ElGamal + // OID: 1.3.14.7.2.1.1 + put("KeyFactory.DH", "net.i2p.crypto.elgamal.KeyFactory"); + put("KeyFactory.DiffieHellman", "net.i2p.crypto.elgamal.KeyFactory"); + put("KeyFactory.ElGamal", "net.i2p.crypto.elgamal.KeyFactory"); + put("KeyPairGenerator.DH", "net.i2p.crypto.elgamal.KeyPairGenerator"); + put("KeyPairGenerator.DiffieHellman", "net.i2p.crypto.elgamal.KeyPairGenerator"); + put("KeyPairGenerator.ElGamal", "net.i2p.crypto.elgamal.KeyPairGenerator"); + put("Signature.SHA256withElGamal", "net.i2p.crypto.elgamal.ElGamalSigEngine"); + put("Alg.Alias.KeyPairGenerator.1.3.14.7.2.1.1", "ElGamal"); + put("Alg.Alias.KeyPairGenerator.OID.1.3.14.7.2.1.1", "ElGamal"); + put("Alg.Alias.Signature.1.3.14.7.2.1.1", "SHA256withElGamal"); + put("Alg.Alias.Signature.OID.1.3.14.7.2.1.1", "SHA256withElGamal"); } }