secure_equals 0.1

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.
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: