Ratchet: Prep for ratchet-layer acks (WIP)

This commit is contained in:
zzz
2020-05-18 13:00:45 +00:00
parent d48d16d237
commit 093c46c937
4 changed files with 65 additions and 10 deletions

View File

@ -32,7 +32,7 @@ public class LeaseSetKeys {
* @since 0.9.44 * @since 0.9.44
*/ */
public static final Set<EncType> SET_ELG = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048)); 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_BOTH = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048, EncType.ECIES_X25519));
private static final Set<EncType> SET_NONE = Collections.unmodifiableSet(EnumSet.noneOf(EncType.class)); private static final Set<EncType> SET_NONE = Collections.unmodifiableSet(EnumSet.noneOf(EncType.class));

View File

@ -606,14 +606,14 @@ class ClientConnectionRunner {
if (hasElg) { if (hasElg) {
TransientSessionKeyManager tskm = new TransientSessionKeyManager(_context, tags, thresh); TransientSessionKeyManager tskm = new TransientSessionKeyManager(_context, tags, thresh);
if (hasEC) { if (hasEC) {
RatchetSKM rskm = new RatchetSKM(_context); RatchetSKM rskm = new RatchetSKM(_context, dest);
_sessionKeyManager = new MuxedSKM(tskm, rskm); _sessionKeyManager = new MuxedSKM(tskm, rskm);
} else { } else {
_sessionKeyManager = tskm; _sessionKeyManager = tskm;
} }
} else { } else {
if (hasEC) { if (hasEC) {
_sessionKeyManager = new RatchetSKM(_context); _sessionKeyManager = new RatchetSKM(_context, dest);
} else { } else {
_log.error("No supported encryption types in i2cp.leaseSetEncType for " + dest.toBase32()); _log.error("No supported encryption types in i2cp.leaseSetEncType for " + dest.toBase32());
return SessionStatusMessage.STATUS_INVALID; return SessionStatusMessage.STATUS_INVALID;

View File

@ -19,15 +19,23 @@ import net.i2p.crypto.HKDF;
import net.i2p.crypto.SessionKeyManager; import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.Certificate; import net.i2p.data.Certificate;
import net.i2p.data.DatabaseEntry;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.data.LeaseSet;
import net.i2p.data.LeaseSet2;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag; import net.i2p.data.SessionTag;
import net.i2p.data.i2np.DatabaseStoreMessage;
import net.i2p.data.i2np.GarlicClove; import net.i2p.data.i2np.GarlicClove;
import net.i2p.data.i2np.I2NPMessage;
import static net.i2p.router.crypto.ratchet.RatchetPayload.*; 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.RouterContext;
import net.i2p.router.message.CloveSet; import net.i2p.router.message.CloveSet;
import net.i2p.util.Log; import net.i2p.util.Log;
@ -395,13 +403,13 @@ public final class ECIESAEADEngine {
return NO_CLOVES; return NO_CLOVES;
} }
byte[] bobPK = new byte[KEYLEN]; byte[] alicePK = new byte[KEYLEN];
state.getRemotePublicKey().getPublicKey(bobPK, 0); state.getRemotePublicKey().getPublicKey(alicePK, 0);
if (_log.shouldDebug()) { 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); _log.debug("State after decrypt new session: " + state);
} }
if (Arrays.equals(bobPK, NULLPK)) { if (Arrays.equals(alicePK, NULLPK)) {
// TODO // TODO
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("Zero static key in IB NS"); _log.warn("Zero static key in IB NS");
@ -434,8 +442,8 @@ public final class ECIESAEADEngine {
} }
// tell the SKM // tell the SKM
PublicKey bob = new PublicKey(EncType.ECIES_X25519, bobPK); PublicKey alice = new PublicKey(EncType.ECIES_X25519, alicePK);
keyManager.createSession(bob, state, null); keyManager.createSession(alice, state, null);
if (pc.cloveSet.isEmpty()) { if (pc.cloveSet.isEmpty()) {
// this is legal // this is legal
@ -447,6 +455,8 @@ public final class ECIESAEADEngine {
GarlicClove[] arr = new GarlicClove[num]; GarlicClove[] arr = new GarlicClove[num];
// msg id and expiration not checked in GarlicMessageReceiver // msg id and expiration not checked in GarlicMessageReceiver
CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime); CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
// TODO
//setResponseTimer(alice, pc.cloveSet, keyManager);
return rv; return rv;
} }
@ -587,6 +597,8 @@ public final class ECIESAEADEngine {
GarlicClove[] arr = new GarlicClove[num]; GarlicClove[] arr = new GarlicClove[num];
// msg id and expiration not checked in GarlicMessageReceiver // msg id and expiration not checked in GarlicMessageReceiver
CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime); CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
// TODO
//setResponseTimer(bob, pc.cloveSet, keyManager);
return rv; return rv;
} }
@ -679,6 +691,7 @@ public final class ECIESAEADEngine {
return true; return true;
} }
//// end decrypt, start encrypt //// //// end decrypt, start encrypt ////
@ -1207,6 +1220,39 @@ public final class ECIESAEADEngine {
return payload; 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[]) { public static void main(String args[]) {

View File

@ -26,6 +26,7 @@ import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.TagSetHandle; import net.i2p.crypto.TagSetHandle;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey; import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey; import net.i2p.data.SessionKey;
@ -52,6 +53,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
private volatile boolean _alive; private volatile boolean _alive;
private final HKDF _hkdf; private final HKDF _hkdf;
private final DecayingHashSet _replayFilter; private final DecayingHashSet _replayFilter;
private final Destination _destination;
/** /**
* Let outbound session tags sit around for this long before expiring them. * 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. * appropriate application context itself.
* *
*/ */
public RatchetSKM(RouterContext context) { public RatchetSKM(RouterContext context, Destination dest) {
super(context); super(context);
_log = context.logManager().getLog(RatchetSKM.class); _log = context.logManager().getLog(RatchetSKM.class);
_context = context; _context = context;
_destination = dest;
_outboundSessions = new ConcurrentHashMap<PublicKey, OutboundSession>(64); _outboundSessions = new ConcurrentHashMap<PublicKey, OutboundSession>(64);
_pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64); _pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64);
_inboundTagSets = new ConcurrentHashMap<RatchetSessionTag, RatchetTagSet>(128); _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 */ /** RatchetTagSet */
private Set<RatchetTagSet> getRatchetTagSets() { private Set<RatchetTagSet> getRatchetTagSets() {