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; } }