1/** 2 * Copyright 2014 Paul Querna 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18package otp 19 20import ( 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) 35 36// Error when attempting to convert the secret from base32 to raw bytes. 37var ErrValidateSecretInvalidBase32 = errors.New("Decoding of secret as base32 failed.") 38 39// The user provided passcode length was not expected. 40var ErrValidateInputInvalidLength = errors.New("Input length unexpected") 41 42// When generating a Key, the Issuer must be set. 43var ErrGenerateMissingIssuer = errors.New("Issuer must be set") 44 45// When generating a Key, the Account Name must be set. 46var ErrGenerateMissingAccountName = errors.New("AccountName must be set") 47 48// Key represents an TOTP or HTOP key. 49type Key struct { 50 orig string 51 url *url.URL 52} 53 54// NewKeyFromURL creates a new Key from an TOTP or HOTP url. 55// 56// The URL format is documented here: 57// https://github.com/google/google-authenticator/wiki/Key-Uri-Format 58// 59func NewKeyFromURL(orig string) (*Key, error) { 60 s := strings.TrimSpace(orig) 61 62 u, err := url.Parse(s) 63 64 if err != nil { 65 return nil, err 66 } 67 68 return &Key{ 69 orig: s, 70 url: u, 71 }, nil 72} 73 74func (k *Key) String() string { 75 return k.orig 76} 77 78// Image returns an QR-Code image of the specified width and height, 79// suitable for use by many clients like Google-Authenricator 80// to enroll a user's TOTP/HOTP key. 81func (k *Key) Image(width int, height int) (image.Image, error) { 82 b, err := qr.Encode(k.orig, qr.M, qr.Auto) 83 84 if err != nil { 85 return nil, err 86 } 87 88 b, err = barcode.Scale(b, width, height) 89 90 if err != nil { 91 return nil, err 92 } 93 94 return b, nil 95} 96 97// Type returns "hotp" or "totp". 98func (k *Key) Type() string { 99 return k.url.Host 100} 101 102// Issuer returns the name of the issuing organization. 103func (k *Key) Issuer() string { 104 q := k.url.Query() 105 106 issuer := q.Get("issuer") 107 108 if issuer != "" { 109 return issuer 110 } 111 112 p := strings.TrimPrefix(k.url.Path, "/") 113 i := strings.Index(p, ":") 114 115 if i == -1 { 116 return "" 117 } 118 119 return p[:i] 120} 121 122// AccountName returns the name of the user's account. 123func (k *Key) AccountName() string { 124 p := strings.TrimPrefix(k.url.Path, "/") 125 i := strings.Index(p, ":") 126 127 if i == -1 { 128 return p 129 } 130 131 return p[i+1:] 132} 133 134// Secret returns the opaque secret for this Key. 135func (k *Key) Secret() string { 136 q := k.url.Query() 137 138 return q.Get("secret") 139} 140 141// URL returns the OTP URL as a string 142func (k *Key) URL() string { 143 return k.url.String() 144} 145 146// Algorithm represents the hashing function to use in the HMAC 147// operation needed for OTPs. 148type Algorithm int 149 150const ( 151 AlgorithmSHA1 Algorithm = iota 152 AlgorithmSHA256 153 AlgorithmSHA512 154 AlgorithmMD5 155) 156 157func (a Algorithm) String() string { 158 switch a { 159 case AlgorithmSHA1: 160 return "SHA1" 161 case AlgorithmSHA256: 162 return "SHA256" 163 case AlgorithmSHA512: 164 return "SHA512" 165 case AlgorithmMD5: 166 return "MD5" 167 } 168 panic("unreached") 169} 170 171func (a Algorithm) Hash() hash.Hash { 172 switch a { 173 case AlgorithmSHA1: 174 return sha1.New() 175 case AlgorithmSHA256: 176 return sha256.New() 177 case AlgorithmSHA512: 178 return sha512.New() 179 case AlgorithmMD5: 180 return md5.New() 181 } 182 panic("unreached") 183} 184 185// Digits represents the number of digits present in the 186// user's OTP passcode. Six and Eight are the most common values. 187type Digits int 188 189const ( 190 DigitsSix Digits = 6 191 DigitsEight Digits = 8 192) 193 194// Format converts an integer into the zero-filled size for this Digits. 195func (d Digits) Format(in int32) string { 196 f := fmt.Sprintf("%%0%dd", d) 197 return fmt.Sprintf(f, in) 198} 199 200// Length returns the number of characters for this Digits. 201func (d Digits) Length() int { 202 return int(d) 203} 204 205func (d Digits) String() string { 206 return fmt.Sprintf("%d", d) 207} 208