Archive for November, 2007

Version Numbers and the Updater API

AIR applications can update themselves via the Updater API. It’s straightforward to use: you just provide an AIR file containing the new version of your application and the version number you’re upgrading to. Downloading the AIR file and displaying any appropriate UI is left to the application.

When we designed this API, I thought we’d get a lot of questions about why the version number had to be provided. After all, the version was itself embedded in the AIR file. Surely we could just grab the version number from there, right?

Disappointingly, few have asked.

Disappointing because that argument exists to help you protect your application from downgrade attacks. Downgrade attacks work like this: some earlier version of your application has a vulnerability, but has since been patched. The attacker tricks your update mechanism into using the Update API to install that old version, thus re-introducing the vulnerability. Then they attack the vulnerability.

To protect against this attack, the application must validate before it begins the update that the version it’s updating to is, in fact, a newer version. During the update process itself, AIR will validate that the version number you’ve validated and passed to the update call does in fact match the version in the AIR file. If they don’t match exactly, the update will fail.

Why doesn’t AIR check that the version is newer? Because we didn’t want to impose a version numbering scheme on anyone.

Parameterized Application Install

I’ve been asked a couple of times if it’s possible to parameterize the application identifier, etc. for an AIR-based application during the installation process. The scenario is usually something like this: A developer has created an application that they publish through different vendors. Each vendor stamps some unique branding, etc. on the application but the underlying code is the same. The developer doesn’t want to produce a different AIR file for each vendor. Instead, they’d like to parameterize things like the application identifier, application name, logo, etc. during the installation process.

The problem with this is that it’s directly at odds with secure delivery of the applications. Secure delivery is designed to establish the trusted identity of the publisher of each application via digital signatures. If any part of the application can be changed at install time then it’s (by definition) not covered by that signature, and we can no longer establish the authenticity of the application. Maybe you got assigned a correct parameter, or maybe you got a malicious one that will open you to an attack. How do you know?

In these scenarios it’s the vendor, not the developer, who is actually publishing the application. They’re the ones who should be signing the application; this will insure that it’s their name that appears during application install. And of course they should only be signing it after any re-branding, etc. has been applied.

Too much work for the developer? It shouldn’t be. Packaging can be fully automated via the adt tool provided for free in the SDK. Create a script that uses it to produce all of your unsigned, parameterized, intermediate AIR (.airi) files. Hand this file off to your vendor and have them sign it, again using adt, to produce the final AIR file for distribution. Voila.

Tools: Araxis Merge

Computers are tools. Fancy, general-purpose, expensive, complicated tools, but tools nonetheless.

Most applications are tools, too. Programmers tend to think of applications used to write or debug other applications as tools and everything else as, well, something else–but this is just perspective. To an end user, an application is a tool that helps them write a book, perform research, calculate (the original purpose of the computer!), and so on.

Ironically, most programmers seem rather reluctant to invest the time in assembling a good set of tools. Can you imagine a carpenter who still hadn’t taken the time to learn how a chop saw worked?

In the spirit of someone writing tools in order to help others write tools, then, I thought I’d post occasionally about some of the tools that I use.

First up: Araxis Merge. An awesome GUI diff and merge tool for both files and directories. Can be integrated with various source control systems; I use it to perform all of my non-trivial integrates in Perforce. Its three-way merge algorithm is great, and it will show intra-line diffs, which are particularly handy when merging Visual Studio and XCode project files.

Bonus: Available on both Mac and Windows.

Async APIs and State Machines

Continuing my posts on dealing with async APIs, I’ve been hinting that there options beyond rafts of callbacks functions, be they anonymous or not. So let’s get on to it–I’ve been promising this post for a while.

Briefly, state machines model the behavior of a system as a set of discrete states, events which are accepted in each state, and actions–including changing states–which occur when inputs are received. State machines turn out to be particularly well-suited to asynchronous programming because they don’t force you to organize your logic in sequential steps.

For example, imagine programming a controller for a sensored traffic light. The events it deals with–the arrival of a car, or a push on the crosswalk button–are inherently asynchronous. It’s difficult to write code that, given the arrival of an event, then examines which lights are red, which are green, determines which to change, and so on. It’s much easier to organize your code around the state of the light–which are green, which are red–and then, for every state, specify how each event is handled.

Now there’s more than one way to implement state machines in code, but I’m going to use the implementation proposed by [Samek]. Let’s start translating this to code:


class TrafficLight {
var m_state:Function = stateEastWestGreen;

function stateEastWestGreen( event:Event ):void ...
function stateNorthSouthGreen( event:Event ):void ...

Note that I’m modeling each state as a function and the events that can occur are modeled as, well, events. (You see how well this fits together!) Now a real traffic light probably has more states then this, but it’ll do as an example.

Each state has to deal with all possible events that it might receive and want to react to. For example, suppose I have traffic sensors that dispatch CarEvent each time a sensor is triggered:


function stateEastWestGreen( event:Event ):void {
if( event.type == CarEvent.CAR ) {
var carEvent:CarEvent = event as CarEvent;
if( carEvent.direction == CarEvent.NORTH_SOUTH ) {
// switch to stateNorthSouthGreen
} else {
// We're already green in this direction; nothing to do
}
} else {
// Some other type of event...
}
}

Now I’m not a traffic engineer, but it’s easy for me to read this code and realize that yes, when the light is green in one direction, if a car arrives in the other direction I’m going to want to eventually change the lights for it. For now I’m skipping over the details of turning the lights yellow, etc. but we’ll get back to that.

One more snippet for today, and that’s to get the events from our sensors into our state function. Still inside the TrafficLight class:


function dispatch( event:Event ):void {
m_state( event );
}

function TrafficLight() {
northSouthSensor.addEventListener( CarEvent.CAR, dispatch );
eastWestSensor.addEventListener( CarEvent.CAR, dispatch );
}

Note that I register the same callback function for all events I listen for, and that it in turn simply forwards them to the function that represents the current state. Et voila.

References

[Samek] Practical Statecharts in C/C++: Quantum Programming for Embedded Systems, Miro Samek, CMP Books, 2002.