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:
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);