diff --git a/nkode_api.py b/nkode_api.py index 0a3aaf3..87cf657 100644 --- a/nkode_api.py +++ b/nkode_api.py @@ -2,7 +2,9 @@ from uuid import UUID, uuid4 from pydantic import BaseModel from src.customer import Customer -from src.models import NKodePolicy +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.customer_interface import CustomerInterface @@ -12,10 +14,10 @@ class NKodeAPI(BaseModel): customers: dict[UUID, Customer] = {} signup_sessions: dict[UUID, UserSignupSession] = {} - def create_new_customer(self, numb_keys: int, numb_sets: int, nkode_policy: NKodePolicy) -> UUID: + def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( customer_id=uuid4(), - interface=CustomerInterface.new(numb_keys, numb_sets), + interface=CustomerInterface.new(keypad_size), users={}, nkode_policy=nkode_policy ) @@ -26,16 +28,16 @@ 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.interface.attrs_per_key, customer.interface.numb_of_keys) + set_interface = UserInterface.new(customer.interface.keypad_size) new_session = UserSignupSession( session_id=uuid4(), set_interface=set_interface.attr_indices, customer_id=customer_id, + keypad_size=customer.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, customer_id: UUID, @@ -46,7 +48,7 @@ class NKodeAPI(BaseModel): customer = self.customers[customer_id] assert (username not in customer.users.keys()) assert (session_id in self.signup_sessions.keys()) - self.signup_sessions[session_id].set_user_nkode(username, customer, key_selection) + self.signup_sessions[session_id].set_user_nkode(username, key_selection) return self.signup_sessions[session_id].confirm_interface def confirm_nkode(self, username: str, customer_id: UUID, confirm_key_entry: list[int], session_id: UUID) -> bool: @@ -56,7 +58,24 @@ class NKodeAPI(BaseModel): username == self.signup_sessions[session_id].username ) customer = self.customers[customer_id] - new_user = self.signup_sessions[session_id].create_user(username, customer, confirm_key_entry) + + passcode = self.signup_sessions[session_id].deduce_passcode(confirm_key_entry) + new_user_keys = UserCipherKeys.new( + customer.interface.keypad_size, + customer.interface.set_vals, + customer.nkode_policy.max_nkode_len + ) + + enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.interface) + new_user = User( + username=username, + enciphered_passcode=enciphered_passcode, + user_keys=new_user_keys, + user_interface=UserInterface( + attr_indices=self.signup_sessions[session_id].confirm_interface, + keypad_size=customer.interface.keypad_size + ), + ) self.customers[customer_id].add_new_user(new_user) del self.signup_sessions[session_id] return True @@ -80,4 +99,3 @@ class NKodeAPI(BaseModel): def renew_keys(self, customer_id: UUID) -> bool: assert (customer_id in self.customers.keys()) return self.customers[customer_id].renew_keys() - diff --git a/nkode_tutorial.ipynb b/nkode_tutorial.ipynb index f4d19ee..a16303b 100644 --- a/nkode_tutorial.ipynb +++ b/nkode_tutorial.ipynb @@ -6,10 +6,7 @@ "outputs": [], "source": [ "from nkode_api import NKodeAPI\n", - "from src.models import NKodePolicy\n", - "from src.customer import Customer\n", - "from src.user import User\n", - "from src.user_interface import UserInterface\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" @@ -17,8 +14,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.673846Z", - "start_time": "2024-07-18T13:08:45.604900Z" + "end_time": "2024-07-19T15:38:32.636706Z", + "start_time": "2024-07-19T15:38:32.541836Z" } } }, @@ -43,8 +40,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.677402Z", - "start_time": "2024-07-18T13:08:45.675407Z" + "end_time": "2024-07-19T15:38:32.636920Z", + "start_time": "2024-07-19T15:38:32.606667Z" } } }, @@ -58,8 +55,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.679610Z", - "start_time": "2024-07-18T13:08:45.677796Z" + "end_time": "2024-07-19T15:38:32.636991Z", + "start_time": "2024-07-19T15:38:32.608558Z" } } }, @@ -91,16 +88,18 @@ " distinct_sets=0,\n", " distinct_attributes=4\n", ")\n", - "numb_of_keys = 10\n", - "attrs_per_key = 7 # aka number of sets\n", - "customer_id = api.create_new_customer(numb_of_keys, attrs_per_key, policy)\n", + "keypad_size = KeypadSize(\n", + " numb_of_keys = 10,\n", + " attrs_per_key = 7 # aka number of sets\n", + ")\n", + "customer_id = api.create_new_customer(keypad_size, policy)\n", "customer = api.customers[customer_id]" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.808776Z", - "start_time": "2024-07-18T13:08:45.681012Z" + "end_time": "2024-07-19T15:38:32.817740Z", + "start_time": "2024-07-19T15:38:32.612174Z" } } }, @@ -133,8 +132,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Sets: [60082, 3086, 26494, 12194, 4421, 41427, 16984]\n", - "Customer Attributes: [48380, 48810, 58704, 61681, 27187, 45406, 40741, 28469, 63290, 23552, 9625, 12681, 33702, 3372, 24715, 12612, 13180, 49133, 20493, 58247, 36394, 17390, 56641, 5490, 8213, 63651, 8692, 1703, 15711, 6304, 61079, 31429, 6078, 25130, 3897, 40313, 9253, 64106, 39159, 34587, 23277, 34925, 2070, 21493, 34778, 24158, 59424, 36, 9350, 60729, 46451, 15041, 16164, 41230, 23733, 12735, 33488, 31609, 35924, 43823, 50711, 32364, 41357, 13184, 2619, 33798, 56624, 61788, 24651, 12767]\n" + "Customer Sets: [63949, 41634, 17125, 52762, 13459, 5935, 727]\n", + "Customer Attributes: [52222, 57050, 17039, 29896, 31069, 35859, 36843, 2137, 24380, 34643, 29053, 46327, 39534, 64877, 53617, 14001, 39532, 19103, 38549, 42982, 63429, 11337, 42520, 23186, 49849, 6237, 49291, 44219, 34592, 26781, 25410, 34134, 51292, 40084, 49002, 1221, 30724, 62975, 33074, 22158, 18970, 54242, 15906, 19581, 10796, 6147, 36402, 44152, 43216, 16186, 571, 32657, 20955, 10260, 51931, 46321, 40259, 1905, 14430, 48389, 35797, 48564, 32173, 50142, 65402, 11082, 39195, 46929, 18113, 52721]\n" ] } ], @@ -147,8 +146,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.812291Z", - "start_time": "2024-07-18T13:08:45.809694Z" + "end_time": "2024-07-19T15:38:32.818026Z", + "start_time": "2024-07-19T15:38:32.781094Z" } } }, @@ -170,18 +169,18 @@ "output_type": "stream", "text": [ "Set to Attribute Map:\n", - "60082: [48380, 28469, 24715, 17390, 15711, 40313, 2070, 60729, 33488, 13184]\n", - "3086: [48810, 63290, 12612, 56641, 6304, 9253, 21493, 46451, 31609, 2619]\n", - "26494: [58704, 23552, 13180, 5490, 61079, 64106, 34778, 15041, 35924, 33798]\n", - "12194: [61681, 9625, 49133, 8213, 31429, 39159, 24158, 16164, 43823, 56624]\n", - "4421: [27187, 12681, 20493, 63651, 6078, 34587, 59424, 41230, 50711, 61788]\n", - "41427: [45406, 33702, 58247, 8692, 25130, 23277, 36, 23733, 32364, 24651]\n", - "16984: [40741, 3372, 36394, 1703, 3897, 34925, 9350, 12735, 41357, 12767]\n" + "63949: [52222, 2137, 53617, 11337, 34592, 1221, 15906, 16186, 40259, 50142]\n", + "41634: [57050, 24380, 14001, 42520, 26781, 30724, 19581, 571, 1905, 65402]\n", + "17125: [17039, 34643, 39532, 23186, 25410, 62975, 10796, 32657, 14430, 11082]\n", + "52762: [29896, 29053, 19103, 49849, 34134, 33074, 6147, 20955, 48389, 39195]\n", + "13459: [31069, 46327, 38549, 6237, 51292, 22158, 36402, 10260, 35797, 46929]\n", + "5935: [35859, 39534, 42982, 49291, 40084, 18970, 44152, 51931, 48564, 18113]\n", + "727: [36843, 64877, 63429, 44219, 49002, 54242, 43216, 46321, 32173, 52721]\n" ] } ], "source": [ - "attr_keypad_view = list_to_matrix(attr_vals, customer.interface.attrs_per_key)\n", + "attr_keypad_view = list_to_matrix(attr_vals, keypad_size.attrs_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", @@ -191,8 +190,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.815009Z", - "start_time": "2024-07-18T13:08:45.812687Z" + "end_time": "2024-07-19T15:38:32.818150Z", + "start_time": "2024-07-19T15:38:32.784103Z" } } }, @@ -224,8 +223,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.818253Z", - "start_time": "2024-07-18T13:08:45.815525Z" + "end_time": "2024-07-19T15:38:32.818223Z", + "start_time": "2024-07-19T15:38:32.787316Z" } } }, @@ -248,33 +247,33 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [21, 64, 2, 59, 46, 68, 48]\n", - "Key 1: [7, 1, 44, 66, 39, 33, 41]\n", - "Key 2: [0, 8, 37, 38, 60, 47, 69]\n", - "Key 3: [56, 50, 58, 52, 25, 61, 55]\n", - "Key 4: [28, 36, 65, 31, 18, 26, 34]\n", - "Key 5: [63, 29, 23, 17, 67, 40, 13]\n", - "Key 6: [49, 43, 9, 10, 53, 19, 62]\n", - "Key 7: [35, 57, 51, 3, 11, 54, 6]\n", - "Key 8: [42, 22, 16, 45, 4, 5, 27]\n", - "Key 9: [14, 15, 30, 24, 32, 12, 20]\n", + "Key 0: [14, 50, 16, 59, 39, 26, 6]\n", + "Key 1: [56, 36, 37, 52, 18, 47, 41]\n", + "Key 2: [49, 43, 2, 66, 60, 54, 69]\n", + "Key 3: [7, 8, 44, 3, 11, 68, 13]\n", + "Key 4: [63, 57, 65, 45, 53, 12, 48]\n", + "Key 5: [0, 22, 23, 10, 4, 40, 20]\n", + "Key 6: [35, 15, 51, 31, 67, 5, 55]\n", + "Key 7: [42, 29, 9, 38, 32, 33, 62]\n", + "Key 8: [21, 1, 58, 24, 46, 19, 34]\n", + "Key 9: [28, 64, 30, 17, 25, 61, 27]\n", "Selected Keys\n", - "[9, 0, 3, 4]\n" + "[4, 2, 1, 9]\n" ] } ], "source": [ - "keypad_view(signup_interface, attrs_per_key)\n", + "keypad_view(signup_interface, keypad_size.attrs_per_key)\n", "username = random_username()\n", "user_passcode = [12, 2, 52, 28]\n", - "selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, attrs_per_key)\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-18T13:08:45.820991Z", - "start_time": "2024-07-18T13:08:45.818928Z" + "end_time": "2024-07-19T15:38:32.818320Z", + "start_time": "2024-07-19T15:38:32.791137Z" } } }, @@ -287,32 +286,32 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [42, 1, 65, 59, 11, 19, 69]\n", - "Key 1: [14, 64, 23, 52, 53, 33, 34]\n", - "Key 2: [0, 50, 51, 45, 39, 68, 13]\n", - "Key 3: [28, 22, 9, 24, 46, 61, 6]\n", - "Key 4: [63, 15, 44, 38, 25, 5, 62]\n", - "Key 5: [35, 8, 2, 31, 4, 12, 41]\n", - "Key 6: [49, 36, 58, 17, 32, 47, 48]\n", - "Key 7: [7, 29, 16, 3, 60, 26, 55]\n", - "Key 8: [21, 57, 30, 10, 18, 40, 27]\n", - "Key 9: [56, 43, 37, 66, 67, 54, 20]\n", + "Key 0: [7, 29, 51, 66, 53, 47, 27]\n", + "Key 1: [21, 15, 2, 59, 18, 33, 20]\n", + "Key 2: [63, 43, 16, 17, 32, 5, 13]\n", + "Key 3: [56, 50, 30, 10, 67, 54, 34]\n", + "Key 4: [42, 64, 23, 3, 60, 26, 48]\n", + "Key 5: [35, 22, 44, 24, 39, 61, 41]\n", + "Key 6: [49, 8, 58, 45, 25, 40, 62]\n", + "Key 7: [14, 1, 65, 52, 4, 68, 55]\n", + "Key 8: [28, 57, 37, 38, 11, 19, 69]\n", + "Key 9: [0, 36, 9, 31, 46, 12, 6]\n", "Selected Keys\n", - "[5, 5, 1, 3]\n" + "[9, 1, 7, 8]\n" ] } ], "source": [ "confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id)\n", - "keypad_view(confirm_interface, attrs_per_key)\n", - "selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, attrs_per_key)\n", + "keypad_view(confirm_interface, keypad_size.attrs_per_key)\n", + "selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, keypad_size.attrs_per_key)\n", "print(f\"Selected Keys\\n{selected_keys_confirm}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.823981Z", - "start_time": "2024-07-18T13:08:45.821403Z" + "end_time": "2024-07-19T15:38:32.818413Z", + "start_time": "2024-07-19T15:38:32.794201Z" } } }, @@ -338,28 +337,28 @@ "text": [ "Set Interface\n", "Keypad View\n", - "Key 0: [21, 64, 2, 59, 46, 68, 48]\n", - "Key 1: [7, 1, 44, 66, 39, 33, 41]\n", - "Key 2: [0, 8, 37, 38, 60, 47, 69]\n", - "Key 3: [56, 50, 58, 52, 25, 61, 55]\n", - "Key 4: [28, 36, 65, 31, 18, 26, 34]\n", - "Key 5: [63, 29, 23, 17, 67, 40, 13]\n", - "Key 6: [49, 43, 9, 10, 53, 19, 62]\n", - "Key 7: [35, 57, 51, 3, 11, 54, 6]\n", - "Key 8: [42, 22, 16, 45, 4, 5, 27]\n", - "Key 9: [14, 15, 30, 24, 32, 12, 20]\n" + "Key 0: [14, 50, 16, 59, 39, 26, 6]\n", + "Key 1: [56, 36, 37, 52, 18, 47, 41]\n", + "Key 2: [49, 43, 2, 66, 60, 54, 69]\n", + "Key 3: [7, 8, 44, 3, 11, 68, 13]\n", + "Key 4: [63, 57, 65, 45, 53, 12, 48]\n", + "Key 5: [0, 22, 23, 10, 4, 40, 20]\n", + "Key 6: [35, 15, 51, 31, 67, 5, 55]\n", + "Key 7: [42, 29, 9, 38, 32, 33, 62]\n", + "Key 8: [21, 1, 58, 24, 46, 19, 34]\n", + "Key 9: [28, 64, 30, 17, 25, 61, 27]\n" ] } ], "source": [ "print(\"Set Interface\")\n", - "keypad_view(signup_interface, attrs_per_key)" + "keypad_view(signup_interface, keypad_size.attrs_per_key)" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.826730Z", - "start_time": "2024-07-18T13:08:45.824633Z" + "end_time": "2024-07-19T15:38:32.818504Z", + "start_time": "2024-07-19T15:38:32.797246Z" } } }, @@ -373,28 +372,28 @@ "text": [ "Confirm Interface\n", "Keypad View\n", - "Key 0: [42, 1, 65, 59, 11, 19, 69]\n", - "Key 1: [14, 64, 23, 52, 53, 33, 34]\n", - "Key 2: [0, 50, 51, 45, 39, 68, 13]\n", - "Key 3: [28, 22, 9, 24, 46, 61, 6]\n", - "Key 4: [63, 15, 44, 38, 25, 5, 62]\n", - "Key 5: [35, 8, 2, 31, 4, 12, 41]\n", - "Key 6: [49, 36, 58, 17, 32, 47, 48]\n", - "Key 7: [7, 29, 16, 3, 60, 26, 55]\n", - "Key 8: [21, 57, 30, 10, 18, 40, 27]\n", - "Key 9: [56, 43, 37, 66, 67, 54, 20]\n" + "Key 0: [7, 29, 51, 66, 53, 47, 27]\n", + "Key 1: [21, 15, 2, 59, 18, 33, 20]\n", + "Key 2: [63, 43, 16, 17, 32, 5, 13]\n", + "Key 3: [56, 50, 30, 10, 67, 54, 34]\n", + "Key 4: [42, 64, 23, 3, 60, 26, 48]\n", + "Key 5: [35, 22, 44, 24, 39, 61, 41]\n", + "Key 6: [49, 8, 58, 45, 25, 40, 62]\n", + "Key 7: [14, 1, 65, 52, 4, 68, 55]\n", + "Key 8: [28, 57, 37, 38, 11, 19, 69]\n", + "Key 9: [0, 36, 9, 31, 46, 12, 6]\n" ] } ], "source": [ "print(\"Confirm Interface\")\n", - "keypad_view(confirm_interface, attrs_per_key)" + "keypad_view(confirm_interface, keypad_size.attrs_per_key)" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:45.829497Z", - "start_time": "2024-07-18T13:08:45.826898Z" + "end_time": "2024-07-19T15:38:32.818596Z", + "start_time": "2024-07-19T15:38:32.799553Z" } } }, @@ -421,8 +420,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:46.334453Z", - "start_time": "2024-07-18T13:08:45.830468Z" + "end_time": "2024-07-19T15:38:33.435016Z", + "start_time": "2024-07-19T15:38:32.803552Z" } } }, @@ -445,8 +444,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:46.336225Z", - "start_time": "2024-07-18T13:08:46.334763Z" + "end_time": "2024-07-19T15:38:33.451250Z", + "start_time": "2024-07-19T15:38:33.435255Z" } } }, @@ -469,8 +468,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-18T13:08:46.344268Z", - "start_time": "2024-07-18T13:08:46.338692Z" + "end_time": "2024-07-19T15:38:33.451441Z", + "start_time": "2024-07-19T15:38:33.438804Z" } } } diff --git a/src/customer.py b/src/customer.py index 48cd6e4..c88d177 100644 --- a/src/customer.py +++ b/src/customer.py @@ -17,7 +17,7 @@ class Customer(BaseModel): def valid_key_entry(self, username, selected_keys) -> bool: assert (username in self.users.keys()) - assert (all(key_idx < self.interface.numb_of_keys for key_idx in selected_keys)) + assert (all(key_idx < self.interface.keypad_size.numb_of_keys for key_idx in selected_keys)) passcode_len = len(selected_keys) user = self.users[username] diff --git a/src/customer_interface.py b/src/customer_interface.py index aa06abe..1938942 100644 --- a/src/customer_interface.py +++ b/src/customer_interface.py @@ -1,33 +1,33 @@ from pydantic import BaseModel -from src.utils import generate_random_nonrepeating_list, list_to_matrix, matrix_transpose + +from src.models import KeypadSize +from src.utils import generate_random_nonrepeating_list class CustomerInterface(BaseModel): attr_vals: list[int] set_vals: list[int] - numb_of_keys: int - attrs_per_key: int + keypad_size: KeypadSize @staticmethod - def new(numb_keys: int, attrs_per_key: int): - assert (numb_keys <= 256) - assert (attrs_per_key <= 256) + def new(keypad_size: KeypadSize): + assert (keypad_size.numb_of_keys <= 256) + assert (keypad_size.attrs_per_key <= 256) return CustomerInterface( - attr_vals=generate_random_nonrepeating_list(attrs_per_key * numb_keys), - set_vals=generate_random_nonrepeating_list(attrs_per_key), - numb_of_keys=numb_keys, - attrs_per_key=attrs_per_key, + 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_interface(self): - self.attr_vals = generate_random_nonrepeating_list(self.attrs_per_key * self.numb_of_keys) - self.set_vals = generate_random_nonrepeating_list(self.attrs_per_key) + 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.attrs_per_key + 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: diff --git a/src/models.py b/src/models.py index f5eb97a..322f2f0 100644 --- a/src/models.py +++ b/src/models.py @@ -17,3 +17,12 @@ class NKodePolicy(BaseModel): distinct_sets: int = 0 distinct_attributes: int = 4 # TODO: bytes_per_attr + + +class KeypadSize(BaseModel): + attrs_per_key: int + numb_of_keys: int + + @property + def numb_of_attrs(self) -> int: + return self.attrs_per_key * self.numb_of_keys diff --git a/src/user.py b/src/user.py index 9ef2c82..aec61e9 100644 --- a/src/user.py +++ b/src/user.py @@ -21,8 +21,7 @@ class User(BaseModel): def refresh_passcode(self, passcode_attr_idx: list[int], customer_interface: CustomerInterface): self.user_keys = UserCipherKeys.new( - customer_interface.numb_of_keys, - customer_interface.attrs_per_key, + 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 779cf89..4bf4011 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher_keys.py @@ -4,7 +4,7 @@ import bcrypt from secrets import choice from pydantic import BaseModel -from src.models import EncipheredNKode +from src.models import EncipheredNKode, KeypadSize from src.customer_interface import CustomerInterface from src.utils import generate_random_nonrepeating_list, xor_lists, int_array_to_bytes @@ -18,14 +18,14 @@ class UserCipherKeys(BaseModel): max_nkode_len: int @staticmethod - def new(numb_of_keys: int, attrs_per_key: int, set_values: list[int], max_nkode_len: int): - assert len(set_values) == attrs_per_key + def new(keypad_size: KeypadSize, set_values: list[int], max_nkode_len: int): + assert len(set_values) == keypad_size.attrs_per_key - set_key = generate_random_nonrepeating_list(attrs_per_key) + set_key = generate_random_nonrepeating_list(keypad_size.attrs_per_key) set_key = xor_lists(set_key, set_values) return UserCipherKeys( - alpha_key=generate_random_nonrepeating_list(attrs_per_key * numb_of_keys), + alpha_key=generate_random_nonrepeating_list(keypad_size.attrs_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, diff --git a/src/user_interface.py b/src/user_interface.py index 74578a4..41bf5f8 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -1,20 +1,20 @@ from pydantic import BaseModel 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): attr_indices: list[int] - attrs_per_key: int - numb_of_keys: int + keypad_size: KeypadSize @staticmethod - def new(attrs_per_key: int, numb_of_keys: int): + def new(keypad_size: KeypadSize): # Todo: this a hack do a proper random interface interface = UserInterface( - attr_indices=list(range(attrs_per_key * numb_of_keys)), - attrs_per_key=attrs_per_key, - numb_of_keys=numb_of_keys, + attr_indices=list(range(keypad_size.numb_of_attrs)), + keypad_size=keypad_size ) interface.disperse_interface() for _ in range(10): @@ -22,18 +22,18 @@ class UserInterface(BaseModel): return interface def disperse_interface(self): - user_interface_matrix = list_to_matrix(self.attr_indices, self.attrs_per_key) + user_interface_matrix = list_to_matrix(self.attr_indices, self.keypad_size.attrs_per_key) shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) - dispersed_interface = self._random_attribute_rotation(shuffled_keys, list(range(self.attrs_per_key))) + dispersed_interface = self._random_attribute_rotation(shuffled_keys, list(range(self.keypad_size.attrs_per_key))) self.attr_indices = matrix_to_list(dispersed_interface) def shuffle_interface(self): """just like dispersion but only half the sets are rotated""" - numb_of_selected_sets = self.attrs_per_key // 2 + 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.attrs_per_key & 1) == 1 else 0 - selected_sets = secure_fisher_yates_shuffle(list(range(self.attrs_per_key)))[:numb_of_selected_sets] - user_interface_matrix = list_to_matrix(self.attr_indices, self.attrs_per_key) + 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.attr_indices, self.keypad_size.attrs_per_key) shuffled_keys = secure_fisher_yates_shuffle(user_interface_matrix) interface_by_sets = [] for idx, attrs in enumerate(matrix_transpose(shuffled_keys)): @@ -44,7 +44,7 @@ class UserInterface(BaseModel): self.attr_indices = matrix_to_list(matrix_transpose(interface_by_sets)) def _random_attribute_rotation(self, user_interface: list[list[int]], selected_sets: list[int]) -> list[list[int]]: - attr_rotation = secure_fisher_yates_shuffle(list(range(self.numb_of_keys)))[:self.attrs_per_key] + attr_rotation = secure_fisher_yates_shuffle(list(range(self.keypad_size.numb_of_keys)))[:self.keypad_size.attrs_per_key] transposed_user_interface = matrix_transpose(user_interface) assert (len(attr_rotation) == len(transposed_user_interface)) for idx, attr_set in enumerate(transposed_user_interface): @@ -55,7 +55,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.attr_indices, self.attrs_per_key) + user_interface_keypad = list_to_matrix(self.attr_indices, self.keypad_size.attrs_per_key) graph = {} for key in user_interface_keypad: for attr in key: @@ -65,6 +65,6 @@ class UserInterface(BaseModel): return graph def get_key_attr_idxs(self, key_numb: int) -> list[int]: - assert (0 <= key_numb < self.numb_of_keys) - keypad_attr_idx = list_to_matrix(self.attr_indices, self.attrs_per_key) + assert (0 <= key_numb < self.keypad_size.numb_of_keys) + keypad_attr_idx = list_to_matrix(self.attr_indices, self.keypad_size.attrs_per_key) return keypad_attr_idx[key_numb] diff --git a/src/user_signup_session.py b/src/user_signup_session.py index 5e2b207..3ae389f 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -2,22 +2,23 @@ from uuid import UUID from pydantic import BaseModel -from src.customer import Customer -from src.user import User -from src.user_cipher_keys import UserCipherKeys from src.user_interface import UserInterface +from src.models import KeypadSize class UserSignupSession(BaseModel): session_id: UUID customer_id: UUID + keypad_size: KeypadSize set_interface: list[int] | None = None confirm_interface: list[int] | None = None set_key_entry: list[int] | None = None - confirm_key_entry: list[int] | None = None username: str | None = None - def _deduce_passcode(self, attrs_per_key, confirm_key_entry: list[int]) -> list[int]: + 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)) + 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)) set_interface = self.set_interface @@ -35,37 +36,14 @@ class UserSignupSession(BaseModel): passcode.append(intersection[0]) return passcode - def set_user_nkode(self, username: str, customer: Customer, key_selection: list[int]): - assert (customer.customer_id == self.customer_id) - numb_of_keys = customer.interface.numb_of_keys - attrs_per_key = customer.interface.attrs_per_key - assert (all(0 <= key <= numb_of_keys for key in key_selection)) + 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)) set_interface = UserInterface( attr_indices=self.set_interface, - attrs_per_key=attrs_per_key, - numb_of_keys=numb_of_keys, + keypad_size=self.keypad_size ) set_interface.disperse_interface() self.username = username self.set_key_entry = key_selection self.confirm_interface = set_interface.attr_indices - def create_user(self, username: str, customer: Customer, confirm_key_entry: list[int]) -> User: - numb_of_keys = customer.interface.numb_of_keys - attrs_per_key = customer.interface.attrs_per_key - assert (all(0 <= key <= numb_of_keys for key in confirm_key_entry)) - passcode_attrs = self._deduce_passcode(attrs_per_key, confirm_key_entry) - set_values = customer.interface.set_vals - assert customer.valid_new_nkode(passcode_attrs) - new_user_keys = UserCipherKeys.new(numb_of_keys, attrs_per_key, set_values, customer.nkode_policy.max_nkode_len) - enciphered_passcode = new_user_keys.encipher_nkode(passcode_attrs, customer.interface) - return User( - username=username, - enciphered_passcode=enciphered_passcode, - user_keys=new_user_keys, - user_interface=UserInterface( - attr_indices=self.confirm_interface, - attrs_per_key=attrs_per_key, - numb_of_keys=numb_of_keys, - ), - ) diff --git a/test/test_nkode_api.py b/test/test_nkode_api.py index 61b63ce..dd0d275 100644 --- a/test/test_nkode_api.py +++ b/test/test_nkode_api.py @@ -1,6 +1,6 @@ import pytest from nkode_api import NKodeAPI -from src.models import NKodePolicy +from src.models import NKodePolicy, KeypadSize @pytest.fixture() @@ -8,17 +8,17 @@ def nkode_api() -> NKodeAPI: return NKodeAPI() -@pytest.mark.parametrize("numb_of_keys,attrs_per_key,user_passcode", [ - (10, 7, [3, 10, 27, 68]), - (12, 6, [3, 10, 27, 68, 32]), +@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]), ]) -def test_create_new_user_and_renew_keys(nkode_api, numb_of_keys, attrs_per_key, user_passcode): +def test_create_new_user_and_renew_keys(nkode_api, keypad_size, user_passcode): username = "test_username" nkode_policy = NKodePolicy() # default policy - customer_id = nkode_api.create_new_customer(numb_of_keys, attrs_per_key, nkode_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) // attrs_per_key for attr in user_passcode] + key_selection = lambda interface: [interface.index(attr) // keypad_size.attrs_per_key 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) diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index 1519eef..0e96ecf 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -1,14 +1,15 @@ import pytest from src.user_interface import UserInterface +from src.models import KeypadSize @pytest.mark.parametrize( - "numb_of_keys,attrs_per_key", - [(10, 7,)] + "keypad_size", + [KeypadSize(numb_of_keys=10, attrs_per_key=7)] ) -def test_attr_set_idx(numb_of_keys, attrs_per_key): - user_interface = UserInterface.new(attrs_per_key, numb_of_keys) +def test_attr_set_idx(keypad_size): + user_interface = UserInterface.new(keypad_size) for attr_idx in range(70): user_interface_idx = user_interface.attr_indices[attr_idx] - assert (attr_idx % attrs_per_key == user_interface_idx % attrs_per_key) + 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 db07366..f4f4ceb 100644 --- a/test/test_user_cipher_keys.py +++ b/test/test_user_cipher_keys.py @@ -1,4 +1,6 @@ import pytest + +from src.models import KeypadSize from src.user_cipher_keys import UserCipherKeys, CustomerInterface from src.utils import generate_random_nonrepeating_list @@ -18,20 +20,20 @@ def test_encode_decode_base64(passcode_len): @pytest.mark.parametrize( - "numb_of_keys,attrs_per_key,max_nkode_len", + "keypad_size,max_nkode_len", [ - (10, 7, 10), - (9, 7, 10), - (8, 7, 12), + (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), ]) -def test_decode_mask(numb_of_keys, attrs_per_key, max_nkode_len): - customer = CustomerInterface.new(numb_of_keys, attrs_per_key) +def test_decode_mask(keypad_size, max_nkode_len): + customer = CustomerInterface.new(keypad_size) passcode_entry = generate_random_nonrepeating_list( - numb_of_keys * attrs_per_key, - max_val=(numb_of_keys*attrs_per_key))[:4] + 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(numb_of_keys, attrs_per_key, set_vals, max_nkode_len) + user_keys = UserCipherKeys.new(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 d240e52..471ab22 100644 --- a/test/test_user_interface.py +++ b/test/test_user_interface.py @@ -1,10 +1,11 @@ import pytest from src.user_interface import UserInterface +from src.models import KeypadSize @pytest.fixture() def user_interface(): - return UserInterface.new(7, 10) + return UserInterface.new(keypad_size=KeypadSize(attrs_per_key=7, numb_of_keys=10)) def test_dispersion(user_interface):