forked from I2P_Developers/i2p.i2p
SSU1 removal part 7/n
This commit is contained in:
@@ -989,32 +989,12 @@ class EstablishmentManager {
|
|||||||
|
|
||||||
RouterIdentity remote = state.getConfirmedIdentity();
|
RouterIdentity remote = state.getConfirmedIdentity();
|
||||||
PeerState peer;
|
PeerState peer;
|
||||||
int version = state.getVersion();
|
|
||||||
|
|
||||||
InboundEstablishState2 state2 = (InboundEstablishState2) state;
|
InboundEstablishState2 state2 = (InboundEstablishState2) state;
|
||||||
peer = state2.getPeerState();
|
peer = state2.getPeerState();
|
||||||
// now handled in IES2.createPeerState()
|
// now handled in IES2.createPeerState()
|
||||||
//peer.setWeRelayToThemAs(state.getSentRelayTag());
|
//peer.setWeRelayToThemAs(state.getSentRelayTag());
|
||||||
|
|
||||||
if (version == 1) {
|
|
||||||
// Lookup the peer's MTU from the netdb, since it isn't included in the protocol setup (yet)
|
|
||||||
// TODO if we don't have RI then we will get it shortly, but too late.
|
|
||||||
// Perhaps netdb should notify transport when it gets a new RI...
|
|
||||||
RouterInfo info = _context.netDb().lookupRouterInfoLocally(remote.calculateHash());
|
|
||||||
if (info != null) {
|
|
||||||
RouterAddress addr = _transport.getTargetAddress(info);
|
|
||||||
if (addr != null) {
|
|
||||||
String smtu = addr.getOption(UDPAddress.PROP_MTU);
|
|
||||||
if (smtu != null) {
|
|
||||||
try {
|
|
||||||
boolean isIPv6 = state.getSentIP().length == 16;
|
|
||||||
int mtu = MTU.rectify(isIPv6, Integer.parseInt(smtu));
|
|
||||||
peer.setHisMTU(mtu);
|
|
||||||
} catch (NumberFormatException nfe) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // else IES2 sets PS2 MTU
|
|
||||||
// 0 is the default
|
// 0 is the default
|
||||||
//peer.setTheyRelayToUsAs(0);
|
//peer.setTheyRelayToUsAs(0);
|
||||||
|
|
||||||
|
@@ -1,198 +0,0 @@
|
|||||||
package net.i2p.router.transport.udp;
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
|
|
||||||
* (http://www.bouncycastle.org)
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person
|
|
||||||
* obtaining a copy of this software and associated
|
|
||||||
* documentation files (the "Software"), to deal in the Software
|
|
||||||
* without restriction, including without limitation the rights to
|
|
||||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
||||||
* sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following
|
|
||||||
* conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be
|
|
||||||
* included in all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
||||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
||||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.security.DigestException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import net.i2p.util.SimpleByteCache;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HMAC implementation based on RFC2104
|
|
||||||
*
|
|
||||||
* H(K XOR opad, H(K XOR ipad, text))
|
|
||||||
*
|
|
||||||
* modified by jrandom to use the session key byte array directly and to cache
|
|
||||||
* a frequently used buffer (called on doFinal). changes released into the public
|
|
||||||
* domain in 2005.
|
|
||||||
*
|
|
||||||
* This is renamed from HMac because the constructor HMac(digest, sz) does not exist
|
|
||||||
* in the standard bouncycastle library, thus it conflicts in JVMs that contain the
|
|
||||||
* standard library (Android).
|
|
||||||
*
|
|
||||||
* As of 0.9.12, refactored to use standard MessageDigest.
|
|
||||||
*
|
|
||||||
* Deprecated - Do not use outside of router or Syndie.
|
|
||||||
* Not a public API - Not for external use!
|
|
||||||
*
|
|
||||||
* @since 0.9.43 moved from org.bouncycastle.oldcrypto.macs
|
|
||||||
*/
|
|
||||||
class I2PHMac
|
|
||||||
{
|
|
||||||
private final static int BLOCK_LENGTH = 64;
|
|
||||||
|
|
||||||
private final static byte IPAD = (byte)0x36;
|
|
||||||
private final static byte OPAD = (byte)0x5C;
|
|
||||||
|
|
||||||
private final MessageDigest digest;
|
|
||||||
private final int digestSize;
|
|
||||||
private final byte[] inputPad = new byte[BLOCK_LENGTH];
|
|
||||||
private final byte[] outputPad = new byte[BLOCK_LENGTH];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Standard HMAC, size == digest size.
|
|
||||||
* @deprecated Use javax.crypto.Mac
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public I2PHMac(MessageDigest digest) {
|
|
||||||
this(digest, digest.getDigestLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param sz override the digest's size, nonstandard if different.
|
|
||||||
* SEE NOTES in HMACGenerator about why this isn't compatible with standard HmacMD5
|
|
||||||
*/
|
|
||||||
public I2PHMac(MessageDigest digest, int sz) {
|
|
||||||
this.digest = digest;
|
|
||||||
this.digestSize = sz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAlgorithmName()
|
|
||||||
{
|
|
||||||
return digest.getAlgorithm() + "/HMAC";
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageDigest getUnderlyingDigest()
|
|
||||||
{
|
|
||||||
return digest;
|
|
||||||
}
|
|
||||||
|
|
||||||
//public void init(
|
|
||||||
// CipherParameters params)
|
|
||||||
//{
|
|
||||||
public void init(byte key[])
|
|
||||||
{
|
|
||||||
digest.reset();
|
|
||||||
|
|
||||||
//byte[] key = ((KeyParameter)params).getKey();
|
|
||||||
|
|
||||||
if (key.length > BLOCK_LENGTH)
|
|
||||||
{
|
|
||||||
digest.update(key, 0, key.length);
|
|
||||||
try {
|
|
||||||
digest.digest(inputPad, 0, digestSize);
|
|
||||||
} catch (DigestException de) {
|
|
||||||
digest.reset();
|
|
||||||
throw new IllegalArgumentException(de);
|
|
||||||
}
|
|
||||||
for (int i = digestSize; i < inputPad.length; i++)
|
|
||||||
{
|
|
||||||
inputPad[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
System.arraycopy(key, 0, inputPad, 0, key.length);
|
|
||||||
for (int i = key.length; i < inputPad.length; i++)
|
|
||||||
{
|
|
||||||
inputPad[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// why reallocate? it hasn't changed sizes, and the arraycopy
|
|
||||||
// below fills it completely...
|
|
||||||
//outputPad = new byte[inputPad.length];
|
|
||||||
System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length);
|
|
||||||
|
|
||||||
for (int i = 0; i < inputPad.length; i++)
|
|
||||||
{
|
|
||||||
inputPad[i] ^= IPAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < outputPad.length; i++)
|
|
||||||
{
|
|
||||||
outputPad[i] ^= OPAD;
|
|
||||||
}
|
|
||||||
|
|
||||||
digest.update(inputPad, 0, inputPad.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMacSize() {
|
|
||||||
return digestSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(byte in) {
|
|
||||||
digest.update(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(byte[] in, int inOff, int len) {
|
|
||||||
digest.update(in, inOff, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int doFinal(byte[] out, int outOff) {
|
|
||||||
byte[] tmp = acquireTmp(digestSize);
|
|
||||||
//byte[] tmp = new byte[digestSize];
|
|
||||||
try {
|
|
||||||
digest.digest(tmp, 0, digestSize);
|
|
||||||
digest.update(outputPad, 0, outputPad.length);
|
|
||||||
digest.update(tmp, 0, tmp.length);
|
|
||||||
return digest.digest(out, outOff, digestSize);
|
|
||||||
} catch (DigestException de) {
|
|
||||||
throw new IllegalArgumentException(de);
|
|
||||||
} finally {
|
|
||||||
releaseTmp(tmp);
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] acquireTmp(int sz) {
|
|
||||||
byte[] rv = SimpleByteCache.acquire(sz);
|
|
||||||
Arrays.fill(rv, (byte)0x0);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void releaseTmp(byte buf[]) {
|
|
||||||
SimpleByteCache.release(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the mac generator.
|
|
||||||
*/
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* reset the underlying digest.
|
|
||||||
*/
|
|
||||||
digest.reset();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* reinitialize the digest.
|
|
||||||
*/
|
|
||||||
digest.update(inputPad, 0, inputPad.length);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -13,11 +13,9 @@ import net.i2p.data.DataFormatException;
|
|||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.router.RouterIdentity;
|
import net.i2p.data.router.RouterIdentity;
|
||||||
import net.i2p.data.SessionKey;
|
|
||||||
import net.i2p.data.Signature;
|
import net.i2p.data.Signature;
|
||||||
import net.i2p.router.OutNetMessage;
|
import net.i2p.router.OutNetMessage;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
|
|
||||||
import net.i2p.util.Addresses;
|
import net.i2p.util.Addresses;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@@ -35,21 +33,15 @@ class InboundEstablishState {
|
|||||||
private byte _receivedX[];
|
private byte _receivedX[];
|
||||||
protected byte _bobIP[];
|
protected byte _bobIP[];
|
||||||
protected final int _bobPort;
|
protected final int _bobPort;
|
||||||
private final DHSessionKeyBuilder _keyBuilder;
|
|
||||||
// SessionCreated message
|
// SessionCreated message
|
||||||
private byte _sentY[];
|
|
||||||
protected final byte _aliceIP[];
|
protected final byte _aliceIP[];
|
||||||
protected final int _alicePort;
|
protected final int _alicePort;
|
||||||
protected long _sentRelayTag;
|
protected long _sentRelayTag;
|
||||||
private long _sentSignedOnTime;
|
private long _sentSignedOnTime;
|
||||||
private SessionKey _sessionKey;
|
|
||||||
private SessionKey _macKey;
|
|
||||||
private Signature _sentSignature;
|
|
||||||
// SessionConfirmed messages - fragmented in theory but not in practice - see below
|
// SessionConfirmed messages - fragmented in theory but not in practice - see below
|
||||||
private byte _receivedIdentity[][];
|
private byte _receivedIdentity[][];
|
||||||
private long _receivedSignedOnTime;
|
private long _receivedSignedOnTime;
|
||||||
private byte _receivedSignature[];
|
private byte _receivedSignature[];
|
||||||
private boolean _verificationAttempted;
|
|
||||||
// sig not verified
|
// sig not verified
|
||||||
protected RouterIdentity _receivedUnconfirmedIdentity;
|
protected RouterIdentity _receivedUnconfirmedIdentity;
|
||||||
// identical to uncomfirmed, but sig now verified
|
// identical to uncomfirmed, but sig now verified
|
||||||
@@ -115,25 +107,6 @@ class InboundEstablishState {
|
|||||||
*/
|
*/
|
||||||
protected static final long MAX_DELAY = EstablishmentManager.MAX_IB_ESTABLISH_TIME;
|
protected static final long MAX_DELAY = EstablishmentManager.MAX_IB_ESTABLISH_TIME;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param localPort Must be our external port, otherwise the signature of the
|
|
||||||
* SessionCreated message will be bad if the external port != the internal port.
|
|
||||||
*/
|
|
||||||
public InboundEstablishState(RouterContext ctx, byte remoteIP[], int remotePort, int localPort,
|
|
||||||
DHSessionKeyBuilder dh, UDPPacketReader.SessionRequestReader req) {
|
|
||||||
_context = ctx;
|
|
||||||
_log = ctx.logManager().getLog(InboundEstablishState.class);
|
|
||||||
_aliceIP = remoteIP;
|
|
||||||
_alicePort = remotePort;
|
|
||||||
_remoteHostId = new RemoteHostId(_aliceIP, _alicePort);
|
|
||||||
_bobPort = localPort;
|
|
||||||
_currentState = InboundState.IB_STATE_UNKNOWN;
|
|
||||||
_establishBegin = ctx.clock().now();
|
|
||||||
_keyBuilder = dh;
|
|
||||||
_queuedMessages = new LinkedBlockingQueue<OutNetMessage>();
|
|
||||||
_introductionRequested = true;
|
|
||||||
receiveSessionRequest(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For SSU2
|
* For SSU2
|
||||||
@@ -149,7 +122,6 @@ class InboundEstablishState {
|
|||||||
_bobPort = 0;
|
_bobPort = 0;
|
||||||
_currentState = InboundState.IB_STATE_UNKNOWN;
|
_currentState = InboundState.IB_STATE_UNKNOWN;
|
||||||
_establishBegin = ctx.clock().now();
|
_establishBegin = ctx.clock().now();
|
||||||
_keyBuilder = null;
|
|
||||||
_queuedMessages = new LinkedBlockingQueue<OutNetMessage>();
|
_queuedMessages = new LinkedBlockingQueue<OutNetMessage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,26 +165,6 @@ class InboundEstablishState {
|
|||||||
return _queuedMessages.poll();
|
return _queuedMessages.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void receiveSessionRequest(UDPPacketReader.SessionRequestReader req) {
|
|
||||||
if (_receivedX == null)
|
|
||||||
_receivedX = new byte[UDPPacketReader.SessionRequestReader.X_LENGTH];
|
|
||||||
req.readX(_receivedX, 0);
|
|
||||||
if (_bobIP == null)
|
|
||||||
_bobIP = new byte[req.readIPSize()];
|
|
||||||
req.readIP(_bobIP, 0);
|
|
||||||
byte[] ext = req.readExtendedOptions();
|
|
||||||
if (ext != null && ext.length >= UDPPacket.SESS_REQ_MIN_EXT_OPTIONS_LENGTH) {
|
|
||||||
_introductionRequested = (ext[1] & (byte) UDPPacket.SESS_REQ_EXT_FLAG_REQUEST_RELAY_TAG) != 0;
|
|
||||||
if (_log.shouldInfo())
|
|
||||||
_log.info("got sess req. w/ ext. options, need intro? " + _introductionRequested + ' ' + this);
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Receive sessionRequest, BobIP = " + Addresses.toString(_bobIP));
|
|
||||||
if (_currentState == InboundState.IB_STATE_UNKNOWN)
|
|
||||||
_currentState = InboundState.IB_STATE_REQUEST_RECEIVED;
|
|
||||||
packetReceived();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean sessionRequestReceived() { return _receivedX != null; }
|
public synchronized boolean sessionRequestReceived() { return _receivedX != null; }
|
||||||
public synchronized byte[] getReceivedX() { return _receivedX; }
|
public synchronized byte[] getReceivedX() { return _receivedX; }
|
||||||
public synchronized byte[] getReceivedOurIP() { return _bobIP; }
|
public synchronized byte[] getReceivedOurIP() { return _bobIP; }
|
||||||
@@ -223,40 +175,12 @@ class InboundEstablishState {
|
|||||||
*/
|
*/
|
||||||
public synchronized boolean isIntroductionRequested() { return _introductionRequested; }
|
public synchronized boolean isIntroductionRequested() { return _introductionRequested; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates session key and mac key.
|
|
||||||
*/
|
|
||||||
public synchronized void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
|
|
||||||
if (_sessionKey != null) return;
|
|
||||||
try {
|
|
||||||
_keyBuilder.setPeerPublicValue(_receivedX);
|
|
||||||
} catch (IllegalStateException ise) {
|
|
||||||
throw new DHSessionKeyBuilder.InvalidPublicParameterException("reused keys?", ise);
|
|
||||||
}
|
|
||||||
_sessionKey = _keyBuilder.getSessionKey();
|
|
||||||
ByteArray extra = _keyBuilder.getExtraBytes();
|
|
||||||
_macKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
|
||||||
System.arraycopy(extra.getData(), 0, _macKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Established inbound keys. cipher: " + Base64.encode(_sessionKey.getData())
|
|
||||||
+ " mac: " + Base64.encode(_macKey.getData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized SessionKey getCipherKey() { return _sessionKey; }
|
|
||||||
public synchronized SessionKey getMACKey() { return _macKey; }
|
|
||||||
|
|
||||||
/** what IP do they appear to be on? */
|
/** what IP do they appear to be on? */
|
||||||
public byte[] getSentIP() { return _aliceIP; }
|
public byte[] getSentIP() { return _aliceIP; }
|
||||||
|
|
||||||
/** what port number do they appear to be coming from? */
|
/** what port number do they appear to be coming from? */
|
||||||
public int getSentPort() { return _alicePort; }
|
public int getSentPort() { return _alicePort; }
|
||||||
|
|
||||||
public synchronized byte[] getSentY() {
|
|
||||||
if (_sentY == null)
|
|
||||||
_sentY = _keyBuilder.getMyPublicValueBytes();
|
|
||||||
return _sentY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void fail() {
|
public synchronized void fail() {
|
||||||
_currentState = InboundState.IB_STATE_FAILED;
|
_currentState = InboundState.IB_STATE_FAILED;
|
||||||
}
|
}
|
||||||
@@ -265,59 +189,6 @@ class InboundEstablishState {
|
|||||||
public synchronized void setSentRelayTag(long tag) { _sentRelayTag = tag; }
|
public synchronized void setSentRelayTag(long tag) { _sentRelayTag = tag; }
|
||||||
public synchronized long getSentSignedOnTime() { return _sentSignedOnTime; }
|
public synchronized long getSentSignedOnTime() { return _sentSignedOnTime; }
|
||||||
|
|
||||||
public synchronized void prepareSessionCreated() {
|
|
||||||
if (_sentSignature == null) signSessionCreated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Signature getSentSignature() { return _sentSignature; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign: Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's
|
|
||||||
* new relay tag + Bob's signed on time
|
|
||||||
*/
|
|
||||||
private void signSessionCreated() {
|
|
||||||
byte signed[] = new byte[256 + 256 // X + Y
|
|
||||||
+ _aliceIP.length + 2
|
|
||||||
+ _bobIP.length + 2
|
|
||||||
+ 4 // sent relay tag
|
|
||||||
+ 4 // signed on time
|
|
||||||
];
|
|
||||||
_sentSignedOnTime = _context.clock().now() / 1000;
|
|
||||||
|
|
||||||
int off = 0;
|
|
||||||
System.arraycopy(_receivedX, 0, signed, off, _receivedX.length);
|
|
||||||
off += _receivedX.length;
|
|
||||||
getSentY();
|
|
||||||
System.arraycopy(_sentY, 0, signed, off, _sentY.length);
|
|
||||||
off += _sentY.length;
|
|
||||||
System.arraycopy(_aliceIP, 0, signed, off, _aliceIP.length);
|
|
||||||
off += _aliceIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _alicePort);
|
|
||||||
off += 2;
|
|
||||||
System.arraycopy(_bobIP, 0, signed, off, _bobIP.length);
|
|
||||||
off += _bobIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _bobPort);
|
|
||||||
off += 2;
|
|
||||||
DataHelper.toLong(signed, off, 4, _sentRelayTag);
|
|
||||||
off += 4;
|
|
||||||
DataHelper.toLong(signed, off, 4, _sentSignedOnTime);
|
|
||||||
|
|
||||||
_sentSignature = _context.dsa().sign(signed, _context.keyManager().getSigningPrivateKey());
|
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG)) {
|
|
||||||
StringBuilder buf = new StringBuilder(128);
|
|
||||||
buf.append("Signing sessionCreated:");
|
|
||||||
//buf.append(" ReceivedX: ").append(Base64.encode(_receivedX));
|
|
||||||
//buf.append(" SentY: ").append(Base64.encode(_sentY));
|
|
||||||
buf.append(" Alice: ").append(Addresses.toString(_aliceIP, _alicePort));
|
|
||||||
buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
|
|
||||||
buf.append(" RelayTag: ").append(_sentRelayTag);
|
|
||||||
buf.append(" SignedOn: ").append(_sentSignedOnTime);
|
|
||||||
buf.append(" signature: ").append(Base64.encode(_sentSignature.getData()));
|
|
||||||
_log.debug(buf.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** note that we just sent a SessionCreated packet */
|
/** note that we just sent a SessionCreated packet */
|
||||||
public synchronized void createdPacketSent() {
|
public synchronized void createdPacketSent() {
|
||||||
_lastSend = _context.clock().now();
|
_lastSend = _context.clock().now();
|
||||||
@@ -357,79 +228,6 @@ class InboundEstablishState {
|
|||||||
/** RemoteHostId, uniquely identifies an attempt */
|
/** RemoteHostId, uniquely identifies an attempt */
|
||||||
RemoteHostId getRemoteHostId() { return _remoteHostId; }
|
RemoteHostId getRemoteHostId() { return _remoteHostId; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Note that while a SessionConfirmed could in theory be fragmented,
|
|
||||||
* in practice a RouterIdentity is 387 bytes and a single fragment is 512 bytes max,
|
|
||||||
* so it will never be fragmented.
|
|
||||||
*/
|
|
||||||
public synchronized void receiveSessionConfirmed(UDPPacketReader.SessionConfirmedReader conf) {
|
|
||||||
if (_receivedIdentity == null)
|
|
||||||
_receivedIdentity = new byte[conf.readTotalFragmentNum()][];
|
|
||||||
int cur = conf.readCurrentFragmentNum();
|
|
||||||
if (cur >= _receivedIdentity.length) {
|
|
||||||
// avoid AIOOBE
|
|
||||||
// should do more than this, but what? disconnect?
|
|
||||||
fail();
|
|
||||||
packetReceived();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_receivedIdentity[cur] == null) {
|
|
||||||
byte fragment[] = new byte[conf.readCurrentFragmentSize()];
|
|
||||||
conf.readFragmentData(fragment, 0);
|
|
||||||
_receivedIdentity[cur] = fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur == _receivedIdentity.length-1) {
|
|
||||||
_receivedSignedOnTime = conf.readFinalFragmentSignedOnTime();
|
|
||||||
// TODO verify time to prevent replay attacks
|
|
||||||
buildIdentity();
|
|
||||||
if (_receivedUnconfirmedIdentity != null) {
|
|
||||||
SigType type = _receivedUnconfirmedIdentity.getSigningPublicKey().getType();
|
|
||||||
if (type != null) {
|
|
||||||
int sigLen = type.getSigLen();
|
|
||||||
if (_receivedSignature == null)
|
|
||||||
_receivedSignature = new byte[sigLen];
|
|
||||||
conf.readFinalSignature(_receivedSignature, 0, sigLen);
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Unsupported sig type from: " + toString());
|
|
||||||
// _x() in UDPTransport
|
|
||||||
_context.banlist().banlistRouterForever(_receivedUnconfirmedIdentity.calculateHash(),
|
|
||||||
"Unsupported signature type");
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
Hash h = _receivedUnconfirmedIdentity.calculateHash();
|
|
||||||
if (_context.banlist().isBanlistedForever(h)) {
|
|
||||||
// validate sig to prevent spoofing
|
|
||||||
if (getConfirmedIdentity() != null)
|
|
||||||
_context.blocklist().add(_aliceIP);
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Router is banned: " + h.toBase64() + " on " + this);
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Bad ident from: " + toString());
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (_currentState == InboundState.IB_STATE_UNKNOWN) ||
|
|
||||||
(_currentState == InboundState.IB_STATE_REQUEST_RECEIVED) ||
|
|
||||||
(_currentState == InboundState.IB_STATE_CREATED_SENT) ) {
|
|
||||||
if (confirmedFullyReceived())
|
|
||||||
_currentState = InboundState.IB_STATE_CONFIRMED_COMPLETELY;
|
|
||||||
else
|
|
||||||
_currentState = InboundState.IB_STATE_CONFIRMED_PARTIALLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_createdSentCount == 1) {
|
|
||||||
_rtt = (int) ( _context.clock().now() - _lastSend );
|
|
||||||
}
|
|
||||||
|
|
||||||
packetReceived();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Have we fully received the SessionConfirmed messages from Alice?
|
* Have we fully received the SessionConfirmed messages from Alice?
|
||||||
* Caller must synch on this.
|
* Caller must synch on this.
|
||||||
@@ -452,111 +250,9 @@ class InboundEstablishState {
|
|||||||
* Note that this isn't really confirmed - see below.
|
* Note that this isn't really confirmed - see below.
|
||||||
*/
|
*/
|
||||||
public synchronized RouterIdentity getConfirmedIdentity() {
|
public synchronized RouterIdentity getConfirmedIdentity() {
|
||||||
if (!_verificationAttempted) {
|
|
||||||
verifyIdentity();
|
|
||||||
_verificationAttempted = true;
|
|
||||||
}
|
|
||||||
return _receivedConfirmedIdentity;
|
return _receivedConfirmedIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct Alice's RouterIdentity.
|
|
||||||
* Must have received all fragments.
|
|
||||||
* Sets _receivedUnconfirmedIdentity, unless invalid.
|
|
||||||
*
|
|
||||||
* Caller must synch on this.
|
|
||||||
*
|
|
||||||
* @since 0.9.16 was in verifyIdentity()
|
|
||||||
*/
|
|
||||||
private void buildIdentity() {
|
|
||||||
if (_receivedUnconfirmedIdentity != null)
|
|
||||||
return; // dup pkt?
|
|
||||||
int frags = _receivedIdentity.length;
|
|
||||||
byte[] ident;
|
|
||||||
if (frags > 1) {
|
|
||||||
int identSize = 0;
|
|
||||||
for (int i = 0; i < _receivedIdentity.length; i++)
|
|
||||||
identSize += _receivedIdentity[i].length;
|
|
||||||
ident = new byte[identSize];
|
|
||||||
int off = 0;
|
|
||||||
for (int i = 0; i < _receivedIdentity.length; i++) {
|
|
||||||
int len = _receivedIdentity[i].length;
|
|
||||||
System.arraycopy(_receivedIdentity[i], 0, ident, off, len);
|
|
||||||
off += len;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// no need to copy
|
|
||||||
ident = _receivedIdentity[0];
|
|
||||||
}
|
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(ident);
|
|
||||||
RouterIdentity peer = new RouterIdentity();
|
|
||||||
try {
|
|
||||||
peer.readBytes(in);
|
|
||||||
_receivedUnconfirmedIdentity = peer;
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Improperly formatted yet fully received ident", dfe);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Improperly formatted yet fully received ident", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if Alice sent us a valid confirmation packet. The
|
|
||||||
* identity signs: Alice's IP + Alice's port + Bob's IP + Bob's port
|
|
||||||
* + Alice's new relay key + Alice's signed on time
|
|
||||||
*
|
|
||||||
* Note that the protocol does not include a signature of the RouterIdentity,
|
|
||||||
* which could be a problem?
|
|
||||||
*
|
|
||||||
* Caller must synch on this.
|
|
||||||
*/
|
|
||||||
private void verifyIdentity() {
|
|
||||||
if (_receivedUnconfirmedIdentity == null)
|
|
||||||
return; // either not yet recvd or bad ident
|
|
||||||
if (_receivedSignature == null)
|
|
||||||
return; // either not yet recvd or bad sig
|
|
||||||
|
|
||||||
byte signed[] = new byte[256+256 // X + Y
|
|
||||||
+ _aliceIP.length + 2
|
|
||||||
+ _bobIP.length + 2
|
|
||||||
+ 4 // Alice's relay key
|
|
||||||
+ 4 // signed on time
|
|
||||||
];
|
|
||||||
|
|
||||||
int off = 0;
|
|
||||||
System.arraycopy(_receivedX, 0, signed, off, _receivedX.length);
|
|
||||||
off += _receivedX.length;
|
|
||||||
getSentY();
|
|
||||||
System.arraycopy(_sentY, 0, signed, off, _sentY.length);
|
|
||||||
off += _sentY.length;
|
|
||||||
System.arraycopy(_aliceIP, 0, signed, off, _aliceIP.length);
|
|
||||||
off += _aliceIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _alicePort);
|
|
||||||
off += 2;
|
|
||||||
System.arraycopy(_bobIP, 0, signed, off, _bobIP.length);
|
|
||||||
off += _bobIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _bobPort);
|
|
||||||
off += 2;
|
|
||||||
DataHelper.toLong(signed, off, 4, _sentRelayTag);
|
|
||||||
off += 4;
|
|
||||||
DataHelper.toLong(signed, off, 4, _receivedSignedOnTime);
|
|
||||||
Signature sig = new Signature(_receivedUnconfirmedIdentity.getSigType(), _receivedSignature);
|
|
||||||
boolean ok = _context.dsa().verifySignature(sig, signed, _receivedUnconfirmedIdentity.getSigningPublicKey());
|
|
||||||
if (ok) {
|
|
||||||
// todo partial spoof detection - get peer.calculateHash(),
|
|
||||||
// lookup in netdb locally, if not equal, fail?
|
|
||||||
_receivedConfirmedIdentity = _receivedUnconfirmedIdentity;
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Signature failed from " + _receivedUnconfirmedIdentity + "\non: " + this);
|
|
||||||
// we can't ban the hash because it could be spoofed, but we can block the IP
|
|
||||||
_context.blocklist().add(_aliceIP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call from synchronized method only
|
* Call from synchronized method only
|
||||||
*/
|
*/
|
||||||
@@ -569,15 +265,9 @@ class InboundEstablishState {
|
|||||||
StringBuilder buf = new StringBuilder(128);
|
StringBuilder buf = new StringBuilder(128);
|
||||||
buf.append("IES ");
|
buf.append("IES ");
|
||||||
buf.append(Addresses.toString(_aliceIP, _alicePort));
|
buf.append(Addresses.toString(_aliceIP, _alicePort));
|
||||||
//if (_receivedX != null)
|
|
||||||
// buf.append(" ReceivedX: ").append(Base64.encode(_receivedX, 0, 4));
|
|
||||||
//if (_sentY != null)
|
|
||||||
// buf.append(" SentY: ").append(Base64.encode(_sentY, 0, 4));
|
|
||||||
//buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
|
|
||||||
buf.append(" lifetime: ").append(DataHelper.formatDuration(getLifetime()));
|
buf.append(" lifetime: ").append(DataHelper.formatDuration(getLifetime()));
|
||||||
if (_sentRelayTag > 0)
|
if (_sentRelayTag > 0)
|
||||||
buf.append(" RelayTag: ").append(_sentRelayTag);
|
buf.append(" RelayTag: ").append(_sentRelayTag);
|
||||||
//buf.append(" SignedOn: ").append(_sentSignedOnTime);
|
|
||||||
buf.append(' ').append(_currentState);
|
buf.append(' ').append(_currentState);
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
@@ -552,11 +552,6 @@ class InboundEstablishState2 extends InboundEstablishState implements SSU2Payloa
|
|||||||
super.fail();
|
super.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSU 1 unsupported things
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void generateSessionKey() { throw new UnsupportedOperationException(); }
|
|
||||||
|
|
||||||
// SSU 2 things
|
// SSU 2 things
|
||||||
|
|
||||||
public long getSendConnID() { return _sendConnID; }
|
public long getSendConnID() { return _sendConnID; }
|
||||||
|
@@ -14,7 +14,6 @@ import net.i2p.data.i2np.DatabaseStoreMessage;
|
|||||||
import net.i2p.data.i2np.I2NPMessage;
|
import net.i2p.data.i2np.I2NPMessage;
|
||||||
import net.i2p.router.OutNetMessage;
|
import net.i2p.router.OutNetMessage;
|
||||||
import net.i2p.router.RouterContext;
|
import net.i2p.router.RouterContext;
|
||||||
import net.i2p.router.transport.crypto.DHSessionKeyBuilder;
|
|
||||||
import net.i2p.util.Addresses;
|
import net.i2p.util.Addresses;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
@@ -31,23 +30,18 @@ class OutboundEstablishState {
|
|||||||
private byte _sentX[];
|
private byte _sentX[];
|
||||||
protected byte _bobIP[];
|
protected byte _bobIP[];
|
||||||
protected int _bobPort;
|
protected int _bobPort;
|
||||||
private final DHSessionKeyBuilder.Factory _keyFactory;
|
|
||||||
private DHSessionKeyBuilder _keyBuilder;
|
|
||||||
// SessionCreated message
|
// SessionCreated message
|
||||||
private byte _receivedY[];
|
private byte _receivedY[];
|
||||||
protected byte _aliceIP[];
|
protected byte _aliceIP[];
|
||||||
protected int _alicePort;
|
protected int _alicePort;
|
||||||
protected long _receivedRelayTag;
|
protected long _receivedRelayTag;
|
||||||
private long _receivedSignedOnTime;
|
private long _receivedSignedOnTime;
|
||||||
private SessionKey _sessionKey;
|
|
||||||
private SessionKey _macKey;
|
|
||||||
private Signature _receivedSignature;
|
private Signature _receivedSignature;
|
||||||
// includes trailing padding to mod 16
|
// includes trailing padding to mod 16
|
||||||
private byte[] _receivedEncryptedSignature;
|
private byte[] _receivedEncryptedSignature;
|
||||||
private byte[] _receivedIV;
|
private byte[] _receivedIV;
|
||||||
// SessionConfirmed messages
|
// SessionConfirmed messages
|
||||||
private long _sentSignedOnTime;
|
private long _sentSignedOnTime;
|
||||||
private Signature _sentSignature;
|
|
||||||
// general status
|
// general status
|
||||||
protected final long _establishBegin;
|
protected final long _establishBegin;
|
||||||
//private long _lastReceive;
|
//private long _lastReceive;
|
||||||
@@ -130,51 +124,6 @@ class OutboundEstablishState {
|
|||||||
|
|
||||||
private static final long WAIT_FOR_HOLE_PUNCH_DELAY = 500;
|
private static final long WAIT_FOR_HOLE_PUNCH_DELAY = 500;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param claimedAddress an IP/port based RemoteHostId, or null if unknown
|
|
||||||
* @param remoteHostId non-null, == claimedAddress if direct, or a hash-based one if indirect
|
|
||||||
* @param remotePeer must have supported sig type
|
|
||||||
* @param allowExtendedOptions are we allowed to send extended options to Bob?
|
|
||||||
* @param needIntroduction should we ask Bob to be an introducer for us?
|
|
||||||
ignored unless allowExtendedOptions is true
|
|
||||||
* @param introKey Bob's introduction key, as published in the netdb
|
|
||||||
* @param addr non-null
|
|
||||||
*/
|
|
||||||
public OutboundEstablishState(RouterContext ctx, RemoteHostId claimedAddress,
|
|
||||||
RemoteHostId remoteHostId,
|
|
||||||
RouterIdentity remotePeer, boolean allowExtendedOptions,
|
|
||||||
boolean needIntroduction,
|
|
||||||
SessionKey introKey, UDPAddress addr,
|
|
||||||
DHSessionKeyBuilder.Factory dh) {
|
|
||||||
_context = ctx;
|
|
||||||
_log = ctx.logManager().getLog(OutboundEstablishState.class);
|
|
||||||
if (claimedAddress != null) {
|
|
||||||
_bobIP = claimedAddress.getIP();
|
|
||||||
_bobPort = claimedAddress.getPort();
|
|
||||||
} else {
|
|
||||||
//_bobIP = null;
|
|
||||||
_bobPort = -1;
|
|
||||||
}
|
|
||||||
_claimedAddress = claimedAddress;
|
|
||||||
_remoteHostId = remoteHostId;
|
|
||||||
_allowExtendedOptions = allowExtendedOptions;
|
|
||||||
_needIntroduction = needIntroduction;
|
|
||||||
_remotePeer = remotePeer;
|
|
||||||
_introKey = introKey;
|
|
||||||
_queuedMessages = new LinkedBlockingQueue<OutNetMessage>();
|
|
||||||
_establishBegin = ctx.clock().now();
|
|
||||||
_remoteAddress = addr;
|
|
||||||
_introductionNonce = -1;
|
|
||||||
_keyFactory = dh;
|
|
||||||
if (addr.getIntroducerCount() > 0) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
|
|
||||||
_currentState = OutboundState.OB_STATE_PENDING_INTRO;
|
|
||||||
} else {
|
|
||||||
_currentState = OutboundState.OB_STATE_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For SSU2
|
* For SSU2
|
||||||
*
|
*
|
||||||
@@ -204,7 +153,6 @@ class OutboundEstablishState {
|
|||||||
_establishBegin = ctx.clock().now();
|
_establishBegin = ctx.clock().now();
|
||||||
_remoteAddress = addr;
|
_remoteAddress = addr;
|
||||||
_introductionNonce = -1;
|
_introductionNonce = -1;
|
||||||
_keyFactory = null;
|
|
||||||
if (addr.getIntroducerCount() > 0) {
|
if (addr.getIntroducerCount() > 0) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
|
_log.debug("new outbound establish to " + remotePeer.calculateHash() + ", with address: " + addr);
|
||||||
@@ -294,29 +242,6 @@ class OutboundEstablishState {
|
|||||||
*/
|
*/
|
||||||
public SessionKey getIntroKey() { return _introKey; }
|
public SessionKey getIntroKey() { return _introKey; }
|
||||||
|
|
||||||
/** caller must synch - only call once */
|
|
||||||
private void prepareSessionRequest() {
|
|
||||||
_keyBuilder = _keyFactory.getBuilder();
|
|
||||||
byte X[] = _keyBuilder.getMyPublicValue().toByteArray();
|
|
||||||
if (X.length == 257) {
|
|
||||||
_sentX = new byte[256];
|
|
||||||
System.arraycopy(X, 1, _sentX, 0, _sentX.length);
|
|
||||||
} else if (X.length == 256) {
|
|
||||||
_sentX = X;
|
|
||||||
} else {
|
|
||||||
_sentX = new byte[256];
|
|
||||||
System.arraycopy(X, 0, _sentX, _sentX.length - X.length, X.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized byte[] getSentX() {
|
|
||||||
// We defer keygen until now so that it gets done in the Establisher loop,
|
|
||||||
// and so that we don't waste entropy on failed introductions
|
|
||||||
if (_sentX == null)
|
|
||||||
prepareSessionRequest();
|
|
||||||
return _sentX;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The remote side (Bob) - note that in some places he's called Charlie.
|
* The remote side (Bob) - note that in some places he's called Charlie.
|
||||||
* Warning - may change after introduction. May be null before introduction.
|
* Warning - may change after introduction. May be null before introduction.
|
||||||
@@ -329,62 +254,6 @@ class OutboundEstablishState {
|
|||||||
*/
|
*/
|
||||||
public synchronized int getSentPort() { return _bobPort; }
|
public synchronized int getSentPort() { return _bobPort; }
|
||||||
|
|
||||||
public synchronized void receiveSessionCreated(UDPPacketReader.SessionCreatedReader reader) {
|
|
||||||
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Session created already failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (_receivedY != null) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Session created already received, ignoring");
|
|
||||||
return; // already received
|
|
||||||
}
|
|
||||||
_receivedY = new byte[UDPPacketReader.SessionCreatedReader.Y_LENGTH];
|
|
||||||
reader.readY(_receivedY, 0);
|
|
||||||
if (_aliceIP == null)
|
|
||||||
_aliceIP = new byte[reader.readIPSize()];
|
|
||||||
reader.readIP(_aliceIP, 0);
|
|
||||||
_alicePort = reader.readPort();
|
|
||||||
_receivedRelayTag = reader.readRelayTag();
|
|
||||||
_receivedSignedOnTime = reader.readSignedOnTime();
|
|
||||||
// handle variable signature size
|
|
||||||
SigType type = _remotePeer.getSigningPublicKey().getType();
|
|
||||||
if (type == null) {
|
|
||||||
// shouldn't happen, we only connect to supported peers
|
|
||||||
fail();
|
|
||||||
packetReceived();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int sigLen = type.getSigLen();
|
|
||||||
int mod = sigLen % 16;
|
|
||||||
int pad = (mod == 0) ? 0 : (16 - mod);
|
|
||||||
int esigLen = sigLen + pad;
|
|
||||||
_receivedEncryptedSignature = new byte[esigLen];
|
|
||||||
reader.readEncryptedSignature(_receivedEncryptedSignature, 0, esigLen);
|
|
||||||
_receivedIV = new byte[UDPPacket.IV_SIZE];
|
|
||||||
reader.readIV(_receivedIV, 0);
|
|
||||||
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Receive session created:Sig: " + Base64.encode(_receivedEncryptedSignature)
|
|
||||||
+ "receivedIV: " + Base64.encode(_receivedIV)
|
|
||||||
+ "AliceIP: " + Addresses.toString(_aliceIP)
|
|
||||||
+ " RelayTag: " + _receivedRelayTag
|
|
||||||
+ " SignedOn: " + _receivedSignedOnTime
|
|
||||||
+ ' ' + this.toString());
|
|
||||||
|
|
||||||
if (_currentState == OutboundState.OB_STATE_UNKNOWN ||
|
|
||||||
_currentState == OutboundState.OB_STATE_REQUEST_SENT ||
|
|
||||||
_currentState == OutboundState.OB_STATE_INTRODUCED ||
|
|
||||||
_currentState == OutboundState.OB_STATE_PENDING_INTRO)
|
|
||||||
_currentState = OutboundState.OB_STATE_CREATED_RECEIVED;
|
|
||||||
|
|
||||||
if (_requestSentCount == 1) {
|
|
||||||
_rtt = (int) (_context.clock().now() - _requestSentTime);
|
|
||||||
}
|
|
||||||
packetReceived();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocking call (run in the establisher thread) to determine if the
|
* Blocking call (run in the establisher thread) to determine if the
|
||||||
* session was created properly. If it wasn't, all the SessionCreated
|
* session was created properly. If it wasn't, all the SessionCreated
|
||||||
@@ -396,38 +265,7 @@ class OutboundEstablishState {
|
|||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public synchronized boolean validateSessionCreated() {
|
public synchronized boolean validateSessionCreated() {
|
||||||
if (_currentState == OutboundState.OB_STATE_VALIDATION_FAILED) {
|
throw new UnsupportedOperationException("see override");
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Session created already failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (_receivedSignature != null) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Session created already validated");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean valid = true;
|
|
||||||
try {
|
|
||||||
generateSessionKey();
|
|
||||||
} catch (DHSessionKeyBuilder.InvalidPublicParameterException ippe) {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Peer " + getRemoteHostId() + " sent us an invalid DH parameter", ippe);
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
if (valid)
|
|
||||||
decryptSignature();
|
|
||||||
|
|
||||||
if (valid && verifySessionCreated()) {
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Session created passed validation");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("Session created failed validation, clearing state for " + _remoteHostId.toString());
|
|
||||||
fail();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -441,11 +279,6 @@ class OutboundEstablishState {
|
|||||||
_receivedEncryptedSignature = null;
|
_receivedEncryptedSignature = null;
|
||||||
_receivedIV = null;
|
_receivedIV = null;
|
||||||
_receivedSignature = null;
|
_receivedSignature = null;
|
||||||
if (_keyBuilder != null) {
|
|
||||||
//if (_keyBuilder.getPeerPublicValue() == null)
|
|
||||||
// _keyFactory.returnUnused(_keyBuilder);
|
|
||||||
_keyBuilder = null;
|
|
||||||
}
|
|
||||||
// sure, there's a chance the packet was corrupted, but in practice
|
// sure, there's a chance the packet was corrupted, but in practice
|
||||||
// this means that Bob doesn't know his external port, so give up.
|
// this means that Bob doesn't know his external port, so give up.
|
||||||
_currentState = OutboundState.OB_STATE_VALIDATION_FAILED;
|
_currentState = OutboundState.OB_STATE_VALIDATION_FAILED;
|
||||||
@@ -453,155 +286,12 @@ class OutboundEstablishState {
|
|||||||
_nextSend = _context.clock().now();
|
_nextSend = _context.clock().now();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates session key and mac key.
|
|
||||||
* Caller must synch on this.
|
|
||||||
*/
|
|
||||||
private void generateSessionKey() throws DHSessionKeyBuilder.InvalidPublicParameterException {
|
|
||||||
if (_sessionKey != null) return;
|
|
||||||
if (_keyBuilder == null)
|
|
||||||
throw new DHSessionKeyBuilder.InvalidPublicParameterException("Illegal state - never generated a key builder");
|
|
||||||
try {
|
|
||||||
_keyBuilder.setPeerPublicValue(_receivedY);
|
|
||||||
} catch (IllegalStateException ise) {
|
|
||||||
throw new DHSessionKeyBuilder.InvalidPublicParameterException("reused keys?", ise);
|
|
||||||
}
|
|
||||||
_sessionKey = _keyBuilder.getSessionKey();
|
|
||||||
ByteArray extra = _keyBuilder.getExtraBytes();
|
|
||||||
_macKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
|
|
||||||
System.arraycopy(extra.getData(), 0, _macKey.getData(), 0, SessionKey.KEYSIZE_BYTES);
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Established outbound keys. cipher: " + _sessionKey
|
|
||||||
+ " mac: " + _macKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* decrypt the signature (and subsequent pad bytes) with the
|
|
||||||
* additional layer of encryption using the negotiated key along side
|
|
||||||
* the packet's IV
|
|
||||||
*
|
|
||||||
* Caller must synch on this.
|
|
||||||
* Only call this once! Decrypts in-place.
|
|
||||||
*/
|
|
||||||
private void decryptSignature() {
|
|
||||||
if (_receivedEncryptedSignature == null) throw new NullPointerException("encrypted signature is null! this=" + this.toString());
|
|
||||||
if (_sessionKey == null) throw new NullPointerException("SessionKey is null!");
|
|
||||||
if (_receivedIV == null) throw new NullPointerException("IV is null!");
|
|
||||||
_context.aes().decrypt(_receivedEncryptedSignature, 0, _receivedEncryptedSignature, 0,
|
|
||||||
_sessionKey, _receivedIV, _receivedEncryptedSignature.length);
|
|
||||||
// handle variable signature size
|
|
||||||
SigType type = _remotePeer.getSigningPublicKey().getType();
|
|
||||||
// if type == null throws NPE
|
|
||||||
int sigLen = type.getSigLen();
|
|
||||||
int mod = sigLen % 16;
|
|
||||||
if (mod != 0) {
|
|
||||||
byte signatureBytes[] = new byte[sigLen];
|
|
||||||
System.arraycopy(_receivedEncryptedSignature, 0, signatureBytes, 0, sigLen);
|
|
||||||
_receivedSignature = new Signature(type, signatureBytes);
|
|
||||||
} else {
|
|
||||||
_receivedSignature = new Signature(type, _receivedEncryptedSignature);
|
|
||||||
}
|
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
|
||||||
_log.debug("Decrypted received signature: " + Base64.encode(_receivedSignature.getData()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify: Alice's IP + Alice's port + Bob's IP + Bob's port + Alice's
|
|
||||||
* new relay tag + Bob's signed on time
|
|
||||||
* Caller must synch on this.
|
|
||||||
*/
|
|
||||||
private boolean verifySessionCreated() {
|
|
||||||
byte signed[] = new byte[256+256 // X + Y
|
|
||||||
+ _aliceIP.length + 2
|
|
||||||
+ _bobIP.length + 2
|
|
||||||
+ 4 // sent relay tag
|
|
||||||
+ 4 // signed on time
|
|
||||||
];
|
|
||||||
|
|
||||||
int off = 0;
|
|
||||||
System.arraycopy(_sentX, 0, signed, off, _sentX.length);
|
|
||||||
off += _sentX.length;
|
|
||||||
System.arraycopy(_receivedY, 0, signed, off, _receivedY.length);
|
|
||||||
off += _receivedY.length;
|
|
||||||
System.arraycopy(_aliceIP, 0, signed, off, _aliceIP.length);
|
|
||||||
off += _aliceIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _alicePort);
|
|
||||||
off += 2;
|
|
||||||
System.arraycopy(_bobIP, 0, signed, off, _bobIP.length);
|
|
||||||
off += _bobIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _bobPort);
|
|
||||||
off += 2;
|
|
||||||
DataHelper.toLong(signed, off, 4, _receivedRelayTag);
|
|
||||||
off += 4;
|
|
||||||
DataHelper.toLong(signed, off, 4, _receivedSignedOnTime);
|
|
||||||
boolean valid = _context.dsa().verifySignature(_receivedSignature, signed, _remotePeer.getSigningPublicKey());
|
|
||||||
if (_log.shouldLog(Log.DEBUG) || (_log.shouldLog(Log.WARN) && !valid)) {
|
|
||||||
StringBuilder buf = new StringBuilder(128);
|
|
||||||
buf.append("Signed sessionCreated:");
|
|
||||||
buf.append(" Alice: ").append(Addresses.toString(_aliceIP, _alicePort));
|
|
||||||
buf.append(" Bob: ").append(Addresses.toString(_bobIP, _bobPort));
|
|
||||||
buf.append(" RelayTag: ").append(_receivedRelayTag);
|
|
||||||
buf.append(" SignedOn: ").append(_receivedSignedOnTime);
|
|
||||||
buf.append(" signature: ").append(Base64.encode(_receivedSignature.getData()));
|
|
||||||
if (valid)
|
|
||||||
_log.debug(buf.toString());
|
|
||||||
else if (_log.shouldLog(Log.WARN))
|
|
||||||
_log.warn("INVALID: " + buf.toString());
|
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized SessionKey getCipherKey() { return _sessionKey; }
|
|
||||||
public synchronized SessionKey getMACKey() { return _macKey; }
|
|
||||||
|
|
||||||
public synchronized long getReceivedRelayTag() { return _receivedRelayTag; }
|
public synchronized long getReceivedRelayTag() { return _receivedRelayTag; }
|
||||||
public synchronized long getSentSignedOnTime() { return _sentSignedOnTime; }
|
public synchronized long getSentSignedOnTime() { return _sentSignedOnTime; }
|
||||||
public synchronized long getReceivedSignedOnTime() { return _receivedSignedOnTime; }
|
public synchronized long getReceivedSignedOnTime() { return _receivedSignedOnTime; }
|
||||||
public synchronized byte[] getReceivedIP() { return _aliceIP; }
|
public synchronized byte[] getReceivedIP() { return _aliceIP; }
|
||||||
public synchronized int getReceivedPort() { return _alicePort; }
|
public synchronized int getReceivedPort() { return _alicePort; }
|
||||||
|
|
||||||
/**
|
|
||||||
* Let's sign everything so we can fragment properly.
|
|
||||||
*
|
|
||||||
* Note that while a SessionConfirmed could in theory be fragmented,
|
|
||||||
* in practice a RouterIdentity is 387 bytes and a single fragment is 512 bytes max,
|
|
||||||
* so it will never be fragmented.
|
|
||||||
*/
|
|
||||||
public synchronized void prepareSessionConfirmed() {
|
|
||||||
if (_sentSignedOnTime > 0)
|
|
||||||
return;
|
|
||||||
byte signed[] = new byte[256+256 // X + Y
|
|
||||||
+ _aliceIP.length + 2
|
|
||||||
+ _bobIP.length + 2
|
|
||||||
+ 4 // Alice's relay key
|
|
||||||
+ 4 // signed on time
|
|
||||||
];
|
|
||||||
|
|
||||||
_sentSignedOnTime = _context.clock().now() / 1000;
|
|
||||||
|
|
||||||
int off = 0;
|
|
||||||
System.arraycopy(_sentX, 0, signed, off, _sentX.length);
|
|
||||||
off += _sentX.length;
|
|
||||||
System.arraycopy(_receivedY, 0, signed, off, _receivedY.length);
|
|
||||||
off += _receivedY.length;
|
|
||||||
System.arraycopy(_aliceIP, 0, signed, off, _aliceIP.length);
|
|
||||||
off += _aliceIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _alicePort);
|
|
||||||
off += 2;
|
|
||||||
System.arraycopy(_bobIP, 0, signed, off, _bobIP.length);
|
|
||||||
off += _bobIP.length;
|
|
||||||
DataHelper.toLong(signed, off, 2, _bobPort);
|
|
||||||
off += 2;
|
|
||||||
DataHelper.toLong(signed, off, 4, _receivedRelayTag);
|
|
||||||
off += 4;
|
|
||||||
DataHelper.toLong(signed, off, 4, _sentSignedOnTime);
|
|
||||||
// BUG - if SigningPrivateKey is null, _sentSignature will be null, leading to NPE later
|
|
||||||
// should we throw something from here?
|
|
||||||
_sentSignature = _context.dsa().sign(signed, _context.keyManager().getSigningPrivateKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized Signature getSentSignature() { return _sentSignature; }
|
|
||||||
|
|
||||||
/** note that we just sent the SessionConfirmed packet */
|
/** note that we just sent the SessionConfirmed packet */
|
||||||
public synchronized void confirmedPacketsSent() {
|
public synchronized void confirmedPacketsSent() {
|
||||||
_lastSend = _context.clock().now();
|
_lastSend = _context.clock().now();
|
||||||
|
@@ -1,183 +0,0 @@
|
|||||||
package net.i2p.router.transport.udp;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
// following are for main() tests
|
|
||||||
//import java.security.InvalidKeyException;
|
|
||||||
//import java.security.Key;
|
|
||||||
//import java.security.NoSuchAlgorithmException;
|
|
||||||
//import javax.crypto.spec.SecretKeySpec;
|
|
||||||
//import net.i2p.data.Base64;
|
|
||||||
|
|
||||||
import net.i2p.crypto.HMACGenerator;
|
|
||||||
import net.i2p.data.DataHelper;
|
|
||||||
import net.i2p.data.Hash;
|
|
||||||
import net.i2p.data.SessionKey;
|
|
||||||
import net.i2p.util.SimpleByteCache;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the HMAC-MD5-128 of a key+message. All the good stuff occurs
|
|
||||||
* in {@link I2PHMac}
|
|
||||||
*
|
|
||||||
* Keys are always 32 bytes.
|
|
||||||
* This is used only by UDP.
|
|
||||||
* Use deprecated outside the router, this may move to router.jar.
|
|
||||||
*
|
|
||||||
* NOTE THIS IS NOT COMPATIBLE with javax.crypto.Mac.getInstance("HmacMD5")
|
|
||||||
* as we tell I2PHMac that the digest length is 32 bytes, so it generates
|
|
||||||
* a different result.
|
|
||||||
*
|
|
||||||
* Quote jrandom:
|
|
||||||
* "The HMAC is hardcoded to use SHA256 digest size
|
|
||||||
* for backwards compatability. next time we have a backwards
|
|
||||||
* incompatible change, we should update this."
|
|
||||||
*
|
|
||||||
* Does this mean he intended it to be compatible with MD5?
|
|
||||||
* See also 2005-07-05 status notes.
|
|
||||||
*
|
|
||||||
* @since 0.9.42 moved from net.i2p.crypto.HMACGenerator
|
|
||||||
*/
|
|
||||||
class SSUHMACGenerator extends HMACGenerator {
|
|
||||||
/** set of available HMAC instances for calculate */
|
|
||||||
private final LinkedBlockingQueue<I2PHMac> _available;
|
|
||||||
|
|
||||||
public SSUHMACGenerator() {
|
|
||||||
super();
|
|
||||||
_available = new LinkedBlockingQueue<I2PHMac>(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the HMAC of the data with the given key
|
|
||||||
*
|
|
||||||
* @param target out parameter the first 16 bytes contain the HMAC, the last 16 bytes are zero
|
|
||||||
* @param targetOffset offset into target to put the hmac
|
|
||||||
* @throws IllegalArgumentException for bad key or target too small
|
|
||||||
*/
|
|
||||||
public void calculate(SessionKey key, byte data[], int offset, int length, byte target[], int targetOffset) {
|
|
||||||
if ((key == null) || (key.getData() == null) || (data == null))
|
|
||||||
throw new NullPointerException("Null arguments for HMAC");
|
|
||||||
|
|
||||||
I2PHMac mac = acquire();
|
|
||||||
mac.init(key.getData());
|
|
||||||
mac.update(data, offset, length);
|
|
||||||
mac.doFinal(target, targetOffset);
|
|
||||||
release(mac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify the MAC inline, reducing some unnecessary memory churn.
|
|
||||||
*
|
|
||||||
* @param key session key to verify the MAC with
|
|
||||||
* @param curData MAC to verify
|
|
||||||
* @param curOffset index into curData to MAC
|
|
||||||
* @param curLength how much data in curData do we want to run the HMAC over
|
|
||||||
* @param origMAC what do we expect the MAC of curData to equal
|
|
||||||
* @param origMACOffset index into origMAC
|
|
||||||
* @param origMACLength how much of the MAC do we want to verify
|
|
||||||
* @throws IllegalArgumentException for bad key
|
|
||||||
*/
|
|
||||||
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength,
|
|
||||||
byte origMAC[], int origMACOffset, int origMACLength) {
|
|
||||||
if ((key == null) || (key.getData() == null) || (curData == null))
|
|
||||||
throw new NullPointerException("Null arguments for HMAC");
|
|
||||||
|
|
||||||
I2PHMac mac = acquire();
|
|
||||||
mac.init(key.getData());
|
|
||||||
mac.update(curData, curOffset, curLength);
|
|
||||||
byte rv[] = acquireTmp();
|
|
||||||
mac.doFinal(rv, 0);
|
|
||||||
release(mac);
|
|
||||||
|
|
||||||
boolean eq = DataHelper.eqCT(rv, 0, origMAC, origMACOffset, origMACLength);
|
|
||||||
releaseTmp(rv);
|
|
||||||
return eq;
|
|
||||||
}
|
|
||||||
|
|
||||||
private I2PHMac acquire() {
|
|
||||||
I2PHMac rv = _available.poll();
|
|
||||||
if (rv != null)
|
|
||||||
return rv;
|
|
||||||
// the HMAC is hardcoded to use SHA256 digest size
|
|
||||||
// for backwards compatability. next time we have a backwards
|
|
||||||
// incompatible change, we should update this by removing ", 32"
|
|
||||||
// SEE NOTES ABOVE
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
|
||||||
return new I2PHMac(md, 32);
|
|
||||||
} catch (NoSuchAlgorithmException nsae) {
|
|
||||||
throw new UnsupportedOperationException("MD5");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void release(I2PHMac mac) {
|
|
||||||
_available.offer(mac);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 0.9.42
|
|
||||||
*/
|
|
||||||
public void clearCache() {
|
|
||||||
_available.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
//private static final int RUNS = 100000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the BC and the JVM's implementations for speed
|
|
||||||
*/
|
|
||||||
/**** All this did was prove that we aren't compatible with standard HmacMD5
|
|
||||||
public static void main(String args[]) {
|
|
||||||
if (args.length != 2) {
|
|
||||||
System.err.println("Usage: HMACGenerator keySeedString dataString");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] rand = SHA256Generator.getInstance().calculateHash(args[0].getBytes()).getData();
|
|
||||||
byte[] data = args[1].getBytes();
|
|
||||||
Key keyObj = new SecretKeySpec(rand, "HmacMD5");
|
|
||||||
|
|
||||||
byte[] keyBytes = keyObj.getEncoded();
|
|
||||||
System.out.println("key bytes (" + keyBytes.length + ") is [" + Base64.encode(keyBytes) + "]");
|
|
||||||
SessionKey key = new SessionKey(keyBytes);
|
|
||||||
System.out.println("session key is [" + key);
|
|
||||||
System.out.println("key object is [" + keyObj);
|
|
||||||
|
|
||||||
HMACGenerator gen = new HMACGenerator(I2PAppContext.getGlobalContext());
|
|
||||||
byte[] result = new byte[16];
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
for (int i = 0; i < RUNS; i++) {
|
|
||||||
gen.calculate(key, data, 0, data.length, result, 0);
|
|
||||||
if (i == 0)
|
|
||||||
System.out.println("MAC [" + Base64.encode(result) + "]");
|
|
||||||
}
|
|
||||||
long time = System.currentTimeMillis() - start;
|
|
||||||
System.out.println("Time for " + RUNS + " HMAC-MD5 computations:");
|
|
||||||
System.out.println("BC time (ms): " + time);
|
|
||||||
|
|
||||||
start = System.currentTimeMillis();
|
|
||||||
javax.crypto.Mac mac;
|
|
||||||
try {
|
|
||||||
mac = javax.crypto.Mac.getInstance("HmacMD5");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
System.err.println("Fatal: " + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < RUNS; i++) {
|
|
||||||
try {
|
|
||||||
mac.init(keyObj);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
System.err.println("Fatal: " + e);
|
|
||||||
}
|
|
||||||
byte[] sha = mac.doFinal(data);
|
|
||||||
if (i == 0)
|
|
||||||
System.out.println("MAC [" + Base64.encode(sha) + "]");
|
|
||||||
}
|
|
||||||
time = System.currentTimeMillis() - start;
|
|
||||||
|
|
||||||
System.out.println("JVM time (ms): " + time);
|
|
||||||
}
|
|
||||||
****/
|
|
||||||
}
|
|
@@ -241,100 +241,6 @@ class UDPPacket implements CDPQEntry {
|
|||||||
}
|
}
|
||||||
return _remoteHost;
|
return _remoteHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate the packet against the MAC specified, returning true if the
|
|
||||||
* MAC matches, false otherwise.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public synchronized boolean validate(SessionKey macKey, HMACGenerator hmac) {
|
|
||||||
verifyNotReleased();
|
|
||||||
//_beforeValidate = _context.clock().now();
|
|
||||||
boolean eq = false;
|
|
||||||
Arrays.fill(_validateBuf, (byte)0);
|
|
||||||
|
|
||||||
// validate by comparing _data[0:15] and
|
|
||||||
// HMAC(payload + IV + (payloadLength ^ protocolVersion), macKey)
|
|
||||||
|
|
||||||
int payloadLength = _packet.getLength() - MAC_SIZE - IV_SIZE;
|
|
||||||
if (payloadLength > 0) {
|
|
||||||
int off = 0;
|
|
||||||
System.arraycopy(_data, _packet.getOffset() + MAC_SIZE + IV_SIZE, _validateBuf, off, payloadLength);
|
|
||||||
off += payloadLength;
|
|
||||||
System.arraycopy(_data, _packet.getOffset() + MAC_SIZE, _validateBuf, off, IV_SIZE);
|
|
||||||
off += IV_SIZE;
|
|
||||||
// version is zero, unlikely to ever change
|
|
||||||
int plval = payloadLength /* ^ PacketBuilder.PROTOCOL_VERSION */ ;
|
|
||||||
// network ID cross-check, proposal 147
|
|
||||||
int netid = _context.router().getNetworkID();
|
|
||||||
if (netid != 2) {
|
|
||||||
plval ^= (netid - 2) << 8;
|
|
||||||
}
|
|
||||||
DataHelper.toLong(_validateBuf, off, 2, plval);
|
|
||||||
off += 2;
|
|
||||||
|
|
||||||
eq = hmac.verify(macKey, _validateBuf, 0, off, _data, _packet.getOffset(), MAC_SIZE);
|
|
||||||
|
|
||||||
if (!eq) {
|
|
||||||
// this is relatively frequent, as you can get old keys in PacketHandler.
|
|
||||||
Log log = _context.logManager().getLog(UDPPacket.class);
|
|
||||||
if (log.shouldLog(Log.INFO)) {
|
|
||||||
byte[] calc = new byte[32];
|
|
||||||
hmac.calculate(macKey, _validateBuf, 0, off, calc, 0);
|
|
||||||
StringBuilder str = new StringBuilder(512);
|
|
||||||
str.append("Bad HMAC:\n\t");
|
|
||||||
str.append(_packet.getLength()).append(" byte pkt, ");
|
|
||||||
str.append(payloadLength).append(" byte payload");
|
|
||||||
str.append("\n\tFrom: ").append(getRemoteHost().toString());
|
|
||||||
str.append("\n\tIV: ").append(Base64.encode(_validateBuf, payloadLength, IV_SIZE));
|
|
||||||
str.append("\n\tIV2: ").append(Base64.encode(_data, MAC_SIZE, IV_SIZE));
|
|
||||||
str.append("\n\tGiven Len: ").append(DataHelper.fromLong(_validateBuf, payloadLength + IV_SIZE, 2));
|
|
||||||
str.append("\n\tCalc HMAC: ").append(Base64.encode(calc, 0, MAC_SIZE));
|
|
||||||
str.append("\n\tRead HMAC: ").append(Base64.encode(_data, _packet.getOffset(), MAC_SIZE));
|
|
||||||
str.append("\n\tUsing key: ").append(macKey.toBase64());
|
|
||||||
if (DataHelper.eq(macKey.getData(), 0, _context.routerHash().getData(), 0, 32))
|
|
||||||
str.append(" (Intro)");
|
|
||||||
else
|
|
||||||
str.append(" (Session)");
|
|
||||||
str.append("\n\tRaw: ").append(Base64.encode(_data, _packet.getOffset(), _packet.getLength()));
|
|
||||||
log.info(str.toString(), new Exception());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log log = _context.logManager().getLog(UDPPacket.class);
|
|
||||||
if (log.shouldLog(Log.WARN))
|
|
||||||
log.warn("Payload length is " + payloadLength + ", too short! From: " + getRemoteHost() + '\n' +
|
|
||||||
net.i2p.util.HexDump.dump(_data, _packet.getOffset(), _packet.getLength()));
|
|
||||||
}
|
|
||||||
|
|
||||||
//_afterValidate = _context.clock().now();
|
|
||||||
_validateCount++;
|
|
||||||
return eq;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt this valid packet, overwriting the _data buffer's payload
|
|
||||||
* with the decrypted data (leaving the MAC and IV unaltered)
|
|
||||||
*
|
|
||||||
* SSU 1 only.
|
|
||||||
* SSU 2 decryption is in PacketHandler.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public synchronized void decrypt(SessionKey cipherKey) {
|
|
||||||
verifyNotReleased();
|
|
||||||
System.arraycopy(_data, MAC_SIZE, _ivBuf, 0, IV_SIZE);
|
|
||||||
int len = _packet.getLength();
|
|
||||||
// As of 0.9.7, ignore padding beyond the last mod 16,
|
|
||||||
// it could otherwise blow up in decryption.
|
|
||||||
// This allows for better obfuscation.
|
|
||||||
// Probably works without this since _data is bigger than necessary, but let's not
|
|
||||||
// bother decrypting and risk overrun.
|
|
||||||
int rem = len & 0x0f;
|
|
||||||
if (rem != 0)
|
|
||||||
len -= rem;
|
|
||||||
int off = _packet.getOffset() + MAC_SIZE + IV_SIZE;
|
|
||||||
_context.aes().decrypt(_data, off, _data, off, cipherKey, _ivBuf, len - MAC_SIZE - IV_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For CDQ
|
* For CDQ
|
||||||
|
@@ -1,922 +0,0 @@
|
|||||||
package net.i2p.router.transport.udp;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.data.Base64;
|
|
||||||
import net.i2p.data.DataFormatException;
|
|
||||||
import net.i2p.data.DataHelper;
|
|
||||||
import net.i2p.data.SessionKey;
|
|
||||||
import net.i2p.data.Signature;
|
|
||||||
import net.i2p.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To read a packet, initialize this reader with the data and fetch out
|
|
||||||
* the appropriate fields. If the interesting bits are in message specific
|
|
||||||
* elements, grab the appropriate subreader.
|
|
||||||
*
|
|
||||||
* Many of the methods here and in the subclasses will throw AIOOBE on
|
|
||||||
* malformed packets, that should be caught also.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class UDPPacketReader {
|
|
||||||
//private final I2PAppContext _context;
|
|
||||||
//private final Log _log;
|
|
||||||
private byte _message[];
|
|
||||||
private int _payloadBeginOffset;
|
|
||||||
private int _payloadLength;
|
|
||||||
private final SessionRequestReader _sessionRequestReader;
|
|
||||||
private final SessionCreatedReader _sessionCreatedReader;
|
|
||||||
private final SessionConfirmedReader _sessionConfirmedReader;
|
|
||||||
private final DataReader _dataReader;
|
|
||||||
private final PeerTestReader _peerTestReader;
|
|
||||||
private final RelayRequestReader _relayRequestReader;
|
|
||||||
private final RelayIntroReader _relayIntroReader;
|
|
||||||
private final RelayResponseReader _relayResponseReader;
|
|
||||||
|
|
||||||
private static final int KEYING_MATERIAL_LENGTH = 64;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ctx unused
|
|
||||||
*/
|
|
||||||
public UDPPacketReader(I2PAppContext ctx) {
|
|
||||||
//_context = ctx;
|
|
||||||
//_log = ctx.logManager().getLog(UDPPacketReader.class);
|
|
||||||
_sessionRequestReader = new SessionRequestReader();
|
|
||||||
_sessionCreatedReader = new SessionCreatedReader();
|
|
||||||
_sessionConfirmedReader = new SessionConfirmedReader();
|
|
||||||
_dataReader = new DataReader();
|
|
||||||
_peerTestReader = new PeerTestReader();
|
|
||||||
_relayRequestReader = new RelayRequestReader();
|
|
||||||
_relayIntroReader = new RelayIntroReader();
|
|
||||||
_relayResponseReader = new RelayResponseReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(UDPPacket packet) {
|
|
||||||
int off = packet.getPacket().getOffset();
|
|
||||||
int len = packet.getPacket().getLength();
|
|
||||||
off += UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
|
||||||
len -= UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE;
|
|
||||||
initialize(packet.getPacket().getData(), off, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initialize(byte message[], int payloadOffset, int payloadLength) {
|
|
||||||
_message = message;
|
|
||||||
_payloadBeginOffset = payloadOffset;
|
|
||||||
_payloadLength = payloadLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what type of payload is in here? */
|
|
||||||
public int readPayloadType() {
|
|
||||||
// 4 highest order bits == payload type
|
|
||||||
return (_message[_payloadBeginOffset] & 0xFF) >>> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does this packet include rekeying data in the header?
|
|
||||||
* Unused, should always be false.
|
|
||||||
*/
|
|
||||||
public boolean isRekeyingIncluded() {
|
|
||||||
return (_message[_payloadBeginOffset] & UDPPacket.HEADER_FLAG_REKEY) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does this packet include extended options in the header?
|
|
||||||
*/
|
|
||||||
public boolean isExtendedOptionsIncluded() {
|
|
||||||
return (_message[_payloadBeginOffset] & UDPPacket.HEADER_FLAG_EXTENDED_OPTIONS) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return seconds */
|
|
||||||
public long readTimestamp() {
|
|
||||||
// Note, this is unsigned, so we're good until February 2106
|
|
||||||
return DataHelper.fromLong(_message, _payloadBeginOffset + 1, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns rekeying data (64 bytes), or null if none.
|
|
||||||
* Unused, should always return null.
|
|
||||||
*
|
|
||||||
* @deprecated unused
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public byte[] readKeyingMaterial() {
|
|
||||||
if (!isRekeyingIncluded())
|
|
||||||
return null;
|
|
||||||
byte[] rv = new byte[KEYING_MATERIAL_LENGTH];
|
|
||||||
System.arraycopy(_message, _payloadBeginOffset + 1 + 4, rv, 0, KEYING_MATERIAL_LENGTH);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns extended option data, 0-255 bytes, or null if none.
|
|
||||||
* Returned array does NOT include the length byte.
|
|
||||||
*
|
|
||||||
* @return extended options or null if none is included
|
|
||||||
* @since 0.9.24
|
|
||||||
*/
|
|
||||||
public byte[] readExtendedOptions() {
|
|
||||||
if (!isExtendedOptionsIncluded())
|
|
||||||
return null;
|
|
||||||
int offset = _payloadBeginOffset + 1 + 4;
|
|
||||||
if (isRekeyingIncluded())
|
|
||||||
offset += KEYING_MATERIAL_LENGTH;
|
|
||||||
int optionsSize = _message[offset++] & 0xff;
|
|
||||||
byte[] rv = new byte[optionsSize];
|
|
||||||
System.arraycopy(_message, offset, rv, 0, optionsSize);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** index into the message where the body begins */
|
|
||||||
private int readBodyOffset() {
|
|
||||||
int offset = _payloadBeginOffset + 1 + 4;
|
|
||||||
if (isRekeyingIncluded())
|
|
||||||
offset += KEYING_MATERIAL_LENGTH;
|
|
||||||
if (isExtendedOptionsIncluded()) {
|
|
||||||
int optionsSize = _message[offset] & 0xff;
|
|
||||||
offset += optionsSize + 1;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SessionRequestReader getSessionRequestReader() { return _sessionRequestReader; }
|
|
||||||
public SessionCreatedReader getSessionCreatedReader() { return _sessionCreatedReader; }
|
|
||||||
public SessionConfirmedReader getSessionConfirmedReader() { return _sessionConfirmedReader; }
|
|
||||||
public DataReader getDataReader() { return _dataReader; }
|
|
||||||
public PeerTestReader getPeerTestReader() { return _peerTestReader; }
|
|
||||||
public RelayRequestReader getRelayRequestReader() { return _relayRequestReader; }
|
|
||||||
public RelayIntroReader getRelayIntroReader() { return _relayIntroReader; }
|
|
||||||
public RelayResponseReader getRelayResponseReader() { return _relayResponseReader; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
int type = readPayloadType();
|
|
||||||
switch (type) {
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_DATA:
|
|
||||||
return _dataReader.toString();
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_SESSION_CONFIRMED:
|
|
||||||
return "Session confirmed packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_SESSION_CREATED:
|
|
||||||
return "Session created packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_SESSION_REQUEST:
|
|
||||||
return "Session request packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_TEST:
|
|
||||||
return "Peer test packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_RELAY_INTRO:
|
|
||||||
return "Relay intro packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_RELAY_REQUEST:
|
|
||||||
return "Relay request packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE:
|
|
||||||
return "Relay response packet";
|
|
||||||
case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY:
|
|
||||||
return "Session destroyed packet";
|
|
||||||
default:
|
|
||||||
return "Unknown packet type " + type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toRawString(StringBuilder buf) {
|
|
||||||
if (_message != null)
|
|
||||||
buf.append(Base64.encode(_message, _payloadBeginOffset, _payloadLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- Begin Reader Classes ------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base
|
|
||||||
*
|
|
||||||
* @since 0.9.24
|
|
||||||
*/
|
|
||||||
public abstract class Reader {
|
|
||||||
/**
|
|
||||||
* Returns extended option data from the header, 0-255 bytes, or null if none.
|
|
||||||
* Returned array does NOT include the length byte.
|
|
||||||
*
|
|
||||||
* @return extended options or null if none is included
|
|
||||||
* @since 0.9.24
|
|
||||||
*/
|
|
||||||
public byte[] readExtendedOptions() {
|
|
||||||
return UDPPacketReader.this.readExtendedOptions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Help read the SessionRequest payload */
|
|
||||||
public class SessionRequestReader extends Reader {
|
|
||||||
public static final int X_LENGTH = 256;
|
|
||||||
public void readX(byte target[], int targetOffset) {
|
|
||||||
int readOffset = readBodyOffset();
|
|
||||||
System.arraycopy(_message, readOffset, target, targetOffset, X_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readIPSize() {
|
|
||||||
int offset = readBodyOffset() + X_LENGTH;
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what IP bob is reachable on */
|
|
||||||
public void readIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + X_LENGTH;
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Help read the SessionCreated payload */
|
|
||||||
public class SessionCreatedReader extends Reader {
|
|
||||||
public static final int Y_LENGTH = 256;
|
|
||||||
public void readY(byte target[], int targetOffset) {
|
|
||||||
int readOffset = readBodyOffset();
|
|
||||||
System.arraycopy(_message, readOffset, target, targetOffset, Y_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** sizeof(IP) */
|
|
||||||
public int readIPSize() {
|
|
||||||
int offset = readBodyOffset() + Y_LENGTH;
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what IP do they think we are coming on? */
|
|
||||||
public void readIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + Y_LENGTH;
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what port do they think we are coming from? */
|
|
||||||
public int readPort() {
|
|
||||||
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize();
|
|
||||||
return (int)DataHelper.fromLong(_message, offset, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** read in the 4 byte relayAs tag */
|
|
||||||
public long readRelayTag() {
|
|
||||||
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2;
|
|
||||||
return DataHelper.fromLong(_message, offset, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long readSignedOnTime() {
|
|
||||||
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2 + 4;
|
|
||||||
long rv = DataHelper.fromLong(_message, offset, 4);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("Signed on time offset: " + offset + " val: " + rv
|
|
||||||
// + "\nRawCreated: " + Base64.encode(_message, _payloadBeginOffset, _payloadLength));
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param size the amount to be copied, including padding to mod 16 */
|
|
||||||
public void readEncryptedSignature(byte target[], int targetOffset, int size) {
|
|
||||||
int offset = readBodyOffset() + Y_LENGTH + 1 + readIPSize() + 2 + 4 + 4;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readIV(byte target[], int targetOffset) {
|
|
||||||
int offset = _payloadBeginOffset - UDPPacket.IV_SIZE;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, UDPPacket.IV_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** parse out the confirmed message */
|
|
||||||
public class SessionConfirmedReader extends Reader {
|
|
||||||
/** which fragment is this? */
|
|
||||||
public int readCurrentFragmentNum() {
|
|
||||||
int readOffset = readBodyOffset();
|
|
||||||
return (_message[readOffset] & 0xFF) >>> 4;
|
|
||||||
}
|
|
||||||
/** how many fragments will there be? */
|
|
||||||
public int readTotalFragmentNum() {
|
|
||||||
int readOffset = readBodyOffset();
|
|
||||||
return (_message[readOffset] & 0xF);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readCurrentFragmentSize() {
|
|
||||||
int readOffset = readBodyOffset() + 1;
|
|
||||||
return (int)DataHelper.fromLong(_message, readOffset, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** read the fragment data from the nonterminal sessionConfirmed packet */
|
|
||||||
public void readFragmentData(byte target[], int targetOffset) {
|
|
||||||
int readOffset = readBodyOffset() + 1 + 2;
|
|
||||||
int len = readCurrentFragmentSize();
|
|
||||||
System.arraycopy(_message, readOffset, target, targetOffset, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the time at which the signature was generated.
|
|
||||||
* TODO must be completely in final fragment.
|
|
||||||
* Time and sig cannot be split across fragments.
|
|
||||||
*/
|
|
||||||
public long readFinalFragmentSignedOnTime() {
|
|
||||||
if (readCurrentFragmentNum() != readTotalFragmentNum()-1)
|
|
||||||
throw new IllegalStateException("This is not the final fragment");
|
|
||||||
int readOffset = readBodyOffset() + 1 + 2 + readCurrentFragmentSize();
|
|
||||||
return DataHelper.fromLong(_message, readOffset, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the signature from the final sessionConfirmed packet.
|
|
||||||
* TODO must be completely in final fragment.
|
|
||||||
* Time and sig cannot be split across fragments.
|
|
||||||
* @param size not including padding
|
|
||||||
*/
|
|
||||||
public void readFinalSignature(byte target[], int targetOffset, int size) {
|
|
||||||
if (readCurrentFragmentNum() != readTotalFragmentNum()-1)
|
|
||||||
throw new IllegalStateException("This is not the final fragment");
|
|
||||||
int readOffset = _payloadBeginOffset + _payloadLength - size;
|
|
||||||
if (readOffset < readBodyOffset() + (1 + 2 + 4))
|
|
||||||
throw new IllegalStateException("Sig split across fragments");
|
|
||||||
System.arraycopy(_message, readOffset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** parse out the data message */
|
|
||||||
public class DataReader extends Reader {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the data size, NOT including IP header, UDP header, IV, or MAC
|
|
||||||
*/
|
|
||||||
public int getPacketSize() { return _payloadLength; }
|
|
||||||
|
|
||||||
public boolean readACKsIncluded() {
|
|
||||||
return flagSet(UDPPacket.DATA_FLAG_EXPLICIT_ACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readACKBitfieldsIncluded() {
|
|
||||||
return flagSet(UDPPacket.DATA_FLAG_ACK_BITFIELDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readECN() {
|
|
||||||
return flagSet(UDPPacket.DATA_FLAG_ECN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readWantPreviousACKs() {
|
|
||||||
return flagSet(UDPPacket.DATA_FLAG_WANT_ACKS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readReplyRequested() {
|
|
||||||
return flagSet(UDPPacket.DATA_FLAG_WANT_REPLY);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readExtendedDataIncluded() {
|
|
||||||
return flagSet(UDPPacket.DATA_FLAG_EXTENDED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readACKCount() {
|
|
||||||
if (!readACKsIncluded()) return 0;
|
|
||||||
int off = readBodyOffset() + 1;
|
|
||||||
return _message[off] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long readACK(int index) {
|
|
||||||
if (!readACKsIncluded()) return -1;
|
|
||||||
int off = readBodyOffset() + 1;
|
|
||||||
//int num = (int)DataHelper.fromLong(_message, off, 1);
|
|
||||||
off++;
|
|
||||||
return DataHelper.fromLong(_message, off + (4 * index), 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ACKBitfield[] readACKBitfields() throws DataFormatException {
|
|
||||||
if (!readACKBitfieldsIncluded()) return null;
|
|
||||||
int off = readBodyOffset() + 1;
|
|
||||||
if (readACKsIncluded()) {
|
|
||||||
int numACKs = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
off += 4 * numACKs;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numBitfields = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
|
|
||||||
PacketACKBitfield rv[] = new PacketACKBitfield[numBitfields];
|
|
||||||
for (int i = 0; i < numBitfields; i++) {
|
|
||||||
rv[i] = new PacketACKBitfield(off);
|
|
||||||
off += rv[i].getByteLength();
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readFragmentCount() throws DataFormatException {
|
|
||||||
int off = readBodyOffset() + 1;
|
|
||||||
if (readACKsIncluded()) {
|
|
||||||
int numACKs = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
off += 4 * numACKs;
|
|
||||||
}
|
|
||||||
if (readACKBitfieldsIncluded()) {
|
|
||||||
int numBitfields = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
|
|
||||||
for (int i = 0; i < numBitfields; i++) {
|
|
||||||
PacketACKBitfield bf = new PacketACKBitfield(off);
|
|
||||||
off += bf.getByteLength();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (readExtendedDataIncluded()) {
|
|
||||||
int size = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
off += size;
|
|
||||||
}
|
|
||||||
return _message[off];
|
|
||||||
}
|
|
||||||
|
|
||||||
public long readMessageId(int fragmentNum) throws DataFormatException {
|
|
||||||
int fragmentBegin = getFragmentBegin(fragmentNum);
|
|
||||||
return DataHelper.fromLong(_message, fragmentBegin, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readMessageFragmentNum(int fragmentNum) throws DataFormatException {
|
|
||||||
int off = getFragmentBegin(fragmentNum);
|
|
||||||
off += 4; // messageId
|
|
||||||
return (_message[off] & 0xFF) >>> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean readMessageIsLast(int fragmentNum) throws DataFormatException {
|
|
||||||
int off = getFragmentBegin(fragmentNum);
|
|
||||||
off += 4; // messageId
|
|
||||||
return ((_message[off] & 1) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readMessageFragmentSize(int fragmentNum) throws DataFormatException {
|
|
||||||
int off = getFragmentBegin(fragmentNum);
|
|
||||||
off += 5; // messageId + fragment info
|
|
||||||
return ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readMessageFragment(int fragmentNum, byte target[], int targetOffset)
|
|
||||||
throws DataFormatException {
|
|
||||||
int off = getFragmentBegin(fragmentNum);
|
|
||||||
off += 5; // messageId + fragment info
|
|
||||||
int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
|
|
||||||
off += 2;
|
|
||||||
System.arraycopy(_message, off, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getFragmentBegin(int fragmentNum) throws DataFormatException {
|
|
||||||
int off = readBodyOffset() + 1;
|
|
||||||
if (readACKsIncluded()) {
|
|
||||||
int numACKs = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
off += 4 * numACKs;
|
|
||||||
}
|
|
||||||
if (readACKBitfieldsIncluded()) {
|
|
||||||
int numBitfields = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
|
|
||||||
PacketACKBitfield bf[] = new PacketACKBitfield[numBitfields];
|
|
||||||
for (int i = 0; i < numBitfields; i++) {
|
|
||||||
bf[i] = new PacketACKBitfield(off);
|
|
||||||
off += bf[i].getByteLength();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (readExtendedDataIncluded()) {
|
|
||||||
int size = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
off += size;
|
|
||||||
}
|
|
||||||
off++; // # fragments
|
|
||||||
|
|
||||||
if (fragmentNum > 0) {
|
|
||||||
for (int i = 0; i < fragmentNum; i++) {
|
|
||||||
off += 5; // messageId+info
|
|
||||||
off += ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
|
|
||||||
off += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return off;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean flagSet(byte flag) {
|
|
||||||
int flagOffset = readBodyOffset();
|
|
||||||
return ((_message[flagOffset] & flag) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder buf = new StringBuilder(512);
|
|
||||||
long msAgo = System.currentTimeMillis() - readTimestamp()*1000;
|
|
||||||
buf.append("Data packet sent ").append(msAgo).append("ms ago ");
|
|
||||||
buf.append("IV ");
|
|
||||||
buf.append(Base64.encode(_message, _payloadBeginOffset-UDPPacket.IV_SIZE, UDPPacket.IV_SIZE));
|
|
||||||
buf.append(" ");
|
|
||||||
int off = readBodyOffset() + 1;
|
|
||||||
if (readACKsIncluded()) {
|
|
||||||
int numACKs = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
buf.append("with ACKs for ");
|
|
||||||
for (int i = 0; i < numACKs; i++) {
|
|
||||||
buf.append(DataHelper.fromLong(_message, off, 4)).append(' ');
|
|
||||||
off += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (readACKBitfieldsIncluded()) {
|
|
||||||
int numBitfields = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
buf.append("with partial ACKs for ");
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < numBitfields; i++) {
|
|
||||||
PacketACKBitfield bf = new PacketACKBitfield(off);
|
|
||||||
buf.append(bf.getMessageId()).append(' ');
|
|
||||||
off += bf.getByteLength();
|
|
||||||
}
|
|
||||||
} catch (DataFormatException dfe) {
|
|
||||||
buf.append("CORRUPT");
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (readExtendedDataIncluded()) {
|
|
||||||
int size = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
buf.append("with extended size of ");
|
|
||||||
buf.append(size);
|
|
||||||
buf.append(' ');
|
|
||||||
off += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numFragments = _message[off] & 0xff;
|
|
||||||
off++;
|
|
||||||
buf.append("with fragmentCount of ");
|
|
||||||
buf.append(numFragments);
|
|
||||||
buf.append(' ');
|
|
||||||
|
|
||||||
for (int i = 0; i < numFragments; i++) {
|
|
||||||
buf.append("containing messageId ");
|
|
||||||
buf.append(DataHelper.fromLong(_message, off, 4));
|
|
||||||
off += 4;
|
|
||||||
int fragNum = (_message[off] & 0xFF) >>> 1;
|
|
||||||
boolean isLast = (_message[off] & 1) != 0;
|
|
||||||
off++;
|
|
||||||
buf.append(" frag# ").append(fragNum);
|
|
||||||
buf.append(" isLast? ").append(isLast);
|
|
||||||
buf.append(" info ").append(_message[off-1]);
|
|
||||||
int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
|
|
||||||
off += 2;
|
|
||||||
buf.append(" with ").append(size).append(" bytes; ");
|
|
||||||
off += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void toRawString(StringBuilder buf) throws DataFormatException {
|
|
||||||
UDPPacketReader.this.toRawString(buf);
|
|
||||||
buf.append(" payload: ");
|
|
||||||
|
|
||||||
int off = getFragmentBegin(0); // first fragment
|
|
||||||
off += 4 + 1; // messageId + fragment info
|
|
||||||
int size = ((int)DataHelper.fromLong(_message, off, 2)) & 0x3FFF;
|
|
||||||
off += 2;
|
|
||||||
buf.append(Base64.encode(_message, off, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to fetch the particular bitfields from the raw packet
|
|
||||||
*/
|
|
||||||
private class PacketACKBitfield implements ACKBitfield {
|
|
||||||
private final int _start;
|
|
||||||
private final int _bitfieldStart;
|
|
||||||
private final int _bitfieldSize;
|
|
||||||
|
|
||||||
public PacketACKBitfield(int start) throws DataFormatException {
|
|
||||||
_start = start;
|
|
||||||
_bitfieldStart = start + 4;
|
|
||||||
int bfsz = 1;
|
|
||||||
// bitfield is an array of bytes where the high bit is 1 if
|
|
||||||
// further bytes in the bitfield follow
|
|
||||||
while ((_message[_bitfieldStart + bfsz - 1] & UDPPacket.BITFIELD_CONTINUATION) != 0x0) {
|
|
||||||
bfsz++;
|
|
||||||
//if (bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES)
|
|
||||||
// throw new DataFormatException();
|
|
||||||
}
|
|
||||||
if (bfsz > InboundMessageState.MAX_PARTIAL_BITFIELD_BYTES)
|
|
||||||
throw new DataFormatException("bitfield size: " + bfsz);
|
|
||||||
_bitfieldSize = bfsz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMessageId() { return DataHelper.fromLong(_message, _start, 4); }
|
|
||||||
public int getByteLength() { return 4 + _bitfieldSize; }
|
|
||||||
public int fragmentCount() { return _bitfieldSize * 7; }
|
|
||||||
public boolean receivedComplete() { return false; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of fragments acked in this bitfield.
|
|
||||||
* Faster than looping through received()
|
|
||||||
* @since 0.9.16
|
|
||||||
*/
|
|
||||||
public int ackCount() {
|
|
||||||
int rv = 0;
|
|
||||||
for (int i = _bitfieldStart; i < _bitfieldStart + _bitfieldSize; i++) {
|
|
||||||
byte b = _message[i];
|
|
||||||
if ((b & 0x7f) != 0) {
|
|
||||||
for (int j = 0; j < 7; j++) {
|
|
||||||
if ((b & 0x01) != 0)
|
|
||||||
rv++;
|
|
||||||
b >>= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Highest fragment number acked in this bitfield.
|
|
||||||
* @return highest fragment number acked, or -1 if none
|
|
||||||
* @since 0.9.16
|
|
||||||
*/
|
|
||||||
public int highestReceived() {
|
|
||||||
for (int i = _bitfieldSize - 1; i >= 0; i--) {
|
|
||||||
byte b = _message[_bitfieldStart + i];
|
|
||||||
if ((b & 0x7f) == 0)
|
|
||||||
continue;
|
|
||||||
for (int j = 6; j >= 0; j--) {
|
|
||||||
if ((b & 0x40) != 0)
|
|
||||||
return (7 * i) + j;
|
|
||||||
b <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean received(int fragmentNum) {
|
|
||||||
if ( (fragmentNum < 0) || (fragmentNum >= _bitfieldSize*7) )
|
|
||||||
return false;
|
|
||||||
// the fragment has been received if the bit is set
|
|
||||||
int byteNum = _bitfieldStart + (fragmentNum/7);
|
|
||||||
int flagNum = fragmentNum % 7;
|
|
||||||
return (_message[byteNum] & (1 << flagNum)) != 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder buf = new StringBuilder(64);
|
|
||||||
buf.append("IB Partial ACK of ");
|
|
||||||
buf.append(getMessageId());
|
|
||||||
buf.append(" highest: ").append(highestReceived());
|
|
||||||
buf.append(" with ACKs for: [");
|
|
||||||
int numFrags = fragmentCount();
|
|
||||||
for (int i = 0; i < numFrags; i++) {
|
|
||||||
if (received(i))
|
|
||||||
buf.append(i).append(' ');
|
|
||||||
}
|
|
||||||
buf.append("] / ").append(numFrags);
|
|
||||||
return buf.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Help read the PeerTest payload */
|
|
||||||
public class PeerTestReader extends Reader {
|
|
||||||
private static final int NONCE_LENGTH = 4;
|
|
||||||
|
|
||||||
public long readNonce() {
|
|
||||||
int readOffset = readBodyOffset();
|
|
||||||
return DataHelper.fromLong(_message, readOffset, NONCE_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int readIPSize() {
|
|
||||||
int offset = readBodyOffset() + NONCE_LENGTH;
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what IP Alice is reachable on */
|
|
||||||
public void readIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + NONCE_LENGTH;
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what IP Alice is reachable on */
|
|
||||||
public int readPort() {
|
|
||||||
int offset = readBodyOffset() + NONCE_LENGTH;
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
offset += size; // skip the IP
|
|
||||||
return (int)DataHelper.fromLong(_message, offset, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what Alice's intro key is (if known - if unknown, the key is INVALID_KEY) */
|
|
||||||
public void readIntroKey(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + NONCE_LENGTH;
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2; // skip the size + port
|
|
||||||
offset += size; // skip the IP
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, SessionKey.KEYSIZE_BYTES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Help read the RelayRequest payload */
|
|
||||||
public class RelayRequestReader extends Reader {
|
|
||||||
public long readTag() {
|
|
||||||
long rv = DataHelper.fromLong(_message, readBodyOffset(), 4);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read alice tag: " + rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
public int readIPSize() {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
int rv = _message[offset] & 0xff;
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read alice ip size: " + rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what IP Alice is reachable on */
|
|
||||||
public void readIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read alice ip: " + Base64.encode(target, targetOffset, size));
|
|
||||||
}
|
|
||||||
public int readPort() {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
int rv = (int)DataHelper.fromLong(_message, offset, 2);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read alice port: " + rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** unused */
|
|
||||||
public int readChallengeSize() {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int rv = _message[offset] & 0xff;
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read challenge size: " + rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** unused */
|
|
||||||
public void readChallengeData(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, sz);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read challenge data: " + Base64.encode(target));
|
|
||||||
}
|
|
||||||
public void readAliceIntroKey(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
offset += sz;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, SessionKey.KEYSIZE_BYTES);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read alice intro key: " + Base64.encode(target, targetOffset, SessionKey.KEYSIZE_BYTES)
|
|
||||||
// + " packet size: " + _payloadLength + " off: " + offset + " data: " + Base64.encode(_message));
|
|
||||||
}
|
|
||||||
public long readNonce() {
|
|
||||||
int offset = readBodyOffset() + 4;
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
offset += sz;
|
|
||||||
offset += SessionKey.KEYSIZE_BYTES;
|
|
||||||
long rv = DataHelper.fromLong(_message, offset, 4);
|
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
|
||||||
// _log.debug("read request nonce: " + rv);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Help read the RelayIntro payload */
|
|
||||||
public class RelayIntroReader extends Reader {
|
|
||||||
public int readIPSize() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** what IP Alice is reachable on */
|
|
||||||
public void readIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
public int readPort() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
return (int)DataHelper.fromLong(_message, offset, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** unused */
|
|
||||||
public int readChallengeSize() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** unused */
|
|
||||||
public void readChallengeData(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, sz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/** Help read the RelayResponse payload */
|
|
||||||
public class RelayResponseReader extends Reader {
|
|
||||||
public int readCharlieIPSize() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
/** what IP charlie is reachable on */
|
|
||||||
public void readCharlieIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
int size = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, size);
|
|
||||||
}
|
|
||||||
/** what port charlie is reachable on */
|
|
||||||
public int readCharliePort() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
return (int)DataHelper.fromLong(_message, offset, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @deprecated unused */
|
|
||||||
@Deprecated
|
|
||||||
public int readAliceIPSize() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
return _message[offset] & 0xff;
|
|
||||||
}
|
|
||||||
/** @deprecated unused */
|
|
||||||
@Deprecated
|
|
||||||
public void readAliceIP(byte target[], int targetOffset) {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
System.arraycopy(_message, offset, target, targetOffset, sz);
|
|
||||||
}
|
|
||||||
/** @deprecated unused */
|
|
||||||
@Deprecated
|
|
||||||
public int readAlicePort() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset++;
|
|
||||||
offset += sz;
|
|
||||||
return (int)DataHelper.fromLong(_message, offset, 2);
|
|
||||||
}
|
|
||||||
public long readNonce() {
|
|
||||||
int offset = readBodyOffset();
|
|
||||||
offset += _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2;
|
|
||||||
int sz = _message[offset] & 0xff;
|
|
||||||
offset += 1 + 2; // sz + port
|
|
||||||
offset += sz;
|
|
||||||
return DataHelper.fromLong(_message, offset, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------- End Reader Classes ------- */
|
|
||||||
|
|
||||||
/******
|
|
||||||
public static void main(String args[]) {
|
|
||||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
|
||||||
try {
|
|
||||||
PacketBuilder b = new PacketBuilder(ctx, null);
|
|
||||||
InetAddress introHost = InetAddress.getLocalHost();
|
|
||||||
int introPort = 1234;
|
|
||||||
byte introKey[] = new byte[SessionKey.KEYSIZE_BYTES];
|
|
||||||
ctx.random().nextBytes(introKey);
|
|
||||||
long introTag = ctx.random().nextLong(0xFFFFFFFFl);
|
|
||||||
long introNonce = ctx.random().nextLong(0xFFFFFFFFl);
|
|
||||||
SessionKey ourIntroKey = ctx.keyGenerator().generateSessionKey();
|
|
||||||
UDPPacket packet = b.buildRelayRequest(introHost, introPort, introKey, introTag, ourIntroKey, introNonce, false);
|
|
||||||
UDPPacketReader r = new UDPPacketReader(ctx);
|
|
||||||
r.initialize(packet);
|
|
||||||
RelayRequestReader reader = r.getRelayRequestReader();
|
|
||||||
System.out.println("Nonce: " + reader.readNonce() + " / " + introNonce);
|
|
||||||
System.out.println("Tag : " + reader.readTag() + " / " + introTag);
|
|
||||||
byte readKey[] = new byte[SessionKey.KEYSIZE_BYTES];
|
|
||||||
reader.readAliceIntroKey(readKey, 0);
|
|
||||||
System.out.println("Key : " + Base64.encode(readKey) + " / " + ourIntroKey.toBase64());
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
*******/
|
|
||||||
}
|
|
@@ -23,7 +23,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
|
|
||||||
import net.i2p.CoreVersion;
|
import net.i2p.CoreVersion;
|
||||||
import net.i2p.crypto.EncType;
|
import net.i2p.crypto.EncType;
|
||||||
import net.i2p.crypto.HMACGenerator;
|
|
||||||
import net.i2p.crypto.KeyPair;
|
import net.i2p.crypto.KeyPair;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
@@ -102,7 +101,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
private long _v6IntroducersSelectedOn;
|
private long _v6IntroducersSelectedOn;
|
||||||
private long _lastInboundReceivedOn;
|
private long _lastInboundReceivedOn;
|
||||||
private final DHSessionKeyBuilder.Factory _dhFactory;
|
private final DHSessionKeyBuilder.Factory _dhFactory;
|
||||||
private final SSUHMACGenerator _hmac;
|
|
||||||
private int _mtu = PeerState.MIN_MTU;
|
private int _mtu = PeerState.MIN_MTU;
|
||||||
private int _mtu_ipv6 = PeerState.MIN_IPV6_MTU;
|
private int _mtu_ipv6 = PeerState.MIN_IPV6_MTU;
|
||||||
private int _mtu_ssu2;
|
private int _mtu_ssu2;
|
||||||
@@ -421,7 +419,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
_v4IntroducersSelectedOn = -1;
|
_v4IntroducersSelectedOn = -1;
|
||||||
_v6IntroducersSelectedOn = -1;
|
_v6IntroducersSelectedOn = -1;
|
||||||
_lastInboundReceivedOn = -1;
|
_lastInboundReceivedOn = -1;
|
||||||
_hmac = (dh != null) ? new SSUHMACGenerator() : null;
|
|
||||||
_mtu = PeerState.LARGE_MTU;
|
_mtu = PeerState.LARGE_MTU;
|
||||||
_mtu_ipv6 = PeerState.MIN_IPV6_MTU;
|
_mtu_ipv6 = PeerState.MIN_IPV6_MTU;
|
||||||
setupPort();
|
setupPort();
|
||||||
@@ -939,8 +936,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
UDPPacket.clearCache();
|
UDPPacket.clearCache();
|
||||||
UDPAddress.clearCache();
|
UDPAddress.clearCache();
|
||||||
_lastInboundIPv6 = 0;
|
_lastInboundIPv6 = 0;
|
||||||
if (_hmac != null)
|
|
||||||
_hmac.clearCache();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3707,22 +3702,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
// _log.debug("UDP transport returning " + skews.size() + " peer clock skews.");
|
// _log.debug("UDP transport returning " + skews.size() + " peer clock skews.");
|
||||||
return skews;
|
return skews;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a new DHSessionKeyBuilder, or null if SSU1 disabled
|
|
||||||
* @since 0.9
|
|
||||||
*/
|
|
||||||
DHSessionKeyBuilder getDHBuilder() {
|
|
||||||
return _enableSSU1 ? _dhFactory.getBuilder() : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the factory, or null if SSU1 disabled
|
|
||||||
* @since 0.9.2
|
|
||||||
*/
|
|
||||||
DHSessionKeyBuilder.Factory getDHFactory() {
|
|
||||||
return _dhFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return null if not configured for SSU2
|
* @return null if not configured for SSU2
|
||||||
@@ -3732,14 +3711,6 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
|||||||
return _xdhFactory;
|
return _xdhFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the SSU HMAC, or null if SSU1 disabled
|
|
||||||
* @since 0.9.42
|
|
||||||
*/
|
|
||||||
HMACGenerator getHMAC() {
|
|
||||||
return _hmac;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return null if not configured for SSU2
|
* @return null if not configured for SSU2
|
||||||
* @since 0.9.54
|
* @since 0.9.54
|
||||||
|
@@ -1,124 +0,0 @@
|
|||||||
package net.i2p.router.transport.udp;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2003, TheCrypto
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* - Redistributions of source code must retain the above copyright notice, this
|
|
||||||
* list of conditions and the following disclaimer.
|
|
||||||
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* - Neither the name of the TheCrypto may be used to endorse or promote
|
|
||||||
* products derived from this software without specific prior written
|
|
||||||
* permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
* POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.data.DataHelper;
|
|
||||||
import net.i2p.data.Hash;
|
|
||||||
import net.i2p.data.SessionKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warning, misnamed, this tests the SSU HMAC,
|
|
||||||
* not net.i2p.crypto.HMAC256Generator
|
|
||||||
*/
|
|
||||||
public class HMACSHA256Bench {
|
|
||||||
public static void main(String args[]) {
|
|
||||||
runTest(new I2PAppContext());
|
|
||||||
System.out.println("Running as MD5");
|
|
||||||
Properties props = new Properties();
|
|
||||||
//props.setProperty("i2p.fakeHMAC", "true");
|
|
||||||
props.setProperty("i2p.HMACMD5", "true");
|
|
||||||
runTest(new I2PAppContext(props));
|
|
||||||
}
|
|
||||||
private static void runTest(I2PAppContext ctx) {
|
|
||||||
SessionKey key = ctx.keyGenerator().generateSessionKey();
|
|
||||||
byte[] output = new byte[32];
|
|
||||||
SSUHMACGenerator hmac = new SSUHMACGenerator();
|
|
||||||
hmac.calculate(key, "qwerty".getBytes(), 0, 6, output, 0);
|
|
||||||
|
|
||||||
int times = 100000;
|
|
||||||
long shorttime = 0;
|
|
||||||
long medtime = 0;
|
|
||||||
long longtime = 0;
|
|
||||||
long minShort = 0;
|
|
||||||
long maxShort = 0;
|
|
||||||
long minMed = 0;
|
|
||||||
long maxMed = 0;
|
|
||||||
long minLong = 0;
|
|
||||||
long maxLong = 0;
|
|
||||||
|
|
||||||
long shorttime1 = 0;
|
|
||||||
long medtime1 = 0;
|
|
||||||
long longtime1 = 0;
|
|
||||||
long minShort1 = 0;
|
|
||||||
long maxShort1 = 0;
|
|
||||||
long minMed1 = 0;
|
|
||||||
long maxMed1 = 0;
|
|
||||||
long minLong1 = 0;
|
|
||||||
long maxLong1 = 0;
|
|
||||||
|
|
||||||
byte[] smess = "abc".getBytes();
|
|
||||||
StringBuilder buf = new StringBuilder();
|
|
||||||
for (int x = 0; x < 2*1024; x++) {
|
|
||||||
buf.append("a");
|
|
||||||
}
|
|
||||||
byte[] mmess = DataHelper.getASCII(buf.toString()); // new String("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq").getBytes();
|
|
||||||
buf = new StringBuilder();
|
|
||||||
for (int x = 0; x < 10000; x++) {
|
|
||||||
buf.append("a");
|
|
||||||
}
|
|
||||||
byte[] lmess = DataHelper.getASCII(buf.toString());
|
|
||||||
|
|
||||||
// warm up the engines
|
|
||||||
hmac.calculate(key, smess, 0, smess.length, output, 0);
|
|
||||||
hmac.calculate(key, mmess, 0, mmess.length, output, 0);
|
|
||||||
hmac.calculate(key, lmess, 0, lmess.length, output, 0);
|
|
||||||
|
|
||||||
long before = System.currentTimeMillis();
|
|
||||||
for (int x = 0; x < times; x++)
|
|
||||||
hmac.calculate(key, smess, 0, smess.length, output, 0);
|
|
||||||
long after = System.currentTimeMillis();
|
|
||||||
|
|
||||||
display(times, before, after, smess.length, "3 byte");
|
|
||||||
|
|
||||||
before = System.currentTimeMillis();
|
|
||||||
for (int x = 0; x < times; x++)
|
|
||||||
hmac.calculate(key, mmess, 0, mmess.length, output, 0);
|
|
||||||
after = System.currentTimeMillis();
|
|
||||||
|
|
||||||
display(times, before, after, mmess.length, "2KB");
|
|
||||||
|
|
||||||
before = System.currentTimeMillis();
|
|
||||||
for (int x = 0; x < times; x++)
|
|
||||||
hmac.calculate(key, lmess, 0, lmess.length, output, 0);
|
|
||||||
after = System.currentTimeMillis();
|
|
||||||
|
|
||||||
display(times, before, after, lmess.length, "10KB");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void display(int times, long before, long after, int len, String name) {
|
|
||||||
double rate = times/(((double)after-(double)before)/1000.0d);
|
|
||||||
double kbps = (len/1024.0d)*times/(((double)after-(double)before)/1000.0d);
|
|
||||||
System.out.println(name + " HMAC pulled " + kbps + "KBps or " + rate + " calcs per second");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -1,40 +0,0 @@
|
|||||||
package net.i2p.router.transport.udp;
|
|
||||||
/*
|
|
||||||
* free (adj.): unencumbered; not under the control of others
|
|
||||||
* Written by jrandom in 2003 and released into the public domain
|
|
||||||
* with no warranty of any kind, either expressed or implied.
|
|
||||||
* It probably won't make your computer catch on fire, or eat
|
|
||||||
* your children, but it might. Use at your own risk.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import net.i2p.I2PAppContext;
|
|
||||||
import net.i2p.data.SessionKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warning, misnamed, this tests the SSU HMAC,
|
|
||||||
* not net.i2p.crypto.HMAC256Generator
|
|
||||||
*/
|
|
||||||
public class HMACSHA256Test extends TestCase{
|
|
||||||
private I2PAppContext _context;
|
|
||||||
|
|
||||||
protected void setUp() {
|
|
||||||
_context = I2PAppContext.getGlobalContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testMultiple(){
|
|
||||||
SSUHMACGenerator hmac = new SSUHMACGenerator();
|
|
||||||
int size = 1;
|
|
||||||
for(int i = 0; i < 16; i++){
|
|
||||||
SessionKey key = _context.keyGenerator().generateSessionKey();
|
|
||||||
|
|
||||||
byte[] message = new byte[size];
|
|
||||||
size*=2;
|
|
||||||
_context.random().nextBytes(message);
|
|
||||||
|
|
||||||
byte[] output = new byte[32];
|
|
||||||
hmac.calculate(key, message, 0, message.length, output, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user