protected SslHandshakeStatus ProcessAlert(RecordMessage message) { if (message.length != 2 || message.fragment.Length != 2) { throw new SslException(AlertDescription.RecordOverflow, "The alert message is invalid."); } try { AlertLevel level = (AlertLevel)message.fragment[0]; AlertDescription description = (AlertDescription)message.fragment[1]; if (level == AlertLevel.Fatal) { throw new SslException(description, "The other side has sent a failure alert."); } SslHandshakeStatus ret; if (description == AlertDescription.CloseNotify) { if (m_State == HandshakeType.ShuttingDown) // true if we've already sent a shutdown notification // close connection { ret = new SslHandshakeStatus(SslStatus.Close, null); } else { // send a shutdown notifications, and then close the connection ret = new SslHandshakeStatus(SslStatus.Close, GetControlBytes(ControlType.Shutdown)); } } else { ret = new SslHandshakeStatus(SslStatus.OK, null); } return(ret); } catch (SslException t) { throw t; } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "There was an internal error."); } }
protected abstract SslHandshakeStatus ProcessChangeCipherSpec(RecordMessage message);
protected override SslHandshakeStatus ProcessChangeCipherSpec(RecordMessage message) { if (message.length != 1 || message.fragment[0] != 1) throw new SslException(AlertDescription.IllegalParameter, "The ChangeCipherSpec message was invalid."); if (m_State == HandshakeType.ServerHelloDone) { m_RecordLayer.ChangeRemoteState(null, m_CipherSuite.Decryptor, m_CipherSuite.RemoteHasher); return new SslHandshakeStatus(SslStatus.MessageIncomplete, null); // needs a finished message } else { throw new SslException(AlertDescription.UnexpectedMessage, "ChangeCipherSpec message must be preceded by a ServerHelloDone message."); } }
protected SslHandshakeStatus ProcessAlert(RecordMessage message) { if (message.length != 2 || message.fragment.Length != 2) throw new SslException(AlertDescription.RecordOverflow, "The alert message is invalid."); try { AlertLevel level = (AlertLevel)message.fragment[0]; AlertDescription description = (AlertDescription)message.fragment[1]; if (level == AlertLevel.Fatal) throw new SslException(description, "The other side has sent a failure alert."); SslHandshakeStatus ret; if (description == AlertDescription.CloseNotify) { if (m_State == HandshakeType.ShuttingDown) { // true if we've already sent a shutdown notification // close connection ret = new SslHandshakeStatus(SslStatus.Close, null); } else { // send a shutdown notifications, and then close the connection ret = new SslHandshakeStatus(SslStatus.Close, GetControlBytes(ControlType.Shutdown)); } } else { ret = new SslHandshakeStatus(SslStatus.OK, null); } return ret; } catch (SslException t) { throw t; } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "There was an internal error."); } }
// processes Handshake & ChangeCipherSpec messages public SslHandshakeStatus ProcessMessages(RecordMessage message) { if (message == null) throw new ArgumentNullException(); SslHandshakeStatus ret; if (message.contentType == ContentType.ChangeCipherSpec) { ret = ProcessChangeCipherSpec(message); m_State = HandshakeType.ChangeCipherSpec; } else if (message.contentType == ContentType.Handshake) { ret = new SslHandshakeStatus(); // copy the new bytes and the old bytes in one buffer MemoryStream ms = new MemoryStream(); byte[] fullbuffer = new byte[m_IncompleteMessage.Length + message.length]; Array.Copy(m_IncompleteMessage, 0, fullbuffer, 0, m_IncompleteMessage.Length); Array.Copy(message.fragment, 0, fullbuffer, m_IncompleteMessage.Length, message.length); // loop through all messages in buffer, if any int offset = 0; HandshakeMessage hm = GetHandshakeMessage(fullbuffer, offset); while(hm != null) { offset += hm.fragment.Length + 4; SslHandshakeStatus status = ProcessMessage(hm); if (status.Message != null) { ms.Write(status.Message, 0, status.Message.Length); } ret.Status = status.Status; // go to next message m_State = hm.type; hm = GetHandshakeMessage(fullbuffer, offset); } if (offset > 0) { m_IncompleteMessage = new byte[fullbuffer.Length - offset]; Array.Copy(fullbuffer, offset, m_IncompleteMessage, 0, m_IncompleteMessage.Length); } else { m_IncompleteMessage = fullbuffer; } if (ms.Length > 0) { ret.Message = ms.ToArray(); } ms.Close(); } else { // message.contentType == ContentType.Alert ret = ProcessAlert(message); } return ret; }
public SslRecordStatus ProcessBytes(byte[] buffer, int offset, int size) { if (buffer == null) throw new ArgumentNullException(); if (offset < 0 || offset + size > buffer.Length || size <= 0) throw new ArgumentException(); SslRecordStatus ret = new SslRecordStatus(); ret.Status = SslStatus.MessageIncomplete; MemoryStream decrypted = new MemoryStream(); MemoryStream protocol = new MemoryStream(); // copy the new bytes and the old bytes in one buffer byte[] fullbuffer = new byte[m_IncompleteMessage.Length + size]; Array.Copy(m_IncompleteMessage, 0, fullbuffer, 0, m_IncompleteMessage.Length); Array.Copy(buffer, offset, fullbuffer, m_IncompleteMessage.Length, size); // extract all record messages, if any, and process them int recordSize = 0; int recordLength; while(IsRecordMessageComplete(fullbuffer, recordSize)) { RecordMessage message = new RecordMessage(fullbuffer, recordSize); recordLength = message.length + 5; UnwrapMessage(message); // decrypt and verify message // process message if (message.contentType == ContentType.ApplicationData) { if (!m_HandshakeLayer.IsNegotiating()) { decrypted.Write(message.fragment, 0, message.fragment.Length); } else { throw new SslException(AlertDescription.UnexpectedMessage, "The handshake procedure was not completed successfully before application data was received."); } ret.Status = SslStatus.OK; } else { // handshake message or change cipher spec message SslHandshakeStatus status = m_HandshakeLayer.ProcessMessages(message); if (status.Message != null) protocol.Write(status.Message, 0, status.Message.Length); ret.Status = status.Status; } recordSize += recordLength; } // copy remaining data [incomplete record] if (recordSize > 0) { m_IncompleteMessage = new byte[fullbuffer.Length - recordSize]; Array.Copy(fullbuffer, recordSize, m_IncompleteMessage, 0, m_IncompleteMessage.Length); } else { m_IncompleteMessage = fullbuffer; } if (decrypted.Length > 0) { ret.Decrypted = decrypted.ToArray(); } decrypted.Close(); if (protocol.Length > 0) { ret.Buffer = protocol.ToArray(); } protocol.Close(); return ret; }
} //*/ protected void UnwrapMessage(RecordMessage message) { if (message.length != message.fragment.Length) throw new SslException(AlertDescription.IllegalParameter, "Message length is invalid."); byte[] remoteMac = null, decrypted = null, localMac = null; bool cipherError = false; // decrypt and verify the message if (m_BulkDecryption != null) { if (message.length <= m_RemoteHasher.HashSize / 8) throw new SslException(AlertDescription.DecodeError, "Message is too small."); if (message.length % m_BulkDecryption.OutputBlockSize != 0) throw new SslException(AlertDescription.DecryptError, "Message length is invalid."); // decrypt the message if (m_BulkDecryption.OutputBlockSize == 1) { // is stream cipher? decrypted = new byte[message.length]; m_BulkDecryption.TransformBlock(message.fragment, 0, message.length, decrypted, 0); remoteMac = new byte[m_RemoteHasher.HashSize / 8]; Array.Copy(decrypted, message.length - remoteMac.Length, remoteMac, 0, remoteMac.Length); message.fragment = new byte[decrypted.Length - remoteMac.Length]; Array.Copy(decrypted, 0, message.fragment, 0, message.fragment.Length); message.length = (ushort)message.fragment.Length; } else { // cipher is block cipher decrypted = new byte[message.fragment.Length]; m_BulkDecryption.TransformBlock(message.fragment, 0, decrypted.Length, decrypted, 0); byte padding = decrypted[decrypted.Length - 1]; if (message.length < padding + m_RemoteHasher.HashSize / 8 + 1) { cipherError = true; remoteMac = new byte[m_RemoteHasher.HashSize / 8]; } else { int realSize = (message.length - padding) - 1; remoteMac = new byte[m_RemoteHasher.HashSize / 8]; Array.Copy(decrypted, realSize - remoteMac.Length, remoteMac, 0, remoteMac.Length); message.fragment = new byte[realSize - remoteMac.Length]; Array.Copy(decrypted, 0, message.fragment, 0, message.fragment.Length); message.length = (ushort)message.fragment.Length; if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { // check padding for(int i = realSize; i < decrypted.Length; i++) { if (decrypted[i] != padding) { cipherError = true; } } } } } // calculate the MAC localMac = GetULongBytes(m_InputSequenceNumber); m_RemoteHasher.Initialize(); m_RemoteHasher.TransformBlock(localMac, 0, localMac.Length, localMac, 0); // seq_num + .. localMac = message.ToBytes(); if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { m_RemoteHasher.TransformFinalBlock(localMac, 0, localMac.Length); // .. + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment } else if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Ssl3) { m_RemoteHasher.TransformBlock(localMac, 0, 1, localMac, 0); // type m_RemoteHasher.TransformFinalBlock(localMac, 3, localMac.Length - 3); // length + fragment } else { throw new NotSupportedException("Only SSL3 and TLS1 are supported"); } localMac = m_RemoteHasher.Hash; // compare MACs for(int i = 0; i < remoteMac.Length; i++) { if (remoteMac[i] != localMac[i]) { cipherError = true; } } // throw cipher error, if necessary if (cipherError) throw new SslException(AlertDescription.BadRecordMac, "An error occurred during the decryption and verification process."); } // decompress the message if (m_RemoteCompressor != null) { message.fragment = m_RemoteCompressor.Decompress(message.fragment); message.length = (ushort)message.fragment.Length; } // final adjustments message.messageType = MessageType.PlainText; m_InputSequenceNumber++; }
protected void WrapMessage(RecordMessage message) { if (message.length != message.fragment.Length) throw new SslException(AlertDescription.IllegalParameter, "Message length is invalid."); byte[] mac = null; try { // compress the message if (m_LocalCompressor != null) { message.fragment = m_LocalCompressor.Compress(message.fragment); message.length = (ushort)message.fragment.Length; } // encrypt the message and MAC if (m_LocalHasher != null) { // calculate the MAC mac = GetULongBytes(m_OutputSequenceNumber); m_LocalHasher.Initialize(); m_LocalHasher.TransformBlock(mac, 0, mac.Length, mac, 0); // seq_num + .. mac = message.ToBytes(); if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { m_LocalHasher.TransformFinalBlock(mac, 0, mac.Length); // .. + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment } else if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Ssl3) { m_LocalHasher.TransformBlock(mac, 0, 1, mac, 0); // type m_LocalHasher.TransformFinalBlock(mac, 3, mac.Length - 3); // length + fragment } else { throw new NotSupportedException("Only SSL3 and TLS1 are supported"); } mac = m_LocalHasher.Hash; // encrypt the message if (m_BulkEncryption.OutputBlockSize == 1) { // is stream cipher? byte[] ret = new byte[message.length + mac.Length]; m_BulkEncryption.TransformBlock(message.fragment, 0, message.length, ret, 0); m_BulkEncryption.TransformBlock(mac, 0, mac.Length, ret, message.length); message.fragment = ret; } else { // cipher is block cipher int obs = m_BulkEncryption.OutputBlockSize; byte padding = (byte)((obs - (message.length + mac.Length + 1) % obs) % obs); byte[] ret = new byte[message.length + mac.Length + padding + 1]; Array.Copy(message.fragment, 0, ret, 0, message.length); Array.Copy(mac, 0, ret, message.length, mac.Length); if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { for(int i = message.length + mac.Length; i < ret.Length; i++) { ret[i] = padding; } } else { byte[] buffer = new byte[ret.Length - message.length - mac.Length]; m_HandshakeLayer.RNG.GetBytes(buffer); Array.Copy(buffer, 0, ret, message.length + mac.Length, buffer.Length); ret[ret.Length - 1] = padding; } m_BulkEncryption.TransformBlock(ret, 0, ret.Length, ret, 0); message.fragment = ret; } message.length = (ushort)message.fragment.Length; } } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "An exception occurred"); } // final adjustments message.messageType = MessageType.Encrypted; m_OutputSequenceNumber++; } //*/
protected byte[] InternalEncryptBytes(byte[] buffer, int offset, int size, ContentType type) { // only accepts sizes of less than 16Kb; does not do any error checking byte[] bytes = new byte[size]; Array.Copy(buffer, offset, bytes, 0, size); RecordMessage message = new RecordMessage(MessageType.PlainText, type, m_HandshakeLayer.GetVersion(), bytes); WrapMessage(message); return message.ToBytes(); }
public SslRecordStatus ProcessBytes(byte[] buffer, int offset, int size) { if (buffer == null) { throw new ArgumentNullException(); } if (offset < 0 || offset + size > buffer.Length || size <= 0) { throw new ArgumentException(); } SslRecordStatus ret = new SslRecordStatus(); ret.Status = SslStatus.MessageIncomplete; MemoryStream decrypted = new MemoryStream(); MemoryStream protocol = new MemoryStream(); // copy the new bytes and the old bytes in one buffer byte[] fullbuffer = new byte[m_IncompleteMessage.Length + size]; Array.Copy(m_IncompleteMessage, 0, fullbuffer, 0, m_IncompleteMessage.Length); Array.Copy(buffer, offset, fullbuffer, m_IncompleteMessage.Length, size); // extract all record messages, if any, and process them int recordSize = 0; int recordLength; while (IsRecordMessageComplete(fullbuffer, recordSize)) { RecordMessage message = new RecordMessage(fullbuffer, recordSize); recordLength = message.length + 5; UnwrapMessage(message); // decrypt and verify message // process message if (message.contentType == ContentType.ApplicationData) { if (!m_HandshakeLayer.IsNegotiating()) { decrypted.Write(message.fragment, 0, message.fragment.Length); } else { throw new SslException(AlertDescription.UnexpectedMessage, "The handshake procedure was not completed successfully before application data was received."); } ret.Status = SslStatus.OK; } else // handshake message or change cipher spec message { SslHandshakeStatus status = m_HandshakeLayer.ProcessMessages(message); if (status.Message != null) { protocol.Write(status.Message, 0, status.Message.Length); } ret.Status = status.Status; } recordSize += recordLength; } // copy remaining data [incomplete record] if (recordSize > 0) { m_IncompleteMessage = new byte[fullbuffer.Length - recordSize]; Array.Copy(fullbuffer, recordSize, m_IncompleteMessage, 0, m_IncompleteMessage.Length); } else { m_IncompleteMessage = fullbuffer; } if (decrypted.Length > 0) { ret.Decrypted = decrypted.ToArray(); } decrypted.Close(); if (protocol.Length > 0) { ret.Buffer = protocol.ToArray(); } protocol.Close(); return(ret); }
} //*/ protected void UnwrapMessage(RecordMessage message) { if (message.length != message.fragment.Length) { throw new SslException(AlertDescription.IllegalParameter, "Message length is invalid."); } byte[] remoteMac = null, decrypted = null, localMac = null; bool cipherError = false; // decrypt and verify the message if (m_BulkDecryption != null) { if (message.length <= m_RemoteHasher.HashSize / 8) { throw new SslException(AlertDescription.DecodeError, "Message is too small."); } if (message.length % m_BulkDecryption.OutputBlockSize != 0) { throw new SslException(AlertDescription.DecryptError, "Message length is invalid."); } // decrypt the message if (m_BulkDecryption.OutputBlockSize == 1) // is stream cipher? { decrypted = new byte[message.length]; m_BulkDecryption.TransformBlock(message.fragment, 0, message.length, decrypted, 0); remoteMac = new byte[m_RemoteHasher.HashSize / 8]; Array.Copy(decrypted, message.length - remoteMac.Length, remoteMac, 0, remoteMac.Length); message.fragment = new byte[decrypted.Length - remoteMac.Length]; Array.Copy(decrypted, 0, message.fragment, 0, message.fragment.Length); message.length = (ushort)message.fragment.Length; } else // cipher is block cipher { decrypted = new byte[message.fragment.Length]; m_BulkDecryption.TransformBlock(message.fragment, 0, decrypted.Length, decrypted, 0); byte padding = decrypted[decrypted.Length - 1]; if (message.length < padding + m_RemoteHasher.HashSize / 8 + 1) { cipherError = true; remoteMac = new byte[m_RemoteHasher.HashSize / 8]; } else { int realSize = (message.length - padding) - 1; remoteMac = new byte[m_RemoteHasher.HashSize / 8]; Array.Copy(decrypted, realSize - remoteMac.Length, remoteMac, 0, remoteMac.Length); message.fragment = new byte[realSize - remoteMac.Length]; Array.Copy(decrypted, 0, message.fragment, 0, message.fragment.Length); message.length = (ushort)message.fragment.Length; if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { // check padding for (int i = realSize; i < decrypted.Length; i++) { if (decrypted[i] != padding) { cipherError = true; } } } } } // calculate the MAC localMac = GetULongBytes(m_InputSequenceNumber); m_RemoteHasher.Initialize(); m_RemoteHasher.TransformBlock(localMac, 0, localMac.Length, localMac, 0); // seq_num + .. localMac = message.ToBytes(); if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { m_RemoteHasher.TransformFinalBlock(localMac, 0, localMac.Length); // .. + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment } else if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Ssl3) { m_RemoteHasher.TransformBlock(localMac, 0, 1, localMac, 0); // type m_RemoteHasher.TransformFinalBlock(localMac, 3, localMac.Length - 3); // length + fragment } else { throw new NotSupportedException("Only SSL3 and TLS1 are supported"); } localMac = m_RemoteHasher.Hash; // compare MACs for (int i = 0; i < remoteMac.Length; i++) { if (remoteMac[i] != localMac[i]) { cipherError = true; } } // throw cipher error, if necessary if (cipherError) { throw new SslException(AlertDescription.BadRecordMac, "An error occurred during the decryption and verification process."); } } // decompress the message if (m_RemoteCompressor != null) { message.fragment = m_RemoteCompressor.Decompress(message.fragment); message.length = (ushort)message.fragment.Length; } // final adjustments message.messageType = MessageType.PlainText; m_InputSequenceNumber++; }
protected void WrapMessage(RecordMessage message) { if (message.length != message.fragment.Length) { throw new SslException(AlertDescription.IllegalParameter, "Message length is invalid."); } byte[] mac = null; try { // compress the message if (m_LocalCompressor != null) { message.fragment = m_LocalCompressor.Compress(message.fragment); message.length = (ushort)message.fragment.Length; } // encrypt the message and MAC if (m_LocalHasher != null) { // calculate the MAC mac = GetULongBytes(m_OutputSequenceNumber); m_LocalHasher.Initialize(); m_LocalHasher.TransformBlock(mac, 0, mac.Length, mac, 0); // seq_num + .. mac = message.ToBytes(); if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { m_LocalHasher.TransformFinalBlock(mac, 0, mac.Length); // .. + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length + TLSCompressed.fragment } else if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Ssl3) { m_LocalHasher.TransformBlock(mac, 0, 1, mac, 0); // type m_LocalHasher.TransformFinalBlock(mac, 3, mac.Length - 3); // length + fragment } else { throw new NotSupportedException("Only SSL3 and TLS1 are supported"); } mac = m_LocalHasher.Hash; // encrypt the message if (m_BulkEncryption.OutputBlockSize == 1) // is stream cipher? { byte[] ret = new byte[message.length + mac.Length]; m_BulkEncryption.TransformBlock(message.fragment, 0, message.length, ret, 0); m_BulkEncryption.TransformBlock(mac, 0, mac.Length, ret, message.length); message.fragment = ret; } else // cipher is block cipher { int obs = m_BulkEncryption.OutputBlockSize; byte padding = (byte)((obs - (message.length + mac.Length + 1) % obs) % obs); byte[] ret = new byte[message.length + mac.Length + padding + 1]; Array.Copy(message.fragment, 0, ret, 0, message.length); Array.Copy(mac, 0, ret, message.length, mac.Length); if (m_HandshakeLayer.GetProtocol() == SecureProtocol.Tls1) { for (int i = message.length + mac.Length; i < ret.Length; i++) { ret[i] = padding; } } else { byte[] buffer = new byte[ret.Length - message.length - mac.Length]; m_HandshakeLayer.RNG.GetBytes(buffer); Array.Copy(buffer, 0, ret, message.length + mac.Length, buffer.Length); ret[ret.Length - 1] = padding; } m_BulkEncryption.TransformBlock(ret, 0, ret.Length, ret, 0); message.fragment = ret; } message.length = (ushort)message.fragment.Length; } } catch (Exception e) { throw new SslException(e, AlertDescription.InternalError, "An exception occurred"); } // final adjustments message.messageType = MessageType.Encrypted; m_OutputSequenceNumber++; } //*/