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 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, 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, "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); cryptoAuthDebug(wrapper, "Dropped message with nonce [%d], decryption failed", nonce); 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->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); }