forked from I2P_Developers/i2p.i2p
- DSAEngine: Start of support for signing hashes with other SigTypes
- KeyGenerator: Measure sign and verify separately in test - SU3File: Start to support other SigTypes - SigType: Add getHashInstance(), fix hash length for ECDSA_SHA256_P192 - SHA1Hash: Add no-arg constructor
This commit is contained in:
@ -155,6 +155,31 @@ public class DSAEngine {
|
||||
return verifySig(signature, hash, verifyingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic signature type.
|
||||
*
|
||||
* @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);
|
||||
// FIXME hash of hash
|
||||
try {
|
||||
return altVerifySig(signature, hash.getData(), verifyingKey);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn(type + " Sig Verify Fail", gse);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify using DSA-SHA1 or Syndie DSA-SHA256 ONLY.
|
||||
* @param hash either a Hash or a SHA1Hash
|
||||
@ -286,6 +311,30 @@ public class DSAEngine {
|
||||
return signIt(hash, signingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic signature type.
|
||||
*
|
||||
* @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);
|
||||
// FIXME hash of hash
|
||||
try {
|
||||
return altSign(hash.getData(), signingKey);
|
||||
} 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.
|
||||
*
|
||||
@ -334,6 +383,9 @@ public class DSAEngine {
|
||||
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);
|
||||
@ -350,6 +402,9 @@ public class DSAEngine {
|
||||
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);
|
||||
@ -405,7 +460,7 @@ public class DSAEngine {
|
||||
throws GeneralSecurityException {
|
||||
SigType type = signature.getType();
|
||||
if (type != verifyingKey.getType())
|
||||
throw new IllegalArgumentException("type mismatch sig=" + signature.getType() + " key=" + verifyingKey.getType());
|
||||
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
|
||||
if (type == SigType.DSA_SHA1)
|
||||
return altVerifySigSHA1(signature, data, verifyingKey);
|
||||
|
||||
|
@ -282,7 +282,8 @@ public class KeyGenerator {
|
||||
|
||||
private static void testSig(SigType type, int runs) throws GeneralSecurityException {
|
||||
byte src[] = new byte[512];
|
||||
long time = 0;
|
||||
long stime = 0;
|
||||
long vtime = 0;
|
||||
SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
|
||||
//System.out.println("pubkey " + keys[0]);
|
||||
//System.out.println("privkey " + keys[1]);
|
||||
@ -290,14 +291,19 @@ public class KeyGenerator {
|
||||
RandomSource.getInstance().nextBytes(src);
|
||||
long start = System.nanoTime();
|
||||
Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey) keys[1]);
|
||||
long mid = System.nanoTime();
|
||||
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey) keys[0]);
|
||||
long end = System.nanoTime();
|
||||
time += end - start;
|
||||
stime += mid - start;
|
||||
vtime += end - mid;
|
||||
if (!ok)
|
||||
throw new GeneralSecurityException(type + " V(S(data)) fail");
|
||||
}
|
||||
time /= 1000*1000;
|
||||
System.out.println("Signing Keygen " + runs + " times: " + time + " ms = " + (((double) time) / runs) + " each");
|
||||
stime /= 1000*1000;
|
||||
vtime /= 1000*1000;
|
||||
System.out.println("Sign/verify " + runs + " times: " + (vtime+stime) + " ms = " +
|
||||
(((double) stime) / runs) + " each sign, " +
|
||||
(((double) vtime) / runs) + " each verify");
|
||||
}
|
||||
|
||||
/******
|
||||
|
@ -26,6 +26,11 @@ public class SHA1Hash extends SimpleDataStructure {
|
||||
|
||||
public final static int HASH_LENGTH = SHA1.HASH_LENGTH;
|
||||
|
||||
/** @since 0.9.9 */
|
||||
public SHA1Hash() {
|
||||
super();
|
||||
}
|
||||
|
||||
/** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */
|
||||
public SHA1Hash(byte data[]) {
|
||||
super(data);
|
||||
|
@ -12,6 +12,7 @@ import java.io.OutputStream;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -21,6 +22,7 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Succesor to the ".sud" format used in TrustedUpdate.
|
||||
@ -42,6 +44,7 @@ public class SU3File {
|
||||
private long _contentLength;
|
||||
private SigningPublicKey _signerPubkey;
|
||||
private boolean _headerVerified;
|
||||
private SigType _sigType;
|
||||
|
||||
private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
|
||||
private static final int FILE_VERSION = 0;
|
||||
@ -55,7 +58,7 @@ public class SU3File {
|
||||
private static final int CONTENT_PLUGIN = 2;
|
||||
private static final int CONTENT_RESEED = 3;
|
||||
|
||||
private static final int SIG_DSA_160 = SigType.DSA_SHA1.getCode();
|
||||
private static final SigType DEFAULT_TYPE = SigType.DSA_SHA1;
|
||||
|
||||
/**
|
||||
* Uses TrustedUpdate's default keys for verification.
|
||||
@ -129,13 +132,14 @@ public class SU3File {
|
||||
if (foo != FILE_VERSION)
|
||||
throw new IOException("bad file version");
|
||||
skip(in, 1);
|
||||
int sigType = in.read();
|
||||
int sigTypeCode = in.read();
|
||||
_sigType = SigType.getByCode(sigTypeCode);
|
||||
// TODO, for other known algos we must start over with a new MessageDigest
|
||||
// (rewind 10 bytes)
|
||||
if (sigType != SIG_DSA_160)
|
||||
throw new IOException("bad sig type");
|
||||
if (_sigType == null)
|
||||
throw new IOException("unknown sig type: " + sigTypeCode);
|
||||
_signerLength = (int) DataHelper.readLong(in, 2);
|
||||
if (_signerLength != Signature.SIGNATURE_BYTES)
|
||||
if (_signerLength != _sigType.getSigLen())
|
||||
throw new IOException("bad sig length");
|
||||
skip(in, 1);
|
||||
int _versionLength = in.read();
|
||||
@ -214,7 +218,7 @@ public class SU3File {
|
||||
OutputStream out = null;
|
||||
boolean rv = false;
|
||||
try {
|
||||
MessageDigest md = SHA1.getInstance();
|
||||
MessageDigest md = _sigType.getDigestInstance();
|
||||
in = new DigestInputStream(new BufferedInputStream(new FileInputStream(_file)), md);
|
||||
if (!_headerVerified)
|
||||
verifyHeader(in);
|
||||
@ -234,9 +238,10 @@ public class SU3File {
|
||||
}
|
||||
byte[] sha = md.digest();
|
||||
in.on(false);
|
||||
Signature signature = new Signature();
|
||||
Signature signature = new Signature(_sigType);
|
||||
signature.readBytes(in);
|
||||
SHA1Hash hash = new SHA1Hash(sha);
|
||||
SimpleDataStructure hash = _sigType.getHashInstance();
|
||||
hash.setData(sha);
|
||||
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
|
||||
} catch (DataFormatException dfe) {
|
||||
IOException ioe = new IOException("foo");
|
||||
@ -268,14 +273,15 @@ public class SU3File {
|
||||
boolean ok = false;
|
||||
try {
|
||||
in = new BufferedInputStream(new FileInputStream(content));
|
||||
MessageDigest md = SHA1.getInstance();
|
||||
SigType sigType = privkey.getType();
|
||||
MessageDigest md = sigType.getDigestInstance();
|
||||
out = new DigestOutputStream(new BufferedOutputStream(new FileOutputStream(_file)), md);
|
||||
out.write(MAGIC);
|
||||
out.write((byte) 0);
|
||||
out.write((byte) FILE_VERSION);
|
||||
out.write((byte) 0);
|
||||
out.write((byte) SIG_DSA_160);
|
||||
DataHelper.writeLong(out, 2, Signature.SIGNATURE_BYTES);
|
||||
out.write((byte) sigType.getCode());
|
||||
DataHelper.writeLong(out, 2, sigType.getSigLen());
|
||||
out.write((byte) 0);
|
||||
byte[] verBytes = DataHelper.getUTF8(version);
|
||||
if (verBytes.length == 0 || verBytes.length > 255)
|
||||
@ -315,7 +321,8 @@ public class SU3File {
|
||||
|
||||
byte[] sha = md.digest();
|
||||
out.on(false);
|
||||
SHA1Hash hash = new SHA1Hash(sha);
|
||||
SimpleDataStructure hash = sigType.getHashInstance();
|
||||
hash.setData(sha);
|
||||
Signature signature = _context.dsa().sign(hash, privkey);
|
||||
signature.writeBytes(out);
|
||||
ok = true;
|
||||
@ -344,7 +351,10 @@ public class SU3File {
|
||||
if ("showversion".equals(args[0])) {
|
||||
ok = showVersionCLI(args[1]);
|
||||
} else if ("sign".equals(args[0])) {
|
||||
ok = signCLI(args[1], args[2], args[3], args[4], args[5]);
|
||||
if (args[1].equals("-t"))
|
||||
ok = signCLI(args[2], args[3], args[4], args[5], args[6], args[7]);
|
||||
else
|
||||
ok = signCLI(args[1], args[2], args[3], args[4], args[5]);
|
||||
} else if ("verifysig".equals(args[0])) {
|
||||
ok = verifySigCLI(args[1]);
|
||||
} else {
|
||||
@ -359,8 +369,22 @@ public class SU3File {
|
||||
|
||||
private static final void showUsageCLI() {
|
||||
System.err.println("Usage: SU3File showversion signedFile.su3");
|
||||
System.err.println(" SU3File sign inputFile.zip signedFile.su3 privateKeyFile version signerName@mail.i2p");
|
||||
System.err.println(" SU3File sign [-t type|code] inputFile.zip signedFile.su3 privateKeyFile version signerName@mail.i2p");
|
||||
System.err.println(" SU3File verifysig signedFile.su3");
|
||||
System.err.println(dumpSigTypes());
|
||||
}
|
||||
|
||||
/** @since 0.9.9 */
|
||||
private static String dumpSigTypes() {
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
buf.append("Available signature types:\n");
|
||||
for (SigType t : EnumSet.allOf(SigType.class)) {
|
||||
buf.append(" ").append(t).append("\t(code: ").append(t.getCode()).append(')');
|
||||
if (t == SigType.DSA_SHA1)
|
||||
buf.append(" DEFAULT");
|
||||
buf.append('\n');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** @return success */
|
||||
@ -371,7 +395,14 @@ public class SU3File {
|
||||
if (versionString.equals(""))
|
||||
System.out.println("No version string found in file '" + signedFile + "'");
|
||||
else
|
||||
System.out.println("Version: " + versionString);
|
||||
System.out.println("Version: " + versionString);
|
||||
String signerString = file.getSignerString();
|
||||
if (signerString.equals(""))
|
||||
System.out.println("No signer string found in file '" + signedFile + "'");
|
||||
else
|
||||
System.out.println("Signer: " + signerString);
|
||||
if (file._sigType != null)
|
||||
System.out.println("SigType: " + file._sigType);
|
||||
return !versionString.equals("");
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
@ -380,12 +411,43 @@ public class SU3File {
|
||||
}
|
||||
|
||||
/** @return success */
|
||||
private static final boolean signCLI(String inputFile, String signedFile, String privateKeyFile,
|
||||
String version, String signerName) {
|
||||
private static final boolean signCLI(String inputFile, String signedFile,
|
||||
String privateKeyFile, String version, String signerName) {
|
||||
return signCLI(DEFAULT_TYPE, inputFile, signedFile, privateKeyFile, version, signerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return success
|
||||
* @since 0.9.9
|
||||
*/
|
||||
private static final boolean signCLI(String stype, String inputFile, String signedFile,
|
||||
String privateKeyFile, String version, String signerName) {
|
||||
SigType type = null;
|
||||
try {
|
||||
type = SigType.valueOf(stype);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
try {
|
||||
int code = Integer.parseInt(stype);
|
||||
type = SigType.getByCode(code);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
if (type == null) {
|
||||
System.out.println("Signature type " + stype + " is not supported");
|
||||
return false;
|
||||
}
|
||||
return signCLI(type, inputFile, signedFile, privateKeyFile, version, signerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return success
|
||||
* @since 0.9.9
|
||||
*/
|
||||
private static final boolean signCLI(SigType type, String inputFile, String signedFile,
|
||||
String privateKeyFile, String version, String signerName) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(privateKeyFile);
|
||||
SigningPrivateKey spk = new SigningPrivateKey();
|
||||
SigningPrivateKey spk = new SigningPrivateKey(type);
|
||||
spk.readBytes(in);
|
||||
in.close();
|
||||
SU3File file = new SU3File(signedFile);
|
||||
|
@ -7,6 +7,9 @@ import java.security.spec.InvalidParameterSpecException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
|
||||
/**
|
||||
* Defines the properties for various signature types
|
||||
* that I2P supports or may someday support.
|
||||
@ -35,7 +38,7 @@ public enum SigType {
|
||||
|
||||
// TESTING....................
|
||||
|
||||
ECDSA_SHA256_P192(5, 48, 24, 20, 48, "SHA-1", "SHA256withECDSA", ECConstants.P192_SPEC),
|
||||
ECDSA_SHA256_P192(5, 48, 24, 32, 48, "SHA-256", "SHA256withECDSA", ECConstants.P192_SPEC),
|
||||
ECDSA_SHA256_P384(6, 96, 48, 32, 96, "SHA-256", "SHA256withECDSA", ECConstants.P384_SPEC),
|
||||
ECDSA_SHA256_P521(7, 132, 66, 32, 132, "SHA-256", "SHA256withECDSA", ECConstants.P521_SPEC),
|
||||
|
||||
@ -112,6 +115,25 @@ public enum SigType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.9
|
||||
* @throws UnsupportedOperationException if not supported
|
||||
*/
|
||||
public SimpleDataStructure getHashInstance() {
|
||||
switch (getHashLen()) {
|
||||
case 20:
|
||||
return new SHA1Hash();
|
||||
case 32:
|
||||
return new Hash();
|
||||
case 48:
|
||||
return new Hash384();
|
||||
case 64:
|
||||
return new Hash512();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported hash length: " + getHashLen());
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
|
||||
|
||||
static {
|
||||
|
Reference in New Issue
Block a user