encrypt('cipher-concurrency-test'); exit(0); } function cipherCheck(bool $ok, string $message): void { if (!$ok) { throw new RuntimeException($message); } } $root = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'qywx_cipher_test_' . bin2hex(random_bytes(8)); mkdir($root, 0700); new think\App($root); $processes = []; try { // 多个进程首次启动必须共享同一完整密钥,不能读到空文件或覆盖对方的密钥。 for ($i = 0; $i < 6; $i++) { $pipes = []; $process = proc_open([PHP_BINARY, __FILE__, '--worker', $root], [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes, null, null, ['bypass_shell' => true]); cipherCheck(is_resource($process), 'start isolated cipher worker'); fclose($pipes[0]); $processes[] = [$process, $pipes]; } $cipher = new QywxPromotionCodeCipher(); $encrypted = []; foreach ($processes as [$process, $pipes]) { $value = stream_get_contents($pipes[1]); $error = stream_get_contents($pipes[2]); fclose($pipes[1]); fclose($pipes[2]); cipherCheck(proc_close($process) === 0 && $error === '', 'cipher worker completed without error'); cipherCheck($cipher->decrypt($value) === 'cipher-concurrency-test', 'concurrent processes share one persisted key'); $encrypted[] = $value; } $processes = []; cipherCheck(count(array_unique($encrypted)) === 6, 'fresh nonce for every encryption'); $raw = base64_decode($encrypted[0], true); $raw[15] = chr(ord($raw[15]) ^ 1); $rejected = false; try { $cipher->decrypt(base64_encode($raw)); } catch (RuntimeException) { $rejected = true; } cipherCheck($rejected, 'tampered authentication tag rejected'); $rejected = false; try { (new QywxPromotionCodeCipher(str_repeat('wrong-key', 8)))->decrypt($encrypted[0]); } catch (RuntimeException) { $rejected = true; } cipherCheck($rejected, 'wrong key cannot decrypt'); file_put_contents($root . '/runtime/qywx_promotion_private/welcome.key', 'incomplete-key'); $rejected = false; try { (new QywxPromotionCodeCipher())->encrypt('test'); } catch (RuntimeException) { $rejected = true; } cipherCheck($rejected, 'damaged persisted key fails closed instead of silently rotating'); } finally { foreach ($processes as [$process, $pipes]) { foreach ($pipes as $pipe) { if (is_resource($pipe)) { fclose($pipe); } } if (is_resource($process)) { proc_close($process); } } $keyPath = $root . '/runtime/qywx_promotion_private/welcome.key'; if (is_file($keyPath)) { unlink($keyPath); } if (is_dir(dirname($keyPath))) { rmdir(dirname($keyPath)); } if (is_dir($root . '/runtime')) { rmdir($root . '/runtime'); } rmdir($root); } echo "QYWX_PROMOTION_CODE_CIPHER_OK\n";