7.0 KiB
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_keyset_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
- set_key_i = (set_rand_numb_i ^ set_val_i)
- 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:
- the keys are randomly shuffled
- 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
- Server-Side Attribute Renewal: ss_attr and ss_set values are replaced with new values
- Intermdiate User Renewal: every user goes through an intermediate phase before full Renewal
- 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