From 3848fa315401e148c9b1c41f0b4ef9cb73deb193 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 9 Nov 2026 20:12:28 -0500 Subject: [PATCH 1/3] docs(plans): track #1094 ncrypto alignment --- plans/todo/1094-align-ncrypto-prime-sec1.md | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 plans/todo/1094-align-ncrypto-prime-sec1.md diff --git a/plans/todo/1094-align-ncrypto-prime-sec1.md b/plans/todo/1094-align-ncrypto-prime-sec1.md new file mode 100644 index 000000000..f28ff672e --- /dev/null +++ b/plans/todo/1094-align-ncrypto-prime-sec1.md @@ -0,0 +1,37 @@ +# 1094 — Align vendored ncrypto prime checks and empty SEC1 passphrases with Node f39899d + +## Problem + +Vendored ncrypto (`boorad/ncrypto` `ef5d829`) lacks two details from Node `f39899d`: + +1. `BignumPointer::isPrime` does not invoke the callback between repeated `BN_check_prime()` rounds, so trial-division primes (e.g. `311`) cannot be cancelled. +2. `WriteEncryptedTraditionalPEM` hardcodes `OSSL_KEYMGMT_SELECT_KEYPAIR`; the SEC1 empty-passphrase path drops explicit-curve domain parameters. + +Outcome: fork carries one compat commit with both fixes and gtest coverage; RNQC pins it and the example app covers SEC1 encrypted PEM rejections. + +## Live tracker + +- [ ] Phase 1 — ncrypto compat commit: `isPrime` inter-round cancel, `selection` param, `tests/basic.cpp` cases (2^127-1 @ 65, 311 @ INT_MAX, explicit-curve SEC1 empty passphrase) +- [ ] Phase 2 — fork publish: backup ref `backup/ef5d829`, new `rnqc/evp-only` branch, reset fork `main` to `c954a1a` +- [ ] Phase 3 — RNQC submodule bump + example test (SEC1 empty/NUL passphrase round-trip, wrong and missing passphrase rejection) +- [ ] Qualification — clang-format, deprecation script, gtest, native builds, example-app keys/prime suites +- [ ] Independent local review +- [ ] Plan move + `/pr` + +## Decisions + +- Fork layout (user, 2026-11-09): backup ref for `ef5d829`, amended single compat commit on new `rnqc/evp-only` branch based on upstream `c954a1a`, force-push fork `main` back to `c954a1a` (architecture.md mirror rule). + +## Assumptions + +- Amend `ef5d829` rather than stack a second commit (architecture.md: one compat commit). +- No `.gitmodules` `branch =` key; pins are by SHA. +- SELECT_ALL proof for explicit curves lives in gtest; example app covers the public named-curve path (no explicit private-key fixture exists). + +## Evidence + +_(SHA — tool/test — result — platform)_ + +## Unverified + +- Native runtime on iOS/Android — settled by example-app keys + prime suites after the bump. From edef883f5211f5e3072497e81b56307b06d09f29 Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 9 Nov 2026 20:16:19 -0500 Subject: [PATCH 2/3] fix(ncrypto): align prime-check cancellation and SEC1 empty-passphrase export with Node f39899d --- example/src/tests/keys/create_keys.ts | 7 +++++++ packages/react-native-quick-crypto/deps/ncrypto | 2 +- plans/todo/1094-align-ncrypto-prime-sec1.md | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/example/src/tests/keys/create_keys.ts b/example/src/tests/keys/create_keys.ts index df7278aea..d1ac0f9bd 100644 --- a/example/src/tests/keys/create_keys.ts +++ b/example/src/tests/keys/create_keys.ts @@ -588,6 +588,13 @@ test( expect( createPrivateKey({ key: pem, passphrase }).equals(privateKey), ).to.equal(true); + + if (passphrase.length > 0) { + expect(() => createPrivateKey(pem)).to.throw('Passphrase required'); + expect(() => + createPrivateKey({ key: pem, passphrase: 'wrong' }), + ).to.throw('Failed to read'); + } } }, ); diff --git a/packages/react-native-quick-crypto/deps/ncrypto b/packages/react-native-quick-crypto/deps/ncrypto index ef5d829e2..14d602315 160000 --- a/packages/react-native-quick-crypto/deps/ncrypto +++ b/packages/react-native-quick-crypto/deps/ncrypto @@ -1 +1 @@ -Subproject commit ef5d829e27c9fcf539d4bf6b478353b888d607a2 +Subproject commit 14d602315ec98e205faea4e25a6b3fe1d98d6449 diff --git a/plans/todo/1094-align-ncrypto-prime-sec1.md b/plans/todo/1094-align-ncrypto-prime-sec1.md index f28ff672e..9dd0b494b 100644 --- a/plans/todo/1094-align-ncrypto-prime-sec1.md +++ b/plans/todo/1094-align-ncrypto-prime-sec1.md @@ -11,9 +11,9 @@ Outcome: fork carries one compat commit with both fixes and gtest coverage; RNQC ## Live tracker -- [ ] Phase 1 — ncrypto compat commit: `isPrime` inter-round cancel, `selection` param, `tests/basic.cpp` cases (2^127-1 @ 65, 311 @ INT_MAX, explicit-curve SEC1 empty passphrase) +- [x] Phase 1 — ncrypto compat commit: `isPrime` inter-round cancel, `selection` param, `tests/basic.cpp` cases (2^127-1 @ 65, 311 @ INT_MAX, explicit-curve SEC1 empty passphrase) - [ ] Phase 2 — fork publish: backup ref `backup/ef5d829`, new `rnqc/evp-only` branch, reset fork `main` to `c954a1a` -- [ ] Phase 3 — RNQC submodule bump + example test (SEC1 empty/NUL passphrase round-trip, wrong and missing passphrase rejection) +- [x] Phase 3 — RNQC submodule bump + example test (SEC1 empty/NUL passphrase round-trip, wrong and missing passphrase rejection) - [ ] Qualification — clang-format, deprecation script, gtest, native builds, example-app keys/prime suites - [ ] Independent local review - [ ] Plan move + `/pr` @@ -27,11 +27,21 @@ Outcome: fork carries one compat commit with both fixes and gtest coverage; RNQC - Amend `ef5d829` rather than stack a second commit (architecture.md: one compat commit). - No `.gitmodules` `branch =` key; pins are by SHA. - SELECT_ALL proof for explicit curves lives in gtest; example app covers the public named-curve path (no explicit private-key fixture exists). +- On OpenSSL 3.6.2, OSSL_KEYMGMT_SELECT_KEYPAIR already emits explicit-curve EC parameters for SEC1 type-specific encoding (gtest explicitEcSec1EncryptedPemPreservesParameters passes pre-fix); SELECT_ALL is kept for Node f39899d parity and future-OpenSSL safety. +- Example SEC1 test mirrors the RSA PKCS1 test: missing/wrong passphrase rejections are asserted only for non-empty passphrases (an empty-passphrase PEM has no meaningful missing-vs-wrong distinction). +- Podfile.lock unchanged: the QuickCrypto checksum is podspec-derived (06a2f7e9 changed it only because the podspec changed), not submodule-content-derived. ## Evidence _(SHA — tool/test — result — platform)_ +- submodule 14d6023 — cmake (NCRYPTO_SHARED_LIBS=ON, NCRYPTO_EVP_ONLY=ON, openssl@3 3.6.2, googletest 1.18.0) + ctest — 37/37 pass; pre-fix both new prime-cancel tests fail — macOS host +- submodule 14d6023 — clang-format --style=file --dry-run --Werror src/ncrypto.cpp tests/basic.cpp include/ncrypto.h — pass +- submodule 14d6023 — OPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3 scripts/check-ncrypto-openssl-deprecations.sh — pass (default invocation picks Homebrew openssl@4 4.1.0 via pkg-config and fails on pre-existing deprecations unrelated to this diff) +- HEAD of phase 3 commit — prettier --check create_keys.ts, bun tsc, bun lint (repo root) — pass — macOS host + ## Unverified - Native runtime on iOS/Android — settled by example-app keys + prime suites after the bump. +- Deprecation script default OpenSSL discovery picks openssl@4 — pre-existing, out of scope. +- New SEC1 example assertions not yet executed in the app (tsc/lint only). From a1912d1b346dcdef9483098f85aa78fc8744103c Mon Sep 17 00:00:00 2001 From: Brad Anderson Date: Mon, 9 Nov 2026 20:19:51 -0500 Subject: [PATCH 3/3] docs(plans): complete #1094 tracker --- .../1094-align-ncrypto-prime-sec1.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) rename plans/{todo => done}/1094-align-ncrypto-prime-sec1.md (76%) diff --git a/plans/todo/1094-align-ncrypto-prime-sec1.md b/plans/done/1094-align-ncrypto-prime-sec1.md similarity index 76% rename from plans/todo/1094-align-ncrypto-prime-sec1.md rename to plans/done/1094-align-ncrypto-prime-sec1.md index 9dd0b494b..682cd169e 100644 --- a/plans/todo/1094-align-ncrypto-prime-sec1.md +++ b/plans/done/1094-align-ncrypto-prime-sec1.md @@ -12,11 +12,12 @@ Outcome: fork carries one compat commit with both fixes and gtest coverage; RNQC ## Live tracker - [x] Phase 1 — ncrypto compat commit: `isPrime` inter-round cancel, `selection` param, `tests/basic.cpp` cases (2^127-1 @ 65, 311 @ INT_MAX, explicit-curve SEC1 empty passphrase) -- [ ] Phase 2 — fork publish: backup ref `backup/ef5d829`, new `rnqc/evp-only` branch, reset fork `main` to `c954a1a` +- [x] Phase 2 — fork publish: backup ref `backup/ef5d829`, new `rnqc/evp-only` branch, reset fork `main` to `c954a1a` - [x] Phase 3 — RNQC submodule bump + example test (SEC1 empty/NUL passphrase round-trip, wrong and missing passphrase rejection) -- [ ] Qualification — clang-format, deprecation script, gtest, native builds, example-app keys/prime suites -- [ ] Independent local review -- [ ] Plan move + `/pr` +- [x] Qualification (static/host) — clang-format, deprecation script, gtest; native iOS/Android runtime owned by the `/pr` attestation producer run +- [x] Independent local review (Opus): no correctness defects; fixed missing evidence SHA; fork publish done as Phase 2; two Info items skipped (explicit-curve test passes pre-fix — recorded; no PEM header assertion — low value) +- [x] Plan move +- [ ] `/pr` with signed iOS+Android attestation and screenshots — owner: orchestrator (this session) ## Decisions @@ -35,13 +36,17 @@ Outcome: fork carries one compat commit with both fixes and gtest coverage; RNQC _(SHA — tool/test — result — platform)_ +- fork boorad/ncrypto — git ls-remote + gh compare nodejs/ncrypto main...boorad:main — main=c954a1a identical; rnqc/evp-only=14d6023; backup/ef5d829 kept +- 15a3fc91 — reviewer rerun of basic gtest filter BignumPointer.*:EVPKeyPointer.explicitEcSec1* — 7/7 pass, cancel tests 0 ms — macOS host + - submodule 14d6023 — cmake (NCRYPTO_SHARED_LIBS=ON, NCRYPTO_EVP_ONLY=ON, openssl@3 3.6.2, googletest 1.18.0) + ctest — 37/37 pass; pre-fix both new prime-cancel tests fail — macOS host - submodule 14d6023 — clang-format --style=file --dry-run --Werror src/ncrypto.cpp tests/basic.cpp include/ncrypto.h — pass - submodule 14d6023 — OPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3 scripts/check-ncrypto-openssl-deprecations.sh — pass (default invocation picks Homebrew openssl@4 4.1.0 via pkg-config and fails on pre-existing deprecations unrelated to this diff) -- HEAD of phase 3 commit — prettier --check create_keys.ts, bun tsc, bun lint (repo root) — pass — macOS host +- 15a3fc91 — prettier --check create_keys.ts, bun tsc, bun lint (repo root) — pass — macOS host ## Unverified + - Native runtime on iOS/Android — settled by example-app keys + prime suites after the bump. - Deprecation script default OpenSSL discovery picks openssl@4 — pre-existing, out of scope. - New SEC1 example assertions not yet executed in the app (tsc/lint only).