private void handleDHKeyMessage(DHKeyMessage m) throws OtrException {
    Session session = getSession();
    SessionID sessionID = session.getSessionID();
    logger.finest(
        sessionID.getLocalUserId()
            + " received a D-H key message from "
            + sessionID.getRemoteUserId()
            + " throught "
            + sessionID.getProtocolName()
            + ".");

    if (!session.getSessionPolicy().getAllowV2()) {
      logger.finest("If ALLOW_V2 is not set, ignore this message.");
      return;
    }

    switch (this.getAuthenticationState()) {
      case AWAITING_DHKEY:
        // Reply with a Reveal Signature Message and transition
        // authstate to
        // AUTHSTATE_AWAITING_SIG
        this.setRemoteDHPublicKey(m.dhPublicKey);
        this.setAuthenticationState(AuthContext.AWAITING_SIG);
        getSession().injectMessage(messageFactory.getRevealSignatureMessage());
        logger.finest("Sent Reveal Signature.");
        break;
      case AWAITING_SIG:
        if (m.dhPublicKey.getY().equals(this.getRemoteDHPublicKey().getY())) {
          // If this D-H Key message is the same the one you received
          // earlier (when you entered AUTHSTATE_AWAITING_SIG):
          // Retransmit
          // your Reveal Signature Message.
          getSession().injectMessage(messageFactory.getRevealSignatureMessage());
          logger.finest("Resent Reveal Signature.");
        } else {
          // Otherwise: Ignore the message.
          logger.finest("Ignoring message.");
        }
        break;
      default:
        // Ignore the message
        break;
    }
  }
Пример #2
0
  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 void handleDHCommitMessage(DHCommitMessage m) throws OtrException {
    Session session = getSession();
    SessionID sessionID = session.getSessionID();
    logger.finest(
        sessionID.getLocalUserId()
            + " received a D-H commit message from "
            + sessionID.getRemoteUserId()
            + " throught "
            + sessionID.getProtocolName()
            + ".");

    if (!session.getSessionPolicy().getAllowV2()) {
      logger.finest("ALLOW_V2 is not set, ignore this message.");
      return;
    }

    switch (this.getAuthenticationState()) {
      case NONE:
        // Reply with a D-H Key Message, and transition authstate to
        // AUTHSTATE_AWAITING_REVEALSIG.
        this.reset();
        this.setProtocolVersion(2);
        this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
        this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
        this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
        getSession().injectMessage(messageFactory.getDHKeyMessage());
        logger.finest("Sent D-H key.");
        break;

      case AWAITING_DHKEY:
        // This is the trickiest transition in the whole protocol. It
        // indicates that you have already sent a D-H Commit message to
        // your
        // correspondent, but that he either didn't receive it, or just
        // didn't receive it yet, and has sent you one as well. The
        // symmetry
        // will be broken by comparing the hashed gx you sent in your
        // D-H
        // Commit Message with the one you received, considered as
        // 32-byte
        // unsigned big-endian values.
        BigInteger ourHash = new BigInteger(1, this.getLocalDHPublicKeyHash());
        BigInteger theirHash = new BigInteger(1, m.dhPublicKeyHash);

        if (theirHash.compareTo(ourHash) == -1) {
          // Ignore the incoming D-H Commit message, but resend your
          // D-H
          // Commit message.
          getSession().injectMessage(messageFactory.getDHCommitMessage());
          logger.finest(
              "Ignored the incoming D-H Commit message, but resent our D-H Commit message.");
        } else {
          // *Forget* your old gx value that you sent (encrypted)
          // earlier,
          // and pretend you're in AUTHSTATE_NONE; i.e. reply with a
          // D-H
          // Key Message, and transition authstate to
          // AUTHSTATE_AWAITING_REVEALSIG.
          this.reset();
          this.setProtocolVersion(2);
          this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
          this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
          this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
          getSession().injectMessage(messageFactory.getDHKeyMessage());
          logger.finest(
              "Forgot our old gx value that we sent (encrypted) earlier, and pretended we're in AUTHSTATE_NONE -> Sent D-H key.");
        }
        break;

      case AWAITING_REVEALSIG:
        // Retransmit your D-H Key Message (the same one as you sent
        // when
        // you entered AUTHSTATE_AWAITING_REVEALSIG). Forget the old D-H
        // Commit message, and use this new one instead.
        this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
        this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
        getSession().injectMessage(messageFactory.getDHKeyMessage());
        logger.finest("Sent D-H key.");
        break;
      case AWAITING_SIG:
        // Reply with a new D-H Key message, and transition authstate to
        // AUTHSTATE_AWAITING_REVEALSIG
        this.reset();
        this.setRemoteDHPublicKeyEncrypted(m.dhPublicKeyEncrypted);
        this.setRemoteDHPublicKeyHash(m.dhPublicKeyHash);
        this.setAuthenticationState(AuthContext.AWAITING_REVEALSIG);
        getSession().injectMessage(messageFactory.getDHKeyMessage());
        logger.finest("Sent D-H key.");
        break;
      case V1_SETUP:
        throw new UnsupportedOperationException();
    }
  }
  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;
    }
  }