-
Notifications
You must be signed in to change notification settings - Fork 70
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): Add command-line interface for self-encryption
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
Showing
4 changed files
with
178 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters