Experimenting with Unity’s Asset Bundle Loading in Holographic UWP Projects

NB: The usual blog disclaimer for this site applies to posts around HoloLens. I am not on the HoloLens team. I have no details on HoloLens other than what is on the public web and so what I post here is just from my own experience experimenting with pieces that are publicly available and you should always check out the official developer site for the product documentation.

I wanted to experiment with Asset Bundles from Unity running on HoloLens in the sense that I wanted to make sure that I had the right pieces to dynamically load up something like some 3D models bundled on a web server dynamically at runtime and display them in a scene.

This is, no doubt, a very basic scenario for experienced Unity folks but I hadn’t tried it out on HoloLens so I thought I’d do that and share the write-up here.

Step 1 – Reading the Unity Asset Bundle Docs

I had a decent read of Unity’s docs around asset bundles, serialization graphs and that type of thing all from this set of resources;

A guide to AssetBundles and Resources

and that was pretty useful and led me through to the demo and companion scripts that Unity publishes on this repo;

Asset Bundle Demo Repo

and so I cloned that.

Step 2 – Building Asset Bundles

I then opened up that demo project in Unity and used the Assets menu to make the asset bundles;

image

which dropped the assets out into a folder called Windows;

image

and so I just need to drop these onto a web server somewhere and serve them up. I’ve heard that Azure thing is pretty good at this Winking smile

Step 3 – A Web Server for the Asset Bundles

I popped over to Azure and created a basic web server using the regular ‘Add App Service’ button;

image

and then it turned out that I didn’t actually have the Visual Studio web tools installed (woops, I should fix that) so I just went with the “edit this in the browser” option;

image

and hacked the web.config so as to try and make sure that it would serve up the files in question;

image

and then used the “Upload files” option to upload the files – note that this means that these files live at /AssetBundles/Windows and there’s another file in that folder also called Windows;

image

Step 4 – Trying the Desktop Demo

To see if this was “working” ok, I opened up the AssetLoader scene provided in the demo project, opened up the LoadAssets script associated with the Loader GameObject as below;

image

noting that it is loading the bundle named cube-bundle and the asset named MyCube;

image

and I hacked the accompanying script such that it loaded the asset from my new web site;

image

and, sure enough, I built and ran the desktop app and it displayed the cube as expected;

image

and Fiddler shows me where it’s coming from;

image

and so that’s all good, what about doing this on HoloLens?

Step 5 – Starting with a Blank HoloLens Project

I made a new Unity project much like I do in this video and imported the HoloToolkit-Unity;

Step 6 – Adding in the Asset Bundle Manager

I took some of the scripts from the demo asset bundle project and added them to my project, trying to be as ‘minimal’ as I could be so I brought in;

image

Bringing in the Asset Bundle Manager caused me a slight problem in that there are a couple of functions that don’t build in there on the HoloLens/UWP platform;

    //private static string GetStreamingAssetsPath()
    //{
    //  if (Application.isEditor)
    //    return "file://" + System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.
    //  else if (Application.isWebPlayer)
    //    return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";
    //  else if (Application.isMobilePlatform || Application.isConsolePlatform)
    //    return Application.streamingAssetsPath;
    //  else // For standalone player.
    //    return "file://" + Application.streamingAssetsPath;
    //}


    /// <summary>
    /// Sets base downloading URL to a directory relative to the streaming assets directory.
    /// Asset bundles are loaded from a local directory.
    /// </summary>
    public static void SetSourceAssetBundleDirectory(string relativePath)
    {
      throw new NotImplementedException();
      // BaseDownloadingURL = GetStreamingAssetsPath() + relativePath;
    }

one is public and calls the other and nothing seemed to call the public function so I figured that I was safe to comment these out at the moment (as above).

I continued by making a small “UI” with a button on it which would respond to a click;

image

and added some code to respond to the click;

using AssetBundles;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Placeholder : MonoBehaviour
{
  public void OnClick()
  {
    if (!this.loaded)
    {
      StartCoroutine(this.LoadModelAsync());
      this.loaded = true;
    }
  }
  IEnumerator LoadModelAsync()
  {
    AssetBundleManager.SetSourceAssetBundleURL("http://mttestbundles.azurewebsites.net/AssetBundles/");

    var initializeOperation = AssetBundleManager.Initialize();

    if (initializeOperation == null)
    {
      yield break;
    }
    yield return StartCoroutine(initializeOperation);

    var loadOperation = AssetBundleManager.LoadAssetAsync(
      "cube-bundle", "MyCube", typeof(GameObject));

    if (loadOperation == null)
    {
      yield break;
    }
    yield return StartCoroutine(loadOperation);

    var prefab = loadOperation.GetAsset<GameObject>();

    if (prefab != null)
    {
      var cube = GameObject.Instantiate(prefab);

      // Put the cube somewhere obvious.
      cube.transform.position = new Vector3(0, 1.0f, 2.0f);

      // The cube in the asset bundle is quite big.
      cube.transform.localScale = new Vector3(0.2f, 0.2f, 0.2f);
    }
  }
  bool loaded;
}

This code is really just me taking what seemed like the “essential” code out of the Unity sample and seeing if I could get it running on its own here inside of my HoloLens project.

Not surprisingly, I couldn’t Smile The code crashed.

Step 7 – Adding the Platform Name

Fixing the crash wasn’t too difficult – there are two functions in the supplied Utility.cs code file named GetPlatformForAssetBundles (one for the editor and one for the non-editor) and they are just a big switch statement that turns an enumeration for the build platform into a string. They needed me to add;

image

although I can’t be 100% certain that this is the right thing to do but it seemed like a reasonable place to start.

However, this didn’t work either Smile

Step 8 – The Wrong Build Target

With that change in place, my code no longer crashed but I did some debugging and some watching of the Unity debug spew and ultimately I could see that Unity was taking a look at my asset bundle and rejecting it. That is, it was saying (and I quote Winking smile);

The file can not be loaded because it was created for another build target that is not compatible with this platform.
Please make sure to build AssetBundles using the build target platform that it is used by.
File’s Build target is: 5

Ok – so clearly, here, I’ve built the asset bundles using the demo project supplied and they emit some target identifier of “5” into the output and that “5” is not viewed as being compatible with my current build target in my HoloLens project.

I left the assets in the demo project and simply changed the build target to be Windows Store;

image

as the script which does the building (BuildScript) seemed to be making use of EditorUserBuildSettings.activeBuildTarget so that felt like it might work. Because the build platform was now UWP, I needed to make the same changes to the scripts that I’d made in my own project in order to add the “Windows” name for the WSA… runtime platform enumerations.

With that done, I could click “Build Asset Bundles…” and get some new bundles and I overwrote the ones on my web server with these new variants.

That worked out fine and on the HoloLens I can then click the button in order to dynamically pull the cube asset bundle from the web server and display it on the device as below;

20170328_120919_HoloLens

Wrapping Up

With a couple of minor script changes, this workflow around asset bundles seems to work pretty well for the HoloLens. I could perhaps experiment a little further and try out working with different variants of assets and so on but, for my quick experiment, I’m happy that it’s working reasonably well and without too much of a deviation from what the Unity docs say.

What I haven’t yet tried is using the local server that Unity provides and the simulation mode to see if I can get that working for a HoloLens project but it feels like it should work to me…maybe that’s one for another post?

Windows Hello, Microsoft Passport and the KeyCredentialManager API

I’m a big fan of multi-factor authentication where some party authenticates me not just on the basis of my providing the right combination of username and password but, in addition, asks me to prove that I have access to some other piece of material that’s, hopefully, unlikely to be available to anyone but me.

This is pretty common in the physical world – when we sign up for new domestic services, it’s quite common to have to be asked to provide;

  1. An identity card (e.g. a UK driving license has a photo, signature and address).
  2. A recent utility bill.

and the two factors are taken as being a stronger proof than just the one although it’s perhaps a bit of a stretched analogy.

Back in the virtual world, there’s 2 places where I encounter 2-factor authentication frequently;

  1. In the public space, when I sign in to sites with my Microsoft Account, I have 2-factor authentication turned on and so, often, the server-side bits will send a text message to my mobile phone with some random code number that I need to enter back on the website before authentication will complete.
    1. This only works because I have previously registered my mobile phone number as being associated with my account.
  2. In the enterprise space, when I try and sign on to Microsoft properties these days with a username and password, the server-side bits send out a voice call to my mobile phone and I’m asked to provide an additional PIN number to complete the authentication.
    1. This only works because my employer knows my mobile phone number and I have previously set up a PIN with their servers.

As the sub-bullets note in both cases, the authentication system is using some additional ‘prior knowledge’ that it has about me (e.g. my phone number and/or an extra PIN) in order to make doubly sure that it’s me that is sending the authentication request rather than just someone who’s somehow got hold of my user name and password.

In order for that to work, there needs to be some kind of registration step where I provide the authentication mechanism with whatever information the second factor authentication relies upon – for example, this would be my mobile phone number in (1) above.

The additional security is welcome but, naturally, it does come with slightly more friction than a regular ‘username + password’ style login and the increasing use of these multi-factor mechanisms relies on finding the right security/convenience trade-off for me as a user.

Windows 10 has new technologies that are trying to find that security/convenience balance – Windows Hello and Microsoft Passport – and which radically change the way in which we sign in to a Windows machine and how we might then authenticate ourselves from that Windows machine to other services.

In short, I think Windows 10 changes 2 things;

  1. The user can sign in to their device with a short PIN code or with a biometric mechanism such as facial recognition, fingerprint or iris scanner known as a “Hello”;
    1. A “Hello” should be more convenient for the user than a username and password.
    2. A “Hello” is a local mechanism to unlock one device. It’s local to that device and the data for it never leaves that device. It is not part of the data used to authenticate with a service and so it is hard for a hacker to steal. Additionally, a “Hello” for one device (like a PIN) will not unlock another device.
  2. The device becomes part of the authentication process when the user attempts to access other services;
    1. The device can generate and store a ‘Passport’ for the user to access a particular service.
    2. The device can secure a user’s ‘Passports’ such that access to them is only granted to that signed-on user and only then for specific scenarios.
    3. The device can ‘prove’ to a particular service that it holds a ‘Passport’ for access to that service.

The pieces under (2) above make use of cryptography and 2.2 would usually involve special hardware support from something like a TPM chip although I don’t believe that the Windows 10 bits mandate a TPM chip.

Clearly for 2 to work with backend services (including e.g. accessing services across machines within a managed Windows domain/AD environment) then some back-end services would need to change and I believe that work is already done for identities that are represented by Microsoft Accounts, Active Directory Accounts, Azure Active Directory Accounts and, beyond Microsoft, for services that support the Fast ID Online standard.

In terms of background reading around Windows Hello and Microsoft Passport, I found this Technet article to be a really good read.

Then in terms of background watching, this video arrived recently on Channel 9 from my friends @andy_wigley and @rajen_k and it’s one of the best that I’ve seen to date.

Windows Hello and Microsoft Passport on Channel9

Along with that video, there’s also the code sample on GitHub;

Microsoft Passport and Windows Hello Sample

I read up and watched but, naturally, it doesn’t turn me into some kind of ‘security expert’ over night so apply a pinch of salt to my ramblings on these topics but I wanted to understand what’s going on here and experiment with the UWP APIs that enable these types of scenarios.

The way that I think about the changes here is something like;

  • Without ‘Hello’ and ‘Passport’ I authenticate by following a process that’s something like;
    • I send a username and password combination over the network (hopefully using some secure transport protocol).
    • The service on the other side of that connection looks up the username in a database and finds a (salted) hashed password value associated with it.
    • The service performs a (salted) hash on the password it has received and checks that against the stored hash.
    • If the values match, the service issues some token that lets me access that service for some period of time and the process is repeated when it times out.
  • With ‘Hello’ and ‘Passport’ I authenticate by following a process that’s something like;
    • I send some identifying data over the network – perhaps a user id and a device id.
    • The service on the other side looks up the username and device in a database to find the public component of a ‘Passport’ associated with me on that device.
    • The service sends a response asking for proof that I have access to the private component of the ‘Passport’ in question.
    • The device asks me to provide a PIN/‘Hello’ to unlock my stored ‘Passport’ for that service. 
    • The device uses the unlocked ‘Passport’ to generate proof that the user has access to that ‘Passport’.
    • The device might also generate proof that the ‘Passport’ is being accessed securely under the remit of a TPM chip.
    • The service receives the proof that the user owns the ‘Passport’ and possibly proof that it is being stored securely by a TPM chip that the service trusts and, if so, issues a token that lets me access the service for some period of time.
    • At some point, the token times out and the process is repeated.

and I hope that I’ve got that ‘close enough’ to how things work but feel free to let me know if I’ve got things wrong.

For the client-side of this interaction, there’s clearly a need to be able to do at least;

  1. Request a new ‘passport’ for access to some service.
  2. Send the public component of that ‘passport’ to the service such that it can be registered for later retrieval.
  3. Prove to a service that the user of the device has access to the private component of a ‘passport’.
  4. Prove to a service that the private component of a ‘passport’ has been accessed under suitable restrictions (e.g. managed by a TMP chip).

and what’s surprising (in a good way) about all of this is that the APIs involved are really neat, simple and don’t involve going off to crypto-school for the next 12 months.

They key class in the UWP is the KeyCredentialManager and I wanted to walk through it’s methods to see if I could try and understand how it can enable using ‘Passport’ and ‘Hello’ for an app.

These are the steps that I took.

Request a New ‘Passport’ for Access to Some Service

I made a little UI, just some XAML with 4 TextBoxes in it and a couple of buttons. It’s as below;


  <Grid
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <!-- Naturally, hard coded strings here should be moved to x:Uid resources -->
    <Grid.Resources>
      <Style
        x:Key="baseStyle"
        TargetType="FrameworkElement">
        <Setter
          Property="Margin"
          Value="8" />
      </Style>
      <Style
        TargetType="TextBox" BasedOn="{StaticResource baseStyle}">
      </Style>
      <Style
        TargetType="Button"
        BasedOn="{StaticResource baseStyle}">
      </Style>
    </Grid.Resources>
    <Grid.RowDefinitions>
      <RowDefinition
        Height="Auto" />
      <RowDefinition
        Height="Auto" />
      <RowDefinition
        Height="*" />
    </Grid.RowDefinitions>
    <TextBox
      Header="Name of Service to Create Passport for"
      Text="{Binding ServiceName, Mode=TwoWay}"
      Grid.Row="0" />
    <StackPanel
      Grid.Row="1"
      HorizontalAlignment="Right"
      Orientation="Horizontal">
      <Button
        Content="Get Existing Passport"
        HorizontalAlignment="Right"
        IsEnabled="{Binding HasServiceName}">
        <b:Interaction.Behaviors>
          <bc:EventTriggerBehavior
            EventName="Click">
            <bc:CallMethodAction
              TargetObject="{Binding}"
              MethodName="GetExistingPassport" />
          </bc:EventTriggerBehavior>
        </b:Interaction.Behaviors>
      </Button>
      <Button
        Content="Create or Replace Passport"
        HorizontalAlignment="Right"
        IsEnabled="{Binding HasServiceName}">
        <b:Interaction.Behaviors>
          <bc:EventTriggerBehavior
            EventName="Click">
            <bc:CallMethodAction
              TargetObject="{Binding}"
              MethodName="CreatePassportAsync" />
          </bc:EventTriggerBehavior>
        </b:Interaction.Behaviors>
      </Button>
    </StackPanel>
    
    <!-- Grid that displays the passport details -->
    <Grid
      Grid.Row="2">
      <Grid.RowDefinitions>
        <RowDefinition
          Height="Auto" />
        <RowDefinition
          Height="Auto" />
        <RowDefinition
          Height="Auto" />
      </Grid.RowDefinitions>
      <TextBox
        Header="Passport Name"
        IsReadOnly="True"
        Text="{Binding PassportName}" 
        Grid.Row="0"/>
      <TextBox
        Header="Passport Public Key"
        IsReadOnly="True"
        TextWrapping="Wrap"
        Text="{Binding PassportPublicKey}"
        Grid.Row="1" />
      <TextBox
        Header="Passport Attestation"
        IsReadOnly="True"
        Text="{Binding PassportAttestation}"
        Grid.Row="2" />
    </Grid>
  </Grid>

and I then wrote a little code behind it in an attempt to be able to both create a new ‘Passport’ and/or to retrieve an existing one by name;

namespace App2
{
  using System;
  using System.ComponentModel;
  using System.Runtime.CompilerServices;
  using System.Runtime.InteropServices.WindowsRuntime;
  using System.Threading.Tasks;
  using Windows.Security.Credentials;
  using Windows.Security.Cryptography;
  using Windows.Storage.Streams;
  using Windows.UI.Popups;
  using Windows.UI.Xaml.Controls;

  public sealed partial class MainPage : Page, INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    public MainPage()
    {
      this.InitializeComponent();
      this.Loaded += (s, e) =>
      {
        this.DataContext = this;
      };
    }

    public string PassportName
    {
      get
      {
        return (this.passportName);
      }
      set
      {
        if (this.passportName != value)
        {
          this.passportName = value;
          this.RaisePropertyChanged();
        }
      }
    }
    public string PassportPublicKey
    {
      get
      {
        return (this.passportPublicKey);
      }
      set
      {
        if (this.passportPublicKey != value)
        {
          this.passportPublicKey = value;
          this.RaisePropertyChanged();
        }
      }
    }
    public string PassportAttestation
    {
      get
      {
        return (this.passportAttestation);
      }
      set
      {
        if (this.passportAttestation != value)
        {
          this.passportAttestation = value;
          this.RaisePropertyChanged();
        }
      }
    }
    public string ServiceName
    {
      get
      {
        return (this.serviceName);
      }
      set
      {
        if (this.serviceName != value)
        {
          this.serviceName = value;
          this.RaisePropertyChanged();
          this.RaisePropertyChanged("HasServiceName");
        }
      }
    }
    public bool HasServiceName
    {
      get
      {
        return (!string.IsNullOrEmpty(this.ServiceName));
      }
    }
    public async void CreatePassportAsync()
    {
      if (await this.CheckSupportAsync())
      {
        var result = await KeyCredentialManager.RequestCreateAsync(
          this.ServiceName,
          KeyCredentialCreationOption.ReplaceExisting);

        if (result.Status == KeyCredentialStatus.Success)
        {
          // We have a new 'passport', what can we get from it?
          this.keyCredential = result.Credential;
          this.OnKeyCredentialChange();
        }
        else
        {
          // TODO: put text into resources.
          await this.ShowDialogAsync(
            $"Failed to create a passport for the service named {this.ServiceName}",
            "Failed to Create Passport");
        }
      }
    }
    public async void GetExistingPassport()
    {
      this.keyCredential = null;

      if (await this.CheckSupportAsync())
      {
        var result = await KeyCredentialManager.OpenAsync(
          this.ServiceName);

        if (result.Status != KeyCredentialStatus.Success)
        {
          await this.ShowDialogAsync(
            $"Sorry, failed to open the passport called {this.ServiceName}",
            "Failed to Open Passport");
        }
        else
        {
          this.keyCredential = result.Credential;
        }
        this.OnKeyCredentialChange();
      }
    }
    async void OnKeyCredentialChange()
    {
      if (this.keyCredential == null)
      {
        this.PassportName = null;
        this.PassportPublicKey = null;
        this.PassportAttestation = null;
      }
      else
      {
        this.PassportName = this.keyCredential.Name;

        this.PassportPublicKey =
          CryptographicBuffer.EncodeToHexString(
            this.keyCredential.RetrievePublicKey());

        var result = await this.keyCredential.GetAttestationAsync();

        if (result.Status == KeyCredentialAttestationStatus.Success)
        {
          this.PassportAttestation = 
            CryptographicBuffer.EncodeToHexString(result.AttestationBuffer);
        }
        else
        {
          this.PassportAttestation = 
            $"Failed to get attestation - status is {result.Status}";
        }
      }
    }
    void RaiseKeyCredentialPropertiesChanged()
    {
      this.RaisePropertyChanged("PassportName");
      this.RaisePropertyChanged("PassportPublicKey");
      this.RaisePropertyChanged("PassportAttestation");
    }
    async Task<bool> CheckSupportAsync()
    {
      bool supported = await KeyCredentialManager.IsSupportedAsync();

      if (!supported)
      {
        // TODO: put text into resources.
        await this.ShowDialogAsync(
          "Sorry, we can't do this - maybe you haven't set a PIN?",
          "Functionality Not Supported"
        );
      }
      return (supported);
    }
    async Task ShowDialogAsync(string text, string title)
    {
      // Naturally, this text would belong in a resource.
      var dialog = new MessageDialog(text, title);

      await dialog.ShowAsync();
    }
    void RaisePropertyChanged([CallerMemberName] string callerName = "")
    {
      this.PropertyChanged?.Invoke(this,
        new PropertyChangedEventArgs(callerName));
    }
    KeyCredential keyCredential;
    string serviceName;
    string passportAttestation;
    string passportPublicKey;
    string passportName;
  }
}

and so this is pretty simple apart from its use of the GetExistingPassport, CreatePassportAsync and OnKeyCredentialChange methods which make use of KeyCredentialManager.RequestCreateAsync, OpenAsync in order to get hold of a KeyCredential either by creating it or by opening up an existing one by name.

From there, the values obtained from KeyCredential.Name, KeyCredential.RetrievePublicKey and KeyCredential.GetAttestationAsync are displayed in TextBoxes on the screen.

When I run this on my system and I create a new ‘Passport’ using the button I get the prompt for a PIN;

image

whereas opening up an existing Passport doesn’t require that extra authentication step.

The data that I get back then looks like;

image

and so it seems that I can create/access a passport and get its public key but note that the ‘Passport Attestation’ field is reporting a ‘NotSupported’ value.

Send the Public Component of that ‘Passport’ to the Service

Clearly, this is something that I can do – I’ve got the public key component and so I can send it off to a web service to store alongside some information about which user and device it belongs to and, as the Channel 9 video suggests, this has impact on the back-end service as there would be a need to store this public key information for each device for a user  rather than just storing a single password hash for a user across all their devices.

Prove to a Service that the User of the Device has Access to the Private Component of a ‘Passport’

If a service sends some piece of data to the client then the client can digitally sign it to prove that it has access to the private key of the ‘Passport’ that matches up with the public key part that the service has stored for the user/device combination.

I added another grid to my UI to enable me to enter a piece of text to be signed, a button to click to sign it and somewhere to display the results;

    <!-- Grid that allows the signing of a piece of data -->
    <Grid
      Grid.Row="3">
      <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
      </Grid.RowDefinitions>
      <TextBox
        Header="Text to sign"
        Text="{Binding TextToSign,Mode=TwoWay}" />
      <Button
        Grid.Row="1"
        HorizontalAlignment="Right"
        Content="Sign with Passport"
        IsEnabled="{Binding CanSignText}">
        <b:Interaction.Behaviors>
          <bc:EventTriggerBehavior
            EventName="Click">
            <bc:CallMethodAction
              TargetObject="{Binding}"
              MethodName="SignTextWithPassport" />
          </bc:EventTriggerBehavior>
        </b:Interaction.Behaviors>
      </Button>
      <TextBox
        Grid.Row="2"
        IsReadOnly="True"
        TextWrapping="Wrap"
        Header="Signature Text"
        Text="{Binding SignatureText}" />
    </Grid>

and I made a few code additions – I’ve listed just the additions/modifications to the code behind class below with a ‘NEW’ comment on them;

    async void OnKeyCredentialChange()
    {
      if (this.keyCredential == null)
      {
        this.PassportName = null;
        this.PassportPublicKey = null;
        this.PassportAttestation = null;
      }
      else
      {
        this.PassportName = this.keyCredential.Name;

        this.PassportPublicKey =
          CryptographicBuffer.EncodeToHexString(
            this.keyCredential.RetrievePublicKey());

        var result = await this.keyCredential.GetAttestationAsync();

        if (result.Status == KeyCredentialAttestationStatus.Success)
        {
          this.PassportAttestation =
            CryptographicBuffer.EncodeToHexString(result.AttestationBuffer);
        }
        else
        {
          this.PassportAttestation =
            $"Failed to get attestation - status is {result.Status}";
        }
      }
      // NEW: added this so that we change our opinion of whether we can
      // or can't sign text based on the key credential changing.
      this.RaisePropertyChanged("CanSignText");
    }
    // NEW: all the rest of the code from here onwards is new.
    public string SignatureText
    {
      get
      {
        return (this.signatureText);
      }
      set
      {
        if (this.signatureText != value)
        {
          this.signatureText = value;
          this.RaisePropertyChanged();
        }
      }
    }
    public bool CanSignText
    {
      get
      {
        return ((this.keyCredential != null) &&
          !string.IsNullOrEmpty(this.TextToSign));
      }
    }
    public string TextToSign  
    {
      get
      {
        return (this.textToSign);
      }
      set
      {
        if (this.textToSign != value)
        {
          this.textToSign = value;
          this.RaisePropertyChanged();
          this.RaisePropertyChanged("CanSignText");
        }
      }
    }
    public async void SignTextWithPassport()
    {
      // We need to take the text that we've got in the textbox and
      // convert it into an ibuffer.
      IBuffer buffer = CryptographicBuffer.ConvertStringToBinary(
        this.TextToSign, BinaryStringEncoding.Utf8);

      var result = await this.keyCredential.RequestSignAsync(buffer);

      if (result.Status == KeyCredentialStatus.Success)
      {
        this.SignatureText = CryptographicBuffer.EncodeToHexString(result.Result);
      }
      else
      {
        this.SignatureText =
          $"Failed to sign text with passport {result.Status}";
      }
    }
    string textToSign;
    string signatureText;

and then that lets me use the UI to generate a signature over some arbitrary piece of text and when I click that ‘generate signature’ button I get asked to verify my identity again by the system as it needs to use the private key to make the signature;

image

Note that I’m signing the whole ‘Text to sign’ here whereas it might be more normal to sign a hash of that text as signing can be expensive but, regardless, after a second I get some data that I assume is the signature back from the API;

image

If I pretend that the service had originally sent the client the text ‘the quick, brown fox jumps over the lazy dog’ then the client is now in a position to send back that text signed by the private key that belongs to the ‘Passport’;

I’d imagine that the service might want to ask for a signature of something more ‘unique’ and ‘time sensitive’ than my piece of text here about foxes and dogs Smile

How would the service then verify that this has been signed by that private key given that it would now have access to the original data, the signed data and the public key?

Without getting into having to write a service, I can replicate how that might happen by adding another button to my UI to invoke signature verification;

      <Button
        Grid.Row="3"
        HorizontalAlignment="Right"
        Content="Verify Signature"
        IsEnabled="{Binding CanVerifySignature}">
        <b:Interaction.Behaviors>
          <bc:EventTriggerBehavior
            EventName="Click">
            <bc:CallMethodAction
              TargetObject="{Binding}"
              MethodName="VerifySignature" />
          </bc:EventTriggerBehavior>
        </b:Interaction.Behaviors>
      </Button>

and then I can add some code to my code-behind in order to attempt to verify that signature using only the information that the service would have, namely;

  • the public key
  • the original text (which the service sent to the client)
  • the signature (which the client would send to the service)

and the service would more than likely not be using WinRT APIs so would end up with different code to what I will write here in my ‘client-side, WinRT simulation’ of the process but here’s how I am trying to do it – again, I’m listing only the code-behind modifications/additions with NEW comments;

    public string SignatureText
    {
      get
      {
        return (this.signatureText);
      }
      set
      {
        if (this.signatureText != value)
        {
          this.signatureText = value;
          this.RaisePropertyChanged();

          // NEW!
          this.RaisePropertyChanged("CanVerifySignature");
        }
      }
    }
    // NEW from here on.
    public bool CanVerifySignature
    {
      get
      {
        return (!string.IsNullOrEmpty(this.SignatureText));
      }
    }
    public void VerifySignature()
    {
      // not 100% sure that I have this algorithm right, needs more checking.
      var algorithm = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(
        AsymmetricAlgorithmNames.RsaSignPkcs1Sha256);

      // the original data, pulled from the textbox on screen
      var textBuffer = CryptographicBuffer.ConvertStringToBinary(
        this.TextToSign, BinaryStringEncoding.Utf8);

      // the signature data, pulled from the textbox on screen
      var signatureBuffer = CryptographicBuffer.DecodeFromHexString(
        this.SignatureText);

      // the public key, pulled from the textbox on screen
      var keyBuffer = CryptographicBuffer.DecodeFromHexString(
        this.PassportPublicKey);

      var cryptoKey = algorithm.ImportPublicKey(keyBuffer);

      var verified = 
        CryptographicEngine.VerifySignature(cryptoKey, textBuffer, signatureBuffer);

      ShowDialogAsync(
        $"The signature verification result was {verified}",
        "Signature Verification");
    }

and that seems to verify a correct signature and fail an incorrect one;

image

Prove to a Service that the Private Component of a ‘Passport’ has been Suitably Accessed

At this point, the service knows that the client has the private portion of the ‘Passport’ that it has stored as being associated with that user on that device.

What the service doesn’t ‘know’ is how that private portion is being managed – i.e. is it being secured by hardware like a TPM chip and, if so, is that a TPM chip that the service thinks that it can trust?

Or…has the private portion been exported and is being stored on a USB key or similar?

How can the service decide? The process is known as ‘TPM key attestation’ and you can read up on it on Technet here;

TPM Key Attestation

Again, I claim no expertise whatsoever here but this means that the TPM chip can then provide ‘proof’ to a service that it is managing the ‘Passport’ in question and that ultimately means doing some more signing by a certificate that’s on the chip and part of a certificate chain that the service is willing to trust.

The API calls that I’ve made so far to ask for attestation seem to return ‘NotSupported’ and back in this article;

Microsoft Passport Guide

there is a comment that a future feature would be to add;

“TPM attestation to protect keys so that a malicious user or program can’t create keys in software (because those keys won’t be TPM attested and can thus be identified as fake)

so perhaps that’s why my API calls are coming back with ‘NotSupported’ for the moment.

Wrapping Up, Code

I learned quite a lot while writing this post. If you want the code that I played with above then it’s linked here but remember that this was put together quickly for my experimentation.

Give me back my CPU(s)

Too many times I’m seeing this in my system tray;

image

What is that? It’s Process Explorer ( I run that instead of Task Manager ) saying that my CPU is pegged ( again ).

The primary culprits are;

  1. My Anti-Virus software. This has never detected a virus for me yet and so, generally, I run the risk and I stop the service and get my CPU back.
  2. The search indexer ( on Vista ). Generally, I fix this by restarting the service.

I wonder how many users throw up their arms and think that either;

  1. The OS is broken.
  2. The PC is broken.

I hope that future versions of Windows do something about run-away processes. It’s a tricky problem to solve but when I wrote here about what I was hoping for Vista, this was one of my top “hopes” and I don’t think that we’ve really got there just yet.