From 5198a019405af8d6fff89fcaf56274be93cdc8de Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Wed, 10 May 2017 07:36:50 -0600 Subject: [PATCH 1/8] otp --- browserpass.go | 30 +++++++++++++++++++++++++++--- browserpass_test.go | 26 ++++++++++++++++++++++++++ chrome/script.browserify.js | 3 +++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/browserpass.go b/browserpass.go index 34a3532..4c2725e 100644 --- a/browserpass.go +++ b/browserpass.go @@ -13,12 +13,15 @@ import ( "strings" "github.com/dannyvankooten/browserpass/pass" + "github.com/gokyle/twofactor" ) // Login represents a single pass login. type Login struct { - Username string `json:"u"` - Password string `json:"p"` + Username string `json:"u"` + Password string `json:"p"` + OTP string `json:"digits"` + otpSecret string } var endianness = binary.LittleEndian @@ -126,7 +129,8 @@ func readLoginGPG(r io.Reader) (*Login, error) { if err != nil { return nil, err } - rc.Close() + + defer rc.Close() if err := cmd.Wait(); err != nil { return nil, errors.New(err.Error() + "\n" + errbuf.String()) @@ -134,6 +138,22 @@ func readLoginGPG(r io.Reader) (*Login, error) { return login, nil } +func parseTotp(str string, l *Login) error { + re := regexp.MustCompile("(?i)^(totp|otp):") + replaced := re.ReplaceAllString(str, "") + if len(replaced) != len(str) { + secret := strings.TrimSpace(replaced) + o, err := twofactor.NewGoogleTOTP(secret) + if err != nil { + return err + } + l.OTP = o.OTP() + l.otpSecret = secret + } + + return nil +} + // parseLogin parses a login and a password from a decrypted password file. func parseLogin(r io.Reader) (*Login, error) { login := new(Login) @@ -148,6 +168,10 @@ func parseLogin(r io.Reader) (*Login, error) { re := regexp.MustCompile("(?i)^(login|username|user):") for scanner.Scan() { line := scanner.Text() + err := parseTotp(line, login) + if err != nil { + return nil, err + } replaced := re.ReplaceAllString(line, "") if len(replaced) != len(line) { login.Username = strings.TrimSpace(replaced) diff --git a/browserpass_test.go b/browserpass_test.go index d34a76f..4df4a73 100644 --- a/browserpass_test.go +++ b/browserpass_test.go @@ -3,6 +3,8 @@ package browserpass import ( "strings" "testing" + + "github.com/gokyle/twofactor" ) func TestParseLogin(t *testing.T) { @@ -21,6 +23,30 @@ func TestParseLogin(t *testing.T) { } } +func TestOtp(t *testing.T) { + r := strings.NewReader("password\n\nfoo\nlogin: bar\nTOTP: GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ") + + login, err := parseLogin(r) + if err != nil { + t.Fatal(err) + } + + if login.otpSecret != "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ" { + t.Errorf("otpSecret is '%s', expected '%s'", login.otpSecret, "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ") + } + + o, err := twofactor.NewGoogleTOTP(login.otpSecret) + if err != nil { + t.Error(err) + } + + otp := o.OTP() + + if login.OTP != otp { + t.Errorf("OTP is %s, expected %s", login.OTP, otp) + } +} + func TestGuessUsername(t *testing.T) { tests := map[string]string{ "foo": "", diff --git a/chrome/script.browserify.js b/chrome/script.browserify.js index eab3245..e7a3d40 100644 --- a/chrome/script.browserify.js +++ b/chrome/script.browserify.js @@ -137,6 +137,9 @@ function getLoginData() { searching = false; fillLoginForm(response); + if (response.digits !== "") { + alert(response.digits) + } }); } From 43ceae3ca46faf86c68f620f019dacd880000bde Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sun, 17 Sep 2017 09:48:20 -0600 Subject: [PATCH 2/8] use pass-otp format --- browserpass.go | 20 ++++++++++---------- browserpass_test.go | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/browserpass.go b/browserpass.go index 4c2725e..3b7178c 100644 --- a/browserpass.go +++ b/browserpass.go @@ -18,10 +18,10 @@ import ( // Login represents a single pass login. type Login struct { - Username string `json:"u"` - Password string `json:"p"` - OTP string `json:"digits"` - otpSecret string + Username string `json:"u"` + Password string `json:"p"` + OTP string `json:"digits"` + otpLabel string `json:"label"` } var endianness = binary.LittleEndian @@ -139,16 +139,16 @@ func readLoginGPG(r io.Reader) (*Login, error) { } func parseTotp(str string, l *Login) error { - re := regexp.MustCompile("(?i)^(totp|otp):") - replaced := re.ReplaceAllString(str, "") - if len(replaced) != len(str) { - secret := strings.TrimSpace(replaced) - o, err := twofactor.NewGoogleTOTP(secret) + re := regexp.MustCompile("^otpauth.*$") + url := re.FindString(str) + if url != "" { + secret := strings.TrimSpace(url) + o, label, err := twofactor.FromURL(secret) if err != nil { return err } l.OTP = o.OTP() - l.otpSecret = secret + l.otpLabel = label } return nil diff --git a/browserpass_test.go b/browserpass_test.go index 4df4a73..62a0dd6 100644 --- a/browserpass_test.go +++ b/browserpass_test.go @@ -24,18 +24,18 @@ func TestParseLogin(t *testing.T) { } func TestOtp(t *testing.T) { - r := strings.NewReader("password\n\nfoo\nlogin: bar\nTOTP: GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ") + r := strings.NewReader("password\n\nfoo\nlogin: bar\notpauth://totp/totp-secret?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=test") login, err := parseLogin(r) if err != nil { t.Fatal(err) } - if login.otpSecret != "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ" { - t.Errorf("otpSecret is '%s', expected '%s'", login.otpSecret, "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ") + if login.otpLabel != "totp-secret" { + t.Errorf("otpLabel is '%s', expected '%s'", login.otpLabel, "totp-secret") } - o, err := twofactor.NewGoogleTOTP(login.otpSecret) + o, err := twofactor.NewGoogleTOTP("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ") if err != nil { t.Error(err) } From 095e47621fbcf8843aadb25b00fcd2b0d0d009f2 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sun, 17 Sep 2017 14:41:05 -0600 Subject: [PATCH 3/8] don't return err when we have more lines to iterate over --- browserpass.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/browserpass.go b/browserpass.go index 7e0eed2..304a26d 100644 --- a/browserpass.go +++ b/browserpass.go @@ -142,8 +142,7 @@ func parseTotp(str string, l *Login) error { re := regexp.MustCompile("^otpauth.*$") url := re.FindString(str) if url != "" { - secret := strings.TrimSpace(url) - o, label, err := twofactor.FromURL(secret) + o, label, err := twofactor.FromURL(url) if err != nil { return err } @@ -168,10 +167,7 @@ func parseLogin(r io.Reader) (*Login, error) { re := regexp.MustCompile("(?i)^(login|username|user):") for scanner.Scan() { line := scanner.Text() - err := parseTotp(line, login) - if err != nil { - return nil, err - } + parseTotp(line, login) replaced := re.ReplaceAllString(line, "") if len(replaced) != len(line) { login.Username = strings.TrimSpace(replaced) From a37cb8fb5024162c016b799d26c1e72faa86bf82 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sun, 17 Sep 2017 14:41:19 -0600 Subject: [PATCH 4/8] alert otp if we have it --- chrome/inject.browserify.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/chrome/inject.browserify.js b/chrome/inject.browserify.js index 504b03e..22dc048 100644 --- a/chrome/inject.browserify.js +++ b/chrome/inject.browserify.js @@ -80,6 +80,10 @@ update(field(PASSWORD_FIELDS), login.p); update(field(USERNAME_FIELDS), login.u); + if (login.digits !== "") { + alert(login.digits); + } + var password_inputs = queryAllVisible(form(), PASSWORD_FIELDS); if (autoSubmit == "false" || password_inputs.length > 1) { password_inputs[1].select(); From 51bfdc9a9198748eb844e90cf3f9e1cacea7d425 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sun, 17 Sep 2017 16:11:12 -0600 Subject: [PATCH 5/8] Upper our secrets before we get the OTP This can be undone once https://github.com/gokyle/twofactor/pull/7 is merged. --- browserpass.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/browserpass.go b/browserpass.go index 304a26d..4b1e766 100644 --- a/browserpass.go +++ b/browserpass.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "io" + "net/url" "os/exec" "path/filepath" "regexp" @@ -140,9 +141,20 @@ func readLoginGPG(r io.Reader) (*Login, error) { func parseTotp(str string, l *Login) error { re := regexp.MustCompile("^otpauth.*$") - url := re.FindString(str) - if url != "" { - o, label, err := twofactor.FromURL(url) + ourl := re.FindString(str) + + if ourl != "" { + u, err := url.Parse(ourl) + + if u.Scheme != "otpauth" { + return nil + } + + v := u.Query() + v.Set("secret", strings.ToUpper(v.Get("secret"))) + ourl = u.String() + + o, label, err := twofactor.FromURL(ourl) if err != nil { return err } From fdfd13bcf1e14041d35676ce4c02d44e69114b85 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sun, 17 Sep 2017 16:21:18 -0600 Subject: [PATCH 6/8] less strict check for digits --- chrome/inject.browserify.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chrome/inject.browserify.js b/chrome/inject.browserify.js index 22dc048..1b65abf 100644 --- a/chrome/inject.browserify.js +++ b/chrome/inject.browserify.js @@ -80,7 +80,7 @@ update(field(PASSWORD_FIELDS), login.p); update(field(USERNAME_FIELDS), login.u); - if (login.digits !== "") { + if (login.digits != "") { alert(login.digits); } From a2bf29c5dca04d25767edea36cb8846a9bdca9e3 Mon Sep 17 00:00:00 2001 From: Aaron Bieber Date: Sun, 17 Sep 2017 16:25:02 -0600 Subject: [PATCH 7/8] vendor with dep --- Gopkg.lock | 27 + Gopkg.toml | 30 + .../github.com/gokyle/twofactor/.travis.yml | 7 + vendor/github.com/gokyle/twofactor/LICENSE | 19 + vendor/github.com/gokyle/twofactor/doc.go | 5 + vendor/github.com/gokyle/twofactor/hotp.go | 101 +++ .../github.com/gokyle/twofactor/hotp_test.go | 67 ++ vendor/github.com/gokyle/twofactor/oath.go | 150 ++++ .../github.com/gokyle/twofactor/oath_test.go | 30 + vendor/github.com/gokyle/twofactor/otp.go | 86 ++ .../github.com/gokyle/twofactor/otp_test.go | 97 +++ vendor/github.com/gokyle/twofactor/totp.go | 164 ++++ .../github.com/gokyle/twofactor/totp_test.go | 55 ++ vendor/github.com/mattn/go-zglob/.travis.yml | 6 + .../mattn/go-zglob/cmd/zglob/main.go | 22 + .../mattn/go-zglob/fastwalk/fastwalk_test.go | 171 ++++ .../github.com/mattn/go-zglob/zglob_test.go | 138 +++ vendor/rsc.io/qr/LICENSE | 27 + vendor/rsc.io/qr/README.md | 3 + vendor/rsc.io/qr/coding/gen.go | 149 ++++ vendor/rsc.io/qr/coding/qr.go | 815 ++++++++++++++++++ vendor/rsc.io/qr/coding/qr_test.go | 133 +++ vendor/rsc.io/qr/gf256/blog_test.go | 85 ++ vendor/rsc.io/qr/gf256/gf256.go | 241 ++++++ vendor/rsc.io/qr/gf256/gf256_test.go | 194 +++++ vendor/rsc.io/qr/libqrencode/qrencode.go | 149 ++++ vendor/rsc.io/qr/png.go | 400 +++++++++ vendor/rsc.io/qr/png_test.go | 73 ++ vendor/rsc.io/qr/qr.go | 116 +++ vendor/vendor.json | 19 - 30 files changed, 3560 insertions(+), 19 deletions(-) create mode 100644 Gopkg.lock create mode 100644 Gopkg.toml create mode 100644 vendor/github.com/gokyle/twofactor/.travis.yml create mode 100644 vendor/github.com/gokyle/twofactor/LICENSE create mode 100644 vendor/github.com/gokyle/twofactor/doc.go create mode 100644 vendor/github.com/gokyle/twofactor/hotp.go create mode 100644 vendor/github.com/gokyle/twofactor/hotp_test.go create mode 100644 vendor/github.com/gokyle/twofactor/oath.go create mode 100644 vendor/github.com/gokyle/twofactor/oath_test.go create mode 100644 vendor/github.com/gokyle/twofactor/otp.go create mode 100644 vendor/github.com/gokyle/twofactor/otp_test.go create mode 100644 vendor/github.com/gokyle/twofactor/totp.go create mode 100644 vendor/github.com/gokyle/twofactor/totp_test.go create mode 100644 vendor/github.com/mattn/go-zglob/.travis.yml create mode 100644 vendor/github.com/mattn/go-zglob/cmd/zglob/main.go create mode 100644 vendor/github.com/mattn/go-zglob/fastwalk/fastwalk_test.go create mode 100644 vendor/github.com/mattn/go-zglob/zglob_test.go create mode 100644 vendor/rsc.io/qr/LICENSE create mode 100644 vendor/rsc.io/qr/README.md create mode 100644 vendor/rsc.io/qr/coding/gen.go create mode 100644 vendor/rsc.io/qr/coding/qr.go create mode 100644 vendor/rsc.io/qr/coding/qr_test.go create mode 100644 vendor/rsc.io/qr/gf256/blog_test.go create mode 100644 vendor/rsc.io/qr/gf256/gf256.go create mode 100644 vendor/rsc.io/qr/gf256/gf256_test.go create mode 100644 vendor/rsc.io/qr/libqrencode/qrencode.go create mode 100644 vendor/rsc.io/qr/png.go create mode 100644 vendor/rsc.io/qr/png_test.go create mode 100644 vendor/rsc.io/qr/qr.go delete mode 100644 vendor/vendor.json diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000..517f119 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,27 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/gokyle/twofactor" + packages = ["."] + revision = "5d57d844d43d0e2d9d37c28f9a6e3a14963b284b" + +[[projects]] + branch = "master" + name = "github.com/mattn/go-zglob" + packages = [".","fastwalk"] + revision = "95345c4e1c0ebc9d16a3284177f09360f4d20fab" + +[[projects]] + branch = "master" + name = "rsc.io/qr" + packages = [".","coding","gf256"] + revision = "48b2ede4844e13f1a2b7ce4d2529c9af7e359fc5" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "f962ba80d83a10086a4fbc92f1ba5ec0ccf46703e0d8a0e2f291d6a156bb8bac" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..728508e --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,30 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + branch = "master" + name = "github.com/gokyle/twofactor" + +[[constraint]] + branch = "master" + name = "github.com/mattn/go-zglob" diff --git a/vendor/github.com/gokyle/twofactor/.travis.yml b/vendor/github.com/gokyle/twofactor/.travis.yml new file mode 100644 index 0000000..69d2f6d --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.x + - 1.6 + - 1.7.x + - master diff --git a/vendor/github.com/gokyle/twofactor/LICENSE b/vendor/github.com/gokyle/twofactor/LICENSE new file mode 100644 index 0000000..027cde0 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 Kyle Isom + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/gokyle/twofactor/doc.go b/vendor/github.com/gokyle/twofactor/doc.go new file mode 100644 index 0000000..6f1eef8 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/doc.go @@ -0,0 +1,5 @@ +// twofactor implements two-factor authentication. +// +// Currently supported are RFC 4226 HOTP one-time passwords and +// RFC 6238 TOTP SHA-1 one-time passwords. +package twofactor diff --git a/vendor/github.com/gokyle/twofactor/hotp.go b/vendor/github.com/gokyle/twofactor/hotp.go new file mode 100644 index 0000000..1232e98 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/hotp.go @@ -0,0 +1,101 @@ +package twofactor + +import ( + "crypto" + "crypto/sha1" + "encoding/base32" + "io" + "net/url" + "strconv" +) + +// HOTP represents an RFC-4226 Hash-based One Time Password instance. +type HOTP struct { + *OATH +} + +// Type returns OATH_HOTP. +func (otp *HOTP) Type() Type { + return OATH_HOTP +} + +// NewHOTP takes the key, the initial counter value, and the number +// of digits (typically 6 or 8) and returns a new HOTP instance. +func NewHOTP(key []byte, counter uint64, digits int) *HOTP { + return &HOTP{ + OATH: &OATH{ + key: key, + counter: counter, + size: digits, + hash: sha1.New, + algo: crypto.SHA1, + }, + } +} + +// OTP returns the next OTP and increments the counter. +func (otp *HOTP) OTP() string { + code := otp.OATH.OTP(otp.counter) + otp.counter++ + return code +} + +// URL returns an HOTP URL (i.e. for putting in a QR code). +func (otp *HOTP) URL(label string) string { + return otp.OATH.URL(otp.Type(), label) +} + +// SetProvider sets up the provider component of the OTP URL. +func (otp *HOTP) SetProvider(provider string) { + otp.provider = provider +} + +// GenerateGoogleHOTP generates a new HOTP instance as used by +// Google Authenticator. +func GenerateGoogleHOTP() *HOTP { + key := make([]byte, sha1.Size) + if _, err := io.ReadFull(PRNG, key); err != nil { + return nil + } + return NewHOTP(key, 0, 6) +} + +func hotpFromURL(u *url.URL) (*HOTP, string, error) { + label := u.Path[1:] + v := u.Query() + + secret := v.Get("secret") + if secret == "" { + return nil, "", ErrInvalidURL + } + + var digits = 6 + if sdigit := v.Get("digits"); sdigit != "" { + tmpDigits, err := strconv.ParseInt(sdigit, 10, 8) + if err != nil { + return nil, "", err + } + digits = int(tmpDigits) + } + + var counter uint64 = 0 + if scounter := v.Get("counter"); scounter != "" { + var err error + counter, err = strconv.ParseUint(scounter, 10, 64) + if err != nil { + return nil, "", err + } + } + + key, err := base32.StdEncoding.DecodeString(secret) + if err != nil { + return nil, "", err + } + otp := NewHOTP(key, counter, digits) + return otp, label, nil +} + +// QR generates a new QR code for the HOTP. +func (otp *HOTP) QR(label string) ([]byte, error) { + return otp.OATH.QR(otp.Type(), label) +} diff --git a/vendor/github.com/gokyle/twofactor/hotp_test.go b/vendor/github.com/gokyle/twofactor/hotp_test.go new file mode 100644 index 0000000..09e0f6a --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/hotp_test.go @@ -0,0 +1,67 @@ +package twofactor + +import ( + "fmt" + "testing" +) + +var testKey = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + +func newZeroHOTP() *HOTP { + return NewHOTP(testKey, 0, 6) +} + +var rfcHotpKey = []byte("12345678901234567890") +var rfcHotpExpected = []string{ + "755224", + "287082", + "359152", + "969429", + "338314", + "254676", + "287922", + "162583", + "399871", + "520489", +} + +// This test runs through the test cases presented in the RFC, and +// ensures that this implementation is in compliance. +func TestHotpRFC(t *testing.T) { + otp := NewHOTP(rfcHotpKey, 0, 6) + for i := 0; i < len(rfcHotpExpected); i++ { + if otp.Counter() != uint64(i) { + fmt.Printf("twofactor: invalid counter (should be %d, is %d", + i, otp.Counter()) + t.FailNow() + } + code := otp.OTP() + if code == "" { + fmt.Printf("twofactor: failed to produce an OTP\n") + t.FailNow() + } else if code != rfcHotpExpected[i] { + fmt.Printf("twofactor: invalid OTP\n") + fmt.Printf("\tExpected: %s\n", rfcHotpExpected[i]) + fmt.Printf("\t Actual: %s\n", code) + fmt.Printf("\t Counter: %d\n", otp.counter) + t.FailNow() + } + } +} + +// This test uses a different key than the test cases in the RFC, +// but runs through the same test cases to ensure that they fail as +// expected. +func TestHotpBadRFC(t *testing.T) { + otp := NewHOTP(testKey, 0, 6) + for i := 0; i < len(rfcHotpExpected); i++ { + code := otp.OTP() + if code == "" { + fmt.Printf("twofactor: failed to produce an OTP\n") + t.FailNow() + } else if code == rfcHotpExpected[i] { + fmt.Printf("twofactor: should not have received a valid OTP\n") + t.FailNow() + } + } +} diff --git a/vendor/github.com/gokyle/twofactor/oath.go b/vendor/github.com/gokyle/twofactor/oath.go new file mode 100644 index 0000000..a6d9233 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/oath.go @@ -0,0 +1,150 @@ +package twofactor + +import ( + "crypto" + "crypto/hmac" + "encoding/base32" + "encoding/binary" + "fmt" + "hash" + "net/url" + + "rsc.io/qr" +) + +const defaultSize = 6 + +// OATH provides a baseline structure for the two OATH algorithms. +type OATH struct { + key []byte + counter uint64 + size int + hash func() hash.Hash + algo crypto.Hash + provider string +} + +// Size returns the output size (in characters) of the password. +func (o OATH) Size() int { + return o.size +} + +// Counter returns the OATH token's counter. +func (o OATH) Counter() uint64 { + return o.counter +} + +// SetCounter updates the OATH token's counter to a new value. +func (o OATH) SetCounter(counter uint64) { + o.counter = counter +} + +// Key returns the token's secret key. +func (o OATH) Key() []byte { + return o.key[:] +} + +// Hash returns the token's hash function. +func (o OATH) Hash() func() hash.Hash { + return o.hash +} + +// URL constructs a URL appropriate for the token (i.e. for use in a +// QR code). +func (o OATH) URL(t Type, label string) string { + secret := base32.StdEncoding.EncodeToString(o.key) + u := url.URL{} + v := url.Values{} + u.Scheme = "otpauth" + switch t { + case OATH_HOTP: + u.Host = "hotp" + case OATH_TOTP: + u.Host = "totp" + } + u.Path = label + v.Add("secret", secret) + if o.Counter() != 0 && t == OATH_HOTP { + v.Add("counter", fmt.Sprintf("%d", o.Counter())) + } + if o.Size() != defaultSize { + v.Add("digits", fmt.Sprintf("%d", o.Size())) + } + + switch { + case o.algo == crypto.SHA256: + v.Add("algorithm", "SHA256") + case o.algo == crypto.SHA512: + v.Add("algorithm", "SHA512") + } + + if o.provider != "" { + v.Add("provider", o.provider) + } + + u.RawQuery = v.Encode() + return u.String() + +} + +var digits = []int64{ + 0: 1, + 1: 10, + 2: 100, + 3: 1000, + 4: 10000, + 5: 100000, + 6: 1000000, + 7: 10000000, + 8: 100000000, + 9: 1000000000, + 10: 10000000000, +} + +// The top-level type should provide a counter; for example, HOTP +// will provide the counter directly while TOTP will provide the +// time-stepped counter. +func (o OATH) OTP(counter uint64) string { + var ctr [8]byte + binary.BigEndian.PutUint64(ctr[:], counter) + + var mod int64 = 1 + if len(digits) > o.size { + for i := 1; i <= o.size; i++ { + mod *= 10 + } + } else { + mod = digits[o.size] + } + + h := hmac.New(o.hash, o.key) + h.Write(ctr[:]) + dt := truncate(h.Sum(nil)) % mod + fmtStr := fmt.Sprintf("%%0%dd", o.size) + return fmt.Sprintf(fmtStr, dt) +} + +// truncate contains the DT function from the RFC; this is used to +// deterministically select a sequence of 4 bytes from the HMAC +// counter hash. +func truncate(in []byte) int64 { + offset := int(in[len(in)-1] & 0xF) + p := in[offset : offset+4] + var binCode int32 + binCode = int32((p[0] & 0x7f)) << 24 + binCode += int32((p[1] & 0xff)) << 16 + binCode += int32((p[2] & 0xff)) << 8 + binCode += int32((p[3] & 0xff)) + return int64(binCode) & 0x7FFFFFFF +} + +// QR generates a byte slice containing the a QR code encoded as a +// PNG with level Q error correction. +func (o OATH) QR(t Type, label string) ([]byte, error) { + u := o.URL(t, label) + code, err := qr.Encode(u, qr.Q) + if err != nil { + return nil, err + } + return code.PNG(), nil +} diff --git a/vendor/github.com/gokyle/twofactor/oath_test.go b/vendor/github.com/gokyle/twofactor/oath_test.go new file mode 100644 index 0000000..7b145b0 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/oath_test.go @@ -0,0 +1,30 @@ +package twofactor + +import ( + "fmt" + "testing" +) + +var sha1Hmac = []byte{ + 0x1f, 0x86, 0x98, 0x69, 0x0e, + 0x02, 0xca, 0x16, 0x61, 0x85, + 0x50, 0xef, 0x7f, 0x19, 0xda, + 0x8e, 0x94, 0x5b, 0x55, 0x5a, +} + +var truncExpect int64 = 0x50ef7f19 + +// This test runs through the truncation example given in the RFC. +func TestTruncate(t *testing.T) { + if result := truncate(sha1Hmac); result != truncExpect { + fmt.Printf("hotp: expected truncate -> %d, saw %d\n", + truncExpect, result) + t.FailNow() + } + + sha1Hmac[19]++ + if result := truncate(sha1Hmac); result == truncExpect { + fmt.Println("hotp: expected truncation to fail") + t.FailNow() + } +} diff --git a/vendor/github.com/gokyle/twofactor/otp.go b/vendor/github.com/gokyle/twofactor/otp.go new file mode 100644 index 0000000..9d2faaf --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/otp.go @@ -0,0 +1,86 @@ +package twofactor + +import ( + "crypto/rand" + "errors" + "fmt" + "hash" + "net/url" +) + +type Type uint + +const ( + OATH_HOTP = iota + OATH_TOTP +) + +// PRNG is an io.Reader that provides a cryptographically secure +// random byte stream. +var PRNG = rand.Reader + +var ( + ErrInvalidURL = errors.New("twofactor: invalid URL") + ErrInvalidAlgo = errors.New("twofactor: invalid algorithm") +) + +// Type OTP represents a one-time password token -- whether a +// software taken (as in the case of Google Authenticator) or a +// hardware token (as in the case of a YubiKey). +type OTP interface { + // Returns the current counter value; the meaning of the + // returned value is algorithm-specific. + Counter() uint64 + + // Set the counter to a specific value. + SetCounter(uint64) + + // the secret key contained in the OTP + Key() []byte + + // generate a new OTP + OTP() string + + // the output size of the OTP + Size() int + + // the hash function used by the OTP + Hash() func() hash.Hash + + // Returns the type of this OTP. + Type() Type +} + +func otpString(otp OTP) string { + var typeName string + switch otp.Type() { + case OATH_HOTP: + typeName = "OATH-HOTP" + case OATH_TOTP: + typeName = "OATH-TOTP" + default: + typeName = "UNKNOWN" + } + return fmt.Sprintf("%s, %d", typeName, otp.Size()) +} + +// FromURL constructs a new OTP token from a URL string. +func FromURL(URL string) (OTP, string, error) { + u, err := url.Parse(URL) + if err != nil { + return nil, "", err + } + + if u.Scheme != "otpauth" { + return nil, "", ErrInvalidURL + } + + switch { + case u.Host == "totp": + return totpFromURL(u) + case u.Host == "hotp": + return hotpFromURL(u) + default: + return nil, "", ErrInvalidURL + } +} diff --git a/vendor/github.com/gokyle/twofactor/otp_test.go b/vendor/github.com/gokyle/twofactor/otp_test.go new file mode 100644 index 0000000..7fec809 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/otp_test.go @@ -0,0 +1,97 @@ +package twofactor + +import "fmt" +import "io" +import "testing" + +func TestHOTPString(t *testing.T) { + hotp := NewHOTP(nil, 0, 6) + hotpString := otpString(hotp) + if hotpString != "OATH-HOTP, 6" { + fmt.Println("twofactor: invalid OTP string") + t.FailNow() + } +} + +// This test generates a new OTP, outputs the URL for that OTP, +// and attempts to parse that URL. It verifies that the two OTPs +// are the same, and that they produce the same output. +func TestURL(t *testing.T) { + var ident = "testuser@foo" + otp := NewHOTP(testKey, 0, 6) + url := otp.URL("testuser@foo") + otp2, id, err := FromURL(url) + if err != nil { + fmt.Printf("hotp: failed to parse HOTP URL\n") + t.FailNow() + } else if id != ident { + fmt.Printf("hotp: bad label\n") + fmt.Printf("\texpected: %s\n", ident) + fmt.Printf("\t actual: %s\n", id) + t.FailNow() + } else if otp2.Counter() != otp.Counter() { + fmt.Printf("hotp: OTP counters aren't synced\n") + fmt.Printf("\toriginal: %d\n", otp.Counter()) + fmt.Printf("\t second: %d\n", otp2.Counter()) + t.FailNow() + } + + code1 := otp.OTP() + code2 := otp2.OTP() + if code1 != code2 { + fmt.Printf("hotp: mismatched OTPs\n") + fmt.Printf("\texpected: %s\n", code1) + fmt.Printf("\t actual: %s\n", code2) + } + + // There's not much we can do test the QR code, except to + // ensure it doesn't fail. + _, err = otp.QR(ident) + if err != nil { + fmt.Printf("hotp: failed to generate QR code PNG (%v)\n", err) + t.FailNow() + } + + // This should fail because the maximum size of an alphanumeric + // QR code with the lowest-level of error correction should + // max out at 4296 bytes. 8k may be a bit overkill... but it + // gets the job done. The value is read from the PRNG to + // increase the likelihood that the returned data is + // uncompressible. + var tooBigIdent = make([]byte, 8192) + _, err = io.ReadFull(PRNG, tooBigIdent) + if err != nil { + fmt.Printf("hotp: failed to read identity (%v)\n", err) + t.FailNow() + } else if _, err = otp.QR(string(tooBigIdent)); err == nil { + fmt.Println("hotp: QR code should fail to encode oversized URL") + t.FailNow() + } +} + +// This test attempts a variety of invalid urls against the parser +// to ensure they fail. +func TestBadURL(t *testing.T) { + var urlList = []string{ + "http://google.com", + "", + "-", + "foo", + "otpauth:/foo/bar/baz", + "://", + "otpauth://hotp/secret=bar", + "otpauth://hotp/?secret=QUJDRA&algorithm=SHA256", + "otpauth://hotp/?digits=", + "otpauth://hotp/?secret=123", + "otpauth://hotp/?secret=MFRGGZDF&digits=ABCD", + "otpauth://hotp/?secret=MFRGGZDF&counter=ABCD", + } + + for i := range urlList { + if _, _, err := FromURL(urlList[i]); err == nil { + fmt.Println("hotp: URL should not have parsed successfully") + fmt.Printf("\turl was: %s\n", urlList[i]) + t.FailNow() + } + } +} diff --git a/vendor/github.com/gokyle/twofactor/totp.go b/vendor/github.com/gokyle/twofactor/totp.go new file mode 100644 index 0000000..10d6782 --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/totp.go @@ -0,0 +1,164 @@ +package twofactor + +import ( + "crypto" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "encoding/base32" + "hash" + "io" + "net/url" + "strconv" + "time" +) + +// TOTP represents an RFC 6238 Time-based One-Time Password instance. +type TOTP struct { + *OATH + step uint64 +} + +// Type returns OATH_TOTP. +func (otp *TOTP) Type() Type { + return OATH_TOTP +} + +func (otp *TOTP) otp(counter uint64) string { + return otp.OATH.OTP(counter) +} + +// OTP returns the OTP for the current timestep. +func (otp *TOTP) OTP() string { + return otp.otp(otp.OTPCounter()) +} + +// URL returns a TOTP URL (i.e. for putting in a QR code). +func (otp *TOTP) URL(label string) string { + return otp.OATH.URL(otp.Type(), label) +} + +// SetProvider sets up the provider component of the OTP URL. +func (otp *TOTP) SetProvider(provider string) { + otp.provider = provider +} + +func (otp *TOTP) otpCounter(t uint64) uint64 { + return (t - otp.counter) / otp.step +} + +// OTPCounter returns the current time value for the OTP. +func (otp *TOTP) OTPCounter() uint64 { + return otp.otpCounter(uint64(time.Now().Unix())) +} + +// NewOTP takes a new key, a starting time, a step, the number of +// digits of output (typically 6 or 8) and the hash algorithm to +// use, and builds a new OTP. +func NewTOTP(key []byte, start uint64, step uint64, digits int, algo crypto.Hash) *TOTP { + h := hashFromAlgo(algo) + if h == nil { + return nil + } + + return &TOTP{ + OATH: &OATH{ + key: key, + counter: start, + size: digits, + hash: h, + algo: algo, + }, + step: step, + } + +} + +// NewTOTPSHA1 will build a new TOTP using SHA-1. +func NewTOTPSHA1(key []byte, start uint64, step uint64, digits int) *TOTP { + return NewTOTP(key, start, step, digits, crypto.SHA1) +} + +func hashFromAlgo(algo crypto.Hash) func() hash.Hash { + switch algo { + case crypto.SHA1: + return sha1.New + case crypto.SHA256: + return sha256.New + case crypto.SHA512: + return sha512.New + } + return nil +} + +// GenerateGoogleTOTP produces a new TOTP token with the defaults expected by +// Google Authenticator. +func GenerateGoogleTOTP() *TOTP { + key := make([]byte, sha1.Size) + if _, err := io.ReadFull(PRNG, key); err != nil { + return nil + } + return NewTOTP(key, 0, 30, 6, crypto.SHA1) +} + +// NewGoogleTOTP takes a secret as a base32-encoded string and +// returns an appropriate Google Authenticator TOTP instance. +func NewGoogleTOTP(secret string) (*TOTP, error) { + key, err := base32.StdEncoding.DecodeString(secret) + if err != nil { + return nil, err + } + return NewTOTP(key, 0, 30, 6, crypto.SHA1), nil +} + +func totpFromURL(u *url.URL) (*TOTP, string, error) { + label := u.Path[1:] + v := u.Query() + + secret := v.Get("secret") + if secret == "" { + return nil, "", ErrInvalidURL + } + + var algo = crypto.SHA1 + if algorithm := v.Get("algorithm"); algorithm != "" { + switch { + case algorithm == "SHA256": + algo = crypto.SHA256 + case algorithm == "SHA512": + algo = crypto.SHA512 + case algorithm != "SHA1": + return nil, "", ErrInvalidAlgo + } + } + + var digits = 6 + if sdigit := v.Get("digits"); sdigit != "" { + tmpDigits, err := strconv.ParseInt(sdigit, 10, 8) + if err != nil { + return nil, "", err + } + digits = int(tmpDigits) + } + + var period uint64 = 30 + if speriod := v.Get("period"); speriod != "" { + var err error + period, err = strconv.ParseUint(speriod, 10, 64) + if err != nil { + return nil, "", err + } + } + + key, err := base32.StdEncoding.DecodeString(secret) + if err != nil { + return nil, "", err + } + otp := NewTOTP(key, 0, period, digits, algo) + return otp, label, nil +} + +// QR generates a new TOTP QR code. +func (otp *TOTP) QR(label string) ([]byte, error) { + return otp.OATH.QR(otp.Type(), label) +} diff --git a/vendor/github.com/gokyle/twofactor/totp_test.go b/vendor/github.com/gokyle/twofactor/totp_test.go new file mode 100644 index 0000000..244e43e --- /dev/null +++ b/vendor/github.com/gokyle/twofactor/totp_test.go @@ -0,0 +1,55 @@ +package twofactor + +import ( + "crypto" + "fmt" + "testing" +) + +var rfcTotpKey = []byte("12345678901234567890") +var rfcTotpStep uint64 = 30 + +var rfcTotpTests = []struct { + Time uint64 + Code string + T uint64 + Algo crypto.Hash +}{ + {59, "94287082", 1, crypto.SHA1}, + //{59, "46119246", 1, crypto.SHA256}, + //{59, "90693936", 1, crypto.SHA512}, + {1111111109, "07081804", 37037036, crypto.SHA1}, + // {1111111109, "68084774", 37037036, crypto.SHA256}, + // {1111111109, "25091201", 37037036, crypto.SHA512}, + {1111111111, "14050471", 37037037, crypto.SHA1}, + // {1111111111, "67062674", 37037037, crypto.SHA256}, + // {1111111111, "99943326", 37037037, crypto.SHA512}, + {1234567890, "89005924", 41152263, crypto.SHA1}, + // {1234567890, "91819424", 41152263, crypto.SHA256}, + // {1234567890, "93441116", 41152263, crypto.SHA512}, + {2000000000, "69279037", 66666666, crypto.SHA1}, + // {2000000000, "90698825", 66666666, crypto.SHA256}, + // {2000000000, "38618901", 66666666, crypto.SHA512}, + {20000000000, "65353130", 666666666, crypto.SHA1}, + // {20000000000, "77737706", 666666666, crypto.SHA256}, + // {20000000000, "47863826", 666666666, crypto.SHA512}, +} + +func TestTotpRFC(t *testing.T) { + for _, tc := range rfcTotpTests { + otp := NewTOTP(rfcTotpKey, 0, 30, 8, tc.Algo) + if otp.otpCounter(tc.Time) != tc.T { + fmt.Println("twofactor: invalid T") + fmt.Printf("\texpected: %d\n", tc.T) + fmt.Printf("\t actual: %d\n", otp.otpCounter(tc.Time)) + t.FailNow() + } + + if code := otp.otp(otp.otpCounter(tc.Time)); code != tc.Code { + fmt.Println("twofactor: invalid TOTP") + fmt.Printf("\texpected: %s\n", tc.Code) + fmt.Printf("\t actual: %s\n", code) + t.FailNow() + } + } +} diff --git a/vendor/github.com/mattn/go-zglob/.travis.yml b/vendor/github.com/mattn/go-zglob/.travis.yml new file mode 100644 index 0000000..5f72342 --- /dev/null +++ b/vendor/github.com/mattn/go-zglob/.travis.yml @@ -0,0 +1,6 @@ +language: go +go: + - tip +sudo: false +script: + - go test diff --git a/vendor/github.com/mattn/go-zglob/cmd/zglob/main.go b/vendor/github.com/mattn/go-zglob/cmd/zglob/main.go new file mode 100644 index 0000000..6df2955 --- /dev/null +++ b/vendor/github.com/mattn/go-zglob/cmd/zglob/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "os" + + "github.com/mattn/go-zglob" +) + +func main() { + for _, arg := range os.Args[1:] { + matches, err := zglob.Glob(arg) + if err != nil { + continue + } + for _, m := range matches { + if fi, err := os.Stat(m); err == nil && fi.Mode().IsRegular() { + fmt.Println(m) + } + } + } +} diff --git a/vendor/github.com/mattn/go-zglob/fastwalk/fastwalk_test.go b/vendor/github.com/mattn/go-zglob/fastwalk/fastwalk_test.go new file mode 100644 index 0000000..1a4ec91 --- /dev/null +++ b/vendor/github.com/mattn/go-zglob/fastwalk/fastwalk_test.go @@ -0,0 +1,171 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fastwalk + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "reflect" + "runtime" + "sort" + "strings" + "sync" + "testing" +) + +func formatFileModes(m map[string]os.FileMode) string { + var keys []string + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + var buf bytes.Buffer + for _, k := range keys { + fmt.Fprintf(&buf, "%-20s: %v\n", k, m[k]) + } + return buf.String() +} + +func testFastWalk(t *testing.T, files map[string]string, callback func(path string, typ os.FileMode) error, want map[string]os.FileMode) { + testConfig{ + gopathFiles: files, + }.test(t, func(t *goimportTest) { + got := map[string]os.FileMode{} + var mu sync.Mutex + if err := fastWalk(t.gopath, func(path string, typ os.FileMode) error { + mu.Lock() + defer mu.Unlock() + if !strings.HasPrefix(path, t.gopath) { + t.Fatal("bogus prefix on %q, expect %q", path, t.gopath) + } + key := filepath.ToSlash(strings.TrimPrefix(path, t.gopath)) + if old, dup := got[key]; dup { + t.Fatalf("callback called twice for key %q: %v -> %v", key, old, typ) + } + got[key] = typ + return callback(path, typ) + }); err != nil { + t.Fatalf("callback returned: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("walk mismatch.\n got:\n%v\nwant:\n%v", formatFileModes(got), formatFileModes(want)) + } + }) +} + +func TestFastWalk_Basic(t *testing.T) { + testFastWalk(t, map[string]string{ + "foo/foo.go": "one", + "bar/bar.go": "two", + "skip/skip.go": "skip", + }, + func(path string, typ os.FileMode) error { + return nil + }, + map[string]os.FileMode{ + "": os.ModeDir, + "/src": os.ModeDir, + "/src/bar": os.ModeDir, + "/src/bar/bar.go": 0, + "/src/foo": os.ModeDir, + "/src/foo/foo.go": 0, + "/src/skip": os.ModeDir, + "/src/skip/skip.go": 0, + }) +} + +func TestFastWalk_Symlink(t *testing.T) { + switch runtime.GOOS { + case "windows", "plan9": + t.Skipf("skipping on %s", runtime.GOOS) + } + testFastWalk(t, map[string]string{ + "foo/foo.go": "one", + "bar/bar.go": "LINK:../foo.go", + "symdir": "LINK:foo", + }, + func(path string, typ os.FileMode) error { + return nil + }, + map[string]os.FileMode{ + "": os.ModeDir, + "/src": os.ModeDir, + "/src/bar": os.ModeDir, + "/src/bar/bar.go": os.ModeSymlink, + "/src/foo": os.ModeDir, + "/src/foo/foo.go": 0, + "/src/symdir": os.ModeSymlink, + }) +} + +func TestFastWalk_SkipDir(t *testing.T) { + testFastWalk(t, map[string]string{ + "foo/foo.go": "one", + "bar/bar.go": "two", + "skip/skip.go": "skip", + }, + func(path string, typ os.FileMode) error { + if typ == os.ModeDir && strings.HasSuffix(path, "skip") { + return filepath.SkipDir + } + return nil + }, + map[string]os.FileMode{ + "": os.ModeDir, + "/src": os.ModeDir, + "/src/bar": os.ModeDir, + "/src/bar/bar.go": 0, + "/src/foo": os.ModeDir, + "/src/foo/foo.go": 0, + "/src/skip": os.ModeDir, + }) +} + +func TestFastWalk_TraverseSymlink(t *testing.T) { + switch runtime.GOOS { + case "windows", "plan9": + t.Skipf("skipping on %s", runtime.GOOS) + } + + testFastWalk(t, map[string]string{ + "foo/foo.go": "one", + "bar/bar.go": "two", + "skip/skip.go": "skip", + "symdir": "LINK:foo", + }, + func(path string, typ os.FileMode) error { + if typ == os.ModeSymlink { + return traverseLink + } + return nil + }, + map[string]os.FileMode{ + "": os.ModeDir, + "/src": os.ModeDir, + "/src/bar": os.ModeDir, + "/src/bar/bar.go": 0, + "/src/foo": os.ModeDir, + "/src/foo/foo.go": 0, + "/src/skip": os.ModeDir, + "/src/skip/skip.go": 0, + "/src/symdir": os.ModeSymlink, + "/src/symdir/foo.go": 0, + }) +} + +var benchDir = flag.String("benchdir", runtime.GOROOT(), "The directory to scan for BenchmarkFastWalk") + +func BenchmarkFastWalk(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + err := fastWalk(*benchDir, func(path string, typ os.FileMode) error { return nil }) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/vendor/github.com/mattn/go-zglob/zglob_test.go b/vendor/github.com/mattn/go-zglob/zglob_test.go new file mode 100644 index 0000000..ca666e1 --- /dev/null +++ b/vendor/github.com/mattn/go-zglob/zglob_test.go @@ -0,0 +1,138 @@ +package zglob + +import ( + "io/ioutil" + "os" + "path/filepath" + "reflect" + "sort" + "testing" +) + +func check(got []string, expected []string) bool { + sort.Strings(got) + sort.Strings(expected) + return reflect.DeepEqual(expected, got) +} + +type testZGlob struct { + pattern string + expected []string + err error +} + +var testGlobs = []testZGlob{ + {`fo*`, []string{`foo`}, nil}, + {`foo`, []string{`foo`}, nil}, + {`foo/*`, []string{`foo/bar`, `foo/baz`}, nil}, + {`foo/**`, []string{`foo/bar`, `foo/baz`}, nil}, + {`f*o/**`, []string{`foo/bar`, `foo/baz`}, nil}, + {`*oo/**`, []string{`foo/bar`, `foo/baz`, `hoo/bar`}, nil}, + {`*oo/b*`, []string{`foo/bar`, `foo/baz`, `hoo/bar`}, nil}, + {`*oo/*z`, []string{`foo/baz`}, nil}, + {`foo/**/*`, []string{`foo/bar`, `foo/bar/baz`, `foo/bar/baz.txt`, `foo/bar/baz/noo.txt`, `foo/baz`}, nil}, + {`*oo/**/*`, []string{`foo/bar`, `foo/bar/baz`, `foo/bar/baz.txt`, `foo/bar/baz/noo.txt`, `foo/baz`, `hoo/bar`}, nil}, + {`*oo/*.txt`, []string{}, nil}, + {`*oo/*/*.txt`, []string{`foo/bar/baz.txt`}, nil}, + {`*oo/**/*.txt`, []string{`foo/bar/baz.txt`, `foo/bar/baz/noo.txt`}, nil}, + {`doo`, nil, os.ErrNotExist}, + {`./f*`, []string{`foo`}, nil}, +} + +func setup(t *testing.T) string { + tmpdir, err := ioutil.TempDir("", "zglob") + if err != nil { + t.Fatal(err) + } + + os.MkdirAll(filepath.Join(tmpdir, "foo/baz"), 0755) + os.MkdirAll(filepath.Join(tmpdir, "foo/bar"), 0755) + ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644) + os.MkdirAll(filepath.Join(tmpdir, "foo/bar/baz"), 0755) + ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz/noo.txt"), []byte{}, 0644) + os.MkdirAll(filepath.Join(tmpdir, "hoo/bar"), 0755) + ioutil.WriteFile(filepath.Join(tmpdir, "foo/bar/baz.txt"), []byte{}, 0644) + + return tmpdir +} + +func TestGlob(t *testing.T) { + tmpdir := setup(t) + defer os.RemoveAll(tmpdir) + + curdir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + err = os.Chdir(tmpdir) + if err != nil { + t.Fatal(err) + } + defer os.Chdir(curdir) + + tmpdir = "." + for _, test := range testGlobs { + expected := make([]string, len(test.expected)) + for i, e := range test.expected { + expected[i] = e + } + got, err := Glob(test.pattern) + if err != nil { + if test.err != err { + t.Error(err) + } + continue + } + if !check(expected, got) { + t.Errorf(`zglob failed: pattern %q(%q): expected %v but got %v`, test.pattern, tmpdir, expected, got) + } + } +} + +func TestGlobAbs(t *testing.T) { + tmpdir := setup(t) + defer os.RemoveAll(tmpdir) + + curdir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + err = os.Chdir(tmpdir) + if err != nil { + t.Fatal(err) + } + defer os.Chdir(curdir) + + for _, test := range testGlobs { + pattern := filepath.ToSlash(filepath.Join(tmpdir, test.pattern)) + expected := make([]string, len(test.expected)) + for i, e := range test.expected { + expected[i] = filepath.ToSlash(filepath.Join(tmpdir, e)) + } + got, err := Glob(pattern) + if err != nil { + if test.err != err { + t.Error(err) + } + continue + } + if !check(expected, got) { + t.Errorf(`zglob failed: pattern %q(%q): expected %v but got %v`, pattern, tmpdir, expected, got) + } + } +} + +func TestMatch(t *testing.T) { + for _, test := range testGlobs { + for _, f := range test.expected { + got, err := Match(test.pattern, f) + if err != nil { + t.Error(err) + continue + } + if !got { + t.Errorf("%q should match with %q", f, test.pattern) + } + } + } +} diff --git a/vendor/rsc.io/qr/LICENSE b/vendor/rsc.io/qr/LICENSE new file mode 100644 index 0000000..6a66aea --- /dev/null +++ b/vendor/rsc.io/qr/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/rsc.io/qr/README.md b/vendor/rsc.io/qr/README.md new file mode 100644 index 0000000..0ba6214 --- /dev/null +++ b/vendor/rsc.io/qr/README.md @@ -0,0 +1,3 @@ +Basic QR encoder. + +go get [-u] rsc.io/qr diff --git a/vendor/rsc.io/qr/coding/gen.go b/vendor/rsc.io/qr/coding/gen.go new file mode 100644 index 0000000..a3857f2 --- /dev/null +++ b/vendor/rsc.io/qr/coding/gen.go @@ -0,0 +1,149 @@ +// +build ignore + +package main + +import "fmt" + +// tables from qrencode-3.1.1/qrspec.c + +var capacity = [41]struct { + width int + words int + remainder int + ec [4]int +}{ + {0, 0, 0, [4]int{0, 0, 0, 0}}, + {21, 26, 0, [4]int{7, 10, 13, 17}}, // 1 + {25, 44, 7, [4]int{10, 16, 22, 28}}, + {29, 70, 7, [4]int{15, 26, 36, 44}}, + {33, 100, 7, [4]int{20, 36, 52, 64}}, + {37, 134, 7, [4]int{26, 48, 72, 88}}, // 5 + {41, 172, 7, [4]int{36, 64, 96, 112}}, + {45, 196, 0, [4]int{40, 72, 108, 130}}, + {49, 242, 0, [4]int{48, 88, 132, 156}}, + {53, 292, 0, [4]int{60, 110, 160, 192}}, + {57, 346, 0, [4]int{72, 130, 192, 224}}, //10 + {61, 404, 0, [4]int{80, 150, 224, 264}}, + {65, 466, 0, [4]int{96, 176, 260, 308}}, + {69, 532, 0, [4]int{104, 198, 288, 352}}, + {73, 581, 3, [4]int{120, 216, 320, 384}}, + {77, 655, 3, [4]int{132, 240, 360, 432}}, //15 + {81, 733, 3, [4]int{144, 280, 408, 480}}, + {85, 815, 3, [4]int{168, 308, 448, 532}}, + {89, 901, 3, [4]int{180, 338, 504, 588}}, + {93, 991, 3, [4]int{196, 364, 546, 650}}, + {97, 1085, 3, [4]int{224, 416, 600, 700}}, //20 + {101, 1156, 4, [4]int{224, 442, 644, 750}}, + {105, 1258, 4, [4]int{252, 476, 690, 816}}, + {109, 1364, 4, [4]int{270, 504, 750, 900}}, + {113, 1474, 4, [4]int{300, 560, 810, 960}}, + {117, 1588, 4, [4]int{312, 588, 870, 1050}}, //25 + {121, 1706, 4, [4]int{336, 644, 952, 1110}}, + {125, 1828, 4, [4]int{360, 700, 1020, 1200}}, + {129, 1921, 3, [4]int{390, 728, 1050, 1260}}, + {133, 2051, 3, [4]int{420, 784, 1140, 1350}}, + {137, 2185, 3, [4]int{450, 812, 1200, 1440}}, //30 + {141, 2323, 3, [4]int{480, 868, 1290, 1530}}, + {145, 2465, 3, [4]int{510, 924, 1350, 1620}}, + {149, 2611, 3, [4]int{540, 980, 1440, 1710}}, + {153, 2761, 3, [4]int{570, 1036, 1530, 1800}}, + {157, 2876, 0, [4]int{570, 1064, 1590, 1890}}, //35 + {161, 3034, 0, [4]int{600, 1120, 1680, 1980}}, + {165, 3196, 0, [4]int{630, 1204, 1770, 2100}}, + {169, 3362, 0, [4]int{660, 1260, 1860, 2220}}, + {173, 3532, 0, [4]int{720, 1316, 1950, 2310}}, + {177, 3706, 0, [4]int{750, 1372, 2040, 2430}}, //40 +} + +var eccTable = [41][4][2]int{ + {{0, 0}, {0, 0}, {0, 0}, {0, 0}}, + {{1, 0}, {1, 0}, {1, 0}, {1, 0}}, // 1 + {{1, 0}, {1, 0}, {1, 0}, {1, 0}}, + {{1, 0}, {1, 0}, {2, 0}, {2, 0}}, + {{1, 0}, {2, 0}, {2, 0}, {4, 0}}, + {{1, 0}, {2, 0}, {2, 2}, {2, 2}}, // 5 + {{2, 0}, {4, 0}, {4, 0}, {4, 0}}, + {{2, 0}, {4, 0}, {2, 4}, {4, 1}}, + {{2, 0}, {2, 2}, {4, 2}, {4, 2}}, + {{2, 0}, {3, 2}, {4, 4}, {4, 4}}, + {{2, 2}, {4, 1}, {6, 2}, {6, 2}}, //10 + {{4, 0}, {1, 4}, {4, 4}, {3, 8}}, + {{2, 2}, {6, 2}, {4, 6}, {7, 4}}, + {{4, 0}, {8, 1}, {8, 4}, {12, 4}}, + {{3, 1}, {4, 5}, {11, 5}, {11, 5}}, + {{5, 1}, {5, 5}, {5, 7}, {11, 7}}, //15 + {{5, 1}, {7, 3}, {15, 2}, {3, 13}}, + {{1, 5}, {10, 1}, {1, 15}, {2, 17}}, + {{5, 1}, {9, 4}, {17, 1}, {2, 19}}, + {{3, 4}, {3, 11}, {17, 4}, {9, 16}}, + {{3, 5}, {3, 13}, {15, 5}, {15, 10}}, //20 + {{4, 4}, {17, 0}, {17, 6}, {19, 6}}, + {{2, 7}, {17, 0}, {7, 16}, {34, 0}}, + {{4, 5}, {4, 14}, {11, 14}, {16, 14}}, + {{6, 4}, {6, 14}, {11, 16}, {30, 2}}, + {{8, 4}, {8, 13}, {7, 22}, {22, 13}}, //25 + {{10, 2}, {19, 4}, {28, 6}, {33, 4}}, + {{8, 4}, {22, 3}, {8, 26}, {12, 28}}, + {{3, 10}, {3, 23}, {4, 31}, {11, 31}}, + {{7, 7}, {21, 7}, {1, 37}, {19, 26}}, + {{5, 10}, {19, 10}, {15, 25}, {23, 25}}, //30 + {{13, 3}, {2, 29}, {42, 1}, {23, 28}}, + {{17, 0}, {10, 23}, {10, 35}, {19, 35}}, + {{17, 1}, {14, 21}, {29, 19}, {11, 46}}, + {{13, 6}, {14, 23}, {44, 7}, {59, 1}}, + {{12, 7}, {12, 26}, {39, 14}, {22, 41}}, //35 + {{6, 14}, {6, 34}, {46, 10}, {2, 64}}, + {{17, 4}, {29, 14}, {49, 10}, {24, 46}}, + {{4, 18}, {13, 32}, {48, 14}, {42, 32}}, + {{20, 4}, {40, 7}, {43, 22}, {10, 67}}, + {{19, 6}, {18, 31}, {34, 34}, {20, 61}}, //40 +} + +var align = [41][2]int{ + {0, 0}, + {0, 0}, {18, 0}, {22, 0}, {26, 0}, {30, 0}, // 1- 5 + {34, 0}, {22, 38}, {24, 42}, {26, 46}, {28, 50}, // 6-10 + {30, 54}, {32, 58}, {34, 62}, {26, 46}, {26, 48}, //11-15 + {26, 50}, {30, 54}, {30, 56}, {30, 58}, {34, 62}, //16-20 + {28, 50}, {26, 50}, {30, 54}, {28, 54}, {32, 58}, //21-25 + {30, 58}, {34, 62}, {26, 50}, {30, 54}, {26, 52}, //26-30 + {30, 56}, {34, 60}, {30, 58}, {34, 62}, {30, 54}, //31-35 + {24, 50}, {28, 54}, {32, 58}, {26, 54}, {30, 58}, //35-40 +} + +var versionPattern = [41]int{ + 0, + 0, 0, 0, 0, 0, 0, + 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, + 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, + 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, + 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, + 0x27541, 0x28c69, +} + +func main() { + fmt.Printf("\t{},\n") + for i := 1; i <= 40; i++ { + apos := align[i][0] - 2 + if apos < 0 { + apos = 100 + } + astride := align[i][1] - align[i][0] + if astride < 1 { + astride = 100 + } + fmt.Printf("\t{%v, %v, %v, %#x, [4]level{{%v, %v}, {%v, %v}, {%v, %v}, {%v, %v}}}, // %v\n", + apos, astride, capacity[i].words, + versionPattern[i], + eccTable[i][0][0]+eccTable[i][0][1], + float64(capacity[i].ec[0])/float64(eccTable[i][0][0]+eccTable[i][0][1]), + eccTable[i][1][0]+eccTable[i][1][1], + float64(capacity[i].ec[1])/float64(eccTable[i][1][0]+eccTable[i][1][1]), + eccTable[i][2][0]+eccTable[i][2][1], + float64(capacity[i].ec[2])/float64(eccTable[i][2][0]+eccTable[i][2][1]), + eccTable[i][3][0]+eccTable[i][3][1], + float64(capacity[i].ec[3])/float64(eccTable[i][3][0]+eccTable[i][3][1]), + i, + ) + } +} diff --git a/vendor/rsc.io/qr/coding/qr.go b/vendor/rsc.io/qr/coding/qr.go new file mode 100644 index 0000000..4aa5288 --- /dev/null +++ b/vendor/rsc.io/qr/coding/qr.go @@ -0,0 +1,815 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package coding implements low-level QR coding details. +package coding + +import ( + "fmt" + "strconv" + "strings" + + "rsc.io/qr/gf256" +) + +// Field is the field for QR error correction. +var Field = gf256.NewField(0x11d, 2) + +// A Version represents a QR version. +// The version specifies the size of the QR code: +// a QR code with version v has 4v+17 pixels on a side. +// Versions number from 1 to 40: the larger the version, +// the more information the code can store. +type Version int + +const MinVersion = 1 +const MaxVersion = 40 + +func (v Version) String() string { + return strconv.Itoa(int(v)) +} + +func (v Version) sizeClass() int { + if v <= 9 { + return 0 + } + if v <= 26 { + return 1 + } + return 2 +} + +// DataBytes returns the number of data bytes that can be +// stored in a QR code with the given version and level. +func (v Version) DataBytes(l Level) int { + vt := &vtab[v] + lev := &vt.level[l] + return vt.bytes - lev.nblock*lev.check +} + +// Encoding implements a QR data encoding scheme. +// The implementations--Numeric, Alphanumeric, and String--specify +// the character set and the mapping from UTF-8 to code bits. +// The more restrictive the mode, the fewer code bits are needed. +type Encoding interface { + Check() error + Bits(v Version) int + Encode(b *Bits, v Version) +} + +type Bits struct { + b []byte + nbit int +} + +func (b *Bits) Reset() { + b.b = b.b[:0] + b.nbit = 0 +} + +func (b *Bits) Bits() int { + return b.nbit +} + +func (b *Bits) Bytes() []byte { + if b.nbit%8 != 0 { + panic("fractional byte") + } + return b.b +} + +func (b *Bits) Append(p []byte) { + if b.nbit%8 != 0 { + panic("fractional byte") + } + b.b = append(b.b, p...) + b.nbit += 8 * len(p) +} + +func (b *Bits) Write(v uint, nbit int) { + for nbit > 0 { + n := nbit + if n > 8 { + n = 8 + } + if b.nbit%8 == 0 { + b.b = append(b.b, 0) + } else { + m := -b.nbit & 7 + if n > m { + n = m + } + } + b.nbit += n + sh := uint(nbit - n) + b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7)) + v -= v >> sh << sh + nbit -= n + } +} + +// Num is the encoding for numeric data. +// The only valid characters are the decimal digits 0 through 9. +type Num string + +func (s Num) String() string { + return fmt.Sprintf("Num(%#q)", string(s)) +} + +func (s Num) Check() error { + for _, c := range s { + if c < '0' || '9' < c { + return fmt.Errorf("non-numeric string %#q", string(s)) + } + } + return nil +} + +var numLen = [3]int{10, 12, 14} + +func (s Num) Bits(v Version) int { + return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3 +} + +func (s Num) Encode(b *Bits, v Version) { + b.Write(1, 4) + b.Write(uint(len(s)), numLen[v.sizeClass()]) + var i int + for i = 0; i+3 <= len(s); i += 3 { + w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0') + b.Write(w, 10) + } + switch len(s) - i { + case 1: + w := uint(s[i] - '0') + b.Write(w, 4) + case 2: + w := uint(s[i]-'0')*10 + uint(s[i+1]-'0') + b.Write(w, 7) + } +} + +// Alpha is the encoding for alphanumeric data. +// The valid characters are 0-9A-Z$%*+-./: and space. +type Alpha string + +const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:" + +func (s Alpha) String() string { + return fmt.Sprintf("Alpha(%#q)", string(s)) +} + +func (s Alpha) Check() error { + for _, c := range s { + if strings.IndexRune(alphabet, c) < 0 { + return fmt.Errorf("non-alphanumeric string %#q", string(s)) + } + } + return nil +} + +var alphaLen = [3]int{9, 11, 13} + +func (s Alpha) Bits(v Version) int { + return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2 +} + +func (s Alpha) Encode(b *Bits, v Version) { + b.Write(2, 4) + b.Write(uint(len(s)), alphaLen[v.sizeClass()]) + var i int + for i = 0; i+2 <= len(s); i += 2 { + w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 + + uint(strings.IndexRune(alphabet, rune(s[i+1]))) + b.Write(w, 11) + } + + if i < len(s) { + w := uint(strings.IndexRune(alphabet, rune(s[i]))) + b.Write(w, 6) + } +} + +// String is the encoding for 8-bit data. All bytes are valid. +type String string + +func (s String) String() string { + return fmt.Sprintf("String(%#q)", string(s)) +} + +func (s String) Check() error { + return nil +} + +var stringLen = [3]int{8, 16, 16} + +func (s String) Bits(v Version) int { + return 4 + stringLen[v.sizeClass()] + 8*len(s) +} + +func (s String) Encode(b *Bits, v Version) { + b.Write(4, 4) + b.Write(uint(len(s)), stringLen[v.sizeClass()]) + for i := 0; i < len(s); i++ { + b.Write(uint(s[i]), 8) + } +} + +// A Pixel describes a single pixel in a QR code. +type Pixel uint32 + +const ( + Black Pixel = 1 << iota + Invert +) + +func (p Pixel) Offset() uint { + return uint(p >> 6) +} + +func OffsetPixel(o uint) Pixel { + return Pixel(o << 6) +} + +func (r PixelRole) Pixel() Pixel { + return Pixel(r << 2) +} + +func (p Pixel) Role() PixelRole { + return PixelRole(p>>2) & 15 +} + +func (p Pixel) String() string { + s := p.Role().String() + if p&Black != 0 { + s += "+black" + } + if p&Invert != 0 { + s += "+invert" + } + s += "+" + strconv.FormatUint(uint64(p.Offset()), 10) + return s +} + +// A PixelRole describes the role of a QR pixel. +type PixelRole uint32 + +const ( + _ PixelRole = iota + Position // position squares (large) + Alignment // alignment squares (small) + Timing // timing strip between position squares + Format // format metadata + PVersion // version pattern + Unused // unused pixel + Data // data bit + Check // error correction check bit + Extra +) + +var roles = []string{ + "", + "position", + "alignment", + "timing", + "format", + "pversion", + "unused", + "data", + "check", + "extra", +} + +func (r PixelRole) String() string { + if Position <= r && r <= Check { + return roles[r] + } + return strconv.Itoa(int(r)) +} + +// A Level represents a QR error correction level. +// From least to most tolerant of errors, they are L, M, Q, H. +type Level int + +const ( + L Level = iota + M + Q + H +) + +func (l Level) String() string { + if L <= l && l <= H { + return "LMQH"[l : l+1] + } + return strconv.Itoa(int(l)) +} + +// A Code is a square pixel grid. +type Code struct { + Bitmap []byte // 1 is black, 0 is white + Size int // number of pixels on a side + Stride int // number of bytes per row +} + +func (c *Code) Black(x, y int) bool { + return 0 <= x && x < c.Size && 0 <= y && y < c.Size && + c.Bitmap[y*c.Stride+x/8]&(1<= pad { + break + } + b.Write(0x11, 8) + } + } +} + +func (b *Bits) AddCheckBytes(v Version, l Level) { + nd := v.DataBytes(l) + if b.nbit < nd*8 { + b.Pad(nd*8 - b.nbit) + } + if b.nbit != nd*8 { + panic("qr: too much data") + } + + dat := b.Bytes() + vt := &vtab[v] + lev := &vt.level[l] + db := nd / lev.nblock + extra := nd % lev.nblock + chk := make([]byte, lev.check) + rs := gf256.NewRSEncoder(Field, lev.check) + for i := 0; i < lev.nblock; i++ { + if i == lev.nblock-extra { + db++ + } + rs.ECC(dat[:db], chk) + b.Append(chk) + dat = dat[db:] + } + + if len(b.Bytes()) != vt.bytes { + panic("qr: internal error") + } +} + +func (p *Plan) Encode(text ...Encoding) (*Code, error) { + var b Bits + for _, t := range text { + if err := t.Check(); err != nil { + return nil, err + } + t.Encode(&b, p.Version) + } + if b.Bits() > p.DataBytes*8 { + return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8) + } + b.AddCheckBytes(p.Version, p.Level) + bytes := b.Bytes() + + // Now we have the checksum bytes and the data bytes. + // Construct the actual code. + c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7} + c.Bitmap = make([]byte, c.Stride*c.Size) + crow := c.Bitmap + for _, row := range p.Pixel { + for x, pix := range row { + switch pix.Role() { + case Data, Check: + o := pix.Offset() + if bytes[o/8]&(1< 40 { + return nil, fmt.Errorf("invalid QR version %d", int(v)) + } + siz := 17 + int(v)*4 + m := grid(siz) + p.Pixel = m + + // Timing markers (overwritten by boxes). + const ti = 6 // timing is in row/column 6 (counting from 0) + for i := range m { + p := Timing.Pixel() + if i&1 == 0 { + p |= Black + } + m[i][ti] = p + m[ti][i] = p + } + + // Position boxes. + posBox(m, 0, 0) + posBox(m, siz-7, 0) + posBox(m, 0, siz-7) + + // Alignment boxes. + info := &vtab[v] + for x := 4; x+5 < siz; { + for y := 4; y+5 < siz; { + // don't overwrite timing markers + if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) { + } else { + alignBox(m, x, y) + } + if y == 4 { + y = info.apos + } else { + y += info.astride + } + } + if x == 4 { + x = info.apos + } else { + x += info.astride + } + } + + // Version pattern. + pat := vtab[v].pattern + if pat != 0 { + v := pat + for x := 0; x < 6; x++ { + for y := 0; y < 3; y++ { + p := PVersion.Pixel() + if v&1 != 0 { + p |= Black + } + m[siz-11+y][x] = p + m[x][siz-11+y] = p + v >>= 1 + } + } + } + + // One lonely black pixel + m[siz-8][8] = Unused.Pixel() | Black + + return p, nil +} + +// fplan adds the format pixels +func fplan(l Level, m Mask, p *Plan) error { + // Format pixels. + fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10 + fb |= uint32(m) << 10 // mask + const formatPoly = 0x537 + rem := fb + for i := 14; i >= 10; i-- { + if rem&(1<>i)&1 == 1 { + pix |= Black + } + if (invert>>i)&1 == 1 { + pix ^= Invert | Black + } + // top left + switch { + case i < 6: + p.Pixel[i][8] = pix + case i < 8: + p.Pixel[i+1][8] = pix + case i < 9: + p.Pixel[8][7] = pix + default: + p.Pixel[8][14-i] = pix + } + // bottom right + switch { + case i < 8: + p.Pixel[8][siz-1-int(i)] = pix + default: + p.Pixel[siz-1-int(14-i)][8] = pix + } + } + return nil +} + +// lplan edits a version-only Plan to add information +// about the error correction levels. +func lplan(v Version, l Level, p *Plan) error { + p.Level = l + + nblock := vtab[v].level[l].nblock + ne := vtab[v].level[l].check + nde := (vtab[v].bytes - ne*nblock) / nblock + extra := (vtab[v].bytes - ne*nblock) % nblock + dataBits := (nde*nblock + extra) * 8 + checkBits := ne * nblock * 8 + + p.DataBytes = vtab[v].bytes - ne*nblock + p.CheckBytes = ne * nblock + p.Blocks = nblock + + // Make data + checksum pixels. + data := make([]Pixel, dataBits) + for i := range data { + data[i] = Data.Pixel() | OffsetPixel(uint(i)) + } + check := make([]Pixel, checkBits) + for i := range check { + check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits)) + } + + // Split into blocks. + dataList := make([][]Pixel, nblock) + checkList := make([][]Pixel, nblock) + for i := 0; i < nblock; i++ { + // The last few blocks have an extra data byte (8 pixels). + nd := nde + if i >= nblock-extra { + nd++ + } + dataList[i], data = data[0:nd*8], data[nd*8:] + checkList[i], check = check[0:ne*8], check[ne*8:] + } + if len(data) != 0 || len(check) != 0 { + panic("data/check math") + } + + // Build up bit sequence, taking first byte of each block, + // then second byte, and so on. Then checksums. + bits := make([]Pixel, dataBits+checkBits) + dst := bits + for i := 0; i < nde+1; i++ { + for _, b := range dataList { + if i*8 < len(b) { + copy(dst, b[i*8:(i+1)*8]) + dst = dst[8:] + } + } + } + for i := 0; i < ne; i++ { + for _, b := range checkList { + if i*8 < len(b) { + copy(dst, b[i*8:(i+1)*8]) + dst = dst[8:] + } + } + } + if len(dst) != 0 { + panic("dst math") + } + + // Sweep up pair of columns, + // then down, assigning to right then left pixel. + // Repeat. + // See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm + siz := len(p.Pixel) + rem := make([]Pixel, 7) + for i := range rem { + rem[i] = Extra.Pixel() + } + src := append(bits, rem...) + for x := siz; x > 0; { + for y := siz - 1; y >= 0; y-- { + if p.Pixel[y][x-1].Role() == 0 { + p.Pixel[y][x-1], src = src[0], src[1:] + } + if p.Pixel[y][x-2].Role() == 0 { + p.Pixel[y][x-2], src = src[0], src[1:] + } + } + x -= 2 + if x == 7 { // vertical timing strip + x-- + } + for y := 0; y < siz; y++ { + if p.Pixel[y][x-1].Role() == 0 { + p.Pixel[y][x-1], src = src[0], src[1:] + } + if p.Pixel[y][x-2].Role() == 0 { + p.Pixel[y][x-2], src = src[0], src[1:] + } + } + x -= 2 + } + return nil +} + +// mplan edits a version+level-only Plan to add the mask. +func mplan(m Mask, p *Plan) error { + p.Mask = m + for y, row := range p.Pixel { + for x, pix := range row { + if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) { + row[x] ^= Black | Invert + } + } + } + return nil +} + +// posBox draws a position (large) box at upper left x, y. +func posBox(m [][]Pixel, x, y int) { + pos := Position.Pixel() + // box + for dy := 0; dy < 7; dy++ { + for dx := 0; dx < 7; dx++ { + p := pos + if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 { + p |= Black + } + m[y+dy][x+dx] = p + } + } + // white border + for dy := -1; dy < 8; dy++ { + if 0 <= y+dy && y+dy < len(m) { + if x > 0 { + m[y+dy][x-1] = pos + } + if x+7 < len(m) { + m[y+dy][x+7] = pos + } + } + } + for dx := -1; dx < 8; dx++ { + if 0 <= x+dx && x+dx < len(m) { + if y > 0 { + m[y-1][x+dx] = pos + } + if y+7 < len(m) { + m[y+7][x+dx] = pos + } + } + } +} + +// alignBox draw an alignment (small) box at upper left x, y. +func alignBox(m [][]Pixel, x, y int) { + // box + align := Alignment.Pixel() + for dy := 0; dy < 5; dy++ { + for dx := 0; dx < 5; dx++ { + p := align + if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 { + p |= Black + } + m[y+dy][x+dx] = p + } + } +} diff --git a/vendor/rsc.io/qr/coding/qr_test.go b/vendor/rsc.io/qr/coding/qr_test.go new file mode 100644 index 0000000..c933bb8 --- /dev/null +++ b/vendor/rsc.io/qr/coding/qr_test.go @@ -0,0 +1,133 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package coding + +import ( + "bytes" + "testing" + + "rsc.io/qr/gf256" + "rsc.io/qr/libqrencode" +) + +func test(t *testing.T, v Version, l Level, text ...Encoding) bool { + s := "" + ty := libqrencode.EightBit + switch x := text[0].(type) { + case String: + s = string(x) + case Alpha: + s = string(x) + ty = libqrencode.Alphanumeric + case Num: + s = string(x) + ty = libqrencode.Numeric + } + key, err := libqrencode.Encode(libqrencode.Version(v), libqrencode.Level(l), ty, s) + if err != nil { + t.Errorf("libqrencode.Encode(%v, %v, %d, %#q): %v", v, l, ty, s, err) + return false + } + mask := (^key.Pixel[8][2]&1)<<2 | (key.Pixel[8][3]&1)<<1 | (^key.Pixel[8][4] & 1) + p, err := NewPlan(v, l, Mask(mask)) + if err != nil { + t.Errorf("NewPlan(%v, L, %d): %v", v, err, mask) + return false + } + if len(p.Pixel) != len(key.Pixel) { + t.Errorf("%v: NewPlan uses %dx%d, libqrencode uses %dx%d", v, len(p.Pixel), len(p.Pixel), len(key.Pixel), len(key.Pixel)) + return false + } + c, err := p.Encode(text...) + if err != nil { + t.Errorf("Encode: %v", err) + return false + } + badpix := 0 +Pixel: + for y, prow := range p.Pixel { + for x, pix := range prow { + pix &^= Black + if c.Black(x, y) { + pix |= Black + } + + keypix := key.Pixel[y][x] + want := Pixel(0) + switch { + case keypix&libqrencode.Finder != 0: + want = Position.Pixel() + case keypix&libqrencode.Alignment != 0: + want = Alignment.Pixel() + case keypix&libqrencode.Timing != 0: + want = Timing.Pixel() + case keypix&libqrencode.Format != 0: + want = Format.Pixel() + want |= OffsetPixel(pix.Offset()) // sic + want |= pix & Invert + case keypix&libqrencode.PVersion != 0: + want = PVersion.Pixel() + case keypix&libqrencode.DataECC != 0: + if pix.Role() == Check || pix.Role() == Extra { + want = pix.Role().Pixel() + } else { + want = Data.Pixel() + } + want |= OffsetPixel(pix.Offset()) + want |= pix & Invert + default: + want = Unused.Pixel() + } + if keypix&libqrencode.Black != 0 { + want |= Black + } + if pix != want { + t.Errorf("%v/%v: Pixel[%d][%d] = %v, want %v %#x", v, mask, y, x, pix, want, keypix) + if badpix++; badpix >= 100 { + t.Errorf("stopping after %d bad pixels", badpix) + break Pixel + } + } + } + } + return badpix == 0 +} + +var input = []Encoding{ + String("hello"), + Num("1"), + Num("12"), + Num("123"), + Alpha("AB"), + Alpha("ABC"), +} + +func TestVersion(t *testing.T) { + badvers := 0 +Version: + for v := Version(1); v <= 40; v++ { + for l := L; l <= H; l++ { + for _, in := range input { + if !test(t, v, l, in) { + if badvers++; badvers >= 10 { + t.Errorf("stopping after %d bad versions", badvers) + break Version + } + } + } + } + } +} + +func TestEncode(t *testing.T) { + data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} + check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} + rs := gf256.NewRSEncoder(Field, len(check)) + out := make([]byte, len(check)) + rs.ECC(data, out) + if !bytes.Equal(out, check) { + t.Errorf("have %x want %x", out, check) + } +} diff --git a/vendor/rsc.io/qr/gf256/blog_test.go b/vendor/rsc.io/qr/gf256/blog_test.go new file mode 100644 index 0000000..4fbf7ec --- /dev/null +++ b/vendor/rsc.io/qr/gf256/blog_test.go @@ -0,0 +1,85 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file contains a straightforward implementation of +// Reed-Solomon encoding, along with a benchmark. +// It goes with http://research.swtch.com/field. +// +// For an optimized implementation, see gf256.go. + +package gf256 + +import ( + "bytes" + "fmt" + "testing" +) + +// BlogECC writes to check the error correcting code bytes +// for data using the given Reed-Solomon parameters. +func BlogECC(rs *RSEncoder, m []byte, check []byte) { + if len(check) < rs.c { + panic("gf256: invalid check byte length") + } + if rs.c == 0 { + return + } + + // The check bytes are the remainder after dividing + // data padded with c zeros by the generator polynomial. + + // p = data padded with c zeros. + var p []byte + n := len(m) + rs.c + if len(rs.p) >= n { + p = rs.p + } else { + p = make([]byte, n) + } + copy(p, m) + for i := len(m); i < len(p); i++ { + p[i] = 0 + } + + gen := rs.gen + + // Divide p by gen, leaving the remainder in p[len(data):]. + // p[0] is the most significant term in p, and + // gen[0] is the most significant term in the generator. + for i := 0; i < len(m); i++ { + k := f.Mul(p[i], f.Inv(gen[0])) // k = pi / g0 + // p -= k·g + for j, g := range gen { + p[i+j] = f.Add(p[i+j], f.Mul(k, g)) + } + } + + copy(check, p[len(m):]) + rs.p = p +} + +func BenchmarkBlogECC(b *testing.B) { + data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} + check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f} + out := make([]byte, len(check)) + rs := NewRSEncoder(f, len(check)) + for i := 0; i < b.N; i++ { + BlogECC(rs, data, out) + } + b.SetBytes(int64(len(data))) + if !bytes.Equal(out, check) { + fmt.Printf("have %#v want %#v\n", out, check) + } +} + +func TestBlogECC(t *testing.T) { + data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} + check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} + out := make([]byte, len(check)) + rs := NewRSEncoder(f, len(check)) + BlogECC(rs, data, out) + if !bytes.Equal(out, check) { + t.Errorf("have %x want %x", out, check) + } +} diff --git a/vendor/rsc.io/qr/gf256/gf256.go b/vendor/rsc.io/qr/gf256/gf256.go new file mode 100644 index 0000000..bfeeeb3 --- /dev/null +++ b/vendor/rsc.io/qr/gf256/gf256.go @@ -0,0 +1,241 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gf256 implements arithmetic over the Galois Field GF(256). +package gf256 + +import "strconv" + +// A Field represents an instance of GF(256) defined by a specific polynomial. +type Field struct { + log [256]byte // log[0] is unused + exp [510]byte +} + +// NewField returns a new field corresponding to the polynomial poly +// and generator α. The Reed-Solomon encoding in QR codes uses +// polynomial 0x11d with generator 2. +// +// The choice of generator α only affects the Exp and Log operations. +func NewField(poly, α int) *Field { + if poly < 0x100 || poly >= 0x200 || reducible(poly) { + panic("gf256: invalid polynomial: " + strconv.Itoa(poly)) + } + + var f Field + x := 1 + for i := 0; i < 255; i++ { + if x == 1 && i != 0 { + panic("gf256: invalid generator " + strconv.Itoa(α) + + " for polynomial " + strconv.Itoa(poly)) + } + f.exp[i] = byte(x) + f.exp[i+255] = byte(x) + f.log[x] = byte(i) + x = mul(x, α, poly) + } + f.log[0] = 255 + for i := 0; i < 255; i++ { + if f.log[f.exp[i]] != byte(i) { + panic("bad log") + } + if f.log[f.exp[i+255]] != byte(i) { + panic("bad log") + } + } + for i := 1; i < 256; i++ { + if f.exp[f.log[i]] != byte(i) { + panic("bad log") + } + } + + return &f +} + +// nbit returns the number of significant in p. +func nbit(p int) uint { + n := uint(0) + for ; p > 0; p >>= 1 { + n++ + } + return n +} + +// polyDiv divides the polynomial p by q and returns the remainder. +func polyDiv(p, q int) int { + np := nbit(p) + nq := nbit(q) + for ; np >= nq; np-- { + if p&(1<<(np-1)) != 0 { + p ^= q << (np - nq) + } + } + return p +} + +// mul returns the product x*y mod poly, a GF(256) multiplication. +func mul(x, y, poly int) int { + z := 0 + for x > 0 { + if x&1 != 0 { + z ^= y + } + x >>= 1 + y <<= 1 + if y&0x100 != 0 { + y ^= poly + } + } + return z +} + +// reducible reports whether p is reducible. +func reducible(p int) bool { + // Multiplying n-bit * n-bit produces (2n-1)-bit, + // so if p is reducible, one of its factors must be + // of np/2+1 bits or fewer. + np := nbit(p) + for q := 2; q < 1<<(np/2+1); q++ { + if polyDiv(p, q) == 0 { + return true + } + } + return false +} + +// Add returns the sum of x and y in the field. +func (f *Field) Add(x, y byte) byte { + return x ^ y +} + +// Exp returns the base-α exponential of e in the field. +// If e < 0, Exp returns 0. +func (f *Field) Exp(e int) byte { + if e < 0 { + return 0 + } + return f.exp[e%255] +} + +// Log returns the base-α logarithm of x in the field. +// If x == 0, Log returns -1. +func (f *Field) Log(x byte) int { + if x == 0 { + return -1 + } + return int(f.log[x]) +} + +// Inv returns the multiplicative inverse of x in the field. +// If x == 0, Inv returns 0. +func (f *Field) Inv(x byte) byte { + if x == 0 { + return 0 + } + return f.exp[255-f.log[x]] +} + +// Mul returns the product of x and y in the field. +func (f *Field) Mul(x, y byte) byte { + if x == 0 || y == 0 { + return 0 + } + return f.exp[int(f.log[x])+int(f.log[y])] +} + +// An RSEncoder implements Reed-Solomon encoding +// over a given field using a given number of error correction bytes. +type RSEncoder struct { + f *Field + c int + gen []byte + lgen []byte + p []byte +} + +func (f *Field) gen(e int) (gen, lgen []byte) { + // p = 1 + p := make([]byte, e+1) + p[e] = 1 + + for i := 0; i < e; i++ { + // p *= (x + Exp(i)) + // p[j] = p[j]*Exp(i) + p[j+1]. + c := f.Exp(i) + for j := 0; j < e; j++ { + p[j] = f.Mul(p[j], c) ^ p[j+1] + } + p[e] = f.Mul(p[e], c) + } + + // lp = log p. + lp := make([]byte, e+1) + for i, c := range p { + if c == 0 { + lp[i] = 255 + } else { + lp[i] = byte(f.Log(c)) + } + } + + return p, lp +} + +// NewRSEncoder returns a new Reed-Solomon encoder +// over the given field and number of error correction bytes. +func NewRSEncoder(f *Field, c int) *RSEncoder { + gen, lgen := f.gen(c) + return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen} +} + +// ECC writes to check the error correcting code bytes +// for data using the given Reed-Solomon parameters. +func (rs *RSEncoder) ECC(data []byte, check []byte) { + if len(check) < rs.c { + panic("gf256: invalid check byte length") + } + if rs.c == 0 { + return + } + + // The check bytes are the remainder after dividing + // data padded with c zeros by the generator polynomial. + + // p = data padded with c zeros. + var p []byte + n := len(data) + rs.c + if len(rs.p) >= n { + p = rs.p + } else { + p = make([]byte, n) + } + copy(p, data) + for i := len(data); i < len(p); i++ { + p[i] = 0 + } + + // Divide p by gen, leaving the remainder in p[len(data):]. + // p[0] is the most significant term in p, and + // gen[0] is the most significant term in the generator, + // which is always 1. + // To avoid repeated work, we store various values as + // lv, not v, where lv = log[v]. + f := rs.f + lgen := rs.lgen[1:] + for i := 0; i < len(data); i++ { + c := p[i] + if c == 0 { + continue + } + q := p[i+1:] + exp := f.exp[f.log[c]:] + for j, lg := range lgen { + if lg != 255 { // lgen uses 255 for log 0 + q[j] ^= exp[lg] + } + } + } + copy(check, p[len(data):]) + rs.p = p +} diff --git a/vendor/rsc.io/qr/gf256/gf256_test.go b/vendor/rsc.io/qr/gf256/gf256_test.go new file mode 100644 index 0000000..f77fa7d --- /dev/null +++ b/vendor/rsc.io/qr/gf256/gf256_test.go @@ -0,0 +1,194 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gf256 + +import ( + "bytes" + "fmt" + "testing" +) + +var f = NewField(0x11d, 2) // x^8 + x^4 + x^3 + x^2 + 1 + +func TestBasic(t *testing.T) { + if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 { + panic("bad Exp") + } +} + +func TestECC(t *testing.T) { + data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} + check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55} + out := make([]byte, len(check)) + rs := NewRSEncoder(f, len(check)) + rs.ECC(data, out) + if !bytes.Equal(out, check) { + t.Errorf("have %x want %x", out, check) + } +} + +func TestLinear(t *testing.T) { + d1 := []byte{0x00, 0x00} + c1 := []byte{0x00, 0x00} + out := make([]byte, len(c1)) + rs := NewRSEncoder(f, len(c1)) + if rs.ECC(d1, out); !bytes.Equal(out, c1) { + t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out) + } + d2 := []byte{0x00, 0x01} + c2 := make([]byte, 2) + rs.ECC(d2, c2) + d3 := []byte{0x00, 0x02} + c3 := make([]byte, 2) + rs.ECC(d3, c3) + cx := make([]byte, 2) + for i := range cx { + cx[i] = c2[i] ^ c3[i] + } + d4 := []byte{0x00, 0x03} + c4 := make([]byte, 2) + rs.ECC(d4, c4) + if !bytes.Equal(cx, c4) { + t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x", + d2, c2, d3, c3, cx, d4, c4) + } +} + +func TestGaussJordan(t *testing.T) { + rs := NewRSEncoder(f, 2) + m := make([][]byte, 16) + for i := range m { + m[i] = make([]byte, 4) + m[i][i/8] = 1 << uint(i%8) + rs.ECC(m[i][:2], m[i][2:]) + } + if false { + fmt.Printf("---\n") + for _, row := range m { + fmt.Printf("%x\n", row) + } + } + b := []uint{0, 1, 2, 3, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27} + for i := 0; i < 16; i++ { + bi := b[i] + if m[i][bi/8]&(1<<(7-bi%8)) == 0 { + for j := i + 1; ; j++ { + if j >= len(m) { + t.Errorf("lost track for %d", bi) + break + } + if m[j][bi/8]&(1<<(7-bi%8)) != 0 { + m[i], m[j] = m[j], m[i] + break + } + } + } + for j := i + 1; j < len(m); j++ { + if m[j][bi/8]&(1<<(7-bi%8)) != 0 { + for k := range m[j] { + m[j][k] ^= m[i][k] + } + } + } + } + if false { + fmt.Printf("---\n") + for _, row := range m { + fmt.Printf("%x\n", row) + } + } + for i := 15; i >= 0; i-- { + bi := b[i] + for j := i - 1; j >= 0; j-- { + if m[j][bi/8]&(1<<(7-bi%8)) != 0 { + for k := range m[j] { + m[j][k] ^= m[i][k] + } + } + } + } + if false { + fmt.Printf("---\n") + for _, row := range m { + fmt.Printf("%x", row) + out := make([]byte, 2) + if rs.ECC(row[:2], out); !bytes.Equal(out, row[2:]) { + fmt.Printf(" - want %x", out) + } + fmt.Printf("\n") + } + } +} + +func BenchmarkECC(b *testing.B) { + data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11} + check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f} + out := make([]byte, len(check)) + rs := NewRSEncoder(f, len(check)) + for i := 0; i < b.N; i++ { + rs.ECC(data, out) + } + b.SetBytes(int64(len(data))) + if !bytes.Equal(out, check) { + fmt.Printf("have %#v want %#v\n", out, check) + } +} + +func TestGen(t *testing.T) { + for i := 0; i < 256; i++ { + _, lg := f.gen(i) + if lg[0] != 0 { + t.Errorf("#%d: %x", i, lg) + } + } +} + +func TestReducible(t *testing.T) { + var count = []int{1, 2, 3, 6, 9, 18, 30, 56, 99, 186} // oeis.org/A1037 + for i, want := range count { + n := 0 + for p := 1 << uint(i+2); p < 1< +*/ +import "C" + +import ( + "fmt" + "image" + "image/color" + "unsafe" +) + +type Version int + +type Mode int + +const ( + Numeric Mode = C.QR_MODE_NUM + Alphanumeric Mode = C.QR_MODE_AN + EightBit Mode = C.QR_MODE_8 +) + +type Level int + +const ( + L Level = C.QR_ECLEVEL_L + M Level = C.QR_ECLEVEL_M + Q Level = C.QR_ECLEVEL_Q + H Level = C.QR_ECLEVEL_H +) + +type Pixel int + +const ( + Black Pixel = 1 << iota + DataECC + Format + PVersion + Timing + Alignment + Finder + NonData +) + +type Code struct { + Version int + Width int + Pixel [][]Pixel + Scale int +} + +func (*Code) ColorModel() color.Model { + return color.RGBAModel +} + +func (c *Code) Bounds() image.Rectangle { + d := (c.Width + 8) * c.Scale + return image.Rect(0, 0, d, d) +} + +var ( + white color.Color = color.RGBA{0xFF, 0xFF, 0xFF, 0xFF} + black color.Color = color.RGBA{0x00, 0x00, 0x00, 0xFF} + blue color.Color = color.RGBA{0x00, 0x00, 0x80, 0xFF} + red color.Color = color.RGBA{0xFF, 0x40, 0x40, 0xFF} + yellow color.Color = color.RGBA{0xFF, 0xFF, 0x00, 0xFF} + gray color.Color = color.RGBA{0x80, 0x80, 0x80, 0xFF} + green color.Color = color.RGBA{0x22, 0x8B, 0x22, 0xFF} +) + +func (c *Code) At(x, y int) color.Color { + x = x/c.Scale - 4 + y = y/c.Scale - 4 + if 0 <= x && x < c.Width && 0 <= y && y < c.Width { + switch p := c.Pixel[y][x]; { + case p&Black == 0: + // nothing + case p&DataECC != 0: + return black + case p&Format != 0: + return blue + case p&PVersion != 0: + return red + case p&Timing != 0: + return yellow + case p&Alignment != 0: + return gray + case p&Finder != 0: + return green + } + } + return white +} + +type Chunk struct { + Mode Mode + Text string +} + +func Encode(version Version, level Level, mode Mode, text string) (*Code, error) { + return EncodeChunk(version, level, Chunk{mode, text}) +} + +func EncodeChunk(version Version, level Level, chunk ...Chunk) (*Code, error) { + qi, err := C.QRinput_new2(C.int(version), C.QRecLevel(level)) + if qi == nil { + return nil, fmt.Errorf("QRinput_new2: %v", err) + } + defer C.QRinput_free(qi) + for _, ch := range chunk { + data := []byte(ch.Text) + n, err := C.QRinput_append(qi, C.QRencodeMode(ch.Mode), C.int(len(data)), (*C.uchar)(&data[0])) + if n < 0 { + return nil, fmt.Errorf("QRinput_append %q: %v", data, err) + } + } + + qc, err := C.QRcode_encodeInput(qi) + if qc == nil { + return nil, fmt.Errorf("QRinput_encodeInput: %v", err) + } + + c := &Code{ + Version: int(qc.version), + Width: int(qc.width), + Scale: 16, + } + pix := make([]Pixel, c.Width*c.Width) + cdat := (*[1000 * 1000]byte)(unsafe.Pointer(qc.data))[:len(pix)] + for i := range pix { + pix[i] = Pixel(cdat[i]) + } + c.Pixel = make([][]Pixel, c.Width) + for i := range c.Pixel { + c.Pixel[i] = pix[i*c.Width : (i+1)*c.Width] + } + return c, nil +} diff --git a/vendor/rsc.io/qr/png.go b/vendor/rsc.io/qr/png.go new file mode 100644 index 0000000..db49d05 --- /dev/null +++ b/vendor/rsc.io/qr/png.go @@ -0,0 +1,400 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qr + +// PNG writer for QR codes. + +import ( + "bytes" + "encoding/binary" + "hash" + "hash/crc32" +) + +// PNG returns a PNG image displaying the code. +// +// PNG uses a custom encoder tailored to QR codes. +// Its compressed size is about 2x away from optimal, +// but it runs about 20x faster than calling png.Encode +// on c.Image(). +func (c *Code) PNG() []byte { + var p pngWriter + return p.encode(c) +} + +type pngWriter struct { + tmp [16]byte + wctmp [4]byte + buf bytes.Buffer + zlib bitWriter + crc hash.Hash32 +} + +var pngHeader = []byte("\x89PNG\r\n\x1a\n") + +func (w *pngWriter) encode(c *Code) []byte { + scale := c.Scale + siz := c.Size + + w.buf.Reset() + + // Header + w.buf.Write(pngHeader) + + // Header block + binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale)) + binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale)) + w.tmp[8] = 1 // 1-bit + w.tmp[9] = 0 // gray + w.tmp[10] = 0 + w.tmp[11] = 0 + w.tmp[12] = 0 + w.writeChunk("IHDR", w.tmp[:13]) + + // Comment + w.writeChunk("tEXt", comment) + + // Data + w.zlib.writeCode(c) + w.writeChunk("IDAT", w.zlib.bytes.Bytes()) + + // End + w.writeChunk("IEND", nil) + + return w.buf.Bytes() +} + +var comment = []byte("Software\x00QR-PNG http://qr.swtch.com/") + +func (w *pngWriter) writeChunk(name string, data []byte) { + if w.crc == nil { + w.crc = crc32.NewIEEE() + } + binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data))) + w.buf.Write(w.wctmp[0:4]) + w.crc.Reset() + copy(w.wctmp[0:4], name) + w.buf.Write(w.wctmp[0:4]) + w.crc.Write(w.wctmp[0:4]) + w.buf.Write(data) + w.crc.Write(data) + crc := w.crc.Sum32() + binary.BigEndian.PutUint32(w.wctmp[0:4], crc) + w.buf.Write(w.wctmp[0:4]) +} + +func (b *bitWriter) writeCode(c *Code) { + const ftNone = 0 + + b.adler32.Reset() + b.bytes.Reset() + b.nbit = 0 + + scale := c.Scale + siz := c.Size + + // zlib header + b.tmp[0] = 0x78 + b.tmp[1] = 0 + b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31) + b.bytes.Write(b.tmp[0:2]) + + // Start flate block. + b.writeBits(1, 1, false) // final block + b.writeBits(1, 2, false) // compressed, fixed Huffman tables + + // White border. + // First row. + b.byte(ftNone) + n := (scale*(siz+8) + 7) / 8 + b.byte(255) + b.repeat(n-1, 1) + // 4*scale rows total. + b.repeat((4*scale-1)*(1+n), 1+n) + + for i := 0; i < 4*scale; i++ { + b.adler32.WriteNByte(ftNone, 1) + b.adler32.WriteNByte(255, n) + } + + row := make([]byte, 1+n) + for y := 0; y < siz; y++ { + row[0] = ftNone + j := 1 + var z uint8 + nz := 0 + for x := -4; x < siz+4; x++ { + // Raw data. + for i := 0; i < scale; i++ { + z <<= 1 + if !c.Black(x, y) { + z |= 1 + } + if nz++; nz == 8 { + row[j] = z + j++ + nz = 0 + } + } + } + if j < len(row) { + row[j] = z + } + for _, z := range row { + b.byte(z) + } + + // Scale-1 copies. + b.repeat((scale-1)*(1+n), 1+n) + + b.adler32.WriteN(row, scale) + } + + // White border. + // First row. + b.byte(ftNone) + b.byte(255) + b.repeat(n-1, 1) + // 4*scale rows total. + b.repeat((4*scale-1)*(1+n), 1+n) + + for i := 0; i < 4*scale; i++ { + b.adler32.WriteNByte(ftNone, 1) + b.adler32.WriteNByte(255, n) + } + + // End of block. + b.hcode(256) + b.flushBits() + + // adler32 + binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32()) + b.bytes.Write(b.tmp[0:4]) +} + +// A bitWriter is a write buffer for bit-oriented data like deflate. +type bitWriter struct { + bytes bytes.Buffer + bit uint32 + nbit uint + + tmp [4]byte + adler32 adigest +} + +func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) { + // reverse, for huffman codes + if rev { + br := uint32(0) + for i := uint(0); i < nbit; i++ { + br |= ((bit >> i) & 1) << (nbit - 1 - i) + } + bit = br + } + b.bit |= bit << b.nbit + b.nbit += nbit + for b.nbit >= 8 { + b.bytes.WriteByte(byte(b.bit)) + b.bit >>= 8 + b.nbit -= 8 + } +} + +func (b *bitWriter) flushBits() { + if b.nbit > 0 { + b.bytes.WriteByte(byte(b.bit)) + b.nbit = 0 + b.bit = 0 + } +} + +func (b *bitWriter) hcode(v int) { + /* + Lit Value Bits Codes + --------- ---- ----- + 0 - 143 8 00110000 through + 10111111 + 144 - 255 9 110010000 through + 111111111 + 256 - 279 7 0000000 through + 0010111 + 280 - 287 8 11000000 through + 11000111 + */ + switch { + case v <= 143: + b.writeBits(uint32(v)+0x30, 8, true) + case v <= 255: + b.writeBits(uint32(v-144)+0x190, 9, true) + case v <= 279: + b.writeBits(uint32(v-256)+0, 7, true) + case v <= 287: + b.writeBits(uint32(v-280)+0xc0, 8, true) + default: + panic("invalid hcode") + } +} + +func (b *bitWriter) byte(x byte) { + b.hcode(int(x)) +} + +func (b *bitWriter) codex(c int, val int, nx uint) { + b.hcode(c + val>>nx) + b.writeBits(uint32(val)&(1<= 258+3; n -= 258 { + b.repeat1(258, d) + } + if n > 258 { + // 258 < n < 258+3 + b.repeat1(10, d) + b.repeat1(n-10, d) + return + } + if n < 3 { + panic("invalid flate repeat") + } + b.repeat1(n, d) +} + +func (b *bitWriter) repeat1(n, d int) { + /* + Extra Extra Extra + Code Bits Length(s) Code Bits Lengths Code Bits Length(s) + ---- ---- ------ ---- ---- ------- ---- ---- ------- + 257 0 3 267 1 15,16 277 4 67-82 + 258 0 4 268 1 17,18 278 4 83-98 + 259 0 5 269 2 19-22 279 4 99-114 + 260 0 6 270 2 23-26 280 4 115-130 + 261 0 7 271 2 27-30 281 5 131-162 + 262 0 8 272 2 31-34 282 5 163-194 + 263 0 9 273 3 35-42 283 5 195-226 + 264 0 10 274 3 43-50 284 5 227-257 + 265 1 11,12 275 3 51-58 285 0 258 + 266 1 13,14 276 3 59-66 + */ + switch { + case n <= 10: + b.codex(257, n-3, 0) + case n <= 18: + b.codex(265, n-11, 1) + case n <= 34: + b.codex(269, n-19, 2) + case n <= 66: + b.codex(273, n-35, 3) + case n <= 130: + b.codex(277, n-67, 4) + case n <= 257: + b.codex(281, n-131, 5) + case n == 258: + b.hcode(285) + default: + panic("invalid repeat length") + } + + /* + Extra Extra Extra + Code Bits Dist Code Bits Dist Code Bits Distance + ---- ---- ---- ---- ---- ------ ---- ---- -------- + 0 0 1 10 4 33-48 20 9 1025-1536 + 1 0 2 11 4 49-64 21 9 1537-2048 + 2 0 3 12 5 65-96 22 10 2049-3072 + 3 0 4 13 5 97-128 23 10 3073-4096 + 4 1 5,6 14 6 129-192 24 11 4097-6144 + 5 1 7,8 15 6 193-256 25 11 6145-8192 + 6 2 9-12 16 7 257-384 26 12 8193-12288 + 7 2 13-16 17 7 385-512 27 12 12289-16384 + 8 3 17-24 18 8 513-768 28 13 16385-24576 + 9 3 25-32 19 8 769-1024 29 13 24577-32768 + */ + if d <= 4 { + b.writeBits(uint32(d-1), 5, true) + } else if d <= 32768 { + nbit := uint(16) + for d <= 1<<(nbit-1) { + nbit-- + } + v := uint32(d - 1) + v &^= 1 << (nbit - 1) // top bit is implicit + code := uint32(2*nbit - 2) // second bit is low bit of code + code |= v >> (nbit - 2) + v &^= 1 << (nbit - 2) + b.writeBits(code, 5, true) + // rest of bits follow + b.writeBits(uint32(v), nbit-2, false) + } else { + panic("invalid repeat distance") + } +} + +func (b *bitWriter) run(v byte, n int) { + if n == 0 { + return + } + b.byte(v) + if n-1 < 3 { + for i := 0; i < n-1; i++ { + b.byte(v) + } + } else { + b.repeat(n-1, 1) + } +} + +type adigest struct { + a, b uint32 +} + +func (d *adigest) Reset() { d.a, d.b = 1, 0 } + +const amod = 65521 + +func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) { + // TODO(rsc): 6g doesn't do magic multiplies for b %= amod, + // only for b = b%amod. + + // invariant: a, b < amod + if pi == 0 { + b += uint32(n%amod) * a + b = b % amod + return a, b + } + + // n times: + // a += pi + // b += a + // is same as + // b += n*a + n*(n+1)/2*pi + // a += n*pi + m := uint32(n) + b += (m % amod) * a + b = b % amod + b += (m * (m + 1) / 2) % amod * uint32(pi) + b = b % amod + a += (m % amod) * uint32(pi) + a = a % amod + return a, b +} + +func afinish(a, b uint32) uint32 { + return b<<16 | a +} + +func (d *adigest) WriteN(p []byte, n int) { + for i := 0; i < n; i++ { + for _, pi := range p { + d.a, d.b = aupdate(d.a, d.b, pi, 1) + } + } +} + +func (d *adigest) WriteNByte(pi byte, n int) { + d.a, d.b = aupdate(d.a, d.b, pi, n) +} + +func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) } diff --git a/vendor/rsc.io/qr/png_test.go b/vendor/rsc.io/qr/png_test.go new file mode 100644 index 0000000..27a6229 --- /dev/null +++ b/vendor/rsc.io/qr/png_test.go @@ -0,0 +1,73 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package qr + +import ( + "bytes" + "image" + "image/color" + "image/png" + "io/ioutil" + "testing" +) + +func TestPNG(t *testing.T) { + c, err := Encode("hello, world", L) + if err != nil { + t.Fatal(err) + } + pngdat := c.PNG() + if true { + ioutil.WriteFile("x.png", pngdat, 0666) + } + m, err := png.Decode(bytes.NewBuffer(pngdat)) + if err != nil { + t.Fatal(err) + } + gm := m.(*image.Gray) + + scale := c.Scale + siz := c.Size + nbad := 0 + for y := 0; y < scale*(8+siz); y++ { + for x := 0; x < scale*(8+siz); x++ { + v := byte(255) + if c.Black(x/scale-4, y/scale-4) { + v = 0 + } + if gv := gm.At(x, y).(color.Gray).Y; gv != v { + t.Errorf("%d,%d = %d, want %d", x, y, gv, v) + if nbad++; nbad >= 20 { + t.Fatalf("too many bad pixels") + } + } + } + } +} + +func BenchmarkPNG(b *testing.B) { + c, err := Encode("0123456789012345678901234567890123456789", L) + if err != nil { + panic(err) + } + var bytes []byte + for i := 0; i < b.N; i++ { + bytes = c.PNG() + } + b.SetBytes(int64(len(bytes))) +} + +func BenchmarkImagePNG(b *testing.B) { + c, err := Encode("0123456789012345678901234567890123456789", L) + if err != nil { + panic(err) + } + var buf bytes.Buffer + for i := 0; i < b.N; i++ { + buf.Reset() + png.Encode(&buf, c.Image()) + } + b.SetBytes(int64(buf.Len())) +} diff --git a/vendor/rsc.io/qr/qr.go b/vendor/rsc.io/qr/qr.go new file mode 100644 index 0000000..254b532 --- /dev/null +++ b/vendor/rsc.io/qr/qr.go @@ -0,0 +1,116 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package qr encodes QR codes. +*/ +package qr + +import ( + "errors" + "image" + "image/color" + + "rsc.io/qr/coding" +) + +// A Level denotes a QR error correction level. +// From least to most tolerant of errors, they are L, M, Q, H. +type Level int + +const ( + L Level = iota // 20% redundant + M // 38% redundant + Q // 55% redundant + H // 65% redundant +) + +// Encode returns an encoding of text at the given error correction level. +func Encode(text string, level Level) (*Code, error) { + // Pick data encoding, smallest first. + // We could split the string and use different encodings + // but that seems like overkill for now. + var enc coding.Encoding + switch { + case coding.Num(text).Check() == nil: + enc = coding.Num(text) + case coding.Alpha(text).Check() == nil: + enc = coding.Alpha(text) + default: + enc = coding.String(text) + } + + // Pick size. + l := coding.Level(level) + var v coding.Version + for v = coding.MinVersion; ; v++ { + if v > coding.MaxVersion { + return nil, errors.New("text too long to encode as QR") + } + if enc.Bits(v) <= v.DataBytes(l)*8 { + break + } + } + + // Build and execute plan. + p, err := coding.NewPlan(v, l, 0) + if err != nil { + return nil, err + } + cc, err := p.Encode(enc) + if err != nil { + return nil, err + } + + // TODO: Pick appropriate mask. + + return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil +} + +// A Code is a square pixel grid. +// It implements image.Image and direct PNG encoding. +type Code struct { + Bitmap []byte // 1 is black, 0 is white + Size int // number of pixels on a side + Stride int // number of bytes per row + Scale int // number of image pixels per QR pixel +} + +// Black returns true if the pixel at (x,y) is black. +func (c *Code) Black(x, y int) bool { + return 0 <= x && x < c.Size && 0 <= y && y < c.Size && + c.Bitmap[y*c.Stride+x/8]&(1< Date: Sun, 17 Sep 2017 17:01:52 -0600 Subject: [PATCH 8/8] properly reconstruct url, add a clean target in makefile --- browserpass.go | 3 ++- makefile | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/browserpass.go b/browserpass.go index 4b1e766..3281b2b 100644 --- a/browserpass.go +++ b/browserpass.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "encoding/json" "errors" + "fmt" "io" "net/url" "os/exec" @@ -152,7 +153,7 @@ func parseTotp(str string, l *Login) error { v := u.Query() v.Set("secret", strings.ToUpper(v.Get("secret"))) - ourl = u.String() + ourl = fmt.Sprintf("%s://%s/%s?%s", u.Scheme, u.Host, u.Path[1:], v.Encode()) o, label, err := twofactor.FromURL(ourl) if err != nil { diff --git a/makefile b/makefile index dbfeaf9..434dede 100644 --- a/makefile +++ b/makefile @@ -23,7 +23,7 @@ firefox: .PHONY: js js: $(JS_OUTPUT) -chrome/script.js: chrome/script.browserify.js +chrome/script.js: chrome/script.browserify.js browserify chrome/script.browserify.js -o chrome/script.js chrome/inject.js: chrome/inject.browserify.js @@ -49,6 +49,10 @@ browserpass-openbsd64: cmd/browserpass/ pass/ browserpass.go browserpass-freebsd64: cmd/browserpass/ pass/ browserpass.go env GOOS=freebsd GOARCH=amd64 go build -o $@ ./cmd/browserpass +clean: + rm -f browserpass-* + rm -rf release + .PHONY: static-files chrome firefox release: static-files chrome firefox browserpass-linux64 browserpass-darwinx64 browserpass-openbsd64 browserpass-freebsd64 mkdir -p release