aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-08-08 14:51:37 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-08-08 14:51:37 -0400
commitf8ec3640e4ba2249fcc0ba324ac5b071e5d6f40b (patch)
tree61d3b053ca81fa657754852550d03305b618d15b
parentb69876b1786f5e99448266a3b74ca4404d7118fd (diff)
add noscrypt cipher and update for new noscrypt utils api
-rw-r--r--CMakeLists.txt4
-rw-r--r--src/signer/noscrypt_cipher.cpp146
-rw-r--r--src/signer/noscrypt_cipher.hpp154
-rw-r--r--src/signer/noscrypt_signer.cpp108
4 files changed, 328 insertions, 84 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6b933d9..89894cf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,7 +57,7 @@ include_directories(${uuid_v4_SOURCE_DIR})
FetchContent_Declare(
libnoscrypt
GIT_REPOSITORY git@github.com:VnUgE/noscrypt.git
- GIT_TAG v0.1.2
+ GIT_TAG fb3608b9455b3e0956e401e6254da13cebd71558
)
FetchContent_MakeAvailable(libnoscrypt)
@@ -65,6 +65,7 @@ FetchContent_GetProperties(libnoscrypt)
include_directories(${libnoscrypt_SOURCE_DIR}/include)
set_target_properties(noscrypt PROPERTIES CRYPTO_LIB openssl)
+set_target_properties(noscrypt PROPERTIES NC_ENABLE_UTILS ON) #utilities library is required
#======== Build the project ========#
set(INCLUDE_DIR ./include)
@@ -98,6 +99,7 @@ set(SOURCES
${DATA_SOURCE_DIR}/filters.cpp
${SERVICE_SOURCE_DIR}/nostr_service_base.cpp
${SIGNER_SOURCE_DIR}/noscrypt_signer.cpp
+ ${SIGNER_SOURCE_DIR}/noscrypt_cipher.cpp
)
add_library(aedile ${SOURCES} ${HEADERS})
diff --git a/src/signer/noscrypt_cipher.cpp b/src/signer/noscrypt_cipher.cpp
new file mode 100644
index 0000000..7f499a8
--- /dev/null
+++ b/src/signer/noscrypt_cipher.cpp
@@ -0,0 +1,146 @@
+#include <plog/Init.h>
+#include <plog/Log.h>
+#include <noscryptutil.h>
+
+#include <openssl/evp.h>
+
+#include "noscrypt_cipher.hpp"
+
+using namespace nostr::signer;
+using namespace std;
+
+static void _printNoscryptError(NCResult result, const std::string funcName, int lineNum)
+{
+ uint8_t argPosition;
+
+ switch (NCParseErrorCode(result, &argPosition))
+ {
+ case E_NULL_PTR:
+ PLOG_ERROR << "noscrypt - error: A null pointer was passed in " << funcName << "(" << argPosition << ") at line " << lineNum;
+ break;
+
+ case E_INVALID_ARG:
+ PLOG_ERROR << "noscrypt - error: An invalid argument was passed in " << funcName << "(" << argPosition << ") at line " << lineNum;
+ break;
+
+ case E_INVALID_CONTEXT:
+ PLOG_ERROR << "noscrypt - error: An invalid context was passed in " << funcName << "(" << argPosition << ") on line " << lineNum;
+ break;
+
+ case E_ARGUMENT_OUT_OF_RANGE:
+ PLOG_ERROR << "noscrypt - error: An argument was out of range in " << funcName << "(" << argPosition << ") at line " << lineNum;
+ break;
+
+ case E_OPERATION_FAILED:
+ PLOG_ERROR << "noscrypt - error: An operation failed in " << funcName << "(" << argPosition << ") at line " << lineNum;
+ break;
+
+ default:
+ PLOG_ERROR << "noscrypt - error: An unknown error " << result << " occurred in " << funcName << "(" << argPosition << ") at line " << lineNum;
+ break;
+ }
+
+}
+
+#define LOG_NC_ERROR(result) _printNoscryptError(result, __func__, __LINE__)
+
+NoscryptCipher::NoscryptCipher(uint32_t version, uint32_t mode)
+{
+ this->_cipher = NoscryptCipherContext(version, mode);
+
+ /*
+ * We can know what iv size we need for the cipher now and allocate
+ * a buffer just to save some allocations and code during the
+ * encryption phase. This buffer is only needed during an encryption
+ * operation.
+ */
+
+ if ((mode & NC_UTIL_CIPHER_MODE) == NC_UTIL_CIPHER_MODE_ENCRYPT)
+ {
+ //Resize the vector to the size of the current cipher
+ this->_ivBuffer.resize(&this->_cipher.ivSize());
+
+ //Safe to assign the iv to the context now and it will maintain a pointer to the buffer
+ this->_cipher.setIV(&this->_ivBuffer);
+ }
+}
+
+std::string NoscryptCipher::update(
+ const std::shared_ptr<const NCContext> libContext,
+ const std::shared_ptr<const NCSecretKey> localKey,
+ const std::shared_ptr<const NCPublicKey> remoteKey,
+ const std::string& input
+)
+{
+ NCResult result;
+
+ //Argument exception if the input is empty
+ if (input.empty())
+ {
+ return string();
+ }
+
+ //Safely convert the string to a vector of bytes (allocates and copies, so maybe speed up later)
+ const vector<uint8_t> inputBuffer(input.begin(), input.end());
+
+ result = this->_cipher.setInput(&inputBuffer);
+ if (result != NC_SUCCESS)
+ {
+ LOG_NC_ERROR(result);
+ return string();
+ }
+
+ /*
+ * If were in encryption mode a random nonce (iv) must be generated. The size was determined
+ * when the cipher was created and already assigned to the context, so we just need to assign
+ * the random data.
+ *
+ * Keep in mind, this will automatically work for nip44 and nip04, either the
+ * AES iv or the ChaCha nonce.
+ */
+ if ((this->_cipher.flags() & NC_UTIL_CIPHER_MODE) == NC_UTIL_CIPHER_MODE_ENCRYPT)
+ {
+ int code = RAND_bytes(
+ this->_ivBuffer.data(),
+ this->_ivBuffer.size() //Size set in constructor
+ );
+
+ if (code <= 0)
+ {
+ PLOG_ERROR << "Failed to generate a nonce or IV for encryption.";
+ return string();
+ }
+ }
+
+ //Performs the operation (either encryption or decryption)
+ result = this->_cipher.update(libContext, localKey, remoteKey);
+ if (result != NC_SUCCESS)
+ {
+ LOG_NC_ERROR(result);
+ return string();
+ }
+
+ /*
+ * Time to read the ciper output by getting the size of the output, then creating
+ * a string to store it to
+ */
+
+ NCResult outputSize = this->_cipher.outputSize();
+ if (outputSize <= 0)
+ {
+ LOG_NC_ERROR(outputSize);
+ return string();
+ }
+
+ //Alloc vector for reading input data (maybe only alloc once)
+ const vector<uint8_t> output(outputSize);
+
+ result = this->_cipher.readOutput(&output);
+ if (result != outputSize)
+ {
+ LOG_NC_ERROR(result);
+ return string();
+ }
+
+ return string(output.begin(), output.end());
+}
diff --git a/src/signer/noscrypt_cipher.hpp b/src/signer/noscrypt_cipher.hpp
new file mode 100644
index 0000000..8fd5ad4
--- /dev/null
+++ b/src/signer/noscrypt_cipher.hpp
@@ -0,0 +1,154 @@
+
+#include <memory>
+
+#include <noscrypt.h>
+#include <noscryptutil.h>
+
+namespace nostr
+{
+ namespace signer
+ {
+ class NoscryptCipher
+ {
+ public:
+ NoscryptCipher(uint32_t version, uint32_t mode);
+
+ ~NoscryptCipher();
+
+ /*
+ * @brief Performs the cipher operation on the input data. Depending on the mode
+ * the cipher was initialized as, this will either encrypt or decrypt the data.
+ * @param libContext The noscrypt library context.
+ * @param localKey The local secret key used to encrypt/decrypt the data.
+ * @param remoteKey The remote public key used to encrypt/decrypt the data.
+ * @param input The data to encrypt/decrypt.
+ * @returns The opposite of the input data.
+ * @remark This cipher function follows the nostr nips format and will use do it's
+ * best to
+ */
+ std::string update(
+ const std::shared_ptr<const NCContext> libContext,
+ const std::shared_ptr<const NCSecretKey> localKey,
+ const std::shared_ptr<const NCPublicKey> remoteKey,
+ const std::string input
+ );
+
+ static std::string naiveEncodeBase64(const std::string& str)
+ {
+ //TODO Implement base64 encoding
+ return str;
+ }
+
+ static std::string naiveDecodeBase64(const std::string& str)
+ {
+ //TODO Implement base64 decoding
+ return str;
+ }
+
+ private:
+
+ NoscryptCipherContext _cipher;
+ std::vector<uint8_t> _ivBuffer;
+ };
+
+ class NoscryptCipherContext
+ {
+ public:
+
+ NoscryptCipherContext(uint32_t version, uint32_t mode)
+ {
+ /*
+ * Create a new cipher context with the specified
+ * version and mode that will live for the duration of the
+ * instance.
+ *
+ * The user is expected to use the noscryptutil mode for
+ * setting encryption/decryption modes.
+ *
+ * The cipher will zero out the memory when it is freed.
+ *
+ * For decryption, by default the mac is verified before
+ * decryption occurs.
+ *
+ * NOTE: The ciper is set to reusable mode, so encrypt/decrypt
+ * can be called multiple times although it's not recommended,
+ * its just the more predictable way for users to handle it.
+ */
+
+ _cipher = NCUtilCipherAlloc(
+ version,
+ mode | NC_UTIL_CIPHER_ZERO_ON_FREE | NC_UTIL_CIPHER_REUSEABLE
+ );
+ }
+
+ ~NoscryptCipherContext()
+ {
+ //Free the cipher context (will also zero any data/pointers)
+ NCUtilCipherFree(_cipher);
+ }
+
+ NCResult update(
+ const shared_ptr<const NCContext>& libContext,
+ const shared_ptr<const NCSecretKey>& localKey,
+ const shared_ptr<const NCPublicKey>& remoteKey
+ )
+ {
+ return NCUtilCipherUpdate(this->_cipher, libContext.get(), localKey.get(), remoteKey.get());
+ }
+
+ NCResult setIV(const std::vector<uint8_t>& iv)
+ {
+ return NCUtilCipherSetProperty(_cipher, NC_ENC_SET_IV, iv.data(), (uint32_t)iv.size());
+ }
+
+ size_t ivSize() const
+ {
+ NCResult size = NCUtilCipherGetIvSize(_cipher);
+
+ if (size <= 0)
+ {
+ //TODO Implement error handling
+ return 0;
+ }
+
+ return size;
+ }
+
+ NCResult outputSize() const
+ {
+ return NCUtilCipherGetOutputSize(_cipher);
+ }
+
+ uint32_t flags() const
+ {
+ NCResult result = NCUtilCipherGetFlags(_cipher);
+
+ if (result <= 0)
+ {
+ //TODO Implement error handling
+ return 0;
+ }
+
+ return (uint32_t)result;
+ }
+
+ NCResult readOutput(const std::vector<uint8_t>& output) const
+ {
+ return NCUtilCipherReadOutput(_cipher, output.data(), (uint32_t)output.size());
+ }
+
+ NCResult setInput(const std::vector<uint8_t>& input) const
+ {
+ /*
+ * Assign and validate input string. Init can be only called multiple times
+ * without side effects when the reusable flag is set. (currently set)
+ */
+
+ return NCUtilCipherInit(_cipher, input.data(), input.size());
+ }
+
+ private:
+ NCUtilCipherContext* _cipher;
+ };
+ }
+}
diff --git a/src/signer/noscrypt_signer.cpp b/src/signer/noscrypt_signer.cpp
index 68cdf6a..3cf4b6f 100644
--- a/src/signer/noscrypt_signer.cpp
+++ b/src/signer/noscrypt_signer.cpp
@@ -12,6 +12,7 @@
#include <uuid_v4.h>
#include "signer/noscrypt_signer.hpp"
+#include "noscrypt_cipher.hpp"
using namespace nostr::data;
using namespace nostr::service;
@@ -532,92 +533,32 @@ string NoscryptSigner::_decryptNip04(string input)
string NoscryptSigner::_encryptNip44(string input)
{
- uint32_t nip44Version = 0x02;
-
- auto nonce = make_shared<uint8_t>(32);
- auto hmacKey = make_shared<uint8_t>(32);
-
- uint32_t bufferSize = input.length();
- auto output = make_shared<uint8_t>(bufferSize);
-
- // Generate a nonce to use for the encryption.
- int code = RAND_bytes(nonce.get(), 32);
- if (code <= 0)
- {
- PLOG_ERROR << "Failed to generate a nonce for NIP-44 encryption.";
- return string();
- }
-
- // Setup the encryption context.
- auto encryptionArgs = make_unique<NCEncryptionArgs>(NCEncryptionArgs
- {
- nonce.get(),
- hmacKey.get(),
- reinterpret_cast<uint8_t*>(input.data()),
- output.get(),
- bufferSize,
- nip44Version
- });
-
- // Perform the encryption.
- NCResult encryptionResult = NCEncrypt(
- this->_noscryptContext.get(),
- this->_localPrivateKey.get(),
- this->_remotePublicKey.get(),
- encryptionArgs.get());
-
- // TODO: Handle various codes.
- if (encryptionResult != NC_SUCCESS)
- {
- return string();
- }
-
- return string((char*)output.get(), bufferSize);
+ NoscryptCipher cipher = NoscryptCipher(NC_ENC_VERSION_NIP44, NC_UTIL_CIPHER_MODE_ENCRYPT);
+
+ auto output = cipher.update(
+ this->_noscryptContext,
+ this->_localPrivateKey,
+ this->_remotePublicKey,
+ input
+ );
+
+ return output.empty()
+ ? string()
+ : NoscryptCipher::naiveEncodeBase64(output);
};
string NoscryptSigner::_decryptNip44(string input)
{
- uint32_t nip44Version = 0x02;
+ //TODO handle input validation as per nip44 spec
- auto nonce = make_shared<uint8_t>(32);
- auto hmacKey = make_shared<uint8_t>(32);
+ NoscryptCipher cipher = NoscryptCipher(NC_ENC_VERSION_NIP44, NC_UTIL_CIPHER_MODE_DECRYPT);
- uint32_t bufferSize = input.length();
- auto output = make_shared<uint8_t>(bufferSize);
-
- // Generate a nonce to use for the decryption.
- int code = RAND_bytes(nonce.get(), 32);
- if (code <= 0)
- {
- PLOG_ERROR << "Failed to generate a nonce for NIP-44 decryption.";
- return string();
- }
-
- // Set up the decryption context.
- auto decryptionArgs = make_unique<NCEncryptionArgs>(NCEncryptionArgs
- {
- nonce.get(),
- hmacKey.get(),
- reinterpret_cast<uint8_t*>(input.data()),
- output.get(),
- bufferSize,
- nip44Version
- });
-
- // Perform the decryption.
- NCResult decryptionResult = NCDecrypt(
- this->_noscryptContext.get(),
- this->_localPrivateKey.get(),
- this->_remotePublicKey.get(),
- decryptionArgs.get());
-
- // TODO: Handle various codes.
- if (decryptionResult != NC_SUCCESS)
- {
- return string();
- }
-
- return string((char*)output.get(), bufferSize);
+ return cipher.update(
+ this->_noscryptContext,
+ this->_localPrivateKey,
+ this->_remotePublicKey,
+ NoscryptCipher::naiveDecodeBase64(input)
+ );
};
#pragma endregion
@@ -626,7 +567,7 @@ string NoscryptSigner::_decryptNip44(string input)
inline void NoscryptSigner::_logNoscryptInitResult(NCResult initResult) const
{
- switch (initResult) {
+ switch (NCParseErrorCode(initResult, NULL)) {
case NC_SUCCESS:
PLOG_INFO << "noscrypt - success";
break;
@@ -655,7 +596,7 @@ inline void NoscryptSigner::_logNoscryptInitResult(NCResult initResult) const
inline void NoscryptSigner::_logNoscryptSecretValidationResult(NCResult secretValidationResult) const
{
- if (secretValidationResult == NC_SUCCESS)
+ if (NCParseErrorCode(secretValidationResult, NULL) == NC_SUCCESS)
{
PLOG_INFO << "noscrypt_signer - success: Generated a valid secret key.";
}
@@ -667,7 +608,8 @@ inline void NoscryptSigner::_logNoscryptSecretValidationResult(NCResult secretVa
inline void NoscryptSigner::_logNoscryptPubkeyGenerationResult(NCResult pubkeyGenerationResult) const
{
- switch (pubkeyGenerationResult) {
+
+ switch (NCParseErrorCode(pubkeyGenerationResult, NULL)) {
case NC_SUCCESS:
PLOG_INFO << "noscrypt - success: Generated a valid public key.";
break;