Wandering Thoughts archives

2010-01-28

Always sign exactly what you are authenticating

I can't claim to know very much about cryptography programming, but I like to think that I have picked up on a few mistakes to avoid. Here's one of them: you want to sign exactly what you are authenticating, not some mangled version of it.

(Note that 'canonicalizing' things is a form of mangling them.)

Suppose, as a not entirely hypothetical example, that you are signing some sort of web request with a bunch of (URL) parameters. In order to deal with annoying software, you define a canonical form for these URL parameters, which is to sort them into alphabetical order and concatenate them all together; you sign this mangled, canonical result.

Great, except that that this canonical form has just allowed an attacker to turn 'login=fred&next=10' into 'login=fredne&xt=10' (and worse is possible if you do not sort the parameters into order but use them in the request's order). As Colin Percival puts it, good crypto signatures are designed so it is very difficult to produce collisions and when you mangle what you sign, part of what you do is that you create opportunities for attackers to produce deliberate collisions. This rarely ends well.

Another part of what you do when you mangle is that you are no longer necessarily verifying what you think you are verifying. Instead of verifying what you are actually about to act on and use, you are verifying something else, some mangled transformation of it. This is almost invariably a mistake, one that attackers will be happy to take advantage of to slip dangerous things into the gap between what you verify and what you act on.

(It is possible that your mangling will be un-exploitable. But the historical odds are against you; over and over, people who have done this sort of mangling and imprecise verification have turned out to have created exploitable vulnerabilities. And if you are writing crypto code, you should not be betting on things going your way.)

Sidebar: how I think you have to do canonicalization

Disclaimer: you should not necessarily trust what I write about how to do crypto (as opposed to how not to do crypto), because I am not an experienced crypto person.

I believe that the corollary to this is that if you absolutely have to do mangling and canonicalization, you must do it as part of generating the plaintext; you take raw input, transform it into the canonical form, sign the canonical form, and output the canonical form and the signature. On verification, you canonicalize, verify the canonicalization, and then use the canonicalization for further processing, not the raw input.

If you cannot use your canonical form as input to the rest of your processing or as your public plaintext, you need a new canonical form. Try again.

AlwaysSignExactVersion written at 02:00:43; Add Comment

2010-01-04

Turning synchronous channels asynchronous

(This is likely obvious, but since I keep working it out again in my head I'm going to write it down once and for all.)

Suppose that you have a CSP-like environment, with lightweight processes and synchronous communication channels with no buffering. Synchronous channels are simple but very inconvenient for many real-world things, where you need to have asynchronous channels. Fortunately, you can turn synchronous channels into acceptable asynchronous ones as follows.

To send an asynchronous message, you spawn a new process and write the message (along with the destination channel) to it through a new, dedicated channel. This will not block (barring scheduling issues), because you know the first thing that the new process does is read your message. The sub-process then writes the message to the real destination; while it may delay a very long time as it waits for the process on the other end of the channel to pick up your message to it, that's no longer a problem for your main process.

(This assumes that you can't pass any data to the new process except via a channel. If you can, you may be able to skip writing anything to it.)

I don't think you can do a pure asynchronous receive, but if the system doesn't already provide a 'select' operation you can implement it in a similar way. You have N persistent processes, each of which reads from a specific outside channel and then writes the received message to a common channel to your main process. Your main process then just reads the common channel and gets all events as they come in (although in some random order).

If the language is limited and type-safe enough that you can't write all of the messages to a single common channel, you need a slightly more elaborate scheme. Have a number of type-specific channels, and have the sub-processes first write a pointer message to the common channel and then retransmit the actual message to the type-specific channel. Your main process picks up the pointer from the common channel and then reads from the appropriate type-specific one; you may get a bit of blocking due to scheduling, but you should never stall waiting for a message.

(You might get into this situation if you are, say, a window system receiving keyboard events, mouse events, and messages from individual windows. The respective types may be so different that you can't smash them all into one structure and pass it over one channel.)

I will note in passing that while Go has channels, it allows them to be asynchronous (and has a powerful select operation).

MakingChannelsAsynchronous written at 00:05:04; Add Comment

2010-01-03

Go interfaces are not my sort of interfaces

I've written about interfaces before, or at least what I mean by the term (and how it relates to inheritance). To summarize, 'interfaces' are a way of formally expressing 'is-a' relationships; when you say that a class or a type implements the String interface, you mean that it is a string.

One of the things that jogged my mind about the whole issue was Go, which has things that it calls 'interfaces' (okay, technically they're called interface types). After thinking about it, it's become clear to me that what Go calls interfaces are not what I'm calling interfaces here; instead, they are a way of doing vaguely checked duck typing.

The problem with Go interfaces (from the perspective of being my sort of interfaces) is that no one actively asserts that a class implements an interface. Instead, an interface to class relationship is passively discovered as valid by the compiler. Because this relationship is not actively asserted, it is subject to inadvertent duck typing naming collisions, where a class coincidentally has a method or three with acceptable names and signatures.

Requiring active assertion, instead of mere passive discovery, means that at least someone told you (the compiler, the programmer, etc) that type <X> was a compiled regular expression or a string or whatever, and any inaccuracies in this statement are clearly their fault or at least mean that someone is wrong and you can fix the error. (And no programming language can really save you from active lies.)

(Note that this is my confusion, not particularly Go's; Go has merely chosen a somewhat overloaded and imprecise term because it was a good one for their purposes. Various Go people have written quite clear things about what Go interfaces do and are there for.)

GoInterfaces written at 02:20:45; Add Comment


Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.