From 772c93c8a8b70afb28941d7ff4438d81d082a013 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 24 Jul 2024 14:17:56 -0500 Subject: [PATCH] test and validate signup with dispersable keypad and login with non-dispersable keypad --- nkode_api.py | 16 +- nkode_tutorial.ipynb | 265 ++++++++++++++++++++-------------- src/customer.py | 3 + src/customer_attributes.py | 9 +- src/models.py | 6 +- src/user_interface.py | 43 +++++- src/user_signup_session.py | 2 + test/test_nkode_api.py | 13 +- test/test_nkode_interface.py | 4 +- test/test_user_cipher_keys.py | 6 +- test/test_user_interface.py | 2 +- 11 files changed, 227 insertions(+), 142 deletions(-) diff --git a/nkode_api.py b/nkode_api.py index 17c0ef2..d71d2cb 100644 --- a/nkode_api.py +++ b/nkode_api.py @@ -28,18 +28,21 @@ class NKodeAPI(BaseModel): def generate_index_interface(self, customer_id: UUID) -> tuple[UUID, list[int]]: assert (customer_id in self.customers.keys()) customer = self.customers[customer_id] - set_interface = UserInterface.new(customer.attributes.keypad_size) + login_interface = UserInterface.new(customer.attributes.keypad_size) + set_interface = login_interface.sign_up_interface() new_session = UserSignupSession( session_id=uuid4(), + login_interface=login_interface, set_interface=set_interface.interface, customer_id=customer_id, - keypad_size=customer.attributes.keypad_size, + keypad_size=set_interface.keypad_size, ) self.signup_sessions[new_session.session_id] = new_session return new_session.session_id, new_session.set_interface def set_nkode( - self, username: str, + self, + username: str, customer_id: UUID, key_selection: list[int], session_id: UUID @@ -71,10 +74,7 @@ class NKodeAPI(BaseModel): username=username, enciphered_passcode=enciphered_passcode, user_keys=new_user_keys, - user_interface=UserInterface( - interface=self.signup_sessions[session_id].confirm_interface, - keypad_size=customer.attributes.keypad_size - ), + user_interface=self.signup_sessions[session_id].login_interface, ) self.customers[customer_id].add_new_user(new_user) del self.signup_sessions[session_id] @@ -88,7 +88,7 @@ class NKodeAPI(BaseModel): customer = self.customers[customer_id] assert (username in customer.users.keys()) user = customer.users[username] - user.user_interface.shuffle_interface() + user.user_interface.partial_interface_shuffle() return user.user_interface.interface def login(self, customer_id: UUID, username: str, key_selection: list[int]) -> bool: diff --git a/nkode_tutorial.ipynb b/nkode_tutorial.ipynb index 4d96db9..d648c89 100644 --- a/nkode_tutorial.ipynb +++ b/nkode_tutorial.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 11, + "execution_count": 62, "outputs": [], "source": [ "from nkode_api import NKodeAPI\n", @@ -14,14 +14,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:26.168590Z", - "start_time": "2024-07-19T19:42:26.080917Z" + "end_time": "2024-07-22T16:43:14.323435Z", + "start_time": "2024-07-22T16:43:14.225885Z" } } }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 63, "outputs": [], "source": [ "def random_username() -> str:\n", @@ -40,14 +40,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:26.169484Z", - "start_time": "2024-07-19T19:42:26.084626Z" + "end_time": "2024-07-22T16:43:14.323797Z", + "start_time": "2024-07-22T16:43:14.229848Z" } } }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 64, "outputs": [], "source": [ "api = NKodeAPI()" @@ -55,8 +55,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:26.169561Z", - "start_time": "2024-07-19T19:42:26.087253Z" + "end_time": "2024-07-22T16:43:14.323871Z", + "start_time": "2024-07-22T16:43:14.232313Z" } } }, @@ -79,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 65, "outputs": [], "source": [ "policy = NKodePolicy(\n", @@ -89,8 +89,8 @@ " distinct_attributes=4\n", ")\n", "keypad_size = KeypadSize(\n", - " numb_of_keys = 10,\n", - " attrs_per_key = 7 # aka number of sets\n", + " numb_of_keys = 5,\n", + " attrs_per_key = 4 # aka number of sets\n", ")\n", "customer_id = api.create_new_customer(keypad_size, policy)\n", "customer = api.customers[customer_id]" @@ -98,8 +98,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:26.266945Z", - "start_time": "2024-07-19T19:42:26.091559Z" + "end_time": "2024-07-22T16:43:14.414667Z", + "start_time": "2024-07-22T16:43:14.238210Z" } } }, @@ -126,14 +126,14 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 66, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Customer Sets: [36939, 30293, 19380, 1964, 37282, 10031, 35065]\n", - "Customer Attributes: [3505, 54543, 45347, 44748, 53903, 52815, 60524, 9975, 48764, 34550, 64903, 32542, 23911, 35423, 63534, 65510, 21500, 37871, 19901, 6884, 255, 17031, 63424, 28925, 34144, 42192, 31390, 40124, 17254, 48729, 39518, 57631, 43182, 61064, 4422, 53465, 55843, 37538, 60243, 21929, 51240, 24244, 27507, 57663, 45226, 31543, 58101, 37770, 62668, 49947, 64348, 20373, 64786, 14379, 13970, 11323, 3662, 62709, 37802, 27817, 60687, 22963, 42846, 3620, 9784, 48482, 13855, 60342, 34564, 45989]\n" + "Customer Sets: [6259, 41347, 52298, 30406]\n", + "Customer Attributes: [65233, 63177, 42010, 46768, 5724, 12484, 7294, 20676, 9411, 8047, 18067, 6808, 51196, 46992, 15995, 34735, 37237, 57815, 17974, 10378]\n" ] } ], @@ -146,8 +146,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:43.930527Z", - "start_time": "2024-07-19T19:42:43.924673Z" + "end_time": "2024-07-22T16:43:14.431062Z", + "start_time": "2024-07-22T16:43:14.413572Z" } } }, @@ -162,20 +162,17 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 67, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set to Attribute Map:\n", - "36939: [3505, 9975, 63534, 17031, 17254, 53465, 27507, 49947, 3662, 3620]\n", - "30293: [54543, 48764, 65510, 63424, 48729, 55843, 57663, 64348, 62709, 9784]\n", - "19380: [45347, 34550, 21500, 28925, 39518, 37538, 45226, 20373, 37802, 48482]\n", - "1964: [44748, 64903, 37871, 34144, 57631, 60243, 31543, 64786, 27817, 13855]\n", - "37282: [53903, 32542, 19901, 42192, 43182, 21929, 58101, 14379, 60687, 60342]\n", - "10031: [52815, 23911, 6884, 31390, 61064, 51240, 37770, 13970, 22963, 34564]\n", - "35065: [60524, 35423, 255, 40124, 4422, 24244, 62668, 11323, 42846, 45989]\n" + "6259: [65233, 5724, 9411, 51196, 37237]\n", + "41347: [63177, 12484, 8047, 46992, 57815]\n", + "52298: [42010, 7294, 18067, 15995, 17974]\n", + "30406: [46768, 20676, 6808, 34735, 10378]\n" ] } ], @@ -190,8 +187,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:46.177582Z", - "start_time": "2024-07-19T19:42:46.173094Z" + "end_time": "2024-07-22T16:43:14.431341Z", + "start_time": "2024-07-22T16:43:14.417167Z" } } }, @@ -206,7 +203,7 @@ "\n", "#### Generate Index Interface\n", "- random interface is generated. Run the cell below over and over to see it change. Notice that values never move out of their columns jus their rows.\n", - "- each value in the interface is the index value of an attribute in the customer interface\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": { @@ -215,16 +212,26 @@ }, { "cell_type": "code", - "execution_count": 18, - "outputs": [], + "execution_count": 68, + "outputs": [ + { + "data": { + "text/plain": "[[4, 1, 10, 3],\n [8, 5, 2, 15],\n [0, 13, 14, 11],\n [16, 9, 6, 7],\n [12, 17, 18, 19]]" + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "session_id, signup_interface = api.generate_index_interface(customer_id)" + "session_id, signup_interface = api.generate_index_interface(customer_id)\n", + "list_to_matrix(signup_interface, keypad_size.attrs_per_key)" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:47.644598Z", - "start_time": "2024-07-19T19:42:47.640134Z" + "end_time": "2024-07-22T16:43:14.431562Z", + "start_time": "2024-07-22T16:43:14.420566Z" } } }, @@ -232,7 +239,7 @@ "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 12, 2, 52, 28. Graphiclly represent with anything. The only requirment is that the graphical must be associated with the same number everytime the user goes to login. If the user wants to change anything about their interface, they must also change their nkode." + "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. Graphiclly represent with anything. The only requirment is that the graphical must be associated with the same number everytime the user goes to login. If the user wants to change anything about their interface, they must also change their nkode." ], "metadata": { "collapsed": false @@ -240,64 +247,54 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 69, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [63, 1, 16, 59, 32, 19, 34]\n", - "Key 1: [49, 57, 65, 66, 4, 47, 48]\n", - "Key 2: [28, 50, 58, 10, 11, 5, 27]\n", - "Key 3: [21, 43, 51, 52, 25, 68, 41]\n", - "Key 4: [7, 64, 2, 38, 60, 54, 62]\n", - "Key 5: [56, 29, 23, 31, 67, 61, 20]\n", - "Key 6: [42, 15, 9, 24, 53, 26, 13]\n", - "Key 7: [14, 36, 30, 45, 46, 33, 69]\n", - "Key 8: [0, 22, 44, 17, 39, 40, 55]\n", - "Key 9: [35, 8, 37, 3, 18, 12, 6]\n", + "Key 0: [4, 1, 10, 3]\n", + "Key 1: [8, 5, 2, 15]\n", + "Key 2: [0, 13, 14, 11]\n", + "Key 3: [16, 9, 6, 7]\n", + "Key 4: [12, 17, 18, 19]\n", "Selected Keys\n", - "[9, 4, 3, 2]\n" + "[3, 3, 3, 4]\n" ] } ], "source": [ "keypad_view(signup_interface, keypad_size.attrs_per_key)\n", "username = random_username()\n", - "user_passcode = [12, 2, 52, 28]\n", + "user_passcode = [16, 9, 6, 19]\n", "selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.attrs_per_key)\n", "print(f\"Selected Keys\\n{selected_keys_set}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:48.948115Z", - "start_time": "2024-07-19T19:42:48.942682Z" + "end_time": "2024-07-22T16:43:14.431673Z", + "start_time": "2024-07-22T16:43:14.424684Z" } } }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 70, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [49, 22, 23, 59, 11, 12, 62]\n", - "Key 1: [42, 64, 44, 52, 46, 5, 6]\n", - "Key 2: [56, 8, 2, 66, 32, 33, 27]\n", - "Key 3: [0, 50, 37, 24, 25, 19, 69]\n", - "Key 4: [7, 36, 58, 31, 4, 68, 34]\n", - "Key 5: [35, 1, 30, 17, 53, 47, 41]\n", - "Key 6: [28, 43, 16, 38, 67, 26, 48]\n", - "Key 7: [14, 57, 51, 3, 39, 61, 13]\n", - "Key 8: [63, 15, 65, 10, 60, 40, 20]\n", - "Key 9: [21, 29, 9, 45, 18, 54, 55]\n", + "Key 0: [12, 9, 14, 15]\n", + "Key 1: [0, 17, 2, 3]\n", + "Key 2: [8, 13, 10, 7]\n", + "Key 3: [4, 5, 6, 19]\n", + "Key 4: [16, 1, 18, 11]\n", "Selected Keys\n", - "[0, 2, 1, 6]\n" + "[4, 0, 3, 3]\n" ] } ], @@ -310,8 +307,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:49.532737Z", - "start_time": "2024-07-19T19:42:49.528687Z" + "end_time": "2024-07-22T16:43:14.431769Z", + "start_time": "2024-07-22T16:43:14.427713Z" } } }, @@ -329,7 +326,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 71, "outputs": [ { "name": "stdout", @@ -337,16 +334,11 @@ "text": [ "Set Interface\n", "Keypad View\n", - "Key 0: [63, 1, 16, 59, 32, 19, 34]\n", - "Key 1: [49, 57, 65, 66, 4, 47, 48]\n", - "Key 2: [28, 50, 58, 10, 11, 5, 27]\n", - "Key 3: [21, 43, 51, 52, 25, 68, 41]\n", - "Key 4: [7, 64, 2, 38, 60, 54, 62]\n", - "Key 5: [56, 29, 23, 31, 67, 61, 20]\n", - "Key 6: [42, 15, 9, 24, 53, 26, 13]\n", - "Key 7: [14, 36, 30, 45, 46, 33, 69]\n", - "Key 8: [0, 22, 44, 17, 39, 40, 55]\n", - "Key 9: [35, 8, 37, 3, 18, 12, 6]\n" + "Key 0: [4, 1, 10, 3]\n", + "Key 1: [8, 5, 2, 15]\n", + "Key 2: [0, 13, 14, 11]\n", + "Key 3: [16, 9, 6, 7]\n", + "Key 4: [12, 17, 18, 19]\n" ] } ], @@ -357,14 +349,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:50.432534Z", - "start_time": "2024-07-19T19:42:50.427816Z" + "end_time": "2024-07-22T16:43:14.447860Z", + "start_time": "2024-07-22T16:43:14.430888Z" } } }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 72, "outputs": [ { "name": "stdout", @@ -372,16 +364,11 @@ "text": [ "Confirm Interface\n", "Keypad View\n", - "Key 0: [49, 22, 23, 59, 11, 12, 62]\n", - "Key 1: [42, 64, 44, 52, 46, 5, 6]\n", - "Key 2: [56, 8, 2, 66, 32, 33, 27]\n", - "Key 3: [0, 50, 37, 24, 25, 19, 69]\n", - "Key 4: [7, 36, 58, 31, 4, 68, 34]\n", - "Key 5: [35, 1, 30, 17, 53, 47, 41]\n", - "Key 6: [28, 43, 16, 38, 67, 26, 48]\n", - "Key 7: [14, 57, 51, 3, 39, 61, 13]\n", - "Key 8: [63, 15, 65, 10, 60, 40, 20]\n", - "Key 9: [21, 29, 9, 45, 18, 54, 55]\n" + "Key 0: [12, 9, 14, 15]\n", + "Key 1: [0, 17, 2, 3]\n", + "Key 2: [8, 13, 10, 7]\n", + "Key 3: [4, 5, 6, 19]\n", + "Key 4: [16, 1, 18, 11]\n" ] } ], @@ -392,14 +379,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:50.897855Z", - "start_time": "2024-07-19T19:42:50.894734Z" + "end_time": "2024-07-22T16:43:14.448110Z", + "start_time": "2024-07-22T16:43:14.433535Z" } } }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 73, "outputs": [ { "name": "stdout", @@ -420,8 +407,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:51.884002Z", - "start_time": "2024-07-19T19:42:51.198018Z" + "end_time": "2024-07-22T16:43:15.081248Z", + "start_time": "2024-07-22T16:43:14.437249Z" } } }, @@ -438,14 +425,14 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 73, "outputs": [], "source": [], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:53.411190Z", - "start_time": "2024-07-19T19:42:53.406912Z" + "end_time": "2024-07-22T16:43:15.083482Z", + "start_time": "2024-07-22T16:43:15.078924Z" } } }, @@ -471,7 +458,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 74, "outputs": [ { "data": { @@ -513,19 +500,19 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:55.684088Z", - "start_time": "2024-07-19T19:42:55.680817Z" + "end_time": "2024-07-22T16:43:15.097910Z", + "start_time": "2024-07-22T16:43:15.084617Z" } } }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 75, "outputs": [ { "data": { "text/plain": "", - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|5|50|55|500|\n|key2|3|30|33|300|\n|key3|2|20|22|200|\n|key4|4|40|44|400|\n|key5|1|10|11|100|" + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|4|40|44|400|\n|key2|2|20|22|200|\n|key3|1|10|11|100|\n|key4|5|50|55|500|\n|key5|3|30|33|300|" }, "metadata": {}, "output_type": "display_data" @@ -540,19 +527,19 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:56.543819Z", - "start_time": "2024-07-19T19:42:56.537207Z" + "end_time": "2024-07-22T16:43:15.098089Z", + "start_time": "2024-07-22T16:43:15.088214Z" } } }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 76, "outputs": [ { "data": { "text/plain": "", - "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|3|40|11|200|\n|key2|2|10|55|400|\n|key3|4|50|33|100|\n|key4|1|30|22|500|\n|key5|5|20|44|300|" + "text/markdown": "|key|set0|set1|set2|set3|\n|-|-|-|-|-|\n|key1|2|10|44|500|\n|key2|1|50|22|300|\n|key3|5|30|11|400|\n|key4|3|40|55|200|\n|key5|4|20|33|100|" }, "metadata": {}, "output_type": "display_data" @@ -570,8 +557,65 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T19:42:56.940543Z", - "start_time": "2024-07-19T19:42:56.935742Z" + "end_time": "2024-07-22T16:43:15.098218Z", + "start_time": "2024-07-22T16:43:15.091198Z" + } + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 80, + "outputs": [ + { + "data": { + "text/plain": "'$2b$12$M2kUbtPVmpphEHJIw/AKc.v96GcyR9WqPzNcbQqSHJWjiZJ6NwqMm'" + }, + "execution_count": 80, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "user_hash = list(api.customers[customer_id].users.values())[0].enciphered_passcode.code\n", + "user_hash\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-07-22T16:46:22.182625Z", + "start_time": "2024-07-22T16:46:22.178757Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 84, + "outputs": [ + { + "data": { + "text/plain": "[27172,\n 59678,\n 42816,\n 42517,\n 14869,\n 3862,\n 33437,\n 50365,\n 1299,\n 423,\n 16708,\n 23065,\n 1118,\n 54789,\n 22038,\n 22603,\n 36630,\n 18246,\n 48084,\n 32460]" + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(api.customers[customer_id].users.values())[0].user_keys.alpha_key\n", + "\n" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-07-22T16:48:52.733419Z", + "start_time": "2024-07-22T16:48:52.728228Z" } } }, @@ -581,10 +625,7 @@ "outputs": [], "source": [], "metadata": { - "collapsed": false, - "ExecuteTime": { - "start_time": "2024-07-19T19:42:26.284802Z" - } + "collapsed": false } } ], diff --git a/src/customer.py b/src/customer.py index 98837d4..05db63e 100644 --- a/src/customer.py +++ b/src/customer.py @@ -12,6 +12,9 @@ class Customer(BaseModel): attributes: CustomerAttributes users: dict[str, User] + # TODO: validate policy and keypad size don't conflict + + def add_new_user(self, user: User): self.users[user.username] = user diff --git a/src/customer_attributes.py b/src/customer_attributes.py index af04e6c..768af45 100644 --- a/src/customer_attributes.py +++ b/src/customer_attributes.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel +from pydantic import BaseModel, model_validator from src.models import KeypadSize from src.utils import generate_random_nonrepeating_list @@ -9,6 +9,13 @@ class CustomerAttributes(BaseModel): set_vals: list[int] keypad_size: KeypadSize + @model_validator(mode='after') + def check_keys_vs_attrs(self): + 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) diff --git a/src/models.py b/src/models.py index 322f2f0..2d4dd7a 100644 --- a/src/models.py +++ b/src/models.py @@ -16,7 +16,7 @@ class NKodePolicy(BaseModel): min_nkode_len: int = 4 distinct_sets: int = 0 distinct_attributes: int = 4 - # TODO: bytes_per_attr + byte_len: int = 2 # Todo: this should change the total number of bytes an attribute or set value can be class KeypadSize(BaseModel): @@ -26,3 +26,7 @@ class KeypadSize(BaseModel): @property def numb_of_attrs(self) -> int: return self.attrs_per_key * self.numb_of_keys + + @property + def is_dispersable(self) -> bool: + return self.attrs_per_key <= self.numb_of_keys diff --git a/src/user_interface.py b/src/user_interface.py index 2366c68..99681af 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -16,28 +16,55 @@ class UserInterface(BaseModel): interface=list(range(keypad_size.numb_of_attrs)), keypad_size=keypad_size ) - interface.disperse_interface() - for _ in range(10): - interface.shuffle_interface() + interface.random_interface_shuffle() return interface + def sign_up_interface(self): + assert (not self.keypad_size.is_dispersable) + self.random_interface_shuffle() + interface_matrix = self.interface_keypad_matrix() + attr_set_view = matrix_transpose(interface_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_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 random_interface_shuffle(self): + keypad_view = self.interface_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) + def disperse_interface(self): + assert (self.keypad_size.is_dispersable) user_interface_matrix = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) - attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.attrs_per_key] + 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( shuffled_keys, attr_rotation, ) self.interface = matrix_to_list(dispersed_interface) - def shuffle_interface(self): + def partial_interface_shuffle(self): 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 = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) + user_interface_matrix = self.interface_keypad_matrix() shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) interface_by_sets = [] for idx, attrs in enumerate(matrix_transpose(shuffled_keys)): @@ -60,7 +87,7 @@ class UserInterface(BaseModel): return matrix_transpose(transposed_user_interface) def attribute_adjacency_graph(self) -> dict[int, set[int]]: - user_interface_keypad = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) + user_interface_keypad = self.interface_keypad_matrix() graph = {} for key in user_interface_keypad: for attr in key: @@ -71,5 +98,5 @@ class UserInterface(BaseModel): def get_key_attr_idxs(self, key_numb: int) -> list[int]: assert (0 <= key_numb < self.keypad_size.numb_of_keys) - keypad_attr_idx = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) + keypad_attr_idx = self.interface_keypad_matrix() return keypad_attr_idx[key_numb] diff --git a/src/user_signup_session.py b/src/user_signup_session.py index d1b5bcb..2bedf59 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -9,6 +9,8 @@ from src.models import KeypadSize 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 diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index dd0d275..a84399d 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -8,17 +8,17 @@ def nkode_api() -> NKodeAPI: return NKodeAPI() -@pytest.mark.parametrize("keypad_size,user_passcode", [ - (KeypadSize(numb_of_keys=10, attrs_per_key=7), [3, 10, 27, 68]), - (KeypadSize(numb_of_keys=10, attrs_per_key=7), [3, 10, 27, 68, 32]), +@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), ]) -def test_create_new_user_and_renew_keys(nkode_api, keypad_size, user_passcode): +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_index_interface(customer_id) - - key_selection = lambda interface: [interface.index(attr) // keypad_size.attrs_per_key for attr in user_passcode] + user_passcode = set_interface[:passocode_len] + key_selection = lambda interface: [interface.index(attr) // keypad_size.numb_of_keys for attr in user_passcode] set_key_selection = key_selection(set_interface) confirm_interface = nkode_api.set_nkode(username, customer_id, set_key_selection, session_id) @@ -31,6 +31,7 @@ def test_create_new_user_and_renew_keys(nkode_api, keypad_size, user_passcode): ) assert successful_confirm + key_selection = lambda interface: [interface.index(attr) // keypad_size.attrs_per_key for attr in user_passcode] login_interface = nkode_api.get_login_index_interface(username, customer_id) login_key_selection = key_selection(login_interface) 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 07f161d..05f048d 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=7)] + [KeypadSize(numb_of_keys=10, attrs_per_key=11)] ) def test_attr_set_idx(keypad_size): user_interface = UserInterface.new(keypad_size) - for attr_idx in range(70): + for attr_idx in range(keypad_size.numb_of_attrs): user_interface_idx = user_interface.interface[attr_idx] assert (attr_idx % keypad_size.attrs_per_key == user_interface_idx % keypad_size.attrs_per_key) diff --git a/test/test_user_cipher_keys.py b/test/test_user_cipher_keys.py index 7177e25..528551d 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -22,9 +22,9 @@ def test_encode_decode_base64(passcode_len): @pytest.mark.parametrize( "keypad_size,max_nkode_len", [ - (KeypadSize(numb_of_keys=10, attrs_per_key=7), 10), - (KeypadSize(numb_of_keys=9, attrs_per_key=7), 10), - (KeypadSize(numb_of_keys=8, attrs_per_key=7), 12), + (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), ]) def test_decode_mask(keypad_size, max_nkode_len): customer = CustomerAttributes.new(keypad_size) diff --git a/test/test_user_interface.py b/test/test_user_interface.py index 8191420..b371b1b 100644 --- a/test/test_user_interface.py +++ b/test/test_user_interface.py @@ -26,7 +26,7 @@ def test_shuffle_attrs(user_interface): - 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.shuffle_interface() + user_interface.partial_interface_shuffle() post_shuffle_interface = user_interface.interface for i in range(1000): assert (not all(