public bool IsLaterVersionOf(ProtocolVersion version) { if (MajorVersion != version.MajorVersion) { return false; } int diffMinorVersion = version.MinorVersion - MinorVersion; return IsDtls ? diffMinorVersion > 0 : diffMinorVersion < 0; }
public bool IsEqualOrEarlierVersionOf(ProtocolVersion version) { if (MajorVersion != version.MajorVersion) { return false; } int diffMinorVersion = version.MinorVersion - MinorVersion; return IsDtls ? diffMinorVersion <= 0 : diffMinorVersion >= 0; }
protected virtual void ReportServerVersion(ClientHandshakeState state, ProtocolVersion server_version) { TlsClientContextImpl clientContext = state.clientContext; ProtocolVersion currentServerVersion = clientContext.ServerVersion; if (null == currentServerVersion) { clientContext.SetServerVersion(server_version); state.client.NotifyServerVersion(server_version); } else if (!currentServerVersion.Equals(server_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } }
public virtual void NotifyClientVersion(ProtocolVersion clientVersion) { this.mClientVersion = clientVersion; }
internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer) { SecurityParameters securityParameters = state.serverContext.SecurityParameters; DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.serverContext, recordLayer); DtlsReliableHandshake.Message clientMessage = handshake.ReceiveMessage(); { // NOTE: After receiving a record from the client, we discover the record layer version ProtocolVersion client_version = recordLayer.DiscoveredPeerVersion; // TODO Read RFCs for guidance on the expected record layer version number state.serverContext.SetClientVersion(client_version); } if (clientMessage.Type == HandshakeType.client_hello) { ProcessClientHello(state, clientMessage.Body); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } { byte[] serverHelloBody = GenerateServerHello(state); if (state.maxFragmentLength >= 0) { int plainTextLimit = 1 << (8 + state.maxFragmentLength); recordLayer.SetPlaintextLimit(plainTextLimit); } securityParameters.cipherSuite = state.selectedCipherSuite; securityParameters.compressionAlgorithm = (byte)state.selectedCompressionMethod; securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.serverContext, state.selectedCipherSuite); /* * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length * has a verify_data_length equal to 12. This includes all existing cipher suites. */ securityParameters.verifyDataLength = 12; handshake.SendMessage(HandshakeType.server_hello, serverHelloBody); } handshake.NotifyHelloComplete(); IList serverSupplementalData = state.server.GetServerSupplementalData(); if (serverSupplementalData != null) { byte[] supplementalDataBody = GenerateSupplementalData(serverSupplementalData); handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody); } state.keyExchange = state.server.GetKeyExchange(); state.keyExchange.Init(state.serverContext); state.serverCredentials = state.server.GetCredentials(); Certificate serverCertificate = null; if (state.serverCredentials == null) { state.keyExchange.SkipServerCredentials(); } else { state.keyExchange.ProcessServerCredentials(state.serverCredentials); serverCertificate = state.serverCredentials.Certificate; byte[] certificateBody = GenerateCertificate(serverCertificate); handshake.SendMessage(HandshakeType.certificate, certificateBody); } // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus if (serverCertificate == null || serverCertificate.IsEmpty) { state.allowCertificateStatus = false; } if (state.allowCertificateStatus) { CertificateStatus certificateStatus = state.server.GetCertificateStatus(); if (certificateStatus != null) { byte[] certificateStatusBody = GenerateCertificateStatus(state, certificateStatus); handshake.SendMessage(HandshakeType.certificate_status, certificateStatusBody); } } byte[] serverKeyExchange = state.keyExchange.GenerateServerKeyExchange(); if (serverKeyExchange != null) { handshake.SendMessage(HandshakeType.server_key_exchange, serverKeyExchange); } if (state.serverCredentials != null) { state.certificateRequest = state.server.GetCertificateRequest(); if (state.certificateRequest != null) { state.keyExchange.ValidateCertificateRequest(state.certificateRequest); byte[] certificateRequestBody = GenerateCertificateRequest(state, state.certificateRequest); handshake.SendMessage(HandshakeType.certificate_request, certificateRequestBody); TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, state.certificateRequest.SupportedSignatureAlgorithms); } } handshake.SendMessage(HandshakeType.server_hello_done, TlsUtilities.EmptyBytes); handshake.HandshakeHash.SealHashAlgorithms(); clientMessage = handshake.ReceiveMessage(); if (clientMessage.Type == HandshakeType.supplemental_data) { ProcessClientSupplementalData(state, clientMessage.Body); clientMessage = handshake.ReceiveMessage(); } else { state.server.ProcessClientSupplementalData(null); } if (state.certificateRequest == null) { state.keyExchange.SkipClientCredentials(); } else { if (clientMessage.Type == HandshakeType.certificate) { ProcessClientCertificate(state, clientMessage.Body); clientMessage = handshake.ReceiveMessage(); } else { if (TlsUtilities.IsTlsV12(state.serverContext)) { /* * RFC 5246 If no suitable certificate is available, the client MUST send a * certificate message containing no certificates. * * NOTE: In previous RFCs, this was SHOULD instead of MUST. */ throw new TlsFatalAlert(AlertDescription.unexpected_message); } NotifyClientCertificate(state, Certificate.EmptyChain); } } if (clientMessage.Type == HandshakeType.client_key_exchange) { ProcessClientKeyExchange(state, clientMessage.Body); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish(); securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.serverContext, prepareFinishHash, null); TlsProtocol.EstablishMasterSecret(state.serverContext, state.keyExchange); recordLayer.InitPendingEpoch(state.server.GetCipher()); /* * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing * capability (i.e., all certificates except those containing fixed Diffie-Hellman * parameters). */ if (ExpectCertificateVerifyMessage(state)) { byte[] certificateVerifyBody = handshake.ReceiveMessageBody(HandshakeType.certificate_verify); ProcessCertificateVerify(state, certificateVerifyBody, prepareFinishHash); } // NOTE: Calculated exclusive of the actual Finished message from the client byte[] expectedClientVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.client_finished, TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null)); ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedClientVerifyData); if (state.expectSessionTicket) { NewSessionTicket newSessionTicket = state.server.GetNewSessionTicket(); byte[] newSessionTicketBody = GenerateNewSessionTicket(state, newSessionTicket); handshake.SendMessage(HandshakeType.session_ticket, newSessionTicketBody); } // NOTE: Calculated exclusive of the Finished message itself byte[] serverVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.server_finished, TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null)); handshake.SendMessage(HandshakeType.finished, serverVerifyData); handshake.Finish(); state.server.NotifyHandshakeComplete(); return(new DtlsTransport(recordLayer)); }
public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion) { return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(clientVersion.GetEquivalentTLSVersion()); }
public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) { int blockSize = encryptCipher.GetBlockSize(); int macSize = mWriteMac.Size; ProtocolVersion version = context.ServerVersion; int enc_input_length = len; if (!encryptThenMac) { enc_input_length += macSize; } int padding_length = blockSize - 1 - (enc_input_length % blockSize); /* * Don't use variable-length padding with truncated MACs. * * See "Tag Size Does Matter: Attacks and Proofs for the TLS Record Protocol", Paterson, * Ristenpart, Shrimpton. */ if (encryptThenMac || !context.SecurityParameters.truncatedHMac) { // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though) if (!version.IsDtls && !version.IsSsl) { // Add a random number of extra blocks worth of padding int maxExtraPadBlocks = (255 - padding_length) / blockSize; int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks); padding_length += actualExtraPadBlocks * blockSize; } } int totalSize = len + macSize + padding_length + 1; if (useExplicitIV) { totalSize += blockSize; } byte[] outBuf = new byte[totalSize]; int outOff = 0; if (useExplicitIV) { byte[] explicitIV = new byte[blockSize]; context.NonceRandomGenerator.NextBytes(explicitIV); encryptCipher.Init(true, new ParametersWithIV(null, explicitIV)); Array.Copy(explicitIV, 0, outBuf, outOff, blockSize); outOff += blockSize; } int blocks_start = outOff; Array.Copy(plaintext, offset, outBuf, outOff, len); outOff += len; if (!encryptThenMac) { byte[] mac = mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len); Array.Copy(mac, 0, outBuf, outOff, mac.Length); outOff += mac.Length; } for (int i = 0; i <= padding_length; i++) { outBuf[outOff++] = (byte)padding_length; } for (int i = blocks_start; i < outOff; i += blockSize) { encryptCipher.ProcessBlock(outBuf, i, outBuf, i); } if (encryptThenMac) { byte[] mac = mWriteMac.CalculateMac(seqNo, type, outBuf, 0, outOff); Array.Copy(mac, 0, outBuf, outOff, mac.Length); outOff += mac.Length; } // assert outBuf.length == outOff; return(outBuf); }
public static byte[] SafeDecryptPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPrivateKey, byte[] encryptedPreMasterSecret) { /* * RFC 5246 7.4.7.1. */ ProtocolVersion clientVersion = context.ClientVersion; // TODO Provide as configuration option? bool versionNumberCheckDisabled = false; /* * Generate 48 random bytes we can use as a Pre-Master-Secret, if the * PKCS1 padding check should fail. */ byte[] fallback = new byte[48]; context.SecureRandom.NextBytes(fallback); byte[] M = Arrays.Clone(fallback); try { Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback); encoding.Init(false, new ParametersWithRandom(rsaServerPrivateKey, context.SecureRandom)); M = encoding.ProcessBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length); } catch (Exception) { /* * This should never happen since the decryption should never throw an exception * and return a random value instead. * * In any case, a TLS server MUST NOT generate an alert if processing an * RSA-encrypted premaster secret message fails, or the version number is not as * expected. Instead, it MUST continue the handshake with a randomly generated * premaster secret. */ } /* * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST * check the version number [..]. */ if (versionNumberCheckDisabled && clientVersion.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv10)) { /* * If the version number is TLS 1.0 or earlier, server * implementations SHOULD check the version number, but MAY have a * configuration option to disable the check. * * So there is nothing to do here. */ } else { /* * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the * clientVersion received during the handshake. If they don't match, we replace the * decrypted Pre-Master-Secret with a random one. */ int correct = (clientVersion.MajorVersion ^ (M[0] & 0xff)) | (clientVersion.MinorVersion ^ (M[1] & 0xff)); correct |= correct >> 1; correct |= correct >> 2; correct |= correct >> 4; int mask = ~((correct & 1) - 1); /* * mask will be all bits set to 0xff if the version number differed. */ for (int i = 0; i < 48; i++) { M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask)); } } return(M); }
public static bool IsTlsV12(ProtocolVersion version) { return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(version.GetEquivalentTLSVersion()); }
protected virtual byte[] GenerateServerHello(ServerHandshakeState state) { SecurityParameters securityParameters = state.serverContext.SecurityParameters; MemoryStream buf = new MemoryStream(); { ProtocolVersion server_version = state.server.GetServerVersion(); if (!server_version.IsEqualOrEarlierVersionOf(state.serverContext.ClientVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } // TODO Read RFCs for guidance on the expected record layer version number // recordStream.setReadVersion(server_version); // recordStream.setWriteVersion(server_version); // recordStream.setRestrictReadVersion(true); state.serverContext.SetServerVersion(server_version); TlsUtilities.WriteVersion(state.serverContext.ServerVersion, buf); } buf.Write(securityParameters.ServerRandom, 0, securityParameters.ServerRandom.Length); /* * The server may return an empty session_id to indicate that the session will not be cached * and therefore cannot be resumed. */ TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); int selectedCipherSuite = state.server.GetSelectedCipherSuite(); if (!Arrays.Contains(state.offeredCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, state.serverContext.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } ValidateSelectedCipherSuite(selectedCipherSuite, AlertDescription.internal_error); securityParameters.cipherSuite = selectedCipherSuite; byte selectedCompressionMethod = state.server.GetSelectedCompressionMethod(); if (!Arrays.Contains(state.offeredCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } securityParameters.compressionAlgorithm = selectedCompressionMethod; TlsUtilities.WriteUint16(selectedCipherSuite, buf); TlsUtilities.WriteUint8(selectedCompressionMethod, buf); state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.server.GetServerExtensions()); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ if (state.secure_renegotiation) { byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); if (noRenegExt) { /* * Note that sending a "renegotiation_info" extension in response to a ClientHello * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed * because the client is signaling its willingness to receive the extension via the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ /* * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty * "renegotiation_info" extension in the ServerHello message. */ state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes); } } if (securityParameters.IsExtendedMasterSecret) { TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions); } /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and send a server hello containing no * extensions. */ if (state.serverExtensions.Count > 0) { securityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(state.serverExtensions); securityParameters.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.resumedSession, state.clientExtensions, state.serverExtensions, AlertDescription.internal_error); securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(state.serverExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in * a session resumption handshake. */ state.allowCertificateStatus = !state.resumedSession && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.status_request, AlertDescription.internal_error); state.expectSessionTicket = !state.resumedSession && TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.session_ticket, AlertDescription.internal_error); ProcessCertificateFormats(state, state.clientExtensions, state.serverExtensions); TlsProtocol.WriteExtensions(buf, state.serverExtensions); } securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.serverContext, securityParameters.CipherSuite); /* * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify verify_data_length * has a verify_data_length equal to 12. This includes all existing cipher suites. */ securityParameters.verifyDataLength = 12; return(buf.ToArray()); }
internal virtual DtlsTransport ServerHandshake(ServerHandshakeState state, DtlsRecordLayer recordLayer) { SecurityParameters securityParameters = state.serverContext.SecurityParameters; DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.serverContext, recordLayer, state.server.GetHandshakeTimeoutMillis()); DtlsReliableHandshake.Message clientMessage = handshake.ReceiveMessage(); // NOTE: DTLSRecordLayer requires any DTLS version, we don't otherwise constrain this //ProtocolVersion recordLayerVersion = recordLayer.ReadVersion; if (clientMessage.Type == HandshakeType.client_hello) { ProcessClientHello(state, clientMessage.Body); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } { byte[] serverHelloBody = GenerateServerHello(state); ApplyMaxFragmentLengthExtension(recordLayer, securityParameters.maxFragmentLength); ProtocolVersion recordLayerVersion = state.serverContext.ServerVersion; recordLayer.ReadVersion = recordLayerVersion; recordLayer.SetWriteVersion(recordLayerVersion); handshake.SendMessage(HandshakeType.server_hello, serverHelloBody); } handshake.NotifyHelloComplete(); IList serverSupplementalData = state.server.GetServerSupplementalData(); if (serverSupplementalData != null) { byte[] supplementalDataBody = GenerateSupplementalData(serverSupplementalData); handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody); } state.keyExchange = state.server.GetKeyExchange(); state.keyExchange.Init(state.serverContext); state.serverCredentials = state.server.GetCredentials(); AbstractCertificate serverCertificate = null; if (state.serverCredentials == null) { state.keyExchange.SkipServerCredentials(); } else { state.keyExchange.ProcessServerCredentials(state.serverCredentials); serverCertificate = state.serverCredentials.Certificate; byte[] certificateBody = GenerateCertificate(serverCertificate); handshake.SendMessage(HandshakeType.certificate, certificateBody); } // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus if (serverCertificate == null || serverCertificate.IsEmpty) { state.allowCertificateStatus = false; } if (state.allowCertificateStatus) { CertificateStatus certificateStatus = state.server.GetCertificateStatus(); if (certificateStatus != null) { byte[] certificateStatusBody = GenerateCertificateStatus(state, certificateStatus); handshake.SendMessage(HandshakeType.certificate_status, certificateStatusBody); } } byte[] serverKeyExchange = state.keyExchange.GenerateServerKeyExchange(); if (serverKeyExchange != null) { handshake.SendMessage(HandshakeType.server_key_exchange, serverKeyExchange); } if (state.serverCredentials != null) { state.certificateRequest = state.server.GetCertificateRequest(); if (state.certificateRequest != null) { if (TlsUtilities.IsTlsV12(state.serverContext) != (state.certificateRequest.SupportedSignatureAlgorithms != null)) { throw new TlsFatalAlert(AlertDescription.internal_error); } state.keyExchange.ValidateCertificateRequest(state.certificateRequest); byte[] certificateRequestBody = GenerateCertificateRequest(state, state.certificateRequest); handshake.SendMessage(HandshakeType.certificate_request, certificateRequestBody); TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, state.certificateRequest.SupportedSignatureAlgorithms); } } handshake.SendMessage(HandshakeType.server_hello_done, TlsUtilities.EmptyBytes); handshake.HandshakeHash.SealHashAlgorithms(); clientMessage = handshake.ReceiveMessage(); if (clientMessage.Type == HandshakeType.supplemental_data) { ProcessClientSupplementalData(state, clientMessage.Body); clientMessage = handshake.ReceiveMessage(); } else { state.server.ProcessClientSupplementalData(null); } if (state.certificateRequest == null) { state.keyExchange.SkipClientCredentials(); } else { if (clientMessage.Type == HandshakeType.certificate) { ProcessClientCertificate(state, clientMessage.Body); clientMessage = handshake.ReceiveMessage(); } else { if (TlsUtilities.IsTlsV12(state.serverContext)) { /* * RFC 5246 If no suitable certificate is available, the client MUST send a * certificate message containing no certificates. * * NOTE: In previous RFCs, this was SHOULD instead of MUST. */ throw new TlsFatalAlert(AlertDescription.unexpected_message); } NotifyClientCertificate(state, Certificate.EmptyChain); } } if (clientMessage.Type == HandshakeType.client_key_exchange) { ProcessClientKeyExchange(state, clientMessage.Body); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish(); securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.serverContext, prepareFinishHash, null); TlsProtocol.EstablishMasterSecret(state.serverContext, state.keyExchange); recordLayer.InitPendingEpoch(state.server.GetCipher()); /* * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing * capability (i.e., all certificates except those containing fixed Diffie-Hellman * parameters). */ if (ExpectCertificateVerifyMessage(state)) { byte[] certificateVerifyBody = handshake.ReceiveMessageBody(HandshakeType.certificate_verify); ProcessCertificateVerify(state, certificateVerifyBody, prepareFinishHash); } // NOTE: Calculated exclusive of the actual Finished message from the client byte[] expectedClientVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.client_finished, TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null)); ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedClientVerifyData); if (state.expectSessionTicket) { NewSessionTicket newSessionTicket = state.server.GetNewSessionTicket(); byte[] newSessionTicketBody = GenerateNewSessionTicket(state, newSessionTicket); handshake.SendMessage(HandshakeType.session_ticket, newSessionTicketBody); } // NOTE: Calculated exclusive of the Finished message itself byte[] serverVerifyData = TlsUtilities.CalculateVerifyData(state.serverContext, ExporterLabel.server_finished, TlsProtocol.GetCurrentPrfHash(state.serverContext, handshake.HandshakeHash, null)); handshake.SendMessage(HandshakeType.finished, serverVerifyData); handshake.Finish(); //{ // state.sessionParameters = new SessionParameters.Builder() // .SetCipherSuite(securityParameters.CipherSuite) // .SetCompressionAlgorithm(securityParameters.CompressionAlgorithm) // .SetExtendedMasterSecret(securityParameters.IsExtendedMasterSecret) // .SetMasterSecret(securityParameters.MasterSecret) // .SetPeerCertificate(state.clientCertificate) // .SetPskIdentity(securityParameters.PskIdentity) // .SetSrpIdentity(securityParameters.SrpIdentity) // // TODO Consider filtering extensions that aren't relevant to resumed sessions // .SetServerExtensions(state.serverExtensions) // .Build(); // state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters); // state.serverContext.SetResumableSession(state.tlsSession); //} state.server.NotifyHandshakeComplete(); return(new DtlsTransport(recordLayer)); }
private int ProcessRecord(int received, byte[] record, byte[] buf, int off) { // NOTE: received < 0 (timeout) is covered by this first case if (received < RECORD_HEADER_LENGTH) { return(-1); } int length = TlsUtilities.ReadUint16(record, 11); if (received != (length + RECORD_HEADER_LENGTH)) { return(-1); } byte type = TlsUtilities.ReadUint8(record, 0); switch (type) { case ContentType.alert: case ContentType.application_data: case ContentType.change_cipher_spec: case ContentType.handshake: case ContentType.heartbeat: break; default: return(-1); } int epoch = TlsUtilities.ReadUint16(record, 3); DtlsEpoch recordEpoch = null; if (epoch == mReadEpoch.Epoch) { recordEpoch = mReadEpoch; } else if (type == ContentType.handshake && mRetransmitEpoch != null && epoch == mRetransmitEpoch.Epoch) { recordEpoch = mRetransmitEpoch; } if (recordEpoch == null) { return(-1); } long seq = TlsUtilities.ReadUint48(record, 5); if (recordEpoch.ReplayWindow.ShouldDiscard(seq)) { return(-1); } ProtocolVersion version = TlsUtilities.ReadVersion(record, 1); if (!version.IsDtls) { return(-1); } if (mReadVersion != null && !mReadVersion.Equals(version)) { return(-1); } byte[] plaintext = recordEpoch.Cipher.DecodeCiphertext( GetMacSequenceNumber(recordEpoch.Epoch, seq), type, record, RECORD_HEADER_LENGTH, received - RECORD_HEADER_LENGTH); recordEpoch.ReplayWindow.ReportAuthenticated(seq); if (plaintext.Length > this.mPlaintextLimit) { return(-1); } if (mReadVersion == null) { mReadVersion = version; } switch (type) { case ContentType.alert: { if (plaintext.Length == 2) { byte alertLevel = plaintext[0]; byte alertDescription = plaintext[1]; mPeer.NotifyAlertReceived(alertLevel, alertDescription); if (alertLevel == AlertLevel.fatal) { Failed(); throw new TlsFatalAlert(alertDescription); } // TODO Can close_notify be a fatal alert? if (alertDescription == AlertDescription.close_notify) { CloseTransport(); } } return(-1); } case ContentType.application_data: { if (mInHandshake) { // TODO Consider buffering application data for new epoch that arrives // out-of-order with the Finished message return(-1); } break; } case ContentType.change_cipher_spec: { // Implicitly receive change_cipher_spec and change to pending cipher state for (int i = 0; i < plaintext.Length; ++i) { byte message = TlsUtilities.ReadUint8(plaintext, i); if (message != ChangeCipherSpec.change_cipher_spec) { continue; } if (mPendingEpoch != null) { mReadEpoch = mPendingEpoch; } } return(-1); } case ContentType.handshake: { if (!mInHandshake) { if (mRetransmit != null) { mRetransmit.ReceivedHandshakeRecord(epoch, plaintext, 0, plaintext.Length); } // TODO Consider support for HelloRequest return(-1); } break; } case ContentType.heartbeat: { // TODO[RFC 6520] return(-1); } } /* * NOTE: If we receive any non-handshake data in the new epoch implies the peer has * received our final flight. */ if (!mInHandshake && mRetransmit != null) { this.mRetransmit = null; this.mRetransmitEpoch = null; this.mRetransmitTimeout = null; } Array.Copy(plaintext, 0, buf, off, plaintext.Length); return(plaintext.Length); }
protected virtual void SendServerHelloMessage() { HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello); { ProtocolVersion server_version = mTlsServer.GetServerVersion(); if (!server_version.IsEqualOrEarlierVersionOf(Context.ClientVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } mRecordStream.ReadVersion = server_version; mRecordStream.SetWriteVersion(server_version); mRecordStream.SetRestrictReadVersion(true); ContextAdmin.SetServerVersion(server_version); TlsUtilities.WriteVersion(server_version, message); } message.Write(this.mSecurityParameters.serverRandom); /* * The server may return an empty session_id to indicate that the session will not be cached * and therefore cannot be resumed. */ TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, message); int selectedCipherSuite = mTlsServer.GetSelectedCipherSuite(); if (!Arrays.Contains(mOfferedCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } mSecurityParameters.cipherSuite = selectedCipherSuite; byte selectedCompressionMethod = mTlsServer.GetSelectedCompressionMethod(); if (!Arrays.Contains(mOfferedCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; TlsUtilities.WriteUint16(selectedCipherSuite, message); TlsUtilities.WriteUint8(selectedCompressionMethod, message); this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(mTlsServer.GetServerExtensions()); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ if (this.mSecureRenegotiation) { byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); if (noRenegExt) { /* * Note that Sending a "renegotiation_info" extension in response to a ClientHello * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, * Section 7.4.1.4, on the server Sending unsolicited extensions and is only allowed * because the client is signaling its willingness to receive the extension via the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ /* * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty * "renegotiation_info" extension in the ServerHello message. */ this.mServerExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); } } if (TlsUtilities.IsSsl(mTlsServerContext)) { mSecurityParameters.extendedMasterSecret = false; } else if (mSecurityParameters.IsExtendedMasterSecret) { TlsExtensionsUtilities.AddExtendedMasterSecretExtension(mServerExtensions); } /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and Send a server hello containing no * extensions. */ if (this.mServerExtensions.Count > 0) { this.mSecurityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(mClientExtensions, mServerExtensions, AlertDescription.internal_error); this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in * a session resumption handshake. */ this.mAllowCertificateStatus = !mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.status_request, AlertDescription.internal_error); this.mExpectSessionTicket = !mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, ExtensionType.session_ticket, AlertDescription.internal_error); WriteExtensions(message, this.mServerExtensions); } mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); /* * RFC 5246 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has * a verify_data_length equal to 12. This includes all existing cipher suites. */ mSecurityParameters.verifyDataLength = 12; ApplyMaxFragmentLengthExtension(); message.WriteToRecordStream(this); }
protected virtual void ReceiveClientHelloMessage(MemoryStream buf) { ProtocolVersion client_version = TlsUtilities.ReadVersion(buf); mRecordStream.SetWriteVersion(client_version); if (client_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } byte[] client_random = TlsUtilities.ReadFully(32, buf); /* * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to * use the Session ID in the ClientHello for stateful session resumption. */ byte[] sessionID = TlsUtilities.ReadOpaque8(buf); if (sessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } /* * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session * resumption request), this vector MUST include at least the cipher_suite from that * session. */ int cipher_suites_length = TlsUtilities.ReadUint16(buf); if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) { throw new TlsFatalAlert(AlertDescription.decode_error); } this.mOfferedCipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, buf); /* * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session * resumption request), it MUST include the compression_method from that session. */ int compression_methods_length = TlsUtilities.ReadUint8(buf); if (compression_methods_length < 1) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mOfferedCompressionMethods = TlsUtilities.ReadUint8Array(compression_methods_length, buf); /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and Send a server hello containing no * extensions. */ this.mClientExtensions = ReadExtensions(buf); /* * TODO[resumption] Check RFC 7627 5.4. for required behaviour */ /* * RFC 7627 4. Clients and servers SHOULD NOT accept handshakes that do not use the extended * master secret [..]. (and see 5.2, 5.3) */ this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions); if (!mSecurityParameters.IsExtendedMasterSecret && mTlsServer.RequiresExtendedMasterSecret()) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } ContextAdmin.SetClientVersion(client_version); mTlsServer.NotifyClientVersion(client_version); mTlsServer.NotifyFallback(Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)); mSecurityParameters.clientRandom = client_random; mTlsServer.NotifyOfferedCipherSuites(mOfferedCipherSuites); mTlsServer.NotifyOfferedCompressionMethods(mOfferedCompressionMethods); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ { /* * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ /* * When a ClientHello is received, the server MUST check if it includes the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag * to TRUE. */ if (Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { this.mSecureRenegotiation = true; } /* * The server MUST check if the "renegotiation_info" extension is included in the * ClientHello. */ byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info); if (renegExtData != null) { /* * If the extension is present, set secure_renegotiation flag to TRUE. The * server MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake. */ this.mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } mTlsServer.NotifySecureRenegotiation(this.mSecureRenegotiation); if (mClientExtensions != null) { // NOTE: Validates the padding extension data, if present TlsExtensionsUtilities.GetPaddingExtension(mClientExtensions); mTlsServer.ProcessClientExtensions(mClientExtensions); } }
public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { byte[] record = null; for (;;) { int receiveLimit = System.Math.Min(len, GetReceiveLimit()) + RECORD_HEADER_LENGTH; if (record == null || record.Length < receiveLimit) { record = new byte[receiveLimit]; } try { if (mRetransmit != null && DateTimeUtilities.CurrentUnixMs() > mRetransmitExpiry) { mRetransmit = null; mRetransmitEpoch = null; } int received = ReceiveRecord(record, 0, receiveLimit, waitMillis); if (received < 0) { return received; } if (received < RECORD_HEADER_LENGTH) { continue; } int length = TlsUtilities.ReadUint16(record, 11); if (received != (length + RECORD_HEADER_LENGTH)) { continue; } byte type = TlsUtilities.ReadUint8(record, 0); // TODO Support user-specified custom protocols? switch (type) { case ContentType.alert: case ContentType.application_data: case ContentType.change_cipher_spec: case ContentType.handshake: case ContentType.heartbeat: break; default: // TODO Exception? continue; } int epoch = TlsUtilities.ReadUint16(record, 3); DtlsEpoch recordEpoch = null; if (epoch == mReadEpoch.Epoch) { recordEpoch = mReadEpoch; } else if (type == ContentType.handshake && mRetransmitEpoch != null && epoch == mRetransmitEpoch.Epoch) { recordEpoch = mRetransmitEpoch; } if (recordEpoch == null) { continue; } long seq = TlsUtilities.ReadUint48(record, 5); if (recordEpoch.ReplayWindow.ShouldDiscard(seq)) { continue; } ProtocolVersion version = TlsUtilities.ReadVersion(record, 1); if (!version.IsDtls) { continue; } if (mReadVersion != null && !mReadVersion.Equals(version)) { continue; } byte[] plaintext = recordEpoch.Cipher.DecodeCiphertext( GetMacSequenceNumber(recordEpoch.Epoch, seq), type, record, RECORD_HEADER_LENGTH, received - RECORD_HEADER_LENGTH); recordEpoch.ReplayWindow.ReportAuthenticated(seq); if (plaintext.Length > this.mPlaintextLimit) { continue; } if (mReadVersion == null) { mReadVersion = version; } switch (type) { case ContentType.alert: { if (plaintext.Length == 2) { byte alertLevel = plaintext[0]; byte alertDescription = plaintext[1]; mPeer.NotifyAlertReceived(alertLevel, alertDescription); if (alertLevel == AlertLevel.fatal) { Failed(); throw new TlsFatalAlert(alertDescription); } // TODO Can close_notify be a fatal alert? if (alertDescription == AlertDescription.close_notify) { CloseTransport(); } } continue; } case ContentType.application_data: { if (mInHandshake) { // TODO Consider buffering application data for new epoch that arrives // out-of-order with the Finished message continue; } break; } case ContentType.change_cipher_spec: { // Implicitly receive change_cipher_spec and change to pending cipher state for (int i = 0; i < plaintext.Length; ++i) { byte message = TlsUtilities.ReadUint8(plaintext, i); if (message != ChangeCipherSpec.change_cipher_spec) { continue; } if (mPendingEpoch != null) { mReadEpoch = mPendingEpoch; } } continue; } case ContentType.handshake: { if (!mInHandshake) { if (mRetransmit != null) { mRetransmit.ReceivedHandshakeRecord(epoch, plaintext, 0, plaintext.Length); } // TODO Consider support for HelloRequest continue; } break; } case ContentType.heartbeat: { // TODO[RFC 6520] continue; } } /* * NOTE: If we receive any non-handshake data in the new epoch implies the peer has * received our final flight. */ if (!mInHandshake && mRetransmit != null) { this.mRetransmit = null; this.mRetransmitEpoch = null; } Array.Copy(plaintext, 0, buf, off, plaintext.Length); return plaintext.Length; } catch (IOException e) { // NOTE: Assume this is a timeout for the moment throw e; } } }
public virtual void Init(TlsContext context) { this.mContext = context; ProtocolVersion clientVersion = context.ClientVersion; if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion)) { /* * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension, * the server MUST do the following: * * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK, * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}. * * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if * the client had sent the value {sha1,dsa}. * * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA), * behave as if the client had sent value {sha1,ecdsa}. */ if (this.mSupportedSignatureAlgorithms == null) { switch (mKeyExchange) { case KeyExchangeAlgorithm.DH_DSS: case KeyExchangeAlgorithm.DHE_DSS: case KeyExchangeAlgorithm.SRP_DSS: { this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultDssSignatureAlgorithms(); break; } case KeyExchangeAlgorithm.ECDH_ECDSA: case KeyExchangeAlgorithm.ECDHE_ECDSA: { this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultECDsaSignatureAlgorithms(); break; } case KeyExchangeAlgorithm.DH_RSA: case KeyExchangeAlgorithm.DHE_RSA: case KeyExchangeAlgorithm.ECDH_RSA: case KeyExchangeAlgorithm.ECDHE_RSA: case KeyExchangeAlgorithm.RSA: case KeyExchangeAlgorithm.RSA_PSK: case KeyExchangeAlgorithm.SRP_RSA: { this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultRsaSignatureAlgorithms(); break; } case KeyExchangeAlgorithm.DHE_PSK: case KeyExchangeAlgorithm.ECDHE_PSK: case KeyExchangeAlgorithm.PSK: case KeyExchangeAlgorithm.SRP: break; default: throw new InvalidOperationException("unsupported key exchange algorithm"); } } } else if (this.mSupportedSignatureAlgorithms != null) { throw new InvalidOperationException("supported_signature_algorithms not allowed for " + clientVersion); } }
internal virtual void SetWriteVersion(ProtocolVersion writeVersion) { this.mWriteVersion = writeVersion; }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { ProtocolVersion writeVersion = TlsUtilities.ReadVersion(buf); if (writeVersion.IsDtls) { throw new TlsFatalAlert(0x2f); } if (!writeVersion.Equals(base.mRecordStream.ReadVersion)) { throw new TlsFatalAlert(0x2f); } ProtocolVersion clientVersion = this.Context.ClientVersion; if (!writeVersion.IsEqualOrEarlierVersionOf(clientVersion)) { throw new TlsFatalAlert(0x2f); } base.mRecordStream.SetWriteVersion(writeVersion); this.ContextAdmin.SetServerVersion(writeVersion); this.mTlsClient.NotifyServerVersion(writeVersion); base.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(0x20, buf); this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); if (this.mSelectedSessionID.Length > 0x20) { throw new TlsFatalAlert(0x2f); } this.mTlsClient.NotifySessionID(this.mSelectedSessionID); base.mResumedSession = ((this.mSelectedSessionID.Length > 0) && (base.mTlsSession != null)) && Arrays.AreEqual(this.mSelectedSessionID, base.mTlsSession.SessionID); int n = TlsUtilities.ReadUint16(buf); if ((!Arrays.Contains(base.mOfferedCipherSuites, n) || (n == 0)) || (CipherSuite.IsScsv(n) || !TlsUtilities.IsValidCipherSuiteForVersion(n, this.Context.ServerVersion))) { throw new TlsFatalAlert(0x2f); } this.mTlsClient.NotifySelectedCipherSuite(n); byte num2 = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(base.mOfferedCompressionMethods, num2)) { throw new TlsFatalAlert(0x2f); } this.mTlsClient.NotifySelectedCompressionMethod(num2); base.mServerExtensions = TlsProtocol.ReadExtensions(buf); if (base.mServerExtensions != null) { IEnumerator enumerator = base.mServerExtensions.Keys.GetEnumerator(); try { while (enumerator.MoveNext()) { int current = (int)enumerator.Current; if (current != 0xff01) { if (TlsUtilities.GetExtensionData(base.mClientExtensions, current) == null) { throw new TlsFatalAlert(110); } if (base.mResumedSession) { } } } } finally { if (enumerator is IDisposable disposable) { IDisposable disposable; disposable.Dispose(); } } } byte[] extensionData = TlsUtilities.GetExtensionData(base.mServerExtensions, 0xff01); if (extensionData != null) { base.mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(extensionData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(40); } } this.mTlsClient.NotifySecureRenegotiation(base.mSecureRenegotiation); IDictionary mClientExtensions = base.mClientExtensions; IDictionary mServerExtensions = base.mServerExtensions; if (base.mResumedSession) { if ((n != base.mSessionParameters.CipherSuite) || (num2 != base.mSessionParameters.CompressionAlgorithm)) { throw new TlsFatalAlert(0x2f); } mClientExtensions = null; mServerExtensions = base.mSessionParameters.ReadServerExtensions(); } base.mSecurityParameters.cipherSuite = n; base.mSecurityParameters.compressionAlgorithm = num2; if (mServerExtensions != null) { bool flag = TlsExtensionsUtilities.HasEncryptThenMacExtension(mServerExtensions); if (flag && !TlsUtilities.IsBlockCipherSuite(n)) { throw new TlsFatalAlert(0x2f); } base.mSecurityParameters.encryptThenMac = flag; base.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions); base.mSecurityParameters.maxFragmentLength = this.ProcessMaxFragmentLengthExtension(mClientExtensions, mServerExtensions, 0x2f); base.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(mServerExtensions); base.mAllowCertificateStatus = !base.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, 5, 0x2f); base.mExpectSessionTicket = !base.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(mServerExtensions, 0x23, 0x2f); } if (mClientExtensions != null) { this.mTlsClient.ProcessServerExtensions(mServerExtensions); } base.mSecurityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(this.Context, base.mSecurityParameters.CipherSuite); base.mSecurityParameters.verifyDataLength = 12; }
public static void WriteVersion(ProtocolVersion version, Stream output) { output.WriteByte((byte)version.MajorVersion); output.WriteByte((byte)version.MinorVersion); }
protected virtual byte[] GenerateServerHello(ServerHandshakeState state) { SecurityParameters securityParameters = state.serverContext.SecurityParameters; MemoryStream buf = new MemoryStream(); ProtocolVersion server_version = state.server.GetServerVersion(); if (!server_version.IsEqualOrEarlierVersionOf(state.serverContext.ClientVersion)) { throw new TlsFatalAlert(AlertDescription.internal_error); } // TODO Read RFCs for guidance on the expected record layer version number // recordStream.setReadVersion(server_version); // recordStream.setWriteVersion(server_version); // recordStream.setRestrictReadVersion(true); state.serverContext.SetServerVersion(server_version); TlsUtilities.WriteVersion(state.serverContext.ServerVersion, buf); buf.Write(securityParameters.ServerRandom, 0, securityParameters.ServerRandom.Length); /* * The server may return an empty session_id to indicate that the session will not be cached * and therefore cannot be resumed. */ TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); state.selectedCipherSuite = state.server.GetSelectedCipherSuite(); if (!Arrays.Contains(state.offeredCipherSuites, state.selectedCipherSuite) || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(state.selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(state.selectedCipherSuite, server_version)) { throw new TlsFatalAlert(AlertDescription.internal_error); } ValidateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error); state.selectedCompressionMethod = state.server.GetSelectedCompressionMethod(); if (!Arrays.Contains(state.offeredCompressionMethods, (byte)state.selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } TlsUtilities.WriteUint16(state.selectedCipherSuite, buf); TlsUtilities.WriteUint8((byte)state.selectedCompressionMethod, buf); state.serverExtensions = state.server.GetServerExtensions(); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ if (state.secure_renegotiation) { byte[] renegExtData = TlsUtilities.GetExtensionData(state.serverExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); if (noRenegExt) { /* * Note that sending a "renegotiation_info" extension in response to a ClientHello * containing only the SCSV is an explicit exception to the prohibition in RFC 5246, * Section 7.4.1.4, on the server sending unsolicited extensions and is only allowed * because the client is signaling its willingness to receive the extension via the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ /* * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty * "renegotiation_info" extension in the ServerHello message. */ state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions); state.serverExtensions[ExtensionType.renegotiation_info] = TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes); } } if (securityParameters.extendedMasterSecret) { state.serverExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(state.serverExtensions); TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.serverExtensions); } if (state.serverExtensions != null) { securityParameters.encryptThenMac = TlsExtensionsUtilities.HasEncryptThenMacExtension(state.serverExtensions); state.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.clientExtensions, state.serverExtensions, AlertDescription.internal_error); securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(state.serverExtensions); state.allowCertificateStatus = TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.status_request, AlertDescription.internal_error); state.expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(state.serverExtensions, ExtensionType.session_ticket, AlertDescription.internal_error); TlsProtocol.WriteExtensions(buf, state.serverExtensions); } return(buf.ToArray()); }
internal virtual void SetClientVersion(ProtocolVersion clientVersion) { this.mClientVersion = clientVersion; }
public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) { int blockSize = this.encryptCipher.GetBlockSize(); int size = this.mWriteMac.Size; ProtocolVersion serverVersion = this.context.ServerVersion; int num3 = len; if (!this.encryptThenMac) { num3 += size; } int num4 = (blockSize - 1) - (num3 % blockSize); if (!serverVersion.IsDtls && !serverVersion.IsSsl) { int max = (0xff - num4) / blockSize; int num6 = this.ChooseExtraPadBlocks(this.context.SecureRandom, max); num4 += num6 * blockSize; } int num7 = ((len + size) + num4) + 1; if (this.useExplicitIV) { num7 += blockSize; } byte[] destinationArray = new byte[num7]; int destinationIndex = 0; if (this.useExplicitIV) { byte[] bytes = new byte[blockSize]; this.context.NonceRandomGenerator.NextBytes(bytes); this.encryptCipher.Init(true, new ParametersWithIV(null, bytes)); Array.Copy(bytes, 0, destinationArray, destinationIndex, blockSize); destinationIndex += blockSize; } int num9 = destinationIndex; Array.Copy(plaintext, offset, destinationArray, destinationIndex, len); destinationIndex += len; if (!this.encryptThenMac) { byte[] sourceArray = this.mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len); Array.Copy(sourceArray, 0, destinationArray, destinationIndex, sourceArray.Length); destinationIndex += sourceArray.Length; } for (int i = 0; i <= num4; i++) { destinationArray[destinationIndex++] = (byte)num4; } for (int j = num9; j < destinationIndex; j += blockSize) { this.encryptCipher.ProcessBlock(destinationArray, j, destinationArray, j); } if (this.encryptThenMac) { byte[] sourceArray = this.mWriteMac.CalculateMac(seqNo, type, destinationArray, 0, destinationIndex); Array.Copy(sourceArray, 0, destinationArray, destinationIndex, sourceArray.Length); destinationIndex += sourceArray.Length; } return(destinationArray); }
protected virtual void SendClientHelloMessage() { this.mRecordStream.SetWriteVersion(this.mTlsClient.ClientHelloRecordLayerVersion); ProtocolVersion client_version = this.mTlsClient.ClientVersion; if (client_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.internal_error); } ContextAdmin.SetClientVersion(client_version); /* * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a * Session ID in the TLS ClientHello. */ byte[] session_id = TlsUtilities.EmptyBytes; if (this.mTlsSession != null) { session_id = this.mTlsSession.SessionID; if (session_id == null || session_id.Length > 32) { session_id = TlsUtilities.EmptyBytes; } } bool fallback = this.mTlsClient.IsFallback; this.mOfferedCipherSuites = this.mTlsClient.GetCipherSuites(); this.mOfferedCompressionMethods = this.mTlsClient.GetCompressionMethods(); if (session_id.Length > 0 && this.mSessionParameters != null) { if (!Arrays.Contains(this.mOfferedCipherSuites, mSessionParameters.CipherSuite) || !Arrays.Contains(this.mOfferedCompressionMethods, mSessionParameters.CompressionAlgorithm)) { session_id = TlsUtilities.EmptyBytes; } } this.mClientExtensions = this.mTlsClient.GetClientExtensions(); HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello); TlsUtilities.WriteVersion(client_version, message); message.Write(this.mSecurityParameters.ClientRandom); TlsUtilities.WriteOpaque8(session_id, message); // Cipher Suites (and SCSV) { /* * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ byte[] renegExtData = TlsUtilities.GetExtensionData(mClientExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); bool noRenegScsv = !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); if (noRenegExt && noRenegScsv) { // TODO Consider whether to default to a client extension instead // this.mClientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mClientExtensions); // this.mClientExtensions[ExtensionType.renegotiation_info] = CreateRenegotiationInfo(TlsUtilities.EmptyBytes); this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); } /* * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version * containing a lower value than the latest (highest-valued) version supported by the * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in * ClientHello.cipher_suites. */ if (fallback && !Arrays.Contains(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)) { this.mOfferedCipherSuites = Arrays.Append(mOfferedCipherSuites, CipherSuite.TLS_FALLBACK_SCSV); } TlsUtilities.WriteUint16ArrayWithUint16Length(mOfferedCipherSuites, message); } TlsUtilities.WriteUint8ArrayWithUint8Length(mOfferedCompressionMethods, message); if (mClientExtensions != null) { WriteExtensions(message, mClientExtensions); } message.WriteToRecordStream(this); }
internal virtual void SetServerVersion(ProtocolVersion serverVersion) { this.mServerVersion = serverVersion; }
protected virtual void ProcessClientHello(ServerHandshakeState state, byte[] body) { MemoryStream buf = new MemoryStream(body, false); // TODO Read RFCs for guidance on the expected record layer version number ProtocolVersion client_version = TlsUtilities.ReadVersion(buf); if (!client_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } /* * Read the client random */ byte[] client_random = TlsUtilities.ReadFully(32, buf); byte[] sessionID = TlsUtilities.ReadOpaque8(buf); if (sessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347 byte[] cookie = TlsUtilities.ReadOpaque8(buf); int cipher_suites_length = TlsUtilities.ReadUint16(buf); if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0) { throw new TlsFatalAlert(AlertDescription.decode_error); } /* * NOTE: "If the session_id field is not empty (implying a session resumption request) this * vector must include at least the cipher_suite from that session." */ state.offeredCipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, buf); int compression_methods_length = TlsUtilities.ReadUint8(buf); if (compression_methods_length < 1) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.offeredCompressionMethods = TlsUtilities.ReadUint8Array(compression_methods_length, buf); /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and send a server hello containing no * extensions. */ state.clientExtensions = TlsProtocol.ReadExtensions(buf); TlsServerContextImpl context = state.serverContext; SecurityParameters securityParameters = context.SecurityParameters; securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions); context.SetClientVersion(client_version); state.server.NotifyClientVersion(client_version); state.server.NotifyFallback(Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)); securityParameters.clientRandom = client_random; state.server.NotifyOfferedCipherSuites(state.offeredCipherSuites); state.server.NotifyOfferedCompressionMethods(state.offeredCompressionMethods); /* * RFC 5746 3.6. Server Behavior: Initial Handshake */ { /* * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ /* * When a ClientHello is received, the server MUST check if it includes the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag * to TRUE. */ if (Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { state.secure_renegotiation = true; } /* * The server MUST check if the "renegotiation_info" extension is included in the * ClientHello. */ byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info); if (renegExtData != null) { /* * If the extension is present, set secure_renegotiation flag to TRUE. The * server MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake. */ state.secure_renegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } state.server.NotifySecureRenegotiation(state.secure_renegotiation); if (state.clientExtensions != null) { state.server.ProcessClientExtensions(state.clientExtensions); } }
internal virtual void SetClientVersion(ProtocolVersion clientVersion) { mClientVersion = clientVersion; }
public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { byte[] record = null; for (;;) { int receiveLimit = System.Math.Min(len, GetReceiveLimit()) + RECORD_HEADER_LENGTH; if (record == null || record.Length < receiveLimit) { record = new byte[receiveLimit]; } try { if (mRetransmit != null && DateTimeUtilities.CurrentUnixMs() > mRetransmitExpiry) { mRetransmit = null; mRetransmitEpoch = null; } int received = ReceiveRecord(record, 0, receiveLimit, waitMillis); if (received < 0) { return(received); } if (received < RECORD_HEADER_LENGTH) { continue; } int length = TlsUtilities.ReadUint16(record, 11); if (received != (length + RECORD_HEADER_LENGTH)) { continue; } byte type = TlsUtilities.ReadUint8(record, 0); // TODO Support user-specified custom protocols? switch (type) { case ContentType.alert: case ContentType.application_data: case ContentType.change_cipher_spec: case ContentType.handshake: case ContentType.heartbeat: break; default: // TODO Exception? continue; } int epoch = TlsUtilities.ReadUint16(record, 3); DtlsEpoch recordEpoch = null; if (epoch == mReadEpoch.Epoch) { recordEpoch = mReadEpoch; } else if (type == ContentType.handshake && mRetransmitEpoch != null && epoch == mRetransmitEpoch.Epoch) { recordEpoch = mRetransmitEpoch; } if (recordEpoch == null) { continue; } long seq = TlsUtilities.ReadUint48(record, 5); if (recordEpoch.ReplayWindow.ShouldDiscard(seq)) { continue; } ProtocolVersion version = TlsUtilities.ReadVersion(record, 1); if (!version.IsDtls) { continue; } if (mReadVersion != null && !mReadVersion.Equals(version)) { continue; } byte[] plaintext = recordEpoch.Cipher.DecodeCiphertext( GetMacSequenceNumber(recordEpoch.Epoch, seq), type, record, RECORD_HEADER_LENGTH, received - RECORD_HEADER_LENGTH); recordEpoch.ReplayWindow.ReportAuthenticated(seq); if (plaintext.Length > this.mPlaintextLimit) { continue; } if (mReadVersion == null) { mReadVersion = version; } switch (type) { case ContentType.alert: { if (plaintext.Length == 2) { byte alertLevel = plaintext[0]; byte alertDescription = plaintext[1]; mPeer.NotifyAlertReceived(alertLevel, alertDescription); if (alertLevel == AlertLevel.fatal) { Failed(); throw new TlsFatalAlert(alertDescription); } // TODO Can close_notify be a fatal alert? if (alertDescription == AlertDescription.close_notify) { CloseTransport(); } } continue; } case ContentType.application_data: { if (mInHandshake) { // TODO Consider buffering application data for new epoch that arrives // out-of-order with the Finished message continue; } break; } case ContentType.change_cipher_spec: { // Implicitly receive change_cipher_spec and change to pending cipher state for (int i = 0; i < plaintext.Length; ++i) { byte message = TlsUtilities.ReadUint8(plaintext, i); if (message != ChangeCipherSpec.change_cipher_spec) { continue; } if (mPendingEpoch != null) { mReadEpoch = mPendingEpoch; } } continue; } case ContentType.handshake: { if (!mInHandshake) { if (mRetransmit != null) { mRetransmit.ReceivedHandshakeRecord(epoch, plaintext, 0, plaintext.Length); } // TODO Consider support for HelloRequest continue; } break; } case ContentType.heartbeat: { // TODO[RFC 6520] continue; } } /* * NOTE: If we receive any non-handshake data in the new epoch implies the peer has * received our final flight. */ if (!mInHandshake && mRetransmit != null) { this.mRetransmit = null; this.mRetransmitEpoch = null; } Array.Copy(plaintext, 0, buf, off, plaintext.Length); return(plaintext.Length); } catch (IOException e) { // NOTE: Assume this is a timeout for the moment throw e; } } }
internal virtual void SetServerVersion(ProtocolVersion serverVersion) { mServerVersion = serverVersion; }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { ProtocolVersion protocolVersion = TlsUtilities.ReadVersion(buf); if (protocolVersion.IsDtls) { throw new TlsFatalAlert(47); } if (!protocolVersion.Equals(mRecordStream.ReadVersion)) { throw new TlsFatalAlert(47); } ProtocolVersion clientVersion = Context.ClientVersion; if (!protocolVersion.IsEqualOrEarlierVersionOf(clientVersion)) { throw new TlsFatalAlert(47); } mRecordStream.SetWriteVersion(protocolVersion); ContextAdmin.SetServerVersion(protocolVersion); mTlsClient.NotifyServerVersion(protocolVersion); mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); if (mSelectedSessionID.Length > 32) { throw new TlsFatalAlert(47); } mTlsClient.NotifySessionID(mSelectedSessionID); mResumedSession = (mSelectedSessionID.Length > 0 && mTlsSession != null && Arrays.AreEqual(mSelectedSessionID, mTlsSession.SessionID)); int num = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(mOfferedCipherSuites, num) || num == 0 || CipherSuite.IsScsv(num) || !TlsUtilities.IsValidCipherSuiteForVersion(num, Context.ServerVersion)) { throw new TlsFatalAlert(47); } mTlsClient.NotifySelectedCipherSuite(num); byte b = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(mOfferedCompressionMethods, b)) { throw new TlsFatalAlert(47); } mTlsClient.NotifySelectedCompressionMethod(b); mServerExtensions = TlsProtocol.ReadExtensions(buf); if (mServerExtensions != null) { foreach (int key in mServerExtensions.Keys) { if (key != 65281) { if (TlsUtilities.GetExtensionData(mClientExtensions, key) == null) { throw new TlsFatalAlert(110); } if (!mResumedSession) { } } } } byte[] extensionData = TlsUtilities.GetExtensionData(mServerExtensions, 65281); if (extensionData != null) { mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(extensionData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(40); } } mTlsClient.NotifySecureRenegotiation(mSecureRenegotiation); IDictionary dictionary = mClientExtensions; IDictionary dictionary2 = mServerExtensions; if (mResumedSession) { if (num != mSessionParameters.CipherSuite || b != mSessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(47); } dictionary = null; dictionary2 = mSessionParameters.ReadServerExtensions(); } mSecurityParameters.cipherSuite = num; mSecurityParameters.compressionAlgorithm = b; if (dictionary2 != null) { bool flag = TlsExtensionsUtilities.HasEncryptThenMacExtension(dictionary2); if (flag && !TlsUtilities.IsBlockCipherSuite(num)) { throw new TlsFatalAlert(47); } mSecurityParameters.encryptThenMac = flag; mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(dictionary2); mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(dictionary, dictionary2, 47); mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(dictionary2); mAllowCertificateStatus = (!mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(dictionary2, 5, 47)); mExpectSessionTicket = (!mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(dictionary2, 35, 47)); } if (dictionary != null) { mTlsClient.ProcessServerExtensions(dictionary2); } mSecurityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(Context, mSecurityParameters.CipherSuite); mSecurityParameters.verifyDataLength = 12; }
internal virtual ProtocolVersion ResetDiscoveredPeerVersion() { ProtocolVersion result = mDiscoveredPeerVersion; mDiscoveredPeerVersion = null; return result; }
public virtual IDictionary GetClientExtensions() { IDictionary clientExtensions = null; ProtocolVersion clientVersion = mContext.ClientVersion; /* * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2. * Clients MUST NOT offer it if they are offering prior versions. */ if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion)) { // TODO Provide a way for the user to specify the acceptable hash/signature algorithms. byte[] hashAlgorithms = new byte[] { HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256, HashAlgorithm.sha224, HashAlgorithm.sha1 }; // TODO Sort out ECDSA signatures and add them as the preferred option here byte[] signatureAlgorithms = new byte[] { SignatureAlgorithm.rsa }; this.mSupportedSignatureAlgorithms = Platform.CreateArrayList(); for (int i = 0; i < hashAlgorithms.Length; ++i) { for (int j = 0; j < signatureAlgorithms.Length; ++j) { this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i], signatureAlgorithms[j])); } } /* * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1. */ this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa)); clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions); TlsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, mSupportedSignatureAlgorithms); } if (TlsEccUtilities.ContainsEccCipherSuites(GetCipherSuites())) { /* * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message * appends these extensions (along with any others), enumerating the curves it supports * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic * Curves Extension and the Supported Point Formats Extension. */ /* * TODO Could just add all the curves since we support them all, but users may not want * to use unnecessarily large fields. Need configuration options. */ this.mNamedCurves = new int[] { NamedCurve.secp256r1, NamedCurve.secp384r1 }; this.mClientECPointFormats = new byte[] { ECPointFormat.uncompressed, ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, }; clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions); TlsEccUtilities.AddSupportedEllipticCurvesExtension(clientExtensions, mNamedCurves); TlsEccUtilities.AddSupportedPointFormatsExtension(clientExtensions, mClientECPointFormats); } return(clientExtensions); }
public virtual ProtocolVersion GetServerVersion() { if (MinimumVersion.IsEqualOrEarlierVersionOf(mClientVersion)) { ProtocolVersion maximumVersion = MaximumVersion; if (mClientVersion.IsEqualOrEarlierVersionOf(maximumVersion)) { return mServerVersion = mClientVersion; } if (mClientVersion.IsLaterVersionOf(maximumVersion)) { return mServerVersion = maximumVersion; } } throw new TlsFatalAlert(AlertDescription.protocol_version); }
public bool Equals(ProtocolVersion other) { return(other != null && version == other.version); }
internal virtual bool ReadRecord() { byte[] recordHeader = TlsUtilities.ReadAllOrNothing(TLS_HEADER_SIZE, mInput); if (recordHeader == null) return false; byte type = TlsUtilities.ReadUint8(recordHeader, TLS_HEADER_TYPE_OFFSET); /* * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an * unexpected_message alert. */ CheckType(type, AlertDescription.unexpected_message); if (!mRestrictReadVersion) { int version = TlsUtilities.ReadVersionRaw(recordHeader, TLS_HEADER_VERSION_OFFSET); if ((version & 0xffffff00) != 0x0300) throw new TlsFatalAlert(AlertDescription.illegal_parameter); } else { ProtocolVersion version = TlsUtilities.ReadVersion(recordHeader, TLS_HEADER_VERSION_OFFSET); if (mReadVersion == null) { mReadVersion = version; } else if (!version.Equals(mReadVersion)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } int length = TlsUtilities.ReadUint16(recordHeader, TLS_HEADER_LENGTH_OFFSET); byte[] plaintext = DecodeAndVerify(type, mInput, length); mHandler.ProcessRecord(type, plaintext, 0, plaintext.Length); return true; }
protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client) { ProtocolVersion client_version = client.ClientVersion; if (!client_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.internal_error); } TlsClientContextImpl context = state.clientContext; context.SetClientVersion(client_version); SecurityParameters securityParameters = context.SecurityParameters; // Session ID byte[] session_id = TlsUtilities.EmptyBytes; if (state.tlsSession != null) { session_id = state.tlsSession.SessionID; if (session_id == null || session_id.Length > 32) { session_id = TlsUtilities.EmptyBytes; } } bool fallback = client.IsFallback; state.offeredCipherSuites = client.GetCipherSuites(); if (session_id.Length > 0 && state.sessionParameters != null) { if (!state.sessionParameters.IsExtendedMasterSecret || !Arrays.Contains(state.offeredCipherSuites, state.sessionParameters.CipherSuite) || CompressionMethod.cls_null != state.sessionParameters.CompressionAlgorithm) { session_id = TlsUtilities.EmptyBytes; } } state.clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(client.GetClientExtensions()); TlsExtensionsUtilities.AddExtendedMasterSecretExtension(state.clientExtensions); MemoryStream buf = new MemoryStream(); TlsUtilities.WriteVersion(client_version, buf); buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length); TlsUtilities.WriteOpaque8(session_id, buf); // Cookie TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); // Cipher Suites (and SCSV) { /* * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); bool noRenegSCSV = !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); if (noRenegExt && noRenegSCSV) { // TODO Consider whether to default to a client extension instead state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); } /* * RFC 7507 4. If a client sends a ClientHello.client_version containing a lower value * than the latest (highest-valued) version supported by the client, it SHOULD include * the TLS_FALLBACK_SCSV cipher suite value in ClientHello.cipher_suites [..]. (The * client SHOULD put TLS_FALLBACK_SCSV after all cipher suites that it actually intends * to negotiate.) */ if (fallback && !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)) { state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV); } TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf); } TlsUtilities.WriteUint8ArrayWithUint8Length(new byte[] { CompressionMethod.cls_null }, buf); TlsProtocol.WriteExtensions(buf, state.clientExtensions); return(buf.ToArray()); }
public virtual void NotifyServerVersion(ProtocolVersion serverVersion) { if (!MinimumVersion.IsEqualOrEarlierVersionOf(serverVersion)) throw new TlsFatalAlert(AlertDescription.protocol_version); }
public virtual int Receive(byte[] buf, int off, int len, int waitMillis) { byte[] array = null; int result; while (true) { int num = Math.Min(len, this.GetReceiveLimit()) + 13; if (array == null || array.Length < num) { array = new byte[num]; } try { if (this.mRetransmit != null && DateTimeUtilities.CurrentUnixMs() > this.mRetransmitExpiry) { this.mRetransmit = null; this.mRetransmitEpoch = null; } int num2 = this.ReceiveRecord(array, 0, num, waitMillis); if (num2 < 0) { result = num2; } else { if (num2 < 13) { continue; } int num3 = TlsUtilities.ReadUint16(array, 11); if (num2 != num3 + 13) { continue; } byte b = TlsUtilities.ReadUint8(array, 0); switch (b) { case 20: case 21: case 22: case 23: case 24: { int num4 = TlsUtilities.ReadUint16(array, 3); DtlsEpoch dtlsEpoch = null; if (num4 == this.mReadEpoch.Epoch) { dtlsEpoch = this.mReadEpoch; } else if (b == 22 && this.mRetransmitEpoch != null && num4 == this.mRetransmitEpoch.Epoch) { dtlsEpoch = this.mRetransmitEpoch; } if (dtlsEpoch == null) { continue; } long num5 = TlsUtilities.ReadUint48(array, 5); if (dtlsEpoch.ReplayWindow.ShouldDiscard(num5)) { continue; } ProtocolVersion other = TlsUtilities.ReadVersion(array, 1); if (this.mDiscoveredPeerVersion != null && !this.mDiscoveredPeerVersion.Equals(other)) { continue; } byte[] array2 = dtlsEpoch.Cipher.DecodeCiphertext(DtlsRecordLayer.GetMacSequenceNumber(dtlsEpoch.Epoch, num5), b, array, 13, num2 - 13); dtlsEpoch.ReplayWindow.ReportAuthenticated(num5); if (array2.Length > this.mPlaintextLimit) { continue; } if (this.mDiscoveredPeerVersion == null) { this.mDiscoveredPeerVersion = other; } switch (b) { case 20: for (int i = 0; i < array2.Length; i++) { byte b2 = TlsUtilities.ReadUint8(array2, i); if (b2 == 1 && this.mPendingEpoch != null) { this.mReadEpoch = this.mPendingEpoch; } } continue; case 21: if (array2.Length == 2) { byte b3 = array2[0]; byte b4 = array2[1]; this.mPeer.NotifyAlertReceived(b3, b4); if (b3 == 2) { this.Fail(b4); throw new TlsFatalAlert(b4); } if (b4 == 0) { this.CloseTransport(); } } continue; case 22: if (!this.mInHandshake) { if (this.mRetransmit != null) { this.mRetransmit.ReceivedHandshakeRecord(num4, array2, 0, array2.Length); } continue; } break; case 23: if (this.mInHandshake) { continue; } break; case 24: continue; } if (!this.mInHandshake && this.mRetransmit != null) { this.mRetransmit = null; this.mRetransmitEpoch = null; } Array.Copy(array2, 0, buf, off, array2.Length); result = array2.Length; break; } default: continue; } } } catch (IOException ex) { throw ex; } break; } return(result); }
public static bool IsValidCipherSuiteForVersion(int cipherSuite, ProtocolVersion serverVersion) { return GetMinimumVersion(cipherSuite).IsEqualOrEarlierVersionOf(serverVersion.GetEquivalentTLSVersion()); }
protected virtual byte[] GenerateClientHello(ClientHandshakeState state, TlsClient client) { MemoryStream buf = new MemoryStream(); ProtocolVersion client_version = client.ClientVersion; if (!client_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.internal_error); } TlsClientContextImpl context = state.clientContext; context.SetClientVersion(client_version); TlsUtilities.WriteVersion(client_version, buf); SecurityParameters securityParameters = context.SecurityParameters; buf.Write(securityParameters.ClientRandom, 0, securityParameters.ClientRandom.Length); // Session ID byte[] session_id = TlsUtilities.EmptyBytes; if (state.tlsSession != null) { session_id = state.tlsSession.SessionID; if (session_id == null || session_id.Length > 32) { session_id = TlsUtilities.EmptyBytes; } } TlsUtilities.WriteOpaque8(session_id, buf); // Cookie TlsUtilities.WriteOpaque8(TlsUtilities.EmptyBytes, buf); bool fallback = client.IsFallback; /* * Cipher suites */ state.offeredCipherSuites = client.GetCipherSuites(); // Integer -> byte[] state.clientExtensions = client.GetClientExtensions(); securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions); // Cipher Suites (and SCSV) { /* * RFC 5746 3.4. The client MUST include either an empty "renegotiation_info" extension, * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ byte[] renegExtData = TlsUtilities.GetExtensionData(state.clientExtensions, ExtensionType.renegotiation_info); bool noRenegExt = (null == renegExtData); bool noRenegSCSV = !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); if (noRenegExt && noRenegSCSV) { // TODO Consider whether to default to a client extension instead state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); } /* * draft-ietf-tls-downgrade-scsv-00 4. If a client sends a ClientHello.client_version * containing a lower value than the latest (highest-valued) version supported by the * client, it SHOULD include the TLS_FALLBACK_SCSV cipher suite value in * ClientHello.cipher_suites. */ if (fallback && !Arrays.Contains(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV)) { state.offeredCipherSuites = Arrays.Append(state.offeredCipherSuites, CipherSuite.TLS_FALLBACK_SCSV); } TlsUtilities.WriteUint16ArrayWithUint16Length(state.offeredCipherSuites, buf); } // TODO Add support for compression // Compression methods // state.offeredCompressionMethods = client.getCompressionMethods(); state.offeredCompressionMethods = new byte[] { CompressionMethod.cls_null }; TlsUtilities.WriteUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf); // Extensions if (state.clientExtensions != null) { TlsProtocol.WriteExtensions(buf, state.clientExtensions); } return(buf.ToArray()); }
public static void WriteVersion(ProtocolVersion version, byte[] buf, int offset) { buf[offset] = (byte)version.MajorVersion; buf[offset + 1] = (byte)version.MinorVersion; }
protected virtual void ProcessServerHello(ClientHandshakeState state, byte[] body) { SecurityParameters securityParameters = state.clientContext.SecurityParameters; MemoryStream buf = new MemoryStream(body, false); ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); ReportServerVersion(state, server_version); securityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); state.selectedSessionID = TlsUtilities.ReadOpaque8(buf); if (state.selectedSessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.client.NotifySessionID(state.selectedSessionID); state.selectedCipherSuite = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(state.offeredCipherSuites, state.selectedCipherSuite) || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(state.selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(state.selectedCipherSuite, server_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ValidateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.illegal_parameter); state.client.NotifySelectedCipherSuite(state.selectedCipherSuite); state.selectedCompressionMethod = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(state.offeredCompressionMethods, (byte)state.selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } state.client.NotifySelectedCompressionMethod((byte)state.selectedCompressionMethod); /* * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server * hello message when the client has requested extended functionality via the extended * client hello message specified in Section 2.1. ... Note that the extended server hello * message is only sent in response to an extended client hello message. This prevents the * possibility that the extended server hello message could "break" existing TLS 1.0 * clients. */ /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and send a server hello containing no * extensions. */ // Integer -> byte[] IDictionary serverExtensions = TlsProtocol.ReadExtensions(buf); /* * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret" * extension, it MUST include the "extended_master_secret" extension in its ServerHello * message. */ bool serverSentExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverExtensions); if (serverSentExtendedMasterSecret != securityParameters.extendedMasterSecret) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } /* * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an * extended client hello message. However, see RFC 5746 exception below. We always include * the SCSV, so an Extended Server Hello is always allowed. */ if (serverExtensions != null) { foreach (int extType in serverExtensions.Keys) { /* * RFC 5746 3.6. Note that sending a "renegotiation_info" extension in response to a * ClientHello containing only the SCSV is an explicit exception to the prohibition * in RFC 5246, Section 7.4.1.4, on the server sending unsolicited extensions and is * only allowed because the client is signaling its willingness to receive the * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ if (extType == ExtensionType.renegotiation_info) { continue; } /* * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the * same extension type appeared in the corresponding ClientHello. If a client * receives an extension type in ServerHello that it did not request in the * associated ClientHello, it MUST abort the handshake with an unsupported_extension * fatal alert. */ if (null == TlsUtilities.GetExtensionData(state.clientExtensions, extType)) { throw new TlsFatalAlert(AlertDescription.unsupported_extension); } /* * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to * proceed with resumption, the extension does not have any effect. Requiring the * extension to be included anyway makes the extension negotiation logic easier, * because it does not depend on whether resumption is accepted or not. */ if (extType == ExtensionType.extended_master_secret) { continue; } /* * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and send a server hello containing no * extensions[.] */ // TODO[sessions] // if (this.mResumedSession) // { // // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats // // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats // // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats //// throw new TlsFatalAlert(AlertDescription.illegal_parameter); // } } /* * RFC 5746 3.4. Client Behavior: Initial Handshake */ { /* * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ byte[] renegExtData = (byte[])serverExtensions[ExtensionType.renegotiation_info]; if (renegExtData != null) { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The * client MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake (by sending a fatal * handshake_failure alert). */ state.secure_renegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, TlsProtocol.CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } /* * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the * client. */ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(serverExtensions); if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(state.selectedCipherSuite)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } securityParameters.encryptThenMac = serverSentEncryptThenMAC; state.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions, AlertDescription.illegal_parameter); securityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(serverExtensions); state.allowCertificateStatus = TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.status_request, AlertDescription.illegal_parameter); state.expectSessionTicket = TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.session_ticket, AlertDescription.illegal_parameter); } state.client.NotifySecureRenegotiation(state.secure_renegotiation); if (state.clientExtensions != null) { state.client.ProcessServerExtensions(serverExtensions); } }
internal virtual DtlsTransport ClientHandshake(ClientHandshakeState state, DtlsRecordLayer recordLayer) { SecurityParameters securityParameters = state.clientContext.SecurityParameters; DtlsReliableHandshake handshake = new DtlsReliableHandshake(state.clientContext, recordLayer); byte[] clientHelloBody = GenerateClientHello(state, state.client); handshake.SendMessage(HandshakeType.client_hello, clientHelloBody); DtlsReliableHandshake.Message serverMessage = handshake.ReceiveMessage(); while (serverMessage.Type == HandshakeType.hello_verify_request) { ProtocolVersion recordLayerVersion = recordLayer.ResetDiscoveredPeerVersion(); ProtocolVersion client_version = state.clientContext.ClientVersion; /* * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0 regardless of * the version of TLS that is expected to be negotiated. DTLS 1.2 and 1.0 clients MUST use * the version solely to indicate packet formatting (which is the same in both DTLS 1.2 and * 1.0) and not as part of version negotiation. */ if (!recordLayerVersion.IsEqualOrEarlierVersionOf(client_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } byte[] cookie = ProcessHelloVerifyRequest(state, serverMessage.Body); byte[] patched = PatchClientHelloWithCookie(clientHelloBody, cookie); handshake.ResetHandshakeMessagesDigest(); handshake.SendMessage(HandshakeType.client_hello, patched); serverMessage = handshake.ReceiveMessage(); } if (serverMessage.Type == HandshakeType.server_hello) { ReportServerVersion(state, recordLayer.DiscoveredPeerVersion); ProcessServerHello(state, serverMessage.Body); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } if (state.maxFragmentLength >= 0) { int plainTextLimit = 1 << (8 + state.maxFragmentLength); recordLayer.SetPlaintextLimit(plainTextLimit); } securityParameters.cipherSuite = state.selectedCipherSuite; securityParameters.compressionAlgorithm = (byte)state.selectedCompressionMethod; securityParameters.prfAlgorithm = TlsProtocol.GetPrfAlgorithm(state.clientContext, state.selectedCipherSuite); /* * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has * a verify_data_length equal to 12. This includes all existing cipher suites. */ securityParameters.verifyDataLength = 12; handshake.NotifyHelloComplete(); bool resumedSession = state.selectedSessionID.Length > 0 && state.tlsSession != null && Arrays.AreEqual(state.selectedSessionID, state.tlsSession.SessionID); if (resumedSession) { if (securityParameters.CipherSuite != state.sessionParameters.CipherSuite || securityParameters.CompressionAlgorithm != state.sessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } IDictionary sessionServerExtensions = state.sessionParameters.ReadServerExtensions(); securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); securityParameters.masterSecret = Arrays.Clone(state.sessionParameters.MasterSecret); recordLayer.InitPendingEpoch(state.client.GetCipher()); // NOTE: Calculated exclusive of the actual Finished message from the server byte[] resExpectedServerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.server_finished, TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), resExpectedServerVerifyData); // NOTE: Calculated exclusive of the Finished message itself byte[] resClientVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.client_finished, TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); handshake.SendMessage(HandshakeType.finished, resClientVerifyData); handshake.Finish(); state.clientContext.SetResumableSession(state.tlsSession); state.client.NotifyHandshakeComplete(); return(new DtlsTransport(recordLayer)); } InvalidateSession(state); if (state.selectedSessionID.Length > 0) { state.tlsSession = new TlsSessionImpl(state.selectedSessionID, null); } serverMessage = handshake.ReceiveMessage(); if (serverMessage.Type == HandshakeType.supplemental_data) { ProcessServerSupplementalData(state, serverMessage.Body); serverMessage = handshake.ReceiveMessage(); } else { state.client.ProcessServerSupplementalData(null); } state.keyExchange = state.client.GetKeyExchange(); state.keyExchange.Init(state.clientContext); Certificate serverCertificate = null; if (serverMessage.Type == HandshakeType.certificate) { serverCertificate = ProcessServerCertificate(state, serverMessage.Body); serverMessage = handshake.ReceiveMessage(); } else { // Okay, Certificate is optional state.keyExchange.SkipServerCredentials(); } // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus if (serverCertificate == null || serverCertificate.IsEmpty) { state.allowCertificateStatus = false; } if (serverMessage.Type == HandshakeType.certificate_status) { ProcessCertificateStatus(state, serverMessage.Body); serverMessage = handshake.ReceiveMessage(); } else { // Okay, CertificateStatus is optional } if (serverMessage.Type == HandshakeType.server_key_exchange) { ProcessServerKeyExchange(state, serverMessage.Body); serverMessage = handshake.ReceiveMessage(); } else { // Okay, ServerKeyExchange is optional state.keyExchange.SkipServerKeyExchange(); } if (serverMessage.Type == HandshakeType.certificate_request) { ProcessCertificateRequest(state, serverMessage.Body); /* * TODO Give the client a chance to immediately select the CertificateVerify hash * algorithm here to avoid tracking the other hash algorithms unnecessarily? */ TlsUtilities.TrackHashAlgorithms(handshake.HandshakeHash, state.certificateRequest.SupportedSignatureAlgorithms); serverMessage = handshake.ReceiveMessage(); } else { // Okay, CertificateRequest is optional } if (serverMessage.Type == HandshakeType.server_hello_done) { if (serverMessage.Body.Length != 0) { throw new TlsFatalAlert(AlertDescription.decode_error); } } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } handshake.HandshakeHash.SealHashAlgorithms(); IList clientSupplementalData = state.client.GetClientSupplementalData(); if (clientSupplementalData != null) { byte[] supplementalDataBody = GenerateSupplementalData(clientSupplementalData); handshake.SendMessage(HandshakeType.supplemental_data, supplementalDataBody); } if (state.certificateRequest != null) { state.clientCredentials = state.authentication.GetClientCredentials(state.certificateRequest); /* * RFC 5246 If no suitable certificate is available, the client MUST send a certificate * message containing no certificates. * * NOTE: In previous RFCs, this was SHOULD instead of MUST. */ Certificate clientCertificate = null; if (state.clientCredentials != null) { clientCertificate = state.clientCredentials.Certificate; } if (clientCertificate == null) { clientCertificate = Certificate.EmptyChain; } byte[] certificateBody = GenerateCertificate(clientCertificate); handshake.SendMessage(HandshakeType.certificate, certificateBody); } if (state.clientCredentials != null) { state.keyExchange.ProcessClientCredentials(state.clientCredentials); } else { state.keyExchange.SkipClientCredentials(); } byte[] clientKeyExchangeBody = GenerateClientKeyExchange(state); handshake.SendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody); TlsHandshakeHash prepareFinishHash = handshake.PrepareToFinish(); securityParameters.sessionHash = TlsProtocol.GetCurrentPrfHash(state.clientContext, prepareFinishHash, null); TlsProtocol.EstablishMasterSecret(state.clientContext, state.keyExchange); recordLayer.InitPendingEpoch(state.client.GetCipher()); if (state.clientCredentials != null && state.clientCredentials is TlsSignerCredentials) { TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials; /* * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 */ SignatureAndHashAlgorithm signatureAndHashAlgorithm; byte[] hash; if (TlsUtilities.IsTlsV12(state.clientContext)) { signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm; if (signatureAndHashAlgorithm == null) { throw new TlsFatalAlert(AlertDescription.internal_error); } hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash); } else { signatureAndHashAlgorithm = null; hash = securityParameters.SessionHash; } byte[] signature = signerCredentials.GenerateCertificateSignature(hash); DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature); byte[] certificateVerifyBody = GenerateCertificateVerify(state, certificateVerify); handshake.SendMessage(HandshakeType.certificate_verify, certificateVerifyBody); } // NOTE: Calculated exclusive of the Finished message itself byte[] clientVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.client_finished, TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); handshake.SendMessage(HandshakeType.finished, clientVerifyData); if (state.expectSessionTicket) { serverMessage = handshake.ReceiveMessage(); if (serverMessage.Type == HandshakeType.session_ticket) { ProcessNewSessionTicket(state, serverMessage.Body); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } } // NOTE: Calculated exclusive of the actual Finished message from the server byte[] expectedServerVerifyData = TlsUtilities.CalculateVerifyData(state.clientContext, ExporterLabel.server_finished, TlsProtocol.GetCurrentPrfHash(state.clientContext, handshake.HandshakeHash, null)); ProcessFinished(handshake.ReceiveMessageBody(HandshakeType.finished), expectedServerVerifyData); handshake.Finish(); if (state.tlsSession != null) { state.sessionParameters = new SessionParameters.Builder() .SetCipherSuite(securityParameters.cipherSuite) .SetCompressionAlgorithm(securityParameters.compressionAlgorithm) .SetMasterSecret(securityParameters.masterSecret) .SetPeerCertificate(serverCertificate) .Build(); state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters); state.clientContext.SetResumableSession(state.tlsSession); } state.client.NotifyHandshakeComplete(); return(new DtlsTransport(recordLayer)); }
protected virtual void ReceiveServerHelloMessage(MemoryStream buf) { { ProtocolVersion server_version = TlsUtilities.ReadVersion(buf); if (server_version.IsDtls) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } // Check that this matches what the server is Sending in the record layer if (!server_version.Equals(this.mRecordStream.ReadVersion)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ProtocolVersion client_version = Context.ClientVersion; if (!server_version.IsEqualOrEarlierVersionOf(client_version)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mRecordStream.SetWriteVersion(server_version); ContextAdmin.SetServerVersion(server_version); this.mTlsClient.NotifyServerVersion(server_version); } /* * Read the server random */ this.mSecurityParameters.serverRandom = TlsUtilities.ReadFully(32, buf); this.mSelectedSessionID = TlsUtilities.ReadOpaque8(buf); if (this.mSelectedSessionID.Length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mTlsClient.NotifySessionID(this.mSelectedSessionID); this.mResumedSession = this.mSelectedSessionID.Length > 0 && this.mTlsSession != null && Arrays.AreEqual(this.mSelectedSessionID, this.mTlsSession.SessionID); /* * Find out which CipherSuite the server has chosen and check that it was one of the offered * ones, and is a valid selection for the negotiated version. */ int selectedCipherSuite = TlsUtilities.ReadUint16(buf); if (!Arrays.Contains(this.mOfferedCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || CipherSuite.IsScsv(selectedCipherSuite) || !TlsUtilities.IsValidCipherSuiteForVersion(selectedCipherSuite, Context.ServerVersion)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mTlsClient.NotifySelectedCipherSuite(selectedCipherSuite); /* * Find out which CompressionMethod the server has chosen and check that it was one of the * offered ones. */ byte selectedCompressionMethod = TlsUtilities.ReadUint8(buf); if (!Arrays.Contains(this.mOfferedCompressionMethods, selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mTlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod); /* * RFC3546 2.2 The extended server hello message format MAY be sent in place of the server * hello message when the client has requested extended functionality via the extended * client hello message specified in Section 2.1. ... Note that the extended server hello * message is only sent in response to an extended client hello message. This prevents the * possibility that the extended server hello message could "break" existing TLS 1.0 * clients. */ this.mServerExtensions = ReadExtensions(buf); /* * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an * extended client hello message. * * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server * Hello is always allowed. */ if (this.mServerExtensions != null) { foreach (int extType in this.mServerExtensions.Keys) { /* * RFC 5746 3.6. Note that Sending a "renegotiation_info" extension in response to a * ClientHello containing only the SCSV is an explicit exception to the prohibition * in RFC 5246, Section 7.4.1.4, on the server Sending unsolicited extensions and is * only allowed because the client is signaling its willingness to receive the * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ if (extType == ExtensionType.renegotiation_info) { continue; } /* * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the * same extension type appeared in the corresponding ClientHello. If a client * receives an extension type in ServerHello that it did not request in the * associated ClientHello, it MUST abort the handshake with an unsupported_extension * fatal alert. */ if (null == TlsUtilities.GetExtensionData(this.mClientExtensions, extType)) { throw new TlsFatalAlert(AlertDescription.unsupported_extension); } /* * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore * extensions appearing in the client hello, and Send a server hello containing no * extensions[.] */ if (this.mResumedSession) { // TODO[compat-gnutls] GnuTLS test server Sends server extensions e.g. ec_point_formats // TODO[compat-openssl] OpenSSL test server Sends server extensions e.g. ec_point_formats // TODO[compat-polarssl] PolarSSL test server Sends server extensions e.g. ec_point_formats // throw new TlsFatalAlert(AlertDescription.illegal_parameter); } } } /* * RFC 5746 3.4. Client Behavior: Initial Handshake */ { /* * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ byte[] renegExtData = TlsUtilities.GetExtensionData(this.mServerExtensions, ExtensionType.renegotiation_info); if (renegExtData != null) { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The * client MUST then verify that the length of the "renegotiated_connection" * field is zero, and if it is not, MUST abort the handshake (by Sending a fatal * handshake_failure alert). */ this.mSecureRenegotiation = true; if (!Arrays.ConstantTimeAreEqual(renegExtData, CreateRenegotiationInfo(TlsUtilities.EmptyBytes))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } // TODO[compat-gnutls] GnuTLS test server fails to Send renegotiation_info extension when resuming this.mTlsClient.NotifySecureRenegotiation(this.mSecureRenegotiation); IDictionary sessionClientExtensions = mClientExtensions, sessionServerExtensions = mServerExtensions; if (this.mResumedSession) { if (selectedCipherSuite != this.mSessionParameters.CipherSuite || selectedCompressionMethod != this.mSessionParameters.CompressionAlgorithm) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } sessionClientExtensions = null; sessionServerExtensions = this.mSessionParameters.ReadServerExtensions(); } this.mSecurityParameters.cipherSuite = selectedCipherSuite; this.mSecurityParameters.compressionAlgorithm = selectedCompressionMethod; if (sessionServerExtensions != null) { { /* * RFC 7366 3. If a server receives an encrypt-then-MAC request extension from a client * and then selects a stream or Authenticated Encryption with Associated Data (AEAD) * ciphersuite, it MUST NOT send an encrypt-then-MAC response extension back to the * client. */ bool serverSentEncryptThenMAC = TlsExtensionsUtilities.HasEncryptThenMacExtension(sessionServerExtensions); if (serverSentEncryptThenMAC && !TlsUtilities.IsBlockCipherSuite(selectedCipherSuite)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC; } this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions); this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions, sessionServerExtensions, AlertDescription.illegal_parameter); this.mSecurityParameters.truncatedHMac = TlsExtensionsUtilities.HasTruncatedHMacExtension(sessionServerExtensions); /* * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in * a session resumption handshake. */ this.mAllowCertificateStatus = !this.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.status_request, AlertDescription.illegal_parameter); this.mExpectSessionTicket = !this.mResumedSession && TlsUtilities.HasExpectedEmptyExtensionData(sessionServerExtensions, ExtensionType.session_ticket, AlertDescription.illegal_parameter); } /* * TODO[session-hash] * * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes * that do not use the extended master secret [..]. (and see 5.2, 5.3) */ if (sessionClientExtensions != null) { this.mTlsClient.ProcessServerExtensions(sessionServerExtensions); } this.mSecurityParameters.prfAlgorithm = GetPrfAlgorithm(Context, this.mSecurityParameters.CipherSuite); /* * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify * verify_data_length has a verify_data_length equal to 12. This includes all * existing cipher suites. */ this.mSecurityParameters.verifyDataLength = 12; }
public bool Equals(ProtocolVersion other) { return other != null && this.version == other.version; }
public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len) { int blockSize = encryptCipher.GetBlockSize(); int size = mWriteMac.Size; ProtocolVersion serverVersion = context.ServerVersion; int num = len; if (!encryptThenMac) { num += size; } int num2 = blockSize - 1 - num % blockSize; if (!serverVersion.IsDtls && !serverVersion.IsSsl) { int max = (255 - num2) / blockSize; int num3 = ChooseExtraPadBlocks(context.SecureRandom, max); num2 += num3 * blockSize; } int num4 = len + size + num2 + 1; if (useExplicitIV) { num4 += blockSize; } byte[] array = new byte[num4]; int num5 = 0; if (useExplicitIV) { byte[] array2 = new byte[blockSize]; context.NonceRandomGenerator.NextBytes(array2); encryptCipher.Init(forEncryption: true, new ParametersWithIV(null, array2)); global::System.Array.Copy((global::System.Array)array2, 0, (global::System.Array)array, num5, blockSize); num5 += blockSize; } int num6 = num5; global::System.Array.Copy((global::System.Array)plaintext, offset, (global::System.Array)array, num5, len); num5 += len; if (!encryptThenMac) { byte[] array3 = mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len); global::System.Array.Copy((global::System.Array)array3, 0, (global::System.Array)array, num5, array3.Length); num5 += array3.Length; } for (int i = 0; i <= num2; i++) { array[num5++] = (byte)num2; } for (int j = num6; j < num5; j += blockSize) { encryptCipher.ProcessBlock(array, j, array, j); } if (encryptThenMac) { byte[] array4 = mWriteMac.CalculateMac(seqNo, type, array, 0, num5); global::System.Array.Copy((global::System.Array)array4, 0, (global::System.Array)array, num5, array4.Length); num5 += array4.Length; } return(array); }