Pages

Saturday 16 October 2010

What are you scared of?

Finding yourself stood in front of Eric Ries can be somewhat daunting. You have the opportunity to ask a question to, arguably, the leading light in the field of Lean Startup. As soon as you start speaking, though, the guy stood behind you in the queue starts getting restless. So, you better make it a good one!

I was unable to attend the Business of Software in Boston last month so was excited to see that Eric was doing a whistle-stop talk last night at TechHub, London. After a, not too inconvenient, train and tube journey I found myself in a vibrant room packed with budding entrepreneurs. The 'formal' part of Eric's talk was mostly content that I'd already seen online, so no major surprises there. What always makes these events unique, though, is the interaction with the people that turn up. The Q and A hand-raising session was very busy but I did manage to talk with Eric afterwards. Here's what I asked him:

Imagine you're back at IMVU circa 2004, but this time you know what you know now. You've created a Minimum Viable Product for just one Instant Messaging network that's already getting good traction. Geoffrey Moore would have probably suggested that you capture your beachhead in that first IM. Would you agree with that or would your next move be to create an MVP for each IM?
As is often the case in this field there was no short, definitive answer! What follows is paraphrased.

Eric: "What are you scared of?"
Me: "Uh, nothing. I'm an entrepreneur creating a startup - by definition am I not fearless?"
Eric: "No. What's your biggest fear?"
Without any prepared answer for this one I said "Well, probably someone else copying us". Eric rocked back, head shaking "No." I got the impression that he hears this 'wrong answer' often.

The gist of Eric's response was that I needed to look at my assumptions. I'd assumed that because my product 'works' for one social network that it will automatically be greeted in the same manner on all the other networks. The way you reassess these assumptions is to do more measuring against hard targets e.g. I'm currently at 10, I think I can get to 20 by time t. If the increments are 10, 12, 14... you're probably gonna make it, but if they're 10, 11, 11.5... you might have a problem. Testing your worst fears early is key.

MBAs are seen as a hindrance in the startup world, but the remainder of Eric's lesson to me did seem to cross a chasm into this territory. The basics of the Lean Startup are pretty straight forward: iterate through 'the loop' as quickly as you can and learn what works. Simple. Once you get into the guts of it, by talking with Eric, it starts sounding a whole lot more complicated. If I wanted an MBA I would have studied for one. I like my vision of the startup as having less politics, less overheard and, by extension, less rigor; but, again, this is an assumption.

I am, however, extremely grateful for the way Eric makes you question all of your assumptions - sometimes to the point of your own existence!

So, what is my biggest fear? After some considerable thought I think it's that people won't like what I do. I fully agree with Eric's observation that the best way to utterly deject developers is to have them spend time and energy creating something only for it to never see the light of day. Lean helps us avoid wasting everyone's time and for that, alone, it is worth pursuing.

Tuesday 12 October 2010

Standardising String Cryptography

I was writing the new Twitter Client for TweetPivot when I hit a problem. I needed to store a small amount of information on our users' machines; specifically in isolated storage. Most of this would be fine as plain text but one field needed to be encrypted. This meant I had to solve a problem I seem to come back to again and again - simple string cryptography.

Crypto Rule #1 seems to be "Don't roll your own.". OK fine, so give me something pre-rolled. C# / .Net doesn't contain the method I was looking for:

string Encrypt(string plainText, string password, string salt);

Luckily, @blowdart came to my rescue and posted this code. Now, there's a few things worth pointing out about this:
  1. Barry's book sales suggest he might actually know what he's doing, but who else amongst us has the confidence and knowledge to say that it's safe and reliable?
  2. Did anyone read the code and think "actually, TripleDES would suit my needs, not Rijndael"?
Answer: probably no.

So, given that a) it's hard to do correctly and b) this standard method would probably suit 99% of developers 99% of the time why, then, do we not find this in the BCL? If I want to open a file I can use System.IO.File.OpenRead(string path). If I need to take more control over the process I can use a StreamReader and dictate encodings etc.

I've included my final code below (you deserve something for reading this far!). Porting it into Silverlight simplifies matters - mainly because there's only one implementation of SymmetricAlgorithm. "But Barry defaulted to Rijndael. Does this mean that Silverlight is less secure than standard .Net applications?" Another crypto conundrum!

I do feel that this functionality needs abstracting behind a simplified facade. I genuinely don't care how long my initialisation vector is or whether I'm using DES, DoubleDES or even TripleDES. Cryptography is simply a tool for hiding stuff and it should be easier, safer and more confidence-imspiring than it currently is. @blowdart's subsequent tweet kinda sums up the complexity of the subject. Signatures, really? You might just as well have asked me if I wanted tweeters with it! #referenceToOldJoke
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public static class CryptographicExtensions
{
  public static string Encrypt(this string plainText, string password, string salt)
  {
    var algorithm = new AesManaged();

    algorithm.SetKeyAndIV(password, salt);

    return Encrypt(algorithm, plainText);
  }

  public static string Encrypt(this string plainText, ref byte[] key, ref byte[] iv)
  {
    if (string.IsNullOrEmpty(plainText))
      return plainText;

    var algorithm = new AesManaged();

    if (key != null)
      algorithm.Key = key;
    else
      key = algorithm.Key;

    if (iv != null)
      algorithm.IV = iv;
    else
      iv = algorithm.IV;

    return Encrypt(algorithm, plainText);
  }

  private static string Encrypt(SymmetricAlgorithm algorithm, string plainText)
  {
    var plainTextAsBytes = Encoding.UTF8.GetBytes(plainText);

    using (ICryptoTransform transform = algorithm.CreateEncryptor())
    {
      using (var outputStream = new MemoryStream())
      {
        using (var inputStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
        {
          inputStream.Write(plainTextAsBytes, 0, plainText.Length);
          inputStream.FlushFinalBlock();
          var cipherText = outputStream.ToArray();
          return Convert.ToBase64String(cipherText);
        }
      }
    }
  }

  public static string Decrypt(this string cypherText, string password, string salt)
  {
    var algorithm = new AesManaged();

    algorithm.SetKeyAndIV(password, salt);

    return Decrypt(algorithm, cypherText);
  }

  public static string Decrypt(this string cypherText, byte[] key, byte[] iv)
  {
    if (string.IsNullOrEmpty(cypherText))
      return cypherText;

    if (key == null)
      throw new ArgumentNullException("key");

    if (iv == null)
      throw new ArgumentNullException("iv");

    var algorithm = new AesManaged() { Key = key, IV = iv };

    return Decrypt(algorithm, cypherText);
  }

  private static string Decrypt(SymmetricAlgorithm algorithm, string cypherText)
  {
    var cipherTextAsBytes = Convert.FromBase64String(cypherText);

    using (ICryptoTransform transform = algorithm.CreateDecryptor())
    {
      using (var outputStream = new MemoryStream())
      {
        using (var inputStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
        {
          inputStream.Write(cipherTextAsBytes, 0, cipherTextAsBytes.Length);
          inputStream.FlushFinalBlock();
          var plainTextAsBytes = outputStream.ToArray();

          return Encoding.UTF8.GetString(plainTextAsBytes, 0, plainTextAsBytes.Length);
        }
      }
    }
  }

  public static void SetKeyAndIV(this SymmetricAlgorithm algorithm, string password, string salt)
  {
    string paddedSalt = salt;

    while (paddedSalt.Length < 8)
      paddedSalt += salt;

    var rfc2898 = new Rfc2898DeriveBytes(password, Encoding.UTF8.GetBytes(paddedSalt));

    algorithm.Key = rfc2898.GetBytes(algorithm.KeySize / 8);
    algorithm.IV = rfc2898.GetBytes(algorithm.BlockSize / 8);
  }
}