Skip to content

Instantly share code, notes, and snippets.

@amitu amitu/encoded_key.py
Last active Apr 29, 2016

Embed
What would you like to do?
Django Model base class for encoded key. Useful for when you want to pass id in URL or JSON, but do not leak data to world (about how many objects you have of that kind).
from Crypto.Cipher import AES
from Crypto import Random
import base64
import binascii
import struct
from django.db import models
from django.conf import settings
class EncodedKeyManager(models.Manager):
def get_by_ekey(self, ekey):
return self.get(pk=decode(ekey))
def get_by_ekey_or_404(self, ekey):
from django.http import Http404
try:
return self.get_by_ekey(ekey)
except models.ObjectDoesNotExist:
raise Http404
class EncodedKeyModel(models.Model):
class Meta:
abstract = True
@property
def ekey(self):
return encode(self.pk)
def encode(m):
message = str(m)
length_bytes = struct.pack("i", len(message))
crc_bytes = struct.pack("i", binascii.crc32(message))
message = crc_bytes + length_bytes + message
if len(message) % 16:
message += " " * (16 - len(message) % 16)
cypher = AES.new(
settings.SECRET_KEY[:24], AES.MODE_CBC, settings.SECRET_KEY[-16:]
)
return base64.urlsafe_b64encode(cypher.encrypt(message)).replace("=", ".")
def decode(e):
e = base64.urlsafe_b64decode(str(e).replace(".", "="))
for skey in getattr(settings, "SECRET_KEYS", [settings.SECRET_KEY]):
cypher = AES.new(skey[:24], AES.MODE_CBC, skey[-16:])
msg = cypher.decrypt(e)
crc, length, msg = msg[:4], msg[4:8], msg[8:]
length_int = struct.unpack("i", length)[0]
crc = struct.unpack("i", crc)[0]
msg = msg[:length_int]
if crc != binascii.crc32(msg):
continue
return msg
raise ValueError("Failed to decrypt, CRC never matched.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.