obfuscurity 1.0.0

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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in obfuscurity.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Graham Ashton
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,116 @@
1
+ # Obfuscurity
2
+
3
+ Have you ever needed to generate a unique number for a piece of data (a
4
+ little like a primary key) that's exposed to your customers? You might
5
+ use your database's primary key, but you don't want to expose an
6
+ internal counter that could give away sensitive information.
7
+
8
+ An **order number in a billing system** is a good example; until you've
9
+ processed thousands of orders you won't want people to know just how
10
+ young your business it (in some cases, it doesn't instil confidence).
11
+
12
+ Or perhaps you don't want your competitors to be able to work out **how
13
+ many people have signed up for your web app**, but want to include a
14
+ customer number in the URL? If you were to use an incrementing field
15
+ from your database, that information becomes painfully apparent.
16
+
17
+ The obvious solution (generating a random number and checking to see
18
+ whether it's already in use) feels like a hack, and performance starts
19
+ to suffer (because you find yourself generating numbers that have
20
+ already been used) quicker than you might expect.
21
+
22
+ Luckily, there's an easy solution, as outlined in [this comment][comment]
23
+ on StackOverflow. Just in case you don't have access to StackOverflow
24
+ right now, this is what it says:
25
+
26
+ > Pick a 8 or 9 digit number at random, say 839712541. Then, take your
27
+ > order number's binary representation (for this example, I'm not using
28
+ > 2's complement), pad it out to the same number of bits (30), reverse it,
29
+ > and xor the flipped order number and the magic number.
30
+
31
+ [comment]: http://stackoverflow.com/a/612085/158841
32
+
33
+ (Don't worry if you didn't follow that)
34
+
35
+ It's rather ingenius, and allows you to take a random seed (e.g.
36
+ 839712541) and a incrementing series of numbers, and convert them into a
37
+ seemingly random series. You can then convert them back again simply by
38
+ reversing the approach.
39
+
40
+ Of course, this **isn't** a secure approach. Anybody with a computer and
41
+ plenty of time would be able to work out the pattern.
42
+
43
+ If you've got some numbers that you seriously need to protect, use
44
+ cryptography. Obscurity provides [no security at all][wikipedia].
45
+
46
+ [wikipedia]: http://en.wikipedia.org/wiki/Security_through_obscurity
47
+
48
+ ## Installation
49
+
50
+ Add this line to your application's Gemfile:
51
+
52
+ gem 'obfuscurity'
53
+
54
+ And then execute:
55
+
56
+ $ bundle
57
+
58
+ Or install it yourself as:
59
+
60
+ $ gem install obfuscurity
61
+
62
+ ## Usage
63
+
64
+ To obfuscate the number you want to hide, make yourself a `Baffler`:
65
+
66
+ baffler = Obfuscurity::Baffler.new
67
+ baffler.obfuscate(1) # -> 302841629
68
+ baffler.obfuscate(2) # -> 571277085
69
+
70
+ If you later want to convert back to your primary key, use the clarify
71
+ method:
72
+
73
+ baffler.clarify(302841629) # -> 1
74
+ baffler.clarify(571277085) # -> 2
75
+
76
+ The `Baffler` object just happens to convert `1` to `302841629` because
77
+ of the seed that it's using (see the [StackOverflow comment][comment]
78
+ for details).
79
+
80
+ ### Configuring behaviour
81
+
82
+ If you'd like to use a different sequence specify a different seed when
83
+ you create the `Baffler` instance:
84
+
85
+ baffler = Obfuscurity::Baffler.new(seed: 61493749)
86
+
87
+ You'll no doubt have noticed that the numbers produced by default are
88
+ rather large. The algorithm uses a fixed number of bits (30 by default),
89
+ so any number as large as `2 ** 30` could be returned by the `obfuscate`
90
+ method.
91
+
92
+ If you know you won't need anything like that many unique numbers you
93
+ can reduce the number of bits:
94
+
95
+ baffler = Obfuscurity::Baffler.new(max_bits: 16)
96
+ baffler.obfuscate(42) # -> 21505
97
+
98
+ Just be aware that the maximum number of unique numbers that you can
99
+ cope with is `2 ** max_bits`, or in the case of 16 bits, just 32768
100
+ (which isn't a lot).
101
+
102
+ If you attempt to obfuscate a number that's too big to fit in the number
103
+ space available (i.e. you exceed the value set for `max_bits`) a
104
+ `Obfuscurity::Error` exception will be raised.
105
+
106
+ ## Contributing
107
+
108
+ 1. Fork it
109
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
110
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
111
+ 4. Push to the branch (`git push origin my-new-feature`)
112
+ 5. Create new Pull Request
113
+
114
+ ## Credits
115
+
116
+ Thanks to @benlovell for suggesting the name.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,43 @@
1
+ require 'obfuscurity/version'
2
+
3
+ module Obfuscurity
4
+ class Error < RuntimeError; end
5
+
6
+ class Baffler
7
+ def initialize(params = {})
8
+ @seed = params.fetch(:seed, 839712541)
9
+ @max_bits = params.fetch(:max_bits, 30)
10
+ check_size_of_number_space(@seed)
11
+ end
12
+
13
+ def obfuscate(number)
14
+ check_size_of_number_space(number)
15
+ seed_bits = number_to_bits(@seed)
16
+ xor_bits = (0 ... seed_bits.size).map { |i| number[i] ^ seed_bits[i] }
17
+ bits_to_number(xor_bits)
18
+ end
19
+
20
+ def clarify(number)
21
+ bits = number_to_bits(number ^ @seed)
22
+ bits_to_number(bits.reverse)
23
+ end
24
+
25
+ private
26
+ def number_to_bits(number)
27
+ [].tap do |bits|
28
+ (@max_bits - 1).downto(0) { |i| bits << number[i] }
29
+ end
30
+ end
31
+
32
+ def bits_to_number(bits)
33
+ bits.inject(0) { |result, bit| (result << 1) | bit }
34
+ end
35
+
36
+ def check_size_of_number_space(number)
37
+ root = number ** (1.0 / @max_bits)
38
+ if root > 2
39
+ raise Error.new("#{number} requires more than #{@max_bits} bits")
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Obfuscurity
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'obfuscurity/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "obfuscurity"
8
+ gem.version = Obfuscurity::VERSION
9
+ gem.authors = ["Graham Ashton"]
10
+ gem.email = ["graham@effectif.com"]
11
+ gem.description = <<-EOF
12
+ Sometimes exposing your app's internal counters (e.g. your database's
13
+ auto-incrementing primary keys) to the world is a bad idea. Maybe your
14
+ competitors will be able to work out how many orders you're making per
15
+ week, or your customers will be able to infer how many other customers
16
+ you've got. This gem will allow you to obscure those numbers so you can
17
+ use them in your URLs, your user interface, or as seemingly random order
18
+ numbers.
19
+ EOF
20
+ gem.summary = %q{Obfuscate your database ids, converting them to (seemingly) random numbers}
21
+ gem.homepage = "https://github.com/gma/obfuscurity"
22
+
23
+ gem.files = `git ls-files`.split($/)
24
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
25
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
26
+ gem.require_paths = ["lib"]
27
+
28
+ gem.add_development_dependency('nutrasuite')
29
+ gem.add_development_dependency('rake')
30
+ end
@@ -0,0 +1,52 @@
1
+ require 'test/unit'
2
+ require 'nutrasuite'
3
+
4
+ require_relative '../lib/obfuscurity'
5
+
6
+ class BafflerTest < MiniTest::Unit::TestCase
7
+ a 'Baffler' do
8
+ it 'should generate unique (repeatable) number from integer' do
9
+ # Wondering why the results are 302841629 and 571277085? See this
10
+ # thread on Stack Overflow:
11
+ #
12
+ # http://stackoverflow.com/questions/1179439/best-way-to-generate-order-numbers-for-an-online-store
13
+
14
+ 2.times do
15
+ assert_equal 302841629, Obfuscurity::Baffler.new.obfuscate(1)
16
+ end
17
+ assert_equal 571277085, Obfuscurity::Baffler.new.obfuscate(2)
18
+ end
19
+
20
+ it 'should convert an obfuscated number back into the original integer' do
21
+ baffler = Obfuscurity::Baffler.new
22
+
23
+ assert_equal 1, baffler.clarify(302841629)
24
+ assert_equal 2, baffler.clarify(571277085)
25
+
26
+ 10.times do |i|
27
+ n = baffler.obfuscate(i)
28
+ assert_equal i, baffler.clarify(n)
29
+ end
30
+ end
31
+
32
+ it 'allow you to control the size of the number space' do
33
+ baffler = Obfuscurity::Baffler.new(max_bits: 16, seed: 1)
34
+ assert_equal 21505, baffler.obfuscate(42)
35
+ end
36
+
37
+ it "should ensure that seed doesn't exceed available bits" do
38
+ max_bits = 30
39
+ too_big = (2 ** max_bits) + 1
40
+ assert_raises(Obfuscurity::Error) do
41
+ Obfuscurity::Baffler.new(max_bits: max_bits, seed: too_big)
42
+ end
43
+ end
44
+
45
+ it "should ensure that obscured numbers don't exceed available bits" do
46
+ baffler = Obfuscurity::Baffler.new
47
+ assert_raises(Obfuscurity::Error) do
48
+ baffler.obfuscate((2 ** 30) + 1)
49
+ end
50
+ end
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: obfuscurity
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Graham Ashton
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nutrasuite
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: ! 'Sometimes exposing your app''s internal counters (e.g. your database''s
47
+
48
+ auto-incrementing primary keys) to the world is a bad idea. Maybe your
49
+
50
+ competitors will be able to work out how many orders you''re making per
51
+
52
+ week, or your customers will be able to infer how many other customers
53
+
54
+ you''ve got. This gem will allow you to obscure those numbers so you can
55
+
56
+ use them in your URLs, your user interface, or as seemingly random order
57
+
58
+ numbers.
59
+
60
+ '
61
+ email:
62
+ - graham@effectif.com
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - .gitignore
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - lib/obfuscurity.rb
73
+ - lib/obfuscurity/version.rb
74
+ - obfuscurity.gemspec
75
+ - test/obfuscator_test.rb
76
+ homepage: https://github.com/gma/obfuscurity
77
+ licenses: []
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ segments:
89
+ - 0
90
+ hash: 2796161447218621274
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ segments:
98
+ - 0
99
+ hash: 2796161447218621274
100
+ requirements: []
101
+ rubyforge_project:
102
+ rubygems_version: 1.8.23
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Obfuscate your database ids, converting them to (seemingly) random numbers
106
+ test_files:
107
+ - test/obfuscator_test.rb