Oleg Yukhnevich
02/07/2023, 2:40 PMfun EVP_MAC_CTX_set_params(ctx: CValuesRef<EVP_MAC_CTX>?, params: CValuesRef<OSSL_PARAM>?): <http://kotlin.Int|kotlin.Int>
(params is a reference to an array)
example of usage in C is here https://www.openssl.org/docs/man3.0/man3/EVP_MAC_init.html (in the end of the page).
I’ve tried to something like this, and it compiles and works without errors on runtime, but looks like it doesn’t work as expected, as those params
are not visible by `openssl`…
val mac = EVP_MAC_fetch(null, "HMAC", null)
val context = EVP_MAC_CTX_new(mac)
val params = allocArrayOf(
OSSL_PARAM_construct_utf8_string("digest", "SHA256".cstr, 0).ptr,
OSSL_PARAM_construct_end().ptr
)
check(EVP_MAC_CTX_set_params(context, params[0]) == 1) // works, returns 1
check(EVP_MAC_CTX_get_mac_size(context) > 0) // fails, but should not
The error is error:0300009F:digital envelope routines::message digest is null
- so looks like it don’t see digest
key in params
array for some reason
Where I could dig to understand what I do wrong? Thx in advance! 🙂ephemient
02/07/2023, 2:50 PMcstr
doesn't live long enough. OSSL_PARAM_construct_utf8_string
doesn't copy it, it holds onto the pointerOleg Yukhnevich
02/07/2023, 3:10 PMOleg Yukhnevich
02/07/2023, 4:48 PMEVP_MAC_CTX_set_params
):
static inline int hmac_init(EVP_MAC_CTX *ctx, const unsigned char *key, size_t keylen, const char *digest_name) {
OSSL_PARAM params[4], *p = params;
*p++ = OSSL_PARAM_construct_utf8_string("digest", digest_name, 0);
*p = OSSL_PARAM_construct_end();
return EVP_MAC_init(ctx, key, keylen, params);
}
and using like:
hmac_init(context, key.asUByteArray().refTo(0), key.size.convert(), hashAlgorithm)
it works as expected
While I can leave with it, I literally don’t understand, why it doesn’t work with kotlin constructions…napperley
02/07/2023, 10:54 PMnapperley
02/07/2023, 10:55 PMOleg Yukhnevich
02/09/2023, 10:57 AMOleg Yukhnevich
02/09/2023, 11:03 AMval params = allocArrayOf(
OSSL_PARAM_construct_utf8_string("digest".cstr.ptr, hashAlgorithm.cstr.ptr, 0).ptr,
OSSL_PARAM_construct_end().ptr
)
EVP_MAC_init(context, key.asUByteArray().refTo(0), key.size.convert(), params[0])
But in some conditions (looks like when GC works, or anything else) this code starts to fail with seg fault
So I tried once again using C code in def file:
static inline int EVP_MAC_init_HMAC(EVP_MAC_CTX *ctx, const unsigned char *key, size_t keylen, const char *digest_name) {
OSSL_PARAM params[2], *p = params;
*p++ = OSSL_PARAM_construct_utf8_string("digest", digest_name, 0);
*p = OSSL_PARAM_construct_end();
return EVP_MAC_init(ctx, key, keylen, params);
}
and on call site:
EVP_MAC_init_HMAC(context, key.asUByteArray().refTo(0), key.size.convert(), hashAlgorithm)
This way it works all time. (and look, here we pass hashAlgorithm: String
as plain string and it works as expected
for reference commit with this changes: https://github.com/whyoleg/kcwrapper/commit/93dec2fbbb4b74499684b1addce5b92519655c0d
So I would think that is some issue in K/N interop mappings generation or I’m missing some obvious thing.
@svyatoslav.scherbina may be you can somehow help with understanding: is it an issue on my side, or inside K/N interop and I should fill an issue?svyatoslav.scherbina
02/09/2023, 12:43 PMSo, in C version you pass the pointer to theCopy codereturn EVP_MAC_init(ctx, key, keylen, params);
params
array, while in Kotlin
Copy codeEVP_MAC_init(context, key.asUByteArray().refTo(0), key.size.convert(), params[0])
params[0]
is not a pointer to the array, but the first value instead, i.e. OSSL_PARAM_construct_utf8_string("digest".cstr.ptr, hashAlgorithm.cstr.ptr, 0).ptr
. Please try replacing params[0]
with params
to make it equivalent to C.Oleg Yukhnevich
02/09/2023, 12:53 PMparams.reinterpret()
and just casting (because types are different), but in this case it doesn’t work at all (init returns 0, means failure)
I will point one time, that when passing params[0]
code works, but under stress test (which create a lot of keys and then try to sign with them) it starts to FAIL with seg fault at random moment specifically in call to EVP_MAC_init
. while using C code in def - everything works
For context
• OSSL_PARAM_construct_utf8_string
returns CValue<OSSL_PARAM>
(https://www.openssl.org/docs/man3.0/man3/OSSL_PARAM_construct_utf8_string.html)
• params
in kotlin is of type CPointer<CPointerVarOf<CPointer<OSSL_PARAM>>>
• EVP_MAC_init accepts for params CValuesRef<OSSL_PARAM>
(in openssl header it’s OSSL_PARAM[], https://www.openssl.org/docs/man3.0/man3/EVP_MAC_init.html)svyatoslav.scherbina
02/09/2023, 2:50 PMEVP_MAC_init
.
Your Kotlin code creates an array of two pointers to struct values, and passes the first of them to EVP_MAC_init
. So, as a result, EVP_MAC_init
gets a pointer to OSSL_PARAM_construct_utf8_string("digest", hashAlgorithm.cstr.ptr, 0)
, followed by arbitrary garbage instead of OSSL_PARAM_construct_end()
. Sometimes it gets lucky, and that garbage works as a terminator, sometimes not.
So, you need to start with val params = allocArray<OSSL_PARAM>(2)
, initialize the elements properly and pass params
to EVP_MAC_init
. Or, alternatively, use val params = createValues<OSSL_PARAM>(2) { ... }
.Oleg Yukhnevich
02/09/2023, 3:17 PMOSSL_PARAM_construct_utf8_string
declaration from openssl, and not construct structs by my self (error prone)
And so I haven’t found any information/documentation about how to allocate array of structs, and then put in it already created struct values…
But!
Looks like I found a way (after several tries on finding what I need):
val params = allocArray<OSSL_PARAM>(2)
OSSL_PARAM_construct_utf8_string("digest".cstr.ptr, hashAlgorithm.cstr.ptr, 0).place(params[0].ptr)
OSSL_PARAM_construct_end().place(params[1].ptr)
EVP_MAC_init(context, key.asUByteArray().refTo(0), key.size.convert(), params)
And this works as expected!
Big thanks for help!
P.S. I think, that API or at least documentation around creating arrays of structs could be improved 🙂svyatoslav.scherbina
02/09/2023, 3:40 PMP.S. I think, that API or at least documentation around creating arrays of structs could be improved 🙂That is totally correct 🙂
Oleg Yukhnevich
02/09/2023, 3:43 PMsvyatoslav.scherbina
02/09/2023, 3:51 PMOleg Yukhnevich
02/09/2023, 4:10 PMsvyatoslav.scherbina
02/10/2023, 11:04 AMis there any plans to release cinterop for JVM/WASM ?We have no particular plans for this. As you probably know, we have a YouTrack issue for this feature request: https://youtrack.jetbrains.com/issue/KT-39144/Generate-C-bindings-for-all-platforms-through-cinterop (just adding it here for visibility).
I know, that JVM flavor exists to power cinterop clang bindings, but no doc for it of course :)Missing docs is not the main problem here. There are a lot of complicated integration tasks to be solved before this can become useful. Also, even the existing basic implementation for JVM is somewhat behind. And that implementation we have should probably be ported to panama.
Oleg Yukhnevich
02/10/2023, 11:21 AMWe have no particular plans for this.okay.. yes, I know about an issue, still thought that ‘speculation’ talks are better to perform in slack to not pollute an issue discussion 🙂
And that implementation we have should probably be ported to panama.just wanted to mention, in ideal case I would think that there should be a choice: use JNI or use Panama, as there is no Panama support in Android. So to be able to build 2 different JARs (or klibs?) or multi-release JAR will be useful
There are a lot of complicated integration tasks to be solved before this can become useful. Also, even the existing basic implementation for JVM is somewhat behindYeah, I understand, that it’s not easy… still, if implementation of something like this will be started some day, i’m willing to help!
Oleg Yukhnevich
02/10/2023, 12:02 PMsvyatoslav.scherbina
02/10/2023, 1:55 PMOleg Yukhnevich
03/02/2023, 9:25 AM