/****************************************************************************** __ ____ __ / / ___ ____ _/ __/_____________ _/ /__ / / / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \ / /___/ __/ /_/ / __(__ ) /__/ /_/ / / __/ /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/ (C)opyright 2025, Leafscale, LLC - https://www.leafscale.com Project: Zygaena Filename: signing.reef Authors: Chris Tusa License: Description: Ed25519 signing and verification for packages and repositories ******************************************************************************/ module core.signing import core.str import io.file import io.dir import io.path import sys.process export type KeyPair type SignResult type VerifyResult fn generate_keypair(name: string): SignResult fn sign_file(file_path: string, key_path: string): SignResult fn verify_file(file_path: string, sig_path: string, key_path: string): VerifyResult fn import_key(key_data: string, name: string): SignResult fn list_keys(keys: [KeyPair], max_count: int): int fn get_keyring_dir(): string fn get_trusted_keys_dir(): string // High-level verification functions fn verify_package(pkg_path: string, sig_path: string): VerifyResult fn verify_manifest(manifest_path: string): VerifyResult fn is_key_trusted(keyid: string): bool end export // Key pair information type KeyPair = struct name: string keyid: string public_key: string private_key: string created: string trusted: bool end KeyPair // Result for signing operations type SignResult = struct success: bool signature: string error: string end SignResult // Result for verification type VerifyResult = struct success: bool valid: bool keyid: string error: string end VerifyResult // Get the keyring directory fn get_keyring_dir(): string return "/etc/coral/keys" end get_keyring_dir // Create empty keypair fn new_keypair(): KeyPair return KeyPair{ name: "", keyid: "", public_key: "", private_key: "", created: "", trusted: false } end new_keypair // Create error result fn error_sign_result(msg: string): SignResult return SignResult{ success: false, signature: "", error: msg } end error_sign_result fn error_verify_result(msg: string): VerifyResult return VerifyResult{ success: false, valid: false, keyid: "", error: msg } end error_verify_result // Generate a new Ed25519 keypair // Note: This requires an external tool (openssl or signify) fn generate_keypair(name: string): SignResult let keyring = get_keyring_dir() // Ensure keyring directory exists if not dir.dir_exists(keyring) if not dir.create_dir_all(keyring) return error_sign_result("Failed to create keyring directory") end if end if let private_path = path.join_path(keyring, name + ".key") let public_path = path.join_path(keyring, name + ".pub") // Check if key already exists if file.fileExists(private_path) return error_sign_result("Key already exists: " + name) end if // Generate key using openssl let cmd = "openssl genpkey -algorithm ed25519 -out \"" + private_path + "\" && " + "openssl pkey -in \"" + private_path + "\" -pubout -out \"" + public_path + "\"" let pid = process.process_spawn_shell(cmd) if pid < 0 return error_sign_result("Failed to start key generation") end if let exit_code = process.process_wait(pid) if exit_code != 0 return error_sign_result("Key generation failed") end if return SignResult{ success: true, signature: "", error: "" } end generate_keypair // Sign a file with Ed25519 fn sign_file(file_path: string, key_path: string): SignResult if not file.fileExists(file_path) return error_sign_result("File not found: " + file_path) end if if not file.fileExists(key_path) return error_sign_result("Key not found: " + key_path) end if let sig_path = file_path + ".sig" // Sign using openssl let cmd = "openssl pkeyutl -sign -inkey \"" + key_path + "\" -in \"" + file_path + "\" -out \"" + sig_path + "\"" let pid = process.process_spawn_shell(cmd) if pid < 0 return error_sign_result("Failed to start signing") end if let exit_code = process.process_wait(pid) if exit_code != 0 return error_sign_result("Signing failed") end if return SignResult{ success: true, signature: sig_path, error: "" } end sign_file // Verify a file signature fn verify_file(file_path: string, sig_path: string, key_path: string): VerifyResult if not file.fileExists(file_path) return error_verify_result("File not found: " + file_path) end if if not file.fileExists(sig_path) return error_verify_result("Signature not found: " + sig_path) end if if not file.fileExists(key_path) return error_verify_result("Key not found: " + key_path) end if // Verify using openssl let cmd = "openssl pkeyutl -verify -pubin -inkey \"" + key_path + "\" -in \"" + file_path + "\" -sigfile \"" + sig_path + "\"" let pid = process.process_spawn_shell(cmd) if pid < 0 return error_verify_result("Failed to start verification") end if let exit_code = process.process_wait(pid) if exit_code == 0 return VerifyResult{ success: true, valid: true, keyid: "", error: "" } else return VerifyResult{ success: true, valid: false, keyid: "", error: "Signature verification failed" } end if end verify_file // Import a public key fn import_key(key_data: string, name: string): SignResult let keyring = get_keyring_dir() if not dir.dir_exists(keyring) if not dir.create_dir_all(keyring) return error_sign_result("Failed to create keyring directory") end if end if let key_path = path.join_path(keyring, name + ".pub") if file.fileExists(key_path) return error_sign_result("Key already exists: " + name) end if if not file.writeFile(key_path, key_data) return error_sign_result("Failed to write key file") end if return SignResult{ success: true, signature: "", error: "" } end import_key // List all keys in keyring fn list_keys(keys: [KeyPair], max_count: int): int let keyring = get_keyring_dir() if not dir.dir_exists(keyring) return 0 end if mut entries: [string] = new [string](64) let entry_count = dir.list_dir(keyring, entries, 64) mut count = 0 mut i = 0 while i < entry_count and count < max_count let entry = entries[i] // Look for .pub files if str.ends_with(entry, ".pub") let name_len = str.length(entry) - 4 let name = str.substring(entry, 0, name_len) mut kp = new_keypair() kp.name = name kp.trusted = true // Local keys are trusted // Read public key let pub_path = path.join_path(keyring, entry) kp.public_key = file.readFile(pub_path) // Check for private key let priv_path = path.join_path(keyring, name + ".key") if file.fileExists(priv_path) kp.private_key = "(present)" end if keys[count] = kp count = count + 1 end if i = i + 1 end while return count end list_keys // Get the trusted keys directory fn get_trusted_keys_dir(): string return "/var/lib/coral/trusted-keys" end get_trusted_keys_dir // Check if a key is in the trusted keys list fn is_key_trusted(keyid: string): bool let trusted_dir = get_trusted_keys_dir() if not dir.dir_exists(trusted_dir) return false end if // Look for key file by keyid let key_path = path.join_path(trusted_dir, keyid + ".pub") if file.fileExists(key_path) return true end if // Also check the main keyring let keyring_path = path.join_path(get_keyring_dir(), keyid + ".pub") return file.fileExists(keyring_path) end is_key_trusted // Get path to a trusted key by keyid fn get_trusted_key_path(keyid: string): string // Try trusted-keys directory first let trusted_dir = get_trusted_keys_dir() let trusted_path = path.join_path(trusted_dir, keyid + ".pub") if file.fileExists(trusted_path) return trusted_path end if // Fall back to main keyring let keyring_path = path.join_path(get_keyring_dir(), keyid + ".pub") if file.fileExists(keyring_path) return keyring_path end if return "" end get_trusted_key_path // Verify a package with any trusted key fn verify_package(pkg_path: string, sig_path: string): VerifyResult if not file.fileExists(pkg_path) return error_verify_result("Package not found: " + pkg_path) end if if not file.fileExists(sig_path) return error_verify_result("Signature not found: " + sig_path) end if // Try to verify with each trusted key let trusted_dir = get_trusted_keys_dir() let keyring_dir = get_keyring_dir() // Collect all public keys from both directories mut all_keys: [string] = new [string](64) mut key_count = 0 // Get keys from trusted-keys directory if dir.dir_exists(trusted_dir) mut entries: [string] = new [string](32) let entry_count = dir.list_dir(trusted_dir, entries, 32) mut i = 0 while i < entry_count and key_count < 64 if str.ends_with(entries[i], ".pub") all_keys[key_count] = path.join_path(trusted_dir, entries[i]) key_count = key_count + 1 end if i = i + 1 end while end if // Get keys from keyring directory if dir.dir_exists(keyring_dir) mut entries: [string] = new [string](32) let entry_count = dir.list_dir(keyring_dir, entries, 32) mut i = 0 while i < entry_count and key_count < 64 if str.ends_with(entries[i], ".pub") let key_path = path.join_path(keyring_dir, entries[i]) // Avoid duplicates mut is_dup = false mut j = 0 while j < key_count if str.equals(all_keys[j], key_path) is_dup = true end if j = j + 1 end while if not is_dup all_keys[key_count] = key_path key_count = key_count + 1 end if end if i = i + 1 end while end if if key_count == 0 return error_verify_result("No trusted keys found") end if // Try each key mut i = 0 while i < key_count let key_path = all_keys[i] let result = verify_file(pkg_path, sig_path, key_path) if result.success and result.valid // Extract keyid from path let filename = path.basename(key_path) let keyid = str.substring(filename, 0, str.length(filename) - 4) return VerifyResult{ success: true, valid: true, keyid: keyid, error: "" } end if i = i + 1 end while return VerifyResult{ success: true, valid: false, keyid: "", error: "Signature does not match any trusted key" } end verify_package // Verify a repository manifest (repo.toml and repo.toml.sig) fn verify_manifest(manifest_path: string): VerifyResult if not file.fileExists(manifest_path) return error_verify_result("Manifest not found: " + manifest_path) end if let sig_path = manifest_path + ".sig" if not file.fileExists(sig_path) return error_verify_result("Manifest signature not found: " + sig_path) end if return verify_package(manifest_path, sig_path) end verify_manifest end module