From 50dc917a907349df9125161e7a8282ba29bc982e Mon Sep 17 00:00:00 2001 From: Donovan Date: Fri, 19 Jul 2024 14:43:15 -0500 Subject: [PATCH] refactor customer interface to attributes --- nkode_api.py | 22 +- nkode_tutorial.ipynb | 304 ++++++++++++------ src/customer.py | 26 +- ...er_interface.py => customer_attributes.py} | 6 +- src/user.py | 4 +- src/user_cipher_keys.py | 8 +- src/user_interface.py | 33 +- src/user_signup_session.py | 4 +- test/test_nkode_interface.py | 2 +- test/test_user_cipher_keys.py | 4 +- test/test_user_interface.py | 4 +- 11 files changed, 268 insertions(+), 149 deletions(-) rename src/{customer_interface.py => customer_attributes.py} (91%) diff --git a/nkode_api.py b/nkode_api.py index 87cf657..17c0ef2 100644 --- a/nkode_api.py +++ b/nkode_api.py @@ -7,7 +7,7 @@ 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 +from src.customer_attributes import CustomerAttributes class NKodeAPI(BaseModel): @@ -17,7 +17,7 @@ class NKodeAPI(BaseModel): def create_new_customer(self, keypad_size: KeypadSize, nkode_policy: NKodePolicy) -> UUID: new_customer = Customer( customer_id=uuid4(), - interface=CustomerInterface.new(keypad_size), + attributes=CustomerAttributes.new(keypad_size), users={}, nkode_policy=nkode_policy ) @@ -28,12 +28,12 @@ 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.keypad_size) + set_interface = UserInterface.new(customer.attributes.keypad_size) new_session = UserSignupSession( session_id=uuid4(), - set_interface=set_interface.attr_indices, + set_interface=set_interface.interface, customer_id=customer_id, - keypad_size=customer.interface.keypad_size, + keypad_size=customer.attributes.keypad_size, ) self.signup_sessions[new_session.session_id] = new_session return new_session.session_id, new_session.set_interface @@ -61,19 +61,19 @@ class NKodeAPI(BaseModel): 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.attributes.keypad_size, + customer.attributes.set_vals, customer.nkode_policy.max_nkode_len ) - enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.interface) + enciphered_passcode = new_user_keys.encipher_nkode(passcode, customer.attributes) 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 + interface=self.signup_sessions[session_id].confirm_interface, + keypad_size=customer.attributes.keypad_size ), ) self.customers[customer_id].add_new_user(new_user) @@ -89,7 +89,7 @@ class NKodeAPI(BaseModel): assert (username in customer.users.keys()) user = customer.users[username] user.user_interface.shuffle_interface() - return user.user_interface.attr_indices + 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()) diff --git a/nkode_tutorial.ipynb b/nkode_tutorial.ipynb index a16303b..4d96db9 100644 --- a/nkode_tutorial.ipynb +++ b/nkode_tutorial.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 11, "outputs": [], "source": [ "from nkode_api import NKodeAPI\n", @@ -14,14 +14,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.636706Z", - "start_time": "2024-07-19T15:38:32.541836Z" + "end_time": "2024-07-19T19:42:26.168590Z", + "start_time": "2024-07-19T19:42:26.080917Z" } } }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 12, "outputs": [], "source": [ "def random_username() -> str:\n", @@ -40,14 +40,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.636920Z", - "start_time": "2024-07-19T15:38:32.606667Z" + "end_time": "2024-07-19T19:42:26.169484Z", + "start_time": "2024-07-19T19:42:26.084626Z" } } }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 13, "outputs": [], "source": [ "api = NKodeAPI()" @@ -55,8 +55,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.636991Z", - "start_time": "2024-07-19T15:38:32.608558Z" + "end_time": "2024-07-19T19:42:26.169561Z", + "start_time": "2024-07-19T19:42:26.087253Z" } } }, @@ -79,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 14, "outputs": [], "source": [ "policy = NKodePolicy(\n", @@ -98,8 +98,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.817740Z", - "start_time": "2024-07-19T15:38:32.612174Z" + "end_time": "2024-07-19T19:42:26.266945Z", + "start_time": "2024-07-19T19:42:26.091559Z" } } }, @@ -126,28 +126,28 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 16, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "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" + "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" ] } ], "source": [ - "set_vals = customer.interface.set_vals\n", - "attr_vals = customer.interface.attr_vals\n", + "set_vals = customer.attributes.set_vals\n", + "attr_vals = customer.attributes.attr_vals\n", "print(f\"Customer Sets: {set_vals}\")\n", "print(f\"Customer Attributes: {attr_vals}\")" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818026Z", - "start_time": "2024-07-19T15:38:32.781094Z" + "end_time": "2024-07-19T19:42:43.930527Z", + "start_time": "2024-07-19T19:42:43.924673Z" } } }, @@ -162,20 +162,20 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 17, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Set to Attribute Map:\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" + "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" ] } ], @@ -190,8 +190,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818150Z", - "start_time": "2024-07-19T15:38:32.784103Z" + "end_time": "2024-07-19T19:42:46.177582Z", + "start_time": "2024-07-19T19:42:46.173094Z" } } }, @@ -215,7 +215,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 18, "outputs": [], "source": [ "session_id, signup_interface = api.generate_index_interface(customer_id)" @@ -223,8 +223,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818223Z", - "start_time": "2024-07-19T15:38:32.787316Z" + "end_time": "2024-07-19T19:42:47.644598Z", + "start_time": "2024-07-19T19:42:47.640134Z" } } }, @@ -240,25 +240,25 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 19, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\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", + "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", "Selected Keys\n", - "[4, 2, 1, 9]\n" + "[9, 4, 3, 2]\n" ] } ], @@ -272,32 +272,32 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818320Z", - "start_time": "2024-07-19T15:38:32.791137Z" + "end_time": "2024-07-19T19:42:48.948115Z", + "start_time": "2024-07-19T19:42:48.942682Z" } } }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 20, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Keypad View\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", + "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", "Selected Keys\n", - "[9, 1, 7, 8]\n" + "[0, 2, 1, 6]\n" ] } ], @@ -310,8 +310,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818413Z", - "start_time": "2024-07-19T15:38:32.794201Z" + "end_time": "2024-07-19T19:42:49.532737Z", + "start_time": "2024-07-19T19:42:49.528687Z" } } }, @@ -329,7 +329,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 21, "outputs": [ { "name": "stdout", @@ -337,16 +337,16 @@ "text": [ "Set Interface\n", "Keypad View\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" + "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" ] } ], @@ -357,14 +357,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818504Z", - "start_time": "2024-07-19T15:38:32.797246Z" + "end_time": "2024-07-19T19:42:50.432534Z", + "start_time": "2024-07-19T19:42:50.427816Z" } } }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 22, "outputs": [ { "name": "stdout", @@ -372,16 +372,16 @@ "text": [ "Confirm Interface\n", "Keypad View\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" + "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" ] } ], @@ -392,14 +392,14 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:32.818596Z", - "start_time": "2024-07-19T15:38:32.799553Z" + "end_time": "2024-07-19T19:42:50.897855Z", + "start_time": "2024-07-19T19:42:50.894734Z" } } }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 23, "outputs": [ { "name": "stdout", @@ -420,8 +420,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:33.435016Z", - "start_time": "2024-07-19T15:38:32.803552Z" + "end_time": "2024-07-19T19:42:51.884002Z", + "start_time": "2024-07-19T19:42:51.198018Z" } } }, @@ -438,14 +438,14 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 23, "outputs": [], "source": [], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:33.451250Z", - "start_time": "2024-07-19T15:38:33.435255Z" + "end_time": "2024-07-19T19:42:53.411190Z", + "start_time": "2024-07-19T19:42:53.406912Z" } } }, @@ -460,16 +460,130 @@ "collapsed": false } }, + { + "cell_type": "markdown", + "source": [ + "## Dispersion" + ], + "metadata": { + "collapsed": false + } + }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 24, + "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\n", + "from src.user_interface import UserInterface\n", + "from IPython.display import Markdown, display\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", + "\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 += \"|\"\n", + " return table\n", + "\n", + "\n", + "keypad_size = KeypadSize(numb_of_keys=5, attrs_per_key=4)\n", + "attrs = [1, 10, 11, 100]\n", + "interface = []\n", + "for key_numb in range(1,keypad_size.numb_of_keys+1):\n", + " interface.extend([key_numb*attr for attr in attrs])\n", + "\n", + "demo_interface = UserInterface(keypad_size=keypad_size, interface=interface)\n", + "\n", + "display(Markdown(keypad_md_table(demo_interface.interface, keypad_size)))" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-07-19T19:42:55.684088Z", + "start_time": "2024-07-19T19:42:55.680817Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 25, + "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|" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "demo_interface_matrix = list_to_matrix(demo_interface.interface, 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" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-07-19T19:42:56.543819Z", + "start_time": "2024-07-19T19:42:56.537207Z" + } + } + }, + { + "cell_type": "code", + "execution_count": 26, + "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|" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "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", + " shuffled_keys,\n", + " attr_rotation\n", + ")\n", + "\n", + "display(Markdown(keypad_md_table(matrix_to_list(dispersed_interface), keypad_size)))" + ], + "metadata": { + "collapsed": false, + "ExecuteTime": { + "end_time": "2024-07-19T19:42:56.940543Z", + "start_time": "2024-07-19T19:42:56.935742Z" + } + } + }, + { + "cell_type": "code", + "execution_count": null, "outputs": [], "source": [], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2024-07-19T15:38:33.451441Z", - "start_time": "2024-07-19T15:38:33.438804Z" + "start_time": "2024-07-19T19:42:26.284802Z" } } } diff --git a/src/customer.py b/src/customer.py index c88d177..98837d4 100644 --- a/src/customer.py +++ b/src/customer.py @@ -1,6 +1,6 @@ from uuid import UUID from pydantic import BaseModel -from src.customer_interface import CustomerInterface +from src.customer_attributes import CustomerAttributes 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(BaseModel): customer_id: UUID nkode_policy: NKodePolicy - interface: CustomerInterface + attributes: CustomerAttributes users: dict[str, User] def add_new_user(self, user: User): @@ -17,13 +17,13 @@ class Customer(BaseModel): def valid_key_entry(self, username, selected_keys) -> bool: assert (username in self.users.keys()) - assert (all(key_idx < self.interface.keypad_size.numb_of_keys for key_idx in selected_keys)) + assert (all(key_idx < self.attributes.keypad_size.numb_of_keys for key_idx in selected_keys)) passcode_len = len(selected_keys) user = self.users[username] passcode_set_vals = user.user_keys.decipher_mask( - user.enciphered_passcode.mask, self.interface.set_vals, len(selected_keys)) - set_vals_idx = [self.interface.get_set_index(set_val) for set_val in passcode_set_vals] + user.enciphered_passcode.mask, self.attributes.set_vals, len(selected_keys)) + set_vals_idx = [self.attributes.get_set_index(set_val) for set_val in passcode_set_vals] presumed_selected_attributes = [] presumed_selected_attributes_idx = [] @@ -36,20 +36,20 @@ class Customer(BaseModel): presumed_selected_attributes_idx.append(selected_attr_idx) presumed_selected_attributes.append(selected_attr_idx) - enciphered_attr = user.user_keys.encipher_salt_hash_code(presumed_selected_attributes, self.interface) + enciphered_attr = user.user_keys.encipher_salt_hash_code(presumed_selected_attributes, self.attributes) if enciphered_attr != user.enciphered_passcode.code: return False if user.renew: - user.refresh_passcode(presumed_selected_attributes_idx, self.interface) + user.refresh_passcode(presumed_selected_attributes_idx, self.attributes) return True def renew_keys(self) -> bool: - attrs_before = self.interface.attr_vals.copy() - sets_before = self.interface.set_vals.copy() - self.interface.renew_interface() - attrs_after = self.interface.attr_vals - sets_after = self.interface.set_vals + attrs_before = self.attributes.attr_vals.copy() + sets_before = self.attributes.set_vals.copy() + self.attributes.renew() + attrs_after = self.attributes.attr_vals + sets_after = self.attributes.set_vals attrs_xor = xor_lists(attrs_after, attrs_before) set_xor = xor_lists(sets_after, sets_before) @@ -60,7 +60,7 @@ class Customer(BaseModel): def valid_new_nkode(self, passcode_attr_idx: list[int]) -> bool: nkode_len = len(passcode_attr_idx) passcode_set_values = [ - self.interface.get_attr_set_val(self.interface.attr_vals[attr_idx]) for attr_idx in passcode_attr_idx + self.attributes.get_attr_set_val(self.attributes.attr_vals[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_interface.py b/src/customer_attributes.py similarity index 91% rename from src/customer_interface.py rename to src/customer_attributes.py index 1938942..af04e6c 100644 --- a/src/customer_interface.py +++ b/src/customer_attributes.py @@ -4,7 +4,7 @@ from src.models import KeypadSize from src.utils import generate_random_nonrepeating_list -class CustomerInterface(BaseModel): +class CustomerAttributes(BaseModel): attr_vals: list[int] set_vals: list[int] keypad_size: KeypadSize @@ -14,13 +14,13 @@ class CustomerInterface(BaseModel): assert (keypad_size.numb_of_keys <= 256) assert (keypad_size.attrs_per_key <= 256) - return CustomerInterface( + return CustomerAttributes( 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): + 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) diff --git a/src/user.py b/src/user.py index aec61e9..e9c7b75 100644 --- a/src/user.py +++ b/src/user.py @@ -1,7 +1,7 @@ from pydantic import BaseModel from src.models import EncipheredNKode -from src.customer_interface import CustomerInterface +from src.customer_attributes import CustomerAttributes from src.user_cipher_keys import UserCipherKeys from src.user_interface import UserInterface from src.utils import xor_lists @@ -19,7 +19,7 @@ class User(BaseModel): 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: CustomerInterface): + def refresh_passcode(self, passcode_attr_idx: list[int], customer_interface: CustomerAttributes): self.user_keys = UserCipherKeys.new( customer_interface.keypad_size, customer_interface.set_vals, diff --git a/src/user_cipher_keys.py b/src/user_cipher_keys.py index 4bf4011..81a90b9 100644 --- a/src/user_cipher_keys.py +++ b/src/user_cipher_keys.py @@ -5,7 +5,7 @@ from secrets import choice from pydantic import BaseModel from src.models import EncipheredNKode, KeypadSize -from src.customer_interface import CustomerInterface +from src.customer_attributes import CustomerAttributes from src.utils import generate_random_nonrepeating_list, xor_lists, int_array_to_bytes @@ -63,7 +63,7 @@ class UserCipherKeys(BaseModel): def encipher_nkode( self, passcode_attr_idx: list[int], - customer_interface: CustomerInterface + customer_interface: CustomerAttributes ) -> EncipheredNKode: passcode_attrs = [customer_interface.attr_vals[idx] for idx in passcode_attr_idx] @@ -78,7 +78,7 @@ class UserCipherKeys(BaseModel): def encipher_salt_hash_code( self, passcode_attr_idx: list[int], - customer_interface: CustomerInterface, + customer_interface: CustomerAttributes, ) -> str: passcode_len = len(passcode_attr_idx) passcode_attrs = [customer_interface.attr_vals[idx] for idx in passcode_attr_idx] @@ -95,7 +95,7 @@ class UserCipherKeys(BaseModel): def encipher_mask( self, passcode_sets: list[int], - customer_interface: CustomerInterface + customer_interface: 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] diff --git a/src/user_interface.py b/src/user_interface.py index 41bf5f8..2366c68 100644 --- a/src/user_interface.py +++ b/src/user_interface.py @@ -6,14 +6,14 @@ from src.utils import list_to_matrix, secure_fisher_yates_shuffle, matrix_to_lis class UserInterface(BaseModel): - attr_indices: list[int] + interface: list[int] keypad_size: KeypadSize @staticmethod def new(keypad_size: KeypadSize): # Todo: this a hack do a proper random interface interface = UserInterface( - attr_indices=list(range(keypad_size.numb_of_attrs)), + interface=list(range(keypad_size.numb_of_attrs)), keypad_size=keypad_size ) interface.disperse_interface() @@ -22,18 +22,22 @@ class UserInterface(BaseModel): return interface def disperse_interface(self): - user_interface_matrix = list_to_matrix(self.attr_indices, self.keypad_size.attrs_per_key) + user_interface_matrix = list_to_matrix(self.interface, 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.keypad_size.attrs_per_key))) - self.attr_indices = matrix_to_list(dispersed_interface) + + 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): - """just like dispersion but only half the sets are rotated""" 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.attr_indices, self.keypad_size.attrs_per_key) + user_interface_matrix = list_to_matrix(self.interface, 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)): @@ -41,21 +45,22 @@ class UserInterface(BaseModel): interface_by_sets.append(secure_fisher_yates_shuffle(attrs)) else: interface_by_sets.append(attrs) - self.attr_indices = matrix_to_list(matrix_transpose(interface_by_sets)) + self.interface = 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.keypad_size.numb_of_keys)))[:self.keypad_size.attrs_per_key] + @staticmethod + def random_attribute_rotation( + user_interface: list[list[int]], + attr_rotation: list[int] + ) -> list[list[int]]: transposed_user_interface = matrix_transpose(user_interface) assert (len(attr_rotation) == len(transposed_user_interface)) for idx, attr_set in enumerate(transposed_user_interface): - if idx not in selected_sets: - continue rotation = attr_rotation[idx] transposed_user_interface[idx] = attr_set[rotation:] + attr_set[:rotation] 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.keypad_size.attrs_per_key) + user_interface_keypad = list_to_matrix(self.interface, self.keypad_size.attrs_per_key) graph = {} for key in user_interface_keypad: for attr in key: @@ -66,5 +71,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.attr_indices, self.keypad_size.attrs_per_key) + keypad_attr_idx = list_to_matrix(self.interface, 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 3ae389f..d1b5bcb 100644 --- a/src/user_signup_session.py +++ b/src/user_signup_session.py @@ -39,11 +39,11 @@ class UserSignupSession(BaseModel): 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, + interface=self.set_interface, 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 + self.confirm_interface = set_interface.interface diff --git a/test/test_nkode_interface.py b/test/test_nkode_interface.py index 0e96ecf..07f161d 100644 --- a/test/test_nkode_interface.py +++ b/test/test_nkode_interface.py @@ -10,6 +10,6 @@ from src.models import KeypadSize 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] + 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 f4f4ceb..7177e25 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, CustomerInterface +from src.user_cipher_keys import UserCipherKeys, CustomerAttributes from src.utils import generate_random_nonrepeating_list @@ -27,7 +27,7 @@ def test_encode_decode_base64(passcode_len): (KeypadSize(numb_of_keys=8, attrs_per_key=7), 12), ]) def test_decode_mask(keypad_size, max_nkode_len): - customer = CustomerInterface.new(keypad_size) + customer = CustomerAttributes.new(keypad_size) passcode_entry = generate_random_nonrepeating_list( keypad_size.numb_of_attrs, max_val=keypad_size.numb_of_attrs)[:4] diff --git a/test/test_user_interface.py b/test/test_user_interface.py index 471ab22..8191420 100644 --- a/test/test_user_interface.py +++ b/test/test_user_interface.py @@ -25,9 +25,9 @@ def test_shuffle_attrs(user_interface): - 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.attr_indices + pre_shuffle_interface = user_interface.interface user_interface.shuffle_interface() - post_shuffle_interface = user_interface.attr_indices + post_shuffle_interface = user_interface.interface for i in range(1000): assert (not all( post_shuffle_interface[idx] == pre_shuffle_interface[idx] for idx in range(len(post_shuffle_interface))