Example #1
0
/**
 * If we don't know her key, the handshake has to be done backwards.
 * Reverse handshake requests are signaled by sending a non-obfuscated zero nonce.
 */
static uint8_t genReverseHandshake(struct Message* message,
                                   struct CryptoAuth_Wrapper* wrapper,
                                   union Headers_CryptoAuth* header)
{
    reset(wrapper);
    Message_shift(message, -Headers_CryptoAuth_SIZE, NULL);

    // Buffer the packet so it can be sent ASAP
    if (wrapper->bufferedMessage != NULL) {
        // Not exactly a drop but a message is not going to reach the destination.
        cryptoAuthDebug0(wrapper,
            "DROP Expelled a message because a session has not yet been setup");
        Allocator_free(wrapper->bufferedMessage->alloc);
    }

    cryptoAuthDebug0(wrapper, "Buffered a message");
    struct Allocator* bmalloc = Allocator_child(wrapper->externalInterface.allocator);
    wrapper->bufferedMessage = Message_clone(message, bmalloc);
    Assert_ifParanoid(wrapper->nextNonce == 0);

    Message_shift(message, Headers_CryptoAuth_SIZE, NULL);
    header = (union Headers_CryptoAuth*) message->bytes;
    header->nonce = UINT32_MAX;
    message->length = Headers_CryptoAuth_SIZE;

    // sessionState must be 0, auth and 24 byte nonce are garbaged and public key is set
    // now garbage the authenticator and the encrypted key which are not used.
    Random_bytes(wrapper->context->rand, (uint8_t*) &header->handshake.authenticator, 48);

    // This is a special packet which the user should never see.
    Headers_setSetupPacket(&header->handshake.auth, 1);

    return wrapper->wrappedInterface->sendMessage(message, wrapper->wrappedInterface);
}
Example #2
0
/**
 * If we don't know her key, the handshake has to be done backwards.
 * Reverse handshake requests are signaled by sending a non-obfuscated zero nonce.
 */
static uint8_t genReverseHandshake(struct Message* message,
                                   struct CryptoAuth_Wrapper* wrapper,
                                   union Headers_CryptoAuth* header)
{
    wrapper->nextNonce = 0;
    Message_shift(message, -Headers_CryptoAuth_SIZE);

    // Buffer the packet so it can be sent ASAP
    if (wrapper->bufferedMessage == NULL) {
        cryptoAuthDebug0(wrapper, "Buffered a message");
        wrapper->bufferedMessage =
            Message_clone(message, wrapper->externalInterface.allocator);
        Assert_true(wrapper->nextNonce == 0);
    } else {
        cryptoAuthDebug0(wrapper, "Expelled a message because a session has not yet been setup");
        Message_copyOver(wrapper->bufferedMessage,
                         message,
                         wrapper->externalInterface.allocator);
        Assert_true(wrapper->nextNonce == 0);
    }
    wrapper->hasBufferedMessage = true;

    Message_shift(message, Headers_CryptoAuth_SIZE);
    header = (union Headers_CryptoAuth*) message->bytes;
    header->nonce = UINT32_MAX;
    message->length = Headers_CryptoAuth_SIZE;

    // sessionState must be 0, auth and 24 byte nonce are garbaged and public key is set
    // now garbage the authenticator and the encrypted key which are not used.
    Random_bytes(wrapper->context->rand, (uint8_t*) &header->handshake.authenticator, 48);

    return wrapper->wrappedInterface->sendMessage(message, wrapper->wrappedInterface);
}
Example #3
0
static uint8_t receiveMessage(struct Message* received, struct Interface* interface)
{
    struct CryptoAuth_Wrapper* wrapper =
        Identity_cast((struct CryptoAuth_Wrapper*) interface->receiverContext);

    union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) received->bytes;

    if (received->length < (wrapper->authenticatePackets ? 20 : 4)) {
        cryptoAuthDebug0(wrapper, "Dropped runt");
        return Error_UNDERSIZE_MESSAGE;
    }
    Assert_true(received->padding >= 12 || "need at least 12 bytes of padding in incoming message");
    #ifdef Log_DEBUG
        Assert_true(!((uintptr_t)received->bytes % 4) || !"alignment fault");
    #endif
    Message_shift(received, -4);

    uint32_t nonce = Endian_bigEndianToHost32(header->nonce);

    if (wrapper->nextNonce < 5) {
        if (nonce > 3 && nonce != UINT32_MAX && knowHerKey(wrapper)) {
            cryptoAuthDebug(wrapper, "Trying final handshake step, nonce=%u\n", nonce);
            uint8_t secret[32];
            getSharedSecret(secret,
                            wrapper->secret,
                            wrapper->tempKey,
                            NULL,
                            wrapper->context->logger);

            // We'll optimistically advance the nextNonce value because decryptMessage()
            // passes the message on to the upper level and if this message causes a
            // response, we want the CA to be in ESTABLISHED state.
            // if the decryptMessage() call fails, we CryptoAuth_reset() it back.
            wrapper->nextNonce += 3;

            if (decryptMessage(wrapper, nonce, received, secret)) {
                cryptoAuthDebug0(wrapper, "Final handshake step succeeded");
                Bits_memcpyConst(wrapper->secret, secret, 32);
                return callReceivedMessage(wrapper, received);
            }
            CryptoAuth_reset(&wrapper->externalInterface);
            cryptoAuthDebug0(wrapper, "Final handshake step failed");
            return Error_UNDELIVERABLE;
        }
    } else if (nonce > 4) {
        if (decryptMessage(wrapper, nonce, received, wrapper->secret)) {
            return callReceivedMessage(wrapper, received);
        } else {
            cryptoAuthDebug0(wrapper, "Failed to decrypt message");
            return Error_UNDELIVERABLE;
        }
    } else {
        cryptoAuthDebug0(wrapper, "Received handshake message during established connection");
    }
    Message_shift(received, 4);
    return decryptHandshake(wrapper, nonce, received, header);
}
Example #4
0
/** @return 0 on success, -1 otherwise. */
int CryptoAuth_encrypt(struct CryptoAuth_Session* sessionPub, struct Message* msg)
{
    struct CryptoAuth_Session_pvt* session =
        Identity_check((struct CryptoAuth_Session_pvt*) sessionPub);

    // If there has been no incoming traffic for a while, reset the connection to state 0.
    // This will prevent "connection in bad state" situations from lasting forever.
    // this will reset the session if it has timed out.
    resetIfTimeout(session);

    // If the nonce wraps, start over.
    if (session->nextNonce >= 0xfffffff0) {
        reset(session);
    }

    Assert_true(!((uintptr_t)msg->bytes % 4) || !"alignment fault");

    // nextNonce 0: sending hello, we are initiating connection.
    // nextNonce 1: sending another hello, nothing received yet.
    // nextNonce 2: sending key, hello received.
    // nextNonce 3: sending key again, no data packet recieved yet.
    // nextNonce >3: handshake complete
    //
    // if it's a blind handshake, every message will be empty and nextNonce will remain
    // zero until the first message is received back.
    if (session->nextNonce < 5) {
        if (session->nextNonce < 4) {
            encryptHandshake(msg, session, 0);
            return 0;
        } else {
            cryptoAuthDebug0(session, "Doing final step to send message. nonce=4");
            Assert_ifParanoid(!Bits_isZero(session->ourTempPrivKey, 32));
            Assert_ifParanoid(!Bits_isZero(session->herTempPubKey, 32));
            getSharedSecret(session->sharedSecret,
                            session->ourTempPrivKey,
                            session->herTempPubKey,
                            NULL,
                            session->context->logger);
        }
    }

    Assert_true(msg->length > 0 && "Empty packet during handshake");
    Assert_true(msg->padding >= 36 || !"not enough padding");

    encrypt(session->nextNonce, msg, session->sharedSecret, session->isInitiator);

    Message_push32(msg, session->nextNonce, NULL);
    session->nextNonce++;
    return 0;
}
Example #5
0
static inline bool decryptMessage(struct CryptoAuth_Wrapper* wrapper,
                                  uint32_t nonce,
                                  struct Message* content,
                                  uint8_t secret[32])
{
    // Decrypt with authentication and replay prevention.
    if (decrypt(nonce, content, secret, wrapper->isInitiator)) {
        cryptoAuthDebug0(wrapper, "DROP authenticated decryption failed");
        return false;
    }
    if (!ReplayProtector_checkNonce(nonce, &wrapper->replayProtector)) {
        cryptoAuthDebug(wrapper, "DROP nonce checking failed nonce=[%u]", nonce);
        return false;
    }
    return true;
}
Example #6
0
static inline Gcc_USE_RET bool decryptMessage(struct CryptoAuth_Session_pvt* session,
                                              uint32_t nonce,
                                              struct Message* content,
                                              uint8_t secret[32])
{
    // Decrypt with authentication and replay prevention.
    if (decrypt(nonce, content, secret, session->isInitiator)) {
        cryptoAuthDebug0(session, "DROP authenticated decryption failed");
        return false;
    }
    if (!ReplayProtector_checkNonce(nonce, &session->pub.replayProtector)) {
        cryptoAuthDebug(session, "DROP nonce checking failed nonce=[%u]", nonce);
        return false;
    }
    return true;
}
Example #7
0
static uint8_t sendMessage(struct Message* message, struct Interface* interface)
{
    struct CryptoAuth_Wrapper* wrapper =
        Identity_cast((struct CryptoAuth_Wrapper*) interface->senderContext);

    // If there has been no incoming traffic for a while, reset the connection to state 0.
    // This will prevent "connection in bad state" situations from lasting forever.
    // this will reset the session if it has timed out.
    CryptoAuth_getState(interface);

    // If the nonce wraps, start over.
    if (wrapper->nextNonce >= 0xfffffff0) {
        reset(wrapper);
    }

    #ifdef Log_DEBUG
        Assert_true(!((uintptr_t)message->bytes % 4) || !"alignment fault");
    #endif

    // nextNonce 0: sending hello, we are initiating connection.
    // nextNonce 1: sending another hello, nothing received yet.
    // nextNonce 2: sending key, hello received.
    // nextNonce 3: sending key again, no data packet recieved yet.
    // nextNonce >3: handshake complete
    //
    // if it's a blind handshake, every message will be empty and nextNonce will remain
    // zero until the first message is received back.
    if (wrapper->nextNonce < 5) {
        if (wrapper->nextNonce < 4) {
            return encryptHandshake(message, wrapper, 0);
        } else {
            cryptoAuthDebug0(wrapper, "Doing final step to send message. nonce=4");
            Assert_true(!Bits_isZero(wrapper->ourTempPrivKey, 32));
            Assert_true(!Bits_isZero(wrapper->herTempPubKey, 32));
            getSharedSecret(wrapper->sharedSecret,
                            wrapper->ourTempPrivKey,
                            wrapper->herTempPubKey,
                            NULL,
                            wrapper->context->logger);
        }
    }

    Assert_true(message->length > 0 && "Empty packet during handshake");

    return encryptMessage(message, wrapper);
}
Example #8
0
static inline bool decryptMessage(struct CryptoAuth_Wrapper* wrapper,
                                  uint32_t nonce,
                                  struct Message* content,
                                  uint8_t secret[32])
{
    if (wrapper->authenticatePackets) {
        // Decrypt with authentication and replay prevention.
        int ret = decrypt(nonce, content, secret, wrapper->isInitiator, true);
        if (ret) {
            cryptoAuthDebug(wrapper, "Authenticated decryption failed returning %u", ret);
            return false;
        }
        ret = !ReplayProtector_checkNonce(nonce, &wrapper->replayProtector);
        if (ret) {
            cryptoAuthDebug0(wrapper, "Nonce checking failed");
            return false;
        }
    } else {
        decrypt(nonce, content, secret, wrapper->isInitiator, false);
    }
    return true;
}
Example #9
0
static uint8_t receiveMessage(struct Message* received, struct Interface* interface)
{
    struct CryptoAuth_Wrapper* wrapper =
        Identity_check((struct CryptoAuth_Wrapper*) interface->receiverContext);

    union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) received->bytes;

    if (received->length < 20) {
        cryptoAuthDebug0(wrapper, "DROP runt");
        return Error_UNDERSIZE_MESSAGE;
    }
    Assert_true(received->padding >= 12 || "need at least 12 bytes of padding in incoming message");
    Assert_true(!((uintptr_t)received->bytes % 4) || !"alignment fault");

    Message_shift(received, -4, NULL);

    uint32_t nonce = Endian_bigEndianToHost32(header->nonce);

    if (!wrapper->established) {
        if (nonce > 3 && nonce != UINT32_MAX) {
            if (wrapper->nextNonce < 3) {
                // This is impossible because we have not exchanged hello and key messages.
                cryptoAuthDebug0(wrapper, "DROP Received a run message to an un-setup session");
                return Error_UNDELIVERABLE;
            }
            cryptoAuthDebug(wrapper, "Trying final handshake step, nonce=%u\n", nonce);
            uint8_t secret[32];
            Assert_ifParanoid(!Bits_isZero(wrapper->ourTempPrivKey, 32));
            Assert_ifParanoid(!Bits_isZero(wrapper->herTempPubKey, 32));
            getSharedSecret(secret,
                            wrapper->ourTempPrivKey,
                            wrapper->herTempPubKey,
                            NULL,
                            wrapper->context->logger);

            // We'll optimistically advance the nextNonce value because decryptMessage()
            // passes the message on to the upper level and if this message causes a
            // response, we want the CA to be in ESTABLISHED state.
            // if the decryptMessage() call fails, we CryptoAuth_reset() it back.
            wrapper->nextNonce += 3;

            if (decryptMessage(wrapper, nonce, received, secret)) {
                cryptoAuthDebug0(wrapper, "Final handshake step succeeded");
                Bits_memcpyConst(wrapper->sharedSecret, secret, 32);

                // Now we're in run mode, no more handshake packets will be accepted
                Bits_memset(wrapper->ourTempPrivKey, 0, 32);
                Bits_memset(wrapper->ourTempPubKey, 0, 32);
                Bits_memset(wrapper->herTempPubKey, 0, 32);
                wrapper->established = true;

                return callReceivedMessage(wrapper, received);
            }
            CryptoAuth_reset(&wrapper->externalInterface);
            cryptoAuthDebug0(wrapper, "DROP Final handshake step failed");
            return Error_UNDELIVERABLE;
        }

        Message_shift(received, 4, NULL);
        return decryptHandshake(wrapper, nonce, received, header);

    } else if (nonce > 3 && nonce != UINT32_MAX) {
        Assert_ifParanoid(!Bits_isZero(wrapper->sharedSecret, 32));
        if (decryptMessage(wrapper, nonce, received, wrapper->sharedSecret)) {
            return callReceivedMessage(wrapper, received);
        } else {
            cryptoAuthDebug0(wrapper, "DROP Failed to decrypt message");
            return Error_UNDELIVERABLE;
        }
    } else if (nonce < 2) {
        cryptoAuthDebug(wrapper, "hello packet during established session nonce=[%d]", nonce);
        Message_shift(received, 4, NULL);
        return decryptHandshake(wrapper, nonce, received, header);
    } else {
        // setup keys are already zeroed, not much we can do here.
        cryptoAuthDebug(wrapper, "DROP key packet during established session nonce=[%d]", nonce);
        return Error_UNDELIVERABLE;
    }
    Assert_true(0);
}
Example #10
0
static uint8_t decryptHandshake(struct CryptoAuth_Wrapper* wrapper,
                                const uint32_t nonce,
                                struct Message* message,
                                union Headers_CryptoAuth* header)
{
    if (message->length < Headers_CryptoAuth_SIZE) {
        cryptoAuthDebug0(wrapper, "DROP runt");
        return Error_UNDERSIZE_MESSAGE;
    }

    // handshake
    // nextNonce 0: recieving hello.
    // nextNonce 1: recieving key, we sent hello.
    // nextNonce 2: recieving first data packet or duplicate hello.
    // nextNonce 3: recieving first data packet.
    // nextNonce >3: handshake complete

    if (knowHerKey(wrapper)) {
        if (Bits_memcmp(wrapper->herPerminentPubKey, header->handshake.publicKey, 32)) {
            cryptoAuthDebug0(wrapper, "DROP a packet with different public key than this session");
            return Error_AUTHENTICATION;
        }
    } else if (!Bits_isZero(wrapper->herIp6, 16)) {
        uint8_t calculatedIp6[16];
        AddressCalc_addressForPublicKey(calculatedIp6, header->handshake.publicKey);
        if (Bits_memcmp(wrapper->herIp6, calculatedIp6, 16)) {
            cryptoAuthDebug0(wrapper, "DROP packet with public key not matching ip6 for session");
            return Error_AUTHENTICATION;
        }
    }

    if (wrapper->nextNonce < 2 && nonce == UINT32_MAX && !wrapper->requireAuth) {
        // Reset without knowing key is allowed until state reaches 2.
        // this is because we don't know that the other end knows our key until we
        // have received a valid packet from them.
        // We can't allow the upper layer to see this message because it's not authenticated.
        if (!knowHerKey(wrapper)) {
            Bits_memcpyConst(wrapper->herPerminentPubKey, header->handshake.publicKey, 32);
        }
        Message_shift(message, -Headers_CryptoAuth_SIZE, NULL);
        message->length = 0;
        reset(wrapper);
        wrapper->user = NULL;
        cryptoAuthDebug0(wrapper, "Got a connect-to-me message, sending a hello");
        // Send an empty response (to initiate the connection).
        encryptHandshake(message, wrapper, 1);
        return Error_NONE;
    }

    String* user = NULL;
    uint8_t passwordHashStore[32];
    uint8_t* passwordHash = tryAuth(header, passwordHashStore, wrapper, &user);
    if (wrapper->requireAuth && !user) {
        cryptoAuthDebug0(wrapper, "DROP message because auth was not given");
        return Error_AUTHENTICATION;
    }
    if (passwordHash == NULL && header->handshake.auth.challenge.type != 0) {
        cryptoAuthDebug0(wrapper, "DROP message with unrecognized authenticator");
        return Error_AUTHENTICATION;
    }

    // What the nextNonce will become if this packet is valid.
    uint32_t nextNonce;

    // The secret for decrypting this message.
    uint8_t sharedSecret[32];

    uint8_t* herPermKey = NULL;
    if (nonce < 2) {
        if (nonce == 0) {
            cryptoAuthDebug(wrapper, "Received a hello packet, using auth: %d",
                            (passwordHash != NULL));
        } else {
            cryptoAuthDebug0(wrapper, "Received a repeat hello packet");
        }

        // Decrypt message with perminent keys.
        if (!knowHerKey(wrapper) || wrapper->nextNonce == 0) {
            herPermKey = header->handshake.publicKey;
            #ifdef Log_DEBUG
                if (Bits_isZero(header->handshake.publicKey, 32)) {
                    cryptoAuthDebug0(wrapper, "Node sent public key of ZERO!");
                }
            #endif
        } else {
            herPermKey = wrapper->herPerminentPubKey;
            if (Bits_memcmp(header->handshake.publicKey, herPermKey, 32)) {
                cryptoAuthDebug0(wrapper, "DROP packet contains different perminent key");
                return Error_AUTHENTICATION;
            }
        }

        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        herPermKey,
                        passwordHash,
                        wrapper->context->logger);
        nextNonce = 2;
    } else {
        if (nonce == 2) {
            cryptoAuthDebug0(wrapper, "Received a key packet");
        } else if (nonce == 3) {
            cryptoAuthDebug0(wrapper, "Received a repeat key packet");
        } else {
            cryptoAuthDebug(wrapper, "Received a packet of unknown type! nonce=%u", nonce);
        }
        if (Bits_memcmp(header->handshake.publicKey, wrapper->herPerminentPubKey, 32)) {
            cryptoAuthDebug0(wrapper, "DROP packet contains different perminent key");
            return Error_AUTHENTICATION;
        }
        if (!wrapper->isInitiator) {
            cryptoAuthDebug0(wrapper, "DROP a stray key packet");
            return Error_AUTHENTICATION;
        }
        // We sent the hello, this is a key
        getSharedSecret(sharedSecret,
                        wrapper->ourTempPrivKey,
                        wrapper->herPerminentPubKey,
                        passwordHash,
                        wrapper->context->logger);
        nextNonce = 4;
    }

    // Shift it on top of the authenticator before the encrypted public key
    Message_shift(message, 48 - Headers_CryptoAuth_SIZE, NULL);

    #ifdef Log_KEYS
        uint8_t sharedSecretHex[65];
        printHexKey(sharedSecretHex, sharedSecret);
        uint8_t nonceHex[49];
        Hex_encode(nonceHex, 49, header->handshake.nonce, 24);
        uint8_t cipherHex[65];
        printHexKey(cipherHex, message->bytes);
        Log_keys(wrapper->context->logger,
                  "Decrypting message with:\n"
                  "    nonce: %s\n"
                  "   secret: %s\n"
                  "   cipher: %s\n",
                  nonceHex, sharedSecretHex, cipherHex);
    #endif

    // Decrypt her temp public key and the message.
    if (decryptRndNonce(header->handshake.nonce, message, sharedSecret) != 0) {
        // just in case
        Bits_memset(header, 0, Headers_CryptoAuth_SIZE);
        cryptoAuthDebug(wrapper, "DROP message with nonce [%d], decryption failed", nonce);
        return Error_AUTHENTICATION;
    }

    Assert_ifParanoid(!Bits_isZero(header->handshake.encryptedTempKey, 32));

    #ifdef Log_KEYS
        uint8_t tempKeyHex[65];
        Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32);
        Log_keys(wrapper->context->logger,
                  "Unwrapping temp public key:\n"
                  "    %s\n",
                  tempKeyHex);
    #endif

    Message_shift(message, -32, NULL);

    // Post-decryption checking
    if (nonce == 0) {
        // A new hello packet
        if (!Bits_memcmp(wrapper->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
            // possible replay attack or duped packet
            cryptoAuthDebug0(wrapper, "DROP dupe hello packet with same temp key");
            return Error_AUTHENTICATION;
        }
    } else if (nonce == 2 && wrapper->nextNonce >= 4) {
        // we accept a new key packet and let it change the session since the other end might have
        // killed off the session while it was in the midst of setting up.
        if (!Bits_memcmp(wrapper->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
            Assert_true(!Bits_isZero(wrapper->herTempPubKey, 32));
            cryptoAuthDebug0(wrapper, "DROP dupe key packet with same temp key");
            return Error_AUTHENTICATION;
        }

    } else if (nonce == 3 && wrapper->nextNonce >= 4) {
        // Got a repeat key packet, make sure the temp key is the same as the one we know.
        if (Bits_memcmp(wrapper->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
            Assert_true(!Bits_isZero(wrapper->herTempPubKey, 32));
            cryptoAuthDebug0(wrapper, "DROP repeat key packet with different temp key");
            return Error_AUTHENTICATION;
        }
    }

    // If Alice sent a hello packet then Bob sent a hello packet and they crossed on the wire,
    // somebody has to yield and the other has to stand firm otherwise they will either deadlock
    // each believing their hello packet is superior or they will livelock, each switching to the
    // other's session and never synchronizing.
    // In this event whoever has the lower permanent public key wins.

    // If we receive a (possibly repeat) key packet
    if (nextNonce == 4) {
        if (wrapper->nextNonce <= 4) {
            // and have not yet begun sending "run" data
            Assert_true(wrapper->nextNonce <= nextNonce);
            wrapper->nextNonce = nextNonce;

            wrapper->user = user;
            Bits_memcpyConst(wrapper->herTempPubKey, header->handshake.encryptedTempKey, 32);
        } else {
            // It's a (possibly repeat) key packet and we have begun sending run data.
            // We will change the shared secret to the one specified in the new key packet but
            // intentionally avoid de-incrementing the nonce just in case
            getSharedSecret(wrapper->sharedSecret,
                            wrapper->ourTempPrivKey,
                            header->handshake.encryptedTempKey,
                            NULL,
                            wrapper->context->logger);
            cryptoAuthDebug0(wrapper, "New key packet but we are already sending data");
        }

    } else if (nextNonce == 2 && (!wrapper->isInitiator || wrapper->established)) {
        // This is a hello packet and we are either in ESTABLISHED state or we are
        // not the initiator of the connection.
        // If the case is that we are in ESTABLISHED state, the other side tore down the session
        // and we have not so lets tear it down.
        // If we are not in ESTABLISHED state then we don't allow resetting of the session unless
        // they are the sender of the hello packet or their permanent public key is lower.
        // this is a tie-breaker in case hello packets cross on the wire.
        if (wrapper->established) {
            reset(wrapper);
        }
        // We got a (possibly repeat) hello packet and we have not sent any hello packet,
        // new session.
        if (wrapper->nextNonce == 3 && nextNonce == 2) {
            // We sent a key packet so the next packet is a repeat key but we got another hello
            // We'll just keep steaming along sending repeat key packets
            nextNonce = 3;
        }

        Assert_true(wrapper->nextNonce <= nextNonce);
        wrapper->nextNonce = nextNonce;
        wrapper->user = user;
        Bits_memcpyConst(wrapper->herTempPubKey, header->handshake.encryptedTempKey, 32);

    } else if (nextNonce == 2
        && Bits_memcmp(header->handshake.publicKey, wrapper->context->pub.publicKey, 32) < 0)
    {
        // It's a hello and we are the initiator but their permant public key is numerically lower
        // than ours, this is so that in the event of two hello packets crossing on the wire, the
        // nodes will agree on who is the initiator.
        cryptoAuthDebug0(wrapper, "Incoming hello from node with lower key, resetting");
        reset(wrapper);

        Assert_true(wrapper->nextNonce <= nextNonce);
        wrapper->nextNonce = nextNonce;
        wrapper->user = user;
        Bits_memcpyConst(wrapper->herTempPubKey, header->handshake.encryptedTempKey, 32);

    } else {
        cryptoAuthDebug0(wrapper, "Incoming hello from node with higher key, not resetting");
    }

    if (herPermKey && herPermKey != wrapper->herPerminentPubKey) {
        Bits_memcpyConst(wrapper->herPerminentPubKey, herPermKey, 32);
    }

    // If this is a handshake which was initiated in reverse because we
    // didn't know the other node's key, now send what we were going to send.

    if (wrapper->bufferedMessage) {
        // This can only happen when we have received a (maybe repeat) hello packet.
        Assert_true(wrapper->nextNonce == 2);

        struct Message* bm = wrapper->bufferedMessage;
        wrapper->bufferedMessage = NULL;
        cryptoAuthDebug0(wrapper, "Sending buffered message");
        sendMessage(bm, &wrapper->externalInterface);
        Allocator_free(bm->alloc);
    }

    if (message->length == 0 && Headers_isSetupPacket(&header->handshake.auth)) {
        return Error_NONE;
    }

    Bits_memset(&wrapper->replayProtector, 0, sizeof(struct ReplayProtector));

    setRequiredPadding(wrapper);
    return callReceivedMessage(wrapper, message);
}
Example #11
0
static uint8_t encryptHandshake(struct Message* message,
                                struct CryptoAuth_Wrapper* wrapper,
                                int setupMessage)
{
    Message_shift(message, sizeof(union Headers_CryptoAuth), NULL);

    union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) message->bytes;

    // garbage the auth challenge and set the nonce which follows it
    Random_bytes(wrapper->context->rand, (uint8_t*) &header->handshake.auth,
                 sizeof(union Headers_AuthChallenge) + 24);

    // set the permanent key
    Bits_memcpyConst(&header->handshake.publicKey, wrapper->context->pub.publicKey, 32);

    if (!knowHerKey(wrapper)) {
        return genReverseHandshake(message, wrapper, header);
    } else if (!Bits_isZero(wrapper->herIp6, 16)) {
        // If someone starts a CA session and then discovers the key later and memcpy's it into the
        // result of getHerPublicKey() then we want to make sure they didn't memcpy in an invalid
        // key.
        uint8_t calculatedIp6[16];
        AddressCalc_addressForPublicKey(calculatedIp6, wrapper->herPerminentPubKey);
        Assert_true(!Bits_memcmp(wrapper->herIp6, calculatedIp6, 16));
    }

    if (wrapper->bufferedMessage) {
        // We wanted to send a message but we didn't know the peer's key so we buffered it
        // and sent a connectToMe.
        // Now we just discovered their key and we're sending a hello packet.
        // Lets send 2 hello packets instead and on one will attach our buffered message.

        // This can never happen when the machine is beyond the first hello packet because
        // it should have been sent either by this or in the recipet of a hello packet from
        // the other node.
        Assert_true(wrapper->nextNonce == 0);

        struct Message* bm = wrapper->bufferedMessage;
        wrapper->bufferedMessage = NULL;
        cryptoAuthDebug0(wrapper, "Sending buffered message");
        sendMessage(bm, &wrapper->externalInterface);
        Allocator_free(bm->alloc);
    }

    // Password auth
    uint8_t* passwordHash = NULL;
    struct CryptoAuth_Auth auth;
    if (wrapper->password != NULL) {
        passwordHash = hashPassword(&auth, wrapper->password, wrapper->authType);
        Bits_memcpyConst(header->handshake.auth.bytes,
                         &auth.challenge,
                         sizeof(union Headers_AuthChallenge));
    }
    header->handshake.auth.challenge.type = wrapper->authType;

    // Packet authentication option is deprecated, it must always be enabled.
    Headers_setPacketAuthRequired(&header->handshake.auth, 1);

    // This is a special packet which the user should never see.
    Headers_setSetupPacket(&header->handshake.auth, setupMessage);

    // Set the session state
    uint32_t sessionState_be = Endian_hostToBigEndian32(wrapper->nextNonce);
    header->nonce = sessionState_be;

    if (wrapper->nextNonce == 0 || wrapper->nextNonce == 2) {
        // If we're sending a hello or a key
        // Here we make up a temp keypair
        Random_bytes(wrapper->context->rand, wrapper->ourTempPrivKey, 32);
        crypto_scalarmult_curve25519_base(wrapper->ourTempPubKey, wrapper->ourTempPrivKey);

        #ifdef Log_KEYS
            uint8_t tempPrivateKeyHex[65];
            Hex_encode(tempPrivateKeyHex, 65, wrapper->ourTempPrivKey, 32);
            uint8_t tempPubKeyHex[65];
            Hex_encode(tempPubKeyHex, 65, header->handshake.encryptedTempKey, 32);
            Log_keys(wrapper->context->logger, "Generating temporary keypair\n"
                                                "    myTempPrivateKey=%s\n"
                                                "     myTempPublicKey=%s\n",
                      tempPrivateKeyHex, tempPubKeyHex);
        #endif
    }

    Bits_memcpyConst(header->handshake.encryptedTempKey, wrapper->ourTempPubKey, 32);

    #ifdef Log_KEYS
        uint8_t tempKeyHex[65];
        Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32);
        Log_keys(wrapper->context->logger,
                  "Wrapping temp public key:\n"
                  "    %s\n",
                  tempKeyHex);
    #endif

    cryptoAuthDebug(wrapper, "Sending %s%s packet",
                    ((wrapper->nextNonce & 1) ? "repeat " : ""),
                    ((wrapper->nextNonce < 2) ? "hello" : "key"));

    uint8_t sharedSecret[32];
    if (wrapper->nextNonce < 2) {
        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        wrapper->herPerminentPubKey,
                        passwordHash,
                        wrapper->context->logger);

        wrapper->isInitiator = true;

        Assert_true(wrapper->nextNonce <= 1);
        wrapper->nextNonce = 1;
    } else {
        // Handshake2
        // herTempPubKey was set by receiveMessage()
        Assert_ifParanoid(!Bits_isZero(wrapper->herTempPubKey, 32));
        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        wrapper->herTempPubKey,
                        passwordHash,
                        wrapper->context->logger);

        Assert_true(wrapper->nextNonce <= 3);
        wrapper->nextNonce = 3;

        #ifdef Log_KEYS
            uint8_t tempKeyHex[65];
            Hex_encode(tempKeyHex, 65, wrapper->herTempPubKey, 32);
            Log_keys(wrapper->context->logger,
                      "Using their temp public key:\n"
                      "    %s\n",
                      tempKeyHex);
        #endif
    }

    // Shift message over the encryptedTempKey field.
    Message_shift(message, 32 - Headers_CryptoAuth_SIZE, NULL);

    encryptRndNonce(header->handshake.nonce, message, sharedSecret);

    #ifdef Log_KEYS
        uint8_t sharedSecretHex[65];
        printHexKey(sharedSecretHex, sharedSecret);
        uint8_t nonceHex[49];
        Hex_encode(nonceHex, 49, header->handshake.nonce, 24);
        uint8_t cipherHex[65];
        printHexKey(cipherHex, message->bytes);
        Log_keys(wrapper->context->logger,
                  "Encrypting message with:\n"
                  "    nonce: %s\n"
                  "   secret: %s\n"
                  "   cipher: %s\n",
                  nonceHex, sharedSecretHex, cipherHex);
    #endif

    // Shift it back -- encryptRndNonce adds 16 bytes of authenticator.
    Message_shift(message, Headers_CryptoAuth_SIZE - 32 - 16, NULL);

    return wrapper->wrappedInterface->sendMessage(message, wrapper->wrappedInterface);
}
Example #12
0
static uint8_t decryptHandshake(struct CryptoAuth_Wrapper* wrapper,
                                const uint32_t nonce,
                                struct Message* message,
                                union Headers_CryptoAuth* header)
{
    if (message->length < Headers_CryptoAuth_SIZE) {
        cryptoAuthDebug0(wrapper, "Dropped runt packet");
        return Error_UNDERSIZE_MESSAGE;
    }

    // handshake
    // nextNonce 0: recieving hello.
    // nextNonce 1: recieving key, we sent hello.
    // nextNonce 2: recieving first data packet or duplicate hello.
    // nextNonce 3: recieving first data packet.
    // nextNonce >3: handshake complete

    if (wrapper->nextNonce < 2 && nonce == UINT32_MAX && !wrapper->requireAuth) {
        // Reset without knowing key is allowed until state reaches 2.
        // this is because we don't know that the other end knows our key until we
        // have received a valid packet from them.
        // We can't allow the upper layer to see this message because it's not authenticated.
        if (!knowHerKey(wrapper)) {
            Bits_memcpyConst(wrapper->herPerminentPubKey, header->handshake.publicKey, 32);
        }
        Message_shift(message, -Headers_CryptoAuth_SIZE);
        message->length = 0;
        wrapper->nextNonce = 0;
        wrapper->user = NULL;
        cryptoAuthDebug0(wrapper, "Got a connect-to-me message, sending a hello");
        // Send an empty response (to initiate the connection).
        encryptHandshake(message, wrapper);
        return Error_NONE;
    }

    void* user = NULL;
    uint8_t passwordHashStore[32];
    uint8_t* passwordHash = tryAuth(header, passwordHashStore, wrapper, &user);
    if (wrapper->requireAuth && !user) {
        cryptoAuthDebug0(wrapper, "Dropping message because auth was not given");
        return Error_AUTHENTICATION;
    }
    if (passwordHash == NULL && header->handshake.auth.challenge.type != 0) {
        cryptoAuthDebug0(wrapper,
            "Dropping message because it contans an authenticator which is unrecognized");
        return Error_AUTHENTICATION;
    }

    // What the nextNonce will become if this packet is valid.
    uint32_t nextNonce;

    // The secret for decrypting this message.
    uint8_t sharedSecret[32];

    uint8_t* herPermKey = NULL;
    if (nonce < 2) {
        if (nonce == 0) {
            cryptoAuthDebug(wrapper, "Received a hello packet, using auth: %d",
                            (passwordHash != NULL));
        } else {
            cryptoAuthDebug0(wrapper, "Received a repeat hello packet");
        }

        // Decrypt message with perminent keys.
        if (!knowHerKey(wrapper) || wrapper->nextNonce == 0) {
            herPermKey = header->handshake.publicKey;
            #ifdef Log_DEBUG
                if (Bits_isZero(header->handshake.publicKey, 32)) {
                    cryptoAuthDebug0(wrapper, "Node sent public key of ZERO!");
                }
            #endif
        } else {
            herPermKey = wrapper->herPerminentPubKey;
            if (Bits_memcmp(header->handshake.publicKey, herPermKey, 32)) {
                cryptoAuthDebug0(wrapper, "Packet contains different perminent key");
                return Error_AUTHENTICATION;
            }
        }

        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        herPermKey,
                        passwordHash,
                        wrapper->context->logger);
        nextNonce = 2;
    } else {
        if (nonce == 2) {
            cryptoAuthDebug0(wrapper, "Received a key packet");
        } else if (nonce == 3) {
            cryptoAuthDebug0(wrapper, "Received a repeat key packet");
        } else {
            cryptoAuthDebug(wrapper, "Received a packet of unknown type! nonce=%u", nonce);
        }
        if (Bits_memcmp(header->handshake.publicKey, wrapper->herPerminentPubKey, 32)) {
            cryptoAuthDebug0(wrapper, "Packet contains different perminent key");
            return Error_AUTHENTICATION;
        }
        // We sent the hello, this is a key
        getSharedSecret(sharedSecret,
                        wrapper->secret,
                        wrapper->herPerminentPubKey,
                        passwordHash,
                        wrapper->context->logger);
        nextNonce = 4;
    }

    // Shift it on top of the authenticator before the encrypted public key
    Message_shift(message, 48 - Headers_CryptoAuth_SIZE);

    #ifdef Log_KEYS
        uint8_t sharedSecretHex[65];
        printHexKey(sharedSecretHex, sharedSecret);
        uint8_t nonceHex[49];
        Hex_encode(nonceHex, 49, header->handshake.nonce, 24);
        uint8_t cipherHex[65];
        printHexKey(cipherHex, message->bytes);
        Log_keys(wrapper->context->logger,
                  "Decrypting message with:\n"
                  "    nonce: %s\n"
                  "   secret: %s\n"
                  "   cipher: %s\n",
                  nonceHex, sharedSecretHex, cipherHex);
    #endif

    // Decrypt her temp public key and the message.
    if (decryptRndNonce(header->handshake.nonce, message, sharedSecret) != 0) {
        // just in case
        Bits_memset(header, 0, Headers_CryptoAuth_SIZE);
        cryptoAuthDebug0(wrapper, "Dropped message because authenticated decryption failed");
        return Error_AUTHENTICATION;
    }

    wrapper->user = user;
    Bits_memcpyConst(wrapper->tempKey, header->handshake.encryptedTempKey, 32);

    #ifdef Log_DEBUG
        Assert_true(!Bits_isZero(header->handshake.encryptedTempKey, 32));
    #endif
    #ifdef Log_KEYS
        uint8_t tempKeyHex[65];
        Hex_encode(tempKeyHex, 65, wrapper->tempKey, 32);
        Log_keys(wrapper->context->logger,
                  "Unwrapping temp public key:\n"
                  "    %s\n",
                  tempKeyHex);
    #endif

    Message_shift(message, -32);
    wrapper->nextNonce = nextNonce;
    if (nextNonce == 2) {
        wrapper->isInitiator = false;
    }
    if (herPermKey && herPermKey != wrapper->herPerminentPubKey) {
        Bits_memcpyConst(wrapper->herPerminentPubKey, herPermKey, 32);
    }

    // If this is a handshake which was initiated in reverse because we
    // didn't know the other node's key, now send what we were going to send.
    if (wrapper->hasBufferedMessage && message->length == 0) {
        cryptoAuthDebug0(wrapper, "Sending buffered message");
        sendMessage(wrapper->bufferedMessage, &wrapper->externalInterface);
        wrapper->hasBufferedMessage = false;
        return Error_NONE;
    } else if (wrapper->hasBufferedMessage) {
        cryptoAuthDebug0(wrapper, "There is a buffered message");
    }

    Bits_memset(&wrapper->replayProtector, 0, sizeof(struct ReplayProtector));

    setRequiredPadding(wrapper);
    return callReceivedMessage(wrapper, message);
}
Example #13
0
static uint8_t encryptHandshake(struct Message* message, struct CryptoAuth_Wrapper* wrapper)
{
    Message_shift(message, sizeof(union Headers_CryptoAuth));

    union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) message->bytes;

    // garbage the auth field to frustrate DPI and set the nonce (next 24 bytes after the auth)
    Random_bytes(wrapper->context->rand,
                 (uint8_t*) &header->handshake.auth,
                 sizeof(union Headers_AuthChallenge) + 24);
    Bits_memcpyConst(&header->handshake.publicKey, wrapper->context->pub.publicKey, 32);

    if (!knowHerKey(wrapper)) {
        return genReverseHandshake(message, wrapper, header);
    }

    // Password auth
    uint8_t* passwordHash = NULL;
    struct CryptoAuth_Auth auth;
    if (wrapper->password != NULL) {
        passwordHash = hashPassword(&auth, wrapper->password, wrapper->authType);
        Bits_memcpyConst(header->handshake.auth.bytes,
                         &auth.challenge,
                         sizeof(union Headers_AuthChallenge));
    }
    header->handshake.auth.challenge.type = wrapper->authType;

    Headers_setPacketAuthRequired(&header->handshake.auth, wrapper->authenticatePackets);

    // set the session state
    uint32_t sessionState_be = Endian_hostToBigEndian32(wrapper->nextNonce);
    header->nonce = sessionState_be;

    if (wrapper->nextNonce == 0 || wrapper->nextNonce == 2) {
        // If we're sending a hello or a key
        Random_bytes(wrapper->context->rand, wrapper->secret, 32);
        crypto_scalarmult_curve25519_base(header->handshake.encryptedTempKey,  wrapper->secret);

        #ifdef Log_KEYS
            uint8_t tempPrivateKeyHex[65];
            Hex_encode(tempPrivateKeyHex, 65, wrapper->secret, 32);
            uint8_t tempPubKeyHex[65];
            Hex_encode(tempPubKeyHex, 65, header->handshake.encryptedTempKey, 32);
            Log_keys(wrapper->context->logger, "Generating temporary keypair\n"
                                                "    myTempPrivateKey=%s\n"
                                                "     myTempPublicKey=%s\n",
                      tempPrivateKeyHex, tempPubKeyHex);
        #endif
        if (wrapper->nextNonce == 0) {
            Bits_memcpyConst(wrapper->tempKey, header->handshake.encryptedTempKey, 32);
        }
        #ifdef Log_DEBUG
            Assert_true(!Bits_isZero(header->handshake.encryptedTempKey, 32));
            Assert_true(!Bits_isZero(wrapper->secret, 32));
        #endif
    } else if (wrapper->nextNonce == 3) {
        // Dupe key
        // If nextNonce is 1 then we have our pubkey stored in wrapper->tempKey,
        // If nextNonce is 3 we need to recalculate it each time
        // because tempKey the final secret.
        crypto_scalarmult_curve25519_base(header->handshake.encryptedTempKey,
                                          wrapper->secret);
    } else {
        // Dupe hello
        // wrapper->nextNonce == 1
        // Our public key is cached in wrapper->tempKey so lets copy it out.
        Bits_memcpyConst(header->handshake.encryptedTempKey, wrapper->tempKey, 32);
    }
    #ifdef Log_KEYS
        uint8_t tempKeyHex[65];
        Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32);
        Log_keys(wrapper->context->logger,
                  "Wrapping temp public key:\n"
                  "    %s\n",
                  tempKeyHex);
    #endif

    uint8_t sharedSecret[32];
    if (wrapper->nextNonce < 2) {
        if (wrapper->nextNonce == 0) {
            cryptoAuthDebug0(wrapper, "Sending hello packet");
        } else {
            cryptoAuthDebug0(wrapper, "Sending repeat hello packet");
        }
        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        wrapper->herPerminentPubKey,
                        passwordHash,
                        wrapper->context->logger);

        wrapper->isInitiator = true;
        wrapper->nextNonce = 1;
    } else {
        if (wrapper->nextNonce == 2) {
            cryptoAuthDebug0(wrapper, "Sending key packet");
        } else {
            cryptoAuthDebug0(wrapper, "Sending repeat key packet");
        }
        // Handshake2 wrapper->tempKey holds her public temp key.
        // it was put there by receiveMessage()
        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        wrapper->tempKey,
                        passwordHash,
                        wrapper->context->logger);
        wrapper->nextNonce = 3;

        #ifdef Log_KEYS
            uint8_t tempKeyHex[65];
            Hex_encode(tempKeyHex, 65, wrapper->tempKey, 32);
            Log_keys(wrapper->context->logger,
                      "Using their temp public key:\n"
                      "    %s\n",
                      tempKeyHex);
        #endif
    }

    // Shift message over the encryptedTempKey field.
    Message_shift(message, 32 - Headers_CryptoAuth_SIZE);

    encryptRndNonce(header->handshake.nonce, message, sharedSecret);

    #ifdef Log_KEYS
        uint8_t sharedSecretHex[65];
        printHexKey(sharedSecretHex, sharedSecret);
        uint8_t nonceHex[49];
        Hex_encode(nonceHex, 49, header->handshake.nonce, 24);
        uint8_t cipherHex[65];
        printHexKey(cipherHex, message->bytes);
        Log_keys(wrapper->context->logger,
                  "Encrypting message with:\n"
                  "    nonce: %s\n"
                  "   secret: %s\n"
                  "   cipher: %s\n",
                  nonceHex, sharedSecretHex, cipherHex);
    #endif
    #ifdef Log_DEBUG
        Assert_true(!Bits_isZero(header->handshake.encryptedTempKey, 32));
    #endif

    // Shift it back -- encryptRndNonce adds 16 bytes of authenticator.
    Message_shift(message, Headers_CryptoAuth_SIZE - 32 - 16);

    return wrapper->wrappedInterface->sendMessage(message, wrapper->wrappedInterface);
}
Example #14
0
/** @return 0 on success, -1 otherwise. */
int CryptoAuth_decrypt(struct CryptoAuth_Session* sessionPub, struct Message* msg)
{
    struct CryptoAuth_Session_pvt* session =
        Identity_check((struct CryptoAuth_Session_pvt*) sessionPub);
    union CryptoHeader* header = (union CryptoHeader*) msg->bytes;

    if (msg->length < 20) {
        cryptoAuthDebug0(session, "DROP runt");
        return -1;
    }
    Assert_true(msg->padding >= 12 || "need at least 12 bytes of padding in incoming message");
    Assert_true(!((uintptr_t)msg->bytes % 4) || !"alignment fault");
    Assert_true(!(msg->capacity % 4) || !"length fault");

    Message_shift(msg, -4, NULL);

    uint32_t nonce = Endian_bigEndianToHost32(header->nonce);

    if (!session->established) {
        if (nonce > 3) {
            if (session->nextNonce < 3) {
                // This is impossible because we have not exchanged hello and key messages.
                cryptoAuthDebug0(session, "DROP Received a run message to an un-setup session");
                return -1;
            }
            cryptoAuthDebug(session, "Trying final handshake step, nonce=%u\n", nonce);
            uint8_t secret[32];
            Assert_ifParanoid(!Bits_isZero(session->ourTempPrivKey, 32));
            Assert_ifParanoid(!Bits_isZero(session->herTempPubKey, 32));
            getSharedSecret(secret,
                            session->ourTempPrivKey,
                            session->herTempPubKey,
                            NULL,
                            session->context->logger);

            if (decryptMessage(session, nonce, msg, secret)) {
                cryptoAuthDebug0(session, "Final handshake step succeeded");
                Bits_memcpyConst(session->sharedSecret, secret, 32);

                // Now we're in run mode, no more handshake packets will be accepted
                Bits_memset(session->ourTempPrivKey, 0, 32);
                Bits_memset(session->ourTempPubKey, 0, 32);
                Bits_memset(session->herTempPubKey, 0, 32);
                session->established = true;
                session->nextNonce += 3;
                updateTime(session, msg);
                return 0;
            }
            cryptoAuthDebug0(session, "DROP Final handshake step failed");
            return -1;
        }

        Message_shift(msg, 4, NULL);
        return decryptHandshake(session, nonce, msg, header);

    } else if (nonce > 3) {
        Assert_ifParanoid(!Bits_isZero(session->sharedSecret, 32));
        if (decryptMessage(session, nonce, msg, session->sharedSecret)) {
            updateTime(session, msg);
            return 0;
        } else {
            cryptoAuthDebug0(session, "DROP Failed to decrypt message");
            return -1;
        }
    } else if (nonce < 2) {
        cryptoAuthDebug(session, "hello packet during established session nonce=[%d]", nonce);
        Message_shift(msg, 4, NULL);
        return decryptHandshake(session, nonce, msg, header);
    } else {
        // setup keys are already zeroed, not much we can do here.
        cryptoAuthDebug(session, "DROP key packet during established session nonce=[%d]", nonce);
        return -1;
    }
    Assert_true(0);
}
Example #15
0
static Gcc_USE_RET int decryptHandshake(struct CryptoAuth_Session_pvt* session,
                                        const uint32_t nonce,
                                        struct Message* message,
                                        union CryptoHeader* header)
{
    if (message->length < CryptoHeader_SIZE) {
        cryptoAuthDebug0(session, "DROP runt");
        return -1;
    }

    // handshake
    // nextNonce 0: recieving hello.
    // nextNonce 1: recieving key, we sent hello.
    // nextNonce 2: recieving first data packet or duplicate hello.
    // nextNonce 3: recieving first data packet.
    // nextNonce >3: handshake complete

    if (knowHerKey(session)) {
        if (Bits_memcmp(session->pub.herPublicKey, header->handshake.publicKey, 32)) {
            cryptoAuthDebug0(session, "DROP a packet with different public key than this session");
            return -1;
        }
    } else if (Bits_isZero(session->pub.herIp6, 16)) {
        // ok fallthrough
    } else if (!ip6MatchesKey(session->pub.herIp6, header->handshake.publicKey)) {
        cryptoAuthDebug0(session, "DROP packet with public key not matching ip6 for session");
        return -1;
    }

    struct CryptoAuth_User* userObj = getAuth(&header->handshake.auth, session->context);
    uint8_t* restrictedToip6 = NULL;
    uint8_t* passwordHash = NULL;
    if (userObj) {
        passwordHash = userObj->secret;
        if (userObj->restrictedToip6[0]) {
            restrictedToip6 = userObj->restrictedToip6;
            if (!ip6MatchesKey(restrictedToip6, header->handshake.publicKey)) {
                cryptoAuthDebug0(session, "DROP packet with key not matching restrictedToip6");
                return -1;
            }
        }
    }
    if (session->requireAuth && !userObj) {
        cryptoAuthDebug0(session, "DROP message because auth was not given");
        return -1;
    }
    if (!userObj && header->handshake.auth.challenge.type != 0) {
        cryptoAuthDebug0(session, "DROP message with unrecognized authenticator");
        return -1;
    }
    // What the nextNonce will become if this packet is valid.
    uint32_t nextNonce;

    // The secret for decrypting this message.
    uint8_t sharedSecret[32];

    uint8_t* herPermKey = session->pub.herPublicKey;
    if (nonce < 2) {
        if (nonce == 0) {
            cryptoAuthDebug(session, "Received a hello packet, using auth: %d",
                            (userObj != NULL));
        } else {
            cryptoAuthDebug0(session, "Received a repeat hello packet");
        }

        // Decrypt message with perminent keys.
        if (!knowHerKey(session) || session->nextNonce == 0) {
            herPermKey = header->handshake.publicKey;
            if (Defined(Log_DEBUG) && Bits_isZero(header->handshake.publicKey, 32)) {
                cryptoAuthDebug0(session, "DROP Node sent public key of ZERO!");
                // This is strictly informational, we will not alter the execution path for it.
            }
        }

        getSharedSecret(sharedSecret,
                        session->context->privateKey,
                        herPermKey,
                        passwordHash,
                        session->context->logger);
        nextNonce = 2;
    } else {
        if (nonce == 2) {
            cryptoAuthDebug0(session, "Received a key packet");
        } else {
            Assert_true(nonce == 3);
            cryptoAuthDebug0(session, "Received a repeat key packet");
        }
        if (Bits_memcmp(header->handshake.publicKey, session->pub.herPublicKey, 32)) {
            cryptoAuthDebug0(session, "DROP packet contains different perminent key");
            return -1;
        }
        if (!session->isInitiator) {
            cryptoAuthDebug0(session, "DROP a stray key packet");
            return -1;
        }
        // We sent the hello, this is a key
        getSharedSecret(sharedSecret,
                        session->ourTempPrivKey,
                        session->pub.herPublicKey,
                        passwordHash,
                        session->context->logger);
        nextNonce = 4;
    }

    // Shift it on top of the authenticator before the encrypted public key
    Message_shift(message, 48 - CryptoHeader_SIZE, NULL);

    if (Defined(Log_KEYS)) {
        uint8_t sharedSecretHex[65];
        printHexKey(sharedSecretHex, sharedSecret);
        uint8_t nonceHex[49];
        Hex_encode(nonceHex, 49, header->handshake.nonce, 24);
        uint8_t cipherHex[65];
        printHexKey(cipherHex, message->bytes);
        Log_keys(session->context->logger,
                  "Decrypting message with:\n"
                  "    nonce: %s\n"
                  "   secret: %s\n"
                  "   cipher: %s\n",
                  nonceHex, sharedSecretHex, cipherHex);
    }

    // Decrypt her temp public key and the message.
    if (decryptRndNonce(header->handshake.nonce, message, sharedSecret)) {
        // just in case
        Bits_memset(header, 0, CryptoHeader_SIZE);
        cryptoAuthDebug(session, "DROP message with nonce [%d], decryption failed", nonce);
        return -1;
    }

    if (Bits_isZero(header->handshake.encryptedTempKey, 32)) {
        // we need to reject 0 public keys outright because they will be confused with "unknown"
        cryptoAuthDebug0(session, "DROP message with zero as temp public key");
        return -1;
    }

    if (Defined(Log_KEYS)) {
        uint8_t tempKeyHex[65];
        Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32);
        Log_keys(session->context->logger,
                  "Unwrapping temp public key:\n"
                  "    %s\n",
                  tempKeyHex);
    }

    Message_shift(message, -32, NULL);

    // Post-decryption checking
    if (nonce == 0) {
        // A new hello packet
        if (!Bits_memcmp(session->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
            // possible replay attack or duped packet
            cryptoAuthDebug0(session, "DROP dupe hello packet with same temp key");
            return -1;
        }
    } else if (nonce == 2 && session->nextNonce >= 4) {
        // we accept a new key packet and let it change the session since the other end might have
        // killed off the session while it was in the midst of setting up.
        // This is NOT a repeat key packet because it's nonce is 2, not 3
        if (!Bits_memcmp(session->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
            Assert_true(!Bits_isZero(session->herTempPubKey, 32));
            cryptoAuthDebug0(session, "DROP dupe key packet with same temp key");
            return -1;
        }

    } else if (nonce == 3 && session->nextNonce >= 4) {
        // Got a repeat key packet, make sure the temp key is the same as the one we know.
        if (Bits_memcmp(session->herTempPubKey, header->handshake.encryptedTempKey, 32)) {
            Assert_true(!Bits_isZero(session->herTempPubKey, 32));
            cryptoAuthDebug0(session, "DROP repeat key packet with different temp key");
            return -1;
        }
    }

    // If Alice sent a hello packet then Bob sent a hello packet and they crossed on the wire,
    // somebody has to yield and the other has to stand firm otherwise they will either deadlock
    // each believing their hello packet is superior or they will livelock, each switching to the
    // other's session and never synchronizing.
    // In this event whoever has the lower permanent public key wins.

    // If we receive a (possibly repeat) key packet
    if (nextNonce == 4) {
        if (session->nextNonce <= 4) {
            // and have not yet begun sending "run" data
            Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);
        } else {
            // It's a (possibly repeat) key packet and we have begun sending run data.
            // We will change the shared secret to the one specified in the new key packet but
            // intentionally avoid de-incrementing the nonce just in case
            getSharedSecret(session->sharedSecret,
                            session->ourTempPrivKey,
                            header->handshake.encryptedTempKey,
                            NULL,
                            session->context->logger);
            nextNonce = session->nextNonce + 1;
            cryptoAuthDebug0(session, "New key packet but we are already sending data");
        }

    } else if (nextNonce != 2) {

        Assert_true(!"should never happen");

    } else if (!session->isInitiator || session->established) {
        // This is a hello packet and we are either in ESTABLISHED state or we are
        // not the initiator of the connection.
        // If the case is that we are in ESTABLISHED state, the other side tore down the session
        // and we have not so lets tear it down.
        // If we are not in ESTABLISHED state then we don't allow resetting of the session unless
        // they are the sender of the hello packet or their permanent public key is lower.
        // this is a tie-breaker in case hello packets cross on the wire.
        if (session->established) {
            cryptoAuthDebug0(session, "new hello during established session, resetting");
            reset(session);
        }
        // We got a (possibly repeat) hello packet and we have not sent any hello packet,
        // new session.
        if (session->nextNonce == 3) {
            // We sent a key packet so the next packet is a repeat key but we got another hello
            // We'll just keep steaming along sending repeat key packets
            nextNonce = 3;
        }

        Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);

    } else if (Bits_memcmp(header->handshake.publicKey, session->context->pub.publicKey, 32) < 0) {
        // It's a hello and we are the initiator but their permant public key is numerically lower
        // than ours, this is so that in the event of two hello packets crossing on the wire, the
        // nodes will agree on who is the initiator.
        cryptoAuthDebug0(session, "Incoming hello from node with lower key, resetting");
        reset(session);

        Bits_memcpyConst(session->herTempPubKey, header->handshake.encryptedTempKey, 32);

    } else {
        cryptoAuthDebug0(session, "DROP Incoming hello from node with higher key, not resetting");
        return -1;
    }

    if (herPermKey && herPermKey != session->pub.herPublicKey) {
        Bits_memcpyConst(session->pub.herPublicKey, herPermKey, 32);
    }
    if (restrictedToip6) {
        Bits_memcpyConst(session->pub.herIp6, restrictedToip6, 16);
    }

    // Nonces can never go backward and can only "not advance" if they're 0,1,2,3,4 session state.
    Assert_true(session->nextNonce < nextNonce ||
        (session->nextNonce <= 4 && nextNonce == session->nextNonce)
    );
    session->nextNonce = nextNonce;

    Bits_memset(&session->pub.replayProtector, 0, sizeof(struct ReplayProtector));

    return 0;
}
Example #16
0
static uint8_t encryptHandshake(struct Message* message,
                                struct CryptoAuth_Wrapper* wrapper,
                                int setupMessage)
{
    Message_shift(message, sizeof(union Headers_CryptoAuth));

    union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) message->bytes;

    // garbage the auth challenge and set the nonce which follows it
    Random_bytes(wrapper->context->rand, (uint8_t*) &header->handshake.auth,
                 sizeof(union Headers_AuthChallenge) + 24);

    // set the permanent key
    Bits_memcpyConst(&header->handshake.publicKey, wrapper->context->pub.publicKey, 32);

    if (!knowHerKey(wrapper)) {
        return genReverseHandshake(message, wrapper, header);
    }

    if (wrapper->bufferedMessage) {
        // We wanted to send a message but we didn't know the peer's key so we buffered it
        // and sent a connectToMe, this or it's reply was lost in the network.
        // Now we just discovered their key and we're sending a hello packet.
        // Lets send 2 hello packets instead and on one will attach our buffered message.

        // This can never happen when the machine is beyond the first hello packet because
        // it should have been sent either by this or in the recipet of a hello packet from
        // the other node.
        Assert_true(wrapper->nextNonce == 0);

        struct Message* bm = wrapper->bufferedMessage;
        wrapper->bufferedMessage = NULL;
        cryptoAuthDebug0(wrapper, "Sending buffered message");
        sendMessage(bm, &wrapper->externalInterface);
        Allocator_free(bm->alloc);
    }

    // Password auth
    uint8_t* passwordHash = NULL;
    struct CryptoAuth_Auth auth;
    if (wrapper->password != NULL) {
        passwordHash = hashPassword(&auth, wrapper->password, wrapper->authType);
        Bits_memcpyConst(header->handshake.auth.bytes,
                         &auth.challenge,
                         sizeof(union Headers_AuthChallenge));
    }
    header->handshake.auth.challenge.type = wrapper->authType;

    Headers_setPacketAuthRequired(&header->handshake.auth, wrapper->authenticatePackets);

    // This is a special packet which the user should never see.
    Headers_setSetupPacket(&header->handshake.auth, setupMessage);

    // Set the session state
    uint32_t sessionState_be = Endian_hostToBigEndian32(wrapper->nextNonce);
    header->nonce = sessionState_be;

    if (wrapper->nextNonce == 0 || wrapper->nextNonce == 2) {
        // If we're sending a hello or a key
        Random_bytes(wrapper->context->rand, wrapper->secret, 32);
        crypto_scalarmult_curve25519_base(header->handshake.encryptedTempKey,  wrapper->secret);

        #ifdef Log_KEYS
            uint8_t tempPrivateKeyHex[65];
            Hex_encode(tempPrivateKeyHex, 65, wrapper->secret, 32);
            uint8_t tempPubKeyHex[65];
            Hex_encode(tempPubKeyHex, 65, header->handshake.encryptedTempKey, 32);
            Log_keys(wrapper->context->logger, "Generating temporary keypair\n"
                                                "    myTempPrivateKey=%s\n"
                                                "     myTempPublicKey=%s\n",
                      tempPrivateKeyHex, tempPubKeyHex);
        #endif
        if (wrapper->nextNonce == 0) {
            Bits_memcpyConst(wrapper->tempKey, header->handshake.encryptedTempKey, 32);
        }
        #ifdef Log_DEBUG
            Assert_true(!Bits_isZero(header->handshake.encryptedTempKey, 32));
            Assert_true(!Bits_isZero(wrapper->secret, 32));
        #endif
    } else if (wrapper->nextNonce == 3) {
        // Dupe key
        // If nextNonce is 1 then we have our pubkey stored in wrapper->tempKey,
        // If nextNonce is 3 we need to recalculate it each time
        // because tempKey the final secret.
        crypto_scalarmult_curve25519_base(header->handshake.encryptedTempKey,
                                          wrapper->secret);
    } else {
        // Dupe hello
        // wrapper->nextNonce == 1
        // Our public key is cached in wrapper->tempKey so lets copy it out.
        Bits_memcpyConst(header->handshake.encryptedTempKey, wrapper->tempKey, 32);
    }
    #ifdef Log_KEYS
        uint8_t tempKeyHex[65];
        Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32);
        Log_keys(wrapper->context->logger,
                  "Wrapping temp public key:\n"
                  "    %s\n",
                  tempKeyHex);
    #endif

    cryptoAuthDebug(wrapper, "Sending %s%s packet",
                    ((wrapper->nextNonce & 1) ? "repeat " : ""),
                    ((wrapper->nextNonce < 2) ? "hello" : "key"));

    uint8_t sharedSecret[32];
    if (wrapper->nextNonce < 2) {
        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        wrapper->herPerminentPubKey,
                        passwordHash,
                        wrapper->context->logger);

        wrapper->isInitiator = true;
        wrapper->nextNonce = 1;
    } else {
        // Handshake2 wrapper->tempKey holds her public temp key.
        // it was put there by receiveMessage()
        getSharedSecret(sharedSecret,
                        wrapper->context->privateKey,
                        wrapper->tempKey,
                        passwordHash,
                        wrapper->context->logger);
        wrapper->nextNonce = 3;

        #ifdef Log_KEYS
            uint8_t tempKeyHex[65];
            Hex_encode(tempKeyHex, 65, wrapper->tempKey, 32);
            Log_keys(wrapper->context->logger,
                      "Using their temp public key:\n"
                      "    %s\n",
                      tempKeyHex);
        #endif
    }

    // Shift message over the encryptedTempKey field.
    Message_shift(message, 32 - Headers_CryptoAuth_SIZE);

    encryptRndNonce(header->handshake.nonce, message, sharedSecret);

    #ifdef Log_KEYS
        uint8_t sharedSecretHex[65];
        printHexKey(sharedSecretHex, sharedSecret);
        uint8_t nonceHex[49];
        Hex_encode(nonceHex, 49, header->handshake.nonce, 24);
        uint8_t cipherHex[65];
        printHexKey(cipherHex, message->bytes);
        Log_keys(wrapper->context->logger,
                  "Encrypting message with:\n"
                  "    nonce: %s\n"
                  "   secret: %s\n"
                  "   cipher: %s\n",
                  nonceHex, sharedSecretHex, cipherHex);
    #endif
    #ifdef Log_DEBUG
        Assert_true(!Bits_isZero(header->handshake.encryptedTempKey, 32));
    #endif

    // Shift it back -- encryptRndNonce adds 16 bytes of authenticator.
    Message_shift(message, Headers_CryptoAuth_SIZE - 32 - 16);

    return wrapper->wrappedInterface->sendMessage(message, wrapper->wrappedInterface);
}