/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "crypto/CryptoAuth_pvt.h" #include "crypto/AddressCalc.h" #include "crypto/ReplayProtector.h" #include "crypto/random/Random.h" #include "interface/Interface.h" #include "benc/Dict.h" #include "benc/List.h" #include "benc/String.h" #include "util/log/Log.h" #include "memory/Allocator.h" #include "util/Assert.h" #include "util/AddrTools.h" #include "util/Bits.h" #include "util/Endian.h" #include "util/Hex.h" #include "util/events/Time.h" #include "wire/Error.h" #include "wire/Headers.h" #include "wire/Message.h" #include "crypto_box_curve25519xsalsa20poly1305.h" #include "crypto_core_hsalsa20.h" #include "crypto_hash_sha256.h" #include "crypto_scalarmult_curve25519.h" #include "crypto_stream_salsa20.h" #include "crypto_stream_xsalsa20.h" #include #include #ifdef win32 #undef interface #endif #ifdef Log_KEYS static inline void printHexKey(uint8_t output[65], uint8_t key[32]) { if (key) { Hex_encode(output, 65, key, 32); } else { Bits_memcpyConst(output, "NULL", 5); } } static inline void printHexPubKey(uint8_t output[65], uint8_t privateKey[32]) { if (privateKey) { uint8_t publicKey[32]; crypto_scalarmult_curve25519_base(publicKey, privateKey); printHexKey(output, publicKey); } else { printHexKey(output, NULL); } } #endif /** * Get a shared secret. * * @param outputSecret an array to place the shared secret in. * @param myPrivateKey * @param herPublicKey * @param logger * @param passwordHash a 32 byte value known to both ends, this must be provably pseudorandom * the first 32 bytes of a sha256 output from hashing a password is ok, * whatever she happens to send me in the Auth field is NOT ok. * If this field is null, the secret will be generated without the password. */ static inline void getSharedSecret(uint8_t outputSecret[32], uint8_t myPrivateKey[32], uint8_t herPublicKey[32], uint8_t passwordHash[32], struct Log* logger) { if (passwordHash == NULL) { crypto_box_curve25519xsalsa20poly1305_beforenm(outputSecret, herPublicKey, myPrivateKey); } else { union { struct { uint8_t key[32]; uint8_t passwd[32]; } components; uint8_t bytes[64]; } buff; crypto_scalarmult_curve25519(buff.components.key, myPrivateKey, herPublicKey); Bits_memcpyConst(buff.components.passwd, passwordHash, 32); crypto_hash_sha256(outputSecret, buff.bytes, 64); } #ifdef Log_KEYS uint8_t myPublicKeyHex[65]; printHexPubKey(myPublicKeyHex, myPrivateKey); uint8_t herPublicKeyHex[65]; printHexKey(herPublicKeyHex, herPublicKey); uint8_t passwordHashHex[65]; printHexKey(passwordHashHex, passwordHash); uint8_t outputSecretHex[65] = "NULL"; printHexKey(outputSecretHex, outputSecret); Log_keys(logger, "Generated a shared secret:\n" " myPublicKey=%s\n" " herPublicKey=%s\n" " passwordHash=%s\n" " outputSecret=%s\n", myPublicKeyHex, herPublicKeyHex, passwordHashHex, outputSecretHex); #endif } static inline void hashPassword_sha256(struct CryptoAuth_Auth* auth, const String* password) { uint8_t tempBuff[32]; crypto_hash_sha256(auth->secret, (uint8_t*) password->bytes, password->len); crypto_hash_sha256(tempBuff, auth->secret, 32); Bits_memcpyConst(auth->challenge.bytes, tempBuff, Headers_AuthChallenge_SIZE); Headers_setAuthChallengeDerivations(&auth->challenge, 0); auth->challenge.challenge.type = 1; } static inline uint8_t* hashPassword(struct CryptoAuth_Auth* auth, const String* password, const uint8_t authType) { switch (authType) { case 1: hashPassword_sha256(auth, password); break; default: Assert_true(!"Unsupported auth type."); }; return auth->secret; } /** * Search the authorized passwords for one matching this auth header. * * @param auth the auth header. * @param context the CryptoAuth engine to search in. * @return an Auth struct with a if one is found, otherwise NULL. */ static inline struct CryptoAuth_Auth* getAuth(union Headers_AuthChallenge auth, struct CryptoAuth_pvt* context) { if (auth.challenge.type != 1) { return NULL; } for (uint32_t i = 0; i < context->passwordCount; i++) { if (Bits_memcmp(auth.bytes, &context->passwords[i], Headers_AuthChallenge_KEYSIZE) == 0) { return &context->passwords[i]; } } Log_debug(context->logger, "Got unrecognized auth, password count = [%d]", context->passwordCount); return NULL; } static inline void getPasswordHash_typeOne(uint8_t output[32], uint16_t derivations, struct CryptoAuth_Auth* auth) { Bits_memcpyConst(output, auth->secret, 32); if (derivations) { union { uint8_t bytes[2]; uint8_t asShort; } deriv = { .asShort = derivations }; output[0] ^= deriv.bytes[0]; output[1] ^= deriv.bytes[1]; crypto_hash_sha256(output, output, 32); } } static inline uint8_t* tryAuth(union Headers_CryptoAuth* cauth, uint8_t hashOutput[32], struct CryptoAuth_Wrapper* wrapper, String** userPtr) { struct CryptoAuth_Auth* auth = getAuth(cauth->handshake.auth, wrapper->context); if (auth != NULL) { uint16_t deriv = Headers_getAuthChallengeDerivations(&cauth->handshake.auth); getPasswordHash_typeOne(hashOutput, deriv, auth); if (deriv == 0) { *userPtr = auth->user; } return hashOutput; } return NULL; } /** * Decrypt and authenticate. * * @param nonce a 24 byte number, may be random, cannot repeat. * @param msg a message to encipher and authenticate. * @param secret a shared secret. * @return 0 if decryption is succeddful, otherwise -1. */ static inline int decryptRndNonce(uint8_t nonce[24], struct Message* msg, uint8_t secret[32]) { if (msg->length < 16) { return -1; } Assert_true(msg->padding >= 16); uint8_t* startAt = msg->bytes - 16; uint8_t paddingSpace[16]; Bits_memcpyConst(paddingSpace, startAt, 16); Bits_memset(startAt, 0, 16); if (crypto_box_curve25519xsalsa20poly1305_open_afternm( startAt, startAt, msg->length + 16, nonce, secret) != 0) { return -1; } Bits_memcpyConst(startAt, paddingSpace, 16); Message_shift(msg, -16, NULL); return 0; } /** * Encrypt and authenticate. * Shifts the message by 16 bytes. * * @param nonce a 24 byte number, may be random, cannot repeat. * @param msg a message to encipher and authenticate. * @param secret a shared secret. */ static inline void encryptRndNonce(uint8_t nonce[24], struct Message* msg, uint8_t secret[32]) { Assert_true(msg->padding >= 32); uint8_t* startAt = msg->bytes - 32; // This function trashes 16 bytes of the padding so we will put it back uint8_t paddingSpace[16]; Bits_memcpyConst(paddingSpace, startAt, 16); Bits_memset(startAt, 0, 32); crypto_box_curve25519xsalsa20poly1305_afternm( startAt, startAt, msg->length + 32, nonce, secret); Bits_memcpyConst(startAt, paddingSpace, 16); Message_shift(msg, 16, NULL); } /** * Decrypt a packet. * * @param nonce a counter. * @param msg the message to decrypt, decrypted in place. * @param secret the shared secret. * @param isInitiator true if we started the connection. */ static inline int decrypt(uint32_t nonce, struct Message* msg, uint8_t secret[32], bool isInitiator) { union { uint32_t ints[2]; uint8_t bytes[24]; } nonceAs = { .ints = {0, 0} }; nonceAs.ints[!isInitiator] = Endian_hostToLittleEndian32(nonce); return decryptRndNonce(nonceAs.bytes, msg, secret); } /** * Encrypt a packet. * * @param nonce a counter. * @param msg the message to decrypt, decrypted in place. * @param secret the shared secret. * @param isInitiator true if we started the connection. */ static inline void encrypt(uint32_t nonce, struct Message* msg, uint8_t secret[32], bool isInitiator) { union { uint32_t ints[2]; uint8_t bytes[24]; } nonceAs = { .ints = {0, 0} }; nonceAs.ints[isInitiator] = Endian_hostToLittleEndian32(nonce); encryptRndNonce(nonceAs.bytes, msg, secret); } static inline void setRequiredPadding(struct CryptoAuth_Wrapper* wrapper) { uint32_t padding = (wrapper->nextNonce < 4) ? 36 : sizeof(union Headers_CryptoAuth) + 32; wrapper->externalInterface.requiredPadding = wrapper->wrappedInterface->requiredPadding + padding; wrapper->externalInterface.maxMessageLength = wrapper->wrappedInterface->maxMessageLength - padding; } static inline bool knowHerKey(struct CryptoAuth_Wrapper* wrapper) { return !Bits_isZero(wrapper->herPerminentPubKey, 32); } static void getIp6(struct CryptoAuth_Wrapper* wrapper, uint8_t* addr) { if (knowHerKey(wrapper)) { uint8_t ip6[16]; AddressCalc_addressForPublicKey(ip6, wrapper->herPerminentPubKey); AddrTools_printIp(addr, ip6); } } #define cryptoAuthDebug(wrapper, format, ...) \ { \ uint8_t addr[40] = "unknown"; \ getIp6((wrapper), addr); \ Log_debug((wrapper)->context->logger, \ "%p %s [%s]: " format, (void*)(wrapper), (wrapper)->name, addr, __VA_ARGS__); \ } #define cryptoAuthDebug0(wrapper, format) \ cryptoAuthDebug(wrapper, format "%s", "") static void reset(struct CryptoAuth_Wrapper* wrapper) { wrapper->nextNonce = 0; wrapper->isInitiator = false; Bits_memset(wrapper->ourTempPrivKey, 0, 32); Bits_memset(wrapper->ourTempPubKey, 0, 32); Bits_memset(wrapper->herTempPubKey, 0, 32); Bits_memset(wrapper->sharedSecret, 0, 32); wrapper->established = false; Bits_memset(&wrapper->replayProtector, 0, sizeof(struct ReplayProtector)); } /** * 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); } static uint8_t sendMessage(struct Message* message, struct Interface* interface); 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 inline uint8_t encryptMessage(struct Message* message, struct CryptoAuth_Wrapper* wrapper) { Assert_true(message->padding >= 36 || !"not enough padding"); encrypt(wrapper->nextNonce, message, wrapper->sharedSecret, wrapper->isInitiator); Message_shift(message, 4, NULL); union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) message->bytes; header->nonce = Endian_hostToBigEndian32(wrapper->nextNonce); wrapper->nextNonce++; return wrapper->wrappedInterface->sendMessage(message, wrapper->wrappedInterface); } static uint8_t sendMessage(struct Message* message, struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((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_resetIfTimeout(interface); // If the nonce wraps, start over. if (wrapper->nextNonce >= 0xfffffff0) { reset(wrapper); } Assert_true(!((uintptr_t)message->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 (wrapper->nextNonce < 5) { if (wrapper->nextNonce < 4) { return encryptHandshake(message, wrapper, 0); } else { cryptoAuthDebug0(wrapper, "Doing final step to send message. nonce=4"); Assert_ifParanoid(!Bits_isZero(wrapper->ourTempPrivKey, 32)); Assert_ifParanoid(!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); } /** Call the external interface and tell it that a message has been received. */ static inline uint8_t callReceivedMessage(struct CryptoAuth_Wrapper* wrapper, struct Message* message) { wrapper->timeOfLastPacket = Time_currentTimeSeconds(wrapper->context->eventBase); uint8_t ret = 0; if (wrapper->externalInterface.receiveMessage != NULL) { ret = wrapper->externalInterface.receiveMessage(message, &wrapper->externalInterface); } return ret; } 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 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 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); } ///////////////////////////////////////////////////////////////////////////////////////////////// struct CryptoAuth* CryptoAuth_new(struct Allocator* allocator, const uint8_t* privateKey, struct EventBase* eventBase, struct Log* logger, struct Random* rand) { struct CryptoAuth_pvt* ca = Allocator_calloc(allocator, sizeof(struct CryptoAuth_pvt), 1); ca->allocator = allocator; ca->passwords = Allocator_calloc(allocator, sizeof(struct CryptoAuth_Auth), 256); ca->passwordCount = 0; ca->passwordCapacity = 256; ca->eventBase = eventBase; ca->logger = logger; ca->pub.resetAfterInactivitySeconds = CryptoAuth_DEFAULT_RESET_AFTER_INACTIVITY_SECONDS; ca->rand = rand; Identity_set(ca); if (privateKey != NULL) { Bits_memcpyConst(ca->privateKey, privateKey, 32); } else { Random_bytes(rand, ca->privateKey, 32); } crypto_scalarmult_curve25519_base(ca->pub.publicKey, ca->privateKey); #ifdef Log_KEYS uint8_t publicKeyHex[65]; printHexKey(publicKeyHex, ca->pub.publicKey); uint8_t privateKeyHex[65]; printHexKey(privateKeyHex, ca->privateKey); Log_keys(logger, "Initialized CryptoAuth:\n myPrivateKey=%s\n myPublicKey=%s\n", privateKeyHex, publicKeyHex); #endif return &ca->pub; } int32_t CryptoAuth_addUser(String* password, uint8_t authType, String* user, struct CryptoAuth* ca) { struct CryptoAuth_pvt* context = Identity_check((struct CryptoAuth_pvt*) ca); if (authType != 1) { return CryptoAuth_addUser_INVALID_AUTHTYPE; } if (context->passwordCount == context->passwordCapacity) { // TODO(cjd): realloc password space and increase buffer. return CryptoAuth_addUser_OUT_OF_SPACE; } struct CryptoAuth_Auth a; hashPassword_sha256(&a, password); for (uint32_t i = 0; i < context->passwordCount; i++) { if (!Bits_memcmp(a.secret, context->passwords[i].secret, 32) || String_equals(user, context->passwords[i].user)) { return CryptoAuth_addUser_DUPLICATE; } } a.user = String_new(user->bytes, context->allocator); Bits_memcpyConst(&context->passwords[context->passwordCount], &a, sizeof(struct CryptoAuth_Auth)); context->passwordCount++; return 0; } int CryptoAuth_removeUsers(struct CryptoAuth* context, String* user) { struct CryptoAuth_pvt* ctx = Identity_check((struct CryptoAuth_pvt*) context); if (!user) { int count = ctx->passwordCount; Log_debug(ctx->logger, "Flushing [%d] users", count); ctx->passwordCount = 0; return count; } int count = 0; int i = 0; while (i < (int)ctx->passwordCount) { if (String_equals(ctx->passwords[i].user, user)) { Bits_memcpyConst(&ctx->passwords[i], &ctx->passwords[ctx->passwordCount--], sizeof(struct CryptoAuth_Auth)); count++; } else { i++; } } Log_debug(ctx->logger, "Removing [%d] user(s) identified by [%s]", count, user->bytes); return count; } List* CryptoAuth_getUsers(struct CryptoAuth* context, struct Allocator* alloc) { struct CryptoAuth_pvt* ctx = Identity_check((struct CryptoAuth_pvt*) context); uint32_t count = ctx->passwordCount; List* users = NULL; for (uint32_t i = 0; i < count; i++ ) { users = List_addString(users, String_clone(ctx->passwords[i].user, alloc), alloc); } return users; } String* CryptoAuth_getUser(struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)interface->senderContext); String* user = wrapper->user; if (user) { // If the user was lost in flushusers, then we need to return null. for (uint32_t i = 0; i < wrapper->context->passwordCount; i++) { if (user == wrapper->context->passwords[i].user) { return user; } } // Null it since it's been removed. wrapper->user = NULL; } return NULL; } struct Interface* CryptoAuth_wrapInterface(struct Interface* toWrap, const uint8_t herPublicKey[32], const uint8_t herIp6[16], const bool requireAuth, char* name, struct CryptoAuth* ca) { struct CryptoAuth_pvt* context = Identity_check((struct CryptoAuth_pvt*) ca); struct CryptoAuth_Wrapper* wrapper = Allocator_clone(toWrap->allocator, (&(struct CryptoAuth_Wrapper) { .user = NULL, .nextNonce = 0, .context = context, .wrappedInterface = toWrap, .requireAuth = requireAuth, .name = name })); wrapper->timeOfLastPacket = Time_currentTimeSeconds(context->eventBase); Identity_set(wrapper); toWrap->receiverContext = wrapper; toWrap->receiveMessage = receiveMessage; struct Interface iface = { .senderContext = wrapper, .sendMessage = sendMessage, .allocator = toWrap->allocator }; Bits_memcpyConst(&wrapper->externalInterface, &iface, sizeof(struct Interface)); if (herPublicKey != NULL) { Bits_memcpyConst(wrapper->herPerminentPubKey, herPublicKey, 32); uint8_t calculatedIp6[16]; AddressCalc_addressForPublicKey(calculatedIp6, herPublicKey); Bits_memcpyConst(wrapper->herIp6, calculatedIp6, 16); if (herIp6 != NULL) { Assert_true(!Bits_memcmp(calculatedIp6, herIp6, 16)); } } else if (herIp6) { Bits_memcpyConst(wrapper->herIp6, herIp6, 16); } return &wrapper->externalInterface; } void CryptoAuth_setAuth(const String* password, const uint8_t authType, struct Interface* wrappedInterface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)wrappedInterface->senderContext); wrapper->password = (password != NULL) ? String_newBinary(password->bytes, password->len, wrappedInterface->allocator) : NULL; wrapper->authType = (password != NULL) ? authType : 0; } uint8_t* CryptoAuth_getHerPublicKey(struct Interface* interface) { return ((struct CryptoAuth_Wrapper*) interface->senderContext)->herPerminentPubKey; } void CryptoAuth_reset(struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)interface->senderContext); reset(wrapper); } int CryptoAuth_getState(struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)interface->senderContext); switch (wrapper->nextNonce) { case 0: return CryptoAuth_NEW; case 1: // Sent a hello, waiting for the key return CryptoAuth_HANDSHAKE1; case 2: // Received a hello, sent a key packet. case 3: // Received a hello, sent multiple key packets. return CryptoAuth_HANDSHAKE2; case 4: // state 4 = waiting for first data packet to prove the handshake succeeded. // At this point you have sent a challenge and received a response so it is safe // to assume you are not being hit with replay packets. // // Sent a hello, received one or more keys, waiting for data. // In this state data packets will be sent but no data packets have yet been received. return CryptoAuth_HANDSHAKE3; default: // Received data. return (wrapper->established) ? CryptoAuth_ESTABLISHED : CryptoAuth_HANDSHAKE3; } } void CryptoAuth_resetIfTimeout(struct Interface* iface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)iface->senderContext); if (wrapper->nextNonce == 1) { // Lets not reset the session, we just sent one or more hello packets and // have not received a response, if they respond after we reset then we'll // be in a tough state. return; } uint64_t nowSecs = Time_currentTimeSeconds(wrapper->context->eventBase); if (nowSecs - wrapper->timeOfLastPacket > wrapper->context->pub.resetAfterInactivitySeconds) { cryptoAuthDebug(wrapper, "No traffic in [%d] seconds, resetting connection.", (int) (nowSecs - wrapper->timeOfLastPacket)); wrapper->timeOfLastPacket = nowSecs; reset(wrapper); } } struct Interface* CryptoAuth_getConnectedInterface(struct Interface* iface) { if (iface->sendMessage == sendMessage) { // internal (plaintext side) struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)iface->senderContext); return wrapper->wrappedInterface; } else if (iface->receiveMessage == receiveMessage) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)iface->receiverContext); return &wrapper->externalInterface; } return NULL; } struct ReplayProtector* CryptoAuth_getReplayProtector(struct Interface* iface) { struct CryptoAuth_Wrapper* wrapper = Identity_check((struct CryptoAuth_Wrapper*)iface->senderContext); return &wrapper->replayProtector; } // For testing: void CryptoAuth_encryptRndNonce(uint8_t nonce[24], struct Message* msg, uint8_t secret[32]) { encryptRndNonce(nonce, msg, secret); } int CryptoAuth_decryptRndNonce(uint8_t nonce[24], struct Message* msg, uint8_t secret[32]) { return decryptRndNonce(nonce, msg, secret); } uint8_t CryptoAuth_encryptHandshake(struct Message* message, struct CryptoAuth_Wrapper* wrapper, int setupMessage) { return encryptHandshake(message, wrapper, setupMessage); } uint8_t CryptoAuth_receiveMessage(struct Message* received, struct Interface* interface) { return receiveMessage(received, interface); }