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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab533788e10594ef3290782dba5f044b4f6d259e
|
4
|
+
data.tar.gz: 39f4d7d06f29f05656374b5d2edef7d05f36fdd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cfb56d25e3520f7d5875ce8d5d0a3b3d0a22cce6bb1c2b727ca5b95522b8a00cefe5ab43fe01b7d08038db675a902270662156dcffd3a0ed6d40f6bbe3375a66
|
7
|
+
data.tar.gz: a755331e53a9f06eb4627d820ec39db3092795dcd139c9e05dedaab7427e1c8c0ae1ae8c8fa21b23fdbb905c733206fb9decbc9d1ee36407abab2984d5f79c9c
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
### Changelog
|
2
|
+
|
3
|
+
#### 2.1.0
|
4
|
+
|
5
|
+
- Add a CLI for generating OTP's mdp/rotp/pull/35
|
6
|
+
|
7
|
+
#### 2.0.0
|
8
|
+
|
9
|
+
- Move to only comparing string OTP's.
|
10
|
+
|
11
|
+
#### 1.7.0
|
12
|
+
|
13
|
+
- Move to only comparing string OTP's. See mdp/rotp/issues/32 - Moved to 2.0.0 - yanked from RubyGems
|
14
|
+
|
15
|
+
#### 1.6.1
|
16
|
+
|
17
|
+
- Remove deprecation warning in Ruby 2.1.0 (@ylansegal)
|
18
|
+
- Add Ruby 2.0 and 2.1 to Travis
|
19
|
+
|
20
|
+
#### 1.6.0
|
21
|
+
|
22
|
+
- Add verify_with_retries to HOTP
|
23
|
+
- Fix 'cgi' require and global DEFAULT_INTERVAL
|
24
|
+
|
25
|
+
#### 1.5.0
|
26
|
+
|
27
|
+
- Add support for "issuer" parameter on provisioning url
|
28
|
+
- Add support for "period/interval" parameter on provisioning url
|
29
|
+
|
30
|
+
#### 1.4.6
|
31
|
+
|
32
|
+
- Revert to previous Base32
|
33
|
+
|
34
|
+
#### 1.4.5
|
35
|
+
|
36
|
+
- Fix and test correct implementation of Base32
|
37
|
+
|
38
|
+
#### 1.4.4
|
39
|
+
|
40
|
+
- Fix issue with base32 decoding of strings in a length that's not a multiple of 8
|
41
|
+
|
42
|
+
#### 1.4.3
|
43
|
+
|
44
|
+
- Bugfix on padding
|
45
|
+
|
46
|
+
#### 1.4.2
|
47
|
+
|
48
|
+
- Better padding options (Pad the output with leading 0's)
|
49
|
+
|
50
|
+
#### 1.4.1
|
51
|
+
|
52
|
+
- Clean up drift logic
|
53
|
+
|
54
|
+
#### 1.4.0
|
55
|
+
|
56
|
+
- Added clock drift support via 'verify_with_drift' for TOTP
|
57
|
+
|
58
|
+
####1.3.0
|
59
|
+
|
60
|
+
- Added support for Ruby 1.9.x
|
61
|
+
- Removed dependency on Base32
|
data/Gemfile
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
source
|
1
|
+
source 'http://rubygems.org'
|
2
2
|
gemspec
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rotp (2.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
celluloid (0.16.0)
|
10
|
+
timers (~> 4.0.0)
|
11
|
+
coderay (1.1.0)
|
12
|
+
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)
|
55
|
+
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
|
66
|
+
|
67
|
+
PLATFORMS
|
68
|
+
ruby
|
69
|
+
|
70
|
+
DEPENDENCIES
|
71
|
+
guard-rspec (~> 4.5.0)
|
72
|
+
rake (~> 10.4.2)
|
73
|
+
rotp!
|
74
|
+
rspec (~> 3.1.0)
|
75
|
+
timecop (~> 0.7.1)
|
data/Guardfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
guard :rspec, cmd: 'bundle exec rspec --format progress' do
|
2
|
+
require "guard/rspec/dsl"
|
3
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
4
|
+
|
5
|
+
# RSpec files
|
6
|
+
rspec = dsl.rspec
|
7
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
8
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
9
|
+
watch(rspec.spec_files)
|
10
|
+
|
11
|
+
# Ruby files
|
12
|
+
ruby = dsl.ruby
|
13
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
14
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# The Ruby One Time Password Library
|
2
|
+
|
3
|
+
[](https://travis-ci.org/mdp/rotp)
|
4
|
+
[](https://rubygems.org/gems/rotp)
|
5
|
+
[](https://github.com/mdp/rotp/blob/master/LICENSE)
|
6
|
+
|
7
|
+
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).
|
8
|
+
|
9
|
+
ROTP is compatible with the [Google Authenticator](https://github.com/google/google-authenticator) available for [Android](https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2) and [iPhone](https://itunes.apple.com/en/app/google-authenticator/id388497605).
|
10
|
+
|
11
|
+
Many websites use this for [multi-factor authentication](https://www.youtube.com/watch?v=17rykTIX_HY), such as GMail, Facebook, Amazon EC2, WordPress, and Salesforce. You can find the whole [list here](https://en.wikipedia.org/wiki/Google_Authenticator#Usage).
|
12
|
+
|
13
|
+
## Dependencies
|
14
|
+
|
15
|
+
* OpenSSL
|
16
|
+
* Ruby 1.9.3 or higher
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
```bash
|
21
|
+
gem install rotp
|
22
|
+
```
|
23
|
+
|
24
|
+
## Library Usage
|
25
|
+
|
26
|
+
### Time based OTP's
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
totp = ROTP::TOTP.new("base32secret3232")
|
30
|
+
totp.now # => "492039"
|
31
|
+
|
32
|
+
# OTP verified for current time
|
33
|
+
totp.verify("492039") # => true
|
34
|
+
sleep 30
|
35
|
+
totp.verify("492039") # => false
|
36
|
+
```
|
37
|
+
|
38
|
+
### Counter based OTP's
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
hotp = ROTP::HOTP.new("base32secretkey3232")
|
42
|
+
hotp.at(0) # => "260182"
|
43
|
+
hotp.at(1) # => "055283"
|
44
|
+
hotp.at(1401) # => "316439"
|
45
|
+
|
46
|
+
# OTP verified with a counter
|
47
|
+
hotp.verify("316439", 1401) # => true
|
48
|
+
hotp.verify("316439", 1402) # => false
|
49
|
+
```
|
50
|
+
|
51
|
+
### Generating a Base32 Secret key
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
ROTP::Base32.random_base32 # returns a 16 character base32 secret. Compatible with Google Authenticator
|
55
|
+
```
|
56
|
+
|
57
|
+
Note: The Base32 format conforms to [RFC 4648 Base32](http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet)
|
58
|
+
|
59
|
+
### Google Authenticator Compatible URI's
|
60
|
+
|
61
|
+
Provisioning URI's generated by ROTP are compatible with the Google Authenticator App
|
62
|
+
to be scanned with the in-built QR Code scanner.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
totp.provisioning_uri("alice@google.com") # => 'otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP'
|
66
|
+
hotp.provisioning_uri("alice@google.com", 0) # => 'otpauth://hotp/alice@google.com?secret=JBSWY3DPEHPK3PXP&counter=0'
|
67
|
+
```
|
68
|
+
|
69
|
+
This can then be rendered as a QR Code which can then be scanned and added to the users
|
70
|
+
list of OTP credentials.
|
71
|
+
|
72
|
+
#### Working example
|
73
|
+
|
74
|
+
Scan the following barcode with your phone, using Google Authenticator
|
75
|
+
|
76
|
+

|
77
|
+
|
78
|
+
Now run the following and compare the output
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
require 'rubygems'
|
82
|
+
require 'rotp'
|
83
|
+
totp = ROTP::TOTP.new("JBSWY3DPEHPK3PXP")
|
84
|
+
p "Current OTP: #{totp.now}"
|
85
|
+
```
|
86
|
+
|
87
|
+
### Testing
|
88
|
+
|
89
|
+
```bash
|
90
|
+
bundle install
|
91
|
+
bundle exec rspec
|
92
|
+
```
|
93
|
+
|
94
|
+
## Executable Usage
|
95
|
+
|
96
|
+
Once the rotp rubygem is installed on your system, you should be able to run the `rotp` executable
|
97
|
+
(if not, you might find trouble-shooting help [at this stackoverflow question](http://stackoverflow.com/a/909980)).
|
98
|
+
|
99
|
+
```bash
|
100
|
+
# Try this to get an overview of the commands
|
101
|
+
rotp --help
|
102
|
+
|
103
|
+
# Examples
|
104
|
+
rotp --secret p4ssword # Generates a time-based one-time password
|
105
|
+
rotp --hmac --secret p4ssword --counter 42 # Generates a counter-based one-time password
|
106
|
+
```
|
107
|
+
|
108
|
+
## Contributors
|
109
|
+
|
110
|
+
Have a look at the [contributors graph](https://github.com/mdp/rotp/graphs/contributors) on Github.
|
111
|
+
|
112
|
+
## License
|
113
|
+
|
114
|
+
MIT Copyright (C) 2011 by Mark Percival, see [LICENSE](https://github.com/mdp/rotp/blob/master/LICENSE) for details.
|
115
|
+
|
116
|
+
## Other implementations
|
117
|
+
|
118
|
+
A list can be found at [Wikipedia](https://en.wikipedia.org/wiki/Google_Authenticator#Implementations).
|
data/Rakefile
CHANGED
data/bin/rotp
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module ROTP
|
5
|
+
class Arguments
|
6
|
+
|
7
|
+
def initialize(filename, arguments)
|
8
|
+
@filename = filename
|
9
|
+
@arguments = Array(arguments)
|
10
|
+
end
|
11
|
+
|
12
|
+
def options
|
13
|
+
parse
|
14
|
+
options!
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
parser.help + "\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_reader :arguments, :filename
|
24
|
+
|
25
|
+
def options!
|
26
|
+
@options ||= default_options
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_options
|
30
|
+
OpenStruct.new time: true, counter: 0, mode: :time
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse
|
34
|
+
return options!.mode = :help if arguments.empty?
|
35
|
+
parser.parse arguments
|
36
|
+
|
37
|
+
rescue OptionParser::InvalidOption => exception
|
38
|
+
options!.mode = :help
|
39
|
+
options!.warnings = red(exception.message + '. Try --help for help.')
|
40
|
+
end
|
41
|
+
|
42
|
+
def parser
|
43
|
+
OptionParser.new do |parser|
|
44
|
+
parser.banner = ''
|
45
|
+
parser.separator green(' Usage: ') + bold("#{filename} [options]")
|
46
|
+
parser.separator ''
|
47
|
+
parser.separator green ' Examples: '
|
48
|
+
parser.separator ' ' + bold("#{filename} --secret p4ssword") + ' # Generates a time-based one-time password'
|
49
|
+
parser.separator ' ' + bold("#{filename} --hmac --secret p4ssword --counter 42") + ' # Generates a counter-based one-time password'
|
50
|
+
parser.separator ''
|
51
|
+
parser.separator green ' Options:'
|
52
|
+
|
53
|
+
parser.on('-s', '--secret [SECRET]', 'The shared secret') do |secret|
|
54
|
+
options!.secret = secret
|
55
|
+
end
|
56
|
+
|
57
|
+
parser.on('-c', '--counter [COUNTER]', 'The counter for counter-based hmac OTP') do |counter|
|
58
|
+
options!.counter = counter.to_i
|
59
|
+
end
|
60
|
+
|
61
|
+
parser.on('-t', '--time', 'Use time-based OTP according to RFC 6238 (default)') do
|
62
|
+
options!.mode = :time
|
63
|
+
end
|
64
|
+
|
65
|
+
parser.on('-m', '--hmac', 'Use counter-based OTP according to RFC 4226') do
|
66
|
+
options!.mode = :hmac
|
67
|
+
end
|
68
|
+
|
69
|
+
parser.on_tail('-h', '--help', 'Show this message') do
|
70
|
+
options!.mode = :help
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def bold(string)
|
76
|
+
"\033[1m#{string}\033[22m"
|
77
|
+
end
|
78
|
+
|
79
|
+
def green(string)
|
80
|
+
"\033[32m#{string}\033[0m"
|
81
|
+
end
|
82
|
+
|
83
|
+
def red(string)
|
84
|
+
"\033[31m#{string}\033[0m"
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
data/lib/rotp/cli.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rotp/arguments'
|
2
|
+
|
3
|
+
module ROTP
|
4
|
+
class CLI
|
5
|
+
attr_reader :filename, :argv
|
6
|
+
|
7
|
+
def initialize(filename, argv)
|
8
|
+
@filename = filename
|
9
|
+
@argv = argv
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
puts output
|
14
|
+
end
|
15
|
+
|
16
|
+
def errors
|
17
|
+
if [:time, :hmac].include?(options.mode)
|
18
|
+
if options.secret.to_s == ''
|
19
|
+
red 'You must also specify a --secret. Try --help for help.'
|
20
|
+
elsif options.secret.to_s.chars.any? { |c| ROTP::Base32::CHARS.index(c.downcase) == nil }
|
21
|
+
red 'Secret must be in RFC4648 Base32 format - http://en.wikipedia.org/wiki/Base32#RFC_4648_Base32_alphabet'
|
22
|
+
end
|
23
|
+
elsif options.mode == :hmac && options.counter.to_i < 0
|
24
|
+
red 'You must also specify a --counter. Try --help for help.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def output
|
29
|
+
return options.warnings if options.warnings
|
30
|
+
return errors if errors
|
31
|
+
return arguments.to_s if options.mode == :help
|
32
|
+
|
33
|
+
if options.mode == :time
|
34
|
+
ROTP::TOTP.new(options.secret).now
|
35
|
+
elsif options.mode == :hmac
|
36
|
+
ROTP::HOTP.new(options.secret).at options.counter
|
37
|
+
|
38
|
+
else
|
39
|
+
fail NotImplementedError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def arguments
|
44
|
+
@arguments ||= ROTP::Arguments.new(filename, argv)
|
45
|
+
end
|
46
|
+
|
47
|
+
def options
|
48
|
+
arguments.options
|
49
|
+
end
|
50
|
+
|
51
|
+
def red(string)
|
52
|
+
"\033[31m#{string}\033[0m"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|