<commit_before>/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.network.tls

import io.ktor.network.tls.SecretExchangeType.*
import io.ktor.network.tls.certificates.*
import io.ktor.network.tls.cipher.*
import io.ktor.network.tls.extensions.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.io.*
import kotlinx.io.core.*
import java.security.*
import java.security.cert.*
import java.security.cert.Certificate
import java.security.interfaces.*
import java.security.spec.*
import javax.crypto.*
import javax.crypto.spec.*
import javax.security.auth.x500.*
import kotlin.coroutines.*

internal class TLSClientHandshake(
    rawInput: ByteReadChannel,
    rawOutput: ByteWriteChannel,
    private val config: TLSConfig,
    override val coroutineContext: CoroutineContext
) : CoroutineScope {
    private val digest = Digest()
    private val clientSeed: ByteArray = config.random.generateClientSeed()

    @Volatile
    private lateinit var serverHello: TLSServerHello

    @Volatile
    private lateinit var masterSecret: SecretKeySpec

    private val keyMaterial: ByteArray by lazy {
        with(serverHello.cipherSuite) {
            keyMaterial(
                masterSecret, serverHello.serverSeed + clientSeed,
                keyStrengthInBytes, macStrengthInBytes, fixedIvLength
            )
        }
    }

    private val cipher: TLSCipher by lazy {
        TLSCipher.fromSuite(serverHello.cipherSuite, keyMaterial)
    }

    @UseExperimental(ExperimentalCoroutinesApi::class)
    val input: ReceiveChannel<TLSRecord> = produce(CoroutineName("cio-tls-parser")) {
        var useCipher = false
        try {
            loop@ while (true) {
                val rawRecord = rawInput.readTLSRecord()
                val record = if (useCipher) cipher.decrypt(rawRecord) else rawRecord
                val packet = record.packet

                when (record.type) {
                    TLSRecordType.Alert -> {
                        val level = TLSAlertLevel.byCode(packet.readByte().toInt())
                        val code = TLSAlertType.byCode(packet.readByte().toInt())

                        if (code == TLSAlertType.CloseNotify) return@produce
                        val cause = TLSException("Received alert during handshake. Level: $level, code: $code")

                        channel.close(cause)
                        return@produce
                    }
                    TLSRecordType.ChangeCipherSpec -> {
                        check(!useCipher)
                        val flag = packet.readByte()
                        if (flag != 1.toByte()) throw TLSException("Expected flag: 1, received $flag in ChangeCipherSpec")
                        useCipher = true
                        continue@loop
                    }
                    else -> {
                    }
                }

                channel.send(TLSRecord(record.type, packet = packet))
            }
        } catch (cause: ClosedReceiveChannelException) {
            channel.close()
        } catch (cause: Throwable) {
            channel.close(cause)
            // Remote server closed connection
        } finally {
            output.close()
        }
    }

    @UseExperimental(ObsoleteCoroutinesApi::class)
    val output: SendChannel<TLSRecord> = actor(CoroutineName("cio-tls-encoder")) {
        var useCipher = false

        for (rawRecord in channel) {
            try {
                val record = if (useCipher) cipher.encrypt(rawRecord) else rawRecord
                if (rawRecord.type == TLSRecordType.ChangeCipherSpec) useCipher = true

                rawOutput.writeRecord(record)
            } catch (cause: Throwable) {
                channel.close(cause)
            }
        }

        rawOutput.close()
    }

    @UseExperimental(ExperimentalCoroutinesApi::class)
    private val handshakes = produce<TLSHandshake>(CoroutineName("cio-tls-handshake")) {<commit_msg>Send TLS close notification at session termination<commit_after>/*
 * Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

package io.ktor.network.tls

import io.ktor.network.tls.SecretExchangeType.*
import io.ktor.network.tls.certificates.*
import io.ktor.network.tls.cipher.*
import io.ktor.network.tls.extensions.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.io.*
import kotlinx.io.core.*
import java.security.*
import java.security.cert.*
import java.security.cert.Certificate
import java.security.interfaces.*
import java.security.spec.*
import javax.crypto.*
import javax.crypto.spec.*
import javax.security.auth.x500.*
import kotlin.coroutines.*

internal class TLSClientHandshake(
    rawInput: ByteReadChannel,
    rawOutput: ByteWriteChannel,
    private val config: TLSConfig,
    override val coroutineContext: CoroutineContext
) : CoroutineScope {
    private val digest = Digest()
    private val clientSeed: ByteArray = config.random.generateClientSeed()

    @Volatile
    private lateinit var serverHello: TLSServerHello

    @Volatile
    private lateinit var masterSecret: SecretKeySpec

    private val keyMaterial: ByteArray by lazy {
        with(serverHello.cipherSuite) {
            keyMaterial(
                masterSecret, serverHello.serverSeed + clientSeed,
                keyStrengthInBytes, macStrengthInBytes, fixedIvLength
            )
        }
    }

    private val cipher: TLSCipher by lazy {
        TLSCipher.fromSuite(serverHello.cipherSuite, keyMaterial)
    }

    @UseExperimental(ExperimentalCoroutinesApi::class)
    val input: ReceiveChannel<TLSRecord> = produce(CoroutineName("cio-tls-parser")) {
        var useCipher = false
        try {
            loop@ while (true) {
                val rawRecord = rawInput.readTLSRecord()
                val record = if (useCipher) cipher.decrypt(rawRecord) else rawRecord
                val packet = record.packet

                when (record.type) {
                    TLSRecordType.Alert -> {
                        val level = TLSAlertLevel.byCode(packet.readByte().toInt())
                        val code = TLSAlertType.byCode(packet.readByte().toInt())

                        if (code == TLSAlertType.CloseNotify) return@produce
                        val cause = TLSException("Received alert during handshake. Level: $level, code: $code")

                        channel.close(cause)
                        return@produce
                    }
                    TLSRecordType.ChangeCipherSpec -> {
                        check(!useCipher)
                        val flag = packet.readByte()
                        if (flag != 1.toByte()) throw TLSException("Expected flag: 1, received $flag in ChangeCipherSpec")
                        useCipher = true
                        continue@loop
                    }
                    else -> {
                    }
                }

                channel.send(TLSRecord(record.type, packet = packet))
            }
        } catch (cause: ClosedReceiveChannelException) {
            channel.close()
        } catch (cause: Throwable) {
            channel.close(cause)
            // Remote server closed connection
        } finally {
            output.close()
        }
    }

    @UseExperimental(ObsoleteCoroutinesApi::class)
    val output: SendChannel<TLSRecord> = actor(CoroutineName("cio-tls-encoder")) {
        var useCipher = false

        try {
            for (rawRecord in channel) {
                try {
                    val record = if (useCipher) cipher.encrypt(rawRecord) else rawRecord
                    if (rawRecord.type == TLSRecordType.ChangeCipherSpec) useCipher = true

                    rawOutput.writeRecord(record)
                } catch (cause: Throwable) {
                    channel.close(cause)
                }
            }
        } finally {
            rawOutput.writeRecord(TLSRecord(TLSRecordType.Alert, packet = buildPacket {
                writeByte(TLSAlertLevel.WARNING.code.toByte())
                writeByte(TLSAlertType.CloseNotify.code.toByte())
            }))

            rawOutput.close()
        }
    }

    @UseExperimental(ExperimentalCoroutinesApi::class)
    private val handshakes = produce<TLSHandshake>(CoroutineName("cio-tls-handshake")) {
{'fraction_non_alphanumeric': 0.0885207100591716, 'fraction_numerical': 0.0035502958579881655, 'mean_word_length': 2.0, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2373688', 'n_tokens_mistral': 2319, 'n_tokens_neox': 2166, 'n_words': 487}