[Cryptography] Heartbleed and fundamental crypto programming practices

Roland C. Dowdeswell elric at imrryr.org
Fri Apr 11 15:13:44 EDT 2014


On Fri, Apr 11, 2014 at 01:55:02AM -0400, Kevin W. Wall wrote:
>

> On Thu, Apr 10, 2014 at 2:30 PM, Bill Cox <waywardgeek at gmail.com> wrote:
> 
> [big snip]
> 
> > For years, the getpass manpage said getpass was obsolete and not to
> > use it.  At the same time, it did not recommend any other solution,
> > and AKAIK, there was none.  The current manpage does say in the BUGs
> > section that it is critical for the user to clear the password ASAP,
> > but there is no hint about how to actually do that in a way that the
> > optimizer will honor.
> 
> I'd bet one way that you could clear the password would be to overwrite
> it with random characters or just '*' and THEN write out the overwritten
> password to a file descriptor associated with /dev/null.  If there's
> an optimizer that currently optimizes away code with writes to /dev/null,
> I'd really be surprised.  Sure, that is going to incur an additional
> system call and it may reveal the length of a password for those who
> can locally monitor I/O stats, etc. but I think that would be portable to
> any *nix flavor system and I'd be really surprised if the optimizer
> optimized it away because of the I/O. You could pass in a file descriptor
> argument you are concerned about overly zealous optimizers as well, but
> just make damn sure that it is really associated with /dev/null. (But
> even it if is not, all you likely are leaking is a password length.)

This is probably too complicated.  If you don't trust volatile and
whatnot, all you need to do is pass the pointer to a function that
the optimiser doesn't understand.  At this point, no assumption
can be made that the writes are superflous.  Granted, the optimiser
may become more sophisticated over time but you can probably trust
that optimising over, say, .o or library boundaries is unlikely.

If the function that you call actually tests the string to ensure
that it is all zeros and aborts on failure, then you would even be
using the data which would make the compiler substantially less
likely to be able to optimise it away---and it would provide a sort
of test that it is working.

The key point, though, is if you confuse the optimiser then it
can't remove the writes.  I.e.:

	size_t	i;
	size_t	accum;
	char	buf[SIZE];

	for (i=0; i < sizeof(buf); i++)
		buf[i] = (i * 2) & 0xff;

	for (i=0; i < sizeof(buf); i++)
		accum += buf[i];

	if (accum % 2 == 1)
		/* Value can't be odd */
		abort();

Should confuse a majority of optimisers as I would assume that not
many of them have logic that knows that the sum of a list of even
numbers must also be even.  This is a little slower than simply
zeroing the memory but it illustrates the point.

--
    Roland Dowdeswell                      http://Imrryr.ORG/~elric/


More information about the cryptography mailing list