Saturday, March 3, 2012

Before I get to the StrifeBarge update, I've been thinking about a problem. It's one I vaguely assumed had been definitively solved, but it turns out that's only a matter of perspective. Now that I really sit down to think about how I'd implement a "solution", it's quite obvious that there is no good way of countering a sufficiently long-running and elaborate man-in-the-middle attack.

You can counter short-term MitM attacks by relying on keys that you exchange with Alice before the attack started

You can counter long-term MitM attacks by relying on some additional authentication channel like a phonecall, video conference or SMS chain

You can counter elaborate, long-term MitM attacks by relying on a web of trust to confirm Alice's key

You can counter elaborate, network-saturating, long-term MitM attacks by flying out and actually meeting Alice, exchanging keys in bothsenses

I'm not entirely sure how you can counter supremely elaborate, long-term MitM attacks, but it probably involves several independent notaries that hate each other as well as Alice[1], or possibly a DNA sample in addition to the key exchange. That list was cumulative, by the way, not independent.

Handshakes or public-key crypto get you past regular old surveillance. Both together can get you past an active attacker on the current line, but that's basically it. After that, any technique you try only buys you a bit of extra confidence that your messages aren't being tampered with, and that confidence approaches 100% without ever getting there. Certificate authorities can be compromised, networks of trust can be saturated by an attacker's agents[2], and any other authentication mechanism I can think of can be faked by a sufficiently motivated attacker.

Now, the good news is that StrifeBarge isn't anything like a juicy enough target to tempt the Chinese government into trying something. The bad news is that it's never really possible to fully trust a given user. The best you can hope to do is maximize the chance that they're really who they say they are. From another perspective, the best you can do is make the resource expenditure required to fool your authentication greater than the potential payoff in succeeding. It's relevant, because you don't want an opponent to be able to peek over at your map during a game for some hopefully obvious reasons[3]. This really, truly looked like it should have been solved already, until I plucked it from the periphery of my thoughts and focused on it for a couple of days. Upon closer inspection, I'm not sure why I ever thought that.

With that, let's move on to the highlights.

The big change is that I've settled on a license. The intention is that this game should be out in public for educational purposes in case anyone actually cares, so the AGPL seems most appropriate[4]. I may also pull out the basic stuff into a separate turn-based-HTTP-game framework that I will release under MIT/BSD for people to more easily mess with. That's a long way off[5], but I thought I'd mention it.

The big, noticeable change is that styles, images and js have been incorporated semi-well into the program. It's gone into space rather than the traditional naval setting. Collecting the sprites was easier than it should have been, by the way, and that's entirely thanks to OpenGameArt. The star background is going to have to be replaced by something snazzier at some point, but the rest of the graphics are actually quite nice.

Part of integrating CSS included using cl-css. I tried to avoid it, I really did, because I wanted you to be able to install StrifeBarge just by ql:quickloading the single appropriate package name, but doing CSS3 directives manually sucks some of the biggest donkey testicles that exist on this earth. I decided that the ability to define functions like

instead of manually writing this shit every time just about trumps ease of installation. Once the web stabilizes enough that the big browser writers don't feel the need to implement their own cutely named directives and ignore CSS3 equivalents, I'll ditch my CSS generator. Until then, I will happily debase myself for the ability to define higher level constructs which will deal with the equine testicle sucking on my behalf. I guess I could submit the library for consideration in quicklisp, but I really, really don't want to until I've ironed out that case-insensitivity issue, and possibly defined CSS3-abstracting-functions as part of the default library (though I'm still pretty sure cl-css will ever attempt to validate your input).

I estimate having spent a good twenty minutes or so puzzling over why (ships a-player) was suddenly returning a very odd list of elements once a game had started. The reason is that mapcan uses nconc to put its results together, which means that the arguments are going to be modified destructively. So I guess this is another place where State Is Hard. I ended up defining the functional, unary mapcan seen above, which suffices for my purposes.

And on we go to the diffs. I don't actually expect anyone to read past this, by the way, but thinking about code I wrote well enough to explain it prosaically has proven to be a very effective technique[6]. Oh, also, before I forget, we've gone from 220 lines to about 550 (and that new count doesn't include the license info, generated css/js, or any of the images being tracked as binary files).

I didn't do a complete overhaul of the ship placement routines, but I got pretty close. I abstracted point creation a bit and replaced loop with a nested mapcan/mapcar on the results of the range utility function I mentioned last time. That's back by the way. mapcan doesn't cause any trouble here because range creates an entirely new list, which means that there are no outside references that might get tripped up by nconc. Also, the reason for nesting iterations in the assign-ship-spaces function is that I'm now generating ships wider than one square[7].

The rest of the additions here just deal with outputting the appropriate html and styles for ships and boards; that's the price you pay for choosing a more pleasing visual representation than just ascii characters in a table. There's enough ship-specific code at this point for me to consider pulling it out into its own file, but not really enough to actually force me to do it yet. Also, I get the feeling I should be referring to these as "barges" instead. In fact, that's two mental notes for after the write-up.

We'll dispense with the diff here because css.lisp is an entirely new file. This is basically just how I like to write CSS in Lisp projects. The library I'm using is cl-css; a lightweight, non-validating CSS generator of my own devising. Use it if you like, but remember that it does downcase everything[8] you pass it at the moment, so you may have a rough time using it with certain JS libraries that insist on using CamelCase or snakeCase rather than lisp-case for their DOM classes. As mentioned, the main reason I resorted to this can be seen in the css-scale, css-rotate and css-transform-origin macros, which just paper over the fact that browser developers don't quite seem to be cooperating yet.

Again, most of the additions here have to do with generating html/json representations of classes we've already gone over. The semi-interesting bits are the shiphp calculations (which would be good candidates for moving out into that ship-specific file I mentioned thinking about). instance-to-id is something we'll go over later because I'm unsure of its implementation. The point is to take an instance and return a string suitable for use as a CSS id while being ambiguous enough that a player can't get too much information from it[9].

fire has changed pretty substantially. First off, it's now making calls to push-record (which we'll go over in the next file), and it's also applying damage to the stricken ship in addition to placing the shot marker on the board as before. The new stuff is all happening in that unless block.

This is another new file. The interactions involving game history were getting complex enough that I wanted them on their own, but I ended up refactoring a lot of it out, leaving this rather sparse file. This stuff deals with the information we'll be passing back to the clients in-flight. To that end, I've defined history-event as its own class, and made sure that it handles echos properly. push-record is also a shortcut method you've already seen used. All of this could fit comfortable in the game file, and it probably will be moved before the next check-in.

Another entirely new file. This is what parenscript tends to look like if you use it with jQuery. Most of this deals with the server-sent-event stuff we need to send incremental, on-the-fly updates to the client. Particularly note the define-event-listeners and define-event-source. Neither are primitives, and we'll discuss implications in a moment. The only other thing we've got here is send-shot, which is actually an ajax call to /turn. It doesn't bother doing anything if it's not your turn (defined implicitly by the asserts we saw last time), but places the shot flag and updates the turn marker when needed.

This one's... a bit complicated. I went a bit heavy on the comments for exactly that reason. compile-js is just responsible for generating js files from parenscript code. The next few macros are jQuery-oriented shortcuts that I've already discussed here[10]. The define pair let me specify SSE feed sources and event handlers much more simply than I could in vanilla JS. $-space-at just lets me shortcut the selection of a map square.

Back into merely modified files. As you can see, ship definitions got shorter (we'll see the macro behind that in the util file), history-events became their own explicitly defined objects, and ship got a few new slots to make wider pieces possible. Incidentally, if you look at those new ship slots, you'll see one of the CLOS speedbumps that Yegge whinged about quite a while ago. length is a polymorphic function and not a method. Meaning that if you have a non-sequence foo that would benefit from having a length method, you either need to name it foo-length or len or some other annoyingly minute variation of the word. I went with len and have no particular regrets there (other than not actually being able to use length as a method).

Not much new to see here. I've imported cl-css, as well as choice functions from cl-ppcre and :json. Still importing :ironclad chunklets, and still haven't put them to use. I did pull out the server port number and board square size into their own variables though. Thanks to my use of :parenscript and :cl-css, that single parameter should be all you need to change in order to mess with square size. The server startup, as well as a couple of related statements were moved out to a separate start file.

And here it is. The defparameter line starts a new hunchentoot instance listening on *server-port*, and web-folders does exactly what you'd expect (we'll discuss it in very slightly more detail in the util file).

This is possibly the most changed file at the moment. Firstly, we've added an ajax handler for player actions. Second, update-map shows you exactly what you need to do on the server to set up a Server-Sent-Event source that the client will actually support. emit-record was shown earlier, but I didn't really explain it. It still takes a player as an argument, because the record digest used to be subjective[11]. In the non-ajax handlers, note that the various asserts have all been replaced with equivalent redirect-unless calls. That's a new utility function that does exactly what you'd think. It may seem to be more complex than necessary at this point, but I will eventually shunt the player off into different pages depending on where they came from (you know, improving signage and all that).

Finally, util has grown quite a bit. Range has returned after being removed temporarily. We've already gone through mapcan-f and redirect-unless. take is a naive definition of the equivalent function from Scheme, define-ship is a minimal shortcut for cutting out ship definition boilerplate now that ships are two-dimensional, and the html-to- twins are the thinnest possible useful wrappers around the equivalent with-html-output from :cl-who. web-folders is a shortcut that makes it easier to have hunchentoot serve folders. Its operation should be obvious if you know how this normally works; the only note I'll make is that if you were to actually run StrifeBarge in production for some bizarre reason, you'd actually want to set up nginx or some other lightweight HTTP server out front to serve these static directories instead of having your Lisp process handle everything.

The only really interesting piece here is instance-to-id, which takes a CLOS instance and returns a sufficiently-ambiguous string mostly useful as a DOM id. I initially had this implemented as

(regex-replace-all "[#<>{} ]" (format nil "~a" instance))

instead, but found that a player can get too much information from that (though it did perform marginally better in profiling reports). Running it on a carrier would produce an id like CARRIER100AABD943, from which it's trivial to find out what kind of ship just got hit. The actual definition above instead returns 100AABD943 in the same situation. This is specific enough to unambiguously identify the DOM element for any JS function we need invoked, but it's general enough that a player probably can't get any useful, new information just by pulling down update-map and reading the output manually. I may change my mind on this later, by the by, since I'm already taking quite a few liberties with the rules, but it stays for now.

So that's almost that for the game itself. I still need to make ships send out a "You sunk my..." message when they get taken down, and there's a few usability-related things I'd like to add to the client side (ok, yes, and there needs to be support for multiple games at once, as well as actually winning), but most of the remaining development actually needs to happen outside the game itself. I hinted at it earlier, but just to make it explicit, I still need to implement

A chat-room/lobby for people to start games up from (in-game chat might be nice too)

A leaderboard system to show off high scores (possibly with replays too)

A formalized way of automating games

Sounds like this'll actually keep me busier than I expected.

Footnotes

1 - [back] - That'd be a meatspace, belligerent network of trust. The idea being that if you can get n people who certainly aren't cooperating to vouch for Alice face-to-face, your odds are better than if you merely had n of Alice's random online social contacts do the same.

2 - [back] - And don't really help you much in the general case, unless you're assuming that you'll always be able to trace a line from your direct contacts to the other party, or you're assuming belligerence.

3 - [back] - I have to be honest though; preventing people from cheating in a hobby-horse project of mine isn't why I was researching this. I'm doing some development at work that will need me to build at least reasonably secure components, so I'm beefing up on the basics of crypto and computer security.

5 - [back] - I still need to implement multiple games per server, some sort of lobby/leaderboard system, and some way of actually winning before I think about implying that I've solved most game-related problems. Even for something as simple as this.

7 - [back] - There really isn't a technical reason for that; I took a look at the spaceship sprite sets at OpenGameArt and thought it would be a shame not to be able to use some of the oddly-shaped ones.

8 - [back] - No longer actually true; the version up at github currently lets you preserver case on selectors by passing them in as strings, and it incorporates the CSS3 transformation abstractions from this project. I'll probably be adding animation and transition to the pile shortly.

9 - [back] - Specifically, to stick to the classic game as closely as possible, it shouldn't be possible for a player to identify what kind of ship they hit until they either sink it, or deduce it from the number of squares it occupies.

10 - [back] - Though I did start calling the anonymous function shortcut fn rather than \ for ease of exporting.

11 - [back] - It's not at the moment, but this is something I won't be changing in the short term, since it will become true again once I implement a chat client.

Ruby and Erlang each come with their own modes, and recent Emacs versions ship with a built-in Python mode and shell. Smalltalk uses its own environment (though GNU Smalltalk does have its own mode), and I'd really rather not talk about PHP. If you're writing in it, chances are you're using Eclipse or an IDE anyway.