aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hkdf.c15
-rw-r--r--src/hkdf.h4
-rw-r--r--src/nc-util.h46
-rw-r--r--src/noscrypt.c15
-rw-r--r--src/noscryptutil.c418
5 files changed, 475 insertions, 23 deletions
diff --git a/src/hkdf.c b/src/hkdf.c
index 0d91d14..cff7d60 100644
--- a/src/hkdf.c
+++ b/src/hkdf.c
@@ -21,23 +21,10 @@
#include "hkdf.h"
-/* Include string for memmove */
-#include <string.h>
-
#define HKDF_MIN(a, b) (a < b ? a : b)
STATIC_ASSERT(HKDF_IN_BUF_SIZE > SHA256_DIGEST_SIZE, "HDK Buffer must be at least the size of the underlying hashing alg output")
-static _nc_fn_inline void ncWriteSpanS(span_t* span, uint32_t offset, const uint8_t* data, uint32_t size)
-{
- DEBUG_ASSERT2(span != NULL, "Expected span 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 */
- memmove(span->data + offset, data, size);
-}
-
static _nc_fn_inline void debugValidateHandler(const struct nc_hkdf_fn_cb_struct* handler)
{
DEBUG_ASSERT(handler != NULL)
@@ -114,7 +101,7 @@ cstatus_t hkdfExpandProcess(
DEBUG_ASSERT(tLen <= sizeof(t));
/* write the T buffer back to okm */
- ncWriteSpanS(okm, okmOffset, t, tLen);
+ ncSpanWrite(*okm, okmOffset, t, tLen);
/* shift base okm pointer by T */
okmOffset += tLen;
diff --git a/src/hkdf.h b/src/hkdf.h
index 460e203..2e3a55e 100644
--- a/src/hkdf.h
+++ b/src/hkdf.h
@@ -42,12 +42,12 @@
/* typedefs for hdkf callback functions */
-typedef cstatus_t (*hmac_hash_func)(void* ctx, const cspan_t* data);
+typedef cstatus_t (*hmac_hash_fn)(void* ctx, const cspan_t* data);
typedef cstatus_t (*hmac_finish_fn)(void* ctx, sha256_t hmacOut32);
struct nc_hkdf_fn_cb_struct
{
- hmac_hash_func update;
+ hmac_hash_fn update;
hmac_finish_fn finish;
};
diff --git a/src/nc-util.h b/src/nc-util.h
index dd319c7..0647f4c 100644
--- a/src/nc-util.h
+++ b/src/nc-util.h
@@ -68,6 +68,28 @@
#define _overflow_check(x)
#endif
+#ifdef NC_EXTREME_COMPAT
+
+ void _nc_memmove(void* dst, const void* src, uint32_t size)
+ {
+ uint32_t i;
+
+ for (i = 0; i < size; i++)
+ {
+ ((uint8_t*)dst)[i] = ((uint8_t*)src)[i];
+ }
+ }
+
+ #define MEMMOV _nc_memmove
+
+#else
+
+ /* Include string for memmove */
+ #include <string.h>
+ #define MEMMOV(dst, src, size) memmove(dst, src, size)
+
+#endif /* NC_EXTREME_COMPAT */
+
typedef struct memory_span_struct
{
uint8_t* data;
@@ -92,4 +114,28 @@ static _nc_fn_inline void ncSpanInit(span_t* span, uint8_t* data, uint32_t size)
span->size = size;
}
+static _nc_fn_inline void ncSpanWrite(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(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 */
+ MEMMOV(span.data + offset, data, size);
+}
+
+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(*offset + size <= span.size, "Expected offset + size to be less than span size")
+
+ /* Copy data to span */
+ MEMMOV(span.data + *offset, data, size);
+
+ /* Increment offset */
+ *offset += size;
+}
+
#endif /* !_NC_UTIL_H */ \ No newline at end of file
diff --git a/src/noscrypt.c b/src/noscrypt.c
index c523262..910f559 100644
--- a/src/noscrypt.c
+++ b/src/noscrypt.c
@@ -32,10 +32,6 @@
*/
#define ZERO_FILL(x, size) ncCryptoSecureZero(x, size)
-/* Include string for memmove */
-#include <string.h>
-#define MEMMOV(dst, src, size) memmove(dst, src, size)
-
/*
* Validation macros
*/
@@ -44,7 +40,6 @@
#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_CONTEXT_STATE(ctx, argPos) CHECK_INVALID_ARG(ctx->secpCtx, argPos)
#else
/* empty macros */
#define CHECK_INVALID_ARG(x)
@@ -52,6 +47,8 @@
#define CHECK_ARG_RANGE(x, min, max, argPos)
#endif /* !NC_DISABLE_INPUT_VALIDATION */
+#define CHECK_CONTEXT_STATE(ctx, argPos) CHECK_INVALID_ARG(ctx->secpCtx, argPos)
+
/*
* Actual, private defintion of the NCContext structure
* to allow for future development and ABI backords
@@ -449,7 +446,6 @@ NC_EXPORT NCResult NC_CC NCResultWithArgPosition(NCResult err, uint8_t argPositi
return -(((NCResult)argPosition << NC_ARG_POSITION_OFFSET) | -err);
}
-
NC_EXPORT int NC_CC NCParseErrorCode(NCResult result, uint8_t* argPositionOut)
{
NCResult asPositive;
@@ -460,7 +456,12 @@ NC_EXPORT int NC_CC NCParseErrorCode(NCResult result, uint8_t* argPositionOut)
/* Get the error code from the lower 8 bits and the argument position from the upper 8 bits*/
code = -(asPositive & NC_ERROR_CODE_MASK);
- *argPositionOut = (asPositive >> NC_ARG_POSITION_OFFSET) & 0xFF;
+
+ /* Allow argument position assignment to be null */
+ if (argPositionOut)
+ {
+ *argPositionOut = (asPositive >> NC_ARG_POSITION_OFFSET) & 0xFF;
+ }
return code;
}
diff --git a/src/noscryptutil.c b/src/noscryptutil.c
new file mode 100644
index 0000000..b7723cb
--- /dev/null
+++ b/src/noscryptutil.c
@@ -0,0 +1,418 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Package: noscrypt
+* File: noscryptutil.h
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public License
+* as published by the Free Software Foundation; either version 2.1
+* of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with noscrypt. If not, see http://www.gnu.org/licenses/.
+*/
+
+
+#include <noscryptutil.h>
+#include "nc-util.h"
+#include "nc-crypto.h"
+
+/*
+* Validation macros
+*/
+
+#ifdef NC_EXTREME_COMPAT
+ #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);
+
+#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);
+#else
+ /* empty macros */
+ #define CHECK_INVALID_ARG(x)
+ #define CHECK_NULL_ARG(x, argPos)
+ #define CHECK_ARG_RANGE(x, min, max, 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
+
+/* Currently were on nip44 version 2 */
+const static uint8_t Nip44VersionValue = 0x02;
+
+typedef struct nc_util_enc_buffer_state
+{
+ uint8_t* ciphertext;
+ uint32_t ciphertextSize;
+
+} NCCipherTextOutState;
+
+struct nc_util_enc_struct {
+
+ /* Dynamically allocated during initialization */
+ NCCipherTextOutState* outState;
+
+ const uint8_t* plaintext;
+
+ uint32_t plaintextSize;
+
+ NCEncryptionArgs encArgs;
+};
+
+static _nc_fn_inline int32_t _calcNip44PtPadding(int32_t plaintextSize)
+{
+ int32_t chunk, nextPower, factor;
+
+ /*
+ * Taken from https://github.com/nostr-protocol/nips/blob/master/44.md
+ *
+ * I believe the idea is to add consisten padding for some better
+ * disgusing of the plainText data.
+ */
+
+ if (plaintextSize <= MIN_PADDING_SIZE)
+ {
+ return MIN_PADDING_SIZE;
+ }
+
+ nextPower = _math_int_log2(plaintextSize - 1);
+
+ nextPower += 1;
+
+ nextPower = 1 << nextPower;
+
+ if (nextPower <= 256)
+ {
+ chunk = 32;
+ }
+ else
+ {
+ chunk = nextPower / 8;
+ }
+
+ factor = plaintextSize - 1;
+
+ factor /= chunk;
+
+ factor += 1;
+
+ return chunk * factor;
+}
+
+static _nc_fn_inline int32_t _calcNip44TotalOutSize(int32_t inputSize)
+{
+ int32_t bufferSize;
+
+ /*
+ * Buffer size for nip44 is calculated as follows:
+ * 1 byte for the version
+ * 32 bytes for the nonce
+ * 2 bytes for the length of the plainText
+ * ... padding size
+ * 32 bytes for the MAC
+ */
+
+ bufferSize = NIP44_VERSION_SIZE;
+
+ bufferSize += NC_ENCRYPTION_NONCE_SIZE;
+
+ bufferSize += NIP44_PT_LEN_SIZE;
+
+ bufferSize += _calcNip44PtPadding(inputSize);
+
+ bufferSize += NC_ENCRYPTION_MAC_SIZE;
+
+ return bufferSize;
+}
+
+static NCResult _nip44EncryptCompleteCore(
+ const NCContext* libContext,
+ const NCSecretKey* sk,
+ const NCPublicKey* pk,
+ NCEncryptionArgs encArgs,
+ span_t cipherText,
+ span_t plainText
+)
+{
+
+ NCResult result;
+ uint32_t outPos, paddedCtSize;
+ uint16_t ptSize;
+
+ 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);
+
+ /* Start by appending the version number */
+ ncSpanAppend(cipherText, &outPos, &Nip44VersionValue, 0x01);
+
+ /* next is nonce data */
+ ncSpanAppend(cipherText, &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
+ * 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,
+ * 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.
+ */
+
+ encArgs.inputData = (cipherText.data + outPos);
+ encArgs.outputData = (cipherText.data + outPos);
+ encArgs.dataSize = paddedCtSize + sizeof(uint16_t); /* Plaintext + pt size must be encrypted */
+
+ ptSize = (uint16_t)plainText.size;
+
+ /* Can write the plainText size to buffer now */
+ ncSpanAppend(cipherText, &outPos, &ptSize, sizeof(uint16_t));
+
+ /* concat plainText */
+ ncSpanAppend(cipherText, &outPos, plainText.data, plainText.size);
+
+ /* Time to perform encryption operation */
+ result = NCEncrypt(libContext, sk, pk, &encArgs);
+
+ if (result == NC_SUCCESS)
+ {
+
+ }
+}
+
+static NCResult _nip44EncryptCompleteCore(
+ NCUtilEncryptionContext* encCtx,
+ const NCContext* libContext,
+ const NCSecretKey* sk,
+ const NCPublicKey* pk
+)
+{
+ span_t cipherText, plainText;
+
+ /* Set up spans */
+ ncSpanInit(
+ &cipherText,
+ encCtx->outState->ciphertext,
+ encCtx->outState->ciphertextSize
+ );
+
+ ncSpanInit(
+ &plainText,
+ encCtx->plaintext,
+ encCtx->plaintextSize
+ );
+
+ return _nip44EncryptCompleteCore(
+ libContext,
+ sk,
+ pk,
+ encCtx->encArgs,
+ cipherText,
+ plainText
+ );
+}
+
+NC_EXPORT NCResult NC_CC NCUtilGetEncryptionPaddedSize(uint32_t encVersion, int32_t plaintextSize)
+{
+ int32_t paddingSize;
+
+ CHECK_ARG_RANGE(plaintextSize, 0, INT32_MAX, 1)
+
+ switch (encVersion)
+ {
+ default:
+ return E_VERSION_NOT_SUPPORTED;
+
+ case NC_ENC_VERSION_NIP04:
+ return plaintextSize;
+
+ case NC_ENC_VERSION_NIP44:
+ paddingSize = _calcNip44PtPadding(plaintextSize);
+
+ DEBUG_ASSERT(paddingSize > 0)
+
+ return (NCResult)(paddingSize);
+ }
+}
+
+NC_EXPORT NCResult NC_CC NCUtilGetEncryptionBufferSize(uint32_t encVersion, int32_t plaintextSize)
+{
+ int32_t totalSize;
+
+ CHECK_ARG_RANGE(plaintextSize, 0, INT32_MAX, 1)
+
+ switch (encVersion)
+ {
+ 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);
+
+ }
+}
+
+
+NC_EXPORT NCUtilEncryptionContext* NC_CC NCUtilAllocEncryptionContext(uint32_t encVersion)
+{
+ NCUtilEncryptionContext* encCtx;
+
+ /*
+ * Alloc context on heap
+ */
+ encCtx = (NCUtilEncryptionContext*)_nc_mem_alloc(1, sizeof(NCUtilEncryptionContext));
+
+ if (encCtx != NULL)
+ {
+ encCtx->encArgs.version = encVersion;
+ }
+
+ return encCtx;
+}
+
+NC_EXPORT void NC_CC NCUtilFreeEncryptionContext(NCUtilEncryptionContext* encCtx)
+{
+ if (!encCtx)
+ {
+ return;
+ }
+
+ /* Free output buffers */
+ _nc_mem_free(encCtx->outState);
+
+ /* context can be released */
+ _nc_mem_free(encCtx);
+}
+
+NC_EXPORT NCResult NC_CC NCUtilInitEncryptionContext(
+ NCUtilEncryptionContext* encCtx,
+ const uint8_t* plainText,
+ uint32_t plainTextSize
+)
+{
+
+ NCResult outputSize;
+ NCCipherTextOutState* output;
+
+ CHECK_NULL_ARG(encCtx, 0)
+ CHECK_NULL_ARG(plainText, 1)
+ CHECK_ARG_RANGE(plainTextSize, 0, INT32_MAX, 2)
+
+ /*
+ * The output state must not have alraedy been allocated
+ */
+ if (encCtx->outState)
+ {
+ return E_INVALID_ARG;
+ }
+
+ /*
+ * Calculate the correct output size to store the encryption
+ * data for the given cipher version
+ */
+ outputSize = NCUtilGetEncryptionBufferSize(encCtx->encArgs.version, plainTextSize);
+
+ if (outputSize <= 0)
+ {
+ return outputSize;
+ }
+
+ /*Alloc output buffer within the struct */
+ output = (NCCipherTextOutState*)_nc_mem_alloc(sizeof(NCCipherTextOutState) + (int)outputSize, 1);
+
+ if (!output)
+ {
+ 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;
+
+ return NC_SUCCESS;
+}
+
+NC_EXPORT NCResult NC_CC NCUtilGetEncryptedSize(const NCUtilEncryptionContext* encCtx)
+{
+ CHECK_NULL_ARG(encCtx, 0);
+
+ return (NCResult)(encCtx->outState->ciphertextSize);
+}
+
+NC_EXPORT NCResult NC_CC NCUtilReadEncryptedData(
+ const NCUtilEncryptionContext* 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)
+ {
+ return E_OPERATION_FAILED;
+ }
+
+ MEMMOV(output, encCtx->outState->ciphertext, encCtx->outState->ciphertextSize);
+
+ return (NCResult)encCtx->outState->ciphertextSize;
+}
+
+NC_EXPORT NCResult NCUtilSetEncryptionProperty(
+ NCUtilEncryptionContext* ctx,
+ uint32_t property,
+ uint8_t* value,
+ uint32_t valueLen
+)
+{
+
+ CHECK_NULL_ARG(ctx, 0)
+
+ /* All other arguments are verified */
+ return NCSetEncryptionPropertyEx(&ctx->encArgs, property, value, valueLen);
+}