diff --git a/notebooks/nkode_tutorial.ipynb b/notebooks/nkode_tutorial.ipynb index fd831d3..513645a 100644 --- a/notebooks/nkode_tutorial.ipynb +++ b/notebooks/nkode_tutorial.ipynb @@ -7,17 +7,18 @@ "from src.models import NKodePolicy, KeypadSize\n", "from secrets import choice\n", "from string import ascii_lowercase\n", - "import numpy as np" + "import numpy as np\n", + "import bcrypt" ], "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.665193Z", - "start_time": "2025-03-16T11:17:06.662525Z" + "end_time": "2025-03-16T12:51:50.430038Z", + "start_time": "2025-03-16T12:51:50.426267Z" } }, "outputs": [], - "execution_count": 23 + "execution_count": 17 }, { "cell_type": "code", @@ -40,12 +41,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.680622Z", - "start_time": "2025-03-16T11:17:06.677352Z" + "end_time": "2025-03-16T12:51:50.441672Z", + "start_time": "2025-03-16T12:51:50.437979Z" } }, "outputs": [], - "execution_count": 24 + "execution_count": 18 }, { "cell_type": "code", @@ -55,12 +56,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.696721Z", - "start_time": "2025-03-16T11:17:06.694897Z" + "end_time": "2025-03-16T12:51:50.451921Z", + "start_time": "2025-03-16T12:51:50.449896Z" } }, "outputs": [], - "execution_count": 25 + "execution_count": 19 }, { "cell_type": "markdown", @@ -100,12 +101,12 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.708524Z", - "start_time": "2025-03-16T11:17:06.704492Z" + "end_time": "2025-03-16T12:51:50.462932Z", + "start_time": "2025-03-16T12:51:50.458899Z" } }, "outputs": [], - "execution_count": 26 + "execution_count": 20 }, { "cell_type": "markdown", @@ -138,8 +139,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.718027Z", - "start_time": "2025-03-16T11:17:06.715343Z" + "end_time": "2025-03-16T12:51:50.471838Z", + "start_time": "2025-03-16T12:51:50.469241Z" } }, "outputs": [ @@ -147,17 +148,17 @@ "name": "stdout", "output_type": "stream", "text": [ - "Customer Set Key: [51574 4745 25889 18231 60748 18510]\n", + "Customer Set Key: [29252 55146 27501 36339 61988 56812]\n", "Customer Properties Key:\n", - "[60468 25732 54355 40123 30642 22334]\n", - "[12163 30704 51913 11579 53335 30868]\n", - "[36499 37185 3991 26970 25932 19506]\n", - "[ 7000 7490 26410 33717 8308 41888]\n", - "[59350 41496 33957 33571 58466 45968]\n" + "[58958 438 23479 56544 19227 62335]\n", + "[19972 45333 49189 28952 25210 7101]\n", + "[27924 36712 27932 6192 11310 25890]\n", + "[41961 42300 62264 30572 21755 25167]\n", + "[56659 59117 55559 18611 58570 62570]\n" ] } ], - "execution_count": 27 + "execution_count": 21 }, { "cell_type": "markdown", @@ -177,8 +178,8 @@ "metadata": { "collapsed": false, "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.735483Z", - "start_time": "2025-03-16T11:17:06.733076Z" + "end_time": "2025-03-16T12:51:50.482832Z", + "start_time": "2025-03-16T12:51:50.480624Z" } }, "outputs": [ @@ -187,16 +188,16 @@ "output_type": "stream", "text": [ "Set to Properties Map:\n", - "51574: [60468 12163 36499 7000 59350]\n", - "4745: [25732 30704 37185 7490 41496]\n", - "25889: [54355 51913 3991 26410 33957]\n", - "18231: [40123 11579 26970 33717 33571]\n", - "60748: [30642 53335 25932 8308 58466]\n", - "18510: [22334 30868 19506 41888 45968]\n" + "29252: [58958 19972 27924 41961 56659]\n", + "55146: [ 438 45333 36712 42300 59117]\n", + "27501: [23479 49189 27932 62264 55559]\n", + "36339: [56544 28952 6192 30572 18611]\n", + "61988: [19227 25210 11310 21755 58570]\n", + "56812: [62335 7101 25890 25167 62570]\n" ] } ], - "execution_count": 28 + "execution_count": 22 }, { "metadata": {}, @@ -218,8 +219,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.760751Z", - "start_time": "2025-03-16T11:17:06.758403Z" + "end_time": "2025-03-16T12:51:50.496968Z", + "start_time": "2025-03-16T12:51:50.494830Z" } }, "cell_type": "code", @@ -232,15 +233,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "[[21 15 9 3 27]\n", - " [22 16 10 4 28]\n", - " [18 12 6 0 24]\n", - " [20 14 8 2 26]\n", - " [23 17 11 5 29]]\n" + "[[10 4 22 16 28]\n", + " [11 5 23 17 29]\n", + " [ 9 3 21 15 27]\n", + " [ 8 2 20 14 26]\n", + " [ 7 1 19 13 25]]\n" ] } ], - "execution_count": 29 + "execution_count": 23 }, { "metadata": {}, @@ -253,8 +254,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.783887Z", - "start_time": "2025-03-16T11:17:06.780577Z" + "end_time": "2025-03-16T12:51:50.521411Z", + "start_time": "2025-03-16T12:51:50.518203Z" } }, "cell_type": "code", @@ -275,25 +276,25 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [21 15 9 3 27]\n", - "Key 1: [22 16 10 4 28]\n", - "Key 2: [18 12 6 0 24]\n", - "Key 3: [20 14 8 2 26]\n", - "Key 4: [23 17 11 5 29]\n", - "User Passcode: [21 15 9 3]\n", + "Key 0: [10 4 22 16 28]\n", + "Key 1: [11 5 23 17 29]\n", + "Key 2: [ 9 3 21 15 27]\n", + "Key 3: [ 8 2 20 14 26]\n", + "Key 4: [ 7 1 19 13 25]\n", + "User Passcode: [10 4 22 16]\n", "Selected Keys\n", "[0, 0, 0, 0]\n", - "User Passcode Server-side properties: [33717, 26970, 11579, 40123]\n" + "User Passcode Server-side properties: [25210, 19227, 21755, 11310]\n" ] } ], - "execution_count": 30 + "execution_count": 24 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:06.799077Z", - "start_time": "2025-03-16T11:17:06.796555Z" + "end_time": "2025-03-16T12:51:50.539870Z", + "start_time": "2025-03-16T12:51:50.536979Z" } }, "cell_type": "code", @@ -309,23 +310,23 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [18 17 8 4 27]\n", - "Key 1: [23 16 6 3 26]\n", - "Key 2: [22 15 11 2 24]\n", - "Key 3: [21 14 10 0 29]\n", - "Key 4: [20 12 9 5 28]\n", + "Key 0: [ 7 2 22 15 29]\n", + "Key 1: [ 9 4 23 14 25]\n", + "Key 2: [ 8 5 19 16 27]\n", + "Key 3: [10 1 21 17 26]\n", + "Key 4: [11 3 20 13 28]\n", "Selected Keys\n", - "[3, 2, 4, 1]\n" + "[3, 1, 0, 2]\n" ] } ], - "execution_count": 31 + "execution_count": 25 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.061018Z", - "start_time": "2025-03-16T11:17:06.824471Z" + "end_time": "2025-03-16T12:51:50.790192Z", + "start_time": "2025-03-16T12:51:50.552031Z" } }, "cell_type": "code", @@ -343,7 +344,7 @@ ] } ], - "execution_count": 32 + "execution_count": 26 }, { "metadata": {}, @@ -362,8 +363,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.070338Z", - "start_time": "2025-03-16T11:17:07.066596Z" + "end_time": "2025-03-16T12:51:50.801004Z", + "start_time": "2025-03-16T12:51:50.797001Z" } }, "cell_type": "code", @@ -371,7 +372,7 @@ "from src.user_cipher import UserCipher\n", "\n", "user_set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\n", - "combined_set_key = np.bitwise_xor(user_set_key, customer.cipher.set_key)\n", + "combined_set_key = user_set_key ^ customer.cipher.set_key\n", "user_keys = UserCipher(\n", " prop_key = np.array([\n", " 57200, 8398, 54694, 25997, 30388,\n", @@ -384,8 +385,7 @@ " pass_key=np.array([16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206]),\n", " mask_key=np.array([29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325]),\n", " combined_set_key=np.array(combined_set_key),\n", - " salt=b'$2b$12$fX.in.GGAjz3QBBwqSWc6e',\n", - " max_nkode_len=customer.nkode_policy.max_nkode_len, \n", + " max_nkode_len=customer.nkode_policy.max_nkode_len,\n", ")\n", "\n", "ordered_customer_prop_key = customer.cipher.prop_key[user_passcode] # [int(customer.cipher.prop_key[idx]) for idx in user_passcode]\n", @@ -398,12 +398,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "Passcode Set Vals: [18231, 18231, 18231, 18231]\n", - "Passcode Prop Vals: [33717 26970 11579 40123]\n" + "Passcode Set Vals: [61988, 61988, 61988, 61988]\n", + "Passcode Prop Vals: [25210 19227 21755 11310]\n" ] } ], - "execution_count": 33 + "execution_count": 27 }, { "metadata": {}, @@ -416,16 +416,16 @@ "3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n", "where i is the index\n", " \n", - "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_set_key\n", - "- mask = mask_rand_num ^ set_val ^ set_rand_numb ^ set_val\n", + "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_combined_set_key\n", + "- mask = mask_key ^ (customer_set_key) ^ set_rand_numb ^ set_val\n", "- mask = mask_rand_num ^ set_rand_numb # set_val is cancelled out" ] }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.150818Z", - "start_time": "2025-03-16T11:17:07.148125Z" + "end_time": "2025-03-16T12:51:50.811181Z", + "start_time": "2025-03-16T12:51:50.808721Z" } }, "cell_type": "code", @@ -433,12 +433,11 @@ "padded_ordered_customer_set = user_keys.pad_user_mask(ordered_customer_set_key, customer.cipher.set_key)\n", "set_idx = [customer.cipher.get_set_index(set_val) for set_val in padded_ordered_customer_set]\n", "ordered_set_key = user_keys.combined_set_key[set_idx]\n", - "ciphered_mask = ordered_set_key ^ padded_ordered_customer_set\n", - "ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key)\n", - "mask = user_keys.encode_base64_str(ciphered_mask)" + "mask = ordered_set_key ^ padded_ordered_customer_set ^ user_keys.mask_key\n", + "encoded_mask = user_keys.encode_base64_str(mask)" ], "outputs": [], - "execution_count": 34 + "execution_count": 28 }, { "metadata": {}, @@ -455,16 +454,14 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.413741Z", - "start_time": "2025-03-16T11:17:07.177338Z" + "end_time": "2025-03-16T12:51:51.058527Z", + "start_time": "2025-03-16T12:51:50.823135Z" } }, "cell_type": "code", "source": [ - "import bcrypt\n", "import hashlib\n", "import base64\n", - "\n", "ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\n", "passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode]\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", @@ -473,20 +470,19 @@ "\n", "ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key)\n", "\n", - "#passcode_bytes = int_array_to_bytes(ciphered_code)\n", "passcode_bytes = ciphered_code.tobytes()\n", - "passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest())\n", - "hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt)\n", + "passcode_digest = user_keys.prehash_passcode(user_passcode, customer.cipher)# base64.b64encode(hashlib.sha256(passcode_bytes).digest())\n", + "hashed_data = bcrypt.hashpw(passcode_digest, bcrypt.gensalt(rounds=12))\n", "code = hashed_data.decode(\"utf-8\")" ], "outputs": [], - "execution_count": 35 + "execution_count": 29 }, { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.428200Z", - "start_time": "2025-03-16T11:17:07.426275Z" + "end_time": "2025-03-16T12:51:51.063978Z", + "start_time": "2025-03-16T12:51:51.061965Z" } }, "cell_type": "code", @@ -494,12 +490,12 @@ "from src.models import EncipheredNKode\n", "\n", "enciphered_nkode = EncipheredNKode(\n", - " mask=mask,\n", + " mask=encoded_mask,\n", " code=code,\n", ")" ], "outputs": [], - "execution_count": 36 + "execution_count": 30 }, { "metadata": {}, @@ -513,8 +509,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.668766Z", - "start_time": "2025-03-16T11:17:07.436169Z" + "end_time": "2025-03-16T12:51:51.305419Z", + "start_time": "2025-03-16T12:51:51.071190Z" } }, "cell_type": "code", @@ -532,17 +528,17 @@ "output_type": "stream", "text": [ "Keypad View\n", - "Key 0: [18 19 20 21 22 23]\n", - "Key 1: [12 13 14 15 16 17]\n", - "Key 2: [ 6 7 8 9 10 11]\n", - "Key 3: [0 1 2 3 4 5]\n", + "Key 0: [ 6 7 8 9 10 11]\n", + "Key 1: [0 1 2 3 4 5]\n", + "Key 2: [18 19 20 21 22 23]\n", + "Key 3: [12 13 14 15 16 17]\n", "Key 4: [24 25 26 27 28 29]\n", "Selected Keys: [0, 1, 2, 3]\n", "True\n" ] } ], - "execution_count": 37 + "execution_count": 31 }, { "metadata": {}, @@ -574,16 +570,16 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.679342Z", - "start_time": "2025-03-16T11:17:07.676500Z" + "end_time": "2025-03-16T12:51:51.315072Z", + "start_time": "2025-03-16T12:51:51.312249Z" } }, "cell_type": "code", "source": [ "user = api.customers[customer_id].users[username]\n", - "decoded_mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", - "deciphered_mask = np.bitwise_xor(decoded_mask, user.cipher.mask_key)\n", - "set_key = np.bitwise_xor(customer.cipher.set_key, user.cipher.combined_set_key)\n", + "mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", + "deciphered_mask = mask ^ user.cipher.mask_key\n", + "set_key = customer.cipher.set_key ^ user.cipher.combined_set_key\n", "passcode_sets = []\n", "for set_cipher in deciphered_mask[:passcode_len]:\n", " set_idx = np.where(set_key == set_cipher)[0][0]\n", @@ -595,11 +591,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[18231, 18231, 18231, 18231]\n" + "[61988, 61988, 61988, 61988]\n" ] } ], - "execution_count": 38 + "execution_count": 32 }, { "metadata": {}, @@ -609,8 +605,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.690303Z", - "start_time": "2025-03-16T11:17:07.687880Z" + "end_time": "2025-03-16T12:51:51.324331Z", + "start_time": "2025-03-16T12:51:51.321676Z" } }, "cell_type": "code", @@ -635,7 +631,7 @@ ] } ], - "execution_count": 39 + "execution_count": 33 }, { "metadata": {}, @@ -645,14 +641,14 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:07.937619Z", - "start_time": "2025-03-16T11:17:07.705824Z" + "end_time": "2025-03-16T12:51:51.571291Z", + "start_time": "2025-03-16T12:51:51.338129Z" } }, "cell_type": "code", "source": [ - "enciphered_nkode = user.cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher)\n", - "print(enciphered_nkode == user.enciphered_passcode.code)\n" + "enciphered_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n", + "print(enciphered_nkode)\n" ], "outputs": [ { @@ -663,7 +659,7 @@ ] } ], - "execution_count": 40 + "execution_count": 34 }, { "metadata": {}, @@ -679,8 +675,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.414223Z", - "start_time": "2025-03-16T11:17:07.946756Z" + "end_time": "2025-03-16T12:51:52.048827Z", + "start_time": "2025-03-16T12:51:51.579003Z" } }, "cell_type": "code", @@ -705,14 +701,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", - "mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", + "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n", + "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n", "True\n", - "mask: Q7Dw+o+qDTXYlg8TC2hOkn9r1s0=, code: $2b$12$Xp2oD/vFDHwzvBmELBxC6e.iLK4xn5ZtLwW4UCFVoFY2ioTvGaWwa\n" + "mask: rY763sxEo+aU8G2h7Avf9btV0h8=, code: $2b$12$ZB.x5hSFVjDg4niUAL7TsOtwkTG2uXedMsnFqXNjm0Y0SDf5fgOui\n" ] } ], - "execution_count": 41 + "execution_count": 35 }, { "metadata": {}, @@ -726,8 +722,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.424680Z", - "start_time": "2025-03-16T11:17:08.421148Z" + "end_time": "2025-03-16T12:51:52.059119Z", + "start_time": "2025-03-16T12:51:52.055816Z" } }, "cell_type": "code", @@ -739,7 +735,7 @@ "new_sets = customer.cipher.set_key" ], "outputs": [], - "execution_count": 42 + "execution_count": 36 }, { "metadata": {}, @@ -752,8 +748,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.434263Z", - "start_time": "2025-03-16T11:17:08.431951Z" + "end_time": "2025-03-16T12:51:52.068302Z", + "start_time": "2025-03-16T12:51:52.065921Z" } }, "cell_type": "code", @@ -766,7 +762,7 @@ " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" ], "outputs": [], - "execution_count": 43 + "execution_count": 37 }, { "metadata": {}, @@ -776,8 +772,8 @@ { "metadata": { "ExecuteTime": { - "end_time": "2025-03-16T11:17:08.683077Z", - "start_time": "2025-03-16T11:17:08.447107Z" + "end_time": "2025-03-16T12:51:52.311112Z", + "start_time": "2025-03-16T12:51:52.074783Z" } }, "cell_type": "code", @@ -791,7 +787,7 @@ "user.renew = False" ], "outputs": [], - "execution_count": 44 + "execution_count": 38 } ], "metadata": { diff --git a/src/customer.py b/src/customer.py index 6177ba9..c06ba55 100644 --- a/src/customer.py +++ b/src/customer.py @@ -41,8 +41,7 @@ class Customer: selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) presumed_property_idxs.append(selected_prop_idx) - enciphered_prop = user.cipher.encipher_salt_hash_code(presumed_property_idxs, self.cipher) - if enciphered_prop != user.enciphered_passcode.code: + if not user.cipher.compare_nkode(presumed_property_idxs, self.cipher,user.enciphered_passcode.code): return False if user.renew: diff --git a/src/user_cipher.py b/src/user_cipher.py index 5e2123a..1136d22 100644 --- a/src/user_cipher.py +++ b/src/user_cipher.py @@ -3,7 +3,6 @@ import hashlib from dataclasses import dataclass import bcrypt import numpy as np -from secrets import choice from src.models import EncipheredNKode, KeypadSize from src.customer_cipher import CustomerCipher @@ -14,83 +13,89 @@ class UserCipher: combined_set_key: np.ndarray pass_key: np.ndarray mask_key: np.ndarray - salt: bytes max_nkode_len: int @classmethod - def create(cls, keypad_size: KeypadSize, set_values: np.ndarray, max_nkode_len: int) -> 'UserCipher': - if len(set_values) != keypad_size.props_per_key: + def create(cls, keypad_size: KeypadSize, customer_set_key: np.ndarray, max_nkode_len: int) -> 'UserCipher': + if len(customer_set_key) != keypad_size.props_per_key: raise ValueError("Invalid set values") - set_values_array = np.array(set_values, dtype=np.uint16) - set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) - set_key = np.bitwise_xor(set_key, set_values_array) + user_set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False) return UserCipher( prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False), mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False), - combined_set_key=set_key, - salt=bcrypt.gensalt(), + combined_set_key=user_set_key ^ customer_set_key, max_nkode_len=max_nkode_len ) def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray: if len(user_mask) >= self.max_nkode_len: - raise ValueError("User mask is too long") + raise ValueError("User encoded_mask is too long") padding_size = self.max_nkode_len - len(user_mask) - # Generate padding directly using np.random.choice padding = np.random.choice(set_vals, size=padding_size, replace=True).astype(np.uint16) - # Concatenate original mask with padding return np.concatenate([user_mask, padding]) @staticmethod def encode_base64_str(data: np.ndarray) -> str: - return base64.b64encode(int_array_to_bytes(data)).decode("utf-8") + return base64.b64encode( b"".join([int(num).to_bytes(2, byteorder='big') for num in data])).decode("utf-8") @staticmethod def decode_base64_str(data: str) -> np.ndarray: byte_data = base64.b64decode(data) int_list = [] - for i in range(0, len(byte_data), 2): int_val = int.from_bytes(byte_data[i:i + 2], byteorder='big') int_list.append(int_val) return np.array(int_list, dtype=np.uint16) - def _hash_passcode(self, passcode: np.ndarray) -> str: - passcode_bytes = passcode.astype(np.uint16).tobytes() - passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) - hashed_data = bcrypt.hashpw(passcode_digest, self.salt) - return hashed_data.decode("utf-8") - def encipher_nkode( self, passcode_prop_idx: list[int], customer_cipher: CustomerCipher ) -> EncipheredNKode: mask = self.encipher_mask(passcode_prop_idx, customer_cipher) - code = self.encipher_salt_hash_code(passcode_prop_idx, customer_cipher) + code = self.hash_nkode(passcode_prop_idx, customer_cipher) return EncipheredNKode( code=code, mask=mask ) - def encipher_salt_hash_code( + def hash_nkode( self, passcode_prop_idx: list[int], customer_prop: CustomerCipher, ) -> str: + salt = bcrypt.gensalt(rounds=12) + passcode_bytes = self.prehash_passcode(passcode_prop_idx, customer_prop) + passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) + hashed_data = bcrypt.hashpw(passcode_digest, salt) + return hashed_data.decode("utf-8") + + def compare_nkode( + self, + passcode_prop_idx: list[int], + customer_prop: CustomerCipher, + hashed_passcode: str + ) -> bool: + passcode_bytes = self.prehash_passcode(passcode_prop_idx, customer_prop) + passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) + return bcrypt.checkpw(passcode_digest, hashed_passcode.encode('utf-8')) + + def prehash_passcode( + self, + passcode_prop_idx: list[int], + customer_prop: CustomerCipher + ) -> bytes: passcode_len = len(passcode_prop_idx) passcode_cipher = self.pass_key.copy() passcode_cipher[:passcode_len] = ( - passcode_cipher[:passcode_len] ^ - self.prop_key[passcode_prop_idx] ^ - customer_prop.prop_key[passcode_prop_idx] + passcode_cipher[:passcode_len] ^ + self.prop_key[passcode_prop_idx] ^ + customer_prop.prop_key[passcode_prop_idx] ) - - return self._hash_passcode(passcode_cipher) - + return passcode_cipher.astype(np.uint16).tobytes() def encipher_mask( self, @@ -100,33 +105,21 @@ class UserCipher: customer_props = customer_cipher.prop_key[passcode_prop_idx] customer_sets = [customer_cipher.get_prop_set_val(prop) for prop in customer_props] padded_customer_sets = self.pad_user_mask(np.array(customer_sets), customer_cipher.set_key) - - # Get indices of set values set_idx = np.array([customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets], dtype=np.uint16) - mask_set_keys = np.array([self.combined_set_key[idx] for idx in set_idx], dtype=np.uint16) + ordered_set_key = self.combined_set_key[set_idx] + mask = ordered_set_key ^ padded_customer_sets ^ self.mask_key + encoded_mask = self.encode_base64_str(mask) + return encoded_mask - # XOR operations - ciphered_mask = np.bitwise_xor(mask_set_keys, padded_customer_sets) - ciphered_mask = np.bitwise_xor(ciphered_mask, self.mask_key) - - mask = self.encode_base64_str(ciphered_mask) - return mask - - def decipher_mask(self, mask: str, set_vals: np.ndarray, passcode_len: int) -> np.ndarray: - set_vals_array = np.array(set_vals, dtype=np.uint16) - decoded_mask = self.decode_base64_str(mask) - deciphered_mask = np.bitwise_xor(decoded_mask, self.mask_key) - - set_key_rand_component = np.bitwise_xor(set_vals_array, self.combined_set_key) + def decipher_mask(self, encoded_mask: str, customer_set_key: np.ndarray, passcode_len: int) -> list[int]: + mask = self.decode_base64_str(encoded_mask) + # user_set_key ordered by the user's nkode and padded to be length max_nkode_len + ordered_set_key = mask ^ self.mask_key + user_set_key = customer_set_key ^ self.combined_set_key passcode_sets = [] + for partial_set in ordered_set_key[:passcode_len]: + set_idx = np.where(user_set_key == partial_set)[0][0] + passcode_sets.append(int(customer_set_key[set_idx])) + return passcode_sets - for set_cipher in deciphered_mask[:passcode_len]: - # Find index where values match - set_idx = np.where(set_key_rand_component == set_cipher)[0][0] - passcode_sets.append(set_vals[set_idx]) - - return np.array(passcode_sets) - -def int_array_to_bytes(int_arr: np.ndarray, byte_size: int = 2) -> bytes: - return b"".join([int(num).to_bytes(byte_size, byteorder='big') for num in int_arr]) \ No newline at end of file