add missing files

new files are
	oath_test
	totp code
This commit is contained in:
Kyle Isom
2013-12-19 00:21:26 -07:00
parent 2ee9cae5ba
commit 1dec15fd11
3 changed files with 236 additions and 0 deletions

151
totp.go Normal file
View File

@@ -0,0 +1,151 @@
package twofactor
import (
"crypto"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base32"
"hash"
"io"
"net/url"
"strconv"
"time"
)
type TOTP struct {
*oath
step uint64
}
func (otp *TOTP) Type() Type {
return OATH_TOTP
}
func (otp *TOTP) otp(counter uint64) string {
return otp.oath.OTP(counter)
}
func (otp *TOTP) OTP() string {
return otp.otp(otp.OTPCounter())
}
func (otp *TOTP) URL(label string) string {
return otp.oath.URL(otp.Type(), label)
}
func (otp *TOTP) SetProvider(provider string) {
otp.provider = provider
}
func (otp *TOTP) otpCounter(t uint64) uint64 {
return (t - otp.counter) / otp.step
}
func (otp *TOTP) OTPCounter() uint64 {
return otp.otpCounter(uint64(time.Now().Unix()))
}
func NewTOTP(key []byte, start uint64, step uint64, digits int, algo crypto.Hash) *TOTP {
h := hashFromAlgo(algo)
if h == nil {
return nil
}
return &TOTP{
oath: &oath{
key: key,
counter: start,
size: digits,
hash: h,
algo: algo,
},
step: step,
}
}
func NewTOTPSHA1(key []byte, start uint64, step uint64, digits int) *TOTP {
return NewTOTP(key, start, step, digits, crypto.SHA1)
}
func NewTOTPSHA256(key []byte, start uint64, step uint64, digits int) *TOTP {
return NewTOTP(key, start, step, digits, crypto.SHA256)
}
func NewTOTPSHA512(key []byte, start uint64, step uint64, digits int) *TOTP {
return NewTOTP(key, start, step, digits, crypto.SHA512)
}
func hashFromAlgo(algo crypto.Hash) func() hash.Hash {
switch algo {
case crypto.SHA1:
return sha1.New
case crypto.SHA256:
return sha256.New
case crypto.SHA512:
return sha512.New
}
return nil
}
// GenerateGoogleTOTP produces a new TOTP token with the defaults expected by
// Google Authenticator.
func GenerateGoogleTOTP() *TOTP {
key := make([]byte, sha1.Size)
if _, err := io.ReadFull(PRNG, key); err != nil {
return nil
}
return NewTOTP(key, 0, 30, 6, crypto.SHA1)
}
func totpFromURL(u *url.URL) (*TOTP, string, error) {
label := u.Path[1:]
v := u.Query()
secret := v.Get("secret")
if secret == "" {
return nil, "", ErrInvalidURL
}
var algo = crypto.SHA1
if algorithm := v.Get("algorithm"); algorithm != "" {
switch {
case algorithm == "SHA256":
algo = crypto.SHA256
case algorithm == "SHA512":
algo = crypto.SHA512
case algorithm != "SHA1":
return nil, "", ErrInvalidAlgo
}
}
var digits = 6
if sdigit := v.Get("digits"); sdigit != "" {
tmpDigits, err := strconv.ParseInt(sdigit, 10, 8)
if err != nil {
return nil, "", err
}
digits = int(tmpDigits)
}
var period uint64 = 30
if speriod := v.Get("period"); speriod != "" {
var err error
period, err = strconv.ParseUint(speriod, 10, 64)
if err != nil {
return nil, "", err
}
}
key, err := base32.StdEncoding.DecodeString(secret)
if err != nil {
return nil, "", err
}
otp := NewTOTP(key, 0, period, digits, algo)
return otp, label, nil
}
func (otp *TOTP) QR(label string) ([]byte, error) {
return otp.oath.QR(otp.Type(), label)
}