Completed jvm implementation, there are some issues in lazysodium-java that I've worked around
This commit is contained in:
parent
4af1477c90
commit
2d63215c70
@ -56,6 +56,22 @@ class LibsodiumUtilTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testPaddingChars() {
|
||||||
|
LibsodiumInitializer.initializeWithCallback {
|
||||||
|
val input = charArrayOf('a', 'b', 'c', 'd').map { it.toByte().toUByte() }.toUByteArray()
|
||||||
|
val blocksize = 4
|
||||||
|
val padded = LibsodiumUtil.pad(input, blocksize)
|
||||||
|
println(padded.hexColumsPrint())
|
||||||
|
val unpadded = LibsodiumUtil.unpad(padded, blocksize)
|
||||||
|
println(unpadded.hexColumsPrint())
|
||||||
|
|
||||||
|
assertTrue {
|
||||||
|
input.contentEquals(unpadded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testPaddingAligned() {
|
fun testPaddingAligned() {
|
||||||
LibsodiumInitializer.initializeWithCallback {
|
LibsodiumInitializer.initializeWithCallback {
|
||||||
|
@ -11,9 +11,9 @@ actual object LibsodiumUtil {
|
|||||||
|
|
||||||
actual fun memzero(target: UByteArray) {
|
actual fun memzero(target: UByteArray) {
|
||||||
// libsodium.js does this as well, and theres no clear way at the moment of casting ubytearray to uint8array
|
// libsodium.js does this as well, and theres no clear way at the moment of casting ubytearray to uint8array
|
||||||
//although I feel like there should be a way to work around it
|
// although I feel like there should be a way to work around it
|
||||||
target.forEachIndexed {
|
(target.indices).forEach {
|
||||||
index, _ -> target[index] = 0U
|
index -> target[index] = 0U
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,166 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.util
|
||||||
|
|
||||||
|
import com.goterl.lazycode.lazysodium.LazySodiumJava
|
||||||
|
import com.ionspin.kotlin.crypto.LibsodiumInitializer.sodium
|
||||||
|
import com.sun.jna.ptr.IntByReference
|
||||||
|
import java.lang.RuntimeException
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
actual object LibsodiumUtil {
|
||||||
|
actual fun memcmp(first: UByteArray, second: UByteArray): Boolean {
|
||||||
|
if (first.size != second.size) {
|
||||||
|
throw RuntimeException("Sodium memcmp() only supports comparing same length arrays")
|
||||||
|
}
|
||||||
|
return sodium.sodium_memcmp(first.asByteArray(), second.asByteArray(), first.size) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun memzero(target: UByteArray) {
|
||||||
|
sodium.sodium_memzero(target.asByteArray(), target.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun pad(unpaddedData: UByteArray, blocksize: Int): UByteArray {
|
||||||
|
|
||||||
|
// Pad is invalid in lazysodium-java because it uses char arrays, which are 2 bytes in java
|
||||||
|
// while libsodium expects 1 byte char array
|
||||||
|
// See https://github.com/terl/lazysodium-java/issues/85
|
||||||
|
|
||||||
|
//This is temporary solution until lazysodium is fixed
|
||||||
|
if (blocksize == 0) {
|
||||||
|
throw RuntimeException("Invalid block size: $blocksize")
|
||||||
|
}
|
||||||
|
val padAmount = blocksize - (unpaddedData.size % blocksize)
|
||||||
|
val result = if (padAmount == blocksize) {
|
||||||
|
unpaddedData + UByteArray(blocksize) {
|
||||||
|
when (it) {
|
||||||
|
0 -> 0x80U
|
||||||
|
else -> 0x00U
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unpaddedData + UByteArray(padAmount) {
|
||||||
|
when (it) {
|
||||||
|
0 -> 0x80U
|
||||||
|
else -> 0x00U
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
// val newPadSizeReference = IntByReference(0)
|
||||||
|
// val newSize = ((unpaddedData.size / blocksize) + 1) * blocksize
|
||||||
|
// val charArray = CharArray(newSize) {
|
||||||
|
// if (it < unpaddedData.size) {
|
||||||
|
// unpaddedData[it].toByte().toChar()
|
||||||
|
// } else {
|
||||||
|
// '\u0000'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// sodium.sodium_pad(
|
||||||
|
// newPadSizeReference.pointer,
|
||||||
|
// charArray,
|
||||||
|
// unpaddedData.size + 1,
|
||||||
|
// blocksize,
|
||||||
|
// newSize
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// return charArray.slice(0 until newPadSizeReference.value).map { it.toByte().toUByte()}.toUByteArray()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun unpad(paddedData: UByteArray, blocksize: Int): UByteArray {
|
||||||
|
|
||||||
|
// Pad is invalid in lazysodium-java because it uses char arrays, which are 2 bytes in java
|
||||||
|
// while libsodium expects 1 byte char array
|
||||||
|
// See https://github.com/terl/lazysodium-java/issues/85
|
||||||
|
|
||||||
|
//This is temporary solution until lazysodium is fixed
|
||||||
|
|
||||||
|
val unpaddedData = paddedData.dropLastWhile { it == 0U.toUByte() }
|
||||||
|
if (unpaddedData.last() != 0x80U.toUByte()) {
|
||||||
|
throw RuntimeException("Invalid padding!")
|
||||||
|
}
|
||||||
|
return unpaddedData.dropLast(1).toUByteArray()
|
||||||
|
|
||||||
|
|
||||||
|
// val paddedDataCopy = paddedData.copyOf().asByteArray().map { it.toChar() }.toCharArray()
|
||||||
|
// var unpaddedSize = IntByReference(0)
|
||||||
|
//
|
||||||
|
// sodium.sodium_unpad(
|
||||||
|
// unpaddedSize.pointer,
|
||||||
|
// paddedDataCopy,
|
||||||
|
// paddedData.size,
|
||||||
|
// blocksize
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// val unpadded = paddedDataCopy.sliceArray(0 until unpaddedSize.value).map { it.toByte().toUByte() }.toUByteArray()
|
||||||
|
//
|
||||||
|
// return unpadded
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun toBase64(
|
||||||
|
data: UByteArray,
|
||||||
|
variant: Base64Variants
|
||||||
|
): String {
|
||||||
|
val maxlen = sodium.sodium_base64_encoded_len(data.size, variant.value)
|
||||||
|
val result = ByteArray(maxlen) { 0 }
|
||||||
|
sodium.sodium_bin2base64(
|
||||||
|
result,
|
||||||
|
maxlen,
|
||||||
|
data.asByteArray(),
|
||||||
|
data.size,
|
||||||
|
variant.value
|
||||||
|
)
|
||||||
|
//Drop terminating char \0
|
||||||
|
return String(result.sliceArray(0 until result.size - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun toHex(data: UByteArray): String {
|
||||||
|
val hexLen = (data.size * 2) + 1 // +1 for terminator char
|
||||||
|
val result = ByteArray(hexLen)
|
||||||
|
sodium.sodium_bin2hex(
|
||||||
|
result,
|
||||||
|
hexLen,
|
||||||
|
data.asByteArray(),
|
||||||
|
data.size
|
||||||
|
)
|
||||||
|
//Drop terminating char \0
|
||||||
|
return String(result.sliceArray(0 until result.size - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun fromBase64(
|
||||||
|
data: String,
|
||||||
|
variant: Base64Variants
|
||||||
|
): UByteArray {
|
||||||
|
// from base64 is currently broken in lazysodium-java
|
||||||
|
// see https://github.com/terl/lazysodium-java/issues/83
|
||||||
|
// val maxLength = (data.length * 3) / 4
|
||||||
|
// val intermediaryResult = UByteArray(maxLength) { 0U }
|
||||||
|
//
|
||||||
|
// sodium.sodium_base642bin(
|
||||||
|
// intermediaryResult.asByteArray(),
|
||||||
|
// maxLength,
|
||||||
|
// data.encodeToByteArray(),
|
||||||
|
// data.length,
|
||||||
|
// null,
|
||||||
|
// binLenPinned.addressOf(0),
|
||||||
|
// null,
|
||||||
|
// variant.value
|
||||||
|
//
|
||||||
|
// )
|
||||||
|
val decoder = when(variant) {
|
||||||
|
Base64Variants.ORIGINAL -> Base64.getDecoder()
|
||||||
|
Base64Variants.ORIGINAL_NO_PADDING -> Base64.getDecoder()
|
||||||
|
Base64Variants.URLSAFE -> Base64.getUrlDecoder()
|
||||||
|
Base64Variants.URLSAFE_NO_PADDING -> Base64.getUrlDecoder()
|
||||||
|
}
|
||||||
|
return decoder.decode(data).asUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
actual fun fromHex(data: String): UByteArray {
|
||||||
|
// from hex is currently broken in lazysodium-java
|
||||||
|
// see https://github.com/terl/lazysodium-java/issues/83
|
||||||
|
return data.hexStringToUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -40,7 +40,7 @@ actual object LibsodiumUtil {
|
|||||||
val resultingSize = if (unpaddedData.size % blocksize != 0 ) {
|
val resultingSize = if (unpaddedData.size % blocksize != 0 ) {
|
||||||
((unpaddedData.size / blocksize) + 1 ) * blocksize
|
((unpaddedData.size / blocksize) + 1 ) * blocksize
|
||||||
} else {
|
} else {
|
||||||
unpaddedData.size + 1
|
unpaddedData.size + blocksize
|
||||||
}
|
}
|
||||||
val paddedData = UByteArray(resultingSize)
|
val paddedData = UByteArray(resultingSize)
|
||||||
unpaddedData.copyInto(paddedData, 0, 0)
|
unpaddedData.copyInto(paddedData, 0, 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user