Skip to content

feat(import): import Google Authenticator via otpauth-migration URIs#674

Merged
replydev merged 3 commits into
mainfrom
fix/import-from-google-authenticator
Jul 22, 2026
Merged

feat(import): import Google Authenticator via otpauth-migration URIs#674
replydev merged 3 commits into
mainfrom
fix/import-from-google-authenticator

Conversation

@replydev

@replydev replydev commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Problem

The --google-authenticator import relied on converters/gauth.py extracting the app's SQLite database (/data/data/com.google.android.apps.authenticator2/databases/databases), which requires root on the device. Those README/--help instructions are outdated — Google Authenticator's real export path is the "Export accounts" feature, which generates QR codes encoding otpauth-migration://offline?data=<base64 protobuf> URIs.

Closes the request to import from the migration URI / exported QR codes.

Changes

  • New native importer src/importers/google_authenticator.rs:
    • Reads a text file containing one or more otpauth-migration:// URIs (one per exported QR code, whitespace/newline separated).
    • Base64-decodes the data payload and decodes Google's MigrationPayload protobuf using a small, dependency-free in-crate protobuf reader (varints + length-delimited fields, fully bounds-checked — no unsafe, no new crates; reuses existing base64/data-encoding).
    • Maps each entry to an OTPElement: raw secret → BASE32_NOPAD, algorithm (SHA1/256/512/MD5), digit count (6/8), TOTP/HOTP type, HOTP counter.
  • Route --google-authenticator to the new importer and refresh its help text.
  • Update the README migration table to document the Export-accounts/QR flow (no Python conversion needed).
  • Document the Conventional Commits convention and semantic-release versioning in CLAUDE.md.

Scope note

URI text is accepted directly (extract it from the QR with any scanner). In-app QR image decoding was intentionally left out to avoid pulling in heavy image-decoding dependencies. converters/gauth.py is left in place for rooted users who still prefer SQLite extraction.

Testing

  • 5 new unit tests covering single/multi-entry, HOTP+SHA512+8-digit, multi-URI, and error paths.
  • Full suite green: cargo test --locked (debug + release), cargo clippy -D warnings, cargo fmt --check.
  • End-to-end verified: built a real migration URI and imported it into a throwaway database via the cotp binary, confirming the account listed and generated a live code.

The previous --google-authenticator flow relied on gauth.py extracting the
app's SQLite database, which requires root on the device. Google
Authenticator's actual export path is the "Export accounts" feature, which
produces QR codes encoding otpauth-migration:// URIs.

Add a native importer (src/importers/google_authenticator.rs) that reads a
file of one or more otpauth-migration:// URIs, base64-decodes the data
payload and decodes the MigrationPayload protobuf with prost (hand-declared
message structs, no protoc build step), mapping each entry to an OTPElement.
Route --google-authenticator to it and update the help text and README
accordingly.

Also document the Conventional Commits convention and semantic-release
versioning in CLAUDE.md.
@replydev
replydev force-pushed the fix/import-from-google-authenticator branch from f18bbac to e82ac01 Compare July 22, 2026 18:50
replydev added 2 commits July 22, 2026 20:57
Rust 1.97 clippy flags a useless_borrows_in_formatting lint in
interface/app.rs; remove the redundant references so `cargo clippy -D
warnings` (the CI gate, which tracks latest stable) passes. Also document in
CLAUDE.md that clippy must be run exactly as CI does before finishing any work.
Pin the toolchain in rust-toolchain.toml (channel 1.97.1 + rustfmt/clippy)
so local and CI builds are coherent, and bump every dependency to its latest
release via `cargo upgrade --incompatible`.

The RustCrypto bumps (hmac 0.13, sha1/sha2 0.11, aes-gcm 0.11,
chacha20poly1305 0.11, scrypt 0.12, md-5 0.11) carry breaking API changes,
migrated here:

- generic-array is replaced by hybrid-array; build Key/Nonce values with
  new_from_slice / TryFrom / From instead of GenericArray::{from,clone_from}_slice.
- hmac 0.13 collapses the HMAC generic bounds to a single `D: EagerHash`,
  removing the CoreProxy/BlockSizeUser/IsLess<U256> machinery in the HOTP and
  Yandex generators; hotp_hash now returns an owned Vec<u8>.
- scrypt 0.12 Params::new drops the output-length argument.

The on-disk formats are unchanged (same Argon2id + XChaCha20Poly1305 envelope
and AES-GCM Aegis handling); verified a database written by the previous
crypto stack still decrypts and generates correct codes.
@replydev
replydev merged commit e39ae83 into main Jul 22, 2026
9 checks passed
@replydev
replydev deleted the fix/import-from-google-authenticator branch July 22, 2026 21:34
cotp-release-bot Bot pushed a commit that referenced this pull request Jul 22, 2026
# [1.10.0](v1.9.10...v1.10.0) (2026-07-22)

### Features

* **import:** import Google Authenticator via otpauth-migration URIs ([e82ac01](e82ac01))
* **import:** import Google Authenticator via otpauth-migration URIs ([#674](#674)) ([e39ae83](e39ae83))
@cotp-release-bot

Copy link
Copy Markdown

🎉 This PR is included in version 1.10.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant