rotp 1.6.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 925325389a0599d8e3d5fa61035be448cba2d26d
4
- data.tar.gz: e5639873115b3f91ff962250c1e0528e332a225c
3
+ metadata.gz: 5c42bc8b1ce8ab11bb556cef750ae50187a6640d
4
+ data.tar.gz: 165cbc7edb89427967f2777b7d8705b45486f18d
5
5
  SHA512:
6
- metadata.gz: 5b377c8eedeb3de0c68ccbb8f2c6896bdfe58bf62259129583bc1933685e4f5a9ee34e3b9e1558f747935f2b1214ed08b2fba64061ba81d6f648885a04eaf4d4
7
- data.tar.gz: e781069e1760f538924cecf5476eecabdf19423e8289a933ff824ce7b3c788b8369835d5f91a804ef3a9267e62688fd06a86d9fa300dddff65b2dc06d3e5aa79
6
+ metadata.gz: ac2979aa19949bedb563e5fc407171efec7d80785e0246d19fed92bc78fca4b64eacdd1ef5c01a848f37c77a7d5bbbc03dc84e1e49d5f84bd55710ffc36b33af
7
+ data.tar.gz: 8349669bdc320ff49ec55eb487eaaf38c352a52faa95bde3cfa8fde06b5ed7b889db670a254d2b2fd8b36286cb13bbaf03b86579466430e6b3d4850db1b0a770
@@ -3,4 +3,3 @@ rvm:
3
3
  - 2.1.0
4
4
  - 2.0.0
5
5
  - 1.9.3
6
- - 1.8.7
@@ -25,23 +25,23 @@ This is compatible with Google Authenticator apps available for Android and iPho
25
25
  ### Time based OTP's
26
26
 
27
27
  totp = ROTP::TOTP.new("base32secret3232")
28
- totp.now # => 492039
28
+ totp.now # => "492039"
29
29
 
30
30
  # OTP verified for current time
31
- totp.verify(492039) # => true
31
+ totp.verify("492039") # => true
32
32
  sleep 30
33
- totp.verify(492039) # => false
33
+ totp.verify("492039") # => false
34
34
 
35
35
  ### Counter based OTP's
36
36
 
37
37
  hotp = ROTP::HOTP.new("base32secretkey3232")
38
- hotp.at(0) # => 260182
39
- hotp.at(1) # => 55283
40
- hotp.at(1401) # => 316439
38
+ hotp.at(0) # => "260182"
39
+ hotp.at(1) # => "055283"
40
+ hotp.at(1401) # => "316439"
41
41
 
42
42
  # OTP verified with a counter
43
- totp.verify(316439, 1401) # => true
44
- totp.verify(316439, 1402) # => false
43
+ totp.verify("316439", 1401) # => true
44
+ totp.verify("316439", 1402) # => false
45
45
 
46
46
  ### Generating a Base32 Secret key
47
47
 
@@ -92,6 +92,19 @@ Now run the following and compare the output
92
92
 
93
93
  ### Changelog
94
94
 
95
+
96
+ #### 2.0.0
97
+
98
+ - Move to only comparing string OTP's.
99
+
100
+ #### 1.7.1
101
+
102
+ - Revert to former API
103
+
104
+ #### 1.7.0
105
+
106
+ - Move to only comparing string OTP's. See mdp/rotp/issues/32 - Moved to 2.0.0
107
+
95
108
  #### 1.6.1
96
109
 
97
110
  - Remove deprecation warning in Ruby 2.1.0 (@ylansegal)
@@ -40,11 +40,7 @@ module ROTP
40
40
  end
41
41
 
42
42
  def decode_quint(q)
43
- if d = CHARS.index(q.downcase)
44
- d
45
- else
46
- raise Base32Error, "Invalid Base32 Character - '#{q}'"
47
- end
43
+ CHARS.index(q.downcase) or raise(Base32Error, "Invalid Base32 Character - '#{q}'")
48
44
  end
49
45
 
50
46
  end
@@ -4,7 +4,7 @@ module ROTP
4
4
  # @param [Integer] count counter
5
5
  # @option [Boolean] padding (false) Issue the number as a 0 padded string
6
6
  # @returns [Integer] OTP
7
- def at(count, padding=false)
7
+ def at(count, padding=true)
8
8
  generate_otp(count, padding)
9
9
  end
10
10
 
@@ -20,7 +20,7 @@ module ROTP
20
20
  # @option padded [Boolean] (false) Output the otp as a 0 padded string
21
21
  # Usually either the counter, or the computed integer
22
22
  # based on the Unix timestamp
23
- def generate_otp(input, padded=false)
23
+ def generate_otp(input, padded=true)
24
24
  hmac = OpenSSL::HMAC.digest(
25
25
  OpenSSL::Digest.new(digest),
26
26
  byte_secret,
@@ -42,7 +42,10 @@ module ROTP
42
42
  private
43
43
 
44
44
  def verify(input, generated)
45
- input.to_i == generated
45
+ unless input.is_a?(String) && generated.is_a?(String)
46
+ raise ArgumentError, "ROTP only verifies strings - See: https://github.com/mdp/rotp/issues/32"
47
+ end
48
+ time_constant_compare(input, generated)
46
49
  end
47
50
 
48
51
  def byte_secret
@@ -74,5 +77,16 @@ module ROTP
74
77
  uri + params_str
75
78
  end
76
79
 
80
+ private
81
+
82
+ # constant-time compare the strings
83
+ def time_constant_compare(a, b)
84
+ return false if a.empty? || b.empty? || a.bytesize != b.bytesize
85
+ l = a.unpack "C#{a.bytesize}"
86
+ res = 0
87
+ b.each_byte { |byte| res |= byte ^ l.shift }
88
+ res == 0
89
+ end
90
+
77
91
  end
78
92
  end
@@ -15,8 +15,8 @@ module ROTP
15
15
  # Accepts either a Unix timestamp integer or a Time object.
16
16
  # Time objects will be adjusted to UTC automatically
17
17
  # @param [Time/Integer] time the time to generate an OTP for
18
- # @option [Boolean] padding (false) Issue the number as a 0 padded string
19
- def at(time, padding=false)
18
+ # @option [Boolean] padding (true) Issue the number as a 0 padded string
19
+ def at(time, padding=true)
20
20
  unless time.class == Time
21
21
  time = Time.at(time.to_i)
22
22
  end
@@ -25,7 +25,7 @@ module ROTP
25
25
 
26
26
  # Generate the current time OTP
27
27
  # @return [Integer] the OTP as an integer
28
- def now(padding=false)
28
+ def now(padding=true)
29
29
  generate_otp(timecode(Time.now), padding)
30
30
  end
31
31
 
@@ -37,7 +37,7 @@ module ROTP
37
37
 
38
38
  # Verifies the OTP passed in against the current time OTP
39
39
  # and adjacent intervals up to +drift+.
40
- # @param [String/Integer] otp the OTP to check against
40
+ # @param [String] otp the OTP to check against
41
41
  # @param [Integer] drift the number of seconds that the client
42
42
  # and server are allowed to drift apart
43
43
  def verify_with_drift(otp, drift, time = Time.now)
@@ -1,3 +1,3 @@
1
1
  module ROTP
2
- VERSION = "1.6.1"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
20
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
21
  s.require_paths = ["lib"]
22
22
 
23
- s.add_development_dependency('rake')
24
- s.add_development_dependency('rspec')
23
+ s.add_development_dependency('rake', '~>10.1.0')
24
+ s.add_development_dependency('rspec', '~>2.13.0')
25
25
  if RUBY_VERSION < "1.9"
26
26
  s.add_development_dependency('timecop', "~>0.5.9.2")
27
27
  else
@@ -9,7 +9,8 @@ describe "the Base32 implementation" do
9
9
  ROTP::Base32.random_base32(32).length.should == 32
10
10
  end
11
11
  it "raise a sane error on a bad decode" do
12
- expect { ROTP::Base32.decode("4BCDEFG234BCDEF1") }.to raise_error(ROTP::Base32::Base32Error)
12
+ expect { ROTP::Base32.decode("4BCDEFG234BCDEF1") }.to \
13
+ raise_error(ROTP::Base32::Base32Error, "Invalid Base32 Character - '1'")
13
14
  end
14
15
  it "should correctly decode a string" do
15
16
  ROTP::Base32.decode("F").unpack('H*').first.should == "28"
@@ -5,14 +5,16 @@ describe ROTP::HOTP do
5
5
 
6
6
  subject { ROTP::HOTP.new('a' * 32) }
7
7
 
8
- it "should generate a number given a time" do
9
- subject.at(@counter).should == 161024
8
+ it "should generate a string OTP given a count" do
9
+ subject.at(@counter).should == "161024"
10
10
  end
11
- it "should generate a number as a string" do
12
- subject.at(@counter, true).should == "161024"
11
+ it "should generate a number if padding is set to false" do
12
+ subject.at(@counter, false).should == 161024
13
13
  end
14
- it "should verify a number" do
15
- subject.verify(161024, @counter).should be_true
14
+ it "should not verify a number" do
15
+ expect {
16
+ subject.verify(161024, @counter)
17
+ }.to raise_error
16
18
  end
17
19
  it "should verify a string" do
18
20
  subject.verify("161024", @counter).should be_true
@@ -26,16 +28,16 @@ describe ROTP::HOTP do
26
28
 
27
29
  context "with retries" do
28
30
  it "should verify that retry is a valid number" do
29
- subject.verify_with_retries(161024, @counter, -1).should be_false
30
- subject.verify_with_retries(161024, @counter, 0).should be_false
31
+ subject.verify_with_retries("161024", @counter, -1).should be_false
32
+ subject.verify_with_retries("161024", @counter, 0).should be_false
31
33
  end
32
34
 
33
35
  it "should verify up to the total number of retries and return the counter" do
34
- subject.verify_with_retries(161024, @counter - 10, 10).should == @counter
36
+ subject.verify_with_retries("161024", @counter - 10, 10).should == @counter
35
37
  end
36
38
 
37
39
  it "should verify that retry is a valid number" do
38
- subject.verify_with_retries(161024, @counter - 20, 10).should be_false
40
+ subject.verify_with_retries("161024", @counter - 20, 10).should be_false
39
41
  end
40
42
  end
41
43
  end
@@ -45,20 +47,20 @@ describe "HOTP example values from the rfc" do
45
47
  # 12345678901234567890 in Base32
46
48
  # GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
47
49
  hotp = ROTP::HOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
48
- hotp.at(0).should ==(755224)
49
- hotp.at(1).should ==(287082)
50
- hotp.at(2).should ==(359152)
51
- hotp.at(3).should ==(969429)
52
- hotp.at(4).should ==(338314)
53
- hotp.at(5).should ==(254676)
54
- hotp.at(6).should ==(287922)
55
- hotp.at(7).should ==(162583)
56
- hotp.at(8).should ==(399871)
57
- hotp.at(9).should ==(520489)
50
+ hotp.at(0).should ==("755224")
51
+ hotp.at(1).should ==("287082")
52
+ hotp.at(2).should ==("359152")
53
+ hotp.at(3).should ==("969429")
54
+ hotp.at(4).should ==("338314")
55
+ hotp.at(5).should ==("254676")
56
+ hotp.at(6).should ==("287922")
57
+ hotp.at(7).should ==("162583")
58
+ hotp.at(8).should ==("399871")
59
+ hotp.at(9).should ==("520489")
58
60
  end
59
61
  it "should verify an OTP and not allow reuse" do
60
62
  hotp = ROTP::HOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
61
- hotp.verify(520489, 9).should be_true
62
- hotp.verify(520489, 10).should be_false
63
+ hotp.verify("520489", 9).should be_true
64
+ hotp.verify("520489", 10).should be_false
63
65
  end
64
66
  end
@@ -6,16 +6,24 @@ describe ROTP::TOTP do
6
6
  subject { ROTP::TOTP.new("JBSWY3DPEHPK3PXP") }
7
7
 
8
8
  it "should generate a number given a number" do
9
- subject.at(@now).should == 68212
9
+ subject.at(@now, false).should == 68212
10
10
  end
11
- it "should generate a number as a padded string" do
12
- subject.at(@now, true).should == "068212"
11
+
12
+ it "should generate a number as a padded string by default" do
13
+ subject.at(@now).should == "068212"
14
+ end
15
+
16
+ # Users of the lib
17
+ it "should not verify a number" do
18
+ expect {
19
+ subject.verify(68212, @now)
20
+ }.to raise_error
13
21
  end
14
- it "should verify a number" do
15
- subject.verify(68212, @now).should be_true
22
+ it "should not verify an unpadded string" do
23
+ subject.verify("68212", @now).should be_false
16
24
  end
17
25
  it "should verify a string" do
18
- subject.verify("68212", @now).should be_true
26
+ subject.verify("068212", @now).should be_true
19
27
  end
20
28
 
21
29
  it "should output its provisioning URI" do
@@ -50,10 +58,10 @@ describe ROTP::TOTP do
50
58
 
51
59
  context "with drift" do
52
60
  it "should verify a number" do
53
- subject.verify_with_drift(68212, 0, @now).should be_true
61
+ subject.verify_with_drift("068212", 0, @now).should be_true
54
62
  end
55
63
  it "should verify a string" do
56
- subject.verify_with_drift("68212", 0, @now).should be_true
64
+ subject.verify_with_drift("068212", 0, @now).should be_true
57
65
  end
58
66
  it "should verify a slightly old number" do
59
67
  subject.verify_with_drift(subject.at(@now - 30), 60, @now).should be_true
@@ -78,30 +86,30 @@ end
78
86
  describe "TOTP example values from the documented output" do
79
87
  it "should match the RFC" do
80
88
  totp = ROTP::TOTP.new("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
81
- totp.at(1111111111).should ==(50471)
82
- totp.at(1234567890).should ==(5924)
83
- totp.at(2000000000).should ==(279037)
89
+ totp.at(1111111111).should ==("050471")
90
+ totp.at(1234567890).should ==("005924")
91
+ totp.at(2000000000).should ==("279037")
84
92
  end
85
93
 
86
94
  it "should match the Google Authenticator output" do
87
95
  totp = ROTP::TOTP.new("wrn3pqx5uqxqvnqr")
88
96
  Timecop.freeze(Time.at(1297553958)) do
89
- totp.now.should ==(102705)
97
+ totp.now.should ==("102705")
90
98
  end
91
99
  end
92
100
  it "should match Dropbox 26 char secret output" do
93
101
  totp = ROTP::TOTP.new("tjtpqea6a42l56g5eym73go2oa")
94
102
  Timecop.freeze(Time.at(1378762454)) do
95
- totp.now.should ==(747864)
103
+ totp.now.should ==("747864")
96
104
  end
97
105
  end
98
106
  it "should validate a time based OTP" do
99
107
  totp = ROTP::TOTP.new("wrn3pqx5uqxqvnqr")
100
108
  Timecop.freeze(Time.at(1297553958)) do
101
- totp.verify(102705).should be_true
109
+ totp.verify("102705").should be_true
102
110
  end
103
111
  Timecop.freeze(Time.at(1297553958 + 30)) do
104
- totp.verify(102705).should be_false
112
+ totp.verify("102705").should be_false
105
113
  end
106
114
  end
107
115
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rotp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Percival
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-03 00:00:00.000000000 Z
11
+ date: 2014-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 10.1.0
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 10.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 2.13.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 2.13.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: timecop
43
43
  requirement: !ruby/object:Gem::Requirement