Description
🔍 Problem Description:
I am encountering a problem where I cannot recover the QR code after restarting my application. The key.Secret() is the only information that is permanently stored. However, it seems impossible to recover the same key using only the secret.
💡 Relevant Code Snippet:
In the code section below from totp.go (lines 182-192), I initially believed that the key could be recovered only from the secret.
v := url.Values{}
if len(opts.Secret) != 0 {
v.Set("secret", b32NoPadding.EncodeToString(opts.Secret))
} else {
secret := make([]byte, opts.SecretSize)
_, err := opts.Rand.Read(secret)
if err != nil {
return nil, err
}
v.Set("secret", b32NoPadding.EncodeToString(secret))
}
However, recent tests have contradicted this assumption.
🔬 Test Results:
fmt.Println(keySecret)
key, err := totp.Generate(totp.GenerateOpts{
Issuer: issuer,
AccountName: accountName,
Secret: []byte(keySecret),
})
fmt.Println(key.Secret())
keySecret: JRKDOQRSJJBDIVCRLJJEQS22KBGQINGFQMRUGZHDGNKYINBUSRKI
key.Secret(): KKKJFUIT2RKJJUUSSCIREVMQ2SJRFEURKRKMZDES2CI5IQJFHEORSRJVJFKR22JBCEOTSLLFEU4QSVKNJEWS
🛠️ Environment:
Operating System: Windows X86@64
Golang Version: 1.21
Library version used: pqerna/otp v1.4.0
🏁 Expected Outcome:
My expectation is to get a key with the exact same secret when entering the secret.
🚫 Actual Outcome:
Because the entered secret is encoded again, another secret is created.
v.Set("secret", b32NoPadding.EncodeToString(opts.Secret))
📝 Solution:
I assume that this decision was made on purpose so I provide a wrapper that decrypts the entered secret first.
func Regenerate(opts GenerateOpts) (*otp.Key, error) {
if opts.SecretSize == 0 {
return nil, otp.ErrRegenerateMissingSecret
}
secret := make([]byte, base32.StdEncoding.DecodedLen(len(opts.Secret)))
_, err := base32.StdEncoding.Decode(secret, opts.Secret)
if err != nil {
return nil, err
}
opts.Secret = secret
return Generate(opts)
}