secure_equals 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9883242630ffc2552eb1859d0a0b869f218237b
4
+ data.tar.gz: 0222f297a7683a1927b92241e7fde4de2c9618f4
5
+ SHA512:
6
+ metadata.gz: 0ba9b81d792b35adcabbe6163debddf1f8107b11c62b5c12d60cba6d4feab8577ae08a15aaea9bfe692712a5fce9d1d8ad84ecb5c2888a616646381ffdad3700
7
+ data.tar.gz: e66e06d162ac9d9983533330ea9d9e77d873bbbc4f9e63144dedfd5d196eaf02720a3c7dce13dceacd78618a8a48424731caac22959995ede2ccc63ffc60c9e7
data/README.md ADDED
@@ -0,0 +1,27 @@
1
+ Secure equals provides a constant time equality method for ruby strings.
2
+
3
+ By avoiding any branches that depend on the similarity of the two strings,
4
+ we make it significantly harder for an attacker to discover properties of
5
+ the secret by observing the time it takes to run the comparison.
6
+
7
+ You should use this for checking checksums or shared secrets in your code.
8
+
9
+ ## Installation
10
+
11
+ You can either `gem install secure_equals` or add `gem 'secure_equals'` to your Gemfile and then run `bundle install`
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ SecureEquals.equal? checksum, @params[:checksum]
17
+ ```
18
+
19
+ ## TODO
20
+
21
+ I was unable to implement a successful timing attack against ruby's string == on my Mac Book Pro; so while the code here looks promising, I've not actually been able to verify that it actually solves the problem.
22
+
23
+ We should try running the attack on 16- and 32- byte sized chunks instead of the character-by-character mode, as I suspect the problem is memcmp(). Alternatively there might be a way to get higher resolution timings, or to use some stats that I don't know about, to make it work.
24
+
25
+ ## BUGS
26
+
27
+ I'm pretty sure this must already exist somewhere...
@@ -0,0 +1,23 @@
1
+ module SecureEquals
2
+ # Provides an equality method on strings that is not vulnerable to timing attacks.
3
+ #
4
+ # This prevents attackers from being able to guess the answer byte-by-byte using
5
+ # tiny differences in response time.
6
+ #
7
+ # @param [String] The secret string
8
+ # @param [String] The string provided by the user
9
+ # @return [Boolean] Are the strings the same?
10
+ #
11
+ def self.equal?(mine, theirs)
12
+ mine = mine.to_str
13
+ theirs = theirs.to_str
14
+ return false unless mine.length == theirs.length
15
+
16
+ difference = 0
17
+ 0.upto(mine.length - 1) do |i|
18
+ difference |= (mine[i].ord ^ theirs[i].ord)
19
+ end
20
+
21
+ difference == 0
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "secure_equals"
3
+ s.version = "0.1"
4
+ s.platform = Gem::Platform::RUBY
5
+ s.author = "Conrad Irwin"
6
+ s.email = "conrad.irwin@gmail.com"
7
+ s.homepage = "http://github.com/Conradirwin/secure_equals"
8
+ s.summary = "Provides constant time equality for ruby strings"
9
+ s.description = "Constant time equality (also known as time insensitive equality) lets you compare user-provided strings with secrets in a way that does not leak data about those secrets."
10
+ s.files = `git ls-files`.split("\n")
11
+ s.require_path = "lib"
12
+ end
@@ -0,0 +1,80 @@
1
+ require_relative '../lib/secure_equals'
2
+ require 'securerandom'
3
+ require 'hitimes'
4
+ GC.disable
5
+
6
+ class Box
7
+ def initialize
8
+ @secret = '0123456789abcdeffedcba0987654321'
9
+ end
10
+
11
+ # The expected score of a random guess is 0.5.
12
+ # The expected score of the correct answer is 1.
13
+ #
14
+ # If you are able to get scores consistently higher
15
+ # than 0.5 using only the guess method, then you
16
+ # have a successful timing attack.
17
+ def score(str)
18
+ score = 0.0
19
+ mine = @secret.each_byte.map{ |x| x.to_s(2) }.join
20
+ theirs = str.each_byte.map{ |x| x.to_s(2) }.join
21
+
22
+ mine.each_byte.zip(theirs.each_byte) do |a,b|
23
+ score += 1 if a == b
24
+ end
25
+ (score / mine.length)
26
+ end
27
+
28
+ class Weak < Box
29
+ def guess(str)
30
+ 0.upto(@secret.length - 1) do |i|
31
+ return false unless @secret[i] == str[i]
32
+ end
33
+ true
34
+ end
35
+ end
36
+
37
+ class Standard < Box
38
+ def guess(str)
39
+ @secret == str
40
+ end
41
+ end
42
+
43
+ class Secure < Box
44
+ def guess(str)
45
+ SecureEquals.same? @secret, str
46
+ end
47
+ end
48
+ end
49
+
50
+ def brute_force(box, trials)
51
+ scores = []
52
+ 1.times do
53
+ guess = '0' * 32
54
+ (0..32).each do |pos|
55
+ max = 0
56
+ result = nil
57
+ this_time = guess.dup
58
+ 'abcdef0123456789'.each_char do |letter|
59
+ this_time[pos] = letter
60
+ time = Hitimes::Interval.measure do
61
+ trials.times{ box.guess this_time }
62
+ end
63
+ if time > max
64
+ max = time
65
+ result = letter
66
+ end
67
+ end
68
+ guess[pos] = result
69
+ end
70
+ scores << box.score(guess)
71
+ end
72
+
73
+ puts "average: #{scores.inject(&:+) / scores.size}"
74
+ end
75
+
76
+ 10.times do
77
+ brute_force Box::Weak.new, 1000
78
+ brute_force Box::Standard.new, 100000
79
+ brute_force Box::Secure.new, 1000000
80
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: secure_equals
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Conrad Irwin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Constant time equality (also known as time insensitive equality) lets
14
+ you compare user-provided strings with secrets in a way that does not leak data
15
+ about those secrets.
16
+ email: conrad.irwin@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.md
22
+ - lib/secure_equals.rb
23
+ - secure_equals.gemspec
24
+ - test/timing_attack.rb
25
+ homepage: http://github.com/Conradirwin/secure_equals
26
+ licenses: []
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.0.3
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Provides constant time equality for ruby strings
48
+ test_files: []
49
+ has_rdoc: