(string $path)
| 218 | } |
| 219 | |
| 220 | public static function encryptFileInPlace(string $path): void |
| 221 | { |
| 222 | if (!self::isAvailable()) { |
| 223 | throw new \RuntimeException('libsodium secretstream is not available on this PHP build.'); |
| 224 | } |
| 225 | if (!is_file($path)) { |
| 226 | throw new \RuntimeException('File not found for encryption.'); |
| 227 | } |
| 228 | if (self::isEncryptedFile($path)) { |
| 229 | return; |
| 230 | } |
| 231 | |
| 232 | $key = self::requireMasterKey(); |
| 233 | |
| 234 | $dir = dirname($path); |
| 235 | $base = basename($path); |
| 236 | $tmp = $dir . DIRECTORY_SEPARATOR . '.' . $base . '.frtmp.' . bin2hex(random_bytes(6)); |
| 237 | |
| 238 | $in = @fopen($path, 'rb'); |
| 239 | if ($in === false) { |
| 240 | throw new \RuntimeException('Unable to open file for encryption.'); |
| 241 | } |
| 242 | |
| 243 | $out = @fopen($tmp, 'wb'); |
| 244 | if ($out === false) { |
| 245 | @fclose($in); |
| 246 | throw new \RuntimeException('Unable to open temp file for encryption.'); |
| 247 | } |
| 248 | |
| 249 | try { |
| 250 | $plainSize = @filesize($path); |
| 251 | if (!is_int($plainSize) || $plainSize < 0) { |
| 252 | $plainSize = 0; |
| 253 | } |
| 254 | |
| 255 | $init = sodium_crypto_secretstream_xchacha20poly1305_init_push($key); |
| 256 | if (!is_array($init) || count($init) < 2) { |
| 257 | throw new \RuntimeException('Failed to initialize secretstream (push).'); |
| 258 | } |
| 259 | $state = $init[0]; |
| 260 | $header = $init[1]; |
| 261 | |
| 262 | // Write header |
| 263 | fwrite($out, self::MAGIC); |
| 264 | fwrite($out, chr(self::VERSION)); |
| 265 | fwrite($out, chr(self::FLAGS)); |
| 266 | fwrite($out, self::packU64BE((int)$plainSize)); |
| 267 | fwrite($out, $header); |
| 268 | |
| 269 | while (!feof($in)) { |
| 270 | $chunk = fread($in, self::CHUNK_SIZE); |
| 271 | if ($chunk === '' || $chunk === false) { |
| 272 | break; |
| 273 | } |
| 274 | $cipher = sodium_crypto_secretstream_xchacha20poly1305_push( |
| 275 | $state, |
| 276 | $chunk, |
| 277 | '', |
no test coverage detected