/** * Ed25519 key management for Trust Ledger entry signing. * * Binary-compatible with the Python signing.py implementation. * * Key files: * .forgeos/keys/private.pem (mode 0o600 — owner read/write only) * .forgeos/keys/public.pem (world-readable) * * Key format: PKCS8 PEM for private, SPKI PEM for public — matching * Python's serialization.PrivateFormat.PKCS8 and * serialization.PublicFormat.SubjectPublicKeyInfo. * * Fingerprint: first 16 hex chars of SHA-256(raw_32_byte_public_key). * This matches Python's get_fingerprint() which calls: * public_key.public_bytes(Encoding.Raw, PublicFormat.Raw) → 32 bytes * SHA-256(raw_bytes).hexdigest()[:16] * * Signatures: Ed25519 raw signatures, base64-encoded (standard alphabet), * produced over the canonical UTF-8 bytes of the entry dict. This matches * Python's base64.b64encode(raw_sig).decode("ascii"). * * Security invariants (matching Python): * - load_private_key raises if file mode is broader than 0o600 * - Key rotation archives old_public.pem before overwriting */ import { type KeyObject } from 'crypto'; export declare class SigningError extends Error { constructor(message: string); } export declare class KeyNotFoundError extends SigningError { constructor(message: string); } export declare class KeyPermissionError extends SigningError { constructor(message: string); } /** * Manages Ed25519 keypairs for a ForgeOS project. * * All key files are stored under {projectPath}/.forgeos/keys/. * * Example: * const km = new KeyManager('/my/project'); * km.generateKeypair(); * const sig = km.signEntry({ seq: 0, ... }); * const ok = km.verifySignature({ seq: 0, ... }, sig); */ export declare class KeyManager { private keysDir; constructor(projectPath: string); get privateKeyPath(): string; get publicKeyPath(): string; /** * Generate a new Ed25519 keypair and persist it to disk. * * Creates the key directory (mode 0o700) if it does not exist. * Overwrites any existing keypair — rotate deliberately via rotateKey(). * * @returns The key fingerprint (first 16 hex chars of SHA-256 of raw public key bytes). */ generateKeypair(): string; /** * Rotate the keypair, returning [oldFingerprint, newFingerprint]. * * The old public key is preserved at old_public.pem during the transition * so callers can construct a capability_declared ledger entry referencing * both keys. After the transition entry is written, the caller may delete * old_public.pem. * * @returns Tuple of [oldFingerprint, newFingerprint]. * @throws KeyNotFoundError if no existing keypair is found. */ rotateKey(): [string, string]; /** * Load the private key from disk, enforcing strict file permissions. * * @throws KeyNotFoundError if the private key file does not exist. * @throws KeyPermissionError if the file mode is broader than 0o600. */ loadPrivateKey(): KeyObject; /** * Load the public key from disk. * * @throws KeyNotFoundError if the public key file does not exist. */ loadPublicKey(): KeyObject; /** * Return the raw 32-byte Ed25519 public key material. * * This replicates Python's: * public_key.public_bytes(Encoding.Raw, PublicFormat.Raw) * which returns the 32-byte raw Ed25519 public key (not PEM, not DER SPKI). */ getPublicKeyBytes(): Buffer; /** * Return the key fingerprint as 16 hex characters. * * Fingerprint = first 16 hex chars of SHA-256(raw_public_key_bytes). * * This matches Python's: * raw_bytes = public_key.public_bytes(Encoding.Raw, PublicFormat.Raw) * digest = hashlib.sha256(raw_bytes).hexdigest() * return digest[:16] */ getFingerprint(): string; /** * Sign an entry dict with the project's private key. * * The entry is serialized to canonical JSON (sorted keys, UTF-8) before * signing, matching Python's: * entry_bytes = json.dumps(entry, sort_keys=True, ensure_ascii=False).encode("utf-8") * raw_sig = private_key.sign(entry_bytes) * return base64.b64encode(raw_sig).decode("ascii") * * @param entry - The entry dict to sign (before signature field is set). * @returns Base64-encoded (standard alphabet) signature string. */ signEntry(entry: Record): string; /** * Verify an Ed25519 signature against an entry dict. * * Returns false on any verification failure — never throws. * * Matches Python's verify_signature which catches all exceptions and * returns False rather than propagating. * * @param entry - The entry dict that was signed. * @param signature - Base64-encoded signature string. * @returns true if the signature is valid, false otherwise. */ verifySignature(entry: Record, signature: string): boolean; private assertPrivateKeyPermissions; } //# sourceMappingURL=signing.d.ts.map