Added util jna
This commit is contained in:
parent
625a3e1f91
commit
531deb6a57
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user