static void showConn(struct IpTunnel_Connection* conn, String* txid, struct Admin* admin, struct Allocator* alloc) { Dict* d = Dict_new(alloc); if (!Bits_isZero(conn->connectionIp6, 16)) { struct Sockaddr* addr = Sockaddr_clone(Sockaddr_LOOPBACK6, alloc); uint8_t* address; Assert_true(16 == Sockaddr_getAddress(addr, &address)); Bits_memcpy(address, conn->connectionIp6, 16); char* printedAddr = Sockaddr_print(addr, alloc); Dict_putString(d, String_CONST("ip6Address"), String_CONST(printedAddr), alloc); Dict_putInt(d, String_CONST("ip6Prefix"), conn->connectionIp6Prefix, alloc); } if (!Bits_isZero(conn->connectionIp4, 4)) { struct Sockaddr* addr = Sockaddr_clone(Sockaddr_LOOPBACK, alloc); uint8_t* address; Assert_true(4 == Sockaddr_getAddress(addr, &address)); Bits_memcpy(address, conn->connectionIp4, 4); char* printedAddr = Sockaddr_print(addr, alloc); Dict_putString(d, String_CONST("ip4Address"), String_CONST(printedAddr), alloc); Dict_putInt(d, String_CONST("ip4Prefix"), conn->connectionIp4Prefix, alloc); } Dict_putString(d, String_CONST("key"), Key_stringify(conn->routeHeader.publicKey, alloc), alloc); Dict_putInt(d, String_CONST("outgoing"), (conn->isOutgoing) ? 1 : 0, alloc); Dict_putString(d, String_CONST("error"), String_CONST("none"), alloc); Admin_sendMessage(d, txid, admin); }
/** @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 void checkBytes(struct Random* rand, int alignment, int length) { Assert_true(length < 128 && alignment < 8); uint64_t buff64[20] = {0}; uint8_t* buff = (uint8_t*) (&buff64[1]); buff += alignment; // Check for bytes which are always the same, a few \0s are ok // but if every cycle they are always zero then there's a bug. uint8_t oldBuff[128] = {0}; // Preload into the output buff so alignment is same. Random_bytes(rand, buff, length); Bits_memcpy(oldBuff, buff, length); uint8_t sameAsOld[128]; Bits_memset(sameAsOld, 0xff, 128); // Check for bytes which are the same as other bytes every time. // if buff[3] always equals buff[8] then there's a bug. uint8_t sameBytes[128+128]; Bits_memset(sameBytes, 0xff, 128+128); for (int i = 0; i < 100; i++) { Random_bytes(rand, buff, length); for (int j = 0; j < length; j++) { for (int jj = j; jj < length; jj++) { sameBytes[j+jj] &= (jj != j && buff[j] == buff[jj]); } } for (int j = 0; j < length; j++) { sameAsOld[j] &= (oldBuff[i] == buff[i]); } // Check that the function did not write after or before the buffer. uint8_t* origBuff = (uint8_t*) (buff64); Assert_true(Bits_isZero(origBuff, 8+alignment)); Assert_true(Bits_isZero(buff+length, 8)); } for (int i = 0; i < length+length-1; i++) { Assert_true(!sameBytes[i]); } for (int i = 0; i < length; i++) { Assert_true(!sameAsOld[i]); } }
static void adminPingOnResponse(struct SwitchPinger_Response* resp, void* vping) { struct Allocator* pingAlloc = resp->ping->pingAlloc; struct Ping* ping = vping; Dict* rd = Dict_new(pingAlloc); if (resp->res == SwitchPinger_Result_LABEL_MISMATCH) { uint8_t path[20] = {0}; AddrTools_printPath(path, resp->label); String* pathStr = String_new(path, pingAlloc); Dict_putString(rd, String_CONST("rpath"), pathStr, pingAlloc); } Dict_putInt(rd, String_CONST("version"), resp->version, pingAlloc); Dict_putInt(rd, String_CONST("ms"), resp->milliseconds, pingAlloc); Dict_putString(rd, String_CONST("result"), SwitchPinger_resultString(resp->res), pingAlloc); Dict_putString(rd, String_CONST("path"), ping->path, pingAlloc); if (resp->data) { Dict_putString(rd, String_CONST("data"), resp->data, pingAlloc); } if (!Bits_isZero(resp->key, 32)) { Dict_putString(rd, String_CONST("key"), Key_stringify(resp->key, pingAlloc), pingAlloc); } Admin_sendMessage(rd, ping->txid, ping->context->admin); }
static int get(struct RandomSeed* randomSeed, uint64_t output[8]) { int mib[] = { CTL_KERN, KERN_ARND }; Bits_memset(output, 0, 64); size_t len = 64; if (sysctl(mib, 2, output, &len, NULL, 0) == -1) { // TOR/Libevent retry this 4 bytes at a time if it fails initially. size_t four = 4; int tries = 0; union { uint64_t longs[8]; uint32_t ints[16]; } num; for (int i = 0; i < 16; i++) { if (sysctl(mib, 2, &num.ints[i], &four, NULL, 0) == -1) { return -1; } if (num.ints[i] == 0) { i--; if (++tries > MAX_TRIES) { return -1; } } } } return Bits_isZero(output, 64) ? -1 : 0; }
static uint8_t responseWithIpCallback(struct Message* message, struct Interface* iface) { struct IpTunnel_PacketInfoHeader* pi = (struct IpTunnel_PacketInfoHeader*) message->bytes; Assert_true(!Bits_memcmp(nodeCjdnsIp6, pi->nodeIp6Addr, 16)); Assert_true(!Bits_memcmp(fakePubKey, pi->nodeKey, 32)); Message_shift(message, -IpTunnel_PacketInfoHeader_SIZE, NULL); struct Headers_IP6Header* ip = (struct Headers_IP6Header*) message->bytes; Assert_true(Headers_getIpVersion(ip) == 6); uint16_t length = Endian_bigEndianToHost16(ip->payloadLength_be); Assert_true(length + Headers_IP6Header_SIZE == message->length); Assert_true(ip->nextHeader == 17); Assert_true(Bits_isZero(ip->sourceAddr, 32)); Message_shift(message, -Headers_IP6Header_SIZE, NULL); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; Assert_true(!Checksum_udpIp6(ip->sourceAddr, message->bytes, length)); Assert_true(uh->srcPort_be == 0); Assert_true(uh->destPort_be == 0); Assert_true(Endian_bigEndianToHost16(uh->length_be) + Headers_UDPHeader_SIZE == length); Message_shift(message, -Headers_UDPHeader_SIZE, NULL); char* expectedResponse = "d" "9:addresses" "d" "3:ip6" "16:\xfd\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1" "e" "4:txid" "4:abcd" "e"; Assert_true(message->length == (int32_t) CString_strlen(expectedResponse)); Assert_true(!Bits_memcmp(message->bytes, expectedResponse, message->length)); called = 1; return 0; }
/** * Incoming message from someone we don't know, maybe someone responding to a beacon? * expects: [ struct LLAddress ][ content ] */ static Iface_DEFUN handleUnexpectedIncoming(struct Message* msg, struct InterfaceController_Iface_pvt* ici) { struct InterfaceController_pvt* ic = ici->ic; struct Sockaddr* lladdr = (struct Sockaddr*) msg->bytes; Message_shift(msg, -lladdr->addrLen, NULL); if (msg->length < CryptoHeader_SIZE) { return NULL; } struct Allocator* epAlloc = Allocator_child(ici->alloc); lladdr = Sockaddr_clone(lladdr, epAlloc); Assert_true(!((uintptr_t)msg->bytes % 4) && "alignment fault"); struct Peer* ep = Allocator_calloc(epAlloc, sizeof(struct Peer), 1); Identity_set(ep); ep->alloc = epAlloc; ep->ici = ici; ep->lladdr = lladdr; ep->alloc = epAlloc; ep->peerLink = PeerLink_new(ic->eventBase, epAlloc); struct CryptoHeader* ch = (struct CryptoHeader*) msg->bytes; ep->caSession = CryptoAuth_newSession(ic->ca, epAlloc, ch->publicKey, true, "outer"); if (CryptoAuth_decrypt(ep->caSession, msg)) { // If the first message is a dud, drop all state for this peer. // probably some random crap that wandered in the socket. Allocator_free(epAlloc); return NULL; } Assert_true(!Bits_isZero(ep->caSession->herPublicKey, 32)); Assert_true(Map_EndpointsBySockaddr_indexForKey(&lladdr, &ici->peerMap) == -1); int index = Map_EndpointsBySockaddr_put(&lladdr, &ep, &ici->peerMap); Assert_true(index >= 0); ep->handle = ici->peerMap.handles[index]; Allocator_onFree(epAlloc, closeInterface, ep); ep->state = InterfaceController_PeerState_UNAUTHENTICATED; ep->isIncomingConnection = true; ep->switchIf.send = sendFromSwitch; if (SwitchCore_addInterface(ic->switchCore, &ep->switchIf, epAlloc, &ep->addr.path)) { Log_debug(ic->logger, "handleUnexpectedIncoming() SwitchCore out of space"); Allocator_free(epAlloc); return NULL; } // We want the node to immedietly be pinged but we don't want it to appear unresponsive because // the pinger will only ping every (PING_INTERVAL * 8) so we set timeOfLastMessage to // (now - pingAfterMilliseconds - 1) so it will be considered a "lazy node". ep->timeOfLastMessage = Time_currentTimeMilliseconds(ic->eventBase) - ic->pingAfterMilliseconds - 1; Bits_memcpy(ep->addr.key, ep->caSession->herPublicKey, 32); Bits_memcpy(ep->addr.ip6.bytes, ep->caSession->herIp6, 16); Log_info(ic->logger, "Added peer [%s] from incoming message", Address_toString(&ep->addr, msg->alloc)->bytes); return receivedPostCryptoAuth(msg, ep, ic); }
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 void check(struct SessionManager* sm, int mapIndex) { Assert_true(sm->ifaceMap.keys[mapIndex].bytes[0] == 0xfc); uint8_t* herPubKey = CryptoAuth_getHerPublicKey(sm->ifaceMap.values[mapIndex]->pub.internal); if (!Bits_isZero(herPubKey, 32)) { uint8_t ip6[16]; AddressCalc_addressForPublicKey(ip6, herPubKey); Assert_true(!Bits_memcmp(&sm->ifaceMap.keys[mapIndex], ip6, 16)); } }
static int getUUID(uint64_t output[2]) { int mib[] = { CTL_KERN, KERN_RANDOM, RANDOM_UUID }; size_t sixteen = 16; Bits_memset(output, 0, 16); if (sysctl(mib, 3, output, &sixteen, NULL, 0) || Bits_isZero(output, 16)) { return -1; } return 0; }
static void showConn(struct IpTunnel_Connection* conn, String* txid, struct Admin* admin) { struct Allocator* alloc; BufferAllocator_STACK(alloc, 1024); Dict* d = Dict_new(alloc); char ip6[40]; if (!Bits_isZero(conn->connectionIp6, 16)) { Assert_always(evutil_inet_ntop(AF_INET6, conn->connectionIp6, ip6, 40)); Dict_putString(d, String_CONST("ip6Address"), String_CONST(ip6), alloc); } char ip4[16]; if (!Bits_isZero(conn->connectionIp4, 4)) { Assert_always(evutil_inet_ntop(AF_INET, conn->connectionIp4, ip4, 16)); Dict_putString(d, String_CONST("ip4Address"), String_CONST(ip4), alloc); } Dict_putString(d, String_CONST("key"), Key_stringify(conn->header.nodeKey, alloc), alloc); Dict_putInt(d, String_CONST("outgoing"), conn->isOutgoing, alloc); Admin_sendMessage(d, txid, admin); }
static int incomingFromDHT(struct DHTMessage* dmessage, void* vpf) { struct Pathfinder_pvt* pf = Identity_check((struct Pathfinder_pvt*) vpf); struct Message* msg = dmessage->binMessage; struct Address* addr = dmessage->address; if (addr->path == 1) { // Message to myself, can't handle this later because encrypting a message to yourself // causes problems. DHTModuleRegistry_handleIncoming(dmessage, pf->registry); return 0; } // Sanity check (make sure the addr was actually calculated) Assert_true(AddressCalc_validAddress(addr->ip6.bytes)); Message_shift(msg, PFChan_Msg_MIN_SIZE, NULL); struct PFChan_Msg* emsg = (struct PFChan_Msg*) msg->bytes; Bits_memset(emsg, 0, PFChan_Msg_MIN_SIZE); DataHeader_setVersion(&emsg->data, DataHeader_CURRENT_VERSION); DataHeader_setContentType(&emsg->data, ContentType_CJDHT); Bits_memcpy(emsg->route.ip6, addr->ip6.bytes, 16); emsg->route.version_be = Endian_hostToBigEndian32(addr->protocolVersion); emsg->route.sh.label_be = Endian_hostToBigEndian64(addr->path); emsg->route.flags |= RouteHeader_flags_PATHFINDER; SwitchHeader_setVersion(&emsg->route.sh, SwitchHeader_CURRENT_VERSION); Bits_memcpy(emsg->route.publicKey, addr->key, 32); Assert_true(!Bits_isZero(emsg->route.publicKey, 32)); Assert_true(emsg->route.sh.label_be); Assert_true(emsg->route.version_be); Message_push32(msg, PFChan_Pathfinder_SENDMSG, NULL); if (dmessage->replyTo) { // see incomingMsg dmessage->replyTo->pleaseRespond = true; //Log_debug(pf->log, "send DHT reply"); return 0; } //Log_debug(pf->log, "send DHT request"); Iface_send(&pf->pub.eventIf, msg); return 0; }
static int get(struct RandomSeed* randomSeed, uint64_t output[8]) { #ifdef SYS_getrandom // Try using getrandom instead sysctl as with systems with getrandom // sysctl is probably alrady deprecated and possibly disabled. Bits_memset(output, 0, 64); // Just make sure that it is zero all along. long ret = syscall(SYS_getrandom, output, 64, 0); if (ret == 64 && !Bits_isZero(output, 64)) { return 0; } #endif if (getUUID(output) || getUUID(output+2) || getUUID(output+4) || getUUID(output+6)) { return -1; } return 0; }
static Iface_DEFUN receiveMessage(struct Message* msg, struct Iface* external) { struct TAPWrapper_pvt* tw = Identity_containerOf(external, struct TAPWrapper_pvt, external); if (msg->length < Ethernet_SIZE-2) { Log_debug(tw->log, "runt"); return 0; } // wacky 14 byte headers, back off into outer-space to create the padding... Message_shift(msg, 2, NULL); struct Ethernet eth; Message_pop(msg, ð, sizeof(struct Ethernet), NULL); // Not for us and not multicast... if (Bits_memcmp(eth.destAddr, TAPWrapper_LOCAL_MAC, Ethernet_ADDRLEN) && !(eth.destAddr[0] & 0x01)) { if (Defined(Log_DEBUG)) { uint8_t printedMac[18]; AddrTools_printMac(printedMac, eth.destAddr); Log_debug(tw->log, "Packet destine for unknown ethernet MAC [%s]", printedMac); } //return 0; } if (Bits_memcmp(eth.srcAddr, tw->pub.peerAddress, Ethernet_ADDRLEN)) { if (Bits_isZero(tw->pub.peerAddress, Ethernet_ADDRLEN)) { Bits_memcpy(tw->pub.peerAddress, eth.srcAddr, Ethernet_ADDRLEN); } else { #ifdef Log_DEBUG uint8_t printedMac[18]; AddrTools_printMac(printedMac, eth.srcAddr); Log_debug(tw->log, "DROP Packet with unexpected source MAC [%s]", printedMac); #endif return 0; } } TUNMessageType_push(msg, eth.ethertype, NULL); return Iface_next(&tw->pub.internal, msg); }
static Iface_DEFUN responseWithIpCallback(struct Message* message, struct Iface* iface) { struct Context* ctx = Identity_check(((struct IfaceContext*)iface)->ctx); struct RouteHeader* rh = (struct RouteHeader*) message->bytes; Assert_true(!Bits_memcmp(ctx->ipv6, rh->ip6, 16)); Assert_true(!Bits_memcmp(ctx->pubKey, rh->publicKey, 32)); Message_shift(message, -(RouteHeader_SIZE + DataHeader_SIZE), NULL); struct Headers_IP6Header* ip = (struct Headers_IP6Header*) message->bytes; Assert_true(Headers_getIpVersion(ip) == 6); uint16_t length = Endian_bigEndianToHost16(ip->payloadLength_be); Assert_true(length + Headers_IP6Header_SIZE == message->length); Assert_true(ip->nextHeader == 17); Assert_true(Bits_isZero(ip->sourceAddr, 32)); Message_shift(message, -Headers_IP6Header_SIZE, NULL); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; Assert_true(!Checksum_udpIp6(ip->sourceAddr, message->bytes, length)); Assert_true(uh->srcPort_be == 0); Assert_true(uh->destPort_be == 0); Assert_true(Endian_bigEndianToHost16(uh->length_be) + Headers_UDPHeader_SIZE == length); Message_shift(message, -Headers_UDPHeader_SIZE, NULL); struct Allocator* alloc = Allocator_child(ctx->alloc); char* messageContent = Escape_getEscaped(message->bytes, message->length, alloc); char* expectedContent = Escape_getEscaped(ctx->expectedResponse->bytes, ctx->expectedResponse->len, alloc); Log_debug(ctx->log, "Response: [%s]", messageContent); Log_debug(ctx->log, "Expected: [%s]", expectedContent); Allocator_free(alloc); // We can't check that the message is an exact match because the padding depends on the // alignment of the output but we can make sure the right content is there... // Message should start with "d0000" (with some number of zeros) Assert_true((int)ctx->expectedResponse->len == message->length); Assert_true(!Bits_memcmp(message->bytes, ctx->expectedResponse->bytes, message->length)); ctx->called |= 2; return NULL; }
static Iface_DEFUN sendMessage(struct Message* msg, struct Iface* internal) { struct TAPWrapper_pvt* tw = Identity_containerOf(internal, struct TAPWrapper_pvt, pub.internal); uint16_t etherType = TUNMessageType_pop(msg, NULL); struct Ethernet eth = { .ethertype = etherType }; Bits_memcpyConst(eth.srcAddr, TAPWrapper_LOCAL_MAC, Ethernet_ADDRLEN); Bits_memcpyConst(eth.destAddr, tw->pub.peerAddress, Ethernet_ADDRLEN); if (Bits_isZero(tw->pub.peerAddress, Ethernet_ADDRLEN)) { Log_debug(tw->log, "DROP Packet because peers MAC is not yet known"); return NULL; } Message_push(msg, ð, sizeof(struct Ethernet), NULL); // struct Ethernet contains 2 bytes of padding at the beginning. Message_shift(msg, -2, NULL); return Iface_next(&tw->external, msg); }
static Iface_DEFUN responseWithIpCallback(struct Message* message, struct Iface* iface) { struct RouteHeader* rh = (struct RouteHeader*) message->bytes; Assert_true(!Bits_memcmp(nodeCjdnsIp6, rh->ip6, 16)); Assert_true(!Bits_memcmp(fakePubKey, rh->publicKey, 32)); Message_shift(message, -(RouteHeader_SIZE + DataHeader_SIZE), NULL); struct Headers_IP6Header* ip = (struct Headers_IP6Header*) message->bytes; Assert_true(Headers_getIpVersion(ip) == 6); uint16_t length = Endian_bigEndianToHost16(ip->payloadLength_be); Assert_true(length + Headers_IP6Header_SIZE == message->length); Assert_true(ip->nextHeader == 17); Assert_true(Bits_isZero(ip->sourceAddr, 32)); Message_shift(message, -Headers_IP6Header_SIZE, NULL); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; Assert_true(!Checksum_udpIp6(ip->sourceAddr, message->bytes, length)); Assert_true(uh->srcPort_be == 0); Assert_true(uh->destPort_be == 0); Assert_true(Endian_bigEndianToHost16(uh->length_be) + Headers_UDPHeader_SIZE == length); Message_shift(message, -Headers_UDPHeader_SIZE, NULL); // We can't check that the message is an exact match because the padding depends on the // alignment of the output but we can make sure the right content is there... // Message should start with "d0000" (with some number of zeros) char* expectedResponse = "9:addresses" "d" "3:ip6" "16:\xfd\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1" "9:ip6Prefix" "i128e" "e" "4:txid" "4:abcd" "e"; Assert_true(message->length >= (int32_t) CString_strlen(expectedResponse)); Assert_true(CString_strstr(message->bytes, expectedResponse)); called |= 2; 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); }
static uint8_t decryptHandshake(struct Wrapper* wrapper, const uint32_t nonce, struct Message* message, union Headers_CryptoAuth* header) { if (message->length < sizeof(union Headers_CryptoAuth)) { Log_debug(wrapper->context->logger, "Dropped runt packet\n"); 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)) { memcpy(wrapper->herPerminentPubKey, header->handshake.publicKey, 32); } Message_shift(message, -Headers_CryptoAuth_SIZE); message->length = 0; wrapper->nextNonce = 0; wrapper->user = NULL; // 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) { Log_debug(wrapper->context->logger, "Dropping message because auth was not given and is required.\n"); return Error_AUTHENTICATION; } if (passwordHash == NULL && header->handshake.auth.challenge.type != 0) { Log_debug(wrapper->context->logger, "Dropping message because it contans an authenticator which is unrecognized.\n"); 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) { Log_debug1(wrapper->context->logger, "Received a hello packet, using auth: %d\n", (passwordHash != NULL)); } else { Log_debug(wrapper->context->logger, "Received a repeat hello packet\n"); } // 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)) { Log_debug(wrapper->context->logger, "Node sent public key of ZERO!\n"); } #endif } else { herPermKey = wrapper->herPerminentPubKey; if (memcmp(header->handshake.publicKey, herPermKey, 32)) { Log_debug(wrapper->context->logger, "Packet contains different perminent key.\n"); return Error_AUTHENTICATION; } } getSharedSecret(sharedSecret, wrapper->context->privateKey, herPermKey, passwordHash, wrapper->context->logger); nextNonce = 2; } else { if (nonce == 2) { Log_debug(wrapper->context->logger, "Received a key packet\n"); } else if (nonce == 3) { Log_debug(wrapper->context->logger, "Received a repeat key packet\n"); } else { Log_debug1(wrapper->context->logger, "Received a packet of unknown type! nonce=%u\n", nonce); } if (memcmp(header->handshake.publicKey, wrapper->herPerminentPubKey, 32)) { Log_debug(wrapper->context->logger, "Packet contains different perminent key.\n"); 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); Log_debug1(wrapper->context->logger, "Message length: %u\n", message->length); #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_keys3(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 memset(header, 0, Headers_CryptoAuth_SIZE); Log_debug(wrapper->context->logger, "Dropped message because authenticated decryption failed.\n"); return Error_AUTHENTICATION; } wrapper->user = user; memcpy(wrapper->tempKey, header->handshake.encryptedTempKey, 32); #ifdef Log_DEBUG assert(!Bits_isZero(header->handshake.encryptedTempKey, 32)); #endif #ifdef Log_KEYS uint8_t tempKeyHex[65]; Hex_encode(tempKeyHex, 65, wrapper->tempKey, 32); Log_keys1(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) { memcpy(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) { Log_debug(wrapper->context->logger, "Sending buffered message.\n"); sendMessage(wrapper->bufferedMessage, &wrapper->externalInterface); wrapper->hasBufferedMessage = false; return Error_NONE; } else if (wrapper->hasBufferedMessage) { Log_debug(wrapper->context->logger, "There is a buffered message.\n"); } memset(&wrapper->replayProtector, 0, sizeof(struct ReplayProtector)); setRequiredPadding(wrapper); return callReceivedMessage(wrapper, message); }
static uint8_t encryptHandshake(struct Message* message, struct Wrapper* wrapper) { assert(message->padding >= sizeof(union Headers_CryptoAuth) || !"not enough padding"); 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) randombytes((uint8_t*) &header->handshake.auth, sizeof(union Headers_AuthChallenge) + 24); memcpy(&header->handshake.publicKey, wrapper->context->publicKey, 32); if (!knowHerKey(wrapper)) { return genReverseHandshake(message, wrapper, header); } // Password auth uint8_t* passwordHash = NULL; if (wrapper->password != NULL) { struct Auth auth; passwordHash = hashPassword(&auth, wrapper->password, wrapper->authType); memcpy(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 crypto_box_curve25519xsalsa20poly1305_keypair(header->handshake.encryptedTempKey, wrapper->secret); if (wrapper->nextNonce == 0) { memcpy(wrapper->tempKey, header->handshake.encryptedTempKey, 32); } #ifdef Log_DEBUG assert(!Bits_isZero(header->handshake.encryptedTempKey, 32)); assert(!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. memcpy(header->handshake.encryptedTempKey, wrapper->tempKey, 32); } uint8_t sharedSecret[32]; if (wrapper->nextNonce < 2) { if (wrapper->nextNonce == 0) { Log_debug(wrapper->context->logger, "Sending hello packet\n"); } else { Log_debug(wrapper->context->logger, "Sending repeat hello packet\n"); } getSharedSecret(sharedSecret, wrapper->context->privateKey, wrapper->herPerminentPubKey, passwordHash, wrapper->context->logger); wrapper->isInitiator = true; wrapper->nextNonce = 1; #ifdef Log_DEBUG assert(!Bits_isZero(header->handshake.encryptedTempKey, 32)); uint8_t myTempPubKey[32]; crypto_scalarmult_curve25519_base(myTempPubKey, wrapper->secret); assert(!memcmp(header->handshake.encryptedTempKey, myTempPubKey, 32)); #endif #ifdef Log_KEYS uint8_t tempKeyHex[65]; Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32); Log_keys1(wrapper->context->logger, "Wrapping temp public key:\n" " %s\n", tempKeyHex); #endif } else { if (wrapper->nextNonce == 2) { Log_debug(wrapper->context->logger, "Sending key packet\n"); } else { Log_debug(wrapper->context->logger, "Sending repeat key packet\n"); } // 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_keys1(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); Log_debug1(wrapper->context->logger, "Message length: %u\n", message->length); #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_keys3(wrapper->context->logger, "Encrypting message with:\n" " nonce: %s\n" " secret: %s\n" " cipher: %s\n", nonceHex, sharedSecretHex, cipherHex); #endif #ifdef Log_DEBUG assert(!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); }
static inline bool knowHerKey(struct Wrapper* wrapper) { return !Bits_isZero(wrapper->herPerminentPubKey, 32); }
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 bool knowHerKey(struct CryptoAuth_Session_pvt* session) { return !Bits_isZero(session->pub.herPublicKey, 32); }
static void encryptHandshake(struct Message* message, struct CryptoAuth_Session_pvt* session, int setupMessage) { Message_shift(message, sizeof(union CryptoHeader), NULL); union CryptoHeader* header = (union CryptoHeader*) message->bytes; // garbage the auth challenge and set the nonce which follows it Random_bytes(session->context->rand, (uint8_t*) &header->handshake.auth, sizeof(union CryptoHeader_Challenge) + 24); // set the permanent key Bits_memcpyConst(&header->handshake.publicKey, session->context->pub.publicKey, 32); Assert_true(knowHerKey(session)); uint8_t calculatedIp6[16]; AddressCalc_addressForPublicKey(calculatedIp6, session->pub.herPublicKey); if (!Bits_isZero(session->pub.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. Assert_true(!Bits_memcmp(session->pub.herIp6, calculatedIp6, 16)); } // Password auth uint8_t* passwordHash = NULL; uint8_t passwordHashStore[32]; if (session->password != NULL) { hashPassword(passwordHashStore, &header->handshake.auth, session->login, session->password, session->authType); passwordHash = passwordHashStore; } else { header->handshake.auth.challenge.type = session->authType; header->handshake.auth.challenge.additional = 0; } // Set the session state header->nonce = Endian_hostToBigEndian32(session->nextNonce); if (session->nextNonce == 0 || session->nextNonce == 2) { // If we're sending a hello or a key // Here we make up a temp keypair Random_bytes(session->context->rand, session->ourTempPrivKey, 32); crypto_scalarmult_curve25519_base(session->ourTempPubKey, session->ourTempPrivKey); if (Defined(Log_KEYS)) { uint8_t tempPrivateKeyHex[65]; Hex_encode(tempPrivateKeyHex, 65, session->ourTempPrivKey, 32); uint8_t tempPubKeyHex[65]; Hex_encode(tempPubKeyHex, 65, session->ourTempPubKey, 32); Log_keys(session->context->logger, "Generating temporary keypair\n" " myTempPrivateKey=%s\n" " myTempPublicKey=%s\n", tempPrivateKeyHex, tempPubKeyHex); } } Bits_memcpyConst(header->handshake.encryptedTempKey, session->ourTempPubKey, 32); if (Defined(Log_KEYS)) { uint8_t tempKeyHex[65]; Hex_encode(tempKeyHex, 65, header->handshake.encryptedTempKey, 32); Log_keys(session->context->logger, "Wrapping temp public key:\n" " %s\n", tempKeyHex); } cryptoAuthDebug(session, "Sending %s%s packet", ((session->nextNonce & 1) ? "repeat " : ""), ((session->nextNonce < 2) ? "hello" : "key")); uint8_t sharedSecret[32]; if (session->nextNonce < 2) { getSharedSecret(sharedSecret, session->context->privateKey, session->pub.herPublicKey, passwordHash, session->context->logger); session->isInitiator = true; Assert_true(session->nextNonce <= 1); session->nextNonce = 1; } else { // Handshake2 // herTempPubKey was set by decryptHandshake() Assert_ifParanoid(!Bits_isZero(session->herTempPubKey, 32)); getSharedSecret(sharedSecret, session->context->privateKey, session->herTempPubKey, passwordHash, session->context->logger); Assert_true(session->nextNonce <= 3); session->nextNonce = 3; if (Defined(Log_KEYS)) { uint8_t tempKeyHex[65]; Hex_encode(tempKeyHex, 65, session->herTempPubKey, 32); Log_keys(session->context->logger, "Using their temp public key:\n" " %s\n", tempKeyHex); } } // Shift message over the encryptedTempKey field. Message_shift(message, 32 - CryptoHeader_SIZE, NULL); encryptRndNonce(header->handshake.nonce, message, sharedSecret); 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, "Encrypting message with:\n" " nonce: %s\n" " secret: %s\n" " cipher: %s\n", nonceHex, sharedSecretHex, cipherHex); } // Shift it back -- encryptRndNonce adds 16 bytes of authenticator. Message_shift(message, CryptoHeader_SIZE - 32 - 16, NULL); }
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 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 void beginConnection(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc) { struct Context* ctx = vcontext; String* password = Dict_getString(args, String_CONST("password")); String* publicKey = Dict_getString(args, String_CONST("publicKey")); String* address = Dict_getString(args, String_CONST("address")); int64_t* interfaceNumber = Dict_getInt(args, String_CONST("interfaceNumber")); uint32_t ifNum = (interfaceNumber) ? ((uint32_t) *interfaceNumber) : 0; String* peerName = Dict_getString(args, String_CONST("peerName")); String* error = NULL; Log_debug(ctx->logger, "Peering with [%s]", publicKey->bytes); struct Sockaddr_storage ss; uint8_t pkBytes[32]; int ret; if (interfaceNumber && *interfaceNumber < 0) { error = String_CONST("negative interfaceNumber"); } else if ((ret = Key_parse(publicKey, pkBytes, NULL))) { error = String_CONST(Key_parse_strerror(ret)); } else if (Sockaddr_parse(address->bytes, &ss)) { error = String_CONST("unable to parse ip address and port."); } else if (Sockaddr_getFamily(&ss.addr) != Sockaddr_getFamily(ctx->udpIf->addr)) { error = String_CONST("different address type than this socket is bound to."); } else { struct Sockaddr* addr = &ss.addr; char* addrPtr = NULL; int addrLen = Sockaddr_getAddress(&ss.addr, &addrPtr); Assert_true(addrLen > 0); struct Allocator* tempAlloc = Allocator_child(ctx->alloc); if (Bits_isZero(addrPtr, addrLen)) { // unspec'd address, convert to loopback if (Sockaddr_getFamily(addr) == Sockaddr_AF_INET) { addr = Sockaddr_clone(Sockaddr_LOOPBACK, tempAlloc); } else if (Sockaddr_getFamily(addr) == Sockaddr_AF_INET6) { addr = Sockaddr_clone(Sockaddr_LOOPBACK6, tempAlloc); } else { Assert_failure("Sockaddr which is not AF_INET nor AF_INET6"); } Sockaddr_setPort(addr, Sockaddr_getPort(&ss.addr)); } int ret = InterfaceController_bootstrapPeer( ctx->ic, ifNum, pkBytes, addr, password, peerName, ctx->alloc); Allocator_free(tempAlloc); if (ret) { switch(ret) { case InterfaceController_bootstrapPeer_BAD_IFNUM: error = String_CONST("no such interface for interfaceNumber"); break; case InterfaceController_bootstrapPeer_BAD_KEY: error = String_CONST("invalid cjdns public key."); break; case InterfaceController_bootstrapPeer_OUT_OF_SPACE: error = String_CONST("no more space to register with the switch."); break; default: error = String_CONST("unknown error"); break; } } else { error = String_CONST("none"); } } Dict out = Dict_CONST(String_CONST("error"), String_OBJ(error), NULL); Admin_sendMessage(&out, txid, ctx->admin); }
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); }
/** @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); }