All Downloads are FREE. Search and download functionalities are using the official Maven repository.

secretblob.SecretBlob.kt Maven / Gradle / Ivy

There is a newer version: 0.3.1
Show newest version
package se.wollan.crypto.secretblob

import se.wollan.crypto.fromHexString
import se.wollan.crypto.xor
import java.io.IOException
import java.io.RandomAccessFile
import java.nio.file.Path
import kotlin.io.path.absolutePathString

interface SecretBlob {

    /**
     * @return a fresh byte array - implementations are not allowed to not hold on to the instance after it is returned
     */
    fun getSecretDataForKey(key: SecretBlobKey): ByteArray
}

internal class SecretBlobImpl(
    private val keys: List,
    private val secretBlobFilePath: Path
) : SecretBlob {
    private val skipLength by lazy { getInitialNumberOfBytesToSkip(secretBlobFilePath) }

    override fun getSecretDataForKey(key: SecretBlobKey): ByteArray {
        val (startIndex, length) = key.startIndexAndLength(keys)
        val hexBytes = readBytesFromFile(secretBlobFilePath, skipLength + startIndex * 2, length * 2)
        val hexString = hexBytes.toString(Charsets.UTF_8)
        val secretData = hexString.fromHexString()
        check(secretData.size == key.length) // sanity check
        val decodedSecretData = key.hexMask.fromHexString().xor(secretData)
        return decodedSecretData
    }
}

internal fun SecretBlobKey.startIndexAndLength(keys: List): Pair {
    var i = 0
    for (key in keys) {
        if (this == key)
            return i to key.length

        i += key.length
    }

    throw IndexOutOfBoundsException("couldn't find secret blob key $this among ${keys.joinToString()}")
}

private fun getInitialNumberOfBytesToSkip(filePath: Path): Int {
    val batchSize = 16
    var index = 0
    while (true) { // until exception
        val bytes = readBytesFromFile(filePath, index, batchSize)
        val indexOfFirstByteToUse = bytes.indexOfFirst { it.isHex() }
        if (indexOfFirstByteToUse != -1)
            return index + indexOfFirstByteToUse

        index += batchSize
    }
}

private fun Byte.isHex(): Boolean {
    val c = toInt().toChar()
    return c.isDigit() || (c in 'a'..'f') || (c in 'A'..'F')
}

private fun readBytesFromFile(filePath: Path, startIndex: Int, length: Int): ByteArray {
    RandomAccessFile(filePath.absolutePathString(), "r").use { file ->
        val buffer = ByteArray(length)
        file.seek(startIndex.toLong())
        val read = file.read(buffer, 0, length)
        if (read != length)
            throw IOException("end of file reached, please generate more secret blob data")

        return buffer
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy