| @@ -30,3 +30,6 @@ install: | |||
| uninstall: | |||
| rm -f /usr/local/lib/libchat6.so | |||
| test: | |||
| $(MAKE) -C src test | |||
| @@ -51,6 +51,13 @@ NOTE: All encrypted data is represented as base64. | |||
| * neighbour: ipaddress,port | |||
| * metric: a 32 bits unsigned integer representing the sum of all latencies between the client and the announcing client. | |||
| ### begin and end of session | |||
| ``` | |||
| HELLO {version} | |||
| BYE | |||
| ``` | |||
| ### path anouncement and withdrawal | |||
| ``` | |||
| @@ -66,10 +73,12 @@ NOTE: All encrypted data is represented as base64. | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> KEEPALIVE RESPONSE | |||
| ``` | |||
| ### MESSAGE | |||
| ### FRIENDS & MESSAGES | |||
| ``` | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> MESSAGE SEND {target_hash, chatid, msgid, key, message} | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> FRIEND REQUEST {target_hash, key} | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> FRIEND CONFIRM {target_hash, key} | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> MESSAGE SEND {target_hash, chatid, msgid, message} | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> MESSAGE RECV {target_hash, chatid, msgid} | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> MESSAGE READ {target_hash, chatid, msgid} | |||
| ``` | |||
| @@ -79,4 +88,4 @@ NOTE: All encrypted data is represented as base64. | |||
| ``` | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> NEIGHBORS REQUEST # request the neighbours of your neighbour | |||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> NEIGHBORS RESPONSE {neighbour,..} # neighbours list, multiple time the same key | |||
| ``` | |||
| ``` | |||
| @@ -18,12 +18,17 @@ CC := gcc | |||
| #CCFLAGS := -fPIC -Wall -Werror -std=c11 -pedantic -O2 | |||
| CCFLAGS := -fPIC -Wall -Werror -std=c11 -pedantic -g | |||
| LDFLAGS := | |||
| LIBS := -lchat6 -lssl -lpthread -lcrypto | |||
| LIBS := -lchat6 -lssl -lpthread -lcrypto -lsodium | |||
| TARGETS:= libchat6.so libchat6.a | |||
| MAINS := $(.o, $(TARGETS) ) | |||
| OBJ := \ | |||
| lc6_ssl.o \ | |||
| lc6_node.o \ | |||
| lc6_config.o \ | |||
| lc6_helpers.o \ | |||
| lc6_user.o \ | |||
| lc6_crypto.o \ | |||
| lc6_base64.o \ | |||
| libchat6.o \ | |||
| $(MAINS) | |||
| DEPS := | |||
| @@ -46,5 +51,5 @@ libchat6.a: $(OBJ) | |||
| ranlib $@ | |||
| test: test.o | |||
| $(CC) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| $(CC) $(CCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||
| @@ -0,0 +1,156 @@ | |||
| /* | |||
| * Base64 encoding/decoding (RFC1341) | |||
| * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi> | |||
| * | |||
| * This software may be distributed under the terms of the BSD license. | |||
| * See README for more details. | |||
| */ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include "lc6_base64.h" | |||
| static const unsigned char base64_table[65] = | |||
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | |||
| /** | |||
| * base64_encode - Base64 encode | |||
| * @src: Data to be encoded | |||
| * @len: Length of the data to be encoded | |||
| * @out_len: Pointer to output length variable, or %NULL if not used | |||
| * Returns: Allocated buffer of out_len bytes of encoded data, | |||
| * or %NULL on failure | |||
| * | |||
| * Caller is responsible for freeing the returned buffer. Returned buffer is | |||
| * nul terminated to make it easier to use as a C string. The nul terminator is | |||
| * not included in out_len. | |||
| */ | |||
| unsigned char* base64_encode(const unsigned char *src, size_t len, | |||
| size_t *out_len) | |||
| { | |||
| unsigned char *out, *pos; | |||
| const unsigned char *end, *in; | |||
| size_t olen; | |||
| int line_len; | |||
| olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ | |||
| olen += olen / 72; /* line feeds */ | |||
| olen++; /* nul termination */ | |||
| if (olen < len) | |||
| return NULL; /* integer overflow */ | |||
| out = malloc(olen); | |||
| if (out == NULL) | |||
| return NULL; | |||
| end = src + len; | |||
| in = src; | |||
| pos = out; | |||
| line_len = 0; | |||
| while (end - in >= 3) { | |||
| *pos++ = base64_table[in[0] >> 2]; | |||
| *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; | |||
| *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; | |||
| *pos++ = base64_table[in[2] & 0x3f]; | |||
| in += 3; | |||
| line_len += 4; | |||
| if (line_len >= 72) { | |||
| *pos++ = '\n'; | |||
| line_len = 0; | |||
| } | |||
| } | |||
| if (end - in) { | |||
| *pos++ = base64_table[in[0] >> 2]; | |||
| if (end - in == 1) { | |||
| *pos++ = base64_table[(in[0] & 0x03) << 4]; | |||
| *pos++ = '='; | |||
| } else { | |||
| *pos++ = base64_table[((in[0] & 0x03) << 4) | | |||
| (in[1] >> 4)]; | |||
| *pos++ = base64_table[(in[1] & 0x0f) << 2]; | |||
| } | |||
| *pos++ = '='; | |||
| line_len += 4; | |||
| } | |||
| if (line_len) | |||
| *pos++ = '\n'; | |||
| *pos = '\0'; | |||
| if (out_len) | |||
| *out_len = pos - out; | |||
| return out; | |||
| } | |||
| /** | |||
| * base64_decode - Base64 decode | |||
| * @src: Data to be decoded | |||
| * @len: Length of the data to be decoded | |||
| * @out_len: Pointer to output length variable | |||
| * Returns: Allocated buffer of out_len bytes of decoded data, | |||
| * or %NULL on failure | |||
| * | |||
| * Caller is responsible for freeing the returned buffer. | |||
| */ | |||
| unsigned char * base64_decode(const unsigned char *src, size_t len, | |||
| size_t *out_len) | |||
| { | |||
| unsigned char dtable[256], *out, *pos, block[4], tmp; | |||
| size_t i, count, olen; | |||
| int pad = 0; | |||
| memset(dtable, 0x80, 256); | |||
| for (i = 0; i < sizeof(base64_table) - 1; i++) | |||
| dtable[base64_table[i]] = (unsigned char) i; | |||
| dtable['='] = 0; | |||
| count = 0; | |||
| for (i = 0; i < len; i++) { | |||
| if (dtable[src[i]] != 0x80) | |||
| count++; | |||
| } | |||
| if (count == 0 || count % 4) | |||
| return NULL; | |||
| olen = count / 4 * 3; | |||
| pos = out = malloc(olen); | |||
| if (out == NULL) | |||
| return NULL; | |||
| count = 0; | |||
| for (i = 0; i < len; i++) { | |||
| tmp = dtable[src[i]]; | |||
| if (tmp == 0x80) | |||
| continue; | |||
| if (src[i] == '=') | |||
| pad++; | |||
| block[count] = tmp; | |||
| count++; | |||
| if (count == 4) { | |||
| *pos++ = (block[0] << 2) | (block[1] >> 4); | |||
| *pos++ = (block[1] << 4) | (block[2] >> 2); | |||
| *pos++ = (block[2] << 6) | block[3]; | |||
| count = 0; | |||
| if (pad) { | |||
| if (pad == 1) | |||
| pos--; | |||
| else if (pad == 2) | |||
| pos -= 2; | |||
| else { | |||
| /* Invalid padding */ | |||
| free(out); | |||
| return NULL; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| *out_len = pos - out; | |||
| return out; | |||
| } | |||
| @@ -0,0 +1,8 @@ | |||
| #ifndef LC6_BASE64_H | |||
| #define LC6_BASE64_H | |||
| unsigned char* base64_encode(const unsigned char*, size_t, size_t*); | |||
| unsigned char* base64_decode(const unsigned char*, size_t, size_t*); | |||
| #endif | |||
| @@ -0,0 +1,96 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include "lc6_config.h" | |||
| #include "lc6_crypto.h" | |||
| LC6_CTX* lc6config_load(unsigned char *password, char *path) { | |||
| LC6_CTX *conf; | |||
| conf = malloc(sizeof(LC6_CTX)); | |||
| assert(conf); | |||
| memset(conf, '\0', sizeof(LC6_CTX)); | |||
| lc6config_load_file(conf, path, LC6_CONFIG_BOOTSTRAP, password); | |||
| lc6config_load_file(conf, path, LC6_CONFIG_USER, password); | |||
| lc6config_load_file(conf, path, LC6_CONFIG_FRIENDS, password); | |||
| // complete defaults | |||
| // conf->path | |||
| strncpy(conf->path, path, sizeof(conf->path)); | |||
| conf->path[sizeof(conf->path)-1]='\0'; | |||
| // conf->node | |||
| // generate new keys at every startup. | |||
| // those are only used for direct signaling to | |||
| // the neighbours. | |||
| conf->node = malloc(sizeof(LC6_USER)); | |||
| assert(conf->node); | |||
| lc6crypto_genuserkey(conf->node); | |||
| // conf->user may stay NULL | |||
| // conf->friends maybe you're all alone, that's ok tho. | |||
| // conf->bootstrap | |||
| // this is a major issue if we can't bootstrap | |||
| if ( !conf->bootstrap ) | |||
| return NULL; | |||
| return conf; | |||
| } | |||
| void lc6config_load_file( | |||
| LC6_CTX *conf, | |||
| char *path, | |||
| char *filename, | |||
| unsigned char *password) | |||
| { | |||
| unsigned char *data = NULL; | |||
| int data_len = 0; | |||
| char file[MAXPATHLEN]; | |||
| snprintf(file, sizeof(file), "%s/%s", path, filename); | |||
| if ( lc6crypto_readfile(file, password, &data, &data_len) != 0 ) | |||
| return; | |||
| if ( strcmp(filename, LC6_CONFIG_BOOTSTRAP) == 0 ) { | |||
| LC6_BOOTSTRAP *prev = NULL; | |||
| int offset = 0; | |||
| conf->bootstrap = (LC6_BOOTSTRAP*)data; | |||
| while(offset<data_len/sizeof(LC6_BOOTSTRAP)) { | |||
| LC6_BOOTSTRAP *node = conf->bootstrap + offset; | |||
| node->prev = prev; | |||
| node->next = NULL; | |||
| node->prev->next = node; | |||
| prev = node; | |||
| offset++; | |||
| } | |||
| } | |||
| else if ( strcmp(filename, LC6_CONFIG_USER) == 0 ) { | |||
| conf->user = (LC6_USER*)data; | |||
| } | |||
| else if ( strcmp(filename, LC6_CONFIG_FRIENDS) == 0 ) { | |||
| LC6_USER *prev = NULL; | |||
| int offset = 0; | |||
| conf->friends = (LC6_USER*)data; | |||
| while(offset<data_len/sizeof(LC6_USER)) { | |||
| LC6_USER *node = conf->friends + offset; | |||
| node->prev = prev; | |||
| node->next = NULL; | |||
| node->prev->next = node; | |||
| prev = node; | |||
| offset++; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| #ifndef LC6_CONFIG_H | |||
| #define LC6_CONFIG_H | |||
| #include <sys/param.h> | |||
| #include <arpa/inet.h> | |||
| #include "lc6_user.h" | |||
| #define LC6_CONFIG_BOOTSTRAP "bootstrap.bin" | |||
| #define LC6_CONFIG_USER "user.bin" | |||
| #define LC6_CONFIG_FRIENDS "friends.bin" | |||
| typedef struct LC6_BOOTSTRAP { | |||
| struct LC6_BOOTSTRAP *prev; | |||
| struct LC6_BOOTSTRAP *next; | |||
| time_t last_contact; | |||
| int af; | |||
| union addr { | |||
| struct in_addr inet; | |||
| struct in6_addr inet6; | |||
| } addr; | |||
| } LC6_BOOTSTRAP; | |||
| typedef struct LC6_CTX { | |||
| char path[MAXPATHLEN]; | |||
| LC6_USER *user; | |||
| LC6_USER *node; | |||
| LC6_USER *friends; | |||
| LC6_BOOTSTRAP *bootstrap; | |||
| } LC6_CTX; | |||
| LC6_CTX* lc6config_load(unsigned char *key, char *path); | |||
| void lc6config_load_file( | |||
| LC6_CTX *conf, | |||
| char *path, | |||
| char *filename, | |||
| unsigned char *password); | |||
| #endif | |||
| @@ -0,0 +1,195 @@ | |||
| #include <string.h> | |||
| #include <sodium.h> | |||
| #include <assert.h> | |||
| #include "lc6_crypto.h" | |||
| int lc6crypto_seeded = 0; | |||
| LC6_USER* lc6crypto_genuserkey(LC6_USER *user) { | |||
| assert(user); | |||
| user->pub_key = malloc(crypto_box_PUBLICKEYBYTES); | |||
| user->priv_key = malloc(crypto_box_SECRETKEYBYTES); | |||
| assert(user->pub_key); | |||
| assert(user->priv_key); | |||
| crypto_box_keypair(user->pub_key, user->priv_key); | |||
| return user; | |||
| } | |||
| int lc6crypto_public_encrypt( | |||
| const unsigned char *data, | |||
| const int data_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **enc_data, | |||
| unsigned char **nonce) | |||
| { | |||
| *nonce = malloc(crypto_box_NONCEBYTES); | |||
| assert(*nonce); | |||
| *enc_data = malloc(crypto_box_MACBYTES + data_len); | |||
| assert(*enc_data); | |||
| randombytes_buf(*nonce, crypto_box_NONCEBYTES); | |||
| if ( crypto_box_easy(*enc_data, data, data_len, *nonce, pub, priv) == -1 ) | |||
| return 0; | |||
| return crypto_box_MACBYTES + data_len; | |||
| } | |||
| int lc6crypto_private_decrypt( | |||
| const unsigned char *enc_data, | |||
| const int enc_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **data, | |||
| const unsigned char *nonce) | |||
| { | |||
| *data = malloc(enc_len - crypto_box_MACBYTES); | |||
| assert(data); | |||
| if ( crypto_box_open_easy(*data, enc_data, enc_len, nonce, pub, priv) != 0 ) | |||
| return 0; | |||
| return enc_len - crypto_box_MACBYTES; | |||
| } | |||
| int lc6crypto_private_encrypt( | |||
| const unsigned char *data, | |||
| const int data_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **enc_data, | |||
| unsigned char **nonce) | |||
| { | |||
| return lc6crypto_public_encrypt( | |||
| data, data_len, | |||
| pub, priv, | |||
| enc_data, nonce); | |||
| } | |||
| int lc6crypto_public_decrypt( | |||
| const unsigned char *enc_data, | |||
| const int enc_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **data, | |||
| const unsigned char *nonce) | |||
| { | |||
| return lc6crypto_private_decrypt( | |||
| enc_data, enc_len, | |||
| pub, priv, | |||
| data, nonce); | |||
| } | |||
| int lc6crypto_readfile( | |||
| const char *file, | |||
| const unsigned char *password, | |||
| unsigned char **data, | |||
| int *data_len) | |||
| { | |||
| FILE *fh; | |||
| unsigned char hash[crypto_generichash_BYTES]; | |||
| unsigned char *encrypted; | |||
| size_t total = 0; | |||
| crypto_generichash(hash, sizeof hash, password, strlen((char*)password), NULL, 0); | |||
| if ( ( fh = fopen(file, "r") ) == NULL ) | |||
| return -1; | |||
| while(1) { | |||
| unsigned char buf[4096]; | |||
| size_t len; | |||
| len = fread(buf, sizeof(buf), 1, fh); | |||
| if ( len == 0 && !feof(fh) ) { | |||
| fclose(fh); | |||
| if ( encrypted ) | |||
| free(encrypted); | |||
| return -1; | |||
| } | |||
| if ( !encrypted ) | |||
| encrypted = malloc(len); | |||
| else | |||
| encrypted = realloc(encrypted, total + len); | |||
| assert(encrypted); | |||
| memcpy(encrypted + total, buf, len); | |||
| total+=len; | |||
| } | |||
| fclose(fh); | |||
| *data_len = total - crypto_secretbox_NONCEBYTES; | |||
| *data = malloc(*data_len); | |||
| assert(*data); | |||
| memset(*data, '\0', total - crypto_secretbox_NONCEBYTES); | |||
| if (crypto_secretbox_open_easy(*data, encrypted + crypto_secretbox_NONCEBYTES, *data_len, encrypted, hash) != 0) { | |||
| free(encrypted); | |||
| free(*data); | |||
| return -1; | |||
| } | |||
| free(encrypted); | |||
| return 0; | |||
| } | |||
| int lc6crypto_writefile( | |||
| const char *file, | |||
| const unsigned char *password, | |||
| const unsigned char *data, | |||
| const int data_len) | |||
| { | |||
| FILE *fh; | |||
| unsigned char hash[crypto_generichash_BYTES]; | |||
| unsigned char nonce[crypto_secretbox_NONCEBYTES]; | |||
| unsigned char *encrypted; | |||
| int offset = 0; | |||
| crypto_generichash(hash, sizeof hash, password, strlen((char*)password), NULL, 0); | |||
| encrypted = malloc(data_len + crypto_secretbox_NONCEBYTES); | |||
| memcpy(encrypted, nonce, crypto_secretbox_NONCEBYTES); | |||
| crypto_secretbox_easy(encrypted + crypto_secretbox_NONCEBYTES, data, data_len, nonce, hash); | |||
| if ( ( fh = fopen(file, "w") ) == NULL ) | |||
| return -1; | |||
| if ( fwrite(encrypted + crypto_secretbox_NONCEBYTES + offset, data_len + crypto_secretbox_NONCEBYTES, 1, fh) != 1 ) { | |||
| free(encrypted); | |||
| return -1; | |||
| } | |||
| fclose(fh); | |||
| free(encrypted); | |||
| return 0; | |||
| } | |||
| unsigned char* lc6crypto_hash( | |||
| unsigned char *data, | |||
| int data_len) | |||
| { | |||
| unsigned char *hash = malloc(crypto_generichash_BYTES); | |||
| assert(hash); | |||
| crypto_generichash(hash, sizeof hash, data, data_len, NULL, 0); | |||
| return hash; | |||
| } | |||
| void lc6crypto_random(unsigned char *data, int data_len) { | |||
| randombytes_buf(data, data_len); | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| #ifndef LC6_SSL_H | |||
| #define LC6_SSL_H | |||
| #include "lc6_user.h" | |||
| int lc6crypto_public_encrypt( | |||
| const unsigned char *data, | |||
| const int data_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **enc_data, | |||
| unsigned char **nonce); | |||
| int lc6crypto_private_decrypt( | |||
| const unsigned char *enc_data, | |||
| const int enc_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **data, | |||
| const unsigned char *nonce); | |||
| int lc6crypto_private_encrypt( | |||
| const unsigned char *data, | |||
| const int data_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **enc_data, | |||
| unsigned char **nonce); | |||
| int lc6crypto_public_decrypt( | |||
| const unsigned char *enc_data, | |||
| const int enc_len, | |||
| const unsigned char *priv, | |||
| const unsigned char *pub, | |||
| unsigned char **data, | |||
| const unsigned char *nonce); | |||
| int lc6crypto_readfile( | |||
| const char *file, | |||
| const unsigned char *password, | |||
| unsigned char **data, | |||
| int *data_len); | |||
| int lc6crypto_writefile( | |||
| const char *file, | |||
| const unsigned char *password, | |||
| const unsigned char *data, | |||
| const int data_len); | |||
| LC6_USER* lc6crypto_genuserkey(LC6_USER*); | |||
| unsigned char* lc6crypto_hash(unsigned char*, int); | |||
| void lc6crypto_random(unsigned char*, int); | |||
| #endif | |||
| @@ -0,0 +1,217 @@ | |||
| #include <openssl/rsa.h> | |||
| #include <openssl/pem.h> | |||
| #include <openssl/sha.h> | |||
| #include <openssl/rand.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include "lc6_crypto.h" | |||
| int lc6crypto_seeded = 0; | |||
| libchat_user* lc6crypto_genuserkey(libchat_user *user) { | |||
| BIGNUM *bn; | |||
| RSA *rsa; | |||
| BIO *pub; | |||
| BIO *priv; | |||
| size_t publen; | |||
| size_t privlen; | |||
| assert(user); | |||
| bn = BN_new(); | |||
| BN_set_word(bn, RSA_F4); | |||
| rsa = RSA_new(); | |||
| RSA_generate_key_ex(rsa, 2048, bn, NULL); | |||
| priv = BIO_new(BIO_s_mem()); | |||
| pub = BIO_new(BIO_s_mem()); | |||
| PEM_write_bio_RSAPrivateKey(priv, rsa, NULL, NULL, 0, NULL, NULL); | |||
| PEM_write_bio_RSAPublicKey(pub, rsa); | |||
| RSA_free(rsa); | |||
| privlen = BIO_pending(priv); | |||
| publen = BIO_pending(pub); | |||
| user->priv_key = malloc(privlen); | |||
| user->pub_key = malloc(publen); | |||
| BIO_read(priv, user->priv_key, privlen); | |||
| BIO_read(pub, user->pub_key, publen); | |||
| user->priv_key[privlen-1] = '\0'; | |||
| user->pub_key[publen-1] = '\0'; | |||
| BIO_free_all(priv); | |||
| BIO_free_all(pub); | |||
| BN_free(bn); | |||
| return user; | |||
| } | |||
| RSA * __lc6crypto_createRSA(unsigned char *key, int public) | |||
| { | |||
| RSA *rsa= NULL; | |||
| BIO *keybio = NULL; | |||
| keybio = BIO_new_mem_buf(key, -1); | |||
| assert(keybio); | |||
| if(public) { | |||
| PEM_read_bio_RSA_PUBKEY(keybio, &rsa, NULL, NULL); | |||
| } | |||
| else { | |||
| PEM_read_bio_RSAPrivateKey(keybio, &rsa, NULL, NULL); | |||
| } | |||
| assert(rsa); | |||
| return rsa; | |||
| } | |||
| int lc6crypto_public_encrypt( | |||
| unsigned char *data, | |||
| int data_len, | |||
| unsigned char *key, | |||
| unsigned char *enc_data) | |||
| { | |||
| RSA *rsa = __lc6crypto_createRSA(key,1); | |||
| int result = RSA_public_encrypt(data_len, data, enc_data, rsa, RSA_PKCS1_OAEP_PADDING); | |||
| return result; | |||
| } | |||
| int lc6crypto_private_decrypt( | |||
| unsigned char *enc_data, | |||
| int enc_len, | |||
| unsigned char *key, | |||
| unsigned char *data) | |||
| { | |||
| RSA *rsa = __lc6crypto_createRSA(key,0); | |||
| int result = RSA_private_decrypt(enc_len, enc_data, data, rsa, RSA_PKCS1_OAEP_PADDING); | |||
| return result; | |||
| } | |||
| int lc6crypto_private_encrypt( | |||
| unsigned char *data, | |||
| int data_len, | |||
| unsigned char *key, | |||
| unsigned char *enc_data) | |||
| { | |||
| RSA *rsa = __lc6crypto_createRSA(key,0); | |||
| int result = RSA_private_encrypt(data_len, data, enc_data, rsa, RSA_PKCS1_OAEP_PADDING); | |||
| return result; | |||
| } | |||
| int lc6crypto_public_decrypt( | |||
| unsigned char *enc_data, | |||
| int enc_len, | |||
| unsigned char *key, | |||
| unsigned char *data) | |||
| { | |||
| RSA *rsa = __lc6crypto_createRSA(key,1); | |||
| int result = RSA_public_decrypt(enc_len, enc_data, data, rsa, RSA_PKCS1_OAEP_PADDING); | |||
| return result; | |||
| } | |||
| void lc6crypto_sha256( | |||
| unsigned char *data, | |||
| int data_len, | |||
| unsigned char *hash) | |||
| { | |||
| SHA256_CTX sha256; | |||
| SHA256_Init(&sha256); | |||
| SHA256_Update(&sha256, data, data_len); | |||
| SHA256_Final(hash, &sha256); | |||
| } | |||
| int lc6crypto_encrypt( | |||
| unsigned char *data, | |||
| int data_len, | |||
| unsigned char *encrypted, | |||
| unsigned char *key, | |||
| unsigned char *iv) | |||
| { | |||
| EVP_CIPHER_CTX *ctx; | |||
| int encrypted_len = 0; | |||
| int len = 0; | |||
| if(!(ctx = EVP_CIPHER_CTX_new())) | |||
| return -1; | |||
| if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) { | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return -1; | |||
| } | |||
| if(1 != EVP_EncryptUpdate(ctx, encrypted, &len, data, data_len)) { | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return -1; | |||
| } | |||
| encrypted_len += len; | |||
| if(1 != EVP_EncryptFinal_ex(ctx, encrypted + len, &len)) { | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return -1; | |||
| } | |||
| encrypted_len += len; | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return encrypted_len; | |||
| } | |||
| int lc6crypto_decrypt( | |||
| unsigned char *encrypted, | |||
| int encrypted_len, | |||
| unsigned char *data, | |||
| unsigned char *key, | |||
| unsigned char *iv) | |||
| { | |||
| EVP_CIPHER_CTX *ctx; | |||
| int data_len = 0; | |||
| int len = 0; | |||
| if(!(ctx = EVP_CIPHER_CTX_new())) | |||
| return -1; | |||
| if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv)) { | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return -1; | |||
| } | |||
| if(1 != EVP_DecryptUpdate(ctx, data, &len, encrypted, encrypted_len)) { | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return -1; | |||
| } | |||
| data_len += len; | |||
| if(1 != EVP_DecryptFinal_ex(ctx, encrypted + len, &len)) { | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return -1; | |||
| } | |||
| data_len += len; | |||
| EVP_CIPHER_CTX_free(ctx); | |||
| return data_len; | |||
| } | |||
| int lc6crypto_random(unsigned char *data, int data_len) { | |||
| if ( ! lc6crypto_seeded ) { | |||
| RAND_poll(); | |||
| lc6crypto_seeded=1; | |||
| } | |||
| if ( RAND_bytes(data, data_len) != 1 ) | |||
| return -1; | |||
| return data_len; | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| #include <stdio.h> | |||
| void lc6helpers_printhex(unsigned char *data, int data_len) { | |||
| int i; | |||
| for(i=0; i<data_len; i++) { | |||
| printf("%02x", data[i]); | |||
| } | |||
| } | |||
| void lc6helpers_banner(unsigned char *str) { | |||
| printf("\n"); | |||
| printf("################################################\n"); | |||
| printf("# %s\n", str); | |||
| printf("################################################\n"); | |||
| printf("\n"); | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| #ifndef LC6_HELPERS_H | |||
| #define LC6_HELPERS_H | |||
| void lc6helpers_printhex(unsigned char*, int); | |||
| void lc6helpers_banner(unsigned char*); | |||
| #endif | |||
| @@ -0,0 +1,29 @@ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <assert.h> | |||
| #include "lc6_crypto.h" | |||
| #include "lc6_node.h" | |||
| LC6_NODE* lc6node_create(void) { | |||
| LC6_NODE *node; | |||
| node = malloc(sizeof(LC6_NODE)); | |||
| assert(node); | |||
| memset(node, '\0', sizeof(LC6_NODE)); | |||
| return node; | |||
| } | |||
| void lc6node_free(LC6_NODE *node) { | |||
| if ( !node ) | |||
| return; | |||
| if ( node->pub_key ) | |||
| free(node->pub_key); | |||
| if ( node->priv_key ) | |||
| free(node->priv_key); | |||
| free(node); | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| #ifndef LC6_NODE_H | |||
| #define LC6_NODE_H | |||
| typedef struct LC6_NODE { | |||
| unsigned char *pub_key; | |||
| unsigned char *priv_key; | |||
| } LC6_NODE; | |||
| LC6_NODE* lc6node_create(void); | |||
| void lc6node_free(LC6_NODE *node); | |||
| #endif | |||
| @@ -1,56 +0,0 @@ | |||
| #include <openssl/rsa.h> | |||
| #include <openssl/pem.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include "lc6_ssl.h" | |||
| libchat_user* ssl_create_keypair(libchat_user *user) { | |||
| BIGNUM *bn; | |||
| RSA *rsa; | |||
| BIO *pub; | |||
| BIO *priv; | |||
| size_t publen; | |||
| size_t privlen; | |||
| if ( user == NULL ) { | |||
| user = malloc(sizeof(&user)); | |||
| assert(user); | |||
| memset(user, 0, sizeof(&user)); | |||
| } | |||
| bn = BN_new(); | |||
| BN_set_word(bn, RSA_F4); | |||
| rsa = RSA_new(); | |||
| RSA_generate_key_ex(rsa, 2048, bn, NULL); | |||
| priv = BIO_new(BIO_s_mem()); | |||
| pub = BIO_new(BIO_s_mem()); | |||
| PEM_write_bio_RSAPrivateKey(priv, rsa, NULL, NULL, 0, NULL, NULL); | |||
| PEM_write_bio_RSAPublicKey(pub, rsa); | |||
| RSA_free(rsa); | |||
| privlen = BIO_pending(priv); | |||
| publen = BIO_pending(pub); | |||
| user->priv_key = malloc(privlen+1); | |||
| user->pub_key = malloc(publen+1); | |||
| BIO_read(priv, user->priv_key, privlen); | |||
| BIO_read(pub, user->pub_key, publen); | |||
| user->priv_key[privlen] = '\0'; | |||
| user->pub_key[publen] = '\0'; | |||
| BIO_free_all(priv); | |||
| BIO_free_all(pub); | |||
| BN_free(bn); | |||
| // RSA_free(rsa); | |||
| return user; | |||
| } | |||
| @@ -1,8 +0,0 @@ | |||
| #ifndef LC6_SSL_H | |||
| #define LC6_SSL_H | |||
| #include "libchat6.h" | |||
| libchat_user* ssl_create_keypair(libchat_user*); | |||
| #endif | |||
| @@ -0,0 +1,33 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include "lc6_crypto.h" | |||
| #include "lc6_user.h" | |||
| LC6_USER* lc6user_create(void) { | |||
| LC6_USER *user = malloc(sizeof(LC6_USER)); | |||
| return lc6crypto_genuserkey(user); | |||
| } | |||
| void lc6user_free(LC6_USER *user) { | |||
| if ( !user ) | |||
| return; | |||
| if ( user->pub_key ) | |||
| free(user->pub_key); | |||
| if ( user->priv_key ) | |||
| free(user->priv_key); | |||
| if ( user->icon ) | |||
| free(user->icon); | |||
| if ( user->next ) | |||
| user->next->prev = user->prev; | |||
| if ( user->prev ) | |||
| user->prev->next = user->next; | |||
| free(user); | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| #ifndef LC6_USER_H | |||
| #define LC6_USER_H | |||
| typedef struct LC6_USER { | |||
| struct LC6_USER *prev; | |||
| struct LC6_USER *next; | |||
| unsigned char nickname[256]; | |||
| unsigned char *pub_key; | |||
| unsigned char *priv_key; | |||
| unsigned char *icon; | |||
| unsigned int status; | |||
| } LC6_USER; | |||
| LC6_USER* lc6user_create(void); | |||
| void lc6user_free(LC6_USER *user); | |||
| #endif | |||
| @@ -1,31 +1,20 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include "lc6_ssl.h" | |||
| #include "lc6_config.h" | |||
| LC6_CTX* libchat_init(unsigned char *password, char *path) { | |||
| int libchat_init(libchat_user *user) { | |||
| LC6_CTX *conf = lc6config_load(password, path); | |||
| if ( user == NULL ) | |||
| return LIBCHAT_ERROR_INPUT_NULL; | |||
| if ( !conf ) | |||
| return NULL; | |||
| return 0; | |||
| } | |||
| if ( !conf->user ) | |||
| conf->user = lc6user_create(); | |||
| libchat_user* libchat_create_user(libchat_user *user) { | |||
| return ssl_create_keypair(user); | |||
| return conf; | |||
| } | |||
| void libchat_free_user(libchat_user *user) { | |||
| if ( !user ) | |||
| return; | |||
| if ( user->pub_key ) | |||
| free(user->pub_key); | |||
| if ( user->priv_key ) | |||
| free(user->priv_key); | |||
| free(user); | |||
| } | |||
| @@ -1,19 +1,15 @@ | |||
| #ifndef LC6_LIBCHAT6_H | |||
| #define LC6_LIBCHAT6_H | |||
| typedef struct libchat_user { | |||
| char nickname[256]; | |||
| char *pub_key; | |||
| char *priv_key; | |||
| char icon[32768]; | |||
| } libchat_user; | |||
| enum LIBCHAT_ERRORS { | |||
| LIBCHAT_ERROR_INPUT_NULL = 1 | |||
| enum LIBCHAR_USER_STATUS { | |||
| LC6_STATUS_OFFLINE = 0, | |||
| LC6_STATUS_ONLINE, | |||
| LC6_STATUS_AWAY, | |||
| LC6_STATUS_TYPING | |||
| }; | |||
| int libchat_init(libchat_user*); | |||
| libchat_user* libchat_create_user(libchat_user*); | |||
| void libchat_free_user(libchat_user*); | |||
| typedef void *LIBCHAT; | |||
| LIBCHAT* libchat_init(unsigned char *password, char *path); | |||
| #endif | |||
| @@ -2,19 +2,94 @@ | |||
| #include <string.h> | |||
| #include <stdlib.h> | |||
| #include <sodium.h> | |||
| #include "libchat6.h" | |||
| #include "lc6_helpers.h" | |||
| #include "lc6_crypto.h" | |||
| #include "lc6_user.h" | |||
| int main(int argc, char **argv) { | |||
| libchat_user *user; | |||
| user = libchat_create_user(NULL); | |||
| unsigned char data[1024]; | |||
| unsigned char data2[1024]; | |||
| unsigned char *ptr_data = NULL; | |||
| unsigned char *ptr_data2 = NULL; | |||
| unsigned char *ptr_nonce = NULL; | |||
| int i, len; | |||
| LC6_USER *user; | |||
| lc6helpers_banner("Generating random bytes..."); | |||
| for(i=0; i<8; i++) { | |||
| printf("random bytes... "); | |||
| lc6crypto_random(data, 16); | |||
| lc6helpers_printhex(data, 16); | |||
| printf("\n"); | |||
| } | |||
| lc6helpers_banner("Generating SHA256 hashs..."); | |||
| for(i=0; i<8; i++) { | |||
| unsigned char *hash; | |||
| lc6crypto_random(data, 16); | |||
| hash = lc6crypto_hash(data, 16); | |||
| printf("random: "); | |||
| lc6helpers_printhex(data, 16); | |||
| printf(", hash: "); | |||
| lc6helpers_printhex(hash, 32); | |||
| printf("\n"); | |||
| free(hash); | |||
| } | |||
| lc6helpers_banner("Generating new user and keys..."); | |||
| user = lc6user_create(); | |||
| strcpy(user->nickname,"Päscu"); | |||
| printf("pub\n%s\n", user->pub_key); | |||
| printf("priv\n%s\n", user->priv_key); | |||
| printf("Private key\n"); | |||
| lc6helpers_printhex(user->priv_key, crypto_box_SECRETKEYBYTES); | |||
| printf("\n"); | |||
| printf("Public key\n"); | |||
| lc6helpers_printhex(user->pub_key, crypto_box_PUBLICKEYBYTES); | |||
| lc6helpers_banner("Public key encrypt, private key decrypt"); | |||
| strcpy(data,"This is a test message to be encrypted. It may contain UTF-8 chars like 🤪"); | |||
| printf("plain data: %s\n", data); | |||
| len = lc6crypto_public_encrypt(data, strlen(data), user->priv_key, user->pub_key, &ptr_data, &ptr_nonce); | |||
| printf("encrypted : "); | |||
| lc6helpers_printhex(ptr_data, len); | |||
| printf("\n"); | |||
| len = lc6crypto_private_decrypt(ptr_data, len, user->priv_key, user->pub_key, &ptr_data, ptr_nonce); | |||
| printf("decrypted : %s\n", ptr_data); | |||
| free(ptr_data); | |||
| free(ptr_nonce); | |||
| free(ptr_data2); | |||
| lc6helpers_banner("Private key encrypt, public key decrypt"); | |||
| strcpy(data,"This is a test message to be encrypted. It may contain UTF-8 chars like 🤪"); | |||
| printf("plain data: %s\n", data); | |||
| len = lc6crypto_private_encrypt(data, strlen(data), user->priv_key, user->pub_key, &ptr_data, &ptr_nonce); | |||
| printf("encrypted : "); | |||
| lc6helpers_printhex(ptr_data, len); | |||
| printf("\n"); | |||
| len = lc6crypto_public_decrypt(ptr_data, len, user->priv_key, user->pub_key, &ptr_data, ptr_nonce); | |||
| printf("decrypted : %s\n", ptr_data); | |||
| free(ptr_data); | |||
| free(ptr_nonce); | |||
| free(ptr_data2); | |||
| libchat_free_user(user); | |||
| lc6user_free(user); | |||
| user = NULL; | |||
| return 0; | |||