feat: expand onion service api

This commit is contained in:
Iain Learmonth 2024-12-06 13:34:44 +00:00
parent c1b385ed99
commit e5976c4739
11 changed files with 646 additions and 348 deletions

View file

@ -11,101 +11,10 @@ from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
from datetime import datetime, timedelta, timezone
def generate_onion_keys_with_mkp224o(folder_name: str, label: str):
"""
Generate Tor-compatible Onion service keys using mkp224o.
The keys are saved in the specified folder, and the Onion address is returned.
"""
os.makedirs(folder_name, exist_ok=True)
# Call mkp224o to generate a single Onion service key
process = subprocess.run(
["mkp224o", "-n", "1", "-d", folder_name, label],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
try:
process.check_returncode()
except subprocess.CalledProcessError:
print("STDOUT:", process.stdout.decode())
print("STDERR:", process.stderr.decode())
raise
# Find the generated Onion address
for filename in os.listdir(folder_name):
if filename.endswith(".onion"):
onion_address = filename
onion_dir = os.path.join(folder_name, filename)
# Move files to parent directory
for key_file in ["hs_ed25519_secret_key", "hs_ed25519_public_key", "hostname"]:
src = os.path.join(onion_dir, key_file)
dst = os.path.join(folder_name, key_file)
if os.path.exists(src):
shutil.move(src, dst)
# Remove the now-empty directory
os.rmdir(onion_dir)
return onion_address
raise RuntimeError("Failed to generate Onion keys using mkp224o")
from tests.api.test_onion import generate_onion_keys_with_mkp224o, generate_self_signed_tls_certificate
def generate_self_signed_tls_certificate(folder_name: str, onion_address: str, valid_from: datetime, valid_to: datetime, dns_names=None):
"""
Generate a self-signed TLS certificate for the Onion address and save it in the specified folder.
"""
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend(),
)
subject = x509.Name(
[
x509.NameAttribute(NameOID.COUNTRY_NAME, "US"),
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Test State"),
x509.NameAttribute(NameOID.LOCALITY_NAME, "Test City"),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Test Org"),
x509.NameAttribute(NameOID.COMMON_NAME, onion_address),
]
)
if dns_names is None:
dns_names = [onion_address, f"*.{onion_address}"]
san_extension = x509.SubjectAlternativeName([x509.DNSName(name) for name in dns_names])
certificate = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(subject)
.public_key(private_key.public_key())
.serial_number(x509.random_serial_number())
.not_valid_before(valid_from)
.not_valid_after(valid_to)
.add_extension(san_extension, critical=False)
.sign(private_key, SHA256(), default_backend())
)
private_key_path = os.path.join(folder_name, "tls_private_key.pem")
certificate_path = os.path.join(folder_name, "tls_certificate.pem")
with open(private_key_path, "wb") as f:
f.write(
private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
)
)
with open(certificate_path, "wb") as f:
f.write(certificate.public_bytes(serialization.Encoding.PEM))
return private_key_path, certificate_path
def generate_rest_payload(parent_folder: str, folder_name: str, onion_address: str):
def generate_create_rest_payload(parent_folder: str, folder_name: str):
"""
Generate REST payload for a specific Onion service and append it to a shared .rest file.
"""
@ -121,7 +30,7 @@ def generate_rest_payload(parent_folder: str, folder_name: str, onion_address: s
tls_public_key = f.read()
payload = {
"DomainName": onion_address,
"DomainName": "example.com",
"Description": f"Generated Onion Service for {folder_name}",
"OnionPrivateKey": onion_private_key,
"OnionPublicKey": onion_public_key,
@ -155,6 +64,6 @@ if __name__ == "__main__":
print(f"Generating {folder_name}...")
onion_address = generate_onion_keys_with_mkp224o(folder_name, "test")
generate_self_signed_tls_certificate(folder_name, onion_address, valid_from, valid_to, dns_names)
generate_rest_payload(parent_folder, folder_name, onion_address)
generate_create_rest_payload(parent_folder, folder_name)
print("All Onion services and REST requests generated successfully.")