Skip to content

Instantly share code, notes, and snippets.

@dolph
Last active December 20, 2015 21:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dolph/6198529 to your computer and use it in GitHub Desktop.
Save dolph/6198529 to your computer and use it in GitHub Desktop.
encrypted access token demo
"""
Offline validation of oauth access_keys.
"""
import base64
import string
import unittest
import urllib
import uuid
import zlib
from Crypto.Cipher import AES
from Crypto import Random
KEY = Random.new().read(32)
IV = Random.new().read(16)
ALPHABET = string.digits + string.letters
def encode(s):
s = base64.b64encode(s)
s = s.replace('+', '-')
s = s.replace('/', '_')
s = s.replace('=', '.')
return s
def decode(s):
s = s.replace('-', '+')
s = s.replace('_', '/')
s = s.replace('.', '=')
s = base64.b64decode(s)
return s
def compress(s):
return zlib.compress(s, 9)
def decompress(s):
return zlib.decompress(s)
def encrypt(plaintext):
cipher = AES.new(KEY, AES.MODE_CFB, IV)
return cipher.encrypt(plaintext)
def decrypt(ciphertext):
cipher = AES.new(KEY, AES.MODE_CFB, IV)
return cipher.decrypt(ciphertext)
def change_base(number):
"""Converts an integer to a string."""
s = ''
while number != 0:
number, i = divmod(number, len(ALPHABET))
s = ALPHABET[i] + s
return s
def generate_access_key(secret):
# the oauth secret is encoded into the access key so that oauth
# middleware can validate the oauth signature before making backend or
# remote calls
plaintext = compress(secret)
ciphertext = encrypt(plaintext)
encoded = encode(ciphertext)
return encoded
def verify_access_key(access_key):
ciphertext = decode(access_key)
plaintext = decrypt(ciphertext)
secret = decompress(plaintext)
return secret
class Tests(unittest.TestCase):
def setUp(self):
self.secret = uuid.uuid4().hex
def test_encode_decode(self):
s = 'a /+-_\xe8'
self.assertNotEqual(s, encode(s))
self.assertEqual(s, decode(encode(s)))
def test_encrypt_decrypt(self):
s = uuid.uuid4().hex
self.assertEqual(s, decrypt(encrypt(s)))
def test_encrypt_encode_decode_decrypt(self):
s = 'a /+-_\xe8'
self.assertEqual(s, decrypt(decode(encode(encrypt(s)))))
def test_decrypt_access_key(self):
access_key = generate_access_key(self.secret)
# access keys should be url friendly as-is
self.assertEqual(urllib.quote(access_key), access_key)
self.assertEqual(urllib.quote_plus(access_key), access_key)
secret = verify_access_key(access_key)
self.assertEqual(self.secret, secret)
def test_access_key_length_reasonable(self):
access_key = generate_access_key(self.secret)
self.assertLessEqual(len(access_key), 255)
if __name__ == '__main__':
wrap = lambda s: '\n\n %s\n' % s
secret = change_base(uuid.uuid4().int)
print('Generate the secret key first: %s' % wrap(secret))
access_key = generate_access_key(secret)
print('Then we can derive the access key: %s' % wrap(access_key))
print('Which has a reasonable length of: %s' % wrap(len(access_key)))
verified_secret = verify_access_key(access_key)
print('Later, middleware receives an OAuth-signed request with an access '
'key, and can independently extract the secret used to sign the '
'request: %s' % wrap(verified_secret))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment