[BUG]
LiteDB version 5.0.21 Microsoft Windows Embedded Standard 6.1.7601.NULL, 64 bit.
We've developed an application in .Net Core 6 that works fine on most machines, but I got a report that it fails with this exception on certain machines:
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Unknown error (0xc1000001)
at Interop.BCrypt.BCryptAlgorithmCache.GetCachedBCryptAlgorithmHandle(String hashAlgorithmId, BCryptOpenAlgorithmProviderFlags flags, Int32& hashSizeInBytes)
at Internal.Cryptography.HashProviderCng..ctor(String hashAlgId, ReadOnlySpan`1 key, Boolean isHmac)
at System.Security.Cryptography.SHA1.Implementation..ctor()
at LiteDB.StringExtensions.Sha1(String value)
at LiteDB.SharedEngine..ctor(EngineSettings settings)
at LiteDB.ConnectionString.CreateEngine(Action`1 engineSettingsAction)
at LiteDB.LiteDatabase..ctor(ConnectionString connectionString, BsonMapper mapper)
at LiteDB.LiteDatabase..ctor(String connectionString, BsonMapper mapper)
I'm starting LiteDb in shared mode, and as the exception shows, something goes wrong when creating the name for the mutex. Any idea what could be causing this?
The error only happens once in a while, and if the app is restarted it typically works fine again
Looks like your issue arises from the following code:
var name = Path.GetFullPath(settings.Filename).ToLower().Sha1();
public static string Sha1(this string value)
{
var data = Encoding.UTF8.GetBytes(value);
using (var sha = SHA1.Create())
{
var hashData = sha.ComputeHash(data);
var hash = new StringBuilder();
foreach (var b in hashData)
{
hash.Append(b.ToString("X2"));
}
return hash.ToString();
}
}
The .Sha1() seems to be necessary because if you remove it, it tries to create a mutex with an invalid name like Global\c:\users\user\documents\github\litedb\litedb.tests\bin\debug\net8\demo.db.Mutex, so to avoid this it hashes the database filepath with SHA1.
To me that seems like a sketchy workaround, especially when SHA1 has known collisions.
I would avoid hashing entirely by using URL encoding, which I've tested correctly encodes all invalid file path characters:
string name = Uri.EscapeDataString(Path.GetFullPath(settings.Filename).ToLowerInvariant());