Scenario:
Problem:
So far my predecessor used Symmetric Rijndael with Cipher Block Chaining and hardcoded digest to obfuscate the password. Any user & any machine which got the %localappdata% file copied over could authenticate successfully with User A's stored creds. I don't like.
Constraints for obfuscation:
Jon Galloway's blog which is from 2008 but made me look into DPAPI: as I understand it, System.Security.Cryptography.ProtectedData with DataProtectionScope.CurrentUser should disable other Users to deobfustcate the password.
Question:
What do I provide as entropy value to disable access for a cloned User A on a different machine or should I go another route altogether?
Some code to test the ProtectedData stuff from MSDN with a string passphrase:
// modified from https://learn.microsoft.com/de-de/dotnet/api/system.security.cryptography.protecteddata
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class DataProtectionSample
{
static string GetHexString (byte [] data)
=> string.Join (" ", data.Select (d => d.ToString ("x2")));
static string GetUnicodeString (byte [] data)
=> Encoding.Unicode.GetString (data);
static byte [] GetBytesFromUnicode (string data)
=> Encoding.Unicode.GetBytes (data);
public static void Main ()
{
// Create byte array for additional entropy when using Protect method.
byte [] entropy = { 9, 8, 7, 6, 5 };
byte [] secret = GetBytesFromUnicode ("Cräzy obfuscated paß phrâse");
//Encrypt the data.
byte [] encryptedSecret = Protect( secret , entropy );
Console.WriteLine ("The encrypted byte array is:");
Console.WriteLine (GetHexString (encryptedSecret));
// Decrypt the data and store in a byte array.
byte [] originalData = Unprotect( encryptedSecret, entropy );
Console.WriteLine ("{0}The original data is:", Environment.NewLine);
Console.WriteLine (GetUnicodeString (originalData));
Console.ReadLine ();
}
static byte [] Protect (byte [] data, byte [] entropy)
{
try
{
return ProtectedData.Protect (data, entropy, DataProtectionScope.CurrentUser);
}
catch (CryptographicException e)
{
Console.WriteLine (e.ToString ());
return null;
}
}
static byte [] Unprotect (byte [] data, byte [] entropy)
{
try
{
return ProtectedData.Unprotect (data, entropy, DataProtectionScope.CurrentUser);
}
catch (CryptographicException e)
{
Console.WriteLine (e.ToString());
return null;
}
}
}
I am aware that impersonating User A will make the obfuscation void - hence obfuscation not encryption. What I am after, is a kind of tougher version of "can't read plaintext pw from file" with a second factor of "need to be logged in with same windows user" and "on the same machine".
I also looked into How should I ethically approach user password storage for later plaintext retrieval? which does not quite help as it's answers mostly assume some kind of web scenario with password reset mechanism which I do not have.
Disclaimer: My passwords are random generated Keypass stored values. I do not use "Keep logged in" features. Storing passwords in plaintext is evil, storing passwords symmetrically encrypted can be broken and has to be avoided. Still need to provide above feature ...
https://www.harmj0y.net/blog/redteaming/operational-guidance-for-offensive-user-dpapi-abuse/ suggests that DPAPI has several exploitable "flaws" as soon as you are able to impersonate the user in any way or form.
I went with a combination of ProtectData.Protect and it's counterpart ProtectData.Unprotect adding in an entropy calculated from some values pertinent/different to each user.
The resulting bytestream is compressed using a DeflateStream and stored.
For decoding I do similar things just in reverse using the appropriate functions.
I am aware: this is password-obfuscation at best but for now it is "good enough" - and it is far better then using the same Crypto.dll with fixed key-phrases that work for everyone without access to a specific windows account/pc with its stored secrets.