From 35409ac487c2a5fc995a6772f5653fbfa871cfe3 Mon Sep 17 00:00:00 2001 From: Google AI Agent Date: Mon, 30 Mar 2026 21:14:18 +0000 Subject: [PATCH] feat: add NostrIdentity for sovereign registration --- nexus/nostr_identity.py | 102 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 nexus/nostr_identity.py diff --git a/nexus/nostr_identity.py b/nexus/nostr_identity.py new file mode 100644 index 0000000..82587e9 --- /dev/null +++ b/nexus/nostr_identity.py @@ -0,0 +1,102 @@ + +import hashlib +import hmac +import os +import binascii + +# ═══════════════════════════════════════════ +# NOSTR SOVEREIGN IDENTITY (NIP-01) +# ═══════════════════════════════════════════ +# Pure Python implementation of Schnorr signatures for Nostr. +# No dependencies required. + +def sha256(data): + return hashlib.sha256(data).digest() + +def hmac_sha256(key, data): + return hmac.new(key, data, hashlib.sha256).digest() + +# Secp256k1 Constants +P = 2**256 - 2**32 - 977 +N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 +G = (0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, + 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8) + +def inverse(a, n): + return pow(a, n - 2, n) + +def point_add(p1, p2): + if p1 is None: return p2 + if p2 is None: return p1 + (x1, y1), (x2, y2) = p1, p2 + if x1 == x2 and y1 != y2: return None + if x1 == x2: + m = (3 * x1 * x1 * inverse(2 * y1, P)) % P + else: + m = ((y2 - y1) * inverse(x2 - x1, P)) % P + x3 = (m * m - x1 - x2) % P + y3 = (m * (x1 - x3) - y1) % P + return (x3, y3) + +def point_mul(p, n): + r = None + for i in range(256): + if (n >> i) & 1: + r = point_add(r, p) + p = point_add(p, p) + return r + +def get_pubkey(privkey): + p = point_mul(G, privkey) + return binascii.hexlify(p[0].to_bytes(32, 'big')).decode() + +# Schnorr Signature (BIP340) +def sign_schnorr(msg_hash, privkey): + k = int.from_bytes(sha256(privkey.to_bytes(32, 'big') + msg_hash), 'big') % N + R = point_mul(G, k) + if R[1] % 2 != 0: + k = N - k + r = R[0].to_bytes(32, 'big') + e = int.from_bytes(sha256(r + binascii.unhexlify(get_pubkey(privkey)) + msg_hash), 'big') % N + s = (k + e * privkey) % N + return binascii.hexlify(r + s.to_bytes(32, 'big')).decode() + +class NostrIdentity: + def __init__(self, privkey_hex=None): + if privkey_hex: + self.privkey = int(privkey_hex, 16) + else: + self.privkey = int.from_bytes(os.urandom(32), 'big') % N + self.pubkey = get_pubkey(self.privkey) + + def sign_event(self, event): + # NIP-01 Event Signing + import json + event_data = [ + 0, + event['pubkey'], + event['created_at'], + event['kind'], + event['tags'], + event['content'] + ] + serialized = json.dumps(event_data, separators=(',', ':')) + msg_hash = sha256(serialized.encode()) + event['id'] = binascii.hexlify(msg_hash).decode() + event['sig'] = sign_schnorr(msg_hash, self.privkey) + return event + +if __name__ == "__main__": + # Test Identity + identity = NostrIdentity() + print(f"Nostr Pubkey: {identity.pubkey}") + + event = { + "pubkey": identity.pubkey, + "created_at": 1677628800, + "kind": 1, + "tags": [], + "content": "Sovereignty and service always. #Timmy" + } + signed_event = identity.sign_event(event) + print(f"Signed Event: {signed_event}")