diff options
-rw-r--r-- | include/noscryptutil.h | 125 | ||||
-rw-r--r-- | src/nc-util.h | 19 | ||||
-rw-r--r-- | src/noscrypt.c | 168 | ||||
-rw-r--r-- | src/noscryptutil.c | 433 | ||||
-rw-r--r-- | tests/hex.h | 51 | ||||
-rw-r--r-- | tests/test.c | 107 |
6 files changed, 600 insertions, 303 deletions
diff --git a/include/noscryptutil.h b/include/noscryptutil.h index 1a98698..7db5738 100644 --- a/include/noscryptutil.h +++ b/include/noscryptutil.h @@ -19,9 +19,8 @@ */ /* -* noscrypt is a an open-source, strict C89 library that performs the basic -* cryptographic operations found in the Nostr protocol. It is designed to be -* portable and easy to use in any C89 compatible environment. It is also designed +* This header includes some optional high-level nostr crypto utility functions +* for much easer app development. */ #pragma once @@ -33,42 +32,132 @@ extern "C" { #endif -#include <stdlib.h> #include "noscrypt.h" #define E_OUT_OF_MEMORY -10 -typedef struct nc_util_enc_struct NCUtilEncryptionContext; +#define NC_UTIL_CIPHER_MODE_ENCRYPT 0x00ui32 +#define NC_UTIL_CIPHER_MODE_DECRYPT 0x01ui32 +#define NC_UTIL_CIPHER_ZERO_ON_FREE 0x02ui32 -NC_EXPORT NCResult NC_CC NCUtilGetEncryptionPaddedSize(uint32_t encVersion, int32_t plaintextSize); +/* +* The encryption context structure. This structure is used to store the state +* of the encryption operation. The structure is opaque and should not be accessed +* directly. +*/ +typedef struct nc_util_enc_struct NCUtilCipherContext; + +/* +* Gets the size of the padded buffer required for an encryption operation. +* @param encVersion The encryption specification version to use +* @param plaintextSize The size of the plaintext buffer in bytes +* @return The size of the padded buffer in bytes +*/ +NC_EXPORT NCResult NC_CC NCUtilGetEncryptionPaddedSize(uint32_t encVersion, uint32_t plaintextSize); -NC_EXPORT NCResult NC_CC NCUtilGetEncryptionBufferSize(uint32_t encVersion, int32_t plaintextSize); +/* +* Gets the size of the payload buffer required for an encryption operation. +* @param encVersion The encryption specification version to use +* @param plaintextSize The size of the plaintext buffer in bytes +* @return The size of the payload buffer in bytes +* @note The payload buffer is the final buffer to be sent to a nostr user. For nip04 this +* is a raw AES message, for nip44 this is a mucher lager buffer. See the nostr specifications +* for more information. +*/ +NC_EXPORT NCResult NC_CC NCUtilGetEncryptionBufferSize(uint32_t encVersion, uint32_t plaintextSize); -NC_EXPORT NCUtilEncryptionContext* NC_CC NCUtilAllocEncryptionContext(uint32_t encVersion); +/* +* Allocates a new encryption context and sets the encryption version and flags. The encryption context +* must be freed with NCUtilCipherFree when it is no longer needed. +* @param encVersion The encryption specification version to use +* @param flags The flags to set on the encryption context +* @return A valid pointer to an encryption context or NULL if the operation failed +*/ +NC_EXPORT NCUtilCipherContext* NC_CC NCUtilCipherAlloc(uint32_t encVersion, uint32_t flags); -NC_EXPORT NCResult NC_CC NCUtilInitEncryptionContext( - NCUtilEncryptionContext* encCtx, - const uint8_t* plainText, - uint32_t plainTextSize +/* +* Initializes the encryption context with the input data and size. This function will + internally allocate a the required output buffer for the ciper operation. You may only call + this function once. +* @param encCtx A valid pointer to an allocated encryption context +* @param inputData A pointer to the input data for the ciper +* @param inputSize The size of the input data +* @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 NCUtilCipherInit( + NCUtilCipherContext* encCtx, + const uint8_t* inputData, + uint32_t inputSize ); -NC_EXPORT void NC_CC NCUtilFreeEncryptionContext(NCUtilEncryptionContext* encCtx); +/* +* Frees the encryption context and clears the memory if the NC_UTIL_CIPHER_ZERO_ON_FREE +* flag is set. +* @param encCtx A valid pointer to an allocated encryption context to free +*/ +NC_EXPORT void NC_CC NCUtilCipherFree(NCUtilCipherContext* encCtx); -NC_EXPORT NCResult NC_CC NCUtilGetEncryptedSize(const NCUtilEncryptionContext* encCtx); +/* +* Gets the output size of the encryption context. This function will return the size of +* the output buffer that will be written to when calling NCUtilCipherReadOutput. +* @param encCtx A valid pointer to an allocated encryption context +* @return The size of the output buffer in bytes +*/ +NC_EXPORT NCResult NC_CC NCUtilCipherGetOutputSize(const NCUtilCipherContext* encCtx); -NC_EXPORT NCResult NC_CC NCUtilReadEncryptedData( - const NCUtilEncryptionContext* encCtx, +/* +* Reads the output buffer from the encryption context. This function will copy the output +* buffer to the output buffer provided. The output buffer must be at least the size of the +* output buffer returned by NCUtilCipherGetOutputSize. +* @param encCtx A valid pointer to an initialized encryption context +* @param output A pointer to the output buffer to copy the output to +* @param outputSize The size of the output buffer in bytes +* @returns The number of bytes written to the output buffer or an error code. Use NCParseErrorCode +* to get the error code and positional argument that caused the error +*/ +NC_EXPORT NCResult NC_CC NCUtilCipherReadOutput( + const NCUtilCipherContext* encCtx, uint8_t* output, uint32_t outputSize ); -NC_EXPORT NCResult NCUtilSetEncryptionProperty( - NCUtilEncryptionContext* ctx, +/* +* Sets a property on the encryption context. Equivalent to calling NCSetEncryptionPropertyEx +* @param ctx A valid pointer to an encryption context +* @param property The property to set +* @param value A pointer to the value to set +* @param valueLen The length of the value +* @return NC_SUCCESS if the operation was successful, otherwise an error code. Use NCParseErrorCode to +* get the error code and positional argument that caused the error +*/ +NC_EXPORT NCResult NCUtilCipherSetProperty( + NCUtilCipherContext* ctx, uint32_t property, uint8_t* value, uint32_t valueLen ); +/* +* Performs the desired ciper option once. This may either cause an encryption +* or decryption operation to be performed. Regardless of the operation, input data +* is consumed and output data is produced. +* @param encCtx A valid pointer to an initialized encryption context +* @param libContext A valid pointer to an NCContext structure +* @param sk A valid pointer to the sender's private key +* @param pk A valid pointer to the receivers public key +* @return NC_SUCCESS if the operation was successful, otherwise an error code. Use NCParseErrorCode to +* get the error code and positional argument that caused the error. +* @note This function should only be called once. However it is indempotent and deterministic +* so the exact same operation should happen if called again. +*/ +NC_EXPORT NCResult NC_CC NCUtilCipherUpdate( + const NCUtilCipherContext* encCtx, + const NCContext* libContext, + const NCSecretKey* sk, + const NCPublicKey* pk +); + #ifdef __cplusplus } #endif diff --git a/src/nc-util.h b/src/nc-util.h index 0647f4c..e94a222 100644 --- a/src/nc-util.h +++ b/src/nc-util.h @@ -126,9 +126,9 @@ static _nc_fn_inline void ncSpanWrite(span_t span, uint32_t offset, const uint8_ static _nc_fn_inline void ncSpanAppend(span_t span, uint32_t* offset, const uint8_t* data, uint32_t size) { - DEBUG_ASSERT2(span.data != NULL, "Expected span to be non-null") - DEBUG_ASSERT2(offset != NULL, "Expected offset to be non-null") - DEBUG_ASSERT2(data != NULL, "Expected data to be non-null") + DEBUG_ASSERT2(span.data != NULL, "Expected span to be non-null") + DEBUG_ASSERT2(offset != NULL, "Expected offset to be non-null") + DEBUG_ASSERT2(data != NULL, "Expected data to be non-null") DEBUG_ASSERT2(*offset + size <= span.size, "Expected offset + size to be less than span size") /* Copy data to span */ @@ -138,4 +138,17 @@ static _nc_fn_inline void ncSpanAppend(span_t span, uint32_t* offset, const uint *offset += size; } +static _nc_fn_inline span_t ncSpanSlice(span_t span, uint32_t offset, uint32_t size) +{ + span_t slice; + + DEBUG_ASSERT2(span.data != NULL, "Expected span to be non-null"); + DEBUG_ASSERT2(offset + size <= span.size, "Expected offset + size to be less than span size") + + /* Initialize slice, offset input data by the specified offset */ + ncSpanInit(&slice, span.data + offset, size); + + return slice; +} + #endif /* !_NC_UTIL_H */
\ No newline at end of file diff --git a/src/noscrypt.c b/src/noscrypt.c index 910f559..01ec136 100644 --- a/src/noscrypt.c +++ b/src/noscrypt.c @@ -272,14 +272,13 @@ static cstatus_t _chachaEncipher(const struct nc_expand_keys* keys, NCEncryption static _nc_fn_inline cstatus_t _getMessageKey( const struct conversation_key* converstationKey, - const cspan_t* nonce, + cspan_t nonce, struct message_key* messageKey ) { cspan_t prkSpan; span_t okmSpan; - DEBUG_ASSERT2(nonce != NULL, "Expected valid nonce buffer") DEBUG_ASSERT2(converstationKey != NULL, "Expected valid conversation key") DEBUG_ASSERT2(messageKey != NULL, "Expected valid message key buffer") @@ -287,7 +286,7 @@ static _nc_fn_inline cstatus_t _getMessageKey( ncSpanInit(&okmSpan, messageKey->value, sizeof(struct message_key)); /* Output produces a message key (write it directly to struct memory) */ /* Nonce is the info */ - return ncCryptoSha256HkdfExpand(&prkSpan, nonce, &okmSpan); + return ncCryptoSha256HkdfExpand(&prkSpan, &nonce, &okmSpan); } static _nc_fn_inline NCResult _encryptNip44Ex( @@ -312,7 +311,7 @@ static _nc_fn_inline NCResult _encryptNip44Ex( ncSpanInitC(&nonceSpan, args->nonceData, NC_ENCRYPTION_NONCE_SIZE); /* Message key will be derrived on every encryption call */ - if (_getMessageKey(ck, &nonceSpan, &messageKey) != CSTATUS_OK) + if (_getMessageKey(ck, nonceSpan, &messageKey) != CSTATUS_OK) { result = E_OPERATION_FAILED; goto Cleanup; @@ -351,7 +350,7 @@ static _nc_fn_inline NCResult _decryptNip44Ex(const NCContext* ctx, const struct ncSpanInitC(&nonceSpan, args->nonceData, NC_ENCRYPTION_NONCE_SIZE); - if (_getMessageKey(ck, &nonceSpan, &messageKey) != CSTATUS_OK) + if (_getMessageKey(ck, nonceSpan, &messageKey) != CSTATUS_OK) { result = E_OPERATION_FAILED; goto Cleanup; @@ -372,17 +371,16 @@ Cleanup: return result; } -static _nc_fn_inline cstatus_t _computeHmac(const uint8_t key[NC_HMAC_KEY_SIZE], const cspan_t* payload, sha256_t hmacOut) +static _nc_fn_inline cstatus_t _computeHmac(const uint8_t key[NC_HMAC_KEY_SIZE], cspan_t payload, sha256_t hmacOut) { cspan_t keySpan; DEBUG_ASSERT2(key != NULL, "Expected valid hmac key") - DEBUG_ASSERT2(payload != NULL, "Expected valid mac verification args") DEBUG_ASSERT2(hmacOut != NULL, "Expected valid hmac output buffer") ncSpanInitC(&keySpan, key, NC_HMAC_KEY_SIZE); - return ncCryptoHmacSha256(&keySpan, payload, hmacOut); + return ncCryptoHmacSha256(&keySpan, &payload, hmacOut); } static NCResult _verifyMacEx( @@ -408,7 +406,7 @@ static NCResult _verifyMacEx( * Message key is again required for the hmac verification */ - if (_getMessageKey((struct conversation_key*)conversationKey, &nonceSpan, &messageKey) != CSTATUS_OK) + if (_getMessageKey((struct conversation_key*)conversationKey, nonceSpan, &messageKey) != CSTATUS_OK) { result = E_OPERATION_FAILED; goto Cleanup; @@ -420,7 +418,7 @@ static NCResult _verifyMacEx( /* * Compute the hmac of the data using the computed hmac key */ - if (_computeHmac(keys->hmac_key, &payloadSpan, hmacOut) != CSTATUS_OK) + if (_computeHmac(keys->hmac_key, payloadSpan, hmacOut) != CSTATUS_OK) { result = E_OPERATION_FAILED; goto Cleanup; @@ -888,8 +886,8 @@ Cleanup: } NC_EXPORT NCResult NC_CC NCDecryptEx( - const NCContext* ctx, - const uint8_t conversationKey[NC_CONV_KEY_SIZE], + const NCContext* ctx, + const uint8_t conversationKey[NC_CONV_KEY_SIZE], NCEncryptionArgs* args ) { @@ -906,12 +904,12 @@ NC_EXPORT NCResult NC_CC NCDecryptEx( switch (args->version) { - case NC_ENC_VERSION_NIP44: - return _decryptNip44Ex(ctx, (struct conversation_key*)conversationKey, args); + case NC_ENC_VERSION_NIP44: + return _decryptNip44Ex(ctx, (struct conversation_key*)conversationKey, args); - case NC_ENC_VERSION_NIP04: - default: - return E_VERSION_NOT_SUPPORTED; + case NC_ENC_VERSION_NIP04: + default: + return E_VERSION_NOT_SUPPORTED; } } @@ -942,26 +940,26 @@ NC_EXPORT NCResult NC_CC NCDecrypt( switch (args->version) { - case NC_ENC_VERSION_NIP44: + case NC_ENC_VERSION_NIP44: + { + if ((result = _computeSharedSecret(ctx, sk, pk, &sharedSecret)) != NC_SUCCESS) { - if ((result = _computeSharedSecret(ctx, sk, pk, &sharedSecret)) != NC_SUCCESS) - { - goto Cleanup; - } - - if ((result = _computeConversationKey(ctx, &sharedSecret, &conversationKey)) != NC_SUCCESS) - { - goto Cleanup; - } + goto Cleanup; + } - result = _decryptNip44Ex(ctx, &conversationKey, args); + if ((result = _computeConversationKey(ctx, &sharedSecret, &conversationKey)) != NC_SUCCESS) + { + goto Cleanup; } - break; - case NC_ENC_VERSION_NIP04: - default: - result = E_VERSION_NOT_SUPPORTED; - break; + result = _decryptNip44Ex(ctx, &conversationKey, args); + } + break; + + case NC_ENC_VERSION_NIP04: + default: + result = E_VERSION_NOT_SUPPORTED; + break; } Cleanup: @@ -994,7 +992,7 @@ NC_EXPORT NCResult NCComputeMac( /* * Compute the hmac of the data using the supplied hmac key */ - return _computeHmac(hmacKey, &payloadSpan, hmacOut) == CSTATUS_OK ? NC_SUCCESS : E_OPERATION_FAILED; + return _computeHmac(hmacKey, payloadSpan, hmacOut) == CSTATUS_OK ? NC_SUCCESS : E_OPERATION_FAILED; } @@ -1075,74 +1073,74 @@ NC_EXPORT NCResult NCSetEncryptionPropertyEx( switch (property) { - case NC_ENC_SET_VERSION: - - /* Ensure version is proper length */ - CHECK_ARG_RANGE(valueLen, sizeof(uint32_t), sizeof(uint32_t), 2) + case NC_ENC_SET_VERSION: + + /* Ensure version is proper length */ + CHECK_ARG_RANGE(valueLen, sizeof(uint32_t), sizeof(uint32_t), 2) + + args->version = *((uint32_t*)value); + + return NC_SUCCESS; + + case NC_ENC_SET_NIP04_IV: + /* + * The safest way to store the nip04 IV is in the nonce + * field. An IV is essentially a nonce. A secure random + * number used to encrypt the first block of a CBC chain. + */ + + CHECK_ARG_RANGE(valueLen, AES_IV_SIZE, UINT32_MAX, 3) + + ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP04) + + args->nonceData = value; - args->version = *((uint32_t*)value); - - return NC_SUCCESS; + return NC_SUCCESS; - case NC_ENC_SET_NIP04_IV: - /* - * The safest way to store the nip04 IV is in the nonce - * field. An IV is essentially a nonce. A secure random - * number used to encrypt the first block of a CBC chain. - */ - - CHECK_ARG_RANGE(valueLen, AES_IV_SIZE, UINT32_MAX, 3) - ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP04) + case NC_ENC_SET_NIP04_KEY: + /* + * The AES key is stored in the hmac key field, since + * it won't be used for the operating and should be the same size + * as the hmac key. + */ - args->nonceData = value; + CHECK_ARG_RANGE(valueLen, AES_KEY_SIZE, UINT32_MAX, 3) - return NC_SUCCESS; - + ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP04) - case NC_ENC_SET_NIP04_KEY: - /* - * The AES key is stored in the hmac key field, since - * it won't be used for the operating and should be the same size - * as the hmac key. - */ - - CHECK_ARG_RANGE(valueLen, AES_KEY_SIZE, UINT32_MAX, 3) + args->keyData = value; - ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP04) + return NC_SUCCESS; - args->keyData = value; + case NC_ENC_SET_NIP44_NONCE: - return NC_SUCCESS; + /* Nonce buffer must be at least the size, max doesnt matter */ + CHECK_ARG_RANGE(valueLen, NC_ENCRYPTION_NONCE_SIZE, UINT32_MAX, 3) - case NC_ENC_SET_NIP44_NONCE: - - /* Nonce buffer must be at least the size, max doesnt matter */ - CHECK_ARG_RANGE(valueLen, NC_ENCRYPTION_NONCE_SIZE, UINT32_MAX, 3) + /* Nonce is only used in nip44 mode */ + ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP44) - /* Nonce is only used in nip44 mode */ - ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP44) + args->nonceData = value; - args->nonceData = value; + return NC_SUCCESS; - return NC_SUCCESS; + case NC_ENC_SET_NIP44_MAC_KEY: - case NC_ENC_SET_NIP44_MAC_KEY: - - /* The maximum size of the buffer doesn't matter as long as its larger than the key size */ - CHECK_ARG_RANGE(valueLen, NC_HMAC_KEY_SIZE, UINT32_MAX, 3) + /* The maximum size of the buffer doesn't matter as long as its larger than the key size */ + CHECK_ARG_RANGE(valueLen, NC_HMAC_KEY_SIZE, UINT32_MAX, 3) - /* Mac key is only used in nip44 mode */ - ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP44) + /* Mac key is only used in nip44 mode */ + ENSURE_ENC_MODE(args, NC_ENC_VERSION_NIP44) - /* - * During encryption the key data buffer is used - * to write the hmac hey used for MAC computation - * operations. - */ - args->keyData = value; + /* + * During encryption the key data buffer is used + * to write the hmac hey used for MAC computation + * operations. + */ + args->keyData = value; - return NC_SUCCESS; + return NC_SUCCESS; } return E_INVALID_ARG; diff --git a/src/noscryptutil.c b/src/noscryptutil.c index b7723cb..c0eb036 100644 --- a/src/noscryptutil.c +++ b/src/noscryptutil.c @@ -19,10 +19,14 @@ */ -#include <noscryptutil.h> +#include <stdlib.h> +#include <math.h> + #include "nc-util.h" #include "nc-crypto.h" +#include <noscryptutil.h> + /* * Validation macros */ @@ -31,56 +35,78 @@ #error "Utilities library must be disabled when using extreme compat mode" #endif /* NC_EXTREME_COMPAT */ -#include <stdlib.h> -#include <math.h> - #define _nc_mem_free(x) if(x != NULL) { free(x); x = NULL; } #define _nc_mem_alloc(elements, size) calloc(elements, size); +#define ZERO_FILL ncCryptoSecureZero #ifndef NC_INPUT_VALIDATION_OFF #define CHECK_INVALID_ARG(x, argPos) if(x == NULL) return NCResultWithArgPosition(E_INVALID_ARG, argPos); #define CHECK_NULL_ARG(x, argPos) if(x == NULL) return NCResultWithArgPosition(E_NULL_PTR, argPos); #define CHECK_ARG_RANGE(x, min, max, argPos) if(x < min || x > max) return NCResultWithArgPosition(E_ARGUMENT_OUT_OF_RANGE, argPos); + #define CHECK_ARG_IS(exp, argPos) if(!(exp)) return NCResultWithArgPosition(E_INVALID_ARG, argPos); #else /* empty macros */ #define CHECK_INVALID_ARG(x) #define CHECK_NULL_ARG(x, argPos) #define CHECK_ARG_RANGE(x, min, max, argPos) + #define CHECK_ARG_IS(is, expected, argPos) #endif /* !NC_DISABLE_INPUT_VALIDATION */ /* performs a log2 on integer types */ #define _math_int_log2(x) (int32_t)log2((double)x) -#define MIN_PADDING_SIZE 0x20 -#define NIP44_VERSION_SIZE 0x01 -#define NIP44_PT_LEN_SIZE 0x02 +#define MIN_PADDING_SIZE 0x20u +#define NIP44_VERSION_SIZE 0x01u +#define NIP44_PT_LEN_SIZE sizeof(uint16_t) -/* Currently were on nip44 version 2 */ -const static uint8_t Nip44VersionValue = 0x02; +#define NC_ENC_FLAG_MODE_MASK 0x01ui32 -typedef struct nc_util_enc_buffer_state -{ - uint8_t* ciphertext; - uint32_t ciphertextSize; -} NCCipherTextOutState; +/* Currently were on nip44 version 2 */ +const static uint8_t Nip44VersionValue[1] = { 0x02u }; struct nc_util_enc_struct { - - /* Dynamically allocated during initialization */ - NCCipherTextOutState* outState; - const uint8_t* plaintext; - - uint32_t plaintextSize; + uint32_t _flags; + + cspan_t cipherInput; + + /* + The data this span points to is allocated during initialization + */ + span_t cipherOutput; NCEncryptionArgs encArgs; }; -static _nc_fn_inline int32_t _calcNip44PtPadding(int32_t plaintextSize) +static _nc_fn_inline span_t _ncUtilAllocSpan(uint32_t count, size_t size) { - int32_t chunk, nextPower, factor; + span_t span; + +#if SIZE_MAX < UINT32_MAX + + if (count > SIZE_MAX) + { + return span; + } + +#endif + + span.data = _nc_mem_alloc((size_t)count, size); + span.size = (uint32_t)count; + + return span; +} + +static _nc_fn_inline void _ncUtilFreeSpan(span_t span) +{ + _nc_mem_free(span.data); +} + +static _nc_fn_inline uint32_t _calcNip44PtPadding(uint32_t plaintextSize) +{ + uint32_t chunk, nextPower, factor; /* * Taken from https://github.com/nostr-protocol/nips/blob/master/44.md @@ -94,19 +120,20 @@ static _nc_fn_inline int32_t _calcNip44PtPadding(int32_t plaintextSize) return MIN_PADDING_SIZE; } + /* Safe to subtract because pt > 0 */ nextPower = _math_int_log2(plaintextSize - 1); - nextPower += 1; + nextPower += 1u; nextPower = 1 << nextPower; - if (nextPower <= 256) + if (nextPower <= 256u) { - chunk = 32; + chunk = 32u; } else { - chunk = nextPower / 8; + chunk = nextPower / 8u; } factor = plaintextSize - 1; @@ -118,9 +145,9 @@ static _nc_fn_inline int32_t _calcNip44PtPadding(int32_t plaintextSize) return chunk * factor; } -static _nc_fn_inline int32_t _calcNip44TotalOutSize(int32_t inputSize) +static _nc_fn_inline uint32_t _calcNip44TotalOutSize(uint32_t inputSize) { - int32_t bufferSize; + uint32_t bufferSize; /* * Buffer size for nip44 is calculated as follows: @@ -144,213 +171,281 @@ static _nc_fn_inline int32_t _calcNip44TotalOutSize(int32_t inputSize) return bufferSize; } +static _nc_fn_inline span_t _nip44GetMacData(span_t payload) +{ + DEBUG_ASSERT(payload.size > NIP44_VERSION_SIZE + NC_ENCRYPTION_MAC_SIZE); + + /* + * The nip44 mac is computed over the nonce+encrypted ciphertext + * + * the ciphertext is the entire message buffer, so it includes + * version, nonce, data, padding, and mac space available. + * + * This function will return a span that points to the nonce+data + * segment of the buffer for mac computation. + * + * The nonce sits directly after the version byte, ct is after, + * and the remaining 32 bytes are for the mac. So that means + * macData = ct.size - version.size + mac.size + */ + + return ncSpanSlice( + payload, + NIP44_VERSION_SIZE, + payload.size - (NIP44_VERSION_SIZE + NC_ENCRYPTION_MAC_SIZE) + ); +} + +static _nc_fn_inline span_t _nip44GetMacOutput(span_t payload) +{ + DEBUG_ASSERT(payload.size > NC_ENCRYPTION_MAC_SIZE); + + /* + * Mac is the final 32 bytes of the ciphertext buffer + */ + return ncSpanSlice( + payload, + payload.size - NC_ENCRYPTION_MAC_SIZE, + NC_ENCRYPTION_MAC_SIZE + ); +} + + static NCResult _nip44EncryptCompleteCore( const NCContext* libContext, const NCSecretKey* sk, const NCPublicKey* pk, NCEncryptionArgs encArgs, - span_t cipherText, - span_t plainText + cspan_t plainText, + span_t payload ) { NCResult result; + span_t macData, macOutput; uint32_t outPos, paddedCtSize; - uint16_t ptSize; - + uint8_t ptSize[2]; + uint8_t hmacKeyOut[NC_ENCRYPTION_MAC_SIZE]; + outPos = 0; - + DEBUG_ASSERT(encArgs.version == NC_ENC_VERSION_NIP44); /* Padded size is required to know how large the CT buffer is for encryption */ - paddedCtSize = (int32_t)_calcNip44PtPadding((int32_t)plainText.size); + paddedCtSize = _calcNip44PtPadding(plainText.size); /* Start by appending the version number */ - ncSpanAppend(cipherText, &outPos, &Nip44VersionValue, 0x01); + ncSpanAppend(payload, &outPos, Nip44VersionValue, 0x01); /* next is nonce data */ - ncSpanAppend(cipherText, &outPos, encArgs.nonceData, NC_ENCRYPTION_NONCE_SIZE); + ncSpanAppend(payload, &outPos, encArgs.nonceData, NC_ENCRYPTION_NONCE_SIZE); DEBUG_ASSERT(outPos == 1 + NC_ENCRYPTION_NONCE_SIZE); /* - * So this is the tricky part. The encryption operation appens directly + * Assign the hmac key from the stack buffer. Since the args structure + * is copied, it won't leak the address to the stack buffer. + * + * Should always return success for nip44 because all properties are valid + * addresses. + */ + + result = NCSetEncryptionPropertyEx( + &encArgs, + NC_ENC_SET_NIP44_MAC_KEY, + hmacKeyOut, + sizeof(hmacKeyOut) + ); + + DEBUG_ASSERT(result == NC_SUCCESS); + + /* + * So this is the tricky part. The encryption operation appens directly * on the ciphertext segment - * + * * All current implementations allow overlapping input and output buffers * so we can assign the pt segment on the encryption args */ /* - * Since the message size and padding bytes will get encrypted, + * Since the message size and padding bytes will get encrypted, * the buffer should currently point to the start of the encryption segment - * + * * The size of the data to encrypt is the padded size plus the size of the - * plainText size field. + * plainText size field. */ - encArgs.inputData = (cipherText.data + outPos); - encArgs.outputData = (cipherText.data + outPos); - encArgs.dataSize = paddedCtSize + sizeof(uint16_t); /* Plaintext + pt size must be encrypted */ + result = NCSetEncryptionData( + &encArgs, + (payload.data + outPos), + (payload.data + outPos), + paddedCtSize + NIP44_PT_LEN_SIZE /* Plaintext + pt size must be encrypted */ + ); + + DEBUG_ASSERT(result == NC_SUCCESS); - ptSize = (uint16_t)plainText.size; + /* big endian plaintext size */ + ptSize[0] = (uint8_t)(plainText.size >> 8); + ptSize[1] = (uint8_t)(plainText.size & 0xFF); - /* Can write the plainText size to buffer now */ - ncSpanAppend(cipherText, &outPos, &ptSize, sizeof(uint16_t)); + /* + * Written position must point to the end of the padded ciphertext + * area which the plaintext is written to. + * + * The plaintext data will be encrypted in place. The encrypted + * data is the entired padded region containing the leading byte count + * the plaintext data, followed by zero padding. + */ - /* concat plainText */ - ncSpanAppend(cipherText, &outPos, plainText.data, plainText.size); + ncSpanWrite(payload, outPos, ptSize, NIP44_PT_LEN_SIZE); + + ncSpanWrite( + payload, + outPos + NIP44_PT_LEN_SIZE, /* write pt directly after length */ + plainText.data, + plainText.size + ); + + /* Move position pointer directly after final padding bytes */ + outPos += encArgs.dataSize; - /* Time to perform encryption operation */ result = NCEncrypt(libContext, sk, pk, &encArgs); - if (result == NC_SUCCESS) + if (result != NC_SUCCESS) { - + return result; } -} -static NCResult _nip44EncryptCompleteCore( - NCUtilEncryptionContext* encCtx, - const NCContext* libContext, - const NCSecretKey* sk, - const NCPublicKey* pk -) -{ - span_t cipherText, plainText; + /* + MAC is computed over the nonce+encrypted data + this helper captures that data segment into a span + */ - /* Set up spans */ - ncSpanInit( - &cipherText, - encCtx->outState->ciphertext, - encCtx->outState->ciphertextSize - ); + macData = _nip44GetMacData(payload); + macOutput = _nip44GetMacOutput(payload); - ncSpanInit( - &plainText, - encCtx->plaintext, - encCtx->plaintextSize + result = NCComputeMac( + libContext, + hmacKeyOut, + macData.data, + macData.size, + macOutput.data ); - return _nip44EncryptCompleteCore( - libContext, - sk, - pk, - encCtx->encArgs, - cipherText, - plainText - ); -} + if (result != NC_SUCCESS) + { + return result; + } -NC_EXPORT NCResult NC_CC NCUtilGetEncryptionPaddedSize(uint32_t encVersion, int32_t plaintextSize) -{ - int32_t paddingSize; + outPos += NC_ENCRYPTION_MAC_SIZE; + + DEBUG_ASSERT2(outPos == payload.size, "Buffer under/overflow detected"); + + /* zero hmac key before returning */ + ZERO_FILL(hmacKeyOut, sizeof(hmacKeyOut)); - CHECK_ARG_RANGE(plaintextSize, 0, INT32_MAX, 1) + /* Notify the caller how many bytes were written */ + return NC_SUCCESS; +} + +NC_EXPORT NCResult NC_CC NCUtilGetEncryptionPaddedSize(uint32_t encVersion, uint32_t plaintextSize) +{ switch (encVersion) { - default: - return E_VERSION_NOT_SUPPORTED; - - case NC_ENC_VERSION_NIP04: - return plaintextSize; + default: + return E_VERSION_NOT_SUPPORTED; - case NC_ENC_VERSION_NIP44: - paddingSize = _calcNip44PtPadding(plaintextSize); + case NC_ENC_VERSION_NIP04: + return plaintextSize; - DEBUG_ASSERT(paddingSize > 0) + case NC_ENC_VERSION_NIP44: - return (NCResult)(paddingSize); + return (NCResult)(_calcNip44PtPadding(plaintextSize)); } } -NC_EXPORT NCResult NC_CC NCUtilGetEncryptionBufferSize(uint32_t encVersion, int32_t plaintextSize) +NC_EXPORT NCResult NC_CC NCUtilGetEncryptionBufferSize(uint32_t encVersion, uint32_t plaintextSize) { - int32_t totalSize; - - CHECK_ARG_RANGE(plaintextSize, 0, INT32_MAX, 1) switch (encVersion) { - default: - return E_VERSION_NOT_SUPPORTED; + default: + return E_VERSION_NOT_SUPPORTED; /* * NIP-04 simply uses AES to 1:1 encrypt the plainText * to ciphertext. */ - case NC_ENC_VERSION_NIP04: - return plaintextSize; - - case NC_ENC_VERSION_NIP44: - totalSize = _calcNip44TotalOutSize(plaintextSize); - - DEBUG_ASSERT(totalSize > 0) - - return (NCResult)(totalSize); + case NC_ENC_VERSION_NIP04: + return plaintextSize; + case NC_ENC_VERSION_NIP44: + return (NCResult)(_calcNip44TotalOutSize(plaintextSize)); } } -NC_EXPORT NCUtilEncryptionContext* NC_CC NCUtilAllocEncryptionContext(uint32_t encVersion) +NC_EXPORT NCUtilCipherContext* NC_CC NCUtilCipherAlloc(uint32_t encVersion, uint32_t flags) { - NCUtilEncryptionContext* encCtx; + NCUtilCipherContext* encCtx; /* * Alloc context on heap */ - encCtx = (NCUtilEncryptionContext*)_nc_mem_alloc(1, sizeof(NCUtilEncryptionContext)); + encCtx = (NCUtilCipherContext*)_nc_mem_alloc(1, sizeof(NCUtilCipherContext)); if (encCtx != NULL) { encCtx->encArgs.version = encVersion; + encCtx->_flags = flags; } return encCtx; } -NC_EXPORT void NC_CC NCUtilFreeEncryptionContext(NCUtilEncryptionContext* encCtx) +NC_EXPORT void NC_CC NCUtilCipherFree(NCUtilCipherContext* encCtx) { if (!encCtx) { return; } + /* + * If zero on free flag is set, we can zero all output memory + * before returning the buffer back to the heap + */ + if ((encCtx->_flags & NC_UTIL_CIPHER_ZERO_ON_FREE) > 0 && encCtx->cipherOutput.data) + { + ZERO_FILL(encCtx->cipherOutput.data, encCtx->cipherOutput.size); + } + /* Free output buffers */ - _nc_mem_free(encCtx->outState); + _ncUtilFreeSpan(encCtx->cipherOutput); /* context can be released */ _nc_mem_free(encCtx); } -NC_EXPORT NCResult NC_CC NCUtilInitEncryptionContext( - NCUtilEncryptionContext* encCtx, - const uint8_t* plainText, - uint32_t plainTextSize +NC_EXPORT NCResult NC_CC NCUtilCipherInit( + NCUtilCipherContext* encCtx, + const uint8_t* inputData, + uint32_t inputSize ) { - NCResult outputSize; - NCCipherTextOutState* output; - - CHECK_NULL_ARG(encCtx, 0) - CHECK_NULL_ARG(plainText, 1) - CHECK_ARG_RANGE(plainTextSize, 0, INT32_MAX, 2) + CHECK_NULL_ARG(encCtx, 0); + CHECK_NULL_ARG(inputData, 1); /* * The output state must not have alraedy been allocated */ - if (encCtx->outState) - { - return E_INVALID_ARG; - } + CHECK_ARG_IS(encCtx->cipherOutput.data == NULL, 0); /* * Calculate the correct output size to store the encryption * data for the given cipher version */ - outputSize = NCUtilGetEncryptionBufferSize(encCtx->encArgs.version, plainTextSize); + outputSize = NCUtilGetEncryptionBufferSize(encCtx->encArgs.version, inputSize); if (outputSize <= 0) { @@ -358,53 +453,50 @@ NC_EXPORT NCResult NC_CC NCUtilInitEncryptionContext( } /*Alloc output buffer within the struct */ - output = (NCCipherTextOutState*)_nc_mem_alloc(sizeof(NCCipherTextOutState) + (int)outputSize, 1); + encCtx->cipherOutput = _ncUtilAllocSpan((uint32_t)outputSize, sizeof(uint8_t)); - if (!output) + if (!encCtx->cipherOutput.data) { return E_OUT_OF_MEMORY; } - /* set cipertext buffer to end of the structure memory */ - output->ciphertext = (uint8_t*)(output + 1); - output->ciphertextSize = outputSize; - - encCtx->outState = output; - encCtx->plaintext = plainText; - encCtx->plaintextSize = plainTextSize; + ncSpanInitC(&encCtx->cipherInput, inputData, inputSize); return NC_SUCCESS; } -NC_EXPORT NCResult NC_CC NCUtilGetEncryptedSize(const NCUtilEncryptionContext* encCtx) +NC_EXPORT NCResult NC_CC NCUtilCipherGetOutputSize(const NCUtilCipherContext* encCtx) { CHECK_NULL_ARG(encCtx, 0); - return (NCResult)(encCtx->outState->ciphertextSize); + return (NCResult)(encCtx->cipherOutput.size); } -NC_EXPORT NCResult NC_CC NCUtilReadEncryptedData( - const NCUtilEncryptionContext* encCtx, +NC_EXPORT NCResult NC_CC NCUtilCipherReadOutput( + const NCUtilCipherContext* encCtx, uint8_t* output, uint32_t outputSize ) { CHECK_NULL_ARG(encCtx, 0) CHECK_NULL_ARG(output, 1) - CHECK_ARG_RANGE(outputSize, 0, INT32_MAX, 2) - if (outputSize < encCtx->outState->ciphertextSize) + if (outputSize < encCtx->cipherOutput.size) { return E_OPERATION_FAILED; } - MEMMOV(output, encCtx->outState->ciphertext, encCtx->outState->ciphertextSize); + MEMMOV( + output, + encCtx->cipherOutput.data, + encCtx->cipherOutput.size + ); - return (NCResult)encCtx->outState->ciphertextSize; + return (NCResult)encCtx->cipherOutput.size; } -NC_EXPORT NCResult NCUtilSetEncryptionProperty( - NCUtilEncryptionContext* ctx, +NC_EXPORT NCResult NCUtilCipherSetProperty( + NCUtilCipherContext* ctx, uint32_t property, uint8_t* value, uint32_t valueLen @@ -414,5 +506,50 @@ NC_EXPORT NCResult NCUtilSetEncryptionProperty( CHECK_NULL_ARG(ctx, 0) /* All other arguments are verified */ - return NCSetEncryptionPropertyEx(&ctx->encArgs, property, value, valueLen); + return NCSetEncryptionPropertyEx( + &ctx->encArgs, + property, + value, + valueLen + ); +} + +NC_EXPORT NCResult NC_CC NCUtilCipherUpdate( + const NCUtilCipherContext* encCtx, + const NCContext* libContext, + const NCSecretKey* sk, + const NCPublicKey* pk +) +{ + uint32_t mode; + + CHECK_NULL_ARG(encCtx, 0); + CHECK_NULL_ARG(libContext, 1); + CHECK_NULL_ARG(sk, 2); + CHECK_NULL_ARG(pk, 3); + + mode = encCtx->_flags & NC_ENC_FLAG_MODE_MASK; + + switch (encCtx->encArgs.version) + { + case NC_ENC_VERSION_NIP44: + if (mode == NC_UTIL_CIPHER_MODE_ENCRYPT) + { + return _nip44EncryptCompleteCore( + libContext, + sk, + pk, + encCtx->encArgs, + encCtx->cipherInput, + encCtx->cipherOutput + ); + } + else + { + return E_VERSION_NOT_SUPPORTED; + } + + default: + return E_VERSION_NOT_SUPPORTED; + } } diff --git a/tests/hex.h b/tests/hex.h index 5e90ce9..e6a2a07 100644 --- a/tests/hex.h +++ b/tests/hex.h @@ -28,18 +28,12 @@ #include <nc-util.h> -typedef struct hexBytes -{ - uint8_t* data; - size_t length; -} HexBytes; - -/* Deferred list of HexBytes to be freed on exit */ -static HexBytes* _hdeferList[10]; +/* Deferred list of span_t to be freed on exit */ +static span_t _hdeferList[20]; static size_t _hdeferListIndex = 0; /* - Allocates a HexBytes and decodes the hexadecimal string into it's binary + Allocates a span_t and decodes the hexadecimal string into it's binary representation. The string must be a valid hexadecimal string and the length and may not be NULL. The length may be known at compile time and can be used to assert the length of the string literal. @@ -48,34 +42,33 @@ static size_t _hdeferListIndex = 0; */ #define FromHexString(str, len) _fromHexString(str, sizeof(str) - 1); STATIC_ASSERT(sizeof(str)/2 == len && len > 0, "Invalid length hex string literal"); -static HexBytes* __allocHexBytes(size_t length) +static span_t __allocHexBytes(size_t length) { - HexBytes* hexBytes; + span_t hexBytes; length /= 2; - hexBytes = (HexBytes*)malloc(length + sizeof(HexBytes)); - if(!hexBytes) + hexBytes.data = malloc(length); + + if(!hexBytes.data) { - return NULL; + return hexBytes; } - hexBytes->length = length; - /* data starts after the structure size */ - hexBytes-> data = ((uint8_t*)hexBytes) + sizeof(HexBytes); + hexBytes.size = length; /* add new value to deferred cleanup list */ _hdeferList[_hdeferListIndex++] = hexBytes; return hexBytes; } -static HexBytes* _fromHexString(const char* hexLiteral, size_t strLen) +static span_t _fromHexString(const char* hexLiteral, uint32_t strLen) { - HexBytes* hexBytes; + span_t hexBytes; size_t i; if(!hexLiteral) { - return NULL; + return hexBytes; } /* alloc the raw bytes */ @@ -90,14 +83,14 @@ static HexBytes* _fromHexString(const char* hexLiteral, size_t strLen) byteString[0] = hexLiteral[i]; byteString[1] = hexLiteral[i + 1]; - hexBytes->data[i / 2] = (uint8_t)strtol(byteString, NULL, 16); + hexBytes.data[i / 2] = (uint8_t)strtol(byteString, NULL, 16); } return hexBytes; } /* - Frees all the HexBytes that were allocated by the + Frees all the span_t that were allocated by the FromHexString function. To be called at the end of the program. */ @@ -105,8 +98,8 @@ static void FreeHexBytes(void) { while(_hdeferListIndex > 0) { - free(_hdeferList[--_hdeferListIndex]); - _hdeferList[_hdeferListIndex] = NULL; + free(_hdeferList[--_hdeferListIndex].data); + memset(&_hdeferList[_hdeferListIndex], 0, sizeof(span_t)); } } @@ -127,18 +120,18 @@ static void PrintHexRaw(void* bytes, size_t len) } /* -* Prints the value of the HexBytes as a hexadecimal string -* @param hexBytes A pointer to the HexBytes structure to print the value of +* Prints the value of the span_t as a hexadecimal string +* @param hexBytes A pointer to the span_t structure to print the value of */ -static void PrintHexBytes(HexBytes* hexBytes) +static void PrintHexBytes(span_t hexBytes) { - if (!hexBytes) + if (!hexBytes.data) { puts("NULL"); } else { - PrintHexRaw(hexBytes->data, hexBytes->length); + PrintHexRaw(hexBytes.data, hexBytes.size); } } diff --git a/tests/test.c b/tests/test.c index 3833e7b..c917f52 100644 --- a/tests/test.c +++ b/tests/test.c @@ -102,7 +102,7 @@ static int TestKnownKeys(const NCContext* context); static int TestCorrectEncryption(const NCContext* context); #ifdef NC_ENABLE_UTILS -static int TestUtilFunctions(void); +static int TestUtilFunctions(const NCContext * libCtx); #endif #ifndef NC_INPUT_VALIDATION_OFF @@ -172,7 +172,7 @@ static int RunTests(void) } #ifdef NC_ENABLE_UTILS - if (TestUtilFunctions() != 0) + if (TestUtilFunctions(ctx) != 0) { return 1; } @@ -213,7 +213,7 @@ static int TestEcdsa(const NCContext* context, NCSecretKey* secKey, NCPublicKey* uint8_t sigEntropy[32]; uint8_t invalidSig[64]; - HexBytes* digestHex; + span_t digestHex; PRINTL("TEST: Ecdsa") @@ -227,8 +227,8 @@ static int TestEcdsa(const NCContext* context, NCSecretKey* secKey, NCPublicKey* /* Test signing just the message digest */ { uint8_t sig[64]; - TEST(NCSignDigest(context, secKey, sigEntropy, digestHex->data, sig), NC_SUCCESS); - TEST(NCVerifyDigest(context, pubKey, digestHex->data, sig), NC_SUCCESS); + TEST(NCSignDigest(context, secKey, sigEntropy, digestHex.data, sig), NC_SUCCESS); + TEST(NCVerifyDigest(context, pubKey, digestHex.data, sig), NC_SUCCESS); } /* Sign and verify the raw message */ @@ -245,7 +245,7 @@ static int TestEcdsa(const NCContext* context, NCSecretKey* secKey, NCPublicKey* /* Ensure operations succeed but dont print them as test cases */ ENSURE(NCSignData(context, secKey, sigEntropy, (uint8_t*)message, strlen32(message), sig1) == NC_SUCCESS); - ENSURE(NCSignDigest(context, secKey, sigEntropy, digestHex->data, sig2) == NC_SUCCESS); + ENSURE(NCSignDigest(context, secKey, sigEntropy, digestHex.data, sig2) == NC_SUCCESS); /* Perform test */ TEST(memcmp(sig1, sig2, 64), 0); @@ -256,18 +256,18 @@ static int TestEcdsa(const NCContext* context, NCSecretKey* secKey, NCPublicKey* uint8_t sig[64]; ENSURE(NCSignData(context, secKey, sigEntropy, (uint8_t*)message, strlen32(message), sig) == NC_SUCCESS); - TEST(NCVerifyDigest(context, pubKey, digestHex->data, sig), NC_SUCCESS); + TEST(NCVerifyDigest(context, pubKey, digestHex.data, sig), NC_SUCCESS); /* Now invert test, zero signature to ensure its overwritten */ ZERO_FILL(sig, sizeof(sig)); - ENSURE(NCSignDigest(context, secKey, sigEntropy, digestHex->data, sig) == NC_SUCCESS); + ENSURE(NCSignDigest(context, secKey, sigEntropy, digestHex.data, sig) == NC_SUCCESS); TEST(NCVerifyData(context, pubKey, (uint8_t*)message, strlen32(message), sig), NC_SUCCESS); } /* test verification of invalid signature */ { - TEST(NCVerifyDigest(context, pubKey, digestHex->data, invalidSig), E_INVALID_ARG); + TEST(NCVerifyDigest(context, pubKey, digestHex.data, invalidSig), E_INVALID_ARG); } PRINTL("\nPASSED: Ecdsa tests completed") @@ -504,7 +504,7 @@ static int TestPublicApiArgumentValidation() static int TestKnownKeys(const NCContext* context) { NCPublicKey pubKey; - HexBytes* secKey1, * pubKey1, * secKey2, * pubKey2; + span_t secKey1, pubKey1, secKey2, pubKey2; PRINTL("TEST: Known keys") @@ -515,18 +515,18 @@ static int TestKnownKeys(const NCContext* context) pubKey2 = FromHexString("421181660af5d39eb95e48a0a66c41ae393ba94ffeca94703ef81afbed724e5a", sizeof(NCPublicKey)); /*Test known keys*/ - TEST(NCValidateSecretKey(context, NCByteCastToSecretKey(secKey1->data)), NC_SUCCESS); + TEST(NCValidateSecretKey(context, NCByteCastToSecretKey(secKey1.data)), NC_SUCCESS); /* Recover a public key from secret key 1 */ - TEST(NCGetPublicKey(context, NCByteCastToSecretKey(secKey1->data), &pubKey), NC_SUCCESS); + TEST(NCGetPublicKey(context, NCByteCastToSecretKey(secKey1.data), &pubKey), NC_SUCCESS); /* Ensure the public key matches the known public key value */ - TEST(memcmp(pubKey1->data, &pubKey, sizeof(pubKey)), 0); + TEST(memcmp(pubKey1.data, &pubKey, sizeof(pubKey)), 0); /* Repeat with second key */ - TEST(NCValidateSecretKey(context, (NCSecretKey*)secKey2->data), NC_SUCCESS); - TEST(NCGetPublicKey(context, (NCSecretKey*)secKey2->data, &pubKey), NC_SUCCESS); - TEST(memcmp(pubKey2->data, &pubKey, sizeof(pubKey)), 0); + TEST(NCValidateSecretKey(context, NCByteCastToSecretKey(secKey2.data)), NC_SUCCESS); + TEST(NCGetPublicKey(context, NCByteCastToSecretKey(secKey2.data), &pubKey), NC_SUCCESS); + TEST(memcmp(pubKey2.data, &pubKey, sizeof(pubKey)), 0); PRINTL("\nPASSED: Known keys tests completed") return 0; @@ -614,10 +614,62 @@ static int TestCorrectEncryption(const NCContext* context) extern NCResult NCUtilGetEncryptionPaddedSize(uint32_t encVersion, int32_t plaintextSize); /* Padding tests taken from the nip44 repo vectors.json file */ -const int32_t _padTestActual[24] = { 16, 32, 33, 37, 45, 49, 64, 65, 100, 111, 200, 250, 320, 383, 384, 400, 500, 512, 515, 700, 800, 900, 1020, 65536 }; -const int32_t _padTestExpected[24] = { 32, 32, 64, 64, 64, 64, 64, 96, 128, 128, 224, 256, 320, 384, 384, 448, 512, 512, 640, 768, 896, 1024, 1024, 65536 }; +const uint32_t _padTestActual[24] = { 16, 32, 33, 37, 45, 49, 64, 65, 100, 111, 200, 250, 320, 383, 384, 400, 500, 512, 515, 700, 800, 900, 1020, 65536 }; +const uint32_t _padTestExpected[24] = { 32, 32, 64, 64, 64, 64, 64, 96, 128, 128, 224, 256, 320, 384, 384, 448, 512, 512, 640, 768, 896, 1024, 1024, 65536 }; + +static int TestUtilNip44Encryption( + const NCContext* libCtx, + span_t sendKey, + span_t recvKey, + span_t nonce, + span_t expected, + const char* plainText +) +{ + NCPublicKey recvPubKey; + span_t outData; + + ENSURE(NCValidateSecretKey(libCtx, NCByteCastToSecretKey(sendKey.data)) == NC_SUCCESS); + ENSURE(NCGetPublicKey(libCtx, NCByteCastToSecretKey(recvKey.data), &recvPubKey) == NC_SUCCESS); + + /* Alloc cipher in nip44 encryption mode */ + NCUtilCipherContext* ctx = NCUtilCipherAlloc( + NC_ENC_VERSION_NIP44, + NC_UTIL_CIPHER_MODE_ENCRYPT | NC_UTIL_CIPHER_ZERO_ON_FREE + ); + + ENSURE(ctx != NULL); + + TEST(NCUtilCipherInit(ctx, (const uint8_t*)plainText, strlen(plainText)), NC_SUCCESS); + + /* Nonce is required for nip44 encryption */ + TEST(NCUtilCipherSetProperty(ctx, NC_ENC_SET_NIP44_NONCE, nonce.data, nonce.size), NC_SUCCESS); + + /* Ciper update should return the */ + TEST(NCUtilCipherUpdate(ctx, libCtx, NCByteCastToSecretKey(sendKey.data), &recvPubKey), NC_SUCCESS); + + NCResult cipherOutputSize = NCUtilCipherGetOutputSize(ctx); + + TEST(cipherOutputSize, expected.size); -static int TestUtilFunctions(void) + outData.data = (uint8_t*)malloc(cipherOutputSize); + outData.size = (uint32_t)cipherOutputSize; + + TASSERT(outData.data != NULL); + + /* Read the encrypted payload to test */ + TEST(NCUtilCipherReadOutput(ctx, outData.data, cipherOutputSize), cipherOutputSize); + + /* Ensure encrypted payload matches */ + TEST(memcmp(outData.data, expected.data, cipherOutputSize), 0); + + free(outData.data); + + /* Free encryption memory */ + NCUtilCipherFree(ctx); +} + +static int TestUtilFunctions(const NCContext* libCtx) { PRINTL("TEST: Util functions") @@ -628,8 +680,23 @@ static int TestUtilFunctions(void) TEST(NCUtilGetEncryptionPaddedSize(NC_ENC_VERSION_NIP44, _padTestActual[i]), _padTestExpected[i]); TEST(NCUtilGetEncryptionBufferSize(NC_ENC_VERSION_NIP44, _padTestActual[i]), totalSize); } + { + PRINTL("TEST: NIP-44 util encryption") + + /* From the nip44 vectors file */ + span_t sendKey = FromHexString("0000000000000000000000000000000000000000000000000000000000000001", sizeof(NCSecretKey)); + span_t recvKey = FromHexString("0000000000000000000000000000000000000000000000000000000000000002", sizeof(NCSecretKey)); + span_t nonce = FromHexString("0000000000000000000000000000000000000000000000000000000000000001", NC_ENCRYPTION_NONCE_SIZE); + span_t payload = FromHexString("02000000000000000000000000000000000000000000000000000000000000000179ed06e5548ad3ff58ca920e6c0b4329f6040230f7e6e5641f20741780f0adc35a09794259929a02bb06ad8e8cf709ee4ccc567e9d514cdf5781af27a3e905e55b1b", 99); + const char* plainText = "a"; + + if (TestUtilNip44Encryption(libCtx, sendKey, recvKey, nonce, payload, plainText) != 0) + { + return 1; + } + } - PRINTL("PASSED: Util functions tests completed") + PRINTL("\nPASSED: Util functions tests completed") return 0; } |