Nicholastempleman commited on
Commit
94afb63
·
verified ·
1 Parent(s): 8c4f8b5

Upload sigil_ed25519.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. sigil_ed25519.py +200 -0
sigil_ed25519.py ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Ed25519 SIGIL signing for SOV33 API endpoints.
3
+
4
+ Usage:
5
+ from sigil_ed25519 import SigilSigner
6
+
7
+ signer = SigilSigner()
8
+ sigil = signer.sign({"action": "test", "timestamp": "2026-07-26"})
9
+ assert signer.verify(sigil)
10
+ """
11
+ import json
12
+ import hashlib
13
+ import time
14
+ from pathlib import Path
15
+
16
+ try:
17
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
18
+ from cryptography.hazmat.primitives import serialization
19
+ HAS_CRYPTO = True
20
+ except ImportError:
21
+ HAS_CRYPTO = False
22
+
23
+
24
+ class SigilSigner:
25
+ """Ed25519 SIGIL signer for sovereign actions."""
26
+
27
+ def __init__(self, key_path=None):
28
+ if not HAS_CRYPTO:
29
+ raise ImportError("pip install cryptography")
30
+
31
+ if key_path and Path(key_path).exists():
32
+ self._load_key(key_path)
33
+ else:
34
+ self._generate_key(key_path)
35
+
36
+ def _generate_key(self, save_path=None):
37
+ """Generate new Ed25519 key pair."""
38
+ self.private_key = Ed25519PrivateKey.generate()
39
+ self.public_key = self.private_key.public_key()
40
+
41
+ if save_path:
42
+ Path(save_path).parent.mkdir(parents=True, exist_ok=True)
43
+ # Save private key
44
+ priv_pem = self.private_key.private_bytes(
45
+ encoding=serialization.Encoding.PEM,
46
+ format=serialization.PrivateFormat.PKCS8,
47
+ encryption_algorithm=serialization.NoEncryption()
48
+ )
49
+ Path(save_path).write_bytes(priv_pem)
50
+ Path(save_path).chmod(0o600)
51
+
52
+ # Save public key
53
+ pub_path = str(save_path) + ".pub"
54
+ pub_pem = self.public_key.public_bytes(
55
+ encoding=serialization.Encoding.PEM,
56
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
57
+ )
58
+ Path(pub_path).write_bytes(pub_pem)
59
+
60
+ def _load_key(self, key_path):
61
+ """Load existing Ed25519 key pair."""
62
+ priv_pem = Path(key_path).read_bytes()
63
+ self.private_key = serialization.load_pem_private_key(priv_pem, password=None)
64
+ self.public_key = self.private_key.public_key()
65
+
66
+ def sign(self, payload):
67
+ """Create SIGIL signature for payload."""
68
+ if isinstance(payload, dict):
69
+ payload = json.dumps(payload, sort_keys=True).encode()
70
+ elif isinstance(payload, str):
71
+ payload = payload.encode()
72
+
73
+ signature = self.private_key.sign(payload)
74
+
75
+ return {
76
+ "payload": payload.decode() if isinstance(payload, bytes) else payload,
77
+ "signature": signature.hex(),
78
+ "algorithm": "Ed25519",
79
+ "timestamp": time.time(),
80
+ "sha256": hashlib.sha256(payload).hexdigest()
81
+ }
82
+
83
+ def verify(self, sigil):
84
+ """Verify SIGIL signature."""
85
+ try:
86
+ payload = sigil["payload"]
87
+ if isinstance(payload, str):
88
+ payload = payload.encode()
89
+
90
+ signature = bytes.fromhex(sigil["signature"])
91
+ self.public_key.verify(signature, payload)
92
+
93
+ # Verify SHA256
94
+ expected_hash = hashlib.sha256(payload).hexdigest()
95
+ if sigil.get("sha256") != expected_hash:
96
+ return False
97
+
98
+ return True
99
+ except Exception:
100
+ return False
101
+
102
+ def get_public_key_hex(self):
103
+ """Get public key as hex string."""
104
+ pub_bytes = self.public_key.public_bytes(
105
+ encoding=serialization.Encoding.Raw,
106
+ format=serialization.PublicFormat.Raw
107
+ )
108
+ return pub_bytes.hex()
109
+
110
+
111
+ class SigilChain:
112
+ """Hash-chained SIGIL ledger."""
113
+
114
+ def __init__(self, signer):
115
+ self.signer = signer
116
+ self.chain = []
117
+ self.prev_hash = "genesis"
118
+
119
+ def append(self, payload):
120
+ """Append new SIGIL to chain."""
121
+ chain_payload = {
122
+ "payload": payload,
123
+ "prev_hash": self.prev_hash,
124
+ "chain_index": len(self.chain),
125
+ }
126
+
127
+ sigil = self.signer.sign(chain_payload)
128
+ sigil["chain_hash"] = hashlib.sha256(
129
+ (self.prev_hash + sigil["sha256"]).encode()
130
+ ).hexdigest()
131
+
132
+ self.chain.append(sigil)
133
+ self.prev_hash = sigil["chain_hash"]
134
+
135
+ return sigil
136
+
137
+ def verify_chain(self):
138
+ """Verify entire chain integrity."""
139
+ prev_hash = "genesis"
140
+
141
+ for i, sigil in enumerate(self.chain):
142
+ # Verify signature
143
+ if not self.signer.verify(sigil):
144
+ return False, f"Signature invalid at index {i}"
145
+
146
+ # Verify chain linkage
147
+ expected_hash = hashlib.sha256(
148
+ (prev_hash + sigil["sha256"]).encode()
149
+ ).hexdigest()
150
+ if sigil.get("chain_hash") != expected_hash:
151
+ return False, f"Chain broken at index {i}"
152
+
153
+ prev_hash = sigil["chain_hash"]
154
+
155
+ return True, "Chain valid"
156
+
157
+
158
+ # Global signer instance
159
+ _signer = None
160
+
161
+ def get_signer(key_path="/workspace/sigil_key.pem"):
162
+ """Get or create global signer."""
163
+ global _signer
164
+ if _signer is None:
165
+ _signer = SigilSigner(key_path)
166
+ return _signer
167
+
168
+
169
+ def sign_response(response_data):
170
+ """Sign API response with SIGIL."""
171
+ signer = get_signer()
172
+ return signer.sign(response_data)
173
+
174
+
175
+ def verify_sigil(sigil):
176
+ """Verify SIGIL signature."""
177
+ signer = get_signer()
178
+ return signer.verify(sigil)
179
+
180
+
181
+ if __name__ == "__main__":
182
+ # Demo
183
+ signer = SigilSigner("/tmp/sigil_demo_key.pem")
184
+
185
+ # Sign
186
+ sigil = signer.sign({"action": "test", "data": "hello world"})
187
+ print("Signed:", json.dumps(sigil, indent=2)[:200])
188
+
189
+ # Verify
190
+ valid = signer.verify(sigil)
191
+ print("Valid:", valid)
192
+
193
+ # Chain demo
194
+ chain = SigilChain(signer)
195
+ chain.append({"step": 1, "data": "first"})
196
+ chain.append({"step": 2, "data": "second"})
197
+ chain.append({"step": 3, "data": "third"})
198
+
199
+ valid, msg = chain.verify_chain()
200
+ print(f"Chain: {len(chain.chain)} entries, valid={valid}, msg={msg}")