import numpy as np from jinja2 import Environment, FileSystemLoader import os from src.nkode_api import NKodeAPI from src.models import NKodePolicy, KeypadSize, EncipheredNKode from src.user_cipher import UserCipher from secrets import choice from string import ascii_lowercase import bcrypt import hashlib import base64 def random_username() -> str: return "test_username" + "".join([choice(ascii_lowercase) for _ in range(6)]) def select_keys_with_passcode_values(user_passcode: list[int], keypad: np.ndarray, props_per_key: int) -> list[int]: indices = [np.where(keypad == prop)[0][0] for prop in user_passcode] return [int(index // props_per_key) for index in indices] def visualize_keypad(keypad_list: np.ndarray, props_per_key: int): print("Keypad View") keypad_mat = keypad_list.reshape(-1, props_per_key) for idx, key_vals in enumerate(keypad_mat): print(f"Key {idx}: {key_vals}") def render_nkode_authentication(data: dict): # Set up the Jinja2 environment and template loader file_loader = FileSystemLoader('') env = Environment(loader=file_loader) # Load the template template = env.get_template('readme_template.md') print(os.getcwd()) # Render the template with the data output = template.render(data) # Print or save the output # output_file = os.path.expanduser("~/Desktop/nkode_authentication.md") output_file = '../README.md' with open(output_file, 'w') as fp: fp.write(output) print("File written successfully") if __name__ == "__main__": api = NKodeAPI() policy = NKodePolicy( max_nkode_len=10, min_nkode_len=4, distinct_sets=0, distinct_properties=4, byte_len=2, ) keypad_size = KeypadSize( numb_of_keys=5, props_per_key=6 # aka number of sets ) customer_id = api.create_new_customer(keypad_size, policy) customer = api.customers[customer_id] set_vals = customer.cipher.set_key prop_vals = customer.cipher.prop_key customer_prop_view = prop_vals.reshape(-1, keypad_size.props_per_key) prop_keypad_view = prop_vals.reshape(-1, keypad_size.props_per_key) prop_set_view = prop_keypad_view.T set_property_dict = dict(zip(set_vals, prop_set_view)) session_id, signup_interface = api.generate_signup_keypad(customer_id) signup_keypad = signup_interface.reshape(-1, keypad_size.numb_of_keys) username = random_username() passcode_len = 4 user_passcode = signup_interface[:passcode_len].tolist() selected_keys_set = select_keys_with_passcode_values(user_passcode, signup_interface, keypad_size.numb_of_keys) server_side_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) confirm_keypad = confirm_interface.reshape(-1, keypad_size.numb_of_keys) selected_keys_confirm = select_keys_with_passcode_values(user_passcode, confirm_interface, keypad_size.numb_of_keys) success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) assert success passcode_server_prop = [customer.cipher.prop_key[idx] for idx in user_passcode] passcode_server_set = [customer.cipher.get_prop_set_val(prop) for prop in passcode_server_prop] user_keys = customer.users[username].cipher padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.set_key) 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 = np.bitwise_xor(mask_set_keys, padded_passcode_server_set) ciphered_mask = np.bitwise_xor(ciphered_mask, user_keys.mask_key) mask = user_keys.encode_base64_str(ciphered_mask) #ciphered_customer_props = xor_lists(customer.cipher.prop_key, user_keys.prop_key) ciphered_customer_props = np.bitwise_xor(customer.cipher.prop_key, user_keys.prop_key) passcode_ciphered_props = [ciphered_customer_props[idx] for idx in user_passcode] pad_len = customer.nkode_policy.max_nkode_len - passcode_len passcode_ciphered_props.extend([0 for _ in range(pad_len)]) ciphered_code = np.bitwise_xor(passcode_ciphered_props, user_keys.pass_key) passcode_bytes = ciphered_code.tobytes() passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest()) hashed_data = bcrypt.hashpw(passcode_digest, user_keys.salt) code = hashed_data.decode("utf-8") enciphered_nkode = EncipheredNKode( mask=mask, code=code, ) """ USER LOGIN """ login_interface = api.get_login_keypad(username, customer_id) login_keypad = login_interface.reshape(-1, keypad_size.props_per_key) selected_keys_login = select_keys_with_passcode_values(user_passcode, login_interface, keypad_size.props_per_key) success = api.login(customer_id, username, selected_keys_login) assert success """ VALIDATE LOGIN KEY ENTRY DECIPHER MASK """ user = customer.users[username] 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 = np.bitwise_xor(decoded_mask, user_keys.mask_key) set_key_rand_component = np.bitwise_xor(set_vals, user_keys.set_key) login_passcode_sets = [] for set_cipher in deciphered_mask[:passcode_len]: set_idx = np.where(set_key_rand_component == set_cipher)[0][0] login_passcode_sets.append(int(set_vals[set_idx])) """ GET PRESUMED properties """ set_vals_idx = [customer.cipher.get_set_index(set_val) for set_val in login_passcode_sets] presumed_selected_properties_idx = [] for idx in range(passcode_len): key_numb = selected_keys_login[idx] set_idx = set_vals_idx[idx] selected_prop_idx = customer.users[username].user_keypad.get_prop_idx_by_keynumb_setidx(key_numb, set_idx) presumed_selected_properties_idx.append(selected_prop_idx) """ RENEW KEYS """ old_props = customer.cipher.prop_key.copy() old_sets = customer.cipher.set_key.copy() customer.cipher.renew() new_props = customer.cipher.prop_key new_sets = customer.cipher.set_key customer_new_prop_view = new_props.reshape(-1, keypad_size.props_per_key) """ RENEW USER """ props_xor = np.bitwise_xor(new_props, old_props) sets_xor = np.bitwise_xor(new_sets, old_sets) for user in customer.users.values(): user.renew = True user.cipher.set_key = np.bitwise_xor(user.cipher.set_key, sets_xor) user.cipher.prop_key = np.bitwise_xor(user.cipher.prop_key, props_xor) """ REFRESH USER KEYS """ user.cipher = UserCipher.create( customer.cipher.keypad_size, customer.cipher.set_key, user.cipher.max_nkode_len ) user.enciphered_passcode = user.cipher.encipher_nkode(presumed_selected_properties_idx, customer.cipher) user.renew = False # Define some data to pass to the template data = { 'keypad_size': keypad_size, 'customer_set_vals': set_vals, 'customer_prop_view': customer_prop_view, 'set_property_dict': set_property_dict, 'signup_keypad': signup_keypad, 'username': 'test_user', 'user_passcode': user_passcode, 'selected_keys_set': selected_keys_set, 'server_side_prop': server_side_prop, 'confirm_keypad': confirm_keypad, 'selected_keys_confirm': selected_keys_confirm, 'user_cipher': user_keys, 'passcode_server_prop': passcode_server_prop, 'passcode_server_set': passcode_server_set, 'enciphered_nkode': enciphered_nkode, 'login_keypad': login_keypad, 'selected_login_keys': selected_keys_login, 'login_passcode_sets': login_passcode_sets, 'presumed_selected_properties_idx': presumed_selected_properties_idx, 'customer_new_prop_view': customer_new_prop_view, 'customer_new_set_vals': new_sets, } render_nkode_authentication(data)