Imported Upstream version 4.6.0.125

Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2016-08-03 10:59:49 +00:00
parent a569aebcfd
commit e79aa3c0ed
17047 changed files with 3137615 additions and 392334 deletions

View File

@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// <copyright file="ApplicationIntent.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">adoprov</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
/// <summary>
/// represents the application workload type when connecting to a server
/// </summary>
[Serializable]
public enum ApplicationIntent {
ReadWrite = 0,
ReadOnly = 1,
}
}

View File

@@ -0,0 +1,359 @@
//------------------------------------------------------------------------------
// <copyright file="LocalDB.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">antonam</owner>
//------------------------------------------------------------------------------
namespace System.Data
{
using System.Configuration;
using System.Threading;
using System.Runtime.InteropServices;
using System.Data.Common;
using System.Globalization;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
internal static class LocalDBAPI
{
const string const_localDbPrefix = @"(localdb)\";
const string const_partialTrustFlagKey = "ALLOW_LOCALDB_IN_PARTIAL_TRUST";
static PermissionSet _fullTrust = null;
static bool _partialTrustFlagChecked = false;
static bool _partialTrustAllowed = false;
// check if name is in format (localdb)\<InstanceName - not empty> and return instance name if it is
internal static string GetLocalDbInstanceNameFromServerName(string serverName)
{
if (serverName == null)
return null;
serverName = serverName.TrimStart(); // it can start with spaces if specified in quotes
if (!serverName.StartsWith(const_localDbPrefix, StringComparison.OrdinalIgnoreCase))
return null;
string instanceName = serverName.Substring(const_localDbPrefix.Length).Trim();
if (instanceName.Length == 0)
return null;
else
return instanceName;
}
#if !MONO
internal static void ReleaseDLLHandles()
{
s_userInstanceDLLHandle = IntPtr.Zero;
s_localDBFormatMessage = null;
s_localDBCreateInstance = null;
}
//This is copy of handle that SNI maintains, so we are responsible for freeing it - therefore there we are not using SafeHandle
static IntPtr s_userInstanceDLLHandle = IntPtr.Zero;
static object s_dllLock = new object();
static IntPtr UserInstanceDLLHandle
{
get
{
if (s_userInstanceDLLHandle==IntPtr.Zero)
{
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_dllLock, ref lockTaken);
if (s_userInstanceDLLHandle == IntPtr.Zero)
{
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_LOCALDB_HMODULE, ref s_userInstanceDLLHandle);
if (s_userInstanceDLLHandle != IntPtr.Zero)
{
Bid.Trace("<sc.LocalDBAPI.UserInstanceDLLHandle> LocalDB - handle obtained");
}
else
{
SNINativeMethodWrapper.SNI_Error sniError = new SNINativeMethodWrapper.SNI_Error();
SNINativeMethodWrapper.SNIGetLastError(sniError);
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_FailedGetDLLHandle"), sniError: (int)sniError.sniError);
}
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_dllLock);
}
}
return s_userInstanceDLLHandle;
}
}
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int LocalDBCreateInstanceDelegate([MarshalAs(UnmanagedType.LPWStr)] string version, [MarshalAs(UnmanagedType.LPWStr)] string instance, UInt32 flags);
static LocalDBCreateInstanceDelegate s_localDBCreateInstance = null;
static LocalDBCreateInstanceDelegate LocalDBCreateInstance
{
get
{
if (s_localDBCreateInstance==null)
{
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_dllLock, ref lockTaken);
if (s_localDBCreateInstance == null)
{
IntPtr functionAddr = SafeNativeMethods.GetProcAddress(UserInstanceDLLHandle, "LocalDBCreateInstance");
if (functionAddr == IntPtr.Zero)
{
int hResult=Marshal.GetLastWin32Error();
Bid.Trace("<sc.LocalDBAPI.LocalDBCreateInstance> GetProcAddress for LocalDBCreateInstance error 0x{%X}",hResult);
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_MethodNotFound"));
}
s_localDBCreateInstance = (LocalDBCreateInstanceDelegate)Marshal.GetDelegateForFunctionPointer(functionAddr, typeof(LocalDBCreateInstanceDelegate));
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_dllLock);
}
}
return s_localDBCreateInstance;
}
}
[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.Cdecl,CharSet=CharSet.Unicode)]
private delegate int LocalDBFormatMessageDelegate(int hrLocalDB, UInt32 dwFlags, UInt32 dwLanguageId, StringBuilder buffer, ref UInt32 buflen);
static LocalDBFormatMessageDelegate s_localDBFormatMessage = null;
static LocalDBFormatMessageDelegate LocalDBFormatMessage
{
get
{
if (s_localDBFormatMessage==null)
{
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_dllLock, ref lockTaken);
if (s_localDBFormatMessage == null)
{
IntPtr functionAddr = SafeNativeMethods.GetProcAddress(UserInstanceDLLHandle, "LocalDBFormatMessage");
if (functionAddr == IntPtr.Zero)
{
// SNI checks for LocalDBFormatMessage during DLL loading, so it is practically impossibe to get this error.
int hResult=Marshal.GetLastWin32Error();
Bid.Trace("<sc.LocalDBAPI.LocalDBFormatMessage> GetProcAddress for LocalDBFormatMessage error 0x{%X}", hResult);
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_MethodNotFound"));
}
s_localDBFormatMessage = (LocalDBFormatMessageDelegate)Marshal.GetDelegateForFunctionPointer(functionAddr, typeof(LocalDBFormatMessageDelegate));
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_dllLock);
}
}
return s_localDBFormatMessage;
}
}
const UInt32 const_LOCALDB_TRUNCATE_ERR_MESSAGE = 1;// flag for LocalDBFormatMessage that indicates that message can be truncated if it does not fit in the buffer
const int const_ErrorMessageBufferSize = 1024; // Buffer size for Local DB error message, according to Serverless team, 1K will be enough for all messages
internal static string GetLocalDBMessage(int hrCode)
{
Debug.Assert(hrCode < 0, "HRCode does not indicate error");
try
{
StringBuilder buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
UInt32 len = (UInt32)buffer.Capacity;
// First try for current culture
int hResult=LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: (UInt32)CultureInfo.CurrentCulture.LCID,
buffer: buffer, buflen: ref len);
if (hResult>=0)
return buffer.ToString();
else
{
// Message is not available for current culture, try default
buffer = new StringBuilder((int)const_ErrorMessageBufferSize);
len = (UInt32) buffer.Capacity;
hResult=LocalDBFormatMessage(hrLocalDB: hrCode, dwFlags: const_LOCALDB_TRUNCATE_ERR_MESSAGE, dwLanguageId: 0 /* thread locale with fallback to English */,
buffer: buffer, buflen: ref len);
if (hResult >= 0)
return buffer.ToString();
else
return string.Format(CultureInfo.CurrentCulture, "{0} (0x{1:X}).", Res.GetString("LocalDB_UnobtainableMessage"), hResult);
}
}
catch (SqlException exc)
{
return string.Format(CultureInfo.CurrentCulture, "{0} ({1}).", Res.GetString("LocalDB_UnobtainableMessage"), exc.Message);
}
}
static SqlException CreateLocalDBException(string errorMessage, string instance = null, int localDbError = 0, int sniError = 0)
{
Debug.Assert((localDbError == 0) || (sniError == 0), "LocalDB error and SNI error cannot be specified simultaneously");
Debug.Assert(!string.IsNullOrEmpty(errorMessage), "Error message should not be null or empty");
SqlErrorCollection collection = new SqlErrorCollection();
int errorCode = (localDbError == 0) ? sniError : localDbError;
if (sniError!=0)
{
string sniErrorMessage = SQL.GetSNIErrorMessage(sniError);
errorMessage = String.Format((IFormatProvider)null, "{0} (error: {1} - {2})",
errorMessage, sniError, sniErrorMessage);
}
collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, errorMessage, null, 0));
if (localDbError != 0)
collection.Add(new SqlError(errorCode, 0, TdsEnums.FATAL_ERROR_CLASS, instance, GetLocalDBMessage(localDbError), null, 0));
SqlException exc = SqlException.CreateException(collection, null);
exc._doNotReconnect = true;
return exc;
}
private class InstanceInfo
{
internal InstanceInfo(string version)
{
this.version = version;
this.created = false;
}
internal readonly string version;
internal bool created;
}
static object s_configLock=new object();
static Dictionary<string, InstanceInfo> s_configurableInstances = null;
internal static void DemandLocalDBPermissions()
{
if (!_partialTrustAllowed)
{
if (!_partialTrustFlagChecked)
{
object partialTrustFlagValue = AppDomain.CurrentDomain.GetData(const_partialTrustFlagKey);
if (partialTrustFlagValue != null && partialTrustFlagValue is bool)
{
_partialTrustAllowed = (bool)partialTrustFlagValue;
}
_partialTrustFlagChecked = true;
if (_partialTrustAllowed)
{
return;
}
}
if (_fullTrust == null)
{
_fullTrust = new NamedPermissionSet("FullTrust");
}
_fullTrust.Demand();
}
}
internal static void AssertLocalDBPermissions()
{
_partialTrustAllowed = true;
}
internal static void CreateLocalDBInstance(string instance)
{
DemandLocalDBPermissions();
if (s_configurableInstances == null)
{
// load list of instances from configuration, mark them as not created
bool lockTaken = false;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Monitor.Enter(s_configLock, ref lockTaken);
if (s_configurableInstances == null)
{
Dictionary<string, InstanceInfo> tempConfigurableInstances = new Dictionary<string, InstanceInfo>(StringComparer.OrdinalIgnoreCase);
#if !NO_CONFIGURATION
object section = PrivilegedConfigurationManager.GetSection("system.data.localdb");
if (section != null) // if no section just skip creation
{
// validate section type
LocalDBConfigurationSection configSection = section as LocalDBConfigurationSection;
if (configSection == null)
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_BadConfigSectionType"));
foreach (LocalDBInstanceElement confElement in configSection.LocalDbInstances)
{
Debug.Assert(confElement.Name != null && confElement.Version != null, "Both name and version should not be null");
tempConfigurableInstances.Add(confElement.Name.Trim(), new InstanceInfo(confElement.Version.Trim()));
}
}
else
#endif
Bid.Trace( "<sc.LocalDBAPI.CreateLocalDBInstance> No system.data.localdb section found in configuration");
s_configurableInstances = tempConfigurableInstances;
}
}
finally
{
if (lockTaken)
Monitor.Exit(s_configLock);
}
}
InstanceInfo instanceInfo = null;
if (!s_configurableInstances.TryGetValue(instance,out instanceInfo))
return; // instance name was not in the config
if (instanceInfo.created)
return; // instance has already been created
Debug.Assert(!instance.Contains("\0"), "Instance name should contain embedded nulls");
if (instanceInfo.version.Contains("\0"))
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_InvalidVersion"), instance: instance);
// LocalDBCreateInstance is thread- and cross-process safe method, it is OK to call from two threads simultaneously
int hr = LocalDBCreateInstance(instanceInfo.version, instance, flags: 0);
Bid.Trace("<sc.LocalDBAPI.CreateLocalDBInstance> Starting creation of instance %ls version %ls", instance, instanceInfo.version);
if (hr < 0)
throw CreateLocalDBException(errorMessage: Res.GetString("LocalDB_CreateFailed"), instance: instance, localDbError: hr);
Bid.Trace("<sc.LocalDBAPI.CreateLocalDBInstance> Finished creation of instance %ls", instance);
instanceInfo.created=true; // mark instance as created
} // CreateLocalDbInstance
#endif
}
}

View File

@@ -0,0 +1,84 @@
//------------------------------------------------------------------------------
// <copyright file="LocalDB.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">antonam</owner>
//------------------------------------------------------------------------------
namespace System.Data
{
using System.Configuration;
using System.Collections;
internal sealed class LocalDBInstanceElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get
{
return this["name"] as string;
}
}
[ConfigurationProperty("version", IsRequired = true)]
public string Version
{
get
{
return this["version"] as string;
}
}
}
internal sealed class LocalDBInstancesCollection : ConfigurationElementCollection
{
private class TrimOrdinalIgnoreCaseStringComparer : IComparer
{
public int Compare(object x, object y)
{
string xStr = x as string;
if (xStr != null)
x = xStr.Trim();
string yStr = y as string;
if (yStr != null)
y = yStr.Trim();
return StringComparer.OrdinalIgnoreCase.Compare(x,y);
}
}
static readonly TrimOrdinalIgnoreCaseStringComparer s_comparer = new TrimOrdinalIgnoreCaseStringComparer();
internal LocalDBInstancesCollection()
: base(s_comparer)
{
}
protected override ConfigurationElement CreateNewElement()
{
return new LocalDBInstanceElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((LocalDBInstanceElement)element).Name;
}
}
internal sealed class LocalDBConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("localdbinstances", IsRequired = true)]
public LocalDBInstancesCollection LocalDbInstances
{
get
{
return (LocalDBInstancesCollection)this["localdbinstances"] ?? new LocalDBInstancesCollection();
}
}
}
}

View File

@@ -0,0 +1,17 @@
//------------------------------------------------------------------------------
// <copyright file="OnChangedEventHandler.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">ramp</owner>
// <owner current="true" primary="false">blained</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System;
using System.ComponentModel;
using System.Collections;
using System.Data;
public delegate void OnChangeEventHandler(object sender, SqlNotificationEventArgs e);
}

View File

@@ -0,0 +1,37 @@
//------------------------------------------------------------------------------
// <copyright file="ParameterPeekAheadValue.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">alazela</owner>
// <owner current="true" primary="false">billin</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using Microsoft.SqlServer.Server;
// simple storage to contain objects that must be generated prior to sending data, but
// that we cannot re-generate at the time of sending the data. The entire purpose is
// to avoid long, complicated parameter lists that take every possible set of values.
// Instead, a single peekahead object is passed in, encapsulating whatever sets are needed.
//
// Example:
// When processing IEnumerable<SqlDataRecord>, we need to obtain the enumerator and
// the first record during metadata generation (metadata is stored in the first record),
// but to properly stream the value, we can't ask the IEnumerable for these objects again
// when it's time to send the actual values.
internal class ParameterPeekAheadValue {
// Peekahead for IEnumerable<SqlDataRecord>
internal IEnumerator<SqlDataRecord> Enumerator;
internal SqlDataRecord FirstRecord;
}
}

View File

@@ -0,0 +1,35 @@
//------------------------------------------------------------------------------
// <copyright file="RowsCopiedEvent.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
public class SqlRowsCopiedEventArgs : System.EventArgs {
private bool _abort;
private long _rowsCopied;
public SqlRowsCopiedEventArgs (long rowsCopied) {
_rowsCopied = rowsCopied;
}
public bool Abort {
get {
return _abort;
}
set {
_abort = value;
}
}
public long RowsCopied {
get {
return _rowsCopied;
}
}
}
}

View File

@@ -0,0 +1,12 @@
//------------------------------------------------------------------------------
// <copyright file="RowsCopiedEventHandler.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">mithomas</owner>
// <owner current="true" primary="false">blained</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
public delegate void SqlRowsCopiedEventHandler(object sender, SqlRowsCopiedEventArgs e);
}

View File

@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// <copyright file="SqlMetaData.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">alazela</owner>
// <owner current="true" primary="false">laled</owner>
// <owner current="true" primary="false">billin</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
public enum SortOrder {
Unspecified = -1,
Ascending = 0,
Descending = 1
}
}

View File

@@ -0,0 +1,406 @@
//------------------------------------------------------------------------------
// <copyright file="SqlAeadAes256CbcHmac256Algorithm.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">balnee</owner>
// <owner current="true" primary="false">krishnib</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
/// <summary>
/// This class implements authenticated encryption algorithm with associated data as described in
/// http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. More specifically this implements
/// AEAD_AES_256_CBC_HMAC_SHA256 algorithm.
/// </summary>
internal class SqlAeadAes256CbcHmac256Algorithm : SqlClientEncryptionAlgorithm
{
/// <summary>
/// Algorithm Name
/// </summary>
internal const string AlgorithmName = @"AEAD_AES_256_CBC_HMAC_SHA256";
/// <summary>
/// Key size in bytes
/// </summary>
private const int _KeySizeInBytes = SqlAeadAes256CbcHmac256EncryptionKey.KeySize / 8;
/// <summary>
/// Block size in bytes. AES uses 16 byte blocks.
/// </summary>
private const int _BlockSizeInBytes = 16;
/// <summary>
/// Minimum Length of cipherText without authentication tag. This value is 1 (version byte) + 16 (IV) + 16 (minimum of 1 block of cipher Text)
/// </summary>
private const int _MinimumCipherTextLengthInBytesNoAuthenticationTag = sizeof(byte) + _BlockSizeInBytes + _BlockSizeInBytes;
/// <summary>
/// Minimum Length of cipherText. This value is 1 (version byte) + 32 (authentication tag) + 16 (IV) + 16 (minimum of 1 block of cipher Text)
/// </summary>
private const int _MinimumCipherTextLengthInBytesWithAuthenticationTag = _MinimumCipherTextLengthInBytesNoAuthenticationTag + _KeySizeInBytes;
/// <summary>
/// Cipher Mode. For this algorithm, we only use CBC mode.
/// </summary>
private const CipherMode _cipherMode = CipherMode.CBC;
/// <summary>
/// Padding mode. This algorithm uses PKCS7.
/// </summary>
private const PaddingMode _paddingMode = PaddingMode.PKCS7;
/// <summary>
/// Variable indicating whether this algorithm should work in Deterministic mode or Randomized mode.
/// For deterministic encryption, we derive an IV from the plaintext data.
/// For randomized encryption, we generate a cryptographically random IV.
/// </summary>
private readonly bool _isDeterministic;
/// <summary>
/// Algorithm Version.
/// </summary>
private readonly byte _algorithmVersion;
/// <summary>
/// Column Encryption Key. This has a root key and three derived keys.
/// </summary>
private readonly SqlAeadAes256CbcHmac256EncryptionKey _columnEncryptionKey;
/// <summary>
/// The pool of crypto providers to use for encrypt/decrypt operations.
/// </summary>
private readonly ConcurrentQueue<AesCryptoServiceProvider> _cryptoProviderPool;
/// <summary>
/// Byte array with algorithm version used for authentication tag computation.
/// </summary>
private static readonly byte[] _version = new byte[] {0x01};
/// <summary>
/// Byte array with algorithm version size used for authentication tag computation.
/// </summary>
private static readonly byte[] _versionSize = new byte[] {sizeof(byte)};
/// <summary>
/// Initializes a new instance of SqlAeadAes256CbcHmac256Algorithm algorithm with a given key and encryption type
/// </summary>
/// <param name="encryptionKey">
/// Root encryption key from which three other keys will be derived
/// </param>
/// <param name="encryptionType">Encryption Type, accepted values are Deterministic and Randomized.
/// For Deterministic encryption, a synthetic IV will be genenrated during encryption
/// For Randomized encryption, a random IV will be generated during encryption.
/// </param>
/// <param name="algorithmVersion">
/// Algorithm version
/// </param>
internal SqlAeadAes256CbcHmac256Algorithm(SqlAeadAes256CbcHmac256EncryptionKey encryptionKey, SqlClientEncryptionType encryptionType, byte algorithmVersion) {
_columnEncryptionKey = encryptionKey;
_algorithmVersion = algorithmVersion;
_version[0] = algorithmVersion;
Debug.Assert (null != encryptionKey, "Null encryption key detected in AeadAes256CbcHmac256 algorithm");
Debug.Assert (0x01 == algorithmVersion, "Unknown algorithm version passed to AeadAes256CbcHmac256");
// Validate encryption type for this algorithm
// This algorithm can only provide randomized or deterministic encryption types.
if (encryptionType == SqlClientEncryptionType.Deterministic) {
_isDeterministic = true;
}
else {
Debug.Assert (SqlClientEncryptionType.Randomized == encryptionType, "Invalid Encryption Type detected in SqlAeadAes256CbcHmac256Algorithm, this should've been caught in factory class");
}
_cryptoProviderPool = new ConcurrentQueue<AesCryptoServiceProvider>();
}
/// <summary>
/// Encryption Algorithm
/// cell_iv = HMAC_SHA-2-256(iv_key, cell_data) truncated to 128 bits
/// cell_ciphertext = AES-CBC-256(enc_key, cell_iv, cell_data) with PKCS7 padding.
/// cell_tag = HMAC_SHA-2-256(mac_key, versionbyte + cell_iv + cell_ciphertext + versionbyte_length)
/// cell_blob = versionbyte + cell_tag + cell_iv + cell_ciphertext
/// </summary>
/// <param name="plainText">Plaintext data to be encrypted</param>
/// <returns>Returns the ciphertext corresponding to the plaintext.</returns>
internal override byte[] EncryptData(byte[] plainText) {
return EncryptData(plainText, hasAuthenticationTag: true);
}
/// <summary>
/// Encryption Algorithm
/// cell_iv = HMAC_SHA-2-256(iv_key, cell_data) truncated to 128 bits
/// cell_ciphertext = AES-CBC-256(enc_key, cell_iv, cell_data) with PKCS7 padding.
/// (optional) cell_tag = HMAC_SHA-2-256(mac_key, versionbyte + cell_iv + cell_ciphertext + versionbyte_length)
/// cell_blob = versionbyte + [cell_tag] + cell_iv + cell_ciphertext
/// </summary>
/// <param name="plainText">Plaintext data to be encrypted</param>
/// <param name="hasAuthenticationTag">Does the algorithm require authentication tag.</param>
/// <returns>Returns the ciphertext corresponding to the plaintext.</returns>
protected byte[] EncryptData(byte[] plainText, bool hasAuthenticationTag) {
// Empty values get encrypted and decrypted properly for both Deterministic and Randomized encryptions.
Debug.Assert(plainText != null);
byte[] iv = new byte[_BlockSizeInBytes];
// Prepare IV
// Should be 1 single block (16 bytes)
if (_isDeterministic) {
SqlSecurityUtility.GetHMACWithSHA256(plainText, _columnEncryptionKey.IVKey, iv);
}
else {
SqlSecurityUtility.GenerateRandomBytes(iv);
}
int numBlocks = plainText.Length / _BlockSizeInBytes + 1;
// Final blob we return = version + HMAC + iv + cipherText
const int hmacStartIndex = 1;
int authenticationTagLen = hasAuthenticationTag ? _KeySizeInBytes : 0;
int ivStartIndex = hmacStartIndex + authenticationTagLen;
int cipherStartIndex = ivStartIndex + _BlockSizeInBytes; // this is where hmac starts.
// Output buffer size = size of VersionByte + Authentication Tag + IV + cipher Text blocks.
int outputBufSize = sizeof(byte) + authenticationTagLen + iv.Length + (numBlocks*_BlockSizeInBytes);
byte[] outBuffer = new byte[outputBufSize];
// Store the version and IV rightaway
outBuffer[0] = _algorithmVersion;
Buffer.BlockCopy(iv, 0, outBuffer, ivStartIndex, iv.Length);
AesCryptoServiceProvider aesAlg;
// Try to get a provider from the pool.
// If no provider is available, create a new one.
if (!_cryptoProviderPool.TryDequeue(out aesAlg)) {
aesAlg = new AesCryptoServiceProvider();
try {
// Set various algorithm properties
aesAlg.Key = _columnEncryptionKey.EncryptionKey;
aesAlg.Mode = _cipherMode;
aesAlg.Padding = _paddingMode;
}
catch (Exception) {
if (aesAlg != null) {
aesAlg.Dispose();
}
throw;
}
}
try {
// Always set the IV since it changes from cell to cell.
aesAlg.IV = iv;
// Compute CipherText and authentication tag in a single pass
using (ICryptoTransform encryptor = aesAlg.CreateEncryptor()) {
Debug.Assert(encryptor.CanTransformMultipleBlocks, "AES Encryptor can transform multiple blocks");
int count = 0;
int cipherIndex = cipherStartIndex; // this is where cipherText starts
if (numBlocks > 1) {
count = (numBlocks - 1) * _BlockSizeInBytes;
cipherIndex += encryptor.TransformBlock(plainText, 0, count, outBuffer, cipherIndex);
}
byte[] buffTmp = encryptor.TransformFinalBlock(plainText, count, plainText.Length - count); // done encrypting
Buffer.BlockCopy(buffTmp, 0, outBuffer, cipherIndex, buffTmp.Length);
cipherIndex += buffTmp.Length;
}
if (hasAuthenticationTag) {
using (HMACSHA256 hmac = new HMACSHA256(_columnEncryptionKey.MACKey)) {
Debug.Assert(hmac.CanTransformMultipleBlocks, "HMAC can't transform multiple blocks");
hmac.TransformBlock(_version, 0, _version.Length, _version, 0);
hmac.TransformBlock(iv, 0, iv.Length, iv, 0);
// Compute HMAC on final block
hmac.TransformBlock(outBuffer, cipherStartIndex, numBlocks * _BlockSizeInBytes, outBuffer, cipherStartIndex);
hmac.TransformFinalBlock(_versionSize, 0, _versionSize.Length);
byte[] hash = hmac.Hash;
Debug.Assert(hash.Length >= authenticationTagLen, "Unexpected hash size");
Buffer.BlockCopy(hash, 0, outBuffer, hmacStartIndex, authenticationTagLen);
}
}
}
finally {
// Return the provider to the pool.
_cryptoProviderPool.Enqueue(aesAlg);
}
return outBuffer;
}
/// <summary>
/// Decryption steps
/// 1. Validate version byte
/// 2. Validate Authentication tag
/// 3. Decrypt the message
/// </summary>
/// <param name="cipherText"></param>
/// <returns></returns>
internal override byte[] DecryptData(byte[] cipherText) {
return DecryptData(cipherText, hasAuthenticationTag: true);
}
/// <summary>
/// Decryption steps
/// 1. Validate version byte
/// 2. (optional) Validate Authentication tag
/// 3. Decrypt the message
/// </summary>
/// <param name="cipherText"></param>
/// <param name="hasAuthenticationTag"></param>
/// <returns></returns>
protected byte[] DecryptData(byte[] cipherText, bool hasAuthenticationTag) {
Debug.Assert(cipherText != null);
byte[] iv = new byte[_BlockSizeInBytes];
int minimumCipherTextLength = hasAuthenticationTag ? _MinimumCipherTextLengthInBytesWithAuthenticationTag : _MinimumCipherTextLengthInBytesNoAuthenticationTag;
if (cipherText.Length < minimumCipherTextLength) {
throw SQL.InvalidCipherTextSize(cipherText.Length, minimumCipherTextLength);
}
// Validate the version byte
int startIndex = 0;
if (cipherText[startIndex] != _algorithmVersion) {
// Cipher text was computed with a different algorithm version than this.
throw SQL.InvalidAlgorithmVersion(cipherText[startIndex], _algorithmVersion);
}
startIndex += 1;
int authenticationTagOffset = 0;
// Read authentication tag
if (hasAuthenticationTag) {
authenticationTagOffset = startIndex;
startIndex += _KeySizeInBytes; // authentication tag size is _KeySizeInBytes
}
// Read cell IV
Buffer.BlockCopy(cipherText, startIndex, iv, 0, iv.Length);
startIndex += iv.Length;
// Read encrypted text
int cipherTextOffset = startIndex;
int cipherTextCount = cipherText.Length - startIndex;
if (hasAuthenticationTag) {
// Compute authentication tag
byte[] authenticationTag = PrepareAuthenticationTag(iv, cipherText, cipherTextOffset, cipherTextCount);
if (!SqlSecurityUtility.CompareBytes(authenticationTag, cipherText, authenticationTagOffset, authenticationTag.Length)) {
// Potentially tampered data, throw an exception
throw SQL.InvalidAuthenticationTag();
}
}
// Decrypt the text and return
return DecryptData(iv, cipherText, cipherTextOffset, cipherTextCount);
}
/// <summary>
/// Decrypts plain text data using AES in CBC mode
/// </summary>
/// <param name="plainText"> cipher text data to be decrypted</param>
/// <param name="iv">IV to be used for decryption</param>
/// <returns>Returns decrypted plain text data</returns>
private byte[] DecryptData(byte[] iv, byte[] cipherText, int offset, int count) {
Debug.Assert((iv != null) && (cipherText != null));
Debug.Assert (offset > -1 && count > -1);
Debug.Assert ((count+offset) <= cipherText.Length);
byte[] plainText;
AesCryptoServiceProvider aesAlg;
// Try to get a provider from the pool.
// If no provider is available, create a new one.
if (!_cryptoProviderPool.TryDequeue(out aesAlg)) {
aesAlg = new AesCryptoServiceProvider();
try {
// Set various algorithm properties
aesAlg.Key = _columnEncryptionKey.EncryptionKey;
aesAlg.Mode = _cipherMode;
aesAlg.Padding = _paddingMode;
}
catch (Exception) {
if (aesAlg != null) {
aesAlg.Dispose();
}
throw;
}
}
try {
// Always set the IV since it changes from cell to cell.
aesAlg.IV = iv;
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream()) {
// Create an encryptor to perform the stream transform.
using (ICryptoTransform decryptor = aesAlg.CreateDecryptor()) {
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write)) {
// Decrypt the secret message and get the plain text data
csDecrypt.Write(cipherText, offset, count);
csDecrypt.FlushFinalBlock();
plainText = msDecrypt.ToArray();
}
}
}
}
finally {
// Return the provider to the pool.
_cryptoProviderPool.Enqueue(aesAlg);
}
return plainText;
}
/// <summary>
/// Prepares an authentication tag.
/// Authentication Tag = HMAC_SHA-2-256(mac_key, versionbyte + cell_iv + cell_ciphertext + versionbyte_length)
/// </summary>
/// <param name="cipherText"></param>
/// <returns></returns>
private byte[] PrepareAuthenticationTag(byte[] iv, byte[] cipherText, int offset, int length) {
Debug.Assert(cipherText != null);
byte[] computedHash;
byte[] authenticationTag = new byte[_KeySizeInBytes];
// Raw Tag Length:
// 1 for the version byte
// 1 block for IV (16 bytes)
// cipherText.Length
// 1 byte for version byte length
using (HMACSHA256 hmac = new HMACSHA256(_columnEncryptionKey.MACKey)) {
int retVal = 0;
retVal = hmac.TransformBlock(_version, 0, _version.Length, _version, 0);
Debug.Assert(retVal == _version.Length);
retVal = hmac.TransformBlock(iv, 0, iv.Length, iv, 0);
Debug.Assert(retVal == iv.Length);
retVal = hmac.TransformBlock(cipherText, offset, length, cipherText, offset);
Debug.Assert(retVal == length);
hmac.TransformFinalBlock(_versionSize, 0, _versionSize.Length);
computedHash = hmac.Hash;
}
Debug.Assert (computedHash.Length >= authenticationTag.Length);
Buffer.BlockCopy (computedHash, 0, authenticationTag, 0, authenticationTag.Length);
return authenticationTag;
}
}
}

View File

@@ -0,0 +1,128 @@
//------------------------------------------------------------------------------
// <copyright file="SqlAeadAes256CbcHmac256EncryptionKey.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">balnee</owner>
// <owner current="true" primary="false">krishnib</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient
{
using System;
using System.Data.SqlClient;
using System.Text;
/// <summary>
/// Encryption key class containing 4 keys. This class is used by SqlAeadAes256CbcHmac256Algorithm and SqlAes256CbcAlgorithm
/// 1) root key - Main key that is used to derive the keys used in the encryption algorithm
/// 2) encryption key - A derived key that is used to encrypt the plain text and generate cipher text
/// 3) mac_key - A derived key that is used to compute HMAC of the cipher text
/// 4) iv_key - A derived key that is used to generate a synthetic IV from plain text data.
/// </summary>
internal class SqlAeadAes256CbcHmac256EncryptionKey : SqlClientSymmetricKey
{
/// <summary>
/// Key size in bits
/// </summary>
internal const int KeySize = 256;
/// <summary>
/// Encryption Key Salt format. This is used to derive the encryption key from the root key.
/// </summary>
private const string _encryptionKeySaltFormat = @"Microsoft SQL Server cell encryption key with encryption algorithm:{0} and key length:{1}";
/// <summary>
/// MAC Key Salt format. This is used to derive the MAC key from the root key.
/// </summary>
private const string _macKeySaltFormat = @"Microsoft SQL Server cell MAC key with encryption algorithm:{0} and key length:{1}";
/// <summary>
/// IV Key Salt format. This is used to derive the IV key from the root key. This is only used for Deterministic encryption.
/// </summary>
private const string _ivKeySaltFormat = @"Microsoft SQL Server cell IV key with encryption algorithm:{0} and key length:{1}";
/// <summary>
/// Encryption Key
/// </summary>
private readonly SqlClientSymmetricKey _encryptionKey;
/// <summary>
/// MAC key
/// </summary>
private readonly SqlClientSymmetricKey _macKey;
/// <summary>
/// IV Key
/// </summary>
private readonly SqlClientSymmetricKey _ivKey;
/// <summary>
/// The name of the algorithm this key will be used with.
/// </summary>
private readonly string _algorithmName;
/// <summary>
/// Derives all the required keys from the given root key
/// </summary>
/// <param name="rootKey">Root key used to derive all the required derived keys</param>
internal SqlAeadAes256CbcHmac256EncryptionKey(byte[] rootKey, string algorithmName): base(rootKey)
{
_algorithmName = algorithmName;
int keySizeInBytes = KeySize / 8;
// Key validation
if (rootKey.Length != keySizeInBytes)
{
throw SQL.InvalidKeySize(_algorithmName,
rootKey.Length,
keySizeInBytes);
}
// Derive keys from the root key
//
// Derive encryption key
string encryptionKeySalt = string.Format(_encryptionKeySaltFormat,
_algorithmName,
KeySize);
byte[] buff1 = new byte[keySizeInBytes];
SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(encryptionKeySalt), RootKey, buff1);
_encryptionKey = new SqlClientSymmetricKey(buff1);
// Derive mac key
string macKeySalt = string.Format(_macKeySaltFormat, _algorithmName, KeySize);
byte[] buff2 = new byte[keySizeInBytes];
SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(macKeySalt),RootKey,buff2);
_macKey = new SqlClientSymmetricKey(buff2);
// Derive iv key
string ivKeySalt = string.Format(_ivKeySaltFormat, _algorithmName, KeySize);
byte[] buff3 = new byte[keySizeInBytes];
SqlSecurityUtility.GetHMACWithSHA256(Encoding.Unicode.GetBytes(ivKeySalt),RootKey,buff3);
_ivKey = new SqlClientSymmetricKey(buff3);
}
/// <summary>
/// Encryption key should be used for encryption and decryption
/// </summary>
internal byte[] EncryptionKey
{
get { return _encryptionKey.RootKey; }
}
/// <summary>
/// MAC key should be used to compute and validate HMAC
/// </summary>
internal byte[] MACKey
{
get { return _macKey.RootKey; }
}
/// <summary>
/// IV key should be used to compute synthetic IV from a given plain text
/// </summary>
internal byte[] IVKey
{
get { return _ivKey.RootKey; }
}
}
}

View File

@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
// <copyright file="SqlAeadAes256CbcHmac256Factory.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">balnee</owner>
// <owner current="true" primary="false">krishnib</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System;
using System.Collections.Concurrent;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Text;
/// <summary>
/// This is a factory class for AEAD_AES_256_CBC_HMAC_SHA256
/// </summary>
internal class SqlAeadAes256CbcHmac256Factory : SqlClientEncryptionAlgorithmFactory {
/// <summary>
/// Factory classes caches the SqlAeadAes256CbcHmac256EncryptionKey objects to avoid computation of the derived keys
/// </summary>
private readonly ConcurrentDictionary<string, SqlAeadAes256CbcHmac256Algorithm> _encryptionAlgorithms =
new ConcurrentDictionary<string, SqlAeadAes256CbcHmac256Algorithm>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 2);
/// <summary>
/// Creates an instance of AeadAes256CbcHmac256Algorithm class with a given key
/// </summary>
/// <param name="encryptionKey">Root key</param>
/// <param name="encryptionType">Encryption Type. Expected values are either Determinitic or Randomized.</param>
/// <param name="encryptionAlgorithm">Encryption Algorithm.</param>
/// <returns></returns>
internal override SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encryptionKey, SqlClientEncryptionType encryptionType, string encryptionAlgorithm) {
// Callers should have validated the encryption algorithm and the encryption key
Debug.Assert(encryptionKey != null);
Debug.Assert(string.Equals(encryptionAlgorithm, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName, StringComparison.OrdinalIgnoreCase) == true);
// Validate encryption type
if (!((encryptionType == SqlClientEncryptionType.Deterministic) || (encryptionType == SqlClientEncryptionType.Randomized))) {
throw SQL.InvalidEncryptionType(SqlAeadAes256CbcHmac256Algorithm.AlgorithmName,
encryptionType,
SqlClientEncryptionType.Deterministic,
SqlClientEncryptionType.Randomized);
}
// Get the cached encryption algorithm if one exists or create a new one, add it to cache and use it
//
// For now, we only have one version. In future, we may need to parse the algorithm names to derive the version byte.
const byte algorithmVersion = 0x1;
StringBuilder algorithmKeyBuilder = new StringBuilder(Convert.ToBase64String(encryptionKey.RootKey), SqlSecurityUtility.GetBase64LengthFromByteLength(encryptionKey.RootKey.Length) + 4/*separators, type and version*/);
#if DEBUG
int capacity = algorithmKeyBuilder.Capacity;
#endif //DEBUG
algorithmKeyBuilder.Append(":");
algorithmKeyBuilder.Append((int)encryptionType);
algorithmKeyBuilder.Append(":");
algorithmKeyBuilder.Append(algorithmVersion);
string algorithmKey = algorithmKeyBuilder.ToString();
#if DEBUG
Debug.Assert(algorithmKey.Length <= capacity, "We needed to allocate a larger array");
#endif //DEBUG
SqlAeadAes256CbcHmac256Algorithm aesAlgorithm;
if (!_encryptionAlgorithms.TryGetValue(algorithmKey, out aesAlgorithm)) {
SqlAeadAes256CbcHmac256EncryptionKey encryptedKey = new SqlAeadAes256CbcHmac256EncryptionKey(encryptionKey.RootKey, SqlAeadAes256CbcHmac256Algorithm.AlgorithmName);
aesAlgorithm = new SqlAeadAes256CbcHmac256Algorithm(encryptedKey, encryptionType, algorithmVersion);
// In case multiple threads reach here at the same time, the first one adds the value
// the second one will be a no-op, the allocated memory will be claimed by Garbage Collector.
_encryptionAlgorithms.TryAdd(algorithmKey, aesAlgorithm);
}
return aesAlgorithm;
}
}
}

View File

@@ -0,0 +1,65 @@
//------------------------------------------------------------------------------
// <copyright file="SqlAes256CbcAlgorithm.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">balnee</owner>
// <owner current="true" primary="false">krishnib</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient
{
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
/// <summary>
/// This class implements AES_256_CBC algorithm.
/// </summary>
internal class SqlAes256CbcAlgorithm : SqlAeadAes256CbcHmac256Algorithm
{
/// <summary>
/// Algorithm Name
/// </summary>
internal new const string AlgorithmName = @"AES_256_CBC";
/// <summary>
/// Initializes a new instance of SqlAes256CbcAlgorithm algorithm with a given key and encryption type
/// </summary>
/// <param name="encryptionKey">
/// Root encryption key from which three other keys will be derived
/// </param>
/// <param name="encryptionType">Encryption Type, accepted values are Deterministic and Randomized.
/// For Deterministic encryption, a synthetic IV will be genenrated during encryption
/// For Randomized encryption, a random IV will be generated during encryption.
/// </param>
/// <param name="algorithmVersion">
/// Algorithm version
/// </param>
internal SqlAes256CbcAlgorithm(SqlAeadAes256CbcHmac256EncryptionKey encryptionKey, SqlClientEncryptionType encryptionType, byte algorithmVersion)
:base(encryptionKey, encryptionType, algorithmVersion)
{ }
/// <summary>
/// Encryption Algorithm
/// Simply call the base class, indicating we don't need an authentication tag.
/// </summary>
/// <param name="plainText">Plaintext data to be encrypted</param>
/// <returns>Returns the ciphertext corresponding to the plaintext.</returns>
internal override byte[] EncryptData(byte[] plainText) {
return EncryptData(plainText, hasAuthenticationTag: false);
}
/// <summary>
/// Decryption Algorithm
/// Simply call the base class, indicating we don't have an authentication tag.
/// </summary>
/// <param name="cipherText"></param>
/// <returns></returns>
internal override byte[] DecryptData(byte[] cipherText) {
return base.DecryptData(cipherText, hasAuthenticationTag: false);
}
}
}

View File

@@ -0,0 +1,85 @@
//------------------------------------------------------------------------------
// <copyright file="SqlAeadAes256CbcHmac256Factory.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">balnee</owner>
// <owner current="true" primary="false">krishnib</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient
{
using System;
using System.Collections.Concurrent;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Text;
/// <summary>
/// This is a factory class for AES_256_CBC.
/// </summary>
internal class SqlAes256CbcFactory : SqlAeadAes256CbcHmac256Factory
{
/// <summary>
/// Factory classes caches the SqlAeadAes256CbcHmac256EncryptionKey objects to avoid computation of the derived keys
/// </summary>
private readonly ConcurrentDictionary<string, SqlAes256CbcAlgorithm> _encryptionAlgorithms =
new ConcurrentDictionary<string, SqlAes256CbcAlgorithm>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/, capacity: 2);
/// <summary>
/// Creates an instance of SqlAes256CbcAlgorithm class with a given key
/// </summary>
/// <param name="encryptionKey">Root key</param>
/// <param name="encryptionType">Encryption Type. Expected values are either Determinitic or Randomized.</param>
/// <param name="encryptionAlgorithm">Encryption Algorithm.</param>
/// <returns></returns>
internal override SqlClientEncryptionAlgorithm Create(SqlClientSymmetricKey encryptionKey, SqlClientEncryptionType encryptionType, string encryptionAlgorithm)
{
// Callers should have validated the encryption algorithm and the encryption key
Debug.Assert(encryptionKey != null);
Debug.Assert(string.Equals(encryptionAlgorithm, SqlAes256CbcAlgorithm.AlgorithmName, StringComparison.OrdinalIgnoreCase) == true);
// Validate encryption type
if (!((encryptionType == SqlClientEncryptionType.Deterministic) || (encryptionType == SqlClientEncryptionType.Randomized)))
{
throw SQL.InvalidEncryptionType(SqlAes256CbcAlgorithm.AlgorithmName,
encryptionType,
SqlClientEncryptionType.Deterministic,
SqlClientEncryptionType.Randomized);
}
// Get the cached encryption algorithm if one exists or create a new one, add it to cache and use it
//
// For now, we only have one version. In future, we may need to parse the algorithm names to derive the version byte.
const byte algorithmVersion = 0x1;
StringBuilder algorithmKeyBuilder = new StringBuilder(Convert.ToBase64String(encryptionKey.RootKey), SqlSecurityUtility.GetBase64LengthFromByteLength(encryptionKey.RootKey.Length) + 4/*separators, type and version*/);
#if DEBUG
int capacity = algorithmKeyBuilder.Capacity;
#endif //DEBUG
algorithmKeyBuilder.Append(":");
algorithmKeyBuilder.Append((int)encryptionType);
algorithmKeyBuilder.Append(":");
algorithmKeyBuilder.Append(algorithmVersion);
string algorithmKey = algorithmKeyBuilder.ToString();
#if DEBUG
Debug.Assert(algorithmKey.Length <= capacity, "We needed to allocate a larger array");
#endif //DEBUG
SqlAes256CbcAlgorithm aesAlgorithm;
if (!_encryptionAlgorithms.TryGetValue(algorithmKey, out aesAlgorithm))
{
SqlAeadAes256CbcHmac256EncryptionKey encryptedKey = new SqlAeadAes256CbcHmac256EncryptionKey(encryptionKey.RootKey, SqlAes256CbcAlgorithm.AlgorithmName);
aesAlgorithm = new SqlAes256CbcAlgorithm(encryptedKey, encryptionType, algorithmVersion);
// In case multiple threads reach here at the same time, the first one adds the value
// the second one will be a no-op, the allocated memory will be claimed by Garbage Collector.
_encryptionAlgorithms.TryAdd(algorithmKey, aesAlgorithm);
}
return aesAlgorithm;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
2a4b96c02a36a45bbc09c39c729d107f10314af7

View File

@@ -0,0 +1,119 @@
//------------------------------------------------------------------------------
// <copyright file="SqlBulkCopyColumnMapping.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
// Todo: rename the file
// Caution! ndp\fx\src\data\netmodule\sources needs to follow this change
namespace System.Data.SqlClient
{
using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlTypes;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
// -------------------------------------------------------------------------------------------------
// this class helps allows the user to create association between source- and targetcolumns
//
//
public sealed class SqlBulkCopyColumnMapping {
internal string _destinationColumnName;
internal int _destinationColumnOrdinal;
internal string _sourceColumnName;
internal int _sourceColumnOrdinal;
// devnote: we don't want the user to detect the columnordinal after WriteToServer call.
// _sourceColumnOrdinal(s) will be copied to _internalSourceColumnOrdinal when WriteToServer executes.
internal int _internalDestinationColumnOrdinal;
internal int _internalSourceColumnOrdinal; // -1 indicates an undetermined value
public string DestinationColumn {
get {
if (_destinationColumnName != null) {
return _destinationColumnName;
}
return string.Empty;
}
set {
_destinationColumnOrdinal = _internalDestinationColumnOrdinal = -1;
_destinationColumnName = value;
}
}
public int DestinationOrdinal {
get {
return _destinationColumnOrdinal;
}
set {
if (value >= 0) {
_destinationColumnName = null;
_destinationColumnOrdinal = _internalDestinationColumnOrdinal = value;
}
else {
throw ADP.IndexOutOfRange(value);
}
}
}
public string SourceColumn {
get {
if (_sourceColumnName != null) {
return _sourceColumnName;
}
return string.Empty;
}
set {
_sourceColumnOrdinal = _internalSourceColumnOrdinal = -1;
_sourceColumnName = value;
}
}
public int SourceOrdinal {
get {
return _sourceColumnOrdinal;
}
set {
if (value >= 0) {
_sourceColumnName = null;
_sourceColumnOrdinal = _internalSourceColumnOrdinal = value;
}
else {
throw ADP.IndexOutOfRange(value);
}
}
}
public SqlBulkCopyColumnMapping () {
_internalSourceColumnOrdinal = -1;
}
public SqlBulkCopyColumnMapping (string sourceColumn, string destinationColumn) {
SourceColumn = sourceColumn;
DestinationColumn = destinationColumn;
}
public SqlBulkCopyColumnMapping (int sourceColumnOrdinal, string destinationColumn) {
SourceOrdinal = sourceColumnOrdinal;
DestinationColumn = destinationColumn;
}
public SqlBulkCopyColumnMapping (string sourceColumn, int destinationOrdinal) {
SourceColumn = sourceColumn;
DestinationOrdinal = destinationOrdinal;
}
public SqlBulkCopyColumnMapping (int sourceColumnOrdinal, int destinationOrdinal) {
SourceOrdinal = sourceColumnOrdinal;
DestinationOrdinal = destinationOrdinal;
}
}
}

View File

@@ -0,0 +1,164 @@
//------------------------------------------------------------------------------
// <copyright file="SqlBulkCopyMappingCollection.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
// todo: rename the file
// Caution! ndp\fx\src\data\netmodule\sources needs to follow this name change
namespace System.Data.SqlClient
{
using System;
using System.Data;
using System.Data.Common;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
public sealed class SqlBulkCopyColumnMappingCollection : CollectionBase {
private enum MappingSchema {
Undefined = 0,
NamesNames = 1,
NemesOrdinals = 2,
OrdinalsNames = 3,
OrdinalsOrdinals = 4,
}
private bool _readOnly;
private MappingSchema _mappingSchema = MappingSchema.Undefined;
internal SqlBulkCopyColumnMappingCollection() {
}
public SqlBulkCopyColumnMapping this [int index] {
get {
return (SqlBulkCopyColumnMapping)this.List[index];
}
}
internal bool ReadOnly {
get {
return _readOnly;
}
set {
_readOnly = value;
}
}
public SqlBulkCopyColumnMapping Add(SqlBulkCopyColumnMapping bulkCopyColumnMapping) {
AssertWriteAccess();
Debug.Assert(ADP.IsEmpty(bulkCopyColumnMapping.SourceColumn) || bulkCopyColumnMapping._internalSourceColumnOrdinal == -1, "BulkLoadAmbigousSourceColumn");
if (((ADP.IsEmpty(bulkCopyColumnMapping.SourceColumn)) && (bulkCopyColumnMapping.SourceOrdinal == -1))
|| ((ADP.IsEmpty(bulkCopyColumnMapping.DestinationColumn))&&(bulkCopyColumnMapping.DestinationOrdinal == -1))) {
throw SQL.BulkLoadNonMatchingColumnMapping();
}
InnerList.Add(bulkCopyColumnMapping);
return bulkCopyColumnMapping;
}
public SqlBulkCopyColumnMapping Add(string sourceColumn, string destinationColumn) {
AssertWriteAccess();
SqlBulkCopyColumnMapping column = new SqlBulkCopyColumnMapping (sourceColumn, destinationColumn);
return Add(column);
}
public SqlBulkCopyColumnMapping Add(int sourceColumnIndex, string destinationColumn) {
AssertWriteAccess();
SqlBulkCopyColumnMapping column = new SqlBulkCopyColumnMapping (sourceColumnIndex, destinationColumn);
return Add(column);
}
public SqlBulkCopyColumnMapping Add(string sourceColumn, int destinationColumnIndex) {
AssertWriteAccess();
SqlBulkCopyColumnMapping column = new SqlBulkCopyColumnMapping (sourceColumn, destinationColumnIndex);
return Add(column);
}
public SqlBulkCopyColumnMapping Add(int sourceColumnIndex, int destinationColumnIndex) {
AssertWriteAccess();
SqlBulkCopyColumnMapping column = new SqlBulkCopyColumnMapping (sourceColumnIndex, destinationColumnIndex);
return Add(column);
}
private void AssertWriteAccess () {
if (ReadOnly) {
throw SQL.BulkLoadMappingInaccessible();
}
}
new public void Clear() {
AssertWriteAccess();
base.Clear();
}
public bool Contains(SqlBulkCopyColumnMapping value) {
return (-1 != InnerList.IndexOf(value));
}
public void CopyTo(SqlBulkCopyColumnMapping[] array, int index) {
InnerList.CopyTo(array, index);
}
internal void CreateDefaultMapping (int columnCount) {
for (int i=0; i<columnCount; i++) {
InnerList.Add(new SqlBulkCopyColumnMapping (i,i));
}
}
public int IndexOf(SqlBulkCopyColumnMapping value) {
return InnerList.IndexOf(value);
}
public void Insert(int index, SqlBulkCopyColumnMapping value) {
AssertWriteAccess();
InnerList.Insert(index, value);
}
public void Remove(SqlBulkCopyColumnMapping value) {
AssertWriteAccess();
InnerList.Remove(value);
}
new public void RemoveAt(int index) {
AssertWriteAccess();
base.RemoveAt(index);
}
internal void ValidateCollection () {
MappingSchema mappingSchema;
foreach (SqlBulkCopyColumnMapping a in this) {
if (a.SourceOrdinal != -1) {
if(a.DestinationOrdinal != -1) {
mappingSchema = MappingSchema.OrdinalsOrdinals;
}
else {
mappingSchema = MappingSchema.OrdinalsNames;
}
}
else {
if(a.DestinationOrdinal != -1) {
mappingSchema = MappingSchema.NemesOrdinals;
}
else {
mappingSchema = MappingSchema.NamesNames;
}
}
if (_mappingSchema == MappingSchema.Undefined) {
_mappingSchema = mappingSchema;
}
else {
if (_mappingSchema != mappingSchema) {
throw SQL.BulkLoadMappingsNamesOrOrdinalsOnly();
}
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <copyright file="SqlBulkCopyOptions.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">mithomas</owner>
// <owner current="true" primary="false">blained</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
[Flags]
public enum SqlBulkCopyOptions {
Default = 0,
KeepIdentity = 1 << 0,
CheckConstraints = 1 << 1,
TableLock = 1 << 2,
KeepNulls = 1 << 3,
FireTriggers = 1 << 4,
UseInternalTransaction = 1 << 5,
AllowEncryptedValueModifications = 1 << 6,
}
}

View File

@@ -0,0 +1,156 @@
//------------------------------------------------------------------------------
// <copyright file="SqlCachedBuffer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">[....]</owner>
// <owner current="true" primary="false">[....]</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Xml;
using System.Data.SqlTypes;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Runtime.CompilerServices;
// Caches the bytes returned from partial length prefixed datatypes, like XML
sealed internal class SqlCachedBuffer : System.Data.SqlTypes.INullable{
public static readonly SqlCachedBuffer Null = new SqlCachedBuffer();
private const int _maxChunkSize = 2048; // Arbitrary value for chunk size. Revisit this later for better perf
private List<byte[]> _cachedBytes;
private SqlCachedBuffer() {
// For constructing Null
}
private SqlCachedBuffer(List<byte[]> cachedBytes) {
_cachedBytes = cachedBytes;
}
internal List<byte[]> CachedBytes {
get { return _cachedBytes; }
}
// Reads off from the network buffer and caches bytes. Only reads one column value in the current row.
static internal bool TryCreate(SqlMetaDataPriv metadata, TdsParser parser, TdsParserStateObject stateObj, out SqlCachedBuffer buffer) {
int cb = 0;
ulong plplength;
byte[] byteArr;
List<byte[]> cachedBytes = new List<byte[]>();
buffer = null;
// the very first length is already read.
if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
return false;
}
// For now we only handle Plp data from the parser directly.
Debug.Assert(metadata.metaType.IsPlp, "SqlCachedBuffer call on a non-plp data");
do {
if (plplength == 0)
break;
do {
cb = (plplength > (ulong) _maxChunkSize) ? _maxChunkSize : (int)plplength ;
byteArr = new byte[cb];
if (!stateObj.TryReadPlpBytes(ref byteArr, 0, cb, out cb)) {
return false;
}
Debug.Assert(cb == byteArr.Length);
if (cachedBytes.Count == 0) {
// Add the Byte order mark if needed if we read the first array
AddByteOrderMark(byteArr, cachedBytes);
}
cachedBytes.Add(byteArr);
plplength -= (ulong)cb;
} while (plplength > 0);
if (!parser.TryPlpBytesLeft(stateObj, out plplength)) {
return false;
}
} while (plplength > 0);
Debug.Assert(stateObj._longlen == 0 && stateObj._longlenleft == 0);
buffer = new SqlCachedBuffer(cachedBytes);
return true;
}
private static void AddByteOrderMark(byte[] byteArr, List<byte[]> cachedBytes) {
// Need to find out if we should add byte order mark or not.
// We need to add this if we are getting ntext xml, not if we are getting binary xml
// Binary Xml always begins with the bytes 0xDF and 0xFF
// If we aren't getting these, then we are getting unicode xml
if ((byteArr.Length < 2 ) || (byteArr[0] != 0xDF) || (byteArr[1] != 0xFF)){
Debug.Assert(cachedBytes.Count == 0);
cachedBytes.Add(TdsEnums.XMLUNICODEBOMBYTES);
}
}
internal Stream ToStream() {
return new SqlCachedStream(this);
}
override public string ToString() {
if (IsNull)
throw new SqlNullValueException();
if (_cachedBytes.Count == 0) {
return String.Empty;
}
SqlXml sxml = new SqlXml(ToStream());
return sxml.Value;
}
internal SqlString ToSqlString() {
if (IsNull)
return SqlString.Null;
string str = ToString();
return new SqlString(str);
}
internal SqlXml ToSqlXml() {
SqlXml sx = new SqlXml(ToStream());
return sx;
}
// Prevent inlining so that reflection calls are not moved to caller that may be in a different assembly that may have a different grant set.
[MethodImpl(MethodImplOptions.NoInlining)]
internal XmlReader ToXmlReader() {
//XmlTextReader xr = new XmlTextReader(fragment, XmlNodeType.Element, null);
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.ConformanceLevel = ConformanceLevel.Fragment;
// Call internal XmlReader.CreateSqlReader from System.Xml.
// Signature: internal static XmlReader CreateSqlReader(Stream input, XmlReaderSettings settings, XmlParserContext inputContext);
MethodInfo createSqlReaderMethodInfo = typeof(System.Xml.XmlReader).GetMethod("CreateSqlReader", BindingFlags.Static | BindingFlags.NonPublic);
object[] args = new object[3] { ToStream(), readerSettings, null };
XmlReader xr;
new System.Security.Permissions.ReflectionPermission(System.Security.Permissions.ReflectionPermissionFlag.MemberAccess).Assert();
try {
xr = (XmlReader)createSqlReaderMethodInfo.Invoke(null, args);
}
finally {
System.Security.Permissions.ReflectionPermission.RevertAssert();
}
return xr;
}
public bool IsNull {
get {
return (_cachedBytes == null) ? true : false ;
}
}
}
}

View File

@@ -0,0 +1,33 @@
//------------------------------------------------------------------------------
// <copyright file="SqlException.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
// <owner current="true" primary="true">balnee</owner>
// <owner current="true" primary="false">krishnib</owner>
//------------------------------------------------------------------------------
namespace System.Data.SqlClient
{
using System;
/// <summary>
/// Abstract base class for all TCE encryption algorithms. It exposes two functions
/// 1. Encrypt - This function is used by SqlClient under the covers to transparently encrypt TCE enabled column data.
/// 2. Decrypt - This function is used by SqlClient under the covers to transparently decrypt TCE enabled column data.
/// </summary>
internal abstract class SqlClientEncryptionAlgorithm
{
/// <summary>
/// Encrypts the plainText with a column encryption key
/// </summary>
/// <param name="plainText">Plain text value to be encrypted</param>
/// <returns></returns>
internal abstract byte[] EncryptData(byte[] plainText);
/// <summary>
/// Decrypts the cipherText with a column encryption key
/// </summary>
/// <param name="cipherText">Ciphertext value to be decrypted</param>
/// <returns></returns>
internal abstract byte[] DecryptData(byte[] cipherText);
}
}

Some files were not shown because too many files have changed in this diff Show More