finish jinja templating

This commit is contained in:
2024-08-07 12:22:17 -05:00
parent d33767b545
commit ab43296ec2
2 changed files with 47 additions and 48 deletions

View File

@@ -1,11 +1,10 @@
# nKode Authentication # 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. 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 ## Customer Creation
Before a user can be created, a customer with random attribute and set Before creating a user, a customer generates random attributes and set
values is created. The customers manage user's. They define an nKode policy, keypad's dimensions, values. The customers manage users. They define an nKode policy, keypad's dimensions,
attributes/sets in the keypad, and the frequency of attribute renew. attributes/sets in the keypad, and the frequency of attribute renewal.
### nKode Policy and Keypad Size ### nKode Policy and Keypad Size
An nKode policy defines: An nKode policy defines:
<ul> <ul>
@@ -13,7 +12,7 @@ An nKode policy defines:
<li>the minimum length of a user's nKode</li> <li>the minimum length of a user's nKode</li>
<li>the number of unique set values in a user's nKode</li> <li>the number of unique set values in a user's nKode</li>
<li>the number of unique values in a user's nKode</li> <li>the number of unique values in a user's nKode</li>
<li>the number of bytes an attribute or set value is</li> <li>the number of bytes in an attribute and set</li>
</ul> </ul>
The keypad size defines: The keypad size defines:
@@ -22,7 +21,8 @@ The keypad size defines:
<li>attributes per key</li> <li>attributes per key</li>
</ul> </ul>
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() api = NKodeAPI()
@@ -35,8 +35,8 @@ policy = NKodePolicy(
) )
keypad_size = KeypadSize( keypad_size = KeypadSize(
numb_of_keys = 5, numb_of_keys = {{ keypad_size.numb_of_keys }},
attrs_per_key = 6 # aka number of sets attrs_per_key = {{ keypad_size.attrs_per_key }} # aka number of sets
) )
customer_id = api.create_new_customer(keypad_size, policy) customer_id = api.create_new_customer(keypad_size, policy)
@@ -44,8 +44,9 @@ customer = api.customers[customer_id]
``` ```
### Customer Attributes and Sets ### Customer Attributes and Sets
A customer has users and defines the attributes and set values for all its users. 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. Since our customer has {{ keypad_size.numb_of_keys }} keys and {{ keypad_size.attrs_per_key }} attributes per key,
Each attribute belongs to one of the 6 sets. In this example, each attribute and set value is a unique 2 byte integer. 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 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: Now that we have a customer, we can create users. To create a new user:
1. Generate a random interface 1. Generate a random interface
2. User sets their nKode and sends their selection to the server 2. The 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 3. The user confirms their nKode. If the user's nKode matches the policy, the server creates the user.
### Random Interface Generation ### Random Interface Generation
For the server to determine the users nkode, the user's interface must be dispersable. The user's interface must be dispersable so the server can determine the user's nkode.
To make the interface dispersable, the server will randomly drop attribute sets to make the The server randomly drops attribute sets until
number of attributes equal to the number of keys. In our case, the server randomly drops 1 attribute set the number of attributes equals the number of keys, making the interface dispersable.
to give us a 5 X 5 keypad with possible index values ranging from 0-29. 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. 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 user never learns what their "real" attribute is. They do not see the index value representing their nKode or
the customer value it is associated with. the customer server-side value.
``` ```
session_id, signup_interface = api.generate_index_interface(customer_id) session_id, signup_interface = api.generate_index_interface(customer_id)
signup_interface_keypad = list_to_matrix(signup_interface, keypad_size.attrs_per_key) signup_interface_keypad = list_to_matrix(signup_interface, keypad_size.attrs_per_key)
@@ -100,9 +103,10 @@ Key {{ loop.index }}: {{ key }}
### Set nKode ### Set nKode
The user identifies attributes in the interface they want in their nkode. Each attribute has an index value. 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. 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. The only requirement is that the GUI attributes be associated with the same index every time the user logs in.
If the user wants to change anything about their interface, they must also change their nkode. If users want to change anything about their interface, they must also change their nkode.
``` ```
username = {{ username }} username = {{ username }}
user_passcode = {{ user_passcode }} user_passcode = {{ user_passcode }}
@@ -120,8 +124,9 @@ User Passcode Server-side Attributes: {{ server_side_attr }}
``` ```
### Confirm nKode ### 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. The user finds their nKode again.
``` ```
confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id) confirm_interface = api.set_nkode(username, customer_id, selected_keys_set, session_id)
keypad_view(confirm_interface, keypad_size.numb_of_keys) keypad_view(confirm_interface, keypad_size.numb_of_keys)
@@ -135,22 +140,22 @@ Selected Keys:
{{ selected_keys_confirm }} {{ 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) success = api.confirm_nkode(username, customer_id, selected_keys_confirm, session_id)
``` ```
### Passcode Enciphering, Hashing, and Salting ### 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. When a new user creates an nKode, the server caches its set and confirms the interface and the user's key selection.
The on the last api.confirm_nkode the server: 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 2. Validates the Passcode against the nKodePolicy
3. Creates new User Cipher Keys 3. Creates new User Cipher Keys
4. Enciphers the user's mask 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 #### 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): class EncipheredNKode(BaseModel):
code: str 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 = {{ user_passcode }}
passcode_server_attr = [customer.attributes.attr_vals[idx] for idx in 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] 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) 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] 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] 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(mask_set_keys, padded_passcode_server_set)
ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key) ciphered_mask = xor_lists(ciphered_mask, user_keys.mask_key)
# encode ciphered mask in base64
mask = user_keys.encode_base64_str(ciphered_mask) mask = user_keys.encode_base64_str(ciphered_mask)
Mask: {{ enciphered_nkode.mask }} Mask: {{ enciphered_nkode.mask }}
``` ```
@@ -281,7 +280,7 @@ success = api.login(customer_id, username, selected_keys_login)
### Validate Login Key Entry ### Validate Login Key Entry
- decipher user mask and recover nkode set values - decipher user mask and recover nkode set values
- get presumed attribute from key selection and 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 #### Decipher Mask
Recall: Recall:
@@ -328,23 +327,22 @@ Recall User Passcode: {{ user_passcode }}
### Compare Enciphered Passcodes ### Compare Enciphered Passcodes
``` ```
enciphered_nkode = user_keys.encipher_salt_hash_code(presumed_selected_attributes_idx, customer.attributes) 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 ## Renew Attributes
Attributes renew is invoked with the renew_attributes method: `api.renew_attributes(customer_id)` 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 1. Renew Customer Attributes
2. Renew User Keys 2. Renew User Keys
3. Refresh User on Login 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 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
renew step. The user if fully renewed after their first successful login. This first login refreshes their keys, salt, and hash. 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 ### 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 old_sets = customer.attributes.set_vals
@@ -360,7 +358,7 @@ Customer Attributes:
{% endfor %} {% 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) api.renew_attributes(customer_id)
@@ -379,7 +377,7 @@ Customer Attributes:
``` ```
### Renew User ### 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) attrs_xor = xor_lists(new_attrs, old_attrs)
sets_xor = xor_lists(new_sets, old_sets) 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`. 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`. 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`. 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 new_alpha_key = old_alpha_key ^ old_attr ^ new_attr
ciphered_customer_attrs = new_alpha_key ^ new_attr ciphered_customer_attrs = new_alpha_key ^ new_attr
ciphered_customer_attrs = old_alpha_key ^ old_attr # since new_attr cancel out 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 ##### User Set Key
The user's set key was a randomly generated list of length `attr_per_key` xor `customer_set_vals`. 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. remains the same.
### User Refresh ### User Refresh

View File

@@ -191,6 +191,7 @@ if __name__ == "__main__":
# Define some data to pass to the template # Define some data to pass to the template
data = { data = {
'keypad_size': keypad_size,
'customer_set_vals': set_vals, 'customer_set_vals': set_vals,
'customer_attr_view': customer_attr_view, 'customer_attr_view': customer_attr_view,
'set_attribute_dict': set_attribute_dict, 'set_attribute_dict': set_attribute_dict,