...
1
17
18 package otp
19
20 import (
21 "github.com/boombuler/barcode"
22 "github.com/boombuler/barcode/qr"
23
24 "crypto/md5"
25 "crypto/sha1"
26 "crypto/sha256"
27 "crypto/sha512"
28 "errors"
29 "fmt"
30 "hash"
31 "image"
32 "net/url"
33 "strings"
34 "strconv"
35 )
36
37
38 var ErrValidateSecretInvalidBase32 = errors.New("Decoding of secret as base32 failed.")
39
40
41 var ErrValidateInputInvalidLength = errors.New("Input length unexpected")
42
43
44 var ErrGenerateMissingIssuer = errors.New("Issuer must be set")
45
46
47 var ErrGenerateMissingAccountName = errors.New("AccountName must be set")
48
49
50 type Key struct {
51 orig string
52 url *url.URL
53 }
54
55
56
57
58
59
60 func NewKeyFromURL(orig string) (*Key, error) {
61 s := strings.TrimSpace(orig)
62
63 u, err := url.Parse(s)
64
65 if err != nil {
66 return nil, err
67 }
68
69 return &Key{
70 orig: s,
71 url: u,
72 }, nil
73 }
74
75 func (k *Key) String() string {
76 return k.orig
77 }
78
79
80
81
82 func (k *Key) Image(width int, height int) (image.Image, error) {
83 b, err := qr.Encode(k.orig, qr.M, qr.Auto)
84
85 if err != nil {
86 return nil, err
87 }
88
89 b, err = barcode.Scale(b, width, height)
90
91 if err != nil {
92 return nil, err
93 }
94
95 return b, nil
96 }
97
98
99 func (k *Key) Type() string {
100 return k.url.Host
101 }
102
103
104 func (k *Key) Issuer() string {
105 q := k.url.Query()
106
107 issuer := q.Get("issuer")
108
109 if issuer != "" {
110 return issuer
111 }
112
113 p := strings.TrimPrefix(k.url.Path, "/")
114 i := strings.Index(p, ":")
115
116 if i == -1 {
117 return ""
118 }
119
120 return p[:i]
121 }
122
123
124 func (k *Key) AccountName() string {
125 p := strings.TrimPrefix(k.url.Path, "/")
126 i := strings.Index(p, ":")
127
128 if i == -1 {
129 return p
130 }
131
132 return p[i+1:]
133 }
134
135
136 func (k *Key) Secret() string {
137 q := k.url.Query()
138
139 return q.Get("secret")
140 }
141
142
143 func (k *Key) Period() uint64 {
144 q := k.url.Query()
145
146 if u, err := strconv.ParseUint(q.Get("period"), 10, 64); err == nil {
147 return u
148 }
149
150
151 return 30
152 }
153
154
155 func (k *Key) URL() string {
156 return k.url.String()
157 }
158
159
160
161 type Algorithm int
162
163 const (
164
165
166
167 AlgorithmSHA1 Algorithm = iota
168 AlgorithmSHA256
169 AlgorithmSHA512
170 AlgorithmMD5
171 )
172
173 func (a Algorithm) String() string {
174 switch a {
175 case AlgorithmSHA1:
176 return "SHA1"
177 case AlgorithmSHA256:
178 return "SHA256"
179 case AlgorithmSHA512:
180 return "SHA512"
181 case AlgorithmMD5:
182 return "MD5"
183 }
184 panic("unreached")
185 }
186
187 func (a Algorithm) Hash() hash.Hash {
188 switch a {
189 case AlgorithmSHA1:
190 return sha1.New()
191 case AlgorithmSHA256:
192 return sha256.New()
193 case AlgorithmSHA512:
194 return sha512.New()
195 case AlgorithmMD5:
196 return md5.New()
197 }
198 panic("unreached")
199 }
200
201
202
203 type Digits int
204
205 const (
206 DigitsSix Digits = 6
207 DigitsEight Digits = 8
208 )
209
210
211 func (d Digits) Format(in int32) string {
212 f := fmt.Sprintf("%%0%dd", d)
213 return fmt.Sprintf(f, in)
214 }
215
216
217 func (d Digits) Length() int {
218 return int(d)
219 }
220
221 func (d Digits) String() string {
222 return fmt.Sprintf("%d", d)
223 }
224
View as plain text