896 lines
27 KiB
Plaintext
896 lines
27 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"source": [
|
|
"from src.nkode_api import NKodeAPI\n",
|
|
"from src.models import NKodePolicy, KeypadSize\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 select_keys_with_passcode_values(user_passcode_idxs: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]:\n",
|
|
" indices = [np.where(keypad == prop)[0][0] for prop in user_passcode_idxs]\n",
|
|
" return [int(index // props_per_key) for index in indices]\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-20T08:32:08.092320Z",
|
|
"start_time": "2025-03-20T08:32:08.087658Z"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"execution_count": 27
|
|
},
|
|
{
|
|
"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-20T08:32:08.113043Z",
|
|
"start_time": "2025-03-20T08:32:08.110624Z"
|
|
}
|
|
},
|
|
"outputs": [],
|
|
"execution_count": 28
|
|
},
|
|
{
|
|
"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 user 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 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-20T08:32:08.198233Z",
|
|
"start_time": "2025-03-20T08:32:08.193182Z"
|
|
}
|
|
},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Customer Position Key: [21284 3999 4057 48308 14680 46323]\n",
|
|
"Customer Properties Key:\n",
|
|
"[45633 21215 48438 45863 52540 14191]\n",
|
|
"[11907 4042 56372 63103 45179 58318]\n",
|
|
"[28909 48497 31171 15125 2886 9246]\n",
|
|
"[15651 10936 5595 16546 8096 13333]\n",
|
|
"[41923 43364 15227 43001 11056 62605]\n",
|
|
"Position to Properties Map:\n",
|
|
"21284: [45633 11907 28909 15651 41923]\n",
|
|
"3999: [21215 4042 48497 10936 43364]\n",
|
|
"4057: [48438 56372 31171 5595 15227]\n",
|
|
"48308: [45863 63103 15125 16546 43001]\n",
|
|
"14680: [52540 45179 2886 8096 11056]\n",
|
|
"46323: [14191 58318 9246 13333 62605]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 29
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.226660Z",
|
|
"start_time": "2025-03-20T08:32:08.224216Z"
|
|
}
|
|
},
|
|
"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",
|
|
"21284: ['😀' '🥺' '🤔' '🐱' '🦄']\n",
|
|
"3999: ['😂' '😡' '🙃' '🐶' '🌟']\n",
|
|
"4057: ['🥳' '😱' '😇' '🦁' '⚡']\n",
|
|
"48308: ['😍' '🤯' '🤖' '🐻' '🔥']\n",
|
|
"14680: ['🤓' '🥰' '👽' '🐸' '🍕']\n",
|
|
"46323: ['😎' '😴' '👾' '🐙' '🎉']\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 30
|
|
},
|
|
{
|
|
"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 what their server-side properties"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.256237Z",
|
|
"start_time": "2025-03-20T08:32:08.250147Z"
|
|
}
|
|
},
|
|
"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": [
|
|
"<IPython.core.display.Markdown object>"
|
|
],
|
|
"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": [
|
|
"<IPython.core.display.Markdown object>"
|
|
],
|
|
"text/markdown": "### Index Keypad"
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Key 0: [24 1 14 16 11]\n",
|
|
"Key 1: [18 25 8 22 5]\n",
|
|
"Key 2: [12 13 20 10 23]\n",
|
|
"Key 3: [ 0 7 26 28 17]\n",
|
|
"Key 4: [ 6 19 2 4 29]\n"
|
|
]
|
|
},
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"<IPython.core.display.Markdown object>"
|
|
],
|
|
"text/markdown": "### Customer Properties Keypad"
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Key 0: [41923 21215 31171 2886 58318]\n",
|
|
"Key 1: [15651 43364 56372 8096 14191]\n",
|
|
"Key 2: [28909 48497 5595 45179 13333]\n",
|
|
"Key 3: [45633 4042 15227 11056 9246]\n",
|
|
"Key 4: [11907 10936 48438 52540 62605]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 31
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"## Set nKode\n",
|
|
"The client receives `user_icons`, `set_signup_keypad`\n"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.280568Z",
|
|
"start_time": "2025-03-20T08:32:08.277339Z"
|
|
}
|
|
},
|
|
"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: [19, 7, 10, 6]\n",
|
|
"User Passcode Icons: ['🐶' '😡' '🥰' '🥺']\n",
|
|
"User Passcode Server-side properties: [10936 4042 45179 11907]\n",
|
|
"Selected Keys: [4, 3, 2, 4]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 32
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.316148Z",
|
|
"start_time": "2025-03-20T08:32:08.313495Z"
|
|
}
|
|
},
|
|
"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}\")"
|
|
],
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Key 0: [18 7 20 4 11]\n",
|
|
"Key 1: [12 1 26 22 29]\n",
|
|
"Key 2: [ 0 19 14 10 5]\n",
|
|
"Key 3: [24 25 2 28 23]\n",
|
|
"Key 4: [ 6 13 8 16 17]\n",
|
|
"Selected Keys\n",
|
|
"[2, 0, 2, 4]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 33
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.575664Z",
|
|
"start_time": "2025-03-20T08:32:08.337518Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": [
|
|
"# the session is deleted after the nkode is confirmed. To rerun this cell, rerun the cells above starting with cell 8 where the username is created\n",
|
|
"success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n",
|
|
"assert success"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 34
|
|
},
|
|
{
|
|
"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-20T08:32:08.588049Z",
|
|
"start_time": "2025-03-20T08:32:08.583087Z"
|
|
}
|
|
},
|
|
"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": 35
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.596984Z",
|
|
"start_time": "2025-03-20T08:32:08.595221Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": "print(f\"Property Key:\\n{user_prop_key_keypad}\")",
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Property Key:\n",
|
|
"[[43643 58945 45655 10493 17462 5635]\n",
|
|
" [42071 15680 60860 39600 15784 4102]\n",
|
|
" [60857 21300 14877 25869 12858 50934]\n",
|
|
" [55451 44486 22660 41758 36853 37697]\n",
|
|
" [24236 53340 57175 52425 5167 2017]]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 36
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.615012Z",
|
|
"start_time": "2025-03-20T08:32:08.613119Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": "print(f\"Passcode Key: {user_cipher.pass_key}\")",
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Passcode Key: [31049 4633 40678 55986 14115 22499 3470 53359 20871 60539]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 37
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.639696Z",
|
|
"start_time": "2025-03-20T08:32:08.637469Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": "print(f\"Mask Key: {user_cipher.mask_key}\")",
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Mask Key: [55361 38182 36656 63013 26815 17961 23911 65497 28524 60226]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 38
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.658960Z",
|
|
"start_time": "2025-03-20T08:32:08.657009Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": "print(f\"Combined Position Key: {user_cipher.combined_position_key}\")",
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Combined Position Key: [33934 3750 42586 25190 7504 35546]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 39
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.678360Z",
|
|
"start_time": "2025-03-20T08:32:08.676228Z"
|
|
}
|
|
},
|
|
"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: [55210 313 43395 57042 9224 15913]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 40
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:08.698976Z",
|
|
"start_time": "2025-03-20T08:32:08.696420Z"
|
|
}
|
|
},
|
|
"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",
|
|
"33934: [43643 42071 60857 55451 24236]\n",
|
|
"3750: [58945 15680 21300 44486 53340]\n",
|
|
"42586: [45655 60860 14877 22660 57175]\n",
|
|
"25190: [10493 39600 25869 41758 52425]\n",
|
|
"7504: [17462 15784 12858 36853 5167]\n",
|
|
"35546: [ 5635 4102 50934 37697 2017]\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 41
|
|
},
|
|
{
|
|
"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-20T08:32:08.762578Z",
|
|
"start_time": "2025-03-20T08:32:08.760236Z"
|
|
}
|
|
},
|
|
"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": 42
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"#### Encipher Passcode\n",
|
|
"UserCipherKeys.encipher_salt_hash_code:\n",
|
|
"\n",
|
|
"- ciphered_customer_prop = alpha_key ^ customer_prop\n",
|
|
"- ciphered_passcode_i = pass_key_i ^ ciphered_customer_prop_i\n",
|
|
"- code = hash(ciphered_passcode, salt)"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:09.024060Z",
|
|
"start_time": "2025-03-20T08:32:08.789902Z"
|
|
}
|
|
},
|
|
"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",
|
|
"user_passcode_padded = np.concatenate((user_passcode, np.zeros(pad_len, dtype=user_passcode.dtype)))\n",
|
|
"ciphered_passcode = user_passcode_padded ^ 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": 43
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"### Enciphered nKode\n",
|
|
"An encipher passcode has two parts:\n",
|
|
"1. Code: the enciphered and hashed passcode\n",
|
|
"2. Mask: the mask is used to recover the passcode sets. The mask and the users key select are used to recover the property values of the user's passcode\n",
|
|
"The method UserCipherKeys.encipher_nkode secures a users nKode in the database. This method is called in api.confirm_nkode\n",
|
|
"```\n",
|
|
"class EncipheredNKode(BaseModel):\n",
|
|
" code: str\n",
|
|
" mask: str\n",
|
|
"```\n"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:09.032955Z",
|
|
"start_time": "2025-03-20T08:32:09.030937Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": [
|
|
"from src.models import EncipheredNKode\n",
|
|
"\n",
|
|
"enciphered_nkode = EncipheredNKode(\n",
|
|
" mask=encoded_mask,\n",
|
|
" code=passcode_hash,\n",
|
|
")"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 44
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"### User Login\n",
|
|
"1. Get login keypad\n",
|
|
"2. Login\n"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:09.273099Z",
|
|
"start_time": "2025-03-20T08:32:09.040255Z"
|
|
}
|
|
},
|
|
"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: [24 1 14 27 16 11]\n",
|
|
"Key 1: [18 25 8 3 22 5]\n",
|
|
"Key 2: [12 13 20 21 10 23]\n",
|
|
"Key 3: [ 0 7 26 9 28 17]\n",
|
|
"Key 4: [ 6 19 2 15 4 29]\n",
|
|
"User Passcode: [19, 7, 10, 6]\n",
|
|
"\n",
|
|
"Selected Keys:\n",
|
|
" [4, 3, 2, 4]\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 45
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"## Validate Login Key Entry\n",
|
|
"- decipher user mask and recover nkode set values\n",
|
|
"- get presumed properties from key selection and set values\n",
|
|
"- encipher, salt and hash presumed properties values and compare it to the users hashed code"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"### Decipher Mask\n",
|
|
"Recall:\n",
|
|
"- combined_set_key = user_set_key ^ customer_set_key\n",
|
|
"- mask = mask_key ^ ordered_user_set_key\n",
|
|
"\n",
|
|
"Recover nKode set values: \n",
|
|
"- decode mask from base64 to int\n",
|
|
"- ordered_user_set_key = mask ^ mask_key\n",
|
|
"- ordered_combined_set_key = ordered_customer_set_key ^ ordered_user_set_key\n",
|
|
"- deduce the set indices"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:09.282728Z",
|
|
"start_time": "2025-03-20T08:32:09.279895Z"
|
|
}
|
|
},
|
|
"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",
|
|
"deciphered_mask = mask ^ user.cipher.mask_key\n",
|
|
"set_key = customer.cipher.position_key ^ user.cipher.combined_position_key\n",
|
|
"passcode_set_index = [int(np.where(set_key == set_cipher)[0][0]) for set_cipher in deciphered_mask[:passcode_len]]\n",
|
|
"presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, passcode_set_index)\n",
|
|
"assert passcode_property_indices == presumed_selected_properties_idx\n"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 46
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": "### Compare Enciphered Passcodes"
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:09.526068Z",
|
|
"start_time": "2025-03-20T08:32:09.295445Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": [
|
|
"valid_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n",
|
|
"assert valid_nkode\n"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 47
|
|
},
|
|
{
|
|
"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-20T08:32:09.996256Z",
|
|
"start_time": "2025-03-20T08:32:09.529950Z"
|
|
}
|
|
},
|
|
"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_user_enciphered_code() \n",
|
|
"api.renew_keys(customer_id)\n",
|
|
"print_user_enciphered_code()\n",
|
|
"\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)\n",
|
|
"assert success\n",
|
|
"print_user_enciphered_code()"
|
|
],
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"mask: cz9m1OunYlEK42X0jRrGAzKvheg=, code: $2b$12$Z.N7qwUTMgSVJFQC9hKgjeQ8owBpZMm5Aa14RQdiJH7C8l61QJENS\n",
|
|
"\n",
|
|
"mask: cz9m1OunYlEK42X0jRrGAzKvheg=, code: $2b$12$Z.N7qwUTMgSVJFQC9hKgjeQ8owBpZMm5Aa14RQdiJH7C8l61QJENS\n",
|
|
"\n",
|
|
"mask: e+RtDYeB1G1RfuTOjCna6K9xLUU=, code: $2b$12$DHdD52jbBdVoXYArhWCm7eABlnch.tNhO/1Eipygj8fpoUFuPzyEC\n",
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"execution_count": 48
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"#### Renew Customer Keys\n",
|
|
"- Get old properties and sets\n",
|
|
"- Replace properties and sets"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:10.012897Z",
|
|
"start_time": "2025-03-20T08:32:10.009292Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": [
|
|
"old_props = customer.cipher.property_key.copy()\n",
|
|
"old_sets = customer.cipher.position_key.copy()\n",
|
|
"customer.cipher.renew()\n",
|
|
"new_props = customer.cipher.property_key\n",
|
|
"new_sets = customer.cipher.position_key"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 49
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": [
|
|
"### Renew User\n",
|
|
"\n"
|
|
]
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:10.019285Z",
|
|
"start_time": "2025-03-20T08:32:10.017224Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": [
|
|
"props_xor = np.bitwise_xor(new_props, old_props)\n",
|
|
"sets_xor = np.bitwise_xor(new_sets, old_sets)\n",
|
|
"for user in customer.users.values():\n",
|
|
" user.renew = True\n",
|
|
" user.cipher.combined_position_key = np.bitwise_xor(user.cipher.combined_position_key, sets_xor)\n",
|
|
" user.cipher.property_key = np.bitwise_xor(user.cipher.property_key, props_xor)"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 50
|
|
},
|
|
{
|
|
"metadata": {},
|
|
"cell_type": "markdown",
|
|
"source": "### Refresh User Keys"
|
|
},
|
|
{
|
|
"metadata": {
|
|
"ExecuteTime": {
|
|
"end_time": "2025-03-20T08:32:10.260051Z",
|
|
"start_time": "2025-03-20T08:32:10.026503Z"
|
|
}
|
|
},
|
|
"cell_type": "code",
|
|
"source": [
|
|
"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_selected_properties_idx, customer.cipher)\n",
|
|
"user.renew = False"
|
|
],
|
|
"outputs": [],
|
|
"execution_count": 51
|
|
}
|
|
],
|
|
"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
|
|
}
|