Bug 38933

Summary: CryptographicException from ProtectedData in multi-threaded execution
Product: [Mono] Class Libraries Reporter: Steve Sheldon <ssheldon>
Component: System.SecurityAssignee: Alexander Köplinger [MSFT] <alkpli>
Status: RESOLVED FIXED    
Severity: normal CC: alkpli, mono-bugs+mono
Priority: ---    
Version: 4.2.0 (C6)   
Target Milestone: Untriaged   
Hardware: PC   
OS: Linux   
Tags: Is this bug a regression?: ---
Last known good build:
Attachments: test case

Description Steve Sheldon 2016-02-19 19:05:23 UTC
Created attachment 15097 [details]
test case

I've been able to confirm this error in Mono 4.2.2.30 on multiple environments.  This error doesn't occur compiling the same code in Visual Studio.

Ubuntu 14.04 in an Oracle VM Virtualbox
Centos 5.11 in an Oracle VM Virtualbox
Centos 5.8 on physical hardware
But also 4.2.2.30 on Windows 10

A big factor is the number of processors, with 1 there is no problem... with more than 1 then it starts to fail.

If I do any sort of ProtectedData.Encrypt or ProtectedData.Decrypt in the main thread before starting the tasks everything works great.  If the first access to ProtectedData is in the tasks, then it fails.   It looks like there is a race condition to get the LocalMachine key, but once it's retrieved then it's fine.

My real problem, is I have a process that spawns off a number of threads where each thread loads it's configuration out of a file.   There is a encrypted key stored in the file which we have to decrypt.   I'm working around it right now by decrypting and passing it to the task instead of having the task do it.

I'm enclosing my test which confirms the issue by spawning multiple tasks and trying to encrypt a string.

compiled with 'mcs -r:System.Security test.cs'
execute with: 'mono test.exe [number of threads]'

Default for number of threads is 5, but sometimes it works so I bump it up to 10 or even 100 to force a failure.

If it works it should print out a number of encrypted values.

When it fails, the exception received is:

System.AggregateException: One or more errors occurred. ---> System.Security.Cryptography.CryptographicException: Data protection failed. ---> System.FormatException: Invalid length for a Base-64 char array or string.
  at System.Convert.FromBase64_Decode (System.Char* startInputPtr, Int32 inputLength, System.Byte* startDestPtr, Int32 destLength) <0x7f34d1b61020 + 0x00383> in <filename unknown>:0
  at System.Convert.FromBase64CharPtr (System.Char* inputPtr, Int32 inputLength) <0x7f34d1b60f30 + 0x000b7> in <filename unknown>:0
  at System.Convert.FromBase64String (System.String s) <0x7f34d1b60d80 + 0x00027> in <filename unknown>:0
  at System.Security.Cryptography.RSA.FromXmlString (System.String xmlString) <0x7f34d1c49200 + 0x0025e> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) <0x7f34d1e0c270 + 0x000c6> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) <0x7f34d1e0bfe0 + 0x0005c> in <filename unknown>:0
  at Mono.Security.Cryptography.ManagedProtection.GetKey (DataProtectionScope scope) <0x4199ae20 + 0x001e8> in <filename unknown>:0
  at Mono.Security.Cryptography.ManagedProtection.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41996400 + 0x0043f> in <filename unknown>:0
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41995e20 + 0x00097> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41995e20 + 0x0012b> in <filename unknown>:0
  at ProtectedDataFailureTest.Program.<Main>m__0 () <0x41995d90 + 0x0003b> in <filename unknown>:0
  at System.Threading.Tasks.Task.InnerInvoke () <0x7f34d1c9e920 + 0x0004f> in <filename unknown>:0
  at System.Threading.Tasks.Task.Execute () <0x7f34d1c9e1e0 + 0x00055> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Threading.Tasks.Task.WaitAll (System.Threading.Tasks.Task[] tasks, Int32 millisecondsTimeout, CancellationToken cancellationToken) <0x7f34d1ca12a0 + 0x005a9> in <filename unknown>:0
  at System.Threading.Tasks.Task.WaitAll (System.Threading.Tasks.Task[] tasks, Int32 millisecondsTimeout) <0x7f34d1ca1240 + 0x00028> in <filename unknown>:0
  at System.Threading.Tasks.Task.WaitAll (System.Threading.Tasks.Task[] tasks) <0x7f34d1ca11b0 + 0x00016> in <filename unknown>:0
  at ProtectedDataFailureTest.Program.Main (System.String[] args) <0x419950a0 + 0x001fe> in <filename unknown>:0
---> (Inner Exception #0) System.Security.Cryptography.CryptographicException: Data protection failed. ---> System.FormatException: Invalid length for a Base-64 char array or string.
  at System.Convert.FromBase64_Decode (System.Char* startInputPtr, Int32 inputLength, System.Byte* startDestPtr, Int32 destLength) <0x7f34d1b61020 + 0x00383> in <filename unknown>:0
  at System.Convert.FromBase64CharPtr (System.Char* inputPtr, Int32 inputLength) <0x7f34d1b60f30 + 0x000b7> in <filename unknown>:0
  at System.Convert.FromBase64String (System.String s) <0x7f34d1b60d80 + 0x00027> in <filename unknown>:0
  at System.Security.Cryptography.RSA.FromXmlString (System.String xmlString) <0x7f34d1c49200 + 0x0025e> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) <0x7f34d1e0c270 + 0x000c6> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) <0x7f34d1e0bfe0 + 0x0005c> in <filename unknown>:0
  at Mono.Security.Cryptography.ManagedProtection.GetKey (DataProtectionScope scope) <0x4199ae20 + 0x001e8> in <filename unknown>:0
  at Mono.Security.Cryptography.ManagedProtection.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41996400 + 0x0043f> in <filename unknown>:0
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41995e20 + 0x00097> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41995e20 + 0x0012b> in <filename unknown>:0
  at ProtectedDataFailureTest.Program.<Main>m__0 () <0x41995d90 + 0x0003b> in <filename unknown>:0
  at System.Threading.Tasks.Task.InnerInvoke () <0x7f34d1c9e920 + 0x0004f> in <filename unknown>:0
  at System.Threading.Tasks.Task.Execute () <0x7f34d1c9e1e0 + 0x00055> in <filename unknown>:0 <---

---> (Inner Exception #1) System.Security.Cryptography.CryptographicException: Data protection failed. ---> System.Security.Cryptography.CryptographicException: Input string does not contain a valid encoding of the 'RSA' 'Modulus' parameter.
  at System.Security.Cryptography.RSA.FromXmlString (System.String xmlString) <0x7f34d1c49200 + 0x004f9> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider.Common (System.Security.Cryptography.CspParameters p) <0x7f34d1e0c270 + 0x000c6> in <filename unknown>:0
  at System.Security.Cryptography.RSACryptoServiceProvider..ctor (Int32 dwKeySize, System.Security.Cryptography.CspParameters parameters) <0x7f34d1e0bfe0 + 0x0005c> in <filename unknown>:0
  at Mono.Security.Cryptography.ManagedProtection.GetKey (DataProtectionScope scope) <0x4199ae20 + 0x001e8> in <filename unknown>:0
  at Mono.Security.Cryptography.ManagedProtection.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41996400 + 0x0043f> in <filename unknown>:0
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41995e20 + 0x00097> in <filename unknown>:0
  --- End of inner exception stack trace ---
  at System.Security.Cryptography.ProtectedData.Protect (System.Byte[] userData, System.Byte[] optionalEntropy, DataProtectionScope scope) <0x41995e20 + 0x0012b> in <filename unknown>:0
  at ProtectedDataFailureTest.Program.<Main>m__0 () <0x41995d90 + 0x0003b> in <filename unknown>:0
  at System.Threading.Tasks.Task.InnerInvoke () <0x7f34d1c9e920 + 0x0004f> in <filename unknown>:0
  at System.Threading.Tasks.Task.Execute () <0x7f34d1c9e1e0 + 0x00055> in <filename unknown>:0 <---
Comment 1 Alexander Köplinger [MSFT] 2016-02-22 19:17:45 UTC
I can reproduce this on Mono 4.2, 4.3.2 and master.

Thank you for the excellent test case, it was really helpful! I'll look into the problem.
Comment 2 Alexander Köplinger [MSFT] 2016-03-01 10:15:20 UTC
Fixed with https://github.com/mono/mono/pull/2702. I'll put this into the Mono 4.4 branch too :)