#!/usr/bin/env python3 """ http_test.py — test the HTTP listener mode with curl-like requests. Starts nsigner in HTTP mode, sends requests via urllib, and verifies the responses. """ import json import os import subprocess import sys import time import urllib.request NSIGNER = "./build/nsigner" PAD_DIR = "/media/user/Music/pads" PAD_SPEC = "333e9902db839d9d" MNEMONIC_FILE = ".test_mnemonic" MNEMONIC_TMP = ".test_mnemonic_http.tmp" PORT = 11111 def main(): # Reset pad offset state_path = f"{PAD_DIR}/333e9902db839d9d7f1f6aaa30f392a77c9abd011dd6274d9d3cf167361a789e.state" with open(state_path, "w") as f: f.write("offset=32\n") # Prepare mnemonic temp file with open(MNEMONIC_FILE) as f: mnemonic = f.read().strip() with open(MNEMONIC_TMP, "w") as f: f.write(mnemonic + "\n") # Start nsigner in HTTP mode shell_cmd = ( f"exec 3<{MNEMONIC_TMP} 2>/dev/null; " f"exec {NSIGNER} --listen http:127.0.0.1:{PORT} --mnemonic-fd 3 " f"--otp-pad-dir {PAD_DIR} --otp-pad {PAD_SPEC} " f"--otp-allow-blkback --allow-all" ) proc = subprocess.Popen( ["bash", "-c", shell_cmd], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) time.sleep(2.0) # let the signer start if proc.poll() is not None: err = proc.stderr.read().decode() print(f"ERROR: nsigner exited early (code {proc.returncode})") print(f"stderr: {err}") try: os.unlink(MNEMONIC_TMP) except OSError: pass return 1 try: url = f"http://127.0.0.1:{PORT}/" # Test 1: get_public_key print("=== Test 1: get_public_key via HTTP ===") req_data = json.dumps({ "id": "1", "method": "get_public_key", "params": [{"role": "main"}], }).encode() req = urllib.request.Request(url, data=req_data, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=5) as resp: result = json.loads(resp.read().decode()) print(f"Response: {json.dumps(result)[:120]}...") if "result" not in result: print("ERROR: no result in get_public_key response") return 1 pubkey = result["result"].strip('"') print(f"Public key: {pubkey}") # Test 2: otp_encrypt print("\n=== Test 2: otp_encrypt via HTTP ===") import base64 pt_b64 = base64.b64encode(b"Hello, OTP via HTTP!").decode() req_data = json.dumps({ "id": "2", "method": "otp_encrypt", "params": [pt_b64, {"encoding": "ascii"}], }).encode() req = urllib.request.Request(url, data=req_data, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=5) as resp: result = json.loads(resp.read().decode()) print(f"Response: {json.dumps(result)[:200]}...") if "result" not in result: print("ERROR: no result in otp_encrypt response") return 1 enc_result = json.loads(result["result"]) ciphertext = enc_result["ciphertext"] print(f"Pad offset: {enc_result['pad_offset_before']} -> {enc_result['pad_offset_after']}") print(f"Ciphertext (first 60 chars): {ciphertext[:60]}...") # Test 3: otp_decrypt print("\n=== Test 3: otp_decrypt via HTTP ===") req_data = json.dumps({ "id": "3", "method": "otp_decrypt", "params": [ciphertext, {"encoding": "ascii"}], }).encode() req = urllib.request.Request(url, data=req_data, headers={"Content-Type": "application/json"}) with urllib.request.urlopen(req, timeout=5) as resp: result = json.loads(resp.read().decode()) print(f"Response: {json.dumps(result)[:200]}...") if "result" not in result: print("ERROR: no result in otp_decrypt response") return 1 dec_result = json.loads(result["result"]) recovered = base64.b64decode(dec_result["plaintext"]).decode() print(f"Recovered plaintext: {recovered}") if recovered == "Hello, OTP via HTTP!": print("\n=== HTTP ROUND-TRIP SUCCESS ===") return 0 else: print("\n=== HTTP ROUND-TRIP FAILED ===") print(f"Expected: Hello, OTP via HTTP!") print(f"Got: {recovered}") return 1 finally: proc.terminate() try: proc.wait(timeout=3) except subprocess.TimeoutExpired: proc.kill() try: os.unlink(MNEMONIC_TMP) except OSError: pass if __name__ == "__main__": sys.exit(main())