/// <summary> /// Encode Network Message Header /// </summary> /// <param name="encoder"></param> private void EncodeNetworkMessageHeader(BinaryEncoder encoder) { // byte[0..3] UADPVersion value 1 (for now) // byte[4..7] UADPFlags encoder.WriteByte("VersionFlags", (byte)(UADPVersion | (byte)UADPFlags)); if ((UADPFlags & UADPFlagsEncodingMask.ExtendedFlags1) != 0) { encoder.WriteByte("ExtendedFlags1", (byte)ExtendedFlags1); } if ((ExtendedFlags1 & ExtendedFlags1EncodingMask.ExtendedFlags2) != 0) { encoder.WriteByte("ExtendedFlags2", (byte)ExtendedFlags2); } if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.PublisherId) != 0) { PublisherIdTypeEncodingMask publisherIdType = (PublisherIdTypeEncodingMask)((byte)ExtendedFlags1 & kPublishedIdTypeUsedBits); switch (publisherIdType) { case PublisherIdTypeEncodingMask.Byte: encoder.WriteByte("PublisherId", Convert.ToByte(PublisherId)); break; case PublisherIdTypeEncodingMask.UInt16: encoder.WriteUInt16("PublisherId", Convert.ToUInt16(PublisherId)); break; case PublisherIdTypeEncodingMask.UInt32: encoder.WriteUInt32("PublisherId", Convert.ToUInt32(PublisherId)); break; case PublisherIdTypeEncodingMask.UInt64: encoder.WriteUInt64("PublisherId", Convert.ToUInt64(PublisherId)); break; case PublisherIdTypeEncodingMask.String: encoder.WriteString("PublisherId", Convert.ToString(PublisherId)); break; default: // Reserved - no type provided break; } } if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.DataSetClassId) != 0) { encoder.WriteGuid("DataSetClassId", DataSetClassId); } }
/// <summary> /// Encode Group Message Header /// </summary> /// <param name="encoder"></param> private void EncodeGroupMessageHeader(BinaryEncoder encoder) { if ((NetworkMessageContentMask & (UadpNetworkMessageContentMask.GroupHeader | UadpNetworkMessageContentMask.WriterGroupId | UadpNetworkMessageContentMask.GroupVersion | UadpNetworkMessageContentMask.NetworkMessageNumber | UadpNetworkMessageContentMask.SequenceNumber)) != UadpNetworkMessageContentMask.None) { encoder.WriteByte("GroupFlags", (byte)GroupFlags); } if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.WriterGroupId) != 0) { encoder.WriteUInt16("WriterGroupId", WriterGroupId); } if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.GroupVersion) != 0) { encoder.WriteUInt32("GroupVersion", GroupVersion); } if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.NetworkMessageNumber) != 0) { encoder.WriteUInt16("NetworkMessageNumber", NetworkMessageNumber); } if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.SequenceNumber) != 0) { encoder.WriteUInt16("SequenceNumber", SequenceNumber); } }
/// <summary> /// Encode security header /// </summary> /// <param name="encoder"></param> private void EncodeSecurityHeader(BinaryEncoder encoder) { if ((ExtendedFlags1 & ExtendedFlags1EncodingMask.Security) != 0) { encoder.WriteByte("SecurityFlags", (byte)SecurityFlags); encoder.WriteUInt32("SecurityTokenId", SecurityTokenId); encoder.WriteByte("NonceLength", NonceLength); MessageNonce = new byte[NonceLength]; encoder.WriteByteArray("MessageNonce", MessageNonce); if ((SecurityFlags & SecurityFlagsEncodingMask.SecurityFooter) != 0) { encoder.WriteUInt16("SecurityFooterSize", SecurityFooterSize); } } }
/// <summary> /// Saves object in an binary stream. /// </summary> /// <param name="context">The context user.</param> /// <param name="encoder">The encoder to write to.</param> /// <param name="attributesToSave">The masks indicating what attributes to write.</param> public override void Save(ISystemContext context, BinaryEncoder encoder, AttributesToSave attributesToSave) { base.Save(context, encoder, attributesToSave); if ((attributesToSave & AttributesToSave.EventNotifier) != 0) { encoder.WriteByte(null, m_eventNotifier); } }
/// <summary> /// Encode Payload Header /// </summary> /// <param name="encoder"></param> private void EncodePayloadHeader(BinaryEncoder encoder) { if ((NetworkMessageContentMask & UadpNetworkMessageContentMask.PayloadHeader) != 0) { encoder.WriteByte("Count", (byte)DataSetMessages.Count); // Collect DataSetSetMessages headers for (int index = 0; index < DataSetMessages.Count; index++) { UadpDataSetMessage uadpDataSetMessage = DataSetMessages[index] as UadpDataSetMessage; if (uadpDataSetMessage != null && uadpDataSetMessage.DataSet != null) { encoder.WriteUInt16("DataSetWriterId", uadpDataSetMessage.DataSetWriterId); } } } }
/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, ChannelToken token, object messageBody, bool isRequest, out bool limitsExceeded) { limitsExceeded = false; bool success = false; BufferCollection chunksToProcess = null; try { // calculate chunk sizes. int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize; int maxCipherBlocks = maxCipherTextSize / EncryptionBlockSize; int maxPlainTextSize = maxCipherBlocks * EncryptionBlockSize; int maxPayloadSize = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int headerSize = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; // write the body to stream. ArraySegmentStream ostrm = new ArraySegmentStream( BufferManager, SendBufferSize, headerSize, maxPayloadSize); // check for encodeable body. IEncodeable encodeable = messageBody as IEncodeable; if (encodeable != null) { // debug code used to verify that message aborts are handled correctly. // int maxMessageSize = Quotas.MessageContext.MaxMessageSize; // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue; BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext); // Quotas.MessageContext.MaxMessageSize = maxMessageSize; } // check for raw bytes. ArraySegment <byte>?rawBytes = messageBody as ArraySegment <byte>?; if (rawBytes != null) { BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count); encoder.Close(); } chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage"); // ensure there is at least one chunk. if (chunksToProcess.Count == 0) { byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage"); chunksToProcess.Add(new ArraySegment <byte>(buffer, 0, 0)); } BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity); int messageSize = 0; for (int ii = 0; ii < chunksToProcess.Count; ii++) { ArraySegment <byte> chunkToProcess = chunksToProcess[ii]; // nothing more to do if limits exceeded. if (limitsExceeded) { BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage"); continue; } MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext); // check if the message needs to be aborted. if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii + 1)) { encoder.WriteUInt32(null, messageType | TcpMessageType.Abort); // replace the body in the chunk with an error message. BinaryEncoder errorEncoder = new BinaryEncoder( chunkToProcess.Array, chunkToProcess.Offset, chunkToProcess.Count, Quotas.MessageContext); WriteErrorMessageBody(errorEncoder, (isRequest) ? StatusCodes.BadRequestTooLarge : StatusCodes.BadResponseTooLarge); int size = errorEncoder.Close(); chunkToProcess = new ArraySegment <byte>(chunkToProcess.Array, chunkToProcess.Offset, size); limitsExceeded = true; } // check if the message is complete. else if (ii == chunksToProcess.Count - 1) { encoder.WriteUInt32(null, messageType | TcpMessageType.Final); } // more chunks to follow. else { encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate); } int count = 0; count += TcpMessageLimits.SequenceHeaderSize; count += chunkToProcess.Count; count += SymmetricSignatureSize; // calculate the padding. int padding = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // reserve one byte for the padding size. count++; if (count % EncryptionBlockSize != 0) { padding = EncryptionBlockSize - (count % EncryptionBlockSize); } count += padding; } count += TcpMessageLimits.SymmetricHeaderSize; encoder.WriteUInt32(null, (uint)count); encoder.WriteUInt32(null, ChannelId); encoder.WriteUInt32(null, token.TokenId); uint sequenceNumber = GetNewSequenceNumber(); encoder.WriteUInt32(null, sequenceNumber); encoder.WriteUInt32(null, requestId); // skip body. strm.Seek(chunkToProcess.Count, SeekOrigin.Current); // update message size count. messageSize += chunkToProcess.Count; // write padding. if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { for (int jj = 0; jj <= padding; jj++) { encoder.WriteByte(null, (byte)padding); } } if (SecurityMode != MessageSecurityMode.None) { // calculate and write signature. byte[] signature = Sign(token, new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position), isRequest); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } } if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // encrypt the data. ArraySegment <byte> dataToEncrypt = new ArraySegment <byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position - TcpMessageLimits.SymmetricHeaderSize); Encrypt(token, dataToEncrypt, isRequest); } // add the header into chunk. chunksToSend.Add(new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position)); } // ensure the buffers don't get cleaned up on exit. success = true; return(chunksToSend); } finally { if (!success) { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "WriteSymmetricMessage"); } } } }
/// <summary> /// Sends a OpenSecureChannel request. /// </summary> protected BufferCollection WriteAsymmetricMessage( uint messageType, uint requestId, X509Certificate2 senderCertificate, X509Certificate2Collection senderCertificateChain, X509Certificate2 receiverCertificate, ArraySegment <byte> messageBody) { bool success = false; BufferCollection chunksToSend = new BufferCollection(); byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage"); try { BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext); int headerSize = 0; if (senderCertificateChain != null && senderCertificateChain.Count > 0) { int senderCertificateSize = 0; WriteAsymmetricMessageHeader( encoder, messageType | TcpMessageType.Intermediate, ChannelId, SecurityPolicyUri, senderCertificate, senderCertificateChain, receiverCertificate, out senderCertificateSize); headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate, senderCertificateSize); } else { WriteAsymmetricMessageHeader( encoder, messageType | TcpMessageType.Intermediate, ChannelId, SecurityPolicyUri, senderCertificate, receiverCertificate); headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate); } int signatureSize = GetAsymmetricSignatureSize(senderCertificate); // save the header. ArraySegment <byte> header = new ArraySegment <byte>(buffer, 0, headerSize); // calculate the space available. int plainTextBlockSize = GetPlainTextBlockSize(receiverCertificate); int cipherTextBlockSize = GetCipherTextBlockSize(receiverCertificate); int maxCipherTextSize = SendBufferSize - headerSize; int maxCipherBlocks = maxCipherTextSize / cipherTextBlockSize; int maxPlainTextSize = maxCipherBlocks * plainTextBlockSize; int maxPayloadSize = maxPlainTextSize - signatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int bytesToWrite = messageBody.Count; int startOfBytes = messageBody.Offset; while (bytesToWrite > 0) { encoder.WriteUInt32(null, GetNewSequenceNumber()); encoder.WriteUInt32(null, requestId); int payloadSize = bytesToWrite; if (payloadSize > maxPayloadSize) { payloadSize = maxPayloadSize; } else { UpdateMessageType(buffer, 0, messageType | TcpMessageType.Final); } // write the message body. encoder.WriteRawBytes(messageBody.Array, messageBody.Offset + startOfBytes, payloadSize); // calculate the amount of plain text to encrypt. int plainTextSize = encoder.Position - headerSize + signatureSize; // calculate the padding. int padding = 0; if (SecurityMode != MessageSecurityMode.None) { if (CertificateFactory.GetRSAPublicKeySize(receiverCertificate) <= TcpMessageLimits.KeySizeExtraPadding) { // need to reserve one byte for the padding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } encoder.WriteByte(null, (byte)padding); for (int ii = 0; ii < padding; ii++) { encoder.WriteByte(null, (byte)padding); } } else { // need to reserve one byte for the padding. plainTextSize++; // need to reserve one byte for the extrapadding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } byte paddingSize = (byte)(padding & 0xff); byte extraPaddingByte = (byte)((padding >> 8) & 0xff); encoder.WriteByte(null, paddingSize); for (int ii = 0; ii < padding; ii++) { encoder.WriteByte(null, (byte)paddingSize); } encoder.WriteByte(null, extraPaddingByte); } // update the plaintext size with the padding size. plainTextSize += padding; } // calculate the number of block to encrypt. int encryptedBlocks = plainTextSize / plainTextBlockSize; // calculate the size of the encrypted data. int cipherTextSize = encryptedBlocks * cipherTextBlockSize; // put the message size after encryption into the header. UpdateMessageSize(buffer, 0, cipherTextSize + headerSize); // write the signature. byte[] signature = Sign(new ArraySegment <byte>(buffer, 0, encoder.Position), senderCertificate); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } int messageSize = encoder.Close(); // encrypt the data. ArraySegment <byte> encryptedBuffer = Encrypt( new ArraySegment <byte>(buffer, headerSize, messageSize - headerSize), header, receiverCertificate); // check for math errors due to code bugs. if (encryptedBuffer.Count != cipherTextSize + headerSize) { throw new InvalidDataException("Actual message size is not the same as the predicted message size."); } // save chunk. chunksToSend.Add(encryptedBuffer); bytesToWrite -= payloadSize; startOfBytes += payloadSize; // reset the encoder to write the plaintext for the next chunk into the same buffer. if (bytesToWrite > 0) { MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize); ostrm.Seek(header.Count, SeekOrigin.Current); encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); } } // ensure the buffers don't get clean up on exit. success = true; return(chunksToSend); } catch (Exception ex) { throw new Exception("Could not write async message", ex); } finally { BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage"); if (!success) { chunksToSend.Release(BufferManager, "WriteAsymmetricMessage"); } } }
/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, TcpChannelToken token, object messageBody, bool isRequest, out bool limitsExceeded) { limitsExceeded = false; bool success = false; BufferCollection chunksToProcess = null; try { // calculate chunk sizes. int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize; int maxCipherBlocks = maxCipherTextSize/EncryptionBlockSize; int maxPlainTextSize = maxCipherBlocks*EncryptionBlockSize; int maxPayloadSize = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int headerSize = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; // write the body to stream. ArraySegmentStream ostrm = new ArraySegmentStream( BufferManager, SendBufferSize, headerSize, maxPayloadSize); // check for encodeable body. IEncodeable encodeable = messageBody as IEncodeable; if (encodeable != null) { // debug code used to verify that message aborts are handled correctly. // int maxMessageSize = Quotas.MessageContext.MaxMessageSize; // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue; BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext); // Quotas.MessageContext.MaxMessageSize = maxMessageSize; } // check for raw bytes. ArraySegment<byte>? rawBytes = messageBody as ArraySegment<byte>?; if (rawBytes != null) { BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count); encoder.Close(); } chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage"); // ensure there is at least one chunk. if (chunksToProcess.Count == 0) { byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage"); chunksToProcess.Add(new ArraySegment<byte>(buffer, 0, 0)); } BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity); int messageSize = 0; for (int ii = 0; ii < chunksToProcess.Count; ii++) { ArraySegment<byte> chunkToProcess = chunksToProcess[ii]; // nothing more to do if limits exceeded. if (limitsExceeded) { BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage"); continue; } MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext); // check if the message needs to be aborted. if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii+1)) { encoder.WriteUInt32(null, messageType | TcpMessageType.Abort); // replace the body in the chunk with an error message. BinaryEncoder errorEncoder = new BinaryEncoder( chunkToProcess.Array, chunkToProcess.Offset, chunkToProcess.Count, Quotas.MessageContext); WriteErrorMessageBody(errorEncoder, (isRequest)?StatusCodes.BadRequestTooLarge:StatusCodes.BadResponseTooLarge); int size = errorEncoder.Close(); chunkToProcess = new ArraySegment<byte>(chunkToProcess.Array, chunkToProcess.Offset, size); limitsExceeded = true; } // check if the message is complete. else if (ii == chunksToProcess.Count-1) { encoder.WriteUInt32(null, messageType | TcpMessageType.Final); } // more chunks to follow. else { encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate); } int count = 0; count += TcpMessageLimits.SequenceHeaderSize; count += chunkToProcess.Count; count += SymmetricSignatureSize; // calculate the padding. int padding = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // reserve one byte for the padding size. count++; if (count%EncryptionBlockSize != 0) { padding = EncryptionBlockSize - (count%EncryptionBlockSize); } count += padding; } count += TcpMessageLimits.SymmetricHeaderSize; encoder.WriteUInt32(null, (uint)count); encoder.WriteUInt32(null, ChannelId); encoder.WriteUInt32(null, token.TokenId); uint sequenceNumber = GetNewSequenceNumber(); encoder.WriteUInt32(null, sequenceNumber); encoder.WriteUInt32(null, requestId); // skip body. strm.Seek(chunkToProcess.Count, SeekOrigin.Current); // update message size count. messageSize += chunkToProcess.Count; // write padding. if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { for (int jj = 0; jj <= padding; jj++) { encoder.WriteByte(null, (byte)padding); } } if (SecurityMode != MessageSecurityMode.None) { // calculate and write signature. byte[] signature = Sign(token, new ArraySegment<byte>(chunkToProcess.Array, 0, encoder.Position), isRequest); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } } if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // encrypt the data. ArraySegment<byte> dataToEncrypt = new ArraySegment<byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position-TcpMessageLimits.SymmetricHeaderSize); Encrypt(token, dataToEncrypt, isRequest); } // add the header into chunk. chunksToSend.Add(new ArraySegment<byte>(chunkToProcess.Array, 0, encoder.Position)); } // ensure the buffers don't get cleaned up on exit. success = true; return chunksToSend; } finally { if (!success) { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "WriteSymmetricMessage"); } } } }
/// <summary> /// Encodes field value as RawData /// </summary> /// <param name="binaryEncoder"></param> /// <param name="field"></param> private void EncodeFieldAsRawData(BinaryEncoder binaryEncoder, Field field) { try { // 01 RawData Field Encoding var variant = field.Value.WrappedValue; if (variant.TypeInfo == null || variant.TypeInfo.BuiltInType == BuiltInType.Null) { return; } object valueToEncode = variant.Value; if (field.FieldMetaData.ValueRank == ValueRanks.Scalar) { switch ((BuiltInType)field.FieldMetaData.BuiltInType) { case BuiltInType.Boolean: binaryEncoder.WriteBoolean("Bool", Convert.ToBoolean(valueToEncode)); break; case BuiltInType.SByte: binaryEncoder.WriteSByte("SByte", Convert.ToSByte(valueToEncode)); break; case BuiltInType.Byte: binaryEncoder.WriteByte("Byte", Convert.ToByte(valueToEncode)); break; case BuiltInType.Int16: binaryEncoder.WriteInt16("Int16", Convert.ToInt16(valueToEncode)); break; case BuiltInType.UInt16: binaryEncoder.WriteUInt16("UInt16", Convert.ToUInt16(valueToEncode)); break; case BuiltInType.Int32: binaryEncoder.WriteInt32("Int32", Convert.ToInt32(valueToEncode)); break; case BuiltInType.UInt32: binaryEncoder.WriteUInt32("UInt32", Convert.ToUInt32(valueToEncode)); break; case BuiltInType.Int64: binaryEncoder.WriteInt64("Int64", Convert.ToInt64(valueToEncode)); break; case BuiltInType.UInt64: binaryEncoder.WriteUInt64("UInt64", Convert.ToUInt64(valueToEncode)); break; case BuiltInType.Float: binaryEncoder.WriteFloat("Float", Convert.ToSingle(valueToEncode)); break; case BuiltInType.Double: binaryEncoder.WriteDouble("Double", Convert.ToDouble(valueToEncode)); break; case BuiltInType.DateTime: binaryEncoder.WriteDateTime("DateTime", Convert.ToDateTime(valueToEncode)); break; case BuiltInType.Guid: binaryEncoder.WriteGuid("GUID", (Uuid)valueToEncode); break; case BuiltInType.String: binaryEncoder.WriteString("String", valueToEncode as string); break; case BuiltInType.ByteString: binaryEncoder.WriteByteString("ByteString", (byte[])valueToEncode); break; case BuiltInType.QualifiedName: binaryEncoder.WriteQualifiedName("QualifiedName", valueToEncode as QualifiedName); break; case BuiltInType.LocalizedText: binaryEncoder.WriteLocalizedText("LocalizedText", valueToEncode as LocalizedText); break; case BuiltInType.NodeId: binaryEncoder.WriteNodeId("NodeId", valueToEncode as NodeId); break; case BuiltInType.ExpandedNodeId: binaryEncoder.WriteExpandedNodeId("ExpandedNodeId", valueToEncode as ExpandedNodeId); break; case BuiltInType.StatusCode: binaryEncoder.WriteStatusCode("StatusCode", (StatusCode)valueToEncode); break; case BuiltInType.XmlElement: binaryEncoder.WriteXmlElement("XmlElement", valueToEncode as XmlElement); break; case BuiltInType.Enumeration: binaryEncoder.WriteInt32("Enumeration", Convert.ToInt32(valueToEncode)); break; case BuiltInType.ExtensionObject: binaryEncoder.WriteExtensionObject("ExtensionObject", valueToEncode as ExtensionObject); break; } } else if (field.FieldMetaData.ValueRank >= ValueRanks.OneDimension) { binaryEncoder.WriteArray(null, valueToEncode, field.FieldMetaData.ValueRank, (BuiltInType)field.FieldMetaData.BuiltInType); } } catch (Exception ex) { Utils.Trace(ex, "Error encoding field {0}.", field.FieldMetaData.Name); } }
/// <summary> /// Saves object in an binary stream. /// </summary> /// <param name="context">The context user.</param> /// <param name="encoder">The encoder to write to.</param> /// <param name="attributesToSave">The masks indicating what attributes to write.</param> public override void Save(ISystemContext context, BinaryEncoder encoder, AttributesToSave attributesToSave) { base.Save(context, encoder, attributesToSave); if ((attributesToSave & AttributesToSave.EventNotifier) != 0) { encoder.WriteByte(null, m_eventNotifier); } if ((attributesToSave & AttributesToSave.ContainsNoLoops) != 0) { encoder.WriteBoolean(null, m_containsNoLoops); } }
/// <summary> /// Sends a OpenSecureChannel response. /// </summary> protected BufferCollection WriteAsymmetricMessage( uint messageType, uint requestId, X509Certificate2 senderCertificate, X509Certificate2 receiverCertificate, ArraySegment<byte> messageBody) { bool success = false; BufferCollection chunksToSend = new BufferCollection(); byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteAsymmetricMessage"); try { int headerSize = GetAsymmetricHeaderSize(SecurityPolicyUri, senderCertificate); int signatureSize = GetAsymmetricSignatureSize(senderCertificate); BinaryEncoder encoder = new BinaryEncoder(buffer, 0, SendBufferSize, Quotas.MessageContext); WriteAsymmetricMessageHeader( encoder, messageType | TcpMessageType.Intermediate, ChannelId, SecurityPolicyUri, senderCertificate, receiverCertificate); // save the header. ArraySegment<byte> header = new ArraySegment<byte>(buffer, 0, headerSize); // calculate the space available. int plainTextBlockSize = GetPlainTextBlockSize(receiverCertificate); int cipherTextBlockSize = GetCipherTextBlockSize(receiverCertificate); int maxCipherTextSize = SendBufferSize - headerSize; int maxCipherBlocks = maxCipherTextSize/cipherTextBlockSize; int maxPlainTextSize = maxCipherBlocks*plainTextBlockSize; int maxPayloadSize = maxPlainTextSize - signatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int bytesToWrite = messageBody.Count; int startOfBytes = messageBody.Offset; while (bytesToWrite > 0) { encoder.WriteUInt32(null, GetNewSequenceNumber()); encoder.WriteUInt32(null, requestId); int payloadSize = bytesToWrite; if (payloadSize > maxPayloadSize) { payloadSize = maxPayloadSize; } else { UpdateMessageType(buffer, 0, messageType | TcpMessageType.Final); } // write the message body. encoder.WriteRawBytes(messageBody.Array, messageBody.Offset+startOfBytes, payloadSize); // calculate the amount of plain text to encrypt. int plainTextSize = encoder.Position - headerSize + signatureSize; // calculate the padding. int padding = 0; if (SecurityMode != MessageSecurityMode.None) { if (receiverCertificate.PublicKey.Key.KeySize <= TcpMessageLimits.KeySizeExtraPadding) { // need to reserve one byte for the padding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } encoder.WriteByte(null, (byte)padding); for (int ii = 0; ii < padding; ii++) { encoder.WriteByte(null, (byte)padding); } } else { // need to reserve one byte for the padding. plainTextSize++; // need to reserve one byte for the extrapadding. plainTextSize++; if (plainTextSize % plainTextBlockSize != 0) { padding = plainTextBlockSize - (plainTextSize % plainTextBlockSize); } byte paddingSize = (byte)(padding & 0xff); byte extraPaddingByte = (byte)((padding >> 8) & 0xff); encoder.WriteByte(null, paddingSize); for (int ii = 0; ii < padding; ii++) { encoder.WriteByte(null, (byte)paddingSize); } encoder.WriteByte(null, extraPaddingByte); } // update the plaintext size with the padding size. plainTextSize += padding; } // calculate the number of block to encrypt. int encryptedBlocks = plainTextSize/plainTextBlockSize; // calculate the size of the encrypted data. int cipherTextSize = encryptedBlocks*cipherTextBlockSize; // put the message size after encryption into the header. UpdateMessageSize(buffer, 0, cipherTextSize + headerSize); // write the signature. byte[] signature = Sign(new ArraySegment<byte>(buffer, 0, encoder.Position), senderCertificate); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } int messageSize = encoder.Close(); // encrypt the data. ArraySegment<byte> encryptedBuffer = Encrypt( new ArraySegment<byte>(buffer, headerSize, messageSize-headerSize), header, receiverCertificate); // check for math errors due to code bugs. if (encryptedBuffer.Count != cipherTextSize + headerSize) { throw new InvalidDataException("Actual message size is not the same as the predicted message size."); } // save chunk. chunksToSend.Add(encryptedBuffer); bytesToWrite -= payloadSize; startOfBytes += payloadSize; // reset the encoder to write the plaintext for the next chunk into the same buffer. if (bytesToWrite > 0) { MemoryStream ostrm = new MemoryStream(buffer, 0, SendBufferSize); ostrm.Seek(header.Count, SeekOrigin.Current); encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); } } // ensure the buffers don't get clean up on exit. success = true; return chunksToSend; } finally { BufferManager.ReturnBuffer(buffer, "WriteAsymmetricMessage"); if (!success) { chunksToSend.Release(BufferManager, "WriteAsymmetricMessage"); } } }
private async Task SendCloseSecureChannelRequestAsync(CloseSecureChannelRequest request, CancellationToken token) { var bodyStream = SerializableBytes.CreateWritableStream(); var bodyEncoder = new BinaryEncoder(bodyStream, this); try { bodyEncoder.WriteNodeId(null, CloseSecureChannelRequestNodeId); request.Encode(bodyEncoder); bodyStream.Position = 0; if (bodyStream.Length > this.RemoteMaxMessageSize) { throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded); } // write chunks int chunkCount = 0; int bodyCount = (int)(bodyStream.Length - bodyStream.Position); while (bodyCount > 0) { chunkCount++; if (this.RemoteMaxChunkCount > 0 && chunkCount > this.RemoteMaxChunkCount) { throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded); } var stream = new MemoryStream(this.sendBuffer, 0, (int)this.RemoteReceiveBufferSize, true, true); var encoder = new BinaryEncoder(stream, this); try { // header encoder.WriteUInt32(null, UaTcpMessageTypes.CLOF); encoder.WriteUInt32(null, 0u); encoder.WriteUInt32(null, this.ChannelId); // symmetric security header encoder.WriteUInt32(null, this.TokenId); // detect new TokenId if (this.TokenId != this.currentClientTokenId) { this.currentClientTokenId = this.TokenId; // update signer and encrypter with new symmetric keys if (this.symIsSigned) { this.symSigner.Key = this.clientSigningKey; if (this.symIsEncrypted) { this.currentClientEncryptingKey = this.clientEncryptingKey; this.currentClientInitializationVector = this.clientInitializationVector; this.symEncryptor = this.symEncryptionAlgorithm.CreateEncryptor(this.currentClientEncryptingKey, this.currentClientInitializationVector); } } } int plainHeaderSize = encoder.Position; // sequence header encoder.WriteUInt32(null, this.GetNextSequenceNumber()); encoder.WriteUInt32(null, request.RequestHeader.RequestHandle); // body int paddingHeaderSize; int maxBodySize; int bodySize; int paddingSize; int chunkSize; if (this.symIsEncrypted) { paddingHeaderSize = this.symEncryptionBlockSize > 256 ? 2 : 1; maxBodySize = ((((int)this.RemoteReceiveBufferSize - plainHeaderSize - this.symSignatureSize - paddingHeaderSize) / this.symEncryptionBlockSize) * this.symEncryptionBlockSize) - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; paddingSize = (this.symEncryptionBlockSize - ((SequenceHeaderSize + bodySize + paddingHeaderSize + this.symSignatureSize) % this.symEncryptionBlockSize)) % this.symEncryptionBlockSize; } else { bodySize = maxBodySize; paddingSize = 0; } chunkSize = plainHeaderSize + (((SequenceHeaderSize + bodySize + paddingSize + paddingHeaderSize + this.symSignatureSize) / this.symEncryptionBlockSize) * this.symEncryptionBlockSize); } else { paddingHeaderSize = 0; paddingSize = 0; maxBodySize = (int)this.RemoteReceiveBufferSize - plainHeaderSize - this.symSignatureSize - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; } else { bodySize = maxBodySize; } chunkSize = plainHeaderSize + SequenceHeaderSize + bodySize + this.symSignatureSize; } bodyStream.Read(this.sendBuffer, encoder.Position, bodySize); encoder.Position += bodySize; bodyCount -= bodySize; // padding if (this.symIsEncrypted) { var paddingByte = (byte)(paddingSize & 0xFF); encoder.WriteByte(null, paddingByte); for (int i = 0; i < paddingSize; i++) { encoder.WriteByte(null, paddingByte); } if (paddingHeaderSize == 2) { var extraPaddingByte = (byte)((paddingSize >> 8) & 0xFF); encoder.WriteByte(null, extraPaddingByte); } } // update message type and (encrypted) length var position = encoder.Position; encoder.Position = 3; encoder.WriteByte(null, bodyCount > 0 ? (byte)'C' : (byte)'F'); encoder.WriteUInt32(null, (uint)chunkSize); encoder.Position = position; // signature if (this.symIsSigned) { byte[] signature = this.symSigner.ComputeHash(this.sendBuffer, 0, position); if (signature != null) { encoder.Write(signature, 0, signature.Length); } } // encrypt position = encoder.Position; if (this.symIsEncrypted) { using (var symEncryptor = this.symEncryptionAlgorithm.CreateEncryptor(this.currentClientEncryptingKey, this.currentClientInitializationVector)) { int inputCount = position - plainHeaderSize; Debug.Assert(inputCount % symEncryptor.InputBlockSize == 0, "Input data is not an even number of encryption blocks."); symEncryptor.TransformBlock(this.sendBuffer, plainHeaderSize, inputCount, this.sendBuffer, plainHeaderSize); symEncryptor.TransformFinalBlock(this.sendBuffer, position, 0); } } // pass buffer to transport await this.SendAsync(this.sendBuffer, 0, position, token).ConfigureAwait(false); } finally { encoder.Dispose(); } } } finally { bodyEncoder.Dispose(); } }
private async Task SendOpenSecureChannelRequestAsync(OpenSecureChannelRequest request, CancellationToken token) { var bodyStream = SerializableBytes.CreateWritableStream(); var bodyEncoder = new BinaryEncoder(bodyStream, this); try { bodyEncoder.WriteNodeId(null, OpenSecureChannelRequestNodeId); request.Encode(bodyEncoder); bodyStream.Position = 0; if (bodyStream.Length > this.RemoteMaxMessageSize) { throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded); } // write chunks int chunkCount = 0; int bodyCount = (int)(bodyStream.Length - bodyStream.Position); while (bodyCount > 0) { chunkCount++; if (this.RemoteMaxChunkCount > 0 && chunkCount > this.RemoteMaxChunkCount) { throw new ServiceResultException(StatusCodes.BadEncodingLimitsExceeded); } var stream = new MemoryStream(this.sendBuffer, 0, (int)this.RemoteReceiveBufferSize, true, true); var encoder = new BinaryEncoder(stream, this); try { // header encoder.WriteUInt32(null, UaTcpMessageTypes.OPNF); encoder.WriteUInt32(null, 0u); encoder.WriteUInt32(null, this.ChannelId); // asymmetric security header encoder.WriteString(null, this.RemoteEndpoint.SecurityPolicyUri); if (this.RemoteEndpoint.SecurityMode != MessageSecurityMode.None) { encoder.WriteByteString(null, this.localCertificateBlob); encoder.WriteByteString(null, this.RemoteCertificate.GetCertHash()); } else { encoder.WriteByteString(null, null); encoder.WriteByteString(null, null); } int plainHeaderSize = encoder.Position; // sequence header encoder.WriteUInt32(null, this.GetNextSequenceNumber()); encoder.WriteUInt32(null, request.RequestHeader.RequestHandle); // body int paddingHeaderSize; int maxBodySize; int bodySize; int paddingSize; int chunkSize; if (this.asymIsEncrypted) { paddingHeaderSize = this.asymRemoteCipherTextBlockSize > 256 ? 2 : 1; maxBodySize = ((((int)this.RemoteReceiveBufferSize - plainHeaderSize - this.asymLocalSignatureSize - paddingHeaderSize) / this.asymRemoteCipherTextBlockSize) * this.asymRemotePlainTextBlockSize) - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; paddingSize = (this.asymRemotePlainTextBlockSize - ((SequenceHeaderSize + bodySize + paddingHeaderSize + this.asymLocalSignatureSize) % this.asymRemotePlainTextBlockSize)) % this.asymRemotePlainTextBlockSize; } else { bodySize = maxBodySize; paddingSize = 0; } chunkSize = plainHeaderSize + (((SequenceHeaderSize + bodySize + paddingSize + paddingHeaderSize + this.asymLocalSignatureSize) / this.asymRemotePlainTextBlockSize) * this.asymRemoteCipherTextBlockSize); } else { paddingHeaderSize = 0; paddingSize = 0; maxBodySize = (int)this.RemoteReceiveBufferSize - plainHeaderSize - this.asymLocalSignatureSize - SequenceHeaderSize; if (bodyCount < maxBodySize) { bodySize = bodyCount; } else { bodySize = maxBodySize; } chunkSize = plainHeaderSize + SequenceHeaderSize + bodySize + this.asymLocalSignatureSize; } bodyStream.Read(this.sendBuffer, encoder.Position, bodySize); encoder.Position += bodySize; bodyCount -= bodySize; // padding if (this.asymIsEncrypted) { byte paddingByte = (byte)(paddingSize & 0xFF); encoder.WriteByte(null, paddingByte); for (int i = 0; i < paddingSize; i++) { encoder.WriteByte(null, paddingByte); } if (paddingHeaderSize == 2) { byte extraPaddingByte = (byte)((paddingSize >> 8) & 0xFF); encoder.WriteByte(null, extraPaddingByte); } } // update message type and (encrypted) length var position = encoder.Position; encoder.Position = 3; encoder.WriteByte(null, bodyCount > 0 ? (byte)'C' : (byte)'F'); encoder.WriteUInt32(null, (uint)chunkSize); encoder.Position = position; // sign if (this.asymIsSigned) { // sign with local private key. byte[] signature = this.LocalPrivateKey.SignData(this.sendBuffer, 0, position, this.asymSignatureHashAlgorithmName, RSASignaturePadding.Pkcs1); Debug.Assert(signature.Length == this.asymLocalSignatureSize, nameof(this.asymLocalSignatureSize)); encoder.Write(signature, 0, this.asymLocalSignatureSize); } // encrypt if (this.asymIsEncrypted) { position = encoder.Position; Buffer.BlockCopy(this.sendBuffer, 0, this.encryptionBuffer, 0, plainHeaderSize); byte[] plainText = new byte[this.asymRemotePlainTextBlockSize]; int jj = plainHeaderSize; for (int ii = plainHeaderSize; ii < position; ii += this.asymRemotePlainTextBlockSize) { Buffer.BlockCopy(this.sendBuffer, ii, plainText, 0, this.asymRemotePlainTextBlockSize); // encrypt with remote public key. byte[] cipherText = this.RemotePublicKey.Encrypt(plainText, this.asymEncryptionPadding); Debug.Assert(cipherText.Length == this.asymRemoteCipherTextBlockSize, nameof(this.asymRemoteCipherTextBlockSize)); Buffer.BlockCopy(cipherText, 0, this.encryptionBuffer, jj, this.asymRemoteCipherTextBlockSize); jj += this.asymRemoteCipherTextBlockSize; } await this.SendAsync(this.encryptionBuffer, 0, jj, token).ConfigureAwait(false); return; } // pass buffer to transport await this.SendAsync(this.sendBuffer, 0, encoder.Position, token).ConfigureAwait(false); } finally { encoder.Dispose(); } } } finally { bodyEncoder.Dispose(); } }
/// <summary> /// Encodes field value as RawData /// </summary> /// <param name="binaryEncoder"></param> /// <param name="field"></param> private void EncodeFieldAsRawData(BinaryEncoder binaryEncoder, Field field) { try { // 01 RawData Field Encoding (TODO: StructuredValue) var variant = field.Value.WrappedValue; if (variant.TypeInfo == null || variant.TypeInfo.BuiltInType == BuiltInType.Null) { return; } if (field.FieldMetaData.ValueRank == ValueRanks.Scalar) { switch ((BuiltInType)field.FieldMetaData.BuiltInType) { case BuiltInType.Boolean: binaryEncoder.WriteBoolean("Bool", Convert.ToBoolean(variant.Value)); break; case BuiltInType.SByte: binaryEncoder.WriteSByte("SByte", Convert.ToSByte(variant.Value)); break; case BuiltInType.Byte: binaryEncoder.WriteByte("Byte", Convert.ToByte(variant.Value)); break; case BuiltInType.Int16: binaryEncoder.WriteInt16("Int16", Convert.ToInt16(variant.Value)); break; case BuiltInType.UInt16: binaryEncoder.WriteUInt16("UInt16", Convert.ToUInt16(variant.Value)); break; case BuiltInType.Int32: binaryEncoder.WriteInt32("Int32", Convert.ToInt32(variant.Value)); break; case BuiltInType.UInt32: binaryEncoder.WriteUInt32("UInt32", Convert.ToUInt32(variant.Value)); break; case BuiltInType.Int64: binaryEncoder.WriteInt64("Int64", Convert.ToInt64(variant.Value)); break; case BuiltInType.UInt64: binaryEncoder.WriteUInt64("UInt64", Convert.ToUInt64(variant.Value)); break; case BuiltInType.Float: binaryEncoder.WriteFloat("Float", Convert.ToSingle(variant.Value)); break; case BuiltInType.Double: binaryEncoder.WriteDouble("Double", Convert.ToDouble(variant.Value)); break; case BuiltInType.DateTime: binaryEncoder.WriteDateTime("DateTime", Convert.ToDateTime(variant.Value)); break; case BuiltInType.Guid: binaryEncoder.WriteGuid("GUID", (Uuid)variant.Value); break; case BuiltInType.String: binaryEncoder.WriteString("String", variant.Value as string); break; case BuiltInType.ByteString: binaryEncoder.WriteByteString("ByteString", (byte[])variant.Value); break; case BuiltInType.QualifiedName: binaryEncoder.WriteQualifiedName("QualifiedName", variant.Value as QualifiedName); break; case BuiltInType.LocalizedText: binaryEncoder.WriteLocalizedText("LocalizedText", variant.Value as LocalizedText); break; case BuiltInType.NodeId: binaryEncoder.WriteNodeId("NodeId", variant.Value as NodeId); break; case BuiltInType.ExpandedNodeId: binaryEncoder.WriteExpandedNodeId("ExpandedNodeId", variant.Value as ExpandedNodeId); break; case BuiltInType.StatusCode: binaryEncoder.WriteStatusCode("StatusCode", (StatusCode)variant.Value); break; case BuiltInType.XmlElement: binaryEncoder.WriteXmlElement("XmlElement", variant.Value as XmlElement); break; case BuiltInType.Enumeration: binaryEncoder.WriteInt32("Enumeration", Convert.ToInt32(variant.Value)); break; case BuiltInType.ExtensionObject: binaryEncoder.WriteExtensionObject("ExtensionObject", variant.Value as ExtensionObject); break; } } else { switch ((BuiltInType)field.FieldMetaData.BuiltInType) { case BuiltInType.Boolean: binaryEncoder.WriteBooleanArray("BooleanArray", (bool[])variant.Value); break; case BuiltInType.SByte: binaryEncoder.WriteSByteArray("SByteArray", (sbyte[])variant.Value); break; case BuiltInType.Byte: binaryEncoder.WriteByteArray("ByteArray", (byte[])variant.Value); break; case BuiltInType.Int16: binaryEncoder.WriteInt16Array("ByteArray", (short[])variant.Value); break; case BuiltInType.UInt16: binaryEncoder.WriteUInt16Array("UInt16Array", (ushort[])variant.Value); break; case BuiltInType.Int32: binaryEncoder.WriteInt32Array("Int32Array", (int[])variant.Value); break; case BuiltInType.UInt32: binaryEncoder.WriteUInt32Array("UInt32Array", (uint[])variant.Value); break; case BuiltInType.Int64: binaryEncoder.WriteInt64Array("Int64Array", (long[])variant.Value); break; case BuiltInType.UInt64: binaryEncoder.WriteUInt64Array("UInt64Array", (ulong[])variant.Value); break; case BuiltInType.Float: binaryEncoder.WriteFloatArray("FloatArray", (float[])variant.Value); break; case BuiltInType.Double: binaryEncoder.WriteDoubleArray("DoubleArray", (double[])variant.Value); break; case BuiltInType.DateTime: binaryEncoder.WriteDateTimeArray("DateTimeArray", (DateTime[])variant.Value); break; case BuiltInType.Guid: binaryEncoder.WriteGuidArray("GuidArray", (Uuid[])variant.Value); break; case BuiltInType.String: binaryEncoder.WriteStringArray("StringArray", (string[])variant.Value); break; case BuiltInType.ByteString: binaryEncoder.WriteByteStringArray("StringArray", (byte[][])variant.Value); break; case BuiltInType.QualifiedName: binaryEncoder.WriteQualifiedNameArray("QualifiedNameArray", (QualifiedName[])variant.Value); break; case BuiltInType.LocalizedText: binaryEncoder.WriteLocalizedTextArray("LocalizedTextArray", (LocalizedText[])variant.Value); break; case BuiltInType.NodeId: binaryEncoder.WriteNodeIdArray("NodeIdArray", (NodeId[])variant.Value); break; case BuiltInType.ExpandedNodeId: binaryEncoder.WriteExpandedNodeIdArray("ExpandedNodeIdArray", (ExpandedNodeId[])variant.Value); break; case BuiltInType.StatusCode: binaryEncoder.WriteStatusCodeArray("StatusCodeArray", (StatusCode[])variant.Value); break; case BuiltInType.XmlElement: binaryEncoder.WriteXmlElementArray("XmlElementArray", (System.Xml.XmlElement[])variant.Value); break; case BuiltInType.Variant: binaryEncoder.WriteVariantArray("VariantArray", (Variant[])variant.Value); break; case BuiltInType.Enumeration: //TODO make this work //binaryEncoder.WriteInt32Array("EnumerationArray", Convert.ToInt32(variant.Value)); binaryEncoder.WriteVariantArray("EnumerationArray", (Variant[])variant.Value); break; case BuiltInType.ExtensionObject: binaryEncoder.WriteExtensionObjectArray("ExtensionObjectArray", (ExtensionObject[])variant.Value); break; } } } catch (Exception ex) { Utils.Trace("Error encoding field {0} - {1}", field.FieldMetaData.Name, ex); } }