Skip to content

KotlinCrypto/core

Repository files navigation

core

badge-license badge-latest-release

badge-kotlin badge-endians

badge-platform-android badge-platform-jvm badge-platform-js badge-platform-js-node badge-platform-wasm badge-platform-linux badge-platform-macos badge-platform-ios badge-platform-tvos badge-platform-watchos badge-platform-windows badge-support-android-native badge-support-apple-silicon badge-support-js-ir badge-support-linux-arm

Low level core cryptographic components for Kotlin Multiplatform

NOTE: For Jvm, Digest extends java.security.MessageDigest and Mac extends javax.crypto.Mac for interoperability.

Utilized by KotlinCrypto/hash and KotlinCrypto/MACs

Library Authors

Modules in core are intentionally single purpose and small such that you are able to include them in your APIs without having to import some massive crypto library. Consumers of your APIs can then import the higher level implementations to use with your library (giving them the choice of what algorithms they wish to use, importing only what they need).

This also means that as new higher level functions get implemented, you do not need to do anything.

/**
 * This feature of Foo requires a dependency if you wish to use it.
 * See: https://github.com/KotlinCrypto/hash
 * */
class FooFeatureA(digest: Digest) {
    // ...
}

Usage

Digest
// Using SHA256 from hash repo as an example
import org.kotlincrypto.hash.sha2.SHA256

fun main() {
    val digest = SHA256()
    val bytes = Random.Default.nextBytes(615)
    
    // Digest implements Algorithm
    println(digest.algorithm())
    
    // Digest implements Updatable
    digest.update(5.toByte())
    digest.update(bytes)
    digest.update(bytes, 10, 88)

    // Digest implements Resettable
    digest.reset()

    digest.update(bytes)

    // Digest implements Copyable
    val copy = digest.copy()

    val hash = digest.digest()
    val hash2 = copy.digest(bytes)
}
Mac
// Using SecureRandom from the secure-random repo as an example
import org.kotlincrypto.SecureRandom
// Using HmacSHA3_256 from the MACs repo as an example
import org.kotlincrypto.macs.hmac.sha3.HmacSHA3_256

fun main() {
    val key = SecureRandom().nextBytesOf(100)
    val mac = HmacSHA3_256(key)
    val bytes = Random.Default.nextBytes(615)

    // Mac implements Algorithm
    println(mac.algorithm())

    // Mac implements Updatable
    mac.update(5.toByte())
    mac.update(bytes)
    mac.update(bytes, 10, 88)

    // Mac implements Resettable
    mac.reset()

    mac.update(bytes)

    // Mac implements Copyable
    val copy = mac.copy()

    val hash = mac.doFinal()
    val hash2 = copy.doFinal(bytes)
}
Xof

XOFs (i.e. Extendable-Output Functions) were introduced with SHA3.

XOFs are very similar to Digest and Mac except that instead of calling digest() or doFinal(), which returns a fixed size ByteArray, their output size can be variable in length.

As such, KotlinCrypto takes the approach of making them distinctly different from those types, while implementing the same interfaces (Algorithm, Copyable, Resettable, Updatable).

Output for an Xof is done by reading, instead.

// Using SHAKE128 from hash repo as an example
import org.kotlincrypto.hash.sha3.SHAKE128

fun main() {
    val xof: Xof<SHAKE128> = SHAKE128.xOf()
    val bytes = Random.Default.nextBytes(615)

    // Xof implements Algorithm
    println(xof.algorithm())

    // Xof implements Updatable
    xof.update(5.toByte())
    xof.update(bytes)
    xof.update(bytes, 10, 88)

    // Xof implements Resettable
    xof.reset()

    xof.update(bytes)

    // Xof implements Copyable
    xof.copy()

    val out1 = ByteArray(100)
    val out2 = ByteArray(12345)

    // Use produces a Reader which auto-closes when your action finishes.
    // Reader is using a snapshot of the Xof state (thus the
    // optional argument to resetXof with a default of true).
    xof.use(resetXof = false) { read(out1, 0, out1.size); read(out2) }

    val out3 = ByteArray(out1.size)
    val out4 = ByteArray(out2.size)

    // Can also create a Reader that won't auto-close
    val reader = xof.reader(resetXof = false)
    reader.read(out3)
    reader.read(out4)
    reader.close()

    try {
        // The Reader has been closed and will throw
        // exception when trying to read from again.
        reader.use { read(out4) }
    } catch (e: IllegalStateException) {
        e.printStackTrace()
    }

    // Contents are the same because Reader uses
    // a snapshot of Xof, which was not updated
    // between production of Readers.
    assertContentEquals(out1 + out2, out3 + out4)

    // Still able to update Xof, independent of the production
    // and usage of Readers.
    xof.update(10.toByte())
    xof.use { read(out3); read(out4) }

    try {
        assertContentEquals(out1 + out2, out3 + out4)
        throw IllegalStateException()
    } catch (_: AssertionError) {
        // pass
    }
}

Get Started

The best way to keep KotlinCrypto dependencies up to date is by using the version-catalog. Alternatively, see below.

// build.gradle.kts
dependencies {
    val core = "0.5.1"
    implementation("org.kotlincrypto.core:digest:$core")
    implementation("org.kotlincrypto.core:mac:$core")
    implementation("org.kotlincrypto.core:xof:$core")
}
// build.gradle
dependencies {
    def core = "0.5.1"
    implementation "org.kotlincrypto.core:digest:$core"
    implementation "org.kotlincrypto.core:mac:$core"
    implementation "org.kotlincrypto.core:xof:$core"
}