/** * Takes a Msg and encodes it into a single byte[], in a way that is compatible with the way that * PyBitmessage does. This payload can then be sent to a server to be disseminated across the * network. The payload is stored as a Payload object. * * @param encMsg - A msg Object containing the message data used to create the payload. * @param powDone - A boolean value indicating whether or not POW has been done for this message * @param toPubkey - A Pubkey object containing the data for the Pubkey of the address that this * message is being sent to * @return A Payload object containing the message payload */ private Payload constructMsgPayloadForDissemination( BMObject encMsg, boolean powDone, Pubkey toPubkey) { // Create a new Payload object to hold the payload data Payload msgPayload = new Payload(); msgPayload.setBelongsToMe(true); msgPayload.setPOWDone(powDone); msgPayload.setType(Payload.OBJECT_TYPE_MSG); // Encode the POW nonce, expiration time, object type, object version, and stream number values // into byte form byte[] powNonceBytes = ByteUtils.longToBytes(encMsg.getPOWNonce()); byte[] expirationTimeBytes = ByteUtils.longToBytes(encMsg.getExpirationTime()); byte[] objectTypeBytes = ByteUtils.intToBytes(OBJECT_TYPE_MSG); byte[] objectVersionBytes = VarintEncoder.encode(OBJECT_VERSION_MSG); byte[] streamNumberBytes = VarintEncoder.encode(encMsg.getStreamNumber()); byte[] payload = null; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { if (powDone) { outputStream.write(powNonceBytes); } outputStream.write(expirationTimeBytes); outputStream.write(objectTypeBytes); outputStream.write(objectVersionBytes); outputStream.write(streamNumberBytes); outputStream.write(encMsg.getPayload()); payload = outputStream.toByteArray(); outputStream.close(); } catch (IOException e) { throw new RuntimeException( "IOException occurred in DataProcessor.constructMsgPayloadForDissemination()", e); } msgPayload.setPayload(payload); // Save the Payload object to the database PayloadProvider payProv = PayloadProvider.get(App.getContext()); long msgPayloadId = payProv.addPayload(msgPayload); // Finally, set the msg payload ID to the one generated by the SQLite database msgPayload.setId(msgPayloadId); return msgPayload; }
/** * Takes a Msg and constructs the payload needed to do POW for it. * * @param msg - The msg Object to construct the POW payload for * @return The POW payload */ private byte[] constructMsgPayloadForPOW(BMObject msg) { try { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); outputStream.write( ByteUtils.longToBytes( msg.getExpirationTime())); // This conversion results in a byte[] of length 8, which // is what we want outputStream.write(ByteUtils.intToBytes(OBJECT_TYPE_MSG)); outputStream.write(VarintEncoder.encode(OBJECT_VERSION_MSG)); outputStream.write(VarintEncoder.encode(msg.getStreamNumber())); outputStream.write(msg.getPayload()); return outputStream.toByteArray(); } catch (IOException e) { throw new RuntimeException( "IOException occurred in OutgoingMessageProcessor.constructMsgPayloadForPOW()", e); } }
/** * Takes an UnencryptedMsg object and extracts only the data needed to encrypt the message, * discarding data that is only used by Bitseal internally, such as the ID number. * * @param inputMsgData - The UnencryptedMsg object from which the data is to be extracted * @return A byte[] containing the message data needed for encryption */ private byte[] constructMsgPayloadForEncryption(UnencryptedMsg unencMsg) { byte[] msgDataForEncryption = null; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); try { outputStream.write(VarintEncoder.encode(unencMsg.getSenderAddressVersion())); outputStream.write(VarintEncoder.encode(unencMsg.getSenderStreamNumber())); outputStream.write(ByteUtils.intToBytes(unencMsg.getBehaviourBitfield())); // If the public signing and public encryption keys have their leading 0x04 byte in place then // we need to remove them byte[] publicSigningKey = unencMsg.getPublicSigningKey(); if (publicSigningKey[0] == (byte) 4 && publicSigningKey.length == 65) { publicSigningKey = ArrayCopier.copyOfRange(publicSigningKey, 1, publicSigningKey.length); } outputStream.write(publicSigningKey); byte[] publicEncryptionKey = unencMsg.getPublicEncryptionKey(); if (publicEncryptionKey[0] == (byte) 4 && publicEncryptionKey.length == 65) { publicEncryptionKey = ArrayCopier.copyOfRange(publicEncryptionKey, 1, publicEncryptionKey.length); } outputStream.write(publicEncryptionKey); if (unencMsg.getSenderAddressVersion() >= 3) // The nonceTrialsPerByte and extraBytes fields are only included when the address // version is >= 3 { outputStream.write(VarintEncoder.encode(unencMsg.getNonceTrialsPerByte())); outputStream.write(VarintEncoder.encode(unencMsg.getExtraBytes())); } outputStream.write(unencMsg.getDestinationRipe()); outputStream.write(VarintEncoder.encode(unencMsg.getEncoding())); outputStream.write(VarintEncoder.encode(unencMsg.getMessageLength())); outputStream.write(unencMsg.getMessage()); outputStream.write(VarintEncoder.encode(unencMsg.getAckLength())); outputStream.write(unencMsg.getAckMsg()); outputStream.write(VarintEncoder.encode(unencMsg.getSignatureLength())); outputStream.write(unencMsg.getSignature()); msgDataForEncryption = outputStream.toByteArray(); outputStream.close(); } catch (IOException e) { throw new RuntimeException( "IOException occurred in DataProcessor.constructMsgPayloadForEncryption()", e); } return msgDataForEncryption; }
/** * Calculates the acknowledgement Message for a given message. <br> * <br> * The process for this is as follows:<br> * <br> * 1) initialPayload = time || stream number || 32 bytes of random data<br> * <br> * 2) Do POW for the initialPayload<br> * <br> * 3) ackData = POWnonce || msgHeader || initialPayload<br> * <br> * * @param message - The original plain text Message object, provided so that its status can be * updated during the process * @param ackData - A byte[] containing the 32 bytes of random data which is the acknowledgment * data * @param toStreamNumber - An int representing the stream number of the destination address of the * message to be sent * @param doPOW - A boolean indicating whether or not POW should be done for ack msgs generated * during this process * @param timeToLive - The 'time to live' value (in seconds) to be used in processing this message * @return A byte[] containing the acknowledgement data for the message we wish to send */ private byte[] generateFullAckMessage( Message message, byte[] ackData, int toStreamNumber, boolean doPOW, long timeToLive) { // Get the fuzzed expiration time long expirationTime = TimeUtils.getFuzzedExpirationTime(timeToLive); // Encode the expiration time, object type, object version, and stream number values into byte // form byte[] expirationTimeBytes = ByteUtils.longToBytes((expirationTime)); byte[] objectTypeBytes = ByteUtils.intToBytes(OBJECT_TYPE_MSG); byte[] objectVersionBytes = VarintEncoder.encode(OBJECT_VERSION_MSG); byte[] streamNumberBytes = VarintEncoder.encode((long) toStreamNumber); // Combine the time, object type, object version, stream number, and ack data values into a // single byte[] byte[] initialPayload = ByteUtils.concatenateByteArrays( expirationTimeBytes, objectTypeBytes, objectVersionBytes, streamNumberBytes, ackData); // Create the payload for the ack msg byte[] payload = new byte[0]; if (doPOW) { // Update the status of this message displayed in the UI String messageStatus = App.getContext().getString(R.string.message_status_doing_ack_pow); MessageStatusHandler.updateMessageStatus(message, messageStatus); // Do proof of work for the acknowledgement payload Log.i( TAG, "About to do POW calculations for the acknowledgment payload of a msg that we are sending"); long powNonce = new POWProcessor() .doPOW( initialPayload, expirationTime, POWProcessor.NETWORK_NONCE_TRIALS_PER_BYTE, POWProcessor.NETWORK_EXTRA_BYTES); byte[] powNonceBytes = ByteUtils.longToBytes(powNonce); payload = ByteUtils.concatenateByteArrays(powNonceBytes, initialPayload); } else { payload = initialPayload; } byte[] headerData = new MessageProcessor().generateObjectHeader(payload); byte[] fullAckMsg = ByteUtils.concatenateByteArrays(headerData, payload); return fullAckMsg; }