From 9f85fff3b9f25da7410569ea94f994b88feb3910 Mon Sep 17 00:00:00 2001 From: vnugent Date: Fri, 9 Feb 2024 22:48:35 -0500 Subject: feat: added/update MAC functions to sign or verify nip44 payload --- src/noscrypt.c | 182 ++++++++++++++++++++++++++++++++++++++++++++------------- src/noscrypt.h | 71 ++++++++++++++++++++++ tests/test.c | 29 +++++---- 3 files changed, 232 insertions(+), 50 deletions(-) diff --git a/src/noscrypt.c b/src/noscrypt.c index 9ab1c6e..14f959b 100644 --- a/src/noscrypt.c +++ b/src/noscrypt.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -82,7 +83,7 @@ struct nc_expand_keys { uint8_t chacha_key[CHACHA_KEY_SIZE]; uint8_t chacha_nonce[CHACHA_NONCE_SIZE]; - uint8_t hamc_key[NC_HMAC_KEY_SIZE]; + uint8_t hmac_key[NC_HMAC_KEY_SIZE]; }; struct shared_secret { @@ -286,7 +287,7 @@ static inline void _expandKeysFromHkdf(const struct message_key* hkdf, struct nc hkdfBytes += CHACHA_NONCE_SIZE; //Offset by nonce size MEMMOV( - keys->hamc_key, + keys->hmac_key, hkdfBytes, NC_HMAC_KEY_SIZE ); @@ -376,7 +377,7 @@ static inline NCResult _encryptEx( _expandKeysFromHkdf(&messageKey, &cipherKeys); //Copy the hmac key into the args - MEMMOV(hmacKey, cipherKeys.hamc_key, NC_HMAC_KEY_SIZE); + MEMMOV(hmacKey, cipherKeys.hmac_key, NC_HMAC_KEY_SIZE); //CHACHA20 result = _chachaEncipher(&cipherKeys, args); @@ -427,41 +428,6 @@ Cleanup: return result; } -/* -* Compute the sha256 digest of the data. This function should always return 0 -* on success. -*/ -static inline int _computeSha256Digest(const uint8_t* data, size_t length, uint8_t digest[32]) -{ - int result; - mbedtls_sha256_context sha256; - - DEBUG_ASSERT2(data != NULL, "Expected valid data buffer") - DEBUG_ASSERT2(digest != NULL, "Expected valid digest buffer") - - //Init the sha256 context - mbedtls_sha256_init(&sha256); - - //starting context should never fail - result = mbedtls_sha256_starts(&sha256, 0); - DEBUG_ASSERT2(result == 0, "Expected sha256 starts to return 0") - - //may fail if the data is invalid - if ((result = mbedtls_sha256_update(&sha256, data, length)) != 0) - { - goto Cleanup; - } - - //Finishing context should never fail - result = mbedtls_sha256_finish(&sha256, digest); - -Cleanup: - //Always free the context - mbedtls_sha256_free(&sha256); - - return result; -} - /* * EXTERNAL API FUNCTIONS */ @@ -626,7 +592,7 @@ NC_EXPORT NCResult NC_CC NCSignData( CHECK_NULL_ARG(sig64, 5) //Compute sha256 of the data before signing - if(_computeSha256Digest(data, dataSize, digest) != 0) + if(mbedtls_sha256(data, dataSize, digest, 0) != 0) { return E_INVALID_ARG; } @@ -683,7 +649,7 @@ NC_EXPORT NCResult NC_CC NCVerifyData( CHECK_NULL_ARG(sig64, 4) //Compute sha256 of the data before verifying - if (_computeSha256Digest(data, dataSize, digest) != 0) + if (mbedtls_sha256(data, dataSize, digest, 0) != 0) { return E_INVALID_ARG; } @@ -922,3 +888,139 @@ Cleanup: return result; } +NC_EXPORT NCResult NCComputeMac( + const NCContext* ctx, + const uint8_t hmacKey[NC_HMAC_KEY_SIZE], + const uint8_t* payload, + size_t payloadSize, + uint8_t hmacOut[NC_ENCRYPTION_MAC_SIZE] +) +{ + CHECK_NULL_ARG(ctx, 0) + CHECK_INVALID_ARG(ctx->secpCtx, 0) + CHECK_NULL_ARG(hmacKey, 1) + CHECK_NULL_ARG(payload, 2) + CHECK_ARG_RANGE(payloadSize, 1, UINT32_MAX, 3) + CHECK_NULL_ARG(hmacOut, 4) + + /* + * Compute the hmac of the data using the supplied hmac key + */ + return mbedtls_md_hmac( + _getSha256MdInfo(), + hmacKey, + NC_HMAC_KEY_SIZE, + payload, + payloadSize, + hmacOut + ) == 0 ? NC_SUCCESS : E_OPERATION_FAILED; +} + +NC_EXPORT NCResult NC_CC NCVerifyMacEx( + const NCContext* ctx, + const uint8_t conversationKey[NC_CONV_KEY_SIZE], + NCMacVerifyArgs* args +) +{ + NCResult result; + const mbedtls_md_info_t* sha256Info; + struct message_key messageKey; + struct nc_expand_keys keys; + uint8_t hmacOut[NC_ENCRYPTION_MAC_SIZE]; + + CHECK_NULL_ARG(ctx, 0) + CHECK_INVALID_ARG(ctx->secpCtx, 0) + CHECK_NULL_ARG(conversationKey, 1) + CHECK_NULL_ARG(args, 2) + + CHECK_INVALID_ARG(args->mac, 2) + CHECK_INVALID_ARG(args->payload, 2) + CHECK_INVALID_ARG(args->nonce, 2) + CHECK_ARG_RANGE(args->payloadSize, NIP44_MIN_ENC_MESSAGE_SIZE, NIP44_MAX_ENC_MESSAGE_SIZE, 2) + + sha256Info = _getSha256MdInfo(); + + /* + * We need to get the message key in order to + * get the required hmac key + */ + result = _getMessageKey( + sha256Info, + (struct conversation_key*)conversationKey, + args->nonce, + NC_ENCRYPTION_NONCE_SIZE, + &messageKey + ); + + if(result != NC_SUCCESS) + { + goto Cleanup; + } + + /* Expand keys to get the hmac-key */ + _expandKeysFromHkdf(&messageKey, &keys); + + /* + * Compute the hmac of the data using the computed hmac key + */ + if(mbedtls_md_hmac(sha256Info, keys.hmac_key, NC_HMAC_KEY_SIZE, args->payload, args->payloadSize, hmacOut) != 0) + { + result = E_OPERATION_FAILED; + goto Cleanup; + } + + /* constant time compare the macs */ + + result = 0; + + for (int i = 0; i < NC_ENCRYPTION_MAC_SIZE; i++) + { + result |= args->mac[i] - hmacOut[i]; + } + +Cleanup: + /* Clean up sensitive data */ + ZERO_FILL(&messageKey, sizeof(messageKey)); + ZERO_FILL(&keys, sizeof(keys)); + ZERO_FILL(hmacOut, NC_ENCRYPTION_MAC_SIZE); + + return result; +} + +NC_EXPORT NCResult NC_CC NCVerifyMac( + const NCContext* ctx, + const NCSecretKey* sk, + const NCPublicKey* pk, + NCMacVerifyArgs* args +) +{ + CHECK_NULL_ARG(ctx, 0) + CHECK_INVALID_ARG(ctx->secpCtx, 0) + CHECK_NULL_ARG(sk, 1) + CHECK_NULL_ARG(pk, 2) + CHECK_NULL_ARG(args, 3) + + NCResult result; + struct shared_secret sharedSecret; + struct conversation_key conversationKey; + + /* Computed the shared point so we can get the converstation key */ + if ((result = _computeSharedSecret(ctx, sk, pk, &sharedSecret)) != NC_SUCCESS) + { + goto Cleanup; + } + + if ((result = _computeConversationKey(ctx, _getSha256MdInfo(), &sharedSecret, &conversationKey)) != NC_SUCCESS) + { + goto Cleanup; + } + + result = NCVerifyMacEx(ctx, (uint8_t*)&conversationKey, args); + +Cleanup: + /* Clean up sensitive data */ + ZERO_FILL(&sharedSecret, sizeof(sharedSecret)); + ZERO_FILL(&conversationKey, sizeof(conversationKey)); + + return result; +} \ No newline at end of file diff --git a/src/noscrypt.h b/src/noscrypt.h index 387d856..983206c 100644 --- a/src/noscrypt.h +++ b/src/noscrypt.h @@ -73,6 +73,7 @@ #define NC_SHARED_SEC_SIZE 32 #define NC_CONV_KEY_SIZE 32 #define NC_HMAC_KEY_SIZE 32 +#define NC_ENCRYPTION_MAC_SIZE 32 #define NC_MESSAGE_KEY_SIZE NIP44_MESSAGE_KEY_SIZE /* @@ -167,6 +168,27 @@ typedef struct nc_encryption_struct { } NCCryptoData; +/* +* A structure for Nip44 message authentication code verification. This structure +* is used to pass arguments to the NCVerifyMac and NCVerifyMacEx functions. +*/ +typedef struct nc_mac_verify { + + /* The message authentication code certifying the Nip44 payload */ + const uint8_t mac[NC_ENCRYPTION_MAC_SIZE]; + + /* The nonce used for the original message encryption */ + const uint8_t nonce[NC_ENCRYPTION_NONCE_SIZE]; + + /* The message payload data */ + const uint8_t* payload; + + /* The size of the payload data */ + size_t payloadSize; + +} NCMacVerifyArgs; + + /* API FUNCTIONS */ @@ -397,6 +419,22 @@ NC_EXPORT NCResult NC_CC NCDecrypt( NCCryptoData* args ); +/* +* High level api for verifying a Nip44 message authentication code using a secret key +and a public key. Use the NCVerifyMacEx functions for extended verification functionality. +* @param ctx A pointer to an existing library context +* @param sk A pointer to the secret key +* @param pk A pointer to the 32byte compressed public key (x-only serialized public key) +* @param args A pointer to the mac verification arguments +* @return NC_SUCCESS if the operation was successful, otherwise an error code. Use NCParseErrorCode to +* the error code and positional argument that caused the error +*/ +NC_EXPORT NCResult NC_CC NCVerifyMac( + const NCContext* ctx, + const NCSecretKey* sk, + const NCPublicKey* pk, + NCMacVerifyArgs* args +); /*-------------------------------------- * EXTENDED ENCRYPTION API @@ -481,4 +519,37 @@ NC_EXPORT NCResult NC_CC NCDecryptEx( NCCryptoData* args ); +/* +* Verifies a Nip44 message authentication code using the given conversation key. +* @param ctx A pointer to the existing library context +* @param conversationKey A pointer to the 32byte conversation key +* @param args A pointer to the mac verification arguments +* @return NC_SUCCESS if the operation was successful, otherwise an error code. Use NCParseErrorCode to +* the error code and positional argument that caused the error. +*/ +NC_EXPORT NCResult NC_CC NCVerifyMacEx( + const NCContext* ctx, + const uint8_t conversationKey[NC_CONV_KEY_SIZE], + NCMacVerifyArgs* args +); + +/* +* Computes a message authentication code for a given payload using the given hmacKey and writes the +* mac to the hmacOut buffer. +* @param ctx A pointer to the existing library context +* @param hmacKey A pointer to the 32byte hmac key +* @param payload A pointer to the payload data buffer +* @param payloadSize The size of the payload data buffer +* @param hmacOut A pointer to the 32byte buffer to write the mac to +* @return NC_SUCCESS if the operation was successful, otherwise an error code. Use NCParseErrorCode to +* the error code and positional argument that caused the error. +*/ +NC_EXPORT NCResult NCComputeMac( + const NCContext* ctx, + const uint8_t hmacKey[NC_HMAC_KEY_SIZE], + const uint8_t* payload, + size_t payloadSize, + uint8_t hmacOut[NC_ENCRYPTION_MAC_SIZE] +); + #endif // !NOSCRYPT_H diff --git a/tests/test.c b/tests/test.c index 6c73466..0c35c21 100644 --- a/tests/test.c +++ b/tests/test.c @@ -243,7 +243,7 @@ static int TestPublicApiArgumentValidation(void) NCSecretKey secKey; NCPublicKey pubKey; NCCryptoData cryptoData; - uint8_t hmacOut[NC_HMAC_KEY_SIZE]; + uint8_t hmacKeyOut[NC_HMAC_KEY_SIZE]; PRINTL("TEST: Public API argument validation tests\n") @@ -303,25 +303,25 @@ static int TestPublicApiArgumentValidation(void) cryptoData.outputData = sig64; FillRandomData(&cryptoData.nonce, 32); - TEST(NCEncrypt(NULL, &secKey, &pubKey, hmacOut, &cryptoData), ARG_ERROR_POS_0) - TEST(NCEncrypt(&ctx, NULL, &pubKey, hmacOut, &cryptoData), ARG_ERROR_POS_1) - TEST(NCEncrypt(&ctx, &secKey, NULL, hmacOut, &cryptoData), ARG_ERROR_POS_2) + TEST(NCEncrypt(NULL, &secKey, &pubKey, hmacKeyOut, &cryptoData), ARG_ERROR_POS_0) + TEST(NCEncrypt(&ctx, NULL, &pubKey, hmacKeyOut, &cryptoData), ARG_ERROR_POS_1) + TEST(NCEncrypt(&ctx, &secKey, NULL, hmacKeyOut, &cryptoData), ARG_ERROR_POS_2) TEST(NCEncrypt(&ctx, &secKey, &pubKey, NULL, &cryptoData), ARG_ERROR_POS_3) - TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacOut, NULL), ARG_ERROR_POS_4) + TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacKeyOut, NULL), ARG_ERROR_POS_4) //Test invalid data size cryptoData.dataSize = 0; - TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacOut, &cryptoData), ARG_RAMGE_ERROR_POS_4) + TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacKeyOut, &cryptoData), ARG_RAMGE_ERROR_POS_4) //Test null input data cryptoData.dataSize = 32; cryptoData.inputData = NULL; - TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacOut, &cryptoData), ARG_INVALID_ERROR_POS_4) + TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacKeyOut, &cryptoData), ARG_INVALID_ERROR_POS_4) //Test null output data cryptoData.inputData = zero32; cryptoData.outputData = NULL; - TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacOut, &cryptoData), ARG_INVALID_ERROR_POS_4) + TEST(NCEncrypt(&ctx, &secKey, &pubKey, hmacKeyOut, &cryptoData), ARG_INVALID_ERROR_POS_4) //Decrypt cryptoData.dataSize = 32; @@ -345,8 +345,17 @@ static int TestPublicApiArgumentValidation(void) //Test null output data cryptoData.inputData = zero32; cryptoData.outputData = NULL; - TEST(NCDecrypt(&ctx, &secKey, &pubKey, &cryptoData), ARG_INVALID_ERROR_POS_3) - + TEST(NCDecrypt(&ctx, &secKey, &pubKey, &cryptoData), ARG_INVALID_ERROR_POS_3) + + { + uint8_t hmacDataOut[NC_ENCRYPTION_MAC_SIZE]; + TEST(NCComputeMac(NULL, hmacKeyOut, zero32, 32, hmacDataOut), ARG_ERROR_POS_0) + TEST(NCComputeMac(&ctx, NULL, zero32, 32, hmacDataOut), ARG_ERROR_POS_1) + TEST(NCComputeMac(&ctx, hmacKeyOut, NULL, 32, hmacDataOut), ARG_ERROR_POS_2) + TEST(NCComputeMac(&ctx, hmacKeyOut, zero32, 0, hmacDataOut), ARG_RAMGE_ERROR_POS_3) + TEST(NCComputeMac(&ctx, hmacKeyOut, zero32, 32, NULL), ARG_ERROR_POS_4) + } + PRINTL("\nPASSED: Public API argument validation tests completed\n") return 0; -- cgit