# nKode Tutorial ## nKode Keypad - nKode keypad contains keys - Each key has icons - Each icon belongs to a set and has an index value associated with it - The position within the key is the set value - When attributes are shuffled, they'll always remain in the same position within the key (note: the set value can be obuscated from the client but we find it helpful to quickly find the attribute) ## nKode Enrollment - user sets then confirms their nKode - Set and Confirm are dipersion of each other and allow the server to infer the users nKode - the users login has additional icons added to each to key to make it dispersion resistant - After the user as selected their nKode through the set and confirms, the server can infer the users selection since each key selection will only have one icons in common. - The users nKode is enciphered, salted and hashed. ## Dispersion - Two keypads are dispersion of each other if: Kp1: Keypad 1 Kp2: Keypad 2 for each key1 in Kp1: for each key2 in Kp2: iconsIntersection(key1, key2) <= 1 ## Dispersion Algo - Convert a Keypad view into a matrix view - each row is key and each column is a set - Fisher Yates the keys i.e. the rows in the matrix - Fisher Yates an identity matrix equivalent to the number of keys - The shuffled permutation is used to rotate the columns ## Dispersion Resistance - Dispersion is only possible if {number of icons per key} <= {number keys in the keypad} - If there are more icons per key than keys, it makes it difficult to phish for a users nkode ## Cipher Keys #### attribute key - randomly generated array of unsigned integars - array of length {numb of keys} * {number of attributes per key} - xored with ss-attr to give each user a unique #### passcode key - randomly generated array of unsigned integars - array of length {max nKode length} - xored with final user passcode #### set key - used to recover users nkode set values - array of unsigned integars - each elemenet in the array is a random number XOR'ed with the set value of the users nKode - so `for set_key_i in user_set_key` `set_key_i = user_passcode_set_i ^ random_set_key_i` #### mask key - used to recover users nkode set values - array of random unsigned integars #### server-side attributes (ss-attr) and sets (ss-set) - Each icon has a server side attribute. - The icon and the ss-attr are indexed to the same place in an array. - The dispersion and shuffle operation are applied to the index array and the index array is applied to the icon and ss-attr - ss-attrs are randomly generated permutation of unsigned ints (or byte arrays of any length for extra security) ### Encipher and Hash nKode Passcode passcode = index values of the selected icons infered in set/comfirm user_attrs = arr_xor(ss_attr, user_attr_key) passcode_attr = [user_attrs[idx] for idx in passcode] ciphered_passcode = arr_xor(passcode_attr, user_passcode_key) // optional sha256 if ciphered_passcode is more than 72 bytes passcode_digest = sha256(ciphered_passcode) hashed_passcode = bcrypt(passcode_digest) ### Enicphering nKode Set nKode mask is used to recover the users nKode 1. set_key_i = (set_rand_numb_i ^ set_val_i) 2. mask_key_i = mask_rand_numb_i passcode = index values of the selected icons infered in set/comfirm // recall ss_set array is an order list set_idx = get_set_value(passcode) user_set_keys = [set_key[idx] for idx in set_idx] user_set_vals = [ss_sets[idx] for idx in set_idx] mask = arr_xor(user_set_val, user_set_keys, mask_key) - mask_i = mask_key_i ^ padded_passcode_server_set_i ^ set_key_i - mask_i = mask_rand_num_i ^ set_val_i ^ set_rand_numb_i ^ set_val_i - mask_i = mask_rand_num_i ^ set_rand_numb_i # set_val_i is cancelled out #### nKode Validation // set_val_i = mask_i ^ set_key_i ^ mask_key_i // selected_keys the keys the user selects recovered_set_vals = arr_xor(mask, set_key, mask_key) user_keypad = get_user_keypad(username) presumed_password = get_pressumed_passcode(user_key, selected_keys, recovered_set_val) if is_valid_passcode(presumed_password, hashed_passcode) return LOGIN_SUCCESS else return LOGIN_FAIL ## nKode Login Shuffle - After a successful login, the users keypad is shuffled in a two step process: 1. the keys are randomly shuffled 2. half the sets are randomly shuffled - We've found this works best for xyz reason and we're also working on simulations to find a probablity curve for number of overserveration before an adversery can crack an nkode ## nKode Login - When a user request their nkode keypad, the server sends a list of svgs in the order they should be displayed - The Gen2 demo implementation works a bit different it sends the svgs indexed in order from 0-total_numb_of_attr along with an index array for how the svgs should be ordered - this is a legacy from the gen 1 keypad but it helps to make the implemntation more concrete so we'll use this legacy - The frontend organized the svgs into keys and displays them. - The user selects keys for their nkode and their key selection is sent back to the server for validation ## Preventing User Enumeration - This isn't applied in the demo application - We can prevent a threat actor from enumerating users. - if a valid email is entered into the login for a user that dne, we can create a ghost keypad and have it behave as if it were a real user. - if there are too many "failed nkode attempts" we lock the non-existing user out. - We can prevent timing attacks by either mocking the real control flow or setting a 1 or 2 sec response delay from the time the login request was recieved thereby completely elimiating timing attacks ## Renew Renew changes user ciphers, user salt, ss_attr, and ss_set. This is import in the event of a partial database hack or leak. An admin or cron job can run the renew with any frequency (daily, weekly, monthly) Users are aware of this process. Nothing changes for them. ### Three Step Renew Renew has three phases 1. Server-Side Attribute Renewal: ss_attr and ss_set values are replaced with new values 2. Intermdiate User Renewal: every user goes through an intermediate phase before full Renewal 3. Full User Renewal: The user's first successful login completes the renewal. User has gets new salt, and cipher #### Server-Side Attribute Renewal old_ss_attr, old_ss_set = get_ss_vals() renew_ss_vals() // new randomly generated values new_ss_attr, new_ss_set = get_ss_vals() xor_ss_attr = arr_xor(old_ss_attr, new_ss_attr) xor_ss_set = arr_xor(old_ss_set, new_ss_set) #### Intermdiate User Renewal all_users = get_all_users() for user in all_users: user.renew = True // This flags the server to complete phase 3, full user renewal user.set_key = xor_lists(user.set_key, xor_ss_set) // recall user.set_key = rand_num ^ old_ss_set. this operation cancels out old_ss_set and replaces it with new_ss_set user.attribute_key = xor_lists(user.attribute_key, xor_ss_attr) // recall user.attribute_key ^ old_ss_attr are the user's nkode ss attributes so when we #### Full User Renewal After a successful login user keys are reset as if the user was signing up for the first time