{ "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 }