Generalize setup_engine of apps/apps.c to allow specific crypto methods#4277
Generalize setup_engine of apps/apps.c to allow specific crypto methods#4277DDvO wants to merge 3 commits intoopenssl:masterfrom
Conversation
|
I wonder, how does that change benefit the |
|
@levitte, basically all |
|
I could easily have missed something after a long and hectic day, but I don't see how this helps. The OpenSSL engine framework doesn't distinguish between different engines that support the same algorithm or between an engine and the built in implementation -- if I remember the code sequences correctly, it will choose the default then the first engine that says: "yes, I do that." To avoid this, the calling application needs to specify which engine it wants to use. I don't think there is a way to say to use the built in software implementation. That is, changing the default isn't going to provide the flexibility you want by itself. Would some manner of per engine capability list be more appropriate? That is, each engine specifies more precisely what it does and the application can request exactly what it wants. |
|
I think this highlights a limitation in the command line app and engine. If you specify an engine on the command line an attempt is then made to use it for all crypto algorithms it supports. Sometimes you don't want that and just want to use the ENGINE to load a private key. You might want to specify different keys in different engines. An engine might implement a crypto algorithm that is only for use with its keys and not general purpose crypto: setting ENGINE_FLAGS_NO_REGISTER_ALL in the engine partly helps but is has limitations. |
|
Hmmm, if an engine is used solely for loading keys, said engines should be set to add no default algos... so ok, that's a point. |
|
Stephen has given a very good summary of the main problem: once the methods of an engine have been set as the default, the superseded built-in methods cannot be used any more for key material not related to that engine. While the idea given by Pauli might to provide even more flexibility, the very simple generalization I propose would already be a major gain. In particular, it allows to load and use keys (simultaneously in the same application run) from any number of different engines and from software/file storage. It also allows to specify exactly which methods, if any, of which engine are taken as the default. |
|
I still don't understand how this fixes the problem. I don't see why not having a default engine is the sole problem. What am I missing? When there are two (or more) engines that implement the same algorithm, the code that decides if an engine should be used or not will always pick the same engine. It only knows about the algorithm wanted and cannot distinguish based on the data being processed. Likewise, if any engine implements an algorithm, it will always be chosen in preference to the built in implementation. Setting the default forces the result but it doesn't change the fact that a predetermined engine will be used. That's not the whole story. Some calls have an optional engine argument. If this is specified, then yes you can choose which specific engine you want. The internal software implementation is unavailable. Additionally, some operations carry the engine used with them ( How does disabling the default engine help? An application needs to specify the engine used for each operation if it wants to use more than one and if this is done the default is ignored. |
|
On 01.09.2017 01:53, Pauli wrote:
I still don't understand how this fixes the problem. I don't see why
not having a default engine is the sole problem. What am I missing?
Not setting a default engine is not the problem, but solves the problems
I mentioned.
When there are two (or more) engines that implement the same
algorithm, the code that decides if an engine should be used or not
will always pick the same engine. It only knows about the algorithm
wanted and cannot distinguish based on the data being processed.
Yes, in this case, there are less problems. Yet if engines have
conflicting algorithms, setting one of them as the default can even lead
to crashes because for key material of other engines they typically make
no sense.
Likewise, if any engine implements an algorithm, it will always be
chosen in preference to the built in implementation. Setting the
default forces the result but it doesn't change the fact that a
predetermined engine will be used.
The problem here (which actually is my main problem) is that when an
engine's algorithms are set as default, these are typically inadequate
for key material not held by this engine (in particular keys held in SW
for which the built-in algorithms should be used). This also can easily
lead to crashes.
That's not the whole story. Some calls have an optional /engine/
argument. If this is specified, then yes you can choose which specific
engine you want. The internal software implementation is unavailable.
Additionally, some operations carry the engine used with them
(|EVP_PKEY|) and this is chosen if possible, overriding the /engine/
argument if specified. Again, the internal implementation cannot be used.
When I load a key from SW, its EVP_PKEY structure has a NULL engine
reference, which as what I want. Yet when in addition an engine has been
loaded, setting its algorithms as the default, these wrong algorithms
are applied to the key (although its EVP_PKEY structure does not
reference any engine).
How does disabling the default engine help?
As just detailed, it helps by leaving keys alone that are incompatible
with the engine.
An application needs to specify the engine used for each operation if
it wants to use more than one and if this is done the default is ignored.
Even when no algorithms of any engine(s) are set as the default, the
EVP_PKEY structure of any key loaded via some engine will carry a proper
reference to this specific engine, such that the right algorithms are
chosen for this key automatically.
To conclude, I can see no advantage for setting the algorithms of an
engine as the default, but just disadvantages.
(And even if it does have advantages, the little fix I propose does not
prevent making use of them, and in fact it is conservative in the sense
that for existing code nothing changes.)
|
|
I think that in the end, what we're talking about here is a conflict between two quite separate engine usage:
The trouble is that when both usages are activated, the same functions are used for them, since there is no distinction at a higher level between, say, RSA functions for keys with all parameters exposed and for keys with private parameters hidden. In the end, it requires that the different engines must be able to distinguish between "normal" keys and keys hidden in a device and act accordingly. By turning off the installation of defaults, we essentially lose acceleration. It can, however, be argued that this particular usage has lost its importance these days, compared to when the ENGINE API was first designed, back in around 2000. These days, it seems that the more important usages are to store keys and to bring new algorithms that aren't build into libcrypto... |
|
Thanks for the explanation, I understand why you're trying to do this. @levitte I'm not convinced that the engine as an accelerator is gone. Small embedded CPUs often have cryptographic accelerations. Intel CPUs have the RDRAND engine which is an accelerator but only tangentially related to this. Some of the code paths upon seeing a NULL engine reference try to locate a suitable engine. Other's don't. Using It seems to me that a special internal engine setting that forces the libcrypto implementations to be used might be a more comprehensive solution. |
|
There are also two additional uses of defaults which are not associated with crypto acceleration.
|
|
@snhenson is the first related to the default engine? Won't it be discovered regardless as the only engine that implements GOST? The second is more interesting, it needs to not only be the default but also mandatory -- i.e. no other engine is permitted to supply the same algorithms even if one can. I keep circling back to capability lists for engines which can be specified by the using application. I should pull my finger out and implement something to see what folks think. |
|
Are there any new thoughts on this topic? |
|
I think that the only point where I can see the clash is if you load two engines that both implement the same default algo FOO (and assuming that those engines also implement other desirable stuff that differ from each other or there would be no point loading them both). Am I correct? Note that the |
|
No, as I tried to explain above the issue also arises when using just one engine, namely when loading and using private keys from both the engine and from other source(s), such as a from a file or from memory. |
|
Well, this PR doesn't really change that much, it only moves the exact same flags to another spot and gives the possibility to change them if that would be desired... I'm curious how you want to move forward... how do you see those flags being changed? |
a259132 to
2096101
Compare
|
Right, the code change I proposed does not change anything for use with the current apps. For instance, both |
2096101 to
f900d1e
Compare
|
Thanks a lot @levitte for having a look again at this PR. |
f900d1e to
def1205
Compare
|
Included two further small fixups, also of the documentation. |
0ab0da3 to
e078f1e
Compare
3b29bde to
cd7ac98
Compare
ba7655f to
97638d7
Compare
|
Rebased and mostly squashed. |
|
@levitte, hope you can approve this now? |
|
Pinging again @levitte - this should be a low-hanging fruit. |
…dual method defaults minor changes due to review; use new setup_engine_methods() in apps/cmp.c
|
I've also rebased this PR and made use of the new/extended |
|
Please check the CI status before merging. |
|
24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually. |
Reviewed-by: Richard Levitte <[email protected]> Reviewed-by: David von Oheimb <[email protected]> (Merged from #4277)
…thod defaults Reviewed-by: Richard Levitte <[email protected]> Reviewed-by: David von Oheimb <[email protected]> (Merged from #4277)
|
Merged - thanks! |
|
Wow - note the date of the first commit of this PR: Mon Aug 28 19:14:47 2017 +0200 |
The function
ENGINE *setup_engine(const char *engine, int debug), defined inapps/apps.c, is used by many OpenSSL apps. So far it unconditionally sets all the crypto methods of the given engine as the application-wide default. This is likely what is needed for most (simple) use cases, in particular if an application uses just one private key (and related set of algorithms) or just one engine (corresponding to one type of key store) for all its key material.Yet when several implementations for the same type of algorithm need to be used where, for instance, one of them is taken from an engine and others should be the standard ones defined in the crypto lib or taken from a different engine, it is not appropriate to make (all) the methods of one engine the default for all crypto algorithms. This holds in particular for a new app I am currently enhancing for later inclusion with OpenSSL. CMPforOpenSSL is a CMP client that can use signatures for protecting messages both at application and at TLS level, where for example the private key for CMP-level protection is held in a smart card (and accessed via an engine) and the private key for the (optional) TLS channel is stored in a password-protected file (and the keys may even be of different type, say, RSA and ECC).
I tried to make use of the given implementation of
setup_engine()and to unregister any conflicting crypto methods, but this does not work. The only (clean) solution is to use a variant ofsetup_engine()that does not automatically callENGINE_set_defaultwith argumentENGINE_METHOD_ALL. Then for each private key one has full flexibility which crypto engine and methods (or the default OpenSSL crypto lib implementation) to use.Therefore I propose to generalize
setup_engine()by adding an extra argument,unsigned int flags, to be handed toENGINE_set_default. This allows to decide ifENGINE_set_default()has any effect at all and to choose for which types of methods the implementation given by the engine is taken as the default.Backward compatibility with existing code can be achieved easily by providing a macro that uses
ENGINE_METHOD_ALLas value for theflags.