Skip to content

Commit

Permalink
otpauth: fix base64 decoding for migration URLs
Browse files Browse the repository at this point in the history
The encoding is standard, not URL.

Also, update the test to include two entries in a single URL, to exercise that
aspect of the encoding.
  • Loading branch information
creachadair committed Mar 8, 2024
1 parent 784e0ce commit 539e298
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 11 deletions.
10 changes: 5 additions & 5 deletions otpauth/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import (
//
// otpauth-migration://offline?data=<content>
//
// The content is a protocol buffer message encoded as base64 in URL encoding.
// Note that a single migration URL may encode multiple OTP settings; on
// success this function returns all the otpauth URLs encoded by the content.
// It will always return at least one URL, or report an error.
// The content is a protocol buffer message encoded as base64 in standard
// encoding. Note that a single migration URL may encode multiple OTP
// settings; on success this function returns all the otpauth URLs encoded by
// the content. It will always return at least one URL, or report an error.
func ParseMigrationURL(s string) ([]*URL, error) {
rest, ok := strings.CutPrefix(s, "otpauth-migration://")
if !ok {
Expand All @@ -36,7 +36,7 @@ func ParseMigrationURL(s string) ([]*URL, error) {
if err != nil {
return nil, fmt.Errorf("invalid data: %w", err)
}
bits, err := base64.URLEncoding.DecodeString(dec)
bits, err := base64.StdEncoding.DecodeString(dec)
if err != nil {
return nil, fmt.Errorf("invalid base64: %w", err)
}
Expand Down
23 changes: 17 additions & 6 deletions otpauth/otpauth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,22 +173,33 @@ func TestParseeErrors(t *testing.T) {
}

func TestParseMigrationURL(t *testing.T) {
// Test input synthesized using Google Authenticator:
// Test input synthesized using Google Authenticator. To re-generate this
// test example:
//
// - Create a new entry named "test" with the key "fuzzlebuzzlegibbledibble".
// - Export the entry as a QR code.
// - Create a new entry named "test 1" with the key "fuzzlebuzzlegibbledibble", counter-based.
// Generate 3 codes from this (advancing the counter from 0 to 3).
// - Create a new entry named "test 2" with the key "applepieispeachy", time-based.
// - Export these two entries together as a single QR code.
// - Parse the QR code to export the migration URL.
//
u, err := otpauth.ParseMigrationURL(`otpauth-migration://offline?data=Ch0KDy0zlZA0zlZDICFZBoCFZBIEdGVzdCABKAEwAhACGAEgAA%3D%3D`)
u, err := otpauth.ParseMigrationURL(`otpauth-migration://offline?data=CiEKDy0zlZA0zlZDICFZBoCFZBIGdGVzdCAxIAEoATABOAMKGgoKA96yPQREnkAI%2BBIGdGVzdCAyIAEoATACEAIYASAA`)
if err != nil {
t.Fatalf("ParseMigrationURL: unexpected error: %v", err)
}
if diff := cmp.Diff(u, []*otpauth.URL{{
Type: "TOTP",
Account: "test",
Type: "HOTP",
Account: "test 1",
RawSecret: "FUZZLEBUZZLEGIBBLEDIBBLE",
Algorithm: "SHA1",
Digits: 6,
Counter: 3,
Period: 30, // default
}, {
Type: "TOTP",
Account: "test 2",
RawSecret: "APPLEPIEISPEACHY",
Algorithm: "SHA1",
Digits: 6,
Period: 30, // default
}}); diff != "" {
t.Errorf("Parsed (-got, +want):\n%s", diff)
Expand Down

0 comments on commit 539e298

Please sign in to comment.