rotp 3.2.0 → 3.3.0

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.
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