Skip to content

Commit

Permalink
feat(cli): Add command-line interface for self-encryption
Browse files Browse the repository at this point in the history
Add a comprehensive CLI that exposes all core functionality of the self-encryption
library, making it easily accessible from the terminal. The CLI is fully
integrated with the Python package and will be included in all releases.

New Features:
- Add `self-encryption` CLI command with subcommands:
  - encrypt-file: Encrypt files and store chunks
  - decrypt-file: Decrypt files using data maps and chunks
  - verify: Verify integrity of encrypted chunks
  - shrink: Optimize data maps by consolidating chunks
- Add comprehensive --help documentation for all commands
- Support both JSON and human-readable output formats
- Include streaming support for large file operations

Technical Changes:
- Add click>=8.0.0 as a dependency
- Configure entry points in both pyproject.toml and setup.py
- Integrate CLI module with package's public interface
- Add error handling with colored output

The CLI maintains full compatibility with existing Python bindings while
making the library more accessible to command-line users.
  • Loading branch information
dirvine committed Nov 19, 2024
1 parent cbaa0fd commit 1994b1a
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 2 deletions.
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ classifiers = [
dependencies = [
"pip>=24.0",
"pytest>=7.4.4",
"click>=8.0.0",
]

[project.scripts]
self-encryption = "self_encryption.cli:cli"

[tool.maturin]
features = ["python"]
module-name = "self_encryption._self_encryption"
Expand Down
5 changes: 4 additions & 1 deletion self_encryption/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@
verify_chunk,
)

from .cli import cli

__all__ = [
"DataMap",
"EncryptedChunk",
Expand All @@ -76,4 +78,5 @@
"shrink_data_map",
"streaming_decrypt_from_storage",
"verify_chunk",
]
"cli",
]
161 changes: 161 additions & 0 deletions self_encryption/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
self-encryption-cli - Command line interface for self_encryption library
This CLI provides access to all functionality of the self_encryption library,
including encryption, decryption, and advanced features like streaming operations
and chunk verification.
"""

import click
from pathlib import Path
from typing import Optional
import sys

from self_encryption import (
DataMap,
EncryptedChunk,
XorName,
encrypt,
encrypt_from_file,
decrypt,
decrypt_from_storage,
shrink_data_map,
streaming_decrypt_from_storage,
verify_chunk,
)

def print_error(message: str):
"""Print error message in red."""
click.secho(f"Error: {message}", fg='red', err=True)

@click.group()
@click.version_option()
def cli():
"""
self-encryption - A convergent encryption tool with obfuscation
This tool provides secure data encryption that supports deduplication while
maintaining strong security through content obfuscation and chunk interdependencies.
"""
pass

@cli.command()
@click.argument('input-file', type=click.Path(exists=True, dir_okay=False))
@click.argument('output-dir', type=click.Path(file_okay=False))
@click.option('--json', is_flag=True, help='Output data map in JSON format')
def encrypt_file(input_file: str, output_dir: str, json: bool):
"""
Encrypt a file and store its chunks.
The encrypted chunks will be stored in OUTPUT-DIR, and the data map will be
printed to stdout. The data map is required for later decryption.
Example:
$ self-encryption encrypt-file input.dat chunks/
"""
try:
data_map, chunk_names = encrypt_from_file(input_file, output_dir)
if json:
click.echo(data_map.to_json())
else:
click.echo(str(data_map))
except Exception as e:
print_error(str(e))
sys.exit(1)

@cli.command()
@click.argument('data-map-file', type=click.Path(exists=True, dir_okay=False))
@click.argument('chunks-dir', type=click.Path(exists=True, file_okay=False))
@click.argument('output-file', type=click.Path())
@click.option('--streaming', is_flag=True, help='Use streaming decryption for large files')
def decrypt_file(data_map_file: str, chunks_dir: str, output_file: str, streaming: bool):
"""
Decrypt a file using its data map and stored chunks.
Reads the data map from DATA-MAP-FILE, retrieves chunks from CHUNKS-DIR,
and writes the decrypted data to OUTPUT-FILE.
Example:
$ self-encryption decrypt-file data_map.json chunks/ output.dat
"""
try:
# Read data map from file
with open(data_map_file, 'r') as f:
data_map = DataMap.from_json(f.read())

chunks_path = Path(chunks_dir)

def get_chunk(hash_hex: str) -> bytes:
chunk_path = chunks_path / hash_hex
if not chunk_path.exists():
raise click.ClickException(f"Chunk not found: {hash_hex}")
return chunk_path.read_bytes()

if streaming:
streaming_decrypt_from_storage(data_map, output_file, get_chunk)
else:
decrypt_from_storage(data_map, output_file, get_chunk)

except Exception as e:
print_error(str(e))
sys.exit(1)

@cli.command()
@click.argument('chunk-file', type=click.Path(exists=True, dir_okay=False))
def verify(chunk_file: str):
"""
Verify the integrity of an encrypted chunk.
Checks if the chunk's content matches its XorName.
Example:
$ self-encryption verify chunk_abc123.dat
"""
try:
chunk_path = Path(chunk_file)
content = chunk_path.read_bytes()
name = XorName.from_hex(chunk_path.stem)
chunk = verify_chunk(name, content)
click.echo(f"Chunk {chunk_path.name} verified successfully")
except Exception as e:
print_error(str(e))
sys.exit(1)

@cli.command()
@click.argument('data-map-file', type=click.Path(exists=True, dir_okay=False))
@click.argument('chunks-dir', type=click.Path(exists=True, file_okay=False))
@click.argument('output-map-file', type=click.Path())
def shrink(data_map_file: str, chunks_dir: str, output_map_file: str):
"""
Shrink a data map by consolidating its chunks.
Reads the data map from DATA-MAP-FILE, processes chunks from CHUNKS-DIR,
and writes the optimized data map to OUTPUT-MAP-FILE.
Example:
$ self-encryption shrink data_map.json chunks/ optimized_map.json
"""
try:
# Read data map from file
with open(data_map_file, 'r') as f:
data_map = DataMap.from_json(f.read())

chunks_path = Path(chunks_dir)

def store_chunk(chunk: EncryptedChunk) -> None:
chunk_path = chunks_path / chunk.name.hex()
chunk_path.write_bytes(chunk.content)

new_data_map, _ = shrink_data_map(data_map, store_chunk)

# Write new data map to file
with open(output_map_file, 'w') as f:
f.write(new_data_map.to_json())

except Exception as e:
print_error(str(e))
sys.exit(1)

if __name__ == '__main__':
cli()
10 changes: 9 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@
zip_safe=False,
include_package_data=True,
python_requires=">=3.7",
)
install_requires=[
'click>=8.0.0',
],
entry_points={
'console_scripts': [
'self-encryption=self_encryption.cli:cli',
],
},
)

0 comments on commit 1994b1a

Please sign in to comment.