/** * 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); }
/** * 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); }
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); }
/** @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; }
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; }
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; }
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); }
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; }
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); }
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); }
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); }
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); }
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); }
/** @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); }
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; }
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); }