I2P Address: [http://git.idk.i2p]

Skip to content
Snippets Groups Projects
Commit acf3abb1 authored by zzz's avatar zzz
Browse files

Ratchet: Updates

- Modify NextKey, start of support (WIP)
- Don't expect DSM reply to ECIES destinations
- Debug setting to always sent ack request
parent c5435410
No related branches found
No related tags found
No related merge requests found
......@@ -62,6 +62,8 @@ public final class ECIESAEADEngine {
private static final int MIN_ENCRYPTED_SIZE = MIN_ES_SIZE;
private static final byte[] NULLPK = new byte[KEYLEN];
private static final int MAXPAD = 16;
// debug, send ACKREQ in every ES
private static final boolean ACKREQ_IN_ES = false;
private static final String INFO_0 = "SessionReplyTags";
private static final String INFO_6 = "AttachPayloadKDF";
......@@ -603,7 +605,7 @@ public final class ECIESAEADEngine {
}
if (_log.shouldDebug())
_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, replyDI);
byte rv[] = encryptExistingSession(cloves, target, re, replyDI);
return rv;
}
......@@ -640,7 +642,7 @@ public final class ECIESAEADEngine {
if (_log.shouldDebug())
_log.debug("State before encrypt new session: " + state);
byte[] payload = createPayload(cloves, cloves.getExpiration(), replyDI);
byte[] payload = createPayload(cloves, cloves.getExpiration(), replyDI, null);
byte[] enc = new byte[KEYLEN + KEYLEN + MACLEN + payload.length + MACLEN];
try {
......@@ -699,7 +701,7 @@ public final class ECIESAEADEngine {
if (_log.shouldDebug())
_log.debug("State after mixhash tag before encrypt new session reply: " + state);
byte[] payload = createPayload(cloves, 0, replyDI);
byte[] payload = createPayload(cloves, 0, replyDI, null);
// part 1 - tag and empty payload
byte[] enc = new byte[TAGLEN + KEYLEN + MACLEN + payload.length + MACLEN];
......@@ -762,11 +764,14 @@ public final class ECIESAEADEngine {
* @param replyDI non-null to request an ack, or null
* @return encrypted data or null on failure
*/
private byte[] encryptExistingSession(CloveSet cloves, PublicKey target, SessionKeyAndNonce key,
RatchetSessionTag currentTag,
private byte[] encryptExistingSession(CloveSet cloves, PublicKey target, RatchetEntry re,
DeliveryInstructions replyDI) {
byte rawTag[] = currentTag.getData();
byte[] payload = createPayload(cloves, 0, replyDI);
//
if (ACKREQ_IN_ES && replyDI == null)
replyDI = new DeliveryInstructions();
byte rawTag[] = re.tag.getData();
byte[] payload = createPayload(cloves, 0, replyDI, re.nextKey);
SessionKeyAndNonce key = re.key;
byte encr[] = encryptAEADBlock(rawTag, payload, key, key.getNonce());
System.arraycopy(rawTag, 0, encr, 0, TAGLEN);
return encr;
......@@ -843,12 +848,12 @@ public final class ECIESAEADEngine {
public void gotAck(int id, int n) {
if (_log.shouldDebug())
_log.debug("Got ACK block: " + n);
_log.debug("Got ACK block: " + id + " / " + n);
}
public void gotAckRequest(int id, DeliveryInstructions di) {
if (_log.shouldDebug())
_log.debug("Got ACK REQUEST block: " + di);
_log.debug("Got ACK REQUEST block: " + id + " / " + di);
ackRequested = true;
}
......@@ -872,13 +877,16 @@ public final class ECIESAEADEngine {
* @param expiration if greater than zero, add a DateTime block
* @param replyDI non-null to request an ack, or null
*/
private byte[] createPayload(CloveSet cloves, long expiration, DeliveryInstructions replyDI) {
private byte[] createPayload(CloveSet cloves, long expiration,
DeliveryInstructions replyDI, NextSessionKey nextKey) {
int count = cloves.getCloveCount();
int numblocks = count + 1;
if (expiration > 0)
numblocks++;
if (replyDI != null)
numblocks++;
if (nextKey != null)
numblocks++;
int len = 0;
List<Block> blocks = new ArrayList<Block>(numblocks);
if (expiration > 0) {
......@@ -886,6 +894,11 @@ public final class ECIESAEADEngine {
blocks.add(block);
len += block.getTotalLength();
}
if (nextKey != null) {
Block block = new NextKeyBlock(nextKey);
blocks.add(block);
len += block.getTotalLength();
}
for (int i = 0; i < count; i++) {
GarlicClove clove = cloves.getClove(i);
Block block = new GarlicBlock(clove);
......
package net.i2p.router.crypto.ratchet;
import net.i2p.data.SessionKey;
import net.i2p.crypto.EncType;
import net.i2p.data.PublicKey;
/**
* A session key and key ID.
* A X25519 key and key ID.
*
* @since 0.9.44
*/
class NextSessionKey extends SessionKey {
class NextSessionKey extends PublicKey {
private final int _id;
private final boolean _isReverse, _isRequest;
public NextSessionKey(byte[] data, int id) {
super(data);
public NextSessionKey(byte[] data, int id, boolean isReverse, boolean isRequest) {
super(EncType.ECIES_X25519, data);
_id = id;
_isReverse = isReverse;
_isRequest = isRequest;
}
public int getID() {
return _id;
}
/** @since 0.9.46 */
public boolean isReverse() {
return _isReverse;
}
/** @since 0.9.46 */
public boolean isRequest() {
return _isRequest;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(64);
buf.append("[NextSessionKey: ");
buf.append(toBase64());
buf.append(" ID: ").append(_id);
buf.append(" reverse? ").append(_isReverse);
buf.append(" request? ").append(_isRequest);
buf.append(']');
return buf.toString();
}
......
......@@ -136,12 +136,14 @@ class RatchetPayload {
case BLOCK_NEXTKEY:
{
if (len != 34)
if (len != 35)
throw new IOException("Bad length for NEXTKEY: " + len);
int id = (int) DataHelper.fromLong(payload, i, 2);
boolean isReverse = (payload[i] & 0x01) != 0;
boolean isRequest = (payload[i] & 0x02) != 0;
int id = (int) DataHelper.fromLong(payload, i + 1, 2);
byte[] data = new byte[32];
System.arraycopy(payload, i + 2, data, 0, 32);
NextSessionKey nsk = new NextSessionKey(data, id);
System.arraycopy(payload, i + 3, data, 0, 32);
NextSessionKey nsk = new NextSessionKey(data, id, isReverse, isRequest);
cb.gotNextKey(nsk);
}
break;
......@@ -350,13 +352,17 @@ class RatchetPayload {
}
public int getDataLength() {
return 34;
return 35;
}
public int writeData(byte[] tgt, int off) {
DataHelper.toLong(tgt, off, 2, next.getID());
System.arraycopy(next.getData(), 0, tgt, off + 2, 32);
return off + 34;
if (next.isReverse())
tgt[off] = 0x01;
if (next.isRequest())
tgt[off] |= 0x02;
DataHelper.toLong(tgt, off + 1, 2, next.getID());
System.arraycopy(next.getData(), 0, tgt, off + 3, 32);
return off + 35;
}
}
......
......@@ -1100,6 +1100,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
set.setDate(now);
SessionKeyAndNonce skn = set.consumeNextKey();
// TODO key ID and PN
// TODO next key
return new RatchetEntry(tag, skn, 0, 0);
} else if (_log.shouldInfo()) {
_log.info("Removing empty " + set);
......
......@@ -13,6 +13,7 @@ import com.southernstorm.noise.protocol.HandshakeState;
import net.i2p.I2PAppContext;
import net.i2p.crypto.EncType;
import net.i2p.crypto.HKDF;
import net.i2p.crypto.KeyPair;
import net.i2p.crypto.TagSetHandle;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
......@@ -56,6 +57,9 @@ class RatchetTagSet implements TagSetHandle {
private final byte[] _symmkey_constant;
private int _lastTag = -1;
private int _lastKey = -1;
private KeyPair _nextKeys;
private NextSessionKey _nextKey;
private boolean _nextKeyAcked;
private static final String INFO_1 = "KDFDHRatchetStep";
private static final String INFO_2 = "TagAndKeyGenKeys";
......@@ -65,6 +69,7 @@ class RatchetTagSet implements TagSetHandle {
private static final byte[] ZEROLEN = new byte[0];
private static final int TAGLEN = RatchetSessionTag.LENGTH;
private static final int MAX = 65535;
private static final int LOW = 50;
/**
* Outbound NSR Tagset
......@@ -241,6 +246,27 @@ class RatchetTagSet implements TagSetHandle {
return MAX - nextKey;
}
/**
* Next Key if applicable
* null if remaining is sufficient
*
* @return key or null
* @since 0.9.46
*/
public NextSessionKey getNextKey() {
if (remaining() > LOW)
return null;
if (_nextKeyAcked) // maybe not needed, keep sending until unused
return null;
if (_nextKeys == null) {
_nextKeys = I2PAppContext.getGlobalContext().keyGenerator().generatePKIKeys(EncType.ECIES_X25519);
boolean isIB = _sessionTags != null;
// TODO request only needed every other time
_nextKey = new NextSessionKey(_nextKeys.getPublic().getData(), 0, isIB, !isIB);
}
return _nextKey;
}
/**
* tags still available
* inbound only
......
......@@ -678,7 +678,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl {
SendSuccessJob onReply = null;
SendTimeoutJob onFail = null;
ReplySelector selector = null;
if (wantACK) {
if (wantACK && _encryptionKey.getType() == EncType.ELGAMAL_2048) {
TagSetHandle tsh = null;
if (!tags.isEmpty()) {
SessionKeyManager skm = getContext().clientManager().getClientSessionKeyManager(_from.calculateHash());
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment