I think that a big part missing in the OpenSSL lib is the RSA and PrivatePublic key bindings.
I can try to add some, but looking at Ruby sources they built there own C wrappers around those parts.
How should I proceed with this ? does someone working on that ?
There's this but it's outdated. I've been upgrading it every Crystal release on my computer because I need it for a project of mine but I don't have enough knowledge on the subject to keep a public updated fork.
@Exilor we had this open: https://github.com/crystal-lang/crystal/issues/1710
And it seemed that it just fizzled.
Maybe we should fork it and PR it to the main Crystal repo, as in addition to OpenSSL and not as an external shard
@bararchy I agree but as I said I'm not knowledgeable with this so I can't be of much help :/
For instance, OpenSSL::MemBIO is a struct defining a #finalize which is not currently allowed. I changed it to a class and it does the job but that's just me fiddling with the code until it works. But I don't know why it was a struct in the first place or if memory will leak if it's a class.
If you feel able then by all means go ahead (if asterite is ok with it).
@Exilor If @datanoise was still around we could ask him technical details, TBH , I , like you , am not that knowledgeable in regards to the small details of C-Crystal bindings and structures of types, I guess we need someone to "keep an eye" on those commitsPR's and make sure that if we change stuff it's not bad.
You may look at the Ruby API (not the implementation) but also the OpenSSL C API, see how they are different or similar, then try to implement the features. Keep in mind that macOS still ships with OpenSSL v0.9.8 and many Linux LTS releases ship OpenSSL v1.0.x; some features may not always be available.
Linking to C isn't complex. You may use crystal_lib to generate the initial bindings for example. Constants, enums, structs and functions will be generated automatically from C headers. You'll may want to clean them up, thought, because C uses so much aliases and defines that the generated bindings are usually not pretty.
Once you have them, you merely call the lib functions. If you receive a pointer to a C struct, you most likely will have to free it, using a special OpenSSL function to achieve so. You need a class and a #finalize method for that, because Crystal struct are allocated on the stack and the GC won't collect them (i.e. memory leak) and thus will never call #finalize 鈥攈ence why it's now forbidden on struct.
Make sure to never expose raw C pointers, except through #to_unsafe. Always verify that a returned pointer isn't a null pointer, then either raise or return nil depending on the situation.
If you need to allocate a C struct on the stack (unlikely) you may x = uninitialized LibSSL::MyStruct or x = LibSSL::MyStruct; if you must allocate a struct and return it, allocate heap memory, you may ptr = Pointer(LibSSL::MyStruct).allocate.
And that's about it. If you're unsure about what API to create, you may open an issue first.
If it's enough pain to support multiple OpenSSL versions, possibly crystal could just bundle "a good version" and require its compilation and use if the local installed one is out of date or what not? Just thinking out loud :)
No. OpenSSL is big and flawed. We must let official packagers deal with that.
@ysbaddaden Good idea about using the crystal_lib, sadly it seems not to work (crashes on the exmaples) , I opened a ticket.
@ysbaddaden your answer should be put in C bindings section
Please feel free to copy-paste, rephrase a bit and open a pull request :-)
Hey, I'm completely new to Crystal but really need RSA support in the stdlib.
I've written this using the Ruby API (minus all the aliases) and the datanoise library mentioned above as inspiration.
There is only one flaw I can find so far and that is that I can't seem to write out the RSA priv/pub key to a .pem file when using a passphrase. Reading in seems fine - if anyone is able to help with that at all that would be greatly appreciated as openssl doesn't throw any errors on my machine for this.
Since I'm completely new, I am not 100% sure about the naming conventions, presentation etc when contributing to the stdlib. Is anyone able to skim over my fork before I submit a PR?
https://github.com/crystal-lang/crystal/compare/master...randomstate:feature/openssl-rsa?expand=1
Thanks 馃憤
@CImrie It's probably best if you open a PR anyway (tagged as [WIP]) so it's easier for others to review your code and comment on it.
@straight-shoota Thanks, I've opened one 馃憤
Closing this as it was discussed it would be handled in an outside shard
Most helpful comment
You may look at the Ruby API (not the implementation) but also the OpenSSL C API, see how they are different or similar, then try to implement the features. Keep in mind that macOS still ships with OpenSSL v0.9.8 and many Linux LTS releases ship OpenSSL v1.0.x; some features may not always be available.
Linking to C isn't complex. You may use crystal_lib to generate the initial bindings for example. Constants, enums, structs and functions will be generated automatically from C headers. You'll may want to clean them up, thought, because C uses so much aliases and defines that the generated bindings are usually not pretty.
Once you have them, you merely call the lib functions. If you receive a pointer to a C struct, you most likely will have to free it, using a special OpenSSL function to achieve so. You need a
classand a#finalizemethod for that, because Crystalstructare allocated on the stack and the GC won't collect them (i.e. memory leak) and thus will never call#finalize鈥攈ence why it's now forbidden onstruct.Make sure to never expose raw C pointers, except through
#to_unsafe. Always verify that a returned pointer isn't anullpointer, then eitherraiseor returnnildepending on the situation.If you need to allocate a C struct on the stack (unlikely) you may
x = uninitialized LibSSL::MyStructorx = LibSSL::MyStruct; if you must allocate a struct and return it, allocate heap memory, you mayptr = Pointer(LibSSL::MyStruct).allocate.And that's about it. If you're unsure about what API to create, you may open an issue first.