rotp 2.0.0 → 2.1.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 +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.md +61 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +75 -0
- data/Guardfile +14 -0
- data/README.md +118 -0
- data/Rakefile +0 -1
- data/bin/rotp +7 -0
- data/lib/rotp/arguments.rb +89 -0
- data/lib/rotp/cli.rb +56 -0
- data/lib/rotp/version.rb +1 -1
- data/rotp.gemspec +4 -7
- data/spec/lib/rotp/arguments_spec.rb +88 -0
- data/spec/lib/rotp/base32_spec.rb +49 -0
- data/spec/lib/rotp/cli_spec.rb +29 -0
- data/spec/lib/rotp/hotp_spec.rb +142 -0
- data/spec/lib/rotp/totp_spec.rb +235 -0
- data/spec/spec_helper.rb +9 -7
- metadata +54 -26
- data/.rspec +0 -3
- data/README.markdown +0 -163
- data/spec/base_spec.rb +0 -27
- data/spec/hotp_spec.rb +0 -66
- data/spec/totp_spec.rb +0 -115
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'timecop'
|
4
|
-
require 'rspec'
|
5
|
-
require 'rspec/autorun'
|
6
|
-
|
7
1
|
require 'rotp'
|
2
|
+
require 'timecop'
|
8
3
|
|
9
4
|
RSpec.configure do |config|
|
10
|
-
|
5
|
+
config.disable_monkey_patching!
|
6
|
+
config.raise_errors_for_deprecations!
|
7
|
+
config.color = true
|
8
|
+
config.fail_fast = true
|
9
|
+
|
10
|
+
config.before do
|
11
|
+
Timecop.return
|
12
|
+
end
|
11
13
|
end
|
metadata
CHANGED
@@ -1,71 +1,89 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rotp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.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:
|
11
|
+
date: 2015-01-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: guard-rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.5.0
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.5.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rake
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
16
30
|
requirements:
|
17
|
-
- - ~>
|
31
|
+
- - "~>"
|
18
32
|
- !ruby/object:Gem::Version
|
19
|
-
version: 10.
|
33
|
+
version: 10.4.2
|
20
34
|
type: :development
|
21
35
|
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
|
-
- - ~>
|
38
|
+
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
|
-
version: 10.
|
40
|
+
version: 10.4.2
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: rspec
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- - ~>
|
45
|
+
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
47
|
+
version: 3.1.0
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- - ~>
|
52
|
+
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
54
|
+
version: 3.1.0
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: timecop
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- -
|
59
|
+
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
61
|
+
version: 0.7.1
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- -
|
66
|
+
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
68
|
+
version: 0.7.1
|
55
69
|
description: Works for both HOTP and TOTP, and includes QR Code provisioning
|
56
70
|
email:
|
57
71
|
- mark@markpercival.us
|
58
|
-
executables:
|
72
|
+
executables:
|
73
|
+
- rotp
|
59
74
|
extensions: []
|
60
75
|
extra_rdoc_files: []
|
61
76
|
files:
|
62
|
-
- .gitignore
|
63
|
-
- .
|
64
|
-
- .
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- CHANGELOG.md
|
65
80
|
- Gemfile
|
81
|
+
- Gemfile.lock
|
82
|
+
- Guardfile
|
66
83
|
- LICENSE
|
67
|
-
- README.
|
84
|
+
- README.md
|
68
85
|
- Rakefile
|
86
|
+
- bin/rotp
|
69
87
|
- doc/ROTP/HOTP.html
|
70
88
|
- doc/ROTP/OTP.html
|
71
89
|
- doc/ROTP/TOTP.html
|
@@ -85,16 +103,20 @@ files:
|
|
85
103
|
- doc/method_list.html
|
86
104
|
- doc/top-level-namespace.html
|
87
105
|
- lib/rotp.rb
|
106
|
+
- lib/rotp/arguments.rb
|
88
107
|
- lib/rotp/base32.rb
|
108
|
+
- lib/rotp/cli.rb
|
89
109
|
- lib/rotp/hotp.rb
|
90
110
|
- lib/rotp/otp.rb
|
91
111
|
- lib/rotp/totp.rb
|
92
112
|
- lib/rotp/version.rb
|
93
113
|
- rotp.gemspec
|
94
|
-
- spec/
|
95
|
-
- spec/
|
114
|
+
- spec/lib/rotp/arguments_spec.rb
|
115
|
+
- spec/lib/rotp/base32_spec.rb
|
116
|
+
- spec/lib/rotp/cli_spec.rb
|
117
|
+
- spec/lib/rotp/hotp_spec.rb
|
118
|
+
- spec/lib/rotp/totp_spec.rb
|
96
119
|
- spec/spec_helper.rb
|
97
|
-
- spec/totp_spec.rb
|
98
120
|
homepage: http://github.com/mdp/rotp
|
99
121
|
licenses:
|
100
122
|
- MIT
|
@@ -105,18 +127,24 @@ require_paths:
|
|
105
127
|
- lib
|
106
128
|
required_ruby_version: !ruby/object:Gem::Requirement
|
107
129
|
requirements:
|
108
|
-
- -
|
130
|
+
- - ">="
|
109
131
|
- !ruby/object:Gem::Version
|
110
132
|
version: '0'
|
111
133
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
134
|
requirements:
|
113
|
-
- -
|
135
|
+
- - ">="
|
114
136
|
- !ruby/object:Gem::Version
|
115
137
|
version: '0'
|
116
138
|
requirements: []
|
117
139
|
rubyforge_project: rotp
|
118
|
-
rubygems_version: 2.
|
140
|
+
rubygems_version: 2.4.5
|
119
141
|
signing_key:
|
120
142
|
specification_version: 4
|
121
143
|
summary: A Ruby library for generating and verifying one time passwords
|
122
|
-
test_files:
|
144
|
+
test_files:
|
145
|
+
- spec/lib/rotp/arguments_spec.rb
|
146
|
+
- spec/lib/rotp/base32_spec.rb
|
147
|
+
- spec/lib/rotp/cli_spec.rb
|
148
|
+
- spec/lib/rotp/hotp_spec.rb
|
149
|
+
- spec/lib/rotp/totp_spec.rb
|
150
|
+
- spec/spec_helper.rb
|
data/.rspec
DELETED
data/README.markdown
DELETED
@@ -1,163 +0,0 @@
|
|
1
|
-
# ROTP - The Ruby One Time Password Library
|
2
|
-
[](http://travis-ci.org/mdp/rotp)
|
3
|
-
|
4
|
-
A ruby library for generating one time passwords (HOTP & TOTP) according to [ RFC 4226 ](http://tools.ietf.org/html/rfc4226) and [ RFC 6238 ](http://tools.ietf.org/html/rfc6238)
|
5
|
-
|
6
|
-
This is compatible with Google Authenticator apps available for Android and iPhone, and now in use on GMail
|
7
|
-
|
8
|
-
## Quick overview of using One Time Passwords on your phone
|
9
|
-
|
10
|
-
* OTP's involve a shared secret, stored both on the phone and the server
|
11
|
-
* OTP's can be generated on a phone without internet connectivity (AT&T mode)
|
12
|
-
* OTP's should always be used as a second factor of authentication (if your phone is lost, your account is still secured with a password)
|
13
|
-
* Google Authenticator allows you to store multiple OTP secrets and provision those using a QR Code (no more typing in the secret)
|
14
|
-
|
15
|
-
## Dependencies
|
16
|
-
|
17
|
-
* OpenSSL
|
18
|
-
|
19
|
-
## Installation
|
20
|
-
|
21
|
-
gem install rotp
|
22
|
-
|
23
|
-
## Use
|
24
|
-
|
25
|
-
### Time based OTP's
|
26
|
-
|
27
|
-
totp = ROTP::TOTP.new("base32secret3232")
|
28
|
-
totp.now # => "492039"
|
29
|
-
|
30
|
-
# OTP verified for current time
|
31
|
-
totp.verify("492039") # => true
|
32
|
-
sleep 30
|
33
|
-
totp.verify("492039") # => false
|
34
|
-
|
35
|
-
### Counter based OTP's
|
36
|
-
|
37
|
-
hotp = ROTP::HOTP.new("base32secretkey3232")
|
38
|
-
hotp.at(0) # => "260182"
|
39
|
-
hotp.at(1) # => "055283"
|
40
|
-
hotp.at(1401) # => "316439"
|
41
|
-
|
42
|
-
# OTP verified with a counter
|
43
|
-
totp.verify("316439", 1401) # => true
|
44
|
-
totp.verify("316439", 1402) # => false
|
45
|
-
|
46
|
-
### Generating a Base32 Secret key
|
47
|
-
|
48
|
-
ROTP::Base32.random_base32 # returns a 16 character base32 secret. Compatible with Google Authenticator
|
49
|
-
|
50
|
-
### Google Authenticator Compatible
|
51
|
-
|
52
|
-
The library works with the Google Authenticator iPhone and Android app, and also
|
53
|
-
includes the ability to generate provisioning URI's for use with the QR Code scanner
|
54
|
-
built into the app.
|
55
|
-
|
56
|
-
totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
|
57
|
-
hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
|
58
|
-
|
59
|
-
This can then be rendered as a QR Code which can then be scanned and added to the users
|
60
|
-
list of OTP credentials.
|
61
|
-
|
62
|
-
#### Working example
|
63
|
-
|
64
|
-
Scan the following barcode with your phone, using Google Authenticator
|
65
|
-
|
66
|
-

|
67
|
-
|
68
|
-
Now run the following and compare the output
|
69
|
-
|
70
|
-
require 'rubygems'
|
71
|
-
require 'rotp'
|
72
|
-
totp = ROTP::TOTP.new("JBSWY3DPEHPK3PXP")
|
73
|
-
p "Current OTP: #{totp.now}"
|
74
|
-
|
75
|
-
### Testing
|
76
|
-
|
77
|
-
bundle install
|
78
|
-
bundle exec rspec spec/*
|
79
|
-
|
80
|
-
### Contributors
|
81
|
-
|
82
|
-
git shortlog -s -n
|
83
|
-
|
84
|
-
47 Mark Percival
|
85
|
-
6 David Vrensk
|
86
|
-
1 Guillaume Rose
|
87
|
-
1 Micah Gates
|
88
|
-
1 Michael Brodhead & Shai Rosenfeld
|
89
|
-
1 Nathan Reynolds
|
90
|
-
1 Shai Rosenfeld
|
91
|
-
1 Shai Rosenfeld & Michael Brodhead
|
92
|
-
|
93
|
-
### Changelog
|
94
|
-
|
95
|
-
|
96
|
-
#### 2.0.0
|
97
|
-
|
98
|
-
- Move to only comparing string OTP's.
|
99
|
-
|
100
|
-
#### 1.7.1
|
101
|
-
|
102
|
-
- Revert to former API
|
103
|
-
|
104
|
-
#### 1.7.0
|
105
|
-
|
106
|
-
- Move to only comparing string OTP's. See mdp/rotp/issues/32 - Moved to 2.0.0
|
107
|
-
|
108
|
-
#### 1.6.1
|
109
|
-
|
110
|
-
- Remove deprecation warning in Ruby 2.1.0 (@ylansegal)
|
111
|
-
- Add Ruby 2.0 and 2.1 to Travis
|
112
|
-
|
113
|
-
#### 1.6.0
|
114
|
-
|
115
|
-
- Add verify_with_retries to HOTP
|
116
|
-
- Fix 'cgi' require and global DEFAULT_INTERVAL
|
117
|
-
|
118
|
-
#### 1.5.0
|
119
|
-
|
120
|
-
- Add support for "issuer" parameter on provisioning url
|
121
|
-
- Add support for "period/interval" parameter on provisioning url
|
122
|
-
|
123
|
-
#### 1.4.6
|
124
|
-
|
125
|
-
- Revert to previous Base32
|
126
|
-
|
127
|
-
#### 1.4.5
|
128
|
-
|
129
|
-
- Fix and test correct implementation of Base32
|
130
|
-
|
131
|
-
#### 1.4.4
|
132
|
-
|
133
|
-
- Fix issue with base32 decoding of strings in a length that's not a multiple of 8
|
134
|
-
|
135
|
-
#### 1.4.3
|
136
|
-
|
137
|
-
- Bugfix on padding
|
138
|
-
|
139
|
-
#### 1.4.2
|
140
|
-
|
141
|
-
- Better padding options (Pad the output with leading 0's)
|
142
|
-
|
143
|
-
#### 1.4.1
|
144
|
-
|
145
|
-
- Clean up drift logic
|
146
|
-
|
147
|
-
#### 1.4.0
|
148
|
-
|
149
|
-
- Added clock drift support via 'verify_with_drift' for TOTP
|
150
|
-
|
151
|
-
####1.3.0
|
152
|
-
|
153
|
-
- Added support for Ruby 1.9.x
|
154
|
-
- Removed dependency on Base32
|
155
|
-
|
156
|
-
### License
|
157
|
-
|
158
|
-
MIT Licensed
|
159
|
-
|
160
|
-
### See also:
|
161
|
-
|
162
|
-
Python port PYOTP by [Nathan Reynolds](https://github.com/nathforge) - [https://github.com/nathforge/pyotp](https://github.com/nathforge/pyotp)
|
163
|
-
PHP port OTPHP by [Le Lag](https://github.com/lelag) - [https://github.com/lelag/otphp](https://github.com/lelag/otphp)
|
data/spec/base_spec.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
-
|
3
|
-
describe "the Base32 implementation" do
|
4
|
-
it "should be 16 characters by default" do
|
5
|
-
ROTP::Base32.random_base32.length.should == 16
|
6
|
-
ROTP::Base32.random_base32.should match /\A[a-z2-7]+\z/
|
7
|
-
end
|
8
|
-
it "should be allow a specific length" do
|
9
|
-
ROTP::Base32.random_base32(32).length.should == 32
|
10
|
-
end
|
11
|
-
it "raise a sane error on a bad decode" do
|
12
|
-
expect { ROTP::Base32.decode("4BCDEFG234BCDEF1") }.to \
|
13
|
-
raise_error(ROTP::Base32::Base32Error, "Invalid Base32 Character - '1'")
|
14
|
-
end
|
15
|
-
it "should correctly decode a string" do
|
16
|
-
ROTP::Base32.decode("F").unpack('H*').first.should == "28"
|
17
|
-
ROTP::Base32.decode("23").unpack('H*').first.should == "d6"
|
18
|
-
ROTP::Base32.decode("234").unpack('H*').first.should == "d6f8"
|
19
|
-
ROTP::Base32.decode("234A").unpack('H*').first.should == "d6f800"
|
20
|
-
ROTP::Base32.decode("234B").unpack('H*').first.should == "d6f810"
|
21
|
-
ROTP::Base32.decode("234BCD").unpack('H*').first.should == "d6f8110c"
|
22
|
-
ROTP::Base32.decode("234BCDE").unpack('H*').first.should == "d6f8110c80"
|
23
|
-
ROTP::Base32.decode("234BCDEFG").unpack('H*').first.should == "d6f8110c8530"
|
24
|
-
ROTP::Base32.decode("234BCDEFG234BCDEFG").unpack('H*').first.should == "d6f8110c8536b7c0886429"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
data/spec/hotp_spec.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ROTP::HOTP do
|
4
|
-
before(:all) { @counter = 1234 }
|
5
|
-
|
6
|
-
subject { ROTP::HOTP.new('a' * 32) }
|
7
|
-
|
8
|
-
it "should generate a string OTP given a count" do
|
9
|
-
subject.at(@counter).should == "161024"
|
10
|
-
end
|
11
|
-
it "should generate a number if padding is set to false" do
|
12
|
-
subject.at(@counter, false).should == 161024
|
13
|
-
end
|
14
|
-
it "should not verify a number" do
|
15
|
-
expect {
|
16
|
-
subject.verify(161024, @counter)
|
17
|
-
}.to raise_error
|
18
|
-
end
|
19
|
-
it "should verify a string" do
|
20
|
-
subject.verify("161024", @counter).should be_true
|
21
|
-
end
|
22
|
-
it "should output its provisioning URI" do
|
23
|
-
url = subject.provisioning_uri('mark@percival')
|
24
|
-
params = CGI::parse(URI::parse(url).query)
|
25
|
-
url.should match(/otpauth:\/\/hotp.+/)
|
26
|
-
params["secret"].first.should == "a" * 32
|
27
|
-
end
|
28
|
-
|
29
|
-
context "with retries" do
|
30
|
-
it "should verify that retry is a valid number" do
|
31
|
-
subject.verify_with_retries("161024", @counter, -1).should be_false
|
32
|
-
subject.verify_with_retries("161024", @counter, 0).should be_false
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should verify up to the total number of retries and return the counter" do
|
36
|
-
subject.verify_with_retries("161024", @counter - 10, 10).should == @counter
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should verify that retry is a valid number" do
|
40
|
-
subject.verify_with_retries("161024", @counter - 20, 10).should be_false
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe "HOTP example values from the rfc" do
|
46
|
-
it "should match the RFC" do
|
47
|
-
# 12345678901234567890 in Base32
|
48
|
-
# GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
|
49
|
-
hotp = ROTP::HOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
|
50
|
-
hotp.at(0).should ==("755224")
|
51
|
-
hotp.at(1).should ==("287082")
|
52
|
-
hotp.at(2).should ==("359152")
|
53
|
-
hotp.at(3).should ==("969429")
|
54
|
-
hotp.at(4).should ==("338314")
|
55
|
-
hotp.at(5).should ==("254676")
|
56
|
-
hotp.at(6).should ==("287922")
|
57
|
-
hotp.at(7).should ==("162583")
|
58
|
-
hotp.at(8).should ==("399871")
|
59
|
-
hotp.at(9).should ==("520489")
|
60
|
-
end
|
61
|
-
it "should verify an OTP and not allow reuse" do
|
62
|
-
hotp = ROTP::HOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
|
63
|
-
hotp.verify("520489", 9).should be_true
|
64
|
-
hotp.verify("520489", 10).should be_false
|
65
|
-
end
|
66
|
-
end
|
data/spec/totp_spec.rb
DELETED
@@ -1,115 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe ROTP::TOTP do
|
4
|
-
before(:all) { @now = Time.utc(2012,1,1) }
|
5
|
-
|
6
|
-
subject { ROTP::TOTP.new("JBSWY3DPEHPK3PXP") }
|
7
|
-
|
8
|
-
it "should generate a number given a number" do
|
9
|
-
subject.at(@now, false).should == 68212
|
10
|
-
end
|
11
|
-
|
12
|
-
it "should generate a number as a padded string by default" do
|
13
|
-
subject.at(@now).should == "068212"
|
14
|
-
end
|
15
|
-
|
16
|
-
# Users of the lib
|
17
|
-
it "should not verify a number" do
|
18
|
-
expect {
|
19
|
-
subject.verify(68212, @now)
|
20
|
-
}.to raise_error
|
21
|
-
end
|
22
|
-
it "should not verify an unpadded string" do
|
23
|
-
subject.verify("68212", @now).should be_false
|
24
|
-
end
|
25
|
-
it "should verify a string" do
|
26
|
-
subject.verify("068212", @now).should be_true
|
27
|
-
end
|
28
|
-
|
29
|
-
it "should output its provisioning URI" do
|
30
|
-
url = subject.provisioning_uri('mark@percival')
|
31
|
-
params = CGI::parse(URI::parse(url).query)
|
32
|
-
url.should match(/otpauth:\/\/totp.+/)
|
33
|
-
params["secret"].first.should == "JBSWY3DPEHPK3PXP"
|
34
|
-
end
|
35
|
-
|
36
|
-
context "with issuer" do
|
37
|
-
subject { ROTP::TOTP.new("JBSWY3DPEHPK3PXP", :issuer => "FooCo") }
|
38
|
-
it "should output its provisioning URI with issuer" do
|
39
|
-
url = subject.provisioning_uri('mark@percival')
|
40
|
-
params = CGI::parse(URI::parse(url).query)
|
41
|
-
url.should match(/otpauth:\/\/totp.+/)
|
42
|
-
params["secret"].first.should == "JBSWY3DPEHPK3PXP"
|
43
|
-
params["issuer"].first.should == "FooCo"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context "with non default interval" do
|
48
|
-
subject { ROTP::TOTP.new("JBSWY3DPEHPK3PXP", :interval => 60) }
|
49
|
-
it "should output its provisioning URI with issuer" do
|
50
|
-
url = subject.provisioning_uri('mark@percival')
|
51
|
-
params = CGI::parse(URI::parse(url).query)
|
52
|
-
url.should match(/otpauth:\/\/totp.+/)
|
53
|
-
params["secret"].first.should == "JBSWY3DPEHPK3PXP"
|
54
|
-
params["period"].first.should == "60"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
context "with drift" do
|
60
|
-
it "should verify a number" do
|
61
|
-
subject.verify_with_drift("068212", 0, @now).should be_true
|
62
|
-
end
|
63
|
-
it "should verify a string" do
|
64
|
-
subject.verify_with_drift("068212", 0, @now).should be_true
|
65
|
-
end
|
66
|
-
it "should verify a slightly old number" do
|
67
|
-
subject.verify_with_drift(subject.at(@now - 30), 60, @now).should be_true
|
68
|
-
end
|
69
|
-
it "should verify a slightly new number" do
|
70
|
-
subject.verify_with_drift(subject.at(@now + 60), 60, @now).should be_true
|
71
|
-
end
|
72
|
-
it "should reject a number that is outside the allowed drift" do
|
73
|
-
subject.verify_with_drift(subject.at(@now - 60), 30, @now).should be_false
|
74
|
-
end
|
75
|
-
context "with drift that is not a multiple of the TOTP interval" do
|
76
|
-
it "should verify a slightly old number" do
|
77
|
-
subject.verify_with_drift(subject.at(@now - 45), 45, @now).should be_true
|
78
|
-
end
|
79
|
-
it "should verify a slightly new number" do
|
80
|
-
subject.verify_with_drift(subject.at(@now + 40), 40, @now).should be_true
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
describe "TOTP example values from the documented output" do
|
87
|
-
it "should match the RFC" do
|
88
|
-
totp = ROTP::TOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
|
89
|
-
totp.at(1111111111).should ==("050471")
|
90
|
-
totp.at(1234567890).should ==("005924")
|
91
|
-
totp.at(2000000000).should ==("279037")
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should match the Google Authenticator output" do
|
95
|
-
totp = ROTP::TOTP.new("wrn3pqx5uqxqvnqr")
|
96
|
-
Timecop.freeze(Time.at(1297553958)) do
|
97
|
-
totp.now.should ==("102705")
|
98
|
-
end
|
99
|
-
end
|
100
|
-
it "should match Dropbox 26 char secret output" do
|
101
|
-
totp = ROTP::TOTP.new("tjtpqea6a42l56g5eym73go2oa")
|
102
|
-
Timecop.freeze(Time.at(1378762454)) do
|
103
|
-
totp.now.should ==("747864")
|
104
|
-
end
|
105
|
-
end
|
106
|
-
it "should validate a time based OTP" do
|
107
|
-
totp = ROTP::TOTP.new("wrn3pqx5uqxqvnqr")
|
108
|
-
Timecop.freeze(Time.at(1297553958)) do
|
109
|
-
totp.verify("102705").should be_true
|
110
|
-
end
|
111
|
-
Timecop.freeze(Time.at(1297553958 + 30)) do
|
112
|
-
totp.verify("102705").should be_false
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|