Skip to content

iOS smart punctuation changes BIP39 passphrases during import #2548

Description

@leesalminen

Summary

On iOS, the optional BIP39 passphrase field can silently replace a straight ASCII apostrophe (', U+0027) with a curly apostrophe (, U+2019) through Smart Punctuation.

BIP39 passphrases are exact inputs to seed derivation, so this produces a valid but completely different wallet. The import preview then shows 0 balance and 0 transaction history even though the user typed the expected key on the iOS keyboard.

Reproduction

  1. On an iOS device with Smart Punctuation enabled, start importing a mnemonic.
  2. Enter a passphrase containing a straight apostrophe using the normal apostrophe key.
  3. Observe that iOS renders/inserts a curly apostrophe.
  4. Continue to the wallet scan.
  5. The derived wallet has 0 balance and 0 transactions instead of finding the expected wallet.

Confirmed on a user's iOS device. No mnemonic or passphrase should be collected or logged while diagnosing this.

Expected behavior

The passphrase field preserves every character exactly as entered and does not apply typographic substitutions.

Root cause

The passphrase ultimately uses an unobscured Flutter TextField. Flutter enables smart quotes and smart dashes by default for unobscured text fields, allowing iOS to transform passphrase characters before onChanged receives them.

Minimal suggested fix

Disable smart punctuation and text correction specifically for the passphrase field:

TextField(
  keyboardType: TextInputType.visiblePassword,
  smartQuotesType: SmartQuotesType.disabled,
  smartDashesType: SmartDashesType.disabled,
  autocorrect: false,
  enableSuggestions: false,
  enableIMEPersonalizedLearning: false,
  onChanged: updatePassphrase,
)

The current field is wrapped by LabeledTextInput and BBInputText, so these options will need to be exposed/forwarded through those wrappers and enabled only for the passphrase input in lib/core/widgets/mnemonic_widget.dart.

Do not normalize or replace curly apostrophes in the submitted value: a curly apostrophe may be an intentional passphrase character, and rewriting it would derive the wrong wallet for those users.

Regression coverage

Add a widget test that finds the passphrase TextField and verifies:

  • smartQuotesType == SmartQuotesType.disabled
  • smartDashesType == SmartDashesType.disabled
  • autocorrect == false
  • enableSuggestions == false
  • unrelated label/text inputs retain their existing defaults

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions