rocra 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *~
19
+ \#*
20
+ .\#*
21
+
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "1.9.2"
5
+ - "1.8.7"
6
+ #- jruby-18mode # JRuby in 1.8 mode
7
+ #- jruby-19mode # JRuby in 1.9 mode
8
+ #- rbx-18mode
9
+ #- rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rocra.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 phil
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,59 @@
1
+ [![Build Status](https://travis-ci.org/branch14/rocra.png?branch=master)](https://travis-ci.org/branch14/rocra)
2
+
3
+ _ __ ___ ___ _ __ __ _
4
+ | '__/ _ \ / __| '__/ _` |
5
+ | | | (_) | (__| | | (_| |
6
+ |_| \___/ \___|_| \__,_|
7
+
8
+ # Welcome to rocra
9
+
10
+ rocra is an OCRA (RFC 6287) implementation in Ruby.
11
+
12
+ see http://tools.ietf.org/html/rfc6287
13
+
14
+ It is based on the implementations found here https://github.com/SURFnet/ocra-implementations
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ gem 'rocra'
21
+
22
+ And then execute:
23
+
24
+ $ bundle
25
+
26
+ Or install it yourself as:
27
+
28
+ $ gem install rocra
29
+
30
+ ## Usage
31
+
32
+ suite = 'OCRA-1:HOTP-SHA1-6:QN08' # string, mandatory
33
+ key = "3132333435363738393031323334353637383930" # hex, mandatory
34
+ counter = nil # hex, optional
35
+ question = '12345678'.to_i.to_s(16) # hex, mandatory
36
+ password = nil # hex, optional
37
+ session = nil # hex, optional
38
+ timestamp = nil # optional
39
+
40
+ Rocra.generate(suite, key, counter, question, password, session, timestamp)
41
+
42
+ ## Specs
43
+
44
+ Run
45
+
46
+ rspec
47
+
48
+ ## Contributing
49
+
50
+ 1. Fork it
51
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
52
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
53
+ 4. Push to the branch (`git push origin my-new-feature`)
54
+ 5. Create new Pull Request
55
+
56
+ ## Todo
57
+
58
+ - fix jruby issues
59
+ - make api more rubyesque
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,217 @@
1
+ require 'openssl'
2
+
3
+ require "rocra/version"
4
+
5
+ class Rocra
6
+
7
+ class << self
8
+
9
+ # This method generates an OCRA HOTP value for the given set of
10
+ # parameters.
11
+ #
12
+ # @param ocraSuite the OCRA Suite
13
+ # @param key the shared secret, HEX encoded
14
+ # @param counter the counter that changes
15
+ # on a per use basis,
16
+ # HEX encoded
17
+ # @param question the challenge question, HEX encoded
18
+ # @param password a password that can be used,
19
+ # HEX encoded
20
+ # @param sessionInformation
21
+ # Static information that identifies the
22
+ # current session, Hex encoded
23
+ # @param timestamp a value that reflects a time
24
+ #
25
+ # @return A numeric String in base 10 that includes
26
+ # {@link truncationDigits} digits
27
+ def generate(ocra_suite, key, counter, question, password, session_information, timestamp)
28
+
29
+ code_digits = 0
30
+ crypto = ""
31
+ result = nil
32
+ ocra_suite_length = ocra_suite.length
33
+ counter_length = 0
34
+ question_length = 0
35
+ password_length = 0
36
+
37
+ session_information_length = 0
38
+ timestamp_length = 0
39
+
40
+ # How many digits should we return
41
+ components = ocra_suite.split(':')
42
+ cryptoFunction = components[1]
43
+ dataInput = components[2].downcase # lower here so we can do case insensitive comparisons
44
+
45
+ crypto = 'sha1' if cryptoFunction.downcase.include?('sha1')
46
+ crypto = 'sha256' if cryptoFunction.downcase.include?('sha256')
47
+ crypto = 'sha512' if cryptoFunction.downcase.include?('sha512')
48
+
49
+ code_digits = cryptoFunction.split('-').last
50
+
51
+ # The size of the byte array message to be encrypted
52
+
53
+ # Counter
54
+ if dataInput[0,1] == "c"
55
+ # Fix the length of the HEX string
56
+ counter_length=8
57
+ counter = counter.rjust(16, '0')
58
+ end
59
+
60
+ # Question
61
+ if dataInput[0,1] == "q" || dataInput.include?('-q')
62
+ question = question.ljust(256, '0')
63
+ question_length = 128
64
+ end
65
+
66
+ # Password
67
+ if dataInput.include?("psha1")
68
+ password = password.ljust(40, '0')
69
+ password_length = 20
70
+ end
71
+
72
+ if dataInput.include?("psha256")
73
+ password = password.ljust(64, '0')
74
+ password_length = 32
75
+ end
76
+
77
+ if dataInput.include?("psha512")
78
+ password = password.ljust(128, '0')
79
+ password_length = 64
80
+ end
81
+
82
+ # session_information
83
+ if dataInput.include?("s064")
84
+ session_information = session_information.ljust(128, '0')
85
+ session_information_length = 64
86
+ end
87
+
88
+ if dataInput.include?("s128")
89
+ session_information = session_information.ljust(256, '0')
90
+ session_information_length = 128
91
+ end
92
+
93
+ if dataInput.include?("s256")
94
+ session_information = session_information.ljust(512, '0')
95
+ session_information_length = 256
96
+ end
97
+
98
+ if dataInput.include?("s512")
99
+ session_information = session_information.ljust(128, '0')
100
+ session_information_length = 64
101
+ end
102
+
103
+ # TimeStamp
104
+ if dataInput[0,1] == "t" || dataInput.include?("-t")
105
+ timestamp = timestamp.rjust(16, '0')
106
+ timestamp_length = 8
107
+ end
108
+
109
+ # Put the bytes of "ocra_suite" parameters into the message
110
+ length = ocra_suite_length +
111
+ counter_length +
112
+ question_length +
113
+ password_length +
114
+ session_information_length +
115
+ timestamp_length + 1
116
+ msg = "\0" * length
117
+ msg[0, ocra_suite.length] = ocra_suite
118
+
119
+ # Delimiter
120
+ # msg[ocra_suite.length] = hex2str("0")
121
+
122
+ # Put the bytes of "Counter" to the message
123
+ # Input is HEX encoded
124
+ if counter_length > 0
125
+ pos = ocra_suite_length + 1
126
+ msg[pos, counter.length] = hex2str(counter)
127
+ end
128
+
129
+ # Put the bytes of "question" to the message
130
+ # Input is text encoded
131
+ if question_length > 0
132
+ pos = ocra_suite_length + 1 + counter_length
133
+ msg[pos, question.length] = hex2str(question)
134
+ end
135
+
136
+ # Put the bytes of "password" to the message
137
+ # Input is HEX encoded
138
+ if password_length > 0
139
+ pos = ocra_suite_length + 1 + counter_length + question_length
140
+ msg[pos, password.length] = hex2str(password)
141
+ end
142
+
143
+ # Put the bytes of "session_information" to the message
144
+ # Input is text encoded
145
+ if session_information_length > 0
146
+ pos = ocra_suite_length + 1 + counter_length + question_length + password_length
147
+ msg[pos, session_information.length] = hex2str(session_information)
148
+ end
149
+
150
+ # Put the bytes of "time" to the message
151
+ # Input is text value of minutes
152
+ if timestamp_length > 0
153
+ pos = ocra_suite_length + 1 + counter_length + question_length +
154
+ password_length + session_information_length
155
+ msg[pos, timestamp.length] = hex2str(timestamp)
156
+ end
157
+
158
+ byteKey = hex2str(key)
159
+ hash = hmac_sha1(crypto, byteKey, msg)
160
+ oath_truncate(hash, code_digits)
161
+
162
+ end
163
+
164
+ private
165
+
166
+ # Truncate a result to a certain length
167
+ #
168
+ # - hash is a hex string
169
+ def oath_truncate(hash, length = 6)
170
+
171
+ # Convert to array of decimals
172
+ hmac_result = hash.scan(/../).map { |e| e.hex }
173
+
174
+ # Find offset
175
+ offset = hmac_result.last & 0xf
176
+
177
+ v =
178
+ (hmac_result[offset + 0] & 0x7f) << 24 |
179
+ (hmac_result[offset + 1] & 0xff) << 16 |
180
+ (hmac_result[offset + 2] & 0xff) << 8 |
181
+ (hmac_result[offset + 3] & 0xff)
182
+
183
+ r = v % 10 ** length.to_i
184
+
185
+ "%0#{length}d" % r
186
+ end
187
+
188
+ # This method uses the hmac_hash function to provide the crypto
189
+ # algorithm.
190
+ # HMAC computes a Hashed Message Authentication Code with the
191
+ # crypto hash algorithm as a parameter.
192
+ #
193
+ # @param String crypto the crypto algorithm (sha1, sha256 or sha512)
194
+ # @param String keyBytes the bytes to use for the HMAC key
195
+ # @param String text the message or text to be authenticated.
196
+ #
197
+ def hmac_sha1(crypto, keyBytes, text)
198
+ digest = OpenSSL::Digest::Digest.new(crypto)
199
+ str2hex(OpenSSL::HMAC.digest(digest, keyBytes, text))
200
+ end
201
+
202
+ # This method converts HEX string to Byte[]
203
+ #
204
+ # @param String hex the HEX string
205
+ #
206
+ # @return String a string with raw bytes
207
+ def hex2str(hex)
208
+ [hex].pack('H*')
209
+ end
210
+
211
+ def str2hex(str)
212
+ str.unpack('H*').first
213
+ end
214
+
215
+ end
216
+
217
+ end
@@ -0,0 +1,3 @@
1
+ class Rocra
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rocra/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "rocra"
8
+ gem.version = Rocra::VERSION
9
+ gem.authors = ["Phil Hofmann"]
10
+ gem.email = ["phil@branch14.org"]
11
+ gem.description = %q{An OCRA (RFC 6287) implementation in Ruby}
12
+ gem.summary = %q{An OCRA (RFC 6287) implementation in Ruby}
13
+ gem.homepage = "https://github.com/branch14/rocra"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec'
22
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path('../../lib/rocra', __FILE__)
2
+
3
+ describe Rocra do
4
+
5
+ it 'should nicely translate binary strings into hex strings' do
6
+ Rocra.send(:str2hex, 'ab').should == '6162'
7
+ end
8
+
9
+ it 'should nicely translate hex strings into binary strings' do
10
+ Rocra.send(:hex2str, '6162').should == 'ab'
11
+ end
12
+
13
+ # examples from http://en.wikipedia.org/wiki/Hash-based_message_authentication_code
14
+ it 'should nicely calculate hmac_sha1' do
15
+ Rocra.send(:hmac_sha1, 'sha1', '', '').should == 'fbdb1d1b18aa6c08324b7d64b71fb76370690e1d'
16
+ Rocra.send(:hmac_sha1, 'sha1', "key", "The quick brown fox jumps over the lazy dog").should ==
17
+ 'de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9'
18
+ end
19
+
20
+ KEY_20 = "3132333435363738393031323334353637383930"
21
+ KEY_32 = "3132333435363738393031323334353637383930313233343536373839303132"
22
+ KEY_64 = "3132333435363738393031323334353637383930313233343536373839303132"+
23
+ "3334353637383930313233343536373839303132333435363738393031323334"
24
+ PIN_1234_HASH = "7110eda4d09e062aa5e4a390b0a572ac0d2c0220"
25
+
26
+ it 'should work for OCRA-1:HOTP-SHA1-6:QN08' do
27
+ suite = "OCRA-1:HOTP-SHA1-6:QN08"
28
+ Rocra.generate(suite, KEY_20, nil, '00000000'.to_i.to_s(16), nil, nil, nil).should == '237653'
29
+ Rocra.generate(suite, KEY_20, nil, '11111111'.to_i.to_s(16), nil, nil, nil).should == '243178'
30
+ Rocra.generate(suite, KEY_20, nil, '22222222'.to_i.to_s(16), nil, nil, nil).should == '653583'
31
+ Rocra.generate(suite, KEY_20, nil, '33333333'.to_i.to_s(16), nil, nil, nil).should == '740991'
32
+ Rocra.generate(suite, KEY_20, nil, '44444444'.to_i.to_s(16), nil, nil, nil).should == '608993'
33
+ Rocra.generate(suite, KEY_20, nil, '55555555'.to_i.to_s(16), nil, nil, nil).should == '388898'
34
+ Rocra.generate(suite, KEY_20, nil, '66666666'.to_i.to_s(16), nil, nil, nil).should == '816933'
35
+ Rocra.generate(suite, KEY_20, nil, '77777777'.to_i.to_s(16), nil, nil, nil).should == '224598'
36
+ Rocra.generate(suite, KEY_20, nil, '88888888'.to_i.to_s(16), nil, nil, nil).should == '750600'
37
+ Rocra.generate(suite, KEY_20, nil, '99999999'.to_i.to_s(16), nil, nil, nil).should == '294470'
38
+ end
39
+
40
+ it 'should work with counter' do
41
+ suite = "OCRA-1:HOTP-SHA256-8:C-QN08-PSHA1"
42
+ Rocra.generate(suite, KEY_32, "0", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "65347737"
43
+ Rocra.generate(suite, KEY_32, "1", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "86775851"
44
+ Rocra.generate(suite, KEY_32, "2", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "78192410"
45
+ Rocra.generate(suite, KEY_32, "3", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "71565254"
46
+ Rocra.generate(suite, KEY_32, "4", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "10104329"
47
+ Rocra.generate(suite, KEY_32, "5", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "65983500"
48
+ Rocra.generate(suite, KEY_32, "6", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "70069104"
49
+ Rocra.generate(suite, KEY_32, "7", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "91771096"
50
+ Rocra.generate(suite, KEY_32, "8", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "75011558"
51
+ Rocra.generate(suite, KEY_32, "9", "12345678".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "08522129"
52
+ end
53
+
54
+ it 'should work for OCRA-1:HOTP-SHA256-8:QN08-PSHA1' do
55
+ suite = "OCRA-1:HOTP-SHA256-8:QN08-PSHA1"
56
+ Rocra.generate(suite, KEY_32, nil, "00000000".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "83238735"
57
+ Rocra.generate(suite, KEY_32, nil, "11111111".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "01501458"
58
+ Rocra.generate(suite, KEY_32, nil, "22222222".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "17957585"
59
+ Rocra.generate(suite, KEY_32, nil, "33333333".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "86776967"
60
+ Rocra.generate(suite, KEY_32, nil, "44444444".to_i.to_s(16), PIN_1234_HASH, nil, nil).should == "86807031"
61
+ end
62
+
63
+ it 'should work for OCRA-1:HOTP-SHA512-8:C-QN08' do
64
+ suite = "OCRA-1:HOTP-SHA512-8:C-QN08"
65
+ Rocra.generate(suite, KEY_64, "00000", "00000000".to_i.to_s(16), nil, nil, nil).should == "07016083"
66
+ Rocra.generate(suite, KEY_64, "00001", "11111111".to_i.to_s(16), nil, nil, nil).should == "63947962"
67
+ Rocra.generate(suite, KEY_64, "00002", "22222222".to_i.to_s(16), nil, nil, nil).should == "70123924"
68
+ Rocra.generate(suite, KEY_64, "00003", "33333333".to_i.to_s(16), nil, nil, nil).should == "25341727"
69
+ Rocra.generate(suite, KEY_64, "00004", "44444444".to_i.to_s(16), nil, nil, nil).should == "33203315"
70
+ Rocra.generate(suite, KEY_64, "00005", "55555555".to_i.to_s(16), nil, nil, nil).should == "34205738"
71
+ Rocra.generate(suite, KEY_64, "00006", "66666666".to_i.to_s(16), nil, nil, nil).should == "44343969"
72
+ Rocra.generate(suite, KEY_64, "00007", "77777777".to_i.to_s(16), nil, nil, nil).should == "51946085"
73
+ Rocra.generate(suite, KEY_64, "00008", "88888888".to_i.to_s(16), nil, nil, nil).should == "20403879"
74
+ Rocra.generate(suite, KEY_64, "00009", "99999999".to_i.to_s(16), nil, nil, nil).should == "31409299"
75
+ end
76
+
77
+ it 'should work for OCRA-1:HOTP-SHA512-8:QN08-T1M' do
78
+ suite = "OCRA-1:HOTP-SHA512-8:QN08-T1M"
79
+ Rocra.generate(suite, KEY_64, nil, "00000000".to_i.to_s(16), nil, nil, "132d0b6").should == "95209754"
80
+ Rocra.generate(suite, KEY_64, nil, "11111111".to_i.to_s(16), nil, nil, "132d0b6").should == "55907591"
81
+ Rocra.generate(suite, KEY_64, nil, "22222222".to_i.to_s(16), nil, nil, "132d0b6").should == "22048402"
82
+ Rocra.generate(suite, KEY_64, nil, "33333333".to_i.to_s(16), nil, nil, "132d0b6").should == "24218844"
83
+ Rocra.generate(suite, KEY_64, nil, "44444444".to_i.to_s(16), nil, nil, "132d0b6").should == "36209546"
84
+ end
85
+
86
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rocra
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Phil Hofmann
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: &82726890 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *82726890
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &82726670 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *82726670
36
+ description: An OCRA (RFC 6287) implementation in Ruby
37
+ email:
38
+ - phil@branch14.org
39
+ executables: []
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - .gitignore
44
+ - .travis.yml
45
+ - Gemfile
46
+ - LICENSE.txt
47
+ - README.md
48
+ - Rakefile
49
+ - lib/rocra.rb
50
+ - lib/rocra/version.rb
51
+ - rocra.gemspec
52
+ - spec/rocra_spec.rb
53
+ homepage: https://github.com/branch14/rocra
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.15
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: An OCRA (RFC 6287) implementation in Ruby
77
+ test_files:
78
+ - spec/rocra_spec.rb