Search Unity

[Hololens] CryptographicEngine.Encrypt() throwing InvalidCastException

Discussion in 'VR' started by ssedlmayr, Oct 14, 2016.

  1. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    I'm porting some crypto code to UWP for HoloLens. AFAIK, I've implemented it perfectly according to UWP requirements, but I get a mysterious InvalidCastException from the following code:

    Code (CSharp):
    1. public override byte[] Encrypt(byte[] clearData, byte[] Key, byte[] IV)//...
    2.  
    3. IBuffer dataBuffer = WindowsRuntimeBuffer.Create(clearData, 0, clearData.Length, clearData.Length);
    4.     IBuffer keyBuffer = WindowsRuntimeBuffer.Create(Key, 0, Key.Length, Key.Length);
    5.     IBuffer ivBuffer = WindowsRuntimeBuffer.Create(IV, 0, IV.Length, IV.Length);
    6.  
    7.     string strAlgName = KeyDerivationAlgorithmNames.Pbkdf2Sha256;
    8.  
    9.     // Open the specified algorithm.
    10.     KeyDerivationAlgorithmProvider objAlgProv = KeyDerivationAlgorithmProvider.OpenAlgorithm(strAlgName);
    11.  
    12.     // Create a key by using the passed buffer.
    13.     CryptographicKey key = objAlgProv.CreateKey(keyBuffer);
    14.  
    15.      IBuffer encryptedData = CryptographicEngine.Encrypt(key, dataBuffer, ivBuffer); // throws System.InvalidCastException
    16.      return (byte[])encryptedData.ToArray();
    I'm not really sure what the issue is. I've verified that the types of all parameters being passed into CryptographicEngine.Encrypt are the right type. I tried the AsBuffer extension method earlier on my byte arrays; I switched to WindowsRuntimeBuffer.Create thinking that might do the trick, but it makes no difference whatsoever. dataBuffer and ivBuffer are both of type WindowsRuntimeBuffer, which implements IBuffer, and both are a length that is a multiple of 8 (32 and 16, respectively). I tried casting as IBuffer; that didn't work. I even tried generating 3 random IBuffers, all of lenght 32, to see if that would at least work; it doesn't work. Is this a bug, or am I missing something really obvious? Moreover, the InvalidCastException is completely generic and only gives me the StackTrace from my own app, which tells me absolutely nothing.

    FWIW, here's the whole stack trace:

    at Windows.Security.Cryptography.Core.CryptographicEngine.Encrypt(CryptographicKey key, IBuffer data, IBuffer iv)
    at dk.UWP.Crypto.UWPSimpleEncryption.Encrypt(Byte[] clearData, Byte[] Key, Byte[] IV)
    at dk.UWP.Crypto.UWPSimpleEncryption.Encrypt(String clearText, String Password)
    at dk.Crypto.SimpleEncryption.Encrypt(String clearText, String Password)
    at bbtb.Scene.BBTBInitialization.d__19.MoveNext() at UnityEngine.SetupCoroutine.InvokeMoveNext(IEnumerator enumerator, IntPtr returnValueAddress)
    at UnityEngine.SetupCoroutine.$Invoke1InvokeMoveNext(Int64 instance, Int64* args)
    at UnityEngine.Internal.$MethodUtility.InvokeMethod(Int64 instance, Int64* args, IntPtr method)
     
  2. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    Try enabling .NET native before debugging - it has much better stacktraces and debuggability when exceptions get thrown in the interop layer.
     
    Hodgson_SDAS likes this.
  3. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    @Tautvydas-Zilys thanks, I'll try that. I'll probably also break it down to a standalone use case; this is in a Pre-Alpha library I'm working on... if it was alpha I had unit tests set up, probably would be somewhat easier.
     
  4. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
  5. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    I broke it down to a bare use case; created a new script and attached it to an empty go in the scene. It calls a single test method in Start(). The test method contains a barebones/boilerplate case of string encryption. It still throws the InvalidCastException.
     
  6. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    Code (CSharp):
    1. BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
    2. IBuffer clearTextBuffer = CryptographicBuffer.ConvertStringToBinary(clearText, encoding);
    3.  
    4. string strAlgName = KeyDerivationAlgorithmNames.Pbkdf2Sha256;
    5.  
    6. KeyDerivationAlgorithmProvider objKdfProv = KeyDerivationAlgorithmProvider.OpenAlgorithm(strAlgName);
    7. IBuffer buffSecret = CryptographicBuffer.ConvertStringToBinary(Password, BinaryStringEncoding.Utf8);
    8. CryptographicKey originalKey = objKdfProv.CreateKey(buffSecret);
    9.  
    10. IBuffer encryptedData = CryptographicEngine.Encrypt(originalKey, clearTextBuffer, null); // Still throws System.InvalidCastException
     
  7. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    I also tried UTF16LE encoding for the string; didn't work.
     
  8. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    I just tried another bare-bones example, this time with a byte array.
     
  9. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    Code (CSharp):
    1. IBuffer dataBuffer = WindowsRuntimeBuffer.Create(data, 0, data.Length, data.Length);
    2.  
    3. string strAlgName = KeyDerivationAlgorithmNames.Pbkdf2Sha256;
    4.  
    5. KeyDerivationAlgorithmProvider objKdfProv = KeyDerivationAlgorithmProvider.OpenAlgorithm(strAlgName);
    6. IBuffer buffSecret = CryptographicBuffer.ConvertStringToBinary(Password, BinaryStringEncoding.Utf8);
    7. CryptographicKey originalKey = objKdfProv.CreateKey(buffSecret);
    8.  
    9. IBuffer encryptedData = CryptographicEngine.Encrypt(originalKey, dataBuffer, null); // Still throws System.InvalidCastException
     
  10. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    I've reduced the case as much possible (still doesn't work):

    Code (CSharp):
    1. IBuffer dataBuffer = CryptographicBuffer.GenerateRandom(1);
    2. string strAlgName = KeyDerivationAlgorithmNames.Pbkdf2Sha256;
    3. KeyDerivationAlgorithmProvider objKdfProv = KeyDerivationAlgorithmProvider.OpenAlgorithm(strAlgName);
    4. IBuffer secret = CryptographicBuffer.GenerateRandom(32);
    5. CryptographicKey key = objKdfProv.CreateKey(secret);
    6.      
    7. CryptographicEngine.Encrypt(key, dataBuffer, null); // Still throws System.InvalidCastException
     
  11. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    Looks like I'm going to have to submit some bug reports; one to Unity and one to MS. Unfortunately I have other things to do today, so it's going to have to wait a while.
     
  12. Tautvydas-Zilys

    Tautvydas-Zilys

    Unity Technologies

    Joined:
    Jul 25, 2013
    Posts:
    10,680
    This doesn't seem to have anything to do with Unity: it's windows API that's throwing the exception (or .NET marshaling layer). As I said: enable .NET Native toolchain in Visual Studio project properties - that will allow you to debug the interop layer.
     
  13. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    @Tautvydas-Zilys, despite the Holograms page I cited earlier, you are correct; native debugging can be enabled inside the project properties directly on the Debug page. I did so, but the stack trace was scarcely more helpful:

    System.InvalidCastException was unhandled by user code
    HResult=-2147467262
    Message=Specified cast is not valid.
    Source=Windows
    StackTrace:
    at Windows.Security.Cryptography.Core.CryptographicEngine.Encrypt(CryptographicKey key, IBuffer data, IBuffer iv)
    at TestEncrypter.Encrypt()
    at TestEncrypter.Start()
    at TestEncrypter.$Invoke1Start(Int64 instance, Int64* args)
    at UnityEngine.Internal.$MethodUtility.InvokeMethod(Int64 instance, Int64* args, IntPtr method)
    InnerException:

    Also, I would be inclined to agree with you ordinarily about this being an MS-only bug, except for the fact that you can only build for Hololens via the Unity Technical Preview; and that application generates the VS project.
     
  14. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    I also tried entering the native code from the debugger by stepping inside CryptographicEngine.Encrypt(); this was after painstakingly loading all of the PDBs from the MS Symbol Servers, which took several minutes. All I got was a message stating that the source was unavailable and an option to read the disassembly, which is also not very helpful.
     
  15. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    @Tautvydas-Zilys, I've verified that it isn't a Unity issue; I set up a standalone UWP app with the same test case and it throws the same exception. I guess not very many people must be using the framework encryption in UWP, because this bug more or less shuts down the whole namespace, but I haven't come across any bug reports. I don't suppose you could point me to the best place to report an issue like this? The MSDN websites are a bit ambiguous as far as where to actually report issues. The best candidate I've found so far is:
    https://connect.microsoft.com/VisualStudio/feedback/
     
  16. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
  17. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    It looks as if, for whatever reason, the examples MSDN provides with KeyDerivationAlgorithmProvider and KeyDerivationAlgorithmNames do not work. If you instead use SymmetricAlgorithmNames and SymmetricAlgorithmProvider, it seems to work fine. I'm not sure if this is by design; if so, it is completely undocumented. I will post more information if I discover it.
     
  18. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
  19. ssedlmayr

    ssedlmayr

    Joined:
    Feb 28, 2013
    Posts:
    36
    Although it is not clearly documented, different algorithm providers are used for key derivation and encryption. You need two keys - one to generate an iv buffer for encryption - and another, symmetric key for encryption as well. The key buffer from the first key also gets fed into the creation process for the symmetric key. The examples on the page for CryptographicEngine are unfortunately generic and don't capture these details well.