From e0615d6cca2c6a18919f922741feac8776ea49c1 Mon Sep 17 00:00:00 2001
From: Filipe GP <4021025+Eastrall@users.noreply.github.com>
Date: Tue, 25 Oct 2022 18:06:03 +0200
Subject: [PATCH 01/14] WIP: Version 4 rework
---
.editorconfig | 39 ---
EntityFrameworkCore.DataEncryption.sln | 18 +-
samples/AesSample/AesSample.csproj | 1 +
samples/AesSample/DatabaseContext.cs | 29 +-
samples/AesSample/Program.cs | 77 ++---
samples/AesSample/UserEntity.cs | 29 +-
.../Attributes/EncryptedAttribute.cs | 50 +--
.../Attributes/StorageFormat.cs | 45 +--
.../EntityFrameworkCore.DataEncryption.csproj | 20 +-
...workCore.DataEncryption.csproj.DotSettings | 2 -
.../IEncryptionProvider.cs | 76 +----
.../Internal/ConverterBuilder.cs | 206 -------------
.../Internal/ConverterBuilder`1.cs | 38 ---
.../Internal/ConverterBuilder`2.cs | 69 -----
.../Internal/EncodingExtensions.cs | 57 ----
.../Internal/EncryptionConverter.cs | 87 ++++--
.../Internal/Extensions/EncodingExtensions.cs | 56 ++++
.../Internal/IEncryptionValueConverter.cs | 23 +-
.../Internal/StandardConverters.cs | 57 ----
.../Migration/EncryptionMigrator.cs | 208 -------------
.../Migration/MigrationEncryptionProvider.cs | 105 -------
.../ModelBuilderExtensions.cs | 231 ++++++--------
.../ModelExtensions.cs | 157 +++++-----
.../PropertyBuilderExtensions.cs | 282 ++++++++---------
.../Providers/AesKeyInfo.cs | 107 ++++---
.../Providers/AesKeySize.cs | 39 ++-
.../Providers/AesProvider.cs | 255 +++++++---------
.../Context/AuthorEntity.cs | 49 ++-
.../Context/BookEntity.cs | 43 ++-
.../Context/DatabaseContext.cs | 35 ++-
.../Context/DatabaseContextFactory.cs | 91 +++---
...tyFrameworkCore.DataEncryption.Test.csproj | 14 +-
.../Helpers/DataHelper.cs | 44 +--
.../EncryptedToOriginalMigratorTest.cs | 20 --
.../Migration/MigratorBaseTest.cs | 77 -----
.../OriginalToEncryptedMigratorTest.cs | 20 --
.../Migration/V1ToV2MigratorTest.cs | 21 --
.../Providers/AesProviderTest.cs | 289 ++++++++----------
38 files changed, 1026 insertions(+), 2040 deletions(-)
delete mode 100644 .editorconfig
delete mode 100644 src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs
create mode 100644 src/EntityFrameworkCore.DataEncryption/Internal/Extensions/EncodingExtensions.cs
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs
delete mode 100644 src/EntityFrameworkCore.DataEncryption/Migration/MigrationEncryptionProvider.cs
delete mode 100644 test/EntityFrameworkCore.DataEncryption.Test/Migration/EncryptedToOriginalMigratorTest.cs
delete mode 100644 test/EntityFrameworkCore.DataEncryption.Test/Migration/MigratorBaseTest.cs
delete mode 100644 test/EntityFrameworkCore.DataEncryption.Test/Migration/OriginalToEncryptedMigratorTest.cs
delete mode 100644 test/EntityFrameworkCore.DataEncryption.Test/Migration/V1ToV2MigratorTest.cs
diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index 0d4a6fd..0000000
--- a/.editorconfig
+++ /dev/null
@@ -1,39 +0,0 @@
-[*.cs]
-indent_style = space
-indent_size = 4
-
-csharp_new_line_before_else = true
-csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_open_brace = methods, object_collection_array_initializers, control_blocks, types
-dotnet_sort_system_directives_first = true
-
-csharp_space_after_cast = false
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-
-csharp_preserve_single_line_blocks = true
-csharp_preserve_single_line_statements = false
-csharp_prefer_braces = true:warning
-
-csharp_style_var_when_type_is_apparent = true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
-
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
-csharp_preferred_modifier_order = public,private,internal,protected,static,readonly,sealed,async,override,abstract:suggestion
-
-dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
-dotnet_style_predefined_type_for_member_access = true:suggestion
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_prefer_inferred_tuple_names = true:suggestion
-csharp_prefer_simple_default_expression = true:suggestion
-
-dotnet_style_qualification_for_field = false:suggestion
-dotnet_style_qualification_for_method = false:suggestion
-dotnet_style_qualification_for_property = false:suggestion
diff --git a/EntityFrameworkCore.DataEncryption.sln b/EntityFrameworkCore.DataEncryption.sln
index fb35659..18b7c1c 100644
--- a/EntityFrameworkCore.DataEncryption.sln
+++ b/EntityFrameworkCore.DataEncryption.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.30804.86
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3EC10767-1816-46B2-A78E-9856071CCFDB}"
EndProject
@@ -22,15 +22,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{64C3
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AesSample", "samples\AesSample\AesSample.csproj", "{8AA1E576-4016-4623-96C8-90330F05F9A8}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".azure", ".azure", "{073FEA06-67CF-47F8-8CE4-2B153A7D8443}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{EEF46CDC-C438-48FC-BEF7-83AEE26C63F7}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{68558245-F605-413F-A1D9-A4F60D489D68}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{4F549FEF-C57B-4A34-A2C7-8A632762DF85}"
ProjectSection(SolutionItems) = preProject
- .azure\pipelines\azure-pipelines.yml = .azure\pipelines\azure-pipelines.yml
- EndProjectSection
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5EE4E8BE-6B15-49DB-A4A8-D2CD63D5E90C}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
+ .github\workflows\build.yml = .github\workflows\build.yml
EndProjectSection
EndProject
Global
@@ -59,8 +55,8 @@ Global
{D037F8D0-E606-4C5A-8669-DB6AAE7B056B} = {3EC10767-1816-46B2-A78E-9856071CCFDB}
{5E023B6A-0B47-4EC2-90B9-2DF998E58ADB} = {E4089551-AF4E-41B3-A6F8-2501A3BE0E0C}
{8AA1E576-4016-4623-96C8-90330F05F9A8} = {64C3D7D1-67B8-4070-AE67-C71B761535CC}
- {073FEA06-67CF-47F8-8CE4-2B153A7D8443} = {3A8D800E-77BD-44EF-82DB-C672281ECAAA}
- {68558245-F605-413F-A1D9-A4F60D489D68} = {073FEA06-67CF-47F8-8CE4-2B153A7D8443}
+ {EEF46CDC-C438-48FC-BEF7-83AEE26C63F7} = {3A8D800E-77BD-44EF-82DB-C672281ECAAA}
+ {4F549FEF-C57B-4A34-A2C7-8A632762DF85} = {EEF46CDC-C438-48FC-BEF7-83AEE26C63F7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4997BAE9-29BF-4D79-AE5E-5605E7A0F049}
diff --git a/samples/AesSample/AesSample.csproj b/samples/AesSample/AesSample.csproj
index aa33ab0..7499860 100644
--- a/samples/AesSample/AesSample.csproj
+++ b/samples/AesSample/AesSample.csproj
@@ -3,6 +3,7 @@
Exe
net5.0;net6.0
+ 10
diff --git a/samples/AesSample/DatabaseContext.cs b/samples/AesSample/DatabaseContext.cs
index 029b961..0bd48cb 100644
--- a/samples/AesSample/DatabaseContext.cs
+++ b/samples/AesSample/DatabaseContext.cs
@@ -1,25 +1,24 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.DataEncryption;
-namespace AesSample
+namespace AesSample;
+
+public class DatabaseContext : DbContext
{
- public class DatabaseContext : DbContext
- {
- private readonly IEncryptionProvider _encryptionProvider;
+ private readonly IEncryptionProvider _encryptionProvider;
- public DbSet Users { get; set; }
+ public DbSet Users { get; set; }
- public DatabaseContext(DbContextOptions options, IEncryptionProvider encryptionProvider)
- : base(options)
- {
- _encryptionProvider = encryptionProvider;
- }
+ public DatabaseContext(DbContextOptions options, IEncryptionProvider encryptionProvider)
+ : base(options)
+ {
+ _encryptionProvider = encryptionProvider;
+ }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.UseEncryption(_encryptionProvider);
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.UseEncryption(_encryptionProvider);
- base.OnModelCreating(modelBuilder);
- }
+ base.OnModelCreating(modelBuilder);
}
}
diff --git a/samples/AesSample/Program.cs b/samples/AesSample/Program.cs
index c39313d..3ecc8d9 100644
--- a/samples/AesSample/Program.cs
+++ b/samples/AesSample/Program.cs
@@ -4,53 +4,54 @@
using System.Linq;
using System.Security;
-namespace AesSample
+namespace AesSample;
+
+static class Program
{
- static class Program
+ static void Main()
{
- static void Main()
- {
- var options = new DbContextOptionsBuilder()
- .UseInMemoryDatabase(databaseName: "MyInMemoryDatabase")
- .Options;
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(databaseName: "MyInMemoryDatabase")
+ .Options;
- // AES key randomly generated at each run.
- byte[] encryptionKey = AesProvider.GenerateKey(AesKeySize.AES256Bits).Key;
- var encryptionProvider = new AesProvider(encryptionKey);
+ // AES key randomly generated at each run.
+ AesKeyInfo keyInfo = AesProvider.GenerateKey(AesKeySize.AES256Bits);
+ byte[] encryptionKey = keyInfo.Key;
+ byte[] encryptionIV = keyInfo.IV;
+ var encryptionProvider = new AesProvider(encryptionKey, encryptionIV);
- using var context = new DatabaseContext(options, encryptionProvider);
+ using var context = new DatabaseContext(options, encryptionProvider);
- var user = new UserEntity
- {
- FirstName = "John",
- LastName = "Doe",
- Email = "john@doe.com",
- Password = BuildPassword(),
- };
+ var user = new UserEntity
+ {
+ FirstName = "John",
+ LastName = "Doe",
+ Email = "john@doe.com",
+ //Password = BuildPassword(),
+ };
- context.Users.Add(user);
- context.SaveChanges();
+ context.Users.Add(user);
+ context.SaveChanges();
- Console.WriteLine($"Users count: {context.Users.Count()}");
+ Console.WriteLine($"Users count: {context.Users.Count()}");
- user = context.Users.First();
+ user = context.Users.First();
- Console.WriteLine($"User: {user.FirstName} {user.LastName} - {user.Email} ({user.Password.Length})");
- }
+ Console.WriteLine($"User: {user.FirstName} {user.LastName} - {user.Email}");
+ }
- static SecureString BuildPassword()
- {
- SecureString result = new();
- result.AppendChar('L');
- result.AppendChar('e');
- result.AppendChar('t');
- result.AppendChar('M');
- result.AppendChar('e');
- result.AppendChar('I');
- result.AppendChar('n');
- result.AppendChar('!');
- result.MakeReadOnly();
- return result;
- }
+ static SecureString BuildPassword()
+ {
+ SecureString result = new();
+ result.AppendChar('L');
+ result.AppendChar('e');
+ result.AppendChar('t');
+ result.AppendChar('M');
+ result.AppendChar('e');
+ result.AppendChar('I');
+ result.AppendChar('n');
+ result.AppendChar('!');
+ result.MakeReadOnly();
+ return result;
}
}
diff --git a/samples/AesSample/UserEntity.cs b/samples/AesSample/UserEntity.cs
index 671890b..564dfc3 100644
--- a/samples/AesSample/UserEntity.cs
+++ b/samples/AesSample/UserEntity.cs
@@ -3,24 +3,23 @@
using System.ComponentModel.DataAnnotations.Schema;
using System.Security;
-namespace AesSample
+namespace AesSample;
+
+public class UserEntity
{
- public class UserEntity
- {
- [Key]
- [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public Guid Id { get; set; }
+ [Key]
+ [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public Guid Id { get; set; }
- [Required]
- public string FirstName { get; set; }
+ [Required]
+ public string FirstName { get; set; }
- [Required]
- public string LastName { get; set; }
+ [Required]
+ public string LastName { get; set; }
- [Required]
- [Encrypted]
- public string Email { get; set; }
+ [Required]
+ [Encrypted]
+ public string Email { get; set; }
- public SecureString Password { get; set; }
- }
+ //public SecureString Password { get; set; }
}
diff --git a/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs b/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs
index c76c5cf..2e03135 100644
--- a/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Attributes/EncryptedAttribute.cs
@@ -1,32 +1,32 @@
-namespace System.ComponentModel.DataAnnotations
+namespace System.ComponentModel.DataAnnotations;
+
+///
+/// Specifies that the data field value should be encrypted.
+///
+[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
+public sealed class EncryptedAttribute : Attribute
{
///
- /// Specifies that the data field value should be encrypted.
+ /// Returns the storage format for the database value.
///
- [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
- public sealed class EncryptedAttribute : Attribute
- {
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The storage format.
- ///
- public EncryptedAttribute(StorageFormat format)
- {
- Format = format;
- }
+ public StorageFormat Format { get; }
- ///
- /// Initializes a new instance of the class.
- ///
- public EncryptedAttribute() : this(StorageFormat.Default)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The storage format.
+ ///
+ public EncryptedAttribute(StorageFormat format)
+ {
+ Format = format;
+ }
- ///
- /// Returns the storage format for the database value.
- ///
- public StorageFormat Format { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public EncryptedAttribute()
+ : this(StorageFormat.Default)
+ {
}
}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs b/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs
index f948d12..e83f2aa 100644
--- a/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Attributes/StorageFormat.cs
@@ -1,26 +1,27 @@
-namespace System.ComponentModel.DataAnnotations
+namespace System.ComponentModel.DataAnnotations;
+
+///
+/// Represents the storage format for an encrypted value.
+///
+public enum StorageFormat
{
///
- /// Represents the storage format for an encrypted value.
+ /// The format is determined by the model data type.
///
- public enum StorageFormat
- {
- ///
- /// The format is determined by the model data type.
- ///
- Default,
- ///
- /// The value is stored in binary.
- ///
- Binary,
- ///
- /// The value is stored in a Base64-encoded string.
- ///
- ///
- /// NB: If the source property is a ,
- /// and no encryption provider is configured,
- /// the string will not be modified.
- ///
- Base64,
- }
+ Default,
+
+ ///
+ /// The value is stored in binary.
+ ///
+ Binary,
+
+ ///
+ /// The value is stored in a Base64-encoded string.
+ ///
+ ///
+ /// NB: If the source property is a ,
+ /// and no encryption provider is configured,
+ /// the string will not be modified.
+ ///
+ Base64,
}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj b/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj
index 95d89c6..3ce311c 100644
--- a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj
+++ b/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj
@@ -2,12 +2,12 @@
netstandard2.0;net6.0;net5.0
- 9.0
+ 10.0
true
EntityFrameworkCore.DataEncryption
Microsoft.EntityFrameworkCore.DataEncryption
- true
- 3.0.1
+ true
+ 4.0.0
Filipe GOMES PEIXOTO
EntityFrameworkCore.DataEncryption
https://github.com/Eastrall/EntityFrameworkCore.DataEncryption
@@ -17,7 +17,7 @@
true
entity-framework-core, extensions, dotnet-core, dotnet, encryption, fluent-api
icon.png
- Filipe GOMES PEIXOTO © 2019 - 2021
+ Filipe GOMES PEIXOTO © 2019 - 2022
A plugin for Microsoft.EntityFrameworkCore to add support of encrypted fields using built-in or custom encryption providers.
LICENSE
Add support for .NET 5 and .NET 6
@@ -25,7 +25,7 @@
- Auto
+ Auto
@@ -43,15 +43,15 @@
-
+
True
- True
- \
+ True
+ \
@@ -62,4 +62,8 @@
+
+
+
+
diff --git a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings b/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings
deleted file mode 100644
index 6162834..0000000
--- a/src/EntityFrameworkCore.DataEncryption/EntityFrameworkCore.DataEncryption.csproj.DotSettings
+++ /dev/null
@@ -1,2 +0,0 @@
-
- CSharp90
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs b/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs
index 49a8c6d..77c9eb9 100644
--- a/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs
+++ b/src/EntityFrameworkCore.DataEncryption/IEncryptionProvider.cs
@@ -1,67 +1,21 @@
-using System;
-using System.IO;
+namespace Microsoft.EntityFrameworkCore.DataEncryption;
-namespace Microsoft.EntityFrameworkCore.DataEncryption
+///
+/// Provides a mechanism to encrypt and decrypt data.
+///
+public interface IEncryptionProvider
{
///
- /// Provides a mechanism for implementing a custom encryption provider.
+ /// Encrypts the given input byte array.
///
- public interface IEncryptionProvider
- {
- ///
- /// Encrypts a value.
- ///
- ///
- /// The type of data stored in the database.
- ///
- ///
- /// The type of value stored in the model.
- ///
- ///
- /// Input data to encrypt.
- ///
- ///
- /// Function which converts the model value to a byte array.
- ///
- ///
- /// Function which encodes the value for storing the the database.
- ///
- ///
- /// Encrypted data.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- ///
- TStore Encrypt(TModel dataToEncrypt, Func converter, Func encoder);
+ /// Input to encrypt.
+ /// Encrypted input.
+ byte[] Encrypt(byte[] input);
- ///
- /// Decrypts a value.
- ///
- ///
- /// The type of data stored in the database.
- ///
- ///
- /// The type of value stored in the model.
- ///
- ///
- /// Encrypted data to decrypt.
- ///
- ///
- /// Function which converts the stored data to a byte array.
- ///
- ///
- /// Function which converts the decrypted to the return value.
- ///
- ///
- /// Decrypted data.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- ///
- TModel Decrypt(TStore dataToDecrypt, Func decoder, Func converter);
- }
+ ///
+ /// Decrypts the given input byte array.
+ ///
+ /// Input to decrypt.
+ /// Decrypted input.
+ byte[] Decrypt(byte[] input);
}
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs b/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs
deleted file mode 100644
index 28144e6..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder.cs
+++ /dev/null
@@ -1,206 +0,0 @@
-using System;
-using System.IO;
-using System.Security;
-using System.Text;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// Utilities for building value converters.
- ///
- public static class ConverterBuilder
- {
- ///
- /// Builds a converter for a property with a custom model type.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// The function used to decode the model type to a byte array.
- ///
- ///
- /// The function used to encode a byte array to the model type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- ///
- public static ConverterBuilder From(
- this IEncryptionProvider encryptionProvider,
- Func decoder,
- Func encoder)
- {
- if (decoder is null)
- {
- throw new ArgumentNullException(nameof(decoder));
- }
-
- if (encoder is null)
- {
- throw new ArgumentNullException(nameof(encoder));
- }
-
- return new ConverterBuilder(encryptionProvider, decoder, encoder);
- }
-
- ///
- /// Builds a converter for a binary property.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// An instance.
- ///
- public static ConverterBuilder FromBinary(this IEncryptionProvider encryptionProvider)
- {
- return new ConverterBuilder(encryptionProvider, b => b, StandardConverters.StreamToBytes);
- }
-
- ///
- /// Builds a converter for a string property.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// An instance.
- ///
- public static ConverterBuilder FromString(this IEncryptionProvider encryptionProvider)
- {
- return new ConverterBuilder(encryptionProvider, Encoding.UTF8.GetBytes, StandardConverters.StreamToString);
- }
-
- ///
- /// Builds a converter for a property.
- ///
- ///
- /// The , if any.
- ///
- ///
- /// An instance.
- ///
- public static ConverterBuilder FromSecureString(this IEncryptionProvider encryptionProvider)
- {
- return new ConverterBuilder(encryptionProvider, Encoding.UTF8.GetBytes, StandardConverters.StreamToSecureString);
- }
-
- ///
- /// Specifies that the property should be stored in the database using a custom format.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The store type.
- ///
- ///
- /// The representing the model type.
- ///
- ///
- /// The function used to decode the store type into a byte array.
- ///
- ///
- /// The function used to encode a byte array into the store type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- /// -or-
- /// is .
- /// -or-
- /// is .
- ///
- ///
- /// is not a supported type.
- ///
- public static ConverterBuilder To(
- ConverterBuilder modelType,
- Func decoder,
- Func encoder)
- {
- if (modelType.IsEmpty)
- {
- throw new ArgumentNullException(nameof(modelType));
- }
-
- if (decoder is null)
- {
- throw new ArgumentNullException(nameof(decoder));
- }
-
- if (encoder is null)
- {
- throw new ArgumentNullException(nameof(encoder));
- }
-
- return new ConverterBuilder(modelType, decoder, encoder);
- }
-
- ///
- /// Specifies that the property should be stored in the database in binary.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The representing the model type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- ///
- ///
- /// is not a supported type.
- ///
- public static ConverterBuilder ToBinary(this ConverterBuilder modelType)
- {
- if (modelType.IsEmpty)
- {
- throw new ArgumentNullException(nameof(modelType));
- }
-
- return new ConverterBuilder(modelType, b => b, StandardConverters.StreamToBytes);
- }
-
- ///
- /// Specifies that the property should be stored in the database in a Base64-encoded string.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The representing the model type.
- ///
- ///
- /// An instance.
- ///
- ///
- /// is .
- ///
- ///
- /// is not a supported type.
- ///
- public static ConverterBuilder ToBase64(this ConverterBuilder modelType)
- {
- if (modelType.IsEmpty)
- {
- throw new ArgumentNullException(nameof(modelType));
- }
-
- return new ConverterBuilder(modelType, Convert.FromBase64String, StandardConverters.StreamToBase64String);
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs b/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs
deleted file mode 100644
index c0ae461..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`1.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// A converter builder class which has the model type specified.
- ///
- ///
- /// The model type.
- ///
- public readonly struct ConverterBuilder
- {
- internal ConverterBuilder(IEncryptionProvider encryptionProvider, Func decoder, Func encoder)
- {
- Debug.Assert(decoder is not null);
- Debug.Assert(encoder is not null);
-
- EncryptionProvider = encryptionProvider;
- Decoder = decoder;
- Encoder = encoder;
- }
-
- private readonly IEncryptionProvider EncryptionProvider;
- private readonly Func Decoder;
- private readonly Func Encoder;
-
- internal bool IsEmpty => Decoder is null || Encoder is null;
-
- internal void Deconstruct(out IEncryptionProvider encryptionProvider, out Func decoder, out Func encoder)
- {
- encryptionProvider = EncryptionProvider;
- decoder = Decoder;
- encoder = Encoder;
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs b/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs
deleted file mode 100644
index dddda9f..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/ConverterBuilder`2.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.IO;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- ///
- /// A converter builder class which has both the model type and store type specified.
- ///
- ///
- /// The model type.
- ///
- ///
- /// The store type.
- ///
- public readonly struct ConverterBuilder
- {
- internal ConverterBuilder(ConverterBuilder modelType, Func decoder, Func encoder)
- {
- Debug.Assert(!modelType.IsEmpty);
- Debug.Assert(decoder is not null);
- Debug.Assert(encoder is not null);
-
- ModelType = modelType;
- Decoder = decoder;
- Encoder = encoder;
- }
-
- private readonly ConverterBuilder ModelType;
- private readonly Func Decoder;
- private readonly Func Encoder;
-
- ///
- /// Builds the value converter.
- ///
- ///
- /// The mapping hints to use, if any.
- ///
- ///
- /// The .
- ///
- public ValueConverter Build(ConverterMappingHints mappingHints = null)
- {
- var (encryptionProvider, modelDecoder, modelEncoder) = ModelType;
- var storeDecoder = Decoder;
- var storeEncoder = Encoder;
-
- if (modelDecoder is null || modelEncoder is null || storeDecoder is null || storeEncoder is null)
- {
- return null;
- }
-
- if (encryptionProvider is null)
- {
- return new ValueConverter(
- m => storeEncoder(StandardConverters.BytesToStream(modelDecoder(m))),
- s => modelEncoder(StandardConverters.BytesToStream(storeDecoder(s))),
- mappingHints);
- }
-
- return new EncryptionConverter(
- encryptionProvider,
- m => encryptionProvider.Encrypt(m, modelDecoder, storeEncoder),
- s => encryptionProvider.Decrypt(s, storeDecoder, modelEncoder),
- mappingHints);
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs b/src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs
deleted file mode 100644
index 7cf1634..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/EncodingExtensions.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Security;
-using System.Text;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- internal static class EncodingExtensions
- {
- internal static byte[] GetBytes(this Encoding encoding, SecureString value)
- {
- if (encoding is null)
- {
- throw new ArgumentNullException(nameof(encoding));
- }
-
- if (value is null || value.Length == 0)
- {
- return Array.Empty();
- }
-
- IntPtr valuePtr = IntPtr.Zero;
- try
- {
- valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
- if (valuePtr == IntPtr.Zero)
- {
- return Array.Empty();
- }
-
- unsafe
- {
- char* chars = (char*)valuePtr;
- Debug.Assert(chars != null);
-
- int byteCount = encoding.GetByteCount(chars, value.Length);
-
- var result = new byte[byteCount];
- fixed (byte* bytes = result)
- {
- encoding.GetBytes(chars, value.Length, bytes, byteCount);
- }
-
- return result;
- }
- }
- finally
- {
- if (valuePtr != IntPtr.Zero)
- {
- Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs b/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs
index 20794f2..8dbd201 100644
--- a/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Internal/EncryptionConverter.cs
@@ -1,28 +1,79 @@
-using System;
-using System.Linq.Expressions;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.Security;
+using System.Text;
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
+namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal;
+
+///
+/// Defines the internal encryption converter for string values.
+///
+///
+///
+internal sealed class EncryptionConverter : ValueConverter, IEncryptionValueConverter
{
+ public IEncryptionProvider EncryptionProvider { get; }
+
///
- /// Defines the internal encryption converter for string values.
+ /// Creates a new instance.
///
- internal sealed class EncryptionConverter : ValueConverter, IEncryptionValueConverter
+ public EncryptionConverter(IEncryptionProvider encryptionProvider, StorageFormat storageFormat, ConverterMappingHints mappingHints = null)
+ : base(
+ x => Encrypt(x, encryptionProvider, storageFormat),
+ x => Decrypt(x, encryptionProvider, storageFormat),
+ mappingHints)
+ {
+ EncryptionProvider = encryptionProvider;
+ }
+
+ private static TOutput Encrypt(TInput input, IEncryptionProvider encryptionProvider, StorageFormat storageFormat)
{
- ///
- /// Creates a new instance.
- ///
- public EncryptionConverter(
- IEncryptionProvider encryptionProvider,
- Expression> convertToProviderExpression,
- Expression> convertFromProviderExpression,
- ConverterMappingHints mappingHints = null)
- : base(convertToProviderExpression, convertFromProviderExpression, mappingHints)
+ byte[] inputData = input switch
+ {
+ string => Encoding.UTF8.GetBytes(input.ToString()),
+ byte[] => input as byte[],
+ SecureString => null,
+ _ => null,
+ };
+
+ byte[] encryptedRawBytes = encryptionProvider.Encrypt(inputData);
+
+ object encryptedData = storageFormat switch
+ {
+ StorageFormat.Default or StorageFormat.Base64 => Convert.ToBase64String(encryptedRawBytes),
+ _ => encryptedRawBytes
+ };
+
+ return (TOutput)Convert.ChangeType(encryptedData, typeof(TOutput));
+ }
+
+ private static TModel Decrypt(TProvider input, IEncryptionProvider encryptionProvider, StorageFormat storageFormat)
+ {
+ Type destinationType = typeof(TModel);
+ byte[] inputData = storageFormat switch
+ {
+ StorageFormat.Default or StorageFormat.Base64 => Convert.FromBase64String(input.ToString()),
+ _ => input as byte[]
+ };
+
+ byte[] decryptedRawBytes = encryptionProvider.Decrypt(inputData);
+
+ object decryptedData = null;
+
+ if (destinationType == typeof(string))
+ {
+ decryptedData = Encoding.UTF8.GetString(decryptedRawBytes).Trim('\0');
+ }
+ else if (destinationType == typeof(byte[]))
+ {
+ decryptedData = decryptedRawBytes;
+ }
+ else if (destinationType == typeof(SecureString))
{
- EncryptionProvider = encryptionProvider;
+ // TODO
}
- ///
- public IEncryptionProvider EncryptionProvider { get; }
+ return (TModel)Convert.ChangeType(decryptedData, typeof(TModel));
}
}
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/Extensions/EncodingExtensions.cs b/src/EntityFrameworkCore.DataEncryption/Internal/Extensions/EncodingExtensions.cs
new file mode 100644
index 0000000..840de71
--- /dev/null
+++ b/src/EntityFrameworkCore.DataEncryption/Internal/Extensions/EncodingExtensions.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+
+namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal.Extensions;
+
+internal static class EncodingExtensions
+{
+ internal static byte[] GetBytes(this Encoding encoding, SecureString value)
+ {
+ if (encoding is null)
+ {
+ throw new ArgumentNullException(nameof(encoding));
+ }
+
+ if (value is null || value.Length == 0)
+ {
+ return Array.Empty();
+ }
+
+ IntPtr valuePtr = IntPtr.Zero;
+ try
+ {
+ valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
+ if (valuePtr == IntPtr.Zero)
+ {
+ return Array.Empty();
+ }
+
+ unsafe
+ {
+ char* chars = (char*)valuePtr;
+ Debug.Assert(chars != null);
+
+ int byteCount = encoding.GetByteCount(chars, value.Length);
+
+ var result = new byte[byteCount];
+ fixed (byte* bytes = result)
+ {
+ encoding.GetBytes(chars, value.Length, bytes, byteCount);
+ }
+
+ return result;
+ }
+ }
+ finally
+ {
+ if (valuePtr != IntPtr.Zero)
+ {
+ Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
+ }
+ }
+ }
+}
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs b/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs
index f54fa82..1373715 100644
--- a/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs
+++ b/src/EntityFrameworkCore.DataEncryption/Internal/IEncryptionValueConverter.cs
@@ -1,18 +1,17 @@
using System;
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
+namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal;
+
+///
+/// Interface for an encryption value converter.
+///
+public interface IEncryptionValueConverter
{
///
- /// Interface for an encryption value converter.
+ /// Returns the encryption provider, if any.
///
- public interface IEncryptionValueConverter
- {
- ///
- /// Returns the encryption provider, if any.
- ///
- ///
- /// The for this converter, if any.
- ///
- IEncryptionProvider EncryptionProvider { get; }
- }
+ ///
+ /// The for this converter, if any.
+ ///
+ IEncryptionProvider EncryptionProvider { get; }
}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs b/src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs
deleted file mode 100644
index 0a5df4d..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Internal/StandardConverters.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System;
-using System.IO;
-using System.Security;
-using System.Text;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Internal
-{
- internal static class StandardConverters
- {
- internal static Stream BytesToStream(byte[] bytes) => new MemoryStream(bytes);
-
- internal static byte[] StreamToBytes(Stream stream)
- {
- if (stream is MemoryStream ms)
- {
- return ms.ToArray();
- }
-
- using var output = new MemoryStream();
- stream.CopyTo(output);
- return output.ToArray();
- }
-
- internal static string StreamToBase64String(Stream stream) => Convert.ToBase64String(StreamToBytes(stream));
-
- internal static string StreamToString(Stream stream)
- {
- using var reader = new StreamReader(stream, Encoding.UTF8);
- return reader.ReadToEnd().Trim('\0');
- }
-
- internal static SecureString StreamToSecureString(Stream stream)
- {
- using var reader = new StreamReader(stream, Encoding.UTF8);
-
- var result = new SecureString();
- var buffer = new char[100];
- while (!reader.EndOfStream)
- {
- var charsRead = reader.Read(buffer, 0, buffer.Length);
- if (charsRead != 0)
- {
- for (int index = 0; index < charsRead; index++)
- {
- char c = buffer[index];
- if (c != '\0')
- {
- result.AppendChar(c);
- }
- }
- }
- }
-
- return result;
- }
- }
-}
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs b/src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs
deleted file mode 100644
index 333de7d..0000000
--- a/src/EntityFrameworkCore.DataEncryption/Migration/EncryptionMigrator.cs
+++ /dev/null
@@ -1,208 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.EntityFrameworkCore.DataEncryption.Internal;
-using Microsoft.EntityFrameworkCore.DataEncryption.Providers;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.Logging;
-
-namespace Microsoft.EntityFrameworkCore.DataEncryption.Migration
-{
- ///
- /// Utilities for migrating encrypted data from one provider to another.
- ///
- ///
- /// To migrate from v1 to v2 of :
- ///
- /// var sourceProvider = new AesProvider(key, iv);
- /// var destinationProvider = new AesProvider(key);
- /// var migrationProvider = new MigrationEncryptionProvider(sourceProvider, destinationProvider);
- /// await using var migrationContext = new DatabaseContext(options, migrationProvider);
- /// await migrationContext.MigrateAsync(logger, cancellationToken);
- ///
- ///
- public static class EncryptionMigrator
- {
- private static readonly MethodInfo SetMethod = typeof(DbContext).GetMethod(nameof(DbContext.Set));
-
- private static IQueryable