diff --git a/docs/nkode_authentication_template.md b/docs/nkode_authentication_template.md index cbf4b32..b0a8af6 100644 --- a/docs/nkode_authentication_template.md +++ b/docs/nkode_authentication_template.md @@ -1,11 +1,10 @@ # nKode Authentication Play around with the code in [this](http://sesolgit/Repository/Blob/92a60227-4ef9-4196-8ebb-595581abf98c?encodedName=main&encodedPath=nkode_tutorial.ipynb) jupyter notebook. - ## Customer Creation -Before a user can be created, a customer with random attribute and set -values is created. The customers manage user's. They define an nKode policy, keypad's dimensions, -attributes/sets in the keypad, and the frequency of attribute renew. +Before creating a user, a customer generates random attributes and set +values. The customers manage users. They define an nKode policy, keypad's dimensions, +attributes/sets in the keypad, and the frequency of attribute renewal. ### nKode Policy and Keypad Size An nKode policy defines: The keypad size defines: @@ -22,7 +21,8 @@ The keypad size defines:
  • attributes per key
  • -The number of attributes must be greater than the number of keys to be [dispersion](nkode_concepts.md/#dispersion-resistant-interface) resistant. +To be [dispersion](nkode_concepts.md/#dispersion-resistant-interface) resistant, the number of attributes must be greater than the number of keys. + ``` api = NKodeAPI() @@ -35,8 +35,8 @@ policy = NKodePolicy( ) keypad_size = KeypadSize( - numb_of_keys = 5, - attrs_per_key = 6 # aka number of sets + numb_of_keys = {{ keypad_size.numb_of_keys }}, + attrs_per_key = {{ keypad_size.attrs_per_key }} # aka number of sets ) customer_id = api.create_new_customer(keypad_size, policy) @@ -44,8 +44,9 @@ customer = api.customers[customer_id] ``` ### Customer Attributes and Sets A customer has users and defines the attributes and set values for all its users. -Since our customer has 5 keys and 6 attributes per key, this gives a customer interface of 30 distinct attributes and 6 distinct attribute sets. -Each attribute belongs to one of the 6 sets. In this example, each attribute and set value is a unique 2 byte integer. +Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.attrs_per_key }} attributes per key, +this gives a customer interface of {{ keypad_size.numb_of_attrs }} distinct attributes and {{ keypad_size.attrs_per_key }} distinct attribute sets. +Each attribute belongs to one of the {{ keypad_size.attrs_per_key }} sets. Each attribute and set value is a unique 2-byte integer in this example. ``` set_vals = customer.attributes.set_vals @@ -78,16 +79,18 @@ Set to Attribute Map: Now that we have a customer, we can create users. To create a new user: 1. Generate a random interface -2. User sets their nKode and sends their selection to the server -3. User confirms their nKode and the user is created if the nKode matches the nKode policy +2. The user sets their nKode and sends their selection to the server +3. The user confirms their nKode. If the user's nKode matches the policy, the server creates the user. ### Random Interface Generation -For the server to determine the users nkode, the user's interface must be dispersable. -To make the interface dispersable, the server will randomly drop attribute sets to make the -number of attributes equal to the number of keys. In our case, the server randomly drops 1 attribute set -to give us a 5 X 5 keypad with possible index values ranging from 0-29. +The user's interface must be dispersable so the server can determine the user's nkode. +The server randomly drops attribute sets until +the number of attributes equals the number of keys, making the interface dispersable. +In our case, the server randomly drops {{ keypad_size.attrs_per_key - keypad_size.numb_of_keys }} attribute {{ "sets" if keypad_size.attrs_per_key - keypad_size.numb_of_keys > 1 else "set" }}. +to give us a {{ keypad_size.numb_of_keys }} X {{ keypad_size.numb_of_keys }} keypad with possible index values ranging from 0-{{ keypad_size.numb_of_attrs - 1 }}. Each value in the interface is the index value of a customer attribute. -The user never learns what their "real" attribute is. They don't see the index value that represents their nKode or -the customer value it is associated with. +The user never learns what their "real" attribute is. They do not see the index value representing their nKode or +the customer server-side value. + ``` session_id, signup_interface = api.generate_index_interface(customer_id) signup_interface_keypad = list_to_matrix(signup_interface, keypad_size.attrs_per_key) @@ -100,9 +103,10 @@ Key {{ loop.index }}: {{ key }} ### Set nKode The user identifies attributes in the interface they want in their nkode. Each attribute has an index value. -Below the user has selected `{{ user_passcode }}`. These index values can be represented by anything in the GUI. -The only requirement is that the GUI attributes must be associated with the same index everytime the user goes to login. -If the user wants to change anything about their interface, they must also change their nkode. +Below, the user has selected `{{ user_passcode }}`. These index values can be represented by anything in the GUI. +The only requirement is that the GUI attributes be associated with the same index every time the user logs in. +If users want to change anything about their interface, they must also change their nkode. + ``` username = {{ username }} user_passcode = {{ user_passcode }} @@ -120,8 +124,9 @@ User Passcode Server-side Attributes: {{ server_side_attr }} ``` ### Confirm nKode -The user submits the set interface to the sever and recieves the _confirm interface_ as a response. +The user submits the set interface to the server and receives the _confirm interface_ as a response. The user finds their nKode again. + ``` confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) keypad_view(confirm_interface, keypad_size.numb_of_keys) @@ -135,22 +140,22 @@ Selected Keys: {{ selected_keys_confirm }} ``` -The user submits their confirm key selection and the user is created +The user submits their confirmation key selection and the user is created ``` success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id) ``` ### Passcode Enciphering, Hashing, and Salting -When a new user creates an nKode, the server caches its set and confirm interface as well as the users key selection. -The on the last api.confirm_nkode the server: +When a new user creates an nKode, the server caches its set and confirms the interface and the user's key selection. +On the last api.confirm_nkode, the server: -1. Deduces the users attributes +1. Deduces the user's attributes 2. Validates the Passcode against the nKodePolicy 3. Creates new User Cipher Keys 4. Enciphers the user's mask -5. Enciphers, salts and hashes the user's passcode +5. Enciphers, salts, and hashes the user's passcode -Steps 1-2 are straight forward. For a better idea of how they work, see pyNKode +Steps 1-2 are straightforward. For a better idea of how they work, see pyNKode. #### User Cipher Keys @@ -181,7 +186,8 @@ user_keys = UserCipherKeys( ) ``` -The method UserCipherKeys.encipher_nkode secures a users nKode in the database. This method is called in api.confirm_nkode +The method UserCipherKeys.encipher_nkode secures a user's nKode in the database. This method is called in api.confirm_nkode + ``` class EncipheredNKode(BaseModel): code: str @@ -204,7 +210,6 @@ Recall: ``` -# the passcode is deduced in confirm_nkode. These values are the index values of the customer attribute values passcode = {{ user_passcode }} passcode_server_attr = [customer.attributes.attr_vals[idx] for idx in passcode] passcode_server_set = [customer.attributes.get_attr_set_val(attr) for attr in passcode_server_attr] @@ -214,20 +219,14 @@ Passcode Attr Vals: {{ passcode_server_set }} ``` ``` -# pad passcode set list with random set values so the list is equal to the max nkode value. This hids the nKode's length padded_passcode_server_set = user_keys.pad_user_mask(passcode_server_set, customer.nkode_policy.max_nkode_len) -# get the index of each set value set_idx = [customer.attributes.get_set_index(set_val) for set_val in padded_passcode_server_set] - -# find the set values matching set key to cancel out the set value mask_set_keys = [user_keys.set_key[idx] for idx in set_idx] -# xor the set key, passocode set value and the mask key ciphered_mask = xor_lists(mask_set_keys, padded_passcode_server_set) ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) -# encode ciphered mask in base64 mask = user_keys.encode_base64_str(ciphered_mask) Mask: {{ enciphered_nkode.mask }} ``` @@ -281,7 +280,7 @@ success = api.login(customer_id, username, selected_keys_login) ### Validate Login Key Entry - decipher user mask and recover nkode set values - get presumed attribute from key selection and set values -- encipher, salt and hash presumed attribute values and compare it to the users hashed code +- encipher, salt, and hash presumed attribute values and compare them to the users hashed code #### Decipher Mask Recall: @@ -328,23 +327,22 @@ Recall User Passcode: {{ user_passcode }} ### Compare Enciphered Passcodes ``` enciphered_nkode = user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes) - ``` -If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid and the login is successful. +If `enciphered_nkode == user.enciphered_passcode.code`, the user's key selection is valid, and the login is successful. ## Renew Attributes Attributes renew is invoked with the renew_attributes method: `api.renew_attributes(customer_id)` -The renew attributes processes has three steps: +The renew attributes process has three steps: 1. Renew Customer Attributes 2. Renew User Keys 3. Refresh User on Login -When the `renew_attributes` method is called, the customer attributes are renewed and all it's users go through an intermediate -renew step. The user if fully renewed after their first successful login. This first login refreshes their keys, salt, and hash. +When the customer calls the `renew_attributes` method, the method replaces the customer's attributes and set values. All its users go through an intermediate +renewal step. The users fully renew after their first successful login. This first login refreshes their keys, salt, and hash with new values. ### Customer Renew -Old Customer attributes and set values are cached copied to variables before they are renewed. +Old Customer attributes and set values are cached and copied to variables before renewal. ``` old_sets = customer.attributes.set_vals @@ -360,7 +358,7 @@ Customer Attributes: {% endfor %} ``` -After the renew, the customer attributes and sets are new randomly generated values. +After the renewal, the customer attributes and sets are new randomly generated values. ``` api.renew_attributes(customer_id) @@ -379,7 +377,7 @@ Customer Attributes: ``` ### Renew User -During the renew, each user goes through a temporary transition period. +During the renewal, each user goes through a temporary transition period. ``` attrs_xor = xor_lists(new_attrs, old_attrs) sets_xor = xor_lists(new_sets, old_sets) @@ -392,17 +390,17 @@ for user in customer.users.values(): The user's alpha key was a randomly generated list of length `numb_of_keys * attr_per_key`. Now each value in the alpha key is `alpha_key_i = old_alpha_key_i ^ new_attr_i ^ old_attr_i`. Recall in the login process, `ciphered_customer_attrs = alpha_key ^ customer_attr`. -Since the customer_attr is now the new value, it gets cancelled out leaving: +Since the customer_attr is now the new value, it gets canceled out, leaving: ``` new_alpha_key = old_alpha_key ^ old_attr ^ new_attr ciphered_customer_attrs = new_alpha_key ^ new_attr ciphered_customer_attrs = old_alpha_key ^ old_attr # since new_attr cancel out ``` -We can valid the user's login attempt with the same hash using the new customer attributes +Using the new customer attributes, we can validate the user's login attempt with the same hash. ##### User Set Key The user's set key was a randomly generated list of length `attr_per_key` xor `customer_set_vals`. -Now the `old_set_vals` have been replaced with the new `new_set_vals`. The deciphering process described above +The `old_set_vals` have been replaced with the new `new_set_vals`. The deciphering process described above remains the same. ### User Refresh diff --git a/render_markdown.py b/render_markdown.py index be6e843..c41b0eb 100644 --- a/render_markdown.py +++ b/render_markdown.py @@ -191,6 +191,7 @@ if __name__ == "__main__": # Define some data to pass to the template data = { + 'keypad_size': keypad_size, 'customer_set_vals': set_vals, 'customer_attr_view': customer_attr_view, 'set_attribute_dict': set_attribute_dict,