struct SearchRunner_SearchData* SearchRunner_showActiveSearch(struct SearchRunner* searchRunner, int number, struct Allocator* alloc) { struct SearchRunner_pvt* runner = Identity_check((struct SearchRunner_pvt*)searchRunner); struct SearchRunner_Search* search = runner->firstSearch; while (search && number > 0) { search = search->nextSearch; number--; } struct SearchRunner_SearchData* out = Allocator_calloc(alloc, sizeof(struct SearchRunner_SearchData), 1); if (search) { Bits_memcpyConst(out->target, &search->target.ip6.bytes, 16); Bits_memcpyConst(&out->lastNodeAsked, &search->lastNodeAsked, sizeof(struct Address)); out->totalRequests = search->totalRequests; } out->activeSearches = runner->searches; return out; }
static uint8_t receiveMessageTUN(struct Message* msg, struct Interface* iface) { receivedMessageTUNCount++; uint16_t ethertype = TUNMessageType_pop(msg); if (ethertype != Ethernet_TYPE_IP4) { printf("Spurious packet with ethertype [%u]\n", Endian_bigEndianToHost16(ethertype)); return 0; } struct Headers_IP4Header* header = (struct Headers_IP4Header*) msg->bytes; Assert_always(msg->length == Headers_IP4Header_SIZE + Headers_UDPHeader_SIZE + 12); Assert_always(!Bits_memcmp(header->destAddr, testAddrB, 4)); Assert_always(!Bits_memcmp(header->sourceAddr, testAddrA, 4)); Bits_memcpyConst(header->destAddr, testAddrA, 4); Bits_memcpyConst(header->sourceAddr, testAddrB, 4); TUNMessageType_push(msg, ethertype); return iface->sendMessage(msg, iface); }
static uint8_t sendMessage(struct Message* message, struct Interface* ethIf) { struct ETHInterface* context = Identity_cast((struct ETHInterface*) ethIf); struct sockaddr_ll addr; Bits_memcpyConst(&addr, &context->addrBase, sizeof(struct sockaddr_ll)); Message_pop(message, addr.sll_addr, 8); /* Cut down on the noise uint8_t buff[sizeof(addr) * 2 + 1] = {0}; Hex_encode(buff, sizeof(buff), (uint8_t*)&addr, sizeof(addr)); Log_debug(context->logger, "Sending ethernet frame to [%s]", buff); */ // Check if we will have to pad the message and pad if necessary. int pad = 0; for (int length = message->length; length+2 < MIN_PACKET_SIZE; length += 8) { pad++; } if (pad > 0) { int length = message->length; Message_shift(message, pad*8); Bits_memset(message->bytes, 0, pad*8); Bits_memmove(message->bytes, &message->bytes[pad*8], length); } Assert_true(pad < 8); uint16_t padAndId_be = Endian_hostToBigEndian16((context->id << 3) | pad); Message_push(message, &padAndId_be, 2); if (sendto(context->socket, message->bytes, message->length, 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_ll)) < 0) { switch (errno) { case EMSGSIZE: return Error_OVERSIZE_MESSAGE; case ENOBUFS: case EAGAIN: return Error_LINK_LIMIT_EXCEEDED; default:; Log_info(context->logger, "Got error sending to socket [%s]", strerror(errno)); } } return 0; }
// Called by the TUN device. static inline uint8_t ip6FromTun(struct Message* message, struct Interface* interface) { struct Ducttape* context = (struct Ducttape*) interface->receiverContext; struct Headers_IP6Header* header = (struct Headers_IP6Header*) message->bytes; if (memcmp(header->sourceAddr, context->myAddr.ip6.bytes, 16)) { Log_warn(context->logger, "dropped message because only one address is allowed to be used " "and the source address was different.\n"); return Error_INVALID; } if (!memcmp(header->destinationAddr, context->myAddr.ip6.bytes, 16)) { // I'm Gonna Sit Right Down and Write Myself a Letter interface->sendMessage(message, interface); return Error_NONE; } context->switchHeader = NULL; struct Node* bestNext = RouterModule_lookup(header->destinationAddr, context->routerModule); if (bestNext) { context->forwardTo = &bestNext->address; if (!memcmp(header->destinationAddr, bestNext->address.ip6.bytes, 16)) { // Direct send, skip the innermost layer of encryption. #ifdef Log_DEBUG uint8_t nhAddr[60]; Address_print(nhAddr, &bestNext->address); Log_debug1(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); #endif return sendToRouter(&bestNext->address, message, context); } } // Grab out the header so it doesn't get clobbered. struct Headers_IP6Header headerStore; Bits_memcpyConst(&headerStore, header, Headers_IP6Header_SIZE); context->ip6Header = &headerStore; // Shift over the content. Message_shift(message, -Headers_IP6Header_SIZE); struct Interface* session = SessionManager_getSession(headerStore.destinationAddr, NULL, context->sm); // This comes out at outgoingFromMe() context->layer = INNER_LAYER; return session->sendMessage(message, session); }
int InterfaceController_getPeerStats(struct InterfaceController* ifController, struct Allocator* alloc, struct InterfaceController_PeerStats** statsOut) { struct InterfaceController_pvt* ic = Identity_check((struct InterfaceController_pvt*) ifController); int count = 0; for (int i = 0; i < ic->icis->length; i++) { struct InterfaceController_Iface_pvt* ici = ArrayList_OfIfaces_get(ic->icis, i); count += ici->peerMap.count; } struct InterfaceController_PeerStats* stats = Allocator_calloc(alloc, sizeof(struct InterfaceController_PeerStats), count); int xcount = 0; for (int j = 0; j < ic->icis->length; j++) { struct InterfaceController_Iface_pvt* ici = ArrayList_OfIfaces_get(ic->icis, j); for (int i = 0; i < (int)ici->peerMap.count; i++) { struct Peer* peer = Identity_check((struct Peer*) ici->peerMap.values[i]); struct InterfaceController_PeerStats* s = &stats[xcount]; xcount++; Bits_memcpyConst(&s->addr, &peer->addr, sizeof(struct Address)); s->bytesOut = peer->bytesOut; s->bytesIn = peer->bytesIn; s->timeOfLastMessage = peer->timeOfLastMessage; s->state = peer->state; s->isIncomingConnection = peer->isIncomingConnection; if (peer->caSession->userName) { s->user = String_clone(peer->caSession->userName, alloc); } struct ReplayProtector* rp = &peer->caSession->replayProtector; s->duplicates = rp->duplicates; s->lostPackets = rp->lostPackets; s->receivedOutOfRange = rp->receivedOutOfRange; struct PeerLink_Kbps kbps; PeerLink_kbps(peer->peerLink, &kbps); s->sendKbps = kbps.sendKbps; s->recvKbps = kbps.recvKbps; } } Assert_true(xcount == count); *statsOut = stats; return count; }
static int sendResponse(struct Message* msg, struct Ethernet* eth, struct Headers_IP6Header* ip6, struct NDPServer_pvt* ns) { Bits_memcpyConst(ip6->destinationAddr, ip6->sourceAddr, 16); Bits_memcpyConst(ip6->sourceAddr, UNICAST_ADDR, 16); ip6->hopLimit = 255; struct NDPHeader_RouterAdvert* adv = (struct NDPHeader_RouterAdvert*) msg->bytes; adv->checksum = Checksum_icmp6(ip6->sourceAddr, msg->bytes, msg->length); Message_push(msg, ip6, sizeof(struct Headers_IP6Header), NULL); // Eth Message_push(msg, eth, sizeof(struct Ethernet), NULL); struct Ethernet* ethP = (struct Ethernet*) msg->bytes; Bits_memcpyConst(ethP->destAddr, eth->srcAddr, 6); Bits_memcpyConst(ethP->srcAddr, eth->destAddr, 6); printf("responding\n"); Interface_sendMessage(ns->wrapped, msg); return 1; }
/** * 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 Gcc_USE_RET 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 (!Defined(NSA_APPROVED)) { 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; }
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. uint64_t nowSecs = Time_currentTimeSeconds(wrapper->context->eventBase); if (nowSecs - wrapper->timeOfLastPacket > wrapper->context->pub.resetAfterInactivitySeconds) { Log_debug(wrapper->context->logger, "No traffic in [%d] seconds, resetting connection.", (int) (nowSecs - wrapper->timeOfLastPacket)); wrapper->timeOfLastPacket = nowSecs; CryptoAuth_reset(interface); return encryptHandshake(message, 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); } else { Log_debug(wrapper->context->logger, "@%p Doing final step to send message. nonce=4\n", (void*) wrapper); uint8_t secret[32]; getSharedSecret(secret, wrapper->secret, wrapper->tempKey, NULL, wrapper->context->logger); Bits_memcpyConst(wrapper->secret, secret, 32); } } return encryptMessage(message, wrapper); }
/** * Send a search request to the next node in this search. * This is called whenever a response comes in or after the global mean response time passes. */ static void searchStep(struct SearchRunner_Search* search) { struct SearchRunner_pvt* ctx = Identity_check((struct SearchRunner_pvt*)search->runner); struct Node_Two* node; struct SearchStore_Node* nextSearchNode; for (;;) { nextSearchNode = SearchStore_getNextNode(search->search); // If the number of requests sent has exceeded the max search requests, let's stop there. if (search->totalRequests >= MAX_REQUESTS_PER_SEARCH || nextSearchNode == NULL) { if (search->pub.callback) { search->pub.callback(&search->pub, 0, NULL, NULL); } Allocator_free(search->pub.alloc); return; } node = NodeStore_getBest(&nextSearchNode->address, ctx->nodeStore); if (!node) { continue; } if (node == ctx->nodeStore->selfNode) { continue; } if (Bits_memcmp(node->address.ip6.bytes, nextSearchNode->address.ip6.bytes, 16)) { continue; } break; } Assert_true(node != ctx->nodeStore->selfNode); Bits_memcpyConst(&search->lastNodeAsked, &node->address, sizeof(struct Address)); struct RouterModule_Promise* rp = RouterModule_newMessage(&node->address, 0, ctx->router, search->pub.alloc); Dict* message = Dict_new(rp->alloc); Dict_putString(message, CJDHTConstants_QUERY, CJDHTConstants_QUERY_FN, rp->alloc); Dict_putString(message, CJDHTConstants_TARGET, search->targetStr, rp->alloc); rp->userData = search; rp->callback = searchCallback; RouterModule_sendMessage(rp, message); search->totalRequests++; }
static void incomingFromParent(evutil_socket_t socket, short eventType, void* vcontext) { struct ChildContext* context = (struct ChildContext*) vcontext; errno = 0; ssize_t amount = read(context->inFd, context->buffer, MAX_API_REQUEST_SIZE + TXID_LEN); if (amount < 1) { if (errno == EAGAIN) { return; } if (amount < 0) { perror("broken pipe"); } else { fprintf(stderr, "admin connection closed\n"); } exit(0); return; } else if (amount < 4) { return; } Assert_compileTime(TXID_LEN == 4); uint32_t connNumber; Bits_memcpyConst(&connNumber, context->buffer, TXID_LEN); if (connNumber >= MAX_CONNECTIONS) { fprintf(stderr, "got message for connection #%u, max connections is %d\n", connNumber, MAX_CONNECTIONS); return; } struct Connection* conn = &context->connections[connNumber]; if (!conn->read) { fprintf(stderr, "got message for closed #%u", connNumber); return; } amount -= TXID_LEN; ssize_t sent = send(conn->socket, context->buffer + TXID_LEN, amount, 0); if (sent != amount) { // All errors lead to closing the socket. EVUTIL_CLOSESOCKET(conn->socket); event_free(conn->read); conn->read = 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); } }
/** * Send a search request to the next node in this search. * This is called whenever a response comes in or after the global mean response time passes. */ static void searchStep(struct SearchRunner_Search* search) { struct SearchRunner_pvt* ctx = Identity_check((struct SearchRunner_pvt*)search->runner); struct SearchStore_Node* nextSearchNode; for (;;) { nextSearchNode = SearchStore_getNextNode(search->search); // If the number of requests sent has exceeded the max search requests, let's stop there. if (search->totalRequests >= search->maxRequests) { // fallthrough } else if (search->numFinds > 0 && search->totalRequests >= search->maxRequestsIfFound) { // fallthrough } else if (nextSearchNode == NULL) { // fallthrough } else { break; } if (search->pub.callback) { search->pub.callback(&search->pub, 0, NULL, NULL); } Allocator_free(search->pub.alloc); return; } Bits_memcpyConst(&search->lastNodeAsked, &nextSearchNode->address, sizeof(struct Address)); struct RouterModule_Promise* rp = RouterModule_newMessage(&nextSearchNode->address, 0, ctx->router, search->pub.alloc); Dict* message = Dict_new(rp->alloc); if (!Bits_memcmp(nextSearchNode->address.ip6.bytes, search->target.ip6.bytes, 16)) { Dict_putString(message, CJDHTConstants_QUERY, CJDHTConstants_QUERY_GP, rp->alloc); } else { Dict_putString(message, CJDHTConstants_QUERY, CJDHTConstants_QUERY_FN, rp->alloc); } Dict_putString(message, CJDHTConstants_TARGET, search->targetStr, rp->alloc); rp->userData = search; rp->callback = searchCallback; RouterModule_sendMessage(rp, message); search->totalRequests++; }
static uint8_t receiveMessage(struct Message* received, struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = (struct CryptoAuth_Wrapper*) interface->receiverContext; union Headers_CryptoAuth* header = (union Headers_CryptoAuth*) received->bytes; if (received->length < (wrapper->authenticatePackets ? 20 : 4)) { Log_debug(wrapper->context->logger, "Dropped runt"); return Error_UNDERSIZE_MESSAGE; } Assert_true(received->padding >= 12 || "need at least 12 bytes of padding in incoming message"); #ifdef Log_DEBUG Assert_true(!((uintptr_t)received->bytes % 4) || !"alignment fault"); #endif Message_shift(received, -4); uint32_t nonce = Endian_bigEndianToHost32(header->nonce); if (wrapper->nextNonce < 5) { if (nonce > 3 && nonce != UINT32_MAX && knowHerKey(wrapper)) { Log_debug(wrapper->context->logger, "@%p Trying final handshake step, nonce=%u\n", (void*) wrapper, nonce); uint8_t secret[32]; getSharedSecret(secret, wrapper->secret, wrapper->tempKey, NULL, wrapper->context->logger); if (decryptMessage(wrapper, nonce, received, secret)) { Log_debug(wrapper->context->logger, "Final handshake step succeeded.\n"); wrapper->nextNonce += 3; Bits_memcpyConst(wrapper->secret, secret, 32); return Error_NONE; } CryptoAuth_reset(&wrapper->externalInterface); Log_debug(wrapper->context->logger, "Final handshake step failed.\n"); } } else if (nonce > 2 && decryptMessage(wrapper, nonce, received, wrapper->secret)) { // If decryptMessage returns false then we will try the packet as a handshake. return Error_NONE; } else { Log_debug(wrapper->context->logger, "Decryption failed, trying message as a handshake.\n"); } Message_shift(received, 4); return decryptHandshake(wrapper, nonce, received, header); }
static struct sock_fprog* compile(struct Filter* input, int inputLen, struct Allocator* alloc) { // compute gotos int totalOut = 0; for (int i = inputLen-1; i >= 0; i--) { struct Filter* a = &input[i]; if (a->label == 0) { // check for unresolved gotos... Assert_true(a->jt == 0 && a->jf == 0); totalOut++; continue; } int diff = 0; for (int j = i-1; j >= 0; j--) { struct Filter* b = &input[j]; if (b->label != 0) { continue; } if (b->jt == a->label) { b->sf.jt = diff; b->jt = 0; } if (b->jf == a->label) { b->sf.jf = diff; b->jf = 0; } diff++; } } // copy into output filter array... struct sock_filter* sf = Allocator_calloc(alloc, sizeof(struct sock_filter), totalOut); int outI = 0; for (int i = 0; i < inputLen; i++) { if (input[i].label == 0) { Bits_memcpyConst(&sf[outI++], &input[i].sf, sizeof(struct sock_filter)); } Assert_true(outI <= totalOut); Assert_true(i != inputLen-1 || outI == totalOut); } struct sock_fprog* out = Allocator_malloc(alloc, sizeof(struct sock_fprog)); out->len = (unsigned short) totalOut; out->filter = sf; return out; }
static void responseCallback(struct RouterModule_Promise* promise, uint32_t lagMilliseconds, struct Address* from, Dict* result) { struct Janitor_Search* search = Identity_check((struct Janitor_Search*)promise->userData); if (from) { Bits_memcpyConst(&search->best, from, sizeof(struct Address)); return; } search->janitor->searches--; if (!search->best.path) { Log_debug(search->janitor->logger, "Search completed with no nodes found"); } Allocator_free(search->alloc); }
struct RouterModule_Promise* SearchRunner_search(uint8_t target[16], struct SearchRunner* searchRunner, struct Allocator* allocator) { struct SearchRunner_pvt* runner = Identity_check((struct SearchRunner_pvt*)searchRunner); if (runner->searches > runner->maxConcurrentSearches) { Log_debug(runner->logger, "Skipping search because there are already [%d] searches active", runner->searches); return NULL; } struct Allocator* alloc = Allocator_child(allocator); struct Address targetAddr = { .path = 0 }; Bits_memcpyConst(targetAddr.ip6.bytes, target, Address_SEARCH_TARGET_SIZE); struct NodeList* nodes = NodeStore_getClosestNodes(runner->nodeStore, &targetAddr, MAX_REQUESTS_PER_SEARCH, Version_CURRENT_PROTOCOL, alloc); if (nodes->size == 0) { Log_debug(runner->logger, "No nodes available for beginning search"); Allocator_free(alloc); return NULL; } struct SearchStore_Search* sss = SearchStore_newSearch(target, runner->searchStore, alloc); for (int i = 0; i < (int)nodes->size; i++) { SearchStore_addNodeToSearch(&nodes->nodes[i]->address, sss); } struct SearchRunner_Search* search = Allocator_clone(alloc, (&(struct SearchRunner_Search) { .pub = { .alloc = alloc }, .runner = runner, .search = sss }));
struct Ducttape* Ducttape_register(uint8_t privateKey[32], struct DHTModuleRegistry* registry, struct RouterModule* routerModule, struct SwitchCore* switchCore, struct EventBase* eventBase, struct Allocator* allocator, struct Log* logger, struct IpTunnel* ipTun, struct Random* rand) { struct Ducttape_pvt* context = Allocator_calloc(allocator, sizeof(struct Ducttape_pvt), 1); context->registry = registry; context->routerModule = routerModule; context->logger = logger; context->eventBase = eventBase; context->alloc = allocator; Identity_set(context); context->ipTunnel = ipTun; ipTun->nodeInterface.receiveMessage = sendToNode; ipTun->nodeInterface.receiverContext = context; ipTun->tunInterface.receiveMessage = sendToTun; ipTun->tunInterface.receiverContext = context; struct CryptoAuth* cryptoAuth = CryptoAuth_new(allocator, privateKey, eventBase, logger, rand); Bits_memcpyConst(context->myAddr.key, cryptoAuth->publicKey, 32); Address_getPrefix(&context->myAddr); context->sm = SessionManager_new(incomingFromCryptoAuth, outgoingFromCryptoAuth, context, eventBase, cryptoAuth, allocator); context->pub.sessionManager = context->sm; Bits_memcpyConst(&context->module, (&(struct DHTModule) { .name = "Ducttape", .context = context, .handleOutgoing = handleOutgoing }), sizeof(struct DHTModule));
static inline void hashPassword(uint8_t secretOut[32], union CryptoHeader_Challenge* challengeOut, const String* login, const String* password, const uint8_t authType) { crypto_hash_sha256(secretOut, (uint8_t*) password->bytes, password->len); uint8_t tempBuff[32]; if (authType == 1) { crypto_hash_sha256(tempBuff, secretOut, 32); } else if (authType == 2) { crypto_hash_sha256(tempBuff, (uint8_t*) login->bytes, login->len); } else { Assert_failure("Unsupported auth type [%u]", authType); } Bits_memcpyConst(challengeOut->bytes, tempBuff, CryptoHeader_Challenge_SIZE); CryptoHeader_setAuthChallengeDerivations(challengeOut, 0); challengeOut->challenge.type = authType; challengeOut->challenge.additional = 0; }
void Core_admin_register(uint8_t ipAddr[16], struct Ducttape* dt, struct Log* logger, struct IpTunnel* ipTunnel, struct Allocator* alloc, struct Admin* admin, struct EventBase* eventBase) { struct Context* ctx = Allocator_malloc(alloc, sizeof(struct Context)); Bits_memcpyConst(ctx->ipAddr, ipAddr, 16); ctx->ducttape = dt; ctx->logger = logger; ctx->alloc = alloc; ctx->admin = admin; ctx->eventBase = eventBase; ctx->ipTunnel = ipTunnel; struct Admin_FunctionArg args[] = { { .name = "desiredTunName", .required = 0, .type = "String" } };
/** Header must not be encrypted and must be aligned on the beginning of the ipv6 header. */ static inline uint8_t sendToRouter(struct Address* sendTo, struct Message* message, struct Ducttape* context) { // We have to copy out the switch header because it // will probably be clobbered by the crypto headers. struct Headers_SwitchHeader header; if (context->switchHeader) { Bits_memcpyConst(&header, context->switchHeader, Headers_SwitchHeader_SIZE); } else { memset(&header, 0, Headers_SwitchHeader_SIZE); } header.label_be = Endian_hostToBigEndian64(sendTo->path); context->switchHeader = &header; struct Interface* session = SessionManager_getSession(sendTo->ip6.bytes, sendTo->key, context->sm); // This comes out in outgoingFromCryptoAuth() then sendToSwitch() context->layer = OUTER_LAYER; return session->sendMessage(message, session); }
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); #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"); uint8_t secret[32]; getSharedSecret(secret, wrapper->secret, wrapper->tempKey, NULL, wrapper->context->logger); Bits_memcpyConst(wrapper->secret, secret, 32); } } Assert_true(message->length > 0 && "Empty packet during handshake"); return encryptMessage(message, wrapper); }
int main() { struct Allocator* alloc = MallocAllocator_new(1<<20); struct Writer* logwriter = FileWriter_new(stdout, alloc); struct Log* logger = &(struct Log) { .writer = logwriter }; struct event_base* eventBase = event_base_new(); struct CryptoAuth* ca = CryptoAuth_new(alloc, NULL, eventBase, logger); uint8_t publicKey[32]; Bits_memcpyConst(publicKey, ca->publicKey, 32); CryptoAuth_addUser(String_CONST("passwd"), 1, (void*)0x01, ca); struct SwitchCore* switchCore = SwitchCore_new(logger, alloc); struct Message* message; struct Interface iface = { .sendMessage = messageFromInterface, .senderContext = &message, .allocator = alloc }; SwitchCore_setRouterInterface(&iface, switchCore); // These are unused. struct DHTModuleRegistry* registry = DHTModuleRegistry_new(alloc); struct RouterModule* rm = RouterModule_register(registry, alloc, publicKey, eventBase, logger, NULL); struct InterfaceController* ifController = DefaultInterfaceController_new(ca, switchCore, rm, logger, eventBase, NULL, alloc); //////////////////////// return reconnectionNewEndpointTest(ifController, publicKey, &message, alloc, eventBase, logger, &iface); }
struct EncodingScheme* EncodingScheme_deserialize(String* data, struct Allocator* alloc) { struct EncodingScheme_Form* forms = NULL; int outCount = 0; uint64_t block = 0; int bits = 0; int dataIndex = 0; for (;;) { // load data into the block from the incoming data source while (bits < 56 && dataIndex < (int)data->len) { block |= (((uint64_t)data->bytes[dataIndex++] & 0xff) << bits); bits += 8; } struct EncodingScheme_Form next; int ret = decodeForm(&next, block); bits -= ret; if (!ret || bits < 0) { if (block || dataIndex < (int)data->len || bits < 0) { // Invalid encoding return NULL; } break; } block >>= ret; Assert_true((next.prefix >> next.prefixLen) == 0); outCount += 1; forms = Allocator_realloc(alloc, forms, outCount * sizeof(struct EncodingScheme_Form)); Bits_memcpyConst(&forms[outCount-1], &next, sizeof(struct EncodingScheme_Form)); } struct EncodingScheme* out = Allocator_clone(alloc, (&(struct EncodingScheme) { .forms = forms, .count = outCount }));
/** * 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 }
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; }
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; }
int CryptoAuth_removeUsers(struct CryptoAuth* context, void* uid) { struct CryptoAuth_pvt* ctx = Identity_cast((struct CryptoAuth_pvt*) context); if (!uid) { 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 (ctx->passwords[i].user == uid) { 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 [%p]", count, uid); return count; }
// incoming message from network, pointing to the beginning of the switch header. static uint8_t receiveMessage(struct Message* msg, struct Interface* iface) { struct SwitchPinger* ctx = Identity_check((struct SwitchPinger*) iface->receiverContext); struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes; ctx->incomingLabel = Endian_bigEndianToHost64(switchHeader->label_be); ctx->incomingVersion = 0; Message_shift(msg, -SwitchHeader_SIZE, NULL); uint32_t handle = Message_pop32(msg, NULL); #ifdef Version_7_COMPAT if (handle != 0xffffffff) { Message_push32(msg, handle, NULL); handle = 0xffffffff; Assert_true(SwitchHeader_isV7Ctrl(switchHeader)); } #endif Assert_true(handle == 0xffffffff); struct Control* ctrl = (struct Control*) msg->bytes; if (ctrl->type_be == Control_PONG_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); ctx->error = Error_NONE; if (msg->length >= Control_Pong_MIN_SIZE) { struct Control_Ping* pongHeader = (struct Control_Ping*) msg->bytes; ctx->incomingVersion = Endian_bigEndianToHost32(pongHeader->version_be); if (pongHeader->magic != Control_Pong_MAGIC) { Log_debug(ctx->logger, "dropped invalid switch pong"); return Error_INVALID; } Message_shift(msg, -Control_Pong_HEADER_SIZE, NULL); } else { Log_debug(ctx->logger, "got runt pong message, length: [%d]", msg->length); return Error_INVALID; } } else if (ctrl->type_be == Control_KEYPONG_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); ctx->error = Error_NONE; if (msg->length >= Control_KeyPong_HEADER_SIZE && msg->length <= Control_KeyPong_MAX_SIZE) { struct Control_KeyPing* pongHeader = (struct Control_KeyPing*) msg->bytes; ctx->incomingVersion = Endian_bigEndianToHost32(pongHeader->version_be); if (pongHeader->magic != Control_KeyPong_MAGIC) { Log_debug(ctx->logger, "dropped invalid switch key-pong"); return Error_INVALID; } Bits_memcpyConst(ctx->incomingKey, pongHeader->key, 32); Message_shift(msg, -Control_KeyPong_HEADER_SIZE, NULL); } else if (msg->length > Control_KeyPong_MAX_SIZE) { Log_debug(ctx->logger, "got overlong key-pong message, length: [%d]", msg->length); return Error_INVALID; } else { Log_debug(ctx->logger, "got runt key-pong message, length: [%d]", msg->length); return Error_INVALID; } } else if (ctrl->type_be == Control_ERROR_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); Assert_true((uint8_t*)&ctrl->content.error.errorType_be == msg->bytes); if (msg->length < (Control_Error_HEADER_SIZE + SwitchHeader_SIZE + Control_HEADER_SIZE)) { Log_debug(ctx->logger, "runt error packet"); return Error_NONE; } ctx->error = Message_pop32(msg, NULL); Message_push32(msg, 0, NULL); Message_shift(msg, -(Control_Error_HEADER_SIZE + SwitchHeader_SIZE), NULL); struct Control* origCtrl = (struct Control*) msg->bytes; Log_debug(ctx->logger, "error [%s] was caused by our [%s]", Error_strerror(ctx->error), Control_typeString(origCtrl->type_be)); int shift; if (origCtrl->type_be == Control_PING_be) { shift = -(Control_HEADER_SIZE + Control_Ping_HEADER_SIZE); } else if (origCtrl->type_be == Control_KEYPING_be) { shift = -(Control_HEADER_SIZE + Control_KeyPing_HEADER_SIZE); } else { Assert_failure("problem in Ducttape.c"); } if (msg->length < -shift) { Log_debug(ctx->logger, "runt error packet"); } Message_shift(msg, shift, NULL); } else { // If it gets here then Ducttape.c is failing. Assert_true(false); } String* msgStr = &(String) { .bytes = (char*) msg->bytes, .len = msg->length }; Pinger_pongReceived(msgStr, ctx->pinger); Bits_memset(ctx->incomingKey, 0, 32); return Error_NONE; } static void onPingResponse(String* data, uint32_t milliseconds, void* vping) { struct Ping* p = Identity_check((struct Ping*) vping); enum SwitchPinger_Result err = SwitchPinger_Result_OK; uint64_t label = p->context->incomingLabel; if (data) { if (label != p->label) { err = SwitchPinger_Result_LABEL_MISMATCH; } else if ((p->data || data->len > 0) && !String_equals(data, p->data)) { err = SwitchPinger_Result_WRONG_DATA; } else if (p->context->error == Error_LOOP_ROUTE) { err = SwitchPinger_Result_LOOP_ROUTE; } else if (p->context->error) { err = SwitchPinger_Result_ERROR_RESPONSE; } } else { err = SwitchPinger_Result_TIMEOUT; } uint32_t version = p->context->incomingVersion; struct SwitchPinger_Response* resp = Allocator_calloc(p->pub.pingAlloc, sizeof(struct SwitchPinger_Response), 1); resp->version = p->context->incomingVersion; resp->res = err; resp->label = label; resp->data = data; resp->milliseconds = milliseconds; resp->version = version; Bits_memcpyConst(resp->key, p->context->incomingKey, 32); resp->ping = &p->pub; p->onResponse(resp, p->pub.onResponseContext); } static void sendPing(String* data, void* sendPingContext) { struct Ping* p = Identity_check((struct Ping*) sendPingContext); struct Message* msg = Message_new(0, data->len + 512, p->pub.pingAlloc); while (((uintptr_t)msg->bytes - data->len) % 4) { Message_push8(msg, 0, NULL); } msg->length = 0; Message_push(msg, data->bytes, data->len, NULL); Assert_true(!((uintptr_t)msg->bytes % 4) && "alignment fault"); if (p->pub.keyPing) { Message_shift(msg, Control_KeyPing_HEADER_SIZE, NULL); struct Control_KeyPing* keyPingHeader = (struct Control_KeyPing*) msg->bytes; keyPingHeader->magic = Control_KeyPing_MAGIC; keyPingHeader->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); Bits_memcpyConst(keyPingHeader->key, p->context->myAddr->key, 32); } else { Message_shift(msg, Control_Ping_HEADER_SIZE, NULL); struct Control_Ping* pingHeader = (struct Control_Ping*) msg->bytes; pingHeader->magic = Control_Ping_MAGIC; pingHeader->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); } Message_shift(msg, Control_HEADER_SIZE, NULL); struct Control* ctrl = (struct Control*) msg->bytes; ctrl->checksum_be = 0; ctrl->type_be = (p->pub.keyPing) ? Control_KEYPING_be : Control_PING_be; ctrl->checksum_be = Checksum_engine(msg->bytes, msg->length); #ifdef Version_7_COMPAT if (0) { #endif Message_push32(msg, 0xffffffff, NULL); #ifdef Version_7_COMPAT } #endif Message_shift(msg, SwitchHeader_SIZE, NULL); struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes; switchHeader->label_be = Endian_hostToBigEndian64(p->label); SwitchHeader_setVersion(switchHeader, SwitchHeader_CURRENT_VERSION); SwitchHeader_setPenalty(switchHeader, 0); SwitchHeader_setCongestion(switchHeader, 0); #ifdef Version_7_COMPAT // v7 detects ctrl packets by the bit which has been // re-appropriated for suppression of errors. switchHeader->congestAndSuppressErrors = 1; SwitchHeader_setVersion(switchHeader, 0); #endif p->context->iface->sendMessage(msg, p->context->iface); } static String* RESULT_STRING_OK = String_CONST_SO("pong"); static String* RESULT_STRING_LABEL_MISMATCH = String_CONST_SO("diff_label"); static String* RESULT_STRING_WRONG_DATA = String_CONST_SO("diff_data"); static String* RESULT_STRING_ERROR_RESPONSE = String_CONST_SO("err_switch"); static String* RESULT_STRING_TIMEOUT = String_CONST_SO("timeout"); static String* RESULT_STRING_UNKNOWN = String_CONST_SO("err_unknown"); static String* RESULT_STRING_LOOP = String_CONST_SO("err_loop"); String* SwitchPinger_resultString(enum SwitchPinger_Result result) { switch (result) { case SwitchPinger_Result_OK: return RESULT_STRING_OK; case SwitchPinger_Result_LABEL_MISMATCH: return RESULT_STRING_LABEL_MISMATCH; case SwitchPinger_Result_WRONG_DATA: return RESULT_STRING_WRONG_DATA; case SwitchPinger_Result_ERROR_RESPONSE: return RESULT_STRING_ERROR_RESPONSE; case SwitchPinger_Result_TIMEOUT: return RESULT_STRING_TIMEOUT; case SwitchPinger_Result_LOOP_ROUTE: return RESULT_STRING_LOOP; default: return RESULT_STRING_UNKNOWN; }; } static int onPingFree(struct Allocator_OnFreeJob* job) { struct Ping* ping = Identity_check((struct Ping*)job->userData); struct SwitchPinger* ctx = Identity_check(ping->context); ctx->outstandingPings--; Assert_true(ctx->outstandingPings >= 0); return 0; } struct SwitchPinger_Ping* SwitchPinger_newPing(uint64_t label, String* data, uint32_t timeoutMilliseconds, SwitchPinger_ResponseCallback onResponse, struct Allocator* alloc, struct SwitchPinger* ctx) { if (data && data->len > Control_Ping_MAX_SIZE) { return NULL; } if (ctx->outstandingPings > ctx->maxConcurrentPings) { Log_debug(ctx->logger, "Skipping switch ping because there are already [%d] outstanding", ctx->outstandingPings); return NULL; } struct Pinger_Ping* pp = Pinger_newPing(data, onPingResponse, sendPing, timeoutMilliseconds, alloc, ctx->pinger); struct Ping* ping = Allocator_clone(pp->pingAlloc, (&(struct Ping) { .pub = { .pingAlloc = pp->pingAlloc }, .label = label, .data = String_clone(data, pp->pingAlloc), .context = ctx, .onResponse = onResponse, .pingerPing = pp }));
int main() { AddressCalc_addressForPublicKey(nodeCjdnsIp6, fakePubKey); struct Allocator* alloc = MallocAllocator_new(1<<20); struct Writer* w = FileWriter_new(stdout, alloc); struct Log* logger = WriterLog_new(w, alloc); struct Random* rand = Random_new(alloc, logger, NULL); struct EventBase* eb = EventBase_new(alloc); struct IpTunnel* ipTun = IpTunnel_new(logger, eb, alloc, rand, NULL); struct Sockaddr_storage ip6ToGive; Sockaddr_parse("fd01:0101:0101:0101:0101:0101:0101:0101", &ip6ToGive); IpTunnel_allowConnection(fakePubKey, &ip6ToGive.addr, NULL, ipTun); struct Message* message; Message_STACK(message, 64, 512); message->alloc = alloc; const char* requestForAddresses = "d" "1:q" "21:IpTunnel_getAddresses" "4:txid" "4:abcd" "e"; CString_strcpy((char*)message->bytes, requestForAddresses); message->length = CString_strlen(requestForAddresses); Message_shift(message, Headers_UDPHeader_SIZE, NULL); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; uh->srcPort_be = 0; uh->destPort_be = 0; uh->length_be = Endian_hostToBigEndian16(message->length - Headers_UDPHeader_SIZE); uint16_t* checksum = &uh->checksum_be; *checksum = 0; uint32_t length = message->length; Message_shift(message, Headers_IP6Header_SIZE, NULL); struct Headers_IP6Header* ip = (struct Headers_IP6Header*) message->bytes; ip->versionClassAndFlowLabel = 0; ip->flowLabelLow_be = 0; ip->payloadLength_be = Endian_hostToBigEndian16(length); ip->nextHeader = 17; ip->hopLimit = 255; Bits_memset(ip->sourceAddr, 0, 32); Headers_setIpVersion(ip); Message_shift(message, IpTunnel_PacketInfoHeader_SIZE, NULL); struct IpTunnel_PacketInfoHeader* pi = (struct IpTunnel_PacketInfoHeader*) message->bytes; Bits_memcpyConst(pi->nodeIp6Addr, nodeCjdnsIp6, 16); Bits_memcpyConst(pi->nodeKey, fakePubKey, 32); *checksum = Checksum_udpIp6(ip->sourceAddr, (uint8_t*) uh, length); ipTun->nodeInterface.receiveMessage = responseWithIpCallback; ipTun->nodeInterface.sendMessage(message, &ipTun->nodeInterface); Assert_true(called); called = 0; // Now create a message for someone else. Message_shift(message, Headers_UDPHeader_SIZE + Headers_IP6Header_SIZE + IpTunnel_PacketInfoHeader_SIZE, NULL); Bits_memcpyConst(ip->sourceAddr, fakeIp6ToGive, 16); // This can't be zero. Bits_memset(ip->destinationAddr, 1, 16); ipTun->tunInterface.receiveMessage = messageToTun; ipTun->nodeInterface.sendMessage(message, &ipTun->nodeInterface); Assert_true(called); Allocator_free(alloc); return 0; }
static int reconnectionNewEndpointTest(struct InterfaceController* ifController, uint8_t* pk, struct Message** fromSwitchPtr, struct Allocator* alloc, struct EventBase* eventBase, struct Log* logger, struct Interface* routerIf, struct Random* rand) { struct Message* message; struct Interface iface = { .sendMessage = messageFromInterface, .senderContext = &message, .allocator = alloc }; uint8_t* buffer = Allocator_malloc(alloc, 512); struct Message* outgoing = &(struct Message) { .length = 0, .padding = 512, .bytes = buffer + 512 }; struct CryptoAuth* externalCa = CryptoAuth_new(alloc, NULL, eventBase, logger, rand); struct Interface* wrapped = CryptoAuth_wrapInterface(&iface, pk, NULL, false, "", externalCa); CryptoAuth_setAuth(String_CONST("passwd"), 1, wrapped); struct Interface icIface = { .allocator = alloc, .sendMessage = messageFromInterface, .senderContext = &message }; InterfaceController_registerPeer(ifController, NULL, NULL, true, false, &icIface); uint8_t hexBuffer[1025]; for (int i = 0; i < 4; i++) { outgoing->length = 0; outgoing->padding = 512; outgoing->bytes = buffer + 512; Message_shift(outgoing, 12, NULL); Bits_memcpyConst(outgoing->bytes, "hello world", 12); Message_shift(outgoing, SwitchHeader_SIZE, NULL); Bits_memcpyConst(outgoing->bytes, (&(struct SwitchHeader) { .label_be = Endian_hostToBigEndian64(1), .lowBits_be = 0 }), SwitchHeader_SIZE); wrapped->sendMessage(outgoing, wrapped); *fromSwitchPtr = NULL; icIface.receiveMessage(outgoing, &icIface); message = *fromSwitchPtr; Assert_true(message); Assert_true(message->length == 24); Hex_encode(hexBuffer, 1025, message->bytes, message->length); printf("%s\n", hexBuffer); // Need to bounce the packets back when connecting after the first try. // This is needed to establish the CryptoAuth session and make the InterfaceController // merge the endpoints. if (i > 0) { // Reverse the bits to reverse the path: uint64_t path; Bits_memcpyConst(&path, message->bytes, 8); path = Bits_bitReverse64(path); Bits_memcpyConst(message->bytes, &path, 8); printf("sending back response.\n"); routerIf->receiveMessage(message, routerIf); printf("forwarding response to external cryptoAuth.\n"); iface.receiveMessage(message, &iface); printf("forwarded.\n"); } else { printf("not responding because we don't want to establish a connection yet.\n"); } } // check everything except the label Assert_true(!CString_strcmp((char*)hexBuffer+16, "0000000068656c6c6f20776f726c6400")); // check label: make sure the interface has been switched back into position 0. uint64_t label_be; Hex_decode((uint8_t*) &label_be, 8, hexBuffer, 16); uint64_t rev_label = Bits_bitReverse64(Endian_bigEndianToHost64(label_be)); // check label is decoded to 0 Assert_true(0 == NumberCompress_getDecompressed(rev_label, NumberCompress_bitsUsedForLabel(rev_label))); // check no other bits are set uint64_t out = NumberCompress_getCompressed(0, NumberCompress_bitsUsedForLabel(rev_label)); Assert_true(rev_label == out); return 0; }