Skip to content
/ go-totp Public

Simple Go package to implement TOTP authentication functionality to the Go app. It supports ECDH as well.

License

Notifications You must be signed in to change notification settings

KEINOS/go-totp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-totp

go1.22+ Go Reference

go-totp is a simple Go package to implement Timebased-One-Time-Password authentication functionality, a.k.a. TOTP, to the Go app.

Note: This is a wrapper of the awesome github.com/pquerna/otp package to facilitate the use of TOTP.

As an optional feature, this package supports ECDH (Elliptic-Curve Diffie-Hellman) key agreement protocol, where public keys are exchanged between two parties to obtain a common TOTP passcode.

Usage

// Install module
go get "github.com/KEINOS/go-totp"
// Use package
import "github.com/KEINOS/go-totp/totp"

Basic Usage

// import "github.com/KEINOS/go-totp/totp"

func Example() {
    Issuer := "Example.com"            // name of the service
    AccountName := "alice@example.com" // name of the user

    // Generate a new secret key with default options.
    // Compatible with most TOTP authenticator apps.
    key, err := totp.GenerateKey(Issuer, AccountName)
    if err != nil {
        log.Fatal(err)
    }

    // Print the default option values.
    fmt.Println("- Algorithm:", key.Options.Algorithm)
    fmt.Println("- Period:", key.Options.Period)
    fmt.Println("- Secret Size:", key.Options.SecretSize)
    fmt.Println("- Skew (time tolerance):", key.Options.Skew)
    fmt.Println("- Digits:", key.Options.Digits)

    // Generate 6 digits passcode (valid for 30 seconds)
    passcode, err := key.PassCode()
    if err != nil {
        log.Fatal(err)
    }

    // Validate the passcode
    if key.Validate(passcode) {
        fmt.Println("* Validation result: Passcode is valid")
    }
    //
    // Output:
    // - Algorithm: SHA1
    // - Period: 30
    // - Secret Size: 128
    // - Skew (time tolerance): 1
    // - Digits: 6
    // * Validation result: Passcode is valid
}
// ----------------------------------------------------------------------------
//  Generate a new secret key with custom options
// ----------------------------------------------------------------------------
key, err := totp.GenerateKey(Issuer, AccountName,
    totp.WithAlgorithm(totp.Algorithm("SHA256")),
    totp.WithPeriod(15),
    totp.WithSecretSize(256),
    totp.WithSkew(5),
    totp.WithDigits(totp.DigitsEight),
)

// ----------------------------------------------------------------------------
//  Major methods of totp.Key object
// ----------------------------------------------------------------------------
//  * You should handle the error in your code.

// Generate the current passcode.
//
// Which is a string of 8 digit numbers and valid for with 5 skews as set in
// the above options (5 skew = time tolerance of ± 15 seconds(period) * 5).
passcode, err := key.PassCode()

// Validate the received passcode.
ok := key.Validate(passcode)

// Get 100x100 px image of QR code as PNG byte data.
//
// FixLevelDefault is the 15% of error correction.
qrCodeObj, err := key.QRCode(totp.FixLevelDefault)
pngBytes, err := qrCodeObj.PNG(100, 100)

// Get the secret key in PEM format text.
pemKey, err := key.PEM()

// Get the secret key in TOTP URI format string.
// This is equivalent to key.String().
uriKey := key.URI()

// Retrieve the secret value in various formats.
// ---------------------------------------------

// Get the secret value in Base32 format string.
// This encoding is used in TOTP URI format and is equivalent to
// key.Secret.String().
base32Key := key.Secret.Base32()

// Get the secret value in Base62 format string.
base62Key := key.Secret.Base62()

// Get the secret value in Base64 format string.
// This encoding is used in PEM format.
base64Key := key.Secret.Base64()

// Get the secret value in bytes. This is the raw secret value.
rawKey := key.Secret.Bytes()

ECDH Support

This package supports ECDH key agreement protocol for the TOTP secret key generation (deriving TOTP secret from ECDH shared secret).

// Pre-agreement between Alice and Bob. commonCtx can be any string but consistent
// between Alice and Bob.
commonCurve := ecdh.X25519()
commonCtx := "example.com alice@example.com bob@example.com TOTP secret v1"

// Key exchange between Alice and Bob.
alicePriv, alicePub := getECDHKeysSomeHowForAlice(commonCurve)
bobPriv, bobPub := getECDHKeysSomeHowForBob(commonCurve)

// Generate a new TOTP key for Alice using:
// - Alice's ECDH private key
// - Bob's ECDH public key
// - Alice and Bob's common context string
Issuer := "Example.com"
AccountName := "alice@example.com"

key, err := totp.GenerateKey(Issuer, AccountName,
    totp.WithECDH(alicePriv, bobPub, commonCtx),
)
if err != nil {
    log.Fatal(err)
}

// Alice generates 6 digits of TOTP passcode which should be the same as Bob's.
passcode, err := key.PassCode()
if err != nil {
    log.Fatal(err)
}

A shared secret key can be created by exchanging a public ECDH key between two parties. This shared secret key is used to derive the TOTP key. Thus the same TOTP passcode can be shared within the same time period.

This feature is useful when a shared but ephemeral/volatile secret value (a common TOTP passcode) is required to increase security between two parties.

For example, a time-based shared salt for hashing or an additional value to generate a shared secret key for symmetric encryption.

The values expire, but the possibilities are endless.

Contributing

go1.22+ Go Reference Opened Issues PR

Any Pull-Request for improvement is welcome!

Test Statuses

UnitTests PlatformTests

golangci-lint CodeQL-Analysis Vulnerability Scan

codecov Go Report Card

License, copyright and credits

About

Simple Go package to implement TOTP authentication functionality to the Go app. It supports ECDH as well.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Languages