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:
103
providers/dpaste.py
Normal file
103
providers/dpaste.py
Normal file
@@ -0,0 +1,103 @@
|
||||
"""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')
|
||||
Reference in New Issue
Block a user