HOTP 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,76 @@
1
+ Authors: Michael DeCandia <mdecandia@vonage.com> and Stephen Becker <sbecker@vonage.com>
2
+
3
+ License: BSD License
4
+
5
+ Copyright (c) 2007, Vonage Holdings
6
+
7
+ All rights reserved.
8
+
9
+ Redistribution and use in source and binary forms, with or without
10
+ modification, are permitted provided that the following conditions are met:
11
+
12
+ * Redistributions of source code must retain the above copyright
13
+ notice, this list of conditions and the following disclaimer.
14
+ * Redistributions in binary form must reproduce the above copyright
15
+ notice, this list of conditions and the following disclaimer in the
16
+ documentation and/or other materials provided with the distribution.
17
+ * Neither the name of Vonage Holdings nor the names of its
18
+ contributors may be used to endorse or promote products derived from this
19
+ software without specific prior written permission.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
+ POSSIBILITY OF SUCH DAMAGE.
32
+
33
+ = HOTP -- HMAC-Based One-Time Password Algorithm
34
+
35
+ * HOTP: Implements RFC 4226 using Ruby. (http://www.ietf.org/rfc/rfc4226.txt) HOTP is an HMAC-SHA1 based algorithm
36
+
37
+ == Download
38
+
39
+ The latest version of hotp can be found at
40
+
41
+ * http://rubyforge.org/hotp/
42
+
43
+ == Installation
44
+
45
+ === Normal Installation
46
+
47
+ Setup based installation is currently unsupported
48
+
49
+ === GEM Installation
50
+
51
+ % gem install hotp
52
+
53
+ == Usage, ...
54
+
55
+ === HOTP
56
+
57
+ The HOTP object can be used to calculate the HOTP value for any secret and count. The number of digits in the HOTP value is determined by an optional parameter. The default HOTP value is 6 digits long.
58
+
59
+ Creating an HOTP instance
60
+
61
+ h = HOTP.new()
62
+ h.secret="12345678901234567890" # 20 byte key
63
+ h.count=0 # Where to start calculating
64
+ h.digits=6 # How many digits to return (length of the HOTP value)
65
+ puts(h.hotp) # => "755224"
66
+ h.count=1 # Change the count
67
+ puts(h.update) # => "287082"
68
+
69
+
70
+ Using HOTP as a class method
71
+
72
+ secret = "12345678901234567890"
73
+ count = 0
74
+ puts(HOTP::hotp(secret,count)) # => "755224"
75
+
76
+
@@ -0,0 +1,48 @@
1
+ #!/usr/local/bin/ruby
2
+ =begin rdoc
3
+ Copyright (c) 2007, Vonage Holdings
4
+
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright
11
+ notice, this list of conditions and the following disclaimer.
12
+ * Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+ * Neither the name of Vonage Holdings nor the names of its
16
+ contributors may be used to endorse or promote products derived from this
17
+ software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ POSSIBILITY OF SUCH DAMAGE.
30
+
31
+ Authors: Michael DeCandia <mdecandia@vonage.com> and Stephen Becker <sbecker@vonage.com>
32
+ =end
33
+
34
+ require 'rubygems'
35
+ require 'HOTP'
36
+
37
+ secret = "12345678901234567890"
38
+ count = 30
39
+
40
+ h = HOTP.new(secret, count)
41
+ puts("[#{h.hotp}]")
42
+
43
+ # Display the HOTP values for the secret starting at 0 up to 100
44
+ (0..100).each{ |c|
45
+ h.count=c
46
+ puts("[#{h.update}]")
47
+ }
48
+
@@ -0,0 +1,163 @@
1
+ =begin rdoc
2
+ Copyright (c) 2007, Vonage Holdings
3
+
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright
10
+ notice, this list of conditions and the following disclaimer.
11
+ * Redistributions in binary form must reproduce the above copyright
12
+ notice, this list of conditions and the following disclaimer in the
13
+ documentation and/or other materials provided with the distribution.
14
+ * Neither the name of Vonage Holdings nor the names of its
15
+ contributors may be used to endorse or promote products derived from this
16
+ software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ Authors: Michael DeCandia <mdecandia@vonage.com> and Stephen Becker <sbecker@vonage.com>
31
+
32
+ === HOTP
33
+
34
+ The HOTP object can be used to calculate the HOTP value for any secret and count. The number of digits in the HOTP value is determined by an optional parameter. The default HOTP value is 6 digits long.
35
+
36
+ Creating an HOTP instance
37
+
38
+ h = HOTP.new()
39
+ h.secret="12345678901234567890" # 20 byte key
40
+ h.count=0 # Where to start calculating
41
+ h.digits=6 # How many digits to return (length of the HOTP value)
42
+ puts(h.hotp) # => "755224"
43
+ h.count=1 # Change the count
44
+ puts(h.update) # => "287082"
45
+
46
+
47
+ Using HOTP as a class method
48
+
49
+ secret = "12345678901234567890"
50
+ count = 0
51
+ puts(HOTP::hotp(secret,count)) # => "755224"
52
+
53
+ =end
54
+ require 'openssl'
55
+ require 'logger'
56
+
57
+ #This implements HOTP: An HMAC-Based One-Time Password Algorithm as described in RFC 4226 (http://www.ietf.org/rfc/rfc4226.txt)
58
+ class HOTP
59
+ #The secret key in ascii (K)
60
+ attr_accessor :secret
61
+ #The count used to start the calculation (C)
62
+ attr_accessor :count
63
+ #The length of the HOTP value (Digit) (Defaults to 6)
64
+ attr_accessor :digits
65
+ #The default logger to use when reporting an error. (Defaults to STDERR)
66
+ @@logger = Logger.new(STDERR)
67
+ #Creates an HOTP object
68
+ #* :secret is your secret key
69
+ #* :count is where to start your calculation
70
+ #* :digits is the length of the HOTP value
71
+ #
72
+ #Creating an HOTP instance
73
+ #
74
+ # h = HOTP.new()
75
+ # h.secret="12345678901234567890" # 20 byte key
76
+ # h.count=0 # Where to start calculating
77
+ # h.digits=6 # How many digits to return (length of the HOTP value)
78
+ # puts(h.hotp) # => "755224"
79
+ # h.count=1 # Change the count
80
+ # puts(h.update) # => "287082"
81
+ #
82
+ #
83
+ #Using HOTP as a class method
84
+ #
85
+ # secret = "12345678901234567890"
86
+ # count = 0
87
+ # puts(HOTP::hotp(secret,count)) # => "755224"
88
+ #
89
+ def initialize(secret=nil,count=nil,digits=6)
90
+ begin
91
+ @secret = secret
92
+ @count = count
93
+ @digits = digits
94
+ rescue Exception => e
95
+ @@logger.fatal{"Unable to initialize HOTP properly. Reason: #{e}"}
96
+ end
97
+ end
98
+ #Returns the HOTP value as a string
99
+ def update
100
+ begin
101
+ return calculate_hotp(@secret, @count, @digits)
102
+ rescue Exception => e
103
+ @@logger.fatal{"Unable to update. Reason #{e}"}
104
+ end
105
+ end
106
+ alias_method :hotp, :update
107
+ #Returns the HOTP value as a string
108
+ def calculate_hotp(secret,count,digits=6)
109
+ return HOTP::hotp(secret,count,digits)
110
+ end
111
+ #Set the Logger object. By default this logs to STDERR
112
+ def self.logger=(logger)
113
+ @@logger = logger
114
+ end
115
+ #Class method that returns the HOTP value as a string
116
+ def self.hotp(secret,count,digits=6)
117
+ begin
118
+ # The secret is plain text
119
+ # i.e "1234567891234567890"
120
+ # The count needs to be in hex so we need to convert it into a padded hex string
121
+ # i.e. 10 = "\x00\x00\x00\x00\x00\x00\x00\x0a"
122
+ # The digits are the number of digits to use when calculating the HOTP. (defaults to 6)
123
+ sha1_hash = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new("SHA1"), secret, string_to_hex_value(count,8))
124
+ #the last hex value is the offset
125
+ offset = sha1_hash[-1].chr.hex
126
+ #get the string in the offset range
127
+ dbc1 = sha1_hash[(offset*2)...((offset*2)+8)]
128
+ #31bits
129
+ dbc2 = dbc1.hex & "7fffffff".hex
130
+ hotp = dbc2%(10**digits.to_i)
131
+ hotp = sprintf("%0#{digits}d", hotp)
132
+ return hotp
133
+ rescue Exception => e
134
+ @@logger.fatal{"Unable to calculate the HOTP value. Reason: #{e}"}
135
+ end
136
+ end
137
+
138
+ private
139
+ #Private class method that converts a string to a padded hex value
140
+ def self.string_to_hex_value(count,padding=nil,pad_char="0")
141
+ begin
142
+ return_string = ""
143
+ # Change count to an integer then base 16 convert to a string
144
+ # i.e. 10.to_s(16) = a
145
+ hex_count = count.to_i.to_s(16) #string
146
+
147
+ # Pad the resulting string with the pad_charater ("0" by default)
148
+ # i.e. Takes "a" and makes it "000000a"
149
+ # We multiply the padding by two since we evaluate the hex as two characters
150
+ # i.e. "a" in hex is "0a"
151
+ ((padding.to_i*2) - hex_count.size).times{hex_count.insert(0,pad_char)} if padding
152
+ hex_count = "0"+hex_count if hex_count.size%2==1
153
+ temp_char = "0"
154
+ hex_count.scan(/../).each { |hexs|
155
+ temp_char[0]= hexs.hex
156
+ return_string << temp_char
157
+ }
158
+ return return_string
159
+ rescue Exception => e
160
+ @@logger.fatal{"Unable to convert string to hex. Reason #{e}"}
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,163 @@
1
+ #!/usr/bin/env ruby
2
+ require 'runit/testcase'
3
+ require 'runit/cui/testrunner'
4
+ require 'HOTP'
5
+ class TestHOPT < RUNIT::TestCase
6
+ #example from http://www.rfc-editor.org/rfc/rfc4226.txt
7
+ #We would love more examples
8
+ def test_instance_no_params
9
+ h = HOTP.new
10
+ h.secret = "12345678901234567890"
11
+ h.count = "0"
12
+ h.digits = 6
13
+ assert_equal(h.update,"755224")
14
+ h.count = "30"
15
+ #make sure you get back 6 digits in the string.
16
+ assert_equal(h.update,"026920")
17
+ end
18
+ def test_instance_no_digits_params
19
+ h = HOTP.new("12345678901234567890","0")
20
+ assert_equal(h.update,"755224")
21
+ h.count = "30"
22
+ #make sure you get back 6 digits in the string.
23
+ assert_equal(h.update,"026920")
24
+ end
25
+ def test_instance_6_digits
26
+ h = HOTP.new("12345678901234567890","0",6)
27
+ assert_equal(h.update,"755224")
28
+ h.count = "30"
29
+ #make sure you get back 6 digits in the string.
30
+ assert_equal(h.update,"026920")
31
+ end
32
+ def test_instance_8_digits
33
+ h = HOTP.new("12345678901234567890","0",8)
34
+ assert_equal(h.update,"84755224")
35
+ h.count = "1"
36
+ assert_equal(h.update,"94287082")
37
+ end
38
+ def test_instance_8_digits_string
39
+ h = HOTP.new("12345678901234567890","0","8")
40
+ assert_equal(h.update,"84755224")
41
+ h.count = "1"
42
+ assert_equal(h.update,"94287082")
43
+ end
44
+ def test_class
45
+ #you can call the class with key,count and digits
46
+ assert_equal(HOTP::hotp("12345678901234567890","0"),"755224")
47
+ assert_equal(HOTP::hotp("12345678901234567890","0",6),"755224")
48
+ #make sure you get back 6 digits in the string.
49
+ assert_equal(HOTP::hotp("12345678901234567890","30",6),"026920")
50
+ end
51
+ def test_class_100_loop
52
+ #first 100 values. as calclated from Perl's Authen::HOTP 0.02
53
+ results = [
54
+ "755224",
55
+ "287082",
56
+ "359152",
57
+ "969429",
58
+ "338314",
59
+ "254676",
60
+ "287922",
61
+ "162583",
62
+ "399871",
63
+ "520489",
64
+ "403154",
65
+ "481090",
66
+ "868912",
67
+ "736127",
68
+ "229903",
69
+ "436521",
70
+ "186581",
71
+ "447589",
72
+ "903435",
73
+ "578337",
74
+ "328281",
75
+ "191635",
76
+ "184416",
77
+ "574561",
78
+ "797908",
79
+ "396619",
80
+ "122382",
81
+ "939082",
82
+ "908316",
83
+ "316591",
84
+ "026920",
85
+ "523596",
86
+ "370250",
87
+ "841346",
88
+ "749439",
89
+ "037211",
90
+ "003784",
91
+ "520231",
92
+ "521952",
93
+ "619416",
94
+ "268376",
95
+ "471723",
96
+ "435478",
97
+ "303194",
98
+ "000152",
99
+ "287422",
100
+ "318298",
101
+ "098238",
102
+ "039329",
103
+ "710717",
104
+ "528155",
105
+ "980838",
106
+ "249088",
107
+ "354406",
108
+ "399156",
109
+ "478026",
110
+ "294892",
111
+ "941415",
112
+ "964363",
113
+ "083773",
114
+ "864257",
115
+ "719632",
116
+ "005080",
117
+ "925505",
118
+ "317632",
119
+ "088627",
120
+ "024418",
121
+ "954526",
122
+ "036679",
123
+ "864060",
124
+ "569881",
125
+ "029459",
126
+ "486963",
127
+ "559613",
128
+ "202372",
129
+ "705686",
130
+ "272974",
131
+ "379493",
132
+ "839877",
133
+ "393669",
134
+ "863623",
135
+ "198167",
136
+ "935444",
137
+ "108405",
138
+ "305499",
139
+ "563707",
140
+ "447439",
141
+ "332099",
142
+ "087812",
143
+ "032830",
144
+ "811649",
145
+ "190372",
146
+ "458549",
147
+ "202468",
148
+ "722946",
149
+ "047817",
150
+ "229689",
151
+ "430056",
152
+ "289357",
153
+ "516516" ]
154
+ (0...100).each do |c|
155
+ assert_equal(HOTP::hotp("12345678901234567890",c,6), results[c])
156
+ end
157
+ end
158
+ end
159
+ if $0 == __FILE__
160
+ suite = RUNIT::TestSuite.new
161
+ suite.add_test(TestHOPT.suite)
162
+ RUNIT::CUI::TestRunner.run(suite)
163
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.2
3
+ specification_version: 1
4
+ name: HOTP
5
+ version: !ruby/object:Gem::Version
6
+ version: 1.0.0
7
+ date: 2007-10-24 00:00:00 -04:00
8
+ summary: "This implements HOTP: An HMAC-Based One-Time Password Algorithm as described in RFC 4226"
9
+ require_paths:
10
+ - lib
11
+ email:
12
+ - mdecandia@vonage.com
13
+ - sbecker@vonage.com
14
+ homepage: http://www.vonage.com/
15
+ rubyforge_project:
16
+ description:
17
+ autorequire: HOTP
18
+ default_executable:
19
+ bindir: bin
20
+ has_rdoc: true
21
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
22
+ requirements:
23
+ - - ">"
24
+ - !ruby/object:Gem::Version
25
+ version: 0.0.0
26
+ version:
27
+ platform: ruby
28
+ signing_key:
29
+ cert_chain:
30
+ post_install_message:
31
+ authors:
32
+ - Michael DeCandia
33
+ - Stephen Becker
34
+ files:
35
+ - examples/HOTP
36
+ - examples/HOTP/example1.rb
37
+ - lib/HOTP.rb
38
+ - tests/HOTP
39
+ - tests/HOTP/tests.rb
40
+ - README
41
+ test_files:
42
+ - tests/HOTP
43
+ - tests/HOTP/tests.rb
44
+ rdoc_options: []
45
+
46
+ extra_rdoc_files:
47
+ - README
48
+ executables: []
49
+
50
+ extensions: []
51
+
52
+ requirements: []
53
+
54
+ dependencies: []
55
+