Christopher Swenson http://www.caswenson.com/ 2018-02-07T03:28:42-00:00 Christopher Swenson 64-bit assembly language programming under macOS with NASM2018_02_06_64bit_assembly_language_programming_under_macos_with_nasm.html2018-02-07T03:28:42-00:002018-02-07T03:28:42-00:00Christopher Swenson <p>Just a quick follow-up to a <a href="http://caswenson.com/2009_09_26_assembly_language_programmi <p>Just a quick follow-up to a <a href="http://caswenson.com/2009_09_26_assembly_language_programming_under_os_x_with_nasm.html">previous post on 32-bit assembly language programming for OS X</a>.</p> <p>I've had a fair amount of interest in this post, surprisingly, so I thought I would update it for the 64-bit Intel world we live in now. The biggest change is that the calling convention now uses registers instead of the stack <a href="https://courses.cs.washington.edu/courses/cse378/10au/sections/Section1_recap.pdf">by default</a>, and we use the <code>r*</code> 64-bit registers instead of the <code>e*</code> 32-bit registers.</p> <p>A huge source of annoyance for me is the way we pass pointers to data. Now, in macOS, it is necessary for local data pointers to relative to the instruction pointer, which is most easily accomplished using <code>rel your_data_here</code> and using <code>lea</code> instead of a bare <code>mov</code>. This can also be accomplished using the <code>DEFAULT REL</code> directive, which says that all addresses in <code>lea</code> should be <code>rel</code>.</p> <pre name="code" class="nasm"> BITS 64 DEFAULT REL ; RIP-relative addressing by default ; ; Basic OS X calls to glibc ; ; compile with: ; nasm -g -f macho64 malloc64.asm ; gcc -o a.out malloc64.o ; ; glibc stuff extern _puts, _printf, _malloc, _free ; static data section .data hello_world_str db "Hello world!", 10, 0 int_str db "Address %llx", 10, 0 ; code section .text global _main _main: ; save registers and align stack push rbp push r12 push rbx lea rdi, [hello_world_str] call _puts mov rdi, 16 call _malloc ; check if the malloc failed test rax, rax jz fail_exit mov rbx, rax xor rax, rax mov rsi, rbx lea rdi, [int_str] call _printf ; print "A\nB\n..." mov [rbx], word 0xD41 ; 'A\n' mov r12, 10 _loop: mov rdi, rbx call _puts inc qword [rbx] dec r12 jnz _loop ; free the malloc'd memory mov rdi, rbx call _free xor rax, rax pop rbx pop r12 pop rbp ret fail_exit: mov rax, 1 pop rbx pop r12 pop rbp ret </pre> <p>The output should look something like this:</p> <pre> Hello world! Address 100200000 A B C D E F G H I J </pre> ttyrec conversion2018_01_14_ttyrec_conversion.html2018-01-14T21:32:58-00:002018-01-14T21:32:58-00:00Christopher Swenson <p>I'm an armchair fan of <a href="http://www.nethack.org">NetHack</a> and other roguelikes, though <p>I'm an armchair fan of <a href="http://www.nethack.org">NetHack</a> and other roguelikes, though I don't play them too much. I've always wondered why there wasn't, say, a NetHack screensaver that would just play through the game, since it seemed to be ripe for screensavering.</p> <p>I'd found some pieces of tools that might do the job around the Internet: <a href="http://nethack.wikia.com/wiki/Notable_ascensions">TTY "recordings" of NetHack ascensions</a> (essentially, replaying through the terminal sequences while they are playing), <a href="https://github.com/selectel/pyte">pyte</a> (a Python terminal emulator), and <a href="https://pypi.python.org/pypi/images2gif">images2gif</a> (a mostly working animated GIF maker).</p> <p>It's <em>almost</em> there, so I wrote the missing piece that takes the screen buffer (as a matrix of character codes) and converts it to an image using an <a href="http://en.wikipedia.org/wiki/Code_page_437#mediaviewer/File:Codepage-437.png">old DOS bitmap font</a>, and then all of the glue code to make it all work together. I sped up the results by 5&times; or so, and then run the whole thing through ImageMagick in the end to shrink the animated GIF.</p> <p>I open sourced the whole thing as <a href="https://github.com/swenson/ttyrec2gif">ttyrec2gif</a>. It produces nice GIFs like:</p> <p><img src="http://m.caswenson.com/images/out00001.gif" alt="Sample nethack screensaver animated GIF" /></p> <p>There's one last piece: how to actually turn this into a screen saver.</p> <p>In OS X, it's a matter of writing a little Quartz Composer program to randomly pick GIFs from a directory and play them as movies. This has hard-coded paths in it, because I haven't figured out how to make OS X screen saver configuration parameters, so I'll just post a screen shot of what the program looks like:</p> <p><img src="http://m.caswenson.com/images/nethack-screensaver_-_Editor_2014-10-19_11-38-46.png" alt="Quartz Composer screenshot of my screensaver app."/></p> <p>(Having Quartz pick a random GIF and then reload and pick another one after 5 minutes is a fun challenge. The best solution I came up with involved using a wave generator that, when rounded, would trigger an event only when it reached its apex, and this triggered a sampler to resample and pick a new random GIF. Kind of a Rube Goldberg way of doing it, but it was fun.)</p> <p>Anyway, at the very least, there are some cool GIFs out there to look at now. I'm considering doing a big run and converting a more runs into GIFs, but it's very time-consuming (it takes many hours to convert a game to an animated GIF for now).</p> Friendship API2018_01_14_friendship_api.html2018-01-14T21:32:58-00:002018-01-14T21:32:58-00:00Christopher Swenson <p>This weekend, I wrote a quick email application called <a href="http://friendshipapi.com">Friend <p>This weekend, I wrote a quick email application called <a href="http://friendshipapi.com">Friendship API</a>. It solves a problem that I have: I am just terrible at staying in touch with my friends via email. This is a shame: email is really a great tool for keeping in touch.</p> <p>Friendship API works like this: it uses ContextIO to look at people you interact with, filter out what looks like spam and business email, and send you a weekly email reminding you that you owe a person an email.</p> <p>There's no account or password to manage: the weekly emails contain action links to ignore certain people in the future, generate another reminder, and cancel your subscription. Hence, I use the term "email application".</p> <p>There's also very little storage we have to do: basically, we keep track of email addresses of people who sign up, their ContextIO tokens, what recommendations we send out, and a list of email addresses that the people want to ignore. If they cancel their subscription, we revoke our ContextIO tokens.</p> <h2>Why Python? The Stack</h2> <p>It's all built on Python 2.7. Why Python? Because there are a lot of great libraries and utilities that I can leverage to help me write the app quickly (over the weekend). Sadly, some of the stack was still not ready for Python 3+.</p> <ul> <li><a href="http://context.io">ContextIO</a>: to do the extremely hard work of connecting to people's email accounts. Plus, they have a great <a href="https://github.com/contextio/Python-ContextIO">Python API</a>.</li> <li><a href="http://mailgun.com">Mailgun</a>: to send emails, which has a dead-simple RESTful API.</li> <li><a href="http://flask.pocoo.org/">Flask</a>: a straightforward way to build simple web APIs.</li> <li><a href="http://www.celeryproject.org/">Celery</a>: a wonderful Python-based task queue system.</li> <li><a href="http://www.sqlalchemy.org/">SQLAlchemy</a>: a robust way to deal with SQL databases.</li> <li><a href="http://heroku.com">Heroku</a>: an easy way to host the email application.</li> <li><a href="https://aws.amazon.com/s3/">Amazon S3</a>: an easy way to host a simple static website.</li> <li><a href="http://themeforest.net/?ref=swenson">ThemeForest</a>: a cheap way to get an website theme that looks good.</li> <li><a href="http://www.amazon.com/gp/product/B003I81BW2/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B003I81BW2&amp;linkCode=as2&amp;tag=mathfigu-20&amp;linkId=KN3HZJLTADLF6DFJ">Diet Dr Pepper</a>: my choice of caffeine to keep me typing throughout the weekend. :)</li> </ul> <p>Massive thanks to the hard work of those who wrote the stack I stand on.</p> <h2>The Process</h2> <p>How does one go about writing an app like this after getting the idea and setting aside a spare weekend? Well, here's the process I followed at least:</p> <ol> <li>Outline in your head (or write down) the features that you need to launch with.</li> <li>Talk it over with someone.</li> <li>Sign up for all of the accounts you might need and don't already have (in this case, the ContextIO developer account).</li> <li>Write a simple program to prove that the hard things are possible: in this case, that is reasonable to use ContextIO to get the data we need, and then to generate sensible recommendations.</li> <li>Use Celery to make the program calls asynchronous.</li> <li>Write a simple Flask app that calls into that program.</li> <li>Use <a href="http://requestb.in/">RequestBin</a> to capture the ContextIO callback, so you know how to capture the authentication token when someone signs in.</li> <li>Write the Flask endpoints to generate a ContextIO signup session, and the Flask endpoint to capture the ContextIO callback information, and process based on that.</li> <li>Hook up the recommendation function to send out an email.</li> <li>Write a barebones web page that calls out to your web site.</li> <li>Setup hosting for everything.</li> <li>Have someone who isn't you test out the flow.</li> <li>Write the rest of the functions for your bare minimum features.</li> <li>Theme the static site.</li> </ol> <p>That was fun!</p> <h1>Future Improvements</h1> <p>There are a few more things that would be nice to do for the application.</p> <ul> <li>Better recommendations. There's still a lot of work that could be done to improve recommendations: <ul> <li>Using past recommendations to influence future recommendations.</li> <li>Adjusting the metric used to pick who to recommend.</li> <li>More advanced filters to differentiate business, spam, and friendship conversations.</li> </ul></li> <li>Adjusting the frequency of recommendations. It's currently hardcoded to one week per email.</li> <li>Better-looking recommendation emails.</li> <li>Suggest topics of conversation. We could pick topics randomly, or we could try to analyze past emails, perhaps.</li> <li>Handle people with multiple email addresses. Currently, the application keys off of email addresses alone, so it can suggest someone whom you might be corresponding with regularly if you are doing so with a different email address.</li> </ul> An simple AES-based random number generator2015_04_26_an_simple_aesbased_random_number_generator.html2015-04-27T00:43:28-00:002015-04-27T00:43:28-00:00Christopher Swenson <p>When browsing through some stack traces doing Go development, I noticed that Go had done somethi <p>When browsing through some stack traces doing Go development, I noticed that Go had done something rather clever in their codebase: on x86-64 processors, they used <a href="http://en.wikipedia.org/wiki/AES_instruction_set">the AES instruction set</a> <a href="https://github.com/golang/go/blob/7a4a64e8f3dc14717695e53c7560992789f8bc9e/src/runtime/asm_amd64.s#L874">to build a hash function</a>.</p> <p>The more I've thought about it, the more this is potentially quite brilliant. AES, the block cipher, compounds multiple rounds of mixing data together, shuffling bits around, and transforming it. For the past several years, all Intel and AMD processors have supported doing an AES round in a single instruction.</p> <p>Hashing and random number generation use the same sort of principles as AES is based on: basically, shuffle bits around and transform them. AES gets cryptographic strength by doing this many times in a row (usually 10+).</p> <p>However, if we are building a <a href="http://en.wikipedia.org/wiki/Hash_table">hash table</a> or generating random numbers for simulations (or other non-cryptographic uses), then we don't need the full cryptographic strength of AES. But, we can still leverage the AES instruction set to build some fast random number generators and hashes.</p> <p>For starters, I've gone ahead and written <a href="https://github.com/swenson/aesrng">an AES-based random number generator</a> that performs a single round of AES as its core, and iterates to produce more random numbers.</p> <p>In practice, it is about twice as fast as the <a href="http://en.wikipedia.org/wiki/Mersenne_twister">Mersenne Twister</a>.</p> <p>I think that the biggest reason that most people haven't done this is that it's a relatively new feature, and doing CPUID detection to make sure that the AES instruction set is available at runtime is kind of annoying. At the very least, this library has those pieces already written.</p> <p>Some caveats:</p> <ul> <li>The quality of random numbers produced is not as high as the mersenne twister (they don't <em>quite</em> pass the <a href="http://www.phy.duke.edu/~rgb/General/dieharder.php">dieharder</a> test suite). They're not terrible either; they're just not as good.</li> <li>This only works on relatively modern x86-64 processors. To detect support, you can use the included <code>cpuid.h</code> to call <code>intel_has_feature(INTEL_FEATURE_AES)</code> to see if the <code>CPUID</code> instruction indicates support for the AES instruction set.</li> <li>It's very probably that more performance or better numbers could be created. I used a fairly straightforward seed and random number generator.</li> </ul> <p>I've also been experimenting with an AES-based hash function that is extremely similar to this. There is an equivalent test set to the Diehard tests for hash functions, called <a href="https://code.google.com/p/smhasher/">SMHasher</a>.</p> <p>Unfortunately, my preliminary results are not encouraging: AES instructions are possibly slower than the Murmur3 algorithm when tuned to pass the SMHasher tests. With only 1 or 2 rounds of encryption used, my AES hash function does not seem to pass the SMHasher suite, though it does after 3 or 4 rounds. Unfortunately, these extra rounds make the performance not quite as competitive.</p> <p>I'm still tinkering with the AES-based hash function though to try to make it faster or hash better. I'll report back with my findings.</p> So You Want To Learn Crypto, Part 2: Cyclic Groups And Short Codes2014_11_30_so_you_want_to_learn_crypto_part_2_cyclic_groups_and_short_codes.html2014-11-30T13:05:00-00:002014-11-30T13:05:00-00:00Christopher Swenson <p><a href="http://www.caswenson.com/2014_11_06_so_you_want_to_learn_crypto_part_1">Part 1 is here< <p><a href="http://www.caswenson.com/2014_11_06_so_you_want_to_learn_crypto_part_1">Part 1 is here</a></p> <h2>Modular arithmetic</h2> <p>When I first started tinkering with crypto, one thing I was confused about was modular arithmetic. It's applications to crypto are everywhere: AES is based in part on finite field arithmetic, for example. However, modular arithmetic acts a little strange if you aren't used to it. I've seen this topic confuse just about everyone at some point, so I thought I would explore modular arithmetic a little, and show a fun application of it for generating shortened URLs (or short codes).</p> <p>To start, you can think of modular arithmetic simply as: every time you do a numerical operation, like adding, subtracting, or multiplying two integers, you also take the modulus with respect to some other number. Like, we know that $1 + 10 = 11$, but if we are doing arithmetic modulo $6$, then $1 + 10 \equiv 5$ (because $11 \div 6 = 1$ with remainder $5$). We use $\equiv$ to mean "equals modulo something" or "is congruent modulo something".</p> <p>Addition, subtraction, and multiplication are easy: just add, subtract, or multiply the numbers like normal, then divide by the modulus and take the remainder.</p> <p>Some more examples modulo $6$: $2 + 5 \equiv 1$, $2 \cdot 5 \equiv 4$, $2 - 5 \equiv -3 \equiv 3$.</p> <p>There in the last example we saw that $-3 \equiv 3$ modulo $6$. In math, we often will use only the positive remainder (so, $3$), but in some programming languages, negative remainders are allowed in some cases (so you might see $-3$). For the most part, that doesn't matter: all of the arithmetic will still work as expected.</p> <h2>Division, or, multiplicative inverses</h2> <p>Addition has its opposite, subtraction, that works by default. Multiplication also works, but its opposite, division, isn't <em>guaranteed</em> to work.</p> <p>For example, we might want to know, can we divide by 2? Division is really multiplying by the inverse, so what is the inverse of 2? Looking, again, modulo 6:</p> <p>$$2 \cdot 1 \equiv 2$$ $$2 \cdot 2 \equiv 4$$ $$2 \cdot 3 \equiv 0$$ $$2 \cdot 4 \equiv 2$$ $$2 \cdot 5 \equiv 4$$</p> <p>So, $2$ has no inverse modulo $6$, because nothing, when multiplied by $2$, is equal to $1$. And if we try to divide by $2$ modulo $6$, it fails: $4 \div 2$ is still $2$, but what is $3 \div 2$? Normally, we'd just say $3/2$ or $1.5$, but we don't have fractions or decimal points here: all we have is the integers $0--5$, and <em>none</em> of them, when multiplied by $2$, is equal to $3$.</p> <p>But, sometimes we have multiplicative inverses. Modulo $6$, $5$ has a multiplicative inverse: it's also $5$. This means that dividing by $5$ is the same as multiplying by it.</p> <p>$$2 \cdot 5 \equiv 10$$ $$2 \div 5 \equiv 2 \cdot 5 \equiv 10$$</p> <h2>Cyclic groups</h2> <p>Okay, so, division sometimes works. When?</p> <p>Well, simply put, division is guaranteed to work, in that we will be able to find a multiplicative inverse, if we are working modulo a prime or a prime power, e.g., modulo $7$ or $7^2 = 49$. When we have such a case, we call the set of numbers modulo the prime (or prime power) a <em>finite field</em>. I won't go into the nitty gritty details on the terminology, but essentially, a <em>field</em> is what we get when division works. Sometimes, we call the numbers (modulo our modulus), except $0$, the multiplicative group, or the cyclic group.</p> <p>Why cyclic? Well, when we are working modulo, say, a prime $p$, then we can generate at least part of the group with another prime, $g &lt; p$, by multiplying $g$ by itself a bunch of times.</p> <p>For example, modulo $11$, with $g = 2$:</p> <p>$$g \equiv 2$$ $$g^2 \equiv 2 \cdot 2 \equiv 4$$ $$g^3 \equiv 4 \cdot 2 \equiv 8$$ $$g^4 \equiv 8 \cdot 2 \equiv 16 \equiv 5$$ $$g^5 \equiv 5 \cdot 2 \equiv 10$$ $$g^6 \equiv 10 \cdot 2 \equiv 20 \equiv 9$$ $$g^7 \equiv 9 \cdot 2 \equiv 18 \equiv 7$$ $$g^8 \equiv 7 \cdot 2 \equiv 14 \equiv 3$$ $$g^9 \equiv 3 \cdot 2 \equiv 6$$ $$g^{10} \equiv 6 \cdot 2 \equiv 12 \equiv 1$$ $$g^{11} \equiv 1 \cdot 2 \equiv 2$$ and we've looped back around. Hence the term <em>cyclic</em>.</p> <p>Modulo $11$, we generated the whole multiplicative group (of size $10$). However, we won't necessarily always generate the entire group: sometimes we get a subgroup instead. For example, look at $g = 2, p = 7$:</p> <p>$$g \equiv 2$$ $$g^2 \equiv 2 \cdot 2 \equiv 4$$ $$g^3 \equiv 4 \cdot 2 \equiv 8 \equiv 1$$ $$g^4 \equiv 1 \equiv 2 \equiv 2$$ and we've looped after only $3$ elements.</p> <p>In general, for a multiplicative group defined by $q$ of size $q - 1$, then any element will generate a subgroup that <em>divides</em> $q - 1$. So, for $7 - 1 = 6 = 2 \cdot 3$, so an element could generate subgroups of size $1$, $2$, $3$, or $6$.</p> <h2>Crypto</h2> <p>So, where's the crypto?</p> <p>Well, first off, we might have noticed that the operation $g^x$ seems to have this "scrambling" effect: the numbers that you get out are kind of random.</p> <p>In fact, they're very random for large moduli $q$. In general, it's believed to be quite hard, given $g$, $q$, and $g^x$ (modulo $q$) to figure out $x$. This is called the <em>discrete logarithm</em> problem, and the fact that it is believed to be difficult means that people have used its difficulty as the foundation behind crypto, such as, especially:</p> <ul> <li><a href="http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange">Diffie&ndash;Hellman</a></li> <li>the <a href="http://en.wikipedia.org/wiki/Digital_Signature_Algorithm">Digital Signature Algorithm</a></li> <li><a href="http://en.wikipedia.org/wiki/ElGamal_encryption">ElGamal</a></li> </ul> <p>How large is "large"? Large $q$ generally means hundreds or thousands of bits in size.</p> <p>We'll come back to this bit in a bit, but the important part to note here is that, in general, inverting the exponentiation, or, taking the discrete logarithm, is hard.</p> <p>Also, finite field arithmetic, in particular, division in a finite field, plays a critical part in <a href="http://en.wikipedia.org/wiki/Rijndael_S-box">AES</a>.</p> <h2>Short codes</h2> <p>There's another useful thing we can use modular arithmetic and discrete logarithms for: a URL shortening scheme, like the URLs you see starting with <code>g.co</code>, <code>t.co</code>, <code>bit.ly</code>, etc.</p> <p>There are a few ways you might try to implement such a scheme. First, we'll assume that you are just assigning each URL a number, and then you'll use something like <a href="http://en.wikipedia.org/wiki/Base32">Base32</a> to convert the number (in binary) to a string.</p> <p>Then how do you assign the number?</p> <ol> <li>Just pick a random number and store it in a database, tied to the full URL.</li> <li>Hash the full URL, and use part of the hash as the number.</li> <li>Use a 1-up counter and tie it to the full URL.</li> <li>Use a 1-up counter, but try to <em>scramble</em> it, for some definition of <em>scramble</em>.</li> </ol> <p>Option 1 is the simplest, and requires basically no math: just generate a random number and stick it in the database. If there is a collision, try again.</p> <p>The downside is that, for very short codes, such as for a 5-character Base32 code, the numbers will all be $&lt; 2^{25}$, and will be very likely to collide because of the <a href="http://en.wikipedia.org/wiki/Birthday_problem">birthday paradox</a>. Lots of collisions means you have to try a bunch of times to insert into the database successfully, and that means more round trips, more chances for weird race conditions, and more chances for bugs.</p> <p>Option 2, hashing the full URL, has similar downsides for short hashes: collisions. This case is even worse though, because you can't change the hash of the URL, so any collisions are fatal, and mean you can't use that full URL. So that option is not great.</p> <p>Option 3, using a 1-up counter in the database, is really easy: just use a built-in 1-up counter, or assign a bunch of numbers to individual servers to use, and have them request new batches of numbers occasionally. However, this leads to undesirable URLs, like <code>http://short.url/AAAAC</code>. It allows users to guess what their URLs might be, and to start poking around at other short URLs.</p> <p>Option 4 is a compromise: we want the simplicity of the 1-up counter, but we don't want users to be able to easily and meaningfully guess the short URLs. We just need a scrambling function.</p> <h2>Scrambling a 1-up sequence</h2> <p>But what to choose?</p> <p>Well, again, we have some options. Some thought might lead you to think: well, we could just add a constant to the number, or multiply by a constant. However, this will be pretty obvious to users: they'd notice that consecutive short URLs would always differ by constant amounts.</p> <p>Another line of thought: could we use the number in a random number generator (say, as the seed), and just turn the crank and use the next number in the sequence? Yes, that's a great idea, as long as we're careful. Specifically, we have to be careful about collisions.</p> <p>Two common random number generators that might fit the bill are <a href="http://en.wikipedia.org/wiki/Linear_feedback_shift_register">linear-feedback shift registers</a> and <a href="http://en.wikipedia.org/wiki/Linear_congruential_generator">linear congruential generators</a>.</p> <p>But, we might have a problem: we might need to "unscramble" the numbers, to tell if they might be in the database. We might just store the scrambled versions next to the 1-up counter, but that would require extra storage, and would probably require us to either write complicated SQL or do an <code>UPDATE</code>, and another database round-trip, to set the scrambled number as well. I don't like either of those options.</p> <p>So, how hard is it to unscramble those random number generators? For linear-feedback shift registers, this is trying to count how many steps it took to get to the given output number, which is kind of difficult to do. But, the same is true of linear congruential generators.</p> <p>Let's take a closer look at the linear congruential generator. It works by, given an number $X_i$, generating the next number by:</p> <p>$$X_ {i+1} = A \cdot X_i + C$$</p> <p>If we assume $C = 0$ and $X_ 0 = 1$, we can compute $X_i = A^i$: this is exactly the same thing as exponentiation, which is how we found cyclic groups. So, to "scramble", all we need to do is exponentiate in our cyclic group.</p> <p>Oh, but wait, this sounds like bad news: we know that inverting exponentiation is the discrete logarithm, which is hard in general.</p> <p>The keywords there are "in general". Can we make pick cyclic groups where it is easy?</p> <p>Yes, we can.</p> <h2>Easy discrete logarithms</h2> <p>If we are working in a cyclic group modulo $p$, a prime, and if $p - 1$ is the product of a bunch of small primes or prime powers, then computing discrete logarithms is easy. To compute discrete logarithms in that case, we can use the <a href="http://en.wikipedia.org/wiki/Pohlig%E2%80%93Hellman_algorithm">Pohlig&ndash;Hellman algorithm</a>.</p> <p>The Pohlig&ndash;Hellman algorithm works by taking advantage of short cycles in the cyclic group: essentially, we can piece together the "full" discrete logarithm by computing a bunch of "small" discrete logarithms, and using the <a href="http://en.wikipedia.org/wiki/Chinese_remainder_theorem">Chinese remainder theorem</a> to stitch them back together. Each of these "small" discrete logarithms are in subgroups defined by the factors of $p - 1$: so a bunch of small factors of $p - 1$ means a bunch of small discrete logarithms to do. If the factors are all very small, then we can just precompute all factors in the small subgroups.</p> <h2>Putting it all together</h2> <p>Since we want roughly five-character codes, we need to find a large 25-bit prime with which to make our cyclic group. Specifically, we want a prime $p$ such that $p - 1$ is all tiny factors. To make the math easier, it would be nice if $p - 1$ has no repeated factors (that is, no prime powers).</p> <p>In <a href="http://www.sagemath.org">Sage</a> (a Python-based mathematics environment), we can find such a prime with this code:</p> <pre> smoothest = 2^30 # just something large for p in primes(2^24, 2^25): smoothness = 0 for f, e in (p - 1).factor(): if e != 1: smoothness = 2^30 break smoothness += f if smoothness < smoothest: smooth = p smoothest = smoothness print smooth, smooth - 1, (smooth - 1).factor() </pre> <p>If we run this snippet, we find the best prime is $17160991$. We can confirm that $17160991 - 1 = 17160990 = 2 \cdot 3 \cdot 5 \cdot 7 \cdot 11 \cdot 17 \cdot 19 \cdot 23$.</p> <p>This means that we can compute logarithms modulo $17160991$ by pre-computing and storing just $87$ numbers (the sum of the factors of $p - 1$) using Pohlig&ndash;Hellman.</p> <p>We also need to pick a base $g$, that is, the number we are exponentiating in our cyclic group. We basically need a number that generates the entire multiplicative group. We can use Sage again to find such a number:</p> <pre> for q in primes(3, 1000): if GF(modulus)(q).multiplicative_order() == modulus - 1: print q break </pre> <p>In this case, we find that $61$ is the smallest prime that generates the full multiplicative group, so it will be our base.</p> <p>So, our procedure so far to scramble an integer $x$ looks like:</p> <p>$$61^x\ (\text{mod}\ 17160991)$$</p> <p>However, there is one tiny problem here: for $x = 0$, we get $1$, and for $x = 1$, we get $61$, and for $x = 2$, we get $3721$. These values stick out a bit (they're all small and easily recognized). To hide them, we can just add some small number to $x$, like 30, giving us</p> <p>$$61^{x + 30}\ (\text{mod}\ 17160991)$$</p> <p>So, for $x = 0$, we have $4244504$ and $x = 1$ gives us $1499879$.</p> <p>If we want to invert a short code, we'll need to unscramble a number $y$ to find the unscrambled number, that is, to find $x$ in</p> <p>$$y \equiv 61^{x+30}\ (\text{mod}\ 17160991)$$</p> <p>In the first of the two examples above, we would be trying to solve:</p> <p>$$4244504 \equiv 61^{x+30}\ (\text{mod}\ 17160991)$$</p> <p>Using the Pohlig&ndash;Hellman algorithm, we can easily compute (with a few modular exponentiations) the discrete logarithm of $4244504$ is $30$, which means that $x = 0$.</p> <h2>Some code</h2> <p>I've released code for the above computations, including the Pohlig&ndash;Hellman algorithm implementation, in Python on <a href="https://github.com/swenson/shortcodes">GitHub</a>, licensed under MIT.</p> How to pronounce programming words2014_11_10_how_to_pronounce_programming_words.html2014-11-10T16:51:59-00:002014-11-10T16:51:59-00:00Christopher Swenson <p>Certain words in programming I've seen people mangle in various ways, and many times, I realized <p>Certain words in programming I've seen people mangle in various ways, and many times, I realized that I don't actually know the correct way to pronounce them.</p> <p>So, I've constructed a list of what I consider to be the correct pronounciations of various words. I've tried to use the original authors of as the source, when possible, or other authoritative sources.</p> <p>In alphabetical order:</p> <ul> <li><em>Akka</em>: ahh kahh, <a href="https://www.youtube.com/watch?v=GBvtE61Wrto">https://www.youtube.com/watch?v=GBvtE61Wrto</a></li> <li><em>Clique</em>: click, <a href="https://www.youtube.com/watch?v=txaGsawljjA">https://www.youtube.com/watch?v=txaGsawljjA</a></li> <li><em>GIF</em>: jif, sorry, <a href="http://www.olsenhome.com/gif/">http://www.olsenhome.com/gif/</a></li> <li><em>GNU</em>: guh-new, <a href="http://upload.wikimedia.org/wikipedia/commons/2/24/En-gnu.ogg">http://upload.wikimedia.org/wikipedia/commons/2/24/En-gnu.ogg</a></li> <li><em>Godel</em>: gurr-dle, <a href="http://upload.wikimedia.org/wikipedia/commons/f/fd/Kurt_g%C3%B6del.ogg">http://upload.wikimedia.org/wikipedia/commons/f/fd/Kurt_g%C3%B6del.ogg</a></li> <li><em>Knuth</em>: kuh-nooth, <a href="http://cs.stanford.edu/~uno/faq.html">http://cs.stanford.edu/~uno/faq.html</a></li> <li><em>LaTeX</em>: Leslie Lamport says there is no official pronunciation. Knuth pronounces it lah-tech, <a href="http://www.webofstories.com/play/donald.knuth/69;jsessionid=57A6533A825A17DE1474F30F5B254ADD">http://www.webofstories.com/play/donald.knuth/69;jsessionid=57A6533A825A17DE1474F30F5B254ADD</a></li> <li><em>Linux</em>: Lih-nucks, <a href="https://www.youtube.com/watch?v=uK0xXFZtJ8Q">https://www.youtube.com/watch?v=uK0xXFZtJ8Q</a></li> <li><em>MySQL</em>: my-ess-que-ell, <a href="https://www.youtube.com/watch?v=nalzRhtn-vw">https://www.youtube.com/watch?v=nalzRhtn-vw</a></li> <li><em>NumPy</em>: num-pie, <a href="https://www.youtube.com/watch?v=vrPRwUOt-7k">https://www.youtube.com/watch?v=vrPRwUOt-7k</a></li> <li><em>OS X</em>: oh-ess-Ten, <a href="https://www.youtube.com/watch?v=WcZqJHH3Gkc">https://www.youtube.com/watch?v=WcZqJHH3Gkc</a></li> <li><em>PyPI</em>: pie pee eye, <a href="http://www.quora.com/How-do-Pythonistas-pronounce-PyPy-and-PyPI">http://www.quora.com/How-do-Pythonistas-pronounce-PyPy-and-PyPI</a></li> <li><em>Qt</em>: cute, <a href="https://www.youtube.com/watch?v=2hE-Oo4ltJg">https://www.youtube.com/watch?v=2hE-Oo4ltJg</a></li> <li><em>Scala</em>: scah-lah, rhymes with challah, <a href="https://www.youtube.com/watch?v=ecekSCX3B4Q">https://www.youtube.com/watch?v=ecekSCX3B4Q</a></li> <li><em>SQL</em>: ess-que-ell (since Sequel is the predecessor), <a href="https://www.youtube.com/watch?v=BvmMGHtqY6Q">https://www.youtube.com/watch?v=BvmMGHtqY6Q</a></li> <li><em>TeX</em>: tech. <a href="https://www.youtube.com/watch?v=C3vILM2cNuo">https://www.youtube.com/watch?v=C3vILM2cNuo</a></li> <li><em>Trie</em>: tree, <a href="http://www.webcitation.org/5pqUULy24">http://www.webcitation.org/5pqUULy24</a></li> <li><em>von Neumann</em>: von noi-man <a href="https://www.youtube.com/watch?v=VTS9O0CoVng">https://www.youtube.com/watch?v=VTS9O0CoVng</a></li> </ul> So You Want To Learn Crypto, Part 12014_11_06_so_you_want_to_learn_crypto_part_1.html2014-11-06T20:00:00-00:002014-11-06T20:00:00-00:00Christopher Swenson <p>Cryptography and cryptanalysis are topics that fascinate many people, but it's a tricky area to <p>Cryptography and cryptanalysis are topics that fascinate many people, but it's a tricky area to get into. For instance, knowing the difference between a <a href="http://en.wikipedia.org/wiki/Hash_function">hash function</a> and a <a href="http://en.wikipedia.org/wiki/Cryptographic_hash_function">cryptographically strong hash function</a> is critical, but extremely easy to mix up due to confusing terminology. In general, there's heavy jargon that takes serious studying to get a grasp on.</p> <p>Cryptography is also almost entirely a self-taught discipline: even today, there are few college and graduate courses on cryptography, and most of those only cover the basics. (Indeed, this is why I wrote my book on cryptanalysis: I wanted to teach a class on the topic, and there was no suitable text, so I wrote my own.) This means that it is reasonable, even necessary, to learn a great deal of crypto on your own.</p> <p>With that in mind, I'm going list a few resources, recommendations, and maybe even explain a little bit myself to help get you started.</p> <h2>Some resources to get you started</h2> <ul> <li><a href="https://twitter.com/lvh">lvh</a> has a video from PyCon 2013 called <a href="http://pyvideo.org/video/1778/crypto-101">Crypto 101</a>. I'm not the biggest fan of this video, as it goes through too much material in too short an amount of time, but it does give you a quick look at many different areas of cryptography. Since it does cover so much, don't worry if not everything clicks. Instead, I'd pick something that interests you, like hash functions or AES, and dive further into that one topic. If you try to tackle too many subjects at once, it might be overwhelming.</li> <li>Once you've figured out some areas you actually like, you can start digging in in that particular field.</li> <li>There are some great books on cryptography that you'll probably want to read eventually, so I'll go ahead and list some of them here: <ul> <li><a href="http://www.amazon.com/gp/product/047013593X/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=047013593X&amp;linkCode=as2&amp;tag=mathfigu-20&amp;linkId=T5SNSQXDSP6LHEER">Modern Cryptanlysis</a> by Christopher Swenson. Obviously, I'm a bit biased here, but I wrote some decent introduction sections to many areas of cryptography and cryptanalysis with lots of examples in Python, and there are far worse places to start. I focus primarily on block ciphers (like AES) and public key cryptography (like RSA).</li> <li><a href="http://www.amazon.com/gp/product/0471117099/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0471117099&amp;linkCode=as2&amp;tag=mathfigu-20&amp;linkId=CBNLGUN6PZETLFCV">Applied Cryptography</a> by Bruce Schneier. Extremely dated, but still has some useful stuff. A lot of block and stream cipher stuff.</li> <li><a href="http://www.amazon.com/gp/product/0471223573/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=0471223573&amp;linkCode=as2&amp;tag=mathfigu-20&amp;linkId=6ZT4U6PDRCMS4V3V">Practical Cryptography</a> by Bruce Schneier. A good companion to Applied Cryptography that is a bit more updated, with lots of focus on understanding and correctly using cryptography.</li> <li><a href="http://www.amazon.com/gp/product/1420070029/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=390957&amp;creativeASIN=1420070029&amp;linkCode=as2&amp;tag=mathfigu-20&amp;linkId=CNJQJOUTTRFOKRBH">Algorithmic Cryptanalysis</a> by Antoine Joux. One of the newest books, and chock full of good stuff, with a focus on public key cryptography, stream ciphers, and lattices. This one's probably one of the most advanced cryptography books on the market.</li> <li><a href="http://cacr.uwaterloo.ca/hac/">Handbook for Applied Cryptography</a> by Alfred Menezes, Paul van Oorschot and Scott Vanstone. This book covers a lot of material, but I find it a little terse at times. It is free though, which is a big plus.</li> </ul></li> </ul> <p>This list is not comprehensive: it's probably best to just look at all of the available books, glance through some of the pages and the table of contents, and see if the style, approach, and depth meet your interests. In general, there are two kinds of crypto books you'll see on the market: academic ones and practical ones. When you start out learning, you'll definitely want to focus on the more practical tomes (Schneier and Swenson, for example), and start reading academic books (Joux's book, and many published by academic publishers like Springer) as you get more comfortable.</p> <h2>I know the basics, what next?</h2> <p>Once you know Crypto 101 and you want to dive into Crypto 201, where do you go?</p> <p>One great next step is to work on <a href="http://cryptopals.com/">The Matasano Crypto Challenges</a>. Not only will you have to learn a lot to complete them, but you'll have real experience making and breaking codes. I think this is a great next-step for a would-be crypto enthusiast. (And this is even a great step for cryptographers on any level &ndash; I certainly had fun doing them.)</p> <p>There are some Coursera courses on Cryptography taught by Dan Boneh (<a href="https://www.coursera.org/course/crypto">Cryptography I</a> and <a href="https://www.coursera.org/course/crypto2">Cryptography II</a>). I haven't taken them, but the instructor is top-notch, and the syllabi are solid.</p> <p>As you progress further in your knowledge, you should start reading recent research papers in cryptography. One great place to keep track of is the IACR <a href="http://eprint.iacr.org/">Cryptology ePrint Archive</a>. These are the cutting edge of cryptography and cryptanalysis, and they range from beginner-friendly to extremely deep, specialist literature. The writing and publication quality of the cutting-edge papers also has a wide range. And even though it is possible to learn almost everything about cryptography and cryptanalysis for free from these papers, the books in the previous section are often much easier to learn from since they have consistent writing style, terminology, notation, and editing.</p> <h2>This part confuses me. Help!</h2> <p>I'm going to have some more blog posts in the near future clearing up some areas that I've seen be particularly sticky for some people. In particular, I know that there are some sticky points in finite fields, RSA, and hashing that I think I can help clarify.</p> <p>Check back next week for part 2 of <em>So You Want To Learn Crypto</em>.</p> What I Learned As A Teen Programmer2014_10_26_what_i_learned_as_a_teen_programmer.html2014-10-27T01:38:59-00:002014-10-27T01:38:59-00:00Christopher Swenson <p>I recently stumbled across the source to a NES emulator I wrote when I was a teenager. Reading t <p>I recently stumbled across the source to a NES emulator I wrote when I was a teenager. Reading through the source code, I thought a lot about how much I had grown as a developer and engineer since that long-forgotten summer. For instance, I now understand things like (ha!) having a consistent style (some of my code was indented over 24 spaces so that it would be in the middle of the monitor), writing comments (there are basically no comments in this code), and tests (I didn't even know that testing was a thing). But, I also realized that writing that emulator, and all of the development I did at the time, laid a strange foundation that colored how I've thought about programming since.</p> <p>Up to that point, I'd puttered around a little in BASIC and C, but I didn't fall in love with programming until I discovered assembly language. Assembly was the first thing that really clicked for me: it was so simple, its semantics were so clear, and I found some great resources to help me learn (e.g., a nice tutorial I found on a BBS). So, in my heart, assembly language is my first language.</p> <p>When I tell this to people now, they give me strange looks. It's almost unfathomable to teach people assembly language first. But, in doing so, I learned an awful lot about how software works, how computers work, and what foundation programming languages are built on top of.</p> <p>My masterpiece as a teen programmer was the NES emulator I wrote entirely in x86 assembly language: almost exactly 10,000 lines of it. This was my first taste of a real program: it was potentially useful, it had meaning, and it was a moderately large software project (my first). Programming this was what made me dig deep and learn how to program.</p> <p>Here's some things that writing that emulator, and assembly language in general, taught me:</p> <ol> <li><p><strong>Data is just memory</strong>. I remember trying to understand pointers in C and being incredibly frustrated with the strange syntax and the weird semantics. On the other hand, dealing with memory in assembly is so clearcut: memory is just a giant flat array (more or less), and pointers just point to stuff in that giant array. Indirect references, arrays of pointers, etc., make so much more sense when boiled down like this.</p> <p>Even today, when trying to visualize many problems, I think to myself: what does this look like in memory? What structures are being used and how are they laid out? This helps me understand how algorithms and data structures work together.</p></li> <li><p><strong>How to write everything yourself.</strong> There were some limited BIOS and DOS kernel calls, but for the most part, if I needed some capability, say, reading from a floppy drive or writing to the screen, I had to write it from scratch: calling I/O ports, reading and writing to RAM addresses, etc. If I wanted graphics, I had to draw every single pixel myself. These days, I know that this is just reinventing the wheel, something normally regarded as a bad habit; however, I also learned an awful lot about inventing wheels doing it. I still have the bad habit of writing too many things myself, and not leveraging existing libraries as often as I should, so this lesson wasn't a complete victory.</p></li> <li><p><strong>Sometimes you just have to buckle down and program all of the tedious parts</strong>. A big part of the NES emulator is writing a cycle-accurate CPU emulator. I'm sure that you can imagine how daunting it must be to start writing <code>CPU.ASM</code> and have an empty jump table with 256 entries, knowing that you have to write all 256 functions to handle every single operation. Even worse, you have to get every single piece <em>exactly</em> right.</p> <p>This taught me an extremely valuable skill: being able to focus on tedious, repetitive tasks. Programming is absolutely chock-full of tedious programming that is absolutely necessary. A great example is date and time functionality (especially time zones): incredibly important code to get correct, but so incredibly tedious and detail-oriented. Being able to dive into thousands of pages of specifications and turn them into working libraries and APIs has paid great dividends in my career as a software engineer, and I first learned that here. Being methodical and untiring is a great skill.</p> <p>And really, sometimes there is just no elegant solution. Often I see programmers strive to come up with a beautiful, simple, clean way to solve a problem, only to realize that the solution doesn't stand up to reality. Sometimes reality is <em>ugly</em>, and sometimes you have to write <em>ugly</em> code to solve it: you just need to program your way out of it.</p></li> <li><p><strong>Assembly is useful</strong>. Learning assembly has come in handy countless times: reading GDB disassembly of my code for degugging, understanding and optimizing performance, writing compilers. Every time I see an assembly dump on a screen, it feels like home.</p></li> <li><p><strong>You have to fail a lot to succeed</strong>. When writing code as a teenager in assembly language, I spent a lot of time crashing my computer. I mean, assembly is really easy to get wrong. When you get it wrong, you often do so catastrophically.</p> <p>But more so, in order to learn how to build things, you first have to learn to build them wrong, and learn to recognize your mistakes. And this code is just full of mistakes.</p></li> </ol> <p>Overall, revisiting old code like this helped me reflect on my programming: where I've been, where I am, where I'm going. I encourage everyone to save as much of their code as they ever write, and come back to it in a few years, and to reflect.</p> Pyway or the highway2014_10_12_pyway_or_the_highway.html2014-10-12T19:15:24-00:002014-10-12T19:15:24-00:00Christopher Swenson <p><a href="https://github.com/swenson/pyway">Pyway</a> is a database migration tool that I wrote t <p><a href="https://github.com/swenson/pyway">Pyway</a> is a database migration tool that I wrote to solve the problem of: I want database migrations, but I don't want to bring <a href="http://guides.rubyonrails.org/migrations.html">Rails</a> or the entire <a href="http://flywaydb.org/">JVM</a> with me just to do them.</p> <p>So, I wrote a little script that does the most simple thing you can do with database migrations: keep track of them by file name, and run any new ones in sorted order. Python is pretty much universally present at this point in server environments, so that's the tool that I used.</p> <p>This is nothing sophisticated, and currently has <code>sqlite3</code> hardcoded in it, but it should be trivial to modify for any other databases. I didn't even use Python bindings for <code>sqlite3</code>: I just called the command-line program to do the migrations. That way, it should be easy to modify it for other databases in the near future.</p> <p>I'll probably end up adding Postgres or MySQL support sometime soon (when one of my projects that uses those technologies needs to be updated).</p> This Holacracy Thing2014_10_05_this_holacracy_thing.html2014-10-05T21:35:58-00:002014-10-05T21:35:58-00:00Christopher Swenson <p>I've had a busy year where I've been trying out a lot of new ideas for working and collaborating <p>I've had a busy year where I've been trying out a lot of new ideas for working and collaborating, and learning a lot. One thing that I learned about and experienced for a few weeks was the system of <a href="http://en.wikipedia.org/wiki/Holacracy">Holacracy</a>. The goal of Holacracy is to have a managerless, self-organized team of people rather than having a traditional top-down management tree. First, I'll state that my view of Holacracy comes after only about a month of working within it, and I don't feel that even after that I fully got it. So take this post with a grain of salt.</p> <h2>It's an AI for a boss</h2> <p>The initial appeal of Holacracy was that the group within the startup I was working at had no boss. The Holacratic system has enough processes in place to theoretically allow decisions to be made without a central leader: a true peer-to-peer sort of system.</p> <p>Essentially, we had several Holacracy meetings a week. These meetings were incredibly structure-heavy. People would propose ideas at point A, discuss them at point B, and these discussions and round robins followed rules that theoretically ensured fairness.</p> <p>An insight I had to this was that <em>your team is controlling an NPC "boss"</em>, and you have a little script that you run through to control its AI.</p> <p>This sounds like a good thing: if you have a very small team, maybe only a couple of people, it might seem like there would not be enough work for a full-time manager. So you can have a cardboard cutout that you all pretend is your boss, and you have a system through which your cardboard captain makes decisions. (I like to think of my cardboard cutout boss as Captain Picard.)</p> <h2>It's too complex</h2> <p>But, this AI is quite complex. Holacracy has a lot of rules. And I mean <a href="http://holacracy.org/constitution">a lot</a>.</p> <p>There was a discussion I had with someone at the startup where they indicated that Holacracy was probably impossible to understand (and certainly to get right) without hiring a special Holacracy consulting company to come in and help you adopt the system &mdash; helping you go learn the rules and make your Captain Picard make decisions, basically.</p> <p>This kind of put me off. A system that's so complex that it takes weeks of training to be able to even use it to have a constructive meeting? It smells a bit fishy to me. Meetings are hard, but they're not <em>that</em> hard, even for a cave troll programmer like me. There are entire consulting companies who would love to sell you tons of expensive training for Holacracy.</p> <p>To make it even worse, it's also nearly impossible to run with Holacracy without using special Holacracy meeting software to help you facilitate your meetings. There's a lot of structure and rules, and the software helps enforce these rules. This also smells a bit fishy.</p> <p>Being a newcomer to this incredibly confusing set of rules was also incredibly unpleasant. I felt lost in a maze of rules. I felt that I couldn't contribute to meetings because I didn't even know when I was allowed to talk. And when I was allowed to talk, I often didn't know what to say. (There are lots of magic words you use to indicate your status, and I sort of had to learn these all from context.) Given that I was on a feeler contract (before I came on full-time), I was not given any training in Holacracy.</p> <p>Finally, with such elaborate rules, there's inevitable rules-lawyering. The rules are meant to be used as a weapon to combat inefficiency, but sometimes people get wrapped up in them to the point that legitimate, necessary work was dropped on the floor because it did not fit into the system well. I felt that at times this did a disservice to the team, and certainly to the users of our software.</p> <h2>It's time-consuming</h2> <p>Given the AI manager and the complex rules, I think it's about a wash so far. It's nice not to have a manager, but annoying to have a large RPG rules book to memorize before starting work.</p> <p>But, those meetings to make Captain Picard dance were <em>ridiculously time-consuming</em>. I'm of the leave-me-alone-so-I-can-code-in-my-cave School of Programming. I can be coaxed out of my cave for the occasional meeting, and sometimes even enjoy it. But the level of meetings required for the Captain Picard gears to run was debilitating. Hours and hours each week were spent. Several people had a significant amount of work before and after meetings dedicated to cleanup and prep work. (And even during the meeting, a couple of people were often not allowed to participate in discussions, or supposed to limit such contributions significantly, as they played the role of facilitator, secretary, etc.)</p> <h2>In conclusion</h2> <p>Coming in as a new hire is daunting. You are adjusting to a new team, learning their quirks, and learning their work. When you add on a whole new system of thinking about meetings and management, and give no training (when a lot of training is required), you are setting people up to fail. It certainly was just too much for me. Holacracy was a huge contributor to my deciding not to work for this particular company long-term.</p> <p>The company that I worked with clearly prefers Holacracy, and that's their prerogative. And if it works for them, that's great. But, I didn't feel comfortable in that system, and <a href="http://recode.net/2014/10/03/holacracy-or-hella-crazy-the-fringe-ideas-driving-the-las-vegas-downtown-project/">I don't think I'm the only one</a>. I don't expect to in the future, and I plan to avoid working for companies that embrace Holacracy.</p> <p>Or, you know, maybe Holacracy could be rebranded to use an actual Captain Picard cardboard cutout and a real RPG-like rulebook. I might have had more fun then.</p>