public String transformSending(String msgText, List<TLV> tlvs) throws OtrException { switch (this.getSessionStatus()) { case PLAINTEXT: if (getSessionPolicy().getRequireEncryption()) { lastSentMessage = msgText; doTransmitLastMessage = true; this.startSession(); return null; } else // TODO this does not precisly behave according to // specification. return msgText; case ENCRYPTED: this.lastSentMessage = msgText; logger.finest( getSessionID().getAccountID() + " sends an encrypted message to " + getSessionID().getUserID() + " through " + getSessionID().getProtocolName() + "."); // Get encryption keys. SessionKeys encryptionKeys = this.getEncryptionSessionKeys(); int senderKeyID = encryptionKeys.getLocalKeyID(); int receipientKeyID = encryptionKeys.getRemoteKeyID(); // Increment CTR. encryptionKeys.incrementSendingCtr(); byte[] ctr = encryptionKeys.getSendingCtr(); ByteArrayOutputStream out = new ByteArrayOutputStream(); if (msgText != null && msgText.length() > 0) try { out.write(msgText.getBytes("UTF8")); } catch (IOException e) { throw new OtrException(e); } // Append tlvs if (tlvs != null && tlvs.size() > 0) { out.write((byte) 0x00); OtrOutputStream eoos = new OtrOutputStream(out); for (TLV tlv : tlvs) { try { eoos.writeShort(tlv.type); eoos.writeTlvData(tlv.value); } catch (IOException e) { throw new OtrException(e); } } } OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl(); byte[] data = out.toByteArray(); // Encrypt message. logger.finest( "Encrypting message with keyids (localKeyID, remoteKeyID) = (" + senderKeyID + ", " + receipientKeyID + ")"); byte[] encryptedMsg = otrCryptoEngine.aesEncrypt(encryptionKeys.getSendingAESKey(), ctr, data); // Get most recent keys to get the next D-H public key. SessionKeys mostRecentKeys = this.getMostRecentSessionKeys(); DHPublicKey nextDH = (DHPublicKey) mostRecentKeys.getLocalPair().getPublic(); // Calculate T. MysteriousT t = new MysteriousT(2, 0, senderKeyID, receipientKeyID, nextDH, ctr, encryptedMsg); // Calculate T hash. byte[] sendingMACKey = encryptionKeys.getSendingMACKey(); logger.finest("Transforming T to byte[] to calculate it's HmacSHA1."); byte[] serializedT; try { serializedT = SerializationUtils.toByteArray(t); } catch (IOException e) { throw new OtrException(e); } byte[] mac = otrCryptoEngine.sha1Hmac( serializedT, sendingMACKey, SerializationConstants.TYPE_LEN_MAC); // Get old MAC keys to be revealed. byte[] oldKeys = this.collectOldMacKeys(); DataMessage m = new DataMessage(t, mac, oldKeys); try { return SerializationUtils.toString(m); } catch (IOException e) { throw new OtrException(e); } case FINISHED: this.lastSentMessage = msgText; showError( "Your message to " + sessionID.getUserID() + " was not sent. Either end your private conversation, or restart it."); return null; default: logger.finest("Uknown message state, not processing."); return msgText; } }