rotp 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e81c60dc4a476ab617e914fe50d3739290e9a110
4
- data.tar.gz: 3f99b3ec451c23b615ff7a58acbcf5a105f5f5c3
3
+ metadata.gz: 6966922a92bffb8bf74497e36d3f2e42e63f47a5
4
+ data.tar.gz: 0dbef9d1ed476e382b243d232240458d396b3593
5
5
  SHA512:
6
- metadata.gz: 0f2ad34ca1702d72edb5e16dd715d58f6980c4412577cc40cd5337f6f3cb21958345883e4e67573eaf540364fecf4d9e73a720c4a79042327c2ac6d7e8f6bbbd
7
- data.tar.gz: ad3ddffe067a7716d6cfaadcc7c9e8ac256831efc7d6214d7422269d4805eed427161af4f0c1de773ebe386d93355d495551aba03498a61e9277d3377ac1cdc1
6
+ metadata.gz: 3af99a2dcadd3591d235ecb8f1d0752ceadaa0b8649c1851163a7d0ce7e88bf5a564d1d32b6f3a2e2a3b290e36fd378b39e1d0a3a5225ba7ead905e3c207806f
7
+ data.tar.gz: c9504564ca0ec36d3efac011b9079d33d5038bf917ff4f5d6700556a71956fab49710304551e2d2d0a23f6c6a91dcdb7a296dd26468f6a78de8dc0584a0c35da
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  .yardoc
4
4
  pkg/*
5
+ coverage
@@ -1,5 +1,9 @@
1
1
  ### Changelog
2
2
 
3
+ #### 3.3.0
4
+
5
+ - Add digest algorithm parameter for non SHA1 digests - #62 from @btalbot
6
+
3
7
  #### 3.2.0
4
8
 
5
9
  - Add 'verify_with_drift_and_prior' to prevent prior token use - #58 from @jlfaber
@@ -1,75 +1,41 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rotp (2.1.2)
4
+ rotp (3.2.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
- celluloid (0.16.0)
10
- timers (~> 4.0.0)
11
- coderay (1.1.0)
12
9
  diff-lcs (1.2.5)
13
- ffi (1.9.6)
14
- formatador (0.2.5)
15
- guard (2.11.1)
16
- formatador (>= 0.2.4)
17
- listen (~> 2.7)
18
- lumberjack (~> 1.0)
19
- nenv (~> 0.1)
20
- notiffany (~> 0.0)
21
- pry (>= 0.9.12)
22
- shellany (~> 0.0)
23
- thor (>= 0.18.1)
24
- guard-compat (1.2.0)
25
- guard-rspec (4.5.0)
26
- guard (~> 2.1)
27
- guard-compat (~> 1.1)
28
- rspec (>= 2.99.0, < 4.0)
29
- hitimes (1.2.2)
30
- listen (2.8.5)
31
- celluloid (>= 0.15.2)
32
- rb-fsevent (>= 0.9.3)
33
- rb-inotify (>= 0.9)
34
- lumberjack (1.0.9)
35
- method_source (0.8.2)
36
- nenv (0.1.1)
37
- notiffany (0.0.3)
38
- nenv (~> 0.1)
39
- shellany (~> 0.0)
40
- pry (0.10.1)
41
- coderay (~> 1.1.0)
42
- method_source (~> 0.8.1)
43
- slop (~> 3.4)
44
- rake (10.4.2)
45
- rb-fsevent (0.9.4)
46
- rb-inotify (0.9.5)
47
- ffi (>= 0.5.0)
48
- rspec (3.1.0)
49
- rspec-core (~> 3.1.0)
50
- rspec-expectations (~> 3.1.0)
51
- rspec-mocks (~> 3.1.0)
52
- rspec-core (3.1.7)
53
- rspec-support (~> 3.1.0)
54
- rspec-expectations (3.1.2)
10
+ docile (1.1.5)
11
+ json (1.8.3)
12
+ rake (10.5.0)
13
+ rspec (3.5.0)
14
+ rspec-core (~> 3.5.0)
15
+ rspec-expectations (~> 3.5.0)
16
+ rspec-mocks (~> 3.5.0)
17
+ rspec-core (3.5.2)
18
+ rspec-support (~> 3.5.0)
19
+ rspec-expectations (3.5.0)
55
20
  diff-lcs (>= 1.2.0, < 2.0)
56
- rspec-support (~> 3.1.0)
57
- rspec-mocks (3.1.3)
58
- rspec-support (~> 3.1.0)
59
- rspec-support (3.1.2)
60
- shellany (0.0.1)
61
- slop (3.6.0)
62
- thor (0.19.1)
63
- timecop (0.7.1)
64
- timers (4.0.1)
65
- hitimes
21
+ rspec-support (~> 3.5.0)
22
+ rspec-mocks (3.5.0)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.5.0)
25
+ rspec-support (3.5.0)
26
+ simplecov (0.12.0)
27
+ docile (~> 1.1.0)
28
+ json (>= 1.8, < 3)
29
+ simplecov-html (~> 0.10.0)
30
+ simplecov-html (0.10.0)
31
+ timecop (0.8.1)
66
32
 
67
33
  PLATFORMS
68
34
  ruby
69
35
 
70
36
  DEPENDENCIES
71
- guard-rspec (~> 4.5)
72
- rake (~> 10.4)
37
+ rake (~> 10.5)
73
38
  rotp!
74
- rspec (~> 3.1)
75
- timecop (~> 0.7)
39
+ rspec (~> 3.5)
40
+ simplecov (~> 0.12)
41
+ timecop (~> 0.8)
data/README.md CHANGED
@@ -55,6 +55,39 @@ hotp.verify("316439", 1401) # => true
55
55
  hotp.verify("316439", 1402) # => false
56
56
  ```
57
57
 
58
+ ### Verifying a Time based OTP with drift
59
+
60
+ Some users devices may be slightly behind or ahead of the actual time. ROTP allows users to verify
61
+ an OTP code with an specific amount of 'drift'
62
+
63
+ ```ruby
64
+ totp = ROTP::TOTP.new("base32secret3232")
65
+ totp.now # => "492039"
66
+
67
+ # OTP verified for current time with 120 seconds allowed drift
68
+ totp.verify_with_drift("492039", 60, Time.now - 30) # => true
69
+ totp.verify_with_drift("492039", 60, Time.now - 90) # => false
70
+ ```
71
+
72
+ ### Preventing reuse of Time based OTP's
73
+
74
+ In order to prevent reuse of time based tokens within the interval window (default 30 seconds)
75
+ it is necessary to store the last time an OTP was used. The following is an example of this in action:
76
+
77
+ ```ruby
78
+ User.find(someUserID)
79
+ totp = ROTP::TOTP.new(user.otp_secret)
80
+ totp.now # => "492039"
81
+
82
+ user.last_otp_at # => 1472145530
83
+
84
+ # Verify the OTP
85
+ verified_at_timestamp = totp.verify_with_drift_and_prior("492039", 0, user.last_otp_at) #=> 1472145760
86
+ # Store this on the user's account
87
+ user.update(last_otp_at: verified_at_timestamp)
88
+ verified_at_timestamp = totp.verify_with_drift_and_prior("492039", 0, user.last_otp_at) #=> false
89
+ ```
90
+
58
91
  ### Generating a Base32 Secret key
59
92
 
60
93
  ```ruby
@@ -87,6 +87,7 @@ module ROTP
87
87
  period: interval == 30 ? nil : interval,
88
88
  issuer: issuer,
89
89
  digits: digits == DEFAULT_DIGITS ? nil : digits,
90
+ algorithm: digest.upcase == 'SHA1' ? nil : digest.upcase,
90
91
  }
91
92
  encode_params("otpauth://totp/#{issuer_string}#{URI.encode(name)}", params)
92
93
  end
@@ -1,3 +1,3 @@
1
1
  module ROTP
2
- VERSION = "3.2.0"
2
+ VERSION = "3.3.0"
3
3
  end
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
21
 
22
- s.add_development_dependency 'guard-rspec', '~> 4.5'
23
- s.add_development_dependency 'rake', '~> 10.4'
24
- s.add_development_dependency 'rspec', '~> 3.1'
25
- s.add_development_dependency 'timecop', '~> 0.7'
22
+ s.add_development_dependency 'rake', '~> 10.5'
23
+ s.add_development_dependency 'rspec', '~> 3.5'
24
+ s.add_development_dependency 'timecop', '~> 0.8'
25
+ s.add_development_dependency 'simplecov', '~> 0.12'
26
26
  end
@@ -50,7 +50,7 @@ RSpec.describe ROTP::HOTP do
50
50
  let(:token) { 161024 }
51
51
 
52
52
  it 'raises an error' do
53
- expect { verification }.to raise_error
53
+ expect { verification }.to raise_error(ArgumentError)
54
54
  end
55
55
  end
56
56
 
@@ -41,7 +41,7 @@ RSpec.describe ROTP::TOTP do
41
41
  let(:token) { 68212 }
42
42
 
43
43
  it 'raises an error' do
44
- expect { verification }.to raise_error
44
+ expect { verification }.to raise_error(ArgumentError)
45
45
  end
46
46
  end
47
47
 
@@ -142,37 +142,29 @@ RSpec.describe ROTP::TOTP do
142
142
  expect(params['period'].first).to eq '60'
143
143
  end
144
144
  end
145
- end
146
145
 
147
- describe '#verify_with_drift' do
148
- let(:verification) { totp.verify_with_drift token, drift, now }
149
- let(:drift) { 0 }
150
-
151
- context 'numeric token' do
152
- let(:token) { 68212 }
146
+ context 'with custom digest' do
147
+ let(:totp) { ROTP::TOTP.new 'JBSWY3DPEHPK3PXP', digest: 'sha256' }
153
148
 
154
- it 'raises an error' do
155
- # In the "old" specs this was not tested due to a typo. What is the expected behavior here?
156
- expect { verification }.to raise_error
149
+ it 'has the correct format' do
150
+ expect(uri).to match %r{\Aotpauth:\/\/totp.+}
157
151
  end
158
- end
159
152
 
160
- context 'unpadded string token' do
161
- let(:token) { '68212' }
153
+ it 'includes the secret as parameter' do
154
+ expect(params['secret'].first).to eq 'JBSWY3DPEHPK3PXP'
155
+ end
162
156
 
163
- it 'is false' do
164
- # Not sure whether this should be tested. It didn't exist in the "old" specs
165
- expect(verification).to be_falsey
157
+ it 'includes the digest as algorithm parameter' do
158
+ expect(params['algorithm'].first).to eq 'SHA256'
166
159
  end
167
160
  end
168
161
 
169
- context 'correctly padded string token' do
170
- let(:token) { '068212' }
162
+ end
163
+
164
+ describe '#verify_with_drift' do
165
+ let(:verification) { totp.verify_with_drift token, drift, now }
166
+ let(:drift) { 0 }
171
167
 
172
- it 'is true' do
173
- expect(verification).to be_truthy
174
- end
175
- end
176
168
 
177
169
  context 'slightly old number' do
178
170
  let(:token) { totp.at now - 30 }
@@ -227,88 +219,15 @@ RSpec.describe ROTP::TOTP do
227
219
  let(:drift) { 0 }
228
220
  let(:prior) { nil }
229
221
 
230
- context 'numeric token' do
231
- let(:token) { 68212 }
232
-
233
- it 'raises an error' do
234
- # In the "old" specs this was not tested due to a typo. What is the expected behavior here?
235
- expect { verification }.to raise_error
236
- end
237
- end
238
-
239
- context 'unpadded string token' do
240
- let(:token) { '68212' }
241
-
242
- it 'is false' do
243
- # Not sure whether this should be tested. It didn't exist in the "old" specs
244
- expect(verification).to be_falsey
245
- end
246
- end
247
-
248
- context 'correctly padded string token' do
249
- let(:token) { '068212' }
250
-
251
- it 'is true' do
252
- expect(verification).to be_truthy
253
- end
254
- end
255
-
256
- context 'slightly old number' do
257
- let(:token) { totp.at now - 30 }
258
- let(:drift) { 60 }
259
-
260
- it 'is true' do
261
- expect(verification).to be_truthy
262
- end
263
- end
264
-
265
- context 'slightly new number' do
266
- let(:token) { totp.at now + 60 }
267
- let(:drift) { 60 }
268
-
269
- it 'is true' do
270
- expect(verification).to be_truthy
271
- end
272
- end
273
-
274
- context 'outside of drift range' do
275
- let(:token) { totp.at now - 60 }
276
- let(:drift) { 30 }
277
-
278
- it 'is false' do
279
- expect(verification).to be_falsey
280
- end
281
- end
282
-
283
- context 'drift is not multiple of TOTP interval' do
284
- context 'slightly old number' do
285
- let(:token) { totp.at now - 45 }
286
- let(:drift) { 45 }
287
-
288
- it 'is true' do
289
- expect(verification).to be_truthy
290
- end
291
- end
292
-
293
- context 'slightly new number' do
294
- let(:token) { totp.at now + 40 }
295
- let(:drift) { 40 }
296
-
297
- it 'is true' do
298
- expect(verification).to be_truthy
299
- end
300
- end
301
- end
302
-
303
222
  context 'with a prior verify' do
304
223
  let(:prior) { totp.verify_with_drift_and_prior '068212', 0, nil, now }
305
224
 
306
225
  it 'returns a timecode' do
226
+ expect(prior).to be_kind_of(Integer)
307
227
  expect(prior).to be_within(30).of(now.to_i)
308
228
  end
309
229
 
310
230
  context 'reusing same token' do
311
-
312
231
  it 'is false' do
313
232
  expect(verification).to be_falsy
314
233
  end
@@ -319,6 +238,8 @@ RSpec.describe ROTP::TOTP do
319
238
  let(:drift) { 40 }
320
239
 
321
240
  it 'is true' do
241
+ expect(verification).to be_kind_of(Integer)
242
+ expect(verification).to be_within(30).of(now.to_i)
322
243
  expect(verification).to be_truthy
323
244
  end
324
245
  end
@@ -1,5 +1,6 @@
1
1
  require 'rotp'
2
2
  require 'timecop'
3
+ require 'simplecov'
3
4
 
4
5
  RSpec.configure do |config|
5
6
  config.disable_monkey_patching!
@@ -11,3 +12,7 @@ RSpec.configure do |config|
11
12
  Timecop.return
12
13
  end
13
14
  end
15
+
16
+ SimpleCov.start
17
+
18
+ require_relative '../lib/rotp'
metadata CHANGED
@@ -1,71 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rotp
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Percival
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-25 00:00:00.000000000 Z
11
+ date: 2016-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: guard-rspec
14
+ name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.5'
19
+ version: '10.5'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.5'
26
+ version: '10.5'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.4'
33
+ version: '3.5'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.4'
40
+ version: '3.5'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: timecop
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.1'
47
+ version: '0.8'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.1'
54
+ version: '0.8'
55
55
  - !ruby/object:Gem::Dependency
56
- name: timecop
56
+ name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.7'
61
+ version: '0.12'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.7'
68
+ version: '0.12'
69
69
  description: Works for both HOTP and TOTP, and includes QR Code provisioning
70
70
  email:
71
71
  - mark@markpercival.us