Skip to content

fix: snowden mode could not decrypt its own output - #9

Merged
JTorkk merged 2 commits into
mainfrom
fix/snowden-iv-regression
Aug 24, 2026
Merged

fix: snowden mode could not decrypt its own output#9
JTorkk merged 2 commits into
mainfrom
fix/snowden-iv-regression

Conversation

@JTorkk

@JTorkk JTorkk commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

encrypt 5.0.3 changed IV.fromLength(16) to return RANDOM bytes instead of all zeros. The renderer and the reader each build their own IV, so every snowden-mode QR code became undecryptable ("Invalid or corrupted pad block"). tryParseBase64 swallows that exception, so the reader silently ignored every code and an app using it looked like its camera was broken.

Use IV.allZerosOfLength(16) instead. This restores the previous behaviour and produces byte-identical output to encrypt 5.0.1, so clients on 1.3.0 and earlier can still exchange codes with 1.4.0.

Also:

  • Add onError to QrPlusReader, reporting a QrPlusReadError when a code is detected but cannot be decoded. The silent catch is what made this bug so hard to diagnose: an undecryptable code was indistinguishable from the camera seeing nothing.
  • Add crypto regression tests that pin the exact wire format, so any future change in crypto behaviour fails loudly instead of shipping codes no device can read.
  • Fix a TTL test that asserted nothing because it built its crumb with QrPlusMode.safe, which carries no TTL at all.
  • Run CI on pushes to main, not only on pull requests. The 1.3.0 dependency update went straight to main and skipped CI entirely, which is why the existing round-trip tests never flagged this.
  • Bump the CI and CD Flutter versions, which were pinned to 3.10.0 and 3.7.0 and no longer satisfy this package's SDK constraint.

Description

Conventional Type

  • ✨ feat
  • 🛠️ fix
  • ✅ chore
  • 📝 doc
  • 🧪 test
  • ❌ BREAKING CHANGE

encrypt 5.0.3 changed IV.fromLength(16) to return RANDOM bytes instead of
all zeros. The renderer and the reader each build their own IV, so every
snowden-mode QR code became undecryptable ("Invalid or corrupted pad
block"). tryParseBase64 swallows that exception, so the reader silently
ignored every code and an app using it looked like its camera was broken.

Use IV.allZerosOfLength(16) instead. This restores the previous behaviour
and produces byte-identical output to encrypt 5.0.1, so clients on 1.3.0
and earlier can still exchange codes with 1.4.0.

Also:

- Add onError to QrPlusReader, reporting a QrPlusReadError when a code is
  detected but cannot be decoded. The silent catch is what made this bug
  so hard to diagnose: an undecryptable code was indistinguishable from
  the camera seeing nothing.
- Add crypto regression tests that pin the exact wire format, so any
  future change in crypto behaviour fails loudly instead of shipping
  codes no device can read.
- Fix a TTL test that asserted nothing because it built its crumb with
  QrPlusMode.safe, which carries no TTL at all.
- Run CI on pushes to main, not only on pull requests. The 1.3.0
  dependency update went straight to main and skipped CI entirely, which
  is why the existing round-trip tests never flagged this.
- Bump the CI and CD Flutter versions, which were pinned to 3.10.0 and
  3.7.0 and no longer satisfy this package's SDK constraint.
@JTorkk
JTorkk requested a review from O-Hannonen August 24, 2026 19:36
`dart format . --fix` no longer exists: the flag was removed from newer
Dart SDKs, so bumping CI from Flutter 3.10.0 to 3.44.2 broke the format
step with exit code 64. Formatting is written in place by default, and
applying lint fixes is now a separate command (`dart fix --apply`).

Also updates QrPlusReaderController's test, which had been failing since
44e26b0 deliberately changed `barcodes` from `const Stream.empty()` to
forwarding `super.barcodes`. The implementation is intentional; the test
was simply never updated. It stays deprecated because the values it
emits are raw, still-encoded scanner data.

Full CI pipeline now passes locally: pub get, format (0 changed),
analyze --fatal-infos, and all 132 tests.
@JTorkk
JTorkk merged commit 15dac16 into main Aug 24, 2026
1 check failed

@O-Hannonen O-Hannonen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with one nit, nice job! 🚀

Comment thread pubspec.yaml
@@ -1,6 +1,6 @@
name: qr_plus
description: Advanced all-in-one qr code package with support for safe qr codes.
version: 1.3.0

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a patch as 1.3.0 is broken? So 1.3.1?

@JTorkk JTorkk Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the onError callback so I think 1.4.0 is more suitable. Probably should have been its own pr though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants