rotp 3.1.0 → 3.2.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: c450bbb03ba621504bffd4d10d86beb9aa0df214
4
- data.tar.gz: 13a046cf63204bea4517283ae0a2473becc67ceb
3
+ metadata.gz: e81c60dc4a476ab617e914fe50d3739290e9a110
4
+ data.tar.gz: 3f99b3ec451c23b615ff7a58acbcf5a105f5f5c3
5
5
  SHA512:
6
- metadata.gz: d76a02cc91e2de40619a99a925c3452546c31914fc0172918c40a68f405975b6ee293f437d91d49004b1e4074607f9f7abf8b266345ced389e9e313d6f0ab030
7
- data.tar.gz: 08e82ddf0a585ee225a9d98b04cf0a22c7a386504f8f3db3936c07dc6231ecafa3f8f920fd9048b4606cb14080a194f7a4c333311735696d9016a88975d0c5b1
6
+ metadata.gz: 0f2ad34ca1702d72edb5e16dd715d58f6980c4412577cc40cd5337f6f3cb21958345883e4e67573eaf540364fecf4d9e73a720c4a79042327c2ac6d7e8f6bbbd
7
+ data.tar.gz: ad3ddffe067a7716d6cfaadcc7c9e8ac256831efc7d6214d7422269d4805eed427161af4f0c1de773ebe386d93355d495551aba03498a61e9277d3377ac1cdc1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ### Changelog
2
2
 
3
+ #### 3.2.0
4
+
5
+ - Add 'verify_with_drift_and_prior' to prevent prior token use - #58 from @jlfaber
6
+
3
7
  #### 3.1.0
4
8
 
5
9
  - Add Add digits paramater to provisioning URI. #54 from @sbc100
data/lib/rotp/totp.rb CHANGED
@@ -47,6 +47,30 @@ module ROTP
47
47
  times.any? { |ti| verify(otp, ti) }
48
48
  end
49
49
 
50
+ # Verifies the OTP passed in against the current time OTP
51
+ # and adjacent intervals up to +drift+. Excludes OTPs
52
+ # from prior_time and earlier. Returns time value of
53
+ # matching OTP code for use in subsequent call.
54
+ # @param [String] otp the OTP to check against
55
+ # @param [Integer] drift the number of seconds that the client
56
+ # and server are allowed to drift apart
57
+ # @param [Integer] time value of previous match
58
+ def verify_with_drift_and_prior(otp, drift, prior_time = nil, time = Time.now)
59
+ # calculate normalized bin start times based on drift
60
+ first_bin = (time - drift).to_i / interval * interval
61
+ last_bin = (time + drift).to_i / interval * interval
62
+
63
+ # if prior_time was supplied, adjust first bin if necessary to exclude it
64
+ if prior_time
65
+ prior_bin = prior_time.to_i / interval * interval
66
+ first_bin = prior_bin + interval if prior_bin >= first_bin
67
+ # fail if we've already used the last available OTP code
68
+ return if first_bin > last_bin
69
+ end
70
+ times = (first_bin..last_bin).step(interval).to_a
71
+ times.find { |ti| verify(otp, ti) }
72
+ end
73
+
50
74
  # Returns the provisioning URI for the OTP
51
75
  # This can then be encoded in a QR Code and used
52
76
  # to provision the Google Authenticator app
data/lib/rotp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ROTP
2
- VERSION = "3.1.0"
2
+ VERSION = "3.2.0"
3
3
  end
@@ -222,6 +222,118 @@ RSpec.describe ROTP::TOTP do
222
222
  end
223
223
  end
224
224
 
225
+ describe '#verify_with_drift_and_prior' do
226
+ let(:verification) { totp.verify_with_drift_and_prior token, drift, prior, now }
227
+ let(:drift) { 0 }
228
+ let(:prior) { nil }
229
+
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
+ context 'with a prior verify' do
304
+ let(:prior) { totp.verify_with_drift_and_prior '068212', 0, nil, now }
305
+
306
+ it 'returns a timecode' do
307
+ expect(prior).to be_within(30).of(now.to_i)
308
+ end
309
+
310
+ context 'reusing same token' do
311
+
312
+ it 'is false' do
313
+ expect(verification).to be_falsy
314
+ end
315
+ end
316
+
317
+ context 'newer token' do
318
+ let(:token) { totp.at now + 40 }
319
+ let(:drift) { 40 }
320
+
321
+ it 'is true' do
322
+ expect(verification).to be_truthy
323
+ end
324
+ end
325
+
326
+ context 'older token' do
327
+ let(:token) { totp.at now - 40 }
328
+ let(:drift) { 40 }
329
+
330
+ it 'is false' do
331
+ expect(verification).to be_falsy
332
+ end
333
+ end
334
+ end
335
+ end
336
+
225
337
  describe '#now' do
226
338
  before do
227
339
  Timecop.freeze now
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rotp
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.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-06-14 00:00:00.000000000 Z
11
+ date: 2016-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: guard-rspec