totp 1.0.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 +7 -0
- data/.gems +2 -0
- data/LICENSE +19 -0
- data/README.md +59 -0
- data/lib/totp.rb +50 -0
- data/makefile +4 -0
- data/test/totp_test.rb +36 -0
- data/totp.gemspec +14 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc0d44097f7757f0363bb7ce7eaa4f236f86b0eb
|
4
|
+
data.tar.gz: 18c991e130d749c77a59da809066fe869a4f4623
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bb98acd87360c4b57f497aa7216cd9ab45ec2f7291c07ba04dcbd58ecc73cb5e0411653a7b2a1441ed54c82966c5975ec7314a5fc80d57e084c7a9ec85692c23
|
7
|
+
data.tar.gz: 3b844ad850ebddad6940991f021cf7a17e57949f4df79dc636fbde0f6821d12397b747863bea4425ff34168441fa425b1a8482ff7a18396a88dc8206d0350934
|
data/.gems
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2014 Michel Martens
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Time-based One-Time Passwords
|
2
|
+
|
3
|
+
This is an implementation of a Time-Based One-Time Password
|
4
|
+
Algorithm compatible with Google Authenticator. You can read more
|
5
|
+
about TOTP at http://tools.ietf.org/html/rfc6238.
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
Passwords are derived from a secret and a time. You can use the
|
10
|
+
`TOTP.secret` function to obtain a new, random secret.
|
11
|
+
|
12
|
+
``` ruby
|
13
|
+
TOTP.secret #=> "YYZ27CO4WZTPZAYX"
|
14
|
+
```
|
15
|
+
|
16
|
+
This helper is handy if you want to implement 2 factor
|
17
|
+
authentication in your application.
|
18
|
+
|
19
|
+
In order to generate time based one time passwords, you can use
|
20
|
+
the `TOTP.passwords` function:
|
21
|
+
|
22
|
+
``` ruby
|
23
|
+
# Using the secret "AAAAAAAAAAAAAAAA" and the current time
|
24
|
+
TOTP.passwords("AAAAAAAAAAAAAAAA") #=> [783743, 17086, 955099]
|
25
|
+
```
|
26
|
+
|
27
|
+
You can also provide a specific time or timestamp:
|
28
|
+
|
29
|
+
``` ruby
|
30
|
+
# Using the same secret and a timestamp
|
31
|
+
passwords = TOTP.passwords("AAAAAAAAAAAAAAAA", 1400000000)
|
32
|
+
|
33
|
+
assert_equal [306281, 553572, 304383], passwords #=> true
|
34
|
+
```
|
35
|
+
|
36
|
+
To check the validity of a given password, you can use the
|
37
|
+
`TOTP.valid?` function:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
TOTP.valid?(secret, password)
|
41
|
+
```
|
42
|
+
|
43
|
+
If you want to check if a password was valid at a particular time,
|
44
|
+
you can provide a time object or a timestamp as a third argument:
|
45
|
+
|
46
|
+
``` ruby
|
47
|
+
TOTP.valid?(secret, password, time)
|
48
|
+
```
|
49
|
+
|
50
|
+
## Installation
|
51
|
+
|
52
|
+
$ gem install totp
|
53
|
+
|
54
|
+
### Acknowledgments
|
55
|
+
|
56
|
+
This library is inspired by the following implementations:
|
57
|
+
|
58
|
+
- https://gist.github.com/bithive/987839
|
59
|
+
- https://github.com/nricciar/google_authenticator_auth
|
data/lib/totp.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base32'
|
3
|
+
|
4
|
+
module TOTP
|
5
|
+
|
6
|
+
# Generate a random secret
|
7
|
+
def self.secret
|
8
|
+
return Base32.encode((0...10).map { rand(255).chr }.join)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Return whether or not the key is valid for the given secret
|
12
|
+
def self.valid?(secret, pass, time = Time.now)
|
13
|
+
return self.passwords(secret, time).include?(pass)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.totp(hmac, time)
|
17
|
+
bytes = [time].pack('>q').reverse
|
18
|
+
|
19
|
+
hmac.reset
|
20
|
+
hmac.update(bytes)
|
21
|
+
|
22
|
+
code = hmac.digest
|
23
|
+
offs = code[-1].ord & 0x0F
|
24
|
+
hash = code[offs...offs + 4]
|
25
|
+
|
26
|
+
pass = hash.reverse.unpack('L')[0]
|
27
|
+
pass &= 0x7FFFFFFF
|
28
|
+
pass %= 1000000
|
29
|
+
|
30
|
+
return pass
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generate passwords based on the secret and time
|
34
|
+
def self.passwords(secret, time = Time.now)
|
35
|
+
|
36
|
+
interval = time.to_i / 30
|
37
|
+
|
38
|
+
hmac = OpenSSL::HMAC.new(
|
39
|
+
Base32.decode(secret),
|
40
|
+
OpenSSL::Digest::SHA1.new,
|
41
|
+
)
|
42
|
+
|
43
|
+
# Cover three 30 second intervals
|
44
|
+
return [
|
45
|
+
totp(hmac, interval.pred),
|
46
|
+
totp(hmac, interval),
|
47
|
+
totp(hmac, interval.succ),
|
48
|
+
]
|
49
|
+
end
|
50
|
+
end
|
data/makefile
ADDED
data/test/totp_test.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative "../lib/totp"
|
2
|
+
|
3
|
+
test "normal usage" do
|
4
|
+
secret = TOTP.secret
|
5
|
+
|
6
|
+
# Secret is a string of size 16
|
7
|
+
assert_equal 16, secret.size
|
8
|
+
assert_equal String, secret.class
|
9
|
+
|
10
|
+
# Secret should be different on each call
|
11
|
+
assert secret != TOTP.secret
|
12
|
+
|
13
|
+
passwords = TOTP.passwords(secret)
|
14
|
+
|
15
|
+
# Passwords should be unique
|
16
|
+
assert_equal passwords.size, passwords.uniq.size
|
17
|
+
|
18
|
+
# Passwords should be valid
|
19
|
+
passwords.each do |pass|
|
20
|
+
assert TOTP.valid?(secret, pass)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
test "usage with known values" do
|
25
|
+
secret = "AAAAAAAAAAAAAAAA"
|
26
|
+
time = 1400000000
|
27
|
+
known = [306281, 553572, 304383]
|
28
|
+
|
29
|
+
# Accepts an optional argument for time
|
30
|
+
passwords = TOTP.passwords(secret, time)
|
31
|
+
|
32
|
+
# Accepts an optional argument for time
|
33
|
+
assert TOTP.valid?(secret, known[0], time)
|
34
|
+
|
35
|
+
assert_equal known, passwords
|
36
|
+
end
|
data/totp.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "totp"
|
3
|
+
s.version = "1.0.0"
|
4
|
+
s.summary = "Time-based One-Time Passwords"
|
5
|
+
s.description = "Time-based One-Time Passwords"
|
6
|
+
s.authors = ["Michel Martens"]
|
7
|
+
s.email = ["michel@soveran.com"]
|
8
|
+
s.homepage = "http://github.com/soveran/totp"
|
9
|
+
|
10
|
+
s.files = `git ls-files`.split("\n")
|
11
|
+
|
12
|
+
s.add_dependency "base32"
|
13
|
+
s.add_development_dependency "cutest"
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: totp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michel Martens
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: base32
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cutest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Time-based One-Time Passwords
|
42
|
+
email:
|
43
|
+
- michel@soveran.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gems
|
49
|
+
- LICENSE
|
50
|
+
- README.md
|
51
|
+
- lib/totp.rb
|
52
|
+
- makefile
|
53
|
+
- test/totp_test.rb
|
54
|
+
- totp.gemspec
|
55
|
+
homepage: http://github.com/soveran/totp
|
56
|
+
licenses: []
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 2.0.3
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Time-based One-Time Passwords
|
78
|
+
test_files: []
|