update README; and environment.yaml

This commit is contained in:
2025-03-30 05:01:21 -05:00
parent 881949e653
commit 2ccf647137
4 changed files with 255 additions and 48 deletions

View File

@@ -1,3 +1,36 @@
# Evil nKode # Evil nKode
Simulated nKode Cracker Simulated nKode Cracker
## Installation
- Python version 3.10 or greater is required
- Install conda (or your preferred tool) for environment management
### Using conda
```bash
conda env create -f environment.yml
conda activate pynkode
```
## Starting a Jupyter Notebook
### Option 1: Using classic Jupyter Notebook
```bash
# Ensure your environment is activated
# For conda: conda activate pynkode
# For pyenv: (should be automatic if in the directory)
# Start the Jupyter Notebook server
jupyter notebook
```
### Option 2: Using JupyterLab
```bash
# Ensure your environment is activated
# Start JupyterLab
jupyter lab
```
## Notebooks
- [evilnkode](notebooks/evilkode.ipynb)

125
environment.yml Normal file
View File

@@ -0,0 +1,125 @@
name: evilnkode
channels:
- defaults
dependencies:
- anyio=4.6.2=py312hca03da5_0
- appnope=0.1.3=py312hca03da5_1001
- argon2-cffi=21.3.0=pyhd3eb1b0_0
- argon2-cffi-bindings=21.2.0=py312h80987f9_0
- asttokens=2.0.5=pyhd3eb1b0_0
- async-lru=2.0.4=py312hca03da5_0
- attrs=24.2.0=py312hca03da5_0
- babel=2.11.0=py312hca03da5_0
- beautifulsoup4=4.12.3=py312hca03da5_0
- bleach=6.2.0=py312hca03da5_0
- brotli-python=1.0.9=py312h313beb8_8
- bzip2=1.0.8=h80987f9_6
- ca-certificates=2024.12.31=hca03da5_0
- certifi=2024.12.14=py312hca03da5_0
- cffi=1.17.1=py312h3eb5a62_0
- charset-normalizer=3.3.2=pyhd3eb1b0_0
- comm=0.2.1=py312hca03da5_0
- debugpy=1.6.7=py312h313beb8_0
- decorator=5.1.1=pyhd3eb1b0_0
- defusedxml=0.7.1=pyhd3eb1b0_0
- executing=0.8.3=pyhd3eb1b0_0
- expat=2.6.3=h313beb8_0
- h11=0.14.0=py312hca03da5_0
- httpcore=1.0.2=py312hca03da5_0
- httpx=0.27.0=py312hca03da5_0
- idna=3.7=py312hca03da5_0
- ipykernel=6.29.5=py312hca03da5_0
- ipython=8.27.0=py312hca03da5_0
- jedi=0.19.1=py312hca03da5_0
- jinja2=3.1.4=py312hca03da5_1
- json5=0.9.25=py312hca03da5_0
- jsonschema=4.23.0=py312hca03da5_0
- jsonschema-specifications=2023.7.1=py312hca03da5_0
- jupyter-lsp=2.2.0=py312hca03da5_0
- jupyter_client=8.6.0=py312hca03da5_0
- jupyter_core=5.7.2=py312hca03da5_0
- jupyter_events=0.10.0=py312hca03da5_0
- jupyter_server=2.14.1=py312hca03da5_0
- jupyter_server_terminals=0.4.4=py312hca03da5_1
- jupyterlab=4.2.5=py312hca03da5_0
- jupyterlab_pygments=0.1.2=py_0
- jupyterlab_server=2.27.3=py312hca03da5_0
- libcxx=14.0.6=h848a8c0_0
- libffi=3.4.4=hca03da5_1
- libsodium=1.0.18=h1a28f6b_0
- markupsafe=2.1.3=py312h80987f9_0
- matplotlib-inline=0.1.6=py312hca03da5_0
- mistune=2.0.4=py312hca03da5_0
- nbclient=0.8.0=py312hca03da5_0
- nbconvert=7.16.4=py312hca03da5_0
- nbformat=5.10.4=py312hca03da5_0
- ncurses=6.4=h313beb8_0
- nest-asyncio=1.6.0=py312hca03da5_0
- notebook=7.2.2=py312hca03da5_1
- notebook-shim=0.2.3=py312hca03da5_0
- openssl=3.0.15=h80987f9_0
- overrides=7.4.0=py312hca03da5_0
- packaging=24.1=py312hca03da5_0
- pandocfilters=1.5.0=pyhd3eb1b0_0
- parso=0.8.3=pyhd3eb1b0_0
- pexpect=4.8.0=pyhd3eb1b0_3
- pip=24.2=py312hca03da5_0
- platformdirs=3.10.0=py312hca03da5_0
- prometheus_client=0.21.0=py312hca03da5_0
- prompt-toolkit=3.0.43=py312hca03da5_0
- prompt_toolkit=3.0.43=hd3eb1b0_0
- psutil=5.9.0=py312h80987f9_0
- ptyprocess=0.7.0=pyhd3eb1b0_2
- pure_eval=0.2.2=pyhd3eb1b0_0
- pycparser=2.21=pyhd3eb1b0_0
- pygments=2.15.1=py312hca03da5_1
- pysocks=1.7.1=py312hca03da5_0
- python=3.12.7=h99e199e_0
- python-dateutil=2.9.0post0=py312hca03da5_2
- python-fastjsonschema=2.20.0=py312hca03da5_0
- python-json-logger=2.0.7=py312hca03da5_0
- pytz=2024.1=py312hca03da5_0
- pyyaml=6.0.2=py312h80987f9_0
- pyzmq=25.1.2=py312h313beb8_0
- readline=8.2=h1a28f6b_0
- referencing=0.30.2=py312hca03da5_0
- requests=2.32.3=py312hca03da5_1
- rfc3339-validator=0.1.4=py312hca03da5_0
- rfc3986-validator=0.1.1=py312hca03da5_0
- rpds-py=0.10.6=py312h2aea54e_1
- send2trash=1.8.2=py312hca03da5_0
- setuptools=75.1.0=py312hca03da5_0
- six=1.16.0=pyhd3eb1b0_1
- sniffio=1.3.0=py312hca03da5_0
- soupsieve=2.5=py312hca03da5_0
- sqlite=3.45.3=h80987f9_0
- stack_data=0.2.0=pyhd3eb1b0_0
- terminado=0.17.1=py312hca03da5_0
- tinycss2=1.2.1=py312hca03da5_0
- tk=8.6.14=h6ba3021_0
- tornado=6.4.1=py312h80987f9_0
- traitlets=5.14.3=py312hca03da5_0
- typing-extensions=4.11.0=py312hca03da5_0
- typing_extensions=4.11.0=py312hca03da5_0
- tzdata=2024b=h04d1e81_0
- urllib3=2.2.3=py312hca03da5_0
- wcwidth=0.2.5=pyhd3eb1b0_0
- webencodings=0.5.1=py312hca03da5_2
- websocket-client=1.8.0=py312hca03da5_0
- wheel=0.44.0=py312hca03da5_0
- xz=5.4.6=h80987f9_1
- yaml=0.2.5=h1a28f6b_0
- zeromq=4.3.5=h313beb8_0
- zlib=1.2.13=h18a0788_1
- pip:
- contourpy==1.3.1
- cycler==0.12.1
- fonttools==4.55.3
- iniconfig==2.0.0
- kiwisolver==1.4.7
- matplotlib==3.9.3
- numpy==2.1.3
- pillow==11.0.0
- pluggy==1.5.0
- pyparsing==3.2.0
- pytest==8.3.4

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@ class Keypad:
keypad: np.ndarray keypad: np.ndarray
k: int # number of keys k: int # number of keys
p: int # properties per key p: int # properties per key
keypad_cache: list keypad_cache: list #
max_cache_size: int = 100 max_cache_size: int = 100
@staticmethod @staticmethod
@@ -24,16 +24,25 @@ class Keypad:
return Keypad(keypad=set_view.T, k=k, p=p, keypad_cache=[]) return Keypad(keypad=set_view.T, k=k, p=p, keypad_cache=[])
def split_shuffle(self): def split_shuffle(self):
"""
This is a modified split shuffle.
It doesn't shuffle the keys only the properties in the keys.
Shuffling the keys makes it hard for people to guess an nKode not a machine.
This split shuffle includes a cache to prevent the same configuration from being used.
This cache is not in any other implementation.
Testing suggests it's not necessary.
Getting the same keypad twice over 100 shuffles is very unlikely.
"""
shuffled_sets = self._shuffle() shuffled_sets = self._shuffle()
# Sort the shuffled sets by the first column
sorted_set = shuffled_sets[np.argsort(shuffled_sets[:, 0])] sorted_set = shuffled_sets[np.argsort(shuffled_sets[:, 0])]
while str(sorted_set) in self.keypad_cache: while str(sorted_set) in self.keypad_cache:
# continue shuffling until we get a unique configuration
shuffled_sets = self._shuffle() shuffled_sets = self._shuffle()
sorted_set = shuffled_sets[np.argsort(shuffled_sets[:, 0])] sorted_set = shuffled_sets[np.argsort(shuffled_sets[:, 0])]
self.keypad_cache.append(str(sorted_set)) self.keypad_cache.append(str(sorted_set))
self.keypad_cache = self.keypad_cache[:self.max_cache_size] self.keypad_cache = self.keypad_cache[:self.max_cache_size]
self.keypad = shuffled_sets self.keypad = shuffled_sets