You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -0,0 +1 @@
|
||||
2a4b96c02a36a45bbc09c39c729d107f10314af7
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 ;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user