Permalink
Browse files

files

  • Loading branch information...
1 parent 8c644c5 commit 2e2158c304da0147c2df3bb399c2f091b0fdd6ef @JordanReiter committed Jun 21, 2011
View
7 README
@@ -0,0 +1,7 @@
+To install:
+
+`$ python setup.py install`
+
+To configure, make sure to put ID_OBFUSCATOR_KEY in the settings. You can generate a key by running id_obfuscator.utils.generate_secret_key()
+
+Unlike a simple base conversion like Base58, id_obfuscator also uses an obfuscator key which ensures that adjacent ids are not simply off by one letter or number. Note that it is not secure and with enough ids with corresponding numbers, a hacker could figure out the correct ID. This is therefore recommended for lower security use.
View
No changes.
No changes.
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+##
+## Copyright 2009 Adriana Lukas & Alec Muffett
+##
+## Licensed under the Apache License, Version 2.0 (the "License"); you
+## may not use this file except in compliance with the License. You
+## may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+## implied. See the License for the specific language governing
+## permissions and limitations under the License.
+##
+
+"""docstring goes here""" # :-)
+
+# spec: http://www.flickr.com/groups/api/discuss/72157616713786392/
+
+__b58chars = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
+__b58base = len(__b58chars) # let's not bother hard-coding
+
+def b58encode(value):
+ """
+ encode integer 'value' as a base58 string; returns string
+ """
+
+ encoded = ''
+ while value >= __b58base:
+ div, mod = divmod(value, __b58base)
+ encoded = __b58chars[mod] + encoded # add to left
+ value = div
+ encoded = __b58chars[value] + encoded # most significant remainder
+ return encoded
+
+def b58decode(encoded):
+ """
+ decodes base58 string 'encoded' to return integer
+ """
+
+ value = 0
+ column_multiplier = 1;
+ for c in encoded[::-1]:
+ column = __b58chars.index(c)
+ value += column * column_multiplier
+ column_multiplier *= __b58base
+ return value
+
+if __name__ == '__main__':
+ x = b58encode(12345678)
+ print x, '26gWw'
+ print b58decode(x), 12345678
+
+characters = __b58chars
+base = __b58base
No changes.
@@ -0,0 +1,39 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+import sys
+from django.test import TestCase
+from utils import obfuscate, deobfuscate
+
+class SimpleTest(TestCase):
+
+ def test_correctly_hashes(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ seen_hashes = set()
+ collisions = 0
+ max_num = 1000000
+ for k in range(0, max_num):
+ o = obfuscate(k)
+ d = deobfuscate(o)
+ if d in seen_hashes:
+ collisions += 1
+ seen_hashes.add(d)
+ self.assertEquals(k, d, "Incorrect hash: %s -> %s -> %s " % (k, o, d))
+ self.assertEqual(collisions, 0, "There were %d collisions between 0 and %d" % (collisions, max_num))
+
+ for k in range(0, 4294967295, 1048576):
+ o = obfuscate(k)
+ d = deobfuscate(o)
+ self.assertEquals(k, d, "Incorrect hash: %s -> %s -> %s " % (k, o, d))
+
+ def test_bad_inputs(self):
+ self.assertRaises(ValueError, obfuscate, -1)
+ self.assertRaises(ValueError, obfuscate, "noninteger")
+ self.assertRaises(ValueError, deobfuscate, "!@#$%^")
+ self.assertRaises(ValueError, deobfuscate, "Zd..4")
@@ -0,0 +1,58 @@
+from django.conf import settings
+import random, re
+from id_obfuscator import base58
+
+def generate_secret_key(length=40):
+ random.sample(base58.characters, length)
+
+def obfuscate(i, length=8, key=settings.ID_OBFUSCATOR_KEY):
+ i = int(i)
+ if i < 0:
+ raise ValueError("Input must be a positive integer.")
+ result = ""
+ pos = i % len(key)
+ combine_key = key[pos:] + key[:pos]
+ asc = base58.b58encode(i)
+ pos_asc = base58.b58encode(pos)
+ if len(asc) < length:
+ asc = ("0" * (length-len(asc))) + asc
+ for cpos in range(0, len(asc)):
+ a_lookup = base58.characters.find(asc[cpos])
+ k_lookup = base58.characters.find(combine_key[cpos])
+ k = a_lookup + k_lookup
+ if k == -1:
+ result += '0'
+ continue
+ k = (k + base58.base) % base58.base
+ #print asc[cpos], "(%d) + " % a_lookup , combine_key[cpos], "(%d) =" % k_lookup, base58.characters[k], "(%d)" % k
+ if k - k_lookup == -1 and asc[cpos] != "0":
+ result += "."
+ result += base58.characters[k]
+ result = pos_asc + result
+ return result
+
+VALID_HASH_FORMAT=r"^[0%(chars)s](\.?[0%(chars)s])+$" % {'chars': base58.characters}
+def deobfuscate(s, key=settings.ID_OBFUSCATOR_KEY):
+ if not re.search(VALID_HASH_FORMAT, s):
+ raise ValueError("Invalid hash value: %s" % s)
+ pos = base58.b58decode(s[0])
+ combine_key = key[pos:] + key[:pos]
+ #print "The combine key is %s" % combine_key
+ s = s[1:]
+ asc = ""
+ for cpos in range(0, len(s.replace(".",""))):
+ if s[cpos] == ".":
+ treat_as_number = True
+ s = s[:cpos] + s[cpos+1:]
+ else:
+ treat_as_number = False
+ k = base58.characters.find(s[cpos])
+ k = k - base58.characters.find(combine_key[cpos])
+ if not treat_as_number and (k == -1 or s[cpos] == "0"):
+ #print s[cpos], "(%d) - " % base58.characters.find(s[cpos]) , combine_key[cpos], "(%d) =" % base58.characters.find(combine_key[cpos]), base58.characters[k], "(%d)" % k
+ continue
+ k = (k + base58.base) % base58.base
+ #print s[cpos], "(%d) - " % base58.characters.find(s[cpos]) , combine_key[cpos], "(%d) =" % base58.characters.find(combine_key[cpos]), base58.characters[k], "(%d)" % k
+ asc += base58.characters[k]
+ result = base58.b58decode(asc)
+ return result
@@ -0,0 +1 @@
+# Create your views here.
No changes.
View
Binary file not shown.
View
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+##
+## Copyright 2009 Adriana Lukas & Alec Muffett
+##
+## Licensed under the Apache License, Version 2.0 (the "License"); you
+## may not use this file except in compliance with the License. You
+## may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+## implied. See the License for the specific language governing
+## permissions and limitations under the License.
+##
+
+"""docstring goes here""" # :-)
+
+# spec: http://www.flickr.com/groups/api/discuss/72157616713786392/
+
+__b58chars = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
+__b58base = len(__b58chars) # let's not bother hard-coding
+
+def b58encode(value):
+ """
+ encode integer 'value' as a base58 string; returns string
+ """
+
+ encoded = ''
+ while value >= __b58base:
+ div, mod = divmod(value, __b58base)
+ encoded = __b58chars[mod] + encoded # add to left
+ value = div
+ encoded = __b58chars[value] + encoded # most significant remainder
+ return encoded
+
+def b58decode(encoded):
+ """
+ decodes base58 string 'encoded' to return integer
+ """
+
+ value = 0
+ column_multiplier = 1;
+ for c in encoded[::-1]:
+ column = __b58chars.index(c)
+ value += column * column_multiplier
+ column_multiplier *= __b58base
+ return value
+
+if __name__ == '__main__':
+ x = b58encode(12345678)
+ print x, '26gWw'
+ print b58decode(x), 12345678
+
+characters = __b58chars
+base = __b58base
View
Binary file not shown.
View
No changes.
View
Binary file not shown.
View
@@ -0,0 +1,39 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+import sys
+from django.test import TestCase
+from utils import obfuscate, deobfuscate
+
+class SimpleTest(TestCase):
+
+ def test_correctly_hashes(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ seen_hashes = set()
+ collisions = 0
+ max_num = 1000000
+ for k in range(0, max_num):
+ o = obfuscate(k)
+ d = deobfuscate(o)
+ if d in seen_hashes:
+ collisions += 1
+ seen_hashes.add(d)
+ self.assertEquals(k, d, "Incorrect hash: %s -> %s -> %s " % (k, o, d))
+ self.assertEqual(collisions, 0, "There were %d collisions between 0 and %d" % (collisions, max_num))
+
+ for k in range(0, 4294967295, 1048576):
+ o = obfuscate(k)
+ d = deobfuscate(o)
+ self.assertEquals(k, d, "Incorrect hash: %s -> %s -> %s " % (k, o, d))
+
+ def test_bad_inputs(self):
+ self.assertRaises(ValueError, obfuscate, -1)
+ self.assertRaises(ValueError, obfuscate, "noninteger")
+ self.assertRaises(ValueError, deobfuscate, "!@#$%^")
+ self.assertRaises(ValueError, deobfuscate, "Zd..4")
View
Binary file not shown.
View
@@ -0,0 +1,58 @@
+from django.conf import settings
+import random, re
+from id_obfuscator import base58
+
+def generate_secret_key(length=40):
+ random.sample(base58.characters, length)
+
+def obfuscate(i, length=8, key=settings.ID_OBFUSCATOR_KEY):
+ i = int(i)
+ if i < 0:
+ raise ValueError("Input must be a positive integer.")
+ result = ""
+ pos = i % len(key)
+ combine_key = key[pos:] + key[:pos]
+ asc = base58.b58encode(i)
+ pos_asc = base58.b58encode(pos)
+ if len(asc) < length:
+ asc = ("0" * (length-len(asc))) + asc
+ for cpos in range(0, len(asc)):
+ a_lookup = base58.characters.find(asc[cpos])
+ k_lookup = base58.characters.find(combine_key[cpos])
+ k = a_lookup + k_lookup
+ if k == -1:
+ result += '0'
+ continue
+ k = (k + base58.base) % base58.base
+ #print asc[cpos], "(%d) + " % a_lookup , combine_key[cpos], "(%d) =" % k_lookup, base58.characters[k], "(%d)" % k
+ if k - k_lookup == -1 and asc[cpos] != "0":
+ result += "."
+ result += base58.characters[k]
+ result = pos_asc + result
+ return result
+
+VALID_HASH_FORMAT=r"^[0%(chars)s](\.?[0%(chars)s])+$" % {'chars': base58.characters}
+def deobfuscate(s, key=settings.ID_OBFUSCATOR_KEY):
+ if not re.search(VALID_HASH_FORMAT, s):
+ raise ValueError("Invalid hash value: %s" % s)
+ pos = base58.b58decode(s[0])
+ combine_key = key[pos:] + key[:pos]
+ #print "The combine key is %s" % combine_key
+ s = s[1:]
+ asc = ""
+ for cpos in range(0, len(s.replace(".",""))):
+ if s[cpos] == ".":
+ treat_as_number = True
+ s = s[:cpos] + s[cpos+1:]
+ else:
+ treat_as_number = False
+ k = base58.characters.find(s[cpos])
+ k = k - base58.characters.find(combine_key[cpos])
+ if not treat_as_number and (k == -1 or s[cpos] == "0"):
+ #print s[cpos], "(%d) - " % base58.characters.find(s[cpos]) , combine_key[cpos], "(%d) =" % base58.characters.find(combine_key[cpos]), base58.characters[k], "(%d)" % k
+ continue
+ k = (k + base58.base) % base58.base
+ #print s[cpos], "(%d) - " % base58.characters.find(s[cpos]) , combine_key[cpos], "(%d) =" % base58.characters.find(combine_key[cpos]), base58.characters[k], "(%d)" % k
+ asc += base58.characters[k]
+ result = base58.b58decode(asc)
+ return result
View
Binary file not shown.
View
@@ -0,0 +1 @@
+# Create your views here.
View
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+import imp
+try:
+ imp.find_module('settings') # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
+ sys.exit(1)
+
+import settings
+
+if __name__ == "__main__":
+ execute_manager(settings)
Oops, something went wrong.

0 comments on commit 2e2158c

Please sign in to comment.