/*- * Copyright 2016 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 guchar 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, guint *len) { g_assert(kp != NULL); if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) { 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; } } else { if (kp->type == RSPAMD_KEYPAIR_KEX) { *len = 32; return RSPAMD_CRYPTOBOX_KEYPAIR_NIST(kp)->sk; } else { *len = 32; return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_NIST(kp)->sk; } } /* Not reached */ return NULL; } static void * rspamd_cryptobox_keypair_pk(struct rspamd_cryptobox_keypair *kp, guint *len) { g_assert(kp != NULL); if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) { 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; } } else { if (kp->type == RSPAMD_KEYPAIR_KEX) { *len = 65; return RSPAMD_CRYPTOBOX_KEYPAIR_NIST(kp)->pk; } else { *len = 65; return RSPAMD_CRYPTOBOX_KEYPAIR_SIG_NIST(kp)->pk; } } /* Not reached */ return NULL; } static void * rspamd_cryptobox_pubkey_pk(const struct rspamd_cryptobox_pubkey *kp, guint *len) { g_assert(kp != NULL); if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) { 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; } } else { if (kp->type == RSPAMD_KEYPAIR_KEX) { *len = 65; return RSPAMD_CRYPTOBOX_PUBKEY_NIST(kp)->pk; } else { *len = 65; return RSPAMD_CRYPTOBOX_PUBKEY_SIG_NIST(kp)->pk; } } /* Not reached */ return NULL; } static struct rspamd_cryptobox_keypair * rspamd_cryptobox_keypair_alloc(enum rspamd_cryptobox_keypair_type type, enum rspamd_cryptobox_mode alg) { struct rspamd_cryptobox_keypair *kp; guint size = 0; if (alg == RSPAMD_CRYPTOBOX_MODE_25519) { if (type == RSPAMD_KEYPAIR_KEX) { size = sizeof(struct rspamd_cryptobox_keypair_25519); } else { size = sizeof(struct rspamd_cryptobox_keypair_sig_25519); } } else { if (type == RSPAMD_KEYPAIR_KEX) { size = sizeof(struct rspamd_cryptobox_keypair_nist); } else { size = sizeof(struct rspamd_cryptobox_keypair_sig_nist); } } 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, enum rspamd_cryptobox_mode alg) { struct rspamd_cryptobox_pubkey *pk; guint size = 0; if (alg == RSPAMD_CRYPTOBOX_MODE_25519) { if (type == RSPAMD_KEYPAIR_KEX) { size = sizeof(struct rspamd_cryptobox_pubkey_25519); } else { size = sizeof(struct rspamd_cryptobox_pubkey_sig_25519); } } else { if (type == RSPAMD_KEYPAIR_KEX) { size = sizeof(struct rspamd_cryptobox_pubkey_nist); } else { size = sizeof(struct rspamd_cryptobox_pubkey_sig_nist); } } 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; guint 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, enum rspamd_cryptobox_mode alg) { struct rspamd_cryptobox_keypair *kp; void *pk, *sk; guint size; kp = rspamd_cryptobox_keypair_alloc(type, alg); kp->alg = alg; 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, alg); } else { rspamd_cryptobox_keypair_sig(pk, sk, alg); } 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; } enum rspamd_cryptobox_mode rspamd_keypair_alg(struct rspamd_cryptobox_keypair *kp) { g_assert(kp != NULL); return kp->alg; } enum rspamd_cryptobox_mode rspamd_pubkey_alg(struct rspamd_cryptobox_pubkey *p) { g_assert(p != NULL); return p->alg; } struct rspamd_cryptobox_pubkey * rspamd_pubkey_from_base32(const gchar *b32, gsize len, enum rspamd_cryptobox_keypair_type type, enum rspamd_cryptobox_mode alg) { guchar *decoded; gsize dlen, expected_len; guint pklen; struct rspamd_cryptobox_pubkey *pk; guchar *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) ? rspamd_cryptobox_pk_bytes(alg) : rspamd_cryptobox_pk_sig_bytes(alg); if (dlen != expected_len) { g_free(decoded); return NULL; } pk = rspamd_cryptobox_pubkey_alloc(type, alg); REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor); pk->alg = alg; 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 gchar *hex, gsize len, enum rspamd_cryptobox_keypair_type type, enum rspamd_cryptobox_mode alg) { guchar *decoded; gsize dlen, expected_len; guint pklen; struct rspamd_cryptobox_pubkey *pk; guchar *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) ? rspamd_cryptobox_pk_bytes(alg) : rspamd_cryptobox_pk_sig_bytes(alg); if (dlen != expected_len) { g_free(decoded); return NULL; } pk = rspamd_cryptobox_pubkey_alloc(type, alg); REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor); pk->alg = alg; 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 guchar *raw, gsize len, enum rspamd_cryptobox_keypair_type type, enum rspamd_cryptobox_mode alg) { gsize expected_len; guint pklen; struct rspamd_cryptobox_pubkey *pk; guchar *pk_data; g_assert(raw != NULL && len > 0); expected_len = (type == RSPAMD_KEYPAIR_KEX) ? rspamd_cryptobox_pk_bytes(alg) : rspamd_cryptobox_pk_sig_bytes(alg); if (len != expected_len) { return NULL; } pk = rspamd_cryptobox_pubkey_alloc(type, alg); REF_INIT_RETAIN(pk, rspamd_cryptobox_pubkey_dtor); pk->alg = alg; 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 guchar * 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 guchar *) &p->nm->sk_id, sizeof(guint64)) == 0) { return p->nm->nm; } /* Wrong ID, need to recalculate */ REF_RELEASE(p->nm); p->nm = NULL; } return NULL; } const guchar * rspamd_pubkey_calculate_nm(struct rspamd_cryptobox_pubkey *p, struct rspamd_cryptobox_keypair *kp) { g_assert(kp->alg == p->alg); 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(guint64)); REF_INIT_RETAIN(p->nm, rspamd_cryptobox_nm_dtor); } if (kp->alg == RSPAMD_CRYPTOBOX_MODE_25519) { 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, p->alg); } else { struct rspamd_cryptobox_pubkey_nist *rk_nist = RSPAMD_CRYPTOBOX_PUBKEY_NIST(p); struct rspamd_cryptobox_keypair_nist *sk_nist = RSPAMD_CRYPTOBOX_KEYPAIR_NIST(kp); rspamd_cryptobox_nm(p->nm->nm, rk_nist->pk, sk_nist->sk, p->alg); } return p->nm->nm; } const guchar * 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 guchar * rspamd_pubkey_get_id(struct rspamd_cryptobox_pubkey *pk) { g_assert(pk != NULL); return pk->id; } const guchar * rspamd_pubkey_get_pk(struct rspamd_cryptobox_pubkey *pk, guint *len) { guchar *ret = NULL; guint rlen; ret = rspamd_cryptobox_pubkey_pk(pk, &rlen); if (len) { *len = rlen; } return ret; } static void rspamd_keypair_print_component(guchar *data, gsize datalen, GString *res, guint how, const gchar *description) { gint olen, b32_len; if (how & RSPAMD_KEYPAIR_HUMAN) { rspamd_printf_gstring(res, "%s: ", description); } if (how & RSPAMD_KEYPAIR_BASE32) { 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 (how & RSPAMD_KEYPAIR_HEX) { rspamd_printf_gstring(res, "%*xs", (gint) 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, guint how) { GString *res; guint 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"); } if ((how & RSPAMD_KEYPAIR_PRIVKEY)) { p = rspamd_cryptobox_keypair_sk(kp, &len); rspamd_keypair_print_component(p, len, res, how, "Private key"); } if ((how & RSPAMD_KEYPAIR_ID_SHORT)) { rspamd_keypair_print_component(kp->id, RSPAMD_KEYPAIR_SHORT_ID_LEN, res, how, "Short key ID"); } if ((how & RSPAMD_KEYPAIR_ID)) { rspamd_keypair_print_component(kp->id, sizeof(kp->id), res, how, "Key ID"); } return res; } GString * rspamd_pubkey_print(struct rspamd_cryptobox_pubkey *pk, guint how) { GString *res; guint 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"); } if ((how & RSPAMD_KEYPAIR_ID_SHORT)) { rspamd_keypair_print_component(pk->id, RSPAMD_KEYPAIR_SHORT_ID_LEN, res, how, "Short key ID"); } if ((how & RSPAMD_KEYPAIR_ID)) { rspamd_keypair_print_component(pk->id, sizeof(pk->id), res, how, "Key ID"); } return res; } const guchar * rspamd_keypair_component(struct rspamd_cryptobox_keypair *kp, guint ncomp, guint *len) { guint rlen = 0; const guchar *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 gchar *str; enum rspamd_cryptobox_keypair_type type = RSPAMD_KEYPAIR_KEX; enum rspamd_cryptobox_mode mode = RSPAMD_CRYPTOBOX_MODE_25519; gboolean is_hex = FALSE; struct rspamd_cryptobox_keypair *kp; guint len; gsize ucl_len; gint 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, "algorithm"); if (elt && ucl_object_type(elt) == UCL_STRING) { str = ucl_object_tostring(elt); if (g_ascii_strcasecmp(str, "curve25519") == 0) { mode = RSPAMD_CRYPTOBOX_MODE_25519; } else if (g_ascii_strcasecmp(str, "nistp256") == 0) { mode = RSPAMD_CRYPTOBOX_MODE_NIST; } /* 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) { is_hex = TRUE; } /* TODO: handle errors */ } kp = rspamd_cryptobox_keypair_alloc(type, mode); kp->type = type; kp->alg = mode; 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); if (is_hex) { dec_len = rspamd_decode_hex_buf(str, ucl_len, target, len); } else { dec_len = rspamd_decode_base32_buf(str, ucl_len, target, len, RSPAMD_BASE32_DEFAULT); } if (dec_len != (gint) len) { rspamd_keypair_unref(kp); return NULL; } target = rspamd_cryptobox_keypair_pk(kp, &len); str = ucl_object_tolstring(pubkey, &ucl_len); if (is_hex) { dec_len = rspamd_decode_hex_buf(str, ucl_len, target, len); } else { dec_len = rspamd_decode_base32_buf(str, ucl_len, target, len, RSPAMD_BASE32_DEFAULT); } if (dec_len != (gint) 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_keypair_dump_flags flags) { ucl_object_t *ucl_out, *elt; gint how = 0; GString *keypair_out; const gchar *encoding; g_assert(kp != NULL); if (flags & RSPAMD_KEYPAIR_DUMP_HEX) { how |= RSPAMD_KEYPAIR_HEX; encoding = "hex"; } else { how |= RSPAMD_KEYPAIR_BASE32; encoding = "base32"; } 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); } /* pubkey part */ keypair_out = rspamd_keypair_print(kp, RSPAMD_KEYPAIR_PUBKEY | how); 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, RSPAMD_KEYPAIR_PRIVKEY | how); 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, RSPAMD_KEYPAIR_ID | how); ucl_object_insert_key(elt, ucl_object_fromlstring(keypair_out->str, keypair_out->len), "id", 0, false); g_string_free(keypair_out, TRUE); ucl_object_insert_key(elt, ucl_object_fromstring(encoding), "encoding", 0, false); ucl_object_insert_key(elt, ucl_object_fromstring( kp->alg == RSPAMD_CRYPTOBOX_MODE_NIST ? "nistp256" : "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 guchar *in, gsize inlen, guchar **out, gsize *outlen, GError **err) { const guchar *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) + rspamd_cryptobox_pk_bytes(kp->alg) + rspamd_cryptobox_mac_bytes(kp->alg) + rspamd_cryptobox_nonce_bytes(kp->alg)) { 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 + rspamd_cryptobox_pk_bytes(kp->alg); nonce = mac + rspamd_cryptobox_mac_bytes(kp->alg); data = nonce + rspamd_cryptobox_nonce_bytes(kp->alg); 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, kp->alg)) { 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 guchar *in, gsize inlen, guchar **out, gsize *outlen, GError **err) { guchar *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, kp->alg); olen = inlen + sizeof(encrypted_magic) + rspamd_cryptobox_pk_bytes(kp->alg) + rspamd_cryptobox_mac_bytes(kp->alg) + rspamd_cryptobox_nonce_bytes(kp->alg); *out = g_malloc(olen); memcpy(*out, encrypted_magic, sizeof(encrypted_magic)); pubkey = *out + sizeof(encrypted_magic); mac = pubkey + rspamd_cryptobox_pk_bytes(kp->alg); nonce = mac + rspamd_cryptobox_mac_bytes(kp->alg); data = nonce + rspamd_cryptobox_nonce_bytes(kp->alg); ottery_rand_bytes(nonce, rspamd_cryptobox_nonce_bytes(kp->alg)); memcpy(data, in, inlen); memcpy(pubkey, rspamd_keypair_component(kp, RSPAMD_KEYPAIR_COMPONENT_PK, NULL), rspamd_cryptobox_pk_bytes(kp->alg)); rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, pubkey, rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), mac, kp->alg); rspamd_keypair_unref(local); if (outlen) { *outlen = olen; } return TRUE; } gboolean rspamd_pubkey_encrypt(struct rspamd_cryptobox_pubkey *pk, const guchar *in, gsize inlen, guchar **out, gsize *outlen, GError **err) { guchar *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, pk->alg); olen = inlen + sizeof(encrypted_magic) + rspamd_cryptobox_pk_bytes(pk->alg) + rspamd_cryptobox_mac_bytes(pk->alg) + rspamd_cryptobox_nonce_bytes(pk->alg); *out = g_malloc(olen); memcpy(*out, encrypted_magic, sizeof(encrypted_magic)); pubkey = *out + sizeof(encrypted_magic); mac = pubkey + rspamd_cryptobox_pk_bytes(pk->alg); nonce = mac + rspamd_cryptobox_mac_bytes(pk->alg); data = nonce + rspamd_cryptobox_nonce_bytes(pk->alg); ottery_rand_bytes(nonce, rspamd_cryptobox_nonce_bytes(pk->alg)); memcpy(data, in, inlen); memcpy(pubkey, rspamd_pubkey_get_pk(pk, NULL), rspamd_cryptobox_pk_bytes(pk->alg)); rspamd_cryptobox_encrypt_inplace(data, inlen, nonce, pubkey, rspamd_keypair_component(local, RSPAMD_KEYPAIR_COMPONENT_SK, NULL), mac, pk->alg); rspamd_keypair_unref(local); if (outlen) { *outlen = olen; } return TRUE; }