mode === 'bad-finished' && $this->finishedCalls++ === 0 ? ~$f : $f; } protected function finished12(string $master, string $label): string { $f = parent::finished12($master, $label); return $this->mode === 'bad-finished' && $label === 'server finished' ? ~$f : $f; } protected function signTranscript(Rsa|Ec $key, int $scheme, string $context): string { $sig = parent::signTranscript($key, $scheme, $context); return $this->mode === 'bad-cv' ? $key->sign('別のメッセージ', 'sha256') : $sig; } protected function signKeyExchange(Rsa|Ec $key, int $scheme, string $data): string { $sig = parent::signKeyExchange($key, $scheme, $data); return $this->mode === 'bad-cv' ? $key->sign('別のメッセージ', 'sha256') : $sig; } protected function parseClientHello(string $b): array { $ch = parent::parseClientHello($b); if ($this->mode === 'downgrade') $ch['versions'] = array_values(array_diff($ch['versions'], [0x0304])); return $ch; } /** TLS 1.2の再ネゴシエーションを求める */ public function helloRequest(): void { $this->writeRecord(22, "\x00\x00\x00\x00"); } } $port = (int)($argv[1] ?? 0); $opt = ['cert' => $argv[2] ?? '', 'key' => $argv[3] ?? '']; if (!empty($argv[4])) $opt['alpn'] = explode(',', $argv[4]); if (!empty($argv[5])) $opt['groups'] = explode(',', $argv[5]); $evil = $argv[6] ?? ''; if (!empty($argv[7])) $opt['max_version'] = $argv[7]; $server = @stream_socket_server("tcp://127.0.0.1:{$port}", $errno, $errstr); if (!$server) { fwrite(STDOUT, "error {$errstr}\n"); exit(1); } fwrite(STDOUT, "ready\n"); fflush(STDOUT); while (true) { $c = @stream_socket_accept($server, 120); if (!$c) break; stream_set_timeout($c, 10); // MockServer::stop()の終了のリクエスト(暗号化されていない) $peek = stream_socket_recvfrom($c, 4, STREAM_PEEK); if ($peek === 'QUIT') break; try { $conn = new EvilConnection($c, $opt, $evil); $conn->handshake(); $s = Stream::wrap($conn); } catch (\Throwable $e) { fwrite(STDERR, $e->getMessage()."\n"); @fclose($c); continue; } while (($line = fgets($s)) !== false) { $cmd = rtrim($line, "\r\n"); if ($cmd === 'INFO') { $i = Tls::info($s); unset($i['peer_certificates']); fwrite($s, json_encode($i)."\n"); } else if ($cmd === 'KEYUPDATE') { Stream::connection($s)->keyUpdate(true); fwrite($s, "updated\n"); } else if (str_starts_with($cmd, 'BIG ')) { fwrite($s, str_repeat('x', (int)substr($cmd, 4))); } else if ($cmd === 'CLOSE') { break; } else if ($cmd === 'HELLOREQ') { $conn->helloRequest(); fwrite($s, "asked\n"); } else if ($cmd === 'GARBAGE') { fwrite(Stream::connection($s)->socket(), "\x17\x03\x03\x00\x20".random_bytes(32)); } else if ($cmd === 'DROP') { fclose(Stream::connection($s)->socket()); continue 2; } else { fwrite($s, $line); } } fclose($s); }