* DataStructures:

- Shim in 3 new abstract classes
        SimpleDataStructure, KeysAndCert, and DatabaseEntry

===========

Right now, everything in net.i2p.data extends DataStructureImpl.


There are several goals for adding some intermediate abstract classes,
between DataStructureImpl and the concrete classes:


1) Merge common code
2) Make the simple cases (a single byte array) more efficient
   by adding a common base class.
   I'm calling this one SimpleDataStructure.
3) Make a common base class for Destination and RouterIdentity
   since they are almost exactly the same thing.
   Consolidate the getters/setters and hash functions here.
   I'm calling this one KeysAndCert.
4) Make a common base class for LeaseSet and RouterInfo so
   so netDb and I2NP can handle them easier, without doing
   "instanceof" all over the place.
   Consolidate the hash, signature, and routing key functions here.
   I'm calling this one DatabaseEntry.
5) Make it easier to add more object caching.


The additional classes are retrofit
above many of the data types:


DataStructureImpl (unchanged)
-------------------
	ByteArray
	Certificate
	Lease
	Payload
	RouterAddress
	TunnelId


	SimpleDataStructure (new)
	-------------------------
		Hash
		PrivateKey
		PublicKey
		SessionKey
		SessionTag (was ByteArray)
		Signature
		SigningPrivateKey
		SigningPublicKey


	KeysAndCert (new)
	-----------------
		Destination
		RouterIdentity


	DatabaseEntry (new)
	-------------------
		LeaseSet
		RouterInfo
This commit is contained in:
zzz
2010-11-14 14:09:58 +00:00
parent ad060c5d5d
commit 7967653dd1
16 changed files with 578 additions and 794 deletions

View File

@@ -4,6 +4,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* ????????
* deprecated unused except by naming/FilesystemAddressDB, which is unused.
*/
public class Address extends DataStructureImpl {
private String _hostname;
private Destination _destination;

View File

@@ -0,0 +1,176 @@
package net.i2p.data;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import net.i2p.crypto.DSAEngine;
/**
* Base implementation of common methods for the two data structures
* that are stored in the netDb, i.e. LeaseSet and RouterInfo.
* Implemented in 0.8.2 and retrofitted over LeaseSet and RouterInfo.
*
* This consolidates some common code and makes it easier to
* implement the NetDB and I2NP without doing instanceof all over the place.
*
* DatabaseEntries have a SHA256 hash, a routing key, a timestamp, and
* signatures.
*
* @author zzz
* @since 0.8.2
*/
public abstract class DatabaseEntry extends DataStructureImpl {
/** these are the same as in i2np's DatabaseStoreMessage */
public final static int KEY_TYPE_ROUTERINFO = 0;
public final static int KEY_TYPE_LEASESET = 1;
protected volatile Signature _signature;
protected volatile Hash _currentRoutingKey;
protected volatile byte[] _routingKeyGenMod;
/**
* A common interface to the timestamp of the two subclasses.
* Identical to getEarliestLeaseData() in LeaseSet,
* and getPublished() in RouterInfo.
* Note that for a LeaseSet this will be in the future,
* and for a RouterInfo it will be in the past.
* Either way, it's a timestamp.
*
* @since 0.8.2
*/
public abstract long getDate();
/**
* Get the keys and the cert
* Identical to getDestination() in LeaseSet,
* and getIdentity() in RouterInfo.
*
* @return KAC or null
* @since 0.8.2
*/
protected abstract KeysAndCert getKeysAndCert();
/**
* A common interface to the Hash of the two subclasses.
* Identical to getDestination().calculateHash() in LeaseSet,
* and getIdentity().getHash() in RouterInfo.
*
* @return Hash or null
* @since 0.8.2
*/
public Hash getHash() {
KeysAndCert kac = getKeysAndCert();
if (kac == null)
return null;
return kac.getHash();
}
/**
* Get the type of the data structure.
* This should be faster than instanceof.
*
* @return KEY_TYPE_ROUTERINFO or KEY_TYPE_LEASESET
* @since 0.8.2
*/
public abstract int getType();
/**
* Returns the raw payload data, excluding the signature, to be signed by sign().
* FIXME RouterInfo throws DFE and LeaseSet returns null
* @return null on error ???????????????????????
*/
protected abstract byte[] getBytes() throws DataFormatException;
/**
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
* This only calculates a new one when necessary though (if the generator's key modifier changes)
*
*/
public Hash getRoutingKey() {
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
if ((gen.getModData() == null) || (_routingKeyGenMod == null)
|| (!DataHelper.eq(gen.getModData(), _routingKeyGenMod))) {
_currentRoutingKey = gen.getRoutingKey(getHash());
_routingKeyGenMod = gen.getModData();
}
return _currentRoutingKey;
}
public void setRoutingKey(Hash key) {
_currentRoutingKey = key;
}
public boolean validateRoutingKey() {
Hash destKey = getHash();
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(destKey);
return rk.equals(getRoutingKey());
}
/**
* Retrieve the proof that the identity stands behind the info here
*
*/
public Signature getSignature() {
return _signature;
}
/**
* Configure the proof that the entity stands behind the info here
*
*/
public void setSignature(Signature signature) {
_signature = signature;
}
/**
* Sign the structure using the supplied signing key
*
*/
public void sign(SigningPrivateKey key) throws DataFormatException {
byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign");
// now sign with the key
_signature = DSAEngine.getInstance().sign(bytes, key);
}
/**
* Identical to getDestination().getSigningPublicKey() in LeaseSet,
* and getIdentity().getSigningPublicKey() in RouterInfo.
*
* @return SPK or null
* @since 0.8.2
*/
protected SigningPublicKey getSigningPublicKey() {
KeysAndCert kac = getKeysAndCert();
if (kac == null)
return null;
return kac.getSigningPublicKey();
}
/**
* This is the same as isValid() in RouterInfo
* or verifySignature() in LeaseSet.
* @return valid
*/
protected boolean verifySignature() {
if (_signature == null)
return false;
byte data[];
try {
data = getBytes();
} catch (DataFormatException dfe) {
return false;
}
if (data == null)
return false;
// if the data is non-null the SPK will be non-null
return DSAEngine.getInstance().verifySignature(_signature, data, getSigningPublicKey());
}
}

View File

@@ -9,22 +9,13 @@ package net.i2p.data;
*
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Defines an end point in the I2P network. The Destination may move around
* in the network, but messages sent to the Destination will find it
*
* @author jrandom
*/
public class Destination extends DataStructureImpl {
protected Certificate _certificate;
protected SigningPublicKey _signingKey;
protected PublicKey _publicKey;
protected Hash __calculatedHash;
public class Destination extends KeysAndCert {
public Destination() {
}
@@ -37,51 +28,7 @@ public class Destination extends DataStructureImpl {
fromBase64(s);
}
public Certificate getCertificate() {
return _certificate;
}
public void setCertificate(Certificate cert) {
_certificate = cert;
__calculatedHash = null;
}
public PublicKey getPublicKey() {
return _publicKey;
}
public void setPublicKey(PublicKey key) {
_publicKey = key;
__calculatedHash = null;
}
public SigningPublicKey getSigningPublicKey() {
return _signingKey;
}
public void setSigningPublicKey(SigningPublicKey key) {
_signingKey = key;
__calculatedHash = null;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_publicKey = new PublicKey();
_publicKey.readBytes(in);
_signingKey = new SigningPublicKey();
_signingKey.readBytes(in);
_certificate = new Certificate();
_certificate.readBytes(in);
__calculatedHash = null;
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
throw new DataFormatException("Not enough data to format the destination");
_publicKey.writeBytes(out);
_signingKey.writeBytes(out);
_certificate.writeBytes(out);
}
/** deprecated, used only by Packet.java in streaming */
public int writeBytes(byte target[], int offset) {
int cur = offset;
System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);
@@ -92,6 +39,7 @@ public class Destination extends DataStructureImpl {
return cur - offset;
}
/** deprecated, used only by Packet.java in streaming */
public int readBytes(byte source[], int offset) throws DataFormatException {
if (source == null) throw new DataFormatException("Null source");
if (source.length <= offset + PublicKey.KEYSIZE_BYTES + SigningPublicKey.KEYSIZE_BYTES)
@@ -119,57 +67,4 @@ public class Destination extends DataStructureImpl {
public int size() {
return PublicKey.KEYSIZE_BYTES + SigningPublicKey.KEYSIZE_BYTES + _certificate.size();
}
@Override
public boolean equals(Object object) {
if ((object == null) || !(object instanceof Destination)) return false;
Destination dst = (Destination) object;
return DataHelper.eq(_certificate, dst.getCertificate())
&& DataHelper.eq(_signingKey, dst.getSigningPublicKey())
&& DataHelper.eq(_publicKey, dst.getPublicKey());
}
/** the public key has enough randomness in it to use it by itself for speed */
@Override
public int hashCode() {
if (_publicKey == null)
return 0;
return _publicKey.hashCode();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("[Destination: ");
buf.append("\n\tHash: ").append(calculateHash().toBase64());
buf.append("\n\tPublic Key: ").append(getPublicKey());
buf.append("\n\tSigning Public Key: ").append(getSigningPublicKey());
buf.append("\n\tCertificate: ").append(getCertificate());
buf.append("]");
return buf.toString();
}
@Override
public Hash calculateHash() {
if (__calculatedHash == null) __calculatedHash = super.calculateHash();
return __calculatedHash;
}
public static void main(String args[]) {
if (args.length == 0) {
System.err.println("Usage: Destination filename");
} else {
FileInputStream in = null;
try {
in = new FileInputStream(args[0]);
Destination d = new Destination();
d.readBytes(in);
System.out.println(d.toBase64());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) try { in.close(); } catch (IOException ioe) {}
}
}
}
}

View File

@@ -11,7 +11,6 @@ package net.i2p.data;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Defines the hash as defined by the I2P data structure spec.
@@ -19,8 +18,7 @@ import java.io.OutputStream;
*
* @author jrandom
*/
public class Hash extends DataStructureImpl {
private byte[] _data;
public class Hash extends SimpleDataStructure {
private volatile String _stringified;
private volatile String _base64ed;
private int _cachedHashCode;
@@ -29,46 +27,34 @@ public class Hash extends DataStructureImpl {
public final static Hash FAKE_HASH = new Hash(new byte[HASH_LENGTH]);
public Hash() {
super();
}
/** @throws IllegalArgumentException if data is not 32 bytes (null is ok) */
public Hash(byte data[]) {
super();
setData(data);
}
public byte[] getData() {
return _data;
public int length() {
return HASH_LENGTH;
}
/** @throws IllegalArgumentException if data is not 32 bytes (null is ok) */
@Override
public void setData(byte[] data) {
if (data != null && data.length != HASH_LENGTH)
throw new IllegalArgumentException("Hash must be 32 bytes");
_data = data;
super.setData(data);
_stringified = null;
_base64ed = null;
_cachedHashCode = calcHashCode();
_cachedHashCode = super.hashCode();
}
@Override
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[HASH_LENGTH];
super.readBytes(in);
_stringified = null;
_base64ed = null;
int read = read(in, _data);
if (read != HASH_LENGTH) throw new DataFormatException("Not enough bytes to read the hash");
_cachedHashCode = calcHashCode();
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the hash to write out");
if (_data.length != HASH_LENGTH) throw new DataFormatException("Invalid size of data in the hash");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof Hash)) return false;
return DataHelper.eq(_data, ((Hash) obj)._data);
_cachedHashCode = super.hashCode();
}
/** a Hash is a hash, so just use the first 4 bytes for speed */
@@ -77,32 +63,6 @@ public class Hash extends DataStructureImpl {
return _cachedHashCode;
}
/** a Hash is a hash, so just use the first 4 bytes for speed */
private int calcHashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
@Override
public String toString() {
if (_stringified == null) {
StringBuilder buf = new StringBuilder(64);
buf.append("[Hash: ");
if (_data == null) {
buf.append("null hash");
} else {
buf.append(toBase64());
}
buf.append("]");
_stringified = buf.toString();
}
return _stringified;
}
@Override
public String toBase64() {
if (_base64ed == null) {

View File

@@ -0,0 +1,129 @@
package net.i2p.data;
/*
* free (adj.): unencumbered; not under the control of others
* Written by jrandom in 2003 and released into the public domain
* with no warranty of any kind, either expressed or implied.
* It probably won't make your computer catch on fire, or eat
* your children, but it might. Use at your own risk.
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.SHA256Generator;
/**
* KeysAndCert has a public key, a signing key, and a certificate.
* In that order.
* We also store a cached Hash.
*
* Implemented in 0.8.2 and retrofitted over Destination and RouterIdentity.
* There's actually no difference between the two of them.
*
* @since 0.8.2
* @author zzz
*/
public class KeysAndCert extends DataStructureImpl {
protected PublicKey _publicKey;
protected SigningPublicKey _signingKey;
protected Certificate _certificate;
protected Hash __calculatedHash;
public Certificate getCertificate() {
return _certificate;
}
public void setCertificate(Certificate cert) {
_certificate = cert;
__calculatedHash = null;
}
public PublicKey getPublicKey() {
return _publicKey;
}
public void setPublicKey(PublicKey key) {
_publicKey = key;
__calculatedHash = null;
}
public SigningPublicKey getSigningPublicKey() {
return _signingKey;
}
public void setSigningPublicKey(SigningPublicKey key) {
_signingKey = key;
__calculatedHash = null;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_publicKey = new PublicKey();
_publicKey.readBytes(in);
_signingKey = new SigningPublicKey();
_signingKey.readBytes(in);
_certificate = new Certificate();
_certificate.readBytes(in);
__calculatedHash = null;
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
throw new DataFormatException("Not enough data to format the router identity");
_publicKey.writeBytes(out);
_signingKey.writeBytes(out);
_certificate.writeBytes(out);
}
@Override
public boolean equals(Object object) {
if ((object == null) || !(object instanceof KeysAndCert)) return false;
KeysAndCert ident = (KeysAndCert) object;
return DataHelper.eq(_certificate, ident._certificate)
&& DataHelper.eq(_signingKey, ident._signingKey)
&& DataHelper.eq(_publicKey, ident._publicKey);
}
/** the public key has enough randomness in it to use it by itself for speed */
@Override
public int hashCode() {
if (_publicKey == null)
return 0;
return _publicKey.hashCode();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append('[').append(getClass().getSimpleName()).append(": ");
buf.append("\n\tHash: ").append(getHash().toBase64());
buf.append("\n\tCertificate: ").append(_certificate);
buf.append("\n\tPublicKey: ").append(_publicKey);
buf.append("\n\tSigningPublicKey: ").append(_signingKey);
buf.append(']');
return buf.toString();
}
@Override
public Hash calculateHash() {
return getHash();
}
public Hash getHash() {
if (__calculatedHash != null)
return __calculatedHash;
byte identBytes[];
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeBytes(baos);
identBytes = baos.toByteArray();
} catch (Throwable t) {
return null;
}
__calculatedHash = SHA256Generator.getInstance().calculateHash(identBytes);
return __calculatedHash;
}
}

View File

@@ -55,16 +55,13 @@ import net.i2p.util.RandomSource;
*
* @author jrandom
*/
public class LeaseSet extends DataStructureImpl {
public class LeaseSet extends DatabaseEntry {
private final static Log _log = new Log(LeaseSet.class);
private Destination _destination;
private PublicKey _encryptionKey;
private SigningPublicKey _signingKey;
// Keep leases in the order received, or else signature verification will fail!
private List<Lease> _leases;
private Signature _signature;
private volatile Hash _currentRoutingKey;
private volatile byte[] _routingKeyGenMod;
private boolean _receivedAsPublished;
private boolean _receivedAsReply;
// Store these since isCurrent() and getEarliestLeaseDate() are called frequently
@@ -82,6 +79,18 @@ public class LeaseSet extends DataStructureImpl {
_firstExpiration = Long.MAX_VALUE;
}
public long getDate() {
return getEarliestLeaseDate();
}
protected KeysAndCert getKeysAndCert() {
return _destination;
}
public int getType() {
return KEY_TYPE_LEASESET;
}
public Destination getDestination() {
return _destination;
}
@@ -157,42 +166,6 @@ public class LeaseSet extends DataStructureImpl {
return (Lease) _leases.get(index);
}
public Signature getSignature() {
return _signature;
}
public void setSignature(Signature sig) {
_signature = sig;
}
/**
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
* This only calculates a new one when necessary though (if the generator's key modifier changes)
*
*/
public Hash getRoutingKey() {
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
if ((gen.getModData() == null) || (_routingKeyGenMod == null)
|| (!DataHelper.eq(gen.getModData(), _routingKeyGenMod))) {
setRoutingKey(gen.getRoutingKey(getDestination().calculateHash()));
_routingKeyGenMod = gen.getModData();
}
return _currentRoutingKey;
}
public void setRoutingKey(Hash key) {
_currentRoutingKey = key;
}
public boolean validateRoutingKey() {
Hash destKey = getDestination().calculateHash();
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(destKey);
if (rk.equals(getRoutingKey()))
return true;
return false;
}
/**
* Retrieve the end date of the earliest lease include in this leaseSet.
* This is the date that should be used in comparisons for leaseSet age - to
@@ -207,27 +180,16 @@ public class LeaseSet extends DataStructureImpl {
return _firstExpiration;
}
/**
* Sign the structure using the supplied signing key
*
*/
public void sign(SigningPrivateKey key) throws DataFormatException {
byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign");
// now sign with the key
Signature sig = DSAEngine.getInstance().sign(bytes, key);
setSignature(sig);
}
/**
* Verify that the signature matches the lease set's destination's signing public key.
* OR the included revocation key.
*
* @return true only if the signature matches
*/
@Override
public boolean verifySignature() {
if (getSignature() == null) return false;
if (getDestination() == null) return false;
if (_signature == null) return false;
if (_destination == null) return false;
byte data[] = getBytes();
if (data == null) return false;
boolean signedByDest = DSAEngine.getInstance().verifySignature(_signature, data,
@@ -271,7 +233,7 @@ public class LeaseSet extends DataStructureImpl {
return _lastExpiration > now - fudge;
}
private byte[] getBytes() {
protected byte[] getBytes() {
if ((_destination == null) || (_encryptionKey == null) || (_signingKey == null) || (_leases == null))
return null;
int len = PublicKey.KEYSIZE_BYTES // dest

View File

@@ -9,10 +9,6 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.KeyGenerator;
/**
@@ -22,77 +18,28 @@ import net.i2p.crypto.KeyGenerator;
*
* @author jrandom
*/
public class PrivateKey extends DataStructureImpl {
private byte[] _data;
public class PrivateKey extends SimpleDataStructure {
public final static int KEYSIZE_BYTES = 256;
public PrivateKey() {
super();
}
public PrivateKey(byte data[]) { setData(data); }
public PrivateKey(byte data[]) {
super(data);
}
/** constructs from base64
* @param base64Data a string of base64 data (the output of .toBase64() called
* on a prior instance of PrivateKey
*/
public PrivateKey(String base64Data) throws DataFormatException {
super();
fromBase64(base64Data);
}
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES];
int read = read(in, _data);
if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the private key");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the private key to write out");
if (_data.length != KEYSIZE_BYTES)
throw new DataFormatException("Invalid size of data in the private key [" + _data.length + "]");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof PrivateKey)) return false;
return DataHelper.eq(_data, ((PrivateKey) obj)._data);
}
/** the key has enough randomness in it, use the first 4 bytes for speed */
@Override
public int hashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[PrivateKey: ");
if (_data == null) {
buf.append("null key");
} else {
buf.append("size: ").append(_data.length);
//int len = 32;
//if (len > _data.length) len = _data.length;
//buf.append(" first ").append(len).append(" bytes: ");
//buf.append(DataHelper.toString(_data, len));
}
buf.append("]");
return buf.toString();
public int length() {
return KEYSIZE_BYTES;
}
/** derives a new PublicKey object derived from the secret contents

View File

@@ -9,10 +9,6 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Defines the PublicKey as defined by the I2P data structure spec.
* A public key is 256byte Integer. The public key represents only the
@@ -20,18 +16,19 @@ import java.io.OutputStream;
*
* @author jrandom
*/
public class PublicKey extends DataStructureImpl {
private byte[] _data;
public class PublicKey extends SimpleDataStructure {
public final static int KEYSIZE_BYTES = 256;
public PublicKey() {
super();
}
/** @param data must be non-null */
public PublicKey(byte data[]) {
if ( (data == null) || (data.length != KEYSIZE_BYTES) )
throw new IllegalArgumentException("Data must be specified, and the correct size");
setData(data);
super();
if (data == null)
throw new IllegalArgumentException("Data must be specified");
_data = data;
}
/** constructs from base64
@@ -39,61 +36,11 @@ public class PublicKey extends DataStructureImpl {
* on a prior instance of PublicKey
*/
public PublicKey(String base64Data) throws DataFormatException {
super();
fromBase64(base64Data);
}
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES];
int read = read(in, _data);
if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the public key");
public int length() {
return KEYSIZE_BYTES;
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the public key to write out");
if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the public key");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof PublicKey)) return false;
return DataHelper.eq(_data, ((PublicKey) obj)._data);
}
/** the key has enough randomness in it, use the first 4 bytes for speed */
@Override
public int hashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[PublicKey: ");
if (_data == null) {
buf.append("null key");
} else {
buf.append("size: ").append(_data.length);
//int len = 32;
//if (len > _data.length) len = _data.length;
//buf.append(" first ").append(len).append(" bytes: ");
//buf.append(DataHelper.toString(_data, len));
}
buf.append("]");
return buf.toString();
}
}

View File

@@ -9,57 +9,14 @@ package net.i2p.data;
*
*/
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.SHA256Generator;
import net.i2p.util.Log;
/**
* Defines the unique identifier of a router, including any certificate or
* public key.
*
* @author jrandom
*/
public class RouterIdentity extends DataStructureImpl {
private final static Log _log = new Log(RouterIdentity.class);
private Certificate _certificate;
private SigningPublicKey _signingKey;
private PublicKey _publicKey;
private Hash __calculatedHash;
public class RouterIdentity extends KeysAndCert {
public RouterIdentity() {
}
public Certificate getCertificate() {
return _certificate;
}
public void setCertificate(Certificate cert) {
_certificate = cert;
__calculatedHash = null;
}
public PublicKey getPublicKey() {
return _publicKey;
}
public void setPublicKey(PublicKey key) {
_publicKey = key;
__calculatedHash = null;
}
public SigningPublicKey getSigningPublicKey() {
return _signingKey;
}
public void setSigningPublicKey(SigningPublicKey key) {
_signingKey = key;
__calculatedHash = null;
}
/**
* This router specified that they should not be used as a part of a tunnel,
* nor queried for the netDb, and that disclosure of their contact information
@@ -69,73 +26,4 @@ public class RouterIdentity extends DataStructureImpl {
public boolean isHidden() {
return (_certificate != null) && (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_HIDDEN);
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_publicKey = new PublicKey();
_publicKey.readBytes(in);
_signingKey = new SigningPublicKey();
_signingKey.readBytes(in);
_certificate = new Certificate();
_certificate.readBytes(in);
__calculatedHash = null;
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
throw new DataFormatException("Not enough data to format the router identity");
_publicKey.writeBytes(out);
_signingKey.writeBytes(out);
_certificate.writeBytes(out);
}
@Override
public boolean equals(Object object) {
if ((object == null) || !(object instanceof RouterIdentity)) return false;
RouterIdentity ident = (RouterIdentity) object;
return DataHelper.eq(_certificate, ident.getCertificate())
&& DataHelper.eq(_signingKey, ident.getSigningPublicKey())
&& DataHelper.eq(_publicKey, ident.getPublicKey());
}
/** the public key has enough randomness in it to use it by itself for speed */
@Override
public int hashCode() {
if (_publicKey == null)
return 0;
return _publicKey.hashCode();
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[RouterIdentity: ");
buf.append("\n\tHash: ").append(getHash().toBase64());
buf.append("\n\tCertificate: ").append(getCertificate());
buf.append("\n\tPublicKey: ").append(getPublicKey());
buf.append("\n\tSigningPublicKey: ").append(getSigningPublicKey());
buf.append("]");
return buf.toString();
}
@Override
public Hash calculateHash() {
return getHash();
}
public Hash getHash() {
if (__calculatedHash != null) {
//_log.info("Returning cached ident hash");
return __calculatedHash; }
byte identBytes[] = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
writeBytes(baos);
identBytes = baos.toByteArray();
} catch (Throwable t) {
_log.error("Error writing out hash");
return null;
}
__calculatedHash = SHA256Generator.getInstance().calculateHash(identBytes);
return __calculatedHash;
}
}

View File

@@ -34,16 +34,13 @@ import net.i2p.util.OrderedProperties;
*
* @author jrandom
*/
public class RouterInfo extends DataStructureImpl {
public class RouterInfo extends DatabaseEntry {
private final static Log _log = new Log(RouterInfo.class);
private RouterIdentity _identity;
private volatile long _published;
private final Set<RouterAddress> _addresses;
private final Set<Hash> _peers;
private /* FIXME final FIXME */ Properties _options;
private volatile Signature _signature;
private volatile Hash _currentRoutingKey;
private volatile byte _routingKeyGenMod[];
private volatile boolean _validated;
private volatile boolean _isValid;
private volatile String _stringified;
@@ -75,6 +72,18 @@ public class RouterInfo extends DataStructureImpl {
setSignature(old.getSignature());
}
public long getDate() {
return _published;
}
protected KeysAndCert getKeysAndCert() {
return _identity;
}
public int getType() {
return KEY_TYPE_ROUTERINFO;
}
private void resetCache() {
_stringified = null;
_byteified = null;
@@ -204,47 +213,13 @@ public class RouterInfo extends DataStructureImpl {
resetCache();
}
/**
* Retrieve the proof that the identity stands behind the info here
*
*/
public Signature getSignature() {
return _signature;
}
/**
* Configure the proof that the entity stands behind the info here
*
*/
public void setSignature(Signature signature) {
_signature = signature;
resetCache();
}
/**
* Sign the structure using the supplied signing key
*
*/
public synchronized void sign(SigningPrivateKey key) throws DataFormatException {
byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign");
// now sign with the key
Signature sig = DSAEngine.getInstance().sign(bytes, key);
if (sig == null) throw new DataFormatException("Not enough data to sign, or other signature failure");
setSignature(sig);
//_log.debug("Signed " + SHA256Generator.getInstance().calculateHash(bytes).toBase64() + " with " + key);
//_log.debug("verify ok? " + DSAEngine.getInstance().verifySignature(sig, bytes, getIdentity().getSigningPublicKey()));
//_log.debug("Signed data: \n" + Base64.encode(bytes));
//_log.debug("Signature: " + getSignature());
}
/**
* Write out the raw payload of the routerInfo, excluding the signature. This
* caches the data in memory if possible.
*
* @throws DataFormatException if the data is somehow b0rked (missing props, etc)
*/
private byte[] getBytes() throws DataFormatException {
protected byte[] getBytes() throws DataFormatException {
if (_byteified != null) return _byteified;
if (_identity == null) throw new DataFormatException("Router identity isn't set? wtf!");
if (_addresses == null) throw new DataFormatException("Router addressess isn't set? wtf!");
@@ -392,35 +367,6 @@ public class RouterInfo extends DataStructureImpl {
}
}
/**
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
* This only calculates a new one when necessary though (if the generator's key modifier changes)
*
*/
public synchronized Hash getRoutingKey() {
RoutingKeyGenerator gen = RoutingKeyGenerator.getInstance();
if ((gen.getModData() == null) || (_routingKeyGenMod == null)
|| (!DataHelper.eq(gen.getModData(), _routingKeyGenMod))) {
setRoutingKey(gen.getRoutingKey(_identity.getHash()));
_routingKeyGenMod = gen.getModData();
}
return _currentRoutingKey;
}
public void setRoutingKey(Hash key) {
_currentRoutingKey = key;
}
public boolean validateRoutingKey() {
Hash identKey = _identity.getHash();
Hash rk = RoutingKeyGenerator.getInstance().getRoutingKey(identKey);
if (rk.equals(getRoutingKey()))
return true;
return false;
}
/**
* Determine whether the router was published recently (within the given age milliseconds).
* The age should be large enough to take into consideration any clock fudge factor, so
@@ -453,6 +399,10 @@ public class RouterInfo extends DataStructureImpl {
return null;
}
/**
* For future multiple addresses per-transport (IPV6), currently unused
* @since 0.7.11
*/
public List<RouterAddress> getTargetAddresses(String transportStyle) {
List<RouterAddress> ret = new Vector<RouterAddress>();
synchronized(this._addresses) {

View File

@@ -9,32 +9,28 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Defines the SessionKey as defined by the I2P data structure spec.
* A session key is 32byte Integer.
*
* @author jrandom
*/
public class SessionKey extends DataStructureImpl {
private byte[] _data;
public class SessionKey extends SimpleDataStructure {
private Object _preparedKey;
public final static int KEYSIZE_BYTES = 32;
public static final SessionKey INVALID_KEY = new SessionKey(new byte[KEYSIZE_BYTES]);
public SessionKey() {
super();
}
public SessionKey(byte data[]) {
setData(data);
super(data);
}
public byte[] getData() {
return _data;
public int length() {
return KEYSIZE_BYTES;
}
/**
@@ -43,6 +39,7 @@ public class SessionKey extends DataStructureImpl {
* encryption/decryption (or if you do change it, be sure this object isn't
* mid decrypt)
*/
@Override
public void setData(byte[] data) {
_data = data;
_preparedKey = null;
@@ -54,54 +51,4 @@ public class SessionKey extends DataStructureImpl {
*/
public Object getPreparedKey() { return _preparedKey; }
public void setPreparedKey(Object obj) { _preparedKey = obj; }
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES];
int read = read(in, _data);
if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the session key");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the session key to write out");
if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the private key");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SessionKey)) return false;
return DataHelper.eq(_data, ((SessionKey) obj)._data);
}
/** the key has enough randomness in it, use the first 4 bytes for speed */
@Override
public int hashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
@Override
public String toString() {
return "SessionKey " + toBase64();
/****
if (true) return super.toString();
StringBuilder buf = new StringBuilder(64);
buf.append("[SessionKey: ");
if (_data == null) {
buf.append("null key");
} else {
buf.append("size: ").append(_data.length);
//int len = 32;
//if (len > _data.length) len = _data.length;
//buf.append(" first ").append(len).append(" bytes: ");
//buf.append(DataHelper.toString(_data, len));
}
buf.append("]");
return buf.toString();
****/
}
}

View File

@@ -9,57 +9,35 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.util.RandomSource;
public class SessionTag extends ByteArray {
/**
* 32 bytes, usually of random data.
* Changed from ByteArray to SimpleDataStructure in 0.8.2.
*/
public class SessionTag extends SimpleDataStructure {
public final static int BYTE_LENGTH = 32;
public SessionTag() {
super();
}
/**
* @param create if true, instantiate the data array and fill it with random data.
*/
public SessionTag(boolean create) {
super();
if (create) {
byte buf[] = new byte[BYTE_LENGTH];
RandomSource.getInstance().nextBytes(buf);
setData(buf);
_data = new byte[BYTE_LENGTH];
RandomSource.getInstance().nextBytes(_data);
}
}
public SessionTag(byte val[]) {
super();
setData(val);
super(val);
}
@Override
public void setData(byte val[]) throws IllegalArgumentException {
if (val == null)
throw new NullPointerException("SessionTags cannot be null");
if (val.length != BYTE_LENGTH)
throw new IllegalArgumentException("SessionTags must be " + BYTE_LENGTH + " bytes");
super.setData(val);
setValid(BYTE_LENGTH);
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
byte data[] = new byte[BYTE_LENGTH];
int read = DataHelper.read(in, data);
if (read != BYTE_LENGTH)
throw new DataFormatException("Not enough data (read " + read + " wanted " + BYTE_LENGTH + ")");
setData(data);
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
out.write(getData());
}
@Override
public String toString() {
return "SessionTag " + toBase64();
public int length() {
return BYTE_LENGTH;
}
}

View File

@@ -9,10 +9,6 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Defines the signature as defined by the I2P data structure spec.
* A signature is a 40byte Integer verifying the authenticity of some data
@@ -20,9 +16,7 @@ import java.io.OutputStream;
*
* @author jrandom
*/
public class Signature extends DataStructureImpl {
private byte[] _data;
public class Signature extends SimpleDataStructure {
public final static int SIGNATURE_BYTES = 40;
public final static byte[] FAKE_SIGNATURE = new byte[SIGNATURE_BYTES];
static {
@@ -30,61 +24,15 @@ public class Signature extends DataStructureImpl {
FAKE_SIGNATURE[i] = 0x00;
}
public Signature() {}
public Signature(byte data[]) { setData(data); }
public byte[] getData() {
return _data;
public Signature() {
super();
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[SIGNATURE_BYTES];
int read = read(in, _data);
if (read != SIGNATURE_BYTES) throw new DataFormatException("Not enough bytes to read the signature");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the signature to write out");
if (_data.length != SIGNATURE_BYTES) throw new DataFormatException("Invalid size of data in the private key");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof Signature)) return false;
return DataHelper.eq(_data, ((Signature) obj)._data);
}
/** the sig has enough randomness in it, use the first 4 bytes for speed */
@Override
public int hashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
public Signature(byte data[]) {
super(data);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[Signature: ");
if (_data == null) {
buf.append("null signature");
} else {
buf.append("size: ").append(_data.length);
//int len = 32;
//if (len > _data.length) len = _data.length;
//buf.append(" first ").append(len).append(" bytes: ");
//buf.append(DataHelper.toString(_data, len));
}
buf.append("]");
return buf.toString();
public int length() {
return SIGNATURE_BYTES;
}
}

View File

@@ -9,10 +9,6 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.KeyGenerator;
/**
@@ -23,75 +19,28 @@ import net.i2p.crypto.KeyGenerator;
*
* @author jrandom
*/
public class SigningPrivateKey extends DataStructureImpl {
private byte[] _data;
public class SigningPrivateKey extends SimpleDataStructure {
public final static int KEYSIZE_BYTES = 20;
public SigningPrivateKey() {}
public SigningPrivateKey() {
super();
}
public SigningPrivateKey(byte data[]) { setData(data); }
public SigningPrivateKey(byte data[]) {
super(data);
}
/** constructs from base64
* @param base64Data a string of base64 data (the output of .toBase64() called
* on a prior instance of SigningPrivateKey
*/
public SigningPrivateKey(String base64Data) throws DataFormatException {
super();
fromBase64(base64Data);
}
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES];
int read = read(in, _data);
if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the private key");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the private key to write out");
if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the private key");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SigningPrivateKey)) return false;
return DataHelper.eq(_data, ((SigningPrivateKey) obj)._data);
}
/** the key has enough randomness in it, use the first 4 bytes for speed */
@Override
public int hashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[SigningPrivateKey: ");
if (_data == null) {
buf.append("null key");
} else {
buf.append("size: ").append(_data.length);
//int len = 32;
//if (len > _data.length) len = _data.length;
//buf.append(" first ").append(len).append(" bytes: ");
//buf.append(DataHelper.toString(_data, len));
}
buf.append("]");
return buf.toString();
public int length() {
return KEYSIZE_BYTES;
}
/** converts this signing private key to its public equivalent

View File

@@ -9,10 +9,6 @@ package net.i2p.data;
*
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Defines the SigningPublicKey as defined by the I2P data structure spec.
* A public key is 256byte Integer. The public key represents only the
@@ -21,74 +17,27 @@ import java.io.OutputStream;
*
* @author jrandom
*/
public class SigningPublicKey extends DataStructureImpl {
private byte[] _data;
public class SigningPublicKey extends SimpleDataStructure {
public final static int KEYSIZE_BYTES = 128;
public SigningPublicKey() {}
public SigningPublicKey() {
super();
}
public SigningPublicKey(byte data[]) { setData(data); }
public SigningPublicKey(byte data[]) {
super(data);
}
/** constructs from base64
* @param base64Data a string of base64 data (the output of .toBase64() called
* on a prior instance of SigningPublicKey
*/
public SigningPublicKey(String base64Data) throws DataFormatException {
super();
fromBase64(base64Data);
}
public byte[] getData() {
return _data;
}
public void setData(byte[] data) {
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[KEYSIZE_BYTES];
int read = read(in, _data);
if (read != KEYSIZE_BYTES) throw new DataFormatException("Not enough bytes to read the public key");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data in the public key to write out");
if (_data.length != KEYSIZE_BYTES) throw new DataFormatException("Invalid size of data in the public key");
out.write(_data);
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SigningPublicKey)) return false;
return DataHelper.eq(_data, ((SigningPublicKey) obj)._data);
}
/** the key has enough randomness in it, use the first 4 bytes for speed */
@Override
public int hashCode() {
int rv = 0;
if (_data != null) {
for (int i = 0; i < 4; i++)
rv ^= (_data[i] << (i*8));
}
return rv;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[SigningPublicKey: ");
if (_data == null) {
buf.append("null key");
} else {
buf.append("size: ").append(_data.length);
//int len = 32;
//if (len > _data.length) len = _data.length;
//buf.append(" first ").append(len).append(" bytes: ");
//buf.append(DataHelper.toString(_data, len));
}
buf.append("]");
return buf.toString();
public int length() {
return KEYSIZE_BYTES;
}
}

View File

@@ -0,0 +1,155 @@
package net.i2p.data;
/*
* Public domain
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import net.i2p.crypto.SHA256Generator;
/**
* A SimpleDataStructure contains only a single fixed-length byte array.
* The main reason to do this is to override
* toByteArray() and fromByteArray(), which are used by toBase64(), fromBase64(),
* and calculateHash() in DataStructureImpl - otherwise these would go through
* a wasteful array-to-stream-to-array pass.
* It also centralizes a lot of common code.
*
* Implemented in 0.8.2 and retrofitted over several of the classes in this package.
*
* @since 0.8.2
* @author zzz
*/
public abstract class SimpleDataStructure extends DataStructureImpl {
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 from ByteArray() after this to set the data */
public SimpleDataStructure() {
_length = length();
}
/** @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) */
public SimpleDataStructure(byte data[]) {
_length = length();
setData(data);
}
/**
* The legal length of the byte array in this data structure
* @since 0.8.2
*/
abstract public int length();
/**
* Get the data reference (not a copy)
* @return the byte array, or null if unset
*/
public byte[] getData() {
return _data;
}
/**
* Sets the data.
* @param data of correct length, or null
* @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok)
*/
public void setData(byte[] data) {
if (data != null && data.length != _length)
throw new IllegalArgumentException("Bad data length");
_data = data;
}
public void readBytes(InputStream in) throws DataFormatException, IOException {
_data = new byte[_length];
int read = read(in, _data);
if (read != _length) throw new DataFormatException("Not enough bytes to read the data");
}
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_data == null) throw new DataFormatException("No data to write out");
out.write(_data);
}
@Override
public String toBase64() {
if (_data == null)
return null;
return Base64.encode(_data);
}
@Override
public void fromBase64(String data) throws DataFormatException {
if (data == null) throw new DataFormatException("Null data passed in");
_data = Base64.decode(data);
}
/** @return the SHA256 hash of the byte array, or null if the data is null */
@Override
public Hash calculateHash() {
if (_data != null) return SHA256Generator.getInstance().calculateHash(_data);
return null;
}
/**
* Overridden for efficiency.
* @return same thing as getData()
*/
@Override
public byte[] toByteArray() {
return _data;
}
/**
* Overridden for efficiency.
* Does the same thing as getData() but null not allowed.
* @param data non-null
* @throws DataFormatException if null or wrong length
*/
@Override
public void fromByteArray(byte data[]) throws DataFormatException {
if (data == null) throw new DataFormatException("Null data passed in");
if (data.length != _length) throw new DataFormatException("Bad data length");
_data = data;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append('[').append(getClass().getSimpleName()).append(": ");
if (_data == null) {
buf.append("null");
} else if (_length <= 32) {
buf.append(toBase64());
} else {
buf.append("size: ").append(Integer.toString(_length));
}
buf.append(']');
return buf.toString();
}
/**
* We assume the data has enough randomness in it, so use the first 4 bytes for speed.
* If this is not the case, override in the extending class.
*/
@Override
public int hashCode() {
if (_data == null)
return 0;
int rv = _data[0];
for (int i = 1; i < 4; i++)
rv ^= (_data[i] << (i*8));
return rv;
}
@Override
public boolean equals(Object obj) {
if ((obj == null) || !(obj instanceof SimpleDataStructure)) return false;
return DataHelper.eq(_data, ((SimpleDataStructure) obj)._data);
}
}