| /* |
| Copyright (C) 2006-2013 Werner Dittmann |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Lesser General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /** |
| * @author Werner Dittmann <Werner.Dittmann@t-online.de> |
| */ |
| |
| #include <iostream> |
| #include <cstdlib> |
| #include <ctype.h> |
| |
| #include <libzrtpcpp/ZRtp.h> |
| #include <libzrtpcpp/ZrtpStateClass.h> |
| |
| using namespace std; |
| using namespace GnuZrtpCodes; |
| |
| state_t states[numberOfStates] = { |
| {Initial, &ZrtpStateClass::evInitial }, |
| {Detect, &ZrtpStateClass::evDetect }, |
| {AckDetected, &ZrtpStateClass::evAckDetected }, |
| {AckSent, &ZrtpStateClass::evAckSent }, |
| {WaitCommit, &ZrtpStateClass::evWaitCommit }, |
| {CommitSent, &ZrtpStateClass::evCommitSent }, |
| {WaitDHPart2, &ZrtpStateClass::evWaitDHPart2 }, |
| {WaitConfirm1, &ZrtpStateClass::evWaitConfirm1 }, |
| {WaitConfirm2, &ZrtpStateClass::evWaitConfirm2 }, |
| {WaitConfAck, &ZrtpStateClass::evWaitConfAck }, |
| {WaitClearAck, &ZrtpStateClass::evWaitClearAck }, |
| {SecureState, &ZrtpStateClass::evSecureState }, |
| {WaitErrorAck, &ZrtpStateClass::evWaitErrorAck } |
| }; |
| |
| |
| ZrtpStateClass::ZrtpStateClass(ZRtp *p) : parent(p), commitPkt(NULL), multiStream(false), secSubstate(Normal), sentVersion(0) { |
| engine = new ZrtpStates(states, numberOfStates, Initial); |
| |
| // Set up timers according to ZRTP spec |
| T1.start = 50; |
| T1.maxResend = 20; |
| T1.capping = 200; |
| |
| T2.start = 150; |
| T2.maxResend = 10; |
| T2.capping = 600; |
| } |
| |
| ZrtpStateClass::~ZrtpStateClass(void) { |
| |
| // If not in Initial state: close the protocol engine |
| // before destroying it. This will free pending packets |
| // if necessary. |
| if (!inState(Initial)) { |
| Event_t ev; |
| |
| cancelTimer(); |
| ev.type = ZrtpClose; |
| event = &ev; |
| engine->processEvent(*this); |
| } |
| delete engine; |
| } |
| |
| void ZrtpStateClass::processEvent(Event_t *ev) { |
| |
| char *msg, first, middle, last; |
| uint8_t *pkt; |
| |
| parent->synchEnter(); |
| |
| event = ev; |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| first = tolower(*msg); |
| middle = tolower(*(msg+4)); |
| last = tolower(*(msg+7)); |
| |
| // Sanity check of packet size for all states except WaitErrorAck. |
| if (!inState(WaitErrorAck)) { |
| uint16_t totalLength = *(uint16_t*)(pkt+2); |
| totalLength = zrtpNtohs(totalLength) * ZRTP_WORD_SIZE; |
| totalLength += 12 + sizeof(uint32_t); // 12 bytes is fixed header, uint32_t is CRC |
| |
| if (totalLength != ev->length) { |
| fprintf(stderr, "Total length does not match received length: %d - %ld\n", totalLength, ev->length); |
| sendErrorPacket(MalformedPacket); |
| parent->synchLeave(); |
| return; |
| } |
| } |
| |
| // Check if this is an Error packet. |
| if (first == 'e' && middle =='r' && last == ' ') { |
| /* |
| * Process a received Error packet. |
| * |
| * In any case stop timer to prevent resending packets. |
| * Use callback method to prepare and get an ErrorAck packet. |
| * Modify event type to "ErrorPkt" and hand it over to current |
| * state for further processing. |
| */ |
| cancelTimer(); |
| ZrtpPacketError epkt(pkt); |
| ZrtpPacketErrorAck* eapkt = parent->prepareErrorAck(&epkt); |
| parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(eapkt)); |
| event->type = ErrorPkt; |
| } |
| else if (first == 'p' && middle == ' ' && last == ' ') { |
| ZrtpPacketPing ppkt(pkt); |
| ZrtpPacketPingAck* ppktAck = parent->preparePingAck(&ppkt); |
| if (ppktAck != NULL) { // ACK only to valid PING packet, otherwise ignore it |
| parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(ppktAck)); |
| } |
| parent->synchLeave(); |
| return; |
| } |
| else if (first == 's' && last == 'y') { |
| uint32_t errorCode = 0; |
| ZrtpPacketSASrelay* srly = new ZrtpPacketSASrelay(pkt); |
| ZrtpPacketRelayAck* rapkt = parent->prepareRelayAck(srly, &errorCode); |
| parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(rapkt)); |
| parent->synchLeave(); |
| return; |
| } |
| } |
| /* |
| * Shut down protocol state engine: cancel outstanding timer, further |
| * processing in current state. |
| */ |
| else if (event->type == ZrtpClose) { |
| cancelTimer(); |
| } |
| engine->processEvent(*this); |
| parent->synchLeave(); |
| } |
| |
| |
| void ZrtpStateClass::evInitial(void) { |
| DEBUGOUT((cout << "Checking for match in Initial.\n")); |
| |
| if (event->type == ZrtpInitial) { |
| ZrtpPacketHello* hello = parent->prepareHello(); |
| sentVersion = hello->getVersionInt(); |
| |
| // remember packet for easy resend in case timer triggers |
| sentPacket = static_cast<ZrtpPacketBase *>(hello); |
| |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T1) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| return; |
| } |
| nextState(Detect); |
| } |
| } |
| |
| /* |
| * Detect state. |
| * |
| * When in this state the protocol engine sent an initial Hello packet |
| * to the peer. |
| * |
| * When entering this state transition function then: |
| * - Assume Initiator mode, mode may change later on peer reaction |
| * - Instance variable sentPacket contains the sent Hello packet |
| * - Hello timer T1 may be active. This is the case if the other peer |
| * has prepared its RTP session and answers our Hello packets nearly |
| * immediately, i.e. before the Hello timeout counter expires. If the |
| * other peer does not send a Hello during this time the state engine |
| * reports "other peer does not support ZRTP" but stays |
| * in state Detect with no active timer (passiv mode). Staying in state |
| * Detect allows another peer to start its detect phase any time later. |
| * |
| * This restart capability is the reason why we use "startTimer(&T1)" in |
| * case we received a Hello packet from another peer. This effectively |
| * restarts the Hello timeout counter. |
| * |
| * In this state we also handle ZrtpInitialize event. This forces a |
| * restart of ZRTP discovery if an application calls ZrtpQueue#startZrtp |
| * again. This may happen after a previous discovery phase were not |
| * successful. |
| * |
| * Usually applications use some sort of signaling protocol, for example |
| * SIP, to negotiate the RTP parameters. Thus the RTP sessions setup is |
| * fairly sychronized and thus also the ZRTP detection phase. Applications |
| * that use some other ways to setup the RTP sessions this restart capability |
| * comes in handy because no RTP setup sychronization is necessary. |
| * |
| * Possible events in this state are: |
| * - timeout for sent Hello packet: causes a resend check and |
| * repeat sending of Hello packet |
| * - received a HelloAck: stop active timer, prepare and send Hello packet, |
| * switch to state AckDeteced. |
| * - received a Hello: stop active timer, send HelloAck, prepare Commit |
| * packet, switch to state AckSent. |
| * |
| */ |
| void ZrtpStateClass::evDetect(void) { |
| |
| DEBUGOUT((cout << "Checking for match in Detect.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| /* |
| * First check the general event type, then discrimnate |
| * the real event. |
| */ |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| /* |
| * HelloAck: |
| * - our peer acknowledged our Hello packet, we have not seen the peer's Hello yet |
| * - cancel timer T1 to stop resending Hello |
| * - switch to state AckDetected, wait for peer's Hello (F3) |
| * |
| * When we receive an HelloAck this also means that out partner accepted our protocol version. |
| */ |
| if (first == 'h' && last =='k') { |
| cancelTimer(); |
| sentPacket = NULL; |
| nextState(AckDetected); |
| return; |
| } |
| /* |
| * Hello: |
| * - send HelloAck packet to acknowledge the received Hello packet if versions match. |
| * Otherweise negotiate ZRTP versions. |
| * - use received Hello packet to prepare own Commit packet. We need to |
| * do it at this point because we need the hash value computed from |
| * peer's Hello packet. Follwing states my use the prepared Commit. |
| * - switch to new state AckSent which sends own Hello packet until |
| * peer acknowledges this |
| * - Don't clear sentPacket, points to Hello |
| */ |
| if (first == 'h' && last ==' ') { |
| ZrtpPacketHello hpkt(pkt); |
| |
| cancelTimer(); |
| |
| /* |
| * Check and negotiate the ZRTP protocol version first. |
| * |
| * This selection mechanism relies on the fact that we sent the highest supported protocol version in |
| * the initial Hello packet with as stated in RFC6189, section 4.1.1 |
| */ |
| int32_t recvVersion = hpkt.getVersionInt(); |
| if (recvVersion > sentVersion) { // We don't support this version, stay in state with timer active |
| if (startTimer(&T1) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| } |
| return; |
| } |
| |
| /* |
| * The versions don't match. Start negotiating versions. This negotiation stays in the Detect state. |
| * Only if the received version matches our own sent version we start to send a HelloAck. |
| */ |
| if (recvVersion != sentVersion) { |
| ZRtp::HelloPacketVersion* hpv = parent->helloPackets; |
| |
| int32_t index; |
| for (index = 0; hpv->packet && hpv->packet != parent->currentHelloPacket; hpv++, index++) // Find current sent Hello |
| ; |
| |
| for(; index >= 0 && hpv->version > recvVersion; hpv--, index--) // find a supported version less-equal to received version |
| ; |
| |
| if (index < 0) { |
| sendErrorPacket(UnsuppZRTPVersion); |
| return; |
| } |
| parent->currentHelloPacket = hpv->packet; |
| sentVersion = parent->currentHelloPacket->getVersionInt(); |
| |
| // remember packet for easy resend in case timer triggers |
| sentPacket = static_cast<ZrtpPacketBase *>(parent->currentHelloPacket); |
| |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T1) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| return; |
| } |
| return; |
| } |
| ZrtpPacketHelloAck* helloAck = parent->prepareHelloAck(); |
| |
| if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) { |
| parent->zrtpNegotiationFailed(Severe, SevereCannotSend); |
| return; |
| } |
| // Use peer's Hello packet to create my commit packet, store it |
| // for possible later usage in state AckSent |
| commitPkt = parent->prepareCommit(&hpkt, &errorCode); |
| |
| nextState(AckSent); |
| if (commitPkt == NULL) { |
| sendErrorPacket(errorCode); // switches to Error state |
| return; |
| } |
| if (startTimer(&T1) <= 0) { // restart own Hello timer/counter |
| timerFailed(SevereNoTimer); // returns to state Initial |
| } |
| T1.maxResend = 60; // more retries to extend time, see chap. 6 |
| } |
| return; // unknown packet for this state - Just ignore it |
| } |
| // Timer event triggered - this is Timer T1 to resend Hello |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (nextTimer(&T1) <= 0) { |
| commitPkt = NULL; |
| parent->zrtpNotSuppOther(); |
| nextState(Detect); |
| } |
| } |
| // If application calls zrtpStart() to restart discovery |
| else if (event->type == ZrtpInitial) { |
| cancelTimer(); |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T1) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * AckSent state. |
| * |
| * The protocol engine got a Hello packet from peer and answered with a |
| * HelloAck response. According to the protocol we must also send a |
| * Hello after HelloAck (refer to figure 1 in ZRTP RFC 6189, message |
| * HelloACK (F2) must be followed by Hello (F3)). We use the timeout in |
| * this state to send the required Hello (F3). |
| * |
| * Our peer must acknowledge the Hello with HelloAck. In earlier versions |
| * also a Commit was a valid packet thus the code covers this. |
| * Figure 1 in the RFC shows the HelloAck, chapter 7 states that a Commit |
| * may be send to acknowledge Hello. There is one constraint when using a Commit to |
| * acknowledge Hello: refer to chapter 4.1 that requires that both parties |
| * have completed the Hello/HelloAck discovery handshake. This implies that |
| * only message F4 may be replaced by a Commit. This constraint guarantees |
| * that both peers have seen at least one Hello. |
| * |
| * When entering this transition function: |
| * - The instance variabe sentPacket contains own Hello packet |
| * - The instance variabe commitPkt points to prepared Commit packet |
| * - Timer T1 is active |
| * |
| * Possible events in this state are: |
| * - timeout for sent Hello packet: causes a resend check and repeat sending |
| * of Hello packet |
| * - HelloAck: The peer answered with HelloAck to own HelloAck/Hello. Send |
| * prepared Commit packet and try Initiator mode. |
| * - Commit: The peer answered with Commit to HelloAck/Hello, thus switch to |
| * responder mode. |
| * - Hello: If the protcol engine receives another Hello it repeats the |
| * HelloAck/Hello response until Timer T1 exceeds its maximum. This may |
| * happen if the other peer sends Hello only (maybe due to network problems) |
| */ |
| void ZrtpStateClass::evAckSent(void) { |
| |
| DEBUGOUT((cout << "Checking for match in AckSent.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| /* |
| * First check the general event type, then discrimnate |
| * the real event. |
| */ |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| |
| /* |
| * HelloAck: |
| * The peer answers with HelloAck to own HelloAck/Hello. Send Commit |
| * and try Initiator mode. The requirement defined in chapter 4.1 to |
| * have a complete Hello/HelloAck is fulfilled. |
| * - stop Hello timer T1 |
| * - send own Commit message |
| * - switch state to CommitSent, start Commit timer, assume Initiator |
| */ |
| if (first == 'h' && last =='k') { |
| cancelTimer(); |
| |
| // remember packet for easy resend in case timer triggers |
| // Timer trigger received in new state CommitSend |
| sentPacket = static_cast<ZrtpPacketBase *>(commitPkt); |
| commitPkt = NULL; // now stored in sentPacket |
| nextState(CommitSent); |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T2) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| } |
| return; |
| } |
| /* |
| * Hello: |
| * - peer didn't receive our HelloAck |
| * - repeat HelloAck/Hello response: |
| * -- get HelloAck packet, send it |
| * -- The timeout trigger of T1 sends our Hello packet |
| * -- stay in state AckSent |
| * |
| * Similar to Detect state: just acknowledge the Hello, the next |
| * timeout sends the following Hello. |
| */ |
| |
| if (first == 'h' && last ==' ') { |
| ZrtpPacketHelloAck* helloAck = parent->prepareHelloAck(); |
| |
| if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) { |
| nextState(Detect); |
| parent->zrtpNegotiationFailed(Severe, SevereCannotSend); |
| } |
| return; |
| } |
| /* |
| * Commit: |
| * The peer answers with Commit to HelloAck/Hello, thus switch to |
| * responder mode. |
| * - stop timer T1 |
| * - prepare and send our DHPart1 |
| * - switch to state WaitDHPart2 and wait for peer's DHPart2 |
| * - don't start timer, we are responder |
| */ |
| if (first == 'c' && last == ' ') { |
| cancelTimer(); |
| ZrtpPacketCommit cpkt(pkt); |
| |
| if (!multiStream) { |
| ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the Commit packet |
| if (dhPart1 == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| commitPkt = NULL; |
| sentPacket = static_cast<ZrtpPacketBase *>(dhPart1); |
| nextState(WaitDHPart2); |
| } |
| else { |
| ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the Commit packet |
| if (confirm == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| sentPacket = static_cast<ZrtpPacketBase *>(confirm); |
| nextState(WaitConfirm2); |
| } |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| } |
| } |
| } |
| /* |
| * Timer: |
| * - resend Hello packet, stay in state, restart timer until repeat |
| * counter triggers |
| * - if repeat counter triggers switch to state Detect, con't clear |
| * sentPacket, Detect requires it to point to own Hello message |
| */ |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| return sendFailed(); // returns to state Initial |
| } |
| if (nextTimer(&T1) <= 0) { |
| parent->zrtpNotSuppOther(); |
| commitPkt = NULL; |
| // Stay in state Detect to be prepared get an hello from |
| // other peer any time later |
| nextState(Detect); |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| commitPkt = NULL; |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| /* |
| * AckDetected state. |
| * |
| * The protocol engine received a HelloAck in state Detect, thus the peer |
| * acknowledged our the Hello. According to ZRT RFC 6189 our peer must send |
| * its Hello until our protocol engine sees it (refer also to comment for |
| * state AckSent). This protocol sequence gurantees that both peers got at |
| * least one Hello. |
| * |
| * When entering this transition function |
| * - instance variable sentPacket is NULL, Hello timer stopped |
| * |
| * Possible events in this state are: |
| * Hello: we have to choices |
| * 1) we can acknowledge the peer's Hello with a HelloAck |
| * 2) we can acknowledge the peer's Hello with a Commit |
| * Both choices are implemented and may be enabled by setting a compile |
| * time #if (see code below). Currently we use choice 1) here because |
| * it's more aligned to the ZRTP specification |
| */ |
| void ZrtpStateClass::evAckDetected(void) { |
| |
| DEBUGOUT((cout << "Checking for match in AckDetected.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| |
| #if 1 |
| /* |
| * Implementation for choice 1) |
| * Hello: |
| * - Acknowledge peer's Hello, sending HelloACK (F4) |
| * - switch to state WaitCommit, wait for peer's Commit |
| * - we are going to be in the Responder role |
| */ |
| |
| if (first == 'h' && last ==' ') { |
| // Parse Hello packet and build an own Commit packet even if the |
| // Commit is not send to the peer. We need to do this to check the |
| // Hello packet and prepare the shared secret stuff. |
| ZrtpPacketHello hpkt(pkt); |
| ZrtpPacketCommit* commit = parent->prepareCommit(&hpkt, &errorCode); |
| |
| // Something went wrong during processing of the Hello packet, for |
| // example wrong version, duplicate ZID. |
| if (commit == NULL) { |
| sendErrorPacket(errorCode); |
| return; |
| } |
| ZrtpPacketHelloAck *helloAck = parent->prepareHelloAck(); |
| nextState(WaitCommit); |
| |
| // remember packet for easy resend |
| sentPacket = static_cast<ZrtpPacketBase *>(helloAck); |
| if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(helloAck))) { |
| sendFailed(); |
| } |
| } |
| #else |
| /* |
| * Implementation for choice 2) |
| * Hello: |
| * - Acknowledge peer's Hello by sending Commit (F5) |
| * instead of HelloAck (F4) |
| * - switch to state CommitSent |
| * - Initiator role, thus start timer T2 to monitor timeout for Commit |
| */ |
| |
| if (first == 'h' && last == ' ') { |
| // Parse peer's packet data into a Hello packet |
| ZrtpPacketHello hpkt(pkt); |
| ZrtpPacketCommit* commit = parent->prepareCommit(&hpkt, &errorCode); |
| // Something went wrong during processing of the Hello packet |
| if (commit == NULL) { |
| sendErrorPacket(errorCode); |
| return; |
| } |
| nextState(CommitSent); |
| |
| // remember packet for easy resend in case timer triggers |
| // Timer trigger received in new state CommitSend |
| sentPacket = static_cast<ZrtpPacketBase *>(commit); |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); |
| return; |
| } |
| if (startTimer(&T2) <= 0) { |
| timerFailed(SevereNoTimer); |
| } |
| } |
| #endif |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * WaitCommit state. |
| * |
| * This state is only used if we use choice 1) in AckDetected. |
| * |
| * When entering this transition function |
| * - instance variable sentPacket contains a HelloAck packet |
| * |
| * Possible events in this state are: |
| * - Hello: just resend our HelloAck |
| * - Commit: prepare and send our DHPart1 message to start first |
| * half of DH key agreement. Switch to state WaitDHPart2, don't |
| * start any timer, we a Responder. |
| */ |
| void ZrtpStateClass::evWaitCommit(void) { |
| |
| DEBUGOUT((cout << "Checking for match in WaitCommit.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| /* |
| * Hello: |
| * - resend HelloAck |
| * - stay in WaitCommit |
| */ |
| if (first == 'h' && last == ' ') { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| } |
| return; |
| } |
| /* |
| * Commit: |
| * - prepare DH1Part packet or Confirm1 if multi stream mode |
| * - send it to peer |
| * - switch state to WaitDHPart2 or WaitConfirm2 if multi stream mode |
| * - don't start timer, we are responder |
| */ |
| if (first == 'c' && last == ' ') { |
| ZrtpPacketCommit cpkt(pkt); |
| |
| if (!multiStream) { |
| ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the Commit packet |
| if (dhPart1 == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| sentPacket = static_cast<ZrtpPacketBase *>(dhPart1); |
| nextState(WaitDHPart2); |
| } |
| else { |
| ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the Commit packet |
| if (confirm == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| sentPacket = static_cast<ZrtpPacketBase *>(confirm); |
| nextState(WaitConfirm2); |
| } |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| } |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * CommitSent state. |
| * |
| * This state either handles a DH1Part1 message to start the first |
| * half of DH key agreement or it handles a Commit clash. If handling a |
| * Commit clash it may happen that we change our role from Initiator to |
| * Responder. |
| * |
| * When entering this transition function |
| * - assume Initiator mode, may change if we reveice a Commit here |
| * - sentPacket contains Commit packet |
| * - Commit timer (T2) active |
| * |
| * Possible events in this state are: |
| * - timeout for sent Commit packet: causes a resend check and repeat sending |
| * of Commit packet |
| * - Commit: This is a Commit clash. Break the tie accroding to chapter 5.2 |
| * - DHPart1: start first half of DH key agreement. Perpare and send own DHPart2 |
| * and switch to state WaitConfirm1. |
| */ |
| |
| void ZrtpStateClass::evCommitSent(void) { |
| |
| DEBUGOUT((cout << "Checking for match in CommitSend.\n")); |
| |
| char *msg, first, middle, last, secondLast; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| middle = tolower(*(msg+4)); |
| last = tolower(*(msg+7)); |
| secondLast = tolower(*(msg+6)); |
| |
| /* |
| * HelloAck or Hello: |
| * - delayed "HelloAck" or "Hello", maybe due to network latency, just |
| * ignore it |
| * - no switch in state, leave timer as it is |
| */ |
| if (first == 'h' && middle == 'o' && (last =='k' || last == ' ')) { |
| return; |
| } |
| |
| /* |
| * Commit: |
| * We have a "Commit" clash. Resolve it. |
| * |
| * - switch off resending Commit |
| * - compare my hvi with peer's hvi |
| * - if my hvi is greater |
| * - we are Initiator, stay in state, wait for peer's DHPart1 packet |
| * - else |
| * - we are Responder, stop timer |
| * - prepare and send DH1Packt, |
| * - switch to state WaitDHPart2, implies Responder path |
| */ |
| if (first == 'c' && last == ' ') { |
| ZrtpPacketCommit zpCo(pkt); |
| |
| if (!parent->verifyH2(&zpCo)) { |
| return; |
| } |
| cancelTimer(); // this cancels the Commit timer T2 |
| |
| if (!zpCo.isLengthOk(multiStream ? ZrtpPacketCommit::MultiStream : ZrtpPacketCommit::DhExchange)) { |
| sendErrorPacket(CriticalSWError); |
| return; |
| } |
| |
| // if our hvi is less than peer's hvi: switch to Responder mode and |
| // send DHPart1 or Confirm1 packet. Peer (as Initiator) will retrigger if |
| // necessary |
| // |
| if (parent->compareCommit(&zpCo) < 0) { |
| if (!multiStream) { |
| ZrtpPacketDHPart* dhPart1 = parent->prepareDHPart1(&zpCo, &errorCode); |
| |
| // Something went wrong during processing of the Commit packet |
| if (dhPart1 == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| nextState(WaitDHPart2); |
| sentPacket = static_cast<ZrtpPacketBase *>(dhPart1); |
| } |
| else { |
| ZrtpPacketConfirm* confirm = parent->prepareConfirm1MultiStream(&zpCo, &errorCode); |
| |
| // Something went wrong during processing of the Commit packet |
| if (confirm == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| nextState(WaitConfirm2); |
| sentPacket = static_cast<ZrtpPacketBase *>(confirm); |
| } |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| } |
| } |
| // Stay in state, we are Initiator, wait for DHPart1 of Confirm1 packet from peer. |
| // Resend Commit after timeout until we get a DHPart1 or Confirm1 |
| else { |
| if (startTimer(&T2) <= 0) { // restart the Commit timer, gives peer more time to react |
| timerFailed(SevereNoTimer); // returns to state Initial |
| } |
| } |
| return; |
| } |
| |
| /* |
| * DHPart1: |
| * - switch off resending Commit |
| * - Prepare and send DHPart2 |
| * - switch to WaitConfirm1 |
| * - start timer to resend DHPart2 if necessary, we are Initiator |
| */ |
| if (first == 'd' && secondLast == '1') { |
| cancelTimer(); |
| sentPacket = NULL; |
| ZrtpPacketDHPart dpkt(pkt); |
| ZrtpPacketDHPart* dhPart2 = parent->prepareDHPart2(&dpkt, &errorCode); |
| |
| // Something went wrong during processing of the DHPart1 packet |
| if (dhPart2 == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| else { |
| if (startTimer(&T2) <= 0) { |
| timerFailed(SevereNoTimer); // switches to state Initial |
| } |
| } |
| |
| return; |
| } |
| sentPacket = static_cast<ZrtpPacketBase *>(dhPart2); |
| nextState(WaitConfirm1); |
| |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T2) <= 0) { |
| timerFailed(SevereNoTimer); // switches to state Initial |
| } |
| return; |
| } |
| |
| /* |
| * Confirm1 and multi-stream mode |
| * - switch off resending commit |
| * - prepare Confirm2 |
| */ |
| if (multiStream && (first == 'c' && last == '1')) { |
| cancelTimer(); |
| ZrtpPacketConfirm cpkt(pkt); |
| |
| ZrtpPacketConfirm* confirm = parent->prepareConfirm2MultiStream(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the Confirm1 packet |
| if (confirm == NULL) { |
| sendErrorPacket(errorCode); |
| return; |
| } |
| nextState(WaitConfAck); |
| sentPacket = static_cast<ZrtpPacketBase *>(confirm); |
| |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T2) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| return; |
| } |
| // according to chap 5.6: after sending Confirm2 the Initiator must |
| // be ready to receive SRTP data. SRTP sender will be enabled in WaitConfAck |
| // state. |
| if (!parent->srtpSecretsReady(ForReceiver)) { |
| parent->sendInfo(Severe, CriticalSWError); |
| sendErrorPacket(CriticalSWError); |
| return; |
| } |
| } |
| } |
| // Timer event triggered, resend the Commit packet |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (nextTimer(&T2) <= 0) { |
| timerFailed(SevereTooMuchRetries); // returns to state Initial |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * WaitDHPart2 state. |
| * |
| * This state handles the second part of SH key agreement. Only the Resonder |
| * can enter this state. |
| * |
| * When entering this transition function |
| * - sentPacket contains DHPart1 packet, no timer active |
| * |
| * Possible events in this state are: |
| * - Commit: Our peer didn't receive out DHPart1 thus the peer sends Commit again. |
| * Just repeat our DHPart1. |
| * - DHPart2: start second half of DH key agreement. Perpare and send own Confirm1 |
| * and switch to state WaitConfirm2. |
| */ |
| void ZrtpStateClass::evWaitDHPart2(void) { |
| |
| DEBUGOUT((cout << "Checking for match in DHPart2.\n")); |
| |
| char *msg, first, secondLast, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| secondLast = tolower(*(msg+6)); |
| /* |
| * Commit: |
| * - resend DHPart1 |
| * - stay in state |
| */ |
| if (first == 'c' && last == ' ') { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| return sendFailed(); // returns to state Initial |
| } |
| return; |
| } |
| /* |
| * DHPart2: |
| * - prepare Confirm1 packet |
| * - switch to WaitConfirm2 |
| * - No timer, we are responder |
| */ |
| if (first == 'd' && secondLast == '2') { |
| ZrtpPacketDHPart dpkt(pkt); |
| ZrtpPacketConfirm* confirm = parent->prepareConfirm1(&dpkt, &errorCode); |
| |
| if (confirm == NULL) { |
| if (errorCode != IgnorePacket) { |
| sendErrorPacket(errorCode); |
| } |
| return; |
| } |
| nextState(WaitConfirm2); |
| sentPacket = static_cast<ZrtpPacketBase *>(confirm); |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| } |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * WaitConirm1 state. |
| * |
| * This state handles a received Confirm1 message and only the Initiator |
| * can enter this state. |
| * |
| * When entering this transition function in DH mode: |
| * - Initiator mode |
| * - sentPacket contains DHPart2 packet, DHPart2 timer active |
| * |
| * When entering this transition function in Multi stream mode via AckSent: |
| * - Initiator mode |
| * - sentPacket contains my Commit packet, Commit timer active |
| * |
| * Possible events in this state are: |
| * - timeout for sent DHPart2 packet: causes a resend check and repeat sending |
| * of DHPart2 packet. |
| * - Confirm1: Check Confirm1 message. If it is ok then prepare and send own |
| * Confirm2 packet and switch to state WaitConfAck. |
| */ |
| void ZrtpStateClass::evWaitConfirm1(void) { |
| |
| DEBUGOUT((cout << "Checking for match in WaitConfirm1.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| |
| /* |
| * Confirm1: |
| * - Switch off resending DHPart2 |
| * - prepare a Confirm2 packet |
| * - switch to state WaitConfAck |
| * - set timer to monitor Confirm2 packet, we are initiator |
| */ |
| if (first == 'c' && last == '1') { |
| cancelTimer(); |
| ZrtpPacketConfirm cpkt(pkt); |
| |
| ZrtpPacketConfirm* confirm = parent->prepareConfirm2(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the Confirm1 packet |
| if (confirm == NULL) { |
| sendErrorPacket(errorCode); |
| return; |
| } |
| // according to chap 5.8: after sending Confirm2 the Initiator must |
| // be ready to receive SRTP data. SRTP sender will be enabled in WaitConfAck |
| // state. |
| if (!parent->srtpSecretsReady(ForReceiver)) { |
| parent->sendInfo(Severe, CriticalSWError); |
| sendErrorPacket(CriticalSWError); |
| return; |
| } |
| nextState(WaitConfAck); |
| sentPacket = static_cast<ZrtpPacketBase *>(confirm); |
| |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (startTimer(&T2) <= 0) { |
| timerFailed(SevereNoTimer); // returns to state Initial |
| } |
| } |
| } |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (nextTimer(&T2) <= 0) { |
| timerFailed(SevereTooMuchRetries); // returns to state Initial |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * WaitConfirm2 state. |
| * |
| * Handles the Confirm2 message that closes the key agreement handshake. Only |
| * the Responder can enter this state. If the Confirm2 message is ok send a |
| * Conf2Ack to our peer. Switch to secure mode after sending Conf2Ack, our |
| * peer switches to secure mode after receiving Conf2Ack. |
| * |
| * TODO - revise documentation comments |
| * |
| * When entering this transition function |
| * - Responder mode |
| * - sentPacket contains Confirm1 packet, no timer active |
| * |
| * Possible events in this state are: |
| * - DHPart2: Our peer didn't receive our Confirm1 thus sends DHPart2 again. |
| * Just repeat our Confirm1. |
| * - Confirm2: close DH key agreement. Perpare and send own Conf2Ack |
| * and switch to state SecureState. |
| */ |
| void ZrtpStateClass::evWaitConfirm2(void) { |
| |
| DEBUGOUT((cout << "Checking for match in WaitConfirm2.\n")); |
| |
| char *msg, first, secondLast, last; |
| uint8_t *pkt; |
| uint32_t errorCode = 0; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| secondLast = tolower(*(msg+6)); |
| last = tolower(*(msg+7)); |
| |
| /* |
| * DHPart2 or Commit in multi stream mode: |
| * - resend Confirm1 packet |
| * - stay in state |
| */ |
| if ((first == 'd' && secondLast == '2') || (multiStream && (first == 'c' && last == ' '))) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| } |
| return; |
| } |
| /* |
| * Confirm2: |
| * - prepare ConfAck |
| * - switch on security (SRTP) |
| * - switch to SecureState |
| */ |
| if (first == 'c' && last == '2') { |
| ZrtpPacketConfirm cpkt(pkt); |
| ZrtpPacketConf2Ack* confack = parent->prepareConf2Ack(&cpkt, &errorCode); |
| |
| // Something went wrong during processing of the confirm2 packet |
| if (confack == NULL) { |
| sendErrorPacket(errorCode); |
| return; |
| } |
| sentPacket = static_cast<ZrtpPacketBase *>(confack); |
| |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (!parent->srtpSecretsReady(ForReceiver) || !parent->srtpSecretsReady(ForSender)) { |
| parent->sendInfo(Severe, CriticalSWError); |
| sendErrorPacket(CriticalSWError); |
| return; |
| } |
| nextState(SecureState); |
| parent->sendInfo(Info, InfoSecureStateOn); |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| /* |
| * WaitConf2Ack state. |
| * |
| * This state handles the Conf2Ack message that acknowledges the successfull |
| * processing of Confirm2. Only the Initiator can enter this state. Switch on |
| * secure mode and switch to state SecureState. |
| * |
| * When entering this transition function |
| * - Initiator mode |
| * - sentPacket contains Confirm2 packet, Confirm2 timer active |
| * - receiver security switched on |
| * |
| * Possible events in this state are: |
| * - timeout for sent Confirm2 packet: causes a resend check and repeat sending |
| * of Confirm2 packet |
| * - Conf2Ack: Key agreement was successfull, switch to secure mode. |
| */ |
| void ZrtpStateClass::evWaitConfAck(void) { |
| |
| DEBUGOUT((cout << "Checking for match in WaitConfAck.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| /* |
| * ConfAck: |
| * - Switch off resending Confirm2 |
| * - switch to SecureState |
| */ |
| if (first == 'c' && last == 'k') { |
| cancelTimer(); |
| sentPacket = NULL; |
| // Receiver was already enabled after sending Confirm2 packet |
| // see previous states. |
| if (!parent->srtpSecretsReady(ForSender)) { |
| parent->sendInfo(Severe, CriticalSWError); |
| sendErrorPacket(CriticalSWError); |
| return; |
| } |
| nextState(SecureState); |
| // TODO: call parent to clear signature data at initiator |
| parent->sendInfo(Info, InfoSecureStateOn); |
| } |
| } |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| parent->srtpSecretsOff(ForReceiver); |
| return; |
| } |
| if (nextTimer(&T2) <= 0) { |
| timerFailed(SevereTooMuchRetries); // returns to state Initial |
| parent->srtpSecretsOff(ForReceiver); |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| parent->srtpSecretsOff(ForReceiver); |
| } |
| } |
| |
| /* |
| * When entering this transition function |
| * - sentPacket contains GoClear packet, GoClear timer active |
| */ |
| |
| void ZrtpStateClass::evWaitClearAck(void) { |
| DEBUGOUT((cout << "Checking for match in ClearAck.\n")); |
| |
| // char *msg, first, last, middle; |
| // uint8_t *pkt; |
| // |
| // if (event->type == ZrtpPacket) { |
| // pkt = event->packet; |
| // msg = (char *)pkt + 4; |
| // |
| // first = tolower(*msg); |
| // middle = tolower(*(msg+4)); |
| // last = tolower(*(msg+7)); |
| // |
| // /* |
| // * ClearAck: |
| // * - stop resending GoClear, |
| // * - switch to state AckDetected, wait for peer's Hello |
| // */ |
| // if (first == 'c' && middle == 'r' && last =='k') { |
| // cancelTimer(); |
| // sentPacket = NULL; |
| // nextState(Initial); |
| // } |
| // } |
| // // Timer event triggered - this is Timer T2 to resend GoClear w/o HMAC |
| // else if (event->type == Timer) { |
| // if (!parent->sendPacketZRTP(sentPacket)) { |
| // sendFailed(); // returns to state Initial |
| // return; |
| // } |
| // if (nextTimer(&T2) <= 0) { |
| // timerFailed(SevereTooMuchRetries); // returns to state Initial |
| // } |
| // } |
| // else { // unknown Event type for this state (covers Error and ZrtpClose) |
| // if (event->type != ZrtpClose) { |
| // parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| // } |
| // sentPacket = NULL; |
| // nextState(Initial); |
| // } |
| } |
| |
| |
| /* |
| * WaitErrorAck state. |
| * |
| * This state belongs to the "error handling state overlay" and handle |
| * ErrorAck message. Most of the ZRTP states can send Error message for |
| * example if they detect wrong packets. After sending an Error message |
| * the protocol engine switches to WaitErrorAck state. Receiving an |
| * ErrorAck message completes the ZRTP error handling. |
| * |
| * When entering this transition function |
| * - sentPacket contains Error packet, Error timer active |
| * |
| * Possible events in this state are: |
| * - timeout for sent Error packet: causes a resend check and repeat sending |
| * of Error packet |
| * - ErrorAck: Stop timer and switch to state Initial. |
| */ |
| |
| void ZrtpStateClass::evWaitErrorAck(void) { |
| DEBUGOUT((cout << "Checking for match in ErrorAck.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| |
| /* |
| * Errorck: |
| * - stop resending Error, |
| * - switch to state Initial |
| */ |
| if (first == 'e' && last =='k') { |
| cancelTimer(); |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| // Timer event triggered - this is Timer T2 to resend Error. |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return; |
| } |
| if (nextTimer(&T2) <= 0) { |
| timerFailed(SevereTooMuchRetries); // returns to state Initial |
| } |
| } |
| else { // unknown Event type for this state (covers Error and ZrtpClose) |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| sentPacket = NULL; |
| nextState(Initial); |
| } |
| } |
| |
| void ZrtpStateClass::evSecureState(void) { |
| |
| DEBUGOUT((cout << "Checking for match in SecureState.\n")); |
| |
| char *msg, first, last; |
| uint8_t *pkt; |
| |
| /* |
| * Handle a possible substate. If substate handling was ok just return. |
| */ |
| if (secSubstate == WaitSasRelayAck) { |
| if (subEvWaitRelayAck()) |
| return; |
| } |
| |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| |
| /* |
| * Confirm2: |
| * - resend Conf2Ack packet |
| * - stay in state |
| */ |
| if (first == 'c' && last == '2') { |
| if (sentPacket != NULL && !parent->sendPacketZRTP(sentPacket)) { |
| sentPacket = NULL; |
| nextState(Initial); |
| parent->srtpSecretsOff(ForSender); |
| parent->srtpSecretsOff(ForReceiver); |
| parent->zrtpNegotiationFailed(Severe, SevereCannotSend); |
| } |
| return; |
| } |
| /* |
| * GoClear received, handle it. TODO fix go clear handling |
| * |
| if (first == 'g' && last == 'r') { |
| ZrtpPacketGoClear gpkt(pkt); |
| ZrtpPacketClearAck* clearAck = parent->prepareClearAck(&gpkt); |
| |
| if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(clearAck))) { |
| return; |
| } |
| // TODO Timeout to resend clear ack until user user confirmation |
| } |
| */ |
| } |
| else if (event->type == Timer) { |
| // Ignore stray timeout in this state |
| ; |
| } |
| // unknown Event type for this state (covers Error and ZrtpClose) |
| else { |
| // If in secure state ingnore error events to avoid Error packet injection |
| // attack - found by Dmitry Monakhov (dmonakhov@openvz.org) |
| if (event->type == ErrorPkt) |
| return; |
| sentPacket = NULL; |
| parent->srtpSecretsOff(ForSender); |
| parent->srtpSecretsOff(ForReceiver); |
| nextState(Initial); |
| if (event->type != ZrtpClose) { |
| parent->zrtpNegotiationFailed(Severe, SevereProtocolError); |
| } |
| parent->sendInfo(Info, InfoSecureStateOff); |
| } |
| } |
| |
| bool ZrtpStateClass::subEvWaitRelayAck() { |
| char *msg, first, last; |
| uint8_t* pkt; |
| |
| /* |
| * First check the general event type, then discrimnate the real event. |
| */ |
| if (event->type == ZrtpPacket) { |
| pkt = event->packet; |
| msg = (char *)pkt + 4; |
| |
| first = tolower(*msg); |
| last = tolower(*(msg+7)); |
| |
| /* |
| * SAS relayAck: |
| * - stop resending SASRelay, |
| * - switch to secure substate Normal |
| */ |
| if (first == 'r' && last =='k') { |
| cancelTimer(); |
| secSubstate = Normal; |
| sentPacket = NULL; |
| } |
| return true; |
| } |
| // Timer event triggered - this is Timer T2 to resend Error. |
| else if (event->type == Timer) { |
| if (!parent->sendPacketZRTP(sentPacket)) { |
| sendFailed(); // returns to state Initial |
| return false; |
| } |
| if (nextTimer(&T2) <= 0) { |
| // returns to state initial |
| // timerFailed(ZrtpCodes.SevereCodes.SevereTooMuchRetries); |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| int32_t ZrtpStateClass::startTimer(zrtpTimer_t *t) { |
| |
| t->time = t->start; |
| t->counter = 0; |
| return parent->activateTimer(t->time); |
| } |
| |
| int32_t ZrtpStateClass::nextTimer(zrtpTimer_t *t) { |
| |
| t->time += t->time; |
| t->time = (t->time > t->capping)? t->capping : t->time; |
| t->counter++; |
| if (t->counter > t->maxResend) { |
| return -1; |
| } |
| return parent->activateTimer(t->time); |
| } |
| |
| void ZrtpStateClass::sendErrorPacket(uint32_t errorCode) { |
| cancelTimer(); |
| |
| ZrtpPacketError* err = parent->prepareError(errorCode); |
| parent->zrtpNegotiationFailed(ZrtpError, errorCode); |
| |
| sentPacket = static_cast<ZrtpPacketBase *>(err); |
| nextState(WaitErrorAck); |
| if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(err)) || (startTimer(&T2) <= 0)) { |
| sendFailed(); |
| } |
| } |
| |
| void ZrtpStateClass::sendSASRelay(ZrtpPacketSASrelay* relay) { |
| cancelTimer(); |
| sentPacket = static_cast<ZrtpPacketBase *>(relay); |
| secSubstate = WaitSasRelayAck; |
| if (!parent->sendPacketZRTP(static_cast<ZrtpPacketBase *>(relay)) || (startTimer(&T2) <= 0)) { |
| sendFailed(); |
| } |
| } |
| |
| void ZrtpStateClass::sendFailed() { |
| sentPacket = NULL; |
| nextState(Initial); |
| parent->zrtpNegotiationFailed(Severe, SevereCannotSend); |
| } |
| |
| void ZrtpStateClass::timerFailed(int32_t subCode) { |
| sentPacket = NULL; |
| nextState(Initial); |
| parent->zrtpNegotiationFailed(Severe, subCode); |
| } |
| |
| void ZrtpStateClass::setMultiStream(bool multi) { |
| multiStream = multi; |
| } |
| |
| bool ZrtpStateClass::isMultiStream() { |
| return multiStream; |
| } |
| |
| /** EMACS ** |
| * Local variables: |
| * mode: c++ |
| * c-default-style: ellemtel |
| * c-basic-offset: 4 |
| * End: |
| */ |