remove salt from UserCipher

This commit is contained in:
2025-03-16 08:07:42 -05:00
parent b6ab0c1890
commit 2a19aa73c4
3 changed files with 169 additions and 181 deletions

View File

@@ -7,17 +7,18 @@
"from src.models import NKodePolicy, KeypadSize\n", "from src.models import NKodePolicy, KeypadSize\n",
"from secrets import choice\n", "from secrets import choice\n",
"from string import ascii_lowercase\n", "from string import ascii_lowercase\n",
"import numpy as np" "import numpy as np\n",
"import bcrypt"
], ],
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.665193Z", "end_time": "2025-03-16T12:51:50.430038Z",
"start_time": "2025-03-16T11:17:06.662525Z" "start_time": "2025-03-16T12:51:50.426267Z"
} }
}, },
"outputs": [], "outputs": [],
"execution_count": 23 "execution_count": 17
}, },
{ {
"cell_type": "code", "cell_type": "code",
@@ -40,12 +41,12 @@
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.680622Z", "end_time": "2025-03-16T12:51:50.441672Z",
"start_time": "2025-03-16T11:17:06.677352Z" "start_time": "2025-03-16T12:51:50.437979Z"
} }
}, },
"outputs": [], "outputs": [],
"execution_count": 24 "execution_count": 18
}, },
{ {
"cell_type": "code", "cell_type": "code",
@@ -55,12 +56,12 @@
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.696721Z", "end_time": "2025-03-16T12:51:50.451921Z",
"start_time": "2025-03-16T11:17:06.694897Z" "start_time": "2025-03-16T12:51:50.449896Z"
} }
}, },
"outputs": [], "outputs": [],
"execution_count": 25 "execution_count": 19
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
@@ -100,12 +101,12 @@
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.708524Z", "end_time": "2025-03-16T12:51:50.462932Z",
"start_time": "2025-03-16T11:17:06.704492Z" "start_time": "2025-03-16T12:51:50.458899Z"
} }
}, },
"outputs": [], "outputs": [],
"execution_count": 26 "execution_count": 20
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
@@ -138,8 +139,8 @@
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.718027Z", "end_time": "2025-03-16T12:51:50.471838Z",
"start_time": "2025-03-16T11:17:06.715343Z" "start_time": "2025-03-16T12:51:50.469241Z"
} }
}, },
"outputs": [ "outputs": [
@@ -147,17 +148,17 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "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", "Customer Properties Key:\n",
"[60468 25732 54355 40123 30642 22334]\n", "[58958 438 23479 56544 19227 62335]\n",
"[12163 30704 51913 11579 53335 30868]\n", "[19972 45333 49189 28952 25210 7101]\n",
"[36499 37185 3991 26970 25932 19506]\n", "[27924 36712 27932 6192 11310 25890]\n",
"[ 7000 7490 26410 33717 8308 41888]\n", "[41961 42300 62264 30572 21755 25167]\n",
"[59350 41496 33957 33571 58466 45968]\n" "[56659 59117 55559 18611 58570 62570]\n"
] ]
} }
], ],
"execution_count": 27 "execution_count": 21
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
@@ -177,8 +178,8 @@
"metadata": { "metadata": {
"collapsed": false, "collapsed": false,
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.735483Z", "end_time": "2025-03-16T12:51:50.482832Z",
"start_time": "2025-03-16T11:17:06.733076Z" "start_time": "2025-03-16T12:51:50.480624Z"
} }
}, },
"outputs": [ "outputs": [
@@ -187,16 +188,16 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Set to Properties Map:\n", "Set to Properties Map:\n",
"51574: [60468 12163 36499 7000 59350]\n", "29252: [58958 19972 27924 41961 56659]\n",
"4745: [25732 30704 37185 7490 41496]\n", "55146: [ 438 45333 36712 42300 59117]\n",
"25889: [54355 51913 3991 26410 33957]\n", "27501: [23479 49189 27932 62264 55559]\n",
"18231: [40123 11579 26970 33717 33571]\n", "36339: [56544 28952 6192 30572 18611]\n",
"60748: [30642 53335 25932 8308 58466]\n", "61988: [19227 25210 11310 21755 58570]\n",
"18510: [22334 30868 19506 41888 45968]\n" "56812: [62335 7101 25890 25167 62570]\n"
] ]
} }
], ],
"execution_count": 28 "execution_count": 22
}, },
{ {
"metadata": {}, "metadata": {},
@@ -218,8 +219,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.760751Z", "end_time": "2025-03-16T12:51:50.496968Z",
"start_time": "2025-03-16T11:17:06.758403Z" "start_time": "2025-03-16T12:51:50.494830Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -232,15 +233,15 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"[[21 15 9 3 27]\n", "[[10 4 22 16 28]\n",
" [22 16 10 4 28]\n", " [11 5 23 17 29]\n",
" [18 12 6 0 24]\n", " [ 9 3 21 15 27]\n",
" [20 14 8 2 26]\n", " [ 8 2 20 14 26]\n",
" [23 17 11 5 29]]\n" " [ 7 1 19 13 25]]\n"
] ]
} }
], ],
"execution_count": 29 "execution_count": 23
}, },
{ {
"metadata": {}, "metadata": {},
@@ -253,8 +254,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.783887Z", "end_time": "2025-03-16T12:51:50.521411Z",
"start_time": "2025-03-16T11:17:06.780577Z" "start_time": "2025-03-16T12:51:50.518203Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -275,25 +276,25 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Keypad View\n", "Keypad View\n",
"Key 0: [21 15 9 3 27]\n", "Key 0: [10 4 22 16 28]\n",
"Key 1: [22 16 10 4 28]\n", "Key 1: [11 5 23 17 29]\n",
"Key 2: [18 12 6 0 24]\n", "Key 2: [ 9 3 21 15 27]\n",
"Key 3: [20 14 8 2 26]\n", "Key 3: [ 8 2 20 14 26]\n",
"Key 4: [23 17 11 5 29]\n", "Key 4: [ 7 1 19 13 25]\n",
"User Passcode: [21 15 9 3]\n", "User Passcode: [10 4 22 16]\n",
"Selected Keys\n", "Selected Keys\n",
"[0, 0, 0, 0]\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": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:06.799077Z", "end_time": "2025-03-16T12:51:50.539870Z",
"start_time": "2025-03-16T11:17:06.796555Z" "start_time": "2025-03-16T12:51:50.536979Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -309,23 +310,23 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Keypad View\n", "Keypad View\n",
"Key 0: [18 17 8 4 27]\n", "Key 0: [ 7 2 22 15 29]\n",
"Key 1: [23 16 6 3 26]\n", "Key 1: [ 9 4 23 14 25]\n",
"Key 2: [22 15 11 2 24]\n", "Key 2: [ 8 5 19 16 27]\n",
"Key 3: [21 14 10 0 29]\n", "Key 3: [10 1 21 17 26]\n",
"Key 4: [20 12 9 5 28]\n", "Key 4: [11 3 20 13 28]\n",
"Selected Keys\n", "Selected Keys\n",
"[3, 2, 4, 1]\n" "[3, 1, 0, 2]\n"
] ]
} }
], ],
"execution_count": 31 "execution_count": 25
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.061018Z", "end_time": "2025-03-16T12:51:50.790192Z",
"start_time": "2025-03-16T11:17:06.824471Z" "start_time": "2025-03-16T12:51:50.552031Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -343,7 +344,7 @@
] ]
} }
], ],
"execution_count": 32 "execution_count": 26
}, },
{ {
"metadata": {}, "metadata": {},
@@ -362,8 +363,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.070338Z", "end_time": "2025-03-16T12:51:50.801004Z",
"start_time": "2025-03-16T11:17:07.066596Z" "start_time": "2025-03-16T12:51:50.797001Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -371,7 +372,7 @@
"from src.user_cipher import UserCipher\n", "from src.user_cipher import UserCipher\n",
"\n", "\n",
"user_set_key = np.array([46785, 4782, 4405, 44408, 35377, 55527])\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", "user_keys = UserCipher(\n",
" prop_key = np.array([\n", " prop_key = np.array([\n",
" 57200, 8398, 54694, 25997, 30388,\n", " 57200, 8398, 54694, 25997, 30388,\n",
@@ -384,7 +385,6 @@
" pass_key=np.array([16090, 38488, 45111, 32674, 46216, 52013, 48980, 36811, 35296, 17206]),\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", " mask_key=np.array([29575, 43518, 44373, 62063, 37651, 31671, 31663, 65514, 36454, 47325]),\n",
" combined_set_key=np.array(combined_set_key),\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",
"\n", "\n",
@@ -398,12 +398,12 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Passcode Set Vals: [18231, 18231, 18231, 18231]\n", "Passcode Set Vals: [61988, 61988, 61988, 61988]\n",
"Passcode Prop Vals: [33717 26970 11579 40123]\n" "Passcode Prop Vals: [25210 19227 21755 11310]\n"
] ]
} }
], ],
"execution_count": 33 "execution_count": 27
}, },
{ {
"metadata": {}, "metadata": {},
@@ -416,16 +416,16 @@
"3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n", "3. len(set_key) == len(mask_key) == len(padded_ordered_customer_set) == max_nkode_len == 10\n",
"where i is the index\n", "where i is the index\n",
" \n", " \n",
"- mask = mask_key ^ padded_ordered_customer_set ^ ordered_set_key\n", "- mask = mask_key ^ padded_ordered_customer_set ^ ordered_combined_set_key\n",
"- mask = mask_rand_num ^ set_val ^ set_rand_numb ^ set_val\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" "- mask = mask_rand_num ^ set_rand_numb # set_val is cancelled out"
] ]
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.150818Z", "end_time": "2025-03-16T12:51:50.811181Z",
"start_time": "2025-03-16T11:17:07.148125Z" "start_time": "2025-03-16T12:51:50.808721Z"
} }
}, },
"cell_type": "code", "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", "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", "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", "ordered_set_key = user_keys.combined_set_key[set_idx]\n",
"ciphered_mask = ordered_set_key ^ padded_ordered_customer_set\n", "mask = ordered_set_key ^ padded_ordered_customer_set ^ user_keys.mask_key\n",
"ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key)\n", "encoded_mask = user_keys.encode_base64_str(mask)"
"mask = user_keys.encode_base64_str(ciphered_mask)"
], ],
"outputs": [], "outputs": [],
"execution_count": 34 "execution_count": 28
}, },
{ {
"metadata": {}, "metadata": {},
@@ -455,16 +454,14 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.413741Z", "end_time": "2025-03-16T12:51:51.058527Z",
"start_time": "2025-03-16T11:17:07.177338Z" "start_time": "2025-03-16T12:51:50.823135Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
"source": [ "source": [
"import bcrypt\n",
"import hashlib\n", "import hashlib\n",
"import base64\n", "import base64\n",
"\n",
"ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key)\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", "passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode]\n",
"pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n", "pad_len = customer.nkode_policy.max_nkode_len - passcode_len\n",
@@ -473,20 +470,19 @@
"\n", "\n",
"ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key)\n", "ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key)\n",
"\n", "\n",
"#passcode_bytes = int_array_to_bytes(ciphered_code)\n",
"passcode_bytes = ciphered_code.tobytes()\n", "passcode_bytes = ciphered_code.tobytes()\n",
"passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest())\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, user_keys.salt)\n", "hashed_data = bcrypt.hashpw(passcode_digest, bcrypt.gensalt(rounds=12))\n",
"code = hashed_data.decode(\"utf-8\")" "code = hashed_data.decode(\"utf-8\")"
], ],
"outputs": [], "outputs": [],
"execution_count": 35 "execution_count": 29
}, },
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.428200Z", "end_time": "2025-03-16T12:51:51.063978Z",
"start_time": "2025-03-16T11:17:07.426275Z" "start_time": "2025-03-16T12:51:51.061965Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -494,12 +490,12 @@
"from src.models import EncipheredNKode\n", "from src.models import EncipheredNKode\n",
"\n", "\n",
"enciphered_nkode = EncipheredNKode(\n", "enciphered_nkode = EncipheredNKode(\n",
" mask=mask,\n", " mask=encoded_mask,\n",
" code=code,\n", " code=code,\n",
")" ")"
], ],
"outputs": [], "outputs": [],
"execution_count": 36 "execution_count": 30
}, },
{ {
"metadata": {}, "metadata": {},
@@ -513,8 +509,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.668766Z", "end_time": "2025-03-16T12:51:51.305419Z",
"start_time": "2025-03-16T11:17:07.436169Z" "start_time": "2025-03-16T12:51:51.071190Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -532,17 +528,17 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Keypad View\n", "Keypad View\n",
"Key 0: [18 19 20 21 22 23]\n", "Key 0: [ 6 7 8 9 10 11]\n",
"Key 1: [12 13 14 15 16 17]\n", "Key 1: [0 1 2 3 4 5]\n",
"Key 2: [ 6 7 8 9 10 11]\n", "Key 2: [18 19 20 21 22 23]\n",
"Key 3: [0 1 2 3 4 5]\n", "Key 3: [12 13 14 15 16 17]\n",
"Key 4: [24 25 26 27 28 29]\n", "Key 4: [24 25 26 27 28 29]\n",
"Selected Keys: [0, 1, 2, 3]\n", "Selected Keys: [0, 1, 2, 3]\n",
"True\n" "True\n"
] ]
} }
], ],
"execution_count": 37 "execution_count": 31
}, },
{ {
"metadata": {}, "metadata": {},
@@ -574,16 +570,16 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.679342Z", "end_time": "2025-03-16T12:51:51.315072Z",
"start_time": "2025-03-16T11:17:07.676500Z" "start_time": "2025-03-16T12:51:51.312249Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
"source": [ "source": [
"user = api.customers[customer_id].users[username]\n", "user = api.customers[customer_id].users[username]\n",
"decoded_mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n", "mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n",
"deciphered_mask = np.bitwise_xor(decoded_mask, user.cipher.mask_key)\n", "deciphered_mask = mask ^ user.cipher.mask_key\n",
"set_key = np.bitwise_xor(customer.cipher.set_key, user.cipher.combined_set_key)\n", "set_key = customer.cipher.set_key ^ user.cipher.combined_set_key\n",
"passcode_sets = []\n", "passcode_sets = []\n",
"for set_cipher in deciphered_mask[:passcode_len]:\n", "for set_cipher in deciphered_mask[:passcode_len]:\n",
" set_idx = np.where(set_key == set_cipher)[0][0]\n", " set_idx = np.where(set_key == set_cipher)[0][0]\n",
@@ -595,11 +591,11 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"[18231, 18231, 18231, 18231]\n" "[61988, 61988, 61988, 61988]\n"
] ]
} }
], ],
"execution_count": 38 "execution_count": 32
}, },
{ {
"metadata": {}, "metadata": {},
@@ -609,8 +605,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.690303Z", "end_time": "2025-03-16T12:51:51.324331Z",
"start_time": "2025-03-16T11:17:07.687880Z" "start_time": "2025-03-16T12:51:51.321676Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -635,7 +631,7 @@
] ]
} }
], ],
"execution_count": 39 "execution_count": 33
}, },
{ {
"metadata": {}, "metadata": {},
@@ -645,14 +641,14 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:07.937619Z", "end_time": "2025-03-16T12:51:51.571291Z",
"start_time": "2025-03-16T11:17:07.705824Z" "start_time": "2025-03-16T12:51:51.338129Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
"source": [ "source": [
"enciphered_nkode = user.cipher.encipher_salt_hash_code(presumed_selected_properties_idx, customer.cipher)\n", "enciphered_nkode = user.cipher.compare_nkode(presumed_selected_properties_idx, customer.cipher, user.enciphered_passcode.code)\n",
"print(enciphered_nkode == user.enciphered_passcode.code)\n" "print(enciphered_nkode)\n"
], ],
"outputs": [ "outputs": [
{ {
@@ -663,7 +659,7 @@
] ]
} }
], ],
"execution_count": 40 "execution_count": 34
}, },
{ {
"metadata": {}, "metadata": {},
@@ -679,8 +675,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:08.414223Z", "end_time": "2025-03-16T12:51:52.048827Z",
"start_time": "2025-03-16T11:17:07.946756Z" "start_time": "2025-03-16T12:51:51.579003Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -705,14 +701,14 @@
"name": "stdout", "name": "stdout",
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n",
"mask: zLwyQdPMPUFVeJjeKNJl1nyJ9OA=, code: $2b$12$RBM/q1Vu0bJy3kbYqMCymu5ojiE9bTq5Y7eK2iPR1FGHlnZdjzOHu\n", "mask: 8T1ZD3B2c6v+ofoNRIzBbqxVYFw=, code: $2b$12$lAmbDegOx03xLmrB//ZCh.1dyrjtpEmAdv1TWNUO/XjjPofQItzVq\n",
"True\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": {}, "metadata": {},
@@ -726,8 +722,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:08.424680Z", "end_time": "2025-03-16T12:51:52.059119Z",
"start_time": "2025-03-16T11:17:08.421148Z" "start_time": "2025-03-16T12:51:52.055816Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -739,7 +735,7 @@
"new_sets = customer.cipher.set_key" "new_sets = customer.cipher.set_key"
], ],
"outputs": [], "outputs": [],
"execution_count": 42 "execution_count": 36
}, },
{ {
"metadata": {}, "metadata": {},
@@ -752,8 +748,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:08.434263Z", "end_time": "2025-03-16T12:51:52.068302Z",
"start_time": "2025-03-16T11:17:08.431951Z" "start_time": "2025-03-16T12:51:52.065921Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -766,7 +762,7 @@
" user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)" " user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor)"
], ],
"outputs": [], "outputs": [],
"execution_count": 43 "execution_count": 37
}, },
{ {
"metadata": {}, "metadata": {},
@@ -776,8 +772,8 @@
{ {
"metadata": { "metadata": {
"ExecuteTime": { "ExecuteTime": {
"end_time": "2025-03-16T11:17:08.683077Z", "end_time": "2025-03-16T12:51:52.311112Z",
"start_time": "2025-03-16T11:17:08.447107Z" "start_time": "2025-03-16T12:51:52.074783Z"
} }
}, },
"cell_type": "code", "cell_type": "code",
@@ -791,7 +787,7 @@
"user.renew = False" "user.renew = False"
], ],
"outputs": [], "outputs": [],
"execution_count": 44 "execution_count": 38
} }
], ],
"metadata": { "metadata": {

View File

@@ -41,8 +41,7 @@ class Customer:
selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) selected_prop_idx = user.user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx)
presumed_property_idxs.append(selected_prop_idx) presumed_property_idxs.append(selected_prop_idx)
enciphered_prop = user.cipher.encipher_salt_hash_code(presumed_property_idxs, self.cipher) if not user.cipher.compare_nkode(presumed_property_idxs, self.cipher,user.enciphered_passcode.code):
if enciphered_prop != user.enciphered_passcode.code:
return False return False
if user.renew: if user.renew:

View File

@@ -3,7 +3,6 @@ import hashlib
from dataclasses import dataclass from dataclasses import dataclass
import bcrypt import bcrypt
import numpy as np import numpy as np
from secrets import choice
from src.models import EncipheredNKode, KeypadSize from src.models import EncipheredNKode, KeypadSize
from src.customer_cipher import CustomerCipher from src.customer_cipher import CustomerCipher
@@ -14,73 +13,81 @@ class UserCipher:
combined_set_key: np.ndarray combined_set_key: np.ndarray
pass_key: np.ndarray pass_key: np.ndarray
mask_key: np.ndarray mask_key: np.ndarray
salt: bytes
max_nkode_len: int max_nkode_len: int
@classmethod @classmethod
def create(cls, keypad_size: KeypadSize, set_values: np.ndarray, max_nkode_len: int) -> 'UserCipher': def create(cls, keypad_size: KeypadSize, customer_set_key: np.ndarray, max_nkode_len: int) -> 'UserCipher':
if len(set_values) != keypad_size.props_per_key: if len(customer_set_key) != keypad_size.props_per_key:
raise ValueError("Invalid set values") raise ValueError("Invalid set values")
set_values_array = np.array(set_values, dtype=np.uint16) user_set_key = np.random.choice(2**16,size=keypad_size.props_per_key, replace=False)
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)
return UserCipher( return UserCipher(
prop_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False), 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), 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), mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False),
combined_set_key=set_key, combined_set_key=user_set_key ^ customer_set_key,
salt=bcrypt.gensalt(),
max_nkode_len=max_nkode_len max_nkode_len=max_nkode_len
) )
def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray: def pad_user_mask(self, user_mask: np.ndarray, set_vals: np.ndarray) -> np.ndarray:
if len(user_mask) >= self.max_nkode_len: 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) 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) 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]) return np.concatenate([user_mask, padding])
@staticmethod @staticmethod
def encode_base64_str(data: np.ndarray) -> str: 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 @staticmethod
def decode_base64_str(data: str) -> np.ndarray: def decode_base64_str(data: str) -> np.ndarray:
byte_data = base64.b64decode(data) byte_data = base64.b64decode(data)
int_list = [] int_list = []
for i in range(0, len(byte_data), 2): for i in range(0, len(byte_data), 2):
int_val = int.from_bytes(byte_data[i:i + 2], byteorder='big') int_val = int.from_bytes(byte_data[i:i + 2], byteorder='big')
int_list.append(int_val) int_list.append(int_val)
return np.array(int_list, dtype=np.uint16) 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( def encipher_nkode(
self, self,
passcode_prop_idx: list[int], passcode_prop_idx: list[int],
customer_cipher: CustomerCipher customer_cipher: CustomerCipher
) -> EncipheredNKode: ) -> EncipheredNKode:
mask = self.encipher_mask(passcode_prop_idx, customer_cipher) 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( return EncipheredNKode(
code=code, code=code,
mask=mask mask=mask
) )
def encipher_salt_hash_code( def hash_nkode(
self, self,
passcode_prop_idx: list[int], passcode_prop_idx: list[int],
customer_prop: CustomerCipher, customer_prop: CustomerCipher,
) -> str: ) -> 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_len = len(passcode_prop_idx)
passcode_cipher = self.pass_key.copy() passcode_cipher = self.pass_key.copy()
passcode_cipher[:passcode_len] = ( passcode_cipher[:passcode_len] = (
@@ -88,9 +95,7 @@ class UserCipher:
self.prop_key[passcode_prop_idx] ^ self.prop_key[passcode_prop_idx] ^
customer_prop.prop_key[passcode_prop_idx] customer_prop.prop_key[passcode_prop_idx]
) )
return passcode_cipher.astype(np.uint16).tobytes()
return self._hash_passcode(passcode_cipher)
def encipher_mask( def encipher_mask(
self, self,
@@ -100,33 +105,21 @@ class UserCipher:
customer_props = customer_cipher.prop_key[passcode_prop_idx] customer_props = customer_cipher.prop_key[passcode_prop_idx]
customer_sets = [customer_cipher.get_prop_set_val(prop) for prop in customer_props] 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) 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], set_idx = np.array([customer_cipher.get_set_index(set_val) for set_val in padded_customer_sets],
dtype=np.uint16) 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 def decipher_mask(self, encoded_mask: str, customer_set_key: np.ndarray, passcode_len: int) -> list[int]:
ciphered_mask = np.bitwise_xor(mask_set_keys, padded_customer_sets) mask = self.decode_base64_str(encoded_mask)
ciphered_mask = np.bitwise_xor(ciphered_mask, self.mask_key) # user_set_key ordered by the user's nkode and padded to be length max_nkode_len
ordered_set_key = mask ^ self.mask_key
mask = self.encode_base64_str(ciphered_mask) user_set_key = customer_set_key ^ self.combined_set_key
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)
passcode_sets = [] 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])