diff --git a/core/java/src/net/i2p/data/LeaseSet.java b/core/java/src/net/i2p/data/LeaseSet.java index 0287c5a45f..18aa3609bf 100644 --- a/core/java/src/net/i2p/data/LeaseSet.java +++ b/core/java/src/net/i2p/data/LeaseSet.java @@ -187,7 +187,8 @@ public class LeaseSet extends DatabaseEntry { public void addLease(Lease lease) { if (lease == null) throw new IllegalArgumentException("erm, null lease"); if (lease.getGateway() == null) throw new IllegalArgumentException("erm, lease has no gateway"); - if (lease.getTunnelId() == null) throw new IllegalArgumentException("erm, lease has no tunnel"); + if (getType() != KEY_TYPE_META_LS2 && lease.getTunnelId() == null) + throw new IllegalArgumentException("erm, lease has no tunnel"); if (_signature != null) throw new IllegalStateException(); if (_leases.size() >= MAX_LEASES) diff --git a/core/java/src/net/i2p/data/LeaseSet2.java b/core/java/src/net/i2p/data/LeaseSet2.java index e62b2ebad3..ef8541b2e1 100644 --- a/core/java/src/net/i2p/data/LeaseSet2.java +++ b/core/java/src/net/i2p/data/LeaseSet2.java @@ -324,8 +324,10 @@ public class LeaseSet2 extends LeaseSet { _destination.writeBytes(out); if (_published <= 0) _published = Clock.getInstance().now(); - DataHelper.writeLong(out, 4, _published / 1000); - DataHelper.writeLong(out, 2, (_expires - _published) / 1000); + long pub1k = _published / 1000; + DataHelper.writeLong(out, 4, pub1k); + // Divide separately to prevent rounding errors + DataHelper.writeLong(out, 2, ((_expires / 1000) - pub1k)); DataHelper.writeLong(out, 2, _flags); if (isOffline()) writeOfflineBytes(out); @@ -359,11 +361,14 @@ public class LeaseSet2 extends LeaseSet { @Override public int size() { int rv = _destination.size() - + _encryptionKey.length() - + 11 + + 10 + (_leases.size() * 40); + for (PublicKey key : getEncryptionKeys()) { + rv += 4; + rv += key.length(); + } if (isOffline()) - rv += 2 + _transientSigningPublicKey.length() + _offlineSignature.length(); + rv += 6 + _transientSigningPublicKey.length() + _offlineSignature.length(); if (_options != null && !_options.isEmpty()) { try { rv += DataHelper.toProperties(_options).length; @@ -382,7 +387,7 @@ public class LeaseSet2 extends LeaseSet { */ @Override public void addLease(Lease lease) { - if (!(lease instanceof Lease2)) + if (getType() == KEY_TYPE_LS2 && !(lease instanceof Lease2)) throw new IllegalArgumentException(); super.addLease(lease); _expires = _lastExpiration; @@ -590,6 +595,8 @@ public class LeaseSet2 extends LeaseSet { ls2.writeBytes(out2); out2.close(); java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(out.toByteArray()); + System.out.println("Size calculated: " + (ls2.size() + ls2.getSignature().length())); + System.out.println("Size to read in: " + in.available()); LeaseSet2 ls3 = new LeaseSet2(); ls3.readBytes(in); System.out.println("Read back: " + ls3); diff --git a/core/java/src/net/i2p/data/MetaLease.java b/core/java/src/net/i2p/data/MetaLease.java index 17997322ed..3c85a5b44f 100644 --- a/core/java/src/net/i2p/data/MetaLease.java +++ b/core/java/src/net/i2p/data/MetaLease.java @@ -79,4 +79,15 @@ public class MetaLease extends Lease { return (int) _end.getTime() ^ DataHelper.hashCode(_gateway) ^ _cost; } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(128); + buf.append("[Meta Lease: "); + buf.append("\n\tEnd Date: ").append(_end); + buf.append("\n\tTarget: ").append(_gateway); + buf.append("\n\tCost: ").append(_cost); + buf.append("]"); + return buf.toString(); + } } diff --git a/core/java/src/net/i2p/data/MetaLeaseSet.java b/core/java/src/net/i2p/data/MetaLeaseSet.java index eec7dca1f1..24b4ba7eee 100644 --- a/core/java/src/net/i2p/data/MetaLeaseSet.java +++ b/core/java/src/net/i2p/data/MetaLeaseSet.java @@ -3,6 +3,7 @@ package net.i2p.data; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Map; import net.i2p.crypto.SigType; @@ -24,6 +25,22 @@ public class MetaLeaseSet extends LeaseSet2 { return KEY_TYPE_META_LS2; } + /** + * @throws UnsupportedOperationException always + */ + @Override + public void setEncryptionKey(PublicKey key) { + throw new UnsupportedOperationException(); + } + + /** + * @throws UnsupportedOperationException always + */ + @Override + public void addEncryptionKey(PublicKey key) { + throw new UnsupportedOperationException(); + } + /** * This does NOT validate the signature * @@ -64,7 +81,7 @@ public class MetaLeaseSet extends LeaseSet2 { */ @Override protected void writeBytesWithoutSig(OutputStream out) throws DataFormatException, IOException { - if (_destination == null || _encryptionKey == null) + if (_destination == null) throw new DataFormatException("Not enough data to write out a LeaseSet"); // LS2 header writeHeader(out); @@ -88,10 +105,10 @@ public class MetaLeaseSet extends LeaseSet2 { @Override public int size() { int rv = _destination.size() - + 2 + + 10 + (_leases.size() * 40); if (isOffline()) - rv += 2 + _transientSigningPublicKey.length() + _offlineSignature.length(); + rv += 6 + _transientSigningPublicKey.length() + _offlineSignature.length(); if (_options != null && !_options.isEmpty()) { try { rv += DataHelper.toProperties(_options).length; @@ -101,6 +118,8 @@ public class MetaLeaseSet extends LeaseSet2 { } else { rv += 2; } + // revocations TODO + // rv += 32 * numRevocations return rv; } @@ -124,16 +143,13 @@ public class MetaLeaseSet extends LeaseSet2 { return DataHelper.eq(_signature, ls.getSignature()) && DataHelper.eq(_leases, ls._leases) - && DataHelper.eq(getEncryptionKey(), ls.getEncryptionKey()) && DataHelper.eq(_destination, ls.getDestination()); } /** the destination has enough randomness in it to use it by itself for speed */ @Override public int hashCode() { - if (_destination == null) - return 0; - return _destination.hashCode(); + return super.hashCode(); } @Override @@ -147,6 +163,13 @@ public class MetaLeaseSet extends LeaseSet2 { buf.append("\n\tOffline Signature: ").append(_offlineSignature); } buf.append("\n\tOptions: ").append((_options != null) ? _options.size() : 0); + if (_options != null && !_options.isEmpty()) { + for (Map.Entry 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("\n\tSignature: ").append(_signature); buf.append("\n\tPublished: ").append(new java.util.Date(_published)); buf.append("\n\tExpires: ").append(new java.util.Date(_expires)); @@ -156,4 +179,78 @@ public class MetaLeaseSet extends LeaseSet2 { buf.append("]"); return buf.toString(); } + +/**** + public static void main(String args[]) throws Exception { + if (args.length != 1) { + System.out.println("Usage: MetaLeaseSet privatekeyfile.dat"); + System.exit(1); + } + java.io.File f = new java.io.File(args[0]); + PrivateKeyFile pkf = new PrivateKeyFile(f); + pkf.createIfAbsent(SigType.EdDSA_SHA512_Ed25519); + System.out.println("Online test"); + java.io.File f2 = new java.io.File("online-metals2.dat"); + test(pkf, f2, false); + System.out.println("Offline test"); + f2 = new java.io.File("offline-metals2.dat"); + test(pkf, f2, true); + } + + private static void test(PrivateKeyFile pkf, java.io.File outfile, boolean offline) throws Exception { + net.i2p.util.RandomSource rand = net.i2p.util.RandomSource.getInstance(); + long now = System.currentTimeMillis() + 5*60*1000; + MetaLeaseSet ls2 = new MetaLeaseSet(); + for (int i = 0; i < 3; i++) { + MetaLease l2 = new MetaLease(); + now += 10000; + l2.setEndDate(new java.util.Date(now)); + byte[] gw = new byte[32]; + rand.nextBytes(gw); + l2.setGateway(new Hash(gw)); + l2.setCost(i * 5); + ls2.addLease(l2); + } + java.util.Properties opts = new java.util.Properties(); + opts.setProperty("foo", "bar"); + opts.setProperty("test", "bazzle"); + ls2.setOptions(opts); + ls2.setDestination(pkf.getDestination()); + SigningPrivateKey spk = pkf.getSigningPrivKey(); + if (offline) { + now += 365*24*60*60*1000L; + SimpleDataStructure transKeys[] = net.i2p.crypto.KeyGenerator.getInstance().generateSigningKeys(SigType.EdDSA_SHA512_Ed25519); + SigningPublicKey transientPub = (SigningPublicKey) transKeys[0]; + SigningPrivateKey transientPriv = (SigningPrivateKey) transKeys[1]; + Signature sig = offlineSign(now, transientPub, spk); + ls2.setOfflineSignature(now, transientPub, sig); + ls2.sign(transientPriv); + } else { + ls2.sign(spk); + } + System.out.println("Created: " + ls2); + if (!ls2.verifySignature()) + System.out.println("Verify FAILED"); + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + ls2.writeBytes(out); + java.io.OutputStream out2 = new java.io.FileOutputStream(outfile); + ls2.writeBytes(out2); + out2.close(); + java.io.ByteArrayInputStream in = new java.io.ByteArrayInputStream(out.toByteArray()); + System.out.println("Size calculated: " + (ls2.size() + ls2.getSignature().length())); + System.out.println("Size to read in: " + in.available()); + LeaseSet2 ls3 = new MetaLeaseSet(); + ls3.readBytes(in); + System.out.println("Read back: " + ls3); + if (!ls3.verifySignature()) { + System.out.println("Verify FAILED"); + System.out.println("Wrote out"); + byte[] b2 = ls2.toByteArray(); + System.out.println(net.i2p.util.HexDump.dump(b2)); + System.out.println("Read in"); + byte[] b3 = ls3.toByteArray(); + System.out.println(net.i2p.util.HexDump.dump(b3)); + } + } +****/ }