Ratchet: Add support for zero key (prop. #144, WIP for prop. #156)

This commit is contained in:
zzz
2020-10-15 11:17:41 +00:00
parent 0ebca7e8e3
commit c1f531ea92
2 changed files with 75 additions and 25 deletions

View File

@ -548,6 +548,7 @@ public class HandshakeState implements Destroyable, Cloneable {
// Format the message. // Format the message.
try { try {
// Process tokens until the direction changes or the patten ends. // Process tokens until the direction changes or the patten ends.
loop:
for (;;) { for (;;) {
if (patternIndex >= pattern.length) { if (patternIndex >= pattern.length) {
// The pattern has finished, so the next action is "split". // The pattern has finished, so the next action is "split".
@ -627,6 +628,11 @@ public class HandshakeState implements Destroyable, Cloneable {
case Pattern.SS: case Pattern.SS:
{ {
// I2P N extension to IK
if (patternId.equals(PATTERN_ID_IK) &&
localKeyPair.isNullPublicKey()) {
break loop;
}
// DH operation with initiator and responder static keys. // DH operation with initiator and responder static keys.
mixDH(localKeyPair, remotePublicKey); mixDH(localKeyPair, remotePublicKey);
} }
@ -702,6 +708,7 @@ public class HandshakeState implements Destroyable, Cloneable {
// Process the message. // Process the message.
try { try {
// Process tokens until the direction changes or the patten ends. // Process tokens until the direction changes or the patten ends.
loop:
for (;;) { for (;;) {
if (patternIndex >= pattern.length) { if (patternIndex >= pattern.length) {
// The pattern has finished, so the next action is "split". // The pattern has finished, so the next action is "split".
@ -792,6 +799,11 @@ public class HandshakeState implements Destroyable, Cloneable {
case Pattern.SS: case Pattern.SS:
{ {
// I2P N extension to IK
if (patternId.equals(PATTERN_ID_IK) &&
remotePublicKey.isNullPublicKey()) {
break loop;
}
// DH operation with initiator and responder static keys. // DH operation with initiator and responder static keys.
mixDH(localKeyPair, remotePublicKey); mixDH(localKeyPair, remotePublicKey);
} }

View File

@ -401,19 +401,6 @@ public final class ECIESAEADEngine {
if (keyManager.isDuplicate(pk)) { if (keyManager.isDuplicate(pk)) {
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("Dup eph. key in IB NS: " + pk); _log.warn("Dup eph. key in IB NS: " + pk);
return NO_CLOVES;
}
byte[] alicePK = new byte[KEYLEN];
state.getRemotePublicKey().getPublicKey(alicePK, 0);
if (_log.shouldDebug()) {
_log.debug("NS decrypt success from PK " + Base64.encode(alicePK));
_log.debug("State after decrypt new session: " + state);
}
if (Arrays.equals(alicePK, NULLPK)) {
// TODO
if (_log.shouldWarn())
_log.warn("Zero static key in IB NS");
state.destroy(); state.destroy();
return NO_CLOVES; return NO_CLOVES;
} }
@ -447,10 +434,6 @@ public final class ECIESAEADEngine {
return NO_CLOVES; return NO_CLOVES;
} }
// tell the SKM
PublicKey alice = new PublicKey(EncType.ECIES_X25519, alicePK);
keyManager.createSession(alice, null, state, null);
if (pc.cloveSet.isEmpty()) { if (pc.cloveSet.isEmpty()) {
// this is legal // this is legal
if (_log.shouldDebug()) if (_log.shouldDebug())
@ -458,11 +441,26 @@ public final class ECIESAEADEngine {
state.destroy(); state.destroy();
return NO_CLOVES; return NO_CLOVES;
} }
byte[] alicePK = new byte[KEYLEN];
state.getRemotePublicKey().getPublicKey(alicePK, 0);
if (_log.shouldDebug()) {
_log.debug("NS decrypt success from PK " + Base64.encode(alicePK));
_log.debug("State after decrypt new session: " + state);
}
if (Arrays.equals(alicePK, NULLPK)) {
state.destroy();
} else {
// tell the SKM
PublicKey alice = new PublicKey(EncType.ECIES_X25519, alicePK);
keyManager.createSession(alice, null, state, null);
setResponseTimerNS(alice, pc.cloveSet, keyManager);
}
int num = pc.cloveSet.size(); int num = pc.cloveSet.size();
GarlicClove[] arr = new GarlicClove[num]; GarlicClove[] arr = new GarlicClove[num];
// msg id and expiration not checked in GarlicMessageReceiver // msg id and expiration not checked in GarlicMessageReceiver
CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime); CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
setResponseTimerNS(alice, pc.cloveSet, keyManager);
return rv; return rv;
} }
@ -713,8 +711,12 @@ public final class ECIESAEADEngine {
* This is the one called from GarlicMessageBuilder and is the primary entry point. * This is the one called from GarlicMessageBuilder and is the primary entry point.
* *
* @param target public key to which the data should be encrypted. * @param target public key to which the data should be encrypted.
* @param to ignored if priv is null
* @param priv local private key to encrypt with, from the leaseset * @param priv local private key to encrypt with, from the leaseset
* @param callback may be null, if non-null an ack will be requested (except NS/NSR) * may be null for anonymous (N-in-IK)
* @param keyManager ignored if priv is null
* @param callback may be null, if non-null an ack will be requested (except NS/NSR),
* ignored if priv is null
* @return encrypted data or null on failure * @return encrypted data or null on failure
* *
*/ */
@ -729,17 +731,28 @@ public final class ECIESAEADEngine {
} }
} }
/**
* @param to ignored if priv is null
* @param priv local private key to encrypt with, from the leaseset
* may be null for anonymous (N-in-IK)
* @param keyManager ignored if priv is null
* @param callback may be null, ignored if priv is null
*/
private byte[] x_encrypt(CloveSet cloves, PublicKey target, Destination to, PrivateKey priv, private byte[] x_encrypt(CloveSet cloves, PublicKey target, Destination to, PrivateKey priv,
RatchetSKM keyManager, RatchetSKM keyManager,
ReplyCallback callback) { ReplyCallback callback) {
if (target.getType() != EncType.ECIES_X25519) if (target.getType() != EncType.ECIES_X25519)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if (Arrays.equals(target.getData(), NULLPK)) { if (Arrays.equals(target.getData(), NULLPK)) {
// TODO
if (_log.shouldWarn()) if (_log.shouldWarn())
_log.warn("Zero static key target"); _log.warn("Zero static key target");
return null; return null;
} }
if (priv == null) {
if (_log.shouldDebug())
_log.debug("Encrypting as NS zero-key to " + target);
return encryptNewSession(cloves, target, null, null, null, null);
}
RatchetEntry re = keyManager.consumeNextAvailableTag(target); RatchetEntry re = keyManager.consumeNextAvailableTag(target);
if (re == null) { if (re == null) {
if (_log.shouldDebug()) if (_log.shouldDebug())
@ -781,7 +794,11 @@ public final class ECIESAEADEngine {
* - 16 byte MAC * - 16 byte MAC
* </pre> * </pre>
* *
* @param callback may be null * @param to ignored if priv is null
* @param priv local private key to encrypt with, from the leaseset
* may be null for anonymous (N-in-IK)
* @param keyManager ignored if priv is null
* @param callback may be null, ignored if priv is null
* @return encrypted data or null on failure * @return encrypted data or null on failure
*/ */
private byte[] encryptNewSession(CloveSet cloves, PublicKey target, Destination to, PrivateKey priv, private byte[] encryptNewSession(CloveSet cloves, PublicKey target, Destination to, PrivateKey priv,
@ -794,8 +811,12 @@ public final class ECIESAEADEngine {
throw new IllegalStateException("bad proto", gse); throw new IllegalStateException("bad proto", gse);
} }
state.getRemotePublicKey().setPublicKey(target.getData(), 0); state.getRemotePublicKey().setPublicKey(target.getData(), 0);
if (priv != null) {
state.getLocalKeyPair().setKeys(priv.getData(), 0, state.getLocalKeyPair().setKeys(priv.getData(), 0,
priv.toPublic().getData(), 0); priv.toPublic().getData(), 0);
} else {
state.getLocalKeyPair().setKeys(NULLPK, 0, NULLPK, 0);
}
state.start(); state.start();
if (_log.shouldDebug()) if (_log.shouldDebug())
_log.debug("State before encrypt new session: " + state); _log.debug("State before encrypt new session: " + state);
@ -826,8 +847,12 @@ public final class ECIESAEADEngine {
if (_log.shouldDebug()) if (_log.shouldDebug())
_log.debug("Elligator2 encoded eph. key: " + Base64.encode(enc, 0, 32)); _log.debug("Elligator2 encoded eph. key: " + Base64.encode(enc, 0, 32));
if (priv != null) {
// tell the SKM // tell the SKM
keyManager.createSession(target, to, state, callback); keyManager.createSession(target, to, state, callback);
} else {
state.destroy();
}
return enc; return enc;
} }
@ -963,6 +988,19 @@ public final class ECIESAEADEngine {
return encr; return encr;
} }
/**
* Encrypt the data to the target using the given key from an anonymous source,
* for netdb lookups.
* Called from MessageWrapper.
*
* @param target public key to which the data should be encrypted.
* @return encrypted data or null on failure
* @since 0.9.48
*/
public byte[] encrypt(CloveSet cloves, PublicKey target) {
return encrypt(cloves, target, null, null, null, null);
}
/** /**
* No ad * No ad
*/ */