//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // // [....] // [....] //------------------------------------------------------------------------------ namespace System.Data.SqlClient { using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.ProviderBase; using System.Data.Sql; using System.Data.SqlTypes; using System.Diagnostics; using System.Reflection; using System.Text; using System.Threading; using System.Security; using System.Globalization; using Microsoft.SqlServer.Server; // for SMI metadata internal enum CallbackType { Read = 0, Write = 1 } internal enum EncryptionOptions { OFF, ON, NOT_SUP, REQ, LOGIN } internal enum PreLoginHandshakeStatus { Successful, InstanceFailure } internal enum PreLoginOptions { VERSION, ENCRYPT, INSTANCE, THREADID, MARS, TRACEID, FEDAUTHREQUIRED, NUMOPT, LASTOPT = 255 } internal enum RunBehavior { UntilDone = 1, // 0001 binary ReturnImmediately = 2, // 0010 binary Clean = 5, // 0101 binary - Clean AND UntilDone Attention = 13 // 1101 binary - Clean AND UntilDone AND Attention } internal enum TdsParserState { Closed, OpenNotLoggedIn, OpenLoggedIn, Broken, } /// /// Struct encapsulating the data to be sent to the server as part of Federated Authentication Feature Extension. /// internal struct FederatedAuthenticationFeatureExtensionData { internal TdsEnums.FedAuthLibrary libraryType; internal bool fedAuthRequiredPreLoginResponse; internal SqlAuthenticationMethod authentication; internal byte[] accessToken; } /// /// Represents a single encrypted value for a CEK. It contains the encrypted CEK, /// the store type, name,the key path and encryption algorithm. /// internal struct SqlEncryptionKeyInfo { internal byte[] encryptedKey; // the encrypted "column encryption key" internal int databaseId; internal int cekId; internal int cekVersion; internal byte[] cekMdVersion; internal string keyPath; internal string keyStoreName; internal string algorithmName; internal byte normalizationRuleVersion; } /// /// Encapsulates one entry in the CipherInfo table sent as part of Colmetadata. /// The same CEK is encrypted multiple times with different master keys (for master key /// rotation scenario) We need to keep all these around until we can resolve the CEK /// using the correct master key. /// internal struct SqlTceCipherInfoEntry { /// /// List of Column Encryption Key Information. /// private readonly List _columnEncryptionKeyValues; /// /// Key Ordinal. /// private readonly int _ordinal; /// /// Database ID /// private int _databaseId; /// /// Cek ID /// private int _cekId; /// /// Cek Version /// private int _cekVersion; /// /// Cek MD Version /// private byte[] _cekMdVersion; /// /// Return the ordinal. /// internal int Ordinal { get { return _ordinal; } } /// /// Return the DatabaseID. /// internal int DatabaseId { get { return _databaseId; } } /// /// Return the CEK ID. /// internal int CekId { get { return _cekId; } } /// /// Return the CEK Version. /// internal int CekVersion { get { return _cekVersion; } } /// /// Return the CEK MD Version. /// internal byte[] CekMdVersion { get { return _cekMdVersion; } } /// /// Return the list of Column Encryption Key Values. /// internal List ColumnEncryptionKeyValues { get { return _columnEncryptionKeyValues; } } /// /// Add an entry to the list of ColumnEncryptionKeyValues. /// /// /// /// /// /// /// /// /// internal void Add(byte[] encryptedKey, int databaseId, int cekId, int cekVersion, byte[] cekMdVersion, string keyPath, string keyStoreName, string algorithmName) { Debug.Assert(_columnEncryptionKeyValues != null, "_columnEncryptionKeyValues should already be initialized."); SqlEncryptionKeyInfo encryptionKey = new SqlEncryptionKeyInfo(); encryptionKey.encryptedKey = encryptedKey; encryptionKey.databaseId = databaseId; encryptionKey.cekId = cekId; encryptionKey.cekVersion = cekVersion; encryptionKey.cekMdVersion = cekMdVersion; encryptionKey.keyPath = keyPath; encryptionKey.keyStoreName = keyStoreName; encryptionKey.algorithmName = algorithmName; _columnEncryptionKeyValues.Add(encryptionKey); if (0 == _databaseId) { _databaseId = databaseId; _cekId = cekId; _cekVersion = cekVersion; _cekMdVersion = cekMdVersion; } else { Debug.Assert(_databaseId == databaseId); Debug.Assert(_cekId == cekId); Debug.Assert(_cekVersion == cekVersion); Debug.Assert (_cekMdVersion != null && cekMdVersion != null && _cekMdVersion.Length == _cekMdVersion.Length); } } /// /// Constructor. /// /// internal SqlTceCipherInfoEntry(int ordinal = 0) : this() { _ordinal = ordinal; _databaseId = 0; _cekId = 0; _cekVersion = 0; _cekMdVersion = null; _columnEncryptionKeyValues = new List(); } } /// /// Represents a table with various CEKs used in a resultset. Each entry corresponds to one (unique) CEK. The CEK /// may have been encrypted using multiple master keys (giving us multiple CEK values). All these values form one single /// entry in this table. /// internal struct SqlTceCipherInfoTable { private readonly SqlTceCipherInfoEntry [] keyList; internal SqlTceCipherInfoTable (int tabSize) { Debug.Assert (0 < tabSize, "Invalid Table Size"); keyList = new SqlTceCipherInfoEntry[tabSize]; } internal SqlTceCipherInfoEntry this [int index] { get { Debug.Assert (index < keyList.Length, "Invalid index specified."); return keyList[index]; } set { Debug.Assert (index < keyList.Length, "Invalid index specified."); keyList[index] = value; } } internal int Size { get { return keyList.Length; } } } sealed internal class SqlCollation { // First 20 bits of info field represent the lcid, bits 21-25 are compare options private const uint IgnoreCase = 1 << 20; // bit 21 - IgnoreCase private const uint IgnoreNonSpace = 1 << 21; // bit 22 - IgnoreNonSpace / IgnoreAccent private const uint IgnoreWidth = 1 << 22; // bit 23 - IgnoreWidth private const uint IgnoreKanaType = 1 << 23; // bit 24 - IgnoreKanaType private const uint BinarySort = 1 << 24; // bit 25 - BinarySort internal const uint MaskLcid = 0xfffff; private const int LcidVersionBitOffset = 28; private const uint MaskLcidVersion = unchecked((uint)(0xf << LcidVersionBitOffset)); private const uint MaskCompareOpt = IgnoreCase | IgnoreNonSpace | IgnoreWidth | IgnoreKanaType | BinarySort; internal uint info; internal byte sortId; static int FirstSupportedCollationVersion(int lcid) { // NOTE: switch-case works ~3 times faster in this case than search with Dictionary switch (lcid) { case 1044: return 2; // Norwegian_100_BIN case 1047: return 2; // Romansh_100_BIN case 1056: return 2; // Urdu_100_BIN case 1065: return 2; // Persian_100_BIN case 1068: return 2; // Azeri_Latin_100_BIN case 1070: return 2; // Upper_Sorbian_100_BIN case 1071: return 1; // ----n_FYROM_90_BIN case 1081: return 1; // Indic_General_90_BIN case 1082: return 2; // Maltese_100_BIN case 1083: return 2; // Sami_Norway_100_BIN case 1087: return 1; // Kazakh_90_BIN case 1090: return 2; // Turkmen_100_BIN case 1091: return 1; // Uzbek_Latin_90_BIN case 1092: return 1; // Tatar_90_BIN case 1093: return 2; // Bengali_100_BIN case 1101: return 2; // Assamese_100_BIN case 1105: return 2; // Tibetan_100_BIN case 1106: return 2; // Welsh_100_BIN case 1107: return 2; // Khmer_100_BIN case 1108: return 2; // Lao_100_BIN case 1114: return 1; // Syriac_90_BIN case 1121: return 2; // Nepali_100_BIN case 1122: return 2; // Frisian_100_BIN case 1123: return 2; // Pashto_100_BIN case 1125: return 1; // Divehi_90_BIN case 1133: return 2; // Bashkir_100_BIN case 1146: return 2; // Mapudungan_100_BIN case 1148: return 2; // Mohawk_100_BIN case 1150: return 2; // Breton_100_BIN case 1152: return 2; // Uighur_100_BIN case 1153: return 2; // Maori_100_BIN case 1155: return 2; // Corsican_100_BIN case 1157: return 2; // Yakut_100_BIN case 1164: return 2; // Dari_100_BIN case 2074: return 2; // Serbian_Latin_100_BIN case 2092: return 2; // Azeri_Cyrillic_100_BIN case 2107: return 2; // Sami_Sweden_Finland_100_BIN case 2143: return 2; // Tamazight_100_BIN case 3076: return 1; // Chinese_Hong_Kong_Stroke_90_BIN case 3098: return 2; // Serbian_Cyrillic_100_BIN case 5124: return 2; // Chinese_Traditional_Pinyin_100_BIN case 5146: return 2; // Bosnian_Latin_100_BIN case 8218: return 2; // Bosnian_Cyrillic_100_BIN default: return 0; // other LCIDs have collation with version 0 } } internal int LCID { // First 20 bits of info field represent the lcid get { return unchecked((int)(info & MaskLcid)); } set { int lcid = value & (int)MaskLcid; Debug.Assert(lcid == value, "invalid set_LCID value"); // VSTFDEVDIV 479474: some new Katmai LCIDs do not have collation with version = 0 // since user has no way to specify collation version, we set the first (minimal) supported version for these collations int versionBits = FirstSupportedCollationVersion(lcid) << LcidVersionBitOffset; Debug.Assert((versionBits & MaskLcidVersion) == versionBits, "invalid version returned by FirstSupportedCollationVersion"); // combine the current compare options with the new locale ID and its first supported version info = (info & MaskCompareOpt) | unchecked((uint)lcid) | unchecked((uint)versionBits); } } internal SqlCompareOptions SqlCompareOptions { get { SqlCompareOptions options = SqlCompareOptions.None; if (0 != (info & IgnoreCase)) options |= SqlCompareOptions.IgnoreCase; if (0 != (info & IgnoreNonSpace)) options |= SqlCompareOptions.IgnoreNonSpace; if (0 != (info & IgnoreWidth)) options |= SqlCompareOptions.IgnoreWidth; if (0 != (info & IgnoreKanaType)) options |= SqlCompareOptions.IgnoreKanaType; if (0 != (info & BinarySort)) options |= SqlCompareOptions.BinarySort; return options; } set { Debug.Assert((value & SqlString.x_iValidSqlCompareOptionMask) == value, "invalid set_SqlCompareOptions value"); uint tmp = 0; if (0 != (value & SqlCompareOptions.IgnoreCase)) tmp |= IgnoreCase; if (0 != (value & SqlCompareOptions.IgnoreNonSpace)) tmp |= IgnoreNonSpace; if (0 != (value & SqlCompareOptions.IgnoreWidth)) tmp |= IgnoreWidth; if (0 != (value & SqlCompareOptions.IgnoreKanaType)) tmp |= IgnoreKanaType; if (0 != (value & SqlCompareOptions.BinarySort)) tmp |= BinarySort; info = (info & MaskLcid) | tmp; } } internal string TraceString() { return String.Format(/*IFormatProvider*/ null, "(LCID={0}, Opts={1})", this.LCID, (int)this.SqlCompareOptions); } static internal bool AreSame(SqlCollation a, SqlCollation b) { if (a == null || b == null) { return a == b; } else { return a.info == b.info && a.sortId == b.sortId; } } } internal class RoutingInfo { internal byte Protocol { get; private set; } internal UInt16 Port { get; private set; } internal string ServerName { get; private set; } internal RoutingInfo(byte protocol, UInt16 port, string servername) { Protocol = protocol; Port = port; ServerName = servername; } } sealed internal class SqlEnvChange { internal byte type; internal byte oldLength; internal int newLength; // 7206 TDS changes makes this length an int internal int length; internal string newValue; internal string oldValue; internal byte[] newBinValue; internal byte[] oldBinValue; internal long newLongValue; internal long oldLongValue; internal SqlCollation newCollation; internal SqlCollation oldCollation; internal RoutingInfo newRoutingInfo; } sealed internal class SqlLogin { internal SqlAuthenticationMethod authentication = SqlAuthenticationMethod.NotSpecified; // Authentication type internal int timeout; // login timeout internal bool userInstance = false; // user instance internal string hostName = ""; // client machine name internal string userName = ""; // user id internal string password = ""; // password internal string applicationName = ""; // application name internal string serverName = ""; // server name internal string language = ""; // initial language internal string database = ""; // initial database internal string attachDBFilename = ""; // DB filename to be attached internal string newPassword = ""; // new password for reset password internal bool useReplication = false; // user login for replication internal bool useSSPI = false; // use integrated security internal int packetSize = SqlConnectionString.DEFAULT.Packet_Size; // packet size internal bool readOnlyIntent = false; // read-only intent internal SqlCredential credential; // user id and password in SecureString internal SecureString newSecurePassword; // new password in SecureString for resetting pasword } sealed internal class SqlLoginAck { internal string programName; internal byte majorVersion; internal byte minorVersion; internal short buildNum; internal bool isVersion8; internal UInt32 tdsVersion; } sealed internal class SqlFedAuthInfo { internal string spn; internal string stsurl; public override string ToString() { return String.Format(CultureInfo.InvariantCulture, "STSURL: {0}, SPN: {1}", stsurl ?? String.Empty, spn ?? String.Empty); } } sealed internal class SqlFedAuthToken { internal UInt32 dataLen; internal byte[] accessToken; internal long expirationFileTime; } sealed internal class _SqlMetaData : SqlMetaDataPriv, ICloneable { internal string column; internal string baseColumn; internal MultiPartTableName multiPartTableName; internal readonly int ordinal; internal byte updatability; // two bit field (0 is read only, 1 is updatable, 2 is updatability unknown) internal byte tableNum; internal bool isDifferentName; internal bool isKey; internal bool isHidden; internal bool isExpression; internal bool isIdentity; internal bool isColumnSet; internal byte op; // for altrow-columns only internal ushort operand; // for altrow-columns only internal _SqlMetaData(int ordinal) : base() { this.ordinal = ordinal; } internal string serverName { get { return multiPartTableName.ServerName; } } internal string catalogName { get { return multiPartTableName.CatalogName; } } internal string schemaName { get { return multiPartTableName.SchemaName; } } internal string tableName { get { return multiPartTableName.TableName; } } internal bool IsNewKatmaiDateTimeType { get { return SqlDbType.Date == type || SqlDbType.Time == type || SqlDbType.DateTime2 == type || SqlDbType.DateTimeOffset == type; } } internal bool IsLargeUdt { get { return type == SqlDbType.Udt && length == Int32.MaxValue; } } public object Clone() { _SqlMetaData result = new _SqlMetaData(ordinal); result.CopyFrom(this); result.column = column; result.baseColumn = baseColumn; result.multiPartTableName = multiPartTableName; result.updatability = updatability; result.tableNum = tableNum; result.isDifferentName = isDifferentName; result.isKey = isKey; result.isHidden = isHidden; result.isExpression = isExpression; result.isIdentity = isIdentity; result.isColumnSet = isColumnSet; result.op = op; result.operand = operand; return result; } } sealed internal class _SqlMetaDataSet : ICloneable { internal ushort id; // for altrow-columns only internal int[] indexMap; internal int visibleColumns; internal DataTable schemaTable; internal readonly SqlTceCipherInfoTable? cekTable; // table of "column encryption keys" used for this metadataset internal readonly _SqlMetaData[] metaDataArray; internal _SqlMetaDataSet(int count, SqlTceCipherInfoTable? cipherTable) { cekTable = cipherTable; metaDataArray = new _SqlMetaData[count]; for(int i = 0; i < metaDataArray.Length; ++i) { metaDataArray[i] = new _SqlMetaData(i); } } private _SqlMetaDataSet(_SqlMetaDataSet original) { this.id = original.id; // although indexMap is not immutable, in practice it is initialized once and then passed around this.indexMap = original.indexMap; this.visibleColumns = original.visibleColumns; this.schemaTable = original.schemaTable; if (original.metaDataArray == null) { metaDataArray = null; } else { metaDataArray = new _SqlMetaData[original.metaDataArray.Length]; for (int idx=0; idx altMetaDataSetArray; internal _SqlMetaDataSet metaDataSet; internal _SqlMetaDataSetCollection () { altMetaDataSetArray = new List<_SqlMetaDataSet>(); } internal void SetAltMetaData(_SqlMetaDataSet altMetaDataSet) { // VSTFDEVDIV 479675: if altmetadata with same id is found, override it rather than adding a new one int newId = altMetaDataSet.id; for (int i = 0; i < altMetaDataSetArray.Count; i++) { if (altMetaDataSetArray[i].id == newId) { // override the existing metadata with the same id altMetaDataSetArray[i] = altMetaDataSet; return; } } // if we did not find metadata to override, add as new altMetaDataSetArray.Add(altMetaDataSet); } internal _SqlMetaDataSet GetAltMetaData(int id) { foreach (_SqlMetaDataSet altMetaDataSet in altMetaDataSetArray) { if (altMetaDataSet.id == id) { return altMetaDataSet; } } Debug.Assert (false, "Can't match up altMetaDataSet with given id"); return null; } public object Clone() { _SqlMetaDataSetCollection result = new _SqlMetaDataSetCollection(); result.metaDataSet = metaDataSet == null ? null : (_SqlMetaDataSet)metaDataSet.Clone(); foreach (_SqlMetaDataSet set in altMetaDataSetArray) { result.altMetaDataSetArray.Add((_SqlMetaDataSet)set.Clone()); } return result; } } /// /// Represents Encryption related information of the cipher data. /// internal class SqlCipherMetadata { /// /// Cipher Info Entry. /// private SqlTceCipherInfoEntry? _sqlTceCipherInfoEntry; /// /// Encryption Algorithm Id. /// private readonly byte _cipherAlgorithmId; /// /// Encryption Algorithm Name. /// private readonly string _cipherAlgorithmName; /// /// Encryption Type. /// private readonly byte _encryptionType; /// /// Normalization Rule Version. /// private readonly byte _normalizationRuleVersion; /// /// Encryption Algorithm Handle. /// private SqlClientEncryptionAlgorithm _sqlClientEncryptionAlgorithm; /// /// Sql Encryption Key Info. /// private SqlEncryptionKeyInfo? _sqlEncryptionKeyInfo; /// /// Ordinal (into the Cek Table). /// private readonly ushort _ordinal; /// /// Return the Encryption Info Entry. /// internal SqlTceCipherInfoEntry? EncryptionInfo { get { return _sqlTceCipherInfoEntry; } set { Debug.Assert(!_sqlTceCipherInfoEntry.HasValue, "We can only set the EncryptionInfo once."); _sqlTceCipherInfoEntry = value; } } /// /// Return the cipher's encryption algorithm id. /// internal byte CipherAlgorithmId { get { return _cipherAlgorithmId; } } /// /// Return the cipher's encryption algorithm name (could be null). /// internal string CipherAlgorithmName { get { return _cipherAlgorithmName; } } /// /// Return EncryptionType (Deterministic, Randomized, etc.) /// internal byte EncryptionType { get { return _encryptionType; } } /// /// Return normalization rule version. /// internal byte NormalizationRuleVersion { get { return _normalizationRuleVersion; } } /// /// Return the cipher encyrption algorithm handle. /// internal SqlClientEncryptionAlgorithm CipherAlgorithm { get { return _sqlClientEncryptionAlgorithm; } set { Debug.Assert(_sqlClientEncryptionAlgorithm == null, "_sqlClientEncryptionAlgorithm should not be set more than once."); _sqlClientEncryptionAlgorithm = value; } } /// /// Return Encryption Key Info. /// internal SqlEncryptionKeyInfo? EncryptionKeyInfo { get { return _sqlEncryptionKeyInfo; } set { Debug.Assert(!_sqlEncryptionKeyInfo.HasValue, "_sqlEncryptionKeyInfo should not be set more than once."); _sqlEncryptionKeyInfo = value; } } /// /// Return Ordinal into Cek Table. /// internal ushort CekTableOrdinal { get { return _ordinal; } } /// /// Constructor. /// /// /// /// /// /// internal SqlCipherMetadata (SqlTceCipherInfoEntry? sqlTceCipherInfoEntry, ushort ordinal, byte cipherAlgorithmId, string cipherAlgorithmName, byte encryptionType, byte normalizationRuleVersion) { Debug.Assert(!sqlTceCipherInfoEntry.Equals(default(SqlTceCipherInfoEntry)), "sqlTceCipherInfoEntry should not be un-initialized."); _sqlTceCipherInfoEntry = sqlTceCipherInfoEntry; _ordinal = ordinal; _cipherAlgorithmId = cipherAlgorithmId; _cipherAlgorithmName = cipherAlgorithmName; _encryptionType = encryptionType; _normalizationRuleVersion = normalizationRuleVersion; _sqlEncryptionKeyInfo = null; } /// /// Do we have an handle to the cipher encryption algorithm already ? /// /// internal bool IsAlgorithmInitialized() { return (null != _sqlClientEncryptionAlgorithm) ? true : false; } } internal class SqlMetaDataPriv { internal SqlDbType type; // SqlDbType enum value internal byte tdsType; // underlying tds type internal byte precision = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) internal byte scale = TdsEnums.UNKNOWN_PRECISION_SCALE; // give default of unknown (-1) internal int length; internal SqlCollation collation; internal int codePage; internal Encoding encoding; internal bool isNullable; internal bool isMultiValued = false; // UDT specific metadata // server metadata info // additional temporary UDT meta data internal string udtDatabaseName; internal string udtSchemaName; internal string udtTypeName; internal string udtAssemblyQualifiedName; // on demand internal Type udtType; // Xml specific metadata internal string xmlSchemaCollectionDatabase; internal string xmlSchemaCollectionOwningSchema; internal string xmlSchemaCollectionName; internal MetaType metaType; // cached metaType // Structured type-specific metadata internal string structuredTypeDatabaseName; internal string structuredTypeSchemaName; internal string structuredTypeName; internal IList structuredFields; internal bool isEncrypted; // TCE encrypted? internal SqlMetaDataPriv baseTI; // for encrypted columns, represents the TYPE_INFO for plaintext value internal SqlCipherMetadata cipherMD; // Cipher related metadata for encrypted columns. internal SqlMetaDataPriv() { } internal virtual void CopyFrom(SqlMetaDataPriv original) { this.type = original.type; this.tdsType = original.tdsType; this.precision = original.precision; this.scale = original.scale; this.length = original.length; this.collation = original.collation; this.codePage = original.codePage; this.encoding = original.encoding; this.isNullable = original.isNullable; this.isMultiValued = original.isMultiValued; this.udtDatabaseName = original.udtDatabaseName; this.udtSchemaName = original.udtSchemaName; this.udtTypeName = original.udtTypeName; this.udtAssemblyQualifiedName = original.udtAssemblyQualifiedName; this.udtType = original.udtType; this.xmlSchemaCollectionDatabase = original.xmlSchemaCollectionDatabase; this.xmlSchemaCollectionOwningSchema = original.xmlSchemaCollectionOwningSchema; this.xmlSchemaCollectionName = original.xmlSchemaCollectionName; this.metaType = original.metaType; // this.structuredTypeDatabaseName = original.structuredTypeDatabaseName; this.structuredTypeSchemaName = original.structuredTypeSchemaName; this.structuredTypeName = original.structuredTypeName; this.structuredFields = original.structuredFields; } /// /// Is the algorithm handle for the cipher encryption initialized ? /// /// internal bool IsAlgorithmInitialized() { if (null != cipherMD) { return cipherMD.IsAlgorithmInitialized(); } return false; } /// /// Returns the normalization rule version byte. /// /// internal byte NormalizationRuleVersion { get { if (null != cipherMD){ return cipherMD.NormalizationRuleVersion; } return 0x00; } } } /// /// Class encapsulating additional information when sending encrypted input parameters. /// sealed internal class SqlColumnEncryptionInputParameterInfo { /// /// Metadata of the parameter to write the TYPE_INFO of the unencrypted column data type. /// private readonly SmiParameterMetaData _smiParameterMetadata; /// /// Column encryption related metadata. /// private readonly SqlCipherMetadata _cipherMetadata; /// /// Serialized format for a subset of members. /// Does not include _smiParameterMetadata's serialization. /// private readonly byte[] _serializedWireFormat; /// /// Return the SMI Parameter Metadata. /// internal SmiParameterMetaData ParameterMetadata { get { return _smiParameterMetadata; } } /// /// Return the serialized format for some members. /// This is pre-calculated and cached since members are immutable. /// Does not include _smiParameterMetadata's serialization. /// internal byte[] SerializedWireFormat { get { return _serializedWireFormat; } } /// /// Constructor. /// /// /// internal SqlColumnEncryptionInputParameterInfo(SmiParameterMetaData smiParameterMetadata, SqlCipherMetadata cipherMetadata) { Debug.Assert(smiParameterMetadata != null, "smiParameterMetadata should not be null."); Debug.Assert(cipherMetadata != null, "cipherMetadata should not be null"); Debug.Assert(cipherMetadata.EncryptionKeyInfo.HasValue, "cipherMetadata.EncryptionKeyInfo.HasValue should be true."); _smiParameterMetadata = smiParameterMetadata; _cipherMetadata = cipherMetadata; _serializedWireFormat = SerializeToWriteFormat(); } /// /// Serializes some data members to wire format. /// private byte[] SerializeToWriteFormat() { int totalLength = 0; // CipherAlgorithmId. totalLength += sizeof(byte); // Encryption Type. totalLength += sizeof(byte); // Database id of the encryption key. totalLength += sizeof(int); // Id of the encryption key. totalLength += sizeof(int); // Version of the encryption key. totalLength += sizeof(int); // Metadata version of the encryption key. totalLength += _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length; // Normalization Rule Version. totalLength += sizeof(byte); byte[] serializedWireFormat = new byte[totalLength]; // No:of bytes consumed till now. Running variable. int consumedBytes = 0; // 1 - Write Cipher Algorithm Id. serializedWireFormat[consumedBytes++] = _cipherMetadata.CipherAlgorithmId; // 2 - Write Encryption Type. serializedWireFormat[consumedBytes++] = _cipherMetadata.EncryptionType; // 3 - Write the database id of the encryption key. SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.databaseId, serializedWireFormat, ref consumedBytes); // 4 - Write the id of the encryption key. SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.cekId, serializedWireFormat, ref consumedBytes); // 5 - Write the version of the encryption key. SerializeIntIntoBuffer(_cipherMetadata.EncryptionKeyInfo.Value.cekVersion, serializedWireFormat, ref consumedBytes); // 6 - Write the metadata version of the encryption key. Buffer.BlockCopy(_cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion, 0, serializedWireFormat, consumedBytes, _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length); consumedBytes += _cipherMetadata.EncryptionKeyInfo.Value.cekMdVersion.Length; // 7 - Write Normalization Rule Version. serializedWireFormat[consumedBytes++] = _cipherMetadata.NormalizationRuleVersion; return serializedWireFormat; } /// /// Serializes an int into the provided buffer and offset. /// private void SerializeIntIntoBuffer(int value, byte[] buffer, ref int offset) { buffer[offset++] = (byte)(value & 0xff); buffer[offset++] = (byte)((value >> 8) & 0xff); buffer[offset++] = (byte)((value >> 16) & 0xff); buffer[offset++] = (byte)((value >> 24) & 0xff); } } sealed internal class _SqlRPC { internal string rpcName; internal string databaseName; // Used for UDTs internal ushort ProcID; // Used instead of name internal ushort options; internal SqlParameter[] parameters; internal byte[] paramoptions; internal int? recordsAffected; internal int cumulativeRecordsAffected; internal int errorsIndexStart; internal int errorsIndexEnd; internal SqlErrorCollection errors; internal int warningsIndexStart; internal int warningsIndexEnd; internal SqlErrorCollection warnings; internal bool needsFetchParameterEncryptionMetadata; internal string GetCommandTextOrRpcName() { if (TdsEnums.RPC_PROCID_EXECUTESQL == ProcID) { // Param 0 is the actual sql executing return (string)parameters[0].Value; } else { return rpcName; } } } sealed internal class SqlReturnValue : SqlMetaDataPriv { internal ushort parmIndex; //Yukon or later only internal string parameter; internal readonly SqlBuffer value; internal SqlReturnValue() : base() { value = new SqlBuffer(); } } internal struct MultiPartTableName { private string _multipartName; private string _serverName; private string _catalogName; private string _schemaName; private string _tableName; internal MultiPartTableName(string[] parts) { _multipartName = null; _serverName = parts[0]; _catalogName = parts[1]; _schemaName = parts[2]; _tableName = parts[3]; } internal MultiPartTableName(string multipartName) { _multipartName = multipartName; _serverName = null; _catalogName = null; _schemaName = null; _tableName = null; } internal string ServerName { get { ParseMultipartName(); return _serverName; } set { _serverName = value; } } internal string CatalogName { get { ParseMultipartName(); return _catalogName; } set { _catalogName = value; } } internal string SchemaName { get { ParseMultipartName(); return _schemaName; } set { _schemaName = value; } } internal string TableName { get { ParseMultipartName(); return _tableName; } set { _tableName = value; } } private void ParseMultipartName() { if (null != _multipartName) { string[] parts = MultipartIdentifier.ParseMultipartIdentifier(_multipartName, "[\"", "]\"", Res.SQL_TDSParserTableName, false); _serverName = parts[0]; _catalogName = parts[1]; _schemaName = parts[2]; _tableName = parts[3]; _multipartName = null; } } internal static readonly MultiPartTableName Null = new MultiPartTableName(new string[] {null, null, null, null}); } }