Path: csiph.com!xmission!feeder.erje.net!2.eu.feeder.erje.net!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail From: Robert Klemme Newsgroups: sci.crypt,comp.lang.java.programmer Subject: Re: Why is Java 7 converting the Password toCharArray instead of Byte in it's PBKDF2 function? Date: Tue, 19 Jun 2018 23:15:15 +0200 Lines: 123 Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8; format=flowed Content-Transfer-Encoding: quoted-printable X-Trace: individual.net U/cKzgOvziYJgh9cqtzpjAg5AHIO7jf8u93G+/cM0lL/gblmI= Cancel-Lock: sha1:ngJbbl04eoygGzjkS+K/Z8fFA5M= User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.8.0 In-Reply-To: Content-Language: en-US Xref: csiph.com sci.crypt:43718 comp.lang.java.programmer:38393 On 19.06.2018 12:54, Karl.Frank wrote: > On 18.06.18 18:41, Robert Klemme wrote: >> On 15.05.2018 23:37, Karl.Frank wrote: >>> Holy moley! It's a buggy Java Library I'm currently using!! >> >> You are converting a byte array to a String without explicit mention o= f >> the encoding. Maybe the way Java picks up the default encoding has >> changed between versions. Anyway, this is an unsafe practice. >> > Robert, probably that might be the case. It is, it is. > In regards of cryptography I always tend to stay on the byte level. Thi= s > way my own implementation of HMAC and PBKDF2 has no need to bother with= > string encoding when reading in from stdin or files. Curiously Java's > PBKDF2 expect a string or char array rather than a byte array and > therefore I need the extra conversion. Actually from now on my own > functions will be used instead. The point is not your usage of byte array nor your need for a=20 conversion. The point is that your conversion is unsafe because the=20 default encoding is used implicitly instead of making your choice of=20 encoding explicit. There are various factors that might influence the=20 JVM's choice of default encoding and they might not be the same as=20 govern the encoding of the user input (what you send via echo). This is = a source of errors and the bad thing is it might actually work in one=20 environment - but not in others. Such bugs can go unnoticed for a long=20 time before they hit you. And then you're left with wrongly encoded=20 data which can be very painful. > It might be a different matter if the user type in password and/or salt= > in some GUI. In this case however I would define the kind of strings > accepted and later convert them into byte arrays before processing them= > further. Graphical inputs usually emit String, char[] or CharSequence so you can=20 be sure the input is properly encoded (modulo some issues) and you can=20 pick whatever encoding you want for the transformation into bytes. >> Also, you are reading only once, there is the chance that there is sti= ll >> data in the pipe which you will miss. Even worse, depending on how dat= a >> is written to stdin you might even get different results. This might n= ot >> be an issue for your test program, but could be if you use this code i= n >> production. >> > Well, yes I read only once from stdin and store the result in a byte > array. How do you suppose to read twice? >=20 > The amount of byte to read is exactly defined by >=20 > =C2=A0 int BuffSize =3D System.in.available(); > =C2=A0 byte[] pwByte =3D new byte[BuffSize]; > =C2=A0 System.in.read(pwByte, 0, BuffSize); >=20 > How would the program read more byte than necessary? Please read carefully: I wrote it might read _less_ bytes. You should=20 really read the JavaDocs of InputStream#available(). For example it is=20 perfectly legal for an implementation to always return 0 yet still yield = data via read(). Basically, available() is quite useless most of the=20 time if you want to read the complete input anyway. > As data on stdin is always byte and all my programs read in byte and > never ever strings in my opinion it doesn't matter how these byte are > written and piped by any previous instance or program. Again, the point is a different one: your reading does not ensure you=20 get the complete input. For this you need a loop that reads until EOF.=20 Using just a single read() is not sufficient as an implementation is=20 free to choose how many bytes to return for a single read(). There is no way you can get away without a loop. final byte[] buffer =3D new byte[1024]; final ByteArrayOutputStream out =3D new ByteArrayOutputStream(); for (int len; (len =3D in.read(buffer)) !=3D -1;) { out.write(buffer, 0, len); } byte[] data =3D out.toByteArray(); There are other more elegant variants depending on which libs you use=20 and on which Java version you are. >>> So my own implementation works correctly - what a relieve! >> >> There are cases where wrong code emits correct results - and this >> surfaces only later, when the input or some other environmental factor= >> changes. >> > That's one reason why I tend to run large and extensive tests before my= > program go into a production environment. Until today I've never > experienced any real problem. Even in this case I'd implement my own > HMAC and PBKDF2 and therefore found the "buggy" Java lib. Sadly the > developer of the original program some years ago didn't check that and > now everyone is baffled that we can't decrypt old files with the > correctly working PBKDF2 implementation. I am not convinced you have identified a "'buggy' Java lib". Kind regards robert --=20 remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/