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.
Files changed (9) hide show
  1. checksums.yaml +7 -0
  2. data/.gems +2 -0
  3. data/LICENSE +19 -0
  4. data/README.md +59 -0
  5. data/lib/totp.rb +50 -0
  6. data/makefile +4 -0
  7. data/test/totp_test.rb +36 -0
  8. data/totp.gemspec +14 -0
  9. metadata +78 -0
@@ -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
@@ -0,0 +1,2 @@
1
+ base32 -v 0.3.2
2
+ cutest -v 1.2.1
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.
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ test:
2
+ cutest ./test/*.rb
3
+
4
+ .PHONY: test
@@ -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
@@ -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: []