├── testdata ├── gauth_example.txt ├── gauth_example.key ├── gauth_example.png └── yubikey.csv ├── LICENSE ├── hotpgen ├── LICENSE ├── hotpgen.go └── README ├── doc.go ├── hotpcli ├── cli.go └── README ├── README.md ├── hotp.go ├── hotp_test.go └── rfc4226.txt /testdata/gauth_example.txt: -------------------------------------------------------------------------------- 1 | otpauth://hotp/kyle?counter=0&secret=EXZLUP7IGHQ673ZCP32RTLRU2N427Z6L -------------------------------------------------------------------------------- /testdata/gauth_example.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gokyle/hotp/HEAD/testdata/gauth_example.key -------------------------------------------------------------------------------- /testdata/gauth_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gokyle/hotp/HEAD/testdata/gauth_example.png -------------------------------------------------------------------------------- /testdata/yubikey.csv: -------------------------------------------------------------------------------- 1 | LOGGING START,12/17/13 4:16 PM 2 | OATH-HOTP,12/17/13 4:16 PM,1,ccccgdefgiii,,d4be97ace3317295d895ebd6b2eca67849794db3,,,0,1,0,6,0,0,0,0,0,0 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kyle Isom 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /hotpgen/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kyle Isom 2 | 3 | Permission to use, copy, modify, and distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package hotp implements the RFC 4226 OATH-HOTP algorithm; these 3 | passwords derived from the HMAC-SHA1 of an internal counter. They 4 | are presented as (typically) 6 or 8-digit numeric passphrases. 5 | 6 | The package provides facilities for interacting with YubiKeys 7 | programmed in OATH-HOTP mode, as well as with the Google 8 | Authenticator application. The package also provides QR-code 9 | generation for new OTPs. 10 | */ 11 | package hotp 12 | 13 | /* 14 | Copyright (c) 2013 Kyle Isom 15 | 16 | Permission to use, copy, modify, and distribute this software for any 17 | purpose with or without fee is hereby granted, provided that the above 18 | copyright notice and this permission notice appear in all copies. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 21 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 22 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 23 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 24 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 25 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 26 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27 | */ 28 | -------------------------------------------------------------------------------- /hotpgen/hotpgen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base32" 5 | "flag" 6 | "fmt" 7 | "github.com/gokyle/hotp" 8 | "io/ioutil" 9 | ) 10 | 11 | func main() { 12 | digits := flag.Int("d", 6, "number of digits") 13 | doRand := flag.Bool("r", false, "randomise counter") 14 | flag.Parse() 15 | 16 | var label string 17 | if flag.NArg() == 1 { 18 | label = flag.Arg(0) 19 | } 20 | 21 | otp, err := hotp.GenerateHOTP(*digits, *doRand) 22 | if err != nil { 23 | fmt.Printf("! %v\n", err.Error()) 24 | return 25 | } 26 | 27 | url := otp.URL(label) 28 | png, err := otp.QR(label) 29 | if err != nil { 30 | fmt.Printf("! %v\n", err.Error()) 31 | return 32 | } 33 | 34 | filename := label 35 | if label == "" { 36 | filename = base32.StdEncoding.EncodeToString([]byte(url)) 37 | } 38 | err = ioutil.WriteFile(filename+".png", png, 0644) 39 | if err != nil { 40 | fmt.Printf("! %v\n", err.Error()) 41 | return 42 | } 43 | 44 | err = ioutil.WriteFile(filename+".txt", []byte(url), 0644) 45 | if err != nil { 46 | fmt.Printf("! %v\n", err.Error()) 47 | return 48 | } 49 | 50 | keyFile, err := hotp.Marshal(otp) 51 | if err != nil { 52 | fmt.Printf("! %v\n", err.Error()) 53 | return 54 | } 55 | err = ioutil.WriteFile(filename+".key", keyFile, 0644) 56 | if err != nil { 57 | fmt.Printf("! %v\n", err.Error()) 58 | return 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /hotpgen/README: -------------------------------------------------------------------------------- 1 | hotpgen: generate new HOTP key values from the command line 2 | 3 | Usage: 4 | 5 | hotpgen [-d digits] [-p password] [-r] [label] 6 | -d digits Specify the number of digits to 7 | use, typically 6 or 8; defaults 8 | to 6. 9 | 10 | -r Randomise the initial counter; 11 | defaults to false as this is 12 | not supported by all clients. 13 | 14 | label An optional label for the key; 15 | for example, an email address 16 | or username. 17 | 18 | The program will dump out three files; a PNG image, a text file, 19 | and a DER-encoded key file that can be used with hotpcli. The text 20 | file contains the URL encoded in the QR code; the QR code may be 21 | displayed to users to scan into their mobile apps. If a label is 22 | provided, the files are named by the label (e.g. label.png and 23 | label.txt); otherwise, the files are named as the base32-encoded 24 | URL. 25 | 26 | 27 | LICENSE 28 | 29 | Copyright (c) 2013 Kyle Isom 30 | 31 | Permission to use, copy, modify, and distribute this software for any 32 | purpose with or without fee is hereby granted, provided that the above 33 | copyright notice and this permission notice appear in all copies. 34 | 35 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 36 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 37 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 38 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 39 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 40 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 41 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42 | 43 | -------------------------------------------------------------------------------- /hotpcli/cli.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/gokyle/hotp" 7 | "io/ioutil" 8 | ) 9 | 10 | func main() { 11 | check := flag.Bool("c", false, "do integrity check") 12 | noUpdate := flag.Bool("n", false, "don't update counter") 13 | keyFile := flag.String("k", "hotp.key", "key file") 14 | url := flag.String("u", "", "URL to load new key from") 15 | write := flag.Bool("w", false, "only write URL-loaded key to file") 16 | flag.Parse() 17 | 18 | var otp *hotp.HOTP 19 | if *url != "" { 20 | var err error 21 | otp, _, err = hotp.FromURL(*url) 22 | if err != nil { 23 | fmt.Printf("[!] %v\n", err.Error()) 24 | return 25 | } 26 | 27 | if *write { 28 | out, err := hotp.Marshal(otp) 29 | if err != nil { 30 | fmt.Printf("[!] %v\n", err.Error()) 31 | return 32 | } 33 | 34 | err = ioutil.WriteFile(*keyFile, out, 0600) 35 | if err != nil { 36 | fmt.Printf("[!] %v\n", err.Error()) 37 | return 38 | } 39 | 40 | return 41 | } 42 | } else { 43 | in, err := ioutil.ReadFile(*keyFile) 44 | if err != nil { 45 | fmt.Printf("[!] %v\n", err.Error()) 46 | return 47 | } 48 | 49 | otp, err = hotp.Unmarshal(in) 50 | if err != nil { 51 | fmt.Printf("[!] %v\n", err.Error()) 52 | return 53 | } 54 | } 55 | 56 | if *check { 57 | code, counter := otp.IntegrityCheck() 58 | fmt.Println(" code:", code) 59 | fmt.Println("counter:", counter) 60 | } else { 61 | fmt.Println(otp.OTP()) 62 | } 63 | 64 | if !*noUpdate { 65 | out, err := hotp.Marshal(otp) 66 | if err != nil { 67 | fmt.Printf("[!] %v\n", err.Error()) 68 | return 69 | } 70 | 71 | err = ioutil.WriteFile(*keyFile, out, 0600) 72 | if err != nil { 73 | fmt.Printf("[!] %v\n", err.Error()) 74 | return 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /hotpcli/README: -------------------------------------------------------------------------------- 1 | hotpcli: OATH-HOTP command line tool 2 | 3 | hotpcli reads the DER-encoded key file specified and prints out the next code, 4 | updating the key file with the new key. 5 | 6 | 7 | Usage: 8 | hotpcli [-cnw] [-k keyfile] [-u url] 9 | -c Print out an integrity check; this 10 | displays a baseline key value and 11 | the current counter for synchronisation. 12 | -k keyfile Specify the file containing the 13 | key; defaults to hotp.key. 14 | -n Don't update the counter in the key 15 | file. 16 | -u url An OATH-HOTP URL containing the 17 | key-value information to use for 18 | the key. 19 | -w Write the url specified with -u to the 20 | key file and exit. 21 | 22 | The program automatically updates the key file with the new counter; 23 | the -n option will allow this behaviour to be overridden. To generate 24 | a key from a url, pass the -w and -u options; the counter will not be 25 | updated. 26 | 27 | 28 | LICENSE 29 | 30 | Copyright (c) 2013 Kyle Isom 31 | 32 | Permission to use, copy, modify, and distribute this software for any 33 | purpose with or without fee is hereby granted, provided that the above 34 | copyright notice and this permission notice appear in all copies. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 37 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 38 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 39 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 40 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 41 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 42 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 43 | 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## `hotp` 2 | 3 | This package implements the RFC 4226 OATH-HOTP algorithm; these 4 | passwords derived from the HMAC-SHA1 of an internal counter. They 5 | are presented as (typically) 6 or 8-digit numeric passphrases. 6 | 7 | This package was designed to be interoperable with the Google 8 | Authenticator app and YubiKeys programmed in OATH-HOTP mode. 9 | 10 | Also provided is the `hotpgen` command-line program. This generates 11 | a QR code suitable for use with the Google Authenticator application 12 | alongside a text file containing the URL for the QR code. For more 13 | information, see the README in the file. 14 | 15 | See also the [godocs](https://godoc.org/github.com/gokyle/hotp/) 16 | for this package. The [hotpweb](https://github.com/gokyle/hotpweb/) 17 | package provides a simple webapp demonstrating the use of the Google 18 | Authenticator interaction. 19 | 20 | 21 | ### Storing Keys 22 | 23 | > **These keys are cryptographic secrets.** Please store them with 24 | > all due caution! For example, they could be encrypted in a database 25 | > using [cryptobox](https://github.com/cryptobox/gocryptobox/). 26 | 27 | The HOTP keys can be serialised with the `Marshal` function; this preserves 28 | a "snapshot", so to speak, of the key value. Serialisation is done 29 | in DER-format: 30 | 31 | ``` 32 | SEQUENCE { 33 | OCTET STRING 34 | INTEGER 35 | INTEGER 36 | } 37 | ``` 38 | 39 | Serialised key values can be parsed with the `Unmarshal` function; 40 | as a serialised key value is a snapshot, the counter state at the 41 | time of marshalling will be restored. 42 | 43 | If the key values are to be stored in a database, the key and counter 44 | values must be preserved. To avoid any potention issues, the counter 45 | value should be stored using the `Counter` method (i.e., as an 46 | `uint64`) and key values restored with `NewHOTP`. *It is strongly 47 | recommended that the `Key` field be stored securely.* The `Digit` 48 | field can be stored as constant in the program, and used whenever 49 | key values are loaded. 50 | 51 | 52 | ### Example Usages 53 | 54 | #### Case 1: Google Authenticator 55 | 56 | A server that wants to generate a new HOTP authentication for users 57 | can generate a new random HOTP source; this example saves a QR code 58 | to a file. The user can scan this QR code in with the app on their 59 | phone and use it to generate codes for the server. 60 | 61 | // Generate a new, random 6-digit HOTP source with an initial 62 | // counter of 0 (the second argument, if true, will randomise 63 | // the counter). 64 | otp, err := GenerateHOTP(6, false) 65 | if err != nil { 66 | // error handling elided 67 | } 68 | 69 | qrCode, err := otp.QR("user@example.net") 70 | if err != nil { 71 | // error handling elided 72 | } 73 | 74 | err = ioutil.WriteFile("user@example.net.png", qrCode 75 | if err != nil { 76 | // error handling elided 77 | } 78 | 79 | After the user has imported this QR code, they can immediately begin 80 | using codes for it. The `Check` method on an OTP source will check 81 | whether the code is valid; if it isn't, the counter won't be 82 | decremented to prevent the server from falling out of sync. If the 83 | either side is suspected of falling out of sync, the `Scan` method 84 | will look ahead a certain window of values. If it finds a valid 85 | value, the counter is updated and the two will be in sync again. 86 | 87 | The Google Authenticator app on Android also provides users a means 88 | to "Check key value"; the `IntegrityCheck` method will provide the 89 | the two values shown here (the initial code and the current counter) 90 | that may be used to verify the integrity of the key value. 91 | 92 | In the `testdata` directory, there are three files with the base name 93 | of "gauth_example" that contain the HOTP key values used in the 94 | test suite. The PNG image may be scanned in using a mobile phone, 95 | the text file contains the URL that the QR code is based on, and 96 | the `.key` file may be used with the `hotpcli` program. The first 97 | several codes produced by this URL are: 98 | 99 | * 023667, counter = 0 100 | * 641344, counter = 1 101 | * 419615, counter = 2 102 | * 692589, counter = 3 103 | * 237233, counter = 4 104 | * 711695, counter = 5 105 | * 620195, counter = 6 106 | 107 | The codes may be checked against the app to ensure they are correct; 108 | these values are used in the test suite to ensure interoperability. 109 | 110 | 111 | #### Case 2: YubiKey 112 | 113 | A YubiKey programmed in "OATH-HOTP" mode can also be used with this 114 | package. The YubiKey user will need to provide their key, and 115 | optionally their token identifier for additional security. If the 116 | token is used across multiple sites, the `Scan` function will need 117 | to be used (with a probably generous window) to sync the counters 118 | initially. 119 | 120 | When reading input from a YubiKey, the `YubiKey` method takes as 121 | input the output directly from the token, and splits it into the 122 | code, the token identity, and a boolean indicating whether it was 123 | valid output. Note that `YubiKey` **does not** check whether the 124 | code is correct; this ensures that the user can check the code with 125 | whatever means is appropriate (i.e. `Scan` or `Check`). 126 | 127 | In the `testdata` directory, there is a configuration file containing 128 | the paramters for the YubiKey used to generate the test suite. This 129 | may be used to program a YubiKey to verify the package's interoperability. 130 | The first several codes produced by this configuration are (split 131 | into raw output from yubikey / the code / the counter): 132 | 133 | * cccc52345777705179, 705179, counter = 0 134 | * cccc52345777404068, 404068, counter = 1 135 | * cccc52345777490143, 490143, counter = 2 136 | * cccc52345777739740, 739740, counter = 3 137 | * cccc52345777043269, 043269, counter = 4 138 | * cccc52345777035666, 035666, counter = 5 139 | * cccc52345777725326, 725325, counter = 6 140 | 141 | 142 | ### TODO 143 | 144 | * Add example code. 145 | 146 | 147 | ### Test Coverage 148 | 149 | Test coverage is currently at 100%. 150 | 151 | #### Current test status 152 | 153 | [![Build Status](https://drone.io/github.com/gokyle/hotp/status.png)](https://drone.io/github.com/gokyle/hotp/latest) 154 | 155 | 156 | ### References 157 | 158 | * [RFC 4226 - *HOTP: An HMAC-Based One-Time Password Algorithm*](http://www.ietf.org/rfc/rfc4226.txt) 159 | is the specification of the OATH-HOTP algorithm. A copy is provided 160 | in the package's source directory. 161 | 162 | * The [Key URI Format](https://code.google.com/p/google-authenticator/wiki/KeyUriFormat) 163 | page on the Google Authenticator wiki documents the URI format for 164 | use with the Google Authenticator app. This package follows that 165 | format when generating URLs (and by extension, QR codes). 166 | 167 | * The [YubiKey manual](http://www.yubico.com/wp-content/uploads/2013/07/YubiKey-Manual-v3_1.pdf) 168 | contains documentation on the YubiKey HOTP format. 169 | 170 | 171 | ### Author 172 | 173 | `hotp` was written by Kyle Isom . 174 | 175 | 176 | ### License 177 | 178 | ``` 179 | Copyright (c) 2013 Kyle Isom 180 | 181 | Permission to use, copy, modify, and distribute this software for any 182 | purpose with or without fee is hereby granted, provided that the above 183 | copyright notice and this permission notice appear in all copies. 184 | 185 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 186 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 187 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 188 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 189 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 190 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 191 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 192 | ``` 193 | -------------------------------------------------------------------------------- /hotp.go: -------------------------------------------------------------------------------- 1 | package hotp 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/rand" 6 | "crypto/sha1" 7 | "crypto/subtle" 8 | "encoding/asn1" 9 | "encoding/base32" 10 | "encoding/binary" 11 | "errors" 12 | "fmt" 13 | "io" 14 | "math" 15 | "math/big" 16 | "net/url" 17 | "strconv" 18 | 19 | "github.com/mdp/rsc/qr" 20 | ) 21 | 22 | // RFC 4226 specifies the counter as being 8 bytes. 23 | const ctrSize = 8 24 | 25 | // ErrInvalidHOTPURL is returned via FromURL; it indicates a malformed 26 | // HOTP otpauth URL. 27 | var ErrInvalidHOTPURL = errors.New("hotp: invalid HOTP url") 28 | 29 | // PRNG is the source of random data; this is used by GenerateHOTP 30 | // and should be a cryptographically-secure PRNG. 31 | var PRNG = rand.Reader 32 | 33 | // HOTP represents a new key value for generating one-time passwords; 34 | // it contains the key used to construct one-time passwords and the 35 | // counter state used in the OTP generation. Digits contains the 36 | // number of digits that generated OTPs should output. Key is a 37 | // cryptographic secret, and should be treated as such. 38 | type HOTP struct { 39 | Key []byte 40 | counter *[ctrSize]byte 41 | Digits int 42 | } 43 | 44 | // Counter returns the HOTP's 8-byte counter as an unsigned 64-bit 45 | // integer. 46 | func (otp HOTP) Counter() uint64 { 47 | counter := binary.BigEndian.Uint64(otp.counter[:]) 48 | return counter 49 | } 50 | 51 | // Increment will increment an HOTP source's counter. This is useful 52 | // for providers like the Google Authenticator app, which immediately 53 | // increments the counter and uses the 0 counter value as an integrity 54 | // check. 55 | func (otp HOTP) Increment() { 56 | for i := ctrSize - 1; i >= 0; i-- { 57 | if otp.counter[i]++; otp.counter[i] != 0 { 58 | return 59 | } 60 | } 61 | } 62 | 63 | // OTP generates a new one-time password. 64 | func (otp HOTP) OTP() string { 65 | h := hmac.New(sha1.New, otp.Key) 66 | h.Write(otp.counter[:]) 67 | otp.Increment() 68 | hash := h.Sum(nil) 69 | result := truncate(hash) 70 | 71 | mod := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(otp.Digits)), nil) 72 | mod = mod.Mod(big.NewInt(result), mod) 73 | fmtStr := fmt.Sprintf("%%0%dd", otp.Digits) 74 | return fmt.Sprintf(fmtStr, mod.Uint64()) 75 | } 76 | 77 | func (otp *HOTP) setCounter(counter uint64) bool { 78 | if otp.counter == nil { 79 | otp.counter = new([ctrSize]byte) 80 | } 81 | binary.BigEndian.PutUint64(otp.counter[:], counter) 82 | return true 83 | } 84 | 85 | // NewHOTP intialises a new HOTP instance with the key and counter 86 | // values. No check is done on the digits, but typical values are 6 87 | // and 8. 88 | func NewHOTP(key []byte, counter uint64, digits int) *HOTP { 89 | otp := &HOTP{ 90 | Key: key, 91 | Digits: digits, 92 | } 93 | otp.counter = new([ctrSize]byte) 94 | binary.BigEndian.PutUint64(otp.counter[:], counter) 95 | 96 | return otp 97 | } 98 | 99 | // URL returns a suitable URL, such as for the Google Authenticator 100 | // app. The label is used by these apps to identify the service to 101 | // which this OTP belongs. The digits value is ignored by the Google 102 | // authenticator app, and is therefore elided in the resulting URL. 103 | func (otp *HOTP) URL(label string) string { 104 | secret := base32.StdEncoding.EncodeToString(otp.Key) 105 | u := url.URL{} 106 | v := url.Values{} 107 | u.Scheme = "otpauth" 108 | u.Host = "hotp" 109 | u.Path = label 110 | v.Add("secret", secret) 111 | v.Add("counter", fmt.Sprintf("%d", otp.Counter())) 112 | u.RawQuery = v.Encode() 113 | return u.String() 114 | } 115 | 116 | // QR generates a byte slice containing the a QR code encoded as a 117 | // PNG with level Q error correction. 118 | func (otp *HOTP) QR(label string) ([]byte, error) { 119 | u := otp.URL(label) 120 | code, err := qr.Encode(u, qr.Q) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return code.PNG(), nil 125 | } 126 | 127 | // truncate contains the DT function from the RFC; this is used to 128 | // deterministically select a sequence of 4 bytes from the HMAC 129 | // counter hash. 130 | func truncate(in []byte) int64 { 131 | offset := int(in[len(in)-1] & 0xF) 132 | p := in[offset : offset+4] 133 | var binCode int32 134 | binCode = int32((p[0] & 0x7f)) << 24 135 | binCode += int32((p[1] & 0xff)) << 16 136 | binCode += int32((p[2] & 0xff)) << 8 137 | binCode += int32((p[3] & 0xff)) 138 | return int64(binCode) & 0x7FFFFFFF 139 | } 140 | 141 | // FromURL parses a new HOTP from a URL string. It returns the OTP, 142 | // the label associated with the OTP, and any errors that occurred. 143 | func FromURL(urlString string) (*HOTP, string, error) { 144 | u, err := url.Parse(urlString) 145 | if err != nil { 146 | return nil, "", err 147 | } 148 | 149 | if u.Scheme != "otpauth" { 150 | return nil, "", ErrInvalidHOTPURL 151 | } else if u.Host != "hotp" { 152 | return nil, "", ErrInvalidHOTPURL 153 | } 154 | 155 | v := u.Query() 156 | if len(v) == 0 { 157 | return nil, "", ErrInvalidHOTPURL 158 | } 159 | if v.Get("secret") == "" { 160 | return nil, "", ErrInvalidHOTPURL 161 | } else if algo := v.Get("algorithm"); algo != "" && algo != "SHA1" { 162 | return nil, "", ErrInvalidHOTPURL 163 | } 164 | 165 | var identity string 166 | if len(u.Path) > 1 { 167 | identity = u.Path[1:] 168 | } 169 | 170 | var counter uint64 171 | if ctr := v.Get("counter"); ctr != "" { 172 | counter, err = strconv.ParseUint(ctr, 10, 64) 173 | if err != nil { 174 | return nil, "", ErrInvalidHOTPURL 175 | } 176 | } 177 | 178 | secret, err := base32.StdEncoding.DecodeString(v.Get("secret")) 179 | if err != nil { 180 | return nil, "", ErrInvalidHOTPURL 181 | } 182 | 183 | var digits int64 = 6 184 | if v.Get("digits") != "" { 185 | digits, err = strconv.ParseInt(v.Get("digits"), 10, 8) 186 | if err != nil { 187 | return nil, "", ErrInvalidHOTPURL 188 | } 189 | } 190 | 191 | otp := NewHOTP(secret, counter, int(digits)) 192 | return otp, identity, nil 193 | } 194 | 195 | // GenerateHOTP will generate a randomised HOTP source; if the 196 | // randCounter parameter is true, the counter will be randomised. 197 | func GenerateHOTP(digits int, randCounter bool) (*HOTP, error) { 198 | key := make([]byte, sha1.Size) 199 | _, err := io.ReadFull(PRNG, key) 200 | if err != nil { 201 | return nil, err 202 | } 203 | 204 | var counter uint64 205 | if randCounter { 206 | ctr, err := rand.Int(PRNG, big.NewInt(int64(math.MaxInt64))) 207 | if err != nil { 208 | return nil, err 209 | } 210 | counter = ctr.Uint64() 211 | } 212 | 213 | return NewHOTP(key, counter, digits), nil 214 | } 215 | 216 | // YubiKey reads an OATH-HOTP string as returned by a YubiKey, and 217 | // returns three values. The first value contains the actual OTP, the 218 | // second value contains the YubiKey's token identifier, and the final 219 | // value indicates whether the input string was a valid YubiKey 220 | // OTP. This does not check whether the code is correct or not, it 221 | // only ensures that it is well-formed output from a token and 222 | // splits the output into the code and the public identity. 223 | func (otp *HOTP) YubiKey(in string) (string, string, bool) { 224 | if len(in) < otp.Digits { 225 | return "", "", false 226 | } 227 | 228 | otpStart := len(in) - otp.Digits 229 | code := in[otpStart:] 230 | pubid := in[:otpStart] 231 | return code, pubid, true 232 | } 233 | 234 | // IntegrityCheck returns two values, the base OTP and the current 235 | // counter. This is used, for example, with the Google Authenticator 236 | // app's "Check key value" function and can be used to verify that 237 | // the application and the provider are in sync. 238 | func (otp *HOTP) IntegrityCheck() (string, uint64) { 239 | h := hmac.New(sha1.New, otp.Key) 240 | counter := make([]byte, 8) 241 | h.Write(counter) 242 | hash := h.Sum(nil) 243 | result := truncate(hash) 244 | 245 | mod := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(otp.Digits)), nil) 246 | mod = mod.Mod(big.NewInt(result), mod) 247 | fmtStr := fmt.Sprintf("%%0%dd", otp.Digits) 248 | return fmt.Sprintf(fmtStr, mod.Uint64()), otp.Counter() 249 | } 250 | 251 | // Scan takes a code input (i.e. from the user), and scans ahead 252 | // within a certain window of counter values. This can be used in the 253 | // case where the server's counter and the user's counter have fallen 254 | // out of sync. 255 | func (otp *HOTP) Scan(code string, window int) bool { 256 | var valid bool 257 | codeBytes := []byte(code) 258 | counter := otp.Counter() 259 | 260 | for i := 0; i < window; i++ { 261 | genCode := []byte(otp.OTP()) 262 | if subtle.ConstantTimeCompare(codeBytes, genCode) == 1 { 263 | valid = true 264 | break 265 | } 266 | } 267 | if !valid { 268 | otp.setCounter(counter) 269 | } 270 | return valid 271 | } 272 | 273 | // Check takes an input code and verifies it against the OTP. If 274 | // successful, the counter is incremented. 275 | func (otp *HOTP) Check(code string) bool { 276 | codeBytes := []byte(code) 277 | genCode := []byte(otp.OTP()) 278 | if subtle.ConstantTimeCompare(codeBytes, genCode) != 1 { 279 | otp.setCounter(otp.Counter() - 1) 280 | return false 281 | } 282 | return true 283 | } 284 | 285 | // Marshal serialises an HOTP key value as a DER-encoded byte slice. 286 | func Marshal(otp *HOTP) ([]byte, error) { 287 | var asnHOTP struct { 288 | Key []byte 289 | Counter *big.Int 290 | Digits int 291 | } 292 | asnHOTP.Key = otp.Key[:] 293 | asnHOTP.Counter = new(big.Int).SetUint64(otp.Counter()) 294 | asnHOTP.Digits = otp.Digits 295 | return asn1.Marshal(asnHOTP) 296 | } 297 | 298 | // Unmarshal parses a DER-encoded serialised HOTP key value. 299 | func Unmarshal(in []byte) (otp *HOTP, err error) { 300 | var asnHOTP struct { 301 | Key []byte 302 | Counter *big.Int 303 | Digits int 304 | } 305 | _, err = asn1.Unmarshal(in, &asnHOTP) 306 | if err != nil { 307 | return 308 | } 309 | 310 | otp = &HOTP{ 311 | Key: asnHOTP.Key[:], 312 | Digits: asnHOTP.Digits, 313 | } 314 | otp.setCounter(asnHOTP.Counter.Uint64()) 315 | return 316 | } 317 | -------------------------------------------------------------------------------- /hotp_test.go: -------------------------------------------------------------------------------- 1 | package hotp 2 | 3 | import "fmt" 4 | import "testing" 5 | import "bytes" 6 | import "io" 7 | 8 | var testKey = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} 9 | 10 | func newZeroHOTP() *HOTP { 11 | return NewHOTP(testKey, 0, 6) 12 | } 13 | 14 | // This test verifies that the counter increment works as expected. As per the RFC, 15 | // the counter should be treated as an 8-byte big-endian unsigned integer. 16 | func TestIncrement(t *testing.T) { 17 | otp := newZeroHOTP() 18 | for i := 0; i < 32; i++ { 19 | if otp.Counter() != uint64(i) { 20 | fmt.Printf("hotp: counter should be %d, is %d\n", 21 | i, otp.Counter()) 22 | fmt.Printf("\tcounter state: %x\n", *otp.counter) 23 | t.FailNow() 24 | } 25 | otp.Increment() 26 | } 27 | } 28 | 29 | var sha1Hmac = []byte{ 30 | 0x1f, 0x86, 0x98, 0x69, 0x0e, 31 | 0x02, 0xca, 0x16, 0x61, 0x85, 32 | 0x50, 0xef, 0x7f, 0x19, 0xda, 33 | 0x8e, 0x94, 0x5b, 0x55, 0x5a, 34 | } 35 | 36 | var truncExpect int64 = 0x50ef7f19 37 | 38 | // This test runs through the truncation example given in the RFC. 39 | func TestTruncate(t *testing.T) { 40 | if result := truncate(sha1Hmac); result != truncExpect { 41 | fmt.Printf("hotp: expected truncate -> %d, saw %d\n", 42 | truncExpect, result) 43 | t.FailNow() 44 | } 45 | 46 | sha1Hmac[19]++ 47 | if result := truncate(sha1Hmac); result == truncExpect { 48 | fmt.Println("hotp: expected truncation to fail") 49 | t.FailNow() 50 | } 51 | } 52 | 53 | var rfcKey = []byte("12345678901234567890") 54 | var rfcExpected = []string{ 55 | "755224", 56 | "287082", 57 | "359152", 58 | "969429", 59 | "338314", 60 | "254676", 61 | "287922", 62 | "162583", 63 | "399871", 64 | "520489", 65 | } 66 | 67 | // This test runs through the test cases presented in the RFC, and 68 | // ensures that this implementation is in compliance. 69 | func TestRFC(t *testing.T) { 70 | otp := NewHOTP(rfcKey, 0, 6) 71 | for i := 0; i < len(rfcExpected); i++ { 72 | code := otp.OTP() 73 | if code == "" { 74 | fmt.Printf("hotp: failed to produce an OTP\n") 75 | t.FailNow() 76 | } else if code != rfcExpected[i] { 77 | fmt.Printf("hotp: invalid OTP\n") 78 | fmt.Printf("\tExpected: %s\n", rfcExpected[i]) 79 | fmt.Printf("\t Actual: %s\n", code) 80 | t.FailNow() 81 | } 82 | } 83 | } 84 | 85 | // This test uses a different key than the test cases in the RFC, 86 | // but runs through the same test cases to ensure that they fail as 87 | // expected. 88 | func TestBadRFC(t *testing.T) { 89 | otp := NewHOTP(testKey, 0, 6) 90 | for i := 0; i < len(rfcExpected); i++ { 91 | code := otp.OTP() 92 | if code == "" { 93 | fmt.Printf("hotp: failed to produce an OTP\n") 94 | t.FailNow() 95 | } else if code == rfcExpected[i] { 96 | fmt.Printf("hotp: should not have received a valid OTP\n") 97 | t.FailNow() 98 | } 99 | } 100 | } 101 | 102 | // This test takes input from a test YubiKey and ensures that the 103 | // YubiKey functionality works as expected. 104 | func TestYubiKey(t *testing.T) { 105 | ykKey := []byte{ 106 | 0xd4, 0xbe, 0x97, 0xac, 0xe3, 107 | 0x31, 0x72, 0x95, 0xd8, 0x95, 108 | 0xeb, 0xd6, 0xb2, 0xec, 0xa6, 109 | 0x78, 0x49, 0x79, 0x4d, 0xb3, 110 | } 111 | otp := NewHOTP(ykKey, 0, 6) 112 | out := []string{ 113 | "cccc52345777705179", 114 | "cccc52345777404068", 115 | "cccc52345777490143", 116 | "cccc52345777739740", 117 | "cccc52345777043269", 118 | "cccc52345777035666", 119 | "cccc52345777725326", 120 | } 121 | 122 | codes := []string{ 123 | "705179", 124 | "404068", 125 | "490143", 126 | "739740", 127 | "043269", 128 | "035666", 129 | "725326", 130 | } 131 | 132 | ykpub := "cccc52345777" 133 | 134 | if _, _, ok := otp.YubiKey("abcd"); ok { 135 | fmt.Println("hotp: accepted invalid YubiKey input") 136 | t.FailNow() 137 | } 138 | 139 | for i := 0; i < len(out); i++ { 140 | code := otp.OTP() 141 | if ykCode, pubid, ok := otp.YubiKey(out[i]); !ok { 142 | fmt.Printf("hotp: invalid YubiKey OTP\n") 143 | t.FailNow() 144 | } else if ykCode != code && code != codes[i] { 145 | fmt.Printf("hotp: YubiKey did not produce valid OTP\n") 146 | t.FailNow() 147 | } else if ykpub != pubid { 148 | fmt.Printf("hotp: invalid YubiKey public ID\n") 149 | t.FailNow() 150 | } 151 | } 152 | 153 | code, counter := otp.IntegrityCheck() 154 | if code != codes[0] { 155 | fmt.Println("hotp: YubiKey integrity check fails (bad code)") 156 | t.FailNow() 157 | } else if counter != uint64(len(out)) { 158 | fmt.Println("hotp: YubiKey integrity check fails (bad counter)") 159 | t.FailNow() 160 | } 161 | } 162 | 163 | // This test generates a new HOTP, outputs the URL for that HOTP, 164 | // and attempts to parse that URL. It verifies that the two HOTPs 165 | // are the same, and that they produce the same output. 166 | func TestURL(t *testing.T) { 167 | var ident = "testuser@foo" 168 | otp := NewHOTP(testKey, 0, 6) 169 | url := otp.URL("testuser@foo") 170 | otp2, id, err := FromURL(url) 171 | if err != nil { 172 | fmt.Printf("hotp: failed to parse HOTP URL\n") 173 | t.FailNow() 174 | } else if id != ident { 175 | fmt.Printf("hotp: bad label\n") 176 | fmt.Printf("\texpected: %s\n", ident) 177 | fmt.Printf("\t actual: %s\n", id) 178 | t.FailNow() 179 | } else if otp2.Counter() != otp.Counter() { 180 | fmt.Printf("hotp: OTP counters aren't synced\n") 181 | fmt.Printf("\toriginal: %d\n", otp.Counter()) 182 | fmt.Printf("\t second: %d\n", otp2.Counter()) 183 | t.FailNow() 184 | } 185 | 186 | code1 := otp.OTP() 187 | code2 := otp2.OTP() 188 | if code1 != code2 { 189 | fmt.Printf("hotp: mismatched OTPs\n") 190 | fmt.Printf("\texpected: %s\n", code1) 191 | fmt.Printf("\t actual: %s\n", code2) 192 | } 193 | 194 | // There's not much we can do test the QR code, except to 195 | // ensure it doesn't fail. 196 | _, err = otp.QR(ident) 197 | if err != nil { 198 | fmt.Printf("hotp: failed to generate QR code PNG (%v)\n", err) 199 | t.FailNow() 200 | } 201 | 202 | // This should fail because the maximum size of an alphanumeric 203 | // QR code with the lowest-level of error correction should 204 | // max out at 4296 bytes. 8k may be a bit overkill... but it 205 | // gets the job done. The value is read from the PRNG to 206 | // increase the likelihood that the returned data is 207 | // uncompressible. 208 | var tooBigIdent = make([]byte, 8192) 209 | _, err = io.ReadFull(PRNG, tooBigIdent) 210 | if err != nil { 211 | fmt.Printf("hotp: failed to read identity (%v)\n", err) 212 | t.FailNow() 213 | } else if _, err = otp.QR(string(tooBigIdent)); err == nil { 214 | fmt.Println("hotp: QR code should fail to encode oversized URL") 215 | t.FailNow() 216 | } 217 | } 218 | 219 | // This test attempts a variety of invalid urls against the parser 220 | // to ensure they fail. 221 | func TestBadURL(t *testing.T) { 222 | var urlList = []string{ 223 | "http://google.com", 224 | "", 225 | "-", 226 | "foo", 227 | "otpauth:/foo/bar/baz", 228 | "://", 229 | "otpauth://totp/foo@bar?secret=ABCD", 230 | "otpauth://hotp/secret=bar", 231 | "otpauth://hotp/?secret=QUJDRA&algorithm=SHA256", 232 | "otpauth://hotp/?digits=", 233 | "otpauth://hotp/?secret=123", 234 | "otpauth://hotp/?secret=MFRGGZDF&digits=ABCD", 235 | "otpauth://hotp/?secret=MFRGGZDF&counter=ABCD", 236 | } 237 | 238 | for i := range urlList { 239 | if _, _, err := FromURL(urlList[i]); err == nil { 240 | fmt.Println("hotp: URL should not have parsed successfully") 241 | fmt.Printf("\turl was: %s\n", urlList[i]) 242 | t.FailNow() 243 | } 244 | } 245 | } 246 | 247 | // This test uses a url generated with the `hotpgen` tool; this url 248 | // was imported into the Google Authenticator app and the resulting 249 | // codes generated by the app are checked here to verify interoperability. 250 | func TestGAuth(t *testing.T) { 251 | url := "otpauth://hotp/kyle?counter=0&secret=EXZLUP7IGHQ673ZCP32RTLRU2N427Z6L" 252 | expected := []string{ 253 | "023667", 254 | "641344", 255 | "419615", 256 | "692589", 257 | "237233", 258 | "711695", 259 | "620195", 260 | } 261 | 262 | otp, label, err := FromURL(url) 263 | if err != nil { 264 | fmt.Printf("hotp: failed to parse HOTP URL\n") 265 | t.FailNow() 266 | } else if label != "kyle" { 267 | fmt.Printf("hotp: invalid label") 268 | t.FailNow() 269 | } 270 | otp.Increment() 271 | 272 | // Validate codes 273 | for i := 1; i < len(expected); i++ { 274 | code := otp.OTP() 275 | if otp.Counter() != uint64(i+1) { 276 | fmt.Printf("hotp: invalid OTP counter (should be %d but is %d)", 277 | i, otp.Counter()) 278 | t.FailNow() 279 | } else if code != expected[i] { 280 | fmt.Println("hotp: invalid OTP") 281 | t.FailNow() 282 | } 283 | } 284 | 285 | // Validate integrity check 286 | code, counter := otp.IntegrityCheck() 287 | if code != expected[0] { 288 | fmt.Println("hotp: invalid integrity code") 289 | t.FailNow() 290 | } else if counter != uint64(len(expected)) { 291 | fmt.Println("hotp: invalid integrity counter") 292 | t.FailNow() 293 | } 294 | } 295 | 296 | // This test verifies that a scan will successfully sync and update 297 | // the OTP counter. 298 | func TestScan(t *testing.T) { 299 | url := "otpauth://hotp/kyle?counter=0&secret=EXZLUP7IGHQ673ZCP32RTLRU2N427Z6L" 300 | expected := []string{ 301 | "023667", 302 | "641344", 303 | "419615", 304 | "692589", 305 | "237233", 306 | "711695", 307 | "620195", 308 | } 309 | 310 | otp, _, err := FromURL(url) 311 | if err != nil { 312 | fmt.Printf("hotp: failed to parse HOTP URL\n") 313 | t.FailNow() 314 | } 315 | 316 | if !otp.Scan(expected[4], 10) { 317 | fmt.Println("hotp: scan should have found code") 318 | t.FailNow() 319 | } 320 | 321 | if otp.Counter() != 5 { 322 | fmt.Println("hotp: counter was not properly synced") 323 | t.FailNow() 324 | } 325 | } 326 | 327 | // This test verifies that a scan that does not successfully sync 328 | // will not update the OTP counter. 329 | func TestBadScan(t *testing.T) { 330 | url := "otpauth://hotp/kyle?counter=0&secret=EXZLUP7IGHQ673ZCP32RTLRU2N427Z6L" 331 | expected := []string{ 332 | "023667", 333 | "641344", 334 | "419615", 335 | "692589", 336 | "237233", 337 | "711695", 338 | "620195", 339 | } 340 | 341 | otp, _, err := FromURL(url) 342 | if err != nil { 343 | fmt.Printf("hotp: failed to parse HOTP URL\n") 344 | t.FailNow() 345 | } 346 | 347 | if otp.Scan(expected[6], 3) { 348 | fmt.Println("hotp: scan should not have found code") 349 | t.FailNow() 350 | } 351 | 352 | if otp.Counter() != 0 { 353 | fmt.Println("hotp: counter was not properly synced") 354 | t.FailNow() 355 | } 356 | } 357 | 358 | // This test ensures that a valid code is recognised and increments 359 | // the counter. 360 | func TestCheck(t *testing.T) { 361 | otp := NewHOTP(rfcKey, 0, 6) 362 | if !otp.Check(rfcExpected[0]) { 363 | fmt.Println("hotp: Check failed when it should have succeeded") 364 | t.FailNow() 365 | } 366 | 367 | if otp.Counter() != 1 { 368 | fmt.Println("hotp: counter should have been incremented") 369 | t.FailNow() 370 | } 371 | } 372 | 373 | // This test ensures that an invalid code is recognised as such and 374 | // does not increment the counter. 375 | func TestBadCheck(t *testing.T) { 376 | otp := NewHOTP(rfcKey, 0, 6) 377 | if otp.Check(rfcExpected[1]) { 378 | fmt.Println("hotp: Check succeeded when it should have failed") 379 | t.FailNow() 380 | } 381 | 382 | if otp.Counter() != 0 { 383 | fmt.Println("hotp: counter should not have been incremented") 384 | t.FailNow() 385 | } 386 | } 387 | 388 | func TestSerialisation(t *testing.T) { 389 | otp := NewHOTP(rfcKey, 123456, 8) 390 | out, err := Marshal(otp) 391 | if err != nil { 392 | fmt.Printf("hotp: failed to marshal HOTP (%v)\n", err) 393 | t.FailNow() 394 | } 395 | 396 | otp2, err := Unmarshal(out) 397 | if err != nil { 398 | fmt.Printf("hotp: failed to unmarshal HOTP (%v)\n", err) 399 | t.FailNow() 400 | } 401 | 402 | if otp.Counter() != otp2.Counter() { 403 | fmt.Println("hotp: serialisation failed to preserve counter") 404 | t.FailNow() 405 | } 406 | 407 | if _, err = Unmarshal([]byte{0x0}); err == nil { 408 | fmt.Println("hotp: Unmarshal should have failed") 409 | t.FailNow() 410 | } 411 | } 412 | 413 | func TestGenerateHOTP(t *testing.T) { 414 | _, err := GenerateHOTP(6, false) 415 | if err != nil { 416 | fmt.Printf("hotp: failed to generate random key value (%v)\n", err) 417 | t.FailNow() 418 | } 419 | 420 | _, err = GenerateHOTP(8, true) 421 | if err != nil { 422 | fmt.Printf("hotp: failed to generate random key value (%v)\n", err) 423 | t.FailNow() 424 | } 425 | 426 | PRNG = new(bytes.Buffer) 427 | _, err = GenerateHOTP(8, true) 428 | if err == nil { 429 | fmt.Println("hotp: should have failed to generate random key value") 430 | t.FailNow() 431 | } 432 | 433 | // should read just enough for the key 434 | PRNG = bytes.NewBufferString("abcdeabcdeabcdeabcde") 435 | _, err = GenerateHOTP(8, true) 436 | if err == nil { 437 | fmt.Println("hotp: should have failed to generate random key value") 438 | t.FailNow() 439 | } 440 | 441 | } 442 | 443 | func BenchmarkFromURL(b *testing.B) { 444 | url := "otpauth://hotp/kyle?counter=0&secret=EXZLUP7IGHQ673ZCP32RTLRU2N427Z6L" 445 | for i := 0; i < b.N; i++ { 446 | _, _, err := FromURL(url) 447 | if err != nil { 448 | fmt.Printf("hotp: failed to parse url (%v)\n", err) 449 | b.FailNow() 450 | } 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /rfc4226.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Network Working Group D. M'Raihi 8 | Request for Comments: 4226 VeriSign 9 | Category: Informational M. Bellare 10 | UCSD 11 | F. Hoornaert 12 | Vasco 13 | D. Naccache 14 | Gemplus 15 | O. Ranen 16 | Aladdin 17 | December 2005 18 | 19 | 20 | HOTP: An HMAC-Based One-Time Password Algorithm 21 | 22 | Status of This Memo 23 | 24 | This memo provides information for the Internet community. It does 25 | not specify an Internet standard of any kind. Distribution of this 26 | memo is unlimited. 27 | 28 | Copyright Notice 29 | 30 | Copyright (C) The Internet Society (2005). 31 | 32 | Abstract 33 | 34 | This document describes an algorithm to generate one-time password 35 | values, based on Hashed Message Authentication Code (HMAC). A 36 | security analysis of the algorithm is presented, and important 37 | parameters related to the secure deployment of the algorithm are 38 | discussed. The proposed algorithm can be used across a wide range of 39 | network applications ranging from remote Virtual Private Network 40 | (VPN) access, Wi-Fi network logon to transaction-oriented Web 41 | applications. 42 | 43 | This work is a joint effort by the OATH (Open AuTHentication) 44 | membership to specify an algorithm that can be freely distributed to 45 | the technical community. The authors believe that a common and 46 | shared algorithm will facilitate adoption of two-factor 47 | authentication on the Internet by enabling interoperability across 48 | commercial and open-source implementations. 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | M'Raihi, et al. Informational [Page 1] 59 | 60 | RFC 4226 HOTP Algorithm December 2005 61 | 62 | 63 | Table of Contents 64 | 65 | 1. Overview ........................................................3 66 | 2. Introduction ....................................................3 67 | 3. Requirements Terminology ........................................4 68 | 4. Algorithm Requirements ..........................................4 69 | 5. HOTP Algorithm ..................................................5 70 | 5.1. Notation and Symbols .......................................5 71 | 5.2. Description ................................................6 72 | 5.3. Generating an HOTP Value ...................................6 73 | 5.4. Example of HOTP Computation for Digit = 6 ..................7 74 | 6. Security Considerations .........................................8 75 | 7. Security Requirements ...........................................9 76 | 7.1. Authentication Protocol Requirements .......................9 77 | 7.2. Validation of HOTP Values .................................10 78 | 7.3. Throttling at the Server ..................................10 79 | 7.4. Resynchronization of the Counter ..........................11 80 | 7.5. Management of Shared Secrets ..............................11 81 | 8. Composite Shared Secrets .......................................14 82 | 9. Bi-Directional Authentication ..................................14 83 | 10. Conclusion ....................................................15 84 | 11. Acknowledgements ..............................................15 85 | 12. Contributors ..................................................15 86 | 13. References ....................................................15 87 | 13.1. Normative References .....................................15 88 | 13.2. Informative References ...................................16 89 | Appendix A - HOTP Algorithm Security: Detailed Analysis ...........17 90 | A.1. Definitions and Notations .................................17 91 | A.2. The Idealized Algorithm: HOTP-IDEAL .......................17 92 | A.3. Model of Security .........................................18 93 | A.4. Security of the Ideal Authentication Algorithm ............19 94 | A.4.1. From Bits to Digits ................................19 95 | A.4.2. Brute Force Attacks ................................21 96 | A.4.3. Brute force attacks are the best possible attacks ..22 97 | A.5. Security Analysis of HOTP .................................23 98 | Appendix B - SHA-1 Attacks ........................................25 99 | B.1. SHA-1 Status ..............................................25 100 | B.2. HMAC-SHA-1 Status .........................................26 101 | B.3. HOTP Status ...............................................26 102 | Appendix C - HOTP Algorithm: Reference Implementation .............27 103 | Appendix D - HOTP Algorithm: Test Values ..........................32 104 | Appendix E - Extensions ...........................................33 105 | E.1. Number of Digits ..........................................33 106 | E.2. Alphanumeric Values .......................................33 107 | E.3. Sequence of HOTP values ...................................34 108 | E.4. A Counter-Based Resynchronization Method ..................34 109 | E.5. Data Field ................................................35 110 | 111 | 112 | 113 | 114 | M'Raihi, et al. Informational [Page 2] 115 | 116 | RFC 4226 HOTP Algorithm December 2005 117 | 118 | 119 | 1. Overview 120 | 121 | The document introduces first the context around an algorithm that 122 | generates one-time password values based on HMAC [BCK1] and, thus, is 123 | named the HMAC-Based One-Time Password (HOTP) algorithm. In Section 124 | 4, the algorithm requirements are listed and in Section 5, the HOTP 125 | algorithm is described. Sections 6 and 7 focus on the algorithm 126 | security. Section 8 proposes some extensions and improvements, and 127 | Section 10 concludes this document. In Appendix A, the interested 128 | reader will find a detailed, full-fledged analysis of the algorithm 129 | security: an idealized version of the algorithm is evaluated, and 130 | then the HOTP algorithm security is analyzed. 131 | 132 | 2. Introduction 133 | 134 | Today, deployment of two-factor authentication remains extremely 135 | limited in scope and scale. Despite increasingly higher levels of 136 | threats and attacks, most Internet applications still rely on weak 137 | authentication schemes for policing user access. The lack of 138 | interoperability among hardware and software technology vendors has 139 | been a limiting factor in the adoption of two-factor authentication 140 | technology. In particular, the absence of open specifications has 141 | led to solutions where hardware and software components are tightly 142 | coupled through proprietary technology, resulting in high-cost 143 | solutions, poor adoption, and limited innovation. 144 | 145 | In the last two years, the rapid rise of network threats has exposed 146 | the inadequacies of static passwords as the primary mean of 147 | authentication on the Internet. At the same time, the current 148 | approach that requires an end user to carry an expensive, single- 149 | function device that is only used to authenticate to the network is 150 | clearly not the right answer. For two-factor authentication to 151 | propagate on the Internet, it will have to be embedded in more 152 | flexible devices that can work across a wide range of applications. 153 | 154 | The ability to embed this base technology while ensuring broad 155 | interoperability requires that it be made freely available to the 156 | broad technical community of hardware and software developers. Only 157 | an open-system approach will ensure that basic two-factor 158 | authentication primitives can be built into the next generation of 159 | consumer devices such as USB mass storage devices, IP phones, and 160 | personal digital assistants. 161 | 162 | One-Time Password is certainly one of the simplest and most popular 163 | forms of two-factor authentication for securing network access. For 164 | example, in large enterprises, Virtual Private Network access often 165 | requires the use of One-Time Password tokens for remote user 166 | authentication. One-Time Passwords are often preferred to stronger 167 | 168 | 169 | 170 | M'Raihi, et al. Informational [Page 3] 171 | 172 | RFC 4226 HOTP Algorithm December 2005 173 | 174 | 175 | forms of authentication such as Public-Key Infrastructure (PKI) or 176 | biometrics because an air-gap device does not require the 177 | installation of any client desktop software on the user machine, 178 | therefore allowing them to roam across multiple machines including 179 | home computers, kiosks, and personal digital assistants. 180 | 181 | This document proposes a simple One-Time Password algorithm that can 182 | be implemented by any hardware manufacturer or software developer to 183 | create interoperable authentication devices and software agents. The 184 | algorithm is event-based so that it can be embedded in high-volume 185 | devices such as Java smart cards, USB dongles, and GSM SIM cards. 186 | The presented algorithm is made freely available to the developer 187 | community under the terms and conditions of the IETF Intellectual 188 | Property Rights [RFC3979]. 189 | 190 | The authors of this document are members of the Open AuTHentication 191 | initiative [OATH]. The initiative was created in 2004 to facilitate 192 | collaboration among strong authentication technology providers. 193 | 194 | 3. Requirements Terminology 195 | 196 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 197 | "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 198 | document are to be interpreted as described in [RFC2119]. 199 | 200 | 4. Algorithm Requirements 201 | 202 | This section presents the main requirements that drove this algorithm 203 | design. A lot of emphasis was placed on end-consumer usability as 204 | well as the ability for the algorithm to be implemented by low-cost 205 | hardware that may provide minimal user interface capabilities. In 206 | particular, the ability to embed the algorithm into high-volume SIM 207 | and Java cards was a fundamental prerequisite. 208 | 209 | R1 - The algorithm MUST be sequence- or counter-based: one of the 210 | goals is to have the HOTP algorithm embedded in high-volume devices 211 | such as Java smart cards, USB dongles, and GSM SIM cards. 212 | 213 | R2 - The algorithm SHOULD be economical to implement in hardware by 214 | minimizing requirements on battery, number of buttons, computational 215 | horsepower, and size of LCD display. 216 | 217 | R3 - The algorithm MUST work with tokens that do not support any 218 | numeric input, but MAY also be used with more sophisticated devices 219 | such as secure PIN-pads. 220 | 221 | R4 - The value displayed on the token MUST be easily read and entered 222 | by the user: This requires the HOTP value to be of reasonable length. 223 | 224 | 225 | 226 | M'Raihi, et al. Informational [Page 4] 227 | 228 | RFC 4226 HOTP Algorithm December 2005 229 | 230 | 231 | The HOTP value must be at least a 6-digit value. It is also 232 | desirable that the HOTP value be 'numeric only' so that it can be 233 | easily entered on restricted devices such as phones. 234 | 235 | R5 - There MUST be user-friendly mechanisms available to 236 | resynchronize the counter. Section 7.4 and Appendix E.4 details the 237 | resynchronization mechanism proposed in this document 238 | 239 | R6 - The algorithm MUST use a strong shared secret. The length of 240 | the shared secret MUST be at least 128 bits. This document 241 | RECOMMENDs a shared secret length of 160 bits. 242 | 243 | 5. HOTP Algorithm 244 | 245 | In this section, we introduce the notation and describe the HOTP 246 | algorithm basic blocks -- the base function to compute an HMAC-SHA-1 247 | value and the truncation method to extract an HOTP value. 248 | 249 | 5.1. Notation and Symbols 250 | 251 | A string always means a binary string, meaning a sequence of zeros 252 | and ones. 253 | 254 | If s is a string, then |s| denotes its length. 255 | 256 | If n is a number, then |n| denotes its absolute value. 257 | 258 | If s is a string, then s[i] denotes its i-th bit. We start numbering 259 | the bits at 0, so s = s[0]s[1]...s[n-1] where n = |s| is the length 260 | of s. 261 | 262 | Let StToNum (String to Number) denote the function that as input a 263 | string s returns the number whose binary representation is s. (For 264 | example, StToNum(110) = 6.) 265 | 266 | Here is a list of symbols used in this document. 267 | 268 | Symbol Represents 269 | ------------------------------------------------------------------- 270 | C 8-byte counter value, the moving factor. This counter 271 | MUST be synchronized between the HOTP generator (client) 272 | and the HOTP validator (server). 273 | 274 | K shared secret between client and server; each HOTP 275 | generator has a different and unique secret K. 276 | 277 | T throttling parameter: the server will refuse connections 278 | from a user after T unsuccessful authentication attempts. 279 | 280 | 281 | 282 | M'Raihi, et al. Informational [Page 5] 283 | 284 | RFC 4226 HOTP Algorithm December 2005 285 | 286 | 287 | 288 | s resynchronization parameter: the server will attempt to 289 | verify a received authenticator across s consecutive 290 | counter values. 291 | 292 | Digit number of digits in an HOTP value; system parameter. 293 | 294 | 5.2. Description 295 | 296 | The HOTP algorithm is based on an increasing counter value and a 297 | static symmetric key known only to the token and the validation 298 | service. In order to create the HOTP value, we will use the HMAC- 299 | SHA-1 algorithm, as defined in RFC 2104 [BCK2]. 300 | 301 | As the output of the HMAC-SHA-1 calculation is 160 bits, we must 302 | truncate this value to something that can be easily entered by a 303 | user. 304 | 305 | HOTP(K,C) = Truncate(HMAC-SHA-1(K,C)) 306 | 307 | Where: 308 | 309 | - Truncate represents the function that converts an HMAC-SHA-1 310 | value into an HOTP value as defined in Section 5.3. 311 | 312 | The Key (K), the Counter (C), and Data values are hashed high-order 313 | byte first. 314 | 315 | The HOTP values generated by the HOTP generator are treated as big 316 | endian. 317 | 318 | 5.3. Generating an HOTP Value 319 | 320 | We can describe the operations in 3 distinct steps: 321 | 322 | Step 1: Generate an HMAC-SHA-1 value Let HS = HMAC-SHA-1(K,C) // HS 323 | is a 20-byte string 324 | 325 | Step 2: Generate a 4-byte string (Dynamic Truncation) 326 | Let Sbits = DT(HS) // DT, defined below, 327 | // returns a 31-bit string 328 | 329 | Step 3: Compute an HOTP value 330 | Let Snum = StToNum(Sbits) // Convert S to a number in 331 | 0...2^{31}-1 332 | Return D = Snum mod 10^Digit // D is a number in the range 333 | 0...10^{Digit}-1 334 | 335 | 336 | 337 | 338 | M'Raihi, et al. Informational [Page 6] 339 | 340 | RFC 4226 HOTP Algorithm December 2005 341 | 342 | 343 | The Truncate function performs Step 2 and Step 3, i.e., the dynamic 344 | truncation and then the reduction modulo 10^Digit. The purpose of 345 | the dynamic offset truncation technique is to extract a 4-byte 346 | dynamic binary code from a 160-bit (20-byte) HMAC-SHA-1 result. 347 | 348 | DT(String) // String = String[0]...String[19] 349 | Let OffsetBits be the low-order 4 bits of String[19] 350 | Offset = StToNum(OffsetBits) // 0 <= OffSet <= 15 351 | Let P = String[OffSet]...String[OffSet+3] 352 | Return the Last 31 bits of P 353 | 354 | The reason for masking the most significant bit of P is to avoid 355 | confusion about signed vs. unsigned modulo computations. Different 356 | processors perform these operations differently, and masking out the 357 | signed bit removes all ambiguity. 358 | 359 | Implementations MUST extract a 6-digit code at a minimum and possibly 360 | 7 and 8-digit code. Depending on security requirements, Digit = 7 or 361 | more SHOULD be considered in order to extract a longer HOTP value. 362 | 363 | The following paragraph is an example of using this technique for 364 | Digit = 6, i.e., that a 6-digit HOTP value is calculated from the 365 | HMAC value. 366 | 367 | 5.4. Example of HOTP Computation for Digit = 6 368 | 369 | The following code example describes the extraction of a dynamic 370 | binary code given that hmac_result is a byte array with the HMAC- 371 | SHA-1 result: 372 | 373 | int offset = hmac_result[19] & 0xf ; 374 | int bin_code = (hmac_result[offset] & 0x7f) << 24 375 | | (hmac_result[offset+1] & 0xff) << 16 376 | | (hmac_result[offset+2] & 0xff) << 8 377 | | (hmac_result[offset+3] & 0xff) ; 378 | 379 | SHA-1 HMAC Bytes (Example) 380 | 381 | ------------------------------------------------------------- 382 | | Byte Number | 383 | ------------------------------------------------------------- 384 | |00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19| 385 | ------------------------------------------------------------- 386 | | Byte Value | 387 | ------------------------------------------------------------- 388 | |1f|86|98|69|0e|02|ca|16|61|85|50|ef|7f|19|da|8e|94|5b|55|5a| 389 | -------------------------------***********----------------++| 390 | 391 | 392 | 393 | 394 | M'Raihi, et al. Informational [Page 7] 395 | 396 | RFC 4226 HOTP Algorithm December 2005 397 | 398 | 399 | * The last byte (byte 19) has the hex value 0x5a. 400 | * The value of the lower 4 bits is 0xa (the offset value). 401 | * The offset value is byte 10 (0xa). 402 | * The value of the 4 bytes starting at byte 10 is 0x50ef7f19, 403 | which is the dynamic binary code DBC1. 404 | * The MSB of DBC1 is 0x50 so DBC2 = DBC1 = 0x50ef7f19 . 405 | * HOTP = DBC2 modulo 10^6 = 872921. 406 | 407 | We treat the dynamic binary code as a 31-bit, unsigned, big-endian 408 | integer; the first byte is masked with a 0x7f. 409 | 410 | We then take this number modulo 1,000,000 (10^6) to generate the 6- 411 | digit HOTP value 872921 decimal. 412 | 413 | 6. Security Considerations 414 | 415 | The conclusion of the security analysis detailed in the Appendix is 416 | that, for all practical purposes, the outputs of the Dynamic 417 | Truncation (DT) on distinct counter inputs are uniformly and 418 | independently distributed 31-bit strings. 419 | 420 | The security analysis then details the impact of the conversion from 421 | a string to an integer and the final reduction modulo 10^Digit, where 422 | Digit is the number of digits in an HOTP value. 423 | 424 | The analysis demonstrates that these final steps introduce a 425 | negligible bias, which does not impact the security of the HOTP 426 | algorithm, in the sense that the best possible attack against the 427 | HOTP function is the brute force attack. 428 | 429 | Assuming an adversary is able to observe numerous protocol exchanges 430 | and collect sequences of successful authentication values. This 431 | adversary, trying to build a function F to generate HOTP values based 432 | on his observations, will not have a significant advantage over a 433 | random guess. 434 | 435 | The logical conclusion is simply that the best strategy will once 436 | again be to perform a brute force attack to enumerate and try all the 437 | possible values. 438 | 439 | Considering the security analysis in the Appendix of this document, 440 | without loss of generality, we can approximate closely the security 441 | of the HOTP algorithm by the following formula: 442 | 443 | Sec = sv/10^Digit 444 | 445 | 446 | 447 | 448 | 449 | 450 | M'Raihi, et al. Informational [Page 8] 451 | 452 | RFC 4226 HOTP Algorithm December 2005 453 | 454 | 455 | Where: 456 | - Sec is the probability of success of the adversary; 457 | - s is the look-ahead synchronization window size; 458 | - v is the number of verification attempts; 459 | - Digit is the number of digits in HOTP values. 460 | 461 | Obviously, we can play with s, T (the Throttling parameter that would 462 | limit the number of attempts by an attacker), and Digit until 463 | achieving a certain level of security, still preserving the system 464 | usability. 465 | 466 | 7. Security Requirements 467 | 468 | Any One-Time Password algorithm is only as secure as the application 469 | and the authentication protocols that implement it. Therefore, this 470 | section discusses the critical security requirements that our choice 471 | of algorithm imposes on the authentication protocol and validation 472 | software. 473 | 474 | The parameters T and s discussed in this section have a significant 475 | impact on the security -- further details in Section 6 elaborate on 476 | the relations between these parameters and their impact on the system 477 | security. 478 | 479 | It is also important to remark that the HOTP algorithm is not a 480 | substitute for encryption and does not provide for the privacy of 481 | data transmission. Other mechanisms should be used to defeat attacks 482 | aimed at breaking confidentiality and privacy of transactions. 483 | 484 | 7.1. Authentication Protocol Requirements 485 | 486 | We introduce in this section some requirements for a protocol P 487 | implementing HOTP as the authentication method between a prover and a 488 | verifier. 489 | 490 | RP1 - P MUST support two-factor authentication, i.e., the 491 | communication and verification of something you know (secret code 492 | such as a Password, Pass phrase, PIN code, etc.) and something you 493 | have (token). The secret code is known only to the user and usually 494 | entered with the One-Time Password value for authentication purpose 495 | (two-factor authentication). 496 | 497 | RP2 - P SHOULD NOT be vulnerable to brute force attacks. This 498 | implies that a throttling/lockout scheme is RECOMMENDED on the 499 | validation server side. 500 | 501 | RP3 - P SHOULD be implemented over a secure channel in order to 502 | protect users' privacy and avoid replay attacks. 503 | 504 | 505 | 506 | M'Raihi, et al. Informational [Page 9] 507 | 508 | RFC 4226 HOTP Algorithm December 2005 509 | 510 | 511 | 7.2. Validation of HOTP Values 512 | 513 | The HOTP client (hardware or software token) increments its counter 514 | and then calculates the next HOTP value HOTP client. If the value 515 | received by the authentication server matches the value calculated by 516 | the client, then the HOTP value is validated. In this case, the 517 | server increments the counter value by one. 518 | 519 | If the value received by the server does not match the value 520 | calculated by the client, the server initiate the resynch protocol 521 | (look-ahead window) before it requests another pass. 522 | 523 | If the resynch fails, the server asks then for another 524 | authentication pass of the protocol to take place, until the 525 | maximum number of authorized attempts is reached. 526 | 527 | If and when the maximum number of authorized attempts is reached, the 528 | server SHOULD lock out the account and initiate a procedure to inform 529 | the user. 530 | 531 | 7.3. Throttling at the Server 532 | 533 | Truncating the HMAC-SHA-1 value to a shorter value makes a brute 534 | force attack possible. Therefore, the authentication server needs to 535 | detect and stop brute force attacks. 536 | 537 | We RECOMMEND setting a throttling parameter T, which defines the 538 | maximum number of possible attempts for One-Time Password validation. 539 | The validation server manages individual counters per HOTP device in 540 | order to take note of any failed attempt. We RECOMMEND T not to be 541 | too large, particularly if the resynchronization method used on the 542 | server is window-based, and the window size is large. T SHOULD be 543 | set as low as possible, while still ensuring that usability is not 544 | significantly impacted. 545 | 546 | Another option would be to implement a delay scheme to avoid a brute 547 | force attack. After each failed attempt A, the authentication server 548 | would wait for an increased T*A number of seconds, e.g., say T = 5, 549 | then after 1 attempt, the server waits for 5 seconds, at the second 550 | failed attempt, it waits for 5*2 = 10 seconds, etc. 551 | 552 | The delay or lockout schemes MUST be across login sessions to prevent 553 | attacks based on multiple parallel guessing techniques. 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | M'Raihi, et al. Informational [Page 10] 563 | 564 | RFC 4226 HOTP Algorithm December 2005 565 | 566 | 567 | 7.4. Resynchronization of the Counter 568 | 569 | Although the server's counter value is only incremented after a 570 | successful HOTP authentication, the counter on the token is 571 | incremented every time a new HOTP is requested by the user. Because 572 | of this, the counter values on the server and on the token might be 573 | out of synchronization. 574 | 575 | We RECOMMEND setting a look-ahead parameter s on the server, which 576 | defines the size of the look-ahead window. In a nutshell, the server 577 | can recalculate the next s HOTP-server values, and check them against 578 | the received HOTP client. 579 | 580 | Synchronization of counters in this scenario simply requires the 581 | server to calculate the next HOTP values and determine if there is a 582 | match. Optionally, the system MAY require the user to send a 583 | sequence of (say, 2, 3) HOTP values for resynchronization purpose, 584 | since forging a sequence of consecutive HOTP values is even more 585 | difficult than guessing a single HOTP value. 586 | 587 | The upper bound set by the parameter s ensures the server does not go 588 | on checking HOTP values forever (causing a denial-of-service attack) 589 | and also restricts the space of possible solutions for an attacker 590 | trying to manufacture HOTP values. s SHOULD be set as low as 591 | possible, while still ensuring that usability is not impacted. 592 | 593 | 7.5. Management of Shared Secrets 594 | 595 | The operations dealing with the shared secrets used to generate and 596 | verify OTP values must be performed securely, in order to mitigate 597 | risks of any leakage of sensitive information. We describe in this 598 | section different modes of operations and techniques to perform these 599 | different operations with respect to the state of the art in data 600 | security. 601 | 602 | We can consider two different avenues for generating and storing 603 | (securely) shared secrets in the Validation system: 604 | 605 | * Deterministic Generation: secrets are derived from a master 606 | seed, both at provisioning and verification stages and generated 607 | on-the-fly whenever it is required. 608 | * Random Generation: secrets are generated randomly at 609 | provisioning stage and must be stored immediately and kept 610 | secure during their life cycle. 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | M'Raihi, et al. Informational [Page 11] 619 | 620 | RFC 4226 HOTP Algorithm December 2005 621 | 622 | 623 | Deterministic Generation 624 | ------------------------ 625 | 626 | A possible strategy is to derive the shared secrets from a master 627 | secret. The master secret will be stored at the server only. A 628 | tamper-resistant device MUST be used to store the master key and 629 | derive the shared secrets from the master key and some public 630 | information. The main benefit would be to avoid the exposure of the 631 | shared secrets at any time and also avoid specific requirements on 632 | storage, since the shared secrets could be generated on-demand when 633 | needed at provisioning and validation time. 634 | 635 | We distinguish two different cases: 636 | 637 | - A single master key MK is used to derive the shared secrets; 638 | each HOTP device has a different secret, K_i = SHA-1 (MK,i) 639 | where i stands for a public piece of information that identifies 640 | uniquely the HOTP device such as a serial number, a token ID, 641 | etc. Obviously, this is in the context of an application or 642 | service -- different application or service providers will have 643 | different secrets and settings. 644 | - Several master keys MK_i are used and each HOTP device stores a 645 | set of different derived secrets, {K_i,j = SHA-1(MK_i,j)} where 646 | j stands for a public piece of information identifying the 647 | device. The idea would be to store ONLY the active master key 648 | at the validation server, in the Hardware Security Module (HSM), 649 | and keep in a safe place, using secret sharing methods such as 650 | [Shamir] for instance. In this case, if a master secret MK_i is 651 | compromised, then it is possible to switch to another secret 652 | without replacing all the devices. 653 | 654 | The drawback in the deterministic case is that the exposure of the 655 | master secret would obviously enable an attacker to rebuild any 656 | shared secret based on correct public information. The revocation of 657 | all secrets would be required, or switching to a new set of secrets 658 | in the case of multiple master keys. 659 | 660 | On the other hand, the device used to store the master key(s) and 661 | generate the shared secrets MUST be tamper resistant. Furthermore, 662 | the HSM will not be exposed outside the security perimeter of the 663 | validation system, therefore reducing the risk of leakage. 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | M'Raihi, et al. Informational [Page 12] 675 | 676 | RFC 4226 HOTP Algorithm December 2005 677 | 678 | 679 | Random Generation 680 | ----------------- 681 | 682 | The shared secrets are randomly generated. We RECOMMEND following 683 | the recommendations in [RFC4086] and selecting a good and secure 684 | random source for generating these secrets. A (true) random 685 | generator requires a naturally occurring source of randomness. 686 | Practically, there are two possible avenues to consider for the 687 | generation of the shared secrets: 688 | 689 | * Hardware-based generators: they exploit the randomness that 690 | occurs in physical phenomena. A nice implementation can be based on 691 | oscillators and built in such ways that active attacks are more 692 | difficult to perform. 693 | 694 | * Software-based generators: designing a good software random 695 | generator is not an easy task. A simple, but efficient, 696 | implementation should be based on various sources and apply to the 697 | sampled sequence a one-way function such as SHA-1. 698 | 699 | We RECOMMEND selecting proven products, being hardware or software 700 | generators, for the computation of shared secrets. 701 | 702 | We also RECOMMEND storing the shared secrets securely, and more 703 | specifically encrypting the shared secrets when stored using tamper- 704 | resistant hardware encryption and exposing them only when required: 705 | for example, the shared secret is decrypted when needed to verify an 706 | HOTP value, and re-encrypted immediately to limit exposure in the RAM 707 | for a short period of time. The data store holding the shared 708 | secrets MUST be in a secure area, to avoid as much as possible direct 709 | attack on the validation system and secrets database. 710 | 711 | Particularly, access to the shared secrets should be limited to 712 | programs and processes required by the validation system only. We 713 | will not elaborate on the different security mechanisms to put in 714 | place, but obviously, the protection of shared secrets is of the 715 | uttermost importance. 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | M'Raihi, et al. Informational [Page 13] 731 | 732 | RFC 4226 HOTP Algorithm December 2005 733 | 734 | 735 | 8. Composite Shared Secrets 736 | 737 | It may be desirable to include additional authentication factors in 738 | the shared secret K. These additional factors can consist of any 739 | data known at the token but not easily obtained by others. Examples 740 | of such data include: 741 | 742 | * PIN or Password obtained as user input at the token 743 | * Phone number 744 | * Any unique identifier programmatically available at the token 745 | 746 | In this scenario, the composite shared secret K is constructed during 747 | the provisioning process from a random seed value combined with one 748 | or more additional authentication factors. The server could either 749 | build on-demand or store composite secrets -- in any case, depending 750 | on implementation choice, the token only stores the seed value. When 751 | the token performs the HOTP calculation, it computes K from the seed 752 | value and the locally derived or input values of the other 753 | authentication factors. 754 | 755 | The use of composite shared secrets can strengthen HOTP-based 756 | authentication systems through the inclusion of additional 757 | authentication factors at the token. To the extent that the token is 758 | a trusted device, this approach has the further benefit of not 759 | requiring exposure of the authentication factors (such as the user 760 | input PIN) to other devices. 761 | 762 | 9. Bi-Directional Authentication 763 | 764 | Interestingly enough, the HOTP client could also be used to 765 | authenticate the validation server, claiming that it is a genuine 766 | entity knowing the shared secret. 767 | 768 | Since the HOTP client and the server are synchronized and share the 769 | same secret (or a method to recompute it), a simple 3-pass protocol 770 | could be put in place: 771 | 1- The end user enter the TokenID and a first OTP value OTP1; 772 | 2- The server checks OTP1 and if correct, sends back OTP2; 773 | 3- The end user checks OTP2 using his HOTP device and if correct, 774 | uses the web site. 775 | 776 | Obviously, as indicated previously, all the OTP communications have 777 | to take place over a secure channel, e.g., SSL/TLS, IPsec 778 | connections. 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | M'Raihi, et al. Informational [Page 14] 787 | 788 | RFC 4226 HOTP Algorithm December 2005 789 | 790 | 791 | 10. Conclusion 792 | 793 | This document describes HOTP, a HMAC-based One-Time Password 794 | algorithm. It also recommends the preferred implementation and 795 | related modes of operations for deploying the algorithm. 796 | 797 | The document also exhibits elements of security and demonstrates that 798 | the HOTP algorithm is practical and sound, the best possible attack 799 | being a brute force attack that can be prevented by careful 800 | implementation of countermeasures in the validation server. 801 | 802 | Eventually, several enhancements have been proposed, in order to 803 | improve security if needed for specific applications. 804 | 805 | 11. Acknowledgements 806 | 807 | The authors would like to thank Siddharth Bajaj, Alex Deacon, Loren 808 | Hart, and Nico Popp for their help during the conception and 809 | redaction of this document. 810 | 811 | 12. Contributors 812 | 813 | The authors of this document would like to emphasize the role of 814 | three persons who have made a key contribution to this document: 815 | 816 | - Laszlo Elteto is system architect with SafeNet, Inc. 817 | 818 | - Ernesto Frutos is director of Engineering with Authenex, Inc. 819 | 820 | - Fred McClain is Founder and CTO with Boojum Mobile, Inc. 821 | 822 | Without their advice and valuable inputs, this document would not be 823 | the same. 824 | 825 | 13. References 826 | 827 | 13.1. Normative References 828 | 829 | [BCK1] M. Bellare, R. Canetti and H. Krawczyk, "Keyed Hash 830 | Functions and Message Authentication", Proceedings of 831 | Crypto'96, LNCS Vol. 1109, pp. 1-15. 832 | 833 | [BCK2] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed- 834 | Hashing for Message Authentication", RFC 2104, February 835 | 1997. 836 | 837 | [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate 838 | Requirement Levels", BCP 14, RFC 2119, March 1997. 839 | 840 | 841 | 842 | M'Raihi, et al. Informational [Page 15] 843 | 844 | RFC 4226 HOTP Algorithm December 2005 845 | 846 | 847 | [RFC3979] Bradner, S., "Intellectual Property Rights in IETF 848 | Technology", BCP 79, RFC 3979, March 2005. 849 | 850 | [RFC4086] Eastlake, D., 3rd, Schiller, J., and S. Crocker, 851 | "Randomness Requirements for Security", BCP 106, RFC 4086, 852 | June 2005. 853 | 854 | 13.2. Informative References 855 | 856 | [OATH] Initiative for Open AuTHentication 857 | http://www.openauthentication.org 858 | 859 | [PrOo] B. Preneel and P. van Oorschot, "MD-x MAC and building 860 | fast MACs from hash functions", Advances in Cryptology 861 | CRYPTO '95, Lecture Notes in Computer Science Vol. 963, D. 862 | Coppersmith ed., Springer-Verlag, 1995. 863 | 864 | [Crack] Crack in SHA-1 code 'stuns' security gurus 865 | http://www.eetimes.com/showArticle.jhtml? 866 | articleID=60402150 867 | 868 | [Sha1] Bruce Schneier. SHA-1 broken. February 15, 2005. 869 | http://www.schneier.com/blog/archives/2005/02/ 870 | sha1_broken.html 871 | 872 | [Res] Researchers: Digital encryption standard flawed 873 | http://news.com.com/ 874 | Researchers+Digital+encryption+standard+flawed/ 875 | 2100-1002-5579881.html?part=dht&tag=ntop&tag=nl.e703 876 | 877 | [Shamir] How to Share a Secret, by Adi Shamir. In Communications 878 | of the ACM, Vol. 22, No. 11, pp. 612-613, November, 1979. 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | M'Raihi, et al. Informational [Page 16] 899 | 900 | RFC 4226 HOTP Algorithm December 2005 901 | 902 | 903 | Appendix A - HOTP Algorithm Security: Detailed Analysis 904 | 905 | The security analysis of the HOTP algorithm is summarized in this 906 | section. We first detail the best attack strategies, and then 907 | elaborate on the security under various assumptions and the impact of 908 | the truncation and make some recommendations regarding the number of 909 | digits. 910 | 911 | We focus this analysis on the case where Digit = 6, i.e., an HOTP 912 | function that produces 6-digit values, which is the bare minimum 913 | recommended in this document. 914 | 915 | A.1. Definitions and Notations 916 | 917 | We denote by {0,1}^l the set of all strings of length l. 918 | 919 | Let Z_{n} = {0,.., n - 1}. 920 | 921 | Let IntDiv(a,b) denote the integer division algorithm that takes 922 | input integers a, b where a >= b >= 1 and returns integers (q,r) 923 | 924 | the quotient and remainder, respectively, of the division of a by b. 925 | (Thus, a = bq + r and 0 <= r < b.) 926 | 927 | Let H: {0,1}^k x {0,1}^c --> {0,1}^n be the base function that takes 928 | a k-bit key K and c-bit counter C and returns an n-bit output H(K,C). 929 | (In the case of HOTP, H is HMAC-SHA-1; we use this formal definition 930 | for generalizing our proof of security.) 931 | 932 | A.2. The Idealized Algorithm: HOTP-IDEAL 933 | 934 | We now define an idealized counterpart of the HOTP algorithm. In 935 | this algorithm, the role of H is played by a random function that 936 | forms the key. 937 | 938 | To be more precise, let Maps(c,n) denote the set of all functions 939 | mapping from {0,1}^c to {0,1}^n. The idealized algorithm has key 940 | space Maps(c,n), so that a "key" for such an algorithm is a function 941 | h from {0,1}^c to {0,1}^n. We imagine this key (function) to be 942 | drawn at random. It is not feasible to implement this idealized 943 | algorithm, since the key, being a function from {0,1}^c to {0,1}^n, 944 | is way too large to even store. So why consider it? 945 | 946 | Our security analysis will show that as long as H satisfies a certain 947 | well-accepted assumption, the security of the actual and idealized 948 | algorithms is for all practical purposes the same. The task that 949 | really faces us, then, is to assess the security of the idealized 950 | algorithm. 951 | 952 | 953 | 954 | M'Raihi, et al. Informational [Page 17] 955 | 956 | RFC 4226 HOTP Algorithm December 2005 957 | 958 | 959 | In analyzing the idealized algorithm, we are concentrating on 960 | assessing the quality of the design of the algorithm itself, 961 | independently of HMAC-SHA-1. This is in fact the important issue. 962 | 963 | A.3. Model of Security 964 | 965 | The model exhibits the type of threats or attacks that are being 966 | considered and enables one to assess the security of HOTP and HOTP- 967 | IDEAL. We denote ALG as either HOTP or HOTP-IDEAL for the purpose of 968 | this security analysis. 969 | 970 | The scenario we are considering is that a user and server share a key 971 | K for ALG. Both maintain a counter C, initially zero, and the user 972 | authenticates itself by sending ALG(K,C) to the server. The latter 973 | accepts if this value is correct. 974 | 975 | In order to protect against accidental increment of the user counter, 976 | the server, upon receiving a value z, will accept as long as z equals 977 | ALG(K,i) for some i in the range C,...,C + s-1, where s is the 978 | resynchronization parameter and C is the server counter. If it 979 | accepts with some value of i, it then increments its counter to i+1. 980 | If it does not accept, it does not change its counter value. 981 | 982 | The model we specify captures what an adversary can do and what it 983 | needs to achieve in order to "win". First, the adversary is assumed 984 | to be able to eavesdrop, meaning, to see the authenticator 985 | transmitted by the user. Second, the adversary wins if it can get 986 | the server to accept an authenticator relative to a counter value for 987 | which the user has never transmitted an authenticator. 988 | 989 | The formal adversary, which we denote by B, starts out knowing which 990 | algorithm ALG is being used, knowing the system design, and knowing 991 | all system parameters. The one and only thing it is not given a 992 | priori is the key K shared between the user and the server. 993 | 994 | The model gives B full control of the scheduling of events. It has 995 | access to an authenticator oracle representing the user. By calling 996 | this oracle, the adversary can ask the user to authenticate itself 997 | and get back the authenticator in return. It can call this oracle as 998 | often as it wants and when it wants, using the authenticators it 999 | accumulates to perhaps "learn" how to make authenticators itself. At 1000 | any time, it may also call a verification oracle, supplying the 1001 | latter with a candidate authenticator of its choice. It wins if the 1002 | server accepts this accumulator. 1003 | 1004 | Consider the following game involving an adversary B that is 1005 | attempting to compromise the security of an authentication algorithm 1006 | ALG: K x {0,1}^c --> R. 1007 | 1008 | 1009 | 1010 | M'Raihi, et al. Informational [Page 18] 1011 | 1012 | RFC 4226 HOTP Algorithm December 2005 1013 | 1014 | 1015 | Initializations - A key K is selected at random from K, a counter C 1016 | is initialized to 0, and the Boolean value win is set to false. 1017 | 1018 | Game execution - Adversary B is provided with the two following 1019 | oracles: 1020 | 1021 | Oracle AuthO() 1022 | -------------- 1023 | A = ALG(K,C) 1024 | C = C + 1 1025 | Return O to B 1026 | 1027 | Oracle VerO(A) 1028 | -------------- 1029 | i = C 1030 | While (i <= C + s - 1 and Win == FALSE) do 1031 | If A == ALG(K,i) then Win = TRUE; C = i + 1 1032 | Else i = i + 1 1033 | Return Win to B 1034 | 1035 | AuthO() is the authenticator oracle and VerO(A) is the verification 1036 | oracle. 1037 | 1038 | Upon execution, B queries the two oracles at will. Let Adv(B) be the 1039 | probability that win gets set to true in the above game. This is the 1040 | probability that the adversary successfully impersonates the user. 1041 | 1042 | Our goal is to assess how large this value can be as a function of 1043 | the number v of verification queries made by B, the number a of 1044 | authenticator oracle queries made by B, and the running time t of B. 1045 | This will tell us how to set the throttle, which effectively upper 1046 | bounds v. 1047 | 1048 | A.4. Security of the Ideal Authentication Algorithm 1049 | 1050 | This section summarizes the security analysis of HOTP-IDEAL, starting 1051 | with the impact of the conversion modulo 10^Digit and then focusing 1052 | on the different possible attacks. 1053 | 1054 | A.4.1. From Bits to Digits 1055 | 1056 | The dynamic offset truncation of a random n-bit string yields a 1057 | random 31-bit string. What happens to the distribution when it is 1058 | taken modulo m = 10^Digit, as done in HOTP? 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | M'Raihi, et al. Informational [Page 19] 1067 | 1068 | RFC 4226 HOTP Algorithm December 2005 1069 | 1070 | 1071 | The following lemma estimates the biases in the outputs in this case. 1072 | 1073 | Lemma 1 1074 | ------- 1075 | Let N >= m >= 1 be integers, and let (q,r) = IntDiv(N,m). For z in 1076 | Z_{m} let: 1077 | 1078 | P_{N,m}(z) = Pr [x mod m = z : x randomly pick in Z_{n}] 1079 | 1080 | Then for any z in Z_{m} 1081 | 1082 | P_{N,m}(z) = (q + 1) / N if 0 <= z < r 1083 | q / N if r <= z < m 1084 | 1085 | Proof of Lemma 1 1086 | ---------------- 1087 | Let the random variable X be uniformly distributed over Z_{N}. Then: 1088 | 1089 | P_{N,m}(z) = Pr [X mod m = z] 1090 | 1091 | = Pr [X < mq] * Pr [X mod m = z| X < mq] 1092 | + Pr [mq <= X < N] * Pr [X mod m = z| mq <= X < N] 1093 | 1094 | = mq/N * 1/m + 1095 | (N - mq)/N * 1 / (N - mq) if 0 <= z < N - mq 1096 | 0 if N - mq <= z <= m 1097 | 1098 | = q/N + 1099 | r/N * 1 / r if 0 <= z < N - mq 1100 | 0 if r <= z <= m 1101 | 1102 | Simplifying yields the claimed equation. 1103 | 1104 | Let N = 2^31, d = 6, and m = 10^d. If x is chosen at random from 1105 | Z_{N} (meaning, is a random 31-bit string), then reducing it to a 6- 1106 | digit number by taking x mod m does not yield a random 6-digit 1107 | number. 1108 | 1109 | Rather, x mod m is distributed as shown in the following table: 1110 | 1111 | Values Probability that each appears as output 1112 | ---------------------------------------------------------------- 1113 | 0,1,...,483647 2148/2^31 roughly equals to 1.00024045/10^6 1114 | 483648,...,999999 2147/2^31 roughly equals to 0.99977478/10^6 1115 | 1116 | If X is uniformly distributed over Z_{2^31} (meaning, is a random 1117 | 31-bit string), then the above shows the probabilities for different 1118 | outputs of X mod 10^6. The first set of values appears with 1119 | 1120 | 1121 | 1122 | M'Raihi, et al. Informational [Page 20] 1123 | 1124 | RFC 4226 HOTP Algorithm December 2005 1125 | 1126 | 1127 | probability slightly greater than 10^-6, the rest with probability 1128 | slightly less, meaning that the distribution is slightly non-uniform. 1129 | 1130 | However, as the table above indicates, the bias is small, and as we 1131 | will see later, negligible: the probabilities are very close to 1132 | 10^-6. 1133 | 1134 | A.4.2. Brute Force Attacks 1135 | 1136 | If the authenticator consisted of d random digits, then a brute force 1137 | attack using v verification attempts would succeed with probability 1138 | sv/10^Digit. 1139 | 1140 | However, an adversary can exploit the bias in the outputs of 1141 | HOTP-IDEAL, predicted by Lemma 1, to mount a slightly better attack. 1142 | 1143 | Namely, it makes authentication attempts with authenticators that are 1144 | the most likely values, meaning the ones in the range 0,...,r - 1, 1145 | where (q,r) = IntDiv(2^31,10^Digit). 1146 | 1147 | The following specifies an adversary in our model of security that 1148 | mounts the attack. It estimates the success probability as a 1149 | function of the number of verification queries. 1150 | 1151 | For simplicity, we assume that the number of verification queries is 1152 | at most r. With N = 2^31 and m = 10^6, we have r = 483,648, and the 1153 | throttle value is certainly less than this, so this assumption is not 1154 | much of a restriction. 1155 | 1156 | Proposition 1 1157 | ------------- 1158 | 1159 | Suppose m = 10^Digit < 2^31, and let (q,r) = IntDiv(2^31,m). Assume 1160 | s <= m. The brute-force-attack adversary B-bf attacks HOTP using v 1161 | <= r verification oracle queries. This adversary makes no 1162 | authenticator oracle queries, and succeeds with probability 1163 | 1164 | Adv(B-bf) = 1 - (1 - v(q+1)/2^31)^s 1165 | 1166 | which is roughly equal to 1167 | 1168 | sv * (q+1)/2^31 1169 | 1170 | With m = 10^6 we get q = 2,147. In that case, the brute force attack 1171 | using v verification attempts succeeds with probability 1172 | 1173 | Adv(B-bf) roughly = sv * 2148/2^31 = sv * 1.00024045/10^6 1174 | 1175 | 1176 | 1177 | 1178 | M'Raihi, et al. Informational [Page 21] 1179 | 1180 | RFC 4226 HOTP Algorithm December 2005 1181 | 1182 | 1183 | As this equation shows, the resynchronization parameter s has a 1184 | significant impact in that the adversary's success probability is 1185 | proportional to s. This means that s cannot be made too large 1186 | without compromising security. 1187 | 1188 | A.4.3. Brute force attacks are the best possible attacks. 1189 | 1190 | A central question is whether there are attacks any better than the 1191 | brute force one. In particular, the brute force attack did not 1192 | attempt to collect authenticators sent by the user and try to 1193 | cryptanalyze them in an attempt to learn how to better construct 1194 | authenticators. Would doing this help? Is there some way to "learn" 1195 | how to build authenticators that result in a higher success rate than 1196 | given by the brute-force attack? 1197 | 1198 | The following says the answer to these questions is no. No matter 1199 | what strategy the adversary uses, and even if it sees, and tries to 1200 | exploit, the authenticators from authentication attempts of the user, 1201 | its success probability will not be above that of the brute force 1202 | attack -- this is true as long as the number of authentications it 1203 | observes is not incredibly large. This is valuable information 1204 | regarding the security of the scheme. 1205 | 1206 | Proposition 2 ------------- Suppose m = 10^Digit < 2^31, and let 1207 | (q,r) = IntDiv(2^31,m). Let B be any adversary attacking HOTP-IDEAL 1208 | using v verification oracle queries and a <= 2^c - s authenticator 1209 | oracle queries. Then 1210 | 1211 | Adv(B) < = sv * (q+1)/ 2^31 1212 | 1213 | Note: This result is conditional on the adversary not seeing more 1214 | than 2^c - s authentications performed by the user, which is hardly 1215 | restrictive as long as c is large enough. 1216 | 1217 | With m = 10^6, we get q = 2,147. In that case, Proposition 2 says 1218 | that any adversary B attacking HOTP-IDEAL and making v verification 1219 | attempts succeeds with probability at most 1220 | 1221 | Equation 1 1222 | ---------- 1223 | sv * 2148/2^31 roughly = sv * 1.00024045/10^6 1224 | 1225 | Meaning, B's success rate is not more than that achieved by the brute 1226 | force attack. 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | M'Raihi, et al. Informational [Page 22] 1235 | 1236 | RFC 4226 HOTP Algorithm December 2005 1237 | 1238 | 1239 | A.5. Security Analysis of HOTP 1240 | 1241 | We have analyzed, in the previous sections, the security of the 1242 | idealized counterparts HOTP-IDEAL of the actual authentication 1243 | algorithm HOTP. We now show that, under appropriate and well- 1244 | believed assumption on H, the security of the actual algorithms is 1245 | essentially the same as that of its idealized counterpart. 1246 | 1247 | The assumption in question is that H is a secure pseudorandom 1248 | function, or PRF, meaning that its input-output values are 1249 | indistinguishable from those of a random function in practice. 1250 | 1251 | Consider an adversary A that is given an oracle for a function f: 1252 | {0,1}^c --> {0, 1}^n and eventually outputs a bit. We denote Adv(A) 1253 | as the prf-advantage of A, which represents how well the adversary 1254 | does at distinguishing the case where its oracle is H(K,.) from the 1255 | case where its oracle is a random function of {0,1}^c to {0,1}^n. 1256 | 1257 | One possible attack is based on exhaustive search for the key K. If 1258 | A runs for t steps and T denotes the time to perform one computation 1259 | of H, its prf-advantage from this attack turns out to be (t/T)2^-k. 1260 | Another possible attack is a birthday one [PrOo], whereby A can 1261 | attain advantage p^2/2^n in p oracle queries and running time about 1262 | pT. 1263 | 1264 | Our assumption is that these are the best possible attacks. This 1265 | translates into the following. 1266 | 1267 | Assumption 1 1268 | ------------ 1269 | 1270 | Let T denotes the time to perform one computation of H. Then if A is 1271 | any adversary with running time at most t and making at most p oracle 1272 | queries, 1273 | 1274 | Adv(A) <= (t/T)/2^k + p^2/2^n 1275 | 1276 | In practice, this assumption means that H is very secure as PRF. For 1277 | example, given that k = n = 160, an attacker with running time 2^60 1278 | and making 2^40 oracle queries has advantage at most (about) 2^-80. 1279 | 1280 | Theorem 1 1281 | --------- 1282 | 1283 | Suppose m = 10^Digit < 2^31, and let (q,r) = IntDiv(2^31,m). Let B 1284 | be any adversary attacking HOTP using v verification oracle queries, 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | M'Raihi, et al. Informational [Page 23] 1291 | 1292 | RFC 4226 HOTP Algorithm December 2005 1293 | 1294 | 1295 | a <= 2^c - s authenticator oracle queries, and running time t. Let T 1296 | denote the time to perform one computation of H. If Assumption 1 is 1297 | true, then 1298 | 1299 | Adv(B) <= sv * (q + 1)/2^31 + (t/T)/2^k + ((sv + a)^2)/2^n 1300 | 1301 | In practice, the (t/T)2^-k + ((sv + a)^2)2^-n term is much smaller 1302 | than the sv(q + 1)/2^n term, so that the above says that for all 1303 | practical purposes the success rate of an adversary attacking HOTP is 1304 | sv(q + 1)/2^n, just as for HOTP-IDEAL, meaning the HOTP algorithm is 1305 | in practice essentially as good as its idealized counterpart. 1306 | 1307 | In the case m = 10^6 of a 6-digit output, this means that an 1308 | adversary making v authentication attempts will have a success rate 1309 | that is at most that of Equation 1. 1310 | 1311 | For example, consider an adversary with running time at most 2^60 1312 | that sees at most 2^40 authentication attempts of the user. Both 1313 | these choices are very generous to the adversary, who will typically 1314 | not have these resources, but we are saying that even such a powerful 1315 | adversary will not have more success than indicated by Equation 1. 1316 | 1317 | We can safely assume sv <= 2^40 due to the throttling and bounds on 1318 | s. So: 1319 | 1320 | (t/T)/2^k + ((sv + a)^2)/2^n <= 2^60/2^160 + (2^41)^2/2^160 1321 | roughly <= 2^-78 1322 | 1323 | which is much smaller than the success probability of Equation 1 and 1324 | negligible compared to it. 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | M'Raihi, et al. Informational [Page 24] 1347 | 1348 | RFC 4226 HOTP Algorithm December 2005 1349 | 1350 | 1351 | Appendix B - SHA-1 Attacks 1352 | 1353 | This sections addresses the impact of the recent attacks on SHA-1 on 1354 | the security of the HMAC-SHA-1-based HOTP. We begin with some 1355 | discussion of the situation of SHA-1 and then discuss the relevance 1356 | to HMAC-SHA-1 and HOTP. Cited references are in Section 13. 1357 | 1358 | B.1. SHA-1 Status 1359 | 1360 | A collision for a hash function h means a pair x,y of different 1361 | inputs such that h(x)=h(y). Since SHA-1 outputs 160 bits, a birthday 1362 | attack finds a collision in 2^{80} trials. (A trial means one 1363 | computation of the function.) This was thought to be the best 1364 | possible until Wang, Yin, and Yu announced on February 15, 2005, that 1365 | they had an attack finding collisions in 2^{69} trials. 1366 | 1367 | Is SHA-1 broken? For most practical purposes, we would say probably 1368 | not, since the resources needed to mount the attack are huge. Here 1369 | is one way to get a sense of it: we can estimate it is about the same 1370 | as the time we would need to factor a 760-bit RSA modulus, and this 1371 | is currently considered out of reach. 1372 | 1373 | Burr of NIST is quoted in [Crack] as saying "Large national 1374 | intelligence agencies could do this in a reasonable amount of time 1375 | with a few million dollars in computer time". However, the 1376 | computation may be out of reach of all but such well-funded agencies. 1377 | 1378 | One should also ask what impact finding SHA-1 collisions actually has 1379 | on security of real applications such as signatures. To exploit a 1380 | collision x,y to forge signatures, you need to somehow obtain a 1381 | signature of x and then you can forge a signature of y. How damaging 1382 | this is depends on the content of y: the y created by the attack may 1383 | not be meaningful in the application context. Also, one needs a 1384 | chosen-message attack to get the signature of x. This seems possible 1385 | in some contexts, but not others. Overall, it is not clear that the 1386 | impact on the security of signatures is significant. 1387 | 1388 | Indeed, one can read in the press that SHA-1 is "broken" [Sha1] and 1389 | that encryption and SSL are "broken" [Res]. The media have a 1390 | tendency to magnify events: it would hardly be interesting to 1391 | announce in the news that a team of cryptanalysts did very 1392 | interesting theoretical work in attacking SHA-1. 1393 | 1394 | Cryptographers are excited too. But mainly because this is an 1395 | important theoretical breakthrough. Attacks can only get better with 1396 | time: it is therefore important to monitor any progress in hash 1397 | functions cryptanalysis and be prepared for any really practical 1398 | break with a sound migration plan for the future. 1399 | 1400 | 1401 | 1402 | M'Raihi, et al. Informational [Page 25] 1403 | 1404 | RFC 4226 HOTP Algorithm December 2005 1405 | 1406 | 1407 | B.2. HMAC-SHA-1 Status 1408 | 1409 | The new attacks on SHA-1 have no impact on the security of 1410 | HMAC-SHA-1. The best attack on the latter remains one needing a 1411 | sender to authenticate 2^{80} messages before an adversary can create 1412 | a forgery. Why? 1413 | 1414 | HMAC is not a hash function. It is a message authentication code 1415 | (MAC) that uses a hash function internally. A MAC depends on a 1416 | secret key, while hash functions don't. What one needs to worry 1417 | about with a MAC is forgery, not collisions. HMAC was designed so 1418 | that collisions in the hash function (here SHA-1) do not yield 1419 | forgeries for HMAC. 1420 | 1421 | Recall that HMAC-SHA-1(K,x) = SHA-1(K_o,SHA-1(K_i,x)) where the keys 1422 | K_o,K_i are derived from K. Suppose the attacker finds a pair x,y 1423 | such that SHA-1(K_i,x) = SHA-1(K_i,y). (Call this a hidden-key 1424 | collision.) Then if it can obtain the MAC of x (itself a tall 1425 | order), it can forge the MAC of y. (These values are the same.) But 1426 | finding hidden-key collisions is harder than finding collisions, 1427 | because the attacker does not know the hidden key K_i. All it may 1428 | have is some outputs of HMAC-SHA-1 with key K. To date, there are no 1429 | claims or evidence that the recent attacks on SHA-1 extend to find 1430 | hidden-key collisions. 1431 | 1432 | Historically, the HMAC design has already proven itself in this 1433 | regard. MD5 is considered broken in that collisions in this hash 1434 | function can be found relatively easily. But there is still no 1435 | attack on HMAC-MD5 better than the trivial 2^{64} time birthday one. 1436 | (MD5 outputs 128 bits, not 160.) We are seeing this strength of HMAC 1437 | coming into play again in the SHA-1 context. 1438 | 1439 | B.3. HOTP Status 1440 | 1441 | Since no new weakness has surfaced in HMAC-SHA-1, there is no impact 1442 | on HOTP. The best attacks on HOTP remain those described in the 1443 | document, namely, to try to guess output values. 1444 | 1445 | The security proof of HOTP requires that HMAC-SHA-1 behave like a 1446 | pseudorandom function. The quality of HMAC-SHA-1 as a pseudorandom 1447 | function is not impacted by the new attacks on SHA-1, and so neither 1448 | is this proven guarantee. 1449 | 1450 | 1451 | 1452 | 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | M'Raihi, et al. Informational [Page 26] 1459 | 1460 | RFC 4226 HOTP Algorithm December 2005 1461 | 1462 | 1463 | Appendix C - HOTP Algorithm: Reference Implementation 1464 | 1465 | /* 1466 | * OneTimePasswordAlgorithm.java 1467 | * OATH Initiative, 1468 | * HOTP one-time password algorithm 1469 | * 1470 | */ 1471 | 1472 | /* Copyright (C) 2004, OATH. All rights reserved. 1473 | * 1474 | * License to copy and use this software is granted provided that it 1475 | * is identified as the "OATH HOTP Algorithm" in all material 1476 | * mentioning or referencing this software or this function. 1477 | * 1478 | * License is also granted to make and use derivative works provided 1479 | * that such works are identified as 1480 | * "derived from OATH HOTP algorithm" 1481 | * in all material mentioning or referencing the derived work. 1482 | * 1483 | * OATH (Open AuTHentication) and its members make no 1484 | * representations concerning either the merchantability of this 1485 | * software or the suitability of this software for any particular 1486 | * purpose. 1487 | * 1488 | * It is provided "as is" without express or implied warranty 1489 | * of any kind and OATH AND ITS MEMBERS EXPRESSaLY DISCLAIMS 1490 | * ANY WARRANTY OR LIABILITY OF ANY KIND relating to this software. 1491 | * 1492 | * These notices must be retained in any copies of any part of this 1493 | * documentation and/or software. 1494 | */ 1495 | 1496 | package org.openauthentication.otp; 1497 | 1498 | import java.io.IOException; 1499 | import java.io.File; 1500 | import java.io.DataInputStream; 1501 | import java.io.FileInputStream ; 1502 | import java.lang.reflect.UndeclaredThrowableException; 1503 | 1504 | import java.security.GeneralSecurityException; 1505 | import java.security.NoSuchAlgorithmException; 1506 | import java.security.InvalidKeyException; 1507 | 1508 | import javax.crypto.Mac; 1509 | import javax.crypto.spec.SecretKeySpec; 1510 | 1511 | 1512 | 1513 | 1514 | M'Raihi, et al. Informational [Page 27] 1515 | 1516 | RFC 4226 HOTP Algorithm December 2005 1517 | 1518 | 1519 | /** 1520 | * This class contains static methods that are used to calculate the 1521 | * One-Time Password (OTP) using 1522 | * JCE to provide the HMAC-SHA-1. 1523 | * 1524 | * @author Loren Hart 1525 | * @version 1.0 1526 | */ 1527 | public class OneTimePasswordAlgorithm { 1528 | private OneTimePasswordAlgorithm() {} 1529 | 1530 | // These are used to calculate the check-sum digits. 1531 | // 0 1 2 3 4 5 6 7 8 9 1532 | private static final int[] doubleDigits = 1533 | { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 }; 1534 | 1535 | /** 1536 | * Calculates the checksum using the credit card algorithm. 1537 | * This algorithm has the advantage that it detects any single 1538 | * mistyped digit and any single transposition of 1539 | * adjacent digits. 1540 | * 1541 | * @param num the number to calculate the checksum for 1542 | * @param digits number of significant places in the number 1543 | * 1544 | * @return the checksum of num 1545 | */ 1546 | public static int calcChecksum(long num, int digits) { 1547 | boolean doubleDigit = true; 1548 | int total = 0; 1549 | while (0 < digits--) { 1550 | int digit = (int) (num % 10); 1551 | num /= 10; 1552 | if (doubleDigit) { 1553 | digit = doubleDigits[digit]; 1554 | } 1555 | total += digit; 1556 | doubleDigit = !doubleDigit; 1557 | } 1558 | int result = total % 10; 1559 | if (result > 0) { 1560 | result = 10 - result; 1561 | } 1562 | return result; 1563 | } 1564 | 1565 | /** 1566 | * This method uses the JCE to provide the HMAC-SHA-1 1567 | 1568 | 1569 | 1570 | M'Raihi, et al. Informational [Page 28] 1571 | 1572 | RFC 4226 HOTP Algorithm December 2005 1573 | 1574 | 1575 | * algorithm. 1576 | * HMAC computes a Hashed Message Authentication Code and 1577 | * in this case SHA1 is the hash algorithm used. 1578 | * 1579 | * @param keyBytes the bytes to use for the HMAC-SHA-1 key 1580 | * @param text the message or text to be authenticated. 1581 | * 1582 | * @throws NoSuchAlgorithmException if no provider makes 1583 | * either HmacSHA1 or HMAC-SHA-1 1584 | * digest algorithms available. 1585 | * @throws InvalidKeyException 1586 | * The secret provided was not a valid HMAC-SHA-1 key. 1587 | * 1588 | */ 1589 | 1590 | public static byte[] hmac_sha1(byte[] keyBytes, byte[] text) 1591 | throws NoSuchAlgorithmException, InvalidKeyException 1592 | { 1593 | // try { 1594 | Mac hmacSha1; 1595 | try { 1596 | hmacSha1 = Mac.getInstance("HmacSHA1"); 1597 | } catch (NoSuchAlgorithmException nsae) { 1598 | hmacSha1 = Mac.getInstance("HMAC-SHA-1"); 1599 | } 1600 | SecretKeySpec macKey = 1601 | new SecretKeySpec(keyBytes, "RAW"); 1602 | hmacSha1.init(macKey); 1603 | return hmacSha1.doFinal(text); 1604 | // } catch (GeneralSecurityException gse) { 1605 | // throw new UndeclaredThrowableException(gse); 1606 | // } 1607 | } 1608 | 1609 | private static final int[] DIGITS_POWER 1610 | // 0 1 2 3 4 5 6 7 8 1611 | = {1,10,100,1000,10000,100000,1000000,10000000,100000000}; 1612 | 1613 | /** 1614 | * This method generates an OTP value for the given 1615 | * set of parameters. 1616 | * 1617 | * @param secret the shared secret 1618 | * @param movingFactor the counter, time, or other value that 1619 | * changes on a per use basis. 1620 | * @param codeDigits the number of digits in the OTP, not 1621 | * including the checksum, if any. 1622 | * @param addChecksum a flag that indicates if a checksum digit 1623 | 1624 | 1625 | 1626 | M'Raihi, et al. Informational [Page 29] 1627 | 1628 | RFC 4226 HOTP Algorithm December 2005 1629 | 1630 | 1631 | * should be appended to the OTP. 1632 | * @param truncationOffset the offset into the MAC result to 1633 | * begin truncation. If this value is out of 1634 | * the range of 0 ... 15, then dynamic 1635 | * truncation will be used. 1636 | * Dynamic truncation is when the last 4 1637 | * bits of the last byte of the MAC are 1638 | * used to determine the start offset. 1639 | * @throws NoSuchAlgorithmException if no provider makes 1640 | * either HmacSHA1 or HMAC-SHA-1 1641 | * digest algorithms available. 1642 | * @throws InvalidKeyException 1643 | * The secret provided was not 1644 | * a valid HMAC-SHA-1 key. 1645 | * 1646 | * @return A numeric String in base 10 that includes 1647 | * {@link codeDigits} digits plus the optional checksum 1648 | * digit if requested. 1649 | */ 1650 | static public String generateOTP(byte[] secret, 1651 | long movingFactor, 1652 | int codeDigits, 1653 | boolean addChecksum, 1654 | int truncationOffset) 1655 | throws NoSuchAlgorithmException, InvalidKeyException 1656 | { 1657 | // put movingFactor value into text byte array 1658 | String result = null; 1659 | int digits = addChecksum ? (codeDigits + 1) : codeDigits; 1660 | byte[] text = new byte[8]; 1661 | for (int i = text.length - 1; i >= 0; i--) { 1662 | text[i] = (byte) (movingFactor & 0xff); 1663 | movingFactor >>= 8; 1664 | } 1665 | 1666 | // compute hmac hash 1667 | byte[] hash = hmac_sha1(secret, text); 1668 | 1669 | // put selected bytes into result int 1670 | int offset = hash[hash.length - 1] & 0xf; 1671 | if ( (0<=truncationOffset) && 1672 | (truncationOffset<(hash.length-4)) ) { 1673 | offset = truncationOffset; 1674 | } 1675 | int binary = 1676 | ((hash[offset] & 0x7f) << 24) 1677 | | ((hash[offset + 1] & 0xff) << 16) 1678 | | ((hash[offset + 2] & 0xff) << 8) 1679 | 1680 | 1681 | 1682 | M'Raihi, et al. Informational [Page 30] 1683 | 1684 | RFC 4226 HOTP Algorithm December 2005 1685 | 1686 | 1687 | | (hash[offset + 3] & 0xff); 1688 | 1689 | int otp = binary % DIGITS_POWER[codeDigits]; 1690 | if (addChecksum) { 1691 | otp = (otp * 10) + calcChecksum(otp, codeDigits); 1692 | } 1693 | result = Integer.toString(otp); 1694 | while (result.length() < digits) { 1695 | result = "0" + result; 1696 | } 1697 | return result; 1698 | } 1699 | } 1700 | 1701 | 1702 | 1703 | 1704 | 1705 | 1706 | 1707 | 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1720 | 1721 | 1722 | 1723 | 1724 | 1725 | 1726 | 1727 | 1728 | 1729 | 1730 | 1731 | 1732 | 1733 | 1734 | 1735 | 1736 | 1737 | 1738 | M'Raihi, et al. Informational [Page 31] 1739 | 1740 | RFC 4226 HOTP Algorithm December 2005 1741 | 1742 | 1743 | Appendix D - HOTP Algorithm: Test Values 1744 | 1745 | The following test data uses the ASCII string 1746 | "12345678901234567890" for the secret: 1747 | 1748 | Secret = 0x3132333435363738393031323334353637383930 1749 | 1750 | Table 1 details for each count, the intermediate HMAC value. 1751 | 1752 | Count Hexadecimal HMAC-SHA-1(secret, count) 1753 | 0 cc93cf18508d94934c64b65d8ba7667fb7cde4b0 1754 | 1 75a48a19d4cbe100644e8ac1397eea747a2d33ab 1755 | 2 0bacb7fa082fef30782211938bc1c5e70416ff44 1756 | 3 66c28227d03a2d5529262ff016a1e6ef76557ece 1757 | 4 a904c900a64b35909874b33e61c5938a8e15ed1c 1758 | 5 a37e783d7b7233c083d4f62926c7a25f238d0316 1759 | 6 bc9cd28561042c83f219324d3c607256c03272ae 1760 | 7 a4fb960c0bc06e1eabb804e5b397cdc4b45596fa 1761 | 8 1b3c89f65e6c9e883012052823443f048b4332db 1762 | 9 1637409809a679dc698207310c8c7fc07290d9e5 1763 | 1764 | Table 2 details for each count the truncated values (both in 1765 | hexadecimal and decimal) and then the HOTP value. 1766 | 1767 | Truncated 1768 | Count Hexadecimal Decimal HOTP 1769 | 0 4c93cf18 1284755224 755224 1770 | 1 41397eea 1094287082 287082 1771 | 2 82fef30 137359152 359152 1772 | 3 66ef7655 1726969429 969429 1773 | 4 61c5938a 1640338314 338314 1774 | 5 33c083d4 868254676 254676 1775 | 6 7256c032 1918287922 287922 1776 | 7 4e5b397 82162583 162583 1777 | 8 2823443f 673399871 399871 1778 | 9 2679dc69 645520489 520489 1779 | 1780 | 1781 | 1782 | 1783 | 1784 | 1785 | 1786 | 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 1793 | 1794 | M'Raihi, et al. Informational [Page 32] 1795 | 1796 | RFC 4226 HOTP Algorithm December 2005 1797 | 1798 | 1799 | Appendix E - Extensions 1800 | 1801 | 1802 | We introduce in this section several enhancements to the HOTP 1803 | algorithm. These are not recommended extensions or part of the 1804 | standard algorithm, but merely variations that could be used for 1805 | customized implementations. 1806 | 1807 | E.1. Number of Digits 1808 | 1809 | A simple enhancement in terms of security would be to extract more 1810 | digits from the HMAC-SHA-1 value. 1811 | 1812 | For instance, calculating the HOTP value modulo 10^8 to build an 8- 1813 | digit HOTP value would reduce the probability of success of the 1814 | adversary from sv/10^6 to sv/10^8. 1815 | 1816 | This could give the opportunity to improve usability, e.g., by 1817 | increasing T and/or s, while still achieving a better security 1818 | overall. For instance, s = 10 and 10v/10^8 = v/10^7 < v/10^6 which 1819 | is the theoretical optimum for 6-digit code when s = 1. 1820 | 1821 | E.2. Alphanumeric Values 1822 | 1823 | Another option is to use A-Z and 0-9 values; or rather a subset of 32 1824 | symbols taken from the alphanumerical alphabet in order to avoid any 1825 | confusion between characters: 0, O, and Q as well as l, 1, and I are 1826 | very similar, and can look the same on a small display. 1827 | 1828 | The immediate consequence is that the security is now in the order of 1829 | sv/32^6 for a 6-digit HOTP value and sv/32^8 for an 8-digit HOTP 1830 | value. 1831 | 1832 | 32^6 > 10^9 so the security of a 6-alphanumeric HOTP code is slightly 1833 | better than a 9-digit HOTP value, which is the maximum length of an 1834 | HOTP code supported by the proposed algorithm. 1835 | 1836 | 32^8 > 10^12 so the security of an 8-alphanumeric HOTP code is 1837 | significantly better than a 9-digit HOTP value. 1838 | 1839 | Depending on the application and token/interface used for displaying 1840 | and entering the HOTP value, the choice of alphanumeric values could 1841 | be a simple and efficient way to improve security at a reduced cost 1842 | and impact on users. 1843 | 1844 | 1845 | 1846 | 1847 | 1848 | 1849 | 1850 | M'Raihi, et al. Informational [Page 33] 1851 | 1852 | RFC 4226 HOTP Algorithm December 2005 1853 | 1854 | 1855 | E.3. Sequence of HOTP Values 1856 | 1857 | As we suggested for the resynchronization to enter a short sequence 1858 | (say, 2 or 3) of HOTP values, we could generalize the concept to the 1859 | protocol, and add a parameter L that would define the length of the 1860 | HOTP sequence to enter. 1861 | 1862 | Per default, the value L SHOULD be set to 1, but if security needs to 1863 | be increased, users might be asked (possibly for a short period of 1864 | time, or a specific operation) to enter L HOTP values. 1865 | 1866 | This is another way, without increasing the HOTP length or using 1867 | alphanumeric values to tighten security. 1868 | 1869 | Note: The system MAY also be programmed to request synchronization on 1870 | a regular basis (e.g., every night, twice a week, etc.) and to 1871 | achieve this purpose, ask for a sequence of L HOTP values. 1872 | 1873 | E.4. A Counter-Based Resynchronization Method 1874 | 1875 | In this case, we assume that the client can access and send not only 1876 | the HOTP value but also other information, more specifically, the 1877 | counter value. 1878 | 1879 | A more efficient and secure method for resynchronization is possible 1880 | in this case. The client application will not send the HOTP-client 1881 | value only, but the HOTP-client and the related C-client counter 1882 | value, the HOTP value acting as a message authentication code of the 1883 | counter. 1884 | 1885 | Resynchronization Counter-based Protocol (RCP) 1886 | ---------------------------------------------- 1887 | 1888 | The server accepts if the following are all true, where C-server is 1889 | its own current counter value: 1890 | 1891 | 1) C-client >= C-server 1892 | 2) C-client - C-server <= s 1893 | 3) Check that HOTP client is valid HOTP(K,C-Client) 1894 | 4) If true, the server sets C to C-client + 1 and client is 1895 | authenticated 1896 | 1897 | In this case, there is no need for managing a look-ahead window 1898 | anymore. The probability of success of the adversary is only v/10^6 1899 | or roughly v in one million. A side benefit is obviously to be able 1900 | to increase s "infinitely" and therefore improve the system usability 1901 | without impacting the security. 1902 | 1903 | 1904 | 1905 | 1906 | M'Raihi, et al. Informational [Page 34] 1907 | 1908 | RFC 4226 HOTP Algorithm December 2005 1909 | 1910 | 1911 | This resynchronization protocol SHOULD be used whenever the related 1912 | impact on the client and server applications is deemed acceptable. 1913 | 1914 | E.5. Data Field 1915 | 1916 | Another interesting option is the introduction of a Data field, which 1917 | would be used for generating the One-Time Password values: HOTP (K, 1918 | C, [Data]) where Data is an optional field that can be the 1919 | concatenation of various pieces of identity-related information, 1920 | e.g., Data = Address | PIN. 1921 | 1922 | We could also use a Timer, either as the only moving factor or in 1923 | combination with the Counter -- in this case, e.g., Data = Timer, 1924 | where Timer could be the UNIX-time (GMT seconds since 1/1/1970) 1925 | divided by some factor (8, 16, 32, etc.) in order to give a specific 1926 | time step. The time window for the One-Time Password is then equal 1927 | to the time step multiplied by the resynchronization parameter as 1928 | defined before. For example, if we take 64 seconds as the time step 1929 | and 7 for the resynchronization parameter, we obtain an acceptance 1930 | window of +/- 3 minutes. 1931 | 1932 | Using a Data field opens for more flexibility in the algorithm 1933 | implementation, provided that the Data field is clearly specified. 1934 | 1935 | 1936 | 1937 | 1938 | 1939 | 1940 | 1941 | 1942 | 1943 | 1944 | 1945 | 1946 | 1947 | 1948 | 1949 | 1950 | 1951 | 1952 | 1953 | 1954 | 1955 | 1956 | 1957 | 1958 | 1959 | 1960 | 1961 | 1962 | M'Raihi, et al. Informational [Page 35] 1963 | 1964 | RFC 4226 HOTP Algorithm December 2005 1965 | 1966 | 1967 | Authors' Addresses 1968 | 1969 | David M'Raihi (primary contact for sending comments and questions) 1970 | VeriSign, Inc. 1971 | 685 E. Middlefield Road 1972 | Mountain View, CA 94043 USA 1973 | 1974 | Phone: 1-650-426-3832 1975 | EMail: dmraihi@verisign.com 1976 | 1977 | 1978 | Mihir Bellare 1979 | Dept of Computer Science and Engineering, Mail Code 0114 1980 | University of California at San Diego 1981 | 9500 Gilman Drive 1982 | La Jolla, CA 92093, USA 1983 | 1984 | EMail: mihir@cs.ucsd.edu 1985 | 1986 | 1987 | Frank Hoornaert 1988 | VASCO Data Security, Inc. 1989 | Koningin Astridlaan 164 1990 | 1780 Wemmel, Belgium 1991 | 1992 | EMail: frh@vasco.com 1993 | 1994 | 1995 | David Naccache 1996 | Gemplus Innovation 1997 | 34 rue Guynemer, 92447, 1998 | Issy les Moulineaux, France 1999 | and 2000 | Information Security Group, 2001 | Royal Holloway, 2002 | University of London, Egham, 2003 | Surrey TW20 0EX, UK 2004 | 2005 | EMail: david.naccache@gemplus.com, david.naccache@rhul.ac.uk 2006 | 2007 | 2008 | Ohad Ranen 2009 | Aladdin Knowledge Systems Ltd. 2010 | 15 Beit Oved Street 2011 | Tel Aviv, Israel 61110 2012 | 2013 | EMail: Ohad.Ranen@ealaddin.com 2014 | 2015 | 2016 | 2017 | 2018 | M'Raihi, et al. Informational [Page 36] 2019 | 2020 | RFC 4226 HOTP Algorithm December 2005 2021 | 2022 | 2023 | Full Copyright Statement 2024 | 2025 | Copyright (C) The Internet Society (2005). 2026 | 2027 | This document is subject to the rights, licenses and restrictions 2028 | contained in BCP 78, and except as set forth therein, the authors 2029 | retain all their rights. 2030 | 2031 | This document and the information contained herein are provided on an 2032 | "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS 2033 | OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET 2034 | ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, 2035 | INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE 2036 | INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED 2037 | WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. 2038 | 2039 | Intellectual Property 2040 | 2041 | The IETF takes no position regarding the validity or scope of any 2042 | Intellectual Property Rights or other rights that might be claimed to 2043 | pertain to the implementation or use of the technology described in 2044 | this document or the extent to which any license under such rights 2045 | might or might not be available; nor does it represent that it has 2046 | made any independent effort to identify any such rights. Information 2047 | on the procedures with respect to rights in RFC documents can be 2048 | found in BCP 78 and BCP 79. 2049 | 2050 | Copies of IPR disclosures made to the IETF Secretariat and any 2051 | assurances of licenses to be made available, or the result of an 2052 | attempt made to obtain a general license or permission for the use of 2053 | such proprietary rights by implementers or users of this 2054 | specification can be obtained from the IETF on-line IPR repository at 2055 | http://www.ietf.org/ipr. 2056 | 2057 | The IETF invites any interested party to bring to its attention any 2058 | copyrights, patents or patent applications, or other proprietary 2059 | rights that may cover technology that may be required to implement 2060 | this standard. Please address the information to the IETF at ietf- 2061 | ipr@ietf.org. 2062 | 2063 | Acknowledgement 2064 | 2065 | Funding for the RFC Editor function is currently provided by the 2066 | Internet Society. 2067 | 2068 | 2069 | 2070 | 2071 | 2072 | 2073 | 2074 | M'Raihi, et al. Informational [Page 37] 2075 | 2076 | --------------------------------------------------------------------------------