Ratchet: Next Key WIP, got first ratchet working

Remove more ElG tag code from SKM
This commit is contained in:
zzz
2020-04-04 11:56:25 +00:00
parent 2ec9a4ab64
commit 8c4410277d
4 changed files with 165 additions and 158 deletions

View File

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

View File

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

View File

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

View File

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