build(agent): melter#14fd4b iteration
This commit is contained in:
parent
9ea6c06bdd
commit
a8cef41ff4
|
|
@ -63,6 +63,7 @@ class Attestation:
|
|||
timestamp: float
|
||||
contract_id: str
|
||||
version: int
|
||||
signature: Optional[str] = None
|
||||
|
||||
def to_json(self) -> str:
|
||||
return to_json(asdict(self))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
"""Lightweight Ed25519 signing helpers using PyNaCl.
|
||||
|
||||
Provides simple key generation, signing and verification utilities used by
|
||||
the Attestation flow in ExProve. We keep this intentionally small to make
|
||||
it easy to replace with HSM/DID integrations later.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Tuple
|
||||
import hashlib
|
||||
import hmac
|
||||
import secrets
|
||||
|
||||
try:
|
||||
# Prefer PyNaCl if available for real Ed25519 signatures
|
||||
from nacl.signing import SigningKey, VerifyKey
|
||||
from nacl.encoding import HexEncoder
|
||||
|
||||
def generate_signing_key() -> SigningKey:
|
||||
"""Generate a new Ed25519 signing key."""
|
||||
return SigningKey.generate()
|
||||
|
||||
|
||||
def signing_key_to_hex(sk: SigningKey) -> str:
|
||||
return sk.encode(encoder=HexEncoder).decode("utf-8")
|
||||
|
||||
|
||||
def verify_key_hex_from_signing_key(sk: SigningKey) -> str:
|
||||
vk = sk.verify_key
|
||||
return vk.encode(encoder=HexEncoder).decode("utf-8")
|
||||
|
||||
|
||||
def sign_message_hex(sk: SigningKey, message: bytes) -> str:
|
||||
"""Sign a message and return signature as hex string."""
|
||||
sig = sk.sign(message).signature
|
||||
return sig.hex()
|
||||
|
||||
|
||||
def verify_signature_hex(verify_key_hex: str, message: bytes, sig_hex: str) -> bool:
|
||||
"""Verify signature hex against message. Returns True if valid."""
|
||||
vk = VerifyKey(verify_key_hex, encoder=HexEncoder)
|
||||
try:
|
||||
vk.verify(message, bytes.fromhex(sig_hex))
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
except Exception:
|
||||
# Fallback: HMAC-SHA256-based signing for environments without PyNaCl.
|
||||
# This is NOT cryptographically equivalent to Ed25519 and is only a
|
||||
# pragmatic fallback for tests and lightweight local usage.
|
||||
|
||||
class _FallbackKey:
|
||||
def __init__(self, key: bytes):
|
||||
self._key = key
|
||||
|
||||
def raw(self) -> bytes:
|
||||
return self._key
|
||||
|
||||
|
||||
def generate_signing_key() -> _FallbackKey:
|
||||
return _FallbackKey(secrets.token_bytes(32))
|
||||
|
||||
|
||||
def signing_key_to_hex(sk: _FallbackKey) -> str:
|
||||
return sk.raw().hex()
|
||||
|
||||
|
||||
def verify_key_hex_from_signing_key(sk: _FallbackKey) -> str:
|
||||
return signing_key_to_hex(sk)
|
||||
|
||||
|
||||
def sign_message_hex(sk: _FallbackKey, message: bytes) -> str:
|
||||
sig = hmac.new(sk.raw(), message, hashlib.sha256).digest()
|
||||
return sig.hex()
|
||||
|
||||
|
||||
def verify_signature_hex(verify_key_hex: str, message: bytes, sig_hex: str) -> bool:
|
||||
key = bytes.fromhex(verify_key_hex)
|
||||
expected = hmac.new(key, message, hashlib.sha256).digest()
|
||||
return hmac.compare_digest(expected, bytes.fromhex(sig_hex))
|
||||
|
|
@ -13,6 +13,7 @@ authors = [ { name = "ExProve Team" } ]
|
|||
dependencies = [
|
||||
"dataclasses; python_version < '3.7'",
|
||||
"typing; python_version < '3.8'",
|
||||
"pynacl>=1.5.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
import json
|
||||
|
||||
from exprove.contracts import LocalExecutionTask, SharedMarketContext, PlanDelta, Attestation
|
||||
from exprove.contracts import compute_delta
|
||||
from exprove.crypto import generate_signing_key, verify_key_hex_from_signing_key, sign_message_hex, verify_signature_hex
|
||||
|
||||
|
||||
def test_sign_and_verify_plan_delta():
|
||||
task = LocalExecutionTask(task_id="t-sig", instrument="AAPL", venue="V1", objective="VWAP", constraints={})
|
||||
ctx = SharedMarketContext(signals={"depth": 5}, version=1, contract_id="c-sig")
|
||||
|
||||
delta = compute_delta(task, ctx)
|
||||
# canonical payload for signing
|
||||
payload = json.dumps(delta.to_dict(), sort_keys=True).encode("utf-8")
|
||||
|
||||
sk = generate_signing_key()
|
||||
vk_hex = verify_key_hex_from_signing_key(sk)
|
||||
sig_hex = sign_message_hex(sk, payload)
|
||||
|
||||
# attach signature to attestation and verify
|
||||
a = Attestation(entry=delta.to_json(), signer=vk_hex, timestamp=0.0, contract_id=ctx.contract_id, version=1, signature=sig_hex)
|
||||
|
||||
assert verify_signature_hex(vk_hex, payload, a.signature) is True
|
||||
Loading…
Reference in New Issue