Compare commits
2 Commits
UpdateLowb
...
UpdateDocs
| Author | SHA1 | Date | |
|---|---|---|---|
| ccf1bac337 | |||
|
|
64ddd9f348 |
1
.obsidian/app.json
vendored
Normal file
1
.obsidian/app.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
.obsidian/appearance.json
vendored
Normal file
1
.obsidian/appearance.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
31
.obsidian/core-plugins.json
vendored
Normal file
31
.obsidian/core-plugins.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"file-explorer": true,
|
||||||
|
"global-search": true,
|
||||||
|
"switcher": true,
|
||||||
|
"graph": true,
|
||||||
|
"backlink": true,
|
||||||
|
"canvas": true,
|
||||||
|
"outgoing-link": true,
|
||||||
|
"tag-pane": true,
|
||||||
|
"properties": false,
|
||||||
|
"page-preview": true,
|
||||||
|
"daily-notes": true,
|
||||||
|
"templates": true,
|
||||||
|
"note-composer": true,
|
||||||
|
"command-palette": true,
|
||||||
|
"slash-command": false,
|
||||||
|
"editor-status": true,
|
||||||
|
"bookmarks": true,
|
||||||
|
"markdown-importer": false,
|
||||||
|
"zk-prefixer": false,
|
||||||
|
"random-note": false,
|
||||||
|
"outline": true,
|
||||||
|
"word-count": true,
|
||||||
|
"slides": false,
|
||||||
|
"audio-recorder": false,
|
||||||
|
"workspaces": false,
|
||||||
|
"file-recovery": true,
|
||||||
|
"publish": false,
|
||||||
|
"sync": true,
|
||||||
|
"webviewer": false
|
||||||
|
}
|
||||||
166
.obsidian/workspace.json
vendored
Normal file
166
.obsidian/workspace.json
vendored
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
{
|
||||||
|
"main": {
|
||||||
|
"id": "2d317d71045425f3",
|
||||||
|
"type": "split",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "51f85f0ef7594aef",
|
||||||
|
"type": "tabs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "60d702f2c5c78820",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "empty",
|
||||||
|
"state": {},
|
||||||
|
"icon": "lucide-file",
|
||||||
|
"title": "New tab"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"direction": "vertical"
|
||||||
|
},
|
||||||
|
"left": {
|
||||||
|
"id": "933955ad281257d5",
|
||||||
|
"type": "split",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "348366d432902e79",
|
||||||
|
"type": "tabs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "58e50c4fbb960df6",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "file-explorer",
|
||||||
|
"state": {
|
||||||
|
"sortOrder": "alphabetical",
|
||||||
|
"autoReveal": false
|
||||||
|
},
|
||||||
|
"icon": "lucide-folder-closed",
|
||||||
|
"title": "Files"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "db056d2cd1bd4a5b",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "search",
|
||||||
|
"state": {
|
||||||
|
"query": "",
|
||||||
|
"matchingCase": false,
|
||||||
|
"explainSearch": false,
|
||||||
|
"collapseAll": false,
|
||||||
|
"extraContext": false,
|
||||||
|
"sortOrder": "alphabetical"
|
||||||
|
},
|
||||||
|
"icon": "lucide-search",
|
||||||
|
"title": "Search"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "766ff21acbbfb952",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "bookmarks",
|
||||||
|
"state": {},
|
||||||
|
"icon": "lucide-bookmark",
|
||||||
|
"title": "Bookmarks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"direction": "horizontal",
|
||||||
|
"width": 300
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"id": "666e4843944fb168",
|
||||||
|
"type": "split",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "7fae59c3dd546dfc",
|
||||||
|
"type": "tabs",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "5a06b50e5b947981",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "backlink",
|
||||||
|
"state": {
|
||||||
|
"collapseAll": false,
|
||||||
|
"extraContext": false,
|
||||||
|
"sortOrder": "alphabetical",
|
||||||
|
"showSearch": false,
|
||||||
|
"searchQuery": "",
|
||||||
|
"backlinkCollapsed": false,
|
||||||
|
"unlinkedCollapsed": true
|
||||||
|
},
|
||||||
|
"icon": "links-coming-in",
|
||||||
|
"title": "Backlinks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "57089c89592d341c",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "outgoing-link",
|
||||||
|
"state": {
|
||||||
|
"linksCollapsed": false,
|
||||||
|
"unlinkedCollapsed": true
|
||||||
|
},
|
||||||
|
"icon": "links-going-out",
|
||||||
|
"title": "Outgoing links"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "648806ec4584fa8d",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "tag",
|
||||||
|
"state": {
|
||||||
|
"sortOrder": "frequency",
|
||||||
|
"useHierarchy": true,
|
||||||
|
"showSearch": false,
|
||||||
|
"searchQuery": ""
|
||||||
|
},
|
||||||
|
"icon": "lucide-tags",
|
||||||
|
"title": "Tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "490ac34f60740511",
|
||||||
|
"type": "leaf",
|
||||||
|
"state": {
|
||||||
|
"type": "outline",
|
||||||
|
"state": {
|
||||||
|
"followCursor": false,
|
||||||
|
"showSearch": false,
|
||||||
|
"searchQuery": ""
|
||||||
|
},
|
||||||
|
"icon": "lucide-list",
|
||||||
|
"title": "Outline"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"direction": "horizontal",
|
||||||
|
"width": 300,
|
||||||
|
"collapsed": true
|
||||||
|
},
|
||||||
|
"left-ribbon": {
|
||||||
|
"hiddenItems": {
|
||||||
|
"switcher:Open quick switcher": false,
|
||||||
|
"graph:Open graph view": false,
|
||||||
|
"canvas:Create new canvas": false,
|
||||||
|
"daily-notes:Open today's daily note": false,
|
||||||
|
"templates:Insert template": false,
|
||||||
|
"command-palette:Open command palette": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": "60d702f2c5c78820",
|
||||||
|
"lastOpenFiles": []
|
||||||
|
}
|
||||||
207
docs/encipher_decipher_renew_nkode_v2.md
Normal file
207
docs/encipher_decipher_renew_nkode_v2.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# Encipher, Decipher and Renew nKode
|
||||||
|
|
||||||
|
## Tenant Policy
|
||||||
|
- max nkode length: 10
|
||||||
|
- number of keys: 6
|
||||||
|
- properties per key: 9
|
||||||
|
- total number of properties: 54
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deterministic CSPRNG
|
||||||
|
- AES-CTR DRBG
|
||||||
|
- ChaCha20
|
||||||
|
|
||||||
|
## User Cipher Keys
|
||||||
|
Derive keys in memory from PRNG above:
|
||||||
|
- property key: [ 6890 54130 42240 40467 46502 31074 10598 63689 27697 54461 21116 31999
|
||||||
|
10698 14870 50779 48637 29314 33075 52993 42014 2837 1935 34274 63380
|
||||||
|
36021 26329 20788 39848 7335 2619 61516 61122 39878 32506 19151 6611
|
||||||
|
2803 10730 53682 39987 11998 42378 6081 8624 34336 15222 35632 33233
|
||||||
|
4072 53750 54671 63845 2770 43728]
|
||||||
|
- passcode key: [10632 49355 48031 9925 15082 24190 5137 14304 24524 16141]
|
||||||
|
- user position key: [45632 9012 27470 28203 15901 7554 16974 54240 53827]
|
||||||
|
- mask key: [ 8177 54825 26281 51895 8940 16695 19756 63041 7376 54396]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Keypad
|
||||||
|
- keypad example:<br/>
|
||||||
|
- user passcode indices: [48 12 7 36]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## nKode Cipher
|
||||||
|
|
||||||
|
### Passcode Hash
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 2
|
||||||
|
prop["user_property_key\n[ 6890 54130 42240 40467 46502 31074 10598 63689 27697 54461 21116 31999
|
||||||
|
10698 14870 50779 48637 29314 33075 52993 42014 2837 1935 34274 63380
|
||||||
|
36021 26329 20788 39848 7335 2619 61516 61122 39878 32506 19151 6611
|
||||||
|
2803 10730 53682 39987 11998 42378 6081 8624 34336 15222 35632 33233
|
||||||
|
4072 53750 54671 63845 2770 43728]"]
|
||||||
|
pass["user_passcode_indices\n[48 12 7 36]"]
|
||||||
|
space:2
|
||||||
|
sel(("select\nproperties")):2
|
||||||
|
pass --> sel
|
||||||
|
prop --> sel
|
||||||
|
space:2
|
||||||
|
passcode["user passcode properties:\n[ 4072 10698 63689 2803]"]:2
|
||||||
|
sel --> passcode
|
||||||
|
space:2
|
||||||
|
pad["zero pad to\nmax nkode length: 10"]:2
|
||||||
|
passcode -->pad
|
||||||
|
space:2
|
||||||
|
paddedpasscode["padded passcode:\n[ 4072 10698 63689 2803 0 0 0 0 0 0]"]
|
||||||
|
pad --> paddedpasscode
|
||||||
|
passkey["passcode key:\n[10632 49355 48031 9925 15082 24190 5137 14304 24524 16141]"]
|
||||||
|
space:2
|
||||||
|
xor2(("XOR")):2
|
||||||
|
passkey --> xor2
|
||||||
|
paddedpasscode --> xor2
|
||||||
|
space:2
|
||||||
|
cipheredpass["ciphered passcode:\n[ 9824 59649 17238 11318 15082 24190 5137 14304 24524 16141]"]:2
|
||||||
|
xor2 --> cipheredpass
|
||||||
|
space:2
|
||||||
|
hash(("hash")):2
|
||||||
|
cipheredpass --> hash
|
||||||
|
space:2
|
||||||
|
cipheredhashed["hashed ciphered passcode:\n$2b$12$XcXlcNKMyXQziv.kQWKngO88KUcm.xrn4YRZOOhGgIyMmpMw7NPJa"]:2
|
||||||
|
hash --> cipheredhashed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mask Encipher
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 2
|
||||||
|
passcode_idx["passcode indices:\n[48 12 7 36]"]
|
||||||
|
|
||||||
|
space:3
|
||||||
|
propidx(["Get Position Idx:\nmap each to element mod props_per_key"])
|
||||||
|
passcode_idx-->propidx
|
||||||
|
space:3
|
||||||
|
passcode_position_idx["passcode poition indices:\n[3, 3, 7, 0]"]
|
||||||
|
propidx --> passcode_position_idx
|
||||||
|
|
||||||
|
space:3
|
||||||
|
pad1(("Pad with\nrandom indices"))
|
||||||
|
passcode_position_idx --> pad1
|
||||||
|
|
||||||
|
space:3
|
||||||
|
posidx["Padded Passcode Position Indices:\n[3, 3, 7, 0, 0, 0, 5, 8, 8, 8]"]
|
||||||
|
pad1 --> posidx
|
||||||
|
user_pos["user position key:\n[45632 9012 27470 28203 15901 7554 16974 54240 53827]"]
|
||||||
|
|
||||||
|
space:2
|
||||||
|
sel(("select positions"))
|
||||||
|
user_pos --> sel
|
||||||
|
posidx --> sel
|
||||||
|
space:3
|
||||||
|
passcode_pos["ordered user passcode positions:\n[28203 28203 54240 45632 45632 45632 7554 53827 53827 53827]"]
|
||||||
|
sel --> passcode_pos
|
||||||
|
mask_key["mask key\n[ 8177 54825 26281 51895 8940 16695 19756 63041 7376 54396]"]
|
||||||
|
space:2
|
||||||
|
xor2(("XOR"))
|
||||||
|
mask_key --> xor2
|
||||||
|
passcode_pos --> xor2
|
||||||
|
space:3
|
||||||
|
mask["enciphered mask:\n cdq4ArVJePcB2PN3D2LrwyLN6mE="]
|
||||||
|
xor2 --> mask
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validate nKode
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 3
|
||||||
|
selected_keys["keys selected by user during login:\n[1, 3, 1, 4]"]
|
||||||
|
login_keypad["login keypad:\nKey 0: [ 9 28 2 39 49 32 42 34 44]
|
||||||
|
Key 1: [ 0 1 20 48 40 50 51 7 35]
|
||||||
|
Key 2: [18 19 47 21 13 14 24 25 8]
|
||||||
|
Key 3: [27 10 11 12 4 5 6 16 17]
|
||||||
|
Key 4: [36 37 38 3 22 23 33 43 53]
|
||||||
|
Key 5: [45 46 29 30 31 41 15 52 26]
|
||||||
|
"]
|
||||||
|
space:4
|
||||||
|
|
||||||
|
selectkeys(("filter keys"))
|
||||||
|
mask["enciphered mask:\n cdq4ArVJePcB2PN3D2LrwyLN6mE="]
|
||||||
|
mask_key["mask key:\n[ 8177 54825 26281 51895 8940 16695 19756 63041 7376 54396]"]
|
||||||
|
space:2
|
||||||
|
|
||||||
|
xor1(("XOR"))
|
||||||
|
mask --> xor1
|
||||||
|
mask_key --> xor1
|
||||||
|
selected_keys --> selectkeys
|
||||||
|
login_keypad --> selectkeys
|
||||||
|
space:3
|
||||||
|
|
||||||
|
ordered_keys["ordered keys:\n[[ 0 1 20 48 40 50 51 7 35]
|
||||||
|
[27 10 11 12 4 5 6 16 17]
|
||||||
|
[ 0 1 20 48 40 50 51 7 35]
|
||||||
|
[36 37 38 3 22 23 33 43 53]]"]
|
||||||
|
user_position_key["user position key:\n[45632 9012 27470 28203 15901 7554 16974 54240 53827]"]
|
||||||
|
passcode_pos["ordered user passcode positions:\n[28203 28203 54240 45632 45632 45632 7554 53827 53827 53827]"]
|
||||||
|
selectkeys --> ordered_keys
|
||||||
|
xor1 --> passcode_pos
|
||||||
|
space:8
|
||||||
|
|
||||||
|
get_passcode_idxs(("recover passcode\nposition indices"))
|
||||||
|
user_position_key --> get_passcode_idxs
|
||||||
|
passcode_pos --> get_passcode_idxs
|
||||||
|
space:8
|
||||||
|
|
||||||
|
passcode_pos_idxs["padded passcode position indices:\n[3, 3, 7, 0, 0, 0, 5, 8, 8, 8]"]
|
||||||
|
get_passcode_idxs --> passcode_pos_idxs
|
||||||
|
space:3
|
||||||
|
|
||||||
|
get_presumed_idxs(("recover passcode\nproperty indices"))
|
||||||
|
ordered_keys --> get_presumed_idxs
|
||||||
|
passcode_pos_idxs --> get_presumed_idxs
|
||||||
|
space:5
|
||||||
|
|
||||||
|
passcode_prop_idxs["presumed passcode property indices:\n[48 12 7 36]"]
|
||||||
|
prop["user_property_key\n[ 6890 54130 42240 40467 46502 31074 10598 63689 27697 54461 21116 31999
|
||||||
|
10698 14870 50779 48637 29314 33075 52993 42014 2837 1935 34274 63380
|
||||||
|
36021 26329 20788 39848 7335 2619 61516 61122 39878 32506 19151 6611
|
||||||
|
2803 10730 53682 39987 11998 42378 6081 8624 34336 15222 35632 33233
|
||||||
|
4072 53750 54671 63845 2770 43728]"]
|
||||||
|
cipheredhashed["hashed ciphered passcode:\n$2b$12$XcXlcNKMyXQziv.kQWKngO88KUcm.xrn4YRZOOhGgIyMmpMw7NPJa"]
|
||||||
|
get_presumed_idxs --> passcode_prop_idxs
|
||||||
|
space:3
|
||||||
|
|
||||||
|
sel(("select\nproperties"))
|
||||||
|
passcode_prop_idxs --> sel
|
||||||
|
prop --> sel
|
||||||
|
space:5
|
||||||
|
|
||||||
|
passcode_prop["presumed passcode properties:\n[ 4072 10698 63689 2803]"]
|
||||||
|
sel --> passcode_prop
|
||||||
|
space:5
|
||||||
|
|
||||||
|
cipher(("encipher"))
|
||||||
|
passcode_prop --> cipher
|
||||||
|
space:5
|
||||||
|
|
||||||
|
cipheredpass["ciphered passcode:\n[ 9824 59649 17238 11318 15082 24190 5137 14304 24524 16141]"]
|
||||||
|
cipher --> cipheredpass
|
||||||
|
space:7
|
||||||
|
|
||||||
|
|
||||||
|
comp{"compare"}
|
||||||
|
cipheredpass --> comp
|
||||||
|
cipheredhashed --> comp
|
||||||
|
space:5
|
||||||
|
|
||||||
|
suc(("success"))
|
||||||
|
comp --"Equal"--> suc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Renew
|
||||||
|
A renewal happens every login
|
||||||
|
### Nonce Renew
|
||||||
|
With ChaCha20, we can renew the keys and hash every login with a new nonce
|
||||||
|
### Secret Renew
|
||||||
|
The secret is renewed less frequently. It's stored securely using a service like AWS Secrets Manager.
|
||||||
69
docs/scripts/render_encipher_decipher_diagrams_v2.py
Normal file
69
docs/scripts/render_encipher_decipher_diagrams_v2.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import numpy as np
|
||||||
|
from docs.scripts.utils import render_markdown_template
|
||||||
|
from src.models import NKodePolicy, KeypadSize
|
||||||
|
from src.user_keypad import UserKeypad
|
||||||
|
from src.nkode_cipher_v2.nkode_cipher import NKodeCipher
|
||||||
|
from src.utils import select_keys_with_passcode_values
|
||||||
|
|
||||||
|
|
||||||
|
def display_keypad(icons_array: np.ndarray, props_per_key: int) -> str:
|
||||||
|
icons = ""
|
||||||
|
for idx, row in enumerate(icons_array.reshape(-1, props_per_key)):
|
||||||
|
icons += f"Key {idx}: "
|
||||||
|
icons += str(row)
|
||||||
|
icons += "\n"
|
||||||
|
return icons
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
policy = NKodePolicy(
|
||||||
|
max_nkode_len=10,
|
||||||
|
min_nkode_len=4,
|
||||||
|
distinct_positions=0,
|
||||||
|
distinct_properties=4,
|
||||||
|
)
|
||||||
|
keypad_size = KeypadSize(
|
||||||
|
numb_of_keys=6,
|
||||||
|
props_per_key=9
|
||||||
|
)
|
||||||
|
user_keys = NKodeCipher.create(keypad_size=keypad_size, max_nkode_len=policy.max_nkode_len)
|
||||||
|
passcode_len = 4
|
||||||
|
|
||||||
|
passcode_property_indices = np.random.choice([i for i in range(keypad_size.total_props)], size=passcode_len,
|
||||||
|
replace=False)
|
||||||
|
user_passcode = user_keys.property_key[passcode_property_indices]
|
||||||
|
pad_len = policy.max_nkode_len - passcode_len
|
||||||
|
padded_passcode = np.concatenate((user_passcode, np.zeros(pad_len, dtype=user_passcode.dtype)))
|
||||||
|
ciphered_passcode = padded_passcode ^ user_keys.pass_key
|
||||||
|
cipher = user_keys.encipher_nkode(passcode_property_indices.tolist())
|
||||||
|
padded_passcode_position_indices = user_keys.get_passcode_position_indices_padded(
|
||||||
|
passcode_property_indices.tolist())
|
||||||
|
|
||||||
|
ordered_user_position_key = user_keys.position_key[padded_passcode_position_indices]
|
||||||
|
login_keypad = UserKeypad.create(keypad_size)
|
||||||
|
selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad.keypad,
|
||||||
|
keypad_size.props_per_key)
|
||||||
|
context = {
|
||||||
|
"max_nkode_len": policy.max_nkode_len,
|
||||||
|
"numb_of_keys": keypad_size.numb_of_keys,
|
||||||
|
"props_per_key": keypad_size.props_per_key,
|
||||||
|
"user_property_key": user_keys.property_key,
|
||||||
|
"pass_key": user_keys.pass_key,
|
||||||
|
"user_position_key": user_keys.position_key,
|
||||||
|
"mask_key": user_keys.mask_key,
|
||||||
|
"user_passcode_idxs": passcode_property_indices,
|
||||||
|
"user_passcode_props": user_passcode,
|
||||||
|
"padded_passcode": padded_passcode,
|
||||||
|
"ciphered_passcode": ciphered_passcode,
|
||||||
|
"code": cipher.code,
|
||||||
|
"passcode_position_idxs": padded_passcode_position_indices[:passcode_len],
|
||||||
|
"pad_user_passcode_idxs": padded_passcode_position_indices,
|
||||||
|
"ordered_user_position_key": ordered_user_position_key,
|
||||||
|
"mask": cipher.mask,
|
||||||
|
"login_keypad": display_keypad(login_keypad.keypad, keypad_size.props_per_key),
|
||||||
|
"selected_keys": selected_keys_login,
|
||||||
|
"ordered_keys": login_keypad.keypad.reshape(-1, keypad_size.props_per_key)[selected_keys_login],
|
||||||
|
}
|
||||||
|
render_markdown_template(Path("../templates/encipher_decipher_renew_nkode_v2.template.md"),
|
||||||
|
Path("../encipher_decipher_renew_nkode_v2.md"), context)
|
||||||
70
docs/scripts/render_zero_trust_nkode.py
Normal file
70
docs/scripts/render_zero_trust_nkode.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import numpy as np
|
||||||
|
from docs.scripts.utils import render_markdown_template
|
||||||
|
from src.models import NKodePolicy, KeypadSize
|
||||||
|
from src.user_keypad import UserKeypad
|
||||||
|
from src.nkode_cipher_v2.nkode_cipher import NKodeCipher
|
||||||
|
from src.utils import select_keys_with_passcode_values
|
||||||
|
|
||||||
|
|
||||||
|
def display_keypad(icons_array: np.ndarray, props_per_key: int) -> str:
|
||||||
|
icons = ""
|
||||||
|
for idx, row in enumerate(icons_array.reshape(-1, props_per_key)):
|
||||||
|
icons += f"Key {idx}: "
|
||||||
|
icons += str(row)
|
||||||
|
icons += "\n"
|
||||||
|
return icons
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
policy = NKodePolicy(
|
||||||
|
max_nkode_len=10,
|
||||||
|
min_nkode_len=4,
|
||||||
|
distinct_positions=0,
|
||||||
|
distinct_properties=4,
|
||||||
|
)
|
||||||
|
keypad_size = KeypadSize(
|
||||||
|
numb_of_keys=6,
|
||||||
|
props_per_key=9
|
||||||
|
)
|
||||||
|
user_keys = NKodeCipher.create(keypad_size=keypad_size, max_nkode_len=policy.max_nkode_len)
|
||||||
|
passcode_len = 4
|
||||||
|
|
||||||
|
passcode_property_indices = np.random.choice([i for i in range(keypad_size.total_props)], size=passcode_len,
|
||||||
|
replace=False)
|
||||||
|
user_passcode = user_keys.property_key[passcode_property_indices]
|
||||||
|
pad_len = policy.max_nkode_len - passcode_len
|
||||||
|
padded_passcode = np.concatenate((user_passcode, np.zeros(pad_len, dtype=user_passcode.dtype)))
|
||||||
|
ciphered_passcode = padded_passcode ^ user_keys.pass_key
|
||||||
|
cipher = user_keys.encipher_nkode(passcode_property_indices.tolist())
|
||||||
|
padded_passcode_position_indices = user_keys.get_passcode_position_indices_padded(
|
||||||
|
passcode_property_indices.tolist())
|
||||||
|
|
||||||
|
ordered_user_position_key = user_keys.position_key[padded_passcode_position_indices]
|
||||||
|
login_keypad = UserKeypad.create(keypad_size)
|
||||||
|
selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad.keypad,
|
||||||
|
keypad_size.props_per_key)
|
||||||
|
context = {
|
||||||
|
"max_nkode_len": policy.max_nkode_len,
|
||||||
|
"numb_of_keys": keypad_size.numb_of_keys,
|
||||||
|
"props_per_key": keypad_size.props_per_key,
|
||||||
|
"user_property_key": user_keys.property_key,
|
||||||
|
"pass_key": user_keys.pass_key,
|
||||||
|
"user_position_key": user_keys.position_key,
|
||||||
|
"mask_key": user_keys.mask_key,
|
||||||
|
"user_passcode_idxs": passcode_property_indices,
|
||||||
|
"user_passcode_props": user_passcode,
|
||||||
|
"padded_passcode": padded_passcode,
|
||||||
|
"ciphered_passcode": ciphered_passcode,
|
||||||
|
"code": cipher.code,
|
||||||
|
"passcode_position_idxs": padded_passcode_position_indices[:passcode_len],
|
||||||
|
"pad_user_passcode_idxs": padded_passcode_position_indices,
|
||||||
|
"ordered_user_position_key": ordered_user_position_key,
|
||||||
|
"mask": cipher.mask,
|
||||||
|
"login_keypad": display_keypad(login_keypad.keypad, keypad_size.props_per_key),
|
||||||
|
"selected_keys": selected_keys_login,
|
||||||
|
"ordered_keys": login_keypad.keypad.reshape(-1, keypad_size.props_per_key)[selected_keys_login],
|
||||||
|
}
|
||||||
|
|
||||||
|
render_markdown_template(Path("../templates/zero_trust_nkode.template.md"),
|
||||||
|
Path("../zero_trust_nkode.md"), context)
|
||||||
187
docs/templates/encipher_decipher_renew_nkode_v2.template.md
vendored
Normal file
187
docs/templates/encipher_decipher_renew_nkode_v2.template.md
vendored
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# Encipher, Decipher and Renew nKode
|
||||||
|
|
||||||
|
## Tenant Policy
|
||||||
|
- max nkode length: {{ max_nkode_len }}
|
||||||
|
- number of keys: {{ numb_of_keys }}
|
||||||
|
- properties per key: {{ props_per_key }}
|
||||||
|
- total number of properties: {{ numb_of_keys * props_per_key }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Deterministic CSPRNG
|
||||||
|
- AES-CTR DRBG
|
||||||
|
- ChaCha20
|
||||||
|
|
||||||
|
## User Cipher Keys
|
||||||
|
Derive keys in memory from PRNG above:
|
||||||
|
- property key: {{ user_property_key }}
|
||||||
|
- passcode key: {{ pass_key }}
|
||||||
|
- user position key: {{ user_position_key }}
|
||||||
|
- mask key: {{ mask_key }}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Keypad
|
||||||
|
- keypad example:<br/>{{ login_keypad_md }}
|
||||||
|
- user passcode indices: {{ user_passcode_idxs}}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## nKode Cipher
|
||||||
|
|
||||||
|
### Passcode Hash
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 2
|
||||||
|
prop["user_property_key\n{{user_property_key}}"]
|
||||||
|
pass["user_passcode_indices\n{{user_passcode_idxs}}"]
|
||||||
|
space:2
|
||||||
|
sel(("select\nproperties")):2
|
||||||
|
pass --> sel
|
||||||
|
prop --> sel
|
||||||
|
space:2
|
||||||
|
passcode["user passcode properties:\n{{user_passcode_props}}"]:2
|
||||||
|
sel --> passcode
|
||||||
|
space:2
|
||||||
|
pad["zero pad to\nmax nkode length: {{max_nkode_len}}"]:2
|
||||||
|
passcode -->pad
|
||||||
|
space:2
|
||||||
|
paddedpasscode["padded passcode:\n{{padded_passcode}}"]
|
||||||
|
pad --> paddedpasscode
|
||||||
|
passkey["passcode key:\n{{pass_key}}"]
|
||||||
|
space:2
|
||||||
|
xor2(("XOR")):2
|
||||||
|
passkey --> xor2
|
||||||
|
paddedpasscode --> xor2
|
||||||
|
space:2
|
||||||
|
cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"]:2
|
||||||
|
xor2 --> cipheredpass
|
||||||
|
space:2
|
||||||
|
hash(("hash")):2
|
||||||
|
cipheredpass --> hash
|
||||||
|
space:2
|
||||||
|
cipheredhashed["hashed ciphered passcode:\n{{code}}"]:2
|
||||||
|
hash --> cipheredhashed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mask Encipher
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 2
|
||||||
|
passcode_idx["passcode indices:\n{{user_passcode_idxs}}"]
|
||||||
|
|
||||||
|
space:3
|
||||||
|
propidx(["Get Position Idx:\nmap each to element mod props_per_key"])
|
||||||
|
passcode_idx-->propidx
|
||||||
|
space:3
|
||||||
|
passcode_position_idx["passcode poition indices:\n{{passcode_position_idxs}}"]
|
||||||
|
propidx --> passcode_position_idx
|
||||||
|
|
||||||
|
space:3
|
||||||
|
pad1(("Pad with\nrandom indices"))
|
||||||
|
passcode_position_idx --> pad1
|
||||||
|
|
||||||
|
space:3
|
||||||
|
posidx["Padded Passcode Position Indices:\n{{pad_user_passcode_idxs}}"]
|
||||||
|
pad1 --> posidx
|
||||||
|
user_pos["user position key:\n{{user_position_key}}"]
|
||||||
|
|
||||||
|
space:2
|
||||||
|
sel(("select positions"))
|
||||||
|
user_pos --> sel
|
||||||
|
posidx --> sel
|
||||||
|
space:3
|
||||||
|
passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"]
|
||||||
|
sel --> passcode_pos
|
||||||
|
mask_key["mask key\n{{mask_key}}"]
|
||||||
|
space:2
|
||||||
|
xor2(("XOR"))
|
||||||
|
mask_key --> xor2
|
||||||
|
passcode_pos --> xor2
|
||||||
|
space:3
|
||||||
|
mask["enciphered mask:\n {{mask}}"]
|
||||||
|
xor2 --> mask
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validate nKode
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
block-beta
|
||||||
|
columns 3
|
||||||
|
selected_keys["keys selected by user during login:\n{{selected_keys}}"]
|
||||||
|
login_keypad["login keypad:\n{{login_keypad}}"]
|
||||||
|
space:4
|
||||||
|
|
||||||
|
selectkeys(("filter keys"))
|
||||||
|
mask["enciphered mask:\n {{mask}}"]
|
||||||
|
mask_key["mask key:\n{{mask_key}}"]
|
||||||
|
space:2
|
||||||
|
|
||||||
|
xor1(("XOR"))
|
||||||
|
mask --> xor1
|
||||||
|
mask_key --> xor1
|
||||||
|
selected_keys --> selectkeys
|
||||||
|
login_keypad --> selectkeys
|
||||||
|
space:3
|
||||||
|
|
||||||
|
ordered_keys["ordered keys:\n{{ordered_keys}}"]
|
||||||
|
user_position_key["user position key:\n{{user_position_key}}"]
|
||||||
|
passcode_pos["ordered user passcode positions:\n{{ordered_user_position_key}}"]
|
||||||
|
selectkeys --> ordered_keys
|
||||||
|
xor1 --> passcode_pos
|
||||||
|
space:8
|
||||||
|
|
||||||
|
get_passcode_idxs(("recover passcode\nposition indices"))
|
||||||
|
user_position_key --> get_passcode_idxs
|
||||||
|
passcode_pos --> get_passcode_idxs
|
||||||
|
space:8
|
||||||
|
|
||||||
|
passcode_pos_idxs["padded passcode position indices:\n{{pad_user_passcode_idxs}}"]
|
||||||
|
get_passcode_idxs --> passcode_pos_idxs
|
||||||
|
space:3
|
||||||
|
|
||||||
|
get_presumed_idxs(("recover passcode\nproperty indices"))
|
||||||
|
ordered_keys --> get_presumed_idxs
|
||||||
|
passcode_pos_idxs --> get_presumed_idxs
|
||||||
|
space:5
|
||||||
|
|
||||||
|
passcode_prop_idxs["presumed passcode property indices:\n{{user_passcode_idxs}}"]
|
||||||
|
prop["user_property_key\n{{user_property_key}}"]
|
||||||
|
cipheredhashed["hashed ciphered passcode:\n{{code}}"]
|
||||||
|
get_presumed_idxs --> passcode_prop_idxs
|
||||||
|
space:3
|
||||||
|
|
||||||
|
sel(("select\nproperties"))
|
||||||
|
passcode_prop_idxs --> sel
|
||||||
|
prop --> sel
|
||||||
|
space:5
|
||||||
|
|
||||||
|
passcode_prop["presumed passcode properties:\n{{user_passcode_props}}"]
|
||||||
|
sel --> passcode_prop
|
||||||
|
space:5
|
||||||
|
|
||||||
|
cipher(("encipher"))
|
||||||
|
passcode_prop --> cipher
|
||||||
|
space:5
|
||||||
|
|
||||||
|
cipheredpass["ciphered passcode:\n{{ciphered_passcode}}"]
|
||||||
|
cipher --> cipheredpass
|
||||||
|
space:7
|
||||||
|
|
||||||
|
|
||||||
|
comp{"compare"}
|
||||||
|
cipheredpass --> comp
|
||||||
|
cipheredhashed --> comp
|
||||||
|
space:5
|
||||||
|
|
||||||
|
suc(("success"))
|
||||||
|
comp --"Equal"--> suc
|
||||||
|
```
|
||||||
|
|
||||||
|
## Renew
|
||||||
|
A renewal happens every login
|
||||||
|
### Nonce Renew
|
||||||
|
With ChaCha20, we can renew the keys and hash every login with a new nonce
|
||||||
|
### Secret Renew
|
||||||
|
The secret is renewed less frequently. It's stored securely using a service like AWS Secrets Manager.
|
||||||
|
|
||||||
43
docs/templates/zero_trust_nkode.template.md
vendored
Normal file
43
docs/templates/zero_trust_nkode.template.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Zero Trust nKode with aPAKE (OPAQUE)
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Client
|
||||||
|
participant Server
|
||||||
|
Note over Client, Server: Enrollment
|
||||||
|
Client ->> Server: Signup Session: email
|
||||||
|
Client ->> Client: Create 128-bit Secret Key
|
||||||
|
Note left of Client: Request user stores Secret Key in a safe place
|
||||||
|
Client ->> Server: OPAQUE Register with Secret Key<br/>https://github.com/facebook/opaque-ke
|
||||||
|
Client ->> Server: OPAQUE Login with email + Secret Key
|
||||||
|
opt Secret Key OPAQUE tunnel
|
||||||
|
Client ->> Server: Get New Icons
|
||||||
|
Server -->> Client: icons
|
||||||
|
Note left of Client: Icons are stored on Client
|
||||||
|
Note left of Client: well-known nonce: 0x1 (or any number)
|
||||||
|
Client ->> Client: Assign random names to icons from<br/>secret_key and well known nonce
|
||||||
|
Client ->> Server: list of random icon names
|
||||||
|
Note right of Server: Only a client with the secret key can request these icons.<br/>Server doesn't know the owner
|
||||||
|
loop assign icons
|
||||||
|
Client ->> Client: Regenerate 4-6 icons until user accepts them
|
||||||
|
end
|
||||||
|
|
||||||
|
Client ->> Client: Create new nonce
|
||||||
|
Client ->> Client: ChaCha20 key derivation (pass_key, mask_key, prop_key, pos_key)
|
||||||
|
Client ->> Client: Compute Mask
|
||||||
|
Note left of Client: User Password is concat([list of assigned icon values])
|
||||||
|
Client ->> Server: OPAQUE Register with User Password + nonce, mask
|
||||||
|
end
|
||||||
|
Note over Client, Server: Login
|
||||||
|
Client ->> Server: OPAQUE Login with email + Secret Key
|
||||||
|
opt Secret Key OPAQUE tunnel
|
||||||
|
Server ->> Client: nonce, mask
|
||||||
|
Client ->> Client: Display Keypad to User<br/>User makes key selection
|
||||||
|
Client ->> Client: recover user password
|
||||||
|
Client ->> Server: OPAQUE Password Login
|
||||||
|
end
|
||||||
|
Note over Client, Server: User Session
|
||||||
|
opt Secret Key PAKE Key XOR nKode PAKE Key tunnel
|
||||||
|
Client ->> Server: all communication goes through this double PAKE
|
||||||
|
end
|
||||||
|
```
|
||||||
@@ -2,6 +2,18 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-03-27T19:17:57.439685Z",
|
||||||
|
"start_time": "2025-03-27T19:17:57.405237Z"
|
||||||
|
},
|
||||||
|
"collapsed": false,
|
||||||
|
"jupyter": {
|
||||||
|
"outputs_hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"import sys\n",
|
"import sys\n",
|
||||||
"import os\n",
|
"import os\n",
|
||||||
@@ -25,25 +37,18 @@
|
|||||||
" interface_keypad = keypad.reshape(-1, props_per_key)\n",
|
" interface_keypad = keypad.reshape(-1, props_per_key)\n",
|
||||||
" for idx, key_vals in enumerate(interface_keypad):\n",
|
" for idx, key_vals in enumerate(interface_keypad):\n",
|
||||||
" print(f\"Key {idx}: {key_vals}\")\n"
|
" print(f\"Key {idx}: {key_vals}\")\n"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false,
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-03-27T19:17:57.439685Z",
|
|
||||||
"start_time": "2025-03-27T19:17:57.405237Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 2,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.446190Z",
|
"end_time": "2025-03-27T19:17:57.446190Z",
|
||||||
"start_time": "2025-03-27T19:17:57.443952Z"
|
"start_time": "2025-03-27T19:17:57.443952Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"api = NKodeAPI()\n",
|
"api = NKodeAPI()\n",
|
||||||
"user_icons = np.array([\n",
|
"user_icons = np.array([\n",
|
||||||
@@ -54,13 +59,11 @@
|
|||||||
" \"🦁\", \"🐻\", \"🐸\", \"🐙\", \"🦄\",\n",
|
" \"🦁\", \"🐻\", \"🐸\", \"🐙\", \"🦄\",\n",
|
||||||
" \"🌟\", \"⚡\", \"🔥\", \"🍕\", \"🎉\"\n",
|
" \"🌟\", \"⚡\", \"🔥\", \"🍕\", \"🎉\"\n",
|
||||||
"])"
|
"])"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 2
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### nKode Customer\n",
|
"### nKode Customer\n",
|
||||||
"An nKode customer is business has employees (users). An nKode API can service many customers each with their own users.\n",
|
"An nKode customer is business has employees (users). An nKode API can service many customers each with their own users.\n",
|
||||||
@@ -69,8 +72,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"#### Customer Cipher Keys\n",
|
"#### Customer Cipher Keys\n",
|
||||||
"Each customer has unique cipher keys.\n",
|
"Each customer has unique cipher keys.\n",
|
||||||
@@ -81,36 +84,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.487136Z",
|
"end_time": "2025-03-27T19:17:57.487136Z",
|
||||||
"start_time": "2025-03-27T19:17:57.475079Z"
|
"start_time": "2025-03-27T19:17:57.475079Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"policy = NKodePolicy(\n",
|
|
||||||
" max_nkode_len=10,\n",
|
|
||||||
" min_nkode_len=4,\n",
|
|
||||||
" distinct_positions=0,\n",
|
|
||||||
" distinct_properties=4,\n",
|
|
||||||
")\n",
|
|
||||||
"keypad_size = KeypadSize(\n",
|
|
||||||
" numb_of_keys = 5,\n",
|
|
||||||
" props_per_key = 6\n",
|
|
||||||
")\n",
|
|
||||||
"customer_id = api.create_new_customer(keypad_size, policy)\n",
|
|
||||||
"customer = api.customers[customer_id]\n",
|
|
||||||
"print(f\"Customer Position Key: {customer.cipher.position_key}\")\n",
|
|
||||||
"print(f\"Customer Properties Key:\")\n",
|
|
||||||
"customer_prop_keypad = customer.cipher.property_key.reshape(-1, keypad_size.props_per_key)\n",
|
|
||||||
"for idx, key_vals in enumerate(customer_prop_keypad):\n",
|
|
||||||
" print(f\"{key_vals}\")\n",
|
|
||||||
"position_properties_dict = dict(zip(customer.cipher.position_key, customer_prop_keypad.T))\n",
|
|
||||||
"print(f\"Position to Properties Map:\")\n",
|
|
||||||
"for pos_val, props in position_properties_dict.items():\n",
|
|
||||||
" print(f\"{pos_val}: {props}\")"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -133,23 +114,39 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 3
|
"source": [
|
||||||
|
"policy = NKodePolicy(\n",
|
||||||
|
" max_nkode_len=10,\n",
|
||||||
|
" min_nkode_len=4,\n",
|
||||||
|
" distinct_positions=0,\n",
|
||||||
|
" distinct_properties=4,\n",
|
||||||
|
")\n",
|
||||||
|
"keypad_size = KeypadSize(\n",
|
||||||
|
" numb_of_keys = 5,\n",
|
||||||
|
" props_per_key = 6\n",
|
||||||
|
")\n",
|
||||||
|
"customer_id = api.create_new_customer(keypad_size, policy)\n",
|
||||||
|
"customer = api.customers[customer_id]\n",
|
||||||
|
"print(f\"Customer Position Key: {customer.cipher.position_key}\")\n",
|
||||||
|
"print(f\"Customer Properties Key:\")\n",
|
||||||
|
"customer_prop_keypad = customer.cipher.property_key.reshape(-1, keypad_size.props_per_key)\n",
|
||||||
|
"for idx, key_vals in enumerate(customer_prop_keypad):\n",
|
||||||
|
" print(f\"{key_vals}\")\n",
|
||||||
|
"position_properties_dict = dict(zip(customer.cipher.position_key, customer_prop_keypad.T))\n",
|
||||||
|
"print(f\"Position to Properties Map:\")\n",
|
||||||
|
"for pos_val, props in position_properties_dict.items():\n",
|
||||||
|
" print(f\"{pos_val}: {props}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 4,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.507904Z",
|
"end_time": "2025-03-27T19:17:57.507904Z",
|
||||||
"start_time": "2025-03-27T19:17:57.505187Z"
|
"start_time": "2025-03-27T19:17:57.505187Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"user_icon_keypad = user_icons.reshape(-1, keypad_size.props_per_key)\n",
|
|
||||||
"pos_icons_dict = dict(zip(customer.cipher.position_key, user_icon_keypad.T))\n",
|
|
||||||
"print(\"Position Value to Icons Map:\")\n",
|
|
||||||
"for pos_val, icons in pos_icons_dict.items():\n",
|
|
||||||
" print(f\"{pos_val}: {icons}\")\n"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -165,11 +162,17 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 4
|
"source": [
|
||||||
|
"user_icon_keypad = user_icons.reshape(-1, keypad_size.props_per_key)\n",
|
||||||
|
"pos_icons_dict = dict(zip(customer.cipher.position_key, user_icon_keypad.T))\n",
|
||||||
|
"print(\"Position Value to Icons Map:\")\n",
|
||||||
|
"for pos_val, icons in pos_icons_dict.items():\n",
|
||||||
|
" print(f\"{pos_val}: {icons}\")\n"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### User Signup\n",
|
"### User Signup\n",
|
||||||
"Users can create an nKode with these steps:\n",
|
"Users can create an nKode with these steps:\n",
|
||||||
@@ -187,30 +190,23 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.541997Z",
|
"end_time": "2025-03-27T19:17:57.541997Z",
|
||||||
"start_time": "2025-03-27T19:17:57.534379Z"
|
"start_time": "2025-03-27T19:17:57.534379Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"username = random_username()\n",
|
|
||||||
"signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username)\n",
|
|
||||||
"display(Markdown(\"\"\"### Icon Keypad\"\"\"))\n",
|
|
||||||
"keypad_view(user_icons[set_signup_keypad], keypad_size.numb_of_keys)\n",
|
|
||||||
"display(Markdown(\"\"\"### Index Keypad\"\"\"))\n",
|
|
||||||
"keypad_view(set_signup_keypad, keypad_size.numb_of_keys)\n",
|
|
||||||
"display(Markdown(\"\"\"### Customer Properties Keypad\"\"\"))\n",
|
|
||||||
"keypad_view(customer.cipher.property_key[set_signup_keypad], keypad_size.numb_of_keys)"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
|
"text/markdown": [
|
||||||
|
"### Icon Keypad"
|
||||||
|
],
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<IPython.core.display.Markdown object>"
|
"<IPython.core.display.Markdown object>"
|
||||||
],
|
]
|
||||||
"text/markdown": "### Icon Keypad"
|
|
||||||
},
|
},
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "display_data"
|
"output_type": "display_data"
|
||||||
@@ -228,10 +224,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
|
"text/markdown": [
|
||||||
|
"### Index Keypad"
|
||||||
|
],
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<IPython.core.display.Markdown object>"
|
"<IPython.core.display.Markdown object>"
|
||||||
],
|
]
|
||||||
"text/markdown": "### Index Keypad"
|
|
||||||
},
|
},
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "display_data"
|
"output_type": "display_data"
|
||||||
@@ -249,10 +247,12 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
|
"text/markdown": [
|
||||||
|
"### Customer Properties Keypad"
|
||||||
|
],
|
||||||
"text/plain": [
|
"text/plain": [
|
||||||
"<IPython.core.display.Markdown object>"
|
"<IPython.core.display.Markdown object>"
|
||||||
],
|
]
|
||||||
"text/markdown": "### Customer Properties Keypad"
|
|
||||||
},
|
},
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"output_type": "display_data"
|
"output_type": "display_data"
|
||||||
@@ -269,33 +269,34 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 5
|
"source": [
|
||||||
|
"username = random_username()\n",
|
||||||
|
"signup_session_id, set_signup_keypad = api.generate_signup_keypad(customer_id, username)\n",
|
||||||
|
"display(Markdown(\"\"\"### Icon Keypad\"\"\"))\n",
|
||||||
|
"keypad_view(user_icons[set_signup_keypad], keypad_size.numb_of_keys)\n",
|
||||||
|
"display(Markdown(\"\"\"### Index Keypad\"\"\"))\n",
|
||||||
|
"keypad_view(set_signup_keypad, keypad_size.numb_of_keys)\n",
|
||||||
|
"display(Markdown(\"\"\"### Customer Properties Keypad\"\"\"))\n",
|
||||||
|
"keypad_view(customer.cipher.property_key[set_signup_keypad], keypad_size.numb_of_keys)"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Set nKode\n",
|
"### Set nKode\n",
|
||||||
"The client receives `user_icons`, `set_signup_keypad`\n"
|
"The client receives `user_icons`, `set_signup_keypad`\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 6,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.582109Z",
|
"end_time": "2025-03-27T19:17:57.582109Z",
|
||||||
"start_time": "2025-03-27T19:17:57.578783Z"
|
"start_time": "2025-03-27T19:17:57.578783Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"passcode_len = 4\n",
|
|
||||||
"passcode_property_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n",
|
|
||||||
"selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_signup_keypad, keypad_size.numb_of_keys)\n",
|
|
||||||
"print(f\"User Passcode Indices: {passcode_property_indices}\")\n",
|
|
||||||
"print(f\"User Passcode Icons: {user_icons[passcode_property_indices]}\")\n",
|
|
||||||
"print(f\"User Passcode Server-side properties: {customer.cipher.property_key[passcode_property_indices]}\")\n",
|
|
||||||
"print(f\"Selected Keys: {selected_keys_set}\")"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -308,32 +309,33 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 6
|
"source": [
|
||||||
|
"passcode_len = 4\n",
|
||||||
|
"passcode_property_indices = np.random.choice(set_signup_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n",
|
||||||
|
"selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_signup_keypad, keypad_size.numb_of_keys)\n",
|
||||||
|
"print(f\"User Passcode Indices: {passcode_property_indices}\")\n",
|
||||||
|
"print(f\"User Passcode Icons: {user_icons[passcode_property_indices]}\")\n",
|
||||||
|
"print(f\"User Passcode Server-side properties: {customer.cipher.property_key[passcode_property_indices]}\")\n",
|
||||||
|
"print(f\"Selected Keys: {selected_keys_set}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Confirm nKode\n",
|
"### Confirm nKode\n",
|
||||||
"Submit the set key entry to render the confirm keypad."
|
"Submit the set key entry to render the confirm keypad."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 7,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.846195Z",
|
"end_time": "2025-03-27T19:17:57.846195Z",
|
||||||
"start_time": "2025-03-27T19:17:57.599899Z"
|
"start_time": "2025-03-27T19:17:57.599899Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"confirm_keypad = api.set_nkode(customer_id, selected_keys_set, signup_session_id)\n",
|
|
||||||
"keypad_view(confirm_keypad, keypad_size.numb_of_keys)\n",
|
|
||||||
"selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys)\n",
|
|
||||||
"print(f\"Selected Keys\\n{selected_keys_confirm}\")\n",
|
|
||||||
"success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id)\n",
|
|
||||||
"assert success"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -349,31 +351,31 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 7
|
"source": [
|
||||||
|
"confirm_keypad = api.set_nkode(customer_id, selected_keys_set, signup_session_id)\n",
|
||||||
|
"keypad_view(confirm_keypad, keypad_size.numb_of_keys)\n",
|
||||||
|
"selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys)\n",
|
||||||
|
"print(f\"Selected Keys\\n{selected_keys_confirm}\")\n",
|
||||||
|
"success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id)\n",
|
||||||
|
"assert success"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"source": "### Inferring an nKode selection"
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Inferring an nKode selection"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.858315Z",
|
"end_time": "2025-03-27T19:17:57.858315Z",
|
||||||
"start_time": "2025-03-27T19:17:57.855013Z"
|
"start_time": "2025-03-27T19:17:57.855013Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"for idx in range(passcode_len):\n",
|
|
||||||
" selected_key_set = selected_keys_set[idx]\n",
|
|
||||||
" selected_set_key_idx = set_signup_keypad.reshape(-1, keypad_size.numb_of_keys)[selected_key_set]\n",
|
|
||||||
" print(f\"Set Key {idx}: {user_icons[selected_set_key_idx]}\")\n",
|
|
||||||
" selected_key_confirm = selected_keys_confirm[idx]\n",
|
|
||||||
" selected_confirm_key_idx = confirm_keypad.reshape(-1, keypad_size.numb_of_keys)[selected_key_confirm]\n",
|
|
||||||
" print(f\"Confirm Key {idx}: {user_icons[selected_confirm_key_idx]}\")\n",
|
|
||||||
" print(f\"Overlapping icon {user_icons[passcode_property_indices[idx]]}\")"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -394,11 +396,20 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 8
|
"source": [
|
||||||
|
"for idx in range(passcode_len):\n",
|
||||||
|
" selected_key_set = selected_keys_set[idx]\n",
|
||||||
|
" selected_set_key_idx = set_signup_keypad.reshape(-1, keypad_size.numb_of_keys)[selected_key_set]\n",
|
||||||
|
" print(f\"Set Key {idx}: {user_icons[selected_set_key_idx]}\")\n",
|
||||||
|
" selected_key_confirm = selected_keys_confirm[idx]\n",
|
||||||
|
" selected_confirm_key_idx = confirm_keypad.reshape(-1, keypad_size.numb_of_keys)[selected_key_confirm]\n",
|
||||||
|
" print(f\"Confirm Key {idx}: {user_icons[selected_confirm_key_idx]}\")\n",
|
||||||
|
" print(f\"Overlapping icon {user_icons[passcode_property_indices[idx]]}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## User Cipher\n",
|
"## User Cipher\n",
|
||||||
"\n",
|
"\n",
|
||||||
@@ -412,30 +423,30 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 9,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.879460Z",
|
"end_time": "2025-03-27T19:17:57.879460Z",
|
||||||
"start_time": "2025-03-27T19:17:57.873816Z"
|
"start_time": "2025-03-27T19:17:57.873816Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"from src.user_cipher import UserCipher\n",
|
"from src.user_cipher import UserCipher\n",
|
||||||
"user_cipher = UserCipher.create(keypad_size, customer.cipher.position_key, customer.nkode_policy.max_nkode_len)\n",
|
"user_cipher = UserCipher.create(keypad_size, customer.cipher.position_key, customer.nkode_policy.max_nkode_len)\n",
|
||||||
"user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)"
|
"user_prop_key_keypad = user_cipher.property_key.reshape(-1, keypad_size.props_per_key)"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 9
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 10,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.904749Z",
|
"end_time": "2025-03-27T19:17:57.904749Z",
|
||||||
"start_time": "2025-03-27T19:17:57.902224Z"
|
"start_time": "2025-03-27T19:17:57.902224Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": "print(f\"Property Key:\\n{user_prop_key_keypad}\")",
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -450,17 +461,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 10
|
"source": [
|
||||||
|
"print(f\"Property Key:\\n{user_prop_key_keypad}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 11,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.951642Z",
|
"end_time": "2025-03-27T19:17:57.951642Z",
|
||||||
"start_time": "2025-03-27T19:17:57.949347Z"
|
"start_time": "2025-03-27T19:17:57.949347Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": "print(f\"Passcode Key: {user_cipher.pass_key}\")",
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -470,17 +483,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 11
|
"source": [
|
||||||
|
"print(f\"Passcode Key: {user_cipher.pass_key}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 12,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:57.989194Z",
|
"end_time": "2025-03-27T19:17:57.989194Z",
|
||||||
"start_time": "2025-03-27T19:17:57.986687Z"
|
"start_time": "2025-03-27T19:17:57.986687Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": "print(f\"Mask Key: {user_cipher.mask_key}\")",
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -490,17 +505,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 12
|
"source": [
|
||||||
|
"print(f\"Mask Key: {user_cipher.mask_key}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 13,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.019886Z",
|
"end_time": "2025-03-27T19:17:58.019886Z",
|
||||||
"start_time": "2025-03-27T19:17:58.017350Z"
|
"start_time": "2025-03-27T19:17:58.017350Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": "print(f\"Combined Position Key: {user_cipher.combined_position_key}\")",
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -510,17 +527,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 13
|
"source": [
|
||||||
|
"print(f\"Combined Position Key: {user_cipher.combined_position_key}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.050384Z",
|
"end_time": "2025-03-27T19:17:58.050384Z",
|
||||||
"start_time": "2025-03-27T19:17:58.047868Z"
|
"start_time": "2025-03-27T19:17:58.047868Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": "print(f\"User Position Key = combined_pos_key XOR customer_pos_key: {user_cipher.combined_position_key ^ customer.cipher.position_key}\")",
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -530,22 +549,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 14
|
"source": [
|
||||||
|
"print(f\"User Position Key = combined_pos_key XOR customer_pos_key: {user_cipher.combined_position_key ^ customer.cipher.position_key}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 15,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.138360Z",
|
"end_time": "2025-03-27T19:17:58.138360Z",
|
||||||
"start_time": "2025-03-27T19:17:58.135782Z"
|
"start_time": "2025-03-27T19:17:58.135782Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"position_properties_dict = dict(zip(user_cipher.combined_position_key, user_prop_key_keypad.T))\n",
|
|
||||||
"print(f\"Combined Position to Properties Map:\")\n",
|
|
||||||
"for pos_val, props in position_properties_dict.items():\n",
|
|
||||||
" print(f\"{pos_val}: {props}\")"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -561,11 +577,16 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 15
|
"source": [
|
||||||
|
"position_properties_dict = dict(zip(user_cipher.combined_position_key, user_prop_key_keypad.T))\n",
|
||||||
|
"print(f\"Combined Position to Properties Map:\")\n",
|
||||||
|
"for pos_val, props in position_properties_dict.items():\n",
|
||||||
|
" print(f\"{pos_val}: {props}\")"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"#### Encipher Mask\n",
|
"#### Encipher Mask\n",
|
||||||
"1. Get the `padded_passcode_position_indices`; padded with random position indices to equal length `max_nkode_len`.\n",
|
"1. Get the `padded_passcode_position_indices`; padded with random position indices to equal length `max_nkode_len`.\n",
|
||||||
@@ -576,26 +597,26 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 16,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.179247Z",
|
"end_time": "2025-03-27T19:17:58.179247Z",
|
||||||
"start_time": "2025-03-27T19:17:58.176595Z"
|
"start_time": "2025-03-27T19:17:58.176595Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"padded_passcode_position_indices = customer.cipher.get_passcode_position_indices_padded(list(passcode_property_indices), customer.nkode_policy.max_nkode_len)\n",
|
"padded_passcode_position_indices = customer.cipher.get_passcode_position_indices_padded(list(passcode_property_indices), customer.nkode_policy.max_nkode_len)\n",
|
||||||
"user_position_key = user_cipher.combined_position_key ^ customer.cipher.position_key\n",
|
"user_position_key = user_cipher.combined_position_key ^ customer.cipher.position_key\n",
|
||||||
"ordered_user_position_key = user_position_key[padded_passcode_position_indices]\n",
|
"ordered_user_position_key = user_position_key[padded_passcode_position_indices]\n",
|
||||||
"mask = ordered_user_position_key ^ user_cipher.mask_key\n",
|
"mask = ordered_user_position_key ^ user_cipher.mask_key\n",
|
||||||
"encoded_mask = user_cipher.encode_base64_str(mask)"
|
"encoded_mask = user_cipher.encode_base64_str(mask)"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 16
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"#### Encipher Passcode\n",
|
"#### Encipher Passcode\n",
|
||||||
"1. Compute `combined_property_key`\n",
|
"1. Compute `combined_property_key`\n",
|
||||||
@@ -606,13 +627,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 17,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.455536Z",
|
"end_time": "2025-03-27T19:17:58.455536Z",
|
||||||
"start_time": "2025-03-27T19:17:58.212205Z"
|
"start_time": "2025-03-27T19:17:58.212205Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"combined_prop_key = customer.cipher.property_key ^ user_cipher.property_key\n",
|
"combined_prop_key = customer.cipher.property_key ^ user_cipher.property_key\n",
|
||||||
"user_passcode = combined_prop_key[passcode_property_indices]\n",
|
"user_passcode = combined_prop_key[passcode_property_indices]\n",
|
||||||
@@ -621,13 +644,11 @@
|
|||||||
"ciphered_passcode = padded_passcode ^ user_cipher.pass_key\n",
|
"ciphered_passcode = padded_passcode ^ user_cipher.pass_key\n",
|
||||||
"passcode_prehash = base64.b64encode(hashlib.sha256(ciphered_passcode.tobytes()).digest())\n",
|
"passcode_prehash = base64.b64encode(hashlib.sha256(ciphered_passcode.tobytes()).digest())\n",
|
||||||
"passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode(\"utf-8\")"
|
"passcode_hash = bcrypt.hashpw(passcode_prehash, bcrypt.gensalt(rounds=12)).decode(\"utf-8\")"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 17
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### User Login\n",
|
"### User Login\n",
|
||||||
"1. Get login keypad\n",
|
"1. Get login keypad\n",
|
||||||
@@ -635,22 +656,14 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 18,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.702891Z",
|
"end_time": "2025-03-27T19:17:58.702891Z",
|
||||||
"start_time": "2025-03-27T19:17:58.461555Z"
|
"start_time": "2025-03-27T19:17:58.461555Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
|
||||||
"source": [
|
|
||||||
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
|
||||||
"keypad_view(login_keypad, keypad_size.props_per_key)\n",
|
|
||||||
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
|
||||||
"print(f\"User Passcode: {passcode_property_indices}\\n\")\n",
|
|
||||||
"print(f\"Selected Keys:\\n {selected_keys_login}\\n\")\n",
|
|
||||||
"success = api.login(customer_id, username, selected_keys_login)\n",
|
|
||||||
"assert success"
|
|
||||||
],
|
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"name": "stdout",
|
"name": "stdout",
|
||||||
@@ -669,11 +682,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"execution_count": 18
|
"source": [
|
||||||
|
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
||||||
|
"keypad_view(login_keypad, keypad_size.props_per_key)\n",
|
||||||
|
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
||||||
|
"print(f\"User Passcode: {passcode_property_indices}\\n\")\n",
|
||||||
|
"print(f\"Selected Keys:\\n {selected_keys_login}\\n\")\n",
|
||||||
|
"success = api.login(customer_id, username, selected_keys_login)\n",
|
||||||
|
"assert success"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Validate Login Key Entry\n",
|
"## Validate Login Key Entry\n",
|
||||||
"- decipher user mask and recover nkode position values\n",
|
"- decipher user mask and recover nkode position values\n",
|
||||||
@@ -682,8 +703,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Decipher Mask\n",
|
"### Decipher Mask\n",
|
||||||
"Recover nKode position values:\n",
|
"Recover nKode position values:\n",
|
||||||
@@ -694,13 +715,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 19,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.713231Z",
|
"end_time": "2025-03-27T19:17:58.713231Z",
|
||||||
"start_time": "2025-03-27T19:17:58.710458Z"
|
"start_time": "2025-03-27T19:17:58.710458Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
||||||
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
||||||
@@ -708,13 +731,11 @@
|
|||||||
"mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n",
|
"mask = user.cipher.decode_base64_str(user.enciphered_passcode.mask)\n",
|
||||||
"ordered_user_position_key = mask ^ user.cipher.mask_key\n",
|
"ordered_user_position_key = mask ^ user.cipher.mask_key\n",
|
||||||
"user_position_key = customer.cipher.position_key ^ user.cipher.combined_position_key"
|
"user_position_key = customer.cipher.position_key ^ user.cipher.combined_position_key"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 19
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"#### Get Presumed Properties\n",
|
"#### Get Presumed Properties\n",
|
||||||
"- Get the passcode position indices (within the keys)\n",
|
"- Get the passcode position indices (within the keys)\n",
|
||||||
@@ -722,44 +743,46 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 20,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.729425Z",
|
"end_time": "2025-03-27T19:17:58.729425Z",
|
||||||
"start_time": "2025-03-27T19:17:58.727025Z"
|
"start_time": "2025-03-27T19:17:58.727025Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"passcode_position_indices = [int(np.where(user_position_key == pos)[0][0]) for pos in ordered_user_position_key[:passcode_len]]\n",
|
"passcode_position_indices = [int(np.where(user_position_key == pos)[0][0]) for pos in ordered_user_position_key[:passcode_len]]\n",
|
||||||
"presumed_property_indices = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, passcode_position_indices)\n",
|
"presumed_property_indices = customer.users[username].user_keypad.get_prop_idxs_by_keynumb_setidx(selected_keys_login, passcode_position_indices)\n",
|
||||||
"assert passcode_property_indices == presumed_property_indices\n"
|
"assert passcode_property_indices == presumed_property_indices\n"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 20
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"source": "### Compare Enciphered Passcodes\n"
|
"metadata": {},
|
||||||
|
"source": [
|
||||||
|
"### Compare Enciphered Passcodes\n"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 21,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:58.983936Z",
|
"end_time": "2025-03-27T19:17:58.983936Z",
|
||||||
"start_time": "2025-03-27T19:17:58.739679Z"
|
"start_time": "2025-03-27T19:17:58.739679Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"valid_nkode = user.cipher.compare_nkode(presumed_property_indices, customer.cipher, user.enciphered_passcode.code)\n",
|
"valid_nkode = user.cipher.compare_nkode(presumed_property_indices, customer.cipher, user.enciphered_passcode.code)\n",
|
||||||
"assert valid_nkode"
|
"assert valid_nkode"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 21
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Renew Properties\n",
|
"## Renew Properties\n",
|
||||||
"1. Renew Customer Keys\n",
|
"1. Renew Customer Keys\n",
|
||||||
@@ -769,13 +792,28 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 22,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:59.477909Z",
|
"end_time": "2025-03-27T19:17:59.477909Z",
|
||||||
"start_time": "2025-03-27T19:17:58.990632Z"
|
"start_time": "2025-03-27T19:17:58.990632Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Old User Cipher and Mask\n",
|
||||||
|
"mask: DUY6OIJHwzgG5ajVEGKHARHM6So=, code: $2b$12$/Za40kT7mC0quZxMUWCDs.4cF.3r2meCUBEoz0EWlSKkJAMOPiJTy\n",
|
||||||
|
"\n",
|
||||||
|
"New User Cipher and Mask\n",
|
||||||
|
"mask: GGBaMjr+zlPhS+pmfstihUeFt6A=, code: $2b$12$wE7bq7sYd8Q58j.qKS5ASO2IMzaJ71UW/0vOYAhx7zUhURDJYIaZi\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"def print_user_enciphered_code():\n",
|
"def print_user_enciphered_code():\n",
|
||||||
" mask = api.customers[customer_id].users[username].enciphered_passcode.mask\n",
|
" mask = api.customers[customer_id].users[username].enciphered_passcode.mask\n",
|
||||||
@@ -791,39 +829,26 @@
|
|||||||
"print(\"New User Cipher and Mask\")\n",
|
"print(\"New User Cipher and Mask\")\n",
|
||||||
"print_user_enciphered_code()\n",
|
"print_user_enciphered_code()\n",
|
||||||
"assert success"
|
"assert success"
|
||||||
],
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"Old User Cipher and Mask\n",
|
|
||||||
"mask: DUY6OIJHwzgG5ajVEGKHARHM6So=, code: $2b$12$/Za40kT7mC0quZxMUWCDs.4cF.3r2meCUBEoz0EWlSKkJAMOPiJTy\n",
|
|
||||||
"\n",
|
|
||||||
"New User Cipher and Mask\n",
|
|
||||||
"mask: GGBaMjr+zlPhS+pmfstihUeFt6A=, code: $2b$12$wE7bq7sYd8Q58j.qKS5ASO2IMzaJ71UW/0vOYAhx7zUhURDJYIaZi\n",
|
|
||||||
"\n"
|
|
||||||
]
|
]
|
||||||
}
|
|
||||||
],
|
|
||||||
"execution_count": 22
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Renew Customer Keys\n",
|
"### Renew Customer Keys\n",
|
||||||
"The customer cipher keys are replaced."
|
"The customer cipher keys are replaced."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 23,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:59.490134Z",
|
"end_time": "2025-03-27T19:17:59.490134Z",
|
||||||
"start_time": "2025-03-27T19:17:59.486082Z"
|
"start_time": "2025-03-27T19:17:59.486082Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"old_props = customer.cipher.property_key.copy()\n",
|
"old_props = customer.cipher.property_key.copy()\n",
|
||||||
"old_pos = customer.cipher.position_key.copy()\n",
|
"old_pos = customer.cipher.position_key.copy()\n",
|
||||||
@@ -831,13 +856,11 @@
|
|||||||
"customer.cipher.position_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False)\n",
|
"customer.cipher.position_key = np.random.choice(2 ** 16, size=keypad_size.props_per_key, replace=False)\n",
|
||||||
"new_props = customer.cipher.property_key\n",
|
"new_props = customer.cipher.property_key\n",
|
||||||
"new_pos = customer.cipher.position_key"
|
"new_pos = customer.cipher.position_key"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 23
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Intermediate Renew User\n",
|
"### Intermediate Renew User\n",
|
||||||
"User property and position keys go through an intermediate phase.\n",
|
"User property and position keys go through an intermediate phase.\n",
|
||||||
@@ -851,13 +874,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 24,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:59.500256Z",
|
"end_time": "2025-03-27T19:17:59.500256Z",
|
||||||
"start_time": "2025-03-27T19:17:59.497839Z"
|
"start_time": "2025-03-27T19:17:59.497839Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"props_xor = new_props ^ old_props\n",
|
"props_xor = new_props ^ old_props\n",
|
||||||
"pos_xor = new_pos ^ old_pos\n",
|
"pos_xor = new_pos ^ old_pos\n",
|
||||||
@@ -865,26 +890,26 @@
|
|||||||
" user.renew = True\n",
|
" user.renew = True\n",
|
||||||
" user.cipher.combined_position_key = user.cipher.combined_position_key ^ pos_xor\n",
|
" user.cipher.combined_position_key = user.cipher.combined_position_key ^ pos_xor\n",
|
||||||
" user.cipher.property_key = user.cipher.property_key ^ props_xor"
|
" user.cipher.property_key = user.cipher.property_key ^ props_xor"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 24
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### New User Keys\n",
|
"### New User Keys\n",
|
||||||
"After a user's first successful login, the renew flag is checked. If it's true, the user's cipher is replaced with a new cipher."
|
"After a user's first successful login, the renew flag is checked. If it's true, the user's cipher is replaced with a new cipher."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 25,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-27T19:17:59.752960Z",
|
"end_time": "2025-03-27T19:17:59.752960Z",
|
||||||
"start_time": "2025-03-27T19:17:59.508826Z"
|
"start_time": "2025-03-27T19:17:59.508826Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"if user.renew:\n",
|
"if user.renew:\n",
|
||||||
" user.cipher = UserCipher.create(\n",
|
" user.cipher = UserCipher.create(\n",
|
||||||
@@ -894,30 +919,28 @@
|
|||||||
" )\n",
|
" )\n",
|
||||||
" user.enciphered_passcode = user.cipher.encipher_nkode(presumed_property_indices, customer.cipher)\n",
|
" user.enciphered_passcode = user.cipher.encipher_nkode(presumed_property_indices, customer.cipher)\n",
|
||||||
" user.renew = False"
|
" user.renew = False"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 25
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3",
|
"display_name": "Python 3 (ipykernel)",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "python3"
|
||||||
},
|
},
|
||||||
"language_info": {
|
"language_info": {
|
||||||
"codemirror_mode": {
|
"codemirror_mode": {
|
||||||
"name": "ipython",
|
"name": "ipython",
|
||||||
"version": 2
|
"version": 3
|
||||||
},
|
},
|
||||||
"file_extension": ".py",
|
"file_extension": ".py",
|
||||||
"mimetype": "text/x-python",
|
"mimetype": "text/x-python",
|
||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython2",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "2.7.6"
|
"version": "3.10.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
"nbformat_minor": 0
|
"nbformat_minor": 4
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,18 @@
|
|||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": 30,
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-03-28T15:06:18.878127Z",
|
||||||
|
"start_time": "2025-03-28T15:06:18.874618Z"
|
||||||
|
},
|
||||||
|
"collapsed": false,
|
||||||
|
"jupyter": {
|
||||||
|
"outputs_hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"import sys\n",
|
"import sys\n",
|
||||||
"import os\n",
|
"import os\n",
|
||||||
@@ -15,20 +27,11 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"def random_username() -> str:\n",
|
"def random_username() -> str:\n",
|
||||||
" return \"test_username\" + \"\".join([choice(ascii_lowercase) for _ in range(6)])\n"
|
" return \"test_username\" + \"\".join([choice(ascii_lowercase) for _ in range(6)])\n"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false,
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-03-28T15:06:18.878127Z",
|
|
||||||
"start_time": "2025-03-28T15:06:18.874618Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 30
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Initialize nKode API and Create Customer\n",
|
"## Initialize nKode API and Create Customer\n",
|
||||||
"#### nKode Customer\n",
|
"#### nKode Customer\n",
|
||||||
@@ -39,6 +42,18 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
|
"execution_count": 31,
|
||||||
|
"metadata": {
|
||||||
|
"ExecuteTime": {
|
||||||
|
"end_time": "2025-03-28T15:06:18.896461Z",
|
||||||
|
"start_time": "2025-03-28T15:06:18.891125Z"
|
||||||
|
},
|
||||||
|
"collapsed": false,
|
||||||
|
"jupyter": {
|
||||||
|
"outputs_hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"api = NKodeAPI()\n",
|
"api = NKodeAPI()\n",
|
||||||
"policy = NKodePolicy(\n",
|
"policy = NKodePolicy(\n",
|
||||||
@@ -53,20 +68,11 @@
|
|||||||
")\n",
|
")\n",
|
||||||
"customer_id = api.create_new_customer(keypad_size, policy)\n",
|
"customer_id = api.create_new_customer(keypad_size, policy)\n",
|
||||||
"customer = api.get_customer(customer_id)"
|
"customer = api.get_customer(customer_id)"
|
||||||
],
|
]
|
||||||
"metadata": {
|
|
||||||
"collapsed": false,
|
|
||||||
"ExecuteTime": {
|
|
||||||
"end_time": "2025-03-28T15:06:18.896461Z",
|
|
||||||
"start_time": "2025-03-28T15:06:18.891125Z"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 31
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## nKode Enrollment\n",
|
"## nKode Enrollment\n",
|
||||||
"Users enroll in three steps:\n",
|
"Users enroll in three steps:\n",
|
||||||
@@ -79,72 +85,72 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 32,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-28T15:06:18.914254Z",
|
"end_time": "2025-03-28T15:06:18.914254Z",
|
||||||
"start_time": "2025-03-28T15:06:18.911798Z"
|
"start_time": "2025-03-28T15:06:18.911798Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"username = random_username()\n",
|
"username = random_username()\n",
|
||||||
"signup_session_id, set_keypad = api.generate_signup_keypad(customer_id, username)"
|
"signup_session_id, set_keypad = api.generate_signup_keypad(customer_id, username)"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 32
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Set nKode\n",
|
"### Set nKode\n",
|
||||||
"The client receives `user_icons`, `set_signup_keypad`\n"
|
"The client receives `user_icons`, `set_signup_keypad`\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 33,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-28T15:06:18.931791Z",
|
"end_time": "2025-03-28T15:06:18.931791Z",
|
||||||
"start_time": "2025-03-28T15:06:18.929028Z"
|
"start_time": "2025-03-28T15:06:18.929028Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"passcode_len = 4\n",
|
"passcode_len = 4\n",
|
||||||
"passcode_property_indices = np.random.choice(set_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n",
|
"passcode_property_indices = np.random.choice(set_keypad.reshape(-1), size=passcode_len, replace=False).tolist()\n",
|
||||||
"selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_keypad, keypad_size.numb_of_keys)"
|
"selected_keys_set = select_keys_with_passcode_values(passcode_property_indices, set_keypad, keypad_size.numb_of_keys)"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 33
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### Confirm nKode\n",
|
"### Confirm nKode\n",
|
||||||
"The user enter then submits their key entry. The server returns the confirm_keypad, another index array and dispersion of the set_keypad."
|
"The user enter then submits their key entry. The server returns the confirm_keypad, another index array and dispersion of the set_keypad."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 34,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-28T15:06:19.247638Z",
|
"end_time": "2025-03-28T15:06:19.247638Z",
|
||||||
"start_time": "2025-03-28T15:06:18.938601Z"
|
"start_time": "2025-03-28T15:06:18.938601Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"confirm_keypad = api.set_nkode(customer_id, selected_keys_set, signup_session_id)\n",
|
"confirm_keypad = api.set_nkode(customer_id, selected_keys_set, signup_session_id)\n",
|
||||||
"selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys)\n",
|
"selected_keys_confirm = select_keys_with_passcode_values(passcode_property_indices, confirm_keypad, keypad_size.numb_of_keys)\n",
|
||||||
"success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id)\n",
|
"success = api.confirm_nkode(customer_id, selected_keys_confirm, signup_session_id)\n",
|
||||||
"assert success"
|
"assert success"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 34
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"### User Login\n",
|
"### User Login\n",
|
||||||
"1. Get login keypad\n",
|
"1. Get login keypad\n",
|
||||||
@@ -152,25 +158,25 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 35,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-28T15:06:19.559753Z",
|
"end_time": "2025-03-28T15:06:19.559753Z",
|
||||||
"start_time": "2025-03-28T15:06:19.254675Z"
|
"start_time": "2025-03-28T15:06:19.254675Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
||||||
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
||||||
"success = api.login(customer_id, username, selected_keys_login)\n",
|
"success = api.login(customer_id, username, selected_keys_login)\n",
|
||||||
"assert success"
|
"assert success"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 35
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"metadata": {},
|
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"## Renew Properties\n",
|
"## Renew Properties\n",
|
||||||
"Replace server-side ciphers keys and nkode hash with new values.\n",
|
"Replace server-side ciphers keys and nkode hash with new values.\n",
|
||||||
@@ -180,60 +186,60 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 36,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-28T15:06:20.181548Z",
|
"end_time": "2025-03-28T15:06:20.181548Z",
|
||||||
"start_time": "2025-03-28T15:06:19.568067Z"
|
"start_time": "2025-03-28T15:06:19.568067Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"api.renew_keys(customer_id) # Steps 1 and 2\n",
|
"api.renew_keys(customer_id) # Steps 1 and 2\n",
|
||||||
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
||||||
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
||||||
"success = api.login(customer_id, username, selected_keys_login) # Step 3\n",
|
"success = api.login(customer_id, username, selected_keys_login) # Step 3\n",
|
||||||
"assert success"
|
"assert success"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 36
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 37,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"ExecuteTime": {
|
"ExecuteTime": {
|
||||||
"end_time": "2025-03-28T15:06:20.500050Z",
|
"end_time": "2025-03-28T15:06:20.500050Z",
|
||||||
"start_time": "2025-03-28T15:06:20.194912Z"
|
"start_time": "2025-03-28T15:06:20.194912Z"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cell_type": "code",
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
"login_keypad = api.get_login_keypad(username, customer_id)\n",
|
||||||
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
"selected_keys_login = select_keys_with_passcode_values(passcode_property_indices, login_keypad, keypad_size.props_per_key)\n",
|
||||||
"success = api.login(customer_id, username, selected_keys_login)\n",
|
"success = api.login(customer_id, username, selected_keys_login)\n",
|
||||||
"assert success"
|
"assert success"
|
||||||
],
|
]
|
||||||
"outputs": [],
|
|
||||||
"execution_count": 37
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3",
|
"display_name": "Python 3 (ipykernel)",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "python3"
|
||||||
},
|
},
|
||||||
"language_info": {
|
"language_info": {
|
||||||
"codemirror_mode": {
|
"codemirror_mode": {
|
||||||
"name": "ipython",
|
"name": "ipython",
|
||||||
"version": 2
|
"version": 3
|
||||||
},
|
},
|
||||||
"file_extension": ".py",
|
"file_extension": ".py",
|
||||||
"mimetype": "text/x-python",
|
"mimetype": "text/x-python",
|
||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython2",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "2.7.6"
|
"version": "3.10.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
"nbformat_minor": 0
|
"nbformat_minor": 4
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/nkode_cipher_v2/__init__.py
Normal file
0
src/nkode_cipher_v2/__init__.py
Normal file
128
src/nkode_cipher_v2/nkode_cipher.py
Normal file
128
src/nkode_cipher_v2/nkode_cipher.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import bcrypt
|
||||||
|
import numpy as np
|
||||||
|
from src.models import EncipheredNKode, KeypadSize
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class NKodeCipher:
|
||||||
|
property_key: np.ndarray
|
||||||
|
position_key: np.ndarray
|
||||||
|
pass_key: np.ndarray
|
||||||
|
mask_key: np.ndarray
|
||||||
|
keypad_size: KeypadSize
|
||||||
|
max_nkode_len: int
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, keypad_size: KeypadSize, max_nkode_len: int) -> 'NKodeCipher':
|
||||||
|
return NKodeCipher(
|
||||||
|
property_key=np.random.choice(2 ** 16, size=keypad_size.total_props, replace=False),
|
||||||
|
pass_key=np.random.choice(2 ** 16, size=max_nkode_len, replace=False),
|
||||||
|
mask_key=np.random.choice(2**16, size=max_nkode_len, replace=False),
|
||||||
|
position_key= np.random.choice(2**16,size=keypad_size.props_per_key, replace=False),
|
||||||
|
max_nkode_len=max_nkode_len,
|
||||||
|
keypad_size=keypad_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_props_position_vals(self, props: np.ndarray | list[int]) -> np.ndarray:
|
||||||
|
if not all([prop in self.property_key for prop in props]):
|
||||||
|
raise ValueError("Property values must be within valid range")
|
||||||
|
pos_vals = [self._get_prop_position_val(prop) for prop in props]
|
||||||
|
return np.array(pos_vals)
|
||||||
|
|
||||||
|
def _get_prop_position_val(self, prop: int) -> int:
|
||||||
|
assert prop in self.property_key
|
||||||
|
prop_idx = np.where(self.property_key == prop)[0][0]
|
||||||
|
pos_idx = prop_idx % self.keypad_size.props_per_key
|
||||||
|
return int(self.position_key[pos_idx])
|
||||||
|
|
||||||
|
def pad_user_mask(self, user_mask: np.ndarray, pos_vals: np.ndarray) -> np.ndarray:
|
||||||
|
# TODO: replace with new method
|
||||||
|
if len(user_mask) >= self.max_nkode_len:
|
||||||
|
raise ValueError("User encoded_mask is too long")
|
||||||
|
padding_size = self.max_nkode_len - len(user_mask)
|
||||||
|
padding = np.random.choice(pos_vals, size=padding_size, replace=True).astype(np.uint16)
|
||||||
|
return np.concatenate([user_mask, padding])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def encode_base64_str(data: np.ndarray) -> str:
|
||||||
|
return base64.b64encode( b"".join([int(num).to_bytes(2, byteorder='big') for num in data])).decode("utf-8")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def decode_base64_str(data: str) -> np.ndarray:
|
||||||
|
byte_data = base64.b64decode(data)
|
||||||
|
int_list = []
|
||||||
|
for i in range(0, len(byte_data), 2):
|
||||||
|
int_val = int.from_bytes(byte_data[i:i + 2], byteorder='big')
|
||||||
|
int_list.append(int_val)
|
||||||
|
return np.array(int_list, dtype=np.uint16)
|
||||||
|
|
||||||
|
def encipher_nkode(
|
||||||
|
self,
|
||||||
|
passcode_prop_idx: list[int],
|
||||||
|
) -> EncipheredNKode:
|
||||||
|
mask = self.encipher_mask(passcode_prop_idx)
|
||||||
|
code = self.hash_nkode(passcode_prop_idx)
|
||||||
|
return EncipheredNKode(
|
||||||
|
code=code,
|
||||||
|
mask=mask
|
||||||
|
)
|
||||||
|
|
||||||
|
def hash_nkode(
|
||||||
|
self,
|
||||||
|
passcode_prop_idx: list[int],
|
||||||
|
) -> str:
|
||||||
|
salt = bcrypt.gensalt(rounds=12)
|
||||||
|
passcode_bytes = self.prehash_passcode(passcode_prop_idx)
|
||||||
|
passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest())
|
||||||
|
hashed_data = bcrypt.hashpw(passcode_digest, salt)
|
||||||
|
return hashed_data.decode("utf-8")
|
||||||
|
|
||||||
|
def compare_nkode(
|
||||||
|
self,
|
||||||
|
passcode_prop_idx: list[int],
|
||||||
|
hashed_passcode: str
|
||||||
|
) -> bool:
|
||||||
|
passcode_bytes = self.prehash_passcode(passcode_prop_idx)
|
||||||
|
passcode_digest = base64.b64encode(hashlib.sha256(passcode_bytes).digest())
|
||||||
|
return bcrypt.checkpw(passcode_digest, hashed_passcode.encode('utf-8'))
|
||||||
|
|
||||||
|
def prehash_passcode(
|
||||||
|
self,
|
||||||
|
passcode_prop_idx: list[int],
|
||||||
|
) -> bytes:
|
||||||
|
passcode_len = len(passcode_prop_idx)
|
||||||
|
passcode_cipher = self.pass_key.copy()
|
||||||
|
passcode_cipher[:passcode_len] = (
|
||||||
|
passcode_cipher[:passcode_len] ^
|
||||||
|
self.property_key[passcode_prop_idx]
|
||||||
|
)
|
||||||
|
return passcode_cipher.astype(np.uint16).tobytes()
|
||||||
|
|
||||||
|
def get_passcode_position_indices_padded(self, passcode_indices: list[int]) -> list[int]:
|
||||||
|
pos_indices = [idx % self.keypad_size.props_per_key for idx in passcode_indices]
|
||||||
|
pad_len = self.max_nkode_len - len(passcode_indices)
|
||||||
|
pad = np.random.choice(self.keypad_size.props_per_key, pad_len, replace=True)
|
||||||
|
return pos_indices + pad.tolist()
|
||||||
|
|
||||||
|
def encipher_mask(
|
||||||
|
self,
|
||||||
|
passcode_prop_idx: list[int],
|
||||||
|
) -> str:
|
||||||
|
pos_idxs = self.get_passcode_position_indices_padded(passcode_prop_idx)
|
||||||
|
ordered_pos_key = self.position_key[pos_idxs]
|
||||||
|
mask = ordered_pos_key ^ self.mask_key
|
||||||
|
encoded_mask = self.encode_base64_str(mask)
|
||||||
|
return encoded_mask
|
||||||
|
|
||||||
|
def decipher_mask(self, encoded_mask: str, passcode_len: int) -> list[int]:
|
||||||
|
mask = self.decode_base64_str(encoded_mask)
|
||||||
|
# user_pos_key ordered by the user's nkode and padded to be length max_nkode_len
|
||||||
|
ordered_user_pos_key = mask ^ self.mask_key
|
||||||
|
passcode_position = []
|
||||||
|
for position_val in ordered_user_pos_key[:passcode_len]:
|
||||||
|
position_idx = np.where(self.position_key == position_val)[0][0]
|
||||||
|
passcode_position.append(int(self.position_key[position_idx]))
|
||||||
|
return passcode_position
|
||||||
|
|
||||||
37
test/test_nkode_cipher_keys.py
Normal file
37
test/test_nkode_cipher_keys.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
from src.models import KeypadSize
|
||||||
|
from src.nkode_cipher_v2.nkode_cipher import NKodeCipher
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"passcode_len",
|
||||||
|
[
|
||||||
|
6
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_encode_decode_base64(passcode_len):
|
||||||
|
data = np.random.choice(2**16, passcode_len, replace=False)
|
||||||
|
encoded = NKodeCipher.encode_base64_str(data)
|
||||||
|
decoded = NKodeCipher.decode_base64_str(encoded)
|
||||||
|
assert (len(data) == len(decoded))
|
||||||
|
assert (all(data[idx] == decoded[idx] for idx in range(passcode_len)))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"keypad_size,max_nkode_len",
|
||||||
|
[
|
||||||
|
(KeypadSize(numb_of_keys=10, props_per_key=11), 10),
|
||||||
|
(KeypadSize(numb_of_keys=9, props_per_key=11), 10),
|
||||||
|
(KeypadSize(numb_of_keys=8, props_per_key=11), 12),
|
||||||
|
])
|
||||||
|
def test_decode_mask(keypad_size, max_nkode_len):
|
||||||
|
passcode_entry = np.random.choice(keypad_size.total_props, 4, replace=False)
|
||||||
|
user_keys = NKodeCipher.create(keypad_size, max_nkode_len)
|
||||||
|
passcode_values = [user_keys.property_key[idx] for idx in passcode_entry]
|
||||||
|
enciphered = user_keys.encipher_nkode(passcode_entry)
|
||||||
|
orig_passcode_pos_vals = user_keys.get_props_position_vals(passcode_values)
|
||||||
|
passcode_pos_vals = user_keys.decipher_mask(enciphered.mask, len(passcode_entry))
|
||||||
|
assert (len(passcode_pos_vals) == len(orig_passcode_pos_vals))
|
||||||
|
assert (all(orig_passcode_pos_vals[idx] == passcode_pos_vals[idx] for idx in range(len(passcode_pos_vals))))
|
||||||
|
assert(user_keys.compare_nkode(passcode_entry, enciphered.code))
|
||||||
Reference in New Issue
Block a user