| uninstall: | uninstall: | ||||
| rm -f /usr/local/lib/libchat6.so | rm -f /usr/local/lib/libchat6.so | ||||
| test: | |||||
| $(MAKE) -C src test |
| * neighbour: ipaddress,port | * neighbour: ipaddress,port | ||||
| * metric: a 32 bits unsigned integer representing the sum of all latencies between the client and the announcing client. | * 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 | ### path anouncement and withdrawal | ||||
| ``` | ``` | ||||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> KEEPALIVE RESPONSE | <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 RECV {target_hash, chatid, msgid} | ||||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> MESSAGE READ {target_hash, chatid, msgid} | <TIMESTAMP> <PROOF> <MSGHASH> <KEY> MESSAGE READ {target_hash, chatid, msgid} | ||||
| ``` | ``` | ||||
| ``` | ``` | ||||
| <TIMESTAMP> <PROOF> <MSGHASH> <KEY> NEIGHBORS REQUEST # request the neighbours of your neighbour | <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 | <TIMESTAMP> <PROOF> <MSGHASH> <KEY> NEIGHBORS RESPONSE {neighbour,..} # neighbours list, multiple time the same key | ||||
| ``` | |||||
| ``` |
| #CCFLAGS := -fPIC -Wall -Werror -std=c11 -pedantic -O2 | #CCFLAGS := -fPIC -Wall -Werror -std=c11 -pedantic -O2 | ||||
| CCFLAGS := -fPIC -Wall -Werror -std=c11 -pedantic -g | CCFLAGS := -fPIC -Wall -Werror -std=c11 -pedantic -g | ||||
| LDFLAGS := | LDFLAGS := | ||||
| LIBS := -lchat6 -lssl -lpthread -lcrypto | |||||
| LIBS := -lchat6 -lssl -lpthread -lcrypto -lsodium | |||||
| TARGETS:= libchat6.so libchat6.a | TARGETS:= libchat6.so libchat6.a | ||||
| MAINS := $(.o, $(TARGETS) ) | MAINS := $(.o, $(TARGETS) ) | ||||
| OBJ := \ | OBJ := \ | ||||
| lc6_ssl.o \ | |||||
| lc6_node.o \ | |||||
| lc6_config.o \ | |||||
| lc6_helpers.o \ | |||||
| lc6_user.o \ | |||||
| lc6_crypto.o \ | |||||
| lc6_base64.o \ | |||||
| libchat6.o \ | libchat6.o \ | ||||
| $(MAINS) | $(MAINS) | ||||
| DEPS := | DEPS := | ||||
| ranlib $@ | ranlib $@ | ||||
| test: test.o | test: test.o | ||||
| $(CC) $(LDFLAGS) $^ -o $@ $(LIBS) | |||||
| $(CC) $(CCFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) | |||||
| /* | |||||
| * 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; | |||||
| } |
| #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 | |||||
| #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++; | |||||
| } | |||||
| } | |||||
| } |
| #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 |
| #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); | |||||
| } |
| #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 |
| #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; | |||||
| } |
| #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"); | |||||
| } |
| #ifndef LC6_HELPERS_H | |||||
| #define LC6_HELPERS_H | |||||
| void lc6helpers_printhex(unsigned char*, int); | |||||
| void lc6helpers_banner(unsigned char*); | |||||
| #endif |
| #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); | |||||
| } |
| #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 |
| #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; | |||||
| } |
| #ifndef LC6_SSL_H | |||||
| #define LC6_SSL_H | |||||
| #include "libchat6.h" | |||||
| libchat_user* ssl_create_keypair(libchat_user*); | |||||
| #endif |
| #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); | |||||
| } |
| #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 |
| #include <stdio.h> | #include <stdio.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | |||||
| #include <assert.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); | |||||
| } |
| #ifndef LC6_LIBCHAT6_H | #ifndef LC6_LIBCHAT6_H | ||||
| #define 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 | #endif |
| #include <string.h> | #include <string.h> | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <sodium.h> | |||||
| #include "libchat6.h" | #include "libchat6.h" | ||||
| #include "lc6_helpers.h" | |||||
| #include "lc6_crypto.h" | |||||
| #include "lc6_user.h" | |||||
| int main(int argc, char **argv) { | 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"); | 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; | user = NULL; | ||||
| return 0; | return 0; |