removal of internal OSSL dependencies#267
Conversation
- Removed more internal includes - Assume provider is always active - Re-generate OQS library contents - Added initial provider testing - Added initial CI
|
@mattcaswell Updated everything as per the discussion above (and included now some CI using the OpenSSL test framework: Is that OK or would you think we should have tests separate from the OpenSSL test framework?). openssl/providers/implementations/signature/oqs_sig.c Lines 26 to 32 in d710bfb All suggestions/questions welcome as always if you find the time. |
| }; | ||
|
|
||
| return digest_md_to_nid(md, name_to_nid, OSSL_NELEM(name_to_nid)); | ||
| } |
There was a problem hiding this comment.
It's not entirely clear why you need any of this code (see comment below).
There was a problem hiding this comment.
Removed with latest push
| //WPACKET pkt; | ||
| EVP_MD *md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); | ||
| int md_nid = digest_get_approved_nid_with_sha1(md, sha1_allowed); | ||
| int md_nid = digest_get_approved_nid(md); |
There was a problem hiding this comment.
Why do you need to get a nid here? The only reason I can see is for use in the (currently commented out) Alg Id code. But that code is using OpenSSL internal APIs. That just happens to be our solution for creating alg ids. You might do something else (and if so there would be no need for a nid). The alg ids will ultimately be fixed strings of octets identifying the sig/md combination. There might be simpler ways to do this for your needs.
There was a problem hiding this comment.
ACK. Looks like we need to implement OQS sig (or hybrids) here for real (and see whether we can make do without referring to internal APIs at all) before making a final call on this matter.
There was a problem hiding this comment.
Removed with latest push
I think the main objective should be to remove any internal dependencies on internal OpenSSL APIs from the provider itself, so that your provider can be entirely standalone and rely on having stability in the APIs that it uses. Internal APIs are subject to change at any time (including in a patch release) - so you could suddenly find everything breaks (potentially even in deployed instances) if OpenSSL gets upgraded in a new patch release if you have internal dependencies. Tests are slightly different, since they don't end up in the actual provider code - so if you want to reuse the OpenSSL test framework then fair enough. That said though, I would think that you would ultimately want to build your provider and tests in its own build tree and not be part of the OpenSSL one. If you get to the point where you're not modifying any OpenSSL internals any more then you no longer have a need to have a fork of OpenSSL, and really you only want the code in your repo that is directly relevant to your provider. There's no reason why you couldn't lift all the bits out of OpenSSL that you need for that (including the underlying build and test frameworks). |
Fully agree. For this clean separation to happen we must get to the point that both the OQS provider doesn't (need to) rely on OpenSSL internals (Thanks again for your help on this) and OpenSSL in turn must fully support externally provided sigs so we don't need the OQS-OpenSSL fork in the future. So let me really try to get down to the details of that. For now, I'd leave things as-is after implementing the OQS-sig provider to have something to run that is not OpenSSL-internal. |
Licensing is a bit of a minefield. Of course I have to caveat anything I say on this before I start with "I am not a lawyer". Therefore anything I say might be wrong and should be treated with caution. Therefore (caveat 2) "you should seek professional advice on this". That said here are some thoughts. I guess there are a few things you need to consider:
(1) is of course entirely you're own affair and for you to decide. For (2), if its been inherited from the OpenSSL master branch then Apache 2 applies (previous versions are using different licences). You can apply your own licence to changes that you make to this code but in that case both licences would have to apply (the Apache licence for the old code, your licence for the new code). That is usually considered undesirable, but is perfectly possible and is in fact what OpenSSL itself did. OpenSSL inherited its original code from the SSLeay project, and then applied its own licence on top. For all versions of OpenSSL up until (and including) OpenSSL 1.1.1, both the OpenSSL and the SSLeay licences applied. We went through a very major effort (that took several years) to contact all previous contributors in order to fix that problem so that we were eventually able to change the licence. The other alternative you have is to just use the the Apache licence for the code that you have inherited. For (3) the issue here isn't so much about licensing but about CLAs. You can have whatever licence you see fit for this code but OpenSSL will relicense it to Apache 2 when it accepts the code. In order for that to work OpenSSL requires all contributors to have signed an OpenSSL CLA. When I say all, I mean everyone who has ever contributed to the code (even if that contribution is small). This can actually be a big problem, and has been a showstopper in the past for some highly desirable contributions that we would really have liked to incorporate, but were unable to because of CLA problems. With regards to "correctly reference the OpenSSL Licence", I think this text applies from the Apache licence: So, basically, you should retain any existing licence/copyright banners. But you can add your own additional ones (e.g. citing your own licence file). |
That is perfect advice: Thanks! I'll do that and we'll also discuss within the OQS team which license text to adopt for an OQS-OpenSSL provider given the new license conditions of OpenSSL you explained above. @dstebila : Please see the above for consideration. |
|
When now activating testing for all KEMs using the OpenSSL test environment, I ran (again, as @christianpaquin did with OpenSSL1.1.1) into a problem with the OpenSSL constant |
I think an upstream fix for that constant would be appropriate for 3.0. The situation is different compared to 1.1.1. In, 1.1.1 there was no possibility of plugging in new groups etc from external sources. Therefore the only way you could go over the SERVER_HELLO_MAX_LENGTH limit was if you had already modified the OpenSSL source. This is no longer the case with 3.0, so it seems reasonable to change it. Want to propose something? |
Gladly done: openssl#13868 |
|
@mattcaswell Quick question: Is there any way to test (new) signature providers -- short of adding all "plumbing" like asn1_meth, pkey_meth (i.e., effectively implementing openssl#10512) wired to call into a (generic) signature provider? I wanted to put "real code" into the OQS sig provider to finally complete this PR but wonder how to trigger it... If it's not simple, please just say No; I'll then get going on 10512 for real. |
|
Adding a new signature provider and testing it should be straight forward - although a little laborious. You shouldn't have to touch the "meths" at all. To implement the signature provider you need to:
An optional extra is to implement encoders/decoders for your algorithm so that you can actually serialise/deserialise your keys to/from files. But you don't need to do that to do some simple tests. Once created, and with your provider loaded into your libctx it should just "work" with EVP signature code. First you'll have to generate a key (error handling omitted for brevity): Then to actually test the signature itself: |
|
There are some docs on some of this, e.g. see: |
|
Thanks for the sample code and pointers! Now all of that is working nicely (sign/verify for OQS algs via EVP). But this still didn't trigger the |
- Signature functionality added - Signature testing added - OID retrieval logic added - tracing output made switchable
|
@mattcaswell Thanks again for your pointers and sample above! It really helped getting me going with the latest push; so let me change the question above to: Did I understand the operations of the (not exceptionally deeply documented) function |
|
Will try and take a look at this later today and respond to your questions, once I've caught up on some other stuff. Just to respond to this one question though:
Of course the motivation for introducing |
Yes - that works. My mistake was to first use my own libctx in combination with
Thanks in advance! |
The AID logic is triggered via functions such as:
I think probably what you need to do is call |
| static int get_oqs_oid(unsigned char* oidbuf, const char *oqs_name) { | ||
| ///// OQS_TEMPLATE_FRAGMENT_SIG_OIDS_START | ||
| if (!strcmp(OQS_SIG_alg_default, oqs_name)) | ||
| return a2d_ASN1_OBJECT(oidbuf, OQS_MAX_OIDBUF_LEN, "1.3.9999.1.1", -1); |
There was a problem hiding this comment.
As noted in my previous comments, probably you want OBJ_txt2obj, and i2d_ASN1_OBJECT.
There was a problem hiding this comment.
Thanks for those pointers. So implemented.
| if (!TEST_true(EVP_DigestSignInit_ex(mdctx, NULL, "SHA512", libctx, NULL, key))) | ||
| goto end; | ||
|
|
||
| if (!TEST_true(EVP_DigestSignUpdate(mdctx, msg, sizeof(msg)))) |
There was a problem hiding this comment.
Probably you should initialise msg with something - otherwise you get an uninit read.
There was a problem hiding this comment.
Hmm -- didn't happen. But better safe than sorry. So done. Thanks for the catch.
test/oqs_test.c
Outdated
| if (!TEST_true(EVP_DigestSignFinal(mdctx, NULL, &siglen))) | ||
| goto end; | ||
|
|
||
| if ((!(sig = OPENSSL_malloc(siglen)))) |
There was a problem hiding this comment.
You can use TEST_ptr here to check for NULL (which gives benefits of giving output that integrates into the test framework if it fails)
OK -- when looking at how to manually trigger Wouldn't that trip me up when not using/configuring the NULL libctx? Also see issue openssl#13732 Could it be as simple as replacing https://github.com/openssl/openssl/blob/3aff5b4bac7186fda9208a76127eff040cafae13/crypto/asn1/a_sign.c#L139 with ? |
That code does look a little suspect, but I think it ends up being ok because the |
|
As I face the option of creating low-level (ASN.1) tests now using rather undocumented internal functions I think I'd prefer to call it a day and merge this PR now: The code already massively improved due to your input, @mattcaswell, so many thanks for that. |
To the extent that my opinion matters on what gets merged in this repo - fine by me :-) Unfortunately I think this bug is going to be a bit of a killer for you: Until that bug is fixed you're not going to be able to use X509 certs that contain externally provided algorithm public keys. Once we can do X509 with external algs I'm thinking that the next problem will be the fixed table of cert types that libssl holds: We currently store in that structure the certificate/chain and associated key for each algorithm type that we support. And the algorithms types are a fixed list. I think this should be where you start. We need that to be able to cope with algorithm types that we don't know up front. It's unclear how much you can do on this without the X509 bug being fixed - but perhaps you can start. Once that works the next problem will be to introduce pluggable sigalgs in the same manner that we have pluggable groups. Independently of the above I think you are going to need to write encoders/decoders in your provider for the various algs. At least for DER anyway because you need to encode and decode public keys for libssl. |
It absolutely does. Thanks.
Yup - rings a bell. Current change in our 1.1.1 fork: Line 499 in b61ce60 Given there are lots of "explicit" index-references to the What currently makes me wonder more is how to deal with the function lists ( Line 1318 in b61ce60 Line 1732 in b61ce60 ossl_algorithm_do_all and method construction but I'm only at the very start....
|
* removal of internal OSSL dependencies - Removed internal includes - Assume provider is always active - Re-generate OQS library contents - Added initial provider testing - Added initial CI - corrected license statements - Signature functionality added - Signature testing added (via EVP) - OID retrieval logic added - tracing output made switchable
GOST is only available via ENGINE at the moment, and we still need to be able to support that. So we will need to support "legacy" pkeys.
This is possible. I would envisage using "capabilities" to enumerate the sig-algs that a provider supports, and then having some kind of dynamic storage to store the certs/keys etc. Possibly we need to add to the sig-algs that we have enumerated from providers the "well-known" classic algorithms that might be filled in from some other source. I'm less sure if the dynamic storage needs to necessarily be an array the size of all algorithms. It might be an array that is sufficiently big only to store the certs/keys that have been provided by the application. If we did that we would have to refactor everywhere that uses the array to call some function to "look up" the cert/key that we are interested in.
I'm not sure I understand your concern here. We shouldn't need to deal with the "METHOD"s at all? That should all be "under the covers" of the EVP interface. |
|
Unfortunately, I have to be the bearer of bad news. The OTC discussed the possible addition of pluggable signatures in the 3.0 timeframe. The conclusion is that this is unlikely to be accepted on the basis that we have so much other stuff we need to do by then then adding this significant feature in the mix as well (even if its just review time etc) is likely to cause us to miss deadlines... :-( |
Not entirely unexpected, so no problem. We have a fully working OQS KEM provider for OSSL3 and a fork for 1.1.1 doing certs and pretty much everything else, so I guess we're fine for now. I'd be glad to reciprocate your support so far (and maybe in the future) by trying to resolve existing issues you have -- in the process picking up knowledge to make progress on pluggable signatures eventually. So if there's a list of items to resolve before v3beta1, please let me know, maybe pointing to ones most suitable for someone not entirely certain about all nuts and bolts in OpenSSL (if such issues were around). |
Any help you can offer would be gladly accepted. The list of issues that we are most concerned with is here: However, I might suggest openssl#13893 would be a good one to tackle. It's a little tricky, but actually is helpful to OQS even without pluggable signatures. Wouldn't it be cool if you could load X509 certs using standard OpenSSL 3.0 using OQS algs (even if you can't actually use those certs in libssl)? If you also had encoders/decoders implemented in your provider then (in theory) you should be able to use the standard openssl "x509" app to create and examine such certs. |
Looking into it -- particularly interesting as we can already generate OQS X509 certs (using the 1.1.1 fork), so have something to test against. So if I understand the goal right, one would need to create an EVP_PKEY given the appID, iff a matching provider decoder can parse the X509 key data (to keep the API of |
|
I think it may not be that difficult with a decoder. The "x509_pubkey_decode" function mentioned in the issue above would have to have some code, something like the following for the OID "1.2.3.4": This requires the decoder algorithms to have "1.2.3.4" as an alias to their normal name in the provider so that we can find them. This also requires all algorithms to have a decoder - which you don't yet in your OQS implementation. |
Thanks for this pointer: Going through this code I now better understand how this decoder logic works (basically iterating through all decoders), hence understanding this statement better:
I'll work on that then, too. Original plan was to understand and enable this for traditional crypto first, but after seeing the decoder iterator (and finally reading provider-encoder) things become clearer. Next then is to see how to add OID aliases in all provider decoders -- or is it as simple as just adding (all proper) ":" to the |
Let me clarify that statement a bit. It requires all algorithms that you want to decode through this route to have decoders. So you could work on the traditional crypto first and get that working and the then add the decoders for OQS later.
In theory...yes. |
Probably a better approach, indeed.
It does even in practice (after adding "1.2.840.113549.1.1.1" to the "RSA" decoder registrar),
I feel I'm missing way too much insight on how the X509 decoding logic works to follow through: For example, when running this, Which documentation pertaining to all this would you recommend I should read to better get a grasp on this? Just reading the code seems suboptimal. |
This is the ASN.1 DER decoding logic. Really you don't need to understand this to tackle this particular problem. We have an X509 structure which contains many fields and nested sequences that aren't particularly interesting for this problem. Eventually, within the bigger X509 structures, we hit a sub-structure, i.e. the "SubjectPublicKeyInfo" ASN.1 structure. This is represented internally as an You will note that the definition of that structure has a callback associated with it called By this stage in the ASN.1 DER decoding logic we have successfully decoded an X509_PUBKEY structure and the callback gets called to do whatever post-processing we care to do on it.
I've not investigated this, but I guess the upper layer decoder is to decode the higher level X509 cert structure. There shouldn't be a problem with re-entering the decoder code because we are now decoding a different sub-structure.
We can't get hold of the BIO at this point - what we have to work with is the X509_PUBKEY structure. The simplest thing to do is probably counter-intuitive and a bit nasty....call
There are a number of pages on the OSSL_DECODER API, e.g. start with: https://www.openssl.org/docs/manmaster/man3/OSSL_DECODER_CTX_new_by_EVP_PKEY.html There doesn't seem to be an "overview" (man7) page for this, which seems like a bit of an omission. |
Hmmm... If we re-encode the decoded DER structure (SubjectPublicKeyInfo) to then apply a (arguably the same) DER decoder (again) wouldn't it be logical that we recurse (into the same "done decoding this DER component" code)? To show the point, here's the code executed within and its result: Indeed, it blows the stack apart -- "Decode result" never appears. Or did I overlook a mechanism to just call the EVP_PKEY constructor (which is apparently properly set after calling
Yup. Same with provider doc: Only provider-encoder, nothing on decoders. |
Hmmm....that's an unexpected problem....somehow we need to act differently when called libcrypto side, to how we act provider side....because it seems the implementation of the provider side decoder code is itself calling d2i_X509_PUBKEY and hence we get the recursion, i.e. if an application calls d2i_X509_PUBKEY then we need need |
Arguably not a(ny) (decoder) provider but only the built-in provider(s), right? Or is the assumption that all providers (incl. oqsprovider) could utilize Now, when checking who's calling
The only idea reading the code gave me is to check whether or not Edit: Here's the diff at a glance: https://github.com/openssl/openssl/compare/master...baentsch:mb-providerdecoderselect?expand=1 |
Right. I suspect the provider side handling of this is not quite right. Maybe, it should not call d2i_PUBKEY at all - but possibly an alternative internal only version that uses a different callback...i.e. not |
|
Uhmmmm, I don't see why calling So yeah, for a conversion of a SubjectPublicKeyInfo to a X509_PUBKEY with a provider side EVP_PKEY, the function to rewrite is |
|
We have avoided, as much as possible, to make too much changes for provider stuff in the ASN.1 code... some of it is definitely not made for that. |
When looking at it, isn't this PR rather "perpetuating" the concept of NIDs/legacy keys: As such it's not much of an inspiration to resolve openssl#13893 (where exactly the
I tend to agree: This https://github.com/openssl/openssl/blob/b897b353dff8138aa838bae9766ecd3de8c03280/crypto/asn1/a_verify.c#L133-L137 also looks like it wouldn't possibly be able to work with provider( key)s... Maybe time to re-word openssl#13893 as "add provider key support everywhere" (or "eliminate use of |
|
Something to remember is that OpenSSL is currently in a state of transition. For backward compatibility reasons, we need to consider the possibility that there are legacy implementations in the wild, and that involves both NIDs and structures like EVP_PKEY_METHOD. We may get them because of engines, or because the calling application has its own built-in implementations! NIDs as we know them aren't really that good to keep around, because of their "constant" nature. I have it on good authority that the constants were never meant to become public, but they became so, and we got the role of OID/NID registry. We don't want to continue with that role, so we're avoiding the cementing of NIDs in the libcrypto<->provider interface, which is the boundary where we care very much what's assumed and what's being passed back and forth. Another thing we decided is that we don't want to see any ASN1 code inside the FIPS module. Apart from that, what happens within the confines of a provider is a different matter, and as you can see, we play pretty hard and fast in places, and that's ok, as long as we're careful what passes over the libcrypto<->provider interface. The problem isn't so much about |
|
I believe I found the correct solution for openssl#13893, BTW. Thank you for the discussion so far, @baentsch, that was valuable input for finding my direction in this. |
|
Thanks in turn for your insight and feedback, @levitte and @mattcaswell : It helped me understand better where things stand and how to better contribute. Please let me know when you're doing the PR addressing openssl#13893, so I understand where this is heading. Again, please let me know if you think there'd be issues for me to look into (short of re-writing ASN.1 for good :-) Until then, I'll try creating an oqsprovider encoder/decoder. Let's see how that pans out... |
That will be a really useful exercise to make sure we got the API right. |
I've started on that. It will be a hack, but hopefully not too horrible... |
|
See openssl#13994. With that, it should be fairly easy to write your own decoders, with just one caveat: there's now a We're able to cheat quite a lot in OpenSSL's providers, mainly because we know that our decoders are built in and has access to all of libcrypto's internals. The ideal thing would be, of course, if OpenSSL would offer unencumbered structure decoders (for example a variant of d2i_X509_PUBKEY() that didn't try to automatically decode its contents further, and something similar for PKCS#8)... it should be possible to do, and what to name those is the question. Anyway, the best I can recommend is, unfortunately, that you create your own SubjectPublicKeyInfo structure for provider usage, using our |
Addresses various improvement suggestions by @mattcaswell: Thanks!