Ratchet: Changes to match current proposal 144

Pass CloveSet to/from ECIESEngine
This commit is contained in:
zzz
2019-10-31 10:56:01 +00:00
parent a51ee8e745
commit 71411be6d9
7 changed files with 215 additions and 119 deletions

View File

@@ -30,7 +30,6 @@ import net.i2p.util.Log;
*/
public class GarlicClove extends DataStructureImpl {
//private final Log _log;
private static final long serialVersionUID = 1L;
private transient final I2PAppContext _context;
private DeliveryInstructions _instructions;
@@ -41,7 +40,6 @@ public class GarlicClove extends DataStructureImpl {
public GarlicClove(I2PAppContext context) {
_context = context;
//_log = context.logManager().getLog(GarlicClove.class);
_cloveId = -1;
}
@@ -66,14 +64,12 @@ public class GarlicClove extends DataStructureImpl {
}
/**
*
* @return length read
*/
public int readBytes(byte source[], int offset) throws DataFormatException {
int cur = offset;
_instructions = DeliveryInstructions.create(source, offset);
cur += _instructions.getSize();
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Read instructions: " + _instructions);
try {
I2NPMessageHandler handler = new I2NPMessageHandler(_context);
cur += handler.readMessage(source, cur);
@@ -85,17 +81,31 @@ public class GarlicClove extends DataStructureImpl {
cur += 4;
_expiration = DataHelper.fromDate(source, cur);
cur += DataHelper.DATE_LENGTH;
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration);
//_certificate = new Certificate();
//cur += _certificate.readBytes(source, cur);
_certificate = Certificate.create(source, cur);
cur += _certificate.size();
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Read cert: " + _certificate);
return cur - offset;
}
/**
* Short format for ECIES-Ratchet, saves 22 bytes.
* NTCP2-style header, no ID, no separate expiration, no cert.
*
* @since 0.9.44
*/
public void readBytesRatchet(byte source[], int offset, int len) throws DataFormatException {
_instructions = DeliveryInstructions.create(source, offset);
int isz = _instructions.getSize();
try {
I2NPMessageHandler handler = new I2NPMessageHandler(_context);
_msg = I2NPMessageImpl.fromRawByteArrayNTCP2(_context, source, offset + isz, len - isz, handler);
_cloveId = _msg.getUniqueId();
_expiration = new Date(_msg.getMessageExpiration());
_certificate = Certificate.NULL_CERT;
} catch (I2NPMessageException ime) {
throw new DataFormatException("Unable to read the message from a garlic clove", ime);
}
}
/**
* @deprecated unused, use byte array method to avoid copying
* @throws UnsupportedOperationException always
@@ -111,16 +121,8 @@ public class GarlicClove extends DataStructureImpl {
@Override
public byte[] toByteArray() {
byte rv[] = new byte[estimateSize()];
int offset = 0;
offset += _instructions.writeBytes(rv, offset);
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Wrote instructions: " + _instructions);
//offset += _msg.toByteArray(rv);
try {
byte m[] = _msg.toByteArray();
System.arraycopy(m, 0, rv, offset, m.length);
offset += m.length;
} catch (RuntimeException e) { throw new RuntimeException("Unable to write: " + _msg + ": " + e.getMessage()); }
int offset = _instructions.writeBytes(rv, 0);
offset = _msg.toByteArray(rv, offset);
DataHelper.toLong(rv, offset, 4, _cloveId);
offset += 4;
DataHelper.toDate(rv, offset, _expiration.getTime());
@@ -132,6 +134,28 @@ public class GarlicClove extends DataStructureImpl {
}
return rv;
}
/**
* Short format for ECIES-Ratchet, saves 22 bytes.
* NTCP2-style header, no ID, no separate expiration, no cert.
*
* @return new offset
* @since 0.9.44
*/
public int writeBytesRatchet(byte[] tgt, int offset) {
// returns length written
offset += _instructions.writeBytes(tgt, offset);
// returns new offset
offset = _msg.toRawByteArrayNTCP2(tgt, offset);
return offset;
}
/**
* @since 0.9.44
*/
public int getSizeRatchet() {
return _instructions.getSize() + _msg.getMessageSize() - 7;
}
public int estimateSize() {
return _instructions.getSize()

View File

@@ -19,6 +19,7 @@ import net.i2p.crypto.EncType;
import net.i2p.crypto.HKDF;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.Base64;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -26,7 +27,9 @@ import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.data.i2np.GarlicClove;
import static net.i2p.router.crypto.ratchet.RatchetPayload.*;
import net.i2p.router.message.CloveSet;
import net.i2p.util.Log;
import net.i2p.util.SimpleByteCache;
@@ -122,7 +125,7 @@ public final class ECIESAEADEngine {
*
* @return decrypted data or null on failure
*/
public byte[] decrypt(byte data[], PrivateKey targetPrivateKey, RatchetSKM keyManager) throws DataFormatException {
public CloveSet decrypt(byte data[], PrivateKey targetPrivateKey, RatchetSKM keyManager) throws DataFormatException {
if (targetPrivateKey.getType() != EncType.ECIES_X25519)
throw new IllegalArgumentException();
if (data == null) {
@@ -139,7 +142,7 @@ public final class ECIESAEADEngine {
System.arraycopy(data, 0, tag, 0, TAGLEN);
RatchetSessionTag st = new RatchetSessionTag(tag);
SessionKeyAndNonce key = keyManager.consumeTag(st);
byte decrypted[];
CloveSet decrypted;
final boolean shouldDebug = _log.shouldDebug();
if (key != null) {
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
@@ -150,6 +153,7 @@ public final class ECIESAEADEngine {
if (state != null) {
decrypted = decryptExistingSession(tag, data, key, targetPrivateKey);
} else if (data.length >= MIN_NSR_SIZE) {
/** TODO find the state
try {
state = state.clone();
} catch (CloneNotSupportedException e) {
@@ -158,6 +162,8 @@ public final class ECIESAEADEngine {
return null;
}
decrypted = decryptNewSessionReply(tag, data, state);
**/
decrypted = null;
} else {
decrypted = null;
if (_log.shouldWarn())
@@ -210,7 +216,7 @@ public final class ECIESAEADEngine {
* @param data 96 bytes minimum
* @return null if decryption fails
*/
private byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey)
private CloveSet decryptNewSession(byte data[], PrivateKey targetPrivateKey)
throws DataFormatException {
HandshakeState state;
try {
@@ -271,11 +277,16 @@ public final class ECIESAEADEngine {
} catch (Exception e) {
throw new DataFormatException("Msg 1 payload error", e);
}
if (pc.cloveSet == null) {
if (pc.cloveSet.isEmpty()) {
if (_log.shouldWarn())
_log.warn("No garlic block in NS payload");
}
return pc.cloveSet;
int num = pc.cloveSet.size();
// return non-null even if zero cloves
GarlicClove[] arr = new GarlicClove[num];
// msg id and expiration not checked in GarlicMessageReceiver
CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
return rv;
}
/**
@@ -298,7 +309,7 @@ public final class ECIESAEADEngine {
* @param state must have already been cloned
* @return null if decryption fails
*/
private byte[] decryptNewSessionReply(byte[] tag, byte[] data, HandshakeState state)
private CloveSet decryptNewSessionReply(byte[] tag, byte[] data, HandshakeState state)
throws DataFormatException {
// part 1 - handshake
byte[] yy = new byte[KEYLEN];
@@ -361,11 +372,16 @@ public final class ECIESAEADEngine {
}
RatchetTagSet tagset_ab = new RatchetTagSet(_hkdf, new SessionKey(ck), new SessionKey(k_ab), 0, 0);
RatchetTagSet tagset_ba = new RatchetTagSet(_hkdf, null, new SessionKey(ck), new SessionKey(k_ba), 0, 0, 5, 5);
if (pc.cloveSet == null) {
if (pc.cloveSet.isEmpty()) {
if (_log.shouldWarn())
_log.warn("No garlic block in NSR payload");
}
return pc.cloveSet;
int num = pc.cloveSet.size();
// return non-null even if zero cloves
GarlicClove[] arr = new GarlicClove[num];
// msg id and expiration not checked in GarlicMessageReceiver
CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
return rv;
}
/**
@@ -385,7 +401,7 @@ public final class ECIESAEADEngine {
* @return decrypted data or null on failure
*
*/
private byte[] decryptExistingSession(byte[] tag, byte[] data, SessionKeyAndNonce key, PrivateKey targetPrivateKey)
private CloveSet decryptExistingSession(byte[] tag, byte[] data, SessionKeyAndNonce key, PrivateKey targetPrivateKey)
throws DataFormatException {
// TODO decrypt in place?
byte decrypted[] = decryptAEADBlock(tag, data, TAGLEN, data.length - TAGLEN, key, key.getNonce());
@@ -409,11 +425,16 @@ public final class ECIESAEADEngine {
} catch (Exception e) {
throw new DataFormatException("ES payload error", e);
}
if (pc.cloveSet == null) {
if (pc.cloveSet.isEmpty()) {
if (_log.shouldWarn())
_log.warn("No garlic block in ES payload");
}
return pc.cloveSet;
int num = pc.cloveSet.size();
// return non-null even if zero cloves
GarlicClove[] arr = new GarlicClove[num];
// msg id and expiration not checked in GarlicMessageReceiver
CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
return rv;
}
/**
@@ -476,12 +497,11 @@ public final class ECIESAEADEngine {
*
* @param target public key to which the data should be encrypted.
* @param priv local private key to encrypt with, from the leaseset
* @param expiration only used for new session messages
* @return encrypted data or null on failure
*
*/
public byte[] encrypt(byte data[], PublicKey target, PrivateKey priv,
RatchetSKM keyManager, long expiration) {
public byte[] encrypt(CloveSet cloves, PublicKey target, PrivateKey priv,
RatchetSKM keyManager) {
if (target.getType() != EncType.ECIES_X25519)
throw new IllegalArgumentException();
if (Arrays.equals(target.getData(), NULLPK)) {
@@ -494,7 +514,7 @@ public final class ECIESAEADEngine {
if (re == null) {
if (_log.shouldDebug())
_log.debug("Encrypting as NS to " + target);
return encryptNewSession(data, target, priv, keyManager, expiration);
return encryptNewSession(cloves, target, priv, keyManager);
}
////
byte[] tagsetkey = new byte[32];
@@ -513,9 +533,9 @@ public final class ECIESAEADEngine {
return null;
}
// register state with skm
return encryptNewSessionReply(data, state, re.tag);
return encryptNewSessionReply(cloves, state, re.tag);
}
byte rv[] = encryptExistingSession(data, target, re.key, re.tag);
byte rv[] = encryptExistingSession(cloves, target, re.key, re.tag);
return rv;
}
@@ -536,8 +556,8 @@ public final class ECIESAEADEngine {
*
* @return encrypted data or null on failure
*/
private byte[] encryptNewSession(byte data[], PublicKey target, PrivateKey priv,
RatchetSKM keyManager, long expiration) {
private byte[] encryptNewSession(CloveSet cloves, PublicKey target, PrivateKey priv,
RatchetSKM keyManager) {
HandshakeState state;
try {
state = new HandshakeState(HandshakeState.PATTERN_ID_IK, HandshakeState.INITIATOR, _edhThread);
@@ -549,22 +569,11 @@ public final class ECIESAEADEngine {
state.getLocalKeyPair().setPrivateKey(priv.getData(), 0);
state.start();
int padlen = 1 + _context.random().nextInt(MAXPAD);
byte[] payload = new byte[BHLEN + padlen + BHLEN + 4 + BHLEN + data.length];
List<Block> blocks = new ArrayList<Block>(4);
Block block = new DateTimeBlock(expiration);
blocks.add(block);
block = new GarlicBlock(data);
blocks.add(block);
block = new PaddingBlock(_context, padlen);
blocks.add(block);
int payloadlen = createPayload(payload, 0, blocks);
if (payloadlen != payload.length)
throw new IllegalStateException("payload size mismatch");
byte[] payload = createPayload(cloves, cloves.getExpiration());
byte[] enc = new byte[KEYLEN + KEYLEN + MACLEN + payloadlen + MACLEN];
byte[] enc = new byte[KEYLEN + KEYLEN + MACLEN + payload.length + MACLEN];
try {
state.writeMessage(enc, 0, payload, 0, payloadlen);
state.writeMessage(enc, 0, payload, 0, payload.length);
} catch (GeneralSecurityException gse) {
if (_log.shouldWarn())
_log.warn("Encrypt fail NS", gse);
@@ -607,23 +616,14 @@ public final class ECIESAEADEngine {
* @param state must have already been cloned
* @return encrypted data or null on failure
*/
private byte[] encryptNewSessionReply(byte data[], HandshakeState state, RatchetSessionTag currentTag) {
private byte[] encryptNewSessionReply(CloveSet cloves, HandshakeState state, RatchetSessionTag currentTag) {
byte[] tag = currentTag.getData();
state.mixHash(tag, 0, TAGLEN);
int padlen = 1 + _context.random().nextInt(MAXPAD);
byte[] payload = new byte[BHLEN + padlen + BHLEN + data.length];
List<Block> blocks = new ArrayList<Block>(2);
Block block = new GarlicBlock(data);
blocks.add(block);
block = new PaddingBlock(_context, padlen);
blocks.add(block);
int payloadlen = createPayload(payload, 0, blocks);
if (payloadlen != payload.length)
throw new IllegalStateException("payload size mismatch");
byte[] payload = createPayload(cloves, 0);
// part 1 - tag and empty payload
byte[] enc = new byte[TAGLEN + KEYLEN + MACLEN + payloadlen + MACLEN];
byte[] enc = new byte[TAGLEN + KEYLEN + MACLEN + payload.length + MACLEN];
System.arraycopy(tag, 0, enc, 0, TAGLEN);
try {
state.writeMessage(enc, TAGLEN, ZEROLEN, 0, 0);
@@ -685,19 +685,10 @@ public final class ECIESAEADEngine {
* @param target unused, this is AEAD encrypt only using the session key and tag
* @return encrypted data or null on failure
*/
private byte[] encryptExistingSession(byte data[], PublicKey target, SessionKeyAndNonce key,
private byte[] encryptExistingSession(CloveSet cloves, PublicKey target, SessionKeyAndNonce key,
RatchetSessionTag currentTag) {
byte rawTag[] = currentTag.getData();
int padlen = 1 + _context.random().nextInt(MAXPAD);
byte[] payload = new byte[BHLEN + padlen + BHLEN + data.length];
List<Block> blocks = new ArrayList<Block>(2);
Block block = new GarlicBlock(data);
blocks.add(block);
block = new PaddingBlock(_context, padlen);
blocks.add(block);
int payloadlen = createPayload(payload, 0, blocks);
if (payloadlen != payload.length)
throw new IllegalStateException("payload size mismatch");
byte[] payload = createPayload(cloves, 0);
byte encr[] = encryptAEADBlock(rawTag, payload, key, key.getNonce());
System.arraycopy(rawTag, 0, encr, 0, TAGLEN);
return encr;
@@ -741,11 +732,8 @@ public final class ECIESAEADEngine {
// payload stuff
/////////////////////////////////////////////////////////
private void processPayload(byte[] payload, int length, boolean isHandshake) throws Exception {
}
private class PLCallback implements RatchetPayload.PayloadCallback {
public byte[] cloveSet;
public final List<GarlicClove> cloveSet = new ArrayList<GarlicClove>(3);
public long datetime;
public void gotDateTime(long time) {
@@ -761,13 +749,10 @@ public final class ECIESAEADEngine {
_log.debug("Got OPTIONS block length " + options.length);
}
public void gotGarlic(byte[] data, int off, int len) {
public void gotGarlic(GarlicClove clove) {
if (_log.shouldDebug())
_log.debug("Got GARLIC block length " + len);
if (cloveSet != null)
throw new IllegalArgumentException("Multiple GARLIC blocks");
cloveSet = new byte[len];
System.arraycopy(data, off, cloveSet, 0, len);
_log.debug("Got GARLIC block");
cloveSet.add(clove);
}
public void gotTermination(int reason, long count) {
@@ -786,6 +771,38 @@ public final class ECIESAEADEngine {
}
}
/**
* @param expiration if greater than zero, add a DateTime block
*/
private byte[] createPayload(CloveSet cloves, long expiration) {
int count = cloves.getCloveCount();
int numblocks = count + 1;
if (expiration > 0)
numblocks++;
int len = 0;
List<Block> blocks = new ArrayList<Block>(numblocks);
if (expiration > 0) {
Block block = new DateTimeBlock(expiration);
blocks.add(block);
len += block.getTotalLength();
}
for (int i = 0; i < count; i++) {
GarlicClove clove = cloves.getClove(i);
Block block = new GarlicBlock(clove);
blocks.add(block);
len += block.getTotalLength();
}
int padlen = 1 + _context.random().nextInt(MAXPAD);
Block block = new PaddingBlock(_context, padlen);
blocks.add(block);
len += block.getTotalLength();
byte[] payload = new byte[len];
int payloadlen = createPayload(payload, 0, blocks);
if (payloadlen != len)
throw new IllegalStateException("payload size mismatch");
return payload;
}
/**
* @return the new offset
*/

View File

@@ -6,6 +6,7 @@ import net.i2p.crypto.EncType;
import net.i2p.data.DataFormatException;
import net.i2p.data.PrivateKey;
import net.i2p.router.RouterContext;
import net.i2p.router.message.CloveSet;
import net.i2p.util.Log;
/**
@@ -28,11 +29,11 @@ public final class MuxedEngine {
*
* @return decrypted data or null on failure
*/
public byte[] decrypt(byte data[], PrivateKey elgKey, PrivateKey ecKey, MuxedSKM keyManager) throws DataFormatException {
public CloveSet decrypt(byte data[], PrivateKey elgKey, PrivateKey ecKey, MuxedSKM keyManager) throws DataFormatException {
if (elgKey.getType() != EncType.ELGAMAL_2048 ||
ecKey.getType() != EncType.ECIES_X25519)
throw new IllegalArgumentException();
byte[] rv = null;
CloveSet rv = null;
boolean tryElg = false;
// See proposal 144
if (data.length >= 128) {
@@ -41,10 +42,20 @@ public final class MuxedEngine {
tryElg = true;
}
// Always try ElG first, for now
if (tryElg)
rv = _context.elGamalAESEngine().decrypt(data, elgKey, keyManager.getElgSKM());
if (rv == null)
rv = _context.eciesEngine().decrypt(data, ecKey, keyManager.getECSKM());
if (tryElg) {
byte[] dec = _context.elGamalAESEngine().decrypt(data, elgKey, keyManager.getElgSKM());
if (dec != null) {
try {
rv = _context.garlicMessageParser().readCloveSet(dec, 0);
} catch (DataFormatException dfe) {
if (_log.shouldWarn())
_log.warn("ElG decrypt failed, trying ECIES", dfe);
}
}
}
if (rv == null) {
rv = _context.eciesEngine().decrypt(data, ecKey, keyManager.getECSKM());
}
return rv;
}
}

View File

@@ -8,6 +8,7 @@ import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.i2np.GarlicClove;
import net.i2p.data.i2np.GarlicMessage;
import net.i2p.data.i2np.I2NPMessage;
import net.i2p.data.i2np.I2NPMessageException;
@@ -26,13 +27,13 @@ class RatchetPayload {
private static final int BLOCK_DATETIME = 0;
private static final int BLOCK_SESSIONID = 1;
private static final int BLOCK_GARLIC = 3;
private static final int BLOCK_TERMINATION = 4;
private static final int BLOCK_OPTIONS = 5;
private static final int BLOCK_MSGNUM = 6;
private static final int BLOCK_NEXTKEY = 7;
private static final int BLOCK_ACKKEY = 8;
private static final int BLOCK_REPLYDI = 9;
private static final int BLOCK_GARLIC = 11;
private static final int BLOCK_PADDING = 254;
/**
@@ -43,7 +44,7 @@ class RatchetPayload {
public interface PayloadCallback {
public void gotDateTime(long time) throws DataFormatException;
public void gotGarlic(byte[] data, int off, int len) throws DataFormatException;
public void gotGarlic(GarlicClove clove);
/**
* @param isHandshake true only for message 3 part 2
@@ -111,7 +112,9 @@ class RatchetPayload {
break;
case BLOCK_GARLIC:
cb.gotGarlic(payload, i, len);
GarlicClove clove = new GarlicClove(ctx);
clove.readBytesRatchet(payload, i, len);
cb.gotGarlic(clove);
break;
case BLOCK_TERMINATION:
@@ -200,20 +203,19 @@ class RatchetPayload {
}
public static class GarlicBlock extends Block {
private byte[] d;
private final GarlicClove c;
public GarlicBlock(byte[] data) {
public GarlicBlock(GarlicClove clove) {
super(BLOCK_GARLIC);
d = data;
c = clove;
}
public int getDataLength() {
return d.length;
return c.getSizeRatchet();
}
public int writeData(byte[] tgt, int off) {
System.arraycopy(d, 0, tgt, off, d.length);
return off + d.length;
return c.writeBytesRatchet(tgt, off);
}
}

View File

@@ -14,8 +14,9 @@ import net.i2p.data.i2np.GarlicClove;
/**
* Wrap up the data contained in a GarlicMessage after being decrypted
*
* @since public since 0.9.44, was package private
*/
class CloveSet {
public class CloveSet {
private final GarlicClove[] _cloves;
private final Certificate _cert;
private final long _msgId;

View File

@@ -16,6 +16,7 @@ import java.util.Set;
import net.i2p.crypto.EncType;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
@@ -261,7 +262,7 @@ public class GarlicMessageBuilder {
throw new IllegalArgumentException();
Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
GarlicMessage msg = new GarlicMessage(ctx);
byte cloveSet[] = buildCloveSet(ctx, config);
CloveSet cloveSet = buildECIESCloveSet(ctx, config);
LeaseSetKeys lsk = ctx.keyManager().getKeys(from);
if (lsk == null) {
if (log.shouldWarn())
@@ -285,7 +286,7 @@ public class GarlicMessageBuilder {
log.warn("No SKM for " + from.toBase32());
return null;
}
byte encData[] = ctx.eciesEngine().encrypt(cloveSet, target, priv, rskm, config.getExpiration());
byte encData[] = ctx.eciesEngine().encrypt(cloveSet, target, priv, rskm);
if (encData == null) {
if (log.shouldWarn())
log.warn("Encrypt fail for " + from.toBase32());
@@ -300,8 +301,8 @@ public class GarlicMessageBuilder {
return null;
}
if (log.shouldDebug())
log.debug("CloveSet (" + config.getCloveCount() + " cloves) for message " + msg.getUniqueId() + " is " + cloveSet.length
+ " bytes and encrypted message data is " + encData.length + " bytes");
log.debug("CloveSet (" + config.getCloveCount() + " cloves) for message " + msg.getUniqueId()
+ " encrypted message data is " + encData.length + " bytes");
return msg;
}
@@ -366,7 +367,7 @@ public class GarlicMessageBuilder {
return baos.toByteArray();
}
private static byte[] buildClove(RouterContext ctx, PayloadGarlicConfig config) throws DataFormatException, IOException {
private static byte[] buildClove(RouterContext ctx, PayloadGarlicConfig config) {
GarlicClove clove = new GarlicClove(ctx);
clove.setData(config.getPayload());
return buildCommonClove(clove, config);
@@ -397,17 +398,52 @@ public class GarlicMessageBuilder {
return buildCommonClove(clove, config);
}
private static byte[] buildCommonClove(GarlicClove clove, GarlicConfig config) throws DataFormatException, IOException {
private static byte[] buildCommonClove(GarlicClove clove, GarlicConfig config) {
clove.setCertificate(config.getCertificate());
clove.setCloveId(config.getId());
clove.setExpiration(new Date(config.getExpiration()));
clove.setInstructions(config.getDeliveryInstructions());
return clove.toByteArray();
/*
int size = clove.estimateSize();
ByteArrayOutputStream baos = new ByteArrayOutputStream(size);
clove.writeBytes(baos);
return baos.toByteArray();
*/
}
/**
* Build the unencrypted GarlicMessage specified by the config.
* It contains the number of cloves, followed by each clove,
* followed by a certificate, ID, and expiration date.
*
* @throws IllegalArgumentException on error
* @since 0.9.44
*/
private static CloveSet buildECIESCloveSet(RouterContext ctx, GarlicConfig config) {
GarlicClove[] arr;
if (config instanceof PayloadGarlicConfig) {
GarlicClove clove = buildECIESClove(ctx, (PayloadGarlicConfig)config);
arr = new GarlicClove[1];
arr[0] = clove;
} else {
int cnt = config.getCloveCount();
arr = new GarlicClove[cnt];
for (int i = 0; i < cnt; i++) {
GarlicConfig c = config.getClove(i);
if (c instanceof PayloadGarlicConfig) {
arr[i] = buildECIESClove(ctx, (PayloadGarlicConfig)c);
} else {
throw new IllegalArgumentException("Subclove IS NOT a payload garlic clove");
}
}
}
// GarlicConfig cert, ID, and expiration all ignored here
CloveSet rv = new CloveSet(arr, Certificate.NULL_CERT, config.getId(), config.getExpiration());
return rv;
}
private static GarlicClove buildECIESClove(RouterContext ctx, PayloadGarlicConfig config) {
GarlicClove clove = new GarlicClove(ctx);
clove.setData(config.getPayload());
clove.setCertificate(config.getCertificate());
clove.setCloveId(config.getId());
clove.setExpiration(new Date(config.getExpiration()));
clove.setInstructions(config.getDeliveryInstructions());
return clove;
}
}

View File

@@ -70,13 +70,15 @@ public class GarlicMessageParser {
_log.warn("No SKM to decrypt ECIES");
return null;
}
decrData = _context.eciesEngine().decrypt(encData, encryptionKey, rskm);
if (decrData != null) {
CloveSet rv = _context.eciesEngine().decrypt(encData, encryptionKey, rskm);
if (rv != null) {
if (_log.shouldWarn())
_log.warn("ECIES decrypt success, length: " + decrData.length);
_log.warn("ECIES decrypt success, cloves: " + rv.getCloveCount());
return rv;
} else {
if (_log.shouldWarn())
_log.warn("ECIES decrypt fail");
return null;
}
} else {
if (_log.shouldWarn())
@@ -108,10 +110,13 @@ public class GarlicMessageParser {
}
/**
* ElGamal only
*
* @param offset where in data to start
* @return non-null, throws on all errors
* @since public since 0.9.44
*/
private CloveSet readCloveSet(byte data[], int offset) throws DataFormatException {
public CloveSet readCloveSet(byte data[], int offset) throws DataFormatException {
int numCloves = data[offset] & 0xff;
offset++;
//if (_log.shouldLog(Log.DEBUG))