diff --git a/example/src/tests/ecdh/ecdh_tests.ts b/example/src/tests/ecdh/ecdh_tests.ts index 023babfa..2e955716 100644 --- a/example/src/tests/ecdh/ecdh_tests.ts +++ b/example/src/tests/ecdh/ecdh_tests.ts @@ -1,5 +1,5 @@ import { test } from '../util'; -import crypto, { Buffer } from 'react-native-quick-crypto'; +import crypto, { Buffer, getCurves } from 'react-native-quick-crypto'; import { assert } from 'chai'; const SUITE = 'ecdh'; @@ -68,3 +68,25 @@ test(SUITE, 'should work with string input', () => { const secret = alice.computeSecret(bobPubHex, 'hex'); assert.isOk(secret); }); + +test(SUITE, 'getCurves - should return array of supported curves', () => { + const curves = getCurves(); + assert.isArray(curves); + assert.isAbove(curves.length, 0, 'should have at least one curve'); + + const expectedCurves = ['prime256v1', 'secp384r1', 'secp521r1', 'secp256k1']; + for (const curve of expectedCurves) { + assert.include(curves, curve, `should include ${curve}`); + } + + const isSorted = curves.every( + (val: string, i: number) => i === 0 || val >= curves[i - 1]!, + ); + assert.isTrue(isSorted, 'curves should be sorted alphabetically'); +}); + +test(SUITE, 'getCurves - should match crypto.getCurves()', () => { + const named = getCurves(); + const fromDefault = crypto.getCurves(); + assert.deepEqual(named, fromDefault); +}); diff --git a/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.cpp b/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.cpp index d6f7d30c..49b959a4 100644 --- a/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.cpp +++ b/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -425,4 +426,24 @@ void HybridEcKeyPair::checkKeyPair() { } } +std::vector HybridEcKeyPair::getSupportedCurves() { + const size_t count = EC_get_builtin_curves(nullptr, 0); + std::vector curves(count); + if (EC_get_builtin_curves(curves.data(), count) != count) { + throw std::runtime_error("Failed to enumerate EC curves"); + } + + std::vector names; + names.reserve(count); + for (const auto& curve : curves) { + const char* sn = OBJ_nid2sn(curve.nid); + if (sn != nullptr) { + names.emplace_back(sn); + } + } + + std::sort(names.begin(), names.end()); + return names; +} + } // namespace margelo::nitro::crypto diff --git a/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.hpp b/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.hpp index bbc5aeff..0504a841 100644 --- a/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.hpp +++ b/packages/react-native-quick-crypto/cpp/ec/HybridEcKeyPair.hpp @@ -34,6 +34,7 @@ class HybridEcKeyPair : public HybridEcKeyPairSpec { std::shared_ptr sign(const std::shared_ptr& data, const std::string& hashAlgorithm) override; bool verify(const std::shared_ptr& data, const std::shared_ptr& signature, const std::string& hashAlgorithm) override; + std::vector getSupportedCurves() override; protected: void checkKeyPair(); diff --git a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.cpp b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.cpp index abb20916..0ae1c883 100644 --- a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.cpp +++ b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.cpp @@ -23,6 +23,7 @@ namespace margelo::nitro::crypto { prototype.registerHybridMethod("setCurve", &HybridEcKeyPairSpec::setCurve); prototype.registerHybridMethod("sign", &HybridEcKeyPairSpec::sign); prototype.registerHybridMethod("verify", &HybridEcKeyPairSpec::verify); + prototype.registerHybridMethod("getSupportedCurves", &HybridEcKeyPairSpec::getSupportedCurves); }); } diff --git a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.hpp b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.hpp index bb5c6731..7ef4ea24 100644 --- a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.hpp +++ b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEcKeyPairSpec.hpp @@ -62,6 +62,7 @@ namespace margelo::nitro::crypto { virtual void setCurve(const std::string& curve) = 0; virtual std::shared_ptr sign(const std::shared_ptr& data, const std::string& hashAlgorithm) = 0; virtual bool verify(const std::shared_ptr& data, const std::shared_ptr& signature, const std::string& hashAlgorithm) = 0; + virtual std::vector getSupportedCurves() = 0; protected: // Hybrid Setup diff --git a/packages/react-native-quick-crypto/src/ec.ts b/packages/react-native-quick-crypto/src/ec.ts index 3b219048..49b4b4ba 100644 --- a/packages/react-native-quick-crypto/src/ec.ts +++ b/packages/react-native-quick-crypto/src/ec.ts @@ -34,6 +34,23 @@ import { import { Buffer } from '@craftzdog/react-native-buffer'; import { ECDH } from './ecdh'; +class EcUtils { + private static _native: EcKeyPair | undefined; + private static get native(): EcKeyPair { + if (!this._native) { + this._native = NitroModules.createHybridObject('EcKeyPair'); + } + return this._native; + } + public static getSupportedCurves(): string[] { + return this.native.getSupportedCurves(); + } +} + +export function getCurves(): string[] { + return EcUtils.getSupportedCurves(); +} + export class Ec { native: EcKeyPair; diff --git a/packages/react-native-quick-crypto/src/index.ts b/packages/react-native-quick-crypto/src/index.ts index e12301a4..21d10352 100644 --- a/packages/react-native-quick-crypto/src/index.ts +++ b/packages/react-native-quick-crypto/src/index.ts @@ -14,6 +14,7 @@ import * as scrypt from './scrypt'; import * as random from './random'; import * as ecdh from './ecdh'; import * as dh from './diffie-hellman'; +import { getCurves } from './ec'; import { constants } from './constants'; // utils import @@ -39,6 +40,7 @@ const QuickCrypto = { ...dh, ...utils, ...subtle, + getCurves, constants, Buffer, }; @@ -81,6 +83,7 @@ export * from './pbkdf2'; export * from './scrypt'; export * from './random'; export * from './ecdh'; +export { getCurves } from './ec'; export * from './diffie-hellman'; export * from './utils'; export * from './subtle'; diff --git a/packages/react-native-quick-crypto/src/specs/ecKeyPair.nitro.ts b/packages/react-native-quick-crypto/src/specs/ecKeyPair.nitro.ts index 5b6aaabd..bbba7662 100644 --- a/packages/react-native-quick-crypto/src/specs/ecKeyPair.nitro.ts +++ b/packages/react-native-quick-crypto/src/specs/ecKeyPair.nitro.ts @@ -35,4 +35,6 @@ export interface EcKeyPair signature: ArrayBuffer, hashAlgorithm: string, ): boolean; + + getSupportedCurves(): string[]; }