Initial commit: SecPaste encrypted pastebin client
SecPaste is a Python library and CLI tool for sharing encrypted content via public pastebin services with zero-knowledge architecture. Features: - Pluggable crypto backends (AES-256-GCM, ChaCha20-Poly1305, Kyber-768) - Pluggable pastebin providers (dpaste.com, extensible) - URL fragment key storage (key never sent to server) - Both CLI and library usage - Post-quantum cryptography support (experimental) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
65
crypto/chacha.py
Normal file
65
crypto/chacha.py
Normal file
@@ -0,0 +1,65 @@
|
||||
"""ChaCha20-Poly1305 cipher backend."""
|
||||
|
||||
import os
|
||||
import base64
|
||||
from typing import Tuple
|
||||
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
|
||||
from .base import CipherBackend
|
||||
|
||||
|
||||
class ChaCha20Cipher(CipherBackend):
|
||||
"""ChaCha20-Poly1305 authenticated encryption."""
|
||||
|
||||
def encrypt(self, plaintext: bytes) -> Tuple[bytes, str]:
|
||||
"""
|
||||
Encrypt using ChaCha20-Poly1305.
|
||||
|
||||
Returns:
|
||||
Tuple of (nonce + ciphertext, base64url-encoded key)
|
||||
"""
|
||||
# Generate random 256-bit key
|
||||
key = ChaCha20Poly1305.generate_key()
|
||||
chacha = ChaCha20Poly1305(key)
|
||||
|
||||
# Generate random 96-bit nonce
|
||||
nonce = os.urandom(12)
|
||||
|
||||
# Encrypt and authenticate
|
||||
ciphertext = chacha.encrypt(nonce, plaintext, None)
|
||||
|
||||
# Prepend nonce to ciphertext
|
||||
encrypted_data = nonce + ciphertext
|
||||
|
||||
# Encode key for URL fragment
|
||||
key_encoded = base64.urlsafe_b64encode(key).decode('ascii').rstrip('=')
|
||||
|
||||
return encrypted_data, key_encoded
|
||||
|
||||
def decrypt(self, ciphertext: bytes, key: str) -> bytes:
|
||||
"""
|
||||
Decrypt using ChaCha20-Poly1305.
|
||||
|
||||
Args:
|
||||
ciphertext: Nonce (12 bytes) + encrypted data
|
||||
key: Base64url-encoded 256-bit key
|
||||
"""
|
||||
# Decode key (add padding if needed)
|
||||
padding = '=' * (4 - len(key) % 4) if len(key) % 4 else ''
|
||||
key_bytes = base64.urlsafe_b64decode(key + padding)
|
||||
|
||||
if len(key_bytes) != 32:
|
||||
raise ValueError("Invalid key length. Expected 32 bytes for ChaCha20.")
|
||||
|
||||
# Extract nonce and ciphertext
|
||||
nonce = ciphertext[:12]
|
||||
encrypted = ciphertext[12:]
|
||||
|
||||
# Decrypt
|
||||
chacha = ChaCha20Poly1305(key_bytes)
|
||||
plaintext = chacha.decrypt(nonce, encrypted, None)
|
||||
|
||||
return plaintext
|
||||
|
||||
def get_name(self) -> str:
|
||||
"""Return cipher name."""
|
||||
return "chacha20-poly1305"
|
||||
Reference in New Issue
Block a user