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>
104 lines
2.9 KiB
Python
104 lines
2.9 KiB
Python
"""dpaste.com provider implementation."""
|
|
|
|
import base64
|
|
import requests
|
|
from typing import Optional
|
|
from .base import PastebinProvider
|
|
|
|
|
|
class DPasteProvider(PastebinProvider):
|
|
"""Provider for dpaste.com pastebin service."""
|
|
|
|
BASE_URL = "https://dpaste.com"
|
|
|
|
def __init__(self, api_url: Optional[str] = None):
|
|
"""
|
|
Initialize dpaste provider.
|
|
|
|
Args:
|
|
api_url: Override API URL (useful for self-hosted instances)
|
|
"""
|
|
self.api_url = api_url or self.BASE_URL
|
|
|
|
def paste(self, content: bytes, expiry: str = "onetime", syntax: str = "text", **kwargs) -> str:
|
|
"""
|
|
Upload encrypted content to dpaste.
|
|
|
|
Args:
|
|
content: Encrypted bytes to upload
|
|
expiry: Expiration time (onetime, hour, day, week, month, never)
|
|
syntax: Syntax highlighting (text, python, etc.)
|
|
**kwargs: Additional provider-specific options
|
|
|
|
Returns:
|
|
Full paste URL (without fragment)
|
|
"""
|
|
# Base64 encode binary content for safe transmission
|
|
content_b64 = base64.b64encode(content).decode('ascii')
|
|
|
|
# dpaste API expects form data
|
|
data = {
|
|
'content': content_b64,
|
|
'syntax': syntax,
|
|
'expiry_days': self._convert_expiry(expiry),
|
|
}
|
|
|
|
# POST to create paste
|
|
response = requests.post(
|
|
f"{self.api_url}/api/v2/",
|
|
data=data,
|
|
headers={'User-Agent': 'SecPaste/0.1.0'}
|
|
)
|
|
response.raise_for_status()
|
|
|
|
# dpaste returns plain text URL
|
|
paste_url = response.text.strip()
|
|
return paste_url
|
|
|
|
def fetch(self, paste_id: str) -> bytes:
|
|
"""
|
|
Fetch encrypted content from dpaste.
|
|
|
|
Args:
|
|
paste_id: Full URL or just the paste ID
|
|
|
|
Returns:
|
|
Encrypted bytes (base64 decoded)
|
|
"""
|
|
# Handle both full URLs and just IDs
|
|
if paste_id.startswith('http'):
|
|
url = paste_id
|
|
else:
|
|
url = f"{self.api_url}/{paste_id}"
|
|
|
|
# Add .raw to get raw content
|
|
if not url.endswith('.raw'):
|
|
url = f"{url}.raw"
|
|
|
|
response = requests.get(url, headers={'User-Agent': 'SecPaste/0.1.0'})
|
|
response.raise_for_status()
|
|
|
|
# Decode from base64
|
|
content = base64.b64decode(response.text.strip())
|
|
return content
|
|
|
|
def get_name(self) -> str:
|
|
"""Return provider name."""
|
|
return "dpaste"
|
|
|
|
def get_base_url(self) -> str:
|
|
"""Return base URL."""
|
|
return self.api_url
|
|
|
|
def _convert_expiry(self, expiry: str) -> str:
|
|
"""Convert expiry string to dpaste format."""
|
|
expiry_map = {
|
|
'onetime': '0',
|
|
'hour': '1',
|
|
'day': '1',
|
|
'week': '7',
|
|
'month': '30',
|
|
'never': '365' # dpaste max is 365 days
|
|
}
|
|
return expiry_map.get(expiry, '7')
|