finish jinja templating
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user