Skip to content

Commit 0e8ae37

Browse files
authored
Merge | SqlCommand Encryption Methods (#3676)
1 parent fc514f6 commit 0e8ae37

File tree

9 files changed

+1656
-2704
lines changed

9 files changed

+1656
-2704
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,9 @@
585585
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.cs">
586586
<Link>Microsoft\Data\SqlClient\SqlCommand.cs</Link>
587587
</Compile>
588+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.Encryption.cs">
589+
<Link>Microsoft\Data\SqlClient\SqlCommand.Encryption.cs</Link>
590+
</Compile>
588591
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.NonQuery.cs">
589592
<Link>Microsoft\Data\SqlClient\SqlCommand.NonQuery.cs</Link>
590593
</Compile>

src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlCommand.netcore.cs

Lines changed: 98 additions & 1412 deletions
Large diffs are not rendered by default.

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,9 @@
756756
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.cs">
757757
<Link>Microsoft\Data\SqlClient\SqlCommand.cs</Link>
758758
</Compile>
759+
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.Encryption.cs">
760+
<Link>Microsoft\Data\SqlClient\SqlCommand.Encryption.cs</Link>
761+
</Compile>
759762
<Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlClient\SqlCommand.NonQuery.cs">
760763
<Link>Microsoft\Data\SqlClient\SqlCommand.NonQuery.cs</Link>
761764
</Compile>

src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlCommand.netfx.cs

Lines changed: 0 additions & 1281 deletions
Large diffs are not rendered by default.

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/EnclaveDelegate.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ namespace Microsoft.Data.SqlClient
1313
/// <summary>
1414
/// A delegate for communicating with secure enclave
1515
/// </summary>
16+
// @TODO: This isn't a delegate... it's a utility class
1617
internal sealed partial class EnclaveDelegate
1718
{
1819
private static readonly SqlAeadAes256CbcHmac256Factory s_sqlAeadAes256CbcHmac256Factory = new SqlAeadAes256CbcHmac256Factory();

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.Encryption.cs

Lines changed: 1407 additions & 0 deletions
Large diffs are not rendered by default.

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlCommand.cs

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System;
6+
using System.Collections.Concurrent;
67
using System.Collections.Generic;
78
using System.ComponentModel;
89
using System.Data;
@@ -40,7 +41,45 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
4041
#endregion
4142

4243
#region Fields
43-
44+
#region Test-Only Behavior Overrides
45+
#if DEBUG
46+
/// <summary>
47+
/// Force the client to sleep during sp_describe_parameter_encryption in the function TryFetchInputParameterEncryptionInfo.
48+
/// </summary>
49+
private static bool _sleepDuringTryFetchInputParameterEncryptionInfo = false;
50+
51+
/// <summary>
52+
/// Force the client to sleep during sp_describe_parameter_encryption in the function RunExecuteReaderTds.
53+
/// </summary>
54+
private static bool _sleepDuringRunExecuteReaderTdsForSpDescribeParameterEncryption = false;
55+
56+
/// <summary>
57+
/// Force the client to sleep during sp_describe_parameter_encryption after ReadDescribeEncryptionParameterResults.
58+
/// </summary>
59+
private static bool _sleepAfterReadDescribeEncryptionParameterResults = false;
60+
61+
/// <summary>
62+
/// Internal flag for testing purposes that forces all queries to internally end async calls.
63+
/// </summary>
64+
private static bool _forceInternalEndQuery = false;
65+
66+
/// <summary>
67+
/// Internal flag for testing purposes that forces one RetryableEnclaveQueryExecutionException during GenerateEnclavePackage
68+
/// </summary>
69+
private static bool _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false;
70+
#endif
71+
#endregion
72+
73+
// @TODO: Make property - non-private fields are bad
74+
// @TODO: Rename to match naming convention _enclavePackage
75+
internal EnclavePackage enclavePackage = null;
76+
77+
// @TODO: Make property - non-private fields are bad (this should be read-only externally)
78+
internal ConcurrentDictionary<int, SqlTceCipherInfoEntry> keysToBeSentToEnclave;
79+
80+
// @TODO: Make property - non-private fields are bad (this can be read-only externally)
81+
internal bool requiresEnclaveComputations = false;
82+
4483
// @TODO: Make property - non-private fields are bad
4584
internal SqlDependency _sqlDep;
4685

@@ -75,7 +114,7 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
75114
/// false. This may also be used to set other behavior which overrides connection level
76115
/// setting.
77116
/// </summary>
78-
// @TODO: Make auto-property
117+
// @TODO: Make auto-property, also make nullable.
79118
private SqlCommandColumnEncryptionSetting _columnEncryptionSetting =
80119
SqlCommandColumnEncryptionSetting.UseConnectionSetting;
81120

@@ -94,6 +133,25 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
94133
/// </summary>
95134
private CommandType _commandType;
96135

136+
/// <summary>
137+
/// This variable is used to keep track of which RPC batch's results are being read when reading the results of
138+
/// describe parameter encryption RPC requests in BatchRPCMode.
139+
/// </summary>
140+
// @TODO: Rename to match naming conventions
141+
private int _currentlyExecutingDescribeParameterEncryptionRPC;
142+
143+
/// <summary>
144+
/// Per-command custom providers. It can be provided by the user and can be set more than
145+
/// once.
146+
/// </summary>
147+
private IReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _customColumnEncryptionKeyStoreProviders;
148+
149+
// @TODO: Rename to indicate that this is for enclave stuff, I think...
150+
private byte[] customData = null;
151+
152+
// @TODO: Rename to indicate that this is for enclave stuff. Or just get rid of it and use the length of customData if possible.
153+
private int customDataLength = 0;
154+
97155
/// <summary>
98156
/// By default, the cmd object is visible on the design surface (i.e. VS7 Server Tray) to
99157
/// limit the number of components that clutter the design surface, when the DataAdapter
@@ -102,20 +160,23 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
102160
/// </summary>
103161
// @TODO: Make auto-property
104162
private bool _designTimeInvisible;
105-
106-
/// <summary>
107-
/// Current state of preparation of the command.
108-
/// By default, assume the user is not sharing a connection so the command has not been prepared.
109-
/// </summary>
110-
private EXECTYPE _execType = EXECTYPE.UNPREPARED;
111163

112164
/// <summary>
113165
/// True if the user changes the command text or number of parameters after the command has
114166
/// already prepared.
115167
/// </summary>
116168
// @TODO: Consider renaming "_IsUserDirty"
117169
private bool _dirty = false;
118-
170+
171+
/// <summary>
172+
/// Current state of preparation of the command.
173+
/// By default, assume the user is not sharing a connection so the command has not been prepared.
174+
/// </summary>
175+
private EXECTYPE _execType = EXECTYPE.UNPREPARED;
176+
177+
// @TODO: Rename to match naming conventions _enclaveAttestationParameters
178+
private SqlEnclaveAttestationParameters enclaveAttestationParameters = null;
179+
119180
/// <summary>
120181
/// On 8.0 and above the Prepared state cannot be left. Once a command is prepared it will
121182
/// always be prepared. A change in parameters, command text, etc (IsDirty) automatically
@@ -183,6 +244,22 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
183244
// @TODO: Use int? and replace -1 usage with null
184245
private int _rowsAffected = -1;
185246

247+
/// <summary>
248+
/// number of rows affected by sp_describe_parameter_encryption.
249+
/// </summary>
250+
// @TODO: Use int? and replace -1 usage with null
251+
// @TODO: This is only used for debug asserts?
252+
// @TODO: Rename to drop Sp
253+
private int _rowsAffectedBySpDescribeParameterEncryption = -1;
254+
255+
/// <summary>
256+
/// RPC for tracking execution of sp_describe_parameter_encryption.
257+
/// </summary>
258+
private _SqlRPC _rpcForEncryption = null;
259+
260+
// @TODO: Rename to match naming convention
261+
private _SqlRPC[] _sqlRPCParameterEncryptionReqArray;
262+
186263
/// <summary>
187264
/// TDS session the current instance is using.
188265
/// </summary>
@@ -204,7 +281,14 @@ public sealed partial class SqlCommand : DbCommand, ICloneable
204281
/// DbDataAdapter.
205282
/// </summary>
206283
private UpdateRowSource _updatedRowSource = UpdateRowSource.Both;
207-
284+
285+
/// <summary>
286+
/// Indicates if the column encryption setting was set at-least once in the batch rpc mode,
287+
/// when using AddBatchCommand.
288+
/// </summary>
289+
// @TODO: can be replaced by using nullable for _columnEncryptionSetting.
290+
private bool _wasBatchModeColumnEncryptionSettingSetOnce;
291+
208292
#endregion
209293

210294
#region Constructors
@@ -624,9 +708,15 @@ public override UpdateRowSource UpdatedRowSource
624708
#endregion
625709

626710
#region Internal/Protected/Private Properties
627-
711+
712+
internal bool HasColumnEncryptionKeyStoreProvidersRegistered
713+
{
714+
get => _customColumnEncryptionKeyStoreProviders?.Count > 0;
715+
}
716+
628717
internal bool InPrepare => _inPrepare;
629718

719+
// @TODO: Rename RowsAffectedInternal or
630720
internal int InternalRecordsAffected
631721
{
632722
get => _rowsAffected;
@@ -643,9 +733,36 @@ internal int InternalRecordsAffected
643733
}
644734
}
645735

736+
/// <summary>
737+
/// A flag to indicate if we have in-progress describe parameter encryption RPC requests.
738+
/// Reset to false when completed.
739+
/// </summary>
740+
// @TODO: Rename to match naming conventions
741+
internal bool IsDescribeParameterEncryptionRPCCurrentlyInProgress { get; private set; }
742+
646743
// @TODO: Rename to match conventions.
647744
internal int ObjectID { get; } = Interlocked.Increment(ref _objectTypeCount);
648745

746+
/// <summary>
747+
/// Get or add to the number of records affected by SpDescribeParameterEncryption.
748+
/// The below line is used only for debug asserts and not exposed publicly or impacts functionality otherwise.
749+
/// </summary>
750+
internal int RowsAffectedByDescribeParameterEncryption
751+
{
752+
get => _rowsAffectedBySpDescribeParameterEncryption;
753+
set
754+
{
755+
if (_rowsAffectedBySpDescribeParameterEncryption == -1)
756+
{
757+
_rowsAffectedBySpDescribeParameterEncryption = value;
758+
}
759+
else if (value > 0)
760+
{
761+
_rowsAffectedBySpDescribeParameterEncryption += value;
762+
}
763+
}
764+
}
765+
649766
internal SqlStatistics Statistics
650767
{
651768
get
@@ -768,6 +885,20 @@ private bool IsDirty
768885
private bool IsSimpleTextQuery => CommandType is CommandType.Text &&
769886
(_parameters is null || _parameters.Count == 0);
770887

888+
private bool ShouldCacheEncryptionMetadata
889+
{
890+
// @TODO: Should we check for null on _activeConnection?
891+
get => !requiresEnclaveComputations || _activeConnection.Parser.AreEnclaveRetriesSupported;
892+
}
893+
894+
private bool ShouldUseEnclaveBasedWorkflow
895+
{
896+
// @TODO: I'm pretty sure the or'd condition is used in several places. We could factor that out.
897+
get => (!string.IsNullOrWhiteSpace(_activeConnection.EnclaveAttestationUrl) ||
898+
_activeConnection.AttestationProtocol is SqlConnectionAttestationProtocol.None) &&
899+
IsColumnEncryptionEnabled;
900+
}
901+
771902
#endregion
772903

773904
#region Public/Internal Methods

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,7 @@ internal string GetPrefixedParameterName()
10501050
/// </summary>
10511051
/// <param name="builder"></param>
10521052
/// <param name="rawParameterName"></param>
1053+
// @TODO: This is only used in SqlCommand, and literally just adds a '@' at the beginning. This belongs in SqlCommand, without the append logic.
10531054
internal static void AppendPrefixedParameterName(StringBuilder builder, string rawParameterName)
10541055
{
10551056
if (!string.IsNullOrEmpty(rawParameterName))

src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlSecurityUtility.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ internal static void DecryptSymmetricKey(SqlCipherMetadata md, SqlConnection con
262262
/// </summary>
263263
internal static void DecryptSymmetricKey(SqlTceCipherInfoEntry sqlTceCipherInfoEntry, out SqlClientSymmetricKey sqlClientSymmetricKey, out SqlEncryptionKeyInfo encryptionkeyInfoChosen, SqlConnection connection, SqlCommand command)
264264
{
265+
Debug.Assert(connection is not null, "Connection should not be null.");
265266
Debug.Assert(sqlTceCipherInfoEntry is not null, "sqlTceCipherInfoEntry should not be null in DecryptSymmetricKey.");
266267
Debug.Assert(sqlTceCipherInfoEntry.ColumnEncryptionKeyValues is not null,
267268
"sqlTceCipherInfoEntry.ColumnEncryptionKeyValues should not be null in DecryptSymmetricKey.");

0 commit comments

Comments
 (0)