Last active
December 20, 2015 21:38
-
-
Save dolph/6198529 to your computer and use it in GitHub Desktop.
encrypted access token demo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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