Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions distribution/peat.spec
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ hidden_imports = [


excludes = [
'matplotlib', 'IPython', 'jedi', 'Cryptodome',
'zmq', 'pydoc', 'setuptools', 'pycryptodomex',
'matplotlib', 'IPython', 'jedi',
'zmq', 'pydoc', 'setuptools',
'pygments', 'traitlets', 'tcl', 'Tkinter',
'Cython', 'cython', 'ipython', 'tox', 'wheel', 'virtualenv',
'requests-toolbelt', 'fire', 'coverage', 'mypy', 'pip', 'PyGObject',
Expand Down
4 changes: 2 additions & 2 deletions distribution/sneakypeat.spec
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ hidden_imports = [


excludes = [
'matplotlib', 'IPython', 'jedi', 'Cryptodome',
'zmq', 'pydoc', 'setuptools', 'pycryptodomex',
'matplotlib', 'IPython', 'jedi',
'zmq', 'pydoc', 'setuptools',
'pygments', 'traitlets', 'tcl', 'Tkinter',
'Cython', 'cython', 'ipython', 'tox', 'wheel', 'virtualenv',
'requests-toolbelt', 'fire', 'coverage', 'mypy', 'pip', 'PyGObject',
Expand Down
48 changes: 48 additions & 0 deletions docs/operate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ PEAT's primary interface is a command-line program with sub-commands for each fu
- ``push``: push firmware, logic, or configuration to a device
- ``pillage``: search for :term:`OT` device-specific configuration and project files on a host machine
- ``heat``: extract and parse device artifacts from network traffic captures (PCAPs)
- ``encrypt-results``: encrypt a PEAT results directory into a password-protected zip archive
- ``decrypt-results``: decrypt a PEAT encrypted results archive into a results directory

Basics
======
Expand All @@ -28,6 +30,8 @@ Basics
peat parse -h
peat push -h
peat pillage -h
peat encrypt-results --help
peat decrypt-results -h

# Examples
peat scan --examples
Expand Down Expand Up @@ -171,6 +175,8 @@ The output directory structure generally looks like this:
temp/
...

Encrypted result archives created with ``peat encrypt-results`` are written separately from the run directory structure shown above. By default, the archive is created in the current working directory as ``encrypted_<run-dir>.zip``.


Viewing the results
-------------------
Expand All @@ -193,6 +199,48 @@ Examples and helpful commands for inspecting the file results.
# Filtering memory and event entries from device results for 192.168.3.200 using 'jq'
cat peat_results/example_pull/devices/192.168.3.200/device-data-full.json | jq 'del(.memory,.event)'

Encrypting results
------------------
PEAT can encrypt a PEAT results run directory into a password-protected zip archive using the ``encrypt-results`` command. This is useful when PEAT results need to be stored or shared more securely after a scan, pull, parse, or other run.

The command expects the path to a PEAT results directory, such as ``./examples/encryption/example_peat_results/``. By default, the encrypted archive is written to the current working directory and named ``encrypted_<run-dir>.zip``.

Use ``-w`` (``--write-file``) to choose where the encrypted archive is written. Use ``-p`` (``--password``) to provide the archive password on the command line. If ``-p`` is not provided, PEAT will prompt interactively for a password.

.. warning::
PEAT does not save the password for the encrypted archive. If the password is lost, the encrypted results cannot be recovered.

.. code-block:: bash

# Encrypt the example PEAT results directory and write the archive
# to the current directory
peat encrypt-results -f ./examples/encryption/example_peat_results

# Encrypt the example PEAT results directory into examples/encryption/
peat encrypt-results -f ./examples/encryption/example_peat_results -w ./examples/encryption/

# Encrypt a PEAT run directory with an explicit password
peat encrypt-results -f ./examples/encryption/example_peat_results -w ./examples/encryption/ -p example-password

Decrypting results
------------------
PEAT can decrypt an archive previously created with ``encrypt-results`` using the ``decrypt-results`` command and the same password that was used when the archive was created.

The command expects the path to an encrypted PEAT archive, such as ``./examples/encryption/encrypted_example_peat_results.zip``. Use ``-w`` (``--write-file``) to choose where the decrypted results directory is written. Use ``-p`` (``--password``) to provide the archive password on the command line. If ``-p`` is not provided, PEAT will prompt interactively for a password.

By default, the decrypted directory is written to the current working directory and named after the original run directory.

.. code-block:: bash

# Decrypt an encrypted archive into the current directory
peat decrypt-results -f ./examples/encryption/encrypted_example_peat_results.zip

# Decrypt an encrypted archive into a specific output directory
peat decrypt-results -f ./examples/encryption/encrypted_example_peat_results.zip -w ./restored_results/

# Decrypt an encrypted archive with an explicit password
peat decrypt-results -f ./examples/encryption/encrypted_example_peat_results.zip -w ./restored_results/ -p example-password

Device-specific results
-----------------------
.. warning::
Expand Down
Binary file not shown.
2 changes: 2 additions & 0 deletions examples/encryption/example_peat_results/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This directory contains a minimal example of PEAT-style results for the
``encrypt-results`` and ``decrypt-results`` documentation examples.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ip": "192.0.2.10",
"device_type": "example-device",
"hostname": "example-plc",
"pull_success": true
}
1 change: 1 addition & 0 deletions examples/encryption/example_peat_results/logs/peat.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
INFO example PEAT run log for encryption documentation
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"command": "pull",
"device_count": 1,
"devices": [
{
"ip": "192.0.2.10",
"device_type": "example-device",
"pull_success": true
}
]
}
76 changes: 45 additions & 31 deletions pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion peat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,9 @@ def emit(self, record: logging.LogRecord) -> None:
from .api.push_api import push
from .api.heat_api import heat_main
from .api.config_builder_api import generate_simple_config, generate_full_config
from .api.crypto_api import encrypt, decrypt
from .api.crypto_api import (
encrypt_config_api,
decrypt_config_api,
encrypt_results_api,
decrypt_results_api,
)
68 changes: 65 additions & 3 deletions peat/api/crypto_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import yaml

from peat import config_crypto, log
from peat import config_crypto, log, results_crypto


def encrypt(config_path: str, user_password: str | None = None) -> bool:
def encrypt_config_api(config_path: str, user_password: str | None = None) -> bool:
"""
PEAT CLI functionality to encrypt a file

Expand All @@ -14,6 +14,10 @@ def encrypt(config_path: str, user_password: str | None = None) -> bool:
user_password: (Optional) password for encryption specified by CLI, defaults to None.
If none is given by CLI command, user will be asked to input one
"""
if config_path is None:
log.error("No config specified")
return False

fp = Path(config_path)
result = config_crypto.encrypt_config(fp, user_password)
if result:
Expand All @@ -23,7 +27,7 @@ def encrypt(config_path: str, user_password: str | None = None) -> bool:
return False


def decrypt(
def decrypt_config_api(
config_path: str,
output_path: str | None = None,
new_filename: str | None = "decrypted_config.yaml",
Expand All @@ -41,6 +45,10 @@ def decrypt(
user_password: (Optional) password for encryption specified by CLI, defaults to None.
If none is given by CLI command, user will be asked to input one
"""
if config_path is None:
log.error("No config specified")
return False

fp = Path(config_path)
decrypted_str = config_crypto.decrypt_config(fp, user_password=user_password)
if not decrypted_str:
Expand All @@ -63,3 +71,57 @@ def decrypt(
yaml.dump(yaml_data, file, default_flow_style=False, sort_keys=False)
log.info(f"Encrypted config saved to current directory as {new_filename}")
return True


def encrypt_results_api(
results_dir_path: str, write_path: str | None = None, user_password: str | None = None
) -> bool:
"""
API for CLI -> Encrypt results function

Args:
results_dir_path: PEAT results directory
write_path: Path to write encrypted archive
user_password: password to encrypt archive with
Returns:
bool
"""
if results_dir_path is None:
log.error("No directory specified")
return False

results_dir_path = Path(results_dir_path)

if write_path is None:
write_path = Path("./")
else:
write_path = Path(write_path)

return results_crypto.zip_encrypt_results(results_dir_path, write_path, user_password)


def decrypt_results_api(
encrypted_dir_path: str, write_path: str | None = None, user_password: str | None = None
) -> bool:
"""
API for CLI -> Decrypt archive function

Args:
encrypted_dir_path: Encrypted zip archive path
write_path: Path to write extracted PEAT results
user_password: password to decrypt archive with
Returns:
bool
"""
if encrypted_dir_path is None:
log.error("No archive specified")
return False

encrypted_dir_path = Path(encrypted_dir_path)

if write_path is None:
write_path = Path("./")
else:
write_path = Path(write_path)

return results_crypto.unzip_decrypt_results(encrypted_dir_path, write_path, user_password)
Loading