forked from I2P_Developers/i2p.i2p
Ratchet: Next Key WIP, got first ratchet working
Remove more ElG tag code from SKM
This commit is contained in:
@ -146,13 +146,14 @@ public class MuxedSKM extends SessionKeyManager {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ElG only
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) {
|
public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) {
|
||||||
EncType type = target.getType();
|
EncType type = target.getType();
|
||||||
if (type == EncType.ELGAMAL_2048)
|
if (type == EncType.ELGAMAL_2048)
|
||||||
return _elg.tagsDelivered(target, key, sessionTags);
|
return _elg.tagsDelivered(target, key, sessionTags);
|
||||||
if (type == EncType.ECIES_X25519)
|
|
||||||
return _ec.tagsDelivered(target, key, sessionTags);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,21 +196,23 @@ public class MuxedSKM extends SessionKeyManager {
|
|||||||
_ec.renderStatusHTML(out);
|
_ec.renderStatusHTML(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ElG only
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {
|
public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {
|
||||||
EncType type = target.getType();
|
EncType type = target.getType();
|
||||||
if (type == EncType.ELGAMAL_2048)
|
if (type == EncType.ELGAMAL_2048)
|
||||||
_elg.failTags(target, key, ts);
|
_elg.failTags(target, key, ts);
|
||||||
else if (type == EncType.ECIES_X25519)
|
|
||||||
_ec.failTags(target, key, ts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ElG only
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {
|
public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {
|
||||||
EncType type = target.getType();
|
EncType type = target.getType();
|
||||||
if (type == EncType.ELGAMAL_2048)
|
if (type == EncType.ELGAMAL_2048)
|
||||||
_elg.tagsAcked(target, key, ts);
|
_elg.tagsAcked(target, key, ts);
|
||||||
else if (type == EncType.ECIES_X25519)
|
|
||||||
_ec.tagsAcked(target, key, ts);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,23 @@ class NextSessionKey extends PublicKey {
|
|||||||
return _isRequest;
|
return _isRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.46
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equals if keys are equal
|
||||||
|
* @since 0.9.46
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return super.equals(obj);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder(64);
|
StringBuilder buf = new StringBuilder(64);
|
||||||
|
@ -413,42 +413,11 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Take note of the fact that the given sessionTags associated with the key for
|
* @throws UnsupportedOperationException always
|
||||||
* encryption to the target have been sent. Whether to use the tags immediately
|
|
||||||
* (i.e. assume they will be received) or to wait until an ack, is implementation dependent.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @param sessionTags ignored, must be null
|
|
||||||
* @return the TagSetHandle. Caller MUST subsequently call failTags() or tagsAcked()
|
|
||||||
* with this handle. May be null.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) {
|
public TagSetHandle tagsDelivered(PublicKey target, SessionKey key, Set<SessionTag> sessionTags) {
|
||||||
// TODO
|
throw new UnsupportedOperationException();
|
||||||
if (!(key instanceof SessionKeyAndNonce)) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("Bad SK type");
|
|
||||||
//TODO
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
SessionKeyAndNonce sk = (SessionKeyAndNonce) key;
|
|
||||||
// if this is ever null, this is racy and needs synch
|
|
||||||
OutboundSession sess = getSession(target);
|
|
||||||
if (sess == null) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("No session for delivered RatchetTagSet to target: " + toString(target));
|
|
||||||
// TODO
|
|
||||||
createSession(target, key);
|
|
||||||
} else {
|
|
||||||
sess.setCurrentKey(key);
|
|
||||||
}
|
|
||||||
// TODO
|
|
||||||
RatchetTagSet set = new RatchetTagSet(_hkdf, key, key, _context.clock().now(), 0);
|
|
||||||
sess.addTags(set);
|
|
||||||
if (_log.shouldDebug())
|
|
||||||
_log.debug("Tags delivered: " + set +
|
|
||||||
" target: " + toString(target) /** + ": " + sessionTags */ );
|
|
||||||
return set;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -457,55 +426,28 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
* from corrupted tag sets and crashes
|
* from corrupted tag sets and crashes
|
||||||
*
|
*
|
||||||
* @deprecated unused and rather drastic
|
* @deprecated unused and rather drastic
|
||||||
|
* @throws UnsupportedOperationException always
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void failTags(PublicKey target) {
|
public void failTags(PublicKey target) {
|
||||||
removeSession(target);
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark these tags as invalid, since the peer
|
* @throws UnsupportedOperationException always
|
||||||
* has failed to ack them in time.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {
|
public void failTags(PublicKey target, SessionKey key, TagSetHandle ts) {
|
||||||
OutboundSession sess = getSession(target);
|
throw new UnsupportedOperationException();
|
||||||
if (sess == null) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("No session for failed RatchetTagSet: " + ts);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!key.equals(sess.getCurrentKey())) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("Wrong session key (wanted " + sess.getCurrentKey() + ") for failed RatchetTagSet: " + ts);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("TagSet failed: " + ts);
|
|
||||||
sess.failTags((RatchetTagSet)ts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark these tags as acked, start to use them (if we haven't already)
|
* @throws UnsupportedOperationException always
|
||||||
* If the set was previously failed, it will be added back in.
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {
|
public void tagsAcked(PublicKey target, SessionKey key, TagSetHandle ts) {
|
||||||
OutboundSession sess = getSession(target);
|
throw new UnsupportedOperationException();
|
||||||
if (sess == null) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("No session for acked RatchetTagSet: " + ts);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(!key.equals(sess.getCurrentKey())) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("Wrong session key (wanted " + sess.getCurrentKey() + ") for acked RatchetTagSet: " + ts);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_log.shouldDebug())
|
|
||||||
_log.debug("TagSet acked: " + ts);
|
|
||||||
sess.ackTags((RatchetTagSet)ts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -597,9 +539,9 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
}
|
}
|
||||||
if (_log.shouldDebug()) {
|
if (_log.shouldDebug()) {
|
||||||
if (state != null)
|
if (state != null)
|
||||||
_log.debug("IB NSR Tag consumed: " + tag.toBase64() + " from: " + tagSet);
|
_log.debug("IB NSR Tag " + key.getNonce() + " consumed: " + tag.toBase64() + " from\n" + tagSet);
|
||||||
else
|
else
|
||||||
_log.debug("IB ES Tag consumed: " + tag.toBase64() + " from: " + tagSet);
|
_log.debug("IB ES Tag " + key.getNonce() + " consumed: " + tag.toBase64() + " from\n" + tagSet);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
@ -840,7 +782,6 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
totalSets += sets.size();
|
totalSets += sets.size();
|
||||||
buf.append("<tr class=\"debug_outboundtarget\"><td><div class=\"debug_targetinfo\"><b>Target public key:</b> ").append(toString(sess.getTarget())).append("<br>" +
|
buf.append("<tr class=\"debug_outboundtarget\"><td><div class=\"debug_targetinfo\"><b>Target public key:</b> ").append(toString(sess.getTarget())).append("<br>" +
|
||||||
"<b>Established:</b> ").append(DataHelper.formatDuration2(now - sess.getEstablishedDate())).append(" ago<br>" +
|
"<b>Established:</b> ").append(DataHelper.formatDuration2(now - sess.getEstablishedDate())).append(" ago<br>" +
|
||||||
"<b>Ack Received?</b> ").append(sess.getAckReceived()).append("<br>" +
|
|
||||||
"<b>Last Used:</b> ").append(DataHelper.formatDuration2(now - sess.getLastUsedDate())).append(" ago<br>");
|
"<b>Last Used:</b> ").append(DataHelper.formatDuration2(now - sess.getLastUsedDate())).append(" ago<br>");
|
||||||
SessionKey sk = sess.getCurrentKey();
|
SessionKey sk = sess.getCurrentKey();
|
||||||
if (sk != null)
|
if (sk != null)
|
||||||
@ -938,11 +879,17 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
private int _myIBKeyID = -1;
|
private int _myIBKeyID = -1;
|
||||||
private int _hisIBKeyID = -1;
|
private int _hisIBKeyID = -1;
|
||||||
private int _currentIBTagSetID;
|
private int _currentIBTagSetID;
|
||||||
|
private int _myIBKeySendCount;
|
||||||
|
private KeyPair _myIBKeys;
|
||||||
|
private NextSessionKey _myIBKey;
|
||||||
|
private SessionKey _nextIBRootKey;
|
||||||
|
private static final String INFO_7 = "XDHRatchetTagSet";
|
||||||
|
|
||||||
private static final int MAX_FAILS = 2;
|
private static final int MAX_FAILS = 2;
|
||||||
private static final int MAX_SEND_ACKS = 8;
|
private static final int MAX_SEND_ACKS = 8;
|
||||||
private static final int DEBUG_OB_NSR = 0x10001;
|
private static final int DEBUG_OB_NSR = 0x10001;
|
||||||
private static final int DEBUG_IB_NSR = 0x10002;
|
private static final int DEBUG_IB_NSR = 0x10002;
|
||||||
|
private static final int MAX_SEND_REVERSE_KEY = 25;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param key may be null
|
* @param key may be null
|
||||||
@ -974,7 +921,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
_tagSets.add(tagset);
|
_tagSets.add(tagset);
|
||||||
_state = null;
|
_state = null;
|
||||||
if (_log.shouldDebug())
|
if (_log.shouldDebug())
|
||||||
_log.debug("New OB Session, rk = " + rk + " tk = " + tk + " 1st tagset: " + tagset);
|
_log.debug("New OB Session, rk = " + rk + " tk = " + tk + " 1st tagset:\n" + tagset);
|
||||||
} else {
|
} else {
|
||||||
// We are Alice
|
// We are Alice
|
||||||
// This is an OUTBOUND NS, we make an INBOUND tagset for the NSR
|
// This is an OUTBOUND NS, we make an INBOUND tagset for the NSR
|
||||||
@ -985,7 +932,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
// store the state so we can find the right session when we receive the NSR
|
// store the state so we can find the right session when we receive the NSR
|
||||||
_state = state;
|
_state = state;
|
||||||
if (_log.shouldDebug())
|
if (_log.shouldDebug())
|
||||||
_log.debug("New IB Session, rk = " + rk + " tk = " + tk + " 1st tagset: " + tagset);
|
_log.debug("New IB Session, rk = " + rk + " tk = " + tk + " 1st tagset:\n" + tagset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,8 +962,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, rk, new SessionKey(k_ba),
|
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, rk, new SessionKey(k_ba),
|
||||||
now, 0);
|
now, 0);
|
||||||
if (_log.shouldDebug()) {
|
if (_log.shouldDebug()) {
|
||||||
_log.debug("Update IB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset: " + tagset_ab);
|
_log.debug("Update IB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset:\n" + tagset_ab);
|
||||||
_log.debug("Pending OB Session, rk = " + rk + " tk = " + Base64.encode(k_ba) + " ES tagset: " + tagset_ba);
|
_log.debug("Pending OB Session, rk = " + rk + " tk = " + Base64.encode(k_ba) + " ES tagset:\n" + tagset_ba);
|
||||||
}
|
}
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
_unackedTagSets.add(tagset_ba);
|
_unackedTagSets.add(tagset_ba);
|
||||||
@ -1032,8 +979,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
now, 0,
|
now, 0,
|
||||||
MIN_RCV_WINDOW_ES, MAX_RCV_WINDOW_ES);
|
MIN_RCV_WINDOW_ES, MAX_RCV_WINDOW_ES);
|
||||||
if (_log.shouldDebug()) {
|
if (_log.shouldDebug()) {
|
||||||
_log.debug("Update OB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset: " + tagset_ab);
|
_log.debug("Update OB Session, rk = " + rk + " tk = " + Base64.encode(k_ab) + " ES tagset:\n" + tagset_ab);
|
||||||
_log.debug("Update IB Session, rk = " + rk + " tk = " + Base64.encode(k_ba) + " ES tagset: " + tagset_ba);
|
_log.debug("Update IB Session, rk = " + rk + " tk = " + Base64.encode(k_ba) + " ES tagset:\n" + tagset_ba);
|
||||||
}
|
}
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
_tagSets.add(tagset_ab);
|
_tagSets.add(tagset_ab);
|
||||||
@ -1062,27 +1009,58 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
// this is about my outbound tag set,
|
// this is about my outbound tag set,
|
||||||
// and is an ack of new key sent
|
// and is an ack of new key sent
|
||||||
if (_hisIBKeyID != id) {
|
if (_hisIBKeyID != id) {
|
||||||
if (_log.shouldWarn())
|
if (_hisIBKeyID != id - 1) {
|
||||||
_log.warn("Got new key id, ratchet OB " + id);
|
|
||||||
if (_hisIBKeyID != id + 1) {
|
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Got bad new key id OB? " + id);
|
_log.warn("Got nextkey id OB: " + id + " expected " + (_hisIBKeyID + 1));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (hasKey) {
|
if (!hasKey) {
|
||||||
KeyPair nextKeys = _context.keyGenerator().generatePKIKeys(EncType.ECIES_X25519);
|
|
||||||
PublicKey pub = nextKeys.getPublic();
|
|
||||||
PrivateKey priv = nextKeys.getPrivate();
|
|
||||||
PrivateKey sharedSecret = ECIESAEADEngine.doDH(priv, key);
|
|
||||||
// create new OB TS
|
|
||||||
// find current OB TS, and delete it
|
|
||||||
} else {
|
|
||||||
// TODO get it from above
|
// TODO get it from above
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Got nextkey w/o key but we don't have it " + id);
|
_log.warn("Got nextkey OB w/o key but we don't have it " + id);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
int oldtsID = 1 + _myIBKeyID + id;
|
||||||
|
RatchetTagSet oldts = null;
|
||||||
|
for (RatchetTagSet ts : _tagSets) {
|
||||||
|
if (ts.getID() == oldtsID) {
|
||||||
|
oldts = ts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldts == null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got nextkey id OB " + id + " but can't find existing OB tagset " + oldtsID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KeyPair nextKeys = oldts.getNextKeys();
|
||||||
|
if (nextKeys == null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got nextkey id OB " + id + " but didn't send OB keys " + oldtsID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// create new OB TS, delete old one
|
||||||
|
int newtsID = oldtsID + 1;
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got nextkey id, ratchet OB: " + id);
|
||||||
|
PublicKey pub = nextKeys.getPublic();
|
||||||
|
PrivateKey priv = nextKeys.getPrivate();
|
||||||
|
PrivateKey sharedSecret = ECIESAEADEngine.doDH(priv, key);
|
||||||
|
byte[] sk = new byte[32];
|
||||||
|
_hkdf.calculate(sharedSecret.getData(), ZEROLEN, INFO_7, sk);
|
||||||
|
SessionKey ssk = new SessionKey(sk);
|
||||||
|
RatchetTagSet ts = new RatchetTagSet(_hkdf, oldts.getNextRootKey(), ssk,
|
||||||
|
_context.clock().now(), newtsID);
|
||||||
|
_tagSets.add(ts);
|
||||||
|
_tagSets.remove(oldts);
|
||||||
|
_myOBKeyID++;
|
||||||
|
_hisIBKeyID = id;
|
||||||
|
_currentOBTagSetID = newtsID;
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got nextkey id " + id + " ratchet to new OB ES TS:\n" + ts);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Got dup new key id for OB " + id);
|
_log.warn("Got dup nextkey id for OB " + id);
|
||||||
}
|
}
|
||||||
if (isRequest) {
|
if (isRequest) {
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
@ -1092,21 +1070,44 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
} else {
|
} else {
|
||||||
// this is about my inbound tag set
|
// this is about my inbound tag set
|
||||||
if (_hisOBKeyID != id) {
|
if (_hisOBKeyID != id) {
|
||||||
if (_log.shouldWarn())
|
if (_hisOBKeyID != id - 1) {
|
||||||
_log.warn("Got new key id, ratchet IB " + id);
|
|
||||||
if (_hisOBKeyID != id + 1) {
|
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Got bad new key id IB? " + id);
|
_log.warn("Got nextkey id IB: " + id + " expected " + (_hisOBKeyID + 1));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!hasKey) {
|
if (!hasKey) {
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Got nextkey w/o key but we don't have it " + id);
|
_log.warn("Got nextkey IB w/o key but we don't have it " + id);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// find current OB TS, tell him to send ack
|
if (_nextIBRootKey == null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got nextkey IB but we don't have next root key " + id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO new key only needed every other time
|
||||||
|
_myIBKeys = _context.keyGenerator().generatePKIKeys(EncType.ECIES_X25519);
|
||||||
|
PrivateKey sharedSecret = ECIESAEADEngine.doDH(_myIBKeys.getPrivate(), key);
|
||||||
|
// store next key for sending via getReverseSendKey()
|
||||||
|
_myIBKeyID++;
|
||||||
|
_hisOBKeyID = id;
|
||||||
|
int newtsID = 1 + _myIBKeyID + id;
|
||||||
|
_currentOBTagSetID = newtsID;
|
||||||
|
_myIBKeySendCount = 0;
|
||||||
|
_myIBKey = new NextSessionKey(_myIBKeys.getPublic().getData(), _myIBKeyID, true, false);
|
||||||
// create new IB TS
|
// create new IB TS
|
||||||
|
byte[] sk = new byte[32];
|
||||||
|
_hkdf.calculate(sharedSecret.getData(), ZEROLEN, INFO_7, sk);
|
||||||
|
SessionKey ssk = new SessionKey(sk);
|
||||||
|
RatchetTagSet ts = new RatchetTagSet(_hkdf, RatchetSKM.this, _target, _nextIBRootKey, ssk,
|
||||||
|
_context.clock().now(), newtsID,
|
||||||
|
MIN_RCV_WINDOW_ES, MAX_RCV_WINDOW_ES);
|
||||||
|
_nextIBRootKey = ts.getNextRootKey();
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got nextkey id " + id + " ratchet to new IB ES TS:\n" + ts);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.warn("Got dup new key id for IB " + id);
|
_log.warn("Got dup nextkey id for IB " + id);
|
||||||
// find current OB TS, tell him to send ack if nec.
|
// find current OB TS, tell him to send ack if nec.
|
||||||
// create new IB TS if nec.
|
// create new IB TS if nec.
|
||||||
}
|
}
|
||||||
@ -1119,6 +1120,19 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse key to send, or null
|
||||||
|
* @since 0.9.46
|
||||||
|
*/
|
||||||
|
private NextSessionKey getReverseSendKey() {
|
||||||
|
if (_myIBKey == null)
|
||||||
|
return null;
|
||||||
|
if (_myIBKeySendCount > MAX_SEND_REVERSE_KEY)
|
||||||
|
return null;
|
||||||
|
_myIBKeySendCount++;
|
||||||
|
return _myIBKey;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First tag was received for this inbound (ES) tagset.
|
* First tag was received for this inbound (ES) tagset.
|
||||||
* Find the corresponding outbound (ES) tagset in _unackedTagSets,
|
* Find the corresponding outbound (ES) tagset in _unackedTagSets,
|
||||||
@ -1129,6 +1143,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
void firstTagConsumed(RatchetTagSet set) {
|
void firstTagConsumed(RatchetTagSet set) {
|
||||||
SessionKey sk = set.getAssociatedKey();
|
SessionKey sk = set.getAssociatedKey();
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
|
// save next root key
|
||||||
|
_nextIBRootKey = set.getNextRootKey();
|
||||||
for (RatchetTagSet obSet : _unackedTagSets) {
|
for (RatchetTagSet obSet : _unackedTagSets) {
|
||||||
if (obSet.getAssociatedKey().equals(sk)) {
|
if (obSet.getAssociatedKey().equals(sk)) {
|
||||||
if (_log.shouldDebug())
|
if (_log.shouldDebug())
|
||||||
@ -1165,33 +1181,6 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* got an ack for these tags
|
|
||||||
* For tagsets delivered after the session was acked, this is a nop
|
|
||||||
* because the tagset was originally placed directly on the acked list.
|
|
||||||
* If the set was previously failed, it will be added back in.
|
|
||||||
*/
|
|
||||||
void ackTags(RatchetTagSet set) {
|
|
||||||
synchronized (_tagSets) {
|
|
||||||
if (_unackedTagSets.remove(set)) {
|
|
||||||
// we could perhaps use it even if not previuosly in unacked,
|
|
||||||
// i.e. it was expired already, but _tagSets is a list not a set...
|
|
||||||
_tagSets.add(set);
|
|
||||||
} else if (!_tagSets.contains(set)) {
|
|
||||||
// add back (sucess after fail)
|
|
||||||
_tagSets.add(set);
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("Ack of unknown (previously failed?) tagset: " + set);
|
|
||||||
} else if (set.getAcked()) {
|
|
||||||
if (_log.shouldWarn())
|
|
||||||
_log.warn("Dup ack of tagset: " + set);
|
|
||||||
}
|
|
||||||
_acked = true;
|
|
||||||
_consecutiveFailures = 0;
|
|
||||||
}
|
|
||||||
set.setAcked();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** didn't get an ack for these tags */
|
/** didn't get an ack for these tags */
|
||||||
void failTags(RatchetTagSet set) {
|
void failTags(RatchetTagSet set) {
|
||||||
synchronized (_tagSets) {
|
synchronized (_tagSets) {
|
||||||
@ -1215,28 +1204,6 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
return _currentKey;
|
return _currentKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCurrentKey(SessionKey key) {
|
|
||||||
_lastUsed = _context.clock().now();
|
|
||||||
if (_currentKey != null) {
|
|
||||||
if (!_currentKey.equals(key)) {
|
|
||||||
synchronized (_tagSets) {
|
|
||||||
if (_log.shouldWarn()) {
|
|
||||||
int dropped = 0;
|
|
||||||
for (RatchetTagSet set : _tagSets) {
|
|
||||||
dropped += set.remaining();
|
|
||||||
}
|
|
||||||
_log.warn("Rekeyed from " + _currentKey + " to " + key
|
|
||||||
+ ": dropping " + dropped + " session tags", new Exception());
|
|
||||||
}
|
|
||||||
_acked = false;
|
|
||||||
_tagSets.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_currentKey = key;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getEstablishedDate() {
|
public long getEstablishedDate() {
|
||||||
return _established;
|
return _established;
|
||||||
}
|
}
|
||||||
@ -1285,8 +1252,8 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
|
|||||||
set.setDate(now);
|
set.setDate(now);
|
||||||
SessionKeyAndNonce skn = set.consumeNextKey();
|
SessionKeyAndNonce skn = set.consumeNextKey();
|
||||||
// TODO PN
|
// TODO PN
|
||||||
// TODO reverse next key
|
return new RatchetEntry(tag, skn, set.getID(), 0, set.getNextKey(),
|
||||||
return new RatchetEntry(tag, skn, set.getID(), 0, set.getNextKey(), null, getAcksToSend());
|
getReverseSendKey(), getAcksToSend());
|
||||||
} else if (_log.shouldInfo()) {
|
} else if (_log.shouldInfo()) {
|
||||||
_log.info("Removing empty " + set);
|
_log.info("Removing empty " + set);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ class RatchetTagSet implements TagSetHandle {
|
|||||||
private final SessionTagListener _lsnr;
|
private final SessionTagListener _lsnr;
|
||||||
private final PublicKey _remoteKey;
|
private final PublicKey _remoteKey;
|
||||||
protected final SessionKey _key;
|
protected final SessionKey _key;
|
||||||
|
// debug only, to be removed
|
||||||
|
private final SessionKey _tagsetKey;
|
||||||
|
// NSR only, else null
|
||||||
private final HandshakeState _state;
|
private final HandshakeState _state;
|
||||||
// inbound only, else null
|
// inbound only, else null
|
||||||
// We use object for tags because we must do indexOfValueByValue()
|
// We use object for tags because we must do indexOfValueByValue()
|
||||||
@ -130,6 +133,7 @@ class RatchetTagSet implements TagSetHandle {
|
|||||||
_state = state;
|
_state = state;
|
||||||
_remoteKey = remoteKey;
|
_remoteKey = remoteKey;
|
||||||
_key = rootKey;
|
_key = rootKey;
|
||||||
|
_tagsetKey = data;
|
||||||
_created = date;
|
_created = date;
|
||||||
_timeout = timeout;
|
_timeout = timeout;
|
||||||
_date = date;
|
_date = date;
|
||||||
@ -170,6 +174,7 @@ class RatchetTagSet implements TagSetHandle {
|
|||||||
_state = null;
|
_state = null;
|
||||||
_remoteKey = null;
|
_remoteKey = null;
|
||||||
_key = rootKey;
|
_key = rootKey;
|
||||||
|
_tagsetKey = null;
|
||||||
_created = date;
|
_created = date;
|
||||||
_timeout = timeout;
|
_timeout = timeout;
|
||||||
_date = date;
|
_date = date;
|
||||||
@ -300,15 +305,15 @@ class RatchetTagSet implements TagSetHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next Forward Key if applicable (we're running low).
|
* Next Forward Key if applicable (outbound ES and we're running low).
|
||||||
* Null if remaining is sufficient.
|
* Null if NSR or inbound or remaining is sufficient.
|
||||||
* Once non-null, will be constant for the remaining life of the tagset.
|
* Once non-null, will be constant for the remaining life of the tagset.
|
||||||
*
|
*
|
||||||
* @return key or null
|
* @return key or null
|
||||||
* @since 0.9.46
|
* @since 0.9.46
|
||||||
*/
|
*/
|
||||||
public NextSessionKey getNextKey() {
|
public NextSessionKey getNextKey() {
|
||||||
if (remaining() > LOW)
|
if (_sessionTags != null || _state != null || remaining() > LOW)
|
||||||
return null;
|
return null;
|
||||||
if (_nextKeys == null) {
|
if (_nextKeys == null) {
|
||||||
_nextKeys = I2PAppContext.getGlobalContext().keyGenerator().generatePKIKeys(EncType.ECIES_X25519);
|
_nextKeys = I2PAppContext.getGlobalContext().keyGenerator().generatePKIKeys(EncType.ECIES_X25519);
|
||||||
@ -331,6 +336,17 @@ class RatchetTagSet implements TagSetHandle {
|
|||||||
return _nextKeys;
|
return _nextKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Root key for the next DH ratchet.
|
||||||
|
* Should only be needed for ES, but valid for NSR also.
|
||||||
|
*
|
||||||
|
* @return key
|
||||||
|
* @since 0.9.46
|
||||||
|
*/
|
||||||
|
public SessionKey getNextRootKey() {
|
||||||
|
return new SessionKey(_nextRootKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* tags still available
|
* tags still available
|
||||||
* inbound only
|
* inbound only
|
||||||
@ -541,13 +557,17 @@ class RatchetTagSet implements TagSetHandle {
|
|||||||
else
|
else
|
||||||
buf.append("ES ");
|
buf.append("ES ");
|
||||||
buf.append("TagSet #").append(_tagSetID)
|
buf.append("TagSet #").append(_tagSetID)
|
||||||
.append(" keyID #").append(_id)
|
.append(" ID #").append(_id)
|
||||||
.append("\nCreated: ").append(DataHelper.formatTime(_created))
|
.append("\nCreated: ").append(DataHelper.formatTime(_created))
|
||||||
.append("\nLast use: ").append(DataHelper.formatTime(_date));
|
.append("\nLast use: ").append(DataHelper.formatTime(_date));
|
||||||
PublicKey pk = getRemoteKey();
|
PublicKey pk = getRemoteKey();
|
||||||
if (pk != null)
|
if (pk != null)
|
||||||
buf.append("\nRemote Public Key: ").append(pk.toBase64());
|
buf.append("\nRemote Public Key: ").append(pk.toBase64());
|
||||||
buf.append("\nRoot Symmetr. Key: ").append(_key.toBase64());
|
buf.append("\nRoot Key: ").append(_key.toBase64());
|
||||||
|
if (_tagsetKey != null)
|
||||||
|
buf.append("\nTagset Key: ").append(_tagsetKey.toBase64());
|
||||||
|
if (_nextKey != null)
|
||||||
|
buf.append("\nNext Key: ").append(_nextKey);
|
||||||
int sz = size();
|
int sz = size();
|
||||||
buf.append("\nSize: ").append(sz)
|
buf.append("\nSize: ").append(sz)
|
||||||
.append(" Orig: ").append(_originalSize)
|
.append(" Orig: ").append(_originalSize)
|
||||||
|
Reference in New Issue
Block a user