Skip to content

Commit

Permalink
files
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanReiter committed Jun 21, 2011
1 parent 8c644c5 commit 2e2158c
Show file tree
Hide file tree
Showing 23 changed files with 513 additions and 0 deletions.
7 changes: 7 additions & 0 deletions 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.
Empty file added __init__.py
Empty file.
Empty file.
57 changes: 57 additions & 0 deletions build/lib/id_obfuscator/base58.py
@@ -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
Empty file.
39 changes: 39 additions & 0 deletions build/lib/id_obfuscator/tests.py
@@ -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")
58 changes: 58 additions & 0 deletions build/lib/id_obfuscator/utils.py
@@ -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
1 change: 1 addition & 0 deletions build/lib/id_obfuscator/views.py
@@ -0,0 +1 @@
# Create your views here.
Empty file added id_obfuscator/__init__.py
Empty file.
Binary file added id_obfuscator/__init__.pyc
Binary file not shown.
57 changes: 57 additions & 0 deletions id_obfuscator/base58.py
@@ -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
Binary file added id_obfuscator/base58.pyc
Binary file not shown.
Empty file added id_obfuscator/models.py
Empty file.
Binary file added id_obfuscator/models.pyc
Binary file not shown.
39 changes: 39 additions & 0 deletions id_obfuscator/tests.py
@@ -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")
Binary file added id_obfuscator/tests.pyc
Binary file not shown.
58 changes: 58 additions & 0 deletions id_obfuscator/utils.py
@@ -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
Binary file added id_obfuscator/utils.pyc
Binary file not shown.
1 change: 1 addition & 0 deletions id_obfuscator/views.py
@@ -0,0 +1 @@
# Create your views here.
14 changes: 14 additions & 0 deletions manage.py
@@ -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)

0 comments on commit 2e2158c

Please sign in to comment.