Ratchet: Add support for database lookup replies (proposal 154)

This commit is contained in:
zzz
2020-03-31 18:43:52 +00:00
parent 6dd0b23c61
commit 9307cc8a0c

View File

@ -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());