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
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
|
+
[![Build Status](https://secure.travis-ci.org/mdp/rotp.png)](https://travis-ci.org/mdp/rotp)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/rotp.svg)](https://rubygems.org/gems/rotp)
|
5
|
+
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](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
|
+
![QR Code for OTP](http://chart.apis.google.com/chart?cht=qr&chs=250x250&chl=otpauth%3A%2F%2Ftotp%2Falice%40google.com%3Fsecret%3DJBSWY3DPEHPK3PXP)
|
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
|