Issue Details (XML | Word | Printable)

Key: DNET-832
Type: Bug Bug
Status: Closed Closed
Resolution: Fixed
Priority: Blocker Blocker
Assignee: Jiri Cincura
Reporter: Michael Wresche
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
.NET Data provider

Accessing entities using EntityFramework on encrypted database failed

Created: 29/Jun/18 08:06 PM   Updated: 29/Jun/18 08:21 PM
Component/s: None
Affects Version/s: 6.0.0.0
Fix Version/s: None

Environment: Issue occurred on develoment environment: Windows 10; .NET 4.7.1; Firebird 3.0.3.32900 (32 Bit); Entity Framework 6.2.0-61023.0; IBPhoenix Encryption Plugin 1.2.0
Issue Links:
Duplicate
 


 Description  « Hide
At first:
 - We have an encrypted Firebird database which will be accessed from our application by using Entity Framework
 - The encryption plugin uses a file-based key so the connection string does not contain any encryption information.
 - Accessing the encrypted database by using FbConnection, FbCommand, etc. works fine
 - If the database is not encrypted and the encryption plugin is not configured in Firebird, Entity Framework as well as FbConnection works fine
 - If we access the encrypted database using Entity Framework, we got the following exception (Unfortunately in german): Das Objekt des Typs "FirebirdSql.Data.Client.Managed.Version13.CryptKeyCallbackReponse" kann nicht in Typ "FirebirdSql.Data.Client.Managed.GenericResponse" umgewandelt werden. You can find the callstack of the exception at the end of the bug description.

I have debugged the issue with the latest sources on GitHub and was able to find the root cause (hopefully): The method GdsConnection.ProcessOperation returned an CryptKeyCallbackResponse which will be casted to GenericResponse in Version10.GdsDatabase.ReadGenericResponse and this fails.

After some short debugging sessions in order to find out what happened when I access the encrypted database directly (without Entity Framework), I came to the following conclusion:

Entity Framework invoked FbProviderServices.GetDbProviderManifestToken() with a connection which is not yet open. Because of the connection state the requested server version will be determined by using the FbServerProperties class (FbProviderServices Line 130). This class uses the FbService to communicate with the database. The FbService uses the GdsServiceManager and this class creates an instance of the Version10.GdsDatabase class which is used to communicate with the database. In my opinion, the instance of GdsDatabase should be created by using the appropriate factory method. Anyway, it seems that Version10.GdsDatabase is not able to communicate with encrypted databases. I came to the conclusion that encryption is only compatible with Version13. I made the following two changes in order to get working solution:

1. I changed the constructor of GdsServiceManager to create a Version13.GdsDatabase class.
2. In class Version13.GdsDatabase, I override ReadGenericResponse:
               public override GenericResponse ReadGenericResponse()
{
byte[] cryptKey = new byte[2096];

var response = ReadResponse();
while (response is CryptKeyCallbackReponse cryptResponse)
{
XdrStream.Write(IscCodes.op_crypt_key_callback);
XdrStream.WriteBuffer(cryptKey);
XdrStream.Flush();
response = ReadResponse();
}

return response as GenericResponse;
}
 
I know that this solution is just a hack but I don't know the architecture of the source code and I found no helpful source code comments. With the described changes, I was able to access the database.

I was wondering why no other user has reported a similar behavior.

I do my best to add all required information. If you need further information, feel free to contact me.

The callstack of the exception:

FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Client.Managed.Version10.GdsDatabase.ReadGenericResponse() Line 602
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Client\Managed\Version10\GdsDatabase.cs(602)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Client.Managed.Version10.GdsServiceManager.Attach(FirebirdSql.Data.Common.ServiceParameterBuffer spb, string dataSource, int port, string service) Line 73
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Client\Managed\Version10\GdsServiceManager.cs(73)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.Open() Line 114
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(114)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.QueryService(byte[] items) Line 341
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(341)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.ProcessQuery(byte[] items, System.Action<bool, object> queryResponseAction) Line 231
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(231)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.Query(byte[] items, System.Action<bool, object> resultAction) Line 200
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(200)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbService.Query(byte[] items) Line 159
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbService.cs(159)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetInfo(byte[] items) Line 90
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(90)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetInfo(int item) Line 85
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(85)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetString(int item) Line 75
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(75)
FirebirdSql.Data.FirebirdClient.dll!FirebirdSql.Data.Services.FbServerProperties.GetServerVersion() Line 40
at C:\Users\michael.wresche\source\repos\FirebirdSql.Data.FirebirdClient\Provider\src\FirebirdSql.Data.FirebirdClient\Services\FbServerProperties.cs(40)
EntityFramework.Firebird.dll!EntityFramework.Firebird.FbProviderServices.GetDbProviderManifestToken(System.Data.Common.DbConnection connection) Line 130
at C:\Users\michael.wresche\Source\Repos\FirebirdSql.Data.FirebirdClient\Provider\src\EntityFramework.Firebird\FbProviderServices.cs(130)
EntityFramework.dll!System.Data.Entity.Core.Common.DbProviderServices.GetProviderManifestToken(System.Data.Common.DbConnection connection)
EntityFramework.dll!System.Data.Entity.Utilities.DbProviderServicesExtensions.GetProviderManifestTokenChecked(System.Data.Entity.Core.Common.DbProviderServices providerServices, System.Data.Common.DbConnection connection)
mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<System.Tuple<System.Type, string, string>, string>.GetOrAdd(System.Tuple<System.Type, string, string> key, System.Func<System.Tuple<System.Type, string, string>, string> valueFactory)
EntityFramework.dll!System.Data.Entity.Utilities.DbConnectionExtensions.GetProviderInfo(System.Data.Common.DbConnection connection, out System.Data.Entity.Core.Common.DbProviderManifest providerManifest)
EntityFramework.dll!System.Data.Entity.DbModelBuilder.Build(System.Data.Common.DbConnection providerConnection)
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.CreateModel(System.Data.Entity.Internal.LazyInternalContext internalContext)
EntityFramework.dll!System.Data.Entity.Internal.RetryLazy<System.Data.Entity.Internal.LazyInternalContext, System.Data.Entity.Infrastructure.DbCompiledModel>.GetValue(System.Data.Entity.Internal.LazyInternalContext input)
EntityFramework.dll!System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
EntityFramework.dll!System.Data.Entity.Internal.InternalContext.Initialize()
EntityFramework.dll!System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(System.Type entityType)
EntityFramework.dll!System.Data.Entity.Internal.Linq.InternalSet<OTTO.IBA.Entities.DBVersion>.Initialize()
EntityFramework.dll!System.Data.Entity.Internal.Linq.InternalSet<OTTO.IBA.Entities.DBVersion>.AsNoTracking()
EntityFramework.dll!System.Data.Entity.Infrastructure.DbQuery<OTTO.IBA.Entities.DBVersion>.AsNoTracking()
OTTO.IBA.Datenbankzugriff.EntityFramework.dll!OTTO.IBA.Datenbankzugriff.EntityFramework.DAOs.DBVersionDAO.GetAktuelleDBVersion() Line 45
at C:\Projekte\Otto_IBA2\Development\Sources\App\Datenbankzugriff\OTTO.IBA.Datenbankzugriff.EntityFramework\DAOs\DBVersionDAO.cs(45)


 All   Comments   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Jiri Cincura added a comment - 29/Jun/18 08:21 PM
Thanks for very detailed bug report. Thumbs up! This is already tracked in DNET-821.

As a workaround you can hardcode the token using IManifestTokenResolver or by using DbProviderInfo and manually invoking DbModelBuilder.Build and using pre-compiled model, sadly none of these is really straightforward.