From 98bd8960bad783b8d5f6331f8c48e96c54225c2c Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 10 Nov 2024 13:36:55 -0600 Subject: [PATCH 01/85] test.txt --- test.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test.txt diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..e69de29 -- 2.49.1 From 7688f3c25260398b920ef4e40178bb0b2d990f42 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 10 Nov 2024 13:37:43 -0600 Subject: [PATCH 02/85] remove test.txt --- test.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test.txt diff --git a/test.txt b/test.txt deleted file mode 100644 index e69de29..0000000 -- 2.49.1 From 88b533c27bb57e025572cf844ed8b8836315ac57 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 7 Mar 2025 13:04:10 -0600 Subject: [PATCH 03/85] refactor loose files --- render_markdown.py => docs/render_markdown.py | 4 +- .../dispersion_tutorial.ipynb | 0 .../nkode_calculation.ipynb | 0 .../nkode_tutorial.ipynb | 254 ++++++++++-------- nkode_api.py => src/nkode_api.py | 0 test/test_nkode_api.py | 2 +- 6 files changed, 152 insertions(+), 108 deletions(-) rename render_markdown.py => docs/render_markdown.py (99%) rename dispersion_tutorial.ipynb => notebooks/dispersion_tutorial.ipynb (100%) rename nkode_calculation.ipynb => notebooks/nkode_calculation.ipynb (100%) rename nkode_tutorial.ipynb => notebooks/nkode_tutorial.ipynb (79%) rename nkode_api.py => src/nkode_api.py (100%) diff --git a/render_markdown.py b/docs/render_markdown.py similarity index 99% rename from render_markdown.py rename to docs/render_markdown.py index c41b0eb..5b61aaa 100644 --- a/render_markdown.py +++ b/docs/render_markdown.py @@ -1,6 +1,6 @@ from jinja2 import Environment, FileSystemLoader import os -from nkode_api import NKodeAPI +from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize, EncipheredNKode from src.user_cipher_keys import UserCipherKeys from src.utils import list_to_matrix, matrix_transpose, xor_lists @@ -29,7 +29,7 @@ def keypad_view(interface: list[int], attrs_per_key: int): def render_nkode_authentication(data: dict): # Set up the Jinja2 environment and template loader - file_loader = FileSystemLoader('./docs') + file_loader = FileSystemLoader('') env = Environment(loader=file_loader) # Load the template diff --git a/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb similarity index 100% rename from dispersion_tutorial.ipynb rename to notebooks/dispersion_tutorial.ipynb diff --git a/nkode_calculation.ipynb b/notebooks/nkode_calculation.ipynb similarity index 100% rename from nkode_calculation.ipynb rename to notebooks/nkode_calculation.ipynb diff --git a/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb similarity index 79% rename from nkode_tutorial.ipynb rename to notebooks/nkode_tutorial.ipynb index b083088..e2d9abc 100644 --- a/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -3,7 +3,7 @@ { "cell_type": "code", "source": [ - "from nkode_api import NKodeAPI\n", + "from src.nkode_api import NKodeAPI\n", "from src.models import NKodePolicy, KeypadSize\n", "from src.utils import list_to_matrix, matrix_transpose\n", "from secrets import choice\n", @@ -12,12 +12,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.475149Z", - "start_time": "2024-08-05T17:40:17.473025Z" + "end_time": "2025-03-07T19:01:08.154404Z", + "start_time": "2025-03-07T19:01:08.149432Z" } }, "outputs": [], - "execution_count": 12 + "execution_count": 58 }, { "cell_type": "code", @@ -38,12 +38,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.556128Z", - "start_time": "2024-08-05T17:40:17.552985Z" + "end_time": "2025-03-07T19:01:08.165511Z", + "start_time": "2025-03-07T19:01:08.162339Z" } }, "outputs": [], - "execution_count": 13 + "execution_count": 59 }, { "cell_type": "code", @@ -53,12 +53,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.559851Z", - "start_time": "2024-08-05T17:40:17.557794Z" + "end_time": "2025-03-07T19:01:08.174320Z", + "start_time": "2025-03-07T19:01:08.172724Z" } }, "outputs": [], - "execution_count": 14 + "execution_count": 60 }, { "cell_type": "markdown", @@ -98,12 +98,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.740656Z", - "start_time": "2024-08-05T17:40:17.570041Z" + "end_time": "2025-03-07T19:01:08.378578Z", + "start_time": "2025-03-07T19:01:08.188114Z" } }, "outputs": [], - "execution_count": 15 + "execution_count": 61 }, { "cell_type": "markdown", @@ -140,8 +140,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.744240Z", - "start_time": "2024-08-05T17:40:17.741773Z" + "end_time": "2025-03-07T19:01:08.387613Z", + "start_time": "2025-03-07T19:01:08.385501Z" } }, "outputs": [ @@ -149,17 +149,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Sets: [41463, 23817, 10115, 59621, 17701, 39232]\n", + "Customer Sets: [14533, 13441, 52408, 39610, 49828, 11300]\n", "Customer Attributes:\n", - "[15101, 42072, 1835, 15413, 46991, 41978]\n", - "[47133, 25183, 43097, 17581, 20893, 52753]\n", - "[14863, 31428, 41789, 18633, 3962, 50372]\n", - "[53357, 51786, 31149, 59745, 30907, 16058]\n", - "[60389, 36053, 8246, 53494, 23730, 15317]\n" + "[50415, 3350, 62907, 34493, 46982, 55292]\n", + "[23503, 57678, 17001, 8963, 4893, 53685]\n", + "[25912, 9324, 2770, 57761, 57056, 5837]\n", + "[49831, 16518, 53473, 35853, 12433, 20763]\n", + "[60930, 19614, 2083, 2879, 58781, 13705]\n" ] } ], - "execution_count": 16 + "execution_count": 62 }, { "cell_type": "markdown", @@ -183,8 +183,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.747131Z", - "start_time": "2024-08-05T17:40:17.744937Z" + "end_time": "2025-03-07T19:01:08.412394Z", + "start_time": "2025-03-07T19:01:08.410114Z" } }, "outputs": [ @@ -193,16 +193,16 @@ "output_type": "stream", "text": [ "Set to Attribute Map:\n", - "41463: [15101, 47133, 14863, 53357, 60389]\n", - "23817: [42072, 25183, 31428, 51786, 36053]\n", - "10115: [1835, 43097, 41789, 31149, 8246]\n", - "59621: [15413, 17581, 18633, 59745, 53494]\n", - "17701: [46991, 20893, 3962, 30907, 23730]\n", - "39232: [41978, 52753, 50372, 16058, 15317]\n" + "14533: [50415, 23503, 25912, 49831, 60930]\n", + "13441: [3350, 57678, 9324, 16518, 19614]\n", + "52408: [62907, 17001, 2770, 53473, 2083]\n", + "39610: [34493, 8963, 57761, 35853, 2879]\n", + "49828: [46982, 4893, 57056, 12433, 58781]\n", + "11300: [55292, 53685, 5837, 20763, 13705]\n" ] } ], - "execution_count": 17 + "execution_count": 63 }, { "cell_type": "markdown", @@ -232,27 +232,27 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.751757Z", - "start_time": "2024-08-05T17:40:17.748531Z" + "end_time": "2025-03-07T19:01:08.439639Z", + "start_time": "2025-03-07T19:01:08.436629Z" } }, "outputs": [ { "data": { "text/plain": [ - "[[29, 3, 19, 12, 26],\n", - " [5, 27, 1, 0, 8],\n", - " [23, 9, 13, 24, 20],\n", - " [11, 21, 25, 18, 14],\n", - " [17, 15, 7, 6, 2]]" + "[[3, 20, 10, 6, 29],\n", + " [27, 26, 4, 24, 11],\n", + " [15, 2, 22, 0, 5],\n", + " [9, 8, 16, 12, 23],\n", + " [21, 14, 28, 18, 17]]" ] }, - "execution_count": 18, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], - "execution_count": 18 + "execution_count": 64 }, { "cell_type": "markdown", @@ -280,8 +280,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.755453Z", - "start_time": "2024-08-05T17:40:17.752507Z" + "end_time": "2025-03-07T19:01:08.474579Z", + "start_time": "2025-03-07T19:01:08.471813Z" } }, "outputs": [ @@ -290,19 +290,19 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [29, 3, 19, 12, 26]\n", - "Key 1: [5, 27, 1, 0, 8]\n", - "Key 2: [23, 9, 13, 24, 20]\n", - "Key 3: [11, 21, 25, 18, 14]\n", - "Key 4: [17, 15, 7, 6, 2]\n", - "User Passcode: [29, 3, 19, 12]\n", + "Key 0: [3, 20, 10, 6, 29]\n", + "Key 1: [27, 26, 4, 24, 11]\n", + "Key 2: [15, 2, 22, 0, 5]\n", + "Key 3: [9, 8, 16, 12, 23]\n", + "Key 4: [21, 14, 28, 18, 17]\n", + "User Passcode: [3, 20, 10, 6]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side Attributes: [15317, 15413, 51786, 14863]\n" + "User Passcode Server-side Attributes: [34493, 53473, 4893, 23503]\n" ] } ], - "execution_count": 19 + "execution_count": 65 }, { "metadata": {}, @@ -320,8 +320,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:17.760514Z", - "start_time": "2024-08-05T17:40:17.758171Z" + "end_time": "2025-03-07T19:01:08.496620Z", + "start_time": "2025-03-07T19:01:08.494090Z" } }, "outputs": [ @@ -330,17 +330,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [23, 3, 25, 6, 8]\n", - "Key 1: [17, 9, 19, 0, 14]\n", - "Key 2: [5, 15, 13, 18, 26]\n", - "Key 3: [11, 27, 7, 12, 20]\n", - "Key 4: [29, 21, 1, 24, 2]\n", + "Key 0: [15, 8, 28, 24, 29]\n", + "Key 1: [27, 20, 22, 12, 17]\n", + "Key 2: [9, 14, 4, 6, 5]\n", + "Key 3: [3, 2, 16, 18, 11]\n", + "Key 4: [21, 26, 10, 0, 23]\n", "Selected Keys\n", - "[4, 0, 1, 3]\n" + "[3, 1, 4, 2]\n" ] } ], - "execution_count": 20 + "execution_count": 66 }, { "cell_type": "code", @@ -352,8 +352,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:40:18.401946Z", - "start_time": "2024-08-05T17:40:17.761154Z" + "end_time": "2025-03-07T19:01:09.173784Z", + "start_time": "2025-03-07T19:01:08.522178Z" } }, "outputs": [ @@ -365,7 +365,7 @@ ] } ], - "execution_count": 21 + "execution_count": 67 }, { "metadata": {}, @@ -384,8 +384,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-08-05T17:54:37.484873Z", - "start_time": "2024-08-05T17:54:37.480227Z" + "end_time": "2025-03-07T19:01:09.193871Z", + "start_time": "2025-03-07T19:01:09.190856Z" } }, "cell_type": "code", @@ -422,12 +422,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [17701, 39232, 10115, 23817]\n", - "Passcode Attr Vals: [20893, 16058, 41789, 25183]\n" + "Passcode Set Vals: [39610, 52408, 49828, 14533]\n", + "Passcode Attr Vals: [34493, 53473, 4893, 23503]\n" ] } ], - "execution_count": 36 + "execution_count": 68 }, { "metadata": {}, @@ -449,8 +449,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-08-05T17:52:29.553010Z", - "start_time": "2024-08-05T17:52:29.549861Z" + "end_time": "2025-03-07T19:01:09.223843Z", + "start_time": "2025-03-07T19:01:09.221595Z" } }, "cell_type": "code", @@ -466,7 +466,7 @@ "mask = user_keys.encode_base64_str(ciphered_mask)" ], "outputs": [], - "execution_count": 28 + "execution_count": 69 }, { "metadata": {}, @@ -483,8 +483,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-08-05T17:52:34.104235Z", - "start_time": "2024-08-05T17:52:33.794884Z" + "end_time": "2025-03-07T19:01:09.479816Z", + "start_time": "2025-03-07T19:01:09.236822Z" } }, "cell_type": "code", @@ -495,7 +495,7 @@ "from src.utils import int_array_to_bytes\n", "\n", "ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.alpha_key)\n", - "passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in passcode]\n", + "passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", "\n", "passcode_ciphered_attrs.extend([0 for _ in range(pad_len)])\n", @@ -508,13 +508,13 @@ "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 29 + "execution_count": 70 }, { "metadata": { "ExecuteTime": { - "end_time": "2024-08-05T17:52:34.205433Z", - "start_time": "2024-08-05T17:52:34.203278Z" + "end_time": "2025-03-07T19:01:09.495420Z", + "start_time": "2025-03-07T19:01:09.488943Z" } }, "cell_type": "code", @@ -527,7 +527,7 @@ ")" ], "outputs": [], - "execution_count": 30 + "execution_count": 71 }, { "cell_type": "markdown", @@ -553,8 +553,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-08-05T17:52:38.297363Z", - "start_time": "2024-08-05T17:52:37.990052Z" + "end_time": "2025-03-07T19:01:09.736403Z", + "start_time": "2025-03-07T19:01:09.500580Z" } }, "outputs": [ @@ -563,17 +563,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [6, 19, 14, 15, 4, 17]\n", - "Key 1: [24, 13, 26, 3, 28, 23]\n", - "Key 2: [0, 7, 2, 9, 16, 5]\n", - "Key 3: [18, 1, 8, 27, 22, 11]\n", - "Key 4: [12, 25, 20, 21, 10, 29]\n", - "Selected Keys: [4, 1, 0, 4]\n", + "Key 0: [24, 25, 8, 15, 4, 29]\n", + "Key 1: [12, 19, 2, 3, 16, 23]\n", + "Key 2: [18, 7, 26, 21, 28, 5]\n", + "Key 3: [6, 1, 14, 9, 10, 11]\n", + "Key 4: [0, 13, 20, 27, 22, 17]\n", + "Selected Keys: [1, 4, 3, 3]\n", "True\n" ] } ], - "execution_count": 31 + "execution_count": 72 }, { "metadata": {}, @@ -605,8 +605,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-08-05T17:59:58.753205Z", - "start_time": "2024-08-05T17:59:58.749714Z" + "end_time": "2025-03-07T19:01:09.774425Z", + "start_time": "2025-03-07T19:01:09.772200Z" } }, "cell_type": "code", @@ -629,11 +629,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[39232, 59621, 23817, 41463]\n" + "[39610, 52408, 49828, 14533]\n" ] } ], - "execution_count": 38 + "execution_count": 73 }, { "metadata": {}, @@ -643,8 +643,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2024-08-05T17:54:03.252644Z", - "start_time": "2024-08-05T17:54:03.249271Z" + "end_time": "2025-03-07T19:01:09.795190Z", + "start_time": "2025-03-07T19:01:09.792579Z" } }, "cell_type": "code", @@ -669,7 +669,7 @@ ] } ], - "execution_count": 35 + "execution_count": 74 }, { "metadata": {}, @@ -677,15 +677,28 @@ "source": "### Compare Enciphered Passcodes" }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-07T19:01:10.057899Z", + "start_time": "2025-03-07T19:01:09.819860Z" + } + }, "cell_type": "code", "source": [ "enciphered_nkode = user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes)\n", "\n", "print(enciphered_nkode == user.enciphered_passcode.code)\n" ], - "outputs": [], - "execution_count": null + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "execution_count": 75 }, { "cell_type": "markdown", @@ -701,7 +714,12 @@ } }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-07T19:01:11.147274Z", + "start_time": "2025-03-07T19:01:10.066784Z" + } + }, "cell_type": "code", "source": [ "def print_user_enciphered_code():\n", @@ -719,8 +737,19 @@ "print(success)\n", "print_user_enciphered_code()" ], - "outputs": [], - "execution_count": null + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mask: xYa4LE04KVq0jRcaFG9HA8BZHqo=, code: $2b$12$GdGDlzHY6rt.It6xq.EBIud5PgIWKObJdMms3AaYz1JWK1nf4Jj5G\n", + "mask: xYa4LE04KVq0jRcaFG9HA8BZHqo=, code: $2b$12$GdGDlzHY6rt.It6xq.EBIud5PgIWKObJdMms3AaYz1JWK1nf4Jj5G\n", + "True\n", + "mask: jTZF7KTs3zviTwTt7yqXk7BEK7I=, code: $2b$12$BIJdJ/77MAKNtETz8WSB9uWpJo4.jisH/HsN5o55ez294T1a40rxm\n" + ] + } + ], + "execution_count": 76 }, { "metadata": {}, @@ -732,7 +761,12 @@ ] }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-07T19:01:11.355505Z", + "start_time": "2025-03-07T19:01:11.153001Z" + } + }, "cell_type": "code", "source": [ "old_attrs = customer.attributes.attr_vals.copy()\n", @@ -742,7 +776,7 @@ "new_sets = customer.attributes.set_vals" ], "outputs": [], - "execution_count": null + "execution_count": 77 }, { "metadata": {}, @@ -753,7 +787,12 @@ ] }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-07T19:01:11.363643Z", + "start_time": "2025-03-07T19:01:11.361690Z" + } + }, "cell_type": "code", "source": [ "attrs_xor = xor_lists(new_attrs, old_attrs)\n", @@ -764,7 +803,7 @@ " user.user_keys.alpha_key = xor_lists(user.user_keys.alpha_key, attrs_xor)" ], "outputs": [], - "execution_count": null + "execution_count": 78 }, { "metadata": {}, @@ -772,7 +811,12 @@ "source": "### Refresh User Keys" }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-07T19:01:11.989265Z", + "start_time": "2025-03-07T19:01:11.369911Z" + } + }, "cell_type": "code", "source": [ "user.user_keys = UserCipherKeys.new(\n", @@ -784,7 +828,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": null + "execution_count": 79 } ], "metadata": { diff --git a/nkode_api.py b/src/nkode_api.py similarity index 100% rename from nkode_api.py rename to src/nkode_api.py diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 7720beb..186438c 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -1,5 +1,5 @@ import pytest -from nkode_api import NKodeAPI +from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize -- 2.49.1 From 9fdf79842dd8d3ced7127ba51a4df2e28550ee8b Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 07:39:29 -0500 Subject: [PATCH 04/85] refactor asserts --- docs/render_markdown.py | 2 +- src/customer.py | 12 ++++++--- src/customer_attributes.py | 20 +++++++------- src/nkode_api.py | 50 +++++++++++++++++++---------------- src/user.py | 2 +- src/user_cipher_keys.py | 10 ++++--- src/user_interface.py | 23 +++++++++------- src/user_signup_session.py | 15 ++++++----- src/utils.py | 6 +++-- test/test_nkode_interface.py | 2 +- test/test_user_cipher_keys.py | 4 +-- test/test_user_interface.py | 2 +- 12 files changed, 84 insertions(+), 64 deletions(-) diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 5b61aaa..ddd1439 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -181,7 +181,7 @@ if __name__ == "__main__": """ REFRESH USER KEYS """ - user.user_keys = UserCipherKeys.new( + user.user_keys = UserCipherKeys.create( customer.attributes.keypad_size, customer.attributes.set_vals, user.user_keys.max_nkode_len diff --git a/src/customer.py b/src/customer.py index bc278bf..8b25694 100644 --- a/src/customer.py +++ b/src/customer.py @@ -14,13 +14,19 @@ class Customer(BaseModel): # TODO: validate policy and keypad size don't conflict - def add_new_user(self, user: User): + if user.username in self.users: + raise ValueError(f"User with username '{user.username}' already exists") self.users[user.username] = user def valid_key_entry(self, username, selected_keys) -> bool: - assert (username in self.users.keys()) - assert (all(0 <= key_idx < self.attributes.keypad_size.numb_of_keys for key_idx in selected_keys)) + if username not in self.users: + raise ValueError(f"User '{username}' does not exist") + + keypad_size = self.attributes.keypad_size.numb_of_keys + if not all(0 <= key_idx < keypad_size for key_idx in selected_keys): + raise ValueError(f"Invalid key indices. Must be between 0 and {keypad_size - 1}") + passcode_len = len(selected_keys) user = self.users[username] diff --git a/src/customer_attributes.py b/src/customer_attributes.py index 768af45..10929ce 100644 --- a/src/customer_attributes.py +++ b/src/customer_attributes.py @@ -1,5 +1,5 @@ +from typing import ClassVar from pydantic import BaseModel, model_validator - from src.models import KeypadSize from src.utils import generate_random_nonrepeating_list @@ -8,20 +8,21 @@ class CustomerAttributes(BaseModel): attr_vals: list[int] set_vals: list[int] keypad_size: KeypadSize + MAX_KEYS: ClassVar[int] = 256 + MAX_ATTRS_PER_KEY: ClassVar[int] = 256 @model_validator(mode='after') - def check_keys_vs_attrs(self): + def check_keys_vs_attrs(self) -> 'CustomerAttributes': if self.keypad_size.is_dispersable: raise ValueError("number of keys must be less than the number of " "attributes per key to be dispersion resistant") return self - @staticmethod - def new(keypad_size: KeypadSize): - assert (keypad_size.numb_of_keys <= 256) - assert (keypad_size.attrs_per_key <= 256) - - return CustomerAttributes( + @classmethod + def create(cls, keypad_size: KeypadSize) -> 'CustomerAttributes': + if keypad_size.numb_of_keys > cls.MAX_KEYS or keypad_size.attrs_per_key > cls.MAX_ATTRS_PER_KEY: + raise ValueError(f"Keys and attributes per key must not exceed {cls.MAX_KEYS}") + return cls( attr_vals=generate_random_nonrepeating_list(keypad_size.numb_of_attrs), set_vals=generate_random_nonrepeating_list(keypad_size.attrs_per_key), keypad_size=keypad_size, @@ -38,5 +39,6 @@ class CustomerAttributes(BaseModel): return self.set_vals[set_idx] def get_set_index(self, set_val: int) -> int: - assert (set_val in self.set_vals) + if set_val not in self.set_vals: + raise ValueError(f"Set value {set_val} not found in set values") return self.set_vals.index(set_val) diff --git a/src/nkode_api.py b/src/nkode_api.py index 5d9cd4e..d425ab0 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -16,18 +16,18 @@ class NKodeAPI(BaseModel): def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( customer_id=uuid4(), - attributes=CustomerAttributes.new(keypad_size), + attributes=CustomerAttributes.create(keypad_size), users={}, nkode_policy=nkode_policy ) self.customers[new_customer.customer_id] = new_customer - return new_customer.customer_id def generate_signup_interface(self, customer_id: UUID) -> tuple[UUID, list[int]]: - assert (customer_id in self.customers.keys()) + if customer_id not in self.customers.keys(): + raise ValueError(f"Customer with ID '{customer_id}' does not exist") customer = self.customers[customer_id] - login_interface = UserInterface.new(customer.attributes.keypad_size) + login_interface = UserInterface.create(customer.attributes.keypad_size) set_interface = login_interface.sign_up_interface() new_session = UserSignupSession( session_id=uuid4(), @@ -46,10 +46,13 @@ class NKodeAPI(BaseModel): key_selection: list[int], session_id: UUID ) -> list[int]: - assert (customer_id in self.customers.keys()) + if customer_id not in self.customers.keys(): + raise ValueError(f"Customer ID {customer_id} not found") customer = self.customers[customer_id] - assert (username not in customer.users.keys()) - assert (session_id in self.signup_sessions.keys()) + 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) return self.signup_sessions[session_id].confirm_interface @@ -60,20 +63,20 @@ class NKodeAPI(BaseModel): confirm_key_entry: list[int], session_id: UUID ) -> bool: - assert ( - session_id in self.signup_sessions.keys() and - customer_id == self.signup_sessions[session_id].customer_id and - username == self.signup_sessions[session_id].username - ) + if session_id not in self.signup_sessions.keys(): + raise AssertionError(f"Session ID {session_id} not found in signup sessions") + 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 = UserCipherKeys.new( + new_user_keys = UserCipherKeys.create( customer.attributes.keypad_size, customer.attributes.set_vals, customer.nkode_policy.max_nkode_len ) - enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.attributes) new_user = User( username=username, @@ -86,21 +89,22 @@ class NKodeAPI(BaseModel): return True def get_login_interface(self, username: str, customer_id: UUID) -> list[int]: - """ - TODO: how do we prevent a targeted denial-of-service attack? - """ - assert (customer_id in self.customers.keys()) + if customer_id not in self.customers.keys(): + raise ValueError("Customer ID not found") customer = self.customers[customer_id] - assert (username in customer.users.keys()) + if username not in customer.users.keys(): + raise ValueError("Username not found") user = customer.users[username] user.user_interface.partial_interface_shuffle() return user.user_interface.interface def login(self, customer_id: UUID, username: str, key_selection: list[int]) -> bool: - assert (customer_id in self.customers.keys()) + if customer_id not in self.customers.keys(): + raise ValueError("Customer ID not found") customer = self.customers[customer_id] return customer.valid_key_entry(username, key_selection) def renew_attributes(self, customer_id: UUID) -> bool: - assert (customer_id in self.customers.keys()) - return self.customers[customer_id].renew_keys() + if customer_id not in self.customers.keys(): + raise ValueError("Customer ID not found") + return self.customers[customer_id].renew_keys() \ No newline at end of file diff --git a/src/user.py b/src/user.py index e9c7b75..9e7a864 100644 --- a/src/user.py +++ b/src/user.py @@ -20,7 +20,7 @@ class User(BaseModel): self.user_keys.alpha_key = xor_lists(self.user_keys.alpha_key, attrs_xor) def refresh_passcode(self, passcode_attr_idx: list[int], customer_interface: CustomerAttributes): - self.user_keys = UserCipherKeys.new( + self.user_keys = UserCipherKeys.create( customer_interface.keypad_size, customer_interface.set_vals, self.user_keys.max_nkode_len diff --git a/src/user_cipher_keys.py b/src/user_cipher_keys.py index 391bffc..8f1bbe8 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher_keys.py @@ -17,9 +17,10 @@ class UserCipherKeys(BaseModel): salt: bytes max_nkode_len: int - @staticmethod - def new(keypad_size: KeypadSize, set_values: list[int], max_nkode_len: int): - assert len(set_values) == keypad_size.attrs_per_key + @classmethod + def create(cls, keypad_size: KeypadSize, set_values: list[int], max_nkode_len: int) -> 'UserCipherKeys': + if len(set_values) != keypad_size.attrs_per_key: + raise ValueError("Invalid set values") set_key = generate_random_nonrepeating_list(keypad_size.attrs_per_key) set_key = xor_lists(set_key, set_values) @@ -34,7 +35,8 @@ class UserCipherKeys(BaseModel): ) def pad_user_mask(self, user_mask: list[int], set_vals: list[int]) -> list[int]: - assert (len(user_mask) <= self.max_nkode_len) + if len(user_mask) >= self.max_nkode_len: + raise ValueError("User mask is too long") padded_user_mask = user_mask.copy() for _ in range(self.max_nkode_len - len(user_mask)): padded_user_mask.append(choice(set_vals)) diff --git a/src/user_interface.py b/src/user_interface.py index 673272e..bb23e39 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -9,9 +9,8 @@ class UserInterface(BaseModel): interface: list[int] keypad_size: KeypadSize - @staticmethod - def new(keypad_size: KeypadSize): - # Todo: this a hack do a proper random interface + @classmethod + def create(cls, keypad_size: KeypadSize) -> 'UserInterface': interface = UserInterface( interface=list(range(keypad_size.numb_of_attrs)), keypad_size=keypad_size @@ -20,7 +19,8 @@ class UserInterface(BaseModel): return interface def sign_up_interface(self): - assert (not self.keypad_size.is_dispersable) + if self.keypad_size.is_dispersable: + raise ValueError("Keypad size is dispersable") self.random_interface_shuffle() interface_matrix = self.interface_keypad_matrix() attr_set_view = matrix_transpose(interface_matrix) @@ -47,7 +47,8 @@ class UserInterface(BaseModel): self.interface = matrix_to_list(keypad_view) def disperse_interface(self): - assert (self.keypad_size.is_dispersable) + if not self.keypad_size.is_dispersable: + raise ValueError("Keypad size is not dispersable") user_interface_matrix = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) @@ -60,6 +61,7 @@ class UserInterface(BaseModel): self.interface = matrix_to_list(dispersed_interface) def partial_interface_shuffle(self): + # TODO: this should be split shuffle numb_of_selected_sets = self.keypad_size.attrs_per_key // 2 # randomly shuffle half the sets. if attrs_per_key is odd, randomly add one 50% of the time numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.attrs_per_key & 1) == 1 else 0 @@ -80,7 +82,8 @@ class UserInterface(BaseModel): attr_rotation: list[int] ) -> list[list[int]]: transposed_user_interface = matrix_transpose(user_interface) - assert (len(attr_rotation) == len(transposed_user_interface)) + if len(attr_rotation) != len(transposed_user_interface): + raise ValueError("attr_rotation must be the same length as the transposed user interface") for idx, attr_set in enumerate(transposed_user_interface): rotation = attr_rotation[idx] transposed_user_interface[idx] = attr_set[rotation:] + attr_set[:rotation] @@ -93,12 +96,12 @@ class UserInterface(BaseModel): for attr in key: graph[attr] = set(key) graph[attr].remove(attr) - return graph def get_attr_idx_by_keynumb_setidx(self, key_numb: int, set_idx: int) -> int: - assert (0 <= key_numb < self.keypad_size.numb_of_keys) - assert (0 <= set_idx < self.keypad_size.attrs_per_key) + if not (0 <= key_numb < self.keypad_size.numb_of_keys): + raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") + if not (0 <= set_idx < self.keypad_size.attrs_per_key): + raise ValueError(f"set_idx must be between 0 and {self.keypad_size.attrs_per_key - 1}") keypad_attr_idx = self.interface_keypad_matrix() - return keypad_attr_idx[key_numb][set_idx] diff --git a/src/user_signup_session.py b/src/user_signup_session.py index 2bedf59..539f5fd 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -10,7 +10,6 @@ class UserSignupSession(BaseModel): session_id: UUID customer_id: UUID login_interface: UserInterface - keypad_size: KeypadSize set_interface: list[int] | None = None confirm_interface: list[int] | None = None @@ -18,28 +17,30 @@ class UserSignupSession(BaseModel): username: str | None = None def deduce_passcode(self, confirm_key_entry: list[int]) -> list[int]: - assert (all(0 <= key <= self.keypad_size.numb_of_keys for key in confirm_key_entry)) + if not all(0 <= key <= self.keypad_size.numb_of_keys for key in confirm_key_entry): + raise ValueError("Key values must be within valid range") attrs_per_key = self.keypad_size.attrs_per_key - set_key_entry = self.set_key_entry - assert (len(set_key_entry) == len(confirm_key_entry)) + if len(set_key_entry) != len(confirm_key_entry): + raise ValueError("Key entry lengths must match") set_interface = self.set_interface confirm_interface = self.confirm_interface set_key_vals = [set_interface[key * attrs_per_key:(key + 1) * attrs_per_key] for key in set_key_entry] confirm_key_vals = [confirm_interface[key * attrs_per_key:(key + 1) * attrs_per_key] for key in confirm_key_entry] - passcode = [] for idx in range(len(set_key_entry)): set_key = set(set_key_vals[idx]) confirm_key = set(confirm_key_vals[idx]) intersection = list(set_key.intersection(confirm_key)) - assert (len(intersection) == 1) + if len(intersection) != 1: + raise ValueError("Intersection must contain exactly one element") passcode.append(intersection[0]) return passcode def set_user_nkode(self, username: str, key_selection: list[int]): - assert (all(0 <= key <= self.keypad_size.numb_of_keys for key in key_selection)) + if not all(0 <= key <= self.keypad_size.numb_of_keys for key in key_selection): + raise ValueError("Key values must be within valid range") set_interface = UserInterface( interface=self.set_interface, keypad_size=self.keypad_size diff --git a/src/utils.py b/src/utils.py index d2c6692..8a28da2 100644 --- a/src/utils.py +++ b/src/utils.py @@ -10,12 +10,14 @@ def secure_fisher_yates_shuffle(arr: list) -> list: def generate_random_nonrepeating_list(list_len: int, min_val: int = 0, max_val: int = 2 ** 16) -> list[int]: - assert (max_val - min_val >= list_len) + if max_val - min_val < list_len: + raise ValueError("Range of values is less than the list length requested") return secure_fisher_yates_shuffle(list(range(min_val, max_val)))[:list_len] def xor_lists(l1: list[int], l2: list[int]): - assert len(l1) == len(l2) + if len(l1) != len(l2): + raise ValueError("Lists must be of equal length") return [l2[i] ^ l1[i] for i in range(len(l1))] diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index 05f048d..da3d4d4 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -8,7 +8,7 @@ from src.models import KeypadSize [KeypadSize(numb_of_keys=10, attrs_per_key=11)] ) def test_attr_set_idx(keypad_size): - user_interface = UserInterface.new(keypad_size) + user_interface = UserInterface.create(keypad_size) for attr_idx in range(keypad_size.numb_of_attrs): user_interface_idx = user_interface.interface[attr_idx] diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 528551d..1b7e11f 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -27,13 +27,13 @@ def test_encode_decode_base64(passcode_len): (KeypadSize(numb_of_keys=8, attrs_per_key=11), 12), ]) def test_decode_mask(keypad_size, max_nkode_len): - customer = CustomerAttributes.new(keypad_size) + customer = CustomerAttributes.create(keypad_size) passcode_entry = generate_random_nonrepeating_list( keypad_size.numb_of_attrs, max_val=keypad_size.numb_of_attrs)[:4] passcode_values = [customer.attr_vals[idx] for idx in passcode_entry] set_vals = customer.set_vals - user_keys = UserCipherKeys.new(keypad_size, set_vals, max_nkode_len) + user_keys = UserCipherKeys.create(keypad_size, set_vals, max_nkode_len) passcode = user_keys.encipher_nkode(passcode_entry, customer) orig_passcode_set_vals = [customer.get_attr_set_val(attr) for attr in passcode_values] diff --git a/test/test_user_interface.py b/test/test_user_interface.py index d3fd2f3..563d4ec 100644 --- a/test/test_user_interface.py +++ b/test/test_user_interface.py @@ -5,7 +5,7 @@ from src.models import KeypadSize @pytest.fixture() def user_interface(): - return UserInterface.new(keypad_size=KeypadSize(attrs_per_key=7, numb_of_keys=10)) + return UserInterface.create(keypad_size=KeypadSize(attrs_per_key=7, numb_of_keys=10)) def test_dispersion(user_interface): -- 2.49.1 From 75a5e470d3fc1591ab3b067762294d014bf97348 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:14:48 -0500 Subject: [PATCH 05/85] remove pydantic from user_interface.py --- src/user_interface.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/user_interface.py b/src/user_interface.py index bb23e39..9ea72d5 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -1,11 +1,10 @@ -from pydantic import BaseModel +from dataclasses import dataclass from secrets import choice - from src.models import KeypadSize from src.utils import list_to_matrix, secure_fisher_yates_shuffle, matrix_to_list, matrix_transpose - -class UserInterface(BaseModel): +@dataclass +class UserInterface: interface: list[int] keypad_size: KeypadSize -- 2.49.1 From c365fd7a7f7991165b4b1b60b5621ced674adb78 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:15:39 -0500 Subject: [PATCH 06/85] remove pydantic from customer.py --- src/customer.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/customer.py b/src/customer.py index 8b25694..4fc310b 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,12 +1,12 @@ +from dataclasses import dataclass from uuid import UUID -from pydantic import BaseModel from src.customer_attributes import CustomerAttributes from src.models import NKodePolicy from src.user import User from src.utils import xor_lists - -class Customer(BaseModel): +@dataclass +class Customer: customer_id: UUID nkode_policy: NKodePolicy attributes: CustomerAttributes -- 2.49.1 From 237d294b25ab0ce144a9e57437b6abd526aa2b8c Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:18:15 -0500 Subject: [PATCH 07/85] remove pydantic from customer_attributes.py --- src/customer_attributes.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/customer_attributes.py b/src/customer_attributes.py index 10929ce..fff9121 100644 --- a/src/customer_attributes.py +++ b/src/customer_attributes.py @@ -1,22 +1,25 @@ +from dataclasses import dataclass, field from typing import ClassVar -from pydantic import BaseModel, model_validator + from src.models import KeypadSize from src.utils import generate_random_nonrepeating_list -class CustomerAttributes(BaseModel): +@dataclass +class CustomerAttributes: attr_vals: list[int] set_vals: list[int] keypad_size: KeypadSize MAX_KEYS: ClassVar[int] = 256 MAX_ATTRS_PER_KEY: ClassVar[int] = 256 - @model_validator(mode='after') - def check_keys_vs_attrs(self) -> 'CustomerAttributes': + def __post_init__(self): + self.check_keys_vs_attrs() + + def check_keys_vs_attrs(self) -> None: if self.keypad_size.is_dispersable: raise ValueError("number of keys must be less than the number of " "attributes per key to be dispersion resistant") - return self @classmethod def create(cls, keypad_size: KeypadSize) -> 'CustomerAttributes': @@ -41,4 +44,4 @@ class CustomerAttributes(BaseModel): def get_set_index(self, set_val: int) -> int: if set_val not in self.set_vals: raise ValueError(f"Set value {set_val} not found in set values") - return self.set_vals.index(set_val) + return self.set_vals.index(set_val) \ No newline at end of file -- 2.49.1 From 94cab10aff754cce27f061cc78042be643f17c69 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:20:19 -0500 Subject: [PATCH 08/85] remove pydantic from models.py --- src/customer_attributes.py | 2 +- src/models.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/customer_attributes.py b/src/customer_attributes.py index fff9121..6680cbe 100644 --- a/src/customer_attributes.py +++ b/src/customer_attributes.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass, field +from dataclasses import dataclass from typing import ClassVar from src.models import KeypadSize diff --git a/src/models.py b/src/models.py index 475cb0b..9178196 100644 --- a/src/models.py +++ b/src/models.py @@ -1,22 +1,24 @@ -from pydantic import BaseModel +from dataclasses import dataclass - -class EncipheredNKode(BaseModel): +@dataclass +class EncipheredNKode: code: str mask: str -class NKodePolicy(BaseModel): +@dataclass +class NKodePolicy: max_nkode_len: int = 10 min_nkode_len: int = 4 distinct_sets: int = 0 distinct_attributes: int = 4 byte_len: int = 2 # Todo: this should change the total number of bytes an attribute or set value can be lock_out: int = 5 - expiration: int = -1 # in seconds -1 means never expires + expiration: int = -1 # in seconds -1 means nkode never expires -class KeypadSize(BaseModel): +@dataclass +class KeypadSize: attrs_per_key: int numb_of_keys: int @@ -26,4 +28,4 @@ class KeypadSize(BaseModel): @property def is_dispersable(self) -> bool: - return self.attrs_per_key <= self.numb_of_keys + return self.attrs_per_key <= self.numb_of_keys \ No newline at end of file -- 2.49.1 From 75787b7142426ced9399533957c6dd28a7422b7e Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:22:32 -0500 Subject: [PATCH 09/85] remove pydantic from nkode_api.py --- src/nkode_api.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/nkode_api.py b/src/nkode_api.py index d425ab0..3ddfdce 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -1,5 +1,7 @@ +from dataclasses import dataclass, field from uuid import UUID, uuid4 -from pydantic import BaseModel +from typing import Dict, List, Tuple + from src.customer import Customer from src.models import NKodePolicy, KeypadSize from src.user import User @@ -9,9 +11,10 @@ from src.user_interface import UserInterface from src.customer_attributes import CustomerAttributes -class NKodeAPI(BaseModel): - customers: dict[UUID, Customer] = {} - signup_sessions: dict[UUID, UserSignupSession] = {} +@dataclass +class NKodeAPI: + customers: Dict[UUID, Customer] = field(default_factory=dict) + signup_sessions: Dict[UUID, UserSignupSession] = field(default_factory=dict) def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( @@ -23,7 +26,7 @@ class NKodeAPI(BaseModel): self.customers[new_customer.customer_id] = new_customer return new_customer.customer_id - def generate_signup_interface(self, customer_id: UUID) -> tuple[UUID, list[int]]: + def generate_signup_interface(self, customer_id: UUID) -> Tuple[UUID, List[int]]: if customer_id not in self.customers.keys(): raise ValueError(f"Customer with ID '{customer_id}' does not exist") customer = self.customers[customer_id] @@ -43,9 +46,9 @@ class NKodeAPI(BaseModel): self, username: str, customer_id: UUID, - key_selection: list[int], + key_selection: List[int], session_id: UUID - ) -> list[int]: + ) -> List[int]: if customer_id not in self.customers.keys(): raise ValueError(f"Customer ID {customer_id} not found") customer = self.customers[customer_id] @@ -60,7 +63,7 @@ class NKodeAPI(BaseModel): self, username: str, customer_id: UUID, - confirm_key_entry: list[int], + confirm_key_entry: List[int], session_id: UUID ) -> bool: if session_id not in self.signup_sessions.keys(): @@ -88,7 +91,7 @@ class NKodeAPI(BaseModel): del self.signup_sessions[session_id] return True - def get_login_interface(self, username: str, customer_id: UUID) -> list[int]: + def get_login_interface(self, username: str, customer_id: UUID) -> List[int]: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") customer = self.customers[customer_id] @@ -98,7 +101,7 @@ class NKodeAPI(BaseModel): user.user_interface.partial_interface_shuffle() return user.user_interface.interface - def login(self, customer_id: UUID, username: str, key_selection: list[int]) -> bool: + def login(self, customer_id: UUID, username: str, key_selection: List[int]) -> bool: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") customer = self.customers[customer_id] @@ -107,4 +110,4 @@ class NKodeAPI(BaseModel): def renew_attributes(self, customer_id: UUID) -> bool: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") - return self.customers[customer_id].renew_keys() \ No newline at end of file + return self.customers[customer_id].renew_keys() -- 2.49.1 From 0d2a5240477ce4896d1332517bc72132284d2da1 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:24:50 -0500 Subject: [PATCH 10/85] remove pydantic from user.py --- src/user.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/user.py b/src/user.py index 9e7a864..6ce67c7 100644 --- a/src/user.py +++ b/src/user.py @@ -1,5 +1,4 @@ -from pydantic import BaseModel - +from dataclasses import dataclass, field from src.models import EncipheredNKode from src.customer_attributes import CustomerAttributes from src.user_cipher_keys import UserCipherKeys @@ -7,12 +6,13 @@ from src.user_interface import UserInterface from src.utils import xor_lists -class User(BaseModel): +@dataclass +class User: username: str enciphered_passcode: EncipheredNKode user_keys: UserCipherKeys user_interface: UserInterface - renew: bool = False + renew: bool = field(default=False) def renew_keys(self, sets_xor: list[int], attrs_xor: list[int]): self.renew = True -- 2.49.1 From e55e18abf8d5af539a3aebb0982c3d60c131b181 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:25:39 -0500 Subject: [PATCH 11/85] remove pydantic from user_cipher_keys.py --- src/user_cipher_keys.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/user_cipher_keys.py b/src/user_cipher_keys.py index 8f1bbe8..700c99b 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher_keys.py @@ -1,15 +1,14 @@ import base64 import hashlib +from dataclasses import dataclass import bcrypt from secrets import choice -from pydantic import BaseModel - from src.models import EncipheredNKode, KeypadSize from src.customer_attributes import CustomerAttributes from src.utils import generate_random_nonrepeating_list, xor_lists, int_array_to_bytes - -class UserCipherKeys(BaseModel): +@dataclass +class UserCipherKeys: alpha_key: list[int] set_key: list[int] pass_key: list[int] -- 2.49.1 From c1ca01eb933a83f2ea2b631b64dd9fc995ac797d Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 9 Mar 2025 09:37:35 -0500 Subject: [PATCH 12/85] refactor; remove the term interface --- notebooks/dispersion_tutorial.ipynb | 14 ++-- src/customer.py | 2 +- src/nkode_api.py | 26 +++---- src/user.py | 12 ++-- src/user_cipher_keys.py | 20 +++--- src/{user_interface.py => user_keypad.py} | 82 +++++++++++------------ src/user_signup_session.py | 24 +++---- src/utils.py | 4 +- test/test_nkode_api.py | 26 +++---- test/test_nkode_interface.py | 8 +-- test/test_user_interface.py | 35 ---------- test/test_user_keypad.py | 35 ++++++++++ 12 files changed, 144 insertions(+), 144 deletions(-) rename src/{user_interface.py => user_keypad.py} (55%) delete mode 100644 test/test_user_interface.py create mode 100644 test/test_user_keypad.py diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index 6109776..97ca9d8 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -24,7 +24,7 @@ ], "source": [ "from src.utils import secure_fisher_yates_shuffle, matrix_to_list, list_to_matrix\n", - "from src.user_interface import UserInterface\n", + "from src.user_interface import UserKeypad\n", "from IPython.display import Markdown, display\n", "from src.models import KeypadSize\n", "\n", @@ -43,13 +43,13 @@ "\n", "keypad_size = KeypadSize(numb_of_keys=5, attrs_per_key=4)\n", "attrs = [1, 10, 11, 100]\n", - "interface = []\n", + "keypad = []\n", "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", - " interface.extend([key_numb*attr for attr in attrs])\n", + " keypad.extend([key_numb * attr for attr in attrs])\n", "\n", - "demo_interface = UserInterface(keypad_size=keypad_size, interface=interface)\n", + "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=keypad)\n", "\n", - "display(Markdown(keypad_md_table(demo_interface.interface, keypad_size)))\n" + "display(Markdown(keypad_md_table(demo_interface.keypad, keypad_size)))\n" ], "metadata": { "collapsed": false, @@ -73,7 +73,7 @@ } ], "source": [ - "demo_interface_matrix = list_to_matrix(demo_interface.interface, demo_interface.keypad_size.attrs_per_key)\n", + "demo_interface_matrix = list_to_matrix(demo_interface.keypad, demo_interface.keypad_size.attrs_per_key)\n", "shuffled_keys = secure_fisher_yates_shuffle(demo_interface_matrix)\n", "shuffled_keys_list = matrix_to_list(shuffled_keys)\n", "display(Markdown(keypad_md_table(shuffled_keys_list, keypad_size)))\n" @@ -101,7 +101,7 @@ ], "source": [ "attr_rotation = secure_fisher_yates_shuffle(list(range(keypad_size.numb_of_keys)))[:keypad_size.attrs_per_key]\n", - "dispersed_interface = UserInterface.random_attribute_rotation(\n", + "dispersed_interface = UserKeypad.random_attribute_rotation(\n", " shuffled_keys,\n", " attr_rotation\n", ")\n", diff --git a/src/customer.py b/src/customer.py index 4fc310b..ee14c07 100644 --- a/src/customer.py +++ b/src/customer.py @@ -38,7 +38,7 @@ class Customer: for idx in range(passcode_len): key_numb = selected_keys[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = user.user_interface.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) + selected_attr_idx = user.user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) presumed_selected_attributes_idx.append(selected_attr_idx) enciphered_attr = user.user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, self.attributes) diff --git a/src/nkode_api.py b/src/nkode_api.py index 3ddfdce..a02e9ff 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -7,7 +7,7 @@ from src.models import NKodePolicy, KeypadSize from src.user import User from src.user_cipher_keys import UserCipherKeys from src.user_signup_session import UserSignupSession -from src.user_interface import UserInterface +from src.user_keypad import UserKeypad from src.customer_attributes import CustomerAttributes @@ -26,21 +26,21 @@ class NKodeAPI: self.customers[new_customer.customer_id] = new_customer return new_customer.customer_id - def generate_signup_interface(self, customer_id: UUID) -> Tuple[UUID, List[int]]: + def generate_signup_keypad(self, customer_id: UUID) -> Tuple[UUID, List[int]]: if customer_id not in self.customers.keys(): raise ValueError(f"Customer with ID '{customer_id}' does not exist") customer = self.customers[customer_id] - login_interface = UserInterface.create(customer.attributes.keypad_size) - set_interface = login_interface.sign_up_interface() + login_keypad = UserKeypad.create(customer.attributes.keypad_size) + set_keypad = login_keypad.sign_up_keypad() new_session = UserSignupSession( session_id=uuid4(), - login_interface=login_interface, - set_interface=set_interface.interface, + login_keypad=login_keypad, + set_keypad=set_keypad.keypad, customer_id=customer_id, - keypad_size=set_interface.keypad_size, + keypad_size=set_keypad.keypad_size, ) self.signup_sessions[new_session.session_id] = new_session - return new_session.session_id, new_session.set_interface + return new_session.session_id, new_session.set_keypad def set_nkode( self, @@ -57,7 +57,7 @@ class NKodeAPI: 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) - return self.signup_sessions[session_id].confirm_interface + return self.signup_sessions[session_id].confirm_keypad def confirm_nkode( self, @@ -85,21 +85,21 @@ class NKodeAPI: username=username, enciphered_passcode=enciphered_passcode, user_keys=new_user_keys, - user_interface=self.signup_sessions[session_id].login_interface, + user_keypad=self.signup_sessions[session_id].login_keypad, ) self.customers[customer_id].add_new_user(new_user) del self.signup_sessions[session_id] return True - def get_login_interface(self, username: str, customer_id: UUID) -> List[int]: + def get_login_keypad(self, username: str, customer_id: UUID) -> List[int]: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") customer = self.customers[customer_id] if username not in customer.users.keys(): raise ValueError("Username not found") user = customer.users[username] - user.user_interface.partial_interface_shuffle() - return user.user_interface.interface + user.user_keypad.partial_keypad_shuffle() + return user.user_keypad.keypad def login(self, customer_id: UUID, username: str, key_selection: List[int]) -> bool: if customer_id not in self.customers.keys(): diff --git a/src/user.py b/src/user.py index 6ce67c7..dad959c 100644 --- a/src/user.py +++ b/src/user.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from src.models import EncipheredNKode from src.customer_attributes import CustomerAttributes from src.user_cipher_keys import UserCipherKeys -from src.user_interface import UserInterface +from src.user_keypad import UserKeypad from src.utils import xor_lists @@ -11,7 +11,7 @@ class User: username: str enciphered_passcode: EncipheredNKode user_keys: UserCipherKeys - user_interface: UserInterface + user_keypad: UserKeypad renew: bool = field(default=False) def renew_keys(self, sets_xor: list[int], attrs_xor: list[int]): @@ -19,11 +19,11 @@ class User: self.user_keys.set_key = xor_lists(self.user_keys.set_key, sets_xor) self.user_keys.alpha_key = xor_lists(self.user_keys.alpha_key, attrs_xor) - def refresh_passcode(self, passcode_attr_idx: list[int], customer_interface: CustomerAttributes): + def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerAttributes): self.user_keys = UserCipherKeys.create( - customer_interface.keypad_size, - customer_interface.set_vals, + customer_attributes.keypad_size, + customer_attributes.set_vals, self.user_keys.max_nkode_len ) - self.enciphered_passcode = self.user_keys.encipher_nkode(passcode_attr_idx, customer_interface) + self.enciphered_passcode = self.user_keys.encipher_nkode(passcode_attr_idx, customer_attributes) self.renew = False diff --git a/src/user_cipher_keys.py b/src/user_cipher_keys.py index 700c99b..a4a4d3f 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher_keys.py @@ -64,13 +64,13 @@ class UserCipherKeys: def encipher_nkode( self, passcode_attr_idx: list[int], - customer_interface: CustomerAttributes + customer_attributes: CustomerAttributes ) -> EncipheredNKode: - passcode_attrs = [customer_interface.attr_vals[idx] for idx in passcode_attr_idx] - passcode_sets = [customer_interface.get_attr_set_val(attr) for attr in passcode_attrs] - mask = self.encipher_mask(passcode_sets, customer_interface) - code = self.encipher_salt_hash_code(passcode_attr_idx, customer_interface) + passcode_attrs = [customer_attributes.attr_vals[idx] for idx in passcode_attr_idx] + passcode_sets = [customer_attributes.get_attr_set_val(attr) for attr in passcode_attrs] + mask = self.encipher_mask(passcode_sets, customer_attributes) + code = self.encipher_salt_hash_code(passcode_attr_idx, customer_attributes) return EncipheredNKode( code=code, mask=mask @@ -79,10 +79,10 @@ class UserCipherKeys: def encipher_salt_hash_code( self, passcode_attr_idx: list[int], - customer_interface: CustomerAttributes, + customer_attributes: CustomerAttributes, ) -> str: passcode_len = len(passcode_attr_idx) - passcode_attrs = [customer_interface.attr_vals[idx] for idx in passcode_attr_idx] + passcode_attrs = [customer_attributes.attr_vals[idx] for idx in passcode_attr_idx] passcode_cipher = self.pass_key.copy() @@ -96,10 +96,10 @@ class UserCipherKeys: def encipher_mask( self, passcode_sets: list[int], - customer_interface: CustomerAttributes + customer_attributes: CustomerAttributes ) -> str: - padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_interface.set_vals) - set_idx = [customer_interface.get_set_index(set_val) for set_val in padded_passcode_sets] + padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_attributes.set_vals) + set_idx = [customer_attributes.get_set_index(set_val) for set_val in padded_passcode_sets] mask_set_keys = [self.set_key[idx] for idx in set_idx] ciphered_mask = xor_lists(mask_set_keys, padded_passcode_sets) ciphered_mask = xor_lists(ciphered_mask, self.mask_key) diff --git a/src/user_interface.py b/src/user_keypad.py similarity index 55% rename from src/user_interface.py rename to src/user_keypad.py index 9ea72d5..4efe5c6 100644 --- a/src/user_interface.py +++ b/src/user_keypad.py @@ -4,94 +4,94 @@ from src.models import KeypadSize from src.utils import list_to_matrix, secure_fisher_yates_shuffle, matrix_to_list, matrix_transpose @dataclass -class UserInterface: - interface: list[int] +class UserKeypad: + keypad: list[int] keypad_size: KeypadSize @classmethod - def create(cls, keypad_size: KeypadSize) -> 'UserInterface': - interface = UserInterface( - interface=list(range(keypad_size.numb_of_attrs)), + def create(cls, keypad_size: KeypadSize) -> 'UserKeypad': + keypad = UserKeypad( + keypad=list(range(keypad_size.numb_of_attrs)), keypad_size=keypad_size ) - interface.random_interface_shuffle() - return interface + keypad.random_keypad_shuffle() + return keypad - def sign_up_interface(self): + def sign_up_keypad(self): if self.keypad_size.is_dispersable: raise ValueError("Keypad size is dispersable") - self.random_interface_shuffle() - interface_matrix = self.interface_keypad_matrix() - attr_set_view = matrix_transpose(interface_matrix) + self.random_keypad_shuffle() + keypad_matrix = self.keypad_matrix() + attr_set_view = matrix_transpose(keypad_matrix) attr_set_view = secure_fisher_yates_shuffle(attr_set_view) attr_set_view = attr_set_view[:self.keypad_size.numb_of_keys] - interface_matrix = matrix_transpose(attr_set_view) - return UserInterface( - interface=matrix_to_list(interface_matrix), + keypad_matrix = matrix_transpose(attr_set_view) + return UserKeypad( + keypad=matrix_to_list(keypad_matrix), keypad_size=KeypadSize( numb_of_keys=self.keypad_size.numb_of_keys, attrs_per_key=self.keypad_size.numb_of_keys ) ) - def interface_keypad_matrix(self) -> list[list[int]]: - return list_to_matrix(self.interface, self.keypad_size.attrs_per_key) + def keypad_matrix(self) -> list[list[int]]: + return list_to_matrix(self.keypad, self.keypad_size.attrs_per_key) - def random_interface_shuffle(self): - keypad_view = self.interface_keypad_matrix() + def random_keypad_shuffle(self): + keypad_view = self.keypad_matrix() keypad_view = secure_fisher_yates_shuffle(keypad_view) set_view = matrix_transpose(keypad_view) set_view = [secure_fisher_yates_shuffle(attr_set) for attr_set in set_view] keypad_view = matrix_transpose(set_view) - self.interface = matrix_to_list(keypad_view) + self.keypad = matrix_to_list(keypad_view) - def disperse_interface(self): + def disperse_keypad(self): if not self.keypad_size.is_dispersable: raise ValueError("Keypad size is not dispersable") - user_interface_matrix = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) - shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) + user_keypad_matrix = list_to_matrix(self.keypad, self.keypad_size.attrs_per_key) + shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[ :self.keypad_size.attrs_per_key] - dispersed_interface = self.random_attribute_rotation( + dispersed_keypad = self.random_attribute_rotation( shuffled_keys, attr_rotation, ) - self.interface = matrix_to_list(dispersed_interface) + self.keypad = matrix_to_list(dispersed_keypad) - def partial_interface_shuffle(self): + def partial_keypad_shuffle(self): # TODO: this should be split shuffle numb_of_selected_sets = self.keypad_size.attrs_per_key // 2 # randomly shuffle half the sets. if attrs_per_key is odd, randomly add one 50% of the time numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.attrs_per_key & 1) == 1 else 0 selected_sets = secure_fisher_yates_shuffle(list(range(self.keypad_size.attrs_per_key)))[:numb_of_selected_sets] - user_interface_matrix = self.interface_keypad_matrix() - shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) - interface_by_sets = [] + user_keypad_matrix = self.keypad_matrix() + shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) + keypad_by_sets = [] for idx, attrs in enumerate(matrix_transpose(shuffled_keys)): if idx in selected_sets: - interface_by_sets.append(secure_fisher_yates_shuffle(attrs)) + keypad_by_sets.append(secure_fisher_yates_shuffle(attrs)) else: - interface_by_sets.append(attrs) - self.interface = matrix_to_list(matrix_transpose(interface_by_sets)) + keypad_by_sets.append(attrs) + self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) @staticmethod def random_attribute_rotation( - user_interface: list[list[int]], + user_keypad: list[list[int]], attr_rotation: list[int] ) -> list[list[int]]: - transposed_user_interface = matrix_transpose(user_interface) - if len(attr_rotation) != len(transposed_user_interface): - raise ValueError("attr_rotation must be the same length as the transposed user interface") - for idx, attr_set in enumerate(transposed_user_interface): + transposed_user_keypad = matrix_transpose(user_keypad) + if len(attr_rotation) != len(transposed_user_keypad): + raise ValueError("attr_rotation must be the same length as the transposed user keypad") + for idx, attr_set in enumerate(transposed_user_keypad): rotation = attr_rotation[idx] - transposed_user_interface[idx] = attr_set[rotation:] + attr_set[:rotation] - return matrix_transpose(transposed_user_interface) + transposed_user_keypad[idx] = attr_set[rotation:] + attr_set[:rotation] + return matrix_transpose(transposed_user_keypad) def attribute_adjacency_graph(self) -> dict[int, set[int]]: - user_interface_keypad = self.interface_keypad_matrix() + user_keypad_keypad = self.keypad_matrix() graph = {} - for key in user_interface_keypad: + for key in user_keypad_keypad: for attr in key: graph[attr] = set(key) graph[attr].remove(attr) @@ -102,5 +102,5 @@ class UserInterface: raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not (0 <= set_idx < self.keypad_size.attrs_per_key): raise ValueError(f"set_idx must be between 0 and {self.keypad_size.attrs_per_key - 1}") - keypad_attr_idx = self.interface_keypad_matrix() + keypad_attr_idx = self.keypad_matrix() return keypad_attr_idx[key_numb][set_idx] diff --git a/src/user_signup_session.py b/src/user_signup_session.py index 539f5fd..b9d14ed 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -2,17 +2,17 @@ from uuid import UUID from pydantic import BaseModel -from src.user_interface import UserInterface +from src.user_keypad import UserKeypad from src.models import KeypadSize class UserSignupSession(BaseModel): session_id: UUID customer_id: UUID - login_interface: UserInterface + login_keypad: UserKeypad keypad_size: KeypadSize - set_interface: list[int] | None = None - confirm_interface: list[int] | None = None + set_keypad: list[int] | None = None + confirm_keypad: list[int] | None = None set_key_entry: list[int] | None = None username: str | None = None @@ -23,10 +23,10 @@ class UserSignupSession(BaseModel): set_key_entry = self.set_key_entry if len(set_key_entry) != len(confirm_key_entry): raise ValueError("Key entry lengths must match") - set_interface = self.set_interface - confirm_interface = self.confirm_interface - set_key_vals = [set_interface[key * attrs_per_key:(key + 1) * attrs_per_key] for key in set_key_entry] - confirm_key_vals = [confirm_interface[key * attrs_per_key:(key + 1) * attrs_per_key] for key in + set_keypad = self.set_keypad + confirm_keypad = self.confirm_keypad + set_key_vals = [set_keypad[key * attrs_per_key:(key + 1) * attrs_per_key] for key in set_key_entry] + confirm_key_vals = [confirm_keypad[key * attrs_per_key:(key + 1) * attrs_per_key] for key in confirm_key_entry] passcode = [] for idx in range(len(set_key_entry)): @@ -41,12 +41,12 @@ class UserSignupSession(BaseModel): def set_user_nkode(self, username: str, key_selection: list[int]): if not all(0 <= key <= self.keypad_size.numb_of_keys for key in key_selection): raise ValueError("Key values must be within valid range") - set_interface = UserInterface( - interface=self.set_interface, + set_keypad = UserKeypad( + keypad=self.set_keypad, keypad_size=self.keypad_size ) - set_interface.disperse_interface() + set_keypad.disperse_keypad() self.username = username self.set_key_entry = key_selection - self.confirm_interface = set_interface.interface + self.confirm_keypad = set_keypad.keypad diff --git a/src/utils.py b/src/utils.py index 8a28da2..3a099f2 100644 --- a/src/utils.py +++ b/src/utils.py @@ -29,8 +29,8 @@ def list_to_matrix(lst: list[int], cols: int) -> list[list[int]]: return [lst[i:i + cols] for i in range(0, len(lst), cols)] -def matrix_transpose(interface: list[list[int]]) -> list[list[int]]: - return [list(row) for row in zip(*interface)] +def matrix_transpose(mat: list[list[int]]) -> list[list[int]]: + return [list(row) for row in zip(*mat)] def int_array_to_bytes(int_arr: list[int], byte_size: int = 2) -> bytes: diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 186438c..13b2df3 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -16,14 +16,14 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): username = "test_username" nkode_policy = NKodePolicy() # default policy customer_id = nkode_api.create_new_customer(keypad_size, nkode_policy) - session_id, set_interface = nkode_api.generate_signup_interface(customer_id) - user_passcode = set_interface[:passocode_len] + session_id, set_keypad = nkode_api.generate_signup_keypad(customer_id) + user_passcode = set_keypad[:passocode_len] - signup_key_selection = lambda interface: [interface.index(attr) // keypad_size.numb_of_keys for attr in user_passcode] - set_key_selection = signup_key_selection(set_interface) + signup_key_selection = lambda keypad: [keypad.index(attr) // keypad_size.numb_of_keys for attr in user_passcode] + set_key_selection = signup_key_selection(set_keypad) - confirm_interface = nkode_api.set_nkode(username, customer_id, set_key_selection, session_id) - confirm_key_selection = signup_key_selection(confirm_interface) + confirm_keypad = nkode_api.set_nkode(username, customer_id, set_key_selection, session_id) + confirm_key_selection = signup_key_selection(confirm_keypad) successful_confirm = nkode_api.confirm_nkode( username, customer_id, @@ -32,21 +32,21 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): ) assert successful_confirm - sign_in_key_selection = lambda interface: [interface.index(attr) // keypad_size.attrs_per_key for attr in user_passcode] - login_interface = nkode_api.get_login_interface(username, customer_id) - login_key_selection = sign_in_key_selection(login_interface) + sign_in_key_selection = lambda keypad: [keypad.index(attr) // keypad_size.attrs_per_key for attr in user_passcode] + login_keypad = nkode_api.get_login_keypad(username, customer_id) + login_key_selection = sign_in_key_selection(login_keypad) successful_login = nkode_api.login(customer_id, username, login_key_selection) assert successful_login successful_renew = nkode_api.renew_attributes(customer_id) assert successful_renew - login_interface = nkode_api.get_login_interface(username, customer_id) - login_key_selection = sign_in_key_selection(login_interface) + login_keypad = nkode_api.get_login_keypad(username, customer_id) + login_key_selection = sign_in_key_selection(login_keypad) successful_login = nkode_api.login(customer_id, username, login_key_selection) assert successful_login - login_interface = nkode_api.get_login_interface(username, customer_id) - login_key_selection = sign_in_key_selection(login_interface) + login_keypad = nkode_api.get_login_keypad(username, customer_id) + login_key_selection = sign_in_key_selection(login_keypad) successful_login = nkode_api.login(customer_id, username, login_key_selection) assert successful_login diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index da3d4d4..7998e77 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -1,5 +1,5 @@ import pytest -from src.user_interface import UserInterface +from src.user_keypad import UserKeypad from src.models import KeypadSize @@ -8,8 +8,8 @@ from src.models import KeypadSize [KeypadSize(numb_of_keys=10, attrs_per_key=11)] ) def test_attr_set_idx(keypad_size): - user_interface = UserInterface.create(keypad_size) + user_keypad = UserKeypad.create(keypad_size) for attr_idx in range(keypad_size.numb_of_attrs): - user_interface_idx = user_interface.interface[attr_idx] + user_keypad_idx = user_keypad.keypad[attr_idx] - assert (attr_idx % keypad_size.attrs_per_key == user_interface_idx % keypad_size.attrs_per_key) + assert (attr_idx % keypad_size.attrs_per_key == user_keypad_idx % keypad_size.attrs_per_key) diff --git a/test/test_user_interface.py b/test/test_user_interface.py deleted file mode 100644 index 563d4ec..0000000 --- a/test/test_user_interface.py +++ /dev/null @@ -1,35 +0,0 @@ -import pytest -from src.user_interface import UserInterface -from src.models import KeypadSize - - -@pytest.fixture() -def user_interface(): - return UserInterface.create(keypad_size=KeypadSize(attrs_per_key=7, numb_of_keys=10)) - - -def test_dispersion(user_interface): - for _ in range(10000): - pre_dispersion_graph = user_interface.attribute_adjacency_graph() - user_interface.disperse_interface() - post_dispersion_graph = user_interface.attribute_adjacency_graph() - for attr, adj_graph in pre_dispersion_graph.items(): - assert (adj_graph.isdisjoint(post_dispersion_graph[attr])) - - -def test_shuffle_attrs(user_interface): - """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get - expected statistical outcomes like: - - every attribute gets to every key with a uniform distribution - - every attribute is adjacent to every other attribute with uniform distribution - - the order in which the attributes move from key to key is random (i.e. the distance traveled is uniform) - """ - pre_shuffle_interface = user_interface.interface - user_interface.partial_interface_shuffle() - post_shuffle_interface = user_interface.interface - assert (not all( - post_shuffle_interface[idx] == pre_shuffle_interface[idx] for idx in range(len(post_shuffle_interface)) - )) - assert (not all( - post_shuffle_interface[idx] != pre_shuffle_interface[idx] for idx in range(len(post_shuffle_interface)) - )) diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py new file mode 100644 index 0000000..c1f2439 --- /dev/null +++ b/test/test_user_keypad.py @@ -0,0 +1,35 @@ +import pytest +from src.user_keypad import UserKeypad +from src.models import KeypadSize + + +@pytest.fixture() +def user_keypad(): + return UserKeypad.create(keypad_size=KeypadSize(attrs_per_key=7, numb_of_keys=10)) + + +def test_dispersion(user_keypad): + for _ in range(10000): + pre_dispersion_graph = user_keypad.attribute_adjacency_graph() + user_keypad.disperse_keypad() + post_dispersion_graph = user_keypad.attribute_adjacency_graph() + for attr, adj_graph in pre_dispersion_graph.items(): + assert (adj_graph.isdisjoint(post_dispersion_graph[attr])) + + +def test_shuffle_attrs(user_keypad): + """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get + expected statistical outcomes like: + - every attribute gets to every key with a uniform distribution + - every attribute is adjacent to every other attribute with uniform distribution + - the order in which the attributes move from key to key is random (i.e. the distance traveled is uniform) + """ + pre_shuffle_keypad = user_keypad.keypad + user_keypad.partial_keypad_shuffle() + post_shuffle_keypad = user_keypad.keypad + assert (not all( + post_shuffle_keypad[idx] == pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) + )) + assert (not all( + post_shuffle_keypad[idx] != pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) + )) -- 2.49.1 From 571268b86acc752e1c1d5ff6cdd03e43495050e7 Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 10 Mar 2025 09:26:55 -0500 Subject: [PATCH 13/85] refactor; rename classes and methods --- docs/render_markdown.py | 52 ++++++++++++++--------------- notebooks/dispersion_tutorial.ipynb | 14 ++++---- src/customer.py | 30 ++++++++--------- src/customer_attributes.py | 47 -------------------------- src/customer_cipher.py | 47 ++++++++++++++++++++++++++ src/models.py | 8 ++--- src/nkode_api.py | 16 ++++----- src/user.py | 12 +++---- src/user_cipher_keys.py | 28 ++++++++-------- src/user_keypad.py | 22 ++++++------ src/user_signup_session.py | 2 +- test/test_nkode_api.py | 6 ++-- test/test_nkode_interface.py | 6 ++-- test/test_user_cipher_keys.py | 26 +++++++-------- test/test_user_keypad.py | 4 +-- 15 files changed, 160 insertions(+), 160 deletions(-) delete mode 100644 src/customer_attributes.py create mode 100644 src/customer_cipher.py diff --git a/docs/render_markdown.py b/docs/render_markdown.py index ddd1439..1884b11 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -2,7 +2,7 @@ from jinja2 import Environment, FileSystemLoader import os from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize, EncipheredNKode -from src.user_cipher_keys import UserCipherKeys +from src.user_cipher_keys import UserCipher from src.utils import list_to_matrix, matrix_transpose, xor_lists from secrets import choice from string import ascii_lowercase @@ -58,16 +58,16 @@ if __name__ == "__main__": ) keypad_size = KeypadSize( numb_of_keys=5, - attrs_per_key=6 # aka number of sets + props_per_key=6 # aka number of sets ) customer_id = api.create_new_customer(keypad_size, policy) customer = api.customers[customer_id] - set_vals = customer.attributes.set_vals - attr_vals = customer.attributes.attr_vals - customer_attr_view = list_to_matrix(attr_vals, keypad_size.attrs_per_key) + set_vals = customer.customer_cipher.set_key + attr_vals = customer.customer_cipher.prop_key + customer_attr_view = list_to_matrix(attr_vals, keypad_size.props_per_key) - attr_keypad_view = list_to_matrix(attr_vals, keypad_size.attrs_per_key) + attr_keypad_view = list_to_matrix(attr_vals, keypad_size.props_per_key) attr_set_view = matrix_transpose(attr_keypad_view) set_attribute_dict = dict(zip(set_vals, attr_set_view)) @@ -78,7 +78,7 @@ if __name__ == "__main__": passcode_len = 4 user_passcode = signup_interface[:passcode_len] selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) - server_side_attr = [customer.attributes.attr_vals[idx] for idx in user_passcode] + server_side_attr = [customer.customer_cipher.prop_key[idx] for idx in user_passcode] confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) @@ -88,20 +88,20 @@ if __name__ == "__main__": success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) assert success - passcode_server_attr = [customer.attributes.attr_vals[idx] for idx in user_passcode] - passcode_server_set = [customer.attributes.get_attr_set_val(attr) for attr in passcode_server_attr] + passcode_server_attr = [customer.customer_cipher.prop_key[idx] for idx in user_passcode] + passcode_server_set = [customer.customer_cipher.get_prop_set_val(attr) for attr in passcode_server_attr] user_keys = customer.users[username].user_keys - padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.attributes.set_vals) + padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.customer_cipher.set_key) - set_idx = [customer.attributes.get_set_index(set_val) for set_val in padded_passcode_server_set] + set_idx = [customer.customer_cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] mask_set_keys = [user_keys.set_key[idx] for idx in set_idx] ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) mask = user_keys.encode_base64_str(ciphered_mask) - ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.alpha_key) + ciphered_customer_attrs = xor_lists(customer.customer_cipher.prop_key, user_keys.alpha_key) passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len @@ -122,8 +122,8 @@ if __name__ == "__main__": USER LOGIN """ login_interface = api.get_login_interface(username, customer_id) - login_keypad = list_to_matrix(login_interface, keypad_size.attrs_per_key) - selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.attrs_per_key) + login_keypad = list_to_matrix(login_interface, keypad_size.props_per_key) + selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.props_per_key) success = api.login(customer_id, username, selected_keys_login) assert success @@ -133,7 +133,7 @@ if __name__ == "__main__": """ user = customer.users[username] - set_vals = customer.attributes.set_vals + set_vals = customer.customer_cipher.set_key user_keys = user.user_keys user_mask = user.enciphered_passcode.mask decoded_mask = user_keys.decode_base64_str(user_mask) @@ -148,7 +148,7 @@ if __name__ == "__main__": GET PRESUMED ATTRIBUTES """ - set_vals_idx = [customer.attributes.get_set_index(set_val) for set_val in login_passcode_sets] + set_vals_idx = [customer.customer_cipher.get_set_index(set_val) for set_val in login_passcode_sets] presumed_selected_attributes_idx = [] for idx in range(passcode_len): @@ -161,12 +161,12 @@ if __name__ == "__main__": RENEW KEYS """ - old_attrs = customer.attributes.attr_vals.copy() - old_sets = customer.attributes.set_vals.copy() - customer.attributes.renew() - new_attrs = customer.attributes.attr_vals - new_sets = customer.attributes.set_vals - customer_new_attr_view = list_to_matrix(new_attrs, keypad_size.attrs_per_key) + old_attrs = customer.customer_cipher.prop_key.copy() + old_sets = customer.customer_cipher.set_key.copy() + customer.customer_cipher.renew() + new_attrs = customer.customer_cipher.prop_key + new_sets = customer.customer_cipher.set_key + customer_new_attr_view = list_to_matrix(new_attrs, keypad_size.props_per_key) """ RENEW USER @@ -181,12 +181,12 @@ if __name__ == "__main__": """ REFRESH USER KEYS """ - user.user_keys = UserCipherKeys.create( - customer.attributes.keypad_size, - customer.attributes.set_vals, + user.user_keys = UserCipher.create( + customer.customer_cipher.keypad_size, + customer.customer_cipher.set_key, user.user_keys.max_nkode_len ) - user.enciphered_passcode = user.user_keys.encipher_nkode(presumed_selected_attributes_idx, customer.attributes) + user.enciphered_passcode = user.user_keys.encipher_nkode(presumed_selected_attributes_idx, customer.customer_cipher) user.renew = False # Define some data to pass to the template diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index 97ca9d8..2d08272 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -29,10 +29,10 @@ "from src.models import KeypadSize\n", "\n", "def keypad_md_table(interface: list[int], keypad_size: KeypadSize) -> str:\n", - " assert (keypad_size.numb_of_attrs == len(interface))\n", - " interface_keypad = list_to_matrix(interface, keypad_size.attrs_per_key)\n", - " table = \"|key|\" + \"\".join([f\"set{idx}|\" for idx in range(keypad_size.attrs_per_key)])\n", - " table += \"\\n|\" + \"\".join(\"-|\" for _ in range(keypad_size.attrs_per_key+1))\n", + " assert (keypad_size.numb_of_props == len(interface))\n", + " interface_keypad = list_to_matrix(interface, keypad_size.props_per_key)\n", + " table = \"|key|\" + \"\".join([f\"set{idx}|\" for idx in range(keypad_size.props_per_key)])\n", + " table += \"\\n|\" + \"\".join(\"-|\" for _ in range(keypad_size.props_per_key + 1))\n", "\n", " for key in range(keypad_size.numb_of_keys):\n", " table += f\"\\n|key{key+1}|\"\n", @@ -41,7 +41,7 @@ " return table\n", "\n", "\n", - "keypad_size = KeypadSize(numb_of_keys=5, attrs_per_key=4)\n", + "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", "attrs = [1, 10, 11, 100]\n", "keypad = []\n", "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", @@ -73,7 +73,7 @@ } ], "source": [ - "demo_interface_matrix = list_to_matrix(demo_interface.keypad, demo_interface.keypad_size.attrs_per_key)\n", + "demo_interface_matrix = list_to_matrix(demo_interface.keypad, demo_interface.keypad_size.props_per_key)\n", "shuffled_keys = secure_fisher_yates_shuffle(demo_interface_matrix)\n", "shuffled_keys_list = matrix_to_list(shuffled_keys)\n", "display(Markdown(keypad_md_table(shuffled_keys_list, keypad_size)))\n" @@ -100,7 +100,7 @@ } ], "source": [ - "attr_rotation = secure_fisher_yates_shuffle(list(range(keypad_size.numb_of_keys)))[:keypad_size.attrs_per_key]\n", + "attr_rotation = secure_fisher_yates_shuffle(list(range(keypad_size.numb_of_keys)))[:keypad_size.props_per_key]\n", "dispersed_interface = UserKeypad.random_attribute_rotation(\n", " shuffled_keys,\n", " attr_rotation\n", diff --git a/src/customer.py b/src/customer.py index ee14c07..613a195 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from uuid import UUID -from src.customer_attributes import CustomerAttributes +from src.customer_cipher import CustomerCipher from src.models import NKodePolicy from src.user import User from src.utils import xor_lists @@ -9,7 +9,7 @@ from src.utils import xor_lists class Customer: customer_id: UUID nkode_policy: NKodePolicy - attributes: CustomerAttributes + customer_cipher: CustomerCipher users: dict[str, User] # TODO: validate policy and keypad size don't conflict @@ -23,16 +23,16 @@ class Customer: if username not in self.users: raise ValueError(f"User '{username}' does not exist") - keypad_size = self.attributes.keypad_size.numb_of_keys - if not all(0 <= key_idx < keypad_size for key_idx in selected_keys): - raise ValueError(f"Invalid key indices. Must be between 0 and {keypad_size - 1}") + numb_of_keys = self.customer_cipher.keypad_size.numb_of_keys + if not all(0 <= key_idx < numb_of_keys for key_idx in selected_keys): + raise ValueError(f"Invalid key indices. Must be between 0 and {numb_of_keys - 1}") passcode_len = len(selected_keys) user = self.users[username] passcode_set_vals = user.user_keys.decipher_mask( - user.enciphered_passcode.mask, self.attributes.set_vals, passcode_len) - set_vals_idx = [self.attributes.get_set_index(set_val) for set_val in passcode_set_vals] + user.enciphered_passcode.mask, self.customer_cipher.set_key, passcode_len) + set_vals_idx = [self.customer_cipher.get_set_index(set_val) for set_val in passcode_set_vals] presumed_selected_attributes_idx = [] for idx in range(passcode_len): @@ -41,20 +41,20 @@ class Customer: selected_attr_idx = user.user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) presumed_selected_attributes_idx.append(selected_attr_idx) - enciphered_attr = user.user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, self.attributes) + enciphered_attr = user.user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, self.customer_cipher) if enciphered_attr != user.enciphered_passcode.code: return False if user.renew: - user.refresh_passcode(presumed_selected_attributes_idx, self.attributes) + user.refresh_passcode(presumed_selected_attributes_idx, self.customer_cipher) return True def renew_keys(self) -> bool: - old_attrs = self.attributes.attr_vals.copy() - old_sets = self.attributes.set_vals.copy() - self.attributes.renew() - new_attrs = self.attributes.attr_vals - new_sets = self.attributes.set_vals + old_attrs = self.customer_cipher.prop_key.copy() + old_sets = self.customer_cipher.set_key.copy() + self.customer_cipher.renew() + new_attrs = self.customer_cipher.prop_key + new_sets = self.customer_cipher.set_key attrs_xor = xor_lists(new_attrs, old_attrs) set_xor = xor_lists(new_sets, old_sets) @@ -66,7 +66,7 @@ class Customer: def valid_new_nkode(self, passcode_attr_idx: list[int]) -> bool: nkode_len = len(passcode_attr_idx) passcode_set_values = [ - self.attributes.get_attr_set_val(self.attributes.attr_vals[attr_idx]) for attr_idx in passcode_attr_idx + self.customer_cipher.get_prop_set_val(self.customer_cipher.prop_key[attr_idx]) for attr_idx in passcode_attr_idx ] distinct_sets = len(set(passcode_set_values)) distinct_attributes = len(set(passcode_attr_idx)) diff --git a/src/customer_attributes.py b/src/customer_attributes.py deleted file mode 100644 index 6680cbe..0000000 --- a/src/customer_attributes.py +++ /dev/null @@ -1,47 +0,0 @@ -from dataclasses import dataclass -from typing import ClassVar - -from src.models import KeypadSize -from src.utils import generate_random_nonrepeating_list - - -@dataclass -class CustomerAttributes: - attr_vals: list[int] - set_vals: list[int] - keypad_size: KeypadSize - MAX_KEYS: ClassVar[int] = 256 - MAX_ATTRS_PER_KEY: ClassVar[int] = 256 - - def __post_init__(self): - self.check_keys_vs_attrs() - - def check_keys_vs_attrs(self) -> None: - if self.keypad_size.is_dispersable: - raise ValueError("number of keys must be less than the number of " - "attributes per key to be dispersion resistant") - - @classmethod - def create(cls, keypad_size: KeypadSize) -> 'CustomerAttributes': - if keypad_size.numb_of_keys > cls.MAX_KEYS or keypad_size.attrs_per_key > cls.MAX_ATTRS_PER_KEY: - raise ValueError(f"Keys and attributes per key must not exceed {cls.MAX_KEYS}") - return cls( - attr_vals=generate_random_nonrepeating_list(keypad_size.numb_of_attrs), - set_vals=generate_random_nonrepeating_list(keypad_size.attrs_per_key), - keypad_size=keypad_size, - ) - - def renew(self): - self.attr_vals = generate_random_nonrepeating_list(self.keypad_size.numb_of_attrs) - self.set_vals = generate_random_nonrepeating_list(self.keypad_size.attrs_per_key) - - def get_attr_set_val(self, attr: int) -> int: - assert (attr in self.attr_vals) - attr_idx = self.attr_vals.index(attr) - set_idx = attr_idx % self.keypad_size.attrs_per_key - return self.set_vals[set_idx] - - def get_set_index(self, set_val: int) -> int: - if set_val not in self.set_vals: - raise ValueError(f"Set value {set_val} not found in set values") - return self.set_vals.index(set_val) \ No newline at end of file diff --git a/src/customer_cipher.py b/src/customer_cipher.py new file mode 100644 index 0000000..cfade71 --- /dev/null +++ b/src/customer_cipher.py @@ -0,0 +1,47 @@ +from dataclasses import dataclass +from typing import ClassVar + +from src.models import KeypadSize +from src.utils import generate_random_nonrepeating_list + + +@dataclass +class CustomerCipher: + prop_key: list[int] + set_key: list[int] + keypad_size: KeypadSize + MAX_KEYS: ClassVar[int] = 256 + MAX_PROP_PER_KEY: ClassVar[int] = 256 + + def __post_init__(self): + self.check_keys_vs_props() + + def check_keys_vs_props(self) -> None: + if self.keypad_size.is_dispersable: + raise ValueError("number of keys must be less than the number of " + "properties per key to be dispersion resistant") + + @classmethod + def create(cls, keypad_size: KeypadSize) -> 'CustomerCipher': + if keypad_size.numb_of_keys > cls.MAX_KEYS or keypad_size.props_per_key > cls.MAX_PROP_PER_KEY: + raise ValueError(f"Keys and properties per key must not exceed {cls.MAX_KEYS}") + return cls( + prop_key=generate_random_nonrepeating_list(keypad_size.numb_of_props), + set_key=generate_random_nonrepeating_list(keypad_size.props_per_key), + keypad_size=keypad_size, + ) + + def renew(self): + self.prop_key = generate_random_nonrepeating_list(self.keypad_size.numb_of_props) + self.set_key = generate_random_nonrepeating_list(self.keypad_size.props_per_key) + + def get_prop_set_val(self, prop: int) -> int: + assert (prop in self.prop_key) + prop_idx = self.prop_key.index(prop) + set_idx = prop_idx % self.keypad_size.props_per_key + return self.set_key[set_idx] + + def get_set_index(self, set_val: int) -> int: + if set_val not in self.set_key: + raise ValueError(f"Set value {set_val} not found in set values") + return self.set_key.index(set_val) \ No newline at end of file diff --git a/src/models.py b/src/models.py index 9178196..39957ae 100644 --- a/src/models.py +++ b/src/models.py @@ -19,13 +19,13 @@ class NKodePolicy: @dataclass class KeypadSize: - attrs_per_key: int + props_per_key: int numb_of_keys: int @property - def numb_of_attrs(self) -> int: - return self.attrs_per_key * self.numb_of_keys + def numb_of_props(self) -> int: + return self.props_per_key * self.numb_of_keys @property def is_dispersable(self) -> bool: - return self.attrs_per_key <= self.numb_of_keys \ No newline at end of file + return self.props_per_key <= self.numb_of_keys \ No newline at end of file diff --git a/src/nkode_api.py b/src/nkode_api.py index a02e9ff..f647280 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -5,10 +5,10 @@ from typing import Dict, List, Tuple from src.customer import Customer from src.models import NKodePolicy, KeypadSize from src.user import User -from src.user_cipher_keys import UserCipherKeys +from src.user_cipher_keys import UserCipher from src.user_signup_session import UserSignupSession from src.user_keypad import UserKeypad -from src.customer_attributes import CustomerAttributes +from src.customer_cipher import CustomerCipher @dataclass @@ -19,7 +19,7 @@ class NKodeAPI: def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( customer_id=uuid4(), - attributes=CustomerAttributes.create(keypad_size), + customer_cipher=CustomerCipher.create(keypad_size), users={}, nkode_policy=nkode_policy ) @@ -30,7 +30,7 @@ class NKodeAPI: if customer_id not in self.customers.keys(): raise ValueError(f"Customer with ID '{customer_id}' does not exist") customer = self.customers[customer_id] - login_keypad = UserKeypad.create(customer.attributes.keypad_size) + login_keypad = UserKeypad.create(customer.customer_cipher.keypad_size) set_keypad = login_keypad.sign_up_keypad() new_session = UserSignupSession( session_id=uuid4(), @@ -75,12 +75,12 @@ class NKodeAPI: 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 = UserCipherKeys.create( - customer.attributes.keypad_size, - customer.attributes.set_vals, + new_user_keys = UserCipher.create( + customer.customer_cipher.keypad_size, + customer.customer_cipher.set_key, customer.nkode_policy.max_nkode_len ) - enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.attributes) + enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.customer_cipher) new_user = User( username=username, enciphered_passcode=enciphered_passcode, diff --git a/src/user.py b/src/user.py index dad959c..7b3d21d 100644 --- a/src/user.py +++ b/src/user.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from src.models import EncipheredNKode -from src.customer_attributes import CustomerAttributes -from src.user_cipher_keys import UserCipherKeys +from src.customer_cipher import CustomerCipher +from src.user_cipher_keys import UserCipher from src.user_keypad import UserKeypad from src.utils import xor_lists @@ -10,7 +10,7 @@ from src.utils import xor_lists class User: username: str enciphered_passcode: EncipheredNKode - user_keys: UserCipherKeys + user_keys: UserCipher user_keypad: UserKeypad renew: bool = field(default=False) @@ -19,10 +19,10 @@ class User: self.user_keys.set_key = xor_lists(self.user_keys.set_key, sets_xor) self.user_keys.alpha_key = xor_lists(self.user_keys.alpha_key, attrs_xor) - def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerAttributes): - self.user_keys = UserCipherKeys.create( + def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerCipher): + self.user_keys = UserCipher.create( customer_attributes.keypad_size, - customer_attributes.set_vals, + customer_attributes.set_key, self.user_keys.max_nkode_len ) self.enciphered_passcode = self.user_keys.encipher_nkode(passcode_attr_idx, customer_attributes) diff --git a/src/user_cipher_keys.py b/src/user_cipher_keys.py index a4a4d3f..d5390d6 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher_keys.py @@ -4,11 +4,11 @@ from dataclasses import dataclass import bcrypt from secrets import choice from src.models import EncipheredNKode, KeypadSize -from src.customer_attributes import CustomerAttributes +from src.customer_cipher import CustomerCipher from src.utils import generate_random_nonrepeating_list, xor_lists, int_array_to_bytes @dataclass -class UserCipherKeys: +class UserCipher: alpha_key: list[int] set_key: list[int] pass_key: list[int] @@ -17,15 +17,15 @@ class UserCipherKeys: max_nkode_len: int @classmethod - def create(cls, keypad_size: KeypadSize, set_values: list[int], max_nkode_len: int) -> 'UserCipherKeys': - if len(set_values) != keypad_size.attrs_per_key: + def create(cls, keypad_size: KeypadSize, set_values: list[int], max_nkode_len: int) -> 'UserCipher': + if len(set_values) != keypad_size.props_per_key: raise ValueError("Invalid set values") - set_key = generate_random_nonrepeating_list(keypad_size.attrs_per_key) + set_key = generate_random_nonrepeating_list(keypad_size.props_per_key) set_key = xor_lists(set_key, set_values) - return UserCipherKeys( - alpha_key=generate_random_nonrepeating_list(keypad_size.attrs_per_key * keypad_size.numb_of_keys), + return UserCipher( + alpha_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys), pass_key=generate_random_nonrepeating_list(max_nkode_len), mask_key=generate_random_nonrepeating_list(max_nkode_len), set_key=set_key, @@ -64,11 +64,11 @@ class UserCipherKeys: def encipher_nkode( self, passcode_attr_idx: list[int], - customer_attributes: CustomerAttributes + customer_attributes: CustomerCipher ) -> EncipheredNKode: - passcode_attrs = [customer_attributes.attr_vals[idx] for idx in passcode_attr_idx] - passcode_sets = [customer_attributes.get_attr_set_val(attr) for attr in passcode_attrs] + passcode_attrs = [customer_attributes.prop_key[idx] for idx in passcode_attr_idx] + passcode_sets = [customer_attributes.get_prop_set_val(attr) for attr in passcode_attrs] mask = self.encipher_mask(passcode_sets, customer_attributes) code = self.encipher_salt_hash_code(passcode_attr_idx, customer_attributes) return EncipheredNKode( @@ -79,10 +79,10 @@ class UserCipherKeys: def encipher_salt_hash_code( self, passcode_attr_idx: list[int], - customer_attributes: CustomerAttributes, + customer_attributes: CustomerCipher, ) -> str: passcode_len = len(passcode_attr_idx) - passcode_attrs = [customer_attributes.attr_vals[idx] for idx in passcode_attr_idx] + passcode_attrs = [customer_attributes.prop_key[idx] for idx in passcode_attr_idx] passcode_cipher = self.pass_key.copy() @@ -96,9 +96,9 @@ class UserCipherKeys: def encipher_mask( self, passcode_sets: list[int], - customer_attributes: CustomerAttributes + customer_attributes: CustomerCipher ) -> str: - padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_attributes.set_vals) + padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_attributes.set_key) set_idx = [customer_attributes.get_set_index(set_val) for set_val in padded_passcode_sets] mask_set_keys = [self.set_key[idx] for idx in set_idx] ciphered_mask = xor_lists(mask_set_keys, padded_passcode_sets) diff --git a/src/user_keypad.py b/src/user_keypad.py index 4efe5c6..bee1749 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -11,7 +11,7 @@ class UserKeypad: @classmethod def create(cls, keypad_size: KeypadSize) -> 'UserKeypad': keypad = UserKeypad( - keypad=list(range(keypad_size.numb_of_attrs)), + keypad=list(range(keypad_size.numb_of_props)), keypad_size=keypad_size ) keypad.random_keypad_shuffle() @@ -30,12 +30,12 @@ class UserKeypad: keypad=matrix_to_list(keypad_matrix), keypad_size=KeypadSize( numb_of_keys=self.keypad_size.numb_of_keys, - attrs_per_key=self.keypad_size.numb_of_keys + props_per_key=self.keypad_size.numb_of_keys ) ) def keypad_matrix(self) -> list[list[int]]: - return list_to_matrix(self.keypad, self.keypad_size.attrs_per_key) + return list_to_matrix(self.keypad, self.keypad_size.props_per_key) def random_keypad_shuffle(self): keypad_view = self.keypad_matrix() @@ -48,11 +48,11 @@ class UserKeypad: def disperse_keypad(self): if not self.keypad_size.is_dispersable: raise ValueError("Keypad size is not dispersable") - user_keypad_matrix = list_to_matrix(self.keypad, self.keypad_size.attrs_per_key) + user_keypad_matrix = list_to_matrix(self.keypad, self.keypad_size.props_per_key) shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[ - :self.keypad_size.attrs_per_key] + :self.keypad_size.props_per_key] dispersed_keypad = self.random_attribute_rotation( shuffled_keys, attr_rotation, @@ -61,10 +61,10 @@ class UserKeypad: def partial_keypad_shuffle(self): # TODO: this should be split shuffle - numb_of_selected_sets = self.keypad_size.attrs_per_key // 2 - # randomly shuffle half the sets. if attrs_per_key is odd, randomly add one 50% of the time - numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.attrs_per_key & 1) == 1 else 0 - selected_sets = secure_fisher_yates_shuffle(list(range(self.keypad_size.attrs_per_key)))[:numb_of_selected_sets] + numb_of_selected_sets = self.keypad_size.props_per_key // 2 + # randomly shuffle half the sets. if props_per_key is odd, randomly add one 50% of the time + numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.props_per_key & 1) == 1 else 0 + selected_sets = secure_fisher_yates_shuffle(list(range(self.keypad_size.props_per_key)))[:numb_of_selected_sets] user_keypad_matrix = self.keypad_matrix() shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) keypad_by_sets = [] @@ -100,7 +100,7 @@ class UserKeypad: def get_attr_idx_by_keynumb_setidx(self, key_numb: int, set_idx: int) -> int: if not (0 <= key_numb < self.keypad_size.numb_of_keys): raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") - if not (0 <= set_idx < self.keypad_size.attrs_per_key): - raise ValueError(f"set_idx must be between 0 and {self.keypad_size.attrs_per_key - 1}") + if not (0 <= set_idx < self.keypad_size.props_per_key): + raise ValueError(f"set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_attr_idx = self.keypad_matrix() return keypad_attr_idx[key_numb][set_idx] diff --git a/src/user_signup_session.py b/src/user_signup_session.py index b9d14ed..ae277e5 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -19,7 +19,7 @@ class UserSignupSession(BaseModel): 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): raise ValueError("Key values must be within valid range") - attrs_per_key = self.keypad_size.attrs_per_key + attrs_per_key = self.keypad_size.props_per_key set_key_entry = self.set_key_entry if len(set_key_entry) != len(confirm_key_entry): raise ValueError("Key entry lengths must match") diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 13b2df3..128880d 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -9,8 +9,8 @@ def nkode_api() -> NKodeAPI: @pytest.mark.parametrize("keypad_size,passocode_len", [ - (KeypadSize(numb_of_keys=10, attrs_per_key=11), 4), - (KeypadSize(numb_of_keys=10, attrs_per_key=12), 5), + (KeypadSize(numb_of_keys=10, props_per_key=11), 4), + (KeypadSize(numb_of_keys=10, props_per_key=12), 5), ]) def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): username = "test_username" @@ -32,7 +32,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): ) assert successful_confirm - sign_in_key_selection = lambda keypad: [keypad.index(attr) // keypad_size.attrs_per_key for attr in user_passcode] + sign_in_key_selection = lambda keypad: [keypad.index(attr) // keypad_size.props_per_key for attr in user_passcode] login_keypad = nkode_api.get_login_keypad(username, customer_id) login_key_selection = sign_in_key_selection(login_keypad) successful_login = nkode_api.login(customer_id, username, login_key_selection) diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index 7998e77..b56908c 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -5,11 +5,11 @@ from src.models import KeypadSize @pytest.mark.parametrize( "keypad_size", - [KeypadSize(numb_of_keys=10, attrs_per_key=11)] + [KeypadSize(numb_of_keys=10, props_per_key=11)] ) def test_attr_set_idx(keypad_size): user_keypad = UserKeypad.create(keypad_size) - for attr_idx in range(keypad_size.numb_of_attrs): + for attr_idx in range(keypad_size.numb_of_props): user_keypad_idx = user_keypad.keypad[attr_idx] - assert (attr_idx % keypad_size.attrs_per_key == user_keypad_idx % keypad_size.attrs_per_key) + assert (attr_idx % keypad_size.props_per_key == user_keypad_idx % keypad_size.props_per_key) diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 1b7e11f..e96adbd 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -1,7 +1,7 @@ import pytest from src.models import KeypadSize -from src.user_cipher_keys import UserCipherKeys, CustomerAttributes +from src.user_cipher_keys import UserCipher, CustomerCipher from src.utils import generate_random_nonrepeating_list @@ -13,8 +13,8 @@ from src.utils import generate_random_nonrepeating_list ) def test_encode_decode_base64(passcode_len): data = generate_random_nonrepeating_list(passcode_len) - encoded = UserCipherKeys.encode_base64_str(data) - decoded = UserCipherKeys.decode_base64_str(encoded) + encoded = UserCipher.encode_base64_str(data) + decoded = UserCipher.decode_base64_str(encoded) assert (len(data) == len(decoded)) assert (all(data[idx] == decoded[idx] for idx in range(passcode_len))) @@ -22,21 +22,21 @@ def test_encode_decode_base64(passcode_len): @pytest.mark.parametrize( "keypad_size,max_nkode_len", [ - (KeypadSize(numb_of_keys=10, attrs_per_key=11), 10), - (KeypadSize(numb_of_keys=9, attrs_per_key=11), 10), - (KeypadSize(numb_of_keys=8, attrs_per_key=11), 12), + (KeypadSize(numb_of_keys=10, props_per_key=11), 10), + (KeypadSize(numb_of_keys=9, props_per_key=11), 10), + (KeypadSize(numb_of_keys=8, props_per_key=11), 12), ]) def test_decode_mask(keypad_size, max_nkode_len): - customer = CustomerAttributes.create(keypad_size) + customer = CustomerCipher.create(keypad_size) passcode_entry = generate_random_nonrepeating_list( - keypad_size.numb_of_attrs, - max_val=keypad_size.numb_of_attrs)[:4] - passcode_values = [customer.attr_vals[idx] for idx in passcode_entry] - set_vals = customer.set_vals - user_keys = UserCipherKeys.create(keypad_size, set_vals, max_nkode_len) + keypad_size.numb_of_props, + max_val=keypad_size.numb_of_props)[:4] + passcode_values = [customer.prop_key[idx] for idx in passcode_entry] + set_vals = customer.set_key + user_keys = UserCipher.create(keypad_size, set_vals, max_nkode_len) passcode = user_keys.encipher_nkode(passcode_entry, customer) - orig_passcode_set_vals = [customer.get_attr_set_val(attr) for attr in passcode_values] + orig_passcode_set_vals = [customer.get_prop_set_val(attr) for attr in passcode_values] passcode_set_vals = user_keys.decipher_mask(passcode.mask, set_vals, len(passcode_entry)) assert (len(passcode_set_vals) == len(orig_passcode_set_vals)) assert (all(orig_passcode_set_vals[idx] == passcode_set_vals[idx] for idx in range(len(passcode_set_vals)))) diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index c1f2439..0929d65 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -5,7 +5,7 @@ from src.models import KeypadSize @pytest.fixture() def user_keypad(): - return UserKeypad.create(keypad_size=KeypadSize(attrs_per_key=7, numb_of_keys=10)) + return UserKeypad.create(keypad_size=KeypadSize(props_per_key=7, numb_of_keys=10)) def test_dispersion(user_keypad): @@ -22,7 +22,7 @@ def test_shuffle_attrs(user_keypad): expected statistical outcomes like: - every attribute gets to every key with a uniform distribution - every attribute is adjacent to every other attribute with uniform distribution - - the order in which the attributes move from key to key is random (i.e. the distance traveled is uniform) + - the order in which the customer_cipher move from key to key is random (i.e. the distance traveled is uniform) """ pre_shuffle_keypad = user_keypad.keypad user_keypad.partial_keypad_shuffle() -- 2.49.1 From dd0b496a21c2719eb3c0d7871ea4942ca47dba13 Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 10 Mar 2025 10:05:49 -0500 Subject: [PATCH 14/85] refactor; rename alpha to prop --- docs/nkode_authentication_template.md | 24 ++++++++-------- docs/render_markdown.py | 6 ++-- src/customer_cipher.py | 1 - src/nkode_api.py | 2 +- src/user.py | 4 +-- src/{user_cipher_keys.py => user_cipher.py} | 32 +++++++++------------ test/test_user_cipher_keys.py | 2 +- 7 files changed, 33 insertions(+), 38 deletions(-) rename src/{user_cipher_keys.py => user_cipher.py} (78%) diff --git a/docs/nkode_authentication_template.md b/docs/nkode_authentication_template.md index b981ac1..cfa9cb2 100644 --- a/docs/nkode_authentication_template.md +++ b/docs/nkode_authentication_template.md @@ -165,7 +165,7 @@ set_key = generate_random_nonrepeating_list(keypad_size.attrs_per_key, max_numb= set_key = xor_lists(set_key, customer_attr.set_vals) UserCipherKeys( - alpha_key=generate_random_nonrepeating_list(keypad_size.attrs_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), + prop_key=generate_random_nonrepeating_list(keypad_size.attrs_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), pass_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), mask_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), set_key=set_key, @@ -177,7 +177,7 @@ UserCipherKeys( ##### User Cipher Keys Values ``` user_keys = UserCipherKeys( - alpha_key = {{ user_keys.alpha_key }}, + prop_key = {{ user_keys.prop_key }}, pass_key = {{ user_keys.pass_key }}, mask_key = {{ user_keys.mask_key }}, set_key = {{ user_keys.set_key }}, @@ -233,12 +233,12 @@ Mask: {{ enciphered_nkode.mask }} #### Passcode Enciphering and Hashing -- ciphered_customer_attr = alpha_key ^ customer_attr +- ciphered_customer_attr = prop_key ^ customer_attr - ciphered_passcode_i = pass_key_i ^ ciphered_customer_attr_i - code = hash(ciphered_passcode, salt) ``` -ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.alpha_key) +ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.prop_key) passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len @@ -389,17 +389,17 @@ sets_xor = xor_lists(new_sets, old_sets) for user in customer.users.values(): user.renew = True user.user_keys.set_key = xor_lists(user.user_keys.set_key, sets_xor) - user.user_keys.alpha_key = xor_lists(user.user_keys.alpha_key, attrs_xor) + user.user_keys.prop_key = xor_lists(user.user_keys.prop_key, attrs_xor) ``` -##### User Alpha Key -The user's alpha key was a randomly generated list of length `numb_of_keys * attr_per_key`. -Now each value in the alpha key is `alpha_key_i = old_alpha_key_i ^ new_attr_i ^ old_attr_i`. -Recall in the login process, `ciphered_customer_attrs = alpha_key ^ customer_attr`. +##### User prop Key +The user's prop key was a randomly generated list of length `numb_of_keys * attr_per_key`. +Now each value in the prop key is `prop_key_i = old_prop_key_i ^ new_attr_i ^ old_attr_i`. +Recall in the login process, `ciphered_customer_attrs = prop_key ^ customer_attr`. Since the customer_attr is now the new value, it gets canceled out, leaving: ``` -new_alpha_key = old_alpha_key ^ old_attr ^ new_attr -ciphered_customer_attrs = new_alpha_key ^ new_attr -ciphered_customer_attrs = old_alpha_key ^ old_attr # since new_attr cancel out +new_prop_key = old_prop_key ^ old_attr ^ new_attr +ciphered_customer_attrs = new_prop_key ^ new_attr +ciphered_customer_attrs = old_prop_key ^ old_attr # since new_attr cancel out ``` Using the new customer attributes, we can validate the user's login attempt with the same hash. diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 1884b11..86ac36f 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -2,7 +2,7 @@ from jinja2 import Environment, FileSystemLoader import os from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize, EncipheredNKode -from src.user_cipher_keys import UserCipher +from src.user_cipher import UserCipher from src.utils import list_to_matrix, matrix_transpose, xor_lists from secrets import choice from string import ascii_lowercase @@ -101,7 +101,7 @@ if __name__ == "__main__": ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) mask = user_keys.encode_base64_str(ciphered_mask) - ciphered_customer_attrs = xor_lists(customer.customer_cipher.prop_key, user_keys.alpha_key) + ciphered_customer_attrs = xor_lists(customer.customer_cipher.prop_key, user_keys.prop_key) passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len @@ -176,7 +176,7 @@ if __name__ == "__main__": for user in customer.users.values(): user.renew = True user.user_keys.set_key = xor_lists(user.user_keys.set_key, sets_xor) - user.user_keys.alpha_key = xor_lists(user.user_keys.alpha_key, attrs_xor) + user.user_keys.prop_key = xor_lists(user.user_keys.prop_key, attrs_xor) """ REFRESH USER KEYS diff --git a/src/customer_cipher.py b/src/customer_cipher.py index cfade71..f77470b 100644 --- a/src/customer_cipher.py +++ b/src/customer_cipher.py @@ -1,6 +1,5 @@ from dataclasses import dataclass from typing import ClassVar - from src.models import KeypadSize from src.utils import generate_random_nonrepeating_list diff --git a/src/nkode_api.py b/src/nkode_api.py index f647280..d81e329 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -5,7 +5,7 @@ from typing import Dict, List, Tuple from src.customer import Customer from src.models import NKodePolicy, KeypadSize from src.user import User -from src.user_cipher_keys import UserCipher +from src.user_cipher import UserCipher from src.user_signup_session import UserSignupSession from src.user_keypad import UserKeypad from src.customer_cipher import CustomerCipher diff --git a/src/user.py b/src/user.py index 7b3d21d..9c0e319 100644 --- a/src/user.py +++ b/src/user.py @@ -1,7 +1,7 @@ from dataclasses import dataclass, field from src.models import EncipheredNKode from src.customer_cipher import CustomerCipher -from src.user_cipher_keys import UserCipher +from src.user_cipher import UserCipher from src.user_keypad import UserKeypad from src.utils import xor_lists @@ -17,7 +17,7 @@ class User: def renew_keys(self, sets_xor: list[int], attrs_xor: list[int]): self.renew = True self.user_keys.set_key = xor_lists(self.user_keys.set_key, sets_xor) - self.user_keys.alpha_key = xor_lists(self.user_keys.alpha_key, attrs_xor) + self.user_keys.prop_key = xor_lists(self.user_keys.prop_key, attrs_xor) def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerCipher): self.user_keys = UserCipher.create( diff --git a/src/user_cipher_keys.py b/src/user_cipher.py similarity index 78% rename from src/user_cipher_keys.py rename to src/user_cipher.py index d5390d6..c69177e 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher.py @@ -9,7 +9,7 @@ from src.utils import generate_random_nonrepeating_list, xor_lists, int_array_to @dataclass class UserCipher: - alpha_key: list[int] + prop_key: list[int] set_key: list[int] pass_key: list[int] mask_key: list[int] @@ -25,7 +25,7 @@ class UserCipher: set_key = xor_lists(set_key, set_values) return UserCipher( - alpha_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys), + prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys), pass_key=generate_random_nonrepeating_list(max_nkode_len), mask_key=generate_random_nonrepeating_list(max_nkode_len), set_key=set_key, @@ -63,14 +63,14 @@ class UserCipher: def encipher_nkode( self, - passcode_attr_idx: list[int], - customer_attributes: CustomerCipher + passcode_prop_idx: list[int], + customer_cipher: CustomerCipher ) -> EncipheredNKode: - passcode_attrs = [customer_attributes.prop_key[idx] for idx in passcode_attr_idx] - passcode_sets = [customer_attributes.get_prop_set_val(attr) for attr in passcode_attrs] - mask = self.encipher_mask(passcode_sets, customer_attributes) - code = self.encipher_salt_hash_code(passcode_attr_idx, customer_attributes) + passcode_attrs = [customer_cipher.prop_key[idx] for idx in passcode_prop_idx] + passcode_sets = [customer_cipher.get_prop_set_val(attr) for attr in passcode_attrs] + mask = self.encipher_mask(passcode_sets, customer_cipher) + code = self.encipher_salt_hash_code(passcode_prop_idx, customer_cipher) return EncipheredNKode( code=code, mask=mask @@ -78,19 +78,15 @@ class UserCipher: def encipher_salt_hash_code( self, - passcode_attr_idx: list[int], - customer_attributes: CustomerCipher, + passcode_prop_idx: list[int], + customer_prop: CustomerCipher, ) -> str: - passcode_len = len(passcode_attr_idx) - passcode_attrs = [customer_attributes.prop_key[idx] for idx in passcode_attr_idx] - + passcode_len = len(passcode_prop_idx) + passcode_attrs = [customer_prop.prop_key[idx] for idx in passcode_prop_idx] passcode_cipher = self.pass_key.copy() - for idx in range(passcode_len): - attr_idx = passcode_attr_idx[idx] - alpha = self.alpha_key[attr_idx] - attr_val = passcode_attrs[idx] - passcode_cipher[idx] ^= alpha ^ attr_val + attr_idx = passcode_prop_idx[idx] + passcode_cipher[idx] ^= self.prop_key[attr_idx] ^ passcode_attrs[idx] return self._hash_passcode(passcode_cipher) def encipher_mask( diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index e96adbd..c5ec6d5 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -1,7 +1,7 @@ import pytest from src.models import KeypadSize -from src.user_cipher_keys import UserCipher, CustomerCipher +from src.user_cipher import UserCipher, CustomerCipher from src.utils import generate_random_nonrepeating_list -- 2.49.1 From 526e53758691c17483bbcb9bb158fabc667fc565 Mon Sep 17 00:00:00 2001 From: Donovan Date: Tue, 11 Mar 2025 10:33:08 -0500 Subject: [PATCH 15/85] refactor use numpy in user_cipher.py --- docs/render_markdown.py | 50 ++++----- notebooks/nkode_tutorial.ipynb | 195 ++++++++++++++++++--------------- src/customer.py | 26 ++--- src/nkode_api.py | 13 +-- src/user.py | 14 +-- src/user_cipher.py | 107 ++++++++++++------ test/test_user_keypad.py | 2 +- 7 files changed, 230 insertions(+), 177 deletions(-) diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 86ac36f..8d9ebed 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -63,8 +63,8 @@ if __name__ == "__main__": customer_id = api.create_new_customer(keypad_size, policy) customer = api.customers[customer_id] - set_vals = customer.customer_cipher.set_key - attr_vals = customer.customer_cipher.prop_key + set_vals = customer.cipher.set_key + attr_vals = customer.cipher.prop_key customer_attr_view = list_to_matrix(attr_vals, keypad_size.props_per_key) attr_keypad_view = list_to_matrix(attr_vals, keypad_size.props_per_key) @@ -78,7 +78,7 @@ if __name__ == "__main__": passcode_len = 4 user_passcode = signup_interface[:passcode_len] selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) - server_side_attr = [customer.customer_cipher.prop_key[idx] for idx in user_passcode] + server_side_attr = [customer.cipher.prop_key[idx] for idx in user_passcode] confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) @@ -88,20 +88,20 @@ if __name__ == "__main__": success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) assert success - passcode_server_attr = [customer.customer_cipher.prop_key[idx] for idx in user_passcode] - passcode_server_set = [customer.customer_cipher.get_prop_set_val(attr) for attr in passcode_server_attr] + passcode_server_attr = [customer.cipher.prop_key[idx] for idx in user_passcode] + passcode_server_set = [customer.cipher.get_prop_set_val(attr) for attr in passcode_server_attr] - user_keys = customer.users[username].user_keys + user_keys = customer.users[username].cipher - padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.customer_cipher.set_key) + padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.cipher.set_key) - set_idx = [customer.customer_cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] + set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] mask_set_keys = [user_keys.set_key[idx] for idx in set_idx] ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) mask = user_keys.encode_base64_str(ciphered_mask) - ciphered_customer_attrs = xor_lists(customer.customer_cipher.prop_key, user_keys.prop_key) + ciphered_customer_attrs = xor_lists(customer.cipher.prop_key, user_keys.prop_key) passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len @@ -133,8 +133,8 @@ if __name__ == "__main__": """ user = customer.users[username] - set_vals = customer.customer_cipher.set_key - user_keys = user.user_keys + set_vals = customer.cipher.set_key + user_keys = user.cipher user_mask = user.enciphered_passcode.mask decoded_mask = user_keys.decode_base64_str(user_mask) deciphered_mask = xor_lists(decoded_mask, user_keys.mask_key) @@ -148,7 +148,7 @@ if __name__ == "__main__": GET PRESUMED ATTRIBUTES """ - set_vals_idx = [customer.customer_cipher.get_set_index(set_val) for set_val in login_passcode_sets] + set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in login_passcode_sets] presumed_selected_attributes_idx = [] for idx in range(passcode_len): @@ -161,11 +161,11 @@ if __name__ == "__main__": RENEW KEYS """ - old_attrs = customer.customer_cipher.prop_key.copy() - old_sets = customer.customer_cipher.set_key.copy() - customer.customer_cipher.renew() - new_attrs = customer.customer_cipher.prop_key - new_sets = customer.customer_cipher.set_key + old_attrs = customer.cipher.prop_key.copy() + old_sets = customer.cipher.set_key.copy() + customer.cipher.renew() + new_attrs = customer.cipher.prop_key + new_sets = customer.cipher.set_key customer_new_attr_view = list_to_matrix(new_attrs, keypad_size.props_per_key) """ @@ -175,18 +175,18 @@ if __name__ == "__main__": sets_xor = xor_lists(new_sets, old_sets) for user in customer.users.values(): user.renew = True - user.user_keys.set_key = xor_lists(user.user_keys.set_key, sets_xor) - user.user_keys.prop_key = xor_lists(user.user_keys.prop_key, attrs_xor) + user.cipher.set_key = xor_lists(user.cipher.set_key, sets_xor) + user.cipher.prop_key = xor_lists(user.cipher.prop_key, attrs_xor) """ REFRESH USER KEYS """ - user.user_keys = UserCipher.create( - customer.customer_cipher.keypad_size, - customer.customer_cipher.set_key, - user.user_keys.max_nkode_len + user.cipher = UserCipher.create( + customer.cipher.keypad_size, + customer.cipher.set_key, + user.cipher.max_nkode_len ) - user.enciphered_passcode = user.user_keys.encipher_nkode(presumed_selected_attributes_idx, customer.customer_cipher) + user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_attributes_idx, customer.cipher) user.renew = False # Define some data to pass to the template @@ -202,7 +202,7 @@ if __name__ == "__main__": 'server_side_attr': server_side_attr, 'confirm_keypad': confirm_keypad, 'selected_keys_confirm': selected_keys_confirm, - 'user_keys': user_keys, + 'cipher': user_keys, 'passcode_server_attr': passcode_server_attr, 'passcode_server_set': passcode_server_set, 'enciphered_nkode': enciphered_nkode, diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index e2d9abc..dea0a83 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -12,12 +12,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.154404Z", - "start_time": "2025-03-07T19:01:08.149432Z" + "end_time": "2025-03-10T15:54:01.727846Z", + "start_time": "2025-03-10T15:54:01.637744Z" } }, "outputs": [], - "execution_count": 58 + "execution_count": 2 }, { "cell_type": "code", @@ -38,12 +38,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.165511Z", - "start_time": "2025-03-07T19:01:08.162339Z" + "end_time": "2025-03-10T15:54:01.735895Z", + "start_time": "2025-03-10T15:54:01.733121Z" } }, "outputs": [], - "execution_count": 59 + "execution_count": 3 }, { "cell_type": "code", @@ -53,12 +53,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.174320Z", - "start_time": "2025-03-07T19:01:08.172724Z" + "end_time": "2025-03-10T15:54:01.746332Z", + "start_time": "2025-03-10T15:54:01.744308Z" } }, "outputs": [], - "execution_count": 60 + "execution_count": 4 }, { "cell_type": "markdown", @@ -90,7 +90,7 @@ ")\n", "keypad_size = KeypadSize(\n", " numb_of_keys = 5,\n", - " attrs_per_key = 6 # aka number of sets\n", + " props_per_key = 6 # aka number of sets\n", ")\n", "customer_id = api.create_new_customer(keypad_size, policy)\n", "customer = api.customers[customer_id]" @@ -98,12 +98,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.378578Z", - "start_time": "2025-03-07T19:01:08.188114Z" + "end_time": "2025-03-10T15:54:02.001142Z", + "start_time": "2025-03-10T15:54:01.752948Z" } }, "outputs": [], - "execution_count": 61 + "execution_count": 5 }, { "cell_type": "markdown", @@ -129,19 +129,19 @@ { "cell_type": "code", "source": [ - "set_vals = customer.attributes.set_vals\n", - "attr_vals = customer.attributes.attr_vals\n", + "set_vals = customer.cipher.set_key\n", + "attr_vals = customer.cipher.prop_key\n", "print(f\"Customer Sets: {set_vals}\")\n", "print(f\"Customer Attributes:\")\n", - "interface_keypad = list_to_matrix(attr_vals, keypad_size.attrs_per_key)\n", + "interface_keypad = list_to_matrix(attr_vals, keypad_size.props_per_key)\n", "for idx, key_vals in enumerate(interface_keypad):\n", " print(f\"{key_vals}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.387613Z", - "start_time": "2025-03-07T19:01:08.385501Z" + "end_time": "2025-03-10T15:54:02.011259Z", + "start_time": "2025-03-10T15:54:02.008541Z" } }, "outputs": [ @@ -149,17 +149,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Sets: [14533, 13441, 52408, 39610, 49828, 11300]\n", + "Customer Sets: [5584, 23615, 13665, 42236, 20931, 35557]\n", "Customer Attributes:\n", - "[50415, 3350, 62907, 34493, 46982, 55292]\n", - "[23503, 57678, 17001, 8963, 4893, 53685]\n", - "[25912, 9324, 2770, 57761, 57056, 5837]\n", - "[49831, 16518, 53473, 35853, 12433, 20763]\n", - "[60930, 19614, 2083, 2879, 58781, 13705]\n" + "[51116, 4647, 54248, 42959, 35151, 56238]\n", + "[37806, 51776, 33630, 63761, 13028, 29812]\n", + "[41783, 30499, 23526, 21846, 1217, 40587]\n", + "[20418, 53142, 62008, 29738, 64343, 49564]\n", + "[4306, 19073, 56680, 38208, 21317, 14264]\n" ] } ], - "execution_count": 62 + "execution_count": 6 }, { "cell_type": "markdown", @@ -173,7 +173,7 @@ { "cell_type": "code", "source": [ - "attr_keypad_view = list_to_matrix(attr_vals, keypad_size.attrs_per_key)\n", + "attr_keypad_view = list_to_matrix(attr_vals, keypad_size.props_per_key)\n", "attr_set_view = matrix_transpose(attr_keypad_view)\n", "set_attribute_dict = dict(zip(set_vals, attr_set_view))\n", "print(f\"Set to Attribute Map:\")\n", @@ -183,8 +183,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.412394Z", - "start_time": "2025-03-07T19:01:08.410114Z" + "end_time": "2025-03-10T15:54:16.867336Z", + "start_time": "2025-03-10T15:54:16.864589Z" } }, "outputs": [ @@ -193,16 +193,16 @@ "output_type": "stream", "text": [ "Set to Attribute Map:\n", - "14533: [50415, 23503, 25912, 49831, 60930]\n", - "13441: [3350, 57678, 9324, 16518, 19614]\n", - "52408: [62907, 17001, 2770, 53473, 2083]\n", - "39610: [34493, 8963, 57761, 35853, 2879]\n", - "49828: [46982, 4893, 57056, 12433, 58781]\n", - "11300: [55292, 53685, 5837, 20763, 13705]\n" + "5584: [51116, 37806, 41783, 20418, 4306]\n", + "23615: [4647, 51776, 30499, 53142, 19073]\n", + "13665: [54248, 33630, 23526, 62008, 56680]\n", + "42236: [42959, 63761, 21846, 29738, 38208]\n", + "20931: [35151, 13028, 1217, 64343, 21317]\n", + "35557: [56238, 29812, 40587, 49564, 14264]\n" ] } ], - "execution_count": 63 + "execution_count": 8 }, { "cell_type": "markdown", @@ -232,27 +232,24 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.439639Z", - "start_time": "2025-03-07T19:01:08.436629Z" + "end_time": "2025-03-10T15:54:20.741427Z", + "start_time": "2025-03-10T15:54:20.731719Z" } }, "outputs": [ { - "data": { - "text/plain": [ - "[[3, 20, 10, 6, 29],\n", - " [27, 26, 4, 24, 11],\n", - " [15, 2, 22, 0, 5],\n", - " [9, 8, 16, 12, 23],\n", - " [21, 14, 28, 18, 17]]" - ] - }, - "execution_count": 64, - "metadata": {}, - "output_type": "execute_result" + "ename": "AttributeError", + "evalue": "'NKodeAPI' object has no attribute 'generate_signup_interface'", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[9], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m session_id, signup_interface \u001B[38;5;241m=\u001B[39m \u001B[43mapi\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mgenerate_signup_interface\u001B[49m(customer_id)\n\u001B[1;32m 2\u001B[0m list_to_matrix(signup_interface, keypad_size\u001B[38;5;241m.\u001B[39mnumb_of_keys)\n", + "\u001B[0;31mAttributeError\u001B[0m: 'NKodeAPI' object has no attribute 'generate_signup_interface'" + ] } ], - "execution_count": 64 + "execution_count": 9 }, { "cell_type": "markdown", @@ -280,7 +277,7 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.474579Z", + "end_time": "2025-03-10T15:54:02.091787Z", "start_time": "2025-03-07T19:01:08.471813Z" } }, @@ -320,7 +317,7 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:08.496620Z", + "end_time": "2025-03-10T15:54:02.111894Z", "start_time": "2025-03-07T19:01:08.494090Z" } }, @@ -352,7 +349,7 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.173784Z", + "end_time": "2025-03-10T15:54:02.115281Z", "start_time": "2025-03-07T19:01:08.522178Z" } }, @@ -384,30 +381,31 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.193871Z", - "start_time": "2025-03-07T19:01:09.190856Z" + "end_time": "2025-03-10T15:54:02.129677Z", + "start_time": "2025-03-10T15:41:40.490919Z" } }, "cell_type": "code", "source": [ - "from src.user_cipher_keys import UserCipherKeys\n", + "from src.user_cipher import UserCipher\n", "from src.utils import xor_lists\n", + "import numpy as np\n", "\n", "\n", "set_key = [46785, 4782, 4405, 44408, 35377, 55527]\n", "set_key = xor_lists(set_key, customer.attributes.set_vals)\n", - "user_keys = UserCipherKeys(\n", - " alpha_key = [\n", + "user_keys = UserCipher(\n", + " prop_key = np.array([\n", " 57200, 8398, 54694, 25997, 30388,\n", " 46948, 45549, 30364, 49712, 10447,\n", " 9205, 1777, 10731, 30979, 2795,\n", " 17068, 56758, 62574, 28641, 11451,\n", " 26820, 50373, 48783, 25350, 62177,\n", " 60608, 54242, 4637, 3525, 16313\n", - " ],\n", - " pass_key=[16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206],\n", - " mask_key=[29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325],\n", - " set_key=set_key,\n", + " ]),\n", + " pass_key=np.array([16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206]),\n", + " mask_key=np.array([29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325]),\n", + " set_key=np.array(set_key),\n", " salt=b'$2b$12$fX.in.GGAjz3QBBwqSWc6e',\n", " max_nkode_len=customer.nkode_policy.max_nkode_len, \n", ")\n", @@ -419,15 +417,18 @@ ], "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "Passcode Set Vals: [39610, 52408, 49828, 14533]\n", - "Passcode Attr Vals: [34493, 53473, 4893, 23503]\n" + "ename": "NameError", + "evalue": "name 'customer' is not defined", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[5], line 6\u001B[0m\n\u001B[1;32m 2\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01msrc\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mutils\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m xor_lists\n\u001B[1;32m 5\u001B[0m set_key \u001B[38;5;241m=\u001B[39m [\u001B[38;5;241m46785\u001B[39m, \u001B[38;5;241m4782\u001B[39m, \u001B[38;5;241m4405\u001B[39m, \u001B[38;5;241m44408\u001B[39m, \u001B[38;5;241m35377\u001B[39m, \u001B[38;5;241m55527\u001B[39m]\n\u001B[0;32m----> 6\u001B[0m set_key \u001B[38;5;241m=\u001B[39m xor_lists(set_key, \u001B[43mcustomer\u001B[49m\u001B[38;5;241m.\u001B[39mattributes\u001B[38;5;241m.\u001B[39mset_vals)\n\u001B[1;32m 7\u001B[0m user_keys \u001B[38;5;241m=\u001B[39m UserCipher(\n\u001B[1;32m 8\u001B[0m prop_key \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 9\u001B[0m \u001B[38;5;241m57200\u001B[39m, \u001B[38;5;241m8398\u001B[39m, \u001B[38;5;241m54694\u001B[39m, \u001B[38;5;241m25997\u001B[39m, \u001B[38;5;241m30388\u001B[39m,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 20\u001B[0m max_nkode_len\u001B[38;5;241m=\u001B[39mcustomer\u001B[38;5;241m.\u001B[39mnkode_policy\u001B[38;5;241m.\u001B[39mmax_nkode_len, \n\u001B[1;32m 21\u001B[0m )\n\u001B[1;32m 23\u001B[0m passcode_server_attr \u001B[38;5;241m=\u001B[39m [customer\u001B[38;5;241m.\u001B[39mattributes\u001B[38;5;241m.\u001B[39mattr_vals[idx] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m user_passcode]\n", + "\u001B[0;31mNameError\u001B[0m: name 'customer' is not defined" ] } ], - "execution_count": 68 + "execution_count": 5 }, { "metadata": {}, @@ -449,24 +450,36 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.223843Z", - "start_time": "2025-03-07T19:01:09.221595Z" + "end_time": "2025-03-10T15:54:02.130699Z", + "start_time": "2025-03-10T15:45:03.947109Z" } }, "cell_type": "code", "source": [ "from src.utils import xor_lists\n", "\n", - "padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.attributes.set_vals)\n", + "padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.properites.set_vals)\n", "\n", - "set_idx = [customer.attributes.get_set_index(set_val) for set_val in padded_passcode_server_set]\n", + "set_idx = [customer.properites.get_set_index(set_val) for set_val in padded_passcode_server_set]\n", "mask_set_keys = [user_keys.set_key[idx] for idx in set_idx]\n", "ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set)\n", "ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key)\n", "mask = user_keys.encode_base64_str(ciphered_mask)" ], - "outputs": [], - "execution_count": 69 + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'user_keys' is not defined", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[5], line 3\u001B[0m\n\u001B[1;32m 1\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01msrc\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mutils\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m xor_lists\n\u001B[0;32m----> 3\u001B[0m padded_passcode_server_set \u001B[38;5;241m=\u001B[39m \u001B[43muser_keys\u001B[49m\u001B[38;5;241m.\u001B[39mpad_user_mask(passcode_server_set, customer\u001B[38;5;241m.\u001B[39mproperites\u001B[38;5;241m.\u001B[39mset_vals)\n\u001B[1;32m 5\u001B[0m set_idx \u001B[38;5;241m=\u001B[39m [customer\u001B[38;5;241m.\u001B[39mproperites\u001B[38;5;241m.\u001B[39mget_set_index(set_val) \u001B[38;5;28;01mfor\u001B[39;00m set_val \u001B[38;5;129;01min\u001B[39;00m padded_passcode_server_set]\n\u001B[1;32m 6\u001B[0m mask_set_keys \u001B[38;5;241m=\u001B[39m [user_keys\u001B[38;5;241m.\u001B[39mset_key[idx] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m set_idx]\n", + "\u001B[0;31mNameError\u001B[0m: name 'user_keys' is not defined" + ] + } + ], + "execution_count": 5 }, { "metadata": {}, @@ -483,7 +496,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.479816Z", + "end_time": "2025-03-10T15:54:02.130948Z", "start_time": "2025-03-07T19:01:09.236822Z" } }, @@ -494,7 +507,7 @@ "import base64\n", "from src.utils import int_array_to_bytes\n", "\n", - "ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.alpha_key)\n", + "ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.prop_key)\n", "passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", "\n", @@ -513,7 +526,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.495420Z", + "end_time": "2025-03-10T15:54:02.141842Z", "start_time": "2025-03-07T19:01:09.488943Z" } }, @@ -553,7 +566,7 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.736403Z", + "end_time": "2025-03-10T15:54:02.143871Z", "start_time": "2025-03-07T19:01:09.500580Z" } }, @@ -605,7 +618,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.774425Z", + "end_time": "2025-03-10T15:54:02.144830Z", "start_time": "2025-03-07T19:01:09.772200Z" } }, @@ -613,7 +626,7 @@ "source": [ "user = customer.users[username]\n", "set_vals = customer.attributes.set_vals\n", - "user_keys = user.user_keys\n", + "user_keys = user.cipher\n", "user_mask = user.enciphered_passcode.mask\n", "decoded_mask = user_keys.decode_base64_str(user_mask)\n", "deciphered_mask = xor_lists(decoded_mask, user_keys.mask_key)\n", @@ -643,7 +656,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:09.795190Z", + "end_time": "2025-03-10T15:54:02.145423Z", "start_time": "2025-03-07T19:01:09.792579Z" } }, @@ -679,7 +692,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:10.057899Z", + "end_time": "2025-03-10T15:54:02.145859Z", "start_time": "2025-03-07T19:01:09.819860Z" } }, @@ -716,7 +729,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:11.147274Z", + "end_time": "2025-03-10T15:54:02.146298Z", "start_time": "2025-03-07T19:01:10.066784Z" } }, @@ -763,7 +776,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:11.355505Z", + "end_time": "2025-03-10T15:54:02.148090Z", "start_time": "2025-03-07T19:01:11.153001Z" } }, @@ -789,7 +802,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:11.363643Z", + "end_time": "2025-03-10T15:54:02.154941Z", "start_time": "2025-03-07T19:01:11.361690Z" } }, @@ -799,8 +812,8 @@ "sets_xor = xor_lists(new_sets, old_sets)\n", "for user in customer.users.values():\n", " user.renew = True\n", - " user.user_keys.set_key = xor_lists(user.user_keys.set_key, sets_xor)\n", - " user.user_keys.alpha_key = xor_lists(user.user_keys.alpha_key, attrs_xor)" + " user.cipher.set_key = xor_lists(user.cipher.set_key, sets_xor)\n", + " user.cipher.prop_key = xor_lists(user.cipher.prop_key, attrs_xor)" ], "outputs": [], "execution_count": 78 @@ -813,18 +826,18 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-07T19:01:11.989265Z", + "end_time": "2025-03-10T15:54:02.155673Z", "start_time": "2025-03-07T19:01:11.369911Z" } }, "cell_type": "code", "source": [ - "user.user_keys = UserCipherKeys.new(\n", + "user.cipher = UserCipher.create(\n", " customer.attributes.keypad_size,\n", " customer.attributes.set_vals,\n", - " user.user_keys.max_nkode_len\n", + " user.cipher.max_nkode_len\n", ")\n", - "user.enciphered_passcode = user.user_keys.encipher_nkode(presumed_selected_attributes_idx, customer.attributes)\n", + "user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_attributes_idx, customer.attributes)\n", "user.renew = False" ], "outputs": [], diff --git a/src/customer.py b/src/customer.py index 613a195..7531dd4 100644 --- a/src/customer.py +++ b/src/customer.py @@ -9,7 +9,7 @@ from src.utils import xor_lists class Customer: customer_id: UUID nkode_policy: NKodePolicy - customer_cipher: CustomerCipher + cipher: CustomerCipher users: dict[str, User] # TODO: validate policy and keypad size don't conflict @@ -23,16 +23,16 @@ class Customer: if username not in self.users: raise ValueError(f"User '{username}' does not exist") - numb_of_keys = self.customer_cipher.keypad_size.numb_of_keys + numb_of_keys = self.cipher.keypad_size.numb_of_keys if not all(0 <= key_idx < numb_of_keys for key_idx in selected_keys): raise ValueError(f"Invalid key indices. Must be between 0 and {numb_of_keys - 1}") passcode_len = len(selected_keys) user = self.users[username] - passcode_set_vals = user.user_keys.decipher_mask( - user.enciphered_passcode.mask, self.customer_cipher.set_key, passcode_len) - set_vals_idx = [self.customer_cipher.get_set_index(set_val) for set_val in passcode_set_vals] + passcode_set_vals = user.cipher.decipher_mask( + user.enciphered_passcode.mask, self.cipher.set_key, passcode_len) + set_vals_idx = [self.cipher.get_set_index(set_val) for set_val in passcode_set_vals] presumed_selected_attributes_idx = [] for idx in range(passcode_len): @@ -41,20 +41,20 @@ class Customer: selected_attr_idx = user.user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) presumed_selected_attributes_idx.append(selected_attr_idx) - enciphered_attr = user.user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, self.customer_cipher) + enciphered_attr = user.cipher.encipher_salt_hash_code(presumed_selected_attributes_idx, self.cipher) if enciphered_attr != user.enciphered_passcode.code: return False if user.renew: - user.refresh_passcode(presumed_selected_attributes_idx, self.customer_cipher) + user.refresh_passcode(presumed_selected_attributes_idx, self.cipher) return True def renew_keys(self) -> bool: - old_attrs = self.customer_cipher.prop_key.copy() - old_sets = self.customer_cipher.set_key.copy() - self.customer_cipher.renew() - new_attrs = self.customer_cipher.prop_key - new_sets = self.customer_cipher.set_key + old_attrs = self.cipher.prop_key.copy() + old_sets = self.cipher.set_key.copy() + self.cipher.renew() + new_attrs = self.cipher.prop_key + new_sets = self.cipher.set_key attrs_xor = xor_lists(new_attrs, old_attrs) set_xor = xor_lists(new_sets, old_sets) @@ -66,7 +66,7 @@ class Customer: def valid_new_nkode(self, passcode_attr_idx: list[int]) -> bool: nkode_len = len(passcode_attr_idx) passcode_set_values = [ - self.customer_cipher.get_prop_set_val(self.customer_cipher.prop_key[attr_idx]) for attr_idx in passcode_attr_idx + self.cipher.get_prop_set_val(self.cipher.prop_key[attr_idx]) for attr_idx in passcode_attr_idx ] distinct_sets = len(set(passcode_set_values)) distinct_attributes = len(set(passcode_attr_idx)) diff --git a/src/nkode_api.py b/src/nkode_api.py index d81e329..bf33629 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -1,7 +1,6 @@ from dataclasses import dataclass, field from uuid import UUID, uuid4 from typing import Dict, List, Tuple - from src.customer import Customer from src.models import NKodePolicy, KeypadSize from src.user import User @@ -19,7 +18,7 @@ class NKodeAPI: def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( customer_id=uuid4(), - customer_cipher=CustomerCipher.create(keypad_size), + cipher=CustomerCipher.create(keypad_size), users={}, nkode_policy=nkode_policy ) @@ -30,7 +29,7 @@ class NKodeAPI: if customer_id not in self.customers.keys(): raise ValueError(f"Customer with ID '{customer_id}' does not exist") customer = self.customers[customer_id] - login_keypad = UserKeypad.create(customer.customer_cipher.keypad_size) + login_keypad = UserKeypad.create(customer.cipher.keypad_size) set_keypad = login_keypad.sign_up_keypad() new_session = UserSignupSession( session_id=uuid4(), @@ -76,15 +75,15 @@ class NKodeAPI: customer = self.customers[customer_id] passcode = self.signup_sessions[session_id].deduce_passcode(confirm_key_entry) new_user_keys = UserCipher.create( - customer.customer_cipher.keypad_size, - customer.customer_cipher.set_key, + customer.cipher.keypad_size, + customer.cipher.set_key, customer.nkode_policy.max_nkode_len ) - enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.customer_cipher) + enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.cipher) new_user = User( username=username, enciphered_passcode=enciphered_passcode, - user_keys=new_user_keys, + cipher=new_user_keys, user_keypad=self.signup_sessions[session_id].login_keypad, ) self.customers[customer_id].add_new_user(new_user) diff --git a/src/user.py b/src/user.py index 9c0e319..e30a4c7 100644 --- a/src/user.py +++ b/src/user.py @@ -10,20 +10,20 @@ from src.utils import xor_lists class User: username: str enciphered_passcode: EncipheredNKode - user_keys: UserCipher + cipher: UserCipher user_keypad: UserKeypad renew: bool = field(default=False) - def renew_keys(self, sets_xor: list[int], attrs_xor: list[int]): + def renew_keys(self, set_xor: list[int], prop_xor: list[int]): self.renew = True - self.user_keys.set_key = xor_lists(self.user_keys.set_key, sets_xor) - self.user_keys.prop_key = xor_lists(self.user_keys.prop_key, attrs_xor) + self.cipher.set_key = xor_lists(self.cipher.set_key, set_xor) + self.cipher.prop_key = xor_lists(self.cipher.prop_key, prop_xor) def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerCipher): - self.user_keys = UserCipher.create( + self.cipher = UserCipher.create( customer_attributes.keypad_size, customer_attributes.set_key, - self.user_keys.max_nkode_len + self.cipher.max_nkode_len ) - self.enciphered_passcode = self.user_keys.encipher_nkode(passcode_attr_idx, customer_attributes) + self.enciphered_passcode = self.cipher.encipher_nkode(passcode_attr_idx, customer_attributes) self.renew = False diff --git a/src/user_cipher.py b/src/user_cipher.py index c69177e..724e16c 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -2,17 +2,18 @@ import base64 import hashlib from dataclasses import dataclass import bcrypt +import numpy as np from secrets import choice from src.models import EncipheredNKode, KeypadSize from src.customer_cipher import CustomerCipher -from src.utils import generate_random_nonrepeating_list, xor_lists, int_array_to_bytes + @dataclass class UserCipher: - prop_key: list[int] - set_key: list[int] - pass_key: list[int] - mask_key: list[int] + prop_key: np.ndarray + set_key: np.ndarray + pass_key: np.ndarray + mask_key: np.ndarray salt: bytes max_nkode_len: int @@ -21,41 +22,50 @@ class UserCipher: if len(set_values) != keypad_size.props_per_key: raise ValueError("Invalid set values") - set_key = generate_random_nonrepeating_list(keypad_size.props_per_key) - set_key = xor_lists(set_key, set_values) + set_values_array = np.array(set_values, dtype=np.uint16) + set_key = generate_random_nonrepeating_array(keypad_size.props_per_key) + set_key = np.bitwise_xor(set_key, set_values_array) return UserCipher( - prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys), - pass_key=generate_random_nonrepeating_list(max_nkode_len), - mask_key=generate_random_nonrepeating_list(max_nkode_len), + prop_key=generate_random_nonrepeating_array(keypad_size.props_per_key * keypad_size.numb_of_keys), + pass_key=generate_random_nonrepeating_array(max_nkode_len), + mask_key=generate_random_nonrepeating_array(max_nkode_len), set_key=set_key, salt=bcrypt.gensalt(), max_nkode_len=max_nkode_len ) - def pad_user_mask(self, user_mask: list[int], set_vals: list[int]) -> list[int]: + def pad_user_mask(self, user_mask: list[int], set_vals: list[int]) -> np.ndarray: if len(user_mask) >= self.max_nkode_len: raise ValueError("User mask is too long") - padded_user_mask = user_mask.copy() - for _ in range(self.max_nkode_len - len(user_mask)): - padded_user_mask.append(choice(set_vals)) + + user_mask_array = np.array(user_mask, dtype=np.uint16) + set_vals_array = np.array(set_vals, dtype=np.uint16) + + # Create padding of random choices from set_vals + padding_size = self.max_nkode_len - len(user_mask) + padding_indices = np.random.choice(len(set_vals), padding_size) + padding = np.array([set_vals[i] for i in padding_indices], dtype=np.uint16) + + # Concatenate original mask with padding + padded_user_mask = np.concatenate([user_mask_array, padding]) return padded_user_mask @staticmethod - def encode_base64_str(data: list[int]) -> str: + def encode_base64_str(data: np.ndarray) -> str: return base64.b64encode(int_array_to_bytes(data)).decode("utf-8") @staticmethod - def decode_base64_str(data: str) -> list[int]: + def decode_base64_str(data: str) -> np.ndarray: byte_data = base64.b64decode(data) int_list = [] for i in range(0, len(byte_data), 2): int_val = int.from_bytes(byte_data[i:i + 2], byteorder='big') int_list.append(int_val) - return int_list + return np.array(int_list, dtype=np.uint16) - def _hash_passcode(self, passcode: list[int]) -> str: + def _hash_passcode(self, passcode: np.ndarray) -> str: passcode_bytes = int_array_to_bytes(passcode) passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) hashed_data = bcrypt.hashpw(passcode_digest, self.salt) @@ -66,10 +76,10 @@ class UserCipher: passcode_prop_idx: list[int], customer_cipher: CustomerCipher ) -> EncipheredNKode: - - passcode_attrs = [customer_cipher.prop_key[idx] for idx in passcode_prop_idx] - passcode_sets = [customer_cipher.get_prop_set_val(attr) for attr in passcode_attrs] - mask = self.encipher_mask(passcode_sets, customer_cipher) + passcode_prop_idx_array = np.array(passcode_prop_idx, dtype=np.uint16) + passcode_attrs = np.array([customer_cipher.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) + passcode_sets = np.array([customer_cipher.get_prop_set_val(attr) for attr in passcode_attrs], dtype=np.uint16) + mask = self.encipher_mask(passcode_sets.tolist(), customer_cipher) code = self.encipher_salt_hash_code(passcode_prop_idx, customer_cipher) return EncipheredNKode( code=code, @@ -81,12 +91,15 @@ class UserCipher: passcode_prop_idx: list[int], customer_prop: CustomerCipher, ) -> str: - passcode_len = len(passcode_prop_idx) - passcode_attrs = [customer_prop.prop_key[idx] for idx in passcode_prop_idx] + passcode_prop_idx_array = np.array(passcode_prop_idx, dtype=np.uint16) + passcode_len = len(passcode_prop_idx_array) + passcode_attrs = np.array([customer_prop.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) + passcode_cipher = self.pass_key.copy() for idx in range(passcode_len): - attr_idx = passcode_prop_idx[idx] - passcode_cipher[idx] ^= self.prop_key[attr_idx] ^ passcode_attrs[idx] + attr_idx = passcode_prop_idx_array[idx] + passcode_cipher[idx] = passcode_cipher[idx] ^ self.prop_key[attr_idx] ^ passcode_attrs[idx] + return self._hash_passcode(passcode_cipher) def encipher_mask( @@ -95,19 +108,47 @@ class UserCipher: customer_attributes: CustomerCipher ) -> str: padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_attributes.set_key) - set_idx = [customer_attributes.get_set_index(set_val) for set_val in padded_passcode_sets] - mask_set_keys = [self.set_key[idx] for idx in set_idx] - ciphered_mask = xor_lists(mask_set_keys, padded_passcode_sets) - ciphered_mask = xor_lists(ciphered_mask, self.mask_key) + + # Get indices of set values + set_idx = np.array([customer_attributes.get_set_index(set_val) for set_val in padded_passcode_sets], + dtype=np.uint16) + mask_set_keys = np.array([self.set_key[idx] for idx in set_idx], dtype=np.uint16) + + # XOR operations + ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_sets) + ciphered_mask = np.bitwise_xor(ciphered_mask, self.mask_key) + mask = self.encode_base64_str(ciphered_mask) return mask def decipher_mask(self, mask: str, set_vals: list, passcode_len: int) -> list[int]: + set_vals_array = np.array(set_vals, dtype=np.uint16) decoded_mask = self.decode_base64_str(mask) - deciphered_mask = xor_lists(decoded_mask, self.mask_key) - set_key_rand_component = xor_lists(set_vals, self.set_key) + deciphered_mask = np.bitwise_xor(decoded_mask, self.mask_key) + + set_key_rand_component = np.bitwise_xor(set_vals_array, self.set_key) passcode_sets = [] + for set_cipher in deciphered_mask[:passcode_len]: - set_idx = set_key_rand_component.index(set_cipher) + # Find index where values match + set_idx = np.where(set_key_rand_component == set_cipher)[0][0] passcode_sets.append(set_vals[set_idx]) + return passcode_sets + + +# NumPy utility functions to replace the existing ones +def generate_random_nonrepeating_array(array_len: int, min_val: int = 0, max_val: int = 2 ** 16) -> np.ndarray: + if max_val - min_val < array_len: + raise ValueError("Range of values is less than the array length requested") + + # Generate array of random unique integers + return np.random.choice( + np.arange(min_val, max_val, dtype=np.uint16), + size=array_len, + replace=False + ) + + +def int_array_to_bytes(int_arr: np.ndarray, byte_size: int = 2) -> bytes: + return b"".join([int(num).to_bytes(byte_size, byteorder='big') for num in int_arr]) \ No newline at end of file diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index 0929d65..32fe9c1 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -22,7 +22,7 @@ def test_shuffle_attrs(user_keypad): expected statistical outcomes like: - every attribute gets to every key with a uniform distribution - every attribute is adjacent to every other attribute with uniform distribution - - the order in which the customer_cipher move from key to key is random (i.e. the distance traveled is uniform) + - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) """ pre_shuffle_keypad = user_keypad.keypad user_keypad.partial_keypad_shuffle() -- 2.49.1 From 80cd981ddd7fe5d8c80a642f1ef53ee741b2a2bf Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 12 Mar 2025 09:20:03 -0500 Subject: [PATCH 16/85] refactor remove pydantic --- src/user_signup_session.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/user_signup_session.py b/src/user_signup_session.py index ae277e5..26ba1f5 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -1,20 +1,20 @@ +from dataclasses import dataclass from uuid import UUID - -from pydantic import BaseModel - +import numpy as np from src.user_keypad import UserKeypad from src.models import KeypadSize +from typing import Optional - -class UserSignupSession(BaseModel): +@dataclass +class UserSignupSession: session_id: UUID customer_id: UUID login_keypad: UserKeypad keypad_size: KeypadSize - set_keypad: list[int] | None = None - confirm_keypad: list[int] | None = None - set_key_entry: list[int] | None = None - username: str | None = None + 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): -- 2.49.1 From facd9ee318fc080e912ae2c7bd0d394d4de824dd Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 12 Mar 2025 09:20:11 -0500 Subject: [PATCH 17/85] refactor remove pydantic --- requirements.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index f0d555e..fe1c025 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,5 @@ -pydantic -bcrypt +bcrypt~=4.1.3 +numpy~=2.0.0 +jinja2~=3.1.4 +pytest~=8.2.2 +ipython~=8.25.0 \ No newline at end of file -- 2.49.1 From f6bf73118626d62b889faeb95e4e44e92c167a68 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 13 Mar 2025 04:40:45 -0500 Subject: [PATCH 18/85] numpy refactor --- docs/render_markdown.py | 60 +++++++++-------- notebooks/test_book.ipynb | 118 ++++++++++++++++++++++++++++++++++ src/customer.py | 10 +-- src/customer_cipher.py | 29 +++++---- src/nkode_api.py | 19 +++--- src/user.py | 10 +-- src/user_cipher.py | 9 +-- src/user_keypad.py | 92 +++++++++++++------------- src/utils.py | 6 -- test/test_nkode_api.py | 5 +- test/test_user_cipher_keys.py | 11 ++-- test/test_user_keypad.py | 32 ++++----- 12 files changed, 261 insertions(+), 140 deletions(-) create mode 100644 notebooks/test_book.ipynb diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 8d9ebed..47581dd 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -1,15 +1,14 @@ +import numpy as np from jinja2 import Environment, FileSystemLoader import os from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize, EncipheredNKode from src.user_cipher import UserCipher -from src.utils import list_to_matrix, matrix_transpose, xor_lists from secrets import choice from string import ascii_lowercase import bcrypt import hashlib import base64 -from src.utils import int_array_to_bytes def random_username() -> str: @@ -20,10 +19,10 @@ def select_keys_with_passcode_values(user_passcode: list[int], interface: list[i return [interface.index(attr) // attrs_per_key for attr in user_passcode] -def keypad_view(interface: list[int], attrs_per_key: int): +def visualize_keypad(keypad_list: np.ndarray, props_per_key: int): print("Keypad View") - interface_keypad = list_to_matrix(interface, attrs_per_key) - for idx, key_vals in enumerate(interface_keypad): + keypad_mat = keypad_list.reshape(-1, props_per_key) + for idx, key_vals in enumerate(keypad_mat): print(f"Key {idx}: {key_vals}") @@ -65,14 +64,15 @@ if __name__ == "__main__": set_vals = customer.cipher.set_key attr_vals = customer.cipher.prop_key - customer_attr_view = list_to_matrix(attr_vals, keypad_size.props_per_key) + customer_attr_view = attr_vals.reshape(-1, keypad_size.props_per_key) - attr_keypad_view = list_to_matrix(attr_vals, keypad_size.props_per_key) - attr_set_view = matrix_transpose(attr_keypad_view) + attr_keypad_view = attr_vals.reshape(-1, keypad_size.props_per_key) + attr_set_view = attr_keypad_view.T set_attribute_dict = dict(zip(set_vals, attr_set_view)) - session_id, signup_interface = api.generate_signup_interface(customer_id) - signup_keypad = list_to_matrix(signup_interface, keypad_size.numb_of_keys) + session_id, signup_interface = api.generate_signup_keypad(customer_id) + #signup_keypad = list_to_matrix(signup_interface, keypad_size.numb_of_keys) + signup_keypad = signup_interface.reshape(-1, keypad_size.props_per_key) username = random_username() passcode_len = 4 @@ -93,23 +93,22 @@ if __name__ == "__main__": user_keys = customer.users[username].cipher - padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.cipher.set_key) + padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.set_key) set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] mask_set_keys = [user_keys.set_key[idx] for idx in set_idx] - ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) - ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) + ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_server_set) + ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key) mask = user_keys.encode_base64_str(ciphered_mask) - - ciphered_customer_attrs = xor_lists(customer.cipher.prop_key, user_keys.prop_key) + #ciphered_customer_attrs = xor_lists(customer.cipher.prop_key, user_keys.prop_key) + ciphered_customer_attrs = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key) passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len - passcode_ciphered_attrs.extend([0 for _ in range(pad_len)]) - - ciphered_code = xor_lists(passcode_ciphered_attrs, user_keys.pass_key) - - passcode_bytes = int_array_to_bytes(ciphered_code) + #ciphered_code = xor_lists(passcode_ciphered_attrs, user_keys.pass_key) + ciphered_code = np.bitwise_xor(passcode_ciphered_attrs, user_keys.pass_key) + #passcode_bytes = int_array_to_bytes(ciphered_code) + passcode_bytes = ciphered_code.tobytes() passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt) code = hashed_data.decode("utf-8") @@ -121,7 +120,7 @@ if __name__ == "__main__": """ USER LOGIN """ - login_interface = api.get_login_interface(username, customer_id) + login_interface = api.get_login_keypad(username, customer_id) login_keypad = list_to_matrix(login_interface, keypad_size.props_per_key) selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.props_per_key) success = api.login(customer_id, username, selected_keys_login) @@ -137,11 +136,11 @@ if __name__ == "__main__": user_keys = user.cipher user_mask = user.enciphered_passcode.mask decoded_mask = user_keys.decode_base64_str(user_mask) - deciphered_mask = xor_lists(decoded_mask, user_keys.mask_key) - set_key_rand_component = xor_lists(set_vals, user_keys.set_key) + deciphered_mask = np.bitwise_xor(decoded_mask, user_keys.mask_key) + set_key_rand_component = np.bitwise_xor(set_vals, user_keys.set_key) login_passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: - set_idx = set_key_rand_component.index(set_cipher) + set_idx = np.where(set_key_rand_component == set_cipher)[0][0] login_passcode_sets.append(set_vals[set_idx]) """ @@ -154,7 +153,7 @@ if __name__ == "__main__": for idx in range(passcode_len): key_numb = selected_keys_login[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = customer.users[username].user_interface.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) + selected_attr_idx = customer.users[username].user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) presumed_selected_attributes_idx.append(selected_attr_idx) """ @@ -166,17 +165,16 @@ if __name__ == "__main__": customer.cipher.renew() new_attrs = customer.cipher.prop_key new_sets = customer.cipher.set_key - customer_new_attr_view = list_to_matrix(new_attrs, keypad_size.props_per_key) - + customer_new_attr_view = new_attrs.reshape(-1, keypad_size.props_per_key) """ RENEW USER """ - attrs_xor = xor_lists(new_attrs, old_attrs) - sets_xor = xor_lists(new_sets, old_sets) + attrs_xor = np.bitwise_xor(new_attrs, old_attrs) + sets_xor = np.bitwise_xor(new_sets, old_sets) for user in customer.users.values(): user.renew = True - user.cipher.set_key = xor_lists(user.cipher.set_key, sets_xor) - user.cipher.prop_key = xor_lists(user.cipher.prop_key, attrs_xor) + user.cipher.set_key = np.bitwise_xor(user.cipher.set_key, sets_xor) + user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, attrs_xor) """ REFRESH USER KEYS diff --git a/notebooks/test_book.ipynb b/notebooks/test_book.ipynb new file mode 100644 index 0000000..bbc6822 --- /dev/null +++ b/notebooks/test_book.ipynb @@ -0,0 +1,118 @@ +{ + "cells": [ + { + "cell_type": "code", + "id": "initial_id", + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-03-12T14:46:19.236991Z", + "start_time": "2025-03-12T14:46:19.232966Z" + } + }, + "source": [ + "import numpy as np\n", + "keypad_matrix = np.array([[1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9]])" + ], + "outputs": [], + "execution_count": 5 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-12T14:46:19.249251Z", + "start_time": "2025-03-12T14:46:19.244635Z" + } + }, + "cell_type": "code", + "source": [ + "rng = np.random.default_rng()\n", + "\n", + "# Step 1: Get the matrix\n", + "keypad_view = keypad_matrix.copy() # Using copy to simulate self.keypad_matrix()\n", + "print(\"Original keypad_view:\")\n", + "print(keypad_view)\n", + "# Output:\n", + "# [[1 2 3]\n", + "# [4 5 6]\n", + "# [7 8 9]]\n", + "\n", + "# Step 2: Shuffle rows in place\n", + "rng.shuffle(keypad_view, axis=0)\n", + "print(\"After rng.shuffle(keypad_view, axis=0):\")\n", + "print(keypad_view)\n", + "# Output (rows shuffled):\n", + "# [[7 8 9]\n", + "# [1 2 3]\n", + "# [4 5 6]]\n", + "\n", + "# Step 3: Transpose\n", + "set_view = keypad_view.T\n", + "print(\"After set_view = keypad_view.T:\")\n", + "print(set_view)\n", + "# Output (rows become columns):\n", + "# [[7 1 4]\n", + "# [8 2 5]\n", + "# [9 3 6]]\n", + "\n", + "# Step 4: Shuffle each row independently\n", + "set_view = rng.permutation(set_view, axis=1)\n", + "print(\"After rng.permutation(set_view, axis=1):\")\n", + "print(set_view)\n", + "# Output (each row shuffled independently):\n", + "# [[4 1 7]\n", + "# [5 8 2]\n", + "# [3 6 9]]" + ], + "id": "c7db73ce336d9f0a", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original keypad_view:\n", + "[[1 2 3]\n", + " [4 5 6]\n", + " [7 8 9]]\n", + "After rng.shuffle(keypad_view, axis=0):\n", + "[[7 8 9]\n", + " [1 2 3]\n", + " [4 5 6]]\n", + "After set_view = keypad_view.T:\n", + "[[7 1 4]\n", + " [8 2 5]\n", + " [9 3 6]]\n", + "After rng.permutation(set_view, axis=1):\n", + "[[4 7 1]\n", + " [5 8 2]\n", + " [6 9 3]]\n" + ] + } + ], + "execution_count": 6 + } + ], + "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": 5 +} diff --git a/src/customer.py b/src/customer.py index 7531dd4..4fb3662 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,9 +1,9 @@ from dataclasses import dataclass from uuid import UUID +import numpy as np from src.customer_cipher import CustomerCipher from src.models import NKodePolicy from src.user import User -from src.utils import xor_lists @dataclass class Customer: @@ -12,7 +12,7 @@ class Customer: cipher: CustomerCipher users: dict[str, User] - # TODO: validate policy and keypad size don't conflict + # TODO: validate policy and keypad_list size don't conflict def add_new_user(self, user: User): if user.username in self.users: @@ -56,8 +56,8 @@ class Customer: new_attrs = self.cipher.prop_key new_sets = self.cipher.set_key - attrs_xor = xor_lists(new_attrs, old_attrs) - set_xor = xor_lists(new_sets, old_sets) + attrs_xor = np.bitwise_xor(new_attrs, old_attrs) + set_xor = np.bitwise_xor(new_sets, old_sets) for user in self.users.values(): user.renew_keys(set_xor, attrs_xor) self.users[user.username] = user @@ -66,7 +66,7 @@ class Customer: def valid_new_nkode(self, passcode_attr_idx: list[int]) -> bool: nkode_len = len(passcode_attr_idx) passcode_set_values = [ - self.cipher.get_prop_set_val(self.cipher.prop_key[attr_idx]) for attr_idx in passcode_attr_idx + self.cipher.get_prop_set_val(int(self.cipher.prop_key[attr_idx])) for attr_idx in passcode_attr_idx ] distinct_sets = len(set(passcode_set_values)) distinct_attributes = len(set(passcode_attr_idx)) diff --git a/src/customer_cipher.py b/src/customer_cipher.py index f77470b..7a19fbc 100644 --- a/src/customer_cipher.py +++ b/src/customer_cipher.py @@ -1,13 +1,13 @@ +import numpy as np from dataclasses import dataclass from typing import ClassVar from src.models import KeypadSize -from src.utils import generate_random_nonrepeating_list @dataclass class CustomerCipher: - prop_key: list[int] - set_key: list[int] + prop_key: np.ndarray + set_key: np.ndarray keypad_size: KeypadSize MAX_KEYS: ClassVar[int] = 256 MAX_PROP_PER_KEY: ClassVar[int] = 256 @@ -24,23 +24,28 @@ class CustomerCipher: def create(cls, keypad_size: KeypadSize) -> 'CustomerCipher': if keypad_size.numb_of_keys > cls.MAX_KEYS or keypad_size.props_per_key > cls.MAX_PROP_PER_KEY: raise ValueError(f"Keys and properties per key must not exceed {cls.MAX_KEYS}") + + # Using numpy to generate non-repeating random integers + prop_key = np.random.choice(2 ** 16, size=keypad_size.numb_of_props, replace=False) + set_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False) + return cls( - prop_key=generate_random_nonrepeating_list(keypad_size.numb_of_props), - set_key=generate_random_nonrepeating_list(keypad_size.props_per_key), + prop_key=prop_key, + set_key=set_key, keypad_size=keypad_size, ) def renew(self): - self.prop_key = generate_random_nonrepeating_list(self.keypad_size.numb_of_props) - self.set_key = generate_random_nonrepeating_list(self.keypad_size.props_per_key) + self.prop_key = np.random.choice(2 ** 16, size=self.keypad_size.numb_of_props, replace=False) + self.set_key = np.random.choice(2 ** 16, size=self.keypad_size.props_per_key, replace=False) def get_prop_set_val(self, prop: int) -> int: - assert (prop in self.prop_key) - prop_idx = self.prop_key.index(prop) + assert np.isin(prop, self.prop_key) + prop_idx = np.where(self.prop_key == prop)[0][0] set_idx = prop_idx % self.keypad_size.props_per_key - return self.set_key[set_idx] + return int(self.set_key[set_idx]) def get_set_index(self, set_val: int) -> int: - if set_val not in self.set_key: + if not np.isin(set_val, self.set_key): raise ValueError(f"Set value {set_val} not found in set values") - return self.set_key.index(set_val) \ No newline at end of file + return int(np.where(self.set_key == set_val)[0][0]) \ No newline at end of file diff --git a/src/nkode_api.py b/src/nkode_api.py index bf33629..baa7736 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -1,6 +1,5 @@ from dataclasses import dataclass, field from uuid import UUID, uuid4 -from typing import Dict, List, Tuple from src.customer import Customer from src.models import NKodePolicy, KeypadSize from src.user import User @@ -8,12 +7,13 @@ from src.user_cipher import UserCipher from src.user_signup_session import UserSignupSession from src.user_keypad import UserKeypad from src.customer_cipher import CustomerCipher +import numpy as np @dataclass class NKodeAPI: - customers: Dict[UUID, Customer] = field(default_factory=dict) - signup_sessions: Dict[UUID, UserSignupSession] = field(default_factory=dict) + customers: dict[UUID, Customer] = field(default_factory=dict) + signup_sessions: dict[UUID, UserSignupSession] = field(default_factory=dict) def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( @@ -25,7 +25,7 @@ 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, List[int]]: + def generate_signup_keypad(self, customer_id: UUID) -> 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] @@ -45,9 +45,9 @@ class NKodeAPI: self, username: str, customer_id: UUID, - key_selection: List[int], + key_selection: list[int], session_id: UUID - ) -> List[int]: + ) -> np.ndarray: if customer_id not in self.customers.keys(): raise ValueError(f"Customer ID {customer_id} not found") customer = self.customers[customer_id] @@ -62,7 +62,7 @@ class NKodeAPI: self, username: str, customer_id: UUID, - confirm_key_entry: List[int], + confirm_key_entry: list[int], session_id: UUID ) -> bool: if session_id not in self.signup_sessions.keys(): @@ -90,7 +90,7 @@ class NKodeAPI: del self.signup_sessions[session_id] return True - def get_login_keypad(self, username: str, customer_id: UUID) -> List[int]: + def get_login_keypad(self, username: str, customer_id: UUID) -> np.ndarray: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") customer = self.customers[customer_id] @@ -98,9 +98,10 @@ class NKodeAPI: raise ValueError("Username not found") user = customer.users[username] user.user_keypad.partial_keypad_shuffle() + # TODO: implement split_keypad_shuffle() return user.user_keypad.keypad - def login(self, customer_id: UUID, username: str, key_selection: List[int]) -> bool: + def login(self, customer_id: UUID, username: str, key_selection: list[int]) -> bool: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") customer = self.customers[customer_id] diff --git a/src/user.py b/src/user.py index e30a4c7..6fa1bd8 100644 --- a/src/user.py +++ b/src/user.py @@ -1,9 +1,11 @@ from dataclasses import dataclass, field + +import numpy as np + from src.models import EncipheredNKode from src.customer_cipher import CustomerCipher from src.user_cipher import UserCipher from src.user_keypad import UserKeypad -from src.utils import xor_lists @dataclass @@ -14,10 +16,10 @@ class User: user_keypad: UserKeypad renew: bool = field(default=False) - def renew_keys(self, set_xor: list[int], prop_xor: list[int]): + def renew_keys(self, set_xor: np.ndarray, prop_xor: np.ndarray): self.renew = True - self.cipher.set_key = xor_lists(self.cipher.set_key, set_xor) - self.cipher.prop_key = xor_lists(self.cipher.prop_key, prop_xor) + self.cipher.set_key = np.bitwise_xor(self.cipher.set_key, set_xor) + self.cipher.prop_key = np.bitwise_xor(self.cipher.prop_key, prop_xor) def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerCipher): self.cipher = UserCipher.create( diff --git a/src/user_cipher.py b/src/user_cipher.py index 724e16c..0a964ca 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -18,7 +18,7 @@ class UserCipher: max_nkode_len: int @classmethod - def create(cls, keypad_size: KeypadSize, set_values: list[int], max_nkode_len: int) -> 'UserCipher': + def create(cls, keypad_size: KeypadSize, set_values: np.ndarray, max_nkode_len: int) -> 'UserCipher': if len(set_values) != keypad_size.props_per_key: raise ValueError("Invalid set values") @@ -35,18 +35,15 @@ class UserCipher: max_nkode_len=max_nkode_len ) - def pad_user_mask(self, user_mask: list[int], set_vals: list[int]) -> np.ndarray: + def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray: if len(user_mask) >= self.max_nkode_len: raise ValueError("User mask is too long") user_mask_array = np.array(user_mask, dtype=np.uint16) - set_vals_array = np.array(set_vals, dtype=np.uint16) - # Create padding of random choices from set_vals padding_size = self.max_nkode_len - len(user_mask) padding_indices = np.random.choice(len(set_vals), padding_size) padding = np.array([set_vals[i] for i in padding_indices], dtype=np.uint16) - # Concatenate original mask with padding padded_user_mask = np.concatenate([user_mask_array, padding]) return padded_user_mask @@ -121,7 +118,7 @@ class UserCipher: mask = self.encode_base64_str(ciphered_mask) return mask - def decipher_mask(self, mask: str, set_vals: list, passcode_len: int) -> list[int]: + def decipher_mask(self, mask: str, set_vals: np.ndarray, passcode_len: int) -> np.ndarray: set_vals_array = np.array(set_vals, dtype=np.uint16) decoded_mask = self.decode_base64_str(mask) deciphered_mask = np.bitwise_xor(decoded_mask, self.mask_key) diff --git a/src/user_keypad.py b/src/user_keypad.py index bee1749..434ef21 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -1,17 +1,17 @@ from dataclasses import dataclass from secrets import choice +import numpy as np from src.models import KeypadSize -from src.utils import list_to_matrix, secure_fisher_yates_shuffle, matrix_to_list, matrix_transpose @dataclass class UserKeypad: - keypad: list[int] + keypad: np.ndarray keypad_size: KeypadSize @classmethod def create(cls, keypad_size: KeypadSize) -> 'UserKeypad': keypad = UserKeypad( - keypad=list(range(keypad_size.numb_of_props)), + keypad=np.arange(keypad_size.numb_of_props), keypad_size=keypad_size ) keypad.random_keypad_shuffle() @@ -22,71 +22,77 @@ class UserKeypad: raise ValueError("Keypad size is dispersable") self.random_keypad_shuffle() keypad_matrix = self.keypad_matrix() - attr_set_view = matrix_transpose(keypad_matrix) - attr_set_view = secure_fisher_yates_shuffle(attr_set_view) + attr_set_view = keypad_matrix.T + #attr_set_view = secure_fisher_yates_shuffle(attr_set_view) + attr_set_view = np.random.permutation(attr_set_view) attr_set_view = attr_set_view[:self.keypad_size.numb_of_keys] - keypad_matrix = matrix_transpose(attr_set_view) + keypad_matrix = attr_set_view.reshape(-1)#matrix_transpose(attr_set_view) return UserKeypad( - keypad=matrix_to_list(keypad_matrix), + keypad=keypad_matrix.reshape(-1),#matrix_to_list(keypad_matrix), keypad_size=KeypadSize( numb_of_keys=self.keypad_size.numb_of_keys, props_per_key=self.keypad_size.numb_of_keys ) ) - def keypad_matrix(self) -> list[list[int]]: - return list_to_matrix(self.keypad, self.keypad_size.props_per_key) + def keypad_matrix(self) -> np.ndarray: + return self.keypad.reshape(-1,self.keypad_size.props_per_key) def random_keypad_shuffle(self): + rng = np.random.default_rng() keypad_view = self.keypad_matrix() - keypad_view = secure_fisher_yates_shuffle(keypad_view) - set_view = matrix_transpose(keypad_view) - set_view = [secure_fisher_yates_shuffle(attr_set) for attr_set in set_view] - keypad_view = matrix_transpose(set_view) - self.keypad = matrix_to_list(keypad_view) + rng.shuffle(keypad_view, axis=0) + set_view = keypad_view.T + set_view = rng.permutation(set_view, axis=1) + keypad_view = set_view.T + self.keypad = keypad_view.reshape(-1) def disperse_keypad(self): if not self.keypad_size.is_dispersable: raise ValueError("Keypad size is not dispersable") - user_keypad_matrix = list_to_matrix(self.keypad, self.keypad_size.props_per_key) - shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) - - attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[ - :self.keypad_size.props_per_key] + rng = np.random.default_rng() + #user_keypad_matrix = list_to_matrix(self.keypad, self.keypad_size.props_per_key) + user_keypad_matrix = self.keypad_matrix() + #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) + shuffled_keys = rng.permutation(user_keypad_matrix, axis=0) + #attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] + attr_rotation = rng.permutation(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] dispersed_keypad = self.random_attribute_rotation( shuffled_keys, - attr_rotation, + attr_rotation.tolist(), ) - self.keypad = matrix_to_list(dispersed_keypad) + self.keypad = dispersed_keypad.reshape(-1) def partial_keypad_shuffle(self): # TODO: this should be split shuffle - numb_of_selected_sets = self.keypad_size.props_per_key // 2 - # randomly shuffle half the sets. if props_per_key is odd, randomly add one 50% of the time - numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.props_per_key & 1) == 1 else 0 - selected_sets = secure_fisher_yates_shuffle(list(range(self.keypad_size.props_per_key)))[:numb_of_selected_sets] - user_keypad_matrix = self.keypad_matrix() - shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) - keypad_by_sets = [] - for idx, attrs in enumerate(matrix_transpose(shuffled_keys)): - if idx in selected_sets: - keypad_by_sets.append(secure_fisher_yates_shuffle(attrs)) - else: - keypad_by_sets.append(attrs) - self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) + #numb_of_selected_sets = self.keypad_size.props_per_key // 2 + ## randomly shuffle half the sets. if props_per_key is odd, randomly add one 50% of the time + #numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.props_per_key & 1) == 1 else 0 + #selected_sets = secure_fisher_yates_shuffle(list(range(self.keypad_size.props_per_key)))[:numb_of_selected_sets] + #user_keypad_matrix = self.keypad_matrix() + #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) + #keypad_by_sets = [] + #for idx, attrs in enumerate(matrix_transpose(shuffled_keys)): + # if idx in selected_sets: + # keypad_by_sets.append(secure_fisher_yates_shuffle(attrs)) + # else: + # keypad_by_sets.append(attrs) + #self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) + pass @staticmethod def random_attribute_rotation( - user_keypad: list[list[int]], + user_keypad: np.ndarray, attr_rotation: list[int] - ) -> list[list[int]]: - transposed_user_keypad = matrix_transpose(user_keypad) - if len(attr_rotation) != len(transposed_user_keypad): - raise ValueError("attr_rotation must be the same length as the transposed user keypad") - for idx, attr_set in enumerate(transposed_user_keypad): + ) -> np.ndarray: + transposed = user_keypad.T + if len(attr_rotation) != len(transposed): + raise ValueError("attr_rotation must be the same length as the number of attributes") + for idx, attr_set in enumerate(transposed): rotation = attr_rotation[idx] - transposed_user_keypad[idx] = attr_set[rotation:] + attr_set[:rotation] - return matrix_transpose(transposed_user_keypad) + rotation = rotation % len(attr_set) if len(attr_set) > 0 else 0 + transposed[idx] = np.roll(attr_set, rotation) + return transposed.T def attribute_adjacency_graph(self) -> dict[int, set[int]]: user_keypad_keypad = self.keypad_matrix() @@ -103,4 +109,4 @@ class UserKeypad: if not (0 <= set_idx < self.keypad_size.props_per_key): raise ValueError(f"set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_attr_idx = self.keypad_matrix() - return keypad_attr_idx[key_numb][set_idx] + return int(keypad_attr_idx[key_numb][set_idx]) diff --git a/src/utils.py b/src/utils.py index 3a099f2..38c9284 100644 --- a/src/utils.py +++ b/src/utils.py @@ -9,12 +9,6 @@ def secure_fisher_yates_shuffle(arr: list) -> list: return arr -def generate_random_nonrepeating_list(list_len: int, min_val: int = 0, max_val: int = 2 ** 16) -> list[int]: - if max_val - min_val < list_len: - raise ValueError("Range of values is less than the list length requested") - return secure_fisher_yates_shuffle(list(range(min_val, max_val)))[:list_len] - - def xor_lists(l1: list[int], l2: list[int]): if len(l1) != len(l2): raise ValueError("Lists must be of equal length") diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 128880d..ad5d6b0 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -1,3 +1,4 @@ +import numpy as np import pytest from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize @@ -19,7 +20,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): session_id, set_keypad = nkode_api.generate_signup_keypad(customer_id) user_passcode = set_keypad[:passocode_len] - signup_key_selection = lambda keypad: [keypad.index(attr) // keypad_size.numb_of_keys for attr in user_passcode] + signup_key_selection = lambda keypad: [int(np.where(keypad == attr)[0][0]) // keypad_size.numb_of_keys for attr 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) @@ -32,7 +33,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): ) assert successful_confirm - sign_in_key_selection = lambda keypad: [keypad.index(attr) // keypad_size.props_per_key for attr in user_passcode] + sign_in_key_selection = lambda keypad: [int(np.where(keypad ==attr)[0][0]) // keypad_size.props_per_key for attr in user_passcode] login_keypad = nkode_api.get_login_keypad(username, customer_id) login_key_selection = sign_in_key_selection(login_keypad) successful_login = nkode_api.login(customer_id, username, login_key_selection) diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index c5ec6d5..9f82e66 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -1,8 +1,7 @@ +import numpy as np import pytest - from src.models import KeypadSize from src.user_cipher import UserCipher, CustomerCipher -from src.utils import generate_random_nonrepeating_list @pytest.mark.parametrize( @@ -12,7 +11,8 @@ from src.utils import generate_random_nonrepeating_list ] ) def test_encode_decode_base64(passcode_len): - data = generate_random_nonrepeating_list(passcode_len) + #data = generate_random_nonrepeating_list(passcode_len) + data = np.random.choice(2**16, passcode_len, replace=False) encoded = UserCipher.encode_base64_str(data) decoded = UserCipher.decode_base64_str(encoded) assert (len(data) == len(decoded)) @@ -28,9 +28,8 @@ def test_encode_decode_base64(passcode_len): ]) def test_decode_mask(keypad_size, max_nkode_len): customer = CustomerCipher.create(keypad_size) - passcode_entry = generate_random_nonrepeating_list( - keypad_size.numb_of_props, - max_val=keypad_size.numb_of_props)[:4] + #passcode_entry = generate_random_nonrepeating_list(keypad_size.numb_of_props,max_val=keypad_size.numb_of_props)[:4] + passcode_entry = np.random.choice(keypad_size.numb_of_props, 4, replace=False) passcode_values = [customer.prop_key[idx] for idx in passcode_entry] set_vals = customer.set_key user_keys = UserCipher.create(keypad_size, set_vals, max_nkode_len) diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index 32fe9c1..f449a96 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -17,19 +17,19 @@ def test_dispersion(user_keypad): assert (adj_graph.isdisjoint(post_dispersion_graph[attr])) -def test_shuffle_attrs(user_keypad): - """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get - expected statistical outcomes like: - - every attribute gets to every key with a uniform distribution - - every attribute is adjacent to every other attribute with uniform distribution - - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) - """ - pre_shuffle_keypad = user_keypad.keypad - user_keypad.partial_keypad_shuffle() - post_shuffle_keypad = user_keypad.keypad - assert (not all( - post_shuffle_keypad[idx] == pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) - )) - assert (not all( - post_shuffle_keypad[idx] != pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) - )) +#def test_shuffle_attrs(user_keypad): +# """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get +# expected statistical outcomes like: +# - every attribute gets to every key with a uniform distribution +# - every attribute is adjacent to every other attribute with uniform distribution +# - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) +# """ +# pre_shuffle_keypad = user_keypad.keypad +# user_keypad.partial_keypad_shuffle() +# post_shuffle_keypad = user_keypad.keypad +# assert (not all( +# post_shuffle_keypad[idx] == pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) +# )) +# assert (not all( +# post_shuffle_keypad[idx] != pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) +# )) -- 2.49.1 From 05226edd09449d826b2d007af471d8e306930b42 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 13 Mar 2025 10:53:46 -0500 Subject: [PATCH 19/85] remove utils from nkode_tutorial.ipynb --- docs/render_markdown.py | 2 +- notebooks/nkode_tutorial.ipynb | 370 +++++++++++++++------------------ src/user_keypad.py | 1 - 3 files changed, 172 insertions(+), 201 deletions(-) diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 47581dd..b6fa7f3 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -71,7 +71,7 @@ if __name__ == "__main__": set_attribute_dict = dict(zip(set_vals, attr_set_view)) session_id, signup_interface = api.generate_signup_keypad(customer_id) - #signup_keypad = list_to_matrix(signup_interface, keypad_size.numb_of_keys) + #signup_keypad = list_to_matrix(signup_keypad, keypad_size.numb_of_keys) signup_keypad = signup_interface.reshape(-1, keypad_size.props_per_key) username = random_username() diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index dea0a83..39bb6dc 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -5,19 +5,19 @@ "source": [ "from src.nkode_api import NKodeAPI\n", "from src.models import NKodePolicy, KeypadSize\n", - "from src.utils import list_to_matrix, matrix_transpose\n", "from secrets import choice\n", - "from string import ascii_lowercase" + "from string import ascii_lowercase\n", + "import numpy as np" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:01.727846Z", - "start_time": "2025-03-10T15:54:01.637744Z" + "end_time": "2025-03-13T15:40:54.981894Z", + "start_time": "2025-03-13T15:40:54.975972Z" } }, "outputs": [], - "execution_count": 2 + "execution_count": 157 }, { "cell_type": "code", @@ -26,24 +26,26 @@ " return \"test_username\" + \"\".join([choice(ascii_lowercase) for _ in range(6)])\n", "\n", "\n", - "def select_keys_with_passcode_values(user_passcode: list[int], interface: list[int], attrs_per_key: int) -> list[int]:\n", - " return [interface.index(attr) // attrs_per_key for attr in user_passcode]\n", + "def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, attrs_per_key: int) -> list[int]:\n", + " indices = [np.where(keypad == attr)[0][0] for attr in user_passcode]\n", + " return [int(index // attrs_per_key) for index in indices]\n", "\n", - "def keypad_view(interface: list[int], attrs_per_key: int):\n", + "\n", + "def keypad_view(keypad: np.ndarray, attrs_per_key: int):\n", " print(\"Keypad View\")\n", - " interface_keypad = list_to_matrix(interface, attrs_per_key)\n", + " interface_keypad = keypad.reshape(-1, attrs_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-10T15:54:01.735895Z", - "start_time": "2025-03-10T15:54:01.733121Z" + "end_time": "2025-03-13T15:41:02.574902Z", + "start_time": "2025-03-13T15:41:02.566577Z" } }, "outputs": [], - "execution_count": 3 + "execution_count": 159 }, { "cell_type": "code", @@ -53,12 +55,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:01.746332Z", - "start_time": "2025-03-10T15:54:01.744308Z" + "end_time": "2025-03-13T15:41:06.702836Z", + "start_time": "2025-03-13T15:41:06.697810Z" } }, "outputs": [], - "execution_count": 4 + "execution_count": 160 }, { "cell_type": "markdown", @@ -98,12 +100,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.001142Z", - "start_time": "2025-03-10T15:54:01.752948Z" + "end_time": "2025-03-13T15:41:09.768933Z", + "start_time": "2025-03-13T15:41:09.760522Z" } }, "outputs": [], - "execution_count": 5 + "execution_count": 161 }, { "cell_type": "markdown", @@ -133,15 +135,15 @@ "attr_vals = customer.cipher.prop_key\n", "print(f\"Customer Sets: {set_vals}\")\n", "print(f\"Customer Attributes:\")\n", - "interface_keypad = list_to_matrix(attr_vals, keypad_size.props_per_key)\n", - "for idx, key_vals in enumerate(interface_keypad):\n", + "customer_prop_keypad = attr_vals.reshape(-1, keypad_size.props_per_key)\n", + "for idx, key_vals in enumerate(customer_prop_keypad):\n", " print(f\"{key_vals}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.011259Z", - "start_time": "2025-03-10T15:54:02.008541Z" + "end_time": "2025-03-13T15:41:11.949505Z", + "start_time": "2025-03-13T15:41:11.941034Z" } }, "outputs": [ @@ -149,17 +151,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Sets: [5584, 23615, 13665, 42236, 20931, 35557]\n", + "Customer Sets: [25709 8136 44044 46233 29423 25038]\n", "Customer Attributes:\n", - "[51116, 4647, 54248, 42959, 35151, 56238]\n", - "[37806, 51776, 33630, 63761, 13028, 29812]\n", - "[41783, 30499, 23526, 21846, 1217, 40587]\n", - "[20418, 53142, 62008, 29738, 64343, 49564]\n", - "[4306, 19073, 56680, 38208, 21317, 14264]\n" + "[57582 3595 14389 48861 6004 53011]\n", + "[ 2051 49181 8905 9645 63388 60901]\n", + "[ 5952 47243 39367 26329 21337 27385]\n", + "[ 8812 29822 227 12486 4712 16109]\n", + "[28006 29445 47125 35353 35910 6662]\n" ] } ], - "execution_count": 6 + "execution_count": 162 }, { "cell_type": "markdown", @@ -173,9 +175,7 @@ { "cell_type": "code", "source": [ - "attr_keypad_view = list_to_matrix(attr_vals, keypad_size.props_per_key)\n", - "attr_set_view = matrix_transpose(attr_keypad_view)\n", - "set_attribute_dict = dict(zip(set_vals, attr_set_view))\n", + "set_attribute_dict = dict(zip(set_vals, customer_prop_keypad.T))\n", "print(f\"Set to Attribute Map:\")\n", "for set_val, attrs in set_attribute_dict.items():\n", " print(f\"{set_val}: {attrs}\")" @@ -183,8 +183,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:16.867336Z", - "start_time": "2025-03-10T15:54:16.864589Z" + "end_time": "2025-03-13T15:41:14.890303Z", + "start_time": "2025-03-13T15:41:14.883511Z" } }, "outputs": [ @@ -193,16 +193,16 @@ "output_type": "stream", "text": [ "Set to Attribute Map:\n", - "5584: [51116, 37806, 41783, 20418, 4306]\n", - "23615: [4647, 51776, 30499, 53142, 19073]\n", - "13665: [54248, 33630, 23526, 62008, 56680]\n", - "42236: [42959, 63761, 21846, 29738, 38208]\n", - "20931: [35151, 13028, 1217, 64343, 21317]\n", - "35557: [56238, 29812, 40587, 49564, 14264]\n" + "25709: [57582 2051 5952 8812 28006]\n", + "8136: [ 3595 49181 47243 29822 29445]\n", + "44044: [14389 8905 39367 227 47125]\n", + "46233: [48861 9645 26329 12486 35353]\n", + "29423: [ 6004 63388 21337 4712 35910]\n", + "25038: [53011 60901 27385 16109 6662]\n" ] } ], - "execution_count": 8 + "execution_count": 163 }, { "cell_type": "markdown", @@ -226,30 +226,30 @@ { "cell_type": "code", "source": [ - "session_id, signup_interface = api.generate_signup_interface(customer_id)\n", - "list_to_matrix(signup_interface, keypad_size.numb_of_keys)" + "session_id, signup_keypad = api.generate_signup_keypad(customer_id)\n", + "print(signup_keypad.reshape(-1, keypad_size.numb_of_keys))" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:20.741427Z", - "start_time": "2025-03-10T15:54:20.731719Z" + "end_time": "2025-03-13T15:41:19.446496Z", + "start_time": "2025-03-13T15:41:19.440194Z" } }, "outputs": [ { - "ename": "AttributeError", - "evalue": "'NKodeAPI' object has no attribute 'generate_signup_interface'", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mAttributeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[9], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m session_id, signup_interface \u001B[38;5;241m=\u001B[39m \u001B[43mapi\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mgenerate_signup_interface\u001B[49m(customer_id)\n\u001B[1;32m 2\u001B[0m list_to_matrix(signup_interface, keypad_size\u001B[38;5;241m.\u001B[39mnumb_of_keys)\n", - "\u001B[0;31mAttributeError\u001B[0m: 'NKodeAPI' object has no attribute 'generate_signup_interface'" + "name": "stdout", + "output_type": "stream", + "text": [ + "[[21 3 27 15 9]\n", + " [19 1 25 13 7]\n", + " [23 5 29 17 11]\n", + " [22 4 28 16 10]\n", + " [20 2 26 14 8]]\n" ] } ], - "execution_count": 9 + "execution_count": 164 }, { "cell_type": "markdown", @@ -264,21 +264,21 @@ { "cell_type": "code", "source": [ - "keypad_view(signup_interface, keypad_size.numb_of_keys)\n", + "keypad_view(signup_keypad, keypad_size.numb_of_keys)\n", "username = random_username()\n", "passcode_len = 4\n", - "user_passcode = signup_interface[:passcode_len]\n", - "selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys)\n", + "user_passcode = signup_keypad[:passcode_len]\n", + "selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_keypad, keypad_size.numb_of_keys)\n", "print(f\"User Passcode: {user_passcode}\")\n", "print(f\"Selected Keys\\n{selected_keys_set}\")\n", - "server_side_attr = [customer.attributes.attr_vals[idx] for idx in user_passcode]\n", + "server_side_attr = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", "print(f\"User Passcode Server-side Attributes: {server_side_attr}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.091787Z", - "start_time": "2025-03-07T19:01:08.471813Z" + "end_time": "2025-03-13T15:41:22.138365Z", + "start_time": "2025-03-13T15:41:22.130412Z" } }, "outputs": [ @@ -287,24 +287,19 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [3, 20, 10, 6, 29]\n", - "Key 1: [27, 26, 4, 24, 11]\n", - "Key 2: [15, 2, 22, 0, 5]\n", - "Key 3: [9, 8, 16, 12, 23]\n", - "Key 4: [21, 14, 28, 18, 17]\n", - "User Passcode: [3, 20, 10, 6]\n", + "Key 0: [21 3 27 15 9]\n", + "Key 1: [19 1 25 13 7]\n", + "Key 2: [23 5 29 17 11]\n", + "Key 3: [22 4 28 16 10]\n", + "Key 4: [20 2 26 14 8]\n", + "User Passcode: [21 3 27 15]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side Attributes: [34493, 53473, 4893, 23503]\n" + "User Passcode Server-side Attributes: [12486, 48861, 35353, 26329]\n" ] } ], - "execution_count": 65 - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": "" + "execution_count": 165 }, { "cell_type": "code", @@ -317,8 +312,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.111894Z", - "start_time": "2025-03-07T19:01:08.494090Z" + "end_time": "2025-03-13T15:41:25.094831Z", + "start_time": "2025-03-13T15:41:25.087580Z" } }, "outputs": [ @@ -327,17 +322,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [15, 8, 28, 24, 29]\n", - "Key 1: [27, 20, 22, 12, 17]\n", - "Key 2: [9, 14, 4, 6, 5]\n", - "Key 3: [3, 2, 16, 18, 11]\n", - "Key 4: [21, 26, 10, 0, 23]\n", + "Key 0: [22 2 29 15 7]\n", + "Key 1: [23 4 27 13 8]\n", + "Key 2: [21 5 25 14 10]\n", + "Key 3: [19 3 26 16 11]\n", + "Key 4: [20 1 28 17 9]\n", "Selected Keys\n", - "[3, 1, 4, 2]\n" + "[2, 3, 1, 0]\n" ] } ], - "execution_count": 66 + "execution_count": 166 }, { "cell_type": "code", @@ -349,8 +344,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.115281Z", - "start_time": "2025-03-07T19:01:08.522178Z" + "end_time": "2025-03-13T15:43:12.401745Z", + "start_time": "2025-03-13T15:42:59.685701Z" } }, "outputs": [ @@ -362,7 +357,7 @@ ] } ], - "execution_count": 67 + "execution_count": 168 }, { "metadata": {}, @@ -381,19 +376,17 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.129677Z", - "start_time": "2025-03-10T15:41:40.490919Z" + "end_time": "2025-03-13T15:43:26.496044Z", + "start_time": "2025-03-13T15:43:26.484618Z" } }, "cell_type": "code", "source": [ "from src.user_cipher import UserCipher\n", - "from src.utils import xor_lists\n", - "import numpy as np\n", "\n", "\n", - "set_key = [46785, 4782, 4405, 44408, 35377, 55527]\n", - "set_key = xor_lists(set_key, customer.attributes.set_vals)\n", + "set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\n", + "set_key = np.bitwise_xor(set_key, customer.cipher.set_key)\n", "user_keys = UserCipher(\n", " prop_key = np.array([\n", " 57200, 8398, 54694, 25997, 30388,\n", @@ -410,25 +403,22 @@ " max_nkode_len=customer.nkode_policy.max_nkode_len, \n", ")\n", "\n", - "passcode_server_attr = [customer.attributes.attr_vals[idx] for idx in user_passcode]\n", - "passcode_server_set = [customer.attributes.get_attr_set_val(attr) for attr in passcode_server_attr]\n", + "passcode_server_attr = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", + "passcode_server_set = [int(customer.cipher.get_prop_set_val(attr)) for attr in passcode_server_attr]\n", "print(f\"Passcode Set Vals: {passcode_server_set}\")\n", "print(f\"Passcode Attr Vals: {passcode_server_attr}\")" ], "outputs": [ { - "ename": "NameError", - "evalue": "name 'customer' is not defined", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[5], line 6\u001B[0m\n\u001B[1;32m 2\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01msrc\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mutils\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m xor_lists\n\u001B[1;32m 5\u001B[0m set_key \u001B[38;5;241m=\u001B[39m [\u001B[38;5;241m46785\u001B[39m, \u001B[38;5;241m4782\u001B[39m, \u001B[38;5;241m4405\u001B[39m, \u001B[38;5;241m44408\u001B[39m, \u001B[38;5;241m35377\u001B[39m, \u001B[38;5;241m55527\u001B[39m]\n\u001B[0;32m----> 6\u001B[0m set_key \u001B[38;5;241m=\u001B[39m xor_lists(set_key, \u001B[43mcustomer\u001B[49m\u001B[38;5;241m.\u001B[39mattributes\u001B[38;5;241m.\u001B[39mset_vals)\n\u001B[1;32m 7\u001B[0m user_keys \u001B[38;5;241m=\u001B[39m UserCipher(\n\u001B[1;32m 8\u001B[0m prop_key \u001B[38;5;241m=\u001B[39m [\n\u001B[1;32m 9\u001B[0m \u001B[38;5;241m57200\u001B[39m, \u001B[38;5;241m8398\u001B[39m, \u001B[38;5;241m54694\u001B[39m, \u001B[38;5;241m25997\u001B[39m, \u001B[38;5;241m30388\u001B[39m,\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 20\u001B[0m max_nkode_len\u001B[38;5;241m=\u001B[39mcustomer\u001B[38;5;241m.\u001B[39mnkode_policy\u001B[38;5;241m.\u001B[39mmax_nkode_len, \n\u001B[1;32m 21\u001B[0m )\n\u001B[1;32m 23\u001B[0m passcode_server_attr \u001B[38;5;241m=\u001B[39m [customer\u001B[38;5;241m.\u001B[39mattributes\u001B[38;5;241m.\u001B[39mattr_vals[idx] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m user_passcode]\n", - "\u001B[0;31mNameError\u001B[0m: name 'customer' is not defined" + "name": "stdout", + "output_type": "stream", + "text": [ + "Passcode Set Vals: [46233, 46233, 46233, 46233]\n", + "Passcode Attr Vals: [12486, 48861, 35353, 26329]\n" ] } ], - "execution_count": 5 + "execution_count": 169 }, { "metadata": {}, @@ -450,36 +440,22 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.130699Z", - "start_time": "2025-03-10T15:45:03.947109Z" + "end_time": "2025-03-13T15:43:30.557640Z", + "start_time": "2025-03-13T15:43:30.550158Z" } }, "cell_type": "code", "source": [ - "from src.utils import xor_lists\n", + "padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.cipher.set_key)\n", "\n", - "padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.properites.set_vals)\n", - "\n", - "set_idx = [customer.properites.get_set_index(set_val) for set_val in padded_passcode_server_set]\n", + "set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set]\n", "mask_set_keys = [user_keys.set_key[idx] for idx in set_idx]\n", - "ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set)\n", - "ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key)\n", + "ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_server_set)\n", + "ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key)\n", "mask = user_keys.encode_base64_str(ciphered_mask)" ], - "outputs": [ - { - "ename": "NameError", - "evalue": "name 'user_keys' is not defined", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mNameError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[5], line 3\u001B[0m\n\u001B[1;32m 1\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01msrc\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mutils\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m xor_lists\n\u001B[0;32m----> 3\u001B[0m padded_passcode_server_set \u001B[38;5;241m=\u001B[39m \u001B[43muser_keys\u001B[49m\u001B[38;5;241m.\u001B[39mpad_user_mask(passcode_server_set, customer\u001B[38;5;241m.\u001B[39mproperites\u001B[38;5;241m.\u001B[39mset_vals)\n\u001B[1;32m 5\u001B[0m set_idx \u001B[38;5;241m=\u001B[39m [customer\u001B[38;5;241m.\u001B[39mproperites\u001B[38;5;241m.\u001B[39mget_set_index(set_val) \u001B[38;5;28;01mfor\u001B[39;00m set_val \u001B[38;5;129;01min\u001B[39;00m padded_passcode_server_set]\n\u001B[1;32m 6\u001B[0m mask_set_keys \u001B[38;5;241m=\u001B[39m [user_keys\u001B[38;5;241m.\u001B[39mset_key[idx] \u001B[38;5;28;01mfor\u001B[39;00m idx \u001B[38;5;129;01min\u001B[39;00m set_idx]\n", - "\u001B[0;31mNameError\u001B[0m: name 'user_keys' is not defined" - ] - } - ], - "execution_count": 5 + "outputs": [], + "execution_count": 170 }, { "metadata": {}, @@ -496,8 +472,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.130948Z", - "start_time": "2025-03-07T19:01:09.236822Z" + "end_time": "2025-03-13T15:43:33.209403Z", + "start_time": "2025-03-13T15:43:32.898812Z" } }, "cell_type": "code", @@ -505,29 +481,29 @@ "import bcrypt\n", "import hashlib\n", "import base64\n", - "from src.utils import int_array_to_bytes\n", "\n", - "ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.prop_key)\n", + "ciphered_customer_attrs = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", "passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", "\n", "passcode_ciphered_attrs.extend([0 for _ in range(pad_len)])\n", "\n", - "ciphered_code = xor_lists(passcode_ciphered_attrs, user_keys.pass_key)\n", + "ciphered_code = np.bitwise_xor(passcode_ciphered_attrs, user_keys.pass_key)\n", "\n", - "passcode_bytes = int_array_to_bytes(ciphered_code)\n", + "#passcode_bytes = int_array_to_bytes(ciphered_code)\n", + "passcode_bytes = ciphered_code.tobytes()\n", "passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest())\n", "hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt)\n", "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 70 + "execution_count": 171 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.141842Z", - "start_time": "2025-03-07T19:01:09.488943Z" + "end_time": "2025-03-13T15:43:35.234325Z", + "start_time": "2025-03-13T15:43:35.229292Z" } }, "cell_type": "code", @@ -540,7 +516,7 @@ ")" ], "outputs": [], - "execution_count": 71 + "execution_count": 172 }, { "cell_type": "markdown", @@ -556,9 +532,9 @@ { "cell_type": "code", "source": [ - "login_interface = api.get_login_interface(username, customer_id)\n", - "keypad_view(login_interface, keypad_size.attrs_per_key)\n", - "selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.attrs_per_key)\n", + "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(user_passcode, login_keypad, keypad_size.props_per_key)\n", "print(f\"Selected Keys: {selected_keys_login}\")\n", "success = api.login(customer_id, username, selected_keys_login)\n", "print(success)" @@ -566,8 +542,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.143871Z", - "start_time": "2025-03-07T19:01:09.500580Z" + "end_time": "2025-03-13T15:43:55.881744Z", + "start_time": "2025-03-13T15:43:37.544889Z" } }, "outputs": [ @@ -576,17 +552,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [24, 25, 8, 15, 4, 29]\n", - "Key 1: [12, 19, 2, 3, 16, 23]\n", - "Key 2: [18, 7, 26, 21, 28, 5]\n", - "Key 3: [6, 1, 14, 9, 10, 11]\n", - "Key 4: [0, 13, 20, 27, 22, 17]\n", - "Selected Keys: [1, 4, 3, 3]\n", + "Key 0: [18 19 20 21 22 23]\n", + "Key 1: [0 1 2 3 4 5]\n", + "Key 2: [24 25 26 27 28 29]\n", + "Key 3: [12 13 14 15 16 17]\n", + "Key 4: [ 6 7 8 9 10 11]\n", + "Selected Keys: [0, 1, 2, 3]\n", "True\n" ] } ], - "execution_count": 72 + "execution_count": 173 }, { "metadata": {}, @@ -618,23 +594,20 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.144830Z", - "start_time": "2025-03-07T19:01:09.772200Z" + "end_time": "2025-03-13T15:44:12.223540Z", + "start_time": "2025-03-13T15:44:12.215204Z" } }, "cell_type": "code", "source": [ - "user = customer.users[username]\n", - "set_vals = customer.attributes.set_vals\n", - "user_keys = user.cipher\n", - "user_mask = user.enciphered_passcode.mask\n", - "decoded_mask = user_keys.decode_base64_str(user_mask)\n", - "deciphered_mask = xor_lists(decoded_mask, user_keys.mask_key)\n", - "set_key_rand_component = xor_lists(set_vals, user_keys.set_key)\n", + "user = api.customers[customer_id].users[username]\n", + "decoded_mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", + "deciphered_mask = np.bitwise_xor(decoded_mask, user.cipher.mask_key)\n", + "set_key = np.bitwise_xor(customer.cipher.set_key, user.cipher.set_key)\n", "passcode_sets = []\n", "for set_cipher in deciphered_mask[:passcode_len]:\n", - " set_idx = set_key_rand_component.index(set_cipher)\n", - " passcode_sets.append(set_vals[set_idx])\n", + " set_idx = np.where(set_key == set_cipher)[0][0]\n", + " passcode_sets.append(int(customer.cipher.set_key[set_idx]))\n", "print(passcode_sets)" ], "outputs": [ @@ -642,11 +615,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[39610, 52408, 49828, 14533]\n" + "[46233, 46233, 46233, 46233]\n" ] } ], - "execution_count": 73 + "execution_count": 174 }, { "metadata": {}, @@ -656,22 +629,22 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.145423Z", - "start_time": "2025-03-07T19:01:09.792579Z" + "end_time": "2025-03-13T15:44:41.027416Z", + "start_time": "2025-03-13T15:44:15.077074Z" } }, "cell_type": "code", "source": [ - "set_vals_idx = [customer.attributes.get_set_index(set_val) for set_val in passcode_sets]\n", + "set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets]\n", "\n", "presumed_selected_attributes_idx = []\n", "for idx in range(passcode_len):\n", " key_numb = selected_keys_login[idx]\n", " set_idx = set_vals_idx[idx]\n", - " selected_attr_idx = customer.users[username].user_interface.get_attr_idx_by_keynumb_setidx(key_numb, set_idx)\n", + " selected_attr_idx = customer.users[username].user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx)\n", " presumed_selected_attributes_idx.append(selected_attr_idx)\n", "\n", - "print(user_passcode == presumed_selected_attributes_idx)" + "print(user_passcode.tolist() == presumed_selected_attributes_idx)" ], "outputs": [ { @@ -682,7 +655,7 @@ ] } ], - "execution_count": 74 + "execution_count": 175 }, { "metadata": {}, @@ -692,14 +665,13 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.145859Z", - "start_time": "2025-03-07T19:01:09.819860Z" + "end_time": "2025-03-13T15:48:46.158380Z", + "start_time": "2025-03-13T15:48:45.854570Z" } }, "cell_type": "code", "source": [ - "enciphered_nkode = user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes)\n", - "\n", + "enciphered_nkode = user.cipher.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.cipher)\n", "print(enciphered_nkode == user.enciphered_passcode.code)\n" ], "outputs": [ @@ -711,7 +683,7 @@ ] } ], - "execution_count": 75 + "execution_count": 178 }, { "cell_type": "markdown", @@ -729,8 +701,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.146298Z", - "start_time": "2025-03-07T19:01:10.066784Z" + "end_time": "2025-03-13T15:50:03.065695Z", + "start_time": "2025-03-13T15:50:02.452807Z" } }, "cell_type": "code", @@ -744,8 +716,8 @@ "api.renew_attributes(customer_id)\n", "print_user_enciphered_code()\n", "\n", - "login_interface = api.get_login_interface(username, customer_id)\n", - "selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.attrs_per_key)\n", + "login_keypad = api.get_login_keypad(username, customer_id)\n", + "selected_keys_login = select_keys_with_passcode_values(user_passcode, login_keypad, keypad_size.props_per_key)\n", "success = api.login(customer_id, username, selected_keys_login)\n", "print(success)\n", "print_user_enciphered_code()" @@ -755,14 +727,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: xYa4LE04KVq0jRcaFG9HA8BZHqo=, code: $2b$12$GdGDlzHY6rt.It6xq.EBIud5PgIWKObJdMms3AaYz1JWK1nf4Jj5G\n", - "mask: xYa4LE04KVq0jRcaFG9HA8BZHqo=, code: $2b$12$GdGDlzHY6rt.It6xq.EBIud5PgIWKObJdMms3AaYz1JWK1nf4Jj5G\n", + "mask: Veawti5Qkgy2y/XtFTZZ7w5oYgA=, code: $2b$12$OHHiqL888FauxiXIVICtouT1xR8iwe79oz63mXkPWKldHoLHPsByy\n", + "mask: Veawti5Qkgy2y/XtFTZZ7w5oYgA=, code: $2b$12$OHHiqL888FauxiXIVICtouT1xR8iwe79oz63mXkPWKldHoLHPsByy\n", "True\n", - "mask: jTZF7KTs3zviTwTt7yqXk7BEK7I=, code: $2b$12$BIJdJ/77MAKNtETz8WSB9uWpJo4.jisH/HsN5o55ez294T1a40rxm\n" + "mask: Sxe3AjutJzGrQLgq/RnfK9G3wHc=, code: $2b$12$.wb5cZjbXJBY6/MQxJIlGu/JyqaF78IM.1n3QP6coxOcKLORU/Io6\n" ] } ], - "execution_count": 76 + "execution_count": 179 }, { "metadata": {}, @@ -776,20 +748,20 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.148090Z", - "start_time": "2025-03-07T19:01:11.153001Z" + "end_time": "2025-03-13T15:52:30.906955Z", + "start_time": "2025-03-13T15:52:30.902577Z" } }, "cell_type": "code", "source": [ - "old_attrs = customer.attributes.attr_vals.copy()\n", - "old_sets = customer.attributes.set_vals.copy()\n", - "customer.attributes.renew()\n", - "new_attrs = customer.attributes.attr_vals\n", - "new_sets = customer.attributes.set_vals" + "old_props = customer.cipher.prop_key.copy()\n", + "old_sets = customer.cipher.set_key.copy()\n", + "customer.cipher.renew()\n", + "new_props = customer.cipher.prop_key\n", + "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 77 + "execution_count": 181 }, { "metadata": {}, @@ -802,21 +774,21 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.154941Z", - "start_time": "2025-03-07T19:01:11.361690Z" + "end_time": "2025-03-13T15:52:39.513715Z", + "start_time": "2025-03-13T15:52:39.510395Z" } }, "cell_type": "code", "source": [ - "attrs_xor = xor_lists(new_attrs, old_attrs)\n", - "sets_xor = xor_lists(new_sets, old_sets)\n", + "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.set_key = xor_lists(user.cipher.set_key, sets_xor)\n", - " user.cipher.prop_key = xor_lists(user.cipher.prop_key, attrs_xor)" + " user.cipher.set_key = np.bitwise_xor(user.cipher.set_key, sets_xor)\n", + " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 78 + "execution_count": 182 }, { "metadata": {}, @@ -826,22 +798,22 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-10T15:54:02.155673Z", - "start_time": "2025-03-07T19:01:11.369911Z" + "end_time": "2025-03-13T15:53:22.653638Z", + "start_time": "2025-03-13T15:53:22.345351Z" } }, "cell_type": "code", "source": [ "user.cipher = UserCipher.create(\n", - " customer.attributes.keypad_size,\n", - " customer.attributes.set_vals,\n", + " customer.cipher.keypad_size,\n", + " customer.cipher.set_key,\n", " user.cipher.max_nkode_len\n", ")\n", - "user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_attributes_idx, customer.attributes)\n", + "user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_attributes_idx, customer.cipher)\n", "user.renew = False" ], "outputs": [], - "execution_count": 79 + "execution_count": 186 } ], "metadata": { diff --git a/src/user_keypad.py b/src/user_keypad.py index 434ef21..f668bc2 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -1,5 +1,4 @@ from dataclasses import dataclass -from secrets import choice import numpy as np from src.models import KeypadSize -- 2.49.1 From 762639de8aa95d8ac6b1b8fd9b0178fcd850d57b Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 13 Mar 2025 11:08:11 -0500 Subject: [PATCH 20/85] refactor render_markdown.py --- docs/nkode_authentication_template.md | 72 +++++++++++++-------------- docs/render_markdown.py | 20 ++++---- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/docs/nkode_authentication_template.md b/docs/nkode_authentication_template.md index cfa9cb2..52b0a27 100644 --- a/docs/nkode_authentication_template.md +++ b/docs/nkode_authentication_template.md @@ -36,7 +36,7 @@ policy = NKodePolicy( keypad_size = KeypadSize( numb_of_keys = {{ keypad_size.numb_of_keys }}, - attrs_per_key = {{ keypad_size.attrs_per_key }} # aka number of sets + props_per_key = {{ keypad_size.props_per_key }} # aka number of sets ) customer_id = api.create_new_customer(keypad_size, policy) @@ -44,9 +44,9 @@ customer = api.customers[customer_id] ``` ### Customer Attributes and Sets A customer has users and defines the attributes and set values for all its users. -Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.attrs_per_key }} attributes per key, -this gives a customer interface of {{ keypad_size.numb_of_attrs }} distinct attributes and {{ keypad_size.attrs_per_key }} distinct attribute sets. -Each attribute belongs to one of the {{ keypad_size.attrs_per_key }} sets. Each attribute and set value is a unique 2-byte integer in this example. +Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.props_per_key }} attributes per key, +this gives a customer interface of {{ keypad_size.numb_of_props }} distinct attributes and {{ keypad_size.props_per_key }} distinct attribute sets. +Each attribute belongs to one of the {{ keypad_size.props_per_key }} sets. Each attribute and set value is a unique 2-byte integer in this example. ``` set_vals = customer.attributes.set_vals @@ -56,7 +56,7 @@ Customer Sets: {{ customer_set_vals }} ``` attr_vals = customer.attributes.attr_vals -keypad_view(attr_vals, keypad_size.attrs_per_key) +keypad_view(attr_vals, keypad_size.props_per_key) Customer Attributes: {% for attrs in customer_attr_view -%} @@ -85,15 +85,15 @@ Now that we have a customer, we can create users. To create a new user: The user's interface must be dispersable so the server can determine the user's nkode. The server randomly drops attribute sets until the number of attributes equals the number of keys, making the interface dispersable. -In our case, the server randomly drops {{ keypad_size.attrs_per_key - keypad_size.numb_of_keys }} attribute {{ "sets" if keypad_size.attrs_per_key - keypad_size.numb_of_keys > 1 else "set" }}. -to give us a {{ keypad_size.numb_of_keys }} X {{ keypad_size.numb_of_keys }} keypad with possible index values ranging from 0-{{ keypad_size.numb_of_attrs - 1 }}. +In our case, the server randomly drops {{ keypad_size.props_per_key - keypad_size.numb_of_keys }} attribute {{ "sets" if keypad_size.props_per_key - keypad_size.numb_of_keys > 1 else "set" }}. +to give us a {{ keypad_size.numb_of_keys }} X {{ keypad_size.numb_of_keys }} keypad with possible index values ranging from 0-{{ keypad_size.numb_of_props - 1 }}. Each value in the interface is the index value of a customer attribute. The user never learns what their "real" attribute is. They do not see the index value representing their nKode or the customer server-side value. ``` session_id, signup_interface = api.generate_index_interface(customer_id) -signup_interface_keypad = list_to_matrix(signup_interface, keypad_size.attrs_per_key) +signup_interface_keypad = list_to_matrix(signup_interface, keypad_size.props_per_key) Signup Keypad: {% for key in signup_keypad -%} @@ -110,7 +110,7 @@ If users want to change anything about their interface, they must also change th ``` username = {{ username }} user_passcode = {{ user_passcode }} -selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.attrs_per_key) +selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.props_per_key) Selected Keys {{ selected_keys_set }} @@ -161,11 +161,11 @@ Steps 1-2 are straightforward. For a better idea of how they work, see pyNKode. ##### User Cipher Keys Data Structure ``` -set_key = generate_random_nonrepeating_list(keypad_size.attrs_per_key, max_numb=2**(8*numb_of_bytes)) +set_key = generate_random_nonrepeating_list(keypad_size.props_per_key, max_numb=2**(8*numb_of_bytes)) set_key = xor_lists(set_key, customer_attr.set_vals) UserCipherKeys( - prop_key=generate_random_nonrepeating_list(keypad_size.attrs_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), + prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), pass_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), mask_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), set_key=set_key, @@ -176,13 +176,13 @@ UserCipherKeys( ##### User Cipher Keys Values ``` -user_keys = UserCipherKeys( - prop_key = {{ user_keys.prop_key }}, - pass_key = {{ user_keys.pass_key }}, - mask_key = {{ user_keys.mask_key }}, - set_key = {{ user_keys.set_key }}, - salt = {{ user_keys.salt }}, - max_nkode_len = {{ user_keys.max_nkode_len }} +user_cipher = UserCipherKeys( + prop_key = {{ user_cipher.prop_key }}, + pass_key = {{ user_cipher.pass_key }}, + mask_key = {{ user_cipher.mask_key }}, + set_key = {{ user_cipher.set_key }}, + salt = {{ user_cipher.salt }}, + max_nkode_len = {{ user_cipher.max_nkode_len }} ) ``` @@ -219,15 +219,15 @@ Passcode Attr Vals: {{ passcode_server_set }} ``` ``` -padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) +padded_passcode_server_set = user_cipher.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) set_idx = [customer.attributes.get_set_index(set_val) for set_val in padded_passcode_server_set] -mask_set_keys = [user_keys.set_key[idx] for idx in set_idx] +mask_set_keys = [user_cipher.set_key[idx] for idx in set_idx] ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) -ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) +ciphered_mask = xor_lists(ciphered_mask, user_cipher.mask_key) -mask = user_keys.encode_base64_str(ciphered_mask) +mask = user_cipher.encode_base64_str(ciphered_mask) Mask: {{ enciphered_nkode.mask }} ``` @@ -238,17 +238,17 @@ Mask: {{ enciphered_nkode.mask }} - code = hash(ciphered_passcode, salt) ``` -ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_keys.prop_key) +ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_cipher.prop_key) passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len passcode_ciphered_attrs.extend([0 for _ in range(pad_len)]) -ciphered_code = xor_lists(passcode_ciphered_attrs, user_keys.pass_key) +ciphered_code = xor_lists(passcode_ciphered_attrs, user_cipher.pass_key) passcode_bytes = int_array_to_bytes(ciphered_code) passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) -hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt) +hashed_data = bcrypt.hashpw(passcode_digest, user_cipher.salt) code = hashed_data.decode("utf-8") Code: {{ enciphered_nkode.code }} @@ -264,7 +264,7 @@ To login, a user: The client requests the user's login interface. ``` login_interface = api.get_login_interface(username, customer_id) -keypad_view(login_interface, keypad_size.attrs_per_key) +keypad_view(login_interface, keypad_size.props_per_key) ``` The server returns a randomly shuffled interface. Learn more about how the [User Interface Shuffle](nkode_concepts.md/#user-interface-shuffle) works ``` @@ -301,11 +301,11 @@ Recover nKode set values: ``` user = customer.users[username] -user_keys = user.user_keys +user_cipher = user.user_cipher user_mask = user.enciphered_passcode.mask -decoded_mask = user_keys.decode_base64_str(user_mask) -deciphered_mask = xor_lists(decoded_mask, user_keys.mask_key) -set_key_rand_component = xor_lists(set_vals, user_keys.set_key) +decoded_mask = user_cipher.decode_base64_str(user_mask) +deciphered_mask = xor_lists(decoded_mask, user_cipher.mask_key) +set_key_rand_component = xor_lists(set_vals, user_cipher.set_key) passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: set_idx = set_key_rand_component.index(set_cipher) @@ -331,7 +331,7 @@ Recall User Passcode: {{ user_passcode }} ``` ### Compare Enciphered Passcodes ``` -enciphered_nkode = user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes) +enciphered_nkode = user_cipher.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes) ``` If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid, and the login is successful. @@ -388,8 +388,8 @@ attrs_xor = xor_lists(new_attrs, old_attrs) sets_xor = xor_lists(new_sets, old_sets) for user in customer.users.values(): user.renew = True - user.user_keys.set_key = xor_lists(user.user_keys.set_key, sets_xor) - user.user_keys.prop_key = xor_lists(user.user_keys.prop_key, attrs_xor) + user.user_cipher.set_key = xor_lists(user.user_cipher.set_key, sets_xor) + user.user_cipher.prop_key = xor_lists(user.user_cipher.prop_key, attrs_xor) ``` ##### User prop Key The user's prop key was a randomly generated list of length `numb_of_keys * attr_per_key`. @@ -412,11 +412,11 @@ remains the same. Once the user has a successful login, they get a new salt and cipher keys, and their `enciphered_passcode` is recomputed with the new values. ``` -user.user_keys = UserCipherKeys.new( +user.user_cipher = UserCipherKeys.new( customer.attributes.keypad_size, customer.attributes.set_vals, - user.user_keys.max_nkode_len + user.user_cipher.max_nkode_len ) -user.enciphered_passcode = user.user_keys.encipher_nkode(presumed_selected_attributes_idx, customer.attributes) +user.enciphered_passcode = user.user_cipher.encipher_nkode(presumed_selected_attributes_idx, customer.attributes) user.renew = False ``` diff --git a/docs/render_markdown.py b/docs/render_markdown.py index b6fa7f3..13c9ff9 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -15,8 +15,9 @@ def random_username() -> str: return "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) -def select_keys_with_passcode_values(user_passcode: list[int], interface: list[int], attrs_per_key: int) -> list[int]: - return [interface.index(attr) // attrs_per_key for attr in user_passcode] +def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, attrs_per_key: int) -> list[int]: + indices = [np.where(keypad == attr)[0][0] for attr in user_passcode] + return [int(index // attrs_per_key) for index in indices] def visualize_keypad(keypad_list: np.ndarray, props_per_key: int): @@ -39,7 +40,7 @@ def render_nkode_authentication(data: dict): output = template.render(data) # Print or save the output - output_file = "/Users/donov/Desktop/NKode_documentation/nkode/docs/nkode_authentication.md" + output_file = os.path.expanduser("~/Desktop/nkode_authentication.md") with open(output_file, 'w') as fp: fp.write(output) print("File written successfully") @@ -71,18 +72,17 @@ if __name__ == "__main__": set_attribute_dict = dict(zip(set_vals, attr_set_view)) session_id, signup_interface = api.generate_signup_keypad(customer_id) - #signup_keypad = list_to_matrix(signup_keypad, keypad_size.numb_of_keys) - signup_keypad = signup_interface.reshape(-1, keypad_size.props_per_key) + signup_keypad = signup_interface.reshape(-1, keypad_size.numb_of_keys) username = random_username() passcode_len = 4 - user_passcode = signup_interface[:passcode_len] + user_passcode = signup_interface[:passcode_len].tolist() selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) server_side_attr = [customer.cipher.prop_key[idx] for idx in user_passcode] confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) - confirm_keypad = list_to_matrix(confirm_interface, keypad_size.numb_of_keys) + confirm_keypad = confirm_interface.reshape(-1, keypad_size.numb_of_keys) selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, keypad_size.numb_of_keys) @@ -121,7 +121,7 @@ if __name__ == "__main__": USER LOGIN """ login_interface = api.get_login_keypad(username, customer_id) - login_keypad = list_to_matrix(login_interface, keypad_size.props_per_key) + login_keypad = login_interface.reshape(-1, keypad_size.props_per_key) selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.props_per_key) success = api.login(customer_id, username, selected_keys_login) assert success @@ -141,7 +141,7 @@ if __name__ == "__main__": login_passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: set_idx = np.where(set_key_rand_component == set_cipher)[0][0] - login_passcode_sets.append(set_vals[set_idx]) + login_passcode_sets.append(int(set_vals[set_idx])) """ GET PRESUMED ATTRIBUTES @@ -200,7 +200,7 @@ if __name__ == "__main__": 'server_side_attr': server_side_attr, 'confirm_keypad': confirm_keypad, 'selected_keys_confirm': selected_keys_confirm, - 'cipher': user_keys, + 'user_cipher': user_keys, 'passcode_server_attr': passcode_server_attr, 'passcode_server_set': passcode_server_set, 'enciphered_nkode': enciphered_nkode, -- 2.49.1 From e99daab90a69abe11af261cb8f40c48fa057115c Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 06:18:01 -0500 Subject: [PATCH 21/85] refactor dispersion_tutorial.ipynb --- notebooks/dispersion_tutorial.ipynb | 96 +++++++++++++++-------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index 2d08272..bfd7754 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -11,32 +11,21 @@ }, { "cell_type": "code", - "execution_count": 1, - "outputs": [ - { - "data": { - "text/plain": "", - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|1|10|11|100|\n|key2|2|20|22|200|\n|key3|3|30|33|300|\n|key4|4|40|44|400|\n|key5|5|50|55|500|" - }, - "metadata": {}, - "output_type": "display_data" - } - ], "source": [ - "from src.utils import secure_fisher_yates_shuffle, matrix_to_list, list_to_matrix\n", - "from src.user_interface import UserKeypad\n", + "from src.user_keypad import UserKeypad\n", "from IPython.display import Markdown, display\n", "from src.models import KeypadSize\n", + "import numpy as np\n", "\n", - "def keypad_md_table(interface: list[int], keypad_size: KeypadSize) -> str:\n", - " assert (keypad_size.numb_of_props == len(interface))\n", - " interface_keypad = list_to_matrix(interface, keypad_size.props_per_key)\n", + "def keypad_md_table(keypad_list: np.ndarray, keypad_size: KeypadSize) -> str:\n", + " assert (keypad_size.numb_of_props == len(keypad_list))\n", + " keypad = keypad_list.reshape(-1, keypad_size.props_per_key)\n", " table = \"|key|\" + \"\".join([f\"set{idx}|\" for idx in range(keypad_size.props_per_key)])\n", " table += \"\\n|\" + \"\".join(\"-|\" for _ in range(keypad_size.props_per_key + 1))\n", "\n", " for key in range(keypad_size.numb_of_keys):\n", " table += f\"\\n|key{key+1}|\"\n", - " table += \"|\".join([str(attr) for attr in interface_keypad[key]])\n", + " table += \"|\".join([str(attr) for attr in keypad[key]])\n", " table += \"|\"\n", " return table\n", "\n", @@ -47,74 +36,91 @@ "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", " keypad.extend([key_numb * attr for attr in attrs])\n", "\n", - "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=keypad)\n", + "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad))\n", "\n", "display(Markdown(keypad_md_table(demo_interface.keypad, keypad_size)))\n" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-24T22:23:18.200797Z", - "start_time": "2024-07-24T22:23:18.119660Z" + "end_time": "2025-03-14T11:14:49.006056Z", + "start_time": "2025-03-14T11:14:48.964902Z" } - } - }, - { - "cell_type": "code", - "execution_count": 2, + }, "outputs": [ { "data": { - "text/plain": "", - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|3|30|33|300|\n|key2|1|10|11|100|\n|key3|5|50|55|500|\n|key4|2|20|22|200|\n|key5|4|40|44|400|" + "text/plain": [ + "" + ], + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|1|10|11|100|\n|key2|2|20|22|200|\n|key3|3|30|33|300|\n|key4|4|40|44|400|\n|key5|5|50|55|500|" }, "metadata": {}, "output_type": "display_data" } ], + "execution_count": 1 + }, + { + "cell_type": "code", "source": [ - "demo_interface_matrix = list_to_matrix(demo_interface.keypad, demo_interface.keypad_size.props_per_key)\n", - "shuffled_keys = secure_fisher_yates_shuffle(demo_interface_matrix)\n", - "shuffled_keys_list = matrix_to_list(shuffled_keys)\n", - "display(Markdown(keypad_md_table(shuffled_keys_list, keypad_size)))\n" + "demo_interface_matrix = demo_interface.keypad.reshape(-1, demo_interface.keypad_size.props_per_key)\n", + "shuffled_keys = np.random.permutation(demo_interface_matrix)\n", + "shuffled_keys_list = shuffled_keys.reshape(-1)\n", + "display(Markdown(keypad_md_table(shuffled_keys_list, keypad_size)))" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-24T22:23:18.201039Z", - "start_time": "2024-07-24T22:23:18.185696Z" + "end_time": "2025-03-14T11:15:32.023001Z", + "start_time": "2025-03-14T11:15:32.009838Z" } - } - }, - { - "cell_type": "code", - "execution_count": 3, + }, "outputs": [ { "data": { - "text/plain": "", - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|2|10|55|400|\n|key2|4|50|22|300|\n|key3|3|20|44|100|\n|key4|1|40|33|500|\n|key5|5|30|11|200|" + "text/plain": [ + "" + ], + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|4|40|44|400|\n|key2|3|30|33|300|\n|key3|2|20|22|200|\n|key4|1|10|11|100|\n|key5|5|50|55|500|" }, "metadata": {}, "output_type": "display_data" } ], + "execution_count": 3 + }, + { + "cell_type": "code", "source": [ - "attr_rotation = secure_fisher_yates_shuffle(list(range(keypad_size.numb_of_keys)))[:keypad_size.props_per_key]\n", + "attr_rotation = np.random.choice(range(keypad_size.numb_of_keys), size=keypad_size.props_per_key, replace=False)\n", "dispersed_interface = UserKeypad.random_attribute_rotation(\n", " shuffled_keys,\n", " attr_rotation\n", ")\n", "\n", - "display(Markdown(keypad_md_table(matrix_to_list(dispersed_interface), keypad_size)))\n" + "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))\n" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-24T22:23:18.201164Z", - "start_time": "2024-07-24T22:23:18.190198Z" + "end_time": "2025-03-14T11:17:30.317806Z", + "start_time": "2025-03-14T11:17:30.313082Z" } - } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|2|50|33|100|\n|key2|1|40|22|500|\n|key3|5|30|11|400|\n|key4|4|20|55|300|\n|key5|3|10|44|200|" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 5 } ], "metadata": { -- 2.49.1 From 80cd71170925f5fdb39716c12b16f8adbc61f47a Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 06:18:43 -0500 Subject: [PATCH 22/85] remove old notebook --- notebooks/nkode_calculation.ipynb | 172 ------------------------------ 1 file changed, 172 deletions(-) delete mode 100644 notebooks/nkode_calculation.ipynb diff --git a/notebooks/nkode_calculation.ipynb b/notebooks/nkode_calculation.ipynb deleted file mode 100644 index ef094dc..0000000 --- a/notebooks/nkode_calculation.ipynb +++ /dev/null @@ -1,172 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "source": [ - "# What does is take to compromise a users nKode in the worst case scenario?\n" - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "execution_count": 16, - "outputs": [], - "source": [ - "def compute_time_to_crack_hash(\n", - " num_of_attributes: int,\n", - " nkode_len,\n", - " secs_per_hash: float = 0.5,\n", - ") -> list[float, float]:\n", - " secs_to_days = 1/(60*60*24)\n", - " secs_to_years = secs_to_days / 365\n", - " total_secs_to_compute_half_of_hashes = num_of_attributes ** nkode_len * secs_per_hash * 0.5\n", - " return [round(total_secs_to_compute_half_of_hashes * secs_to_days, 2), round(total_secs_to_compute_half_of_hashes * secs_to_years, 2)]\n" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2024-07-26T15:29:35.667921Z", - "start_time": "2024-07-26T15:29:35.664278Z" - } - } - }, - { - "cell_type": "code", - "execution_count": 21, - "outputs": [], - "source": [ - "def matrix_to_md_table(col_names: list[str], mat: list[list[any]]) -> str:\n", - " assert len(col_names) == len(mat[0])\n", - " numb_cols = len(col_names)\n", - " table = \"|\" + \"|\".join(col_names) + \"|\\n\"\n", - " table += \"|\" + \"|\".join(['-' for _ in range(numb_cols)]) + \"|\\n\"\n", - " for row in mat:\n", - " table += \"|\" + \"|\".join([str(el) for el in row]) + \"|\\n\"\n", - " return table" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2024-07-27T18:42:04.980503Z", - "start_time": "2024-07-27T18:42:04.976703Z" - } - } - }, - { - "cell_type": "code", - "execution_count": 22, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "|number of attributes|nkode length|days to crack|years to crack|\n", - "|-|-|-|-|\n", - "|9|4|0.02|0.0|\n", - "|9|5|0.17|0.0|\n", - "|9|6|1.54|0.0|\n", - "|9|7|13.84|0.04|\n", - "|9|8|124.56|0.34|\n", - "|9|9|1121.01|3.07|\n", - "|9|10|10089.08|27.64|\n", - "|10|4|0.03|0.0|\n", - "|10|5|0.29|0.0|\n", - "|10|6|2.89|0.01|\n", - "|10|7|28.94|0.08|\n", - "|10|8|289.35|0.79|\n", - "|10|9|2893.52|7.93|\n", - "|10|10|28935.19|79.27|\n", - "|11|4|0.04|0.0|\n", - "|11|5|0.47|0.0|\n", - "|11|6|5.13|0.01|\n", - "|11|7|56.39|0.15|\n", - "|11|8|620.25|1.7|\n", - "|11|9|6822.77|18.69|\n", - "|11|10|75050.42|205.62|\n", - "|12|4|0.06|0.0|\n", - "|12|5|0.72|0.0|\n", - "|12|6|8.64|0.02|\n", - "|12|7|103.68|0.28|\n", - "|12|8|1244.16|3.41|\n", - "|12|9|14929.92|40.9|\n", - "|12|10|179159.04|490.85|\n", - "|13|4|0.08|0.0|\n", - "|13|5|1.07|0.0|\n", - "|13|6|13.97|0.04|\n", - "|13|7|181.56|0.5|\n", - "|13|8|2360.33|6.47|\n", - "|13|9|30684.32|84.07|\n", - "|13|10|398896.1|1092.87|\n", - "|14|4|0.11|0.0|\n", - "|14|5|1.56|0.0|\n", - "|14|6|21.79|0.06|\n", - "|14|7|305.02|0.84|\n", - "|14|8|4270.22|11.7|\n", - "|14|9|59783.12|163.79|\n", - "|14|10|836963.7|2293.05|\n", - "|15|4|0.15|0.0|\n", - "|15|5|2.2|0.01|\n", - "|15|6|32.96|0.09|\n", - "|15|7|494.38|1.35|\n", - "|15|8|7415.77|20.32|\n", - "|15|9|111236.57|304.76|\n", - "|15|10|1668548.58|4571.37|\n", - "\n" - ] - } - ], - "source": [ - "time_to_crack = []\n", - "for num_attr in range(9, 16):\n", - " for nkod_len in range(4, 11):\n", - " hash_crack = [num_attr, nkod_len]\n", - " hash_crack.extend(compute_time_to_crack_hash(num_attr, nkod_len))\n", - " time_to_crack.append(hash_crack)\n", - "\n", - "hash_crack_md = matrix_to_md_table([\"number of attributes\", \"nkode length\", \"days to crack\", \"years to crack\"], time_to_crack)\n", - "\n", - "\n", - "print(hash_crack_md)" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2024-07-27T18:43:31.002026Z", - "start_time": "2024-07-27T18:43:30.997876Z" - } - } - }, - { - "cell_type": "code", - "execution_count": null, - "outputs": [], - "source": "", - "metadata": { - "collapsed": false - } - } - ], - "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 -} -- 2.49.1 From 5c1697d8f49d2629fea3a50f77be988a12e7a6c1 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 06:20:24 -0500 Subject: [PATCH 23/85] remove utils.py --- src/utils.py | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/utils.py diff --git a/src/utils.py b/src/utils.py deleted file mode 100644 index 38c9284..0000000 --- a/src/utils.py +++ /dev/null @@ -1,31 +0,0 @@ -import secrets - - -def secure_fisher_yates_shuffle(arr: list) -> list: - n = len(arr) - for i in range(n - 1, 0, -1): - j = secrets.randbelow(i + 1) - arr[i], arr[j] = arr[j], arr[i] - return arr - - -def xor_lists(l1: list[int], l2: list[int]): - if len(l1) != len(l2): - raise ValueError("Lists must be of equal length") - return [l2[i] ^ l1[i] for i in range(len(l1))] - - -def matrix_to_list(mat: list[list[int]]) -> list[int]: - return [val for row in mat for val in row] - - -def list_to_matrix(lst: list[int], cols: int) -> list[list[int]]: - return [lst[i:i + cols] for i in range(0, len(lst), cols)] - - -def matrix_transpose(mat: list[list[int]]) -> list[list[int]]: - return [list(row) for row in zip(*mat)] - - -def int_array_to_bytes(int_arr: list[int], byte_size: int = 2) -> bytes: - return b"".join([numb.to_bytes(byte_size, byteorder='big') for numb in int_arr]) -- 2.49.1 From acc5780dc443f1acf13f6f0e80a763a2a07a01a0 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 06:21:22 -0500 Subject: [PATCH 24/85] remove test book --- notebooks/test_book.ipynb | 118 -------------------------------------- 1 file changed, 118 deletions(-) delete mode 100644 notebooks/test_book.ipynb diff --git a/notebooks/test_book.ipynb b/notebooks/test_book.ipynb deleted file mode 100644 index bbc6822..0000000 --- a/notebooks/test_book.ipynb +++ /dev/null @@ -1,118 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "id": "initial_id", - "metadata": { - "collapsed": true, - "ExecuteTime": { - "end_time": "2025-03-12T14:46:19.236991Z", - "start_time": "2025-03-12T14:46:19.232966Z" - } - }, - "source": [ - "import numpy as np\n", - "keypad_matrix = np.array([[1, 2, 3],\n", - " [4, 5, 6],\n", - " [7, 8, 9]])" - ], - "outputs": [], - "execution_count": 5 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-03-12T14:46:19.249251Z", - "start_time": "2025-03-12T14:46:19.244635Z" - } - }, - "cell_type": "code", - "source": [ - "rng = np.random.default_rng()\n", - "\n", - "# Step 1: Get the matrix\n", - "keypad_view = keypad_matrix.copy() # Using copy to simulate self.keypad_matrix()\n", - "print(\"Original keypad_view:\")\n", - "print(keypad_view)\n", - "# Output:\n", - "# [[1 2 3]\n", - "# [4 5 6]\n", - "# [7 8 9]]\n", - "\n", - "# Step 2: Shuffle rows in place\n", - "rng.shuffle(keypad_view, axis=0)\n", - "print(\"After rng.shuffle(keypad_view, axis=0):\")\n", - "print(keypad_view)\n", - "# Output (rows shuffled):\n", - "# [[7 8 9]\n", - "# [1 2 3]\n", - "# [4 5 6]]\n", - "\n", - "# Step 3: Transpose\n", - "set_view = keypad_view.T\n", - "print(\"After set_view = keypad_view.T:\")\n", - "print(set_view)\n", - "# Output (rows become columns):\n", - "# [[7 1 4]\n", - "# [8 2 5]\n", - "# [9 3 6]]\n", - "\n", - "# Step 4: Shuffle each row independently\n", - "set_view = rng.permutation(set_view, axis=1)\n", - "print(\"After rng.permutation(set_view, axis=1):\")\n", - "print(set_view)\n", - "# Output (each row shuffled independently):\n", - "# [[4 1 7]\n", - "# [5 8 2]\n", - "# [3 6 9]]" - ], - "id": "c7db73ce336d9f0a", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Original keypad_view:\n", - "[[1 2 3]\n", - " [4 5 6]\n", - " [7 8 9]]\n", - "After rng.shuffle(keypad_view, axis=0):\n", - "[[7 8 9]\n", - " [1 2 3]\n", - " [4 5 6]]\n", - "After set_view = keypad_view.T:\n", - "[[7 1 4]\n", - " [8 2 5]\n", - " [9 3 6]]\n", - "After rng.permutation(set_view, axis=1):\n", - "[[4 7 1]\n", - " [5 8 2]\n", - " [6 9 3]]\n" - ] - } - ], - "execution_count": 6 - } - ], - "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": 5 -} -- 2.49.1 From c6bf401bc5dda8fefbf66d89cd8b3d7c55e71c23 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 09:20:49 -0500 Subject: [PATCH 25/85] rename attribute to property --- docs/render_markdown.py | 4 +- notebooks/dispersion_tutorial.ipynb | 7 +- notebooks/nkode_tutorial.ipynb | 373 +++++++++++++--------------- src/customer.py | 28 +-- src/models.py | 2 +- src/nkode_api.py | 2 +- src/user.py | 8 +- src/user_keypad.py | 22 +- src/utils.py | 14 ++ test/test_nkode_api.py | 2 +- test/test_user_keypad.py | 4 +- 11 files changed, 225 insertions(+), 241 deletions(-) create mode 100644 src/utils.py diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 13c9ff9..90df4b3 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -53,7 +53,7 @@ if __name__ == "__main__": max_nkode_len=10, min_nkode_len=4, distinct_sets=0, - distinct_attributes=4, + distinct_properties=4, byte_len=2, ) keypad_size = KeypadSize( @@ -153,7 +153,7 @@ if __name__ == "__main__": for idx in range(passcode_len): key_numb = selected_keys_login[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = customer.users[username].user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) + selected_attr_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) presumed_selected_attributes_idx.append(selected_attr_idx) """ diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index bfd7754..0ac3434 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -13,6 +13,7 @@ "cell_type": "code", "source": [ "from src.user_keypad import UserKeypad\n", + "from src.utils import random_property_rotation\n", "from IPython.display import Markdown, display\n", "from src.models import KeypadSize\n", "import numpy as np\n", @@ -93,10 +94,10 @@ { "cell_type": "code", "source": [ - "attr_rotation = np.random.choice(range(keypad_size.numb_of_keys), size=keypad_size.props_per_key, replace=False)\n", - "dispersed_interface = UserKeypad.random_attribute_rotation(\n", + "prop_rotation = np.random.choice(range(keypad_size.numb_of_keys), size=keypad_size.props_per_key, replace=False)\n", + "dispersed_interface = random_property_rotation(\n", " shuffled_keys,\n", - " attr_rotation\n", + " prop_rotation\n", ")\n", "\n", "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))\n" diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 39bb6dc..30c99a1 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -12,12 +12,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:40:54.981894Z", - "start_time": "2025-03-13T15:40:54.975972Z" + "end_time": "2025-03-14T14:18:14.423890Z", + "start_time": "2025-03-14T14:18:14.420788Z" } }, "outputs": [], - "execution_count": 157 + "execution_count": 23 }, { "cell_type": "code", @@ -40,12 +40,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:41:02.574902Z", - "start_time": "2025-03-13T15:41:02.566577Z" + "end_time": "2025-03-14T14:18:14.434459Z", + "start_time": "2025-03-14T14:18:14.430679Z" } }, "outputs": [], - "execution_count": 159 + "execution_count": 24 }, { "cell_type": "code", @@ -55,12 +55,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:41:06.702836Z", - "start_time": "2025-03-13T15:41:06.697810Z" + "end_time": "2025-03-14T14:18:14.453086Z", + "start_time": "2025-03-14T14:18:14.451030Z" } }, "outputs": [], - "execution_count": 160 + "execution_count": 25 }, { "cell_type": "markdown", @@ -70,11 +70,11 @@ "A customer defines their NKode Policy and their interface. Below we've set:\n", "- max nkode length = 10\n", "- min nkode length = 4\n", - "- distinct attributes = 4\n", + "- distinct properties = 4\n", "- distinct set = 0\n", "- byte len = 2\n", "\n", - "This customer also has an interface with 5 keys and 6 attributes per key. The number of attributes must be greater than the number of keys to be dispersion resistant." + "This customer also has an interface with 5 keys and 6 properties per key. The number of properties must be greater than the number of keys to be dispersion resistant." ], "metadata": { "collapsed": false @@ -87,7 +87,7 @@ " max_nkode_len=10,\n", " min_nkode_len=4,\n", " distinct_sets=0,\n", - " distinct_attributes=4,\n", + " distinct_properties=4,\n", " byte_len=2,\n", ")\n", "keypad_size = KeypadSize(\n", @@ -100,20 +100,20 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:41:09.768933Z", - "start_time": "2025-03-13T15:41:09.760522Z" + "end_time": "2025-03-14T14:18:14.461793Z", + "start_time": "2025-03-14T14:18:14.457601Z" } }, "outputs": [], - "execution_count": 161 + "execution_count": 26 }, { "cell_type": "markdown", "source": [ "### NKode Customer\n", - "A customer has users and defines the attributes and set values for all its users.\n", - "Since our customer has 5 keys and 6 attributes per key, this gives a customer interface of 30 distinct attributes and 6 distinct attribute sets.\n", - "Each attribute belongs to one of the 6 sets." + "A customer has users and defines the properties and set values for all its users.\n", + "Since our customer has 5 keys and 6 properties per key, this gives a customer interface of 30 distinct properties and 6 distinct properties sets.\n", + "Each properties belongs to one of the 6 sets." ], "metadata": { "collapsed": false @@ -121,9 +121,7 @@ }, { "cell_type": "markdown", - "source": [ - "#### Customer and Attribute Values" - ], + "source": "#### Customer and Properties Values", "metadata": { "collapsed": false } @@ -131,19 +129,17 @@ { "cell_type": "code", "source": [ - "set_vals = customer.cipher.set_key\n", - "attr_vals = customer.cipher.prop_key\n", - "print(f\"Customer Sets: {set_vals}\")\n", - "print(f\"Customer Attributes:\")\n", - "customer_prop_keypad = attr_vals.reshape(-1, keypad_size.props_per_key)\n", + "print(f\"Customer Set Key: {customer.cipher.set_key}\")\n", + "print(f\"Customer properties Key:\")\n", + "customer_prop_keypad = customer.cipher.prop_key.reshape(-1, keypad_size.props_per_key)\n", "for idx, key_vals in enumerate(customer_prop_keypad):\n", " print(f\"{key_vals}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:41:11.949505Z", - "start_time": "2025-03-13T15:41:11.941034Z" + "end_time": "2025-03-14T14:18:14.472200Z", + "start_time": "2025-03-14T14:18:14.469516Z" } }, "outputs": [ @@ -151,23 +147,21 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Sets: [25709 8136 44044 46233 29423 25038]\n", - "Customer Attributes:\n", - "[57582 3595 14389 48861 6004 53011]\n", - "[ 2051 49181 8905 9645 63388 60901]\n", - "[ 5952 47243 39367 26329 21337 27385]\n", - "[ 8812 29822 227 12486 4712 16109]\n", - "[28006 29445 47125 35353 35910 6662]\n" + "Customer Set Key: [57513 39162 37238 40595 30029 8573]\n", + "Customer properties Key:\n", + "[14659 8841 6728 32702 39675 63074]\n", + "[19274 15250 33729 24532 34839 38348]\n", + "[ 2539 51139 60808 11045 55452 61221]\n", + "[55550 11904 10001 59259 26026 26740]\n", + "[59910 49698 45525 62003 49414 40644]\n" ] } ], - "execution_count": 162 + "execution_count": 27 }, { "cell_type": "markdown", - "source": [ - "#### Customer Set To Attribute Map" - ], + "source": "#### Customer Set To Properties Map", "metadata": { "collapsed": false } @@ -175,16 +169,16 @@ { "cell_type": "code", "source": [ - "set_attribute_dict = dict(zip(set_vals, customer_prop_keypad.T))\n", - "print(f\"Set to Attribute Map:\")\n", - "for set_val, attrs in set_attribute_dict.items():\n", + "set_properties_dict = dict(zip(customer.cipher.set_key, customer_prop_keypad.T))\n", + "print(f\"Set to Properties Map:\")\n", + "for set_val, attrs in set_properties_dict.items():\n", " print(f\"{set_val}: {attrs}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:41:14.890303Z", - "start_time": "2025-03-13T15:41:14.883511Z" + "end_time": "2025-03-14T14:18:14.486583Z", + "start_time": "2025-03-14T14:18:14.484301Z" } }, "outputs": [ @@ -192,76 +186,77 @@ "name": "stdout", "output_type": "stream", "text": [ - "Set to Attribute Map:\n", - "25709: [57582 2051 5952 8812 28006]\n", - "8136: [ 3595 49181 47243 29822 29445]\n", - "44044: [14389 8905 39367 227 47125]\n", - "46233: [48861 9645 26329 12486 35353]\n", - "29423: [ 6004 63388 21337 4712 35910]\n", - "25038: [53011 60901 27385 16109 6662]\n" + "Set to Properties Map:\n", + "57513: [14659 19274 2539 55550 59910]\n", + "39162: [ 8841 15250 51139 11904 49698]\n", + "37238: [ 6728 33729 60808 10001 45525]\n", + "40595: [32702 24532 11045 59259 62003]\n", + "30029: [39675 34839 55452 26026 49414]\n", + "8573: [63074 38348 61221 26740 40644]\n" ] } ], - "execution_count": 163 + "execution_count": 28 }, { + "metadata": {}, "cell_type": "markdown", "source": [ "### User Signup\n", "To create a new must call this endpoints in order:\n", - "1. Generate Index Interface\n", + "1. Generate Keypad\n", "2. Set User NKode\n", "3. Confirm User NKode\n", "\n", - "#### Generate Index Interface\n", - " For the server to determine the users nkode, the user's interface must be dispersable. To make the interface dispersable, the server will randomly drop attribute sets to the number of attributes is equal to the number of keys. In our case, the server drops 1 attribute set to give us a 5 X 5 keypad with possible index values ranging from 0-29.\n", + "#### Generate Keypad\n", + " For the server to determine the users nkode, the user's keypad must be dispersable. To make the keypad dispersable, the server will randomly drop properties sets to the number of properties is equal to the number of keys. In our case, the server drops 1 properties set 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 interface is the index value of a customer attribute\n", - " - the user never learns what their \"real\" attribute is. All they do is specify an index in the customer interface\n" - ], - "metadata": { - "collapsed": false - } + " - each value in the keypad is the index value of a customer properties\n", + " - the user never learns what their \"real\" properties is. All they do is specify an index in the customer keypad\n" + ] }, { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-14T14:18:14.510067Z", + "start_time": "2025-03-14T14:18:14.507204Z" + } + }, "cell_type": "code", "source": [ "session_id, signup_keypad = api.generate_signup_keypad(customer_id)\n", "print(signup_keypad.reshape(-1, keypad_size.numb_of_keys))" ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-13T15:41:19.446496Z", - "start_time": "2025-03-13T15:41:19.440194Z" - } - }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[[21 3 27 15 9]\n", - " [19 1 25 13 7]\n", - " [23 5 29 17 11]\n", - " [22 4 28 16 10]\n", - " [20 2 26 14 8]]\n" + "[[ 1 7 13 19 25]\n", + " [ 0 6 12 18 24]\n", + " [ 5 11 17 23 29]\n", + " [ 2 8 14 20 26]\n", + " [ 4 10 16 22 28]]\n" ] } ], - "execution_count": 164 + "execution_count": 29 }, { + "metadata": {}, "cell_type": "markdown", "source": [ - "#### Set NKode\n", - "The user identifies attributes in the interface they want in their nkode. Each attribute in the gui has an index value. Below the user has selected 16, 9, 6, 19. Graphically represent with anything. The only requirement is that the graphical attributes must be associated with the same index value everytime the user goes to login. If the user wants to change anything about their interface(the number of keys, attributes or graphical attributes), they must also change their nkode." - ], - "metadata": { - "collapsed": false - } + "#### Set nKode\n", + "The user identifies properties in the keypad they want in their nkode. Each properties in the gui has an index value. Below the user has selected 16, 9, 6, 19. Graphically represent with anything. The only requirement is that the graphical properties must be associated with the same index value everytime the user goes to login. If the user wants to change anything about their keypad(the number of keys, properties or graphical properties), they must also change their nkode." + ] }, { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-14T14:18:14.535581Z", + "start_time": "2025-03-14T14:18:14.531909Z" + } + }, "cell_type": "code", "source": [ "keypad_view(signup_keypad, keypad_size.numb_of_keys)\n", @@ -272,82 +267,73 @@ "print(f\"User Passcode: {user_passcode}\")\n", "print(f\"Selected Keys\\n{selected_keys_set}\")\n", "server_side_attr = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", - "print(f\"User Passcode Server-side Attributes: {server_side_attr}\")" + "print(f\"User Passcode Server-side properties: {server_side_attr}\")" ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-13T15:41:22.138365Z", - "start_time": "2025-03-13T15:41:22.130412Z" - } - }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [21 3 27 15 9]\n", - "Key 1: [19 1 25 13 7]\n", - "Key 2: [23 5 29 17 11]\n", - "Key 3: [22 4 28 16 10]\n", - "Key 4: [20 2 26 14 8]\n", - "User Passcode: [21 3 27 15]\n", + "Key 0: [ 1 7 13 19 25]\n", + "Key 1: [ 0 6 12 18 24]\n", + "Key 2: [ 5 11 17 23 29]\n", + "Key 3: [ 2 8 14 20 26]\n", + "Key 4: [ 4 10 16 22 28]\n", + "User Passcode: [ 1 7 13 19]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side Attributes: [12486, 48861, 35353, 26329]\n" + "User Passcode Server-side properties: [8841, 15250, 51139, 11904]\n" ] } ], - "execution_count": 165 + "execution_count": 30 }, { - "cell_type": "code", - "source": [ - "confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id)\n", - "keypad_view(confirm_interface, keypad_size.numb_of_keys)\n", - "selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, keypad_size.numb_of_keys)\n", - "print(f\"Selected Keys\\n{selected_keys_confirm}\")" - ], "metadata": { - "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-13T15:41:25.094831Z", - "start_time": "2025-03-13T15:41:25.087580Z" + "end_time": "2025-03-14T14:18:14.556541Z", + "start_time": "2025-03-14T14:18:14.553217Z" } }, + "cell_type": "code", + "source": [ + "confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, session_id)\n", + "keypad_view(confirm_keypad, keypad_size.numb_of_keys)\n", + "selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_keypad, keypad_size.numb_of_keys)\n", + "print(f\"Selected Keys\\n{selected_keys_confirm}\")" + ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [22 2 29 15 7]\n", - "Key 1: [23 4 27 13 8]\n", - "Key 2: [21 5 25 14 10]\n", - "Key 3: [19 3 26 16 11]\n", - "Key 4: [20 1 28 17 9]\n", + "Key 0: [ 0 11 16 20 25]\n", + "Key 1: [ 2 6 13 22 29]\n", + "Key 2: [ 4 8 17 19 24]\n", + "Key 3: [ 1 10 12 23 26]\n", + "Key 4: [ 5 7 14 18 28]\n", "Selected Keys\n", - "[2, 3, 1, 0]\n" + "[3, 4, 1, 2]\n" ] } ], - "execution_count": 166 + "execution_count": 31 }, { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-14T14:18:14.884832Z", + "start_time": "2025-03-14T14:18:14.575902Z" + } + }, "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, session_id)\n", "print(success)" ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-13T15:43:12.401745Z", - "start_time": "2025-03-13T15:42:59.685701Z" - } - }, "outputs": [ { "name": "stdout", @@ -357,7 +343,7 @@ ] } ], - "execution_count": 168 + "execution_count": 32 }, { "metadata": {}, @@ -376,8 +362,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:43:26.496044Z", - "start_time": "2025-03-13T15:43:26.484618Z" + "end_time": "2025-03-14T14:18:14.894865Z", + "start_time": "2025-03-14T14:18:14.889948Z" } }, "cell_type": "code", @@ -413,12 +399,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [46233, 46233, 46233, 46233]\n", - "Passcode Attr Vals: [12486, 48861, 35353, 26329]\n" + "Passcode Set Vals: [39162, 39162, 39162, 39162]\n", + "Passcode Attr Vals: [8841, 15250, 51139, 11904]\n" ] } ], - "execution_count": 169 + "execution_count": 33 }, { "metadata": {}, @@ -440,8 +426,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:43:30.557640Z", - "start_time": "2025-03-13T15:43:30.550158Z" + "end_time": "2025-03-14T14:18:14.917821Z", + "start_time": "2025-03-14T14:18:14.914851Z" } }, "cell_type": "code", @@ -455,7 +441,7 @@ "mask = user_keys.encode_base64_str(ciphered_mask)" ], "outputs": [], - "execution_count": 170 + "execution_count": 34 }, { "metadata": {}, @@ -472,8 +458,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:43:33.209403Z", - "start_time": "2025-03-13T15:43:32.898812Z" + "end_time": "2025-03-14T14:18:15.243102Z", + "start_time": "2025-03-14T14:18:14.937865Z" } }, "cell_type": "code", @@ -497,13 +483,13 @@ "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 171 + "execution_count": 35 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:43:35.234325Z", - "start_time": "2025-03-13T15:43:35.229292Z" + "end_time": "2025-03-14T14:18:15.249186Z", + "start_time": "2025-03-14T14:18:15.247147Z" } }, "cell_type": "code", @@ -516,20 +502,24 @@ ")" ], "outputs": [], - "execution_count": 172 + "execution_count": 36 }, { + "metadata": {}, "cell_type": "markdown", "source": [ "### User Login\n", - "1. Get login interface\n", + "1. Get login keypad\n", "2. Login\n" - ], - "metadata": { - "collapsed": false - } + ] }, { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-14T14:18:15.563030Z", + "start_time": "2025-03-14T14:18:15.257823Z" + } + }, "cell_type": "code", "source": [ "login_keypad = api.get_login_keypad(username, customer_id)\n", @@ -539,30 +529,23 @@ "success = api.login(customer_id, username, selected_keys_login)\n", "print(success)" ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-13T15:43:55.881744Z", - "start_time": "2025-03-13T15:43:37.544889Z" - } - }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [18 19 20 21 22 23]\n", - "Key 1: [0 1 2 3 4 5]\n", - "Key 2: [24 25 26 27 28 29]\n", - "Key 3: [12 13 14 15 16 17]\n", - "Key 4: [ 6 7 8 9 10 11]\n", + "Key 0: [0 1 2 3 4 5]\n", + "Key 1: [ 6 7 8 9 10 11]\n", + "Key 2: [12 13 14 15 16 17]\n", + "Key 3: [18 19 20 21 22 23]\n", + "Key 4: [24 25 26 27 28 29]\n", "Selected Keys: [0, 1, 2, 3]\n", "True\n" ] } ], - "execution_count": 173 + "execution_count": 37 }, { "metadata": {}, @@ -570,8 +553,8 @@ "source": [ "## Validate Login Key Entry\n", "- decipher user mask and recover nkode set values\n", - "- get presumed attribute from key selection and set values\n", - "- encipher, salt and hash presumed attribute values and compare it to the users hashed code" + "- 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" ] }, { @@ -594,8 +577,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:44:12.223540Z", - "start_time": "2025-03-13T15:44:12.215204Z" + "end_time": "2025-03-14T14:18:15.574718Z", + "start_time": "2025-03-14T14:18:15.571571Z" } }, "cell_type": "code", @@ -615,36 +598,36 @@ "name": "stdout", "output_type": "stream", "text": [ - "[46233, 46233, 46233, 46233]\n" + "[39162, 39162, 39162, 39162]\n" ] } ], - "execution_count": 174 + "execution_count": 38 }, { "metadata": {}, "cell_type": "markdown", - "source": "### Get Presumed Attributes\n" + "source": "### Get Presumed Properties\n" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:44:41.027416Z", - "start_time": "2025-03-13T15:44:15.077074Z" + "end_time": "2025-03-14T14:18:15.591180Z", + "start_time": "2025-03-14T14:18:15.587992Z" } }, "cell_type": "code", "source": [ "set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets]\n", "\n", - "presumed_selected_attributes_idx = []\n", + "presumed_selected_properties_idx = []\n", "for idx in range(passcode_len):\n", " key_numb = selected_keys_login[idx]\n", " set_idx = set_vals_idx[idx]\n", - " selected_attr_idx = customer.users[username].user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx)\n", - " presumed_selected_attributes_idx.append(selected_attr_idx)\n", + " selected_attr_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx)\n", + " presumed_selected_properties_idx.append(selected_attr_idx)\n", "\n", - "print(user_passcode.tolist() == presumed_selected_attributes_idx)" + "print(user_passcode.tolist() == presumed_selected_properties_idx)" ], "outputs": [ { @@ -655,7 +638,7 @@ ] } ], - "execution_count": 175 + "execution_count": 39 }, { "metadata": {}, @@ -665,13 +648,13 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:48:46.158380Z", - "start_time": "2025-03-13T15:48:45.854570Z" + "end_time": "2025-03-14T14:18:15.916787Z", + "start_time": "2025-03-14T14:18:15.610843Z" } }, "cell_type": "code", "source": [ - "enciphered_nkode = user.cipher.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.cipher)\n", + "enciphered_nkode = user.cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher)\n", "print(enciphered_nkode == user.enciphered_passcode.code)\n" ], "outputs": [ @@ -683,26 +666,24 @@ ] } ], - "execution_count": 178 + "execution_count": 40 }, { + "metadata": {}, "cell_type": "markdown", "source": [ - "## Renew Attributes \n", - "1. Renew Customer Attributes \n", + "## Renew Properties\n", + "1. Renew Customer Properties\n", "2. Renew User Keys\n", "3. Refresh User on Login\n", "\n" - ], - "metadata": { - "collapsed": false - } + ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:50:03.065695Z", - "start_time": "2025-03-13T15:50:02.452807Z" + "end_time": "2025-03-14T14:18:16.539065Z", + "start_time": "2025-03-14T14:18:15.926843Z" } }, "cell_type": "code", @@ -713,7 +694,7 @@ " print(f\"mask: {mask}, code: {code}\")\n", "\n", "print_user_enciphered_code() \n", - "api.renew_attributes(customer_id)\n", + "api.renew_keys(customer_id)\n", "print_user_enciphered_code()\n", "\n", "login_keypad = api.get_login_keypad(username, customer_id)\n", @@ -727,29 +708,29 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: Veawti5Qkgy2y/XtFTZZ7w5oYgA=, code: $2b$12$OHHiqL888FauxiXIVICtouT1xR8iwe79oz63mXkPWKldHoLHPsByy\n", - "mask: Veawti5Qkgy2y/XtFTZZ7w5oYgA=, code: $2b$12$OHHiqL888FauxiXIVICtouT1xR8iwe79oz63mXkPWKldHoLHPsByy\n", + "mask: 4NCIa/fFtx0udGaF1ub+O/6oU04=, code: $2b$12$XuGO.allDcb91peVIjwsq./Ba/TZI3wxAc0acgWkApzkzGWfwPQCC\n", + "mask: 4NCIa/fFtx0udGaF1ub+O/6oU04=, code: $2b$12$XuGO.allDcb91peVIjwsq./Ba/TZI3wxAc0acgWkApzkzGWfwPQCC\n", "True\n", - "mask: Sxe3AjutJzGrQLgq/RnfK9G3wHc=, code: $2b$12$.wb5cZjbXJBY6/MQxJIlGu/JyqaF78IM.1n3QP6coxOcKLORU/Io6\n" + "mask: ceaWZR+hNRpEkj3fq1cfPu1Zyok=, code: $2b$12$q7lqdTj6qBMDDGEog9Pq3.M2Wso0TI8cx4/PhOK/fE1mhsws2FGe.\n" ] } ], - "execution_count": 179 + "execution_count": 41 }, { "metadata": {}, "cell_type": "markdown", "source": [ "#### Renew Customer Keys\n", - "- Get old attributes and sets\n", - "- Replace attributes and sets" + "- Get old properties and sets\n", + "- Replace properties and sets" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:52:30.906955Z", - "start_time": "2025-03-13T15:52:30.902577Z" + "end_time": "2025-03-14T14:18:16.551437Z", + "start_time": "2025-03-14T14:18:16.547371Z" } }, "cell_type": "code", @@ -761,7 +742,7 @@ "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 181 + "execution_count": 42 }, { "metadata": {}, @@ -774,8 +755,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:52:39.513715Z", - "start_time": "2025-03-13T15:52:39.510395Z" + "end_time": "2025-03-14T14:18:16.568881Z", + "start_time": "2025-03-14T14:18:16.566223Z" } }, "cell_type": "code", @@ -788,7 +769,7 @@ " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 182 + "execution_count": 43 }, { "metadata": {}, @@ -798,8 +779,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-13T15:53:22.653638Z", - "start_time": "2025-03-13T15:53:22.345351Z" + "end_time": "2025-03-14T14:18:16.884432Z", + "start_time": "2025-03-14T14:18:16.572989Z" } }, "cell_type": "code", @@ -809,11 +790,11 @@ " customer.cipher.set_key,\n", " user.cipher.max_nkode_len\n", ")\n", - "user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_attributes_idx, customer.cipher)\n", + "user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher)\n", "user.renew = False" ], "outputs": [], - "execution_count": 186 + "execution_count": 44 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 4fb3662..5f7c925 100644 --- a/src/customer.py +++ b/src/customer.py @@ -34,46 +34,46 @@ class Customer: user.enciphered_passcode.mask, self.cipher.set_key, passcode_len) set_vals_idx = [self.cipher.get_set_index(set_val) for set_val in passcode_set_vals] - presumed_selected_attributes_idx = [] + presumed_property_idxs = [] for idx in range(passcode_len): key_numb = selected_keys[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = user.user_keypad.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_selected_attributes_idx.append(selected_attr_idx) + selected_attr_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) + presumed_property_idxs.append(selected_attr_idx) - enciphered_attr = user.cipher.encipher_salt_hash_code(presumed_selected_attributes_idx, self.cipher) + enciphered_attr = user.cipher.encipher_salt_hash_code(presumed_property_idxs, self.cipher) if enciphered_attr != user.enciphered_passcode.code: return False if user.renew: - user.refresh_passcode(presumed_selected_attributes_idx, self.cipher) + user.refresh_passcode(presumed_property_idxs, self.cipher) return True def renew_keys(self) -> bool: - old_attrs = self.cipher.prop_key.copy() + old_props = self.cipher.prop_key.copy() old_sets = self.cipher.set_key.copy() self.cipher.renew() - new_attrs = self.cipher.prop_key + new_props = self.cipher.prop_key new_sets = self.cipher.set_key - attrs_xor = np.bitwise_xor(new_attrs, old_attrs) + props_xor = np.bitwise_xor(new_props, old_props) set_xor = np.bitwise_xor(new_sets, old_sets) for user in self.users.values(): - user.renew_keys(set_xor, attrs_xor) + user.renew_keys(set_xor, props_xor) self.users[user.username] = user return True - def valid_new_nkode(self, passcode_attr_idx: list[int]) -> bool: - nkode_len = len(passcode_attr_idx) + def valid_new_nkode(self, passcode_prop_idx: list[int]) -> bool: + nkode_len = len(passcode_prop_idx) passcode_set_values = [ - self.cipher.get_prop_set_val(int(self.cipher.prop_key[attr_idx])) for attr_idx in passcode_attr_idx + self.cipher.get_prop_set_val(int(self.cipher.prop_key[prop_idx])) for prop_idx in passcode_prop_idx ] distinct_sets = len(set(passcode_set_values)) - distinct_attributes = len(set(passcode_attr_idx)) + distinct_properties = len(set(passcode_prop_idx)) if ( self.nkode_policy.min_nkode_len <= nkode_len <= self.nkode_policy.max_nkode_len and distinct_sets >= self.nkode_policy.distinct_sets and - distinct_attributes >= self.nkode_policy.distinct_attributes + distinct_properties >= self.nkode_policy.distinct_properties ): return True return False diff --git a/src/models.py b/src/models.py index 39957ae..101aafa 100644 --- a/src/models.py +++ b/src/models.py @@ -11,7 +11,7 @@ class NKodePolicy: max_nkode_len: int = 10 min_nkode_len: int = 4 distinct_sets: int = 0 - distinct_attributes: int = 4 + distinct_properties: int = 4 byte_len: int = 2 # Todo: this should change the total number of bytes an attribute or set value can be lock_out: int = 5 expiration: int = -1 # in seconds -1 means nkode never expires diff --git a/src/nkode_api.py b/src/nkode_api.py index baa7736..15fd941 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -107,7 +107,7 @@ class NKodeAPI: customer = self.customers[customer_id] return customer.valid_key_entry(username, key_selection) - def renew_attributes(self, customer_id: UUID) -> bool: + def renew_keys(self, customer_id: UUID) -> bool: if customer_id not in self.customers.keys(): raise ValueError("Customer ID not found") return self.customers[customer_id].renew_keys() diff --git a/src/user.py b/src/user.py index 6fa1bd8..2cc565f 100644 --- a/src/user.py +++ b/src/user.py @@ -21,11 +21,11 @@ class User: self.cipher.set_key = np.bitwise_xor(self.cipher.set_key, set_xor) self.cipher.prop_key = np.bitwise_xor(self.cipher.prop_key, prop_xor) - def refresh_passcode(self, passcode_attr_idx: list[int], customer_attributes: CustomerCipher): + def refresh_passcode(self, passcode_prop_idxs: list[int], customer_cipher: CustomerCipher): self.cipher = UserCipher.create( - customer_attributes.keypad_size, - customer_attributes.set_key, + customer_cipher.keypad_size, + customer_cipher.set_key, self.cipher.max_nkode_len ) - self.enciphered_passcode = self.cipher.encipher_nkode(passcode_attr_idx, customer_attributes) + self.enciphered_passcode = self.cipher.encipher_nkode(passcode_prop_idxs, customer_cipher) self.renew = False diff --git a/src/user_keypad.py b/src/user_keypad.py index f668bc2..f59bb72 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -1,5 +1,6 @@ from dataclasses import dataclass import numpy as np +from src.utils import random_property_rotation from src.models import KeypadSize @dataclass @@ -54,9 +55,9 @@ class UserKeypad: user_keypad_matrix = self.keypad_matrix() #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) shuffled_keys = rng.permutation(user_keypad_matrix, axis=0) - #attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] + #prop_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] attr_rotation = rng.permutation(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] - dispersed_keypad = self.random_attribute_rotation( + dispersed_keypad = random_property_rotation( shuffled_keys, attr_rotation.tolist(), ) @@ -79,21 +80,8 @@ class UserKeypad: #self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) pass - @staticmethod - def random_attribute_rotation( - user_keypad: np.ndarray, - attr_rotation: list[int] - ) -> np.ndarray: - transposed = user_keypad.T - if len(attr_rotation) != len(transposed): - raise ValueError("attr_rotation must be the same length as the number of attributes") - for idx, attr_set in enumerate(transposed): - rotation = attr_rotation[idx] - rotation = rotation % len(attr_set) if len(attr_set) > 0 else 0 - transposed[idx] = np.roll(attr_set, rotation) - return transposed.T - def attribute_adjacency_graph(self) -> dict[int, set[int]]: + def property_adjacency_graph(self) -> dict[int, set[int]]: user_keypad_keypad = self.keypad_matrix() graph = {} for key in user_keypad_keypad: @@ -102,7 +90,7 @@ class UserKeypad: graph[attr].remove(attr) return graph - def get_attr_idx_by_keynumb_setidx(self, key_numb: int, set_idx: int) -> int: + def get_prop_idx_by_keynumb_setidx(self, key_numb: int, set_idx: int) -> int: if not (0 <= key_numb < self.keypad_size.numb_of_keys): raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not (0 <= set_idx < self.keypad_size.props_per_key): diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..f9e7125 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,14 @@ +import numpy as np + +def random_property_rotation( + user_keypad: np.ndarray, + attr_rotation: list[int] +) -> np.ndarray: + transposed = user_keypad.T + if len(attr_rotation) != len(transposed): + raise ValueError("prop_rotation must be the same length as the number of attributes") + for idx, attr_set in enumerate(transposed): + rotation = attr_rotation[idx] + rotation = rotation % len(attr_set) if len(attr_set) > 0 else 0 + transposed[idx] = np.roll(attr_set, rotation) + return transposed.T diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index ad5d6b0..341bd88 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -39,7 +39,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): successful_login = nkode_api.login(customer_id, username, login_key_selection) assert successful_login - successful_renew = nkode_api.renew_attributes(customer_id) + successful_renew = nkode_api.renew_keys(customer_id) assert successful_renew login_keypad = nkode_api.get_login_keypad(username, customer_id) diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index f449a96..811c1f4 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -10,9 +10,9 @@ def user_keypad(): def test_dispersion(user_keypad): for _ in range(10000): - pre_dispersion_graph = user_keypad.attribute_adjacency_graph() + pre_dispersion_graph = user_keypad.property_adjacency_graph() user_keypad.disperse_keypad() - post_dispersion_graph = user_keypad.attribute_adjacency_graph() + post_dispersion_graph = user_keypad.property_adjacency_graph() for attr, adj_graph in pre_dispersion_graph.items(): assert (adj_graph.isdisjoint(post_dispersion_graph[attr])) -- 2.49.1 From 1e2dfa9c9c2a9ca91bda4443a9a8bd11ba06768e Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 09:27:57 -0500 Subject: [PATCH 26/85] rename attribute to property --- docs/nkode_authentication_template.md | 174 +++++++++++++------------- docs/render_markdown.py | 66 +++++----- notebooks/dispersion_tutorial.ipynb | 6 +- 3 files changed, 122 insertions(+), 124 deletions(-) diff --git a/docs/nkode_authentication_template.md b/docs/nkode_authentication_template.md index 52b0a27..ae7a32c 100644 --- a/docs/nkode_authentication_template.md +++ b/docs/nkode_authentication_template.md @@ -2,9 +2,9 @@ Play around with the code in [this](http://sesolgit/Repository/Blob/92a60227-4ef9-4196-8ebb-595581abf98c?encodedName=main&encodedPath=nkode_tutorial.ipynb) jupyter notebook. ## Customer Creation -Before creating a user, a customer generates random attributes and set +Before creating a user, a customer generates random properties and set values. The customers manage users. They define an nKode policy, keypad's dimensions, -attributes/sets in the keypad, and the frequency of attribute renewal. +properties/sets in the keypad, and the frequency of property renewal. ### nKode Policy and Keypad Size An nKode policy defines:
    @@ -12,16 +12,16 @@ An nKode policy defines:
  • the minimum length of a user's nKode
  • the number of unique set values in a user's nKode
  • the number of unique values in a user's nKode
  • -
  • the number of bytes in an attribute and set
  • +
  • the number of bytes in an property and set
The keypad size defines:
  • the number of keys in the keypad displayed to the user
  • -
  • attributes per key
  • +
  • properties per key
-To be [dispersion](nkode_concepts.md/#dispersion-resistant-interface) resistant, the number of attributes must be greater than the number of keys. +To be [dispersion](nkode_concepts.md/#dispersion-resistant-interface) resistant, the number of properties must be greater than the number of keys. ``` api = NKodeAPI() @@ -30,7 +30,7 @@ policy = NKodePolicy( max_nkode_len=10, min_nkode_len=4, distinct_sets=0, - distinct_attributes=4, + distinct_properties=4, byte_len=2 ) @@ -42,36 +42,36 @@ keypad_size = KeypadSize( customer_id = api.create_new_customer(keypad_size, policy) customer = api.customers[customer_id] ``` -### Customer Attributes and Sets -A customer has users and defines the attributes and set values for all its users. -Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.props_per_key }} attributes per key, -this gives a customer interface of {{ keypad_size.numb_of_props }} distinct attributes and {{ keypad_size.props_per_key }} distinct attribute sets. -Each attribute belongs to one of the {{ keypad_size.props_per_key }} sets. Each attribute and set value is a unique 2-byte integer in this example. +### Customer properties and Sets +A customer has users and defines the properties and set values for all its users. +Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.props_per_key }} properties per key, +this gives a customer interface of {{ keypad_size.numb_of_props }} distinct properties and {{ keypad_size.props_per_key }} distinct property sets. +Each property belongs to one of the {{ keypad_size.props_per_key }} sets. Each property and set value is a unique 2-byte integer in this example. ``` -set_vals = customer.attributes.set_vals +set_vals = customer.cipher.set_key Customer Sets: {{ customer_set_vals }} ``` ``` -attr_vals = customer.attributes.attr_vals -keypad_view(attr_vals, keypad_size.props_per_key) +prop_vals = customer.cipher.prop_key +keypad_view(prop_vals, keypad_size.props_per_key) -Customer Attributes: -{% for attrs in customer_attr_view -%} -{{ attrs }} +Customer properties: +{% for props in customer_prop_view -%} +{{ props }} {% endfor %} ``` -Attributes organized by set: +properties organized by set: ``` -attr_set_view = matrix_transpose(attr_keypad_view) -set_attribute_dict = dict(zip(set_vals, attr_set_view)) +prop_set_view = matrix_transpose(prop_keypad_view) +set_property_dict = dict(zip(set_vals, prop_set_view)) -Set to Attribute Map: -{% for set_val, attrs in set_attribute_dict.items() -%} -{{ set_val }} : {{ attrs }} +Set to property Map: +{% for set_val, props in set_property_dict.items() -%} +{{ set_val }} : {{ props }} {% endfor %} ``` @@ -83,12 +83,12 @@ Now that we have a customer, we can create users. To create a new user: 3. The user confirms their nKode. If the user's nKode matches the policy, the server creates the user. ### Random Interface Generation The user's interface must be dispersable so the server can determine the user's nkode. -The server randomly drops attribute sets until -the number of attributes equals the number of keys, making the interface dispersable. -In our case, the server randomly drops {{ keypad_size.props_per_key - keypad_size.numb_of_keys }} attribute {{ "sets" if keypad_size.props_per_key - keypad_size.numb_of_keys > 1 else "set" }}. +The server randomly drops property sets until +the number of properties equals the number of keys, making the interface dispersable. +In our case, the server randomly drops {{ keypad_size.props_per_key - keypad_size.numb_of_keys }} property {{ "sets" if keypad_size.props_per_key - keypad_size.numb_of_keys > 1 else "set" }}. to give us a {{ keypad_size.numb_of_keys }} X {{ keypad_size.numb_of_keys }} keypad with possible index values ranging from 0-{{ keypad_size.numb_of_props - 1 }}. -Each value in the interface is the index value of a customer attribute. -The user never learns what their "real" attribute is. They do not see the index value representing their nKode or +Each value in the interface is the index value of a customer property. +The user never learns what their "real" property is. They do not see the index value representing their nKode or the customer server-side value. ``` @@ -102,9 +102,9 @@ Key {{ loop.index }}: {{ key }} ``` ### Set nKode -The user identifies attributes in the interface they want in their nkode. Each attribute has an index value. +The user identifies properties in the interface they want in their nkode. Each property has an index value. Below, the user has selected `{{ user_passcode }}`. These index values can be represented by anything in the GUI. -The only requirement is that the GUI attributes be associated with the same index every time the user logs in. +The only requirement is that the GUI properties be associated with the same index every time the user logs in. If users want to change anything about their interface, they must also change their nkode. ``` @@ -116,11 +116,11 @@ Selected Keys {{ selected_keys_set }} ``` -The user's passcode server side attributes are: +The user's passcode server side properties are: ``` -server_side_attr = [customer.attributes.attr_vals[idx] for idx in user_passcode] +server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] -User Passcode Server-side Attributes: {{ server_side_attr }} +User Passcode Server-side properties: {{ server_side_prop }} ``` ### Confirm nKode @@ -149,7 +149,7 @@ success = api.confirm_nkode(username, customer_id, selected_keys_confirm, sessio When a new user creates an nKode, the server caches its set and confirms the interface and the user's key selection. On the last api.confirm_nkode, the server: -1. Deduces the user's attributes +1. Deduces the user's properties 2. Validates the Passcode against the nKodePolicy 3. Creates new User Cipher Keys 4. Enciphers the user's mask @@ -162,7 +162,7 @@ Steps 1-2 are straightforward. For a better idea of how they work, see pyNKode. ##### User Cipher Keys Data Structure ``` set_key = generate_random_nonrepeating_list(keypad_size.props_per_key, max_numb=2**(8*numb_of_bytes)) -set_key = xor_lists(set_key, customer_attr.set_vals) +set_key = xor_lists(set_key, customer_prop.set_vals) UserCipherKeys( prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), @@ -211,17 +211,17 @@ Recall: ``` passcode = {{ user_passcode }} -passcode_server_attr = [customer.attributes.attr_vals[idx] for idx in passcode] -passcode_server_set = [customer.attributes.get_attr_set_val(attr) for attr in passcode_server_attr] +passcode_server_prop = [customer.cipher.prop_key[idx] for idx in passcode] +passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] -Passcode Set Vals: {{ passcode_server_attr }} -Passcode Attr Vals: {{ passcode_server_set }} +Passcode Set Vals: {{ passcode_server_prop }} +Passcode prop Vals: {{ passcode_server_set }} ``` ``` padded_passcode_server_set = user_cipher.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) -set_idx = [customer.attributes.get_set_index(set_val) for set_val in padded_passcode_server_set] +set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] mask_set_keys = [user_cipher.set_key[idx] for idx in set_idx] ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) @@ -233,18 +233,18 @@ Mask: {{ enciphered_nkode.mask }} #### Passcode Enciphering and Hashing -- ciphered_customer_attr = prop_key ^ customer_attr -- ciphered_passcode_i = pass_key_i ^ ciphered_customer_attr_i +- ciphered_customer_prop = prop_key ^ customer_prop +- ciphered_passcode_i = pass_key_i ^ ciphered_customer_prop_i - code = hash(ciphered_passcode, salt) ``` -ciphered_customer_attrs = xor_lists(customer.attributes.attr_vals, user_cipher.prop_key) -passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in passcode] +ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_cipher.prop_key) +passcode_ciphered_props = [ciphered_customer_props[idx] for idx in passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len -passcode_ciphered_attrs.extend([0 for _ in range(pad_len)]) +passcode_ciphered_props.extend([0 for _ in range(pad_len)]) -ciphered_code = xor_lists(passcode_ciphered_attrs, user_cipher.pass_key) +ciphered_code = xor_lists(passcode_ciphered_props, user_cipher.pass_key) passcode_bytes = int_array_to_bytes(ciphered_code) passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) @@ -281,8 +281,8 @@ success = api.login(customer_id, username, selected_keys_login) ### Validate Login Key Entry - decipher user mask and recover nkode set values -- get presumed attribute from key selection and set values -- encipher, salt, and hash presumed attribute values and compare them to the users hashed code +- get presumed property from key selection and set values +- encipher, salt, and hash presumed property values and compare them to the users hashed code #### Decipher Mask @@ -315,96 +315,96 @@ Passcode Sets: {{ login_passcode_sets }} ``` -### Get Presumed Attributes +### Get Presumed properties ``` -set_vals_idx = [customer.attributes.get_set_index(set_val) for set_val in passcode_sets] +set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets] -presumed_selected_attributes_idx = [] +presumed_selected_properties_idx = [] for idx in range(passcode_len): key_numb = selected_keys_login[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = customer.users[username].user_interface.get_attr_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_selected_attributes_idx.append(selected_attr_idx) + selected_prop_idx = customer.users[username].user_interface.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) + presumed_selected_properties_idx.append(selected_prop_idx) -Presumped Passcode: {{ presumed_selected_attributes_idx }} +Presumped Passcode: {{ presumed_selected_properties_idx }} Recall User Passcode: {{ user_passcode }} ``` ### Compare Enciphered Passcodes ``` -enciphered_nkode = user_cipher.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes) +enciphered_nkode = user_cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher) ``` If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid, and the login is successful. -## Renew Attributes -Attributes renew is invoked with the renew_attributes method: `api.renew_attributes(customer_id)` -The renew attributes process has three steps: -1. Renew Customer Attributes +## Renew properties +properties renew is invoked with the renew_properties method: `api.renew_properties(customer_id)` +The renew properties process has three steps: +1. Renew Customer properties 2. Renew User Keys 3. Refresh User on Login -When the customer calls the `renew_attributes` method, the method replaces the customer's attributes and set values. All its users go through an intermediate +When the customer calls the `renew_properties` method, the method replaces the customer's properties and set values. All its users go through an intermediate renewal step. The users fully renew after their first successful login. This first login refreshes their keys, salt, and hash with new values. ### Customer Renew -Old Customer attributes and set values are cached and copied to variables before renewal. +Old Customer properties and set values are cached and copied to variables before renewal. ``` -old_sets = customer.attributes.set_vals +old_sets = customer.cipher.set_key Customer Sets: {{ customer_set_vals }} ``` ``` -old_attr = customer.attributes.attr_vals +old_prop = customer.cipher.prop_key -Customer Attributes: -{% for attrs in customer_attr_view -%} -{{ attrs }} +Customer properties: +{% for props in customer_prop_view -%} +{{ props }} {% endfor %} ``` -After the renewal, the customer attributes and sets are new randomly generated values. +After the renewal, the customer properties and sets are new randomly generated values. ``` -api.renew_attributes(customer_id) +api.renew_properties(customer_id) -set_vals = customer.attributes.set_vals +set_vals = customer.cipher.set_key Customer Sets: {{ customer_new_set_vals }} ``` ``` -attr_vals = customer.attributes.attr_vals +prop_vals = customer.cipher.prop_key -Customer Attributes: -{% for attrs in customer_new_attr_view -%} -{{ attrs }} +Customer properties: +{% for props in customer_new_prop_view -%} +{{ props }} {% endfor %} ``` ### Renew User During the renewal, each user goes through a temporary transition period. ``` -attrs_xor = xor_lists(new_attrs, old_attrs) +props_xor = xor_lists(new_props, old_props) sets_xor = xor_lists(new_sets, old_sets) for user in customer.users.values(): user.renew = True user.user_cipher.set_key = xor_lists(user.user_cipher.set_key, sets_xor) - user.user_cipher.prop_key = xor_lists(user.user_cipher.prop_key, attrs_xor) + user.user_cipher.prop_key = xor_lists(user.user_cipher.prop_key, props_xor) ``` ##### User prop Key -The user's prop key was a randomly generated list of length `numb_of_keys * attr_per_key`. -Now each value in the prop key is `prop_key_i = old_prop_key_i ^ new_attr_i ^ old_attr_i`. -Recall in the login process, `ciphered_customer_attrs = prop_key ^ customer_attr`. -Since the customer_attr is now the new value, it gets canceled out, leaving: +The user's prop key was a randomly generated list of length `numb_of_keys * prop_per_key`. +Now each value in the prop key is `prop_key_i = old_prop_key_i ^ new_prop_i ^ old_prop_i`. +Recall in the login process, `ciphered_customer_props = prop_key ^ customer_prop`. +Since the customer_prop is now the new value, it gets canceled out, leaving: ``` -new_prop_key = old_prop_key ^ old_attr ^ new_attr -ciphered_customer_attrs = new_prop_key ^ new_attr -ciphered_customer_attrs = old_prop_key ^ old_attr # since new_attr cancel out +new_prop_key = old_prop_key ^ old_prop ^ new_prop +ciphered_customer_props = new_prop_key ^ new_prop +ciphered_customer_props = old_prop_key ^ old_prop # since new_prop cancel out ``` -Using the new customer attributes, we can validate the user's login attempt with the same hash. +Using the new customer properties, we can validate the user's login attempt with the same hash. ##### User Set Key -The user's set key was a randomly generated list of length `attr_per_key` xor `customer_set_vals`. +The user's set key was a randomly generated list of length `prop_per_key` xor `customer_set_vals`. The `old_set_vals` have been replaced with the new `new_set_vals`. The deciphering process described above remains the same. @@ -413,10 +413,10 @@ Once the user has a successful login, they get a new salt and cipher keys, and t with the new values. ``` user.user_cipher = UserCipherKeys.new( - customer.attributes.keypad_size, - customer.attributes.set_vals, + customer.cipher.keypad_size, + customer.cipher.set_key, user.user_cipher.max_nkode_len ) -user.enciphered_passcode = user.user_cipher.encipher_nkode(presumed_selected_attributes_idx, customer.attributes) +user.enciphered_passcode = user.user_cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) user.renew = False ``` diff --git a/docs/render_markdown.py b/docs/render_markdown.py index 90df4b3..9472500 100644 --- a/docs/render_markdown.py +++ b/docs/render_markdown.py @@ -15,9 +15,9 @@ def random_username() -> str: return "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) -def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, attrs_per_key: int) -> list[int]: - indices = [np.where(keypad == attr)[0][0] for attr in user_passcode] - return [int(index // attrs_per_key) for index in indices] +def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]: + indices = [np.where(keypad == prop)[0][0] for prop in user_passcode] + return [int(index // props_per_key) for index in indices] def visualize_keypad(keypad_list: np.ndarray, props_per_key: int): @@ -64,12 +64,12 @@ if __name__ == "__main__": customer = api.customers[customer_id] set_vals = customer.cipher.set_key - attr_vals = customer.cipher.prop_key - customer_attr_view = attr_vals.reshape(-1, keypad_size.props_per_key) + prop_vals = customer.cipher.prop_key + customer_prop_view = prop_vals.reshape(-1, keypad_size.props_per_key) - attr_keypad_view = attr_vals.reshape(-1, keypad_size.props_per_key) - attr_set_view = attr_keypad_view.T - set_attribute_dict = dict(zip(set_vals, attr_set_view)) + prop_keypad_view = prop_vals.reshape(-1, keypad_size.props_per_key) + prop_set_view = prop_keypad_view.T + set_property_dict = dict(zip(set_vals, prop_set_view)) session_id, signup_interface = api.generate_signup_keypad(customer_id) signup_keypad = signup_interface.reshape(-1, keypad_size.numb_of_keys) @@ -78,7 +78,7 @@ if __name__ == "__main__": passcode_len = 4 user_passcode = signup_interface[:passcode_len].tolist() selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) - server_side_attr = [customer.cipher.prop_key[idx] for idx in user_passcode] + server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) @@ -88,8 +88,8 @@ if __name__ == "__main__": success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) assert success - passcode_server_attr = [customer.cipher.prop_key[idx] for idx in user_passcode] - passcode_server_set = [customer.cipher.get_prop_set_val(attr) for attr in passcode_server_attr] + passcode_server_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] + passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] user_keys = customer.users[username].cipher @@ -100,14 +100,12 @@ if __name__ == "__main__": ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_server_set) ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key) mask = user_keys.encode_base64_str(ciphered_mask) - #ciphered_customer_attrs = xor_lists(customer.cipher.prop_key, user_keys.prop_key) - ciphered_customer_attrs = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key) - passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode] + #ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_keys.prop_key) + ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key) + passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len - passcode_ciphered_attrs.extend([0 for _ in range(pad_len)]) - #ciphered_code = xor_lists(passcode_ciphered_attrs, user_keys.pass_key) - ciphered_code = np.bitwise_xor(passcode_ciphered_attrs, user_keys.pass_key) - #passcode_bytes = int_array_to_bytes(ciphered_code) + passcode_ciphered_props.extend([0 for _ in range(pad_len)]) + ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key) passcode_bytes = ciphered_code.tobytes() passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt) @@ -144,37 +142,37 @@ if __name__ == "__main__": login_passcode_sets.append(int(set_vals[set_idx])) """ - GET PRESUMED ATTRIBUTES + GET PRESUMED properties """ set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in login_passcode_sets] - presumed_selected_attributes_idx = [] + presumed_selected_properties_idx = [] for idx in range(passcode_len): key_numb = selected_keys_login[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_selected_attributes_idx.append(selected_attr_idx) + selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) + presumed_selected_properties_idx.append(selected_prop_idx) """ RENEW KEYS """ - old_attrs = customer.cipher.prop_key.copy() + old_props = customer.cipher.prop_key.copy() old_sets = customer.cipher.set_key.copy() customer.cipher.renew() - new_attrs = customer.cipher.prop_key + new_props = customer.cipher.prop_key new_sets = customer.cipher.set_key - customer_new_attr_view = new_attrs.reshape(-1, keypad_size.props_per_key) + customer_new_prop_view = new_props.reshape(-1, keypad_size.props_per_key) """ RENEW USER """ - attrs_xor = np.bitwise_xor(new_attrs, old_attrs) + props_xor = np.bitwise_xor(new_props, old_props) sets_xor = np.bitwise_xor(new_sets, old_sets) for user in customer.users.values(): user.renew = True user.cipher.set_key = np.bitwise_xor(user.cipher.set_key, sets_xor) - user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, attrs_xor) + user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor) """ REFRESH USER KEYS @@ -184,31 +182,31 @@ if __name__ == "__main__": customer.cipher.set_key, user.cipher.max_nkode_len ) - user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_attributes_idx, customer.cipher) + user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) user.renew = False # Define some data to pass to the template data = { 'keypad_size': keypad_size, 'customer_set_vals': set_vals, - 'customer_attr_view': customer_attr_view, - 'set_attribute_dict': set_attribute_dict, + 'customer_prop_view': customer_prop_view, + 'set_property_dict': set_property_dict, 'signup_keypad': signup_keypad, 'username': 'test_user', 'user_passcode': user_passcode, 'selected_keys_set': selected_keys_set, - 'server_side_attr': server_side_attr, + 'server_side_prop': server_side_prop, 'confirm_keypad': confirm_keypad, 'selected_keys_confirm': selected_keys_confirm, 'user_cipher': user_keys, - 'passcode_server_attr': passcode_server_attr, + 'passcode_server_prop': passcode_server_prop, 'passcode_server_set': passcode_server_set, 'enciphered_nkode': enciphered_nkode, 'login_keypad': login_keypad, 'selected_login_keys': selected_keys_login, 'login_passcode_sets': login_passcode_sets, - 'presumed_selected_attributes_idx': presumed_selected_attributes_idx, - 'customer_new_attr_view': customer_new_attr_view, + 'presumed_selected_properties_idx': presumed_selected_properties_idx, + 'customer_new_prop_view': customer_new_prop_view, 'customer_new_set_vals': new_sets, } diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index 0ac3434..dca29ea 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -26,16 +26,16 @@ "\n", " for key in range(keypad_size.numb_of_keys):\n", " table += f\"\\n|key{key+1}|\"\n", - " table += \"|\".join([str(attr) for attr in keypad[key]])\n", + " table += \"|\".join([str(prop) for prop in keypad[key]])\n", " table += \"|\"\n", " return table\n", "\n", "\n", "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", - "attrs = [1, 10, 11, 100]\n", + "props = [1, 10, 11, 100]\n", "keypad = []\n", "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", - " keypad.extend([key_numb * attr for attr in attrs])\n", + " keypad.extend([key_numb * prop for prop in props])\n", "\n", "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad))\n", "\n", -- 2.49.1 From 1162bd54e17b139430e95e12f4526224a74c5198 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 09:35:19 -0500 Subject: [PATCH 27/85] rename attribute to property --- notebooks/dispersion_tutorial.ipynb | 22 +-- notebooks/nkode_tutorial.ipynb | 252 ++++++++++++++-------------- src/customer.py | 8 +- src/models.py | 2 +- src/user_cipher.py | 16 +- src/user_keypad.py | 31 ++-- src/user_signup_session.py | 6 +- src/utils.py | 14 +- test/test_nkode_api.py | 4 +- test/test_nkode_interface.py | 8 +- test/test_user_cipher_keys.py | 2 +- test/test_user_keypad.py | 10 +- 12 files changed, 187 insertions(+), 188 deletions(-) diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index dca29ea..c4effe6 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -44,8 +44,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T11:14:49.006056Z", - "start_time": "2025-03-14T11:14:48.964902Z" + "end_time": "2025-03-14T14:34:55.702615Z", + "start_time": "2025-03-14T14:34:55.694963Z" } }, "outputs": [ @@ -60,7 +60,7 @@ "output_type": "display_data" } ], - "execution_count": 1 + "execution_count": 4 }, { "cell_type": "code", @@ -73,8 +73,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T11:15:32.023001Z", - "start_time": "2025-03-14T11:15:32.009838Z" + "end_time": "2025-03-14T14:34:55.718161Z", + "start_time": "2025-03-14T14:34:55.714512Z" } }, "outputs": [ @@ -83,13 +83,13 @@ "text/plain": [ "" ], - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|4|40|44|400|\n|key2|3|30|33|300|\n|key3|2|20|22|200|\n|key4|1|10|11|100|\n|key5|5|50|55|500|" + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|1|10|11|100|\n|key2|4|40|44|400|\n|key3|3|30|33|300|\n|key4|2|20|22|200|\n|key5|5|50|55|500|" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 3 + "execution_count": 5 }, { "cell_type": "code", @@ -105,8 +105,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T11:17:30.317806Z", - "start_time": "2025-03-14T11:17:30.313082Z" + "end_time": "2025-03-14T14:34:55.731332Z", + "start_time": "2025-03-14T14:34:55.728135Z" } }, "outputs": [ @@ -115,13 +115,13 @@ "text/plain": [ "" ], - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|2|50|33|100|\n|key2|1|40|22|500|\n|key3|5|30|11|400|\n|key4|4|20|55|300|\n|key5|3|10|44|200|" + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|5|30|44|100|\n|key2|1|20|33|400|\n|key3|4|50|22|300|\n|key4|3|10|55|200|\n|key5|2|40|11|500|" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 5 + "execution_count": 6 } ], "metadata": { diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 30c99a1..204be73 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -12,12 +12,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.423890Z", - "start_time": "2025-03-14T14:18:14.420788Z" + "end_time": "2025-03-14T14:35:01.634660Z", + "start_time": "2025-03-14T14:35:01.631713Z" } }, "outputs": [], - "execution_count": 23 + "execution_count": 67 }, { "cell_type": "code", @@ -26,26 +26,26 @@ " return \"test_username\" + \"\".join([choice(ascii_lowercase) for _ in range(6)])\n", "\n", "\n", - "def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, attrs_per_key: int) -> list[int]:\n", - " indices = [np.where(keypad == attr)[0][0] for attr in user_passcode]\n", - " return [int(index // attrs_per_key) for index in indices]\n", + "def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]:\n", + " indices = [np.where(keypad == prop)[0][0] for prop in user_passcode]\n", + " return [int(index // props_per_key) for index in indices]\n", "\n", "\n", - "def keypad_view(keypad: np.ndarray, attrs_per_key: int):\n", + "def keypad_view(keypad: np.ndarray, props_per_key: int):\n", " print(\"Keypad View\")\n", - " interface_keypad = keypad.reshape(-1, attrs_per_key)\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-14T14:18:14.434459Z", - "start_time": "2025-03-14T14:18:14.430679Z" + "end_time": "2025-03-14T14:35:01.653961Z", + "start_time": "2025-03-14T14:35:01.650229Z" } }, "outputs": [], - "execution_count": 24 + "execution_count": 68 }, { "cell_type": "code", @@ -55,12 +55,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.453086Z", - "start_time": "2025-03-14T14:18:14.451030Z" + "end_time": "2025-03-14T14:35:01.663429Z", + "start_time": "2025-03-14T14:35:01.661723Z" } }, "outputs": [], - "execution_count": 25 + "execution_count": 69 }, { "cell_type": "markdown", @@ -100,12 +100,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.461793Z", - "start_time": "2025-03-14T14:18:14.457601Z" + "end_time": "2025-03-14T14:35:01.675378Z", + "start_time": "2025-03-14T14:35:01.671221Z" } }, "outputs": [], - "execution_count": 26 + "execution_count": 70 }, { "cell_type": "markdown", @@ -138,8 +138,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.472200Z", - "start_time": "2025-03-14T14:18:14.469516Z" + "end_time": "2025-03-14T14:35:01.692557Z", + "start_time": "2025-03-14T14:35:01.689797Z" } }, "outputs": [ @@ -147,17 +147,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Set Key: [57513 39162 37238 40595 30029 8573]\n", + "Customer Set Key: [21871 61274 2713 19029 26505 50649]\n", "Customer properties Key:\n", - "[14659 8841 6728 32702 39675 63074]\n", - "[19274 15250 33729 24532 34839 38348]\n", - "[ 2539 51139 60808 11045 55452 61221]\n", - "[55550 11904 10001 59259 26026 26740]\n", - "[59910 49698 45525 62003 49414 40644]\n" + "[42655 23174 1254 25551 11488 27210]\n", + "[60953 53938 20104 25057 1695 3339]\n", + "[32918 29164 48427 35503 41361 24040]\n", + "[44451 37655 36339 20189 49770 11581]\n", + "[31824 8094 2501 29432 12778 20956]\n" ] } ], - "execution_count": 27 + "execution_count": 71 }, { "cell_type": "markdown", @@ -171,14 +171,14 @@ "source": [ "set_properties_dict = dict(zip(customer.cipher.set_key, customer_prop_keypad.T))\n", "print(f\"Set to Properties Map:\")\n", - "for set_val, attrs in set_properties_dict.items():\n", - " print(f\"{set_val}: {attrs}\")" + "for set_val, props in set_properties_dict.items():\n", + " print(f\"{set_val}: {props}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.486583Z", - "start_time": "2025-03-14T14:18:14.484301Z" + "end_time": "2025-03-14T14:35:01.702167Z", + "start_time": "2025-03-14T14:35:01.699777Z" } }, "outputs": [ @@ -187,16 +187,16 @@ "output_type": "stream", "text": [ "Set to Properties Map:\n", - "57513: [14659 19274 2539 55550 59910]\n", - "39162: [ 8841 15250 51139 11904 49698]\n", - "37238: [ 6728 33729 60808 10001 45525]\n", - "40595: [32702 24532 11045 59259 62003]\n", - "30029: [39675 34839 55452 26026 49414]\n", - "8573: [63074 38348 61221 26740 40644]\n" + "21871: [42655 60953 32918 44451 31824]\n", + "61274: [23174 53938 29164 37655 8094]\n", + "2713: [ 1254 20104 48427 36339 2501]\n", + "19029: [25551 25057 35503 20189 29432]\n", + "26505: [11488 1695 41361 49770 12778]\n", + "50649: [27210 3339 24040 11581 20956]\n" ] } ], - "execution_count": 28 + "execution_count": 72 }, { "metadata": {}, @@ -218,8 +218,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.510067Z", - "start_time": "2025-03-14T14:18:14.507204Z" + "end_time": "2025-03-14T14:35:01.722470Z", + "start_time": "2025-03-14T14:35:01.719357Z" } }, "cell_type": "code", @@ -232,15 +232,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[ 1 7 13 19 25]\n", - " [ 0 6 12 18 24]\n", - " [ 5 11 17 23 29]\n", - " [ 2 8 14 20 26]\n", - " [ 4 10 16 22 28]]\n" + "[[24 0 12 6 18]\n", + " [28 4 16 10 22]\n", + " [27 3 15 9 21]\n", + " [26 2 14 8 20]\n", + " [25 1 13 7 19]]\n" ] } ], - "execution_count": 29 + "execution_count": 73 }, { "metadata": {}, @@ -253,8 +253,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.535581Z", - "start_time": "2025-03-14T14:18:14.531909Z" + "end_time": "2025-03-14T14:35:01.746158Z", + "start_time": "2025-03-14T14:35:01.742671Z" } }, "cell_type": "code", @@ -266,8 +266,8 @@ "selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_keypad, keypad_size.numb_of_keys)\n", "print(f\"User Passcode: {user_passcode}\")\n", "print(f\"Selected Keys\\n{selected_keys_set}\")\n", - "server_side_attr = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", - "print(f\"User Passcode Server-side properties: {server_side_attr}\")" + "server_side_prop = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", + "print(f\"User Passcode Server-side properties: {server_side_prop}\")" ], "outputs": [ { @@ -275,25 +275,25 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [ 1 7 13 19 25]\n", - "Key 1: [ 0 6 12 18 24]\n", - "Key 2: [ 5 11 17 23 29]\n", - "Key 3: [ 2 8 14 20 26]\n", - "Key 4: [ 4 10 16 22 28]\n", - "User Passcode: [ 1 7 13 19]\n", + "Key 0: [24 0 12 6 18]\n", + "Key 1: [28 4 16 10 22]\n", + "Key 2: [27 3 15 9 21]\n", + "Key 3: [26 2 14 8 20]\n", + "Key 4: [25 1 13 7 19]\n", + "User Passcode: [24 0 12 6]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side properties: [8841, 15250, 51139, 11904]\n" + "User Passcode Server-side properties: [31824, 42655, 32918, 60953]\n" ] } ], - "execution_count": 30 + "execution_count": 74 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.556541Z", - "start_time": "2025-03-14T14:18:14.553217Z" + "end_time": "2025-03-14T14:35:01.770157Z", + "start_time": "2025-03-14T14:35:01.766784Z" } }, "cell_type": "code", @@ -309,23 +309,23 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [ 0 11 16 20 25]\n", - "Key 1: [ 2 6 13 22 29]\n", - "Key 2: [ 4 8 17 19 24]\n", - "Key 3: [ 1 10 12 23 26]\n", - "Key 4: [ 5 7 14 18 28]\n", + "Key 0: [26 3 16 6 19]\n", + "Key 1: [27 0 13 10 20]\n", + "Key 2: [24 4 14 7 21]\n", + "Key 3: [28 1 15 8 18]\n", + "Key 4: [25 2 12 9 22]\n", "Selected Keys\n", - "[3, 4, 1, 2]\n" + "[2, 1, 4, 0]\n" ] } ], - "execution_count": 31 + "execution_count": 75 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.884832Z", - "start_time": "2025-03-14T14:18:14.575902Z" + "end_time": "2025-03-14T14:35:02.101712Z", + "start_time": "2025-03-14T14:35:01.792851Z" } }, "cell_type": "code", @@ -343,7 +343,7 @@ ] } ], - "execution_count": 32 + "execution_count": 76 }, { "metadata": {}, @@ -362,8 +362,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.894865Z", - "start_time": "2025-03-14T14:18:14.889948Z" + "end_time": "2025-03-14T14:35:02.115075Z", + "start_time": "2025-03-14T14:35:02.110397Z" } }, "cell_type": "code", @@ -389,22 +389,22 @@ " max_nkode_len=customer.nkode_policy.max_nkode_len, \n", ")\n", "\n", - "passcode_server_attr = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", - "passcode_server_set = [int(customer.cipher.get_prop_set_val(attr)) for attr in passcode_server_attr]\n", + "passcode_server_prop = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", + "passcode_server_set = [int(customer.cipher.get_prop_set_val(prop)) for prop in passcode_server_prop]\n", "print(f\"Passcode Set Vals: {passcode_server_set}\")\n", - "print(f\"Passcode Attr Vals: {passcode_server_attr}\")" + "print(f\"Passcode prop Vals: {passcode_server_prop}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [39162, 39162, 39162, 39162]\n", - "Passcode Attr Vals: [8841, 15250, 51139, 11904]\n" + "Passcode Set Vals: [21871, 21871, 21871, 21871]\n", + "Passcode prop Vals: [31824, 42655, 32918, 60953]\n" ] } ], - "execution_count": 33 + "execution_count": 77 }, { "metadata": {}, @@ -426,8 +426,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:14.917821Z", - "start_time": "2025-03-14T14:18:14.914851Z" + "end_time": "2025-03-14T14:35:02.132228Z", + "start_time": "2025-03-14T14:35:02.129307Z" } }, "cell_type": "code", @@ -441,7 +441,7 @@ "mask = user_keys.encode_base64_str(ciphered_mask)" ], "outputs": [], - "execution_count": 34 + "execution_count": 78 }, { "metadata": {}, @@ -450,16 +450,16 @@ "#### Encipher Passcode\n", "UserCipherKeys.encipher_salt_hash_code:\n", "\n", - "- ciphered_customer_attr = alpha_key ^ customer_attr\n", - "- ciphered_passcode_i = pass_key_i ^ ciphered_customer_attr_i\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-14T14:18:15.243102Z", - "start_time": "2025-03-14T14:18:14.937865Z" + "end_time": "2025-03-14T14:35:02.448903Z", + "start_time": "2025-03-14T14:35:02.143953Z" } }, "cell_type": "code", @@ -468,13 +468,13 @@ "import hashlib\n", "import base64\n", "\n", - "ciphered_customer_attrs = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", - "passcode_ciphered_attrs = [ciphered_customer_attrs[idx] for idx in user_passcode]\n", + "ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", + "passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", "\n", - "passcode_ciphered_attrs.extend([0 for _ in range(pad_len)])\n", + "passcode_ciphered_props.extend([0 for _ in range(pad_len)])\n", "\n", - "ciphered_code = np.bitwise_xor(passcode_ciphered_attrs, user_keys.pass_key)\n", + "ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key)\n", "\n", "#passcode_bytes = int_array_to_bytes(ciphered_code)\n", "passcode_bytes = ciphered_code.tobytes()\n", @@ -483,13 +483,13 @@ "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 35 + "execution_count": 79 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:15.249186Z", - "start_time": "2025-03-14T14:18:15.247147Z" + "end_time": "2025-03-14T14:35:02.459349Z", + "start_time": "2025-03-14T14:35:02.457325Z" } }, "cell_type": "code", @@ -502,7 +502,7 @@ ")" ], "outputs": [], - "execution_count": 36 + "execution_count": 80 }, { "metadata": {}, @@ -516,8 +516,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:15.563030Z", - "start_time": "2025-03-14T14:18:15.257823Z" + "end_time": "2025-03-14T14:35:02.775720Z", + "start_time": "2025-03-14T14:35:02.468856Z" } }, "cell_type": "code", @@ -535,17 +535,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [0 1 2 3 4 5]\n", - "Key 1: [ 6 7 8 9 10 11]\n", + "Key 0: [24 25 26 27 28 29]\n", + "Key 1: [0 1 2 3 4 5]\n", "Key 2: [12 13 14 15 16 17]\n", - "Key 3: [18 19 20 21 22 23]\n", - "Key 4: [24 25 26 27 28 29]\n", + "Key 3: [ 6 7 8 9 10 11]\n", + "Key 4: [18 19 20 21 22 23]\n", "Selected Keys: [0, 1, 2, 3]\n", "True\n" ] } ], - "execution_count": 37 + "execution_count": 81 }, { "metadata": {}, @@ -577,8 +577,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:15.574718Z", - "start_time": "2025-03-14T14:18:15.571571Z" + "end_time": "2025-03-14T14:35:02.787442Z", + "start_time": "2025-03-14T14:35:02.784207Z" } }, "cell_type": "code", @@ -598,11 +598,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[39162, 39162, 39162, 39162]\n" + "[21871, 21871, 21871, 21871]\n" ] } ], - "execution_count": 38 + "execution_count": 82 }, { "metadata": {}, @@ -612,8 +612,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:15.591180Z", - "start_time": "2025-03-14T14:18:15.587992Z" + "end_time": "2025-03-14T14:35:02.805826Z", + "start_time": "2025-03-14T14:35:02.802523Z" } }, "cell_type": "code", @@ -624,8 +624,8 @@ "for idx in range(passcode_len):\n", " key_numb = selected_keys_login[idx]\n", " set_idx = set_vals_idx[idx]\n", - " selected_attr_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx)\n", - " presumed_selected_properties_idx.append(selected_attr_idx)\n", + " selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx)\n", + " presumed_selected_properties_idx.append(selected_prop_idx)\n", "\n", "print(user_passcode.tolist() == presumed_selected_properties_idx)" ], @@ -638,7 +638,7 @@ ] } ], - "execution_count": 39 + "execution_count": 83 }, { "metadata": {}, @@ -648,8 +648,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:15.916787Z", - "start_time": "2025-03-14T14:18:15.610843Z" + "end_time": "2025-03-14T14:35:03.129079Z", + "start_time": "2025-03-14T14:35:02.822467Z" } }, "cell_type": "code", @@ -666,7 +666,7 @@ ] } ], - "execution_count": 40 + "execution_count": 84 }, { "metadata": {}, @@ -682,8 +682,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:16.539065Z", - "start_time": "2025-03-14T14:18:15.926843Z" + "end_time": "2025-03-14T14:35:03.757448Z", + "start_time": "2025-03-14T14:35:03.142511Z" } }, "cell_type": "code", @@ -708,14 +708,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: 4NCIa/fFtx0udGaF1ub+O/6oU04=, code: $2b$12$XuGO.allDcb91peVIjwsq./Ba/TZI3wxAc0acgWkApzkzGWfwPQCC\n", - "mask: 4NCIa/fFtx0udGaF1ub+O/6oU04=, code: $2b$12$XuGO.allDcb91peVIjwsq./Ba/TZI3wxAc0acgWkApzkzGWfwPQCC\n", + "mask: QUR1wAPG8jJh/nG1LbgMIupAY1I=, code: $2b$12$0Ia665myY64l8.UyKRc4tu/OR.0BG6KFLyXcuDdIXrDkpP6cjo7xO\n", + "mask: QUR1wAPG8jJh/nG1LbgMIupAY1I=, code: $2b$12$0Ia665myY64l8.UyKRc4tu/OR.0BG6KFLyXcuDdIXrDkpP6cjo7xO\n", "True\n", - "mask: ceaWZR+hNRpEkj3fq1cfPu1Zyok=, code: $2b$12$q7lqdTj6qBMDDGEog9Pq3.M2Wso0TI8cx4/PhOK/fE1mhsws2FGe.\n" + "mask: SB1G0mGYnu2Fy2usk08r9uTTugo=, code: $2b$12$pKJtlWu9gwXqA4b.Q/WEZ.yzz9ntnXnFSindkyVOWa3sNOoVd0LLK\n" ] } ], - "execution_count": 41 + "execution_count": 85 }, { "metadata": {}, @@ -729,8 +729,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:16.551437Z", - "start_time": "2025-03-14T14:18:16.547371Z" + "end_time": "2025-03-14T14:35:03.770972Z", + "start_time": "2025-03-14T14:35:03.766802Z" } }, "cell_type": "code", @@ -742,7 +742,7 @@ "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 42 + "execution_count": 86 }, { "metadata": {}, @@ -755,8 +755,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:16.568881Z", - "start_time": "2025-03-14T14:18:16.566223Z" + "end_time": "2025-03-14T14:35:03.781705Z", + "start_time": "2025-03-14T14:35:03.779309Z" } }, "cell_type": "code", @@ -769,7 +769,7 @@ " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 43 + "execution_count": 87 }, { "metadata": {}, @@ -779,8 +779,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:18:16.884432Z", - "start_time": "2025-03-14T14:18:16.572989Z" + "end_time": "2025-03-14T14:35:04.107750Z", + "start_time": "2025-03-14T14:35:03.797025Z" } }, "cell_type": "code", @@ -794,7 +794,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": 44 + "execution_count": 88 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 5f7c925..b173c36 100644 --- a/src/customer.py +++ b/src/customer.py @@ -38,11 +38,11 @@ class Customer: for idx in range(passcode_len): key_numb = selected_keys[idx] set_idx = set_vals_idx[idx] - selected_attr_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_property_idxs.append(selected_attr_idx) + selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) + presumed_property_idxs.append(selected_prop_idx) - enciphered_attr = user.cipher.encipher_salt_hash_code(presumed_property_idxs, self.cipher) - if enciphered_attr != user.enciphered_passcode.code: + enciphered_prop = user.cipher.encipher_salt_hash_code(presumed_property_idxs, self.cipher) + if enciphered_prop != user.enciphered_passcode.code: return False if user.renew: diff --git a/src/models.py b/src/models.py index 101aafa..04e1158 100644 --- a/src/models.py +++ b/src/models.py @@ -12,7 +12,7 @@ class NKodePolicy: min_nkode_len: int = 4 distinct_sets: int = 0 distinct_properties: int = 4 - byte_len: int = 2 # Todo: this should change the total number of bytes an attribute or set value can be + byte_len: int = 2 # Todo: this should change the total number of bytes an properities or set value can be lock_out: int = 5 expiration: int = -1 # in seconds -1 means nkode never expires diff --git a/src/user_cipher.py b/src/user_cipher.py index 0a964ca..ee9f15d 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -74,8 +74,8 @@ class UserCipher: customer_cipher: CustomerCipher ) -> EncipheredNKode: passcode_prop_idx_array = np.array(passcode_prop_idx, dtype=np.uint16) - passcode_attrs = np.array([customer_cipher.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) - passcode_sets = np.array([customer_cipher.get_prop_set_val(attr) for attr in passcode_attrs], dtype=np.uint16) + passcode_props = np.array([customer_cipher.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) + passcode_sets = np.array([customer_cipher.get_prop_set_val(prop) for prop in passcode_props], dtype=np.uint16) mask = self.encipher_mask(passcode_sets.tolist(), customer_cipher) code = self.encipher_salt_hash_code(passcode_prop_idx, customer_cipher) return EncipheredNKode( @@ -90,24 +90,24 @@ class UserCipher: ) -> str: passcode_prop_idx_array = np.array(passcode_prop_idx, dtype=np.uint16) passcode_len = len(passcode_prop_idx_array) - passcode_attrs = np.array([customer_prop.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) + passcode_props = np.array([customer_prop.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) passcode_cipher = self.pass_key.copy() for idx in range(passcode_len): - attr_idx = passcode_prop_idx_array[idx] - passcode_cipher[idx] = passcode_cipher[idx] ^ self.prop_key[attr_idx] ^ passcode_attrs[idx] + prop_idx = passcode_prop_idx_array[idx] + passcode_cipher[idx] = passcode_cipher[idx] ^ self.prop_key[prop_idx] ^ passcode_props[idx] return self._hash_passcode(passcode_cipher) def encipher_mask( self, passcode_sets: list[int], - customer_attributes: CustomerCipher + customer_properites: CustomerCipher ) -> str: - padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_attributes.set_key) + padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_properites.set_key) # Get indices of set values - set_idx = np.array([customer_attributes.get_set_index(set_val) for set_val in padded_passcode_sets], + set_idx = np.array([customer_properites.get_set_index(set_val) for set_val in padded_passcode_sets], dtype=np.uint16) mask_set_keys = np.array([self.set_key[idx] for idx in set_idx], dtype=np.uint16) diff --git a/src/user_keypad.py b/src/user_keypad.py index f59bb72..a082ebb 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -22,13 +22,12 @@ class UserKeypad: raise ValueError("Keypad size is dispersable") self.random_keypad_shuffle() keypad_matrix = self.keypad_matrix() - attr_set_view = keypad_matrix.T - #attr_set_view = secure_fisher_yates_shuffle(attr_set_view) - attr_set_view = np.random.permutation(attr_set_view) - attr_set_view = attr_set_view[:self.keypad_size.numb_of_keys] - keypad_matrix = attr_set_view.reshape(-1)#matrix_transpose(attr_set_view) + prop_set_view = keypad_matrix.T + prop_set_view = np.random.permutation(prop_set_view) + prop_set_view = prop_set_view[:self.keypad_size.numb_of_keys] + keypad_matrix = prop_set_view.reshape(-1) return UserKeypad( - keypad=keypad_matrix.reshape(-1),#matrix_to_list(keypad_matrix), + keypad=keypad_matrix.reshape(-1), keypad_size=KeypadSize( numb_of_keys=self.keypad_size.numb_of_keys, props_per_key=self.keypad_size.numb_of_keys @@ -56,10 +55,10 @@ class UserKeypad: #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) shuffled_keys = rng.permutation(user_keypad_matrix, axis=0) #prop_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] - attr_rotation = rng.permutation(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] + prop_rotation = rng.permutation(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] dispersed_keypad = random_property_rotation( shuffled_keys, - attr_rotation.tolist(), + prop_rotation.tolist(), ) self.keypad = dispersed_keypad.reshape(-1) @@ -72,11 +71,11 @@ class UserKeypad: #user_keypad_matrix = self.keypad_matrix() #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) #keypad_by_sets = [] - #for idx, attrs in enumerate(matrix_transpose(shuffled_keys)): + #for idx, props in enumerate(matrix_transpose(shuffled_keys)): # if idx in selected_sets: - # keypad_by_sets.append(secure_fisher_yates_shuffle(attrs)) + # keypad_by_sets.append(secure_fisher_yates_shuffle(props)) # else: - # keypad_by_sets.append(attrs) + # keypad_by_sets.append(props) #self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) pass @@ -85,9 +84,9 @@ class UserKeypad: user_keypad_keypad = self.keypad_matrix() graph = {} for key in user_keypad_keypad: - for attr in key: - graph[attr] = set(key) - graph[attr].remove(attr) + for prop in key: + graph[prop] = set(key) + graph[prop].remove(prop) return graph def get_prop_idx_by_keynumb_setidx(self, key_numb: int, set_idx: int) -> int: @@ -95,5 +94,5 @@ class UserKeypad: raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not (0 <= set_idx < self.keypad_size.props_per_key): raise ValueError(f"set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") - keypad_attr_idx = self.keypad_matrix() - return int(keypad_attr_idx[key_numb][set_idx]) + keypad_prop_idx = self.keypad_matrix() + return int(keypad_prop_idx[key_numb][set_idx]) diff --git a/src/user_signup_session.py b/src/user_signup_session.py index 26ba1f5..25cb835 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -19,14 +19,14 @@ class UserSignupSession: 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): raise ValueError("Key values must be within valid range") - attrs_per_key = self.keypad_size.props_per_key + props_per_key = self.keypad_size.props_per_key set_key_entry = self.set_key_entry if len(set_key_entry) != len(confirm_key_entry): raise ValueError("Key entry lengths must match") set_keypad = self.set_keypad confirm_keypad = self.confirm_keypad - set_key_vals = [set_keypad[key * attrs_per_key:(key + 1) * attrs_per_key] for key in set_key_entry] - confirm_key_vals = [confirm_keypad[key * attrs_per_key:(key + 1) * attrs_per_key] for key in + set_key_vals = [set_keypad[key * props_per_key:(key + 1) * props_per_key] for key in set_key_entry] + confirm_key_vals = [confirm_keypad[key * props_per_key:(key + 1) * props_per_key] for key in confirm_key_entry] passcode = [] for idx in range(len(set_key_entry)): diff --git a/src/utils.py b/src/utils.py index f9e7125..bfb68da 100644 --- a/src/utils.py +++ b/src/utils.py @@ -2,13 +2,13 @@ import numpy as np def random_property_rotation( user_keypad: np.ndarray, - attr_rotation: list[int] + prop_rotation: list[int] ) -> np.ndarray: transposed = user_keypad.T - if len(attr_rotation) != len(transposed): - raise ValueError("prop_rotation must be the same length as the number of attributes") - for idx, attr_set in enumerate(transposed): - rotation = attr_rotation[idx] - rotation = rotation % len(attr_set) if len(attr_set) > 0 else 0 - transposed[idx] = np.roll(attr_set, rotation) + if len(prop_rotation) != len(transposed): + raise ValueError("prop_rotation must be the same length as the number of properties") + for idx, prop_set in enumerate(transposed): + rotation = prop_rotation[idx] + rotation = rotation % len(prop_set) if len(prop_set) > 0 else 0 + transposed[idx] = np.roll(prop_set, rotation) return transposed.T diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 341bd88..58f6f07 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -20,7 +20,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): session_id, set_keypad = nkode_api.generate_signup_keypad(customer_id) user_passcode = set_keypad[:passocode_len] - signup_key_selection = lambda keypad: [int(np.where(keypad == attr)[0][0]) // keypad_size.numb_of_keys for attr in user_passcode] + 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) @@ -33,7 +33,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): ) assert successful_confirm - sign_in_key_selection = lambda keypad: [int(np.where(keypad ==attr)[0][0]) // keypad_size.props_per_key for attr in user_passcode] + sign_in_key_selection = lambda keypad: [int(np.where(keypad ==prop)[0][0]) // keypad_size.props_per_key for prop in user_passcode] login_keypad = nkode_api.get_login_keypad(username, customer_id) login_key_selection = sign_in_key_selection(login_keypad) successful_login = nkode_api.login(customer_id, username, login_key_selection) diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index b56908c..8dce0fb 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -7,9 +7,9 @@ from src.models import KeypadSize "keypad_size", [KeypadSize(numb_of_keys=10, props_per_key=11)] ) -def test_attr_set_idx(keypad_size): +def test_prop_set_idx(keypad_size): user_keypad = UserKeypad.create(keypad_size) - for attr_idx in range(keypad_size.numb_of_props): - user_keypad_idx = user_keypad.keypad[attr_idx] + for prop_idx in range(keypad_size.numb_of_props): + user_keypad_idx = user_keypad.keypad[prop_idx] - assert (attr_idx % keypad_size.props_per_key == user_keypad_idx % keypad_size.props_per_key) + assert (prop_idx % keypad_size.props_per_key == user_keypad_idx % keypad_size.props_per_key) diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 9f82e66..3671420 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -35,7 +35,7 @@ def test_decode_mask(keypad_size, max_nkode_len): user_keys = UserCipher.create(keypad_size, set_vals, max_nkode_len) passcode = user_keys.encipher_nkode(passcode_entry, customer) - orig_passcode_set_vals = [customer.get_prop_set_val(attr) for attr in passcode_values] + orig_passcode_set_vals = [customer.get_prop_set_val(prop) for prop in passcode_values] passcode_set_vals = user_keys.decipher_mask(passcode.mask, set_vals, len(passcode_entry)) assert (len(passcode_set_vals) == len(orig_passcode_set_vals)) assert (all(orig_passcode_set_vals[idx] == passcode_set_vals[idx] for idx in range(len(passcode_set_vals)))) diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index 811c1f4..b7deadf 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -13,15 +13,15 @@ def test_dispersion(user_keypad): pre_dispersion_graph = user_keypad.property_adjacency_graph() user_keypad.disperse_keypad() post_dispersion_graph = user_keypad.property_adjacency_graph() - for attr, adj_graph in pre_dispersion_graph.items(): - assert (adj_graph.isdisjoint(post_dispersion_graph[attr])) + for prop, adj_graph in pre_dispersion_graph.items(): + assert (adj_graph.isdisjoint(post_dispersion_graph[prop])) -#def test_shuffle_attrs(user_keypad): +#def test_shuffle_props(user_keypad): # """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get # expected statistical outcomes like: -# - every attribute gets to every key with a uniform distribution -# - every attribute is adjacent to every other attribute with uniform distribution +# - every property gets to every key with a uniform distribution +# - every property is adjacent to every other property with uniform distribution # - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) # """ # pre_shuffle_keypad = user_keypad.keypad -- 2.49.1 From 41a7e14fb4cad573e979c6c4e0893a94394f995d Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 10:22:22 -0500 Subject: [PATCH 28/85] refactor nkode_authentication_template.md --- README.md | 446 ++++++++++++++++++ ...ication_template.md => readme_template.md} | 4 +- docs/{render_markdown.py => render_readme.py} | 5 +- test/test_nkode_api.py | 6 +- 4 files changed, 454 insertions(+), 7 deletions(-) create mode 100644 README.md rename docs/{nkode_authentication_template.md => readme_template.md} (98%) rename docs/{render_markdown.py => render_readme.py} (98%) diff --git a/README.md b/README.md new file mode 100644 index 0000000..ff4768f --- /dev/null +++ b/README.md @@ -0,0 +1,446 @@ +# README +Play around with the code in /notebooks + +## Customer Creation +Before creating a user, a customer generates random properties and set +values. The customers manage users. They define an nKode policy, keypad's dimensions, +properties/sets in the keypad, and the frequency of property renewal. +### nKode Policy and Keypad Size +An nKode policy defines: +
    +
  • the maximum length of a user's nKode
  • +
  • the minimum length of a user's nKode
  • +
  • the number of unique set values in a user's nKode
  • +
  • the number of unique values in a user's nKode
  • +
  • the number of bytes in an property and set
  • +
+ +The keypad size defines: +
    +
  • the number of keys in the keypad displayed to the user
  • +
  • properties per key
  • +
+ +To be [dispersion](nkode_concepts.md/#dispersion-resistant-keypad) resistant, the number of properties must be greater than the number of keys. + +``` +api = NKodeAPI() + +policy = NKodePolicy( + max_nkode_len=10, + min_nkode_len=4, + distinct_sets=0, + distinct_properties=4, + byte_len=2 +) + +keypad_size = KeypadSize( + numb_of_keys = 5, + props_per_key = 6 # aka number of sets +) + +customer_id = api.create_new_customer(keypad_size, policy) +customer = api.customers[customer_id] +``` +### Customer properties and Sets +A customer has users and defines the properties and set values for all its users. +Since our customer has 5 keys and 6 properties per key, +this gives a customer keypad of 30 distinct properties and 6 distinct property sets. +Each property belongs to one of the 6 sets. Each property and set value is a unique 2-byte integer in this example. + +``` +set_vals = customer.cipher.set_key + +Customer Sets: [51397 49224 50087 24444 43554 21522] +``` + +``` +prop_vals = customer.cipher.prop_key +keypad_view(prop_vals, keypad_size.props_per_key) + +Customer properties: +[65030 40058 49729 42519 32475 21731] +[19446 3351 17075 17586 20753 15754] +[19712 56685 43602 30750 54931 27419] +[40397 10398 13477 26037 17943 47642] +[58359 15284 53370 4343 16407 46898] + +``` + +properties organized by set: +``` +prop_set_view = matrix_transpose(prop_keypad_view) +set_property_dict = dict(zip(set_vals, prop_set_view)) + +Set to property Map: +51397 : [65030 19446 19712 40397 58359] +49224 : [40058 3351 56685 10398 15284] +50087 : [49729 17075 43602 13477 53370] +24444 : [42519 17586 30750 26037 4343] +43554 : [32475 20753 54931 17943 16407] +21522 : [21731 15754 27419 47642 46898] + +``` + +## User Signup +Now that we have a customer, we can create users. To create a new user: + +1. Generate a random keypad +2. The user sets their nKode and sends their selection to the server +3. The user confirms their nKode. If the user's nKode matches the policy, the server creates the user. +### Random keypad Generation +The user's keypad must be dispersable so the server can determine the user's nkode. +The server randomly drops property sets until +the number of properties equals the number of keys, making the keypad dispersable. +In our case, the server randomly drops 1 property set. +to give us a 5 X 5 keypad with possible index values ranging from 0-29. +Each value in the keypad is the index value of a customer property. +The user never learns what their "real" property is. They do not see the index value representing their nKode or +the customer server-side value. + +``` +session_id, signup_keypad = api.generate_index_keypad(customer_id) +signup_keypad_keypad = list_to_matrix(signup_keypad, keypad_size.props_per_key) + +Signup Keypad: +Key 1: [19 7 25 1 13] +Key 2: [18 6 24 0 12] +Key 3: [21 9 27 3 15] +Key 4: [23 11 29 5 17] +Key 5: [20 8 26 2 14] + +``` + +### Set nKode +The user identifies properties in the keypad they want in their nkode. Each property has an index value. +Below, the user has selected `[19, 7, 25, 1]`. These index values can be represented by anything in the GUI. +The only requirement is that the GUI properties be associated with the same index every time the user logs in. +If users want to change anything about their keypad, they must also change their nkode. + +``` +username = test_user +user_passcode = [19, 7, 25, 1] +selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_keypad, keypad_size.props_per_key) + +Selected Keys +[0, 0, 0, 0] +``` + +The user's passcode server side properties are: +``` +server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] + +User Passcode Server-side properties: [np.int64(10398), np.int64(3351), np.int64(15284), np.int64(40058)] +``` + +### Confirm nKode +The user submits the set keypad to the server and receives the _confirm keypad_ as a response. +The user finds their nKode again. + +``` +confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, session_id) +keypad_view(confirm_keypad, keypad_size.numb_of_keys) +selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_keypad, keypad_size.numb_of_keys) + +Confirm Keypad: +Key 1: [20 7 27 5 12] +Key 2: [23 9 26 0 13] +Key 3: [18 8 29 1 15] +Key 4: [19 11 24 3 14] +Key 5: [21 6 25 2 17] + +Selected Keys: +[3, 0, 4, 2] +``` + +The user submits their confirmation key selection and the user is created +``` +success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) +``` + +### Passcode Enciphering, Hashing, and Salting +When a new user creates an nKode, the server caches its set and confirms the keypad and the user's key selection. +On the last api.confirm_nkode, the server: + +1. Deduces the user's properties +2. Validates the Passcode against the nKodePolicy +3. Creates new User Cipher Keys +4. Enciphers the user's mask +5. Enciphers, salts, and hashes the user's passcode + +Steps 1-2 are straightforward. For a better idea of how they work, see pyNKode. + +#### User Cipher Keys + +##### User Cipher Keys Data Structure +``` +set_key = generate_random_nonrepeating_list(keypad_size.props_per_key, max_numb=2**(8*numb_of_bytes)) +set_key = xor_lists(set_key, customer_prop.set_vals) + +UserCipherKeys( + prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), + pass_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), + mask_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), + set_key=set_key, + salt=bcrypt.gensalt(), + max_nkode_len=max_nkode_len +) +``` + +##### User Cipher Keys Values +``` +user_cipher = UserCipherKeys( + prop_key = [ 2923 16019 14458 50197 31207 7212 56686 44981 2641 64112 13044 29822 + 1902 22608 40919 35763 49353 20507 18363 34108 32269 6440 21357 37870 + 60382 18170 45147 13683 20896 12198], + pass_key = [31251 55189 60990 1342 51754 25296 19081 956 41188 43289], + mask_key = [54532 41537 22695 64404 28419 7322 24742 54924 2951 57084], + set_key = [ 3824 27422 49987 58720 10692 60061], + salt = b'$2b$12$iLYVBzbu9DVSg7S.ZBzB..', + max_nkode_len = 10 +) +``` + +The method UserCipherKeys.encipher_nkode secures a user's nKode in the database. This method is called in api.confirm_nkode + +``` +class EncipheredNKode(BaseModel): + code: str + mask: str +``` + +#### Mask Enciphering + +Recall: + +- set_key_i = (set_rand_numb_i ^ set_val_i) +- mask_key_i = mask_rand_numb_i +- padded_passcode_server_set_i = set_val_i +- len(set_key) == len(mask_key) == (padded_passcode_server_set) == max_nkode_len == 10 + where i is the index + +- mask_i = mask_key_i ^ padded_passcode_server_set_i ^ set_key_i +- mask_i = mask_rand_num_i ^ set_val_i ^ set_rand_numb_i ^ set_val_i +- mask_i = mask_rand_num_i ^ set_rand_numb_i # set_val_i is cancelled out + + +``` +passcode = [19, 7, 25, 1] +passcode_server_prop = [customer.cipher.prop_key[idx] for idx in passcode] +passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] + +Passcode Set Vals: [np.int64(10398), np.int64(3351), np.int64(15284), np.int64(40058)] +Passcode prop Vals: [49224, 49224, 49224, 49224] +``` + +``` +padded_passcode_server_set = user_cipher.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) + +set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] +mask_set_keys = [user_cipher.set_key[idx] for idx in set_idx] + +ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) +ciphered_mask = xor_lists(ciphered_mask, user_cipher.mask_key) + +mask = user_cipher.encode_base64_str(ciphered_mask) +Mask: c6kE7P4KXTm3d3KmDprj8dPzBog= +``` + +#### Passcode Enciphering and Hashing + +- ciphered_customer_prop = prop_key ^ customer_prop +- ciphered_passcode_i = pass_key_i ^ ciphered_customer_prop_i +- code = hash(ciphered_passcode, salt) + +``` +ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_cipher.prop_key) +passcode_ciphered_props = [ciphered_customer_props[idx] for idx in passcode] +pad_len = customer.nkode_policy.max_nkode_len - passcode_len + +passcode_ciphered_props.extend([0 for _ in range(pad_len)]) + +ciphered_code = xor_lists(passcode_ciphered_props, user_cipher.pass_key) + +passcode_bytes = int_array_to_bytes(ciphered_code) +passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) +hashed_data = bcrypt.hashpw(passcode_digest, user_cipher.salt) +code = hashed_data.decode("utf-8") + +Code: $2b$12$iLYVBzbu9DVSg7S.ZBzB..eoFhCtiWBtfjXNLULtODYBH8Epva1pC +``` + +## User Login +To login, a user: + +1. Gets login keypad +2. Submits key entry + +### Get Login keypad +The client requests the user's login keypad. +``` +login_keypad = api.get_login_keypad(username, customer_id) +keypad_view(login_keypad, keypad_size.props_per_key) +``` +The server returns a randomly shuffled keypad. Learn more about how the [User keypad Shuffle](nkode_concepts.md/#user-keypad-shuffle) works +``` +Login keypad Keypad View: +Key 1: [18 19 20 21 22 23] +Key 2: [ 6 7 8 9 10 11] +Key 3: [24 25 26 27 28 29] +Key 4: [0 1 2 3 4 5] +Key 5: [12 13 14 15 16 17] + +``` +Recall the user's passcode is `user_passcode = [19, 7, 25, 1]` so the user selects keys ` selected_keys_login = [0, 1, 2, 3]` + +``` +success = api.login(customer_id, username, selected_keys_login) +``` + +### Validate Login Key Entry +- decipher user mask and recover nkode set values +- get presumed property from key selection and set values +- encipher, salt, and hash presumed property values and compare them to the users hashed code + +#### Decipher Mask + +Recall: + +- set_key_i = (set_key_rand_numb_i ^ set_val_i) +- mask_i = mask_key_rand_num_i ^ set_key_rand_numb_i + +Recover nKode set values: + +- decode mask from base64 to int +- deciphered_mask = mask ^ mask_key +- deciphered_mask_i = set_key_rand_numb # mask_key_rand_num_i is cancelled out +- set_key_rand_component = set_key ^ set_values +- deduce the set value + +``` +user = customer.users[username] +user_cipher = user.user_cipher +user_mask = user.enciphered_passcode.mask +decoded_mask = user_cipher.decode_base64_str(user_mask) +deciphered_mask = xor_lists(decoded_mask, user_cipher.mask_key) +set_key_rand_component = xor_lists(set_vals, user_cipher.set_key) +passcode_sets = [] +for set_cipher in deciphered_mask[:passcode_len]: + set_idx = set_key_rand_component.index(set_cipher) + passcode_sets.append(set_vals[set_idx]) + +Passcode Sets: [49224, 49224, 49224, 49224] +``` + + +### Get Presumed properties +``` +set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets] + +presumed_selected_properties_idx = [] +for idx in range(passcode_len): + key_numb = selected_keys_login[idx] + set_idx = set_vals_idx[idx] + selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) + presumed_selected_properties_idx.append(selected_prop_idx) + +Presumped Passcode: [19, 7, 25, 1] +Recall User Passcode: [19, 7, 25, 1] +``` +### Compare Enciphered Passcodes +``` +enciphered_nkode = user_cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher) +``` +If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid, and the login is successful. + +## Renew properties +properties renew is invoked with the renew_properties method: `api.renew_properties(customer_id)` +The renew properties process has three steps: +1. Renew Customer properties +2. Renew User Keys +3. Refresh User on Login + +When the customer calls the `renew_properties` method, the method replaces the customer's properties and set values. All its users go through an intermediate +renewal step. The users fully renew after their first successful login. This first login refreshes their keys, salt, and hash with new values. + + +### Customer Renew +Old Customer properties and set values are cached and copied to variables before renewal. +``` +old_sets = customer.cipher.set_key + +Customer Sets: [51397 49224 50087 24444 43554 21522] +``` + +``` +old_prop = customer.cipher.prop_key + +Customer properties: +[65030 40058 49729 42519 32475 21731] +[19446 3351 17075 17586 20753 15754] +[19712 56685 43602 30750 54931 27419] +[40397 10398 13477 26037 17943 47642] +[58359 15284 53370 4343 16407 46898] + +``` + +After the renewal, the customer properties and sets are new randomly generated values. +``` +api.renew_properties(customer_id) + +set_vals = customer.cipher.set_key + +Customer Sets: [ 7754 52659 44415 3961 61872 57312] +``` + +``` +prop_vals = customer.cipher.prop_key + +Customer properties: +[57881 51596 44681 30104 33018 30596] +[35764 62538 21274 10697 11311 42560] +[ 4979 33517 18509 55230 26674 24108] +[63335 41237 52341 30975 12398 7267] +[53495 52030 41547 59730 36417 31547] + +``` + +### Renew User +During the renewal, each user goes through a temporary transition period. +``` +props_xor = xor_lists(new_props, old_props) +sets_xor = xor_lists(new_sets, old_sets) +for user in customer.users.values(): + user.renew = True + user.user_cipher.set_key = xor_lists(user.user_cipher.set_key, sets_xor) + user.user_cipher.prop_key = xor_lists(user.user_cipher.prop_key, props_xor) +``` +##### User prop Key +The user's prop key was a randomly generated list of length `numb_of_keys * prop_per_key`. +Now each value in the prop key is `prop_key_i = old_prop_key_i ^ new_prop_i ^ old_prop_i`. +Recall in the login process, `ciphered_customer_props = prop_key ^ customer_prop`. +Since the customer_prop is now the new value, it gets canceled out, leaving: +``` +new_prop_key = old_prop_key ^ old_prop ^ new_prop +ciphered_customer_props = new_prop_key ^ new_prop +ciphered_customer_props = old_prop_key ^ old_prop # since new_prop cancel out +``` +Using the new customer properties, we can validate the user's login attempt with the same hash. + +##### User Set Key +The user's set key was a randomly generated list of length `prop_per_key` xor `customer_set_vals`. +The `old_set_vals` have been replaced with the new `new_set_vals`. The deciphering process described above +remains the same. + +### User Refresh +Once the user has a successful login, they get a new salt and cipher keys, and their `enciphered_passcode` is recomputed +with the new values. +``` +user.user_cipher = UserCipherKeys.new( + customer.cipher.keypad_size, + customer.cipher.set_key, + user.user_cipher.max_nkode_len +) +user.enciphered_passcode = user.user_cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) +user.renew = False +``` \ No newline at end of file diff --git a/docs/nkode_authentication_template.md b/docs/readme_template.md similarity index 98% rename from docs/nkode_authentication_template.md rename to docs/readme_template.md index ae7a32c..820f295 100644 --- a/docs/nkode_authentication_template.md +++ b/docs/readme_template.md @@ -1,5 +1,5 @@ -# nKode Authentication -Play around with the code in [this](http://sesolgit/Repository/Blob/92a60227-4ef9-4196-8ebb-595581abf98c?encodedName=main&encodedPath=nkode_tutorial.ipynb) jupyter notebook. +# README +Play around with the code in /notebooks ## Customer Creation Before creating a user, a customer generates random properties and set diff --git a/docs/render_markdown.py b/docs/render_readme.py similarity index 98% rename from docs/render_markdown.py rename to docs/render_readme.py index 9472500..77c8a81 100644 --- a/docs/render_markdown.py +++ b/docs/render_readme.py @@ -33,14 +33,15 @@ def render_nkode_authentication(data: dict): env = Environment(loader=file_loader) # Load the template - template = env.get_template('nkode_authentication_template.md') + template = env.get_template('readme_template.md') print(os.getcwd()) # Render the template with the data output = template.render(data) # Print or save the output - output_file = os.path.expanduser("~/Desktop/nkode_authentication.md") + # output_file = os.path.expanduser("~/Desktop/nkode_authentication.md") + output_file = '../README.md' with open(output_file, 'w') as fp: fp.write(output) print("File written successfully") diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 58f6f07..d2be679 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -9,16 +9,16 @@ def nkode_api() -> NKodeAPI: return NKodeAPI() -@pytest.mark.parametrize("keypad_size,passocode_len", [ +@pytest.mark.parametrize("keypad_size,passcode_len", [ (KeypadSize(numb_of_keys=10, props_per_key=11), 4), (KeypadSize(numb_of_keys=10, props_per_key=12), 5), ]) -def test_create_new_user_and_renew_keys(nkode_api, keypad_size, passocode_len): +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) - user_passcode = set_keypad[:passocode_len] + 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) -- 2.49.1 From 439b706fbd52f55287ba5d835c5f87cd7088f6e4 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 11:05:02 -0500 Subject: [PATCH 29/85] small changes --- src/customer.py | 2 ++ src/nkode_api.py | 2 +- src/user_keypad.py | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/customer.py b/src/customer.py index b173c36..1a8a7e8 100644 --- a/src/customer.py +++ b/src/customer.py @@ -47,6 +47,8 @@ class Customer: if user.renew: user.refresh_passcode(presumed_property_idxs, self.cipher) + + user.user_keypad.partial_keypad_shuffle() return True def renew_keys(self) -> bool: diff --git a/src/nkode_api.py b/src/nkode_api.py index 15fd941..318b580 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -97,7 +97,7 @@ class NKodeAPI: if username not in customer.users.keys(): raise ValueError("Username not found") user = customer.users[username] - user.user_keypad.partial_keypad_shuffle() + # user.user_keypad.partial_keypad_shuffle() # TODO: implement split_keypad_shuffle() return user.user_keypad.keypad diff --git a/src/user_keypad.py b/src/user_keypad.py index a082ebb..b192473 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -79,7 +79,6 @@ class UserKeypad: #self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) pass - def property_adjacency_graph(self) -> dict[int, set[int]]: user_keypad_keypad = self.keypad_matrix() graph = {} -- 2.49.1 From 303f4a7457042b8b2e8a31759821e370867ab25e Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 11:15:56 -0500 Subject: [PATCH 30/85] remove unneeded functions --- notebooks/dispersion_tutorial.ipynb | 2 +- src/customer_cipher.py | 4 +-- src/models.py | 2 +- src/user_cipher.py | 42 ++++++++++------------------- src/user_keypad.py | 2 +- test/test_nkode_interface.py | 2 +- test/test_user_cipher_keys.py | 2 +- 7 files changed, 21 insertions(+), 35 deletions(-) diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index c4effe6..1c7f4b2 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -19,7 +19,7 @@ "import numpy as np\n", "\n", "def keypad_md_table(keypad_list: np.ndarray, keypad_size: KeypadSize) -> str:\n", - " assert (keypad_size.numb_of_props == len(keypad_list))\n", + " assert (keypad_size.total_props == len(keypad_list))\n", " keypad = keypad_list.reshape(-1, keypad_size.props_per_key)\n", " table = \"|key|\" + \"\".join([f\"set{idx}|\" for idx in range(keypad_size.props_per_key)])\n", " table += \"\\n|\" + \"\".join(\"-|\" for _ in range(keypad_size.props_per_key + 1))\n", diff --git a/src/customer_cipher.py b/src/customer_cipher.py index 7a19fbc..72ffcac 100644 --- a/src/customer_cipher.py +++ b/src/customer_cipher.py @@ -26,7 +26,7 @@ class CustomerCipher: raise ValueError(f"Keys and properties per key must not exceed {cls.MAX_KEYS}") # Using numpy to generate non-repeating random integers - prop_key = np.random.choice(2 ** 16, size=keypad_size.numb_of_props, replace=False) + prop_key = np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False) set_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False) return cls( @@ -36,7 +36,7 @@ class CustomerCipher: ) def renew(self): - self.prop_key = np.random.choice(2 ** 16, size=self.keypad_size.numb_of_props, replace=False) + self.prop_key = np.random.choice(2 ** 16, size=self.keypad_size.total_props, replace=False) self.set_key = np.random.choice(2 ** 16, size=self.keypad_size.props_per_key, replace=False) def get_prop_set_val(self, prop: int) -> int: diff --git a/src/models.py b/src/models.py index 04e1158..afef20e 100644 --- a/src/models.py +++ b/src/models.py @@ -23,7 +23,7 @@ class KeypadSize: numb_of_keys: int @property - def numb_of_props(self) -> int: + def total_props(self) -> int: return self.props_per_key * self.numb_of_keys @property diff --git a/src/user_cipher.py b/src/user_cipher.py index ee9f15d..daa3bf5 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -23,13 +23,13 @@ class UserCipher: raise ValueError("Invalid set values") set_values_array = np.array(set_values, dtype=np.uint16) - set_key = generate_random_nonrepeating_array(keypad_size.props_per_key) + set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) set_key = np.bitwise_xor(set_key, set_values_array) return UserCipher( - prop_key=generate_random_nonrepeating_array(keypad_size.props_per_key * keypad_size.numb_of_keys), - pass_key=generate_random_nonrepeating_array(max_nkode_len), - mask_key=generate_random_nonrepeating_array(max_nkode_len), + prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), + pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False), + mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False), set_key=set_key, salt=bcrypt.gensalt(), max_nkode_len=max_nkode_len @@ -63,7 +63,7 @@ class UserCipher: return np.array(int_list, dtype=np.uint16) def _hash_passcode(self, passcode: np.ndarray) -> str: - passcode_bytes = int_array_to_bytes(passcode) + passcode_bytes = passcode.astype(np.uint16).tobytes() passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) hashed_data = bcrypt.hashpw(passcode_digest, self.salt) return hashed_data.decode("utf-8") @@ -88,20 +88,20 @@ class UserCipher: passcode_prop_idx: list[int], customer_prop: CustomerCipher, ) -> str: - passcode_prop_idx_array = np.array(passcode_prop_idx, dtype=np.uint16) - passcode_len = len(passcode_prop_idx_array) - passcode_props = np.array([customer_prop.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) - + passcode_len = len(passcode_prop_idx) passcode_cipher = self.pass_key.copy() - for idx in range(passcode_len): - prop_idx = passcode_prop_idx_array[idx] - passcode_cipher[idx] = passcode_cipher[idx] ^ self.prop_key[prop_idx] ^ passcode_props[idx] + passcode_cipher[:passcode_len] = ( + passcode_cipher[:passcode_len] ^ + self.prop_key[passcode_prop_idx] ^ + customer_prop.prop_key[passcode_prop_idx] + ) return self._hash_passcode(passcode_cipher) + def encipher_mask( self, - passcode_sets: list[int], + passcode_sets: np.ndarray, customer_properites: CustomerCipher ) -> str: padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_properites.set_key) @@ -131,21 +131,7 @@ class UserCipher: set_idx = np.where(set_key_rand_component == set_cipher)[0][0] passcode_sets.append(set_vals[set_idx]) - return passcode_sets - - -# NumPy utility functions to replace the existing ones -def generate_random_nonrepeating_array(array_len: int, min_val: int = 0, max_val: int = 2 ** 16) -> np.ndarray: - if max_val - min_val < array_len: - raise ValueError("Range of values is less than the array length requested") - - # Generate array of random unique integers - return np.random.choice( - np.arange(min_val, max_val, dtype=np.uint16), - size=array_len, - replace=False - ) - + return np.array(passcode_sets) def int_array_to_bytes(int_arr: np.ndarray, byte_size: int = 2) -> bytes: return b"".join([int(num).to_bytes(byte_size, byteorder='big') for num in int_arr]) \ No newline at end of file diff --git a/src/user_keypad.py b/src/user_keypad.py index b192473..1405dd3 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -11,7 +11,7 @@ class UserKeypad: @classmethod def create(cls, keypad_size: KeypadSize) -> 'UserKeypad': keypad = UserKeypad( - keypad=np.arange(keypad_size.numb_of_props), + keypad=np.arange(keypad_size.total_props), keypad_size=keypad_size ) keypad.random_keypad_shuffle() diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index 8dce0fb..4a378e3 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -9,7 +9,7 @@ from src.models import KeypadSize ) def test_prop_set_idx(keypad_size): user_keypad = UserKeypad.create(keypad_size) - for prop_idx in range(keypad_size.numb_of_props): + for prop_idx in range(keypad_size.total_props): user_keypad_idx = user_keypad.keypad[prop_idx] assert (prop_idx % keypad_size.props_per_key == user_keypad_idx % keypad_size.props_per_key) diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 3671420..80e6a0b 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -29,7 +29,7 @@ def test_encode_decode_base64(passcode_len): def test_decode_mask(keypad_size, max_nkode_len): customer = CustomerCipher.create(keypad_size) #passcode_entry = generate_random_nonrepeating_list(keypad_size.numb_of_props,max_val=keypad_size.numb_of_props)[:4] - passcode_entry = np.random.choice(keypad_size.numb_of_props, 4, replace=False) + passcode_entry = np.random.choice(keypad_size.total_props, 4, replace=False) passcode_values = [customer.prop_key[idx] for idx in passcode_entry] set_vals = customer.set_key user_keys = UserCipher.create(keypad_size, set_vals, max_nkode_len) -- 2.49.1 From daebb61e56dab65efdb55f90cbd424734fa9a0b7 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 14 Mar 2025 11:23:31 -0500 Subject: [PATCH 31/85] simplify encipher_nkode --- src/user_cipher.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/user_cipher.py b/src/user_cipher.py index daa3bf5..f1513c3 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -73,10 +73,7 @@ class UserCipher: passcode_prop_idx: list[int], customer_cipher: CustomerCipher ) -> EncipheredNKode: - passcode_prop_idx_array = np.array(passcode_prop_idx, dtype=np.uint16) - passcode_props = np.array([customer_cipher.prop_key[idx] for idx in passcode_prop_idx_array], dtype=np.uint16) - passcode_sets = np.array([customer_cipher.get_prop_set_val(prop) for prop in passcode_props], dtype=np.uint16) - mask = self.encipher_mask(passcode_sets.tolist(), customer_cipher) + mask = self.encipher_mask(passcode_prop_idx, customer_cipher) code = self.encipher_salt_hash_code(passcode_prop_idx, customer_cipher) return EncipheredNKode( code=code, @@ -101,18 +98,20 @@ class UserCipher: def encipher_mask( self, - passcode_sets: np.ndarray, - customer_properites: CustomerCipher + passcode_prop_idx: list[int], + customer_cipher: CustomerCipher ) -> str: - padded_passcode_sets = self.pad_user_mask(passcode_sets, customer_properites.set_key) + customer_props = customer_cipher.prop_key[passcode_prop_idx] + customer_sets = [customer_cipher.get_prop_set_val(prop) for prop in customer_props] + padded_customer_sets = self.pad_user_mask(np.array(customer_sets), customer_cipher.set_key) # Get indices of set values - set_idx = np.array([customer_properites.get_set_index(set_val) for set_val in padded_passcode_sets], + set_idx = np.array([customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets], dtype=np.uint16) mask_set_keys = np.array([self.set_key[idx] for idx in set_idx], dtype=np.uint16) # XOR operations - ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_sets) + ciphered_mask = np.bitwise_xor(mask_set_keys, padded_customer_sets) ciphered_mask = np.bitwise_xor(ciphered_mask, self.mask_key) mask = self.encode_base64_str(ciphered_mask) -- 2.49.1 From b6ab0c1890da6f52a636e28780baa2797ac585f8 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 16 Mar 2025 06:20:05 -0500 Subject: [PATCH 32/85] refactor np.bitwise xor to ^ operator --- docs/render_readme.py | 16 +- notebooks/nkode_tutorial.ipynb | 265 ++++++++++++++++----------------- src/customer.py | 4 +- src/user.py | 4 +- src/user_cipher.py | 18 +-- 5 files changed, 149 insertions(+), 158 deletions(-) diff --git a/docs/render_readme.py b/docs/render_readme.py index 77c8a81..df4551c 100644 --- a/docs/render_readme.py +++ b/docs/render_readme.py @@ -97,12 +97,10 @@ if __name__ == "__main__": padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.set_key) set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] - mask_set_keys = [user_keys.set_key[idx] for idx in set_idx] - ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_server_set) - ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key) + mask_set_keys = [user_keys.combined_set_key[idx] for idx in set_idx] + ciphered_mask = mask_set_keys ^ padded_passcode_server_set ^ user_keys.mask_key mask = user_keys.encode_base64_str(ciphered_mask) - #ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_keys.prop_key) - ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key) + ciphered_customer_props = customer.cipher.prop_key ^ user_keys.prop_key passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len passcode_ciphered_props.extend([0 for _ in range(pad_len)]) @@ -136,7 +134,7 @@ if __name__ == "__main__": user_mask = user.enciphered_passcode.mask decoded_mask = user_keys.decode_base64_str(user_mask) deciphered_mask = np.bitwise_xor(decoded_mask, user_keys.mask_key) - set_key_rand_component = np.bitwise_xor(set_vals, user_keys.set_key) + set_key_rand_component = np.bitwise_xor(set_vals, user_keys.combined_set_key) login_passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: set_idx = np.where(set_key_rand_component == set_cipher)[0][0] @@ -172,7 +170,7 @@ if __name__ == "__main__": sets_xor = np.bitwise_xor(new_sets, old_sets) for user in customer.users.values(): user.renew = True - user.cipher.set_key = np.bitwise_xor(user.cipher.set_key, sets_xor) + user.cipher.combined_set_key = np.bitwise_xor(user.cipher.combined_set_key, sets_xor) user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor) """ @@ -200,8 +198,8 @@ if __name__ == "__main__": 'confirm_keypad': confirm_keypad, 'selected_keys_confirm': selected_keys_confirm, 'user_cipher': user_keys, - 'passcode_server_prop': passcode_server_prop, - 'passcode_server_set': passcode_server_set, + 'ordered_customer_prop_key': passcode_server_prop, + 'ordered_customer_set_key': passcode_server_set, 'enciphered_nkode': enciphered_nkode, 'login_keypad': login_keypad, 'selected_login_keys': selected_keys_login, diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 204be73..fd831d3 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -12,12 +12,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.634660Z", - "start_time": "2025-03-14T14:35:01.631713Z" + "end_time": "2025-03-16T11:17:06.665193Z", + "start_time": "2025-03-16T11:17:06.662525Z" } }, "outputs": [], - "execution_count": 67 + "execution_count": 23 }, { "cell_type": "code", @@ -40,12 +40,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.653961Z", - "start_time": "2025-03-14T14:35:01.650229Z" + "end_time": "2025-03-16T11:17:06.680622Z", + "start_time": "2025-03-16T11:17:06.677352Z" } }, "outputs": [], - "execution_count": 68 + "execution_count": 24 }, { "cell_type": "code", @@ -55,12 +55,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.663429Z", - "start_time": "2025-03-14T14:35:01.661723Z" + "end_time": "2025-03-16T11:17:06.696721Z", + "start_time": "2025-03-16T11:17:06.694897Z" } }, "outputs": [], - "execution_count": 69 + "execution_count": 25 }, { "cell_type": "markdown", @@ -100,12 +100,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.675378Z", - "start_time": "2025-03-14T14:35:01.671221Z" + "end_time": "2025-03-16T11:17:06.708524Z", + "start_time": "2025-03-16T11:17:06.704492Z" } }, "outputs": [], - "execution_count": 70 + "execution_count": 26 }, { "cell_type": "markdown", @@ -130,7 +130,7 @@ "cell_type": "code", "source": [ "print(f\"Customer Set Key: {customer.cipher.set_key}\")\n", - "print(f\"Customer properties Key:\")\n", + "print(f\"Customer Properties Key:\")\n", "customer_prop_keypad = customer.cipher.prop_key.reshape(-1, keypad_size.props_per_key)\n", "for idx, key_vals in enumerate(customer_prop_keypad):\n", " print(f\"{key_vals}\")" @@ -138,8 +138,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.692557Z", - "start_time": "2025-03-14T14:35:01.689797Z" + "end_time": "2025-03-16T11:17:06.718027Z", + "start_time": "2025-03-16T11:17:06.715343Z" } }, "outputs": [ @@ -147,17 +147,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Set Key: [21871 61274 2713 19029 26505 50649]\n", - "Customer properties Key:\n", - "[42655 23174 1254 25551 11488 27210]\n", - "[60953 53938 20104 25057 1695 3339]\n", - "[32918 29164 48427 35503 41361 24040]\n", - "[44451 37655 36339 20189 49770 11581]\n", - "[31824 8094 2501 29432 12778 20956]\n" + "Customer Set Key: [51574 4745 25889 18231 60748 18510]\n", + "Customer Properties Key:\n", + "[60468 25732 54355 40123 30642 22334]\n", + "[12163 30704 51913 11579 53335 30868]\n", + "[36499 37185 3991 26970 25932 19506]\n", + "[ 7000 7490 26410 33717 8308 41888]\n", + "[59350 41496 33957 33571 58466 45968]\n" ] } ], - "execution_count": 71 + "execution_count": 27 }, { "cell_type": "markdown", @@ -177,8 +177,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.702167Z", - "start_time": "2025-03-14T14:35:01.699777Z" + "end_time": "2025-03-16T11:17:06.735483Z", + "start_time": "2025-03-16T11:17:06.733076Z" } }, "outputs": [ @@ -187,16 +187,16 @@ "output_type": "stream", "text": [ "Set to Properties Map:\n", - "21871: [42655 60953 32918 44451 31824]\n", - "61274: [23174 53938 29164 37655 8094]\n", - "2713: [ 1254 20104 48427 36339 2501]\n", - "19029: [25551 25057 35503 20189 29432]\n", - "26505: [11488 1695 41361 49770 12778]\n", - "50649: [27210 3339 24040 11581 20956]\n" + "51574: [60468 12163 36499 7000 59350]\n", + "4745: [25732 30704 37185 7490 41496]\n", + "25889: [54355 51913 3991 26410 33957]\n", + "18231: [40123 11579 26970 33717 33571]\n", + "60748: [30642 53335 25932 8308 58466]\n", + "18510: [22334 30868 19506 41888 45968]\n" ] } ], - "execution_count": 72 + "execution_count": 28 }, { "metadata": {}, @@ -218,8 +218,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.722470Z", - "start_time": "2025-03-14T14:35:01.719357Z" + "end_time": "2025-03-16T11:17:06.760751Z", + "start_time": "2025-03-16T11:17:06.758403Z" } }, "cell_type": "code", @@ -232,15 +232,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[24 0 12 6 18]\n", - " [28 4 16 10 22]\n", - " [27 3 15 9 21]\n", - " [26 2 14 8 20]\n", - " [25 1 13 7 19]]\n" + "[[21 15 9 3 27]\n", + " [22 16 10 4 28]\n", + " [18 12 6 0 24]\n", + " [20 14 8 2 26]\n", + " [23 17 11 5 29]]\n" ] } ], - "execution_count": 73 + "execution_count": 29 }, { "metadata": {}, @@ -253,8 +253,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.746158Z", - "start_time": "2025-03-14T14:35:01.742671Z" + "end_time": "2025-03-16T11:17:06.783887Z", + "start_time": "2025-03-16T11:17:06.780577Z" } }, "cell_type": "code", @@ -275,25 +275,25 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [24 0 12 6 18]\n", - "Key 1: [28 4 16 10 22]\n", - "Key 2: [27 3 15 9 21]\n", - "Key 3: [26 2 14 8 20]\n", - "Key 4: [25 1 13 7 19]\n", - "User Passcode: [24 0 12 6]\n", + "Key 0: [21 15 9 3 27]\n", + "Key 1: [22 16 10 4 28]\n", + "Key 2: [18 12 6 0 24]\n", + "Key 3: [20 14 8 2 26]\n", + "Key 4: [23 17 11 5 29]\n", + "User Passcode: [21 15 9 3]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side properties: [31824, 42655, 32918, 60953]\n" + "User Passcode Server-side properties: [33717, 26970, 11579, 40123]\n" ] } ], - "execution_count": 74 + "execution_count": 30 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:01.770157Z", - "start_time": "2025-03-14T14:35:01.766784Z" + "end_time": "2025-03-16T11:17:06.799077Z", + "start_time": "2025-03-16T11:17:06.796555Z" } }, "cell_type": "code", @@ -309,23 +309,23 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [26 3 16 6 19]\n", - "Key 1: [27 0 13 10 20]\n", - "Key 2: [24 4 14 7 21]\n", - "Key 3: [28 1 15 8 18]\n", - "Key 4: [25 2 12 9 22]\n", + "Key 0: [18 17 8 4 27]\n", + "Key 1: [23 16 6 3 26]\n", + "Key 2: [22 15 11 2 24]\n", + "Key 3: [21 14 10 0 29]\n", + "Key 4: [20 12 9 5 28]\n", "Selected Keys\n", - "[2, 1, 4, 0]\n" + "[3, 2, 4, 1]\n" ] } ], - "execution_count": 75 + "execution_count": 31 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.101712Z", - "start_time": "2025-03-14T14:35:01.792851Z" + "end_time": "2025-03-16T11:17:07.061018Z", + "start_time": "2025-03-16T11:17:06.824471Z" } }, "cell_type": "code", @@ -343,7 +343,7 @@ ] } ], - "execution_count": 76 + "execution_count": 32 }, { "metadata": {}, @@ -362,17 +362,16 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.115075Z", - "start_time": "2025-03-14T14:35:02.110397Z" + "end_time": "2025-03-16T11:17:07.070338Z", + "start_time": "2025-03-16T11:17:07.066596Z" } }, "cell_type": "code", "source": [ "from src.user_cipher import UserCipher\n", "\n", - "\n", - "set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\n", - "set_key = np.bitwise_xor(set_key, customer.cipher.set_key)\n", + "user_set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\n", + "combined_set_key = np.bitwise_xor(user_set_key, customer.cipher.set_key)\n", "user_keys = UserCipher(\n", " prop_key = np.array([\n", " 57200, 8398, 54694, 25997, 30388,\n", @@ -384,27 +383,27 @@ " ]),\n", " pass_key=np.array([16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206]),\n", " mask_key=np.array([29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325]),\n", - " set_key=np.array(set_key),\n", + " combined_set_key=np.array(combined_set_key),\n", " salt=b'$2b$12$fX.in.GGAjz3QBBwqSWc6e',\n", " max_nkode_len=customer.nkode_policy.max_nkode_len, \n", ")\n", "\n", - "passcode_server_prop = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", - "passcode_server_set = [int(customer.cipher.get_prop_set_val(prop)) for prop in passcode_server_prop]\n", - "print(f\"Passcode Set Vals: {passcode_server_set}\")\n", - "print(f\"Passcode prop Vals: {passcode_server_prop}\")" + "ordered_customer_prop_key = customer.cipher.prop_key[user_passcode] # [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", + "ordered_customer_set_key = [int(customer.cipher.get_prop_set_val(prop)) for prop in ordered_customer_prop_key]\n", + "print(f\"Passcode Set Vals: {ordered_customer_set_key}\")\n", + "print(f\"Passcode Prop Vals: {ordered_customer_prop_key}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [21871, 21871, 21871, 21871]\n", - "Passcode prop Vals: [31824, 42655, 32918, 60953]\n" + "Passcode Set Vals: [18231, 18231, 18231, 18231]\n", + "Passcode Prop Vals: [33717 26970 11579 40123]\n" ] } ], - "execution_count": 77 + "execution_count": 33 }, { "metadata": {}, @@ -412,36 +411,34 @@ "source": [ "#### Encipher Mask\n", "Recall:\n", - "1. set_key_i = (set_rand_numb_i ^ set_val_i) \n", - "2. mask_key_i = mask_rand_numb_i\n", - "3. padded_passcode_server_set_i = set_val_i\n", - "4. len(set_key) == len(mask_key) == (padded_passcode_server_set) == max_nkode_len == 10\n", + "1. combined_set_key = (user_set_key ^ customer_set_key)\n", + "2. padded_ordered_customer_set = customer_set_key # ordered by user passcode and padded with extra set key values to be equal to max_nkode_len\n", + "3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n", "where i is the index\n", " \n", - "- mask_i = mask_key_i ^ padded_passcode_server_set_i ^ set_key_i\n", - "- mask_i = mask_rand_num_i ^ set_val_i ^ set_rand_numb_i ^ set_val_i\n", - "- mask_i = mask_rand_num_i ^ set_rand_numb_i # set_val_i is cancelled out" + "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_set_key\n", + "- mask = mask_rand_num ^ set_val ^ set_rand_numb ^ set_val\n", + "- mask = mask_rand_num ^ set_rand_numb # set_val is cancelled out" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.132228Z", - "start_time": "2025-03-14T14:35:02.129307Z" + "end_time": "2025-03-16T11:17:07.150818Z", + "start_time": "2025-03-16T11:17:07.148125Z" } }, "cell_type": "code", "source": [ - "padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.cipher.set_key)\n", - "\n", - "set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set]\n", - "mask_set_keys = [user_keys.set_key[idx] for idx in set_idx]\n", - "ciphered_mask = np.bitwise_xor(mask_set_keys, padded_passcode_server_set)\n", + "padded_ordered_customer_set = user_keys.pad_user_mask(ordered_customer_set_key, customer.cipher.set_key)\n", + "set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_ordered_customer_set]\n", + "ordered_set_key = user_keys.combined_set_key[set_idx]\n", + "ciphered_mask = ordered_set_key ^ padded_ordered_customer_set\n", "ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key)\n", "mask = user_keys.encode_base64_str(ciphered_mask)" ], "outputs": [], - "execution_count": 78 + "execution_count": 34 }, { "metadata": {}, @@ -458,8 +455,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.448903Z", - "start_time": "2025-03-14T14:35:02.143953Z" + "end_time": "2025-03-16T11:17:07.413741Z", + "start_time": "2025-03-16T11:17:07.177338Z" } }, "cell_type": "code", @@ -483,13 +480,13 @@ "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 79 + "execution_count": 35 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.459349Z", - "start_time": "2025-03-14T14:35:02.457325Z" + "end_time": "2025-03-16T11:17:07.428200Z", + "start_time": "2025-03-16T11:17:07.426275Z" } }, "cell_type": "code", @@ -502,7 +499,7 @@ ")" ], "outputs": [], - "execution_count": 80 + "execution_count": 36 }, { "metadata": {}, @@ -516,8 +513,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.775720Z", - "start_time": "2025-03-14T14:35:02.468856Z" + "end_time": "2025-03-16T11:17:07.668766Z", + "start_time": "2025-03-16T11:17:07.436169Z" } }, "cell_type": "code", @@ -535,17 +532,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [24 25 26 27 28 29]\n", - "Key 1: [0 1 2 3 4 5]\n", - "Key 2: [12 13 14 15 16 17]\n", - "Key 3: [ 6 7 8 9 10 11]\n", - "Key 4: [18 19 20 21 22 23]\n", + "Key 0: [18 19 20 21 22 23]\n", + "Key 1: [12 13 14 15 16 17]\n", + "Key 2: [ 6 7 8 9 10 11]\n", + "Key 3: [0 1 2 3 4 5]\n", + "Key 4: [24 25 26 27 28 29]\n", "Selected Keys: [0, 1, 2, 3]\n", "True\n" ] } ], - "execution_count": 81 + "execution_count": 37 }, { "metadata": {}, @@ -563,13 +560,13 @@ "source": [ "### Decipher Mask\n", "Recall:\n", - "- set_key_i = (set_key_rand_numb_i ^ set_val_i) \n", - "- mask_i = mask_key_rand_num_i ^ set_key_rand_numb_i\n", + "- set_key = (set_key_rand_numb ^ set_val)\n", + "- mask = mask_key_rand_num ^ set_key_rand_numb\n", "\n", "Recover nKode set values: \n", "- decode mask from base64 to int\n", "- deciphered_mask = mask ^ mask_key\n", - "- deciphered_mask_i = set_key_rand_numb # mask_key_rand_num_i is cancelled out\n", + "- deciphered_mask = set_key_rand_numb # mask_key_rand_num is cancelled out\n", "- set_key_rand_component = set_key ^ set_values\n", "- deduce the set value" ] @@ -577,8 +574,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.787442Z", - "start_time": "2025-03-14T14:35:02.784207Z" + "end_time": "2025-03-16T11:17:07.679342Z", + "start_time": "2025-03-16T11:17:07.676500Z" } }, "cell_type": "code", @@ -586,7 +583,7 @@ "user = api.customers[customer_id].users[username]\n", "decoded_mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", "deciphered_mask = np.bitwise_xor(decoded_mask, user.cipher.mask_key)\n", - "set_key = np.bitwise_xor(customer.cipher.set_key, user.cipher.set_key)\n", + "set_key = np.bitwise_xor(customer.cipher.set_key, user.cipher.combined_set_key)\n", "passcode_sets = []\n", "for set_cipher in deciphered_mask[:passcode_len]:\n", " set_idx = np.where(set_key == set_cipher)[0][0]\n", @@ -598,11 +595,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[21871, 21871, 21871, 21871]\n" + "[18231, 18231, 18231, 18231]\n" ] } ], - "execution_count": 82 + "execution_count": 38 }, { "metadata": {}, @@ -612,8 +609,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:02.805826Z", - "start_time": "2025-03-14T14:35:02.802523Z" + "end_time": "2025-03-16T11:17:07.690303Z", + "start_time": "2025-03-16T11:17:07.687880Z" } }, "cell_type": "code", @@ -638,7 +635,7 @@ ] } ], - "execution_count": 83 + "execution_count": 39 }, { "metadata": {}, @@ -648,8 +645,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:03.129079Z", - "start_time": "2025-03-14T14:35:02.822467Z" + "end_time": "2025-03-16T11:17:07.937619Z", + "start_time": "2025-03-16T11:17:07.705824Z" } }, "cell_type": "code", @@ -666,7 +663,7 @@ ] } ], - "execution_count": 84 + "execution_count": 40 }, { "metadata": {}, @@ -682,8 +679,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:03.757448Z", - "start_time": "2025-03-14T14:35:03.142511Z" + "end_time": "2025-03-16T11:17:08.414223Z", + "start_time": "2025-03-16T11:17:07.946756Z" } }, "cell_type": "code", @@ -708,14 +705,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: QUR1wAPG8jJh/nG1LbgMIupAY1I=, code: $2b$12$0Ia665myY64l8.UyKRc4tu/OR.0BG6KFLyXcuDdIXrDkpP6cjo7xO\n", - "mask: QUR1wAPG8jJh/nG1LbgMIupAY1I=, code: $2b$12$0Ia665myY64l8.UyKRc4tu/OR.0BG6KFLyXcuDdIXrDkpP6cjo7xO\n", + "mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", + "mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", "True\n", - "mask: SB1G0mGYnu2Fy2usk08r9uTTugo=, code: $2b$12$pKJtlWu9gwXqA4b.Q/WEZ.yzz9ntnXnFSindkyVOWa3sNOoVd0LLK\n" + "mask: Q7Dw+o+qDTXYlg8TC2hOkn9r1s0=, code: $2b$12$Xp2oD/vFDHwzvBmELBxC6e.iLK4xn5ZtLwW4UCFVoFY2ioTvGaWwa\n" ] } ], - "execution_count": 85 + "execution_count": 41 }, { "metadata": {}, @@ -729,8 +726,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:03.770972Z", - "start_time": "2025-03-14T14:35:03.766802Z" + "end_time": "2025-03-16T11:17:08.424680Z", + "start_time": "2025-03-16T11:17:08.421148Z" } }, "cell_type": "code", @@ -742,7 +739,7 @@ "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 86 + "execution_count": 42 }, { "metadata": {}, @@ -755,8 +752,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:03.781705Z", - "start_time": "2025-03-14T14:35:03.779309Z" + "end_time": "2025-03-16T11:17:08.434263Z", + "start_time": "2025-03-16T11:17:08.431951Z" } }, "cell_type": "code", @@ -765,11 +762,11 @@ "sets_xor = np.bitwise_xor(new_sets, old_sets)\n", "for user in customer.users.values():\n", " user.renew = True\n", - " user.cipher.set_key = np.bitwise_xor(user.cipher.set_key, sets_xor)\n", + " user.cipher.combined_set_key = np.bitwise_xor(user.cipher.combined_set_key, sets_xor)\n", " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 87 + "execution_count": 43 }, { "metadata": {}, @@ -779,8 +776,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-14T14:35:04.107750Z", - "start_time": "2025-03-14T14:35:03.797025Z" + "end_time": "2025-03-16T11:17:08.683077Z", + "start_time": "2025-03-16T11:17:08.447107Z" } }, "cell_type": "code", @@ -794,7 +791,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": 88 + "execution_count": 44 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 1a8a7e8..6177ba9 100644 --- a/src/customer.py +++ b/src/customer.py @@ -58,8 +58,8 @@ class Customer: new_props = self.cipher.prop_key new_sets = self.cipher.set_key - props_xor = np.bitwise_xor(new_props, old_props) - set_xor = np.bitwise_xor(new_sets, old_sets) + props_xor = new_props ^ old_props + set_xor = new_sets ^ old_sets for user in self.users.values(): user.renew_keys(set_xor, props_xor) self.users[user.username] = user diff --git a/src/user.py b/src/user.py index 2cc565f..1ee091e 100644 --- a/src/user.py +++ b/src/user.py @@ -18,8 +18,8 @@ class User: def renew_keys(self, set_xor: np.ndarray, prop_xor: np.ndarray): self.renew = True - self.cipher.set_key = np.bitwise_xor(self.cipher.set_key, set_xor) - self.cipher.prop_key = np.bitwise_xor(self.cipher.prop_key, prop_xor) + self.cipher.combined_set_key = self.cipher.combined_set_key ^ set_xor + self.cipher.prop_key = self.cipher.prop_key ^ prop_xor def refresh_passcode(self, passcode_prop_idxs: list[int], customer_cipher: CustomerCipher): self.cipher = UserCipher.create( diff --git a/src/user_cipher.py b/src/user_cipher.py index f1513c3..5e2123a 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -11,7 +11,7 @@ from src.customer_cipher import CustomerCipher @dataclass class UserCipher: prop_key: np.ndarray - set_key: np.ndarray + combined_set_key: np.ndarray pass_key: np.ndarray mask_key: np.ndarray salt: bytes @@ -30,7 +30,7 @@ class UserCipher: prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False), mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False), - set_key=set_key, + combined_set_key=set_key, salt=bcrypt.gensalt(), max_nkode_len=max_nkode_len ) @@ -38,15 +38,11 @@ class UserCipher: def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray: if len(user_mask) >= self.max_nkode_len: raise ValueError("User mask is too long") - - user_mask_array = np.array(user_mask, dtype=np.uint16) - # Create padding of random choices from set_vals padding_size = self.max_nkode_len - len(user_mask) - padding_indices = np.random.choice(len(set_vals), padding_size) - padding = np.array([set_vals[i] for i in padding_indices], dtype=np.uint16) + # Generate padding directly using np.random.choice + padding = np.random.choice(set_vals, size=padding_size, replace=True).astype(np.uint16) # Concatenate original mask with padding - padded_user_mask = np.concatenate([user_mask_array, padding]) - return padded_user_mask + return np.concatenate([user_mask, padding]) @staticmethod def encode_base64_str(data: np.ndarray) -> str: @@ -108,7 +104,7 @@ class UserCipher: # Get indices of set values set_idx = np.array([customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets], dtype=np.uint16) - mask_set_keys = np.array([self.set_key[idx] for idx in set_idx], dtype=np.uint16) + mask_set_keys = np.array([self.combined_set_key[idx] for idx in set_idx], dtype=np.uint16) # XOR operations ciphered_mask = np.bitwise_xor(mask_set_keys, padded_customer_sets) @@ -122,7 +118,7 @@ class UserCipher: decoded_mask = self.decode_base64_str(mask) deciphered_mask = np.bitwise_xor(decoded_mask, self.mask_key) - set_key_rand_component = np.bitwise_xor(set_vals_array, self.set_key) + set_key_rand_component = np.bitwise_xor(set_vals_array, self.combined_set_key) passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: -- 2.49.1 From 2a19aa73c4b935f6bd1142375788b3aad4cf2788 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 16 Mar 2025 08:07:42 -0500 Subject: [PATCH 33/85] remove salt from UserCipher --- notebooks/nkode_tutorial.ipynb | 248 ++++++++++++++++----------------- src/customer.py | 3 +- src/user_cipher.py | 99 ++++++------- 3 files changed, 169 insertions(+), 181 deletions(-) diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index fd831d3..513645a 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -7,17 +7,18 @@ "from src.models import NKodePolicy, KeypadSize\n", "from secrets import choice\n", "from string import ascii_lowercase\n", - "import numpy as np" + "import numpy as np\n", + "import bcrypt" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.665193Z", - "start_time": "2025-03-16T11:17:06.662525Z" + "end_time": "2025-03-16T12:51:50.430038Z", + "start_time": "2025-03-16T12:51:50.426267Z" } }, "outputs": [], - "execution_count": 23 + "execution_count": 17 }, { "cell_type": "code", @@ -40,12 +41,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.680622Z", - "start_time": "2025-03-16T11:17:06.677352Z" + "end_time": "2025-03-16T12:51:50.441672Z", + "start_time": "2025-03-16T12:51:50.437979Z" } }, "outputs": [], - "execution_count": 24 + "execution_count": 18 }, { "cell_type": "code", @@ -55,12 +56,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.696721Z", - "start_time": "2025-03-16T11:17:06.694897Z" + "end_time": "2025-03-16T12:51:50.451921Z", + "start_time": "2025-03-16T12:51:50.449896Z" } }, "outputs": [], - "execution_count": 25 + "execution_count": 19 }, { "cell_type": "markdown", @@ -100,12 +101,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.708524Z", - "start_time": "2025-03-16T11:17:06.704492Z" + "end_time": "2025-03-16T12:51:50.462932Z", + "start_time": "2025-03-16T12:51:50.458899Z" } }, "outputs": [], - "execution_count": 26 + "execution_count": 20 }, { "cell_type": "markdown", @@ -138,8 +139,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.718027Z", - "start_time": "2025-03-16T11:17:06.715343Z" + "end_time": "2025-03-16T12:51:50.471838Z", + "start_time": "2025-03-16T12:51:50.469241Z" } }, "outputs": [ @@ -147,17 +148,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Set Key: [51574 4745 25889 18231 60748 18510]\n", + "Customer Set Key: [29252 55146 27501 36339 61988 56812]\n", "Customer Properties Key:\n", - "[60468 25732 54355 40123 30642 22334]\n", - "[12163 30704 51913 11579 53335 30868]\n", - "[36499 37185 3991 26970 25932 19506]\n", - "[ 7000 7490 26410 33717 8308 41888]\n", - "[59350 41496 33957 33571 58466 45968]\n" + "[58958 438 23479 56544 19227 62335]\n", + "[19972 45333 49189 28952 25210 7101]\n", + "[27924 36712 27932 6192 11310 25890]\n", + "[41961 42300 62264 30572 21755 25167]\n", + "[56659 59117 55559 18611 58570 62570]\n" ] } ], - "execution_count": 27 + "execution_count": 21 }, { "cell_type": "markdown", @@ -177,8 +178,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.735483Z", - "start_time": "2025-03-16T11:17:06.733076Z" + "end_time": "2025-03-16T12:51:50.482832Z", + "start_time": "2025-03-16T12:51:50.480624Z" } }, "outputs": [ @@ -187,16 +188,16 @@ "output_type": "stream", "text": [ "Set to Properties Map:\n", - "51574: [60468 12163 36499 7000 59350]\n", - "4745: [25732 30704 37185 7490 41496]\n", - "25889: [54355 51913 3991 26410 33957]\n", - "18231: [40123 11579 26970 33717 33571]\n", - "60748: [30642 53335 25932 8308 58466]\n", - "18510: [22334 30868 19506 41888 45968]\n" + "29252: [58958 19972 27924 41961 56659]\n", + "55146: [ 438 45333 36712 42300 59117]\n", + "27501: [23479 49189 27932 62264 55559]\n", + "36339: [56544 28952 6192 30572 18611]\n", + "61988: [19227 25210 11310 21755 58570]\n", + "56812: [62335 7101 25890 25167 62570]\n" ] } ], - "execution_count": 28 + "execution_count": 22 }, { "metadata": {}, @@ -218,8 +219,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.760751Z", - "start_time": "2025-03-16T11:17:06.758403Z" + "end_time": "2025-03-16T12:51:50.496968Z", + "start_time": "2025-03-16T12:51:50.494830Z" } }, "cell_type": "code", @@ -232,15 +233,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[21 15 9 3 27]\n", - " [22 16 10 4 28]\n", - " [18 12 6 0 24]\n", - " [20 14 8 2 26]\n", - " [23 17 11 5 29]]\n" + "[[10 4 22 16 28]\n", + " [11 5 23 17 29]\n", + " [ 9 3 21 15 27]\n", + " [ 8 2 20 14 26]\n", + " [ 7 1 19 13 25]]\n" ] } ], - "execution_count": 29 + "execution_count": 23 }, { "metadata": {}, @@ -253,8 +254,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.783887Z", - "start_time": "2025-03-16T11:17:06.780577Z" + "end_time": "2025-03-16T12:51:50.521411Z", + "start_time": "2025-03-16T12:51:50.518203Z" } }, "cell_type": "code", @@ -275,25 +276,25 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [21 15 9 3 27]\n", - "Key 1: [22 16 10 4 28]\n", - "Key 2: [18 12 6 0 24]\n", - "Key 3: [20 14 8 2 26]\n", - "Key 4: [23 17 11 5 29]\n", - "User Passcode: [21 15 9 3]\n", + "Key 0: [10 4 22 16 28]\n", + "Key 1: [11 5 23 17 29]\n", + "Key 2: [ 9 3 21 15 27]\n", + "Key 3: [ 8 2 20 14 26]\n", + "Key 4: [ 7 1 19 13 25]\n", + "User Passcode: [10 4 22 16]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side properties: [33717, 26970, 11579, 40123]\n" + "User Passcode Server-side properties: [25210, 19227, 21755, 11310]\n" ] } ], - "execution_count": 30 + "execution_count": 24 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.799077Z", - "start_time": "2025-03-16T11:17:06.796555Z" + "end_time": "2025-03-16T12:51:50.539870Z", + "start_time": "2025-03-16T12:51:50.536979Z" } }, "cell_type": "code", @@ -309,23 +310,23 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [18 17 8 4 27]\n", - "Key 1: [23 16 6 3 26]\n", - "Key 2: [22 15 11 2 24]\n", - "Key 3: [21 14 10 0 29]\n", - "Key 4: [20 12 9 5 28]\n", + "Key 0: [ 7 2 22 15 29]\n", + "Key 1: [ 9 4 23 14 25]\n", + "Key 2: [ 8 5 19 16 27]\n", + "Key 3: [10 1 21 17 26]\n", + "Key 4: [11 3 20 13 28]\n", "Selected Keys\n", - "[3, 2, 4, 1]\n" + "[3, 1, 0, 2]\n" ] } ], - "execution_count": 31 + "execution_count": 25 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.061018Z", - "start_time": "2025-03-16T11:17:06.824471Z" + "end_time": "2025-03-16T12:51:50.790192Z", + "start_time": "2025-03-16T12:51:50.552031Z" } }, "cell_type": "code", @@ -343,7 +344,7 @@ ] } ], - "execution_count": 32 + "execution_count": 26 }, { "metadata": {}, @@ -362,8 +363,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.070338Z", - "start_time": "2025-03-16T11:17:07.066596Z" + "end_time": "2025-03-16T12:51:50.801004Z", + "start_time": "2025-03-16T12:51:50.797001Z" } }, "cell_type": "code", @@ -371,7 +372,7 @@ "from src.user_cipher import UserCipher\n", "\n", "user_set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\n", - "combined_set_key = np.bitwise_xor(user_set_key, customer.cipher.set_key)\n", + "combined_set_key = user_set_key ^ customer.cipher.set_key\n", "user_keys = UserCipher(\n", " prop_key = np.array([\n", " 57200, 8398, 54694, 25997, 30388,\n", @@ -384,8 +385,7 @@ " pass_key=np.array([16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206]),\n", " mask_key=np.array([29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325]),\n", " combined_set_key=np.array(combined_set_key),\n", - " salt=b'$2b$12$fX.in.GGAjz3QBBwqSWc6e',\n", - " max_nkode_len=customer.nkode_policy.max_nkode_len, \n", + " max_nkode_len=customer.nkode_policy.max_nkode_len,\n", ")\n", "\n", "ordered_customer_prop_key = customer.cipher.prop_key[user_passcode] # [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", @@ -398,12 +398,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [18231, 18231, 18231, 18231]\n", - "Passcode Prop Vals: [33717 26970 11579 40123]\n" + "Passcode Set Vals: [61988, 61988, 61988, 61988]\n", + "Passcode Prop Vals: [25210 19227 21755 11310]\n" ] } ], - "execution_count": 33 + "execution_count": 27 }, { "metadata": {}, @@ -416,16 +416,16 @@ "3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n", "where i is the index\n", " \n", - "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_set_key\n", - "- mask = mask_rand_num ^ set_val ^ set_rand_numb ^ set_val\n", + "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_combined_set_key\n", + "- mask = mask_key ^ (customer_set_key) ^ set_rand_numb ^ set_val\n", "- mask = mask_rand_num ^ set_rand_numb # set_val is cancelled out" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.150818Z", - "start_time": "2025-03-16T11:17:07.148125Z" + "end_time": "2025-03-16T12:51:50.811181Z", + "start_time": "2025-03-16T12:51:50.808721Z" } }, "cell_type": "code", @@ -433,12 +433,11 @@ "padded_ordered_customer_set = user_keys.pad_user_mask(ordered_customer_set_key, customer.cipher.set_key)\n", "set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_ordered_customer_set]\n", "ordered_set_key = user_keys.combined_set_key[set_idx]\n", - "ciphered_mask = ordered_set_key ^ padded_ordered_customer_set\n", - "ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key)\n", - "mask = user_keys.encode_base64_str(ciphered_mask)" + "mask = ordered_set_key ^ padded_ordered_customer_set ^ user_keys.mask_key\n", + "encoded_mask = user_keys.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 34 + "execution_count": 28 }, { "metadata": {}, @@ -455,16 +454,14 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.413741Z", - "start_time": "2025-03-16T11:17:07.177338Z" + "end_time": "2025-03-16T12:51:51.058527Z", + "start_time": "2025-03-16T12:51:50.823135Z" } }, "cell_type": "code", "source": [ - "import bcrypt\n", "import hashlib\n", "import base64\n", - "\n", "ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", "passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", @@ -473,20 +470,19 @@ "\n", "ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key)\n", "\n", - "#passcode_bytes = int_array_to_bytes(ciphered_code)\n", "passcode_bytes = ciphered_code.tobytes()\n", - "passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest())\n", - "hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt)\n", + "passcode_digest = user_keys.prehash_passcode(user_passcode, customer.cipher)# base64.b64encode(hashlib.sha256(passcode_bytes).digest())\n", + "hashed_data = bcrypt.hashpw(passcode_digest, bcrypt.gensalt(rounds=12))\n", "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 35 + "execution_count": 29 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.428200Z", - "start_time": "2025-03-16T11:17:07.426275Z" + "end_time": "2025-03-16T12:51:51.063978Z", + "start_time": "2025-03-16T12:51:51.061965Z" } }, "cell_type": "code", @@ -494,12 +490,12 @@ "from src.models import EncipheredNKode\n", "\n", "enciphered_nkode = EncipheredNKode(\n", - " mask=mask,\n", + " mask=encoded_mask,\n", " code=code,\n", ")" ], "outputs": [], - "execution_count": 36 + "execution_count": 30 }, { "metadata": {}, @@ -513,8 +509,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.668766Z", - "start_time": "2025-03-16T11:17:07.436169Z" + "end_time": "2025-03-16T12:51:51.305419Z", + "start_time": "2025-03-16T12:51:51.071190Z" } }, "cell_type": "code", @@ -532,17 +528,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [18 19 20 21 22 23]\n", - "Key 1: [12 13 14 15 16 17]\n", - "Key 2: [ 6 7 8 9 10 11]\n", - "Key 3: [0 1 2 3 4 5]\n", + "Key 0: [ 6 7 8 9 10 11]\n", + "Key 1: [0 1 2 3 4 5]\n", + "Key 2: [18 19 20 21 22 23]\n", + "Key 3: [12 13 14 15 16 17]\n", "Key 4: [24 25 26 27 28 29]\n", "Selected Keys: [0, 1, 2, 3]\n", "True\n" ] } ], - "execution_count": 37 + "execution_count": 31 }, { "metadata": {}, @@ -574,16 +570,16 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.679342Z", - "start_time": "2025-03-16T11:17:07.676500Z" + "end_time": "2025-03-16T12:51:51.315072Z", + "start_time": "2025-03-16T12:51:51.312249Z" } }, "cell_type": "code", "source": [ "user = api.customers[customer_id].users[username]\n", - "decoded_mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", - "deciphered_mask = np.bitwise_xor(decoded_mask, user.cipher.mask_key)\n", - "set_key = np.bitwise_xor(customer.cipher.set_key, user.cipher.combined_set_key)\n", + "mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", + "deciphered_mask = mask ^ user.cipher.mask_key\n", + "set_key = customer.cipher.set_key ^ user.cipher.combined_set_key\n", "passcode_sets = []\n", "for set_cipher in deciphered_mask[:passcode_len]:\n", " set_idx = np.where(set_key == set_cipher)[0][0]\n", @@ -595,11 +591,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[18231, 18231, 18231, 18231]\n" + "[61988, 61988, 61988, 61988]\n" ] } ], - "execution_count": 38 + "execution_count": 32 }, { "metadata": {}, @@ -609,8 +605,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.690303Z", - "start_time": "2025-03-16T11:17:07.687880Z" + "end_time": "2025-03-16T12:51:51.324331Z", + "start_time": "2025-03-16T12:51:51.321676Z" } }, "cell_type": "code", @@ -635,7 +631,7 @@ ] } ], - "execution_count": 39 + "execution_count": 33 }, { "metadata": {}, @@ -645,14 +641,14 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.937619Z", - "start_time": "2025-03-16T11:17:07.705824Z" + "end_time": "2025-03-16T12:51:51.571291Z", + "start_time": "2025-03-16T12:51:51.338129Z" } }, "cell_type": "code", "source": [ - "enciphered_nkode = user.cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher)\n", - "print(enciphered_nkode == user.enciphered_passcode.code)\n" + "enciphered_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n", + "print(enciphered_nkode)\n" ], "outputs": [ { @@ -663,7 +659,7 @@ ] } ], - "execution_count": 40 + "execution_count": 34 }, { "metadata": {}, @@ -679,8 +675,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.414223Z", - "start_time": "2025-03-16T11:17:07.946756Z" + "end_time": "2025-03-16T12:51:52.048827Z", + "start_time": "2025-03-16T12:51:51.579003Z" } }, "cell_type": "code", @@ -705,14 +701,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", - "mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", + "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n", + "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n", "True\n", - "mask: Q7Dw+o+qDTXYlg8TC2hOkn9r1s0=, code: $2b$12$Xp2oD/vFDHwzvBmELBxC6e.iLK4xn5ZtLwW4UCFVoFY2ioTvGaWwa\n" + "mask: rY763sxEo+aU8G2h7Avf9btV0h8=, code: $2b$12$ZB.x5hSFVjDg4niUAL7TsOtwkTG2uXedMsnFqXNjm0Y0SDf5fgOui\n" ] } ], - "execution_count": 41 + "execution_count": 35 }, { "metadata": {}, @@ -726,8 +722,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.424680Z", - "start_time": "2025-03-16T11:17:08.421148Z" + "end_time": "2025-03-16T12:51:52.059119Z", + "start_time": "2025-03-16T12:51:52.055816Z" } }, "cell_type": "code", @@ -739,7 +735,7 @@ "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 42 + "execution_count": 36 }, { "metadata": {}, @@ -752,8 +748,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.434263Z", - "start_time": "2025-03-16T11:17:08.431951Z" + "end_time": "2025-03-16T12:51:52.068302Z", + "start_time": "2025-03-16T12:51:52.065921Z" } }, "cell_type": "code", @@ -766,7 +762,7 @@ " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 43 + "execution_count": 37 }, { "metadata": {}, @@ -776,8 +772,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.683077Z", - "start_time": "2025-03-16T11:17:08.447107Z" + "end_time": "2025-03-16T12:51:52.311112Z", + "start_time": "2025-03-16T12:51:52.074783Z" } }, "cell_type": "code", @@ -791,7 +787,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": 44 + "execution_count": 38 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 6177ba9..c06ba55 100644 --- a/src/customer.py +++ b/src/customer.py @@ -41,8 +41,7 @@ class Customer: selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) presumed_property_idxs.append(selected_prop_idx) - enciphered_prop = user.cipher.encipher_salt_hash_code(presumed_property_idxs, self.cipher) - if enciphered_prop != user.enciphered_passcode.code: + if not user.cipher.compare_nkode(presumed_property_idxs, self.cipher,user.enciphered_passcode.code): return False if user.renew: diff --git a/src/user_cipher.py b/src/user_cipher.py index 5e2123a..1136d22 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -3,7 +3,6 @@ import hashlib from dataclasses import dataclass import bcrypt import numpy as np -from secrets import choice from src.models import EncipheredNKode, KeypadSize from src.customer_cipher import CustomerCipher @@ -14,83 +13,89 @@ class UserCipher: combined_set_key: np.ndarray pass_key: np.ndarray mask_key: np.ndarray - salt: bytes max_nkode_len: int @classmethod - def create(cls, keypad_size: KeypadSize, set_values: np.ndarray, max_nkode_len: int) -> 'UserCipher': - if len(set_values) != keypad_size.props_per_key: + def create(cls, keypad_size: KeypadSize, customer_set_key: np.ndarray, max_nkode_len: int) -> 'UserCipher': + if len(customer_set_key) != keypad_size.props_per_key: raise ValueError("Invalid set values") - set_values_array = np.array(set_values, dtype=np.uint16) - set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) - set_key = np.bitwise_xor(set_key, set_values_array) + user_set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) return UserCipher( prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False), mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False), - combined_set_key=set_key, - salt=bcrypt.gensalt(), + combined_set_key=user_set_key ^ customer_set_key, max_nkode_len=max_nkode_len ) def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray: if len(user_mask) >= self.max_nkode_len: - raise ValueError("User mask is too long") + raise ValueError("User encoded_mask is too long") padding_size = self.max_nkode_len - len(user_mask) - # Generate padding directly using np.random.choice padding = np.random.choice(set_vals, size=padding_size, replace=True).astype(np.uint16) - # Concatenate original mask with padding return np.concatenate([user_mask, padding]) @staticmethod def encode_base64_str(data: np.ndarray) -> str: - return base64.b64encode(int_array_to_bytes(data)).decode("utf-8") + return base64.b64encode( b"".join([int(num).to_bytes(2, byteorder='big') for num in data])).decode("utf-8") @staticmethod def decode_base64_str(data: str) -> np.ndarray: byte_data = base64.b64decode(data) int_list = [] - for i in range(0, len(byte_data), 2): int_val = int.from_bytes(byte_data[i:i + 2], byteorder='big') int_list.append(int_val) return np.array(int_list, dtype=np.uint16) - def _hash_passcode(self, passcode: np.ndarray) -> str: - passcode_bytes = passcode.astype(np.uint16).tobytes() - passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) - hashed_data = bcrypt.hashpw(passcode_digest, self.salt) - return hashed_data.decode("utf-8") - def encipher_nkode( self, passcode_prop_idx: list[int], customer_cipher: CustomerCipher ) -> EncipheredNKode: mask = self.encipher_mask(passcode_prop_idx, customer_cipher) - code = self.encipher_salt_hash_code(passcode_prop_idx, customer_cipher) + code = self.hash_nkode(passcode_prop_idx, customer_cipher) return EncipheredNKode( code=code, mask=mask ) - def encipher_salt_hash_code( + def hash_nkode( self, passcode_prop_idx: list[int], customer_prop: CustomerCipher, ) -> str: + salt = bcrypt.gensalt(rounds=12) + passcode_bytes = self.prehash_passcode(passcode_prop_idx, customer_prop) + passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) + hashed_data = bcrypt.hashpw(passcode_digest, salt) + return hashed_data.decode("utf-8") + + def compare_nkode( + self, + passcode_prop_idx: list[int], + customer_prop: CustomerCipher, + hashed_passcode: str + ) -> bool: + passcode_bytes = self.prehash_passcode(passcode_prop_idx, customer_prop) + passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) + return bcrypt.checkpw(passcode_digest, hashed_passcode.encode('utf-8')) + + def prehash_passcode( + self, + passcode_prop_idx: list[int], + customer_prop: CustomerCipher + ) -> bytes: passcode_len = len(passcode_prop_idx) passcode_cipher = self.pass_key.copy() passcode_cipher[:passcode_len] = ( - passcode_cipher[:passcode_len] ^ - self.prop_key[passcode_prop_idx] ^ - customer_prop.prop_key[passcode_prop_idx] + passcode_cipher[:passcode_len] ^ + self.prop_key[passcode_prop_idx] ^ + customer_prop.prop_key[passcode_prop_idx] ) - - return self._hash_passcode(passcode_cipher) - + return passcode_cipher.astype(np.uint16).tobytes() def encipher_mask( self, @@ -100,33 +105,21 @@ class UserCipher: customer_props = customer_cipher.prop_key[passcode_prop_idx] customer_sets = [customer_cipher.get_prop_set_val(prop) for prop in customer_props] padded_customer_sets = self.pad_user_mask(np.array(customer_sets), customer_cipher.set_key) - - # Get indices of set values set_idx = np.array([customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets], dtype=np.uint16) - mask_set_keys = np.array([self.combined_set_key[idx] for idx in set_idx], dtype=np.uint16) + ordered_set_key = self.combined_set_key[set_idx] + mask = ordered_set_key ^ padded_customer_sets ^ self.mask_key + encoded_mask = self.encode_base64_str(mask) + return encoded_mask - # XOR operations - ciphered_mask = np.bitwise_xor(mask_set_keys, padded_customer_sets) - ciphered_mask = np.bitwise_xor(ciphered_mask, self.mask_key) - - mask = self.encode_base64_str(ciphered_mask) - return mask - - def decipher_mask(self, mask: str, set_vals: np.ndarray, passcode_len: int) -> np.ndarray: - set_vals_array = np.array(set_vals, dtype=np.uint16) - decoded_mask = self.decode_base64_str(mask) - deciphered_mask = np.bitwise_xor(decoded_mask, self.mask_key) - - set_key_rand_component = np.bitwise_xor(set_vals_array, self.combined_set_key) + def decipher_mask(self, encoded_mask: str, customer_set_key: np.ndarray, passcode_len: int) -> list[int]: + mask = self.decode_base64_str(encoded_mask) + # user_set_key ordered by the user's nkode and padded to be length max_nkode_len + ordered_set_key = mask ^ self.mask_key + user_set_key = customer_set_key ^ self.combined_set_key passcode_sets = [] + for partial_set in ordered_set_key[:passcode_len]: + set_idx = np.where(user_set_key == partial_set)[0][0] + passcode_sets.append(int(customer_set_key[set_idx])) + return passcode_sets - for set_cipher in deciphered_mask[:passcode_len]: - # Find index where values match - set_idx = np.where(set_key_rand_component == set_cipher)[0][0] - passcode_sets.append(set_vals[set_idx]) - - return np.array(passcode_sets) - -def int_array_to_bytes(int_arr: np.ndarray, byte_size: int = 2) -> bytes: - return b"".join([int(num).to_bytes(byte_size, byteorder='big') for num in int_arr]) \ No newline at end of file -- 2.49.1 From fb650a40865028a3f3c282db0c7130199683ae59 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 16 Mar 2025 13:26:45 -0500 Subject: [PATCH 34/85] implement split shuffle --- src/customer.py | 9 +-------- src/user_keypad.py | 22 ++++++---------------- test/test_user_keypad.py | 34 +++++++++++++++++----------------- 3 files changed, 24 insertions(+), 41 deletions(-) diff --git a/src/customer.py b/src/customer.py index c06ba55..cb65199 100644 --- a/src/customer.py +++ b/src/customer.py @@ -22,32 +22,25 @@ class Customer: def valid_key_entry(self, username, selected_keys) -> bool: if username not in self.users: raise ValueError(f"User '{username}' does not exist") - numb_of_keys = self.cipher.keypad_size.numb_of_keys if not all(0 <= key_idx < numb_of_keys for key_idx in selected_keys): raise ValueError(f"Invalid key indices. Must be between 0 and {numb_of_keys - 1}") - passcode_len = len(selected_keys) user = self.users[username] - passcode_set_vals = user.cipher.decipher_mask( user.enciphered_passcode.mask, self.cipher.set_key, passcode_len) set_vals_idx = [self.cipher.get_set_index(set_val) for set_val in passcode_set_vals] - presumed_property_idxs = [] for idx in range(passcode_len): key_numb = selected_keys[idx] set_idx = set_vals_idx[idx] selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) presumed_property_idxs.append(selected_prop_idx) - if not user.cipher.compare_nkode(presumed_property_idxs, self.cipher,user.enciphered_passcode.code): return False - if user.renew: user.refresh_passcode(presumed_property_idxs, self.cipher) - - user.user_keypad.partial_keypad_shuffle() + user.user_keypad.split_shuffle() return True def renew_keys(self) -> bool: diff --git a/src/user_keypad.py b/src/user_keypad.py index 1405dd3..ffcb0b6 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -62,22 +62,12 @@ class UserKeypad: ) self.keypad = dispersed_keypad.reshape(-1) - def partial_keypad_shuffle(self): - # TODO: this should be split shuffle - #numb_of_selected_sets = self.keypad_size.props_per_key // 2 - ## randomly shuffle half the sets. if props_per_key is odd, randomly add one 50% of the time - #numb_of_selected_sets += choice([0, 1]) if (self.keypad_size.props_per_key & 1) == 1 else 0 - #selected_sets = secure_fisher_yates_shuffle(list(range(self.keypad_size.props_per_key)))[:numb_of_selected_sets] - #user_keypad_matrix = self.keypad_matrix() - #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) - #keypad_by_sets = [] - #for idx, props in enumerate(matrix_transpose(shuffled_keys)): - # if idx in selected_sets: - # keypad_by_sets.append(secure_fisher_yates_shuffle(props)) - # else: - # keypad_by_sets.append(props) - #self.keypad = matrix_to_list(matrix_transpose(keypad_by_sets)) - pass + def split_shuffle(self): + prop_permutation = np.random.permutation(self.keypad_size.props_per_key)[: self.keypad_size.props_per_key // 2] + key_permutation = np.random.permutation(self.keypad_size.numb_of_keys) + keypad_view = self.keypad_matrix().copy() + keypad_view[:, prop_permutation] = keypad_view[key_permutation, :][:, prop_permutation] + self.keypad = keypad_view.reshape(-1) def property_adjacency_graph(self) -> dict[int, set[int]]: user_keypad_keypad = self.keypad_matrix() diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index b7deadf..f7bb7a6 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -5,7 +5,7 @@ from src.models import KeypadSize @pytest.fixture() def user_keypad(): - return UserKeypad.create(keypad_size=KeypadSize(props_per_key=7, numb_of_keys=10)) + return UserKeypad.create(keypad_size=KeypadSize(props_per_key=8, numb_of_keys=8)) def test_dispersion(user_keypad): @@ -17,19 +17,19 @@ def test_dispersion(user_keypad): assert (adj_graph.isdisjoint(post_dispersion_graph[prop])) -#def test_shuffle_props(user_keypad): -# """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get -# expected statistical outcomes like: -# - every property gets to every key with a uniform distribution -# - every property is adjacent to every other property with uniform distribution -# - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) -# """ -# pre_shuffle_keypad = user_keypad.keypad -# user_keypad.partial_keypad_shuffle() -# post_shuffle_keypad = user_keypad.keypad -# assert (not all( -# post_shuffle_keypad[idx] == pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) -# )) -# assert (not all( -# post_shuffle_keypad[idx] != pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) -# )) +def test_shuffle_props(user_keypad): + """there's no easy way to test this. At some point we'll have to run this code thousands of time to see if we get + expected statistical outcomes like: + - every property gets to every key with a uniform distribution + - every property is adjacent to every other property with uniform distribution + - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) + """ + pre_shuffle_keypad = user_keypad.keypad.copy() + user_keypad.split_shuffle() + post_shuffle_keypad = user_keypad.keypad.copy() + assert (not all( + post_shuffle_keypad[idx] == pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) + )) + assert (not all( + post_shuffle_keypad[idx] != pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) + )) -- 2.49.1 From f9f7081fc62b19dc2f3b9615828ea47338b31453 Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 17 Mar 2025 04:31:01 -0500 Subject: [PATCH 35/85] fix split shuffle --- notebooks/nkode_tutorial.ipynb | 17 ++++++++++++----- src/user_keypad.py | 6 +++++- test/test_user_keypad.py | 5 ++++- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 513645a..b67acaa 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -205,12 +205,14 @@ "source": [ "### User Signup\n", "To create a new must call this endpoints in order:\n", - "1. Generate Keypad\n", - "2. Set User NKode\n", - "3. Confirm User NKode\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. To make the keypad dispersable, the server will randomly drop properties sets to the number of properties is equal to the number of keys. In our case, the server drops 1 properties set to give us a 5 X 5 keypad with possible index values ranging from 0-29.\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 properties sets to the number of properties is equal to the number of keys.\n", + " In our case, the server drops 1 properties set 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 \"real\" properties is. All they do is specify an index in the customer keypad\n" @@ -248,7 +250,12 @@ "cell_type": "markdown", "source": [ "#### Set nKode\n", - "The user identifies properties in the keypad they want in their nkode. Each properties in the gui has an index value. Below the user has selected 16, 9, 6, 19. Graphically represent with anything. The only requirement is that the graphical properties must be associated with the same index value everytime the user goes to login. If the user wants to change anything about their keypad(the number of keys, properties or graphical properties), they must also change their nkode." + "The user identifies properties in the keypad they want in their nkode.\n", + "Each properties in the gui has an index value.\n", + "Below the user has selected 16, 9, 6, 19.\n", + "Graphically represent with anything.\n", + "The only requirement is that the graphical properties must be associated with the same index value everytime the user goes to login.\n", + "If the user wants to change anything about their keypad(the number of keys, properties or graphical properties), they must also change their nkode." ] }, { diff --git a/src/user_keypad.py b/src/user_keypad.py index ffcb0b6..da0d750 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -63,9 +63,13 @@ class UserKeypad: self.keypad = dispersed_keypad.reshape(-1) def split_shuffle(self): + # shuffle all keys + keypad_view = self.keypad_matrix() + np.random.shuffle(keypad_view) + # select half the property sets prop_permutation = np.random.permutation(self.keypad_size.props_per_key)[: self.keypad_size.props_per_key // 2] key_permutation = np.random.permutation(self.keypad_size.numb_of_keys) - keypad_view = self.keypad_matrix().copy() + # shuffle the selected property sets to new keys as a group keypad_view[:, prop_permutation] = keypad_view[key_permutation, :][:, prop_permutation] self.keypad = keypad_view.reshape(-1) diff --git a/test/test_user_keypad.py b/test/test_user_keypad.py index f7bb7a6..5f8fd6e 100644 --- a/test/test_user_keypad.py +++ b/test/test_user_keypad.py @@ -5,7 +5,7 @@ from src.models import KeypadSize @pytest.fixture() def user_keypad(): - return UserKeypad.create(keypad_size=KeypadSize(props_per_key=8, numb_of_keys=8)) + return UserKeypad.create(keypad_size=KeypadSize(props_per_key=5, numb_of_keys=5)) def test_dispersion(user_keypad): @@ -25,8 +25,11 @@ def test_shuffle_props(user_keypad): - the order in which the cipher move from key to key is random (i.e. the distance traveled is uniform) """ pre_shuffle_keypad = user_keypad.keypad.copy() + print("") + print(user_keypad.keypad_matrix()) user_keypad.split_shuffle() post_shuffle_keypad = user_keypad.keypad.copy() + print(user_keypad.keypad_matrix()) assert (not all( post_shuffle_keypad[idx] == pre_shuffle_keypad[idx] for idx in range(len(post_shuffle_keypad)) )) -- 2.49.1 From cfef58613c9cb377b7a415a03af815895984d84f Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 17 Mar 2025 09:58:28 -0500 Subject: [PATCH 36/85] fix bugs --- docs/render_readme.py | 9 +- notebooks/nkode_tutorial.ipynb | 276 ++++++++++++++++----------------- src/customer.py | 24 +-- src/models.py | 4 +- src/nkode_api.py | 5 +- src/user_cipher.py | 3 +- src/user_keypad.py | 31 ++-- src/user_signup_session.py | 1 + test/test_nkode_api.py | 2 - 9 files changed, 170 insertions(+), 185 deletions(-) diff --git a/docs/render_readme.py b/docs/render_readme.py index df4551c..b33f7af 100644 --- a/docs/render_readme.py +++ b/docs/render_readme.py @@ -145,14 +145,7 @@ if __name__ == "__main__": """ set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in login_passcode_sets] - - presumed_selected_properties_idx = [] - for idx in range(passcode_len): - key_numb = selected_keys_login[idx] - set_idx = set_vals_idx[idx] - selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_selected_properties_idx.append(selected_prop_idx) - + presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, set_vals_idx) """ RENEW KEYS """ diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index b67acaa..c396758 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -13,12 +13,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.430038Z", - "start_time": "2025-03-16T12:51:50.426267Z" + "end_time": "2025-03-17T14:56:54.677924Z", + "start_time": "2025-03-17T14:56:54.674055Z" } }, "outputs": [], - "execution_count": 17 + "execution_count": 52 }, { "cell_type": "code", @@ -33,7 +33,7 @@ "\n", "\n", "def keypad_view(keypad: np.ndarray, props_per_key: int):\n", - " print(\"Keypad View\")\n", + " print(\"\\nKeypad View\")\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" @@ -41,12 +41,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.441672Z", - "start_time": "2025-03-16T12:51:50.437979Z" + "end_time": "2025-03-17T14:56:54.687467Z", + "start_time": "2025-03-17T14:56:54.683811Z" } }, "outputs": [], - "execution_count": 18 + "execution_count": 53 }, { "cell_type": "code", @@ -56,12 +56,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.451921Z", - "start_time": "2025-03-16T12:51:50.449896Z" + "end_time": "2025-03-17T14:56:54.698258Z", + "start_time": "2025-03-17T14:56:54.696478Z" } }, "outputs": [], - "execution_count": 19 + "execution_count": 54 }, { "cell_type": "markdown", @@ -101,12 +101,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.462932Z", - "start_time": "2025-03-16T12:51:50.458899Z" + "end_time": "2025-03-17T14:56:54.710370Z", + "start_time": "2025-03-17T14:56:54.706283Z" } }, "outputs": [], - "execution_count": 20 + "execution_count": 55 }, { "cell_type": "markdown", @@ -139,8 +139,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.471838Z", - "start_time": "2025-03-16T12:51:50.469241Z" + "end_time": "2025-03-17T14:56:54.735642Z", + "start_time": "2025-03-17T14:56:54.732921Z" } }, "outputs": [ @@ -148,17 +148,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Set Key: [29252 55146 27501 36339 61988 56812]\n", + "Customer Set Key: [15749 12291 52872 49860 5208 28289]\n", "Customer Properties Key:\n", - "[58958 438 23479 56544 19227 62335]\n", - "[19972 45333 49189 28952 25210 7101]\n", - "[27924 36712 27932 6192 11310 25890]\n", - "[41961 42300 62264 30572 21755 25167]\n", - "[56659 59117 55559 18611 58570 62570]\n" + "[42849 39396 49203 20824 30786 27252]\n", + "[16470 25626 64566 34252 23582 48009]\n", + "[20454 51880 19394 58106 12995 59391]\n", + "[43703 4483 7702 5423 24081 57678]\n", + "[43822 27495 59095 24249 60164 21395]\n" ] } ], - "execution_count": 21 + "execution_count": 56 }, { "cell_type": "markdown", @@ -178,8 +178,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.482832Z", - "start_time": "2025-03-16T12:51:50.480624Z" + "end_time": "2025-03-17T14:56:54.781223Z", + "start_time": "2025-03-17T14:56:54.778381Z" } }, "outputs": [ @@ -188,16 +188,16 @@ "output_type": "stream", "text": [ "Set to Properties Map:\n", - "29252: [58958 19972 27924 41961 56659]\n", - "55146: [ 438 45333 36712 42300 59117]\n", - "27501: [23479 49189 27932 62264 55559]\n", - "36339: [56544 28952 6192 30572 18611]\n", - "61988: [19227 25210 11310 21755 58570]\n", - "56812: [62335 7101 25890 25167 62570]\n" + "15749: [42849 16470 20454 43703 43822]\n", + "12291: [39396 25626 51880 4483 27495]\n", + "52872: [49203 64566 19394 7702 59095]\n", + "49860: [20824 34252 58106 5423 24249]\n", + "5208: [30786 23582 12995 24081 60164]\n", + "28289: [27252 48009 59391 57678 21395]\n" ] } ], - "execution_count": 22 + "execution_count": 57 }, { "metadata": {}, @@ -221,8 +221,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.496968Z", - "start_time": "2025-03-16T12:51:50.494830Z" + "end_time": "2025-03-17T14:56:54.814781Z", + "start_time": "2025-03-17T14:56:54.812317Z" } }, "cell_type": "code", @@ -235,15 +235,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[10 4 22 16 28]\n", - " [11 5 23 17 29]\n", - " [ 9 3 21 15 27]\n", - " [ 8 2 20 14 26]\n", - " [ 7 1 19 13 25]]\n" + "[[24 7 2 3 29]\n", + " [12 13 26 9 11]\n", + " [ 6 1 8 15 23]\n", + " [ 0 25 20 21 5]\n", + " [18 19 14 27 17]]\n" ] } ], - "execution_count": 23 + "execution_count": 58 }, { "metadata": {}, @@ -261,8 +261,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.521411Z", - "start_time": "2025-03-16T12:51:50.518203Z" + "end_time": "2025-03-17T14:56:54.845027Z", + "start_time": "2025-03-17T14:56:54.840926Z" } }, "cell_type": "code", @@ -282,26 +282,27 @@ "name": "stdout", "output_type": "stream", "text": [ + "\n", "Keypad View\n", - "Key 0: [10 4 22 16 28]\n", - "Key 1: [11 5 23 17 29]\n", - "Key 2: [ 9 3 21 15 27]\n", - "Key 3: [ 8 2 20 14 26]\n", - "Key 4: [ 7 1 19 13 25]\n", - "User Passcode: [10 4 22 16]\n", + "Key 0: [24 7 2 3 29]\n", + "Key 1: [12 13 26 9 11]\n", + "Key 2: [ 6 1 8 15 23]\n", + "Key 3: [ 0 25 20 21 5]\n", + "Key 4: [18 19 14 27 17]\n", + "User Passcode: [24 7 2 3]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side properties: [25210, 19227, 21755, 11310]\n" + "User Passcode Server-side properties: [43822, 25626, 49203, 20824]\n" ] } ], - "execution_count": 24 + "execution_count": 59 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.539870Z", - "start_time": "2025-03-16T12:51:50.536979Z" + "end_time": "2025-03-17T14:56:54.869735Z", + "start_time": "2025-03-17T14:56:54.866365Z" } }, "cell_type": "code", @@ -316,42 +317,35 @@ "name": "stdout", "output_type": "stream", "text": [ + "\n", "Keypad View\n", - "Key 0: [ 7 2 22 15 29]\n", - "Key 1: [ 9 4 23 14 25]\n", - "Key 2: [ 8 5 19 16 27]\n", - "Key 3: [10 1 21 17 26]\n", - "Key 4: [11 3 20 13 28]\n", + "Key 0: [24 13 20 15 17]\n", + "Key 1: [ 6 7 14 21 11]\n", + "Key 2: [ 0 1 26 27 29]\n", + "Key 3: [18 25 2 9 23]\n", + "Key 4: [12 19 8 3 5]\n", "Selected Keys\n", - "[3, 1, 0, 2]\n" + "[0, 1, 3, 4]\n" ] } ], - "execution_count": 25 + "execution_count": 60 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.790192Z", - "start_time": "2025-03-16T12:51:50.552031Z" + "end_time": "2025-03-17T14:56:55.202903Z", + "start_time": "2025-03-17T14:56:54.894565Z" } }, "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, session_id)\n", - "print(success)" + "assert success" ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - } - ], - "execution_count": 26 + "outputs": [], + "execution_count": 61 }, { "metadata": {}, @@ -370,8 +364,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.801004Z", - "start_time": "2025-03-16T12:51:50.797001Z" + "end_time": "2025-03-17T14:56:55.227304Z", + "start_time": "2025-03-17T14:56:55.222844Z" } }, "cell_type": "code", @@ -405,12 +399,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [61988, 61988, 61988, 61988]\n", - "Passcode Prop Vals: [25210 19227 21755 11310]\n" + "Passcode Set Vals: [15749, 12291, 52872, 49860]\n", + "Passcode Prop Vals: [43822 25626 49203 20824]\n" ] } ], - "execution_count": 27 + "execution_count": 62 }, { "metadata": {}, @@ -431,8 +425,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:50.811181Z", - "start_time": "2025-03-16T12:51:50.808721Z" + "end_time": "2025-03-17T14:56:55.260751Z", + "start_time": "2025-03-17T14:56:55.258152Z" } }, "cell_type": "code", @@ -444,7 +438,7 @@ "encoded_mask = user_keys.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 28 + "execution_count": 63 }, { "metadata": {}, @@ -461,14 +455,12 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:51.058527Z", - "start_time": "2025-03-16T12:51:50.823135Z" + "end_time": "2025-03-17T14:56:55.590495Z", + "start_time": "2025-03-17T14:56:55.285097Z" } }, "cell_type": "code", "source": [ - "import hashlib\n", - "import base64\n", "ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", "passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", @@ -483,13 +475,13 @@ "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 29 + "execution_count": 64 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:51.063978Z", - "start_time": "2025-03-16T12:51:51.061965Z" + "end_time": "2025-03-17T14:56:55.595860Z", + "start_time": "2025-03-17T14:56:55.593975Z" } }, "cell_type": "code", @@ -502,7 +494,7 @@ ")" ], "outputs": [], - "execution_count": 30 + "execution_count": 65 }, { "metadata": {}, @@ -516,8 +508,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:51.305419Z", - "start_time": "2025-03-16T12:51:51.071190Z" + "end_time": "2025-03-17T14:56:55.910640Z", + "start_time": "2025-03-17T14:56:55.605472Z" } }, "cell_type": "code", @@ -525,27 +517,32 @@ "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(user_passcode, login_keypad, keypad_size.props_per_key)\n", - "print(f\"Selected Keys: {selected_keys_login}\")\n", + "print(f\"User Passcode: {user_passcode}\\n\")\n", + "print(f\"Selected Keys:\\n {selected_keys_login}\\n\")\n", "success = api.login(customer_id, username, selected_keys_login)\n", - "print(success)" + "assert success" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "\n", "Keypad View\n", - "Key 0: [ 6 7 8 9 10 11]\n", - "Key 1: [0 1 2 3 4 5]\n", - "Key 2: [18 19 20 21 22 23]\n", - "Key 3: [12 13 14 15 16 17]\n", - "Key 4: [24 25 26 27 28 29]\n", - "Selected Keys: [0, 1, 2, 3]\n", - "True\n" + "Key 0: [24 7 2 3 28 29]\n", + "Key 1: [12 13 26 9 16 11]\n", + "Key 2: [ 6 1 8 15 10 23]\n", + "Key 3: [ 0 25 20 21 22 5]\n", + "Key 4: [18 19 14 27 4 17]\n", + "User Passcode: [24 7 2 3]\n", + "\n", + "Selected Keys:\n", + " [0, 0, 0, 0]\n", + "\n" ] } ], - "execution_count": 31 + "execution_count": 66 }, { "metadata": {}, @@ -577,8 +574,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:51.315072Z", - "start_time": "2025-03-16T12:51:51.312249Z" + "end_time": "2025-03-17T14:56:55.922451Z", + "start_time": "2025-03-17T14:56:55.919172Z" } }, "cell_type": "code", @@ -598,11 +595,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[61988, 61988, 61988, 61988]\n" + "[15749, 12291, 52872, 49860]\n" ] } ], - "execution_count": 32 + "execution_count": 67 }, { "metadata": {}, @@ -612,33 +609,29 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:51.324331Z", - "start_time": "2025-03-16T12:51:51.321676Z" + "end_time": "2025-03-17T14:56:55.943804Z", + "start_time": "2025-03-17T14:56:55.940721Z" } }, "cell_type": "code", "source": [ + "login_keypad = api.get_login_keypad(username, customer_id)\n", + "selected_keys_login = select_keys_with_passcode_values(user_passcode, login_keypad, keypad_size.props_per_key)\n", "set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets]\n", - "\n", - "presumed_selected_properties_idx = []\n", - "for idx in range(passcode_len):\n", - " key_numb = selected_keys_login[idx]\n", - " set_idx = set_vals_idx[idx]\n", - " selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx)\n", - " presumed_selected_properties_idx.append(selected_prop_idx)\n", - "\n", - "print(user_passcode.tolist() == presumed_selected_properties_idx)" + "presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, set_vals_idx)\n", + "assert user_passcode.tolist() == presumed_selected_properties_idx" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "True\n" + "[24, 7, 2, 3]\n", + "[24, 7, 2, 3]\n" ] } ], - "execution_count": 33 + "execution_count": 68 }, { "metadata": {}, @@ -648,25 +641,17 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:51.571291Z", - "start_time": "2025-03-16T12:51:51.338129Z" + "end_time": "2025-03-17T14:56:56.297835Z", + "start_time": "2025-03-17T14:56:55.992586Z" } }, "cell_type": "code", "source": [ "enciphered_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n", - "print(enciphered_nkode)\n" + "assert enciphered_nkode\n" ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "True\n" - ] - } - ], - "execution_count": 34 + "outputs": [], + "execution_count": 69 }, { "metadata": {}, @@ -682,8 +667,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:52.048827Z", - "start_time": "2025-03-16T12:51:51.579003Z" + "end_time": "2025-03-17T14:56:56.923156Z", + "start_time": "2025-03-17T14:56:56.306667Z" } }, "cell_type": "code", @@ -700,7 +685,7 @@ "login_keypad = api.get_login_keypad(username, customer_id)\n", "selected_keys_login = select_keys_with_passcode_values(user_passcode, login_keypad, keypad_size.props_per_key)\n", "success = api.login(customer_id, username, selected_keys_login)\n", - "print(success)\n", + "assert success\n", "print_user_enciphered_code()" ], "outputs": [ @@ -708,14 +693,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n", - "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n", - "True\n", - "mask: rY763sxEo+aU8G2h7Avf9btV0h8=, code: $2b$12$ZB.x5hSFVjDg4niUAL7TsOtwkTG2uXedMsnFqXNjm0Y0SDf5fgOui\n" + "mask: h7xMhd4kK3faZ6C1Ofi+oVjSI4c=, code: $2b$12$/wwEjbTk90oCnD2Ny6KTWeNJ6YPEyc9yJgY00FGddkQ8cE4LNkSvK\n", + "mask: h7xMhd4kK3faZ6C1Ofi+oVjSI4c=, code: $2b$12$/wwEjbTk90oCnD2Ny6KTWeNJ6YPEyc9yJgY00FGddkQ8cE4LNkSvK\n", + "mask: nzxGInaKD4KfAP9Djc4tz6ROuko=, code: $2b$12$PZ6j.Ccq8/LEWpht4Bnw1.JLXS0dMrgpzBLsnPA33ND7BjsT3CqxW\n" ] } ], - "execution_count": 35 + "execution_count": 70 }, { "metadata": {}, @@ -729,8 +713,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:52.059119Z", - "start_time": "2025-03-16T12:51:52.055816Z" + "end_time": "2025-03-17T14:56:56.935444Z", + "start_time": "2025-03-17T14:56:56.931244Z" } }, "cell_type": "code", @@ -742,7 +726,7 @@ "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 36 + "execution_count": 71 }, { "metadata": {}, @@ -755,8 +739,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:52.068302Z", - "start_time": "2025-03-16T12:51:52.065921Z" + "end_time": "2025-03-17T14:56:56.954373Z", + "start_time": "2025-03-17T14:56:56.952101Z" } }, "cell_type": "code", @@ -769,7 +753,7 @@ " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 37 + "execution_count": 72 }, { "metadata": {}, @@ -779,8 +763,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T12:51:52.311112Z", - "start_time": "2025-03-16T12:51:52.074783Z" + "end_time": "2025-03-17T14:56:57.298001Z", + "start_time": "2025-03-17T14:56:56.988801Z" } }, "cell_type": "code", @@ -794,7 +778,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": 38 + "execution_count": 73 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index cb65199..196d1fe 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,6 +1,5 @@ from dataclasses import dataclass -from uuid import UUID -import numpy as np +from uuid import UUID, uuid4 from src.customer_cipher import CustomerCipher from src.models import NKodePolicy from src.user import User @@ -12,7 +11,18 @@ class Customer: cipher: CustomerCipher users: dict[str, User] - # TODO: validate policy and keypad_list size don't conflict + @classmethod + def create(cls, nkode_policy: NKodePolicy, cipher: CustomerCipher) -> 'Customer': + if nkode_policy.distinct_sets > cipher.keypad_size.numb_of_keys: + raise ValueError("Distinct sets cannot be greater than the number of keys") + if nkode_policy.distinct_properties > cipher.keypad_size.total_props: + raise ValueError("Distinct properties cannot be greater than the total number of properties") + return Customer( + customer_id=uuid4(), + nkode_policy=nkode_policy, + cipher=cipher, + users={} + ) def add_new_user(self, user: User): if user.username in self.users: @@ -30,17 +40,13 @@ class Customer: passcode_set_vals = user.cipher.decipher_mask( user.enciphered_passcode.mask, self.cipher.set_key, passcode_len) set_vals_idx = [self.cipher.get_set_index(set_val) for set_val in passcode_set_vals] - presumed_property_idxs = [] - for idx in range(passcode_len): - key_numb = selected_keys[idx] - set_idx = set_vals_idx[idx] - selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_property_idxs.append(selected_prop_idx) + presumed_property_idxs = user.user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys, set_vals_idx) if not user.cipher.compare_nkode(presumed_property_idxs, self.cipher,user.enciphered_passcode.code): return False if user.renew: user.refresh_passcode(presumed_property_idxs, self.cipher) user.user_keypad.split_shuffle() + #self.users[username] = user return True def renew_keys(self) -> bool: diff --git a/src/models.py b/src/models.py index afef20e..b3143e4 100644 --- a/src/models.py +++ b/src/models.py @@ -12,9 +12,9 @@ class NKodePolicy: min_nkode_len: int = 4 distinct_sets: int = 0 distinct_properties: int = 4 - byte_len: int = 2 # Todo: this should change the total number of bytes an properities or set value can be + byte_len: int = 2 # Todo: this should change the total number of bytes an properties or set value can be lock_out: int = 5 - expiration: int = -1 # in seconds -1 means nkode never expires + expiration: int = -1 # in seconds -1 means nKode never expires @dataclass diff --git a/src/nkode_api.py b/src/nkode_api.py index 318b580..a9f74a9 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -16,10 +16,8 @@ class NKodeAPI: signup_sessions: dict[UUID, UserSignupSession] = field(default_factory=dict) def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: - new_customer = Customer( - customer_id=uuid4(), + new_customer = Customer.create( cipher=CustomerCipher.create(keypad_size), - users={}, nkode_policy=nkode_policy ) self.customers[new_customer.customer_id] = new_customer @@ -97,7 +95,6 @@ class NKodeAPI: if username not in customer.users.keys(): raise ValueError("Username not found") user = customer.users[username] - # user.user_keypad.partial_keypad_shuffle() # TODO: implement split_keypad_shuffle() return user.user_keypad.keypad diff --git a/src/user_cipher.py b/src/user_cipher.py index 1136d22..da49c16 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -105,8 +105,7 @@ class UserCipher: customer_props = customer_cipher.prop_key[passcode_prop_idx] customer_sets = [customer_cipher.get_prop_set_val(prop) for prop in customer_props] padded_customer_sets = self.pad_user_mask(np.array(customer_sets), customer_cipher.set_key) - set_idx = np.array([customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets], - dtype=np.uint16) + set_idx = [customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets] ordered_set_key = self.combined_set_key[set_idx] mask = ordered_set_key ^ padded_customer_sets ^ self.mask_key encoded_mask = self.encode_base64_str(mask) diff --git a/src/user_keypad.py b/src/user_keypad.py index da0d750..65bc21e 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -23,11 +23,11 @@ class UserKeypad: self.random_keypad_shuffle() keypad_matrix = self.keypad_matrix() prop_set_view = keypad_matrix.T - prop_set_view = np.random.permutation(prop_set_view) - prop_set_view = prop_set_view[:self.keypad_size.numb_of_keys] - keypad_matrix = prop_set_view.reshape(-1) + random_sets = np.random.permutation(self.keypad_size.props_per_key)[: self.keypad_size.numb_of_keys] + random_sets.sort() + prop_set_view = prop_set_view[random_sets, :] return UserKeypad( - keypad=keypad_matrix.reshape(-1), + keypad=prop_set_view.T.reshape(-1), keypad_size=KeypadSize( numb_of_keys=self.keypad_size.numb_of_keys, props_per_key=self.keypad_size.numb_of_keys @@ -38,23 +38,20 @@ class UserKeypad: return self.keypad.reshape(-1,self.keypad_size.props_per_key) def random_keypad_shuffle(self): - rng = np.random.default_rng() keypad_view = self.keypad_matrix() - rng.shuffle(keypad_view, axis=0) + np.random.shuffle(keypad_view) set_view = keypad_view.T - set_view = rng.permutation(set_view, axis=1) - keypad_view = set_view.T - self.keypad = keypad_view.reshape(-1) + for prop_set in set_view: + np.random.shuffle(prop_set) + self.keypad = set_view.T.reshape(-1) def disperse_keypad(self): + # TODO: clean this up if not self.keypad_size.is_dispersable: raise ValueError("Keypad size is not dispersable") rng = np.random.default_rng() - #user_keypad_matrix = list_to_matrix(self.keypad, self.keypad_size.props_per_key) user_keypad_matrix = self.keypad_matrix() - #shuffled_keys = secure_fisher_yates_shuffle(user_keypad_matrix) shuffled_keys = rng.permutation(user_keypad_matrix, axis=0) - #prop_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] prop_rotation = rng.permutation(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] dispersed_keypad = random_property_rotation( shuffled_keys, @@ -89,3 +86,13 @@ class UserKeypad: raise ValueError(f"set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_prop_idx = self.keypad_matrix() return int(keypad_prop_idx[key_numb][set_idx]) + + def get_prop_idxs_by_keynumb_setidx(self, key_numb: list[int], set_idx: list[int]) -> list[int]: + if len(key_numb) != len(set_idx): + raise ValueError("key_numb and set_idx must be the same length") + if not all(0 <= kn < self.keypad_size.numb_of_keys for kn in key_numb): + raise ValueError(f"All key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") + if not all(0 <= si < self.keypad_size.props_per_key for si in set_idx): + raise ValueError(f"All set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") + keypad_matrix = self.keypad_matrix() + return keypad_matrix[key_numb, set_idx].reshape(-1).tolist() diff --git a/src/user_signup_session.py b/src/user_signup_session.py index 25cb835..887ec38 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -11,6 +11,7 @@ class UserSignupSession: customer_id: UUID login_keypad: UserKeypad keypad_size: KeypadSize + # TODO: revert back to list[int] set_keypad: Optional[np.ndarray] = None confirm_keypad: Optional[np.ndarray] = None set_key_entry: Optional[np.ndarray] = None diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index d2be679..63332fd 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -3,12 +3,10 @@ import pytest from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize - @pytest.fixture() def nkode_api() -> NKodeAPI: return NKodeAPI() - @pytest.mark.parametrize("keypad_size,passcode_len", [ (KeypadSize(numb_of_keys=10, props_per_key=11), 4), (KeypadSize(numb_of_keys=10, props_per_key=12), 5), -- 2.49.1 From 7b92a6b40b781b46ddfc489288196bea42bcdd5d Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 19 Mar 2025 09:34:02 -0500 Subject: [PATCH 37/85] refactor set_key -> position_key --- docs/render_readme.py | 37 +- notebooks/nkode_tutorial.ipynb | 771 +++++++++++++++++++-------------- src/customer.py | 24 +- src/customer_cipher.py | 51 ++- src/models.py | 2 +- src/nkode_api.py | 2 +- src/user.py | 2 +- src/user_cipher.py | 14 +- src/user_keypad.py | 6 +- test/test_user_cipher_keys.py | 9 +- 10 files changed, 511 insertions(+), 407 deletions(-) diff --git a/docs/render_readme.py b/docs/render_readme.py index b33f7af..1f32057 100644 --- a/docs/render_readme.py +++ b/docs/render_readme.py @@ -64,8 +64,8 @@ if __name__ == "__main__": customer_id = api.create_new_customer(keypad_size, policy) customer = api.customers[customer_id] - set_vals = customer.cipher.set_key - prop_vals = customer.cipher.prop_key + set_vals = customer.cipher.position_key + prop_vals = customer.cipher.property_key customer_prop_view = prop_vals.reshape(-1, keypad_size.props_per_key) prop_keypad_view = prop_vals.reshape(-1, keypad_size.props_per_key) @@ -79,7 +79,7 @@ if __name__ == "__main__": passcode_len = 4 user_passcode = signup_interface[:passcode_len].tolist() selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) - server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] + server_side_prop = [customer.cipher.property_key[idx] for idx in user_passcode] confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) @@ -89,25 +89,24 @@ if __name__ == "__main__": success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) assert success - passcode_server_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] - passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] - + passcode_server_prop = [customer.cipher.property_key[idx] for idx in user_passcode] + passcode_server_set = customer.cipher.get_props_position_vals(user_passcode) user_keys = customer.users[username].cipher - padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.set_key) + padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.position_key) - set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] + set_idx = [customer.cipher.get_position_index(set_val) for set_val in padded_passcode_server_set] mask_set_keys = [user_keys.combined_set_key[idx] for idx in set_idx] ciphered_mask = mask_set_keys ^ padded_passcode_server_set ^ user_keys.mask_key mask = user_keys.encode_base64_str(ciphered_mask) - ciphered_customer_props = customer.cipher.prop_key ^ user_keys.prop_key + ciphered_customer_props = customer.cipher.property_key ^ user_keys.prop_key passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len passcode_ciphered_props.extend([0 for _ in range(pad_len)]) ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key) passcode_bytes = ciphered_code.tobytes() passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) - hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt) + hashed_data = bcrypt.hashpw(passcode_digest, bcrypt.gensalt(rounds=12)) code = hashed_data.decode("utf-8") enciphered_nkode = EncipheredNKode( @@ -129,7 +128,7 @@ if __name__ == "__main__": """ user = customer.users[username] - set_vals = customer.cipher.set_key + set_vals = customer.cipher.position_key user_keys = user.cipher user_mask = user.enciphered_passcode.mask decoded_mask = user_keys.decode_base64_str(user_mask) @@ -144,17 +143,17 @@ if __name__ == "__main__": GET PRESUMED properties """ - set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in login_passcode_sets] + set_vals_idx = [customer.cipher.get_position_index(set_val) for set_val in login_passcode_sets] presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, set_vals_idx) """ RENEW KEYS """ - old_props = customer.cipher.prop_key.copy() - old_sets = customer.cipher.set_key.copy() + old_props = customer.cipher.property_key.copy() + old_sets = customer.cipher.position_key.copy() customer.cipher.renew() - new_props = customer.cipher.prop_key - new_sets = customer.cipher.set_key + new_props = customer.cipher.property_key + new_sets = customer.cipher.position_key customer_new_prop_view = new_props.reshape(-1, keypad_size.props_per_key) """ RENEW USER @@ -171,7 +170,7 @@ if __name__ == "__main__": """ user.cipher = UserCipher.create( customer.cipher.keypad_size, - customer.cipher.set_key, + customer.cipher.position_key, user.cipher.max_nkode_len ) user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) @@ -183,9 +182,9 @@ if __name__ == "__main__": 'customer_set_vals': set_vals, 'customer_prop_view': customer_prop_view, 'set_property_dict': set_property_dict, - 'signup_keypad': signup_keypad, + 'set_signup_keypad': signup_keypad, 'username': 'test_user', - 'user_passcode': user_passcode, + 'user_passcode_indices': user_passcode, 'selected_keys_set': selected_keys_set, 'server_side_prop': server_side_prop, 'confirm_keypad': confirm_keypad, diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index c396758..55495a6 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -8,32 +8,21 @@ "from secrets import choice\n", "from string import ascii_lowercase\n", "import numpy as np\n", - "import bcrypt" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.677924Z", - "start_time": "2025-03-17T14:56:54.674055Z" - } - }, - "outputs": [], - "execution_count": 52 - }, - { - "cell_type": "code", - "source": [ + "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: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]:\n", - " indices = [np.where(keypad == prop)[0][0] for prop in user_passcode]\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", - " print(\"\\nKeypad View\")\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" @@ -41,41 +30,55 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.687467Z", - "start_time": "2025-03-17T14:56:54.683811Z" + "end_time": "2025-03-19T14:18:48.405476Z", + "start_time": "2025-03-19T14:18:48.400091Z" } }, "outputs": [], - "execution_count": 53 + "execution_count": 195 }, { "cell_type": "code", "source": [ - "api = NKodeAPI()" + "api = NKodeAPI()\n", + "user_icons = np.array([\n", + " \"😀\", \"😂\", \"🥳\", \"😍\", \"🤓\",\n", + " \"😎\", \"🥺\", \"😡\", \"😱\", \"🤯\",\n", + " \"🥰\", \"😴\", \"🤔\", \"🙃\", \"😇\",\n", + " \"🤖\", \"👽\", \"👾\", \"🐱\", \"🐶\",\n", + " \"🦁\", \"🐻\", \"🐸\", \"🐙\", \"🦄\",\n", + " \"🌟\", \"⚡\", \"🔥\", \"🍕\", \"🎉\"\n", + "])" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.698258Z", - "start_time": "2025-03-17T14:56:54.696478Z" + "end_time": "2025-03-19T14:18:48.416324Z", + "start_time": "2025-03-19T14:18:48.413345Z" } }, "outputs": [], - "execution_count": 54 + "execution_count": 196 + }, + { + "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": [ - "# NKode API\n", - "### Customer NKode Policy and Interface\n", - "A customer defines their NKode Policy and their interface. Below we've set:\n", - "- max nkode length = 10\n", - "- min nkode length = 4\n", - "- distinct properties = 4\n", - "- distinct set = 0\n", - "- byte len = 2\n", - "\n", - "This customer also has an interface with 5 keys and 6 properties per key. The number of properties must be greater than the number of keys to be dispersion resistant." + "#### 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. Each property belongs to a set.\n", + "2. set key: Combined with the user set Key to the the server-side representation the position in each key.\n" ], "metadata": { "collapsed": false @@ -89,88 +92,19 @@ " min_nkode_len=4,\n", " distinct_sets=0,\n", " distinct_properties=4,\n", - " byte_len=2,\n", ")\n", "keypad_size = KeypadSize(\n", " numb_of_keys = 5,\n", - " props_per_key = 6 # aka number of sets\n", + " props_per_key = 6\n", ")\n", "customer_id = api.create_new_customer(keypad_size, policy)\n", - "customer = api.customers[customer_id]" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.710370Z", - "start_time": "2025-03-17T14:56:54.706283Z" - } - }, - "outputs": [], - "execution_count": 55 - }, - { - "cell_type": "markdown", - "source": [ - "### NKode Customer\n", - "A customer has users and defines the properties and set values for all its users.\n", - "Since our customer has 5 keys and 6 properties per key, this gives a customer interface of 30 distinct properties and 6 distinct properties sets.\n", - "Each properties belongs to one of the 6 sets." - ], - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "markdown", - "source": "#### Customer and Properties Values", - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "source": [ - "print(f\"Customer Set Key: {customer.cipher.set_key}\")\n", + "customer = api.customers[customer_id]\n", + "print(f\"Customer Set Key: {customer.cipher.position_key}\")\n", "print(f\"Customer Properties Key:\")\n", - "customer_prop_keypad = customer.cipher.prop_key.reshape(-1, keypad_size.props_per_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}\")" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.735642Z", - "start_time": "2025-03-17T14:56:54.732921Z" - } - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Customer Set Key: [15749 12291 52872 49860 5208 28289]\n", - "Customer Properties Key:\n", - "[42849 39396 49203 20824 30786 27252]\n", - "[16470 25626 64566 34252 23582 48009]\n", - "[20454 51880 19394 58106 12995 59391]\n", - "[43703 4483 7702 5423 24081 57678]\n", - "[43822 27495 59095 24249 60164 21395]\n" - ] - } - ], - "execution_count": 56 - }, - { - "cell_type": "markdown", - "source": "#### Customer Set To Properties Map", - "metadata": { - "collapsed": false - } - }, - { - "cell_type": "code", - "source": [ - "set_properties_dict = dict(zip(customer.cipher.set_key, customer_prop_keypad.T))\n", + " print(f\"{key_vals}\")\n", + "set_properties_dict = dict(zip(customer.cipher.position_key, customer_prop_keypad.T))\n", "print(f\"Set to Properties Map:\")\n", "for set_val, props in set_properties_dict.items():\n", " print(f\"{set_val}: {props}\")" @@ -178,8 +112,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.781223Z", - "start_time": "2025-03-17T14:56:54.778381Z" + "end_time": "2025-03-19T14:18:48.432441Z", + "start_time": "2025-03-19T14:18:48.426289Z" } }, "outputs": [ @@ -187,24 +121,63 @@ "name": "stdout", "output_type": "stream", "text": [ + "Customer Set Key: [52236 1143 22391 5610 24876 51604]\n", + "Customer Properties Key:\n", + "[48274 26421 59355 34554 61533 13623]\n", + "[22492 47901 17487 10515 61939 8923]\n", + "[32708 61921 973 1449 52341 29868]\n", + "[10212 24136 41690 31747 29169 19891]\n", + "[18093 29532 18702 45116 15485 53514]\n", "Set to Properties Map:\n", - "15749: [42849 16470 20454 43703 43822]\n", - "12291: [39396 25626 51880 4483 27495]\n", - "52872: [49203 64566 19394 7702 59095]\n", - "49860: [20824 34252 58106 5423 24249]\n", - "5208: [30786 23582 12995 24081 60164]\n", - "28289: [27252 48009 59391 57678 21395]\n" + "52236: [48274 22492 32708 10212 18093]\n", + "1143: [26421 47901 61921 24136 29532]\n", + "22391: [59355 17487 973 41690 18702]\n", + "5610: [34554 10515 1449 31747 45116]\n", + "24876: [61533 61939 52341 29169 15485]\n", + "51604: [13623 8923 29868 19891 53514]\n" ] } ], - "execution_count": 57 + "execution_count": 197 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:48.443885Z", + "start_time": "2025-03-19T14:18:48.441137Z" + } + }, + "cell_type": "code", + "source": [ + "user_icon_keypad = user_icons.reshape(-1, keypad_size.props_per_key)\n", + "set_icons_dict = dict(zip(customer.cipher.position_key, user_icon_keypad.T))\n", + "print(\"Set to Icons Map:\")\n", + "for set_val, icons in set_icons_dict.items():\n", + " print(f\"{set_val}: {icons}\")\n" + ], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Set to Icons Map:\n", + "52236: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "1143: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "22391: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "5610: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "24876: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "51604: ['😎' '😴' '👾' '🐙' '🎉']\n" + ] + } + ], + "execution_count": 198 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### User Signup\n", - "To create a new must call this endpoints in order:\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", @@ -215,101 +188,145 @@ " In our case, the server drops 1 properties set 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 \"real\" properties is. All they do is specify an index in the customer keypad\n" + " - the user never learns what their server-side properties" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.814781Z", - "start_time": "2025-03-17T14:56:54.812317Z" + "end_time": "2025-03-19T14:21:05.988739Z", + "start_time": "2025-03-19T14:21:05.981809Z" } }, "cell_type": "code", "source": [ - "session_id, signup_keypad = api.generate_signup_keypad(customer_id)\n", - "print(signup_keypad.reshape(-1, keypad_size.numb_of_keys))" + "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": [ - "[[24 7 2 3 29]\n", - " [12 13 26 9 11]\n", - " [ 6 1 8 15 23]\n", - " [ 0 25 20 21 5]\n", - " [18 19 14 27 17]]\n" + "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: [13 2 27 22 17]\n", + "Key 1: [ 7 8 9 10 11]\n", + "Key 2: [25 20 21 28 5]\n", + "Key 3: [19 26 15 16 29]\n", + "Key 4: [ 1 14 3 4 23]\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "### Customer Properties Keypad" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Key 0: [32049 6729 60466 26997 1309]\n", + "Key 1: [61686 12369 40030 7765 16822]\n", + "Key 2: [29442 13543 37604 7572 18665]\n", + "Key 3: [29124 1259 53208 47841 51215]\n", + "Key 4: [48741 14803 46096 27958 19258]\n" ] } ], - "execution_count": 58 + "execution_count": 229 }, { "metadata": {}, "cell_type": "markdown", "source": [ - "#### Set nKode\n", - "The user identifies properties in the keypad they want in their nkode.\n", - "Each properties in the gui has an index value.\n", - "Below the user has selected 16, 9, 6, 19.\n", - "Graphically represent with anything.\n", - "The only requirement is that the graphical properties must be associated with the same index value everytime the user goes to login.\n", - "If the user wants to change anything about their keypad(the number of keys, properties or graphical properties), they must also change their nkode." + "## Set nKode\n", + "The client receives `user_icons`, `set_signup_keypad`\n" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.845027Z", - "start_time": "2025-03-17T14:56:54.840926Z" + "end_time": "2025-03-19T14:22:48.143268Z", + "start_time": "2025-03-19T14:22:48.140039Z" } }, "cell_type": "code", "source": [ - "keypad_view(signup_keypad, keypad_size.numb_of_keys)\n", "username = random_username()\n", "passcode_len = 4\n", - "user_passcode = signup_keypad[:passcode_len]\n", - "selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_keypad, keypad_size.numb_of_keys)\n", - "print(f\"User Passcode: {user_passcode}\")\n", - "print(f\"Selected Keys\\n{selected_keys_set}\")\n", - "server_side_prop = [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", - "print(f\"User Passcode Server-side properties: {server_side_prop}\")" + "user_passcode_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n", + "selected_keys_set = select_keys_with_passcode_values(user_passcode_indices, set_signup_keypad, keypad_size.numb_of_keys)\n", + "print(f\"User Passcode Indices: {user_passcode_indices}\")\n", + "print(f\"User Passcode Icons: {user_icons[user_passcode_indices]}\")\n", + "print(f\"User Passcode Server-side properties: {customer.cipher.property_key[user_passcode_indices]}\")\n", + "print(f\"Selected Keys: {selected_keys_set}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\n", - "Keypad View\n", - "Key 0: [24 7 2 3 29]\n", - "Key 1: [12 13 26 9 11]\n", - "Key 2: [ 6 1 8 15 23]\n", - "Key 3: [ 0 25 20 21 5]\n", - "Key 4: [18 19 14 27 17]\n", - "User Passcode: [24 7 2 3]\n", - "Selected Keys\n", - "[0, 0, 0, 0]\n", - "User Passcode Server-side properties: [43822, 25626, 49203, 20824]\n" + "User Passcode Indices: [11, 22, 1, 23]\n", + "User Passcode Icons: ['😴' '🐸' '😂' '🐙']\n", + "User Passcode Server-side properties: [16822 26997 48741 19258]\n", + "Selected Keys: [1, 0, 4, 4]\n" ] } ], - "execution_count": 59 + "execution_count": 237 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:54.869735Z", - "start_time": "2025-03-17T14:56:54.866365Z" + "end_time": "2025-03-19T14:18:48.697364Z", + "start_time": "2025-03-19T14:18:48.694055Z" } }, "cell_type": "code", "source": [ - "confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, session_id)\n", + "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(user_passcode, confirm_keypad, keypad_size.numb_of_keys)\n", + "selected_keys_confirm = select_keys_with_passcode_values(user_passcode_indices, confirm_keypad, keypad_size.numb_of_keys)\n", "print(f\"Selected Keys\\n{selected_keys_confirm}\")" ], "outputs": [ @@ -317,102 +334,210 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "Keypad View\n", - "Key 0: [24 13 20 15 17]\n", - "Key 1: [ 6 7 14 21 11]\n", - "Key 2: [ 0 1 26 27 29]\n", - "Key 3: [18 25 2 9 23]\n", - "Key 4: [12 19 8 3 5]\n", + "Key 0: [13 14 9 10 23]\n", + "Key 1: [25 20 21 4 17]\n", + "Key 2: [ 1 8 15 22 5]\n", + "Key 3: [19 26 27 28 11]\n", + "Key 4: [ 7 2 3 16 29]\n", "Selected Keys\n", - "[0, 1, 3, 4]\n" + "[2, 2, 3, 0]\n" ] } ], - "execution_count": 60 + "execution_count": 202 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:55.202903Z", - "start_time": "2025-03-17T14:56:54.894565Z" + "end_time": "2025-03-19T14:18:49.028822Z", + "start_time": "2025-03-19T14:18:48.719642Z" } }, "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, session_id)\n", + "success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n", "assert success" ], "outputs": [], - "execution_count": 61 + "execution_count": 203 }, { "metadata": {}, "cell_type": "markdown", "source": [ - "## Enciphering nKode\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", + "## User Cipher\n", + "\n", + "Users have 4 cipher keys:\n", + "1. prop_key: Half of the user's server-side passcode. the counterpart to the `customer_prop_key`. A user's passcode is made from elements in `user_prop_key XOR customer_prop_key`. Each property belongs to a set.\n", + "2. pass_key: The passcode key is used to encipher user passcode\n", + "3. combined_set_key: The combined set key is `user_set_key XOR customer_set_key`. The user_set_key isn't stored and can't be recovered with the `customer_set_key`\n", + "4. mask_key: The mask key used to encipher user nKode\n", + "\n", "\n" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:55.227304Z", - "start_time": "2025-03-17T14:56:55.222844Z" + "end_time": "2025-03-19T14:18:49.044808Z", + "start_time": "2025-03-19T14:18:49.038903Z" } }, "cell_type": "code", "source": [ "from src.user_cipher import UserCipher\n", - "\n", - "user_set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\n", - "combined_set_key = user_set_key ^ customer.cipher.set_key\n", - "user_keys = UserCipher(\n", - " prop_key = np.array([\n", - " 57200, 8398, 54694, 25997, 30388,\n", - " 46948, 45549, 30364, 49712, 10447,\n", - " 9205, 1777, 10731, 30979, 2795,\n", - " 17068, 56758, 62574, 28641, 11451,\n", - " 26820, 50373, 48783, 25350, 62177,\n", - " 60608, 54242, 4637, 3525, 16313\n", - " ]),\n", - " pass_key=np.array([16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206]),\n", - " mask_key=np.array([29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325]),\n", - " combined_set_key=np.array(combined_set_key),\n", - " max_nkode_len=customer.nkode_policy.max_nkode_len,\n", - ")\n", - "\n", - "ordered_customer_prop_key = customer.cipher.prop_key[user_passcode] # [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", - "ordered_customer_set_key = [int(customer.cipher.get_prop_set_val(prop)) for prop in ordered_customer_prop_key]\n", - "print(f\"Passcode Set Vals: {ordered_customer_set_key}\")\n", - "print(f\"Passcode Prop Vals: {ordered_customer_prop_key}\")" + "user_cipher = UserCipher.create(keypad_size, customer.cipher.position_key, customer.nkode_policy.max_nkode_len)\n", + "user_prop_key_keypad = user_cipher.prop_key.reshape(-1, keypad_size.props_per_key)" + ], + "outputs": [], + "execution_count": 204 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:49.056184Z", + "start_time": "2025-03-19T14:18:49.053919Z" + } + }, + "cell_type": "code", + "source": "print(f\"Property Key:\\n{user_prop_key_keypad}\")", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Property Key:\n", + "[[42472 31697 42349 63196 42777 61068]\n", + " [ 7243 387 55065 19589 60418 22963]\n", + " [26541 59081 11622 22333 35608 42306]\n", + " [58621 57412 35828 19293 16394 53334]\n", + " [ 5908 43761 45282 44085 8881 21753]]\n" + ] + } + ], + "execution_count": 205 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:49.080744Z", + "start_time": "2025-03-19T14:18:49.078469Z" + } + }, + "cell_type": "code", + "source": "print(f\"Passcode Key: {user_cipher.pass_key}\")", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Passcode Key: [15958 16933 12810 19682 61534 54403 52645 54893 63261 22611]\n" + ] + } + ], + "execution_count": 206 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:49.125439Z", + "start_time": "2025-03-19T14:18:49.123Z" + } + }, + "cell_type": "code", + "source": "print(f\"Mask Key: {user_cipher.mask_key}\")", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mask Key: [11511 36348 3693 57612 7883 59516 54039 57361 15218 43846]\n" + ] + } + ], + "execution_count": 207 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:49.154768Z", + "start_time": "2025-03-19T14:18:49.152444Z" + } + }, + "cell_type": "code", + "source": "print(f\"Combined Set Key: {user_cipher.combined_set_key}\")", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Combined Set Key: [16302 28740 39642 59999 35588 576]\n" + ] + } + ], + "execution_count": 208 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:49.188943Z", + "start_time": "2025-03-19T14:18:49.186648Z" + } + }, + "cell_type": "code", + "source": "print(f\"User Set Key = combined_set_key XOR customer_set_key: {user_cipher.combined_set_key ^ customer.cipher.position_key}\")", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "User Set Key = combined_set_key XOR customer_set_key: [62370 29747 52653 65461 59944 52180]\n" + ] + } + ], + "execution_count": 209 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-19T14:18:49.217942Z", + "start_time": "2025-03-19T14:18:49.215165Z" + } + }, + "cell_type": "code", + "source": [ + "set_properties_dict = dict(zip(user_cipher.combined_set_key, user_prop_key_keypad.T))\n", + "print(f\"Combined Set to Properties Map:\")\n", + "for set_val, props in set_properties_dict.items():\n", + " print(f\"{set_val}: {props}\")" ], "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [15749, 12291, 52872, 49860]\n", - "Passcode Prop Vals: [43822 25626 49203 20824]\n" + "Combined Set to Properties Map:\n", + "16302: [42472 7243 26541 58621 5908]\n", + "28740: [31697 387 59081 57412 43761]\n", + "39642: [42349 55065 11622 35828 45282]\n", + "59999: [63196 19589 22333 19293 44085]\n", + "35588: [42777 60418 35608 16394 8881]\n", + "576: [61068 22963 42306 53334 21753]\n" ] } ], - "execution_count": 62 + "execution_count": 210 }, { "metadata": {}, "cell_type": "markdown", "source": [ "#### Encipher Mask\n", - "Recall:\n", - "1. combined_set_key = (user_set_key ^ customer_set_key)\n", + "1. Order customer properties by user passcode.\n", + "2. Get the set indices (`set_indices`) of the ordered customer properties.\n", + "3. Pad the\n", + "1. combined_set_key = user_set_key ^ customer_set_key\n", "2. padded_ordered_customer_set = customer_set_key # ordered by user passcode and padded with extra set key values to be equal to max_nkode_len\n", "3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n", "where i is the index\n", @@ -425,20 +550,21 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:55.260751Z", - "start_time": "2025-03-17T14:56:55.258152Z" + "end_time": "2025-03-19T14:18:49.244375Z", + "start_time": "2025-03-19T14:18:49.241861Z" } }, "cell_type": "code", "source": [ - "padded_ordered_customer_set = user_keys.pad_user_mask(ordered_customer_set_key, customer.cipher.set_key)\n", - "set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_ordered_customer_set]\n", - "ordered_set_key = user_keys.combined_set_key[set_idx]\n", - "mask = ordered_set_key ^ padded_ordered_customer_set ^ user_keys.mask_key\n", - "encoded_mask = user_keys.encode_base64_str(mask)" + "set_indices = customer.cipher.get_passcode_position_indices_padded(list(user_passcode_indices), customer.nkode_policy.max_nkode_len)\n", + "ordered_combined_set_key = user_cipher.combined_set_key[set_indices]\n", + "ordered_customer_set_key = customer.cipher.position_key[set_indices]\n", + "ordered_user_set_key = ordered_customer_set_key ^ ordered_combined_set_key\n", + "mask = ordered_user_set_key ^ user_cipher.mask_key\n", + "encoded_mask = user_cipher.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 63 + "execution_count": 211 }, { "metadata": {}, @@ -455,33 +581,44 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:55.590495Z", - "start_time": "2025-03-17T14:56:55.285097Z" + "end_time": "2025-03-19T14:18:49.575573Z", + "start_time": "2025-03-19T14:18:49.268784Z" } }, "cell_type": "code", "source": [ - "ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", - "passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode]\n", + "combined_prop_key = customer.cipher.property_key ^ user_cipher.prop_key\n", + "user_passcode = combined_prop_key[user_passcode_indices]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", - "\n", - "passcode_ciphered_props.extend([0 for _ in range(pad_len)])\n", - "\n", - "ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key)\n", - "\n", - "passcode_bytes = ciphered_code.tobytes()\n", - "passcode_digest = user_keys.prehash_passcode(user_passcode, customer.cipher)# base64.b64encode(hashlib.sha256(passcode_bytes).digest())\n", - "hashed_data = bcrypt.hashpw(passcode_digest, bcrypt.gensalt(rounds=12))\n", - "code = hashed_data.decode(\"utf-8\")" + "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": 64 + "execution_count": 212 + }, + { + "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-17T14:56:55.595860Z", - "start_time": "2025-03-17T14:56:55.593975Z" + "end_time": "2025-03-19T14:18:49.585161Z", + "start_time": "2025-03-19T14:18:49.583132Z" } }, "cell_type": "code", @@ -490,11 +627,11 @@ "\n", "enciphered_nkode = EncipheredNKode(\n", " mask=encoded_mask,\n", - " code=code,\n", + " code=passcode_hash,\n", ")" ], "outputs": [], - "execution_count": 65 + "execution_count": 213 }, { "metadata": {}, @@ -508,16 +645,16 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:55.910640Z", - "start_time": "2025-03-17T14:56:55.605472Z" + "end_time": "2025-03-19T14:18:49.901534Z", + "start_time": "2025-03-19T14:18:49.594710Z" } }, "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(user_passcode, login_keypad, keypad_size.props_per_key)\n", - "print(f\"User Passcode: {user_passcode}\\n\")\n", + "selected_keys_login = select_keys_with_passcode_values(user_passcode_indices, login_keypad, keypad_size.props_per_key)\n", + "print(f\"User Passcode: {user_passcode_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" @@ -527,22 +664,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "\n", - "Keypad View\n", - "Key 0: [24 7 2 3 28 29]\n", - "Key 1: [12 13 26 9 16 11]\n", - "Key 2: [ 6 1 8 15 10 23]\n", - "Key 3: [ 0 25 20 21 22 5]\n", - "Key 4: [18 19 14 27 4 17]\n", - "User Passcode: [24 7 2 3]\n", + "Key 0: [ 6 19 8 3 4 23]\n", + "Key 1: [18 13 2 21 28 5]\n", + "Key 2: [12 25 14 15 16 11]\n", + "Key 3: [24 7 26 9 22 17]\n", + "Key 4: [ 0 1 20 27 10 29]\n", + "User Passcode: [15, 1, 19, 23]\n", "\n", "Selected Keys:\n", - " [0, 0, 0, 0]\n", + " [2, 4, 0, 0]\n", "\n" ] } ], - "execution_count": 66 + "execution_count": 214 }, { "metadata": {}, @@ -560,78 +695,37 @@ "source": [ "### Decipher Mask\n", "Recall:\n", - "- set_key = (set_key_rand_numb ^ set_val)\n", - "- mask = mask_key_rand_num ^ set_key_rand_numb\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", - "- deciphered_mask = mask ^ mask_key\n", - "- deciphered_mask = set_key_rand_numb # mask_key_rand_num is cancelled out\n", - "- set_key_rand_component = set_key ^ set_values\n", - "- deduce the set value" + "- 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-17T14:56:55.922451Z", - "start_time": "2025-03-17T14:56:55.919172Z" - } - }, - "cell_type": "code", - "source": [ - "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.set_key ^ user.cipher.combined_set_key\n", - "passcode_sets = []\n", - "for set_cipher in deciphered_mask[:passcode_len]:\n", - " set_idx = np.where(set_key == set_cipher)[0][0]\n", - " passcode_sets.append(int(customer.cipher.set_key[set_idx]))\n", - "print(passcode_sets)" - ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[15749, 12291, 52872, 49860]\n" - ] - } - ], - "execution_count": 67 - }, - { - "metadata": {}, - "cell_type": "markdown", - "source": "### Get Presumed Properties\n" - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2025-03-17T14:56:55.943804Z", - "start_time": "2025-03-17T14:56:55.940721Z" + "end_time": "2025-03-19T14:18:49.914512Z", + "start_time": "2025-03-19T14:18:49.911338Z" } }, "cell_type": "code", "source": [ "login_keypad = api.get_login_keypad(username, customer_id)\n", - "selected_keys_login = select_keys_with_passcode_values(user_passcode, login_keypad, keypad_size.props_per_key)\n", - "set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets]\n", - "presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, set_vals_idx)\n", - "assert user_passcode.tolist() == presumed_selected_properties_idx" + "selected_keys_login = select_keys_with_passcode_values(user_passcode_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_set_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 user_passcode_indices == presumed_selected_properties_idx\n" ], - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[24, 7, 2, 3]\n", - "[24, 7, 2, 3]\n" - ] - } - ], - "execution_count": 68 + "outputs": [], + "execution_count": 215 }, { "metadata": {}, @@ -641,17 +735,17 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:56.297835Z", - "start_time": "2025-03-17T14:56:55.992586Z" + "end_time": "2025-03-19T14:18:50.236818Z", + "start_time": "2025-03-19T14:18:49.932427Z" } }, "cell_type": "code", "source": [ - "enciphered_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n", - "assert enciphered_nkode\n" + "valid_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n", + "assert valid_nkode\n" ], "outputs": [], - "execution_count": 69 + "execution_count": 216 }, { "metadata": {}, @@ -667,8 +761,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:56.923156Z", - "start_time": "2025-03-17T14:56:56.306667Z" + "end_time": "2025-03-19T14:18:50.851576Z", + "start_time": "2025-03-19T14:18:50.240709Z" } }, "cell_type": "code", @@ -676,14 +770,14 @@ "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", + " 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(user_passcode, login_keypad, keypad_size.props_per_key)\n", + "selected_keys_login = select_keys_with_passcode_values(user_passcode_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()" @@ -693,13 +787,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: h7xMhd4kK3faZ6C1Ofi+oVjSI4c=, code: $2b$12$/wwEjbTk90oCnD2Ny6KTWeNJ6YPEyc9yJgY00FGddkQ8cE4LNkSvK\n", - "mask: h7xMhd4kK3faZ6C1Ofi+oVjSI4c=, code: $2b$12$/wwEjbTk90oCnD2Ny6KTWeNJ6YPEyc9yJgY00FGddkQ8cE4LNkSvK\n", - "mask: nzxGInaKD4KfAP9Djc4tz6ROuko=, code: $2b$12$PZ6j.Ccq8/LEWpht4Bnw1.JLXS0dMrgpzBLsnPA33ND7BjsT3CqxW\n" + "mask: tBqaWnmsGlsclLRq7qdFeuZ/Krg=, code: $2b$12$iBdtB4bRMx9VnWKwAInkwecUx5wwiL1C0DVWO.sk9EG1syVVe2CaS\n", + "\n", + "mask: tBqaWnmsGlsclLRq7qdFeuZ/Krg=, code: $2b$12$iBdtB4bRMx9VnWKwAInkwecUx5wwiL1C0DVWO.sk9EG1syVVe2CaS\n", + "\n", + "mask: DpuEwDyPrOF4pAxxozyqUHigaak=, code: $2b$12$ngd6PgpZo/UhIANnrRimlOliRIGwAaS6zbe5gqTzYHPBaeAa3vJsy\n", + "\n" ] } ], - "execution_count": 70 + "execution_count": 217 }, { "metadata": {}, @@ -713,20 +810,20 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:56.935444Z", - "start_time": "2025-03-17T14:56:56.931244Z" + "end_time": "2025-03-19T14:18:50.865243Z", + "start_time": "2025-03-19T14:18:50.861260Z" } }, "cell_type": "code", "source": [ - "old_props = customer.cipher.prop_key.copy()\n", - "old_sets = customer.cipher.set_key.copy()\n", + "old_props = customer.cipher.property_key.copy()\n", + "old_sets = customer.cipher.position_key.copy()\n", "customer.cipher.renew()\n", - "new_props = customer.cipher.prop_key\n", - "new_sets = customer.cipher.set_key" + "new_props = customer.cipher.property_key\n", + "new_sets = customer.cipher.position_key" ], "outputs": [], - "execution_count": 71 + "execution_count": 218 }, { "metadata": {}, @@ -739,8 +836,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:56.954373Z", - "start_time": "2025-03-17T14:56:56.952101Z" + "end_time": "2025-03-19T14:18:50.876670Z", + "start_time": "2025-03-19T14:18:50.874092Z" } }, "cell_type": "code", @@ -753,7 +850,7 @@ " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 72 + "execution_count": 219 }, { "metadata": {}, @@ -763,22 +860,22 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-17T14:56:57.298001Z", - "start_time": "2025-03-17T14:56:56.988801Z" + "end_time": "2025-03-19T14:18:51.191895Z", + "start_time": "2025-03-19T14:18:50.885862Z" } }, "cell_type": "code", "source": [ "user.cipher = UserCipher.create(\n", " customer.cipher.keypad_size,\n", - " customer.cipher.set_key,\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": 73 + "execution_count": 220 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 196d1fe..fd8b5cb 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,5 +1,8 @@ from dataclasses import dataclass from uuid import UUID, uuid4 + +import numpy as np + from src.customer_cipher import CustomerCipher from src.models import NKodePolicy from src.user import User @@ -38,8 +41,8 @@ class Customer: passcode_len = len(selected_keys) user = self.users[username] passcode_set_vals = user.cipher.decipher_mask( - user.enciphered_passcode.mask, self.cipher.set_key, passcode_len) - set_vals_idx = [self.cipher.get_set_index(set_val) for set_val in passcode_set_vals] + user.enciphered_passcode.mask, self.cipher.position_key, passcode_len) + set_vals_idx = [self.cipher.get_position_index(set_val) for set_val in passcode_set_vals] presumed_property_idxs = user.user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys, set_vals_idx) if not user.cipher.compare_nkode(presumed_property_idxs, self.cipher,user.enciphered_passcode.code): return False @@ -50,11 +53,11 @@ class Customer: return True def renew_keys(self) -> bool: - old_props = self.cipher.prop_key.copy() - old_sets = self.cipher.set_key.copy() + old_props = self.cipher.property_key.copy() + old_sets = self.cipher.position_key.copy() self.cipher.renew() - new_props = self.cipher.prop_key - new_sets = self.cipher.set_key + new_props = self.cipher.property_key + new_sets = self.cipher.position_key props_xor = new_props ^ old_props set_xor = new_sets ^ old_sets @@ -65,9 +68,10 @@ class Customer: def valid_new_nkode(self, passcode_prop_idx: list[int]) -> bool: nkode_len = len(passcode_prop_idx) - passcode_set_values = [ - self.cipher.get_prop_set_val(int(self.cipher.prop_key[prop_idx])) for prop_idx in passcode_prop_idx - ] + #passcode_set_values = [ + # self.cipher.get_prop_set_val(int(self.cipher.property_key[prop_idx])) for prop_idx in passcode_prop_idx + #] + passcode_set_values = self.cipher.get_props_position_vals(passcode_prop_idx) distinct_sets = len(set(passcode_set_values)) distinct_properties = len(set(passcode_prop_idx)) if ( @@ -77,3 +81,5 @@ class Customer: ): return True return False + + diff --git a/src/customer_cipher.py b/src/customer_cipher.py index 72ffcac..953442e 100644 --- a/src/customer_cipher.py +++ b/src/customer_cipher.py @@ -6,16 +6,13 @@ from src.models import KeypadSize @dataclass class CustomerCipher: - prop_key: np.ndarray - set_key: np.ndarray + property_key: np.ndarray + position_key: np.ndarray keypad_size: KeypadSize MAX_KEYS: ClassVar[int] = 256 MAX_PROP_PER_KEY: ClassVar[int] = 256 def __post_init__(self): - self.check_keys_vs_props() - - def check_keys_vs_props(self) -> None: if self.keypad_size.is_dispersable: raise ValueError("number of keys must be less than the number of " "properties per key to be dispersion resistant") @@ -24,28 +21,40 @@ class CustomerCipher: def create(cls, keypad_size: KeypadSize) -> 'CustomerCipher': if keypad_size.numb_of_keys > cls.MAX_KEYS or keypad_size.props_per_key > cls.MAX_PROP_PER_KEY: raise ValueError(f"Keys and properties per key must not exceed {cls.MAX_KEYS}") - # Using numpy to generate non-repeating random integers prop_key = np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False) - set_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False) - + pos_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False) return cls( - prop_key=prop_key, - set_key=set_key, + property_key=prop_key, + position_key=pos_key, keypad_size=keypad_size, ) def renew(self): - self.prop_key = np.random.choice(2 ** 16, size=self.keypad_size.total_props, replace=False) - self.set_key = np.random.choice(2 ** 16, size=self.keypad_size.props_per_key, replace=False) + self.property_key = np.random.choice(2 ** 16, size=self.keypad_size.total_props, replace=False) + self.position_key = np.random.choice(2 ** 16, size=self.keypad_size.props_per_key, replace=False) - def get_prop_set_val(self, prop: int) -> int: - assert np.isin(prop, self.prop_key) - prop_idx = np.where(self.prop_key == prop)[0][0] - set_idx = prop_idx % self.keypad_size.props_per_key - return int(self.set_key[set_idx]) + def get_props_position_vals(self, props: np.ndarray | list[int]) -> np.ndarray: + if not all([prop in self.property_key for prop in props]): + raise ValueError("Property values must be within valid range") + pos_vals = [self._get_prop_position_val(prop) for prop in props] + return np.array(pos_vals) - def get_set_index(self, set_val: int) -> int: - if not np.isin(set_val, self.set_key): - raise ValueError(f"Set value {set_val} not found in set values") - return int(np.where(self.set_key == set_val)[0][0]) \ No newline at end of file + def _get_prop_position_val(self, prop: int) -> int: + assert prop in self.property_key + prop_idx = np.where(self.property_key == prop)[0][0] + pos_idx = prop_idx % self.keypad_size.props_per_key + return int(self.position_key[pos_idx]) + + def get_position_index(self, pos_val: int) -> int: + if not np.isin(pos_val, self.position_key): + raise ValueError(f"Position value {pos_val} not found in customer cipher position_key") + return int(np.where(self.position_key == pos_val)[0][0]) + + def get_passcode_position_indices_padded(self, passcode_indices: list[int], max_nkode_len: int) -> list[int]: + if not all(0 <= idx < self.keypad_size.total_props for idx in passcode_indices): + raise ValueError("invalid passcode index") + pos_indices = [idx % self.keypad_size.props_per_key for idx in passcode_indices] + pad_len = max_nkode_len - len(passcode_indices) + pad = np.random.choice(self.keypad_size.props_per_key, pad_len, replace=True) + return pos_indices + pad.tolist() \ No newline at end of file diff --git a/src/models.py b/src/models.py index b3143e4..121d29c 100644 --- a/src/models.py +++ b/src/models.py @@ -28,4 +28,4 @@ class KeypadSize: @property def is_dispersable(self) -> bool: - return self.props_per_key <= self.numb_of_keys \ No newline at end of file + return self.props_per_key <= self.numb_of_keys diff --git a/src/nkode_api.py b/src/nkode_api.py index a9f74a9..743ce3b 100644 --- a/src/nkode_api.py +++ b/src/nkode_api.py @@ -74,7 +74,7 @@ class NKodeAPI: passcode = self.signup_sessions[session_id].deduce_passcode(confirm_key_entry) new_user_keys = UserCipher.create( customer.cipher.keypad_size, - customer.cipher.set_key, + customer.cipher.position_key, customer.nkode_policy.max_nkode_len ) enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.cipher) diff --git a/src/user.py b/src/user.py index 1ee091e..0700c0a 100644 --- a/src/user.py +++ b/src/user.py @@ -24,7 +24,7 @@ class User: def refresh_passcode(self, passcode_prop_idxs: list[int], customer_cipher: CustomerCipher): self.cipher = UserCipher.create( customer_cipher.keypad_size, - customer_cipher.set_key, + customer_cipher.position_key, self.cipher.max_nkode_len ) self.enciphered_passcode = self.cipher.encipher_nkode(passcode_prop_idxs, customer_cipher) diff --git a/src/user_cipher.py b/src/user_cipher.py index da49c16..e5bd3de 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -19,9 +19,7 @@ class UserCipher: def create(cls, keypad_size: KeypadSize, customer_set_key: np.ndarray, max_nkode_len: int) -> 'UserCipher': if len(customer_set_key) != keypad_size.props_per_key: raise ValueError("Invalid set values") - user_set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) - return UserCipher( prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False), @@ -93,7 +91,7 @@ class UserCipher: passcode_cipher[:passcode_len] = ( passcode_cipher[:passcode_len] ^ self.prop_key[passcode_prop_idx] ^ - customer_prop.prop_key[passcode_prop_idx] + customer_prop.property_key[passcode_prop_idx] ) return passcode_cipher.astype(np.uint16).tobytes() @@ -102,12 +100,10 @@ class UserCipher: passcode_prop_idx: list[int], customer_cipher: CustomerCipher ) -> str: - customer_props = customer_cipher.prop_key[passcode_prop_idx] - customer_sets = [customer_cipher.get_prop_set_val(prop) for prop in customer_props] - padded_customer_sets = self.pad_user_mask(np.array(customer_sets), customer_cipher.set_key) - set_idx = [customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets] - ordered_set_key = self.combined_set_key[set_idx] - mask = ordered_set_key ^ padded_customer_sets ^ self.mask_key + set_idxs = customer_cipher.get_passcode_position_indices_padded(passcode_prop_idx, len(self.mask_key)) + ordered_set_key = self.combined_set_key[set_idxs] + ordered_customer_key = customer_cipher.position_key[set_idxs] + mask = ordered_set_key ^ ordered_customer_key ^ self.mask_key encoded_mask = self.encode_base64_str(mask) return encoded_mask diff --git a/src/user_keypad.py b/src/user_keypad.py index 65bc21e..399c092 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -83,16 +83,16 @@ class UserKeypad: if not (0 <= key_numb < self.keypad_size.numb_of_keys): raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not (0 <= set_idx < self.keypad_size.props_per_key): - raise ValueError(f"set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") + raise ValueError(f"set_indices must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_prop_idx = self.keypad_matrix() return int(keypad_prop_idx[key_numb][set_idx]) def get_prop_idxs_by_keynumb_setidx(self, key_numb: list[int], set_idx: list[int]) -> list[int]: if len(key_numb) != len(set_idx): - raise ValueError("key_numb and set_idx must be the same length") + raise ValueError("key_numb and set_indices must be the same length") if not all(0 <= kn < self.keypad_size.numb_of_keys for kn in key_numb): raise ValueError(f"All key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not all(0 <= si < self.keypad_size.props_per_key for si in set_idx): - raise ValueError(f"All set_idx must be between 0 and {self.keypad_size.props_per_key - 1}") + raise ValueError(f"All set_indices must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_matrix = self.keypad_matrix() return keypad_matrix[key_numb, set_idx].reshape(-1).tolist() diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 80e6a0b..26845d5 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -11,7 +11,6 @@ from src.user_cipher import UserCipher, CustomerCipher ] ) def test_encode_decode_base64(passcode_len): - #data = generate_random_nonrepeating_list(passcode_len) data = np.random.choice(2**16, passcode_len, replace=False) encoded = UserCipher.encode_base64_str(data) decoded = UserCipher.decode_base64_str(encoded) @@ -28,14 +27,12 @@ def test_encode_decode_base64(passcode_len): ]) def test_decode_mask(keypad_size, max_nkode_len): customer = CustomerCipher.create(keypad_size) - #passcode_entry = generate_random_nonrepeating_list(keypad_size.numb_of_props,max_val=keypad_size.numb_of_props)[:4] passcode_entry = np.random.choice(keypad_size.total_props, 4, replace=False) - passcode_values = [customer.prop_key[idx] for idx in passcode_entry] - set_vals = customer.set_key + passcode_values = [customer.property_key[idx] for idx in passcode_entry] + set_vals = customer.position_key user_keys = UserCipher.create(keypad_size, set_vals, max_nkode_len) passcode = user_keys.encipher_nkode(passcode_entry, customer) - - orig_passcode_set_vals = [customer.get_prop_set_val(prop) for prop in passcode_values] + orig_passcode_set_vals = customer.get_props_position_vals(passcode_values) passcode_set_vals = user_keys.decipher_mask(passcode.mask, set_vals, len(passcode_entry)) assert (len(passcode_set_vals) == len(orig_passcode_set_vals)) assert (all(orig_passcode_set_vals[idx] == passcode_set_vals[idx] for idx in range(len(passcode_set_vals)))) -- 2.49.1 From 65f3559ef006d73d3c99ff2ab6740ad6f0fe5726 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 20 Mar 2025 04:01:15 -0500 Subject: [PATCH 38/85] refactor set_key -> position_key --- docs/render_readme.py | 16 +- notebooks/nkode_tutorial.ipynb | 395 ++++++++++++++++----------------- src/customer.py | 4 +- src/models.py | 2 +- src/user.py | 4 +- src/user_cipher.py | 49 ++-- src/user_keypad.py | 6 +- test/test_user_cipher_keys.py | 12 +- 8 files changed, 241 insertions(+), 247 deletions(-) diff --git a/docs/render_readme.py b/docs/render_readme.py index 1f32057..92238e0 100644 --- a/docs/render_readme.py +++ b/docs/render_readme.py @@ -53,7 +53,7 @@ if __name__ == "__main__": policy = NKodePolicy( max_nkode_len=10, min_nkode_len=4, - distinct_sets=0, + distinct_positions=0, distinct_properties=4, byte_len=2, ) @@ -92,14 +92,14 @@ if __name__ == "__main__": passcode_server_prop = [customer.cipher.property_key[idx] for idx in user_passcode] passcode_server_set = customer.cipher.get_props_position_vals(user_passcode) user_keys = customer.users[username].cipher - + # TODO: pad_user_mask is deprecated padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.position_key) set_idx = [customer.cipher.get_position_index(set_val) for set_val in padded_passcode_server_set] - mask_set_keys = [user_keys.combined_set_key[idx] for idx in set_idx] + mask_set_keys = [user_keys.combined_position_key[idx] for idx in set_idx] ciphered_mask = mask_set_keys ^ padded_passcode_server_set ^ user_keys.mask_key mask = user_keys.encode_base64_str(ciphered_mask) - ciphered_customer_props = customer.cipher.property_key ^ user_keys.prop_key + ciphered_customer_props = customer.cipher.property_key ^ user_keys.property_key passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len passcode_ciphered_props.extend([0 for _ in range(pad_len)]) @@ -133,7 +133,7 @@ if __name__ == "__main__": user_mask = user.enciphered_passcode.mask decoded_mask = user_keys.decode_base64_str(user_mask) deciphered_mask = np.bitwise_xor(decoded_mask, user_keys.mask_key) - set_key_rand_component = np.bitwise_xor(set_vals, user_keys.combined_set_key) + set_key_rand_component = np.bitwise_xor(set_vals, user_keys.combined_position_key) login_passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: set_idx = np.where(set_key_rand_component == set_cipher)[0][0] @@ -162,8 +162,8 @@ if __name__ == "__main__": sets_xor = np.bitwise_xor(new_sets, old_sets) for user in customer.users.values(): user.renew = True - user.cipher.combined_set_key = np.bitwise_xor(user.cipher.combined_set_key, sets_xor) - user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor) + user.cipher.combined_position_key = np.bitwise_xor(user.cipher.combined_position_key, sets_xor) + user.cipher.property_key = np.bitwise_xor(user.cipher.property_key, props_xor) """ REFRESH USER KEYS @@ -184,7 +184,7 @@ if __name__ == "__main__": 'set_property_dict': set_property_dict, 'set_signup_keypad': signup_keypad, 'username': 'test_user', - 'user_passcode_indices': user_passcode, + 'passcode_property_indices': user_passcode, 'selected_keys_set': selected_keys_set, 'server_side_prop': server_side_prop, 'confirm_keypad': confirm_keypad, diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 55495a6..64d4fdc 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -30,12 +30,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-19T14:18:48.405476Z", - "start_time": "2025-03-19T14:18:48.400091Z" + "end_time": "2025-03-20T08:32:08.092320Z", + "start_time": "2025-03-20T08:32:08.087658Z" } }, "outputs": [], - "execution_count": 195 + "execution_count": 27 }, { "cell_type": "code", @@ -53,12 +53,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-19T14:18:48.416324Z", - "start_time": "2025-03-19T14:18:48.413345Z" + "end_time": "2025-03-20T08:32:08.113043Z", + "start_time": "2025-03-20T08:32:08.110624Z" } }, "outputs": [], - "execution_count": 196 + "execution_count": 28 }, { "metadata": {}, @@ -77,8 +77,8 @@ "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. Each property belongs to a set.\n", - "2. set key: Combined with the user set Key to the the server-side representation the position in each key.\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 @@ -90,7 +90,7 @@ "policy = NKodePolicy(\n", " max_nkode_len=10,\n", " min_nkode_len=4,\n", - " distinct_sets=0,\n", + " distinct_positions=0,\n", " distinct_properties=4,\n", ")\n", "keypad_size = KeypadSize(\n", @@ -99,21 +99,21 @@ ")\n", "customer_id = api.create_new_customer(keypad_size, policy)\n", "customer = api.customers[customer_id]\n", - "print(f\"Customer Set Key: {customer.cipher.position_key}\")\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", - "set_properties_dict = dict(zip(customer.cipher.position_key, customer_prop_keypad.T))\n", - "print(f\"Set to Properties Map:\")\n", - "for set_val, props in set_properties_dict.items():\n", - " print(f\"{set_val}: {props}\")" + "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-19T14:18:48.432441Z", - "start_time": "2025-03-19T14:18:48.426289Z" + "end_time": "2025-03-20T08:32:08.198233Z", + "start_time": "2025-03-20T08:32:08.193182Z" } }, "outputs": [ @@ -121,56 +121,56 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Set Key: [52236 1143 22391 5610 24876 51604]\n", + "Customer Position Key: [21284 3999 4057 48308 14680 46323]\n", "Customer Properties Key:\n", - "[48274 26421 59355 34554 61533 13623]\n", - "[22492 47901 17487 10515 61939 8923]\n", - "[32708 61921 973 1449 52341 29868]\n", - "[10212 24136 41690 31747 29169 19891]\n", - "[18093 29532 18702 45116 15485 53514]\n", - "Set to Properties Map:\n", - "52236: [48274 22492 32708 10212 18093]\n", - "1143: [26421 47901 61921 24136 29532]\n", - "22391: [59355 17487 973 41690 18702]\n", - "5610: [34554 10515 1449 31747 45116]\n", - "24876: [61533 61939 52341 29169 15485]\n", - "51604: [13623 8923 29868 19891 53514]\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": 197 + "execution_count": 29 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:48.443885Z", - "start_time": "2025-03-19T14:18:48.441137Z" + "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", - "set_icons_dict = dict(zip(customer.cipher.position_key, user_icon_keypad.T))\n", - "print(\"Set to Icons Map:\")\n", - "for set_val, icons in set_icons_dict.items():\n", - " print(f\"{set_val}: {icons}\")\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": [ - "Set to Icons Map:\n", - "52236: ['😀' '🥺' '🤔' '🐱' '🦄']\n", - "1143: ['😂' '😡' '🙃' '🐶' '🌟']\n", - "22391: ['🥳' '😱' '😇' '🦁' '⚡']\n", - "5610: ['😍' '🤯' '🤖' '🐻' '🔥']\n", - "24876: ['🤓' '🥰' '👽' '🐸' '🍕']\n", - "51604: ['😎' '😴' '👾' '🐙' '🎉']\n" + "Position Value to Icons Map:\n", + "21284: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "3999: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "4057: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "48308: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "14680: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "46323: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], - "execution_count": 198 + "execution_count": 30 }, { "metadata": {}, @@ -184,8 +184,8 @@ "\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 properties sets to the number of properties is equal to the number of keys.\n", - " In our case, the server drops 1 properties set to give us a 5 X 5 keypad with possible index values ranging from 0-29.\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" @@ -194,8 +194,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:21:05.988739Z", - "start_time": "2025-03-19T14:21:05.981809Z" + "end_time": "2025-03-20T08:32:08.256237Z", + "start_time": "2025-03-20T08:32:08.250147Z" } }, "cell_type": "code", @@ -223,11 +223,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: ['🙃' '🥳' '🔥' '🐸' '👾']\n", - "Key 1: ['😡' '😱' '🤯' '🥰' '😴']\n", - "Key 2: ['🌟' '🦁' '🐻' '🍕' '😎']\n", - "Key 3: ['🐶' '⚡' '🤖' '👽' '🎉']\n", - "Key 4: ['😂' '😇' '😍' '🤓' '🐙']\n" + "Key 0: ['🦄' '😂' '😇' '👽' '😴']\n", + "Key 1: ['🐱' '🌟' '😱' '🐸' '😎']\n", + "Key 2: ['🤔' '🙃' '🦁' '🥰' '🐙']\n", + "Key 3: ['😀' '😡' '⚡' '🍕' '👾']\n", + "Key 4: ['🥺' '🐶' '🥳' '🤓' '🎉']\n" ] }, { @@ -244,11 +244,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [13 2 27 22 17]\n", - "Key 1: [ 7 8 9 10 11]\n", - "Key 2: [25 20 21 28 5]\n", - "Key 3: [19 26 15 16 29]\n", - "Key 4: [ 1 14 3 4 23]\n" + "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" ] }, { @@ -265,15 +265,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [32049 6729 60466 26997 1309]\n", - "Key 1: [61686 12369 40030 7765 16822]\n", - "Key 2: [29442 13543 37604 7572 18665]\n", - "Key 3: [29124 1259 53208 47841 51215]\n", - "Key 4: [48741 14803 46096 27958 19258]\n" + "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": 229 + "execution_count": 31 }, { "metadata": {}, @@ -286,19 +286,19 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:22:48.143268Z", - "start_time": "2025-03-19T14:22:48.140039Z" + "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", - "user_passcode_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n", - "selected_keys_set = select_keys_with_passcode_values(user_passcode_indices, set_signup_keypad, keypad_size.numb_of_keys)\n", - "print(f\"User Passcode Indices: {user_passcode_indices}\")\n", - "print(f\"User Passcode Icons: {user_icons[user_passcode_indices]}\")\n", - "print(f\"User Passcode Server-side properties: {customer.cipher.property_key[user_passcode_indices]}\")\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": [ @@ -306,27 +306,27 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Passcode Indices: [11, 22, 1, 23]\n", - "User Passcode Icons: ['😴' '🐸' '😂' '🐙']\n", - "User Passcode Server-side properties: [16822 26997 48741 19258]\n", - "Selected Keys: [1, 0, 4, 4]\n" + "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": 237 + "execution_count": 32 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:48.697364Z", - "start_time": "2025-03-19T14:18:48.694055Z" + "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(user_passcode_indices, 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": [ @@ -334,23 +334,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [13 14 9 10 23]\n", - "Key 1: [25 20 21 4 17]\n", - "Key 2: [ 1 8 15 22 5]\n", - "Key 3: [19 26 27 28 11]\n", - "Key 4: [ 7 2 3 16 29]\n", + "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, 2, 3, 0]\n" + "[2, 0, 2, 4]\n" ] } ], - "execution_count": 202 + "execution_count": 33 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.028822Z", - "start_time": "2025-03-19T14:18:48.719642Z" + "end_time": "2025-03-20T08:32:08.575664Z", + "start_time": "2025-03-20T08:32:08.337518Z" } }, "cell_type": "code", @@ -360,7 +360,7 @@ "assert success" ], "outputs": [], - "execution_count": 203 + "execution_count": 34 }, { "metadata": {}, @@ -369,9 +369,9 @@ "## User Cipher\n", "\n", "Users have 4 cipher keys:\n", - "1. prop_key: Half of the user's server-side passcode. the counterpart to the `customer_prop_key`. A user's passcode is made from elements in `user_prop_key XOR customer_prop_key`. Each property belongs to a set.\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_set_key: The combined set key is `user_set_key XOR customer_set_key`. The user_set_key isn't stored and can't be recovered with the `customer_set_key`\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" @@ -380,24 +380,24 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.044808Z", - "start_time": "2025-03-19T14:18:49.038903Z" + "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.prop_key.reshape(-1, keypad_size.props_per_key)" + "user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)" ], "outputs": [], - "execution_count": 204 + "execution_count": 35 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.056184Z", - "start_time": "2025-03-19T14:18:49.053919Z" + "end_time": "2025-03-20T08:32:08.596984Z", + "start_time": "2025-03-20T08:32:08.595221Z" } }, "cell_type": "code", @@ -408,21 +408,21 @@ "output_type": "stream", "text": [ "Property Key:\n", - "[[42472 31697 42349 63196 42777 61068]\n", - " [ 7243 387 55065 19589 60418 22963]\n", - " [26541 59081 11622 22333 35608 42306]\n", - " [58621 57412 35828 19293 16394 53334]\n", - " [ 5908 43761 45282 44085 8881 21753]]\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": 205 + "execution_count": 36 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.080744Z", - "start_time": "2025-03-19T14:18:49.078469Z" + "end_time": "2025-03-20T08:32:08.615012Z", + "start_time": "2025-03-20T08:32:08.613119Z" } }, "cell_type": "code", @@ -432,17 +432,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Key: [15958 16933 12810 19682 61534 54403 52645 54893 63261 22611]\n" + "Passcode Key: [31049 4633 40678 55986 14115 22499 3470 53359 20871 60539]\n" ] } ], - "execution_count": 206 + "execution_count": 37 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.125439Z", - "start_time": "2025-03-19T14:18:49.123Z" + "end_time": "2025-03-20T08:32:08.639696Z", + "start_time": "2025-03-20T08:32:08.637469Z" } }, "cell_type": "code", @@ -452,119 +452,112 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mask Key: [11511 36348 3693 57612 7883 59516 54039 57361 15218 43846]\n" + "Mask Key: [55361 38182 36656 63013 26815 17961 23911 65497 28524 60226]\n" ] } ], - "execution_count": 207 + "execution_count": 38 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.154768Z", - "start_time": "2025-03-19T14:18:49.152444Z" + "end_time": "2025-03-20T08:32:08.658960Z", + "start_time": "2025-03-20T08:32:08.657009Z" } }, "cell_type": "code", - "source": "print(f\"Combined Set Key: {user_cipher.combined_set_key}\")", + "source": "print(f\"Combined Position Key: {user_cipher.combined_position_key}\")", "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Combined Set Key: [16302 28740 39642 59999 35588 576]\n" + "Combined Position Key: [33934 3750 42586 25190 7504 35546]\n" ] } ], - "execution_count": 208 + "execution_count": 39 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.188943Z", - "start_time": "2025-03-19T14:18:49.186648Z" + "end_time": "2025-03-20T08:32:08.678360Z", + "start_time": "2025-03-20T08:32:08.676228Z" } }, "cell_type": "code", - "source": "print(f\"User Set Key = combined_set_key XOR customer_set_key: {user_cipher.combined_set_key ^ customer.cipher.position_key}\")", + "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 Set Key = combined_set_key XOR customer_set_key: [62370 29747 52653 65461 59944 52180]\n" + "User Position Key = combined_pos_key XOR customer_pos_key: [55210 313 43395 57042 9224 15913]\n" ] } ], - "execution_count": 209 + "execution_count": 40 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.217942Z", - "start_time": "2025-03-19T14:18:49.215165Z" + "end_time": "2025-03-20T08:32:08.698976Z", + "start_time": "2025-03-20T08:32:08.696420Z" } }, "cell_type": "code", "source": [ - "set_properties_dict = dict(zip(user_cipher.combined_set_key, user_prop_key_keypad.T))\n", - "print(f\"Combined Set to Properties Map:\")\n", - "for set_val, props in set_properties_dict.items():\n", - " print(f\"{set_val}: {props}\")" + "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 Set to Properties Map:\n", - "16302: [42472 7243 26541 58621 5908]\n", - "28740: [31697 387 59081 57412 43761]\n", - "39642: [42349 55065 11622 35828 45282]\n", - "59999: [63196 19589 22333 19293 44085]\n", - "35588: [42777 60418 35608 16394 8881]\n", - "576: [61068 22963 42306 53334 21753]\n" + "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": 210 + "execution_count": 41 }, { "metadata": {}, "cell_type": "markdown", "source": [ "#### Encipher Mask\n", - "1. Order customer properties by user passcode.\n", - "2. Get the set indices (`set_indices`) of the ordered customer properties.\n", - "3. Pad the\n", - "1. combined_set_key = user_set_key ^ customer_set_key\n", - "2. padded_ordered_customer_set = customer_set_key # ordered by user passcode and padded with extra set key values to be equal to max_nkode_len\n", - "3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n", - "where i is the index\n", - " \n", - "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_combined_set_key\n", - "- mask = mask_key ^ (customer_set_key) ^ set_rand_numb ^ set_val\n", - "- mask = mask_rand_num ^ set_rand_numb # set_val is cancelled out" + "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-19T14:18:49.244375Z", - "start_time": "2025-03-19T14:18:49.241861Z" + "end_time": "2025-03-20T08:32:08.762578Z", + "start_time": "2025-03-20T08:32:08.760236Z" } }, "cell_type": "code", "source": [ - "set_indices = customer.cipher.get_passcode_position_indices_padded(list(user_passcode_indices), customer.nkode_policy.max_nkode_len)\n", - "ordered_combined_set_key = user_cipher.combined_set_key[set_indices]\n", - "ordered_customer_set_key = customer.cipher.position_key[set_indices]\n", - "ordered_user_set_key = ordered_customer_set_key ^ ordered_combined_set_key\n", - "mask = ordered_user_set_key ^ user_cipher.mask_key\n", + "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": 211 + "execution_count": 42 }, { "metadata": {}, @@ -581,14 +574,14 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.575573Z", - "start_time": "2025-03-19T14:18:49.268784Z" + "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.prop_key\n", - "user_passcode = combined_prop_key[user_passcode_indices]\n", + "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", @@ -596,7 +589,7 @@ "passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode(\"utf-8\")" ], "outputs": [], - "execution_count": 212 + "execution_count": 43 }, { "metadata": {}, @@ -617,8 +610,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.585161Z", - "start_time": "2025-03-19T14:18:49.583132Z" + "end_time": "2025-03-20T08:32:09.032955Z", + "start_time": "2025-03-20T08:32:09.030937Z" } }, "cell_type": "code", @@ -631,7 +624,7 @@ ")" ], "outputs": [], - "execution_count": 213 + "execution_count": 44 }, { "metadata": {}, @@ -645,16 +638,16 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.901534Z", - "start_time": "2025-03-19T14:18:49.594710Z" + "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(user_passcode_indices, login_keypad, keypad_size.props_per_key)\n", - "print(f\"User Passcode: {user_passcode_indices}\\n\")\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" @@ -664,20 +657,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [ 6 19 8 3 4 23]\n", - "Key 1: [18 13 2 21 28 5]\n", - "Key 2: [12 25 14 15 16 11]\n", - "Key 3: [24 7 26 9 22 17]\n", - "Key 4: [ 0 1 20 27 10 29]\n", - "User Passcode: [15, 1, 19, 23]\n", + "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", - " [2, 4, 0, 0]\n", + " [4, 3, 2, 4]\n", "\n" ] } ], - "execution_count": 214 + "execution_count": 45 }, { "metadata": {}, @@ -708,24 +701,24 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:49.914512Z", - "start_time": "2025-03-19T14:18:49.911338Z" + "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(user_passcode_indices, 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", "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_set_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 user_passcode_indices == presumed_selected_properties_idx\n" + "assert passcode_property_indices == presumed_selected_properties_idx\n" ], "outputs": [], - "execution_count": 215 + "execution_count": 46 }, { "metadata": {}, @@ -735,8 +728,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:50.236818Z", - "start_time": "2025-03-19T14:18:49.932427Z" + "end_time": "2025-03-20T08:32:09.526068Z", + "start_time": "2025-03-20T08:32:09.295445Z" } }, "cell_type": "code", @@ -745,7 +738,7 @@ "assert valid_nkode\n" ], "outputs": [], - "execution_count": 216 + "execution_count": 47 }, { "metadata": {}, @@ -761,8 +754,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:50.851576Z", - "start_time": "2025-03-19T14:18:50.240709Z" + "end_time": "2025-03-20T08:32:09.996256Z", + "start_time": "2025-03-20T08:32:09.529950Z" } }, "cell_type": "code", @@ -777,7 +770,7 @@ "print_user_enciphered_code()\n", "\n", "login_keypad = api.get_login_keypad(username, customer_id)\n", - "selected_keys_login = select_keys_with_passcode_values(user_passcode_indices, 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", "success = api.login(customer_id, username, selected_keys_login)\n", "assert success\n", "print_user_enciphered_code()" @@ -787,16 +780,16 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: tBqaWnmsGlsclLRq7qdFeuZ/Krg=, code: $2b$12$iBdtB4bRMx9VnWKwAInkwecUx5wwiL1C0DVWO.sk9EG1syVVe2CaS\n", + "mask: cz9m1OunYlEK42X0jRrGAzKvheg=, code: $2b$12$Z.N7qwUTMgSVJFQC9hKgjeQ8owBpZMm5Aa14RQdiJH7C8l61QJENS\n", "\n", - "mask: tBqaWnmsGlsclLRq7qdFeuZ/Krg=, code: $2b$12$iBdtB4bRMx9VnWKwAInkwecUx5wwiL1C0DVWO.sk9EG1syVVe2CaS\n", + "mask: cz9m1OunYlEK42X0jRrGAzKvheg=, code: $2b$12$Z.N7qwUTMgSVJFQC9hKgjeQ8owBpZMm5Aa14RQdiJH7C8l61QJENS\n", "\n", - "mask: DpuEwDyPrOF4pAxxozyqUHigaak=, code: $2b$12$ngd6PgpZo/UhIANnrRimlOliRIGwAaS6zbe5gqTzYHPBaeAa3vJsy\n", + "mask: e+RtDYeB1G1RfuTOjCna6K9xLUU=, code: $2b$12$DHdD52jbBdVoXYArhWCm7eABlnch.tNhO/1Eipygj8fpoUFuPzyEC\n", "\n" ] } ], - "execution_count": 217 + "execution_count": 48 }, { "metadata": {}, @@ -810,8 +803,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:50.865243Z", - "start_time": "2025-03-19T14:18:50.861260Z" + "end_time": "2025-03-20T08:32:10.012897Z", + "start_time": "2025-03-20T08:32:10.009292Z" } }, "cell_type": "code", @@ -823,7 +816,7 @@ "new_sets = customer.cipher.position_key" ], "outputs": [], - "execution_count": 218 + "execution_count": 49 }, { "metadata": {}, @@ -836,8 +829,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:50.876670Z", - "start_time": "2025-03-19T14:18:50.874092Z" + "end_time": "2025-03-20T08:32:10.019285Z", + "start_time": "2025-03-20T08:32:10.017224Z" } }, "cell_type": "code", @@ -846,11 +839,11 @@ "sets_xor = np.bitwise_xor(new_sets, old_sets)\n", "for user in customer.users.values():\n", " user.renew = True\n", - " user.cipher.combined_set_key = np.bitwise_xor(user.cipher.combined_set_key, sets_xor)\n", - " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" + " 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": 219 + "execution_count": 50 }, { "metadata": {}, @@ -860,8 +853,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-19T14:18:51.191895Z", - "start_time": "2025-03-19T14:18:50.885862Z" + "end_time": "2025-03-20T08:32:10.260051Z", + "start_time": "2025-03-20T08:32:10.026503Z" } }, "cell_type": "code", @@ -875,7 +868,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": 220 + "execution_count": 51 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index fd8b5cb..903eb64 100644 --- a/src/customer.py +++ b/src/customer.py @@ -16,7 +16,7 @@ class Customer: @classmethod def create(cls, nkode_policy: NKodePolicy, cipher: CustomerCipher) -> 'Customer': - if nkode_policy.distinct_sets > cipher.keypad_size.numb_of_keys: + if nkode_policy.distinct_positions > cipher.keypad_size.numb_of_keys: raise ValueError("Distinct sets cannot be greater than the number of keys") if nkode_policy.distinct_properties > cipher.keypad_size.total_props: raise ValueError("Distinct properties cannot be greater than the total number of properties") @@ -76,7 +76,7 @@ class Customer: distinct_properties = len(set(passcode_prop_idx)) if ( self.nkode_policy.min_nkode_len <= nkode_len <= self.nkode_policy.max_nkode_len and - distinct_sets >= self.nkode_policy.distinct_sets and + distinct_sets >= self.nkode_policy.distinct_positions and distinct_properties >= self.nkode_policy.distinct_properties ): return True diff --git a/src/models.py b/src/models.py index 121d29c..326e5f5 100644 --- a/src/models.py +++ b/src/models.py @@ -10,7 +10,7 @@ class EncipheredNKode: class NKodePolicy: max_nkode_len: int = 10 min_nkode_len: int = 4 - distinct_sets: int = 0 + distinct_positions: int = 0 distinct_properties: int = 4 byte_len: int = 2 # Todo: this should change the total number of bytes an properties or set value can be lock_out: int = 5 diff --git a/src/user.py b/src/user.py index 0700c0a..9023afa 100644 --- a/src/user.py +++ b/src/user.py @@ -18,8 +18,8 @@ class User: def renew_keys(self, set_xor: np.ndarray, prop_xor: np.ndarray): self.renew = True - self.cipher.combined_set_key = self.cipher.combined_set_key ^ set_xor - self.cipher.prop_key = self.cipher.prop_key ^ prop_xor + self.cipher.combined_position_key = self.cipher.combined_position_key ^ set_xor + self.cipher.property_key = self.cipher.property_key ^ prop_xor def refresh_passcode(self, passcode_prop_idxs: list[int], customer_cipher: CustomerCipher): self.cipher = UserCipher.create( diff --git a/src/user_cipher.py b/src/user_cipher.py index e5bd3de..42cfcc4 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -9,30 +9,31 @@ from src.customer_cipher import CustomerCipher @dataclass class UserCipher: - prop_key: np.ndarray - combined_set_key: np.ndarray + property_key: np.ndarray + combined_position_key: np.ndarray pass_key: np.ndarray mask_key: np.ndarray max_nkode_len: int @classmethod - def create(cls, keypad_size: KeypadSize, customer_set_key: np.ndarray, max_nkode_len: int) -> 'UserCipher': - if len(customer_set_key) != keypad_size.props_per_key: - raise ValueError("Invalid set values") - user_set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) + def create(cls, keypad_size: KeypadSize, customer_pos_key: np.ndarray, max_nkode_len: int) -> 'UserCipher': + if len(customer_pos_key) != keypad_size.props_per_key: + raise ValueError("Invalid position values") + user_pos_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) return UserCipher( - prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), + property_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False), mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False), - combined_set_key=user_set_key ^ customer_set_key, + combined_position_key=user_pos_key ^ customer_pos_key, max_nkode_len=max_nkode_len ) - def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray: + def pad_user_mask(self, user_mask: np.ndarray, pos_vals: np.ndarray) -> np.ndarray: + # TODO: replace with new method if len(user_mask) >= self.max_nkode_len: raise ValueError("User encoded_mask is too long") padding_size = self.max_nkode_len - len(user_mask) - padding = np.random.choice(set_vals, size=padding_size, replace=True).astype(np.uint16) + padding = np.random.choice(pos_vals, size=padding_size, replace=True).astype(np.uint16) return np.concatenate([user_mask, padding]) @staticmethod @@ -90,7 +91,7 @@ class UserCipher: passcode_cipher = self.pass_key.copy() passcode_cipher[:passcode_len] = ( passcode_cipher[:passcode_len] ^ - self.prop_key[passcode_prop_idx] ^ + self.property_key[passcode_prop_idx] ^ customer_prop.property_key[passcode_prop_idx] ) return passcode_cipher.astype(np.uint16).tobytes() @@ -100,21 +101,21 @@ class UserCipher: passcode_prop_idx: list[int], customer_cipher: CustomerCipher ) -> str: - set_idxs = customer_cipher.get_passcode_position_indices_padded(passcode_prop_idx, len(self.mask_key)) - ordered_set_key = self.combined_set_key[set_idxs] - ordered_customer_key = customer_cipher.position_key[set_idxs] - mask = ordered_set_key ^ ordered_customer_key ^ self.mask_key + pos_idxs = customer_cipher.get_passcode_position_indices_padded(passcode_prop_idx, len(self.mask_key)) + ordered_pos_key = self.combined_position_key[pos_idxs] + ordered_customer_pos_key = customer_cipher.position_key[pos_idxs] + mask = ordered_pos_key ^ ordered_customer_pos_key ^ self.mask_key encoded_mask = self.encode_base64_str(mask) return encoded_mask - def decipher_mask(self, encoded_mask: str, customer_set_key: np.ndarray, passcode_len: int) -> list[int]: + def decipher_mask(self, encoded_mask: str, customer_pos_key: np.ndarray, passcode_len: int) -> list[int]: mask = self.decode_base64_str(encoded_mask) - # user_set_key ordered by the user's nkode and padded to be length max_nkode_len - ordered_set_key = mask ^ self.mask_key - user_set_key = customer_set_key ^ self.combined_set_key - passcode_sets = [] - for partial_set in ordered_set_key[:passcode_len]: - set_idx = np.where(user_set_key == partial_set)[0][0] - passcode_sets.append(int(customer_set_key[set_idx])) - return passcode_sets + # user_pos_key ordered by the user's nkode and padded to be length max_nkode_len + ordered_user_pos_key = mask ^ self.mask_key + user_pos_key = customer_pos_key ^ self.combined_position_key + passcode_position = [] + for position_val in ordered_user_pos_key[:passcode_len]: + position_idx = np.where(user_pos_key == position_val)[0][0] + passcode_position.append(int(customer_pos_key[position_idx])) + return passcode_position diff --git a/src/user_keypad.py b/src/user_keypad.py index 399c092..80efd27 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -83,16 +83,16 @@ class UserKeypad: if not (0 <= key_numb < self.keypad_size.numb_of_keys): raise ValueError(f"key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not (0 <= set_idx < self.keypad_size.props_per_key): - raise ValueError(f"set_indices must be between 0 and {self.keypad_size.props_per_key - 1}") + raise ValueError(f"padded_passcode_position_indices must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_prop_idx = self.keypad_matrix() return int(keypad_prop_idx[key_numb][set_idx]) def get_prop_idxs_by_keynumb_setidx(self, key_numb: list[int], set_idx: list[int]) -> list[int]: if len(key_numb) != len(set_idx): - raise ValueError("key_numb and set_indices must be the same length") + raise ValueError("key_numb and padded_passcode_position_indices must be the same length") if not all(0 <= kn < self.keypad_size.numb_of_keys for kn in key_numb): raise ValueError(f"All key_numb must be between 0 and {self.keypad_size.numb_of_keys - 1}") if not all(0 <= si < self.keypad_size.props_per_key for si in set_idx): - raise ValueError(f"All set_indices must be between 0 and {self.keypad_size.props_per_key - 1}") + raise ValueError(f"All padded_passcode_position_indices must be between 0 and {self.keypad_size.props_per_key - 1}") keypad_matrix = self.keypad_matrix() return keypad_matrix[key_numb, set_idx].reshape(-1).tolist() diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 26845d5..d867995 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -29,10 +29,10 @@ def test_decode_mask(keypad_size, max_nkode_len): customer = CustomerCipher.create(keypad_size) passcode_entry = np.random.choice(keypad_size.total_props, 4, replace=False) passcode_values = [customer.property_key[idx] for idx in passcode_entry] - set_vals = customer.position_key - user_keys = UserCipher.create(keypad_size, set_vals, max_nkode_len) + customer_pos_vals = customer.position_key + user_keys = UserCipher.create(keypad_size, customer_pos_vals, max_nkode_len) passcode = user_keys.encipher_nkode(passcode_entry, customer) - orig_passcode_set_vals = customer.get_props_position_vals(passcode_values) - passcode_set_vals = user_keys.decipher_mask(passcode.mask, set_vals, len(passcode_entry)) - assert (len(passcode_set_vals) == len(orig_passcode_set_vals)) - assert (all(orig_passcode_set_vals[idx] == passcode_set_vals[idx] for idx in range(len(passcode_set_vals)))) + orig_passcode_pos_vals = customer.get_props_position_vals(passcode_values) + passcode_pos_vals = user_keys.decipher_mask(passcode.mask, customer_pos_vals, len(passcode_entry)) + assert (len(passcode_pos_vals) == len(orig_passcode_pos_vals)) + assert (all(orig_passcode_pos_vals[idx] == passcode_pos_vals[idx] for idx in range(len(passcode_pos_vals)))) -- 2.49.1 From 9651455c1ae4347d1fd0f5e4ef11a2ccc9ad3758 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 20 Mar 2025 06:40:54 -0500 Subject: [PATCH 39/85] final draft --- notebooks/nkode_tutorial.ipynb | 443 ++++++++++++++++----------------- 1 file changed, 216 insertions(+), 227 deletions(-) diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 64d4fdc..f53c722 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -30,12 +30,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.092320Z", - "start_time": "2025-03-20T08:32:08.087658Z" + "end_time": "2025-03-20T10:38:44.577863Z", + "start_time": "2025-03-20T10:38:44.572040Z" } }, "outputs": [], - "execution_count": 27 + "execution_count": 75 }, { "cell_type": "code", @@ -53,12 +53,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.113043Z", - "start_time": "2025-03-20T08:32:08.110624Z" + "end_time": "2025-03-20T10:38:44.585583Z", + "start_time": "2025-03-20T10:38:44.582961Z" } }, "outputs": [], - "execution_count": 28 + "execution_count": 76 }, { "metadata": {}, @@ -112,8 +112,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.198233Z", - "start_time": "2025-03-20T08:32:08.193182Z" + "end_time": "2025-03-20T10:38:44.601079Z", + "start_time": "2025-03-20T10:38:44.595017Z" } }, "outputs": [ @@ -121,30 +121,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Position Key: [21284 3999 4057 48308 14680 46323]\n", + "Customer Position Key: [10895 31772 47823 53466 56263 49352]\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", + "[32913 31208 39571 1116 2737 19900]\n", + "[ 4026 23392 64571 25864 56877 34756]\n", + "[56837 8582 51951 34890 37611 61978]\n", + "[55074 11623 3931 21342 53702 21700]\n", + "[26922 1472 49420 42668 7254 41918]\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" + "10895: [32913 4026 56837 55074 26922]\n", + "31772: [31208 23392 8582 11623 1472]\n", + "47823: [39571 64571 51951 3931 49420]\n", + "53466: [ 1116 25864 34890 21342 42668]\n", + "56263: [ 2737 56877 37611 53702 7254]\n", + "49352: [19900 34756 61978 21700 41918]\n" ] } ], - "execution_count": 29 + "execution_count": 77 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.226660Z", - "start_time": "2025-03-20T08:32:08.224216Z" + "end_time": "2025-03-20T10:38:44.612692Z", + "start_time": "2025-03-20T10:38:44.610267Z" } }, "cell_type": "code", @@ -161,16 +161,16 @@ "output_type": "stream", "text": [ "Position Value to Icons Map:\n", - "21284: ['😀' '🥺' '🤔' '🐱' '🦄']\n", - "3999: ['😂' '😡' '🙃' '🐶' '🌟']\n", - "4057: ['🥳' '😱' '😇' '🦁' '⚡']\n", - "48308: ['😍' '🤯' '🤖' '🐻' '🔥']\n", - "14680: ['🤓' '🥰' '👽' '🐸' '🍕']\n", - "46323: ['😎' '😴' '👾' '🐙' '🎉']\n" + "10895: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "31772: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "47823: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "53466: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "56263: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "49352: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], - "execution_count": 30 + "execution_count": 78 }, { "metadata": {}, @@ -194,8 +194,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.256237Z", - "start_time": "2025-03-20T08:32:08.250147Z" + "end_time": "2025-03-20T10:38:44.636348Z", + "start_time": "2025-03-20T10:38:44.630660Z" } }, "cell_type": "code", @@ -223,11 +223,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: ['🦄' '😂' '😇' '👽' '😴']\n", - "Key 1: ['🐱' '🌟' '😱' '🐸' '😎']\n", - "Key 2: ['🤔' '🙃' '🦁' '🥰' '🐙']\n", - "Key 3: ['😀' '😡' '⚡' '🍕' '👾']\n", - "Key 4: ['🥺' '🐶' '🥳' '🤓' '🎉']\n" + "Key 0: ['🐱' '🌟' '🤖' '🤓' '🎉']\n", + "Key 1: ['🥺' '🙃' '🔥' '🍕' '😎']\n", + "Key 2: ['🦄' '🐶' '🤯' '👽' '🐙']\n", + "Key 3: ['😀' '😂' '😍' '🥰' '😴']\n", + "Key 4: ['🤔' '😡' '🐻' '🐸' '👾']\n" ] }, { @@ -244,11 +244,11 @@ "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" + "Key 0: [18 25 15 4 29]\n", + "Key 1: [ 6 13 27 28 5]\n", + "Key 2: [24 19 9 16 23]\n", + "Key 3: [ 0 1 3 10 11]\n", + "Key 4: [12 7 21 22 17]\n" ] }, { @@ -265,15 +265,15 @@ "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" + "Key 0: [55074 1472 34890 2737 41918]\n", + "Key 1: [ 4026 8582 42668 7254 19900]\n", + "Key 2: [26922 11623 25864 37611 21700]\n", + "Key 3: [32913 31208 1116 56877 34756]\n", + "Key 4: [56837 23392 21342 53702 61978]\n" ] } ], - "execution_count": 31 + "execution_count": 79 }, { "metadata": {}, @@ -286,8 +286,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.280568Z", - "start_time": "2025-03-20T08:32:08.277339Z" + "end_time": "2025-03-20T10:38:44.674025Z", + "start_time": "2025-03-20T10:38:44.671302Z" } }, "cell_type": "code", @@ -306,20 +306,20 @@ "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" + "User Passcode Indices: [28, 13, 9, 10]\n", + "User Passcode Icons: ['🍕' '🙃' '🤯' '🥰']\n", + "User Passcode Server-side properties: [ 7254 8582 25864 56877]\n", + "Selected Keys: [1, 1, 2, 3]\n" ] } ], - "execution_count": 32 + "execution_count": 80 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.316148Z", - "start_time": "2025-03-20T08:32:08.313495Z" + "end_time": "2025-03-20T10:38:44.700525Z", + "start_time": "2025-03-20T10:38:44.697597Z" } }, "cell_type": "code", @@ -334,23 +334,23 @@ "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", + "Key 0: [ 6 7 3 4 23]\n", + "Key 1: [24 1 15 28 17]\n", + "Key 2: [12 25 27 16 11]\n", + "Key 3: [ 0 13 9 22 29]\n", + "Key 4: [18 19 21 10 5]\n", "Selected Keys\n", - "[2, 0, 2, 4]\n" + "[1, 3, 3, 4]\n" ] } ], - "execution_count": 33 + "execution_count": 81 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.575664Z", - "start_time": "2025-03-20T08:32:08.337518Z" + "end_time": "2025-03-20T10:38:44.947744Z", + "start_time": "2025-03-20T10:38:44.709897Z" } }, "cell_type": "code", @@ -360,7 +360,7 @@ "assert success" ], "outputs": [], - "execution_count": 34 + "execution_count": 82 }, { "metadata": {}, @@ -380,8 +380,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.588049Z", - "start_time": "2025-03-20T08:32:08.583087Z" + "end_time": "2025-03-20T10:38:44.959614Z", + "start_time": "2025-03-20T10:38:44.954840Z" } }, "cell_type": "code", @@ -391,13 +391,13 @@ "user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)" ], "outputs": [], - "execution_count": 35 + "execution_count": 83 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.596984Z", - "start_time": "2025-03-20T08:32:08.595221Z" + "end_time": "2025-03-20T10:38:44.968863Z", + "start_time": "2025-03-20T10:38:44.966571Z" } }, "cell_type": "code", @@ -408,21 +408,21 @@ "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" + "[[53739 14349 64971 50952 51008 37461]\n", + " [45744 17444 29958 27397 60694 8069]\n", + " [43064 47943 49025 5623 3145 4890]\n", + " [ 1128 11012 53093 14232 26108 5391]\n", + " [59341 47378 48497 6840 34437 29587]]\n" ] } ], - "execution_count": 36 + "execution_count": 84 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.615012Z", - "start_time": "2025-03-20T08:32:08.613119Z" + "end_time": "2025-03-20T10:38:44.985451Z", + "start_time": "2025-03-20T10:38:44.983449Z" } }, "cell_type": "code", @@ -432,17 +432,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Key: [31049 4633 40678 55986 14115 22499 3470 53359 20871 60539]\n" + "Passcode Key: [16245 48001 4534 55258 54613 15211 33171 56565 33961 50654]\n" ] } ], - "execution_count": 37 + "execution_count": 85 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.639696Z", - "start_time": "2025-03-20T08:32:08.637469Z" + "end_time": "2025-03-20T10:38:45.007753Z", + "start_time": "2025-03-20T10:38:45.005712Z" } }, "cell_type": "code", @@ -452,17 +452,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mask Key: [55361 38182 36656 63013 26815 17961 23911 65497 28524 60226]\n" + "Mask Key: [52084 24514 63626 6657 19669 39430 35626 25229 14824 63798]\n" ] } ], - "execution_count": 38 + "execution_count": 86 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.658960Z", - "start_time": "2025-03-20T08:32:08.657009Z" + "end_time": "2025-03-20T10:38:45.026885Z", + "start_time": "2025-03-20T10:38:45.024649Z" } }, "cell_type": "code", @@ -472,17 +472,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Combined Position Key: [33934 3750 42586 25190 7504 35546]\n" + "Combined Position Key: [10235 12456 898 54650 50445 20719]\n" ] } ], - "execution_count": 39 + "execution_count": 87 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.678360Z", - "start_time": "2025-03-20T08:32:08.676228Z" + "end_time": "2025-03-20T10:38:45.041101Z", + "start_time": "2025-03-20T10:38:45.038985Z" } }, "cell_type": "code", @@ -492,17 +492,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Position Key = combined_pos_key XOR customer_pos_key: [55210 313 43395 57042 9224 15913]\n" + "User Position Key = combined_pos_key XOR customer_pos_key: [ 3444 19636 47437 1440 7882 36903]\n" ] } ], - "execution_count": 40 + "execution_count": 88 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.698976Z", - "start_time": "2025-03-20T08:32:08.696420Z" + "end_time": "2025-03-20T10:38:45.059433Z", + "start_time": "2025-03-20T10:38:45.056942Z" } }, "cell_type": "code", @@ -518,16 +518,16 @@ "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" + "10235: [53739 45744 43064 1128 59341]\n", + "12456: [14349 17444 47943 11012 47378]\n", + "898: [64971 29958 49025 53093 48497]\n", + "54650: [50952 27397 5623 14232 6840]\n", + "50445: [51008 60694 3145 26108 34437]\n", + "20719: [37461 8069 4890 5391 29587]\n" ] } ], - "execution_count": 41 + "execution_count": 89 }, { "metadata": {}, @@ -544,8 +544,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:08.762578Z", - "start_time": "2025-03-20T08:32:08.760236Z" + "end_time": "2025-03-20T10:38:45.070036Z", + "start_time": "2025-03-20T10:38:45.068009Z" } }, "cell_type": "code", @@ -557,25 +557,25 @@ "encoded_mask = user_cipher.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 42 + "execution_count": 90 }, { "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)" + "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-20T08:32:09.024060Z", - "start_time": "2025-03-20T08:32:08.789902Z" + "end_time": "2025-03-20T10:38:45.320480Z", + "start_time": "2025-03-20T10:38:45.087096Z" } }, "cell_type": "code", @@ -583,48 +583,13 @@ "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", + "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": 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 + "execution_count": 91 }, { "metadata": {}, @@ -632,14 +597,14 @@ "source": [ "### User Login\n", "1. Get login keypad\n", - "2. Login\n" + "2. Select keys with passcode icons (in our case, passcode property indices)\n" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:09.273099Z", - "start_time": "2025-03-20T08:32:09.040255Z" + "end_time": "2025-03-20T10:38:45.559945Z", + "start_time": "2025-03-20T10:38:45.326214Z" } }, "cell_type": "code", @@ -657,29 +622,29 @@ "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", + "Key 0: [18 25 14 15 4 29]\n", + "Key 1: [ 6 13 20 27 28 5]\n", + "Key 2: [24 19 26 9 16 23]\n", + "Key 3: [ 0 1 8 3 10 11]\n", + "Key 4: [12 7 2 21 22 17]\n", + "User Passcode: [28, 13, 9, 10]\n", "\n", "Selected Keys:\n", - " [4, 3, 2, 4]\n", + " [1, 1, 2, 3]\n", "\n" ] } ], - "execution_count": 45 + "execution_count": 92 }, { "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" + "- decipher user mask and recover nkode position values\n", + "- get presumed properties from key selection and position values\n", + "- compare with hash" ] }, { @@ -687,22 +652,18 @@ "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", + "Recover nKode position 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", + "- 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-20T08:32:09.282728Z", - "start_time": "2025-03-20T08:32:09.279895Z" + "end_time": "2025-03-20T10:38:45.570140Z", + "start_time": "2025-03-20T10:38:45.567396Z" } }, "cell_type": "code", @@ -711,34 +672,51 @@ "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" + "ordered_user_position_key = mask ^ user.cipher.mask_key\n", + "user_position_key = customer.cipher.position_key ^ user.cipher.combined_position_key\n" ], "outputs": [], - "execution_count": 46 + "execution_count": 93 }, { "metadata": {}, "cell_type": "markdown", - "source": "### Compare Enciphered Passcodes" + "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": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "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" + ] + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Compare Enciphered Passcodes\n" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:09.526068Z", - "start_time": "2025-03-20T08:32:09.295445Z" + "end_time": "2025-03-20T10:38:45.809575Z", + "start_time": "2025-03-20T10:38:45.576610Z" } }, "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" + "valid_nkode = user.cipher.compare_nkode(presumed_property_indices, customer.cipher, user.enciphered_passcode.code)\n", + "assert valid_nkode" ], "outputs": [], - "execution_count": 47 + "execution_count": 94 }, { "metadata": {}, @@ -754,8 +732,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:09.996256Z", - "start_time": "2025-03-20T08:32:09.529950Z" + "end_time": "2025-03-20T10:38:46.286081Z", + "start_time": "2025-03-20T10:38:45.816688Z" } }, "cell_type": "code", @@ -765,110 +743,121 @@ " 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(\"Old User Cipher and Mask\")\n", "print_user_enciphered_code()\n", - "\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)\n", - "assert success\n", - "print_user_enciphered_code()" + "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": [ - "mask: cz9m1OunYlEK42X0jRrGAzKvheg=, code: $2b$12$Z.N7qwUTMgSVJFQC9hKgjeQ8owBpZMm5Aa14RQdiJH7C8l61QJENS\n", + "Old User Cipher and Mask\n", + "mask: y8aMd+5qYcPVLETTcs2aHqx2hhk=, code: $2b$12$wMi7WGmlch8kWMYJ2v.FHOne1.YSQPqKU/itpBuycwSFyasryF/2u\n", "\n", - "mask: cz9m1OunYlEK42X0jRrGAzKvheg=, code: $2b$12$Z.N7qwUTMgSVJFQC9hKgjeQ8owBpZMm5Aa14RQdiJH7C8l61QJENS\n", - "\n", - "mask: e+RtDYeB1G1RfuTOjCna6K9xLUU=, code: $2b$12$DHdD52jbBdVoXYArhWCm7eABlnch.tNhO/1Eipygj8fpoUFuPzyEC\n", + "New User Cipher and Mask\n", + "mask: aln5Su79utoVmqQXNCjYiUdwVYw=, code: $2b$12$gQf3UVa3cWMBy0CO0sBLyuJzdXGzg3qpNFMTD6MvycuR6N3gLFdgC\n", "\n" ] } ], - "execution_count": 48 + "execution_count": 95 }, { "metadata": {}, "cell_type": "markdown", "source": [ - "#### Renew Customer Keys\n", - "- Get old properties and sets\n", - "- Replace properties and sets" + "### Renew Customer Keys\n", + "The customer cipher keys are replaced." ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T08:32:10.012897Z", - "start_time": "2025-03-20T08:32:10.009292Z" + "end_time": "2025-03-20T10:38:46.296985Z", + "start_time": "2025-03-20T10:38:46.293584Z" } }, "cell_type": "code", "source": [ "old_props = customer.cipher.property_key.copy()\n", - "old_sets = customer.cipher.position_key.copy()\n", - "customer.cipher.renew()\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_sets = customer.cipher.position_key" + "new_pos = customer.cipher.position_key" ], "outputs": [], - "execution_count": 49 + "execution_count": 96 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Renew User\n", - "\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-20T08:32:10.019285Z", - "start_time": "2025-03-20T08:32:10.017224Z" + "end_time": "2025-03-20T10:38:46.312031Z", + "start_time": "2025-03-20T10:38:46.310019Z" } }, "cell_type": "code", "source": [ - "props_xor = np.bitwise_xor(new_props, old_props)\n", - "sets_xor = np.bitwise_xor(new_sets, old_sets)\n", + "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 = np.bitwise_xor(user.cipher.combined_position_key, sets_xor)\n", - " user.cipher.property_key = np.bitwise_xor(user.cipher.property_key, props_xor)" + " 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": 50 + "execution_count": 97 }, { "metadata": {}, "cell_type": "markdown", - "source": "### Refresh User Keys" + "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-20T08:32:10.260051Z", - "start_time": "2025-03-20T08:32:10.026503Z" + "end_time": "2025-03-20T10:38:46.553807Z", + "start_time": "2025-03-20T10:38:46.318482Z" } }, "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" + "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": 51 + "execution_count": 98 } ], "metadata": { -- 2.49.1 From 36f836421d8ea03188657572659fb236ba84c784 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 20 Mar 2025 06:46:35 -0500 Subject: [PATCH 40/85] delete readme --- README.md | 444 ------------------------------------------------------ 1 file changed, 444 deletions(-) diff --git a/README.md b/README.md index ff4768f..9741fef 100644 --- a/README.md +++ b/README.md @@ -1,446 +1,2 @@ # README Play around with the code in /notebooks - -## Customer Creation -Before creating a user, a customer generates random properties and set -values. The customers manage users. They define an nKode policy, keypad's dimensions, -properties/sets in the keypad, and the frequency of property renewal. -### nKode Policy and Keypad Size -An nKode policy defines: -
    -
  • the maximum length of a user's nKode
  • -
  • the minimum length of a user's nKode
  • -
  • the number of unique set values in a user's nKode
  • -
  • the number of unique values in a user's nKode
  • -
  • the number of bytes in an property and set
  • -
- -The keypad size defines: -
    -
  • the number of keys in the keypad displayed to the user
  • -
  • properties per key
  • -
- -To be [dispersion](nkode_concepts.md/#dispersion-resistant-keypad) resistant, the number of properties must be greater than the number of keys. - -``` -api = NKodeAPI() - -policy = NKodePolicy( - max_nkode_len=10, - min_nkode_len=4, - distinct_sets=0, - distinct_properties=4, - byte_len=2 -) - -keypad_size = KeypadSize( - numb_of_keys = 5, - props_per_key = 6 # aka number of sets -) - -customer_id = api.create_new_customer(keypad_size, policy) -customer = api.customers[customer_id] -``` -### Customer properties and Sets -A customer has users and defines the properties and set values for all its users. -Since our customer has 5 keys and 6 properties per key, -this gives a customer keypad of 30 distinct properties and 6 distinct property sets. -Each property belongs to one of the 6 sets. Each property and set value is a unique 2-byte integer in this example. - -``` -set_vals = customer.cipher.set_key - -Customer Sets: [51397 49224 50087 24444 43554 21522] -``` - -``` -prop_vals = customer.cipher.prop_key -keypad_view(prop_vals, keypad_size.props_per_key) - -Customer properties: -[65030 40058 49729 42519 32475 21731] -[19446 3351 17075 17586 20753 15754] -[19712 56685 43602 30750 54931 27419] -[40397 10398 13477 26037 17943 47642] -[58359 15284 53370 4343 16407 46898] - -``` - -properties organized by set: -``` -prop_set_view = matrix_transpose(prop_keypad_view) -set_property_dict = dict(zip(set_vals, prop_set_view)) - -Set to property Map: -51397 : [65030 19446 19712 40397 58359] -49224 : [40058 3351 56685 10398 15284] -50087 : [49729 17075 43602 13477 53370] -24444 : [42519 17586 30750 26037 4343] -43554 : [32475 20753 54931 17943 16407] -21522 : [21731 15754 27419 47642 46898] - -``` - -## User Signup -Now that we have a customer, we can create users. To create a new user: - -1. Generate a random keypad -2. The user sets their nKode and sends their selection to the server -3. The user confirms their nKode. If the user's nKode matches the policy, the server creates the user. -### Random keypad Generation -The user's keypad must be dispersable so the server can determine the user's nkode. -The server randomly drops property sets until -the number of properties equals the number of keys, making the keypad dispersable. -In our case, the server randomly drops 1 property set. -to give us a 5 X 5 keypad with possible index values ranging from 0-29. -Each value in the keypad is the index value of a customer property. -The user never learns what their "real" property is. They do not see the index value representing their nKode or -the customer server-side value. - -``` -session_id, signup_keypad = api.generate_index_keypad(customer_id) -signup_keypad_keypad = list_to_matrix(signup_keypad, keypad_size.props_per_key) - -Signup Keypad: -Key 1: [19 7 25 1 13] -Key 2: [18 6 24 0 12] -Key 3: [21 9 27 3 15] -Key 4: [23 11 29 5 17] -Key 5: [20 8 26 2 14] - -``` - -### Set nKode -The user identifies properties in the keypad they want in their nkode. Each property has an index value. -Below, the user has selected `[19, 7, 25, 1]`. These index values can be represented by anything in the GUI. -The only requirement is that the GUI properties be associated with the same index every time the user logs in. -If users want to change anything about their keypad, they must also change their nkode. - -``` -username = test_user -user_passcode = [19, 7, 25, 1] -selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_keypad, keypad_size.props_per_key) - -Selected Keys -[0, 0, 0, 0] -``` - -The user's passcode server side properties are: -``` -server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] - -User Passcode Server-side properties: [np.int64(10398), np.int64(3351), np.int64(15284), np.int64(40058)] -``` - -### Confirm nKode -The user submits the set keypad to the server and receives the _confirm keypad_ as a response. -The user finds their nKode again. - -``` -confirm_keypad = api.set_nkode(username, customer_id, selected_keys_set, session_id) -keypad_view(confirm_keypad, keypad_size.numb_of_keys) -selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_keypad, keypad_size.numb_of_keys) - -Confirm Keypad: -Key 1: [20 7 27 5 12] -Key 2: [23 9 26 0 13] -Key 3: [18 8 29 1 15] -Key 4: [19 11 24 3 14] -Key 5: [21 6 25 2 17] - -Selected Keys: -[3, 0, 4, 2] -``` - -The user submits their confirmation key selection and the user is created -``` -success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) -``` - -### Passcode Enciphering, Hashing, and Salting -When a new user creates an nKode, the server caches its set and confirms the keypad and the user's key selection. -On the last api.confirm_nkode, the server: - -1. Deduces the user's properties -2. Validates the Passcode against the nKodePolicy -3. Creates new User Cipher Keys -4. Enciphers the user's mask -5. Enciphers, salts, and hashes the user's passcode - -Steps 1-2 are straightforward. For a better idea of how they work, see pyNKode. - -#### User Cipher Keys - -##### User Cipher Keys Data Structure -``` -set_key = generate_random_nonrepeating_list(keypad_size.props_per_key, max_numb=2**(8*numb_of_bytes)) -set_key = xor_lists(set_key, customer_prop.set_vals) - -UserCipherKeys( - prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), - pass_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), - mask_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), - set_key=set_key, - salt=bcrypt.gensalt(), - max_nkode_len=max_nkode_len -) -``` - -##### User Cipher Keys Values -``` -user_cipher = UserCipherKeys( - prop_key = [ 2923 16019 14458 50197 31207 7212 56686 44981 2641 64112 13044 29822 - 1902 22608 40919 35763 49353 20507 18363 34108 32269 6440 21357 37870 - 60382 18170 45147 13683 20896 12198], - pass_key = [31251 55189 60990 1342 51754 25296 19081 956 41188 43289], - mask_key = [54532 41537 22695 64404 28419 7322 24742 54924 2951 57084], - set_key = [ 3824 27422 49987 58720 10692 60061], - salt = b'$2b$12$iLYVBzbu9DVSg7S.ZBzB..', - max_nkode_len = 10 -) -``` - -The method UserCipherKeys.encipher_nkode secures a user's nKode in the database. This method is called in api.confirm_nkode - -``` -class EncipheredNKode(BaseModel): - code: str - mask: str -``` - -#### Mask Enciphering - -Recall: - -- set_key_i = (set_rand_numb_i ^ set_val_i) -- mask_key_i = mask_rand_numb_i -- padded_passcode_server_set_i = set_val_i -- len(set_key) == len(mask_key) == (padded_passcode_server_set) == max_nkode_len == 10 - where i is the index - -- mask_i = mask_key_i ^ padded_passcode_server_set_i ^ set_key_i -- mask_i = mask_rand_num_i ^ set_val_i ^ set_rand_numb_i ^ set_val_i -- mask_i = mask_rand_num_i ^ set_rand_numb_i # set_val_i is cancelled out - - -``` -passcode = [19, 7, 25, 1] -passcode_server_prop = [customer.cipher.prop_key[idx] for idx in passcode] -passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] - -Passcode Set Vals: [np.int64(10398), np.int64(3351), np.int64(15284), np.int64(40058)] -Passcode prop Vals: [49224, 49224, 49224, 49224] -``` - -``` -padded_passcode_server_set = user_cipher.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) - -set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] -mask_set_keys = [user_cipher.set_key[idx] for idx in set_idx] - -ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) -ciphered_mask = xor_lists(ciphered_mask, user_cipher.mask_key) - -mask = user_cipher.encode_base64_str(ciphered_mask) -Mask: c6kE7P4KXTm3d3KmDprj8dPzBog= -``` - -#### Passcode Enciphering and Hashing - -- ciphered_customer_prop = prop_key ^ customer_prop -- ciphered_passcode_i = pass_key_i ^ ciphered_customer_prop_i -- code = hash(ciphered_passcode, salt) - -``` -ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_cipher.prop_key) -passcode_ciphered_props = [ciphered_customer_props[idx] for idx in passcode] -pad_len = customer.nkode_policy.max_nkode_len - passcode_len - -passcode_ciphered_props.extend([0 for _ in range(pad_len)]) - -ciphered_code = xor_lists(passcode_ciphered_props, user_cipher.pass_key) - -passcode_bytes = int_array_to_bytes(ciphered_code) -passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) -hashed_data = bcrypt.hashpw(passcode_digest, user_cipher.salt) -code = hashed_data.decode("utf-8") - -Code: $2b$12$iLYVBzbu9DVSg7S.ZBzB..eoFhCtiWBtfjXNLULtODYBH8Epva1pC -``` - -## User Login -To login, a user: - -1. Gets login keypad -2. Submits key entry - -### Get Login keypad -The client requests the user's login keypad. -``` -login_keypad = api.get_login_keypad(username, customer_id) -keypad_view(login_keypad, keypad_size.props_per_key) -``` -The server returns a randomly shuffled keypad. Learn more about how the [User keypad Shuffle](nkode_concepts.md/#user-keypad-shuffle) works -``` -Login keypad Keypad View: -Key 1: [18 19 20 21 22 23] -Key 2: [ 6 7 8 9 10 11] -Key 3: [24 25 26 27 28 29] -Key 4: [0 1 2 3 4 5] -Key 5: [12 13 14 15 16 17] - -``` -Recall the user's passcode is `user_passcode = [19, 7, 25, 1]` so the user selects keys ` selected_keys_login = [0, 1, 2, 3]` - -``` -success = api.login(customer_id, username, selected_keys_login) -``` - -### Validate Login Key Entry -- decipher user mask and recover nkode set values -- get presumed property from key selection and set values -- encipher, salt, and hash presumed property values and compare them to the users hashed code - -#### Decipher Mask - -Recall: - -- set_key_i = (set_key_rand_numb_i ^ set_val_i) -- mask_i = mask_key_rand_num_i ^ set_key_rand_numb_i - -Recover nKode set values: - -- decode mask from base64 to int -- deciphered_mask = mask ^ mask_key -- deciphered_mask_i = set_key_rand_numb # mask_key_rand_num_i is cancelled out -- set_key_rand_component = set_key ^ set_values -- deduce the set value - -``` -user = customer.users[username] -user_cipher = user.user_cipher -user_mask = user.enciphered_passcode.mask -decoded_mask = user_cipher.decode_base64_str(user_mask) -deciphered_mask = xor_lists(decoded_mask, user_cipher.mask_key) -set_key_rand_component = xor_lists(set_vals, user_cipher.set_key) -passcode_sets = [] -for set_cipher in deciphered_mask[:passcode_len]: - set_idx = set_key_rand_component.index(set_cipher) - passcode_sets.append(set_vals[set_idx]) - -Passcode Sets: [49224, 49224, 49224, 49224] -``` - - -### Get Presumed properties -``` -set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets] - -presumed_selected_properties_idx = [] -for idx in range(passcode_len): - key_numb = selected_keys_login[idx] - set_idx = set_vals_idx[idx] - selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_selected_properties_idx.append(selected_prop_idx) - -Presumped Passcode: [19, 7, 25, 1] -Recall User Passcode: [19, 7, 25, 1] -``` -### Compare Enciphered Passcodes -``` -enciphered_nkode = user_cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher) -``` -If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid, and the login is successful. - -## Renew properties -properties renew is invoked with the renew_properties method: `api.renew_properties(customer_id)` -The renew properties process has three steps: -1. Renew Customer properties -2. Renew User Keys -3. Refresh User on Login - -When the customer calls the `renew_properties` method, the method replaces the customer's properties and set values. All its users go through an intermediate -renewal step. The users fully renew after their first successful login. This first login refreshes their keys, salt, and hash with new values. - - -### Customer Renew -Old Customer properties and set values are cached and copied to variables before renewal. -``` -old_sets = customer.cipher.set_key - -Customer Sets: [51397 49224 50087 24444 43554 21522] -``` - -``` -old_prop = customer.cipher.prop_key - -Customer properties: -[65030 40058 49729 42519 32475 21731] -[19446 3351 17075 17586 20753 15754] -[19712 56685 43602 30750 54931 27419] -[40397 10398 13477 26037 17943 47642] -[58359 15284 53370 4343 16407 46898] - -``` - -After the renewal, the customer properties and sets are new randomly generated values. -``` -api.renew_properties(customer_id) - -set_vals = customer.cipher.set_key - -Customer Sets: [ 7754 52659 44415 3961 61872 57312] -``` - -``` -prop_vals = customer.cipher.prop_key - -Customer properties: -[57881 51596 44681 30104 33018 30596] -[35764 62538 21274 10697 11311 42560] -[ 4979 33517 18509 55230 26674 24108] -[63335 41237 52341 30975 12398 7267] -[53495 52030 41547 59730 36417 31547] - -``` - -### Renew User -During the renewal, each user goes through a temporary transition period. -``` -props_xor = xor_lists(new_props, old_props) -sets_xor = xor_lists(new_sets, old_sets) -for user in customer.users.values(): - user.renew = True - user.user_cipher.set_key = xor_lists(user.user_cipher.set_key, sets_xor) - user.user_cipher.prop_key = xor_lists(user.user_cipher.prop_key, props_xor) -``` -##### User prop Key -The user's prop key was a randomly generated list of length `numb_of_keys * prop_per_key`. -Now each value in the prop key is `prop_key_i = old_prop_key_i ^ new_prop_i ^ old_prop_i`. -Recall in the login process, `ciphered_customer_props = prop_key ^ customer_prop`. -Since the customer_prop is now the new value, it gets canceled out, leaving: -``` -new_prop_key = old_prop_key ^ old_prop ^ new_prop -ciphered_customer_props = new_prop_key ^ new_prop -ciphered_customer_props = old_prop_key ^ old_prop # since new_prop cancel out -``` -Using the new customer properties, we can validate the user's login attempt with the same hash. - -##### User Set Key -The user's set key was a randomly generated list of length `prop_per_key` xor `customer_set_vals`. -The `old_set_vals` have been replaced with the new `new_set_vals`. The deciphering process described above -remains the same. - -### User Refresh -Once the user has a successful login, they get a new salt and cipher keys, and their `enciphered_passcode` is recomputed -with the new values. -``` -user.user_cipher = UserCipherKeys.new( - customer.cipher.keypad_size, - customer.cipher.set_key, - user.user_cipher.max_nkode_len -) -user.enciphered_passcode = user.user_cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) -user.renew = False -``` \ No newline at end of file -- 2.49.1 From fdcf31948f3b269c4ef762a10b56dd629bb5dbf3 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 20 Mar 2025 07:13:44 -0500 Subject: [PATCH 41/85] add inferring nkode selection --- notebooks/nkode_tutorial.ipynb | 227 +++++++++++++++++++-------------- 1 file changed, 133 insertions(+), 94 deletions(-) diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index f53c722..4557afa 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -30,12 +30,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.577863Z", - "start_time": "2025-03-20T10:38:44.572040Z" + "end_time": "2025-03-20T12:00:21.292663Z", + "start_time": "2025-03-20T12:00:21.253426Z" } }, "outputs": [], - "execution_count": 75 + "execution_count": 2 }, { "cell_type": "code", @@ -53,12 +53,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.585583Z", - "start_time": "2025-03-20T10:38:44.582961Z" + "end_time": "2025-03-20T12:00:21.298357Z", + "start_time": "2025-03-20T12:00:21.296258Z" } }, "outputs": [], - "execution_count": 76 + "execution_count": 3 }, { "metadata": {}, @@ -75,10 +75,10 @@ "source": [ "#### Customer Cipher Keys\n", "Each customer has unique cipher keys.\n", - "These keys are used to encipher and decipher user nKode.\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 the server-side representation the position in each key.\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 @@ -112,8 +112,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.601079Z", - "start_time": "2025-03-20T10:38:44.595017Z" + "end_time": "2025-03-20T12:00:21.317747Z", + "start_time": "2025-03-20T12:00:21.306163Z" } }, "outputs": [ @@ -121,30 +121,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Position Key: [10895 31772 47823 53466 56263 49352]\n", + "Customer Position Key: [37938 42486 36680 47679 5944 46439]\n", "Customer Properties Key:\n", - "[32913 31208 39571 1116 2737 19900]\n", - "[ 4026 23392 64571 25864 56877 34756]\n", - "[56837 8582 51951 34890 37611 61978]\n", - "[55074 11623 3931 21342 53702 21700]\n", - "[26922 1472 49420 42668 7254 41918]\n", + "[14469 12272 54942 53713 60529 64319]\n", + "[49129 65121 60219 33860 48996 57925]\n", + "[61464 3328 59612 33957 62738 42039]\n", + "[ 3074 46306 41956 4712 59250 7730]\n", + "[23724 39640 530 27472 33903 26824]\n", "Position to Properties Map:\n", - "10895: [32913 4026 56837 55074 26922]\n", - "31772: [31208 23392 8582 11623 1472]\n", - "47823: [39571 64571 51951 3931 49420]\n", - "53466: [ 1116 25864 34890 21342 42668]\n", - "56263: [ 2737 56877 37611 53702 7254]\n", - "49352: [19900 34756 61978 21700 41918]\n" + "37938: [14469 49129 61464 3074 23724]\n", + "42486: [12272 65121 3328 46306 39640]\n", + "36680: [54942 60219 59612 41956 530]\n", + "47679: [53713 33860 33957 4712 27472]\n", + "5944: [60529 48996 62738 59250 33903]\n", + "46439: [64319 57925 42039 7730 26824]\n" ] } ], - "execution_count": 77 + "execution_count": 4 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.612692Z", - "start_time": "2025-03-20T10:38:44.610267Z" + "end_time": "2025-03-20T12:00:21.325478Z", + "start_time": "2025-03-20T12:00:21.322970Z" } }, "cell_type": "code", @@ -161,41 +161,41 @@ "output_type": "stream", "text": [ "Position Value to Icons Map:\n", - "10895: ['😀' '🥺' '🤔' '🐱' '🦄']\n", - "31772: ['😂' '😡' '🙃' '🐶' '🌟']\n", - "47823: ['🥳' '😱' '😇' '🦁' '⚡']\n", - "53466: ['😍' '🤯' '🤖' '🐻' '🔥']\n", - "56263: ['🤓' '🥰' '👽' '🐸' '🍕']\n", - "49352: ['😎' '😴' '👾' '🐙' '🎉']\n" + "37938: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "42486: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "36680: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "47679: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "5944: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "46439: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], - "execution_count": 78 + "execution_count": 5 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### User Signup\n", - "Users can create an nkode with these steps:\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", + " 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" + " - the user never learns their server-side properties" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.636348Z", - "start_time": "2025-03-20T10:38:44.630660Z" + "end_time": "2025-03-20T12:00:21.349267Z", + "start_time": "2025-03-20T12:00:21.342655Z" } }, "cell_type": "code", @@ -223,11 +223,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: ['🐱' '🌟' '🤖' '🤓' '🎉']\n", - "Key 1: ['🥺' '🙃' '🔥' '🍕' '😎']\n", - "Key 2: ['🦄' '🐶' '🤯' '👽' '🐙']\n", - "Key 3: ['😀' '😂' '😍' '🥰' '😴']\n", - "Key 4: ['🤔' '😡' '🐻' '🐸' '👾']\n" + "Key 0: ['🐱' '🐶' '😱' '😍' '🍕']\n", + "Key 1: ['🦄' '😡' '⚡' '🤯' '🐸']\n", + "Key 2: ['😀' '🙃' '😇' '🔥' '👽']\n", + "Key 3: ['🥺' '😂' '🥳' '🐻' '🥰']\n", + "Key 4: ['🤔' '🌟' '🦁' '🤖' '🤓']\n" ] }, { @@ -244,11 +244,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [18 25 15 4 29]\n", - "Key 1: [ 6 13 27 28 5]\n", - "Key 2: [24 19 9 16 23]\n", - "Key 3: [ 0 1 3 10 11]\n", - "Key 4: [12 7 21 22 17]\n" + "Key 0: [18 19 8 3 28]\n", + "Key 1: [24 7 26 9 22]\n", + "Key 2: [ 0 13 14 27 16]\n", + "Key 3: [ 6 1 2 21 10]\n", + "Key 4: [12 25 20 15 4]\n" ] }, { @@ -265,29 +265,29 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [55074 1472 34890 2737 41918]\n", - "Key 1: [ 4026 8582 42668 7254 19900]\n", - "Key 2: [26922 11623 25864 37611 21700]\n", - "Key 3: [32913 31208 1116 56877 34756]\n", - "Key 4: [56837 23392 21342 53702 61978]\n" + "Key 0: [ 3074 46306 60219 53713 33903]\n", + "Key 1: [23724 65121 530 33860 59250]\n", + "Key 2: [14469 3328 59612 27472 62738]\n", + "Key 3: [49129 12272 54942 4712 48996]\n", + "Key 4: [61464 39640 41956 33957 60529]\n" ] } ], - "execution_count": 79 + "execution_count": 6 }, { "metadata": {}, "cell_type": "markdown", "source": [ - "## Set nKode\n", + "### Set nKode\n", "The client receives `user_icons`, `set_signup_keypad`\n" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.674025Z", - "start_time": "2025-03-20T10:38:44.671302Z" + "end_time": "2025-03-20T12:00:21.372390Z", + "start_time": "2025-03-20T12:00:21.369564Z" } }, "cell_type": "code", @@ -306,20 +306,28 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Passcode Indices: [28, 13, 9, 10]\n", - "User Passcode Icons: ['🍕' '🙃' '🤯' '🥰']\n", - "User Passcode Server-side properties: [ 7254 8582 25864 56877]\n", - "Selected Keys: [1, 1, 2, 3]\n" + "User Passcode Indices: [19, 28, 22, 18]\n", + "User Passcode Icons: ['🐶' '🍕' '🐸' '🐱']\n", + "User Passcode Server-side properties: [46306 33903 59250 3074]\n", + "Selected Keys: [0, 0, 1, 0]\n" ] } ], - "execution_count": 80 + "execution_count": 7 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "### Confirm nKode\n", + "Submit the set key entry to render the confirm keypad." + ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.700525Z", - "start_time": "2025-03-20T10:38:44.697597Z" + "end_time": "2025-03-20T12:00:21.631111Z", + "start_time": "2025-03-20T12:00:21.391841Z" } }, "cell_type": "code", @@ -327,40 +335,71 @@ "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}\")" + "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: [ 6 7 3 4 23]\n", - "Key 1: [24 1 15 28 17]\n", - "Key 2: [12 25 27 16 11]\n", - "Key 3: [ 0 13 9 22 29]\n", - "Key 4: [18 19 21 10 5]\n", + "Key 0: [0 7 2 3 4]\n", + "Key 1: [ 6 25 26 27 28]\n", + "Key 2: [24 19 20 21 16]\n", + "Key 3: [12 13 8 9 10]\n", + "Key 4: [18 1 14 15 22]\n", "Selected Keys\n", - "[1, 3, 3, 4]\n" + "[2, 1, 4, 4]\n" ] } ], - "execution_count": 81 + "execution_count": 8 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Inferring an nKode selection" }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.947744Z", - "start_time": "2025-03-20T10:38:44.709897Z" + "end_time": "2025-03-20T12:08:59.799827Z", + "start_time": "2025-03-20T12:08:59.796887Z" } }, "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" + "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": [], - "execution_count": 82 + "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": 15 }, { "metadata": {}, @@ -380,7 +419,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.959614Z", + "end_time": "2025-03-20T12:00:21.680780Z", "start_time": "2025-03-20T10:38:44.954840Z" } }, @@ -396,7 +435,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.968863Z", + "end_time": "2025-03-20T12:00:21.683788Z", "start_time": "2025-03-20T10:38:44.966571Z" } }, @@ -421,7 +460,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:44.985451Z", + "end_time": "2025-03-20T12:00:21.684015Z", "start_time": "2025-03-20T10:38:44.983449Z" } }, @@ -441,7 +480,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.007753Z", + "end_time": "2025-03-20T12:00:21.689867Z", "start_time": "2025-03-20T10:38:45.005712Z" } }, @@ -461,7 +500,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.026885Z", + "end_time": "2025-03-20T12:00:21.690242Z", "start_time": "2025-03-20T10:38:45.024649Z" } }, @@ -481,7 +520,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.041101Z", + "end_time": "2025-03-20T12:00:21.694701Z", "start_time": "2025-03-20T10:38:45.038985Z" } }, @@ -501,7 +540,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.059433Z", + "end_time": "2025-03-20T12:00:21.696066Z", "start_time": "2025-03-20T10:38:45.056942Z" } }, @@ -544,7 +583,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.070036Z", + "end_time": "2025-03-20T12:00:21.696415Z", "start_time": "2025-03-20T10:38:45.068009Z" } }, @@ -574,7 +613,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.320480Z", + "end_time": "2025-03-20T12:00:21.696640Z", "start_time": "2025-03-20T10:38:45.087096Z" } }, @@ -603,7 +642,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.559945Z", + "end_time": "2025-03-20T12:00:21.696764Z", "start_time": "2025-03-20T10:38:45.326214Z" } }, @@ -662,7 +701,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.570140Z", + "end_time": "2025-03-20T12:00:21.696874Z", "start_time": "2025-03-20T10:38:45.567396Z" } }, @@ -706,7 +745,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:45.809575Z", + "end_time": "2025-03-20T12:00:21.697134Z", "start_time": "2025-03-20T10:38:45.576610Z" } }, @@ -732,7 +771,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:46.286081Z", + "end_time": "2025-03-20T12:00:21.697273Z", "start_time": "2025-03-20T10:38:45.816688Z" } }, @@ -780,7 +819,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:46.296985Z", + "end_time": "2025-03-20T12:00:21.697386Z", "start_time": "2025-03-20T10:38:46.293584Z" } }, @@ -814,7 +853,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:46.312031Z", + "end_time": "2025-03-20T12:00:21.697503Z", "start_time": "2025-03-20T10:38:46.310019Z" } }, @@ -841,7 +880,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T10:38:46.553807Z", + "end_time": "2025-03-20T12:00:21.697608Z", "start_time": "2025-03-20T10:38:46.318482Z" } }, -- 2.49.1 From 7b1ba996ae79e084e49f60d860323ded33ee4a12 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 20 Mar 2025 11:15:48 -0500 Subject: [PATCH 42/85] refactor dispersion_tutorial.ipynb --- notebooks/dispersion_tutorial.ipynb | 163 ++++++++------ notebooks/nkode_tutorial.ipynb | 315 ++++++++++++++-------------- src/utils.py | 14 -- 3 files changed, 263 insertions(+), 229 deletions(-) delete mode 100644 src/utils.py diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index 1c7f4b2..ad313e2 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -1,36 +1,71 @@ { "cells": [ - { - "cell_type": "markdown", - "source": [ - "## Dispersion" - ], - "metadata": { - "collapsed": false - } - }, { "cell_type": "code", "source": [ "from src.user_keypad import UserKeypad\n", - "from src.utils import random_property_rotation\n", "from IPython.display import Markdown, display\n", "from src.models import KeypadSize\n", "import numpy as np\n", "\n", + "def random_property_rotation(\n", + " user_keypad: np.ndarray,\n", + " prop_rotation: list[int]\n", + ") -> np.ndarray:\n", + " transposed = user_keypad.T\n", + " if len(prop_rotation) != len(transposed):\n", + " raise ValueError(\"prop_rotation must be the same length as the number of properties\")\n", + " for idx, prop_set in enumerate(transposed):\n", + " rotation = prop_rotation[idx]\n", + " rotation = rotation % len(prop_set) if len(prop_set) > 0 else 0\n", + " transposed[idx] = np.roll(prop_set, rotation)\n", + " return transposed.T\n", + "\n", "def keypad_md_table(keypad_list: np.ndarray, keypad_size: KeypadSize) -> str:\n", " assert (keypad_size.total_props == len(keypad_list))\n", " keypad = keypad_list.reshape(-1, keypad_size.props_per_key)\n", - " table = \"|key|\" + \"\".join([f\"set{idx}|\" for idx in range(keypad_size.props_per_key)])\n", + " table = \"||\" + \"\".join([f\"position {idx}|\" for idx in range(keypad_size.props_per_key)])\n", " table += \"\\n|\" + \"\".join(\"-|\" for _ in range(keypad_size.props_per_key + 1))\n", "\n", " for key in range(keypad_size.numb_of_keys):\n", - " table += f\"\\n|key{key+1}|\"\n", + " table += f\"\\n|key {key}|\"\n", " table += \"|\".join([str(prop) for prop in keypad[key]])\n", " table += \"|\"\n", - " return table\n", + " return table" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2025-03-20T16:15:24.214098Z", + "start_time": "2025-03-20T16:15:24.207220Z" + } + }, + "outputs": [], + "execution_count": 80 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Keypad Dispersion\n", "\n", + "Keypad dispersion refers to an operation that redistributes the properties assigned to each key on a keypad, ensuring that no property shares a key with a property that was previously adjacent to it.\n", + "Keypads are only dispersable if `numb_of_keys <= properites_per_key`\n", "\n", + "A keypad dispersion is completed in two steps:\n", + "1. Create a property rotation array; a randomly permuted subset of indices, selected without replacement from a range equal to the number of keys on a keypad, with its length truncated to match the number of properties assigned per key.\n", + "2. Rotate each position, similar to a ring or combination lock, by a distance equal to its corresponding value in the property rotation array." + ] + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-20T16:15:24.225459Z", + "start_time": "2025-03-20T16:15:24.220286Z" + } + }, + "cell_type": "code", + "source": [ "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", "props = [1, 10, 11, 100]\n", "keypad = []\n", @@ -38,90 +73,98 @@ " keypad.extend([key_numb * prop for prop in props])\n", "\n", "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad))\n", - "\n", + "display(Markdown(f\"\"\"\n", + "## Example Keypad\n", + "{keypad_size.numb_of_keys} X {keypad_size.props_per_key} keypad ({keypad_size.numb_of_keys} keys, {keypad_size.props_per_key} properties per key).\n", + "\"\"\"))\n", "display(Markdown(keypad_md_table(demo_interface.keypad, keypad_size)))\n" ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-14T14:34:55.702615Z", - "start_time": "2025-03-14T14:34:55.694963Z" - } - }, "outputs": [ { "data": { "text/plain": [ "" ], - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|1|10|11|100|\n|key2|2|20|22|200|\n|key3|3|30|33|300|\n|key4|4|40|44|400|\n|key5|5|50|55|500|" + "text/markdown": "\n## Example Keypad\n5 X 4 keypad (5 keys, 4 properties per key).\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|1|10|11|100|\n|key 1|2|20|22|200|\n|key 2|3|30|33|300|\n|key 3|4|40|44|400|\n|key 4|5|50|55|500|" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 4 + "execution_count": 81 }, { - "cell_type": "code", - "source": [ - "demo_interface_matrix = demo_interface.keypad.reshape(-1, demo_interface.keypad_size.props_per_key)\n", - "shuffled_keys = np.random.permutation(demo_interface_matrix)\n", - "shuffled_keys_list = shuffled_keys.reshape(-1)\n", - "display(Markdown(keypad_md_table(shuffled_keys_list, keypad_size)))" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-14T14:34:55.718161Z", - "start_time": "2025-03-14T14:34:55.714512Z" - } - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ], - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|1|10|11|100|\n|key2|4|40|44|400|\n|key3|3|30|33|300|\n|key4|2|20|22|200|\n|key5|5|50|55|500|" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "execution_count": 5 + "metadata": {}, + "cell_type": "markdown", + "source": "### Create Property Rotation Array" }, { "cell_type": "code", "source": [ "prop_rotation = np.random.choice(range(keypad_size.numb_of_keys), size=keypad_size.props_per_key, replace=False)\n", - "dispersed_interface = random_property_rotation(\n", - " shuffled_keys,\n", - " prop_rotation\n", - ")\n", - "\n", - "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))\n" + "print(f\"Property Rotation: {prop_rotation}\")\n" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-14T14:34:55.731332Z", - "start_time": "2025-03-14T14:34:55.728135Z" + "end_time": "2025-03-20T16:15:24.240606Z", + "start_time": "2025-03-20T16:15:24.237773Z" } }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Property Rotation: [4 3 2 0]\n" + ] + } + ], + "execution_count": 82 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "### Apply the Rotation" + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-20T16:15:24.256086Z", + "start_time": "2025-03-20T16:15:24.253226Z" + } + }, + "cell_type": "code", + "source": [ + "dispersed_interface = random_property_rotation(\n", + " demo_interface.keypad.reshape(-1, keypad_size.props_per_key),\n", + " prop_rotation\n", + ")\n", + "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))" + ], "outputs": [ { "data": { "text/plain": [ "" ], - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|5|30|44|100|\n|key2|1|20|33|400|\n|key3|4|50|22|300|\n|key4|3|10|55|200|\n|key5|2|40|11|500|" + "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|2|30|44|100|\n|key 1|3|40|55|200|\n|key 2|4|50|11|300|\n|key 3|5|10|22|400|\n|key 4|1|20|33|500|" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 6 + "execution_count": 83 } ], "metadata": { diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index 4557afa..7960aab 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -30,12 +30,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.292663Z", - "start_time": "2025-03-20T12:00:21.253426Z" + "end_time": "2025-03-20T15:13:44.080558Z", + "start_time": "2025-03-20T15:13:44.076372Z" } }, "outputs": [], - "execution_count": 2 + "execution_count": 41 }, { "cell_type": "code", @@ -53,12 +53,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.298357Z", - "start_time": "2025-03-20T12:00:21.296258Z" + "end_time": "2025-03-20T15:13:44.090939Z", + "start_time": "2025-03-20T15:13:44.088281Z" } }, "outputs": [], - "execution_count": 3 + "execution_count": 42 }, { "metadata": {}, @@ -112,8 +112,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.317747Z", - "start_time": "2025-03-20T12:00:21.306163Z" + "end_time": "2025-03-20T15:13:44.107060Z", + "start_time": "2025-03-20T15:13:44.100773Z" } }, "outputs": [ @@ -121,30 +121,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Position Key: [37938 42486 36680 47679 5944 46439]\n", + "Customer Position Key: [22504 45127 46010 30773 46509 63084]\n", "Customer Properties Key:\n", - "[14469 12272 54942 53713 60529 64319]\n", - "[49129 65121 60219 33860 48996 57925]\n", - "[61464 3328 59612 33957 62738 42039]\n", - "[ 3074 46306 41956 4712 59250 7730]\n", - "[23724 39640 530 27472 33903 26824]\n", + "[38076 5253 43656 24035 57980 18197]\n", + "[56754 21362 27799 42374 57145 46164]\n", + "[56031 10476 13741 23847 57450 42577]\n", + "[63205 55476 58864 29925 9463 58182]\n", + "[61997 24583 9269 19956 55576 32892]\n", "Position to Properties Map:\n", - "37938: [14469 49129 61464 3074 23724]\n", - "42486: [12272 65121 3328 46306 39640]\n", - "36680: [54942 60219 59612 41956 530]\n", - "47679: [53713 33860 33957 4712 27472]\n", - "5944: [60529 48996 62738 59250 33903]\n", - "46439: [64319 57925 42039 7730 26824]\n" + "22504: [38076 56754 56031 63205 61997]\n", + "45127: [ 5253 21362 10476 55476 24583]\n", + "46010: [43656 27799 13741 58864 9269]\n", + "30773: [24035 42374 23847 29925 19956]\n", + "46509: [57980 57145 57450 9463 55576]\n", + "63084: [18197 46164 42577 58182 32892]\n" ] } ], - "execution_count": 4 + "execution_count": 43 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.325478Z", - "start_time": "2025-03-20T12:00:21.322970Z" + "end_time": "2025-03-20T15:13:44.127687Z", + "start_time": "2025-03-20T15:13:44.125028Z" } }, "cell_type": "code", @@ -161,16 +161,16 @@ "output_type": "stream", "text": [ "Position Value to Icons Map:\n", - "37938: ['😀' '🥺' '🤔' '🐱' '🦄']\n", - "42486: ['😂' '😡' '🙃' '🐶' '🌟']\n", - "36680: ['🥳' '😱' '😇' '🦁' '⚡']\n", - "47679: ['😍' '🤯' '🤖' '🐻' '🔥']\n", - "5944: ['🤓' '🥰' '👽' '🐸' '🍕']\n", - "46439: ['😎' '😴' '👾' '🐙' '🎉']\n" + "22504: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "45127: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "46010: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "30773: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "46509: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "63084: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], - "execution_count": 5 + "execution_count": 44 }, { "metadata": {}, @@ -194,8 +194,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.349267Z", - "start_time": "2025-03-20T12:00:21.342655Z" + "end_time": "2025-03-20T15:13:44.156638Z", + "start_time": "2025-03-20T15:13:44.150222Z" } }, "cell_type": "code", @@ -223,11 +223,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: ['🐱' '🐶' '😱' '😍' '🍕']\n", - "Key 1: ['🦄' '😡' '⚡' '🤯' '🐸']\n", - "Key 2: ['😀' '🙃' '😇' '🔥' '👽']\n", - "Key 3: ['🥺' '😂' '🥳' '🐻' '🥰']\n", - "Key 4: ['🤔' '🌟' '🦁' '🤖' '🤓']\n" + "Key 0: ['🦄' '🦁' '🐻' '🤓' '👾']\n", + "Key 1: ['🥺' '⚡' '🔥' '🍕' '🎉']\n", + "Key 2: ['🐱' '😱' '🤖' '👽' '🐙']\n", + "Key 3: ['😀' '🥳' '😍' '🐸' '😴']\n", + "Key 4: ['🤔' '😇' '🤯' '🥰' '😎']\n" ] }, { @@ -244,11 +244,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [18 19 8 3 28]\n", - "Key 1: [24 7 26 9 22]\n", - "Key 2: [ 0 13 14 27 16]\n", - "Key 3: [ 6 1 2 21 10]\n", - "Key 4: [12 25 20 15 4]\n" + "Key 0: [24 20 21 4 17]\n", + "Key 1: [ 6 26 27 28 29]\n", + "Key 2: [18 8 15 16 23]\n", + "Key 3: [ 0 2 3 22 11]\n", + "Key 4: [12 14 9 10 5]\n" ] }, { @@ -265,15 +265,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [ 3074 46306 60219 53713 33903]\n", - "Key 1: [23724 65121 530 33860 59250]\n", - "Key 2: [14469 3328 59612 27472 62738]\n", - "Key 3: [49129 12272 54942 4712 48996]\n", - "Key 4: [61464 39640 41956 33957 60529]\n" + "Key 0: [61997 58864 29925 57980 42577]\n", + "Key 1: [56754 9269 19956 55576 32892]\n", + "Key 2: [63205 27799 23847 57450 58182]\n", + "Key 3: [38076 43656 24035 9463 46164]\n", + "Key 4: [56031 13741 42374 57145 18197]\n" ] } ], - "execution_count": 6 + "execution_count": 45 }, { "metadata": {}, @@ -286,8 +286,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.372390Z", - "start_time": "2025-03-20T12:00:21.369564Z" + "end_time": "2025-03-20T15:13:44.190448Z", + "start_time": "2025-03-20T15:13:44.187214Z" } }, "cell_type": "code", @@ -306,14 +306,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Passcode Indices: [19, 28, 22, 18]\n", - "User Passcode Icons: ['🐶' '🍕' '🐸' '🐱']\n", - "User Passcode Server-side properties: [46306 33903 59250 3074]\n", - "Selected Keys: [0, 0, 1, 0]\n" + "User Passcode Indices: [4, 9, 5, 18]\n", + "User Passcode Icons: ['🤓' '🤯' '😎' '🐱']\n", + "User Passcode Server-side properties: [57980 42374 18197 63205]\n", + "Selected Keys: [0, 4, 4, 2]\n" ] } ], - "execution_count": 7 + "execution_count": 46 }, { "metadata": {}, @@ -326,8 +326,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.631111Z", - "start_time": "2025-03-20T12:00:21.391841Z" + "end_time": "2025-03-20T15:13:44.516570Z", + "start_time": "2025-03-20T15:13:44.208909Z" } }, "cell_type": "code", @@ -344,17 +344,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [0 7 2 3 4]\n", - "Key 1: [ 6 25 26 27 28]\n", - "Key 2: [24 19 20 21 16]\n", - "Key 3: [12 13 8 9 10]\n", - "Key 4: [18 1 14 15 22]\n", + "Key 0: [18 20 27 10 11]\n", + "Key 1: [24 14 15 22 29]\n", + "Key 2: [12 2 21 28 23]\n", + "Key 3: [ 0 26 9 16 17]\n", + "Key 4: [6 8 3 4 5]\n", "Selected Keys\n", - "[2, 1, 4, 4]\n" + "[4, 3, 4, 0]\n" ] } ], - "execution_count": 8 + "execution_count": 47 }, { "metadata": {}, @@ -364,8 +364,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:08:59.799827Z", - "start_time": "2025-03-20T12:08:59.796887Z" + "end_time": "2025-03-20T15:13:44.527802Z", + "start_time": "2025-03-20T15:13:44.524844Z" } }, "cell_type": "code", @@ -384,22 +384,22 @@ "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", + "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": 15 + "execution_count": 48 }, { "metadata": {}, @@ -419,8 +419,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.680780Z", - "start_time": "2025-03-20T10:38:44.954840Z" + "end_time": "2025-03-20T15:13:44.546871Z", + "start_time": "2025-03-20T15:13:44.540903Z" } }, "cell_type": "code", @@ -430,13 +430,13 @@ "user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)" ], "outputs": [], - "execution_count": 83 + "execution_count": 49 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.683788Z", - "start_time": "2025-03-20T10:38:44.966571Z" + "end_time": "2025-03-20T15:13:44.565168Z", + "start_time": "2025-03-20T15:13:44.562664Z" } }, "cell_type": "code", @@ -447,21 +447,21 @@ "output_type": "stream", "text": [ "Property Key:\n", - "[[53739 14349 64971 50952 51008 37461]\n", - " [45744 17444 29958 27397 60694 8069]\n", - " [43064 47943 49025 5623 3145 4890]\n", - " [ 1128 11012 53093 14232 26108 5391]\n", - " [59341 47378 48497 6840 34437 29587]]\n" + "[[21364 17750 43270 40570 2897 64106]\n", + " [64901 60553 23487 56910 22974 27412]\n", + " [49276 36687 58910 60854 56432 64908]\n", + " [17816 7963 33663 13564 43318 39697]\n", + " [ 6300 40793 34908 48633 47026 16580]]\n" ] } ], - "execution_count": 84 + "execution_count": 50 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.684015Z", - "start_time": "2025-03-20T10:38:44.983449Z" + "end_time": "2025-03-20T15:13:44.583641Z", + "start_time": "2025-03-20T15:13:44.581757Z" } }, "cell_type": "code", @@ -471,17 +471,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Key: [16245 48001 4534 55258 54613 15211 33171 56565 33961 50654]\n" + "Passcode Key: [65482 44549 30799 45221 44404 5844 32654 14244 48015 17243]\n" ] } ], - "execution_count": 85 + "execution_count": 51 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.689867Z", - "start_time": "2025-03-20T10:38:45.005712Z" + "end_time": "2025-03-20T15:13:44.624946Z", + "start_time": "2025-03-20T15:13:44.622774Z" } }, "cell_type": "code", @@ -491,17 +491,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mask Key: [52084 24514 63626 6657 19669 39430 35626 25229 14824 63798]\n" + "Mask Key: [36463 57168 30740 46459 21013 40282 33046 14986 26644 13]\n" ] } ], - "execution_count": 86 + "execution_count": 52 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.690242Z", - "start_time": "2025-03-20T10:38:45.024649Z" + "end_time": "2025-03-20T15:13:44.653291Z", + "start_time": "2025-03-20T15:13:44.651215Z" } }, "cell_type": "code", @@ -511,17 +511,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Combined Position Key: [10235 12456 898 54650 50445 20719]\n" + "Combined Position Key: [32765 21433 32527 38517 27021 45380]\n" ] } ], - "execution_count": 87 + "execution_count": 53 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.694701Z", - "start_time": "2025-03-20T10:38:45.038985Z" + "end_time": "2025-03-20T15:13:44.682695Z", + "start_time": "2025-03-20T15:13:44.680482Z" } }, "cell_type": "code", @@ -531,17 +531,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Position Key = combined_pos_key XOR customer_pos_key: [ 3444 19636 47437 1440 7882 36903]\n" + "User Position Key = combined_pos_key XOR customer_pos_key: [10261 58366 52405 60992 56352 18216]\n" ] } ], - "execution_count": 88 + "execution_count": 54 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.696066Z", - "start_time": "2025-03-20T10:38:45.056942Z" + "end_time": "2025-03-20T15:13:44.713223Z", + "start_time": "2025-03-20T15:13:44.710535Z" } }, "cell_type": "code", @@ -557,16 +557,16 @@ "output_type": "stream", "text": [ "Combined Position to Properties Map:\n", - "10235: [53739 45744 43064 1128 59341]\n", - "12456: [14349 17444 47943 11012 47378]\n", - "898: [64971 29958 49025 53093 48497]\n", - "54650: [50952 27397 5623 14232 6840]\n", - "50445: [51008 60694 3145 26108 34437]\n", - "20719: [37461 8069 4890 5391 29587]\n" + "32765: [21364 64901 49276 17816 6300]\n", + "21433: [17750 60553 36687 7963 40793]\n", + "32527: [43270 23487 58910 33663 34908]\n", + "38517: [40570 56910 60854 13564 48633]\n", + "27021: [ 2897 22974 56432 43318 47026]\n", + "45380: [64106 27412 64908 39697 16580]\n" ] } ], - "execution_count": 89 + "execution_count": 55 }, { "metadata": {}, @@ -583,8 +583,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.696415Z", - "start_time": "2025-03-20T10:38:45.068009Z" + "end_time": "2025-03-20T15:13:44.732450Z", + "start_time": "2025-03-20T15:13:44.729920Z" } }, "cell_type": "code", @@ -596,7 +596,7 @@ "encoded_mask = user_cipher.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 90 + "execution_count": 56 }, { "metadata": {}, @@ -613,8 +613,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.696640Z", - "start_time": "2025-03-20T10:38:45.087096Z" + "end_time": "2025-03-20T15:13:45.055535Z", + "start_time": "2025-03-20T15:13:44.751147Z" } }, "cell_type": "code", @@ -628,7 +628,7 @@ "passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode(\"utf-8\")" ], "outputs": [], - "execution_count": 91 + "execution_count": 57 }, { "metadata": {}, @@ -642,8 +642,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.696764Z", - "start_time": "2025-03-20T10:38:45.326214Z" + "end_time": "2025-03-20T15:13:45.367717Z", + "start_time": "2025-03-20T15:13:45.061644Z" } }, "cell_type": "code", @@ -661,20 +661,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [18 25 14 15 4 29]\n", - "Key 1: [ 6 13 20 27 28 5]\n", - "Key 2: [24 19 26 9 16 23]\n", - "Key 3: [ 0 1 8 3 10 11]\n", - "Key 4: [12 7 2 21 22 17]\n", - "User Passcode: [28, 13, 9, 10]\n", + "Key 0: [24 13 20 21 4 17]\n", + "Key 1: [ 6 1 26 27 28 29]\n", + "Key 2: [18 7 8 15 16 23]\n", + "Key 3: [ 0 19 2 3 22 11]\n", + "Key 4: [12 25 14 9 10 5]\n", + "User Passcode: [4, 9, 5, 18]\n", "\n", "Selected Keys:\n", - " [1, 1, 2, 3]\n", + " [0, 4, 4, 2]\n", "\n" ] } ], - "execution_count": 92 + "execution_count": 58 }, { "metadata": {}, @@ -701,8 +701,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.696874Z", - "start_time": "2025-03-20T10:38:45.567396Z" + "end_time": "2025-03-20T15:13:45.379898Z", + "start_time": "2025-03-20T15:13:45.377353Z" } }, "cell_type": "code", @@ -712,10 +712,10 @@ "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\n" + "user_position_key = customer.cipher.position_key ^ user.cipher.combined_position_key" ], "outputs": [], - "execution_count": 93 + "execution_count": 59 }, { "metadata": {}, @@ -727,15 +727,20 @@ ] }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-20T15:13:45.391901Z", + "start_time": "2025-03-20T15:13:45.389499Z" + } + }, "cell_type": "code", - "outputs": [], - "execution_count": null, "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": 60 }, { "metadata": {}, @@ -745,8 +750,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.697134Z", - "start_time": "2025-03-20T10:38:45.576610Z" + "end_time": "2025-03-20T15:13:45.711702Z", + "start_time": "2025-03-20T15:13:45.407474Z" } }, "cell_type": "code", @@ -755,7 +760,7 @@ "assert valid_nkode" ], "outputs": [], - "execution_count": 94 + "execution_count": 61 }, { "metadata": {}, @@ -771,8 +776,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.697273Z", - "start_time": "2025-03-20T10:38:45.816688Z" + "end_time": "2025-03-20T15:13:46.331470Z", + "start_time": "2025-03-20T15:13:45.715778Z" } }, "cell_type": "code", @@ -798,15 +803,15 @@ "output_type": "stream", "text": [ "Old User Cipher and Mask\n", - "mask: y8aMd+5qYcPVLETTcs2aHqx2hhk=, code: $2b$12$wMi7WGmlch8kWMYJ2v.FHOne1.YSQPqKU/itpBuycwSFyasryF/2u\n", + "mask: xetVN/8taK1lhtrDjt4w0M4pprQ=, code: $2b$12$6JzDrPs.dAb0iOIvm8afKuwf.Z8qKtg89Nnhx..tlBOD5y1MYMR4y\n", "\n", "New User Cipher and Mask\n", - "mask: aln5Su79utoVmqQXNCjYiUdwVYw=, code: $2b$12$gQf3UVa3cWMBy0CO0sBLyuJzdXGzg3qpNFMTD6MvycuR6N3gLFdgC\n", + "mask: WsKTIcZVngijEKlMfoF2UG5Rz9I=, code: $2b$12$jvQ..z4tPFII5dLXP0D2LOPrypDSB7yoRH6E0SZPO/yIIcZVtgCTS\n", "\n" ] } ], - "execution_count": 95 + "execution_count": 62 }, { "metadata": {}, @@ -819,8 +824,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.697386Z", - "start_time": "2025-03-20T10:38:46.293584Z" + "end_time": "2025-03-20T15:13:46.344092Z", + "start_time": "2025-03-20T15:13:46.339802Z" } }, "cell_type": "code", @@ -833,7 +838,7 @@ "new_pos = customer.cipher.position_key" ], "outputs": [], - "execution_count": 96 + "execution_count": 63 }, { "metadata": {}, @@ -853,8 +858,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.697503Z", - "start_time": "2025-03-20T10:38:46.310019Z" + "end_time": "2025-03-20T15:13:46.361420Z", + "start_time": "2025-03-20T15:13:46.359046Z" } }, "cell_type": "code", @@ -867,7 +872,7 @@ " user.cipher.property_key = user.cipher.property_key ^ props_xor" ], "outputs": [], - "execution_count": 97 + "execution_count": 64 }, { "metadata": {}, @@ -880,8 +885,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T12:00:21.697608Z", - "start_time": "2025-03-20T10:38:46.318482Z" + "end_time": "2025-03-20T15:13:46.679562Z", + "start_time": "2025-03-20T15:13:46.371777Z" } }, "cell_type": "code", @@ -896,7 +901,7 @@ " user.renew = False" ], "outputs": [], - "execution_count": 98 + "execution_count": 65 } ], "metadata": { diff --git a/src/utils.py b/src/utils.py deleted file mode 100644 index bfb68da..0000000 --- a/src/utils.py +++ /dev/null @@ -1,14 +0,0 @@ -import numpy as np - -def random_property_rotation( - user_keypad: np.ndarray, - prop_rotation: list[int] -) -> np.ndarray: - transposed = user_keypad.T - if len(prop_rotation) != len(transposed): - raise ValueError("prop_rotation must be the same length as the number of properties") - for idx, prop_set in enumerate(transposed): - rotation = prop_rotation[idx] - rotation = rotation % len(prop_set) if len(prop_set) > 0 else 0 - transposed[idx] = np.roll(prop_set, rotation) - return transposed.T -- 2.49.1 From d6c5e56a30a1e324ca4166566e1157f594f218ef Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 20 Mar 2025 11:18:16 -0500 Subject: [PATCH 43/85] add doc --- notebooks/dispersion_tutorial.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index ad313e2..315dd21 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -50,7 +50,7 @@ "## Keypad Dispersion\n", "\n", "Keypad dispersion refers to an operation that redistributes the properties assigned to each key on a keypad, ensuring that no property shares a key with a property that was previously adjacent to it.\n", - "Keypads are only dispersable if `numb_of_keys <= properites_per_key`\n", + "Keypads are only dispersable if `numb_of_keys <= properites_per_key`. It's used during nKode enrollment to infer property selection.\n", "\n", "A keypad dispersion is completed in two steps:\n", "1. Create a property rotation array; a randomly permuted subset of indices, selected without replacement from a range equal to the number of keys on a keypad, with its length truncated to match the number of properties assigned per key.\n", -- 2.49.1 From 192ce80598f1f66334af4471778b2d805905e88e Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 21 Mar 2025 04:38:49 -0500 Subject: [PATCH 44/85] accidentally removed important function in utils --- notebooks/dispersion_tutorial.ipynb | 68 ++++++----------- notebooks/split_shuffle_tutorial.ipynb | 100 +++++++++++++++++++++++++ src/user_keypad.py | 16 ++-- src/utils.py | 30 ++++++++ 4 files changed, 159 insertions(+), 55 deletions(-) create mode 100644 notebooks/split_shuffle_tutorial.ipynb create mode 100644 src/utils.py diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/dispersion_tutorial.ipynb index 315dd21..a7d59eb 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/dispersion_tutorial.ipynb @@ -6,42 +6,18 @@ "from src.user_keypad import UserKeypad\n", "from IPython.display import Markdown, display\n", "from src.models import KeypadSize\n", - "import numpy as np\n", - "\n", - "def random_property_rotation(\n", - " user_keypad: np.ndarray,\n", - " prop_rotation: list[int]\n", - ") -> np.ndarray:\n", - " transposed = user_keypad.T\n", - " if len(prop_rotation) != len(transposed):\n", - " raise ValueError(\"prop_rotation must be the same length as the number of properties\")\n", - " for idx, prop_set in enumerate(transposed):\n", - " rotation = prop_rotation[idx]\n", - " rotation = rotation % len(prop_set) if len(prop_set) > 0 else 0\n", - " transposed[idx] = np.roll(prop_set, rotation)\n", - " return transposed.T\n", - "\n", - "def keypad_md_table(keypad_list: np.ndarray, keypad_size: KeypadSize) -> str:\n", - " assert (keypad_size.total_props == len(keypad_list))\n", - " keypad = keypad_list.reshape(-1, keypad_size.props_per_key)\n", - " table = \"||\" + \"\".join([f\"position {idx}|\" for idx in range(keypad_size.props_per_key)])\n", - " table += \"\\n|\" + \"\".join(\"-|\" for _ in range(keypad_size.props_per_key + 1))\n", - "\n", - " for key in range(keypad_size.numb_of_keys):\n", - " table += f\"\\n|key {key}|\"\n", - " table += \"|\".join([str(prop) for prop in keypad[key]])\n", - " table += \"|\"\n", - " return table" + "from src.utils import random_property_rotation, keypad_md_table\n", + "import numpy as np" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T16:15:24.214098Z", - "start_time": "2025-03-20T16:15:24.207220Z" + "end_time": "2025-03-21T09:28:57.936307Z", + "start_time": "2025-03-21T09:28:57.933359Z" } }, "outputs": [], - "execution_count": 80 + "execution_count": 5 }, { "metadata": {}, @@ -60,24 +36,24 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T16:15:24.225459Z", - "start_time": "2025-03-20T16:15:24.220286Z" + "end_time": "2025-03-21T09:28:57.950591Z", + "start_time": "2025-03-21T09:28:57.945584Z" } }, "cell_type": "code", "source": [ "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", "props = [1, 10, 11, 100]\n", - "keypad = []\n", + "keypad_list = []\n", "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", - " keypad.extend([key_numb * prop for prop in props])\n", + " keypad_list.extend([key_numb * prop for prop in props])\n", "\n", - "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad))\n", + "user_keypad = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad_list))\n", "display(Markdown(f\"\"\"\n", "## Example Keypad\n", "{keypad_size.numb_of_keys} X {keypad_size.props_per_key} keypad ({keypad_size.numb_of_keys} keys, {keypad_size.props_per_key} properties per key).\n", "\"\"\"))\n", - "display(Markdown(keypad_md_table(demo_interface.keypad, keypad_size)))\n" + "display(Markdown(keypad_md_table(user_keypad.keypad, keypad_size)))\n" ], "outputs": [ { @@ -101,7 +77,7 @@ "output_type": "display_data" } ], - "execution_count": 81 + "execution_count": 6 }, { "metadata": {}, @@ -111,14 +87,14 @@ { "cell_type": "code", "source": [ - "prop_rotation = np.random.choice(range(keypad_size.numb_of_keys), size=keypad_size.props_per_key, replace=False)\n", + "prop_rotation = np.random.choice(keypad_size.numb_of_keys, size=keypad_size.props_per_key, replace=False)\n", "print(f\"Property Rotation: {prop_rotation}\")\n" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T16:15:24.240606Z", - "start_time": "2025-03-20T16:15:24.237773Z" + "end_time": "2025-03-21T09:28:57.961890Z", + "start_time": "2025-03-21T09:28:57.959507Z" } }, "outputs": [ @@ -126,11 +102,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Property Rotation: [4 3 2 0]\n" + "Property Rotation: [0 3 1 2]\n" ] } ], - "execution_count": 82 + "execution_count": 7 }, { "metadata": {}, @@ -140,14 +116,14 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T16:15:24.256086Z", - "start_time": "2025-03-20T16:15:24.253226Z" + "end_time": "2025-03-21T09:28:57.976227Z", + "start_time": "2025-03-21T09:28:57.973183Z" } }, "cell_type": "code", "source": [ "dispersed_interface = random_property_rotation(\n", - " demo_interface.keypad.reshape(-1, keypad_size.props_per_key),\n", + " user_keypad.keypad_matrix(),\n", " prop_rotation\n", ")\n", "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))" @@ -158,13 +134,13 @@ "text/plain": [ "" ], - "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|2|30|44|100|\n|key 1|3|40|55|200|\n|key 2|4|50|11|300|\n|key 3|5|10|22|400|\n|key 4|1|20|33|500|" + "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|1|30|55|400|\n|key 1|2|40|11|500|\n|key 2|3|50|22|100|\n|key 3|4|10|33|200|\n|key 4|5|20|44|300|" }, "metadata": {}, "output_type": "display_data" } ], - "execution_count": 83 + "execution_count": 8 } ], "metadata": { diff --git a/notebooks/split_shuffle_tutorial.ipynb b/notebooks/split_shuffle_tutorial.ipynb new file mode 100644 index 0000000..79dbd53 --- /dev/null +++ b/notebooks/split_shuffle_tutorial.ipynb @@ -0,0 +1,100 @@ +{ + "cells": [ + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-21T09:22:33.939365Z", + "start_time": "2025-03-21T09:22:33.911673Z" + } + }, + "cell_type": "code", + "source": [ + "from src.models import KeypadSize\n", + "from src.user_keypad import UserKeypad\n", + "from src.utils import keypad_md_table\n", + "import numpy as np\n", + "from IPython.display import Markdown, display" + ], + "id": "1f073371d04d02ef", + "outputs": [], + "execution_count": 1 + }, + { + "metadata": { + "collapsed": true, + "ExecuteTime": { + "end_time": "2025-03-21T09:22:33.948436Z", + "start_time": "2025-03-21T09:22:33.943046Z" + } + }, + "cell_type": "code", + "source": [ + "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", + "props = [1, 10, 11, 100]\n", + "keypad = []\n", + "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", + " keypad.extend([key_numb * prop for prop in props])\n", + "\n", + "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad))\n", + "display(Markdown(f\"\"\"\n", + "## Example Keypad\n", + "{keypad_size.numb_of_keys} X {keypad_size.props_per_key} keypad ({keypad_size.numb_of_keys} keys, {keypad_size.props_per_key} properties per key).\n", + "\"\"\"))\n", + "display(Markdown(keypad_md_table(demo_interface.keypad, keypad_size)))" + ], + "id": "initial_id", + "outputs": [ + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "\n## Example Keypad\n5 X 4 keypad (5 keys, 4 properties per key).\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|1|10|11|100|\n|key 1|2|20|22|200|\n|key 2|3|30|33|300|\n|key 3|4|40|44|400|\n|key 4|5|50|55|500|" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 2 + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "keypad_mat = demo_interface", + "id": "43db2b9d247f420d" + } + ], + "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": 5 +} diff --git a/src/user_keypad.py b/src/user_keypad.py index 80efd27..9fe36f3 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -46,13 +46,11 @@ class UserKeypad: self.keypad = set_view.T.reshape(-1) def disperse_keypad(self): - # TODO: clean this up if not self.keypad_size.is_dispersable: raise ValueError("Keypad size is not dispersable") - rng = np.random.default_rng() - user_keypad_matrix = self.keypad_matrix() - shuffled_keys = rng.permutation(user_keypad_matrix, axis=0) - prop_rotation = rng.permutation(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.props_per_key] + keypad_mat = self.keypad_matrix() + shuffled_keys = np.random.permutation(keypad_mat) + prop_rotation = np.random.choice(self.keypad_size.numb_of_keys, size=self.keypad_size.props_per_key, replace=False) dispersed_keypad = random_property_rotation( shuffled_keys, prop_rotation.tolist(), @@ -61,14 +59,14 @@ class UserKeypad: def split_shuffle(self): # shuffle all keys - keypad_view = self.keypad_matrix() - np.random.shuffle(keypad_view) + keypad_mat = self.keypad_matrix() + np.random.shuffle(keypad_mat) # select half the property sets prop_permutation = np.random.permutation(self.keypad_size.props_per_key)[: self.keypad_size.props_per_key // 2] key_permutation = np.random.permutation(self.keypad_size.numb_of_keys) # shuffle the selected property sets to new keys as a group - keypad_view[:, prop_permutation] = keypad_view[key_permutation, :][:, prop_permutation] - self.keypad = keypad_view.reshape(-1) + keypad_mat[:, prop_permutation] = keypad_mat[key_permutation, :][:, prop_permutation] + self.keypad = keypad_mat.reshape(-1) def property_adjacency_graph(self) -> dict[int, set[int]]: user_keypad_keypad = self.keypad_matrix() diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..40b1593 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,30 @@ +import numpy as np + +from src.models import KeypadSize + + +def random_property_rotation( + user_keypad: np.ndarray, + prop_rotation: list[int] +) -> np.ndarray: + transposed = user_keypad.T + if len(prop_rotation) != len(transposed): + raise ValueError("prop_rotation must be the same length as the number of properties") + for idx, prop_set in enumerate(transposed): + rotation = prop_rotation[idx] + rotation = rotation % len(prop_set) if len(prop_set) > 0 else 0 + transposed[idx] = np.roll(prop_set, rotation) + return transposed.T + + +def keypad_md_table(keypad_list: np.ndarray, keypad_size: KeypadSize) -> str: + assert (keypad_size.total_props == len(keypad_list)) + keypad = keypad_list.reshape(-1, keypad_size.props_per_key) + table = "||" + "".join([f"position {idx}|" for idx in range(keypad_size.props_per_key)]) + table += "\n|" + "".join("-|" for _ in range(keypad_size.props_per_key + 1)) + + for key in range(keypad_size.numb_of_keys): + table += f"\n|key {key}|" + table += "|".join([str(prop) for prop in keypad[key]]) + table += "|" + return table -- 2.49.1 From ad5fc0e6957e8abdef536b8f6820f4641f7ed99e Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 21 Mar 2025 05:15:52 -0500 Subject: [PATCH 45/85] add docs to split_shuffle_tutorial.ipynb --- notebooks/split_shuffle_tutorial.ipynb | 132 ++++++++++++++++++++++--- src/user_keypad.py | 2 +- 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/notebooks/split_shuffle_tutorial.ipynb b/notebooks/split_shuffle_tutorial.ipynb index 79dbd53..84578ef 100644 --- a/notebooks/split_shuffle_tutorial.ipynb +++ b/notebooks/split_shuffle_tutorial.ipynb @@ -3,8 +3,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-21T09:22:33.939365Z", - "start_time": "2025-03-21T09:22:33.911673Z" + "end_time": "2025-03-21T10:15:10.166756Z", + "start_time": "2025-03-21T10:15:10.163120Z" } }, "cell_type": "code", @@ -17,30 +17,44 @@ ], "id": "1f073371d04d02ef", "outputs": [], - "execution_count": 1 + "execution_count": 37 + }, + { + "metadata": {}, + "cell_type": "markdown", + "source": [ + "## Keypad Split Shuffle\n", + "\n", + "The split shuffle algorithm aims to increase the number of observations required to decipher an nKode.\n", + "For more details, refer to the [Evil nKode](https://github.com/Arcanum-Technology/evil-nkode).\n", + "\n", + "The positions of the keypad properties are divided into two sets, with each set being shuffled collectively to form a new key." + ], + "id": "651d7d661f4128d" }, { "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2025-03-21T09:22:33.948436Z", - "start_time": "2025-03-21T09:22:33.943046Z" + "end_time": "2025-03-21T10:15:10.177990Z", + "start_time": "2025-03-21T10:15:10.172547Z" } }, "cell_type": "code", "source": [ "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", "props = [1, 10, 11, 100]\n", - "keypad = []\n", + "keypad_list = []\n", "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", - " keypad.extend([key_numb * prop for prop in props])\n", + " keypad_list.extend([key_numb * prop for prop in props])\n", "\n", - "demo_interface = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad))\n", + "user_keypad = UserKeypad(keypad_size=keypad_size, keypad=np.array(keypad_list))\n", "display(Markdown(f\"\"\"\n", "## Example Keypad\n", "{keypad_size.numb_of_keys} X {keypad_size.props_per_key} keypad ({keypad_size.numb_of_keys} keys, {keypad_size.props_per_key} properties per key).\n", "\"\"\"))\n", - "display(Markdown(keypad_md_table(demo_interface.keypad, keypad_size)))" + "keypad_mat = user_keypad.keypad_matrix()\n", + "display(Markdown(keypad_md_table(user_keypad.keypad, keypad_size)))" ], "id": "initial_id", "outputs": [ @@ -65,15 +79,103 @@ "output_type": "display_data" } ], - "execution_count": 2 + "execution_count": 38 }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-21T10:15:10.187564Z", + "start_time": "2025-03-21T10:15:10.184075Z" + } + }, "cell_type": "code", - "outputs": [], - "execution_count": null, - "source": "keypad_mat = demo_interface", - "id": "43db2b9d247f420d" + "source": [ + "np.random.shuffle(keypad_mat)\n", + "display(Markdown(\"\"\"\n", + "### Step 1\n", + "\n", + "Shuffle the keys\n", + "\"\"\"\n", + "))\n", + "display(Markdown(keypad_md_table(keypad_mat.reshape(-1), keypad_size)))" + ], + "id": "43db2b9d247f420d", + "outputs": [ + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "\n### Step 1\n\nShuffle the keys\n" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|1|10|11|100|\n|key 1|3|30|33|300|\n|key 2|2|20|22|200|\n|key 3|5|50|55|500|\n|key 4|4|40|44|400|" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 39 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-21T10:15:10.202941Z", + "start_time": "2025-03-21T10:15:10.198267Z" + } + }, + "cell_type": "code", + "source": [ + "display(Markdown(\"\"\"\n", + "### Step 2\n", + "\n", + "Choose half of the properties and randomly assign them to a new key as a group, then shuffle this group to another new key.\"\"\"\n", + "))\n", + "prop_permutation = np.random.permutation(keypad_size.props_per_key)[: keypad_size.props_per_key // 2]\n", + "print(f\"Selected Group: {prop_permutation}\")\n", + "key_permutation = np.random.permutation(keypad_size.numb_of_keys)\n", + "# shuffle the selected property sets to new keys as a group\n", + "keypad_mat[:, prop_permutation] = keypad_mat[key_permutation, :][:, prop_permutation]\n", + "display(Markdown(keypad_md_table(keypad_mat.reshape(-1), keypad_size)))" + ], + "id": "8c322a6c074392e6", + "outputs": [ + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "\n### Step 2\n\nChoose half of the properties and randomly assign them to a new key as a group, then shuffle this group to another new key." + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Selected Group: [2 0]\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|2|10|22|100|\n|key 1|4|30|44|300|\n|key 2|1|20|11|200|\n|key 3|5|50|55|500|\n|key 4|3|40|33|400|" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "execution_count": 40 } ], "metadata": { diff --git a/src/user_keypad.py b/src/user_keypad.py index 9fe36f3..ffe9276 100644 --- a/src/user_keypad.py +++ b/src/user_keypad.py @@ -61,7 +61,7 @@ class UserKeypad: # shuffle all keys keypad_mat = self.keypad_matrix() np.random.shuffle(keypad_mat) - # select half the property sets + # select half the property positions prop_permutation = np.random.permutation(self.keypad_size.props_per_key)[: self.keypad_size.props_per_key // 2] key_permutation = np.random.permutation(self.keypad_size.numb_of_keys) # shuffle the selected property sets to new keys as a group -- 2.49.1 From 2f4519f5d7170d006cfd565bc877b2d4dd96def3 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 21 Mar 2025 06:29:00 -0500 Subject: [PATCH 46/85] update readme --- README.md | 63 +++++- ...ersion_tutorial.ipynb => Dispersion.ipynb} | 195 +++++++++++------- ...l.ipynb => Enrollment_Login_Renewal.ipynb} | 3 + ...fle_tutorial.ipynb => Split_Shuffle.ipynb} | 3 + requirements.txt | 1 - 5 files changed, 193 insertions(+), 72 deletions(-) rename notebooks/{dispersion_tutorial.ipynb => Dispersion.ipynb} (64%) rename notebooks/{nkode_tutorial.ipynb => Enrollment_Login_Renewal.ipynb} (99%) rename notebooks/{split_shuffle_tutorial.ipynb => Split_Shuffle.ipynb} (97%) diff --git a/README.md b/README.md index 9741fef..2e4efd7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,61 @@ -# README -Play around with the code in /notebooks +# Readme + +pynkode is a tutorial of how nkode works. +There are jupyter notebooks in /notebooks covering these topics: +- dispersion +- nkode enrollment, login, and renewal +- split shuffle + +## Installation + +- Python version 3.10 or greater is required +- Install pyenv or conda for environment management + +### Option 1: Using conda +```bash +# Create a new conda environment named pynkode +conda create -n pynkode python=3.10 +# Activate the environment +conda activate pynkode +# Install the requirements +pip install -r requirements.txt +``` + +### Option 2: Using pyenv +```bash +# Install Python 3.10 using pyenv +pyenv install 3.10.0 +# Create a virtualenv named pynkode +pyenv virtualenv 3.10.0 pynkode +# Set the local version to pynkode +pyenv local pynkode +# Install the requirements +pip install -r requirements.txt +``` + +## Starting a Jupyter Notebook + +### Option 1: Using classic Jupyter Notebook +```bash +# Ensure your environment is activated +# For conda: conda activate pynkode +# For pyenv: (should be automatic if in the directory) + +# Start the Jupyter Notebook server +jupyter notebook +``` + +### Option 2: Using JupyterLab +```bash +# Ensure your environment is activated +# Start JupyterLab +jupyter lab +``` + +## Exploring the Tutorials + +1. Navigate to the `/notebooks` directory in the Jupyter interface +2. Open the tutorials in the following recommended order: + - `Enrollment_Login_Renewal.ipynb` - Learn how to manage user accounts in nkode + - `Dispersion.ipynb` - Understand the basic concepts of dispersion in nkode + - `Split_Shuffle.ipynb` - Explore the split shuffle functionality diff --git a/notebooks/dispersion_tutorial.ipynb b/notebooks/Dispersion.ipynb similarity index 64% rename from notebooks/dispersion_tutorial.ipynb rename to notebooks/Dispersion.ipynb index a7d59eb..69bb5e3 100644 --- a/notebooks/dispersion_tutorial.ipynb +++ b/notebooks/Dispersion.ipynb @@ -2,26 +2,32 @@ "cells": [ { "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-21T10:41:27.004729Z", + "start_time": "2025-03-21T10:41:26.963236Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false + } + }, + "outputs": [], "source": [ + "import sys\n", + "import os\n", + "sys.path.append(os.path.abspath('..')) # Adds the parent directory to path\n", "from src.user_keypad import UserKeypad\n", "from IPython.display import Markdown, display\n", "from src.models import KeypadSize\n", "from src.utils import random_property_rotation, keypad_md_table\n", "import numpy as np" - ], - "metadata": { - "collapsed": false, - "ExecuteTime": { - "end_time": "2025-03-21T09:28:57.936307Z", - "start_time": "2025-03-21T09:28:57.933359Z" - } - }, - "outputs": [], - "execution_count": 5 + ] }, { - "metadata": {}, "cell_type": "markdown", + "metadata": {}, "source": [ "## Keypad Dispersion\n", "\n", @@ -34,13 +40,48 @@ ] }, { + "cell_type": "code", + "execution_count": 3, "metadata": { "ExecuteTime": { - "end_time": "2025-03-21T09:28:57.950591Z", - "start_time": "2025-03-21T09:28:57.945584Z" + "end_time": "2025-03-21T10:41:27.020289Z", + "start_time": "2025-03-21T10:41:27.014493Z" } }, - "cell_type": "code", + "outputs": [ + { + "data": { + "text/markdown": [ + "\n", + "## Example Keypad\n", + "5 X 4 keypad (5 keys, 4 properties per key).\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/markdown": [ + "||position 0|position 1|position 2|position 3|\n", + "|-|-|-|-|-|\n", + "|key 0|1|10|11|100|\n", + "|key 1|2|20|22|200|\n", + "|key 2|3|30|33|300|\n", + "|key 3|4|40|44|400|\n", + "|key 4|5|50|55|500|" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "keypad_size = KeypadSize(numb_of_keys=5, props_per_key=4)\n", "props = [1, 10, 11, 100]\n", @@ -54,47 +95,26 @@ "{keypad_size.numb_of_keys} X {keypad_size.props_per_key} keypad ({keypad_size.numb_of_keys} keys, {keypad_size.props_per_key} properties per key).\n", "\"\"\"))\n", "display(Markdown(keypad_md_table(user_keypad.keypad, keypad_size)))\n" - ], - "outputs": [ - { - "data": { - "text/plain": [ - "" - ], - "text/markdown": "\n## Example Keypad\n5 X 4 keypad (5 keys, 4 properties per key).\n" - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "" - ], - "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|1|10|11|100|\n|key 1|2|20|22|200|\n|key 2|3|30|33|300|\n|key 3|4|40|44|400|\n|key 4|5|50|55|500|" - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "execution_count": 6 + ] }, { - "metadata": {}, "cell_type": "markdown", - "source": "### Create Property Rotation Array" + "metadata": {}, + "source": [ + "### Create Property Rotation Array" + ] }, { "cell_type": "code", - "source": [ - "prop_rotation = np.random.choice(keypad_size.numb_of_keys, size=keypad_size.props_per_key, replace=False)\n", - "print(f\"Property Rotation: {prop_rotation}\")\n" - ], + "execution_count": 4, "metadata": { - "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-21T09:28:57.961890Z", - "start_time": "2025-03-21T09:28:57.959507Z" + "end_time": "2025-03-21T10:41:27.065332Z", + "start_time": "2025-03-21T10:41:27.056656Z" + }, + "collapsed": false, + "jupyter": { + "outputs_hidden": false } }, "outputs": [ @@ -102,66 +122,103 @@ "name": "stdout", "output_type": "stream", "text": [ - "Property Rotation: [0 3 1 2]\n" + "Property Rotation: [4 2 0 1]\n" ] } ], - "execution_count": 7 + "source": [ + "prop_rotation = np.random.choice(keypad_size.numb_of_keys, size=keypad_size.props_per_key, replace=False)\n", + "print(f\"Property Rotation: {prop_rotation}\")\n" + ] }, { - "metadata": {}, "cell_type": "markdown", - "source": "### Apply the Rotation" + "metadata": {}, + "source": [ + "### Apply the Rotation" + ] }, { + "cell_type": "code", + "execution_count": 5, "metadata": { "ExecuteTime": { - "end_time": "2025-03-21T09:28:57.976227Z", - "start_time": "2025-03-21T09:28:57.973183Z" + "end_time": "2025-03-21T10:41:27.074908Z", + "start_time": "2025-03-21T10:41:27.072449Z" } }, - "cell_type": "code", + "outputs": [ + { + "data": { + "text/markdown": [ + "||position 0|position 1|position 2|position 3|\n", + "|-|-|-|-|-|\n", + "|key 0|2|40|11|500|\n", + "|key 1|3|50|22|100|\n", + "|key 2|4|10|33|200|\n", + "|key 3|5|20|44|300|\n", + "|key 4|1|30|55|400|" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "dispersed_interface = random_property_rotation(\n", " user_keypad.keypad_matrix(),\n", " prop_rotation\n", ")\n", "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "" - ], - "text/markdown": "||position 0|position 1|position 2|position 3|\n|-|-|-|-|-|\n|key 0|1|30|55|400|\n|key 1|2|40|11|500|\n|key 2|3|50|22|100|\n|key 3|4|10|33|200|\n|key 4|5|20|44|300|" - }, - "metadata": {}, - "output_type": "display_data" + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] } ], - "execution_count": 8 + "source": [ + "print(\"hello\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.6" + "pygments_lexer": "ipython3", + "version": "3.10.14" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 4 } diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/Enrollment_Login_Renewal.ipynb similarity index 99% rename from notebooks/nkode_tutorial.ipynb rename to notebooks/Enrollment_Login_Renewal.ipynb index 7960aab..af5515d 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/Enrollment_Login_Renewal.ipynb @@ -3,6 +3,9 @@ { "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 secrets import choice\n", diff --git a/notebooks/split_shuffle_tutorial.ipynb b/notebooks/Split_Shuffle.ipynb similarity index 97% rename from notebooks/split_shuffle_tutorial.ipynb rename to notebooks/Split_Shuffle.ipynb index 84578ef..7ac6e52 100644 --- a/notebooks/split_shuffle_tutorial.ipynb +++ b/notebooks/Split_Shuffle.ipynb @@ -9,6 +9,9 @@ }, "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.models import KeypadSize\n", "from src.user_keypad import UserKeypad\n", "from src.utils import keypad_md_table\n", diff --git a/requirements.txt b/requirements.txt index fe1c025..dcd35dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ bcrypt~=4.1.3 numpy~=2.0.0 -jinja2~=3.1.4 pytest~=8.2.2 ipython~=8.25.0 \ No newline at end of file -- 2.49.1 From ba12ae69f4e7659fd304fae47a4f20786e4c8cd8 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 21 Mar 2025 06:32:12 -0500 Subject: [PATCH 47/85] cleanup --- .gitignore | 1 + notebooks/Dispersion.ipynb | 24 ------------------------ 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index ee05595..17cd06f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea __pycache__ +.ipynb_checkpoints diff --git a/notebooks/Dispersion.ipynb b/notebooks/Dispersion.ipynb index 69bb5e3..0942d98 100644 --- a/notebooks/Dispersion.ipynb +++ b/notebooks/Dispersion.ipynb @@ -174,30 +174,6 @@ ")\n", "display(Markdown(keypad_md_table(dispersed_interface.reshape(-1), keypad_size)))" ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "hello\n" - ] - } - ], - "source": [ - "print(\"hello\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { -- 2.49.1 From 5031062e87dac6daffe2d3aba9d7ae54444f68e0 Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 21 Mar 2025 06:32:45 -0500 Subject: [PATCH 48/85] delete docs --- docs/__init__.py | 0 docs/readme_template.md | 422 ---------------------------------------- docs/render_readme.py | 204 ------------------- 3 files changed, 626 deletions(-) delete mode 100644 docs/__init__.py delete mode 100644 docs/readme_template.md delete mode 100644 docs/render_readme.py diff --git a/docs/__init__.py b/docs/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/docs/readme_template.md b/docs/readme_template.md deleted file mode 100644 index 820f295..0000000 --- a/docs/readme_template.md +++ /dev/null @@ -1,422 +0,0 @@ -# README -Play around with the code in /notebooks - -## Customer Creation -Before creating a user, a customer generates random properties and set -values. The customers manage users. They define an nKode policy, keypad's dimensions, -properties/sets in the keypad, and the frequency of property renewal. -### nKode Policy and Keypad Size -An nKode policy defines: -
    -
  • the maximum length of a user's nKode
  • -
  • the minimum length of a user's nKode
  • -
  • the number of unique set values in a user's nKode
  • -
  • the number of unique values in a user's nKode
  • -
  • the number of bytes in an property and set
  • -
- -The keypad size defines: -
    -
  • the number of keys in the keypad displayed to the user
  • -
  • properties per key
  • -
- -To be [dispersion](nkode_concepts.md/#dispersion-resistant-interface) resistant, the number of properties must be greater than the number of keys. - -``` -api = NKodeAPI() - -policy = NKodePolicy( - max_nkode_len=10, - min_nkode_len=4, - distinct_sets=0, - distinct_properties=4, - byte_len=2 -) - -keypad_size = KeypadSize( - numb_of_keys = {{ keypad_size.numb_of_keys }}, - props_per_key = {{ keypad_size.props_per_key }} # aka number of sets -) - -customer_id = api.create_new_customer(keypad_size, policy) -customer = api.customers[customer_id] -``` -### Customer properties and Sets -A customer has users and defines the properties and set values for all its users. -Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.props_per_key }} properties per key, -this gives a customer interface of {{ keypad_size.numb_of_props }} distinct properties and {{ keypad_size.props_per_key }} distinct property sets. -Each property belongs to one of the {{ keypad_size.props_per_key }} sets. Each property and set value is a unique 2-byte integer in this example. - -``` -set_vals = customer.cipher.set_key - -Customer Sets: {{ customer_set_vals }} -``` - -``` -prop_vals = customer.cipher.prop_key -keypad_view(prop_vals, keypad_size.props_per_key) - -Customer properties: -{% for props in customer_prop_view -%} -{{ props }} -{% endfor %} -``` - -properties organized by set: -``` -prop_set_view = matrix_transpose(prop_keypad_view) -set_property_dict = dict(zip(set_vals, prop_set_view)) - -Set to property Map: -{% for set_val, props in set_property_dict.items() -%} -{{ set_val }} : {{ props }} -{% endfor %} -``` - -## User Signup -Now that we have a customer, we can create users. To create a new user: - -1. Generate a random interface -2. The user sets their nKode and sends their selection to the server -3. The user confirms their nKode. If the user's nKode matches the policy, the server creates the user. -### Random Interface Generation -The user's interface must be dispersable so the server can determine the user's nkode. -The server randomly drops property sets until -the number of properties equals the number of keys, making the interface dispersable. -In our case, the server randomly drops {{ keypad_size.props_per_key - keypad_size.numb_of_keys }} property {{ "sets" if keypad_size.props_per_key - keypad_size.numb_of_keys > 1 else "set" }}. -to give us a {{ keypad_size.numb_of_keys }} X {{ keypad_size.numb_of_keys }} keypad with possible index values ranging from 0-{{ keypad_size.numb_of_props - 1 }}. -Each value in the interface is the index value of a customer property. -The user never learns what their "real" property is. They do not see the index value representing their nKode or -the customer server-side value. - -``` -session_id, signup_interface = api.generate_index_interface(customer_id) -signup_interface_keypad = list_to_matrix(signup_interface, keypad_size.props_per_key) - -Signup Keypad: -{% for key in signup_keypad -%} -Key {{ loop.index }}: {{ key }} -{% endfor %} -``` - -### Set nKode -The user identifies properties in the interface they want in their nkode. Each property has an index value. -Below, the user has selected `{{ user_passcode }}`. These index values can be represented by anything in the GUI. -The only requirement is that the GUI properties be associated with the same index every time the user logs in. -If users want to change anything about their interface, they must also change their nkode. - -``` -username = {{ username }} -user_passcode = {{ user_passcode }} -selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.props_per_key) - -Selected Keys -{{ selected_keys_set }} -``` - -The user's passcode server side properties are: -``` -server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] - -User Passcode Server-side properties: {{ server_side_prop }} -``` - -### Confirm nKode -The user submits the set interface to the server and receives the _confirm interface_ as a response. -The user finds their nKode again. - -``` -confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) -keypad_view(confirm_interface, keypad_size.numb_of_keys) -selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, keypad_size.numb_of_keys) - -Confirm Keypad: -{% for key in confirm_keypad -%} -Key {{ loop.index }}: {{ key }} -{% endfor %} -Selected Keys: -{{ selected_keys_confirm }} -``` - -The user submits their confirmation key selection and the user is created -``` -success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) -``` - -### Passcode Enciphering, Hashing, and Salting -When a new user creates an nKode, the server caches its set and confirms the interface and the user's key selection. -On the last api.confirm_nkode, the server: - -1. Deduces the user's properties -2. Validates the Passcode against the nKodePolicy -3. Creates new User Cipher Keys -4. Enciphers the user's mask -5. Enciphers, salts, and hashes the user's passcode - -Steps 1-2 are straightforward. For a better idea of how they work, see pyNKode. - -#### User Cipher Keys - -##### User Cipher Keys Data Structure -``` -set_key = generate_random_nonrepeating_list(keypad_size.props_per_key, max_numb=2**(8*numb_of_bytes)) -set_key = xor_lists(set_key, customer_prop.set_vals) - -UserCipherKeys( - prop_key=generate_random_nonrepeating_list(keypad_size.props_per_key * keypad_size.numb_of_keys, max_numb=2**(8*numb_of_bytes)), - pass_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), - mask_key=generate_random_nonrepeating_list(max_nkode_len, max_numb=2**(8*numb_of_bytes)), - set_key=set_key, - salt=bcrypt.gensalt(), - max_nkode_len=max_nkode_len -) -``` - -##### User Cipher Keys Values -``` -user_cipher = UserCipherKeys( - prop_key = {{ user_cipher.prop_key }}, - pass_key = {{ user_cipher.pass_key }}, - mask_key = {{ user_cipher.mask_key }}, - set_key = {{ user_cipher.set_key }}, - salt = {{ user_cipher.salt }}, - max_nkode_len = {{ user_cipher.max_nkode_len }} -) -``` - -The method UserCipherKeys.encipher_nkode secures a user's nKode in the database. This method is called in api.confirm_nkode - -``` -class EncipheredNKode(BaseModel): - code: str - mask: str -``` - -#### Mask Enciphering - -Recall: - -- set_key_i = (set_rand_numb_i ^ set_val_i) -- mask_key_i = mask_rand_numb_i -- padded_passcode_server_set_i = set_val_i -- len(set_key) == len(mask_key) == (padded_passcode_server_set) == max_nkode_len == 10 - where i is the index - -- mask_i = mask_key_i ^ padded_passcode_server_set_i ^ set_key_i -- mask_i = mask_rand_num_i ^ set_val_i ^ set_rand_numb_i ^ set_val_i -- mask_i = mask_rand_num_i ^ set_rand_numb_i # set_val_i is cancelled out - - -``` -passcode = {{ user_passcode }} -passcode_server_prop = [customer.cipher.prop_key[idx] for idx in passcode] -passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] - -Passcode Set Vals: {{ passcode_server_prop }} -Passcode prop Vals: {{ passcode_server_set }} -``` - -``` -padded_passcode_server_set = user_cipher.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) - -set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_passcode_server_set] -mask_set_keys = [user_cipher.set_key[idx] for idx in set_idx] - -ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) -ciphered_mask = xor_lists(ciphered_mask, user_cipher.mask_key) - -mask = user_cipher.encode_base64_str(ciphered_mask) -Mask: {{ enciphered_nkode.mask }} -``` - -#### Passcode Enciphering and Hashing - -- ciphered_customer_prop = prop_key ^ customer_prop -- ciphered_passcode_i = pass_key_i ^ ciphered_customer_prop_i -- code = hash(ciphered_passcode, salt) - -``` -ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_cipher.prop_key) -passcode_ciphered_props = [ciphered_customer_props[idx] for idx in passcode] -pad_len = customer.nkode_policy.max_nkode_len - passcode_len - -passcode_ciphered_props.extend([0 for _ in range(pad_len)]) - -ciphered_code = xor_lists(passcode_ciphered_props, user_cipher.pass_key) - -passcode_bytes = int_array_to_bytes(ciphered_code) -passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) -hashed_data = bcrypt.hashpw(passcode_digest, user_cipher.salt) -code = hashed_data.decode("utf-8") - -Code: {{ enciphered_nkode.code }} -``` - -## User Login -To login, a user: - -1. Gets login interface -2. Submits key entry - -### Get Login Interface -The client requests the user's login interface. -``` -login_interface = api.get_login_interface(username, customer_id) -keypad_view(login_interface, keypad_size.props_per_key) -``` -The server returns a randomly shuffled interface. Learn more about how the [User Interface Shuffle](nkode_concepts.md/#user-interface-shuffle) works -``` -Login Interface Keypad View: -{% for key in login_keypad -%} -Key {{ loop.index }}: {{ key }} -{% endfor %} -``` -Recall the user's passcode is `user_passcode = {{ user_passcode }}` so the user selects keys ` selected_keys_login = {{ selected_login_keys }}` - -``` -success = api.login(customer_id, username, selected_keys_login) -``` - -### Validate Login Key Entry -- decipher user mask and recover nkode set values -- get presumed property from key selection and set values -- encipher, salt, and hash presumed property values and compare them to the users hashed code - -#### Decipher Mask - -Recall: - -- set_key_i = (set_key_rand_numb_i ^ set_val_i) -- mask_i = mask_key_rand_num_i ^ set_key_rand_numb_i - -Recover nKode set values: - -- decode mask from base64 to int -- deciphered_mask = mask ^ mask_key -- deciphered_mask_i = set_key_rand_numb # mask_key_rand_num_i is cancelled out -- set_key_rand_component = set_key ^ set_values -- deduce the set value - -``` -user = customer.users[username] -user_cipher = user.user_cipher -user_mask = user.enciphered_passcode.mask -decoded_mask = user_cipher.decode_base64_str(user_mask) -deciphered_mask = xor_lists(decoded_mask, user_cipher.mask_key) -set_key_rand_component = xor_lists(set_vals, user_cipher.set_key) -passcode_sets = [] -for set_cipher in deciphered_mask[:passcode_len]: - set_idx = set_key_rand_component.index(set_cipher) - passcode_sets.append(set_vals[set_idx]) - -Passcode Sets: {{ login_passcode_sets }} -``` - - -### Get Presumed properties -``` -set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in passcode_sets] - -presumed_selected_properties_idx = [] -for idx in range(passcode_len): - key_numb = selected_keys_login[idx] - set_idx = set_vals_idx[idx] - selected_prop_idx = customer.users[username].user_interface.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) - presumed_selected_properties_idx.append(selected_prop_idx) - -Presumped Passcode: {{ presumed_selected_properties_idx }} -Recall User Passcode: {{ user_passcode }} -``` -### Compare Enciphered Passcodes -``` -enciphered_nkode = user_cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher) -``` -If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid, and the login is successful. - -## Renew properties -properties renew is invoked with the renew_properties method: `api.renew_properties(customer_id)` -The renew properties process has three steps: -1. Renew Customer properties -2. Renew User Keys -3. Refresh User on Login - -When the customer calls the `renew_properties` method, the method replaces the customer's properties and set values. All its users go through an intermediate -renewal step. The users fully renew after their first successful login. This first login refreshes their keys, salt, and hash with new values. - - -### Customer Renew -Old Customer properties and set values are cached and copied to variables before renewal. -``` -old_sets = customer.cipher.set_key - -Customer Sets: {{ customer_set_vals }} -``` - -``` -old_prop = customer.cipher.prop_key - -Customer properties: -{% for props in customer_prop_view -%} -{{ props }} -{% endfor %} -``` - -After the renewal, the customer properties and sets are new randomly generated values. -``` -api.renew_properties(customer_id) - -set_vals = customer.cipher.set_key - -Customer Sets: {{ customer_new_set_vals }} -``` - -``` -prop_vals = customer.cipher.prop_key - -Customer properties: -{% for props in customer_new_prop_view -%} -{{ props }} -{% endfor %} -``` - -### Renew User -During the renewal, each user goes through a temporary transition period. -``` -props_xor = xor_lists(new_props, old_props) -sets_xor = xor_lists(new_sets, old_sets) -for user in customer.users.values(): - user.renew = True - user.user_cipher.set_key = xor_lists(user.user_cipher.set_key, sets_xor) - user.user_cipher.prop_key = xor_lists(user.user_cipher.prop_key, props_xor) -``` -##### User prop Key -The user's prop key was a randomly generated list of length `numb_of_keys * prop_per_key`. -Now each value in the prop key is `prop_key_i = old_prop_key_i ^ new_prop_i ^ old_prop_i`. -Recall in the login process, `ciphered_customer_props = prop_key ^ customer_prop`. -Since the customer_prop is now the new value, it gets canceled out, leaving: -``` -new_prop_key = old_prop_key ^ old_prop ^ new_prop -ciphered_customer_props = new_prop_key ^ new_prop -ciphered_customer_props = old_prop_key ^ old_prop # since new_prop cancel out -``` -Using the new customer properties, we can validate the user's login attempt with the same hash. - -##### User Set Key -The user's set key was a randomly generated list of length `prop_per_key` xor `customer_set_vals`. -The `old_set_vals` have been replaced with the new `new_set_vals`. The deciphering process described above -remains the same. - -### User Refresh -Once the user has a successful login, they get a new salt and cipher keys, and their `enciphered_passcode` is recomputed -with the new values. -``` -user.user_cipher = UserCipherKeys.new( - customer.cipher.keypad_size, - customer.cipher.set_key, - user.user_cipher.max_nkode_len -) -user.enciphered_passcode = user.user_cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) -user.renew = False -``` diff --git a/docs/render_readme.py b/docs/render_readme.py deleted file mode 100644 index 92238e0..0000000 --- a/docs/render_readme.py +++ /dev/null @@ -1,204 +0,0 @@ -import numpy as np -from jinja2 import Environment, FileSystemLoader -import os -from src.nkode_api import NKodeAPI -from src.models import NKodePolicy, KeypadSize, EncipheredNKode -from src.user_cipher import UserCipher -from secrets import choice -from string import ascii_lowercase -import bcrypt -import hashlib -import base64 - - -def random_username() -> str: - return "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) - - -def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]: - indices = [np.where(keypad == prop)[0][0] for prop in user_passcode] - return [int(index // props_per_key) for index in indices] - - -def visualize_keypad(keypad_list: np.ndarray, props_per_key: int): - print("Keypad View") - keypad_mat = keypad_list.reshape(-1, props_per_key) - for idx, key_vals in enumerate(keypad_mat): - print(f"Key {idx}: {key_vals}") - - -def render_nkode_authentication(data: dict): - # Set up the Jinja2 environment and template loader - file_loader = FileSystemLoader('') - env = Environment(loader=file_loader) - - # Load the template - template = env.get_template('readme_template.md') - - print(os.getcwd()) - # Render the template with the data - output = template.render(data) - - # Print or save the output - # output_file = os.path.expanduser("~/Desktop/nkode_authentication.md") - output_file = '../README.md' - with open(output_file, 'w') as fp: - fp.write(output) - print("File written successfully") - - -if __name__ == "__main__": - api = NKodeAPI() - - policy = NKodePolicy( - max_nkode_len=10, - min_nkode_len=4, - distinct_positions=0, - distinct_properties=4, - byte_len=2, - ) - keypad_size = KeypadSize( - numb_of_keys=5, - props_per_key=6 # aka number of sets - ) - customer_id = api.create_new_customer(keypad_size, policy) - customer = api.customers[customer_id] - - set_vals = customer.cipher.position_key - prop_vals = customer.cipher.property_key - customer_prop_view = prop_vals.reshape(-1, keypad_size.props_per_key) - - prop_keypad_view = prop_vals.reshape(-1, keypad_size.props_per_key) - prop_set_view = prop_keypad_view.T - set_property_dict = dict(zip(set_vals, prop_set_view)) - - session_id, signup_interface = api.generate_signup_keypad(customer_id) - signup_keypad = signup_interface.reshape(-1, keypad_size.numb_of_keys) - - username = random_username() - passcode_len = 4 - user_passcode = signup_interface[:passcode_len].tolist() - selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) - server_side_prop = [customer.cipher.property_key[idx] for idx in user_passcode] - - confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) - - confirm_keypad = confirm_interface.reshape(-1, keypad_size.numb_of_keys) - - selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, keypad_size.numb_of_keys) - - success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) - assert success - passcode_server_prop = [customer.cipher.property_key[idx] for idx in user_passcode] - passcode_server_set = customer.cipher.get_props_position_vals(user_passcode) - user_keys = customer.users[username].cipher - # TODO: pad_user_mask is deprecated - padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.position_key) - - set_idx = [customer.cipher.get_position_index(set_val) for set_val in padded_passcode_server_set] - mask_set_keys = [user_keys.combined_position_key[idx] for idx in set_idx] - ciphered_mask = mask_set_keys ^ padded_passcode_server_set ^ user_keys.mask_key - mask = user_keys.encode_base64_str(ciphered_mask) - ciphered_customer_props = customer.cipher.property_key ^ user_keys.property_key - passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode] - pad_len = customer.nkode_policy.max_nkode_len - passcode_len - passcode_ciphered_props.extend([0 for _ in range(pad_len)]) - ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key) - passcode_bytes = ciphered_code.tobytes() - passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) - hashed_data = bcrypt.hashpw(passcode_digest, bcrypt.gensalt(rounds=12)) - code = hashed_data.decode("utf-8") - - enciphered_nkode = EncipheredNKode( - mask=mask, - code=code, - ) - """ - USER LOGIN - """ - login_interface = api.get_login_keypad(username, customer_id) - login_keypad = login_interface.reshape(-1, keypad_size.props_per_key) - selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.props_per_key) - success = api.login(customer_id, username, selected_keys_login) - assert success - - """ - VALIDATE LOGIN KEY ENTRY - DECIPHER MASK - """ - - user = customer.users[username] - set_vals = customer.cipher.position_key - user_keys = user.cipher - user_mask = user.enciphered_passcode.mask - decoded_mask = user_keys.decode_base64_str(user_mask) - deciphered_mask = np.bitwise_xor(decoded_mask, user_keys.mask_key) - set_key_rand_component = np.bitwise_xor(set_vals, user_keys.combined_position_key) - login_passcode_sets = [] - for set_cipher in deciphered_mask[:passcode_len]: - set_idx = np.where(set_key_rand_component == set_cipher)[0][0] - login_passcode_sets.append(int(set_vals[set_idx])) - - """ - GET PRESUMED properties - """ - - set_vals_idx = [customer.cipher.get_position_index(set_val) for set_val in login_passcode_sets] - presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, set_vals_idx) - """ - RENEW KEYS - """ - - old_props = customer.cipher.property_key.copy() - old_sets = customer.cipher.position_key.copy() - customer.cipher.renew() - new_props = customer.cipher.property_key - new_sets = customer.cipher.position_key - customer_new_prop_view = new_props.reshape(-1, keypad_size.props_per_key) - """ - RENEW USER - """ - props_xor = np.bitwise_xor(new_props, old_props) - sets_xor = np.bitwise_xor(new_sets, old_sets) - for user in customer.users.values(): - user.renew = True - user.cipher.combined_position_key = np.bitwise_xor(user.cipher.combined_position_key, sets_xor) - user.cipher.property_key = np.bitwise_xor(user.cipher.property_key, props_xor) - - """ - REFRESH USER KEYS - """ - user.cipher = UserCipher.create( - customer.cipher.keypad_size, - customer.cipher.position_key, - user.cipher.max_nkode_len - ) - user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) - user.renew = False - - # Define some data to pass to the template - data = { - 'keypad_size': keypad_size, - 'customer_set_vals': set_vals, - 'customer_prop_view': customer_prop_view, - 'set_property_dict': set_property_dict, - 'set_signup_keypad': signup_keypad, - 'username': 'test_user', - 'passcode_property_indices': user_passcode, - 'selected_keys_set': selected_keys_set, - 'server_side_prop': server_side_prop, - 'confirm_keypad': confirm_keypad, - 'selected_keys_confirm': selected_keys_confirm, - 'user_cipher': user_keys, - 'ordered_customer_prop_key': passcode_server_prop, - 'ordered_customer_set_key': passcode_server_set, - 'enciphered_nkode': enciphered_nkode, - 'login_keypad': login_keypad, - 'selected_login_keys': selected_keys_login, - 'login_passcode_sets': login_passcode_sets, - 'presumed_selected_properties_idx': presumed_selected_properties_idx, - 'customer_new_prop_view': customer_new_prop_view, - 'customer_new_set_vals': new_sets, - - } - render_nkode_authentication(data) \ No newline at end of file -- 2.49.1 From 1f188e82ed5077f9f61f016a89265cd4e53a443b Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 24 Mar 2025 04:49:39 -0500 Subject: [PATCH 49/85] add enrollment diagrams --- docs/enrollment_diagram.md | 37 +++ docs/enrollment_diagram.template.md | 37 +++ docs/render_enrollment_md.py | 107 ++++++++ notebooks/Enrollment_Login_Renewal.ipynb | 314 +++++++++++------------ src/customer.py | 3 - src/utils.py | 4 + 6 files changed, 340 insertions(+), 162 deletions(-) create mode 100644 docs/enrollment_diagram.md create mode 100644 docs/enrollment_diagram.template.md create mode 100644 docs/render_enrollment_md.py diff --git a/docs/enrollment_diagram.md b/docs/enrollment_diagram.md new file mode 100644 index 0000000..e6c52bd --- /dev/null +++ b/docs/enrollment_diagram.md @@ -0,0 +1,37 @@ +# nKode Enrollment +```mermaid +sequenceDiagram + participant User + participant Client + participant Server + Note over User,Client: Enrollment + Client->>User: Signup Form + Note left of User: email: user@example.com + User->>Client: Submit Email + Client->>+Server: Signup Session: user@example.com + Server->>Server: Create Signup Session + Note over User,Client: Set nKode + Server-->>-Client: signup_session_id, set_keypad, icons + Note left of Server: signup_session_id:
92a2a1c6-3146-4d47-ac00-5160a7ebf104 + Note left of Server: set_keypad:
Key 0: [ 7 14 3 28 23]
Key 1: [ 1 26 15 10 5]
Key 2: [13 20 27 4 29]
Key 3: [25 8 21 16 11]
Key 4: [19 2 9 22 17]
+ 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: ['🐶' '🥳' '🤯' '🐸' '👾']
+ Note left of User: User icons: ['😍' '🤓' '😇' '😡'] + User->>Client: Set Key Selection: [0, 2, 0, 0] + Client->>+Server: Set nKode:
92a2a1c6-3146-4d47-ac00-5160a7ebf104
[0, 2, 0, 0] + Server->>Server: Disperse Set Keypad + Note over User,Client: Confirm nKode + Server-->>-Client: signup_session_id, confirm_keypad, icons + Note left of Server: signup_session_id:
92a2a1c6-3146-4d47-ac00-5160a7ebf104 + Note left of Server: confirm_keypad:
Key 0: [ 7 26 21 4 17]
Key 1: [19 20 3 16 5]
Key 2: [ 1 8 9 28 29]
Key 3: [13 14 15 22 11]
Key 4: [25 2 27 10 23]
+ Client->>Client: Order Icons by keypad + Client->>User: Display Keypad + Note left of Client: Key 0: ['😡' '⚡' '🐻' '🤓' '👾']
Key 1: ['🐶' '🦁' '😍' '👽' '😎']
Key 2: ['😂' '😱' '🤯' '🍕' '🎉']
Key 3: ['🙃' '😇' '🤖' '🐸' '😴']
Key 4: ['🌟' '🥳' '🔥' '🥰' '🐙']
+ Note left of User: User icons: ['😍' '🤓' '😇' '😡'] + User->>Client: Key Selection: [1, 0, 3, 0] + Client->>+Server: Confirm nKode:
92a2a1c6-3146-4d47-ac00-5160a7ebf104
[1, 0, 3, 0] + Server->>Server: Create User + Server-->-Client: Success +``` \ No newline at end of file diff --git a/docs/enrollment_diagram.template.md b/docs/enrollment_diagram.template.md new file mode 100644 index 0000000..e2c239c --- /dev/null +++ b/docs/enrollment_diagram.template.md @@ -0,0 +1,37 @@ +# nKode Enrollment +```mermaid +sequenceDiagram + participant User + participant Client + participant Server + Note over User,Client: Enrollment + Client->>User: Signup Form + Note left of User: email: {{ email }} + User->>Client: Submit Email + Client->>+Server: Signup Session: {{ email }} + Server->>Server: Create Signup Session + Note over User,Client: Set nKode + Server-->>-Client: signup_session_id, set_keypad, icons + Note left of Server: signup_session_id:
{{ signup_session_id }} + Note left of Server: set_keypad:
{{set_keypad}} + Note left of Server: Icons:
{{icon_matrix}} + Client->>Client: Order Icons by keypad + Client->>User: Display Keypad + Note left of Client: {{ ordered_keypad }} + Note left of User: User icons: {{ passcode_user_icons }} + User->>Client: Set Key Selection: {{ selected_keys_set }} + Client->>+Server: Set nKode:
{{ signup_session_id }}
{{ selected_keys_set }} + Server->>Server: Disperse Set Keypad + Note over User,Client: Confirm nKode + Server-->>-Client: signup_session_id, confirm_keypad, icons + Note left of Server: signup_session_id:
{{ signup_session_id }} + Note left of Server: confirm_keypad:
{{confirm_keypad}} + Client->>Client: Order Icons by keypad + Client->>User: Display Keypad + Note left of Client: {{ confirm_ordered_keypad }} + Note left of User: User icons: {{ passcode_user_icons }} + User->>Client: Key Selection: {{ confirm_key_selection }} + Client->>+Server: Confirm nKode:
{{ signup_session_id }}
{{ confirm_key_selection }} + Server->>Server: Create User + Server-->-Client: Success +``` \ No newline at end of file diff --git a/docs/render_enrollment_md.py b/docs/render_enrollment_md.py new file mode 100644 index 0000000..e5e7221 --- /dev/null +++ b/docs/render_enrollment_md.py @@ -0,0 +1,107 @@ +# Render markdown template for nKode enrollment +from pathlib import Path + +import numpy as np +from jinja2 import Environment, FileSystemLoader, select_autoescape +from secrets import choice +from string import ascii_lowercase +from src.models import NKodePolicy, KeypadSize +from src.nkode_api import NKodeAPI +from src.utils import select_keys_with_passcode_values + + +def render_markdown_template(template_path, output_path, context): + """ + Render a Jinja2 markdown template with the given context and save to output_path. + + Args: + template_path (Path): Path to the template file + output_path (Path): Path where the rendered file will be saved + context (dict): Template variables + """ + template_dir = template_path.parent + template_file = template_path.name + + env = Environment( + loader=FileSystemLoader(template_dir), + autoescape=select_autoescape(['html', 'xml']), + trim_blocks=True, + lstrip_blocks=True + ) + + template = env.get_template(template_file) + rendered = template.render(**context) + + with open(output_path, 'w') as f: + f.write(rendered) + + print(f"Template rendered to {output_path}") + + +def display_icons(icons_array: np.ndarray, kp: KeypadSize) -> str: + icons = "[" + for row in icons_array.reshape(-1, kp.numb_of_keys): + icons += ",".join(row) + icons += "
" + icons = icons[:-5] + icons += "]" + return icons + +def display_icons_keypad(icons_array: np.ndarray, kp: KeypadSize) -> str: + icons = "" + for idx, row in enumerate(icons_array.reshape(-1, kp.numb_of_keys)): + icons += f"Key {idx}: " + icons += str(row) + icons += "
" + return icons + +if __name__ == "__main__": + api = NKodeAPI() + user_icons = np.array([ + "😀", "😂", "🥳", "😍", "🤓", + "😎", "🥺", "😡", "😱", "🤯", + "🥰", "😴", "🤔", "🙃", "😇", + "🤖", "👽", "👾", "🐱", "🐶", + "🦁", "🐻", "🐸", "🐙", "🦄", + "🌟", "⚡", "🔥", "🍕", "🎉" + ]) + policy = NKodePolicy( + max_nkode_len=10, + min_nkode_len=4, + distinct_positions=0, + distinct_properties=4, + ) + keypad_size = KeypadSize( + numb_of_keys=5, + props_per_key=6 + ) + customer_id = api.create_new_customer(keypad_size, policy) + signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id) + ordered_set_icons = user_icons[set_signup_keypad] + username = "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) + 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) + selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, + keypad_size.numb_of_keys) + ordered_confirm_icons = user_icons[confirm_keypad] + print(f"Selected Keys\n{selected_keys_confirm}") + success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id) + context = { + "email": "user@example.com", + "signup_session_id": signup_session_id, + "set_keypad": display_icons_keypad(set_signup_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size), + "icon_matrix": display_icons(user_icons, keypad_size), + "ordered_keypad": display_icons_keypad(ordered_set_icons, keypad_size), + "passcode_user_icons": str(user_icons[passcode_property_indices]), + "selected_keys_set": str(selected_keys_set), + "confirm_keypad": display_icons_keypad(confirm_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size), + "confirm_ordered_keypad": display_icons_keypad(ordered_confirm_icons.reshape(-1, keypad_size.numb_of_keys), keypad_size), + "confirm_key_selection": selected_keys_confirm + } + + # Render the template + render_markdown_template(Path("./enrollment_diagram.template.md"), Path("./enrollment_diagram.md"), context) \ No newline at end of file diff --git a/notebooks/Enrollment_Login_Renewal.ipynb b/notebooks/Enrollment_Login_Renewal.ipynb index af5515d..eed046a 100644 --- a/notebooks/Enrollment_Login_Renewal.ipynb +++ b/notebooks/Enrollment_Login_Renewal.ipynb @@ -8,6 +8,7 @@ "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", @@ -20,11 +21,6 @@ " 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", @@ -33,12 +29,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.080558Z", - "start_time": "2025-03-20T15:13:44.076372Z" + "end_time": "2025-03-24T09:48:10.442598Z", + "start_time": "2025-03-24T09:48:10.410209Z" } }, "outputs": [], - "execution_count": 41 + "execution_count": 1 }, { "cell_type": "code", @@ -56,12 +52,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.090939Z", - "start_time": "2025-03-20T15:13:44.088281Z" + "end_time": "2025-03-24T09:48:10.450851Z", + "start_time": "2025-03-24T09:48:10.448737Z" } }, "outputs": [], - "execution_count": 42 + "execution_count": 2 }, { "metadata": {}, @@ -115,8 +111,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.107060Z", - "start_time": "2025-03-20T15:13:44.100773Z" + "end_time": "2025-03-24T09:48:10.468165Z", + "start_time": "2025-03-24T09:48:10.456820Z" } }, "outputs": [ @@ -124,30 +120,30 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Position Key: [22504 45127 46010 30773 46509 63084]\n", + "Customer Position Key: [ 4136 64524 15410 9988 20496 18918]\n", "Customer Properties Key:\n", - "[38076 5253 43656 24035 57980 18197]\n", - "[56754 21362 27799 42374 57145 46164]\n", - "[56031 10476 13741 23847 57450 42577]\n", - "[63205 55476 58864 29925 9463 58182]\n", - "[61997 24583 9269 19956 55576 32892]\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", - "22504: [38076 56754 56031 63205 61997]\n", - "45127: [ 5253 21362 10476 55476 24583]\n", - "46010: [43656 27799 13741 58864 9269]\n", - "30773: [24035 42374 23847 29925 19956]\n", - "46509: [57980 57145 57450 9463 55576]\n", - "63084: [18197 46164 42577 58182 32892]\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": 43 + "execution_count": 3 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.127687Z", - "start_time": "2025-03-20T15:13:44.125028Z" + "end_time": "2025-03-24T09:48:10.593687Z", + "start_time": "2025-03-24T09:48:10.591235Z" } }, "cell_type": "code", @@ -164,16 +160,16 @@ "output_type": "stream", "text": [ "Position Value to Icons Map:\n", - "22504: ['😀' '🥺' '🤔' '🐱' '🦄']\n", - "45127: ['😂' '😡' '🙃' '🐶' '🌟']\n", - "46010: ['🥳' '😱' '😇' '🦁' '⚡']\n", - "30773: ['😍' '🤯' '🤖' '🐻' '🔥']\n", - "46509: ['🤓' '🥰' '👽' '🐸' '🍕']\n", - "63084: ['😎' '😴' '👾' '🐙' '🎉']\n" + "4136: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "64524: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "15410: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "9988: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "20496: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "18918: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], - "execution_count": 44 + "execution_count": 4 }, { "metadata": {}, @@ -197,8 +193,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.156638Z", - "start_time": "2025-03-20T15:13:44.150222Z" + "end_time": "2025-03-24T09:48:10.653252Z", + "start_time": "2025-03-24T09:48:10.646267Z" } }, "cell_type": "code", @@ -226,11 +222,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: ['🦄' '🦁' '🐻' '🤓' '👾']\n", - "Key 1: ['🥺' '⚡' '🔥' '🍕' '🎉']\n", - "Key 2: ['🐱' '😱' '🤖' '👽' '🐙']\n", - "Key 3: ['😀' '🥳' '😍' '🐸' '😴']\n", - "Key 4: ['🤔' '😇' '🤯' '🥰' '😎']\n" + "Key 0: ['😂' '🥳' '🐻' '🍕' '😎']\n", + "Key 1: ['🐶' '⚡' '🤖' '🥰' '🎉']\n", + "Key 2: ['🌟' '😇' '😍' '🤓' '🐙']\n", + "Key 3: ['😡' '😱' '🔥' '👽' '😴']\n", + "Key 4: ['🙃' '🦁' '🤯' '🐸' '👾']\n" ] }, { @@ -247,11 +243,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [24 20 21 4 17]\n", - "Key 1: [ 6 26 27 28 29]\n", - "Key 2: [18 8 15 16 23]\n", - "Key 3: [ 0 2 3 22 11]\n", - "Key 4: [12 14 9 10 5]\n" + "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" ] }, { @@ -268,15 +264,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [61997 58864 29925 57980 42577]\n", - "Key 1: [56754 9269 19956 55576 32892]\n", - "Key 2: [63205 27799 23847 57450 58182]\n", - "Key 3: [38076 43656 24035 9463 46164]\n", - "Key 4: [56031 13741 42374 57145 18197]\n" + "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": 45 + "execution_count": 5 }, { "metadata": {}, @@ -289,8 +285,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.190448Z", - "start_time": "2025-03-20T15:13:44.187214Z" + "end_time": "2025-03-24T09:48:10.726327Z", + "start_time": "2025-03-24T09:48:10.723046Z" } }, "cell_type": "code", @@ -309,14 +305,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Passcode Indices: [4, 9, 5, 18]\n", - "User Passcode Icons: ['🤓' '🤯' '😎' '🐱']\n", - "User Passcode Server-side properties: [57980 42374 18197 63205]\n", - "Selected Keys: [0, 4, 4, 2]\n" + "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": 46 + "execution_count": 6 }, { "metadata": {}, @@ -329,8 +325,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.516570Z", - "start_time": "2025-03-20T15:13:44.208909Z" + "end_time": "2025-03-24T09:48:11.002815Z", + "start_time": "2025-03-24T09:48:10.759840Z" } }, "cell_type": "code", @@ -347,17 +343,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [18 20 27 10 11]\n", - "Key 1: [24 14 15 22 29]\n", - "Key 2: [12 2 21 28 23]\n", - "Key 3: [ 0 26 9 16 17]\n", - "Key 4: [6 8 3 4 5]\n", + "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", - "[4, 3, 4, 0]\n" + "[1, 3, 1, 2]\n" ] } ], - "execution_count": 47 + "execution_count": 7 }, { "metadata": {}, @@ -367,8 +363,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.527802Z", - "start_time": "2025-03-20T15:13:44.524844Z" + "end_time": "2025-03-24T09:48:11.011978Z", + "start_time": "2025-03-24T09:48:11.009413Z" } }, "cell_type": "code", @@ -387,22 +383,22 @@ "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" + "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": 48 + "execution_count": 8 }, { "metadata": {}, @@ -422,8 +418,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.546871Z", - "start_time": "2025-03-20T15:13:44.540903Z" + "end_time": "2025-03-24T09:48:11.030598Z", + "start_time": "2025-03-24T09:48:11.025452Z" } }, "cell_type": "code", @@ -433,13 +429,13 @@ "user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)" ], "outputs": [], - "execution_count": 49 + "execution_count": 9 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.565168Z", - "start_time": "2025-03-20T15:13:44.562664Z" + "end_time": "2025-03-24T09:48:11.049702Z", + "start_time": "2025-03-24T09:48:11.047621Z" } }, "cell_type": "code", @@ -450,21 +446,21 @@ "output_type": "stream", "text": [ "Property Key:\n", - "[[21364 17750 43270 40570 2897 64106]\n", - " [64901 60553 23487 56910 22974 27412]\n", - " [49276 36687 58910 60854 56432 64908]\n", - " [17816 7963 33663 13564 43318 39697]\n", - " [ 6300 40793 34908 48633 47026 16580]]\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": 50 + "execution_count": 10 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.583641Z", - "start_time": "2025-03-20T15:13:44.581757Z" + "end_time": "2025-03-24T09:48:11.080213Z", + "start_time": "2025-03-24T09:48:11.077878Z" } }, "cell_type": "code", @@ -474,17 +470,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Key: [65482 44549 30799 45221 44404 5844 32654 14244 48015 17243]\n" + "Passcode Key: [43421 54264 31716 41010 25784 51749 8406 27083 36329 63965]\n" ] } ], - "execution_count": 51 + "execution_count": 11 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.624946Z", - "start_time": "2025-03-20T15:13:44.622774Z" + "end_time": "2025-03-24T09:48:11.120295Z", + "start_time": "2025-03-24T09:48:11.118074Z" } }, "cell_type": "code", @@ -494,17 +490,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mask Key: [36463 57168 30740 46459 21013 40282 33046 14986 26644 13]\n" + "Mask Key: [27744 6339 46141 38859 23071 28613 2855 48530 24494 59329]\n" ] } ], - "execution_count": 52 + "execution_count": 12 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.653291Z", - "start_time": "2025-03-20T15:13:44.651215Z" + "end_time": "2025-03-24T09:48:11.158895Z", + "start_time": "2025-03-24T09:48:11.156729Z" } }, "cell_type": "code", @@ -514,17 +510,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Combined Position Key: [32765 21433 32527 38517 27021 45380]\n" + "Combined Position Key: [42763 65304 30825 5938 4970 58530]\n" ] } ], - "execution_count": 53 + "execution_count": 13 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.682695Z", - "start_time": "2025-03-20T15:13:44.680482Z" + "end_time": "2025-03-24T09:48:11.183517Z", + "start_time": "2025-03-24T09:48:11.181587Z" } }, "cell_type": "code", @@ -534,17 +530,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Position Key = combined_pos_key XOR customer_pos_key: [10261 58366 52405 60992 56352 18216]\n" + "User Position Key = combined_pos_key XOR customer_pos_key: [46883 788 17499 12342 17274 44356]\n" ] } ], - "execution_count": 54 + "execution_count": 14 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.713223Z", - "start_time": "2025-03-20T15:13:44.710535Z" + "end_time": "2025-03-24T09:48:11.207260Z", + "start_time": "2025-03-24T09:48:11.204729Z" } }, "cell_type": "code", @@ -560,16 +556,16 @@ "output_type": "stream", "text": [ "Combined Position to Properties Map:\n", - "32765: [21364 64901 49276 17816 6300]\n", - "21433: [17750 60553 36687 7963 40793]\n", - "32527: [43270 23487 58910 33663 34908]\n", - "38517: [40570 56910 60854 13564 48633]\n", - "27021: [ 2897 22974 56432 43318 47026]\n", - "45380: [64106 27412 64908 39697 16580]\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": 55 + "execution_count": 15 }, { "metadata": {}, @@ -586,8 +582,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:44.732450Z", - "start_time": "2025-03-20T15:13:44.729920Z" + "end_time": "2025-03-24T09:48:11.235048Z", + "start_time": "2025-03-24T09:48:11.232871Z" } }, "cell_type": "code", @@ -599,7 +595,7 @@ "encoded_mask = user_cipher.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 56 + "execution_count": 16 }, { "metadata": {}, @@ -616,8 +612,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:45.055535Z", - "start_time": "2025-03-20T15:13:44.751147Z" + "end_time": "2025-03-24T09:48:11.484858Z", + "start_time": "2025-03-24T09:48:11.252168Z" } }, "cell_type": "code", @@ -631,7 +627,7 @@ "passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode(\"utf-8\")" ], "outputs": [], - "execution_count": 57 + "execution_count": 17 }, { "metadata": {}, @@ -645,8 +641,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:45.367717Z", - "start_time": "2025-03-20T15:13:45.061644Z" + "end_time": "2025-03-24T09:48:11.722985Z", + "start_time": "2025-03-24T09:48:11.488809Z" } }, "cell_type": "code", @@ -664,20 +660,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [24 13 20 21 4 17]\n", - "Key 1: [ 6 1 26 27 28 29]\n", - "Key 2: [18 7 8 15 16 23]\n", - "Key 3: [ 0 19 2 3 22 11]\n", - "Key 4: [12 25 14 9 10 5]\n", - "User Passcode: [4, 9, 5, 18]\n", + "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", - " [0, 4, 4, 2]\n", + " [3, 1, 4, 3]\n", "\n" ] } ], - "execution_count": 58 + "execution_count": 18 }, { "metadata": {}, @@ -704,8 +700,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:45.379898Z", - "start_time": "2025-03-20T15:13:45.377353Z" + "end_time": "2025-03-24T09:48:11.731909Z", + "start_time": "2025-03-24T09:48:11.729517Z" } }, "cell_type": "code", @@ -718,7 +714,7 @@ "user_position_key = customer.cipher.position_key ^ user.cipher.combined_position_key" ], "outputs": [], - "execution_count": 59 + "execution_count": 19 }, { "metadata": {}, @@ -732,8 +728,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:45.391901Z", - "start_time": "2025-03-20T15:13:45.389499Z" + "end_time": "2025-03-24T09:48:11.750757Z", + "start_time": "2025-03-24T09:48:11.748661Z" } }, "cell_type": "code", @@ -743,7 +739,7 @@ "assert passcode_property_indices == presumed_property_indices\n" ], "outputs": [], - "execution_count": 60 + "execution_count": 20 }, { "metadata": {}, @@ -753,8 +749,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:45.711702Z", - "start_time": "2025-03-20T15:13:45.407474Z" + "end_time": "2025-03-24T09:48:11.995883Z", + "start_time": "2025-03-24T09:48:11.760909Z" } }, "cell_type": "code", @@ -763,7 +759,7 @@ "assert valid_nkode" ], "outputs": [], - "execution_count": 61 + "execution_count": 21 }, { "metadata": {}, @@ -779,8 +775,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:46.331470Z", - "start_time": "2025-03-20T15:13:45.715778Z" + "end_time": "2025-03-24T09:48:12.479529Z", + "start_time": "2025-03-24T09:48:12.009222Z" } }, "cell_type": "code", @@ -806,15 +802,15 @@ "output_type": "stream", "text": [ "Old User Cipher and Mask\n", - "mask: xetVN/8taK1lhtrDjt4w0M4pprQ=, code: $2b$12$6JzDrPs.dAb0iOIvm8afKuwf.Z8qKtg89Nnhx..tlBOD5y1MYMR4y\n", + "mask: nheBuPsWD2zInvscNhsJO6Wy0kU=, code: $2b$12$bsV3i1BsLH6nHCZOPVHns.b1ARmpuJETxFPZohcPG2OKO9Mr3B1du\n", "\n", "New User Cipher and Mask\n", - "mask: WsKTIcZVngijEKlMfoF2UG5Rz9I=, code: $2b$12$jvQ..z4tPFII5dLXP0D2LOPrypDSB7yoRH6E0SZPO/yIIcZVtgCTS\n", + "mask: ionSnD9+/0DS5ul8+wMyi3PNCRA=, code: $2b$12$MZejDYT1GDoyE0w1TdFGCedyBD4BY2n6VjLQW73TwJPLyjiimdpA2\n", "\n" ] } ], - "execution_count": 62 + "execution_count": 22 }, { "metadata": {}, @@ -827,8 +823,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:46.344092Z", - "start_time": "2025-03-20T15:13:46.339802Z" + "end_time": "2025-03-24T09:48:12.490144Z", + "start_time": "2025-03-24T09:48:12.486157Z" } }, "cell_type": "code", @@ -841,7 +837,7 @@ "new_pos = customer.cipher.position_key" ], "outputs": [], - "execution_count": 63 + "execution_count": 23 }, { "metadata": {}, @@ -861,8 +857,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:46.361420Z", - "start_time": "2025-03-20T15:13:46.359046Z" + "end_time": "2025-03-24T09:48:12.503462Z", + "start_time": "2025-03-24T09:48:12.501409Z" } }, "cell_type": "code", @@ -875,7 +871,7 @@ " user.cipher.property_key = user.cipher.property_key ^ props_xor" ], "outputs": [], - "execution_count": 64 + "execution_count": 24 }, { "metadata": {}, @@ -888,8 +884,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-20T15:13:46.679562Z", - "start_time": "2025-03-20T15:13:46.371777Z" + "end_time": "2025-03-24T09:48:12.759002Z", + "start_time": "2025-03-24T09:48:12.522251Z" } }, "cell_type": "code", @@ -904,7 +900,7 @@ " user.renew = False" ], "outputs": [], - "execution_count": 65 + "execution_count": 25 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 903eb64..7763d02 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,8 +1,5 @@ from dataclasses import dataclass from uuid import UUID, uuid4 - -import numpy as np - from src.customer_cipher import CustomerCipher from src.models import NKodePolicy from src.user import User diff --git a/src/utils.py b/src/utils.py index 40b1593..cd70ca9 100644 --- a/src/utils.py +++ b/src/utils.py @@ -28,3 +28,7 @@ def keypad_md_table(keypad_list: np.ndarray, keypad_size: KeypadSize) -> str: table += "|".join([str(prop) for prop in keypad[key]]) table += "|" return table + +def select_keys_with_passcode_values(user_passcode_idxs: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]: + indices = [np.where(keypad == prop)[0][0] for prop in user_passcode_idxs] + return [int(index // props_per_key) for index in indices] -- 2.49.1 From 538dc6b17ca50c8bf324ea4b10e2bdc70828090d Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 24 Mar 2025 09:30:16 -0500 Subject: [PATCH 50/85] add login_diagram --- docs/enrollment_diagram.md | 32 +++++++++---------- docs/enrollment_diagram.template.md | 10 +++--- docs/login_diagram.md | 22 +++++++++++++ docs/login_diagram.template.md | 22 +++++++++++++ ...enrollment_md.py => render_diagrams_md.py} | 32 +++++++++++++------ 5 files changed, 87 insertions(+), 31 deletions(-) create mode 100644 docs/login_diagram.md create mode 100644 docs/login_diagram.template.md rename docs/{render_enrollment_md.py => render_diagrams_md.py} (72%) diff --git a/docs/enrollment_diagram.md b/docs/enrollment_diagram.md index e6c52bd..0909c59 100644 --- a/docs/enrollment_diagram.md +++ b/docs/enrollment_diagram.md @@ -4,34 +4,34 @@ sequenceDiagram participant User participant Client participant Server - Note over User,Client: Enrollment + Note over User,Server: Enrollment Client->>User: Signup Form Note left of User: email: user@example.com User->>Client: Submit Email Client->>+Server: Signup Session: user@example.com Server->>Server: Create Signup Session - Note over User,Client: Set nKode + Note over User,Server: Set nKode Server-->>-Client: signup_session_id, set_keypad, icons - Note left of Server: signup_session_id:
92a2a1c6-3146-4d47-ac00-5160a7ebf104 - Note left of Server: set_keypad:
Key 0: [ 7 14 3 28 23]
Key 1: [ 1 26 15 10 5]
Key 2: [13 20 27 4 29]
Key 3: [25 8 21 16 11]
Key 4: [19 2 9 22 17]
+ Note left of Server: signup_session_id:
e029138e-fb64-49ea-a6bc-b6b5affbf263 + Note left of Server: set_keypad:
Key 0: [18 7 20 22 23]
Key 1: [12 13 8 28 29]
Key 2: [24 25 26 16 5]
Key 3: [ 6 19 2 10 11]
Key 4: [ 0 1 14 4 17]
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: ['🐶' '🥳' '🤯' '🐸' '👾']
- Note left of User: User icons: ['😍' '🤓' '😇' '😡'] - User->>Client: Set Key Selection: [0, 2, 0, 0] - Client->>+Server: Set nKode:
92a2a1c6-3146-4d47-ac00-5160a7ebf104
[0, 2, 0, 0] + Note left of Client: Key 0: ['🐱' '😡' '🦁' '🐸' '🐙']
Key 1: ['🤔' '🙃' '😱' '🍕' '🎉']
Key 2: ['🦄' '🌟' '⚡' '👽' '😎']
Key 3: ['🥺' '🐶' '🥳' '🥰' '😴']
Key 4: ['😀' '😂' '😇' '🤓' '👾']
+ Note left of User: User icons: ['🐱' '🥳' '🐶' '⚡'] + User->>Client: Set Key Selection: [0, 3, 3, 2] + Client->>+Server: Set nKode:
e029138e-fb64-49ea-a6bc-b6b5affbf263
[0, 3, 3, 2] Server->>Server: Disperse Set Keypad - Note over User,Client: Confirm nKode + Note over User,Server: Confirm nKode Server-->>-Client: signup_session_id, confirm_keypad, icons - Note left of Server: signup_session_id:
92a2a1c6-3146-4d47-ac00-5160a7ebf104 - Note left of Server: confirm_keypad:
Key 0: [ 7 26 21 4 17]
Key 1: [19 20 3 16 5]
Key 2: [ 1 8 9 28 29]
Key 3: [13 14 15 22 11]
Key 4: [25 2 27 10 23]
+ Note left of Server: signup_session_id:
e029138e-fb64-49ea-a6bc-b6b5affbf263 + Note left of Server: confirm_keypad:
Key 0: [ 6 7 8 16 17]
Key 1: [ 0 13 2 22 5]
Key 2: [24 19 14 28 23]
Key 3: [18 1 26 10 29]
Key 4: [12 25 20 4 11]
Client->>Client: Order Icons by keypad Client->>User: Display Keypad - Note left of Client: Key 0: ['😡' '⚡' '🐻' '🤓' '👾']
Key 1: ['🐶' '🦁' '😍' '👽' '😎']
Key 2: ['😂' '😱' '🤯' '🍕' '🎉']
Key 3: ['🙃' '😇' '🤖' '🐸' '😴']
Key 4: ['🌟' '🥳' '🔥' '🥰' '🐙']
- Note left of User: User icons: ['😍' '🤓' '😇' '😡'] - User->>Client: Key Selection: [1, 0, 3, 0] - Client->>+Server: Confirm nKode:
92a2a1c6-3146-4d47-ac00-5160a7ebf104
[1, 0, 3, 0] + Note left of Client: Key 0: ['🥺' '😡' '😱' '👽' '👾']
Key 1: ['😀' '🙃' '🥳' '🐸' '😎']
Key 2: ['🦄' '🐶' '😇' '🍕' '🐙']
Key 3: ['🐱' '😂' '⚡' '🥰' '🎉']
Key 4: ['🤔' '🌟' '🦁' '🤓' '😴']
+ Note left of User: User icons: ['🐱' '🥳' '🐶' '⚡'] + User->>Client: Key Selection: [3, 1, 2, 3] + Client->>+Server: Confirm nKode:
e029138e-fb64-49ea-a6bc-b6b5affbf263
[3, 1, 2, 3] Server->>Server: Create User - Server-->-Client: Success + Server-->>-Client: Success ``` \ No newline at end of file diff --git a/docs/enrollment_diagram.template.md b/docs/enrollment_diagram.template.md index e2c239c..50c06c7 100644 --- a/docs/enrollment_diagram.template.md +++ b/docs/enrollment_diagram.template.md @@ -4,17 +4,17 @@ sequenceDiagram participant User participant Client participant Server - Note over User,Client: Enrollment + Note over User,Server: Enrollment Client->>User: Signup Form Note left of User: email: {{ email }} User->>Client: Submit Email Client->>+Server: Signup Session: {{ email }} Server->>Server: Create Signup Session - Note over User,Client: Set nKode + Note over User,Server: Set nKode Server-->>-Client: signup_session_id, set_keypad, icons Note left of Server: signup_session_id:
{{ signup_session_id }} Note left of Server: set_keypad:
{{set_keypad}} - Note left of Server: Icons:
{{icon_matrix}} + Note left of Server: Icons:
{{keypad_icons}} Client->>Client: Order Icons by keypad Client->>User: Display Keypad Note left of Client: {{ ordered_keypad }} @@ -22,7 +22,7 @@ sequenceDiagram User->>Client: Set Key Selection: {{ selected_keys_set }} Client->>+Server: Set nKode:
{{ signup_session_id }}
{{ selected_keys_set }} Server->>Server: Disperse Set Keypad - Note over User,Client: Confirm nKode + Note over User,Server: Confirm nKode Server-->>-Client: signup_session_id, confirm_keypad, icons Note left of Server: signup_session_id:
{{ signup_session_id }} Note left of Server: confirm_keypad:
{{confirm_keypad}} @@ -33,5 +33,5 @@ sequenceDiagram User->>Client: Key Selection: {{ confirm_key_selection }} Client->>+Server: Confirm nKode:
{{ signup_session_id }}
{{ confirm_key_selection }} Server->>Server: Create User - Server-->-Client: Success + Server-->>-Client: Success ``` \ No newline at end of file diff --git a/docs/login_diagram.md b/docs/login_diagram.md new file mode 100644 index 0000000..b1e4506 --- /dev/null +++ b/docs/login_diagram.md @@ -0,0 +1,22 @@ +# nKode Login + +```mermaid +sequenceDiagram + participant User + participant Client + participant Server + Note over User,Server: Login + Client->>User: Login Form + 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: [18 7 20 27 22 23]
Key 1: [12 13 8 3 28 29]
Key 2: [24 25 26 9 16 5]
Key 3: [ 6 19 2 21 10 11]
Key 4: [ 0 1 14 15 4 17]
+ 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: ['😀' '😂' '😇' '🤖' '🤓' '👾']
+ Note left of User: User passcode icons: ['🐱' '🥳' '🐶' '⚡'] + User->>Client: Selected Keys
[0, 3, 3, 2] + Client->>Server: Login:
email: user@example.com
selected_keys: [0, 3, 3, 2] + Server-->>Client: Success +``` \ No newline at end of file diff --git a/docs/login_diagram.template.md b/docs/login_diagram.template.md new file mode 100644 index 0000000..ba5ad57 --- /dev/null +++ b/docs/login_diagram.template.md @@ -0,0 +1,22 @@ +# nKode Login + +```mermaid +sequenceDiagram + participant User + participant Client + participant Server + Note over User,Server: Login + Client->>User: Login Form + Note left of User: email: {{ email }} + User->>Server: Submit Email + Server->>Client: login_keypad, icons + Note left of Server: Login Keypad:
{{ login_keypad }} + Note left of Server: Icons:
{{ keypad_icons }} + Client->>Client: Order Icons + Client->>User: Display Keypad + Note left of Client: {{ ordered_login_icons }} + Note left of User: User passcode icons: {{ passcode_user_icons }} + User->>Client: Selected Keys
{{selected_keys_login}} + Client->>Server: Login:
email: {{email}}
selected_keys: {{selected_keys_login}} + Server-->>Client: Success +``` diff --git a/docs/render_enrollment_md.py b/docs/render_diagrams_md.py similarity index 72% rename from docs/render_enrollment_md.py rename to docs/render_diagrams_md.py index e5e7221..6696a5c 100644 --- a/docs/render_enrollment_md.py +++ b/docs/render_diagrams_md.py @@ -47,9 +47,9 @@ def display_icons(icons_array: np.ndarray, kp: KeypadSize) -> str: icons += "]" return icons -def display_icons_keypad(icons_array: np.ndarray, kp: KeypadSize) -> str: +def display_icons_keypad(icons_array: np.ndarray, props_per_key: int) -> str: icons = "" - for idx, row in enumerate(icons_array.reshape(-1, kp.numb_of_keys)): + for idx, row in enumerate(icons_array.reshape(-1, props_per_key)): icons += f"Key {idx}: " icons += str(row) icons += "
" @@ -93,15 +93,27 @@ if __name__ == "__main__": context = { "email": "user@example.com", "signup_session_id": signup_session_id, - "set_keypad": display_icons_keypad(set_signup_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size), - "icon_matrix": display_icons(user_icons, keypad_size), - "ordered_keypad": display_icons_keypad(ordered_set_icons, keypad_size), + "set_keypad": display_icons_keypad(set_signup_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), + "keypad_icons": display_icons(user_icons, keypad_size), + "ordered_keypad": display_icons_keypad(ordered_set_icons, keypad_size.numb_of_keys), "passcode_user_icons": str(user_icons[passcode_property_indices]), "selected_keys_set": str(selected_keys_set), - "confirm_keypad": display_icons_keypad(confirm_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size), - "confirm_ordered_keypad": display_icons_keypad(ordered_confirm_icons.reshape(-1, keypad_size.numb_of_keys), keypad_size), + "confirm_keypad": display_icons_keypad(confirm_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), + "confirm_ordered_keypad": display_icons_keypad(ordered_confirm_icons.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), "confirm_key_selection": selected_keys_confirm } - - # Render the template - render_markdown_template(Path("./enrollment_diagram.template.md"), Path("./enrollment_diagram.md"), context) \ No newline at end of file + render_markdown_template(Path("./enrollment_diagram.template.md"), Path("./enrollment_diagram.md"), context) + login_keypad = api.get_login_keypad(username, customer_id) + selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, + keypad_size.props_per_key) + ordered_login_keypad = user_icons[login_keypad] + context = { + "email": "user@example.com", + "keypad_icons": display_icons(user_icons, keypad_size), + "login_keypad": display_icons_keypad(login_keypad.reshape(-1, keypad_size.props_per_key), keypad_size.props_per_key), + "ordered_login_icons": display_icons_keypad(ordered_login_keypad.reshape(-1, keypad_size.props_per_key),keypad_size.props_per_key), + "passcode_user_icons": str(user_icons[passcode_property_indices]), + "selected_keys_login": str(selected_keys_login) + } + api.login(customer_id, username, selected_keys_login) + render_markdown_template(Path("./login_diagram.template.md"), Path("./login_diagram.md"), context) -- 2.49.1 From 0f7bda0942b4daf0c4c48c9938244e4b4de24ee0 Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 24 Mar 2025 09:52:52 -0500 Subject: [PATCH 51/85] refactor docs --- docs/enrollment_diagram.md | 24 +++++++++---------- docs/login_diagram.md | 10 ++++---- docs/{ => scripts}/render_diagrams_md.py | 12 ++-------- .../enrollment_diagram.template.md | 0 .../{ => templates}/login_diagram.template.md | 0 requirements.txt | 3 ++- 6 files changed, 21 insertions(+), 28 deletions(-) rename docs/{ => scripts}/render_diagrams_md.py (90%) rename docs/{ => templates}/enrollment_diagram.template.md (100%) rename docs/{ => templates}/login_diagram.template.md (100%) diff --git a/docs/enrollment_diagram.md b/docs/enrollment_diagram.md index 0909c59..304fe77 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:
e029138e-fb64-49ea-a6bc-b6b5affbf263 - Note left of Server: set_keypad:
Key 0: [18 7 20 22 23]
Key 1: [12 13 8 28 29]
Key 2: [24 25 26 16 5]
Key 3: [ 6 19 2 10 11]
Key 4: [ 0 1 14 4 17]
+ Note left of Server: signup_session_id:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a + Note left of Server: set_keypad:
Key 0: [12 13 20 21 16]
Key 1: [ 6 1 14 9 4]
Key 2: [ 0 7 26 15 28]
Key 3: [24 19 2 27 10]
Key 4: [18 25 8 3 22]
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: ['😀' '😂' '😇' '🤓' '👾']
- Note left of User: User icons: ['🐱' '🥳' '🐶' '⚡'] - User->>Client: Set Key Selection: [0, 3, 3, 2] - Client->>+Server: Set nKode:
e029138e-fb64-49ea-a6bc-b6b5affbf263
[0, 3, 3, 2] + Note left of Client: Key 0: ['🤔' '🙃' '🦁' '🐻' '👽']
Key 1: ['🥺' '😂' '😇' '🤯' '🤓']
Key 2: ['😀' '😡' '⚡' '🤖' '🍕']
Key 3: ['🦄' '🐶' '🥳' '🔥' '🥰']
Key 4: ['🐱' '🌟' '😱' '😍' '🐸']
+ Note left of User: User icons: ['🤓' '🐶' '🦄' '👽'] + User->>Client: Set Key Selection: [1, 3, 3, 0] + Client->>+Server: Set nKode:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a
[1, 3, 3, 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:
e029138e-fb64-49ea-a6bc-b6b5affbf263 - Note left of Server: confirm_keypad:
Key 0: [ 6 7 8 16 17]
Key 1: [ 0 13 2 22 5]
Key 2: [24 19 14 28 23]
Key 3: [18 1 26 10 29]
Key 4: [12 25 20 4 11]
+ Note left of Server: signup_session_id:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a + Note left of Server: confirm_keypad:
Key 0: [ 6 13 8 15 10]
Key 1: [24 7 14 3 16]
Key 2: [12 25 2 9 28]
Key 3: [ 0 1 20 27 22]
Key 4: [18 19 26 21 4]
Client->>Client: Order Icons by keypad Client->>User: Display Keypad - Note left of Client: Key 0: ['🥺' '😡' '😱' '👽' '👾']
Key 1: ['😀' '🙃' '🥳' '🐸' '😎']
Key 2: ['🦄' '🐶' '😇' '🍕' '🐙']
Key 3: ['🐱' '😂' '⚡' '🥰' '🎉']
Key 4: ['🤔' '🌟' '🦁' '🤓' '😴']
- Note left of User: User icons: ['🐱' '🥳' '🐶' '⚡'] - User->>Client: Key Selection: [3, 1, 2, 3] - Client->>+Server: Confirm nKode:
e029138e-fb64-49ea-a6bc-b6b5affbf263
[3, 1, 2, 3] + Note left of Client: Key 0: ['🥺' '🙃' '😱' '🤖' '🥰']
Key 1: ['🦄' '😡' '😇' '😍' '👽']
Key 2: ['🤔' '🌟' '🥳' '🤯' '🍕']
Key 3: ['😀' '😂' '🦁' '🔥' '🐸']
Key 4: ['🐱' '🐶' '⚡' '🐻' '🤓']
+ Note left of User: User icons: ['🤓' '🐶' '🦄' '👽'] + User->>Client: Key Selection: [4, 4, 1, 1] + Client->>+Server: Confirm nKode:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a
[4, 4, 1, 1] 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 b1e4506..1ae4167 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: [18 7 20 27 22 23]
Key 1: [12 13 8 3 28 29]
Key 2: [24 25 26 9 16 5]
Key 3: [ 6 19 2 21 10 11]
Key 4: [ 0 1 14 15 4 17]
+ Note left of Server: Login Keypad:
Key 0: [12 13 20 21 16 23]
Key 1: [ 6 1 14 9 4 29]
Key 2: [ 0 7 26 15 28 11]
Key 3: [24 19 2 27 10 5]
Key 4: [18 25 8 3 22 17]
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: ['😀' '😂' '😇' '🤖' '🤓' '👾']
- Note left of User: User passcode icons: ['🐱' '🥳' '🐶' '⚡'] - User->>Client: Selected Keys
[0, 3, 3, 2] - Client->>Server: Login:
email: user@example.com
selected_keys: [0, 3, 3, 2] + Note left of Client: Key 0: ['🤔' '🙃' '🦁' '🐻' '👽' '🐙']
Key 1: ['🥺' '😂' '😇' '🤯' '🤓' '🎉']
Key 2: ['😀' '😡' '⚡' '🤖' '🍕' '😴']
Key 3: ['🦄' '🐶' '🥳' '🔥' '🥰' '😎']
Key 4: ['🐱' '🌟' '😱' '😍' '🐸' '👾']
+ Note left of User: User passcode icons: ['🤓' '🐶' '🦄' '👽'] + User->>Client: Selected Keys
[1, 3, 3, 0] + Client->>Server: Login:
email: user@example.com
selected_keys: [1, 3, 3, 0] Server-->>Client: Success ``` \ No newline at end of file diff --git a/docs/render_diagrams_md.py b/docs/scripts/render_diagrams_md.py similarity index 90% rename from docs/render_diagrams_md.py rename to docs/scripts/render_diagrams_md.py index 6696a5c..ec97b54 100644 --- a/docs/render_diagrams_md.py +++ b/docs/scripts/render_diagrams_md.py @@ -11,14 +11,6 @@ from src.utils import select_keys_with_passcode_values def render_markdown_template(template_path, output_path, context): - """ - Render a Jinja2 markdown template with the given context and save to output_path. - - Args: - template_path (Path): Path to the template file - output_path (Path): Path where the rendered file will be saved - context (dict): Template variables - """ template_dir = template_path.parent template_file = template_path.name @@ -102,7 +94,7 @@ if __name__ == "__main__": "confirm_ordered_keypad": display_icons_keypad(ordered_confirm_icons.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), "confirm_key_selection": selected_keys_confirm } - render_markdown_template(Path("./enrollment_diagram.template.md"), Path("./enrollment_diagram.md"), context) + render_markdown_template(Path("../templates/enrollment_diagram.template.md"), Path("../enrollment_diagram.md"), context) login_keypad = api.get_login_keypad(username, customer_id) selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key) @@ -116,4 +108,4 @@ if __name__ == "__main__": "selected_keys_login": str(selected_keys_login) } api.login(customer_id, username, selected_keys_login) - render_markdown_template(Path("./login_diagram.template.md"), Path("./login_diagram.md"), context) + render_markdown_template(Path("../templates/login_diagram.template.md"), Path("../login_diagram.md"), context) diff --git a/docs/enrollment_diagram.template.md b/docs/templates/enrollment_diagram.template.md similarity index 100% rename from docs/enrollment_diagram.template.md rename to docs/templates/enrollment_diagram.template.md diff --git a/docs/login_diagram.template.md b/docs/templates/login_diagram.template.md similarity index 100% rename from docs/login_diagram.template.md rename to docs/templates/login_diagram.template.md diff --git a/requirements.txt b/requirements.txt index dcd35dd..e7517e0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ bcrypt~=4.1.3 numpy~=2.0.0 pytest~=8.2.2 -ipython~=8.25.0 \ No newline at end of file +ipython~=8.25.0 +jinja2~=3.1.4 \ No newline at end of file -- 2.49.1 From 1846dc1065fcddcaa1547161fb4cc39d35a8b5be Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 24 Mar 2025 10:04:11 -0500 Subject: [PATCH 52/85] update emojis --- docs/enrollment_diagram.md | 26 +++++++-------- docs/login_diagram.md | 12 +++---- docs/scripts/render_diagrams_md.py | 51 +++++++----------------------- docs/scripts/utils.py | 46 +++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 59 deletions(-) create mode 100644 docs/scripts/utils.py diff --git a/docs/enrollment_diagram.md b/docs/enrollment_diagram.md index 304fe77..fef2bd0 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:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a - Note left of Server: set_keypad:
Key 0: [12 13 20 21 16]
Key 1: [ 6 1 14 9 4]
Key 2: [ 0 7 26 15 28]
Key 3: [24 19 2 27 10]
Key 4: [18 25 8 3 22]
- Note left of Server: 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: 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: ['🐱' '🌟' '😱' '😍' '🐸']
- Note left of User: User icons: ['🤓' '🐶' '🦄' '👽'] - User->>Client: Set Key Selection: [1, 3, 3, 0] - Client->>+Server: Set nKode:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a
[1, 3, 3, 0] + 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] 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:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a - Note left of Server: confirm_keypad:
Key 0: [ 6 13 8 15 10]
Key 1: [24 7 14 3 16]
Key 2: [12 25 2 9 28]
Key 3: [ 0 1 20 27 22]
Key 4: [18 19 26 21 4]
+ 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]
Client->>Client: Order Icons by keypad Client->>User: Display Keypad - Note left of Client: Key 0: ['🥺' '🙃' '😱' '🤖' '🥰']
Key 1: ['🦄' '😡' '😇' '😍' '👽']
Key 2: ['🤔' '🌟' '🥳' '🤯' '🍕']
Key 3: ['😀' '😂' '🦁' '🔥' '🐸']
Key 4: ['🐱' '🐶' '⚡' '🐻' '🤓']
- Note left of User: User icons: ['🤓' '🐶' '🦄' '👽'] - User->>Client: Key Selection: [4, 4, 1, 1] - Client->>+Server: Confirm nKode:
d7e50a75-d3ff-482a-b0e7-ce99c3838c1a
[4, 4, 1, 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, 0, 2, 1] + Client->>+Server: Confirm nKode:
51b187b9-c930-4f5d-81da-c11c082f2c64
[3, 0, 2, 1] 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 1ae4167..bfa0962 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: [12 13 20 21 16 23]
Key 1: [ 6 1 14 9 4 29]
Key 2: [ 0 7 26 15 28 11]
Key 3: [24 19 2 27 10 5]
Key 4: [18 25 8 3 22 17]
- Note left of Server: 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: Icons:
[🍎,🍏,🍊,🍋,🍌,🍉
🍇,🍓,🍒,🍑,🥭,🍍
🥥,🥝,🍅,🍆,🥑,🥕
🌽,🥔,🍠,🥐,🥖,🥨
🥯,🥞,🧀,🍖,🍗,🥚
🍔,🍟,🍕,🌭,🥪,🌮
🌯,🍣,🍤,🍙,🍚,🍜
🍲,🍛,🍱,🥟,🍦,🍧
🍨,🍩,🍪,🎂,🍰,🧁] Client->>Client: Order Icons Client->>User: Display Keypad - Note left of Client: Key 0: ['🤔' '🙃' '🦁' '🐻' '👽' '🐙']
Key 1: ['🥺' '😂' '😇' '🤯' '🤓' '🎉']
Key 2: ['😀' '😡' '⚡' '🤖' '🍕' '😴']
Key 3: ['🦄' '🐶' '🥳' '🔥' '🥰' '😎']
Key 4: ['🐱' '🌟' '😱' '😍' '🐸' '👾']
- Note left of User: User passcode icons: ['🤓' '🐶' '🦄' '👽'] - User->>Client: Selected Keys
[1, 3, 3, 0] - Client->>Server: Login:
email: user@example.com
selected_keys: [1, 3, 3, 0] + 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] 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 ec97b54..09b5281 100644 --- a/docs/scripts/render_diagrams_md.py +++ b/docs/scripts/render_diagrams_md.py @@ -1,35 +1,13 @@ -# Render markdown template for nKode enrollment from pathlib import Path - import numpy as np -from jinja2 import Environment, FileSystemLoader, select_autoescape from secrets import choice from string import ascii_lowercase +from docs.scripts.utils import render_markdown_template, emojis from src.models import NKodePolicy, KeypadSize from src.nkode_api import NKodeAPI from src.utils import select_keys_with_passcode_values -def render_markdown_template(template_path, output_path, context): - template_dir = template_path.parent - template_file = template_path.name - - env = Environment( - loader=FileSystemLoader(template_dir), - autoescape=select_autoescape(['html', 'xml']), - trim_blocks=True, - lstrip_blocks=True - ) - - template = env.get_template(template_file) - rendered = template.render(**context) - - with open(output_path, 'w') as f: - f.write(rendered) - - print(f"Template rendered to {output_path}") - - def display_icons(icons_array: np.ndarray, kp: KeypadSize) -> str: icons = "[" for row in icons_array.reshape(-1, kp.numb_of_keys): @@ -49,14 +27,7 @@ def display_icons_keypad(icons_array: np.ndarray, props_per_key: int) -> str: if __name__ == "__main__": api = NKodeAPI() - user_icons = np.array([ - "😀", "😂", "🥳", "😍", "🤓", - "😎", "🥺", "😡", "😱", "🤯", - "🥰", "😴", "🤔", "🙃", "😇", - "🤖", "👽", "👾", "🐱", "🐶", - "🦁", "🐻", "🐸", "🐙", "🦄", - "🌟", "⚡", "🔥", "🍕", "🎉" - ]) + policy = NKodePolicy( max_nkode_len=10, min_nkode_len=4, @@ -64,12 +35,12 @@ if __name__ == "__main__": distinct_properties=4, ) keypad_size = KeypadSize( - numb_of_keys=5, - props_per_key=6 + numb_of_keys=6, + 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 = user_icons[set_signup_keypad] + ordered_set_icons = emojis[set_signup_keypad] username = "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) passcode_len = 4 passcode_property_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, @@ -79,16 +50,16 @@ if __name__ == "__main__": confirm_keypad = api.set_nkode(username, 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 = user_icons[confirm_keypad] + 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) context = { "email": "user@example.com", "signup_session_id": signup_session_id, "set_keypad": display_icons_keypad(set_signup_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), - "keypad_icons": display_icons(user_icons, keypad_size), + "keypad_icons": display_icons(emojis[:keypad_size.total_props], keypad_size), "ordered_keypad": display_icons_keypad(ordered_set_icons, keypad_size.numb_of_keys), - "passcode_user_icons": str(user_icons[passcode_property_indices]), + "passcode_user_icons": str(emojis[passcode_property_indices]), "selected_keys_set": str(selected_keys_set), "confirm_keypad": display_icons_keypad(confirm_keypad.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), "confirm_ordered_keypad": display_icons_keypad(ordered_confirm_icons.reshape(-1, keypad_size.numb_of_keys), keypad_size.numb_of_keys), @@ -98,13 +69,13 @@ if __name__ == "__main__": login_keypad = api.get_login_keypad(username, customer_id) selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key) - ordered_login_keypad = user_icons[login_keypad] + ordered_login_keypad = emojis[login_keypad] context = { "email": "user@example.com", - "keypad_icons": display_icons(user_icons, keypad_size), + "keypad_icons": display_icons(emojis[:keypad_size.total_props], keypad_size), "login_keypad": display_icons_keypad(login_keypad.reshape(-1, keypad_size.props_per_key), keypad_size.props_per_key), "ordered_login_icons": display_icons_keypad(ordered_login_keypad.reshape(-1, keypad_size.props_per_key),keypad_size.props_per_key), - "passcode_user_icons": str(user_icons[passcode_property_indices]), + "passcode_user_icons": str(emojis[passcode_property_indices]), "selected_keys_login": str(selected_keys_login) } api.login(customer_id, username, selected_keys_login) diff --git a/docs/scripts/utils.py b/docs/scripts/utils.py new file mode 100644 index 0000000..32531f6 --- /dev/null +++ b/docs/scripts/utils.py @@ -0,0 +1,46 @@ +import numpy as np +from jinja2 import Environment, FileSystemLoader, select_autoescape + + +emojis = np.array([ + "🍎", "🍏", "🍊", "🍋", "🍌", "🍉", "🍇", "🍓", "🍒", "🍑", + "🥭", "🍍", "🥥", "🥝", "🍅", "🍆", "🥑", "🥕", "🌽", "🥔", + "🍠", "🥐", "🥖", "🥨", "🥯", "🥞", "🧀", "🍖", "🍗", "🥚", + "🍔", "🍟", "🍕", "🌭", "🥪", "🌮", "🌯", "🍣", "🍤", "🍙", + "🍚", "🍜", "🍲", "🍛", "🍱", "🥟", "🍦", "🍧", "🍨", "🍩", + "🍪", "🎂", "🍰", "🧁", "🍫", "🍬", "🍭", "🍮", "☕", "🍵", + "🥤", "🧃", "🍷", "🍸", "🍹", "🍺", "🥂", "🥃", "🐶", "🐱", + "🐭", "🐹", "🐰", "🦊", "🐻", "🐼", "🐨", "🐯", "🦁", "🐮", + "🐷", "🐽", "🐴", "🦄", "🦓", "🦒", "🐘", "🦏", "🐪", "🐫", + "🐑", "🐐", "🐓", "🐔", "🐣", "🐤", "🐦", "🦅", "🦇", "🦉", + "🐺", "🐍", "🐢", "🦎", "🐙", "🦑", "🦐", "🦀", "🐡", "🐠", + "🐟", "🐬", "🐳", "🦈", "🐊", "🐅", "🐆", "🐾", "🦋", "🐞", + "🐝", "🐜", "🕷️", "🕸️", "🌸", "🌹", "🌺", "🌻", "🌼", "🌷", + "🌱", "🌲", "🌳", "🌴", "🌵", "🌾", "🌿", "🍀", "🍁", "🍂", + "🍃", "⭐", "🌟", "✨", "⚡️", "☄️", "☀️", "🌤️", "⛅", "🌥️", + "☁️", "🌦️", "🌧️", "⛈️", "🌩️", "❄️", "🌬️", "💨", "🌈", "☔", + "💧", "🌊", "🔥", "💥", "⛄", "🌋", "⛰️", "🏔️", "🏕️", "🏖️", + "🏜️", "🏝️", "🏞️", "🏟️", "🏠", "🏡", "🏢", "🏣", "🏤", "🏥", + "🏦", "🏨", "🏩", "🏪", "🏫", "🏬", "🏭", "🏯", "🏰", "⛪", + "⛩️", "🕌", "🕍", "🗿", "🎡", "🎢", "🎠", "🎪", "🎨", "🎬", + "🎤", "🎧", "🎼", "🎹", "🎺", "🎸", "🥁", "🎻", "🎮", "🎲" +]) + +def render_markdown_template(template_path, output_path, context: dict): + template_dir = template_path.parent + template_file = template_path.name + + env = Environment( + loader=FileSystemLoader(template_dir), + autoescape=select_autoescape(['html', 'xml']), + trim_blocks=True, + lstrip_blocks=True + ) + + template = env.get_template(template_file) + rendered = template.render(**context) + + with open(output_path, 'w') as f: + f.write(rendered) + + print(f"Template rendered to {output_path}") -- 2.49.1 From 3a9fadfc035edcd64844452b37b5651559dd118b Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 24 Mar 2025 15:24:53 -0500 Subject: [PATCH 53/85] fix nkode api; username should be specified at the beginning of the enrollment --- docs/enrollment_diagram.md | 24 +- docs/login_diagram.md | 10 +- docs/scripts/render_diagrams_md.py | 8 +- docs/templates/keypad_dispersion.template.md | 0 docs/templates/keypad_shuffle.template.md | 0 ...> Enrollment_Login_Renewal_Detailed.ipynb} | 193 +++------------- .../Enrollment_Login_Renewal_Simplified.ipynb | 210 ++++++++++++++++++ src/nkode_api.py | 21 +- src/user_signup_session.py | 3 +- test/test_nkode_api.py | 11 +- 10 files changed, 281 insertions(+), 199 deletions(-) create mode 100644 docs/templates/keypad_dispersion.template.md create mode 100644 docs/templates/keypad_shuffle.template.md rename notebooks/{Enrollment_Login_Renewal.ipynb => Enrollment_Login_Renewal_Detailed.ipynb} (86%) create mode 100644 notebooks/Enrollment_Login_Renewal_Simplified.ipynb 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] -- 2.49.1 From 851c547add741dadbbb0e7244a34a984d0d6a209 Mon Sep 17 00:00:00 2001 From: Donovan Date: Mon, 24 Mar 2025 15:34:34 -0500 Subject: [PATCH 54/85] update documentation --- .../Enrollment_Login_Renewal_Detailed.ipynb | 347 ++++++++++++------ .../Enrollment_Login_Renewal_Simplified.ipynb | 70 ++-- 2 files changed, 274 insertions(+), 143 deletions(-) diff --git a/notebooks/Enrollment_Login_Renewal_Detailed.ipynb b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb index a807a5c..c549e71 100644 --- a/notebooks/Enrollment_Login_Renewal_Detailed.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb @@ -29,18 +29,21 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T20:06:28.904351Z", - "start_time": "2025-03-24T20:06:28.864248Z" + "end_time": "2025-03-24T20:25:08.637548Z", + "start_time": "2025-03-24T20:25:08.607886Z" } }, "outputs": [], - "execution_count": 4 + "execution_count": 1 }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:25:08.642553Z", + "start_time": "2025-03-24T20:25:08.640602Z" + } + }, "cell_type": "code", - "outputs": [], - "execution_count": null, "source": [ "api = NKodeAPI()\n", "user_icons = np.array([\n", @@ -51,7 +54,9 @@ " \"🦁\", \"🐻\", \"🐸\", \"🐙\", \"🦄\",\n", " \"🌟\", \"⚡\", \"🔥\", \"🍕\", \"🎉\"\n", "])" - ] + ], + "outputs": [], + "execution_count": 2 }, { "metadata": {}, @@ -76,10 +81,13 @@ ] }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:25:08.679087Z", + "start_time": "2025-03-24T20:25:08.667792Z" + } + }, "cell_type": "code", - "outputs": [], - "execution_count": null, "source": [ "policy = NKodePolicy(\n", " max_nkode_len=10,\n", @@ -102,20 +110,62 @@ "print(f\"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": [ + "Customer Position Key: [36587 51243 16045 24580 51231 48943]\n", + "Customer Properties Key:\n", + "[23910 10306 19502 5449 54702 12273]\n", + "[53013 18581 4421 45433 39661 27006]\n", + "[16680 54596 31667 35220 1865 8499]\n", + "[37220 26796 20234 3387 44239 47346]\n", + "[55497 7967 5622 1002 13135 4901]\n", + "Position to Properties Map:\n", + "36587: [23910 53013 16680 37220 55497]\n", + "51243: [10306 18581 54596 26796 7967]\n", + "16045: [19502 4421 31667 20234 5622]\n", + "24580: [ 5449 45433 35220 3387 1002]\n", + "51231: [54702 39661 1865 44239 13135]\n", + "48943: [12273 27006 8499 47346 4901]\n" + ] + } + ], + "execution_count": 3 }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:25:08.687137Z", + "start_time": "2025-03-24T20:25:08.684437Z" + } + }, "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", + "36587: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "51243: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "16045: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "24580: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "51231: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "48943: ['😎' '😴' '👾' '🐙' '🎉']\n" + ] + } + ], + "execution_count": 4 }, { "metadata": {}, @@ -137,10 +187,13 @@ ] }, { - "metadata": {}, + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-24T20:25:08.705881Z", + "start_time": "2025-03-24T20:25:08.699280Z" + } + }, "cell_type": "code", - "outputs": [], - "execution_count": null, "source": [ "username = random_username()\n", "signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username)\n", @@ -150,7 +203,73 @@ "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: [ 6 1 8 27 16]\n", + "Key 1: [18 13 14 21 22]\n", + "Key 2: [ 0 25 2 15 4]\n", + "Key 3: [24 19 20 9 10]\n", + "Key 4: [12 7 26 3 28]\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ], + "text/markdown": "### Customer Properties Keypad" + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Key 0: [53013 10306 4421 1002 1865]\n", + "Key 1: [37220 54596 31667 3387 44239]\n", + "Key 2: [23910 7967 19502 35220 54702]\n", + "Key 3: [55497 26796 20234 45433 39661]\n", + "Key 4: [16680 18581 5622 5449 13135]\n" + ] + } + ], + "execution_count": 5 }, { "metadata": {}, @@ -163,24 +282,11 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:10.726327Z", - "start_time": "2025-03-24T09:48:10.723046Z" + "end_time": "2025-03-24T20:25:08.729881Z", + "start_time": "2025-03-24T20:25:08.726837Z" } }, "cell_type": "code", - "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, "source": [ "passcode_len = 4\n", "passcode_property_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n", @@ -189,7 +295,20 @@ "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: [22, 8, 14, 12]\n", + "User Passcode Icons: ['🐸' '😱' '😇' '🤔']\n", + "User Passcode Server-side properties: [44239 4421 31667 16680]\n", + "Selected Keys: [1, 0, 1, 4]\n" + ] + } + ], + "execution_count": 6 }, { "metadata": {}, @@ -202,8 +321,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.002815Z", - "start_time": "2025-03-24T09:48:10.759840Z" + "end_time": "2025-03-24T20:25:09.048762Z", + "start_time": "2025-03-24T20:25:08.811165Z" } }, "cell_type": "code", @@ -220,13 +339,13 @@ "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", + "Key 0: [24 25 26 27 22]\n", + "Key 1: [18 19 2 3 16]\n", + "Key 2: [ 6 13 20 15 28]\n", + "Key 3: [12 1 14 9 4]\n", + "Key 4: [ 0 7 8 21 10]\n", "Selected Keys\n", - "[1, 3, 1, 2]\n" + "[0, 4, 3, 3]\n" ] } ], @@ -240,8 +359,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.011978Z", - "start_time": "2025-03-24T09:48:11.009413Z" + "end_time": "2025-03-24T20:25:09.058216Z", + "start_time": "2025-03-24T20:25:09.055315Z" } }, "cell_type": "code", @@ -260,18 +379,18 @@ "name": "stdout", "output_type": "stream", "text": [ - "Set Key 0: ['😡' '😱' '🔥' '👽' '😴']\n", - "Confirm Key 0: ['🐶' '😱' '😍' '🍕' '👾']\n", + "Set Key 0: ['🐱' '🙃' '😇' '🐻' '🐸']\n", + "Confirm Key 0: ['🦄' '🌟' '⚡' '🔥' '🐸']\n", + "Overlapping icon 🐸\n", + "Set Key 1: ['🥺' '😂' '😱' '🔥' '👽']\n", + "Confirm Key 1: ['😀' '😡' '😱' '🐻' '🥰']\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" + "Set Key 2: ['🐱' '🙃' '😇' '🐻' '🐸']\n", + "Confirm Key 2: ['🤔' '😂' '😇' '🤯' '🤓']\n", + "Overlapping icon 😇\n", + "Set Key 3: ['🤔' '😡' '⚡' '😍' '🍕']\n", + "Confirm Key 3: ['🤔' '😂' '😇' '🤯' '🤓']\n", + "Overlapping icon 🤔\n" ] } ], @@ -295,8 +414,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.030598Z", - "start_time": "2025-03-24T09:48:11.025452Z" + "end_time": "2025-03-24T20:25:09.069308Z", + "start_time": "2025-03-24T20:25:09.064490Z" } }, "cell_type": "code", @@ -311,8 +430,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.049702Z", - "start_time": "2025-03-24T09:48:11.047621Z" + "end_time": "2025-03-24T20:25:09.087623Z", + "start_time": "2025-03-24T20:25:09.085495Z" } }, "cell_type": "code", @@ -323,11 +442,11 @@ "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" + "[[ 7202 17463 46638 52425 1136 48374]\n", + " [13320 30423 16460 16440 54741 60051]\n", + " [ 7080 35309 40115 5709 22652 59355]\n", + " [62863 16450 3293 2809 14186 52151]\n", + " [49175 8694 16139 52942 5446 1365]]\n" ] } ], @@ -336,8 +455,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.080213Z", - "start_time": "2025-03-24T09:48:11.077878Z" + "end_time": "2025-03-24T20:25:09.115003Z", + "start_time": "2025-03-24T20:25:09.112867Z" } }, "cell_type": "code", @@ -347,7 +466,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Key: [43421 54264 31716 41010 25784 51749 8406 27083 36329 63965]\n" + "Passcode Key: [64689 33923 20489 20542 33540 51906 6128 40137 14040 24585]\n" ] } ], @@ -356,8 +475,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.120295Z", - "start_time": "2025-03-24T09:48:11.118074Z" + "end_time": "2025-03-24T20:25:09.138929Z", + "start_time": "2025-03-24T20:25:09.136632Z" } }, "cell_type": "code", @@ -367,7 +486,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mask Key: [27744 6339 46141 38859 23071 28613 2855 48530 24494 59329]\n" + "Mask Key: [57275 32944 34918 33126 19845 13409 47088 47492 20658 16069]\n" ] } ], @@ -376,8 +495,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.158895Z", - "start_time": "2025-03-24T09:48:11.156729Z" + "end_time": "2025-03-24T20:25:09.171323Z", + "start_time": "2025-03-24T20:25:09.169122Z" } }, "cell_type": "code", @@ -387,7 +506,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Combined Position Key: [42763 65304 30825 5938 4970 58530]\n" + "Combined Position Key: [ 6982 56074 5098 60427 26358 45400]\n" ] } ], @@ -396,8 +515,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.183517Z", - "start_time": "2025-03-24T09:48:11.181587Z" + "end_time": "2025-03-24T20:25:09.192115Z", + "start_time": "2025-03-24T20:25:09.189931Z" } }, "cell_type": "code", @@ -407,7 +526,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Position Key = combined_pos_key XOR customer_pos_key: [46883 788 17499 12342 17274 44356]\n" + "User Position Key = combined_pos_key XOR customer_pos_key: [38317 4897 11591 35855 44777 3703]\n" ] } ], @@ -416,8 +535,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.207260Z", - "start_time": "2025-03-24T09:48:11.204729Z" + "end_time": "2025-03-24T20:25:09.213042Z", + "start_time": "2025-03-24T20:25:09.210890Z" } }, "cell_type": "code", @@ -433,12 +552,12 @@ "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" + "6982: [ 7202 13320 7080 62863 49175]\n", + "56074: [17463 30423 35309 16450 8694]\n", + "5098: [46638 16460 40115 3293 16139]\n", + "60427: [52425 16440 5709 2809 52942]\n", + "26358: [ 1136 54741 22652 14186 5446]\n", + "45400: [48374 60051 59355 52151 1365]\n" ] } ], @@ -459,8 +578,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.235048Z", - "start_time": "2025-03-24T09:48:11.232871Z" + "end_time": "2025-03-24T20:25:09.238466Z", + "start_time": "2025-03-24T20:25:09.236286Z" } }, "cell_type": "code", @@ -489,8 +608,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.484858Z", - "start_time": "2025-03-24T09:48:11.252168Z" + "end_time": "2025-03-24T20:25:09.492093Z", + "start_time": "2025-03-24T20:25:09.259155Z" } }, "cell_type": "code", @@ -518,8 +637,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.722985Z", - "start_time": "2025-03-24T09:48:11.488809Z" + "end_time": "2025-03-24T20:25:09.730838Z", + "start_time": "2025-03-24T20:25:09.495722Z" } }, "cell_type": "code", @@ -537,15 +656,15 @@ "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", + "Key 0: [ 6 1 8 27 16 11]\n", + "Key 1: [18 13 14 21 22 17]\n", + "Key 2: [ 0 25 2 15 4 23]\n", + "Key 3: [24 19 20 9 10 29]\n", + "Key 4: [12 7 26 3 28 5]\n", + "User Passcode: [22, 8, 14, 12]\n", "\n", "Selected Keys:\n", - " [3, 1, 4, 3]\n", + " [1, 0, 1, 4]\n", "\n" ] } @@ -577,8 +696,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.731909Z", - "start_time": "2025-03-24T09:48:11.729517Z" + "end_time": "2025-03-24T20:25:09.740211Z", + "start_time": "2025-03-24T20:25:09.737938Z" } }, "cell_type": "code", @@ -605,8 +724,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.750757Z", - "start_time": "2025-03-24T09:48:11.748661Z" + "end_time": "2025-03-24T20:25:09.753175Z", + "start_time": "2025-03-24T20:25:09.751006Z" } }, "cell_type": "code", @@ -626,8 +745,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:11.995883Z", - "start_time": "2025-03-24T09:48:11.760909Z" + "end_time": "2025-03-24T20:25:10.000010Z", + "start_time": "2025-03-24T20:25:09.768227Z" } }, "cell_type": "code", @@ -652,8 +771,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:12.479529Z", - "start_time": "2025-03-24T09:48:12.009222Z" + "end_time": "2025-03-24T20:25:10.476203Z", + "start_time": "2025-03-24T20:25:10.003562Z" } }, "cell_type": "code", @@ -679,10 +798,10 @@ "output_type": "stream", "text": [ "Old User Cipher and Mask\n", - "mask: nheBuPsWD2zInvscNhsJO6Wy0kU=, code: $2b$12$bsV3i1BsLH6nHCZOPVHns.b1ARmpuJETxFPZohcPG2OKO9Mr3B1du\n", + "mask: CAB5nFpQ1LM59xYy2OREr9b7Gcc=, code: $2b$12$oy6qiM687DO5qPkEBTy/V.GXIXYFkfiTmRp1oQEBXbZ10MZMV3V.6\n", "\n", "New User Cipher and Mask\n", - "mask: ionSnD9+/0DS5ul8+wMyi3PNCRA=, code: $2b$12$MZejDYT1GDoyE0w1TdFGCedyBD4BY2n6VjLQW73TwJPLyjiimdpA2\n", + "mask: 1oEiOc7ZYxkUkKlVlzNUmbvoc7k=, code: $2b$12$BAKICUuJ.gx39r29krEiu./lWS18zm60dKzfZvpSTDp3LEOzHQGN2\n", "\n" ] } @@ -700,8 +819,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:12.490144Z", - "start_time": "2025-03-24T09:48:12.486157Z" + "end_time": "2025-03-24T20:25:10.488540Z", + "start_time": "2025-03-24T20:25:10.484314Z" } }, "cell_type": "code", @@ -734,8 +853,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:12.503462Z", - "start_time": "2025-03-24T09:48:12.501409Z" + "end_time": "2025-03-24T20:25:10.504982Z", + "start_time": "2025-03-24T20:25:10.502608Z" } }, "cell_type": "code", @@ -761,8 +880,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T09:48:12.759002Z", - "start_time": "2025-03-24T09:48:12.522251Z" + "end_time": "2025-03-24T20:25:10.762626Z", + "start_time": "2025-03-24T20:25:10.523023Z" } }, "cell_type": "code", diff --git a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb index abf8e79..04612f7 100644 --- a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb @@ -19,17 +19,23 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T20:18:04.947152Z", - "start_time": "2025-03-24T20:18:04.916604Z" + "end_time": "2025-03-24T20:25:00.701367Z", + "start_time": "2025-03-24T20:25:00.698108Z" } }, "outputs": [], - "execution_count": 1 + "execution_count": 8 }, { "metadata": {}, "cell_type": "markdown", - "source": "## Initialize nKode API" + "source": [ + "## Initialize nKode API and Create Customer\n", + "#### 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": "code", @@ -51,35 +57,41 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T20:18:04.960698Z", - "start_time": "2025-03-24T20:18:04.950723Z" + "end_time": "2025-03-24T20:25:00.708966Z", + "start_time": "2025-03-24T20:25:00.704877Z" } }, "outputs": [], - "execution_count": 2 + "execution_count": 9 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## nKode Enrollment\n", - "Users enroll in three steps:\n" + "Users enroll in three steps:\n", + "1. Create Signup Session\n", + "2. Set nKode\n", + "3. Confirm nKode\n", + "\n", + "#### Create Signup Session\n", + "A user, associate with customer (or business), specifies a username and receives a signup_session_id and set_keypad. The keypad is a index array. It tells the client how to sort the user's icons." ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:18:04.989029Z", - "start_time": "2025-03-24T20:18:04.986797Z" + "end_time": "2025-03-24T20:25:00.717550Z", + "start_time": "2025-03-24T20:25:00.715159Z" } }, "cell_type": "code", "source": [ "username = random_username()\n", - "signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username)" + "signup_session_id, set_keypad = api.generate_signup_keypad(customer_id, username)" ], "outputs": [], - "execution_count": 3 + "execution_count": 10 }, { "metadata": {}, @@ -92,32 +104,32 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:18:04.997252Z", - "start_time": "2025-03-24T20:18:04.995177Z" + "end_time": "2025-03-24T20:25:00.726553Z", + "start_time": "2025-03-24T20:25:00.724282Z" } }, "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)" + "passcode_property_indices = np.random.choice(set_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n", + "selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_keypad, keypad_size.numb_of_keys)" ], "outputs": [], - "execution_count": 4 + "execution_count": 11 }, { "metadata": {}, "cell_type": "markdown", "source": [ "### Confirm nKode\n", - "Submit the set key entry to render the confirm keypad." + "The user enter then submits their key entry. The server returns the confirm_keypad, another index array and dispersion of the set_keypad." ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:18:05.239384Z", - "start_time": "2025-03-24T20:18:05.002825Z" + "end_time": "2025-03-24T20:25:00.967386Z", + "start_time": "2025-03-24T20:25:00.732130Z" } }, "cell_type": "code", @@ -128,7 +140,7 @@ "assert success" ], "outputs": [], - "execution_count": 5 + "execution_count": 12 }, { "metadata": {}, @@ -142,8 +154,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:18:05.478978Z", - "start_time": "2025-03-24T20:18:05.245767Z" + "end_time": "2025-03-24T20:25:01.204427Z", + "start_time": "2025-03-24T20:25:00.973454Z" } }, "cell_type": "code", @@ -154,24 +166,24 @@ "assert success" ], "outputs": [], - "execution_count": 6 + "execution_count": 13 }, { "metadata": {}, "cell_type": "markdown", "source": [ "## Renew Properties\n", + "Replace server-side ciphers keys and nkode hash with new values.\n", "1. Renew Customer Properties\n", "2. Renew User Keys\n", - "3. Refresh User on Login\n", - "\n" + "3. Refresh User on Login" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:18:05.950627Z", - "start_time": "2025-03-24T20:18:05.484941Z" + "end_time": "2025-03-24T20:25:01.677259Z", + "start_time": "2025-03-24T20:25:01.209950Z" } }, "cell_type": "code", @@ -183,7 +195,7 @@ "assert success" ], "outputs": [], - "execution_count": 7 + "execution_count": 14 } ], "metadata": { -- 2.49.1 From 4f4f0bcea7838efbdbaafa6839deb9788e67e042 Mon Sep 17 00:00:00 2001 From: Donovan Date: Tue, 25 Mar 2025 11:16:06 -0500 Subject: [PATCH 55/85] encipher_decipher_nkode.template.md --- .../encipher_decipher_nkode.template.md | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 docs/templates/encipher_decipher_nkode.template.md diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_nkode.template.md new file mode 100644 index 0000000..3619d3e --- /dev/null +++ b/docs/templates/encipher_decipher_nkode.template.md @@ -0,0 +1,128 @@ +# Encipher and Decipher nKode + +### Customer Policy +- max nkode length: {{ max_nkode_len }} +- number of keys: {{ numb_of_keys }} +- properties per key: {{ props_per_key }} +- total number of properties: {{ numb_of_keys * props_per_key }} + +### Customer Cipher +- property key: {{ customer_property_key }} +- position key: {{ customer_position_key }} + + +--- + +### User Cipher +- property key: {{ user_property_key }} +- passcode key: {{ pass_key }} +- combined position key: {{ combined_position_key }} +- mask key: {{ mask_key }} + +#### Combined Postion Key +```mermaid +block-beta + columns 2 + user_pos["user postion key:\n{{user_postion_key}}"] + customer_pos["customer postion key:\n{{customer_position_key}}"] + space:2 + xor(("XOR")):2 + user_pos --> xor + customer_pos --> xor + space:2 + comb_pos["combined postion key\n{{combined_postion_key}}"]:2 + xor --> comb_pos +``` + +### User Keypad +- keypad: {{ user_keypad}} +- user passcode indicies: {{ user_passcode_idxs}} + +### nKode Cipher + +#### Passcode Hash +```mermaid +block-beta +columns 2 + cprop["customer_property_key\n{{customer_property_key}}"] + uprop["user_property_key\n{{user_property_key}}"] + space:2 + xor1(("XOR")):2 + cprop --> xor1 + uprop --> xor1 + space:2 + prop["combined_properity_key\n{{combined_property_key}}"] + xor1 --> prop + pass["user_passcode_indicies\n{{user_passcode_idxs}}"] + space:2 + sel(("select\nproperites")):2 + pass --> sel + prop --> sel + space:2 + passcode["passcode vals:\n{{passcode_vals}}"]:2 + sel --> passcode + space:2 + pad["zero pad to\nmax nkode length: {{max_nkode_len}}"]:2 + passcode -->pad + space:2 + paddedpasscode["padded passcode:\n{{padded_passcode}}"] + pad --> paddedpasscode + passkey["passcode key:\n{{pass_key}}"] + space:2 + xor2(("XOR")):2 + passkey --> xor2 + paddedpasscode --> xor2 + space:2 + cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"]:2 + xor2 --> cipheredpass + space:2 + hash(("hash")):2 + cipheredpass --> hash + space:2 + cipheredhashed["hashed ciphered passcode:\n{{code}}"]:2 + hash --> cipheredhashed +``` + +#### Mask Encipher +```mermaid +block-beta + columns 3 + passcode_idx["passcode indicies:\n{{user_passcode_idxs}}"] + comb_pos["combined position key"] + cust_pos["customer position key"] + + space:3 + pad1(("Pad with\nrandom indices")) + space:1 + xor1(("XOR")) + comb_pos --> xor1 + cust_pos --> xor1 + passcode_idx --> pad1 + space:3 + padded_passcode_idx["padded passcode indices:\n{{pad_user_passcode_idxs}}"] + pad1 --> padded_passcode_idx + space:5 + propidx(["Get Postion Idx:\nmap each to\nelement mod props_per_key"]) + padded_passcode_idx --> propidx + space:5 + posidx["Passcode Position Indices:\n{{passcode_pos_idx}}"] + propidx --> posidx + space:1 + user_pos["user position key"] + xor1 --> user_pos + space:4 + sel(("select\npositions")) + user_pos --> sel + posidx --> sel + space:5 + passcode_pos["passcode positions:\n{{ordered_user_position_key}}"] + sel --> passcode_pos + mask_key["mask key\n{{mask_key}}"] + space:4 + xor2(("XOR")) + mask_key --> xor2 + passcode_pos --> xor2 + space:5 + mask["enciphered mask:\n {{mask}}"] + xor2 --> mask +``` \ No newline at end of file -- 2.49.1 From ee49b6cd535bc81afada28dbbbbd0a879e894022 Mon Sep 17 00:00:00 2001 From: Donovan Date: Tue, 25 Mar 2025 14:50:48 -0500 Subject: [PATCH 56/85] Add Validate nkode --- docs/encipher_decipher_nkode.md | 152 ++++++++++++ docs/scripts/render_diagrams_md.py | 1 - .../render_encipher_decipher_diagrams.py | 79 +++++++ .../encipher_decipher_nkode.template.md | 223 +++++++++++------- 4 files changed, 365 insertions(+), 90 deletions(-) create mode 100644 docs/encipher_decipher_nkode.md create mode 100644 docs/scripts/render_encipher_decipher_diagrams.py diff --git a/docs/encipher_decipher_nkode.md b/docs/encipher_decipher_nkode.md new file mode 100644 index 0000000..08c5808 --- /dev/null +++ b/docs/encipher_decipher_nkode.md @@ -0,0 +1,152 @@ +# Encipher and Decipher nKode + +### Customer Policy +- max nkode length: 10 +- number of keys: 6 +- properties per key: 9 +- total number of properties: 54 + +### Customer Cipher +- property key: [50817 32783 33745 35836 30092 4212 15938 64571 58354 41241 3649 38852 + 37860 47294 10501 43278 9946 60484 34443 38446 57997 32874 49251 58321 + 15849 40891 43566 53732 31357 5248 22060 15762 23773 10428 22850 7975 + 11108 23454 32723 40871 6994 35130 41638 13889 6569 20765 56191 37770 + 4448 37031 30452 43080 24525 44717] +- position key: [25392 13634 17753 25555 61290 50870 49934 53104 9796] + + +--- + +### User Cipher +- property key: [35724 37130 47630 20125 52673 62248 61979 44917 17470 11483 20317 56084 + 34699 49571 32249 15009 43246 57623 21032 62384 61961 34049 1963 5370 + 1121 47700 26633 59166 16526 38173 56846 41594 54438 8714 46049 25028 + 38681 2017 39749 42164 38277 19216 23760 35115 23020 18954 26604 50262 + 21588 62239 30226 58722 12644 39141] +- passcode key: [51522 14440 21036 1484 4829 51359 61560 41543 23848 29080] +- combined position key: [14972 5803 54986 46940 19674 32943 38431 42194 29083] +- mask key: [15948 8251 52095 4511 25902 21701 15340 4458 15137 44295] + +#### Combined Postion Key +```mermaid +block-beta + columns 2 + user_pos["user position key:\n[22860 9193 37779 54415 41904 17945 21777 27554 22495]"] + customer_pos["customer position key:\n[25392 13634 17753 25555 61290 50870 49934 53104 9796]"] + space:2 + xor(("XOR")):2 + user_pos --> xor + customer_pos --> xor + space:2 + comb_pos["combined position key\n[14972 5803 54986 46940 19674 32943 38431 42194 29083]"]:2 + xor --> comb_pos +``` + +### User Keypad +- keypad: +- user passcode indices: [22, 6, 38, 21] + +### nKode Cipher + +#### Passcode Hash +```mermaid +block-beta +columns 2 + cprop["customer_property_key\n[50817 32783 33745 35836 30092 4212 15938 64571 58354 41241 3649 38852 + 37860 47294 10501 43278 9946 60484 34443 38446 57997 32874 49251 58321 + 15849 40891 43566 53732 31357 5248 22060 15762 23773 10428 22850 7975 + 11108 23454 32723 40871 6994 35130 41638 13889 6569 20765 56191 37770 + 4448 37031 30452 43080 24525 44717]"] + uprop["user_property_key\n[35724 37130 47630 20125 52673 62248 61979 44917 17470 11483 20317 56084 + 34699 49571 32249 15009 43246 57623 21032 62384 61961 34049 1963 5370 + 1121 47700 26633 59166 16526 38173 56846 41594 54438 8714 46049 25028 + 38681 2017 39749 42164 38277 19216 23760 35115 23020 18954 26604 50262 + 21588 62239 30226 58722 12644 39141]"] + space:2 + xor1(("XOR")):2 + cprop --> xor1 + uprop --> xor1 + space:2 + prop["combined_property_key\n[19725 4357 14815 50529 47181 58204 52313 21326 42956 36290 16668 19664 + 5231 31005 21756 37807 36404 3411 54435 26014 4228 1387 51144 63275 + 14728 9711 49703 14074 15091 33181 34850 40936 34939 2742 60067 32483 + 48253 23679 58518 15123 36567 49706 65142 49002 16453 6935 48275 22492 + 17716 25528 230 19754 28329 13896]"] + xor1 --> prop + pass["user_passcode_indices\n[22, 6, 38, 21]"] + space:2 + sel(("select\properties")):2 + pass --> sel + prop --> sel + space:2 + passcode["user passcode properties:\n[51144 52313 58518 1387]"]:2 + sel --> passcode + space:2 + pad["zero pad to\nmax nkode length: 10"]:2 + passcode -->pad + space:2 + paddedpasscode["padded passcode:\n[51144 52313 58518 1387 0 0 0 0 0 0]"] + pad --> paddedpasscode + passkey["passcode key:\n[51522 14440 21036 1484 4829 51359 61560 41543 23848 29080]"] + space:2 + xor2(("XOR")):2 + passkey --> xor2 + paddedpasscode --> xor2 + space:2 + cipheredpass["ciphered passcode:\n[ 3722 62513 46778 167 4829 51359 61560 41543 23848 29080]"]:2 + xor2 --> cipheredpass + space:2 + hash(("hash")):2 + cipheredpass --> hash + space:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$AQRZP88momhTaKGYaPzdiuBybg7V49SiZ8y9PQMoT0plZrEsbYO.K"]:2 + hash --> cipheredhashed +``` + +#### Mask Encipher +```mermaid +block-beta + columns 3 + passcode_idx["passcode indices:\n[22, 6, 38, 21]"] + comb_pos["combined position key:\n[14972 5803 54986 46940 19674 32943 38431 42194 29083]"] + cust_pos["customer position key:\n[25392 13634 17753 25555 61290 50870 49934 53104 9796]"] + + space:3 + propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) + passcode_idx-->propidx + space:1 + xor1(("XOR")) + comb_pos --> xor1 + cust_pos --> xor1 + + space:3 + passcode_position_idx["passcode poition indices:\n[4, 6, 2, 3]"] + propidx --> passcode_position_idx + + space:5 + pad1(("Pad with\nrandom indices")) + passcode_position_idx --> pad1 + + space:5 + posidx["Padded Passcode Position Indices:\n[4, 6, 2, 3, 7, 1, 8, 0, 5, 4]"] + pad1 --> posidx + space:1 + user_pos["user position key:\n[22860 9193 37779 54415 41904 17945 21777 27554 22495]"] + xor1 --> user_pos + + space:4 + sel(("select positions")) + user_pos --> sel + posidx --> sel + space:5 + passcode_pos["passcode positions:\n[41904 21777 37779 54415 27554 9193 22495 22860 17945 41904]"] + sel --> passcode_pos + mask_key["mask key\n[15948 8251 52095 4511 25902 21701 15340 4458 15137 44295]"] + space:4 + xor2(("XOR")) + mask_key --> xor2 + passcode_pos --> xor2 + space:5 + mask["enciphered mask:\n [40444 29994 22764 50448 3724 30508 27699 18470 32056 3767]"] + xor2 --> mask +``` diff --git a/docs/scripts/render_diagrams_md.py b/docs/scripts/render_diagrams_md.py index 69cd0fe..fea929d 100644 --- a/docs/scripts/render_diagrams_md.py +++ b/docs/scripts/render_diagrams_md.py @@ -51,7 +51,6 @@ if __name__ == "__main__": 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(customer_id, selected_keys_confirm, signup_session_id) context = { "email": "user@example.com", diff --git a/docs/scripts/render_encipher_decipher_diagrams.py b/docs/scripts/render_encipher_decipher_diagrams.py new file mode 100644 index 0000000..88e5ee3 --- /dev/null +++ b/docs/scripts/render_encipher_decipher_diagrams.py @@ -0,0 +1,79 @@ +import base64 +import hashlib +from pathlib import Path + +import bcrypt +import numpy as np +from secrets import choice +from string import ascii_lowercase +from docs.scripts.utils import render_markdown_template, emojis +from src.models import NKodePolicy, KeypadSize +from src.nkode_api import NKodeAPI +from src.utils import select_keys_with_passcode_values + +if __name__ == "__main__": + api = NKodeAPI() + + policy = NKodePolicy( + max_nkode_len=10, + min_nkode_len=4, + distinct_positions=0, + distinct_properties=4, + ) + keypad_size = KeypadSize( + numb_of_keys=6, + props_per_key=9 + ) + customer_id = api.create_new_customer(keypad_size, policy) + customer = api.get_customer(customer_id) + 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(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] + success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id) + user = api.customers[customer_id].users[username] + combined_prop_key = user.cipher.property_key ^ customer.cipher.property_key + user_passcode = combined_prop_key[passcode_property_indices] + pad_len = customer.nkode_policy.max_nkode_len - passcode_len + padded_passcode = np.concatenate((user_passcode, np.zeros(pad_len, dtype=user_passcode.dtype))) + ciphered_passcode = padded_passcode ^ user.cipher.pass_key + passcode_prehash = base64.b64encode(hashlib.sha256(ciphered_passcode.tobytes()).digest()) + passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode("utf-8") + + padded_passcode_position_indices = customer.cipher.get_passcode_position_indices_padded( + list(passcode_property_indices), customer.nkode_policy.max_nkode_len) + user_position_key = user.cipher.combined_position_key ^ customer.cipher.position_key + ordered_user_position_key = user_position_key[padded_passcode_position_indices] + mask = ordered_user_position_key ^ user.cipher.mask_key + encoded_mask = user.cipher.encode_base64_str(mask) + context = { + "max_nkode_len": policy.max_nkode_len, + "numb_of_keys": keypad_size.numb_of_keys, + "props_per_key": keypad_size.props_per_key, + "customer_property_key": customer.cipher.property_key, + "customer_position_key": customer.cipher.position_key, + "user_property_key": user.cipher.property_key, + "pass_key": user.cipher.pass_key, + "combined_position_key": user.cipher.combined_position_key, + "user_position_key": user_position_key, + "mask_key": user.cipher.mask_key, + "user_passcode_idxs": passcode_property_indices, + "combined_property_key": combined_prop_key, + "user_passcode_props": user_passcode, + "padded_passcode": padded_passcode, + "ciphered_passcode": ciphered_passcode, + "code": passcode_hash, + "passcode_position_idxs": padded_passcode_position_indices[:passcode_len], + "pad_user_passcode_idxs": padded_passcode_position_indices, + "ordered_user_position_key":ordered_user_position_key, + "mask": mask, + } + render_markdown_template(Path("../templates/encipher_decipher_nkode.template.md"), Path("../encipher_decipher_nkode.md"), context) \ No newline at end of file diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_nkode.template.md index 3619d3e..e3bb878 100644 --- a/docs/templates/encipher_decipher_nkode.template.md +++ b/docs/templates/encipher_decipher_nkode.template.md @@ -1,128 +1,173 @@ # Encipher and Decipher nKode -### Customer Policy +## Customer Policy - max nkode length: {{ max_nkode_len }} - number of keys: {{ numb_of_keys }} - properties per key: {{ props_per_key }} - total number of properties: {{ numb_of_keys * props_per_key }} -### Customer Cipher +## Customer Cipher - property key: {{ customer_property_key }} - position key: {{ customer_position_key }} --- -### User Cipher +## User Cipher - property key: {{ user_property_key }} - passcode key: {{ pass_key }} - combined position key: {{ combined_position_key }} - mask key: {{ mask_key }} -#### Combined Postion Key +### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user postion key:\n{{user_postion_key}}"] - customer_pos["customer postion key:\n{{customer_position_key}}"] + user_pos["user position key:\n{{user_position_key}}"] + customer_pos["customer position key:\n{{customer_position_key}}"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined postion key\n{{combined_postion_key}}"]:2 + comb_pos["combined position key\n{{combined_position_key}}"]:2 xor --> comb_pos ``` -### User Keypad +## User Keypad - keypad: {{ user_keypad}} -- user passcode indicies: {{ user_passcode_idxs}} +- user passcode indices: {{ user_passcode_idxs}} -### nKode Cipher +## nKode Cipher -#### Passcode Hash +### Passcode Hash ```mermaid block-beta -columns 2 - cprop["customer_property_key\n{{customer_property_key}}"] - uprop["user_property_key\n{{user_property_key}}"] - space:2 - xor1(("XOR")):2 - cprop --> xor1 - uprop --> xor1 - space:2 - prop["combined_properity_key\n{{combined_property_key}}"] - xor1 --> prop - pass["user_passcode_indicies\n{{user_passcode_idxs}}"] - space:2 - sel(("select\nproperites")):2 - pass --> sel - prop --> sel - space:2 - passcode["passcode vals:\n{{passcode_vals}}"]:2 - sel --> passcode - space:2 - pad["zero pad to\nmax nkode length: {{max_nkode_len}}"]:2 - passcode -->pad - space:2 - paddedpasscode["padded passcode:\n{{padded_passcode}}"] - pad --> paddedpasscode - passkey["passcode key:\n{{pass_key}}"] - space:2 - xor2(("XOR")):2 - passkey --> xor2 - paddedpasscode --> xor2 - space:2 - cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"]:2 - xor2 --> cipheredpass - space:2 - hash(("hash")):2 - cipheredpass --> hash - space:2 - cipheredhashed["hashed ciphered passcode:\n{{code}}"]:2 - hash --> cipheredhashed + columns 2 + cprop["customer_property_key\n{{customer_property_key}}"] + uprop["user_property_key\n{{user_property_key}}"] + space:2 + xor1(("XOR")):2 + cprop --> xor1 + uprop --> xor1 + space:2 + prop["combined_property_key\n{{combined_property_key}}"] + xor1 --> prop + pass["user_passcode_indices\n{{user_passcode_idxs}}"] + space:2 + sel(("select\nproperties")):2 + pass --> sel + prop --> sel + space:2 + passcode["user passcode properties:\n{{user_passcode_props}}"]:2 + sel --> passcode + space:2 + pad["zero pad to\nmax nkode length: {{max_nkode_len}}"]:2 + passcode -->pad + space:2 + paddedpasscode["padded passcode:\n{{padded_passcode}}"] + pad --> paddedpasscode + passkey["passcode key:\n{{pass_key}}"] + space:2 + xor2(("XOR")):2 + passkey --> xor2 + paddedpasscode --> xor2 + space:2 + cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"]:2 + xor2 --> cipheredpass + space:2 + hash(("hash")):2 + cipheredpass --> hash + space:2 + cipheredhashed["hashed ciphered passcode:\n{{code}}"]:2 + hash --> cipheredhashed ``` -#### Mask Encipher +### Mask Encipher ```mermaid block-beta - columns 3 - passcode_idx["passcode indicies:\n{{user_passcode_idxs}}"] - comb_pos["combined position key"] - cust_pos["customer position key"] + columns 3 + passcode_idx["passcode indices:\n{{user_passcode_idxs}}"] + comb_pos["combined position key:\n{{combined_position_key}}"] + cust_pos["customer position key:\n{{customer_position_key}}"] + + space:3 + propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) + passcode_idx-->propidx + space:1 + xor1(("XOR")) + comb_pos --> xor1 + cust_pos --> xor1 + + space:3 + passcode_position_idx["passcode poition indices:\n{{passcode_position_idxs}}"] + propidx --> passcode_position_idx + + space:5 + pad1(("Pad with\nrandom indices")) + passcode_position_idx --> pad1 + + space:5 + posidx["Padded Passcode Position Indices:\n{{pad_user_passcode_idxs}}"] + pad1 --> posidx + space:1 + user_pos["user position key:\n{{user_position_key}}"] + xor1 --> user_pos + + space:4 + sel(("select positions")) + user_pos --> sel + posidx --> sel + space:5 + passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"] + sel --> passcode_pos + mask_key["mask key\n{{mask_key}}"] + space:4 + xor2(("XOR")) + mask_key --> xor2 + passcode_pos --> xor2 + space:5 + mask["enciphered mask:\n {{mask}}"] + xor2 --> mask +``` - space:3 - pad1(("Pad with\nrandom indices")) - space:1 - xor1(("XOR")) - comb_pos --> xor1 - cust_pos --> xor1 - passcode_idx --> pad1 - space:3 - padded_passcode_idx["padded passcode indices:\n{{pad_user_passcode_idxs}}"] - pad1 --> padded_passcode_idx - space:5 - propidx(["Get Postion Idx:\nmap each to\nelement mod props_per_key"]) - padded_passcode_idx --> propidx - space:5 - posidx["Passcode Position Indices:\n{{passcode_pos_idx}}"] - propidx --> posidx - space:1 - user_pos["user position key"] - xor1 --> user_pos - space:4 - sel(("select\npositions")) - user_pos --> sel - posidx --> sel - space:5 - passcode_pos["passcode positions:\n{{ordered_user_position_key}}"] - sel --> passcode_pos - mask_key["mask key\n{{mask_key}}"] - space:4 - xor2(("XOR")) - mask_key --> xor2 - passcode_pos --> xor2 - space:5 - mask["enciphered mask:\n {{mask}}"] - xor2 --> mask -``` \ No newline at end of file +### Validate nKode + +```mermaid +block-beta + columns 4 + selected_keys["selected keys:\n{{selected_keys}}"] + login_keypad["login keypad:\n{{login_keypad}}"] + mask["enciphered mask:\n {{mask}}"] + mask_key["mask key:\n{{mask_key}}"] + space:6 + + user_position_key["user position key:\n{{user_position_key}}"] + passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"] + space:4 + + space:2 + get_passcode_idxs(("recover passcode\nposition indices")) + space:7 + + passcode_pos_idxs["padded passcode position indices:\n{{pad_user_passcode_idxs}}"] + space:5 + + get_presumed_idxs(("recover passcode\nproperty indices")) + space:7 + + passcode_prop_idxs["presumed passcode property indices:\n{{user_passcode_idxs}}"] + space:7 + + sel(("select\nproperties")) + space:1 + prop["combined_property_key\n{{combined_property_key}}"] + space:5 + passcode_prop["presumed passcode properties:\n{{user_passcode_props}}"] + space:1 + cipheredhashed["hashed ciphered passcode:\n{{code}}"] + space:6 + + comp(("compare")) + +``` -- 2.49.1 From 9cd9b76741beed8ea38a096ad0ab815837423abc Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 04:08:35 -0500 Subject: [PATCH 57/85] document encipher decipher --- docs/encipher_decipher_nkode.md | 331 +++++++++++------- .../render_encipher_decipher_diagrams.py | 16 +- .../encipher_decipher_nkode.template.md | 65 +++- 3 files changed, 279 insertions(+), 133 deletions(-) diff --git a/docs/encipher_decipher_nkode.md b/docs/encipher_decipher_nkode.md index 08c5808..6be3fc4 100644 --- a/docs/encipher_decipher_nkode.md +++ b/docs/encipher_decipher_nkode.md @@ -1,152 +1,245 @@ # Encipher and Decipher nKode -### Customer Policy +## Customer Policy - max nkode length: 10 - number of keys: 6 - properties per key: 9 - total number of properties: 54 -### Customer Cipher -- property key: [50817 32783 33745 35836 30092 4212 15938 64571 58354 41241 3649 38852 - 37860 47294 10501 43278 9946 60484 34443 38446 57997 32874 49251 58321 - 15849 40891 43566 53732 31357 5248 22060 15762 23773 10428 22850 7975 - 11108 23454 32723 40871 6994 35130 41638 13889 6569 20765 56191 37770 - 4448 37031 30452 43080 24525 44717] -- position key: [25392 13634 17753 25555 61290 50870 49934 53104 9796] +## Customer Cipher +- property key: [32025 56251 54239 48726 57043 21466 36907 63872 5185 40361 24862 17007 + 15817 35928 23390 44980 24388 9693 29079 42520 27552 55989 21064 47245 + 31415 33398 15649 37888 60649 19865 33780 9215 998 23952 774 36297 + 56975 53701 54205 26202 57035 1589 27142 51011 41787 17416 14611 52249 + 24554 2826 51850 29516 15825 40218] +- position key: [12958 41262 53564 57128 61719 22386 19660 14119 45982] --- -### User Cipher -- property key: [35724 37130 47630 20125 52673 62248 61979 44917 17470 11483 20317 56084 - 34699 49571 32249 15009 43246 57623 21032 62384 61961 34049 1963 5370 - 1121 47700 26633 59166 16526 38173 56846 41594 54438 8714 46049 25028 - 38681 2017 39749 42164 38277 19216 23760 35115 23020 18954 26604 50262 - 21588 62239 30226 58722 12644 39141] -- passcode key: [51522 14440 21036 1484 4829 51359 61560 41543 23848 29080] -- combined position key: [14972 5803 54986 46940 19674 32943 38431 42194 29083] -- mask key: [15948 8251 52095 4511 25902 21701 15340 4458 15137 44295] +## User Cipher +- property key: [62675 55318 20994 6862 11923 60069 48390 49437 49481 16571 8941 56614 + 10716 5617 39088 5352 303 46293 35634 37801 3265 31330 43043 35493 + 381 22746 14834 21538 11121 1556 31651 46121 26370 30287 43028 991 + 23653 3275 18911 25823 25799 46792 47794 37441 62612 49574 53766 19506 + 22024 38690 14853 51773 46825 63850] +- passcode key: [37275 57463 39825 1075 60548 11847 57381 15417 51401 34599] +- combined position key: [10840 53435 61304 62480 44024 35399 25077 63411 2973] +- mask key: [14299 50705 35900 48569 35541 57915 43280 32407 20229 24566] -#### Combined Postion Key +### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[22860 9193 37779 54415 41904 17945 21777 27554 22495]"] - customer_pos["customer position key:\n[25392 13634 17753 25555 61290 50870 49934 53104 9796]"] + user_pos["user position key:\n[ 6342 29077 15940 11064 23279 56629 11577 49300 47107]"] + customer_pos["customer position key:\n[12958 41262 53564 57128 61719 22386 19660 14119 45982]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[14972 5803 54986 46940 19674 32943 38431 42194 29083]"]:2 + comb_pos["combined position key\n[10840 53435 61304 62480 44024 35399 25077 63411 2973]"]:2 xor --> comb_pos ``` -### User Keypad +## User Keypad - keypad: -- user passcode indices: [22, 6, 38, 21] +- user passcode indices: [26, 31, 1, 53] -### nKode Cipher +## nKode Cipher -#### Passcode Hash +### Passcode Hash ```mermaid block-beta -columns 2 - cprop["customer_property_key\n[50817 32783 33745 35836 30092 4212 15938 64571 58354 41241 3649 38852 - 37860 47294 10501 43278 9946 60484 34443 38446 57997 32874 49251 58321 - 15849 40891 43566 53732 31357 5248 22060 15762 23773 10428 22850 7975 - 11108 23454 32723 40871 6994 35130 41638 13889 6569 20765 56191 37770 - 4448 37031 30452 43080 24525 44717]"] - uprop["user_property_key\n[35724 37130 47630 20125 52673 62248 61979 44917 17470 11483 20317 56084 - 34699 49571 32249 15009 43246 57623 21032 62384 61961 34049 1963 5370 - 1121 47700 26633 59166 16526 38173 56846 41594 54438 8714 46049 25028 - 38681 2017 39749 42164 38277 19216 23760 35115 23020 18954 26604 50262 - 21588 62239 30226 58722 12644 39141]"] - space:2 - xor1(("XOR")):2 - cprop --> xor1 - uprop --> xor1 - space:2 - prop["combined_property_key\n[19725 4357 14815 50529 47181 58204 52313 21326 42956 36290 16668 19664 - 5231 31005 21756 37807 36404 3411 54435 26014 4228 1387 51144 63275 - 14728 9711 49703 14074 15091 33181 34850 40936 34939 2742 60067 32483 - 48253 23679 58518 15123 36567 49706 65142 49002 16453 6935 48275 22492 - 17716 25528 230 19754 28329 13896]"] - xor1 --> prop - pass["user_passcode_indices\n[22, 6, 38, 21]"] - space:2 - sel(("select\properties")):2 - pass --> sel - prop --> sel - space:2 - passcode["user passcode properties:\n[51144 52313 58518 1387]"]:2 - sel --> passcode - space:2 - pad["zero pad to\nmax nkode length: 10"]:2 - passcode -->pad - space:2 - paddedpasscode["padded passcode:\n[51144 52313 58518 1387 0 0 0 0 0 0]"] - pad --> paddedpasscode - passkey["passcode key:\n[51522 14440 21036 1484 4829 51359 61560 41543 23848 29080]"] - space:2 - xor2(("XOR")):2 - passkey --> xor2 - paddedpasscode --> xor2 - space:2 - cipheredpass["ciphered passcode:\n[ 3722 62513 46778 167 4829 51359 61560 41543 23848 29080]"]:2 - xor2 --> cipheredpass - space:2 - hash(("hash")):2 - cipheredpass --> hash - space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$AQRZP88momhTaKGYaPzdiuBybg7V49SiZ8y9PQMoT0plZrEsbYO.K"]:2 - hash --> cipheredhashed + columns 2 + cprop["customer_property_key\n[32025 56251 54239 48726 57043 21466 36907 63872 5185 40361 24862 17007 + 15817 35928 23390 44980 24388 9693 29079 42520 27552 55989 21064 47245 + 31415 33398 15649 37888 60649 19865 33780 9215 998 23952 774 36297 + 56975 53701 54205 26202 57035 1589 27142 51011 41787 17416 14611 52249 + 24554 2826 51850 29516 15825 40218]"] + uprop["user_property_key\n[62675 55318 20994 6862 11923 60069 48390 49437 49481 16571 8941 56614 + 10716 5617 39088 5352 303 46293 35634 37801 3265 31330 43043 35493 + 381 22746 14834 21538 11121 1556 31651 46121 26370 30287 43028 991 + 23653 3275 18911 25823 25799 46792 47794 37441 62612 49574 53766 19506 + 22024 38690 14853 51773 46825 63850]"] + space:2 + xor1(("XOR")):2 + cprop --> xor1 + uprop --> xor1 + space:2 + prop["combined_property_key\n[35274 941 33245 42136 61504 47487 11565 14493 54536 56594 17395 40777 + 5141 39337 50158 47964 24171 37128 64165 13745 26465 41175 64107 12840 + 31690 55980 1235 49186 51096 19341 63575 38870 25828 11231 43794 36374 + 33514 56590 39522 645 47628 45309 53428 21762 22447 34222 60181 32811 + 2530 39976 61583 47473 35640 25712]"] + xor1 --> prop + pass["user_passcode_indices\n[26, 31, 1, 53]"] + space:2 + sel(("select\nproperties")):2 + pass --> sel + prop --> sel + space:2 + passcode["user passcode properties:\n[ 1235 38870 941 25712]"]:2 + sel --> passcode + space:2 + pad["zero pad to\nmax nkode length: 10"]:2 + passcode -->pad + space:2 + paddedpasscode["padded passcode:\n[ 1235 38870 941 25712 0 0 0 0 0 0]"] + pad --> paddedpasscode + passkey["passcode key:\n[37275 57463 39825 1075 60548 11847 57381 15417 51401 34599]"] + space:2 + xor2(("XOR")):2 + passkey --> xor2 + paddedpasscode --> xor2 + space:2 + cipheredpass["ciphered passcode:\n[38216 30625 38972 24643 60548 11847 57381 15417 51401 34599]"]:2 + xor2 --> cipheredpass + space:2 + hash(("hash")):2 + cipheredpass --> hash + space:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$6WJQ1Ilu7fZunT61eo.0e.nN81TiuW5BYHfFrEz.jBwE4dotjOX7e"]:2 + hash --> cipheredhashed ``` -#### Mask Encipher +### Mask Encipher ```mermaid block-beta - columns 3 - passcode_idx["passcode indices:\n[22, 6, 38, 21]"] - comb_pos["combined position key:\n[14972 5803 54986 46940 19674 32943 38431 42194 29083]"] - cust_pos["customer position key:\n[25392 13634 17753 25555 61290 50870 49934 53104 9796]"] - - space:3 - propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) - passcode_idx-->propidx - space:1 - xor1(("XOR")) - comb_pos --> xor1 - cust_pos --> xor1 - - space:3 - passcode_position_idx["passcode poition indices:\n[4, 6, 2, 3]"] - propidx --> passcode_position_idx - - space:5 - pad1(("Pad with\nrandom indices")) - passcode_position_idx --> pad1 - - space:5 - posidx["Padded Passcode Position Indices:\n[4, 6, 2, 3, 7, 1, 8, 0, 5, 4]"] - pad1 --> posidx - space:1 - user_pos["user position key:\n[22860 9193 37779 54415 41904 17945 21777 27554 22495]"] - xor1 --> user_pos - - space:4 - sel(("select positions")) - user_pos --> sel - posidx --> sel - space:5 - passcode_pos["passcode positions:\n[41904 21777 37779 54415 27554 9193 22495 22860 17945 41904]"] - sel --> passcode_pos - mask_key["mask key\n[15948 8251 52095 4511 25902 21701 15340 4458 15137 44295]"] - space:4 - xor2(("XOR")) - mask_key --> xor2 - passcode_pos --> xor2 - space:5 - mask["enciphered mask:\n [40444 29994 22764 50448 3724 30508 27699 18470 32056 3767]"] - xor2 --> mask + columns 3 + passcode_idx["passcode indices:\n[26, 31, 1, 53]"] + comb_pos["combined position key:\n[10840 53435 61304 62480 44024 35399 25077 63411 2973]"] + cust_pos["customer position key:\n[12958 41262 53564 57128 61719 22386 19660 14119 45982]"] + + space:3 + propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) + passcode_idx-->propidx + space:1 + xor1(("XOR")) + comb_pos --> xor1 + cust_pos --> xor1 + + space:3 + passcode_position_idx["passcode poition indices:\n[8, 4, 1, 8]"] + propidx --> passcode_position_idx + + space:5 + pad1(("Pad with\nrandom indices")) + passcode_position_idx --> pad1 + + space:5 + posidx["Padded Passcode Position Indices:\n[8, 4, 1, 8, 5, 4, 0, 1, 2, 3]"] + pad1 --> posidx + space:1 + user_pos["user position key:\n[ 6342 29077 15940 11064 23279 56629 11577 49300 47107]"] + xor1 --> user_pos + + space:4 + sel(("select positions")) + user_pos --> sel + posidx --> sel + space:5 + passcode_pos["ordered user passcode positions:\n[47107 23279 29077 47107 56629 23279 6342 29077 15940 11064]"] + sel --> passcode_pos + mask_key["mask key\n[14299 50705 35900 48569 35541 57915 43280 32407 20229 24566]"] + space:4 + xor2(("XOR")) + mask_key --> xor2 + passcode_pos --> xor2 + space:5 + mask["enciphered mask:\n [36824 40190 64937 1466 22496 47316 45526 3842 28993 29902]"] + xor2 --> mask ``` + +### Validate nKode + +```mermaid +block-beta + columns 4 + selected_keys["selected keys:\n[2, 4, 4, 4]"] + login_keypad["login keypad:\nKey 0: [27 28 20 3 4 41 33 52 35] +Key 1: [18 10 38 39 40 5 6 34 8] +Key 2: [ 0 37 29 21 49 32 24 16 26] +Key 3: [36 19 2 48 13 23 42 25 44] +Key 4: [45 1 47 30 31 14 15 43 53] +Key 5: [ 9 46 11 12 22 50 51 7 17] +"] + mask["enciphered mask:\n [36824 40190 64937 1466 22496 47316 45526 3842 28993 29902]"] + mask_key["mask key:\n[14299 50705 35900 48569 35541 57915 43280 32407 20229 24566]"] + space:4 + + selectkeys(("select keys")) + space:2 + xor1(("XOR")) + mask --> xor1 + mask_key --> xor1 + selected_keys -->selectkeys + login_keypad --> selectkeys + space:4 + + ordered_keys["ordered keys:\n[[ 0 37 29 21 49 32 24 16 26] + [45 1 47 30 31 14 15 43 53] + [45 1 47 30 31 14 15 43 53] + [45 1 47 30 31 14 15 43 53]]"] + space:1 + user_position_key["user position key:\n[ 6342 29077 15940 11064 23279 56629 11577 49300 47107]"] + passcode_pos["ordered user passcode positions:\n[47107 23279 29077 47107 56629 23279 6342 29077 15940 11064]"] + selectkeys --> ordered_keys + xor1 --> passcode_pos + space:7 + + get_passcode_idxs(("recover passcode\nposition indices")) + user_position_key --> get_passcode_idxs + passcode_pos --> get_passcode_idxs + space:7 + + passcode_pos_idxs["padded passcode position indices:\n[8, 4, 1, 8, 5, 4, 0, 1, 2, 3]"] + get_passcode_idxs --> passcode_pos_idxs + space:4 + + get_presumed_idxs(("recover passcode\nproperty indices")) + ordered_keys --> get_presumed_idxs + passcode_pos_idxs --> get_presumed_idxs + space:7 + + passcode_prop_idxs["presumed passcode property indices:\n[26, 31, 1, 53]"] + space:1 + prop["combined_property_key\n[35274 941 33245 42136 61504 47487 11565 14493 54536 56594 17395 40777 + 5141 39337 50158 47964 24171 37128 64165 13745 26465 41175 64107 12840 + 31690 55980 1235 49186 51096 19341 63575 38870 25828 11231 43794 36374 + 33514 56590 39522 645 47628 45309 53428 21762 22447 34222 60181 32811 + 2530 39976 61583 47473 35640 25712]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$6WJQ1Ilu7fZunT61eo.0e.nN81TiuW5BYHfFrEz.jBwE4dotjOX7e"] + get_presumed_idxs --> passcode_pos_idxs + get_presumed_idxs --> passcode_prop_idxs + space:5 + + sel(("select\nproperties")) + passcode_prop_idxs --> sel + prop --> sel + space:7 + + passcode_prop["presumed passcode properties:\n[ 1235 38870 941 25712]"] + sel --> passcode_prop + space:7 + + cipher(("encipher")) + passcode_prop --> cipher + space:7 + + cipheredpass["ciphered passcode:\n[38216 30625 38972 24643 60548 11847 57381 15417 51401 34599]"] + cipher --> cipheredpass + space:8 + + + comp(["compare"]) + cipheredpass --> comp + cipheredhashed --> comp + space:7 + + suc(("valid")) + fail(("invalid")) + comp --> suc + comp --> fail + +``` \ No newline at end of file diff --git a/docs/scripts/render_encipher_decipher_diagrams.py b/docs/scripts/render_encipher_decipher_diagrams.py index 88e5ee3..18e84d4 100644 --- a/docs/scripts/render_encipher_decipher_diagrams.py +++ b/docs/scripts/render_encipher_decipher_diagrams.py @@ -11,6 +11,14 @@ from src.models import NKodePolicy, KeypadSize from src.nkode_api import NKodeAPI from src.utils import select_keys_with_passcode_values +def display_keypad(icons_array: np.ndarray, props_per_key: int) -> str: + icons = "" + for idx, row in enumerate(icons_array.reshape(-1, props_per_key)): + icons += f"Key {idx}: " + icons += str(row) + icons += "\n" + return icons + if __name__ == "__main__": api = NKodeAPI() @@ -54,6 +62,9 @@ if __name__ == "__main__": ordered_user_position_key = user_position_key[padded_passcode_position_indices] mask = ordered_user_position_key ^ user.cipher.mask_key encoded_mask = user.cipher.encode_base64_str(mask) + login_keypad = api.get_login_keypad(username, customer_id) + selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, + keypad_size.props_per_key) context = { "max_nkode_len": policy.max_nkode_len, "numb_of_keys": keypad_size.numb_of_keys, @@ -75,5 +86,8 @@ if __name__ == "__main__": "pad_user_passcode_idxs": padded_passcode_position_indices, "ordered_user_position_key":ordered_user_position_key, "mask": mask, + "selected_keys": selected_keys_login, + "login_keypad": display_keypad(login_keypad, keypad_size.props_per_key), + "ordered_keys": login_keypad.reshape(-1, keypad_size.props_per_key)[selected_keys_login], } - render_markdown_template(Path("../templates/encipher_decipher_nkode.template.md"), Path("../encipher_decipher_nkode.md"), context) \ No newline at end of file + render_markdown_template(Path("../templates/encipher_decipher_nkode.template.md"), Path("../encipher_decipher_nkode.md"), context) diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_nkode.template.md index e3bb878..4bd5b27 100644 --- a/docs/templates/encipher_decipher_nkode.template.md +++ b/docs/templates/encipher_decipher_nkode.template.md @@ -140,34 +140,73 @@ block-beta login_keypad["login keypad:\n{{login_keypad}}"] mask["enciphered mask:\n {{mask}}"] mask_key["mask key:\n{{mask_key}}"] - space:6 + space:4 - user_position_key["user position key:\n{{user_position_key}}"] - passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"] + selectkeys(("select keys")) + space:2 + xor1(("XOR")) + mask --> xor1 + mask_key --> xor1 + selected_keys -->selectkeys + login_keypad --> selectkeys space:4 - space:2 + ordered_keys["ordered keys:\n{{ordered_keys}}"] + space:1 + user_position_key["user position key:\n{{user_position_key}}"] + passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"] + selectkeys --> ordered_keys + xor1 --> passcode_pos + space:7 + get_passcode_idxs(("recover passcode\nposition indices")) + user_position_key --> get_passcode_idxs + passcode_pos --> get_passcode_idxs space:7 passcode_pos_idxs["padded passcode position indices:\n{{pad_user_passcode_idxs}}"] - space:5 + get_passcode_idxs --> passcode_pos_idxs + space:4 get_presumed_idxs(("recover passcode\nproperty indices")) + ordered_keys --> get_presumed_idxs + passcode_pos_idxs --> get_presumed_idxs space:7 passcode_prop_idxs["presumed passcode property indices:\n{{user_passcode_idxs}}"] - space:7 - - sel(("select\nproperties")) space:1 prop["combined_property_key\n{{combined_property_key}}"] - space:5 - passcode_prop["presumed passcode properties:\n{{user_passcode_props}}"] - space:1 cipheredhashed["hashed ciphered passcode:\n{{code}}"] - space:6 + get_presumed_idxs --> passcode_pos_idxs + get_presumed_idxs --> passcode_prop_idxs + space:5 - comp(("compare")) + sel(("select\nproperties")) + passcode_prop_idxs --> sel + prop --> sel + space:7 + + passcode_prop["presumed passcode properties:\n{{user_passcode_props}}"] + sel --> passcode_prop + space:7 + + cipher(("encipher")) + passcode_prop --> cipher + space:7 + + cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"] + cipher --> cipheredpass + space:8 + + + comp(["compare"]) + cipheredpass --> comp + cipheredhashed --> comp + space:7 + + suc(("valid")) + fail(("invalid")) + comp --> suc + comp --> fail ``` -- 2.49.1 From 8a20ffdd5b72455f8df3c123e5229aa85d12e252 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 04:14:13 -0500 Subject: [PATCH 58/85] remove arrow --- docs/encipher_decipher_nkode.md | 149 +++++++++--------- .../encipher_decipher_nkode.template.md | 3 +- 2 files changed, 75 insertions(+), 77 deletions(-) diff --git a/docs/encipher_decipher_nkode.md b/docs/encipher_decipher_nkode.md index 6be3fc4..7d2b480 100644 --- a/docs/encipher_decipher_nkode.md +++ b/docs/encipher_decipher_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [32025 56251 54239 48726 57043 21466 36907 63872 5185 40361 24862 17007 - 15817 35928 23390 44980 24388 9693 29079 42520 27552 55989 21064 47245 - 31415 33398 15649 37888 60649 19865 33780 9215 998 23952 774 36297 - 56975 53701 54205 26202 57035 1589 27142 51011 41787 17416 14611 52249 - 24554 2826 51850 29516 15825 40218] -- position key: [12958 41262 53564 57128 61719 22386 19660 14119 45982] +- property key: [42782 22411 58796 11465 49503 56275 38775 38081 7026 16254 14654 36893 + 55835 42117 397 53181 30070 49716 9591 26367 43862 44491 15032 6140 + 27166 25935 32851 1343 59160 16484 19970 19858 51729 7378 38465 28158 + 54598 17069 55468 61288 4228 11257 1486 8676 38279 55278 49181 18796 + 24775 6931 20611 57138 59596 36232] +- position key: [12647 30491 17139 56844 38964 47773 48376 30998 47349] --- ## User Cipher -- property key: [62675 55318 20994 6862 11923 60069 48390 49437 49481 16571 8941 56614 - 10716 5617 39088 5352 303 46293 35634 37801 3265 31330 43043 35493 - 381 22746 14834 21538 11121 1556 31651 46121 26370 30287 43028 991 - 23653 3275 18911 25823 25799 46792 47794 37441 62612 49574 53766 19506 - 22024 38690 14853 51773 46825 63850] -- passcode key: [37275 57463 39825 1075 60548 11847 57381 15417 51401 34599] -- combined position key: [10840 53435 61304 62480 44024 35399 25077 63411 2973] -- mask key: [14299 50705 35900 48569 35541 57915 43280 32407 20229 24566] +- property key: [33847 39397 2529 4886 18590 30214 36385 48387 45829 57638 20358 47401 + 15283 40354 57184 4978 23666 27711 53811 25217 41251 28914 39980 49243 + 50901 59505 64895 1640 29549 35643 5917 44770 1327 40377 63154 42643 + 27727 34100 38270 40653 59421 61433 33944 52893 5800 59979 60549 28886 + 65479 26388 12482 30070 62886 928] +- passcode key: [39941 46272 6788 28608 6629 37858 57195 10700 63193 44873] +- combined position key: [17324 5420 26420 62365 37521 21226 5241 59891 59082] +- mask key: [59295 36535 38305 40214 13117 40275 7336 42576 57021 2814] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[ 6342 29077 15940 11064 23279 56629 11577 49300 47107]"] - customer_pos["customer position key:\n[12958 41262 53564 57128 61719 22386 19660 14119 45982]"] + user_pos["user position key:\n[29387 25143 9671 11665 2725 59511 43137 37093 24127]"] + customer_pos["customer position key:\n[12647 30491 17139 56844 38964 47773 48376 30998 47349]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[10840 53435 61304 62480 44024 35399 25077 63411 2973]"]:2 + comb_pos["combined position key\n[17324 5420 26420 62365 37521 21226 5241 59891 59082]"]:2 xor --> comb_pos ``` ## User Keypad - keypad: -- user passcode indices: [26, 31, 1, 53] +- user passcode indices: [8, 47, 15, 26] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[32025 56251 54239 48726 57043 21466 36907 63872 5185 40361 24862 17007 - 15817 35928 23390 44980 24388 9693 29079 42520 27552 55989 21064 47245 - 31415 33398 15649 37888 60649 19865 33780 9215 998 23952 774 36297 - 56975 53701 54205 26202 57035 1589 27142 51011 41787 17416 14611 52249 - 24554 2826 51850 29516 15825 40218]"] - uprop["user_property_key\n[62675 55318 20994 6862 11923 60069 48390 49437 49481 16571 8941 56614 - 10716 5617 39088 5352 303 46293 35634 37801 3265 31330 43043 35493 - 381 22746 14834 21538 11121 1556 31651 46121 26370 30287 43028 991 - 23653 3275 18911 25823 25799 46792 47794 37441 62612 49574 53766 19506 - 22024 38690 14853 51773 46825 63850]"] + cprop["customer_property_key\n[42782 22411 58796 11465 49503 56275 38775 38081 7026 16254 14654 36893 + 55835 42117 397 53181 30070 49716 9591 26367 43862 44491 15032 6140 + 27166 25935 32851 1343 59160 16484 19970 19858 51729 7378 38465 28158 + 54598 17069 55468 61288 4228 11257 1486 8676 38279 55278 49181 18796 + 24775 6931 20611 57138 59596 36232]"] + uprop["user_property_key\n[33847 39397 2529 4886 18590 30214 36385 48387 45829 57638 20358 47401 + 15283 40354 57184 4978 23666 27711 53811 25217 41251 28914 39980 49243 + 50901 59505 64895 1640 29549 35643 5917 44770 1327 40377 63154 42643 + 27727 34100 38270 40653 59421 61433 33944 52893 5800 59979 60549 28886 + 65479 26388 12482 30070 62886 928]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[35274 941 33245 42136 61504 47487 11565 14493 54536 56594 17395 40777 - 5141 39337 50158 47964 24171 37128 64165 13745 26465 41175 64107 12840 - 31690 55980 1235 49186 51096 19341 63575 38870 25828 11231 43794 36374 - 33514 56590 39522 645 47628 45309 53428 21762 22447 34222 60181 32811 - 2530 39976 61583 47473 35640 25712]"] + prop["combined_property_key\n[ 9001 52846 60493 16351 35265 44501 6486 10690 43127 56920 30392 10548 + 57768 14631 57069 56527 10500 44555 63300 1150 2677 56633 42644 55207 + 44235 36158 32044 855 38005 52063 22815 58224 53054 33131 24819 52077 + 47369 51097 19922 29093 63641 50176 33110 61305 33583 15781 11416 14778 + 40704 31751 24641 43588 7530 36392]"] xor1 --> prop - pass["user_passcode_indices\n[26, 31, 1, 53]"] + pass["user_passcode_indices\n[8, 47, 15, 26]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[ 1235 38870 941 25712]"]:2 + passcode["user passcode properties:\n[43127 14778 56527 32044]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[ 1235 38870 941 25712 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[43127 14778 56527 32044 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[37275 57463 39825 1075 60548 11847 57381 15417 51401 34599]"] + passkey["passcode key:\n[39941 46272 6788 28608 6629 37858 57195 10700 63193 44873]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[38216 30625 38972 24643 60548 11847 57381 15417 51401 34599]"]:2 + cipheredpass["ciphered passcode:\n[13426 36218 50763 4844 6629 37858 57195 10700 63193 44873]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$6WJQ1Ilu7fZunT61eo.0e.nN81TiuW5BYHfFrEz.jBwE4dotjOX7e"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$n5xAQRuWhK3MiLsQHYiRu.p2Mam48sH7W/L7BojTBkyoQuUGa6/sO"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[26, 31, 1, 53]"] - comb_pos["combined position key:\n[10840 53435 61304 62480 44024 35399 25077 63411 2973]"] - cust_pos["customer position key:\n[12958 41262 53564 57128 61719 22386 19660 14119 45982]"] + passcode_idx["passcode indices:\n[8, 47, 15, 26]"] + comb_pos["combined position key:\n[17324 5420 26420 62365 37521 21226 5241 59891 59082]"] + cust_pos["customer position key:\n[12647 30491 17139 56844 38964 47773 48376 30998 47349]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[8, 4, 1, 8]"] + passcode_position_idx["passcode poition indices:\n[8, 2, 6, 8]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[8, 4, 1, 8, 5, 4, 0, 1, 2, 3]"] + posidx["Padded Passcode Position Indices:\n[8, 2, 6, 8, 8, 3, 8, 6, 3, 7]"] pad1 --> posidx space:1 - user_pos["user position key:\n[ 6342 29077 15940 11064 23279 56629 11577 49300 47107]"] + user_pos["user position key:\n[29387 25143 9671 11665 2725 59511 43137 37093 24127]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[47107 23279 29077 47107 56629 23279 6342 29077 15940 11064]"] + passcode_pos["ordered user passcode positions:\n[24127 9671 43137 24127 24127 11665 24127 43137 11665 37093]"] sel --> passcode_pos - mask_key["mask key\n[14299 50705 35900 48569 35541 57915 43280 32407 20229 24566]"] + mask_key["mask key\n[59295 36535 38305 40214 13117 40275 7336 42576 57021 2814]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [36824 40190 64937 1466 22496 47316 45526 3842 28993 29902]"] + mask["enciphered mask:\n [47520 43888 15648 49961 27906 45250 17047 3793 62252 39451]"] xor2 --> mask ``` @@ -156,16 +156,16 @@ block-beta ```mermaid block-beta columns 4 - selected_keys["selected keys:\n[2, 4, 4, 4]"] - login_keypad["login keypad:\nKey 0: [27 28 20 3 4 41 33 52 35] -Key 1: [18 10 38 39 40 5 6 34 8] -Key 2: [ 0 37 29 21 49 32 24 16 26] -Key 3: [36 19 2 48 13 23 42 25 44] -Key 4: [45 1 47 30 31 14 15 43 53] -Key 5: [ 9 46 11 12 22 50 51 7 17] + selected_keys["selected keys:\n[3, 2, 2, 0]"] + login_keypad["login keypad:\nKey 0: [ 9 19 38 39 31 5 24 25 26] +Key 1: [18 1 20 12 13 50 6 34 53] +Key 2: [36 37 47 21 40 41 15 43 17] +Key 3: [ 0 10 2 3 4 23 42 7 8] +Key 4: [45 46 11 30 22 32 51 16 35] +Key 5: [27 28 29 48 49 14 33 52 44] "] - mask["enciphered mask:\n [36824 40190 64937 1466 22496 47316 45526 3842 28993 29902]"] - mask_key["mask key:\n[14299 50705 35900 48569 35541 57915 43280 32407 20229 24566]"] + mask["enciphered mask:\n [47520 43888 15648 49961 27906 45250 17047 3793 62252 39451]"] + mask_key["mask key:\n[59295 36535 38305 40214 13117 40275 7336 42576 57021 2814]"] space:4 selectkeys(("select keys")) @@ -173,17 +173,17 @@ Key 5: [ 9 46 11 12 22 50 51 7 17] xor1(("XOR")) mask --> xor1 mask_key --> xor1 - selected_keys -->selectkeys + selected_keys --> selectkeys login_keypad --> selectkeys space:4 - ordered_keys["ordered keys:\n[[ 0 37 29 21 49 32 24 16 26] - [45 1 47 30 31 14 15 43 53] - [45 1 47 30 31 14 15 43 53] - [45 1 47 30 31 14 15 43 53]]"] + ordered_keys["ordered keys:\n[[ 0 10 2 3 4 23 42 7 8] + [36 37 47 21 40 41 15 43 17] + [36 37 47 21 40 41 15 43 17] + [ 9 19 38 39 31 5 24 25 26]]"] space:1 - user_position_key["user position key:\n[ 6342 29077 15940 11064 23279 56629 11577 49300 47107]"] - passcode_pos["ordered user passcode positions:\n[47107 23279 29077 47107 56629 23279 6342 29077 15940 11064]"] + user_position_key["user position key:\n[29387 25143 9671 11665 2725 59511 43137 37093 24127]"] + passcode_pos["ordered user passcode positions:\n[24127 9671 43137 24127 24127 11665 24127 43137 11665 37093]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:7 @@ -193,7 +193,7 @@ Key 5: [ 9 46 11 12 22 50 51 7 17] passcode_pos --> get_passcode_idxs space:7 - passcode_pos_idxs["padded passcode position indices:\n[8, 4, 1, 8, 5, 4, 0, 1, 2, 3]"] + passcode_pos_idxs["padded passcode position indices:\n[8, 2, 6, 8, 8, 3, 8, 6, 3, 7]"] get_passcode_idxs --> passcode_pos_idxs space:4 @@ -202,15 +202,14 @@ Key 5: [ 9 46 11 12 22 50 51 7 17] passcode_pos_idxs --> get_presumed_idxs space:7 - passcode_prop_idxs["presumed passcode property indices:\n[26, 31, 1, 53]"] + passcode_prop_idxs["presumed passcode property indices:\n[8, 47, 15, 26]"] space:1 - prop["combined_property_key\n[35274 941 33245 42136 61504 47487 11565 14493 54536 56594 17395 40777 - 5141 39337 50158 47964 24171 37128 64165 13745 26465 41175 64107 12840 - 31690 55980 1235 49186 51096 19341 63575 38870 25828 11231 43794 36374 - 33514 56590 39522 645 47628 45309 53428 21762 22447 34222 60181 32811 - 2530 39976 61583 47473 35640 25712]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$6WJQ1Ilu7fZunT61eo.0e.nN81TiuW5BYHfFrEz.jBwE4dotjOX7e"] - get_presumed_idxs --> passcode_pos_idxs + prop["combined_property_key\n[ 9001 52846 60493 16351 35265 44501 6486 10690 43127 56920 30392 10548 + 57768 14631 57069 56527 10500 44555 63300 1150 2677 56633 42644 55207 + 44235 36158 32044 855 38005 52063 22815 58224 53054 33131 24819 52077 + 47369 51097 19922 29093 63641 50176 33110 61305 33583 15781 11416 14778 + 40704 31751 24641 43588 7530 36392]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$n5xAQRuWhK3MiLsQHYiRu.p2Mam48sH7W/L7BojTBkyoQuUGa6/sO"] get_presumed_idxs --> passcode_prop_idxs space:5 @@ -219,7 +218,7 @@ Key 5: [ 9 46 11 12 22 50 51 7 17] prop --> sel space:7 - passcode_prop["presumed passcode properties:\n[ 1235 38870 941 25712]"] + passcode_prop["presumed passcode properties:\n[43127 14778 56527 32044]"] sel --> passcode_prop space:7 @@ -227,7 +226,7 @@ Key 5: [ 9 46 11 12 22 50 51 7 17] passcode_prop --> cipher space:7 - cipheredpass["ciphered passcode:\n[38216 30625 38972 24643 60548 11847 57381 15417 51401 34599]"] + cipheredpass["ciphered passcode:\n[13426 36218 50763 4844 6629 37858 57195 10700 63193 44873]"] cipher --> cipheredpass space:8 diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_nkode.template.md index 4bd5b27..b1a8c8b 100644 --- a/docs/templates/encipher_decipher_nkode.template.md +++ b/docs/templates/encipher_decipher_nkode.template.md @@ -147,7 +147,7 @@ block-beta xor1(("XOR")) mask --> xor1 mask_key --> xor1 - selected_keys -->selectkeys + selected_keys --> selectkeys login_keypad --> selectkeys space:4 @@ -177,7 +177,6 @@ block-beta space:1 prop["combined_property_key\n{{combined_property_key}}"] cipheredhashed["hashed ciphered passcode:\n{{code}}"] - get_presumed_idxs --> passcode_pos_idxs get_presumed_idxs --> passcode_prop_idxs space:5 -- 2.49.1 From bb784d11c1b08c50221b09b5fa7886de0293f4fd Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 04:29:48 -0500 Subject: [PATCH 59/85] reduce columns --- docs/encipher_decipher_nkode.md | 183 +++++++++--------- .../encipher_decipher_nkode.template.md | 39 ++-- 2 files changed, 110 insertions(+), 112 deletions(-) diff --git a/docs/encipher_decipher_nkode.md b/docs/encipher_decipher_nkode.md index 7d2b480..87a9b7c 100644 --- a/docs/encipher_decipher_nkode.md +++ b/docs/encipher_decipher_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [42782 22411 58796 11465 49503 56275 38775 38081 7026 16254 14654 36893 - 55835 42117 397 53181 30070 49716 9591 26367 43862 44491 15032 6140 - 27166 25935 32851 1343 59160 16484 19970 19858 51729 7378 38465 28158 - 54598 17069 55468 61288 4228 11257 1486 8676 38279 55278 49181 18796 - 24775 6931 20611 57138 59596 36232] -- position key: [12647 30491 17139 56844 38964 47773 48376 30998 47349] +- property key: [29187 8204 6047 64047 53525 11867 18824 35538 45077 33932 37992 15962 + 59642 23909 18198 52159 28347 8078 22066 56398 46985 41696 7084 56103 + 1625 31453 7418 15303 35465 27310 46695 8365 45388 58929 11384 64368 + 30936 44282 62811 22160 20933 21499 61363 9180 47285 5021 18129 20570 + 42619 30657 56801 14839 11286 38709] +- position key: [57791 55443 47428 9481 40757 62312 55581 59207 31500] --- ## User Cipher -- property key: [33847 39397 2529 4886 18590 30214 36385 48387 45829 57638 20358 47401 - 15283 40354 57184 4978 23666 27711 53811 25217 41251 28914 39980 49243 - 50901 59505 64895 1640 29549 35643 5917 44770 1327 40377 63154 42643 - 27727 34100 38270 40653 59421 61433 33944 52893 5800 59979 60549 28886 - 65479 26388 12482 30070 62886 928] -- passcode key: [39941 46272 6788 28608 6629 37858 57195 10700 63193 44873] -- combined position key: [17324 5420 26420 62365 37521 21226 5241 59891 59082] -- mask key: [59295 36535 38305 40214 13117 40275 7336 42576 57021 2814] +- property key: [63039 13937 22392 22556 23851 13063 2277 14453 60483 59297 53238 50783 + 40759 12966 14543 64414 12752 61149 16294 38792 40555 32560 54274 26845 + 8408 516 5126 11787 51712 48283 7084 25997 13305 51796 59458 333 + 50546 46819 29676 41112 7589 39340 7270 40439 32 16909 4097 47405 + 901 23033 12446 65486 41796 14649] +- passcode key: [ 3917 18489 51928 19673 27451 11243 32524 43956 43333 45897] +- combined position key: [65378 4217 1730 61114 26857 28326 25595 30504 39366] +- mask key: [28481 57972 43388 58568 3652 19285 25255 14159 35285 18802] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[29387 25143 9671 11665 2725 59511 43137 37093 24127]"] - customer_pos["customer position key:\n[12647 30491 17139 56844 38964 47773 48376 30998 47349]"] + user_pos["user position key:\n[ 7901 51434 49030 52147 63452 40398 47846 36975 58058]"] + customer_pos["customer position key:\n[57791 55443 47428 9481 40757 62312 55581 59207 31500]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[17324 5420 26420 62365 37521 21226 5241 59891 59082]"]:2 + comb_pos["combined position key\n[65378 4217 1730 61114 26857 28326 25595 30504 39366]"]:2 xor --> comb_pos ``` ## User Keypad - keypad: -- user passcode indices: [8, 47, 15, 26] +- user passcode indices: [16, 34, 51, 12] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[42782 22411 58796 11465 49503 56275 38775 38081 7026 16254 14654 36893 - 55835 42117 397 53181 30070 49716 9591 26367 43862 44491 15032 6140 - 27166 25935 32851 1343 59160 16484 19970 19858 51729 7378 38465 28158 - 54598 17069 55468 61288 4228 11257 1486 8676 38279 55278 49181 18796 - 24775 6931 20611 57138 59596 36232]"] - uprop["user_property_key\n[33847 39397 2529 4886 18590 30214 36385 48387 45829 57638 20358 47401 - 15283 40354 57184 4978 23666 27711 53811 25217 41251 28914 39980 49243 - 50901 59505 64895 1640 29549 35643 5917 44770 1327 40377 63154 42643 - 27727 34100 38270 40653 59421 61433 33944 52893 5800 59979 60549 28886 - 65479 26388 12482 30070 62886 928]"] + cprop["customer_property_key\n[29187 8204 6047 64047 53525 11867 18824 35538 45077 33932 37992 15962 + 59642 23909 18198 52159 28347 8078 22066 56398 46985 41696 7084 56103 + 1625 31453 7418 15303 35465 27310 46695 8365 45388 58929 11384 64368 + 30936 44282 62811 22160 20933 21499 61363 9180 47285 5021 18129 20570 + 42619 30657 56801 14839 11286 38709]"] + uprop["user_property_key\n[63039 13937 22392 22556 23851 13063 2277 14453 60483 59297 53238 50783 + 40759 12966 14543 64414 12752 61149 16294 38792 40555 32560 54274 26845 + 8408 516 5126 11787 51712 48283 7084 25997 13305 51796 59458 333 + 50546 46819 29676 41112 7589 39340 7270 40439 32 16909 4097 47405 + 901 23033 12446 65486 41796 14649]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[ 9001 52846 60493 16351 35265 44501 6486 10690 43127 56920 30392 10548 - 57768 14631 57069 56527 10500 44555 63300 1150 2677 56633 42644 55207 - 44235 36158 32044 855 38005 52063 22815 58224 53054 33131 24819 52077 - 47369 51097 19922 29093 63641 50176 33110 61305 33583 15781 11416 14778 - 40704 31751 24641 43588 7530 36392]"] + prop["combined_property_key\n[33852 5757 16615 41523 35902 7516 16749 45735 23638 25389 23454 63493 + 30669 28611 32729 12321 24427 61779 27028 19398 10722 56784 53166 46074 + 9857 30937 2300 5580 16521 54837 44491 17696 33461 11365 50234 64061 + 48554 6681 34487 62984 19552 51799 62421 48683 47253 20880 22224 59767 + 42494 11832 60799 50745 36690 44556]"] xor1 --> prop - pass["user_passcode_indices\n[8, 47, 15, 26]"] + pass["user_passcode_indices\n[16, 34, 51, 12]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[43127 14778 56527 32044]"]:2 + passcode["user passcode properties:\n[24427 50234 50745 30669]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[43127 14778 56527 32044 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[24427 50234 50745 30669 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[39941 46272 6788 28608 6629 37858 57195 10700 63193 44873]"] + passkey["passcode key:\n[ 3917 18489 51928 19673 27451 11243 32524 43956 43333 45897]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[13426 36218 50763 4844 6629 37858 57195 10700 63193 44873]"]:2 + cipheredpass["ciphered passcode:\n[20518 35843 3297 15124 27451 11243 32524 43956 43333 45897]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$n5xAQRuWhK3MiLsQHYiRu.p2Mam48sH7W/L7BojTBkyoQuUGa6/sO"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$wC7VFWUqOhklJsvhVukMWeGtlHhsuLL58TFxVqahBTvMuyBiLInXW"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[8, 47, 15, 26]"] - comb_pos["combined position key:\n[17324 5420 26420 62365 37521 21226 5241 59891 59082]"] - cust_pos["customer position key:\n[12647 30491 17139 56844 38964 47773 48376 30998 47349]"] + passcode_idx["passcode indices:\n[16, 34, 51, 12]"] + comb_pos["combined position key:\n[65378 4217 1730 61114 26857 28326 25595 30504 39366]"] + cust_pos["customer position key:\n[57791 55443 47428 9481 40757 62312 55581 59207 31500]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[8, 2, 6, 8]"] + passcode_position_idx["passcode poition indices:\n[7, 7, 6, 3]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[8, 2, 6, 8, 8, 3, 8, 6, 3, 7]"] + posidx["Padded Passcode Position Indices:\n[7, 7, 6, 3, 2, 8, 6, 1, 1, 4]"] pad1 --> posidx space:1 - user_pos["user position key:\n[29387 25143 9671 11665 2725 59511 43137 37093 24127]"] + user_pos["user position key:\n[ 7901 51434 49030 52147 63452 40398 47846 36975 58058]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[24127 9671 43137 24127 24127 11665 24127 43137 11665 37093]"] + passcode_pos["ordered user passcode positions:\n[36975 36975 47846 52147 49030 58058 47846 51434 51434 63452]"] sel --> passcode_pos - mask_key["mask key\n[59295 36535 38305 40214 13117 40275 7336 42576 57021 2814]"] + mask_key["mask key\n[28481 57972 43388 58568 3652 19285 25255 14159 35285 18802]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [47520 43888 15648 49961 27906 45250 17047 3793 62252 39451]"] + mask["enciphered mask:\n [65326 29211 5018 12155 45506 43423 55361 65445 16703 48814]"] xor2 --> mask ``` @@ -155,90 +155,89 @@ block-beta ```mermaid block-beta - columns 4 - selected_keys["selected keys:\n[3, 2, 2, 0]"] - login_keypad["login keypad:\nKey 0: [ 9 19 38 39 31 5 24 25 26] -Key 1: [18 1 20 12 13 50 6 34 53] -Key 2: [36 37 47 21 40 41 15 43 17] -Key 3: [ 0 10 2 3 4 23 42 7 8] -Key 4: [45 46 11 30 22 32 51 16 35] -Key 5: [27 28 29 48 49 14 33 52 44] + columns 3 + selected_keys["selected keys:\n[3, 0, 1, 1]"] + login_keypad["login keypad:\nKey 0: [27 19 20 48 40 32 6 34 35] +Key 1: [ 9 37 29 12 13 23 51 7 17] +Key 2: [18 46 2 21 31 41 42 43 26] +Key 3: [36 28 38 39 22 50 33 16 44] +Key 4: [ 0 10 11 3 49 14 24 25 8] +Key 5: [45 1 47 30 4 5 15 52 53] "] - mask["enciphered mask:\n [47520 43888 15648 49961 27906 45250 17047 3793 62252 39451]"] - mask_key["mask key:\n[59295 36535 38305 40214 13117 40275 7336 42576 57021 2814]"] space:4 selectkeys(("select keys")) + mask["enciphered mask:\n [65326 29211 5018 12155 45506 43423 55361 65445 16703 48814]"] + mask_key["mask key:\n[28481 57972 43388 58568 3652 19285 25255 14159 35285 18802]"] space:2 + xor1(("XOR")) mask --> xor1 mask_key --> xor1 selected_keys --> selectkeys login_keypad --> selectkeys - space:4 + space:3 - ordered_keys["ordered keys:\n[[ 0 10 2 3 4 23 42 7 8] - [36 37 47 21 40 41 15 43 17] - [36 37 47 21 40 41 15 43 17] - [ 9 19 38 39 31 5 24 25 26]]"] - space:1 - user_position_key["user position key:\n[29387 25143 9671 11665 2725 59511 43137 37093 24127]"] - passcode_pos["ordered user passcode positions:\n[24127 9671 43137 24127 24127 11665 24127 43137 11665 37093]"] + ordered_keys["ordered keys:\n[[36 28 38 39 22 50 33 16 44] + [27 19 20 48 40 32 6 34 35] + [ 9 37 29 12 13 23 51 7 17] + [ 9 37 29 12 13 23 51 7 17]]"] + user_position_key["user position key:\n[ 7901 51434 49030 52147 63452 40398 47846 36975 58058]"] + passcode_pos["ordered user passcode positions:\n[36975 36975 47846 52147 49030 58058 47846 51434 51434 63452]"] selectkeys --> ordered_keys xor1 --> passcode_pos - space:7 + space:8 get_passcode_idxs(("recover passcode\nposition indices")) user_position_key --> get_passcode_idxs passcode_pos --> get_passcode_idxs - space:7 + space:8 - passcode_pos_idxs["padded passcode position indices:\n[8, 2, 6, 8, 8, 3, 8, 6, 3, 7]"] + passcode_pos_idxs["padded passcode position indices:\n[7, 7, 6, 3, 2, 8, 6, 1, 1, 4]"] get_passcode_idxs --> passcode_pos_idxs - space:4 + space:3 get_presumed_idxs(("recover passcode\nproperty indices")) ordered_keys --> get_presumed_idxs passcode_pos_idxs --> get_presumed_idxs - space:7 - - passcode_prop_idxs["presumed passcode property indices:\n[8, 47, 15, 26]"] - space:1 - prop["combined_property_key\n[ 9001 52846 60493 16351 35265 44501 6486 10690 43127 56920 30392 10548 - 57768 14631 57069 56527 10500 44555 63300 1150 2677 56633 42644 55207 - 44235 36158 32044 855 38005 52063 22815 58224 53054 33131 24819 52077 - 47369 51097 19922 29093 63641 50176 33110 61305 33583 15781 11416 14778 - 40704 31751 24641 43588 7530 36392]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$n5xAQRuWhK3MiLsQHYiRu.p2Mam48sH7W/L7BojTBkyoQuUGa6/sO"] - get_presumed_idxs --> passcode_prop_idxs space:5 + passcode_prop_idxs["presumed passcode property indices:\n[16, 34, 51, 12]"] + prop["combined_property_key\n[33852 5757 16615 41523 35902 7516 16749 45735 23638 25389 23454 63493 + 30669 28611 32729 12321 24427 61779 27028 19398 10722 56784 53166 46074 + 9857 30937 2300 5580 16521 54837 44491 17696 33461 11365 50234 64061 + 48554 6681 34487 62984 19552 51799 62421 48683 47253 20880 22224 59767 + 42494 11832 60799 50745 36690 44556]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$wC7VFWUqOhklJsvhVukMWeGtlHhsuLL58TFxVqahBTvMuyBiLInXW"] + get_presumed_idxs --> passcode_prop_idxs + space:3 + sel(("select\nproperties")) passcode_prop_idxs --> sel prop --> sel - space:7 + space:5 - passcode_prop["presumed passcode properties:\n[43127 14778 56527 32044]"] + passcode_prop["presumed passcode properties:\n[24427 50234 50745 30669]"] sel --> passcode_prop - space:7 + space:5 cipher(("encipher")) passcode_prop --> cipher - space:7 + space:5 - cipheredpass["ciphered passcode:\n[13426 36218 50763 4844 6629 37858 57195 10700 63193 44873]"] + cipheredpass["ciphered passcode:\n[20518 35843 3297 15124 27451 11243 32524 43956 43333 45897]"] cipher --> cipheredpass - space:8 + space:7 - comp(["compare"]) + comp{"compare"} cipheredpass --> comp cipheredhashed --> comp space:7 - suc(("valid")) - fail(("invalid")) - comp --> suc - comp --> fail + suc(("success")) + fail(("fail")) + comp --"Equal"--> suc + comp --"Not Equal"--> fail ``` \ No newline at end of file diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_nkode.template.md index b1a8c8b..99a6976 100644 --- a/docs/templates/encipher_decipher_nkode.template.md +++ b/docs/templates/encipher_decipher_nkode.template.md @@ -135,77 +135,76 @@ block-beta ```mermaid block-beta - columns 4 + columns 3 selected_keys["selected keys:\n{{selected_keys}}"] login_keypad["login keypad:\n{{login_keypad}}"] - mask["enciphered mask:\n {{mask}}"] - mask_key["mask key:\n{{mask_key}}"] space:4 selectkeys(("select keys")) + mask["enciphered mask:\n {{mask}}"] + mask_key["mask key:\n{{mask_key}}"] space:2 + xor1(("XOR")) mask --> xor1 mask_key --> xor1 selected_keys --> selectkeys login_keypad --> selectkeys - space:4 + space:3 ordered_keys["ordered keys:\n{{ordered_keys}}"] - space:1 user_position_key["user position key:\n{{user_position_key}}"] passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"] selectkeys --> ordered_keys xor1 --> passcode_pos - space:7 + space:8 get_passcode_idxs(("recover passcode\nposition indices")) user_position_key --> get_passcode_idxs passcode_pos --> get_passcode_idxs - space:7 + space:8 passcode_pos_idxs["padded passcode position indices:\n{{pad_user_passcode_idxs}}"] get_passcode_idxs --> passcode_pos_idxs - space:4 + space:3 get_presumed_idxs(("recover passcode\nproperty indices")) ordered_keys --> get_presumed_idxs passcode_pos_idxs --> get_presumed_idxs - space:7 + space:5 passcode_prop_idxs["presumed passcode property indices:\n{{user_passcode_idxs}}"] - space:1 prop["combined_property_key\n{{combined_property_key}}"] cipheredhashed["hashed ciphered passcode:\n{{code}}"] get_presumed_idxs --> passcode_prop_idxs - space:5 + space:3 sel(("select\nproperties")) passcode_prop_idxs --> sel prop --> sel - space:7 + space:5 passcode_prop["presumed passcode properties:\n{{user_passcode_props}}"] sel --> passcode_prop - space:7 + space:5 cipher(("encipher")) passcode_prop --> cipher - space:7 + space:5 cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"] cipher --> cipheredpass - space:8 + space:7 - comp(["compare"]) + comp{"compare"} cipheredpass --> comp cipheredhashed --> comp space:7 - suc(("valid")) - fail(("invalid")) - comp --> suc - comp --> fail + suc(("success")) + fail(("fail")) + comp --"Equal"--> suc + comp --"Not Equal"--> fail ``` -- 2.49.1 From 9a7e0ea5f9ffcd6bb1a578c8350c72fea4c61ad4 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 04:33:46 -0500 Subject: [PATCH 60/85] remove failure --- docs/encipher_decipher_nkode.md | 150 +++++++++--------- .../encipher_decipher_nkode.template.md | 4 +- 2 files changed, 75 insertions(+), 79 deletions(-) diff --git a/docs/encipher_decipher_nkode.md b/docs/encipher_decipher_nkode.md index 87a9b7c..45f89ad 100644 --- a/docs/encipher_decipher_nkode.md +++ b/docs/encipher_decipher_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [29187 8204 6047 64047 53525 11867 18824 35538 45077 33932 37992 15962 - 59642 23909 18198 52159 28347 8078 22066 56398 46985 41696 7084 56103 - 1625 31453 7418 15303 35465 27310 46695 8365 45388 58929 11384 64368 - 30936 44282 62811 22160 20933 21499 61363 9180 47285 5021 18129 20570 - 42619 30657 56801 14839 11286 38709] -- position key: [57791 55443 47428 9481 40757 62312 55581 59207 31500] +- property key: [ 7528 63893 43548 25423 47744 42591 13947 15495 44997 9610 32248 3295 + 37638 6583 51113 60339 43861 22578 25507 20049 10766 50355 36708 37036 + 12276 25874 63870 49205 70 53112 40483 39973 24671 4447 24361 43982 + 1997 23736 6951 9733 7407 48242 57341 7624 5578 22071 51177 20155 + 2441 15688 7417 935 33982 43009] +- position key: [41987 59995 38795 29405 53271 32296 44523 10502 49338] --- ## User Cipher -- property key: [63039 13937 22392 22556 23851 13063 2277 14453 60483 59297 53238 50783 - 40759 12966 14543 64414 12752 61149 16294 38792 40555 32560 54274 26845 - 8408 516 5126 11787 51712 48283 7084 25997 13305 51796 59458 333 - 50546 46819 29676 41112 7589 39340 7270 40439 32 16909 4097 47405 - 901 23033 12446 65486 41796 14649] -- passcode key: [ 3917 18489 51928 19673 27451 11243 32524 43956 43333 45897] -- combined position key: [65378 4217 1730 61114 26857 28326 25595 30504 39366] -- mask key: [28481 57972 43388 58568 3652 19285 25255 14159 35285 18802] +- property key: [25869 62495 1734 362 37984 51592 32568 34493 13441 35472 33999 41683 + 20426 4369 47735 37538 8392 1425 8176 40966 37766 22326 21576 20608 + 6581 21051 52337 10975 19283 32306 52352 6318 26386 36937 16351 38760 + 36505 9636 58306 38636 14299 36502 59745 33866 61255 12802 63463 64177 + 38614 28129 16937 786 31611 60757] +- passcode key: [28207 53281 58835 9812 48933 51588 25408 62022 36721 9334] +- combined position key: [60340 19752 2334 9189 29138 61771 17595 21364 54614] +- mask key: [43734 31472 30293 34641 24055 5782 14606 48388 2666 19217] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[ 7901 51434 49030 52147 63452 40398 47846 36975 58058]"] - customer_pos["customer position key:\n[57791 55443 47428 9481 40757 62312 55581 59207 31500]"] + user_pos["user position key:\n[20407 42867 40597 20792 41413 36707 59728 31346 5612]"] + customer_pos["customer position key:\n[41987 59995 38795 29405 53271 32296 44523 10502 49338]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[65378 4217 1730 61114 26857 28326 25595 30504 39366]"]:2 + comb_pos["combined position key\n[60340 19752 2334 9189 29138 61771 17595 21364 54614]"]:2 xor --> comb_pos ``` ## User Keypad - keypad: -- user passcode indices: [16, 34, 51, 12] +- user passcode indices: [37, 38, 40, 10] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[29187 8204 6047 64047 53525 11867 18824 35538 45077 33932 37992 15962 - 59642 23909 18198 52159 28347 8078 22066 56398 46985 41696 7084 56103 - 1625 31453 7418 15303 35465 27310 46695 8365 45388 58929 11384 64368 - 30936 44282 62811 22160 20933 21499 61363 9180 47285 5021 18129 20570 - 42619 30657 56801 14839 11286 38709]"] - uprop["user_property_key\n[63039 13937 22392 22556 23851 13063 2277 14453 60483 59297 53238 50783 - 40759 12966 14543 64414 12752 61149 16294 38792 40555 32560 54274 26845 - 8408 516 5126 11787 51712 48283 7084 25997 13305 51796 59458 333 - 50546 46819 29676 41112 7589 39340 7270 40439 32 16909 4097 47405 - 901 23033 12446 65486 41796 14649]"] + cprop["customer_property_key\n[ 7528 63893 43548 25423 47744 42591 13947 15495 44997 9610 32248 3295 + 37638 6583 51113 60339 43861 22578 25507 20049 10766 50355 36708 37036 + 12276 25874 63870 49205 70 53112 40483 39973 24671 4447 24361 43982 + 1997 23736 6951 9733 7407 48242 57341 7624 5578 22071 51177 20155 + 2441 15688 7417 935 33982 43009]"] + uprop["user_property_key\n[25869 62495 1734 362 37984 51592 32568 34493 13441 35472 33999 41683 + 20426 4369 47735 37538 8392 1425 8176 40966 37766 22326 21576 20608 + 6581 21051 52337 10975 19283 32306 52352 6318 26386 36937 16351 38760 + 36505 9636 58306 38636 14299 36502 59745 33866 61255 12802 63463 64177 + 38614 28129 16937 786 31611 60757]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[33852 5757 16615 41523 35902 7516 16749 45735 23638 25389 23454 63493 - 30669 28611 32729 12321 24427 61779 27028 19398 10722 56784 53166 46074 - 9857 30937 2300 5580 16521 54837 44491 17696 33461 11365 50234 64061 - 48554 6681 34487 62984 19552 51799 62421 48683 47253 20880 22224 59767 - 42494 11832 60799 50745 36690 44556]"] + prop["combined_property_key\n[30821 3466 44250 25125 12000 28631 18755 47674 39748 44826 63799 44556 + 56524 2214 32222 30993 35741 23971 31827 61015 47496 37765 56108 49196 + 13889 14121 13583 60138 19221 45386 21155 33931 1869 33046 24822 15526 + 35156 31004 63717 45289 11060 13028 13980 39298 64141 25653 12302 46090 + 40799 20649 24272 181 65477 17748]"] xor1 --> prop - pass["user_passcode_indices\n[16, 34, 51, 12]"] + pass["user_passcode_indices\n[37, 38, 40, 10]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[24427 50234 50745 30669]"]:2 + passcode["user passcode properties:\n[31004 63717 11060 63799]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[24427 50234 50745 30669 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[31004 63717 11060 63799 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[ 3917 18489 51928 19673 27451 11243 32524 43956 43333 45897]"] + passkey["passcode key:\n[28207 53281 58835 9812 48933 51588 25408 62022 36721 9334]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[20518 35843 3297 15124 27451 11243 32524 43956 43333 45897]"]:2 + cipheredpass["ciphered passcode:\n[ 5939 10436 52967 57187 48933 51588 25408 62022 36721 9334]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$wC7VFWUqOhklJsvhVukMWeGtlHhsuLL58TFxVqahBTvMuyBiLInXW"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$am68CvhXvNZFJE1qsmLSL.gC5I.cykt7LbisLcuUNkoFZ18vV1DPO"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[16, 34, 51, 12]"] - comb_pos["combined position key:\n[65378 4217 1730 61114 26857 28326 25595 30504 39366]"] - cust_pos["customer position key:\n[57791 55443 47428 9481 40757 62312 55581 59207 31500]"] + passcode_idx["passcode indices:\n[37, 38, 40, 10]"] + comb_pos["combined position key:\n[60340 19752 2334 9189 29138 61771 17595 21364 54614]"] + cust_pos["customer position key:\n[41987 59995 38795 29405 53271 32296 44523 10502 49338]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[7, 7, 6, 3]"] + passcode_position_idx["passcode poition indices:\n[1, 2, 4, 1]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[7, 7, 6, 3, 2, 8, 6, 1, 1, 4]"] + posidx["Padded Passcode Position Indices:\n[1, 2, 4, 1, 4, 6, 4, 8, 4, 2]"] pad1 --> posidx space:1 - user_pos["user position key:\n[ 7901 51434 49030 52147 63452 40398 47846 36975 58058]"] + user_pos["user position key:\n[20407 42867 40597 20792 41413 36707 59728 31346 5612]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[36975 36975 47846 52147 49030 58058 47846 51434 51434 63452]"] + passcode_pos["ordered user passcode positions:\n[42867 40597 41413 42867 41413 59728 41413 5612 41413 40597]"] sel --> passcode_pos - mask_key["mask key\n[28481 57972 43388 58568 3652 19285 25255 14159 35285 18802]"] + mask_key["mask key\n[43734 31472 30293 34641 24055 5782 14606 48388 2666 19217]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [65326 29211 5018 12155 45506 43423 55361 65445 16703 48814]"] + mask["enciphered mask:\n [ 3493 58469 55184 8226 64562 65478 39115 43240 43951 54660]"] xor2 --> mask ``` @@ -156,19 +156,19 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n[3, 0, 1, 1]"] - login_keypad["login keypad:\nKey 0: [27 19 20 48 40 32 6 34 35] -Key 1: [ 9 37 29 12 13 23 51 7 17] -Key 2: [18 46 2 21 31 41 42 43 26] -Key 3: [36 28 38 39 22 50 33 16 44] -Key 4: [ 0 10 11 3 49 14 24 25 8] -Key 5: [45 1 47 30 4 5 15 52 53] + selected_keys["selected keys:\n[4, 0, 4, 2]"] + login_keypad["login keypad:\nKey 0: [36 28 38 48 49 32 51 34 17] +Key 1: [ 9 19 2 3 4 41 33 52 8] +Key 2: [18 10 47 39 31 23 24 16 35] +Key 3: [27 46 29 21 22 14 6 43 26] +Key 4: [45 37 11 12 40 5 42 7 53] +Key 5: [ 0 1 20 30 13 50 15 25 44] "] space:4 selectkeys(("select keys")) - mask["enciphered mask:\n [65326 29211 5018 12155 45506 43423 55361 65445 16703 48814]"] - mask_key["mask key:\n[28481 57972 43388 58568 3652 19285 25255 14159 35285 18802]"] + mask["enciphered mask:\n [ 3493 58469 55184 8226 64562 65478 39115 43240 43951 54660]"] + mask_key["mask key:\n[43734 31472 30293 34641 24055 5782 14606 48388 2666 19217]"] space:2 xor1(("XOR")) @@ -178,12 +178,12 @@ Key 5: [45 1 47 30 4 5 15 52 53] login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[36 28 38 39 22 50 33 16 44] - [27 19 20 48 40 32 6 34 35] - [ 9 37 29 12 13 23 51 7 17] - [ 9 37 29 12 13 23 51 7 17]]"] - user_position_key["user position key:\n[ 7901 51434 49030 52147 63452 40398 47846 36975 58058]"] - passcode_pos["ordered user passcode positions:\n[36975 36975 47846 52147 49030 58058 47846 51434 51434 63452]"] + ordered_keys["ordered keys:\n[[45 37 11 12 40 5 42 7 53] + [36 28 38 48 49 32 51 34 17] + [45 37 11 12 40 5 42 7 53] + [18 10 47 39 31 23 24 16 35]]"] + user_position_key["user position key:\n[20407 42867 40597 20792 41413 36707 59728 31346 5612]"] + passcode_pos["ordered user passcode positions:\n[42867 40597 41413 42867 41413 59728 41413 5612 41413 40597]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [45 1 47 30 4 5 15 52 53] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[7, 7, 6, 3, 2, 8, 6, 1, 1, 4]"] + passcode_pos_idxs["padded passcode position indices:\n[1, 2, 4, 1, 4, 6, 4, 8, 4, 2]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [45 1 47 30 4 5 15 52 53] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[16, 34, 51, 12]"] - prop["combined_property_key\n[33852 5757 16615 41523 35902 7516 16749 45735 23638 25389 23454 63493 - 30669 28611 32729 12321 24427 61779 27028 19398 10722 56784 53166 46074 - 9857 30937 2300 5580 16521 54837 44491 17696 33461 11365 50234 64061 - 48554 6681 34487 62984 19552 51799 62421 48683 47253 20880 22224 59767 - 42494 11832 60799 50745 36690 44556]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$wC7VFWUqOhklJsvhVukMWeGtlHhsuLL58TFxVqahBTvMuyBiLInXW"] + passcode_prop_idxs["presumed passcode property indices:\n[37, 38, 40, 10]"] + prop["combined_property_key\n[30821 3466 44250 25125 12000 28631 18755 47674 39748 44826 63799 44556 + 56524 2214 32222 30993 35741 23971 31827 61015 47496 37765 56108 49196 + 13889 14121 13583 60138 19221 45386 21155 33931 1869 33046 24822 15526 + 35156 31004 63717 45289 11060 13028 13980 39298 64141 25653 12302 46090 + 40799 20649 24272 181 65477 17748]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$am68CvhXvNZFJE1qsmLSL.gC5I.cykt7LbisLcuUNkoFZ18vV1DPO"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [45 1 47 30 4 5 15 52 53] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[24427 50234 50745 30669]"] + passcode_prop["presumed passcode properties:\n[31004 63717 11060 63799]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [45 1 47 30 4 5 15 52 53] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[20518 35843 3297 15124 27451 11243 32524 43956 43333 45897]"] + cipheredpass["ciphered passcode:\n[ 5939 10436 52967 57187 48933 51588 25408 62022 36721 9334]"] cipher --> cipheredpass space:7 @@ -233,11 +233,9 @@ Key 5: [45 1 47 30 4 5 15 52 53] comp{"compare"} cipheredpass --> comp cipheredhashed --> comp - space:7 + space:5 suc(("success")) - fail(("fail")) comp --"Equal"--> suc - comp --"Not Equal"--> fail ``` \ No newline at end of file diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_nkode.template.md index 99a6976..9ca9bf6 100644 --- a/docs/templates/encipher_decipher_nkode.template.md +++ b/docs/templates/encipher_decipher_nkode.template.md @@ -200,11 +200,9 @@ block-beta comp{"compare"} cipheredpass --> comp cipheredhashed --> comp - space:7 + space:5 suc(("success")) - fail(("fail")) comp --"Equal"--> suc - comp --"Not Equal"--> fail ``` -- 2.49.1 From de1edb946ac74ae5ab93bf0d288942aee1f59835 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 08:02:10 -0500 Subject: [PATCH 61/85] add renew --- docs/encipher_decipher_nkode.md | 241 ------------- docs/encipher_decipher_renew_nkode.md | 330 ++++++++++++++++++ .../render_encipher_decipher_diagrams.py | 24 +- ...encipher_decipher_renew_nkode.template.md} | 66 ++++ 4 files changed, 419 insertions(+), 242 deletions(-) delete mode 100644 docs/encipher_decipher_nkode.md create mode 100644 docs/encipher_decipher_renew_nkode.md rename docs/templates/{encipher_decipher_nkode.template.md => encipher_decipher_renew_nkode.template.md} (71%) diff --git a/docs/encipher_decipher_nkode.md b/docs/encipher_decipher_nkode.md deleted file mode 100644 index 45f89ad..0000000 --- a/docs/encipher_decipher_nkode.md +++ /dev/null @@ -1,241 +0,0 @@ -# Encipher and Decipher nKode - -## Customer Policy -- max nkode length: 10 -- number of keys: 6 -- properties per key: 9 -- total number of properties: 54 - -## Customer Cipher -- property key: [ 7528 63893 43548 25423 47744 42591 13947 15495 44997 9610 32248 3295 - 37638 6583 51113 60339 43861 22578 25507 20049 10766 50355 36708 37036 - 12276 25874 63870 49205 70 53112 40483 39973 24671 4447 24361 43982 - 1997 23736 6951 9733 7407 48242 57341 7624 5578 22071 51177 20155 - 2441 15688 7417 935 33982 43009] -- position key: [41987 59995 38795 29405 53271 32296 44523 10502 49338] - - ---- - -## User Cipher -- property key: [25869 62495 1734 362 37984 51592 32568 34493 13441 35472 33999 41683 - 20426 4369 47735 37538 8392 1425 8176 40966 37766 22326 21576 20608 - 6581 21051 52337 10975 19283 32306 52352 6318 26386 36937 16351 38760 - 36505 9636 58306 38636 14299 36502 59745 33866 61255 12802 63463 64177 - 38614 28129 16937 786 31611 60757] -- passcode key: [28207 53281 58835 9812 48933 51588 25408 62022 36721 9334] -- combined position key: [60340 19752 2334 9189 29138 61771 17595 21364 54614] -- mask key: [43734 31472 30293 34641 24055 5782 14606 48388 2666 19217] - -### Combined Postion Key -```mermaid -block-beta - columns 2 - user_pos["user position key:\n[20407 42867 40597 20792 41413 36707 59728 31346 5612]"] - customer_pos["customer position key:\n[41987 59995 38795 29405 53271 32296 44523 10502 49338]"] - space:2 - xor(("XOR")):2 - user_pos --> xor - customer_pos --> xor - space:2 - comb_pos["combined position key\n[60340 19752 2334 9189 29138 61771 17595 21364 54614]"]:2 - xor --> comb_pos -``` - -## User Keypad -- keypad: -- user passcode indices: [37, 38, 40, 10] - -## nKode Cipher - -### Passcode Hash -```mermaid -block-beta - columns 2 - cprop["customer_property_key\n[ 7528 63893 43548 25423 47744 42591 13947 15495 44997 9610 32248 3295 - 37638 6583 51113 60339 43861 22578 25507 20049 10766 50355 36708 37036 - 12276 25874 63870 49205 70 53112 40483 39973 24671 4447 24361 43982 - 1997 23736 6951 9733 7407 48242 57341 7624 5578 22071 51177 20155 - 2441 15688 7417 935 33982 43009]"] - uprop["user_property_key\n[25869 62495 1734 362 37984 51592 32568 34493 13441 35472 33999 41683 - 20426 4369 47735 37538 8392 1425 8176 40966 37766 22326 21576 20608 - 6581 21051 52337 10975 19283 32306 52352 6318 26386 36937 16351 38760 - 36505 9636 58306 38636 14299 36502 59745 33866 61255 12802 63463 64177 - 38614 28129 16937 786 31611 60757]"] - space:2 - xor1(("XOR")):2 - cprop --> xor1 - uprop --> xor1 - space:2 - prop["combined_property_key\n[30821 3466 44250 25125 12000 28631 18755 47674 39748 44826 63799 44556 - 56524 2214 32222 30993 35741 23971 31827 61015 47496 37765 56108 49196 - 13889 14121 13583 60138 19221 45386 21155 33931 1869 33046 24822 15526 - 35156 31004 63717 45289 11060 13028 13980 39298 64141 25653 12302 46090 - 40799 20649 24272 181 65477 17748]"] - xor1 --> prop - pass["user_passcode_indices\n[37, 38, 40, 10]"] - space:2 - sel(("select\nproperties")):2 - pass --> sel - prop --> sel - space:2 - passcode["user passcode properties:\n[31004 63717 11060 63799]"]:2 - sel --> passcode - space:2 - pad["zero pad to\nmax nkode length: 10"]:2 - passcode -->pad - space:2 - paddedpasscode["padded passcode:\n[31004 63717 11060 63799 0 0 0 0 0 0]"] - pad --> paddedpasscode - passkey["passcode key:\n[28207 53281 58835 9812 48933 51588 25408 62022 36721 9334]"] - space:2 - xor2(("XOR")):2 - passkey --> xor2 - paddedpasscode --> xor2 - space:2 - cipheredpass["ciphered passcode:\n[ 5939 10436 52967 57187 48933 51588 25408 62022 36721 9334]"]:2 - xor2 --> cipheredpass - space:2 - hash(("hash")):2 - cipheredpass --> hash - space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$am68CvhXvNZFJE1qsmLSL.gC5I.cykt7LbisLcuUNkoFZ18vV1DPO"]:2 - hash --> cipheredhashed -``` - -### Mask Encipher -```mermaid -block-beta - columns 3 - passcode_idx["passcode indices:\n[37, 38, 40, 10]"] - comb_pos["combined position key:\n[60340 19752 2334 9189 29138 61771 17595 21364 54614]"] - cust_pos["customer position key:\n[41987 59995 38795 29405 53271 32296 44523 10502 49338]"] - - space:3 - propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) - passcode_idx-->propidx - space:1 - xor1(("XOR")) - comb_pos --> xor1 - cust_pos --> xor1 - - space:3 - passcode_position_idx["passcode poition indices:\n[1, 2, 4, 1]"] - propidx --> passcode_position_idx - - space:5 - pad1(("Pad with\nrandom indices")) - passcode_position_idx --> pad1 - - space:5 - posidx["Padded Passcode Position Indices:\n[1, 2, 4, 1, 4, 6, 4, 8, 4, 2]"] - pad1 --> posidx - space:1 - user_pos["user position key:\n[20407 42867 40597 20792 41413 36707 59728 31346 5612]"] - xor1 --> user_pos - - space:4 - sel(("select positions")) - user_pos --> sel - posidx --> sel - space:5 - passcode_pos["ordered user passcode positions:\n[42867 40597 41413 42867 41413 59728 41413 5612 41413 40597]"] - sel --> passcode_pos - mask_key["mask key\n[43734 31472 30293 34641 24055 5782 14606 48388 2666 19217]"] - space:4 - xor2(("XOR")) - mask_key --> xor2 - passcode_pos --> xor2 - space:5 - mask["enciphered mask:\n [ 3493 58469 55184 8226 64562 65478 39115 43240 43951 54660]"] - xor2 --> mask -``` - -### Validate nKode - -```mermaid -block-beta - columns 3 - selected_keys["selected keys:\n[4, 0, 4, 2]"] - login_keypad["login keypad:\nKey 0: [36 28 38 48 49 32 51 34 17] -Key 1: [ 9 19 2 3 4 41 33 52 8] -Key 2: [18 10 47 39 31 23 24 16 35] -Key 3: [27 46 29 21 22 14 6 43 26] -Key 4: [45 37 11 12 40 5 42 7 53] -Key 5: [ 0 1 20 30 13 50 15 25 44] -"] - space:4 - - selectkeys(("select keys")) - mask["enciphered mask:\n [ 3493 58469 55184 8226 64562 65478 39115 43240 43951 54660]"] - mask_key["mask key:\n[43734 31472 30293 34641 24055 5782 14606 48388 2666 19217]"] - space:2 - - xor1(("XOR")) - mask --> xor1 - mask_key --> xor1 - selected_keys --> selectkeys - login_keypad --> selectkeys - space:3 - - ordered_keys["ordered keys:\n[[45 37 11 12 40 5 42 7 53] - [36 28 38 48 49 32 51 34 17] - [45 37 11 12 40 5 42 7 53] - [18 10 47 39 31 23 24 16 35]]"] - user_position_key["user position key:\n[20407 42867 40597 20792 41413 36707 59728 31346 5612]"] - passcode_pos["ordered user passcode positions:\n[42867 40597 41413 42867 41413 59728 41413 5612 41413 40597]"] - selectkeys --> ordered_keys - xor1 --> passcode_pos - space:8 - - get_passcode_idxs(("recover passcode\nposition indices")) - user_position_key --> get_passcode_idxs - passcode_pos --> get_passcode_idxs - space:8 - - passcode_pos_idxs["padded passcode position indices:\n[1, 2, 4, 1, 4, 6, 4, 8, 4, 2]"] - get_passcode_idxs --> passcode_pos_idxs - space:3 - - get_presumed_idxs(("recover passcode\nproperty indices")) - ordered_keys --> get_presumed_idxs - passcode_pos_idxs --> get_presumed_idxs - space:5 - - passcode_prop_idxs["presumed passcode property indices:\n[37, 38, 40, 10]"] - prop["combined_property_key\n[30821 3466 44250 25125 12000 28631 18755 47674 39748 44826 63799 44556 - 56524 2214 32222 30993 35741 23971 31827 61015 47496 37765 56108 49196 - 13889 14121 13583 60138 19221 45386 21155 33931 1869 33046 24822 15526 - 35156 31004 63717 45289 11060 13028 13980 39298 64141 25653 12302 46090 - 40799 20649 24272 181 65477 17748]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$am68CvhXvNZFJE1qsmLSL.gC5I.cykt7LbisLcuUNkoFZ18vV1DPO"] - get_presumed_idxs --> passcode_prop_idxs - space:3 - - sel(("select\nproperties")) - passcode_prop_idxs --> sel - prop --> sel - space:5 - - passcode_prop["presumed passcode properties:\n[31004 63717 11060 63799]"] - sel --> passcode_prop - space:5 - - cipher(("encipher")) - passcode_prop --> cipher - space:5 - - cipheredpass["ciphered passcode:\n[ 5939 10436 52967 57187 48933 51588 25408 62022 36721 9334]"] - cipher --> cipheredpass - space:7 - - - comp{"compare"} - cipheredpass --> comp - cipheredhashed --> comp - space:5 - - suc(("success")) - comp --"Equal"--> suc - -``` \ No newline at end of file diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md new file mode 100644 index 0000000..2c15365 --- /dev/null +++ b/docs/encipher_decipher_renew_nkode.md @@ -0,0 +1,330 @@ +# Encipher and Decipher nKode + +## Customer Policy +- max nkode length: 10 +- number of keys: 6 +- properties per key: 9 +- total number of properties: 54 + +## Customer Cipher +- property key: [40493 16536 22567 8632 16931 48077 38465 62133 61510 60074 30483 15029 + 32328 35555 12264 4839 56666 52299 23010 54024 11530 38809 33384 9334 + 53340 25808 36388 7068 48724 2963 19292 41164 31037 24848 7109 29039 + 26458 18532 7462 19458 51071 45191 60228 22739 41943 36765 29356 35589 + 3387 28997 31698 23822 30837 4299] +- position key: [ 8625 16938 65018 7492 24125 667 1225 54737 47670] + + +--- + +## User Cipher +- property key: [57486 43228 16207 60359 59029 6255 7588 52492 24710 2551 40990 56898 + 56863 1051 48892 2945 40292 31660 45216 53538 40537 15451 52595 37467 + 3706 19342 38794 33132 21407 3639 12679 9190 10068 50771 64668 35246 + 60714 28831 4623 8674 56989 23715 57671 23914 51409 36438 10577 34976 + 29440 9581 47915 29410 39979 5656] +- passcode key: [21257 32848 55275 58059 52662 31196 41361 14925 10153 11854] +- combined position key: [34963 12677 35658 5478 60936 17345 54640 40685 50309] +- mask key: [23832 11427 59440 48739 10483 30872 4800 48753 55621 53879] + +### Combined Postion Key +```mermaid +block-beta + columns 2 + user_pos["user position key:\n[58068 33848 20979 58547 24202 53969 27574 25558 64861]"] + customer_pos["customer position key:\n[ 8625 16938 65018 7492 24125 667 1225 54737 47670]"] + space:2 + xor(("XOR")):2 + user_pos --> xor + customer_pos --> xor + space:2 + comb_pos["combined position key\n[34963 12677 35658 5478 60936 17345 54640 40685 50309]"]:2 + xor --> comb_pos +``` + +## User Keypad +- keypad: +- user passcode indices: [22, 34, 45, 43] + +## nKode Cipher + +### Passcode Hash +```mermaid +block-beta + columns 2 + cprop["customer_property_key\n[40493 16536 22567 8632 16931 48077 38465 62133 61510 60074 30483 15029 + 32328 35555 12264 4839 56666 52299 23010 54024 11530 38809 33384 9334 + 53340 25808 36388 7068 48724 2963 19292 41164 31037 24848 7109 29039 + 26458 18532 7462 19458 51071 45191 60228 22739 41943 36765 29356 35589 + 3387 28997 31698 23822 30837 4299]"] + uprop["user_property_key\n[57486 43228 16207 60359 59029 6255 7588 52492 24710 2551 40990 56898 + 56863 1051 48892 2945 40292 31660 45216 53538 40537 15451 52595 37467 + 3706 19342 38794 33132 21407 3639 12679 9190 10068 50771 64668 35246 + 60714 28831 4623 8674 56989 23715 57671 23914 51409 36438 10577 34976 + 29440 9581 47915 29410 39979 5656]"] + space:2 + xor1(("XOR")):2 + cprop --> xor1 + uprop --> xor1 + space:2 + prop["combined_property_key\n[62032 50130 36776 54558 8506 61182 6770 6684 21325 58903 51619 53457 + 24017 10096 25891 36737 43828 60364 23965 63299 43548 43762 63289 61210 + 16490 21923 14215 6365 4383 48550 62830 53206 5237 2426 49278 33751 + 21542 2104 49342 42984 62776 19754 59167 25211 60 58461 118 9841 + 52387 3482 40915 54740 30225 8961]"] + xor1 --> prop + pass["user_passcode_indices\n[22, 34, 45, 43]"] + space:2 + sel(("select\nproperties")):2 + pass --> sel + prop --> sel + space:2 + passcode["user passcode properties:\n[63289 49278 58461 25211]"]:2 + sel --> passcode + space:2 + pad["zero pad to\nmax nkode length: 10"]:2 + passcode -->pad + space:2 + paddedpasscode["padded passcode:\n[63289 49278 58461 25211 0 0 0 0 0 0]"] + pad --> paddedpasscode + passkey["passcode key:\n[21257 32848 55275 58059 52662 31196 41361 14925 10153 11854]"] + space:2 + xor2(("XOR")):2 + passkey --> xor2 + paddedpasscode --> xor2 + space:2 + cipheredpass["ciphered passcode:\n[42032 16430 13238 32944 52662 31196 41361 14925 10153 11854]"]:2 + xor2 --> cipheredpass + space:2 + hash(("hash")):2 + cipheredpass --> hash + space:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$c62ezzyLW3QzUMjYUPSioexdMtSuJKzSM4KLkdKhWIiuNzcxOfT8m"]:2 + hash --> cipheredhashed +``` + +### Mask Encipher +```mermaid +block-beta + columns 3 + passcode_idx["passcode indices:\n[22, 34, 45, 43]"] + comb_pos["combined position key:\n[34963 12677 35658 5478 60936 17345 54640 40685 50309]"] + cust_pos["customer position key:\n[ 8625 16938 65018 7492 24125 667 1225 54737 47670]"] + + space:3 + propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) + passcode_idx-->propidx + space:1 + xor1(("XOR")) + comb_pos --> xor1 + cust_pos --> xor1 + + space:3 + passcode_position_idx["passcode poition indices:\n[4, 7, 0, 7]"] + propidx --> passcode_position_idx + + space:5 + pad1(("Pad with\nrandom indices")) + passcode_position_idx --> pad1 + + space:5 + posidx["Padded Passcode Position Indices:\n[4, 7, 0, 7, 2, 2, 0, 3, 7, 8]"] + pad1 --> posidx + space:1 + user_pos["user position key:\n[58068 33848 20979 58547 24202 53969 27574 25558 64861]"] + xor1 --> user_pos + + space:4 + sel(("select positions")) + user_pos --> sel + posidx --> sel + space:5 + passcode_pos["ordered user passcode positions:\n[24202 25558 58068 25558 20979 20979 58068 58547 25558 64861]"] + sel --> passcode_pos + mask_key["mask key\n[23832 11427 59440 48739 10483 30872 4800 48753 55621 53879]"] + space:4 + xor2(("XOR")) + mask_key --> xor2 + passcode_pos --> xor2 + space:5 + mask["enciphered mask:\n [ 914 20341 2788 56757 30976 10603 61460 23234 47763 12074]"] + xor2 --> mask +``` + +### Validate nKode + +```mermaid +block-beta + columns 3 + selected_keys["selected keys:\n[5, 3, 2, 2]"] + login_keypad["login keypad:\nKey 0: [18 19 11 30 40 41 24 52 26] +Key 1: [36 1 47 12 49 32 42 25 35] +Key 2: [45 46 29 39 13 50 33 43 17] +Key 3: [27 37 2 3 31 5 6 34 8] +Key 4: [ 9 10 20 21 4 23 15 7 53] +Key 5: [ 0 28 38 48 22 14 51 16 44] +"] + space:4 + + selectkeys(("select keys")) + mask["enciphered mask:\n [ 914 20341 2788 56757 30976 10603 61460 23234 47763 12074]"] + mask_key["mask key:\n[23832 11427 59440 48739 10483 30872 4800 48753 55621 53879]"] + space:2 + + xor1(("XOR")) + mask --> xor1 + mask_key --> xor1 + selected_keys --> selectkeys + login_keypad --> selectkeys + space:3 + + ordered_keys["ordered keys:\n[[ 0 28 38 48 22 14 51 16 44] + [27 37 2 3 31 5 6 34 8] + [45 46 29 39 13 50 33 43 17] + [45 46 29 39 13 50 33 43 17]]"] + user_position_key["user position key:\n[58068 33848 20979 58547 24202 53969 27574 25558 64861]"] + passcode_pos["ordered user passcode positions:\n[24202 25558 58068 25558 20979 20979 58068 58547 25558 64861]"] + selectkeys --> ordered_keys + xor1 --> passcode_pos + space:8 + + get_passcode_idxs(("recover passcode\nposition indices")) + user_position_key --> get_passcode_idxs + passcode_pos --> get_passcode_idxs + space:8 + + passcode_pos_idxs["padded passcode position indices:\n[4, 7, 0, 7, 2, 2, 0, 3, 7, 8]"] + get_passcode_idxs --> passcode_pos_idxs + space:3 + + get_presumed_idxs(("recover passcode\nproperty indices")) + ordered_keys --> get_presumed_idxs + passcode_pos_idxs --> get_presumed_idxs + space:5 + + passcode_prop_idxs["presumed passcode property indices:\n[22, 34, 45, 43]"] + prop["combined_property_key\n[62032 50130 36776 54558 8506 61182 6770 6684 21325 58903 51619 53457 + 24017 10096 25891 36737 43828 60364 23965 63299 43548 43762 63289 61210 + 16490 21923 14215 6365 4383 48550 62830 53206 5237 2426 49278 33751 + 21542 2104 49342 42984 62776 19754 59167 25211 60 58461 118 9841 + 52387 3482 40915 54740 30225 8961]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$c62ezzyLW3QzUMjYUPSioexdMtSuJKzSM4KLkdKhWIiuNzcxOfT8m"] + get_presumed_idxs --> passcode_prop_idxs + space:3 + + sel(("select\nproperties")) + passcode_prop_idxs --> sel + prop --> sel + space:5 + + passcode_prop["presumed passcode properties:\n[63289 49278 58461 25211]"] + sel --> passcode_prop + space:5 + + cipher(("encipher")) + passcode_prop --> cipher + space:5 + + cipheredpass["ciphered passcode:\n[42032 16430 13238 32944 52662 31196 41361 14925 10153 11854]"] + cipher --> cipheredpass + space:7 + + + comp{"compare"} + cipheredpass --> comp + cipheredhashed --> comp + space:5 + + suc(("success")) + comp --"Equal"--> suc +``` + +### Renew nKode + +nKode renewal is a three step process: +1. Renew Customer Properties +2. Renew User Keys +3. Refresh User on Login + + +```mermaid + flowchart + subgraph Renew Customer Properties + old_prop["`old customer property key:
[ 4830 27406 45287 16089 51119 63121 2006 55056 13259 61408 27069 3731 + 33742 9067 56287 33792 13904 36960 60733 9825 13381 38569 14922 32065 + 19984 7725 40973 39345 17024 45969 50409 60464 13089 53033 15586 2681 + 47372 30887 53937 34314 11173 4489 1624 16145 51437 27147 10535 44753 + 49059 10487 9464 42806 59962 13593]`"] + new_prop["`new customer property key:
[40493 16536 22567 8632 16931 48077 38465 62133 61510 60074 30483 15029 + 32328 35555 12264 4839 56666 52299 23010 54024 11530 38809 33384 9334 + 53340 25808 36388 7068 48724 2963 19292 41164 31037 24848 7109 29039 + 26458 18532 7462 19458 51071 45191 60228 22739 41943 36765 29356 35589 + 3387 28997 31698 23822 30837 4299]`"] + old_pos["`old customer position key:
[27207 46525 55993 61909 45186 37136 48838 64827 14808]`"] + new_pos["`new customer position key:
[ 8625 16938 65018 7492 24125 667 1225 54737 47670]`"] + xor1(("XOR")) + xor2(("XOR")) + xor_prop["`xor property key:
[36083 11158 59584 8033 34188 19804 37271 9637 50061 1354 7854 13350 + 64902 43400 62519 38631 60170 23595 46303 62825 6479 304 47138 22839 + 40524 31485 11817 33325 64724 47106 36789 19708 18972 44601 10023 31510 + 56918 12483 53143 51720 60634 41230 60700 26562 27450 58774 23435 9684 + 45720 22962 24362 64056 37455 9682]`"] + xor_pos["`xor position key:
[19446 63383 10051 60561 61119 37771 47631 10474 33774]`"] + old_prop --> xor1 + new_prop --> xor1 + xor1 --> xor_prop + old_pos --> xor2 + new_pos --> xor2 + xor2 --> xor_pos + end + + subgraph Renew User Keys + users@{shape: procs, label: "users"} + users --> eachuser + subgraph eachuser [for each user] + subgraph old user keys + old_user_pos["`combined position key:
[58068 33848 20979 58547 24202 53969 27574 25558 64861]`"] + old_user_prop["`property key:
[57486 43228 16207 60359 59029 6255 7588 52492 24710 2551 40990 56898 + 56863 1051 48892 2945 40292 31660 45216 53538 40537 15451 52595 37467 + 3706 19342 38794 33132 21407 3639 12679 9190 10068 50771 64668 35246 + 60714 28831 4623 8674 56989 23715 57671 23914 51409 36438 10577 34976 + 29440 9581 47915 29410 39979 5656]`"] + old_renew["renew: False"] + end + xor3(("XOR")) + xor4(("XOR")) + old_user_pos --> xor3 + xor_pos --> xor3 + xor3 --> inter_user_pos + old_user_prop --> xor4 + xor_prop --> xor4 + xor4 --> inter_user_prop + subgraph inter_user[intermediate user keys] + inter_user_pos["`combined position key:
[50021 50706 44041 63991 183 53322 28543 46599 18283]`"] + inter_user_prop["`property key:
[27773 33610 55183 62630 25369 21811 35891 59561 41739 3261 48816 60004 + 9113 44435 19147 40294 30318 10119 1151 9291 34582 15723 30033 52076 + 36918 12659 47523 833 44875 46645 48690 28442 27976 26730 56251 62136 + 13180 16476 56728 60394 12871 64941 3163 15016 41963 27584 29402 44404 + 49560 31967 58369 35034 3684 13258]`"] + inter_renew["renew: True"] + end + end + + end + + subgraph Refresh User on Login + login["First login post renew"] + inter_user --> login + subgraph new_user [New User Keys] + new_user_pos["`combined position key:
[41920 44706 35397 43292 52009 11204 43818 26002 28955]`"] + new_user_prop["`property key:
[50355 56723 59762 39268 44255 59491 12736 62545 3731 51049 48946 10598 + 23999 55461 35771 20498 38352 13185 17075 30409 22199 3533 32360 12423 + 10680 48407 6174 11559 44174 8103 37520 52514 30628 49367 7063 8839 + 30149 1724 51332 64105 26257 48370 17405 42481 4953 43432 15038 37511 + 62432 22826 16312 39525 13907 33285]`"] + new_renew["renew: False"] + end + login --> new_user + end + +``` \ No newline at end of file diff --git a/docs/scripts/render_encipher_decipher_diagrams.py b/docs/scripts/render_encipher_decipher_diagrams.py index 18e84d4..466b853 100644 --- a/docs/scripts/render_encipher_decipher_diagrams.py +++ b/docs/scripts/render_encipher_decipher_diagrams.py @@ -9,6 +9,7 @@ from string import ascii_lowercase from docs.scripts.utils import render_markdown_template, emojis from src.models import NKodePolicy, KeypadSize from src.nkode_api import NKodeAPI +from src.user_cipher import UserCipher from src.utils import select_keys_with_passcode_values def display_keypad(icons_array: np.ndarray, props_per_key: int) -> str: @@ -65,6 +66,17 @@ if __name__ == "__main__": login_keypad = api.get_login_keypad(username, customer_id) selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key) + + old_props = customer.cipher.property_key.copy() + old_pos = customer.cipher.position_key.copy() + customer.cipher.property_key = np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False) + customer.cipher.position_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False) + new_props = customer.cipher.property_key + new_pos = customer.cipher.position_key + props_xor = new_props ^ old_props + pos_xor = new_pos ^ old_pos + user = customer.users[username] + new_user_cipher = UserCipher.create(keypad_size, customer.cipher.position_key, policy.max_nkode_len) context = { "max_nkode_len": policy.max_nkode_len, "numb_of_keys": keypad_size.numb_of_keys, @@ -89,5 +101,15 @@ if __name__ == "__main__": "selected_keys": selected_keys_login, "login_keypad": display_keypad(login_keypad, keypad_size.props_per_key), "ordered_keys": login_keypad.reshape(-1, keypad_size.props_per_key)[selected_keys_login], + "old_props": old_props, + "new_props": new_props, + "old_pos": old_pos, + "new_pos": new_pos, + "xor_props": props_xor, + "xor_pos": pos_xor, + "inter_user_position": user.cipher.combined_position_key ^ pos_xor, + "inter_user_property_key": user.cipher.property_key ^ props_xor, + "new_user_position": new_user_cipher.combined_position_key, + "new_user_property_key": new_user_cipher.property_key, } - render_markdown_template(Path("../templates/encipher_decipher_nkode.template.md"), Path("../encipher_decipher_nkode.md"), context) + render_markdown_template(Path("../templates/encipher_decipher_renew_nkode.template.md"), Path("../encipher_decipher_renew_nkode.md"), context) diff --git a/docs/templates/encipher_decipher_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md similarity index 71% rename from docs/templates/encipher_decipher_nkode.template.md rename to docs/templates/encipher_decipher_renew_nkode.template.md index 9ca9bf6..bfaa360 100644 --- a/docs/templates/encipher_decipher_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -204,5 +204,71 @@ block-beta suc(("success")) comp --"Equal"--> suc +``` + +### Renew nKode + +nKode renewal is a three step process: +1. Renew Customer Properties +2. Renew User Keys +3. Refresh User on Login + + +{% set md_tick = '`' %} +```mermaid + flowchart + subgraph Renew Customer Properties + old_prop["`old customer property key:
{{old_props}}`"] + new_prop["`new customer property key:
{{new_props}}`"] + old_pos["`old customer position key:
{{old_pos}}`"] + new_pos["`new customer position key:
{{new_pos}}`"] + xor1(("XOR")) + xor2(("XOR")) + xor_prop["`xor property key:
{{xor_props}}`"] + xor_pos["`xor position key:
{{xor_pos}}`"] + old_prop --> xor1 + new_prop --> xor1 + xor1 --> xor_prop + old_pos --> xor2 + new_pos --> xor2 + xor2 --> xor_pos + end + + subgraph Renew User Keys + users@{shape: procs, label: "users"} + users --> eachuser + subgraph eachuser [for each user] + subgraph old user keys + old_user_pos["`combined position key:
{{user_position_key}}`"] + old_user_prop["`property key:
{{user_property_key}}`"] + old_renew["renew: False"] + end + xor3(("XOR")) + xor4(("XOR")) + old_user_pos --> xor3 + xor_pos --> xor3 + xor3 --> inter_user_pos + old_user_prop --> xor4 + xor_prop --> xor4 + xor4 --> inter_user_prop + subgraph inter_user[intermediate user keys] + inter_user_pos["`combined position key:
{{inter_user_position}}`"] + inter_user_prop["`property key:
{{inter_user_property_key}}`"] + inter_renew["renew: True"] + end + end + + end + + subgraph Refresh User on Login + login["First login post renew"] + inter_user --> login + subgraph new_user [New User Keys] + new_user_pos["`combined position key:
{{new_user_position}}`"] + new_user_prop["`property key:
{{new_user_property_key}}`"] + new_renew["renew: False"] + end + login --> new_user + end ``` -- 2.49.1 From 10c83ac965548026987b37a755f34f10f323b705 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 08:05:31 -0500 Subject: [PATCH 62/85] update renew --- docs/encipher_decipher_renew_nkode.md | 230 +++++++++--------- .../encipher_decipher_renew_nkode.template.md | 12 +- .../Enrollment_Login_Renewal_Detailed.ipynb | 10 +- 3 files changed, 126 insertions(+), 126 deletions(-) diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md index 2c15365..f25c2a4 100644 --- a/docs/encipher_decipher_renew_nkode.md +++ b/docs/encipher_decipher_renew_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [40493 16536 22567 8632 16931 48077 38465 62133 61510 60074 30483 15029 - 32328 35555 12264 4839 56666 52299 23010 54024 11530 38809 33384 9334 - 53340 25808 36388 7068 48724 2963 19292 41164 31037 24848 7109 29039 - 26458 18532 7462 19458 51071 45191 60228 22739 41943 36765 29356 35589 - 3387 28997 31698 23822 30837 4299] -- position key: [ 8625 16938 65018 7492 24125 667 1225 54737 47670] +- property key: [12683 37623 46427 24232 53244 25444 34138 18278 52542 17394 45705 57706 + 22403 60684 20477 13672 47069 29025 8419 8211 36163 21756 45607 31517 + 44312 19522 17814 41879 18325 2622 43956 30841 59391 52171 51029 44917 + 28846 25756 24212 4239 11940 31284 58701 7692 31504 25629 20378 35504 + 45861 59860 4819 52686 62304 58952] +- position key: [29587 60364 47856 53105 61491 65374 34021 4048 33167] --- ## User Cipher -- property key: [57486 43228 16207 60359 59029 6255 7588 52492 24710 2551 40990 56898 - 56863 1051 48892 2945 40292 31660 45216 53538 40537 15451 52595 37467 - 3706 19342 38794 33132 21407 3639 12679 9190 10068 50771 64668 35246 - 60714 28831 4623 8674 56989 23715 57671 23914 51409 36438 10577 34976 - 29440 9581 47915 29410 39979 5656] -- passcode key: [21257 32848 55275 58059 52662 31196 41361 14925 10153 11854] -- combined position key: [34963 12677 35658 5478 60936 17345 54640 40685 50309] -- mask key: [23832 11427 59440 48739 10483 30872 4800 48753 55621 53879] +- property key: [14398 56125 29659 36184 61716 58844 42259 48679 36351 53209 63475 35295 + 1927 35139 48674 32220 3724 62784 11332 63262 61320 62990 59038 61659 + 31829 24135 49167 50519 23410 2470 22259 32544 30127 27527 18620 39840 + 105 31222 38444 37307 58991 39926 16285 42364 843 30922 30467 43324 + 13596 3432 28064 33665 35252 8733] +- passcode key: [43892 51915 3968 29221 4 52573 20838 48052 39138 14671] +- combined position key: [40967 35807 54041 52517 31281 56961 42242 11385 1018] +- mask key: [57731 56088 1801 26915 40765 22438 9693 31860 17942 7993] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[58068 33848 20979 58547 24202 53969 27574 25558 64861]"] - customer_pos["customer position key:\n[ 8625 16938 65018 7492 24125 667 1225 54737 47670]"] + user_pos["user position key:\n[24890 2120 45550 47291 61945 28551 49488 49734 20771]"] + customer_pos["customer position key:\n[29587 60364 47856 53105 61491 65374 34021 4048 33167]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[34963 12677 35658 5478 60936 17345 54640 40685 50309]"]:2 + comb_pos["combined position key\n[40967 35807 54041 52517 31281 56961 42242 11385 1018]"]:2 xor --> comb_pos ``` ## User Keypad - keypad: -- user passcode indices: [22, 34, 45, 43] +- user passcode indices: [12, 4, 51, 33] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[40493 16536 22567 8632 16931 48077 38465 62133 61510 60074 30483 15029 - 32328 35555 12264 4839 56666 52299 23010 54024 11530 38809 33384 9334 - 53340 25808 36388 7068 48724 2963 19292 41164 31037 24848 7109 29039 - 26458 18532 7462 19458 51071 45191 60228 22739 41943 36765 29356 35589 - 3387 28997 31698 23822 30837 4299]"] - uprop["user_property_key\n[57486 43228 16207 60359 59029 6255 7588 52492 24710 2551 40990 56898 - 56863 1051 48892 2945 40292 31660 45216 53538 40537 15451 52595 37467 - 3706 19342 38794 33132 21407 3639 12679 9190 10068 50771 64668 35246 - 60714 28831 4623 8674 56989 23715 57671 23914 51409 36438 10577 34976 - 29440 9581 47915 29410 39979 5656]"] + cprop["customer_property_key\n[12683 37623 46427 24232 53244 25444 34138 18278 52542 17394 45705 57706 + 22403 60684 20477 13672 47069 29025 8419 8211 36163 21756 45607 31517 + 44312 19522 17814 41879 18325 2622 43956 30841 59391 52171 51029 44917 + 28846 25756 24212 4239 11940 31284 58701 7692 31504 25629 20378 35504 + 45861 59860 4819 52686 62304 58952]"] + uprop["user_property_key\n[14398 56125 29659 36184 61716 58844 42259 48679 36351 53209 63475 35295 + 1927 35139 48674 32220 3724 62784 11332 63262 61320 62990 59038 61659 + 31829 24135 49167 50519 23410 2470 22259 32544 30127 27527 18620 39840 + 105 31222 38444 37307 58991 39926 16285 42364 843 30922 30467 43324 + 13596 3432 28064 33665 35252 8733]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[62032 50130 36776 54558 8506 61182 6770 6684 21325 58903 51619 53457 - 24017 10096 25891 36737 43828 60364 23965 63299 43548 43762 63289 61210 - 16490 21923 14215 6365 4383 48550 62830 53206 5237 2426 49278 33751 - 21542 2104 49342 42984 62776 19754 59167 25211 60 58461 118 9841 - 52387 3482 40915 54740 30225 8961]"] + prop["combined_property_key\n[32565 54767 5127 26787 61372 9024 10051 31192 36215 25953 63643 7907 + 34275 49131 65253 35224 52025 49525 36214 22719 57589 6411 46986 50058 + 38701 701 36150 6894 36770 58151 63226 62425 5898 2177 3529 29703 + 30896 41755 9016 47283 14611 18887 54104 55155 32367 2577 31449 39233 + 18953 7162 61412 6044 61623 4668]"] xor1 --> prop - pass["user_passcode_indices\n[22, 34, 45, 43]"] + pass["user_passcode_indices\n[12, 4, 51, 33]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[63289 49278 58461 25211]"]:2 + passcode["user passcode properties:\n[34275 61372 6044 2177]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[63289 49278 58461 25211 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[34275 61372 6044 2177 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[21257 32848 55275 58059 52662 31196 41361 14925 10153 11854]"] + passkey["passcode key:\n[43892 51915 3968 29221 4 52573 20838 48052 39138 14671]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[42032 16430 13238 32944 52662 31196 41361 14925 10153 11854]"]:2 + cipheredpass["ciphered passcode:\n[11927 9591 6172 31396 4 52573 20838 48052 39138 14671]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$c62ezzyLW3QzUMjYUPSioexdMtSuJKzSM4KLkdKhWIiuNzcxOfT8m"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$Jf48A5rmjkxb0II7CkuD8.Qg3GCuROgbS99pMbthC2gXt9szx9gmm"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[22, 34, 45, 43]"] - comb_pos["combined position key:\n[34963 12677 35658 5478 60936 17345 54640 40685 50309]"] - cust_pos["customer position key:\n[ 8625 16938 65018 7492 24125 667 1225 54737 47670]"] + passcode_idx["passcode indices:\n[12, 4, 51, 33]"] + comb_pos["combined position key:\n[40967 35807 54041 52517 31281 56961 42242 11385 1018]"] + cust_pos["customer position key:\n[29587 60364 47856 53105 61491 65374 34021 4048 33167]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[4, 7, 0, 7]"] + passcode_position_idx["passcode poition indices:\n[3, 4, 6, 6]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[4, 7, 0, 7, 2, 2, 0, 3, 7, 8]"] + posidx["Padded Passcode Position Indices:\n[3, 4, 6, 6, 3, 3, 7, 5, 0, 8]"] pad1 --> posidx space:1 - user_pos["user position key:\n[58068 33848 20979 58547 24202 53969 27574 25558 64861]"] + user_pos["user position key:\n[24890 2120 45550 47291 61945 28551 49488 49734 20771]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[24202 25558 58068 25558 20979 20979 58068 58547 25558 64861]"] + passcode_pos["ordered user passcode positions:\n[47291 61945 49488 49488 47291 47291 49734 28551 24890 20771]"] sel --> passcode_pos - mask_key["mask key\n[23832 11427 59440 48739 10483 30872 4800 48753 55621 53879]"] + mask_key["mask key\n[57731 56088 1801 26915 40765 22438 9693 31860 17942 7993]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [ 914 20341 2788 56757 30976 10603 61460 23234 47763 12074]"] + mask["enciphered mask:\n [22840 10977 50777 43123 10118 61213 59291 5107 10028 19994]"] xor2 --> mask ``` @@ -156,19 +156,19 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n[5, 3, 2, 2]"] - login_keypad["login keypad:\nKey 0: [18 19 11 30 40 41 24 52 26] -Key 1: [36 1 47 12 49 32 42 25 35] -Key 2: [45 46 29 39 13 50 33 43 17] -Key 3: [27 37 2 3 31 5 6 34 8] -Key 4: [ 9 10 20 21 4 23 15 7 53] -Key 5: [ 0 28 38 48 22 14 51 16 44] + selected_keys["selected keys:\n[1, 0, 5, 0]"] + login_keypad["login keypad:\nKey 0: [45 46 29 48 4 50 33 16 17] +Key 1: [18 1 20 12 49 5 15 34 44] +Key 2: [27 10 38 3 31 14 24 7 26] +Key 3: [ 0 37 11 39 22 23 42 52 8] +Key 4: [36 19 2 21 40 32 6 25 35] +Key 5: [ 9 28 47 30 13 41 51 43 53] "] space:4 selectkeys(("select keys")) - mask["enciphered mask:\n [ 914 20341 2788 56757 30976 10603 61460 23234 47763 12074]"] - mask_key["mask key:\n[23832 11427 59440 48739 10483 30872 4800 48753 55621 53879]"] + mask["enciphered mask:\n [22840 10977 50777 43123 10118 61213 59291 5107 10028 19994]"] + mask_key["mask key:\n[57731 56088 1801 26915 40765 22438 9693 31860 17942 7993]"] space:2 xor1(("XOR")) @@ -178,12 +178,12 @@ Key 5: [ 0 28 38 48 22 14 51 16 44] login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[ 0 28 38 48 22 14 51 16 44] - [27 37 2 3 31 5 6 34 8] - [45 46 29 39 13 50 33 43 17] - [45 46 29 39 13 50 33 43 17]]"] - user_position_key["user position key:\n[58068 33848 20979 58547 24202 53969 27574 25558 64861]"] - passcode_pos["ordered user passcode positions:\n[24202 25558 58068 25558 20979 20979 58068 58547 25558 64861]"] + ordered_keys["ordered keys:\n[[18 1 20 12 49 5 15 34 44] + [45 46 29 48 4 50 33 16 17] + [ 9 28 47 30 13 41 51 43 53] + [45 46 29 48 4 50 33 16 17]]"] + user_position_key["user position key:\n[24890 2120 45550 47291 61945 28551 49488 49734 20771]"] + passcode_pos["ordered user passcode positions:\n[47291 61945 49488 49488 47291 47291 49734 28551 24890 20771]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [ 0 28 38 48 22 14 51 16 44] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[4, 7, 0, 7, 2, 2, 0, 3, 7, 8]"] + passcode_pos_idxs["padded passcode position indices:\n[3, 4, 6, 6, 3, 3, 7, 5, 0, 8]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [ 0 28 38 48 22 14 51 16 44] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[22, 34, 45, 43]"] - prop["combined_property_key\n[62032 50130 36776 54558 8506 61182 6770 6684 21325 58903 51619 53457 - 24017 10096 25891 36737 43828 60364 23965 63299 43548 43762 63289 61210 - 16490 21923 14215 6365 4383 48550 62830 53206 5237 2426 49278 33751 - 21542 2104 49342 42984 62776 19754 59167 25211 60 58461 118 9841 - 52387 3482 40915 54740 30225 8961]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$c62ezzyLW3QzUMjYUPSioexdMtSuJKzSM4KLkdKhWIiuNzcxOfT8m"] + passcode_prop_idxs["presumed passcode property indices:\n[12, 4, 51, 33]"] + prop["combined_property_key\n[32565 54767 5127 26787 61372 9024 10051 31192 36215 25953 63643 7907 + 34275 49131 65253 35224 52025 49525 36214 22719 57589 6411 46986 50058 + 38701 701 36150 6894 36770 58151 63226 62425 5898 2177 3529 29703 + 30896 41755 9016 47283 14611 18887 54104 55155 32367 2577 31449 39233 + 18953 7162 61412 6044 61623 4668]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$Jf48A5rmjkxb0II7CkuD8.Qg3GCuROgbS99pMbthC2gXt9szx9gmm"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [ 0 28 38 48 22 14 51 16 44] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[63289 49278 58461 25211]"] + passcode_prop["presumed passcode properties:\n[34275 61372 6044 2177]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [ 0 28 38 48 22 14 51 16 44] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[42032 16430 13238 32944 52662 31196 41361 14925 10153 11854]"] + cipheredpass["ciphered passcode:\n[11927 9591 6172 31396 4 52573 20838 48052 39138 14671]"] cipher --> cipheredpass space:7 @@ -242,34 +242,34 @@ Key 5: [ 0 28 38 48 22 14 51 16 44] ### Renew nKode nKode renewal is a three step process: -1. Renew Customer Properties -2. Renew User Keys -3. Refresh User on Login +1. Renew Customer Keys +2. Intermediate User Keys +3. Renew User Keys on Login ```mermaid flowchart - subgraph Renew Customer Properties - old_prop["`old customer property key:
[ 4830 27406 45287 16089 51119 63121 2006 55056 13259 61408 27069 3731 - 33742 9067 56287 33792 13904 36960 60733 9825 13381 38569 14922 32065 - 19984 7725 40973 39345 17024 45969 50409 60464 13089 53033 15586 2681 - 47372 30887 53937 34314 11173 4489 1624 16145 51437 27147 10535 44753 - 49059 10487 9464 42806 59962 13593]`"] - new_prop["`new customer property key:
[40493 16536 22567 8632 16931 48077 38465 62133 61510 60074 30483 15029 - 32328 35555 12264 4839 56666 52299 23010 54024 11530 38809 33384 9334 - 53340 25808 36388 7068 48724 2963 19292 41164 31037 24848 7109 29039 - 26458 18532 7462 19458 51071 45191 60228 22739 41943 36765 29356 35589 - 3387 28997 31698 23822 30837 4299]`"] - old_pos["`old customer position key:
[27207 46525 55993 61909 45186 37136 48838 64827 14808]`"] - new_pos["`new customer position key:
[ 8625 16938 65018 7492 24125 667 1225 54737 47670]`"] + subgraph Renew Customer Keys + old_prop["`old customer property key:
[18187 3794 26588 58875 7848 50844 33360 51199 136 43704 3944 38716 + 33380 13992 16583 62532 50613 13365 41266 44961 3965 61189 20756 13137 + 60280 23802 19769 57273 54480 60033 40969 36089 25253 25350 17781 61351 + 30937 56045 46356 10504 57212 53809 60613 29199 32036 29403 3546 12413 + 32533 5778 33348 37917 30979 12321]`"] + new_prop["`new customer property key:
[12683 37623 46427 24232 53244 25444 34138 18278 52542 17394 45705 57706 + 22403 60684 20477 13672 47069 29025 8419 8211 36163 21756 45607 31517 + 44312 19522 17814 41879 18325 2622 43956 30841 59391 52171 51029 44917 + 28846 25756 24212 4239 11940 31284 58701 7692 31504 25629 20378 35504 + 45861 59860 4819 52686 62304 58952]`"] + old_pos["`old customer position key:
[49469 33687 25335 30110 35784 45318 25682 60991 21209]`"] + new_pos["`new customer position key:
[29587 60364 47856 53105 61491 65374 34021 4048 33167]`"] xor1(("XOR")) xor2(("XOR")) - xor_prop["`xor property key:
[36083 11158 59584 8033 34188 19804 37271 9637 50061 1354 7854 13350 - 64902 43400 62519 38631 60170 23595 46303 62825 6479 304 47138 22839 - 40524 31485 11817 33325 64724 47106 36789 19708 18972 44601 10023 31510 - 56918 12483 53143 51720 60634 41230 60700 26562 27450 58774 23435 9684 - 45720 22962 24362 64056 37455 9682]`"] - xor_pos["`xor position key:
[19446 63383 10051 60561 61119 37771 47631 10474 33774]`"] + xor_prop["`xor property key:
[30336 39973 53895 47955 53588 42488 1802 32921 52662 59722 48609 30294 + 54759 56228 3898 49452 29288 17748 33233 36786 33342 48121 58163 18508 + 18016 4280 2223 31790 37701 57535 3005 62592 34138 43213 33312 16594 + 2167 48753 60288 14727 61912 43013 2440 27651 1588 5830 16960 47821 + 52272 65350 37015 22995 35427 54889]`"] + xor_pos["`xor position key:
[45742 26715 55303 47855 31739 20056 57527 57839 54102]`"] old_prop --> xor1 new_prop --> xor1 xor1 --> xor_prop @@ -278,17 +278,17 @@ nKode renewal is a three step process: xor2 --> xor_pos end - subgraph Renew User Keys + subgraph Intermediate User Keys users@{shape: procs, label: "users"} users --> eachuser subgraph eachuser [for each user] subgraph old user keys - old_user_pos["`combined position key:
[58068 33848 20979 58547 24202 53969 27574 25558 64861]`"] - old_user_prop["`property key:
[57486 43228 16207 60359 59029 6255 7588 52492 24710 2551 40990 56898 - 56863 1051 48892 2945 40292 31660 45216 53538 40537 15451 52595 37467 - 3706 19342 38794 33132 21407 3639 12679 9190 10068 50771 64668 35246 - 60714 28831 4623 8674 56989 23715 57671 23914 51409 36438 10577 34976 - 29440 9581 47915 29410 39979 5656]`"] + old_user_pos["`combined position key:
[24890 2120 45550 47291 61945 28551 49488 49734 20771]`"] + old_user_prop["`property key:
[14398 56125 29659 36184 61716 58844 42259 48679 36351 53209 63475 35295 + 1927 35139 48674 32220 3724 62784 11332 63262 61320 62990 59038 61659 + 31829 24135 49167 50519 23410 2470 22259 32544 30127 27527 18620 39840 + 105 31222 38444 37307 58991 39926 16285 42364 843 30922 30467 43324 + 13596 3432 28064 33665 35252 8733]`"] old_renew["renew: False"] end xor3(("XOR")) @@ -300,28 +300,28 @@ nKode renewal is a three step process: xor_prop --> xor4 xor4 --> inter_user_prop subgraph inter_user[intermediate user keys] - inter_user_pos["`combined position key:
[50021 50706 44041 63991 183 53322 28543 46599 18283]`"] - inter_user_prop["`property key:
[27773 33610 55183 62630 25369 21811 35891 59561 41739 3261 48816 60004 - 9113 44435 19147 40294 30318 10119 1151 9291 34582 15723 30033 52076 - 36918 12659 47523 833 44875 46645 48690 28442 27976 26730 56251 62136 - 13180 16476 56728 60394 12871 64941 3163 15016 41963 27584 29402 44404 - 49560 31967 58369 35034 3684 13258]`"] + inter_user_pos["`combined position key:
[ 4777 58244 2846 30666 458 37081 17845 52630 53420]`"] + inter_user_prop["`property key:
[20158 18200 41308 13835 8256 16420 41497 16062 16457 9875 18962 65417 + 53856 21223 45336 48368 31972 45076 44437 30892 28086 19959 1453 47255 + 14901 20223 51360 47481 51255 59673 23886 35744 61685 49994 51868 56178 + 2078 51079 32172 43068 6071 13299 13845 51583 1407 28172 13635 5105 + 63788 61998 64823 55890 983 62580]`"] inter_renew["renew: True"] end end end - subgraph Refresh User on Login + subgraph Renew User Keys on Login login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] - new_user_pos["`combined position key:
[41920 44706 35397 43292 52009 11204 43818 26002 28955]`"] - new_user_prop["`property key:
[50355 56723 59762 39268 44255 59491 12736 62545 3731 51049 48946 10598 - 23999 55461 35771 20498 38352 13185 17075 30409 22199 3533 32360 12423 - 10680 48407 6174 11559 44174 8103 37520 52514 30628 49367 7063 8839 - 30149 1724 51332 64105 26257 48370 17405 42481 4953 43432 15038 37511 - 62432 22826 16312 39525 13907 33285]`"] + new_user_pos["`combined position key:
[37567 32382 36425 6535 11819 18071 35301 38878 29144]`"] + new_user_prop["`property key:
[63204 21530 24393 63295 52024 46102 24361 23381 41968 3713 26133 17989 + 14193 46059 12280 45683 35436 9340 17577 37219 15797 39388 52148 29719 + 48300 18038 25938 62757 21612 28454 23892 12945 18057 62761 37107 25062 + 27001 30212 20943 35287 60202 25046 29690 64376 57055 1554 42998 36674 + 45138 58609 25837 59944 8395 52185]`"] new_renew["renew: False"] end login --> new_user diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index bfaa360..61ac96c 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -209,15 +209,15 @@ block-beta ### Renew nKode nKode renewal is a three step process: -1. Renew Customer Properties -2. Renew User Keys -3. Refresh User on Login +1. Renew Customer Keys +2. Intermediate User Keys +3. Renew User Keys on Login {% set md_tick = '`' %} ```mermaid flowchart - subgraph Renew Customer Properties + subgraph Renew Customer Keys old_prop["`old customer property key:
{{old_props}}`"] new_prop["`new customer property key:
{{new_props}}`"] old_pos["`old customer position key:
{{old_pos}}`"] @@ -234,7 +234,7 @@ nKode renewal is a three step process: xor2 --> xor_pos end - subgraph Renew User Keys + subgraph Intermediate User Keys users@{shape: procs, label: "users"} users --> eachuser subgraph eachuser [for each user] @@ -260,7 +260,7 @@ nKode renewal is a three step process: end - subgraph Refresh User on Login + subgraph Renew User Keys on Login login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] diff --git a/notebooks/Enrollment_Login_Renewal_Detailed.ipynb b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb index c549e71..78811da 100644 --- a/notebooks/Enrollment_Login_Renewal_Detailed.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb @@ -762,9 +762,9 @@ "cell_type": "markdown", "source": [ "## Renew Properties\n", - "1. Renew Customer Properties\n", - "2. Renew User Keys\n", - "3. Refresh User on Login\n", + "1. Renew Customer Keys\n", + "2. Intermediate User Keys\n", + "3. Renew User Keys on Login\n", "\n" ] }, @@ -839,7 +839,7 @@ "metadata": {}, "cell_type": "markdown", "source": [ - "### Renew User\n", + "### Intermediate 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", @@ -873,7 +873,7 @@ "metadata": {}, "cell_type": "markdown", "source": [ - "### Refresh User Keys\n", + "### New 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." ] }, -- 2.49.1 From 407916e30c525a1bc78ce91ad4278bec203451fb Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 08:53:40 -0500 Subject: [PATCH 63/85] fix bugs --- docs/encipher_decipher_renew_nkode.md | 220 +++++++++--------- .../render_encipher_decipher_diagrams.py | 9 + .../encipher_decipher_renew_nkode.template.md | 2 +- 3 files changed, 120 insertions(+), 111 deletions(-) diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md index f25c2a4..9d6c55a 100644 --- a/docs/encipher_decipher_renew_nkode.md +++ b/docs/encipher_decipher_renew_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [12683 37623 46427 24232 53244 25444 34138 18278 52542 17394 45705 57706 - 22403 60684 20477 13672 47069 29025 8419 8211 36163 21756 45607 31517 - 44312 19522 17814 41879 18325 2622 43956 30841 59391 52171 51029 44917 - 28846 25756 24212 4239 11940 31284 58701 7692 31504 25629 20378 35504 - 45861 59860 4819 52686 62304 58952] -- position key: [29587 60364 47856 53105 61491 65374 34021 4048 33167] +- property key: [17384 21071 64263 14173 48872 51553 28021 45694 56555 33805 2625 64735 + 26395 23421 5291 64801 27594 45755 18570 21108 24021 32654 60698 36958 + 32655 8083 48743 18902 2050 58888 49782 53855 6539 61094 11244 16942 + 26841 50742 36717 15708 51338 47424 44243 62691 15630 41373 14725 34483 + 9141 48067 10942 13010 44634 40143] +- position key: [26845 24321 52293 19187 29228 61277 32841 51235 34682] --- ## User Cipher -- property key: [14398 56125 29659 36184 61716 58844 42259 48679 36351 53209 63475 35295 - 1927 35139 48674 32220 3724 62784 11332 63262 61320 62990 59038 61659 - 31829 24135 49167 50519 23410 2470 22259 32544 30127 27527 18620 39840 - 105 31222 38444 37307 58991 39926 16285 42364 843 30922 30467 43324 - 13596 3432 28064 33665 35252 8733] -- passcode key: [43892 51915 3968 29221 4 52573 20838 48052 39138 14671] -- combined position key: [40967 35807 54041 52517 31281 56961 42242 11385 1018] -- mask key: [57731 56088 1801 26915 40765 22438 9693 31860 17942 7993] +- property key: [ 6859 23873 43509 63326 38209 4681 6549 58196 20386 45543 7213 10665 + 59595 37675 38733 28230 18106 11839 217 58358 45503 9119 13711 47091 + 2477 4601 12968 25412 57571 1504 36875 36008 22596 65108 20334 57220 + 57449 9749 48916 23941 16493 45581 22735 8888 45253 44959 47418 52406 + 38448 31290 22494 51292 26944 44318] +- passcode key: [61110 58124 49630 7713 65071 1473 10319 26443 24286 22] +- combined position key: [17105 6226 55543 52110 63357 65173 47236 36681 52804] +- mask key: [ 5981 20544 34128 22101 9735 7951 7840 53333 61565 214] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[24890 2120 45550 47291 61945 28551 49488 49734 20771]"] - customer_pos["customer position key:\n[29587 60364 47856 53105 61491 65374 34021 4048 33167]"] + user_pos["user position key:\n[15348 25802 2932 35759 37505 7088 5681 24341 39057]"] + customer_pos["customer position key:\n[26845 24321 52293 19187 29228 61277 32841 51235 34682]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[40967 35807 54041 52517 31281 56961 42242 11385 1018]"]:2 + comb_pos["combined position key\n[17105 6226 55543 52110 63357 65173 47236 36681 52804]"]:2 xor --> comb_pos ``` ## User Keypad -- keypad: -- user passcode indices: [12, 4, 51, 33] +- keypad example:
Key 0: [ 0 1 2 21 4 5 24 16 44]
Key 1: [36 46 38 3 49 23 33 43 35]
Key 2: [18 37 20 39 40 50 42 25 26]
Key 3: [45 19 47 48 31 41 51 52 53]
Key 4: [27 28 11 12 22 32 15 7 17]
Key 5: [ 9 10 29 30 13 14 6 34 8]
+- user passcode indices: [16, 23, 45, 41] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[12683 37623 46427 24232 53244 25444 34138 18278 52542 17394 45705 57706 - 22403 60684 20477 13672 47069 29025 8419 8211 36163 21756 45607 31517 - 44312 19522 17814 41879 18325 2622 43956 30841 59391 52171 51029 44917 - 28846 25756 24212 4239 11940 31284 58701 7692 31504 25629 20378 35504 - 45861 59860 4819 52686 62304 58952]"] - uprop["user_property_key\n[14398 56125 29659 36184 61716 58844 42259 48679 36351 53209 63475 35295 - 1927 35139 48674 32220 3724 62784 11332 63262 61320 62990 59038 61659 - 31829 24135 49167 50519 23410 2470 22259 32544 30127 27527 18620 39840 - 105 31222 38444 37307 58991 39926 16285 42364 843 30922 30467 43324 - 13596 3432 28064 33665 35252 8733]"] + cprop["customer_property_key\n[17384 21071 64263 14173 48872 51553 28021 45694 56555 33805 2625 64735 + 26395 23421 5291 64801 27594 45755 18570 21108 24021 32654 60698 36958 + 32655 8083 48743 18902 2050 58888 49782 53855 6539 61094 11244 16942 + 26841 50742 36717 15708 51338 47424 44243 62691 15630 41373 14725 34483 + 9141 48067 10942 13010 44634 40143]"] + uprop["user_property_key\n[ 6859 23873 43509 63326 38209 4681 6549 58196 20386 45543 7213 10665 + 59595 37675 38733 28230 18106 11839 217 58358 45503 9119 13711 47091 + 2477 4601 12968 25412 57571 1504 36875 36008 22596 65108 20334 57220 + 57449 9749 48916 23941 16493 45581 22735 8888 45253 44959 47418 52406 + 38448 31290 22494 51292 26944 44318]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[32565 54767 5127 26787 61372 9024 10051 31192 36215 25953 63643 7907 - 34275 49131 65253 35224 52025 49525 36214 22719 57589 6411 46986 50058 - 38701 701 36150 6894 36770 58151 63226 62425 5898 2177 3529 29703 - 30896 41755 9016 47283 14611 18887 54104 55155 32367 2577 31449 39233 - 18953 7162 61412 6044 61623 4668]"] + prop["combined_property_key\n[ 3998 21572 55042 58365 40151 56917 7146 59748 41624 5784 34350 32869 + 53395 9331 14458 19129 53918 11036 33943 47140 13973 55881 48429 60127 + 51683 5313 42763 50259 48824 61851 14521 48287 21836 28900 38175 36849 + 53395 56365 51787 3932 9457 4434 50392 35667 65060 8394 57404 19291 + 36039 26883 24085 29399 24324 41103]"] xor1 --> prop - pass["user_passcode_indices\n[12, 4, 51, 33]"] + pass["user_passcode_indices\n[16, 23, 45, 41]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[34275 61372 6044 2177]"]:2 + passcode["user passcode properties:\n[53918 60127 8394 4434]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[34275 61372 6044 2177 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[53918 60127 8394 4434 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[43892 51915 3968 29221 4 52573 20838 48052 39138 14671]"] + passkey["passcode key:\n[61110 58124 49630 7713 65071 1473 10319 26443 24286 22]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[11927 9591 6172 31396 4 52573 20838 48052 39138 14671]"]:2 + cipheredpass["ciphered passcode:\n[15400 2515 57620 3955 65071 1473 10319 26443 24286 22]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$Jf48A5rmjkxb0II7CkuD8.Qg3GCuROgbS99pMbthC2gXt9szx9gmm"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$4ew1R/6AXYkPuF4PO4CpQeCEEGMuoo7N8Rnj1yAGOGenJ8vc689ge"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[12, 4, 51, 33]"] - comb_pos["combined position key:\n[40967 35807 54041 52517 31281 56961 42242 11385 1018]"] - cust_pos["customer position key:\n[29587 60364 47856 53105 61491 65374 34021 4048 33167]"] + passcode_idx["passcode indices:\n[16, 23, 45, 41]"] + comb_pos["combined position key:\n[17105 6226 55543 52110 63357 65173 47236 36681 52804]"] + cust_pos["customer position key:\n[26845 24321 52293 19187 29228 61277 32841 51235 34682]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[3, 4, 6, 6]"] + passcode_position_idx["passcode poition indices:\n[7, 5, 0, 5]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[3, 4, 6, 6, 3, 3, 7, 5, 0, 8]"] + posidx["Padded Passcode Position Indices:\n[7, 5, 0, 5, 6, 3, 1, 2, 8, 5]"] pad1 --> posidx space:1 - user_pos["user position key:\n[24890 2120 45550 47291 61945 28551 49488 49734 20771]"] + user_pos["user position key:\n[15348 25802 2932 35759 37505 7088 5681 24341 39057]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[47291 61945 49488 49488 47291 47291 49734 28551 24890 20771]"] + passcode_pos["ordered user passcode positions:\n[24341 7088 15348 7088 5681 35759 25802 2932 39057 7088]"] sel --> passcode_pos - mask_key["mask key\n[57731 56088 1801 26915 40765 22438 9693 31860 17942 7993]"] + mask_key["mask key\n[ 5981 20544 34128 22101 9735 7951 7840 53333 61565 214]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [22840 10977 50777 43123 10118 61213 59291 5107 10028 19994]"] + mask["enciphered mask:\n [18504 19440 48804 19941 12342 38048 31338 56097 26860 7014]"] xor2 --> mask ``` @@ -156,19 +156,19 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n[1, 0, 5, 0]"] - login_keypad["login keypad:\nKey 0: [45 46 29 48 4 50 33 16 17] -Key 1: [18 1 20 12 49 5 15 34 44] -Key 2: [27 10 38 3 31 14 24 7 26] -Key 3: [ 0 37 11 39 22 23 42 52 8] -Key 4: [36 19 2 21 40 32 6 25 35] -Key 5: [ 9 28 47 30 13 41 51 43 53] + selected_keys["selected keys:\n[0, 1, 3, 3]"] + login_keypad["login keypad:\nKey 0: [ 0 1 2 21 4 5 24 16 44] +Key 1: [36 46 38 3 49 23 33 43 35] +Key 2: [18 37 20 39 40 50 42 25 26] +Key 3: [45 19 47 48 31 41 51 52 53] +Key 4: [27 28 11 12 22 32 15 7 17] +Key 5: [ 9 10 29 30 13 14 6 34 8] "] space:4 selectkeys(("select keys")) - mask["enciphered mask:\n [22840 10977 50777 43123 10118 61213 59291 5107 10028 19994]"] - mask_key["mask key:\n[57731 56088 1801 26915 40765 22438 9693 31860 17942 7993]"] + mask["enciphered mask:\n [18504 19440 48804 19941 12342 38048 31338 56097 26860 7014]"] + mask_key["mask key:\n[ 5981 20544 34128 22101 9735 7951 7840 53333 61565 214]"] space:2 xor1(("XOR")) @@ -178,12 +178,12 @@ Key 5: [ 9 28 47 30 13 41 51 43 53] login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[18 1 20 12 49 5 15 34 44] - [45 46 29 48 4 50 33 16 17] - [ 9 28 47 30 13 41 51 43 53] - [45 46 29 48 4 50 33 16 17]]"] - user_position_key["user position key:\n[24890 2120 45550 47291 61945 28551 49488 49734 20771]"] - passcode_pos["ordered user passcode positions:\n[47291 61945 49488 49488 47291 47291 49734 28551 24890 20771]"] + ordered_keys["ordered keys:\n[[ 0 1 2 21 4 5 24 16 44] + [36 46 38 3 49 23 33 43 35] + [45 19 47 48 31 41 51 52 53] + [45 19 47 48 31 41 51 52 53]]"] + user_position_key["user position key:\n[15348 25802 2932 35759 37505 7088 5681 24341 39057]"] + passcode_pos["ordered user passcode positions:\n[24341 7088 15348 7088 5681 35759 25802 2932 39057 7088]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [ 9 28 47 30 13 41 51 43 53] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[3, 4, 6, 6, 3, 3, 7, 5, 0, 8]"] + passcode_pos_idxs["padded passcode position indices:\n[7, 5, 0, 5, 6, 3, 1, 2, 8, 5]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [ 9 28 47 30 13 41 51 43 53] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[12, 4, 51, 33]"] - prop["combined_property_key\n[32565 54767 5127 26787 61372 9024 10051 31192 36215 25953 63643 7907 - 34275 49131 65253 35224 52025 49525 36214 22719 57589 6411 46986 50058 - 38701 701 36150 6894 36770 58151 63226 62425 5898 2177 3529 29703 - 30896 41755 9016 47283 14611 18887 54104 55155 32367 2577 31449 39233 - 18953 7162 61412 6044 61623 4668]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$Jf48A5rmjkxb0II7CkuD8.Qg3GCuROgbS99pMbthC2gXt9szx9gmm"] + passcode_prop_idxs["presumed passcode property indices:\n[16, 23, 45, 41]"] + prop["combined_property_key\n[ 3998 21572 55042 58365 40151 56917 7146 59748 41624 5784 34350 32869 + 53395 9331 14458 19129 53918 11036 33943 47140 13973 55881 48429 60127 + 51683 5313 42763 50259 48824 61851 14521 48287 21836 28900 38175 36849 + 53395 56365 51787 3932 9457 4434 50392 35667 65060 8394 57404 19291 + 36039 26883 24085 29399 24324 41103]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$4ew1R/6AXYkPuF4PO4CpQeCEEGMuoo7N8Rnj1yAGOGenJ8vc689ge"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [ 9 28 47 30 13 41 51 43 53] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[34275 61372 6044 2177]"] + passcode_prop["presumed passcode properties:\n[53918 60127 8394 4434]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [ 9 28 47 30 13 41 51 43 53] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[11927 9591 6172 31396 4 52573 20838 48052 39138 14671]"] + cipheredpass["ciphered passcode:\n[15400 2515 57620 3955 65071 1473 10319 26443 24286 22]"] cipher --> cipheredpass space:7 @@ -250,26 +250,26 @@ nKode renewal is a three step process: ```mermaid flowchart subgraph Renew Customer Keys - old_prop["`old customer property key:
[18187 3794 26588 58875 7848 50844 33360 51199 136 43704 3944 38716 - 33380 13992 16583 62532 50613 13365 41266 44961 3965 61189 20756 13137 - 60280 23802 19769 57273 54480 60033 40969 36089 25253 25350 17781 61351 - 30937 56045 46356 10504 57212 53809 60613 29199 32036 29403 3546 12413 - 32533 5778 33348 37917 30979 12321]`"] - new_prop["`new customer property key:
[12683 37623 46427 24232 53244 25444 34138 18278 52542 17394 45705 57706 - 22403 60684 20477 13672 47069 29025 8419 8211 36163 21756 45607 31517 - 44312 19522 17814 41879 18325 2622 43956 30841 59391 52171 51029 44917 - 28846 25756 24212 4239 11940 31284 58701 7692 31504 25629 20378 35504 - 45861 59860 4819 52686 62304 58952]`"] - old_pos["`old customer position key:
[49469 33687 25335 30110 35784 45318 25682 60991 21209]`"] - new_pos["`new customer position key:
[29587 60364 47856 53105 61491 65374 34021 4048 33167]`"] + old_prop["`old customer property key:
[ 5461 2309 32503 5283 2454 52252 639 2608 60730 42879 39427 43468 + 14424 46936 44855 9471 37924 1315 33870 23506 34602 63958 34978 23852 + 49230 1336 38307 42775 24155 62587 43186 12343 3336 36528 55921 20597 + 12538 64056 30047 21209 25756 41823 39959 43499 20193 36693 22790 34797 + 6903 4921 2507 47755 13892 3473]`"] + new_prop["`new customer property key:
[17384 21071 64263 14173 48872 51553 28021 45694 56555 33805 2625 64735 + 26395 23421 5291 64801 27594 45755 18570 21108 24021 32654 60698 36958 + 32655 8083 48743 18902 2050 58888 49782 53855 6539 61094 11244 16942 + 26841 50742 36717 15708 51338 47424 44243 62691 15630 41373 14725 34483 + 9141 48067 10942 13010 44634 40143]`"] + old_pos["`old customer position key:
[31013 31896 54147 16417 26108 58661 44725 53340 22229]`"] + new_pos["`new customer position key:
[26845 24321 52293 19187 29228 61277 32841 51235 34682]`"] xor1(("XOR")) xor2(("XOR")) - xor_prop["`xor property key:
[30336 39973 53895 47955 53588 42488 1802 32921 52662 59722 48609 30294 - 54759 56228 3898 49452 29288 17748 33233 36786 33342 48121 58163 18508 - 18016 4280 2223 31790 37701 57535 3005 62592 34138 43213 33312 16594 - 2167 48753 60288 14727 61912 43013 2440 27651 1588 5830 16960 47821 - 52272 65350 37015 22995 35427 54889]`"] - xor_pos["`xor position key:
[45742 26715 55303 47855 31739 20056 57527 57839 54102]`"] + xor_prop["`xor property key:
[22205 23370 34288 9214 46974 1405 28426 47182 12753 9074 36930 21779 + 24387 60453 48028 55774 65518 47000 52420 2470 56063 34392 26040 52594 + 49089 6827 11204 61121 22105 4723 27332 57960 5251 24598 61853 4699 + 22563 15374 64050 28549 44054 6687 12484 23816 29679 11976 24707 350 + 14658 43258 9077 34905 38942 37214]`"] + xor_pos["`xor position key:
[ 4600 9113 8134 2770 6096 2680 12028 6271 53679]`"] old_prop --> xor1 new_prop --> xor1 xor1 --> xor_prop @@ -283,12 +283,12 @@ nKode renewal is a three step process: users --> eachuser subgraph eachuser [for each user] subgraph old user keys - old_user_pos["`combined position key:
[24890 2120 45550 47291 61945 28551 49488 49734 20771]`"] - old_user_prop["`property key:
[14398 56125 29659 36184 61716 58844 42259 48679 36351 53209 63475 35295 - 1927 35139 48674 32220 3724 62784 11332 63262 61320 62990 59038 61659 - 31829 24135 49167 50519 23410 2470 22259 32544 30127 27527 18620 39840 - 105 31222 38444 37307 58991 39926 16285 42364 843 30922 30467 43324 - 13596 3432 28064 33665 35252 8733]`"] + old_user_pos["`combined position key:
[15348 25802 2932 35759 37505 7088 5681 24341 39057]`"] + old_user_prop["`property key:
[ 6859 23873 43509 63326 38209 4681 6549 58196 20386 45543 7213 10665 + 59595 37675 38733 28230 18106 11839 217 58358 45503 9119 13711 47091 + 2477 4601 12968 25412 57571 1504 36875 36008 22596 65108 20334 57220 + 57449 9749 48916 23941 16493 45581 22735 8888 45253 44959 47418 52406 + 38448 31290 22494 51292 26944 44318]`"] old_renew["renew: False"] end xor3(("XOR")) @@ -300,12 +300,12 @@ nKode renewal is a three step process: xor_prop --> xor4 xor4 --> inter_user_prop subgraph inter_user[intermediate user keys] - inter_user_pos["`combined position key:
[ 4777 58244 2846 30666 458 37081 17845 52630 53420]`"] - inter_user_prop["`property key:
[20158 18200 41308 13835 8256 16420 41497 16062 16457 9875 18962 65417 - 53856 21223 45336 48368 31972 45076 44437 30892 28086 19959 1453 47255 - 14901 20223 51360 47481 51255 59673 23886 35744 61685 49994 51868 56178 - 2078 51079 32172 43068 6071 13299 13845 51583 1407 28172 13635 5105 - 63788 61998 64823 55890 983 62580]`"] + inter_user_pos["`combined position key:
[21289 15307 50993 49500 57517 62701 38520 38710 8171]`"] + inter_user_prop["`property key:
[19574 1547 11269 54432 8767 5940 30367 23322 32371 37525 35951 31930 + 46984 32526 11473 47000 47444 39335 52253 59984 27456 42439 20535 31361 + 46700 2898 6508 36229 46778 6035 64207 28352 19655 40514 48883 52703 + 47178 6683 17702 12800 60539 43026 26635 32688 49962 33111 55737 52712 + 44914 53952 29867 16389 61790 15424]`"] inter_renew["renew: True"] end end @@ -316,12 +316,12 @@ nKode renewal is a three step process: login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] - new_user_pos["`combined position key:
[37567 32382 36425 6535 11819 18071 35301 38878 29144]`"] - new_user_prop["`property key:
[63204 21530 24393 63295 52024 46102 24361 23381 41968 3713 26133 17989 - 14193 46059 12280 45683 35436 9340 17577 37219 15797 39388 52148 29719 - 48300 18038 25938 62757 21612 28454 23892 12945 18057 62761 37107 25062 - 27001 30212 20943 35287 60202 25046 29690 64376 57055 1554 42998 36674 - 45138 58609 25837 59944 8395 52185]`"] + new_user_pos["`combined position key:
[65323 43160 15313 31589 56469 63324 16779 58877 46813]`"] + new_user_prop["`property key:
[ 4877 339 2263 7952 818 48661 46131 56267 23637 56219 31553 4783 + 17734 11957 16009 8164 58199 21329 25805 42484 63685 33425 4845 26086 + 45993 37895 46700 19583 28941 36895 52143 15209 11188 569 60851 63329 + 33273 55731 2564 42007 14482 45812 39063 18143 15261 56200 36966 9873 + 43332 61052 51872 65507 42946 33203]`"] new_renew["renew: False"] end login --> new_user diff --git a/docs/scripts/render_encipher_decipher_diagrams.py b/docs/scripts/render_encipher_decipher_diagrams.py index 466b853..f2837cd 100644 --- a/docs/scripts/render_encipher_decipher_diagrams.py +++ b/docs/scripts/render_encipher_decipher_diagrams.py @@ -20,6 +20,14 @@ def display_keypad(icons_array: np.ndarray, props_per_key: int) -> str: icons += "\n" return icons +def display_md_keypad(icons_array: np.ndarray, props_per_key: int) -> str: + icons = "" + for idx, row in enumerate(icons_array.reshape(-1, props_per_key)): + icons += f"Key {idx}: " + icons += str(row) + icons += "
" + return icons + if __name__ == "__main__": api = NKodeAPI() @@ -100,6 +108,7 @@ if __name__ == "__main__": "mask": mask, "selected_keys": selected_keys_login, "login_keypad": display_keypad(login_keypad, keypad_size.props_per_key), + "login_keypad_md": display_md_keypad(login_keypad, keypad_size.props_per_key), "ordered_keys": login_keypad.reshape(-1, keypad_size.props_per_key)[selected_keys_login], "old_props": old_props, "new_props": new_props, diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index 61ac96c..ef36513 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -35,7 +35,7 @@ block-beta ``` ## User Keypad -- keypad: {{ user_keypad}} +- keypad example:
{{ login_keypad_md }} - user passcode indices: {{ user_passcode_idxs}} ## nKode Cipher -- 2.49.1 From 8376fe7e87d9e1e40ee9284eb5b847c1188858ea Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 08:59:13 -0500 Subject: [PATCH 64/85] fix bugs --- docs/encipher_decipher_renew_nkode.md | 224 +++++++++--------- .../encipher_decipher_renew_nkode.template.md | 6 +- 2 files changed, 115 insertions(+), 115 deletions(-) diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md index 9d6c55a..ba83382 100644 --- a/docs/encipher_decipher_renew_nkode.md +++ b/docs/encipher_decipher_renew_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [17384 21071 64263 14173 48872 51553 28021 45694 56555 33805 2625 64735 - 26395 23421 5291 64801 27594 45755 18570 21108 24021 32654 60698 36958 - 32655 8083 48743 18902 2050 58888 49782 53855 6539 61094 11244 16942 - 26841 50742 36717 15708 51338 47424 44243 62691 15630 41373 14725 34483 - 9141 48067 10942 13010 44634 40143] -- position key: [26845 24321 52293 19187 29228 61277 32841 51235 34682] +- property key: [17433 57572 21505 8444 45652 40584 48852 43446 5599 62405 28323 40256 + 47776 49892 26386 48063 8519 55431 26581 11344 39650 4798 19208 64337 + 33689 12686 45679 3132 40090 40966 37000 33485 30170 45501 20844 20464 + 53748 19697 19768 4387 39395 35315 9619 28777 48707 30931 62584 35610 + 46036 40873 63440 33641 852 11131] +- position key: [38077 51455 31958 23938 20630 60053 9765 10657 7626] --- ## User Cipher -- property key: [ 6859 23873 43509 63326 38209 4681 6549 58196 20386 45543 7213 10665 - 59595 37675 38733 28230 18106 11839 217 58358 45503 9119 13711 47091 - 2477 4601 12968 25412 57571 1504 36875 36008 22596 65108 20334 57220 - 57449 9749 48916 23941 16493 45581 22735 8888 45253 44959 47418 52406 - 38448 31290 22494 51292 26944 44318] -- passcode key: [61110 58124 49630 7713 65071 1473 10319 26443 24286 22] -- combined position key: [17105 6226 55543 52110 63357 65173 47236 36681 52804] -- mask key: [ 5981 20544 34128 22101 9735 7951 7840 53333 61565 214] +- property key: [24983 41762 20840 27438 7273 19213 38577 61466 23138 47314 23998 50092 + 273 14037 34702 41428 64080 27332 13490 12880 55541 24199 778 9414 + 29001 19211 3074 60661 6134 25585 30166 41331 20480 63833 34395 54478 + 26155 58076 18811 43725 18545 28882 15969 25829 22807 22588 35663 41312 + 34398 20904 148 47345 50354 38166] +- passcode key: [29182 58569 45520 52787 7372 34618 40716 38549 23700 43116] +- combined position key: [55717 12248 3398 52977 52974 12019 47318 59100 12380] +- mask key: [51996 30221 59877 58879 34382 22287 31520 64981 60412 37143] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[15348 25802 2932 35759 37505 7088 5681 24341 39057]"] - customer_pos["customer position key:\n[26845 24321 52293 19187 29228 61277 32841 51235 34682]"] + user_pos["user position key:\n[41859 3923 32291 54686 34461 59585 18017 7286 34891]"] + customer_pos["customer position key:\n[38077 51455 31958 23938 20630 60053 9765 10657 7626]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[17105 6226 55543 52110 63357 65173 47236 36681 52804]"]:2 + comb_pos["combined position key\n[55717 12248 3398 52977 52974 12019 47318 59100 12380]"]:2 xor --> comb_pos ``` ## User Keypad -- keypad example:
Key 0: [ 0 1 2 21 4 5 24 16 44]
Key 1: [36 46 38 3 49 23 33 43 35]
Key 2: [18 37 20 39 40 50 42 25 26]
Key 3: [45 19 47 48 31 41 51 52 53]
Key 4: [27 28 11 12 22 32 15 7 17]
Key 5: [ 9 10 29 30 13 14 6 34 8]
-- user passcode indices: [16, 23, 45, 41] +- keypad example:
Key 0: [36 28 38 21 4 41 15 43 26]
Key 1: [ 0 19 47 12 49 14 33 16 17]
Key 2: [ 9 37 2 39 40 50 51 52 44]
Key 3: [27 46 29 30 31 5 24 25 35]
Key 4: [18 10 20 48 22 23 42 34 53]
Key 5: [45 1 11 3 13 32 6 7 8]
+- user passcode indices: [1, 32, 34, 5] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[17384 21071 64263 14173 48872 51553 28021 45694 56555 33805 2625 64735 - 26395 23421 5291 64801 27594 45755 18570 21108 24021 32654 60698 36958 - 32655 8083 48743 18902 2050 58888 49782 53855 6539 61094 11244 16942 - 26841 50742 36717 15708 51338 47424 44243 62691 15630 41373 14725 34483 - 9141 48067 10942 13010 44634 40143]"] - uprop["user_property_key\n[ 6859 23873 43509 63326 38209 4681 6549 58196 20386 45543 7213 10665 - 59595 37675 38733 28230 18106 11839 217 58358 45503 9119 13711 47091 - 2477 4601 12968 25412 57571 1504 36875 36008 22596 65108 20334 57220 - 57449 9749 48916 23941 16493 45581 22735 8888 45253 44959 47418 52406 - 38448 31290 22494 51292 26944 44318]"] + cprop["customer_property_key\n[17433 57572 21505 8444 45652 40584 48852 43446 5599 62405 28323 40256 + 47776 49892 26386 48063 8519 55431 26581 11344 39650 4798 19208 64337 + 33689 12686 45679 3132 40090 40966 37000 33485 30170 45501 20844 20464 + 53748 19697 19768 4387 39395 35315 9619 28777 48707 30931 62584 35610 + 46036 40873 63440 33641 852 11131]"] + uprop["user_property_key\n[24983 41762 20840 27438 7273 19213 38577 61466 23138 47314 23998 50092 + 273 14037 34702 41428 64080 27332 13490 12880 55541 24199 778 9414 + 29001 19211 3074 60661 6134 25585 30166 41331 20480 63833 34395 54478 + 26155 58076 18811 43725 18545 28882 15969 25829 22807 22588 35663 41312 + 34398 20904 148 47345 50354 38166]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[ 3998 21572 55042 58365 40151 56917 7146 59748 41624 5784 34350 32869 - 53395 9331 14458 19129 53918 11036 33943 47140 13973 55881 48429 60127 - 51683 5313 42763 50259 48824 61851 14521 48287 21836 28900 38175 36849 - 53395 56365 51787 3932 9457 4434 50392 35667 65060 8394 57404 19291 - 36039 26883 24085 29399 24324 41103]"] + prop["combined_property_key\n[ 9552 31208 37587 40095 45072 12350 32006 2925 11611 34589 36849 42561 + 26580 61891 26520 39789 65017 7624 63974 8702 9646 62493 54967 42708 + 52347 11714 29071 28123 11157 34826 4115 53992 58115 20982 19018 55103 + 48003 8472 18407 52180 52783 23938 12226 59018 22734 31178 30431 34796 + 35730 22961 11250 19920 45688 49980]"] xor1 --> prop - pass["user_passcode_indices\n[16, 23, 45, 41]"] + pass["user_passcode_indices\n[1, 32, 34, 5]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[53918 60127 8394 4434]"]:2 + passcode["user passcode properties:\n[31208 58115 19018 12350]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[53918 60127 8394 4434 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[31208 58115 19018 12350 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[61110 58124 49630 7713 65071 1473 10319 26443 24286 22]"] + passkey["passcode key:\n[29182 58569 45520 52787 7372 34618 40716 38549 23700 43116]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[15400 2515 57620 3955 65071 1473 10319 26443 24286 22]"]:2 + cipheredpass["ciphered passcode:\n[ 2070 1994 64410 65037 7372 34618 40716 38549 23700 43116]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$4ew1R/6AXYkPuF4PO4CpQeCEEGMuoo7N8Rnj1yAGOGenJ8vc689ge"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$mSfxfqokaUYYc8CHnj1nV.3fz.FUkMo4dNreWfUUO4zdgN0LQQ2Tm"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[16, 23, 45, 41]"] - comb_pos["combined position key:\n[17105 6226 55543 52110 63357 65173 47236 36681 52804]"] - cust_pos["customer position key:\n[26845 24321 52293 19187 29228 61277 32841 51235 34682]"] + passcode_idx["passcode indices:\n[1, 32, 34, 5]"] + comb_pos["combined position key:\n[55717 12248 3398 52977 52974 12019 47318 59100 12380]"] + cust_pos["customer position key:\n[38077 51455 31958 23938 20630 60053 9765 10657 7626]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[7, 5, 0, 5]"] + passcode_position_idx["passcode poition indices:\n[1, 5, 7, 5]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[7, 5, 0, 5, 6, 3, 1, 2, 8, 5]"] + posidx["Padded Passcode Position Indices:\n[1, 5, 7, 5, 7, 2, 8, 1, 7, 1]"] pad1 --> posidx space:1 - user_pos["user position key:\n[15348 25802 2932 35759 37505 7088 5681 24341 39057]"] + user_pos["user position key:\n[41859 3923 32291 54686 34461 59585 18017 7286 34891]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[24341 7088 15348 7088 5681 35759 25802 2932 39057 7088]"] + passcode_pos["ordered user passcode positions:\n[ 3923 59585 7286 59585 7286 32291 34891 3923 7286 3923]"] sel --> passcode_pos - mask_key["mask key\n[ 5981 20544 34128 22101 9735 7951 7840 53333 61565 214]"] + mask_key["mask key\n[51996 30221 59877 58879 34382 22287 31520 64981 60412 37143]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [18504 19440 48804 19941 12342 38048 31338 56097 26860 7014]"] + mask["enciphered mask:\n [50255 40652 62867 3390 39480 10540 62315 62086 63370 40516]"] xor2 --> mask ``` @@ -156,34 +156,34 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n[0, 1, 3, 3]"] - login_keypad["login keypad:\nKey 0: [ 0 1 2 21 4 5 24 16 44] -Key 1: [36 46 38 3 49 23 33 43 35] -Key 2: [18 37 20 39 40 50 42 25 26] -Key 3: [45 19 47 48 31 41 51 52 53] -Key 4: [27 28 11 12 22 32 15 7 17] -Key 5: [ 9 10 29 30 13 14 6 34 8] + pass["user_passcode_indices\n[1, 32, 34, 5]"] + login_keypad["login keypad:\nKey 0: [36 28 38 21 4 41 15 43 26] +Key 1: [ 0 19 47 12 49 14 33 16 17] +Key 2: [ 9 37 2 39 40 50 51 52 44] +Key 3: [27 46 29 30 31 5 24 25 35] +Key 4: [18 10 20 48 22 23 42 34 53] +Key 5: [45 1 11 3 13 32 6 7 8] "] space:4 - selectkeys(("select keys")) - mask["enciphered mask:\n [18504 19440 48804 19941 12342 38048 31338 56097 26860 7014]"] - mask_key["mask key:\n[ 5981 20544 34128 22101 9735 7951 7840 53333 61565 214]"] + selectkeys(("user")) + mask["enciphered mask:\n [50255 40652 62867 3390 39480 10540 62315 62086 63370 40516]"] + mask_key["mask key:\n[51996 30221 59877 58879 34382 22287 31520 64981 60412 37143]"] space:2 xor1(("XOR")) mask --> xor1 mask_key --> xor1 - selected_keys --> selectkeys + pass --> selectkeys login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[ 0 1 2 21 4 5 24 16 44] - [36 46 38 3 49 23 33 43 35] - [45 19 47 48 31 41 51 52 53] - [45 19 47 48 31 41 51 52 53]]"] - user_position_key["user position key:\n[15348 25802 2932 35759 37505 7088 5681 24341 39057]"] - passcode_pos["ordered user passcode positions:\n[24341 7088 15348 7088 5681 35759 25802 2932 39057 7088]"] + ordered_keys["ordered keys:\n[[45 1 11 3 13 32 6 7 8] + [45 1 11 3 13 32 6 7 8] + [18 10 20 48 22 23 42 34 53] + [27 46 29 30 31 5 24 25 35]]"] + user_position_key["user position key:\n[41859 3923 32291 54686 34461 59585 18017 7286 34891]"] + passcode_pos["ordered user passcode positions:\n[ 3923 59585 7286 59585 7286 32291 34891 3923 7286 3923]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [ 9 10 29 30 13 14 6 34 8] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[7, 5, 0, 5, 6, 3, 1, 2, 8, 5]"] + passcode_pos_idxs["padded passcode position indices:\n[1, 5, 7, 5, 7, 2, 8, 1, 7, 1]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [ 9 10 29 30 13 14 6 34 8] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[16, 23, 45, 41]"] - prop["combined_property_key\n[ 3998 21572 55042 58365 40151 56917 7146 59748 41624 5784 34350 32869 - 53395 9331 14458 19129 53918 11036 33943 47140 13973 55881 48429 60127 - 51683 5313 42763 50259 48824 61851 14521 48287 21836 28900 38175 36849 - 53395 56365 51787 3932 9457 4434 50392 35667 65060 8394 57404 19291 - 36039 26883 24085 29399 24324 41103]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$4ew1R/6AXYkPuF4PO4CpQeCEEGMuoo7N8Rnj1yAGOGenJ8vc689ge"] + passcode_prop_idxs["presumed passcode property indices:\n[1, 32, 34, 5]"] + prop["combined_property_key\n[ 9552 31208 37587 40095 45072 12350 32006 2925 11611 34589 36849 42561 + 26580 61891 26520 39789 65017 7624 63974 8702 9646 62493 54967 42708 + 52347 11714 29071 28123 11157 34826 4115 53992 58115 20982 19018 55103 + 48003 8472 18407 52180 52783 23938 12226 59018 22734 31178 30431 34796 + 35730 22961 11250 19920 45688 49980]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$mSfxfqokaUYYc8CHnj1nV.3fz.FUkMo4dNreWfUUO4zdgN0LQQ2Tm"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [ 9 10 29 30 13 14 6 34 8] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[53918 60127 8394 4434]"] + passcode_prop["presumed passcode properties:\n[31208 58115 19018 12350]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [ 9 10 29 30 13 14 6 34 8] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[15400 2515 57620 3955 65071 1473 10319 26443 24286 22]"] + cipheredpass["ciphered passcode:\n[ 2070 1994 64410 65037 7372 34618 40716 38549 23700 43116]"] cipher --> cipheredpass space:7 @@ -250,26 +250,26 @@ nKode renewal is a three step process: ```mermaid flowchart subgraph Renew Customer Keys - old_prop["`old customer property key:
[ 5461 2309 32503 5283 2454 52252 639 2608 60730 42879 39427 43468 - 14424 46936 44855 9471 37924 1315 33870 23506 34602 63958 34978 23852 - 49230 1336 38307 42775 24155 62587 43186 12343 3336 36528 55921 20597 - 12538 64056 30047 21209 25756 41823 39959 43499 20193 36693 22790 34797 - 6903 4921 2507 47755 13892 3473]`"] - new_prop["`new customer property key:
[17384 21071 64263 14173 48872 51553 28021 45694 56555 33805 2625 64735 - 26395 23421 5291 64801 27594 45755 18570 21108 24021 32654 60698 36958 - 32655 8083 48743 18902 2050 58888 49782 53855 6539 61094 11244 16942 - 26841 50742 36717 15708 51338 47424 44243 62691 15630 41373 14725 34483 - 9141 48067 10942 13010 44634 40143]`"] - old_pos["`old customer position key:
[31013 31896 54147 16417 26108 58661 44725 53340 22229]`"] - new_pos["`new customer position key:
[26845 24321 52293 19187 29228 61277 32841 51235 34682]`"] + old_prop["`old customer property key:
[17607 56010 50107 63409 44153 31539 60343 64375 30521 16335 53839 26093 + 26309 50966 57366 15033 1961 30476 52564 5038 64859 43674 54717 33298 + 48434 26313 32141 33070 15459 60411 26053 29595 45827 43183 52241 1009 + 56744 50116 3740 24857 34398 11600 4515 33391 473 8694 64912 9868 + 3532 2073 11110 62753 30410 22058]`"] + new_prop["`new customer property key:
[17433 57572 21505 8444 45652 40584 48852 43446 5599 62405 28323 40256 + 47776 49892 26386 48063 8519 55431 26581 11344 39650 4798 19208 64337 + 33689 12686 45679 3132 40090 40966 37000 33485 30170 45501 20844 20464 + 53748 19697 19768 4387 39395 35315 9619 28777 48707 30931 62584 35610 + 46036 40873 63440 33641 852 11131]`"] + old_pos["`old customer position key:
[31270 8331 29541 7023 18547 50738 65207 64170 47127]`"] + new_pos["`new customer position key:
[38077 51455 31958 23938 20630 60053 9765 10657 7626]`"] xor1(("XOR")) xor2(("XOR")) - xor_prop["`xor property key:
[22205 23370 34288 9214 46974 1405 28426 47182 12753 9074 36930 21779 - 24387 60453 48028 55774 65518 47000 52420 2470 56063 34392 26040 52594 - 49089 6827 11204 61121 22105 4723 27332 57960 5251 24598 61853 4699 - 22563 15374 64050 28549 44054 6687 12484 23816 29679 11976 24707 350 - 14658 43258 9077 34905 38942 37214]`"] - xor_pos["`xor position key:
[ 4600 9113 8134 2770 6096 2680 12028 6271 53679]`"] + xor_prop["`xor property key:
[ 222 14894 38842 55117 7725 58811 21859 21185 25318 52234 48364 63661 + 56421 1522 34564 33030 9966 44939 43649 16382 26553 47140 40629 31043 + 16043 22343 53218 36114 41209 19453 62797 61782 50905 6418 40317 19457 + 3164 36661 17316 28730 8125 42147 13360 61958 49050 22821 2536 44438 + 48664 38832 56502 30280 30110 32081]`"] + xor_pos["`xor position key:
[61083 59508 4019 18157 6373 11431 55442 54027 42461]`"] old_prop --> xor1 new_prop --> xor1 xor1 --> xor_prop @@ -283,12 +283,12 @@ nKode renewal is a three step process: users --> eachuser subgraph eachuser [for each user] subgraph old user keys - old_user_pos["`combined position key:
[15348 25802 2932 35759 37505 7088 5681 24341 39057]`"] - old_user_prop["`property key:
[ 6859 23873 43509 63326 38209 4681 6549 58196 20386 45543 7213 10665 - 59595 37675 38733 28230 18106 11839 217 58358 45503 9119 13711 47091 - 2477 4601 12968 25412 57571 1504 36875 36008 22596 65108 20334 57220 - 57449 9749 48916 23941 16493 45581 22735 8888 45253 44959 47418 52406 - 38448 31290 22494 51292 26944 44318]`"] + old_user_pos["`combined position key:
[41859 3923 32291 54686 34461 59585 18017 7286 34891]`"] + old_user_prop["`property key:
[24983 41762 20840 27438 7273 19213 38577 61466 23138 47314 23998 50092 + 273 14037 34702 41428 64080 27332 13490 12880 55541 24199 778 9414 + 29001 19211 3074 60661 6134 25585 30166 41331 20480 63833 34395 54478 + 26155 58076 18811 43725 18545 28882 15969 25829 22807 22588 35663 41312 + 34398 20904 148 47345 50354 38166]`"] old_renew["renew: False"] end xor3(("XOR")) @@ -300,12 +300,12 @@ nKode renewal is a three step process: xor_prop --> xor4 xor4 --> inter_user_prop subgraph inter_user[intermediate user keys] - inter_user_pos["`combined position key:
[21289 15307 50993 49500 57517 62701 38520 38710 8171]`"] - inter_user_prop["`property key:
[19574 1547 11269 54432 8767 5940 30367 23322 32371 37525 35951 31930 - 46984 32526 11473 47000 47444 39335 52253 59984 27456 42439 20535 31361 - 46700 2898 6508 36229 46778 6035 64207 28352 19655 40514 48883 52703 - 47178 6683 17702 12800 60539 43026 26635 32688 49962 33111 55737 52712 - 44914 53952 29867 16389 61790 15424]`"] + inter_user_pos["`combined position key:
[14142 51116 757 34844 54795 596 24644 13783 38273]`"] + inter_user_prop["`property key:
[24905 39180 50898 48227 580 44726 50130 41691 14468 29912 57682 15105 + 56692 13095 138 8402 56510 50511 40499 3502 48972 59043 40383 23941 + 20450 7244 50144 25063 46863 10252 32923 20517 38617 57419 6950 39119 + 27255 28137 2783 56055 22476 54385 2641 38627 59021 281 33447 3318 + 14406 50712 56354 52921 45356 59463]`"] inter_renew["renew: True"] end end @@ -316,12 +316,12 @@ nKode renewal is a three step process: login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] - new_user_pos["`combined position key:
[65323 43160 15313 31589 56469 63324 16779 58877 46813]`"] - new_user_prop["`property key:
[ 4877 339 2263 7952 818 48661 46131 56267 23637 56219 31553 4783 - 17734 11957 16009 8164 58199 21329 25805 42484 63685 33425 4845 26086 - 45993 37895 46700 19583 28941 36895 52143 15209 11188 569 60851 63329 - 33273 55731 2564 42007 14482 45812 39063 18143 15261 56200 36966 9873 - 43332 61052 51872 65507 42946 33203]`"] + new_user_pos["`combined position key:
[23942 46252 53843 37910 7611 30257 63826 52812 27525]`"] + new_user_prop["`property key:
[12027 59464 45926 15881 63336 21673 37395 23025 6329 62833 48208 18116 + 21253 58416 65368 58683 20373 46352 59037 5050 28807 6687 11647 25865 + 62465 1781 2953 15931 16596 36975 19053 5585 53652 10011 18034 31490 + 25501 8993 19946 5980 35552 33588 63303 2644 62898 12532 3981 16334 + 9471 17209 53533 43576 41327 37349]`"] new_renew["renew: False"] end login --> new_user diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index ef36513..273321c 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -136,11 +136,11 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n{{selected_keys}}"] + pass["user_passcode_indices\n{{user_passcode_idxs}}"] login_keypad["login keypad:\n{{login_keypad}}"] space:4 - selectkeys(("select keys")) + selectkeys(("user")) mask["enciphered mask:\n {{mask}}"] mask_key["mask key:\n{{mask_key}}"] space:2 @@ -148,7 +148,7 @@ block-beta xor1(("XOR")) mask --> xor1 mask_key --> xor1 - selected_keys --> selectkeys + pass --> selectkeys login_keypad --> selectkeys space:3 -- 2.49.1 From ccaab11f45743a7bdea573428875449c33514756 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 09:00:04 -0500 Subject: [PATCH 65/85] fix bugs --- docs/encipher_decipher_renew_nkode.md | 222 +++++++++--------- .../encipher_decipher_renew_nkode.template.md | 2 +- 2 files changed, 112 insertions(+), 112 deletions(-) diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md index ba83382..a1bcf4a 100644 --- a/docs/encipher_decipher_renew_nkode.md +++ b/docs/encipher_decipher_renew_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [17433 57572 21505 8444 45652 40584 48852 43446 5599 62405 28323 40256 - 47776 49892 26386 48063 8519 55431 26581 11344 39650 4798 19208 64337 - 33689 12686 45679 3132 40090 40966 37000 33485 30170 45501 20844 20464 - 53748 19697 19768 4387 39395 35315 9619 28777 48707 30931 62584 35610 - 46036 40873 63440 33641 852 11131] -- position key: [38077 51455 31958 23938 20630 60053 9765 10657 7626] +- property key: [54514 1965 53378 19192 22047 51452 10113 45652 29057 9429 21502 46988 + 49588 18367 49270 58262 42600 57355 6832 60785 43122 21094 14401 36145 + 20935 57304 48338 48291 26673 19914 28977 28652 19987 31121 12774 35536 + 48255 12852 54678 55834 85 26003 45160 20835 54874 52921 15169 50516 + 60357 42125 30022 45817 60130 11197] +- position key: [48112 26772 62306 37312 9280 53287 8575 52285 23223] --- ## User Cipher -- property key: [24983 41762 20840 27438 7273 19213 38577 61466 23138 47314 23998 50092 - 273 14037 34702 41428 64080 27332 13490 12880 55541 24199 778 9414 - 29001 19211 3074 60661 6134 25585 30166 41331 20480 63833 34395 54478 - 26155 58076 18811 43725 18545 28882 15969 25829 22807 22588 35663 41312 - 34398 20904 148 47345 50354 38166] -- passcode key: [29182 58569 45520 52787 7372 34618 40716 38549 23700 43116] -- combined position key: [55717 12248 3398 52977 52974 12019 47318 59100 12380] -- mask key: [51996 30221 59877 58879 34382 22287 31520 64981 60412 37143] +- property key: [25402 45448 18385 37857 57541 32953 19697 45767 30288 57822 46599 257 + 21882 41216 65271 33191 57236 24801 10848 60418 40003 30516 42964 54926 + 4678 57740 23458 22941 19199 13444 34602 28269 28151 31470 11589 62172 + 53096 14757 9376 55563 54085 33633 7682 13084 27027 41237 11751 3039 + 37222 20544 22215 44963 6713 65186] +- passcode key: [28538 33084 55942 23635 1822 36202 45572 34373 48530 20714] +- combined position key: [57013 27802 42252 20840 26015 37308 27613 15454 15422] +- mask key: [29393 54843 61220 63539 63750 28279 20755 38066 32824 58482] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[41859 3923 32291 54686 34461 59585 18017 7286 34891]"] - customer_pos["customer position key:\n[38077 51455 31958 23938 20630 60053 9765 10657 7626]"] + user_pos["user position key:\n[16087 54566 2672 43037 1335 65132 16145 49723 39280]"] + customer_pos["customer position key:\n[48112 26772 62306 37312 9280 53287 8575 52285 23223]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[55717 12248 3398 52977 52974 12019 47318 59100 12380]"]:2 + comb_pos["combined position key\n[57013 27802 42252 20840 26015 37308 27613 15454 15422]"]:2 xor --> comb_pos ``` ## User Keypad -- keypad example:
Key 0: [36 28 38 21 4 41 15 43 26]
Key 1: [ 0 19 47 12 49 14 33 16 17]
Key 2: [ 9 37 2 39 40 50 51 52 44]
Key 3: [27 46 29 30 31 5 24 25 35]
Key 4: [18 10 20 48 22 23 42 34 53]
Key 5: [45 1 11 3 13 32 6 7 8]
-- user passcode indices: [1, 32, 34, 5] +- keypad example:
Key 0: [27 1 2 48 31 41 24 7 17]
Key 1: [45 10 11 39 4 50 42 25 53]
Key 2: [ 9 19 29 3 40 32 51 16 8]
Key 3: [18 37 38 21 22 14 33 43 44]
Key 4: [ 0 28 47 30 49 5 15 34 26]
Key 5: [36 46 20 12 13 23 6 52 35]
+- user passcode indices: [28, 24, 34, 1] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[17433 57572 21505 8444 45652 40584 48852 43446 5599 62405 28323 40256 - 47776 49892 26386 48063 8519 55431 26581 11344 39650 4798 19208 64337 - 33689 12686 45679 3132 40090 40966 37000 33485 30170 45501 20844 20464 - 53748 19697 19768 4387 39395 35315 9619 28777 48707 30931 62584 35610 - 46036 40873 63440 33641 852 11131]"] - uprop["user_property_key\n[24983 41762 20840 27438 7273 19213 38577 61466 23138 47314 23998 50092 - 273 14037 34702 41428 64080 27332 13490 12880 55541 24199 778 9414 - 29001 19211 3074 60661 6134 25585 30166 41331 20480 63833 34395 54478 - 26155 58076 18811 43725 18545 28882 15969 25829 22807 22588 35663 41312 - 34398 20904 148 47345 50354 38166]"] + cprop["customer_property_key\n[54514 1965 53378 19192 22047 51452 10113 45652 29057 9429 21502 46988 + 49588 18367 49270 58262 42600 57355 6832 60785 43122 21094 14401 36145 + 20935 57304 48338 48291 26673 19914 28977 28652 19987 31121 12774 35536 + 48255 12852 54678 55834 85 26003 45160 20835 54874 52921 15169 50516 + 60357 42125 30022 45817 60130 11197]"] + uprop["user_property_key\n[25402 45448 18385 37857 57541 32953 19697 45767 30288 57822 46599 257 + 21882 41216 65271 33191 57236 24801 10848 60418 40003 30516 42964 54926 + 4678 57740 23458 22941 19199 13444 34602 28269 28151 31470 11589 62172 + 53096 14757 9376 55563 54085 33633 7682 13084 27027 41237 11751 3039 + 37222 20544 22215 44963 6713 65186]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[ 9552 31208 37587 40095 45072 12350 32006 2925 11611 34589 36849 42561 - 26580 61891 26520 39789 65017 7624 63974 8702 9646 62493 54967 42708 - 52347 11714 29071 28123 11157 34826 4115 53992 58115 20982 19018 55103 - 48003 8472 18407 52180 52783 23938 12226 59018 22734 31178 30431 34796 - 35730 22961 11250 19920 45688 49980]"] + prop["combined_property_key\n[22860 48501 43623 2691 11008 34410 41991 51506 29272 24365 54234 54358 + 3371 32869 56118 23725 27650 38652 25509 24668 2625 31115 7846 1530 + 28577 8896 10269 5837 40269 59501 63202 34622 24010 60186 38638 52779 + 27566 57898 58845 6587 13015 10925 7087 34598 49492 27689 9569 8776 + 60168 63531 64702 53160 62709 51058]"] xor1 --> prop - pass["user_passcode_indices\n[1, 32, 34, 5]"] + pass["user_passcode_indices\n[28, 24, 34, 1]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[31208 58115 19018 12350]"]:2 + passcode["user passcode properties:\n[40269 28577 38638 48501]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[31208 58115 19018 12350 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[40269 28577 38638 48501 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[29182 58569 45520 52787 7372 34618 40716 38549 23700 43116]"] + passkey["passcode key:\n[28538 33084 55942 23635 1822 36202 45572 34373 48530 20714]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[ 2070 1994 64410 65037 7372 34618 40716 38549 23700 43116]"]:2 + cipheredpass["ciphered passcode:\n[62007 61085 19560 57638 1822 36202 45572 34373 48530 20714]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$mSfxfqokaUYYc8CHnj1nV.3fz.FUkMo4dNreWfUUO4zdgN0LQQ2Tm"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$YW2vbeWXlAc3TdUGX9l8gOghIZ3ZQSMF8ia2K0Bj5dzmtUlx3hWqS"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[1, 32, 34, 5]"] - comb_pos["combined position key:\n[55717 12248 3398 52977 52974 12019 47318 59100 12380]"] - cust_pos["customer position key:\n[38077 51455 31958 23938 20630 60053 9765 10657 7626]"] + passcode_idx["passcode indices:\n[28, 24, 34, 1]"] + comb_pos["combined position key:\n[57013 27802 42252 20840 26015 37308 27613 15454 15422]"] + cust_pos["customer position key:\n[48112 26772 62306 37312 9280 53287 8575 52285 23223]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[1, 5, 7, 5]"] + passcode_position_idx["passcode poition indices:\n[1, 6, 7, 1]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[1, 5, 7, 5, 7, 2, 8, 1, 7, 1]"] + posidx["Padded Passcode Position Indices:\n[1, 6, 7, 1, 5, 0, 8, 6, 2, 0]"] pad1 --> posidx space:1 - user_pos["user position key:\n[41859 3923 32291 54686 34461 59585 18017 7286 34891]"] + user_pos["user position key:\n[16087 54566 2672 43037 1335 65132 16145 49723 39280]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[ 3923 59585 7286 59585 7286 32291 34891 3923 7286 3923]"] + passcode_pos["ordered user passcode positions:\n[54566 16145 49723 54566 65132 16087 39280 16145 2672 16087]"] sel --> passcode_pos - mask_key["mask key\n[51996 30221 59877 58879 34382 22287 31520 64981 60412 37143]"] + mask_key["mask key\n[29393 54843 61220 63539 63750 28279 20755 38066 32824 58482]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [50255 40652 62867 3390 39480 10540 62315 62086 63370 40516]"] + mask["enciphered mask:\n [42999 59690 11551 11541 1898 20640 51299 43939 35400 55973]"] xor2 --> mask ``` @@ -156,19 +156,19 @@ block-beta ```mermaid block-beta columns 3 - pass["user_passcode_indices\n[1, 32, 34, 5]"] - login_keypad["login keypad:\nKey 0: [36 28 38 21 4 41 15 43 26] -Key 1: [ 0 19 47 12 49 14 33 16 17] -Key 2: [ 9 37 2 39 40 50 51 52 44] -Key 3: [27 46 29 30 31 5 24 25 35] -Key 4: [18 10 20 48 22 23 42 34 53] -Key 5: [45 1 11 3 13 32 6 7 8] + pass["user_passcode_indices\n[28, 24, 34, 1]"] + login_keypad["login keypad:\nKey 0: [27 1 2 48 31 41 24 7 17] +Key 1: [45 10 11 39 4 50 42 25 53] +Key 2: [ 9 19 29 3 40 32 51 16 8] +Key 3: [18 37 38 21 22 14 33 43 44] +Key 4: [ 0 28 47 30 49 5 15 34 26] +Key 5: [36 46 20 12 13 23 6 52 35] "] space:4 - selectkeys(("user")) - mask["enciphered mask:\n [50255 40652 62867 3390 39480 10540 62315 62086 63370 40516]"] - mask_key["mask key:\n[51996 30221 59877 58879 34382 22287 31520 64981 60412 37143]"] + selectkeys(("select keys")) + mask["enciphered mask:\n [42999 59690 11551 11541 1898 20640 51299 43939 35400 55973]"] + mask_key["mask key:\n[29393 54843 61220 63539 63750 28279 20755 38066 32824 58482]"] space:2 xor1(("XOR")) @@ -178,12 +178,12 @@ Key 5: [45 1 11 3 13 32 6 7 8] login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[45 1 11 3 13 32 6 7 8] - [45 1 11 3 13 32 6 7 8] - [18 10 20 48 22 23 42 34 53] - [27 46 29 30 31 5 24 25 35]]"] - user_position_key["user position key:\n[41859 3923 32291 54686 34461 59585 18017 7286 34891]"] - passcode_pos["ordered user passcode positions:\n[ 3923 59585 7286 59585 7286 32291 34891 3923 7286 3923]"] + ordered_keys["ordered keys:\n[[ 0 28 47 30 49 5 15 34 26] + [27 1 2 48 31 41 24 7 17] + [ 0 28 47 30 49 5 15 34 26] + [27 1 2 48 31 41 24 7 17]]"] + user_position_key["user position key:\n[16087 54566 2672 43037 1335 65132 16145 49723 39280]"] + passcode_pos["ordered user passcode positions:\n[54566 16145 49723 54566 65132 16087 39280 16145 2672 16087]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [45 1 11 3 13 32 6 7 8] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[1, 5, 7, 5, 7, 2, 8, 1, 7, 1]"] + passcode_pos_idxs["padded passcode position indices:\n[1, 6, 7, 1, 5, 0, 8, 6, 2, 0]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [45 1 11 3 13 32 6 7 8] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[1, 32, 34, 5]"] - prop["combined_property_key\n[ 9552 31208 37587 40095 45072 12350 32006 2925 11611 34589 36849 42561 - 26580 61891 26520 39789 65017 7624 63974 8702 9646 62493 54967 42708 - 52347 11714 29071 28123 11157 34826 4115 53992 58115 20982 19018 55103 - 48003 8472 18407 52180 52783 23938 12226 59018 22734 31178 30431 34796 - 35730 22961 11250 19920 45688 49980]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$mSfxfqokaUYYc8CHnj1nV.3fz.FUkMo4dNreWfUUO4zdgN0LQQ2Tm"] + passcode_prop_idxs["presumed passcode property indices:\n[28, 24, 34, 1]"] + prop["combined_property_key\n[22860 48501 43623 2691 11008 34410 41991 51506 29272 24365 54234 54358 + 3371 32869 56118 23725 27650 38652 25509 24668 2625 31115 7846 1530 + 28577 8896 10269 5837 40269 59501 63202 34622 24010 60186 38638 52779 + 27566 57898 58845 6587 13015 10925 7087 34598 49492 27689 9569 8776 + 60168 63531 64702 53160 62709 51058]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$YW2vbeWXlAc3TdUGX9l8gOghIZ3ZQSMF8ia2K0Bj5dzmtUlx3hWqS"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [45 1 11 3 13 32 6 7 8] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[31208 58115 19018 12350]"] + passcode_prop["presumed passcode properties:\n[40269 28577 38638 48501]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [45 1 11 3 13 32 6 7 8] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[ 2070 1994 64410 65037 7372 34618 40716 38549 23700 43116]"] + cipheredpass["ciphered passcode:\n[62007 61085 19560 57638 1822 36202 45572 34373 48530 20714]"] cipher --> cipheredpass space:7 @@ -250,26 +250,26 @@ nKode renewal is a three step process: ```mermaid flowchart subgraph Renew Customer Keys - old_prop["`old customer property key:
[17607 56010 50107 63409 44153 31539 60343 64375 30521 16335 53839 26093 - 26309 50966 57366 15033 1961 30476 52564 5038 64859 43674 54717 33298 - 48434 26313 32141 33070 15459 60411 26053 29595 45827 43183 52241 1009 - 56744 50116 3740 24857 34398 11600 4515 33391 473 8694 64912 9868 - 3532 2073 11110 62753 30410 22058]`"] - new_prop["`new customer property key:
[17433 57572 21505 8444 45652 40584 48852 43446 5599 62405 28323 40256 - 47776 49892 26386 48063 8519 55431 26581 11344 39650 4798 19208 64337 - 33689 12686 45679 3132 40090 40966 37000 33485 30170 45501 20844 20464 - 53748 19697 19768 4387 39395 35315 9619 28777 48707 30931 62584 35610 - 46036 40873 63440 33641 852 11131]`"] - old_pos["`old customer position key:
[31270 8331 29541 7023 18547 50738 65207 64170 47127]`"] - new_pos["`new customer position key:
[38077 51455 31958 23938 20630 60053 9765 10657 7626]`"] + old_prop["`old customer property key:
[14966 3325 60854 39266 52165 1747 59638 31733 1032 48883 26077 54615 + 22609 8549 9665 56586 45974 63005 18885 35934 38402 3775 47474 54132 + 32231 49996 29631 20304 55218 56553 29128 59731 12349 37364 48043 15607 + 42182 56207 49533 49328 57746 43468 1453 46138 43207 52540 2182 10647 + 31342 43115 43641 24587 61132 14800]`"] + new_prop["`new customer property key:
[54514 1965 53378 19192 22047 51452 10113 45652 29057 9429 21502 46988 + 49588 18367 49270 58262 42600 57355 6832 60785 43122 21094 14401 36145 + 20935 57304 48338 48291 26673 19914 28977 28652 19987 31121 12774 35536 + 48255 12852 54678 55834 85 26003 45160 20835 54874 52921 15169 50516 + 60357 42125 30022 45817 60130 11197]`"] + old_pos["`old customer position key:
[57442 47548 44924 63861 24744 28624 21708 65125 42318]`"] + new_pos["`new customer position key:
[48112 26772 62306 37312 9280 53287 8575 52285 23223]`"] xor1(("XOR")) xor2(("XOR")) - xor_prop["`xor property key:
[ 222 14894 38842 55117 7725 58811 21859 21185 25318 52234 48364 63661 - 56421 1522 34564 33030 9966 44939 43649 16382 26553 47140 40629 31043 - 16043 22343 53218 36114 41209 19453 62797 61782 50905 6418 40317 19457 - 3164 36661 17316 28730 8125 42147 13360 61958 49050 22821 2536 44438 - 48664 38832 56502 30280 30110 32081]`"] - xor_pos["`xor position key:
[61083 59508 4019 18157 6373 11431 55442 54027 42461]`"] + xor_prop["`xor property key:
[61060 2896 15668 54170 40410 52783 53111 51617 30089 39462 13859 25307 + 39397 26330 58807 16028 5630 5654 21365 24879 15984 23769 33075 24133 + 11296 7316 53101 62451 49027 37155 249 34495 32302 59493 35405 46631 + 6329 59835 5355 6826 57799 52319 46533 58713 32413 901 13255 60611 + 37291 3302 57151 54002 1070 4717]`"] + xor_pos["`xor position key:
[23442 53544 23582 26805 17640 49143 30131 12888 65529]`"] old_prop --> xor1 new_prop --> xor1 xor1 --> xor_prop @@ -283,12 +283,12 @@ nKode renewal is a three step process: users --> eachuser subgraph eachuser [for each user] subgraph old user keys - old_user_pos["`combined position key:
[41859 3923 32291 54686 34461 59585 18017 7286 34891]`"] - old_user_prop["`property key:
[24983 41762 20840 27438 7273 19213 38577 61466 23138 47314 23998 50092 - 273 14037 34702 41428 64080 27332 13490 12880 55541 24199 778 9414 - 29001 19211 3074 60661 6134 25585 30166 41331 20480 63833 34395 54478 - 26155 58076 18811 43725 18545 28882 15969 25829 22807 22588 35663 41312 - 34398 20904 148 47345 50354 38166]`"] + old_user_pos["`combined position key:
[16087 54566 2672 43037 1335 65132 16145 49723 39280]`"] + old_user_prop["`property key:
[25402 45448 18385 37857 57541 32953 19697 45767 30288 57822 46599 257 + 21882 41216 65271 33191 57236 24801 10848 60418 40003 30516 42964 54926 + 4678 57740 23458 22941 19199 13444 34602 28269 28151 31470 11589 62172 + 53096 14757 9376 55563 54085 33633 7682 13084 27027 41237 11751 3039 + 37222 20544 22215 44963 6713 65186]`"] old_renew["renew: False"] end xor3(("XOR")) @@ -300,12 +300,12 @@ nKode renewal is a three step process: xor_prop --> xor4 xor4 --> inter_user_prop subgraph inter_user[intermediate user keys] - inter_user_pos["`combined position key:
[14142 51116 757 34844 54795 596 24644 13783 38273]`"] - inter_user_prop["`property key:
[24905 39180 50898 48227 580 44726 50130 41691 14468 29912 57682 15105 - 56692 13095 138 8402 56510 50511 40499 3502 48972 59043 40383 23941 - 20450 7244 50144 25063 46863 10252 32923 20517 38617 57419 6950 39119 - 27255 28137 2783 56055 22476 54385 2641 38627 59021 281 33447 3318 - 14406 50712 56354 52921 45356 59463]`"] + inter_user_pos["`combined position key:
[34087 48562 63762 14813 8567 11851 7790 3590 50119]`"] + inter_user_prop["`property key:
[36286 47832 31461 16507 32031 20118 33670 31590 985 31736 32804 25562 + 52383 51162 6976 48955 51818 30455 30997 36141 41523 11245 9959 35019 + 15974 64792 38095 43630 62844 42407 34771 59602 5081 37515 42760 17659 + 55249 53278 12363 50081 12930 20286 43975 54853 5902 41616 7712 59164 + 205 23718 35320 32081 7703 60623]`"] inter_renew["renew: True"] end end @@ -316,12 +316,12 @@ nKode renewal is a three step process: login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] - new_user_pos["`combined position key:
[23942 46252 53843 37910 7611 30257 63826 52812 27525]`"] - new_user_prop["`property key:
[12027 59464 45926 15881 63336 21673 37395 23025 6329 62833 48208 18116 - 21253 58416 65368 58683 20373 46352 59037 5050 28807 6687 11647 25865 - 62465 1781 2953 15931 16596 36975 19053 5585 53652 10011 18034 31490 - 25501 8993 19946 5980 35552 33588 63303 2644 62898 12532 3981 16334 - 9471 17209 53533 43576 41327 37349]`"] + new_user_pos["`combined position key:
[30180 41733 51338 65033 46822 22413 14740 11265 36086]`"] + new_user_prop["`property key:
[63029 58440 35697 22366 54518 106 30872 42634 43391 56303 8441 57457 + 40463 15976 60517 30636 40657 60903 39392 12539 19960 16602 3225 8105 + 30680 64909 9750 19292 15934 38070 9733 36553 61894 2864 25710 48492 + 54051 23592 44246 3991 38055 9064 51185 33976 39567 54282 59604 15986 + 12137 21928 16931 38440 25594 42613]`"] new_renew["renew: False"] end login --> new_user diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index 273321c..e64fdd9 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -140,7 +140,7 @@ block-beta login_keypad["login keypad:\n{{login_keypad}}"] space:4 - selectkeys(("user")) + selectkeys(("select keys")) mask["enciphered mask:\n {{mask}}"] mask_key["mask key:\n{{mask_key}}"] space:2 -- 2.49.1 From b9e144243d2589c50d145152ceb5da18559f004c Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 09:01:39 -0500 Subject: [PATCH 66/85] revert --- docs/encipher_decipher_renew_nkode.md | 222 +++++++++--------- .../encipher_decipher_renew_nkode.template.md | 4 +- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md index a1bcf4a..04e3067 100644 --- a/docs/encipher_decipher_renew_nkode.md +++ b/docs/encipher_decipher_renew_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [54514 1965 53378 19192 22047 51452 10113 45652 29057 9429 21502 46988 - 49588 18367 49270 58262 42600 57355 6832 60785 43122 21094 14401 36145 - 20935 57304 48338 48291 26673 19914 28977 28652 19987 31121 12774 35536 - 48255 12852 54678 55834 85 26003 45160 20835 54874 52921 15169 50516 - 60357 42125 30022 45817 60130 11197] -- position key: [48112 26772 62306 37312 9280 53287 8575 52285 23223] +- property key: [12463 12679 30383 52780 48100 5096 61398 5750 59949 16574 25395 8158 + 51151 56863 52932 11578 50839 38005 12741 42227 40877 7172 53174 60400 + 12356 13971 19389 64980 16810 26389 39074 63833 12649 64422 4920 54703 + 62742 25787 26216 4453 38866 27951 36713 37903 22497 4454 55622 38346 + 31982 51225 48698 29699 36128 33748] +- position key: [56601 6465 12615 40545 49643 37783 59315 4335 13897] --- ## User Cipher -- property key: [25402 45448 18385 37857 57541 32953 19697 45767 30288 57822 46599 257 - 21882 41216 65271 33191 57236 24801 10848 60418 40003 30516 42964 54926 - 4678 57740 23458 22941 19199 13444 34602 28269 28151 31470 11589 62172 - 53096 14757 9376 55563 54085 33633 7682 13084 27027 41237 11751 3039 - 37222 20544 22215 44963 6713 65186] -- passcode key: [28538 33084 55942 23635 1822 36202 45572 34373 48530 20714] -- combined position key: [57013 27802 42252 20840 26015 37308 27613 15454 15422] -- mask key: [29393 54843 61220 63539 63750 28279 20755 38066 32824 58482] +- property key: [14898 49243 46261 43286 743 60989 43535 48626 62301 50822 24597 10059 + 48516 48141 42915 36062 3286 11914 44159 34414 20879 14779 26916 49959 + 52279 29970 35884 62523 22453 25290 37000 30416 6102 20665 42719 23156 + 54785 3366 46811 57278 42429 58327 26021 27943 614 26543 63962 45468 + 15599 34845 22766 44215 32257 43746] +- passcode key: [ 4981 30039 31839 45969 48275 35799 10819 2121 61259 48949] +- combined position key: [58180 35354 39081 61964 32045 26428 24571 62221 62362] +- mask key: [ 3841 58027 55280 33679 39465 22075 10291 60287 27680 15384] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[16087 54566 2672 43037 1335 65132 16145 49723 39280]"] - customer_pos["customer position key:\n[48112 26772 62306 37312 9280 53287 8575 52285 23223]"] + user_pos["user position key:\n[57725 11307 31071 28372 27611 49409 15121 51933 25996]"] + customer_pos["customer position key:\n[56601 6465 12615 40545 49643 37783 59315 4335 13897]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[57013 27802 42252 20840 26015 37308 27613 15454 15422]"]:2 + comb_pos["combined position key\n[58180 35354 39081 61964 32045 26428 24571 62221 62362]"]:2 xor --> comb_pos ``` ## User Keypad -- keypad example:
Key 0: [27 1 2 48 31 41 24 7 17]
Key 1: [45 10 11 39 4 50 42 25 53]
Key 2: [ 9 19 29 3 40 32 51 16 8]
Key 3: [18 37 38 21 22 14 33 43 44]
Key 4: [ 0 28 47 30 49 5 15 34 26]
Key 5: [36 46 20 12 13 23 6 52 35]
-- user passcode indices: [28, 24, 34, 1] +- keypad example:
Key 0: [18 19 2 21 31 5 6 43 17]
Key 1: [ 9 46 29 30 49 32 42 52 35]
Key 2: [27 10 20 48 40 14 33 34 44]
Key 3: [45 28 47 12 4 23 51 25 26]
Key 4: [ 0 1 38 3 22 41 24 16 53]
Key 5: [36 37 11 39 13 50 15 7 8]
+- user passcode indices: [6, 43, 53, 9] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[54514 1965 53378 19192 22047 51452 10113 45652 29057 9429 21502 46988 - 49588 18367 49270 58262 42600 57355 6832 60785 43122 21094 14401 36145 - 20935 57304 48338 48291 26673 19914 28977 28652 19987 31121 12774 35536 - 48255 12852 54678 55834 85 26003 45160 20835 54874 52921 15169 50516 - 60357 42125 30022 45817 60130 11197]"] - uprop["user_property_key\n[25402 45448 18385 37857 57541 32953 19697 45767 30288 57822 46599 257 - 21882 41216 65271 33191 57236 24801 10848 60418 40003 30516 42964 54926 - 4678 57740 23458 22941 19199 13444 34602 28269 28151 31470 11589 62172 - 53096 14757 9376 55563 54085 33633 7682 13084 27027 41237 11751 3039 - 37222 20544 22215 44963 6713 65186]"] + cprop["customer_property_key\n[12463 12679 30383 52780 48100 5096 61398 5750 59949 16574 25395 8158 + 51151 56863 52932 11578 50839 38005 12741 42227 40877 7172 53174 60400 + 12356 13971 19389 64980 16810 26389 39074 63833 12649 64422 4920 54703 + 62742 25787 26216 4453 38866 27951 36713 37903 22497 4454 55622 38346 + 31982 51225 48698 29699 36128 33748]"] + uprop["user_property_key\n[14898 49243 46261 43286 743 60989 43535 48626 62301 50822 24597 10059 + 48516 48141 42915 36062 3286 11914 44159 34414 20879 14779 26916 49959 + 52279 29970 35884 62523 22453 25290 37000 30416 6102 20665 42719 23156 + 54785 3366 46811 57278 42429 58327 26021 27943 614 26543 63962 45468 + 15599 34845 22766 44215 32257 43746]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[22860 48501 43623 2691 11008 34410 41991 51506 29272 24365 54234 54358 - 3371 32869 56118 23725 27650 38652 25509 24668 2625 31115 7846 1530 - 28577 8896 10269 5837 40269 59501 63202 34622 24010 60186 38638 52779 - 27566 57898 58845 6587 13015 10925 7087 34598 49492 27689 9569 8776 - 60168 63531 64702 53160 62709 51058]"] + prop["combined_property_key\n[ 2598 13817 3743 55138 48519 14914 34131 3917 24848 29635 52388 57749 + 31090 24065 22959 61051 34121 30393 691 47015 26630 54959 13562 20862 + 31358 35382 39584 57405 53720 45712 49496 53667 35073 25844 36156 57792 + 30240 21451 23037 3839 61742 34083 35502 63962 25413 50263 31741 63116 + 30806 16519 51243 8712 32194 8269]"] xor1 --> prop - pass["user_passcode_indices\n[28, 24, 34, 1]"] + pass["user_passcode_indices\n[6, 43, 53, 9]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[40269 28577 38638 48501]"]:2 + passcode["user passcode properties:\n[34131 63962 8269 29635]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[40269 28577 38638 48501 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[34131 63962 8269 29635 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[28538 33084 55942 23635 1822 36202 45572 34373 48530 20714]"] + passkey["passcode key:\n[ 4981 30039 31839 45969 48275 35799 10819 2121 61259 48949]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[62007 61085 19560 57638 1822 36202 45572 34373 48530 20714]"]:2 + cipheredpass["ciphered passcode:\n[38438 35981 23570 49234 48275 35799 10819 2121 61259 48949]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$YW2vbeWXlAc3TdUGX9l8gOghIZ3ZQSMF8ia2K0Bj5dzmtUlx3hWqS"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$rF1TSRHSHfOTE4V/7ZCv3.Plqb1AysifW1KvR96K7CbC1sPt1ungK"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[28, 24, 34, 1]"] - comb_pos["combined position key:\n[57013 27802 42252 20840 26015 37308 27613 15454 15422]"] - cust_pos["customer position key:\n[48112 26772 62306 37312 9280 53287 8575 52285 23223]"] + passcode_idx["passcode indices:\n[6, 43, 53, 9]"] + comb_pos["combined position key:\n[58180 35354 39081 61964 32045 26428 24571 62221 62362]"] + cust_pos["customer position key:\n[56601 6465 12615 40545 49643 37783 59315 4335 13897]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[1, 6, 7, 1]"] + passcode_position_idx["passcode poition indices:\n[6, 7, 8, 0]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[1, 6, 7, 1, 5, 0, 8, 6, 2, 0]"] + posidx["Padded Passcode Position Indices:\n[6, 7, 8, 0, 1, 1, 2, 3, 0, 5]"] pad1 --> posidx space:1 - user_pos["user position key:\n[16087 54566 2672 43037 1335 65132 16145 49723 39280]"] + user_pos["user position key:\n[57725 11307 31071 28372 27611 49409 15121 51933 25996]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[54566 16145 49723 54566 65132 16087 39280 16145 2672 16087]"] + passcode_pos["ordered user passcode positions:\n[15121 51933 25996 57725 11307 11307 31071 28372 57725 49409]"] sel --> passcode_pos - mask_key["mask key\n[29393 54843 61220 63539 63750 28279 20755 38066 32824 58482]"] + mask_key["mask key\n[ 3841 58027 55280 33679 39465 22075 10291 60287 27680 15384]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [42999 59690 11551 11541 1898 20640 51299 43939 35400 55973]"] + mask["enciphered mask:\n [13328 10358 45692 25330 46594 31248 20844 34219 36189 64793]"] xor2 --> mask ``` @@ -156,34 +156,34 @@ block-beta ```mermaid block-beta columns 3 - pass["user_passcode_indices\n[28, 24, 34, 1]"] - login_keypad["login keypad:\nKey 0: [27 1 2 48 31 41 24 7 17] -Key 1: [45 10 11 39 4 50 42 25 53] -Key 2: [ 9 19 29 3 40 32 51 16 8] -Key 3: [18 37 38 21 22 14 33 43 44] -Key 4: [ 0 28 47 30 49 5 15 34 26] -Key 5: [36 46 20 12 13 23 6 52 35] + selected_keys["selected keys:\n[0, 0, 4, 1]"] + login_keypad["login keypad:\nKey 0: [18 19 2 21 31 5 6 43 17] +Key 1: [ 9 46 29 30 49 32 42 52 35] +Key 2: [27 10 20 48 40 14 33 34 44] +Key 3: [45 28 47 12 4 23 51 25 26] +Key 4: [ 0 1 38 3 22 41 24 16 53] +Key 5: [36 37 11 39 13 50 15 7 8] "] space:4 selectkeys(("select keys")) - mask["enciphered mask:\n [42999 59690 11551 11541 1898 20640 51299 43939 35400 55973]"] - mask_key["mask key:\n[29393 54843 61220 63539 63750 28279 20755 38066 32824 58482]"] + mask["enciphered mask:\n [13328 10358 45692 25330 46594 31248 20844 34219 36189 64793]"] + mask_key["mask key:\n[ 3841 58027 55280 33679 39465 22075 10291 60287 27680 15384]"] space:2 xor1(("XOR")) mask --> xor1 mask_key --> xor1 - pass --> selectkeys + selected_keys --> selectkeys login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[ 0 28 47 30 49 5 15 34 26] - [27 1 2 48 31 41 24 7 17] - [ 0 28 47 30 49 5 15 34 26] - [27 1 2 48 31 41 24 7 17]]"] - user_position_key["user position key:\n[16087 54566 2672 43037 1335 65132 16145 49723 39280]"] - passcode_pos["ordered user passcode positions:\n[54566 16145 49723 54566 65132 16087 39280 16145 2672 16087]"] + ordered_keys["ordered keys:\n[[18 19 2 21 31 5 6 43 17] + [18 19 2 21 31 5 6 43 17] + [ 0 1 38 3 22 41 24 16 53] + [ 9 46 29 30 49 32 42 52 35]]"] + user_position_key["user position key:\n[57725 11307 31071 28372 27611 49409 15121 51933 25996]"] + passcode_pos["ordered user passcode positions:\n[15121 51933 25996 57725 11307 11307 31071 28372 57725 49409]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [36 46 20 12 13 23 6 52 35] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[1, 6, 7, 1, 5, 0, 8, 6, 2, 0]"] + passcode_pos_idxs["padded passcode position indices:\n[6, 7, 8, 0, 1, 1, 2, 3, 0, 5]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [36 46 20 12 13 23 6 52 35] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[28, 24, 34, 1]"] - prop["combined_property_key\n[22860 48501 43623 2691 11008 34410 41991 51506 29272 24365 54234 54358 - 3371 32869 56118 23725 27650 38652 25509 24668 2625 31115 7846 1530 - 28577 8896 10269 5837 40269 59501 63202 34622 24010 60186 38638 52779 - 27566 57898 58845 6587 13015 10925 7087 34598 49492 27689 9569 8776 - 60168 63531 64702 53160 62709 51058]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$YW2vbeWXlAc3TdUGX9l8gOghIZ3ZQSMF8ia2K0Bj5dzmtUlx3hWqS"] + passcode_prop_idxs["presumed passcode property indices:\n[6, 43, 53, 9]"] + prop["combined_property_key\n[ 2598 13817 3743 55138 48519 14914 34131 3917 24848 29635 52388 57749 + 31090 24065 22959 61051 34121 30393 691 47015 26630 54959 13562 20862 + 31358 35382 39584 57405 53720 45712 49496 53667 35073 25844 36156 57792 + 30240 21451 23037 3839 61742 34083 35502 63962 25413 50263 31741 63116 + 30806 16519 51243 8712 32194 8269]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$rF1TSRHSHfOTE4V/7ZCv3.Plqb1AysifW1KvR96K7CbC1sPt1ungK"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [36 46 20 12 13 23 6 52 35] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[40269 28577 38638 48501]"] + passcode_prop["presumed passcode properties:\n[34131 63962 8269 29635]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [36 46 20 12 13 23 6 52 35] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[62007 61085 19560 57638 1822 36202 45572 34373 48530 20714]"] + cipheredpass["ciphered passcode:\n[38438 35981 23570 49234 48275 35799 10819 2121 61259 48949]"] cipher --> cipheredpass space:7 @@ -250,26 +250,26 @@ nKode renewal is a three step process: ```mermaid flowchart subgraph Renew Customer Keys - old_prop["`old customer property key:
[14966 3325 60854 39266 52165 1747 59638 31733 1032 48883 26077 54615 - 22609 8549 9665 56586 45974 63005 18885 35934 38402 3775 47474 54132 - 32231 49996 29631 20304 55218 56553 29128 59731 12349 37364 48043 15607 - 42182 56207 49533 49328 57746 43468 1453 46138 43207 52540 2182 10647 - 31342 43115 43641 24587 61132 14800]`"] - new_prop["`new customer property key:
[54514 1965 53378 19192 22047 51452 10113 45652 29057 9429 21502 46988 - 49588 18367 49270 58262 42600 57355 6832 60785 43122 21094 14401 36145 - 20935 57304 48338 48291 26673 19914 28977 28652 19987 31121 12774 35536 - 48255 12852 54678 55834 85 26003 45160 20835 54874 52921 15169 50516 - 60357 42125 30022 45817 60130 11197]`"] - old_pos["`old customer position key:
[57442 47548 44924 63861 24744 28624 21708 65125 42318]`"] - new_pos["`new customer position key:
[48112 26772 62306 37312 9280 53287 8575 52285 23223]`"] + old_prop["`old customer property key:
[12308 62882 47658 32372 48992 54399 12124 45759 37453 46405 44209 50910 + 50422 57868 65036 25253 35231 22579 44748 12745 14729 61204 24030 37465 + 46665 65316 5772 5126 34413 53338 20944 42867 40663 13389 11235 48052 + 40993 24301 61222 53569 21651 26356 61195 38141 24867 41976 33319 18192 + 17593 51354 37061 36543 963 35503]`"] + new_prop["`new customer property key:
[12463 12679 30383 52780 48100 5096 61398 5750 59949 16574 25395 8158 + 51151 56863 52932 11578 50839 38005 12741 42227 40877 7172 53174 60400 + 12356 13971 19389 64980 16810 26389 39074 63833 12649 64422 4920 54703 + 62742 25787 26216 4453 38866 27951 36713 37903 22497 4454 55622 38346 + 31982 51225 48698 29699 36128 33748]`"] + old_pos["`old customer position key:
[ 569 42545 57846 40152 5878 42557 25834 14800 38422]`"] + new_pos["`new customer position key:
[56601 6465 12615 40545 49643 37783 59315 4335 13897]`"] xor1(("XOR")) xor2(("XOR")) - xor_prop["`xor property key:
[61060 2896 15668 54170 40410 52783 53111 51617 30089 39462 13859 25307 - 39397 26330 58807 16028 5630 5654 21365 24879 15984 23769 33075 24133 - 11296 7316 53101 62451 49027 37155 249 34495 32302 59493 35405 46631 - 6329 59835 5355 6826 57799 52319 46533 58713 32413 901 13255 60611 - 37291 3302 57151 54002 1070 4717]`"] - xor_pos["`xor position key:
[23442 53544 23582 26805 17640 49143 30131 12888 65529]`"] + xor_prop["`xor property key:
[ 187 50213 52357 45144 1156 51095 49290 42185 30816 62971 53122 55552 + 825 15379 12488 20383 20232 52294 40713 38202 42532 62224 37480 31145 + 34317 51639 23857 59858 51143 46927 51570 24106 44990 53227 14555 28187 + 21815 14934 35150 49188 49985 3035 24674 242 14018 45726 23393 53978 + 14423 131 12031 64188 36579 2427]`"] + xor_pos["`xor position key:
[57120 49008 53425 697 55069 13738 33625 10559 41055]`"] old_prop --> xor1 new_prop --> xor1 xor1 --> xor_prop @@ -283,12 +283,12 @@ nKode renewal is a three step process: users --> eachuser subgraph eachuser [for each user] subgraph old user keys - old_user_pos["`combined position key:
[16087 54566 2672 43037 1335 65132 16145 49723 39280]`"] - old_user_prop["`property key:
[25402 45448 18385 37857 57541 32953 19697 45767 30288 57822 46599 257 - 21882 41216 65271 33191 57236 24801 10848 60418 40003 30516 42964 54926 - 4678 57740 23458 22941 19199 13444 34602 28269 28151 31470 11589 62172 - 53096 14757 9376 55563 54085 33633 7682 13084 27027 41237 11751 3039 - 37222 20544 22215 44963 6713 65186]`"] + old_user_pos["`combined position key:
[57725 11307 31071 28372 27611 49409 15121 51933 25996]`"] + old_user_prop["`property key:
[14898 49243 46261 43286 743 60989 43535 48626 62301 50822 24597 10059 + 48516 48141 42915 36062 3286 11914 44159 34414 20879 14779 26916 49959 + 52279 29970 35884 62523 22453 25290 37000 30416 6102 20665 42719 23156 + 54785 3366 46811 57278 42429 58327 26021 27943 614 26543 63962 45468 + 15599 34845 22766 44215 32257 43746]`"] old_renew["renew: False"] end xor3(("XOR")) @@ -300,12 +300,12 @@ nKode renewal is a three step process: xor_prop --> xor4 xor4 --> inter_user_prop subgraph inter_user[intermediate user keys] - inter_user_pos["`combined position key:
[34087 48562 63762 14813 8567 11851 7790 3590 50119]`"] - inter_user_prop["`property key:
[36286 47832 31461 16507 32031 20118 33670 31590 985 31736 32804 25562 - 52383 51162 6976 48955 51818 30455 30997 36141 41523 11245 9959 35019 - 15974 64792 38095 43630 62844 42407 34771 59602 5081 37515 42760 17659 - 55249 53278 12363 50081 12930 20286 43975 54853 5902 41616 7712 59164 - 205 23718 35320 32081 7703 60623]`"] + inter_user_pos["`combined position key:
[15460 13674 18456 61621 43568 21142 56482 55858 21445]`"] + inter_user_prop["`property key:
[14985 1150 30768 6478 1635 10666 27269 6459 35645 13181 44951 65099 + 48829 32798 38763 49985 17374 58060 13174 4948 63403 51883 64332 47758 + 19002 48293 53533 7657 36978 54661 23034 10490 47208 40786 40452 13423 + 33590 14192 16277 8090 26364 59404 1479 28117 13476 54577 41659 25414 + 1208 34974 30225 22027 61666 41881]`"] inter_renew["renew: True"] end end @@ -316,12 +316,12 @@ nKode renewal is a three step process: login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] - new_user_pos["`combined position key:
[30180 41733 51338 65033 46822 22413 14740 11265 36086]`"] - new_user_prop["`property key:
[63029 58440 35697 22366 54518 106 30872 42634 43391 56303 8441 57457 - 40463 15976 60517 30636 40657 60903 39392 12539 19960 16602 3225 8105 - 30680 64909 9750 19292 15934 38070 9733 36553 61894 2864 25710 48492 - 54051 23592 44246 3991 38055 9064 51185 33976 39567 54282 59604 15986 - 12137 21928 16931 38440 25594 42613]`"] + new_user_pos["`combined position key:
[47033 63959 2503 19274 39057 42505 33376 18119 33917]`"] + new_user_prop["`property key:
[39652 18270 5028 34303 6339 45092 14652 39608 18788 7857 4800 6775 + 27243 640 51816 11487 16139 34341 38485 4249 44601 62920 30209 8343 + 19333 26092 3947 16867 32616 6792 36914 19857 1483 51567 23049 15969 + 48502 8758 26851 13558 39754 23725 1217 61491 64601 5714 32155 53045 + 11055 63186 5924 56720 60612 13766]`"] new_renew["renew: False"] end login --> new_user diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index e64fdd9..ef36513 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -136,7 +136,7 @@ block-beta ```mermaid block-beta columns 3 - pass["user_passcode_indices\n{{user_passcode_idxs}}"] + selected_keys["selected keys:\n{{selected_keys}}"] login_keypad["login keypad:\n{{login_keypad}}"] space:4 @@ -148,7 +148,7 @@ block-beta xor1(("XOR")) mask --> xor1 mask_key --> xor1 - pass --> selectkeys + selected_keys --> selectkeys login_keypad --> selectkeys space:3 -- 2.49.1 From f7d4e4411fb168201254f01401b2a007cbbe8c1c Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 09:15:40 -0500 Subject: [PATCH 67/85] update readme --- README.md | 16 +- docs/encipher_decipher_renew_nkode.md | 222 +++++++++--------- .../encipher_decipher_renew_nkode.template.md | 4 +- 3 files changed, 125 insertions(+), 117 deletions(-) diff --git a/README.md b/README.md index 2e4efd7..6b9e6b6 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,18 @@ jupyter notebook jupyter lab ``` +## Explore the Docs +1. [nKode Enrollment](docs/enrollment_diagram.md) +2. [nKode Login](docs/login_diagram.md) +3. [Encipher nKode](docs/encipher_decipher_renew_nkode.md#nkode-cipher) +4. [Valid nKode](docs/encipher_decipher_renew_nkode.md#validate-nkode) +5. [Renew nKode](docs/encipher_decipher_renew_nkode.md#renew-nkode) + ## Exploring the Tutorials 1. Navigate to the `/notebooks` directory in the Jupyter interface -2. Open the tutorials in the following recommended order: - - `Enrollment_Login_Renewal.ipynb` - Learn how to manage user accounts in nkode - - `Dispersion.ipynb` - Understand the basic concepts of dispersion in nkode - - `Split_Shuffle.ipynb` - Explore the split shuffle functionality +2. Recommended order: + - [Enrollment_Login_Renewal_Simplified.ipynb](notebooks/Enrollment_Login_Renewal_Simplified.ipynb) - Learn the basics of the nKode API + - [Enrollment_Login_Renewal_Detailed.ipynb](notebooks/Enrollment_Login_Renewal_Detailed.ipynb) - Learn the nKode API in detail + - [Dispersion.ipynb](notebooks/Dispersion.ipynb)- Understand the basic concepts of dispersion in nkode + - [Split_Shuffle.ipynb](notebooks/Split_Shuffle.ipynb) - Explore the split shuffle functionality diff --git a/docs/encipher_decipher_renew_nkode.md b/docs/encipher_decipher_renew_nkode.md index 04e3067..5a0400b 100644 --- a/docs/encipher_decipher_renew_nkode.md +++ b/docs/encipher_decipher_renew_nkode.md @@ -7,44 +7,44 @@ - total number of properties: 54 ## Customer Cipher -- property key: [12463 12679 30383 52780 48100 5096 61398 5750 59949 16574 25395 8158 - 51151 56863 52932 11578 50839 38005 12741 42227 40877 7172 53174 60400 - 12356 13971 19389 64980 16810 26389 39074 63833 12649 64422 4920 54703 - 62742 25787 26216 4453 38866 27951 36713 37903 22497 4454 55622 38346 - 31982 51225 48698 29699 36128 33748] -- position key: [56601 6465 12615 40545 49643 37783 59315 4335 13897] +- property key: [58201 3855 47017 50828 14104 4268 29079 19099 12271 55135 54341 2465 + 32959 3356 36643 50702 8094 11335 39310 38981 55881 6507 22508 16345 + 19379 55560 52385 29733 25178 37713 59696 16347 14811 20691 101 35545 + 48103 63525 408 28174 57763 21416 59610 976 40160 13681 17146 54023 + 25410 42165 3856 59580 27726 13822] +- position key: [59066 9448 56848 58798 57675 37475 8528 34148 34468] --- ## User Cipher -- property key: [14898 49243 46261 43286 743 60989 43535 48626 62301 50822 24597 10059 - 48516 48141 42915 36062 3286 11914 44159 34414 20879 14779 26916 49959 - 52279 29970 35884 62523 22453 25290 37000 30416 6102 20665 42719 23156 - 54785 3366 46811 57278 42429 58327 26021 27943 614 26543 63962 45468 - 15599 34845 22766 44215 32257 43746] -- passcode key: [ 4981 30039 31839 45969 48275 35799 10819 2121 61259 48949] -- combined position key: [58180 35354 39081 61964 32045 26428 24571 62221 62362] -- mask key: [ 3841 58027 55280 33679 39465 22075 10291 60287 27680 15384] +- property key: [61737 49256 8018 54927 21709 65009 3885 46808 12375 11644 35339 52787 + 60805 49964 2754 34451 22775 9278 14454 37226 46352 60033 49123 21025 + 6479 60885 9059 20442 64510 63443 6758 52715 61370 9158 50160 8810 + 27528 11812 34328 47745 43244 9125 55623 10800 49542 55251 26390 19027 + 23252 5772 30639 25220 21506 59558] +- passcode key: [54682 39190 21777 53204 58859 47387 24762 19698 44816 55464] +- combined position key: [52800 11537 463 51813 36017 64303 52526 57594 11887] +- mask key: [61564 54039 23310 1396 28615 39019 39047 46821 40711 32470] ### Combined Postion Key ```mermaid block-beta columns 2 - user_pos["user position key:\n[57725 11307 31071 28372 27611 49409 15121 51933 25996]"] - customer_pos["customer position key:\n[56601 6465 12615 40545 49643 37783 59315 4335 13897]"] + user_pos["user position key:\n[19938 59072 31217 46834 52213 218 38377 1271 54783]"] + customer_pos["customer position key:\n[59066 9448 56848 58798 57675 37475 8528 34148 34468]"] space:2 xor(("XOR")):2 user_pos --> xor customer_pos --> xor space:2 - comb_pos["combined position key\n[58180 35354 39081 61964 32045 26428 24571 62221 62362]"]:2 + comb_pos["combined position key\n[52800 11537 463 51813 36017 64303 52526 57594 11887]"]:2 xor --> comb_pos ``` ## User Keypad -- keypad example:
Key 0: [18 19 2 21 31 5 6 43 17]
Key 1: [ 9 46 29 30 49 32 42 52 35]
Key 2: [27 10 20 48 40 14 33 34 44]
Key 3: [45 28 47 12 4 23 51 25 26]
Key 4: [ 0 1 38 3 22 41 24 16 53]
Key 5: [36 37 11 39 13 50 15 7 8]
-- user passcode indices: [6, 43, 53, 9] +- keypad example:
Key 0: [27 19 38 30 31 41 6 7 26]
Key 1: [36 37 11 48 4 23 51 34 44]
Key 2: [45 1 20 12 49 50 15 52 53]
Key 3: [18 46 47 3 22 5 33 43 35]
Key 4: [ 9 28 29 21 13 14 42 16 17]
Key 5: [ 0 10 2 39 40 32 24 25 8]
+- user passcode indices: [30, 38, 11, 51] ## nKode Cipher @@ -52,54 +52,54 @@ block-beta ```mermaid block-beta columns 2 - cprop["customer_property_key\n[12463 12679 30383 52780 48100 5096 61398 5750 59949 16574 25395 8158 - 51151 56863 52932 11578 50839 38005 12741 42227 40877 7172 53174 60400 - 12356 13971 19389 64980 16810 26389 39074 63833 12649 64422 4920 54703 - 62742 25787 26216 4453 38866 27951 36713 37903 22497 4454 55622 38346 - 31982 51225 48698 29699 36128 33748]"] - uprop["user_property_key\n[14898 49243 46261 43286 743 60989 43535 48626 62301 50822 24597 10059 - 48516 48141 42915 36062 3286 11914 44159 34414 20879 14779 26916 49959 - 52279 29970 35884 62523 22453 25290 37000 30416 6102 20665 42719 23156 - 54785 3366 46811 57278 42429 58327 26021 27943 614 26543 63962 45468 - 15599 34845 22766 44215 32257 43746]"] + cprop["customer_property_key\n[58201 3855 47017 50828 14104 4268 29079 19099 12271 55135 54341 2465 + 32959 3356 36643 50702 8094 11335 39310 38981 55881 6507 22508 16345 + 19379 55560 52385 29733 25178 37713 59696 16347 14811 20691 101 35545 + 48103 63525 408 28174 57763 21416 59610 976 40160 13681 17146 54023 + 25410 42165 3856 59580 27726 13822]"] + uprop["user_property_key\n[61737 49256 8018 54927 21709 65009 3885 46808 12375 11644 35339 52787 + 60805 49964 2754 34451 22775 9278 14454 37226 46352 60033 49123 21025 + 6479 60885 9059 20442 64510 63443 6758 52715 61370 9158 50160 8810 + 27528 11812 34328 47745 43244 9125 55623 10800 49542 55251 26390 19027 + 23252 5772 30639 25220 21506 59558]"] space:2 xor1(("XOR")):2 cprop --> xor1 uprop --> xor1 space:2 - prop["combined_property_key\n[ 2598 13817 3743 55138 48519 14914 34131 3917 24848 29635 52388 57749 - 31090 24065 22959 61051 34121 30393 691 47015 26630 54959 13562 20862 - 31358 35382 39584 57405 53720 45712 49496 53667 35073 25844 36156 57792 - 30240 21451 23037 3839 61742 34083 35502 63962 25413 50263 31741 63116 - 30806 16519 51243 8712 32194 8269]"] + prop["combined_property_key\n[27145 64606 2214 956 59410 24299 43596 51759 39102 28353 20824 14874 + 27994 60897 60356 49474 41290 62135 52049 26214 37014 45529 43514 40298 + 25315 52116 23008 2744 3269 7285 34470 4311 21401 14127 50809 13541 + 5394 30989 34092 27076 35814 63505 33710 18123 25966 61467 19353 14805 + 58907 44436 41458 3440 7755 35403]"] xor1 --> prop - pass["user_passcode_indices\n[6, 43, 53, 9]"] + pass["user_passcode_indices\n[30, 38, 11, 51]"] space:2 sel(("select\nproperties")):2 pass --> sel prop --> sel space:2 - passcode["user passcode properties:\n[34131 63962 8269 29635]"]:2 + passcode["user passcode properties:\n[34470 34092 14874 3440]"]:2 sel --> passcode space:2 pad["zero pad to\nmax nkode length: 10"]:2 passcode -->pad space:2 - paddedpasscode["padded passcode:\n[34131 63962 8269 29635 0 0 0 0 0 0]"] + paddedpasscode["padded passcode:\n[34470 34092 14874 3440 0 0 0 0 0 0]"] pad --> paddedpasscode - passkey["passcode key:\n[ 4981 30039 31839 45969 48275 35799 10819 2121 61259 48949]"] + passkey["passcode key:\n[54682 39190 21777 53204 58859 47387 24762 19698 44816 55464]"] space:2 xor2(("XOR")):2 passkey --> xor2 paddedpasscode --> xor2 space:2 - cipheredpass["ciphered passcode:\n[38438 35981 23570 49234 48275 35799 10819 2121 61259 48949]"]:2 + cipheredpass["ciphered passcode:\n[21308 7226 28427 49828 58859 47387 24762 19698 44816 55464]"]:2 xor2 --> cipheredpass space:2 hash(("hash")):2 cipheredpass --> hash space:2 - cipheredhashed["hashed ciphered passcode:\n$2b$12$rF1TSRHSHfOTE4V/7ZCv3.Plqb1AysifW1KvR96K7CbC1sPt1ungK"]:2 + cipheredhashed["hashed ciphered passcode:\n$2b$12$uR5ilcM4r.xjbzW4kGWf2.tbGVWSwrnqp1s6sGzqUmgcE1ncQK8UW"]:2 hash --> cipheredhashed ``` @@ -107,9 +107,9 @@ block-beta ```mermaid block-beta columns 3 - passcode_idx["passcode indices:\n[6, 43, 53, 9]"] - comb_pos["combined position key:\n[58180 35354 39081 61964 32045 26428 24571 62221 62362]"] - cust_pos["customer position key:\n[56601 6465 12615 40545 49643 37783 59315 4335 13897]"] + passcode_idx["passcode indices:\n[30, 38, 11, 51]"] + comb_pos["combined position key:\n[52800 11537 463 51813 36017 64303 52526 57594 11887]"] + cust_pos["customer position key:\n[59066 9448 56848 58798 57675 37475 8528 34148 34468]"] space:3 propidx(["Get Position Idx:\nmap each to element mod props_per_key"]) @@ -120,7 +120,7 @@ block-beta cust_pos --> xor1 space:3 - passcode_position_idx["passcode poition indices:\n[6, 7, 8, 0]"] + passcode_position_idx["passcode poition indices:\n[3, 2, 2, 6]"] propidx --> passcode_position_idx space:5 @@ -128,10 +128,10 @@ block-beta passcode_position_idx --> pad1 space:5 - posidx["Padded Passcode Position Indices:\n[6, 7, 8, 0, 1, 1, 2, 3, 0, 5]"] + posidx["Padded Passcode Position Indices:\n[3, 2, 2, 6, 1, 2, 3, 6, 8, 5]"] pad1 --> posidx space:1 - user_pos["user position key:\n[57725 11307 31071 28372 27611 49409 15121 51933 25996]"] + user_pos["user position key:\n[19938 59072 31217 46834 52213 218 38377 1271 54783]"] xor1 --> user_pos space:4 @@ -139,15 +139,15 @@ block-beta user_pos --> sel posidx --> sel space:5 - passcode_pos["ordered user passcode positions:\n[15121 51933 25996 57725 11307 11307 31071 28372 57725 49409]"] + passcode_pos["ordered user passcode positions:\n[46834 31217 31217 38377 59072 31217 46834 38377 54783 218]"] sel --> passcode_pos - mask_key["mask key\n[ 3841 58027 55280 33679 39465 22075 10291 60287 27680 15384]"] + mask_key["mask key\n[61564 54039 23310 1396 28615 39019 39047 46821 40711 32470]"] space:4 xor2(("XOR")) mask_key --> xor2 passcode_pos --> xor2 space:5 - mask["enciphered mask:\n [13328 10358 45692 25330 46594 31248 20844 34219 36189 64793]"] + mask["enciphered mask:\n [18062 43750 8959 37021 35079 57754 11893 8972 19192 32268]"] xor2 --> mask ``` @@ -156,19 +156,19 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n[0, 0, 4, 1]"] - login_keypad["login keypad:\nKey 0: [18 19 2 21 31 5 6 43 17] -Key 1: [ 9 46 29 30 49 32 42 52 35] -Key 2: [27 10 20 48 40 14 33 34 44] -Key 3: [45 28 47 12 4 23 51 25 26] -Key 4: [ 0 1 38 3 22 41 24 16 53] -Key 5: [36 37 11 39 13 50 15 7 8] + selected_keys["keys selected by user during login:\n[0, 0, 1, 1]"] + login_keypad["login keypad:\nKey 0: [27 19 38 30 31 41 6 7 26] +Key 1: [36 37 11 48 4 23 51 34 44] +Key 2: [45 1 20 12 49 50 15 52 53] +Key 3: [18 46 47 3 22 5 33 43 35] +Key 4: [ 9 28 29 21 13 14 42 16 17] +Key 5: [ 0 10 2 39 40 32 24 25 8] "] space:4 - selectkeys(("select keys")) - mask["enciphered mask:\n [13328 10358 45692 25330 46594 31248 20844 34219 36189 64793]"] - mask_key["mask key:\n[ 3841 58027 55280 33679 39465 22075 10291 60287 27680 15384]"] + selectkeys(("filter keys")) + mask["enciphered mask:\n [18062 43750 8959 37021 35079 57754 11893 8972 19192 32268]"] + mask_key["mask key:\n[61564 54039 23310 1396 28615 39019 39047 46821 40711 32470]"] space:2 xor1(("XOR")) @@ -178,12 +178,12 @@ Key 5: [36 37 11 39 13 50 15 7 8] login_keypad --> selectkeys space:3 - ordered_keys["ordered keys:\n[[18 19 2 21 31 5 6 43 17] - [18 19 2 21 31 5 6 43 17] - [ 0 1 38 3 22 41 24 16 53] - [ 9 46 29 30 49 32 42 52 35]]"] - user_position_key["user position key:\n[57725 11307 31071 28372 27611 49409 15121 51933 25996]"] - passcode_pos["ordered user passcode positions:\n[15121 51933 25996 57725 11307 11307 31071 28372 57725 49409]"] + ordered_keys["ordered keys:\n[[27 19 38 30 31 41 6 7 26] + [27 19 38 30 31 41 6 7 26] + [36 37 11 48 4 23 51 34 44] + [36 37 11 48 4 23 51 34 44]]"] + user_position_key["user position key:\n[19938 59072 31217 46834 52213 218 38377 1271 54783]"] + passcode_pos["ordered user passcode positions:\n[46834 31217 31217 38377 59072 31217 46834 38377 54783 218]"] selectkeys --> ordered_keys xor1 --> passcode_pos space:8 @@ -193,7 +193,7 @@ Key 5: [36 37 11 39 13 50 15 7 8] passcode_pos --> get_passcode_idxs space:8 - passcode_pos_idxs["padded passcode position indices:\n[6, 7, 8, 0, 1, 1, 2, 3, 0, 5]"] + passcode_pos_idxs["padded passcode position indices:\n[3, 2, 2, 6, 1, 2, 3, 6, 8, 5]"] get_passcode_idxs --> passcode_pos_idxs space:3 @@ -202,13 +202,13 @@ Key 5: [36 37 11 39 13 50 15 7 8] passcode_pos_idxs --> get_presumed_idxs space:5 - passcode_prop_idxs["presumed passcode property indices:\n[6, 43, 53, 9]"] - prop["combined_property_key\n[ 2598 13817 3743 55138 48519 14914 34131 3917 24848 29635 52388 57749 - 31090 24065 22959 61051 34121 30393 691 47015 26630 54959 13562 20862 - 31358 35382 39584 57405 53720 45712 49496 53667 35073 25844 36156 57792 - 30240 21451 23037 3839 61742 34083 35502 63962 25413 50263 31741 63116 - 30806 16519 51243 8712 32194 8269]"] - cipheredhashed["hashed ciphered passcode:\n$2b$12$rF1TSRHSHfOTE4V/7ZCv3.Plqb1AysifW1KvR96K7CbC1sPt1ungK"] + passcode_prop_idxs["presumed passcode property indices:\n[30, 38, 11, 51]"] + prop["combined_property_key\n[27145 64606 2214 956 59410 24299 43596 51759 39102 28353 20824 14874 + 27994 60897 60356 49474 41290 62135 52049 26214 37014 45529 43514 40298 + 25315 52116 23008 2744 3269 7285 34470 4311 21401 14127 50809 13541 + 5394 30989 34092 27076 35814 63505 33710 18123 25966 61467 19353 14805 + 58907 44436 41458 3440 7755 35403]"] + cipheredhashed["hashed ciphered passcode:\n$2b$12$uR5ilcM4r.xjbzW4kGWf2.tbGVWSwrnqp1s6sGzqUmgcE1ncQK8UW"] get_presumed_idxs --> passcode_prop_idxs space:3 @@ -217,7 +217,7 @@ Key 5: [36 37 11 39 13 50 15 7 8] prop --> sel space:5 - passcode_prop["presumed passcode properties:\n[34131 63962 8269 29635]"] + passcode_prop["presumed passcode properties:\n[34470 34092 14874 3440]"] sel --> passcode_prop space:5 @@ -225,7 +225,7 @@ Key 5: [36 37 11 39 13 50 15 7 8] passcode_prop --> cipher space:5 - cipheredpass["ciphered passcode:\n[38438 35981 23570 49234 48275 35799 10819 2121 61259 48949]"] + cipheredpass["ciphered passcode:\n[21308 7226 28427 49828 58859 47387 24762 19698 44816 55464]"] cipher --> cipheredpass space:7 @@ -250,26 +250,26 @@ nKode renewal is a three step process: ```mermaid flowchart subgraph Renew Customer Keys - old_prop["`old customer property key:
[12308 62882 47658 32372 48992 54399 12124 45759 37453 46405 44209 50910 - 50422 57868 65036 25253 35231 22579 44748 12745 14729 61204 24030 37465 - 46665 65316 5772 5126 34413 53338 20944 42867 40663 13389 11235 48052 - 40993 24301 61222 53569 21651 26356 61195 38141 24867 41976 33319 18192 - 17593 51354 37061 36543 963 35503]`"] - new_prop["`new customer property key:
[12463 12679 30383 52780 48100 5096 61398 5750 59949 16574 25395 8158 - 51151 56863 52932 11578 50839 38005 12741 42227 40877 7172 53174 60400 - 12356 13971 19389 64980 16810 26389 39074 63833 12649 64422 4920 54703 - 62742 25787 26216 4453 38866 27951 36713 37903 22497 4454 55622 38346 - 31982 51225 48698 29699 36128 33748]`"] - old_pos["`old customer position key:
[ 569 42545 57846 40152 5878 42557 25834 14800 38422]`"] - new_pos["`new customer position key:
[56601 6465 12615 40545 49643 37783 59315 4335 13897]`"] + old_prop["`old customer property key:
[39712 15414 6132 54579 48351 41754 42337 31991 43241 17341 56147 62505 + 32991 11981 57606 18385 63933 54921 62247 63244 9606 23384 5657 53067 + 31660 9793 31363 17762 63291 60326 40128 56636 48163 5353 1417 5775 + 32410 22313 820 54085 8970 56244 23273 27899 42216 10184 11407 29574 + 48335 47896 54877 28660 19017 25325]`"] + new_prop["`new customer property key:
[58201 3855 47017 50828 14104 4268 29079 19099 12271 55135 54341 2465 + 32959 3356 36643 50702 8094 11335 39310 38981 55881 6507 22508 16345 + 19379 55560 52385 29733 25178 37713 59696 16347 14811 20691 101 35545 + 48103 63525 408 28174 57763 21416 59610 976 40160 13681 17146 54023 + 25410 42165 3856 59580 27726 13822]`"] + old_pos["`old customer position key:
[33698 52177 30782 31895 18244 64501 22727 58381 64400]`"] + new_pos["`new customer position key:
[59066 9448 56848 58798 57675 37475 8528 34148 34468]`"] xor1(("XOR")) xor2(("XOR")) - xor_prop["`xor property key:
[ 187 50213 52357 45144 1156 51095 49290 42185 30816 62971 53122 55552 - 825 15379 12488 20383 20232 52294 40713 38202 42532 62224 37480 31145 - 34317 51639 23857 59858 51143 46927 51570 24106 44990 53227 14555 28187 - 21815 14934 35150 49188 49985 3035 24674 242 14018 45726 23393 53978 - 14423 131 12031 64188 36579 2427]`"] - xor_pos["`xor position key:
[57120 49008 53425 697 55069 13738 33625 10559 41055]`"] + xor_prop["`xor property key:
[30841 13113 41053 5055 35783 46006 54518 13932 34566 38114 3862 64904 + 96 9169 28197 33247 58915 64206 27305 28489 65487 16947 16885 61586 + 12319 65353 46626 12615 38241 30967 30192 58087 34296 17466 1516 40022 + 50557 44812 684 48459 49833 34844 45619 28459 14344 4793 28277 41089 + 57229 8109 55629 34632 9735 22291]`"] + xor_pos["`xor position key:
[25880 61241 42542 39225 42511 27030 31127 24937 32052]`"] old_prop --> xor1 new_prop --> xor1 xor1 --> xor_prop @@ -283,12 +283,12 @@ nKode renewal is a three step process: users --> eachuser subgraph eachuser [for each user] subgraph old user keys - old_user_pos["`combined position key:
[57725 11307 31071 28372 27611 49409 15121 51933 25996]`"] - old_user_prop["`property key:
[14898 49243 46261 43286 743 60989 43535 48626 62301 50822 24597 10059 - 48516 48141 42915 36062 3286 11914 44159 34414 20879 14779 26916 49959 - 52279 29970 35884 62523 22453 25290 37000 30416 6102 20665 42719 23156 - 54785 3366 46811 57278 42429 58327 26021 27943 614 26543 63962 45468 - 15599 34845 22766 44215 32257 43746]`"] + old_user_pos["`combined position key:
[19938 59072 31217 46834 52213 218 38377 1271 54783]`"] + old_user_prop["`property key:
[61737 49256 8018 54927 21709 65009 3885 46808 12375 11644 35339 52787 + 60805 49964 2754 34451 22775 9278 14454 37226 46352 60033 49123 21025 + 6479 60885 9059 20442 64510 63443 6758 52715 61370 9158 50160 8810 + 27528 11812 34328 47745 43244 9125 55623 10800 49542 55251 26390 19027 + 23252 5772 30639 25220 21506 59558]`"] old_renew["renew: False"] end xor3(("XOR")) @@ -300,12 +300,12 @@ nKode renewal is a three step process: xor_prop --> xor4 xor4 --> inter_user_prop subgraph inter_user[intermediate user keys] - inter_user_pos["`combined position key:
[15460 13674 18456 61621 43568 21142 56482 55858 21445]`"] - inter_user_prop["`property key:
[14985 1150 30768 6478 1635 10666 27269 6459 35645 13181 44951 65099 - 48829 32798 38763 49985 17374 58060 13174 4948 63403 51883 64332 47758 - 19002 48293 53533 7657 36978 54661 23034 10490 47208 40786 40452 13423 - 33590 14192 16277 8090 26364 59404 1479 28117 13476 54577 41659 25414 - 1208 34974 30225 22027 61666 41881]`"] + inter_user_pos["`combined position key:
[43864 49704 42977 21340 10942 37561 46265 33171 21339]`"] + inter_user_prop["`property key:
[35152 62289 48911 50480 57098 20039 56283 32948 46929 47518 34077 13243 + 60901 57597 25831 1868 48852 57072 21215 65059 19167 43186 65046 41651 + 10576 4764 38209 32413 28319 36644 28566 12044 27202 26620 50716 48700 + 44789 33064 33972 1994 27205 43961 27508 17691 63886 50538 2403 60114 + 34137 2337 44770 58828 29189 49077]`"] inter_renew["renew: True"] end end @@ -316,12 +316,12 @@ nKode renewal is a three step process: login["First login post renew"] inter_user --> login subgraph new_user [New User Keys] - new_user_pos["`combined position key:
[47033 63959 2503 19274 39057 42505 33376 18119 33917]`"] - new_user_prop["`property key:
[39652 18270 5028 34303 6339 45092 14652 39608 18788 7857 4800 6775 - 27243 640 51816 11487 16139 34341 38485 4249 44601 62920 30209 8343 - 19333 26092 3947 16867 32616 6792 36914 19857 1483 51567 23049 15969 - 48502 8758 26851 13558 39754 23725 1217 61491 64601 5714 32155 53045 - 11055 63186 5924 56720 60612 13766]`"] + new_user_pos["`combined position key:
[ 6533 37152 58679 48102 36450 2846 47432 25286 7670]`"] + new_user_prop["`property key:
[46243 7833 58428 7967 9894 32124 48453 54560 26021 20475 10880 7429 + 26439 31401 37980 56964 32338 15187 9442 19302 53060 31276 34465 43143 + 33088 8587 47204 47532 45177 35842 11142 47055 40284 49647 9306 58542 + 63251 40940 32915 53183 16853 43890 60339 30610 6078 59395 7618 59221 + 44934 37153 10149 59493 8228 59563]`"] new_renew["renew: False"] end login --> new_user diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index ef36513..57cd368 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -136,11 +136,11 @@ block-beta ```mermaid block-beta columns 3 - selected_keys["selected keys:\n{{selected_keys}}"] + selected_keys["keys selected by user during login:\n{{selected_keys}}"] login_keypad["login keypad:\n{{login_keypad}}"] space:4 - selectkeys(("select keys")) + selectkeys(("filter keys")) mask["enciphered mask:\n {{mask}}"] mask_key["mask key:\n{{mask_key}}"] space:2 -- 2.49.1 From daed7fad6f532ba0100bc0aba0ec806ef2bbd6c1 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 26 Mar 2025 10:28:42 -0500 Subject: [PATCH 68/85] update readme --- README.md | 27 ++++++++++--------- .../encipher_decipher_renew_nkode.template.md | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 6b9e6b6..6e7fb39 100644 --- a/README.md +++ b/README.md @@ -53,17 +53,20 @@ jupyter lab ``` ## Explore the Docs -1. [nKode Enrollment](docs/enrollment_diagram.md) -2. [nKode Login](docs/login_diagram.md) -3. [Encipher nKode](docs/encipher_decipher_renew_nkode.md#nkode-cipher) -4. [Valid nKode](docs/encipher_decipher_renew_nkode.md#validate-nkode) -5. [Renew nKode](docs/encipher_decipher_renew_nkode.md#renew-nkode) +You can find documentation in the [docs](docs) folder. -## Exploring the Tutorials +1. [Enrollment](docs/enrollment_diagram.md) +2. [Login](docs/login_diagram.md) +3. [nKode Cipher](docs/encipher_decipher_renew_nkode.md#nkode-cipher) +4. [nKode Validation](docs/encipher_decipher_renew_nkode.md#validate-nkode) +5. [Renew](docs/encipher_decipher_renew_nkode.md#renew-nkode) -1. Navigate to the `/notebooks` directory in the Jupyter interface -2. Recommended order: - - [Enrollment_Login_Renewal_Simplified.ipynb](notebooks/Enrollment_Login_Renewal_Simplified.ipynb) - Learn the basics of the nKode API - - [Enrollment_Login_Renewal_Detailed.ipynb](notebooks/Enrollment_Login_Renewal_Detailed.ipynb) - Learn the nKode API in detail - - [Dispersion.ipynb](notebooks/Dispersion.ipynb)- Understand the basic concepts of dispersion in nkode - - [Split_Shuffle.ipynb](notebooks/Split_Shuffle.ipynb) - Explore the split shuffle functionality + +### Tutorials +You can find tutorials in the [notebooks](notebooks) directory. + +Recommended order: +1. [Enrollment_Login_Renewal_Simplified.ipynb](notebooks/Enrollment_Login_Renewal_Simplified.ipynb) - Learn the basics of the nKode API +2. [Enrollment_Login_Renewal_Detailed.ipynb](notebooks/Enrollment_Login_Renewal_Detailed.ipynb) - Learn the nKode API in detail +3. [Dispersion.ipynb](notebooks/Dispersion.ipynb)- Understand the basic concepts of dispersion in nkode +4. [Split_Shuffle.ipynb](notebooks/Split_Shuffle.ipynb) - Explore the split shuffle functionality diff --git a/docs/templates/encipher_decipher_renew_nkode.template.md b/docs/templates/encipher_decipher_renew_nkode.template.md index 57cd368..6831a92 100644 --- a/docs/templates/encipher_decipher_renew_nkode.template.md +++ b/docs/templates/encipher_decipher_renew_nkode.template.md @@ -1,4 +1,4 @@ -# Encipher and Decipher nKode +# Encipher, Decipher and Renew nKode ## Customer Policy - max nkode length: {{ max_nkode_len }} -- 2.49.1 From 843563fb0cf0cb632ff75a01868c7b863bb26a7e Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 27 Mar 2025 10:38:14 -0500 Subject: [PATCH 69/85] update dipersion attack; add tabletop-discussion.md --- docs/templates/dispersion_attack.template.md | 22 +++++++++++++++ tabletop-discussion.md | 29 ++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 docs/templates/dispersion_attack.template.md create mode 100644 tabletop-discussion.md diff --git a/docs/templates/dispersion_attack.template.md b/docs/templates/dispersion_attack.template.md new file mode 100644 index 0000000..a0efb33 --- /dev/null +++ b/docs/templates/dispersion_attack.template.md @@ -0,0 +1,22 @@ +# Dispersion Attack + +```mermaid +sequenceDiagram + participant User + participant Threat Actor + participant nKode Server + Threat Actor ->> Threat Actor: Get User's email/keypad icons + Threat Actor ->> User: Send Phishing Email + Note left of User: Recieves Email:
Subject: Urgent
Body: Click the link to verify your account https://www.nkod3.tech + User ->> Threat Actor: Clicks link + Threat Actor -->> User: Fake nKode Login Page + Note left of User: Login Page
{{nkode_keypad1}} + User ->>+ Threat Actor: key selection {{key_selection1}} + Threat Actor ->> Threat Actor: Disperse Keypad + Threat Actor -->>- User: Invalid nKode Try Again + Note left of User: Login Page
{{nkode_keypad2}} + User ->> Threat Actor: key selection {{key_selection2}} + Threat Actor ->> Threat Actor: Deduce the user's nKode + Threat Actor ->> nKode Server: {{user_passcode}} + nKode Server -->> Threat Actor: Success +``` \ No newline at end of file diff --git a/tabletop-discussion.md b/tabletop-discussion.md new file mode 100644 index 0000000..39b590c --- /dev/null +++ b/tabletop-discussion.md @@ -0,0 +1,29 @@ +# Table-top discussion + + +## Documentation and Tutorials +1. [Enrollment](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/docs/enrollment_diagram.md) +2. [Login](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/docs/login_diagram.md) +3. [Cipher and Renew](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/docs/encipher_decipher_renew_nkode.md) +4. [nKode API Tutorial 1](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Enrollment_Login_Renewal_Simplified.ipynb) +5. [nKode API Tutorial 2](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Enrollment_Login_Renewal_Detailed.ipynb) +6. [Dispersion Tutorial](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Dispersion.ipynb) +7. [Split Shuffle](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Split_Shuffle.ipynb) + +## Discussion Topics +### nKode Length +[Memorized Secret](https://pages.nist.gov/800-63-3/sp800-63b.html#memsecret) `Memorized secrets SHALL be at least 8 characters in length if chosen by the subscriber. Memorized secrets chosen randomly by the CSP or verifier SHALL be at least 6 characters in length and MAY be entirely numeric.` +- The minimum entropy for a randomly chosen memorized secret is approximately 20 bits. +- A keypad with 6 keys, each having 9 properties, exceeds this requirement with a minimum 4-character nKode, providing approximately 23 bits of entropy. + +### nKode Observation +- Cracking an nKode [Evil nKode](https://git.infra.nkode.tech/dkelly/evilkode) +- Replay Attack + +### Dispersion Attack + +### nKode Over low-bandwidth + +### nKode Over Unencrypted Channel +- TOTP +- DARC \ No newline at end of file -- 2.49.1 From 242d23fe657301d676982abeab6c5b613113c691 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 27 Mar 2025 17:29:10 -0500 Subject: [PATCH 70/85] add asks and dr kandah --- .../Enrollment_Login_Renewal_Detailed.ipynb | 258 +++++++++--------- .../Enrollment_Login_Renewal_Simplified.ipynb | 48 ++-- tabletop-discussion.md | 46 +++- 3 files changed, 204 insertions(+), 148 deletions(-) diff --git a/notebooks/Enrollment_Login_Renewal_Detailed.ipynb b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb index 78811da..6fad737 100644 --- a/notebooks/Enrollment_Login_Renewal_Detailed.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Detailed.ipynb @@ -29,8 +29,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T20:25:08.637548Z", - "start_time": "2025-03-24T20:25:08.607886Z" + "end_time": "2025-03-27T19:17:57.439685Z", + "start_time": "2025-03-27T19:17:57.405237Z" } }, "outputs": [], @@ -39,8 +39,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:08.642553Z", - "start_time": "2025-03-24T20:25:08.640602Z" + "end_time": "2025-03-27T19:17:57.446190Z", + "start_time": "2025-03-27T19:17:57.443952Z" } }, "cell_type": "code", @@ -83,8 +83,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:08.679087Z", - "start_time": "2025-03-24T20:25:08.667792Z" + "end_time": "2025-03-27T19:17:57.487136Z", + "start_time": "2025-03-27T19:17:57.475079Z" } }, "cell_type": "code", @@ -116,20 +116,20 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Position Key: [36587 51243 16045 24580 51231 48943]\n", + "Customer Position Key: [40442 29276 29073 55607 14230 47593]\n", "Customer Properties Key:\n", - "[23910 10306 19502 5449 54702 12273]\n", - "[53013 18581 4421 45433 39661 27006]\n", - "[16680 54596 31667 35220 1865 8499]\n", - "[37220 26796 20234 3387 44239 47346]\n", - "[55497 7967 5622 1002 13135 4901]\n", + "[21190 30210 9541 19864 15205 49310]\n", + "[55331 19035 8032 36826 40376 8457]\n", + "[47892 59102 4159 3691 31648 60302]\n", + "[37326 6094 58132 24712 36587 17695]\n", + "[62108 52530 63411 22211 34115 22936]\n", "Position to Properties Map:\n", - "36587: [23910 53013 16680 37220 55497]\n", - "51243: [10306 18581 54596 26796 7967]\n", - "16045: [19502 4421 31667 20234 5622]\n", - "24580: [ 5449 45433 35220 3387 1002]\n", - "51231: [54702 39661 1865 44239 13135]\n", - "48943: [12273 27006 8499 47346 4901]\n" + "40442: [21190 55331 47892 37326 62108]\n", + "29276: [30210 19035 59102 6094 52530]\n", + "29073: [ 9541 8032 4159 58132 63411]\n", + "55607: [19864 36826 3691 24712 22211]\n", + "14230: [15205 40376 31648 36587 34115]\n", + "47593: [49310 8457 60302 17695 22936]\n" ] } ], @@ -138,8 +138,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:08.687137Z", - "start_time": "2025-03-24T20:25:08.684437Z" + "end_time": "2025-03-27T19:17:57.507904Z", + "start_time": "2025-03-27T19:17:57.505187Z" } }, "cell_type": "code", @@ -156,12 +156,12 @@ "output_type": "stream", "text": [ "Position Value to Icons Map:\n", - "36587: ['😀' '🥺' '🤔' '🐱' '🦄']\n", - "51243: ['😂' '😡' '🙃' '🐶' '🌟']\n", - "16045: ['🥳' '😱' '😇' '🦁' '⚡']\n", - "24580: ['😍' '🤯' '🤖' '🐻' '🔥']\n", - "51231: ['🤓' '🥰' '👽' '🐸' '🍕']\n", - "48943: ['😎' '😴' '👾' '🐙' '🎉']\n" + "40442: ['😀' '🥺' '🤔' '🐱' '🦄']\n", + "29276: ['😂' '😡' '🙃' '🐶' '🌟']\n", + "29073: ['🥳' '😱' '😇' '🦁' '⚡']\n", + "55607: ['😍' '🤯' '🤖' '🐻' '🔥']\n", + "14230: ['🤓' '🥰' '👽' '🐸' '🍕']\n", + "47593: ['😎' '😴' '👾' '🐙' '🎉']\n" ] } ], @@ -189,8 +189,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:08.705881Z", - "start_time": "2025-03-24T20:25:08.699280Z" + "end_time": "2025-03-27T19:17:57.541997Z", + "start_time": "2025-03-27T19:17:57.534379Z" } }, "cell_type": "code", @@ -219,11 +219,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: ['🥺' '😂' '😱' '🔥' '👽']\n", - "Key 1: ['🐱' '🙃' '😇' '🐻' '🐸']\n", - "Key 2: ['😀' '🌟' '🥳' '🤖' '🤓']\n", - "Key 3: ['🦄' '🐶' '🦁' '🤯' '🥰']\n", - "Key 4: ['🤔' '😡' '⚡' '😍' '🍕']\n" + "Key 0: ['🙃' '😱' '🔥' '🍕' '🎉']\n", + "Key 1: ['🐶' '🦁' '🐻' '🤓' '🐙']\n", + "Key 2: ['🌟' '😇' '😍' '👽' '👾']\n", + "Key 3: ['😡' '⚡' '🤯' '🐸' '😴']\n", + "Key 4: ['😂' '🥳' '🤖' '🥰' '😎']\n" ] }, { @@ -240,11 +240,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [ 6 1 8 27 16]\n", - "Key 1: [18 13 14 21 22]\n", - "Key 2: [ 0 25 2 15 4]\n", - "Key 3: [24 19 20 9 10]\n", - "Key 4: [12 7 26 3 28]\n" + "Key 0: [13 8 27 28 29]\n", + "Key 1: [19 20 21 4 23]\n", + "Key 2: [25 14 3 16 17]\n", + "Key 3: [ 7 26 9 22 11]\n", + "Key 4: [ 1 2 15 10 5]\n" ] }, { @@ -261,11 +261,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [53013 10306 4421 1002 1865]\n", - "Key 1: [37220 54596 31667 3387 44239]\n", - "Key 2: [23910 7967 19502 35220 54702]\n", - "Key 3: [55497 26796 20234 45433 39661]\n", - "Key 4: [16680 18581 5622 5449 13135]\n" + "Key 0: [59102 8032 22211 34115 22936]\n", + "Key 1: [ 6094 58132 24712 15205 17695]\n", + "Key 2: [52530 4159 19864 31648 60302]\n", + "Key 3: [19035 63411 36826 36587 8457]\n", + "Key 4: [30210 9541 3691 40376 49310]\n" ] } ], @@ -282,8 +282,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:08.729881Z", - "start_time": "2025-03-24T20:25:08.726837Z" + "end_time": "2025-03-27T19:17:57.582109Z", + "start_time": "2025-03-27T19:17:57.578783Z" } }, "cell_type": "code", @@ -301,10 +301,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Passcode Indices: [22, 8, 14, 12]\n", - "User Passcode Icons: ['🐸' '😱' '😇' '🤔']\n", - "User Passcode Server-side properties: [44239 4421 31667 16680]\n", - "Selected Keys: [1, 0, 1, 4]\n" + "User Passcode Indices: [7, 13, 27, 26]\n", + "User Passcode Icons: ['😡' '🙃' '🔥' '⚡']\n", + "User Passcode Server-side properties: [19035 59102 22211 63411]\n", + "Selected Keys: [3, 0, 0, 3]\n" ] } ], @@ -321,8 +321,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.048762Z", - "start_time": "2025-03-24T20:25:08.811165Z" + "end_time": "2025-03-27T19:17:57.846195Z", + "start_time": "2025-03-27T19:17:57.599899Z" } }, "cell_type": "code", @@ -339,13 +339,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [24 25 26 27 22]\n", - "Key 1: [18 19 2 3 16]\n", - "Key 2: [ 6 13 20 15 28]\n", - "Key 3: [12 1 14 9 4]\n", - "Key 4: [ 0 7 8 21 10]\n", + "Key 0: [25 2 27 4 11]\n", + "Key 1: [19 14 15 22 29]\n", + "Key 2: [ 7 20 3 28 5]\n", + "Key 3: [13 26 21 10 17]\n", + "Key 4: [ 1 8 9 16 23]\n", "Selected Keys\n", - "[0, 4, 3, 3]\n" + "[2, 3, 0, 3]\n" ] } ], @@ -359,8 +359,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.058216Z", - "start_time": "2025-03-24T20:25:09.055315Z" + "end_time": "2025-03-27T19:17:57.858315Z", + "start_time": "2025-03-27T19:17:57.855013Z" } }, "cell_type": "code", @@ -379,18 +379,18 @@ "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" + "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" ] } ], @@ -414,8 +414,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.069308Z", - "start_time": "2025-03-24T20:25:09.064490Z" + "end_time": "2025-03-27T19:17:57.879460Z", + "start_time": "2025-03-27T19:17:57.873816Z" } }, "cell_type": "code", @@ -430,8 +430,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.087623Z", - "start_time": "2025-03-24T20:25:09.085495Z" + "end_time": "2025-03-27T19:17:57.904749Z", + "start_time": "2025-03-27T19:17:57.902224Z" } }, "cell_type": "code", @@ -442,11 +442,11 @@ "output_type": "stream", "text": [ "Property Key:\n", - "[[ 7202 17463 46638 52425 1136 48374]\n", - " [13320 30423 16460 16440 54741 60051]\n", - " [ 7080 35309 40115 5709 22652 59355]\n", - " [62863 16450 3293 2809 14186 52151]\n", - " [49175 8694 16139 52942 5446 1365]]\n" + "[[14162 15278 5779 57356 9084 42916]\n", + " [62550 57245 40145 52844 62606 28882]\n", + " [16963 51204 54863 4741 39274 22253]\n", + " [45416 46785 54545 12343 60959 30982]\n", + " [23400 12253 26764 20884 19959 11955]]\n" ] } ], @@ -455,8 +455,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.115003Z", - "start_time": "2025-03-24T20:25:09.112867Z" + "end_time": "2025-03-27T19:17:57.951642Z", + "start_time": "2025-03-27T19:17:57.949347Z" } }, "cell_type": "code", @@ -466,7 +466,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Key: [64689 33923 20489 20542 33540 51906 6128 40137 14040 24585]\n" + "Passcode Key: [55447 46315 12524 17763 63442 8950 24638 56921 18442 22528]\n" ] } ], @@ -475,8 +475,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.138929Z", - "start_time": "2025-03-24T20:25:09.136632Z" + "end_time": "2025-03-27T19:17:57.989194Z", + "start_time": "2025-03-27T19:17:57.986687Z" } }, "cell_type": "code", @@ -486,7 +486,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Mask Key: [57275 32944 34918 33126 19845 13409 47088 47492 20658 16069]\n" + "Mask Key: [37865 47229 1360 12907 33808 18226 62137 23444 5922 45367]\n" ] } ], @@ -495,8 +495,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.171323Z", - "start_time": "2025-03-24T20:25:09.169122Z" + "end_time": "2025-03-27T19:17:58.019886Z", + "start_time": "2025-03-27T19:17:58.017350Z" } }, "cell_type": "code", @@ -506,7 +506,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Combined Position Key: [ 6982 56074 5098 60427 26358 45400]\n" + "Combined Position Key: [48833 17040 25020 22197 22334 15077]\n" ] } ], @@ -515,8 +515,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.192115Z", - "start_time": "2025-03-24T20:25:09.189931Z" + "end_time": "2025-03-27T19:17:58.050384Z", + "start_time": "2025-03-27T19:17:58.047868Z" } }, "cell_type": "code", @@ -526,7 +526,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "User Position Key = combined_pos_key XOR customer_pos_key: [38317 4897 11591 35855 44777 3703]\n" + "User Position Key = combined_pos_key XOR customer_pos_key: [ 9019 12492 4141 36738 24744 33548]\n" ] } ], @@ -535,8 +535,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.213042Z", - "start_time": "2025-03-24T20:25:09.210890Z" + "end_time": "2025-03-27T19:17:58.138360Z", + "start_time": "2025-03-27T19:17:58.135782Z" } }, "cell_type": "code", @@ -552,12 +552,12 @@ "output_type": "stream", "text": [ "Combined Position to Properties Map:\n", - "6982: [ 7202 13320 7080 62863 49175]\n", - "56074: [17463 30423 35309 16450 8694]\n", - "5098: [46638 16460 40115 3293 16139]\n", - "60427: [52425 16440 5709 2809 52942]\n", - "26358: [ 1136 54741 22652 14186 5446]\n", - "45400: [48374 60051 59355 52151 1365]\n" + "48833: [14162 62550 16963 45416 23400]\n", + "17040: [15278 57245 51204 46785 12253]\n", + "25020: [ 5779 40145 54863 54545 26764]\n", + "22197: [57356 52844 4741 12343 20884]\n", + "22334: [ 9084 62606 39274 60959 19959]\n", + "15077: [42916 28882 22253 30982 11955]\n" ] } ], @@ -578,8 +578,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.238466Z", - "start_time": "2025-03-24T20:25:09.236286Z" + "end_time": "2025-03-27T19:17:58.179247Z", + "start_time": "2025-03-27T19:17:58.176595Z" } }, "cell_type": "code", @@ -608,8 +608,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.492093Z", - "start_time": "2025-03-24T20:25:09.259155Z" + "end_time": "2025-03-27T19:17:58.455536Z", + "start_time": "2025-03-27T19:17:58.212205Z" } }, "cell_type": "code", @@ -637,8 +637,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.730838Z", - "start_time": "2025-03-24T20:25:09.495722Z" + "end_time": "2025-03-27T19:17:58.702891Z", + "start_time": "2025-03-27T19:17:58.461555Z" } }, "cell_type": "code", @@ -656,15 +656,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "Key 0: [ 6 1 8 27 16 11]\n", - "Key 1: [18 13 14 21 22 17]\n", - "Key 2: [ 0 25 2 15 4 23]\n", - "Key 3: [24 19 20 9 10 29]\n", - "Key 4: [12 7 26 3 28 5]\n", - "User Passcode: [22, 8, 14, 12]\n", + "Key 0: [12 13 8 27 28 29]\n", + "Key 1: [24 19 20 21 4 23]\n", + "Key 2: [ 6 25 14 3 16 17]\n", + "Key 3: [ 0 7 26 9 22 11]\n", + "Key 4: [18 1 2 15 10 5]\n", + "User Passcode: [7, 13, 27, 26]\n", "\n", "Selected Keys:\n", - " [1, 0, 1, 4]\n", + " [3, 0, 0, 3]\n", "\n" ] } @@ -696,8 +696,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.740211Z", - "start_time": "2025-03-24T20:25:09.737938Z" + "end_time": "2025-03-27T19:17:58.713231Z", + "start_time": "2025-03-27T19:17:58.710458Z" } }, "cell_type": "code", @@ -724,8 +724,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:09.753175Z", - "start_time": "2025-03-24T20:25:09.751006Z" + "end_time": "2025-03-27T19:17:58.729425Z", + "start_time": "2025-03-27T19:17:58.727025Z" } }, "cell_type": "code", @@ -745,8 +745,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:10.000010Z", - "start_time": "2025-03-24T20:25:09.768227Z" + "end_time": "2025-03-27T19:17:58.983936Z", + "start_time": "2025-03-27T19:17:58.739679Z" } }, "cell_type": "code", @@ -771,8 +771,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:10.476203Z", - "start_time": "2025-03-24T20:25:10.003562Z" + "end_time": "2025-03-27T19:17:59.477909Z", + "start_time": "2025-03-27T19:17:58.990632Z" } }, "cell_type": "code", @@ -798,10 +798,10 @@ "output_type": "stream", "text": [ "Old User Cipher and Mask\n", - "mask: CAB5nFpQ1LM59xYy2OREr9b7Gcc=, code: $2b$12$oy6qiM687DO5qPkEBTy/V.GXIXYFkfiTmRp1oQEBXbZ10MZMV3V.6\n", + "mask: DUY6OIJHwzgG5ajVEGKHARHM6So=, code: $2b$12$/Za40kT7mC0quZxMUWCDs.4cF.3r2meCUBEoz0EWlSKkJAMOPiJTy\n", "\n", "New User Cipher and Mask\n", - "mask: 1oEiOc7ZYxkUkKlVlzNUmbvoc7k=, code: $2b$12$BAKICUuJ.gx39r29krEiu./lWS18zm60dKzfZvpSTDp3LEOzHQGN2\n", + "mask: GGBaMjr+zlPhS+pmfstihUeFt6A=, code: $2b$12$wE7bq7sYd8Q58j.qKS5ASO2IMzaJ71UW/0vOYAhx7zUhURDJYIaZi\n", "\n" ] } @@ -819,8 +819,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:10.488540Z", - "start_time": "2025-03-24T20:25:10.484314Z" + "end_time": "2025-03-27T19:17:59.490134Z", + "start_time": "2025-03-27T19:17:59.486082Z" } }, "cell_type": "code", @@ -853,8 +853,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:10.504982Z", - "start_time": "2025-03-24T20:25:10.502608Z" + "end_time": "2025-03-27T19:17:59.500256Z", + "start_time": "2025-03-27T19:17:59.497839Z" } }, "cell_type": "code", @@ -880,8 +880,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:10.762626Z", - "start_time": "2025-03-24T20:25:10.523023Z" + "end_time": "2025-03-27T19:17:59.752960Z", + "start_time": "2025-03-27T19:17:59.508826Z" } }, "cell_type": "code", diff --git a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb index 04612f7..e980273 100644 --- a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb @@ -19,12 +19,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T20:25:00.701367Z", - "start_time": "2025-03-24T20:25:00.698108Z" + "end_time": "2025-03-27T19:17:34.809483Z", + "start_time": "2025-03-27T19:17:34.735988Z" } }, "outputs": [], - "execution_count": 8 + "execution_count": 1 }, { "metadata": {}, @@ -57,12 +57,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-24T20:25:00.708966Z", - "start_time": "2025-03-24T20:25:00.704877Z" + "end_time": "2025-03-27T19:17:34.878585Z", + "start_time": "2025-03-27T19:17:34.817604Z" } }, "outputs": [], - "execution_count": 9 + "execution_count": 2 }, { "metadata": {}, @@ -81,8 +81,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:00.717550Z", - "start_time": "2025-03-24T20:25:00.715159Z" + "end_time": "2025-03-27T19:17:34.918050Z", + "start_time": "2025-03-27T19:17:34.914192Z" } }, "cell_type": "code", @@ -91,7 +91,7 @@ "signup_session_id, set_keypad = api.generate_signup_keypad(customer_id, username)" ], "outputs": [], - "execution_count": 10 + "execution_count": 3 }, { "metadata": {}, @@ -104,8 +104,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:00.726553Z", - "start_time": "2025-03-24T20:25:00.724282Z" + "end_time": "2025-03-27T19:17:34.928470Z", + "start_time": "2025-03-27T19:17:34.926257Z" } }, "cell_type": "code", @@ -115,7 +115,7 @@ "selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_keypad, keypad_size.numb_of_keys)" ], "outputs": [], - "execution_count": 11 + "execution_count": 4 }, { "metadata": {}, @@ -128,8 +128,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:00.967386Z", - "start_time": "2025-03-24T20:25:00.732130Z" + "end_time": "2025-03-27T19:17:35.223175Z", + "start_time": "2025-03-27T19:17:34.978825Z" } }, "cell_type": "code", @@ -139,8 +139,20 @@ "success = api.confirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n", "assert success" ], - "outputs": [], - "execution_count": 12 + "outputs": [ + { + "ename": "TypeError", + "evalue": "NKodeAPI.set_nkode() takes 4 positional arguments but 5 were given", + "output_type": "error", + "traceback": [ + "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", + "\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)", + "Cell \u001B[0;32mIn[5], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m confirm_keypad \u001B[38;5;241m=\u001B[39m \u001B[43mapi\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mset_nkode\u001B[49m\u001B[43m(\u001B[49m\u001B[43musername\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcustomer_id\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mselected_keys_set\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43msignup_session_id\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 2\u001B[0m selected_keys_confirm \u001B[38;5;241m=\u001B[39m select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size\u001B[38;5;241m.\u001B[39mnumb_of_keys)\n\u001B[1;32m 3\u001B[0m success \u001B[38;5;241m=\u001B[39m api\u001B[38;5;241m.\u001B[39mconfirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n", + "\u001B[0;31mTypeError\u001B[0m: NKodeAPI.set_nkode() takes 4 positional arguments but 5 were given" + ] + } + ], + "execution_count": 5 }, { "metadata": {}, @@ -154,7 +166,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:01.204427Z", + "end_time": "2025-03-27T19:17:35.258024Z", "start_time": "2025-03-24T20:25:00.973454Z" } }, @@ -182,7 +194,7 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-24T20:25:01.677259Z", + "end_time": "2025-03-27T19:17:35.259102Z", "start_time": "2025-03-24T20:25:01.209950Z" } }, diff --git a/tabletop-discussion.md b/tabletop-discussion.md index 39b590c..d0894bc 100644 --- a/tabletop-discussion.md +++ b/tabletop-discussion.md @@ -26,4 +26,48 @@ ### nKode Over Unencrypted Channel - TOTP -- DARC \ No newline at end of file +- DARC + +### Discussion Outcomes: + +#### Attacks and controls +| Attacks | Controls | +|-------------------------|--------------------------------------------------------------------------------| +| Screen Recording Attack | Split shuffle/more icons per key than keys | +| Exfiltrated DB | Physically separated keys and icons, partial or full encryption, nKode renewal | +| *APT | *Don't wait for garbage collector, manage timeouts | +| Phishing | Dispersion Resistant Keypad, nKode policy, passkey protected keypad icons | +| *MiTM | TLS, *TOTP shuffle, *DARC | +*not implemented yet/needs another look + +#### asks for Dr. Kandah + +- Evil nKode screen watching/key replay + - Can we rig the shuffle in our favor? How long do we need to cache? + - shoulder surfing + - Keylogger resistance + - split shuffle is unbiased +- Dispersion Attack/Phishing attack + - CAC/passkey protection for server stored icons + - is the dispersion algorithm unbiased? +- validate the cipher + - validate the server-side values + - validate the relationship between the mask and the hash + - validate the renewal + - are these processes secure? +- Minium amount of encryption needed + - Least encryption:brute force crack with plain text database breach + - Most encryption: everything is encrypted + - Is there an secure inbetween? what stays plain text what gets encrypted with HSM? + - How long does it take to brute-force with plain and what's gained? +- how often does nkode need to be changed if at all? + - if it does need to be changed can we roll the icons? can we start with 4 icons and add icons over time? +- Low-bandwidth: how low can we go? + - TCP vs UDP + - Security of RX/TX without tls/encrypted channel +- Hypothetical: Break the cipher keys onto different machines in different locations? +- TOTP shuffle on client and server + + +Other stuff: +- unbiased icons/psychology \ No newline at end of file -- 2.49.1 From 8728bef61a469f1dbc3bd064135dc31d5b4657e0 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 27 Mar 2025 17:30:48 -0500 Subject: [PATCH 71/85] add asks and dr kandah --- tabletop-discussion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tabletop-discussion.md b/tabletop-discussion.md index d0894bc..0a13834 100644 --- a/tabletop-discussion.md +++ b/tabletop-discussion.md @@ -60,7 +60,7 @@ - Most encryption: everything is encrypted - Is there an secure inbetween? what stays plain text what gets encrypted with HSM? - How long does it take to brute-force with plain and what's gained? -- how often does nkode need to be changed if at all? +- how often do nkode icons need to be changed to maintain security if at all? - if it does need to be changed can we roll the icons? can we start with 4 icons and add icons over time? - Low-bandwidth: how low can we go? - TCP vs UDP -- 2.49.1 From 021e468494a953468042ff074b46d74cd9ad8c99 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 30 Mar 2025 04:44:35 -0500 Subject: [PATCH 72/85] move tabletop-discussion.md --- .../tabletop-discussion.md | 43 +++++----- .../Enrollment_Login_Renewal_Simplified.ipynb | 81 ++++++++++--------- 2 files changed, 63 insertions(+), 61 deletions(-) rename tabletop-discussion.md => docs/tabletop-discussion.md (56%) diff --git a/tabletop-discussion.md b/docs/tabletop-discussion.md similarity index 56% rename from tabletop-discussion.md rename to docs/tabletop-discussion.md index 0a13834..6dc5fee 100644 --- a/tabletop-discussion.md +++ b/docs/tabletop-discussion.md @@ -2,13 +2,13 @@ ## Documentation and Tutorials -1. [Enrollment](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/docs/enrollment_diagram.md) -2. [Login](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/docs/login_diagram.md) -3. [Cipher and Renew](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/docs/encipher_decipher_renew_nkode.md) -4. [nKode API Tutorial 1](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Enrollment_Login_Renewal_Simplified.ipynb) -5. [nKode API Tutorial 2](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Enrollment_Login_Renewal_Detailed.ipynb) -6. [Dispersion Tutorial](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Dispersion.ipynb) -7. [Split Shuffle](https://git.infra.nkode.tech/dkelly/pynkode/src/branch/main/notebooks/Split_Shuffle.ipynb) +1. [Enrollment](enrollment_diagram.md) +2. [Login](login_diagram.md) +3. [Cipher and Renew](encipher_decipher_renew_nkode.md) +4. [nKode API Tutorial 1](../notebooks/Enrollment_Login_Renewal_Simplified.ipynb) +5. [nKode API Tutorial 2](../notebooks/Enrollment_Login_Renewal_Detailed.ipynb) +6. [Dispersion Tutorial](../notebooks/Dispersion.ipynb) +7. [Split Shuffle](../notebooks/Split_Shuffle.ipynb) ## Discussion Topics ### nKode Length @@ -38,36 +38,33 @@ | *APT | *Don't wait for garbage collector, manage timeouts | | Phishing | Dispersion Resistant Keypad, nKode policy, passkey protected keypad icons | | *MiTM | TLS, *TOTP shuffle, *DARC | + *not implemented yet/needs another look #### asks for Dr. Kandah - Evil nKode screen watching/key replay - - Can we rig the shuffle in our favor? How long do we need to cache? - - shoulder surfing - - Keylogger resistance - - split shuffle is unbiased + - Given a particular policy and keypad size: + - what is the probability of a key replay? + - what trade-offs are made between key replay and cracking an nkode? + - Is the split shuffle unbiased? + - Can we rig the shuffle in our favor with keypad caching or other techniques? - Dispersion Attack/Phishing attack - - CAC/passkey protection for server stored icons - is the dispersion algorithm unbiased? + - Develop a modified dispersion algorithm to phish a dispersion resistant keypad - validate the cipher - validate the server-side values - validate the relationship between the mask and the hash - validate the renewal - - are these processes secure? -- Minium amount of encryption needed - - Least encryption:brute force crack with plain text database breach - - Most encryption: everything is encrypted - - Is there an secure inbetween? what stays plain text what gets encrypted with HSM? - - How long does it take to brute-force with plain and what's gained? -- how often do nkode icons need to be changed to maintain security if at all? - - if it does need to be changed can we roll the icons? can we start with 4 icons and add icons over time? + - are these processes/algorithms secure? +- What is the minimum amount of encryption needed to secure user's nkodes against a full/partial database exfiltration + - How long will it take to brute force a hash with a full plain text breach of the database and what's gained? +- How often do nkode icons need to be changed to maintain security if at all? + - if it does need to be changed can we start with 4 icons and add icons over time then roll the icons (drop the first icons and append a new one) after reaching a max size? - Low-bandwidth: how low can we go? - TCP vs UDP - Security of RX/TX without tls/encrypted channel -- Hypothetical: Break the cipher keys onto different machines in different locations? -- TOTP shuffle on client and server - +- Hypothetical: What security gains are made if we split the cipher keys into multiple parts and put them on different machines in many locations? Other stuff: - unbiased icons/psychology \ No newline at end of file diff --git a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb index e980273..0394de6 100644 --- a/notebooks/Enrollment_Login_Renewal_Simplified.ipynb +++ b/notebooks/Enrollment_Login_Renewal_Simplified.ipynb @@ -19,12 +19,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-27T19:17:34.809483Z", - "start_time": "2025-03-27T19:17:34.735988Z" + "end_time": "2025-03-28T15:06:18.878127Z", + "start_time": "2025-03-28T15:06:18.874618Z" } }, "outputs": [], - "execution_count": 1 + "execution_count": 30 }, { "metadata": {}, @@ -44,8 +44,8 @@ "policy = NKodePolicy(\n", " max_nkode_len=10,\n", " min_nkode_len=4,\n", - " distinct_positions=0,\n", - " distinct_properties=4,\n", + " distinct_positions=0, # complexity\n", + " distinct_properties=4, # disparity\n", ")\n", "keypad_size = KeypadSize(\n", " numb_of_keys = 5,\n", @@ -57,12 +57,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-27T19:17:34.878585Z", - "start_time": "2025-03-27T19:17:34.817604Z" + "end_time": "2025-03-28T15:06:18.896461Z", + "start_time": "2025-03-28T15:06:18.891125Z" } }, "outputs": [], - "execution_count": 2 + "execution_count": 31 }, { "metadata": {}, @@ -81,8 +81,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T19:17:34.918050Z", - "start_time": "2025-03-27T19:17:34.914192Z" + "end_time": "2025-03-28T15:06:18.914254Z", + "start_time": "2025-03-28T15:06:18.911798Z" } }, "cell_type": "code", @@ -91,7 +91,7 @@ "signup_session_id, set_keypad = api.generate_signup_keypad(customer_id, username)" ], "outputs": [], - "execution_count": 3 + "execution_count": 32 }, { "metadata": {}, @@ -104,8 +104,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T19:17:34.928470Z", - "start_time": "2025-03-27T19:17:34.926257Z" + "end_time": "2025-03-28T15:06:18.931791Z", + "start_time": "2025-03-28T15:06:18.929028Z" } }, "cell_type": "code", @@ -115,7 +115,7 @@ "selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_keypad, keypad_size.numb_of_keys)" ], "outputs": [], - "execution_count": 4 + "execution_count": 33 }, { "metadata": {}, @@ -128,31 +128,19 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T19:17:35.223175Z", - "start_time": "2025-03-27T19:17:34.978825Z" + "end_time": "2025-03-28T15:06:19.247638Z", + "start_time": "2025-03-28T15:06:18.938601Z" } }, "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", "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", + "success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id)\n", "assert success" ], - "outputs": [ - { - "ename": "TypeError", - "evalue": "NKodeAPI.set_nkode() takes 4 positional arguments but 5 were given", - "output_type": "error", - "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)", - "Cell \u001B[0;32mIn[5], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m confirm_keypad \u001B[38;5;241m=\u001B[39m \u001B[43mapi\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mset_nkode\u001B[49m\u001B[43m(\u001B[49m\u001B[43musername\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mcustomer_id\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mselected_keys_set\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43msignup_session_id\u001B[49m\u001B[43m)\u001B[49m\n\u001B[1;32m 2\u001B[0m selected_keys_confirm \u001B[38;5;241m=\u001B[39m select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size\u001B[38;5;241m.\u001B[39mnumb_of_keys)\n\u001B[1;32m 3\u001B[0m success \u001B[38;5;241m=\u001B[39m api\u001B[38;5;241m.\u001B[39mconfirm_nkode(username, customer_id, selected_keys_confirm, signup_session_id)\n", - "\u001B[0;31mTypeError\u001B[0m: NKodeAPI.set_nkode() takes 4 positional arguments but 5 were given" - ] - } - ], - "execution_count": 5 + "outputs": [], + "execution_count": 34 }, { "metadata": {}, @@ -166,8 +154,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T19:17:35.258024Z", - "start_time": "2025-03-24T20:25:00.973454Z" + "end_time": "2025-03-28T15:06:19.559753Z", + "start_time": "2025-03-28T15:06:19.254675Z" } }, "cell_type": "code", @@ -178,7 +166,7 @@ "assert success" ], "outputs": [], - "execution_count": 13 + "execution_count": 35 }, { "metadata": {}, @@ -194,8 +182,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-27T19:17:35.259102Z", - "start_time": "2025-03-24T20:25:01.209950Z" + "end_time": "2025-03-28T15:06:20.181548Z", + "start_time": "2025-03-28T15:06:19.568067Z" } }, "cell_type": "code", @@ -207,7 +195,24 @@ "assert success" ], "outputs": [], - "execution_count": 14 + "execution_count": 36 + }, + { + "metadata": { + "ExecuteTime": { + "end_time": "2025-03-28T15:06:20.500050Z", + "start_time": "2025-03-28T15:06:20.194912Z" + } + }, + "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": 37 } ], "metadata": { -- 2.49.1 From ac86d6cf207aea955a421a8c1fb144a8e07c3e8a Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 30 Mar 2025 04:45:56 -0500 Subject: [PATCH 73/85] update readme --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6e7fb39..923d7e5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Readme +# PynKode pynkode is a tutorial of how nkode works. There are jupyter notebooks in /notebooks covering these topics: @@ -70,3 +70,6 @@ Recommended order: 2. [Enrollment_Login_Renewal_Detailed.ipynb](notebooks/Enrollment_Login_Renewal_Detailed.ipynb) - Learn the nKode API in detail 3. [Dispersion.ipynb](notebooks/Dispersion.ipynb)- Understand the basic concepts of dispersion in nkode 4. [Split_Shuffle.ipynb](notebooks/Split_Shuffle.ipynb) - Explore the split shuffle functionality + +## Tabletop Discussion +[notes](docs/tabletop-discussion.md) -- 2.49.1 From 8edf291cfaedf098b4be95d492057a1566406bc2 Mon Sep 17 00:00:00 2001 From: Donovan Date: Sun, 30 Mar 2025 04:57:51 -0500 Subject: [PATCH 74/85] add environment.yaml --- README.md | 22 +---- environment.yaml | 208 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+), 19 deletions(-) create mode 100644 environment.yaml diff --git a/README.md b/README.md index 923d7e5..64dc85c 100644 --- a/README.md +++ b/README.md @@ -9,28 +9,12 @@ There are jupyter notebooks in /notebooks covering these topics: ## Installation - Python version 3.10 or greater is required -- Install pyenv or conda for environment management +- Install conda (or your preferred tool) for environment management -### Option 1: Using conda +### Using conda ```bash -# Create a new conda environment named pynkode -conda create -n pynkode python=3.10 -# Activate the environment +conda env create -f environment.yml conda activate pynkode -# Install the requirements -pip install -r requirements.txt -``` - -### Option 2: Using pyenv -```bash -# Install Python 3.10 using pyenv -pyenv install 3.10.0 -# Create a virtualenv named pynkode -pyenv virtualenv 3.10.0 pynkode -# Set the local version to pynkode -pyenv local pynkode -# Install the requirements -pip install -r requirements.txt ``` ## Starting a Jupyter Notebook diff --git a/environment.yaml b/environment.yaml new file mode 100644 index 0000000..a82b455 --- /dev/null +++ b/environment.yaml @@ -0,0 +1,208 @@ +name: pynkode +channels: + - defaults +dependencies: + - annotated-types=0.6.0=py310hca03da5_0 + - anyio=4.2.0=py310hca03da5_0 + - appnope=0.1.2=py310hca03da5_1001 + - argon2-cffi=21.3.0=pyhd3eb1b0_0 + - argon2-cffi-bindings=21.2.0=py310h1a28f6b_0 + - asttokens=2.0.5=pyhd3eb1b0_0 + - async-lru=2.0.4=py310hca03da5_0 + - attrs=23.1.0=py310hca03da5_0 + - babel=2.11.0=py310hca03da5_0 + - beautifulsoup4=4.12.3=py310hca03da5_0 + - bleach=4.1.0=pyhd3eb1b0_0 + - brotli-python=1.0.9=py310h313beb8_8 + - bzip2=1.0.8=h80987f9_6 + - ca-certificates=2024.3.11=hca03da5_0 + - certifi=2024.7.4=py310hca03da5_0 + - cffi=1.16.0=py310h80987f9_1 + - charset-normalizer=2.0.4=pyhd3eb1b0_0 + - comm=0.2.1=py310hca03da5_0 + - cyrus-sasl=2.1.28=h9131b1a_1 + - debugpy=1.6.7=py310h313beb8_0 + - decorator=5.1.1=pyhd3eb1b0_0 + - defusedxml=0.7.1=pyhd3eb1b0_0 + - executing=0.8.3=pyhd3eb1b0_0 + - gettext=0.21.0=h13f89a0_1 + - glib=2.78.4=h313beb8_0 + - glib-tools=2.78.4=h313beb8_0 + - gst-plugins-base=1.14.1=h313beb8_1 + - gstreamer=1.14.1=h80987f9_1 + - icu=73.1=h313beb8_0 + - idna=3.7=py310hca03da5_0 + - ipykernel=6.28.0=py310hca03da5_0 + - ipython=8.25.0=py310hca03da5_0 + - ipywidgets=8.1.2=py310hca03da5_0 + - jedi=0.18.1=py310hca03da5_1 + - jinja2=3.1.4=py310hca03da5_0 + - jpeg=9e=h80987f9_1 + - json5=0.9.6=pyhd3eb1b0_0 + - jsonschema=4.19.2=py310hca03da5_0 + - jsonschema-specifications=2023.7.1=py310hca03da5_0 + - jupyter=1.0.0=py310hca03da5_9 + - jupyter-lsp=2.2.0=py310hca03da5_0 + - jupyter_client=8.6.0=py310hca03da5_0 + - jupyter_console=6.6.3=py310hca03da5_0 + - jupyter_core=5.7.2=py310hca03da5_0 + - jupyter_events=0.10.0=py310hca03da5_0 + - jupyter_server=2.14.1=py310hca03da5_0 + - jupyter_server_terminals=0.4.4=py310hca03da5_1 + - jupyterlab=4.0.11=py310hca03da5_0 + - jupyterlab_pygments=0.1.2=py_0 + - jupyterlab_server=2.25.1=py310hca03da5_0 + - jupyterlab_widgets=3.0.10=py310hca03da5_0 + - krb5=1.20.1=hf3e1bf2_1 + - libclang=14.0.6=default_h1b80db6_1 + - libclang13=14.0.6=default_h24352ff_1 + - libcxx=14.0.6=h848a8c0_0 + - libedit=3.1.20230828=h80987f9_0 + - libffi=3.4.4=hca03da5_1 + - libglib=2.78.4=h0a96307_0 + - libiconv=1.16=h80987f9_3 + - libllvm14=14.0.6=h7ec7a93_3 + - libpng=1.6.39=h80987f9_0 + - libpq=12.17=h02f6b3c_0 + - libsodium=1.0.18=h1a28f6b_0 + - libxml2=2.10.4=h0b34f26_2 + - llvm-openmp=14.0.6=hc6e5704_0 + - lz4-c=1.9.4=h313beb8_1 + - markupsafe=2.1.3=py310h80987f9_0 + - matplotlib-inline=0.1.6=py310hca03da5_0 + - mistune=2.0.4=py310hca03da5_0 + - mysql=5.7.24=ha71a6ea_2 + - nbclient=0.8.0=py310hca03da5_0 + - nbconvert=7.10.0=py310hca03da5_0 + - nbformat=5.9.2=py310hca03da5_0 + - ncurses=6.4=h313beb8_0 + - nest-asyncio=1.6.0=py310hca03da5_0 + - notebook=7.0.8=py310hca03da5_2 + - notebook-shim=0.2.3=py310hca03da5_0 + - openssl=3.0.14=h80987f9_0 + - overrides=7.4.0=py310hca03da5_0 + - packaging=24.1=py310hca03da5_0 + - pandocfilters=1.5.0=pyhd3eb1b0_0 + - parso=0.8.3=pyhd3eb1b0_0 + - pcre2=10.42=hb066dcc_1 + - pexpect=4.8.0=pyhd3eb1b0_3 + - pip=24.0=py310hca03da5_0 + - platformdirs=3.10.0=py310hca03da5_0 + - ply=3.11=py310hca03da5_0 + - prometheus_client=0.14.1=py310hca03da5_0 + - prompt-toolkit=3.0.43=py310hca03da5_0 + - prompt_toolkit=3.0.43=hd3eb1b0_0 + - psutil=5.9.0=py310h1a28f6b_0 + - ptyprocess=0.7.0=pyhd3eb1b0_2 + - pure_eval=0.2.2=pyhd3eb1b0_0 + - pycparser=2.21=pyhd3eb1b0_0 + - pydantic=2.5.3=py310hca03da5_0 + - pydantic-core=2.14.6=py310hf0e4da2_0 + - pygments=2.15.1=py310hca03da5_1 + - pyqt=5.15.10=py310h313beb8_0 + - pyqt5-sip=12.13.0=py310h80987f9_0 + - pysocks=1.7.1=py310hca03da5_0 + - python=3.10.14=hb885b13_1 + - python-dateutil=2.9.0post0=py310hca03da5_2 + - python-fastjsonschema=2.16.2=py310hca03da5_0 + - python-json-logger=2.0.7=py310hca03da5_0 + - pytz=2024.1=py310hca03da5_0 + - pyyaml=6.0.1=py310h80987f9_0 + - pyzmq=25.1.2=py310h313beb8_0 + - qt-main=5.15.2=h0917680_10 + - qtconsole=5.5.1=py310hca03da5_0 + - qtpy=2.4.1=py310hca03da5_0 + - readline=8.2=h1a28f6b_0 + - referencing=0.30.2=py310hca03da5_0 + - requests=2.32.2=py310hca03da5_0 + - rfc3339-validator=0.1.4=py310hca03da5_0 + - rfc3986-validator=0.1.1=py310hca03da5_0 + - rpds-py=0.10.6=py310hf0e4da2_0 + - send2trash=1.8.2=py310hca03da5_0 + - setuptools=69.5.1=py310hca03da5_0 + - sip=6.7.12=py310h313beb8_0 + - six=1.16.0=pyhd3eb1b0_1 + - sniffio=1.3.0=py310hca03da5_0 + - soupsieve=2.5=py310hca03da5_0 + - sqlite=3.45.3=h80987f9_0 + - stack_data=0.2.0=pyhd3eb1b0_0 + - terminado=0.17.1=py310hca03da5_0 + - tinycss2=1.2.1=py310hca03da5_0 + - tk=8.6.14=h6ba3021_0 + - tomli=2.0.1=py310hca03da5_0 + - tornado=6.4.1=py310h80987f9_0 + - traitlets=5.14.3=py310hca03da5_0 + - typing-extensions=4.11.0=py310hca03da5_0 + - typing_extensions=4.11.0=py310hca03da5_0 + - urllib3=2.2.2=py310hca03da5_0 + - wcwidth=0.2.5=pyhd3eb1b0_0 + - webencodings=0.5.1=py310hca03da5_1 + - websocket-client=1.8.0=py310hca03da5_0 + - wheel=0.43.0=py310hca03da5_0 + - widgetsnbextension=4.0.10=py310hca03da5_0 + - xz=5.4.6=h80987f9_1 + - yaml=0.2.5=h1a28f6b_0 + - zeromq=4.3.5=h313beb8_0 + - zlib=1.2.13=h18a0788_1 + - zstd=1.5.5=hd90d995_2 + - pip: + - aiofiles==23.2.1 + - altair==5.3.0 + - arrow==1.3.0 + - bcrypt==4.1.3 + - click==8.1.7 + - contourpy==1.2.1 + - cycler==0.12.1 + - dnspython==2.6.1 + - email-validator==2.2.0 + - exceptiongroup==1.2.1 + - fastapi==0.111.0 + - fastapi-cli==0.0.4 + - ffmpy==0.3.2 + - filelock==3.15.4 + - fonttools==4.53.1 + - fqdn==1.5.1 + - fsspec==2024.6.1 + - gradio==4.37.2 + - gradio-client==1.0.2 + - h11==0.14.0 + - httpcore==1.0.5 + - httptools==0.6.1 + - httpx==0.27.0 + - huggingface-hub==0.23.4 + - importlib-resources==6.4.0 + - iniconfig==2.0.0 + - isoduration==20.11.0 + - jsonpointer==3.0.0 + - kiwisolver==1.4.5 + - markdown-it-py==3.0.0 + - matplotlib==3.9.1 + - mdurl==0.1.2 + - numpy==2.0.0 + - orjson==3.10.6 + - pandas==2.2.2 + - pillow==10.4.0 + - pluggy==1.5.0 + - pydub==0.25.1 + - pyparsing==3.1.2 + - pytest==8.2.2 + - python-dotenv==1.0.1 + - python-multipart==0.0.9 + - rich==13.7.1 + - ruff==0.5.1 + - semantic-version==2.10.0 + - shellingham==1.5.4 + - starlette==0.37.2 + - tomlkit==0.12.0 + - toolz==0.12.1 + - tqdm==4.66.4 + - typer==0.12.3 + - types-python-dateutil==2.9.0.20241206 + - tzdata==2024.1 + - ujson==5.10.0 + - uri-template==1.3.0 + - uvicorn==0.30.1 + - uvloop==0.19.0 + - watchfiles==0.22.0 + - webcolors==24.11.1 + - websockets==11.0.3 -- 2.49.1 From 75168955fa9406e42bf74290b7ddeffdeb7fa330 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 12:55:57 -0500 Subject: [PATCH 75/85] add nkode over unecrypted channel --- .gitignore | 2 + docs/nkode_over_unencrypted_channel.md | 86 ++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 docs/nkode_over_unencrypted_channel.md diff --git a/.gitignore b/.gitignore index 17cd06f..e3fe521 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .idea __pycache__ .ipynb_checkpoints +.DS_Store + diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md new file mode 100644 index 0000000..8c5b89a --- /dev/null +++ b/docs/nkode_over_unencrypted_channel.md @@ -0,0 +1,86 @@ +# nKode Authentication Over Unencrypted Channel in Low-Bandwidth Environments + +## Low-Bandwidth Architecture + +The standard nKode architecture will not work in low-bandwidth environments. +Keypad icons are too large to send from the sever to the client. +To over come this issue, we can move the nKode icons from the serve to the users mobile device. +The server only sends the indices in which the icons need to be arranged. + +```mermaid +sequenceDiagram + participant User + participant Mobile Client + participant Server + Note over User,Server: Enrollment + User ->> Server: Initiate Enrollment + Server ->> Server: Generate Keypad Icons + Server -->> Mobile Client: Store Icons On Device + Note right of Server: Server does not store the icons and does not know what they are + Server ->> Mobile Client: Keypad Index Array + Mobile Client ->> User: Render Keypad + User ->> Server: Set nKode + Server ->> Server: Disperse Keypad + Server ->> Mobile Client: Keypad Index Array + Mobile Client ->> User: Render Keypad + User ->> Server: Confirm nKode + Note over User,Server: Login + Server ->> Mobile Client: Keypad Index Array + Mobile Client ->> User: Render Keypad + User ->> Server: Successful Login + Server ->> Server: Split Shuffle Keypad +``` + +## Chacha20 Deterministic CSPRNG + +A ChaCha20 Deterministic CSPRNG is a cryptographically secure pseudorandom number generator that uses the ChaCha20 stream cipher to produce a reproducible sequence of pseudorandom bytes. Given the same 256-bit key and 96-bit public nonce, it will always generate the same output stream, making it deterministic and suitable for use cases that require both security and repeatability. + +## Secure Low-Bandwidth Architecture + +We can modify the architecture above to allow secure authentication over an unencrypted network + +```mermaid +sequenceDiagram + participant User + participant Mobile Client + participant Server + Note over User,Server: Enrollment + User ->> Server: Initiate Enrollment + Server ->> Server: Generate Keypad Icons + Server -->> Mobile Client: Store Icons On Device + Note right of Server: Server does not store the icons and does not know what they are + rect rgb(191, 223, 255) + Server -->> Mobile Client: Store ChaCha20 256-bit key + end + rect rgb(191, 223, 255) + Server ->> Server: Ciphered Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Ciphered Keypad Index Array + Nonce + end + Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Serve must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. + rect rgb(191, 223, 255) + Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Ciphered Keypad Index Array, SharedKey, Nonce) + end + Mobile Client ->> User: Render Keypad + User ->> Server: Set nKode + Server ->> Server: Disperse Keypad + rect rgb(191, 223, 255) + Server ->> Server: Ciphered Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Ciphered Keypad Index Array + Nonce + end + rect rgb(191, 223, 255) + Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Ciphered Keypad Index Array, SharedKey, Nonce) + end + Mobile Client ->> User: Render Keypad + User ->> Server: Confirm nKode + Note over User,Server: Login + rect rgb(191, 223, 255) + Server ->> Server: Ciphered Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Ciphered Keypad Index Array + Nonce + end + rect rgb(191, 223, 255) + Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Ciphered Keypad Index Array, SharedKey, Nonce) + end + Mobile Client ->> User: Render Keypad + User ->> Server: Successful Login + Server ->> Server: Split Shuffle Keypad +``` -- 2.49.1 From 9a12b3b5e460c65085a8eaea2890b7a14dc8b90b Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:24:36 -0500 Subject: [PATCH 76/85] update document --- docs/nkode_over_unencrypted_channel.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index 8c5b89a..4db88bb 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -37,7 +37,7 @@ A ChaCha20 Deterministic CSPRNG is a cryptographically secure pseudorandom numbe ## Secure Low-Bandwidth Architecture -We can modify the architecture above to allow secure authentication over an unencrypted network +We can modify the architecture above to allow secure authentication over an unencrypted network using ChaCha20. ```mermaid sequenceDiagram -- 2.49.1 From d1b6f192afe4069b2569e4ed4407acf4bf1d248f Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:29:10 -0500 Subject: [PATCH 77/85] fix typos --- docs/nkode_over_unencrypted_channel.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index 4db88bb..9a536ba 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -3,8 +3,8 @@ ## Low-Bandwidth Architecture The standard nKode architecture will not work in low-bandwidth environments. -Keypad icons are too large to send from the sever to the client. -To over come this issue, we can move the nKode icons from the serve to the users mobile device. +Keypad icons are too large to send from the server to the client. +To over come this issue, we can move the nKode icons from the server to the users mobile device. The server only sends the indices in which the icons need to be arranged. ```mermaid @@ -15,8 +15,9 @@ sequenceDiagram Note over User,Server: Enrollment User ->> Server: Initiate Enrollment Server ->> Server: Generate Keypad Icons + Note right of Server: Ideally the icons are generated on the users device.
Since current ML models are too compute intense, a GPU enabled server must run the models during enrollment. Server -->> Mobile Client: Store Icons On Device - Note right of Server: Server does not store the icons and does not know what they are + Note right of Server: The Server does not store the icons Server ->> Mobile Client: Keypad Index Array Mobile Client ->> User: Render Keypad User ->> Server: Set nKode -- 2.49.1 From 1e5fd26464849d3857f3ac7ea0180c46d0b869d9 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:31:01 -0500 Subject: [PATCH 78/85] fix mermaid notes --- docs/nkode_over_unencrypted_channel.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index 9a536ba..ef3333c 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -15,7 +15,7 @@ sequenceDiagram Note over User,Server: Enrollment User ->> Server: Initiate Enrollment Server ->> Server: Generate Keypad Icons - Note right of Server: Ideally the icons are generated on the users device.
Since current ML models are too compute intense, a GPU enabled server must run the models during enrollment. + Note right of Server: Ideally the icons are generated on the users device.
Since current ML models are too compute intense,
a GPU enabled server must run the models during enrollment. Server -->> Mobile Client: Store Icons On Device Note right of Server: The Server does not store the icons Server ->> Mobile Client: Keypad Index Array @@ -49,7 +49,6 @@ sequenceDiagram User ->> Server: Initiate Enrollment Server ->> Server: Generate Keypad Icons Server -->> Mobile Client: Store Icons On Device - Note right of Server: Server does not store the icons and does not know what they are rect rgb(191, 223, 255) Server -->> Mobile Client: Store ChaCha20 256-bit key end -- 2.49.1 From 6ea7486d7607a799e9964264873832ff030874a2 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:34:23 -0500 Subject: [PATCH 79/85] ciphered to shuffled --- docs/nkode_over_unencrypted_channel.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index ef3333c..0d08233 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -53,32 +53,32 @@ sequenceDiagram Server -->> Mobile Client: Store ChaCha20 256-bit key end rect rgb(191, 223, 255) - Server ->> Server: Ciphered Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) - Server ->> Mobile Client: Ciphered Keypad Index Array + Nonce + Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end - Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Serve must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. + Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Server must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Ciphered Keypad Index Array, SharedKey, Nonce) + Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Shuffled Keypad Index Array, SharedKey, Nonce) end Mobile Client ->> User: Render Keypad User ->> Server: Set nKode Server ->> Server: Disperse Keypad rect rgb(191, 223, 255) - Server ->> Server: Ciphered Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) - Server ->> Mobile Client: Ciphered Keypad Index Array + Nonce + Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Ciphered Keypad Index Array, SharedKey, Nonce) + Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Shuffled Keypad Index Array, SharedKey, Nonce) end Mobile Client ->> User: Render Keypad User ->> Server: Confirm nKode Note over User,Server: Login rect rgb(191, 223, 255) - Server ->> Server: Ciphered Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) - Server ->> Mobile Client: Ciphered Keypad Index Array + Nonce + Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Ciphered Keypad Index Array, SharedKey, Nonce) + Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Shuffled Keypad Index Array, SharedKey, Nonce) end Mobile Client ->> User: Render Keypad User ->> Server: Successful Login -- 2.49.1 From 6777a19f5b101cbccab4ef5f78e032511f8e9b4c Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:35:35 -0500 Subject: [PATCH 80/85] reverse to unshuffle --- docs/nkode_over_unencrypted_channel.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index 0d08233..26f1875 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -58,7 +58,7 @@ sequenceDiagram end Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Server must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Shuffled Keypad Index Array, SharedKey, Nonce) + Mobile Client ->> Mobile Client: Keypad Index Array =
Unshuffle(Shuffled Keypad Index Array, SharedKey, Nonce) end Mobile Client ->> User: Render Keypad User ->> Server: Set nKode @@ -68,7 +68,7 @@ sequenceDiagram Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Shuffled Keypad Index Array, SharedKey, Nonce) + Mobile Client ->> Mobile Client: Keypad Index Array =
Unshuffle(Shuffled Keypad Index Array, SharedKey, Nonce) end Mobile Client ->> User: Render Keypad User ->> Server: Confirm nKode @@ -78,7 +78,7 @@ sequenceDiagram Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Reverse(Shuffled Keypad Index Array, SharedKey, Nonce) + Mobile Client ->> Mobile Client: Keypad Index Array =
Unshuffle(Shuffled Keypad Index Array, SharedKey, Nonce) end Mobile Client ->> User: Render Keypad User ->> Server: Successful Login -- 2.49.1 From d22ec80ee7fd3ddd302568a5a5e240578f51bf9b Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:37:50 -0500 Subject: [PATCH 81/85] remove chacha from enrollment --- docs/nkode_over_unencrypted_channel.md | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index 26f1875..136dc3b 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -52,29 +52,18 @@ sequenceDiagram rect rgb(191, 223, 255) Server -->> Mobile Client: Store ChaCha20 256-bit key end - rect rgb(191, 223, 255) - Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) - Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce - end - Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Server must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. - rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Unshuffle(Shuffled Keypad Index Array, SharedKey, Nonce) + Server ->> Mobile Client: Keypad Index Array end Mobile Client ->> User: Render Keypad User ->> Server: Set nKode Server ->> Server: Disperse Keypad - rect rgb(191, 223, 255) - Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) - Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce - end - rect rgb(191, 223, 255) - Mobile Client ->> Mobile Client: Keypad Index Array =
Unshuffle(Shuffled Keypad Index Array, SharedKey, Nonce) - end + Server ->> Mobile Client: Keypad Index Array Mobile Client ->> User: Render Keypad User ->> Server: Confirm nKode Note over User,Server: Login rect rgb(191, 223, 255) Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) + Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Server must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end rect rgb(191, 223, 255) -- 2.49.1 From 13a1a64772425836bc6f95d67d761121c7880e27 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:38:47 -0500 Subject: [PATCH 82/85] remove dangling end --- docs/nkode_over_unencrypted_channel.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index 136dc3b..b84b139 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -53,7 +53,6 @@ sequenceDiagram Server -->> Mobile Client: Store ChaCha20 256-bit key end Server ->> Mobile Client: Keypad Index Array - end Mobile Client ->> User: Render Keypad User ->> Server: Set nKode Server ->> Server: Disperse Keypad -- 2.49.1 From 81829c81b8b7b21669961ec97ddb452c352cb084 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:41:52 -0500 Subject: [PATCH 83/85] update network assumptions --- docs/nkode_over_unencrypted_channel.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_over_unencrypted_channel.md index b84b139..8465ad5 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_over_unencrypted_channel.md @@ -45,7 +45,7 @@ sequenceDiagram participant User participant Mobile Client participant Server - Note over User,Server: Enrollment + Note over User,Server: Enrollment (assume secure network) User ->> Server: Initiate Enrollment Server ->> Server: Generate Keypad Icons Server -->> Mobile Client: Store Icons On Device @@ -59,12 +59,12 @@ sequenceDiagram Server ->> Mobile Client: Keypad Index Array Mobile Client ->> User: Render Keypad User ->> Server: Confirm nKode - Note over User,Server: Login + Note over User,Server: Login (assume unsecure network) rect rgb(191, 223, 255) Server ->> Server: Shuffled Keypad Index Array =
ChaCha20FisherYates(Keypad Index Array, SharedKey, Nonce) - Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Server must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. Server ->> Mobile Client: Shuffled Keypad Index Array + Nonce end + Note right of Server: Server also sends the 96-bit nonce in plain-text.
The Server must never use the same nonce twice.
It must be randonly generated for every authentication.
The only additional overhead is the 96-bit nonce. rect rgb(191, 223, 255) Mobile Client ->> Mobile Client: Keypad Index Array =
Unshuffle(Shuffled Keypad Index Array, SharedKey, Nonce) end -- 2.49.1 From 437d8b0f31c3dde903281bd93a852527948066e4 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 13:44:58 -0500 Subject: [PATCH 84/85] update name --- ...er_unencrypted_channel.md => nkode_unsecure_lowbandwitdh.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename docs/{nkode_over_unencrypted_channel.md => nkode_unsecure_lowbandwitdh.md} (97%) diff --git a/docs/nkode_over_unencrypted_channel.md b/docs/nkode_unsecure_lowbandwitdh.md similarity index 97% rename from docs/nkode_over_unencrypted_channel.md rename to docs/nkode_unsecure_lowbandwitdh.md index 8465ad5..35f45f9 100644 --- a/docs/nkode_over_unencrypted_channel.md +++ b/docs/nkode_unsecure_lowbandwitdh.md @@ -1,4 +1,4 @@ -# nKode Authentication Over Unencrypted Channel in Low-Bandwidth Environments +# nKode Authentication Over Unsecure and Low-Bandwidth Network ## Low-Bandwidth Architecture -- 2.49.1 From 10c84e4535df51ef0501044425234ea2570eb234 Mon Sep 17 00:00:00 2001 From: Donovan Date: Thu, 26 Jun 2025 15:18:30 -0500 Subject: [PATCH 85/85] update title --- docs/nkode_unsecure_lowbandwitdh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nkode_unsecure_lowbandwitdh.md b/docs/nkode_unsecure_lowbandwitdh.md index 35f45f9..884f285 100644 --- a/docs/nkode_unsecure_lowbandwitdh.md +++ b/docs/nkode_unsecure_lowbandwitdh.md @@ -1,4 +1,4 @@ -# nKode Authentication Over Unsecure and Low-Bandwidth Network +# nKode Authentication Over Unsecured and Low-Bandwidth Network ## Low-Bandwidth Architecture -- 2.49.1