[trusted-public-key-base64]\n"); exit(2); } if (! function_exists('sodium_crypto_sign_verify_detached')) { fwrite(STDERR, "FAIL ext-sodium is required.\n"); exit(2); } $file = $argv[1]; if (! is_file($file)) { fwrite(STDERR, "FAIL Bundle not found: {$file}\n"); exit(2); } try { $bundle = json_decode(file_get_contents($file), true, flags: JSON_THROW_ON_ERROR); } catch (Throwable $e) { fwrite(STDERR, 'FAIL Invalid JSON: '.$e->getMessage()."\n"); exit(2); } $document = $bundle['evidence_packet'] ?? null; $payload = is_array($document) ? ($document['signed_payload'] ?? null) : null; $signature = is_array($document) ? ($document['signature'] ?? null) : null; $metadata = $bundle['packet_metadata'] ?? []; $artifacts = $bundle['artifacts'] ?? []; $results = []; $check = function (string $label, bool $ok) use (&$results): void { $results[$label] = $ok; echo str_pad($ok ? 'PASS' : 'FAIL', 6).$label.PHP_EOL; }; if (! is_array($payload) || ! is_array($signature)) { $check('Packet structure', false); exit(1); } $check('Packet structure', true); $canonicalPayload = CanonicalJson::encode($payload); $payloadHash = hash('sha256', $canonicalPayload); $check('Payload SHA-256', hash_equals((string) ($metadata['payload_hash'] ?? ''), $payloadHash)); $public = base64_decode((string) ($signature['public_key_base64'] ?? ''), true); $sig = base64_decode((string) ($signature['value_base64'] ?? ''), true); $signatureValid = false; if ($public !== false && $sig !== false) { try { $signatureValid = sodium_crypto_sign_verify_detached($sig, $canonicalPayload, $public); } catch (SodiumException) { $signatureValid = false; } } $check('Ed25519 signature', $signatureValid); $identity = $payload['signing_identity'] ?? []; $keyFingerprintValid = $public !== false && (($identity['algorithm'] ?? null) === 'Ed25519') && (($identity['key_id'] ?? null) === ($signature['key_id'] ?? null)) && hash_equals((string) ($identity['public_key_sha256'] ?? ''), hash('sha256', $public)); $check('Signing identity fingerprint', $keyFingerprintValid); $artifactMap = [ 'input' => 'input_sha256', 'parameters' => 'parameters_sha256', 'output' => 'output_sha256', ]; $artifactSetValid = count(array_intersect(array_keys($artifactMap), array_keys($artifacts))) === 3; $check('Artifact set', $artifactSetValid); $artifactHashesValid = true; foreach ($artifactMap as $type => $signedKey) { if (! isset($artifacts[$type])) { $artifactHashesValid = false; continue; } $contentJson = $artifacts[$type]['content_json'] ?? null; if (! is_string($contentJson)) { $artifactHashesValid = false; continue; } try { json_decode($contentJson, flags: JSON_THROW_ON_ERROR); } catch (Throwable) { $artifactHashesValid = false; continue; } $actual = hash('sha256', $contentJson); $declared = (string) ($artifacts[$type]['sha256'] ?? ''); $signedHash = (string) (($payload['artifacts'][$signedKey] ?? '')); $valid = hash_equals($declared, $actual) && hash_equals($signedHash, $actual); $artifactHashesValid = $artifactHashesValid && $valid; } $check('Artifact hashes & signed binding', $artifactHashesValid); $trustedValid = true; if (isset($argv[2]) && trim($argv[2]) !== '') { $trusted = base64_decode(trim($argv[2]), true); $trustedValid = $trusted !== false && $public !== false && hash_equals($trusted, $public); $check('Externally trusted public key', $trustedValid); } $verified = ! in_array(false, $results, true) && $trustedValid; echo PHP_EOL; if (! $verified) { fwrite(STDERR, "LQEP portable verification FAILED.\n"); exit(1); } echo "LQEP portable verification PASSED.\n";