From be44243136d710eec0345f8459a64da377bab357 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Sun, 29 Mar 2026 13:43:38 +0200 Subject: Re-format reveal.js slides and add templates --- notebooks/python/cryptographic_primitives.ipynb | 261 ++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 notebooks/python/cryptographic_primitives.ipynb (limited to 'notebooks/python/cryptographic_primitives.ipynb') diff --git a/notebooks/python/cryptographic_primitives.ipynb b/notebooks/python/cryptographic_primitives.ipynb new file mode 100644 index 0000000..4d96549 --- /dev/null +++ b/notebooks/python/cryptographic_primitives.ipynb @@ -0,0 +1,261 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 12, + "id": "fedd7d1a-36a9-4255-8305-a720dfb7b500", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "b\"{\\x8ea\\xf8wu!\\xeb\\xc4\\xc9\\xcb|\\\\\\xf1K\\xd5t\\x80i\\xbcD\\xb6!\\xfc\\xee\\x06lQ'V0\\xc6\"\n", + "7b8e61f8777521ebc4c9cb7c5cf14bd5748069bc44b621fcee066c51275630c6\n", + "b'e45h+Hd1IevEyct8XPFL1XSAabxEtiH87gZsUSdWMMY='\n" + ] + } + ], + "source": [ + "# hashes\n", + "\n", + "import base64\n", + "import hashlib\n", + "\n", + "hashed_result = hashlib.sha256(b\"This is a tesu\")\n", + "print(hashed_result.digest())\n", + "print(hashed_result.hexdigest())\n", + "print(base64.b64encode(hashed_result.digest()))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "9082c388-1c30-46dc-8897-15bf5db255c8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "password_hash1 = 'pbkdf2_sha256$100000$j2jYEb1TvX/wh5JEAYGag6h+hRNTGe3rjyv5pegVBu4=$7cV7MljnJJYuGndge3jozwFP28aP8Gfj+QFGvv5bpfo='\n", + "password_hash2 = 'pbkdf2_sha256$100000$8+PsThJmJXNAbQ/zaR5AgRA/CuNv8L1uPUXRNuWHc9k=$Kf07CSCVQOTQrvWX5l/ZBdA7zsaEFH247QjUaewpxaU='\n" + ] + } + ], + "source": [ + "# PBKDF2\n", + "\n", + "import base64\n", + "import hashlib\n", + "import os\n", + "\n", + "\n", + "def get_password_hash(plaintext_password: str) -> str:\n", + " \"\"\"\n", + " Returns a complete password hash based on `plaintext_password`\n", + "\n", + " :return: The hashed version of `plaintext_password`\n", + " :rtype: str\n", + " \"\"\"\n", + " # parameters typycally sent as arguments or fetched from config\n", + " hash_algo = \"sha256\" # the hash algorithm to use for HMAC\n", + " iterations = 100000 # amount of iterations\n", + "\n", + " salt = os.urandom(32) # 32 bytes of random data\n", + "\n", + " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n", + "\n", + " return (\n", + " f\"pbkdf2_{hash_algo}${iterations}$\"\n", + " f\"{base64.b64encode(salt).decode(\"ascii\")}$\"\n", + " f\"{base64.b64encode(key).decode(\"ascii\")}\"\n", + " )\n", + "\n", + "\n", + "def password_matches(plaintext_password: str, password_hash: str) -> bool:\n", + " \"\"\"Returns True if `plaintext_password` matches `password_hash`, False otherwise\"\"\"\n", + " hash_tokens = password_hash.split(\"$\")\n", + " hash_algo = hash_tokens[0].split(\"_\")[1]\n", + " iterations = int(hash_tokens[1])\n", + " salt = base64.b64decode(hash_tokens[2])\n", + " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n", + " return key == base64.b64decode(hash_tokens[3])\n", + "\n", + "\n", + "password_hash1 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n", + "password_hash2 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n", + "print(f\"{password_hash1 = }\")\n", + "print(f\"{password_hash2 = }\")\n", + "\n", + "# print(password_matches(\"didn't expect a Spanish Inquisition!\", 'pbkdf2_sha256$100000$JfwrS72mFimypbwhJ/NCyds2dkXtknvfWM5AM3+Z5GQ=$k8YA8csSJZhuYW0um7utq3+lSX5pTAQPd9dukObWrPo='))" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1274325e-f670-4af8-a0bc-2ff7f668f769", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('gxk9W0XFgbzbjRWB7zCPjbWs3', '00000bf5db2c3c6484904bf5e8314aeb123f5594af1f283fb62e14c7d196e84c')\n" + ] + } + ], + "source": [ + "# Proof of work\n", + "\n", + "import hashlib\n", + "import random\n", + "import string\n", + "\n", + "VALID_CHARS = string.ascii_letters + string.digits # valid characters for response\n", + "\n", + "\n", + "def pow_solve_rnd(challenge: str, starting_chars: str = \"00000\", answer_size: int = 25) -> tuple:\n", + " \"\"\"\n", + " Solves a POW challenge for SHA-256 using `random.choice`\n", + "\n", + " :return: (response, SHA-256 digest) tuple that solves the challenge\n", + " :rtype: tuple\n", + " \"\"\"\n", + " while True:\n", + " attempt = ''.join(\n", + " [random.choice(VALID_CHARS) for x in range(answer_size)])\n", + " dig = hashlib.sha256(('{0}:{1}'.format(challenge, attempt)).encode()).hexdigest()\n", + " if dig.startswith(starting_chars):\n", + " return attempt, dig\n", + "\n", + "print(f\"{pow_solve_rnd('2024-08-07')}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "6752c055-7e49-46c4-858b-599d069a7e77", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "enc-val$2$6ppVDmiJi0P9BnHuzwFUPA+vV/Gsb4INe2O9E/Ma2uI=$6879D8rw8645nbQ/NQtY3Q+AwKTWpd1P6Q==\n", + "Invalid tag when decrypting enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==\n", + "None\n" + ] + } + ], + "source": [ + "# Encryption / decryption using AES-256-GCM\n", + "\n", + "import hashlib\n", + "import os\n", + "\n", + "from cryptography.exceptions import InvalidTag\n", + "from cryptography.hazmat.primitives.ciphers.aead import AESGCM\n", + "\n", + "\n", + "def encrypt(password: str, data: str) -> str:\n", + " \"\"\"\n", + " Encrypts `data` using `password` and AES-256-GCM.\n", + "\n", + " The output string is in the following format:\n", + " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n", + "\n", + " :param password: The password to generate the key with\n", + " :type password: str\n", + "\n", + " :param data: The data to be encrypted\n", + " :type data: str\n", + "\n", + " :return: The output string\n", + " :rtype: str\n", + " \"\"\"\n", + " data_bytes = data.encode()\n", + " salt = os.urandom(32)\n", + " aesgcm = AESGCM(\n", + " hashlib.scrypt(\n", + " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n", + " )\n", + " )\n", + " nonce = salt[:12]\n", + " # padding_length_bytes = b\"-1\" # no padding used 2 bytes \"sign\"\n", + " edata = aesgcm.encrypt(\n", + " nonce, data_bytes, salt\n", + " )\n", + " return (\n", + " f\"enc-val$2${base64.b64encode(salt).decode()}${base64.b64encode(edata).decode()}\"\n", + " )\n", + "\n", + "\n", + "def decrypt(password: str, edata: str) -> str:\n", + " \"\"\"\n", + " Decrypts `edata` using `password`.\n", + "\n", + " `edata` is in the following format:\n", + " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n", + "\n", + " :param password: The password to generate the key with\n", + " :type password: str\n", + "\n", + " :param edata: The data to be decrypted\n", + " :type edata: str\n", + "\n", + " :return: The output string / decrypted data\n", + " :rtype: str\n", + " \"\"\"\n", + " # check for supported versions first...\n", + "\n", + " try:\n", + " salt, data = (base64.b64decode(t) for t in edata[10:].split('$'))\n", + " nonce = salt[:12]\n", + " aesgcm = AESGCM(\n", + " hashlib.scrypt(\n", + " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n", + " )\n", + " )\n", + "\n", + " # decrypt\n", + " data = aesgcm.decrypt(nonce, data, salt)\n", + " return data.decode()\n", + " except InvalidTag:\n", + " print(f\"Invalid tag when decrypting {edata}\")\n", + "\n", + "print(f\"{encrypt('my passphrase', 'my secret')}\")\n", + "print(f\"{decrypt('my passphrase', 'enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==')}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8ccfb453-d3fd-4383-a01d-f81b19f2cb82", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} -- cgit v1.3