propagate from branch 'i2p.i2p' (head 60a9a2297abeaf042645e3f0bc8d106f1ff585bf)

to branch 'i2p.i2p.zzz.test2' (head 6ff6f0bcee835d32aad62449a37f5171afde915a)
This commit is contained in:
zzz
2014-09-13 14:50:11 +00:00
158 changed files with 2982 additions and 947 deletions

View File

@ -0,0 +1,135 @@
package net.i2p.crypto;
import java.math.BigInteger;
import java.security.spec.ECField;
import java.security.spec.ECFieldFp;
import java.security.spec.ECPoint;
import java.security.spec.EllipticCurve;
import net.i2p.util.NativeBigInteger;
/**
* Used by KeyGenerator.getSigningPublicKey()
*
* Modified from
* http://stackoverflow.com/questions/15727147/scalar-multiplication-of-point-over-elliptic-curve
* Apparently public domain.
* Supported P-192 only.
* Added curve parameters to support all curves.
*
* @since 0.9.16
*/
class ECUtil {
private static final BigInteger TWO = new BigInteger("2");
private static final BigInteger THREE = new BigInteger("3");
public static ECPoint scalarMult(ECPoint p, BigInteger kin, EllipticCurve curve) {
ECPoint r = ECPoint.POINT_INFINITY;
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
BigInteger k = kin.mod(prime);
int length = k.bitLength();
byte[] binarray = new byte[length];
for (int i = 0; i <= length-1; i++) {
binarray[i] = k.mod(TWO).byteValue();
k = k.divide(TWO);
}
for (int i = length-1; i >= 0; i--) {
// i should start at length-1 not -2 because the MSB of binarry may not be 1
r = doublePoint(r, curve);
if (binarray[i] == 1)
r = addPoint(r, p, curve);
}
return r;
}
private static ECPoint addPoint(ECPoint r, ECPoint s, EllipticCurve curve) {
if (r.equals(s))
return doublePoint(r, curve);
else if (r.equals(ECPoint.POINT_INFINITY))
return s;
else if (s.equals(ECPoint.POINT_INFINITY))
return r;
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
BigInteger slope = (r.getAffineY().subtract(s.getAffineY())).multiply(r.getAffineX().subtract(s.getAffineX()).modInverse(prime)).mod(prime);
slope = new NativeBigInteger(slope);
BigInteger xOut = (slope.modPow(TWO, prime).subtract(r.getAffineX())).subtract(s.getAffineX()).mod(prime);
BigInteger yOut = s.getAffineY().negate().mod(prime);
yOut = yOut.add(slope.multiply(s.getAffineX().subtract(xOut))).mod(prime);
ECPoint out = new ECPoint(xOut, yOut);
return out;
}
private static ECPoint doublePoint(ECPoint r, EllipticCurve curve) {
if (r.equals(ECPoint.POINT_INFINITY))
return r;
BigInteger slope = (r.getAffineX().pow(2)).multiply(THREE);
slope = slope.add(curve.getA());
BigInteger prime = ((ECFieldFp) curve.getField()).getP();
slope = slope.multiply((r.getAffineY().multiply(TWO)).modInverse(prime));
BigInteger xOut = slope.pow(2).subtract(r.getAffineX().multiply(TWO)).mod(prime);
BigInteger yOut = (r.getAffineY().negate()).add(slope.multiply(r.getAffineX().subtract(xOut))).mod(prime);
ECPoint out = new ECPoint(xOut, yOut);
return out;
}
/**
* P-192 test only.
* See KeyGenerator.main() for a test of all supported curves.
*/
/****
public static void main(String[] args) {
EllipticCurve P192 = ECConstants.P192_SPEC.getCurve();
BigInteger xs = new BigInteger("d458e7d127ae671b0c330266d246769353a012073e97acf8", 16);
BigInteger ys = new BigInteger("325930500d851f336bddc050cf7fb11b5673a1645086df3b", 16);
BigInteger xt = new BigInteger("f22c4395213e9ebe67ddecdd87fdbd01be16fb059b9753a4", 16);
BigInteger yt = new BigInteger("264424096af2b3597796db48f8dfb41fa9cecc97691a9c79", 16);
ECPoint S = new ECPoint(xs,ys);
ECPoint T = new ECPoint(xt,yt);
// Verifying addition
ECPoint Rst = addPoint(S, T, P192);
BigInteger xst = new BigInteger("48e1e4096b9b8e5ca9d0f1f077b8abf58e843894de4d0290", 16); // Specified value of x of point R for addition in NIST Routine example
System.out.println("x-coordinate of point Rst is : " + Rst.getAffineX());
System.out.println("y-coordinate of point Rst is : " + Rst.getAffineY());
if (Rst.getAffineX().equals(xst))
System.out.println("Adding is correct");
else
System.out.println("Adding FAIL");
//Verifying Doubling
BigInteger xr = new BigInteger("30c5bc6b8c7da25354b373dc14dd8a0eba42d25a3f6e6962", 16); // Specified value of x of point R for doubling in NIST Routine example
BigInteger yr = new BigInteger("0dde14bc4249a721c407aedbf011e2ddbbcb2968c9d889cf", 16);
ECPoint R2s = new ECPoint(xr, yr); // Specified value of y of point R for doubling in NIST Routine example
System.out.println("x-coordinate of point R2s is : " + R2s.getAffineX());
System.out.println("y-coordinate of point R2s is : " + R2s.getAffineY());
System.out.println("x-coordinate of calculated point is : " + doublePoint(S, P192).getAffineX());
System.out.println("y-coordinate of calculated point is : " + doublePoint(S, P192).getAffineY());
if (R2s.getAffineX().equals(doublePoint(S, P192).getAffineX()) &&
R2s.getAffineY().equals(doublePoint(S, P192).getAffineY()))
System.out.println("Doubling is correct");
else
System.out.println("Doubling FAIL");
xr = new BigInteger("1faee4205a4f669d2d0a8f25e3bcec9a62a6952965bf6d31", 16); // Specified value of x of point R for scalar Multiplication in NIST Routine example
yr = new BigInteger("5ff2cdfa508a2581892367087c696f179e7a4d7e8260fb06", 16); // Specified value of y of point R for scalar Multiplication in NIST Routine example
ECPoint Rds = new ECPoint(xr, yr);
BigInteger d = new BigInteger("a78a236d60baec0c5dd41b33a542463a8255391af64c74ee", 16);
ECPoint Rs = scalarMult(S, d, P192);
System.out.println("x-coordinate of point Rds is : " + Rds.getAffineX());
System.out.println("y-coordinate of point Rds is : " + Rds.getAffineY());
System.out.println("x-coordinate of calculated point is : " + Rs.getAffineX());
System.out.println("y-coordinate of calculated point is : " + Rs.getAffineY());
if (Rds.getAffineX().equals(Rs.getAffineX()) &&
Rds.getAffineY().equals(Rs.getAffineY()))
System.out.println("Scalar Multiplication is correct");
else
System.out.println("Scalar Multiplication FAIL");
}
****/
}

View File

@ -12,11 +12,25 @@ package net.i2p.crypto;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.ProviderException;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.EllipticCurve;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.RSAPublicKeySpec;
import net.i2p.I2PAppContext;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import net.i2p.data.Hash;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
@ -268,24 +282,56 @@ public class KeyGenerator {
}
/** Convert a SigningPrivateKey to a SigningPublicKey.
* DSA-SHA1 only.
* As of 0.9.16, supports all key types.
*
* @param priv a SigningPrivateKey object
* @return a SigningPublicKey object
* @throws IllegalArgumentException on bad key
* @throws IllegalArgumentException on bad key or unknown type
*/
public static SigningPublicKey getSigningPublicKey(SigningPrivateKey priv) {
if (priv.getType() != SigType.DSA_SHA1)
throw new IllegalArgumentException();
BigInteger x = new NativeBigInteger(1, priv.toByteArray());
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
SigningPublicKey pub = new SigningPublicKey();
SigType type = priv.getType();
if (type == null)
throw new IllegalArgumentException("Unknown type");
try {
pub.setData(SigUtil.rectify(y, SigningPublicKey.KEYSIZE_BYTES));
} catch (InvalidKeyException ike) {
throw new IllegalArgumentException(ike);
switch (type.getBaseAlgorithm()) {
case DSA:
BigInteger x = new NativeBigInteger(1, priv.toByteArray());
BigInteger y = CryptoConstants.dsag.modPow(x, CryptoConstants.dsap);
SigningPublicKey pub = new SigningPublicKey();
pub.setData(SigUtil.rectify(y, SigningPublicKey.KEYSIZE_BYTES));
return pub;
case EC:
ECPrivateKey ecpriv = SigUtil.toJavaECKey(priv);
BigInteger s = ecpriv.getS();
ECParameterSpec spec = (ECParameterSpec) type.getParams();
EllipticCurve curve = spec.getCurve();
ECPoint g = spec.getGenerator();
ECPoint w = ECUtil.scalarMult(g, s, curve);
ECPublicKeySpec ecks = new ECPublicKeySpec(w, ecpriv.getParams());
KeyFactory eckf = KeyFactory.getInstance("EC");
ECPublicKey ecpub = (ECPublicKey) eckf.generatePublic(ecks);
return SigUtil.fromJavaKey(ecpub, type);
case RSA:
RSAPrivateKey rsapriv = SigUtil.toJavaRSAKey(priv);
BigInteger exp = ((RSAKeyGenParameterSpec)type.getParams()).getPublicExponent();
RSAPublicKeySpec rsaks = new RSAPublicKeySpec(rsapriv.getModulus(), exp);
KeyFactory rsakf = KeyFactory.getInstance("RSA");
RSAPublicKey rsapub = (RSAPublicKey) rsakf.generatePublic(rsaks);
return SigUtil.fromJavaKey(rsapub, type);
case EdDSA:
EdDSAPrivateKey epriv = SigUtil.toJavaEdDSAKey(priv);
EdDSAPublicKey epub = new EdDSAPublicKey(new EdDSAPublicKeySpec(epriv.getA(), epriv.getParams()));
return SigUtil.fromJavaKey(epub, type);
default:
throw new IllegalArgumentException("Unsupported algorithm");
}
} catch (GeneralSecurityException gse) {
throw new IllegalArgumentException("Conversion failed", gse);
}
return pub;
}
public static void main(String args[]) {
@ -322,14 +368,20 @@ public class KeyGenerator {
long stime = 0;
long vtime = 0;
SimpleDataStructure keys[] = KeyGenerator.getInstance().generateSigningKeys(type);
//System.out.println("pubkey " + keys[0]);
SigningPublicKey pubkey = (SigningPublicKey) keys[0];
SigningPrivateKey privkey = (SigningPrivateKey) keys[1];
SigningPublicKey pubkey2 = getSigningPublicKey(privkey);
if (pubkey.equals(pubkey2))
System.out.println(type + " private-to-public test PASSED");
else
System.out.println(type + " private-to-public test FAILED");
//System.out.println("privkey " + keys[1]);
for (int i = 0; i < runs; i++) {
RandomSource.getInstance().nextBytes(src);
long start = System.nanoTime();
Signature sig = DSAEngine.getInstance().sign(src, (SigningPrivateKey) keys[1]);
Signature sig = DSAEngine.getInstance().sign(src, privkey);
long mid = System.nanoTime();
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, (SigningPublicKey) keys[0]);
boolean ok = DSAEngine.getInstance().verifySignature(sig, src, pubkey);
long end = System.nanoTime();
stime += mid - start;
vtime += end - mid;

View File

@ -331,7 +331,7 @@ public class SigUtil {
}
/**
* @deprecated unused
*
*/
public static RSAPrivateKey toJavaRSAKey(SigningPrivateKey pk)
throws GeneralSecurityException {
@ -344,7 +344,7 @@ public class SigUtil {
}
/**
* @deprecated unused
*
*/
public static SigningPublicKey fromJavaKey(RSAPublicKey pk, SigType type)
throws GeneralSecurityException {

View File

@ -24,20 +24,15 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
@ -638,13 +633,17 @@ public class DataHelper {
* Integers are a fixed number of bytes (numBytes), stored as unsigned integers in network byte order.
* @param value value to write out, non-negative
* @param rawStream stream to write to
* @param numBytes number of bytes to write the number into (padding as necessary)
* @throws DataFormatException if value is negative
* @param numBytes number of bytes to write the number into, 1-8 (padding as necessary)
* @throws DataFormatException if value is negative or if numBytes not 1-8
* @throws IOException if there is an IO error writing to the stream
*/
public static void writeLong(OutputStream rawStream, int numBytes, long value)
throws DataFormatException, IOException {
if (value < 0) throw new DataFormatException("Value is negative (" + value + ")");
if (numBytes <= 0 || numBytes > 8)
// probably got the args backwards
throw new DataFormatException("Bad byte count " + numBytes);
if (value < 0)
throw new DataFormatException("Value is negative (" + value + ")");
for (int i = (numBytes - 1) * 8; i >= 0; i -= 8) {
byte cur = (byte) (value >> i);
rawStream.write(cur);
@ -1425,58 +1424,6 @@ public class DataHelper {
out.write(data);
}
/**
* Sort based on the Hash of the DataStructure.
* Warning - relatively slow.
* WARNING - this sort order must be consistent network-wide, so while the order is arbitrary,
* it cannot be changed.
* Why? Just because it has to be consistent so signing will work.
* How to spec as returning the same type as the param?
* DEPRECATED - Only used by RouterInfo.
*
* @return a new list
*/
public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) {
if (dataStructures == null) return Collections.emptyList();
// This used to use Hash.toString(), which is insane, since a change to toString()
// would break the whole network. Now use Hash.toBase64().
// Note that the Base64 sort order is NOT the same as the raw byte sort order,
// despite what you may read elsewhere.
//ArrayList<DataStructure> rv = new ArrayList(dataStructures.size());
//TreeMap<String, DataStructure> tm = new TreeMap();
//for (DataStructure struct : dataStructures) {
// tm.put(struct.calculateHash().toString(), struct);
//}
//for (DataStructure struct : tm.values()) {
// rv.add(struct);
//}
ArrayList<DataStructure> rv = new ArrayList<DataStructure>(dataStructures);
sortStructureList(rv);
return rv;
}
/**
* See above.
* DEPRECATED - Only used by RouterInfo.
*
* @since 0.9
*/
static void sortStructureList(List<? extends DataStructure> dataStructures) {
Collections.sort(dataStructures, new DataStructureComparator());
}
/**
* See sortStructures() comments.
* @since 0.8.3
*/
private static class DataStructureComparator implements Comparator<DataStructure>, Serializable {
public int compare(DataStructure l, DataStructure r) {
return l.calculateHash().toBase64().compareTo(r.calculateHash().toBase64());
}
}
/**
* NOTE: formatDuration2() recommended in most cases for readability
*/

View File

@ -77,6 +77,13 @@ public class KeysAndCert extends DataStructureImpl {
_signingKey = key;
}
/**
* @since 0.9.16
*/
public byte[] getPadding() {
return _padding;
}
/**
* @throws IllegalStateException if was already set
* @since 0.9.12
@ -114,6 +121,8 @@ public class KeysAndCert extends DataStructureImpl {
_publicKey.writeBytes(out);
if (_padding != null)
out.write(_padding);
else if (_signingKey.length() < SigningPublicKey.KEYSIZE_BYTES)
throw new DataFormatException("No padding set");
_signingKey.writeTruncatedBytes(out);
_certificate.writeBytes(out);
}

View File

@ -50,6 +50,7 @@ public class PrivateKey extends SimpleDataStructure {
/** derives a new PublicKey object derived from the secret contents
* of this PrivateKey
* @return a PublicKey object
* @throws IllegalArgumentException on bad key
*/
public PublicKey toPublic() {
return KeyGenerator.getPublicKey(this);

View File

@ -1,11 +1,13 @@
package net.i2p.data;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
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.GeneralSecurityException;
import java.util.Locale;
import java.util.Map;
@ -24,6 +26,7 @@ import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SigType;
import net.i2p.util.RandomSource;
import net.i2p.util.SecureFileOutputStream;
/**
* This helper class reads and writes files in the
@ -48,11 +51,11 @@ public class PrivateKeyFile {
private static final int HASH_EFFORT = VerifiedDestination.MIN_HASHCASH_EFFORT;
private final File file;
protected final File file;
private final I2PClient client;
private Destination dest;
private PrivateKey privKey;
private SigningPrivateKey signingPrivKey;
protected Destination dest;
protected PrivateKey privKey;
protected SigningPrivateKey signingPrivKey;
/**
* Create a new PrivateKeyFile, or modify an existing one, with various
@ -224,6 +227,16 @@ public class PrivateKeyFile {
*/
public PrivateKeyFile(File file, PublicKey pubkey, SigningPublicKey spubkey, Certificate cert,
PrivateKey pk, SigningPrivateKey spk) {
this(file, pubkey, spubkey, cert, pk, spk, null);
}
/**
* @param padding null OK, must be non-null if spubkey length < 128
* @throws IllegalArgumentException on mismatch of spubkey and spk types
* @since 0.9.16
*/
public PrivateKeyFile(File file, PublicKey pubkey, SigningPublicKey spubkey, Certificate cert,
PrivateKey pk, SigningPrivateKey spk, byte[] padding) {
if (spubkey.getType() != spk.getType())
throw new IllegalArgumentException("Signing key type mismatch");
this.file = file;
@ -232,6 +245,8 @@ public class PrivateKeyFile {
this.dest.setPublicKey(pubkey);
this.dest.setSigningPublicKey(spubkey);
this.dest.setCertificate(cert);
if (padding != null)
this.dest.setPadding(padding);
this.privKey = pk;
this.signingPrivKey = spk;
}
@ -241,9 +256,9 @@ public class PrivateKeyFile {
*/
public Destination createIfAbsent() throws I2PException, IOException, DataFormatException {
if(!this.file.exists()) {
FileOutputStream out = null;
OutputStream out = null;
try {
out = new FileOutputStream(this.file);
out = new SecureFileOutputStream(this.file);
if (this.client != null)
this.client.createDestination(out);
else
@ -257,7 +272,10 @@ public class PrivateKeyFile {
return getDestination();
}
/** Also sets the local privKey and signingPrivKey */
/**
* If the destination is not set, read it in from the file.
* Also sets the local privKey and signingPrivKey.
*/
public Destination getDestination() throws I2PSessionException, IOException, DataFormatException {
if (dest == null) {
I2PSession s = open();
@ -408,9 +426,9 @@ public class PrivateKeyFile {
}
public I2PSession open(Properties opts) throws I2PSessionException, IOException {
FileInputStream in = null;
InputStream in = null;
try {
in = new FileInputStream(this.file);
in = new BufferedInputStream(new FileInputStream(this.file));
I2PSession s = this.client.createSession(in, opts);
return s;
} finally {
@ -424,13 +442,12 @@ public class PrivateKeyFile {
* Copied from I2PClientImpl.createDestination()
*/
public void write() throws IOException, DataFormatException {
FileOutputStream out = null;
OutputStream out = null;
try {
out = new FileOutputStream(this.file);
out = new SecureFileOutputStream(this.file);
this.dest.writeBytes(out);
this.privKey.writeBytes(out);
this.signingPrivKey.writeBytes(out);
out.flush();
} finally {
if (out != null) {
try { out.close(); } catch (IOException ioe) {}
@ -438,6 +455,23 @@ public class PrivateKeyFile {
}
}
/**
* Verify that the PublicKey matches the PrivateKey, and
* the SigningPublicKey matches the SigningPrivateKey.
*
* @return success
* @since 0.9.16
*/
public boolean validateKeyPairs() {
try {
if (!dest.getPublicKey().equals(KeyGenerator.getPublicKey(privKey)))
return false;
return dest.getSigningPublicKey().equals(KeyGenerator.getSigningPublicKey(signingPrivKey));
} catch (IllegalArgumentException iae) {
return false;
}
}
@Override
public String toString() {
StringBuilder s = new StringBuilder(128);

View File

@ -1,353 +0,0 @@
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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
import net.i2p.util.Addresses;
import net.i2p.util.OrderedProperties;
/**
* Defines a method of communicating with a router
*
* For efficiency, the options methods and structures here are unsynchronized.
* Initialize the structure with readBytes(), or call the setOptions().
* Don't change it after that.
*
* To ensure integrity of the RouterInfo, methods that change an element of the
* RouterInfo will throw an IllegalStateException after the RouterInfo is signed.
*
* As of 0.9.3, expiration MUST be all zeros as it is ignored on
* readin and the signature will fail.
* If we implement expiration, or other use for the field, we must allow
* several releases for the change to propagate as it is backwards-incompatible.
* Restored as of 0.9.12.
*
* @author jrandom
*/
public class RouterAddress extends DataStructureImpl {
private short _cost;
private long _expiration;
private String _transportStyle;
private final Properties _options;
// cached values
private byte[] _ip;
private int _port;
public static final String PROP_HOST = "host";
public static final String PROP_PORT = "port";
public RouterAddress() {
_options = new OrderedProperties();
}
/**
* For efficiency when created by a Transport.
* @param options not copied; do not reuse or modify
* @param cost 0-255
* @since IPv6
*/
public RouterAddress(String style, OrderedProperties options, int cost) {
_transportStyle = style;
_options = options;
if (cost < 0 || cost > 255)
throw new IllegalArgumentException();
_cost = (short) cost;
}
/**
* Retrieve the weighted cost of this address, relative to other methods of
* contacting this router. The value 0 means free and 255 means really expensive.
* No value above 255 is allowed.
*
* Unused before 0.7.12
* @return 0-255
*/
public int getCost() {
return _cost;
}
/**
* Configure the weighted cost of using the address.
* No value negative or above 255 is allowed.
*
* WARNING - do not change cost on a published address or it will break the RI sig.
* There is no check here.
* Rarely used, use 3-arg constructor.
*
* NTCP is set to 10 and SSU to 5 by default, unused before 0.7.12
*/
public void setCost(int cost) {
if (cost < 0 || cost > 255)
throw new IllegalArgumentException();
_cost = (short) cost;
}
/**
* Retrieve the date after which the address should not be used. If this
* is null, then the address never expires.
* As of 0.9.3, expiration MUST be all zeros as it is ignored on
* readin and the signature will fail.
* Restored as of 0.9.12.
*
* @deprecated unused for now
* @return null for never, or a Date
*/
public Date getExpiration() {
//return _expiration;
if (_expiration > 0)
return new Date(_expiration);
return null;
}
/**
* Retrieve the date after which the address should not be used. If this
* is zero, then the address never expires.
*
* @deprecated unused for now
* @return 0 for never
* @since 0.9.12
*/
public long getExpirationTime() {
return _expiration;
}
/**
* Configure the expiration date of the address (null for no expiration)
* As of 0.9.3, expiration MUST be all zeros as it is ignored on
* readin and the signature will fail.
* Restored as of 0.9.12, wait several more releases before using.
* TODO: Use for introducers
*
* Unused for now, always null
* @deprecated unused for now
*/
public void setExpiration(Date expiration) {
_expiration = expiration.getDate();
}
/**
* Retrieve the type of transport that must be used to communicate on this address.
*
*/
public String getTransportStyle() {
return _transportStyle;
}
/**
* Configure the type of transport that must be used to communicate on this address
*
* @throws IllegalStateException if was already set
* @deprecated unused, use 3-arg constructor
*/
public void setTransportStyle(String transportStyle) {
if (_transportStyle != null)
throw new IllegalStateException();
_transportStyle = transportStyle;
}
/**
* Retrieve the transport specific options necessary for communication
*
* @deprecated use getOptionsMap()
* @return sorted, non-null, NOT a copy, do not modify
*/
public Properties getOptions() {
return _options;
}
/**
* Retrieve the transport specific options necessary for communication
*
* @return an unmodifiable view, non-null, sorted
* @since 0.8.13
*/
public Map<Object, Object> getOptionsMap() {
return Collections.unmodifiableMap(_options);
}
/**
* @since 0.8.13
*/
public String getOption(String opt) {
return _options.getProperty(opt);
}
/**
* Specify the transport specific options necessary for communication.
* Makes a copy.
* @param options non-null
* @throws IllegalStateException if was already set
* @deprecated unused, use 3-arg constructor
*/
public void setOptions(Properties options) {
if (!_options.isEmpty())
throw new IllegalStateException();
_options.putAll(options);
}
/**
* Caching version of InetAddress.getByName(getOption("host")).getAddress(), which is slow.
* Caches numeric host names only.
* Will resolve but not cache resolution of DNS host names.
*
* @return IP or null
* @since 0.9.3
*/
public byte[] getIP() {
if (_ip != null)
return _ip;
byte[] rv = null;
String host = getHost();
if (host != null) {
rv = Addresses.getIP(host);
if (rv != null &&
(host.replaceAll("[0-9\\.]", "").length() == 0 ||
host.replaceAll("[0-9a-fA-F:]", "").length() == 0)) {
_ip = rv;
}
}
return rv;
}
/**
* Convenience, same as getOption("host").
* Does no parsing, so faster than getIP().
*
* @return host string or null
* @since IPv6
*/
public String getHost() {
return _options.getProperty(PROP_HOST);
}
/**
* Caching version of Integer.parseInt(getOption("port"))
* Caches valid ports 1-65535 only.
*
* @return 1-65535 or 0 if invalid
* @since 0.9.3
*/
public int getPort() {
if (_port != 0)
return _port;
String port = _options.getProperty(PROP_PORT);
if (port != null) {
try {
int rv = Integer.parseInt(port);
if (rv > 0 && rv <= 65535)
_port = rv;
} catch (NumberFormatException nfe) {}
}
return _port;
}
/**
* As of 0.9.3, expiration MUST be all zeros as it is ignored on
* readin and the signature will fail.
* Restored as of 0.9.12, wait several more releases before using.
* @throws IllegalStateException if was already read in
*/
public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_transportStyle != null)
throw new IllegalStateException();
_cost = (short) DataHelper.readLong(in, 1);
_expiration = DataHelper.readLong(in, 8);
_transportStyle = DataHelper.readString(in);
// reduce Object proliferation
if (_transportStyle.equals("SSU"))
_transportStyle = "SSU";
else if (_transportStyle.equals("NTCP"))
_transportStyle = "NTCP";
DataHelper.readProperties(in, _options);
}
/**
* As of 0.9.3, expiration MUST be all zeros as it is ignored on
* readin and the signature will fail.
*/
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_transportStyle == null)
throw new DataFormatException("uninitialized");
DataHelper.writeLong(out, 1, _cost);
DataHelper.writeLong(out, 8, _expiration);
DataHelper.writeString(out, _transportStyle);
DataHelper.writeProperties(out, _options);
}
/**
* Transport, host, and port only.
* Never look at cost or other properties.
*/
@Override
public boolean equals(Object object) {
if (object == this) return true;
if ((object == null) || !(object instanceof RouterAddress)) return false;
RouterAddress addr = (RouterAddress) object;
return
getPort() == addr.getPort() &&
DataHelper.eq(getHost(), addr.getHost()) &&
DataHelper.eq(_transportStyle, addr._transportStyle);
//DataHelper.eq(_options, addr._options) &&
//DataHelper.eq(_expiration, addr._expiration);
}
/**
* Everything, including Transport, host, port, options, and cost
* @param addr may be null
* @since IPv6
*/
public boolean deepEquals(RouterAddress addr) {
return
equals(addr) &&
_cost == addr._cost &&
_options.equals(addr._options);
}
/**
* Just use a few items for speed (expiration is always null).
* Never look at cost or other properties.
*/
@Override
public int hashCode() {
return DataHelper.hashCode(_transportStyle) ^
DataHelper.hashCode(getIP()) ^
getPort();
}
/**
* This is used on peers.jsp so sort options so it looks better.
* We don't just use OrderedProperties for _options because DataHelper.writeProperties()
* sorts also.
*/
@Override
public String toString() {
StringBuilder buf = new StringBuilder(128);
buf.append("[RouterAddress: ");
buf.append("\n\tType: ").append(_transportStyle);
buf.append("\n\tCost: ").append(_cost);
if (_expiration > 0)
buf.append("\n\tExpiration: ").append(new Date(_expiration));
buf.append("\n\tOptions (").append(_options.size()).append("):");
for (Map.Entry<Object, Object> e : _options.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
}
buf.append("]");
return buf.toString();
}
}

View File

@ -1,37 +0,0 @@
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.
*
*/
/**
* Defines the unique identifier of a router, including any certificate or
* public key.
*
* As of 0.9.9 this data structure is immutable after the two keys and the certificate
* are set; attempts to change them will throw an IllegalStateException.
*
* @author jrandom
*/
public class RouterIdentity extends KeysAndCert {
/**
* 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
* should be limited.
*
*/
public boolean isHidden() {
return (_certificate != null) && (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_HIDDEN);
}
@Override
public boolean equals(Object o) {
return super.equals(o) && (o instanceof RouterIdentity);
}
}

View File

@ -1,699 +0,0 @@
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 java.security.DigestInputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.SHA1;
import net.i2p.crypto.SHA1Hash;
import net.i2p.crypto.SHA256Generator;
import net.i2p.crypto.SigType;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
import net.i2p.util.SystemVersion;
/**
* Defines the data that a router either publishes to the global routing table or
* provides to trusted peers.
*
* For efficiency, the methods and structures here are now unsynchronized.
* Initialize the RI with readBytes(), or call the setters and then sign() in a single thread.
* Don't change it after that.
*
* To ensure integrity of the RouterInfo, methods that change an element of the
* RouterInfo will throw an IllegalStateException after the RouterInfo is signed.
*
* @author jrandom
*/
public class RouterInfo extends DatabaseEntry {
private RouterIdentity _identity;
private volatile long _published;
/**
* Addresses must be sorted by SHA256.
* When an RI is created, they are sorted in setAddresses().
* Save addresses in the order received so we need not resort.
*/
private final List<RouterAddress> _addresses;
/** may be null to save memory, no longer final */
private Set<Hash> _peers;
private final Properties _options;
private volatile boolean _validated;
private volatile boolean _isValid;
//private volatile String _stringified;
private volatile byte _byteified[];
private volatile int _hashCode;
private volatile boolean _hashCodeInitialized;
/** should we cache the byte and string versions _byteified ? **/
private boolean _shouldCache;
/** maybe we should check if we are floodfill? */
private static final boolean CACHE_ALL = SystemVersion.getMaxMemory() > 128*1024*1024l;
public static final String PROP_NETWORK_ID = "netId";
public static final String PROP_CAPABILITIES = "caps";
public static final char CAPABILITY_HIDDEN = 'H';
// Public string of chars which serve as bandwidth capacity markers
// NOTE: individual chars defined in Router.java
public static final String BW_CAPABILITY_CHARS = "KLMNO";
public RouterInfo() {
_addresses = new ArrayList<RouterAddress>(2);
_options = new OrderedProperties();
}
/**
* Used only by Router and PublishLocalRouterInfoJob.
* Copies ONLY the identity and peers.
* Does not copy published, addresses, options, or signature.
*/
public RouterInfo(RouterInfo old) {
this();
setIdentity(old.getIdentity());
//setPublished(old.getPublished());
//setAddresses(old.getAddresses());
setPeers(old.getPeers());
//setOptions(old.getOptions());
//setSignature(old.getSignature());
// copy over _byteified?
}
public long getDate() {
return _published;
}
protected KeysAndCert getKeysAndCert() {
return _identity;
}
public int getType() {
return KEY_TYPE_ROUTERINFO;
}
/**
* Retrieve the identity of the router represented
*
*/
public RouterIdentity getIdentity() {
return _identity;
}
/**
* Configure the identity of the router represented
*
* @throws IllegalStateException if RouterInfo is already signed
*/
public void setIdentity(RouterIdentity ident) {
if (_signature != null)
throw new IllegalStateException();
_identity = ident;
// We only want to cache the bytes for our own RI, which is frequently written.
// To cache for all RIs doubles the RI memory usage.
// setIdentity() is only called when we are creating our own RI.
// Otherwise, the data is populated with readBytes().
_shouldCache = true;
}
/**
* Retrieve the approximate date on which the info was published
* (essentially a version number for the routerInfo structure, except that
* it also contains freshness information - whether or not the router is
* currently publishing its information). This should be used to help expire
* old routerInfo structures
*
*/
public long getPublished() {
return _published;
}
/**
* Date on which it was published, in milliseconds since Midnight GMT on Jan 01, 1970
*
* @throws IllegalStateException if RouterInfo is already signed
*/
public void setPublished(long published) {
if (_signature != null)
throw new IllegalStateException();
_published = published;
}
/**
* Retrieve the set of RouterAddress structures at which this
* router can be contacted.
*
* @return unmodifiable view, non-null
*/
public Collection<RouterAddress> getAddresses() {
return Collections.unmodifiableCollection(_addresses);
}
/**
* Specify a set of RouterAddress structures at which this router
* can be contacted.
*
* Warning - Sorts the addresses here. Do not modify any address
* after calling this, as the sort order is based on the
* hash of the entire address structure.
*
* @param addresses may be null
* @throws IllegalStateException if RouterInfo is already signed or addresses previously set
*/
public void setAddresses(Collection<RouterAddress> addresses) {
if (_signature != null || !_addresses.isEmpty())
throw new IllegalStateException();
if (addresses != null) {
_addresses.addAll(addresses);
if (_addresses.size() > 1) {
// WARNING this sort algorithm cannot be changed, as it must be consistent
// network-wide. The signature is not checked at readin time, but only
// later, and the addresses are stored in a Set, not a List.
DataHelper.sortStructureList(_addresses);
}
}
}
/**
* Retrieve a set of SHA-256 hashes of RouterIdentities from routers
* this router can be reached through.
*
* @deprecated Implemented here but unused elsewhere
*/
public Set<Hash> getPeers() {
if (_peers == null)
return Collections.emptySet();
return _peers;
}
/**
* Specify a set of SHA-256 hashes of RouterIdentities from routers
* this router can be reached through.
*
* @deprecated Implemented here but unused elsewhere
* @throws IllegalStateException if RouterInfo is already signed
*/
public void setPeers(Set<Hash> peers) {
if (_signature != null)
throw new IllegalStateException();
if (peers == null || peers.isEmpty()) {
_peers = null;
return;
}
if (_peers == null)
_peers = new HashSet<Hash>(2);
synchronized (_peers) {
_peers.clear();
_peers.addAll(peers);
}
}
/**
* Retrieve a set of options or statistics that the router can expose.
*
* @deprecated use getOptionsMap()
* @return sorted, non-null, NOT a copy, do not modify!!!
*/
public Properties getOptions() {
return _options;
}
/**
* Retrieve a set of options or statistics that the router can expose.
*
* @return an unmodifiable view, non-null, sorted
* @since 0.8.13
*/
public Map<Object, Object> getOptionsMap() {
return Collections.unmodifiableMap(_options);
}
public String getOption(String opt) {
return _options.getProperty(opt);
}
/**
* Configure a set of options or statistics that the router can expose.
* Makes a copy.
*
* @param options if null, clears current options
* @throws IllegalStateException if RouterInfo is already signed
*/
public void setOptions(Properties options) {
if (_signature != null)
throw new IllegalStateException();
_options.clear();
if (options != null)
_options.putAll(options);
}
/**
* 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)
*/
protected byte[] getBytes() throws DataFormatException {
if (_byteified != null) return _byteified;
if (_identity == null) throw new DataFormatException("Router identity isn't set? wtf!");
//long before = Clock.getInstance().now();
ByteArrayOutputStream out = new ByteArrayOutputStream(2*1024);
try {
_identity.writeBytes(out);
// avoid thrashing objects
//DataHelper.writeDate(out, new Date(_published));
DataHelper.writeLong(out, 8, _published);
int sz = _addresses.size();
if (sz <= 0 || isHidden()) {
// Do not send IP address to peers in hidden mode
DataHelper.writeLong(out, 1, 0);
} else {
DataHelper.writeLong(out, 1, sz);
for (RouterAddress addr : _addresses) {
addr.writeBytes(out);
}
}
// XXX: what about peers?
// answer: they're always empty... they're a placeholder for one particular
// method of trusted links, which isn't implemented in the router
// at the moment, and may not be later.
int psz = _peers == null ? 0 : _peers.size();
DataHelper.writeLong(out, 1, psz);
if (psz > 0) {
Collection<Hash> peers = _peers;
if (psz > 1)
// WARNING this sort algorithm cannot be changed, as it must be consistent
// network-wide. The signature is not checked at readin time, but only
// later, and the hashes are stored in a Set, not a List.
peers = (Collection<Hash>) DataHelper.sortStructures(peers);
for (Hash peerHash : peers) {
peerHash.writeBytes(out);
}
}
DataHelper.writeProperties(out, _options);
} catch (IOException ioe) {
throw new DataFormatException("IO Error getting bytes", ioe);
}
byte data[] = out.toByteArray();
//if (_log.shouldLog(Log.DEBUG)) {
// long after = Clock.getInstance().now();
// _log.debug("getBytes() took " + (after - before) + "ms");
//}
if (CACHE_ALL || _shouldCache)
_byteified = data;
return data;
}
/**
* Determine whether this router info is authorized with a valid signature
*
*/
public boolean isValid() {
if (!_validated) doValidate();
return _isValid;
}
/**
* Same as isValid()
* @since 0.9
*/
@Override
public boolean verifySignature() {
return isValid();
}
/**
* which network is this routerInfo a part of. configured through the property
* PROP_NETWORK_ID
* @return -1 if unknown
*/
public int getNetworkId() {
String id = _options.getProperty(PROP_NETWORK_ID);
if (id != null) {
try {
return Integer.parseInt(id);
} catch (NumberFormatException nfe) {}
}
return -1;
}
/**
* what special capabilities this router offers
* @return non-null, empty string if none
*/
public String getCapabilities() {
String capabilities = _options.getProperty(PROP_CAPABILITIES);
if (capabilities != null)
return capabilities;
else
return "";
}
/**
* Is this a hidden node?
*/
public boolean isHidden() {
return (getCapabilities().indexOf(CAPABILITY_HIDDEN) != -1);
}
/**
* Return a string representation of this node's bandwidth tier,
* or "Unknown"
*/
public String getBandwidthTier() {
String bwTiers = BW_CAPABILITY_CHARS;
String bwTier = "Unknown";
String capabilities = getCapabilities();
// Iterate through capabilities, searching for known bandwidth tier
for (int i = 0; i < capabilities.length(); i++) {
if (bwTiers.indexOf(String.valueOf(capabilities.charAt(i))) != -1) {
bwTier = String.valueOf(capabilities.charAt(i));
break;
}
}
return (bwTier);
}
/**
* @throws IllegalStateException if RouterInfo is already signed
*/
public void addCapability(char cap) {
if (_signature != null)
throw new IllegalStateException();
String caps = _options.getProperty(PROP_CAPABILITIES);
if (caps == null)
_options.setProperty(PROP_CAPABILITIES, ""+cap);
else if (caps.indexOf(cap) == -1)
_options.setProperty(PROP_CAPABILITIES, caps + cap);
}
/**
* @throws IllegalStateException if RouterInfo is already signed
*/
public void delCapability(char cap) {
if (_signature != null)
throw new IllegalStateException();
String caps = _options.getProperty(PROP_CAPABILITIES);
int idx;
if (caps == null) {
return;
} else if ((idx = caps.indexOf(cap)) == -1) {
return;
} else {
StringBuilder buf = new StringBuilder(caps);
while ( (idx = buf.indexOf(""+cap)) != -1)
buf.deleteCharAt(idx);
_options.setProperty(PROP_CAPABILITIES, buf.toString());
}
}
/**
* 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
* values such as 1 or 2 hours are probably reasonable.
*
* @param maxAgeMs milliseconds between the current time and publish date to check
* @return true if it was published recently, false otherwise
*/
public boolean isCurrent(long maxAgeMs) {
long earliestExpire = Clock.getInstance().now() - maxAgeMs;
if (_published < earliestExpire)
return false;
return true;
}
/**
* Pull the first workable target address for the given transport.
* Use to check for any address. For all addresses, use getTargetAddresses(),
* which you probably want if you care about IPv6.
*/
public RouterAddress getTargetAddress(String transportStyle) {
for (RouterAddress addr : _addresses) {
if (addr.getTransportStyle().equals(transportStyle))
return addr;
}
return null;
}
/**
* For multiple addresses per-transport (IPv4 or IPv6)
* @return non-null
* @since 0.7.11
*/
public List<RouterAddress> getTargetAddresses(String transportStyle) {
List<RouterAddress> ret = new ArrayList<RouterAddress>(_addresses.size());
for (RouterAddress addr : _addresses) {
if(addr.getTransportStyle().equals(transportStyle))
ret.add(addr);
}
return ret;
}
/**
* Actually validate the signature
*/
private void doValidate() {
_isValid = super.verifySignature();
_validated = true;
if (!_isValid) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(RouterInfo.class);
byte data[] = null;
try {
data = getBytes();
} catch (DataFormatException dfe) {
log.error("Error validating", dfe);
return;
}
log.error("Invalid [" + SHA256Generator.getInstance().calculateHash(data).toBase64()
+ (log.shouldLog(Log.WARN) ? ("]\n" + toString()) : ""),
new Exception("Signature failed"));
}
}
/**
* This does NOT validate the signature
*
* @throws IllegalStateException if RouterInfo was already read in
*/
public void readBytes(InputStream in) throws DataFormatException, IOException {
readBytes(in, false);
}
/**
* If verifySig is true,
* this validates the signature while reading in,
* and throws a DataFormatException if the sig is invalid.
* This is faster than reserializing to validate later.
*
* @throws IllegalStateException if RouterInfo was already read in
* @since 0.9
*/
public void readBytes(InputStream in, boolean verifySig) throws DataFormatException, IOException {
if (_signature != null)
throw new IllegalStateException();
_identity = new RouterIdentity();
_identity.readBytes(in);
// can't set the digest until we know the sig type
InputStream din;
MessageDigest digest;
if (verifySig) {
SigType type = _identity.getSigningPublicKey().getType();
if (type != SigType.EdDSA_SHA512_Ed25519) {
// This won't work for EdDSA
digest = _identity.getSigningPublicKey().getType().getDigestInstance();
// TODO any better way?
digest.update(_identity.toByteArray());
din = new DigestInputStream(in, digest);
} else {
digest = null;
din = in;
}
} else {
digest = null;
din = in;
}
// avoid thrashing objects
//Date when = DataHelper.readDate(in);
//if (when == null)
// _published = 0;
//else
// _published = when.getTime();
_published = DataHelper.readLong(din, 8);
int numAddresses = (int) DataHelper.readLong(din, 1);
for (int i = 0; i < numAddresses; i++) {
RouterAddress address = new RouterAddress();
address.readBytes(din);
_addresses.add(address);
}
int numPeers = (int) DataHelper.readLong(din, 1);
if (numPeers == 0) {
_peers = null;
} else {
_peers = new HashSet<Hash>(numPeers);
for (int i = 0; i < numPeers; i++) {
Hash peerIdentityHash = new Hash();
peerIdentityHash.readBytes(din);
_peers.add(peerIdentityHash);
}
}
DataHelper.readProperties(din, _options);
_signature = new Signature(_identity.getSigningPublicKey().getType());
_signature.readBytes(in);
if (verifySig) {
SigType type = _identity.getSigningPublicKey().getType();
if (type != SigType.EdDSA_SHA512_Ed25519) {
// This won't work for EdDSA
SimpleDataStructure hash = _identity.getSigningPublicKey().getType().getHashInstance();
hash.setData(digest.digest());
_isValid = DSAEngine.getInstance().verifySignature(_signature, hash, _identity.getSigningPublicKey());
_validated = true;
} else {
doValidate();
}
if (!_isValid) {
throw new DataFormatException("Bad sig");
}
}
//_log.debug("Read routerInfo: " + toString());
}
/**
* This does NOT validate the signature
*/
public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_identity == null) throw new DataFormatException("Missing identity");
if (_published < 0) throw new DataFormatException("Invalid published date: " + _published);
if (_signature == null) throw new DataFormatException("Signature is null");
//if (!isValid())
// throw new DataFormatException("Data is not valid");
ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
baos.write(getBytes());
_signature.writeBytes(baos);
byte data[] = baos.toByteArray();
//_log.debug("Writing routerInfo [len=" + data.length + "]: " + toString());
out.write(data);
}
@Override
public boolean equals(Object object) {
if (object == this) return true;
if ((object == null) || !(object instanceof RouterInfo)) return false;
RouterInfo info = (RouterInfo) object;
return
_published == info.getPublished()
&& DataHelper.eq(_signature, info.getSignature())
&& DataHelper.eq(_identity, info.getIdentity());
// Let's speed up the NetDB
//&& DataHelper.eq(_addresses, info.getAddresses())
//&& DataHelper.eq(_options, info.getOptions())
//&& DataHelper.eq(getPeers(), info.getPeers());
}
@Override
public int hashCode() {
if (!_hashCodeInitialized) {
_hashCode = DataHelper.hashCode(_identity) + (int) _published;
_hashCodeInitialized = true;
}
return _hashCode;
}
@Override
public String toString() {
//if (_stringified != null) return _stringified;
StringBuilder buf = new StringBuilder(1024);
buf.append("[RouterInfo: ");
buf.append("\n\tIdentity: ").append(_identity);
buf.append("\n\tSignature: ").append(_signature);
buf.append("\n\tPublished: ").append(new Date(_published));
if (_peers != null) {
buf.append("\n\tPeers (").append(_peers.size()).append("):");
for (Hash hash : _peers) {
buf.append("\n\t\tPeer hash: ").append(hash);
}
}
buf.append("\n\tOptions (").append(_options.size()).append("):");
for (Map.Entry<Object, Object> e : _options.entrySet()) {
String key = (String) e.getKey();
String val = (String) e.getValue();
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
}
if (!_addresses.isEmpty()) {
buf.append("\n\tAddresses (").append(_addresses.size()).append("):");
for (RouterAddress addr : _addresses) {
buf.append("\n\t").append(addr);
}
}
buf.append("]");
String rv = buf.toString();
//_stringified = rv;
return rv;
}
/**
* Print out routerinfos from files specified on the command line.
* Exits 1 if any RI is invalid, fails signature, etc.
* @since 0.8
*/
public static void main(String[] args) {
if (args.length <= 0) {
System.err.println("Usage: RouterInfo file ...");
System.exit(1);
}
boolean fail = false;
for (int i = 0; i < args.length; i++) {
RouterInfo ri = new RouterInfo();
InputStream is = null;
try {
is = new java.io.FileInputStream(args[i]);
ri.readBytes(is);
if (ri.isValid()) {
System.out.println(ri.toString());
} else {
System.err.println("Router info " + args[i] + " is invalid");
fail = true;
}
} catch (Exception e) {
System.err.println("Error reading " + args[i] + ": " + e);
fail = true;
} finally {
if (is != null) {
try { is.close(); } catch (IOException ioe) {}
}
}
}
if (fail)
System.exit(1);
}
}

View File

@ -75,8 +75,12 @@ public class SigningPrivateKey extends SimpleDataStructure {
return _type;
}
/** converts this signing private key to its public equivalent
* @return a SigningPublicKey object derived from this private key
/**
* Converts this signing private key to its public equivalent.
* As of 0.9.16, supports all key types.
*
* @return a SigningPublicKey object derived from this private key
* @throws IllegalArgumentException on bad key or unknown or unsupported type
*/
public SigningPublicKey toPublic() {
return KeyGenerator.getSigningPublicKey(this);