package authenticator import ( "crypto/hmac" "crypto/sha1" "crypto/sha256" "crypto/sha512" "encoding/binary" "fmt" "hash" ) // hmacFor returns a fresh HMAC keyed with secret for the given hash type. func hmacFor(h HMACType, secret []byte) hash.Hash { switch h { case HMACSHA256: return hmac.New(sha256.New, secret) case HMACSHA512: return hmac.New(sha512.New, secret) default: return hmac.New(sha1.New, secret) } } // hotpCode computes the RFC 4226 HOTP code for the given (secret, counter) // pair, with the supplied number of decimal digits and HMAC algorithm. func hotpCode(secret []byte, counter uint64, digits int, h HMACType) string { mac := hmacFor(h, secret) var counterBytes [8]byte binary.BigEndian.PutUint64(counterBytes[:], counter) _, _ = mac.Write(counterBytes[:]) sum := mac.Sum(nil) offset := sum[len(sum)-1] & 0x0F truncated := binary.BigEndian.Uint32(sum[offset:offset+4]) & 0x7FFFFFFF mod := uint32(1) for i := 0; i < digits; i++ { mod *= 10 } return fmt.Sprintf("%0*d", digits, truncated%mod) }