Ratchet: Bob transition to ES

Store PK in RTS
New RST constructor
Log tweaks (lots)
This commit is contained in:
zzz
2019-11-05 14:13:35 +00:00
parent 3ae5b90c98
commit 4d1d11d1d4
5 changed files with 121 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

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