forked from I2P_Developers/i2p.i2p
NTCP EstablishState:
- Prep for future enhancements by refactoring to a state machine model - Reduce object churn; use SimpleByteCache - Synchronization - Define some constants - More finals - Log tweaks
This commit is contained in:
@@ -78,8 +78,8 @@ class EstablishState {
|
|||||||
/** how long we expect _sz_aliceIdent_tsA_padding_aliceSig to be when its full */
|
/** how long we expect _sz_aliceIdent_tsA_padding_aliceSig to be when its full */
|
||||||
private int _sz_aliceIdent_tsA_padding_aliceSigSize;
|
private int _sz_aliceIdent_tsA_padding_aliceSigSize;
|
||||||
// alice receives (and bob sends)
|
// alice receives (and bob sends)
|
||||||
private byte _Y[];
|
private final byte _Y[];
|
||||||
private transient byte _e_hXY_tsB[];
|
private final byte _e_hXY_tsB[];
|
||||||
/** Bob's Timestamp in seconds */
|
/** Bob's Timestamp in seconds */
|
||||||
private transient long _tsB;
|
private transient long _tsB;
|
||||||
/** Alice's Timestamp in seconds */
|
/** Alice's Timestamp in seconds */
|
||||||
@@ -88,7 +88,7 @@ class EstablishState {
|
|||||||
|
|
||||||
/** previously received encrypted block (or the IV) */
|
/** previously received encrypted block (or the IV) */
|
||||||
private byte _prevEncrypted[];
|
private byte _prevEncrypted[];
|
||||||
/** current encrypted block we are reading */
|
/** current encrypted block we are reading (IB only) or an IV buf used at the end for OB */
|
||||||
private byte _curEncrypted[];
|
private byte _curEncrypted[];
|
||||||
/**
|
/**
|
||||||
* next index in _curEncrypted to write to (equals _curEncrypted length if the block is
|
* next index in _curEncrypted to write to (equals _curEncrypted length if the block is
|
||||||
@@ -106,27 +106,64 @@ class EstablishState {
|
|||||||
|
|
||||||
private final NTCPTransport _transport;
|
private final NTCPTransport _transport;
|
||||||
private final NTCPConnection _con;
|
private final NTCPConnection _con;
|
||||||
private boolean _corrupt;
|
|
||||||
/** error causing the corruption */
|
/** error causing the corruption */
|
||||||
private String _err;
|
private String _err;
|
||||||
/** exception causing the error */
|
/** exception causing the error */
|
||||||
private Exception _e;
|
private Exception _e;
|
||||||
private boolean _verified;
|
|
||||||
private boolean _confirmWritten;
|
|
||||||
private boolean _failedBySkew;
|
private boolean _failedBySkew;
|
||||||
|
|
||||||
private static final int MIN_RI_SIZE = 387;
|
private static final int MIN_RI_SIZE = 387;
|
||||||
private static final int MAX_RI_SIZE = 2048;
|
private static final int MAX_RI_SIZE = 2048;
|
||||||
|
|
||||||
|
private static final int AES_SIZE = 16;
|
||||||
|
private static final int XY_SIZE = 256;
|
||||||
|
private static final int HXY_SIZE = 32; //Hash.HASH_LENGTH;
|
||||||
|
private static final int HXY_TSB_PAD_SIZE = HXY_SIZE + 4 + 12; // 48
|
||||||
|
|
||||||
|
private State _state;
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
OB_INIT,
|
||||||
|
/** sent 1 */
|
||||||
|
OB_SENT_X,
|
||||||
|
/** sent 1, got 2 partial */
|
||||||
|
OB_GOT_Y,
|
||||||
|
/** sent 1, got 2 */
|
||||||
|
OB_GOT_HXY,
|
||||||
|
/** sent 1, got 2, sent 3 */
|
||||||
|
OB_SENT_RI,
|
||||||
|
/** sent 1, got 2, sent 3, got 4 */
|
||||||
|
OB_GOT_SIG,
|
||||||
|
|
||||||
|
IB_INIT,
|
||||||
|
/** got 1 partial */
|
||||||
|
IB_GOT_X,
|
||||||
|
/** got 1 */
|
||||||
|
IB_GOT_HX,
|
||||||
|
/** got 1, sent 2 */
|
||||||
|
IB_SENT_Y,
|
||||||
|
/** got 1, sent 2, got partial 3 */
|
||||||
|
IB_GOT_RI_SIZE,
|
||||||
|
/** got 1, sent 2, got 3 */
|
||||||
|
IB_GOT_RI,
|
||||||
|
|
||||||
|
/** OB: got and verified 4; IB: got and verified 3 and sent 4 */
|
||||||
|
VERIFIED,
|
||||||
|
CORRUPT
|
||||||
|
}
|
||||||
|
|
||||||
private EstablishState() {
|
private EstablishState() {
|
||||||
_context = null;
|
_context = null;
|
||||||
_log = null;
|
_log = null;
|
||||||
_X = null;
|
_X = null;
|
||||||
|
_Y = null;
|
||||||
_hX_xor_bobIdentHash = null;
|
_hX_xor_bobIdentHash = null;
|
||||||
_curDecrypted = null;
|
_curDecrypted = null;
|
||||||
_dh = null;
|
_dh = null;
|
||||||
_transport = null;
|
_transport = null;
|
||||||
_con = null;
|
_con = null;
|
||||||
|
_e_hXY_tsB = null;
|
||||||
|
_state = State.VERIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EstablishState(RouterContext ctx, NTCPTransport transport, NTCPConnection con) {
|
public EstablishState(RouterContext ctx, NTCPTransport transport, NTCPConnection con) {
|
||||||
@@ -135,20 +172,25 @@ class EstablishState {
|
|||||||
_transport = transport;
|
_transport = transport;
|
||||||
_con = con;
|
_con = con;
|
||||||
_dh = _transport.getDHBuilder();
|
_dh = _transport.getDHBuilder();
|
||||||
_hX_xor_bobIdentHash = new byte[Hash.HASH_LENGTH];
|
_hX_xor_bobIdentHash = SimpleByteCache.acquire(HXY_SIZE);
|
||||||
if (_con.isInbound()) {
|
if (_con.isInbound()) {
|
||||||
_X = new byte[256];
|
_X = SimpleByteCache.acquire(XY_SIZE);
|
||||||
|
_Y = _dh.getMyPublicValueBytes();
|
||||||
_sz_aliceIdent_tsA_padding_aliceSig = new ByteArrayOutputStream(512);
|
_sz_aliceIdent_tsA_padding_aliceSig = new ByteArrayOutputStream(512);
|
||||||
|
_prevEncrypted = SimpleByteCache.acquire(AES_SIZE);
|
||||||
|
_state = State.IB_INIT;
|
||||||
} else {
|
} else {
|
||||||
_X = _dh.getMyPublicValueBytes();
|
_X = _dh.getMyPublicValueBytes();
|
||||||
_Y = new byte[256];
|
_Y = SimpleByteCache.acquire(XY_SIZE);
|
||||||
ctx.sha().calculateHash(_X, 0, _X.length, _hX_xor_bobIdentHash, 0);
|
ctx.sha().calculateHash(_X, 0, XY_SIZE, _hX_xor_bobIdentHash, 0);
|
||||||
xor32(con.getRemotePeer().calculateHash().getData(), _hX_xor_bobIdentHash);
|
xor32(con.getRemotePeer().calculateHash().getData(), _hX_xor_bobIdentHash);
|
||||||
|
// _prevEncrypted will be created later
|
||||||
|
_state = State.OB_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
_prevEncrypted = new byte[16];
|
_e_hXY_tsB = new byte[HXY_TSB_PAD_SIZE];
|
||||||
_curEncrypted = new byte[16];
|
_curEncrypted = SimpleByteCache.acquire(AES_SIZE);
|
||||||
_curDecrypted = new byte[16];
|
_curDecrypted = SimpleByteCache.acquire(AES_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,15 +202,14 @@ class EstablishState {
|
|||||||
* All data must be copied out of the buffer as Reader.processRead()
|
* All data must be copied out of the buffer as Reader.processRead()
|
||||||
* will return it to the pool.
|
* will return it to the pool.
|
||||||
*/
|
*/
|
||||||
public void receive(ByteBuffer src) {
|
public synchronized void receive(ByteBuffer src) {
|
||||||
if (_corrupt || _verified)
|
if (_state == State.VERIFIED || _state == State.CORRUPT)
|
||||||
throw new IllegalStateException(prefix() + "received after completion [corrupt?" +
|
throw new IllegalStateException(prefix() + "received unexpected data on " + _con);
|
||||||
_corrupt + " verified? " + _verified + "] on " + _con);
|
|
||||||
if (!src.hasRemaining())
|
if (!src.hasRemaining())
|
||||||
return; // nothing to receive
|
return; // nothing to receive
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"receive " + src);
|
_log.debug(prefix() + "Receiving: " + src.remaining() + " Received: " + _received);
|
||||||
if (_con.isInbound())
|
if (_con.isInbound())
|
||||||
receiveInbound(src);
|
receiveInbound(src);
|
||||||
else
|
else
|
||||||
@@ -176,12 +217,9 @@ class EstablishState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* we have written all of the data required to confirm the connection
|
* Was this connection failed because of clock skew?
|
||||||
* establishment
|
|
||||||
*/
|
*/
|
||||||
public boolean confirmWritten() { return _confirmWritten; }
|
public synchronized boolean getFailedBySkew() { return _failedBySkew; }
|
||||||
|
|
||||||
public boolean getFailedBySkew() { return _failedBySkew; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* we are Bob, so receive these bytes as part of an inbound connection
|
* we are Bob, so receive these bytes as part of an inbound connection
|
||||||
@@ -189,12 +227,11 @@ class EstablishState {
|
|||||||
*
|
*
|
||||||
* All data must be copied out of the buffer as Reader.processRead()
|
* All data must be copied out of the buffer as Reader.processRead()
|
||||||
* will return it to the pool.
|
* will return it to the pool.
|
||||||
|
*
|
||||||
|
* Caller must synch.
|
||||||
*/
|
*/
|
||||||
private void receiveInbound(ByteBuffer src) {
|
private void receiveInbound(ByteBuffer src) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
while (_state == State.IB_INIT && src.hasRemaining()) {
|
||||||
_log.debug(prefix() + "Receiving inbound: prev received=" + _received +
|
|
||||||
" src.remaining=" + src.remaining());
|
|
||||||
while (_received < _X.length && src.hasRemaining()) {
|
|
||||||
byte c = src.get();
|
byte c = src.get();
|
||||||
_X[_received++] = c;
|
_X[_received++] = c;
|
||||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv x" + (int)c + " received=" + _received);
|
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv x" + (int)c + " received=" + _received);
|
||||||
@@ -205,24 +242,28 @@ class EstablishState {
|
|||||||
// return;
|
// return;
|
||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
if (_received >= XY_SIZE)
|
||||||
|
_state = State.IB_GOT_X;
|
||||||
}
|
}
|
||||||
while (_received < _X.length + _hX_xor_bobIdentHash.length && src.hasRemaining()) {
|
while (_state == State.IB_GOT_X && src.hasRemaining()) {
|
||||||
int i = _received-_X.length;
|
int i = _received - XY_SIZE;
|
||||||
_received++;
|
_received++;
|
||||||
byte c = src.get();
|
byte c = src.get();
|
||||||
_hX_xor_bobIdentHash[i] = c;
|
_hX_xor_bobIdentHash[i] = c;
|
||||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv bih" + (int)c + " received=" + _received);
|
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv bih" + (int)c + " received=" + _received);
|
||||||
|
if (i >= HXY_SIZE - 1)
|
||||||
|
_state = State.IB_GOT_HX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_received >= _X.length + _hX_xor_bobIdentHash.length) {
|
if (_state == State.IB_GOT_HX) {
|
||||||
if (_dh.getSessionKey() == null) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"Enough data for a DH received");
|
_log.debug(prefix()+"Enough data for a DH received");
|
||||||
|
|
||||||
// first verify that Alice knows who she is trying to talk with and that the X
|
// first verify that Alice knows who she is trying to talk with and that the X
|
||||||
// isn't corrupt
|
// isn't corrupt
|
||||||
byte[] realXor = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
byte[] realXor = SimpleByteCache.acquire(HXY_SIZE);
|
||||||
_context.sha().calculateHash(_X, 0, _X.length, realXor, 0);
|
_context.sha().calculateHash(_X, 0, XY_SIZE, realXor, 0);
|
||||||
xor32(_context.routerHash().getData(), realXor);
|
xor32(_context.routerHash().getData(), realXor);
|
||||||
//if (_log.shouldLog(Log.DEBUG)) {
|
//if (_log.shouldLog(Log.DEBUG)) {
|
||||||
//_log.debug(prefix()+"_X = " + Base64.encode(_X));
|
//_log.debug(prefix()+"_X = " + Base64.encode(_X));
|
||||||
@@ -248,44 +289,43 @@ class EstablishState {
|
|||||||
// ok, they're actually trying to talk to us, and we got their (unauthenticated) X
|
// ok, they're actually trying to talk to us, and we got their (unauthenticated) X
|
||||||
_dh.setPeerPublicValue(_X);
|
_dh.setPeerPublicValue(_X);
|
||||||
_dh.getSessionKey(); // force the calc
|
_dh.getSessionKey(); // force the calc
|
||||||
System.arraycopy(realXor, 16, _prevEncrypted, 0, _prevEncrypted.length);
|
System.arraycopy(_hX_xor_bobIdentHash, AES_SIZE, _prevEncrypted, 0, AES_SIZE);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
|
_log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
|
||||||
|
|
||||||
// now prepare our response: Y+E(H(X+Y)+tsB+padding, sk, Y[239:255])
|
// now prepare our response: Y+E(H(X+Y)+tsB+padding, sk, Y[239:255])
|
||||||
_Y = _dh.getMyPublicValueBytes();
|
byte xy[] = new byte[XY_SIZE + XY_SIZE];
|
||||||
byte xy[] = new byte[_X.length+_Y.length];
|
System.arraycopy(_X, 0, xy, 0, XY_SIZE);
|
||||||
System.arraycopy(_X, 0, xy, 0, _X.length);
|
System.arraycopy(_Y, 0, xy, XY_SIZE, XY_SIZE);
|
||||||
System.arraycopy(_Y, 0, xy, _X.length, _Y.length);
|
byte[] hxy = SimpleByteCache.acquire(HXY_SIZE);
|
||||||
byte[] hxy = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
_context.sha().calculateHash(xy, 0, XY_SIZE + XY_SIZE, hxy, 0);
|
||||||
_context.sha().calculateHash(xy, 0, xy.length, hxy, 0);
|
|
||||||
_tsB = (_context.clock().now() + 500) / 1000l; // our (Bob's) timestamp in seconds
|
_tsB = (_context.clock().now() + 500) / 1000l; // our (Bob's) timestamp in seconds
|
||||||
byte toEncrypt[] = new byte[hxy.length + (4 + 12)]; // 48
|
byte toEncrypt[] = new byte[HXY_TSB_PAD_SIZE]; // 48
|
||||||
System.arraycopy(hxy, 0, toEncrypt, 0, hxy.length);
|
System.arraycopy(hxy, 0, toEncrypt, 0, HXY_SIZE);
|
||||||
byte tsB[] = DataHelper.toLong(4, _tsB);
|
byte tsB[] = DataHelper.toLong(4, _tsB);
|
||||||
System.arraycopy(tsB, 0, toEncrypt, hxy.length, tsB.length);
|
System.arraycopy(tsB, 0, toEncrypt, HXY_SIZE, tsB.length);
|
||||||
//DataHelper.toLong(toEncrypt, hxy.getData().length, 4, _tsB);
|
//DataHelper.toLong(toEncrypt, hxy.getData().length, 4, _tsB);
|
||||||
_context.random().nextBytes(toEncrypt, hxy.length + 4, 12);
|
_context.random().nextBytes(toEncrypt, HXY_SIZE + 4, 12);
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
if (_log.shouldLog(Log.DEBUG)) {
|
||||||
//_log.debug(prefix()+"Y="+Base64.encode(_Y));
|
//_log.debug(prefix()+"Y="+Base64.encode(_Y));
|
||||||
//_log.debug(prefix()+"x+y="+Base64.encode(xy));
|
//_log.debug(prefix()+"x+y="+Base64.encode(xy));
|
||||||
_log.debug(prefix()+"h(x+y)="+Base64.encode(hxy));
|
_log.debug(prefix()+"h(x+y)="+Base64.encode(hxy));
|
||||||
_log.debug(prefix()+"tsb="+Base64.encode(tsB));
|
_log.debug(prefix() + "tsb = " + _tsB);
|
||||||
_log.debug(prefix()+"unencrypted H(X+Y)+tsB+padding: " + Base64.encode(toEncrypt));
|
_log.debug(prefix()+"unencrypted H(X+Y)+tsB+padding: " + Base64.encode(toEncrypt));
|
||||||
_log.debug(prefix()+"encryption iv= " + Base64.encode(_Y, _Y.length-16, 16));
|
_log.debug(prefix()+"encryption iv= " + Base64.encode(_Y, XY_SIZE-AES_SIZE, AES_SIZE));
|
||||||
_log.debug(prefix()+"encryption key= " + _dh.getSessionKey().toBase64());
|
_log.debug(prefix()+"encryption key= " + _dh.getSessionKey().toBase64());
|
||||||
}
|
}
|
||||||
SimpleByteCache.release(hxy);
|
SimpleByteCache.release(hxy);
|
||||||
_e_hXY_tsB = new byte[toEncrypt.length];
|
|
||||||
_context.aes().encrypt(toEncrypt, 0, _e_hXY_tsB, 0, _dh.getSessionKey(),
|
_context.aes().encrypt(toEncrypt, 0, _e_hXY_tsB, 0, _dh.getSessionKey(),
|
||||||
_Y, _Y.length-16, toEncrypt.length);
|
_Y, XY_SIZE-AES_SIZE, HXY_TSB_PAD_SIZE);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"encrypted H(X+Y)+tsB+padding: " + Base64.encode(_e_hXY_tsB));
|
_log.debug(prefix()+"encrypted H(X+Y)+tsB+padding: " + Base64.encode(_e_hXY_tsB));
|
||||||
byte write[] = new byte[_Y.length + _e_hXY_tsB.length];
|
byte write[] = new byte[XY_SIZE + HXY_TSB_PAD_SIZE];
|
||||||
System.arraycopy(_Y, 0, write, 0, _Y.length);
|
System.arraycopy(_Y, 0, write, 0, XY_SIZE);
|
||||||
System.arraycopy(_e_hXY_tsB, 0, write, _Y.length, _e_hXY_tsB.length);
|
System.arraycopy(_e_hXY_tsB, 0, write, XY_SIZE, HXY_TSB_PAD_SIZE);
|
||||||
|
|
||||||
// ok, now that is prepared, we want to actually send it, so make sure we are up for writing
|
// ok, now that is prepared, we want to actually send it, so make sure we are up for writing
|
||||||
|
_state = State.IB_SENT_Y;
|
||||||
_transport.getPumper().wantsWrite(_con, write);
|
_transport.getPumper().wantsWrite(_con, write);
|
||||||
if (!src.hasRemaining()) return;
|
if (!src.hasRemaining()) return;
|
||||||
} catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
|
} catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
|
||||||
@@ -293,35 +333,44 @@ class EstablishState {
|
|||||||
fail("Invalid X", e);
|
fail("Invalid X", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ok, we are onto the encrypted area, i.e. Message #3
|
}
|
||||||
while (src.hasRemaining() && !_corrupt) {
|
|
||||||
|
// ok, we are onto the encrypted area, i.e. Message #3
|
||||||
|
while ((_state == State.IB_SENT_Y ||
|
||||||
|
_state == State.IB_GOT_RI_SIZE ||
|
||||||
|
_state == State.IB_GOT_RI) && src.hasRemaining()) {
|
||||||
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug(prefix()+"Encrypted bytes available (" + src.hasRemaining() + ")");
|
// _log.debug(prefix()+"Encrypted bytes available (" + src.hasRemaining() + ")");
|
||||||
while (_curEncryptedOffset < _curEncrypted.length && src.hasRemaining()) {
|
// Collect a 16-byte block
|
||||||
|
while (_curEncryptedOffset < AES_SIZE && src.hasRemaining()) {
|
||||||
_curEncrypted[_curEncryptedOffset++] = src.get();
|
_curEncrypted[_curEncryptedOffset++] = src.get();
|
||||||
_received++;
|
_received++;
|
||||||
}
|
}
|
||||||
if (_curEncryptedOffset >= _curEncrypted.length) {
|
// Decrypt the 16-byte block
|
||||||
|
if (_curEncryptedOffset >= AES_SIZE) {
|
||||||
_context.aes().decrypt(_curEncrypted, 0, _curDecrypted, 0, _dh.getSessionKey(),
|
_context.aes().decrypt(_curEncrypted, 0, _curDecrypted, 0, _dh.getSessionKey(),
|
||||||
_prevEncrypted, 0, _curEncrypted.length);
|
_prevEncrypted, 0, AES_SIZE);
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug(prefix()+"full block read and decrypted: " + Base64.encode(_curDecrypted));
|
// _log.debug(prefix() + "full block read and decrypted: ");
|
||||||
|
|
||||||
byte swap[] = new byte[16];
|
byte swap[] = _prevEncrypted;
|
||||||
_prevEncrypted = _curEncrypted;
|
_prevEncrypted = _curEncrypted;
|
||||||
_curEncrypted = swap;
|
_curEncrypted = swap;
|
||||||
_curEncryptedOffset = 0;
|
_curEncryptedOffset = 0;
|
||||||
|
|
||||||
if (_aliceIdentSize <= 0) { // we are on the first decrypted block
|
if (_state == State.IB_SENT_Y) { // we are on the first decrypted block
|
||||||
int sz = (int)DataHelper.fromLong(_curDecrypted, 0, 2);
|
int sz = (int)DataHelper.fromLong(_curDecrypted, 0, 2);
|
||||||
if (sz < MIN_RI_SIZE || sz > MAX_RI_SIZE) {
|
if (sz < MIN_RI_SIZE || sz > MAX_RI_SIZE) {
|
||||||
_context.statManager().addRateData("ntcp.invalidInboundSize", sz);
|
_context.statManager().addRateData("ntcp.invalidInboundSize", sz);
|
||||||
fail("size is invalid", new Exception("size is " + sz));
|
fail("size is invalid", new Exception("size is " + sz));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(prefix() + "got the RI size: " + sz);
|
||||||
_aliceIdentSize = sz;
|
_aliceIdentSize = sz;
|
||||||
|
_state = State.IB_GOT_RI_SIZE;
|
||||||
|
|
||||||
// We must defer the calculations for total size of the message until
|
// We must defer the calculations for total size of the message until
|
||||||
// we get the full alice ident so
|
// we get the full alice ident so
|
||||||
@@ -337,10 +386,12 @@ class EstablishState {
|
|||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug(prefix()+"subsequent block decrypted (" + _sz_aliceIdent_tsA_padding_aliceSig.size() + ")");
|
// _log.debug(prefix()+"subsequent block decrypted (" + _sz_aliceIdent_tsA_padding_aliceSig.size() + ")");
|
||||||
|
|
||||||
if (_aliceIdent == null &&
|
if (_state == State.IB_GOT_RI_SIZE &&
|
||||||
_sz_aliceIdent_tsA_padding_aliceSig.size() >= 2 + _aliceIdentSize) {
|
_sz_aliceIdent_tsA_padding_aliceSig.size() >= 2 + _aliceIdentSize) {
|
||||||
// we have enough to get Alice's RI and determine the sig+padding length
|
// we have enough to get Alice's RI and determine the sig+padding length
|
||||||
readAliceRouterIdentity();
|
readAliceRouterIdentity();
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(prefix() + "got the RI");
|
||||||
if (_aliceIdent == null) {
|
if (_aliceIdent == null) {
|
||||||
// readAliceRouterIdentity already called fail
|
// readAliceRouterIdentity already called fail
|
||||||
return;
|
return;
|
||||||
@@ -350,12 +401,13 @@ class EstablishState {
|
|||||||
fail("Unsupported sig type");
|
fail("Unsupported sig type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_state = State.IB_GOT_RI;
|
||||||
// handle variable signature size
|
// handle variable signature size
|
||||||
_sz_aliceIdent_tsA_padding_aliceSigSize = 2 + _aliceIdentSize + 4 + type.getSigLen();
|
_sz_aliceIdent_tsA_padding_aliceSigSize = 2 + _aliceIdentSize + 4 + type.getSigLen();
|
||||||
int rem = (_sz_aliceIdent_tsA_padding_aliceSigSize % 16);
|
int rem = (_sz_aliceIdent_tsA_padding_aliceSigSize % AES_SIZE);
|
||||||
int padding = 0;
|
int padding = 0;
|
||||||
if (rem > 0)
|
if (rem > 0)
|
||||||
padding = 16-rem;
|
padding = AES_SIZE-rem;
|
||||||
_sz_aliceIdent_tsA_padding_aliceSigSize += padding;
|
_sz_aliceIdent_tsA_padding_aliceSigSize += padding;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix() + "alice ident size decrypted as " + _aliceIdentSize +
|
_log.debug(prefix() + "alice ident size decrypted as " + _aliceIdentSize +
|
||||||
@@ -363,19 +415,21 @@ class EstablishState {
|
|||||||
_sz_aliceIdent_tsA_padding_aliceSigSize);
|
_sz_aliceIdent_tsA_padding_aliceSigSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_aliceIdent != null &&
|
if (_state == State.IB_GOT_RI &&
|
||||||
_sz_aliceIdent_tsA_padding_aliceSig.size() >= _sz_aliceIdent_tsA_padding_aliceSigSize) {
|
_sz_aliceIdent_tsA_padding_aliceSig.size() >= _sz_aliceIdent_tsA_padding_aliceSigSize) {
|
||||||
// we have the remainder of Message #3, i.e. the padding+signature
|
// we have the remainder of Message #3, i.e. the padding+signature
|
||||||
// Time to verify.
|
// Time to verify.
|
||||||
|
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(prefix() + "got the sig");
|
||||||
verifyInbound();
|
verifyInbound();
|
||||||
if (!_corrupt && _verified && src.hasRemaining())
|
if (_state == State.VERIFIED && src.hasRemaining())
|
||||||
prepareExtra(src);
|
prepareExtra(src);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"verifying size (sz=" + _sz_aliceIdent_tsA_padding_aliceSig.size()
|
_log.debug(prefix()+"verifying size (sz=" + _sz_aliceIdent_tsA_padding_aliceSig.size()
|
||||||
+ " expected=" + _sz_aliceIdent_tsA_padding_aliceSigSize
|
+ " expected=" + _sz_aliceIdent_tsA_padding_aliceSigSize
|
||||||
+ " corrupt=" + _corrupt
|
+ ' ' + _state
|
||||||
+ " verified=" + _verified + " extra=" + (_extra != null ? _extra.length : 0) + ")");
|
+ " extra=" + (_extra != null ? _extra.length : 0) + ")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -385,10 +439,10 @@ class EstablishState {
|
|||||||
_log.debug(prefix() + "end of available data with only a partial block read (" +
|
_log.debug(prefix() + "end of available data with only a partial block read (" +
|
||||||
_curEncryptedOffset + ", " + _received + ")");
|
_curEncryptedOffset + ", " + _received + ")");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug(prefix()+"done with the data, not yet complete or corrupt");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(prefix()+"done with the data, not yet complete or corrupt");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -397,22 +451,22 @@ class EstablishState {
|
|||||||
*
|
*
|
||||||
* All data must be copied out of the buffer as Reader.processRead()
|
* All data must be copied out of the buffer as Reader.processRead()
|
||||||
* will return it to the pool.
|
* will return it to the pool.
|
||||||
|
*
|
||||||
|
* Caller must synch.
|
||||||
*/
|
*/
|
||||||
private void receiveOutbound(ByteBuffer src) {
|
private void receiveOutbound(ByteBuffer src) {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix()+"Receive outbound " + src + " received=" + _received);
|
|
||||||
|
|
||||||
// recv Y+E(H(X+Y)+tsB, sk, Y[239:255])
|
// recv Y+E(H(X+Y)+tsB, sk, Y[239:255])
|
||||||
while (_received < _Y.length && src.hasRemaining()) {
|
while (_state == State.OB_SENT_X && src.hasRemaining()) {
|
||||||
byte c = src.get();
|
byte c = src.get();
|
||||||
_Y[_received++] = c;
|
_Y[_received++] = c;
|
||||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv x" + (int)c + " received=" + _received);
|
//if (_log.shouldLog(Log.DEBUG)) _log.debug("recv x" + (int)c + " received=" + _received);
|
||||||
if (_received >= _Y.length) {
|
if (_received >= XY_SIZE) {
|
||||||
try {
|
try {
|
||||||
_dh.setPeerPublicValue(_Y);
|
_dh.setPeerPublicValue(_Y);
|
||||||
_dh.getSessionKey(); // force the calc
|
_dh.getSessionKey(); // force the calc
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
|
_log.debug(prefix()+"DH session key calculated (" + _dh.getSessionKey().toBase64() + ")");
|
||||||
_e_hXY_tsB = new byte[Hash.HASH_LENGTH+4+12];
|
_state = State.OB_GOT_Y;
|
||||||
} catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
|
} catch (DHSessionKeyBuilder.InvalidPublicParameterException e) {
|
||||||
_context.statManager().addRateData("ntcp.invalidDH", 1);
|
_context.statManager().addRateData("ntcp.invalidDH", 1);
|
||||||
fail("Invalid X", e);
|
fail("Invalid X", e);
|
||||||
@@ -420,34 +474,34 @@ class EstablishState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_e_hXY_tsB == null) return; // !src.hasRemaining
|
|
||||||
|
|
||||||
while (_received < _Y.length + _e_hXY_tsB.length && src.hasRemaining()) {
|
while (_state == State.OB_GOT_Y && src.hasRemaining()) {
|
||||||
int i = _received-_Y.length;
|
int i = _received-XY_SIZE;
|
||||||
_received++;
|
_received++;
|
||||||
byte c = src.get();
|
byte c = src.get();
|
||||||
_e_hXY_tsB[i] = c;
|
_e_hXY_tsB[i] = c;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix() + "recv _e_hXY_tsB " + (int)c + " received=" + _received);
|
// _log.debug(prefix() + "recv _e_hXY_tsB " + (int)c + " received=" + _received);
|
||||||
if (i+1 >= _e_hXY_tsB.length) {
|
if (i+1 >= HXY_TSB_PAD_SIZE) {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix() + "received _e_hXY_tsB fully");
|
if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix() + "received _e_hXY_tsB fully");
|
||||||
byte hXY_tsB[] = new byte[_e_hXY_tsB.length];
|
byte hXY_tsB[] = new byte[HXY_TSB_PAD_SIZE];
|
||||||
_context.aes().decrypt(_e_hXY_tsB, 0, hXY_tsB, 0, _dh.getSessionKey(), _Y, _Y.length-16, _e_hXY_tsB.length);
|
_context.aes().decrypt(_e_hXY_tsB, 0, hXY_tsB, 0, _dh.getSessionKey(), _Y, XY_SIZE-AES_SIZE, HXY_TSB_PAD_SIZE);
|
||||||
byte XY[] = new byte[_X.length + _Y.length];
|
byte XY[] = new byte[XY_SIZE + XY_SIZE];
|
||||||
System.arraycopy(_X, 0, XY, 0, _X.length);
|
System.arraycopy(_X, 0, XY, 0, XY_SIZE);
|
||||||
System.arraycopy(_Y, 0, XY, _X.length, _Y.length);
|
System.arraycopy(_Y, 0, XY, XY_SIZE, XY_SIZE);
|
||||||
byte[] h = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
byte[] h = SimpleByteCache.acquire(HXY_SIZE);
|
||||||
_context.sha().calculateHash(XY, 0, XY.length, h, 0);
|
_context.sha().calculateHash(XY, 0, XY_SIZE + XY_SIZE, h, 0);
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug(prefix() + "h(XY)=" + h.toBase64());
|
// _log.debug(prefix() + "h(XY)=" + h.toBase64());
|
||||||
if (!DataHelper.eq(h, 0, hXY_tsB, 0, Hash.HASH_LENGTH)) {
|
if (!DataHelper.eq(h, 0, hXY_tsB, 0, HXY_SIZE)) {
|
||||||
SimpleByteCache.release(h);
|
SimpleByteCache.release(h);
|
||||||
_context.statManager().addRateData("ntcp.invalidHXY", 1);
|
_context.statManager().addRateData("ntcp.invalidHXY", 1);
|
||||||
fail("Invalid H(X+Y) - mitm attack attempted?");
|
fail("Invalid H(X+Y) - mitm attack attempted?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SimpleByteCache.release(h);
|
SimpleByteCache.release(h);
|
||||||
_tsB = DataHelper.fromLong(hXY_tsB, Hash.HASH_LENGTH, 4); // their (Bob's) timestamp in seconds
|
_state = State.OB_GOT_HXY;
|
||||||
|
_tsB = DataHelper.fromLong(hXY_tsB, HXY_SIZE, 4); // their (Bob's) timestamp in seconds
|
||||||
_tsA = (_context.clock().now() + 500) / 1000; // our (Alice's) timestamp in seconds
|
_tsA = (_context.clock().now() + 500) / 1000; // our (Alice's) timestamp in seconds
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"h(X+Y) is correct, tsA-tsB=" + (_tsA-_tsB));
|
_log.debug(prefix()+"h(X+Y) is correct, tsA-tsB=" + (_tsA-_tsB));
|
||||||
@@ -477,13 +531,13 @@ class EstablishState {
|
|||||||
|
|
||||||
// now prepare and send our response
|
// now prepare and send our response
|
||||||
// send E(#+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB), sk, hX_xor_Bob.identHash[16:31])
|
// send E(#+Alice.identity+tsA+padding+S(X+Y+Bob.identHash+tsA+tsB), sk, hX_xor_Bob.identHash[16:31])
|
||||||
int sigSize = _X.length+_Y.length+Hash.HASH_LENGTH+4+4;//+12;
|
int sigSize = XY_SIZE + XY_SIZE + HXY_SIZE + 4+4;//+12;
|
||||||
byte preSign[] = new byte[sigSize];
|
byte preSign[] = new byte[sigSize];
|
||||||
System.arraycopy(_X, 0, preSign, 0, _X.length);
|
System.arraycopy(_X, 0, preSign, 0, XY_SIZE);
|
||||||
System.arraycopy(_Y, 0, preSign, _X.length, _Y.length);
|
System.arraycopy(_Y, 0, preSign, XY_SIZE, XY_SIZE);
|
||||||
System.arraycopy(_con.getRemotePeer().calculateHash().getData(), 0, preSign, _X.length+_Y.length, Hash.HASH_LENGTH);
|
System.arraycopy(_con.getRemotePeer().calculateHash().getData(), 0, preSign, XY_SIZE + XY_SIZE, HXY_SIZE);
|
||||||
DataHelper.toLong(preSign, _X.length+_Y.length+Hash.HASH_LENGTH, 4, _tsA);
|
DataHelper.toLong(preSign, XY_SIZE + XY_SIZE + HXY_SIZE, 4, _tsA);
|
||||||
DataHelper.toLong(preSign, _X.length+_Y.length+Hash.HASH_LENGTH+4, 4, _tsB);
|
DataHelper.toLong(preSign, XY_SIZE + XY_SIZE + HXY_SIZE + 4, 4, _tsB);
|
||||||
// hXY_tsB has 12 bytes of padding (size=48, tsB=4 + hXY=32)
|
// hXY_tsB has 12 bytes of padding (size=48, tsB=4 + hXY=32)
|
||||||
//System.arraycopy(hXY_tsB, hXY_tsB.length-12, preSign, _X.length+_Y.length+Hash.HASH_LENGTH+4+4, 12);
|
//System.arraycopy(hXY_tsB, hXY_tsB.length-12, preSign, _X.length+_Y.length+Hash.HASH_LENGTH+4+4, 12);
|
||||||
//byte sigPad[] = new byte[padSig];
|
//byte sigPad[] = new byte[padSig];
|
||||||
@@ -498,10 +552,10 @@ class EstablishState {
|
|||||||
byte ident[] = _context.router().getRouterInfo().getIdentity().toByteArray();
|
byte ident[] = _context.router().getRouterInfo().getIdentity().toByteArray();
|
||||||
// handle variable signature size
|
// handle variable signature size
|
||||||
int min = 2 + ident.length + 4 + sig.length();
|
int min = 2 + ident.length + 4 + sig.length();
|
||||||
int rem = min % 16;
|
int rem = min % AES_SIZE;
|
||||||
int padding = 0;
|
int padding = 0;
|
||||||
if (rem > 0)
|
if (rem > 0)
|
||||||
padding = 16 - rem;
|
padding = AES_SIZE - rem;
|
||||||
byte preEncrypt[] = new byte[min+padding];
|
byte preEncrypt[] = new byte[min+padding];
|
||||||
DataHelper.toLong(preEncrypt, 0, 2, ident.length);
|
DataHelper.toLong(preEncrypt, 0, 2, ident.length);
|
||||||
System.arraycopy(ident, 0, preEncrypt, 2, ident.length);
|
System.arraycopy(ident, 0, preEncrypt, 2, ident.length);
|
||||||
@@ -512,17 +566,18 @@ class EstablishState {
|
|||||||
|
|
||||||
_prevEncrypted = new byte[preEncrypt.length];
|
_prevEncrypted = new byte[preEncrypt.length];
|
||||||
_context.aes().encrypt(preEncrypt, 0, _prevEncrypted, 0, _dh.getSessionKey(),
|
_context.aes().encrypt(preEncrypt, 0, _prevEncrypted, 0, _dh.getSessionKey(),
|
||||||
_hX_xor_bobIdentHash, _hX_xor_bobIdentHash.length-16, preEncrypt.length);
|
_hX_xor_bobIdentHash, _hX_xor_bobIdentHash.length-AES_SIZE, preEncrypt.length);
|
||||||
|
|
||||||
//if (_log.shouldLog(Log.DEBUG)) {
|
//if (_log.shouldLog(Log.DEBUG)) {
|
||||||
//_log.debug(prefix() + "unencrypted response to Bob: " + Base64.encode(preEncrypt));
|
//_log.debug(prefix() + "unencrypted response to Bob: " + Base64.encode(preEncrypt));
|
||||||
//_log.debug(prefix() + "encrypted response to Bob: " + Base64.encode(_prevEncrypted));
|
//_log.debug(prefix() + "encrypted response to Bob: " + Base64.encode(_prevEncrypted));
|
||||||
//}
|
//}
|
||||||
// send 'er off (when the bw limiter says, etc)
|
// send 'er off (when the bw limiter says, etc)
|
||||||
|
_state = State.OB_SENT_RI;
|
||||||
_transport.getPumper().wantsWrite(_con, _prevEncrypted);
|
_transport.getPumper().wantsWrite(_con, _prevEncrypted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_received >= _Y.length + _e_hXY_tsB.length && src.hasRemaining()) {
|
if (_state == State.OB_SENT_RI && src.hasRemaining()) {
|
||||||
// we are receiving their confirmation
|
// we are receiving their confirmation
|
||||||
|
|
||||||
// recv E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev)
|
// recv E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev)
|
||||||
@@ -530,10 +585,10 @@ class EstablishState {
|
|||||||
if (_e_bobSig == null) {
|
if (_e_bobSig == null) {
|
||||||
// handle variable signature size
|
// handle variable signature size
|
||||||
int siglen = _con.getRemotePeer().getSigningPublicKey().getType().getSigLen();
|
int siglen = _con.getRemotePeer().getSigningPublicKey().getType().getSigLen();
|
||||||
int rem = siglen % 16;
|
int rem = siglen % AES_SIZE;
|
||||||
int padding;
|
int padding;
|
||||||
if (rem > 0)
|
if (rem > 0)
|
||||||
padding = 16 - rem;
|
padding = AES_SIZE - rem;
|
||||||
else
|
else
|
||||||
padding = 0;
|
padding = 0;
|
||||||
_e_bobSig = new byte[siglen + padding];
|
_e_bobSig = new byte[siglen + padding];
|
||||||
@@ -541,22 +596,23 @@ class EstablishState {
|
|||||||
_log.debug(prefix() + "receiving E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) (remaining? " +
|
_log.debug(prefix() + "receiving E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) (remaining? " +
|
||||||
src.hasRemaining() + ")");
|
src.hasRemaining() + ")");
|
||||||
} else {
|
} else {
|
||||||
off = _received - _Y.length - _e_hXY_tsB.length;
|
off = _received - XY_SIZE - HXY_TSB_PAD_SIZE;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix() + "continuing to receive E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) (remaining? " +
|
_log.debug(prefix() + "continuing to receive E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev) (remaining? " +
|
||||||
src.hasRemaining() + " off=" + off + " recv=" + _received + ")");
|
src.hasRemaining() + " off=" + off + " recv=" + _received + ")");
|
||||||
}
|
}
|
||||||
while (src.hasRemaining() && off < _e_bobSig.length) {
|
while (_state == State.OB_SENT_RI && src.hasRemaining()) {
|
||||||
if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix()+"recv bobSig received=" + _received);
|
//if (_log.shouldLog(Log.DEBUG)) _log.debug(prefix()+"recv bobSig received=" + _received);
|
||||||
_e_bobSig[off++] = src.get();
|
_e_bobSig[off++] = src.get();
|
||||||
_received++;
|
_received++;
|
||||||
|
|
||||||
if (off >= _e_bobSig.length) {
|
if (off >= _e_bobSig.length) {
|
||||||
|
_state = State.OB_GOT_SIG;
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug(prefix() + "received E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev): " + Base64.encode(_e_bobSig));
|
// _log.debug(prefix() + "received E(S(X+Y+Alice.identHash+tsA+tsB)+padding, sk, prev): " + Base64.encode(_e_bobSig));
|
||||||
byte bobSig[] = new byte[_e_bobSig.length];
|
byte bobSig[] = new byte[_e_bobSig.length];
|
||||||
_context.aes().decrypt(_e_bobSig, 0, bobSig, 0, _dh.getSessionKey(),
|
_context.aes().decrypt(_e_bobSig, 0, bobSig, 0, _dh.getSessionKey(),
|
||||||
_e_hXY_tsB, _e_hXY_tsB.length-16, _e_bobSig.length);
|
_e_hXY_tsB, HXY_TSB_PAD_SIZE - AES_SIZE, _e_bobSig.length);
|
||||||
// ignore the padding
|
// ignore the padding
|
||||||
// handle variable signature size
|
// handle variable signature size
|
||||||
SigType type = _con.getRemotePeer().getSigningPublicKey().getType();
|
SigType type = _con.getRemotePeer().getSigningPublicKey().getType();
|
||||||
@@ -565,27 +621,29 @@ class EstablishState {
|
|||||||
System.arraycopy(bobSig, 0, bobSigData, 0, siglen);
|
System.arraycopy(bobSig, 0, bobSigData, 0, siglen);
|
||||||
Signature sig = new Signature(type, bobSigData);
|
Signature sig = new Signature(type, bobSigData);
|
||||||
|
|
||||||
byte toVerify[] = new byte[_X.length+_Y.length+Hash.HASH_LENGTH+4+4];
|
byte toVerify[] = new byte[XY_SIZE + XY_SIZE + HXY_SIZE +4+4];
|
||||||
int voff = 0;
|
int voff = 0;
|
||||||
System.arraycopy(_X, 0, toVerify, voff, _X.length); voff += _X.length;
|
System.arraycopy(_X, 0, toVerify, voff, XY_SIZE); voff += XY_SIZE;
|
||||||
System.arraycopy(_Y, 0, toVerify, voff, _Y.length); voff += _Y.length;
|
System.arraycopy(_Y, 0, toVerify, voff, XY_SIZE); voff += XY_SIZE;
|
||||||
System.arraycopy(_context.routerHash().getData(), 0, toVerify, voff, Hash.HASH_LENGTH); voff += Hash.HASH_LENGTH;
|
System.arraycopy(_context.routerHash().getData(), 0, toVerify, voff, HXY_SIZE); voff += HXY_SIZE;
|
||||||
DataHelper.toLong(toVerify, voff, 4, _tsA); voff += 4;
|
DataHelper.toLong(toVerify, voff, 4, _tsA); voff += 4;
|
||||||
DataHelper.toLong(toVerify, voff, 4, _tsB); voff += 4;
|
DataHelper.toLong(toVerify, voff, 4, _tsB); voff += 4;
|
||||||
|
|
||||||
_verified = _context.dsa().verifySignature(sig, toVerify, _con.getRemotePeer().getSigningPublicKey());
|
boolean ok = _context.dsa().verifySignature(sig, toVerify, _con.getRemotePeer().getSigningPublicKey());
|
||||||
if (!_verified) {
|
if (!ok) {
|
||||||
_context.statManager().addRateData("ntcp.invalidSignature", 1);
|
_context.statManager().addRateData("ntcp.invalidSignature", 1);
|
||||||
fail("Signature was invalid - attempt to spoof " + _con.getRemotePeer().calculateHash().toBase64() + "?");
|
fail("Signature was invalid - attempt to spoof " + _con.getRemotePeer().calculateHash().toBase64() + "?");
|
||||||
} else {
|
} else {
|
||||||
|
_state = State.VERIFIED;
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix() + "signature verified from Bob. done!");
|
_log.debug(prefix() + "signature verified from Bob. done!");
|
||||||
prepareExtra(src);
|
prepareExtra(src);
|
||||||
byte nextWriteIV[] = new byte[16];
|
byte nextWriteIV[] = _curEncrypted; // reuse buf
|
||||||
System.arraycopy(_prevEncrypted, _prevEncrypted.length-16, nextWriteIV, 0, 16);
|
System.arraycopy(_prevEncrypted, _prevEncrypted.length-AES_SIZE, nextWriteIV, 0, AES_SIZE);
|
||||||
byte nextReadIV[] = new byte[16];
|
byte nextReadIV[] = _curDecrypted; // reuse buf
|
||||||
System.arraycopy(_e_bobSig, _e_bobSig.length-16, nextReadIV, 0, nextReadIV.length);
|
System.arraycopy(_e_bobSig, _e_bobSig.length-AES_SIZE, nextReadIV, 0, nextReadIV.length);
|
||||||
_con.finishOutboundEstablishment(_dh.getSessionKey(), (_tsA-_tsB), nextWriteIV, nextReadIV); // skew in seconds
|
_con.finishOutboundEstablishment(_dh.getSessionKey(), (_tsA-_tsB), nextWriteIV, nextReadIV); // skew in seconds
|
||||||
|
releaseBufs();
|
||||||
// if socket gets closed this will be null - prevent NPE
|
// if socket gets closed this will be null - prevent NPE
|
||||||
InetAddress ia = _con.getChannel().socket().getInetAddress();
|
InetAddress ia = _con.getChannel().socket().getInetAddress();
|
||||||
if (ia != null)
|
if (ia != null)
|
||||||
@@ -598,9 +656,10 @@ class EstablishState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** did the handshake fail for some reason? */
|
/** did the handshake fail for some reason? */
|
||||||
public boolean isCorrupt() { return _err != null; }
|
public synchronized boolean isCorrupt() { return _state == State.CORRUPT; }
|
||||||
|
|
||||||
/** @return is the handshake complete and valid? */
|
/** @return is the handshake complete and valid? */
|
||||||
public boolean isComplete() { return _verified; }
|
public synchronized boolean isComplete() { return _state == State.VERIFIED; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We are Alice.
|
* We are Alice.
|
||||||
@@ -608,17 +667,18 @@ class EstablishState {
|
|||||||
* queueing up the write of the first part of the handshake
|
* queueing up the write of the first part of the handshake
|
||||||
* This method sends message #1 to Bob.
|
* This method sends message #1 to Bob.
|
||||||
*/
|
*/
|
||||||
public void prepareOutbound() {
|
public synchronized void prepareOutbound() {
|
||||||
if (_received <= 0) {
|
if (_state == State.OB_INIT) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix() + "write out the first part of our handshake");
|
_log.debug(prefix() + "send X");
|
||||||
byte toWrite[] = new byte[_X.length + _hX_xor_bobIdentHash.length];
|
byte toWrite[] = new byte[XY_SIZE + _hX_xor_bobIdentHash.length];
|
||||||
System.arraycopy(_X, 0, toWrite, 0, _X.length);
|
System.arraycopy(_X, 0, toWrite, 0, XY_SIZE);
|
||||||
System.arraycopy(_hX_xor_bobIdentHash, 0, toWrite, _X.length, _hX_xor_bobIdentHash.length);
|
System.arraycopy(_hX_xor_bobIdentHash, 0, toWrite, XY_SIZE, _hX_xor_bobIdentHash.length);
|
||||||
|
_state = State.OB_SENT_X;
|
||||||
_transport.getPumper().wantsWrite(_con, toWrite);
|
_transport.getPumper().wantsWrite(_con, toWrite);
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.debug(prefix()+"prepare outbound with received=" + _received);
|
_log.warn(prefix() + "unexpected prepareOutbound()");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -635,10 +695,12 @@ class EstablishState {
|
|||||||
* After all of message #3 is received including the signature and
|
* After all of message #3 is received including the signature and
|
||||||
* padding, verifyIdentity() must be called.
|
* padding, verifyIdentity() must be called.
|
||||||
*
|
*
|
||||||
|
* State must be IB_GOT_RI_SIZE.
|
||||||
|
* Caller must synch.
|
||||||
|
*
|
||||||
* @since 0.9.16 pulled out of verifyInbound()
|
* @since 0.9.16 pulled out of verifyInbound()
|
||||||
*/
|
*/
|
||||||
private void readAliceRouterIdentity() {
|
private void readAliceRouterIdentity() {
|
||||||
if (_corrupt) return;
|
|
||||||
byte b[] = _sz_aliceIdent_tsA_padding_aliceSig.toByteArray();
|
byte b[] = _sz_aliceIdent_tsA_padding_aliceSig.toByteArray();
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug(prefix()+"decrypted sz(etc) data: " + Base64.encode(b));
|
// _log.debug(prefix()+"decrypted sz(etc) data: " + Base64.encode(b));
|
||||||
@@ -680,9 +742,11 @@ class EstablishState {
|
|||||||
* NIOConnection with the session key / peer ident / clock skew / iv.
|
* NIOConnection with the session key / peer ident / clock skew / iv.
|
||||||
* The NIOConnection itself is responsible for registering with the
|
* The NIOConnection itself is responsible for registering with the
|
||||||
* transport
|
* transport
|
||||||
|
*
|
||||||
|
* State must be IB_GOT_RI.
|
||||||
|
* Caller must synch.
|
||||||
*/
|
*/
|
||||||
private void verifyInbound() {
|
private void verifyInbound() {
|
||||||
if (_corrupt) return;
|
|
||||||
byte b[] = _sz_aliceIdent_tsA_padding_aliceSig.toByteArray();
|
byte b[] = _sz_aliceIdent_tsA_padding_aliceSig.toByteArray();
|
||||||
try {
|
try {
|
||||||
int sz = _aliceIdentSize;
|
int sz = _aliceIdentSize;
|
||||||
@@ -696,10 +760,10 @@ class EstablishState {
|
|||||||
//baos.write(b, 2+sz+4, b.length-2-sz-4-Signature.SIGNATURE_BYTES);
|
//baos.write(b, 2+sz+4, b.length-2-sz-4-Signature.SIGNATURE_BYTES);
|
||||||
|
|
||||||
byte toVerify[] = baos.toByteArray();
|
byte toVerify[] = baos.toByteArray();
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
//if (_log.shouldLog(Log.DEBUG)) {
|
||||||
_log.debug(prefix()+"checking " + Base64.encode(toVerify, 0, 16));
|
// _log.debug(prefix()+"checking " + Base64.encode(toVerify, 0, AES_SIZE));
|
||||||
//_log.debug(prefix()+"check pad " + Base64.encode(b, 2+sz+4, 12));
|
// //_log.debug(prefix()+"check pad " + Base64.encode(b, 2+sz+4, 12));
|
||||||
}
|
//}
|
||||||
|
|
||||||
// handle variable signature size
|
// handle variable signature size
|
||||||
SigType type = _aliceIdent.getSigningPublicKey().getType();
|
SigType type = _aliceIdent.getSigningPublicKey().getType();
|
||||||
@@ -710,8 +774,8 @@ class EstablishState {
|
|||||||
byte s[] = new byte[type.getSigLen()];
|
byte s[] = new byte[type.getSigLen()];
|
||||||
System.arraycopy(b, b.length-s.length, s, 0, s.length);
|
System.arraycopy(b, b.length-s.length, s, 0, s.length);
|
||||||
Signature sig = new Signature(type, s);
|
Signature sig = new Signature(type, s);
|
||||||
_verified = _context.dsa().verifySignature(sig, toVerify, _aliceIdent.getSigningPublicKey());
|
boolean ok = _context.dsa().verifySignature(sig, toVerify, _aliceIdent.getSigningPublicKey());
|
||||||
if (_verified) {
|
if (ok) {
|
||||||
// get inet-addr
|
// get inet-addr
|
||||||
InetAddress addr = this._con.getChannel().socket().getInetAddress();
|
InetAddress addr = this._con.getChannel().socket().getInetAddress();
|
||||||
byte[] ip = (addr == null) ? null : addr.getAddress();
|
byte[] ip = (addr == null) ? null : addr.getAddress();
|
||||||
@@ -752,13 +816,15 @@ class EstablishState {
|
|||||||
_log.debug(prefix()+"Clock skew: " + diff + " ms");
|
_log.debug(prefix()+"Clock skew: " + diff + " ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_state = State.VERIFIED;
|
||||||
sendInboundConfirm(_aliceIdent, tsA);
|
sendInboundConfirm(_aliceIdent, tsA);
|
||||||
_con.setRemotePeer(_aliceIdent);
|
_con.setRemotePeer(_aliceIdent);
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix()+"e_bobSig is " + _e_bobSig.length + " bytes long");
|
_log.debug(prefix()+"e_bobSig is " + _e_bobSig.length + " bytes long");
|
||||||
byte iv[] = new byte[16];
|
byte iv[] = _curDecrypted; // reuse buf
|
||||||
System.arraycopy(_e_bobSig, _e_bobSig.length-16, iv, 0, 16);
|
System.arraycopy(_e_bobSig, _e_bobSig.length-AES_SIZE, iv, 0, AES_SIZE);
|
||||||
_con.finishInboundEstablishment(_dh.getSessionKey(), (tsA-_tsB), iv, _prevEncrypted); // skew in seconds
|
_con.finishInboundEstablishment(_dh.getSessionKey(), (tsA-_tsB), iv, _prevEncrypted); // skew in seconds
|
||||||
|
releaseBufs();
|
||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info(prefix()+"Verified remote peer as " + _aliceIdent.calculateHash());
|
_log.info(prefix()+"Verified remote peer as " + _aliceIdent.calculateHash());
|
||||||
} else {
|
} else {
|
||||||
@@ -773,13 +839,16 @@ class EstablishState {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* We are Bob. Send message #4 to Alice.
|
* We are Bob. Send message #4 to Alice.
|
||||||
|
*
|
||||||
|
* State must be VERIFIED.
|
||||||
|
* Caller must synch.
|
||||||
*/
|
*/
|
||||||
private void sendInboundConfirm(RouterIdentity alice, long tsA) {
|
private void sendInboundConfirm(RouterIdentity alice, long tsA) {
|
||||||
// send Alice E(S(X+Y+Alice.identHash+tsA+tsB), sk, prev)
|
// send Alice E(S(X+Y+Alice.identHash+tsA+tsB), sk, prev)
|
||||||
byte toSign[] = new byte[256+256+32+4+4];
|
byte toSign[] = new byte[XY_SIZE + XY_SIZE + 32+4+4];
|
||||||
int off = 0;
|
int off = 0;
|
||||||
System.arraycopy(_X, 0, toSign, off, 256); off += 256;
|
System.arraycopy(_X, 0, toSign, off, XY_SIZE); off += XY_SIZE;
|
||||||
System.arraycopy(_Y, 0, toSign, off, 256); off += 256;
|
System.arraycopy(_Y, 0, toSign, off, XY_SIZE); off += XY_SIZE;
|
||||||
Hash h = alice.calculateHash();
|
Hash h = alice.calculateHash();
|
||||||
System.arraycopy(h.getData(), 0, toSign, off, 32); off += 32;
|
System.arraycopy(h.getData(), 0, toSign, off, 32); off += 32;
|
||||||
DataHelper.toLong(toSign, off, 4, tsA); off += 4;
|
DataHelper.toLong(toSign, off, 4, tsA); off += 4;
|
||||||
@@ -788,10 +857,10 @@ class EstablishState {
|
|||||||
// handle variable signature size
|
// handle variable signature size
|
||||||
Signature sig = _context.dsa().sign(toSign, _context.keyManager().getSigningPrivateKey());
|
Signature sig = _context.dsa().sign(toSign, _context.keyManager().getSigningPrivateKey());
|
||||||
int siglen = sig.length();
|
int siglen = sig.length();
|
||||||
int rem = siglen % 16;
|
int rem = siglen % AES_SIZE;
|
||||||
int padding;
|
int padding;
|
||||||
if (rem > 0)
|
if (rem > 0)
|
||||||
padding = 16 - rem;
|
padding = AES_SIZE - rem;
|
||||||
else
|
else
|
||||||
padding = 0;
|
padding = 0;
|
||||||
byte preSig[] = new byte[siglen + padding];
|
byte preSig[] = new byte[siglen + padding];
|
||||||
@@ -799,7 +868,7 @@ class EstablishState {
|
|||||||
if (padding > 0)
|
if (padding > 0)
|
||||||
_context.random().nextBytes(preSig, siglen, padding);
|
_context.random().nextBytes(preSig, siglen, padding);
|
||||||
_e_bobSig = new byte[preSig.length];
|
_e_bobSig = new byte[preSig.length];
|
||||||
_context.aes().encrypt(preSig, 0, _e_bobSig, 0, _dh.getSessionKey(), _e_hXY_tsB, _e_hXY_tsB.length-16, _e_bobSig.length);
|
_context.aes().encrypt(preSig, 0, _e_bobSig, 0, _dh.getSessionKey(), _e_hXY_tsB, HXY_TSB_PAD_SIZE - AES_SIZE, _e_bobSig.length);
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug(prefix() + "Sending encrypted inbound confirmation");
|
_log.debug(prefix() + "Sending encrypted inbound confirmation");
|
||||||
@@ -810,6 +879,9 @@ class EstablishState {
|
|||||||
*
|
*
|
||||||
* All data must be copied out of the buffer as Reader.processRead()
|
* All data must be copied out of the buffer as Reader.processRead()
|
||||||
* will return it to the pool.
|
* will return it to the pool.
|
||||||
|
*
|
||||||
|
* State must be VERIFIED.
|
||||||
|
* Caller must synch.
|
||||||
*/
|
*/
|
||||||
private void prepareExtra(ByteBuffer buf) {
|
private void prepareExtra(ByteBuffer buf) {
|
||||||
int remaining = buf.remaining();
|
int remaining = buf.remaining();
|
||||||
@@ -826,21 +898,42 @@ class EstablishState {
|
|||||||
* if complete, this will contain any bytes received as part of the
|
* if complete, this will contain any bytes received as part of the
|
||||||
* handshake that were after the actual handshake. This may return null.
|
* handshake that were after the actual handshake. This may return null.
|
||||||
*/
|
*/
|
||||||
public byte[] getExtraBytes() { return _extra; }
|
public synchronized byte[] getExtraBytes() { return _extra; }
|
||||||
|
|
||||||
|
/** Caller must synch. */
|
||||||
private void fail(String reason) { fail(reason, null); }
|
private void fail(String reason) { fail(reason, null); }
|
||||||
|
|
||||||
|
/** Caller must synch. */
|
||||||
private void fail(String reason, Exception e) { fail(reason, e, false); }
|
private void fail(String reason, Exception e) { fail(reason, e, false); }
|
||||||
|
|
||||||
|
/** Caller must synch. */
|
||||||
private void fail(String reason, Exception e, boolean bySkew) {
|
private void fail(String reason, Exception e, boolean bySkew) {
|
||||||
_corrupt = true;
|
if (_state == State.CORRUPT || _state == State.VERIFIED)
|
||||||
|
return;
|
||||||
|
_state = State.CORRUPT;
|
||||||
_failedBySkew = bySkew;
|
_failedBySkew = bySkew;
|
||||||
_err = reason;
|
_err = reason;
|
||||||
_e = e;
|
_e = e;
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn(prefix()+"Failed to establish: " + _err, e);
|
_log.warn(prefix()+"Failed to establish: " + _err, e);
|
||||||
|
releaseBufs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getError() { return _err; }
|
/** Only call once. Caller must synch. */
|
||||||
public Exception getException() { return _e; }
|
private void releaseBufs() {
|
||||||
|
// null or longer for OB
|
||||||
|
if (_prevEncrypted != null && _prevEncrypted.length == AES_SIZE)
|
||||||
|
SimpleByteCache.release(_prevEncrypted);
|
||||||
|
SimpleByteCache.release(_curEncrypted);
|
||||||
|
SimpleByteCache.release(_curDecrypted);
|
||||||
|
SimpleByteCache.release(_hX_xor_bobIdentHash);
|
||||||
|
SimpleByteCache.release(_X);
|
||||||
|
SimpleByteCache.release(_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized String getError() { return _err; }
|
||||||
|
|
||||||
|
public synchronized Exception getException() { return _e; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XOR a into b. Modifies b. a is unmodified.
|
* XOR a into b. Modifies b. a is unmodified.
|
||||||
@@ -859,11 +952,12 @@ class EstablishState {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder(64);
|
StringBuilder buf = new StringBuilder(64);
|
||||||
buf.append("est").append(System.identityHashCode(this));
|
if (_con.isInbound())
|
||||||
if (_con.isInbound()) buf.append(" inbound");
|
buf.append("IBES ");
|
||||||
else buf.append(" outbound");
|
else
|
||||||
if (_corrupt) buf.append(" corrupt");
|
buf.append("OBES ");
|
||||||
if (_verified) buf.append(" verified");
|
buf.append(System.identityHashCode(this));
|
||||||
|
buf.append(' ').append(_state);
|
||||||
if (_con.isEstablished()) buf.append(" established");
|
if (_con.isEstablished()) buf.append(" established");
|
||||||
buf.append(": ");
|
buf.append(": ");
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
|
Reference in New Issue
Block a user