summary refs log tree commit
diff options
context:
space:
mode:
authorJames Tucker <jftucker@gmail.com>2013-02-06 15:31:53 -0800
committerJames Tucker <jftucker@gmail.com>2013-02-07 14:40:17 -0800
commitfa2db31472d4bb32700b6f1d18a6884fd32e6856 (patch)
treeab434e2ab8119b0f23a2f42dc748de6716cbbe23
parent22ef9e18198eaa4cb7c6c38df296a2fa7c5d8581 (diff)
downloadrack-fa2db31472d4bb32700b6f1d18a6884fd32e6856.tar.gz
Add secure_compare to Rack::Utils
Conflicts:
	lib/rack/utils.rb
	test/spec_utils.rb
-rw-r--r--lib/rack/utils.rb25
-rw-r--r--test/spec_utils.rb7
2 files changed, 31 insertions, 1 deletions
diff --git a/lib/rack/utils.rb b/lib/rack/utils.rb
index 7dff2873..282fca93 100644
--- a/lib/rack/utils.rb
+++ b/lib/rack/utils.rb
@@ -298,6 +298,31 @@ module Rack
     end
     module_function :rfc2822
 
+    # Return the bytesize of String; uses String#length under Ruby 1.8 and
+    # String#bytesize under 1.9.
+    if ''.respond_to?(:bytesize)
+      def bytesize(string)
+        string.bytesize
+      end
+    else
+      def bytesize(string)
+        string.size
+      end
+    end
+    module_function :bytesize
+
+    # Constant time string comparison.
+    def secure_compare(a, b)
+      return false unless bytesize(a) == bytesize(b)
+
+      l = a.unpack("C*")
+
+      r, i = 0, -1
+      b.each_byte { |v| r |= v ^ l[i+=1] }
+      r == 0
+    end
+    module_function :secure_compare
+
     # Context allows the use of a compatible middleware at different points
     # in a request handling stack. A compatible middleware must define
     # #context which should take the arguments env and app. The first of which
diff --git a/test/spec_utils.rb b/test/spec_utils.rb
index 92770e35..5f234f1d 100644
--- a/test/spec_utils.rb
+++ b/test/spec_utils.rb
@@ -274,7 +274,12 @@ describe Rack::Utils do
     Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
   end
 
-  should "return status code for integer" do
+  should "should perform constant time string comparison" do
+    Rack::Utils.secure_compare('a', 'a').should.equal true
+    Rack::Utils.secure_compare('a', 'b').should.equal false
+  end
+
+  should "should return status code for integer" do
     Rack::Utils.status_code(200).should.equal 200
   end