/** * Takes an UnencryptedMsg object and does all the work necessary to transform it into an * EncyrptedMsg object that is ready to be serialised and sent out to the Bitmessage network. The * two major parts of this process are encryption and proof of work. <br> * <br> * <b>NOTE!</b> Calling this method results in proof of work calculations being done for the * message. This can take a long time and lots of CPU power!<br> * <br> * * @param message - The original plain text Message object, provided so that its status can be * updated during the process * @param unencMsg - The UnencryptedMsg object to be encrypted * @param toPubkey - The Pubkey object containing the public encryption key of the intended * message recipient * @param doPOW - A boolean value indicating whether or not POW should be done for this message * @param timeToLive - The 'time to live' value (in seconds) to be used in creating this msg * @return A Msg object containing the encrypted message data */ private BMObject constructMsg( Message message, UnencryptedMsg unencMsg, Pubkey toPubkey, boolean doPOW, long timeToLive) { // Reconstruct the ECPublicKey object from the byte[] found the the relevant PubKey ECPublicKey publicEncryptionKey = new KeyConverter().reconstructPublicKey(toPubkey.getPublicEncryptionKey()); // Construct the payload to be encrypted byte[] msgDataForEncryption = constructMsgPayloadForEncryption(unencMsg); // Update the status of this message displayed in the UI String messageStatus = App.getContext().getString(R.string.message_status_encrypting_message); MessageStatusHandler.updateMessageStatus(message, messageStatus); // Encrypt the payload CryptProcessor cryptProc = new CryptProcessor(); byte[] encryptedPayload = cryptProc.encrypt(msgDataForEncryption, publicEncryptionKey); // Create a new Msg object and populate its fields BMObject msg = new BMObject(); msg.setBelongsToMe( true); // NOTE: This method assumes that any message I am encrypting 'belongs to me' (i.e. // The user of the application is the author of the message) msg.setExpirationTime(unencMsg.getExpirationTime()); msg.setObjectType(unencMsg.getObjectType()); msg.setObjectVersion(unencMsg.getObjectVersion()); msg.setStreamNumber(toPubkey.getStreamNumber()); msg.setPayload(encryptedPayload); if (doPOW) { MessageStatusHandler.updateMessageStatus( message, App.getContext().getString(R.string.message_status_doing_pow)); // Do proof of work for the Msg object Log.i(TAG, "About to do POW calculations for a msg that we are sending"); byte[] powPayload = constructMsgPayloadForPOW(msg); long powNonce = new POWProcessor() .doPOW( powPayload, unencMsg.getExpirationTime(), toPubkey.getNonceTrialsPerByte(), toPubkey.getExtraBytes()); msg.setPOWNonce(powNonce); } else { msg.setPOWNonce( (long) 0); // If POW is not to be done for this message, set the powNonce as zero for now. } return msg; }
/** * 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; }