From 9307cc8a0c1df2f82bcf63f1e3b89591e8d4362e Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Tue, 31 Mar 2020 18:43:52 +0000 Subject: [PATCH] Ratchet: Add support for database lookup replies (proposal 154) --- .../i2p/data/i2np/DatabaseLookupMessage.java | 136 +++++++++++++++--- 1 file changed, 118 insertions(+), 18 deletions(-) diff --git a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java index ae77921372..b3a7b43392 100644 --- a/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java +++ b/router/java/src/net/i2p/data/i2np/DatabaseLookupMessage.java @@ -15,13 +15,15 @@ import java.util.List; import java.util.Set; import net.i2p.I2PAppContext; +import net.i2p.crypto.EncType; import net.i2p.data.DataHelper; import net.i2p.data.Hash; -import net.i2p.data.router.RouterInfo; +import net.i2p.data.PublicKey; import net.i2p.data.SessionKey; import net.i2p.data.SessionTag; import net.i2p.data.TunnelId; -//import net.i2p.util.Log; +import net.i2p.data.router.RouterInfo; +import net.i2p.router.crypto.ratchet.RatchetSessionTag; import net.i2p.util.VersionComparator; /** @@ -31,7 +33,6 @@ import net.i2p.util.VersionComparator; * @author jrandom */ public class DatabaseLookupMessage extends FastI2NPMessageImpl { - //private final static Log _log = new Log(DatabaseLookupMessage.class); public final static int MESSAGE_TYPE = 2; private Hash _key; private Hash _fromHash; @@ -40,6 +41,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { private List<Hash> _dontIncludePeers; private SessionKey _replyKey; private SessionTag _replyTag; + private RatchetSessionTag _ratchetReplyTag; + private PublicKey _ratchetPubKey; private Type _type; //private static volatile long _currentLookupPeriod = 0; @@ -60,6 +63,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { private static final byte FLAG_TYPE_LS = 0x04; private static final byte FLAG_TYPE_RI = 0x08; private static final byte FLAG_TYPE_EXPL = 0x0c; + // 0.9.46 or higher + private static final byte FLAG_RATCHET = 0x10; /** @since 0.9.16 */ public enum Type { @@ -80,6 +85,7 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { * the receiver rejecting the whole message as invalid. */ private static final String MIN_ENCRYPTION_VERSION = "0.9.7"; + private static final String MIN_RATCHET_VERSION = "0.9.46"; public DatabaseLookupMessage(I2PAppContext context) { this(context, false); @@ -219,7 +225,21 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { } /** - * The included session key or null if unset + * Does this router support ratchet replies? + * + * @param to null OK + * @since 0.9.46 + */ + public static boolean supportsRatchetReplies(RouterInfo to) { + if (to == null) + return false; + String v = to.getVersion(); + return VersionComparator.comp(v, MIN_RATCHET_VERSION) >= 0; + } + + /** + * The included session key or null if unset. + * If non-null, either getReplyTag() or getRatchetReplyTag() is non-null. * * @since 0.9.7 */ @@ -241,12 +261,57 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { * @since 0.9.7 */ public void setReplySession(SessionKey encryptKey, SessionTag encryptTag) { - if (_replyKey != null || _replyTag != null) + if (_replyKey != null || _replyTag != null || + _ratchetReplyTag != null || _ratchetPubKey != null) throw new IllegalStateException(); _replyKey = encryptKey; _replyTag = encryptTag; } + /** + * The included session tag or null if unset + * + * @since 0.9.46 + */ + public RatchetSessionTag getRatchetReplyTag() { return _ratchetReplyTag; } + + /** + * Ratchet + * + * @throws IllegalStateException if key or tag previously set, to protect saved checksum + * @param encryptKey non-null + * @param encryptTag non-null + * @since 0.9.46 + */ + public void setReplySession(SessionKey encryptKey, RatchetSessionTag encryptTag) { + if (_replyKey != null || _replyTag != null || + _ratchetReplyTag != null || _ratchetPubKey != null) + throw new IllegalStateException(); + _replyKey = encryptKey; + _ratchetReplyTag = encryptTag; + } + + /** + * The included session key or null if unset. + * Preliminary, not fully supported, see proposal 154. + * + * @since 0.9.46 + */ + public PublicKey getRatchetPublicKey() { return _ratchetPubKey; } + + /** + * Ratchet. + * Preliminary, not fully supported, see proposal 154. + * + * @throws IllegalStateException if key or tag previously set, to protect saved checksum + * @param encryptKey non-null + * @param encryptTag non-null + * @since 0.9.46 + */ + public void setReplySession(PublicKey pubKey) { + _ratchetPubKey = pubKey; + } + /** * Set of peers that a lookup reply should NOT include. * WARNING - returns a copy. @@ -329,6 +394,7 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { // TODO store the whole flag byte boolean tunnelSpecified = (data[curIndex] & FLAG_TUNNEL) != 0; boolean replyKeySpecified = (data[curIndex] & FLAG_ENCRYPT) != 0; + boolean ratchetSpecified = (data[curIndex] & FLAG_RATCHET) != 0; switch (data[curIndex] & FLAG_TYPE_MASK) { case FLAG_TYPE_LS: _type = Type.LS; @@ -365,20 +431,31 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { peers.add(p); } _dontIncludePeers = peers; - if (replyKeySpecified) { + if (replyKeySpecified || ratchetSpecified) { + // all 3 flavors are 32 bytes byte[] rk = new byte[SessionKey.KEYSIZE_BYTES]; System.arraycopy(data, curIndex, rk, 0, SessionKey.KEYSIZE_BYTES); - _replyKey = new SessionKey(rk); + if (replyKeySpecified && ratchetSpecified) + _ratchetPubKey = new PublicKey(EncType.ECIES_X25519, rk); + else + _replyKey = new SessionKey(rk); curIndex += SessionKey.KEYSIZE_BYTES; - // number of tags, assume always 1 for now - curIndex++; - byte[] rt = new byte[SessionTag.BYTE_LENGTH]; - System.arraycopy(data, curIndex, rt, 0, SessionTag.BYTE_LENGTH); - _replyTag = new SessionTag(rt); + if (!(replyKeySpecified && ratchetSpecified)) { + // number of tags, assume always 1 for now + curIndex++; + if (replyKeySpecified) { + byte[] rt = new byte[SessionTag.BYTE_LENGTH]; + System.arraycopy(data, curIndex, rt, 0, SessionTag.BYTE_LENGTH); + _replyTag = new SessionTag(rt); + } else { + byte[] rt = new byte[RatchetSessionTag.LENGTH]; + System.arraycopy(data, curIndex, rt, 0, RatchetSessionTag.LENGTH); + _ratchetReplyTag = new RatchetSessionTag(rt); + } + } } } - protected int calculateWrittenLength() { int totalLength = 0; totalLength += Hash.HASH_LENGTH*2; // key+fromHash @@ -388,9 +465,17 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { totalLength += 2; // numPeers if (_dontIncludePeers != null) totalLength += Hash.HASH_LENGTH * _dontIncludePeers.size(); - if (_replyKey != null) + if (_replyKey != null) { // number of tags, assume always 1 for now - totalLength += SessionKey.KEYSIZE_BYTES + 1 + SessionTag.BYTE_LENGTH; + totalLength += SessionKey.KEYSIZE_BYTES + 1; + if (_ratchetReplyTag != null) + totalLength += RatchetSessionTag.LENGTH; + else + totalLength += SessionTag.BYTE_LENGTH; + } else if (_ratchetPubKey != null) { + totalLength += 32; + // no tags + } return totalLength; } @@ -404,8 +489,12 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { curIndex += Hash.HASH_LENGTH; // Generate the flag byte byte flag; - if (_replyKey != null) + if (_replyTag != null) flag = FLAG_ENCRYPT; + else if (_ratchetReplyTag != null) + flag = FLAG_RATCHET; + else if (_ratchetPubKey != null) + flag = FLAG_RATCHET | FLAG_ENCRYPT; else flag = 0; switch (_type) { @@ -450,8 +539,17 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { curIndex += SessionKey.KEYSIZE_BYTES; // number of tags, always 1 for now out[curIndex++] = 1; - System.arraycopy(_replyTag.getData(), 0, out, curIndex, SessionTag.BYTE_LENGTH); - curIndex += SessionTag.BYTE_LENGTH; + if (_replyTag != null) { + System.arraycopy(_replyTag.getData(), 0, out, curIndex, SessionTag.BYTE_LENGTH); + curIndex += SessionTag.BYTE_LENGTH; + } else { + System.arraycopy(_ratchetReplyTag.getData(), 0, out, curIndex, RatchetSessionTag.LENGTH); + curIndex += RatchetSessionTag.LENGTH; + } + } else if (_ratchetPubKey != null) { + System.arraycopy(_ratchetPubKey.getData(), 0, out, curIndex, _ratchetPubKey.length()); + curIndex += _ratchetPubKey.length(); + // no tags } return curIndex; } @@ -499,6 +597,8 @@ public class DatabaseLookupMessage extends FastI2NPMessageImpl { buf.append("\n\tReply Key: ").append(_replyKey); if (_replyTag != null) buf.append("\n\tReply Tag: ").append(_replyTag); + else if (_ratchetReplyTag != null) + buf.append("\n\tRatchetReply Tag: ").append(_ratchetReplyTag); if (_dontIncludePeers != null) { buf.append("\n\tDon't Include Peers: "); buf.append(_dontIncludePeers.size()); -- GitLab