From 7bb7677604a6efaba550573b8daad77b1c6908b6 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 27 Mar 2019 12:26:05 +0000 Subject: [PATCH 01/14] SelfSignedGenerator: improve previous fix --- core/java/src/net/i2p/crypto/SelfSignedGenerator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/java/src/net/i2p/crypto/SelfSignedGenerator.java b/core/java/src/net/i2p/crypto/SelfSignedGenerator.java index 9af4be49c2..e45bd9be86 100644 --- a/core/java/src/net/i2p/crypto/SelfSignedGenerator.java +++ b/core/java/src/net/i2p/crypto/SelfSignedGenerator.java @@ -249,7 +249,8 @@ public final class SelfSignedGenerator { cert.verify(cpub); if (!cpub.equals(jpub)) { boolean ok = false; - if (cpub.getClass().getName().equals("sun.security.x509.X509Key")) { + if ((jpub instanceof EdDSAPublicKey) && + cpub.getClass().getName().equals("sun.security.x509.X509Key")) { // X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained // key is valid but needs to be instanced as an EdDSAPublicKey before it can be used. try { From de9d968b763cb25cde305a2e8688e3751a47c611 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 27 Mar 2019 12:32:23 +0000 Subject: [PATCH 02/14] DatabaseEntry: Change from volatile to synched --- core/java/src/net/i2p/data/DatabaseEntry.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/java/src/net/i2p/data/DatabaseEntry.java b/core/java/src/net/i2p/data/DatabaseEntry.java index 7a3fadd97c..77638daa0a 100644 --- a/core/java/src/net/i2p/data/DatabaseEntry.java +++ b/core/java/src/net/i2p/data/DatabaseEntry.java @@ -59,8 +59,9 @@ public abstract class DatabaseEntry extends DataStructureImpl { public final static int KEY_TYPE_SERVICE_LIST = 11; protected volatile Signature _signature; - protected volatile Hash _currentRoutingKey; - protected volatile long _routingKeyGenMod; + // synch: this + private Hash _currentRoutingKey; + private long _routingKeyGenMod; /** * A common interface to the timestamp of the two subclasses. @@ -153,11 +154,13 @@ public abstract class DatabaseEntry extends DataStructureImpl { throw new IllegalStateException("Not in router context"); RoutingKeyGenerator gen = ctx.routingKeyGenerator(); long mod = gen.getLastChanged(); - if (mod != _routingKeyGenMod) { - _currentRoutingKey = gen.getRoutingKey(getHash()); - _routingKeyGenMod = mod; + synchronized(this) { + if (mod != _routingKeyGenMod) { + _currentRoutingKey = gen.getRoutingKey(getHash()); + _routingKeyGenMod = mod; + } + return _currentRoutingKey; } - return _currentRoutingKey; } /** From 14492d72693a299170c6f8976ac103c5e007f072 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 27 Mar 2019 12:38:40 +0000 Subject: [PATCH 03/14] log fix --- .../java/src/net/i2p/router/message/SendMessageDirectJob.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/java/src/net/i2p/router/message/SendMessageDirectJob.java b/router/java/src/net/i2p/router/message/SendMessageDirectJob.java index 6a4871e6c4..83e1890d83 100644 --- a/router/java/src/net/i2p/router/message/SendMessageDirectJob.java +++ b/router/java/src/net/i2p/router/message/SendMessageDirectJob.java @@ -76,7 +76,7 @@ public class SendMessageDirectJob extends JobImpl { _targetHash = toPeer; if (timeoutMs < 10*1000) { if (_log.shouldLog(Log.WARN)) - _log.warn("Very little time given [" + timeoutMs + "], resetting to 5s", new Exception("stingy caller!")); + _log.warn("Very little time given [" + timeoutMs + "], resetting to 10s", new Exception("stingy caller!")); _expiration = ctx.clock().now() + 10*1000; } else { _expiration = timeoutMs + ctx.clock().now(); From 82eea0a8f9c94359a631cf9804686d1691a61e9d Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 27 Mar 2019 12:51:10 +0000 Subject: [PATCH 04/14] NetDB: Cache blinding data for lookups and decryption (proposal #123) --- .../i2p/i2ptunnel/I2PTunnelHTTPClient.java | 2 +- core/java/src/net/i2p/data/BlindData.java | 36 ++- .../src/net/i2p/data/EncryptedLeaseSet.java | 47 ++-- history.txt | 10 + .../net/i2p/router/NetworkDatabaseFacade.java | 17 ++ .../src/net/i2p/router/RouterVersion.java | 2 +- .../net/i2p/router/client/LookupDestJob.java | 38 ++- .../router/networkdb/kademlia/BlindCache.java | 218 ++++++++++++++++++ .../KademliaNetworkDatabaseFacade.java | 77 ++++++- 9 files changed, 427 insertions(+), 20 deletions(-) create mode 100644 router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 3ad564c515..65d1eca00f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -1247,7 +1247,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN); } else if(ahelperPresent) { header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN); - } else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) { + } else if(destination.length() >= 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) { header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN); extraMessage = _t("Destination lease set not found"); } else { diff --git a/core/java/src/net/i2p/data/BlindData.java b/core/java/src/net/i2p/data/BlindData.java index 0d3eed59ce..3e613fd5ba 100644 --- a/core/java/src/net/i2p/data/BlindData.java +++ b/core/java/src/net/i2p/data/BlindData.java @@ -25,6 +25,7 @@ public class BlindData { private long _routingKeyGenMod; /** + * @param secret may be null or zero-length * @throws IllegalArgumentException on various errors */ public BlindData(I2PAppContext ctx, Destination dest, SigType blindType, String secret) { @@ -33,6 +34,7 @@ public class BlindData { } /** + * @param secret may be null or zero-length * @throws IllegalArgumentException on various errors */ public BlindData(I2PAppContext ctx, SigningPublicKey spk, SigType blindType, String secret) { @@ -47,13 +49,27 @@ public class BlindData { } /** - * @return The blinded key for the current day + * @return The unblinded SPK, non-null + */ + public SigningPublicKey getUnblindedPubKey() { + return _clearSPK; + } + + /** + * @return The blinded key for the current day, non-null */ public synchronized SigningPublicKey getBlindedPubKey() { calculate(); return _blindSPK; } + /** + * @return The hash of the destination if known, or null + */ + public synchronized Hash getDestHash() { + return _dest != null ? _dest.getHash() : null; + } + /** * @return The hash of the blinded key for the current day */ @@ -127,4 +143,22 @@ public class BlindData { System.arraycopy(_blindSPK.getData(), 0, hashData, 2, _blindSPK.length()); _blindHash = _context.sha().calculateHash(hashData); } + + @Override + public synchronized String toString() { + calculate(); + StringBuilder buf = new StringBuilder(256); + buf.append("[BlindData: "); + buf.append("\n\tSigningPublicKey: ").append(_clearSPK); + buf.append("\n\tAlpha : ").append(_alpha); + buf.append("\n\tBlindedPublicKey: ").append(_blindSPK); + buf.append("\n\tBlinded Hash : ").append(_blindHash); + buf.append("\n\tSecret: ").append(_secret); + if (_dest != null) + buf.append("\n\tDestination: ").append(_dest); + if (_dest != null) + buf.append("\n\tDestination: unknown"); + buf.append(']'); + return buf.toString(); + } } diff --git a/core/java/src/net/i2p/data/EncryptedLeaseSet.java b/core/java/src/net/i2p/data/EncryptedLeaseSet.java index 2610edb8aa..8b6ae045de 100644 --- a/core/java/src/net/i2p/data/EncryptedLeaseSet.java +++ b/core/java/src/net/i2p/data/EncryptedLeaseSet.java @@ -33,6 +33,8 @@ public class EncryptedLeaseSet extends LeaseSet2 { private LeaseSet2 _decryptedLS2; private Hash __calculatedHash; private SigningPrivateKey _alpha; + // to decrypt with if we don't have full dest + private SigningPublicKey _unblindedSPK; private String _secret; private final Log _log; @@ -120,11 +122,31 @@ public class EncryptedLeaseSet extends LeaseSet2 { _destination = dest; } SigningPublicKey spk = dest.getSigningPublicKey(); + setSigningKey(spk); + } + + /** + * Overridden to set the blinded key + * + * @param spk unblinded key non-null, must be EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519 + * @throws IllegalStateException if already signed + * @throws IllegalArgumentException if not EdDSA + * @since 0.9.40 + */ + @Override + public void setSigningKey(SigningPublicKey spk) { + // TODO already-set checks SigType type = spk.getType(); if (type != SigType.EdDSA_SHA512_Ed25519 && type != SigType.RedDSA_SHA512_Ed25519) throw new IllegalArgumentException(); - SigningPublicKey bpk = blind(); + if (_unblindedSPK != null) { + if (!_unblindedSPK.equals(spk)) + throw new IllegalArgumentException("unblinded pubkey mismatch"); + } else { + _unblindedSPK = spk; + } + SigningPublicKey bpk = blind(spk); if (_signingKey == null) _signingKey = bpk; else if (!_signingKey.equals(bpk)) @@ -139,13 +161,12 @@ public class EncryptedLeaseSet extends LeaseSet2 { * * @since 0.9.39 */ - private SigningPublicKey blind() { - SigningPublicKey spk = _destination.getSigningPublicKey(); + private SigningPublicKey blind(SigningPublicKey spk) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); if (_published <= 0) - _alpha = Blinding.generateAlpha(ctx, _destination.getSigningPublicKey(), _secret); + _alpha = Blinding.generateAlpha(ctx, spk, _secret); else - _alpha = Blinding.generateAlpha(ctx, _destination.getSigningPublicKey(), _secret, _published); + _alpha = Blinding.generateAlpha(ctx, spk, _secret, _published); SigningPublicKey rv = Blinding.blind(spk, _alpha); if (_log.shouldDebug()) _log.debug("Blind:" + @@ -476,14 +497,13 @@ public class EncryptedLeaseSet extends LeaseSet2 { * @since 0.9.39 */ private byte[] getSubcredential(I2PAppContext ctx) { - if (_destination == null) - throw new IllegalStateException("no known destination to decrypt with"); - SigningPublicKey destspk = _destination.getSigningPublicKey(); - int spklen = destspk.length(); + if (_unblindedSPK == null) + throw new IllegalStateException("no known SPK to decrypt with"); + int spklen = _unblindedSPK.length(); byte[] in = new byte[spklen + 4]; // SHA256("credential" || spk || sigtypein || sigtypeout) - System.arraycopy(destspk.getData(), 0, in, 0, spklen); - DataHelper.toLong(in, spklen, 2, destspk.getType().getCode()); + System.arraycopy(_unblindedSPK.getData(), 0, in, 0, spklen); + DataHelper.toLong(in, spklen, 2, _unblindedSPK.getType().getCode()); DataHelper.toLong(in, spklen + 2, 2, SigType.RedDSA_SHA512_Ed25519.getCode()); byte[] credential = hash(ctx, CREDENTIAL, in); byte[] spk = _signingKey.getData(); @@ -572,8 +592,9 @@ public class EncryptedLeaseSet extends LeaseSet2 { return false; } _log.info("ELS2 outer sig verify success"); - if (_destination == null) { - _log.warn("ELS2 no dest to decrypt with"); + if (_unblindedSPK == null) { + if (_log.shouldWarn()) + _log.warn("ELS2 no dest/SPK to decrypt with", new Exception("I did it")); return true; } try { diff --git a/history.txt b/history.txt index 1ba070f22c..d11b960537 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,13 @@ +2019-03-27 zzz + * NetDB: Cache blinding data for lookups and decryption (proposal #123) + +2019-03-23 zzz + * Data: Preliminary work on new b32 format (proposal #149) + * SelfSignedGenerator: + - Fix generation with Ed25519ph keys (ticket #2465) + - Increase serial number from 63 to 71 bits + * SusiDNS: Add import feature (ticket #2447) + 2019-03-22 zzz * i2ptunnel: Escape {} in URLs (ticket #2130) diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java index 610859f1ab..c2287207cd 100644 --- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java @@ -13,10 +13,12 @@ import java.io.Writer; import java.util.Collections; import java.util.Set; +import net.i2p.data.BlindData; import net.i2p.data.DatabaseEntry; import net.i2p.data.Destination; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; +import net.i2p.data.SigningPublicKey; import net.i2p.data.router.RouterInfo; import net.i2p.router.networkdb.reseed.ReseedChecker; @@ -161,4 +163,19 @@ public abstract class NetworkDatabaseFacade implements Service { * @since 0.9.16 */ public boolean isNegativeCachedForever(Hash key) { return false; } + + /** + * @param spk unblinded key + * @return BlindData or null + * @since 0.9.40 + */ + public BlindData getBlindData(SigningPublicKey spk) { + return null; + } + + /** + * @param bd new BlindData to put in the cache + * @since 0.9.40 + */ + public void setBlindData(BlindData bd) {} } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 87e5bffca0..282c18b422 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 1; + public final static long BUILD = 2; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/client/LookupDestJob.java b/router/java/src/net/i2p/router/client/LookupDestJob.java index b6a1cd939f..e03758ab94 100644 --- a/router/java/src/net/i2p/router/client/LookupDestJob.java +++ b/router/java/src/net/i2p/router/client/LookupDestJob.java @@ -9,9 +9,12 @@ import java.util.Locale; import net.i2p.crypto.Blinding; import net.i2p.data.Base32; import net.i2p.data.BlindData; +import net.i2p.data.DatabaseEntry; import net.i2p.data.Destination; +import net.i2p.data.EncryptedLeaseSet; import net.i2p.data.Hash; import net.i2p.data.LeaseSet; +import net.i2p.data.SigningPublicKey; import net.i2p.data.i2cp.DestReplyMessage; import net.i2p.data.i2cp.HostReplyMessage; import net.i2p.data.i2cp.I2CPMessage; @@ -34,6 +37,7 @@ class LookupDestJob extends JobImpl { private final String _name; private final SessionId _sessID; private final Hash _fromLocalDest; + private final BlindData _blindData; private static final long DEFAULT_TIMEOUT = 15*1000; @@ -69,6 +73,7 @@ class LookupDestJob extends JobImpl { _timeout = timeout; _sessID = sessID; _fromLocalDest = fromLocalDest; + BlindData bd = null; if (name != null && name.length() >= 60) { // convert a b32 lookup to a hash lookup String nlc = name.toLowerCase(Locale.US); @@ -82,8 +87,16 @@ class LookupDestJob extends JobImpl { name = null; } else if (b.length >= 35) { // encrypted LS2 + // lookup the blinded hash try { - BlindData bd = Blinding.decode(context, b); + bd = Blinding.decode(context, b); + SigningPublicKey spk = bd.getUnblindedPubKey(); + BlindData bd2 = getContext().netDb().getBlindData(spk); + if (bd2 != null) { + bd = bd2; + } else { + getContext().netDb().setBlindData(bd); + } h = bd.getBlindedHash(); if (_log.shouldDebug()) _log.debug("Converting name lookup " + name + " to blinded " + h); @@ -99,6 +112,7 @@ class LookupDestJob extends JobImpl { } _hash = h; _name = name; + _blindData = bd; } public String getName() { return _name != null ? @@ -107,6 +121,15 @@ class LookupDestJob extends JobImpl { } public void runJob() { + if (_blindData != null) { + Destination d = _blindData.getDestination(); + if (d != null) { + if (_log.shouldDebug()) + _log.debug("Found cached b33 lookup " + _name + " to " + d); + returnDest(d); + return; + } + } if (_name != null) { // inline, ignore timeout Destination d = getContext().namingService().lookup(_name); @@ -121,6 +144,7 @@ class LookupDestJob extends JobImpl { } } else { DoneJob done = new DoneJob(getContext()); + // TODO tell router this is an encrypted lookup, skip 38 or earlier ffs? getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest); } } @@ -132,6 +156,18 @@ class LookupDestJob extends JobImpl { public String getName() { return "LeaseSet Lookup Reply to Client"; } public void runJob() { Destination dest = getContext().netDb().lookupDestinationLocally(_hash); + if (dest == null && _blindData != null) { + // TODO store and lookup original hash instead + LeaseSet ls = getContext().netDb().lookupLeaseSetLocally(_hash); + if (ls != null && ls.getType() == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) { + // already decrypted + EncryptedLeaseSet encls = (EncryptedLeaseSet) ls; + LeaseSet decls = encls.getDecryptedLeaseSet(); + if (decls != null) { + dest = decls.getDestination(); + } + } + } if (dest != null) { if (_log.shouldDebug()) _log.debug("Found hash lookup " + _hash + " to " + dest); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java new file mode 100644 index 0000000000..83f25714dd --- /dev/null +++ b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java @@ -0,0 +1,218 @@ +package net.i2p.router.networkdb.kademlia; + +import java.util.concurrent.ConcurrentHashMap; + +import net.i2p.crypto.Blinding; +import net.i2p.crypto.SigType; +import net.i2p.data.BlindData; +import net.i2p.data.Destination; +import net.i2p.data.Hash; +import net.i2p.data.SigningPublicKey; +import net.i2p.router.RouterContext; + +/** + * Cache of blinding data. See proposal 123. + * + * @since 0.9.40 + */ +class BlindCache { + + private final RouterContext _context; + // unblinded key + private final ConcurrentHashMap _cache; + // blinded key + private final ConcurrentHashMap _reverseCache; + // dest hash + private final ConcurrentHashMap _hashCache; + + /** + * Caller MUST call startup() to load persistent cache from disk + */ + public BlindCache(RouterContext ctx) { + _context = ctx; + _cache = new ConcurrentHashMap(32); + _reverseCache = new ConcurrentHashMap(32); + _hashCache = new ConcurrentHashMap(32); + } + + /** + * May be restarted by calling startup() again. + */ + public synchronized void shutdown() { + store(); + _cache.clear(); + _reverseCache.clear(); + _hashCache.clear(); + } + + public synchronized void startup() { + load(); + } + + /** + * The hash to lookup for a dest. + * If known to be blinded, returns the current blinded hash. + * If not known to be blinded, returns the standard dest hash. + * + * @param dest may or may not be blinded + * @return the unblinded or blinded hash + */ + public Hash getHash(Destination dest) { + Hash rv = getBlindedHash(dest); + if (rv != null) + return rv; + return dest.getHash(); + } + + /** + * The hash to lookup for a dest hash. + * If known to be blinded, returns the current blinded hash. + * If not known to be blinded, returns h. + * + * @param dest may or may not be blinded + * @return the blinded hash or h + */ + public Hash getHash(Hash h) { + BlindData bd = _hashCache.get(h); + if (bd != null) + return bd.getBlindedHash(); + return h; + } + + /** + * The hash to lookup for a dest. + * If known to be blinded, returns the current blinded hash. + * If not known to be blinded, returns null. + * + * @param dest may or may not be blinded + * @return the blinded hash or null if not blinded + */ + public Hash getBlindedHash(Destination dest) { + BlindData bd = _cache.get(dest.getSigningPublicKey()); + if (bd != null) + return bd.getBlindedHash(); + return null; + } + + /** + * The hash to lookup for a SPK known to be blinded. + * Default blinded type assumed. + * Secret assumed null. + * + * @param spk known to be blinded + * @return the blinded hash + * @throws IllegalArgumentException on various errors + */ + public Hash getBlindedHash(SigningPublicKey spk) { + BlindData bd = _cache.get(spk); + if (bd == null) + bd = new BlindData(_context, spk, Blinding.getDefaultBlindedType(spk.getType()), null); + addToCache(bd); + return bd.getBlindedHash(); + } + + /** + * Mark a destination as known to be blinded + * + * @param dest known to be blinded + * @param blindedType null for default + * @param secret may be null + * @throws IllegalArgumentException on various errors + */ + public void setBlinded(Destination dest, SigType blindedType, String secret) { + SigningPublicKey spk = dest.getSigningPublicKey(); + BlindData bd = _cache.get(spk); + if (bd != null) { + bd.setDestination(dest); + } else { + if (blindedType == null) + blindedType = Blinding.getDefaultBlindedType(spk.getType()); + bd = new BlindData(_context, dest, blindedType, secret); + bd.setDestination(dest); + addToCache(bd); + } + } + + /** + * Add the destination to the cache entry. + * Must previously be in cache. + * + * @param dest known to be blinded + * @throws IllegalArgumentException on various errors + */ + public void setBlinded(Destination dest) { + SigningPublicKey spk = dest.getSigningPublicKey(); + BlindData bd = _cache.get(spk); + if (bd != null) { + bd.setDestination(dest); + _hashCache.putIfAbsent(dest.getHash(), bd); + } + } + + public void addToCache(BlindData bd) { + _cache.put(bd.getUnblindedPubKey(), bd); + _reverseCache.put(bd.getBlindedPubKey(), bd); + Destination dest = bd.getDestination(); + if (dest != null) + _hashCache.put(dest.getHash(), bd); + } + + /** + * The cached data or null + */ + public BlindData getData(Destination dest) { + BlindData rv = getData(dest.getSigningPublicKey()); + if (rv != null) { + Destination d = rv.getDestination(); + if (d == null) + rv.setDestination(dest); + else if (!dest.equals(d)) + rv = null; // mismatch ??? + } + return rv; + } + + /** + * The cached data or null + * + * @param spk the unblinded public key + */ + public BlindData getData(SigningPublicKey spk) { + SigType type = spk.getType(); + if (type != SigType.EdDSA_SHA512_Ed25519 && + type != SigType.RedDSA_SHA512_Ed25519) + return null; + return _cache.get(spk); + } + + /** + * The cached data or null + * + * @param spk the blinded public key + */ + public BlindData getReverseData(SigningPublicKey spk) { + SigType type = spk.getType(); + if (type != SigType.RedDSA_SHA512_Ed25519) + return null; + return _reverseCache.get(spk); + } + + /** + * Refresh all the data at midnight + * + */ + public void rollover() { + _reverseCache.clear(); + for (BlindData bd : _cache.values()) { + _reverseCache.put(bd.getBlindedPubKey(), bd); + } + } + + private void load() { + // TODO + } + + private void store() { + // TODO + } +} diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java index 819f2a7667..4141cf3b42 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java @@ -21,15 +21,18 @@ import java.util.Set; import net.i2p.crypto.SigAlgo; import net.i2p.crypto.SigType; +import net.i2p.data.BlindData; import net.i2p.data.Certificate; import net.i2p.data.DatabaseEntry; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; +import net.i2p.data.EncryptedLeaseSet; import net.i2p.data.Hash; import net.i2p.data.KeyCertificate; import net.i2p.data.LeaseSet; import net.i2p.data.LeaseSet2; +import net.i2p.data.SigningPublicKey; import net.i2p.data.i2np.DatabaseLookupMessage; import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.router.RouterAddress; @@ -73,6 +76,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad private volatile long _lastRIPublishTime; private NegativeLookupCache _negativeCache; protected final int _networkID; + private final BlindCache _blindCache; /** * Map of Hash to RepublishLeaseSetJob for leases we'realready managing. @@ -171,6 +175,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad _publishingLeaseSets = new HashMap(8); _activeRequests = new HashMap(8); _reseedChecker = new ReseedChecker(context); + _blindCache = new BlindCache(context); context.statManager().createRateStat("netDb.lookupDeferred", "how many lookups are deferred?", "NetworkDatabase", new long[] { 60*60*1000 }); context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 60*60*1000 }); context.statManager().createRateStat("netDb.negativeCache", "Aborted lookup, already cached", "NetworkDatabase", new long[] { 60*60*1000l }); @@ -246,7 +251,9 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad //_ds = null; _exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't. // _exploreKeys = null; - _negativeCache.clear(); + if (_negativeCache != null) + _negativeCache.clear(); + _blindCache.shutdown(); } public synchronized void restart() { @@ -257,6 +264,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad } _ds.restart(); _exploreKeys.clear(); + _blindCache.startup(); _initialized = true; @@ -287,6 +295,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad // _exploreKeys = new HashSet(64); _dbDir = dbDir; _negativeCache = new NegativeLookupCache(_context); + _blindCache.startup(); createHandlers(); @@ -463,6 +472,27 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad return _kb.size(); } + /** + * @param spk unblinded key + * @return BlindData or null + * @since 0.9.40 + */ + @Override + public BlindData getBlindData(SigningPublicKey spk) { + return _blindCache.getData(spk); + } + + /** + * @param bd new BlindData to put in the cache + * @since 0.9.40 + */ + @Override + public void setBlindData(BlindData bd) { + if (_log.shouldWarn()) + _log.warn("Adding to blind cache: " + bd); + _blindCache.addToCache(bd); + } + /** * @return RouterInfo, LeaseSet, or null, validated * @since 0.8.3 @@ -476,10 +506,12 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad int type = rv.getType(); if (DatabaseEntry.isLeaseSet(type)) { LeaseSet ls = (LeaseSet)rv; - if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) + if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) { return rv; - else + } else { + key = _blindCache.getHash(key); fail(key); + } } else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) { try { if (validate((RouterInfo)rv) == null) @@ -533,6 +565,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad } else { //if (_log.shouldLog(Log.DEBUG)) // _log.debug("leaseSet not found locally, running search"); + key = _blindCache.getHash(key); search(key, onFindJob, onFailedLookupJob, timeoutMs, true, fromLocalDest); } //if (_log.shouldLog(Log.DEBUG)) @@ -549,6 +582,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad */ public void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest) { if (!_initialized) return; + key = _blindCache.getHash(key); search(key, null, null, 20*1000, true, fromLocalDest); } @@ -564,6 +598,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) { return ls; } else { + key = _blindCache.getHash(key); fail(key); // this was an interesting key, so either refetch it or simply explore with it _exploreKeys.add(key); @@ -599,6 +634,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad _log.info("Negative cached, not searching dest: " + key); _context.jobQueue().addJob(onFinishedJob); } else { + key = _blindCache.getHash(key); search(key, onFinishedJob, onFinishedJob, timeoutMs, true, fromLocalDest); } } @@ -892,12 +928,47 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad if (rv != null && !leaseSet.getDestination().equals(rv.getDestination())) throw new IllegalArgumentException("LS Hash collision"); + EncryptedLeaseSet encls = null; + if (leaseSet.getType() == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) { + // set dest or key before validate() calls verifySignature() which + // will do the decryption + BlindData bd = _blindCache.getReverseData(leaseSet.getSigningKey()); + if (bd != null) { + if (_log.shouldWarn()) + _log.warn("Found blind data for encls: " + bd); + encls = (EncryptedLeaseSet) leaseSet; + Destination dest = bd.getDestination(); + if (dest != null) { + encls.setDestination(dest); + } else { + encls.setSigningKey(bd.getUnblindedPubKey()); + } + } else { + if (_log.shouldWarn()) + _log.warn("No blind data found for encls: " + encls); + } + } + + String err = validate(key, leaseSet); if (err != null) throw new IllegalArgumentException("Invalid store attempt - " + err); _ds.put(key, leaseSet); + if (encls != null) { + // we now have decrypted it, store it as well + LeaseSet decls = encls.getDecryptedLeaseSet(); + if (decls != null) { + if (_log.shouldWarn()) + _log.warn("Successfully decrypted encls: " + decls); + // recursion + Destination dest = decls.getDestination(); + store(dest.getHash(), decls); + _blindCache.setBlinded(dest); + } + } + // Iterate through the old failure / success count, copying over the old // values (if any tunnels overlap between leaseSets). no need to be // ueberthreadsafe fascists here, since these values are just heuristics From ea127d3fd43ad7563fb9fc9466afba4c4cded8e7 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 27 Mar 2019 13:58:56 +0000 Subject: [PATCH 05/14] NetDB: Fix b33 lookup looping after failure --- .../src/net/i2p/router/client/LookupDestJob.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/router/java/src/net/i2p/router/client/LookupDestJob.java b/router/java/src/net/i2p/router/client/LookupDestJob.java index e03758ab94..a71240fa35 100644 --- a/router/java/src/net/i2p/router/client/LookupDestJob.java +++ b/router/java/src/net/i2p/router/client/LookupDestJob.java @@ -104,7 +104,9 @@ class LookupDestJob extends JobImpl { } catch (RuntimeException re) { if (_log.shouldWarn()) _log.debug("Failed blinding conversion of " + name, re); - // lookup as a name, which will probably fail + // Do NOT lookup as a name, naming service will call us again and infinite loop + name = null; + // h and name both null, runJob will fail immediately } } } @@ -142,10 +144,13 @@ class LookupDestJob extends JobImpl { _log.debug("Failed name lookup " + _name); returnFail(); } - } else { + } else if (_hash != null) { DoneJob done = new DoneJob(getContext()); // TODO tell router this is an encrypted lookup, skip 38 or earlier ffs? getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest); + } else { + // blinding decode fail + returnFail(); } } @@ -199,8 +204,10 @@ class LookupDestJob extends JobImpl { I2CPMessage msg; if (_reqID >= 0) msg = new HostReplyMessage(_sessID, HostReplyMessage.RESULT_FAILURE, _reqID); - else + else if (_hash != null) msg = new DestReplyMessage(_hash); + else + return; // shouldn't happen try { _runner.doSend(msg); } catch (I2CPMessageException ime) {} From ba801be24f96a0fcf1205351558ea60b49d36b84 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 28 Mar 2019 13:49:21 +0000 Subject: [PATCH 06/14] Debconf translation update from https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=925515 --- debian/po/sv.po | 54 +++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/debian/po/sv.po b/debian/po/sv.po index 3e0631d5f9..40b82a432f 100644 --- a/debian/po/sv.po +++ b/debian/po/sv.po @@ -1,7 +1,7 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. -# +# # Translators: # Anders Nilsson , 2015 # cacapo , 2015 @@ -11,22 +11,24 @@ msgid "" msgstr "" "Project-Id-Version: I2P\n" -"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n" -"POT-Creation-Date: 2015-02-18 22:14+0000\n" -"PO-Revision-Date: 2017-06-30 21:32+0000\n" -"Last-Translator: Jony\n" -"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n" +"Report-Msgid-Bugs-To: i2p@packages.debian.org\n" +"POT-Creation-Date: 2017-11-12 14:01+0000\n" +"PO-Revision-Date: 2019-03-26 06:10+0100\n" +"Last-Translator: Jonatan Nyberg \n" +"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/" +"sv_SE/)\n" +"Language: sv_SE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: sv_SE\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.2.1\n" #. Type: boolean #. Description #: ../i2p.templates:2001 msgid "Should the I2P router be started at boot?" -msgstr "Ska I2P routern startas vid systemstart?" +msgstr "Ska I2P-routern startas vid systemstart?" #. Type: boolean #. Description @@ -34,7 +36,9 @@ msgstr "Ska I2P routern startas vid systemstart?" msgid "" "The I2P router can be run as a daemon that starts automatically when your " "computer boots up. This is the recommended configuration." -msgstr "I2P routern kan köras som en tjänst (demon) som automatiskt startas när datorn startas. Detta är den rekommenderade konfigurationen. " +msgstr "" +"I2P-routern kan köras som en tjänst (demon) som automatiskt startas när " +"datorn startas. Detta är den rekommenderade konfigurationen." #. Type: string #. Description @@ -46,11 +50,15 @@ msgstr "I2P-demon användare:" #. Description #: ../i2p.templates:3001 msgid "" -"By default I2P is configured to run under the account i2psvc when running as" -" a daemon. To use an **existing** I2P profile you may enter a different " -"account name here. For example, if your previous I2P installation is at " -"/home/user/i2p, you may enter 'user' here." -msgstr "Som standard är I2P inställt för att köras under användaren i2psvc när det körs som tjänst. För att använda ett **existerande** I2P profil, ange en annan användare här. Exempelvis, om din tidigare I2P installation är /home/user/i2p så ange 'user' här." +"By default I2P is configured to run under the account i2psvc when running as " +"a daemon. To use an **existing** I2P profile you may enter a different " +"account name here. For example, if your previous I2P installation is at /" +"home/user/i2p, you may enter 'user' here." +msgstr "" +"Som standard är I2P konfigurerad för att köras under användaren i2psvc när " +"det körs som tjänst. Om du vill använda en **befintlig** I2P-profil kan du " +"ange ett annat kontonamn här. Till exempel, om din tidigare I2P-installation " +"är på /home/user/I2P, kan du ange \"användare\" här." #. Type: string #. Description @@ -58,7 +66,9 @@ msgstr "Som standard är I2P inställt för att köras under användaren i2psvc msgid "" "Very important: If a user other than the default of 'i2psvc' is entered " "here, the chosen username *MUST* already exist." -msgstr "OBS! Viktigt: Om en annan användare än standard 'i2psvc' skrivs in här. *MÅSTE* det användarnamnet redan existera." +msgstr "" +"Mycket viktigt: om en annan användare än standard 'i2psvc' anges här, " +"*MÅSTE* det valda användarnamnet *redan finnas." #. Type: string #. Description @@ -70,7 +80,7 @@ msgstr "Minne som kan tilldelas I2P:" #. Description #: ../i2p.templates:4001 msgid "By default, I2P will only be allowed to use up to 128MB of RAM." -msgstr "Som standard kommer I2P bara att använda up till 128MB RAM." +msgstr "Som standard får I2P endast använda upp till 128 MB RAM." #. Type: string #. Description @@ -78,13 +88,15 @@ msgstr "Som standard kommer I2P bara att använda up till 128MB RAM." msgid "" "High bandwidth routers, as well as routers with a lot of active torrents / " "plugins, may need to have this value increased." -msgstr "För routrar med hög bandbredd eller routrar med hög aktivitet kan detta behöva ökas" +msgstr "" +"För routrar med hög bandbredd samt routrar med en hel del aktiva torrenter / " +"insticksmoduler, kan detta värde behöva ökas." #. Type: boolean #. Description #: ../i2p.templates:5001 -msgid "Run I2P daemon confined with AppArmor" -msgstr "Kör I2P begränsad av AppArmor" +msgid "Should the I2P daemon be confined with AppArmor?" +msgstr "Ska I2P-demonen vara begränsad med AppArmor?" #. Type: boolean #. Description @@ -92,4 +104,6 @@ msgstr "Kör I2P begränsad av AppArmor" msgid "" "With this option enabled I2P will be sandboxed with AppArmor, restricting " "which files and directories may be accessed by I2P." -msgstr "Med det här valet aktiverat kommer I2P att köras i sandlåda med AppArmor, som begränsar vilka filer och mappar som kan kommas åt av I2P." +msgstr "" +"Med det här alternativet aktiverat kommer I2P att köras i sandlåda med " +"AppArmor, som begränsar vilka filer och mappar som kan kommas åt av I2P." From eb0920e2c7676cd2a282931b6581019ed8bc76a2 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 29 Mar 2019 12:50:41 +0000 Subject: [PATCH 07/14] NetDB: Persistence for blinding cache --- core/java/src/net/i2p/crypto/Blinding.java | 10 ++ core/java/src/net/i2p/data/BlindData.java | 32 +++- .../router/networkdb/kademlia/BlindCache.java | 167 +++++++++++++++++- 3 files changed, 199 insertions(+), 10 deletions(-) diff --git a/core/java/src/net/i2p/crypto/Blinding.java b/core/java/src/net/i2p/crypto/Blinding.java index a0cd6d8388..bb91214eba 100644 --- a/core/java/src/net/i2p/crypto/Blinding.java +++ b/core/java/src/net/i2p/crypto/Blinding.java @@ -218,6 +218,7 @@ public final class Blinding { * PRELIMINARY - Subject to change - see proposal 149 * * @param b 35+ bytes + * @return BlindData structure, use getUnblindedPubKey() for the result * @throws IllegalArgumentException on bad inputs * @throws UnsupportedOperationException unless supported SigTypes * @since 0.9.40 @@ -313,6 +314,15 @@ public final class Blinding { return Base32.encode(b) + ".b32.i2p"; } + public static void main(String args[]) throws Exception { + if (args.length != 1) { + System.out.println("Usage: blinding {56 chars}.b32.i2p"); + System.exit(1); + } + System.out.println("Blinded B32: " + args[0]); + System.out.println(decode(I2PAppContext.getGlobalContext(), args[0]).toString()); + } + /****** public static void main(String args[]) throws Exception { net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE); diff --git a/core/java/src/net/i2p/data/BlindData.java b/core/java/src/net/i2p/data/BlindData.java index 3e613fd5ba..80d4240c51 100644 --- a/core/java/src/net/i2p/data/BlindData.java +++ b/core/java/src/net/i2p/data/BlindData.java @@ -42,7 +42,7 @@ public class BlindData { _clearSPK = spk; _blindType = blindType; _secret = secret; - _authType = 0; + _authType = -1; _authKey = null; // defer until needed //calculate(); @@ -55,6 +55,13 @@ public class BlindData { return _clearSPK; } + /** + * @return The type of the blinded key + */ + public SigType getBlindedSigType() { + return _blindType; + } + /** * @return The blinded key for the current day, non-null */ @@ -115,12 +122,19 @@ public class BlindData { } /** - * @return 0 for no client auth + * @return -1 for no client auth */ public int getAuthType() { return _authType; } + /** + * @return null for no client auth + */ + public PrivateKey getAuthPrivKey() { + return _authKey; + } + private synchronized void calculate() { if (_context.isRouterContext()) { RoutingKeyGenerator gen = _context.routingKeyGenerator(); @@ -147,16 +161,24 @@ public class BlindData { @Override public synchronized String toString() { calculate(); - StringBuilder buf = new StringBuilder(256); + StringBuilder buf = new StringBuilder(1024); buf.append("[BlindData: "); buf.append("\n\tSigningPublicKey: ").append(_clearSPK); buf.append("\n\tAlpha : ").append(_alpha); buf.append("\n\tBlindedPublicKey: ").append(_blindSPK); buf.append("\n\tBlinded Hash : ").append(_blindHash); - buf.append("\n\tSecret: ").append(_secret); + if (_secret != null) + buf.append("\n\tSecret : \"").append(_secret).append('"'); + buf.append("\n\tAuth Type : "); + if (_authType >= 0) + buf.append(_authType); + else + buf.append("none"); + if (_authKey != null) + buf.append("\n\tAuth Key : ").append(_authKey); if (_dest != null) buf.append("\n\tDestination: ").append(_dest); - if (_dest != null) + else buf.append("\n\tDestination: unknown"); buf.append(']'); return buf.toString(); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java index 83f25714dd..d2e5cf3f3b 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java @@ -1,14 +1,29 @@ package net.i2p.router.networkdb.kademlia; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; import net.i2p.crypto.Blinding; +import net.i2p.crypto.EncType; import net.i2p.crypto.SigType; +import net.i2p.data.Base64; import net.i2p.data.BlindData; +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.Hash; +import net.i2p.data.PrivateKey; import net.i2p.data.SigningPublicKey; import net.i2p.router.RouterContext; +import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; /** * Cache of blinding data. See proposal 123. @@ -25,6 +40,8 @@ class BlindCache { // dest hash private final ConcurrentHashMap _hashCache; + private static final String PERSIST_FILE = "router.blindcache.dat"; + /** * Caller MUST call startup() to load persistent cache from disk */ @@ -201,18 +218,158 @@ class BlindCache { * Refresh all the data at midnight * */ - public void rollover() { + public synchronized void rollover() { _reverseCache.clear(); for (BlindData bd : _cache.values()) { _reverseCache.put(bd.getBlindedPubKey(), bd); } } - private void load() { - // TODO + /** + * Load from file. + * Format: + * sigtype,bsigtype,b64 pubkey,[b64 secret],[b64 dest] + */ + private synchronized void load() { + File file = new File(_context.getConfigDir(), PERSIST_FILE); + if (!file.exists()) + return; + Log log = _context.logManager().getLog(BlindCache.class); + int count = 0; + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader( + new FileInputStream(file), "ISO-8859-1")); + String line = null; + while ( (line = br.readLine()) != null) { + if (line.startsWith("#")) + continue; + try { + addToCache(fromPersistentString(line)); + count++; + } catch (IllegalArgumentException iae) { + if (log.shouldLog(Log.WARN)) + log.warn("Error reading cache entry", iae); + } catch (DataFormatException dfe) { + if (log.shouldLog(Log.WARN)) + log.warn("Error reading cache entry", dfe); + } + } + } catch (IOException ioe) { + if (log.shouldLog(Log.WARN) && file.exists()) + log.warn("Error reading the blinding cache file", ioe); + } finally { + if (br != null) try { br.close(); } catch (IOException ioe) {} + } + if (log.shouldLog(Log.INFO)) + log.info("Loaded " + count + " entries from " + file); } - private void store() { - // TODO + private synchronized void store() { + if (_cache.isEmpty()) + return; + Log log = _context.logManager().getLog(BlindCache.class); + int count = 0; + File file = new File(_context.getConfigDir(), PERSIST_FILE); + PrintWriter out = null; + try { + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "ISO-8859-1"))); + out.println("# Blinding cache entries. Format is: sigtype,bsigtype,authtype,time,key,[secret],[privkey],[dest]"); + for (BlindData bd : _cache.values()) { + out.println(toPersistentString(bd)); + count++; + } + if (out.checkError()) + throw new IOException("Failed write to " + file); + } catch (IOException ioe) { + if (log.shouldLog(Log.WARN)) + log.warn("Error writing the blinding cache File", ioe); + } finally { + if (out != null) out.close(); + } + if (log.shouldLog(Log.INFO)) + log.info("Stored " + count + " entries to " + file); + } + + /** + * Format: + * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] + */ + private BlindData fromPersistentString(String line) throws DataFormatException { + String[] ss = DataHelper.split(line, ",", 8); + if (ss.length != 8) + throw new DataFormatException("bad format"); + int ist1, ist2, auth; + long time; + try { + ist1 = Integer.parseInt(ss[0]); + ist2 = Integer.parseInt(ss[1]); + auth = Integer.parseInt(ss[2]); + time = Long.parseLong(ss[3]); + } catch (NumberFormatException nfe) { + throw new DataFormatException("bad codes", nfe); + } + SigType st1 = SigType.getByCode(ist1); + SigType st2 = SigType.getByCode(ist2); + if (st1 == null || !st1.isAvailable() || st2 == null || !st2.isAvailable()) + throw new DataFormatException("bad codes"); + SigningPublicKey spk = new SigningPublicKey(st1); + spk.fromBase64(ss[4]); + String secret; + if (ss[5].length() > 0) { + byte[] b = Base64.decode(ss[5]); + if (b == null) + throw new DataFormatException("bad secret"); + secret = DataHelper.getUTF8(b); + } else { + secret = null; + } + PrivateKey privkey; + if (ss[6].length() > 0) { + byte[] b = Base64.decode(ss[6]); + if (b == null) + throw new DataFormatException("bad privkey"); + privkey = new PrivateKey(EncType.ECIES_X25519, b); + } else { + privkey = null; + } + BlindData rv; + // TODO pass privkey + if (ss[7].length() > 0) { + Destination dest = new Destination(ss[7]); + if (!spk.equals(dest.getSigningPublicKey())) + throw new DataFormatException("spk mismatch"); + rv = new BlindData(_context, dest, st2, secret); + } else { + rv = new BlindData(_context, spk, st2, secret); + } + return rv; + } + + /** + * Format: + * sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest] + */ + private static String toPersistentString(BlindData bd) { + StringBuilder buf = new StringBuilder(1024); + SigningPublicKey spk = bd.getUnblindedPubKey(); + buf.append(spk.getType().getCode()).append(','); + buf.append(bd.getBlindedSigType().getCode()).append(','); + buf.append(bd.getAuthType()).append(','); + // timestamp todo + buf.append('0').append(','); + buf.append(spk.toBase64()).append(','); + String secret = bd.getSecret(); + if (secret != null && secret.length() > 0) + buf.append(Base64.encode(secret)); + buf.append(','); + PrivateKey pk = bd.getAuthPrivKey(); + if (pk != null) + buf.append(pk.toBase64()); + buf.append(','); + Destination dest = bd.getDestination(); + if (dest != null) + buf.append(dest.toBase64()); + return buf.toString(); } } From 7501e3feea13b8dfafbff0b0e90e910df7df932d Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 29 Mar 2019 12:51:19 +0000 Subject: [PATCH 08/14] javadoc fix --- .../java/src/net/i2p/router/client/ClientConnectionRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 150605aaaa..ac930391a7 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -608,7 +608,7 @@ class ClientConnectionRunner { * called after a new leaseSet is granted by the client, the NetworkDb has been * updated. This takes care of all the LeaseRequestState stuff (including firing any jobs) * - * @param ls, if encrypted, the encrypted LS, not the decrypted one + * @param ls if encrypted, the encrypted LS, not the decrypted one */ void leaseSetCreated(LeaseSet ls) { Hash h = ls.getDestination().calculateHash(); From 944fe4794e3056008a219d12735f52b823c85cad Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 29 Mar 2019 12:54:12 +0000 Subject: [PATCH 09/14] Crypto: new SigContext (WIP) (proposal #148) --- core/java/src/net/i2p/crypto/SigContext.java | 69 ++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 core/java/src/net/i2p/crypto/SigContext.java diff --git a/core/java/src/net/i2p/crypto/SigContext.java b/core/java/src/net/i2p/crypto/SigContext.java new file mode 100644 index 0000000000..fa0d9dd989 --- /dev/null +++ b/core/java/src/net/i2p/crypto/SigContext.java @@ -0,0 +1,69 @@ +package net.i2p.crypto; + +import java.security.spec.AlgorithmParameterSpec; + +import net.i2p.data.DataHelper; + +/** + * Defines the context for signing with personalized hashes. + * See proposal 148. + * + * @since 0.9.40 + */ +public enum SigContext { + + SC_NONE (null), + SC_DATAGRAM ("sign_datagramI2P"), + SC_I2CP ("I2CP_SessionConf"), + SC_NETDB ("network_database"), + SC_NTCP ("NTCP_1_handshake"), + SC_SSU ("SSUHandshakeSign"), + SC_STREAMING("streaming_i2psig"), + SC_SU3 ("i2pSU3FileFormat"), + SC_TEST ("test1234test5678"), + + ; + + private final SigContextSpec spec; + + /** + * The 16 bytes for this type, or null for none + */ + SigContext(String p) { + spec = new SigContextSpec(p); + } + + /** + * The AlgorithmParameterSpec. + * Pass this as an argument in setParameter() + * to the Blake sign/verify engines. + */ + public SigContextSpec getSpec() { return spec; } + + /** + * The AlgorithmParameterSpec. + * Pass this as an argument in setParameter() + * to the Blake sign/verify engines. + */ + public static class SigContextSpec implements AlgorithmParameterSpec { + private final byte[] b; + + /** + * The 16 bytes for this type, or null for none + */ + public SigContextSpec(String p) { + if (p != null) { + b = DataHelper.getASCII(p); + if (b.length != 16) + throw new IllegalArgumentException(); + } else { + b = null; + } + } + + /** + * The 16 bytes for this type, or null for none + */ + public byte[] getData() { return b; } + } +} From 7d40dfe1e505397cc99b9ce6af9a246b018c319d Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 29 Mar 2019 12:55:17 +0000 Subject: [PATCH 10/14] CLI tweak --- core/java/src/net/i2p/data/Base32.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/java/src/net/i2p/data/Base32.java b/core/java/src/net/i2p/data/Base32.java index e2ba29d0b5..a573df03f2 100644 --- a/core/java/src/net/i2p/data/Base32.java +++ b/core/java/src/net/i2p/data/Base32.java @@ -115,7 +115,7 @@ public class Base32 { private static void decode(InputStream in, OutputStream out) throws IOException { byte decoded[] = decode(DataHelper.getUTF8(read(in))); if (decoded == null) { - System.out.println("FAIL"); + System.err.println("FAIL"); return; } out.write(decoded); From 908bf26151774245306619fb2e41bfd703165504 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 31 Mar 2019 12:18:44 +0000 Subject: [PATCH 11/14] javadoc fix --- .../java/src/net/i2p/router/networkdb/kademlia/BlindCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java index d2e5cf3f3b..7bdffccbdf 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/BlindCache.java @@ -86,7 +86,7 @@ class BlindCache { * If known to be blinded, returns the current blinded hash. * If not known to be blinded, returns h. * - * @param dest may or may not be blinded + * @param h may or may not be blinded * @return the blinded hash or h */ public Hash getHash(Hash h) { From d3170de74a7442547cb769f3791ee86b8f8d5a2d Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 31 Mar 2019 12:20:13 +0000 Subject: [PATCH 12/14] SAM prep for b33 --- apps/sam/java/src/net/i2p/sam/SAMUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sam/java/src/net/i2p/sam/SAMUtils.java b/apps/sam/java/src/net/i2p/sam/SAMUtils.java index 3ab98301bb..a9f2737e51 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMUtils.java +++ b/apps/sam/java/src/net/i2p/sam/SAMUtils.java @@ -171,7 +171,7 @@ class SAMUtils { String msg; if (s.length() >= 516) msg = "Bad Base64 dest: "; - else if (s.length() == 60 && s.endsWith(".b32.i2p")) + else if (s.length() >= 60 && s.endsWith(".b32.i2p")) msg = "Lease set not found: "; else msg = "Host name not found: "; From 0e8e3688f71e9eb02f4f2aa8d79f2dba89910980 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 31 Mar 2019 12:23:19 +0000 Subject: [PATCH 13/14] Data: Implement Destroyable for private keys (ticket #2462) --- core/java/src/net/i2p/data/PrivateKey.java | 29 +++++++++++++++++-- .../src/net/i2p/data/SigningPrivateKey.java | 29 +++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/core/java/src/net/i2p/data/PrivateKey.java b/core/java/src/net/i2p/data/PrivateKey.java index 13872cb348..791af89bb0 100644 --- a/core/java/src/net/i2p/data/PrivateKey.java +++ b/core/java/src/net/i2p/data/PrivateKey.java @@ -10,9 +10,11 @@ package net.i2p.data; */ import java.util.Arrays; +import javax.security.auth.Destroyable; import net.i2p.crypto.EncType; import net.i2p.crypto.KeyGenerator; +import net.i2p.util.SimpleByteCache; /** * Defines the PrivateKey as defined by the I2P data structure spec. @@ -24,7 +26,7 @@ import net.i2p.crypto.KeyGenerator; * * @author jrandom */ -public class PrivateKey extends SimpleDataStructure { +public class PrivateKey extends SimpleDataStructure implements Destroyable { private static final EncType DEF_TYPE = EncType.ELGAMAL_2048; public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen(); @@ -89,13 +91,36 @@ public class PrivateKey extends SimpleDataStructure { return KeyGenerator.getPublicKey(this); } + /** + * javax.security.auth.Destroyable interface + * + * @since 0.9.40 + */ + public void destroy() { + byte[] data = _data; + if (data != null) { + _data = null; + Arrays.fill(data, (byte) 0); + SimpleByteCache.release(data); + } + } + + /** + * javax.security.auth.Destroyable interface + * + * @since 0.9.40 + */ + public boolean isDestroyed() { + return _data == null; + } + /** * @since 0.9.38 */ @Override public String toString() { StringBuilder buf = new StringBuilder(64); - buf.append("[PrivateKey ").append(_type).append(": "); + buf.append("[PrivateKey ").append(_type).append(' '); int length = length(); if (_data == null) { buf.append("null"); diff --git a/core/java/src/net/i2p/data/SigningPrivateKey.java b/core/java/src/net/i2p/data/SigningPrivateKey.java index 757c9102a3..2104ccdf4c 100644 --- a/core/java/src/net/i2p/data/SigningPrivateKey.java +++ b/core/java/src/net/i2p/data/SigningPrivateKey.java @@ -10,10 +10,12 @@ package net.i2p.data; */ import java.util.Arrays; +import javax.security.auth.Destroyable; import net.i2p.crypto.Blinding; import net.i2p.crypto.KeyGenerator; import net.i2p.crypto.SigType; +import net.i2p.util.SimpleByteCache; /** * Defines the SigningPrivateKey as defined by the I2P data structure spec. @@ -26,7 +28,7 @@ import net.i2p.crypto.SigType; * * @author jrandom */ -public class SigningPrivateKey extends SimpleDataStructure { +public class SigningPrivateKey extends SimpleDataStructure implements Destroyable { private static final SigType DEF_TYPE = SigType.DSA_SHA1; public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen(); @@ -115,13 +117,36 @@ public class SigningPrivateKey extends SimpleDataStructure { return b == 0; } + /** + * javax.security.auth.Destroyable interface + * + * @since 0.9.40 + */ + public void destroy() { + byte[] data = _data; + if (data != null) { + _data = null; + Arrays.fill(data, (byte) 0); + SimpleByteCache.release(data); + } + } + + /** + * javax.security.auth.Destroyable interface + * + * @since 0.9.40 + */ + public boolean isDestroyed() { + return _data == null; + } + /** * @since 0.9.8 */ @Override public String toString() { StringBuilder buf = new StringBuilder(64); - buf.append("[SigningPrivateKey ").append(_type).append(": "); + buf.append("[SigningPrivateKey ").append(_type).append(' '); int length = length(); if (_data == null) { buf.append("null"); From 567bccb51c918b19bc3abc7be308a629dfcb3f61 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 31 Mar 2019 12:29:26 +0000 Subject: [PATCH 14/14] Debian: Fixes for precise/trusty/jessie (ticket #2470) --- debian-alt/jessie/control | 13 ++++++------- debian-alt/precise/control | 4 +--- debian-alt/trusty/control | 3 --- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/debian-alt/jessie/control b/debian-alt/jessie/control index 71141259d2..304e454755 100644 --- a/debian-alt/jessie/control +++ b/debian-alt/jessie/control @@ -9,11 +9,10 @@ Build-Depends: debhelper (>= 7.0.50~) ,ant (>= 1.8) ,debconf ,openjdk-7-jdk -# Ant requires java 6 tools.jar: -# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar - ,openjdk-6-jdk ,libtomcat8-java ,dh-apparmor + ,dh-systemd + ,bash-completion ,gettext ,libgetopt-java ,libjson-simple-java (<< 3) @@ -33,7 +32,7 @@ Depends: ${java:Depends}, ${shlibs:Depends}, libjbigi-jni, lsb-base (>= 3.0-6), service-wrapper -Description: Anonymous network (I2P) +Description: Invisible Internet Project (I2P) - anonymous network I2P is an anonymizing network, offering a simple layer that identity-sensitive applications can use to securely communicate. All data is wrapped with several layers of encryption, and the network is both distributed and dynamic, with no @@ -48,7 +47,7 @@ Section: java Priority: optional Depends: ${shlibs:Depends}, i2p-router Homepage: https://geti2p.net/ -Description: I2P libjbigi library +Description: Invisible Internet Project (I2P) - libjbigi library This Package contains the libjbigi JNI library (and on x86 platforms, jcpuid). . libjbigi is a math library that is part of the I2P installation. Use of this @@ -62,7 +61,7 @@ Section: doc Priority: extra Depends: ${misc:Depends} Suggests: i2p, default-jdk-doc -Description: I2P developer documentation +Description: Invisible Internet Project (I2P) - developer documentation I2P is an anonymizing network, offering a simple layer that identity-sensitive applications can use to securely communicate. All data is wrapped with several layers of encryption, and the network is both distributed and dynamic, with no @@ -88,7 +87,7 @@ Recommends: libjbigi-jni, fonts-dejavu Suggests: apparmor ,privoxy ,syndie -Description: Router for I2P +Description: Invisible Internet Project (I2P) - Router I2P is an anonymizing network, offering a simple layer that identity-sensitive applications can use to securely communicate. All data is wrapped with several layers of encryption, and the network is both distributed and dynamic, with no diff --git a/debian-alt/precise/control b/debian-alt/precise/control index 503d331e69..3650f5ee44 100644 --- a/debian-alt/precise/control +++ b/debian-alt/precise/control @@ -9,11 +9,9 @@ Build-Depends: debhelper (>= 7.0.50~) ,ant (>= 1.8) ,debconf ,openjdk-7-jdk -# Ant requires java 6 tools.jar: -# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar - ,openjdk-6-jdk ,glassfish-javaee ,dh-apparmor + ,dh-systemd ,bash-completion ,gettext ,libgetopt-java diff --git a/debian-alt/trusty/control b/debian-alt/trusty/control index 7ae0b14b5c..2b8b4995a1 100644 --- a/debian-alt/trusty/control +++ b/debian-alt/trusty/control @@ -9,9 +9,6 @@ Build-Depends: debhelper (>= 7.0.50~) ,ant (>= 1.8) ,debconf ,openjdk-7-jdk -# Ant requires java 6 tools.jar: -# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar - ,openjdk-6-jdk ,glassfish-javaee ,dh-apparmor ,dh-systemd