summaryrefslogtreecommitdiff
path: root/notebooks/python/cryptographic_primitives.ipynb
diff options
context:
space:
mode:
Diffstat (limited to 'notebooks/python/cryptographic_primitives.ipynb')
-rw-r--r--notebooks/python/cryptographic_primitives.ipynb261
1 files changed, 261 insertions, 0 deletions
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 @@
1{
2 "cells": [
3 {
4 "cell_type": "code",
5 "execution_count": 12,
6 "id": "fedd7d1a-36a9-4255-8305-a720dfb7b500",
7 "metadata": {},
8 "outputs": [
9 {
10 "name": "stdout",
11 "output_type": "stream",
12 "text": [
13 "b\"{\\x8ea\\xf8wu!\\xeb\\xc4\\xc9\\xcb|\\\\\\xf1K\\xd5t\\x80i\\xbcD\\xb6!\\xfc\\xee\\x06lQ'V0\\xc6\"\n",
14 "7b8e61f8777521ebc4c9cb7c5cf14bd5748069bc44b621fcee066c51275630c6\n",
15 "b'e45h+Hd1IevEyct8XPFL1XSAabxEtiH87gZsUSdWMMY='\n"
16 ]
17 }
18 ],
19 "source": [
20 "# hashes\n",
21 "\n",
22 "import base64\n",
23 "import hashlib\n",
24 "\n",
25 "hashed_result = hashlib.sha256(b\"This is a tesu\")\n",
26 "print(hashed_result.digest())\n",
27 "print(hashed_result.hexdigest())\n",
28 "print(base64.b64encode(hashed_result.digest()))\n"
29 ]
30 },
31 {
32 "cell_type": "code",
33 "execution_count": 13,
34 "id": "9082c388-1c30-46dc-8897-15bf5db255c8",
35 "metadata": {},
36 "outputs": [
37 {
38 "name": "stdout",
39 "output_type": "stream",
40 "text": [
41 "password_hash1 = 'pbkdf2_sha256$100000$j2jYEb1TvX/wh5JEAYGag6h+hRNTGe3rjyv5pegVBu4=$7cV7MljnJJYuGndge3jozwFP28aP8Gfj+QFGvv5bpfo='\n",
42 "password_hash2 = 'pbkdf2_sha256$100000$8+PsThJmJXNAbQ/zaR5AgRA/CuNv8L1uPUXRNuWHc9k=$Kf07CSCVQOTQrvWX5l/ZBdA7zsaEFH247QjUaewpxaU='\n"
43 ]
44 }
45 ],
46 "source": [
47 "# PBKDF2\n",
48 "\n",
49 "import base64\n",
50 "import hashlib\n",
51 "import os\n",
52 "\n",
53 "\n",
54 "def get_password_hash(plaintext_password: str) -> str:\n",
55 " \"\"\"\n",
56 " Returns a complete password hash based on `plaintext_password`\n",
57 "\n",
58 " :return: The hashed version of `plaintext_password`\n",
59 " :rtype: str\n",
60 " \"\"\"\n",
61 " # parameters typycally sent as arguments or fetched from config\n",
62 " hash_algo = \"sha256\" # the hash algorithm to use for HMAC\n",
63 " iterations = 100000 # amount of iterations\n",
64 "\n",
65 " salt = os.urandom(32) # 32 bytes of random data\n",
66 "\n",
67 " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n",
68 "\n",
69 " return (\n",
70 " f\"pbkdf2_{hash_algo}${iterations}$\"\n",
71 " f\"{base64.b64encode(salt).decode(\"ascii\")}$\"\n",
72 " f\"{base64.b64encode(key).decode(\"ascii\")}\"\n",
73 " )\n",
74 "\n",
75 "\n",
76 "def password_matches(plaintext_password: str, password_hash: str) -> bool:\n",
77 " \"\"\"Returns True if `plaintext_password` matches `password_hash`, False otherwise\"\"\"\n",
78 " hash_tokens = password_hash.split(\"$\")\n",
79 " hash_algo = hash_tokens[0].split(\"_\")[1]\n",
80 " iterations = int(hash_tokens[1])\n",
81 " salt = base64.b64decode(hash_tokens[2])\n",
82 " key = hashlib.pbkdf2_hmac(hash_algo, plaintext_password.encode(), salt, iterations)\n",
83 " return key == base64.b64decode(hash_tokens[3])\n",
84 "\n",
85 "\n",
86 "password_hash1 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n",
87 "password_hash2 = get_password_hash(\"didn't expect a Spanish Inquisition!\")\n",
88 "print(f\"{password_hash1 = }\")\n",
89 "print(f\"{password_hash2 = }\")\n",
90 "\n",
91 "# print(password_matches(\"didn't expect a Spanish Inquisition!\", 'pbkdf2_sha256$100000$JfwrS72mFimypbwhJ/NCyds2dkXtknvfWM5AM3+Z5GQ=$k8YA8csSJZhuYW0um7utq3+lSX5pTAQPd9dukObWrPo='))"
92 ]
93 },
94 {
95 "cell_type": "code",
96 "execution_count": 14,
97 "id": "1274325e-f670-4af8-a0bc-2ff7f668f769",
98 "metadata": {},
99 "outputs": [
100 {
101 "name": "stdout",
102 "output_type": "stream",
103 "text": [
104 "('gxk9W0XFgbzbjRWB7zCPjbWs3', '00000bf5db2c3c6484904bf5e8314aeb123f5594af1f283fb62e14c7d196e84c')\n"
105 ]
106 }
107 ],
108 "source": [
109 "# Proof of work\n",
110 "\n",
111 "import hashlib\n",
112 "import random\n",
113 "import string\n",
114 "\n",
115 "VALID_CHARS = string.ascii_letters + string.digits # valid characters for response\n",
116 "\n",
117 "\n",
118 "def pow_solve_rnd(challenge: str, starting_chars: str = \"00000\", answer_size: int = 25) -> tuple:\n",
119 " \"\"\"\n",
120 " Solves a POW challenge for SHA-256 using `random.choice`\n",
121 "\n",
122 " :return: (response, SHA-256 digest) tuple that solves the challenge\n",
123 " :rtype: tuple\n",
124 " \"\"\"\n",
125 " while True:\n",
126 " attempt = ''.join(\n",
127 " [random.choice(VALID_CHARS) for x in range(answer_size)])\n",
128 " dig = hashlib.sha256(('{0}:{1}'.format(challenge, attempt)).encode()).hexdigest()\n",
129 " if dig.startswith(starting_chars):\n",
130 " return attempt, dig\n",
131 "\n",
132 "print(f\"{pow_solve_rnd('2024-08-07')}\")"
133 ]
134 },
135 {
136 "cell_type": "code",
137 "execution_count": 25,
138 "id": "6752c055-7e49-46c4-858b-599d069a7e77",
139 "metadata": {},
140 "outputs": [
141 {
142 "name": "stdout",
143 "output_type": "stream",
144 "text": [
145 "enc-val$2$6ppVDmiJi0P9BnHuzwFUPA+vV/Gsb4INe2O9E/Ma2uI=$6879D8rw8645nbQ/NQtY3Q+AwKTWpd1P6Q==\n",
146 "Invalid tag when decrypting enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==\n",
147 "None\n"
148 ]
149 }
150 ],
151 "source": [
152 "# Encryption / decryption using AES-256-GCM\n",
153 "\n",
154 "import hashlib\n",
155 "import os\n",
156 "\n",
157 "from cryptography.exceptions import InvalidTag\n",
158 "from cryptography.hazmat.primitives.ciphers.aead import AESGCM\n",
159 "\n",
160 "\n",
161 "def encrypt(password: str, data: str) -> str:\n",
162 " \"\"\"\n",
163 " Encrypts `data` using `password` and AES-256-GCM.\n",
164 "\n",
165 " The output string is in the following format:\n",
166 " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n",
167 "\n",
168 " :param password: The password to generate the key with\n",
169 " :type password: str\n",
170 "\n",
171 " :param data: The data to be encrypted\n",
172 " :type data: str\n",
173 "\n",
174 " :return: The output string\n",
175 " :rtype: str\n",
176 " \"\"\"\n",
177 " data_bytes = data.encode()\n",
178 " salt = os.urandom(32)\n",
179 " aesgcm = AESGCM(\n",
180 " hashlib.scrypt(\n",
181 " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n",
182 " )\n",
183 " )\n",
184 " nonce = salt[:12]\n",
185 " # padding_length_bytes = b\"-1\" # no padding used 2 bytes \"sign\"\n",
186 " edata = aesgcm.encrypt(\n",
187 " nonce, data_bytes, salt\n",
188 " )\n",
189 " return (\n",
190 " f\"enc-val$2${base64.b64encode(salt).decode()}${base64.b64encode(edata).decode()}\"\n",
191 " )\n",
192 "\n",
193 "\n",
194 "def decrypt(password: str, edata: str) -> str:\n",
195 " \"\"\"\n",
196 " Decrypts `edata` using `password`.\n",
197 "\n",
198 " `edata` is in the following format:\n",
199 " enc-val$`version-num`$`bas64-salt`$`base64-encrypted_data`\n",
200 "\n",
201 " :param password: The password to generate the key with\n",
202 " :type password: str\n",
203 "\n",
204 " :param edata: The data to be decrypted\n",
205 " :type edata: str\n",
206 "\n",
207 " :return: The output string / decrypted data\n",
208 " :rtype: str\n",
209 " \"\"\"\n",
210 " # check for supported versions first...\n",
211 "\n",
212 " try:\n",
213 " salt, data = (base64.b64decode(t) for t in edata[10:].split('$'))\n",
214 " nonce = salt[:12]\n",
215 " aesgcm = AESGCM(\n",
216 " hashlib.scrypt(\n",
217 " password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32\n",
218 " )\n",
219 " )\n",
220 "\n",
221 " # decrypt\n",
222 " data = aesgcm.decrypt(nonce, data, salt)\n",
223 " return data.decode()\n",
224 " except InvalidTag:\n",
225 " print(f\"Invalid tag when decrypting {edata}\")\n",
226 "\n",
227 "print(f\"{encrypt('my passphrase', 'my secret')}\")\n",
228 "print(f\"{decrypt('my passphrase', 'enc-val$2$tmuH3MTDQ9vMbxJ1pZUb0rQlF43Lgr/AYfkA+vSOZs0=$5CVt3VrxhuuOOVt1vCXeGpbhNh/20IcFNg==')}\")"
229 ]
230 },
231 {
232 "cell_type": "code",
233 "execution_count": null,
234 "id": "8ccfb453-d3fd-4383-a01d-f81b19f2cb82",
235 "metadata": {},
236 "outputs": [],
237 "source": []
238 }
239 ],
240 "metadata": {
241 "kernelspec": {
242 "display_name": "Python 3 (ipykernel)",
243 "language": "python",
244 "name": "python3"
245 },
246 "language_info": {
247 "codemirror_mode": {
248 "name": "ipython",
249 "version": 3
250 },
251 "file_extension": ".py",
252 "mimetype": "text/x-python",
253 "name": "python",
254 "nbconvert_exporter": "python",
255 "pygments_lexer": "ipython3",
256 "version": "3.13.9"
257 }
258 },
259 "nbformat": 4,
260 "nbformat_minor": 5
261}