{ "cells": [ { "cell_type": "code", "source": [ "import sys\n", "import os\n", "sys.path.append(os.path.abspath('..')) # Adds the parent directory to path\n", "from src.nkode_api import NKodeAPI\n", "from src.models import NKodePolicy, KeypadSize\n", "from src.utils import select_keys_with_passcode_values\n", "from secrets import choice\n", "from string import ascii_lowercase\n", "import numpy as np\n", "import bcrypt\n", "import hashlib\n", "import base64\n", "from IPython.display import Markdown, display\n", "\n", "def random_username() -> str:\n", " return \"test_username\" + \"\".join([choice(ascii_lowercase) for _ in range(6)])\n", "\n", "\n", "def keypad_view(keypad: np.ndarray, props_per_key: int):\n", " interface_keypad = keypad.reshape(-1, props_per_key)\n", " for idx, key_vals in enumerate(interface_keypad):\n", " print(f\"Key {idx}: {key_vals}\")\n" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2025-03-24T09:48:10.442598Z", "start_time": "2025-03-24T09:48:10.410209Z" } }, "outputs": [], "execution_count": 1 }, { "cell_type": "code", "source": [ "api = NKodeAPI()\n", "user_icons = np.array([\n", " \"😀\", \"😂\", \"🥳\", \"😍\", \"🤓\",\n", " \"😎\", \"🥺\", \"😡\", \"😱\", \"🤯\",\n", " \"🥰\", \"😴\", \"🤔\", \"🙃\", \"😇\",\n", " \"🤖\", \"👽\", \"👾\", \"🐱\", \"🐶\",\n", " \"🦁\", \"🐻\", \"🐸\", \"🐙\", \"🦄\",\n", " \"🌟\", \"⚡\", \"🔥\", \"🍕\", \"🎉\"\n", "])" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2025-03-24T09:48:10.450851Z", "start_time": "2025-03-24T09:48:10.448737Z" } }, "outputs": [], "execution_count": 2 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### nKode Customer\n", "An nKode customer is business has employees (users). An nKode API can service many customers each with their own users.\n", "Each customer specifies a keypad size and a nkode policy.\n", "The keypad can't be dispersable (`numb_of_keys < properties_per_key`)" ] }, { "cell_type": "markdown", "source": [ "#### Customer Cipher Keys\n", "Each customer has unique cipher keys.\n", "These keys are used to encipher and decipher a user's nKode.\n", "There are two types of Customer Cipher Keys:\n", "1. property key: Combined with the user property key to get the server-side representation of a users icons.\n", "2. position key: Combined with the user position key to get the server-side representation the position in each key.\n" ], "metadata": { "collapsed": false } }, { "cell_type": "code", "source": [ "policy = NKodePolicy(\n", " max_nkode_len=10,\n", " min_nkode_len=4,\n", " distinct_positions=0,\n", " distinct_properties=4,\n", ")\n", "keypad_size = KeypadSize(\n", " numb_of_keys = 5,\n", " props_per_key = 6\n", ")\n", "customer_id = api.create_new_customer(keypad_size, policy)\n", "customer = api.customers[customer_id]\n", "print(f\"Customer Position Key: {customer.cipher.position_key}\")\n", "print(f\"Customer Properties Key:\")\n", "customer_prop_keypad = customer.cipher.property_key.reshape(-1, keypad_size.props_per_key)\n", "for idx, key_vals in enumerate(customer_prop_keypad):\n", " print(f\"{key_vals}\")\n", "position_properties_dict = dict(zip(customer.cipher.position_key, customer_prop_keypad.T))\n", "print(f\"Position to Properties Map:\")\n", "for pos_val, props in position_properties_dict.items():\n", " print(f\"{pos_val}: {props}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { "end_time": "2025-03-24T09:48:10.468165Z", "start_time": "2025-03-24T09:48:10.456820Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Customer Position Key: [ 4136 64524 15410 9988 20496 18918]\n", "Customer Properties Key:\n", "[46103 19687 10938 59248 5143 27050]\n", "[26684 7431 59668 28699 6929 18710]\n", "[12853 35476 62914 24111 36473 25394]\n", "[63988 14898 1879 44350 53666 23249]\n", "[23805 45848 21514 8782 7583 36157]\n", "Position to Properties Map:\n", "4136: [46103 26684 12853 63988 23805]\n", "64524: [19687 7431 35476 14898 45848]\n", "15410: [10938 59668 62914 1879 21514]\n", "9988: [59248 28699 24111 44350 8782]\n", "20496: [ 5143 6929 36473 53666 7583]\n", "18918: [27050 18710 25394 23249 36157]\n" ] } ], "execution_count": 3 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:10.593687Z", "start_time": "2025-03-24T09:48:10.591235Z" } }, "cell_type": "code", "source": [ "user_icon_keypad = user_icons.reshape(-1, keypad_size.props_per_key)\n", "pos_icons_dict = dict(zip(customer.cipher.position_key, user_icon_keypad.T))\n", "print(\"Position Value to Icons Map:\")\n", "for pos_val, icons in pos_icons_dict.items():\n", " print(f\"{pos_val}: {icons}\")\n" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Position Value to Icons Map:\n", "4136: ['😀' '🥺' '🤔' '🐱' '🦄']\n", "64524: ['😂' '😡' '🙃' '🐶' '🌟']\n", "15410: ['🥳' '😱' '😇' '🦁' '⚡']\n", "9988: ['😍' '🤯' '🤖' '🐻' '🔥']\n", "20496: ['🤓' '🥰' '👽' '🐸' '🍕']\n", "18918: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], "execution_count": 4 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### User Signup\n", "Users can create an nKode with these steps:\n", "1. Generate a randomly shuffled keypad\n", "2. Set user nKode\n", "3. Confirm user nKode\n", "\n", "#### Generate Keypad\n", " For the server to determine the users nKode, the user's keypad must be dispersable.\n", " To make the keypad dispersable, the server will randomly drop key positions so the number of properties per key is equal to the number of keys.\n", " In our case, the server drops 1 key position to give us a 5 X 5 keypad with possible index values ranging from 0-29.\n", " - Run the cell below over and over to see it change. Notice that values never move out of their columns just their rows.\n", " - each value in the keypad is the index value of a customer properties\n", " - the user never learns their server-side properties" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:10.653252Z", "start_time": "2025-03-24T09:48:10.646267Z" } }, "cell_type": "code", "source": [ "signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id)\n", "display(Markdown(\"\"\"### Icon Keypad\"\"\"))\n", "keypad_view(user_icons[set_signup_keypad], keypad_size.numb_of_keys)\n", "display(Markdown(\"\"\"### Index Keypad\"\"\"))\n", "keypad_view(set_signup_keypad, keypad_size.numb_of_keys)\n", "display(Markdown(\"\"\"### Customer Properties Keypad\"\"\"))\n", "keypad_view(customer.cipher.property_key[set_signup_keypad], keypad_size.numb_of_keys)" ], "outputs": [ { "data": { "text/plain": [ "" ], "text/markdown": "### Icon Keypad" }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Key 0: ['😂' '🥳' '🐻' '🍕' '😎']\n", "Key 1: ['🐶' '⚡' '🤖' '🥰' '🎉']\n", "Key 2: ['🌟' '😇' '😍' '🤓' '🐙']\n", "Key 3: ['😡' '😱' '🔥' '👽' '😴']\n", "Key 4: ['🙃' '🦁' '🤯' '🐸' '👾']\n" ] }, { "data": { "text/plain": [ "" ], "text/markdown": "### Index Keypad" }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Key 0: [ 1 2 21 28 5]\n", "Key 1: [19 26 15 10 29]\n", "Key 2: [25 14 3 4 23]\n", "Key 3: [ 7 8 27 16 11]\n", "Key 4: [13 20 9 22 17]\n" ] }, { "data": { "text/plain": [ "" ], "text/markdown": "### Customer Properties Keypad" }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Key 0: [19687 10938 44350 7583 27050]\n", "Key 1: [14898 21514 24111 6929 36157]\n", "Key 2: [45848 62914 59248 5143 23249]\n", "Key 3: [ 7431 59668 8782 36473 18710]\n", "Key 4: [35476 1879 28699 53666 25394]\n" ] } ], "execution_count": 5 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Set nKode\n", "The client receives `user_icons`, `set_signup_keypad`\n" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:10.726327Z", "start_time": "2025-03-24T09:48:10.723046Z" } }, "cell_type": "code", "source": [ "username = random_username()\n", "passcode_len = 4\n", "passcode_property_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n", "selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_signup_keypad, keypad_size.numb_of_keys)\n", "print(f\"User Passcode Indices: {passcode_property_indices}\")\n", "print(f\"User Passcode Icons: {user_icons[passcode_property_indices]}\")\n", "print(f\"User Passcode Server-side properties: {customer.cipher.property_key[passcode_property_indices]}\")\n", "print(f\"Selected Keys: {selected_keys_set}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "User Passcode Indices: [8, 15, 17, 7]\n", "User Passcode Icons: ['😱' '🤖' '👾' '😡']\n", "User Passcode Server-side properties: [59668 24111 25394 7431]\n", "Selected Keys: [3, 1, 4, 3]\n" ] } ], "execution_count": 6 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Confirm nKode\n", "Submit the set key entry to render the confirm keypad." ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.002815Z", "start_time": "2025-03-24T09:48:10.759840Z" } }, "cell_type": "code", "source": [ "confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, signup_session_id)\n", "keypad_view(confirm_keypad, keypad_size.numb_of_keys)\n", "selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys)\n", "print(f\"Selected Keys\\n{selected_keys_confirm}\")\n", "success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n", "assert success" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Key 0: [ 1 26 9 4 11]\n", "Key 1: [19 8 3 28 17]\n", "Key 2: [ 7 20 21 10 23]\n", "Key 3: [13 14 15 16 5]\n", "Key 4: [25 2 27 22 29]\n", "Selected Keys\n", "[1, 3, 1, 2]\n" ] } ], "execution_count": 7 }, { "metadata": {}, "cell_type": "markdown", "source": "### Inferring an nKode selection" }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.011978Z", "start_time": "2025-03-24T09:48:11.009413Z" } }, "cell_type": "code", "source": [ "for idx in range(passcode_len):\n", " selected_key_set = selected_keys_set[idx]\n", " selected_set_key_idx = set_signup_keypad.reshape(-1, keypad_size.numb_of_keys)[selected_key_set]\n", " print(f\"Set Key {idx}: {user_icons[selected_set_key_idx]}\")\n", " selected_key_confirm = selected_keys_confirm[idx]\n", " selected_confirm_key_idx = confirm_keypad.reshape(-1, keypad_size.numb_of_keys)[selected_key_confirm]\n", " print(f\"Confirm Key {idx}: {user_icons[selected_confirm_key_idx]}\")\n", " print(f\"Overlapping icon {user_icons[passcode_property_indices[idx]]}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set Key 0: ['😡' '😱' '🔥' '👽' '😴']\n", "Confirm Key 0: ['🐶' '😱' '😍' '🍕' '👾']\n", "Overlapping icon 😱\n", "Set Key 1: ['🐶' '⚡' '🤖' '🥰' '🎉']\n", "Confirm Key 1: ['🙃' '😇' '🤖' '👽' '😎']\n", "Overlapping icon 🤖\n", "Set Key 2: ['🙃' '🦁' '🤯' '🐸' '👾']\n", "Confirm Key 2: ['🐶' '😱' '😍' '🍕' '👾']\n", "Overlapping icon 👾\n", "Set Key 3: ['😡' '😱' '🔥' '👽' '😴']\n", "Confirm Key 3: ['😡' '🦁' '🐻' '🥰' '🐙']\n", "Overlapping icon 😡\n" ] } ], "execution_count": 8 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## User Cipher\n", "\n", "Users have 4 cipher keys:\n", "1. property_key: The counterpart to the `customer_prop_key`. A user's server-side passcode is composed of elements in `user_prop_key XOR customer_prop_key`.\n", "2. pass_key: The passcode key is used to encipher user passcode\n", "3. combined_position_key: The combined position key is `user_pos_key XOR customer_pos_key`.\n", "4. mask_key: The mask key used to encipher user nKode\n", "\n", "\n" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.030598Z", "start_time": "2025-03-24T09:48:11.025452Z" } }, "cell_type": "code", "source": [ "from src.user_cipher import UserCipher\n", "user_cipher = UserCipher.create(keypad_size, customer.cipher.position_key, customer.nkode_policy.max_nkode_len)\n", "user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)" ], "outputs": [], "execution_count": 9 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.049702Z", "start_time": "2025-03-24T09:48:11.047621Z" } }, "cell_type": "code", "source": "print(f\"Property Key:\\n{user_prop_key_keypad}\")", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Property Key:\n", "[[12284 15976 959 23676 56686 51545]\n", " [ 8349 41940 14857 24411 28369 47234]\n", " [42434 47287 23836 29864 11461 30858]\n", " [39973 19733 43467 29927 8960 22628]\n", " [26608 3550 57761 58209 11329 828]]\n" ] } ], "execution_count": 10 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.080213Z", "start_time": "2025-03-24T09:48:11.077878Z" } }, "cell_type": "code", "source": "print(f\"Passcode Key: {user_cipher.pass_key}\")", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Passcode Key: [43421 54264 31716 41010 25784 51749 8406 27083 36329 63965]\n" ] } ], "execution_count": 11 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.120295Z", "start_time": "2025-03-24T09:48:11.118074Z" } }, "cell_type": "code", "source": "print(f\"Mask Key: {user_cipher.mask_key}\")", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Mask Key: [27744 6339 46141 38859 23071 28613 2855 48530 24494 59329]\n" ] } ], "execution_count": 12 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.158895Z", "start_time": "2025-03-24T09:48:11.156729Z" } }, "cell_type": "code", "source": "print(f\"Combined Position Key: {user_cipher.combined_position_key}\")", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Combined Position Key: [42763 65304 30825 5938 4970 58530]\n" ] } ], "execution_count": 13 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.183517Z", "start_time": "2025-03-24T09:48:11.181587Z" } }, "cell_type": "code", "source": "print(f\"User Position Key = combined_pos_key XOR customer_pos_key: {user_cipher.combined_position_key ^ customer.cipher.position_key}\")", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "User Position Key = combined_pos_key XOR customer_pos_key: [46883 788 17499 12342 17274 44356]\n" ] } ], "execution_count": 14 }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.207260Z", "start_time": "2025-03-24T09:48:11.204729Z" } }, "cell_type": "code", "source": [ "position_properties_dict = dict(zip(user_cipher.combined_position_key, user_prop_key_keypad.T))\n", "print(f\"Combined Position to Properties Map:\")\n", "for pos_val, props in position_properties_dict.items():\n", " print(f\"{pos_val}: {props}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Combined Position to Properties Map:\n", "42763: [12284 8349 42434 39973 26608]\n", "65304: [15976 41940 47287 19733 3550]\n", "30825: [ 959 14857 23836 43467 57761]\n", "5938: [23676 24411 29864 29927 58209]\n", "4970: [56686 28369 11461 8960 11329]\n", "58530: [51545 47234 30858 22628 828]\n" ] } ], "execution_count": 15 }, { "metadata": {}, "cell_type": "markdown", "source": [ "#### Encipher Mask\n", "1. Get the `padded_passcode_position_indices`; padded with random position indices to equal length `max_nkode_len`.\n", "2. Recover the `user_position_key`. Recall `user.cipher.combined_position_key = user_position_key XOR customer.cipher.positon_key`\n", "3. Order the `user_position_key` by the `padded_passcode_position_indices`\n", "4. Mask the `ordered_user_position_key`\n", "5. Base 64 encode the mask" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.235048Z", "start_time": "2025-03-24T09:48:11.232871Z" } }, "cell_type": "code", "source": [ "padded_passcode_position_indices = customer.cipher.get_passcode_position_indices_padded(list(passcode_property_indices), customer.nkode_policy.max_nkode_len)\n", "user_position_key = user_cipher.combined_position_key ^ customer.cipher.position_key\n", "ordered_user_position_key = user_position_key[padded_passcode_position_indices]\n", "mask = ordered_user_position_key ^ user_cipher.mask_key\n", "encoded_mask = user_cipher.encode_base64_str(mask)" ], "outputs": [], "execution_count": 16 }, { "metadata": {}, "cell_type": "markdown", "source": [ "#### Encipher Passcode\n", "1. Compute `combined_property_key`\n", "2. Recover `user_passcode = ordered_combined_proptery_key`; order by passcode_property_indices\n", "3. Zero pad `user_pascode`\n", "4. Encipher `user_passcode` with `user.cipher.pass_key`\n", "5. Hash `ciphered_passcode`" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.484858Z", "start_time": "2025-03-24T09:48:11.252168Z" } }, "cell_type": "code", "source": [ "combined_prop_key = customer.cipher.property_key ^ user_cipher.property_key\n", "user_passcode = combined_prop_key[passcode_property_indices]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", "padded_passcode = np.concatenate((user_passcode, np.zeros(pad_len, dtype=user_passcode.dtype)))\n", "ciphered_passcode = padded_passcode ^ user_cipher.pass_key\n", "passcode_prehash = base64.b64encode(hashlib.sha256(ciphered_passcode.tobytes()).digest())\n", "passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode(\"utf-8\")" ], "outputs": [], "execution_count": 17 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### User Login\n", "1. Get login keypad\n", "2. Select keys with passcode icons (in our case, passcode property indices)\n" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.722985Z", "start_time": "2025-03-24T09:48:11.488809Z" } }, "cell_type": "code", "source": [ "login_keypad = api.get_login_keypad(username, customer_id)\n", "keypad_view(login_keypad, keypad_size.props_per_key)\n", "selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n", "print(f\"User Passcode: {passcode_property_indices}\\n\")\n", "print(f\"Selected Keys:\\n {selected_keys_login}\\n\")\n", "success = api.login(customer_id, username, selected_keys_login)\n", "assert success" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Key 0: [18 1 2 21 28 5]\n", "Key 1: [12 19 26 15 10 29]\n", "Key 2: [ 0 25 14 3 4 23]\n", "Key 3: [ 6 7 8 27 16 11]\n", "Key 4: [24 13 20 9 22 17]\n", "User Passcode: [8, 15, 17, 7]\n", "\n", "Selected Keys:\n", " [3, 1, 4, 3]\n", "\n" ] } ], "execution_count": 18 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Validate Login Key Entry\n", "- decipher user mask and recover nkode position values\n", "- get presumed properties from key selection and position values\n", "- compare with hash" ] }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Decipher Mask\n", "Recover nKode position values:\n", "- decode mask from base64 to int\n", "- ordered_user_position_key = mask ^ mask_key\n", "- user_position_key = user.cipher.co\n", "- deduce the set indices" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.731909Z", "start_time": "2025-03-24T09:48:11.729517Z" } }, "cell_type": "code", "source": [ "login_keypad = api.get_login_keypad(username, customer_id)\n", "selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n", "user = api.customers[customer_id].users[username]\n", "mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", "ordered_user_position_key = mask ^ user.cipher.mask_key\n", "user_position_key = customer.cipher.position_key ^ user.cipher.combined_position_key" ], "outputs": [], "execution_count": 19 }, { "metadata": {}, "cell_type": "markdown", "source": [ "#### Get Presumed Properties\n", "- Get the passcode position indices (within the keys)\n", "- Get the presumed property indices from the key and position within the key" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.750757Z", "start_time": "2025-03-24T09:48:11.748661Z" } }, "cell_type": "code", "source": [ "passcode_position_indices = [int(np.where(user_position_key == pos)[0][0]) for pos in ordered_user_position_key[:passcode_len]]\n", "presumed_property_indices = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, passcode_position_indices)\n", "assert passcode_property_indices == presumed_property_indices\n" ], "outputs": [], "execution_count": 20 }, { "metadata": {}, "cell_type": "markdown", "source": "### Compare Enciphered Passcodes\n" }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:11.995883Z", "start_time": "2025-03-24T09:48:11.760909Z" } }, "cell_type": "code", "source": [ "valid_nkode = user.cipher.compare_nkode(presumed_property_indices, customer.cipher, user.enciphered_passcode.code)\n", "assert valid_nkode" ], "outputs": [], "execution_count": 21 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Renew Properties\n", "1. Renew Customer Properties\n", "2. Renew User Keys\n", "3. Refresh User on Login\n", "\n" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:12.479529Z", "start_time": "2025-03-24T09:48:12.009222Z" } }, "cell_type": "code", "source": [ "def print_user_enciphered_code():\n", " mask = api.customers[customer_id].users[username].enciphered_passcode.mask\n", " code = api.customers[customer_id].users[username].enciphered_passcode.code\n", " print(f\"mask: {mask}, code: {code}\\n\")\n", "\n", "print(\"Old User Cipher and Mask\")\n", "print_user_enciphered_code()\n", "api.renew_keys(customer_id) # Steps 1 and 2\n", "login_keypad = api.get_login_keypad(username, customer_id)\n", "selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n", "success = api.login(customer_id, username, selected_keys_login) # Step 3\n", "print(\"New User Cipher and Mask\")\n", "print_user_enciphered_code()\n", "assert success" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Old User Cipher and Mask\n", "mask: nheBuPsWD2zInvscNhsJO6Wy0kU=, code: $2b$12$bsV3i1BsLH6nHCZOPVHns.b1ARmpuJETxFPZohcPG2OKO9Mr3B1du\n", "\n", "New User Cipher and Mask\n", "mask: ionSnD9+/0DS5ul8+wMyi3PNCRA=, code: $2b$12$MZejDYT1GDoyE0w1TdFGCedyBD4BY2n6VjLQW73TwJPLyjiimdpA2\n", "\n" ] } ], "execution_count": 22 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Renew Customer Keys\n", "The customer cipher keys are replaced." ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:12.490144Z", "start_time": "2025-03-24T09:48:12.486157Z" } }, "cell_type": "code", "source": [ "old_props = customer.cipher.property_key.copy()\n", "old_pos = customer.cipher.position_key.copy()\n", "customer.cipher.property_key = np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False)\n", "customer.cipher.position_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False)\n", "new_props = customer.cipher.property_key\n", "new_pos = customer.cipher.position_key" ], "outputs": [], "execution_count": 23 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Renew User\n", "User property and position keys go through an intermediate phase.\n", "#### user.cipher.combined_position_key\n", "- user_combined_position_key = user_combined_position_key XOR pos_xor\n", "- user_combined_position_key = (user_position_key XOR old_customer_position_key) XOR (old_customer_position_key XOR new_customer_position_key)\n", "- user_combined_position_key = user_position_key XOR new_customer_position_key\n", "#### user.cipher.combined_position_key\n", "- user_property_key = user_property_key XOR props_xor\n", "- user_property_key = user_property_key XOR old_customer_property_key XOR new_customer_property_key\n" ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:12.503462Z", "start_time": "2025-03-24T09:48:12.501409Z" } }, "cell_type": "code", "source": [ "props_xor = new_props ^ old_props\n", "pos_xor = new_pos ^ old_pos\n", "for user in customer.users.values():\n", " user.renew = True\n", " user.cipher.combined_position_key = user.cipher.combined_position_key ^ pos_xor\n", " user.cipher.property_key = user.cipher.property_key ^ props_xor" ], "outputs": [], "execution_count": 24 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Refresh User Keys\n", "After a user's first successful login, the renew flag is checked. If it's true, the user's cipher is replaced with a new cipher." ] }, { "metadata": { "ExecuteTime": { "end_time": "2025-03-24T09:48:12.759002Z", "start_time": "2025-03-24T09:48:12.522251Z" } }, "cell_type": "code", "source": [ "if user.renew:\n", " user.cipher = UserCipher.create(\n", " customer.cipher.keypad_size,\n", " customer.cipher.position_key,\n", " user.cipher.max_nkode_len\n", " )\n", " user.enciphered_passcode = user.cipher.encipher_nkode(presumed_property_indices, customer.cipher)\n", " user.renew = False" ], "outputs": [], "execution_count": 25 } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 0 }