Keep It Secret, Keep It Safe

imageI had a problem with an open source project recently.  I checked out the source, did some research and I thought I had a pretty good idea how to solve the problem effecting me.  I sent the maintainer a patch, and he graciously invited me to do some pair programming on the issue.  Cool.

Now, the project in question is signed, and during my initial research I had trouble building it because I don’t have the signing key.  So, without really thinking about it (since I don’t have check-in rights on the project) I simply reconfigured the assemblies to build without signing enabled.  When we paired today, we ended up sharing my screen and using my working copy.  At the end of the session, we pushed from my copy and he used his credentials to authenticate.  The changes to AssemblyInfo.cs were inadvertently included as well.

I sent him an email explaining what happened and I’m sure the inadvertent changes will be reverted soon, but I naturally wondered what I could have done differently to prevent the problem.  Naturally, we could have both taken a closer look at the change set we were about to check in.  Before that, I could have been more careful with SVN.  We did run an update before starting, but I should also have cleaned the working copy.  Or, I could have just deleted the working copy and started with a fresh checkout.  Ok, so those are good habits to have, but is there any permanent change that would have prevented the problem even if I forgot to do any of that?

What I Could Do

I thought of the easy solution last.  Although the signing key is not checked into the repository, it’s expected name and location are known, since these values are referred to in the AssemblyInfo.cs file (I know because that’s the information I deleted!).  So the easiest thing to do would be for me to create a new key pair with the same filename, and put it in the expected location.  Visual Studio will be happy to compile the project, since it doesn’t actually know or care what key pair is used.

This solution is fine for testing/debugging purposes.  If I happen to go rouge and try to publish my own copies of the assemblies, the public keys will be different.  Anyone who cares enough can still distinguish between assemblies signed by me and those signed by the maintainer.  Since the key pair is not in the repository, it’s a “non-versioned” file and it will not be included in the change set if/when we check-in changes from my working copy.  I like this solution because I can implement it without bothering the maintainer.

What the Maintainer Could Do

If the maintainer wanted to make the project more idiot-proof, he could integrate some changes into the .cspoj files, MSBuild is here help.  Instead of specifying the signing key location in the AssemblyInfo.cs file using the AssemblyKeyFileAttribute, the location can be specified using the AssemblyOriginatorKeyFile property.  Once MSBuild knows the expected key file location, it can check that the file exists, and use that information to control the SignAssembly property.

Here’s an example:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0"
         DefaultTargets="Build"
         xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <!-- Other properties omitted -->
    <AssemblyOriginatorKeyFile>..\SecretKeyPair.snk</AssemblyOriginatorKeyFile>
  </PropertyGroup>
  <Choose>
    <When Condition="Exists($(AssemblyOriginatorKeyFile))">
      <PropertyGroup>
        <SignAssembly>true</SignAssembly>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <SignAssembly>false</SignAssembly>
      </PropertyGroup>
    </Otherwise>
  </Choose>

When Visual Studio starts, it will evaluate this condition once.  If the key is found, SignAssembly will become true; otherwise it will be false.  Moving, renaming, or deleting the file after Visual Studio is running will result in a build error when the IDE tries to sign the assembly.  Similarly, if the file is missing when Visual Studio starts, it will not try to sign the assembly, even if the key is copied to the correct location later.  The condition is not checked at build time, but at load time.  (You could also manually unload/load the project file from Visual Studio.)

I like this solution because its more idiot proof.  The project file adapts to it’s environment, the maintainer has the key and as a result will produce signed assemblies from his working copy.  Meanwhile, contributors can debug and test changes without even having to worry about creating their own signing key in order to build.

Either solution should work.