rotp 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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +2 -0
- data/lib/rotp.rb +8 -0
- data/lib/rotp/hotp.rb +8 -0
- data/lib/rotp/otp.rb +37 -0
- data/lib/rotp/totp.rb +31 -0
- data/lib/rotp/version.rb +3 -0
- data/rotp.gemspec +23 -0
- data/spec/base_spec.rb +35 -0
- data/spec/spec_helper.rb +9 -0
- metadata +94 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
data/lib/rotp.rb
ADDED
data/lib/rotp/hotp.rb
ADDED
data/lib/rotp/otp.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module ROTP
|
2
|
+
class OTP
|
3
|
+
attr_reader :secret, :digits, :digest
|
4
|
+
|
5
|
+
# Params: secret in base32
|
6
|
+
def initialize(s, options = {})
|
7
|
+
@digits = options[:digits] || 6
|
8
|
+
@digest = options[:digest] || "sha1"
|
9
|
+
@secret = options[:raw_secret] ? s : Base32.decode(s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_otp(count)
|
13
|
+
hmac = OpenSSL::HMAC.digest(
|
14
|
+
OpenSSL::Digest::Digest.new(digest),
|
15
|
+
secret,
|
16
|
+
int_to_bytestring(count)
|
17
|
+
)
|
18
|
+
|
19
|
+
offset = hmac[19] & 0xf
|
20
|
+
code = (hmac[offset] & 0x7f) << 24 |
|
21
|
+
(hmac[offset + 1] & 0xff) << 16 |
|
22
|
+
(hmac[offset + 2] & 0xff) << 8 |
|
23
|
+
(hmac[offset + 3] & 0xff)
|
24
|
+
code % 10 ** digits
|
25
|
+
end
|
26
|
+
|
27
|
+
def int_to_bytestring(int, padding = 8)
|
28
|
+
result = []
|
29
|
+
until int == 0
|
30
|
+
result << (int & 0xFF).chr
|
31
|
+
int >>= 8
|
32
|
+
end
|
33
|
+
result.reverse.join.rjust(8, 0.chr)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/rotp/totp.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module ROTP
|
2
|
+
class TOTP < OTP
|
3
|
+
|
4
|
+
attr_reader :interval
|
5
|
+
|
6
|
+
def initialize(s, options = {})
|
7
|
+
@interval = options[:interval] || 30
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def at(time)
|
12
|
+
unless time.class == Time
|
13
|
+
time = Time.at(time.to_i)
|
14
|
+
end
|
15
|
+
generate_otp(timehash(time))
|
16
|
+
end
|
17
|
+
|
18
|
+
def now
|
19
|
+
generate_otp(timehash(Time.now))
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def timehash(time)
|
25
|
+
i = time.utc.to_i * 1000
|
26
|
+
i = i / (interval * 1000)
|
27
|
+
i
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/rotp/version.rb
ADDED
data/rotp.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rotp/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rotp"
|
7
|
+
s.version = Rotp::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Mark Percival"]
|
10
|
+
s.email = ["mark@markpercival.us"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{A Ruby library for generating and verifying one time passwords}
|
13
|
+
s.description = %q{Works for both HOTP and TOTP, and include QR Code provisioning}
|
14
|
+
|
15
|
+
s.rubyforge_project = "rotp"
|
16
|
+
|
17
|
+
s.add_dependency('base32', '0.1.2')
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "HOTP example values from the rfc" do
|
4
|
+
it "should match the RFC" do
|
5
|
+
# 12345678901234567890 in Bas32
|
6
|
+
# GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
|
7
|
+
hotp = ROTP::HOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
|
8
|
+
hotp.at(0).should ==(755224)
|
9
|
+
hotp.at(1).should ==(287082)
|
10
|
+
hotp.at(2).should ==(359152)
|
11
|
+
hotp.at(3).should ==(969429)
|
12
|
+
hotp.at(4).should ==(338314)
|
13
|
+
hotp.at(5).should ==(254676)
|
14
|
+
hotp.at(6).should ==(287922)
|
15
|
+
hotp.at(7).should ==(162583)
|
16
|
+
hotp.at(8).should ==(399871)
|
17
|
+
hotp.at(9).should ==(520489)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "TOTP example values from the rfc" do
|
22
|
+
it "should match the RFC" do
|
23
|
+
totp = ROTP::TOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
|
24
|
+
totp.at(1111111111).should ==(50471)
|
25
|
+
totp.at(1234567890).should ==(5924)
|
26
|
+
totp.at(2000000000).should ==(279037)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should match the Google Authenticator output" do
|
30
|
+
totp = ROTP::TOTP.new("wrn3pqx5uqxqvnqr")
|
31
|
+
Timecop.freeze(Time.at(1297553958)) do
|
32
|
+
totp.now.should ==(102705)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rotp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Mark Percival
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-12 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: base32
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - "="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 31
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 1
|
33
|
+
- 2
|
34
|
+
version: 0.1.2
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
description: Works for both HOTP and TOTP, and include QR Code provisioning
|
38
|
+
email:
|
39
|
+
- mark@markpercival.us
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- .gitignore
|
48
|
+
- Gemfile
|
49
|
+
- Rakefile
|
50
|
+
- lib/rotp.rb
|
51
|
+
- lib/rotp/hotp.rb
|
52
|
+
- lib/rotp/otp.rb
|
53
|
+
- lib/rotp/totp.rb
|
54
|
+
- lib/rotp/version.rb
|
55
|
+
- rotp.gemspec
|
56
|
+
- spec/base_spec.rb
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: ""
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 3
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
hash: 3
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project: rotp
|
88
|
+
rubygems_version: 1.5.2
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: A Ruby library for generating and verifying one time passwords
|
92
|
+
test_files:
|
93
|
+
- spec/base_spec.rb
|
94
|
+
- spec/spec_helper.rb
|