libqaeda

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | README | LICENSE

commit 90652402f4a2db73d2f853a28aa7e366cafb49eb
parent 6b9e7d0adaa5dffdea06a5c8c35aa8f6209e4b84
Author: lash <dev@holbrook.no>
Date:   Fri,  4 Apr 2025 22:41:15 +0100

WIP implementing certificate and message verify

Diffstat:
Msrc/crypto/gcrypt.c | 1+
Msrc/lq/cert.c | 23+++++++++++++++++++++--
Msrc/lq/cert.h | 27++++++++++++++++++++++-----
Msrc/lq/crypto.h | 5+++++
Msrc/lq/err.h | 5+++--
Msrc/lq/msg.c | 54++++++++++++++++++++++++++++++++++++++++++------------
Msrc/lq/msg.h | 14++++++++++++++
Msrc/test/test_cert.c | 57++++++++++++++++++++++++++++++++++++++++++++++-----------
8 files changed, 154 insertions(+), 32 deletions(-)

diff --git a/src/crypto/gcrypt.c b/src/crypto/gcrypt.c @@ -936,6 +936,7 @@ LQSig* lq_privatekey_sign(LQPrivKey *pk, const char *data, size_t data_len, cons LQSig *sig; if ((pk->key_state & LQ_KEY_LOCK) > 0) { + debug_logerr(LLOG_INFO, ERR_KEY_LOCK, "key locked"); return NULL; } diff --git a/src/lq/cert.c b/src/lq/cert.c @@ -156,8 +156,27 @@ int lq_certificate_sign(LQCert *cert, LQPrivKey *pk) { return ERR_OK; } -int lq_certificate_verify(LQCert *cert, LQPubKey *req_key, LQPubKey *res_key) { - return ERR_SUPPORT; +int lq_certificate_verify(LQCert *cert) { + LQCert cert_valid; + char out[LQ_BLOCKSIZE]; + + int r; + + lq_cpy(&cert_valid, cert, sizeof(LQCert)); + cert_valid.response = NULL; + cert_valid.response_sig = NULL; + + r = state_digest(cert, out, 0); + if (r != ERR_OK) { + return r; + } + + r = lq_msg_verify_extra(cert->request, cert->request_sig, NULL, out, LQ_DIGEST_LEN); + if (r != ERR_OK) { + return r; + } + + return ERR_OK; } int lq_certificate_serialize(LQCert *cert, char *out, size_t *out_len, LQResolve *resolve) { diff --git a/src/lq/cert.h b/src/lq/cert.h @@ -35,8 +35,6 @@ struct lq_certificate_t { * * \param[in] Previous certificate to link to. * \param[in] Context to control behavior of certificate processing. If NULL, default behavior will be used. - * \param[in] Request message. - * \param[in] Response message. * \return The allocated certificate object. It is the caller's responsibility to free the object. * \todo request and response message does not make sense to set without option to set signature, factor out to separate functions. * \see lq_certificate_free @@ -103,16 +101,35 @@ int lq_certificate_deserialize(LQCert **cert, char *in, size_t in_len, LQResolve /** - * @brief UNIMPLEMENTED verify the integrity of a certificate. Specifically that signatures in the certificate match given keys and data. + * @brief Verify the integrity of a certificate. Specifically that signatures in the certificate match given keys and data. + * + * The cert must have either + * - no message (NOOP) + * - request message and request_message signature + * - request AND response message, with request AND response signatures + * + * Messages must have public key set. + * * @param[in] Certificate to verify * @return ERR_OK if verified, or: - * .... */ -int lq_certificate_verify(LQCert *cert, LQPubKey *req_key, LQPubKey *res_key); +int lq_certificate_verify(LQCert *cert); +/*** + * \brief + * + * Certificate object takes responsibility to free the message object when it is freed. + * + */ int lq_certificate_request(LQCert *cert, LQMsg *req, LQPrivKey *pk); +/*** + * \brief + * + * Certificate object takes responsibility to free the message object when it is freed. + * + */ int lq_certificate_respond(LQCert *cert, LQMsg *rsp, LQPrivKey *pk); diff --git a/src/lq/crypto.h b/src/lq/crypto.h @@ -152,6 +152,8 @@ LQPubKey* lq_publickey_new(const char *full); /** * \brief Create a new public key object from a private key. * + * Will set the private key property with the given private key. The private key must be freed independently. + * * \param[in] Private key to generate public key from. * \return Pointer to new public key. Freeing the object is the caller's responsibility. * \see lq_publickey_free @@ -236,6 +238,9 @@ int lq_signature_verify(LQSig *sig, const char *msg, size_t msg_len); /** * \brief Free an allocated public key. + * + * Does not free the associated private key. + * * \param[in] Public key to free. */ void lq_publickey_free(LQPubKey *pubk); diff --git a/src/lq/err.h b/src/lq/err.h @@ -7,8 +7,9 @@ /// Error values used across all error contexts. enum err_e { RERR_PFX_LQ = 0x100, - ERR_REQUEST = 0x101, ///< Error related to certificate request messages - ERR_RESPONSE = 0x102, ///< Error related to certificate response messages + ERR_NONSENSE = 0x101, ///< Available data does not make sense in context + ERR_REQUEST = 0x102, ///< Error related to certificate request messages + ERR_RESPONSE = 0x103, ///< Error related to certificate response messages RERR_PFX_CRYPTO = 0x200, ERR_NOCRYPTO = 0x201, diff --git a/src/lq/msg.c b/src/lq/msg.c @@ -1,6 +1,8 @@ #include <stddef.h> #include <time.h> #include <libtasn1.h> +#include <endian.h> +#include <llog.h> #include "lq/msg.h" #include "lq/mem.h" @@ -8,7 +10,7 @@ #include "lq/crypto.h" #include "lq/wire.h" #include "lq/store.h" -#include "endian.h" +#include "debug.h" static char zeros[LQ_PUBKEY_LEN]; static LQPubKey nokey = { @@ -34,31 +36,59 @@ LQSig* lq_msg_sign(LQMsg *msg, LQPrivKey *pk, const char *salt) { return lq_msg_sign_extra(msg, pk, salt, NULL, 0); } -LQSig* lq_msg_sign_extra(LQMsg *msg, LQPrivKey *pk, const char *salt, const char *extra, size_t extra_len) { +static int msg_to_sign(LQMsg *msg, char *out, const char *extra, size_t extra_len) { int l; int r; - char *data; - char digest[LQ_DIGEST_LEN]; + char data[LQ_BLOCKSIZE]; - if (extra == NULL) { - extra_len = 0; - } l = msg->len + extra_len; - data = lq_alloc(l); if (extra_len > 0) { lq_cpy(data, extra, extra_len); } lq_cpy(data + extra_len, msg->data, msg->len); - msg->pubkey = lq_publickey_from_privatekey(pk); - r = lq_digest(data, l, (char*)digest); - if (r != ERR_OK) { + return lq_digest(data, l, out); +} + +LQSig* lq_msg_sign_extra(LQMsg *msg, LQPrivKey *pk, const char *salt, const char *extra, size_t extra_len) { + int r; + char digest[LQ_DIGEST_LEN]; + + if (extra == NULL) { + extra_len = 0; + } + if (msg->pubkey == NULL) { + msg->pubkey = lq_publickey_from_privatekey(pk); + if (msg->pubkey == NULL) { + debug_logerr(LLOG_INFO, ERR_NOKEY, "public key"); + return NULL; + } + } + r = msg_to_sign(msg, digest, extra, extra_len); + if (r) { + debug_logerr(LLOG_INFO, r, "sign message"); return NULL; } - lq_free(data); return lq_privatekey_sign(pk, digest, LQ_DIGEST_LEN, salt); } +int lq_msg_verify_extra(LQMsg *msg, LQSig *sig, const char *salt, const char *extra, size_t extra_len) { + int r; + char digest[LQ_DIGEST_LEN]; + + if (msg->pubkey == NULL) { + return debug_logerr(LLOG_INFO, ERR_NONSENSE, "missing pubkey"); + } + if (extra == NULL) { + extra_len = 0; + } + r = msg_to_sign(msg, digest, extra, extra_len); + if (r) { + return debug_logerr(LLOG_INFO, r, "verify message"); + } + return lq_signature_verify(sig, digest, LQ_DIGEST_LEN); +} + void lq_msg_free(LQMsg *msg) { //if (msg->pubkey->pk = NULL) { if (msg->pubkey != NULL) { diff --git a/src/lq/msg.h b/src/lq/msg.h @@ -54,6 +54,20 @@ LQSig* lq_msg_sign(LQMsg *msg, LQPrivKey *pk, const char *salt); LQSig* lq_msg_sign_extra(LQMsg *msg, LQPrivKey *pk, const char *salt, const char *extra, size_t extra_len); /** + * @brief Verify the signature over the message with specified salt value. The salt value length must be LQ_SALT_LEN. + * + * The message will be verified against the public key defined in the message structure. + * + * @param[in] Message to verify. (Must have the publickey member set). + * @param[in] Salt data to secure signature with. Set to NULL if salt is not to be used. + * @param[in] Extra data to prefix message data with when calculating digest. If set to NULL, only message data will be used in digest. + * @param[in] Length of extra data. Ignored if extra data is NULL. + * @return ERR_OK on valid signature, ERR_NONSENSE if publickey missing. Any other value indicates failure. + * @see lq_signature_free + */ +int lq_msg_verify_extra(LQMsg *msg, LQSig *sig, const char *salt, const char *extra, size_t extra_len); + +/** * @brief Serialize message data payload for inclusion in certificate. * @param[in] Message to serialize. * @param[out] Output buffer. diff --git a/src/test/test_cert.c b/src/test/test_cert.c @@ -32,23 +32,20 @@ static const char passphrase[32] = { START_TEST(check_cert_sig_req) { int r; - size_t c; LQCert *cert; LQMsg *req; LQPrivKey *pk; - LQPubKey *pubk; - pk = lq_privatekey_new(passphrase, LQ_PRIVKEY_LEN); + pk = lq_privatekey_new(passphrase, strlen(passphrase)); ck_assert_ptr_nonnull(pk); + r = lq_privatekey_unlock(pk, passphrase, strlen(passphrase)); + ck_assert_int_eq(r, 0); - pubk = lq_publickey_from_privatekey(pk); - ck_assert_ptr_nonnull(pubk); + cert = lq_certificate_new(NULL); + ck_assert_ptr_nonnull(cert); req = lq_msg_new("foo", 4); ck_assert_ptr_nonnull(req); - - cert = lq_certificate_new(NULL); - ck_assert_ptr_nonnull(cert); r = lq_certificate_request(cert, req, pk); ck_assert_int_eq(r, 0); @@ -57,15 +54,53 @@ START_TEST(check_cert_sig_req) { //r = lq_certificate_respond(cert, res, pk_bob); //ck_assert_int_eq(r, 0); - r = lq_certificate_verify(cert, pubk, NULL); + r = lq_certificate_verify(cert); ck_assert_int_eq(r, 0); lq_certificate_free(cert); - lq_publickey_free(pubk); lq_privatekey_free(pk); } END_TEST +START_TEST(check_cert_sig_res) { + int r; + LQCert *cert; + LQMsg *req; + LQMsg *res; + LQPrivKey *pk_alice; + LQPrivKey *pk_bob; + + pk_alice = lq_privatekey_new(passphrase, LQ_PRIVKEY_LEN); + ck_assert_ptr_nonnull(pk_alice); + r = lq_privatekey_unlock(pk_alice, passphrase, strlen(passphrase)); + ck_assert_int_eq(r, 0); + + pk_bob = lq_privatekey_new(passphrase, LQ_PRIVKEY_LEN); + ck_assert_ptr_nonnull(pk_bob); + r = lq_privatekey_unlock(pk_bob, passphrase, strlen(passphrase)); + ck_assert_int_eq(r, 0); + cert = lq_certificate_new(NULL); + ck_assert_ptr_nonnull(cert); + + req = lq_msg_new("foo", 4); + ck_assert_ptr_nonnull(req); + r = lq_certificate_request(cert, req, pk_alice); + ck_assert_int_eq(r, 0); + + res = lq_msg_new("barbaz", 7); + ck_assert_ptr_nonnull(res); + r = lq_certificate_respond(cert, res, pk_bob); + ck_assert_int_eq(r, 0); + + r = lq_certificate_verify(cert); + ck_assert_int_eq(r, 0); + + lq_certificate_free(cert); + lq_privatekey_free(pk_bob); + lq_privatekey_free(pk_alice); +} +END_TEST + START_TEST(check_cert_symmetric_nomsg) { int r; size_t c; @@ -207,9 +242,9 @@ Suite * common_suite(void) { s = suite_create("cert"); tc = tcase_create("sign"); - tcase_add_test(tc, check_cert_sig_req); // tcase_add_test(tc, check_cert_sig_res); + suite_add_tcase(s, tc); tc = tcase_create("serialize"); // tcase_add_test(tc, check_cert_symmetric_ser_nomsg);