if (item.packet == null) { byte[] dataToSend = preparePayloadForSession(sessionKey, item.sourcePayload); item.packet = new Packet(item.packetId, myNodeInfo.getNumber(), item.receiverNodeId, item.type, dataToSend);
try { byte[] data = Arrays.copyOfRange(receivedDatagram.getData(), 0, receivedDatagram.getLength()); Packet packet = new Packet(); packet.parseFromByteArray(data);
/** * Each adapter will try to send blocks until have got special {@link Packet} with type {@link PacketTypes#ACK}, * that means receiver have got block. So when we got block, but something went wrong - call this method. Note that * for success blocks needs to call {@link UDPAdapter#sendAck(SessionReader, Integer)} * @param nodeNumber node id in which sending is * @param packetId is id of block we have got. */ private void sendNack(Integer nodeNumber, Integer packetId) { try { NodeInfo destination = netConfig.getInfo(nodeNumber); if (destination != null) { report(logLabel, ()->"send nack to "+nodeNumber, VerboseLevel.DETAILED); byte[] randomSeed = Do.randomBytes(64); byte[] data = Boss.dumpToArray(Arrays.asList(packetId, randomSeed)); byte[] sign = new PrivateKey(ownPrivateKey.pack()).sign(data, HashType.SHA512); byte[] payload = Boss.dumpToArray(Arrays.asList(data, sign)); Packet packet = new Packet(0, myNodeInfo.getNumber(), nodeNumber, PacketTypes.NACK, payload); sendPacket(destination, packet); } } catch (EncryptionError e) { callErrorCallbacks("(sendNack) can't send NACK, EncryptionError: " + e); } }
/** * Someone who sent {@link PacketTypes#HELLO} typed {@link Packet}, * send us new KEY_REQ typed {@link Packet} - if all is ok we send session keys to. * SESSION's payload is more than 512 bytes, so used two parts here. * From now we ready to data exchange. * @param sessionReader is {@link SessionReader} in which sending is. */ private void sendSessionKey(SessionReader sessionReader) throws EncryptionError { report(logLabel, ()->"send session_key to "+sessionReader.remoteNodeInfo.getNumber(), VerboseLevel.BASE); List data = Arrays.asList(sessionReader.sessionKey.getKey(), sessionReader.remoteNonce); byte[] packed = Boss.pack(data); byte[] encrypted = new PublicKey(sessionReader.remoteNodeInfo.getPublicKey().pack()).encrypt(packed); byte[] sign = new PrivateKey(ownPrivateKey.pack()).sign(encrypted, HashType.SHA512); Packet packet1 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.SESSION_PART1, encrypted); Packet packet2 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.SESSION_PART2, sign); sendPacket(sessionReader.remoteNodeInfo, packet1); sendPacket(sessionReader.remoteNodeInfo, packet2); sessionReader.addPacketToRetransmitMap(packet1.packetId, packet1, encrypted); sessionReader.addPacketToRetransmitMap(packet2.packetId, packet2, sign); }
/** * When someone send us {@link PacketTypes#HELLO} typed {@link UDPAdapter.Packet}, * we should respond with {@link PacketTypes#WELCOME}. * @param sessionReader is {@link UDPAdapter.SessionReader} in which sending is. */ private void sendWelcome(SessionReader sessionReader) { try { report(logLabel, () -> "send welcome to " + sessionReader.remoteNodeInfo.getNumber(), VerboseLevel.BASE); byte[] data = sessionReader.localNonce; byte[] sign = new PrivateKey(ownPrivateKey.pack()).sign(data, HashType.SHA512); byte[] payload = Boss.dumpToArray(Arrays.asList(data, sign)); Packet packet = new Packet(getNextPacketId(), myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.WELCOME, payload); sendPacket(sessionReader.remoteNodeInfo, packet); sessionReader.removeHandshakePacketsFromRetransmitMap(); sessionReader.addPacketToRetransmitMap(packet.packetId, packet, sessionReader.localNonce); } catch (EncryptionError e) { callErrorCallbacks("(sendWelcome) EncryptionError: " + e); } }
/** * We have sent {@link PacketTypes#HELLO} typed {@link Packet}, * and have got {@link PacketTypes#WELCOME} typed {@link Packet} - it means we can continue handshake and send * request for session's keys. KEY_REQ's payload is more than 512 bytes, so used two parts here. * @param session is {@link Session} in which sending is. * @param payloadPart1 is prepared in {@link SocketListenThread#onReceiveWelcome(Packet)} * @param payloadPart2 is prepared in {@link SocketListenThread#onReceiveWelcome(Packet)} */ private void sendKeyReq(Session session, byte[] payloadPart1, byte[] payloadPart2) throws EncryptionError { report(logLabel, ()->"send key_req to "+session.remoteNodeInfo.getNumber(), VerboseLevel.BASE); Packet packet1 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.KEY_REQ_PART1, payloadPart1); Packet packet2 = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.KEY_REQ_PART2, payloadPart2); sendPacket(session.remoteNodeInfo, packet1); sendPacket(session.remoteNodeInfo, packet2); session.addPacketToRetransmitMap(packet1.packetId, packet1, payloadPart1); session.addPacketToRetransmitMap(packet2.packetId, packet2, payloadPart2); }
/** * This is first step of creation and installation of the session. * @param session is {@link Session} in which sending is. */ private void sendHello(Session session) { try { report(logLabel, () -> "send hello to " + session.remoteNodeInfo.getNumber(), VerboseLevel.BASE); byte[] helloNonce = Do.randomBytes(64); Packet packet = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.HELLO, new PublicKey(session.remoteNodeInfo.getPublicKey().pack()).encrypt(helloNonce)); sendPacket(session.remoteNodeInfo, packet); session.addPacketToRetransmitMap(packet.packetId, packet, helloNonce); } catch (EncryptionError e) { callErrorCallbacks("(sendHello) EncryptionError: " + e); } }
/** * Creates {@link Packet} of type {@link PacketTypes#DATA} and sends it to network, initiates retransmission. * It is normal data sending procedure when {@link Session} with remote node is already established. * @param session {@link Session} with remote node * @param payload data to send */ private void sendPayload(Session session, byte[] payload) { byte[] dataToSend = preparePayloadForSession(session.sessionKey, payload); Packet packet = new Packet(getNextPacketId(), myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.DATA, dataToSend); sendPacket(session.remoteNodeInfo, packet); session.addPacketToRetransmitMap(packet.packetId, packet, payload); }
/** * ACK packets are used only for respond to DATA packets. Retransmission of handshake's packet types stops on each * next handshake step. But last step need to be ACK-ed. For this used {@link PacketTypes#SESSION_ACK} packet. * @param session is {@link Session} in which sending is. * @throws EncryptionError */ private void sendSessionAck(Session session) throws EncryptionError { report(logLabel, ()->"send session_ack to "+session.remoteNodeInfo.getNumber(), VerboseLevel.BASE); Packet packet = new Packet(0, myNodeInfo.getNumber(), session.remoteNodeInfo.getNumber(), PacketTypes.SESSION_ACK, new SymmetricKey(session.sessionKey.getKey()).etaEncrypt(Do.randomBytes(32))); sendPacket(session.remoteNodeInfo, packet); }
/** * for debug and testing */ public Packet createTestPacket(int packetId, int senderNodeId, int receiverNodeId, int type, byte[] payload) { return new Packet(packetId, senderNodeId, receiverNodeId, type, payload); }
/** * Each adapter will try to send blocks until have got special {@link Packet} with type {@link PacketTypes#ACK}, * that means receiver have got block. So when we got packet and all is ok - call this method. * @param sessionReader is {@link SessionReader} in which sending is. * @param packetId is id of packet we have got. */ private void sendAck(SessionReader sessionReader, Integer packetId) throws EncryptionError { report(logLabel, ()->"send ack to "+sessionReader.remoteNodeInfo.getNumber(), VerboseLevel.DETAILED); Packet packet = new Packet(0, myNodeInfo.getNumber(), sessionReader.remoteNodeInfo.getNumber(), PacketTypes.ACK, new SymmetricKey(sessionReader.sessionKey.getKey()).etaEncrypt(Boss.pack(packetId))); sendPacket(sessionReader.remoteNodeInfo, packet); }