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()