#include <napi.h>
#include <libbitcoinpqc/bitcoinpqc.h>
Napi::Value GetPublicKeySize(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsNumber()) {
Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
return env.Null();
}
int algorithm = info[0].As<Napi::Number>().Int32Value();
size_t size = bitcoin_pqc_public_key_size(static_cast<bitcoin_pqc_algorithm_t>(algorithm));
return Napi::Number::New(env, static_cast<double>(size));
}
Napi::Value GetSecretKeySize(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsNumber()) {
Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
return env.Null();
}
int algorithm = info[0].As<Napi::Number>().Int32Value();
size_t size = bitcoin_pqc_secret_key_size(static_cast<bitcoin_pqc_algorithm_t>(algorithm));
return Napi::Number::New(env, static_cast<double>(size));
}
Napi::Value GetSignatureSize(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 1 || !info[0].IsNumber()) {
Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
return env.Null();
}
int algorithm = info[0].As<Napi::Number>().Int32Value();
size_t size = bitcoin_pqc_signature_size(static_cast<bitcoin_pqc_algorithm_t>(algorithm));
return Napi::Number::New(env, static_cast<double>(size));
}
Napi::Value GenerateKeypair(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsTypedArray()) {
Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
return env.Null();
}
int algorithm = info[0].As<Napi::Number>().Int32Value();
Napi::Uint8Array randomData = info[1].As<Napi::Uint8Array>();
size_t min_entropy = 128;
if (algorithm == BITCOIN_PQC_SECP256K1_SCHNORR) {
min_entropy = 32;
}
if (randomData.ByteLength() < min_entropy) {
Napi::Error::New(env, "Random data is too short for the selected algorithm")
.ThrowAsJavaScriptException();
return env.Null();
}
bitcoin_pqc_keypair_t keypair;
bitcoin_pqc_error_t result = bitcoin_pqc_keygen(
static_cast<bitcoin_pqc_algorithm_t>(algorithm),
&keypair,
randomData.Data(),
randomData.ByteLength()
);
Napi::Object returnValue = Napi::Object::New(env);
if (result != BITCOIN_PQC_OK) {
returnValue.Set("resultCode", Napi::Number::New(env, static_cast<double>(result)));
returnValue.Set("publicKey", Napi::Uint8Array::New(env, 0));
returnValue.Set("secretKey", Napi::Uint8Array::New(env, 0));
return returnValue;
}
Napi::Uint8Array publicKey = Napi::Uint8Array::New(env, keypair.public_key_size);
memcpy(publicKey.Data(), keypair.public_key, keypair.public_key_size);
returnValue.Set("publicKey", publicKey);
Napi::Uint8Array secretKey = Napi::Uint8Array::New(env, keypair.secret_key_size);
memcpy(secretKey.Data(), keypair.secret_key, keypair.secret_key_size);
returnValue.Set("secretKey", secretKey);
returnValue.Set("resultCode", Napi::Number::New(env, 0));
bitcoin_pqc_keypair_free(&keypair);
return returnValue;
}
Napi::Value SignMessage(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 3 || !info[0].IsNumber() || !info[1].IsTypedArray() || !info[2].IsTypedArray()) {
Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
return env.Null();
}
int algorithm = info[0].As<Napi::Number>().Int32Value();
Napi::Uint8Array secretKey = info[1].As<Napi::Uint8Array>();
Napi::Uint8Array message = info[2].As<Napi::Uint8Array>();
bitcoin_pqc_signature_t signature;
bitcoin_pqc_error_t result = bitcoin_pqc_sign(
static_cast<bitcoin_pqc_algorithm_t>(algorithm),
secretKey.Data(),
secretKey.ByteLength(),
message.Data(),
message.ByteLength(),
&signature
);
Napi::Object returnValue = Napi::Object::New(env);
if (result != BITCOIN_PQC_OK) {
returnValue.Set("resultCode", Napi::Number::New(env, static_cast<double>(result)));
returnValue.Set("signature", Napi::Uint8Array::New(env, 0));
return returnValue;
}
Napi::Uint8Array signatureData = Napi::Uint8Array::New(env, signature.signature_size);
memcpy(signatureData.Data(), signature.signature, signature.signature_size);
returnValue.Set("signature", signatureData);
returnValue.Set("resultCode", Napi::Number::New(env, 0));
bitcoin_pqc_signature_free(&signature);
return returnValue;
}
Napi::Value VerifySignature(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
if (info.Length() < 4 || !info[0].IsNumber() || !info[1].IsTypedArray() ||
!info[2].IsTypedArray() || !info[3].IsTypedArray()) {
Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException();
return env.Null();
}
int algorithm = info[0].As<Napi::Number>().Int32Value();
Napi::Uint8Array publicKey = info[1].As<Napi::Uint8Array>();
Napi::Uint8Array message = info[2].As<Napi::Uint8Array>();
Napi::Uint8Array signature = info[3].As<Napi::Uint8Array>();
bitcoin_pqc_error_t result = bitcoin_pqc_verify(
static_cast<bitcoin_pqc_algorithm_t>(algorithm),
publicKey.Data(),
publicKey.ByteLength(),
message.Data(),
message.ByteLength(),
signature.Data(),
signature.ByteLength()
);
return Napi::Number::New(env, static_cast<double>(result));
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("bitcoin_pqc_public_key_size", Napi::Function::New(env, GetPublicKeySize));
exports.Set("bitcoin_pqc_secret_key_size", Napi::Function::New(env, GetSecretKeySize));
exports.Set("bitcoin_pqc_signature_size", Napi::Function::New(env, GetSignatureSize));
exports.Set("bitcoin_pqc_keygen", Napi::Function::New(env, GenerateKeypair));
exports.Set("bitcoin_pqc_sign", Napi::Function::New(env, SignMessage));
exports.Set("bitcoin_pqc_verify", Napi::Function::New(env, VerifySignature));
return exports;
}
NODE_API_MODULE(bitcoinpqc, Init)