forked from I2P_Developers/i2p.i2p
Ratchet: Bob transition to ES
Store PK in RTS New RST constructor Log tweaks (lots)
This commit is contained in:
@ -161,11 +161,11 @@ public final class ECIESAEADEngine {
|
||||
HandshakeState state = key.getHandshakeState();
|
||||
if (state == null) {
|
||||
if (shouldDebug)
|
||||
_log.debug("Decrypting ES with tag: " + st + ": key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
_log.debug("Decrypting ES with tag: " + st.toBase64() + ": key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
decrypted = decryptExistingSession(tag, data, key, targetPrivateKey);
|
||||
} else if (data.length >= MIN_NSR_SIZE) {
|
||||
if (shouldDebug)
|
||||
_log.debug("Decrypting NSR with tag: " + st + ": key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
_log.debug("Decrypting NSR with tag: " + st.toBase64() + ": key: " + key.toBase64() + ": " + data.length + " bytes");
|
||||
decrypted = decryptNewSessionReply(tag, data, state, keyManager);
|
||||
} else {
|
||||
decrypted = null;
|
||||
@ -562,11 +562,11 @@ public final class ECIESAEADEngine {
|
||||
return null;
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Encrypting as NSR to " + target + " with tag " + re.tag);
|
||||
_log.debug("Encrypting as NSR to " + target + " with tag " + re.tag.toBase64());
|
||||
return encryptNewSessionReply(cloves, target, state, re.tag, keyManager);
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Encrypting as ES to " + target + " with key " + re.key + " and tag " + re.tag);
|
||||
_log.debug("Encrypting as ES to " + target + " with key " + re.key + " and tag " + re.tag.toBase64());
|
||||
byte rv[] = encryptExistingSession(cloves, target, re.key, re.tag);
|
||||
return rv;
|
||||
}
|
||||
@ -722,7 +722,7 @@ public final class ECIESAEADEngine {
|
||||
/**
|
||||
* No ad
|
||||
*/
|
||||
final byte[] encryptAEADBlock(byte data[], SessionKey key, long n) {
|
||||
private final byte[] encryptAEADBlock(byte data[], SessionKey key, long n) {
|
||||
return encryptAEADBlock(null, data, key, n);
|
||||
}
|
||||
|
||||
|
@ -176,8 +176,7 @@ public class MuxedSKM extends SessionKeyManager {
|
||||
public SessionKey consumeTag(SessionTag tag) {
|
||||
SessionKey rv = _elg.consumeTag(tag);
|
||||
if (rv == null) {
|
||||
byte[] stag = new byte[8];
|
||||
System.arraycopy(tag.getData(), 0, stag, 0, 8);
|
||||
long stag = RatchetPayload.fromLong8(tag.getData(), 0);
|
||||
RatchetSessionTag rstag = new RatchetSessionTag(stag);
|
||||
rv = _ec.consumeTag(rstag);
|
||||
}
|
||||
|
@ -178,9 +178,9 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
boolean rv = addSession(sess);
|
||||
if (_log.shouldInfo()) {
|
||||
if (rv)
|
||||
_log.info("New OB session as Bob. Alice: " + toString(target));
|
||||
_log.info("New OB session " + state.hashCode() + " as Bob. Alice: " + toString(target));
|
||||
else
|
||||
_log.info("Dup OB session as Bob. Alice: " + toString(target));
|
||||
_log.info("Dup OB session " + state.hashCode() + " as Bob. Alice: " + toString(target));
|
||||
}
|
||||
return rv;
|
||||
} else {
|
||||
@ -191,14 +191,14 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
if (pending != null) {
|
||||
pending.add(sess);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Another new OB session as Alice, total now: " + pending.size() +
|
||||
_log.info("Another new OB session " + state.hashCode() + " as Alice, total now: " + pending.size() +
|
||||
". Bob: " + toString(target));
|
||||
} else {
|
||||
pending = new ArrayList<OutboundSession>(4);
|
||||
pending.add(sess);
|
||||
_pendingOutboundSessions.put(target, pending);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("First new OB session as Alice. Bob: " + toString(target));
|
||||
_log.info("First new OB session " + state.hashCode() + " as Alice. Bob: " + toString(target));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -220,23 +220,25 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
boolean isInbound = state.getRole() == HandshakeState.RESPONDER;
|
||||
if (isInbound) {
|
||||
// we are Bob, NSR sent
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Session " + state.hashCode() + " update as Bob. Alice: " + toString(target));
|
||||
OutboundSession sess = getSession(target);
|
||||
if (sess == null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Update session but no session found for " + target);
|
||||
_log.debug("Update Bob session but no session found for " + target);
|
||||
// TODO can we recover?
|
||||
return false;
|
||||
}
|
||||
sess.updateSession(state);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Session update as Bob. Alice: " + toString(target));
|
||||
} else {
|
||||
// we are Alice, NSR received
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Session " + oldState.hashCode() + " to " + state.hashCode() + " update as Alice. Bob: " + toString(target));
|
||||
synchronized (_pendingOutboundSessions) {
|
||||
List<OutboundSession> pending = _pendingOutboundSessions.get(target);
|
||||
if (pending == null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Update session but no sessions found for " + target);
|
||||
_log.debug("Update Alice session but no pending sessions for " + target);
|
||||
// TODO can we recover?
|
||||
return false;
|
||||
}
|
||||
@ -250,7 +252,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
boolean ok = addSession(sess);
|
||||
if (_log.shouldDebug()) {
|
||||
if (ok)
|
||||
_log.debug("Update session from NSR to ES for " + target);
|
||||
_log.debug("Update Alice session from NSR to ES for " + target);
|
||||
else
|
||||
_log.debug("Session already updated from NSR to ES for " + target);
|
||||
}
|
||||
@ -266,16 +268,15 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
}
|
||||
}
|
||||
}
|
||||
_pendingOutboundSessions.remove(target);
|
||||
if (!found) {
|
||||
if (found) {
|
||||
_pendingOutboundSessions.remove(target);
|
||||
} else {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Update session but no session found (out of " + pending.size() + ") for " + target);
|
||||
_log.debug("Update Alice session but no session found (out of " + pending.size() + ") for " + target);
|
||||
// TODO can we recover?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Session update as Alice. Bob: " + toString(target));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -520,8 +521,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
SessionKeyAndNonce key;
|
||||
tagSet = _inboundTagSets.remove(tag);
|
||||
if (tagSet == null) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("IB tag not found: " + tag.toBase64());
|
||||
//if (_log.shouldDebug())
|
||||
// _log.debug("IB tag not found: " + tag.toBase64());
|
||||
return null;
|
||||
}
|
||||
boolean firstInbound;
|
||||
@ -535,14 +536,22 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
HandshakeState state = tagSet.getHandshakeState();
|
||||
if (firstInbound) {
|
||||
if (state == null) {
|
||||
// TODO
|
||||
// TODO this should really be after decrypt...
|
||||
PublicKey pk = tagSet.getRemoteKey();
|
||||
OutboundSession sess = getSession(pk);
|
||||
if (sess != null) {
|
||||
sess.firstTagConsumed(tagSet);
|
||||
} else {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("First tag consumed but session is gone");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_log.shouldDebug()) {
|
||||
if (state != null)
|
||||
_log.debug("IB NSR Tag consumed: " + tag + " from: " + tagSet);
|
||||
_log.debug("IB NSR Tag consumed: " + tag.toBase64() + " from: " + tagSet);
|
||||
else
|
||||
_log.debug("IB ES Tag consumed: " + tag + " from: " + tagSet);
|
||||
_log.debug("IB ES Tag consumed: " + tag.toBase64() + " from: " + tagSet);
|
||||
}
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
@ -838,12 +847,21 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
RatchetTagSet tagset = new RatchetTagSet(_hkdf, RatchetSKM.this, state,
|
||||
rk, tk,
|
||||
_established, _rcvTagSetID.getAndIncrement(), 5, 5);
|
||||
// store the IB tagset as OB so we can lookup the state
|
||||
// TODO just store the state
|
||||
_unackedTagSets.add(tagset);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("New IB Session, rk = " + rk + " tk = " + tk + " 1st tagset: " + tagset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inbound or outbound. Checks state.getRole() to determine.
|
||||
* For outbound (NSR rcvd by Alice), sets session to transition to ES mode outbound.
|
||||
* For inbound (NSR sent by Bob), sets up inbound ES tagset.
|
||||
*
|
||||
* @param state current state
|
||||
*/
|
||||
void updateSession(HandshakeState state) {
|
||||
byte[] ck = state.getChainingKey();
|
||||
byte[] k_ab = new byte[32];
|
||||
@ -855,7 +873,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
if (isInbound) {
|
||||
// We are Bob
|
||||
// This is an OUTBOUND NSR, we make an INBOUND tagset for ES
|
||||
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, RatchetSKM.this, rk, new SessionKey(k_ab),
|
||||
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, rk, new SessionKey(k_ab),
|
||||
now, _rcvTagSetID.getAndIncrement(), 5, 5);
|
||||
// and a pending outbound one
|
||||
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, rk, new SessionKey(k_ba),
|
||||
@ -873,7 +891,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, rk, new SessionKey(k_ab),
|
||||
now, _sentTagSetID.getAndIncrement());
|
||||
// and an inbound one
|
||||
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, RatchetSKM.this, rk, new SessionKey(k_ba),
|
||||
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, rk, new SessionKey(k_ba),
|
||||
now, _rcvTagSetID.getAndIncrement(), 5, 5);
|
||||
if (_log.shouldDebug()) {
|
||||
_log.debug("Update OB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset: " + tagset_ab);
|
||||
@ -888,9 +906,37 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of RatchetTagSet objects
|
||||
* First tag was received for this inbound (ES) tagset.
|
||||
* Find the corresponding outbound (ES) tagset in _unackedTagSets,
|
||||
* move it to _tagSets, and remove all others.
|
||||
*
|
||||
* @param set the inbound tagset
|
||||
*/
|
||||
void firstTagConsumed(RatchetTagSet set) {
|
||||
SessionKey sk = set.getAssociatedKey();
|
||||
synchronized (_tagSets) {
|
||||
for (RatchetTagSet obSet : _unackedTagSets) {
|
||||
if (obSet.getAssociatedKey().equals(sk)) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("First tag received from IB ES " + set +
|
||||
", promoting OB ES " + obSet);
|
||||
_unackedTagSets.clear();
|
||||
_tagSets.clear();
|
||||
_tagSets.add(obSet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("First tag received from IB ES " + set +
|
||||
" but no corresponding OB ES set found, unacked size: " + _unackedTagSets.size() +
|
||||
" acked size: " + _tagSets.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used only by renderStatusHTML().
|
||||
* It includes both acked and unacked RatchetTagSets.
|
||||
* @return list of RatchetTagSet objects
|
||||
*/
|
||||
List<RatchetTagSet> getTagSets() {
|
||||
List<RatchetTagSet> rv;
|
||||
|
@ -15,6 +15,10 @@ public class RatchetSessionTag {
|
||||
|
||||
private final long _data;
|
||||
|
||||
public RatchetSessionTag(long val) {
|
||||
_data = val;
|
||||
}
|
||||
|
||||
public RatchetSessionTag(byte val[]) {
|
||||
if (val.length != LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
|
@ -7,13 +7,16 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.southernstorm.noise.protocol.DHState;
|
||||
import com.southernstorm.noise.protocol.HandshakeState;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.EncType;
|
||||
import net.i2p.crypto.HKDF;
|
||||
import net.i2p.crypto.TagSetHandle;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.PublicKey;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
@ -32,6 +35,7 @@ import net.i2p.util.Log;
|
||||
*/
|
||||
class RatchetTagSet implements TagSetHandle {
|
||||
private final SessionTagListener _lsnr;
|
||||
private final PublicKey _remoteKey;
|
||||
private final SessionKey _key;
|
||||
private final HandshakeState _state;
|
||||
// We use object for tags because we must do indexOfValueByValue()
|
||||
@ -69,7 +73,7 @@ class RatchetTagSet implements TagSetHandle {
|
||||
*/
|
||||
public RatchetTagSet(HKDF hkdf, HandshakeState state, SessionKey rootKey, SessionKey data,
|
||||
long date, int id) {
|
||||
this(hkdf, null, state, rootKey, data, date, id, false, 0, 0);
|
||||
this(hkdf, null, state, null, rootKey, data, date, id, false, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -79,7 +83,7 @@ class RatchetTagSet implements TagSetHandle {
|
||||
*/
|
||||
public RatchetTagSet(HKDF hkdf, SessionKey rootKey, SessionKey data,
|
||||
long date, int id) {
|
||||
this(hkdf, null, null, rootKey, data, date, id, false, 0, 0);
|
||||
this(hkdf, null, null, null, rootKey, data, date, id, false, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +93,7 @@ class RatchetTagSet implements TagSetHandle {
|
||||
*/
|
||||
public RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, HandshakeState state, SessionKey rootKey, SessionKey data,
|
||||
long date, int id, int minSize, int maxSize) {
|
||||
this(hkdf, lsnr, state, rootKey, data, date, id, true, minSize, maxSize);
|
||||
this(hkdf, lsnr, state, null, rootKey, data, date, id, true, minSize, maxSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,19 +101,22 @@ class RatchetTagSet implements TagSetHandle {
|
||||
*
|
||||
* @param date For inbound: creation time
|
||||
*/
|
||||
public RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, SessionKey rootKey, SessionKey data,
|
||||
public RatchetTagSet(HKDF hkdf, SessionTagListener lsnr,
|
||||
PublicKey remoteKey, SessionKey rootKey, SessionKey data,
|
||||
long date, int id, int minSize, int maxSize) {
|
||||
this(hkdf, lsnr, null, rootKey, data, date, id, true, minSize, maxSize);
|
||||
this(hkdf, lsnr, null, remoteKey, rootKey, data, date, id, true, minSize, maxSize);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param date For inbound and outbound: creation time
|
||||
*/
|
||||
private RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, HandshakeState state, SessionKey rootKey, SessionKey data,
|
||||
private RatchetTagSet(HKDF hkdf, SessionTagListener lsnr, HandshakeState state,
|
||||
PublicKey remoteKey, SessionKey rootKey, SessionKey data,
|
||||
long date, int id, boolean isInbound, int minSize, int maxSize) {
|
||||
_lsnr = lsnr;
|
||||
_state = state;
|
||||
_remoteKey = remoteKey;
|
||||
_key = rootKey;
|
||||
_created = date;
|
||||
_date = date;
|
||||
@ -149,7 +156,24 @@ class RatchetTagSet implements TagSetHandle {
|
||||
}
|
||||
|
||||
/**
|
||||
* The identifier for the session..
|
||||
* The far-end's public key.
|
||||
* Valid for NSR and inbound ES tagsets.
|
||||
* Returns null for outbound ES tagsets.
|
||||
*/
|
||||
public PublicKey getRemoteKey() {
|
||||
if (_state != null) {
|
||||
DHState kp = _state.getRemotePublicKey();
|
||||
if (kp != null) {
|
||||
byte[] rv = new byte[32];
|
||||
kp.getPublicKey(rv, 0);
|
||||
return new PublicKey(EncType.ECIES_X25519, rv);
|
||||
}
|
||||
}
|
||||
return _remoteKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* The identifier for the session.
|
||||
* Not used for cryptographic operations after setup.
|
||||
*/
|
||||
public SessionKey getAssociatedKey() {
|
||||
@ -386,21 +410,28 @@ class RatchetTagSet implements TagSetHandle {
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
if (_sessionTags != null)
|
||||
buf.append("Inbound ");
|
||||
else
|
||||
buf.append("Outbound ");
|
||||
if (_state != null)
|
||||
buf.append("NSR ");
|
||||
buf.append("NSR ").append(_state.hashCode()).append(' ');
|
||||
else
|
||||
buf.append("ES ");
|
||||
buf.append("TagSet #").append(_id)
|
||||
.append(" created: ").append(DataHelper.formatTime(_created))
|
||||
.append(" last use: ").append(DataHelper.formatTime(_date));
|
||||
.append("\nCreated: ").append(DataHelper.formatTime(_created))
|
||||
.append("\nLast use: ").append(DataHelper.formatTime(_date));
|
||||
PublicKey pk = getRemoteKey();
|
||||
if (pk != null)
|
||||
buf.append("\nRemote Public Key: ").append(pk.toBase64());
|
||||
buf.append("\nRoot Symmetr. Key: ").append(_key.toBase64());
|
||||
int sz = size();
|
||||
buf.append(" Size: ").append(sz)
|
||||
buf.append("\nSize: ").append(sz)
|
||||
.append(" Orig: ").append(_originalSize)
|
||||
.append(" Max: ").append(_maxSize)
|
||||
.append(" Remaining: ").append(remaining());
|
||||
buf.append(" Acked? ").append(_acked);
|
||||
if (_sessionTags != null) {
|
||||
buf.append(" Inbound");
|
||||
for (int i = 0; i < sz; i++) {
|
||||
int n = _sessionTags.keyAt(i);
|
||||
RatchetSessionTag tag = _sessionTags.valueAt(i);
|
||||
@ -413,8 +444,6 @@ class RatchetTagSet implements TagSetHandle {
|
||||
buf.append("\tdeferred");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buf.append(" Outbound");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
Reference in New Issue
Block a user