Added util jna

This commit is contained in:
Ugljesa Jovanovic 2021-02-21 21:17:33 +01:00
parent 625a3e1f91
commit 531deb6a57
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
5 changed files with 172 additions and 98 deletions

View File

@ -126,6 +126,12 @@ Currently supported native platforms:
|minGW X86 32| :x: | |minGW X86 32| :x: |
### Where do the compiled libraries used by JVM and Android come from
Android .so files come from running dist-build scripts in libsodium which you can find in the libsodium submodule
Java Linux Arm/X86_64 and Mac so and dylib are the same as produced by multiplatform builds, also based on the same submodule commit
Java Windows dll is from https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable-msvc.zip
### TODO: ### TODO:
- Copy/adapt code documentation, currently only some functions have documentation that is a copy-paste from libsodium website - Copy/adapt code documentation, currently only some functions have documentation that is a copy-paste from libsodium website
- Android testing - Android testing

View File

@ -10,6 +10,9 @@ enum class Base64Variants(val value: Int) {
ORIGINAL(1), ORIGINAL_NO_PADDING(3), URLSAFE(5), URLSAFE_NO_PADDING(7) ORIGINAL(1), ORIGINAL_NO_PADDING(3), URLSAFE(5), URLSAFE_NO_PADDING(7)
} }
class ConversionException() : RuntimeException("Conversion failed")
class PaddingException() : RuntimeException("Padding failed")
expect object LibsodiumUtil { expect object LibsodiumUtil {
fun memcmp(first: UByteArray, second: UByteArray) : Boolean fun memcmp(first: UByteArray, second: UByteArray) : Boolean

View File

@ -1,6 +1,7 @@
package com.ionspin.kotlin.crypto package com.ionspin.kotlin.crypto
import com.sun.jna.Library import com.sun.jna.Library
import com.sun.jna.Pointer
import com.sun.jna.Structure import com.sun.jna.Structure
/** /**
@ -61,10 +62,93 @@ class SecretStreamXChaCha20Poly1305State : Structure() {
} }
interface JnaLibsodiumInterface : Library { interface JnaLibsodiumInterface : Library {
// ---- Utils ----
fun sodium_version_string(): String fun sodium_version_string(): String
fun randombytes_buf(buffer: ByteArray, bufferSize: Int) fun randombytes_buf(buffer: ByteArray, bufferSize: Int)
// void sodium_memzero(void * const pnt, const size_t len);
fun sodium_memzero(array: ByteArray, len: Int)
// int sodium_memcmp(const void * const b1_, const void * const b2_, size_t len)
fun sodium_memcmp(b1 : ByteArray, b2 : ByteArray, len: Int) : Int
// char *sodium_bin2hex(char * const hex, const size_t hex_maxlen,
// const unsigned char * const bin, const size_t bin_len)
fun sodium_bin2hex(
hex: ByteArray,
hexMaxlen: Int,
bin: ByteArray,
binLen : Int
) : String
// int sodium_hex2bin(
// unsigned char * const bin, const size_t bin_maxlen,
// const char * const hex, const size_t hex_len,
// const char * const ignore, size_t * const bin_len,
// const char ** const hex_end)
fun sodium_hex2bin(
bin: ByteArray,
binMaxLength: Int,
hex: ByteArray,
hexLen: Int,
ignore: ByteArray?,
binLen: Pointer,
hexEnd: Pointer?
) : Int
// int sodium_pad(size_t *padded_buflen_p, unsigned char *buf,
// size_t unpadded_buflen, size_t blocksize, size_t max_buflen)
fun sodium_pad(
paddedBufferLength : Pointer,
buffer: ByteArray,
unpaddedBufferLength : Int,
blockSize: Int,
maxBufferLength: Int
) : Int
// int sodium_unpad(size_t *unpadded_buflen_p, const unsigned char *buf,
// size_t padded_buflen, size_t blocksize)
fun sodium_unpad(
unpaddedBufferLength: Pointer,
buffer: ByteArray,
paddedBufferLength: Int,
blockSize: Int
)
// char *sodium_bin2base64(char * const b64, const size_t b64_maxlen,
// const unsigned char * const bin, const size_t bin_len,
// const int variant)
fun sodium_bin2base64(
base64: ByteArray,
base64MaxLength: Int,
bin: ByteArray,
binLength: Int,
variant: Int
) : Int
// int sodium_base642bin(
// unsigned char * const bin, const size_t bin_maxlen,
// const char * const b64, const size_t b64_len,
// const char * const ignore, size_t * const bin_len,
// const char ** const b64_end, const int variant)
fun sodium_base642bin(
bin : ByteArray,
binMaxLength: Int,
base64: ByteArray,
base64Length: Int,
ignore: ByteArray?,
binLength: Pointer,
base64End: Pointer?,
variant: Int
) : Int
// size_t sodium_base64_encoded_len(const size_t bin_len, const int variant)
fun sodium_base64_encoded_len(binLength: Int, variant: Int) : Int
// --- Utils end ----
// int crypto_generichash(unsigned char *out, size_t outlen, // int crypto_generichash(unsigned char *out, size_t outlen,
// const unsigned char *in, unsigned long long inlen, // const unsigned char *in, unsigned long long inlen,
// const unsigned char *key, size_t keylen) // const unsigned char *key, size_t keylen)

View File

@ -1,6 +1,7 @@
package com.ionspin.kotlin.crypto.util package com.ionspin.kotlin.crypto.util
import com.ionspin.kotlin.crypto.LibsodiumInitializer.sodium import com.ionspin.kotlin.crypto.LibsodiumInitializer.sodiumJna
import com.sun.jna.ptr.IntByReference
import java.lang.RuntimeException import java.lang.RuntimeException
import java.util.* import java.util.*
@ -9,100 +10,63 @@ actual object LibsodiumUtil {
if (first.size != second.size) { if (first.size != second.size) {
throw RuntimeException("Sodium memcmp() only supports comparing same length arrays") throw RuntimeException("Sodium memcmp() only supports comparing same length arrays")
} }
return sodium.sodium_memcmp(first.asByteArray(), second.asByteArray(), first.size) == 0 return sodiumJna.sodium_memcmp(first.asByteArray(), second.asByteArray(), first.size) == 0
} }
actual fun memzero(target: UByteArray) { actual fun memzero(target: UByteArray) {
sodium.sodium_memzero(target.asByteArray(), target.size) sodiumJna.sodium_memzero(target.asByteArray(), target.size)
} }
actual fun pad(unpaddedData: UByteArray, blocksize: Int): UByteArray { 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 val newPadSizeReference = IntByReference(0)
// while libsodium expects 1 byte char array val newSize = ((unpaddedData.size / blocksize) + 1) * blocksize
// See https://github.com/terl/lazysodium-java/issues/85 val paddedArray = UByteArray(newSize) {
if (it < unpaddedData.size) {
//This is temporary solution until lazysodium is fixed unpaddedData[it]
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 { } else {
unpaddedData + UByteArray(padAmount) { 0U
when (it) {
0 -> 0x80U
else -> 0x00U
} }
} }
val resultCode = sodiumJna.sodium_pad(
newPadSizeReference.pointer,
paddedArray.asByteArray(),
unpaddedData.size,
blocksize,
newSize
)
if (resultCode != 0) {
throw RuntimeException("Padding failed")
} }
return result return paddedArray.slice(0 until newPadSizeReference.value).map { it.toByte().toUByte()}.toUByteArray()
// 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 { 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 val paddedDataCopy = paddedData.copyOf().asByteArray()
// while libsodium expects 1 byte char array var unpaddedSize = IntByReference(0)
// See https://github.com/terl/lazysodium-java/issues/85
//This is temporary solution until lazysodium is fixed sodiumJna.sodium_unpad(
unpaddedSize.pointer,
paddedDataCopy,
paddedData.size,
blocksize
)
val unpaddedData = paddedData.dropLastWhile { it == 0U.toUByte() } val unpadded = paddedDataCopy.sliceArray(0 until unpaddedSize.value).asUByteArray()
if (unpaddedData.last() != 0x80U.toUByte()) {
throw RuntimeException("Invalid padding!")
}
return unpaddedData.dropLast(1).toUByteArray()
return unpadded
// 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( actual fun toBase64(
data: UByteArray, data: UByteArray,
variant: Base64Variants variant: Base64Variants
): String { ): String {
val maxlen = sodium.sodium_base64_encoded_len(data.size, variant.value) val maxlen = sodiumJna.sodium_base64_encoded_len(data.size, variant.value)
val result = ByteArray(maxlen) { 0 } val result = ByteArray(maxlen) { 0 }
sodium.sodium_bin2base64( sodiumJna.sodium_bin2base64(
result, result,
maxlen, maxlen,
data.asByteArray(), data.asByteArray(),
@ -116,49 +80,66 @@ actual object LibsodiumUtil {
actual fun toHex(data: UByteArray): String { actual fun toHex(data: UByteArray): String {
val hexLen = (data.size * 2) + 1 // +1 for terminator char val hexLen = (data.size * 2) + 1 // +1 for terminator char
val result = ByteArray(hexLen) val result = ByteArray(hexLen)
sodium.sodium_bin2hex( sodiumJna.sodium_bin2hex(
result, result,
hexLen, hexLen,
data.asByteArray(), data.asByteArray(),
data.size data.size
) )
//Drop terminating char \0 //Drop terminating char \0
return String(result.sliceArray(0 until result.size - 1)) return String(result.sliceArray(0 until result.size - 1))
} }
actual fun fromHex(data: String): UByteArray {
val binLenReference = IntByReference(0)
val binSize = (data.length + 1) / 2 // -1 for terminator char
val hex = data.toCharArray().map { it.toByte() }.toByteArray()
val result = ByteArray(binSize)
val resultCode = sodiumJna.sodium_hex2bin(
result,
binSize,
hex,
hex.size,
null,
binLenReference.pointer,
null
)
if (resultCode != 0) {
throw ConversionException()
}
return result.slice(0 until binLenReference.value).toByteArray().asUByteArray()
}
actual fun fromBase64( actual fun fromBase64(
data: String, data: String,
variant: Base64Variants variant: Base64Variants
): UByteArray { ): UByteArray {
// from base64 is currently broken in lazysodium-java val binLengthReference = IntByReference(0)
// see https://github.com/terl/lazysodium-java/issues/83 val maxLength = (data.length * 3) / 4
// val maxLength = (data.length * 3) / 4 val intermediaryResult = UByteArray(maxLength) { 0U }
// val intermediaryResult = UByteArray(maxLength) { 0U }
// val resultCode = sodiumJna.sodium_base642bin(
// sodium.sodium_base642bin( intermediaryResult.asByteArray(),
// intermediaryResult.asByteArray(), maxLength,
// maxLength, data.encodeToByteArray(),
// data.encodeToByteArray(), data.length,
// data.length, null,
// null, binLengthReference.pointer,
// binLenPinned.addressOf(0), null,
// null, variant.value
// variant.value )
//
// ) if (resultCode != 0) {
val decoder = when(variant) { throw ConversionException()
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 { return intermediaryResult.sliceArray(0 until binLengthReference.value)
// from hex is currently broken in lazysodium-java
// see https://github.com/terl/lazysodium-java/issues/83
return data.hexStringToUByteArray()
} }
} }