sodiumTest

Libsodium examples, client/server system
git clone https://noulin.net/git/sodiumTest.git
Log | Files | Refs | README

commit 4c98531aa7f0c18f42256de7c19fe89ebb4ec38a
parent cd1a6da42e8ba5dea2bf9109c01677206aca9e47
Author: Remy Noulin <loader2x@gmail.com>
Date:   Fri, 30 Sep 2022 16:42:16 -0400

add client/server3 and client/server4

README.md     |   7 ++
bloom.c       | 331 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
bloom.h       | 243 ++++++++++++++++++++++++++++++++++++++++++
client3.c     | 230 ++++++++++++++++++++++++++++++++++++++++
client4.c     | 253 ++++++++++++++++++++++++++++++++++++++++++++
murmurhash2.c |  64 ++++++++++++
murmurhash2.h |   7 ++
package.yml   |   2 +-
server3.c     | 204 ++++++++++++++++++++++++++++++++++++
server4.c     | 162 ++++++++++++++++++++++++++++
10 files changed, 1502 insertions(+), 1 deletion(-)

Diffstat:
MREADME.md | 7+++++++
Abloom.c | 331+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abloom.h | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aclient3.c | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aclient4.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amurmurhash2.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amurmurhash2.h | 7+++++++
Mpackage.yml | 2+-
Aserver3.c | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aserver4.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 1502 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md @@ -24,3 +24,10 @@ The server identity is verified in the client, the trust is established on first The client can have identity keys which are verified by the server when provided during the key exchange. The server should store the client public identity key and link it to a user. I created `sel.c` to handle the various keys, there are functions that don't take keys as parameters, they use the keys stored in `sel.c` buffers. + +## `client3.c` and `server3.c` +The server keeps the same session keys and uses a bloom filter to detect when client are reusing session keys. + +## `client4.c` and `server4.c` +`server4.c` has the client public keys and if the client is valid when it connects. +The clients can request the server public key, if the server public key is already know, it can send the first encrypted message directly. diff --git a/bloom.c b/bloom.c @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2012-2022, Jyri J. Virkki + * All rights reserved. + * + * This file is under BSD license. See LICENSE file. + */ + +/* + * Refer to bloom.h for documentation on the public interfaces. + */ + +#include <assert.h> +#include <fcntl.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "bloom.h" +#include "murmurhash2.h" + +#define MAKESTRING(n) STRING(n) +#define STRING(n) #n +#define BLOOM_MAGIC "libbloom2" + +inline static int test_bit_set_bit(unsigned char * buf, + unsigned long int bit, int set_bit) +{ + unsigned long int byte = bit >> 3; + unsigned char c = buf[byte]; // expensive memory access + unsigned char mask = 1 << (bit % 8ul); + + if (c & mask) { + return 1; + } else { + if (set_bit) { + buf[byte] = c | mask; + } + return 0; + } +} + + +static int bloom_check_add(struct bloom * bloom, + const void * buffer, int len, int add) +{ + if (bloom->ready == 0) { + printf("bloom at %p not initialized!\n", (void *)bloom); + return -1; + } + + unsigned char hits = 0; + unsigned int a = murmurhash2(buffer, len, 0x9747b28c); + unsigned int b = murmurhash2(buffer, len, a); + unsigned long int x; + unsigned long int i; + + for (i = 0; i < bloom->hashes; i++) { + x = (a + b*i) % bloom->bits; + if (test_bit_set_bit(bloom->bf, x, add)) { + hits++; + } else if (!add) { + // Don't care about the presence of all the bits. Just our own. + return 0; + } + } + + if (hits == bloom->hashes) { + return 1; // 1 == element already in (or collision) + } + + return 0; +} + + +// DEPRECATED - Please migrate to bloom_init2. +int bloom_init(struct bloom * bloom, int entries, double error) +{ + return bloom_init2(bloom, (unsigned int)entries, error); +} + + +int bloom_init2(struct bloom * bloom, unsigned int entries, double error) +{ + if (sizeof(unsigned long int) < 8) { + printf("error: libbloom will not function correctly because\n"); + printf("sizeof(unsigned long int) == %ld\n", sizeof(unsigned long int)); + exit(1); + } + + memset(bloom, 0, sizeof(struct bloom)); + + if (entries < 1000 || error <= 0 || error >= 1) { + return 1; + } + + bloom->entries = entries; + bloom->error = error; + + double num = -log(bloom->error); + double denom = 0.480453013918201; // ln(2)^2 + bloom->bpe = (num / denom); + + long double dentries = (long double)entries; + long double allbits = dentries * bloom->bpe; + bloom->bits = (unsigned long int)allbits; + + if (bloom->bits % 8) { + bloom->bytes = (bloom->bits / 8) + 1; + } else { + bloom->bytes = bloom->bits / 8; + } + + bloom->hashes = (unsigned char)ceil(0.693147180559945 * bloom->bpe); // ln(2) + + bloom->bf = (unsigned char *)calloc(bloom->bytes, sizeof(unsigned char)); + if (bloom->bf == NULL) { // LCOV_EXCL_START + return 1; + } // LCOV_EXCL_STOP + + bloom->ready = 1; + + bloom->major = BLOOM_VERSION_MAJOR; + bloom->minor = BLOOM_VERSION_MINOR; + + return 0; +} + + +int bloom_check(struct bloom * bloom, const void * buffer, int len) +{ + return bloom_check_add(bloom, buffer, len, 0); +} + + +int bloom_add(struct bloom * bloom, const void * buffer, int len) +{ + return bloom_check_add(bloom, buffer, len, 1); +} + + +void bloom_print(struct bloom * bloom) +{ + printf("bloom at %p\n", (void *)bloom); + if (!bloom->ready) { printf(" *** NOT READY ***\n"); } + printf(" ->version = %d.%d\n", bloom->major, bloom->minor); + printf(" ->entries = %u\n", bloom->entries); + printf(" ->error = %f\n", bloom->error); + printf(" ->bits = %lu\n", bloom->bits); + printf(" ->bits per elem = %f\n", bloom->bpe); + printf(" ->bytes = %lu", bloom->bytes); + unsigned int KB = bloom->bytes / 1024; + unsigned int MB = KB / 1024; + printf(" (%u KB, %u MB)\n", KB, MB); + printf(" ->hash functions = %d\n", bloom->hashes); +} + + +void bloom_free(struct bloom * bloom) +{ + if (bloom->ready) { + free(bloom->bf); + } + bloom->ready = 0; +} + + +int bloom_reset(struct bloom * bloom) +{ + if (!bloom->ready) return 1; + memset(bloom->bf, 0, bloom->bytes); + return 0; +} + + +int bloom_save(struct bloom * bloom, char * filename) +{ + if (filename == NULL || filename[0] == 0) { + return 1; + } + + int fd = open(filename, O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + return 1; + } + + ssize_t out = write(fd, BLOOM_MAGIC, strlen(BLOOM_MAGIC)); + if (out != strlen(BLOOM_MAGIC)) { goto save_error; } // LCOV_EXCL_LINE + + uint16_t size = sizeof(struct bloom); + out = write(fd, &size, sizeof(uint16_t)); + if (out != sizeof(uint16_t)) { goto save_error; } // LCOV_EXCL_LINE + + out = write(fd, bloom, sizeof(struct bloom)); + if (out != sizeof(struct bloom)) { goto save_error; } // LCOV_EXCL_LINE + + out = write(fd, bloom->bf, bloom->bytes); + if (out != bloom->bytes) { goto save_error; } // LCOV_EXCL_LINE + + close(fd); + return 0; + // LCOV_EXCL_START + save_error: + close(fd); + return 1; + // LCOV_EXCL_STOP +} + + +int bloom_load(struct bloom * bloom, char * filename) +{ + int rv = 0; + + if (filename == NULL || filename[0] == 0) { return 1; } + if (bloom == NULL) { return 2; } + + memset(bloom, 0, sizeof(struct bloom)); + + int fd = open(filename, O_RDONLY); + if (fd < 0) { return 3; } + + char line[30]; + memset(line, 0, 30); + ssize_t in = read(fd, line, strlen(BLOOM_MAGIC)); + + if (in != strlen(BLOOM_MAGIC)) { + rv = 4; + goto load_error; + } + + if (strncmp(line, BLOOM_MAGIC, strlen(BLOOM_MAGIC))) { + rv = 5; + goto load_error; + } + + uint16_t size; + in = read(fd, &size, sizeof(uint16_t)); + if (in != sizeof(uint16_t)) { + rv = 6; + goto load_error; + } + + if (size != sizeof(struct bloom)) { + rv = 7; + goto load_error; + } + + in = read(fd, bloom, sizeof(struct bloom)); + if (in != sizeof(struct bloom)) { + rv = 8; + goto load_error; + } + + bloom->bf = NULL; + if (bloom->major != BLOOM_VERSION_MAJOR) { + rv = 9; + goto load_error; + } + + bloom->bf = (unsigned char *)malloc(bloom->bytes); + if (bloom->bf == NULL) { rv = 10; goto load_error; } // LCOV_EXCL_LINE + + in = read(fd, bloom->bf, bloom->bytes); + if (in != bloom->bytes) { + rv = 11; + free(bloom->bf); + bloom->bf = NULL; + goto load_error; + } + + close(fd); + return rv; + + load_error: + close(fd); + bloom->ready = 0; + return rv; +} + + +int bloom_merge(struct bloom * bloom_dest, struct bloom * bloom_src) +{ + if (bloom_dest->ready == 0) { + printf("bloom at %p not initialized!\n", (void *)bloom_dest); + return -1; + } + + if (bloom_src->ready == 0) { + printf("bloom at %p not initialized!\n", (void *)bloom_src); + return -1; + } + + if (bloom_dest->entries != bloom_src->entries) { + return 1; + } + + if (bloom_dest->error != bloom_src->error) { + return 1; + } + + if (bloom_dest->major != bloom_src->major) { + return 1; + } + + if (bloom_dest->minor != bloom_src->minor) { + return 1; + } + + // Not really possible if properly used but check anyway to avoid the + // possibility of buffer overruns. + if (bloom_dest->bytes != bloom_src->bytes) { + return 1; // LCOV_EXCL_LINE + } + + unsigned long int p; + for (p = 0; p < bloom_dest->bytes; p++) { + bloom_dest->bf[p] |= bloom_src->bf[p]; + } + + return 0; +} + + +const char * bloom_version() +{ + return MAKESTRING(BLOOM_VERSION); +} diff --git a/bloom.h b/bloom.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2012-2022, Jyri J. Virkki + * All rights reserved. + * + * This file is under BSD license. See LICENSE file. + */ + +#ifndef _BLOOM_H +#define _BLOOM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BLOOM_VERSION_MAJOR 2 +#define BLOOM_VERSION_MINOR 0 + +#define NULL_BLOOM_FILTER { 0, 0, 0, 0, 0.0, 0, 0, 0, 0.0, NULL } + +#define ENTRIES_T unsigned int +#define BYTES_T unsigned long int +#define BITS_T unsigned long int + + +/** *************************************************************************** + * Structure to keep track of one bloom filter. Caller needs to + * allocate this and pass it to the functions below. First call for + * every struct must be to bloom_init(). + * + */ +struct bloom +{ + // These fields are part of the public interface of this structure. + // Client code may read these values if desired. Client code MUST NOT + // modify any of these. + unsigned int entries; + unsigned long int bits; + unsigned long int bytes; + unsigned char hashes; + double error; + + // Fields below are private to the implementation. These may go away or + // change incompatibly at any moment. Client code MUST NOT access or rely + // on these. + unsigned char ready; + unsigned char major; + unsigned char minor; + double bpe; + unsigned char * bf; +}; + + +/** *************************************************************************** + * Initialize the bloom filter for use. + * + * The filter is initialized with a bit field and number of hash functions + * according to the computations from the wikipedia entry: + * http://en.wikipedia.org/wiki/Bloom_filter + * + * Optimal number of bits is: + * bits = (entries * ln(error)) / ln(2)^2 + * + * Optimal number of hash functions is: + * hashes = bpe * ln(2) + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * entries - The expected number of entries which will be inserted. + * Must be at least 1000 (in practice, likely much larger). + * error - Probability of collision (as long as entries are not + * exceeded). + * + * Return: + * ------- + * 0 - on success + * 1 - on failure + * + */ +int bloom_init2(struct bloom * bloom, unsigned int entries, double error); + + +/** + * DEPRECATED. + * Kept for compatibility with libbloom v.1. To be removed in v3.0. + * + */ +int bloom_init(struct bloom * bloom, int entries, double error); + + +/** *************************************************************************** + * Check if the given element is in the bloom filter. Remember this may + * return false positive if a collision occurred. + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * buffer - Pointer to buffer containing element to check. + * len - Size of 'buffer'. + * + * Return: + * ------- + * 0 - element is not present + * 1 - element is present (or false positive due to collision) + * -1 - bloom not initialized + * + */ +int bloom_check(struct bloom * bloom, const void * buffer, int len); + + +/** *************************************************************************** + * Add the given element to the bloom filter. + * The return code indicates if the element (or a collision) was already in, + * so for the common check+add use case, no need to call check separately. + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * buffer - Pointer to buffer containing element to add. + * len - Size of 'buffer'. + * + * Return: + * ------- + * 0 - element was not present and was added + * 1 - element (or a collision) had already been added previously + * -1 - bloom not initialized + * + */ +int bloom_add(struct bloom * bloom, const void * buffer, int len); + + +/** *************************************************************************** + * Print (to stdout) info about this bloom filter. Debugging aid. + * + */ +void bloom_print(struct bloom * bloom); + + +/** *************************************************************************** + * Deallocate internal storage. + * + * Upon return, the bloom struct is no longer usable. You may call bloom_init + * again on the same struct to reinitialize it again. + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * + * Return: none + * + */ +void bloom_free(struct bloom * bloom); + + +/** *************************************************************************** + * Erase internal storage. + * + * Erases all elements. Upon return, the bloom struct returns to its initial + * (initialized) state. + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * + * Return: + * 0 - on success + * 1 - on failure + * + */ +int bloom_reset(struct bloom * bloom); + + +/** *************************************************************************** + * Save a bloom filter to a file. + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * filename - Create (or overwrite) bloom data to this file. + * + * Return: + * 0 - on success + * 1 - on failure + * + */ +int bloom_save(struct bloom * bloom, char * filename); + + +/** *************************************************************************** + * Load a bloom filter from a file. + * + * This functions loads a file previously saved with bloom_save(). + * + * Parameters: + * ----------- + * bloom - Pointer to an allocated struct bloom (see above). + * filename - Load bloom filter data from this file. + * + * Return: + * 0 - on success + * > 0 - on failure + * + */ +int bloom_load(struct bloom * bloom, char * filename); + + +/** *************************************************************************** + * Merge two compatible bloom filters. + * + * On success, bloom_dest will contain all elements of bloom_src in addition + * to its own. The bloom_src bloom filter is never modified. + * + * Both bloom_dest and bloom_src must be initialized and both must have + * identical parameters. + * + * Parameters: + * ----------- + * bloom_dest - will contain the merged elements from bloom_src + * bloom_src - its elements will be merged into bloom_dest + * + * Return: + * ------- + * 0 - on success + * 1 - incompatible bloom filters + * -1 - bloom not initialized + * + */ +int bloom_merge(struct bloom * bloom_dest, struct bloom * bloom_src); + + +/** *************************************************************************** + * Returns version string compiled into library. + * + * Return: version string + * + */ +const char * bloom_version(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/client3.c b/client3.c @@ -0,0 +1,230 @@ +#! /usr/bin/env sheepy + +/* +client2 and server2 are following request/response model. + +Steps in client +- load session key - keep same session key for all sessions +- connect to server +- send signature, identity public key, session public key, write 0 for id key and signature if + the client doesn't have an id +- check server identity with signature, store nonce +- store remote public key +- send encrypted message +- get encrypted response + +Steps in server +- generate keys +- setup bloom filter +- start event loop +- store remote session public key +- check identity with signature +- send signature, identity public key, session public key, nonce +- get encrypted message +- send encrypted response + +This version uses secret/symetric key encryption after the key exchange. + +For more nonce randomness, the server can provide the first nonce with the public keys when the session opens + +>> when sending large amount of data, use stream functions +>> use aead functions only if there are metadata +>> privelege separation: have a seperate process with the secrets +>> to change id: sign id public key, sign id public key and signature and the new id public key with the new id key + +*/ + +#include "libsheepyObject.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> + +#include "sel.h" + +int main(int ac, char **av) { + + setLogMode(LOG_FUNC); + + if (not selInit()) ret 1; + + // generate keys + bool isKnownServer = no; + const char* clientFilename = "client3.bin"; + const char* remoteIdFilename = "remoteId.bin"; + bool sendClientIdToServer = yes; + + // load client if possible + if (isPath(clientFilename)) { + logI("Client already has a session key"); + pError0(bLReadFile(clientFilename, &keys, sizeof(keys))); + } + else { + newKeys(); + pError0(writeFile(clientFilename, &keys, sizeof(keys))); + } + newSignKeys(); + + // load server id if possible + if (isPath(remoteIdFilename)) { + logI("Server is known"); + pError0(bLReadFile(remoteIdFilename, remoteId, sizeof(remoteId))); + isKnownServer = yes; + } + + char *msg = "Hello"; + + logI("message: %s\n", msg); + + + // connect to server + int sock; + struct sockaddr_in server; + struct hostent *hp; + int mysock; + char buf[128*1024]; + int rval; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + perror("Failed to create socket"); + } + + server.sin_family = AF_INET; + + hp = gethostbyname(av[1]); + if (hp==0) { + perror("gethostbyname failed"); + close(sock); + exit(1); + } + + memcpy(&server.sin_addr, hp->h_addr, hp->h_length); + server.sin_port = htons(5000); + + if (connect(sock,(struct sockaddr *) &server, sizeof(server))){ + perror("connect failed"); + close(sock); + exit(1); + } + + void snd(void *buf, size_t sz) { + logVarG(sz); + if(send(sock, buf, sz, 0) < 0){ + perror("send failed"); + close(sock); + exit(1); + } + } + + void rcv(void *buf, size_t sz) { + while (sz > 0) { + rval = recv(sock, buf, sz, MSG_WAITALL); + if (rval < 0) { + perror("reading message"); + exit(1); + } + else if (rval == 0) { + logI("Ending connection"); + close(sock); + exit(0); + } + sz -= rval; + } + logVarG(rval); + } + + // send public key + // store remote public key + void getServerPublicKey(void) { + u8 exchange[crypto_sign_BYTES + crypto_sign_PUBLICKEYBYTES + sizeof(keys.publicKey)] = init0Var; + memcpy(exchange+crypto_sign_BYTES+crypto_sign_PUBLICKEYBYTES, &keys.publicKey, sizeof(keys.publicKey)); + if (not sendClientIdToServer) { + snd(exchange, sizeof(exchange)); + } + else { + // send client id + // sign keys with id key + memcpy(exchange+crypto_sign_BYTES, &identityKeys.publicKey, sizeof(identityKeys.publicKey)); + u8 signed_message[sizeof(exchange)] = init0Var; + unsigned long long signed_message_len = 0; + crypto_sign(signed_message, &signed_message_len, exchange+crypto_sign_BYTES, sizeof(exchange)-crypto_sign_BYTES, identityKeys.secretKey); + snd(signed_message, sizeof(signed_message)); + } + + u8 serverInfo[crypto_sign_BYTES + crypto_sign_PUBLICKEYBYTES + sizeof(keys.remotePublicKey) + crypto_box_NONCEBYTES] = init0Var; + rcv(serverInfo, sizeof(serverInfo)); + + // check remote server + u8 unsigned_message[crypto_sign_PUBLICKEYBYTES + sizeof(keys.remotePublicKey) + crypto_box_NONCEBYTES] = init0Var; + unsigned long long unsigned_message_len; + const u8 *idPublicKey = remoteId; + if (not isKnownServer) { + // TOFU - trust on first use + idPublicKey = serverInfo + crypto_sign_BYTES; + } + + if (crypto_sign_open(unsigned_message, &unsigned_message_len, serverInfo, sizeof(serverInfo), idPublicKey) != 0) { + logE("Incorrect signature!"); + } + else { + logP("Correct signature"); + } + + memcpy(keys.remotePublicKey, unsigned_message + crypto_sign_PUBLICKEYBYTES, sizeof(keys.remotePublicKey)); + memcpy(sessionKeys.nonce, unsigned_message + crypto_sign_PUBLICKEYBYTES + sizeof(keys.remotePublicKey), crypto_box_NONCEBYTES); + + if (not isKnownServer) { + // store server id key + pError0(writeFile(remoteIdFilename, (u8*)idPublicKey, crypto_sign_PUBLICKEYBYTES)); + logI("Saved server id"); + } + + logD("Remote public key"); + loghex(keys.remotePublicKey, sizeof(keys.remotePublicKey)); + put; + + // key exchange + if (not computeSharedKeys(CLIENT_SESSION_KEYS)) { + logE("Invalid server key"); + exit(1); + } + } + + getServerPublicKey(); + + // send encrypted message + // *nonce is incremented by after sending or receiving a message + // *nonce is allowed to wrap from the max value + u64 *nonce = (u64*)sessionKeys.nonce; + logVarG(*nonce); + int len = selEncrypt(buf, sizeof(buf), msg, strlen(msg)); + inc *nonce; + + logVarG(len); + + snd(&len, sizeof(len)); + snd(buf, len); + + // get encrypted response + rcv(&len, sizeof(len)); + rcv(buf, len); + + u8 decrypted[1000]; + logVarG(*nonce); + len = selDecrypt(decrypted, sizeof(decrypted), buf, len); + inc *nonce; + + if (!len) { + logE("failed to decrypt"); + ret 1; + } + + decrypted[len] = 0; + + logI("decrypted: %s", decrypted); + + close(sock); +} +// vim: set expandtab ts=2 sw=2: diff --git a/client4.c b/client4.c @@ -0,0 +1,253 @@ +#! /usr/bin/env sheepy + +/* +client4 and server4 are following request/response model. + +Steps in client +- load key +- connect to server +- generate nonce +- send command, public key and nonce. Command is the first byte, + 0 to request server public key, + 1 to send first encrypted message directly +- store remote public key +- send encrypted message +- get encrypted response + +Steps in server +- generate keys +- start event loop +- store remote session public key +- check identity with signature +- send signature, identity public key, session public key, nonce +- get encrypted message +- send encrypted response + +This version uses secret/symetric key encryption after the key exchange. + +For more nonce randomness, the server can provide the first nonce with the public keys when the session opens + +>> when sending large amount of data, use stream functions +>> use aead functions only if there are metadata +>> privelege separation: have a seperate process with the secrets +>> to change id: sign id public key, sign id public key and signature and the new id public key with the new id key + +*/ + +#include "libsheepyObject.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> + +#include "sel.h" + +int main(int ac, char **av) { + + setLogMode(LOG_FUNC); + + if (not selInit()) ret 1; + + // generate keys + bool isKnownServer = no; + const char* clientSecretFilename = "client4Secret.bin"; + const char* clientPublicFilename = "client4Public.bin"; + bool sendClientIdToServer = yes; + + // load client key if possible + if (isPath(clientPublicFilename)) { + logI("Client already has a session key"); + pError0(bLReadFile(clientSecretFilename, keys.secretKey, sizeof(keys.secretKey))); + pError0(bLReadFile(clientPublicFilename, keys.publicKey, sizeof(keys.publicKey))); + } + else { + newKeys(); + pError0(writeFile(clientSecretFilename, keys.secretKey, sizeof(keys.secretKey))); + pError0(writeFile(clientPublicFilename, keys.publicKey, sizeof(keys.publicKey))); + } + newSignKeys(); + + char *msg = "Hello"; + + logI("message: %s\n", msg); + + + // connect to server + int sock; + struct sockaddr_in server; + struct hostent *hp; + int mysock; + char buf[128*1024]; + int rval; + + void connectToServer(void) { + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + perror("Failed to create socket"); + } + + server.sin_family = AF_INET; + + hp = gethostbyname(av[1]); + if (hp==0) { + perror("gethostbyname failed"); + close(sock); + exit(1); + } + + memcpy(&server.sin_addr, hp->h_addr, hp->h_length); + server.sin_port = htons(5000); + + if (connect(sock,(struct sockaddr *) &server, sizeof(server))){ + perror("connect failed"); + close(sock); + exit(1); + } + } + connectToServer(); + + void snd(void *buf, size_t sz) { + logVarG(sz); + if(send(sock, buf, sz, 0) < 0){ + perror("send failed"); + close(sock); + exit(1); + } + } + + void rcv(void *buf, size_t sz) { + while (sz > 0) { + rval = recv(sock, buf, sz, MSG_WAITALL); + if (rval < 0) { + perror("reading message"); + exit(1); + } + else if (rval == 0) { + logI("Ending connection"); + close(sock); + exit(0); + } + sz -= rval; + } + logVarG(rval); + } + + // send public key + // store remote public key + void getServerPublicKey(void) { + randombytes_buf(keys.nonce, sizeof(keys.nonce)); + u8 exchange[1 + sizeof(keys.publicKey) + sizeof(keys.nonce)] = init0Var; + memcpy(exchange + 1, &keys.publicKey, sizeof(keys.publicKey)); + memcpy(exchange + 1 + sizeof(keys.publicKey), &keys.nonce, sizeof(keys.nonce)); + snd(exchange, sizeof(exchange)); + + u8 serverInfo[sizeof(keys.remotePublicKey)] = init0Var; + rcv(serverInfo, sizeof(serverInfo)); + + memcpy(keys.remotePublicKey, serverInfo, sizeof(keys.remotePublicKey)); + + logD("Remote public key"); + loghex(keys.remotePublicKey, sizeof(keys.remotePublicKey)); + put; + + // key exchange + if (not computeSharedKeys(CLIENT_SESSION_KEYS)) { + logE("Invalid server key"); + exit(1); + } + } + + getServerPublicKey(); + + // send encrypted message + // *nonce is incremented by after sending or receiving a message + // *nonce is allowed to wrap from the max value + u64 *nonce = (u64*)keys.nonce; + logVarG(*nonce); + int len = selEncrypt(buf, sizeof(buf), msg, strlen(msg)); + inc *nonce; + + logVarG(len); + + snd(&len, sizeof(len)); + snd(buf, len); + + // get encrypted response + rcv(&len, sizeof(len)); + rcv(buf, len); + + u8 decrypted[1000]; + logVarG(*nonce); + len = selDecrypt(decrypted, sizeof(decrypted), buf, len); + inc *nonce; + + if (!len) { + logE("failed to decrypt"); + ret 1; + } + + decrypted[len] = 0; + + logI("decrypted: %s", decrypted); + + close(sock); + + + ////////////////////////////////////////// + // second session, server public is known + { + msg = "Second client session"; + connectToServer(); + + // send public key + randombytes_buf(keys.nonce, sizeof(keys.nonce)); + u8 exchange[1 + sizeof(keys.publicKey) + sizeof(keys.nonce)] = init0Var; + // command 1 send encrypted message directly + exchange[0] = 1; + memcpy(exchange + 1, &keys.publicKey, sizeof(keys.publicKey)); + memcpy(exchange + 1 + sizeof(keys.publicKey), &keys.nonce, sizeof(keys.nonce)); + snd(exchange, sizeof(exchange)); + + // key exchange + if (not computeSharedKeys(CLIENT_SESSION_KEYS)) { + logE("Invalid server key"); + exit(1); + } + + // send encrypted message + // *nonce is incremented by after sending or receiving a message + // *nonce is allowed to wrap from the max value + u64 *nonce = (u64*)keys.nonce; + logVarG(*nonce); + int len = selEncrypt(buf, sizeof(buf), msg, strlen(msg)); + inc *nonce; + + logVarG(len); + + snd(&len, sizeof(len)); + snd(buf, len); + + // get encrypted response + rcv(&len, sizeof(len)); + rcv(buf, len); + + u8 decrypted[1000]; + logVarG(*nonce); + len = selDecrypt(decrypted, sizeof(decrypted), buf, len); + inc *nonce; + + if (!len) { + logE("failed to decrypt"); + ret 1; + } + + decrypted[len] = 0; + + logI("decrypted: %s", decrypted); + + close(sock); + } + +} +// vim: set expandtab ts=2 sw=2: diff --git a/murmurhash2.c b/murmurhash2.c @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// MurmurHash2, by Austin Appleby + +// Note - This code makes a few assumptions about how your machine behaves - + +// 1. We can read a 4-byte value from any address without crashing +// 2. sizeof(int) == 4 + +// And it has a few limitations - + +// 1. It will not work incrementally. +// 2. It will not produce the same results on little-endian and big-endian +// machines. + +unsigned int murmurhash2(const void * key, int len, const unsigned int seed) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + const unsigned int m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + + unsigned int h = seed ^ len; + + // Mix 4 bytes at a time into the hash + + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + unsigned int k = *(unsigned int *)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} diff --git a/murmurhash2.h b/murmurhash2.h @@ -0,0 +1,7 @@ + +#ifndef _BLOOM_MURMURHASH2 +#define _BLOOM_MURMURHASH2 + +unsigned int murmurhash2(const void * key, int len, const unsigned int seed); + +#endif diff --git a/package.yml b/package.yml @@ -4,7 +4,7 @@ description: "prototypes" bin: ./sodiumTest.c #cflags: -DA -g3 -std=gnu11 -fPIC -pipe - lflags: /usr/local/lib/libsodium.a + lflags: /usr/local/lib/libsodium.a -lm # need -lm for bloom filter in server3.c repository: type: git url: git+https://noulin.net/git/sodiumTest.git diff --git a/server3.c b/server3.c @@ -0,0 +1,204 @@ +#! /usr/bin/env sheepy + +#include "libsheepyObject.h" + +#include <sys/socket.h> +#include <netinet/in.h> + +#include "sel.h" +#include "bloom.h" + +int main(int ac, char **av){ + + setLogMode(LOG_FUNC); + + if (not selInit()) ret 1; + + // initialize bloomfilter for 45 million sessions, 10% false positive + struct bloom bloom; + u32 filterCount = 0; + bloom_init2(&bloom, 45000000, 0.1); + + // generate id keys + newSignKeys(); + + + // start event loop + int sock; + struct sockaddr_in server; + int mysock; + char buf[128*1024]; + int rval; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + perror("Failed to create socket"); + ret 1; + } + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(5000); + + if (bind(sock, (struct sockaddr *) &server, sizeof(server))){ + perror("bind failed"); + ret 1; + } + + listen(sock, 5); + + logI("Server started"); + forever { + mysock = accept(sock, (struct sockaddr *)0, 0); + if (mysock == -1) + perror("accept failed"); + else { + + bool snd(void *buf, size_t sz) { + logVarG(sz); + if(send(mysock, buf, sz, 0) < 0){ + perror("send failed"); + close(mysock); + ret no; + } + ret yes; + } + + bool rcv(void *buf, size_t sz) { + while (sz > 0) { + // TODO add timeout + rval = recv(mysock, buf, sz, MSG_WAITALL); + if (rval < 0) { + perror("reading message"); + ret no; + } + else if (rval == 0) { + logI("Ending connection"); + close(mysock); + ret no; + } + sz -= rval; + } + ret yes; + } + + // public key for session + newKeys(); + randombytes_buf(sessionKeys.nonce, sizeof(sessionKeys.nonce)); + + // store remote public key + u8 clientInfo[crypto_sign_BYTES + crypto_sign_PUBLICKEYBYTES + sizeof(keys.remotePublicKey)] = init0Var; + rcv(clientInfo, sizeof(clientInfo)); + + // check if the clientInfo is signed + bool is0 = yes; + range(i, crypto_sign_BYTES + crypto_sign_PUBLICKEYBYTES) { + if (clientInfo[i]) { + is0 = no; + break; + } + } + + if (is0) { + logN("Client id not checked"); + } + else { + // check client + // id key should already be known + u8 unsigned_message[crypto_sign_PUBLICKEYBYTES + sizeof(keys.remotePublicKey)] = init0Var; + unsigned long long unsigned_message_len; + + if (crypto_sign_open(unsigned_message, &unsigned_message_len, clientInfo, sizeof(clientInfo), clientInfo + crypto_sign_BYTES) != 0) { + logE("Incorrect signature!"); + } + else { + logP("Correct client signature"); + } + } + + memcpy(keys.remotePublicKey, clientInfo + crypto_sign_BYTES + crypto_sign_PUBLICKEYBYTES, sizeof(keys.remotePublicKey)); + + // check if key is in bloom filter + if (filterCount == bloom.entries) { + logD("Reseting bloom filter"); + bloom_reset(&bloom); + filterCount = 0; + } + int bfr = bloom_check(&bloom, keys.remotePublicKey, sizeof(keys.remotePublicKey)); + if (bfr == -1) { + logE("Bloom filter is not initialized"); + } + elif (bfr == 1) { + logI("Client key already is already in bloom filter, request a new key"); + } + elif (bfr == 0) { + // if not add it to the bloom filter + bloom_add(&bloom, keys.remotePublicKey, sizeof(keys.remotePublicKey)); + inc filterCount; + } + + logD("Remote public key"); + loghex(keys.remotePublicKey, sizeof(keys.remotePublicKey)); + put; + + // send public key + u8 exchange[crypto_sign_PUBLICKEYBYTES + sizeof(keys.publicKey) + crypto_box_NONCEBYTES] = init0Var; + u8 signed_message[crypto_sign_BYTES + sizeof(exchange)] = init0Var; + unsigned long long signed_message_len = 0; + + memcpy(exchange, identityKeys.publicKey, crypto_sign_PUBLICKEYBYTES); + memcpy(exchange + crypto_sign_PUBLICKEYBYTES, keys.publicKey, sizeof(keys.publicKey)); + memcpy(exchange + crypto_sign_PUBLICKEYBYTES + sizeof(keys.publicKey), sessionKeys.nonce, sizeof(sessionKeys.nonce)); + + crypto_sign(signed_message, &signed_message_len, exchange, sizeof(exchange), identityKeys.secretKey); + + if (!snd(signed_message, sizeof(signed_message))) continue; + + // key exchange + if (not computeSharedKeys(SERVER_SESSION_KEYS)) { + logE("Invalid client key"); + exit(1); + } + + // get encrypted message + int len; + // *nonce is incremented by after sending or receiving a message + // *nonce is allowed to wrap from the max value + u64 *nonce = (u64*)sessionKeys.nonce; + if (!rcv(&len, sizeof(len))) continue; + rcv(buf, len); + + u8 decrypted[1000]; + logVarG(*nonce); + len = selDecrypt(decrypted, sizeof(decrypted), buf, len); + inc *nonce; + + if (!len) { + logE("failed to decrypt"); + close(mysock); + continue; + } + + decrypted[len] = 0; + + logI("decrypted: %s", decrypted); + + // send encrypted response + + char *msg = "OK"; + + logVarG(*nonce); + len = selEncrypt(buf, sizeof(buf), msg, strlen(msg)); + inc *nonce; + + logVarG(len); + + snd(&len, sizeof(len)); + snd(buf, len); + + close(mysock); + } + } + bloom_free(&bloom); +} +// vim: set expandtab ts=2 sw=2: diff --git a/server4.c b/server4.c @@ -0,0 +1,162 @@ +#! /usr/bin/env sheepy + +#include "libsheepyObject.h" + +#include <sys/socket.h> +#include <netinet/in.h> + +#include "sel.h" + +int main(int ac, char **av){ + + setLogMode(LOG_FUNC); + + if (not selInit()) ret 1; + + // generate id keys + newSignKeys(); + // public key + newKeys(); + + + const char* clientPublicFilename = "client4Public.bin"; + u8 knownClientPublicKey[crypto_box_PUBLICKEYBYTES]; + if (isPath(clientPublicFilename)) { + logI("Server found known client session key"); + pError0(bLReadFile(clientPublicFilename, knownClientPublicKey, sizeof(knownClientPublicKey))); + } + else { + logE("known client key not found, run client4.c once before starting the server"); + } + + + // start event loop + int sock; + struct sockaddr_in server; + int mysock; + char buf[128*1024]; + int rval; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0){ + perror("Failed to create socket"); + ret 1; + } + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(5000); + + if (bind(sock, (struct sockaddr *) &server, sizeof(server))){ + perror("bind failed"); + ret 1; + } + + listen(sock, 5); + + logI("Server started"); + forever { + mysock = accept(sock, (struct sockaddr *)0, 0); + if (mysock == -1) + perror("accept failed"); + else { + + bool snd(void *buf, size_t sz) { + logVarG(sz); + if(send(mysock, buf, sz, 0) < 0){ + perror("send failed"); + close(mysock); + ret no; + } + ret yes; + } + + bool rcv(void *buf, size_t sz) { + while (sz > 0) { + // TODO add timeout + rval = recv(mysock, buf, sz, MSG_WAITALL); + if (rval < 0) { + perror("reading message"); + ret no; + } + else if (rval == 0) { + logI("Ending connection"); + close(mysock); + ret no; + } + sz -= rval; + } + ret yes; + } + + // store remote public key + u8 clientInfo[1 + sizeof(keys.remotePublicKey) + sizeof(keys.nonce)] = init0Var; + rcv(clientInfo, sizeof(clientInfo)); + + // check if client public key is known + if (memcmp(knownClientPublicKey, clientInfo +1, sizeof(knownClientPublicKey))) { + logE("unknown client"); + } + + memcpy(keys.remotePublicKey, clientInfo + 1, sizeof(keys.remotePublicKey)); + memcpy(keys.nonce, clientInfo + 1 + sizeof(keys.remotePublicKey), sizeof(keys.nonce)); + + logD("Remote public key"); + loghex(keys.remotePublicKey, sizeof(keys.remotePublicKey)); + put; + + // send public key + if (clientInfo[0] == 0) { + logD("command 0 - send server public key"); + u8 exchange[sizeof(keys.publicKey)] = init0Var; + memcpy(exchange, keys.publicKey, sizeof(keys.publicKey)); + if (!snd(exchange, sizeof(exchange))) continue; + } + + // key exchange + if (not computeSharedKeys(SERVER_SESSION_KEYS)) { + logE("Invalid client key"); + exit(1); + } + + // get encrypted message + int len; + // *nonce is incremented by after sending or receiving a message + // *nonce is allowed to wrap from the max value + u64 *nonce = (u64*)keys.nonce; + if (!rcv(&len, sizeof(len))) continue; + rcv(buf, len); + + u8 decrypted[1000]; + logVarG(*nonce); + len = selDecrypt(decrypted, sizeof(decrypted), buf, len); + inc *nonce; + + if (!len) { + logE("failed to decrypt"); + close(mysock); + continue; + } + + decrypted[len] = 0; + + logI("decrypted: %s", decrypted); + + // send encrypted response + + char *msg = "OK"; + + logVarG(*nonce); + len = selEncrypt(buf, sizeof(buf), msg, strlen(msg)); + inc *nonce; + + logVarG(len); + + snd(&len, sizeof(len)); + snd(buf, len); + + close(mysock); + } + } +} +// vim: set expandtab ts=2 sw=2: