otpmigration

package
v0.652.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 9, 2026 License: AGPL-3.0 Imports: 7 Imported by: 0

Documentation

Overview

Package otpmigration decodes the Google Authenticator "Export accounts" payload — the otpauth-migration://offline?data=… URI (and the bare base64 behind it) — into the list of 2FA accounts it carries: issuer, account name, secret, algorithm, digit count, OTP type, and HOTP counter. A single export QR packs ALL of a user's seeds, so decoding one is a high-value post-exploitation / device-forensics step: it bulk-recovers every TOTP/HOTP secret, each ready to feed into totp_generate for the live codes. Pure offline transform; no network or device.

Wrap-vs-native judgement

Native. The payload is a small protobuf (MigrationPayload) whose schema is public and stable. We parse the wire format directly with google.golang.org/protobuf/encoding/protowire — already an indirect dependency of the module, used here only as a low-level varint/bytes reader, with NO generated .pb.go and NO new go.mod entry. The base64 + base32 + URI assembly is stdlib. Nothing is wrapped or shelled out.

Schema (reverse-engineered, stable since the 2020 export feature)

message MigrationPayload {
  message OtpParameters {
    bytes  secret    = 1;  // raw secret bytes (base32-encode for display)
    string name      = 2;  // account / label
    string issuer    = 3;
    Algorithm algorithm = 4;  // 0 unspec,1 SHA1,2 SHA256,3 SHA512,4 MD5
    DigitCount digits   = 5;  // 0 unspec,1 SIX,2 EIGHT
    OtpType type        = 6;  // 0 unspec,1 HOTP,2 TOTP
    int64  counter   = 7;  // HOTP only
  }
  repeated OtpParameters otp_parameters = 1;
  int32 version     = 2;
  int32 batch_size  = 3;
  int32 batch_index = 4;
  int32 batch_id    = 5;
}

Verifiable / no confidently-wrong output

Anchored to the canonical migration example used across the ecosystem — data CjEKCkhlbGxvId6tvu8SGFRlc3Q6YWxpY2VAZ29vZ2xlLmNvbRoHRXhhbXBsZSABKAEwAg== → issuer "Example", secret JBSWY3DPEHPK3PXP (base32 of the bytes 48656C6C6F21DEADBEEF, "Hello!"+0xDEADBEEF), SHA1 / 6 digits / TOTP. An unknown enum value is surfaced raw ("UNSPECIFIED(n)") rather than guessed; a truncated or non-protobuf payload is rejected. The secret is the correctness-critical field, and it is cross-checked by that independent base32 computation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Account

type Account struct {
	Issuer     string `json:"issuer,omitempty"`
	Name       string `json:"name"`
	Secret     string `json:"secret"` // base32, no padding (the otpauth form)
	Algorithm  string `json:"algorithm"`
	Digits     int    `json:"digits"`
	Type       string `json:"type"`              // totp / hotp
	Counter    int64  `json:"counter,omitempty"` // HOTP only
	OtpauthURI string `json:"otpauth_uri"`
}

Account is one decoded OTP entry, with a reconstructed otpauth:// URI ready to paste into totp_generate.

type Result

type Result struct {
	Version    int       `json:"version,omitempty"`
	BatchSize  int       `json:"batch_size,omitempty"`
	BatchIndex int       `json:"batch_index,omitempty"`
	BatchID    int       `json:"batch_id,omitempty"`
	Count      int       `json:"count"`
	Accounts   []Account `json:"accounts"`
}

Result is the decoded migration payload.

func Decode

func Decode(input string) (*Result, error)

Decode accepts a full otpauth-migration:// URI or the bare (URL- or plain) base64 data payload and returns the decoded accounts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL