mirror of
https://github.com/photoprism/photoprism.git
synced 2026-07-17 16:49:04 +00:00
Auth: Refine passcode recovery code comparison
This commit is contained in:
parent
08945d25d7
commit
71a82a9ac9
2 changed files with 19 additions and 2 deletions
|
|
@ -2,6 +2,7 @@ package entity
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
|
|
@ -254,8 +255,9 @@ func (m *Passcode) Valid(code string) (valid bool, recovery bool, err error) {
|
|||
return false, false, authn.ErrInvalidPasscodeKey
|
||||
}
|
||||
|
||||
// Check if recovery code has been used.
|
||||
if m.RecoveryCode == code {
|
||||
// Check if recovery code has been used, comparing in constant time so the
|
||||
// stored secret does not leak through response timing.
|
||||
if m.RecoveryCode != "" && subtle.ConstantTimeCompare([]byte(m.RecoveryCode), []byte(code)) == 1 {
|
||||
return true, true, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -381,6 +381,21 @@ func TestPasscode_Verify(t *testing.T) {
|
|||
assert.True(t, recoveryCode)
|
||||
assert.Nil(t, m.VerifiedAt)
|
||||
})
|
||||
t.Run("EmptyRecoveryCodeNeverMatches", func(t *testing.T) {
|
||||
m := &Passcode{
|
||||
UID: "uqxc08w3d0ej2283",
|
||||
KeyURL: "otpauth://totp/Example:alice",
|
||||
RecoveryCode: "",
|
||||
}
|
||||
// An unset recovery code must not match any submitted code (constant-time
|
||||
// compare returns 0 on a length mismatch, but the guard makes intent explicit).
|
||||
valid, recoveryCode, err := m.Valid("123456")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.False(t, valid)
|
||||
assert.False(t, recoveryCode)
|
||||
})
|
||||
t.Run("ErrPasscodeRequired", func(t *testing.T) {
|
||||
m := &Passcode{
|
||||
UID: "uqxc08w3d0ej2283",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue