Storing and testing credentials: Cocoa Touch Edition

This article introduces the concept of key stretching, using code examples to explain the ideas. For code you can use in an app that more closely resembles current practice, see Password checking with CommonCrypto.

There’s been quite the media circus regarding the possibility that Sony was storing authentication credentials for its PlayStation Network credentials in plain text. I was even quoted in a UK national daily paper regarding the subject. But none of this helps you: how should you deal with user passwords?

The best solution is also the easiest: if you can avoid it, don’t store the passwords yourself. On the Mac, you can use the OpenDirectory framework to authenticate both local users and users with accounts on the network (where the Mac is configured to talk to a networked directory service). This is fully covered in Chapter 2 of Professional Cocoa Application Security.

On the iPhone, you’re not so lucky. And maybe on the Mac there’s a reason you can’t use the local account: your app needs to manage its own password. The important point is that you never need to see that password—you need to know that the same password was presented in order to know (or at least have a good idea) that the same user is at the touchscreen, but that’s not the same as seeing the password itself.

That means that we don’t even need to use encryption where we can protect the password and recover it when we must check the password. Instead we can use a cryptographic one-way hash function to store data derived from the password: we can never get the password back, but we can always generate the same hash value when we see the same password.

Shut up Graham. Show me the code.

Here it is. This code is provided under the terms of the WTFPL, and comes without any warranty to the extent permitted by applicable law.

The first thing you’ll need to do is generate a salt. This is a random string of bytes that is combined with the password to hash: the point here is that if two users on the same system have the same password, the fact that the salt is different means that they still have different hashes. So you can’t do any statistical analysis on the hashes to work out what some of the passwords are. Otherwise, you could take your knowledge that, say, 10% of people use “password” as their password, and look for the hash that appears 10% of the time.

It also protects the password against a rainbow tables attack by removing the one-one mapping between a password and its hash value. This mitigation is actually more important in the real world than the one above, which is easier to explain :-).

This function uses Randomization Services, so remember to link Security.framework in your app’s link libraries build phase.

NSString *FZARandomSalt(void) {
    uint8_t bytes[16] = {0};
    int status = SecRandomCopyBytes(kSecRandomDefault, 16, bytes);
    if (status == -1) {
        NSLog(@"Error using randomization services: %s", strerror(errno));
        return nil;
    }
    NSString *salt = [NSString stringWithFormat: @"%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x%2x",
                      bytes[0],  bytes[1],  bytes[2],  bytes[3],
                      bytes[4],  bytes[5],  bytes[6],  bytes[7],
                      bytes[8],  bytes[9],  bytes[10], bytes[11],
                      bytes[12], bytes[13], bytes[14], bytes[15]];
    return salt;
}

Now you pass this string, and the password, to the next function, which actually calculates the hash. In fact, it runs through the hashing function 5,000 times. That slows things down a little—on an A4-equipped iPad it takes nearly 0.088s to compute the hash—but it also slows down brute-force attacks.

NSData *FZAHashPassword(NSString *password, NSString *salt) {
    NSCParameterAssert([salt length] >= 32);
    uint8_t hashBuffer[64] = {0};
    NSString *saltedPassword = [[salt substringToIndex: 32] stringByAppendingString: password];
    const char *passwordBytes = [saltedPassword cStringUsingEncoding: NSUTF8StringEncoding];
    NSUInteger length = [saltedPassword lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
    CC_SHA512(passwordBytes, length, hashBuffer);
    for (NSInteger i = 0; i < 4999; i++) {
        CC_SHA512(hashBuffer, 64, hashBuffer);
    }
    return [NSData dataWithBytes: hashBuffer length: 64];
}

Where do I go now?

You now have two pieces of information: a random salt, like edbfe42b3da2995a159c16c0a7184211, and a hash of the password, like 855fec563d91576db0e66d8745a3a9cb71dbe40d7cb2615a82b1c87958dd2e8e56db02860739422b976f182a7055dd223a3037dd3dcc5e1ca28aaaf0bade8a08. Store both of these on the machine where the password will be tested. In principle there isn’t too much worry about this data being leaked, because it’s super-hard to get the password out of it, but it’s still best practice to restrict access as much as you can so that attackers have to brute-force passwords on your terms.

When you come to verify the user’s password, pass the string presented by the user and the stored salt to FZAHashPassword(). You should get the same hash out that you previously calculated, if the same password was presented.

Anything else?

Yes. The weakest part of this solution is no longer the password storage: it’s the password itself. The salt+hash shown above is actually for the password “password” (try it yourself), and no amount of software is going to change the fact that that’s a questionable choice of password…well, software that finally does away with password authentication will, but that’s a different argument.

If you want to limit a user’s ability to choose a simple password, you have to do this at password registration and change time. Just look at the (plain-text) password the user has given you and decide whether you want to allow its use.

On cryptographic file storage

In Chapter 3 of Professional Cocoa Application Security, I talk about using CommonCrypto to encrypt files stored on either Mac or iOS file systems. In Chapter 4, I talk about using CommonCrypto to generate Hashed Message Authentication Codes (HMACs) to verify the integrity of messages received by an app. Unfortunately, I didn’t connect the two in the book.

I actually did discuss the reasons for providing an HMAC in a recent talk on cryptographic storage for iOS. But I went to Justin Clark’s talk at Security B-Sides London, where he demonstrated the attacks that are possible against cryptosystems that don’t verify the integrity of their content. This talk was very informative: if the presentation ever makes its way online, I fully recommend that you check it out.

The talk reminded me that there are important attacks I didn’t discuss in the book. So I need to bring these concepts together for the readers of PCAS. The tl;dr version is: if you encrypt content, you must also verify the integrity of the ciphertext. The long version follows.

I recommended in the book using CBC, or Cipher Block Chaining, mode for AES encryption. In this mode, the first block of plaintext is XORed with an Initialization Vector (IV), and this is then encrypted. The resultant ciphertext is then XORed with the second block of plaintext, and this is encrypted. And so on. This offers some protection against certain cryptanalysis attacks that Electronic Code Book (EBC) mode does not: for example, blocks of ciphertext cannot be arbitrarily swapped without the decrypted plaintext becoming nonsensical. A change in the ciphertext not only affects the decryption of the changed block, but also subsequent blocks.

Well, it turns out that this propagating-change property of CBC mode can be used by attackers—if they have information about errors encountered during decryption—to decrypt and encrypt data without needing to discover the key. Ouch. See the B-Sides talk described above (or google for Padding Oracle Attack) for the full details.

Of course, not providing the padding oracle is an important solution to this attack: but refusing to handle content that isn’t verifiably valid avoids any attack involving modified ciphertext: including encrypted content that lies about its length, if that’s an important consideration for your file format. Therefore, if you’re encrypting data in your app, you should be generating an HMAC and verifying that before attempting a decryption operation. Tampered data is not data you want to deal with.

Categories will bite you

What I wanted to do was this:

+ (void)load {
    Method foo = class_getInstanceMethod(self, @selector(foo));
    Method newFoo = class_getInstanceMethod(self, @selector(FZA_swizzleFoo));
    method_exchangeImplementations(foo, newFoo);
}

However, my tests wouldn’t work when I did that. It turns out that for some reason +load was running twice, so the methods got swizzled twice meaning that each implementation ends up married to its original selector. So I thought I’d do this:

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method foo = class_getInstanceMethod(self, @selector(foo));
        Method newFoo = class_getInstanceMethod(self, @selector(FZA_swizzleFoo));
        method_exchangeImplementations(foo, newFoo);
    });
}

…and, *drum roll*, that doesn’t work either. What gives?

Well, it turns out that the reason that the category was being loaded twice is because it was included in two separate binary images: the library I was testing and the unit test bundle. Because of this, the “static” dispatch_once_t instance was actually a separate instance for each image, so dispatch_once() couldn’t tell it had already run the block.

In my case there’s a solution; I only need the category to be loaded once so removed it from one of the targets. But the general message is that category loading is very non-deterministic, and hard to rely on. Imagine if you rely on an open-source category, then one of your plugin developers relies on the same category. Or one of the frameworks your app links against. There are specific cases where the implementation of categories causes no effects, or effects that are understood and may be worked around, but those are really the exceptions.

On internal quality

I was asked by attendees at my VTM talk on test-driven development a small collection of questions on a similar theme, which I’ll summarise here.

  • How do I do TDD when my boss doesn’t want me to?
  • What do I do when my boss wants me to ship untested prototype code?
  • Can you give me rhetoric to convince my boss that I should be doing TDD?

I believe that I can adequately present these questions as facets of the following root issue, and then blabber away endlessly about that one instead.

I have externalised the obligation I have to myself to write software that makes me proud by delegating it up to my boss. Can you help me to sleep at night?

The fundamental problem here is one of cognitive dissonance. Belief: I, as a software engineer, believe that process/technique/tool is good for writing software. Introspection: I observe that I am not using said process/technique/tool. Justifications: it must be that my boss doesn’t hold my output to the same quality standard that I do. It must be that my deadlines and ever-shifting requirements don’t give me time to do things properly. It must be that this company doesn’t want great software engineers.

No. I have learned the hard way that such justifications are false. The only person responsible for how you write code is you. If you work on a team, or are handing your source code over to some other stakeholder (which means you’re working on a team), then you may have style guides that limit what source code you write; but ultimately how you get there is solely down to you.

You may be thinking “but I asked my manager for time to implement TDD/CI/buzzword-compliance on my current project and she said no. Also, by the way, you’re an arsehole for being so rude and presuming to know what I think.” Let me address the latter point first: suck it. Now onto the first point.

Your manager was correct to turn down your request for time on the project. Your customer doesn’t want unit tests, or comment documentation, or build server output. Your customer wants working features. The project exists to satisfy the user’s needs. Therefore the time allocated on the project should be dedicated to making working features.

Now it turns out that doing TDD is one way to write code that leads to working features, so what you should have done was to agree to the version of the product plan where you make working features, and then done TDD anyway. Where this all started going wrong was not when your boss turned down your request, but when you asked your boss in the first place. It turns out that you both have the same goal—making a good product—but that you have different views on the process and different motivators. By asking your boss how to write code you gave her permission to micro-manage you, then got frustrated when she did, and decided that the problem was all her fault.

I’ve seen this failure mode quite a few times now. As one example, I worked in a company where there was an ingrained antagonism between the product managers (who clearly just don’t get that software is a craft and a labour of love) and the programmers (who clearly just don’t get that we work in a competitive marketplace and would rewrite the product from scratch every day if they could).

As is common (and often correct) in our industry, the product managers owned the product requirements. As should be welcomed when we care about the products we make, the programmers were invited to criticise the requirements for the projects they were working on. Due to this antagonistic culture, the programmers would typically stuff “engineering requirements” onto the project, which were things like rewriting components from scratch, epic refactorings, or setting up new developer workflows.

Requirements were, as far as I could tell, prioritised based on how much revenue they would attract (i.e. new customers), how much revenue they would protect (retained customers) and how much they would cost. As you can probably already guess, so-called engineering requirements don’t protect any revenue, they don’t generate any revenue, and they do take time and money to implement, so they would inevitably get dropped or indefinitely postponed.

The moral of that little story is this: it is folly to try and express development processes and methodologies as business requirements, because they fail at being business requirements. It’s the equivalent of a taxi driver asking the customer to pay a $100,000 fare for one journey because he wants to switch to automatic transmission for the next journey and that means buying a new vehicle. The customer cares about being driven somewhere, and doesn’t care about how the driver operates the vehicle as long as the journey is bug-free. He certainly doesn’t intend to pay for the driver to select a particular mode of operation: if the driver wants to use automatic transmission, the driver should just get on and do that.

So what I’m saying is this: it’s not up to your boss, your customer or your project manager to choose how you write software. It’s up to you to choose how you write software, and as long as the approach you take leads to working software that delights your customers, the rest of the stakeholders won’t mind what you’re doing “under the hood”. My good friend and long-lost son @bmf once said “don’t let them see you making it.” I would go further: don’t let them know it was made. Let them use exciting, compelling software: the magic is your business. If you want to make the magic in one particular way, that would let you take additional pride in your creation, that’s entirely your call.

A first look at appCode, and the future of Cocoa IDEs?

It’s been almost a full rotation of this great rock about its axis since JetBrains announced the start of its appCode Early Access Program.

appCode is an Integrated Development Environment, just the same as Xcode. Just like Xcode, appCode works with Objective-C projects on Mac and iOS: in fact, under the hood it’s using xcodebuild, llvm and all of the same tools that Xcode uses.

Unlike Xcode, appCode was written by JetBrains, the company who make the IntelliJ IDEA environment for Java. Now I’ve never used IntelliJ, but I have used Eclipse and NetBeans (and even Xcode!) for Java development, and that’s kindof the point. There’s a lot of competition in the Java world for IDEs, so they all have a boatload of features timesaving devices that are missing from Xcode.

Now, before we get too far into details, let’s get this out of the way. appCode—though not itself a cross-platform app—is a Java app based on the same framework as IntelliJ. It therefore doesn’t look like a Cocoa app, it looks like this:

Screen shot 2011 04 06 at 16 53 22

That’s enough to put some of you off, I’m sure. As people who know that user experience is the first most important factor of any app, it’s easy to be distracted by an obviously alien user interface.

Maybe it’s partly because I’ve used so many cross-platform IDEs, but I’ve become inured to the look of the products. I don’t mind (or at least, can deal with) a nonstandard interface if it provides a better experience. It’s quite pleasing that in some cases, appCode does indeed come through.

What does it do that Xcode doesn’t?

One feature that every other IDE I’ve used has which is missing from Xcode is the ability to automatically stub out methods defined in the class interface (or the superclass interface, or a category, or a protocol…) in the implementation. This is a great feature for saving a boatload of typing (or even flipping between multiple views with copy and paste). Design your class’s header, then use the Override Methods, Implement Methods and Generate… items in appCode’s Code menu to automatically fill in the boilerplate bits in the implementation.

Another improvement appCode brings over Xcode is in its refactoring support. Xcode has long had a refactoring capability that only has a little more syntax awareness than sed, and I’ve previously talked about tops as a command-line equivalent. But what about when you want to change a method signature?

Xcode’s refactoring tools can’t support that, although I want to do it fairly frequently: in fact the last time was this morning. I wanted to change the return type and add a new parameter to an existing method, effectively going from -(void)frobulate: to -(BOOL)frobulate:error:. Well, it turns out that in appCode, you can do that:

Screen shot 2011 04 06 at 17 19 32

So I should be using appCode, then?

Well, that’s not my decision to make, but I definitely wouldn’t recommend using it for production yet. It’s got some bugs (yes, I have filed bug reports) particularly with managing Xcode projects and integrating with the underlying SDKs and tools. You probably don’t want to touch production code with appCode yet.

Some particular issues I’ve found are with the default keyboard shortcuts conflicting with Mac OS X’s default keyboard shortcuts, it doesn’t offer a way to run the static analyzer, and I’ve had a problem where it removed a file from the definition of a project’s target. I was able to address both of the last two problems by going back into Xcode, but of course it would be preferable if I didn’t need to. Another big deal for me is that there isn’t (yet?) any integration with the API documentation.

That said, it is just an early preview, so definitely download it and have a motor around. You’ll probably find some things you do when you’re coding that are easier in appCode, and other things that are easier in Xcode.

What’s all this about the future of Cocoa IDEs?

I sincerely hope that JetBrains take this product to release and that people start adopting it. Based on the current preview, I would expect developers coming to iOS from Java development to be the majority of appCode users, because they’ll be the people most comfortable with its way of working.

My biggest hope is that developers in Apple (both inside the Xcode team and beyond) pick up appCode and take a look at it. What we Cocoa developers need is some competition among IDE producers—particularly now that we need to pay for Xcode— to drive development of the tools we use for our day-to-day work. Xcode is to 2010s Objective-C IDEs as Internet Explorer was to 1990s browsers: it basically works, and you don’t get a choice of what you use so be thankful for whatever features you get from On High.

It’d be great for someone to come along with the Firefox of Objective-C IDEs: a product that reminds users and vendors alike that there’s more than one way to do an IDE and that people are open to trying the other ways.

On my own competency

There was a question on programmers.stackexchange.com about whether to put your Stack Overflow reputation in your CV. I don’t, and answered as much: there’s no point in writing for its own sake, unless you want to be a writer. If you want to be a programmer, then you should be a good programmer and any writing you do should be evidence of that.

There’s a great—if somewhat old—resource for evaluating your own capability as a software engineer called the programmer competency matrix. Writing the answer I gave to the above question renewed the memory of the matrix in my mind, so I decided to evaluate myself against it and use that to help build a research and personal development plan.

I’m sharing my own results in this post. My hope is that this will trigger some introspection of your own, and demonstrate just how easy it is to devote a little time to personal development (this took me about an hour to do, including the writing). I’m also being selfish: when I write for publication I’m a lot more organised than if I just push notes into iThoughtsHD or OmniOutliner.

It doesn’t matter whether you agree or disagree with anything I write. If we agree on any row of the matrix, then we’re both pulling in the same direction and pushing the bar a little higher in that area of software engineering. If we disagree, then we’re still both raising the bar, because we both have our special skills that (hopefully) contribute to making the whole industry that bit more skilful and knowledgeable. Of course, if you think that I’m completely brain-dead for choosing not to develop my skills in a particular area, feel free to flame me in the comments.

Notice that it’s hard to be objective about self-evaluation. I hope that my answers below are somewhat accurate, but it’s easy to have an over-inflated opinion of your own competence. In fact, when I was a junior software engineer I felt like I knew more than I do now :). It would definitely be trite to give a nonsense buzzphrase like “we should always be continually improving at everything we’re doing”, because there aren’t enough hours in the day for that. There should be things that we definitely need to work on, things that we “just do” but reflect on how they’re going, and things that we just automatically get on with. Of course, when I think that something should be automatic but I make a mistake, that’s a time to re-evaluate my perception of my skills.

Computer Science

  • data structures: Level 1. I’ve done some study into some of the level 2 features (and even know what some of the words mean in Level 3), but at heart I feel like that’s stuff for a framework programmer to deal with and I’m an applications programmer. I’m happy to believe that the framework engineers know how to implement List<Integer> or NSArray properly and leave them to it.
    Related tale: I once had an interview where the interviewer drew a list on the whiteboard, and asked how I’d sort it. I wrote a "[" at the beginning, and " sort];" at the end. I didn’t get the job.
  • algorithms: Level 2. Again this is largely the domain of the framework engineer, but there are times when it’s become important to my own work such as when I’ve had to do performance engineering (and of course cryptographic algorithms factor heavily into my work). I tend to address algorithmic problems on a case-by-case basis, and don’t feel the need to explicitly schedule some research time.
  • systems programming: Level 2. I do believe that I need to develop here, particularly in the areas of JIT compiling and interpretation. If BDD and DDD (and, to some extent, OOP) have taught us anything, it’s that there are great efficiency savings to be made by expressing a software problem in a way that’s understandable by, or at least explainable to, the user. And that means Domain Specific Languages. I’ve actually seen that work very well, when I was working for Diamond Light Source. Scientists could write working prototypes in a Jython-based DSL that would then either be cleaned up by engineers or reimplemented in the core engine.

    Of course, BDD and DDD are both also examples of RDD (Résumé-Driven Development), but that doesn’t mean they have nothing to offer us.

Software Engineering

  • source code version control: Level 3. To be honest I think that the matrix is outdated here, and that DVCS is well on its way to becoming the new established workflow. When the matrix was written, SVN was the de facto standard for VCS. Version control is just one of those things that you need to know and need to use properly. It will probably still change quite a lot (I don’t think that any of the DVCS platforms out there make the uber-common “star” distribution any easier than decentralised development), and my opinion is that I need to be good at the tools I need to use. Of course, being a contractor that often means being good at all of the tools.
  • build automation: Level 3. I’ve written here before about setting up tools like HeaderDoc and CruiseControl. I definitely think some of the tools need improving, so could find time to work on that.
  • automated testing: Level 3. Something I need to do more of, and can “learn on the job” while I’m doing that: but there isn’t any point in having a “do UI testing” research project.

Programming

  • problem decomposition: Level 3. That’s not to say I’m completely perfect at object-oriented analysis and design, and every time I see someone else’s code I’m interested in what patterns they’ve chosen, and how they’ve organised their code. Actually, my current writing project—a book, video training course and several conference talks on TDD—has been useful for learning more about decomposition and modularity.
  • systems decomposition: Level 2. This is an area I definitely need to focus on, because almost all modern applications are actually multiple-component systems that either do, or should, present the same data across multiple devices and feature both dedicated and incidental online components.
  • communication: Level 3. I think the amount of time I’ve spent recently on books, conference talks, workshops and the like means that I’ve actually over-engineered this aspect significantly and can dial back on it once my current obligations are discharged.
  • code organisation within a file: Unsure. Maybe level 3: well I definitely have a style I use and am comfortable with: is it “beautiful”? Does that even matter? Shouldn’t the important factor be: “reading this programmer’s code doesn’t get in the way of understanding and modifying this programmer’s code”?
  • code organisation across multiple files: Level 2. Who uses the file system any more, anyway? I organise my classes and other resources in the Xcode project navigator. Turns out I use Xcode as some sort of “project builder” tool, not the Finder. If the view of projects and groups in the IDE were the metric in question, then I think I’d be at Level 3 here.
  • source tree organisation: Level 2, for similar reasons.
  • code readability: Level 3. Again, this book/research project on TDD has been my most recent and valuable guide.
  • defensive coding: Level 3. Ensuring you don’t only code and test the happy path is one of my current rants, if you give me enough beer. There was a section in my first book on this.
  • error handling: Level 3. I think this is really just an aspect of defensive coding, though I agree that a consistent approach across projects is important. I’m not a person who avoids using exceptions in Objective-C code.
  • IDE: Level 2. I suppose if you count shell script build phases as ‘custom macros’, then I’ve done level 3 work, but I don’t have a library of those shell scripts that I carry around to various projects. I probably could make some efficiency savings by recording how I use Xcode and reducing any commonly-repeated steps or actions.
  • API: Level 1. No, srsly. I know of the existence and capabilities of many of the Cocoa and Cocoa Touch APIs (with, for obvious reasons, the most focus on Foundation and Core Foundation) but always have to look up the class reference docs. This is really the most important thing I need to work on, so my next research project will be to deepen my knowledge of the APIs.
  • framework: Level 2. In addition to the Cocoa frameworks, I’ve worked with WebObjects, Eclipse RCP, wxWidgets and (more’s the pity) Swing. Not sure I particularly need to write a framework at the moment, there are other people who do good jobs of that. Exploring MonoTouch would be useful.
  • requirements: Level 3. Being a contractor, it’s important to know when to try and change the client’s mind and do things differently, if you want to write good software rather than write crap for a good hourly rate. I’ve also worked on in-house teams where the product manager or architect didn’t have any Mac experience, and it’s impossible to work on those without being able to suggest improved requirements.
  • scripting: Level 2. Yes, automating common developer tasks is still in its infancy, and I probably should find, clean up and publish some of the scripts I’ve created over time.
    (By the way: different scripting languages are useful for different things. I’ve learnt a lot of bash, perl and python, but should probably pick up ruby too. Don’t dismiss any of them for looking like unintelligible spider scrawl; you’ll probably find a use for it one day.)
  • database: Level 1. EOF and Core Data do exist for a reason…to be honest I don’t think I’ll ever be a Level 3 database person, it’s probably cheaper to hire somebody if I ever need that. Most of the products I’m currently being asked to work on either have somebody else for that or don’t have strong performance requirements.

Experience

Actually, I’m going to skip this section. I don’t believe that you can do a research project on improving your experience; only your exposure. You gain experience by doing.

Knowledge

  • tool knowledge: Level 2. As I’ve said there are clear shortcomings in the tools I’ve used, and I’d love to improve them or write something new and get it out there.
  • languages exposed to: Level 2. I have done Prolog, but no concurrent programming languages. Additionally, Andre Pang’s talk on Functional Programming at NSConf 09 showed that I could learn a lot more from that arena.
  • knowledge of upcoming technologies: Level 3. It’s a particular point of professional pride to me that I know what’s coming up and how it changes what I’m working on. Of course, in a world of non-disclosure agreements then “sharing” has to be limited to immediate project teams and people at WWDC. You are coming to WWDC, right?
  • platform internals: Level 2. I’m not Amit Singh. I already have a project on the horizon to do some more hacking on Clang, and have spoken about that before at NSConf Mini. It’s questionable whether a deep understanding of the platform internals is necessary, or whether that should be abstracted by the platform frameworks and interfaces. Personally I believe it is something useful to know, particularly in diagnosing bug reports. It’s well known that almost no bugs reported in your software are really bugs in the OS, and a deep understanding of the OS can help to confirm that.
  • books: Level 1, because I haven’t read Peopleware. I’ve written 1.5 books, does that count for anything?
  • blogs: Level 3. You’re reading it. As identified above, I probably expend more effort on communication than is necessary.

So, that’s my current state of competency in a nutshell. From there, it looks like my priorities for research goals over the upcoming year or so should be:

  1. Improve breadth and depth of API knowledge
  2. Investigate systems decomposition of a smartphone/desktop/internet system.
  3. Learn how to create and support DSLs
  4. Write and publish improved developer support tools

OK, your turn: what are yours?