rotp 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://secure.travis-ci.org/mdp/rotp.png)](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
|
-
![QR Code for OTP](http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP)
|
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
|