[Cryptography] quantum computers & crypto

Jerry Leichter leichter at lrw.com
Sat Nov 6 18:40:09 EDT 2021


> Even correct code needs safety measures.  We hang extra logic around our
> code to be sure it actually does exactly what we intended for it to do
> and nothing else.  We add the architecture and structures to accommodate
> likely future extensions, and then write code to be absolutely sure
> those additional facilities are not being used yet and cannot be used
> until such extensions are actually made....
This is a "yes, but" situation.  Every extra bit of algorithm or code is something that *can* go wrong.  We've seen cases in which code added specifically to improve security ... broke security.

In fact ... here's a real case I had to deal with not long ago.  A Java program has to deal with passwords that get sent to remote servers.  There were repeated cases where the passwords leaked into log files, which lead to big complaints from customers.  Typical cause:  The code has to use a third-party API, and the third-party API throws an exception that includes the password.  We catch and log it.  Boom.

After fixing one too many particular variants of this, someone finally decides to add a failsafe mechanism:  Any time a message is about to be logged, scan it for occurrences of the password - we know it as we're sending it! - and replace them with a bunch of asterisks.  The Java code is straightforward:

    String safeMessage = message.replaceAll(password, "****");

Then one day a customer notices a password in the log file.  Loud complaint.  What happened?

Consider the Java String API.  It has the following methods:

	String replace(char, char)
	String replace(CharSequence, CharSequence)
	String replaceFirst(String, String)
	String replaceAll(String, String)

The developer chose the obvious one, probably after his development environment responded to him typing "message.rep" and suggesting the four possible calls.  But ... it doesn't mean what he thinks it does!  (Test yourself:  What *does* it do?)

You see, the first two calls replace *all* occurrences of the first character with the second character, or the first sequence of characters (CharSequence is an interface that String implements) with the second.  But the second two calls *interpret the first argument as a regular expression*, not as a simple string.  As it happened, the customer's password contained a single '{' character, making it syntactically invalid as a regex.  So String.replaceAll() threw an exception, which got printed in the log.  Ironically, this happens whether or not the actual exception text contained the password - or even whether the log level would lead to the original exception being suppressed.

Granted, this is an example of a truly hazardous API.  (Ask your Java expert friends what the String.replace(String,String) and String.replaceAll(String,String) calls do and see how many can answer correctly.)  But ... that kind of thing does happen, and in the end it's humans, with all our limitations, who have to write and check code.  Every additional line of code can lead to an issue....

BTW, even after fixing this to use the non-obvious String.replace() - and adding (I hope!) a comment warning people not to change it back "to make sure that all occurrences are replaced" - there's a subtle second-order bug, that arises from the very attempt to add this fail-safe:  If the password happens to match a substring of something that legitimately occurs elsewhere in the message - and the message in question contains the user-assigned names of external objects - it will be replaced with asterisks.  Someone knowing the external name and seeing the asterisks immediately knows the password!  Unlikely, granted; but such things do happen.

I suggested that this might not be an isolated instance in the code and we should look for other examples just to be sure.  Ideally, we should have a code-checker rule to warn about it.  But it's not easy, since you need to know something about the intended semantics of that first argument, which can be tricky to determine.  Perhaps better to just forbid all uses of String.replaceAll() and String.replaceFirst() ... you can get to the same code using calls on Matcher, which is what you get from Pattern.compile().  Longer but safe.

From all the evidence we've seen, the NSA didn't uniquely design (well, modify) DES to be "exactly as secure as claimed, no more and no less."  That appears to be their general design approach to all algorithms they've published.  Why?  Who can say ... but one could well argue that part of it is to avoid having extra stuff that comes back to bite you.
                                                        -- Jerry




More information about the cryptography mailing list