summaryrefslogtreecommitdiff
path: root/src/core/crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/crypto.c')
-rw-r--r--src/core/crypto.c302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/core/crypto.c b/src/core/crypto.c
new file mode 100644
index 0000000..dffcea4
--- /dev/null
+++ b/src/core/crypto.c
@@ -0,0 +1,302 @@
+#include "hybbx/crypto.h"
+
+#include "crypto_backends.h"
+#include "monocypher.h"
+
+#include <string.h>
+
+#define HYBBX_CRYPTO_MAX_BYTES (256u * 1024u)
+
+static hybbx_result_t crypto_len_ok(size_t len)
+{
+ if (len > HYBBX_CRYPTO_MAX_BYTES) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ return HYBBX_OK;
+}
+
+hybbx_result_t hybbx_crypto_random(uint8_t *buf, size_t len)
+{
+ if (buf == NULL || len == 0) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ return hybbx_backend_random(buf, len);
+}
+
+const char *hybbx_crypto_alg_name(hybbx_crypto_alg_t alg)
+{
+ switch (alg) {
+ case HYBBX_CRYPTO_AES_256_GCM:
+ return "aes-256-gcm";
+ case HYBBX_CRYPTO_XCHACHA20_POLY1305:
+ return "xchacha20-poly1305";
+ case HYBBX_CRYPTO_X25519_AEAD:
+ return "x25519-xchacha20-poly1305";
+ default:
+ return "unknown";
+ }
+}
+
+size_t hybbx_crypto_nonce_size(hybbx_crypto_alg_t alg)
+{
+ switch (alg) {
+ case HYBBX_CRYPTO_AES_256_GCM:
+ return HYBBX_CRYPTO_AES_GCM_NONCE;
+ case HYBBX_CRYPTO_XCHACHA20_POLY1305:
+ return HYBBX_CRYPTO_XCHACHA_NONCE;
+ default:
+ return 0;
+ }
+}
+
+static hybbx_result_t derive_x25519_key(const uint8_t shared[32],
+ uint8_t key[32])
+{
+ static const uint8_t label[] = "hybbx-x25519-aead-key-v1";
+
+ hybbx_backend_blake2b_keyed(key, 32, shared, 32, label, sizeof(label) - 1);
+ return HYBBX_OK;
+}
+
+hybbx_result_t hybbx_crypto_encrypt(hybbx_crypto_alg_t alg,
+ const uint8_t key[HYBBX_CRYPTO_KEY_SIZE],
+ const uint8_t *nonce, size_t nonce_len,
+ const uint8_t *aad, size_t aad_len,
+ const uint8_t *plaintext,
+ size_t plaintext_len,
+ uint8_t *ciphertext,
+ uint8_t tag[HYBBX_CRYPTO_TAG_SIZE])
+{
+ hybbx_result_t rc;
+
+ if (key == NULL || nonce == NULL || tag == NULL ||
+ (plaintext_len > 0 && (plaintext == NULL || ciphertext == NULL))) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ rc = crypto_len_ok(plaintext_len);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ if (alg == HYBBX_CRYPTO_AES_256_GCM) {
+ if (nonce_len != HYBBX_CRYPTO_AES_GCM_NONCE) {
+ return HYBBX_ERR_INVALID;
+ }
+ if (hybbx_backend_aes256gcm_encrypt(key, nonce, aad, aad_len, plaintext,
+ plaintext_len, ciphertext, tag) !=
+ 0) {
+ return HYBBX_ERR_IO;
+ }
+ return HYBBX_OK;
+ }
+
+ if (alg == HYBBX_CRYPTO_XCHACHA20_POLY1305) {
+ uint8_t nonce24[HYBBX_CRYPTO_XCHACHA_NONCE];
+
+ if (nonce_len != HYBBX_CRYPTO_XCHACHA_NONCE) {
+ return HYBBX_ERR_INVALID;
+ }
+ memcpy(nonce24, nonce, sizeof(nonce24));
+ hybbx_backend_chacha_lock(ciphertext, tag, key, nonce24, aad, aad_len,
+ plaintext, plaintext_len);
+ crypto_wipe(nonce24, sizeof(nonce24));
+ return HYBBX_OK;
+ }
+
+ return HYBBX_ERR_INVALID;
+}
+
+hybbx_result_t hybbx_crypto_decrypt(hybbx_crypto_alg_t alg,
+ const uint8_t key[HYBBX_CRYPTO_KEY_SIZE],
+ const uint8_t *nonce, size_t nonce_len,
+ const uint8_t *aad, size_t aad_len,
+ const uint8_t *ciphertext,
+ size_t ciphertext_len,
+ uint8_t *plaintext,
+ const uint8_t tag[HYBBX_CRYPTO_TAG_SIZE])
+{
+ hybbx_result_t rc;
+ int ok;
+
+ if (key == NULL || nonce == NULL || tag == NULL ||
+ (ciphertext_len > 0 && (ciphertext == NULL || plaintext == NULL))) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ rc = crypto_len_ok(ciphertext_len);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ if (alg == HYBBX_CRYPTO_AES_256_GCM) {
+ if (nonce_len != HYBBX_CRYPTO_AES_GCM_NONCE) {
+ return HYBBX_ERR_INVALID;
+ }
+ ok = hybbx_backend_aes256gcm_decrypt(key, nonce, aad, aad_len,
+ ciphertext, ciphertext_len,
+ plaintext, tag);
+ return ok == 0 ? HYBBX_OK : HYBBX_ERR_DENIED;
+ }
+
+ if (alg == HYBBX_CRYPTO_XCHACHA20_POLY1305) {
+ uint8_t nonce24[HYBBX_CRYPTO_XCHACHA_NONCE];
+
+ if (nonce_len != HYBBX_CRYPTO_XCHACHA_NONCE) {
+ return HYBBX_ERR_INVALID;
+ }
+ memcpy(nonce24, nonce, sizeof(nonce24));
+ ok = hybbx_backend_chacha_unlock(plaintext, tag, key, nonce24, aad,
+ aad_len, ciphertext, ciphertext_len);
+ crypto_wipe(nonce24, sizeof(nonce24));
+ return ok == 0 ? HYBBX_OK : HYBBX_ERR_DENIED;
+ }
+
+ return HYBBX_ERR_INVALID;
+}
+
+hybbx_result_t hybbx_crypto_x25519_keypair(
+ uint8_t public_key[HYBBX_CRYPTO_X25519_PUBLIC_KEY],
+ uint8_t secret_key[HYBBX_CRYPTO_X25519_SECRET_KEY])
+{
+ if (public_key == NULL || secret_key == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (hybbx_crypto_random(secret_key, HYBBX_CRYPTO_X25519_SECRET_KEY) !=
+ HYBBX_OK) {
+ return HYBBX_ERR_IO;
+ }
+
+ secret_key[0] &= 248;
+ secret_key[31] &= 127;
+ secret_key[31] |= 64;
+
+ hybbx_backend_x25519_public_key(public_key, secret_key);
+ return HYBBX_OK;
+}
+
+hybbx_result_t hybbx_crypto_x25519_seal(
+ const uint8_t recipient_pk[HYBBX_CRYPTO_X25519_PUBLIC_KEY],
+ const uint8_t *aad, size_t aad_len,
+ const uint8_t *plaintext, size_t plaintext_len,
+ uint8_t *sealed, size_t *sealed_len, size_t sealed_cap)
+{
+ uint8_t ep_sk[32];
+ uint8_t ep_pk[32];
+ uint8_t shared[32];
+ uint8_t key[32];
+ uint8_t nonce[HYBBX_CRYPTO_XCHACHA_NONCE];
+ uint8_t *cipher;
+ uint8_t *tag;
+ size_t need;
+ hybbx_result_t rc;
+
+ if (recipient_pk == NULL || sealed == NULL || sealed_len == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ rc = crypto_len_ok(plaintext_len);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ need = HYBBX_CRYPTO_X25519_SEAL_OVERHEAD + plaintext_len;
+ if (sealed_cap < need) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (hybbx_crypto_random(ep_sk, sizeof(ep_sk)) != HYBBX_OK) {
+ return HYBBX_ERR_IO;
+ }
+ ep_sk[0] &= 248;
+ ep_sk[31] &= 127;
+ ep_sk[31] |= 64;
+
+ hybbx_backend_x25519_public_key(ep_pk, ep_sk);
+ hybbx_backend_x25519_shared(shared, ep_sk, recipient_pk);
+ derive_x25519_key(shared, key);
+
+ if (hybbx_crypto_random(nonce, sizeof(nonce)) != HYBBX_OK) {
+ crypto_wipe(ep_sk, sizeof(ep_sk));
+ crypto_wipe(shared, sizeof(shared));
+ crypto_wipe(key, sizeof(key));
+ return HYBBX_ERR_IO;
+ }
+
+ memcpy(sealed, ep_pk, 32);
+ memcpy(sealed + 32, nonce, sizeof(nonce));
+ cipher = sealed + 32 + sizeof(nonce);
+ tag = sealed + 32 + sizeof(nonce) + plaintext_len;
+
+ rc = hybbx_crypto_encrypt(HYBBX_CRYPTO_XCHACHA20_POLY1305, key, nonce,
+ sizeof(nonce), aad, aad_len, plaintext,
+ plaintext_len, cipher, tag);
+
+ crypto_wipe(ep_sk, sizeof(ep_sk));
+ crypto_wipe(shared, sizeof(shared));
+ crypto_wipe(key, sizeof(key));
+
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ *sealed_len = need;
+ return HYBBX_OK;
+}
+
+hybbx_result_t hybbx_crypto_x25519_open(
+ const uint8_t recipient_sk[HYBBX_CRYPTO_X25519_SECRET_KEY],
+ const uint8_t *aad, size_t aad_len,
+ const uint8_t *sealed, size_t sealed_len,
+ uint8_t *plaintext, size_t *plaintext_len, size_t plaintext_cap)
+{
+ const uint8_t *ep_pk;
+ const uint8_t *nonce;
+ const uint8_t *cipher;
+ const uint8_t *tag;
+ size_t cipher_len;
+ uint8_t shared[32];
+ uint8_t key[32];
+ uint8_t tag_buf[HYBBX_CRYPTO_TAG_SIZE];
+ hybbx_result_t rc;
+
+ if (recipient_sk == NULL || sealed == NULL || plaintext == NULL ||
+ plaintext_len == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (sealed_len < HYBBX_CRYPTO_X25519_SEAL_OVERHEAD) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ cipher_len = sealed_len - HYBBX_CRYPTO_X25519_SEAL_OVERHEAD;
+ if (cipher_len > plaintext_cap) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ ep_pk = sealed;
+ nonce = sealed + 32;
+ cipher = sealed + 32 + HYBBX_CRYPTO_XCHACHA_NONCE;
+ tag = cipher + cipher_len;
+ memcpy(tag_buf, tag, sizeof(tag_buf));
+
+ hybbx_backend_x25519_shared(shared, recipient_sk, ep_pk);
+ derive_x25519_key(shared, key);
+
+ rc = hybbx_crypto_decrypt(HYBBX_CRYPTO_XCHACHA20_POLY1305, key, nonce,
+ HYBBX_CRYPTO_XCHACHA_NONCE, aad, aad_len,
+ cipher, cipher_len, plaintext, tag_buf);
+
+ crypto_wipe(shared, sizeof(shared));
+ crypto_wipe(key, sizeof(key));
+
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ *plaintext_len = cipher_len;
+ return HYBBX_OK;
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com