lockbox

password manager
Log | Files | Refs | README | LICENSE

commit b98485ce8bb10045f22beb251344a94943ff4d26
parent 36a46c18348c5addaf5c312fa8df4b9859187bba
Author: Sean Enck <sean@ttypty.com>
Date:   Mon, 24 Nov 2025 12:48:12 -0500

switch totp providers

Diffstat:
Mcmd/lb/tests/expected.log | 2+-
Mgo.mod | 3+--
Mgo.sum | 10++--------
Minternal/app/insert_test.go | 2+-
Minternal/app/totp/core.go | 35++++++++++++++++++++---------------
5 files changed, 25 insertions(+), 27 deletions(-)

diff --git a/cmd/lb/tests/expected.log b/cmd/lb/tests/expected.log @@ -4,7 +4,7 @@ setting up tests 'test3' is not an allowed field name '' is not an allowed field name 'still' is not an allowed field name -Decoding of secret as base32 failed. +illegal base32 data at input byte 16 password can NOT be multi-line testing5 testing5 diff --git a/go.mod b/go.mod @@ -4,12 +4,11 @@ go 1.24.4 require ( github.com/BurntSushi/toml v1.5.0 - github.com/pquerna/otp v1.5.0 + github.com/ja7ad/otp v1.3.3 github.com/tobischo/gokeepasslib/v3 v3.6.1 ) require ( - github.com/boombuler/barcode v1.1.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/tobischo/argon2 v0.1.0 // indirect golang.org/x/crypto v0.45.0 // indirect diff --git a/go.sum b/go.sum @@ -1,19 +1,13 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.1.0 h1:ChaYjBR63fr4LFyGn8E8nt7dBSt3MiU3zMOZqFvVkHo= -github.com/boombuler/barcode v1.1.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/ja7ad/otp v1.3.3 h1:C/IbBXLT7BaShm7eicuAtmQaum3+0s2ms3GXCoiivls= +github.com/ja7ad/otp v1.3.3/go.mod h1:6Da7VUaUpJmzaRz5IZ6v9Yei62eaepYoGCb6jsMDkUs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= -github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tobischo/argon2 v0.1.0 h1:mwAx/9DK/4rP0xzNifb/XMAf43dU3eG1B3aeF88qu4Y= diff --git a/internal/app/insert_test.go b/internal/app/insert_test.go @@ -183,7 +183,7 @@ func TestInsertTOTP(t *testing.T) { } m.command.buf = bytes.Buffer{} m.command.args = []string{"test/test2/test1/otp"} - if err := app.Insert(m); err == nil || err.Error() != "Decoding of secret as base32 failed." { + if err := app.Insert(m); err == nil || err.Error() != "illegal base32 data at input byte 1" { t.Errorf("invalid error: %v", err) } store.SetBool("LOCKBOX_TOTP_CHECK_ON_INSERT", false) diff --git a/internal/app/totp/core.go b/internal/app/totp/core.go @@ -4,10 +4,10 @@ package totp import ( "fmt" "io" + "net/url" "time" - coreotp "github.com/pquerna/otp" - otp "github.com/pquerna/otp/totp" + "github.com/ja7ad/otp" "github.com/enckse/lockbox/internal/config" ) @@ -15,23 +15,23 @@ import ( type ( // Generator is used to generate TOTP codes Generator struct { - key *coreotp.Key secret string - opts otp.ValidateOpts + opts *otp.Param + url *url.URL } ) // Code will generate a new code for the specified TOTP object func (g Generator) Code() (string, error) { - return otp.GenerateCodeCustom(g.secret, time.Now(), g.opts) + return otp.GenerateTOTP(g.secret, time.Now(), g.opts) } // Print will print information about the generator to the writer func (g Generator) Print(w io.Writer, details bool) { if details { - fmt.Fprintf(w, "url: %s\n", g.key.URL()) + fmt.Fprintf(w, "url: %s\n", g.url) fmt.Fprintf(w, "seed: %s\n", g.secret) - fmt.Fprintf(w, "digits: %s\n", g.opts.Digits) + fmt.Fprintf(w, "digits: %d\n", g.opts.Digits) fmt.Fprintf(w, "algorithm: %s\n", g.opts.Algorithm) fmt.Fprintf(w, "period: %d\n", g.opts.Period) return @@ -40,17 +40,22 @@ func (g Generator) Print(w io.Writer, details bool) { } // New will create a new generator -func New(code string) (Generator, error) { - k, err := coreotp.NewKeyFromURL(config.EnvTOTPFormat.Get(code)) +func New(input string) (Generator, error) { + u, err := url.Parse(config.EnvTOTPFormat.Get(input)) + if err != nil { + return Generator{}, err + } + + obj, err := otp.ParseOTPAuthURL(u) if err != nil { return Generator{}, err } wrapper := Generator{} - wrapper.secret = k.Secret() - wrapper.opts = otp.ValidateOpts{} - wrapper.opts.Digits = k.Digits() - wrapper.opts.Algorithm = k.Algorithm() - wrapper.opts.Period = uint(k.Period()) - wrapper.key = k + wrapper.secret = obj.Secret + wrapper.opts = &otp.Param{} + wrapper.opts.Algorithm = obj.Algorithm + wrapper.opts.Digits = obj.Digits + wrapper.opts.Period = obj.Period + wrapper.url = u return wrapper, nil }