Skip to content

Commit

Permalink
Added a "secure_equals" method to Helper to avoid timing attacks as m…
Browse files Browse the repository at this point in the history
  • Loading branch information
pelle committed Aug 13, 2009
1 parent 41931cb commit c867394
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 8 deletions.
1 change: 1 addition & 0 deletions History.txt
Expand Up @@ -2,6 +2,7 @@

* Added -B CLI option to use the :body authentication scheme (Seth)
* Added :ca_file consumer option to allow consumer specific certificate override. (Pelle)
* Added a secure_equals in Helper to prevent timing attacks. (Pelle)

== 0.3.5 2009-06-03

Expand Down
5 changes: 4 additions & 1 deletion Rakefile
@@ -1,10 +1,13 @@
%w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
%w[rubygems rake rake/clean fileutils].each { |f| require f }
$LOAD_PATH << File.dirname(__FILE__) + '/lib'
require 'oauth'
require 'oauth/version'

begin
require 'hoe'
require 'newgem'
require 'rubigen'

# Generate all the Rake tasks
# Run 'rake -T' to see list of generated tasks (from gem root directory)
$hoe = Hoe.new('oauth', OAuth::VERSION) do |p|
Expand Down
24 changes: 23 additions & 1 deletion lib/oauth/helper.rb
@@ -1,5 +1,6 @@
require 'openssl'
require 'base64'
require 'enumerator'

module OAuth
module Helper
Expand Down Expand Up @@ -70,9 +71,30 @@ def parse_header(header)
# convert into a Hash
Hash[*params.flatten]
end


# A secure version of equals meant to avoid timing attacks as specified here
# http://codahale.com/a-lesson-in-timing-attacks/
def secure_equals(a,b)
return a==b unless a.is_a?(String)&&b.is_a?(String)
result = 0
bytes(a).zip(bytes(b)).each do |x,y|
result |= (x ^ y)
end
(result == 0) && (a.length == b.length)
end

def unescape(value)
URI.unescape(value.gsub('+', '%2B'))
end

# Creates a per byte enumerator for a string regardless of RUBY VERSION
def bytes(a)
return [] if a.nil?
if a.respond_to?(:bytes)
a.bytes
else
Enumerable::Enumerator.new(a, :each_byte)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/oauth/signature/base.rb
Expand Up @@ -55,7 +55,7 @@ def signature
end

def ==(cmp_signature)
Base64.decode64(signature) == Base64.decode64(cmp_signature)
secure_equals(Base64.decode64(signature), Base64.decode64(cmp_signature))
end

def verify
Expand Down
2 changes: 1 addition & 1 deletion lib/oauth/signature/plaintext.rb
Expand Up @@ -9,7 +9,7 @@ def signature
end

def ==(cmp_signature)
signature == escape(cmp_signature)
secure_equals(signature , escape(cmp_signature))
end

def signature_base_string
Expand Down
2 changes: 1 addition & 1 deletion oauth.gemspec
Expand Up @@ -2,7 +2,7 @@

Gem::Specification.new do |s|
s.name = %q{oauth}
s.version = "0.3.5"
s.version = "0.3.6"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Pelle Braendgaard", "Blaine Cook", "Larry Halff", "Jesse Clark", "Jon Crosby", "Seth Fitzsimmons", "Matt Sanford"]
Expand Down
22 changes: 19 additions & 3 deletions test/test_signature.rb
@@ -1,19 +1,35 @@
# encoding: utf-8
require File.dirname(__FILE__) + '/test_helper.rb'

class TestOauth < Test::Unit::TestCase

include OAuth::Helper

def test_parameter_escaping_kcode_invariant
old = $KCODE
begin
%w(n N e E s S u U).each do |kcode|
$KCODE = kcode
assert_equal '%E3%81%82', OAuth::Helper.escape('あ'),
assert_equal '%E3%81%82', OAuth::Helper.escape("あ"),
"Failed to correctly escape Japanese under $KCODE = #{kcode}"
assert_equal '%C3%A9', OAuth::Helper.escape('é'),
assert_equal '%C3%A9', OAuth::Helper.escape("é"),
"Failed to correctly escape e+acute under $KCODE = #{kcode}"
end
ensure
$KCODE = old
end
end

def test_secure_equals
[nil,1,12345,"12345",'1','Hello'*45].each do |value|
assert secure_equals(value,value)
end
end

def test_not_secure_equals
a=[nil,1,12345,"12345",'1','Hello'*45]
a.zip(a.reverse).each do |a,b|
assert !secure_equals(a,b)
end
end

end

0 comments on commit c867394

Please sign in to comment.