summaryrefslogtreecommitdiff
path: root/vendor/github.com/pquerna/otp/hotp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pquerna/otp/hotp')
-rw-r--r--vendor/github.com/pquerna/otp/hotp/hotp.go216
1 files changed, 216 insertions, 0 deletions
diff --git a/vendor/github.com/pquerna/otp/hotp/hotp.go b/vendor/github.com/pquerna/otp/hotp/hotp.go
new file mode 100644
index 000000000..13a193e94
--- /dev/null
+++ b/vendor/github.com/pquerna/otp/hotp/hotp.go
@@ -0,0 +1,216 @@
+/**
+ * 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"
+ "github.com/pquerna/otp/internal"
+ "io"
+
+ "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) {
+ //Set default value
+ if opts.Digits == 0 {
+ opts.Digits = otp.DigitsSix
+ }
+ // As noted in issue #10 and #17 this adds support for TOTP secrets that are
+ // missing their padding.
+ secret = strings.TrimSpace(secret)
+ if n := len(secret) % 8; n != 0 {
+ secret = secret + strings.Repeat("=", 8-n)
+ }
+
+ // As noted in issue #24 Google has started producing base32 in lower case,
+ // but the StdEncoding (and the RFC), expect a dictionary of only upper case letters.
+ secret = strings.ToUpper(secret)
+
+ 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
+ // Secret to store. Defaults to a randomly generated secret of SecretSize. You should generally leave this empty.
+ Secret []byte
+ // Digits to request. Defaults to 6.
+ Digits otp.Digits
+ // Algorithm to use for HMAC. Defaults to SHA1.
+ Algorithm otp.Algorithm
+ // Reader to use for generating HOTP Key.
+ Rand io.Reader
+}
+
+var b32NoPadding = base32.StdEncoding.WithPadding(base32.NoPadding)
+
+// 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
+ }
+
+ if opts.Digits == 0 {
+ opts.Digits = otp.DigitsSix
+ }
+
+ if opts.Rand == nil {
+ opts.Rand = rand.Reader
+ }
+
+ // otpauth://hotp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&issuer=Example
+
+ v := url.Values{}
+ if len(opts.Secret) != 0 {
+ v.Set("secret", b32NoPadding.EncodeToString(opts.Secret))
+ } else {
+ secret := make([]byte, opts.SecretSize)
+ _, err := opts.Rand.Read(secret)
+ if err != nil {
+ return nil, err
+ }
+ v.Set("secret", b32NoPadding.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: internal.EncodeQuery(v),
+ }
+
+ return otp.NewKeyFromURL(u.String())
+}