Skip to content

Instantly share code, notes, and snippets.

@davidfischer
Created November 15, 2014 22:48
Show Gist options
  • Save davidfischer/67d1e9519cbb00ebe653 to your computer and use it in GitHub Desktop.
Save davidfischer/67d1e9519cbb00ebe653 to your computer and use it in GitHub Desktop.
A talk on the Python cryptography library (cryptography.io) given by @davidfischer at the San Diego Python Django Unconference on November 15, 2014
Display the source blob
Display the rendered blob
Raw
{
"metadata": {
"name": "",
"signature": "sha256:ca5245a3f9db179c6310a0aa7b0a84c587f5d6ba79796e2dfef0295c7ef981b9"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Python Cryptography"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://cryptography.io"
]
},
{
"cell_type": "heading",
"level": 3,
"metadata": {},
"source": [
"Enter cryptography for humans (the high level API)"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import cryptography"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Symmetric encryption & decryption"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from cryptography.fernet import Fernet\n",
"\n",
"key = Fernet.generate_key() # 32 bytes - urlsafe_base64\n",
"f = Fernet(key)\n",
"token = f.encrypt(b\"San Diego Python is the best\")\n",
"\n",
"# Token includes ciphertext, time, and HMAC as urlsafe_base64\n",
"print(token)\n",
"\n",
"# Decryption\n",
"print(Fernet(key).decrypt(token)) # raises if token isn't valid\n",
"print(Fernet(key).decrypt(token, ttl=5)) # raises if older than ttl seconds"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Fernet encryption format"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import base64, struct, datetime\n",
"\n",
"token_bytes = base64.urlsafe_b64decode(token)\n",
"\n",
"# Fernet version (byte #1)\n",
"print(struct.unpack('B', token_bytes[:1])[0])\n",
"\n",
"# Encryption time (bytes #2-9)\n",
"print(datetime.datetime.fromtimestamp(struct.unpack('>Q', token_bytes[1:9])[0]))\n",
"\n",
"# Random initialization vector (bytes #10-17)\n",
"print(token_bytes[9:17])\n",
"\n",
"# Ciphertext (variable len bytes up to last 32 bytes)\n",
"\n",
"# HMAC - verifies that nothing was tampered with (final 32 bytes)\n",
"from cryptography.hazmat.backends import default_backend\n",
"from cryptography.hazmat.primitives import hashes, hmac\n",
"h = hmac.HMAC(base64.urlsafe_b64decode(key)[:16], hashes.SHA256(), backend=default_backend())\n",
"h.update(token_bytes[:-32])\n",
"h.finalize() == token_bytes[-32:]"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 3,
"metadata": {},
"source": [
"Hazardous materials cryptography"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lots of potential for error when used improperly. Let's dive in!"
]
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Public key cryptography"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from cryptography.hazmat.backends import default_backend\n",
"from cryptography.hazmat.primitives import hashes\n",
"from cryptography.hazmat.primitives.asymmetric import rsa, padding\n",
"\n",
"# Used to decrypt messages and to sign messages\n",
"private_key = rsa.generate_private_key(\n",
" public_exponent=65537,\n",
" key_size=2048,\n",
" backend=default_backend()\n",
")\n",
"\n",
"# Used to encrypt messages and verify signed messages\n",
"public_key = private_key.public_key()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Encryption and decryption\n",
"message = b\"Go Chargers!\"\n",
"ciphertext = public_key.encrypt(\n",
" message,\n",
" padding.OAEP(\n",
" mgf=padding.MGF1(algorithm=hashes.SHA1()),\n",
" algorithm=hashes.SHA1(),\n",
" label=None\n",
" )\n",
")\n",
"print('Ciphertext length: {}'.format(len(ciphertext)))\n",
"\n",
"plaintext = private_key.decrypt(\n",
" ciphertext,\n",
" padding.OAEP(\n",
" mgf=padding.MGF1(algorithm=hashes.SHA1()),\n",
" algorithm=hashes.SHA1(),\n",
" label=None\n",
" )\n",
")\n",
"print(plaintext)"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Signature and verification\n",
"signer = private_key.signer(\n",
" padding.PSS(\n",
" mgf=padding.MGF1(hashes.SHA1()),\n",
" salt_length=padding.PSS.MAX_LENGTH\n",
" ),\n",
" hashes.SHA1()\n",
")\n",
"data = b\"Python is fun\"\n",
"signer.update(data)\n",
"signature = signer.finalize()\n",
"print(signature)\n",
"\n",
"verifier = public_key.verifier(\n",
" signature,\n",
" padding.PSS(\n",
" mgf=padding.MGF1(hashes.SHA1()),\n",
" salt_length=padding.PSS.MAX_LENGTH\n",
" ),\n",
" hashes.SHA1()\n",
")\n",
"verifier.update(data)\n",
"verifier.verify() # Raises InvalidSignature on error"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Two-factor authentication"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Time-based one time password\n",
"import os\n",
"import time\n",
"from cryptography.hazmat.backends import default_backend\n",
"from cryptography.hazmat.primitives.twofactor.totp import TOTP\n",
"from cryptography.hazmat.primitives.hashes import SHA256\n",
"\n",
"OUTPUT_LENGTH = 8 # [6-8] bytes\n",
"TIME_STEP = 30\n",
"key = os.urandom(16)\n",
"totp = TOTP(key, OUTPUT_LENGTH, SHA256(), TIME_STEP, backend=default_backend())\n",
"totp.generate(time.time()) # generates the same value during TIME_STEP seconds"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Other useful primitives"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# PBKDF2 for password storage\n",
"import os\n",
"from cryptography.hazmat.primitives import hashes\n",
"from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC\n",
"from cryptography.hazmat.backends import default_backend\n",
"\n",
"salt = os.urandom(16)\n",
"kdf = PBKDF2HMAC(\n",
" algorithm=hashes.SHA256(),\n",
" length=32,\n",
" salt=salt,\n",
" iterations=100000,\n",
" backend=default_backend()\n",
")\n",
"kdf.derive(b\"my secret password\")"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Constant time comparison\n",
"from cryptography.hazmat.primitives import constant_time\n",
"\n",
"constant_time.bytes_eq(b\"foo\", b\"foo\")"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Quite a few more..."
]
}
],
"metadata": {}
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment