diff --git a/router/java/src/net/i2p/router/LeaseSetKeys.java b/router/java/src/net/i2p/router/LeaseSetKeys.java index 559a0b65f6bb24aa43660e3f2b1de6896b2c831a..537e36d316a60152557ab4c3daebac8fcd3caaf5 100644 --- a/router/java/src/net/i2p/router/LeaseSetKeys.java +++ b/router/java/src/net/i2p/router/LeaseSetKeys.java @@ -32,7 +32,7 @@ public class LeaseSetKeys { * @since 0.9.44 */ public static final Set<EncType> SET_ELG = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048)); - private static final Set<EncType> SET_EC = Collections.unmodifiableSet(EnumSet.of(EncType.ECIES_X25519)); + public static final Set<EncType> SET_EC = Collections.unmodifiableSet(EnumSet.of(EncType.ECIES_X25519)); private static final Set<EncType> SET_BOTH = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048, EncType.ECIES_X25519)); private static final Set<EncType> SET_NONE = Collections.unmodifiableSet(EnumSet.noneOf(EncType.class)); diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java index 5f62ebdfe7b6978a04d717f177021c5b28d7f84c..b16df7199ceeaee52122d515087f70e6bd4ad9ae 100644 --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java @@ -606,14 +606,14 @@ class ClientConnectionRunner { if (hasElg) { TransientSessionKeyManager tskm = new TransientSessionKeyManager(_context, tags, thresh); if (hasEC) { - RatchetSKM rskm = new RatchetSKM(_context); + RatchetSKM rskm = new RatchetSKM(_context, dest); _sessionKeyManager = new MuxedSKM(tskm, rskm); } else { _sessionKeyManager = tskm; } } else { if (hasEC) { - _sessionKeyManager = new RatchetSKM(_context); + _sessionKeyManager = new RatchetSKM(_context, dest); } else { _log.error("No supported encryption types in i2cp.leaseSetEncType for " + dest.toBase32()); return SessionStatusMessage.STATUS_INVALID; diff --git a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java index ebe13ce0d977ab16bb971b4793a72d198ec283ef..d57eaa66bf844e4a8687fb2965d84b9ed4541bf6 100644 --- a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java +++ b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java @@ -19,15 +19,23 @@ import net.i2p.crypto.HKDF; import net.i2p.crypto.SessionKeyManager; import net.i2p.data.Base64; 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.Hash; +import net.i2p.data.LeaseSet; +import net.i2p.data.LeaseSet2; import net.i2p.data.PrivateKey; import net.i2p.data.PublicKey; import net.i2p.data.SessionKey; import net.i2p.data.SessionTag; +import net.i2p.data.i2np.DatabaseStoreMessage; import net.i2p.data.i2np.GarlicClove; +import net.i2p.data.i2np.I2NPMessage; import static net.i2p.router.crypto.ratchet.RatchetPayload.*; +import net.i2p.router.LeaseSetKeys; +import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.message.CloveSet; import net.i2p.util.Log; @@ -395,13 +403,13 @@ public final class ECIESAEADEngine { return NO_CLOVES; } - byte[] bobPK = new byte[KEYLEN]; - state.getRemotePublicKey().getPublicKey(bobPK, 0); + byte[] alicePK = new byte[KEYLEN]; + state.getRemotePublicKey().getPublicKey(alicePK, 0); if (_log.shouldDebug()) { - _log.debug("NS decrypt success from PK " + Base64.encode(bobPK)); + _log.debug("NS decrypt success from PK " + Base64.encode(alicePK)); _log.debug("State after decrypt new session: " + state); } - if (Arrays.equals(bobPK, NULLPK)) { + if (Arrays.equals(alicePK, NULLPK)) { // TODO if (_log.shouldWarn()) _log.warn("Zero static key in IB NS"); @@ -434,8 +442,8 @@ public final class ECIESAEADEngine { } // tell the SKM - PublicKey bob = new PublicKey(EncType.ECIES_X25519, bobPK); - keyManager.createSession(bob, state, null); + PublicKey alice = new PublicKey(EncType.ECIES_X25519, alicePK); + keyManager.createSession(alice, state, null); if (pc.cloveSet.isEmpty()) { // this is legal @@ -447,6 +455,8 @@ public final class ECIESAEADEngine { GarlicClove[] arr = new GarlicClove[num]; // msg id and expiration not checked in GarlicMessageReceiver CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime); + // TODO + //setResponseTimer(alice, pc.cloveSet, keyManager); return rv; } @@ -587,6 +597,8 @@ public final class ECIESAEADEngine { GarlicClove[] arr = new GarlicClove[num]; // msg id and expiration not checked in GarlicMessageReceiver CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime); + // TODO + //setResponseTimer(bob, pc.cloveSet, keyManager); return rv; } @@ -679,6 +691,7 @@ public final class ECIESAEADEngine { return true; } + //// end decrypt, start encrypt //// @@ -1207,6 +1220,39 @@ public final class ECIESAEADEngine { return payload; } + /* + * Set a timer for a ratchet-layer reply if the application does not respond. + * + * @since 0.9.46 + */ + private void setResponseTimer(PublicKey from, List<GarlicClove> cloveSet, RatchetSKM skm) { + for (GarlicClove clove : cloveSet) { + I2NPMessage msg = clove.getData(); + if (msg.getType() != DatabaseStoreMessage.MESSAGE_TYPE) + continue; + DatabaseStoreMessage dsm = (DatabaseStoreMessage) msg; + DatabaseEntry entry = dsm.getEntry(); + if (entry.getType() != DatabaseEntry.KEY_TYPE_LS2) + continue; + LeaseSet2 ls2 = (LeaseSet2) entry; + if (!ls2.isCurrent(Router.CLOCK_FUDGE_FACTOR)) + continue; + PublicKey pk = ls2.getEncryptionKey(LeaseSetKeys.SET_EC); + if (!from.equals(pk)) + continue; + if (!ls2.verifySignature()) + continue; + // OK, we have a valid place to send the reply + Destination d = ls2.getDestination(); + if (_log.shouldInfo()) + _log.info("Validated NS sender: " + d.toBase32()); + // TODO + return; + } + if (_log.shouldInfo()) + _log.info("Unvalidated NS sender: " + from); + } + /**** public static void main(String args[]) { diff --git a/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java b/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java index ad6fd706595cb2a2d1328b186ba39c665bafa497..71ea1b12d6d95dade8a3c4e564679f34227015ee 100644 --- a/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java +++ b/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java @@ -26,6 +26,7 @@ import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.TagSetHandle; import net.i2p.data.Base64; import net.i2p.data.DataHelper; +import net.i2p.data.Destination; import net.i2p.data.PrivateKey; import net.i2p.data.PublicKey; import net.i2p.data.SessionKey; @@ -52,6 +53,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener private volatile boolean _alive; private final HKDF _hkdf; private final DecayingHashSet _replayFilter; + private final Destination _destination; /** * Let outbound session tags sit around for this long before expiring them. @@ -81,10 +83,11 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener * appropriate application context itself. * */ - public RatchetSKM(RouterContext context) { + public RatchetSKM(RouterContext context, Destination dest) { super(context); _log = context.logManager().getLog(RatchetSKM.class); _context = context; + _destination = dest; _outboundSessions = new ConcurrentHashMap<PublicKey, OutboundSession>(64); _pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64); _inboundTagSets = new ConcurrentHashMap<RatchetSessionTag, RatchetTagSet>(128); @@ -124,6 +127,12 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener } } + /** + * @since 0.9.46 + */ + public Destination getDestination() { + return _destination; + } /** RatchetTagSet */ private Set<RatchetTagSet> getRatchetTagSets() {