private RevealSignatureMessage getRevealSignatureMessage() throws OtrException { try { SignatureM m = new SignatureM( (DHPublicKey) getLocalDHKeyPair().getPublic(), getRemoteDHPublicKey(), getLocalLongTermKeyPair().getPublic(), getLocalDHKeyPairID()); OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl(); byte[] mhash = otrCryptoEngine.sha256Hmac(SerializationUtils.toByteArray(m), getM1()); byte[] signature = otrCryptoEngine.sign(mhash, getLocalLongTermKeyPair().getPrivate()); SignatureX mysteriousX = new SignatureX(getLocalLongTermKeyPair().getPublic(), getLocalDHKeyPairID(), signature); byte[] xEncrypted = otrCryptoEngine.aesEncrypt(getC(), null, SerializationUtils.toByteArray(mysteriousX)); byte[] tmp = SerializationUtils.writeData(xEncrypted); byte[] xEncryptedHash = otrCryptoEngine.sha256Hmac160(tmp, getM2()); return new RevealSignatureMessage(getProtocolVersion(), xEncrypted, xEncryptedHash, getR()); } catch (IOException e) { throw new OtrException(e); } }
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; } }
private String handleDataMessage(DataMessage data, List<TLV> tlvs) throws OtrException { logger.finest( getSessionID().getAccountID() + " received a data message from " + getSessionID().getUserID() + "."); switch (this.getSessionStatus()) { case ENCRYPTED: logger.finest("Message state is ENCRYPTED. Trying to decrypt message."); // Find matching session keys. int senderKeyID = data.senderKeyID; int receipientKeyID = data.recipientKeyID; SessionKeys matchingKeys = this.getSessionKeysByID(receipientKeyID, senderKeyID); if (matchingKeys == null) { throw new OtrException("no matching keys found"); } // Verify received MAC with a locally calculated MAC. logger.finest("Transforming T to byte[] to calculate it's HmacSHA1."); byte[] serializedT; try { serializedT = SerializationUtils.toByteArray(data.getT()); } catch (IOException e) { throw new OtrException(e); } OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl(); byte[] computedMAC = otrCryptoEngine.sha1Hmac( serializedT, matchingKeys.getReceivingMACKey(), SerializationConstants.TYPE_LEN_MAC); if (!Arrays.equals(computedMAC, data.mac)) { throw new OtrException("MAC verification failed, ignoring message"); } logger.finest("Computed HmacSHA1 value matches sent one."); // Mark this MAC key as old to be revealed. matchingKeys.setIsUsedReceivingMACKey(true); matchingKeys.setReceivingCtr(data.ctr); byte[] dmc = otrCryptoEngine.aesDecrypt( matchingKeys.getReceivingAESKey(), matchingKeys.getReceivingCtr(), data.encryptedMessage); String decryptedMsgContent; try { // Expect bytes to be text encoded in UTF-8. decryptedMsgContent = new String(dmc, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new OtrException(e); } logger.finest("Decrypted message: \"" + decryptedMsgContent + "\""); // FIXME extraKey = authContext.getExtraSymmetricKey(); // Rotate keys if necessary. SessionKeys mostRecent = this.getMostRecentSessionKeys(); if (mostRecent.getLocalKeyID() == receipientKeyID) this.rotateLocalSessionKeys(); if (mostRecent.getRemoteKeyID() == senderKeyID) this.rotateRemoteSessionKeys(data.nextDH); // Handle TLVs if (tlvs == null) { tlvs = new ArrayList<TLV>(); } int tlvIndex = decryptedMsgContent.indexOf((char) 0x0); if (tlvIndex > -1) { decryptedMsgContent = decryptedMsgContent.substring(0, tlvIndex); tlvIndex++; byte[] tlvsb = new byte[dmc.length - tlvIndex]; System.arraycopy(dmc, tlvIndex, tlvsb, 0, tlvsb.length); ByteArrayInputStream tin = new ByteArrayInputStream(tlvsb); while (tin.available() > 0) { int type; byte[] tdata; OtrInputStream eois = new OtrInputStream(tin); try { type = eois.readShort(); tdata = eois.readTlvData(); eois.close(); } catch (IOException e) { throw new OtrException(e); } tlvs.add(new TLV(type, tdata)); } } if (tlvs.size() > 0) { for (TLV tlv : tlvs) { switch (tlv.getType()) { case TLV.DISCONNECTED: this.setSessionStatus(SessionStatus.FINISHED); return null; default: for (OtrTlvHandler handler : tlvHandlers) { handler.processTlv(tlv); } } } } return decryptedMsgContent; case FINISHED: case PLAINTEXT: showError("Unreadable encrypted message was received."); injectMessage( new ErrorMessage( AbstractMessage.MESSAGE_ERROR, "You sent me an unreadable encrypted message")); throw new OtrException("Unreadable encrypted message received"); } return null; }
private void handleRevealSignatureMessage(RevealSignatureMessage m) throws OtrException { Session session = getSession(); SessionID sessionID = session.getSessionID(); logger.finest( sessionID.getLocalUserId() + " received a reveal signature message from " + sessionID.getRemoteUserId() + " throught " + sessionID.getProtocolName() + "."); if (!session.getSessionPolicy().getAllowV2()) { logger.finest("Policy does not allow OTRv2, ignoring message."); return; } switch (this.getAuthenticationState()) { case AWAITING_REVEALSIG: // Use the received value of r to decrypt the value of gx // received // in the D-H Commit Message, and verify the hash therein. // Decrypt // the encrypted signature, and verify the signature and the // MACs. // If everything checks out: // * Reply with a Signature Message. // * Transition authstate to AUTHSTATE_NONE. // * Transition msgstate to MSGSTATE_ENCRYPTED. // * TODO If there is a recent stored message, encrypt it and // send // it as a Data Message. OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl(); // Uses r to decrypt the value of gx sent earlier byte[] remoteDHPublicKeyDecrypted = otrCryptoEngine.aesDecrypt(m.revealedKey, null, this.getRemoteDHPublicKeyEncrypted()); // Verifies that HASH(gx) matches the value sent earlier byte[] remoteDHPublicKeyHash = otrCryptoEngine.sha256Hash(remoteDHPublicKeyDecrypted); if (!Arrays.equals(remoteDHPublicKeyHash, this.getRemoteDHPublicKeyHash())) { logger.finest("Hashes don't match, ignoring message."); return; } // Verifies that Bob's gx is a legal value (2 <= gx <= // modulus-2) BigInteger remoteDHPublicKeyMpi; try { remoteDHPublicKeyMpi = SerializationUtils.readMpi(remoteDHPublicKeyDecrypted); } catch (IOException e) { throw new OtrException(e); } this.setRemoteDHPublicKey(otrCryptoEngine.getDHPublicKey(remoteDHPublicKeyMpi)); // Verify received Data. if (!m.verify(this.getM2())) { logger.finest("Signature MACs are not equal, ignoring message."); return; } // Decrypt X. byte[] remoteXDecrypted = m.decrypt(this.getC()); SignatureX remoteX; try { remoteX = SerializationUtils.toMysteriousX(remoteXDecrypted); } catch (IOException e) { throw new OtrException(e); } // Compute signature. PublicKey remoteLongTermPublicKey = remoteX.longTermPublicKey; SignatureM remoteM = new SignatureM( this.getRemoteDHPublicKey(), (DHPublicKey) this.getLocalDHKeyPair().getPublic(), remoteLongTermPublicKey, remoteX.dhKeyID); // Verify signature. byte[] signature; try { signature = otrCryptoEngine.sha256Hmac(SerializationUtils.toByteArray(remoteM), this.getM1()); } catch (IOException e) { throw new OtrException(e); } if (!otrCryptoEngine.verify(signature, remoteLongTermPublicKey, remoteX.signature)) { session.showWarning("Bad revealed signature"); logger.finest("Signature verification failed."); return; } logger.finest("Signature verification succeeded."); this.setAuthenticationState(AuthContext.NONE); this.setIsSecure(true); this.setRemoteLongTermPublicKey(remoteLongTermPublicKey); getSession().injectMessage(messageFactory.getSignatureMessage()); break; default: logger.finest("Ignoring message."); break; } }
private void handleSignatureMessage(SignatureMessage m) throws OtrException { Session session = getSession(); SessionID sessionID = session.getSessionID(); logger.finest( sessionID.getLocalUserId() + " received a signature message from " + sessionID.getRemoteUserId() + " throught " + sessionID.getProtocolName() + "."); if (!session.getSessionPolicy().getAllowV2()) { logger.finest("Policy does not allow OTRv2, ignoring message."); return; } switch (this.getAuthenticationState()) { case AWAITING_SIG: // Verify MAC. if (!m.verify(this.getM2p())) { logger.finest("Signature MACs are not equal, ignoring message."); return; } // Decrypt X. byte[] remoteXDecrypted = m.decrypt(this.getCp()); SignatureX remoteX; try { remoteX = SerializationUtils.toMysteriousX(remoteXDecrypted); } catch (IOException e) { throw new OtrException(e); } // Compute signature. PublicKey remoteLongTermPublicKey = remoteX.longTermPublicKey; SignatureM remoteM = new SignatureM( this.getRemoteDHPublicKey(), (DHPublicKey) this.getLocalDHKeyPair().getPublic(), remoteLongTermPublicKey, remoteX.dhKeyID); OtrCryptoEngine otrCryptoEngine = new OtrCryptoEngineImpl(); // Verify signature. byte[] signature; try { signature = otrCryptoEngine.sha256Hmac(SerializationUtils.toByteArray(remoteM), this.getM1p()); } catch (IOException e) { throw new OtrException(e); } if (!otrCryptoEngine.verify(signature, remoteLongTermPublicKey, remoteX.signature)) { session.showWarning("Bad signature"); logger.finest("Signature verification failed."); return; } this.setIsSecure(true); this.setRemoteLongTermPublicKey(remoteLongTermPublicKey); break; default: logger.finest("We were not expecting a signature, ignoring message."); return; } }