204 lines
7.9 KiB
Python
204 lines
7.9 KiB
Python
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_positions=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.position_key
|
|
prop_vals = customer.cipher.property_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.property_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.property_key[idx] for idx in user_passcode]
|
|
passcode_server_set = customer.cipher.get_props_position_vals(user_passcode)
|
|
user_keys = customer.users[username].cipher
|
|
# TODO: pad_user_mask is deprecated
|
|
padded_passcode_server_set = user_keys.pad_user_mask(np.array(passcode_server_set), customer.cipher.position_key)
|
|
|
|
set_idx = [customer.cipher.get_position_index(set_val) for set_val in padded_passcode_server_set]
|
|
mask_set_keys = [user_keys.combined_position_key[idx] for idx in set_idx]
|
|
ciphered_mask = mask_set_keys ^ padded_passcode_server_set ^ user_keys.mask_key
|
|
mask = user_keys.encode_base64_str(ciphered_mask)
|
|
ciphered_customer_props = customer.cipher.property_key ^ user_keys.property_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, bcrypt.gensalt(rounds=12))
|
|
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.position_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.combined_position_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_position_index(set_val) for set_val in login_passcode_sets]
|
|
presumed_selected_properties_idx = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, set_vals_idx)
|
|
"""
|
|
RENEW KEYS
|
|
"""
|
|
|
|
old_props = customer.cipher.property_key.copy()
|
|
old_sets = customer.cipher.position_key.copy()
|
|
customer.cipher.renew()
|
|
new_props = customer.cipher.property_key
|
|
new_sets = customer.cipher.position_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.combined_position_key = np.bitwise_xor(user.cipher.combined_position_key, sets_xor)
|
|
user.cipher.property_key = np.bitwise_xor(user.cipher.property_key, props_xor)
|
|
|
|
"""
|
|
REFRESH USER KEYS
|
|
"""
|
|
user.cipher = UserCipher.create(
|
|
customer.cipher.keypad_size,
|
|
customer.cipher.position_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,
|
|
'set_signup_keypad': signup_keypad,
|
|
'username': 'test_user',
|
|
'passcode_property_indices': 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,
|
|
'ordered_customer_prop_key': passcode_server_prop,
|
|
'ordered_customer_set_key': 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) |