RawSource
Supplies a stream of bytes. RawSource is a base interface for kotlinx-io
data suppliers.
The interface should be implemented to read data from wherever it's located: from the network, storage, or a buffer in memory. Sources may be layered to transform supplied data, such as to decompress, decrypt, or remove protocol framing.
Most applications shouldn't operate on a raw source directly, but rather on a buffered Source which is both more efficient and more convenient. Use buffered to wrap any raw source with a buffer.
Implementors should abstain from throwing exceptions other than those that are documented for RawSource methods.
Thread-safety guarantees
RawSource implementations are not required to be thread safe. However, if an implementation provides some thread safety guarantees, it is recommended to explicitly document them.
Samples
import kotlinx.io.*
import kotlin.test.*
fun main() {
//sampleStart
/**
* Source decrypting all the data read from the downstream using RC4 algorithm.
*
* See https://en.wikipedia.org/wiki/RC4 for more information about the cypher.
*
* Implementation of RC4 stream cypher based on http://cypherpunks.venona.com/archive/1994/09/msg00304.html
*/
@OptIn(ExperimentalUnsignedTypes::class)
class RC4DecryptingSource(private val downstream: RawSource, key: String): RawSource {
private val buffer = Buffer()
private val key = RC4Key(key)
override fun readAtMostTo(sink: Buffer, byteCount: Long): Long {
val bytesRead = downstream.readAtMostTo(buffer, byteCount)
if (bytesRead == -1L) {
return -1L
}
while (!buffer.exhausted()) {
val byte = buffer.readByte()
sink.writeByte(byte.xor(key.nextByte()))
}
return bytesRead
}
override fun close() = downstream.close()
private inner class RC4Key(key: String) {
private var keyState: UByteArray
private var keyX: Int = 0
private var keyY: Int = 0
init {
require(key.isNotEmpty()) { "Key could not be empty" }
val keyBytes = key.encodeToByteArray()
keyState = UByteArray(256) { it.toUByte() }
var index1 = 0
var index2 = 0
for (idx in keyState.indices) {
index2 = (keyBytes[index1] + keyState[idx].toInt() + index2) % 256
swapStateBytes(idx, index2)
index1 = (index1 + 1) % keyBytes.size
}
}
fun nextByte(): Byte {
keyX = (keyX + 1) % 256
keyY = (keyState[keyX].toInt() + keyY) % 256
swapStateBytes(keyX, keyY)
val idx = (keyState[keyX] + keyState[keyY]) % 256U
return keyState[idx.toInt()].toByte()
}
private fun swapStateBytes(x: Int, y: Int) {
val tmp = keyState[x]
keyState[x] = keyState[y]
keyState[y] = tmp
}
}
}
val key = "key"
val source = Buffer().also { it.write(byteArrayOf(0x58, 0x09, 0x57, 0x9fU.toByte(), 0x41, 0xfbU.toByte())) }
val rc4Source = RC4DecryptingSource(source, key).buffered()
assertEquals("Secret", rc4Source.readString())
//sampleEnd
}