diff --git a/nodejs/.gitignore b/nodejs/.gitignore index ada280a..73db82f 100644 --- a/nodejs/.gitignore +++ b/nodejs/.gitignore @@ -21,3 +21,5 @@ coverage/ .vscode/ *.swp *.swo + +c_sources diff --git a/nodejs/README.md b/nodejs/README.md index ab5d2ea..9cec3fc 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -7,10 +7,9 @@ TypeScript bindings for the [libbitcoinpqc](https://github.com/bitcoin/libbitcoi - Full TypeScript support with typings - Clean, ergonomic API - Compatible with NodeJS 16+ -- Works with all three PQC algorithms: +- Works with both PQC algorithms: - ML-DSA-44 (formerly CRYSTALS-Dilithium) - SLH-DSA-Shake-128s (formerly SPHINCS+) - - FN-DSA-512 (formerly FALCON) ## Installation @@ -58,12 +57,10 @@ verify(keypair.publicKey, message, signature.bytes); enum Algorithm { /** BIP-340 Schnorr + X-Only - Elliptic Curve Digital Signature Algorithm */ SECP256K1_SCHNORR = 0, - /** FN-DSA-512 (FALCON) - Fast Fourier lattice-based signature scheme */ - FN_DSA_512 = 1, /** ML-DSA-44 (CRYSTALS-Dilithium) - Lattice-based signature scheme */ - ML_DSA_44 = 2, + ML_DSA_44 = 1, /** SLH-DSA-Shake-128s (SPHINCS+) - Hash-based signature scheme */ - SLH_DSA_SHAKE_128S = 3 + SLH_DSA_SHAKE_128S = 2 } ``` @@ -150,11 +147,10 @@ Verify a signature using the specified public key. Throws a `PqcError` if verifi ## Algorithm Characteristics -| Algorithm | Public Key Size | Secret Key Size | Signature Size | Security Level | -|-----------|----------------|----------------|----------------|----------------| -| ML-DSA-44 | 1,312 bytes | 2,528 bytes | 2,420 bytes | NIST Level 2 | -| SLH-DSA-Shake-128s | 32 bytes | 64 bytes | 7,856 bytes | NIST Level 1 | -| FN-DSA-512 | 897 bytes | 1,281 bytes | ~666 bytes (average) | NIST Level 1 | +| Algorithm | Public Key Size | Secret Key Size | Signature Size | Security Level | +| ------------------ | --------------- | --------------- | -------------- | -------------- | +| ML-DSA-44 | 1,312 bytes | 2,528 bytes | 2,420 bytes | NIST Level 2 | +| SLH-DSA-Shake-128s | 32 bytes | 64 bytes | 7,856 bytes | NIST Level 1 | ## Security Notes diff --git a/nodejs/binding.gyp b/nodejs/binding.gyp index 0a9a462..f10e710 100644 --- a/nodejs/binding.gyp +++ b/nodejs/binding.gyp @@ -4,57 +4,42 @@ "target_name": "bitcoinpqc", "sources": [ "src/native/bitcoinpqc_addon.cc", - "../src/bitcoinpqc.c", - "../src/ml_dsa/keygen.c", - "../src/ml_dsa/sign.c", - "../src/ml_dsa/verify.c", - "../src/ml_dsa/utils.c", - "../src/slh_dsa/keygen.c", - "../src/slh_dsa/sign.c", - "../src/slh_dsa/verify.c", - "../src/slh_dsa/utils.c", - "../src/fn_dsa/keygen.c", - "../src/fn_dsa/sign.c", - "../src/fn_dsa/verify.c", - "../src/fn_dsa/utils.c", - "../dilithium/ref/sign.c", - "../dilithium/ref/packing.c", - "../dilithium/ref/polyvec.c", - "../dilithium/ref/poly.c", - "../dilithium/ref/ntt.c", - "../dilithium/ref/reduce.c", - "../dilithium/ref/rounding.c", - "../dilithium/ref/fips202.c", - "../dilithium/ref/symmetric-shake.c", - "../dilithium/ref/randombytes_custom.c", - "../sphincsplus/ref/address.c", - "../sphincsplus/ref/fors.c", - "../sphincsplus/ref/hash_shake.c", - "../sphincsplus/ref/merkle.c", - "../sphincsplus/ref/sign.c", - "../sphincsplus/ref/thash_shake_simple.c", - "../sphincsplus/ref/utils.c", - "../sphincsplus/ref/utilsx1.c", - "../sphincsplus/ref/wots.c", - "../sphincsplus/ref/wotsx1.c", - "../sphincsplus/ref/fips202.c", - "../falcon/codec.c", - "../falcon/common.c", - "../falcon/falcon.c", - "../falcon/fft.c", - "../falcon/fpr.c", - "../falcon/keygen.c", - "../falcon/shake.c", - "../falcon/sign.c", - "../falcon/vrfy.c", - "../falcon/rng.c" + "src/c_sources/bitcoinpqc.c", + "src/c_sources/ml_dsa/keygen.c", + "src/c_sources/ml_dsa/sign.c", + "src/c_sources/ml_dsa/verify.c", + "src/c_sources/ml_dsa/utils.c", + "src/c_sources/slh_dsa/keygen.c", + "src/c_sources/slh_dsa/sign.c", + "src/c_sources/slh_dsa/verify.c", + "src/c_sources/slh_dsa/utils.c", + "src/c_sources/dilithium_ref/sign.c", + "src/c_sources/dilithium_ref/packing.c", + "src/c_sources/dilithium_ref/polyvec.c", + "src/c_sources/dilithium_ref/poly.c", + "src/c_sources/dilithium_ref/ntt.c", + "src/c_sources/dilithium_ref/reduce.c", + "src/c_sources/dilithium_ref/rounding.c", + "src/c_sources/dilithium_ref/fips202.c", + "src/c_sources/dilithium_ref/symmetric-shake.c", + "src/c_sources/dilithium_ref/randombytes_custom.c", + "src/c_sources/sphincsplus_ref/address.c", + "src/c_sources/sphincsplus_ref/fors.c", + "src/c_sources/sphincsplus_ref/hash_shake.c", + "src/c_sources/sphincsplus_ref/merkle.c", + "src/c_sources/sphincsplus_ref/sign.c", + "src/c_sources/sphincsplus_ref/thash_shake_simple.c", + "src/c_sources/sphincsplus_ref/utils.c", + "src/c_sources/sphincsplus_ref/utilsx1.c", + "src/c_sources/sphincsplus_ref/wots.c", + "src/c_sources/sphincsplus_ref/wotsx1.c", + "src/c_sources/sphincsplus_ref/fips202.c" ], "include_dirs": [ "", "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/bitcoin/libbitcoinpqc.git", + "directory": "nodejs" + }, + "bugs": { + "url": "https://github.com/bitcoin/libbitcoinpqc/issues" + }, + "homepage": "https://github.com/bitcoin/libbitcoinpqc#readme", "type": "commonjs", "devDependencies": { "@types/jest": "^29.5.12", @@ -40,7 +49,12 @@ "dist", "README.md", "binding.gyp", - "src/native" + "tsconfig.json", + "src/native", + "src/c_sources" ], - "gypfile": true + "gypfile": true, + "publishConfig": { + "access": "public" + } } diff --git a/nodejs/scripts/sync-c-sources.sh b/nodejs/scripts/sync-c-sources.sh new file mode 100755 index 0000000..c1b673d --- /dev/null +++ b/nodejs/scripts/sync-c-sources.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Script to sync C source files from parent directory to src/c_sources +# This should be run before building or publishing the package + +set -e + +echo "Syncing C source files..." + +# Remove existing c_sources directory +rm -rf src/c_sources + +# Create new c_sources directory +mkdir -p src/c_sources + +# Copy C source files +echo "Copying main C source file..." +cp ../src/bitcoinpqc.c src/c_sources/ + +echo "Copying ML-DSA source files..." +cp -r ../src/ml_dsa src/c_sources/ + +echo "Copying SLH-DSA source files..." +cp -r ../src/slh_dsa src/c_sources/ + +echo "Copying Dilithium reference implementation..." +cp -r ../dilithium/ref src/c_sources/dilithium_ref + +echo "Copying SPHINCS+ reference implementation..." +cp -r ../sphincsplus/ref src/c_sources/sphincsplus_ref + +echo "Copying include files..." +cp -r ../include src/c_sources/ + +# Update include paths in C source files +echo "Updating include paths..." +find src/c_sources -name "*.c" -exec sed -i 's|../../dilithium/ref/|../dilithium_ref/|g' {} \; +find src/c_sources -name "*.c" -exec sed -i 's|../../sphincsplus/ref/|../sphincsplus_ref/|g' {} \; +find src/c_sources -name "*.c" -exec sed -i 's|../../include/|../include/|g' {} \; + +echo "C source files synced successfully!" \ No newline at end of file diff --git a/nodejs/src/__tests__/bitcoinpqc.test.ts b/nodejs/src/__tests__/bitcoinpqc.test.ts index ed72091..54f7c7c 100644 --- a/nodejs/src/__tests__/bitcoinpqc.test.ts +++ b/nodejs/src/__tests__/bitcoinpqc.test.ts @@ -27,7 +27,6 @@ describe("Bitcoin PQC", () => { // Test key size reporting functions for (const algo of [ Algorithm.SECP256K1_SCHNORR, - Algorithm.FN_DSA_512, Algorithm.ML_DSA_44, Algorithm.SLH_DSA_SHAKE_128S, ]) { @@ -106,47 +105,45 @@ describe("Bitcoin PQC", () => { }); }); - describe("FN-DSA-512 (FALCON)", () => { - const algorithm = Algorithm.FN_DSA_512; - test("should generate keypair, sign and verify", () => { - // Generate random data for key generation - const randomData = getRandomBytes(128); + test("should generate keypair, sign and verify", () => { + // Generate random data for key generation + const randomData = getRandomBytes(128); - // Generate a keypair - const keypair = generateKeyPair(algorithm, randomData); + // Generate a keypair + const keypair = generateKeyPair(algorithm, randomData); - // Verify key sizes match reported sizes - expect(keypair.publicKey.bytes.length).toBe(publicKeySize(algorithm)); - expect(keypair.secretKey.bytes.length).toBe(secretKeySize(algorithm)); + // Verify key sizes match reported sizes + expect(keypair.publicKey.bytes.length).toBe(publicKeySize(algorithm)); + expect(keypair.secretKey.bytes.length).toBe(secretKeySize(algorithm)); - // Test message signing - const message = new TextEncoder().encode("Hello, Bitcoin PQC!"); - const signature = sign(keypair.secretKey, message); + // Test message signing + const message = new TextEncoder().encode("Hello, Bitcoin PQC!"); + const signature = sign(keypair.secretKey, message); - // Verify signature size matches reported size - expect(signature.bytes.length).toBe(signatureSize(algorithm)); + // Verify signature size matches reported size + expect(signature.bytes.length).toBe(signatureSize(algorithm)); - // Verify the signature - should not throw - expect(() => { - verify(keypair.publicKey, message, signature); - }).not.toThrow(); - }); + // Verify the signature - should not throw + expect(() => { + verify(keypair.publicKey, message, signature); + }).not.toThrow(); }); +}); - describe("error conditions", () => { - test("should throw on invalid input", () => { - // Invalid algorithm - expect(() => { - const randomData = getRandomBytes(128); - generateKeyPair(99 as Algorithm, randomData); - }).toThrow(PqcError); - - // Invalid random data size - expect(() => { - const randomData = getRandomBytes(16); // Less than 128 bytes - generateKeyPair(Algorithm.ML_DSA_44, randomData); - }).toThrow(PqcError); - }); +describe("error conditions", () => { + test("should throw on invalid input", () => { + // Invalid algorithm + expect(() => { + const randomData = getRandomBytes(128); + generateKeyPair(99 as Algorithm, randomData); + }).toThrow(PqcError); + + // Invalid random data size + expect(() => { + const randomData = getRandomBytes(16); // Less than 128 bytes + generateKeyPair(Algorithm.ML_DSA_44, randomData); + }).toThrow(PqcError); }); }); +}); diff --git a/nodejs/src/library.ts b/nodejs/src/library.ts index eaffabe..4b75517 100644 --- a/nodejs/src/library.ts +++ b/nodejs/src/library.ts @@ -53,7 +53,6 @@ class MockBitcoinPqcNative implements BitcoinPqcNative { secretKey: 32, signature: 64, }, - [Algorithm.FN_DSA_512]: { publicKey: 897, secretKey: 1281, signature: 666 }, // Average size [Algorithm.ML_DSA_44]: { publicKey: 1312, secretKey: 2528, diff --git a/nodejs/src/native/mock_bitcoinpqc_addon.cc b/nodejs/src/native/mock_bitcoinpqc_addon.cc index 299ca44..9332cc8 100644 --- a/nodejs/src/native/mock_bitcoinpqc_addon.cc +++ b/nodejs/src/native/mock_bitcoinpqc_addon.cc @@ -1,15 +1,11 @@ -#include #include +#include // Mock key sizes const int SECP256K1_PK_SIZE = 32; const int SECP256K1_SK_SIZE = 32; const int SECP256K1_SIG_SIZE = 64; -const int FN_DSA_512_PK_SIZE = 897; -const int FN_DSA_512_SK_SIZE = 1281; -const int FN_DSA_512_SIG_SIZE = 666; - const int ML_DSA_44_PK_SIZE = 1312; const int ML_DSA_44_SK_SIZE = 2528; const int ML_DSA_44_SIG_SIZE = 2420; @@ -25,7 +21,7 @@ const int BAD_KEY = -2; const int BAD_SIGNATURE = -3; const int NOT_IMPLEMENTED = -4; -Napi::Value GetPublicKeySize(const Napi::CallbackInfo& info) { +Napi::Value GetPublicKeySize(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsNumber()) { @@ -37,17 +33,23 @@ Napi::Value GetPublicKeySize(const Napi::CallbackInfo& info) { int size = 0; switch (algorithm) { - case 0: size = SECP256K1_PK_SIZE; break; - case 1: size = FN_DSA_512_PK_SIZE; break; - case 2: size = ML_DSA_44_PK_SIZE; break; - case 3: size = SLH_DSA_SHAKE_128S_PK_SIZE; break; - default: size = 0; + case 0: + size = SECP256K1_PK_SIZE; + break; + case 1: + size = ML_DSA_44_PK_SIZE; + break; + case 2: + size = SLH_DSA_SHAKE_128S_PK_SIZE; + break; + default: + size = 0; } return Napi::Number::New(env, size); } -Napi::Value GetSecretKeySize(const Napi::CallbackInfo& info) { +Napi::Value GetSecretKeySize(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsNumber()) { @@ -59,17 +61,23 @@ Napi::Value GetSecretKeySize(const Napi::CallbackInfo& info) { int size = 0; switch (algorithm) { - case 0: size = SECP256K1_SK_SIZE; break; - case 1: size = FN_DSA_512_SK_SIZE; break; - case 2: size = ML_DSA_44_SK_SIZE; break; - case 3: size = SLH_DSA_SHAKE_128S_SK_SIZE; break; - default: size = 0; + case 0: + size = SECP256K1_SK_SIZE; + break; + case 1: + size = ML_DSA_44_SK_SIZE; + break; + case 2: + size = SLH_DSA_SHAKE_128S_SK_SIZE; + break; + default: + size = 0; } return Napi::Number::New(env, size); } -Napi::Value GetSignatureSize(const Napi::CallbackInfo& info) { +Napi::Value GetSignatureSize(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); if (info.Length() < 1 || !info[0].IsNumber()) { @@ -81,17 +89,23 @@ Napi::Value GetSignatureSize(const Napi::CallbackInfo& info) { int size = 0; switch (algorithm) { - case 0: size = SECP256K1_SIG_SIZE; break; - case 1: size = FN_DSA_512_SIG_SIZE; break; - case 2: size = ML_DSA_44_SIG_SIZE; break; - case 3: size = SLH_DSA_SHAKE_128S_SIG_SIZE; break; - default: size = 0; + case 0: + size = SECP256K1_SIG_SIZE; + break; + case 1: + size = ML_DSA_44_SIG_SIZE; + break; + case 2: + size = SLH_DSA_SHAKE_128S_SIG_SIZE; + break; + default: + size = 0; } return Napi::Number::New(env, size); } -Napi::Value GenerateKeypair(const Napi::CallbackInfo& info) { +Napi::Value GenerateKeypair(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsTypedArray()) { @@ -103,7 +117,8 @@ Napi::Value GenerateKeypair(const Napi::CallbackInfo& info) { Napi::Uint8Array randomData = info[1].As(); if (randomData.ByteLength() < 128) { - Napi::Error::New(env, "Random data must be at least 128 bytes").ThrowAsJavaScriptException(); + Napi::Error::New(env, "Random data must be at least 128 bytes") + .ThrowAsJavaScriptException(); return env.Null(); } @@ -117,24 +132,20 @@ Napi::Value GenerateKeypair(const Napi::CallbackInfo& info) { int skSize = 0; switch (algorithm) { - case 0: - pkSize = SECP256K1_PK_SIZE; - skSize = SECP256K1_SK_SIZE; - break; - case 1: - pkSize = FN_DSA_512_PK_SIZE; - skSize = FN_DSA_512_SK_SIZE; - break; - case 2: - pkSize = ML_DSA_44_PK_SIZE; - skSize = ML_DSA_44_SK_SIZE; - break; - case 3: - pkSize = SLH_DSA_SHAKE_128S_PK_SIZE; - skSize = SLH_DSA_SHAKE_128S_SK_SIZE; - break; - default: - return Napi::Number::New(env, BAD_ARGUMENT); + case 0: + pkSize = SECP256K1_PK_SIZE; + skSize = SECP256K1_SK_SIZE; + break; + case 1: + pkSize = ML_DSA_44_PK_SIZE; + skSize = ML_DSA_44_SK_SIZE; + break; + case 2: + pkSize = SLH_DSA_SHAKE_128S_PK_SIZE; + skSize = SLH_DSA_SHAKE_128S_SK_SIZE; + break; + default: + return Napi::Number::New(env, BAD_ARGUMENT); } // Create result object @@ -160,10 +171,11 @@ Napi::Value GenerateKeypair(const Napi::CallbackInfo& info) { return result; } -Napi::Value SignMessage(const Napi::CallbackInfo& info) { +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()) { + if (info.Length() < 3 || !info[0].IsNumber() || !info[1].IsTypedArray() || + !info[2].IsTypedArray()) { Napi::TypeError::New(env, "Wrong arguments").ThrowAsJavaScriptException(); return env.Null(); } @@ -182,24 +194,20 @@ Napi::Value SignMessage(const Napi::CallbackInfo& info) { int expectedSkSize = 0; switch (algorithm) { - case 0: - sigSize = SECP256K1_SIG_SIZE; - expectedSkSize = SECP256K1_SK_SIZE; - break; - case 1: - sigSize = FN_DSA_512_SIG_SIZE; - expectedSkSize = FN_DSA_512_SK_SIZE; - break; - case 2: - sigSize = ML_DSA_44_SIG_SIZE; - expectedSkSize = ML_DSA_44_SK_SIZE; - break; - case 3: - sigSize = SLH_DSA_SHAKE_128S_SIG_SIZE; - expectedSkSize = SLH_DSA_SHAKE_128S_SK_SIZE; - break; - default: - return Napi::Number::New(env, BAD_ARGUMENT); + case 0: + sigSize = SECP256K1_SIG_SIZE; + expectedSkSize = SECP256K1_SK_SIZE; + break; + case 1: + sigSize = ML_DSA_44_SIG_SIZE; + expectedSkSize = ML_DSA_44_SK_SIZE; + break; + case 2: + sigSize = SLH_DSA_SHAKE_128S_SIG_SIZE; + expectedSkSize = SLH_DSA_SHAKE_128S_SK_SIZE; + break; + default: + return Napi::Number::New(env, BAD_ARGUMENT); } // Check key size @@ -216,7 +224,8 @@ Napi::Value SignMessage(const Napi::CallbackInfo& info) { // Create dummy signature Napi::Uint8Array signature = Napi::Uint8Array::New(env, sigSize); - // Use message to fill the signature (in a real implementation this would be a proper signature) + // Use message to fill the signature (in a real implementation this would be a + // proper signature) for (size_t i = 0; i < sigSize; i++) { signature[i] = i < message.ByteLength() ? message[i] : 0; } @@ -227,7 +236,7 @@ Napi::Value SignMessage(const Napi::CallbackInfo& info) { return result; } -Napi::Value VerifySignature(const Napi::CallbackInfo& info) { +Napi::Value VerifySignature(const Napi::CallbackInfo &info) { Napi::Env env = info.Env(); if (info.Length() < 4 || !info[0].IsNumber() || !info[1].IsTypedArray() || @@ -250,24 +259,20 @@ Napi::Value VerifySignature(const Napi::CallbackInfo& info) { int expectedSigSize = 0; switch (algorithm) { - case 0: - expectedPkSize = SECP256K1_PK_SIZE; - expectedSigSize = SECP256K1_SIG_SIZE; - break; - case 1: - expectedPkSize = FN_DSA_512_PK_SIZE; - expectedSigSize = FN_DSA_512_SIG_SIZE; - break; - case 2: - expectedPkSize = ML_DSA_44_PK_SIZE; - expectedSigSize = ML_DSA_44_SIG_SIZE; - break; - case 3: - expectedPkSize = SLH_DSA_SHAKE_128S_PK_SIZE; - expectedSigSize = SLH_DSA_SHAKE_128S_SIG_SIZE; - break; - default: - return Napi::Number::New(env, BAD_ARGUMENT); + case 0: + expectedPkSize = SECP256K1_PK_SIZE; + expectedSigSize = SECP256K1_SIG_SIZE; + break; + case 1: + expectedPkSize = ML_DSA_44_PK_SIZE; + expectedSigSize = ML_DSA_44_SIG_SIZE; + break; + case 2: + expectedPkSize = SLH_DSA_SHAKE_128S_PK_SIZE; + expectedSigSize = SLH_DSA_SHAKE_128S_SIG_SIZE; + break; + default: + return Napi::Number::New(env, BAD_ARGUMENT); } // Check key and signature sizes @@ -285,9 +290,12 @@ Napi::Value VerifySignature(const Napi::CallbackInfo& info) { } 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_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)); diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts index 28ea188..282c408 100644 --- a/nodejs/src/types.ts +++ b/nodejs/src/types.ts @@ -4,12 +4,10 @@ export enum Algorithm { /** BIP-340 Schnorr + X-Only - Elliptic Curve Digital Signature Algorithm */ SECP256K1_SCHNORR = 0, - /** FN-DSA-512 (FALCON) - Fast Fourier lattice-based signature scheme */ - FN_DSA_512 = 1, /** ML-DSA-44 (CRYSTALS-Dilithium) - Lattice-based signature scheme */ - ML_DSA_44 = 2, + ML_DSA_44 = 1, /** SLH-DSA-Shake-128s (SPHINCS+) - Hash-based signature scheme */ - SLH_DSA_SHAKE_128S = 3, + SLH_DSA_SHAKE_128S = 2, } /** diff --git a/nodejs/tests/bitcoinpqc.test.ts b/nodejs/tests/bitcoinpqc.test.ts index 5a18799..9930055 100644 --- a/nodejs/tests/bitcoinpqc.test.ts +++ b/nodejs/tests/bitcoinpqc.test.ts @@ -31,7 +31,6 @@ describe("Bitcoin PQC", () => { // Test key size reporting functions for (const algo of [ Algorithm.SECP256K1_SCHNORR, - Algorithm.FN_DSA_512, Algorithm.ML_DSA_44, Algorithm.SLH_DSA_SHAKE_128S, ]) { @@ -110,47 +109,45 @@ describe("Bitcoin PQC", () => { }); }); - describe("FN-DSA-512 (FALCON)", () => { - const algorithm = Algorithm.FN_DSA_512; - test("should generate keypair, sign and verify", () => { - // Generate random data for key generation - const randomData = getRandomBytes(128); + test("should generate keypair, sign and verify", () => { + // Generate random data for key generation + const randomData = getRandomBytes(128); - // Generate a keypair - const keypair = generateKeyPair(algorithm, randomData); + // Generate a keypair + const keypair = generateKeyPair(algorithm, randomData); - // Verify key sizes match reported sizes - expect(keypair.publicKey.bytes.length).toBe(publicKeySize(algorithm)); - expect(keypair.secretKey.bytes.length).toBe(secretKeySize(algorithm)); + // Verify key sizes match reported sizes + expect(keypair.publicKey.bytes.length).toBe(publicKeySize(algorithm)); + expect(keypair.secretKey.bytes.length).toBe(secretKeySize(algorithm)); - // Test message signing - const message = new TextEncoder().encode("Hello, Bitcoin PQC!"); - const signature = sign(keypair.secretKey, message); + // Test message signing + const message = new TextEncoder().encode("Hello, Bitcoin PQC!"); + const signature = sign(keypair.secretKey, message); - // Verify signature size matches reported size - expect(signature.bytes.length).toBe(signatureSize(algorithm)); + // Verify signature size matches reported size + expect(signature.bytes.length).toBe(signatureSize(algorithm)); - // Verify the signature - should not throw - expect(() => { - verify(keypair.publicKey, message, signature); - }).not.toThrow(); - }); + // Verify the signature - should not throw + expect(() => { + verify(keypair.publicKey, message, signature); + }).not.toThrow(); }); +}); - describe("error conditions", () => { - test("should throw on invalid input", () => { - // Invalid algorithm - expect(() => { - const randomData = getRandomBytes(128); - generateKeyPair(99 as Algorithm, randomData); - }).toThrow(PqcError); - - // Invalid random data size - expect(() => { - const randomData = getRandomBytes(16); // Less than 128 bytes - generateKeyPair(Algorithm.ML_DSA_44, randomData); - }).toThrow(PqcError); - }); +describe("error conditions", () => { + test("should throw on invalid input", () => { + // Invalid algorithm + expect(() => { + const randomData = getRandomBytes(128); + generateKeyPair(99 as Algorithm, randomData); + }).toThrow(PqcError); + + // Invalid random data size + expect(() => { + const randomData = getRandomBytes(16); // Less than 128 bytes + generateKeyPair(Algorithm.ML_DSA_44, randomData); + }).toThrow(PqcError); }); }); +}); diff --git a/nodejs/tsconfig.json b/nodejs/tsconfig.json index 005fce8..7c2a087 100644 --- a/nodejs/tsconfig.json +++ b/nodejs/tsconfig.json @@ -1,7 +1,6 @@ { "compilerOptions": { /* Visit https://aka.ms/tsconfig to read more about this file */ - /* Projects */ // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ @@ -9,9 +8,8 @@ // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - /* Language and Environment */ - "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "libReplacement": true, /* Enable lib replacement. */ @@ -24,10 +22,10 @@ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - "rootDir": "./src", /* Specify the root folder within your source files. */ + "module": "commonjs", /* Specify what module code is generated. */ + "rootDir": "./src", /* Specify the root folder within your source files. */ + "baseUrl": ".", /* Specify the base directory to resolve non-relative module names. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ @@ -45,21 +43,19 @@ // "resolveJsonModule": true, /* Enable importing .json files. */ // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - /* JavaScript Support */ // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ // "declarationMap": true, /* Create sourcemaps for d.ts files. */ // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ + "outDir": "./dist", /* Specify an output folder for all emitted files. */ // "removeComments": true, /* Disable emitting comments. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ @@ -73,19 +69,17 @@ // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - /* Interop Constraints */ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ @@ -105,11 +99,18 @@ // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": ["src/**/*"], - "exclude": ["node_modules", "**/*.test.ts", "dist"] -} + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules", + "**/*.test.ts", + "dist", + "src/__tests__/**/*", + "src/c_sources/**/*" + ] +} \ No newline at end of file