Added secretbox functions and constants

This commit is contained in:
Ugljesa Jovanovic 2020-08-29 21:53:17 +02:00 committed by Ugljesa Jovanovic
parent 555b69f15c
commit 99b9ee5e9d
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
14 changed files with 464 additions and 32 deletions

View File

@ -61,8 +61,9 @@ object Deps {
val serialization = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:${Versions.kotlinSerialization}"
object Npm {
val libsodium = Pair("libsodium-wrappers-sumo", "0.7.6")
val libsodiumWrappers = Pair("libsodium-wrappers-sumo", "file:${getProjectPath()}/multiplatform-crypto-delegated/libsodium-wrappers-sumo-0.7.6.tgz")
val libsodium = Pair("libsodium-wrappers-sumo", "0.7.8")
//val libsodiumWrappers = Pair("libsodium-wrappers-sumo", "file:${getProjectPath()}/multiplatform-crypto-delegated/libsodium-wrappers-sumo-0.7.6.tgz")
val libsodiumWrappers = Pair("libsodium-wrappers-sumo", "0.7.8")
}
}

View File

@ -0,0 +1,30 @@
package com.ionspin.kotlin.crypto.secretbox
import kotlin.js.JsName
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 29-Aug-2020
*/
val crypto_secretbox_KEYBYTES = 32
val crypto_secretbox_MACBYTES = 16
val crypto_secretbox_NONCEBYTES = 24
class SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey() : RuntimeException("MAC validation failed. Data is corrupted or tampered with.")
data class SecretBoxEncryptedDataAndTag(
@JsName("data")
val data: UByteArray, val tag: UByteArray)
expect object SecretBox {
fun easy(message : UByteArray, nonce: UByteArray, key: UByteArray) : UByteArray
fun openEasy(ciphertext: UByteArray, nonce: UByteArray, key: UByteArray) : UByteArray
fun detached(message: UByteArray, nonce: UByteArray, key: UByteArray) : SecretBoxEncryptedDataAndTag
fun openDetached(ciphertext: UByteArray, tag: UByteArray, nonce: UByteArray, key: UByteArray) : UByteArray
fun keygen() : UByteArray
}

View File

@ -4,6 +4,8 @@ package com.ionspin.kotlin.crypto.secretstream
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 26-Aug-2020
*
* This file is named with Jvm suffix because of https://youtrack.jetbrains.com/issue/KT-21186
*/
expect class SecretStreamState
@ -20,6 +22,8 @@ val crypto_secretstream_xchacha20poly1305_HEADERBYTES = 24
val crypto_secretstream_xchacha20poly1305_KEYBYTES = 32
val crypto_secretstream_xchacha20poly1305_ABYTES = 17
class SecretStreamCorrupedOrTamperedDataException() : RuntimeException("MAC validation failed. Data is corrupted or tampered with.")
expect object SecretStream {
fun xChaCha20Poly1305InitPush(key: UByteArray) : SecretStreamStateAndHeader

View File

@ -0,0 +1,83 @@
package com.ionspin.kotlin.crypto.secretbox
import com.ionspin.kotlin.crypto.LibsodiumInitializer
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import com.ionspin.kotlin.crypto.util.toHexString
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 29-Aug-2020
*/
class SecretBoxTest {
@Test
fun secretBoxTestEasy() {
LibsodiumInitializer.initializeWithCallback {
val message = ("Ladies and Gentlemen of the class of '99: If I could offer you " +
"only one tip for the future, sunscreen would be it.").encodeToUByteArray()
val key = ubyteArrayOf(
0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U,
0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU,
0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U,
0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU,
)
val nonce = ubyteArrayOf(
0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U,
0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU,
0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U,
)
val encrypted = SecretBox.easy(message, nonce, key)
val decrypted = SecretBox.openEasy(encrypted, nonce, key)
assertTrue { decrypted.contentEquals(message) }
assertFailsWith(SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey::class) {
val tamperedTag = encrypted.copyOf()
tamperedTag[2] = 0U
SecretBox.openEasy(tamperedTag, nonce, key)
}
}
}
@Test
fun secretBoxTestDetached() {
LibsodiumInitializer.initializeWithCallback {
val message = ("Ladies and Gentlemen of the class of '99: If I could offer you " +
"only one tip for the future, sunscreen would be it.").encodeToUByteArray()
val key = ubyteArrayOf(
0x80U, 0x81U, 0x82U, 0x83U, 0x84U, 0x85U, 0x86U, 0x87U,
0x88U, 0x89U, 0x8aU, 0x8bU, 0x8cU, 0x8dU, 0x8eU, 0x8fU,
0x90U, 0x91U, 0x92U, 0x93U, 0x94U, 0x95U, 0x96U, 0x97U,
0x98U, 0x99U, 0x9aU, 0x9bU, 0x9cU, 0x9dU, 0x9eU, 0x9fU,
)
val nonce = ubyteArrayOf(
0x40U, 0x41U, 0x42U, 0x43U, 0x44U, 0x45U, 0x46U, 0x47U,
0x48U, 0x49U, 0x4aU, 0x4bU, 0x4cU, 0x4dU, 0x4eU, 0x4fU,
0x50U, 0x51U, 0x52U, 0x53U, 0x54U, 0x55U, 0x56U, 0x57U,
)
println("Debug")
val encrypted = SecretBox.detached(message, nonce, key)
val decrypted = SecretBox.openDetached(encrypted.data, encrypted.tag, nonce, key)
assertTrue { decrypted.contentEquals(message) }
assertFailsWith(SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey::class) {
val tamperedTag = encrypted.tag.copyOf()
tamperedTag[2] = 0U
SecretBox.openDetached(encrypted.data, tamperedTag, nonce, key)
}
}
}
}

View File

@ -5,6 +5,7 @@ import com.ionspin.kotlin.crypto.LibsodiumInitializer
import com.ionspin.kotlin.crypto.util.encodeToUByteArray
import com.ionspin.kotlin.crypto.util.testBlocking
import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
/**
@ -71,7 +72,12 @@ class SecretStreamTest {
decrypted.decryptedData.hexColumsPrint()
assertTrue {
decrypted.decryptedData.contentEquals(message)
}
assertFailsWith(SecretStreamCorrupedOrTamperedDataException::class) {
encrypted[encrypted.size - 5] = 0U
val decryptState = SecretStream.xChaCha20Poly1305InitPull(key, stateAndHeader.header)
val decrypted =
SecretStream.xChaCha20Poly1305Pull(decryptState.state, encrypted, ubyteArrayOf())
}
@ -80,7 +86,8 @@ class SecretStreamTest {
}
// TODO modify nonce in state so we can have reproducible tests, theres already a similar way of doing this
// in crypto delegated project XChaCha20Poly1305 test
expect fun modifyState(state: SecretStreamState, forceNonce: UByteArray)

View File

@ -61,6 +61,16 @@ interface JsSodiumInterface {
fun crypto_secretstream_xchacha20poly1305_keygen() : Uint8Array
fun crypto_secretstream_xchacha20poly1305_rekey(state: dynamic)
// ---- SecretBox ----
fun crypto_secretbox_detached(message: Uint8Array, nonce: Uint8Array, key: Uint8Array) : dynamic
fun crypto_secretbox_easy(message: Uint8Array, nonce: Uint8Array, key: Uint8Array) : Uint8Array
fun crypto_secretbox_keygen() : Uint8Array
fun crypto_secretbox_open_detached(ciphertext : Uint8Array, tag : Uint8Array, nonce: Uint8Array, key: Uint8Array) : dynamic
fun crypto_secretbox_open_easy(ciphertext : Uint8Array, nonce: Uint8Array, key: Uint8Array) : dynamic
// ---- SecretBox End ----
//util
fun memzero(array: Uint8Array)

View File

@ -0,0 +1,74 @@
package com.ionspin.kotlin.crypto.secretbox
import com.ionspin.kotlin.crypto.getSodium
import ext.libsodium.com.ionspin.kotlin.crypto.toUByteArray
import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array
import org.khronos.webgl.Uint8Array
actual object SecretBox {
actual fun easy(message: UByteArray, nonce: UByteArray, key: UByteArray): UByteArray {
return getSodium().crypto_secretbox_easy(
message.toUInt8Array(),
nonce.toUInt8Array(),
key.toUInt8Array()
).toUByteArray()
}
actual fun openEasy(
ciphertext: UByteArray,
nonce: UByteArray,
key: UByteArray
): UByteArray {
try {
val decryptionResult = getSodium().crypto_secretbox_open_easy(
ciphertext.toUInt8Array(),
nonce.toUInt8Array(),
key.toUInt8Array()
)
return (decryptionResult as Uint8Array).toUByteArray()
} catch (error: Throwable) {
throw SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey()
}
}
actual fun detached(
message: UByteArray,
nonce: UByteArray,
key: UByteArray
): SecretBoxEncryptedDataAndTag {
val result = getSodium().crypto_secretbox_detached(
message.toUInt8Array(),
nonce.toUInt8Array(),
key.toUInt8Array()
)
return SecretBoxEncryptedDataAndTag(
(result.cipher as Uint8Array).toUByteArray(),
(result.mac as Uint8Array).toUByteArray()
)
}
actual fun openDetached(
ciphertext: UByteArray,
tag: UByteArray,
nonce: UByteArray,
key: UByteArray
): UByteArray {
try {
val decryptionResult = getSodium().crypto_secretbox_open_detached(
ciphertext.toUInt8Array(),
tag.toUInt8Array(),
nonce.toUInt8Array(),
key.toUInt8Array()
)
return (decryptionResult as Uint8Array).toUByteArray()
} catch (error: Throwable) {
throw SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey()
}
}
actual fun keygen(): UByteArray {
return getSodium().crypto_secretbox_keygen().toUByteArray()
}
}

View File

@ -40,6 +40,9 @@ actual object SecretStream {
val dataAndTag = getSodium().crypto_secretstream_xchacha20poly1305_pull(
state, ciphertext.toUInt8Array(), additionalData.toUInt8Array()
)
if (dataAndTag == false) {
throw SecretStreamCorrupedOrTamperedDataException()
}
return DecryptedDataAndTag((dataAndTag.message as Uint8Array).toUByteArray(), dataAndTag.tag)
}

View File

@ -0,0 +1,82 @@
package com.ionspin.kotlin.crypto.secretbox
import com.ionspin.kotlin.crypto.LibsodiumInitializer.sodium
actual object SecretBox {
actual fun easy(message: UByteArray, nonce: UByteArray, key: UByteArray): UByteArray {
val ciphertext = UByteArray(message.size + crypto_secretbox_MACBYTES)
sodium.crypto_secretbox_easy(
ciphertext.asByteArray(),
message.asByteArray(),
message.size.toLong(),
nonce.asByteArray(),
key.asByteArray()
)
return ciphertext
}
actual fun openEasy(
ciphertext: UByteArray,
nonce: UByteArray,
key: UByteArray
): UByteArray {
val decrypted = UByteArray(ciphertext.size - crypto_secretbox_MACBYTES)
val validationResult = sodium.crypto_secretbox_open_easy(
decrypted.asByteArray(),
ciphertext.asByteArray(),
ciphertext.size.toLong(),
nonce.asByteArray(),
key.asByteArray()
)
if (validationResult != 0) {
throw SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey()
}
return decrypted
}
actual fun detached(
message: UByteArray,
nonce: UByteArray,
key: UByteArray
): SecretBoxEncryptedDataAndTag {
val ciphertext = UByteArray(message.size)
val authenticationTag = UByteArray(crypto_secretbox_MACBYTES)
sodium.crypto_secretbox_detached(
ciphertext.asByteArray(),
authenticationTag.asByteArray(),
message.asByteArray(),
message.size.toLong(),
nonce.asByteArray(),
key.asByteArray()
)
return SecretBoxEncryptedDataAndTag(ciphertext, authenticationTag)
}
actual fun openDetached(
ciphertext: UByteArray,
tag: UByteArray,
nonce: UByteArray,
key: UByteArray
): UByteArray {
val message = UByteArray(ciphertext.size)
val validationResult = sodium.crypto_secretbox_open_detached(
message.asByteArray(),
ciphertext.asByteArray(),
tag.asByteArray(),
ciphertext.size.toLong(),
nonce.asByteArray(),
key.asByteArray()
)
if (validationResult != 0) {
throw SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey()
}
return message
}
actual fun keygen() : UByteArray {
val generatedKey = UByteArray(crypto_secretbox_KEYBYTES)
sodium.crypto_secretbox_keygen(generatedKey.asByteArray())
return generatedKey
}
}

View File

@ -49,7 +49,7 @@ actual object SecretStream {
): DecryptedDataAndTag {
val result = UByteArray(ciphertext.size - crypto_secretstream_xchacha20poly1305_ABYTES)
val tagArray = UByteArray(1) { 0U }
sodium.crypto_secretstream_xchacha20poly1305_pull(
val validationResult = sodium.crypto_secretstream_xchacha20poly1305_pull(
state,
result.asByteArray(),
null,
@ -59,11 +59,14 @@ actual object SecretStream {
additionalData.asByteArray(),
additionalData.size.toLong()
)
if (validationResult != 0) {
throw SecretStreamCorrupedOrTamperedDataException()
}
return DecryptedDataAndTag(result, tagArray[0])
}
actual fun xChaCha20Poly1305Keygen(): UByteArray {
val generatedKey = UByteArray(crypto_aead_xchacha20poly1305_ietf_KEYBYTES)
val generatedKey = UByteArray(crypto_secretstream_xchacha20poly1305_KEYBYTES)
sodium.crypto_secretstream_xchacha20poly1305_keygen(generatedKey.asByteArray())
return generatedKey
}

View File

@ -0,0 +1,127 @@
package com.ionspin.kotlin.crypto.secretbox
import com.ionspin.kotlin.crypto.secretstream.SecretStreamCorrupedOrTamperedDataException
import com.ionspin.kotlin.crypto.util.toPtr
import kotlinx.cinterop.convert
import kotlinx.cinterop.pin
import libsodium.crypto_secretbox_detached
import libsodium.crypto_secretbox_easy
import libsodium.crypto_secretbox_keygen
import libsodium.crypto_secretbox_open_detached
import libsodium.crypto_secretbox_open_easy
actual object SecretBox {
actual fun easy(message: UByteArray, nonce: UByteArray, key: UByteArray): UByteArray {
val ciphertext = UByteArray(message.size + crypto_secretbox_MACBYTES)
val ciphertextPinned = ciphertext.pin()
val messagePinned = message.pin()
val noncePinned = nonce.pin()
val keyPinned = key.pin()
crypto_secretbox_easy(
ciphertextPinned.toPtr(),
messagePinned.toPtr(),
message.size.convert(),
noncePinned.toPtr(),
keyPinned.toPtr()
)
ciphertextPinned.unpin()
messagePinned.unpin()
noncePinned.unpin()
keyPinned.unpin()
return ciphertext
}
actual fun openEasy(
ciphertext: UByteArray,
nonce: UByteArray,
key: UByteArray
): UByteArray {
val message = UByteArray(ciphertext.size - crypto_secretbox_MACBYTES)
val messagePinned = message.pin()
val ciphertextPinned = ciphertext.pin()
val noncePinned = nonce.pin()
val keyPinned = key.pin()
val verificationResult = crypto_secretbox_open_easy(
messagePinned.toPtr(),
ciphertextPinned.toPtr(),
ciphertext.size.convert(),
noncePinned.toPtr(),
keyPinned.toPtr()
)
ciphertextPinned.unpin()
messagePinned.unpin()
noncePinned.unpin()
keyPinned.unpin()
if (verificationResult != 0) {
throw SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey()
}
return message
}
actual fun detached(
message: UByteArray,
nonce: UByteArray,
key: UByteArray
): SecretBoxEncryptedDataAndTag {
val ciphertext = UByteArray(message.size)
val authenticationTag = UByteArray(crypto_secretbox_MACBYTES)
val ciphertextPinned = ciphertext.pin()
val authenticationTagPinned = authenticationTag.pin()
val messagePinned = message.pin()
val noncePinned = nonce.pin()
val keyPinned = key.pin()
crypto_secretbox_detached(
ciphertextPinned.toPtr(),
authenticationTagPinned.toPtr(),
messagePinned.toPtr(),
message.size.convert(),
noncePinned.toPtr(),
keyPinned.toPtr()
)
ciphertextPinned.unpin()
authenticationTagPinned.unpin()
messagePinned.unpin()
noncePinned.unpin()
keyPinned.unpin()
return SecretBoxEncryptedDataAndTag(ciphertext, authenticationTag)
}
actual fun openDetached(
ciphertext: UByteArray,
tag: UByteArray,
nonce: UByteArray,
key: UByteArray
): UByteArray {
val message = UByteArray(ciphertext.size)
val messagePinned = message.pin()
val ciphertextPinned = ciphertext.pin()
val tagPinned = tag.pin()
val noncePinned = nonce.pin()
val keyPinned = key.pin()
val verificationResult = crypto_secretbox_open_detached(
messagePinned.toPtr(),
ciphertextPinned.toPtr(),
tagPinned.toPtr(),
ciphertext.size.convert(),
noncePinned.toPtr(),
keyPinned.toPtr()
)
ciphertextPinned.unpin()
messagePinned.unpin()
noncePinned.unpin()
keyPinned.unpin()
if (verificationResult != 0) {
throw SecretBoxCorruptedOrTamperedDataExceptionOrInvalidKey()
}
return message
}
actual fun keygen() :UByteArray {
val generatedKey = UByteArray(crypto_secretbox_KEYBYTES)
val generatedKeyPinned = generatedKey.pin()
crypto_secretbox_keygen(generatedKeyPinned.toPtr())
generatedKeyPinned.unpin()
return generatedKey
}
}

View File

@ -101,7 +101,7 @@ actual object SecretStream {
}
val tag = UByteArray(1) { 0U }
val tagPinned = tag.pin()
val validTag = crypto_secretstream_xchacha20poly1305_pull(
val validationResult = crypto_secretstream_xchacha20poly1305_pull(
state.ptr,
messagePinned.toPtr(),
null,
@ -115,8 +115,8 @@ actual object SecretStream {
messagePinned.unpin()
additionalDataPinned?.unpin()
tagPinned.unpin()
if (validTag != 0) {
throw RuntimeException("Invalid tag")
if (validationResult != 0) {
throw SecretStreamCorrupedOrTamperedDataException()
}
return DecryptedDataAndTag(message, tag[0])
}

View File

@ -0,0 +1,8 @@
package com.ionspin.kotlin.crypto.util
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 29-Aug-2020
*/
//fun randomBytesBuf()

View File

@ -102,19 +102,19 @@
| crypto_scalarmult_base | |
| crypto_scalarmult_ristretto255 | |
| crypto_scalarmult_ristretto255_base | |
| crypto_secretbox_detached | |
| crypto_secretbox_easy | |
| crypto_secretbox_keygen | |
| crypto_secretbox_open_detached | |
| crypto_secretbox_open_easy | |
| crypto_secretstream_xchacha20poly1305_init_pull | :heavy_check_mark: | DONE
| crypto_secretstream_xchacha20poly1305_init_push | :heavy_check_mark: | DONE
| crypto_secretstream_xchacha20poly1305_keygen | :heavy_check_mark: | DONE
| crypto_secretstream_xchacha20poly1305_pull | :heavy_check_mark: | DONE
| crypto_secretstream_xchacha20poly1305_push | :heavy_check_mark: | DONE
| crypto_secretstream_xchacha20poly1305_rekey | :heavy_check_mark: | DONE
| crypto_shorthash |:heavy_check_mark: | DONE
| crypto_shorthash_keygen | :heavy_check_mark: | DONE
| crypto_secretbox_detached | :heavy_check_mark: |
| crypto_secretbox_easy | :heavy_check_mark: |
| crypto_secretbox_keygen | :heavy_check_mark: |
| crypto_secretbox_open_detached | :heavy_check_mark: |
| crypto_secretbox_open_easy | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_init_pull | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_init_push | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_keygen | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_pull | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_push | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_rekey | :heavy_check_mark: |
| crypto_shorthash |:heavy_check_mark: |
| crypto_shorthash_keygen | :heavy_check_mark: |
| crypto_shorthash_siphashx24 | |
| crypto_sign | |
| crypto_sign_detached | |
@ -310,10 +310,10 @@
| crypto_scalarmult_ed25519_SCALARBYTES | |
| crypto_scalarmult_ristretto255_BYTES | |
| crypto_scalarmult_ristretto255_SCALARBYTES | |
| crypto_secretbox_KEYBYTES | |
| crypto_secretbox_MACBYTES | |
| crypto_secretbox_KEYBYTES | :heavy_check_mark: |
| crypto_secretbox_MACBYTES | :heavy_check_mark: |
| crypto_secretbox_MESSAGEBYTES_MAX | |
| crypto_secretbox_NONCEBYTES | |
| crypto_secretbox_NONCEBYTES | :heavy_check_mark: |
| crypto_secretbox_xchacha20poly1305_KEYBYTES | |
| crypto_secretbox_xchacha20poly1305_MACBYTES | |
| crypto_secretbox_xchacha20poly1305_MESSAGEBYTES_MAX | |
@ -322,14 +322,14 @@
| crypto_secretbox_xsalsa20poly1305_MACBYTES | |
| crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX | |
| crypto_secretbox_xsalsa20poly1305_NONCEBYTES | |
| crypto_secretstream_xchacha20poly1305_ABYTES | |
| crypto_secretstream_xchacha20poly1305_HEADERBYTES | |
| crypto_secretstream_xchacha20poly1305_KEYBYTES | |
| crypto_secretstream_xchacha20poly1305_ABYTES | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_HEADERBYTES | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_KEYBYTES | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX | |
| crypto_secretstream_xchacha20poly1305_TAG_FINAL | |
| crypto_secretstream_xchacha20poly1305_TAG_MESSAGE | |
| crypto_secretstream_xchacha20poly1305_TAG_PUSH | |
| crypto_secretstream_xchacha20poly1305_TAG_REKEY | |
| crypto_secretstream_xchacha20poly1305_TAG_FINAL | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_TAG_MESSAGE | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_TAG_PUSH | :heavy_check_mark: |
| crypto_secretstream_xchacha20poly1305_TAG_REKEY | :heavy_check_mark: |
| crypto_shorthash_BYTES | |
| crypto_shorthash_KEYBYTES | |
| crypto_shorthash_siphash24_BYTES | |