/// <summary> /// Decrypts a message using a symmetric algorithm. /// </summary> private static void SymmetricDecrypt( ChannelToken token, ArraySegment <byte> dataToDecrypt, bool useClientKeys) { // get the decrypting key. SymmetricAlgorithm decryptingKey = (useClientKeys) ? token.ClientEncryptor : token.ServerEncryptor; if (decryptingKey == null) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Token missing symmetric key object."); } using (ICryptoTransform decryptor = decryptingKey.CreateDecryptor()) { byte[] blockToDecrypt = dataToDecrypt.Array; int start = dataToDecrypt.Offset; int count = dataToDecrypt.Count; if (count % decryptor.InputBlockSize != 0) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Input data is not an even number of encryption blocks."); } decryptor.TransformBlock(blockToDecrypt, start, count, blockToDecrypt, start); } }
/// <summary> /// Returns the symmetric signature for the data. /// </summary> protected bool Verify( ChannelToken token, byte[] signature, ArraySegment <byte> dataToVerify, bool useClientKeys) { // verify signature. switch (SecurityPolicyUri) { case SecurityPolicies.None: { return(true); } case SecurityPolicies.Basic128Rsa15: case SecurityPolicies.Basic256: case SecurityPolicies.Basic256Sha256: case SecurityPolicies.Aes128_Sha256_RsaOaep: case SecurityPolicies.Aes256_Sha256_RsaPss: { return(SymmetricVerify(token, signature, dataToVerify, useClientKeys)); } default: { return(false); } } }
/// <summary> /// Activates a new token. /// </summary> protected void ActivateToken(ChannelToken token) { // compute the keys for the token. ComputeKeys(token); m_previousToken = m_currentToken; m_currentToken = token; m_renewedToken = null; Utils.Trace("Token #{0} activated. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime); }
/// <summary> /// Handles a reconnect request. /// </summary> public void Reconnect( IMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { if (socket == null) { throw new ArgumentNullException("socket"); } lock (DataLock) { // make sure the same client certificate is being used. CompareCertificates(ClientCertificate, clientCertificate, false); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "Reconnect")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } try { // replace the socket. Socket = socket; Utils.Trace("TCPSERVERCHANNEL SOCKET RECONNECTED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId); Socket.ChangeSink(this); // need to assign a new token id. token.TokenId = GetNewTokenId(); // put channel back in open state. ActivateToken(token); State = TcpChannelState.Open; // no need to cleanup. CleanupTimer(); // send response. SendOpenSecureChannelResponse(requestId, token, request); // send any queue responses. Task.Factory.StartNew(OnChannelReconnected, m_queuedResponses); m_queuedResponses = new SortedDictionary <uint, IServiceResponse>(); } catch (Exception e) { SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); } } }
/// <summary> /// Activates a new token. /// </summary> protected void ActivateToken(ChannelToken token) { // compute the keys for the token. ComputeKeys(token); m_previousToken = m_currentToken; m_currentToken = token; m_renewedToken = null; Utils.LogInfo("ChannelId {0}: Token #{1} activated. CreatedAt={2:HH:mm:ss.fff}. Lifetime={3}.", Id, token.TokenId, token.CreatedAt, token.Lifetime); }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> public bool ReconnectToExistingChannel( IMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { return(true); }
/// <summary> /// Creates a new token. /// </summary> protected ChannelToken CreateToken() { ChannelToken token = new ChannelToken(); token.ChannelId = m_channelId; token.TokenId = 0; token.CreatedAt = DateTime.UtcNow; token.Lifetime = (int)Quotas.SecurityTokenLifetime; Utils.Trace("Token #{0} created. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime); return(token); }
/// <summary> /// Creates a new token. /// </summary> protected ChannelToken CreateToken() { ChannelToken token = new ChannelToken(); token.ChannelId = m_channelId; token.TokenId = 0; token.CreatedAt = DateTime.UtcNow; token.Lifetime = (int)Quotas.SecurityTokenLifetime; Utils.LogInfo("ChannelId {0}: Token #{1} created. CreatedAt={2:HH:mm:ss.fff}. Lifetime={3}.", Id, token.TokenId, token.CreatedAt, token.Lifetime); return(token); }
/// <summary> /// Signs the message using SHA1 HMAC /// </summary> private static byte[] SymmetricSign(ChannelToken token, ArraySegment <byte> dataToSign, bool useClientKeys) { // get HMAC object. HMAC hmac = (useClientKeys) ? token.ClientHmac : token.ServerHmac; // compute hash. MemoryStream istrm = new MemoryStream(dataToSign.Array, dataToSign.Offset, dataToSign.Count, false); byte[] signature = hmac.ComputeHash(istrm); istrm.Dispose(); // return signature. return(signature); }
/// <summary> /// Sends a fault response secured with the symmetric keys. /// </summary> protected void SendServiceFault(ChannelToken token, uint requestId, ServiceResult fault) { Utils.Trace("Channel {0} Request {1}: SendServiceFault()", ChannelId, requestId); BufferCollection buffers = null; try { // construct fault. ServiceFault response = new ServiceFault(); response.ResponseHeader.ServiceResult = fault.Code; StringTable stringTable = new StringTable(); response.ResponseHeader.ServiceDiagnostics = new DiagnosticInfo( fault, DiagnosticsMasks.NoInnerStatus, true, stringTable); response.ResponseHeader.StringTable = stringTable.ToArray(); // the limits should never be exceeded when sending a fault. bool limitsExceeded = false; // secure message. buffers = WriteSymmetricMessage( TcpMessageType.Message, requestId, token, response, false, out limitsExceeded); // send message. BeginWriteMessage(buffers, null); buffers = null; } catch (Exception e) { if (buffers != null) { buffers.Release(BufferManager, "SendServiceFault"); } ForceChannelFault(ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error sending a service fault.")); } }
/// <summary> /// Returns the symmetric signature for the data. /// </summary> protected byte[] Sign(ChannelToken token, ArraySegment <byte> dataToSign, bool useClientKeys) { switch (SecurityPolicyUri) { default: case SecurityPolicies.None: { return(null); } case SecurityPolicies.Basic128Rsa15: case SecurityPolicies.Basic256: case SecurityPolicies.Basic256Sha256: { return(SymmetricSign(token, dataToSign, useClientKeys)); } } }
/// <summary> /// Decrypts the data in a buffer using symmetric encryption. /// </summary> protected void Decrypt(ChannelToken token, ArraySegment <byte> dataToDecrypt, bool useClientKeys) { switch (SecurityPolicyUri) { default: case SecurityPolicies.None: { break; } case SecurityPolicies.Basic256: case SecurityPolicies.Basic256Sha256: case SecurityPolicies.Basic128Rsa15: { SymmetricDecrypt(token, dataToDecrypt, useClientKeys); break; } } }
/// <summary> /// Verifies a HMAC for a message. /// </summary> private static bool SymmetricVerify( ChannelToken token, byte[] signature, ArraySegment <byte> dataToVerify, bool useClientKeys) { // get HMAC object. HMAC hmac = (useClientKeys) ? token.ClientHmac : token.ServerHmac; // compute hash. MemoryStream istrm = new MemoryStream(dataToVerify.Array, dataToVerify.Offset, dataToVerify.Count, false); byte[] computedSignature = hmac.ComputeHash(istrm); istrm.Dispose(); // compare signatures. for (int ii = 0; ii < signature.Length; ii++) { if (computedSignature[ii] != signature[ii]) { string messageType = new UTF8Encoding().GetString(dataToVerify.Array, dataToVerify.Offset, 4); int messageLength = BitConverter.ToInt32(dataToVerify.Array, dataToVerify.Offset + 4); string expectedSignature = Utils.ToHexString(computedSignature); string actualSignature = Utils.ToHexString(signature); Utils.Trace( "Could not validate signature.\r\nChannelId={0}, TokenId={1}, MessageType={2}, Length={3}\r\nExpectedSignature={4}\r\nActualSignature ={5}", token.ChannelId, token.TokenId, messageType, messageLength, expectedSignature, actualSignature); return(false); } } return(true); }
/// <summary> /// Sends an OpenSecureChannel response. /// </summary> private void SendOpenSecureChannelResponse(uint requestId, ChannelToken token, OpenSecureChannelRequest request) { Utils.Trace("Channel {0}: SendOpenSecureChannelResponse()", ChannelId); OpenSecureChannelResponse response = new OpenSecureChannelResponse(); response.ResponseHeader.RequestHandle = request.RequestHeader.RequestHandle; response.ResponseHeader.Timestamp = DateTime.UtcNow; response.SecurityToken.ChannelId = token.ChannelId; response.SecurityToken.TokenId = token.TokenId; response.SecurityToken.CreatedAt = token.CreatedAt; response.SecurityToken.RevisedLifetime = (uint)token.Lifetime; response.ServerNonce = token.ServerNonce; byte[] buffer = BinaryEncoder.EncodeMessage(response, Quotas.MessageContext); BufferCollection chunksToSend = WriteAsymmetricMessage( TcpMessageType.Open, requestId, ServerCertificate, ServerCertificateChain, ClientCertificate, new ArraySegment <byte>(buffer, 0, buffer.Length)); // write the message to the server. try { BeginWriteMessage(chunksToSend, null); chunksToSend = null; } finally { if (chunksToSend != null) { chunksToSend.Release(BufferManager, "SendOpenSecureChannelResponse"); } } }
/// <summary> /// Binds a new socket to an existing channel. /// </summary> public bool ReconnectToExistingChannel( IMessageSocket socket, uint requestId, uint sequenceNumber, uint channelId, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { TcpServerChannel channel = null; lock (m_lock) { if (!m_channels.TryGetValue(channelId, out channel)) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Could not find secure channel referenced in the OpenSecureChannel request."); } } channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request); Utils.Trace("Channel {0} reconnected", channelId); return(true); }
/// <summary> /// Sets the renewed token /// </summary> protected void SetRenewedToken(ChannelToken token) { m_renewedToken = token; Utils.Trace("RenewedToken #{0} set. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime); }
/// <summary> /// Sets the renewed token /// </summary> protected void SetRenewedToken(ChannelToken token) { m_renewedToken = token; Utils.LogInfo("ChannelId {0}: Renewed Token #{1} set. CreatedAt={2:HH:mm:ss.fff}. Lifetime ={3}.", Id, token.TokenId, token.CreatedAt, token.Lifetime); }
/// <summary> /// Processes an OpenSecureChannel request message. /// </summary> private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk) { // validate the channel state. if (State != TcpChannelState.Opening && State != TcpChannelState.Open) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected OpenSecureChannel message."); return(false); } // parse the security header. uint channelId = 0; X509Certificate2 clientCertificate = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadAsymmetricMessage( messageChunk, ServerCertificate, out channelId, out clientCertificate, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessOpenSecureChannelRequest")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } } catch (Exception e) { ServiceResultException innerException = e.InnerException as ServiceResultException; // If the certificate structre, signature and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed if (innerException != null) { if (innerException.StatusCode == StatusCodes.BadCertificateUntrusted || innerException.StatusCode == StatusCodes.BadCertificateChainIncomplete || innerException.StatusCode == StatusCodes.BadCertificateRevoked || innerException.StatusCode == StatusCodes.BadCertificateInvalid || (innerException.InnerResult != null && innerException.InnerResult.StatusCode == StatusCodes.BadCertificateUntrusted)) { ForceChannelFault(StatusCodes.BadSecurityChecksFailed, e.Message); return(false); } else if (innerException.StatusCode == StatusCodes.BadCertificateTimeInvalid || innerException.StatusCode == StatusCodes.BadCertificateIssuerTimeInvalid || innerException.StatusCode == StatusCodes.BadCertificateHostNameInvalid || innerException.StatusCode == StatusCodes.BadCertificateUriInvalid || innerException.StatusCode == StatusCodes.BadCertificateUseNotAllowed || innerException.StatusCode == StatusCodes.BadCertificateIssuerUseNotAllowed || innerException.StatusCode == StatusCodes.BadCertificateRevocationUnknown || innerException.StatusCode == StatusCodes.BadCertificateIssuerRevocationUnknown || innerException.StatusCode == StatusCodes.BadCertificateIssuerRevoked) { ForceChannelFault(innerException, innerException.StatusCode, e.Message); return(false); } } ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed, "Could not verify security on OpenSecureChannel request."); return(false); } BufferCollection chunksToProcess = null; try { bool firstCall = ClientCertificate == null; // must ensure the same certificate was used. if (ClientCertificate != null) { CompareCertificates(ClientCertificate, clientCertificate, false); } else { ClientCertificate = clientCertificate; } // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(false); } // create a new token. ChannelToken token = CreateToken(); token.TokenId = GetNewTokenId(); token.ServerNonce = CreateNonce(); // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); OpenSecureChannelRequest request = (OpenSecureChannelRequest)BinaryDecoder.DecodeMessage( new ArraySegmentStream(chunksToProcess), typeof(OpenSecureChannelRequest), Quotas.MessageContext); if (request == null) { throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse OpenSecureChannel request body."); } // check the security mode. if (request.SecurityMode != SecurityMode) { ReviseSecurityMode(firstCall, request.SecurityMode); } // check the client nonce. token.ClientNonce = request.ClientNonce; if (!ValidateNonce(token.ClientNonce)) { throw ServiceResultException.Create(StatusCodes.BadNonceInvalid, "Client nonce is not the correct length or not random enough."); } // choose the lifetime. int lifetime = (int)request.RequestedLifetime; if (lifetime < TcpMessageLimits.MinSecurityTokenLifeTime) { lifetime = TcpMessageLimits.MinSecurityTokenLifeTime; } if (lifetime > 0 && lifetime < token.Lifetime) { token.Lifetime = lifetime; } // check the request type. SecurityTokenRequestType requestType = request.RequestType; if (requestType == SecurityTokenRequestType.Issue && State != TcpChannelState.Opening) { throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request a new token for an open channel."); } if (requestType == SecurityTokenRequestType.Renew && State != TcpChannelState.Open) { // may be reconnecting to a dropped channel. if (State == TcpChannelState.Opening) { // tell the listener to find the channel that can process the request. Listener.ReconnectToExistingChannel( Socket, requestId, sequenceNumber, channelId, ClientCertificate, token, request); Utils.Trace( "{0} ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (Socket != null) ? Socket.Handle : 0, (CurrentToken != null) ? CurrentToken.ChannelId : 0, (CurrentToken != null) ? CurrentToken.TokenId : 0); // close the channel. ChannelClosed(); // nothing more to do. return(false); } throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid, "Cannot request to renew a token for a channel that has not been opened."); } // check the channel id. if (requestType == SecurityTokenRequestType.Renew && channelId != ChannelId) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Do not recognize the secure channel id provided."); } // log security information. if (requestType == SecurityTokenRequestType.Issue) { Opc.Ua.Security.Audit.SecureChannelCreated( m_ImplementationString, Listener.EndpointUrl.ToString(), Utils.Format("{0}", ChannelId), EndpointDescription, ClientCertificate, ServerCertificate, BinaryEncodingSupport.Required); } else { Opc.Ua.Security.Audit.SecureChannelRenewed( m_ImplementationString, Utils.Format("{0}", ChannelId)); } if (requestType == SecurityTokenRequestType.Renew) { SetRenewedToken(token); } else { ActivateToken(token); } State = TcpChannelState.Open; // send the response. SendOpenSecureChannelResponse(requestId, token, request); // notify reverse CompleteReverseHello(null); // notify any monitors. NotifyMonitors(ServiceResult.Good, false); return(false); } catch (Exception e) { SendServiceFault(requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing OpenSecureChannel request.")); CompleteReverseHello(e); return(false); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest"); } } }
protected ArraySegment <byte> ReadSymmetricMessage( ArraySegment <byte> buffer, bool isRequest, out ChannelToken token, out uint requestId, out uint sequenceNumber) { BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext); uint messageType = decoder.ReadUInt32(null); uint messageSize = decoder.ReadUInt32(null); uint channelId = decoder.ReadUInt32(null); uint tokenId = decoder.ReadUInt32(null); // ensure the channel is valid. if (channelId != ChannelId) { throw ServiceResultException.Create( StatusCodes.BadTcpSecureChannelUnknown, "SecureChannelId is not known. ChanneId={0}, CurrentChannelId={1}", channelId, ChannelId); } // check for a message secured with the new token. if (RenewedToken != null && RenewedToken.TokenId == tokenId) { ActivateToken(RenewedToken); } // check if activation of the new token should be forced. if (RenewedToken != null && CurrentToken.ActivationRequired) { ActivateToken(RenewedToken); Utils.Trace("Token #{0} activated forced.", CurrentToken.TokenId); } // check for valid token. ChannelToken currentToken = CurrentToken; if (currentToken == null) { throw new ServiceResultException(StatusCodes.BadSecureChannelClosed); } // find the token. if (currentToken.TokenId != tokenId && PreviousToken != null && PreviousToken.TokenId != tokenId) { throw ServiceResultException.Create( StatusCodes.BadTcpSecureChannelUnknown, "TokenId is not known. ChanneId={0}, TokenId={1}, CurrentTokenId={2}, PreviousTokenId={3}", channelId, tokenId, currentToken.TokenId, (PreviousToken != null) ? (int)PreviousToken.TokenId : -1); } token = currentToken; // check for a message secured with the token before it expired. if (PreviousToken != null && PreviousToken.TokenId == tokenId) { token = PreviousToken; } // check if token has expired. if (token.Expired) { throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Token #{0} has expired. Lifetime={1:HH:mm:ss.fff}", token.TokenId, token.CreatedAt); } int headerSize = decoder.Position; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // decrypt the message. Decrypt(token, new ArraySegment <byte>(buffer.Array, buffer.Offset + headerSize, buffer.Count - headerSize), isRequest); } if (SecurityMode != MessageSecurityMode.None) { // extract signature. byte[] signature = new byte[SymmetricSignatureSize]; for (int ii = 0; ii < SymmetricSignatureSize; ii++) { signature[ii] = buffer.Array[buffer.Offset + buffer.Count - SymmetricSignatureSize + ii]; } // verify the signature. if (!Verify(token, signature, new ArraySegment <byte>(buffer.Array, buffer.Offset, buffer.Count - SymmetricSignatureSize), isRequest)) { Utils.Trace("Could not verify signature on message."); throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the signature on the message."); } } int paddingCount = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // verify padding. int paddingStart = buffer.Offset + buffer.Count - SymmetricSignatureSize - 1; paddingCount = buffer.Array[paddingStart]; for (int ii = paddingStart - paddingCount; ii < paddingStart; ii++) { if (buffer.Array[ii] != paddingCount) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message."); } } // add byte for size. paddingCount++; } // extract request id and sequence number. sequenceNumber = decoder.ReadUInt32(null); requestId = decoder.ReadUInt32(null); // return an the data contained in the message. int startOfBody = buffer.Offset + TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; int sizeOfBody = buffer.Count - TcpMessageLimits.SymmetricHeaderSize - TcpMessageLimits.SequenceHeaderSize - paddingCount - SymmetricSignatureSize; return(new ArraySegment <byte>(buffer.Array, startOfBody, sizeOfBody)); }
/// <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> /// Computes the keys for a token. /// </summary> protected void ComputeKeys(ChannelToken token) { if (SecurityMode == MessageSecurityMode.None) { return; } if (SecurityPolicyUri == SecurityPolicies.Basic256Sha256 || SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep || SecurityPolicyUri == SecurityPolicies.Aes256_Sha256_RsaPss) { token.ClientSigningKey = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize); token.ClientEncryptingKey = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize); token.ClientInitializationVector = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize); token.ServerSigningKey = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize); token.ServerEncryptingKey = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize); token.ServerInitializationVector = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize); } else { token.ClientSigningKey = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize); token.ClientEncryptingKey = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize); token.ClientInitializationVector = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize); token.ServerSigningKey = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize); token.ServerEncryptingKey = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize); token.ServerInitializationVector = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize); } switch (SecurityPolicyUri) { case SecurityPolicies.Basic128Rsa15: case SecurityPolicies.Basic256: case SecurityPolicies.Basic256Sha256: case SecurityPolicies.Aes128_Sha256_RsaOaep: case SecurityPolicies.Aes256_Sha256_RsaPss: { // create encryptors. SymmetricAlgorithm AesCbcEncryptorProvider = Aes.Create(); AesCbcEncryptorProvider.Mode = CipherMode.CBC; AesCbcEncryptorProvider.Padding = PaddingMode.None; AesCbcEncryptorProvider.Key = token.ClientEncryptingKey; AesCbcEncryptorProvider.IV = token.ClientInitializationVector; token.ClientEncryptor = AesCbcEncryptorProvider; SymmetricAlgorithm AesCbcDecryptorProvider = Aes.Create(); AesCbcDecryptorProvider.Mode = CipherMode.CBC; AesCbcDecryptorProvider.Padding = PaddingMode.None; AesCbcDecryptorProvider.Key = token.ServerEncryptingKey; AesCbcDecryptorProvider.IV = token.ServerInitializationVector; token.ServerEncryptor = AesCbcDecryptorProvider; // create HMACs. if (SecurityPolicyUri == SecurityPolicies.Basic256Sha256 || SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep || SecurityPolicyUri == SecurityPolicies.Aes256_Sha256_RsaPss) { // SHA256 token.ServerHmac = new HMACSHA256(token.ServerSigningKey); token.ClientHmac = new HMACSHA256(token.ClientSigningKey); } else { // SHA1 token.ServerHmac = new HMACSHA1(token.ServerSigningKey); token.ClientHmac = new HMACSHA1(token.ClientSigningKey); } break; } default: case SecurityPolicies.None: { break; } } }
/// <summary> /// Handles a reconnect request. /// </summary> public virtual void Reconnect(IMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request) { throw new NotImplementedException(); }
/// <summary> /// Processes a request message. /// </summary> private bool ProcessRequestMessage(uint messageType, ArraySegment <byte> messageChunk) { // validate the channel state. if (State != TcpChannelState.Open) { ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected request message."); return(false); } // validate security on the message. ChannelToken token = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessRequestMessage")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } if (token == CurrentToken && PreviousToken != null && !PreviousToken.Expired) { Utils.Trace("Server Revoked Token. ChannelId={1}, TokenId={0}", PreviousToken.TokenId, PreviousToken.ChannelId, DateTime.UtcNow); PreviousToken.Lifetime = 0; } } catch (Exception e) { Utils.Trace("Could not verify security on incoming request."); ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed, "Could not verify security on incoming request."); return(false); } BufferCollection chunksToProcess = null; try { // check for an abort. if (TcpMessageType.IsAbort(messageType)) { Utils.Trace("Request was aborted."); chunksToProcess = GetSavedChunks(requestId, messageBody); return(true); } // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(true); } Utils.Trace("Channel {0}: ProcessRequestMessage {1}", ChannelId, requestId); // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); // decode the request. IServiceRequest request = BinaryDecoder.DecodeMessage(new ArraySegmentStream(chunksToProcess), null, Quotas.MessageContext) as IServiceRequest; if (request == null) { SendServiceFault(token, requestId, ServiceResult.Create(StatusCodes.BadStructureMissing, "Could not parse request body.")); return(true); } // ensure that only discovery requests come through unsecured. if (DiscoveryOnly) { if (!(request is GetEndpointsRequest || request is FindServersRequest)) { SendServiceFault(token, requestId, ServiceResult.Create(StatusCodes.BadSecurityPolicyRejected, "Channel can only be used for discovery.")); return(true); } } // hand the request to the server. RequestReceived?.Invoke(this, requestId, request); return(true); } catch (Exception e) { Utils.Trace(e, "Unexpected error processing request."); SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request.")); return(true); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessRequestMessage"); } } }
/// <summary> /// Processes an CloseSecureChannel request message. /// </summary> private bool ProcessCloseSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk) { // validate security on the message. ChannelToken token = null; uint requestId = 0; uint sequenceNumber = 0; ArraySegment <byte> messageBody; try { messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber); // check for replay attacks. if (!VerifySequenceNumber(sequenceNumber, "ProcessCloseSecureChannelRequest")) { throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid); } } catch (Exception e) { throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, e, "Could not verify security on CloseSecureChannel request."); } BufferCollection chunksToProcess = null; try { // check if it is necessary to wait for more chunks. if (!TcpMessageType.IsFinal(messageType)) { SaveIntermediateChunk(requestId, messageBody); return(false); } // get the chunks to process. chunksToProcess = GetSavedChunks(requestId, messageBody); CloseSecureChannelRequest request = BinaryDecoder.DecodeMessage( new ArraySegmentStream(chunksToProcess), typeof(CloseSecureChannelRequest), Quotas.MessageContext) as CloseSecureChannelRequest; if (request == null) { throw ServiceResultException.Create(StatusCodes.BadStructureMissing, "Could not parse CloseSecureChannel request body."); } // send the response. // SendCloseSecureChannelResponse(requestId, token, request); } catch (Exception e) { Utils.Trace(e, "Unexpected error processing OpenSecureChannel request."); } finally { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "ProcessCloseSecureChannelRequest"); } Utils.Trace( "{0} ProcessCloseSecureChannelRequest Socket={0:X8}, ChannelId={1}, TokenId={2}", ChannelName, (Socket != null) ? Socket.Handle : 0, (CurrentToken != null) ? CurrentToken.ChannelId : 0, (CurrentToken != null) ? CurrentToken.TokenId : 0); // close the channel. ChannelClosed(); } // return false would double free the buffer return(true); }