Added util jna
This commit is contained in:
parent
625a3e1f91
commit
531deb6a57
@ -126,6 +126,12 @@ Currently supported native platforms:
|
||||
|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:
|
||||
- Copy/adapt code documentation, currently only some functions have documentation that is a copy-paste from libsodium website
|
||||
- Android testing
|
||||
|
@ -10,6 +10,9 @@ enum class Base64Variants(val value: Int) {
|
||||
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 {
|
||||
|
||||
fun memcmp(first: UByteArray, second: UByteArray) : Boolean
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.ionspin.kotlin.crypto
|
||||
|
||||
import com.sun.jna.Library
|
||||
import com.sun.jna.Pointer
|
||||
import com.sun.jna.Structure
|
||||
|
||||
/**
|
||||
@ -61,10 +62,93 @@ class SecretStreamXChaCha20Poly1305State : Structure() {
|
||||
}
|
||||
|
||||
interface JnaLibsodiumInterface : Library {
|
||||
|
||||
// ---- Utils ----
|
||||
fun sodium_version_string(): String
|
||||
|
||||
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,
|
||||
// const unsigned char *in, unsigned long long inlen,
|
||||
// const unsigned char *key, size_t keylen)
|
||||
|
@ -1,6 +1,7 @@
|
||||
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.util.*
|
||||
|
||||
@ -9,100 +10,63 @@ actual object LibsodiumUtil {
|
||||
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
|
||||
return sodiumJna.sodium_memcmp(first.asByteArray(), second.asByteArray(), first.size) == 0
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
val newPadSizeReference = IntByReference(0)
|
||||
val newSize = ((unpaddedData.size / blocksize) + 1) * blocksize
|
||||
val paddedArray = UByteArray(newSize) {
|
||||
if (it < unpaddedData.size) {
|
||||
unpaddedData[it]
|
||||
} else {
|
||||
unpaddedData + UByteArray(padAmount) {
|
||||
when (it) {
|
||||
0 -> 0x80U
|
||||
else -> 0x00U
|
||||
0U
|
||||
}
|
||||
}
|
||||
val resultCode = sodiumJna.sodium_pad(
|
||||
newPadSizeReference.pointer,
|
||||
paddedArray.asByteArray(),
|
||||
unpaddedData.size,
|
||||
blocksize,
|
||||
newSize
|
||||
)
|
||||
if (resultCode != 0) {
|
||||
throw RuntimeException("Padding failed")
|
||||
}
|
||||
|
||||
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()
|
||||
return paddedArray.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
|
||||
val paddedDataCopy = paddedData.copyOf().asByteArray()
|
||||
var unpaddedSize = IntByReference(0)
|
||||
|
||||
//This is temporary solution until lazysodium is fixed
|
||||
sodiumJna.sodium_unpad(
|
||||
unpaddedSize.pointer,
|
||||
paddedDataCopy,
|
||||
paddedData.size,
|
||||
blocksize
|
||||
)
|
||||
|
||||
val unpaddedData = paddedData.dropLastWhile { it == 0U.toUByte() }
|
||||
if (unpaddedData.last() != 0x80U.toUByte()) {
|
||||
throw RuntimeException("Invalid padding!")
|
||||
}
|
||||
return unpaddedData.dropLast(1).toUByteArray()
|
||||
val unpadded = paddedDataCopy.sliceArray(0 until unpaddedSize.value).asUByteArray()
|
||||
|
||||
|
||||
// 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
|
||||
return unpadded
|
||||
}
|
||||
|
||||
actual fun toBase64(
|
||||
data: UByteArray,
|
||||
variant: Base64Variants
|
||||
): 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 }
|
||||
sodium.sodium_bin2base64(
|
||||
sodiumJna.sodium_bin2base64(
|
||||
result,
|
||||
maxlen,
|
||||
data.asByteArray(),
|
||||
@ -116,49 +80,66 @@ actual object LibsodiumUtil {
|
||||
actual fun toHex(data: UByteArray): String {
|
||||
val hexLen = (data.size * 2) + 1 // +1 for terminator char
|
||||
val result = ByteArray(hexLen)
|
||||
sodium.sodium_bin2hex(
|
||||
sodiumJna.sodium_bin2hex(
|
||||
result,
|
||||
hexLen,
|
||||
data.asByteArray(),
|
||||
data.size
|
||||
)
|
||||
|
||||
//Drop terminating char \0
|
||||
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(
|
||||
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()
|
||||
val binLengthReference = IntByReference(0)
|
||||
val maxLength = (data.length * 3) / 4
|
||||
val intermediaryResult = UByteArray(maxLength) { 0U }
|
||||
|
||||
val resultCode = sodiumJna.sodium_base642bin(
|
||||
intermediaryResult.asByteArray(),
|
||||
maxLength,
|
||||
data.encodeToByteArray(),
|
||||
data.length,
|
||||
null,
|
||||
binLengthReference.pointer,
|
||||
null,
|
||||
variant.value
|
||||
)
|
||||
|
||||
if (resultCode != 0) {
|
||||
throw ConversionException()
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
return intermediaryResult.sliceArray(0 until binLengthReference.value)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user