Search code examples
c#azureasp.net-coreasp.net-core-identity

Predicting length of UserManager.GenerateUserTokenAsync tokens (via DataProtectorTokenProvider)


I'm using ASP.NET Core 3.1, with some custom logic that reuses ASP.NET Core Identity's UserManager<TUser> class. I want to reuse its ability to GenerateUserTokenAsync(...), the same type of token also used in e.g. e-mail confirmations. For my application's purpose, I need to know the maximum length for generated tokens.

I've done the following research:

  • UserManager<TUser>.GenerateUserTokenAsync(...) calls GenerateAsync on the injected provider

  • in my case that is the DataProtectorTokenProvider<TUser> which will generate tokens like this:

    var ms = new MemoryStream();
    var userId = await manager.GetUserIdAsync(user);
    using (var writer = ms.CreateWriter())
    {
        writer.Write(DateTimeOffset.UtcNow);
        writer.Write(userId);
        writer.Write(purpose ?? "");
        string stamp = null;
        if (manager.SupportsUserSecurityStamp)
        {
            stamp = await manager.GetSecurityStampAsync(user);
        }
        writer.Write(stamp ?? "");
    }
    var protectedBytes = Protector.Protect(ms.ToArray());
    return Convert.ToBase64String(protectedBytes);
    
  • So the basic calculation for length of the memory stream would be:

    27 characters for DateTimeOffset.UtcNow
    36 characters for the user id (string representation of a GUID)
    20 characters for my specific "purpose" string
    36 characters for the security stamp (string representation of a GUID)
    ---- +
    119 characters in total
    
  • In the code snippet this gets Protected and then converted ToBase64String

When I casually tested this on my local machine, I got an encrypted string of 352 characters. How could I predict that the 119 input characters would become 352 characters when encrypted? And worse, my app runs actually in production on an Azure App Service where Azure's DPAPI should kick in, possibly with different encryption than on localhost?

Can I in any way predict what the maximum length of the generated User Tokens will be on Azure App Services? I'm happy to include a margin of error, but am clueless what that would need to be.


Solution

  • By default DPAPI uses AES-256-CBC as encryption algorithm unless you change it via UseCryptographicAlgorithms. As per default algo, the calculation would go like this for your case:

    Since it's AES 256, it would work with 32 bytes block. So with CBC padding, you output becomes ((119/32) + 1) * 32 + 16 (IV) = 144. After base64, it becomes 192.

    So, having it 352 brings up the question is the stamp really 36 in your case?

    36 characters for the security stamp (string representation of a GUID)

    Also, in deployed environment, make sure to store data protection key outside app since each instance of the app service needs to point to the same key.