protected HandshakeSession(SecurityParameters securityParameters, ILogger logger) { this.logger = logger; _pluginManager = new CipherSuitePluginManager(this.logger); _state = HandshakeState.Initial; _minVersion = securityParameters.MinimumVersion; _maxVersion = securityParameters.MaximumVersion; _supportedCipherSuites = securityParameters.CipherSuiteIDs.ToArray(); _supportedCompressions = securityParameters.CompressionIDs.ToArray(); _availableCertificates = new List<X509CertificateCollection>(securityParameters.AvailableCertificates); _availablePrivateKeys = new List<CertificatePrivateKey>(securityParameters.AvailablePrivateKeys); _clientCertificates = new X509CertificateCollection(); _serverCertificates = new X509CertificateCollection(); // Initialize the default ClientHello version, to // be as compatible as possible based on maxVersion _version = _minVersion; _cipherSuite = new CipherSuite(_version); }
public Record[] ProcessHandshakeMessages(ProtocolVersion version, HandshakeMessage[] messages, int maxFragmentLength) { List<Record> records = new List<Record>(); // Write all handshake data to memory stream MemoryStream memStream = new MemoryStream(); for (int i = 0; i < messages.Length; i++) { byte[] msgBytes = messages[i].Encode(); memStream.Write(msgBytes, 0, msgBytes.Length); } // Read handshake data from memory stream one at a time memStream.Position = 0; while (memStream.Position < memStream.Length) { long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength); byte[] fragment = new byte[fragmentLength]; memStream.Read(fragment, 0, fragment.Length); Record record = new Record(RecordType.Handshake, version); record.Fragment = fragment; records.Add(record); } return records.ToArray(); }
protected override byte[] EncodeDataBytes(ProtocolVersion ver) { if (CipherSuites.Count == 0 || CompressionMethods.Count == 0) { throw new Exception("No cipher suites or compression methods defined"); } MemoryStream memStream = new MemoryStream(); HandshakeStream stream = new HandshakeStream(memStream); stream.WriteUInt8(ClientVersion.Major); stream.WriteUInt8(ClientVersion.Minor); stream.WriteBytes(Random.GetBytes()); stream.WriteUInt8((Byte)SessionID.Length); stream.WriteBytes(SessionID); if (ClientVersion.IsUsingDatagrams) { stream.WriteUInt8((Byte)Cookie.Length); stream.WriteBytes(Cookie); } stream.WriteUInt16((UInt16)(2 * CipherSuites.Count)); foreach (UInt16 cipher in CipherSuites) { stream.WriteUInt16(cipher); } stream.WriteUInt8((Byte)CompressionMethods.Count); foreach (Byte compression in CompressionMethods) { stream.WriteUInt8(compression); } if (Extensions.Count > 0) { int length = 0; foreach (HelloExtension ext in Extensions) { if (!ext.SupportsProtocolVersion(ClientVersion)) continue; length += 4 + ext.Data.Length; } stream.WriteUInt16((UInt16)length); foreach (HelloExtension ext in Extensions) { if (!ext.SupportsProtocolVersion(ClientVersion)) continue; HelloExtensionType type = ext.Type; byte[] data = ext.Data; stream.WriteUInt16((ushort)type); stream.WriteUInt16((UInt16)data.Length); stream.WriteBytes(data); } } return memStream.ToArray(); }
public HandshakeServerHello(ProtocolVersion version) : base(HandshakeMessageType.ServerHello, version) { ServerVersion = version; Random = new HandshakeRandom(); SessionID = new byte[0]; CipherSuite = 0x0000; CompressionMethod = 0x00; Extensions = new List<HelloExtension>(); }
public HandshakeClientHello(ProtocolVersion version) : base(HandshakeMessageType.ClientHello, version) { ClientVersion = version; Random = new HandshakeRandom(); SessionID = new byte[0]; Cookie = new byte[0]; CipherSuites = new List<CipherSuiteId>(); CompressionMethods = new List<Byte>(); Extensions = new List<HelloExtension>(); }
public HandshakeMessage(HandshakeMessageType type, ProtocolVersion version, byte[] data) { if (data == null) { throw new AlertException(AlertDescription.InternalError, "Trying to create HandshakeMessage with null data"); } Type = type; _version = version; _data = (byte[])data.Clone(); }
private static CipherSuite SelectCipherSuite(CipherSuitePluginManager pluginManager, ProtocolVersion clientVersion, ProtocolVersion minVersion, ProtocolVersion maxVersion, List<CipherSuiteId> clientSuites, List<CipherSuiteId> serverSuites, ServerCertificateSelectionCallback certificateSelectionCallback, List<X509CertificateCollection> availableCertificates) { if (clientVersion < minVersion) { throw new AlertException(AlertDescription.ProtocolVersion, "Offered client version " + clientVersion + " lower than minimum supported version " + minVersion); } // Initialize our return value as null CipherSuite selectedCipherSuite = null; // Run as long as we either select a cipher suite or run out of versions ProtocolVersion selectedVersion = clientVersion < maxVersion ? clientVersion : maxVersion; while (selectedCipherSuite == null) { foreach (CipherSuiteId id in clientSuites) { if (!serverSuites.Contains(id)) continue; // Try initializing the cipher suite based on ID selectedCipherSuite = pluginManager.GetCipherSuite(selectedVersion, (ushort)id); if (selectedCipherSuite == null) continue; // Try selecting a suitable certificate for this cipher suite int certificateIndex = certificateSelectionCallback(selectedCipherSuite, availableCertificates.ToArray()); if (certificateIndex >= 0 && certificateIndex < availableCertificates.Count) { // We finally found the valid suite, break out from the loop break; } // No certificate was found for the suite, ignore selectedCipherSuite = null; } if (selectedCipherSuite != null) break; if (selectedVersion == minVersion) break; selectedVersion = selectedVersion.PreviousProtocolVersion; } if (selectedCipherSuite == null) { throw new AlertException(AlertDescription.HandshakeFailure, "None of the cipher suites offered by client is accepted"); } return selectedCipherSuite; }
protected override void DecodeDataBytes(ProtocolVersion ver, byte[] data) { MemoryStream memStream = new MemoryStream(data); HandshakeStream stream = new HandshakeStream(memStream); byte major = stream.ReadUInt8(); byte minor = stream.ReadUInt8(); ServerVersion = new ProtocolVersion(major, minor); byte[] randomBytes = stream.ReadBytes(32); Random = new HandshakeRandom(randomBytes); int idLength = (int)stream.ReadUInt8(); SessionID = stream.ReadBytes(idLength); Trace.WriteLine($"SessionId: {SessionID}"); CipherSuite = (CipherSuiteId)stream.ReadUInt16(); Trace.WriteLine($"Cipher Suite Id: {CipherSuite} refer to list at http://www.thesprawl.org/research/tls-and-ssl-cipher-suites/"); CompressionMethod = stream.ReadUInt8(); byte[] extensionList = new byte[0]; if (!stream.EndOfStream && ServerVersion.HasExtensions) { UInt16 extensionListLength = stream.ReadUInt16(); extensionList = stream.ReadBytes(extensionListLength); } stream.ConfirmEndOfStream(); int pos = 0; while (pos + 4 <= extensionList.Length) { HelloExtensionType extensionType = (HelloExtensionType)(UInt16)((extensionList[pos] << 8) | extensionList[pos + 1]); Trace.WriteLine($"Extension Type { extensionType }"); UInt16 extensionDataLength = (UInt16)((extensionList[pos + 2] << 8) | extensionList[pos + 3]); pos += 4; if (pos + extensionDataLength > extensionList.Length) { throw new AlertException(AlertDescription.IllegalParameter, "ServerHello extension data length too large: " + extensionDataLength); } byte[] extensionData = new byte[extensionDataLength]; Buffer.BlockCopy(extensionList, pos, extensionData, 0, extensionData.Length); pos += extensionData.Length; Extensions.Add(new HelloExtension(extensionType, extensionData)); } }
public Record[] ProcessOutputData(ProtocolVersion version, byte type, byte[] data, int maxFragmentLength) { List<Record> records = new List<Record>(); // Read handshake data from memory stream one at a time MemoryStream memStream = new MemoryStream(data); while (memStream.Position < memStream.Length) { long fragmentLength = Math.Min(memStream.Length - memStream.Position, maxFragmentLength); byte[] fragment = new byte[fragmentLength]; memStream.Read(fragment, 0, fragment.Length); Record record = new Record((RecordType)type, version); record.Fragment = fragment; records.Add(record); } return records.ToArray(); }
public SecurityParameters() { _availableCertificates = new List<X509CertificateCollection>(); _availablePrivateKeys = new List<CertificatePrivateKey>(); MinimumVersion = ProtocolVersion.SSL3_0; MaximumVersion = ProtocolVersion.TLS1_2; CipherSuiteIDs = new List<CipherSuiteId>(); //CipherSuiteIDs.Add(0x002F); // TLS_RSA_WITH_AES_128_CBC_SHA //CipherSuiteIDs.Add(0x0033); // TLS_DHE_RSA_WITH_AES_128_CBC_SHA CompressionIDs = new List<byte>(); CompressionIDs.Add(0x00); ClientCertificateTypes = new List<CertificateType>(); ClientCertificateAuthorities = new List<string>(); }
protected override byte[] EncodeDataBytes(ProtocolVersion ver) { MemoryStream memStream = new MemoryStream(); HandshakeStream stream = new HandshakeStream(memStream); stream.WriteUInt8(ServerVersion.Major); stream.WriteUInt8(ServerVersion.Minor); stream.WriteBytes(Random.GetBytes()); stream.WriteUInt8((Byte)SessionID.Length); stream.WriteBytes(SessionID); stream.WriteUInt16((ushort)CipherSuite); stream.WriteUInt8(CompressionMethod); if (Extensions.Count > 0) { int length = 0; foreach (HelloExtension ext in Extensions) { if (!ext.SupportsProtocolVersion(ServerVersion)) continue; length += 4 + ext.Data.Length; } stream.WriteUInt16((UInt16)length); foreach (HelloExtension ext in Extensions) { if (!ext.SupportsProtocolVersion(ServerVersion)) continue; HelloExtensionType type = ext.Type; byte[] data = ext.Data; stream.WriteUInt16((ushort)type); stream.WriteUInt16((UInt16)data.Length); stream.WriteBytes(data); } } return memStream.ToArray(); }
public HandshakeMessage[] ProcessHandshakeRecord(ProtocolVersion version, Record record) { List<HandshakeMessage> ret = new List<HandshakeMessage>(); // Write record to input stream and attempt to parse a handshake message _inputStream.Write(record.Fragment, 0, record.Fragment.Length); while (_inputStream.Length - _inputtedBytes >= 4) { byte[] inputBuffer = _inputStream.GetBuffer(); int dataLength = (inputBuffer[_inputtedBytes + 1] << 16) | (inputBuffer[_inputtedBytes + 2] << 8) | inputBuffer[_inputtedBytes + 3]; // Check that if we have enough data to read message now if (4 + dataLength > _inputStream.Length - _inputtedBytes) break; byte[] msgData = new byte[4 + dataLength]; long writePosition = _inputStream.Position; // Read message data from inputStream _inputStream.Position = _inputtedBytes; _inputStream.Read(msgData, 0, msgData.Length); _inputStream.Position = writePosition; _inputtedBytes += msgData.Length; // Add resulting handshake message to the results ret.Add(HandshakeMessage.GetInstance(version, msgData)); } if (_inputtedBytes == _inputStream.Length) { // Reset stream to save memory _inputStream.Position = 0; _inputStream.SetLength(0); _inputtedBytes = 0; } return ret.ToArray(); }
protected override byte[] EncodeDataBytes(ProtocolVersion version) { int certsLength = 0; foreach (X509Certificate cert in CertificateList) { certsLength += 3; certsLength += cert.GetRawCertData().Length; } MemoryStream memStream = new MemoryStream(); HandshakeStream stream = new HandshakeStream(memStream); stream.WriteUInt24((UInt32) certsLength); foreach (X509Certificate cert in CertificateList) { byte[] certBytes = cert.GetRawCertData(); stream.WriteUInt24((UInt32) certBytes.Length); stream.WriteBytes(certBytes); } return memStream.ToArray(); }
protected override void DecodeDataBytes(ProtocolVersion version, byte[] data) { CertificateList.Clear(); MemoryStream memStream = new MemoryStream(data); HandshakeStream stream = new HandshakeStream(memStream); int certsLength = (int) stream.ReadUInt24(); int readBytes = 0; while (readBytes<certsLength) { int certLength = (int) stream.ReadUInt24(); byte[] certBytes = stream.ReadBytes(certLength); readBytes += 3+certLength; CertificateList.Add(new X509Certificate(certBytes)); } if (readBytes != certsLength) { throw new AlertException(AlertDescription.IllegalParameter, "Certificate total length doesn't match contents"); } stream.ConfirmEndOfStream(); }
protected override void DecodeDataBytes(ProtocolVersion ver, byte[] data) { CipherSuites.Clear(); CompressionMethods.Clear(); MemoryStream memStream = new MemoryStream(data); HandshakeStream stream = new HandshakeStream(memStream); byte major = stream.ReadUInt8(); byte minor = stream.ReadUInt8(); ClientVersion = new ProtocolVersion(major, minor); byte[] randomBytes = stream.ReadBytes(32); Random = new HandshakeRandom(randomBytes); int idLength = (int)stream.ReadUInt8(); SessionID = stream.ReadBytes(idLength); if (ClientVersion.IsUsingDatagrams) { int cookieLength = (int)stream.ReadUInt8(); Cookie = stream.ReadBytes(cookieLength); } int cipherLength = (int)stream.ReadUInt16(); for (int i = 0; i < cipherLength; i += 2) { CipherSuites.Add((CipherSuiteId)stream.ReadUInt16()); } int compressionLength = (int)stream.ReadUInt8(); for (int i = 0; i < compressionLength; i++) { CompressionMethods.Add(stream.ReadUInt8()); } byte[] extensionList = new byte[0]; if (!stream.EndOfStream && ClientVersion.HasExtensions) { UInt16 extensionListLength = stream.ReadUInt16(); extensionList = stream.ReadBytes(extensionListLength); } stream.ConfirmEndOfStream(); int pos = 0; while (pos + 4 <= extensionList.Length) { HelloExtensionType extensionType = (HelloExtensionType)((extensionList[pos] << 8) | extensionList[pos + 1]); UInt16 extensionDataLength = (UInt16)((extensionList[pos + 2] << 8) | extensionList[pos + 3]); pos += 4; if (pos + extensionDataLength > extensionList.Length) { throw new AlertException(AlertDescription.IllegalParameter, "ClientHello extension data length too large: " + extensionDataLength); } byte[] extensionData = new byte[extensionDataLength]; Buffer.BlockCopy(extensionList, pos, extensionData, 0, extensionData.Length); pos += extensionData.Length; Extensions.Add(new HelloExtension(extensionType, extensionData)); } }
protected static byte[] GenerateMasterSecret(ProtocolVersion version, CipherSuite cipherSuite, ConnectionState connectionState) { byte[] seed = new byte[64]; Array.Copy(connectionState.ClientRandom, 0, seed, 0, 32); Array.Copy(connectionState.ServerRandom, 0, seed, 32, 32); byte[] masterSecret = cipherSuite.KeyExchangeAlgorithm.GetMasterSecret(cipherSuite.PseudoRandomFunction, seed); if (masterSecret == null) { throw new Exception("Could not generate master secret"); } return masterSecret; }
private void ValidateForProtocolVersion(ProtocolVersion version) { switch (Description) { // Warning alerts that are SSLv3 only case AlertDescription.NoCertificate: Level = AlertLevel.Warning; if (version != ProtocolVersion.SSL3_0) Level = AlertLevel.None; break; // Warning alerts supported in all versions case AlertDescription.CloseNotify: Level = AlertLevel.Warning; break; // Fatal alerts supported in all versions case AlertDescription.UnexpectedMessage: case AlertDescription.BadRecordMAC: case AlertDescription.DecompressionFailure: case AlertDescription.HandshakeFailure: case AlertDescription.IllegalParameter: Level = AlertLevel.Fatal; break; // Other alerts supported in all versions case AlertDescription.BadCertificate: case AlertDescription.UnsupportedCertificate: case AlertDescription.CertificateRevoked: case AlertDescription.CertificateExpired: case AlertDescription.CertificateUnknown: break; // Fatal alerts that are TLS only case AlertDescription.UnknownCA: case AlertDescription.AccessDenied: Level = AlertLevel.Fatal; if (version == ProtocolVersion.SSL3_0) Description = AlertDescription.CertificateUnknown; break; case AlertDescription.RecordOverflow: case AlertDescription.DecodeError: case AlertDescription.DecryptError: case AlertDescription.InternalError: Level = AlertLevel.Fatal; if (version == ProtocolVersion.SSL3_0) Description = AlertDescription.IllegalParameter; break; case AlertDescription.ProtocolVersion: case AlertDescription.InsufficientSecurity: Level = AlertLevel.Fatal; if (version == ProtocolVersion.SSL3_0) Description = AlertDescription.HandshakeFailure; break; // Warning alerts that are TLS only case AlertDescription.UserCanceled: case AlertDescription.NoRenegotiation: Level = AlertLevel.Warning; if (version == ProtocolVersion.SSL3_0) Level = AlertLevel.None; break; // Fatal alerts that are TLSv1.2 only case AlertDescription.UnsupportedExtension: Level = AlertLevel.Fatal; if (version != ProtocolVersion.TLS1_2) Description = AlertDescription.IllegalParameter; break; } }
public HandshakeHelloVerifyRequest(ProtocolVersion version) : base(HandshakeMessageType.HelloVerifyRequest, version) { ServerVersion = version; Cookie = new byte[0]; }
public static HandshakeMessage GetInstance(ProtocolVersion version, byte[] data) { HandshakeMessage ret = null; switch ((HandshakeMessageType) data[0]) { case HandshakeMessageType.HelloRequest: ret = new HandshakeMessage(HandshakeMessageType.HelloRequest, version); break; case HandshakeMessageType.ClientHello: ret = new HandshakeClientHello(); break; case HandshakeMessageType.ServerHello: ret = new HandshakeServerHello(); break; case HandshakeMessageType.HelloVerifyRequest: ret = new HandshakeMessage(HandshakeMessageType.HelloVerifyRequest, version); break; case HandshakeMessageType.NewSessionTicket: ret = new HandshakeMessage(HandshakeMessageType.NewSessionTicket, version); break; case HandshakeMessageType.Certificate: ret = new HandshakeCertificate(version); break; case HandshakeMessageType.ServerKeyExchange: ret = new HandshakeMessage(HandshakeMessageType.ServerKeyExchange, version); break; case HandshakeMessageType.CertificateRequest: ret = new HandshakeCertificateRequest(version); break; case HandshakeMessageType.ServerHelloDone: ret = new HandshakeMessage(HandshakeMessageType.ServerHelloDone, version); break; case HandshakeMessageType.CertificateVerify: ret = new HandshakeMessage(HandshakeMessageType.CertificateVerify, version); break; case HandshakeMessageType.ClientKeyExchange: ret = new HandshakeMessage(HandshakeMessageType.ClientKeyExchange, version); break; case HandshakeMessageType.Finished: ret = new HandshakeMessage(HandshakeMessageType.Finished, version); break; } if (ret != null) { ret.Decode(data); return ret; } return null; }
protected virtual void DecodeDataBytes(ProtocolVersion version, byte[] data) { _data = data; }
protected virtual byte[] EncodeDataBytes(ProtocolVersion version) { return _data; }
public HandshakeCertificate(ProtocolVersion version) : base(HandshakeMessageType.Certificate, version) { CertificateList = new X509CertificateCollection(); }
public HandshakeMessage(HandshakeMessageType type, ProtocolVersion version) { Type = type; _version = version; _data = new byte[0]; }
protected override void ProcessClientHello(HandshakeClientHello clientHello) { if (_state == HandshakeState.Finished) { throw new AlertException(AlertDescription.NoRenegotiation, "Renegotiation not supported"); } else if (_state != HandshakeState.Initial) { throw new AlertException(AlertDescription.UnexpectedMessage, "Client hello received at the wrong time"); } // Find the compressions that match our supported compressions List<Byte> matchedCompressions = new List<Byte>(); foreach (Byte id in _supportedCompressions) { if (clientHello.CompressionMethods.Contains(id)) { matchedCompressions.Add(id); } } if (matchedCompressions.Count == 0) { throw new AlertException(AlertDescription.HandshakeFailure, "None of the compression methods offered by client is accepted"); } _clientVersion = clientHello.ClientVersion; _cipherSuite = SelectCipherSuite(_pluginManager, _clientVersion, _minVersion, _maxVersion, clientHello.CipherSuites, new List<CipherSuiteId>(_supportedCipherSuites), _certificateSelectionCallback, _availableCertificates); _version = _cipherSuite.ProtocolVersion; // Select the certificate and private key we are going to send int certificateIndex = _certificateSelectionCallback(_cipherSuite, _availableCertificates.ToArray()); if (certificateIndex >= 0 && certificateIndex < _availableCertificates.Count) { _serverCertificates.AddRange(_availableCertificates[certificateIndex]); _selectedPrivateKey = _availablePrivateKeys[certificateIndex]; } // TODO: Create the server hello message correctly HandshakeServerHello serverHello = new HandshakeServerHello(_version); serverHello.CipherSuite = (CipherSuiteId)_cipherSuite.CipherSuiteID; serverHello.CompressionMethod = matchedCompressions[0]; OutputMessage(serverHello); // Initialize the handshake randoms and cipher suite _connectionState.ClientRandom = clientHello.Random.GetBytes(); _connectionState.ServerRandom = serverHello.Random.GetBytes(); // TODO: If we resumed our session, set state to SendChangeCipherSpec and return // Send certificate if required and valid if (!_cipherSuite.IsAnonymous) { if (_serverCertificates.Count == 0) { throw new AlertException(AlertDescription.HandshakeFailure, "Certificate required but not selected"); } string keyexAlg = _cipherSuite.KeyExchangeAlgorithm.CertificateKeyAlgorithm; string signAlg = _cipherSuite.SignatureAlgorithm.CertificateKeyAlgorithm; X509Certificate cert = _serverCertificates[0]; if (keyexAlg != null && !keyexAlg.Equals(cert.GetKeyAlgorithm())) { throw new AlertException(AlertDescription.HandshakeFailure, "Selected certificate type " + cert.GetKeyAlgorithm() + " doesn't match key exchange type " + keyexAlg); } if (signAlg != null && !signAlg.Equals(cert.GetKeyAlgorithm())) { throw new AlertException(AlertDescription.HandshakeFailure, "Selected certificate type " + cert.GetKeyAlgorithm() + " doesn't match signature type " + signAlg); } HandshakeCertificate certificate = new HandshakeCertificate(_version); certificate.CertificateList.AddRange(_serverCertificates); OutputMessage(certificate); } byte[] serverKeys = _cipherSuite.KeyExchangeAlgorithm.GetServerKeys(_maxVersion, _selectedPrivateKey); if (serverKeys != null) { // Generate the signature for server keys byte[] dataToSign = new byte[_connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length + serverKeys.Length]; Buffer.BlockCopy(_connectionState.ClientRandom, 0, dataToSign, 0, _connectionState.ClientRandom.Length); Buffer.BlockCopy(_connectionState.ServerRandom, 0, dataToSign, _connectionState.ClientRandom.Length, _connectionState.ServerRandom.Length); Buffer.BlockCopy(serverKeys, 0, dataToSign, _connectionState.ClientRandom.Length + _connectionState.ServerRandom.Length, serverKeys.Length); byte[] signature = GenerateSignature(_selectedPrivateKey, dataToSign); Log.Debug("Server Signature: " + BitConverter.ToString(signature)); // Combine the server keys with the signature byte[] signedKeys = new byte[serverKeys.Length + signature.Length]; Buffer.BlockCopy(serverKeys, 0, signedKeys, 0, serverKeys.Length); Buffer.BlockCopy(signature, 0, signedKeys, serverKeys.Length, signature.Length); HandshakeMessage keyex = new HandshakeMessage(HandshakeMessageType.ServerKeyExchange, _version, signedKeys); OutputMessage(keyex); } // Send certificate request if needed if (!_cipherSuite.IsAnonymous && _certificateRequest != null) { OutputMessage(_certificateRequest); _certificateRequestSent = true; } HandshakeMessage serverHelloDone = new HandshakeMessage(HandshakeMessageType.ServerHelloDone, _version); OutputMessage(serverHelloDone); // Wait for client certificate or client key exchange _state = HandshakeState.ReceivedClientHello; }
public Alert(AlertLevel level, AlertDescription description, ProtocolVersion version) { Level = level; Description = description; ValidateForProtocolVersion(version); }
public Record(RecordType type, ProtocolVersion version) : this(type, version, new byte[0]) { }
public Record(RecordType type, ProtocolVersion version, byte[] fragment) { this.Type = type; this.Version = version; this.Fragment = fragment; }
public virtual bool SupportsProtocolVersion(ProtocolVersion version) { return version.HasExtensions; }
public override bool SupportsProtocolVersion(ProtocolVersion version) { return version.HasSelectableSighash; }
public Alert(AlertDescription description, ProtocolVersion version) : this(AlertLevel.Fatal, description, version) {}