package net.i2p.crypto; /* * Copyright (c) 2003, TheCrypto * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the TheCrypto may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.Key; import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import java.security.interfaces.DSAKey; import java.security.interfaces.ECKey; import java.security.interfaces.RSAKey; import net.i2p.I2PAppContext; import net.i2p.crypto.eddsa.EdDSAEngine; import net.i2p.crypto.eddsa.EdDSAKey; import net.i2p.data.Hash; import net.i2p.data.Signature; import net.i2p.data.SigningPrivateKey; import net.i2p.data.SigningPublicKey; import net.i2p.data.SimpleDataStructure; import net.i2p.util.Log; import net.i2p.util.NativeBigInteger; /** * Sign and verify using DSA-SHA1 and other signature algorithms. * Also contains methods to sign and verify using a SHA-256 Hash. * * The primary implementation is code from TheCryto. * As of 0.8.7, also included is an alternate implementation using java.security libraries, which * is slightly slower. This implementation could in the future be easily modified * to use a new signing algorithm from java.security when we change the signing algorithm. * * Params and rv's changed from Hash to SHA1Hash for version 0.8.1 * Hash variants of sign() and verifySignature() restored in 0.8.3, required by Syndie. * * As of 0.9.9, certain methods support RSA and ECDSA keys and signatures, i.e. all types * specified in SigType. The type is specified by the getType() method in * Signature, SigningPublicKey, and SigningPrivateKey. See Javadocs for individual * methods for the supported types. Methods encountering an unsupported type * will throw an IllegalArgumentException. * * EdDSA support added in 0.9.15 */ public final class DSAEngine { private final Log _log; private final I2PAppContext _context; //private static final boolean _isAndroid = System.getProperty("java.vendor").contains("Android"); private static final boolean _useJavaLibs = false; // = _isAndroid; public DSAEngine(I2PAppContext context) { _log = context.logManager().getLog(DSAEngine.class); _context = context; } public static DSAEngine getInstance() { return I2PAppContext.getGlobalContext().dsa(); } /** * Verify using any sig type. * Uses TheCrypto code for DSA-SHA1 unless configured to use the java.security libraries. */ public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) { return verifySignature(signature, signedData, 0, signedData.length, verifyingKey); } /** * Verify using any sig type as of 0.9.12 (DSA only prior to that) */ public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) { boolean rv; SigType type = signature.getType(); if (type != verifyingKey.getType()) throw new IllegalArgumentException("type mismatch sig=" + signature.getType() + " key=" + verifyingKey.getType()); if (type != SigType.DSA_SHA1) { try { rv = altVerifySig(signature, signedData, offset, size, verifyingKey); if ((!rv) && _log.shouldLog(Log.WARN)) _log.warn(type + " Sig Verify Fail"); return rv; } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn(type + " Sig Verify Fail", gse); return false; } } if (_useJavaLibs) { try { rv = altVerifySigSHA1(signature, signedData, offset, size, verifyingKey); if ((!rv) && _log.shouldLog(Log.WARN)) _log.warn("Lib DSA Sig Verify Fail"); return rv; } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn("Lib DSA Sig Verify Fail"); // now try TheCrypto } } rv = verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey); if ((!rv) && _log.shouldLog(Log.WARN)) _log.warn("TheCrypto DSA Sig Verify Fail"); return rv; } /** * Verify using DSA-SHA1 ONLY */ public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) { return verifySignature(signature, calculateHash(in), verifyingKey); } /** * Verify using DSA-SHA1 ONLY * @param hash SHA-1 hash, NOT a SHA-256 hash */ public boolean verifySignature(Signature signature, SHA1Hash hash, SigningPublicKey verifyingKey) { return verifySig(signature, hash, verifyingKey); } /** * Nonstandard. * Used by Syndie. * @since 0.8.3 (restored, was removed in 0.8.1 and 0.8.2) */ public boolean verifySignature(Signature signature, Hash hash, SigningPublicKey verifyingKey) { return verifySig(signature, hash, verifyingKey); } /** * Generic signature type. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @param hash SHA1Hash, Hash, Hash384, or Hash512 * @since 0.9.9 */ public boolean verifySignature(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) { SigType type = signature.getType(); if (type != verifyingKey.getType()) throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType()); int hashlen = type.getHashLen(); if (hash.length() != hashlen) throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " sig=" + type); if (type == SigType.DSA_SHA1) return verifySig(signature, hash, verifyingKey); try { return altVerifySigRaw(signature, hash, verifyingKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn(type + " Sig Verify Fail", gse); return false; } } /** * Generic signature type. * If you have a Java pubkey, use this, so you don't lose the key parameters, * which may be different than the ones defined in SigType. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @param hash SHA1Hash, Hash, Hash384, or Hash512 * @param pubKey Java key * @since 0.9.9 */ public boolean verifySignature(Signature signature, SimpleDataStructure hash, PublicKey pubKey) { try { return altVerifySigRaw(signature, hash, pubKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn(signature.getType() + " Sig Verify Fail", gse); return false; } } /** * Verify using DSA-SHA1 or Syndie DSA-SHA256 ONLY. * @param hash either a Hash or a SHA1Hash * @since 0.8.3 */ private boolean verifySig(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) { if (signature.getType() != SigType.DSA_SHA1) throw new IllegalArgumentException("Bad sig type " + signature.getType()); if (verifyingKey.getType() != SigType.DSA_SHA1) throw new IllegalArgumentException("Bad key type " + verifyingKey.getType()); long start = _context.clock().now(); try { byte[] sigbytes = signature.getData(); byte rbytes[] = new byte[20]; byte sbytes[] = new byte[20]; //System.arraycopy(sigbytes, 0, rbytes, 0, 20); //System.arraycopy(sigbytes, 20, sbytes, 0, 20); for (int x = 0; x < 40; x++) { if (x < 20) { rbytes[x] = sigbytes[x]; } else { sbytes[x - 20] = sigbytes[x]; } } BigInteger s = new NativeBigInteger(1, sbytes); BigInteger r = new NativeBigInteger(1, rbytes); BigInteger y = new NativeBigInteger(1, verifyingKey.getData()); BigInteger w; try { w = s.modInverse(CryptoConstants.dsaq); } catch (ArithmeticException ae) { _log.warn("modInverse() error", ae); return false; } byte data[] = hash.getData(); NativeBigInteger bi = new NativeBigInteger(1, data); BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq); BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq); BigInteger modval = CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap); BigInteger modmulval = modval.multiply(y.modPow(u2,CryptoConstants.dsap)); BigInteger v = (modmulval).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq); boolean ok = v.compareTo(r) == 0; long diff = _context.clock().now() - start; if (diff > 1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to verify the signature (" + diff + "ms)"); } return ok; } catch (RuntimeException e) { _log.log(Log.CRIT, "Error verifying the signature", e); return false; } } /** * Sign using any key type. * Uses TheCrypto code unless configured to use the java.security libraries. * * @return null on error */ public Signature sign(byte data[], SigningPrivateKey signingKey) { return sign(data, 0, data.length, signingKey); } /** * Sign using any key type as of 0.9.12 (DSA-SHA1 only prior to that) * * @return null on error */ public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) { if ((signingKey == null) || (data == null) || (data.length <= 0)) return null; SigType type = signingKey.getType(); if (type != SigType.DSA_SHA1) { try { return altSign(data, offset, length, signingKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.ERROR)) _log.error(type + " Sign Fail", gse); return null; } } if (_useJavaLibs) { try { return altSignSHA1(data, offset, length, signingKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn("Lib Sign Fail, privkey = " + signingKey, gse); // now try TheCrypto } } SHA1Hash h = calculateHash(data, offset, length); return sign(h, signingKey); } /** * Sign using DSA-SHA1 ONLY. * Reads the stream until EOF. Does not close the stream. * * @return null on error */ public Signature sign(InputStream in, SigningPrivateKey signingKey) { if ((signingKey == null) || (in == null) ) return null; SHA1Hash h = calculateHash(in); return sign(h, signingKey); } /** * Sign using DSA-SHA1 ONLY. * * @param hash SHA-1 hash, NOT a SHA-256 hash * @return null on error */ public Signature sign(SHA1Hash hash, SigningPrivateKey signingKey) { return signIt(hash, signingKey); } /** * Nonstandard. * Used by Syndie. * * @return null on error * @since 0.8.3 (restored, was removed in 0.8.1 and 0.8.2) */ public Signature sign(Hash hash, SigningPrivateKey signingKey) { return signIt(hash, signingKey); } /** * Generic signature type. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @param hash SHA1Hash, Hash, Hash384, or Hash512 * @return null on error * @since 0.9.9 */ public Signature sign(SimpleDataStructure hash, SigningPrivateKey signingKey) { SigType type = signingKey.getType(); int hashlen = type.getHashLen(); if (hash.length() != hashlen) throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type); if (type == SigType.DSA_SHA1) return signIt(hash, signingKey); try { return altSignRaw(hash, signingKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn(type + " Sign Fail", gse); return null; } } /** * Generic signature type. * If you have a Java privkey, use this, so you don't lose the key parameters, * which may be different than the ones defined in SigType. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @param hash SHA1Hash, Hash, Hash384, or Hash512 * @param privKey Java key * @param type returns a Signature of this type * @return null on error * @since 0.9.9 */ public Signature sign(SimpleDataStructure hash, PrivateKey privKey, SigType type) { String algo = getRawAlgo(privKey); String talgo = getRawAlgo(type); if (!algo.equals(talgo)) throw new IllegalArgumentException("type mismatch type=" + type + " key=" + privKey.getClass().getSimpleName()); try { return altSignRaw(algo, hash, privKey, type); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn(type + " Sign Fail", gse); return null; } } /** * Sign using DSA-SHA1 or Syndie DSA-SHA256 ONLY. * * @param hash either a Hash or a SHA1Hash * @return null on error * @since 0.8.3 */ private Signature signIt(SimpleDataStructure hash, SigningPrivateKey signingKey) { if ((signingKey == null) || (hash == null)) return null; if (signingKey.getType() != SigType.DSA_SHA1) throw new IllegalArgumentException("Bad key type " + signingKey.getType()); long start = _context.clock().now(); BigInteger k; boolean ok; do { k = new NativeBigInteger(160, _context.random()); ok = k.compareTo(CryptoConstants.dsaq) != 1; ok = ok && !k.equals(BigInteger.ZERO); //System.out.println("K picked (ok? " + ok + "): " + k.bitLength() + ": " + k.toString()); } while (!ok); BigInteger r = CryptoConstants.dsag.modPowCT(k, CryptoConstants.dsap).mod(CryptoConstants.dsaq); BigInteger kinv = k.modInverse(CryptoConstants.dsaq); BigInteger M = new NativeBigInteger(1, hash.getData()); BigInteger x = new NativeBigInteger(1, signingKey.getData()); BigInteger s = (kinv.multiply(M.add(x.multiply(r)))).mod(CryptoConstants.dsaq); byte[] rbytes = r.toByteArray(); byte[] sbytes = s.toByteArray(); byte[] out = new byte[40]; // (q^random)%p is computationally random _context.random().harvester().feedEntropy("DSA.sign", rbytes, 0, rbytes.length); if (rbytes.length == 20) { //System.arraycopy(rbytes, 0, out, 0, 20); for (int i = 0; i < 20; i++) { out[i] = rbytes[i]; } } else if (rbytes.length == 21) { //System.arraycopy(rbytes, 1, out, 0, 20); for (int i = 0; i < 20; i++) { out[i] = rbytes[i + 1]; } } else if (rbytes.length > 21) { _log.error("Bad R length " + rbytes.length); return null; } else { //if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short rbytes.length [" + rbytes.length + "]"); //System.arraycopy(rbytes, 0, out, 20 - rbytes.length, rbytes.length); for (int i = 0; i < rbytes.length; i++) out[i + 20 - rbytes.length] = rbytes[i]; } if (sbytes.length == 20) { //System.arraycopy(sbytes, 0, out, 20, 20); for (int i = 0; i < 20; i++) { out[i + 20] = sbytes[i]; } } else if (sbytes.length == 21) { //System.arraycopy(sbytes, 1, out, 20, 20); for (int i = 0; i < 20; i++) { out[i + 20] = sbytes[i + 1]; } } else if (sbytes.length > 21) { _log.error("Bad S length " + sbytes.length); return null; } else { //if (_log.shouldLog(Log.DEBUG)) _log.debug("Using short sbytes.length [" + sbytes.length + "]"); //System.arraycopy(sbytes, 0, out, 40 - sbytes.length, sbytes.length); for (int i = 0; i < sbytes.length; i++) out[i + 20 + 20 - sbytes.length] = sbytes[i]; } long diff = _context.clock().now() - start; if (diff > 1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to sign (" + diff + "ms)"); } return new Signature(out); } /** * Reads the stream until EOF. Does not close the stream. * * @return hash SHA-1 hash, NOT a SHA-256 hash * @deprecated unused */ public SHA1Hash calculateHash(InputStream in) { MessageDigest digest = SHA1.getInstance(); byte buf[] = new byte[64]; int read = 0; try { while ( (read = in.read(buf)) != -1) { digest.update(buf, 0, read); } } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to hash the stream", ioe); return null; } return new SHA1Hash(digest.digest()); } /** @return hash SHA-1 hash, NOT a SHA-256 hash */ public static SHA1Hash calculateHash(byte[] source, int offset, int len) { MessageDigest h = SHA1.getInstance(); h.update(source, offset, len); byte digested[] = h.digest(); return new SHA1Hash(digested); } /** * Generic verify any type. * * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.9.9 added off/len 0.9.12 */ private boolean altVerifySig(Signature signature, byte[] data, int offset, int len, SigningPublicKey verifyingKey) throws GeneralSecurityException { SigType type = signature.getType(); if (type != verifyingKey.getType()) throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType()); if (type == SigType.DSA_SHA1) return altVerifySigSHA1(signature, data, offset, len, verifyingKey); PublicKey pubKey = SigUtil.toJavaKey(verifyingKey); byte[] sigbytes = SigUtil.toJavaSig(signature); boolean rv; if (type.getBaseAlgorithm() == SigAlgo.EdDSA) { // take advantage of one-shot mode EdDSAEngine jsig = new EdDSAEngine(type.getDigestInstance()); jsig.initVerify(pubKey); rv = jsig.verifyOneShot(data, offset, len, sigbytes); } else { java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName()); jsig.initVerify(pubKey); jsig.update(data, offset, len); rv = jsig.verify(sigbytes); } return rv; } /** * Generic raw verify any type * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.9.9 */ private boolean altVerifySigRaw(Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) throws GeneralSecurityException { SigType type = signature.getType(); if (type != verifyingKey.getType()) throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType()); PublicKey pubKey = SigUtil.toJavaKey(verifyingKey); return verifySignature(signature, hash, pubKey); } /** * Generic raw verify any type. * If you have a Java pubkey, use this, so you don't lose the key parameters, * which may be different than the ones defined in SigType. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @throws GeneralSecurityException if algorithm unvailable or on other errors * @param verifyingKey Java key * @since 0.9.9 */ private boolean altVerifySigRaw(Signature signature, SimpleDataStructure hash, PublicKey pubKey) throws GeneralSecurityException { SigType type = signature.getType(); int hashlen = hash.length(); if (type.getHashLen() != hashlen) throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type); byte[] sigbytes = SigUtil.toJavaSig(signature); boolean rv; if (type.getBaseAlgorithm() == SigAlgo.EdDSA) { // take advantage of one-shot mode // Ignore algo, EdDSAKey includes a hash specification. EdDSAEngine jsig = new EdDSAEngine(); jsig.initVerify(pubKey); rv = jsig.verifyOneShot(hash.getData(), sigbytes); } else { String algo = getRawAlgo(type); java.security.Signature jsig = java.security.Signature.getInstance(algo); jsig.initVerify(pubKey); jsig.update(hash.getData()); rv = jsig.verify(sigbytes); } return rv; } /** * Alternate to verifySignature() using java.security libraries. * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.8.7 added off/len 0.9.12 */ private boolean altVerifySigSHA1(Signature signature, byte[] data, int offset, int len, SigningPublicKey verifyingKey) throws GeneralSecurityException { java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA"); PublicKey pubKey = SigUtil.toJavaDSAKey(verifyingKey); jsig.initVerify(pubKey); jsig.update(data, offset, len); boolean rv = jsig.verify(SigUtil.toJavaSig(signature)); //if (!rv) { // System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData())); // System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(sigBytesToASN1(signature.getData()))); //} return rv; } /** * Generic sign any type. * * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.9.9 added off/len 0.9.12 */ private Signature altSign(byte[] data, int offset, int len, SigningPrivateKey privateKey) throws GeneralSecurityException { SigType type = privateKey.getType(); if (type == SigType.DSA_SHA1) return altSignSHA1(data, offset, len, privateKey); PrivateKey privKey = SigUtil.toJavaKey(privateKey); byte[] sigbytes; if (type.getBaseAlgorithm() == SigAlgo.EdDSA) { // take advantage of one-shot mode EdDSAEngine jsig = new EdDSAEngine(type.getDigestInstance()); jsig.initSign(privKey); sigbytes = jsig.signOneShot(data, offset, len); } else { java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName()); jsig.initSign(privKey, _context.random()); jsig.update(data, offset, len); sigbytes = jsig.sign(); } return SigUtil.fromJavaSig(sigbytes, type); } /** * Generic raw sign any type. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @param hash SHA1Hash, Hash, Hash384, or Hash512 * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.9.9 */ private Signature altSignRaw(SimpleDataStructure hash, SigningPrivateKey privateKey) throws GeneralSecurityException { SigType type = privateKey.getType(); String algo = getRawAlgo(type); PrivateKey privKey = SigUtil.toJavaKey(privateKey); return altSignRaw(algo, hash, privKey, type); } /** * Generic raw sign any type. * * Warning, nonstandard for EdDSA, double-hashes, not recommended. * * @param hash SHA1Hash, Hash, Hash384, or Hash512 * @param type returns a Signature of this type * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.9.9 */ private Signature altSignRaw(String algo, SimpleDataStructure hash, PrivateKey privKey, SigType type) throws GeneralSecurityException { int hashlen = hash.length(); if (type.getHashLen() != hashlen) throw new IllegalArgumentException("type mismatch hash=" + hash.getClass() + " key=" + type); byte[] sigbytes; if (type.getBaseAlgorithm() == SigAlgo.EdDSA) { // take advantage of one-shot mode // Ignore algo, EdDSAKey includes a hash specification. EdDSAEngine jsig = new EdDSAEngine(); jsig.initSign(privKey); sigbytes = jsig.signOneShot(hash.getData()); } else { java.security.Signature jsig = java.security.Signature.getInstance(algo); jsig.initSign(privKey, _context.random()); jsig.update(hash.getData()); sigbytes = jsig.sign(); } return SigUtil.fromJavaSig(sigbytes, type); } /** * Alternate to sign() using java.security libraries. * @throws GeneralSecurityException if algorithm unvailable or on other errors * @since 0.8.7 added off/len args 0.9.12 */ private Signature altSignSHA1(byte[] data, int offset, int len, SigningPrivateKey privateKey) throws GeneralSecurityException { java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA"); PrivateKey privKey = SigUtil.toJavaDSAKey(privateKey); jsig.initSign(privKey, _context.random()); jsig.update(data, offset, len); return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1); } /** @since 0.9.9 */ private static String getRawAlgo(SigType type) { switch (type.getBaseAlgorithm()) { case DSA: return "NONEwithDSA"; case EC: return "NONEwithECDSA"; case EdDSA: return "NONEwithEdDSA"; case RSA: return "NONEwithRSA"; default: throw new UnsupportedOperationException("Raw signatures unsupported for " + type); } } /** @since 0.9.9 */ private static String getRawAlgo(Key key) { if (key instanceof DSAKey) return "NONEwithDSA"; if (key instanceof ECKey) return "NONEwithECDSA"; if (key instanceof EdDSAKey) return "NONEwithEdDSA"; if (key instanceof RSAKey) return "NONEwithRSA"; throw new UnsupportedOperationException("Raw signatures unsupported for " + key.getClass().getName()); } //private static final int RUNS = 1000; /** * Run consistency and speed tests with both TheCrypto and java.security libraries. * * TheCrypto is about 5-15% faster than java.security. */ /**** public static void main(String args[]) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); byte data[] = new byte[1024]; // warmump ctx.random().nextBytes(data); try { Thread.sleep(1000); } catch (InterruptedException ie) {} SimpleDataStructure keys[] = null; System.err.println("100 runs with new data and keys each time"); for (int i = 0; i < 100; i++) { ctx.random().nextBytes(data); keys = ctx.keyGenerator().generateSigningKeys(); Signature sig = ctx.dsa().sign(data, (SigningPrivateKey)keys[1]); Signature jsig = null; try { jsig = ctx.dsa().altSignSHA1(data, (SigningPrivateKey)keys[1]); } catch (GeneralSecurityException gse) { gse.printStackTrace(); } boolean ok = ctx.dsa().verifySignature(jsig, data, (SigningPublicKey)keys[0]); boolean usok = ctx.dsa().verifySignature(sig, data, (SigningPublicKey)keys[0]); boolean jok = false; try { jok = ctx.dsa().altVerifySigSHA1(sig, data, (SigningPublicKey)keys[0]); } catch (GeneralSecurityException gse) { gse.printStackTrace(); } boolean jjok = false;; try { jjok = ctx.dsa().altVerifySigSHA1(jsig, data, (SigningPublicKey)keys[0]); } catch (GeneralSecurityException gse) { gse.printStackTrace(); } System.err.println("TC->TC OK: " + usok + " JL->TC OK: " + ok + " TC->JK OK: " + jok + " JL->JL OK: " + jjok); if (!(ok && usok && jok && jjok)) { System.out.println("privkey\n" + net.i2p.util.HexDump.dump(keys[1].getData())); return; } } System.err.println("Starting speed test"); long start = System.currentTimeMillis(); for (int i = 0; i < RUNS; i++) { Signature sig = ctx.dsa().sign(data, (SigningPrivateKey)keys[1]); boolean ok = ctx.dsa().verifySignature(sig, data, (SigningPublicKey)keys[0]); if (!ok) { System.err.println("TheCrypto FAIL"); return; } } long time = System.currentTimeMillis() - start; System.err.println("Time for " + RUNS + " DSA sign/verifies:"); System.err.println("TheCrypto time (ms): " + time); start = System.currentTimeMillis(); for (int i = 0; i < RUNS; i++) { boolean ok = false; try { Signature jsig = ctx.dsa().altSignSHA1(data, (SigningPrivateKey)keys[1]); ok = ctx.dsa().altVerifySigSHA1(jsig, data, (SigningPublicKey)keys[0]); } catch (GeneralSecurityException gse) { gse.printStackTrace(); } if (!ok) { System.err.println("JavaLib FAIL"); return; } } time = System.currentTimeMillis() - start; System.err.println("JavaLib time (ms): " + time); ****/ /**** yes, arraycopy is slower for 20 bytes start = System.currentTimeMillis(); byte b[] = new byte[20]; for (int i = 0; i < 10000000; i++) { data[0] = data[i % 256]; System.arraycopy(data, 0, b, 0, 20); } time = System.currentTimeMillis() - start; System.err.println("arraycopy time (ms): " + time); start = System.currentTimeMillis(); for (int i = 0; i < 10000000; i++) { data[0] = data[i % 256]; for (int j = 0; j < 20; j++) { b[j] = data[j]; } } time = System.currentTimeMillis() - start; System.err.println("loop time (ms): " + time); ****/ /**** } ****/ }