{ "cells": [ { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "# When To Stop Fuzzing\n", "\n", "In the past chapters, we have discussed several fuzzing techniques. Knowing _what_ to do is important, but it is also important to know when to _stop_ doing things. In this chapter, we will learn when to _stop fuzzing_ – and use a prominent example for this purpose: The *Enigma* machine that was used in the second world war by the navy of Nazi Germany to encrypt communications, and how Alan Turing and I.J. Good used _fuzzing techniques_ to crack ciphers for the Naval Enigma machine." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "Turing did not only develop the foundations of computer science, the Turing machine. Together with his assistant I.J. Good, he also invented estimators of the probability of an event occuring that has never previously occured. We show how the Good-Turing estimator can be used to quantify the *residual risk* of a fuzzing campaign that finds no vulnerabilities. Meaning, we show how it estimates the probability of discovering a vulnerability when no vulnerability has been observed before throughout the fuzzing campaign.\n", "\n", "We discuss means to speed up [coverage-based fuzzers](Coverage.ipynb) and introduce a range of estimation and extrapolation methodologies to assess and extrapolate fuzzing progress and residual risk.\n", "\n", "**Prerequisites**\n", "\n", "* _The chapter on [Coverage](Coverage.ipynb) discusses how to use coverage information for an executed test input to guide a coverage-based mutational greybox fuzzer_.\n", "* Some knowledge of statistics is helpful." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:09.715333Z", "iopub.status.busy": "2022-01-11T09:32:09.714713Z", "iopub.status.idle": "2022-01-11T09:32:09.817457Z", "shell.execute_reply": "2022-01-11T09:32:09.817842Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import bookutils" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:09.821344Z", "iopub.status.busy": "2022-01-11T09:32:09.820795Z", "iopub.status.idle": "2022-01-11T09:32:09.822564Z", "shell.execute_reply": "2022-01-11T09:32:09.822978Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from typing import Dict" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:09.825936Z", "iopub.status.busy": "2022-01-11T09:32:09.825345Z", "iopub.status.idle": "2022-01-11T09:32:10.731957Z", "shell.execute_reply": "2022-01-11T09:32:10.732426Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import Fuzzer" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:10.736538Z", "iopub.status.busy": "2022-01-11T09:32:10.735791Z", "iopub.status.idle": "2022-01-11T09:32:11.435263Z", "shell.execute_reply": "2022-01-11T09:32:11.435820Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Coverage import Coverage, cgi_decode" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## The Enigma Machine\n", "\n", "It is autumn in the year of 1938. Turing has just finished his PhD at Princeton University demonstrating the limits of computation and laying the foundation for the theory of computer science. Nazi Germany is rearming. It has reoccupied the Rhineland and annexed Austria against the treaty of Versailles. It has just annexed the Sudetenland in Czechoslovakia and begins preparations to take over the rest of Czechoslovakia despite an agreement just signed in Munich.\n", "\n", "Meanwhile, the British intelligence is building up their capability to break encrypted messages used by the Germans to communicate military and naval information. The Germans are using [Enigma machines](https://en.wikipedia.org/wiki/Enigma_machine) for encryption. Enigma machines use a series of electro-mechanical rotor cipher machines to protect military communication. Here is a picture of an Enigma machine:" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "![Enigma Machine](PICS/Bletchley_Park_Naval_Enigma_IMG_3604.JPG)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "By the time Turing joined the British Bletchley park, the Polish intelligence reverse engineered the logical structure of the Enigma machine and built a decryption machine called *Bomba* (perhaps because of the ticking noise they made). A bomba simulates six Enigma machines simultaneously and tries different decryption keys until the code is broken. The Polish bomba might have been the very _first fuzzer_.\n", "\n", "Turing took it upon himself to crack ciphers of the Naval Enigma machine, which were notoriously hard to crack. The Naval Enigma used, as part of its encryption key, a three letter sequence called *trigram*. These trigrams were selected from a book, called *Kenngruppenbuch*, which contained all trigrams in a random order." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "### The Kenngruppenbuch\n", "\n", "Let's start with the Kenngruppenbuch (K-Book).\n", "\n", "We are going to use the following Python functions.\n", "* `random.shuffle(elements)` - shuffle *elements* and put items in random order.\n", "* `random.choices(elements, weights)` - choose an item from *elements* at random. An element with twice the *weight* is twice as likely to be chosen.\n", "* `log(a)` - returns the natural logarithm of a.\n", "* `a ** b` - means `a` to the power of `b` (a.k.a. [power operator](https://docs.python.org/3/reference/expressions.html#the-power-operator))" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.439857Z", "iopub.status.busy": "2022-01-11T09:32:11.439223Z", "iopub.status.idle": "2022-01-11T09:32:11.442054Z", "shell.execute_reply": "2022-01-11T09:32:11.441582Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import string" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.447664Z", "iopub.status.busy": "2022-01-11T09:32:11.446833Z", "iopub.status.idle": "2022-01-11T09:32:11.449056Z", "shell.execute_reply": "2022-01-11T09:32:11.449596Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import numpy\n", "from numpy import log" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.453841Z", "iopub.status.busy": "2022-01-11T09:32:11.453094Z", "iopub.status.idle": "2022-01-11T09:32:11.455480Z", "shell.execute_reply": "2022-01-11T09:32:11.455918Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import random" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We start with creating the set of trigrams:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.466334Z", "iopub.status.busy": "2022-01-11T09:32:11.465492Z", "iopub.status.idle": "2022-01-11T09:32:11.467330Z", "shell.execute_reply": "2022-01-11T09:32:11.467848Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "letters = list(string.ascii_letters[26:]) # upper-case characters\n", "trigrams = [str(a + b + c) for a in letters for b in letters for c in letters]" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.482596Z", "iopub.status.busy": "2022-01-11T09:32:11.481884Z", "iopub.status.idle": "2022-01-11T09:32:11.484322Z", "shell.execute_reply": "2022-01-11T09:32:11.483749Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "random.shuffle(trigrams)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.490357Z", "iopub.status.busy": "2022-01-11T09:32:11.489708Z", "iopub.status.idle": "2022-01-11T09:32:11.496627Z", "shell.execute_reply": "2022-01-11T09:32:11.497184Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "['TJK', 'NWV', 'LBM', 'AZC', 'GZP', 'ADE', 'DNO', 'OQL', 'FGK', 'IPT']" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trigrams[:10]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "These now go into the Kenngruppenbuch. However, it was observed that some trigrams were more likely chosen than others. For instance, trigrams at the top-left corner of any page, or trigrams on the first or last few pages were more likely than one somewhere in the middle of the book or page. We reflect this difference in distribution by assigning a _probability_ to each trigram, using Benford's law as introduced in [Probabilistic Fuzzing](ProbabilisticGrammarFuzzer.ipynb)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Recall, that Benford's law assigns the $i$-th digit the probability $\\log_{10}\\left(1 + \\frac{1}{i}\\right)$ where the base 10 is chosen because there are 10 digits $i\\in [0,9]$. However, Benford's law works for an arbitrary number of \"digits\". Hence, we assign the $i$-th trigram the probability $\\log_b\\left(1 + \\frac{1}{i}\\right)$ where the base $b$ is the number of all possible trigrams $b=26^3$. " ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.543818Z", "iopub.status.busy": "2022-01-11T09:32:11.539199Z", "iopub.status.idle": "2022-01-11T09:32:11.547032Z", "shell.execute_reply": "2022-01-11T09:32:11.546343Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "k_book = {} # Kenngruppenbuch\n", "\n", "for i in range(1, len(trigrams) + 1):\n", " trigram = trigrams[i - 1]\n", " # choose weights according to Benford's law\n", " k_book[trigram] = log(1 + 1 / i) / log(26**3 + 1)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Here's a random trigram from the Kenngruppenbuch:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.554569Z", "iopub.status.busy": "2022-01-11T09:32:11.553794Z", "iopub.status.idle": "2022-01-11T09:32:11.556728Z", "shell.execute_reply": "2022-01-11T09:32:11.557110Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'PSK'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "random_trigram = random.choices(list(k_book.keys()), weights=list(k_book.values()))[0]\n", "random_trigram" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "And this is its probability:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.562490Z", "iopub.status.busy": "2022-01-11T09:32:11.561538Z", "iopub.status.idle": "2022-01-11T09:32:11.565235Z", "shell.execute_reply": "2022-01-11T09:32:11.565852Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.0008284144853894445" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "k_book[random_trigram]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Fuzzing the Enigma\n", "\n", "In the following, we introduce an extremely simplified implementation of the Naval Enigma based on the trigrams from the K-book. Of course, the encryption mechanism of the actual Enigma machine is much more sophisticated and worthy of a much more detailed investigation. We encourage the interested reader to follow up with further reading listed in the Background section.\n", "\n", "The personell at Bletchley Park can only check whether an encoded message is encoded with a (guessed) trigram.\n", "Our implementation `naval_enigma()` takes a `message` and a `key` (i.e., the guessed trigram). If the given key matches the (previously computed) key for the message, `naval_enigma()` returns `True`." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.569843Z", "iopub.status.busy": "2022-01-11T09:32:11.569111Z", "iopub.status.idle": "2022-01-11T09:32:11.571363Z", "shell.execute_reply": "2022-01-11T09:32:11.571805Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Fuzzer import RandomFuzzer\n", "from Fuzzer import Runner" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.579060Z", "iopub.status.busy": "2022-01-11T09:32:11.578139Z", "iopub.status.idle": "2022-01-11T09:32:11.580266Z", "shell.execute_reply": "2022-01-11T09:32:11.580933Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class EnigmaMachine(Runner):\n", " def __init__(self, k_book):\n", " self.k_book = k_book\n", " self.reset()\n", "\n", " def reset(self):\n", " \"\"\"Resets the key register\"\"\"\n", " self.msg2key = {}\n", " self.cur_msg = \"\"\n", "\n", " def internal_msg2key(self, message):\n", " \"\"\"Internal helper method. \n", " Returns the trigram for an encoded message.\"\"\"\n", " if message not in self.msg2key:\n", " # Simulating how an officer chooses a key from the Kenngruppenbuch\n", " # to encode the message.\n", " self.msg2key[message] = \\\n", " random.choices(list(self.k_book.keys()),\n", " weights=list(self.k_book.values()))[0]\n", " trigram = self.msg2key[message]\n", " return trigram\n", "\n", " def naval_enigma(self, message, key):\n", " \"\"\"Returns true if 'message' is encoded with 'key'\"\"\"\n", " if key == self.internal_msg2key(message):\n", " return True\n", " else:\n", " return False" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "To \"fuzz\" the `naval_enigma()`, our job will be to come up with a key that matches a given (encrypted) message. Since the keys only have three characters, we have a good chance to achieve this in much less than a second. (Of course, longer keys will be much harder to find via random fuzzing.)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.586270Z", "iopub.status.busy": "2022-01-11T09:32:11.585380Z", "iopub.status.idle": "2022-01-11T09:32:11.587567Z", "shell.execute_reply": "2022-01-11T09:32:11.587961Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "class EnigmaMachine(EnigmaMachine):\n", " def run(self, tri):\n", " \"\"\"PASS if cur_msg is encoded with trigram tri\"\"\"\n", " if self.naval_enigma(self.cur_msg, tri):\n", " outcome = self.PASS\n", " else:\n", " outcome = self.FAIL\n", "\n", " return (tri, outcome)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Now we can use the `EnigmaMachine` to check whether a certain message is encoded with a certain trigram." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.595687Z", "iopub.status.busy": "2022-01-11T09:32:11.594645Z", "iopub.status.idle": "2022-01-11T09:32:11.598273Z", "shell.execute_reply": "2022-01-11T09:32:11.598684Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "('AAA', 'FAIL')" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "enigma = EnigmaMachine(k_book)\n", "enigma.cur_msg = \"BrEaK mE. L0Lzz\"\n", "enigma.run(\"AAA\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The simplest way to crack an encoded message is by brute forcing. Suppose, at Bletchley park they would try random trigrams until a message is broken." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.604237Z", "iopub.status.busy": "2022-01-11T09:32:11.603551Z", "iopub.status.idle": "2022-01-11T09:32:11.605996Z", "shell.execute_reply": "2022-01-11T09:32:11.605532Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class BletchleyPark:\n", " def __init__(self, enigma):\n", " self.enigma = enigma\n", " self.enigma.reset()\n", " self.enigma_fuzzer = RandomFuzzer(\n", " min_length=3,\n", " max_length=3,\n", " char_start=65,\n", " char_range=26)\n", " \n", " def break_message(self, message):\n", " \"\"\"Returning the trigram for an encoded message\"\"\"\n", " self.enigma.cur_msg = message\n", " while True:\n", " (trigram, outcome) = self.enigma_fuzzer.run(self.enigma)\n", " if outcome == self.enigma.PASS:\n", " break\n", " return trigram" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "How long does it take Bletchley park to find the key using this brute forcing approach?" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.610225Z", "iopub.status.busy": "2022-01-11T09:32:11.609319Z", "iopub.status.idle": "2022-01-11T09:32:11.612231Z", "shell.execute_reply": "2022-01-11T09:32:11.612767Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Timer import Timer" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.670768Z", "iopub.status.busy": "2022-01-11T09:32:11.632073Z", "iopub.status.idle": "2022-01-11T09:32:11.911840Z", "shell.execute_reply": "2022-01-11T09:32:11.912226Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "enigma = EnigmaMachine(k_book)\n", "bletchley = BletchleyPark(enigma)\n", "\n", "with Timer() as t:\n", " trigram = bletchley.break_message(\"BrEaK mE. L0Lzz\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Here's the key for the current message:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.915755Z", "iopub.status.busy": "2022-01-11T09:32:11.915190Z", "iopub.status.idle": "2022-01-11T09:32:11.917832Z", "shell.execute_reply": "2022-01-11T09:32:11.918214Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'XQC'" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trigram" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "And no, this did not take long:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.921979Z", "iopub.status.busy": "2022-01-11T09:32:11.921326Z", "iopub.status.idle": "2022-01-11T09:32:11.923711Z", "shell.execute_reply": "2022-01-11T09:32:11.924106Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0.293525 seconds'" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'%f seconds' % t.elapsed_time()" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.927589Z", "iopub.status.busy": "2022-01-11T09:32:11.927027Z", "iopub.status.idle": "2022-01-11T09:32:11.929521Z", "shell.execute_reply": "2022-01-11T09:32:11.929924Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'Bletchley cracks about 3 messages per second'" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Bletchley cracks about %d messages per second' % (1/t.elapsed_time())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Turing's Observations\n", "Okay, lets crack a few messages and count the number of times each trigram is observed." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.933291Z", "iopub.status.busy": "2022-01-11T09:32:11.932764Z", "iopub.status.idle": "2022-01-11T09:32:11.934183Z", "shell.execute_reply": "2022-01-11T09:32:11.934621Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from collections import defaultdict" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:11.937489Z", "iopub.status.busy": "2022-01-11T09:32:11.936966Z", "iopub.status.idle": "2022-01-11T09:32:11.939765Z", "shell.execute_reply": "2022-01-11T09:32:11.939064Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "n = 100 # messages to crack" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:12.017190Z", "iopub.status.busy": "2022-01-11T09:32:11.979198Z", "iopub.status.idle": "2022-01-11T09:32:22.060042Z", "shell.execute_reply": "2022-01-11T09:32:22.060428Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "observed: Dict[str, int] = defaultdict(int)\n", "for msg in range(0, n):\n", " trigram = bletchley.break_message(msg)\n", " observed[trigram] += 1\n", "\n", "# list of trigrams that have been observed\n", "counts = [k for k, v in observed.items() if int(v) > 0]\n", "\n", "t_trigrams = len(k_book)\n", "o_trigrams = len(counts)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:22.065629Z", "iopub.status.busy": "2022-01-11T09:32:22.064725Z", "iopub.status.idle": "2022-01-11T09:32:22.067989Z", "shell.execute_reply": "2022-01-11T09:32:22.068408Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'After cracking 100 messages, we observed 72 out of 17576 trigrams.'" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"After cracking %d messages, we observed %d out of %d trigrams.\" % (\n", " n, o_trigrams, t_trigrams)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:22.072924Z", "iopub.status.busy": "2022-01-11T09:32:22.072179Z", "iopub.status.idle": "2022-01-11T09:32:22.074191Z", "shell.execute_reply": "2022-01-11T09:32:22.074798Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "singletons = len([k for k, v in observed.items() if int(v) == 1])" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:22.078850Z", "iopub.status.busy": "2022-01-11T09:32:22.077949Z", "iopub.status.idle": "2022-01-11T09:32:22.081788Z", "shell.execute_reply": "2022-01-11T09:32:22.082254Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'From the 72 observed trigrams, 63 were observed only once.'" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"From the %d observed trigrams, %d were observed only once.\" % (\n", " o_trigrams, singletons)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Given a sample of previously used entries, Turing wanted to _estimate the likelihood_ that the current unknown entry was one that had been previously used, and further, to estimate the probability distribution over the previously used entries. This lead to the development of the estimators of the missing mass and estimates of the true probability mass of the set of items occuring in the sample. Good worked with Turing during the war and, with Turing’s permission, published the analysis of the bias of these estimators in 1953." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Suppose, after finding the keys for n=100 messages, we have observed the trigram \"ABC\" exactly $X_\\text{ABC}=10$ times. What is the probability $p_\\text{ABC}$ that \"ABC\" is the key for the next message? Empirically, we would estimate $\\hat p_\\text{ABC}=\\frac{X_\\text{ABC}}{n}=0.1$. We can derive the empirical estimates for all other trigrams that we have observed. However, it becomes quickly evident that the complete probability mass is distributed over the *observed* trigrams. This leaves no mass for *unobserved* trigrams, i.e., the probability of discovering a new trigram. This is called the missing probability mass or the discovery probability." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Turing and Good derived an estimate of the *discovery probability* $p_0$, i.e., the probability to discover an unobserved trigram, as the number $f_1$ of trigrams observed exactly once divided by the total number $n$ of messages cracked:\n", "$$\n", "p_0 = \\frac{f_1}{n}\n", "$$\n", "where $f_1$ is the number of singletons and $n$ is the number of cracked messages." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Lets explore this idea for a bit. We'll extend `BletchleyPark` to crack `n` messages and record the number of trigrams observed as the number of cracked messages increases." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:22.089897Z", "iopub.status.busy": "2022-01-11T09:32:22.089132Z", "iopub.status.idle": "2022-01-11T09:32:22.091760Z", "shell.execute_reply": "2022-01-11T09:32:22.091151Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class BletchleyPark(BletchleyPark):\n", " def break_message(self, message):\n", " \"\"\"Returning the trigram for an encoded message\"\"\"\n", " # For the following experiment, we want to make it practical\n", " # to break a large number of messages. So, we remove the\n", " # loop and just return the trigram for a message.\n", " #\n", " # enigma.cur_msg = message\n", " # while True:\n", " # (trigram, outcome) = self.enigma_fuzzer.run(self.enigma)\n", " # if outcome == self.enigma.PASS:\n", " # break\n", " trigram = enigma.internal_msg2key(message)\n", " return trigram\n", "\n", " def break_n_messages(self, n):\n", " \"\"\"Returns how often each trigram has been observed, \n", " and #trigrams discovered for each message.\"\"\"\n", " observed = defaultdict(int)\n", " timeseries = [0] * n\n", "\n", " # Crack n messages and record #trigrams observed as #messages increases\n", " cur_observed = 0\n", " for cur_msg in range(0, n):\n", " trigram = self.break_message(cur_msg)\n", "\n", " observed[trigram] += 1\n", " if (observed[trigram] == 1):\n", " cur_observed += 1\n", " timeseries[cur_msg] = cur_observed\n", "\n", " return (observed, timeseries)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's crack 2000 messages and compute the GT-estimate." ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:22.095807Z", "iopub.status.busy": "2022-01-11T09:32:22.094924Z", "iopub.status.idle": "2022-01-11T09:32:22.097605Z", "shell.execute_reply": "2022-01-11T09:32:22.098150Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "n = 2000 # messages to crack" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:22.107567Z", "iopub.status.busy": "2022-01-11T09:32:22.107005Z", "iopub.status.idle": "2022-01-11T09:32:24.822112Z", "shell.execute_reply": "2022-01-11T09:32:24.822518Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "bletchley = BletchleyPark(enigma)\n", "(observed, timeseries) = bletchley.break_n_messages(n)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let us determine the Good-Turing estimate of the probability that the next trigram has not been observed before:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:24.827372Z", "iopub.status.busy": "2022-01-11T09:32:24.826640Z", "iopub.status.idle": "2022-01-11T09:32:24.829554Z", "shell.execute_reply": "2022-01-11T09:32:24.829928Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.401" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "singletons = len([k for k, v in observed.items() if int(v) == 1])\n", "gt = singletons / n\n", "gt" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We can verify the Good-Turing estimate empirically and compute the empirically determined probability that the next trigram has not been observed before. To do this, we repeat the following experiment `repeats=1000` times, reporting the average: If the next message is a new trigram, return 1, otherwise return 0. Note that here, we do not record the newly discovered trigrams as observed." ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:24.833158Z", "iopub.status.busy": "2022-01-11T09:32:24.832611Z", "iopub.status.idle": "2022-01-11T09:32:24.834420Z", "shell.execute_reply": "2022-01-11T09:32:24.834859Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "repeats = 1000 # experiment repetitions " ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:24.921445Z", "iopub.status.busy": "2022-01-11T09:32:24.881386Z", "iopub.status.idle": "2022-01-11T09:32:25.952404Z", "shell.execute_reply": "2022-01-11T09:32:25.952862Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.412" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "newly_discovered = 0\n", "for cur_msg in range(n, n + repeats):\n", " trigram = bletchley.break_message(cur_msg)\n", " if(observed[trigram] == 0):\n", " newly_discovered += 1\n", "\n", "newly_discovered / repeats" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Looks pretty accurate, huh? The difference between estimates is reasonably small, probably below 0.03. However, the Good-Turing estimate did not nearly require as much computational resources as the empirical estimate. Unlike the empirical estimate, the Good-Turing estimate can be computed during the campaign. Unlike the empirical estimate, the Good-Turing estimate requires no additional, redundant repetitions." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "In fact, the Good-Turing (GT) estimator often performs close to the best estimator for arbitrary distributions ([Try it here!](#Kenngruppenbuch)). Of course, the concept of *discovery* is not limited to trigrams. The GT estimator is also used in the study of natural languages to estimate the likelihood that we haven't ever heard or read the word we next encounter. The GT estimator is used in ecology to estimate the likelihood of discovering a new, unseen species in our quest to catalog all _species_ on earth. Later, we will see how it can be used to estimate the probability to discover a vulnerability when none has been observed, yet (i.e., residual risk)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Alan Turing was interested in the _complement_ $(1-GT)$ which gives the proportion of _all_ messages for which the Brits have already observed the trigram needed for decryption. For this reason, the complement is also called sample coverage. The *sample coverage* quantifies how much we know about decryption of all messages given the few messages we have already decrypted. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The probability that the next message can be decrypted with a previously discovered trigram is:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:25.957795Z", "iopub.status.busy": "2022-01-11T09:32:25.957050Z", "iopub.status.idle": "2022-01-11T09:32:25.959820Z", "shell.execute_reply": "2022-01-11T09:32:25.960269Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "0.599" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 - gt" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The *inverse* of the GT-estimate (1/GT) is a _maximum likelihood estimate_ of the expected number of messages that we can decrypt with previously observed trigrams before having to find a new trigram to decrypt the message. In our setting, the number of messages for which we can expect to reuse previous trigrams before having to discover a new trigram is:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:25.964173Z", "iopub.status.busy": "2022-01-11T09:32:25.963483Z", "iopub.status.idle": "2022-01-11T09:32:25.966187Z", "shell.execute_reply": "2022-01-11T09:32:25.966571Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "2.4937655860349124" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 / gt" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "But why is GT so accurate? Intuitively, despite a large sampling effort (i.e., cracking $n$ messages), there are still $f_1$ trigrams that have been observed only once. We could say that such \"singletons\" are very rare trigrams. Hence, the probability that the next messages is encoded with such a rare but observed trigram gives a good upper bound on the probability that the next message is encoded with an evidently much rarer, unobserved trigram. Since Turing's observation 80 years ago, an entire statistical theory has been developed around the hypothesis that rare, observed \"species\" are good predictors of unobserved species.\n", "\n", "Let's have a look at the distribution of rare trigrams." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:25.971623Z", "iopub.status.busy": "2022-01-11T09:32:25.970907Z", "iopub.status.idle": "2022-01-11T09:32:25.972522Z", "shell.execute_reply": "2022-01-11T09:32:25.972947Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:25.976655Z", "iopub.status.busy": "2022-01-11T09:32:25.976018Z", "iopub.status.idle": "2022-01-11T09:32:25.978087Z", "shell.execute_reply": "2022-01-11T09:32:25.978585Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import matplotlib.pyplot as plt # type: ignore" ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:26.005422Z", "iopub.status.busy": "2022-01-11T09:32:26.004624Z", "iopub.status.idle": "2022-01-11T09:32:26.480317Z", "shell.execute_reply": "2022-01-11T09:32:26.481236Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "frequencies = [v for k, v in observed.items() if int(v) > 0]\n", "frequencies.sort(reverse=True)\n", "# Uncomment to see how often each discovered trigram has been observed\n", "# print(frequencies)\n", "\n", "# frequency of rare trigrams\n", "plt.figure(num=None, figsize=(12, 4), dpi=80, facecolor='w', edgecolor='k')\n", "plt.subplot(1, 2, 1)\n", "plt.hist(frequencies, range=[1, 21], bins=numpy.arange(1, 21) - 0.5)\n", "plt.xticks(range(1, 21))\n", "plt.xlabel('# of occurances (e.g., 1 represents singleton trigrams)')\n", "plt.ylabel('Frequency of occurances')\n", "plt.title('Figure 1. Frequency of Rare Trigrams')\n", "\n", "# trigram discovery over time\n", "plt.subplot(1, 2, 2)\n", "plt.plot(timeseries)\n", "plt.xlabel('# of messages cracked')\n", "plt.ylabel('# of trigrams discovered')\n", "plt.title('Figure 2. Trigram Discovery Over Time');" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:26.491761Z", "iopub.status.busy": "2022-01-11T09:32:26.490734Z", "iopub.status.idle": "2022-01-11T09:32:26.494784Z", "shell.execute_reply": "2022-01-11T09:32:26.495444Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "802 of 1009 trigrams (79.485%) have been observed 1 time (i.e., are singleton trigrams).\n", " 1 of 1009 trigrams ( 0.001%) have been observed 152 times.\n" ] } ], "source": [ "# Statistics for most and least often observed trigrams\n", "singletons = len([v for k, v in observed.items() if int(v) == 1])\n", "total = len(frequencies)\n", "\n", "print(\"%3d of %3d trigrams (%.3f%%) have been observed 1 time (i.e., are singleton trigrams).\"\n", " % (singletons, total, singletons * 100 / total))\n", "\n", "print(\"%3d of %3d trigrams ( %.3f%%) have been observed %d times.\"\n", " % (1, total, 1 / total, frequencies[0]))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The *majority of trigrams* have been observed only once, as we can see in Figure 1 (left). In other words, the majority of observed trigrams are \"rare\" singletons. In Figure 2 (right), we can see that discovery is in full swing. The trajectory seems almost linear. However, since there is a finite number of trigrams (26^3 = 17,576) trigram discovery will slow down and eventually approach an asymptote (the total number of trigrams).\n", "\n", "### Boosting the Performance of BletchleyPark\n", "Some trigrams have been observed very often. We call these \"abundant\" trigrams." ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:26.504896Z", "iopub.status.busy": "2022-01-11T09:32:26.503631Z", "iopub.status.idle": "2022-01-11T09:32:26.509170Z", "shell.execute_reply": "2022-01-11T09:32:26.510372Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Trigram : Frequency\n", " TJK : 152\n", " LBM : 69\n", " NWV : 64\n", " AZC : 43\n", " GZP : 41\n", " ADE : 37\n", " DNO : 27\n", " OQL : 26\n", " TCO : 20\n", " BDA : 19\n", " ARO : 18\n", " IPT : 16\n", " FGK : 16\n", " MSV : 15\n", " ONO : 15\n", " EOR : 13\n", " JTV : 11\n", " IBT : 11\n", " PWN : 11\n" ] } ], "source": [ "print(\"Trigram : Frequency\")\n", "for trigram in sorted(observed, key=observed.get, reverse=True): # type: ignore\n", " if observed[trigram] > 10:\n", " print(\" %s : %d\" % (trigram, observed[trigram]))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "We'll speed up the code breaking by _trying the abundant trigrams first_. \n", "\n", "First, we'll find out how many messages can be cracked by the existing brute forcing strategy at Bledgley park, given a maximum number of attempts. We'll also track the number of messages cracked over time (`timeseries`)." ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:26.523338Z", "iopub.status.busy": "2022-01-11T09:32:26.522047Z", "iopub.status.idle": "2022-01-11T09:32:26.524976Z", "shell.execute_reply": "2022-01-11T09:32:26.526086Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class BletchleyPark(BletchleyPark):\n", " def __init__(self, enigma):\n", " super().__init__(enigma)\n", " self.cur_attempts = 0\n", " self.cur_observed = 0\n", " self.observed = defaultdict(int)\n", " self.timeseries = [None] * max_attempts * 2\n", "\n", " def break_message(self, message):\n", " \"\"\"Returns the trigram for an encoded message, and\n", " track #trigrams observed as #attempts increases.\"\"\"\n", " self.enigma.cur_msg = message\n", " while True:\n", " self.cur_attempts += 1 # NEW\n", " (trigram, outcome) = self.enigma_fuzzer.run(self.enigma)\n", " self.timeseries[self.cur_attempts] = self.cur_observed # NEW\n", " if outcome == self.enigma.PASS: \n", " break\n", " return trigram\n", "\n", " def break_max_attempts(self, max_attempts):\n", " \"\"\"Returns #messages successfully cracked after a given #attempts.\"\"\"\n", " cur_msg = 0\n", " n_messages = 0\n", "\n", " while True:\n", " trigram = self.break_message(cur_msg)\n", "\n", " # stop when reaching max_attempts\n", " if self.cur_attempts >= max_attempts:\n", " break\n", "\n", " # update observed trigrams\n", " n_messages += 1\n", " self.observed[trigram] += 1\n", " if (self.observed[trigram] == 1):\n", " self.cur_observed += 1\n", " self.timeseries[self.cur_attempts] = self.cur_observed\n", " cur_msg += 1\n", " return n_messages" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "`original` is the number of messages cracked by the bruteforcing strategy, given 100k attempts. Can we beat this?" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:26.532541Z", "iopub.status.busy": "2022-01-11T09:32:26.531211Z", "iopub.status.idle": "2022-01-11T09:32:26.534989Z", "shell.execute_reply": "2022-01-11T09:32:26.535960Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "max_attempts = 100000" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:26.619449Z", "iopub.status.busy": "2022-01-11T09:32:26.578306Z", "iopub.status.idle": "2022-01-11T09:32:27.165628Z", "shell.execute_reply": "2022-01-11T09:32:27.166133Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "bletchley = BletchleyPark(enigma)\n", "original = bletchley.break_max_attempts(max_attempts)\n", "original" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Now, we'll create a boosting strategy by trying trigrams first that we have previously observed most often." ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:27.172570Z", "iopub.status.busy": "2022-01-11T09:32:27.171765Z", "iopub.status.idle": "2022-01-11T09:32:27.173782Z", "shell.execute_reply": "2022-01-11T09:32:27.174182Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "class BoostedBletchleyPark(BletchleyPark):\n", " def __init__(self, enigma, prior):\n", " super().__init__(enigma)\n", " self.prior = prior\n", "\n", " def break_message(self, message):\n", " \"\"\"Returns the trigram for an encoded message, and\n", " track #trigrams observed as #attempts increases.\"\"\"\n", " self.enigma.cur_msg = message\n", "\n", " # boost cracking by trying observed trigrams first\n", " for trigram in sorted(self.prior, key=self.prior.get, reverse=True):\n", " self.cur_attempts += 1\n", " (_, outcome) = self.enigma.run(trigram)\n", " self.timeseries[self.cur_attempts] = self.cur_observed\n", " if outcome == self.enigma.PASS:\n", " return trigram\n", "\n", " # else fall back to normal cracking\n", " return super().break_message(message)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "`boosted` is the number of messages cracked by the boosted strategy." ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:27.259720Z", "iopub.status.busy": "2022-01-11T09:32:27.221212Z", "iopub.status.idle": "2022-01-11T09:32:27.781456Z", "shell.execute_reply": "2022-01-11T09:32:27.782075Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "23" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "boostedBletchley = BoostedBletchleyPark(enigma, prior=observed)\n", "boosted = boostedBletchley.break_max_attempts(max_attempts)\n", "boosted" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We see that the boosted technique cracks substantially more messages. It is worthwhile to record how often each trigram is being used as key and try them in the order of their occurence.\n", "\n", "***Try it***. *For practical reasons, we use a large number of previous observations as prior (`boostedBletchley.prior = observed`). You can try to change the code such that the strategy uses the trigram frequencies (`self.observed`) observed **during** the campaign itself to boost the campaign. You will need to increase `max_attempts` and wait for a long while.*" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's compare the number of trigrams discovered over time." ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:27.806288Z", "iopub.status.busy": "2022-01-11T09:32:27.805548Z", "iopub.status.idle": "2022-01-11T09:32:28.323625Z", "shell.execute_reply": "2022-01-11T09:32:28.324362Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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J4e8vDx69OhBr169mDFjRo3u/6677qr0NhMnTuSaa67ZJ33dunUMHTqUHj160KVLF8466yzg695ZK6ukd1bnyuVtEFnJM4g0KOmLaf78+dx9993ceGO1+x3cS1UyiERKuu+eP38+H3/8Mffccw9QfgZRWFiYcH+eQbjkeAkiG3kGkWax3X2bGddffz1du3alW7duTJkypdz0tWvXctJJJ5GXl0fXrl159913ueGGG0qf1B45ciQATz75JH379iUvL4/LL7+coqIiACZMmMA3vvEN+vbtyz/+8Y+48Xn33S4jvASRlerXcxAv3wBffFSz+zykG5x5T7mrJOru+7nnnistWWzcuJE+ffpw0kknMWPGjLjpkydPZvDgwdx8880UFRWxc+dOBgwYwNixY0s71Vu8eDFTpkzhH//4B7m5uVx11VVMmjSJgQMHcttttzFnzhyaN2/OqaeeWtpDaizvvttlhpcgslH9yiAyJFF33++99x4jRowgJyeHtm3bcvLJJ/Phhx8mTO/Tpw/f//73KSgo4Fvf+lZp/06x3njjDebMmUOfPn2AKHM6+OCDmTVrFqeccgpt2rQB4IILLuCTTz7ZZ3vvvttlhJcgslL9yiAq+KWfDmW7+66Mk046ienTp/O3v/2N0aNH8+Mf/5iLL754r3XMjFGjRnH33Xfvlf78888nfRzvvtuln5cgspG3QaRZbHffAwYMYMqUKRQVFbFhwwamT59O3759E6avXLmStm3b8oMf/IDLLruMuXPnAlEvsAUFBQCcfvrpPPPMM6xfH43kunnzZlauXMnxxx/PO++8w6ZNmygoKCjtxrss777bZYSXILJSKoccHQ8MBdabWdeQNgXoHFZpAWwxs7w4264AtgNFQGGingZri0TdfZ977rnMnDmTHj16IIlf//rXHHLIIQnTH3/8ce69915yc3Np2rQpTzzxBBB1Wte9e3d69erFpEmTuPPOOxk0aBDFxcXk5ubyu9/9jn79+nH77bfTv39/WrRoEbd6Crz7bpcpXoLIRinr7lvSScAO4ImSDKLM8t8AW83sF3GWrQDyzWxjZY7p3X3XbtXpvruy/O8iyzw+DAp3w6WvZTqSeicj3X2b2XRJHRMEJKKxqE9L1fFd7ePdd9dnXoLIRplqpB4ArDOzZQmWG/CaJAMeM7NxiXYkaQwwBqBDhw41HqhLH+++ux7zNoislKlG6hHAU+UsP9HMegFnAleH6qq4zGycmeWbWX7JLZxx1qlWsK5u8b+HbOUZRLZJewYhqSHwbWBKonXMbE14Xw9MBfpW9XiNGjVi06ZN/qXggChz2LRpE40aNcp0KC6WGchvqsw2mahiOgNYYmar4y2UdADQwMy2h+lBwD4N2clq3749q1evrtJzB65uatSo0V7dibgsYMXQICfTUbgyUnmb61PAKcBBklYDt5nZH4HhlKleknQo8AczOwtoC0yN2rFpCEw2s1eqGkdubi6dOnWq6ubOuXRY9T4ccUKmo3BlpPIuphEJ0kfHSfscOCtMfwb0SFVczrks1KQ17En8IKbLDK/0c85lnhrAoXmZjsKV4RmEcy7zrBi/iyn7eAbhnMs8v4spK/kVcc5lnhX7g3JZyDMI51wW8BJENvIr4pzLPG+DyEqeQTjnMs/wEkQW8ivinMs8b4PISgkflJPUq7wNzWxuzYfjnKufvA0iG5X3JPVvwnsjIB+YT1RJ2B2YDfRPbWjOuXrDijMdgYsjYZZtZqea2anAWqBX6FK7N9ATWJOuAJ1z9YA/B5GVkrkinc3so5IZM1sI+FiNzrma420QWSmZzvoWSPoD8GSYHwksSF1Izrn6x0sQ2SiZDOIS4ErgR2F+OvBIyiJyztU//hxEVqowgzCz3ZIeBaaZ2dI0xOScq2+s2EsQWajCKyJpGDAPeCXM50l6McVxOefqEzNvg8hCyWTZtxGNCb0FwMzmARUO0SZpvKT1khbGpN0uaY2keeF1VoJth0haKulTSTckcyLOudrM2yCyUTJXpMDMtpZJsyS2mwgMiZP+WzPLC69pZRdKygF+B5wJdAFGSOqSxPGcc7XRV1+GCS9BZJtkMohFki4EciQdLelhYEZFG5nZdGBzFWLqC3xqZp+Z2VfAn4FzqrAf51y2K9gFvwl3zTfcL7OxuH0kk0H8EDgO2ANMBrYC11XjmNdIWhCqoFrGWX4YsCpmfnVIi0vSGEmzJc3esGFDNcJyzqXdnh2wZyt0Ox96jc50NK6McjOIUN3zNzO72cz6hNctZra7isd7BDgKyCN6Qvs35a6dBDMbF57yzm/Tpk11d+ecS6eSLjY69IMDWmc2FrePcjMIMysCiiU1r4mDmdk6Mysys2Lg/4iqk8paAxweM98e79rDuTqqpDnT2x+yUTIPyu0APpL0OlDSmoSZXVvZg0lqZ2Zrw+y5wMI4q30IHC2pE1HGMBy4sLLHcs7VAiUlCL+DKSslk0E8F16VIukp4BTgIEmriW6XPUVSHtHPhhXA5WHdQ4E/mNlZZlYo6RrgVSAHGG9miyp7fOdcLWChBOHPQGSlZJ6kflxSY6BDZZ6kNrMRcZL/mGDdz4GzYuanAfvcAuucq2O8BJHVknmS+mz8SWrnXEqUlCA8g8hGyVyV29n3SeojUxaRc67+KB0oyKuYslFVn6T24Z+cc9VnXoLIZsk0Uu/1JDVwLUk8Se2ccxUqbYPwEkQ2ysST1M45tzcvQWSlZEoQx5jZzcDNqQ7GOVfPeBtEVksm2/6NpMWS7pDUNeUROefqD38OIqtVmEGY2anAqcAG4DFJH0m6JeWROefqPm+DyGpJVfyZ2Rdm9hBwBdEzEbemMijnXH3hdzFls2QelDs2jAS3ECgZC6J9yiNzztV93gaR1ZJppB5PNGjPoNAlhnPO1Qx/DiKrJdMXU39J+wHfkNQKWGpmBakPzTlX53kbRFarMIOQdDLwBFHvqwIOlzQqDCnqnKutvvoSPnsHigszF8OWldG7lyCyUjJVTPcTVS8tBZD0DeApoHcqA3POpdicx+HVGzMdRaRxvNGHXaYlk0HkxnbzbWafSMpNYUzOuXT4Koz/NeYdaJDMV0GK5DaG1kdl7vguoWT+KmZL+gPwZJgfCcyuaCNJ44GhwHoz6xrS7gXOBr4ClgOXmNmWONuuALYDRUChmeUnEadzrjKsKHo/pDs08Coet69k/iquBD4m6qTv2jB9ZRLbTQSGlEl7HehqZt2BT4DyyrenmlmeZw7OpUhxyCA8c3AJJFOCaAg8aGb3A0jKAfavaCMzmy6pY5m012Jm3wfOSz5U51yNsiJQTqajcFksmZ8ObwCNY+YbA3+vgWN/H3g5wTIDXpM0R9KY8nYiaYyk2ZJmb9iwoQbCcq6esGJo4BmESyyZDKKRme0omQnTTapzUEk3A4XApASrnGhmvYAzgaslnZRoX2Y2zszyzSy/TZs21QnLufql2EsQrnzJZBBfSupVMiOpN7CrqgeUNJqo8XqkWcljlHszszXhfT0wlWjIU+dcTfIShKtAMm0Q1wFPS/qc6EG5Q4ALqnIwSUOA/wVONrOdCdY5AGhgZtvD9CDgF1U5nnOuHF6CcBVIpquNDyUdA3QOSUl1tSHpKeAU4CBJq4HbiO5a2h94XdGj9e+b2RWSDgX+YGZnAW2BqWF5Q2Cymb1S6TNzLtuZwYK/wM5NmTn+2vl+B5MrVzJdbZwPvGJmC8M4EL0k3Wlmc8vbzsxGxEn+Y4J1PwfOCtOfAT0qjNy52m7zZzC13HswUq9dXmaP77JaMlVM/8/MnpZ0InA6cB/wCHB8SiNzrq4r3BO9n/M7OGZoZmLYr2lmjutqhWQyiPA0Dd8E/s/M/ibpzhTG5Fz9UNKT6f7NoHGLjIbiXDzJVECukfQYUcP0NEn7J7mdc648JV1deEOxy1LJfNF/F3gVGBz6TWoFXJ/KoJyrF0q7uvAMwmWnhFVMkg40s21AI+DtkNYK2EMSnfU55ypQOliOZxAuO5XXBjGZ6IG2OURdX8QO+WTAkSmMy7m6zzvLc1kuYQZhZkPDe6f0heNcPeJtEC7LlVfF1CvRMoCKnoNwzlXA2yBcliuviuk34b0RkA/MJ6pm6k7UBtE/taE5Vwk7N8OMh79+tqA22LoqevcShMtS5VUxnQog6Tmgl5l9FOa7ArenJTrnkvXZW/De/ZDbpHZ94TZrBy2PyHQUzsWVzINynUsyB4DQ5caxKYzJucorKozer3jPxzd2roYkk0EsiDMm9YLUheRcFZQ2+PodQc7VlGQyiEuIxqD+UZifTtQXk3PZwxt8natxyXT3vRv4bXg5l538llHnapyXx13dUOxVTM7VNP9vcnVDSbcVXsXkXI2pVAYhqYGkAyux/nhJ6yUtjElrJel1ScvCe8sE244K6yyTNKoycbp6qNirmJyraRVmEJImSzowjA+9EPhYUrK9uU4EhpRJuwF4w8yOBt4I82WP2YpoiNLjgb7AbYkyEueAr9sgvF8j52pMMncxdTGzbZJGAi8TfaHPAe6taEMzmy6pY5nkc4jGqgZ4nKin2J+VWWcw8LqZbQaQ9DpRRvNUEvG6umjHBnjtFijYGX/5puXRu5cgnKsxyWQQuZJygW8BY82sQJJV45htzWxtmP4CaBtnncOAVTHzq0PaPiSNAcYAdOjQoRphuay2ahYs+DO07AgNG8df5xtDfAhN52pQMhnEY8AKor6Ypks6AthWEwc3M6tmZoOZjQPGAeTn51drXy6LlVQhXTAJDuma2VicqycqrLA1s4fM7DAzO8siK4FTq3HMdZLaAYT39XHWWQMcHjPfPqS5+sofhHMu7SosQUhqAVwMdCyz/rVVPOaLwCjgnvD+Qpx1XgXuimmYHgTcWMXjubrAR19zLu2SqWKaBrwPfAQUV2bnkp4iapA+SNJqojuT7gH+IulSYCXRmNdIygeuMLPLzGyzpDuAD8OuflHSYO3qKS9BOJd2yWQQjczsx1XZuZmNSLDo9DjrzgYui5kfD4yvynFdHeSd8TmXdsn8t/1J0g8ktQsPubUKzyk4lz5egnAu7ZIpQXxF9MzDzUDJXUIGHJmqoJzbh3fG51zaJZNB/AT4LzPbmOpgnEvISxDOpV0yGcSnQILHV52rpjkTYe4TFa+3Y0P07iUI59ImmQziS2CepLeA0hHhzayqt7k697XFf4WNy+DwvuWv17glHHUKNGmdlrCcc8llEM+Hl3M1r7gI2nSGi57NdCTOuTKSGVHu8XQE4uopK/ZqI+eyVDJPUh8N3A10ARqVpJuZ38Xkqs+KveHZuSyVzHMQE4BHgEKiPpieAJ5MZVCuHiku8offnMtSyfxnNjazNwCZ2Uozux34ZmrDcvWGFXkJwrkslUwj9R5JDYBlkq4h6lXVO913NaO4yNsgnMtSyZQgfgQ0Ieq9tTdwEVEvrM5Vn5cgnMta5ZYgJOUAF5jZT4EdwCVpicrVH16CcC5rlZtBmFmRpBPTFYyrpm2fw8Sh8NWOTEeSvC83QgsfKta5bJRMG8Q/Jb0IPE30VDUAZvZcyqJyVbP5M9i8HI4eDAe2y3Q0yet6XqYjcM7FkdR4EMAm4LSYNAM8g8g2JR3anXAtdPSCn3OuepJ5krpG2x0kdQamxCQdCdxqZg/ErHMK0VCk/wpJz5nZL2oyjjrJu8R2ztWgZJ6kfihO8lZgtpnFG0+6XGa2FMgL+84hum12apxV3zWzoZXdf71WHEaE9buCnHM1IJnbXBsRfaEvC6/uQHvgUkkPVPP4pwPLzWxlNffjwEsQzrkalUwbRHfgBLPo20fSI8C7wInAR9U8/nDgqQTL+kuaD3wO/NTMFsVbSdIYYAxAhw71/G6Y0kF1vOsK51z1JfNN0pK9n5w+AGgVMow98TepmKT9gGFEd0eVNRc4wsx6AA9TTnfjZjbOzPLNLL9NmzZVDaduKC1BeAbhnKu+ZEoQvyYaMOhtQMBJwF2SDgD+Xo1jnwnMNbN1ZReY2baY6WmSfi/pIB/2tALFXsXknKs5ydzF9EdJ04CSIb9uMrPPw/T11Tj2CBJUL0k6BFhnZiapL1FJZ1M1jlX3FRVCUUE07Y3UzrkakDCDkHSMmS2R1CskrQrvh0g6xMzmVvWgofQxELg8Ju0KADN7FDgPuFJSIbALGG5mVtXj1Xn/ngUTvwnFIYPI2S+z8Tjn6oTyShA/Jmr8/U2cZcbeD85Vipl9CbQuk/ZozPRYYGxV91/v/GdFlDn0uxpaHwWtfCwn51z1JcwgzGxM6Ob7FjP7RxpjcpVV0jjd9wfQqlNmY3HO1Rnl3u5iZsX4L/nsV3p7q7c9OOdqTjL3Q74h6TuSlPJoXNX4A3LOuRRIJoO4nOhZhT2StknaLmlbRRu5NPIShHMuBZK5zbVZOgJx1WChDyYvQTjnalCFJQhJbyST5jLISxDOuRQo7zmIRkRjUR8kqSXRU9QABwKHpSE2lyzvYsM5lwLlVTFdDlwHHArM4esMYht+Z1N22R2ahLwE4ZyrQeU9B/Eg8KCkH5rZw2mMyVXWO/dE7w1yMxuHc65OSaaR2jOHbNewEbTtCrmNMh2Jc64O8UrrOkFwRP9MB+Gcq2MSZhCSTgjv+6cvHFclVuS3uDrnalx5JYiSsahnpiMQVw1W7A3UzrkaV14bRIGkccBhkh4qu9DMrk1dWK5Sir0E4ZyreeVlEEOBM4DBRLe5umxkBpiXIJxzNa6821w3An+WtNjM5tf0gSWtALYDRUChmeWXWS7gQeAsYCcwujqDFNVZPsyocy5FkrmLaZOkqZLWh9ezktrX0PFPNbO8splDcCZwdHiNAR6poWPWLSVPUTfwG9KcczWrwucggAnAZOD8MH9RSBuYqqCCc4AnwlCj70tqIamdma1N8XGTs24R7Nyc6SigaE/07iUI51wNSyaDONjMJsTMT5R0XQ0c24DXJBnwmJmNK7P8ML4eBxtgdUjbK4OQNIaohEGHDh1qIKwkbP8CHvnv9BwrWY2aZzoC51wdk0wGsVHSRcBTYX4EsKkGjn2ima2RdDDwuqQlZja9sjsJGcs4gPz8fKuBuCpW0vfRSddDp5PTcshyNWgI7ePV0jnnXNUlk0F8H3gY+C3Rr/4ZwCXVPbCZrQnv6yVNBfoCsRnEGuDwmPn2IS3zSur9D+4CnQZkNhbnnEuRCls2zWylmQ0zszZmdrCZfcvM/l2dg0o6QFKzkmlgELCwzGovAhcr0g/YmjXtDz7+gnOuHkimBJEKbYGpYZjrhsBkM3tF0hUAZvYoMI3oFtdPiW5zrXappcb4GNDOuXogIxmEmX0G9IiT/mjMtAFXpzOupHkJwjlXD/jN81XhY0A75+qBZMakviVm2nt2hZinlz1/dc7VXeV19/0zSf2B82KSvWdX8KeXnXP1QnltEEuInp4+UtK7Yb61pM5mtjQt0aVTcTEsnQZ7tle87sZPonevYnLO1WHlZRBbgJuAU8LrWKLbUW8ImUSWPUpcTWvnwZSRldum6cEpCcU557JBeRnEYOBW4CjgfmAB8KWZZc/tpjWpcHf0fu5jcPjxFa+/3wGeQTjn6rTyuvu+CUDSfOBPQC+gjaT3gP+Y2dnpCTFNSu5MatYOWnXKbCzOOZcFknkO4lUzmw3MlnSlmZ0o6aBUB5Z2Frpx8juTnHMOSK6rjf+NmR0d0jamKqCMKX22QZmNwznnskSlfi6nYmS5rFGaQXgJwjnnwJ+kjlHSU7iXIJxzDjyD+JqXIJxzbi/+bViitADhJQjnnIPMdfedfbwE4ZxLoRmfbmTOyv+kZN+N98vhsgFH1vh+PYMoVXKbq5cgnHM17/a/LuKTdTtSsu+Dmu7vGURKlZQgvJHaOZcCXxUWM7R7Ox64IC/ToSQt7RmEpMOBJ4hGlTNgnJk9WGadU4AXgH+FpOfM7BcpDcwflHPOpVCRGQ0biIY5tec7JhMliELgJ2Y2N4xLPUfS62b2cZn13jWzoWmLyh+Uc86lUHExNGhQu75f0p6VmdlaM5sbprcDi4HD0h3HvrwE4ZxLnaJiI6eW/QDN6LehpI5AT2BWnMX9Jc2X9LKk48rZxxhJsyXN3rBhQ9WD8TYI51wKFZuR4yWI5EhqCjwLXGdm28osngscYWY9gIeB5xPtx8zGmVm+meW3adOm6gF5G4RzLoWKzbyKKRmScokyh0lm9lzZ5Wa2zcx2hOlpQG7Ke5D1NgjnXAp5FVMSJAn4I7DYzO5PsM4hYT0k9SWKc1NqI/MShHMudYqKa18VUybuYjoB+B7wkaR5Ie0moAOAmT0KnAdcKakQ2AUMNyupA0qBz96GORPDTO26gM7VJW8tXc9ri77IdBgpsaugiAa1rASR9gzCzN6jgm9hMxsLjE1PRMDM38PKGXDwcXBgu7Qd1jm3t/+b/hkfrthMyyb7ZTqUGtf6gP3p2aFFpsOoFH+SGqC4ENr1gB+8melInKvXCouNXh1aMuXy/pkOxeG9uUasCJST6Sicq/eKa2E9fV3mGQRAcRE08AzCuUwrqoXPCtRlnkFAdIurlyCcy7jiYqt1Dbl1mWcQEEoQ/lE4l2legsgu/q0I3gbhXJYoKsZLEFnEMwjwNgjnskTUSJ3pKFwJvxQQ2iD8o3Au04rM2yCyiT8HAbD5X9DMH5Bz+3rnkw2Mf+9fpO4xfhdrzX920fmQZpkOwwWeQQA0PRh21M3H+131TFuwlhnLN3Lcoc0zHUq90PmQZgzq0jbTYbjAMwiIqpha1fyA3672KzKjTdP9ef7qEzIdinNp5xXv4HcxuYSKi2tfH/7O1RTPICAMFusZhNuX35fv6jPPIMBLEC6h2jjIi3M1xTMI8CepXUK1cZhI52qKfyuClyBcQl6CcPVZpsakHiJpqaRPJd0QZ/n+kqaE5bMkdUxpQP4ktUugqBgvQbh6KxNjUucAvwPOBLoAIyR1KbPapcB/zOy/gN8Cv0ppUF6CcAkUm3f94OqvTDwH0Rf41Mw+A5D0Z+Ac4OOYdc4Bbg/TzwBjJSll41Lv3sqz89by6JJ3UrJ7V3ut2bKLow9umukwnMuITGQQhwGrYuZXA8cnWsfMCiVtBVoDG8vuTNIYYAxAhw4dqhTQhwcOYnnzwRzdyL8I3N6ObtuUM471J3td/VTrn6Q2s3HAOID8/PwqlTD6/Php+tRoVM45V/tlonZ1DXB4zHz7kBZ3HUkNgebAprRE55xzDshMBvEhcLSkTpL2A4YDL5ZZ50VgVJg+D3gzZe0Pzjnn4kp7FVNoU7gGeBXIAcab2SJJvwBmm9mLwB+BP0n6FNhMlIk455xLo4y0QZjZNGBambRbY6Z3A+enOy7nnHNf8zu8nXPOxeUZhHPOubg8g3DOOReXZxDOOefiUl26e1TSBmBlFTc/iDhPatcxfo51g59j7ZdN53eEmbWJt6BOZRDVIWm2meVnOo5U8nOsG/wca7/acn5exeSccy4uzyCcc87F5RnE18ZlOoA08HOsG/wca79acX7eBuGccy4uL0E455yLyzMI55xzcdX7DELSEElLJX0q6YZMx1MRSYdLekvSx5IWSfpRSG8l6XVJy8J7y5AuSQ+F81sgqVfMvkaF9ZdJGhWT3lvSR2GbhyQpA+eZI+mfkl4K850kzQoxTQldxSNp/zD/aVjeMWYfN4b0pZIGx6RnxTWX1ELSM5KWSFosqX8dvI7/E/5OF0p6SlKj2n4tJY2XtF7Swpi0lF+3RMdIKTOrty+i7saXA0cC+wHzgS6ZjquCmNsBvcJ0M+AToAvwa+CGkH4D8KswfRbwMiCgHzArpLcCPgvvLcN0y7Dsg7CuwrZnZuA8fwxMBl4K838BhofpR4Erw/RVwKNhejgwJUx3Cddzf6BTuM452XTNgceBy8L0fkCLunQdiYYO/hfQOOYajq7t1xI4CegFLIxJS/l1S3SMlJ5rJv4xsuUF9AdejZm/Ebgx03FV8hxeAAYCS4F2Ia0dsDRMPwaMiFl/aVg+AngsJv2xkNYOWBKTvtd6aTqn9sAbwGnAS+EfZSPQsOx1IxpXpH+YbhjWU9lrWbJetlxzolES/0W4UaTs9akj17FkbPlW4dq8BAyuC9cS6MjeGUTKr1uiY6TyVd+rmEr+gEusDmm1QiiC9wRmAW3NbG1Y9AXQNkwnOsfy0lfHSU+nB4D/BYrDfGtgi5kVxomp9DzC8q1h/cqed7p1AjYAE0JV2h8kHUAduo5mtga4D/g3sJbo2syh7l1LSM91S3SMlKnvGUStJakp8CxwnZlti11m0U+MWnn/sqShwHozm5PpWFKsIVE1xSNm1hP4kqjaoFRtvo4AoY78HKLM8FDgAGBIRoNKg3Rct3T9bdT3DGINcHjMfPuQltUk5RJlDpPM7LmQvE5Su7C8HbA+pCc6x/LS28dJT5cTgGGSVgB/JqpmehBoIalkBMTYmErPIyxvDmyi8uedbquB1WY2K8w/Q5Rh1JXrCHAG8C8z22BmBcBzRNe3rl1LSM91S3SMlKnvGcSHwNHhror9iBrGXsxwTOUKdzT8EVhsZvfHLHoRKLkTYhRR20RJ+sXhbop+wNZQTH0VGCSpZfilN4ioPnctsE1Sv3Csi2P2lXJmdqOZtTezjkTX400zGwm8BZyX4PxKzvu8sL6F9OHhzphOwNFEjX9Zcc3N7AtglaTOIel04GPqyHUM/g30k9QkxFByjnXqWgbpuG6JjpE66WjQyeYX0V0GnxDdDXFzpuNJIt4TiYqWC4B54XUWUV3tG8Ay4O9Aq7C+gN+F8/sIyI/Z1/eBT8Prkpj0fGBh2GYsZRpS03iup/D1XUxHEn0pfAo8Dewf0huF+U/D8iNjtr85nMNSYu7gyZZrDuQBs8O1fJ7obpY6dR2BnwNLQhx/IroTqVZfS+ApojaVAqKS4KXpuG6JjpHKl3e14ZxzLq76XsXknHMuAc8gnHPOxeUZhHPOubg8g3DOOReXZxDOOefi8gzCZQ1Jd0s6VdK3JN1YyW3bhB5A/ylpQKpijDneREnnxUn/g6QuKT72TTHTLSRdlcrjheN0lHRhqo/jsotnEC6bHA+8D5wMTK/ktqcDH5lZTzN7tzIbxjzVW21mdpmZfVxT+0vgppjpFkS9oKZaR8AziHrGMwiXcZLulbQA6APMBC4DHpF0a5x1O0p6M/St/4akDpLyiLpCPkfSPEmNy2zTR9IMSfMlfSCpmaTRkl6U9CbwhqSmYX9zQ1/858Rsf3E43nxJf4oT0x2hRJEj6W1J+SF9h6Rfhu3el9Q2pB8V5j+SdKekHQk+l+clzVE0nsKYkHYP0Dic5yTgHuCoMH9vWOd6SR+GmH8e87ktCXF+ImmSpDMk/UPR+AJ9w3q3S/qTpJkh/QchnHuAAeE4/yPpuPBZzgvHOTrJy+1qk0w9Reovf8W+iDKHh4Fc4B/lrPdXYFSY/j7wfJgeDYyNs/5+RH3t9wnzBxJ1lDea6CnYkideGwIHhumDiJ5uFXAc0ZO6B4VlJetPJOoO4l6iMQ1KHjp9m/C0LNET72eH6V8Dt4TplwhdQANXADsSnGvJsRoTPVnbOszviFmnI3t3Oz0IGBdibxCOdVJYrxDoFtLnAOPDeufEfI63E42r0Dh8DquIOto7hfBUe1jvYWBkzGfcONN/Q/6q+ZeXIFy26EX0xXQMsLic9foTDSQEUdcNJ1aw387AWjP7EMDMttnXXU2/bmabw7SAu0JJ5u9EXSy3Jeos8Gkz2xi23xyz7/8HNDezKyx8U5bxFdEXNERfyB1jzuHpMD2ZxK6VNJ+o2u1woj6IKjIovP4JzCX6PEu2+5eZfWRmxcAi4I0Q90cxsQG8YGa7wjm/BfSNc5yZwE2SfgYcYWa7kojN1TI1VvfqXFWE6qGJRL1WbgSaRMmaRzR4TCq/eL6MmR4JtAF6m1mBot5kG1Ww/YdAb0mtymQcJQpiMo4iKvH/JukUot5Q+5vZTklvJxEPRBnd3Wb2WJn9dQT2xCQVx8wXl4mtbGa3T+ZnZpMlzQK+CUyTdLmZvZlEfK4W8RKEyygzm2dmeXw9dOqbwGAzy0uQOcwg6rUToi/1ihqklwLtJPUBCO0P8b6omxONQ1Eg6VTgiJD+JnC+pNZh+1Yx27xCVDf/N0nNKogj1vvAd8L08ATrNAf+EzKHY4iGoCxRoKjLd4DtREPPlngV+L6i8UKQdJikgysRG0RtOY3COZ9ClBHudRxJRwKfmdlDRL2Kdq/kMVwt4CUIl3GS2hB9GRZLOsbKvwvoh0SjsF1PNCLbJeXt28y+knQB8HBovN5F9Mu8rEnAXyV9RNTD6pKw/SJJvwTekVREVHUzOmb/T4fM4UVJZyV5ytcBT0q6mSiT2RpnnVeAKyQtJsrk3o9ZNg5YIGmumY0MDc0LgZfN7HpJxwIzFY11vwO4iKgEk6wFRFVLBwF3mNnnkjYARaHKayJRr6zfk1RANLrZXZXYv6slvDdX59JMUhNgl5mZpOFEDdbnVLRdOki6nagR/L5Mx+Iyz0sQzqVfb2Csop/4W4juxnIu63gJwjnnXFzeSO2ccy4uzyCcc87F5RmEc865uDyDcM45F5dnEM455+L6/9SJ0lP2jyA8AAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "# print plots\n", "line_old, = plt.plot(bletchley.timeseries, label=\"Bruteforce Strategy\")\n", "line_new, = plt.plot(boostedBletchley.timeseries, label=\"Boosted Strategy\")\n", "plt.legend(handles=[line_old, line_new])\n", "plt.xlabel('# of cracking attempts')\n", "plt.ylabel('# of trigrams discovered')\n", "plt.title('Trigram Discovery Over Time');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We see that the boosted fuzzer is constantly superior over the random fuzzer." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "## Estimating the Probability of Path Discovery\n", "\n", "\n", "\n", "\n", "So, what does Turing's observation for the Naval Enigma have to do with fuzzing _arbitrary_ programs? Turing's assistant I.J. Good extended and published Turing's work on the estimation procedures in Biometrica, a journal for theoretical biostatistics that still exists today. Good did not talk about trigrams. Instead, he calls them \"species\". Hence, the GT estimator is presented to estimate how likely it is to discover a new species, given an existing sample of individuals (each of which belongs to exactly one species). \n", "\n", "Now, we can associate program inputs to species, as well. For instance, we could define the path that is exercised by an input as that input's species. This would allow us to _estimate the probability that fuzzing discovers a new path._ Later, we will see how this discovery probability estimate also estimates the likelihood of discovering a vulnerability when we have not seen one, yet (residual risk)." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's do this. We identify the species for an input by computing a hash-id over the set of statements exercised by that input. In the [Coverage](Coverage.ipynb) chapter, we have learned about the [Coverage class](Coverage.ipynb#A-Coverage-Class) which collects coverage information for an executed Python function. As an example, the function [`cgi_decode()`](Coverage.ipynb#A-CGI-Decoder) was introduced. The function `cgi_decode()` takes a string encoded for a website URL and decodes it back to its original form.\n", "\n", "Here's what `cgi_decode()` does and how coverage is computed." ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.328948Z", "iopub.status.busy": "2022-01-11T09:32:28.328361Z", "iopub.status.idle": "2022-01-11T09:32:28.330088Z", "shell.execute_reply": "2022-01-11T09:32:28.330560Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Coverage import Coverage, cgi_decode" ] }, { "cell_type": "code", "execution_count": 50, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.334606Z", "iopub.status.busy": "2022-01-11T09:32:28.333916Z", "iopub.status.idle": "2022-01-11T09:32:28.335470Z", "shell.execute_reply": "2022-01-11T09:32:28.335892Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "encoded = \"Hello%2c+world%21\"\n", "with Coverage() as cov:\n", " decoded = cgi_decode(encoded)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.340385Z", "iopub.status.busy": "2022-01-11T09:32:28.339444Z", "iopub.status.idle": "2022-01-11T09:32:28.342660Z", "shell.execute_reply": "2022-01-11T09:32:28.343148Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'Hello, world!'" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "decoded" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.347012Z", "iopub.status.busy": "2022-01-11T09:32:28.346411Z", "iopub.status.idle": "2022-01-11T09:32:28.348862Z", "shell.execute_reply": "2022-01-11T09:32:28.349362Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{('cgi_decode', 28), ('cgi_decode', 31), ('cgi_decode', 37), ('cgi_decode', 15), ('cgi_decode', 18), ('cgi_decode', 27), ('cgi_decode', 24), ('cgi_decode', 30), ('cgi_decode', 33), ('cgi_decode', 39), ('cgi_decode', 17), ('cgi_decode', 23), ('cgi_decode', 29), ('cgi_decode', 26), ('cgi_decode', 32), ('cgi_decode', 38), ('cgi_decode', 16), ('cgi_decode', 22), ('cgi_decode', 19), ('cgi_decode', 25)}\n" ] } ], "source": [ "print(cov.coverage());" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Trace Coverage\n", "First, we will introduce the concept of execution traces, which are a coarse abstraction of the execution path taken by an input. Compared to the definition of path, a trace ignores the sequence in which statements are exercised or how often each statement is exercised.\n", "\n", "* `pickle.dumps()` - serializes an object by producing a byte array from all the information in the object\n", "* `hashlib.md5()` - produces a 128-bit hash value from a byte array" ] }, { "cell_type": "code", "execution_count": 53, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.354164Z", "iopub.status.busy": "2022-01-11T09:32:28.353211Z", "iopub.status.idle": "2022-01-11T09:32:28.355512Z", "shell.execute_reply": "2022-01-11T09:32:28.356078Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "import pickle\n", "import hashlib" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.361520Z", "iopub.status.busy": "2022-01-11T09:32:28.360625Z", "iopub.status.idle": "2022-01-11T09:32:28.363589Z", "shell.execute_reply": "2022-01-11T09:32:28.362665Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "def getTraceHash(cov):\n", " pickledCov = pickle.dumps(cov.coverage())\n", " hashedCov = hashlib.md5(pickledCov).hexdigest()\n", " return hashedCov" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Remember our model for the Naval Enigma machine? Each message must be decrypted using exactly one trigram while multiple messages may be decrypted by the same trigram. Similarly, we need each input to yield exactly one trace hash while multiple inputs can yield the same trace hash." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let's see whether this is true for our `getTraceHash()` function." ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.368953Z", "iopub.status.busy": "2022-01-11T09:32:28.368328Z", "iopub.status.idle": "2022-01-11T09:32:28.370022Z", "shell.execute_reply": "2022-01-11T09:32:28.370447Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "inp1 = \"a+b\"\n", "inp2 = \"a+b+c\"\n", "inp3 = \"abc\"\n", "\n", "with Coverage() as cov1:\n", " cgi_decode(inp1)\n", "with Coverage() as cov2:\n", " cgi_decode(inp2)\n", "with Coverage() as cov3:\n", " cgi_decode(inp3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "The inputs `inp1` and `inp2` execute the same statements:" ] }, { "cell_type": "code", "execution_count": 56, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.374348Z", "iopub.status.busy": "2022-01-11T09:32:28.373719Z", "iopub.status.idle": "2022-01-11T09:32:28.376361Z", "shell.execute_reply": "2022-01-11T09:32:28.376786Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "('a+b', 'a+b+c')" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp1, inp2" ] }, { "cell_type": "code", "execution_count": 57, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.381079Z", "iopub.status.busy": "2022-01-11T09:32:28.380308Z", "iopub.status.idle": "2022-01-11T09:32:28.383050Z", "shell.execute_reply": "2022-01-11T09:32:28.383474Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "set()" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cov1.coverage() - cov2.coverage()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The difference between both coverage sets is empty. Hence, the trace hashes should be the same:" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.387450Z", "iopub.status.busy": "2022-01-11T09:32:28.386853Z", "iopub.status.idle": "2022-01-11T09:32:28.389476Z", "shell.execute_reply": "2022-01-11T09:32:28.389905Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'2d4e312a1b85056de9908fbc5e445662'" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getTraceHash(cov1)" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.393877Z", "iopub.status.busy": "2022-01-11T09:32:28.393229Z", "iopub.status.idle": "2022-01-11T09:32:28.395845Z", "shell.execute_reply": "2022-01-11T09:32:28.396262Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'2d4e312a1b85056de9908fbc5e445662'" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getTraceHash(cov2)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.402135Z", "iopub.status.busy": "2022-01-11T09:32:28.401256Z", "iopub.status.idle": "2022-01-11T09:32:28.403349Z", "shell.execute_reply": "2022-01-11T09:32:28.404077Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert getTraceHash(cov1) == getTraceHash(cov2)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "In contrast, the inputs `inp1` and `inp3` execute _different_ statements:" ] }, { "cell_type": "code", "execution_count": 61, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.410018Z", "iopub.status.busy": "2022-01-11T09:32:28.409131Z", "iopub.status.idle": "2022-01-11T09:32:28.412286Z", "shell.execute_reply": "2022-01-11T09:32:28.412851Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "('a+b', 'abc')" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inp1, inp3" ] }, { "cell_type": "code", "execution_count": 62, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.419861Z", "iopub.status.busy": "2022-01-11T09:32:28.418862Z", "iopub.status.idle": "2022-01-11T09:32:28.422758Z", "shell.execute_reply": "2022-01-11T09:32:28.423532Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "{('cgi_decode', 27)}" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cov1.coverage() - cov3.coverage()" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Hence, the trace hashes should be different, too:" ] }, { "cell_type": "code", "execution_count": 63, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.429014Z", "iopub.status.busy": "2022-01-11T09:32:28.428256Z", "iopub.status.idle": "2022-01-11T09:32:28.433310Z", "shell.execute_reply": "2022-01-11T09:32:28.434282Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "'2d4e312a1b85056de9908fbc5e445662'" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getTraceHash(cov1)" ] }, { "cell_type": "code", "execution_count": 64, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.439338Z", "iopub.status.busy": "2022-01-11T09:32:28.438472Z", "iopub.status.idle": "2022-01-11T09:32:28.441795Z", "shell.execute_reply": "2022-01-11T09:32:28.442255Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'0c0d5a22062b65f1daee557bc9c1ffdc'" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "getTraceHash(cov3)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.447285Z", "iopub.status.busy": "2022-01-11T09:32:28.445994Z", "iopub.status.idle": "2022-01-11T09:32:28.449059Z", "shell.execute_reply": "2022-01-11T09:32:28.449890Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "assert getTraceHash(cov1) != getTraceHash(cov3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Measuring Trace Coverage over Time\n", "In order to measure trace coverage for a `function` executing a `population` of fuzz inputs, we slightly adapt the `population_coverage()` function from the [Chapter on Coverage](Coverage.ipynb#Coverage-of-Basic-Fuzzing)." ] }, { "cell_type": "code", "execution_count": 66, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.456955Z", "iopub.status.busy": "2022-01-11T09:32:28.456296Z", "iopub.status.idle": "2022-01-11T09:32:28.458416Z", "shell.execute_reply": "2022-01-11T09:32:28.459216Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "def population_trace_coverage(population, function):\n", " cumulative_coverage = []\n", " all_coverage = set()\n", " cumulative_singletons = []\n", " cumulative_doubletons = []\n", " singletons = set()\n", " doubletons = set()\n", "\n", " for s in population:\n", " with Coverage() as cov:\n", " try:\n", " function(s)\n", " except BaseException:\n", " pass\n", " cur_coverage = set([getTraceHash(cov)])\n", "\n", " # singletons and doubletons -- we will need them later\n", " doubletons -= cur_coverage\n", " doubletons |= singletons & cur_coverage\n", " singletons -= cur_coverage\n", " singletons |= cur_coverage - (cur_coverage & all_coverage)\n", " cumulative_singletons.append(len(singletons))\n", " cumulative_doubletons.append(len(doubletons))\n", "\n", " # all and cumulative coverage\n", " all_coverage |= cur_coverage\n", " cumulative_coverage.append(len(all_coverage))\n", "\n", " return all_coverage, cumulative_coverage, cumulative_singletons, cumulative_doubletons" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Let's see whether our new function really contains coverage information only for *two* traces given our three inputs for `cgi_decode`." ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.464186Z", "iopub.status.busy": "2022-01-11T09:32:28.463148Z", "iopub.status.idle": "2022-01-11T09:32:28.465500Z", "shell.execute_reply": "2022-01-11T09:32:28.466130Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "all_coverage = population_trace_coverage([inp1, inp2, inp3], cgi_decode)[0]\n", "assert len(all_coverage) == 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Unfortunately, the `cgi_decode()` function is too simple. Instead, we will use the original Python [HTMLParser](https://docs.python.org/3/library/html.parser.html) as our test subject." ] }, { "cell_type": "code", "execution_count": 68, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.471364Z", "iopub.status.busy": "2022-01-11T09:32:28.470372Z", "iopub.status.idle": "2022-01-11T09:32:28.473560Z", "shell.execute_reply": "2022-01-11T09:32:28.474380Z" }, "slideshow": { "slide_type": "skip" } }, "outputs": [], "source": [ "from Fuzzer import RandomFuzzer\n", "from Coverage import population_coverage\n", "from html.parser import HTMLParser" ] }, { "cell_type": "code", "execution_count": 69, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.480842Z", "iopub.status.busy": "2022-01-11T09:32:28.479644Z", "iopub.status.idle": "2022-01-11T09:32:28.482272Z", "shell.execute_reply": "2022-01-11T09:32:28.483336Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "trials = 50000 # number of random inputs generated" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let's run a random fuzzer for $n=50000$ times and plot trace coverage over time." ] }, { "cell_type": "code", "execution_count": 70, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.489682Z", "iopub.status.busy": "2022-01-11T09:32:28.488634Z", "iopub.status.idle": "2022-01-11T09:32:28.490847Z", "shell.execute_reply": "2022-01-11T09:32:28.491708Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# create wrapper function\n", "def my_parser(inp):\n", " parser = HTMLParser() # resets the HTMLParser object for every fuzz input\n", " parser.feed(inp)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:28.571564Z", "iopub.status.busy": "2022-01-11T09:32:28.529036Z", "iopub.status.idle": "2022-01-11T09:32:41.975791Z", "shell.execute_reply": "2022-01-11T09:32:41.976167Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# create random fuzzer\n", "fuzzer = RandomFuzzer(min_length=1, max_length=100,\n", " char_start=32, char_range=94)\n", "\n", "# create population of fuzz inputs\n", "population = []\n", "for i in range(trials):\n", " population.append(fuzzer.fuzz())\n", "\n", "# execute and measure trace coverage\n", "trace_timeseries = population_trace_coverage(population, my_parser)[1]\n", "\n", "# execute and measure code coverage\n", "code_timeseries = population_coverage(population, my_parser)[1]\n", "\n", "# plot trace coverage over time\n", "plt.figure(num=None, figsize=(12, 4), dpi=80, facecolor='w', edgecolor='k')\n", "plt.subplot(1, 2, 1)\n", "plt.plot(trace_timeseries)\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of traces exercised')\n", "plt.title('Trace Coverage Over Time')\n", "\n", "# plot code coverage over time\n", "plt.subplot(1, 2, 2)\n", "plt.plot(code_timeseries)\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of statements covered')\n", "plt.title('Code Coverage Over Time');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Above, we can see trace coverage (left) and code coverage (right) over time. Here are our observations.\n", "1. **Trace coverage is more robust**. There are less sudden jumps in the graph compared to code coverage.\n", "2. **Trace coverage is more fine grained.** There are more traces than statements covered in the end (y-axis).\n", "3. **Trace coverage grows more steadily**. Code coverage exercises more than half the statements it has exercised after 50k inputs with the first input. Instead, the number of traces covered grows slowly and steadily since each input can yield only one execution trace.\n", "\n", "It is for this reason that one of the most prominent and successful fuzzers today, american fuzzy lop (AFL), uses a similar *measure of progress* (a hash computed over the branches exercised by the input)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Evaluating the Discovery Probability Estimate\n", "\n", "Let's find out how the Good-Turing estimator performs as estimate of discovery probability when we are fuzzing to discover execution traces rather than trigrams. \n", "\n", "To measure the empirical probability, we execute the same population of inputs (n=50000) and measure in regular intervals (`measurements=100` intervals). During each measurement, we repeat the following experiment `repeats=500` times, reporting the average: If the next input yields a new trace, return 1, otherwise return 0. Note that during these repetitions, we do not record the newly discovered traces as observed." ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:41.980156Z", "iopub.status.busy": "2022-01-11T09:32:41.979515Z", "iopub.status.idle": "2022-01-11T09:32:41.981346Z", "shell.execute_reply": "2022-01-11T09:32:41.981723Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "repeats = 500 # experiment repetitions\n", "measurements = 100 # experiment measurements" ] }, { "cell_type": "code", "execution_count": 73, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:42.040212Z", "iopub.status.busy": "2022-01-11T09:32:42.000853Z", "iopub.status.idle": "2022-01-11T09:32:57.038774Z", "shell.execute_reply": "2022-01-11T09:32:57.039630Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "emp_timeseries = []\n", "all_coverage = set()\n", "step = int(trials / measurements)\n", "\n", "for i in range(0, trials, step):\n", " if i - step >= 0:\n", " for j in range(step):\n", " inp = population[i - j]\n", " with Coverage() as cov:\n", " try:\n", " my_parser(inp)\n", " except BaseException:\n", " pass\n", " all_coverage |= set([getTraceHash(cov)])\n", "\n", " discoveries = 0\n", " for _ in range(repeats):\n", " inp = fuzzer.fuzz()\n", " with Coverage() as cov:\n", " try:\n", " my_parser(inp)\n", " except BaseException:\n", " pass\n", " if getTraceHash(cov) not in all_coverage:\n", " discoveries += 1\n", " emp_timeseries.append(discoveries / repeats)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Now, we compute the Good-Turing estimate over time." ] }, { "cell_type": "code", "execution_count": 74, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:32:57.094300Z", "iopub.status.busy": "2022-01-11T09:32:57.055134Z", "iopub.status.idle": "2022-01-11T09:33:03.344265Z", "shell.execute_reply": "2022-01-11T09:33:03.345912Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "gt_timeseries = []\n", "singleton_timeseries = population_trace_coverage(population, my_parser)[2]\n", "for i in range(1, trials + 1, step):\n", " gt_timeseries.append(singleton_timeseries[i - 1] / i)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Let's go ahead and plot both time series." ] }, { "cell_type": "code", "execution_count": 75, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:03.368666Z", "iopub.status.busy": "2022-01-11T09:33:03.365300Z", "iopub.status.idle": "2022-01-11T09:33:04.643160Z", "shell.execute_reply": "2022-01-11T09:33:04.642260Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "line_emp, = plt.semilogy(emp_timeseries, label=\"Empirical\")\n", "line_gt, = plt.semilogy(gt_timeseries, label=\"Good-Turing\")\n", "plt.legend(handles=[line_emp, line_gt])\n", "plt.xticks(range(0, measurements + 1, int(measurements / 5)),\n", " range(0, trials + 1, int(trials / 5)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('discovery probability')\n", "plt.title('Discovery Probability Over Time');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "Again, the Good-Turing estimate appears to be *highly accurate*. In fact, the empirical estimator has a much lower precision as indicated by the large swings. You can try and increase the number of repetitions (`repeats`) to get more precision for the empirical estimates, however, at the cost of waiting much longer." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "### Discovery Probability Quantifies Residual Risk\n", "\n", "Alright. You have gotten a hold of a couple of powerful machines and used them to fuzz a software system for several months without finding any vulnerabilities. Is the system vulnerable?\n", "\n", "Well, who knows? We cannot say for sure; there is always some residual risk. Testing is not verification. Maybe the next test input that is generated reveals a vulnerability.\n", "\n", "Let's say *residual risk* is the probability that the next test input reveals a vulnerability that has not been found, yet. Böhme \\cite{Boehme2018stads} has shown that the Good-Turing estimate of the discovery probability is also an estimate of the maximum residual risk." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "**Proof sketch (Residual Risk)**. Here is a proof sketch that shows that an estimator of discovery probability for an arbitrary definition of species gives an upper bound on the probability to discover a vulnerability when none has been found: Suppose, for each \"old\" species A (here, execution trace), we derive two \"new\" species: Some inputs belonging to A expose a vulnerability while others belonging to A do not. We know that _only_ species that do not expose a vulnerability have been discovered. Hence, _all_ species exposing a vulnerability and _some_ species that do not expose a vulnerability remain undiscovered. Hence, the probability to discover a new species gives an upper bound on the probability to discover (a species that exposes) a vulnerability. **QED**." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "An estimate of the discovery probability is useful in many other ways.\n", "\n", "1. **Discovery probability**. We can estimate, at any point during the fuzzing campaign, the probability that the next input belongs to a previously unseen species (here, that it yields a new execution trace, i.e., exercises a new set of statements).\n", "2. **Complement of discovery probability**. We can estimate the proportion of *all* inputs the fuzzer can generate for which we have already seen the species (here, execution traces). In some sense, this allows us to quantify the *progress of the fuzzing campaign towards completion*: If the probability to discovery a new species is too low, we might as well abort the campaign.\n", "3. **Inverse of discovery probability**. We can predict the number of test inputs needed, so that we can expect the discovery of a new species (here, execution trace)." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## How Do We Know When to Stop Fuzzing?\n", "\n", "In fuzzing, we have measures of progress such as [code coverage](Coverage.ipynb) or [grammar coverage](GrammarCoverageFuzzer.ipynb). Suppose, we are interested in covering all statements in the program. The _percentage_ of statements that have already been covered quantifies how \"far\" we are from completing the fuzzing campaign. However, sometimes we know only the _number_ of species $S(n)$ (here, statements) that have been discovered after generating $n$ fuzz inputs. The percentage $S(n)/S$ can only be computed if we know the _total number_ of species $S$. Even then, not all species may be feasible." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "### A Success Estimator\n", "\n", "If we do not _know_ the total number of species, then let's at least _estimate_ it: As we have seen before, species discovery slows down over time. In the beginning, many new species are discovered. Later, many inputs need to be generated before discovering the next species. In fact, given enough time, the fuzzing campaign approaches an _asymptote_. It is this asymptote that we can estimate." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "In 1984, Anne Chao, a well-known theoretical bio-statistician, has developed an estimator $\\hat S$ which estimates the asymptotic total number of species $S$:\n", "\\begin{align}\n", "\\hat S_\\text{Chao1} = \\begin{cases}\n", "S(n) + \\frac{f_1^2}{2f_2} & \\text{if $f_2>0$}\\\\\n", "S(n) + \\frac{f_1(f_1-1)}{2} & \\text{otherwise}\n", "\\end{cases}\n", "\\end{align}\n", "* where $f_1$ and $f_2$ is the number of singleton and doubleton species, respectively (that have been observed exactly once or twice, resp.), and \n", "* where $S(n)$ is the number of species that have been discovered after generating $n$ fuzz inputs." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "So, how does Chao's estimate perform? To investigate this, we generate `trials=400000` fuzz inputs using a fuzzer setting that allows us to see an asymptote in a few seconds: We measure trace coverage. After half-way into our fuzzing campaign (`trials`/2=100000), we generate Chao's estimate $\\hat S$ of the asymptotic total number of species. Then, we run the remainer of the campaign to see the \"empirical\" asymptote." ] }, { "cell_type": "code", "execution_count": 76, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:04.735904Z", "iopub.status.busy": "2022-01-11T09:33:04.696873Z", "iopub.status.idle": "2022-01-11T09:33:41.856352Z", "shell.execute_reply": "2022-01-11T09:33:41.856813Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "trials = 400000\n", "fuzzer = RandomFuzzer(min_length=2, max_length=4,\n", " char_start=32, char_range=32)\n", "population = []\n", "for i in range(trials):\n", " population.append(fuzzer.fuzz())\n", "\n", "_, trace_ts, f1_ts, f2_ts = population_trace_coverage(population, my_parser)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.860870Z", "iopub.status.busy": "2022-01-11T09:33:41.860267Z", "iopub.status.idle": "2022-01-11T09:33:41.863249Z", "shell.execute_reply": "2022-01-11T09:33:41.863675Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "200000" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "time = int(trials / 2)\n", "time" ] }, { "cell_type": "code", "execution_count": 78, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.867684Z", "iopub.status.busy": "2022-01-11T09:33:41.867154Z", "iopub.status.idle": "2022-01-11T09:33:41.869427Z", "shell.execute_reply": "2022-01-11T09:33:41.869004Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "f1 = f1_ts[time]\n", "f2 = f2_ts[time]\n", "Sn = trace_ts[time]\n", "if f2 > 0:\n", " hat_S = Sn + f1 * f1 / (2 * f2)\n", "else:\n", " hat_S = Sn + f1 * (f1 - 1) / 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "After executing `time` fuzz inputs (half of all), we have covered this many traces:" ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.872934Z", "iopub.status.busy": "2022-01-11T09:33:41.872362Z", "iopub.status.idle": "2022-01-11T09:33:41.874773Z", "shell.execute_reply": "2022-01-11T09:33:41.875190Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "200000" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "time" ] }, { "cell_type": "code", "execution_count": 80, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.878500Z", "iopub.status.busy": "2022-01-11T09:33:41.877936Z", "iopub.status.idle": "2022-01-11T09:33:41.880273Z", "shell.execute_reply": "2022-01-11T09:33:41.880732Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "text/plain": [ "66" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Sn" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "We can estimate there are this many traces in total:" ] }, { "cell_type": "code", "execution_count": 81, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.884155Z", "iopub.status.busy": "2022-01-11T09:33:41.883502Z", "iopub.status.idle": "2022-01-11T09:33:41.885883Z", "shell.execute_reply": "2022-01-11T09:33:41.886337Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "73.2" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hat_S" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Hence, we have achieved this percentage of the estimate:" ] }, { "cell_type": "code", "execution_count": 82, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.890227Z", "iopub.status.busy": "2022-01-11T09:33:41.889596Z", "iopub.status.idle": "2022-01-11T09:33:41.892119Z", "shell.execute_reply": "2022-01-11T09:33:41.892511Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "90.1639344262295" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "100 * Sn / hat_S" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "After executing `trials` fuzz inputs, we have covered this many traces:" ] }, { "cell_type": "code", "execution_count": 83, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.896134Z", "iopub.status.busy": "2022-01-11T09:33:41.895528Z", "iopub.status.idle": "2022-01-11T09:33:41.898139Z", "shell.execute_reply": "2022-01-11T09:33:41.898529Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "400000" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trials" ] }, { "cell_type": "code", "execution_count": 84, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.902094Z", "iopub.status.busy": "2022-01-11T09:33:41.901534Z", "iopub.status.idle": "2022-01-11T09:33:41.903785Z", "shell.execute_reply": "2022-01-11T09:33:41.904231Z" }, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "72" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trace_ts[trials - 1]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The accuracy of Chao's estimator is quite reasonable. It isn't always accurate -- particularly at the beginning of a fuzzing campaign when the [discovery probability](WhenIsEnough.ipynb#Measuring-Trace-Coverage-over-Time) is still very high. Nevertheless, it demonstrates the main benefit of reporting a percentage to assess the progress of a fuzzing campaign towards completion.\n", "\n", "***Try it***. *Try setting `trials` to 1 million and `time` to `int(trials / 4)`.*" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "### Extrapolating Fuzzing Success\n", "\n", "\n", "Suppose you have run the fuzzer for a week, which generated $n$ fuzz inputs and discovered $S(n)$ species (here, covered $S(n)$ execution traces). Instead, of running the fuzzer for another week, you would like to *predict* how many more species you would discover. In 2003, Anne Chao and her team developed an extrapolation methodology to do just that. We are interested in the number $S(n+m^*)$ of species discovered if $m^*$ more fuzz inputs were generated:\n", "\n", "\\begin{align}\n", "\\hat S(n + m^*) = S(n) + \\hat f_0 \\left[1-\\left(1-\\frac{f_1}{n\\hat f_0 + f_1}\\right)^{m^*}\\right]\n", "\\end{align}\n", "* where $\\hat f_0=\\hat S - S(n)$ is an estimate of the number $f_0$ of undiscovered species, and \n", "* where $f_1$ is the number of singleton species, i.e., those we have observed exactly once. \n", "\n", "The number $f_1$ of singletons, we can just keep track of during the fuzzing campaign itself. The estimate of the number $\\hat f_0$ of undiscovered species, we can simply derive using Chao's estimate $\\hat S$ and the number of observed species $S(n)$.\n", "\n", "Let's see how Chao's extrapolator performs by comparing the predicted number of species to the empirical number of species." ] }, { "cell_type": "code", "execution_count": 85, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:41.991967Z", "iopub.status.busy": "2022-01-11T09:33:41.953377Z", "iopub.status.idle": "2022-01-11T09:33:42.030006Z", "shell.execute_reply": "2022-01-11T09:33:42.030439Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [], "source": [ "prediction_ts = [None] * time\n", "f0 = hat_S - Sn\n", "\n", "for m in range(trials - time):\n", " assert (time * f0 + f1) != 0 , 'time:%s f0:%s f1:%s' % (time, f0,f1)\n", " prediction_ts.append(Sn + f0 * (1 - (1 - f1 / (time * f0 + f1)) ** m))" ] }, { "cell_type": "code", "execution_count": 86, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:42.094487Z", "iopub.status.busy": "2022-01-11T09:33:42.065982Z", "iopub.status.idle": "2022-01-11T09:33:44.390330Z", "shell.execute_reply": "2022-01-11T09:33:44.390745Z" }, "slideshow": { "slide_type": "subslide" } }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(num=None, figsize=(12, 3), dpi=80, facecolor='w', edgecolor='k')\n", "plt.subplot(1, 3, 1)\n", "plt.plot(trace_ts, color='white')\n", "plt.plot(trace_ts[:time])\n", "plt.xticks(range(0, trials + 1, int(time)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of traces exercised')\n", "\n", "plt.subplot(1, 3, 2)\n", "line_cur, = plt.plot(trace_ts[:time], label=\"Ongoing fuzzing campaign\")\n", "line_pred, = plt.plot(prediction_ts, linestyle='--',\n", " color='black', label=\"Predicted progress\")\n", "plt.legend(handles=[line_cur, line_pred])\n", "plt.xticks(range(0, trials + 1, int(time)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of traces exercised')\n", "\n", "plt.subplot(1, 3, 3)\n", "line_emp, = plt.plot(trace_ts, color='grey', label=\"Actual progress\")\n", "line_cur, = plt.plot(trace_ts[:time], label=\"Ongoing fuzzing campaign\")\n", "line_pred, = plt.plot(prediction_ts, linestyle='--',\n", " color='black', label=\"Predicted progress\")\n", "plt.legend(handles=[line_emp, line_cur, line_pred])\n", "plt.xticks(range(0, trials + 1, int(time)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of traces exercised');" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "subslide" } }, "source": [ "The prediction from Chao's extrapolator looks quite accurate. We make a prediction at `time=trials/4`. Despite an extrapolation by 3 times (i.e., at trials), we can see that the predicted value (black, dashed line) closely matches the empirical value (grey, solid line).\n", "\n", "***Try it***. Again, try setting `trials` to 1 million and `time` to `int(trials / 4)`." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Lessons Learned\n", "\n", "* One can measure the _progress_ of a fuzzing campaign (as species over time, i.e., $S(n)$).\n", "* One can measure the _effectiveness_ of a fuzzing campaign (as asymptotic total number of species $S$).\n", "* One can estimate the _effectiveness_ of a fuzzing campaign using the Chao1-estimator $\\hat S$.\n", "* One can extrapolate the _progress_ of a fuzzing campaign, $\\hat S(n+m^*)$.\n", "* One can estimate the _residual risk_ (i.e., the probability that a bug exists that has not been found) using the Good-Turing estimator $GT$ of the species discovery probability." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Next Steps\n", "\n", "This chapter is the last in the book! If you want to continue reading, have a look at the [Appendices](99_Appendices.ipynb). Otherwise, _make use of what you have learned and go and create great fuzzers and test generators!_" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Background\n", "\n", "* A **statistical framework for fuzzing**, inspired from ecology. Marcel Böhme. [STADS: Software Testing as Species Discovery](https://mboehme.github.io/paper/TOSEM18.pdf). ACM TOSEM 27(2):1--52\n", "* Estimating the **discovery probability**: I.J. Good. 1953. [The population frequencies of species and the\n", "estimation of population parameters](https://www.jstor.org/stable/2333344). Biometrika 40:237–264.\n", "* Estimating the **asymptotic total number of species** when each input can belong to exactly one species: Anne Chao. 1984. [Nonparametric estimation of the number of classes in a population](https://www.jstor.org/stable/4615964). Scandinavian Journal of Statistics 11:265–270\n", "* Estimating the **asymptotic total number of species** when each input can belong to one or more species: Anne Chao. 1987. [Estimating the population size for capture-recapture data with unequal catchability](https://www.jstor.org/stable/2531532). Biometrics 43:783–791\n", "* **Extrapolating** the number of discovered species: Tsung-Jen Shen, Anne Chao, and Chih-Feng Lin. 2003. [Predicting the Number of New Species in Further Taxonomic Sampling](http://chao.stat.nthu.edu.tw/wordpress/paper/2003_Ecology_84_P798.pdf). Ecology 84, 3 (2003), 798–804." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": true, "run_control": { "read_only": false }, "slideshow": { "slide_type": "slide" } }, "source": [ "## Exercises\n", "I.J. Good and Alan Turing developed an estimator for the case where each input belongs to exactly one species. For instance, each input yields exactly one execution trace (see function [`getTraceHash`](#Trace-Coverage)). However, this is not true in general. For instance, each input exercises multiple statements and branches in the source code. Generally, each input can belong to one *or more* species. \n", "\n", "In this extended model, the underlying statistics are quite different. Yet, all estimators that we have discussed in this chapter turn out to be almost identical to those for the simple, single-species model. For instance, the Good-Turing estimator $C$ is defined as \n", "$$C=\\frac{Q_1}{n}$$ \n", "where $Q_1$ is the number of singleton species and $n$ is the number of generated test cases.\n", "Throughout the fuzzing campaign, we record for each species the *incidence frequency*, i.e., the number of inputs that belong to that species. Again, we define a species $i$ as *singleton species* if we have seen exactly one input that belongs to species $i$." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "solution2": "shown", "solution2_first": true }, "source": [ "### Exercise 1: Estimate and Evaluate the Discovery Probability for Statement Coverage\n", "\n", "In this exercise, we create a Good-Turing estimator for the simple fuzzer." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "solution2": "shown", "solution2_first": true }, "source": [ "#### Part 1: Population Coverage\n", "\n", "Implement a function `population_stmt_coverage()` as in [the section on estimating discovery probability](#Estimating-the-Discovery-Probability) that monitors the number of singletons and doubletons over time, i.e., as the number $i$ of test inputs increases." ] }, { "cell_type": "code", "execution_count": 87, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:44.395737Z", "iopub.status.busy": "2022-01-11T09:33:44.395053Z", "iopub.status.idle": "2022-01-11T09:33:44.396735Z", "shell.execute_reply": "2022-01-11T09:33:44.397322Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "from Coverage import population_coverage, Coverage\n", "..." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** Here we go:" ] }, { "cell_type": "code", "execution_count": 88, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:44.404931Z", "iopub.status.busy": "2022-01-11T09:33:44.403883Z", "iopub.status.idle": "2022-01-11T09:33:44.406020Z", "shell.execute_reply": "2022-01-11T09:33:44.406463Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "def population_stmt_coverage(population, function):\n", " cumulative_coverage = []\n", " all_coverage = set()\n", " cumulative_singletons = []\n", " cumulative_doubletons = []\n", " singletons = set()\n", " doubletons = set()\n", "\n", " for s in population:\n", " with Coverage() as cov:\n", " try:\n", " function(s)\n", " except BaseException:\n", " pass\n", " cur_coverage = cov.coverage()\n", "\n", " # singletons and doubletons\n", " doubletons -= cur_coverage\n", " doubletons |= singletons & cur_coverage\n", " singletons -= cur_coverage\n", " singletons |= cur_coverage - (cur_coverage & all_coverage)\n", " cumulative_singletons.append(len(singletons))\n", " cumulative_doubletons.append(len(doubletons))\n", "\n", " # all and cumulative coverage\n", " all_coverage |= cur_coverage\n", " cumulative_coverage.append(len(all_coverage))\n", "\n", " return all_coverage, cumulative_coverage, cumulative_singletons, cumulative_doubletons" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "solution2": "shown", "solution2_first": true }, "source": [ "#### Part 2: Population\n", "\n", "Use the random `fuzzer(min_length=1, max_length=1000, char_start=0, char_range=255)` from [the chapter on Fuzzers](Fuzzer.ipynb) to generate a population of $n=10000$ fuzz inputs." ] }, { "cell_type": "code", "execution_count": 89, "metadata": { "cell_style": "split", "execution": { "iopub.execute_input": "2022-01-11T09:33:44.411176Z", "iopub.status.busy": "2022-01-11T09:33:44.410540Z", "iopub.status.idle": "2022-01-11T09:33:44.412241Z", "shell.execute_reply": "2022-01-11T09:33:44.412630Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden", "solution2_first": true }, "outputs": [], "source": [ "from Fuzzer import RandomFuzzer\n", "from html.parser import HTMLParser\n", "...;" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** This is fairly straightforward:" ] }, { "cell_type": "code", "execution_count": 90, "metadata": { "cell_style": "split", "execution": { "iopub.execute_input": "2022-01-11T09:33:44.416881Z", "iopub.status.busy": "2022-01-11T09:33:44.415918Z", "iopub.status.idle": "2022-01-11T09:33:44.419107Z", "shell.execute_reply": "2022-01-11T09:33:44.420095Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "trials = 2000 # increase to 10000 for better convergences. Will take a while.." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "We create a wrapper function..." ] }, { "cell_type": "code", "execution_count": 91, "metadata": { "cell_style": "split", "execution": { "iopub.execute_input": "2022-01-11T09:33:44.425324Z", "iopub.status.busy": "2022-01-11T09:33:44.424486Z", "iopub.status.idle": "2022-01-11T09:33:44.426716Z", "shell.execute_reply": "2022-01-11T09:33:44.427496Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "def html_parser(inp):\n", " parser = HTMLParser() # resets the HTMLParser object for every fuzz input\n", " parser.feed(inp)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "... and a random fuzzer:" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "cell_style": "split", "execution": { "iopub.execute_input": "2022-01-11T09:33:44.432079Z", "iopub.status.busy": "2022-01-11T09:33:44.431183Z", "iopub.status.idle": "2022-01-11T09:33:44.433906Z", "shell.execute_reply": "2022-01-11T09:33:44.434545Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "fuzzer = RandomFuzzer(min_length=1, max_length=1000,\n", " char_start=0, char_range=255)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "We fill the population:" ] }, { "cell_type": "code", "execution_count": 93, "metadata": { "cell_style": "split", "execution": { "iopub.execute_input": "2022-01-11T09:33:44.526024Z", "iopub.status.busy": "2022-01-11T09:33:44.487895Z", "iopub.status.idle": "2022-01-11T09:33:45.512038Z", "shell.execute_reply": "2022-01-11T09:33:45.512632Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "population = []\n", "for i in range(trials):\n", " population.append(fuzzer.fuzz())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "solution2": "hidden", "solution2_first": true }, "source": [ "#### Part 3: Estimating Probabilities\n", "\n", "Execute the generated inputs on the Python HTML parser (`from html.parser import HTMLParser`) and estimate the probability that the next input covers a previously uncovered statement (i.e., the discovery probability) using the Good-Turing estimator." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** Here we go:" ] }, { "cell_type": "code", "execution_count": 94, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:45.592129Z", "iopub.status.busy": "2022-01-11T09:33:45.549246Z", "iopub.status.idle": "2022-01-11T09:33:46.117586Z", "shell.execute_reply": "2022-01-11T09:33:46.118050Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "measurements = 100 # experiment measurements\n", "step = int(trials / measurements)\n", "\n", "gt_timeseries = []\n", "singleton_timeseries = population_stmt_coverage(population, my_parser)[2]\n", "for i in range(1, trials + 1, step):\n", " gt_timeseries.append(singleton_timeseries[i - 1] / i)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" }, "solution2": "hidden", "solution2_first": true }, "source": [ "#### Part 4: Empirical Evaluation\n", "\n", "Empirically evaluate the accuracy of the Good-Turing estimator (using $10000$ repetitions) of the probability to cover new statements using the experimental procedure at the end of [the section on estimating discovery probability](#Estimating-the-Discovery-Probability)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** This is as above:" ] }, { "cell_type": "code", "execution_count": 95, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:46.121792Z", "iopub.status.busy": "2022-01-11T09:33:46.121225Z", "iopub.status.idle": "2022-01-11T09:33:46.122784Z", "shell.execute_reply": "2022-01-11T09:33:46.123172Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "# increase to 10000 for better precision (less variance). Will take a while..\n", "repeats = 100" ] }, { "cell_type": "code", "execution_count": 96, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:46.204915Z", "iopub.status.busy": "2022-01-11T09:33:46.164512Z", "iopub.status.idle": "2022-01-11T09:33:54.725662Z", "shell.execute_reply": "2022-01-11T09:33:54.726094Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "emp_timeseries = []\n", "all_coverage = set()\n", "for i in range(0, trials, step):\n", " if i - step >= 0:\n", " for j in range(step):\n", " inp = population[i - j]\n", " with Coverage() as cov:\n", " try:\n", " my_parser(inp)\n", " except BaseException:\n", " pass\n", " all_coverage |= cov.coverage()\n", "\n", " discoveries = 0\n", " for _ in range(repeats):\n", " inp = fuzzer.fuzz()\n", " with Coverage() as cov:\n", " try:\n", " my_parser(inp)\n", " except BaseException:\n", " pass\n", " # If intersection not empty, a new stmt was (dis)covered\n", " if cov.coverage() - all_coverage:\n", " discoveries += 1\n", " emp_timeseries.append(discoveries / repeats)" ] }, { "cell_type": "code", "execution_count": 97, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:54.767169Z", "iopub.status.busy": "2022-01-11T09:33:54.766483Z", "iopub.status.idle": "2022-01-11T09:33:55.421553Z", "shell.execute_reply": "2022-01-11T09:33:55.422078Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "image/png": "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zi233MLQoUMZMWIECxYs4OSTT6Znz5489thjAMyaNYtjjjmG0047jb59+3LVVVdRXFxc8v6dO3eydu1a+vbty0UXXcSgQYPYsGFDyWsAzz77LIcccghDhgzhwgsvBODdd9/lsMMOY9iwYZxwwgls27at4iDrWEyVLELmS4b8/dGOwphG5453l/Lj5n1hPeaAjs346/iBVe6Tk5PD0KFDS9ZvueUWJkxwMwN37dqVhQsXcsMNN3DJJZfw1VdfkZuby6BBg7jqqqsAmDt3Lj/++CPdunXjlFNO4c033+Tcc88tc46VK1fyzDPPcPjhh5fZvnTpUu6++26+/vprWrduze7duwE4+uijmTNnDiLC448/zv33388///nPUP85QhZTycKqoYwxNVFVNdTpp58OwODBg9m/fz9paWmkpaWRlJRU0uYwatQoevToAcCkSZOYPXv2QcmiW7duByUKgJkzZ3LeeefRunVrANLT0wHYuHEjEyZMYMuWLeTn5x90L0S0xFSyUNV3gXdHjBhxRa0OkJgCRflQVADxwd/ZaIwJTXUlgGhISkoCIC4uruS5f72wsBA4uAtqRV1SU1JSanTeyZMnc+ONN3L66acza9Ysbr/99hpGHhkx1WYRMl+ye8w/EN04jDENwty5c1mzZg3FxcW88sorHH300UG/d8yYMbz22mvs2rULoKQaKjMzk06dOgHwzDP1p3emJYtAPu8XgFVFGdMo+Nss/MuUKVNq9P6RI0dy3XXX0b9/fzIyMjjrrLOCfu/AgQO59dZbOfbYYxkyZAg33ngjALfffjvnnXcew4cPL6miqg9sbKhAi16DNy+H6+ZB697hD8wYU2LZsmX0798/2mHU2qxZs3jggQeYPn16tEOplYr+/W1sqGD5SxZWDWWMMWXEVLII+T4Lf5uFVUMZY6px3HHHNdhSRW3EVLIIWaKVLIwxpiKWLAJZNZQxxlTIkkUg6zprjDEVsmQRKNG6zhpjTEUsWQSyaihjGp1t27Zx/vnn06NHD4YPH84RRxzBW2+9FfJxjzvuOAK78C9evLjkfo709HQyMjIYOnQoJ5xwQlDH27x580FDidSlmBruI+SxoRKbAmLJwphGQlU588wzufjii3nxxRcBWLduHdOmTQv7uQYPHlwyDtUll1zCuHHjgv7yLywspGPHjrz++uthjytYMVWyCLnrrIgNJmhMIzJz5kx8Pl/JKLLgBv6bPHkyubm5XHrppQwePJhhw4bx2WefAVS6PScnh4kTJ9K/f3/OOusscnJygoohsASyc+dOunfvDrjJjk4//XTGjBnD2LFjWbt2LYMGDSp57eyzz+aUU06hd+/e3HzzzSXHe+KJJ+jTpw+jRo3iiiuuCNuESTFVsgiLxGQrWRhT196fAlsXh/eY7QfDqVXPfLd06VIOPfTQCl97+OGHEREWL17M8uXLOemkk1ixYkWl2x999FGSk5NZtmwZixYtqvS4NbFgwQIWLVpEeno6a9euLfPawoUL+f7770lKSqJv375MnjyZ+Ph47rrrLhYsWEBaWhpjxoxhyJAhIccBliwO5rNkYUxjde211zJ79mx8Ph+dO3dm8uTJAPTr149u3bqxYsUKZs+eXeH2L774guuvvx6AQw45hEMOOSTkeE488cSSocvLGzt2LP5alAEDBrBu3Tp27tzJscceW/Ke8847jxUrVoQcB1iyOJgv1aqhjKlr1ZQAImXgwIG88cYbJesPP/wwO3fuZMSIEXTu3Dks53jrrbe44447AHj88ccZMaLs0EsJCQklM+zl5uaWea2q4c0Dh02Pj48vGTY9UmKqzSIsEm22PGMaizFjxpCbm8ujjz5asi072/1YHD16NC+88AIAK1asYP369fTt27fS7cccc0xJI/mSJUtYtGgRAGeddRYLFy5k4cKFByUKcFOwzp8/HyDkBuyRI0fy+eefs2fPHgoLC8skwlBZsijPl2zzcBvTSIgIb7/9Np9//jkZGRmMGjWKiy++mPvuu49rrrmG4uJiBg8ezIQJE3j66adJSkqqdPvVV1/N/v376d+/P7fddhvDhw8PKoY//OEPPProowwbNqxkbu7a6tSpE3/6058YNWoURx11FN27d6fWHX7KsSHKy3v5AtizFq7+KqwxGWPKauhDlNdX+/fvJzU1lcLCQs466ywuu+yyCufZaNRDlIeFVUMZYxqw22+/naFDhzJo0CAyMjI488wzw3Lcet/ALSI9gFuB5qoa+dsXfSlWDWWMabAeeOCBiBw3KiULEXlSRLaLyJJy208RkZ9EZJWITAFQ1dWq+us6C85uyjOmzsRiNXhDUJt/92hVQz0NnBK4QUTigYeBU4EBwCQRGVDnkflvyrMPsTER1aRJE3bt2mUJo46pKrt27aJJkyY1el9UqqFU9QsR6V5u8yhglaquBhCRl4EzgB+DOaaIXAlcCdC1a9faB+dLARQKckqHLDfGhF3nzp3ZuHEjO3bsiHYojU6TJk1qfB9JfWqz6ARsCFjfCBwmIq2AvwHDROQWVb2nojer6lRgKrjeULWOInDkWUsWxkRMYmIiGRkZ0Q7DBKk+JYsKqeou4KpqdyQMo86Cq4YCKDgAtKn9cYwxJobUp66zm4AuAeudvW1BC3nUWQgoWVgjtzHG+NWnZPEd0FtEMkTEB0wEajSovIiMF5GpmZmZtY/CJkAyxpiDRKvr7EvAN0BfEdkoIr9W1ULgOuBDYBnwqqourclxw1KyKFMNZYwxBqLXG2pSJdtnADNqe9ywtFlYNZQxxhykPlVDhSy8bRZWsjDGGL+YShZh4U8WVg1ljDElYipZhKWB299mYSULY4wpEVPJwrrOGmNMZMRUsgiL+ESI91k1lDHGBIipZBGWaigoHUzQGGMMEGPJIizVUGBzWhhjTDkxlSzCxpdi1VDGGBPAkkVFrBrKGGPKiKlkEbY2C1+qVUMZY0yAmEoW4WuzSIb8/eEJyhhjYkBMJYuwSUy2ebiNMSaAJYuKWDWUMcaUYcmiIlYNZYwxZVSbLERkvohcKyIt6yKgUIT1pjyrhjLGmBLBlCwmAB2B70TkZRE5WUQkwnHVSvgauFOhKB+KCsITmDHGNHDVJgtVXaWqtwJ9gBeBJ4F1InKHiKRHOsCo8NnIs8YYEyioNgsROQT4J/AP4A3gPGAfMDNyoUVRyZwWVhVljDEQxLSqIjIf2As8AUxR1TzvpW9F5KgIxhY9iTZbnjHGBApmDu7zVHV14AYRyVDVNap6doTiii6rhjLGmDKCqYZ6PchtscOqoYwxpoxKSxYi0g8YCDQXkcASRDOgSaQDqw0RGQ+M79WrV2gHsmooY4wpo6pqqL7AOKAFMD5gexZwRQRjqjVVfRd4d8SIEaHFZ9VQxhhTRqXJQlXfAd4RkSNU9Zs6jCn6fFayMMaYQFVVQ92sqvcD54vIpPKvq+r1EY0smhKtzcIYYwJVVQ21zHucVxeB1CtWsjDGmDKqqoZ613t8pu7CqScSmwJiycIYYzxVVUO9C2hlr6vq6RGJqD4QscEEjTEmQFXVUA/UWRT1kS/FShbGGOOpqhrq87oMpDIikgI8AuQDs1T1hTo5sS/ZkoUxxngqvYNbRF71HheLyKKAZbGILArlpCLypIhsF5El5bafIiI/icgqEZnibT4beF1VrwDqruorMcWqoYwxxlNVNdRvvcdxETjv08BDwLP+DSISDzwMnAhsxM2fMQ3oDCz2diuKQCwlcguKyC8qplmTRK8aymbLM8YYqKJkoapbvMd1QB4wBDgEyPO21ZqqfgHsLrd5FLBKVVeraj7wMnAGLnF0ri7ecLji2Xlc/ORct+JLtnm4jTHGE8y0qpcDc3HVQecCc0TksgjE0gnYELC+0dv2JnCOiDwKvFtFnFeKyDwRmbdjx45aBdA0MZ6cfK/wYtVQxhhTIpghym8ChqnqLgARaQV8jZsxL+JU9QBwaRD7TRWRLcB4n883vDbnSklK4EB+oVtp0hxy9tTmMMYYE3OCqdbZhRs80C/L2xZum4AuAeudvW1BC3UO7mRfPNl5XsmiZXfYtwkKcmp1LGOMiSVV3ZR3o/d0FW5WvHdwN+mdAYTUG6oS3wG9RSQDlyQmAudH4DyVSvbFk+2vhmrV0z3uXgPtBtRlGMYYU+9UVbJI85afgbcpvZv7HWBNKCcVkZeAb4C+IrJRRH6tqoXAdcCHuHGpXlXVpTU87ngRmZqZmVmruJJ9CeQUFFFUrNDKmxNj16paHcsYY2JJVTfl3RGpk6rqQaPYettnADNCOG5I81mkJMUDkFNQRKq/ZGHJwhhjqm/gFpE2wM24WfNKZshT1TERjKtWQp0pL9nn/jmy8wpJbZYGqe1g189hjNAYYxqmYBq4XwCWAxnAHcBaXPtCvROOBm4goN2iF+y2ZGGMMcEki1aq+gRQoKqfq+plQL0rVUB42iyA0u6zrXpaNZQxxhBcsijwHreIyGkiMgxIj2BMtRZqycLfZlGmZHFgB+TsDVOExhjTMAVzU97dItIc+D3wH6AZcENEo4qSkpJFnleySPd3n/0ZOtXqPj9jjIkJ1SYLVZ3uPc0Ejo9sONHlb7PICSxZAOxabcnCGNOoBTM2VA8ReVdEdnrDir8jIj3qIriaCrXNIqWkzcJLFukZgFi7hTGm0QumzeJF4FWgPdAReA14KZJB1VbIvaFK2iy8aqiEJGjR1ZKFMabRCyZZJKvqc6pa6C3PE3C/RSw5qOssWI8oY4yh6pny0kUkHXhfRKaISHcR6SYiNxPCXdaRFGo1VJOEeETcTXklWvWC3atB9eA3ZG6E2f8HRQUHv2aMMTGkqgbu+bjxoMRb/03AawrcEqmgaivU4T7i4oTkxPjSNgtwySJvn+tCm9q27Bu+ewJm/wuytsKp94UQuTHG1G9VjQ2VUZeB1BfJSQmlbRZQOvrsrlUHJ4v134DEw7ePQbtBcOiFdReoMcbUoWB6QyWKyPUi8rq3XCciiXURXDSUGaYcSu+1KN9uUZALm+bDqCuhx/Hw3o2wYW7dBWqMMXUomAbuR4HhwCPeMtzbFpOSfQkcyAtIFi26QlziwQMKbl4ARfnQ/Wg490lo1hFe+RXk1q69xBhj6rNgksVIVb1YVWd6y6XAyEgHVhuhNnADpPjiy1ZDxcVDeo+DSxbrv3GPXY+A5HQ48zHYvw1+er/W5zbGmPoqmGRRJCI9/SveDXlFVewfNaHeZwH+Notyl9eq58Eli3XfQOu+kNLKrXc5DNI6wPLpGGNMrAkmWfwB+ExEZonI58BM3DhRMSk5sVzJAqD9IbDzJ9iz1q0XF8GGb6HbEaX7xMVBv9Ng1ac2b7cxJuZUmSxEJB4YAvQGrgcmA31V9bM6iC0qkpPiy7ZZAAy/GCQO5nhNNduWuu60XY8su1+/06AgG36O2X8eY0wjVWWyUNUiYJKq5qnqIm/Jq6PYoiLFl3BwyaJZRxh0Lix4DnL2lLZXBJYsALqPhqTmVhVljIk5wVRDfSUiD4nIaBE51L9EPLIoOajrrN+R10HBAZj3FKz7Gpp1dj2lAsUnQp+TXSN3UeHBxzDGmAYqmPkshnqPdwZsU+rpbHmhSvYlkFdYTGFRMQnxAbm0/WDocRx8+1/QYuhxbMUH6D8OFr/qSh8Zo+skZmOMibRg5rNoMHNYiMh4YHyvXr1qfYyS2fIKimgWX67gdcRkeOEc97xruSoov55jIT4Jlr9nycIYEzOCuYO7lYg8KCILRGS+iPxbRFrVRXA1FZaus96cFjkVVUX1GgttB7jn3Y48+HWApFToOca1W1Q0+KAxxjRAwbRZvAzsAM4BzvWevxLJoKLJP0z5gbwK2hxE4MS7YODZ7h6LyvQ7DTI3uF5TxhgTA4Jps+igqncFrN8tIhMiFVC0VTinRaDeJ7ilKv4qqi0Lof2g8AVnjDFREkzJ4iMRmSgicd7yS+DDSAcWLSlJ3tSqFZUsgpWeAQlNrWRhjIkZwSSLK3BTq+Z5y8vAb0QkS0T2RTK4aCgpWRSEMKJJXDy07Q/bloQpKmOMia5gekOl1UUg9YW/gTu7/F3cNdVuIPw0wzVyi1S/vzHG1GPBlCwalZIG7vJ3cddUu0GQvQv2bw9DVMYYE12WLMrxt1lkh9JmAa5kAVYVZYyJCfU+WYhIDxF5QkRer4vzhaXNAgKShTVyG2MavmBuyvuniAyszcFF5EkR2S4iS8ptP0VEfhKRVSIypapjqOpqVf11bc5fG0kJccRJGNosktPd/BaWLIwxMSCY+yyWAVNFJAF4CnhJVYOdiu5p4CHgWf8Gb9jzh4ETgY3AdyIyDYgH7in3/stUtU4r/UWEFF9C6G0W4EoXliyMMTGg2pKFqj6uqkcBFwHdgUUi8qKIVDtmlKp+Aewut3kUsMorMeTjuuKeoaqLVXVcuSXoRCEiV4rIPBGZt2PHjmDfVqHkpPiKh/uoqXYDYcdyKCoI/VjGGBNFQbVZeKWBft6yE/gBuFFEXq7FOTsBGwLWN3rbKjt3KxF5DBgmIrdUtp+qTlXVEao6ok2bNrUIq1SyL4EDYUkWg6C4AHauDP1YxhgTRdVWQ4nI/wHjcNOp/l1V53ov3SciP0UyOABV3QVcFcy+4Rh1Frw5LULtDQVlG7nbDQj9eMYYEyXVTasquGqkoar6m4BE4TeqFufcBHQJWO/sbQtZOEadBcLXZtGqN8QlWvdZY0yDV920qgr8UlUPVPJ6sA3dgb4DeotIhoj4gInAtFoc5yAiMl5EpmZm1iasUmFrs0jwQZu+sP3H0I9ljDFRFEybxQIRGVmbg4vIS8A3QF8R2Sgiv1bVQuA63GCEy4BXVTUsXYbCVbJI9sWHp80CrEeUMSYmBNN19jDgAhFZBxwABFfoOKS6N6rqpEq2zwBm1CTQYISvzSIhPG0W4JLFolcge7e798IYYxqgYEoWJwM9cXNuj8c1do+PZFC1Fb42i/jQ7+D2a+s1ci95IzzHM8aYKAjmPot1uAbpMd7z7GDe15AlJyWEfge3X/ejoMvhMOMPMG0y5GeH57jGGFOHghnu46/AHwH/PQ6JwPORDKq2wtbAnRhPflEx+YXFoQeV2BQumQ5H3wgLnoX/jYGti0M/rjHG1KFgSghnAafj2itQ1c1AvZzjImwN3N7Is2HpEQUQnwgn/BV+9aYbtnzqcTDzb1CYF57jG2NMhAWTLPK9LrQKICIpkQ0p+lJKRp4NUyO3X6+xcO23MOhc+OJ++O8xsH5OeM9hjDEREEyyeFVE/gu0EJErgE+A/0U2rNoJVzVUU/8ESOFqtwiUnA5n/xfOfw3ysuDJk+GNK2Df5vCfyxhjwiSYBu4HgNeBN4C+wG2q+p9IB1Yb4byDGyA7HHdxV6bPSXDdd3DMTfDjO/CfETDrPpdAjDGmngmmgftG4EdVvUlV/6CqH9dBXFGVnBTBkkUgXwqM+TNcN9dVUc36O/x7KMx51NozjDH1SjDVUGnARyLypYhcJyLtIh1UtPlLFjnhbrOoTMvuMOE5uHymu4nvgynw7yHwzSPW1dYYUy8EUw11h6oOBK4FOgCfi8gnEY+sFsLWdTaSbRZV6TwcLp4GF02DVr3gw1vg/w2Gz//h7gA3xpgoqcnNdduBrcAuoG1kwglNuLvORrTNoio9jnX3Zlz6AXQcCp/dDf8aANNvgB0rohOTMaZRC2Y+i2uAXwJtgNeAK1Q1podRLek6G677LGqr2xHQ7Q3Y9iPMeQS+fwHmPQndR8PIy6Hfae4eDmOMibBgBhLsAvxOVRdGOJZ6I7mkN1SUk4VfuwFwxkMw9q/w/XMw7yl47WJIaQtDJsKwC6FNn2hHaYyJYdUmC1W9RUSGiMh13qYvVfWHCMcVVb6EOBLihAPeyLPPz1nHvz9diap7vWebFF6+8nDc3FB1KLUNjL4RjvotrPoE5j8D3zwMXz8InUfBkAkw8Gwb3dYYE3bBdJ29HngB107RFnheRCZHOrDaCFcDN3hTq+YXkVdYxP/7ZCUtmiZy0sB2dGjehG/X7C5JHFERFw99ToZJL8KNy+DEO939Ge/9Hh7o7R6NMSaMgmngvhw4TFVvU9XbgMOBKyIbVu2Eq4EbICUpgez8QqYt3MzO/XncNn4Afz9rMCf0r2c9h9PauZLGNd/AVbOh76nw3eOQtS3akRljYkgwyUKAwMr7Im9bTEv2xXMgr4gnZq+hb7s0ju7VOtohVU0E2g+Gw69x65u/j248xpiYEkwD91PAtyLylrd+JvBExCKqJ5J9CXz98072ZBdw3zmD6759orY6DAGJg80LoO8p0Y7GGBMjgmng/peIzAKO9jZdqqox/7M12RfPnuwCWqX4OGNop2iHEzxfCrTpB5sWRDsSY0wMCeY+i8OBpaq6wFtvJiKHqeq3EY8uilK8G/MuOLwbTRLjoxxNDXUcBis+BFVXPWWMMSEKps3iUWB/wPp+b1tMS0lKwBcfx4WHd4vI8fMLi+n3l/d5+/tN4T94x2GQvRMyN4T/2MaYRimoBm5v8iMAVLWY4No66lw4u87+5pgePHT+MNqkJYUhsoPtzc4nt6CYv81YFv6DdzrUPVpVlDEmTIJJFqtF5HoRSfSW3wKrIx1YbYSz6+ygTs05aWD7MEQVBe0GQVyi9YgyxoRNMMniKuBIYBOwETgMuDKSQZkQJSS5oc43W8nCGBMewfSG2g5MrINYTDh1OhQWvwHFxRBXk8GFjTHmYMEM93G/1wMqUUQ+FZEdIvKrugjOhKDjMMjLhN31ssbQGNPABPOT8yRV3QeMA9YCvYCbIhmUCYOOXiO3VUUZY8IgmGThr6o6DXhNVUPvamQir00/SGhqjdzGmLAIpgvsdBFZDuQAV4tIGyA3smGZkMUnuKE/rPusMSYMgpmDewquN9QIVS0ADgBnRDowPxE5U0T+JyKviMhJdXXemNBxGGxZCF89aG0XxpiQVFqyEJExqjpTRM4O2Ba4y5vVHVxEnsS1dWxX1UEB208B/g3EA4+r6r2VHUNV3wbeFpGWwAPAR9Wd13hG/hrWfQUf/8UtbQe6Icz7nuraNKyXlDEmSFVVQx0LzATGV/CaEkSyAJ4GHgKe9W8QkXjgYeBE3H0b34nINFziuKfc+y/zuu4C/Nl7X72Vk19EQXExAL74uOiPKdW6N1z1JexZB8unw/L3YPb/wZcPQEob6HUi9D4Beo6Bpi2jG2uMyy8sJrfQjfQfJ0JqUr0cBAEoG6sAaU1snndTRbJQ1b96j5fW9uCq+oWIdC+3eRSwSlVXA4jIy8AZqnoPrhRShrjizL3A+/7BDOujuWt2M3HqNxR7A6P44uP4+MZj6NYqJeLn/t3L3/P2ws2svfe0indo2Q2OuNYt2bv5afbbLP/yNcYtn0H8Dy+6Ic07HuqSRs/jodMISPBVer7uU95j0qgu3HP2IRG6orp14RPfsnD9XhbfcXJEjp9bUMQR93zKnuyCkm33n3MIvxzZpdL3HHP/ZzRrmsD0yaNDOvcnP27j8mfn8cVNx9O1VXLJ9rU7D3DcA7N46pKRHN+vbcn2omJl9P0z2bYvr2Tb7eMHcMlRGSHFEYxvft7FpP/N4YPfjaZf+2YRP18kZGYXMOTOj7jn7MFMGtU1pGNd9vR3fP3zTpbfdWqYogtNVdVQN1b1RlX9Vy3P2QkIHOHOf1d4ZSYDJwDNRaSXqj5W0U4iciXeneVdu4b2R6qNLZk5FCtcdWxP9uUW8OK369mRlVcnyeLthZuD3zk5naeyRvByQTtyxgxgYscdsPJjWP2ZK3F8cT8kpkC3IyDjGOh+NLQf4hrMA7w0d0PMJIsvV+6M6PFz8ovYk13ASQPaMSojnbvfW8bGvTlVvmf97uywnPuthW6gyh827i2TLBas3wPAtB82l0kWBUXFbNuXx/F923BUr9bc98FyNlUTa7h8sGQLAHN+3tVgk8WGPe7v9tw360JOFjOXb69+pzpUVVk4zXvsC4wEpnnr44G5kQwqkKo+CDwYxH5TRWQLMN7n8w2PfGQVO29EZzbvzeHFb9dHK4SgqcRDl1FuGXMr5OyFtV/Cmi/c8vFtbkdfGnQ9DLodCV2PxEcB+VjVRE0d2bMVlxyVwd3vRWDwyDAbmZHO5aN78K+PV0Q7FFNPVFUNdQeAiHwBHKqqWd767cB7IZxzExBY/u7sbQuZqr4LvDtixIh6OUd4vde0BfQf7xZw83ivmw1rvWXVJwAsTkpkkWbAR3Og6+HQeRSktole3MaYiAumla0dkB+wnu9tq63vgN4ikoFLEhOB80M4nomUtHYw6By3ABzYCevn8OwLLzA8bgXMeRS+9gp9LbtD55GuvaPToW4+8MSmUQvdGBNewSSLZ4G55ebgfjqYg4vIS8BxQGsR2Qj8VVWfEJHrgA9xPaCeVNWlNYy7svONB8b36tUrHIcz5aW0hv7j+Fuh60K99i9j3R3iG7+DTfNg7Vew+DW3b1wCtB3g7vXoOAw6DnXrCZGZH8QYE1nBjDr7NxF5H/B3ywh6Dm5VnVTJ9hnAjKCjDJJVQ9WxxCauIbzbEaXb9m12d41vmu8SyY/vwIJn3GtxCdC2v2sw73CIK320GwRNGmZjpjGNSVCdvb0uq/W226qflSzqgWYd3dLf6wWtCnvWwpYf3N3kW36AFe/DwudL39Oim5c4Brql7UBIz4C4Bjb3uTExrP7eGVQLVrKoh0TcF396Bgw8021ThaytsHUxbF0E25bA1iXw0wxQd1MjCU2hTR9XddVpOAy/BOKtB5Yx0RJTycJKFg2ECDTr4JY+AcN95WfDjuWw/UfYthS2L4PVs+CHl6AgG476bdRCNqaxi6lkYSWLBs6X7HpSdTq07PYXJ8AXD8CQ862LrjFRYiPJmfrvxLtcyWJW+aHDjDF1xZKFqf/a9IERv4b5T7mqKWNMnYupZCEi40VkamamTeYXc46bAklp8NGfox2JMY2StVmYhiE5HY79I3z4J/j3UGjTF1r38R77uuHYm7aIdpSmMnvXw6JXoEkL97dMbg3Jrbwl3W7WbABiKlmYGDfqSte1duM82LkCVn0KxaXDfpPSBlr1hta93GOrXi6JtOhW5ZDrpg589zh89e/KX/el8jtSOcfXlFYL2sPWztA03Ussrdx8K8npbpv/uS/V9awzdSKmkoV1nY1x8Ylw5OTS9aJC2LvOJY6dK2DnSti1CpbPgOyAYcclDpp3gVY9Ib0npPcoXVp2q/vraIz2bYHmXeGKmZC9y/19sne55cAuyNnNup9WsydnCx0KstwQMtl7IK+KKuV4n0sc/gTiX5Jbll0vv1iSqZWYShZWDdXIxCe4BNCqp5sqNlDOHtj1s0sgu392z3f/DBu+g/ysgB2F2UmtWF/cFt5539082KIbtMxwgyMmp9sXSzhkbXF39qe2qbT781t5S3hmyzpuPyxgsqWiAve3zN4NObtLHwO35exxw+vvWeuGmMnZA4VVzMERl+Cqw5q2cMnD/7yyxxZd3dLIxVSyMKZE05bQeYRbAqm6X7O7foY9a2D3GubOnE032QYrPoQD5Sac8aW65NGiqyuF+J+36AoturgvFEsm1cva6oZyqan4REht65aaKMhxCSRnj5dQvOe5e12Syd1bui17J+xa6dZzM3GzRgeQePjDSkhpVfP4Y4glC9O4iLjRc1NauwmdgBs/dNOzrL3pNMg/4H6h7lnnqrhKnq93E0Pl7y97PF+aSxzNO7vk0byLe968i1tPDWU0/xiyfxv0OqHuzpfY1C3NOtTsfcXFkLevNJn8/Cl8eqf7LFiyMMaU8KWUDmhYnqr7Jbp3nUsgmRtg74bSxw1zvF+mAeISaJbantd8ybRa1AMO9OFX8VkkFnWqm+upD/L2uy/gtAaQOOPivOqpFtASKC4C7nQlo0YuppJFXTZwa/W7mFgj4vXOSXdzdFQkdx/s2wSZG11pZN8mCndtoHDPElrvWwrfzuTuxHyWrt4OvFin4UfN/m3uMa2Gv/Lrg7T27nG/JYuYShbWwG2irkkzt7TtX7Ip+0A+kxZ8zO1HD+CSI7rxwm3nMmHHx27mwZTWUQy2jmRtcY/+L96GJLUtIFayIMbu4K4L1pZpQhIXx1NFJ5Og+bDg2WhHUzf8X7QNsWQRn+gSuiULSxbG1LVV2pn1zUfAvCe9OvEY15BLFgCp7Uur0hoxSxZRptb40Sgt7PBL1zC+4oNohxJ5WVvdZFZJDXT63LT2pQmvEbNkES1WndWo/Zw+Gpp1hrlTox1K5GVtdV+4DbUON60dZFnJwpKFMVGgkgAjL3MzAcb6sOtZWxtme4VfWgd3s2ZjqDKsQkz1hrKxoUyDcujFMOs+eORwN75Rs07edLMduT5+P1tpCSuT3K/yZh3dXekN8dd51hboMCTaUdReajs3gOWBHQ233SUMYipZWNdZ06CktIZLZ8DPn0HWZtjnLZsWcGOiNxDiC/8r3T/e576s0jq4x9T23nr7suv1LalkbYU+p0Q7itrzJwh/dVojFVPJwpgGp6Lxq4A+U96mrexl9tX9XALZv839Qs/a6ta3L3NJJm/fwceMS3S/htPa8Zu9SRyV0IQ+y/pDfk+3PbUtyQeKaEJe5K8vLwsKDjTsL1l/FVoj7z5ryaIRsh5Y9V8+iWzUNtD18Gp2POC+xPZvO/hx/zbSC9ZyUvxOWi3/DJaX/uFPAZY3gdyfmsKDHSClLaS2IaFpG34bn8nATb3hx4Ecympa5grkdnK9mWpaYim5x6IBJwv/+F6N/C5uSxaNWD2qqDC15UspHaa9Ave+uIDpi7bwnwmDGd/L5768D+xg/tLlfPrdYg5rW8ixHYH922HnSuL3f8UNibthJbASno8DFntLvA/G/AWOuj74+Br6PRZQmixqULKoT7WA4WLJwphGQOMSSts2gPX7+/PInB5sbteRY88tHecqr6CIQX+Zzl+Ob8fFQ1K4/JEZjO+ZyBm9fe6O80Wv1DBZNOC7t/0SfG62PquGMsaYUoUkkJ3UBtr35Gs2kdG6K2ccOcBNKDTzbjezXbDDdcdCyQJcsmvkycLuszDGBKf7Me5x3ezg35O1zU0glZQWmZjqSmq7Rt9mYcnCGBOcTodCYjKsrUmy2BIbE0CltW/0d3HX+2QhIv1F5DEReV1Ero52PMY0WvGJrnfWmi+Df09Dv3vbL80bTLAR38Ud0WQhIk+KyHYRWVJu+yki8pOIrBKRKVUdQ1WXqepVwC+BoyIZrzGmGt1Hw45lsH9HcPtnbWn47RXgbnjUIjd/eyMV6ZLF07gu3SVEJB54GDgVGABMEpEBIjJYRKaXW9p67zkdeA+YEeF4jTFVyfDaLdYGUbpQjZ27nkvu4m68o89GNFmo6hfA7nKbRwGrVHW1quYDLwNnqOpiVR1XbtnuHWeaqp4KXFDZuUTkShGZJyLzduwI8lePMaZmOgwFX1pw7RZ5+1wPqliphoJG3W4Rja6znYANAesbgcMq21lEjgPOBpKoomShqlOBqQAjRoywe5SNiYT4BOh2RHAli1i4e9vP5uKu//dZqOosYFYw+9qos8bUge5Hw8qPqq9iipV7LKBWd3HHmmj0htoEdAlY7+xtC5mqvquqVzZv3jwchzPGVKT7aPdYXVVULNy97ZeQ5IaRb8TJIholi++A3iKSgUsSE4Hzw3FgK1kYUwc6DIGk5jDzLlg+HVLaQHJrd1d3cms39Hpya9i50u0fC/dZgHevhSWLiBCRl4DjgNYishH4q6o+ISLXAR8C8cCTqro0HOez+SyMqQNx8XDcFFg2DbYucZMC5e6teN+k5pCUWqfhRUwjv4s7oslCVSdVsn0GEegGayULY+rIEde4xa+owN2DcGAnZO/0HndBy4zoxRhuaR1KS0uNUL1v4K4JK1kYEyXxiWVGtY1JaV7JorgY4ur94Bdh1/iu2BhjaiOtAxQXwoY5sHcD5O5rVDOJxVTJwqqhjDER07K7e3zq1NJtEgdNmntLC7pLCo8kFhB/oAV8+AE0aeG91sw9JjUL2L+Zu8GxgZRSYipZWDWUMSZiep0Il33oZhXM3Qs5eyE301v2Qu4+ZN9OestWWuavgnmzoSC7moOKSxpJXvJISitJKncm7CGLpjB7RWmSSWpWul9SwGMdJJyYShbGGBMxcXHVzom+ZlMm4/4zm4Edm/He9aOhMB/ysrxkkumGQClJMAHP8/aVru/bBLnLGB+/izSy4ZNp1cfmS/MSTTOY9BKk9wjPNQeIqWRh1VDGmHolwQcJrYKfWTDAsCnvAcraO493iSRvn5d4ApJLXlbAdm8fX2S6KsdUsrBqKGNMbBHwpbiF6N4J3zBaVowxxkSVJQtjjDHViqlkISLjRWRqZmZmtEMxxpiYElPJwkadNcaYyIipZGGMMSYyLFkYY4ypliULY4wx1YqpZFGXDdwaxABiwQ0xFr2ByBrPEGj1UB0PQFfZ5zWYKOp6rLxY+FzG4viCEsyXXkMjIjuAdbV8e2tgZxjDaSga43XbNTcejfG6a3PN3VS1TUUvxGSyCIWIzFPVEdGOo641xuu2a248GuN1h/uaY6oayhhjTGRYsjDGGFMtSxYHmxrtAKKkMV63XXPj0RivO6zXbG0WxhhjqmUlC2OMMdWyZGGMMaZaliwCiMgpIvKTiKwSkSnRjifcRCReRL4XkeneeoaIfOtd7ysi4vO2J3nrq7zXu0c18FoSkRtEZKmILBGRl0SkSSxes4g8KSLbRWRJwLZ/iMhyEVkkIm+JSIuA127xrvMnETk5YHuD+fxXdM3e9snedS8VkfsDtjf4awYQkS4i8pmI/Ohd42+97eki8rGIrPQeW3rbRUQe9K5vkYgcGnCsi739V4rIxdWeXFVtce028cDPQA/AB/wADIh2XGG+xhuBF4Hp3vqrwETv+WPA1d7za4DHvOcTgVeiHXstrrUTsAZoGnCtl8TiNQPHAIcCSwK2nQQkeM/vA+7zng/wPttJQIb3mY9vaJ//Sq75eOATIMlbbxtL1+xdSwfgUO95GrDCu777gSne9ikBf+9fAO8DAhwOfOttTwdWe48tvectqzq3lSxKjQJWqepqVc0HXgbOiHJMYSMinYHTgMe9dQHGAK97uzwDnOk9P8Nbx3t9rLd/Q5MANBWRBCAZ2EIMXrOqfgHsLrftI1Ut9FbnAJ2952cAL6tqnqquAVbhPvsN6vNf0TUDVwP3qmqet892b3tMXDOAqm5R1QXe8yxgGe6HUeDnt/zn+ll15gAtRKQDcDLwsaruVtU9wMfAKVWd25JFqU7AhoD1jd62WPH/gJuBYm+9FbA34Asl8HpL/i281zO9/RsMVd0EPACsxyWJTGA+MXzNVbgM9+sSKv+cx8Lnvw8w2qtG/FxERnrbY/KavarSYcC3QDtV3eK9tBVo5z0P27VbsmgERGQcsF1V50c7lrri1dmegat26AikUM0vp1gkIrcChcAL0Y6lDiTgqlUOB24CXm0opcOaEpFU4A3gd6q6L/A1dfVMYb8nIiHcB2zANgFdAtY7e9tiwVHA6SLyC6AJ0Az4N65ImuD9kg68Xv+/xUavCqc5sKvuww7JCcAaVd0BICJv4v4dYvmayxCRS4BxwFjvCwSq/pw39M//RuBN71rnikgxbjC9mLpmEUnEJYoXVPVNb/M2Eemgqlu8aiZ/FVxl174JOK7c9llVnddKFqW+A3p7vWV8uEbOaVGOKSxU9RZV7ayq3XHXNVNVLwA+A871drsYeMd7Ps1bx3t9ZsCXTUOxHjhcRJK9X5djgR+J7WsuISKn4KodT1fV7ICXpgETvd5fGUBvYC6x8fl/G9fIjYj0wTVa7ySGrtn7LD8BLFPVfwW8FPj5Lf+5vsjrFXU4kOlVV30InCQiLb1S+EnetspFu3W/Pi24ngMrcD0kbo12PBG6xuMo7Q3VA/efZhXwGqW9SJp466u813tEO+5aXusdwHJgCfAcrjdMzF0z8BKuXaYA9+v61951bAAWestjAfvf6n3GfwJODdjeYD7/lVyzD3je+3svAMbE0jV78R6Nq2JaFPC3/QWufe1TYCWuR1i6t78AD3vXtxgYEXCsy7zPySrg0urObcN9GGOMqZZVQxljjKmWJQtjjDHVsmRhjDGmWpYsjDHGVMuShTHGmGpZsjAxTUTuEZHjReRMEbmlhu9t4w0d8b2IjC732mhv1M+FItI0zDGfHokRUL1/gwHhPq5pHCxZmFh3GG4gvWOBL2r43rHAYlUdpqpflnvtAuAeVR2qqjlhiLOEqk5T1XvDeUzPmbgRSo2pMbvPwsQkEfkHbmRN/5DUPXFDlr+uqneW27c78CRuaIgdwKW4MYamAU1xQyMc4U8KInI5bkjoTOBr4H/AH1R1nPf6Q8A83M1hj3uniQcG4QZrmxFw+sG4GwDXBcRzCe7mqetE5GlgHzACaA/crKqvi8hxwJ1AFtALd2f6NapaLCL7VTXVO9a5uCE/pgLTvZgzgXNwoxBfhRs76kdVnRj8v7BpbGxsKBOTVPUmEXkVuAg3j8csVT2qkt3/Azyjqs+IyGXAg6p6pojchvelXe7Yj4vI0bg74f1f3BXFMA8YCiXJ6wNV3Ryw7Vrg2MBEUYkOuDt3++ESmH+I9VG4ksI64APg7IDXysfytYhM88fsnX8KkKGqeRIwOZIxFbFqKBPLDsVNaNMPN+5/ZY7ATQoFbliQo8MZhIhM8GKZErDtKOAK3JAL1XlbVYtV9UdKh54GmKtuLoYi3PAXNY17EfCCiPwKV7owplJWsjAxR0SGAk/jRtLciZv4SERkIQHVSWFUSNkfXk0CYhkE3A4c432p440K+gRukL/9QRw/L+B54JDb5euQtYLtTajcabgZ58YDt4rIYC2d68OYMqxkYWKOqi5U1aGUTjk5Ezi5isbor3EjjoJruC7fmF2ddcAAb1TTFriGcbznLwEXaelQ6Ym4AQv/qKoranie8kZ5I6bGAROA2d72bSLS39t+VsD+WbipOPFe66KqnwF/xA3JnhpiPCaGWbIwMUlE2gB7VLUY6OdV4VRmMnCpiCwCLgR+W5NzqeoG3NzeS7zH772XzgC6Af/zutguBI7ENVbf4d8mIh1rcr4A3wEP4arY1gBvedun4Bqzv8aNzOr3MnCTiHyPG6b7eRFZ7MX7oKrurWUcphGw3lDGNEBeo3pJDyxjIs1KFsYYY6plJQtjjDHVspKFMcaYalmyMMYYUy1LFsYYY6plycIYY0y1LFkYY4yp1v8H2/Cw3/2xs2QAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "line_emp, = plt.semilogy(emp_timeseries, label=\"Empirical\")\n", "line_gt, = plt.semilogy(gt_timeseries, label=\"Good-Turing\")\n", "plt.legend(handles=[line_emp, line_gt])\n", "plt.xticks(range(0, measurements + 1, int(measurements / 5)),\n", " range(0, trials + 1, int(trials / 5)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('discovery probability')\n", "plt.title('Discovery Probability Over Time');" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "solution": "hidden", "solution2": "shown", "solution2_first": true, "solution_first": true }, "source": [ "### Exercise 2: Extrapolate and Evaluate Statement Coverage\n", "\n", "In this exercise, we use Chao's extrapolation method to estimate the success of fuzzing." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "solution": "hidden", "solution2": "hidden", "solution2_first": true, "solution_first": true }, "source": [ "#### Part 1: Create Population\n", "\n", "Use the random `fuzzer(min_length=1, max_length=1000, char_start=0, char_range=255)` to generate a population of $n=400000$ fuzz inputs." ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "skip" }, "solution": "hidden", "solution2": "hidden" }, "source": [ "**Solution.** Here we go:" ] }, { "cell_type": "code", "execution_count": 98, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:55.426915Z", "iopub.status.busy": "2022-01-11T09:33:55.426319Z", "iopub.status.idle": "2022-01-11T09:33:55.428123Z", "shell.execute_reply": "2022-01-11T09:33:55.428544Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "trials = 400 # Use 400000 for actual solution. This takes a while!" ] }, { "cell_type": "code", "execution_count": 99, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:55.525312Z", "iopub.status.busy": "2022-01-11T09:33:55.486842Z", "iopub.status.idle": "2022-01-11T09:33:55.770079Z", "shell.execute_reply": "2022-01-11T09:33:55.770627Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "population = []\n", "for i in range(trials):\n", " population.append(fuzzer.fuzz())\n", "\n", "_, stmt_ts, Q1_ts, Q2_ts = population_stmt_coverage(population, my_parser)" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "solution": "hidden", "solution2": "hidden", "solution2_first": true, "solution_first": true }, "source": [ "#### Part 2: Compute Estimate\n", "\n", "Compute an estimate of the total number of statements $\\hat S$ after $n/4=100000$ fuzz inputs were generated. In the extended model, $\\hat S$ is computed as\n", "\\begin{align}\n", "\\hat S_\\text{Chao1} = \\begin{cases}\n", "S(n) + \\frac{Q_1^2}{2Q_2} & \\text{if $Q_2>0$}\\\\\n", "S(n) + \\frac{Q_1(Q_1-1)}{2} & \\text{otherwise}\n", "\\end{cases}\n", "\\end{align}\n", " * where $Q_1$ and $Q_2$ is the number of singleton and doubleton statements, respectively (i.e., statements that have been exercised by exactly one or two fuzz inputs, resp.), and \n", " * where $S(n)$ is the number of statements that have been (dis)covered after generating $n$ fuzz inputs." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** Here we go:" ] }, { "cell_type": "code", "execution_count": 100, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:55.776378Z", "iopub.status.busy": "2022-01-11T09:33:55.775632Z", "iopub.status.idle": "2022-01-11T09:33:55.778153Z", "shell.execute_reply": "2022-01-11T09:33:55.778575Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "After executing 100 fuzz inputs, we have covered 131 **(70.4 %)** statements.\n", "After executing 100 fuzz inputs, we estimate there are 186 statements in total.\n", "After executing 400 fuzz inputs, we have covered 154 statements.\n" ] } ], "source": [ "time = int(trials / 4)\n", "Q1 = Q1_ts[time]\n", "Q2 = Q2_ts[time]\n", "Sn = stmt_ts[time]\n", "\n", "if Q2 > 0:\n", " hat_S = Sn + Q1 * Q1 / (2 * Q2)\n", "else:\n", " hat_S = Sn + Q1 * (Q1 - 1) / 2\n", "\n", "print(\"After executing %d fuzz inputs, we have covered %d **(%.1f %%)** statements.\\n\" % (time, Sn, 100 * Sn / hat_S) +\n", " \"After executing %d fuzz inputs, we estimate there are %d statements in total.\\n\" % (time, hat_S) +\n", " \"After executing %d fuzz inputs, we have covered %d statements.\" % (trials, stmt_ts[trials - 1]))" ] }, { "cell_type": "markdown", "metadata": { "button": false, "new_sheet": false, "run_control": { "read_only": false }, "slideshow": { "slide_type": "subslide" }, "solution": "hidden", "solution2": "hidden", "solution2_first": true, "solution_first": true, "toc-hr-collapsed": false }, "source": [ "#### Part 3: Compute and Evaluate Extrapolator\n", "\n", "Compute and evaluate Chao's extrapolator by comparing the predicted number of statements to the empirical number of statements." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "source": [ "**Solution.** Here's our solution:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:55.784002Z", "iopub.status.busy": "2022-01-11T09:33:55.783423Z", "iopub.status.idle": "2022-01-11T09:33:55.785010Z", "shell.execute_reply": "2022-01-11T09:33:55.785432Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [], "source": [ "prediction_ts = [None] * time\n", "Q0 = hat_S - Sn\n", "\n", "for m in range(trials - time):\n", " prediction_ts.append(Sn + Q0 * (1 - (1 - Q1 / (time * Q0 + Q1)) ** m))" ] }, { "cell_type": "code", "execution_count": 102, "metadata": { "execution": { "iopub.execute_input": "2022-01-11T09:33:55.856149Z", "iopub.status.busy": "2022-01-11T09:33:55.796882Z", "iopub.status.idle": "2022-01-11T09:33:56.084125Z", "shell.execute_reply": "2022-01-11T09:33:56.084513Z" }, "slideshow": { "slide_type": "skip" }, "solution2": "hidden" }, "outputs": [ { "data": { "image/png": "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rFixolTglOf+L7qVlRXUavUjX6tQKKBQKMqt7UFWVlY4evQoDh8+jL179yIiIgJr1qxB586ddaqPyJwwFx7GXCBiNpSF2VD12EMiQ40bN0Z8fDz69OmDVatWYePGjYiIiEBiYuIjgyUqKgo7duzA1atXARSvTtG9e/cyJ6Q9jq+vL44dOwYAOH78OM6dOwcAUKvVGDx4MAYPHozhw4dLr8/NzcUTTzyBd955B1FRURXeX1lSU1MRGxsLAPj555/h6uoKDw8P+Pj44NKlS7h58yYA4Pvvv5fe4+TkhPz8fBQVFUl13bhxA507d8abb76JTp06IS4uziD1EVU15gJzgagszAZmgzlgD4mM7du3Dx9//DG+//579OjRo9zXBgQEYP78+ejduzeA4oD66quvKrXf9957D6NGjcLSpUvRvn17tGrVCgBw6NAh7Nq1Czdu3MC6desAAE899RSaNm2Ks2fPYv78+dISfRMmTMCECRMqtX8AaNWqFb799ltMnjwZtra2WLNmDRQKBRo1aoRp06ahbdu2cHV1RZ8+faT31K1bFyNHjkRQUBBq1aqFTZs2YfDgwbhz5w4UCgX8/PwwatSoStdEZA6YC8wForIwG5gNpsRlf0l29u7di5dffhnx8fGmLoWIzARzgYjKwmwwDxyyRUREREREJsMeEiIiIiIiMhn2kBARERERkcmwQUJERERERCbDBgkREREREZkMGyRERERERGQybJAQEREREZHJsEFCREREREQmwwYJERERERGZzP8DWpqZ84xvZA0AAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "plt.figure(num=None, figsize=(12, 3), dpi=80, facecolor='w', edgecolor='k')\n", "plt.subplot(1, 3, 1)\n", "plt.plot(stmt_ts, color='white')\n", "plt.plot(stmt_ts[:time])\n", "plt.xticks(range(0, trials + 1, int(time)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of statements exercised')\n", "\n", "plt.subplot(1, 3, 2)\n", "line_cur, = plt.plot(stmt_ts[:time], label=\"Ongoing fuzzing campaign\")\n", "line_pred, = plt.plot(prediction_ts, linestyle='--',\n", " color='black', label=\"Predicted progress\")\n", "plt.legend(handles=[line_cur, line_pred])\n", "plt.xticks(range(0, trials + 1, int(time)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of statements exercised')\n", "\n", "plt.subplot(1, 3, 3)\n", "line_emp, = plt.plot(stmt_ts, color='grey', label=\"Actual progress\")\n", "line_cur, = plt.plot(stmt_ts[:time], label=\"Ongoing fuzzing campaign\")\n", "line_pred, = plt.plot(prediction_ts, linestyle='--',\n", " color='black', label=\"Predicted progress\")\n", "plt.legend(handles=[line_emp, line_cur, line_pred])\n", "plt.xticks(range(0, trials + 1, int(time)))\n", "plt.xlabel('# of fuzz inputs')\n", "plt.ylabel('# of statements exercised');" ] } ], "metadata": { "ipub": { "bibliography": "fuzzingbook.bib", "toc": true }, "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.7" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true }, "toc-autonumbering": false, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }