/*- * 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); /* 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); 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 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); 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); } 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); } if (dec_len != (gint)len) { rspamd_keypair_unref (kp); return NULL; } rspamd_cryptobox_hash (kp->id, target, len, NULL, 0); return kp; } ucl_object_t * rspamd_keypair_to_ucl (struct rspamd_cryptobox_keypair *kp, gboolean is_hex) { ucl_object_t *ucl_out, *elt; gint how = 0; GString *keypair_out; const gchar *encoding; g_assert (kp != NULL); if (is_hex) { how |= RSPAMD_KEYPAIR_HEX; encoding = "hex"; } else { how |= RSPAMD_KEYPAIR_BASE32; encoding = "base32"; } 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); /* 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); return ucl_out; } gboolean rspamd_keypair_sign (struct rspamd_cryptobox_keypair *kp, const void *data, gsize len, guchar **sig, gsize *outlen, GError **err) { unsigned long long siglen; guint sklen; g_assert (kp != NULL); g_assert (data != NULL); g_assert (sig != NULL); if (kp->type != RSPAMD_KEYPAIR_SIGN) { g_set_error (err, rspamd_keypair_quark (), EINVAL, "invalid keypair: expected signature pair"); return FALSE; } siglen = rspamd_cryptobox_signature_bytes (kp->alg); *sig = g_malloc (siglen); rspamd_cryptobox_sign (*sig, &siglen, data, len, rspamd_cryptobox_keypair_sk (kp, &sklen), kp->alg); if (outlen != NULL) { *outlen = siglen; } return TRUE; } gboolean rspamd_keypair_verify (struct rspamd_cryptobox_pubkey *pk, const void *data, gsize len, const guchar *sig, gsize siglen, GError **err) { guint pklen; g_assert (pk != NULL); g_assert (data != NULL); g_assert (sig != NULL); if (pk->type != RSPAMD_KEYPAIR_SIGN) { g_set_error (err, rspamd_keypair_quark (), EINVAL, "invalid keypair: expected signature pair"); return FALSE; } if (siglen != rspamd_cryptobox_signature_bytes (pk->alg)) { g_set_error (err, rspamd_keypair_quark (), E2BIG, "invalid signature length: %d; expected %d", (int)siglen, (int) rspamd_cryptobox_signature_bytes (pk->alg)); return FALSE; } if (!rspamd_cryptobox_verify (sig, siglen, data, len, rspamd_cryptobox_pubkey_pk (pk, &pklen), pk->alg)) { g_set_error (err, rspamd_keypair_quark (), EPERM, "signature verification failed"); return FALSE; } return TRUE; } gboolean rspamd_pubkey_equal (const struct rspamd_cryptobox_pubkey *k1, const struct rspamd_cryptobox_pubkey *k2) { guchar *p1 = NULL, *p2 = NULL; guint len1, len2; if (k1->alg == k2->alg && k1->type == k2->type) { p1 = rspamd_cryptobox_pubkey_pk (k1, &len1); p2 = rspamd_cryptobox_pubkey_pk (k2, &len2); if (len1 == len2) { return (memcmp (p1, p2, len1) == 0); } } return FALSE; } 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; }