diff --git a/include/crypto.h b/include/crypto.h new file mode 100644 index 0000000..f7cd626 --- /dev/null +++ b/include/crypto.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +struct crypto_key; +struct crypto_cipher; +struct crypto_hash; + +enum crypto_cipher_type { + CRYPTO_CIPHER_INVALID = 0, + CRYPTO_CIPHER_AES128_ECB, +}; + +enum crypto_hash_type { + CRYPTO_HASH_INVALID = 0, + CRYPTO_HASH_MD5, +}; + +void crypto_dump_base64(const char* msg, const uint8_t* bytes, size_t len); + +void crypto_cleanup(void); + +// Key generation +struct crypto_key* crypto_key_new(int g, const uint8_t *p, uint32_t p_len, + const uint8_t* q, uint32_t q_len); +void crypto_key_del(struct crypto_key* key); + +int crypto_key_g(const struct crypto_key* key); +uint32_t crypto_key_p(const struct crypto_key* key, uint8_t* dst, + uint32_t dst_size); +uint32_t crypto_key_q(const struct crypto_key* key, uint8_t* dst, + uint32_t dst_size); + +struct crypto_key* crypto_keygen(void); + +// Diffie-Hellman +struct crypto_key* crypto_derive_public_key(const struct crypto_key* priv); +struct crypto_key* crypto_derive_shared_secret( + const struct crypto_key* own_secret, + const struct crypto_key* remote_public_key); + +// Ciphers +struct crypto_cipher* crypto_cipher_new(const uint8_t* key, + enum crypto_cipher_type type); +void crypto_cipher_del(struct crypto_cipher* self); + +bool crypto_cipher_encrypt(struct crypto_cipher* self, uint8_t* dst, + const uint8_t* src, size_t len); +bool crypto_cipher_decrypt(struct crypto_cipher* self, uint8_t* dst, + const uint8_t* src, size_t len); + +// Hashing +struct crypto_hash* crypto_hash_new(enum crypto_hash_type type); +void crypto_hash_del(struct crypto_hash* self); + +void crypto_hash_append(struct crypto_hash* self, const uint8_t* src, + size_t len); +void crypto_hash_digest(struct crypto_hash* self, uint8_t* dst, + size_t len); diff --git a/meson.build b/meson.build index 93557c9..ea1f32a 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,7 @@ pixman = dependency('pixman-1') libturbojpeg = dependency('libturbojpeg', required: get_option('jpeg')) gnutls = dependency('gnutls', required: get_option('tls')) nettle = dependency('nettle', required: get_option('nettle')) +gmp = dependency('gmp', required: get_option('nettle')) zlib = dependency('zlib') gbm = dependency('gbm', required: get_option('gbm')) libdrm = dependency('libdrm', required: get_option('h264')) @@ -118,9 +119,11 @@ if gnutls.found() config.set('ENABLE_TLS', true) endif -if nettle.found() - dependencies += nettle +if nettle.found() and gmp.found() + dependencies += [ nettle, gmp ] enable_websocket = true + config.set('HAVE_CRYPTO', true) + sources += 'src/crypto-nettle.c' endif if host_system == 'linux' and get_option('systemtap') and cc.has_header('sys/sdt.h') diff --git a/src/crypto-nettle.c b/src/crypto-nettle.c new file mode 100644 index 0000000..15f82f1 --- /dev/null +++ b/src/crypto-nettle.c @@ -0,0 +1,323 @@ +#include "crypto.h" +#include "neatvnc.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +// TODO: This is linux specific +#include + +struct crypto_key { + int g; + mpz_t p; + mpz_t q; +}; + +struct crypto_cipher { + union { + struct aes128_ctx aes128_ecb; + } enc_ctx; + + union { + struct aes128_ctx aes128_ecb; + } dec_ctx; + + bool (*encrypt)(struct crypto_cipher*, uint8_t* dst, const uint8_t* src, + size_t len); + bool (*decrypt)(struct crypto_cipher*, uint8_t* dst, const uint8_t* src, + size_t len); +}; + +struct crypto_hash { + union { + struct md5_ctx md5; + } ctx; + + void (*update)(void* ctx, size_t len, const uint8_t* src); + void (*digest)(void* ctx, size_t len, uint8_t* dst); +}; + +void crypto_dump_base64(const char* msg, const uint8_t* bytes, size_t len) +{ + struct base64_encode_ctx ctx = {}; + size_t buflen = BASE64_ENCODE_LENGTH(len); + char* buffer = malloc(buflen + BASE64_ENCODE_FINAL_LENGTH); + assert(buffer); + nettle_base64_encode_init(&ctx); + nettle_base64_encode_update(&ctx, buffer, len, bytes); + nettle_base64_encode_final(&ctx, buffer + buflen); + + nvnc_log(NVNC_LOG_DEBUG, "%s: %s", msg, buffer); + free(buffer); +} + +struct crypto_key *crypto_key_new(int g, const uint8_t* p, uint32_t p_len, + const uint8_t* q, uint32_t q_len) +{ + struct crypto_key* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + self->g = g; + + int order = 1; + int unit_size = 1; + int endian = 1; + int skip_bits = 0; + + mpz_init(self->p); + mpz_import(self->p, p_len, order, unit_size, endian, skip_bits, p); + + mpz_init(self->q); + mpz_import(self->q, q_len, order, unit_size, endian, skip_bits, q); + + return self; +} + +void crypto_key_del(struct crypto_key* key) +{ + mpz_clear(key->q); + mpz_clear(key->p); + free(key); +} + +int crypto_key_g(const struct crypto_key* key) +{ + return key->g; +} + +uint32_t crypto_key_p(const struct crypto_key* key, uint8_t* dst, + uint32_t dst_size) +{ + int order = 1; // msb first + int unit_size = 1; // byte + int endian = 1; // msb first + int skip_bits = 0; + + size_t len = 0; + mpz_export(dst, &len, order, unit_size, endian, skip_bits, key->p); + + return len; +} + +uint32_t crypto_key_q(const struct crypto_key* key, uint8_t* dst, + uint32_t dst_size) +{ + int order = 1; // msb first + int unit_size = 1; // byte + int endian = 1; // msb first + int skip_bits = 0; + + size_t len = 0; + mpz_export(dst, &len, order, unit_size, endian, skip_bits, key->q); + + return len; +} + +static void initialise_p(mpz_t p) +{ + // RFC 3526, section 3 + static const char s[] = + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + "15728E5A8AACAA68FFFFFFFFFFFFFFFF"; + + char buf[256]; + size_t len = 0; + struct base16_decode_ctx ctx; + nettle_base16_decode_init(&ctx); + nettle_base16_decode_update(&ctx, &len, (uint8_t*)buf, sizeof(s) - 1, s); + nettle_base16_decode_final(&ctx); + assert(len == sizeof(buf)); + + int order = 1; + int unit_size = 1; + int endian = 1; + int skip_bits = 0; + + mpz_import(p, sizeof(buf), order, unit_size, endian, skip_bits, buf); +} + +static void generate_random(mpz_t n) +{ + uint8_t buf[256]; + getrandom(buf, sizeof(buf), 0); + + int order = 1; + int unit_size = 1; + int endian = 1; + int skip_bits = 0; + + mpz_import(n, sizeof(buf), order, unit_size, endian, skip_bits, buf); +} + +struct crypto_key* crypto_keygen(void) +{ + struct crypto_key* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + self->g = 2; + + mpz_init(self->p); + initialise_p(self->p); + + mpz_init(self->q); + generate_random(self->q); + + return self; +} + +struct crypto_key* crypto_derive_public_key(const struct crypto_key* priv) +{ + struct crypto_key* pub = calloc(1, sizeof(*pub)); + if (!pub) + return NULL; + + pub->g = priv->g; + mpz_set(pub->p, priv->p); + mpz_init(pub->q); + + mpz_t g; + mpz_init(g); + mpz_set_ui(g, priv->g); + + mpz_powm_sec(pub->q, g, priv->q, priv->p); + mpz_clear(g); + + return pub; +} + +struct crypto_key* crypto_derive_shared_secret( + const struct crypto_key* own_secret, + const struct crypto_key* remote_public_key) +{ + if (own_secret->g != remote_public_key->g) { + return NULL; + } + + if (mpz_cmp(own_secret->p, remote_public_key->p) != 0) { + return NULL; + } + + struct crypto_key* shared = calloc(1, sizeof(*shared)); + if (!shared) + return NULL; + + shared->g = own_secret->g; + mpz_set(shared->p, own_secret->p); + + mpz_t g; + mpz_init(g); + mpz_set_ui(g, own_secret->g); + + mpz_powm_sec(shared->q, remote_public_key->q, own_secret->q, + own_secret->p); + mpz_clear(g); + + return shared; +} + +static bool crypto_cipher_aes128_ecb_encrypt(struct crypto_cipher* self, + uint8_t* dst, const uint8_t* src, size_t len) +{ + aes128_encrypt(&self->enc_ctx.aes128_ecb, len, dst, src); + return true; +} + +static bool crypto_cipher_aes128_ecb_decrypt(struct crypto_cipher* self, + uint8_t* dst, const uint8_t* src, size_t len) +{ + aes128_decrypt(&self->dec_ctx.aes128_ecb, len, dst, src); + return true; +} + +static struct crypto_cipher* crypto_cipher_new_aes128_ecb(const uint8_t* key) +{ + struct crypto_cipher* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + aes128_set_encrypt_key(&self->enc_ctx.aes128_ecb, key); + aes128_invert_key(&self->dec_ctx.aes128_ecb, &self->enc_ctx.aes128_ecb); + + self->encrypt = crypto_cipher_aes128_ecb_encrypt; + self->decrypt = crypto_cipher_aes128_ecb_decrypt; + + return self; +} + +struct crypto_cipher* crypto_cipher_new(const uint8_t* key, + enum crypto_cipher_type type) +{ + switch (type) { + case CRYPTO_CIPHER_AES128_ECB: + return crypto_cipher_new_aes128_ecb(key); + case CRYPTO_CIPHER_INVALID: + break; + } + + nvnc_log(NVNC_LOG_PANIC, "Invalid type: %d", type); + return NULL; +} + +void crypto_cipher_del(struct crypto_cipher* self) +{ + free(self); +} + +bool crypto_cipher_encrypt(struct crypto_cipher* self, uint8_t* dst, + const uint8_t* src, size_t len) +{ + return self->encrypt(self, dst, src, len); +} + +bool crypto_cipher_decrypt(struct crypto_cipher* self, uint8_t* dst, + const uint8_t* src, size_t len) +{ + return self->decrypt(self, dst, src, len); +} + +struct crypto_hash* crypto_hash_new(enum crypto_hash_type type) +{ + struct crypto_hash* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + md5_init(&self->ctx.md5); + self->update = (void*)nettle_md5_update; + self->digest = (void*)nettle_md5_digest; + + return self; +} + +void crypto_hash_del(struct crypto_hash* self) +{ + free(self); +} + +void crypto_hash_append(struct crypto_hash* self, const uint8_t* src, + size_t len) +{ + self->update(&self->ctx, len, src); +} + +void crypto_hash_digest(struct crypto_hash* self, uint8_t* dst, size_t len) +{ + self->digest(&self->ctx, len, dst); +}