/* * Copyright 2024 Vsevolod Stakhov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "libcryptobox/keypair.h" #include "libcryptobox/keypair_private.h" #include "libutil/str_util.h" #include "libutil/printf.h" #include "contrib/libottery/ottery.h" const unsigned char encrypted_magic[7] = {'r', 'u', 'c', 'l', 'e', 'v', '1'}; static GQuark rspamd_keypair_quark(void) { return g_quark_from_static_string("rspamd-cryptobox-keypair"); } /** * Returns specific private key for different keypair types */ static void * rspamd_cryptobox_keypair_sk(struct rspamd_cryptobox_keypair *kp, size_t *len) { g_assert(kp != NULL); if (kp->type == RSPAMD_KEYPAIR_KEX) { *len = 32; return RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp)->sk; } else { *len = 64; return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(kp)->sk; } } static void * rspamd_cryptobox_keypair_pk(struct rspamd_cryptobox_keypair *kp, size_t *len) { g_assert(kp != NULL); if (kp->type == RSPAMD_KEYPAIR_KEX) { *len = 32; return RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp)->pk; } else { *len = 32; return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_25519(kp)->pk; } } static void * rspamd_cryptobox_pubkey_pk(const struct rspamd_cryptobox_pubkey *kp, size_t *len) { g_assert(kp != NULL); if (kp->type == RSPAMD_KEYPAIR_KEX) { *len = 32; return RSPAMD_CRYPTOBOX_PUBKEY_25519(kp)->pk; } else { *len = 32; return RSPAMD_CRYPTOBOX_PUBKEY_SIG_25519(kp)->pk; } } static struct rspamd_cryptobox_keypair * rspamd_cryptobox_keypair_alloc(enum rspamd_cryptobox_keypair_type type) { struct rspamd_cryptobox_keypair *kp; unsigned int size = 0; if (type == RSPAMD_KEYPAIR_KEX) { size = sizeof(struct rspamd_cryptobox_keypair_25519); } else { size = sizeof(struct rspamd_cryptobox_keypair_sig_25519); } g_assert(size >= sizeof(*kp)); if (posix_memalign((void **) &kp, 32, size) != 0) { abort(); } memset(kp, 0, size); return kp; } static struct rspamd_cryptobox_pubkey * rspamd_cryptobox_pubkey_alloc(enum rspamd_cryptobox_keypair_type type) { struct rspamd_cryptobox_pubkey *pk; unsigned int size = 0; if (type == RSPAMD_KEYPAIR_KEX) { size = sizeof(struct rspamd_cryptobox_pubkey_25519); } else { size = sizeof(struct rspamd_cryptobox_pubkey_sig_25519); } g_assert(size >= sizeof(*pk)); if (posix_memalign((void **) &pk, 32, size) != 0) { abort(); } memset(pk, 0, size); return pk; } void rspamd_cryptobox_nm_dtor(struct rspamd_cryptobox_nm *nm) { rspamd_explicit_memzero(nm->nm, sizeof(nm->nm)); free(nm); } void rspamd_cryptobox_keypair_dtor(struct rspamd_cryptobox_keypair *kp) { void *sk; size_t len = 0; sk = rspamd_cryptobox_keypair_sk(kp, &len); g_assert(sk != NULL && len > 0); rspamd_explicit_memzero(sk, len); if (kp->extensions) { ucl_object_unref(kp->extensions); } /* Not g_free as kp is aligned using posix_memalign */ free(kp); } void rspamd_cryptobox_pubkey_dtor(struct rspamd_cryptobox_pubkey *p) { if (p->nm) { REF_RELEASE(p->nm); } /* Not g_free as p is aligned using posix_memalign */ free(p); } struct rspamd_cryptobox_keypair * rspamd_keypair_new(enum rspamd_cryptobox_keypair_type type) { struct rspamd_cryptobox_keypair *kp; void *pk, *sk; size_t size; kp = rspamd_cryptobox_keypair_alloc(type); kp->type = type; sk = rspamd_cryptobox_keypair_sk(kp, &size); pk = rspamd_cryptobox_keypair_pk(kp, &size); if (type == RSPAMD_KEYPAIR_KEX) { rspamd_cryptobox_keypair(pk, sk); } else { rspamd_cryptobox_keypair_sig(pk, sk); } rspamd_cryptobox_hash(kp->id, pk, size, NULL, 0); REF_INIT_RETAIN(kp, rspamd_cryptobox_keypair_dtor); return kp; } struct rspamd_cryptobox_keypair * rspamd_keypair_ref(struct rspamd_cryptobox_keypair *kp) { REF_RETAIN(kp); return kp; } void rspamd_keypair_unref(struct rspamd_cryptobox_keypair *kp) { REF_RELEASE(kp); } struct rspamd_cryptobox_pubkey * rspamd_pubkey_ref(struct rspamd_cryptobox_pubkey *kp) { REF_RETAIN(kp); return kp; } void rspamd_pubkey_unref(struct rspamd_cryptobox_pubkey *kp) { REF_RELEASE(kp); } enum rspamd_cryptobox_keypair_type rspamd_keypair_type(struct rspamd_cryptobox_keypair *kp) { g_assert(kp != NULL); return kp->type; } enum rspamd_cryptobox_keypair_type rspamd_pubkey_type(struct rspamd_cryptobox_pubkey *p) { g_assert(p != NULL); return p->type; } struct rspamd_cryptobox_pubkey * rspamd_pubkey_from_base32(const char *b32, gsize len, enum rspamd_cryptobox_keypair_type type) { unsigned char *decoded; gsize dlen, expected_len; size_t pklen; struct rspamd_cryptobox_pubkey *pk; unsigned char *pk_data; g_assert(b32 != NULL); if (len == 0) { len = strlen(b32); } decoded = rspamd_decode_base32(b32, len, &dlen, RSPAMD_BASE32_DEFAULT); if (decoded == NULL) { return NULL; } expected_len = (type == RSPAMD_KEYPAIR_KEX) ? crypto_box_PUBLICKEYBYTES : crypto_sign_PUBLICKEYBYTES; if (dlen != expected_len) { g_free(decoded); return NULL; } pk = rspamd_cryptobox_pubkey_alloc(type); REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor); pk->type = type; pk_data = rspamd_cryptobox_pubkey_pk(pk, &pklen); memcpy(pk_data, decoded, pklen); g_free(decoded); rspamd_cryptobox_hash(pk->id, pk_data, pklen, NULL, 0); return pk; } struct rspamd_cryptobox_pubkey * rspamd_pubkey_from_hex(const char *hex, gsize len, enum rspamd_cryptobox_keypair_type type) { unsigned char *decoded; gsize dlen, expected_len; size_t pklen; struct rspamd_cryptobox_pubkey *pk; unsigned char *pk_data; g_assert(hex != NULL); if (len == 0) { len = strlen(hex); } dlen = len / 2; decoded = rspamd_decode_hex(hex, len); if (decoded == NULL) { return NULL; } expected_len = (type == RSPAMD_KEYPAIR_KEX) ? crypto_box_PUBLICKEYBYTES : crypto_sign_PUBLICKEYBYTES; if (dlen != expected_len) { g_free(decoded); return NULL; } pk = rspamd_cryptobox_pubkey_alloc(type); REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor); pk->type = type; pk_data = rspamd_cryptobox_pubkey_pk(pk, &pklen); memcpy(pk_data, decoded, pklen); g_free(decoded); rspamd_cryptobox_hash(pk->id, pk_data, pklen, NULL, 0); return pk; } struct rspamd_cryptobox_pubkey * rspamd_pubkey_from_bin(const unsigned char *raw, gsize len, enum rspamd_cryptobox_keypair_type type) { size_t pklen; struct rspamd_cryptobox_pubkey *pk; unsigned char *pk_data; g_assert(raw != NULL && len > 0); if (len != crypto_box_PUBLICKEYBYTES) { return NULL; } pk = rspamd_cryptobox_pubkey_alloc(type); REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor); pk->type = type; pk_data = rspamd_cryptobox_pubkey_pk(pk, &pklen); memcpy(pk_data, raw, pklen); rspamd_cryptobox_hash(pk->id, pk_data, pklen, NULL, 0); return pk; } const unsigned char * rspamd_pubkey_get_nm(struct rspamd_cryptobox_pubkey *p, struct rspamd_cryptobox_keypair *kp) { g_assert(p != NULL); if (p->nm) { if (memcmp(kp->id, (const unsigned char *) &p->nm->sk_id, sizeof(uint64_t)) == 0) { return p->nm->nm; } /* Wrong ID, need to recalculate */ REF_RELEASE(p->nm); p->nm = NULL; } return NULL; } const unsigned char * rspamd_pubkey_calculate_nm(struct rspamd_cryptobox_pubkey *p, struct rspamd_cryptobox_keypair *kp) { g_assert(kp->type == p->type); g_assert(p->type == RSPAMD_KEYPAIR_KEX); if (p->nm == NULL) { if (posix_memalign((void **) &p->nm, 32, sizeof(*p->nm)) != 0) { abort(); } memcpy(&p->nm->sk_id, kp->id, sizeof(uint64_t)); REF_INIT_RETAIN(p->nm, rspamd_cryptobox_nm_dtor); } struct rspamd_cryptobox_pubkey_25519 *rk_25519 = RSPAMD_CRYPTOBOX_PUBKEY_25519(p); struct rspamd_cryptobox_keypair_25519 *sk_25519 = RSPAMD_CRYPTOBOX_KEYPAIR_25519(kp); rspamd_cryptobox_nm(p->nm->nm, rk_25519->pk, sk_25519->sk); return p->nm->nm; } const unsigned char * rspamd_keypair_get_id(struct rspamd_cryptobox_keypair *kp) { g_assert(kp != NULL); return kp->id; } const ucl_object_t * rspamd_keypair_get_extensions(struct rspamd_cryptobox_keypair *kp) { g_assert(kp != NULL); return kp->extensions; } const unsigned char * rspamd_pubkey_get_id(struct rspamd_cryptobox_pubkey *pk) { g_assert(pk != NULL); return pk->id; } const unsigned char * rspamd_pubkey_get_pk(struct rspamd_cryptobox_pubkey *pk, unsigned int *len) { unsigned char *ret = NULL; size_t rlen; ret = rspamd_cryptobox_pubkey_pk(pk, &rlen); if (len) { *len = rlen; } return ret; } static void rspamd_keypair_print_component(unsigned char *data, gsize datalen, GString *res, unsigned int how, const char *description, enum rspamd_cryptobox_keypair_encoding encoding) { int olen, b32_len; if (how & RSPAMD_KEYPAIR_HUMAN) { rspamd_printf_gstring(res, "%s: ", description); } if (encoding == RSPAMD_KEYPAIR_ENCODING_ZBASE32) { b32_len = (datalen * 8 / 5) + 2; g_string_set_size(res, res->len + b32_len); res->len -= b32_len; olen = rspamd_encode_base32_buf(data, datalen, res->str + res->len, res->len + b32_len - 1, RSPAMD_BASE32_DEFAULT); if (olen > 0) { res->len += olen; res->str[res->len] = '\0'; } } else if (encoding == RSPAMD_KEYPAIR_ENCODING_HEX) { rspamd_printf_gstring(res, "%*xs", (int) datalen, data); } else if (encoding == RSPAMD_KEYPAIR_ENCODING_BASE64) { rspamd_printf_gstring(res, "%*Bs", (int) datalen, data); } else { g_string_append_len(res, data, datalen); } if (how & RSPAMD_KEYPAIR_HUMAN) { g_string_append_c(res, '\n'); } } GString * rspamd_keypair_print(struct rspamd_cryptobox_keypair *kp, enum rspamd_cryptobox_keypair_encoding encoding, unsigned int how) { GString *res; size_t len; gpointer p; g_assert(kp != NULL); res = g_string_sized_new(63); if ((how & RSPAMD_KEYPAIR_PUBKEY)) { p = rspamd_cryptobox_keypair_pk(kp, &len); rspamd_keypair_print_component(p, len, res, how, "Public key", encoding); } if ((how & RSPAMD_KEYPAIR_PRIVKEY)) { p = rspamd_cryptobox_keypair_sk(kp, &len); rspamd_keypair_print_component(p, len, res, how, "Private key", encoding); } if ((how & RSPAMD_KEYPAIR_ID_SHORT)) { rspamd_keypair_print_component(kp->id, RSPAMD_KEYPAIR_SHORT_ID_LEN, res, how, "Short key ID", encoding); } if ((how & RSPAMD_KEYPAIR_ID)) { rspamd_keypair_print_component(kp->id, sizeof(kp->id), res, how, "Key ID", encoding); } return res; } GString * rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, enum rspamd_cryptobox_keypair_encoding encoding, unsigned int how) { GString *res; size_t len; gpointer p; g_assert(pk != NULL); res = g_string_sized_new(63); if ((how & RSPAMD_KEYPAIR_PUBKEY)) { p = rspamd_cryptobox_pubkey_pk(pk, &len); rspamd_keypair_print_component(p, len, res, how, "Public key", encoding); } if ((how & RSPAMD_KEYPAIR_ID_SHORT)) { rspamd_keypair_print_component(pk->id, RSPAMD_KEYPAIR_SHORT_ID_LEN, res, how, "Short key ID", encoding); } if ((how & RSPAMD_KEYPAIR_ID)) { rspamd_keypair_print_component(pk->id, sizeof(pk->id), res, how, "Key ID", encoding); } return res; } const unsigned char * rspamd_keypair_component(struct rspamd_cryptobox_keypair *kp, unsigned int ncomp, unsigned int *len) { size_t rlen = 0; const unsigned char *ret = NULL; g_assert(kp != NULL); switch (ncomp) { case RSPAMD_KEYPAIR_COMPONENT_ID: rlen = sizeof(kp->id); ret = kp->id; break; case RSPAMD_KEYPAIR_COMPONENT_PK: ret = rspamd_cryptobox_keypair_pk(kp, &rlen); break; case RSPAMD_KEYPAIR_COMPONENT_SK: ret = rspamd_cryptobox_keypair_sk(kp, &rlen); break; } if (len) { *len = rlen; } return ret; } struct rspamd_cryptobox_keypair * rspamd_keypair_from_ucl(const ucl_object_t *obj) { const ucl_object_t *privkey, *pubkey, *elt; const char *str; enum rspamd_cryptobox_keypair_type type = RSPAMD_KEYPAIR_KEX; enum rspamd_cryptobox_keypair_encoding encoding = RSPAMD_KEYPAIR_ENCODING_DEFAULT; struct rspamd_cryptobox_keypair *kp; size_t len; size_t ucl_len; size_t dec_len; gpointer target; if (ucl_object_type(obj) != UCL_OBJECT) { return NULL; } elt = ucl_object_lookup(obj, "keypair"); if (elt != NULL) { obj = elt; } pubkey = ucl_object_lookup_any(obj, "pubkey", "public", "public_key", NULL); if (pubkey == NULL || ucl_object_type(pubkey) != UCL_STRING) { return NULL; } privkey = ucl_object_lookup_any(obj, "privkey", "private", "private_key", "secret", "secret_key", NULL); if (privkey == NULL || ucl_object_type(privkey) != UCL_STRING) { return NULL; } /* Optional fields */ elt = ucl_object_lookup(obj, "type"); if (elt && ucl_object_type(elt) == UCL_STRING) { str = ucl_object_tostring(elt); if (g_ascii_strcasecmp(str, "kex") == 0) { type = RSPAMD_KEYPAIR_KEX; } else if (g_ascii_strcasecmp(str, "sign") == 0) { type = RSPAMD_KEYPAIR_SIGN; } /* TODO: handle errors */ } elt = ucl_object_lookup(obj, "encoding"); if (elt && ucl_object_type(elt) == UCL_STRING) { str = ucl_object_tostring(elt); if (g_ascii_strcasecmp(str, "hex") == 0) { encoding = RSPAMD_KEYPAIR_ENCODING_HEX; } else if (g_ascii_strcasecmp(str, "base64") == 0) { encoding = RSPAMD_KEYPAIR_ENCODING_BASE64; } /* TODO: handle errors */ } kp = rspamd_cryptobox_keypair_alloc(type); kp->type = type; REF_INIT_RETAIN(kp, rspamd_cryptobox_keypair_dtor); g_assert(kp != NULL); target = rspamd_cryptobox_keypair_sk(kp, &len); str = ucl_object_tolstring(privkey, &ucl_len); switch (encoding) { case RSPAMD_KEYPAIR_ENCODING_HEX: dec_len = rspamd_decode_hex_buf(str, ucl_len, target, len); break; case RSPAMD_KEYPAIR_ENCODING_ZBASE32: dec_len = rspamd_decode_base32_buf(str, ucl_len, target, len, RSPAMD_BASE32_DEFAULT); break; case RSPAMD_KEYPAIR_ENCODING_BASE64: dec_len = rspamd_cryptobox_base64_decode(str, ucl_len, target, &len); break; case RSPAMD_KEYPAIR_ENCODING_BINARY: if (len >= ucl_len) { memcpy(target, str, ucl_len); dec_len = ucl_len; } else { memcpy(target, str, len); dec_len = len; } break; } if (dec_len != (int) len) { rspamd_keypair_unref(kp); return NULL; } target = rspamd_cryptobox_keypair_pk(kp, &len); str = ucl_object_tolstring(pubkey, &ucl_len); switch (encoding) { case RSPAMD_KEYPAIR_ENCODING_HEX: dec_len = rspamd_decode_hex_buf(str, ucl_len, target, len); break; case RSPAMD_KEYPAIR_ENCODING_ZBASE32: dec_len = rspamd_decode_base32_buf(str, ucl_len, target, len, RSPAMD_BASE32_DEFAULT); break; case RSPAMD_KEYPAIR_ENCODING_BASE64: dec_len = rspamd_cryptobox_base64_decode(str, ucl_len, target, &len); break; case RSPAMD_KEYPAIR_ENCODING_BINARY: if (len >= ucl_len) { memcpy(target, str, ucl_len); dec_len = ucl_len; } else { memcpy(target, str, len); dec_len = len; } break; } if (dec_len != (int) len) { rspamd_keypair_unref(kp); return NULL; } rspamd_cryptobox_hash(kp->id, target, len, NULL, 0); elt = ucl_object_lookup(obj, "extensions"); if (elt && ucl_object_type(elt) == UCL_OBJECT) { /* Use copy to avoid issues with the refcounts */ kp->extensions = ucl_object_copy(elt); } return kp; } ucl_object_t * rspamd_keypair_to_ucl(struct rspamd_cryptobox_keypair *kp, enum rspamd_cryptobox_keypair_encoding encoding, enum rspamd_keypair_dump_flags flags) { ucl_object_t *ucl_out, *elt; GString *keypair_out; const char *encoding_str = NULL; g_assert(kp != NULL); if (flags & RSPAMD_KEYPAIR_DUMP_FLATTENED) { ucl_out = ucl_object_typed_new(UCL_OBJECT); elt = ucl_out; } else { ucl_out = ucl_object_typed_new(UCL_OBJECT); elt = ucl_object_typed_new(UCL_OBJECT); ucl_object_insert_key(ucl_out, elt, "keypair", 0, false); } if (encoding == RSPAMD_KEYPAIR_ENCODING_HEX) { encoding_str = "hex"; } else if (encoding == RSPAMD_KEYPAIR_ENCODING_BASE64) { encoding_str = "base64"; } /* XXX: maybe support binary here ? */ /* pubkey part */ keypair_out = rspamd_keypair_print(kp, encoding, RSPAMD_KEYPAIR_PUBKEY | flags); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "pubkey", 0, false); g_string_free(keypair_out, TRUE); if (!(flags & RSPAMD_KEYPAIR_DUMP_NO_SECRET)) { /* privkey part */ keypair_out = rspamd_keypair_print(kp, encoding, RSPAMD_KEYPAIR_PRIVKEY | flags); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "privkey", 0, false); g_string_free(keypair_out, TRUE); } keypair_out = rspamd_keypair_print(kp, encoding, RSPAMD_KEYPAIR_ID | flags); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "id", 0, false); g_string_free(keypair_out, TRUE); if (encoding_str) { ucl_object_insert_key(elt, ucl_object_fromstring(encoding_str), "encoding", 0, false); } ucl_object_insert_key(elt, ucl_object_fromstring("curve25519"), "algorithm", 0, false); ucl_object_insert_key(elt, ucl_object_fromstring( kp->type == RSPAMD_KEYPAIR_KEX ? "kex" : "sign"), "type", 0, false); if (kp->extensions) { ucl_object_insert_key(elt, ucl_object_copy(kp->extensions), "extensions", 0, false); } return ucl_out; } gboolean rspamd_keypair_decrypt(struct rspamd_cryptobox_keypair *kp, const unsigned char *in, gsize inlen, unsigned char **out, gsize *outlen, GError **err) { const unsigned char *nonce, *mac, *data, *pubkey; g_assert(kp != NULL); g_assert(in != NULL); if (kp->type != RSPAMD_KEYPAIR_KEX) { g_set_error(err, rspamd_keypair_quark(), EINVAL, "invalid keypair type"); return FALSE; } if (inlen < sizeof(encrypted_magic) + crypto_box_publickeybytes() + crypto_box_macbytes() + crypto_box_noncebytes()) { g_set_error(err, rspamd_keypair_quark(), E2BIG, "invalid size: too small"); return FALSE; } if (memcmp(in, encrypted_magic, sizeof(encrypted_magic)) != 0) { g_set_error(err, rspamd_keypair_quark(), EINVAL, "invalid magic"); return FALSE; } /* Set pointers */ pubkey = in + sizeof(encrypted_magic); mac = pubkey + crypto_box_publickeybytes(); nonce = mac + crypto_box_macbytes(); data = nonce + crypto_box_noncebytes(); if (data - in >= inlen) { g_set_error(err, rspamd_keypair_quark(), E2BIG, "invalid size: too small"); return FALSE; } inlen -= data - in; /* Allocate memory for output */ *out = g_malloc(inlen); memcpy(*out, data, inlen); if (!rspamd_cryptobox_decrypt_inplace(*out, inlen, nonce, pubkey, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), mac)) { g_set_error(err, rspamd_keypair_quark(), EPERM, "verification failed"); g_free(*out); return FALSE; } if (outlen) { *outlen = inlen; } return TRUE; } gboolean rspamd_keypair_encrypt(struct rspamd_cryptobox_keypair *kp, const unsigned char *in, gsize inlen, unsigned char **out, gsize *outlen, GError **err) { unsigned char *nonce, *mac, *data, *pubkey; struct rspamd_cryptobox_keypair *local; gsize olen; g_assert(kp != NULL); g_assert(in != NULL); if (kp->type != RSPAMD_KEYPAIR_KEX) { g_set_error(err, rspamd_keypair_quark(), EINVAL, "invalid keypair type"); return FALSE; } local = rspamd_keypair_new(kp->type); olen = inlen + sizeof(encrypted_magic) + crypto_box_publickeybytes() + crypto_box_macbytes() + crypto_box_noncebytes(); *out = g_malloc(olen); memcpy(*out, encrypted_magic, sizeof(encrypted_magic)); pubkey = *out + sizeof(encrypted_magic); mac = pubkey + crypto_box_publickeybytes(); nonce = mac + crypto_box_macbytes(); data = nonce + crypto_box_noncebytes(); ottery_rand_bytes(nonce, crypto_box_noncebytes()); memcpy(data, in, inlen); memcpy(pubkey, rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_PK, NULL), crypto_box_publickeybytes()); rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_PK, NULL), rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), mac); rspamd_keypair_unref(local); if (outlen) { *outlen = olen; } return TRUE; } gboolean rspamd_pubkey_encrypt(struct rspamd_cryptobox_pubkey *pk, const unsigned char *in, gsize inlen, unsigned char **out, gsize *outlen, GError **err) { unsigned char *nonce, *mac, *data, *pubkey; struct rspamd_cryptobox_keypair *local; gsize olen; g_assert(pk != NULL); g_assert(in != NULL); if (pk->type != RSPAMD_KEYPAIR_KEX) { g_set_error(err, rspamd_keypair_quark(), EINVAL, "invalid pubkey type"); return FALSE; } local = rspamd_keypair_new(pk->type); olen = inlen + sizeof(encrypted_magic) + crypto_box_publickeybytes() + crypto_box_macbytes() + crypto_box_noncebytes(); *out = g_malloc(olen); memcpy(*out, encrypted_magic, sizeof(encrypted_magic)); pubkey = *out + sizeof(encrypted_magic); mac = pubkey + crypto_box_publickeybytes(); nonce = mac + crypto_box_macbytes(); data = nonce + crypto_box_noncebytes(); ottery_rand_bytes(nonce, crypto_box_noncebytes()); memcpy(data, in, inlen); memcpy(pubkey, rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_PK, NULL), crypto_box_publickeybytes()); rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, rspamd_pubkey_get_pk(pk, NULL), rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), mac); rspamd_keypair_unref(local); if (outlen) { *outlen = olen; } return TRUE; }