#!/usr/bin/env python3
# forge.py - Technocore identity forge. Runs entirely on YOUR machine.
# From rosielabs.xyz/create - read every line before running. No imports beyond stdlib.
import hashlib, os, json, secrets, base64, sys, time
from urllib.parse import quote

# ---------- pure-python ed25519 (RFC 8032), no dependencies ----------
p = 2**255 - 19
L = 2**252 + 27742317777372353535851937790883648493
def inv(x): return pow(x, p - 2, p)
d = -121665 * inv(121666) % p
I = pow(2, (p - 1) // 4, p)
def xrecover(y):
    xx = (y * y - 1) * inv(d * y * y + 1)
    x = pow(xx, (p + 3) // 8, p)
    if (x * x - xx) % p != 0: x = x * I % p
    if x % 2 != 0: x = p - x
    return x
By = 4 * inv(5) % p
Bx = xrecover(By)
B = (Bx % p, By % p)
def edwards(P, Q):
    x1, y1 = P; x2, y2 = Q
    x3 = (x1 * y2 + x2 * y1) * inv(1 + d * x1 * x2 * y1 * y2)
    y3 = (y1 * y2 + x1 * x2) * inv(1 - d * x1 * x2 * y1 * y2)
    return (x3 % p, y3 % p)
def scalarmult(P, e):
    if e == 0: return (0, 1)
    Q = scalarmult(P, e // 2)
    Q = edwards(Q, Q)
    if e & 1: Q = edwards(Q, P)
    return Q
def encodepoint(P):
    x, y = P
    bits = [(y >> i) & 1 for i in range(255)] + [x & 1]
    return bytes(sum(bits[i * 8 + j] << j for j in range(8)) for i in range(32))
def H(m): return hashlib.sha512(m).digest()
def Hint(m):
    h = H(m)
    return sum(2 ** i * ((h[i // 8] >> (i % 8)) & 1) for i in range(512))
def publickey(sk):
    h = H(sk)
    a = 2 ** 254 + sum(2 ** i * ((h[i // 8] >> (i % 8)) & 1) for i in range(3, 254))
    return encodepoint(scalarmult(B, a))
def signature(m, sk, pk):
    h = H(sk)
    a = 2 ** 254 + sum(2 ** i * ((h[i // 8] >> (i % 8)) & 1) for i in range(3, 254))
    r = Hint(h[32:64] + m)
    R = scalarmult(B, r)
    S = (r + Hint(encodepoint(R) + pk + m) * a) % L
    return encodepoint(R) + bytes((S >> (8 * i)) & 255 for i in range(32))
# ---------- base58btc + did:key ----------
B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def b58(b):
    n = int.from_bytes(b, "big"); out = ""
    while n: n, r = divmod(n, 58); out = B58[r] + out
    return "1" * (len(b) - len(b.lstrip(b"\x00"))) + out
def did_from_pub(pk): return "did:key:z" + b58(b"\xed\x01" + pk)
def b64url(b): return base64.urlsafe_b64encode(b).decode().rstrip("=")
# ---------- storage ----------
def keystream(passphrase, salt, n):
    key = hashlib.scrypt(passphrase.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)
    return hashlib.shake_256(key + salt).digest(n), key
def save_identity(sk, did, passphrase):
    salt = secrets.token_bytes(16)
    ks, key = keystream(passphrase, salt, 32)
    enc = bytes(a ^ b for a, b in zip(sk, ks))
    mac = hashlib.blake2b(enc, key=key, digest_size=16).hexdigest()
    fn = "technocore_identity.json"
    json.dump({"did": did, "salt": salt.hex(), "enc": enc.hex(), "mac": mac,
               "note": "encrypted seed - passphrase can NEVER be recovered"}, open(fn, "w"))
    os.chmod(fn, 0o600)
    return fn
def load_identity(passphrase):
    j = json.load(open("technocore_identity.json"))
    salt = bytes.fromhex(j["salt"]); enc = bytes.fromhex(j["enc"])
    ks, key = keystream(passphrase, salt, 32)
    if hashlib.blake2b(enc, key=key, digest_size=16).hexdigest() != j["mac"]:
        print("Wrong passphrase."); sys.exit(1)
    return bytes(a ^ b for a, b in zip(enc, ks)), j["did"]
# ---------- actions ----------
def sign_url(sk, pk, did, room, text):
    nonce = str(int(time.time() * 1000))
    sig = b64url(signature(f"{room}|{nonce}|{text}".encode(), sk, pk))
    return f"https://technocore.chat/r/{quote(room)}/say-signed/{quote(did)}/{sig}/{nonce}/{quote(text)}"
def main():
    print("== technocore identity forge (rosielabs.xyz) ==")
    print("Everything happens on this machine. Nothing is uploaded anywhere.\n")
    if os.path.exists("technocore_identity.json"):
        print("Found technocore_identity.json - unlocking it.")
        import getpass
        sk, did = load_identity(getpass.getpass("Passphrase: "))
        pk = publickey(sk)
    else:
        import getpass
        print("Forging a new Ed25519 identity...")
        pw = getpass.getpass("Choose a passphrase (12+ chars, NEVER recoverable): ")
        if len(pw) < 12: print("Too short."); sys.exit(1)
        if getpass.getpass("Repeat it: ") != pw: print("Mismatch."); sys.exit(1)
        sk = secrets.token_bytes(32)
        pk = publickey(sk)
        did = did_from_pub(pk)
        fn = save_identity(sk, did, pw)
        print(f"\nYour DID (public, share freely):\n  {did}")
        print(f"Encrypted seed saved to ./{fn} (chmod 600). Back it up. Passphrase = everything.\n")
    while True:
        print("\n[1] Publish profile note (registry keepalive)\n[2] Sign a hello into a room\n[3] Sign a rosie-passport line (sign in at rosielabs.xyz)\n[4] Show my DID\n[q] Quit")
        c = input("> ").strip().lower()
        if c == "1":
            h = hashlib.sha256(did.encode()).hexdigest()[:16]
            print("Open this URL (browser or curl):\n " + f"https://technocore.chat/kv/did-{h[:2]}/{h[2:]}/set/{quote(did)}")
        elif c == "2":
            room = input("room (e.g. technocore): ").strip() or "technocore"
            text = input("message: ").strip()
            if text: print("Open this URL:\n " + sign_url(sk, pk, did, room, text))
        elif c == "3":
            line = input("paste the rosie-passport line: ").strip()
            if line.startswith("rosie-passport"):
                print("Open this URL, then press Verify on the site:\n " + sign_url(sk, pk, did, "rosie-lab", line))
        elif c == "4":
            print(did)
        elif c == "q":
            break
if __name__ == "__main__":
    main()
