diff --git a/internal/action/otp.go b/internal/action/otp.go index b247789920..fff994450b 100644 --- a/internal/action/otp.go +++ b/internal/action/otp.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "strconv" - "strings" "time" "github.com/gopasspw/gopass/internal/action/exit" @@ -16,9 +15,6 @@ import ( "github.com/gopasspw/gopass/pkg/debug" "github.com/gopasspw/gopass/pkg/otp" "github.com/gopasspw/gopass/pkg/termio" - "github.com/kbinani/screenshot" - "github.com/makiuchi-d/gozxing" - "github.com/makiuchi-d/gozxing/qrcode" "github.com/mattn/go-tty" "github.com/pquerna/otp/hotp" "github.com/pquerna/otp/totp" @@ -39,41 +35,11 @@ func (s *Action) OTP(c *cli.Context) error { snip := c.Bool("snip") if snip { - var qr string - for i := 0; i < screenshot.NumActiveDisplays(); i++ { - out.Noticef(ctx, "Scanning screen n°%d", i) - - img, err := screenshot.CaptureDisplay(i) - if err != nil { - return err - } - - out.OKf(ctx, "Area scanned on screen n°%d: %v", i, img.Bounds()) - - bmp, err := gozxing.NewBinaryBitmapFromImage(img) - if err != nil { - return err - } - - // decode image - qrReader := qrcode.NewQRCodeReader() - result, err := qrReader.Decode(bmp, nil) - if err != nil { - out.Warningf(ctx, "No QR code found while parsing screen n°%d.", i) - continue - } - out.Noticef(ctx, "Found a qrcode, checking.") - if strings.HasPrefix(result.GetText(), "otpauth://") { - qr = result.GetText() - out.OKf(ctx, "Found an otpauth:// QR code on screen n°%d (%v)", i, img.Bounds()) - break - } - out.Warningf(ctx, "Not an otpauth:// QR code, please make sure to only have your OTP qrcode displayed.") + qr, err := otp.ParseScreen(ctx) + if err != nil || len(qr) == 0 { + return err } - if len(qr) == 0 { - return nil - } choice, err := termio.AskForBool(ctx, "(Over)writing otpauth URL in key 'otpauth'?", true) if err != nil || !choice { return err diff --git a/pkg/otp/otp.go b/pkg/otp/otp.go index f293424963..965bb9d3a9 100644 --- a/pkg/otp/otp.go +++ b/pkg/otp/otp.go @@ -2,7 +2,11 @@ package otp import ( "bytes" + "context" "fmt" + "github.com/kbinani/screenshot" + "github.com/makiuchi-d/gozxing" + "github.com/makiuchi-d/gozxing/qrcode" "image/png" "os" "strings" @@ -87,6 +91,44 @@ func parseOTP(typ string, secKey string) (*otp.Key, error) { return key, nil } +// ParseScreen will attempt to parse all available screen and will look for otpauth QR codes. It returns the first one +// it has found. +func ParseScreen(ctx context.Context) (string, error) { + var qr string + for i := 0; i < screenshot.NumActiveDisplays(); i++ { + out.Noticef(ctx, "Scanning screen n°%d", i) + + img, err := screenshot.CaptureDisplay(i) + if err != nil { + return "", err + } + + out.OKf(ctx, "Area scanned on screen n°%d: %v", i, img.Bounds()) + bmp, err := gozxing.NewBinaryBitmapFromImage(img) + if err != nil { + return "", err + } + qrReader := qrcode.NewQRCodeReader() + result, err := qrReader.Decode(bmp, nil) + if err != nil { + out.Warningf(ctx, "No QR code found while parsing screen n°%d.", i) + + continue + } + + out.Noticef(ctx, "Found a qrcode, checking.") + if strings.HasPrefix(result.GetText(), "otpauth://") { + qr = result.GetText() + out.OKf(ctx, "Found an otpauth:// QR code on screen n°%d (%v)", i, img.Bounds()) + + break + } + out.Warningf(ctx, "Not an otpauth:// QR code, please make sure to only have your OTP qrcode displayed.") + } + + return qr, nil +} + // WriteQRFile writes the given OTP code as a QR image to disk. func WriteQRFile(key *otp.Key, file string) error { // Convert TOTP key into a QR code encoded as a PNG image.