id_shuffler 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile('id_shuffler/id_shuffler')
4
+
@@ -0,0 +1,110 @@
1
+ #include <ruby.h>
2
+
3
+ static VALUE r_base32_shuffle(VALUE self, VALUE original_num, VALUE key_str) {
4
+ // takes a number and a key string as input and returns a base-32 encoded
5
+ // shuffled id.
6
+
7
+ // convert from ruby objects to local long/string
8
+ unsigned long original;
9
+ original = NUM2ULONG(original_num);
10
+ char *key = StringValuePtr(key_str);
11
+ // bounds check the id
12
+ if (original >= (1 << 30)) {
13
+ return rb_str_new2("");
14
+ }
15
+ // initialize the feistel network and lfsr
16
+ unsigned int keypos, key_len;
17
+ unsigned long l, r, r_orig;
18
+ unsigned long lfsr_bit, lfsr_outbit, lfsr_num;
19
+ l = (original >> 15) & 32767;
20
+ r = original & 32767;
21
+ key_len = strlen(key);
22
+ // apply the feistel network in a loop over each char in key. the round
23
+ // function uses the key char as the taps mask for a single iteration of an
24
+ // lfsr.
25
+ for (keypos = 0; keypos < key_len; keypos++) {
26
+ r_orig = r;
27
+ lfsr_num = r+key[keypos];
28
+ lfsr_outbit = 0;
29
+ for (lfsr_bit = 0; lfsr_bit < 15; lfsr_bit++) {
30
+ if ((key[keypos] & (1 >> lfsr_bit)) > 0) {
31
+ lfsr_outbit = lfsr_outbit ^ ((lfsr_num & (1 >> lfsr_bit)) >> lfsr_bit);
32
+ }
33
+ }
34
+ lfsr_num = (lfsr_num >> 1) + (lfsr_outbit << 14);
35
+ lfsr_num = lfsr_num & 32767;
36
+ r = (l ^ lfsr_num);
37
+ l = r_orig;
38
+ }
39
+ // base32 encode the result
40
+ unsigned long shuffled;
41
+ shuffled = (l << 15) + r;
42
+ char buf[6];
43
+ char *digits = "0123456789abcdefghijklmnopqrstuv";
44
+ int cpos;
45
+ for (cpos = 0; cpos < 6; cpos ++) {
46
+ buf[cpos] = digits[((shuffled >> (5*(5-cpos))) & 31)];
47
+ }
48
+ // return a ruby string
49
+ return rb_str_new2(buf);
50
+ }
51
+
52
+ static VALUE r_base32_unshuffle(VALUE self, VALUE shuffled_str, VALUE key_str) {
53
+ // take the a base-32 encoded ruby string and a key string, decodes and
54
+ // unshuffles it, producing the original number.
55
+
56
+ // convert from ruby string to local buf
57
+ char *b32 = StringValuePtr(shuffled_str);
58
+ char *key = StringValuePtr(key_str);
59
+ // remove base32 encoding
60
+ unsigned int shuffled = 0;
61
+ char *found;
62
+ char *digits = "0123456789abcdefghijklmnopqrstuv";
63
+ if (strlen(b32) != 6) {
64
+ return INT2NUM(-1);
65
+ }
66
+ int cpos = 0;
67
+ for (cpos = 0; cpos < 6; cpos++) {
68
+ found = strchr(digits,b32[cpos]);
69
+ if (found) {
70
+ int pos = (found - digits);
71
+ shuffled = shuffled + (pos << (5 * (5-cpos)));
72
+ } else {
73
+ return INT2NUM(-1);
74
+ }
75
+ }
76
+ // iterate the feistel network in reverse
77
+ int keypos,key_len;
78
+ unsigned long l, r, l_orig;
79
+ unsigned long lfsr_bit, lfsr_outbit, lfsr_num;
80
+ l = (shuffled >> 15) & 32767;
81
+ r = shuffled & 32767;
82
+ key_len = strlen(key);
83
+ for (keypos = (key_len - 1); keypos >= 0; keypos--) {
84
+ l_orig = l;
85
+ lfsr_num = l+key[keypos];
86
+ lfsr_outbit = 0;
87
+ for (lfsr_bit = 0; lfsr_bit < 15; lfsr_bit++) {
88
+ if ((key[keypos] & (1 >> lfsr_bit)) > 0) {
89
+ lfsr_outbit = lfsr_outbit ^ ((lfsr_num & (1 >> lfsr_bit)) >> lfsr_bit);
90
+ }
91
+ }
92
+ lfsr_num = (lfsr_num >> 1) + (lfsr_outbit << 14);
93
+ lfsr_num = lfsr_num & 32767;
94
+ l = (r ^ lfsr_num);
95
+ r = l_orig;
96
+ }
97
+ // return the resulting original number as a ruby fixnum
98
+ unsigned long original;
99
+ original = (l << 15) + r;
100
+ return ULONG2NUM(original);
101
+ }
102
+
103
+ void Init_id_shuffler(void) {
104
+ // add two class methods to our IdShuffler class: shuffle and unshuffle, each
105
+ // of which take the object to be [unshuffled] and the key.
106
+ VALUE klass = rb_define_class("IdShuffler", rb_cObject);
107
+ rb_define_singleton_method(klass, "shuffle", r_base32_shuffle, 2);
108
+ rb_define_singleton_method(klass, "unshuffle", r_base32_unshuffle, 2);
109
+ }
110
+
@@ -0,0 +1,28 @@
1
+ # load the c extension
2
+ require 'id_shuffler/id_shuffler'
3
+
4
+ # key strings are hashed via md5 to make them a good length (the shuffle algo
5
+ # iterates once per character in the key) as well as to provide good spread of
6
+ # bits per character (whearas a plain text ascii key would only use a narrow
7
+ # range of bits)
8
+ require 'digest/md5'
9
+
10
+ class IdShuffler
11
+
12
+ def initialize(key)
13
+ @key = Digest::MD5.digest(key)
14
+ end
15
+
16
+ def key=(key)
17
+ @key = Digest::MD5.digest(key)
18
+ end
19
+
20
+ def shuffle(i)
21
+ self.class.shuffle(i.to_i, @key)
22
+ end
23
+
24
+ def unshuffle(s)
25
+ self.class.unshuffle(s.to_s, @key)
26
+ end
27
+
28
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: id_shuffler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Josh Whiting
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-20 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! '
15
+
16
+ An efficient solution to use when it is undesirable to expose internal database
17
+
18
+ ids, IdShuffler converts integers like 123 into strings like ''q34nr1'', and
19
+
20
+ vice-versa, using a very lightweight integer scrambling algorithm plus
21
+
22
+ ''Crockford 32'' encoding. It is built as a native C extension and so is very
23
+
24
+ fast. The algorithm takes a string key as a seed, so you can use different
25
+
26
+ keys for different id spaces and thus obtain different slugs for the same
27
+
28
+ initial integer. This is not a security solution and I am not a cryptographer;
29
+
30
+ it should be assumed a determined individual can unshuffle the ids without
31
+
32
+ knowing the key used to generate them. Also note these are 30-bit ids, so the
33
+
34
+ library can only represent values up to approximately 1 billion
35
+
36
+ (1,073,741,823).
37
+
38
+
39
+ This gem is still under development in so far as I have not written tests or
40
+
41
+ documentation for it.
42
+
43
+
44
+ '
45
+ email: josh@protagonistlabs.com
46
+ executables: []
47
+ extensions:
48
+ - ext/id_shuffler/extconf.rb
49
+ extra_rdoc_files: []
50
+ files:
51
+ - lib/id_shuffler.rb
52
+ - ext/id_shuffler/id_shuffler.c
53
+ - ext/id_shuffler/extconf.rb
54
+ homepage: http://rubygems.org/gems/id_shuffler
55
+ licenses:
56
+ - MIT
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ! '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 1.8.23
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Turns private database ids into lightly-encrypted public-facing strings
79
+ test_files: []