otp

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 11, 2024 License: MIT Imports: 15 Imported by: 0

README

OTP

Build Status MIT License Go Doc Go Coverage Release

This is a Golang OTP library compatible with google-authenticator APP.

Installation

$ go get github.com/huk10/go-otp

Usage

Check API docs at https://godoc.org/github.com/huk10/go-otp

Time-based OTPs
secret := otp.Base32Encode(otp.RandomSecret(20))
totp := otp.NewTOTP(secret)

// Get the token of the current time
_ = totp.Now()

// Get the token for the specified time
token := totp.At(time.Unix(1704075000000, 0))

// otp verify
totp.Verify(token,  time.Unix(1704075000000, 0))

// get qrcode 
png, err := totp.KeyURI("alice@google.com", "Example").QRCode()
if err != nil {
    panic(err)
}
// qrcode write to a file
err = os.WriteFile("./qrcode.png", png, 0666)

// get uri
_ = totp.KeyURI("alice@google.com", "Example").URI().String()
Counter-based OTPs
secret := otp.Base32Encode(otp.RandomSecret(20))
hotp := otp.NewHOTP(secret)

// Get the token corresponding to counter 1
token := hotp.At(1)

// otp verify
hotp.Verify(token, 1)

// get qrcode 
png, err := hotp.KeyURI("alice@google.com", "Example").QRCode()
if err != nil {
    panic(err)
}
// qrcode write to a file
err = os.WriteFile("./qrcode.png", png, 0666)

// get uri
_ = hotp.KeyURI("alice@google.com", "Example").URI().String()
Generate random secret
// generate a 20 byte random secret
secret := otp.RandomSecret(20)
// base32 encode
str := otp.Base32Encode(secret)
Google Authenticator Compatible

This library works with the Google Authenticator APP.

// secret 
secret := otp.Base32Encode(otp.RandomSecret(20))

// totp
otp.NewTOTP(secret).KeyURI("alice@google.com", "Example").URI().String()

// hotp
otp.NewHOTP(secret, otp.WithCounter(10)).KeyURI("alice@google.com", "Example").URI().String()

You can use the URI generated by the above code as the QR code content and use the Google Authenticator APP to scan the code and import it.

Working example

Use Google Authenticator to scan the QR code below.

Demo

Now run the following and compare the output:

package main

import (
	"fmt"
	"github.com/huk10/go-otp"
)

func main() {
	totp := otp.NewTOTP("J3W2XPZP5HDYXYRB4HS6ZLU6M6VBO6C6")
	token := totp.Now()
	fmt.Println("Current OTP is", token)
}

License

go-otp is licensed under the MIT License

Documentation

Overview

Package otp One-Time Password (HOTP and TOTP) library for Go. Implements RFC 4226 and RFC 6238.

Support Google Authenticator.

Example:

// 随机生成一个 20 字节的秘钥
secret := otp.Base32Encode(otp.RandomSecret(20))
totp := otp.NewTOTP(secret)
// 基于当前时间生成一个 token
token := totp.Now()
// 校验 token 是否在指定的时间有效
if totp.Verify(token, time.Now()) {
	// token 有效
}
// 生成一个二维码,此可二维码可以使用 Google Authenticator 扫码导入。
png, err := totp.KeyURI("bar@foo.com", "Example").QRCode()
if err != nil {
	panic(err)
}
// 将二维码写入到本地文件中
err = os.WriteFile("./example/qrcode.png", png, 0666)
if err != nil {
	panic(err)
}

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrURIFormat           = errors.New("uri format error")
	ErrSecretDecode        = errors.New("secret base32 decode error")
	ErrSecretCannotBeEmpty = errors.New("secret cannot be empty")
)

Functions

func Base32Decode

func Base32Decode(str string) ([]byte, error)

Base32Decode 对一个字符串进行 base32 解码

func Base32Encode

func Base32Encode(str []byte) string

Base32Encode 对一个字符串进行 base32 编码

func RandomSecret

func RandomSecret(length int) []byte

RandomSecret 获取一个给定长度(字节数)的随机秘钥,如果生成失败将会 panic。

建议存储时将其转换至 base32 或其他的编码,直接转换成字符串可能会存在换行符等奇怪的字符。

内部使用 rand.Read 方法,如果此方法报错将会 panic

rfc4266 中建议 secret 最少为 160 位也就是 20 个字节。

https://datatracker.ietf.org/doc/html/rfc4226

也可看下此文档解释自行选择合适长度:

https://github.com/darrenedale/php-totp/blob/HEAD/Secrets.md

Types

type Algorithms

type Algorithms int

Algorithms 支持的 HMAC 类型。

默认值:HMAC_SHA1,与 Google Authenticator 兼容。

See https://github.com/google/google-authenticator/wiki/Key-Uri-Format

const (
	AlgorithmSHA1 Algorithms = iota + 1
	AlgorithmSHA256
	AlgorithmSHA512
)

func (Algorithms) String

func (h Algorithms) String() string

String 枚举值转换为字符串形式 - 该值可以放置在 uri 上。

type Digits

type Digits int

Digits 生成出来的一次性密码的长度。6 和 8 是最常见的值。

const (
	DigitsSix   Digits = 6
	DigitsEight Digits = 8
)

type HOTP

type HOTP struct {
	Otp
	// base32 encoded string
	Secret string
	// contains filtered or unexported fields
}

HOTP 基于 RFC-4266 的 HOTP 算法

func NewHOTP

func NewHOTP(secret string, options ...Option) *HOTP

NewHOTP 创建一个 HOTP 结构体,可以使用 option 的模式传递参数。

Params:

secret       : 必传,一个 base32 编码后的字符串,建议使用 RandomSecret 方法生成的。
WithCounter  : 设置初始计数器,该值仅用于 KeyURI 方法。
WithSkew     : 是否校验相邻的窗口。
WithAlgorithm: 设置 hmac 算法类型。

Panic:

  • secret base32 decode error
  • secret is an empty string

注意: Google Authenticator 可能仅支持 Counter 这一个参数

See https://github.com/google/google-authenticator/wiki/Key-Uri-Format

Example:

secret := Base32Encode(RandomSecret(20))
hotp   := NewHOTP(secret, WithCounter(2))

func (*HOTP) At

func (h *HOTP) At(counter int64) string

At 通过指定的 Counter 生成一个 token。

Example:

hotp  := NewHOTP(Base32Encode(RandomSecret(20)))
token := hotp.At(1)  	       // 使用的 1 作为counter 生成 token
bool  := hotp.Verify(token, 1) // 校验 token 是否有效

func (*HOTP) KeyURI

func (h *HOTP) KeyURI(account, issuer string) *KeyURI

KeyURI 返回一个 KeyURI 结构体,其包含转换至 URI 和生成二维码的方法。

func (*HOTP) Verify

func (h *HOTP) Verify(token string, counter int64) bool

Verify 校验token是否有效,窗口内的所有结果都认为有效。

Params:

token  : 需要进行校验的参数,一个字符串,如果字符串为空将会返回 false
counter: 计数器

Example:

hotp  := NewHOTP(Base32Encode(RandomSecret(20)), WithSkew(1))
token := hotp.At(2)  		   // 使用的 2 作为counter 生成 token
bool  := hotp.Verify(token, 2) // 通过 WithSkew 方法指定 skew 参数为1,那么这里将会校验 counter 为 1、2、3 的token

type KeyURI

type KeyURI struct {
	// otp 算法的类型只能是 totp or hotp
	Type string
	// 标签,用于识别密钥与哪个帐户关联。它包含一个帐户名称,该名称是一个 URI 编码的字符串,可以选择以标识管理该帐户的提供商或服务的发行者字符串为前缀。
	// 发行者前缀和帐户名称应使用文字或 URL 编码的冒号分隔,并且帐户名称之前可以有可选空格。发行人或账户名称本身都不能包含冒号。
	// 根据 Google Authenticator 的建议,应该拼接发行商字符串为前缀。
	// 需要已被 url.QueryEscape 方法处理过。
	Label string
	// hotp 或 totp 采用的哈希算法类型
	// Google Authenticator 可能会忽略此参数,而采用默认值:HMAC-SHA1。
	Algorithm string
	// 向用户显示一次性密码的长度。默认值为 6。
	// Google Authenticator 可能会忽略此参数,而采用默认值 6。
	Digits int
	// 当 type 为 hotp 时必选,它将设置初始计数器值。
	Counter int64
	// 仅当 type 为 totp 时可选,该 period 参数定义 TOTP 密码的有效期限(以秒为单位)。默认值为 30。
	// Google Authenticator 可能会忽略此参数,而采用默认值 30。
	Period int
	// 发行商,使用 URL 编码进行编码的字符串
	// 需要已被 url.QueryEscape 方法处理过。
	Issuer string
	// base32 编码的任意字符,不应该填充。
	Secret string
}

KeyURI TOTP 或 HOTP 的 URI 包含的参数。

URI 的格式可以参考:https://github.com/google/google-authenticator/wiki/Key-Uri-Format

部分属性 Google Authenticator 可能不会采用仅支持默认值。具体细节可以查看上面链接的文档。

func FromURI

func FromURI(uri string) (*KeyURI, error)

FromURI 解析 URI 创建一个 KeyURI 结构体。

func (KeyURI) QRCode

func (p KeyURI) QRCode() ([]byte, error)

QRCode 将此 URI 信息生成一个二维码,可供 Google Authenticator 扫码导入。

func (KeyURI) URI

func (p KeyURI) URI() *url.URL

URI 生成 otpauth 的 URI 形式,可以将其作为二维码的内容供 Google Authenticator 扫码导入。 params 顺序:secret、issuer、algorithm、digits、period、counter

type Option

type Option func(opt *Otp)

func WithAlgorithm

func WithAlgorithm(algorithm Algorithms) Option

WithAlgorithm 配置哈希算法类型。

func WithCounter

func WithCounter(counter int64) Option

WithCounter 配置计数器的值,默认为 1 (Google 的默认就是 1),仅支持 HOTP 类型。

func WithDigits

func WithDigits(digits Digits) Option

WithDigits 配置一次性密码的显示长度,默认为 6, Google Authenticator 可能不支持其他的长度。

func WithPeriod

func WithPeriod(period int) Option

WithPeriod 配置时间一次性密码的有效期,默认 30 秒,仅支持 TOTP 类型。

取值范围是:period >=10 如果传入的值小于 10 将会设置为 10。

func WithSkew

func WithSkew(skew int) Option

WithSkew 配置同时校验的窗口数,默认为 0 仅校验当前时间窗口。

取值范围是:skew >=0 如果传入的值小于 0 将会设置为 0。

type Otp

type Otp struct {
	// 指定时间窗口,默认 30 秒有效期。
	// Google Authenticator 可能仅支持默认参数。
	Period int
	// 初始计数器数值,默认为 1。
	// 该参数仅用来指定 otpauth uri 上的 counter 参数,不会使用它来生成 token
	Counter int64
	// 指定一次性密码的长度,默认 6 位数字。
	// Google Authenticator 可能仅支持默认参数。
	Digits Digits
	// 是否校验相邻的时间窗口,默认为 0。
	// 有些时候服务端的时间和客户端的时间并不是同步的,存在时间误差,再加上网络延时,一次性密码的剩余有效期等等,密码刚到达服务端可能就过期了,
	// 这时候可以通过此参数为相邻几个时间窗口进行校验,加强用户体验,但是安全性降低了。
	// 如果此参数为1,那么会同时校验当前时间窗口、上个时间窗口以及下个时间窗口。如果是 HOTP 那么就是相邻的计数器。
	Skew int
	// 指定 hmac 算法,默认 hmac-sha1
	// Google Authenticator 可能仅支持默认参数。
	Algorithm Algorithms
}

type TOTP

type TOTP struct {
	Otp
	// base32 encoded string
	Secret string
	// contains filtered or unexported fields
}

TOTP 基于 RFC-6238 的 TOTP 算法

func NewTOTP

func NewTOTP(secret string, options ...Option) *TOTP

NewTOTP 创建一个 TOTP 结构体,可以使用 option 的模式传递参数。

Params:

secret       : 必传,一个 base32 编码后的字符串,建议使用 RandomSecret 方法生成的。
WithPeriod   : 设置 token 有效期长度。
WithSkew     : 是否校验相邻的窗口。
WithAlgorithm: 设置 hmac 算法类型。

Panic:

  • secret base32 decode error
  • secret is an empty string

默认参数才是 Google Authenticator 兼容的,自定义参数的话 Google Authenticator 可能不会识别。

See https://github.com/google/google-authenticator/wiki/Key-Uri-Format

Example:

secret := Base32Encode(RandomSecret(20))
totp   := NewTOTP(secret, WithDigits(DigitsEight))

func (*TOTP) At

func (o *TOTP) At(t time.Time) string

At 生成某个时间点的 token。

func (*TOTP) Expiration

func (o *TOTP) Expiration(t time.Time) int

Expiration 获取指定时间窗口的 token 剩余有效时间。

func (*TOTP) KeyURI

func (o *TOTP) KeyURI(account, issuer string) *KeyURI

KeyURI 返回一个 KeyURI 结构体,其包含转换至 URI 和生成二维码的方法。

func (*TOTP) Now

func (o *TOTP) Now() string

Now 基于当前时间点生成 token。

func (*TOTP) Verify

func (o *TOTP) Verify(token string, t time.Time) bool

Verify 校验 token 是否在指定的时间有效。

Params:

token: 需要进行校验的参数,一个字符串,如果字符串为空将会返回 false。
t    : 指定的时间,用以校验 token 在这个时间点是否仍有效。

func (*TOTP) WithExpiration

func (o *TOTP) WithExpiration(t time.Time) (string, int)

WithExpiration 获取指定时间的 token 和对应的剩余有效时间。

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL