README

hotp

This package implements the RFC 4226 OATH-HOTP algorithm; these passwords derived from the HMAC-SHA1 of an internal counter. They are presented as (typically) 6 or 8-digit numeric passphrases.

This package was designed to be interoperable with the Google Authenticator app and YubiKeys programmed in OATH-HOTP mode.

Also provided is the hotpgen command-line program. This generates a QR code suitable for use with the Google Authenticator application alongside a text file containing the URL for the QR code. For more information, see the README in the file.

See also the godocs for this package. The hotpweb package provides a simple webapp demonstrating the use of the Google Authenticator interaction.

Storing Keys

These keys are cryptographic secrets. Please store them with all due caution! For example, they could be encrypted in a database using cryptobox.

The HOTP keys can be serialised with the Marshal function; this preserves a "snapshot", so to speak, of the key value. Serialisation is done in DER-format:

SEQUENCE {
	OCTET STRING
	INTEGER
	INTEGER
}

Serialised key values can be parsed with the Unmarshal function; as a serialised key value is a snapshot, the counter state at the time of marshalling will be restored.

If the key values are to be stored in a database, the key and counter values must be preserved. To avoid any potention issues, the counter value should be stored using the Counter method (i.e., as an uint64) and key values restored with NewHOTP. It is strongly recommended that the Key field be stored securely. The Digit field can be stored as constant in the program, and used whenever key values are loaded.

Example Usages

Case 1: Google Authenticator

A server that wants to generate a new HOTP authentication for users can generate a new random HOTP source; this example saves a QR code to a file. The user can scan this QR code in with the app on their phone and use it to generate codes for the server.

// Generate a new, random 6-digit HOTP source with an initial
// counter of 0 (the second argument, if true, will randomise
// the counter).
otp, err := GenerateHOTP(6, false)
if err != nil {
	// error handling elided
}

qrCode, err := otp.QR("user@example.net")
if err != nil {
	// error handling elided
}

err = ioutil.WriteFile("user@example.net.png", qrCode
if err != nil {
	// error handling elided
}

After the user has imported this QR code, they can immediately begin using codes for it. The Check method on an OTP source will check whether the code is valid; if it isn't, the counter won't be decremented to prevent the server from falling out of sync. If the either side is suspected of falling out of sync, the Scan method will look ahead a certain window of values. If it finds a valid value, the counter is updated and the two will be in sync again.

The Google Authenticator app on Android also provides users a means to "Check key value"; the IntegrityCheck method will provide the the two values shown here (the initial code and the current counter) that may be used to verify the integrity of the key value.

In the testdata directory, there are three files with the base name of "gauth_example" that contain the HOTP key values used in the test suite. The PNG image may be scanned in using a mobile phone, the text file contains the URL that the QR code is based on, and the .key file may be used with the hotpcli program. The first several codes produced by this URL are:

  • 023667, counter = 0
  • 641344, counter = 1
  • 419615, counter = 2
  • 692589, counter = 3
  • 237233, counter = 4
  • 711695, counter = 5
  • 620195, counter = 6

The codes may be checked against the app to ensure they are correct; these values are used in the test suite to ensure interoperability.

Case 2: YubiKey

A YubiKey programmed in "OATH-HOTP" mode can also be used with this package. The YubiKey user will need to provide their key, and optionally their token identifier for additional security. If the token is used across multiple sites, the Scan function will need to be used (with a probably generous window) to sync the counters initially.

When reading input from a YubiKey, the YubiKey method takes as input the output directly from the token, and splits it into the code, the token identity, and a boolean indicating whether it was valid output. Note that YubiKey does not check whether the code is correct; this ensures that the user can check the code with whatever means is appropriate (i.e. Scan or Check).

In the testdata directory, there is a configuration file containing the paramters for the YubiKey used to generate the test suite. This may be used to program a YubiKey to verify the package's interoperability. The first several codes produced by this configuration are (split into raw output from yubikey / the code / the counter):

  • cccc52345777705179, 705179, counter = 0
  • cccc52345777404068, 404068, counter = 1
  • cccc52345777490143, 490143, counter = 2
  • cccc52345777739740, 739740, counter = 3
  • cccc52345777043269, 043269, counter = 4
  • cccc52345777035666, 035666, counter = 5
  • cccc52345777725326, 725325, counter = 6

TODO

  • Add example code.

Test Coverage

Test coverage is currently at 100%.

Current test status

Build Status

References

  • RFC 4226 - HOTP: An HMAC-Based One-Time Password Algorithm is the specification of the OATH-HOTP algorithm. A copy is provided in the package's source directory.

  • The Key URI Format page on the Google Authenticator wiki documents the URI format for use with the Google Authenticator app. This package follows that format when generating URLs (and by extension, QR codes).

  • The YubiKey manual contains documentation on the YubiKey HOTP format.

Author

hotp was written by Kyle Isom kyle@tyrfingr.is.

License

Copyright (c) 2013 Kyle Isom <kyle@tyrfingr.is>

Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above 
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 

Documentation

Overview

Package hotp implements the RFC 4226 OATH-HOTP algorithm; these passwords derived from the HMAC-SHA1 of an internal counter. They are presented as (typically) 6 or 8-digit numeric passphrases.

The package provides facilities for interacting with YubiKeys programmed in OATH-HOTP mode, as well as with the Google Authenticator application. The package also provides QR-code generation for new OTPs.

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidHOTPURL = errors.New("hotp: invalid HOTP url")

ErrInvalidHOTPURL is returned via FromURL; it indicates a malformed HOTP otpauth URL.

View Source
var PRNG = rand.Reader

PRNG is the source of random data; this is used by GenerateHOTP and should be a cryptographically-secure PRNG.

Functions

func Marshal

func Marshal(otp *HOTP) ([]byte, error)

Marshal serialises an HOTP key value as a DER-encoded byte slice.

Types

type HOTP

type HOTP struct {
	Key []byte

	Digits int
	// contains filtered or unexported fields
}

HOTP represents a new key value for generating one-time passwords; it contains the key used to construct one-time passwords and the counter state used in the OTP generation. Digits contains the number of digits that generated OTPs should output. Key is a cryptographic secret, and should be treated as such.

func FromURL

func FromURL(urlString string) (*HOTP, string, error)

FromURL parses a new HOTP from a URL string. It returns the OTP, the label associated with the OTP, and any errors that occurred.

func GenerateHOTP

func GenerateHOTP(digits int, randCounter bool) (*HOTP, error)

GenerateHOTP will generate a randomised HOTP source; if the randCounter parameter is true, the counter will be randomised.

func NewHOTP

func NewHOTP(key []byte, counter uint64, digits int) *HOTP

NewHOTP intialises a new HOTP instance with the key and counter values. No check is done on the digits, but typical values are 6 and 8.

func Unmarshal

func Unmarshal(in []byte) (otp *HOTP, err error)

Unmarshal parses a DER-encoded serialised HOTP key value.

func (*HOTP) Check

func (otp *HOTP) Check(code string) bool

Check takes an input code and verifies it against the OTP. If successful, the counter is incremented.

func (HOTP) Counter

func (otp HOTP) Counter() uint64

Counter returns the HOTP's 8-byte counter as an unsigned 64-bit integer.

func (HOTP) Increment

func (otp HOTP) Increment()

Increment will increment an HOTP source's counter. This is useful for providers like the Google Authenticator app, which immediately increments the counter and uses the 0 counter value as an integrity check.

func (*HOTP) IntegrityCheck

func (otp *HOTP) IntegrityCheck() (string, uint64)

IntegrityCheck returns two values, the base OTP and the current counter. This is used, for example, with the Google Authenticator app's "Check key value" function and can be used to verify that the application and the provider are in sync.

func (HOTP) OTP

func (otp HOTP) OTP() string

OTP generates a new one-time password.

func (*HOTP) QR

func (otp *HOTP) QR(label string) ([]byte, error)

QR generates a byte slice containing the a QR code encoded as a PNG with level Q error correction.

func (*HOTP) Scan

func (otp *HOTP) Scan(code string, window int) bool

Scan takes a code input (i.e. from the user), and scans ahead within a certain window of counter values. This can be used in the case where the server's counter and the user's counter have fallen out of sync.

func (*HOTP) URL

func (otp *HOTP) URL(label string) string

URL returns a suitable URL, such as for the Google Authenticator app. The label is used by these apps to identify the service to which this OTP belongs. The digits value is ignored by the Google authenticator app, and is therefore elided in the resulting URL.

func (*HOTP) YubiKey

func (otp *HOTP) YubiKey(in string) (string, string, bool)

YubiKey reads an OATH-HOTP string as returned by a YubiKey, and returns three values. The first value contains the actual OTP, the second value contains the YubiKey's token identifier, and the final value indicates whether the input string was a valid YubiKey OTP. This does not check whether the code is correct or not, it only ensures that it is well-formed output from a token and splits the output into the code and the public identity.

Source Files

Directories

Path Synopsis