summaryrefslogtreecommitdiff
path: root/vendor/github.com/pquerna/otp/totp/totp.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/pquerna/otp/totp/totp.go')
-rw-r--r--vendor/github.com/pquerna/otp/totp/totp.go207
1 files changed, 207 insertions, 0 deletions
diff --git a/vendor/github.com/pquerna/otp/totp/totp.go b/vendor/github.com/pquerna/otp/totp/totp.go
new file mode 100644
index 000000000..a2fb7d557
--- /dev/null
+++ b/vendor/github.com/pquerna/otp/totp/totp.go
@@ -0,0 +1,207 @@
+/**
+ * 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"
+ "github.com/pquerna/otp/internal"
+ "io"
+
+ "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 20 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 TOTP Key.
+ Rand io.Reader
+}
+
+var b32NoPadding = base32.StdEncoding.WithPadding(base32.NoPadding)
+
+// 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 = 20
+ }
+
+ if opts.Digits == 0 {
+ opts.Digits = otp.DigitsSix
+ }
+
+ if opts.Rand == nil {
+ opts.Rand = rand.Reader
+ }
+
+ // otpauth://totp/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("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: internal.EncodeQuery(v),
+ }
+
+ return otp.NewKeyFromURL(u.String())
+}