예제 #1
0
  // Send a message to the network, fragmenting first if necessary.
  // All messages to be sent to the network should go through this
  // method immediately before they are sent, ie after encryption.
  String fragmentAndSend(String message, int fragPolicy, OTRCallbacks callback) {
    int mms = callback.maxMessageSize(this);
    // Don't incur overhead of fragmentation unless necessary
    if (mms == 0 || message.length() <= mms) {
      // No fragmentation necessary
      if (fragPolicy == Policy.FRAGMENT_SEND_ALL) {
        callback.injectMessage(this.accountName, this.protocol, this.recName, message);
        return null;
      } else {
        // return the entire given message.
        return message;
      }
    }
    int fragment_count = ((message.length() - 1) / (mms - 19)) + 1;
    // like ceil(msglen/(mms - 19))

    String[] frags = Proto.fragmentCreate(mms, fragment_count, message);
    String returnFragment = null;

    // Determine which fragments to send and which to return
    // based on given Fragment Policy.  If the first fragment
    // should be returned instead of sent, store it.
    if (fragPolicy == Policy.FRAGMENT_SEND_ALL_BUT_FIRST) {
      returnFragment = frags[0];
    } else {
      callback.injectMessage(accountName, protocol, recName, frags[0]);
    }

    // Prevent the demo receiver to receive all the messages in a single read
    try {
      Thread.sleep(100);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    for (int i = 1; i < fragment_count - 1; i++) {
      callback.injectMessage(accountName, protocol, recName, frags[i]);
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    // If the last fragment should be stored instead of sent,
    // store it
    if (fragPolicy == Policy.FRAGMENT_SEND_ALL_BUT_LAST) {
      returnFragment = frags[fragment_count - 1];
    } else {
      callback.injectMessage(accountName, protocol, recName, frags[fragment_count - 1]);
    }
    return returnFragment;
  }
예제 #2
0
 /**
  * Put a connection into the PLAINTEXT state, first sending the other side a notice that we're
  * doing so if we're currently ENCRYPTED, and we think he's logged in.
  *
  * @throws OTRException
  */
 public void disconnect(OTRCallbacks callback) throws OTRException {
   if (this.msgState.getCurState() == MsgState.ST_ENCRYPTED
       && this.their_keyid > 0
       && callback.isLoggedIn(accountName, protocol, recName) == 1) {
     TLV[] tlvs = new TLV[1];
     tlvs[0] = new TLV(TLV.DISCONNECTED, new byte[0]);
     DataMessage dm = Proto.createData(this, new byte[0], Proto.MSGFLAGS_IGNORE_UNREADABLE, tlvs);
     callback.injectMessage(accountName, protocol, recName, new String(dm.getContent()));
   }
   forceFinished();
   this.msgState.curState = MsgState.ST_UNENCRYPTED;
   callback.updateContextList();
 }
예제 #3
0
  /**
   * Handle a message just received from the network. It is safe to pass all received messages to
   * this routine.
   *
   * <p>If no Exception is thrown, and the return value is not null, replace the received message
   * with msg in the returned StringTLV, and deliver that to the user instead.
   *
   * <p>If the return value is null, then the message you received was an internal protocol message,
   * and no message should be delivered to the user.
   */
  public StringTLV messageReceiving(String inMessage, OTRCallbacks callback) throws OTRException {

    OTRMessage message = OTRMessage.parse(inMessage);

    /* Check the policy */
    int policy = callback.getOtrPolicy(this);

    /* Should we go on at all? */
    if ((policy & Policy.VERSION_MASK) == 0) {
      return null;
    }

    // See if we have a fragment
    switch (Proto.fragmentAccumulate(this, new String(message.getContent()))) {
      case Proto.FRAGMENT_UNFRAGMENTED:
        // Do nothing
        break;
      case Proto.FRAGMENT_INCOMPLETE:
        // We've accumulated this fragment, but we don't have a
        // complete message yet
        return null;
      case Proto.FRAGMENT_COMPLETE:
        // We've got a new complete message, in unfragmessage.
        message = OTRMessage.parse(this.complete_msg);
        break;
    }

    byte msgtype = message.getType();

    /* See if they responded to our OTR offer */
    if ((policy & Policy.SEND_WHITESPACE_TAG) != 0) {
      if (msgtype != OTRMessage.MSG_NOTOTR) {
        otr_offer = OFFER_ACCEPTED;
      } else if (otr_offer == OFFER_SENT) {
        otr_offer = OFFER_REJECTED;
      }
    }

    gone_encrypted = 0;
    ignore_message = -1;

    switch (msgtype) {
      case OTRMessage.MSG_QUERY:

        /* Start AKE */
        try {
          auth.startAKE(null);
          this.sendOrErrorAuth(false, callback);
        } catch (OTRException e) {
          this.sendOrErrorAuth(true, callback);
        }
        if (auth.havemsgp != 0) {
          this.lastMessage = auth.lastauthmsg;
        }
        /* Don't display the Query message to the user. */
        if (ignore_message == -1) ignore_message = 1;
        break;
      case OTRMessage.MSG_DH_COMMIT:
        if ((policy & Policy.ALLOW_V2) != 0) {
          try {
            auth.handleCommit(message.getContent(), null);
            this.sendOrErrorAuth(false, callback);
          } catch (OTRException e) {
            this.sendOrErrorAuth(true, callback);
          }
        }
        if (ignore_message == -1) ignore_message = 1;
        break;
      case OTRMessage.MSG_DH_KEY:
        if ((policy & Policy.ALLOW_V2) != 0) {
          /* Get our private key */
          PrivKey privkey = us.getPrivKey(new Account(accountName, protocol), true);
          try {
            auth.handleKey(message.getContent(), privkey);
            this.sendOrErrorAuth(false, callback);
          } catch (OTRException e) {
            this.sendOrErrorAuth(true, callback);
          }
        }
        if (ignore_message == -1) ignore_message = 1;
        break;

      case OTRMessage.MSG_REVEAL_SIGNATURE:
        if ((policy & Policy.ALLOW_V2) != 0) {
          /* Get our private key */
          PrivKey privkey = us.getPrivKey(new Account(accountName, protocol), true);
          try {
            auth.handleRevealsig(message.getContent(), privkey);
            this.goEncrypted(callback);
            this.sendOrErrorAuth(false, callback);
          } catch (OTRException e) {
            this.sendOrErrorAuth(true, callback);
          }
        }
        if (ignore_message == -1) ignore_message = 1;
        break;
      case OTRMessage.MSG_SIGNATURE:
        if ((policy & Policy.ALLOW_V2) != 0) {
          /* Get our private key */
          try {
            auth.handleSignature(message.getContent());
            this.goEncrypted(callback);
            this.sendOrErrorAuth(false, callback);
          } catch (OTRException e) {
            this.sendOrErrorAuth(true, callback);
          }
        }
        if (ignore_message == -1) ignore_message = 1;
        break;
      case OTRMessage.MSG_DATA:
        switch (msgState.getCurState()) {
          case MsgState.ST_UNENCRYPTED:
          case MsgState.ST_FINISHED:
            callback.handleMsgEvent(OTRCallbacks.OTRL_ERRCODE_MSG_NOT_IN_PRIVATE, this, null);
            ignore_message = 1;
            String err_msg =
                callback.errorMessage(this, OTRCallbacks.OTRL_ERRCODE_MSG_NOT_IN_PRIVATE);
            callback.injectMessage(accountName, protocol, recName, err_msg);
            break;
          case MsgState.ST_ENCRYPTED:
            byte[] res;
            StringTLV stlv = new StringTLV();
            OTRTLV[] tlvs;
            try {
              DataMessage dm = (DataMessage) message;
              res = Proto.acceptData(this, dm, null);
            } catch (OTRException otre) {
              callback.handleMsgEvent(OTRCallbacks.OTRL_MSGEVENT_RCVDMSG_UNREADABLE, this, null);
              return null;
            }
            int end = 0;
            for (; end < res.length && res[end] != 0; end++) {}

            stlv.msg = new String(res, 0, end);
            if (end != res.length && end + 1 != res.length) {
              end++;
              tlvs = new TLV().parse(res, end, res.length - end);
              stlv.tlvs = tlvs;

              /* If the other side told us he's disconnected his
               * private connection, make a note of that so we
               * don't try sending anything else to him. */
              OTRTLV tlv = new TLV().find(tlvs, TLV.DISCONNECTED);
              if (tlv != null) {
                forceFinished();
              }

              /* If TLVs contain SMP data, process it */
              int nextMsg = this.smstate.nextExpected;
              tlv = new TLV().find(tlvs, TLV.SMP1Q);
              if (tlv != null && nextMsg == SM.EXPECT1) {
                /* We can only do the verification half now.
                 * We must wait for the secret to be entered
                 * to continue. */
                byte[] question = tlv.getValue();
                int qlen = 0;
                for (; qlen != question.length && question[qlen] != 0; qlen++) {}
                if (qlen == question.length) qlen = 0;
                else qlen++;
                byte[] input = new byte[question.length - qlen];
                System.arraycopy(question, qlen, input, 0, question.length - qlen);
                SM.step2a(this.smstate, input, 1, prov);
                if (qlen != 0) qlen--;
                byte[] plainq = new byte[qlen];
                System.arraycopy(question, 0, plainq, 0, qlen);
                if (this.smstate.smProgState != SM.PROG_CHEATED) {
                  callback.handleSmpEvent(
                      OTRCallbacks.OTRL_SMPEVENT_ASK_FOR_ANSWER, this, 25, new String(plainq));
                } else {
                  callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_CHEATED, this, 0, null);
                  this.smstate.nextExpected = SM.EXPECT1;
                  this.smstate.smProgState = SM.PROG_OK;
                }
              } else {
                callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_ERROR, this, 0, null);
              }

              tlv = new TLV().find(tlvs, TLV.SMP1);
              if (tlv != null) {
                if (nextMsg == SM.EXPECT1) {
                  /* We can only do the verification half now.
                   * We must wait for the secret to be entered
                   * to continue. */
                  SM.step2a(this.smstate, tlv.getValue(), 0, prov);
                  if (this.smstate.smProgState != SM.PROG_CHEATED) {
                    callback.handleSmpEvent(
                        OTRCallbacks.OTRL_SMPEVENT_ASK_FOR_SECRET, this, 25, null);
                  } else {
                    callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_CHEATED, this, 0, null);
                    this.smstate.nextExpected = SM.EXPECT1;
                    this.smstate.smProgState = SM.PROG_OK;
                  }
                } else {
                  callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_ERROR, this, 0, null);
                }
              }

              tlv = new TLV().find(tlvs, TLV.SMP2);
              if (tlv != null && nextMsg == SM.EXPECT2) {
                byte[] nextmsg = SM.step3(this.smstate, tlv.getValue(), prov);
                if (this.smstate.smProgState != SM.PROG_CHEATED) {
                  /* Send msg with next smp msg content */
                  OTRTLV sendtlv = new TLV(TLV.SMP3, nextmsg);
                  OTRTLV[] stlvs = new OTRTLV[1];
                  stlvs[0] = sendtlv;
                  DataMessage dm =
                      Proto.createData(this, new byte[0], Proto.MSGFLAGS_IGNORE_UNREADABLE, stlvs);
                  byte[] senddata = dm.getContent();
                  this.fragmentAndSend(new String(senddata), Policy.FRAGMENT_SEND_ALL, callback);
                  this.smstate.nextExpected = SM.EXPECT4;
                } else {
                  callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_CHEATED, this, 0, null);
                  this.smstate.nextExpected = SM.EXPECT1;
                  this.smstate.smProgState = SM.PROG_OK;
                }
              } else if (tlv != null) {
                callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_ERROR, this, 0, null);
              }

              tlv = new TLV().find(tlvs, TLV.SMP3);
              if (tlv != null && nextMsg == SM.EXPECT3) {
                byte[] nextmsg = SM.step4(this.smstate, tlv.getValue(), prov);
                /* Set trust level based on result */
                if (this.smstate.smProgState == SM.PROG_SUCCEEDED) {
                  this.setSmpTrust(callback, true);
                } else {
                  this.setSmpTrust(callback, false);
                }
                if (this.smstate.smProgState != SM.PROG_CHEATED) {
                  /* Send msg with next smp msg content */
                  OTRTLV[] stlvs = new TLV[1];
                  stlvs[0] = new TLV(TLV.SMP4, nextmsg);
                  DataMessage dm =
                      Proto.createData(this, new byte[0], Proto.MSGFLAGS_IGNORE_UNREADABLE, stlvs);
                  byte[] senddata = dm.getContent();
                  this.fragmentAndSend(new String(senddata), Policy.FRAGMENT_SEND_ALL, callback);
                  int succorfail =
                      this.smstate.smProgState == SM.PROG_SUCCEEDED
                          ? OTRCallbacks.OTRL_SMPEVENT_SUCCESS
                          : OTRCallbacks.OTRL_SMPEVENT_FAILURE;
                  callback.handleSmpEvent(succorfail, this, 100, null);
                  this.smstate.nextExpected = SM.EXPECT1;
                } else {
                  callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_CHEATED, this, 0, null);
                  this.smstate.nextExpected = SM.EXPECT1;
                  this.smstate.smProgState = SM.PROG_OK;
                }

              } else if (tlv != null) {
                callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_ERROR, this, 0, null);
              }

              tlv = new TLV().find(tlvs, TLV.SMP4);
              if (tlv != null && nextMsg == SM.EXPECT4) {

                SM.step5(this.smstate, tlv.getValue(), prov);
                if (this.smstate.smProgState == SM.PROG_SUCCEEDED) {
                  this.setSmpTrust(callback, true);
                } else {
                  this.setSmpTrust(callback, false);
                }
                if (this.smstate.smProgState != SM.PROG_CHEATED) {
                  int succorfail =
                      this.smstate.smProgState == SM.PROG_SUCCEEDED
                          ? OTRCallbacks.OTRL_SMPEVENT_SUCCESS
                          : OTRCallbacks.OTRL_SMPEVENT_FAILURE;
                  callback.handleSmpEvent(succorfail, this, 100, null);
                  this.smstate.nextExpected = SM.EXPECT1;
                } else {
                  callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_CHEATED, this, 0, null);
                  this.smstate.nextExpected = SM.EXPECT1;
                  this.smstate.smProgState = SM.PROG_OK;
                }

              } else if (tlv != null) {
                callback.handleSmpEvent(OTRCallbacks.OTRL_SMPEVENT_ERROR, this, 0, null);
              }

              tlv = new TLV().find(tlvs, TLV.SMP_ABORT);
              if (tlv != null) {
                this.smstate.nextExpected = SM.EXPECT1;
              }
            }
            return stlv;
        }
        break;
      case OTRMessage.MSG_TAGGED_WHITESPACE:
        /* Start AKE */
        if (msgState.getCurState() == MsgState.ST_UNENCRYPTED) {
          auth.startAKE(null);
          if (auth.havemsgp != 0) {
            this.lastMessage = new String(auth.lastauthmsg);
            fragmentAndSend(lastMessage, Policy.FRAGMENT_SEND_ALL, callback);
            StringTLV stlv = new StringTLV();
            stlv.msg = ((TaggedPlaintextMessage) message).getStripped();
            return stlv;
          }
        } else {
          StringTLV stlv = new StringTLV();
          stlv.msg = ((TaggedPlaintextMessage) message).getStripped();
          return stlv;
        }
        break;
      case OTRMessage.MSG_NOTOTR:
        if (this.msgState.getCurState() != MsgState.ST_UNENCRYPTED
            || (policy & Policy.REQUIRE_ENCRYPTION) != 0) {
          /* Not fine.  Let the user know. */
          callback.handleMsgEvent(
              OTRCallbacks.OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED, this, message.toString());
        }
        ignore_message = 1;
        break;
      default:
        /* We received an OTR message we didn't recognize.  Ignore
         * it, but make a log entry. */
        callback.handleMsgEvent(OTRCallbacks.OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED, this, null);
        ignore_message = 1;
        break;
    }
    return null;
  }