diff --git a/docs/enrollment_diagram.md b/docs/enrollment_diagram.md index fef2bd0..8e030c9 100644 --- a/docs/enrollment_diagram.md +++ b/docs/enrollment_diagram.md @@ -12,26 +12,26 @@ sequenceDiagram Server->>Server: Create Signup Session Note over User,Server: Set nKode Server-->>-Client: signup_session_id, set_keypad, icons - Note left of Server: signup_session_id:
51b187b9-c930-4f5d-81da-c11c082f2c64 - Note left of Server: set_keypad:
Key 0: [ 9 29 3 49 50 25]
Key 1: [27 2 12 22 14 16]
Key 2: [36 47 21 13 23 7]
Key 3: [45 11 30 4 32 43]
Key 4: [18 20 39 40 41 52]
Key 5: [ 0 38 48 31 5 34]
+ Note left of Server: signup_session_id:
1334d391-3c8a-431a-8186-e91b29d42750 + Note left of Server: set_keypad:
Key 0: [18 2 39 13 23 16]
Key 1: [36 20 21 49 5 43]
Key 2: [ 0 47 48 4 50 25]
Key 3: [27 38 12 40 32 52]
Key 4: [45 29 3 31 41 34]
Key 5: [ 9 11 30 22 14 7]
Note left of Server: Icons:
[🍎,🍏,🍊,🍋,🍌,🍉
🍇,🍓,🍒,🍑,🥭,🍍
🥥,🥝,🍅,🍆,🥑,🥕
🌽,🥔,🍠,🥐,🥖,🥨
🥯,🥞,🧀,🍖,🍗,🥚
🍔,🍟,🍕,🌭,🥪,🌮
🌯,🍣,🍤,🍙,🍚,🍜
🍲,🍛,🍱,🥟,🍦,🍧
🍨,🍩,🍪,🎂,🍰,🧁] Client->>Client: Order Icons by keypad Client->>User: Display Keypad - Note left of Client: Key 0: ['🍑' '🥚' '🍋' '🍩' '🍪' '🥞']
Key 1: ['🍖' '🍊' '🥥' '🥖' '🍅' '🥑']
Key 2: ['🌯' '🍧' '🥐' '🥝' '🥨' '🍓']
Key 3: ['🥟' '🍍' '🍔' '🍌' '🍕' '🍛']
Key 4: ['🌽' '🍠' '🍙' '🍚' '🍜' '🍰']
Key 5: ['🍎' '🍤' '🍨' '🍟' '🍉' '🥪']
- Note left of User: User icons: ['🌯' '🍖' '🍰' '🍓'] - User->>Client: Set Key Selection: [2, 1, 4, 2] - Client->>+Server: Set nKode:
51b187b9-c930-4f5d-81da-c11c082f2c64
[2, 1, 4, 2] + Note left of Client: Key 0: ['🌽' '🍊' '🍙' '🥝' '🥨' '🥑']
Key 1: ['🌯' '🍠' '🥐' '🍩' '🍉' '🍛']
Key 2: ['🍎' '🍧' '🍨' '🍌' '🍪' '🥞']
Key 3: ['🍖' '🍤' '🥥' '🍚' '🍕' '🍰']
Key 4: ['🥟' '🥚' '🍋' '🍟' '🍜' '🥪']
Key 5: ['🍑' '🍍' '🍔' '🥖' '🍅' '🍓']
+ Note left of User: User icons: ['🍌' '🍟' '🌽' '🥝'] + User->>Client: Set Key Selection: [2, 4, 0, 0] + Client->>+Server: Set nKode:
1334d391-3c8a-431a-8186-e91b29d42750
[2, 4, 0, 0] Server->>Server: Disperse Set Keypad Note over User,Server: Confirm nKode Server-->>-Client: signup_session_id, confirm_keypad, icons - Note left of Server: signup_session_id:
51b187b9-c930-4f5d-81da-c11c082f2c64 - Note left of Server: confirm_keypad:
Key 0: [27 47 39 4 5 25]
Key 1: [45 20 48 49 14 7]
Key 2: [ 9 38 12 13 32 52]
Key 3: [36 2 30 40 50 34]
Key 4: [18 11 3 31 23 16]
Key 5: [ 0 29 21 22 41 43]
+ Note left of Server: signup_session_id:
1334d391-3c8a-431a-8186-e91b29d42750 + Note left of Server: confirm_keypad:
Key 0: [27 47 3 13 14 43]
Key 1: [ 0 29 30 49 23 52]
Key 2: [45 11 39 40 5 25]
Key 3: [ 9 2 21 4 32 34]
Key 4: [18 20 12 31 50 7]
Key 5: [36 38 48 22 41 16]
Client->>Client: Order Icons by keypad Client->>User: Display Keypad - Note left of Client: Key 0: ['🍖' '🍧' '🍙' '🍌' '🍉' '🥞']
Key 1: ['🥟' '🍠' '🍨' '🍩' '🍅' '🍓']
Key 2: ['🍑' '🍤' '🥥' '🥝' '🍕' '🍰']
Key 3: ['🌯' '🍊' '🍔' '🍚' '🍪' '🥪']
Key 4: ['🌽' '🍍' '🍋' '🍟' '🥨' '🥑']
Key 5: ['🍎' '🥚' '🥐' '🥖' '🍜' '🍛']
- Note left of User: User icons: ['🌯' '🍖' '🍰' '🍓'] - User->>Client: Key Selection: [3, 0, 2, 1] - Client->>+Server: Confirm nKode:
51b187b9-c930-4f5d-81da-c11c082f2c64
[3, 0, 2, 1] + Note left of Client: Key 0: ['🍖' '🍧' '🍋' '🥝' '🍅' '🍛']
Key 1: ['🍎' '🥚' '🍔' '🍩' '🥨' '🍰']
Key 2: ['🥟' '🍍' '🍙' '🍚' '🍉' '🥞']
Key 3: ['🍑' '🍊' '🥐' '🍌' '🍕' '🥪']
Key 4: ['🌽' '🍠' '🥥' '🍟' '🍪' '🍓']
Key 5: ['🌯' '🍤' '🍨' '🥖' '🍜' '🥑']
+ Note left of User: User icons: ['🍌' '🍟' '🌽' '🥝'] + User->>Client: Key Selection: [3, 4, 4, 0] + Client->>+Server: Confirm nKode:
1334d391-3c8a-431a-8186-e91b29d42750
[3, 4, 4, 0] Server->>Server: Create User Server-->>-Client: Success ``` \ No newline at end of file diff --git a/docs/login_diagram.md b/docs/login_diagram.md index bfa0962..4470914 100644 --- a/docs/login_diagram.md +++ b/docs/login_diagram.md @@ -10,13 +10,13 @@ sequenceDiagram Note left of User: email: user@example.com User->>Server: Submit Email Server->>Client: login_keypad, icons - Note left of Server: Login Keypad:
Key 0: [ 9 1 29 3 49 50 33 25 35]
Key 1: [27 28 2 12 22 14 24 16 17]
Key 2: [36 37 47 21 13 23 42 7 26]
Key 3: [45 19 11 30 4 32 51 43 8]
Key 4: [18 46 20 39 40 41 15 52 44]
Key 5: [ 0 10 38 48 31 5 6 34 53]
+ Note left of Server: Login Keypad:
Key 0: [18 46 2 39 13 23 6 16 53]
Key 1: [36 28 20 21 49 5 15 43 8]
Key 2: [ 0 10 47 48 4 50 51 25 35]
Key 3: [27 1 38 12 40 32 42 52 17]
Key 4: [45 37 29 3 31 41 24 34 44]
Key 5: [ 9 19 11 30 22 14 33 7 26]
Note left of Server: Icons:
[🍎,🍏,🍊,🍋,🍌,🍉
🍇,🍓,🍒,🍑,🥭,🍍
🥥,🥝,🍅,🍆,🥑,🥕
🌽,🥔,🍠,🥐,🥖,🥨
🥯,🥞,🧀,🍖,🍗,🥚
🍔,🍟,🍕,🌭,🥪,🌮
🌯,🍣,🍤,🍙,🍚,🍜
🍲,🍛,🍱,🥟,🍦,🍧
🍨,🍩,🍪,🎂,🍰,🧁] Client->>Client: Order Icons Client->>User: Display Keypad - Note left of Client: Key 0: ['🍑' '🍏' '🥚' '🍋' '🍩' '🍪' '🌭' '🥞' '🌮']
Key 1: ['🍖' '🍗' '🍊' '🥥' '🥖' '🍅' '🥯' '🥑' '🥕']
Key 2: ['🌯' '🍣' '🍧' '🥐' '🥝' '🥨' '🍲' '🍓' '🧀']
Key 3: ['🥟' '🥔' '🍍' '🍔' '🍌' '🍕' '🎂' '🍛' '🍒']
Key 4: ['🌽' '🍦' '🍠' '🍙' '🍚' '🍜' '🍆' '🍰' '🍱']
Key 5: ['🍎' '🥭' '🍤' '🍨' '🍟' '🍉' '🍇' '🥪' '🧁']
- Note left of User: User passcode icons: ['🌯' '🍖' '🍰' '🍓'] - User->>Client: Selected Keys
[2, 1, 4, 2] - Client->>Server: Login:
email: user@example.com
selected_keys: [2, 1, 4, 2] + Note left of Client: Key 0: ['🌽' '🍦' '🍊' '🍙' '🥝' '🥨' '🍇' '🥑' '🧁']
Key 1: ['🌯' '🍗' '🍠' '🥐' '🍩' '🍉' '🍆' '🍛' '🍒']
Key 2: ['🍎' '🥭' '🍧' '🍨' '🍌' '🍪' '🎂' '🥞' '🌮']
Key 3: ['🍖' '🍏' '🍤' '🥥' '🍚' '🍕' '🍲' '🍰' '🥕']
Key 4: ['🥟' '🍣' '🥚' '🍋' '🍟' '🍜' '🥯' '🥪' '🍱']
Key 5: ['🍑' '🥔' '🍍' '🍔' '🥖' '🍅' '🌭' '🍓' '🧀']
+ Note left of User: User passcode icons: ['🍌' '🍟' '🌽' '🥝'] + User->>Client: Selected Keys
[2, 4, 0, 0] + Client->>Server: Login:
email: user@example.com
selected_keys: [2, 4, 0, 0] Server-->>Client: Success ``` \ No newline at end of file diff --git a/docs/scripts/render_diagrams_md.py b/docs/scripts/render_diagrams_md.py index 09b5281..69cd0fe 100644 --- a/docs/scripts/render_diagrams_md.py +++ b/docs/scripts/render_diagrams_md.py @@ -39,20 +39,20 @@ if __name__ == "__main__": props_per_key=9 ) customer_id = api.create_new_customer(keypad_size, policy) - signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id) - ordered_set_icons = emojis[set_signup_keypad] username = "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) + signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username) + ordered_set_icons = emojis[set_signup_keypad] passcode_len = 4 passcode_property_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist() selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_signup_keypad, keypad_size.numb_of_keys) - confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, signup_session_id) + confirm_keypad = api.set_nkode(customer_id, selected_keys_set, signup_session_id) selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys) ordered_confirm_icons = emojis[confirm_keypad] print(f"Selected Keys\n{selected_keys_confirm}") - success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id) + success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id) context = { "email": "user@example.com", "signup_session_id": signup_session_id, diff --git a/docs/templates/keypad_dispersion.template.md b/docs/templates/keypad_dispersion.template.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/templates/keypad_shuffle.template.md b/docs/templates/keypad_shuffle.template.md new file mode 100644 index 0000000..e69de29 diff --git a/notebooks/Enrollment_Login_Renewal.ipynb b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb similarity index 86% rename from notebooks/Enrollment_Login_Renewal.ipynb rename to notebooks/Enrollment_Login_Renewal_Detailed.ipynb index eed046a..a807a5c 100644 --- a/notebooks/Enrollment_Login_Renewal.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb @@ -29,15 +29,18 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T09:48:10.442598Z", - "start_time": "2025-03-24T09:48:10.410209Z" + "end_time": "2025-03-24T20:06:28.904351Z", + "start_time": "2025-03-24T20:06:28.864248Z" } }, "outputs": [], - "execution_count": 1 + "execution_count": 4 }, { + "metadata": {}, "cell_type": "code", + "outputs": [], + "execution_count": null, "source": [ "api = NKodeAPI()\n", "user_icons = np.array([\n", @@ -48,16 +51,7 @@ " \"🦁\", \"🐻\", \"🐸\", \"🐙\", \"🦄\",\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": {}, @@ -70,6 +64,7 @@ ] }, { + "metadata": {}, "cell_type": "markdown", "source": [ "#### Customer Cipher Keys\n", @@ -78,13 +73,13 @@ "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 - } + ] }, { + "metadata": {}, "cell_type": "code", + "outputs": [], + "execution_count": null, "source": [ "policy = NKodePolicy(\n", " max_nkode_len=10,\n", @@ -107,69 +102,20 @@ "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" - } - }, + "metadata": {}, "cell_type": "code", + "outputs": [], + "execution_count": null, "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": {}, @@ -191,88 +137,20 @@ ] }, { - "metadata": { - "ExecuteTime": { - "end_time": "2025-03-24T09:48:10.653252Z", - "start_time": "2025-03-24T09:48:10.646267Z" - } - }, + "metadata": {}, "cell_type": "code", + "outputs": [], + "execution_count": null, "source": [ - "signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id)\n", + "username = random_username()\n", + "signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username)\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": {}, @@ -290,16 +168,6 @@ } }, "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", @@ -312,7 +180,16 @@ ] } ], - "execution_count": 6 + "execution_count": 6, + "source": [ + "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}\")" + ] }, { "metadata": {}, @@ -331,11 +208,11 @@ }, "cell_type": "code", "source": [ - "confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, signup_session_id)\n", + "confirm_keypad = api.set_nkode(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", + "success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id)\n", "assert success" ], "outputs": [ diff --git a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb new file mode 100644 index 0000000..abf8e79 --- /dev/null +++ b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb @@ -0,0 +1,210 @@ +{ + "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", + "\n", + "def random_username() -> str:\n", + " return \"test_username\" + \"\".join([choice(ascii_lowercase) for _ in range(6)])\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2025-03-24T20:18:04.947152Z", + "start_time": "2025-03-24T20:18:04.916604Z" + } + }, + "outputs": [], + "execution_count": 1 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "## Initialize nKode API" + }, + { + "cell_type": "code", + "source": [ + "api = NKodeAPI()\n", + "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.get_customer(customer_id)" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2025-03-24T20:18:04.960698Z", + "start_time": "2025-03-24T20:18:04.950723Z" + } + }, + "outputs": [], + "execution_count": 2 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## nKode Enrollment\n", + "Users enroll in three steps:\n" + ] + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:18:04.989029Z", + "start_time": "2025-03-24T20:18:04.986797Z" + } + }, + "cell_type": "code", + "source": [ + "username = random_username()\n", + "signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username)" + ], + "outputs": [], + "execution_count": 3 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### Set nKode\n", + "The client receives `user_icons`, `set_signup_keypad`\n" + ] + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:18:04.997252Z", + "start_time": "2025-03-24T20:18:04.995177Z" + } + }, + "cell_type": "code", + "source": [ + "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)" + ], + "outputs": [], + "execution_count": 4 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### Confirm nKode\n", + "Submit the set key entry to render the confirm keypad." + ] + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:18:05.239384Z", + "start_time": "2025-03-24T20:18:05.002825Z" + } + }, + "cell_type": "code", + "source": [ + "confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, signup_session_id)\n", + "selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys)\n", + "success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n", + "assert success" + ], + "outputs": [], + "execution_count": 5 + }, + { + "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-24T20:18:05.478978Z", + "start_time": "2025-03-24T20:18:05.245767Z" + } + }, + "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", + "success = api.login(customer_id, username, selected_keys_login)\n", + "assert success" + ], + "outputs": [], + "execution_count": 6 + }, + { + "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-24T20:18:05.950627Z", + "start_time": "2025-03-24T20:18:05.484941Z" + } + }, + "cell_type": "code", + "source": [ + "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", + "assert success" + ], + "outputs": [], + "execution_count": 7 + } + ], + "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 +} diff --git a/src/nkode_api.py b/src/nkode_api.py index 743ce3b..971b702 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -15,6 +15,9 @@ class NKodeAPI: customers: dict[UUID, Customer] = field(default_factory=dict) signup_sessions: dict[UUID, UserSignupSession] = field(default_factory=dict) + def get_customer(self, customer_id: UUID) -> Customer: + return self.customers[customer_id] + def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer.create( cipher=CustomerCipher.create(keypad_size), @@ -23,13 +26,17 @@ class NKodeAPI: self.customers[new_customer.customer_id] = new_customer return new_customer.customer_id - def generate_signup_keypad(self, customer_id: UUID) -> tuple[UUID, np.ndarray]: + def generate_signup_keypad(self, customer_id: UUID, username: str) -> tuple[UUID, np.ndarray]: if customer_id not in self.customers.keys(): raise ValueError(f"Customer with ID '{customer_id}' does not exist") customer = self.customers[customer_id] + if username in customer.users.keys(): + raise ValueError(f"Username '{username}' already exists for this customer") + customer = self.customers[customer_id] login_keypad = UserKeypad.create(customer.cipher.keypad_size) set_keypad = login_keypad.sign_up_keypad() new_session = UserSignupSession( + username=username, session_id=uuid4(), login_keypad=login_keypad, set_keypad=set_keypad.keypad, @@ -41,24 +48,20 @@ class NKodeAPI: def set_nkode( self, - username: str, customer_id: UUID, key_selection: list[int], session_id: UUID ) -> np.ndarray: if customer_id not in self.customers.keys(): raise ValueError(f"Customer ID {customer_id} not found") - customer = self.customers[customer_id] - if username in customer.users.keys(): - raise ValueError(f"Username '{username}' already exists for this customer") if session_id not in self.signup_sessions.keys(): raise ValueError(f"Session ID {session_id} not found") - self.signup_sessions[session_id].set_user_nkode(username, key_selection) + session = self.signup_sessions[session_id] + self.signup_sessions[session_id].set_user_nkode(session.username, key_selection) return self.signup_sessions[session_id].confirm_keypad def confirm_nkode( self, - username: str, customer_id: UUID, confirm_key_entry: list[int], session_id: UUID @@ -68,8 +71,6 @@ class NKodeAPI: session = self.signup_sessions[session_id] if customer_id != session.customer_id: raise AssertionError(f"Customer ID mismatch: {customer_id} vs {session.customer_id}") - if username != session.username: - raise AssertionError(f"Username mismatch: {username} vs {session.username}") customer = self.customers[customer_id] passcode = self.signup_sessions[session_id].deduce_passcode(confirm_key_entry) new_user_keys = UserCipher.create( @@ -79,7 +80,7 @@ class NKodeAPI: ) enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.cipher) new_user = User( - username=username, + username=session.username, enciphered_passcode=enciphered_passcode, cipher=new_user_keys, user_keypad=self.signup_sessions[session_id].login_keypad, diff --git a/src/user_signup_session.py b/src/user_signup_session.py index 887ec38..65a1bd6 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -11,11 +11,10 @@ class UserSignupSession: customer_id: UUID login_keypad: UserKeypad keypad_size: KeypadSize - # TODO: revert back to list[int] + username: str set_keypad: Optional[np.ndarray] = None confirm_keypad: Optional[np.ndarray] = None set_key_entry: Optional[np.ndarray] = None - username: Optional[str] = None def deduce_passcode(self, confirm_key_entry: list[int]) -> list[int]: if not all(0 <= key <= self.keypad_size.numb_of_keys for key in confirm_key_entry): diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 63332fd..d89e2de 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -15,20 +15,15 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passcode_len): username = "test_username" nkode_policy = NKodePolicy() # default policy customer_id = nkode_api.create_new_customer(keypad_size, nkode_policy) - session_id, set_keypad = nkode_api.generate_signup_keypad(customer_id) + session_id, set_keypad = nkode_api.generate_signup_keypad(customer_id, username) user_passcode = set_keypad[:passcode_len] signup_key_selection = lambda keypad: [int(np.where(keypad == prop)[0][0]) // keypad_size.numb_of_keys for prop in user_passcode] set_key_selection = signup_key_selection(set_keypad) - confirm_keypad = nkode_api.set_nkode(username, customer_id, set_key_selection, session_id) + confirm_keypad = nkode_api.set_nkode(customer_id, set_key_selection, session_id) confirm_key_selection = signup_key_selection(confirm_keypad) - successful_confirm = nkode_api.confirm_nkode( - username, - customer_id, - confirm_key_selection, - session_id - ) + successful_confirm = nkode_api.confirm_nkode(customer_id, confirm_key_selection, session_id) assert successful_confirm sign_in_key_selection = lambda keypad: [int(np.where(keypad ==prop)[0][0]) // keypad_size.props_per_key for prop in user_passcode]