Beispiel #1
0
 internal LocalCommand(string commandText, SqlParameterCollection parameters,  int returnParameterIndex, CommandType cmdType, SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
     Debug.Assert(0 <= commandText.Length, "no text");
     this.CommandText = commandText;
     this.Parameters = parameters;
     this.ReturnParameterIndex = returnParameterIndex;
     this.CmdType = cmdType;
     this.ColumnEncryptionSetting = columnEncryptionSetting;
 }
Beispiel #2
0
 internal LocalCommand(string commandText, SqlParameterCollection parameters, int returnParameterIndex, CommandType cmdType, SqlCommandColumnEncryptionSetting columnEncryptionSetting)
 {
     Debug.Assert(0 <= commandText.Length, "no text");
     CommandText          = commandText;
     Parameters           = parameters;
     ReturnParameterIndex = returnParameterIndex;
     CmdType = cmdType;
     ColumnEncryptionSetting = columnEncryptionSetting;
 }
Beispiel #3
0
        public void TestSqlCommandCloneColumnEncryptionSettingSpecified(SqlCommandColumnEncryptionSetting sqlCommandColumnEncryptionSetting)
        {
            using (SqlConnection sqlConnection = new SqlConnection())
            {
                using (SqlCommand sqlCommand1 = new SqlCommand(
                           cmdText: "Data source = localhost; Connect Timeout = 65534;",
                           connection: sqlConnection,
                           transaction: null,
                           columnEncryptionSetting: sqlCommandColumnEncryptionSetting))
                {
                    Assert.Equal(sqlCommandColumnEncryptionSetting, sqlCommand1.ColumnEncryptionSetting);

                    // Clone the above sqlCommand1 and verify the Column Encryption Setting is propogated correctly.
                    using (SqlCommand sqlCommand2 = sqlCommand1.Clone())
                    {
                        Assert.Equal(sqlCommandColumnEncryptionSetting, sqlCommand2.ColumnEncryptionSetting);
                    }
                }
            }
        }
Beispiel #4
0
        public void TestSqlCommandSetColumnEncryptionSetting(
            SqlConnectionColumnEncryptionSetting sqlConnectionColumnEncryptionSetting,
            SqlCommandColumnEncryptionSetting sqlCommandColumnEncryptionSetting_1,
            SqlCommandColumnEncryptionSetting sqlCommandColumnEncryptionSetting_2
            )
        {
            MethodInfo SetColumnEncryptionSettingMethod = typeof(SqlCommand).GetMethod("SetColumnEncryptionSetting", BindingFlags.Instance | BindingFlags.NonPublic);

            string[] connectionStrings =
            {
                string.Format(@"Data source = localhost; Column Encryption Setting = {0}",                                        sqlConnectionColumnEncryptionSetting).ToString(),
                string.Format(@"Data source = localhost; Column Encryption Setting = {0}; Enclave Attestation Url=www.foo.com; ", sqlConnectionColumnEncryptionSetting).ToString()
            };

            foreach (var connectionString in connectionStrings)
            {
                using (SqlConnection sqlConnection = new SqlConnection(connectionString))
                {
                    using (SqlCommand sqlCommand = new SqlCommand(@"select 1", sqlConnection, transaction: null, columnEncryptionSetting: sqlCommandColumnEncryptionSetting_1))
                    {
                        // Set the first column encryption setting.
                        SetColumnEncryptionSettingMethod.Invoke(sqlCommand, new object[] { sqlCommandColumnEncryptionSetting_1 });

                        // Simulate setting of the second column encryption setting. If its the same setting, it should succeed.
                        // If its different than the one used before, it should throw an exception.
                        if (sqlCommandColumnEncryptionSetting_1 == sqlCommandColumnEncryptionSetting_2)
                        {
                            SetColumnEncryptionSettingMethod.Invoke(sqlCommand, new object[] { sqlCommandColumnEncryptionSetting_2 });
                            Assert.Equal(sqlCommandColumnEncryptionSetting_1, sqlCommand.ColumnEncryptionSetting);
                        }
                        else
                        {
                            TargetInvocationException exception = Assert.Throws <TargetInvocationException>(() => SetColumnEncryptionSettingMethod.Invoke(sqlCommand, new object[] { sqlCommandColumnEncryptionSetting_2 }));
                            string expectedMessage = "SqlCommandColumnEncryptionSetting should be identical on all commands (SelectCommand, InsertCommand, UpdateCommand, DeleteCommand) when doing batch updates.";
                            Assert.Equal(expectedMessage, exception.InnerException.Message);
                        }
                    }
                }
            }
        }
Beispiel #5
0
 public void SetSqlCommandColumnEncryptionSettingAppropriately(SqlConnectionColumnEncryptionSetting sqlConnectionColumnEncryptionSetting, SqlCommandColumnEncryptionSetting sqlCommandColumnEncryptionSetting)
 {
     using (SqlConnection sqlConnectionEnclaveEnabled = new SqlConnection(DefaultConnectionString(columnEncryptionSetting: sqlConnectionColumnEncryptionSetting, fEnclaveEnabled: false, enclaveAttestationUrl: "")))
         using (SqlConnection sqlConnectionEnclaveDisabled = new SqlConnection(DefaultConnectionString(columnEncryptionSetting: sqlConnectionColumnEncryptionSetting, fEnclaveEnabled: true, enclaveAttestationUrl: "www.foo.coms")))
         {
             using (SqlCommand sqlCommandEnclaveEnabled = new SqlCommand(@"select * from sys.objects", sqlConnectionEnclaveEnabled, transaction: null, columnEncryptionSetting: sqlCommandColumnEncryptionSetting))
                 using (SqlCommand sqlCommandEnclaveDisabled = new SqlCommand(@"select * from sys.objects", sqlConnectionEnclaveDisabled, transaction: null, columnEncryptionSetting: sqlCommandColumnEncryptionSetting))
                 {
                     Assert.Equal(sqlCommandColumnEncryptionSetting, sqlCommandEnclaveEnabled.ColumnEncryptionSetting);
                     Assert.Equal(sqlCommandColumnEncryptionSetting, sqlCommandEnclaveDisabled.ColumnEncryptionSetting);
                 }
         }
 }
Beispiel #6
0
        public void NullValueTests(string connString, ConnStringColumnEncryptionSetting connStringSetting, SqlCommandColumnEncryptionSetting commandSetting, ReturnValueSetting nullReturnValue)
        {
            switch (connStringSetting)
            {
            case ConnStringColumnEncryptionSetting.Enabled:
                connString += "; Column Encryption Setting=Enabled";
                break;

            case ConnStringColumnEncryptionSetting.Disabled:
                connString += "; Column Encryption Setting=Disabled";
                break;
            }

            using (SqlConnection sqlConn = new SqlConnection(connString))
            {
                sqlConn.Open();
                object       value1;
                object       value2;
                SqlParameter param;

                // Create a command similarly
                using (SqlCommand cmd = new SqlCommand(String.Format("SELECT c1 FROM [{0}] ORDER BY c2 ASC", tableName),
                                                       sqlConn, null, commandSetting))
                {
                    using (SqlDataReader reader = cmd.ExecuteReader())
                    {
                        Assert.True(reader.Read(), "No rows fetched first time");

                        // Depending on the various flags verify the results
                        value1 = reader.GetProviderSpecificValue(0);

                        // Read next row
                        Assert.True(reader.Read(), "No rows fetched second time");
                        value2 = reader.GetProviderSpecificValue(0);
                    }
                }

                using (SqlCommand cmd2 = new SqlCommand((ReturnValueSetting.Null == nullReturnValue) ? UdfName : UdfNameNotNull,
                                                        sqlConn, null, commandSetting))
                {
                    cmd2.CommandType = CommandType.StoredProcedure;
                    param            = cmd2.Parameters.Add("@foo", SqlDbType.Int);
                    param.Direction  = ParameterDirection.ReturnValue;
                    param.Value      = new System.Data.SqlTypes.SqlInt32(1);
                    cmd2.ExecuteNonQuery();
                }

                switch (commandSetting)
                {
                case SqlCommandColumnEncryptionSetting.Disabled:
                    // everything should be varbinary
                    Assert.True(value1 is System.Data.SqlTypes.SqlBinary, "Unexpected type");
                    Assert.True(value2 is System.Data.SqlTypes.SqlBinary, "Unexpected type");
                    Assert.True(param.Value is System.Data.SqlTypes.SqlBinary, "Unexpected Return value");
                    break;

                case SqlCommandColumnEncryptionSetting.Enabled:
                // Everything should be int
                // intentional fall through
                case SqlCommandColumnEncryptionSetting.ResultSetOnly:
                    // Again expect int
                    Assert.True(value1 is System.Data.SqlTypes.SqlInt32, "Unexpected type");
                    Assert.True(value2 is System.Data.SqlTypes.SqlInt32, "Unexpected type");
                    Assert.True(10 == ((System.Data.SqlTypes.SqlInt32)value2).Value, "Unexpected Value");
                    if (SqlCommandColumnEncryptionSetting.ResultSetOnly == commandSetting)
                    {
                        // For ResultSetOnly we don't expect to see plaintext for return values
                        Assert.True(param.Value is System.Data.SqlTypes.SqlBinary, "Unexpected Return value");
                    }
                    else
                    {
                        Assert.True(param.Value is System.Data.SqlTypes.SqlInt32, "Unexpected Return value");
                    }
                    break;

                case SqlCommandColumnEncryptionSetting.UseConnectionSetting:
                    // Examine the connection string setting to figure out what to expect
                    if (ConnStringColumnEncryptionSetting.Enabled == connStringSetting)
                    {
                        // Expect int
                        Assert.True(value1 is System.Data.SqlTypes.SqlInt32, "Unexpected type");
                        Assert.True(value2 is System.Data.SqlTypes.SqlInt32, "Unexpected type");
                        Assert.True(10 == ((System.Data.SqlTypes.SqlInt32)value2).Value, "Unexpected Value");
                        Assert.True(param.Value is System.Data.SqlTypes.SqlInt32, "Unexpected Return value");
                    }
                    else
                    {
                        // Expect varbinary
                        Assert.True(value1 is System.Data.SqlTypes.SqlBinary, "Unexpected type");
                        Assert.True(value2 is System.Data.SqlTypes.SqlBinary, "Unexpected type");
                        Assert.True(param.Value is System.Data.SqlTypes.SqlBinary, "Unexpected Return value");
                    }
                    break;
                }
            }
        }
Beispiel #7
0
 static void Postfix(SqlCommand __instance, string cmdText, SqlConnection connection, SqlTransaction transaction, SqlCommandColumnEncryptionSetting columnEncryptionSetting)
 {
     __instance.CommandText = SqlCommandSetProcessor.Process(connection, __instance, cmdText);
 }
        internal static object GetNullSqlValue(
                SqlBuffer nullVal, 
                SqlMetaDataPriv md, 
                SqlCommandColumnEncryptionSetting columnEncryptionSetting,
                SqlInternalConnectionTds connection) {
            SqlDbType type = md.type;

            if (type == SqlDbType.VarBinary && // if its a varbinary
                md.isEncrypted &&// and encrypted
                ShouldHonorTceForRead(columnEncryptionSetting, connection)){
                type = md.baseTI.type; // the use the actual (plaintext) type
            }

            switch (type) {
                case SqlDbType.Real:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Single);
                    break;

                case SqlDbType.Float:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Double);
                    break;

                case SqlDbType.Udt:
                case SqlDbType.Binary:
                case SqlDbType.VarBinary:
                case SqlDbType.Image:
                    nullVal.SqlBinary = SqlBinary.Null;
                    break;

                case SqlDbType.UniqueIdentifier:
                    nullVal.SqlGuid = SqlGuid.Null;
                    break;

                case SqlDbType.Bit:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Boolean);
                    break;

                case SqlDbType.TinyInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Byte);
                    break;

                case SqlDbType.SmallInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int16);
                    break;

                case SqlDbType.Int:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int32);
                    break;

                case SqlDbType.BigInt:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Int64);
                    break;

                case SqlDbType.Char:
                case SqlDbType.VarChar:
                case SqlDbType.NChar:
                case SqlDbType.NVarChar:
                case SqlDbType.Text:
                case SqlDbType.NText:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.String);
                    break;

                case SqlDbType.Decimal:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Decimal);
                    break;

                case SqlDbType.DateTime:
                case SqlDbType.SmallDateTime:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime);
                    break;

                case SqlDbType.Money:
                case SqlDbType.SmallMoney:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Money);
                    break;

                case SqlDbType.Variant:
                    // DBNull.Value will have to work here
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Empty);
                    break;

                case SqlDbType.Xml:
                    nullVal.SqlCachedBuffer = SqlCachedBuffer.Null;
                    break;

                case SqlDbType.Date:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Date);
                    break;

                case SqlDbType.Time:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.Time);
                    break;
                    
                case SqlDbType.DateTime2:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTime2);
                    break;
                    
                case SqlDbType.DateTimeOffset:
                    nullVal.SetToNullOfType(SqlBuffer.StorageType.DateTimeOffset);
                    break;

                case SqlDbType.Timestamp:
                    // Dev10 Bug #479607 - this should have been the same as SqlDbType.Binary, but it's a rejected breaking change
                    // Dev10 Bug #752790 - don't assert when it does happen
                    break;

                default:
                    Debug.Assert(false, "unknown null sqlType!" + md.type.ToString());
                    break;
            }

            return nullVal;
        }
        internal bool TryReadSqlValue(SqlBuffer value,
                                      SqlMetaDataPriv md,
                                      int length,
                                      TdsParserStateObject stateObj,
                                      SqlCommandColumnEncryptionSetting columnEncryptionOverride, 
                                      string columnName) {
            bool isPlp = md.metaType.IsPlp;
            byte tdsType = md.tdsType;

            Debug.Assert(isPlp || !IsNull(md.metaType, (ulong)length), "null value should not get here!");
            if (isPlp) {
                // We must read the column value completely, no matter what length is passed in
                length = Int32.MaxValue;
            }

            //DEVNOTE: When modifying the following routines (for deserialization) please pay attention to 
            // deserialization code in DecryptWithKey () method and modify it accordingly.
            switch (tdsType) {
                case TdsEnums.SQLDECIMALN:
                case TdsEnums.SQLNUMERICN:
                    if (!TryReadSqlDecimal(value, length, md.precision, md.scale, stateObj)) {
                        return false;
                    }
                    break;

                case TdsEnums.SQLUDT:
                case TdsEnums.SQLBINARY:
                case TdsEnums.SQLBIGBINARY:
                case TdsEnums.SQLBIGVARBINARY:
                case TdsEnums.SQLVARBINARY:
                case TdsEnums.SQLIMAGE:
                    byte[] b = null;

                    // If varbinary(max), we only read the first chunk here, expecting the caller to read the rest
                    if (isPlp) {
                        // If we are given -1 for length, then we read the entire value,
                        // otherwise only the requested amount, usually first chunk.
                        int ignored;
                        if (!stateObj.TryReadPlpBytes(ref b, 0, length, out ignored)) {
                            return false;
                        }
                    }
                    else {
                        //Debug.Assert(length > 0 && length < (long)(Int32.MaxValue), "Bad length for column");
                        b = new byte[length];
                        if (!stateObj.TryReadByteArray(b, 0, length)) {
                            return false;
                        }
                    }

                    if (md.isEncrypted 
                        && ((columnEncryptionOverride == SqlCommandColumnEncryptionSetting.Enabled
                            || columnEncryptionOverride == SqlCommandColumnEncryptionSetting.ResultSetOnly)
                            || (columnEncryptionOverride == SqlCommandColumnEncryptionSetting.UseConnectionSetting
                                && _connHandler != null && _connHandler.ConnectionOptions != null
                                && _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled))) {
                        try {
                            // CipherInfo is present, decrypt and read
                            byte[] unencryptedBytes = SqlSecurityUtility.DecryptWithKey(b, md.cipherMD, _connHandler.ConnectionOptions.DataSource);

                            if (unencryptedBytes != null) {
                                DeserializeUnencryptedValue(value, unencryptedBytes, md, stateObj, md.NormalizationRuleVersion);
                            }
                        }
                        catch (Exception e) {
                            throw SQL.ColumnDecryptionFailed(columnName, null, e);
                        }
                    }
                    else {
                        value.SqlBinary = new SqlBinary(b, true);   // doesn't copy the byte array
                    }
                    break;

                case TdsEnums.SQLCHAR:
                case TdsEnums.SQLBIGCHAR:
                case TdsEnums.SQLVARCHAR:
                case TdsEnums.SQLBIGVARCHAR:
                case TdsEnums.SQLTEXT:
                case TdsEnums.SQLNCHAR:
                case TdsEnums.SQLNVARCHAR:
                case TdsEnums.SQLNTEXT:
                    if (!TryReadSqlStringValue(value, tdsType, length, md.encoding, isPlp, stateObj)) {
                        return false;
                    }
                    break;

                case TdsEnums.SQLXMLTYPE:
                    // We store SqlCachedBuffer here, so that we can return either SqlBinary, SqlString or SqlXmlReader.
                    SqlCachedBuffer sqlBuf;
                    if (!SqlCachedBuffer.TryCreate(md, this, stateObj, out sqlBuf)) {
                        return false;
                    }

                    value.SqlCachedBuffer = sqlBuf;
                    break;

                case TdsEnums.SQLDATE:
                case TdsEnums.SQLTIME: 
                case TdsEnums.SQLDATETIME2:
                case TdsEnums.SQLDATETIMEOFFSET:
                    if (!TryReadSqlDateTime(value, tdsType, length, md.scale, stateObj)) {
                        return false;
                    }
                    break;
                    
                default:
                    Debug.Assert(!isPlp, "ReadSqlValue calling ReadSqlValueInternal with plp data");
                    if (!TryReadSqlValueInternal(value, tdsType, length, stateObj)) {
                        return false;
                    }
                    break;
            }

            Debug.Assert((stateObj._longlen == 0) && (stateObj._longlenleft == 0), "ReadSqlValue did not read plp field completely, longlen =" + stateObj._longlen.ToString((IFormatProvider)null) + ",longlenleft=" + stateObj._longlenleft.ToString((IFormatProvider)null));
            return true;
        }
        /// <summary>
        /// Determines if a column value should be transparently decrypted (based on SqlCommand and Connection String settings).
        /// </summary>
        /// <returns>true if the value should be transparently decrypted, false otherwise</returns>
        internal static bool ShouldHonorTceForRead (SqlCommandColumnEncryptionSetting columnEncryptionSetting, 
            SqlInternalConnectionTds connection) {

            // Command leve setting trumps all
            switch (columnEncryptionSetting) {
                case SqlCommandColumnEncryptionSetting.Disabled:
                    return false;
                case SqlCommandColumnEncryptionSetting.Enabled:
                    return true;
                case SqlCommandColumnEncryptionSetting.ResultSetOnly:
                    return true;
                default:
                    // Check connection level setting!
                    Debug.Assert(SqlCommandColumnEncryptionSetting.UseConnectionSetting == columnEncryptionSetting,
                        "Unexpected value for command level override");
                    return (connection != null && connection.ConnectionOptions != null &&
                        connection.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled);
            }
        }
        private bool TryCommonProcessMetaData(TdsParserStateObject stateObj, _SqlMetaData col, SqlTceCipherInfoTable? cipherTable, bool fColMD, SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
            byte byteLen;
            UInt32 userType;

            // read user type - 4 bytes Yukon, 2 backwards
            if (IsYukonOrNewer) {
                if (!stateObj.TryReadUInt32(out userType)) {
                    return false;
                }
            }
            else {
                ushort userTypeShort;
                if (!stateObj.TryReadUInt16(out userTypeShort)) {
                    return false;
                }
                userType = userTypeShort;
            }

            // read flags and set appropriate flags in structure
            byte flags;
            if (!stateObj.TryReadByte(out flags)) {
                return false;
            }

            col.updatability = (byte)((flags & TdsEnums.Updatability) >> 2);
            col.isNullable = (TdsEnums.Nullable == (flags & TdsEnums.Nullable));
            col.isIdentity = (TdsEnums.Identity == (flags & TdsEnums.Identity));

            // read second byte of column metadata flags
            if (!stateObj.TryReadByte(out flags)) {
                return false;
            }
            
            col.isColumnSet = (TdsEnums.IsColumnSet == (flags & TdsEnums.IsColumnSet));
            if (fColMD && _serverSupportsColumnEncryption) {
                col.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted));
            }

            // Read TypeInfo
            if (!TryProcessTypeInfo (stateObj, col, userType)) {
                return false;
            }

            // Read tablename if present
            if (col.metaType.IsLong && !col.metaType.IsPlp) {
                if (_isYukon) {
                    int  unusedLen = 0xFFFF;      //We ignore this value
                    if (!TryProcessOneTable(stateObj, ref unusedLen, out col.multiPartTableName)) {
                        return false;
                    }
                } else {
                    ushort shortLen;
                    if (!stateObj.TryReadUInt16(out shortLen)) {
                        return false;
                    }
                    string tableName;
                    if (!stateObj.TryReadString(shortLen, out tableName)) {
                        return false;
                    }
                    // with Sql2000 this is returned as an unquoted mix of catalog.owner.table
                    // all of which may contain "." and unable to parse correctly from the string alone
                    // example "select * from pubs..[A.B.C.D.E]" AND only when * will contain a image/text/ntext column
                    // by delay parsing from execute to SqlDataReader.GetSchemaTable to enable more scenarios
                    col.multiPartTableName = new MultiPartTableName(tableName);
                }
            }

            // Read the TCE column cryptoinfo
            if (fColMD && _serverSupportsColumnEncryption && col.isEncrypted) {
                // If the column is encrypted, we should have a valid cipherTable
                if (cipherTable.HasValue && !TryProcessTceCryptoMetadata (stateObj, col, cipherTable.Value, columnEncryptionSetting, isReturnValue: false)) {
                    return false;
                }
            }

            // Read the column name 
            if (!stateObj.TryReadByte(out byteLen)) {
                return false;
            }
            if (!stateObj.TryReadString(byteLen, out col.column)) {
                return false;
            }

            // We get too many DONE COUNTs from the server, causing too meany StatementCompleted event firings.
            // We only need to fire this event when we actually have a meta data stream with 0 or more rows.
            stateObj._receivedColMetaData = true;
            return true;
        }
        internal bool TryProcessMetaData(int cColumns, TdsParserStateObject stateObj, out _SqlMetaDataSet metaData, SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
            Debug.Assert(cColumns > 0, "should have at least 1 column in metadata!");

            // Read the cipher info table first 
            SqlTceCipherInfoTable? cipherTable = null;
            if (_serverSupportsColumnEncryption) {
                if (!TryProcessCipherInfoTable (stateObj, out cipherTable)) {
                    metaData = null;
                    return false;
                }
            }

            // Read the ColumnData fields
            _SqlMetaDataSet newMetaData = new _SqlMetaDataSet(cColumns, cipherTable);
            for (int i = 0; i < cColumns; i++) {
                if (!TryCommonProcessMetaData(stateObj, newMetaData[i], cipherTable, fColMD: true, columnEncryptionSetting: columnEncryptionSetting)) {
                    metaData = null;
                    return false;
                }
            }

            // DEVNOTE: cipherTable is discarded at this point since its no longer needed.
            metaData = newMetaData;
            return true;
        }
        internal bool TryProcessTceCryptoMetadata (TdsParserStateObject stateObj, 
            SqlMetaDataPriv col, 
            SqlTceCipherInfoTable? cipherTable, 
            SqlCommandColumnEncryptionSetting columnEncryptionSetting,
            bool isReturnValue) {
            Debug.Assert(isReturnValue == (cipherTable == null), "Ciphertable is not set iff this is a return value");

            // Read the ordinal into cipher table
            ushort index = 0;
            UInt32 userType;

            // For return values there is not cipher table and no ordinal.
            if (cipherTable.HasValue) {
                if (!stateObj.TryReadUInt16(out index)) {
                    return false;
                }

                // validate the index (ordinal passed)
                if (index >= cipherTable.Value.Size) {
                    Bid.Trace("<sc.TdsParser.TryProcessTceCryptoMetadata|TCE> Incorrect ordinal received %d, max tab size: %d\n", index, cipherTable.Value.Size);
                    throw SQL.ParsingErrorValue(ParsingErrorState.TceInvalidOrdinalIntoCipherInfoTable, index);
                }
            }

            // Read the user type
            if (!stateObj.TryReadUInt32(out userType)) {
                return false;
            }

            // Read the base TypeInfo
            col.baseTI = new SqlMetaDataPriv();
            if (!TryProcessTypeInfo(stateObj, col.baseTI, userType)) {
                return false;
            }

            // Read the cipher algorithm Id
            byte cipherAlgorithmId;
            if (!stateObj.TryReadByte(out cipherAlgorithmId)) {
                return false;
            }

            string cipherAlgorithmName = null;
            if (TdsEnums.CustomCipherAlgorithmId == cipherAlgorithmId) {
                // Custom encryption algorithm, read the name
                byte nameSize;
                if (!stateObj.TryReadByte(out nameSize)) {
                    return false;
                }

                if (!stateObj.TryReadString(nameSize, out cipherAlgorithmName)) {
                    return false;
                }
            }

            // Read Encryption Type. 
            byte encryptionType;
            if (!stateObj.TryReadByte(out encryptionType)) {
                return false;
            }

            // Read Normalization Rule Version.
            byte normalizationRuleVersion;
            if (!stateObj.TryReadByte(out normalizationRuleVersion)) {
                return false;
            }

            Debug.Assert(col.cipherMD == null, "col.cipherMD should be null in TryProcessTceCryptoMetadata.");

            // Check if TCE is enable and if it is set the crypto MD for the column.
            // TCE is enabled if the command is set to enabled or to resultset only and this is not a return value
            // or if it is set to use connection setting and the connection has TCE enabled.
            if ((columnEncryptionSetting == SqlCommandColumnEncryptionSetting.Enabled ||
                (columnEncryptionSetting == SqlCommandColumnEncryptionSetting.ResultSetOnly && !isReturnValue)) ||
                (columnEncryptionSetting == SqlCommandColumnEncryptionSetting.UseConnectionSetting &&
                _connHandler != null && _connHandler.ConnectionOptions != null &&
                _connHandler.ConnectionOptions.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled)) {
                col.cipherMD = new SqlCipherMetadata(cipherTable.HasValue ? (SqlTceCipherInfoEntry?)cipherTable.Value[index] : null,
                                                        index,
                                                        cipherAlgorithmId: cipherAlgorithmId,
                                                        cipherAlgorithmName: cipherAlgorithmName,
                                                        encryptionType: encryptionType,
                                                        normalizationRuleVersion: normalizationRuleVersion);
            }
            else {
                // If TCE is disabled mark the MD as not encrypted.
                col.isEncrypted = false;
            }

            return true;
        }
        internal bool TryProcessReturnValue(int length,
                                            TdsParserStateObject stateObj,
                                            out SqlReturnValue returnValue,
                                            SqlCommandColumnEncryptionSetting columnEncryptionSetting) {
            returnValue = null;
            SqlReturnValue rec = new SqlReturnValue();
            rec.length = length;        // In Yukon this length is -1
            if (_isYukon) {
                if (!stateObj.TryReadUInt16(out rec.parmIndex)) {
                    return false;
                }
            }
            byte len;
            if (!stateObj.TryReadByte(out len)) { // Length of parameter name
                return false;
            }

            rec.parameter = null;
            if (len > 0) {
                if (!stateObj.TryReadString(len, out rec.parameter)) {
                    return false;
                }
            }

            // read status and ignore
            byte ignored;
            if (!stateObj.TryReadByte(out ignored)) {
                return false;
            }

            UInt32 userType;

            // read user type - 4 bytes Yukon, 2 backwards
            if (IsYukonOrNewer) {
                if (!stateObj.TryReadUInt32(out userType)) {
                    return false;
                }
            }
            else {
                ushort userTypeShort;
                if (!stateObj.TryReadUInt16(out userTypeShort)) {
                    return false;
                }
                userType = userTypeShort;
            }

            // Read off the flags.
            // The first byte is ignored since it doesn't contain any interesting information.
            byte flags;
            if (!stateObj.TryReadByte(out flags)) {
                return false;
            }

            if (!stateObj.TryReadByte(out flags)) {
                return false;
            }

            // Check if the column is encrypted.
            if (_serverSupportsColumnEncryption) {
                rec.isEncrypted = (TdsEnums.IsEncrypted == (flags & TdsEnums.IsEncrypted));
            }

            // read the type
            byte tdsType;
            if (!stateObj.TryReadByte(out tdsType)) {
                return false;
            }

            // read the MaxLen
            // For xml datatpyes, there is no tokenLength
            int tdsLen;

            if (tdsType == TdsEnums.SQLXMLTYPE) {
                tdsLen = TdsEnums.SQL_USHORTVARMAXLEN;
            }
            else if (IsVarTimeTds(tdsType))
                tdsLen = 0;  // placeholder until we read the scale, just make sure it's not SQL_USHORTVARMAXLEN
            else if (tdsType == TdsEnums.SQLDATE) {
                tdsLen = 3;
            }
            else {
                if (!TryGetTokenLength(tdsType, stateObj, out tdsLen)) {
                    return false;
                }
            }

            rec.metaType = MetaType.GetSqlDataType(tdsType, userType, tdsLen);
            rec.type = rec.metaType.SqlDbType;

            // always use the nullable type for parameters if Shiloh or later
            // Sphinx sometimes sends fixed length return values
            if (_isShiloh) {
                rec.tdsType = rec.metaType.NullableType;
                rec.isNullable = true;
                if (tdsLen == TdsEnums.SQL_USHORTVARMAXLEN) {
                    Debug.Assert(_isYukon, "plp data from pre-Yukon server");
                    rec.metaType = MetaType.GetMaxMetaTypeFromMetaType(rec.metaType);
                }
            }
            else {      // For sphinx, keep the fixed type if that is what is returned
                if (rec.metaType.NullableType == tdsType)
                    rec.isNullable = true;

                rec.tdsType = (byte)tdsType;
            }

            if (rec.type == SqlDbType.Decimal) {
                if (!stateObj.TryReadByte(out rec.precision)) {
                    return false;
                }
                if (!stateObj.TryReadByte(out rec.scale)) {
                    return false;
                }
            }

            if (rec.metaType.IsVarTime) {
                if (!stateObj.TryReadByte(out rec.scale)) {
                    return false;
                }
            }

            if (tdsType == TdsEnums.SQLUDT) {
                if (!TryProcessUDTMetaData((SqlMetaDataPriv) rec, stateObj)) {
                    return false;
                }
            }

            if (rec.type == SqlDbType.Xml) {
                // Read schema info
                byte schemapresent;
                if (!stateObj.TryReadByte(out schemapresent)) {
                    return false;
                }

                if ((schemapresent & 1) != 0) {
                    if (!stateObj.TryReadByte(out len)) {
                        return false;
                    }
                    if (len != 0) {
                        if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionDatabase)) {
                            return false;
                        }
                    }

                    if (!stateObj.TryReadByte(out len)) {
                        return false;
                    }
                    if (len != 0) {
                        if (!stateObj.TryReadString(len, out rec.xmlSchemaCollectionOwningSchema)) {
                            return false;
                        }
                    }

                    short slen;
                    if (!stateObj.TryReadInt16(out slen)) {
                        return false;
                    }

                    if (slen != 0) {
                        if (!stateObj.TryReadString(slen, out rec.xmlSchemaCollectionName)) {
                            return false;
                        }
                    }

                }
            }
            else if (_isShiloh && rec.metaType.IsCharType) {
                // read the collation for 8.x servers
                if (!TryProcessCollation(stateObj, out rec.collation)) {
                    return false;
                }

                int codePage = GetCodePage(rec.collation, stateObj);

                // if the column lcid is the same as the default, use the default encoder
                if (codePage == _defaultCodePage) {
                    rec.codePage = _defaultCodePage;
                    rec.encoding = _defaultEncoding;
                }
                else {
                    rec.codePage = codePage;
                    rec.encoding = System.Text.Encoding.GetEncoding(rec.codePage);
                }
            }

            // For encrypted parameters, read the unencrypted type and encryption information.
            if (_serverSupportsColumnEncryption && rec.isEncrypted) {
                if (!TryProcessTceCryptoMetadata(stateObj, rec, cipherTable: null, columnEncryptionSetting: columnEncryptionSetting, isReturnValue: true)) {
                    return false;
                }
            }

            // for now we coerce return values into a SQLVariant, not good...
            bool isNull = false;
            ulong valLen;
            if (!TryProcessColumnHeaderNoNBC(rec, stateObj, out isNull, out valLen)) {
                return false;
            }

            // always read as sql types
            Debug.Assert(valLen < (ulong)(Int32.MaxValue), "ProcessReturnValue received data size > 2Gb");

            int intlen = valLen > (ulong)(Int32.MaxValue) ? Int32.MaxValue : (int)valLen;

            if (rec.metaType.IsPlp) {
                intlen = Int32.MaxValue;    // If plp data, read it all
            }

            if (isNull) {
                GetNullSqlValue(rec.value, rec, SqlCommandColumnEncryptionSetting.Disabled, _connHandler);
            }
            else {
                // We should never do any decryption here, so pass disabled as the command encryption override.
                // We only read the binary value and decryption will be performed by OnReturnValue().
                if (!TryReadSqlValue(rec.value, rec, intlen, stateObj, SqlCommandColumnEncryptionSetting.Disabled, columnName:null /*Not used*/)) {
                    return false;
                }
            }

            returnValue = rec;
            return true;
        }