// exports.Signature
#include "Signature.h"
#include <cstdint>
#include <memory>
#include <new>
#include <vector>
#include <napi.h>
// liboqs-cpp
#include "oqs_cpp.h"
#include "common.h"
namespace Signature {
using oqs::byte;
using oqs::bytes;
/**
* Constructs an instance of Signature.
* @name Signature
* @class
* @constructs Signature
* @param {Sigs.Algorithm} algorithm - The signature algorithm to use.
* @param {Buffer} [secretKey] - An optional secret key. If not specified, use Signature#generateKeypair later to create a secret key.
* @throws {TypeError} Will throw an error if any argument is invalid.
*/
Signature::Signature(const Napi::CallbackInfo& info) : Napi::ObjectWrap<Signature>(info) {
Napi::Env env = info.Env();
if (info.Length() < 1) {
throw Napi::TypeError::New(env, "Algorithm must be a string");
}
if (!info[0].IsString()) {
throw Napi::TypeError::New(env, "Algorithm must be a string");
}
const auto algorithm = info[0].As<Napi::String>().Utf8Value();
if (info.Length() >= 2) {
if (!info[1].IsBuffer()) {
throw Napi::TypeError::New(env, "Secret key must be a buffer");
}
const auto secretKeyBuffer = info[1].As<Napi::Buffer<byte>>();
const auto secretKeyData = secretKeyBuffer.Data();
const bytes secretKeyVec(secretKeyData, secretKeyData + secretKeyBuffer.Length());
try {
oqsSig = std::make_unique<oqs::Signature>(algorithm, secretKeyVec);
} catch (const std::exception& ex) {
throw Napi::TypeError::New(env, ex.what());
}
} else {
try {
oqsSig = std::make_unique<oqs::Signature>(algorithm);
} catch (const std::exception& ex) {
throw Napi::TypeError::New(env, ex.what());
}
}
}
/**
* Gets the details for the signature algorithm that the instance was constructed with.
* @memberof Signature
* @instance
* @method
* @name getDetails
* @returns {Object} - An object containing the details of the signature algorithm.
*/
Napi::Value Signature::getDetails(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
const oqs::Signature::SignatureDetails details = oqsSig->get_details();
auto detailsObj = Napi::Object::New(env);
detailsObj.Set(
Napi::String::New(env, "name"),
Napi::String::New(env, details.name)
);
detailsObj.Set(
Napi::String::New(env, "version"),
Napi::String::New(env, details.version)
);
detailsObj.Set(
Napi::String::New(env, "claimedNistLevel"),
Napi::Number::New(env, details.claimed_nist_level)
);
detailsObj.Set(
Napi::String::New(env, "isEUFCMA"),
Napi::Boolean::New(env, details.is_euf_cma)
);
detailsObj.Set(
Napi::String::New(env, "publicKeyLength"),
Napi::Number::New(env, details.length_public_key)
);
detailsObj.Set(
Napi::String::New(env, "secretKeyLength"),
Napi::Number::New(env, details.length_secret_key)
);
detailsObj.Set(
Napi::String::New(env, "maxSignatureLength"),
Napi::Number::New(env, details.max_length_signature)
);
return detailsObj;
}
/**
* Generates a keypair. Overwrites any existing secret key on the instance with the generated secret key.
* @memberof Signature
* @instance
* @method
* @name generateKeypair
* @returns {Buffer} - A Buffer containing the public key.
* @throws {Error} Will throw an error if memory cannot be allocated.
*/
Napi::Value Signature::generateKeypair(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
try {
const bytes publicKeyVec = oqsSig->generate_keypair();
bytes* publicKeyVecCopy = new bytes(publicKeyVec);
if (publicKeyVecCopy == nullptr) {
throw Napi::Error::New(env, "Failed to allocate memory");
}
Napi::MemoryManagement::AdjustExternalMemory(env, publicKeyVecCopy->size());
return Napi::Buffer<byte>::New(
env,
publicKeyVecCopy->data(),
publicKeyVecCopy->size(),
[](Napi::Env cbEnv, byte* /* unused */, bytes* vec) -> void {
if (vec != nullptr) {
Napi::MemoryManagement::AdjustExternalMemory(cbEnv, -vec->size());
// No need to secure free public key
}
delete vec;
},
publicKeyVecCopy
);
} catch (const std::exception& ex) {
throw Napi::Error::New(env, ex.what());
}
}
/**
* Exports the secret key.
* @memberof Signature
* @instance
* @method
* @name exportSecretKey
* @returns {Buffer} - A Buffer containing the secret key.
* @throws {Error} Will throw an error if memory cannot be allocated.
*/
Napi::Value Signature::exportSecretKey(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
bytes secretKeyVec = oqsSig->export_secret_key();
bytes* secretKeyVecCopy = new (std::nothrow) bytes(secretKeyVec);
oqs::mem_cleanse(secretKeyVec);
if (secretKeyVecCopy == nullptr) {
throw Napi::Error::New(env, "Failed to allocate memory");
}
Napi::MemoryManagement::AdjustExternalMemory(env, secretKeyVecCopy->size());
return Napi::Buffer<byte>::New(
env,
secretKeyVecCopy->data(),
secretKeyVecCopy->size(),
[](Napi::Env cbEnv, byte* /* unused */, bytes* vec) -> void {
if (vec != nullptr) {
Napi::MemoryManagement::AdjustExternalMemory(cbEnv, -vec->size());
oqs::mem_cleanse(*vec);
}
delete vec;
},
secretKeyVecCopy
);
}
/**
* Signs a message.
* @memberof Signature
* @instance
* @method
* @name sign
* @param {Buffer} message - The message to sign.
* @returns {Buffer} - The signature for the message.
* @throws {TypeError} Will throw an error if any argument is invalid.
* @throws {Error} Will throw an error if memory cannot be allocated.
*/
Napi::Value Signature::sign(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 1) {
throw Napi::TypeError::New(env, "Message must be a buffer");
}
if (!info[0].IsBuffer()) {
throw Napi::TypeError::New(env, "Message must be a buffer");
}
const auto messageBuffer = info[0].As<Napi::Buffer<byte>>();
const auto messageData = messageBuffer.Data();
const bytes messageVec(messageData, messageData + messageBuffer.Length());
try {
bytes signatureVec = oqsSig->sign(messageVec);
bytes* signatureVecCopy = new (std::nothrow) bytes(signatureVec);
if (signatureVecCopy == nullptr) {
throw Napi::Error::New(env, "Failed to allocate memory");
}
Napi::MemoryManagement::AdjustExternalMemory(env, signatureVecCopy->size());
return Napi::Buffer<byte>::New(
env,
signatureVecCopy->data(),
signatureVecCopy->size(),
[](Napi::Env cbEnv, byte* /* unused */, bytes* vec) -> void {
if (vec != nullptr) {
Napi::MemoryManagement::AdjustExternalMemory(cbEnv, -vec->size());
// No need to secure free signature
}
delete vec;
},
signatureVecCopy
);
} catch (const std::exception& ex) {
throw Napi::TypeError::New(env, ex.what());
}
}
/**
* Verifies the signature belonging to a message using a public key.
* @memberof Signature
* @instance
* @method
* @name verify
* @param {Buffer} message - The message that was signed to produce the signature.
* @param {Buffer} signature - The signature to verify.
* @param {Buffer} publicKey - The public key to verify the signature against.
* @returns {boolean} - Whether the message has a valid signature from the owner of the public key.
* @throws {TypeError} Will throw an error if any argument is invalid.
*/
Napi::Value Signature::verify(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 3) {
throw Napi::TypeError::New(env, "Message, signature, and publicKey must be buffers");
}
if (!info[0].IsBuffer() || !info[1].IsBuffer() || !info[2].IsBuffer()) {
throw Napi::TypeError::New(env, "Message, signature, and publicKey must be buffers");
}
const auto messageBuffer = info[0].As<Napi::Buffer<byte>>();
const auto messageData = messageBuffer.Data();
const bytes messageVec(messageData, messageData + messageBuffer.Length());
const auto signatureBuffer = info[1].As<Napi::Buffer<byte>>();
const auto signatureData = signatureBuffer.Data();
const bytes signatureVec(signatureData, signatureData + signatureBuffer.Length());
const auto publicKeyBuffer = info[2].As<Napi::Buffer<byte>>();
const auto publicKeyData = publicKeyBuffer.Data();
const bytes publicKeyVec(publicKeyData, publicKeyData + publicKeyBuffer.Length());
try {
bool valid = oqsSig->verify(messageVec, signatureVec, publicKeyVec);
return Napi::Boolean::New(env, valid);
} catch (const std::exception& ex) {
throw Napi::TypeError::New(env, ex.what());
}
}
void Signature::Init(Napi::Env env, Napi::Object exports) {
Napi::Function func = DefineClass(env, "Signature", {
InstanceMethod<&Signature::getDetails>("getDetails"),
InstanceMethod<&Signature::generateKeypair>("generateKeypair"),
InstanceMethod<&Signature::exportSecretKey>("exportSecretKey"),
InstanceMethod<&Signature::sign>("sign"),
InstanceMethod<&Signature::verify>("verify")
});
Napi::FunctionReference* constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);
exports.Set(
Napi::String::New(env, "Signature"),
func
);
env.SetInstanceData<Napi::FunctionReference>(constructor);
}
void Init(Napi::Env env, Napi::Object exports) {
Signature::Init(env, exports);
}
} // namespace Signature