forked from I2P_Developers/i2p.i2p
* Signatures:
- Prep for new signature algorithms; new SigType enum; Signature, SigningPublicKey, SigningPrivateKey store type - New Hash384 and Hash512 classes - Remove length field in SimpleDataStructure - New SU3File generator/verifier/extractor
This commit is contained in:
31
core/java/src/net/i2p/crypto/Hash384.java
Normal file
31
core/java/src/net/i2p/crypto/Hash384.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* No warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.i2p.data.SimpleDataStructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 48 byte hash
|
||||||
|
*
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public class Hash384 extends SimpleDataStructure {
|
||||||
|
|
||||||
|
public final static int HASH_LENGTH = 48;
|
||||||
|
|
||||||
|
public Hash384() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @throws IllegalArgumentException if data is not correct length (null is ok) */
|
||||||
|
public Hash384(byte data[]) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length() {
|
||||||
|
return HASH_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
31
core/java/src/net/i2p/crypto/Hash512.java
Normal file
31
core/java/src/net/i2p/crypto/Hash512.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* free (adj.): unencumbered; not under the control of others
|
||||||
|
* No warranty of any kind, either expressed or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.i2p.data.SimpleDataStructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 64 byte hash
|
||||||
|
*
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public class Hash512 extends SimpleDataStructure {
|
||||||
|
|
||||||
|
public final static int HASH_LENGTH = 64;
|
||||||
|
|
||||||
|
public Hash512() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @throws IllegalArgumentException if data is not correct length (null is ok) */
|
||||||
|
public Hash512(byte data[]) {
|
||||||
|
super(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length() {
|
||||||
|
return HASH_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
425
core/java/src/net/i2p/crypto/SU3File.java
Normal file
425
core/java/src/net/i2p/crypto/SU3File.java
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.security.DigestInputStream;
|
||||||
|
import java.security.DigestOutputStream;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Signature;
|
||||||
|
import net.i2p.data.SigningPrivateKey;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Succesor to the ".sud" format used in TrustedUpdate.
|
||||||
|
* Format specified in http://www.i2p2.de/updates
|
||||||
|
*
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public class SU3File {
|
||||||
|
|
||||||
|
private final I2PAppContext _context;
|
||||||
|
private final Map<SigningPublicKey, String> _trustedKeys;
|
||||||
|
|
||||||
|
private final File _file;
|
||||||
|
private String _version;
|
||||||
|
private int _versionLength;
|
||||||
|
private String _signer;
|
||||||
|
private int _signerLength;
|
||||||
|
private int _contentType;
|
||||||
|
private long _contentLength;
|
||||||
|
private SigningPublicKey _signerPubkey;
|
||||||
|
private boolean _headerVerified;
|
||||||
|
|
||||||
|
private static final byte[] MAGIC = DataHelper.getUTF8("I2Psu3");
|
||||||
|
private static final int FILE_VERSION = 0;
|
||||||
|
private static final int MIN_VERSION_BYTES = 16;
|
||||||
|
private static final int VERSION_OFFSET = Signature.SIGNATURE_BYTES;
|
||||||
|
|
||||||
|
private static final int TYPE_ZIP = 0;
|
||||||
|
|
||||||
|
private static final int CONTENT_ROUTER = 0;
|
||||||
|
private static final int CONTENT_ROUTER_P200 = 1;
|
||||||
|
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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses TrustedUpdate's default keys for verification.
|
||||||
|
*/
|
||||||
|
public SU3File(String file) {
|
||||||
|
this(new File(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses TrustedUpdate's default keys for verification.
|
||||||
|
*/
|
||||||
|
public SU3File(File file) {
|
||||||
|
this(file, (new TrustedUpdate()).getKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param trustedKeys map of pubkey to signer name, null ok if not verifying
|
||||||
|
*/
|
||||||
|
public SU3File(File file, Map<SigningPublicKey, String> trustedKeys) {
|
||||||
|
this(I2PAppContext.getGlobalContext(), file, trustedKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param trustedKeys map of pubkey to signer name, null ok if not verifying
|
||||||
|
*/
|
||||||
|
public SU3File(I2PAppContext context, File file, Map<SigningPublicKey, String> trustedKeys) {
|
||||||
|
_context = context;
|
||||||
|
_file = file;
|
||||||
|
_trustedKeys = trustedKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersionString() throws IOException {
|
||||||
|
verifyHeader();
|
||||||
|
return _version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSignerString() throws IOException {
|
||||||
|
verifyHeader();
|
||||||
|
return _signer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws IOE if verify vails.
|
||||||
|
*/
|
||||||
|
public void verifyHeader() throws IOException {
|
||||||
|
if (_headerVerified)
|
||||||
|
return;
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
in = new FileInputStream(_file);
|
||||||
|
verifyHeader(in);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
IOException ioe = new IOException("foo");
|
||||||
|
ioe.initCause(dfe);
|
||||||
|
throw ioe;
|
||||||
|
} finally {
|
||||||
|
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws if verify vails.
|
||||||
|
*/
|
||||||
|
private void verifyHeader(InputStream in) throws IOException, DataFormatException {
|
||||||
|
byte[] magic = new byte[MAGIC.length];
|
||||||
|
DataHelper.read(in, magic);
|
||||||
|
if (!DataHelper.eq(magic, MAGIC))
|
||||||
|
throw new IOException("Not an su3 file");
|
||||||
|
skip(in, 1);
|
||||||
|
int foo = in.read();
|
||||||
|
if (foo != FILE_VERSION)
|
||||||
|
throw new IOException("bad file version");
|
||||||
|
skip(in, 1);
|
||||||
|
int sigType = in.read();
|
||||||
|
// 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");
|
||||||
|
_signerLength = (int) DataHelper.readLong(in, 2);
|
||||||
|
if (_signerLength != Signature.SIGNATURE_BYTES)
|
||||||
|
throw new IOException("bad sig length");
|
||||||
|
skip(in, 1);
|
||||||
|
int _versionLength = in.read();
|
||||||
|
if (_versionLength < MIN_VERSION_BYTES)
|
||||||
|
throw new IOException("bad version length");
|
||||||
|
skip(in, 1);
|
||||||
|
int signerLen = in.read();
|
||||||
|
if (signerLen <= 0)
|
||||||
|
throw new IOException("bad signer length");
|
||||||
|
_contentLength = DataHelper.readLong(in, 8);
|
||||||
|
if (_contentLength <= 0)
|
||||||
|
throw new IOException("bad content length");
|
||||||
|
skip(in, 1);
|
||||||
|
foo = in.read();
|
||||||
|
if (foo != TYPE_ZIP)
|
||||||
|
throw new IOException("bad type");
|
||||||
|
skip(in, 1);
|
||||||
|
_contentType = in.read();
|
||||||
|
if (_contentType < CONTENT_ROUTER || _contentType > CONTENT_RESEED)
|
||||||
|
throw new IOException("bad content type");
|
||||||
|
skip(in, 12);
|
||||||
|
|
||||||
|
byte[] data = new byte[_versionLength];
|
||||||
|
int bytesRead = DataHelper.read(in, data);
|
||||||
|
if (bytesRead != _versionLength)
|
||||||
|
throw new EOFException();
|
||||||
|
int zbyte;
|
||||||
|
for (zbyte = 0; zbyte < _versionLength; zbyte++) {
|
||||||
|
if (data[zbyte] == 0x00)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_version = new String(data, 0, zbyte, "UTF-8");
|
||||||
|
|
||||||
|
data = new byte[signerLen];
|
||||||
|
bytesRead = DataHelper.read(in, data);
|
||||||
|
if (bytesRead != signerLen)
|
||||||
|
throw new EOFException();
|
||||||
|
_signer = DataHelper.getUTF8(data);
|
||||||
|
if (_trustedKeys != null) {
|
||||||
|
for (Map.Entry<SigningPublicKey, String> e : _trustedKeys.entrySet()) {
|
||||||
|
if (e.getValue().equals(_signer)) {
|
||||||
|
_signerPubkey = e.getKey();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_signerPubkey == null)
|
||||||
|
throw new IOException("unknown signer: " + _signer);
|
||||||
|
}
|
||||||
|
_headerVerified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** skip but update digest */
|
||||||
|
private static void skip(InputStream in, int cnt) throws IOException {
|
||||||
|
for (int i = 0; i < cnt; i++) {
|
||||||
|
if (in.read() < 0)
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getContentOffset() throws IOException {
|
||||||
|
verifyHeader();
|
||||||
|
return VERSION_OFFSET + _versionLength + _signerLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-pass verify and extract the content.
|
||||||
|
* Recommend extracting to a temp location as the sig is not checked until
|
||||||
|
* after extraction. This will delete the file if the sig does not verify.
|
||||||
|
* Throws IOE on all format errors.
|
||||||
|
*
|
||||||
|
* @param migrateTo the output file, probably in zip format
|
||||||
|
* @return true if signature is good
|
||||||
|
*/
|
||||||
|
public boolean verifyAndMigrate(File migrateTo) throws IOException {
|
||||||
|
DigestInputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
|
boolean rv = false;
|
||||||
|
try {
|
||||||
|
MessageDigest md = SHA1.getInstance();
|
||||||
|
in = new DigestInputStream(new BufferedInputStream(new FileInputStream(_file)), md);
|
||||||
|
if (!_headerVerified)
|
||||||
|
verifyHeader(in);
|
||||||
|
else
|
||||||
|
skip(in, getContentOffset());
|
||||||
|
if (_signerPubkey == null)
|
||||||
|
throw new IOException("unknown signer: " + _signer);
|
||||||
|
out = new FileOutputStream(migrateTo);
|
||||||
|
byte[] buf = new byte[16*1024];
|
||||||
|
long tot = 0;
|
||||||
|
while (tot < _contentLength) {
|
||||||
|
int read = in.read(buf, 0, (int) Math.min(buf.length, _contentLength - tot));
|
||||||
|
if (read < 0)
|
||||||
|
throw new EOFException();
|
||||||
|
out.write(buf, 0, read);
|
||||||
|
tot += read;
|
||||||
|
}
|
||||||
|
byte[] sha = md.digest();
|
||||||
|
in.on(false);
|
||||||
|
Signature signature = new Signature();
|
||||||
|
signature.readBytes(in);
|
||||||
|
SHA1Hash hash = new SHA1Hash(sha);
|
||||||
|
rv = _context.dsa().verifySignature(signature, hash, _signerPubkey);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
IOException ioe = new IOException("foo");
|
||||||
|
ioe.initCause(dfe);
|
||||||
|
throw ioe;
|
||||||
|
} finally {
|
||||||
|
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||||
|
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||||
|
if (!rv)
|
||||||
|
migrateTo.delete();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-pass wrap and sign the content.
|
||||||
|
* Writes to the file specified in the constructor.
|
||||||
|
* Throws on all errors.
|
||||||
|
*
|
||||||
|
* @param content the input file, probably in zip format
|
||||||
|
* @param contentType 0-255, 0 for zip
|
||||||
|
* @param version 1-255 bytes when converted to UTF-8
|
||||||
|
* @param signer ID of the public key, 1-255 bytes when converted to UTF-8
|
||||||
|
*/
|
||||||
|
public void write(File content, int contentType, String version,
|
||||||
|
String signer, SigningPrivateKey privkey) throws IOException {
|
||||||
|
InputStream in = null;
|
||||||
|
DigestOutputStream out = null;
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
in = new BufferedInputStream(new FileInputStream(content));
|
||||||
|
MessageDigest md = SHA1.getInstance();
|
||||||
|
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) 0);
|
||||||
|
byte[] verBytes = DataHelper.getUTF8(version);
|
||||||
|
if (verBytes.length == 0 || verBytes.length > 255)
|
||||||
|
throw new IllegalArgumentException("bad version length");
|
||||||
|
int verLen = Math.max(verBytes.length, MIN_VERSION_BYTES);
|
||||||
|
out.write((byte) verLen);
|
||||||
|
out.write((byte) 0);
|
||||||
|
byte[] signerBytes = DataHelper.getUTF8(signer);
|
||||||
|
if (signerBytes.length == 0 || signerBytes.length > 255)
|
||||||
|
throw new IllegalArgumentException("bad signer length");
|
||||||
|
out.write((byte) signerBytes.length);
|
||||||
|
long contentLength = content.length();
|
||||||
|
if (contentLength <= 0)
|
||||||
|
throw new IllegalArgumentException("No content");
|
||||||
|
DataHelper.writeLong(out, 8, contentLength);
|
||||||
|
out.write((byte) 0);
|
||||||
|
out.write((byte) TYPE_ZIP);
|
||||||
|
out.write((byte) 0);
|
||||||
|
if (contentType < 0 || contentType > 255)
|
||||||
|
throw new IllegalArgumentException("bad content type");
|
||||||
|
out.write((byte) contentType);
|
||||||
|
out.write(new byte[12]);
|
||||||
|
out.write(verBytes);
|
||||||
|
if (verBytes.length < MIN_VERSION_BYTES)
|
||||||
|
out.write(new byte[MIN_VERSION_BYTES - verBytes.length]);
|
||||||
|
out.write(signerBytes);
|
||||||
|
|
||||||
|
byte[] buf = new byte[16*1024];
|
||||||
|
long tot = 0;
|
||||||
|
while (tot < contentLength) {
|
||||||
|
int read = in.read(buf, 0, (int) Math.min(buf.length, contentLength - tot));
|
||||||
|
if (read < 0)
|
||||||
|
throw new EOFException();
|
||||||
|
out.write(buf, 0, read);
|
||||||
|
tot += read;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] sha = md.digest();
|
||||||
|
out.on(false);
|
||||||
|
SHA1Hash hash = new SHA1Hash(sha);
|
||||||
|
Signature signature = _context.dsa().sign(hash, privkey);
|
||||||
|
signature.writeBytes(out);
|
||||||
|
ok = true;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
IOException ioe = new IOException("foo");
|
||||||
|
ioe.initCause(dfe);
|
||||||
|
throw ioe;
|
||||||
|
} finally {
|
||||||
|
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||||
|
if (out != null) try { out.close(); } catch (IOException ioe) {}
|
||||||
|
if (!ok)
|
||||||
|
_file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses command line arguments when this class is used from the command
|
||||||
|
* line.
|
||||||
|
* Exits 1 on failure so this can be used in scripts.
|
||||||
|
*
|
||||||
|
* @param args Command line parameters.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
boolean ok = false;
|
||||||
|
try {
|
||||||
|
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]);
|
||||||
|
} else if ("verifysig".equals(args[0])) {
|
||||||
|
ok = verifySigCLI(args[1]);
|
||||||
|
} else {
|
||||||
|
showUsageCLI();
|
||||||
|
}
|
||||||
|
} catch (ArrayIndexOutOfBoundsException aioobe) {
|
||||||
|
showUsageCLI();
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final void showUsageCLI() {
|
||||||
|
System.err.println("Usage: SU3File showversion signedFile");
|
||||||
|
System.err.println(" SU3File sign inputFile signedFile privateKeyFile version signerName");
|
||||||
|
System.err.println(" SU3File verifysig signedFile");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return success */
|
||||||
|
private static final boolean showVersionCLI(String signedFile) {
|
||||||
|
try {
|
||||||
|
SU3File file = new SU3File(new File(signedFile), null);
|
||||||
|
String versionString = file.getVersionString();
|
||||||
|
if (versionString.equals(""))
|
||||||
|
System.out.println("No version string found in file '" + signedFile + "'");
|
||||||
|
else
|
||||||
|
System.out.println("Version: " + versionString);
|
||||||
|
return !versionString.equals("");
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
ioe.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return success */
|
||||||
|
private static final boolean signCLI(String inputFile, String signedFile, String privateKeyFile,
|
||||||
|
String version, String signerName) {
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
in = new FileInputStream(privateKeyFile);
|
||||||
|
SigningPrivateKey spk = new SigningPrivateKey();
|
||||||
|
spk.readBytes(in);
|
||||||
|
in.close();
|
||||||
|
SU3File file = new SU3File(signedFile);
|
||||||
|
file.write(new File(inputFile), CONTENT_ROUTER, version, signerName, spk);
|
||||||
|
System.out.println("Input file '" + inputFile + "' signed and written to '" + signedFile + "'");
|
||||||
|
return true;
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
System.out.println("Error signing input file '" + inputFile + "'");
|
||||||
|
dfe.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
System.out.println("Error signing input file '" + inputFile + "'");
|
||||||
|
ioe.printStackTrace();
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return valid */
|
||||||
|
private static final boolean verifySigCLI(String signedFile) {
|
||||||
|
InputStream in = null;
|
||||||
|
try {
|
||||||
|
SU3File file = new SU3File(signedFile);
|
||||||
|
boolean isValidSignature = file.verifyAndMigrate(new File("/dev/null"));
|
||||||
|
if (isValidSignature)
|
||||||
|
System.out.println("Signature VALID (signed by " + file.getSignerString() + ')');
|
||||||
|
else
|
||||||
|
System.out.println("Signature INVALID (signed by " + file.getSignerString() + ')');
|
||||||
|
return isValidSignature;
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
System.out.println("Error verifying input file '" + signedFile + "'");
|
||||||
|
ioe.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
91
core/java/src/net/i2p/crypto/SigType.java
Normal file
91
core/java/src/net/i2p/crypto/SigType.java
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the properties for various signature types
|
||||||
|
* that I2P supports or may someday support.
|
||||||
|
*
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public enum SigType {
|
||||||
|
/**
|
||||||
|
* DSA_SHA1 is the default.
|
||||||
|
* Pubkey 128 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
DSA_SHA1(0, 128, 20, 20, 40, "SHA-1", "SHA1withDSA"),
|
||||||
|
/** Pubkey 40 bytes; privkey 20 bytes; hash 20 bytes; sig 40 bytes */
|
||||||
|
ECDSA_SHA1(1, 40, 20, 20, 40, "SHA-1", "SHA1withECDSA"),
|
||||||
|
/** Pubkey 64 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes */
|
||||||
|
ECDSA_SHA256(2, 64, 32, 32, 64, "SHA-256", "SHA256withECDSA"),
|
||||||
|
/** Pubkey 96 bytes; privkey 48 bytes; hash 48 bytes; sig 96 bytes */
|
||||||
|
ECDSA_SHA384(3, 96, 48, 48, 96, "SHA-384", "SHA384withECDSA"),
|
||||||
|
/** Pubkey 128 bytes; privkey 64 bytes; hash 64 bytes; sig 128 bytes */
|
||||||
|
ECDSA_SHA512(4, 128, 64, 64, 128, "SHA-512", "SHA512withECDSA")
|
||||||
|
|
||||||
|
//MD5
|
||||||
|
//ELGAMAL_SHA256
|
||||||
|
//RSA_SHA1
|
||||||
|
//RSA_SHA256
|
||||||
|
//RSA_SHA384
|
||||||
|
//RSA_SHA512
|
||||||
|
//DSA_2048_224(2, 256, 28, 32, 56, "SHA-256"),
|
||||||
|
// Nonstandard, used by Syndie.
|
||||||
|
// Pubkey 128 bytes; privkey 20 bytes; hash 32 bytes; sig 40 bytes
|
||||||
|
//DSA_1024_160_SHA256(1, 128, 20, 32, 40, "SHA-256", "?"),
|
||||||
|
// Pubkey 256 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes
|
||||||
|
//DSA_2048_256(2, 256, 32, 32, 64, "SHA-256", "?"),
|
||||||
|
// Pubkey 384 bytes; privkey 32 bytes; hash 32 bytes; sig 64 bytes
|
||||||
|
//DSA_3072_256(3, 384, 32, 32, 64, "SHA-256", "?"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final int code, pubkeyLen, privkeyLen, hashLen, sigLen;
|
||||||
|
private final String digestName, algoName;
|
||||||
|
|
||||||
|
SigType(int cod, int pubLen, int privLen, int hLen, int sLen, String mdName, String aName) {
|
||||||
|
code = cod;
|
||||||
|
pubkeyLen = pubLen;
|
||||||
|
privkeyLen = privLen;
|
||||||
|
hashLen = hLen;
|
||||||
|
sigLen = sLen;
|
||||||
|
digestName = mdName;
|
||||||
|
algoName = aName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() { return code; }
|
||||||
|
public int getPubkeyLen() { return pubkeyLen; }
|
||||||
|
public int getPrivkeyLen() { return privkeyLen; }
|
||||||
|
public int getHashLen() { return hashLen; }
|
||||||
|
public int getSigLen() { return sigLen; }
|
||||||
|
public String getAlgorithmName() { return algoName; }
|
||||||
|
|
||||||
|
/** @throws UnsupportedOperationException if not supported */
|
||||||
|
public MessageDigest getDigestInstance() {
|
||||||
|
if (digestName.equals("SHA-1"))
|
||||||
|
return SHA1.getInstance();
|
||||||
|
if (digestName.equals("SHA-256"))
|
||||||
|
return SHA256Generator.getDigestInstance();
|
||||||
|
try {
|
||||||
|
return MessageDigest.getInstance(digestName);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new UnsupportedOperationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Integer, SigType> BY_CODE = new HashMap<Integer, SigType>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (SigType type : SigType.values()) {
|
||||||
|
BY_CODE.put(Integer.valueOf(type.getCode()), type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return null if not supported */
|
||||||
|
public static SigType getByCode(int code) {
|
||||||
|
return BY_CODE.get(Integer.valueOf(code));
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import java.io.InputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.SequenceInputStream;
|
import java.io.SequenceInputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
@@ -209,6 +210,13 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
|
|||||||
_log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys.");
|
_log.debug("TrustedUpdate created, trusting " + _trustedKeys.size() + " keys.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
Map<SigningPublicKey, String> getKeys() {
|
||||||
|
return Collections.unmodifiableMap(_trustedKeys);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duplicate keys or names rejected,
|
* Duplicate keys or names rejected,
|
||||||
* except that duplicate empty names are allowed
|
* except that duplicate empty names are allowed
|
||||||
|
@@ -9,6 +9,8 @@ package net.i2p.data;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the signature as defined by the I2P data structure spec.
|
* Defines the signature as defined by the I2P data structure spec.
|
||||||
* A signature is a 40-byte array verifying the authenticity of some data
|
* A signature is a 40-byte array verifying the authenticity of some data
|
||||||
@@ -20,19 +22,47 @@ package net.i2p.data;
|
|||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class Signature extends SimpleDataStructure {
|
public class Signature extends SimpleDataStructure {
|
||||||
public final static int SIGNATURE_BYTES = 40;
|
private static final SigType DEF_TYPE = SigType.DSA_SHA1;
|
||||||
|
/** 40 */
|
||||||
|
public final static int SIGNATURE_BYTES = DEF_TYPE.getSigLen();
|
||||||
/** all zeros */
|
/** all zeros */
|
||||||
public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES];
|
public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES];
|
||||||
|
|
||||||
|
private final SigType _type;
|
||||||
|
|
||||||
public Signature() {
|
public Signature() {
|
||||||
|
this(DEF_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public Signature(SigType type) {
|
||||||
super();
|
super();
|
||||||
|
_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Signature(byte data[]) {
|
public Signature(byte data[]) {
|
||||||
super(data);
|
this(DEF_TYPE, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public Signature(SigType type, byte data[]) {
|
||||||
|
super();
|
||||||
|
_type = type;
|
||||||
|
setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int length() {
|
public int length() {
|
||||||
return SIGNATURE_BYTES;
|
return _type.getSigLen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigType getType() {
|
||||||
|
return _type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ package net.i2p.data;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import net.i2p.crypto.KeyGenerator;
|
import net.i2p.crypto.KeyGenerator;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
|
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
|
||||||
@@ -20,14 +21,34 @@ import net.i2p.crypto.KeyGenerator;
|
|||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class SigningPrivateKey extends SimpleDataStructure {
|
public class SigningPrivateKey extends SimpleDataStructure {
|
||||||
public final static int KEYSIZE_BYTES = 20;
|
private static final SigType DEF_TYPE = SigType.DSA_SHA1;
|
||||||
|
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
|
||||||
|
|
||||||
|
private final SigType _type;
|
||||||
|
|
||||||
public SigningPrivateKey() {
|
public SigningPrivateKey() {
|
||||||
|
this(DEF_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigningPrivateKey(SigType type) {
|
||||||
super();
|
super();
|
||||||
|
_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SigningPrivateKey(byte data[]) {
|
public SigningPrivateKey(byte data[]) {
|
||||||
super(data);
|
this(DEF_TYPE, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigningPrivateKey(SigType type, byte data[]) {
|
||||||
|
super();
|
||||||
|
_type = type;
|
||||||
|
setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** constructs from base64
|
/** constructs from base64
|
||||||
@@ -35,12 +56,20 @@ public class SigningPrivateKey extends SimpleDataStructure {
|
|||||||
* on a prior instance of SigningPrivateKey
|
* on a prior instance of SigningPrivateKey
|
||||||
*/
|
*/
|
||||||
public SigningPrivateKey(String base64Data) throws DataFormatException {
|
public SigningPrivateKey(String base64Data) throws DataFormatException {
|
||||||
super();
|
this();
|
||||||
fromBase64(base64Data);
|
fromBase64(base64Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int length() {
|
public int length() {
|
||||||
return KEYSIZE_BYTES;
|
return _type.getPrivkeyLen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigType getType() {
|
||||||
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** converts this signing private key to its public equivalent
|
/** converts this signing private key to its public equivalent
|
||||||
|
@@ -12,6 +12,8 @@ package net.i2p.data;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the SigningPublicKey as defined by the I2P data structure spec.
|
* Defines the SigningPublicKey as defined by the I2P data structure spec.
|
||||||
* A signing public key is 128 byte Integer. The public key represents only the
|
* A signing public key is 128 byte Integer. The public key represents only the
|
||||||
@@ -21,11 +23,14 @@ import java.io.IOException;
|
|||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class SigningPublicKey extends SimpleDataStructure {
|
public class SigningPublicKey extends SimpleDataStructure {
|
||||||
public final static int KEYSIZE_BYTES = 128;
|
private static final SigType DEF_TYPE = SigType.DSA_SHA1;
|
||||||
|
public final static int KEYSIZE_BYTES = DEF_TYPE.getPubkeyLen();
|
||||||
private static final int CACHE_SIZE = 1024;
|
private static final int CACHE_SIZE = 1024;
|
||||||
|
|
||||||
private static final SDSCache<SigningPublicKey> _cache = new SDSCache(SigningPublicKey.class, KEYSIZE_BYTES, CACHE_SIZE);
|
private static final SDSCache<SigningPublicKey> _cache = new SDSCache(SigningPublicKey.class, KEYSIZE_BYTES, CACHE_SIZE);
|
||||||
|
|
||||||
|
private final SigType _type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pull from cache or return new
|
* Pull from cache or return new
|
||||||
* @throws AIOOBE if not enough bytes
|
* @throws AIOOBE if not enough bytes
|
||||||
@@ -44,11 +49,28 @@ public class SigningPublicKey extends SimpleDataStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SigningPublicKey() {
|
public SigningPublicKey() {
|
||||||
|
this(DEF_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigningPublicKey(SigType type) {
|
||||||
super();
|
super();
|
||||||
|
_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SigningPublicKey(byte data[]) {
|
public SigningPublicKey(byte data[]) {
|
||||||
super(data);
|
this(DEF_TYPE, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigningPublicKey(SigType type, byte data[]) {
|
||||||
|
super();
|
||||||
|
_type = type;
|
||||||
|
setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** constructs from base64
|
/** constructs from base64
|
||||||
@@ -56,11 +78,18 @@ public class SigningPublicKey extends SimpleDataStructure {
|
|||||||
* on a prior instance of SigningPublicKey
|
* on a prior instance of SigningPublicKey
|
||||||
*/
|
*/
|
||||||
public SigningPublicKey(String base64Data) throws DataFormatException {
|
public SigningPublicKey(String base64Data) throws DataFormatException {
|
||||||
super();
|
this();
|
||||||
fromBase64(base64Data);
|
fromBase64(base64Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int length() {
|
public int length() {
|
||||||
return KEYSIZE_BYTES;
|
return _type.getPubkeyLen();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.8
|
||||||
|
*/
|
||||||
|
public SigType getType() {
|
||||||
|
return _type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,17 +31,13 @@ import net.i2p.crypto.SHA256Generator;
|
|||||||
*/
|
*/
|
||||||
public abstract class SimpleDataStructure extends DataStructureImpl {
|
public abstract class SimpleDataStructure extends DataStructureImpl {
|
||||||
protected byte[] _data;
|
protected byte[] _data;
|
||||||
/** this is just to avoid lots of calls to length() */
|
|
||||||
protected final int _length;
|
|
||||||
|
|
||||||
/** A new instance with the data set to null. Call readBytes(), setData(), or fromByteArray() after this to set the data */
|
/** A new instance with the data set to null. Call readBytes(), setData(), or fromByteArray() after this to set the data */
|
||||||
public SimpleDataStructure() {
|
public SimpleDataStructure() {
|
||||||
_length = length();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) */
|
/** @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) */
|
||||||
public SimpleDataStructure(byte data[]) {
|
public SimpleDataStructure(byte data[]) {
|
||||||
_length = length();
|
|
||||||
setData(data);
|
setData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +64,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
|||||||
public void setData(byte[] data) {
|
public void setData(byte[] data) {
|
||||||
if (_data != null)
|
if (_data != null)
|
||||||
throw new RuntimeException("Data already set");
|
throw new RuntimeException("Data already set");
|
||||||
if (data != null && data.length != _length)
|
if (data != null && data.length != length())
|
||||||
throw new IllegalArgumentException("Bad data length: " + data.length + "; required: " + _length);
|
throw new IllegalArgumentException("Bad data length: " + data.length + "; required: " + length());
|
||||||
_data = data;
|
_data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,9 +77,10 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
|||||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||||
if (_data != null)
|
if (_data != null)
|
||||||
throw new RuntimeException("Data already set");
|
throw new RuntimeException("Data already set");
|
||||||
_data = new byte[_length];
|
int length = length();
|
||||||
|
_data = new byte[length];
|
||||||
int read = read(in, _data);
|
int read = read(in, _data);
|
||||||
if (read != _length) throw new DataFormatException("Not enough bytes to read the data");
|
if (read != length) throw new DataFormatException("Not enough bytes to read the data");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
|
||||||
@@ -109,8 +106,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
|||||||
byte[] d = Base64.decode(data);
|
byte[] d = Base64.decode(data);
|
||||||
if (d == null)
|
if (d == null)
|
||||||
throw new DataFormatException("Bad Base64 encoded data");
|
throw new DataFormatException("Bad Base64 encoded data");
|
||||||
if (d.length != _length)
|
if (d.length != length())
|
||||||
throw new DataFormatException("Bad decoded data length, expected " + _length + " got " + d.length);
|
throw new DataFormatException("Bad decoded data length, expected " + length() + " got " + d.length);
|
||||||
// call setData() instead of _data = data in case overridden
|
// call setData() instead of _data = data in case overridden
|
||||||
setData(d);
|
setData(d);
|
||||||
}
|
}
|
||||||
@@ -141,8 +138,8 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
|||||||
@Override
|
@Override
|
||||||
public void fromByteArray(byte data[]) throws DataFormatException {
|
public void fromByteArray(byte data[]) throws DataFormatException {
|
||||||
if (data == null) throw new DataFormatException("Null data passed in");
|
if (data == null) throw new DataFormatException("Null data passed in");
|
||||||
if (data.length != _length)
|
if (data.length != length())
|
||||||
throw new DataFormatException("Bad data length: " + data.length + "; required: " + _length);
|
throw new DataFormatException("Bad data length: " + data.length + "; required: " + length());
|
||||||
// call setData() instead of _data = data in case overridden
|
// call setData() instead of _data = data in case overridden
|
||||||
setData(data);
|
setData(data);
|
||||||
}
|
}
|
||||||
@@ -151,12 +148,13 @@ public abstract class SimpleDataStructure extends DataStructureImpl {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder(64);
|
StringBuilder buf = new StringBuilder(64);
|
||||||
buf.append('[').append(getClass().getSimpleName()).append(": ");
|
buf.append('[').append(getClass().getSimpleName()).append(": ");
|
||||||
|
int length = length();
|
||||||
if (_data == null) {
|
if (_data == null) {
|
||||||
buf.append("null");
|
buf.append("null");
|
||||||
} else if (_length <= 32) {
|
} else if (length <= 32) {
|
||||||
buf.append(toBase64());
|
buf.append(toBase64());
|
||||||
} else {
|
} else {
|
||||||
buf.append("size: ").append(Integer.toString(_length));
|
buf.append("size: ").append(Integer.toString(length));
|
||||||
}
|
}
|
||||||
buf.append(']');
|
buf.append(']');
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
|
@@ -1,3 +1,11 @@
|
|||||||
|
2012-07-29 zzz
|
||||||
|
* Signatures:
|
||||||
|
- Prep for new signature algorithms; new SigType enum;
|
||||||
|
Signature, SigningPublicKey, SigningPrivateKey store type
|
||||||
|
- New Hash384 and Hash512 classes
|
||||||
|
- Remove length field in SimpleDataStructure
|
||||||
|
- New SU3File generator/verifier/extractor
|
||||||
|
|
||||||
2012-07-28 zzz
|
2012-07-28 zzz
|
||||||
* Addresses: Treat RFC 4193 addresses fc00::/7 as local
|
* Addresses: Treat RFC 4193 addresses fc00::/7 as local
|
||||||
* NetDB: Disable RI verifies for now
|
* NetDB: Disable RI verifies for now
|
||||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 13;
|
public final static long BUILD = 14;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user