input = is_resource($input) ? stream_get_meta_data($input)["uri"] : $input; } /* ---- */ // Execute a shell command from enum of allowed commands private static function exec(ShellCommands $cmd, string $args = ""): string|null { return shell_exec("{$cmd->value} {$args} 2>&1"); } // Parent directory of $output is writeable or throw private static function is_writable_or_throw(string $path): true { if (!is_writable(dirname($path))) { throw new \Exception("Output location '{$path}' is not writeable for current user"); } return true; } // Generate asymmetric key pair private static function keygen(): array { // Generate age key pair $keygen = explode(PHP_EOL, self::exec(ShellCommands::KEYGEN)); // Remove first line of output array_shift($keygen); // Return asymmetric key pair strings as assoc array return [ // Extract public key from second line of age-keygen output "public" => substr($keygen[1], 14), // Return generated key pair "private" => implode(PHP_EOL, $keygen) ]; } /* ---- */ // Enable PEM encoding when encrypting public function armor(): self { $this->armor = true; return $this; } // Decrypt a file using a provided private key string and output file name public function decrypt(string $key_file, string $output): true { $this->is_writable_or_throw($output); // Throw if private key file is not readable if (!is_readable($key_file)) { throw new \Exception("Failed to open private key file '{$key_file}'"); } // Attempt to decrypt file using private key file $decrypt = $this->exec(ShellCommands::AGE, "--decrypt -i {$key_file} -o {$output} {$this->input}"); // Decryption failed if (!is_null($decrypt)) { throw new \Exception("Failed to decrypt file '{$this->input}'"); } return true; } // Encrypt a file and return its private key string public function encrypt(string $output, string|null $output_key = null): string|false { $this->is_writable_or_throw($output); // Generate asymmetric keypair $key = $this->keygen(); // Add --armor string if PEM encoding is enabled $armor = $this->armor ? "--armor" : ""; // Encrypt file to output using age $encrypt = $this->exec(ShellCommands::AGE, "--encrypt -r {$key["public"]} {$armor} -o {$output} {$this->input}"); // Write private key to file if (is_null($encrypt) && $output_key) { $this->is_writable_or_throw($output_key); // Write private key to file file_put_contents($output_key, $key["private"]); // Throw if private key file could not be created if (!is_file($output_key)) { throw new \Exception("Failed to write private key file to '{$output_key}'"); } } // Return private key if $encrypt returns null return is_null($encrypt) ? $key["private"] : false; } }