Talk:Libcrypto API

From OpenSSLWiki
Revision as of 20:52, 4 March 2013 by Matt (talk | contribs)
Jump to navigationJump to search

Current Discussions

Initialization and engines?

Should the recommended initialization code include a call to ENGINE_load_builtin_engines? (Or to OPENSSL_config, which calls ENGINE_load_builtin_engines.) Otherwise, the RdRand engine for getting better random numbers from newer Intel chips (as one example) won't be used.

(My own thoughts on OpenSSL initialization are here.)

--Ppelleti 18:05, 3 March 2013 (UTC)

Hmmm - I've not come across this as a recommendation before. What is the original source for your recommendation?

--Matt 22:15, 3 March 2013 (UTC)

It's not from any existing documentation source, other than gleaning some information from the engine manpage (see "Automatically using builtin ENGINE implementations") and the CHANGES file. But mostly it's my own conclusion, based on reading the source code and performing experiments.

The basic question I was trying to answer was, on modern Intel processors which support AES-NI and RdRand, is OpenSSL taking advantage of these hardware features. The answer appears to be different for the two different features. For AES-NI, it appears from the source code (and was recently confirmed on the mailing list) that AES-NI is automatically used if it is available, without needing to do anything special.

However, for RdRand, it appears that the answer is different. In the source code, there is a separate RdRand engine. If the RdRand engine is not used, then the default pool implementation in md_rand.c is used, and you don't get the benefits of RdRand.

From the section I already mentioned in the "engine" manpage, it sounded like no engines are used by default, and you must enable them by calling ENGINE_load_builtin_engines() followed by ENGINE_register_all_complete(). Although the CHANGES file partially contradicts this advice, saying:

  *) Add call to ENGINE_register_all_complete() to
     ENGINE_load_builtin_engines(), so some implementations get used
     automatically instead of needing explicit application support.
     [Steve Henson]

I did some experiments on a machine with RdRand. I wrote the following little bit of code:

  ENGINE *rnd = ENGINE_get_default_RAND ();
  if (rnd)
    printf ("default rand engine: %s\n", ENGINE_get_name (rnd));
    printf ("no default rand engine\n");

If I initialize OpenSSL the typical way:

  SSL_load_error_strings();                /* readable error messages */
  SSL_library_init();                      /* initialize library */

without calling any ENGINE functions, then my little code fragment will print "no default rand engine", indicating the implementation from md_rand.c is being used. But if I call ENGINE_load_builtin_engines() after the other initialization functions, and before my little test, it then prints out that RdRand is the default rand engine.

So, this is how I drew the conclusion that it's necessary to call ENGINE_load_builtin_engines() as part of your initialization, if you want to get RdRand support.

However, this is all made a little bit trickier by the fact that OpenSSL_add_all_algorithms() can actually mean one of two vastly different things, depending on a #define at compile time. If OPENSSL_LOAD_CONF is defined, then OpenSSL_add_all_algorithms() is really OPENSSL_add_all_algorithms_conf(), but if OPENSSL_LOAD_CONF is not defined (which is the default), then OpenSSL_add_all_algorithms() is really OPENSSL_add_all_algorithms_noconf().

OPENSSL_add_all_algorithms_conf() is a two-line function:

void OPENSSL_add_all_algorithms_conf(void)

So the difference is that if OPENSSL_LOAD_CONF is defined, then OPENSSL_config() is called, when it otherwise wouldn't be. What does this have to do with RdRand? The thing is that OPENSSL_config() calls ENGINE_load_builtin_engines(). (And then ENGINE_load_builtin_engines() in turn calls ENGINE_register_all_complete(), as mentioned in the CHANGES entry.)

So, to get RdRand support, you can either #define OPENSSL_LOAD_CONF when building your program, or you can call either ENGINE_load_builtin_engines() or OPENSSL_config() in your initialization sequence. However, it appears that calling ENGINE_load_builtin_engines() more than once will leak memory, so ideally you don't want to call ENGINE_load_builtin_engines() if you also plan on calling OPENSSL_config(), or if you've defined OPENSSL_LOAD_CONF. (Of course, since it's just a small fixed-size leak at initialization, this wouldn't really be a practical problem, but still makes me feel icky.)

--Ppelleti 04:33, 4 March 2013 (UTC)

Note that the use of OPENSSL_config() is recommended during initialisation: this is mentioned in the manual page. Currently the routines associated with OPENSSL_config() can be used for adding OIDs and configuring ENGINEs. In future it may well do much more and calling OPENSSL_config() (or the actual conf library if finer control is needed) will automatically take advantage of that.

Here's an example of what I mean. Suppose you have a user who wants to do something weird with an ENGINE: perhaps load an unusual one that needs various ctrls to get it to work. Maybe they want to do something peculiar like use RSA with one ENGINE and DSA with another. You'd have to delve quite deep into the way ENGINE works to support that kind of thing and would it be worth it for something hardly anyone would use?

If instead you called OPENSSL_config() that user can just set up the config file to do what they want and the application writer doesn't have to worry about all the messy ENGINE calls.

--Steve 13:37, 4 March 2013 (UTC)

Return values

Note that not all of the libcrypto functions return 0 for error and 1 for success. There are exceptions which can trip up the unwary. For example if you want to check a signature with some functions you get 1 if the signature is correct, 0 if it is not correct and -1 if something bad happened like a memory allocation failure. So if you do:

 if (some_verify_function())
    /* signature successful */

and someone can induce the "something bad happened" condition you end up behaving as though a bad signature is good. This one cropped up in the library internals at one point and was fixed in a security release. Currently you should check the manual pages or the source to be sure. It would be really useful if the exceptions were all documented, double checking with the source.

--Steve 13:57, 4 March 2013 (UTC)

Old Discussions

Best practices for printing errors

I'm curious about the recommendation to do this:

   unsigned long errCode;
   while(errCode = ERR_get_error())
     char *err = ERR_error_string(errCode, NULL);
     printf("%s\n", err);

Wouldn't it be much simpler to just do:


Or, if one really does want to iterate through each line of the error queue individually, wouldn't it still be better for us to recommend using ERR_error_string_n with an explicit buffer? ERR_error_string with a NULL argument is not thread-safe.

--Ppelleti 18:12, 3 March 2013 (UTC)

Either way does the trick, but I agree yours is simpler. I'll change it.,

--Matt 22:16, 3 March 2013 (UTC)

ERR_print_errors_fp is the best "call it and forget it" method for errors if it is appropriate to use an fp. Calling the ERR routines directly can be done but it's trickier and the example given is incomplete: I'd have to check it further to see how best to call all the routines. [per Steve Henson]

--Stevem 14:27, 4 March 2013 (UTC)