fix(realunit): accept BitBox-safe ASCII transliterations in registration#3709
Merged
Conversation
❌ Security: 1 critical vulnerabilities |
This was referenced May 15, 2026
TaprootFreak
added a commit
that referenced
this pull request
May 15, 2026
…3711) GHSA-rpr9-rxv7-x643 — default XSS via xmp raw-text passthrough, fixed in 2.17.4. Patch bump only; package range stays ^2.17.x. Unblocks the CI 'Security audit' step (npm audit --audit-level=critical), which started failing this morning after the advisory dropped and now blocks every PR build (#3707, #3708, #3709).
realunit-app v0.0.3+ transliterates EIP-712 string fields to ASCII-with-German-digraphs (Krüger → Krueger) so the BitBox firmware accepts them, while keeping the kycData copy in UTF-8 for ID verification. Without this change the backend rejects every BitBox registration that touches a non-ASCII character: - validateRegistrationDto compares dto.kycData.firstName/lastName (UTF-8) against dto.name (ASCII) and throws "firstName + lastName does not match signed name". Same for street/zip/city/organizationName. - verifyRealUnitRegistrationSignature recomputes the EIP-712 hash from the stored accountData; for users who registered before this fix the stored fields are UTF-8 and a fresh ASCII signature no longer recovers the wallet address. Adds a `toBitboxAscii` helper that mirrors the Dart implementation (`ä` → `ae`, `ß` → `ss`, …) — kept separate from the existing `transliteration` npm package, which uses single-char substitution and would not match the client's hash. validateRegistrationDto now accepts either the literal or the ASCII form via `matchesSignedField`. verifyRealUnitRegistrationSignature retries with ASCII-transliterated message values when the primary recovery fails, so re-login (registerWallet) keeps working for pre-fix registrations.
9f31c29 to
73ab3e1
Compare
Maps now group by base letter (one row per letter, multiple diacritic variants per row) for readability — 212 → 89 lines. Behavior unchanged, all 6 spec tests still pass.
3 tasks
Match the existing house style for character normalization (Util.removeSpecialChars, util.ts). 89 → 79 lines, removes the need for // prettier-ignore (which was the only place in the repo using that pragma). Parity verified: all 168 entries in the Dart source map produce identical output in the chained-replace version.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Unblocks realunit-app#332 (BitBox sign hardening). Without this PR every BitBox-signed registration that touches a non-ASCII character (umlaut, accent, …) is rejected by the backend with
firstName + lastName does not match signed name.The client transliterates EIP-712 string fields to BitBox-safe ASCII (
ä→ae,ß→ss, …) because the BitBox02 firmware rejects any non-ASCII byte in astring-typed value, while keepingkycDataUTF-8 for ID verification. Two server checks need to learn about this duality:validateRegistrationDtocomparesdto.kycData.firstName + lastName(UTF-8) todto.name(ASCII). Same for street/zip/city/organizationName. → introducesmatchesSignedField(kycValue, signedValue)which accepts either the literal or the ASCII form.verifyRealUnitRegistrationSignaturerecovers the wallet address from the EIP-712 hash over the storedaccountData. For users that registered before realunit-app v0.0.3 the stored fields are UTF-8 — a fresh ASCII signature no longer recovers. → retry with ASCII-transliterated message when the primary verify fails.A new
toBitboxAsciihelper mirrors the DarttoBitboxSafeAsciiexactly. It is intentionally kept separate from the existing npmtransliterationpackage: that one mapsütou(single char), while the BitBox-safe / German-romanization convention maps it toue— the only convention the client actually emits.Test plan
npm run formatcleannpm run lintcleannpm run type-checkcleannpx jest src/shared/utils/__tests__/bitbox-ascii.spec.ts(6/6 pass)npx jest src/subdomains/supporting/realunit/__tests__/realunit.service.spec.ts(10/10 pass, no regressions)Compatibility
matchesSignedFieldstill accepts literal equality, primary verify still succeeds → no change.matchesSignedFieldnow passes the ASCII check, primary verify on freshly stored ASCIIaccountDatasucceeds → works.