Parcourir la source

vendor: add packages for 2fa

Unknwon il y a 8 ans
Parent
commit
c8c975c99b

+ 21 - 0
vendor/github.com/boombuler/barcode/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Florian Sundermann
+
+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.

+ 18 - 0
vendor/github.com/boombuler/barcode/README.md

@@ -0,0 +1,18 @@
+##Introduction##
+This is a package for GO which can be used to create different types of barcodes.
+
+##Supported Barcode Types##
+* Aztec Code
+* Codabar
+* Code 128
+* Code 39
+* EAN 8
+* EAN 13
+* Datamatrix
+* QR Codes
+* 2 of 5
+
+##Documentation##
+See [GoDoc](https://godoc.org/github.com/boombuler/barcode)
+
+To create a barcode use the Encode function from one of the subpackages.

+ 27 - 0
vendor/github.com/boombuler/barcode/barcode.go

@@ -0,0 +1,27 @@
+package barcode
+
+import "image"
+
+// Contains some meta information about a barcode
+type Metadata struct {
+	// the name of the barcode kind
+	CodeKind string
+	// contains 1 for 1D barcodes or 2 for 2D barcodes
+	Dimensions byte
+}
+
+// a rendered and encoded barcode
+type Barcode interface {
+	image.Image
+	// returns some meta information about the barcode
+	Metadata() Metadata
+	// the data that was encoded in this barcode
+	Content() string
+}
+
+// Additional interface that some barcodes might implement to provide
+// the value of its checksum.
+type BarcodeIntCS interface {
+	Barcode
+	CheckSum() int
+}

+ 66 - 0
vendor/github.com/boombuler/barcode/qr/alphanumeric.go

@@ -0,0 +1,66 @@
+package qr
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+
+	"github.com/boombuler/barcode/utils"
+)
+
+const charSet string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
+
+func stringToAlphaIdx(content string) <-chan int {
+	result := make(chan int)
+	go func() {
+		for _, r := range content {
+			idx := strings.IndexRune(charSet, r)
+			result <- idx
+			if idx < 0 {
+				break
+			}
+		}
+		close(result)
+	}()
+
+	return result
+}
+
+func encodeAlphaNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
+
+	contentLenIsOdd := len(content)%2 == 1
+	contentBitCount := (len(content) / 2) * 11
+	if contentLenIsOdd {
+		contentBitCount += 6
+	}
+	vi := findSmallestVersionInfo(ecl, alphaNumericMode, contentBitCount)
+	if vi == nil {
+		return nil, nil, errors.New("To much data to encode")
+	}
+
+	res := new(utils.BitList)
+	res.AddBits(int(alphaNumericMode), 4)
+	res.AddBits(len(content), vi.charCountBits(alphaNumericMode))
+
+	encoder := stringToAlphaIdx(content)
+
+	for idx := 0; idx < len(content)/2; idx++ {
+		c1 := <-encoder
+		c2 := <-encoder
+		if c1 < 0 || c2 < 0 {
+			return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric)
+		}
+		res.AddBits(c1*45+c2, 11)
+	}
+	if contentLenIsOdd {
+		c := <-encoder
+		if c < 0 {
+			return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, AlphaNumeric)
+		}
+		res.AddBits(c, 6)
+	}
+
+	addPaddingAndTerminator(res, vi)
+
+	return res, vi, nil
+}

+ 23 - 0
vendor/github.com/boombuler/barcode/qr/automatic.go

@@ -0,0 +1,23 @@
+package qr
+
+import (
+	"fmt"
+
+	"github.com/boombuler/barcode/utils"
+)
+
+func encodeAuto(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
+	bits, vi, _ := Numeric.getEncoder()(content, ecl)
+	if bits != nil && vi != nil {
+		return bits, vi, nil
+	}
+	bits, vi, _ = AlphaNumeric.getEncoder()(content, ecl)
+	if bits != nil && vi != nil {
+		return bits, vi, nil
+	}
+	bits, vi, _ = Unicode.getEncoder()(content, ecl)
+	if bits != nil && vi != nil {
+		return bits, vi, nil
+	}
+	return nil, nil, fmt.Errorf("No encoding found to encode \"%s\"", content)
+}

+ 59 - 0
vendor/github.com/boombuler/barcode/qr/blocks.go

@@ -0,0 +1,59 @@
+package qr
+
+type block struct {
+	data []byte
+	ecc  []byte
+}
+type blockList []*block
+
+func splitToBlocks(data <-chan byte, vi *versionInfo) blockList {
+	result := make(blockList, vi.NumberOfBlocksInGroup1+vi.NumberOfBlocksInGroup2)
+
+	for b := 0; b < int(vi.NumberOfBlocksInGroup1); b++ {
+		blk := new(block)
+		blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup1)
+		for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup1); cw++ {
+			blk.data[cw] = <-data
+		}
+		blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock)
+		result[b] = blk
+	}
+
+	for b := 0; b < int(vi.NumberOfBlocksInGroup2); b++ {
+		blk := new(block)
+		blk.data = make([]byte, vi.DataCodeWordsPerBlockInGroup2)
+		for cw := 0; cw < int(vi.DataCodeWordsPerBlockInGroup2); cw++ {
+			blk.data[cw] = <-data
+		}
+		blk.ecc = ec.calcECC(blk.data, vi.ErrorCorrectionCodewordsPerBlock)
+		result[int(vi.NumberOfBlocksInGroup1)+b] = blk
+	}
+
+	return result
+}
+
+func (bl blockList) interleave(vi *versionInfo) []byte {
+	var maxCodewordCount int
+	if vi.DataCodeWordsPerBlockInGroup1 > vi.DataCodeWordsPerBlockInGroup2 {
+		maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup1)
+	} else {
+		maxCodewordCount = int(vi.DataCodeWordsPerBlockInGroup2)
+	}
+	resultLen := (vi.DataCodeWordsPerBlockInGroup1+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup1 +
+		(vi.DataCodeWordsPerBlockInGroup2+vi.ErrorCorrectionCodewordsPerBlock)*vi.NumberOfBlocksInGroup2
+
+	result := make([]byte, 0, resultLen)
+	for i := 0; i < maxCodewordCount; i++ {
+		for b := 0; b < len(bl); b++ {
+			if len(bl[b].data) > i {
+				result = append(result, bl[b].data[i])
+			}
+		}
+	}
+	for i := 0; i < int(vi.ErrorCorrectionCodewordsPerBlock); i++ {
+		for b := 0; b < len(bl); b++ {
+			result = append(result, bl[b].ecc[i])
+		}
+	}
+	return result
+}

+ 416 - 0
vendor/github.com/boombuler/barcode/qr/encoder.go

@@ -0,0 +1,416 @@
+// Package qr can be used to create QR barcodes.
+package qr
+
+import (
+	"image"
+
+	"github.com/boombuler/barcode"
+	"github.com/boombuler/barcode/utils"
+)
+
+type encodeFn func(content string, eccLevel ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error)
+
+// Encoding mode for QR Codes.
+type Encoding byte
+
+const (
+	// Auto will choose ths best matching encoding
+	Auto Encoding = iota
+	// Numeric encoding only encodes numbers [0-9]
+	Numeric
+	// AlphaNumeric encoding only encodes uppercase letters, numbers and  [Space], $, %, *, +, -, ., /, :
+	AlphaNumeric
+	// Unicode encoding encodes the string as utf-8
+	Unicode
+	// only for testing purpose
+	unknownEncoding
+)
+
+func (e Encoding) getEncoder() encodeFn {
+	switch e {
+	case Auto:
+		return encodeAuto
+	case Numeric:
+		return encodeNumeric
+	case AlphaNumeric:
+		return encodeAlphaNumeric
+	case Unicode:
+		return encodeUnicode
+	}
+	return nil
+}
+
+func (e Encoding) String() string {
+	switch e {
+	case Auto:
+		return "Auto"
+	case Numeric:
+		return "Numeric"
+	case AlphaNumeric:
+		return "AlphaNumeric"
+	case Unicode:
+		return "Unicode"
+	}
+	return ""
+}
+
+// Encode returns a QR barcode with the given content, error correction level and uses the given encoding
+func Encode(content string, level ErrorCorrectionLevel, mode Encoding) (barcode.Barcode, error) {
+	bits, vi, err := mode.getEncoder()(content, level)
+	if err != nil {
+		return nil, err
+	}
+
+	blocks := splitToBlocks(bits.IterateBytes(), vi)
+	data := blocks.interleave(vi)
+	result := render(data, vi)
+	result.content = content
+	return result, nil
+}
+
+func render(data []byte, vi *versionInfo) *qrcode {
+	dim := vi.modulWidth()
+	results := make([]*qrcode, 8)
+	for i := 0; i < 8; i++ {
+		results[i] = newBarcode(dim)
+	}
+
+	occupied := newBarcode(dim)
+
+	setAll := func(x int, y int, val bool) {
+		occupied.Set(x, y, true)
+		for i := 0; i < 8; i++ {
+			results[i].Set(x, y, val)
+		}
+	}
+
+	drawFinderPatterns(vi, setAll)
+	drawAlignmentPatterns(occupied, vi, setAll)
+
+	//Timing Pattern:
+	var i int
+	for i = 0; i < dim; i++ {
+		if !occupied.Get(i, 6) {
+			setAll(i, 6, i%2 == 0)
+		}
+		if !occupied.Get(6, i) {
+			setAll(6, i, i%2 == 0)
+		}
+	}
+	// Dark Module
+	setAll(8, dim-8, true)
+
+	drawVersionInfo(vi, setAll)
+	drawFormatInfo(vi, -1, occupied.Set)
+	for i := 0; i < 8; i++ {
+		drawFormatInfo(vi, i, results[i].Set)
+	}
+
+	// Write the data
+	var curBitNo int
+
+	for pos := range iterateModules(occupied) {
+		var curBit bool
+		if curBitNo < len(data)*8 {
+			curBit = ((data[curBitNo/8] >> uint(7-(curBitNo%8))) & 1) == 1
+		} else {
+			curBit = false
+		}
+
+		for i := 0; i < 8; i++ {
+			setMasked(pos.X, pos.Y, curBit, i, results[i].Set)
+		}
+		curBitNo++
+	}
+
+	lowestPenalty := ^uint(0)
+	lowestPenaltyIdx := -1
+	for i := 0; i < 8; i++ {
+		p := results[i].calcPenalty()
+		if p < lowestPenalty {
+			lowestPenalty = p
+			lowestPenaltyIdx = i
+		}
+	}
+	return results[lowestPenaltyIdx]
+}
+
+func setMasked(x, y int, val bool, mask int, set func(int, int, bool)) {
+	switch mask {
+	case 0:
+		val = val != (((y + x) % 2) == 0)
+		break
+	case 1:
+		val = val != ((y % 2) == 0)
+		break
+	case 2:
+		val = val != ((x % 3) == 0)
+		break
+	case 3:
+		val = val != (((y + x) % 3) == 0)
+		break
+	case 4:
+		val = val != (((y/2 + x/3) % 2) == 0)
+		break
+	case 5:
+		val = val != (((y*x)%2)+((y*x)%3) == 0)
+		break
+	case 6:
+		val = val != ((((y*x)%2)+((y*x)%3))%2 == 0)
+		break
+	case 7:
+		val = val != ((((y+x)%2)+((y*x)%3))%2 == 0)
+	}
+	set(x, y, val)
+}
+
+func iterateModules(occupied *qrcode) <-chan image.Point {
+	result := make(chan image.Point)
+	allPoints := make(chan image.Point)
+	go func() {
+		curX := occupied.dimension - 1
+		curY := occupied.dimension - 1
+		isUpward := true
+
+		for true {
+			if isUpward {
+				allPoints <- image.Pt(curX, curY)
+				allPoints <- image.Pt(curX-1, curY)
+				curY--
+				if curY < 0 {
+					curY = 0
+					curX -= 2
+					if curX == 6 {
+						curX--
+					}
+					if curX < 0 {
+						break
+					}
+					isUpward = false
+				}
+			} else {
+				allPoints <- image.Pt(curX, curY)
+				allPoints <- image.Pt(curX-1, curY)
+				curY++
+				if curY >= occupied.dimension {
+					curY = occupied.dimension - 1
+					curX -= 2
+					if curX == 6 {
+						curX--
+					}
+					isUpward = true
+					if curX < 0 {
+						break
+					}
+				}
+			}
+		}
+
+		close(allPoints)
+	}()
+	go func() {
+		for pt := range allPoints {
+			if !occupied.Get(pt.X, pt.Y) {
+				result <- pt
+			}
+		}
+		close(result)
+	}()
+	return result
+}
+
+func drawFinderPatterns(vi *versionInfo, set func(int, int, bool)) {
+	dim := vi.modulWidth()
+	drawPattern := func(xoff int, yoff int) {
+		for x := -1; x < 8; x++ {
+			for y := -1; y < 8; y++ {
+				val := (x == 0 || x == 6 || y == 0 || y == 6 || (x > 1 && x < 5 && y > 1 && y < 5)) && (x <= 6 && y <= 6 && x >= 0 && y >= 0)
+
+				if x+xoff >= 0 && x+xoff < dim && y+yoff >= 0 && y+yoff < dim {
+					set(x+xoff, y+yoff, val)
+				}
+			}
+		}
+	}
+	drawPattern(0, 0)
+	drawPattern(0, dim-7)
+	drawPattern(dim-7, 0)
+}
+
+func drawAlignmentPatterns(occupied *qrcode, vi *versionInfo, set func(int, int, bool)) {
+	drawPattern := func(xoff int, yoff int) {
+		for x := -2; x <= 2; x++ {
+			for y := -2; y <= 2; y++ {
+				val := x == -2 || x == 2 || y == -2 || y == 2 || (x == 0 && y == 0)
+				set(x+xoff, y+yoff, val)
+			}
+		}
+	}
+	positions := vi.alignmentPatternPlacements()
+
+	for _, x := range positions {
+		for _, y := range positions {
+			if occupied.Get(x, y) {
+				continue
+			}
+			drawPattern(x, y)
+		}
+	}
+}
+
+var formatInfos = map[ErrorCorrectionLevel]map[int][]bool{
+	L: {
+		0: []bool{true, true, true, false, true, true, true, true, true, false, false, false, true, false, false},
+		1: []bool{true, true, true, false, false, true, false, true, true, true, true, false, false, true, true},
+		2: []bool{true, true, true, true, true, false, true, true, false, true, false, true, false, true, false},
+		3: []bool{true, true, true, true, false, false, false, true, false, false, true, true, true, false, true},
+		4: []bool{true, true, false, false, true, true, false, false, false, true, false, true, true, true, true},
+		5: []bool{true, true, false, false, false, true, true, false, false, false, true, true, false, false, false},
+		6: []bool{true, true, false, true, true, false, false, false, true, false, false, false, false, false, true},
+		7: []bool{true, true, false, true, false, false, true, false, true, true, true, false, true, true, false},
+	},
+	M: {
+		0: []bool{true, false, true, false, true, false, false, false, false, false, true, false, false, true, false},
+		1: []bool{true, false, true, false, false, false, true, false, false, true, false, false, true, false, true},
+		2: []bool{true, false, true, true, true, true, false, false, true, true, true, true, true, false, false},
+		3: []bool{true, false, true, true, false, true, true, false, true, false, false, true, false, true, true},
+		4: []bool{true, false, false, false, true, false, true, true, true, true, true, true, false, false, true},
+		5: []bool{true, false, false, false, false, false, false, true, true, false, false, true, true, true, false},
+		6: []bool{true, false, false, true, true, true, true, true, false, false, true, false, true, true, true},
+		7: []bool{true, false, false, true, false, true, false, true, false, true, false, false, false, false, false},
+	},
+	Q: {
+		0: []bool{false, true, true, false, true, false, true, false, true, false, true, true, true, true, true},
+		1: []bool{false, true, true, false, false, false, false, false, true, true, false, true, false, false, false},
+		2: []bool{false, true, true, true, true, true, true, false, false, true, true, false, false, false, true},
+		3: []bool{false, true, true, true, false, true, false, false, false, false, false, false, true, true, false},
+		4: []bool{false, true, false, false, true, false, false, true, false, true, true, false, true, false, false},
+		5: []bool{false, true, false, false, false, false, true, true, false, false, false, false, false, true, true},
+		6: []bool{false, true, false, true, true, true, false, true, true, false, true, true, false, true, false},
+		7: []bool{false, true, false, true, false, true, true, true, true, true, false, true, true, false, true},
+	},
+	H: {
+		0: []bool{false, false, true, false, true, true, false, true, false, false, false, true, false, false, true},
+		1: []bool{false, false, true, false, false, true, true, true, false, true, true, true, true, true, false},
+		2: []bool{false, false, true, true, true, false, false, true, true, true, false, false, true, true, true},
+		3: []bool{false, false, true, true, false, false, true, true, true, false, true, false, false, false, false},
+		4: []bool{false, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
+		5: []bool{false, false, false, false, false, true, false, false, true, false, true, false, true, false, true},
+		6: []bool{false, false, false, true, true, false, true, false, false, false, false, true, true, false, false},
+		7: []bool{false, false, false, true, false, false, false, false, false, true, true, true, false, true, true},
+	},
+}
+
+func drawFormatInfo(vi *versionInfo, usedMask int, set func(int, int, bool)) {
+	var formatInfo []bool
+
+	if usedMask == -1 {
+		formatInfo = []bool{true, true, true, true, true, true, true, true, true, true, true, true, true, true, true} // Set all to true cause -1 --> occupied mask.
+	} else {
+		formatInfo = formatInfos[vi.Level][usedMask]
+	}
+
+	if len(formatInfo) == 15 {
+		dim := vi.modulWidth()
+		set(0, 8, formatInfo[0])
+		set(1, 8, formatInfo[1])
+		set(2, 8, formatInfo[2])
+		set(3, 8, formatInfo[3])
+		set(4, 8, formatInfo[4])
+		set(5, 8, formatInfo[5])
+		set(7, 8, formatInfo[6])
+		set(8, 8, formatInfo[7])
+		set(8, 7, formatInfo[8])
+		set(8, 5, formatInfo[9])
+		set(8, 4, formatInfo[10])
+		set(8, 3, formatInfo[11])
+		set(8, 2, formatInfo[12])
+		set(8, 1, formatInfo[13])
+		set(8, 0, formatInfo[14])
+
+		set(8, dim-1, formatInfo[0])
+		set(8, dim-2, formatInfo[1])
+		set(8, dim-3, formatInfo[2])
+		set(8, dim-4, formatInfo[3])
+		set(8, dim-5, formatInfo[4])
+		set(8, dim-6, formatInfo[5])
+		set(8, dim-7, formatInfo[6])
+		set(dim-8, 8, formatInfo[7])
+		set(dim-7, 8, formatInfo[8])
+		set(dim-6, 8, formatInfo[9])
+		set(dim-5, 8, formatInfo[10])
+		set(dim-4, 8, formatInfo[11])
+		set(dim-3, 8, formatInfo[12])
+		set(dim-2, 8, formatInfo[13])
+		set(dim-1, 8, formatInfo[14])
+	}
+}
+
+var versionInfoBitsByVersion = map[byte][]bool{
+	7:  []bool{false, false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false},
+	8:  []bool{false, false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false},
+	9:  []bool{false, false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true},
+	10: []bool{false, false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true},
+	11: []bool{false, false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false},
+	12: []bool{false, false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false},
+	13: []bool{false, false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true},
+	14: []bool{false, false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true},
+	15: []bool{false, false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false},
+	16: []bool{false, true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false},
+	17: []bool{false, true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true},
+	18: []bool{false, true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true},
+	19: []bool{false, true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false},
+	20: []bool{false, true, false, true, false, false, true, false, false, true, true, false, true, false, false, true, true, false},
+	21: []bool{false, true, false, true, false, true, false, true, true, false, true, false, false, false, false, false, true, true},
+	22: []bool{false, true, false, true, true, false, true, false, false, false, true, true, false, false, true, false, false, true},
+	23: []bool{false, true, false, true, true, true, false, true, true, true, true, true, true, false, true, true, false, false},
+	24: []bool{false, true, true, false, false, false, true, true, true, false, true, true, false, false, false, true, false, false},
+	25: []bool{false, true, true, false, false, true, false, false, false, true, true, true, true, false, false, false, false, true},
+	26: []bool{false, true, true, false, true, false, true, true, true, true, true, false, true, false, true, false, true, true},
+	27: []bool{false, true, true, false, true, true, false, false, false, false, true, false, false, false, true, true, true, false},
+	28: []bool{false, true, true, true, false, false, true, true, false, false, false, false, false, true, true, false, true, false},
+	29: []bool{false, true, true, true, false, true, false, false, true, true, false, false, true, true, true, true, true, true},
+	30: []bool{false, true, true, true, true, false, true, true, false, true, false, true, true, true, false, true, false, true},
+	31: []bool{false, true, true, true, true, true, false, false, true, false, false, true, false, true, false, false, false, false},
+	32: []bool{true, false, false, false, false, false, true, false, false, true, true, true, false, true, false, true, false, true},
+	33: []bool{true, false, false, false, false, true, false, true, true, false, true, true, true, true, false, false, false, false},
+	34: []bool{true, false, false, false, true, false, true, false, false, false, true, false, true, true, true, false, true, false},
+	35: []bool{true, false, false, false, true, true, false, true, true, true, true, false, false, true, true, true, true, true},
+	36: []bool{true, false, false, true, false, false, true, false, true, true, false, false, false, false, true, false, true, true},
+	37: []bool{true, false, false, true, false, true, false, true, false, false, false, false, true, false, true, true, true, false},
+	38: []bool{true, false, false, true, true, false, true, false, true, false, false, true, true, false, false, true, false, false},
+	39: []bool{true, false, false, true, true, true, false, true, false, true, false, true, false, false, false, false, false, true},
+	40: []bool{true, false, true, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true},
+}
+
+func drawVersionInfo(vi *versionInfo, set func(int, int, bool)) {
+	versionInfoBits, ok := versionInfoBitsByVersion[vi.Version]
+
+	if ok && len(versionInfoBits) > 0 {
+		for i := 0; i < len(versionInfoBits); i++ {
+			x := (vi.modulWidth() - 11) + i%3
+			y := i / 3
+			set(x, y, versionInfoBits[len(versionInfoBits)-i-1])
+			set(y, x, versionInfoBits[len(versionInfoBits)-i-1])
+		}
+	}
+
+}
+
+func addPaddingAndTerminator(bl *utils.BitList, vi *versionInfo) {
+	for i := 0; i < 4 && bl.Len() < vi.totalDataBytes()*8; i++ {
+		bl.AddBit(false)
+	}
+
+	for bl.Len()%8 != 0 {
+		bl.AddBit(false)
+	}
+
+	for i := 0; bl.Len() < vi.totalDataBytes()*8; i++ {
+		if i%2 == 0 {
+			bl.AddByte(236)
+		} else {
+			bl.AddByte(17)
+		}
+	}
+}

+ 29 - 0
vendor/github.com/boombuler/barcode/qr/errorcorrection.go

@@ -0,0 +1,29 @@
+package qr
+
+import (
+	"github.com/boombuler/barcode/utils"
+)
+
+type errorCorrection struct {
+	rs *utils.ReedSolomonEncoder
+}
+
+var ec = newErrorCorrection()
+
+func newErrorCorrection() *errorCorrection {
+	fld := utils.NewGaloisField(285, 256, 0)
+	return &errorCorrection{utils.NewReedSolomonEncoder(fld)}
+}
+
+func (ec *errorCorrection) calcECC(data []byte, eccCount byte) []byte {
+	dataInts := make([]int, len(data))
+	for i := 0; i < len(data); i++ {
+		dataInts[i] = int(data[i])
+	}
+	res := ec.rs.Encode(dataInts, int(eccCount))
+	result := make([]byte, len(res))
+	for i := 0; i < len(res); i++ {
+		result[i] = byte(res[i])
+	}
+	return result
+}

+ 56 - 0
vendor/github.com/boombuler/barcode/qr/numeric.go

@@ -0,0 +1,56 @@
+package qr
+
+import (
+	"errors"
+	"fmt"
+	"strconv"
+
+	"github.com/boombuler/barcode/utils"
+)
+
+func encodeNumeric(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
+	contentBitCount := (len(content) / 3) * 10
+	switch len(content) % 3 {
+	case 1:
+		contentBitCount += 4
+	case 2:
+		contentBitCount += 7
+	}
+	vi := findSmallestVersionInfo(ecl, numericMode, contentBitCount)
+	if vi == nil {
+		return nil, nil, errors.New("To much data to encode")
+	}
+	res := new(utils.BitList)
+	res.AddBits(int(numericMode), 4)
+	res.AddBits(len(content), vi.charCountBits(numericMode))
+
+	for pos := 0; pos < len(content); pos += 3 {
+		var curStr string
+		if pos+3 <= len(content) {
+			curStr = content[pos : pos+3]
+		} else {
+			curStr = content[pos:]
+		}
+
+		i, err := strconv.Atoi(curStr)
+		if err != nil || i < 0 {
+			return nil, nil, fmt.Errorf("\"%s\" can not be encoded as %s", content, Numeric)
+		}
+		var bitCnt byte
+		switch len(curStr) % 3 {
+		case 0:
+			bitCnt = 10
+		case 1:
+			bitCnt = 4
+			break
+		case 2:
+			bitCnt = 7
+			break
+		}
+
+		res.AddBits(i, bitCnt)
+	}
+
+	addPaddingAndTerminator(res, vi)
+	return res, vi, nil
+}

+ 166 - 0
vendor/github.com/boombuler/barcode/qr/qrcode.go

@@ -0,0 +1,166 @@
+package qr
+
+import (
+	"image"
+	"image/color"
+	"math"
+
+	"github.com/boombuler/barcode"
+	"github.com/boombuler/barcode/utils"
+)
+
+type qrcode struct {
+	dimension int
+	data      *utils.BitList
+	content   string
+}
+
+func (qr *qrcode) Content() string {
+	return qr.content
+}
+
+func (qr *qrcode) Metadata() barcode.Metadata {
+	return barcode.Metadata{"QR Code", 2}
+}
+
+func (qr *qrcode) ColorModel() color.Model {
+	return color.Gray16Model
+}
+
+func (qr *qrcode) Bounds() image.Rectangle {
+	return image.Rect(0, 0, qr.dimension, qr.dimension)
+}
+
+func (qr *qrcode) At(x, y int) color.Color {
+	if qr.Get(x, y) {
+		return color.Black
+	}
+	return color.White
+}
+
+func (qr *qrcode) Get(x, y int) bool {
+	return qr.data.GetBit(x*qr.dimension + y)
+}
+
+func (qr *qrcode) Set(x, y int, val bool) {
+	qr.data.SetBit(x*qr.dimension+y, val)
+}
+
+func (qr *qrcode) calcPenalty() uint {
+	return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
+}
+
+func (qr *qrcode) calcPenaltyRule1() uint {
+	var result uint
+	for x := 0; x < qr.dimension; x++ {
+		checkForX := false
+		var cntX uint
+		checkForY := false
+		var cntY uint
+
+		for y := 0; y < qr.dimension; y++ {
+			if qr.Get(x, y) == checkForX {
+				cntX++
+			} else {
+				checkForX = !checkForX
+				if cntX >= 5 {
+					result += cntX - 2
+				}
+				cntX = 1
+			}
+
+			if qr.Get(y, x) == checkForY {
+				cntY++
+			} else {
+				checkForY = !checkForY
+				if cntY >= 5 {
+					result += cntY - 2
+				}
+				cntY = 1
+			}
+		}
+
+		if cntX >= 5 {
+			result += cntX - 2
+		}
+		if cntY >= 5 {
+			result += cntY - 2
+		}
+	}
+
+	return result
+}
+
+func (qr *qrcode) calcPenaltyRule2() uint {
+	var result uint
+	for x := 0; x < qr.dimension-1; x++ {
+		for y := 0; y < qr.dimension-1; y++ {
+			check := qr.Get(x, y)
+			if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check {
+				result += 3
+			}
+		}
+	}
+	return result
+}
+
+func (qr *qrcode) calcPenaltyRule3() uint {
+	pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false}
+	pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true}
+
+	var result uint
+	for x := 0; x <= qr.dimension-len(pattern1); x++ {
+		for y := 0; y < qr.dimension; y++ {
+			pattern1XFound := true
+			pattern2XFound := true
+			pattern1YFound := true
+			pattern2YFound := true
+
+			for i := 0; i < len(pattern1); i++ {
+				iv := qr.Get(x+i, y)
+				if iv != pattern1[i] {
+					pattern1XFound = false
+				}
+				if iv != pattern2[i] {
+					pattern2XFound = false
+				}
+				iv = qr.Get(y, x+i)
+				if iv != pattern1[i] {
+					pattern1YFound = false
+				}
+				if iv != pattern2[i] {
+					pattern2YFound = false
+				}
+			}
+			if pattern1XFound || pattern2XFound {
+				result += 40
+			}
+			if pattern1YFound || pattern2YFound {
+				result += 40
+			}
+		}
+	}
+
+	return result
+}
+
+func (qr *qrcode) calcPenaltyRule4() uint {
+	totalNum := qr.data.Len()
+	trueCnt := 0
+	for i := 0; i < totalNum; i++ {
+		if qr.data.GetBit(i) {
+			trueCnt++
+		}
+	}
+	percDark := float64(trueCnt) * 100 / float64(totalNum)
+	floor := math.Abs(math.Floor(percDark/5) - 10)
+	ceil := math.Abs(math.Ceil(percDark/5) - 10)
+	return uint(math.Min(floor, ceil) * 10)
+}
+
+func newBarcode(dim int) *qrcode {
+	res := new(qrcode)
+	res.dimension = dim
+	res.data = utils.NewBitList(dim * dim)
+	return res
+}

+ 27 - 0
vendor/github.com/boombuler/barcode/qr/unicode.go

@@ -0,0 +1,27 @@
+package qr
+
+import (
+	"errors"
+
+	"github.com/boombuler/barcode/utils"
+)
+
+func encodeUnicode(content string, ecl ErrorCorrectionLevel) (*utils.BitList, *versionInfo, error) {
+	data := []byte(content)
+
+	vi := findSmallestVersionInfo(ecl, byteMode, len(data)*8)
+	if vi == nil {
+		return nil, nil, errors.New("To much data to encode")
+	}
+
+	// It's not correct to add the unicode bytes to the result directly but most readers can't handle the
+	// required ECI header...
+	res := new(utils.BitList)
+	res.AddBits(int(byteMode), 4)
+	res.AddBits(len(content), vi.charCountBits(byteMode))
+	for _, b := range data {
+		res.AddByte(b)
+	}
+	addPaddingAndTerminator(res, vi)
+	return res, vi, nil
+}

+ 310 - 0
vendor/github.com/boombuler/barcode/qr/versioninfo.go

@@ -0,0 +1,310 @@
+package qr
+
+import "math"
+
+// ErrorCorrectionLevel indicates the amount of "backup data" stored in the QR code
+type ErrorCorrectionLevel byte
+
+const (
+	// L recovers 7% of data
+	L ErrorCorrectionLevel = iota
+	// M recovers 15% of data
+	M
+	// Q recovers 25% of data
+	Q
+	// H recovers 30% of data
+	H
+)
+
+func (ecl ErrorCorrectionLevel) String() string {
+	switch ecl {
+	case L:
+		return "L"
+	case M:
+		return "M"
+	case Q:
+		return "Q"
+	case H:
+		return "H"
+	}
+	return "unknown"
+}
+
+type encodingMode byte
+
+const (
+	numericMode      encodingMode = 1
+	alphaNumericMode encodingMode = 2
+	byteMode         encodingMode = 4
+	kanjiMode        encodingMode = 8
+)
+
+type versionInfo struct {
+	Version                          byte
+	Level                            ErrorCorrectionLevel
+	ErrorCorrectionCodewordsPerBlock byte
+	NumberOfBlocksInGroup1           byte
+	DataCodeWordsPerBlockInGroup1    byte
+	NumberOfBlocksInGroup2           byte
+	DataCodeWordsPerBlockInGroup2    byte
+}
+
+var versionInfos = []*versionInfo{
+	&versionInfo{1, L, 7, 1, 19, 0, 0},
+	&versionInfo{1, M, 10, 1, 16, 0, 0},
+	&versionInfo{1, Q, 13, 1, 13, 0, 0},
+	&versionInfo{1, H, 17, 1, 9, 0, 0},
+	&versionInfo{2, L, 10, 1, 34, 0, 0},
+	&versionInfo{2, M, 16, 1, 28, 0, 0},
+	&versionInfo{2, Q, 22, 1, 22, 0, 0},
+	&versionInfo{2, H, 28, 1, 16, 0, 0},
+	&versionInfo{3, L, 15, 1, 55, 0, 0},
+	&versionInfo{3, M, 26, 1, 44, 0, 0},
+	&versionInfo{3, Q, 18, 2, 17, 0, 0},
+	&versionInfo{3, H, 22, 2, 13, 0, 0},
+	&versionInfo{4, L, 20, 1, 80, 0, 0},
+	&versionInfo{4, M, 18, 2, 32, 0, 0},
+	&versionInfo{4, Q, 26, 2, 24, 0, 0},
+	&versionInfo{4, H, 16, 4, 9, 0, 0},
+	&versionInfo{5, L, 26, 1, 108, 0, 0},
+	&versionInfo{5, M, 24, 2, 43, 0, 0},
+	&versionInfo{5, Q, 18, 2, 15, 2, 16},
+	&versionInfo{5, H, 22, 2, 11, 2, 12},
+	&versionInfo{6, L, 18, 2, 68, 0, 0},
+	&versionInfo{6, M, 16, 4, 27, 0, 0},
+	&versionInfo{6, Q, 24, 4, 19, 0, 0},
+	&versionInfo{6, H, 28, 4, 15, 0, 0},
+	&versionInfo{7, L, 20, 2, 78, 0, 0},
+	&versionInfo{7, M, 18, 4, 31, 0, 0},
+	&versionInfo{7, Q, 18, 2, 14, 4, 15},
+	&versionInfo{7, H, 26, 4, 13, 1, 14},
+	&versionInfo{8, L, 24, 2, 97, 0, 0},
+	&versionInfo{8, M, 22, 2, 38, 2, 39},
+	&versionInfo{8, Q, 22, 4, 18, 2, 19},
+	&versionInfo{8, H, 26, 4, 14, 2, 15},
+	&versionInfo{9, L, 30, 2, 116, 0, 0},
+	&versionInfo{9, M, 22, 3, 36, 2, 37},
+	&versionInfo{9, Q, 20, 4, 16, 4, 17},
+	&versionInfo{9, H, 24, 4, 12, 4, 13},
+	&versionInfo{10, L, 18, 2, 68, 2, 69},
+	&versionInfo{10, M, 26, 4, 43, 1, 44},
+	&versionInfo{10, Q, 24, 6, 19, 2, 20},
+	&versionInfo{10, H, 28, 6, 15, 2, 16},
+	&versionInfo{11, L, 20, 4, 81, 0, 0},
+	&versionInfo{11, M, 30, 1, 50, 4, 51},
+	&versionInfo{11, Q, 28, 4, 22, 4, 23},
+	&versionInfo{11, H, 24, 3, 12, 8, 13},
+	&versionInfo{12, L, 24, 2, 92, 2, 93},
+	&versionInfo{12, M, 22, 6, 36, 2, 37},
+	&versionInfo{12, Q, 26, 4, 20, 6, 21},
+	&versionInfo{12, H, 28, 7, 14, 4, 15},
+	&versionInfo{13, L, 26, 4, 107, 0, 0},
+	&versionInfo{13, M, 22, 8, 37, 1, 38},
+	&versionInfo{13, Q, 24, 8, 20, 4, 21},
+	&versionInfo{13, H, 22, 12, 11, 4, 12},
+	&versionInfo{14, L, 30, 3, 115, 1, 116},
+	&versionInfo{14, M, 24, 4, 40, 5, 41},
+	&versionInfo{14, Q, 20, 11, 16, 5, 17},
+	&versionInfo{14, H, 24, 11, 12, 5, 13},
+	&versionInfo{15, L, 22, 5, 87, 1, 88},
+	&versionInfo{15, M, 24, 5, 41, 5, 42},
+	&versionInfo{15, Q, 30, 5, 24, 7, 25},
+	&versionInfo{15, H, 24, 11, 12, 7, 13},
+	&versionInfo{16, L, 24, 5, 98, 1, 99},
+	&versionInfo{16, M, 28, 7, 45, 3, 46},
+	&versionInfo{16, Q, 24, 15, 19, 2, 20},
+	&versionInfo{16, H, 30, 3, 15, 13, 16},
+	&versionInfo{17, L, 28, 1, 107, 5, 108},
+	&versionInfo{17, M, 28, 10, 46, 1, 47},
+	&versionInfo{17, Q, 28, 1, 22, 15, 23},
+	&versionInfo{17, H, 28, 2, 14, 17, 15},
+	&versionInfo{18, L, 30, 5, 120, 1, 121},
+	&versionInfo{18, M, 26, 9, 43, 4, 44},
+	&versionInfo{18, Q, 28, 17, 22, 1, 23},
+	&versionInfo{18, H, 28, 2, 14, 19, 15},
+	&versionInfo{19, L, 28, 3, 113, 4, 114},
+	&versionInfo{19, M, 26, 3, 44, 11, 45},
+	&versionInfo{19, Q, 26, 17, 21, 4, 22},
+	&versionInfo{19, H, 26, 9, 13, 16, 14},
+	&versionInfo{20, L, 28, 3, 107, 5, 108},
+	&versionInfo{20, M, 26, 3, 41, 13, 42},
+	&versionInfo{20, Q, 30, 15, 24, 5, 25},
+	&versionInfo{20, H, 28, 15, 15, 10, 16},
+	&versionInfo{21, L, 28, 4, 116, 4, 117},
+	&versionInfo{21, M, 26, 17, 42, 0, 0},
+	&versionInfo{21, Q, 28, 17, 22, 6, 23},
+	&versionInfo{21, H, 30, 19, 16, 6, 17},
+	&versionInfo{22, L, 28, 2, 111, 7, 112},
+	&versionInfo{22, M, 28, 17, 46, 0, 0},
+	&versionInfo{22, Q, 30, 7, 24, 16, 25},
+	&versionInfo{22, H, 24, 34, 13, 0, 0},
+	&versionInfo{23, L, 30, 4, 121, 5, 122},
+	&versionInfo{23, M, 28, 4, 47, 14, 48},
+	&versionInfo{23, Q, 30, 11, 24, 14, 25},
+	&versionInfo{23, H, 30, 16, 15, 14, 16},
+	&versionInfo{24, L, 30, 6, 117, 4, 118},
+	&versionInfo{24, M, 28, 6, 45, 14, 46},
+	&versionInfo{24, Q, 30, 11, 24, 16, 25},
+	&versionInfo{24, H, 30, 30, 16, 2, 17},
+	&versionInfo{25, L, 26, 8, 106, 4, 107},
+	&versionInfo{25, M, 28, 8, 47, 13, 48},
+	&versionInfo{25, Q, 30, 7, 24, 22, 25},
+	&versionInfo{25, H, 30, 22, 15, 13, 16},
+	&versionInfo{26, L, 28, 10, 114, 2, 115},
+	&versionInfo{26, M, 28, 19, 46, 4, 47},
+	&versionInfo{26, Q, 28, 28, 22, 6, 23},
+	&versionInfo{26, H, 30, 33, 16, 4, 17},
+	&versionInfo{27, L, 30, 8, 122, 4, 123},
+	&versionInfo{27, M, 28, 22, 45, 3, 46},
+	&versionInfo{27, Q, 30, 8, 23, 26, 24},
+	&versionInfo{27, H, 30, 12, 15, 28, 16},
+	&versionInfo{28, L, 30, 3, 117, 10, 118},
+	&versionInfo{28, M, 28, 3, 45, 23, 46},
+	&versionInfo{28, Q, 30, 4, 24, 31, 25},
+	&versionInfo{28, H, 30, 11, 15, 31, 16},
+	&versionInfo{29, L, 30, 7, 116, 7, 117},
+	&versionInfo{29, M, 28, 21, 45, 7, 46},
+	&versionInfo{29, Q, 30, 1, 23, 37, 24},
+	&versionInfo{29, H, 30, 19, 15, 26, 16},
+	&versionInfo{30, L, 30, 5, 115, 10, 116},
+	&versionInfo{30, M, 28, 19, 47, 10, 48},
+	&versionInfo{30, Q, 30, 15, 24, 25, 25},
+	&versionInfo{30, H, 30, 23, 15, 25, 16},
+	&versionInfo{31, L, 30, 13, 115, 3, 116},
+	&versionInfo{31, M, 28, 2, 46, 29, 47},
+	&versionInfo{31, Q, 30, 42, 24, 1, 25},
+	&versionInfo{31, H, 30, 23, 15, 28, 16},
+	&versionInfo{32, L, 30, 17, 115, 0, 0},
+	&versionInfo{32, M, 28, 10, 46, 23, 47},
+	&versionInfo{32, Q, 30, 10, 24, 35, 25},
+	&versionInfo{32, H, 30, 19, 15, 35, 16},
+	&versionInfo{33, L, 30, 17, 115, 1, 116},
+	&versionInfo{33, M, 28, 14, 46, 21, 47},
+	&versionInfo{33, Q, 30, 29, 24, 19, 25},
+	&versionInfo{33, H, 30, 11, 15, 46, 16},
+	&versionInfo{34, L, 30, 13, 115, 6, 116},
+	&versionInfo{34, M, 28, 14, 46, 23, 47},
+	&versionInfo{34, Q, 30, 44, 24, 7, 25},
+	&versionInfo{34, H, 30, 59, 16, 1, 17},
+	&versionInfo{35, L, 30, 12, 121, 7, 122},
+	&versionInfo{35, M, 28, 12, 47, 26, 48},
+	&versionInfo{35, Q, 30, 39, 24, 14, 25},
+	&versionInfo{35, H, 30, 22, 15, 41, 16},
+	&versionInfo{36, L, 30, 6, 121, 14, 122},
+	&versionInfo{36, M, 28, 6, 47, 34, 48},
+	&versionInfo{36, Q, 30, 46, 24, 10, 25},
+	&versionInfo{36, H, 30, 2, 15, 64, 16},
+	&versionInfo{37, L, 30, 17, 122, 4, 123},
+	&versionInfo{37, M, 28, 29, 46, 14, 47},
+	&versionInfo{37, Q, 30, 49, 24, 10, 25},
+	&versionInfo{37, H, 30, 24, 15, 46, 16},
+	&versionInfo{38, L, 30, 4, 122, 18, 123},
+	&versionInfo{38, M, 28, 13, 46, 32, 47},
+	&versionInfo{38, Q, 30, 48, 24, 14, 25},
+	&versionInfo{38, H, 30, 42, 15, 32, 16},
+	&versionInfo{39, L, 30, 20, 117, 4, 118},
+	&versionInfo{39, M, 28, 40, 47, 7, 48},
+	&versionInfo{39, Q, 30, 43, 24, 22, 25},
+	&versionInfo{39, H, 30, 10, 15, 67, 16},
+	&versionInfo{40, L, 30, 19, 118, 6, 119},
+	&versionInfo{40, M, 28, 18, 47, 31, 48},
+	&versionInfo{40, Q, 30, 34, 24, 34, 25},
+	&versionInfo{40, H, 30, 20, 15, 61, 16},
+}
+
+func (vi *versionInfo) totalDataBytes() int {
+	g1Data := int(vi.NumberOfBlocksInGroup1) * int(vi.DataCodeWordsPerBlockInGroup1)
+	g2Data := int(vi.NumberOfBlocksInGroup2) * int(vi.DataCodeWordsPerBlockInGroup2)
+	return (g1Data + g2Data)
+}
+
+func (vi *versionInfo) charCountBits(m encodingMode) byte {
+	switch m {
+	case numericMode:
+		if vi.Version < 10 {
+			return 10
+		} else if vi.Version < 27 {
+			return 12
+		}
+		return 14
+
+	case alphaNumericMode:
+		if vi.Version < 10 {
+			return 9
+		} else if vi.Version < 27 {
+			return 11
+		}
+		return 13
+
+	case byteMode:
+		if vi.Version < 10 {
+			return 8
+		}
+		return 16
+
+	case kanjiMode:
+		if vi.Version < 10 {
+			return 8
+		} else if vi.Version < 27 {
+			return 10
+		}
+		return 12
+	default:
+		return 0
+	}
+}
+
+func (vi *versionInfo) modulWidth() int {
+	return ((int(vi.Version) - 1) * 4) + 21
+}
+
+func (vi *versionInfo) alignmentPatternPlacements() []int {
+	if vi.Version == 1 {
+		return make([]int, 0)
+	}
+
+	first := 6
+	last := vi.modulWidth() - 7
+	space := float64(last - first)
+	count := int(math.Ceil(space/28)) + 1
+
+	result := make([]int, count)
+	result[0] = first
+	result[len(result)-1] = last
+	if count > 2 {
+		step := int(math.Ceil(float64(last-first) / float64(count-1)))
+		if step%2 == 1 {
+			frac := float64(last-first) / float64(count-1)
+			_, x := math.Modf(frac)
+			if x >= 0.5 {
+				frac = math.Ceil(frac)
+			} else {
+				frac = math.Floor(frac)
+			}
+
+			if int(frac)%2 == 0 {
+				step--
+			} else {
+				step++
+			}
+		}
+
+		for i := 1; i <= count-2; i++ {
+			result[i] = last - (step * (count - 1 - i))
+		}
+	}
+
+	return result
+}
+
+func findSmallestVersionInfo(ecl ErrorCorrectionLevel, mode encodingMode, dataBits int) *versionInfo {
+	dataBits = dataBits + 4 // mode indicator
+	for _, vi := range versionInfos {
+		if vi.Level == ecl {
+			if (vi.totalDataBytes() * 8) >= (dataBits + int(vi.charCountBits(mode))) {
+				return vi
+			}
+		}
+	}
+	return nil
+}

+ 134 - 0
vendor/github.com/boombuler/barcode/scaledbarcode.go

@@ -0,0 +1,134 @@
+package barcode
+
+import (
+	"errors"
+	"fmt"
+	"image"
+	"image/color"
+	"math"
+)
+
+type wrapFunc func(x, y int) color.Color
+
+type scaledBarcode struct {
+	wrapped     Barcode
+	wrapperFunc wrapFunc
+	rect        image.Rectangle
+}
+
+type intCSscaledBC struct {
+	scaledBarcode
+}
+
+func (bc *scaledBarcode) Content() string {
+	return bc.wrapped.Content()
+}
+
+func (bc *scaledBarcode) Metadata() Metadata {
+	return bc.wrapped.Metadata()
+}
+
+func (bc *scaledBarcode) ColorModel() color.Model {
+	return bc.wrapped.ColorModel()
+}
+
+func (bc *scaledBarcode) Bounds() image.Rectangle {
+	return bc.rect
+}
+
+func (bc *scaledBarcode) At(x, y int) color.Color {
+	return bc.wrapperFunc(x, y)
+}
+
+func (bc *intCSscaledBC) CheckSum() int {
+	if cs, ok := bc.wrapped.(BarcodeIntCS); ok {
+		return cs.CheckSum()
+	}
+	return 0
+}
+
+// Scale returns a resized barcode with the given width and height.
+func Scale(bc Barcode, width, height int) (Barcode, error) {
+	switch bc.Metadata().Dimensions {
+	case 1:
+		return scale1DCode(bc, width, height)
+	case 2:
+		return scale2DCode(bc, width, height)
+	}
+
+	return nil, errors.New("unsupported barcode format")
+}
+
+func newScaledBC(wrapped Barcode, wrapperFunc wrapFunc, rect image.Rectangle) Barcode {
+	result := &scaledBarcode{
+		wrapped:     wrapped,
+		wrapperFunc: wrapperFunc,
+		rect:        rect,
+	}
+
+	if _, ok := wrapped.(BarcodeIntCS); ok {
+		return &intCSscaledBC{*result}
+	}
+	return result
+}
+
+func scale2DCode(bc Barcode, width, height int) (Barcode, error) {
+	orgBounds := bc.Bounds()
+	orgWidth := orgBounds.Max.X - orgBounds.Min.X
+	orgHeight := orgBounds.Max.Y - orgBounds.Min.Y
+
+	factor := int(math.Min(float64(width)/float64(orgWidth), float64(height)/float64(orgHeight)))
+	if factor <= 0 {
+		return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx%d", orgWidth, orgHeight)
+	}
+
+	offsetX := (width - (orgWidth * factor)) / 2
+	offsetY := (height - (orgHeight * factor)) / 2
+
+	wrap := func(x, y int) color.Color {
+		if x < offsetX || y < offsetY {
+			return color.White
+		}
+		x = (x - offsetX) / factor
+		y = (y - offsetY) / factor
+		if x >= orgWidth || y >= orgHeight {
+			return color.White
+		}
+		return bc.At(x, y)
+	}
+
+	return newScaledBC(
+		bc,
+		wrap,
+		image.Rect(0, 0, width, height),
+	), nil
+}
+
+func scale1DCode(bc Barcode, width, height int) (Barcode, error) {
+	orgBounds := bc.Bounds()
+	orgWidth := orgBounds.Max.X - orgBounds.Min.X
+	factor := int(float64(width) / float64(orgWidth))
+
+	if factor <= 0 {
+		return nil, fmt.Errorf("can not scale barcode to an image smaller than %dx1", orgWidth)
+	}
+	offsetX := (width - (orgWidth * factor)) / 2
+
+	wrap := func(x, y int) color.Color {
+		if x < offsetX {
+			return color.White
+		}
+		x = (x - offsetX) / factor
+
+		if x >= orgWidth {
+			return color.White
+		}
+		return bc.At(x, 0)
+	}
+
+	return newScaledBC(
+		bc,
+		wrap,
+		image.Rect(0, 0, width, height),
+	), nil
+}

+ 202 - 0
vendor/github.com/pquerna/otp/LICENSE

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 5 - 0
vendor/github.com/pquerna/otp/NOTICE

@@ -0,0 +1,5 @@
+otp
+Copyright (c) 2014, Paul Querna
+
+This product includes software developed by 
+Paul Querna (http://paul.querna.org/).

Fichier diff supprimé car celui-ci est trop grand
+ 60 - 0
vendor/github.com/pquerna/otp/README.md


+ 70 - 0
vendor/github.com/pquerna/otp/doc.go

@@ -0,0 +1,70 @@
+/**
+ *  Copyright 2014 Paul Querna
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+// Package otp implements both HOTP and TOTP based
+// one time passcodes in a Google Authenticator compatible manner.
+//
+// When adding a TOTP for a user, you must store the "secret" value
+// persistently. It is recommend to store the secret in an encrypted field in your
+// datastore.  Due to how TOTP works, it is not possible to store a hash
+// for the secret value like you would a password.
+//
+// To enroll a user, you must first generate an OTP for them.  Google
+// Authenticator supports using a QR code as an enrollment method:
+//
+//	import (
+//		"github.com/pquerna/otp/totp"
+//
+//		"bytes"
+//		"image/png"
+//	)
+//
+//	key, err := totp.Generate(totp.GenerateOpts{
+//			Issuer: "Example.com",
+//			AccountName: "alice@example.com",
+//	})
+//
+//	// Convert TOTP key into a QR code encoded as a PNG image.
+//	var buf bytes.Buffer
+//	img, err := key.Image(200, 200)
+//	png.Encode(&buf, img)
+//
+//	// display the QR code to the user.
+//	display(buf.Bytes())
+//
+//	// Now Validate that the user's successfully added the passcode.
+//	passcode := promptForPasscode()
+//	valid := totp.Validate(passcode, key.Secret())
+//
+//	if valid {
+//		// User successfully used their TOTP, save it to your backend!
+//		storeSecret("alice@example.com", key.Secret())
+//	}
+//
+// Validating a TOTP passcode is very easy, just prompt the user for a passcode
+// and retrieve the associated user's previously stored secret.
+//	import "github.com/pquerna/otp/totp"
+//
+//	passcode := promptForPasscode()
+//	secret := getSecret("alice@example.com")
+//
+//	valid := totp.Validate(passcode, secret)
+//
+//	if valid {
+//		// Success! continue login process.
+//	}
+package otp

+ 187 - 0
vendor/github.com/pquerna/otp/hotp/hotp.go

@@ -0,0 +1,187 @@
+/**
+ *  Copyright 2014 Paul Querna
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package hotp
+
+import (
+	"github.com/pquerna/otp"
+
+	"crypto/hmac"
+	"crypto/rand"
+	"crypto/subtle"
+	"encoding/base32"
+	"encoding/binary"
+	"fmt"
+	"math"
+	"net/url"
+	"strings"
+)
+
+const debug = false
+
+// Validate a HOTP passcode given a counter and secret.
+// This is a shortcut for ValidateCustom, with parameters that
+// are compataible with Google-Authenticator.
+func Validate(passcode string, counter uint64, secret string) bool {
+	rv, _ := ValidateCustom(
+		passcode,
+		counter,
+		secret,
+		ValidateOpts{
+			Digits:    otp.DigitsSix,
+			Algorithm: otp.AlgorithmSHA1,
+		},
+	)
+	return rv
+}
+
+// ValidateOpts provides options for ValidateCustom().
+type ValidateOpts struct {
+	// Digits as part of the input. Defaults to 6.
+	Digits otp.Digits
+	// Algorithm to use for HMAC. Defaults to SHA1.
+	Algorithm otp.Algorithm
+}
+
+// GenerateCode creates a HOTP passcode given a counter and secret.
+// This is a shortcut for GenerateCodeCustom, with parameters that
+// are compataible with Google-Authenticator.
+func GenerateCode(secret string, counter uint64) (string, error) {
+	return GenerateCodeCustom(secret, counter, ValidateOpts{
+		Digits:    otp.DigitsSix,
+		Algorithm: otp.AlgorithmSHA1,
+	})
+}
+
+// GenerateCodeCustom uses a counter and secret value and options struct to
+// create a passcode.
+func GenerateCodeCustom(secret string, counter uint64, opts ValidateOpts) (passcode string, err error) {
+	// As noted in issue #10 this adds support for TOTP secrets that are
+	// missing their padding.
+	if n := len(secret) % 8; n != 0 {
+		secret = secret + strings.Repeat("=", 8-n)
+	}
+
+	secretBytes, err := base32.StdEncoding.DecodeString(secret)
+	if err != nil {
+		return "", otp.ErrValidateSecretInvalidBase32
+	}
+
+	buf := make([]byte, 8)
+	mac := hmac.New(opts.Algorithm.Hash, secretBytes)
+	binary.BigEndian.PutUint64(buf, counter)
+	if debug {
+		fmt.Printf("counter=%v\n", counter)
+		fmt.Printf("buf=%v\n", buf)
+	}
+
+	mac.Write(buf)
+	sum := mac.Sum(nil)
+
+	// "Dynamic truncation" in RFC 4226
+	// http://tools.ietf.org/html/rfc4226#section-5.4
+	offset := sum[len(sum)-1] & 0xf
+	value := int64(((int(sum[offset]) & 0x7f) << 24) |
+		((int(sum[offset+1] & 0xff)) << 16) |
+		((int(sum[offset+2] & 0xff)) << 8) |
+		(int(sum[offset+3]) & 0xff))
+
+	l := opts.Digits.Length()
+	mod := int32(value % int64(math.Pow10(l)))
+
+	if debug {
+		fmt.Printf("offset=%v\n", offset)
+		fmt.Printf("value=%v\n", value)
+		fmt.Printf("mod'ed=%v\n", mod)
+	}
+
+	return opts.Digits.Format(mod), nil
+}
+
+// ValidateCustom validates an HOTP with customizable options. Most users should
+// use Validate().
+func ValidateCustom(passcode string, counter uint64, secret string, opts ValidateOpts) (bool, error) {
+	passcode = strings.TrimSpace(passcode)
+
+	if len(passcode) != opts.Digits.Length() {
+		return false, otp.ErrValidateInputInvalidLength
+	}
+
+	otpstr, err := GenerateCodeCustom(secret, counter, opts)
+	if err != nil {
+		return false, err
+	}
+
+	if subtle.ConstantTimeCompare([]byte(otpstr), []byte(passcode)) == 1 {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+// GenerateOpts provides options for .Generate()
+type GenerateOpts struct {
+	// Name of the issuing Organization/Company.
+	Issuer string
+	// Name of the User's Account (eg, email address)
+	AccountName string
+	// Size in size of the generated Secret. Defaults to 10 bytes.
+	SecretSize uint
+	// Digits to request. Defaults to 6.
+	Digits otp.Digits
+	// Algorithm to use for HMAC. Defaults to SHA1.
+	Algorithm otp.Algorithm
+}
+
+// Generate creates a new HOTP Key.
+func Generate(opts GenerateOpts) (*otp.Key, error) {
+	// url encode the Issuer/AccountName
+	if opts.Issuer == "" {
+		return nil, otp.ErrGenerateMissingIssuer
+	}
+
+	if opts.AccountName == "" {
+		return nil, otp.ErrGenerateMissingAccountName
+	}
+
+	if opts.SecretSize == 0 {
+		opts.SecretSize = 10
+	}
+
+	// otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
+
+	v := url.Values{}
+	secret := make([]byte, opts.SecretSize)
+	_, err := rand.Read(secret)
+	if err != nil {
+		return nil, err
+	}
+
+	v.Set("secret", base32.StdEncoding.EncodeToString(secret))
+	v.Set("issuer", opts.Issuer)
+	v.Set("algorithm", opts.Algorithm.String())
+	v.Set("digits", opts.Digits.String())
+
+	u := url.URL{
+		Scheme:   "otpauth",
+		Host:     "hotp",
+		Path:     "/" + opts.Issuer + ":" + opts.AccountName,
+		RawQuery: v.Encode(),
+	}
+
+	return otp.NewKeyFromURL(u.String())
+}

+ 200 - 0
vendor/github.com/pquerna/otp/otp.go

@@ -0,0 +1,200 @@
+/**
+ *  Copyright 2014 Paul Querna
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package otp
+
+import (
+	"github.com/boombuler/barcode"
+	"github.com/boombuler/barcode/qr"
+
+	"crypto/md5"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/sha512"
+	"errors"
+	"fmt"
+	"hash"
+	"image"
+	"net/url"
+	"strings"
+)
+
+// Error when attempting to convert the secret from base32 to raw bytes.
+var ErrValidateSecretInvalidBase32 = errors.New("Decoding of secret as base32 failed.")
+
+// The user provided passcode length was not expected.
+var ErrValidateInputInvalidLength = errors.New("Input length unexpected")
+
+// When generating a Key, the Issuer must be set.
+var ErrGenerateMissingIssuer = errors.New("Issuer must be set")
+
+// When generating a Key, the Account Name must be set.
+var ErrGenerateMissingAccountName = errors.New("AccountName must be set")
+
+// Key represents an TOTP or HTOP key.
+type Key struct {
+	orig string
+	url  *url.URL
+}
+
+// NewKeyFromURL creates a new Key from an TOTP or HOTP url.
+//
+// The URL format is documented here:
+//   https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+//
+func NewKeyFromURL(orig string) (*Key, error) {
+	u, err := url.Parse(orig)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &Key{
+		orig: orig,
+		url:  u,
+	}, nil
+}
+
+func (k *Key) String() string {
+	return k.orig
+}
+
+// Image returns an QR-Code image of the specified width and height,
+// suitable for use by many clients like Google-Authenricator
+// to enroll a user's TOTP/HOTP key.
+func (k *Key) Image(width int, height int) (image.Image, error) {
+	b, err := qr.Encode(k.orig, qr.M, qr.Auto)
+
+	if err != nil {
+		return nil, err
+	}
+
+	b, err = barcode.Scale(b, width, height)
+
+	if err != nil {
+		return nil, err
+	}
+
+	return b, nil
+}
+
+// Type returns "hotp" or "totp".
+func (k *Key) Type() string {
+	return k.url.Host
+}
+
+// Issuer returns the name of the issuing organization.
+func (k *Key) Issuer() string {
+	q := k.url.Query()
+
+	issuer := q.Get("issuer")
+
+	if issuer != "" {
+		return issuer
+	}
+
+	p := strings.TrimPrefix(k.url.Path, "/")
+	i := strings.Index(p, ":")
+
+	if i == -1 {
+		return ""
+	}
+
+	return p[:i]
+}
+
+// AccountName returns the name of the user's account.
+func (k *Key) AccountName() string {
+	p := strings.TrimPrefix(k.url.Path, "/")
+	i := strings.Index(p, ":")
+
+	if i == -1 {
+		return p
+	}
+
+	return p[i+1:]
+}
+
+// Secret returns the opaque secret for this Key.
+func (k *Key) Secret() string {
+	q := k.url.Query()
+
+	return q.Get("secret")
+}
+
+// Algorithm represents the hashing function to use in the HMAC
+// operation needed for OTPs.
+type Algorithm int
+
+const (
+	AlgorithmSHA1 Algorithm = iota
+	AlgorithmSHA256
+	AlgorithmSHA512
+	AlgorithmMD5
+)
+
+func (a Algorithm) String() string {
+	switch a {
+	case AlgorithmSHA1:
+		return "SHA1"
+	case AlgorithmSHA256:
+		return "SHA256"
+	case AlgorithmSHA512:
+		return "SHA512"
+	case AlgorithmMD5:
+		return "MD5"
+	}
+	panic("unreached")
+}
+
+func (a Algorithm) Hash() hash.Hash {
+	switch a {
+	case AlgorithmSHA1:
+		return sha1.New()
+	case AlgorithmSHA256:
+		return sha256.New()
+	case AlgorithmSHA512:
+		return sha512.New()
+	case AlgorithmMD5:
+		return md5.New()
+	}
+	panic("unreached")
+}
+
+// Digits represents the number of digits present in the
+// user's OTP passcode. Six and Eight are the most common values.
+type Digits int
+
+const (
+	DigitsSix   Digits = 6
+	DigitsEight Digits = 8
+)
+
+// Format converts an integer into the zero-filled size for this Digits.
+func (d Digits) Format(in int32) string {
+	f := fmt.Sprintf("%%0%dd", d)
+	return fmt.Sprintf(f, in)
+}
+
+// Length returns the number of characters for this Digits.
+func (d Digits) Length() int {
+	return int(d)
+}
+
+func (d Digits) String() string {
+	return fmt.Sprintf("%d", d)
+}

+ 191 - 0
vendor/github.com/pquerna/otp/totp/totp.go

@@ -0,0 +1,191 @@
+/**
+ *  Copyright 2014 Paul Querna
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package totp
+
+import (
+	"github.com/pquerna/otp"
+	"github.com/pquerna/otp/hotp"
+
+	"crypto/rand"
+	"encoding/base32"
+	"math"
+	"net/url"
+	"strconv"
+	"time"
+)
+
+// Validate a TOTP using the current time.
+// A shortcut for ValidateCustom, Validate uses a configuration
+// that is compatible with Google-Authenticator and most clients.
+func Validate(passcode string, secret string) bool {
+	rv, _ := ValidateCustom(
+		passcode,
+		secret,
+		time.Now().UTC(),
+		ValidateOpts{
+			Period:    30,
+			Skew:      1,
+			Digits:    otp.DigitsSix,
+			Algorithm: otp.AlgorithmSHA1,
+		},
+	)
+	return rv
+}
+
+// GenerateCode creates a TOTP token using the current time.
+// A shortcut for GenerateCodeCustom, GenerateCode uses a configuration
+// that is compatible with Google-Authenticator and most clients.
+func GenerateCode(secret string, t time.Time) (string, error) {
+	return GenerateCodeCustom(secret, t, ValidateOpts{
+		Period:    30,
+		Skew:      1,
+		Digits:    otp.DigitsSix,
+		Algorithm: otp.AlgorithmSHA1,
+	})
+}
+
+// ValidateOpts provides options for ValidateCustom().
+type ValidateOpts struct {
+	// Number of seconds a TOTP hash is valid for. Defaults to 30 seconds.
+	Period uint
+	// Periods before or after the current time to allow.  Value of 1 allows up to Period
+	// of either side of the specified time.  Defaults to 0 allowed skews.  Values greater
+	// than 1 are likely sketchy.
+	Skew uint
+	// Digits as part of the input. Defaults to 6.
+	Digits otp.Digits
+	// Algorithm to use for HMAC. Defaults to SHA1.
+	Algorithm otp.Algorithm
+}
+
+// GenerateCodeCustom takes a timepoint and produces a passcode using a
+// secret and the provided opts. (Under the hood, this is making an adapted
+// call to hotp.GenerateCodeCustom)
+func GenerateCodeCustom(secret string, t time.Time, opts ValidateOpts) (passcode string, err error) {
+	if opts.Period == 0 {
+		opts.Period = 30
+	}
+	counter := uint64(math.Floor(float64(t.Unix()) / float64(opts.Period)))
+	passcode, err = hotp.GenerateCodeCustom(secret, counter, hotp.ValidateOpts{
+		Digits:    opts.Digits,
+		Algorithm: opts.Algorithm,
+	})
+	if err != nil {
+		return "", err
+	}
+	return passcode, nil
+}
+
+// ValidateCustom validates a TOTP given a user specified time and custom options.
+// Most users should use Validate() to provide an interpolatable TOTP experience.
+func ValidateCustom(passcode string, secret string, t time.Time, opts ValidateOpts) (bool, error) {
+	if opts.Period == 0 {
+		opts.Period = 30
+	}
+
+	counters := []uint64{}
+	counter := int64(math.Floor(float64(t.Unix()) / float64(opts.Period)))
+
+	counters = append(counters, uint64(counter))
+	for i := 1; i <= int(opts.Skew); i++ {
+		counters = append(counters, uint64(counter+int64(i)))
+		counters = append(counters, uint64(counter-int64(i)))
+	}
+
+	for _, counter := range counters {
+		rv, err := hotp.ValidateCustom(passcode, counter, secret, hotp.ValidateOpts{
+			Digits:    opts.Digits,
+			Algorithm: opts.Algorithm,
+		})
+
+		if err != nil {
+			return false, err
+		}
+
+		if rv == true {
+			return true, nil
+		}
+	}
+
+	return false, nil
+}
+
+// GenerateOpts provides options for Generate().  The default values
+// are compatible with Google-Authenticator.
+type GenerateOpts struct {
+	// Name of the issuing Organization/Company.
+	Issuer string
+	// Name of the User's Account (eg, email address)
+	AccountName string
+	// Number of seconds a TOTP hash is valid for. Defaults to 30 seconds.
+	Period uint
+	// Size in size of the generated Secret. Defaults to 10 bytes.
+	SecretSize uint
+	// Digits to request. Defaults to 6.
+	Digits otp.Digits
+	// Algorithm to use for HMAC. Defaults to SHA1.
+	Algorithm otp.Algorithm
+}
+
+// Generate a new TOTP Key.
+func Generate(opts GenerateOpts) (*otp.Key, error) {
+	// url encode the Issuer/AccountName
+	if opts.Issuer == "" {
+		return nil, otp.ErrGenerateMissingIssuer
+	}
+
+	if opts.AccountName == "" {
+		return nil, otp.ErrGenerateMissingAccountName
+	}
+
+	if opts.Period == 0 {
+		opts.Period = 30
+	}
+
+	if opts.SecretSize == 0 {
+		opts.SecretSize = 10
+	}
+
+	if opts.Digits == 0 {
+		opts.Digits = otp.DigitsSix
+	}
+
+	// otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
+
+	v := url.Values{}
+	secret := make([]byte, opts.SecretSize)
+	_, err := rand.Read(secret)
+	if err != nil {
+		return nil, err
+	}
+
+	v.Set("secret", base32.StdEncoding.EncodeToString(secret))
+	v.Set("issuer", opts.Issuer)
+	v.Set("period", strconv.FormatUint(uint64(opts.Period), 10))
+	v.Set("algorithm", opts.Algorithm.String())
+	v.Set("digits", opts.Digits.String())
+
+	u := url.URL{
+		Scheme:   "otpauth",
+		Host:     "totp",
+		Path:     "/" + opts.Issuer + ":" + opts.AccountName,
+		RawQuery: v.Encode(),
+	}
+
+	return otp.NewKeyFromURL(u.String())
+}

+ 30 - 0
vendor/vendor.json

@@ -33,6 +33,18 @@
 			"revisionTime": "2017-02-05T23:42:31Z"
 		},
 		{
+			"checksumSHA1": "tLl952GRIVsso2Pk/IH3cMJaK8E=",
+			"path": "github.com/boombuler/barcode",
+			"revision": "0dc17c9053c4956f07a6db58b2863924fe03b643",
+			"revisionTime": "2017-04-03T18:34:37Z"
+		},
+		{
+			"checksumSHA1": "tbwzn+sWiZv6veyXae3qRfTjlcQ=",
+			"path": "github.com/boombuler/barcode/qr",
+			"revision": "0dc17c9053c4956f07a6db58b2863924fe03b643",
+			"revisionTime": "2017-04-03T18:34:37Z"
+		},
+		{
 			"checksumSHA1": "fNAC4qgZDqF3kxq74/yyk3PWdy8=",
 			"path": "github.com/bradfitz/gomemcache/memcache",
 			"revision": "fb1f79c6b65acda83063cbc69f6bba1522558bfc",
@@ -279,6 +291,24 @@
 			"revisionTime": "2016-07-24T20:39:20Z"
 		},
 		{
+			"checksumSHA1": "woY3inKe+d7B1jPTFxVKNCCFH9c=",
+			"path": "github.com/pquerna/otp",
+			"revision": "9e1935374bc73ffe011187dafed51a412b90fe43",
+			"revisionTime": "2017-02-23T01:06:52Z"
+		},
+		{
+			"checksumSHA1": "xo32aXW4ZXXRHJ/9E6m10vXJZAo=",
+			"path": "github.com/pquerna/otp/hotp",
+			"revision": "9e1935374bc73ffe011187dafed51a412b90fe43",
+			"revisionTime": "2017-02-23T01:06:52Z"
+		},
+		{
+			"checksumSHA1": "Ie55pTQw1rnOZ8KDekSDXUWDT1I=",
+			"path": "github.com/pquerna/otp/totp",
+			"revision": "9e1935374bc73ffe011187dafed51a412b90fe43",
+			"revisionTime": "2017-02-23T01:06:52Z"
+		},
+		{
 			"checksumSHA1": "c7jHQZk5ZEsFR9EXsWJXkszPBZA=",
 			"path": "github.com/russross/blackfriday",
 			"revision": "5f33e7b7878355cd2b7e6b8eefc48a5472c69f70",