use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class PeerTestManager method receiveFromBobAsCharlie.
// Below here are methods for when we are Bob or Charlie
/**
* The packet's IP/port does not match the IP/port included in the message,
* so we must be Charlie receiving a PeerTest from Bob.
*
* @param state null if new
*/
private void receiveFromBobAsCharlie(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce, PeerTestState state) {
long now = _context.clock().now();
int sz = testInfo.readIPSize();
boolean isNew = false;
if (state == null) {
isNew = true;
state = new PeerTestState(CHARLIE, sz == 16, nonce, now);
} else {
if (state.getReceiveBobTime() > now - (RESEND_TIMEOUT / 2)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Too soon, not retransmitting: " + state);
return;
}
}
// TODO should only do most of this if isNew
byte[] aliceIPData = new byte[sz];
try {
testInfo.readIP(aliceIPData, 0);
boolean expectV6 = state.isIPv6();
if ((!expectV6 && sz != 4) || (expectV6 && sz != 16))
throw new UnknownHostException("bad sz - expect v6? " + expectV6 + " act sz: " + sz);
int alicePort = testInfo.readPort();
if (alicePort == 0)
throw new UnknownHostException("port 0");
InetAddress aliceIP = InetAddress.getByAddress(aliceIPData);
InetAddress bobIP = InetAddress.getByAddress(from.getIP());
SessionKey aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
testInfo.readIntroKey(aliceIntroKey.getData(), 0);
state.setAliceIP(aliceIP);
state.setAlicePort(alicePort);
state.setAliceIntroKey(aliceIntroKey);
state.setBobIP(bobIP);
state.setBobPort(from.getPort());
state.setLastSendTime(now);
state.setReceiveBobTime(now);
PeerState bob = _transport.getPeerState(from);
if (bob == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Received from bob (" + from + ") who hasn't established a session with us, refusing to help him test " + aliceIP + ":" + alicePort);
return;
} else {
state.setBobCipherKey(bob.getCurrentCipherKey());
state.setBobMACKey(bob.getCurrentMACKey());
}
// we send two packets below, but increment just once
if (state.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_CHARLIE) {
if (_log.shouldLog(Log.WARN))
_log.warn("Too many, not retransmitting: " + state);
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive from Bob: " + state);
if (isNew) {
_activeTests.put(Long.valueOf(nonce), state);
_context.simpleTimer2().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME);
}
UDPPacket packet = _packetBuilder.buildPeerTestToBob(bobIP, from.getPort(), aliceIP, alicePort, aliceIntroKey, nonce, state.getBobCipherKey(), state.getBobMACKey());
_transport.send(packet);
packet = _packetBuilder.buildPeerTestToAlice(aliceIP, alicePort, aliceIntroKey, _transport.getIntroKey(), nonce);
_transport.send(packet);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to build the aliceIP from " + from + ", ip size: " + sz + " ip val: " + Base64.encode(aliceIPData), uhe);
_context.statManager().addRateData("udp.testBadIP", 1);
}
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class PeerTestManager method receiveFromAliceAsBob.
/**
* The PeerTest message came from the peer referenced in the message (or there wasn't
* any info in the message), plus we are not acting as Charlie (so we've got to be Bob).
*
* testInfo IP/port ignored
* @param state null if new
*/
private void receiveFromAliceAsBob(RemoteHostId from, UDPPacketReader.PeerTestReader testInfo, long nonce, PeerTestState state) {
// we are Bob, so pick a (potentially) Charlie and send Charlie Alice's info
PeerState charlie;
RouterInfo charlieInfo = null;
int sz = from.getIP().length;
boolean isIPv6 = sz == 16;
if (state == null) {
// pick a new charlie
// if (from.getIP().length != 4) {
// if (_log.shouldLog(Log.WARN))
// _log.warn("PeerTest over IPv6 from Alice as Bob? " + from);
// return;
// }
charlie = _transport.pickTestPeer(CHARLIE, isIPv6, from);
} else {
charlie = _transport.getPeerState(new RemoteHostId(state.getCharlieIP().getAddress(), state.getCharliePort()));
}
if (charlie == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to pick a charlie (no peer), IPv6? " + isIPv6);
return;
}
charlieInfo = _context.netDb().lookupRouterInfoLocally(charlie.getRemotePeer());
if (charlieInfo == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to pick a charlie (no RI), IPv6? " + isIPv6);
return;
}
// TODO should only do most of this if isNew
InetAddress aliceIP = null;
SessionKey aliceIntroKey = null;
try {
aliceIP = InetAddress.getByAddress(from.getIP());
aliceIntroKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]);
testInfo.readIntroKey(aliceIntroKey.getData(), 0);
RouterAddress raddr = _transport.getTargetAddress(charlieInfo);
if (raddr == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to pick a charlie (no addr), IPv6? " + isIPv6);
return;
}
UDPAddress addr = new UDPAddress(raddr);
byte[] ikey = addr.getIntroKey();
if (ikey == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to pick a charlie (no ikey), IPv6? " + isIPv6);
return;
}
SessionKey charlieIntroKey = new SessionKey(ikey);
// UDPPacket packet = _packetBuilder.buildPeerTestToAlice(aliceIP, from.getPort(), aliceIntroKey, charlieIntroKey, nonce);
// _transport.send(packet);
long now = _context.clock().now();
boolean isNew = false;
if (state == null) {
isNew = true;
state = new PeerTestState(BOB, isIPv6, nonce, now);
} else {
if (state.getReceiveAliceTime() > now - (RESEND_TIMEOUT / 2)) {
if (_log.shouldLog(Log.WARN))
_log.warn("Too soon, not retransmitting: " + state);
return;
}
}
state.setAliceIP(aliceIP);
state.setAlicePort(from.getPort());
state.setAliceIntroKey(aliceIntroKey);
state.setCharlieIP(charlie.getRemoteIPAddress());
state.setCharliePort(charlie.getRemotePort());
state.setCharlieIntroKey(charlieIntroKey);
state.setLastSendTime(now);
state.setReceiveAliceTime(now);
if (state.incrementPacketsRelayed() > MAX_RELAYED_PER_TEST_BOB) {
if (_log.shouldLog(Log.WARN))
_log.warn("Too many, not retransmitting: " + state);
return;
}
if (isNew) {
_activeTests.put(Long.valueOf(nonce), state);
_context.simpleTimer2().addEvent(new RemoveTest(nonce), MAX_CHARLIE_LIFETIME);
}
UDPPacket packet = _packetBuilder.buildPeerTestToCharlie(aliceIP, from.getPort(), aliceIntroKey, nonce, charlie.getRemoteIPAddress(), charlie.getRemotePort(), charlie.getCurrentCipherKey(), charlie.getCurrentMACKey());
if (_log.shouldLog(Log.DEBUG))
_log.debug("Receive from Alice: " + state);
_transport.send(packet);
} catch (UnknownHostException uhe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Unable to build the aliceIP from " + from, uhe);
_context.statManager().addRateData("udp.testBadIP", 1);
}
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class BuildMessageGenerator method layeredEncrypt.
/**
* Encrypt the records so their hop ident is visible at the appropriate times.
*
* Note that this layer-encrypts the build records for the message in-place.
* Only call this once for a given message.
*
* @param order list of hop #s as Integers. For instance, if (order.get(1) is 4), it is peer cfg.getPeer(4)
*/
public static void layeredEncrypt(I2PAppContext ctx, TunnelBuildMessage msg, TunnelCreatorConfig cfg, List<Integer> order) {
// encrypt the records so that the right elements will be visible at the right time
for (int i = 0; i < msg.getRecordCount(); i++) {
EncryptedBuildRecord rec = msg.getRecord(i);
Integer hopNum = order.get(i);
int hop = hopNum.intValue();
if ((isBlank(cfg, hop)) || (!cfg.isInbound() && hop == 1)) {
// log.debug(msg.getUniqueId() + ": not pre-decrypting record " + i + "/" + hop + " for " + cfg);
continue;
}
// if (log.shouldLog(Log.DEBUG))
// log.debug(msg.getUniqueId() + ": pre-decrypting record " + i + "/" + hop + " for " + cfg);
// ok, now decrypt the record with all of the reply keys from cfg.getConfig(0) through hop-1
int stop = (cfg.isInbound() ? 0 : 1);
for (int j = hop - 1; j >= stop; j--) {
HopConfig hopConfig = cfg.getConfig(j);
SessionKey key = hopConfig.getReplyKey();
byte[] iv = hopConfig.getReplyIV();
// if (log.shouldLog(Log.DEBUG))
// log.debug(msg.getUniqueId() + ": pre-decrypting record " + i + "/" + hop + " for " + cfg
// + " with " + key.toBase64() + "/" + Base64.encode(iv));
// corrupts the SDS
ctx.aes().decrypt(rec.getData(), 0, rec.getData(), 0, key, iv, TunnelBuildMessage.RECORD_SIZE);
}
}
// if (log.shouldLog(Log.DEBUG))
// log.debug(msg.getUniqueId() + ": done pre-decrypting all records for " + cfg);
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class BuildMessageProcessor method decrypt.
/**
* Decrypt the record targetting us, encrypting all of the other records with the included
* reply key and IV. The original, encrypted record targetting us is removed from the request
* message (so that the reply can be placed in that position after going through the decrypted
* request record).
*
* Note that this layer-decrypts the build records in-place.
* Do not call this more than once for a given message.
*
* @return the current hop's decrypted record or null on failure
*/
public BuildRequestRecord decrypt(TunnelBuildMessage msg, Hash ourHash, PrivateKey privKey) {
BuildRequestRecord rv = null;
int ourHop = -1;
long beforeActualDecrypt = 0;
long afterActualDecrypt = 0;
byte[] ourHashData = ourHash.getData();
long beforeLoop = System.currentTimeMillis();
for (int i = 0; i < msg.getRecordCount(); i++) {
EncryptedBuildRecord rec = msg.getRecord(i);
int len = BuildRequestRecord.PEER_SIZE;
boolean eq = DataHelper.eq(ourHashData, 0, rec.getData(), 0, len);
if (eq) {
beforeActualDecrypt = System.currentTimeMillis();
try {
rv = new BuildRequestRecord(ctx, privKey, rec);
afterActualDecrypt = System.currentTimeMillis();
// i2pd bug
boolean isBad = SessionKey.INVALID_KEY.equals(rv.readReplyKey());
if (isBad) {
if (log.shouldLog(Log.WARN))
log.warn(msg.getUniqueId() + ": Bad reply key: " + rv);
ctx.statManager().addRateData("tunnel.buildRequestBadReplyKey", 1);
return null;
}
// The spec says to feed the 32-byte AES-256 reply key into the Bloom filter.
// But we were using the first 32 bytes of the encrypted reply.
// Fixed in 0.9.24
boolean isDup = _filter.add(rv.getData(), BuildRequestRecord.OFF_REPLY_KEY, 32);
if (isDup) {
if (log.shouldLog(Log.WARN))
log.warn(msg.getUniqueId() + ": Dup record: " + rv);
ctx.statManager().addRateData("tunnel.buildRequestDup", 1);
return null;
}
if (log.shouldLog(Log.DEBUG))
log.debug(msg.getUniqueId() + ": Matching record: " + rv);
ourHop = i;
// TODO should we keep looking for a second match and fail if found?
break;
} catch (DataFormatException dfe) {
if (log.shouldLog(Log.WARN))
log.warn(msg.getUniqueId() + ": Matching record decrypt failure", dfe);
// out there with the same first 16 bytes, go around again
continue;
}
}
}
if (rv == null) {
// none of the records matched, b0rk
if (log.shouldLog(Log.WARN))
log.warn(msg.getUniqueId() + ": No matching record");
return null;
}
long beforeEncrypt = System.currentTimeMillis();
SessionKey replyKey = rv.readReplyKey();
byte[] iv = rv.readReplyIV();
for (int i = 0; i < msg.getRecordCount(); i++) {
if (i != ourHop) {
EncryptedBuildRecord data = msg.getRecord(i);
// if (log.shouldLog(Log.DEBUG))
// log.debug("Encrypting record " + i + "/? with replyKey " + replyKey.toBase64() + "/" + Base64.encode(iv));
// encrypt in-place, corrupts SDS
byte[] bytes = data.getData();
ctx.aes().encrypt(bytes, 0, bytes, 0, replyKey, iv, 0, EncryptedBuildRecord.LENGTH);
}
}
long afterEncrypt = System.currentTimeMillis();
msg.setRecord(ourHop, null);
if (afterEncrypt - beforeLoop > 1000) {
if (log.shouldLog(Log.WARN))
log.warn("Slow decryption, total=" + (afterEncrypt - beforeLoop) + " looping=" + (beforeEncrypt - beforeLoop) + " decrypt=" + (afterActualDecrypt - beforeActualDecrypt) + " encrypt=" + (afterEncrypt - beforeEncrypt));
}
return rv;
}
use of net.i2p.data.SessionKey in project i2p.i2p by i2p.
the class SessionEncryptionTest method testNoSessions1.
public void testNoSessions1() throws Exception {
Object[] keys = KeyGenerator.getInstance().generatePKIKeypair();
PublicKey pubKey = (PublicKey) keys[0];
PrivateKey privKey = (PrivateKey) keys[1];
SessionKeyManager skm = new TransientSessionKeyManager(_context);
SessionKey curKey = skm.createSession(pubKey);
byte[] msg = DataHelper.getASCII("msg 1");
byte[] emsg = _context.elGamalAESEngine().encrypt(msg, pubKey, curKey, null, null, 64);
byte[] dmsg = _context.elGamalAESEngine().decrypt(emsg, privKey, skm);
assertTrue(DataHelper.eq(dmsg, msg));
}
Aggregations