{"action":"created","comment":{"url":"https://api.github.com/repos/mirage/mirage-www/pulls/comments/34081938","id":34081938,"diff_hunk":"@@ -0,0 +1,341 @@\n+# title title title\n+\n+This post gives a bit of background on the Random Number Generator (RNG) in the\n+new MirageOS 2.5.0.\n+\n+First we give background about why RNGs are really critical for security, then\n+we try to clarify the often-confused concepts of \"randomness\" and \"entropy\" as\n+used in this context, and finally we explore the challenges of harvesting\n+good-quality entropy in unikernels.\n+\n+## The race to... Wherever\n+\n+Security software needs to play dice.\n+\n+It needs to do so to create secrets, for example. Secrets can then serve as the\n+keys that protect communication, like the Diffie-Hellman key that two TLS\n+endpoints can exchange. Proof of the knowledge of a particular secret can be\n+used to verify the identity of someone on the Internet, as in the case of\n+verifying the possession of the secret RSA key associated with an X.509\n+certificate. As guessing a secret can have disastrous consequences, it must be\n+chosen in a manner that is not realistically predictable to anyone else.\n+\n+There are other reasons to use randomness. A number of algorithms require a\n+unique value every time they are invoked and badly malfunction when this\n+assumption is violated, and random choice is one way to provide a value unlikely\n+to repeat. For example, repeating the [`k`-parameter][dsa-sensitivity] in DSA\n+digital signatures compromises the secret key, while reusing a GCM\n+[nonce][gcm-security] negates authenticity. Other algorithms are probabilistic,\n+in that they generate random values before operating on an input, and then store\n+the chosen values in the output, such as the [OAEP][wiki-oaep] padding mode for\n+RSA. This is done in order to confuse the relationship between the input and\n+the output and defeat a clever attacker who tries to manipulate the input to\n+gain knowledge about secrets by looking at the output. Yet other algorithms pick\n+random numbers to internally change their operation and hide the physical amount\n+of time they need to execute, to avoid revealing information about the secrets\n+they operate on. This is known as [blinding][wiki-blinding], and is one way to\n+counter timing side-channel attacks.\n+\n+Randomness is therefore quite pervasive in a security context. In fact, many\n+cryptographic algorithms are designed under the assumption of a readily\n+available source of randomness, termed a [*random oracle*][wiki-random-oracle].\n+The security analysis of those algorithms is conditional on the oracle; we known\n+that they have certain security characteristics, like the difficulty of guessing\n+the correct message or impersonating somebody, only given a true random oracle.\n+\n+And security software has a problem here. Computers are inherently\n+deterministic, made to behave reproducibly given a known program and starting\n+state. How to go about solving this?\n+\n+[dsa-sensitivity]: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm#Sensitivity\n+[gcm-security]: https://en.wikipedia.org/wiki/Galois/Counter_Mode#Security\n+[wiki-oaep]: https://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding\n+[wiki-blinding]: https://en.wikipedia.org/wiki/Blinding_%28cryptography%29\n+[wiki-random-oracle]: https://en.wikipedia.org/wiki/Random_oracle\n+\n+## Random failures\n+\n+Before taking a look at how we try to solve this problem, let's instead consider\n+what happens if we fail to do so. There is even a [Wikipedia\n+page][wiki-random-attacks] on this problem, which is a nice starting point. Some\n+of the highlights:\n+\n+The first public release of Netscape's original SSL, version 2.0, was\n+[broken][ian-goldberg-netscape] several months after its release. The weakness\n+was in seeding the generator with the current time, and process ID and parent\n+process ID of the browser. The time stamp can be guessed to a certain precision,\n+leaving only its sub-second part and the two PIDs unknown. This relatively small\n+unknown space of initial values can be brute-forced.\n+\n+About a decade later, Debian patched their version of OpenSSL and reduced RNG\n+seeding to the current PID. As a result, only 32767 random sequences were\n+possible. This flaw went undetected for two years and became known as the\n+[Debian fiasco][debian-fiasco]. Personal reports indicate that some of the 32767\n+distinct secret keys that could be generated with OpenSSL on a Debian system\n+during that time are still in circulation.\n+\n+Computing the largest common divisor of a pair of numbers is much faster than\n+discovering all the prime divisors of a particular number. RSA public keys\n+contain a number, and secrets keys contain its factors. An RSA key is usually\n+generated by randomly picking the factors. If a pool of keys was generated with\n+a heavily biased random number generator, such that factors are likely to\n+repeat, it is possible to search for common factors in all pairs and crack the\n+affected keys, a technique which produces\n+[spectacular][factoring-rsa-smartcards] [results][mining-your-ps-and-qs].\n+\n+Recently, a bitcoin application for Android was\n+[discovered][reddit-bitcoin-post] to be downloading its random seed from\n+[random.org](http://www.random.org). It wasn't even necessary to intercept this\n+unencrypted traffic, because the website started serving a redirect page and the\n+Android application was left seeding its RNG with its text. It therefore started\n+generating the same private ECDSA key and the associated bitcoin address for\n+every affected user, an issue which reportedly [cost][thereg-bitcoin] some users\n+their bitcoins.\n+\n+Playstation 3 game signatures can be forged. Sony [reused][schneier-sony] a\n+single `k`-parameter, which is supposed to be \"unique, unpredictable and\n+secret\", for every ECDSA signature they made. This lead to complete compromise\n+of the signing keys. Admittedly, this not really a random number generator\n+problem in itself, but it is a limit situation of an RNG malfunction.\n+\n+These only are some of the most spectacular failures related to random numbers.\n+For example, it is widely known in security circles that RNGs of embedded\n+devices tend to be predictable. So if you were designing a unikernel operating\n+system, you wouldn't want to end up on that Wikipedia page either.\n+\n+[wiki-random-attacks]: https://en.wikipedia.org/wiki/Random_number_generator_attack\n+[ian-goldberg-netscape]: http://prng.net/faq/netscape-ssl/\n+[debian-fiasco]: http://research.swtch.com/openssl\n+[factoring-rsa-smartcards]: https://eprint.iacr.org/2013/599\n+[mining-your-ps-and-qs]: https://factorable.net/weakkeys12.extended.pdf\n+[reddit-bitcoin-post]: https://www.reddit.com/r/Bitcoin/comments/37oxow/the_security_issue_of_blockchaininfos_android/\n+[thereg-bitcoin]: http://www.theregister.co.uk/2015/06/01/blockchain_app_shows_how_not_to_code/\n+[schneier-sony]: https://www.schneier.com/blog/archives/2011/01/sony_ps3_securi.html\n+\n+## Random sequences and stuff\n+\n+But what are random numbers, really? Intuitively, we tend to think about them as\n+somehow \"dancing around\", or being \"jiggly\" in a sense. If we have a software\n+component that keeps producing random outputs, these outputs form a sequence,\n+and we hope this to be a [random sequence][wiki-random-sequence].\n+\n+But such a thing is notoriously difficult to define. The above page opens with\n+the following quote:\n+\n+\"A random sequence is a vague notion... in which each term is unpredictable to\n+the uninitiated and whose digits pass a certain number of tests traditional with\n+statisticians\"\n+\n+The intuitive jigglyness is captured by [statistical\n+randomness][wiki-statistical-randomness]. We require each output, taken\n+independently, to come from the same distribution (and in fact we want it to be\n+the uniform distribution). That is, when we take a long sequence of outputs, we\n+want them to cover the entire range, we want them to cover it evenly, and we\n+want the evenness to increase as the number of outputs increases -- which\n+constitutes a purely frequentist definition of randomness. In addition, we want\n+the absence of clear patterns between outputs. We don't want the sequence to\n+look like `7, 8, 9, 10, ...`, even if we sum this with a bit of noise, and we\n+don't want correlation between outputs. The problem here is that no-one really\n+knows what \"having patterns\" means; it is entirely possible that we simply\n+searched for a pattern too simple, and that in fact there is one fully\n+explaining the sequence lurking just around the complexity corner.\n+\n+Nonetheless, there is a well established battery of tests to check statistical\n+randomness of RNG outputs, called the [Diehard Tests][diehard-web], and serves\n+as the de-facto standard for random number generators. Here's the beginning of a\n+certain sequence that [passes][marsaglia-pi-randomness] the test with flying\n+colors:\n+\n+```\n+3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, ...\n+```\n+\n+We still would not recommend using π as a secret key. We would recommend\n+releasing software for everyone to study, which uses that sequence to generate\n+the secrets, even less. But what went wrong?\n+\n+The other [concept of randomness][wiki-algorithmic-randomness]. Roughly, a\n+random sequence should not be predictable to anyone with any knowledge other\n+than the sequence itself. In other words, it cannot be compressed now matter how\n+much we try and in the extreme, this means that it cannot be generated by a\n+program. While the latter restriction is obviously a little too strong for our\n+purpose, highlights a deep distinction in what people mean by being \"random\".\n+Jumping around is one thing. Being actually unpredictable is wholly different\n+thing.\n+\n+For example, there are many [simple][wolfram-rule-30] mathematical processes\n+which generate sequence with high statistical randomness, but knowing the rule\n+and observing some of the outputs is enough to predict the rest of the sequence.\n+The entire future behavior of one of the most commonly used RNGs in various\n+programming packages, [Mersenne twister][mersenne-twister], can be predicted\n+after observing only 624 outputs in a row.\n+\n+We can look for something approaching a \"true\" random sequence in the nature.\n+The current agreement is that the nature of quantum processes is random in this\n+sense, and random sequences based on this idea are readily available for\n+[download][dgrb-boskovic]. Or we can use the microphone and keep recording; the\n+lowest-order bits of the signal are pretty random. But we cannot write a program\n+to generate an actually random sequence.\n+\n+Still, we need to compromise. Somewhere in between the basic jigglyness and a\n+sequence that cannot be described by anything other than itself is the area of\n+Cryptographically Secure Pseudo Random Number Generators (CSPRNGs). These have a\n+property that it is computationally prohibitively expensive to distinguish their\n+output from a \"true\" random sequence, with a high probability. This also means\n+that it is computationally prohibitively expensive to reconstruct their internal\n+state, just by looking at their outputs. In a sense, someone trying to predict\n+the outputs can not take a shortcut and is reduced to starting the generator\n+with all the possible states and checking if the output of their generator\n+matches the observed sequence, a laborious task. This is how we can quantify a\n+CSPRNG unpredictability: it takes trying about half of all the possible states to\n+guess the state.\n+\n+MirageOS's security stack contains a CSPRNG, a design called [Fortuna][fortuna].\n+What it really does, is encrypt the simple sequence `0, 1, 2, 3, ...` with AES\n+(AES-CTR) using a secret key. This makes it as resistant to prediction as AES is\n+to [known-plaintext attacks][wiki-known-plaintext]. After each output, it\n+generates a bit more, hashes that, and uses the result as the next key. This is\n+not to improve the statistical randomness, as this is already guaranteed by AES.\n+Rather, it's a form of [forward secrecy][wiki-forward-secrecy]: an attacker who\n+learns the secret key at some point would need to perform the [preimage\n+attack][wiki-preimage-attack] on the hash function to figure out the earlier key\n+and reconstruct the earlier outputs.\n+\n+[wiki-random-sequence]: https://en.wikipedia.org/wiki/Random_sequence\n+[wiki-statistical-randomness]: https://en.wikipedia.org/wiki/Statistical_randomness\n+[diehard-web]: http://www.stat.fsu.edu/pub/diehard/\n+[marsaglia-pi-randomness]: http://interstat.statjournals.net/YEAR/2005/articles/0510005.pdf\n+[wiki-algorithmic-randomness]: https://en.wikipedia.org/wiki/Algorithmically_random_sequence\n+[wolfram-rule-30]: http://mathworld.wolfram.com/Rule30.html\n+[mersenne-twister]: https://en.wikipedia.org/wiki/Mersenne_Twister\n+[dgrb-boskovic]: http://qrbg.irb.hr/\n+[fortuna]: https://www.schneier.com/fortuna.html\n+[wiki-known-plaintext]: https://en.wikipedia.org/wiki/Known-plaintext_attack\n+[wiki-forward-secrecy]: https://en.wikipedia.org/wiki/Forward_secrecy\n+[wiki-preimage-attack]: https://en.wikipedia.org/wiki/Preimage_attack\n+\n+## Entropy\n+\n+Still, just like any other software RNG, Fortuna is just a deterministic\n+algorithm. Its entire output is as unpredictable as its initial seed. From the\n+information perspective, a RNG can only stretch what was unpredictable about its\n+initial seed into an equally unpredictable sequence. The best generators do not\n+give out more hints about their staring position, but they can never out-race\n+the amount of unpredictability that was contained in the seed.\n+\n+We often call this unpredictable bit *entropy*, after the quality we are hoping\n+it can bring into our RNG - the actual unpredictability. In a sense, by\n+employing an algorithmic generator, we have just shifted the burden of being\n+unpredictable around. But now we're cornered and have to search for it in the\n+only place where a computer can find it -- in the physical world.\n+\n+A typical (kernel-level) RNG reaches out into the world around it through\n+hardware interaction: as hardware events happen, various drivers tend to feed\n+small packets of data, like time and hardware-specific state, into the RNG.\n+These events are a product of the user interacts with the keyboard and mouse, of\n+network packets arriving at the interface, or the hard drive asserting\n+interrupts to signal the end of a DMA transfer. These events are combined\n+together and used as a seed for a deterministic CSPRNG.\n+\n+In fact, describing them as a \"seed\" from which the entire sequence is unfolded\n+is a deliberate oversimplification: what really happens is that the RNG is\n+continuously fed with random events, which change its state as they arrive,\n+while the requests for random bytes are served from the RNG. The RNG is used to\n+\"mix\" the unpredictability inherent in its input, that is, to smooth out\n+various timestamps and similar values into a statistically well-behaved\n+sequence.\n+\n+## Do Virtual Machines Dream of Electric Sheep?\n+\n+The problem is that a virtual machine (VM) in a typical configuration barely\n+sees any physical hardware. Users do not interact with VMs in server scenarios\n+using a directly-connected keyboard and mouse. VMs make use of a virtualized\n+network interface and virtualized disks. Even CPU features can be intercepted\n+and virtualized. There is hardly any entropy in this environment.\n+\n+This is a known problem and [various][randomness-exposed]\n+[analyses][not-so-random] of the weakness of RNGs in virtual environments have\n+been published. The problem is especially severe in the beginning, that is,\n+after boot. Since the gradual trickle of unpredictability from hardware events\n+slowly moves the random stream into an unpredictable state, at the very start,\n+it tends to be in a predictable state. Typically, operating systems store a\n+block of their RNG output on shutdown and use it to quickly reseed the RNG on\n+boot, in order to reuse the already-diverges state of the previous boot to\n+quickly kick-start unpredictability. Unfortunately, it is common to boot several\n+machines from the same system image, or from a pristine image lacking a RNG\n+seed, making their RNGs vulnerable to prediction near to the startup phase.\n+\n+To help solve these problems, we employ several sources of entropy in MirageOS\n+unikernels. In the case of a Unix executable, we simply reuse the system's RNG,\n+as exposed via `/dev/urandom`, as the source of entropy. The kernel is in a much\n+better position to enter an unpredictable state than a process running\n+underneath. In the case of a unikernel running on top of Xen, we separate\n+entropy sources according to their origin: some are within the unikernel itself,\n+and some are external.\n+\n+In the external case, we again rely on the kernel interacting with the hardware\n+(dom0) to harvest the actual environmental entropy. We have a background\n+service, [Xentropyd][xentropyd], which runs in dom0, reads the RNG and serves\n+its output to other domains through the Xen Console. The problem is that in many\n+scenarios, like hosting on popular cloud providers, we cannot expect extra\n+cooperation from dom0. (MAKE A NOTE: this mechanism is present, but not even\n+activated in MirageOS 2.5.0!!)\n+\n+So we need to be able to achieve unpredictability relying purely on what is\n+available inside a unikernel. As a unikernel does not directly see the hardware,\n+to tap into the ambiental entropy, we have to continuously sample the\n+inter-event timings. This process is analogous to what happens in a full-blown\n+OS kernel, except our events are stripped of extra hardware context, and our\n+timers are potentially less granular.","path":"tmpl/blog/mirage-entropy.md","position":289,"original_position":289,"commit_id":"d6caa526f5a78e2b0096acbf50a8d01bd1314c4a","original_commit_id":"d6caa526f5a78e2b0096acbf50a8d01bd1314c4a","user":{"login":"dbuenzli","id":485596,"avatar_url":"https://avatars.githubusercontent.com/u/485596?v=3","gravatar_id":"","url":"https://api.github.com/users/dbuenzli","html_url":"https://github.com/dbuenzli","followers_url":"https://api.github.com/users/dbuenzli/followers","following_url":"https://api.github.com/users/dbuenzli/following{/other_user}","gists_url":"https://api.github.com/users/dbuenzli/gists{/gist_id}","starred_url":"https://api.github.com/users/dbuenzli/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dbuenzli/subscriptions","organizations_url":"https://api.github.com/users/dbuenzli/orgs","repos_url":"https://api.github.com/users/dbuenzli/repos","events_url":"https://api.github.com/users/dbuenzli/events{/privacy}","received_events_url":"https://api.github.com/users/dbuenzli/received_events","type":"User","site_admin":false},"body":"Was not able to make much sense out from this paragraph. Which events are you using (I guess networking events) ? What do you mean by \"our timers are potentially less granular\" and what is the implication. ","created_at":"2015-07-07T19:47:31Z","updated_at":"2015-07-07T19:47:31Z","html_url":"https://github.com/mirage/mirage-www/pull/361#discussion_r34081938","pull_request_url":"https://api.github.com/repos/mirage/mirage-www/pulls/361","_links":{"self":{"href":"https://api.github.com/repos/mirage/mirage-www/pulls/comments/34081938"},"html":{"href":"https://github.com/mirage/mirage-www/pull/361#discussion_r34081938"},"pull_request":{"href":"https://api.github.com/repos/mirage/mirage-www/pulls/361"}}},"pull_request":{"url":"https://api.github.com/repos/mirage/mirage-www/pulls/361","id":39403784,"html_url":"https://github.com/mirage/mirage-www/pull/361","diff_url":"https://github.com/mirage/mirage-www/pull/361.diff","patch_url":"https://github.com/mirage/mirage-www/pull/361.patch","issue_url":"https://api.github.com/repos/mirage/mirage-www/issues/361","number":361,"state":"open","locked":false,"title":"The entropy post","user":{"login":"pqwy","id":325535,"avatar_url":"https://avatars.githubusercontent.com/u/325535?v=3","gravatar_id":"","url":"https://api.github.com/users/pqwy","html_url":"https://github.com/pqwy","followers_url":"https://api.github.com/users/pqwy/followers","following_url":"https://api.github.com/users/pqwy/following{/other_user}","gists_url":"https://api.github.com/users/pqwy/gists{/gist_id}","starred_url":"https://api.github.com/users/pqwy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pqwy/subscriptions","organizations_url":"https://api.github.com/users/pqwy/orgs","repos_url":"https://api.github.com/users/pqwy/repos","events_url":"https://api.github.com/users/pqwy/events{/privacy}","received_events_url":"https://api.github.com/users/pqwy/received_events","type":"User","site_admin":false},"body":"Hey,\r\n\r\nThis is the post on RNG and entropy in Mirage 2.5.0. It covers a little more than purely entropy itself, because I wanted to take the opportunity to make it clear what exactly is going on here, and because I think it's a fascinating topic.\r\n\r\nThe text is still rough, but I'm PR-ing to get review (and help). Any suggestions are more than welcome!","created_at":"2015-07-07T18:52:48Z","updated_at":"2015-07-07T19:47:31Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7bda03381888eeeaacb3eeb7b7fff60e7d797c0f","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/mirage/mirage-www/pulls/361/commits","review_comments_url":"https://api.github.com/repos/mirage/mirage-www/pulls/361/comments","review_comment_url":"https://api.github.com/repos/mirage/mirage-www/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mirage/mirage-www/issues/361/comments","statuses_url":"https://api.github.com/repos/mirage/mirage-www/statuses/d6caa526f5a78e2b0096acbf50a8d01bd1314c4a","head":{"label":"pqwy:entropy","ref":"entropy","sha":"d6caa526f5a78e2b0096acbf50a8d01bd1314c4a","user":{"login":"pqwy","id":325535,"avatar_url":"https://avatars.githubusercontent.com/u/325535?v=3","gravatar_id":"","url":"https://api.github.com/users/pqwy","html_url":"https://github.com/pqwy","followers_url":"https://api.github.com/users/pqwy/followers","following_url":"https://api.github.com/users/pqwy/following{/other_user}","gists_url":"https://api.github.com/users/pqwy/gists{/gist_id}","starred_url":"https://api.github.com/users/pqwy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pqwy/subscriptions","organizations_url":"https://api.github.com/users/pqwy/orgs","repos_url":"https://api.github.com/users/pqwy/repos","events_url":"https://api.github.com/users/pqwy/events{/privacy}","received_events_url":"https://api.github.com/users/pqwy/received_events","type":"User","site_admin":false},"repo":{"id":21711148,"name":"mirage-www","full_name":"pqwy/mirage-www","owner":{"login":"pqwy","id":325535,"avatar_url":"https://avatars.githubusercontent.com/u/325535?v=3","gravatar_id":"","url":"https://api.github.com/users/pqwy","html_url":"https://github.com/pqwy","followers_url":"https://api.github.com/users/pqwy/followers","following_url":"https://api.github.com/users/pqwy/following{/other_user}","gists_url":"https://api.github.com/users/pqwy/gists{/gist_id}","starred_url":"https://api.github.com/users/pqwy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pqwy/subscriptions","organizations_url":"https://api.github.com/users/pqwy/orgs","repos_url":"https://api.github.com/users/pqwy/repos","events_url":"https://api.github.com/users/pqwy/events{/privacy}","received_events_url":"https://api.github.com/users/pqwy/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/pqwy/mirage-www","description":"Website infrastructure for openmirage.org","fork":true,"url":"https://api.github.com/repos/pqwy/mirage-www","forks_url":"https://api.github.com/repos/pqwy/mirage-www/forks","keys_url":"https://api.github.com/repos/pqwy/mirage-www/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pqwy/mirage-www/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pqwy/mirage-www/teams","hooks_url":"https://api.github.com/repos/pqwy/mirage-www/hooks","issue_events_url":"https://api.github.com/repos/pqwy/mirage-www/issues/events{/number}","events_url":"https://api.github.com/repos/pqwy/mirage-www/events","assignees_url":"https://api.github.com/repos/pqwy/mirage-www/assignees{/user}","branches_url":"https://api.github.com/repos/pqwy/mirage-www/branches{/branch}","tags_url":"https://api.github.com/repos/pqwy/mirage-www/tags","blobs_url":"https://api.github.com/repos/pqwy/mirage-www/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pqwy/mirage-www/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pqwy/mirage-www/git/refs{/sha}","trees_url":"https://api.github.com/repos/pqwy/mirage-www/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pqwy/mirage-www/statuses/{sha}","languages_url":"https://api.github.com/repos/pqwy/mirage-www/languages","stargazers_url":"https://api.github.com/repos/pqwy/mirage-www/stargazers","contributors_url":"https://api.github.com/repos/pqwy/mirage-www/contributors","subscribers_url":"https://api.github.com/repos/pqwy/mirage-www/subscribers","subscription_url":"https://api.github.com/repos/pqwy/mirage-www/subscription","commits_url":"https://api.github.com/repos/pqwy/mirage-www/commits{/sha}","git_commits_url":"https://api.github.com/repos/pqwy/mirage-www/git/commits{/sha}","comments_url":"https://api.github.com/repos/pqwy/mirage-www/comments{/number}","issue_comment_url":"https://api.github.com/repos/pqwy/mirage-www/issues/comments{/number}","contents_url":"https://api.github.com/repos/pqwy/mirage-www/contents/{+path}","compare_url":"https://api.github.com/repos/pqwy/mirage-www/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pqwy/mirage-www/merges","archive_url":"https://api.github.com/repos/pqwy/mirage-www/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pqwy/mirage-www/downloads","issues_url":"https://api.github.com/repos/pqwy/mirage-www/issues{/number}","pulls_url":"https://api.github.com/repos/pqwy/mirage-www/pulls{/number}","milestones_url":"https://api.github.com/repos/pqwy/mirage-www/milestones{/number}","notifications_url":"https://api.github.com/repos/pqwy/mirage-www/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pqwy/mirage-www/labels{/name}","releases_url":"https://api.github.com/repos/pqwy/mirage-www/releases{/id}","created_at":"2014-07-10T21:33:47Z","updated_at":"2014-07-01T17:30:05Z","pushed_at":"2015-07-07T18:49:44Z","git_url":"git://github.com/pqwy/mirage-www.git","ssh_url":"git@github.com:pqwy/mirage-www.git","clone_url":"https://github.com/pqwy/mirage-www.git","svn_url":"https://github.com/pqwy/mirage-www","homepage":"http://www.openmirage.org/","size":3451,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"mirage:master","ref":"master","sha":"e93f5baa8ca98968ae68171220e6b6b15464e805","user":{"login":"mirage","id":131943,"avatar_url":"https://avatars.githubusercontent.com/u/131943?v=3","gravatar_id":"","url":"https://api.github.com/users/mirage","html_url":"https://github.com/mirage","followers_url":"https://api.github.com/users/mirage/followers","following_url":"https://api.github.com/users/mirage/following{/other_user}","gists_url":"https://api.github.com/users/mirage/gists{/gist_id}","starred_url":"https://api.github.com/users/mirage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mirage/subscriptions","organizations_url":"https://api.github.com/users/mirage/orgs","repos_url":"https://api.github.com/users/mirage/repos","events_url":"https://api.github.com/users/mirage/events{/privacy}","received_events_url":"https://api.github.com/users/mirage/received_events","type":"Organization","site_admin":false},"repo":{"id":4755520,"name":"mirage-www","full_name":"mirage/mirage-www","owner":{"login":"mirage","id":131943,"avatar_url":"https://avatars.githubusercontent.com/u/131943?v=3","gravatar_id":"","url":"https://api.github.com/users/mirage","html_url":"https://github.com/mirage","followers_url":"https://api.github.com/users/mirage/followers","following_url":"https://api.github.com/users/mirage/following{/other_user}","gists_url":"https://api.github.com/users/mirage/gists{/gist_id}","starred_url":"https://api.github.com/users/mirage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mirage/subscriptions","organizations_url":"https://api.github.com/users/mirage/orgs","repos_url":"https://api.github.com/users/mirage/repos","events_url":"https://api.github.com/users/mirage/events{/privacy}","received_events_url":"https://api.github.com/users/mirage/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mirage/mirage-www","description":"Website infrastructure for mirage.io","fork":false,"url":"https://api.github.com/repos/mirage/mirage-www","forks_url":"https://api.github.com/repos/mirage/mirage-www/forks","keys_url":"https://api.github.com/repos/mirage/mirage-www/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mirage/mirage-www/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mirage/mirage-www/teams","hooks_url":"https://api.github.com/repos/mirage/mirage-www/hooks","issue_events_url":"https://api.github.com/repos/mirage/mirage-www/issues/events{/number}","events_url":"https://api.github.com/repos/mirage/mirage-www/events","assignees_url":"https://api.github.com/repos/mirage/mirage-www/assignees{/user}","branches_url":"https://api.github.com/repos/mirage/mirage-www/branches{/branch}","tags_url":"https://api.github.com/repos/mirage/mirage-www/tags","blobs_url":"https://api.github.com/repos/mirage/mirage-www/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mirage/mirage-www/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mirage/mirage-www/git/refs{/sha}","trees_url":"https://api.github.com/repos/mirage/mirage-www/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mirage/mirage-www/statuses/{sha}","languages_url":"https://api.github.com/repos/mirage/mirage-www/languages","stargazers_url":"https://api.github.com/repos/mirage/mirage-www/stargazers","contributors_url":"https://api.github.com/repos/mirage/mirage-www/contributors","subscribers_url":"https://api.github.com/repos/mirage/mirage-www/subscribers","subscription_url":"https://api.github.com/repos/mirage/mirage-www/subscription","commits_url":"https://api.github.com/repos/mirage/mirage-www/commits{/sha}","git_commits_url":"https://api.github.com/repos/mirage/mirage-www/git/commits{/sha}","comments_url":"https://api.github.com/repos/mirage/mirage-www/comments{/number}","issue_comment_url":"https://api.github.com/repos/mirage/mirage-www/issues/comments{/number}","contents_url":"https://api.github.com/repos/mirage/mirage-www/contents/{+path}","compare_url":"https://api.github.com/repos/mirage/mirage-www/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mirage/mirage-www/merges","archive_url":"https://api.github.com/repos/mirage/mirage-www/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mirage/mirage-www/downloads","issues_url":"https://api.github.com/repos/mirage/mirage-www/issues{/number}","pulls_url":"https://api.github.com/repos/mirage/mirage-www/pulls{/number}","milestones_url":"https://api.github.com/repos/mirage/mirage-www/milestones{/number}","notifications_url":"https://api.github.com/repos/mirage/mirage-www/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mirage/mirage-www/labels{/name}","releases_url":"https://api.github.com/repos/mirage/mirage-www/releases{/id}","created_at":"2012-06-22T20:32:07Z","updated_at":"2015-07-07T12:11:17Z","pushed_at":"2015-07-07T18:52:48Z","git_url":"git://github.com/mirage/mirage-www.git","ssh_url":"git@github.com:mirage/mirage-www.git","clone_url":"https://github.com/mirage/mirage-www.git","svn_url":"https://github.com/mirage/mirage-www","homepage":"https://mirage.io","size":7353,"stargazers_count":59,"watchers_count":59,"language":"OCaml","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":46,"mirror_url":null,"open_issues_count":26,"forks":46,"open_issues":26,"watchers":59,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mirage/mirage-www/pulls/361"},"html":{"href":"https://github.com/mirage/mirage-www/pull/361"},"issue":{"href":"https://api.github.com/repos/mirage/mirage-www/issues/361"},"comments":{"href":"https://api.github.com/repos/mirage/mirage-www/issues/361/comments"},"review_comments":{"href":"https://api.github.com/repos/mirage/mirage-www/pulls/361/comments"},"review_comment":{"href":"https://api.github.com/repos/mirage/mirage-www/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mirage/mirage-www/pulls/361/commits"},"statuses":{"href":"https://api.github.com/repos/mirage/mirage-www/statuses/d6caa526f5a78e2b0096acbf50a8d01bd1314c4a"}}}}