@@ -187,7 +187,7 @@ public int getVersion() {
187187 }
188188
189189
190- // TODO: IMPORTANT NOTE: In a future release (hopefully starting in 2.1.1 ),
190+ // TODO: IMPORTANT NOTE: In a future release (hopefully starting in 2.3 ),
191191 // we will be using the 'context' to mix in some additional things. At a
192192 // minimum, we will be using the KDF version (version_) so that downgrade version
193193 // attacks are not possible. Other candidates are the cipher xform and
@@ -267,24 +267,26 @@ public String getContext() {
267267 * @param keySize The cipher's key size (in bits) for the {@code keyDerivationKey}.
268268 * Must have a minimum size of 56 bits and be an integral multiple of 8-bits.
269269 * <b>Note:</b> The derived key will have the same size as this.
270- * @param purpose The purpose for the derived key. For the ESAPI reference implementation,
270+ * @param purpose The purpose for the derived key. <b>IMPORTANT:</b> For the ESAPI reference implementation,
271271 * {@code JavaEncryptor}, this <i>must</i> be either the string "encryption" or
272272 * "authenticity", where "encryption" is used for creating a derived key to use
273273 * for confidentiality, and "authenticity" is used for creating a derived key to
274274 * use with a MAC to ensure message authenticity. However, since parameter serves
275275 * the same purpose as the "Label" in section 5.1 of NIST SP 800-108, it really can
276276 * be set to anything other than {@code null} or an empty string when called outside
277- * of {@code JavaEncryptor}.
277+ * of ESAPI's {@code JavaEncryptor} reference implementation (but you must consistent) .
278278 * @return The derived {@code SecretKey} to be used according
279279 * to the specified purpose.
280280 * @throws NoSuchAlgorithmException The {@code keyDerivationKey} has an unsupported
281- * encryption algorithm or no current JCE provider supports
282- * "HmacSHA1" .
281+ * encryption algorithm or no current JCE provider supports requested
282+ * Hmac algorithrm used for the PRF for key generation .
283283 * @throws EncryptionException If "UTF-8" is not supported as an encoding, then
284284 * this is thrown with the original {@code UnsupportedEncodingException}
285285 * as the cause. (NOTE: This should never happen as "UTF-8" is supposed to
286286 * be a common encoding supported by all Java implementations. Support
287- * for it is usually in rt.jar.)
287+ * for it is usually in rt.jar.) This exception is also thrown if the
288+ * requested {@code keySize} parameter exceeds the length of the number of
289+ * bytes provded in the {@code keyDerivationKey} parameter.
288290 * @throws InvalidKeyException Likely indicates a coding error. Should not happen.
289291 * @throws EncryptionException Throw for some precondition violations.
290292 */
@@ -322,7 +324,29 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri
322324 throw new IllegalArgumentException ("Purpose may not be null or empty." );
323325 }
324326
325- keySize = calcKeySize ( keySize ); // Safely convert to whole # of bytes.
327+ //
328+ // No longer, since we no longer wish to restrict this to use only by JavaEncryptor, so
329+ // we no longer test for this. For details, see the javadoc for '@param purpose', above.
330+ //
331+ /*
332+ *
333+ * if ( ! ( purpose.equals("encryption") || purpose.equals("authenticity") ) ) {
334+ * throw new IllegalArgumentException("Purpose must be \"encryption\" or \"authenticity\".");
335+ * }
336+ *
337+ */
338+
339+ int providedKeyLen = 8 * keyDerivationKey .getEncoded ().length ;
340+ // assert providedKeyLen >= 56 : "Coding error? Length of keyDerivationKey < 56 bits!"; // Ugh. DES compatible.
341+
342+ if ( providedKeyLen < keySize ) {
343+ throw new EncryptionException ("KeyDerivationFunction.computeDerivedKey() not intended for key stretching: " +
344+ "provided key too short (" + providedKeyLen + " bits) to provide " + keySize + " bits." ,
345+ "Key stretching not supported: Provided key, keyDerivationKey, has insufficient entropy (" +
346+ providedKeyLen + " bits) to generate key of requested size of " + keySize + " bits." );
347+ }
348+
349+ keySize = calcKeySize ( keySize ); // Safely convert from bits to a whole # of bytes.
326350 byte [] derivedKey = new byte [ keySize ];
327351 byte [] label ; // Same purpose as NIST SP 800-108's "label" in section 5.1.
328352 byte [] context ; // See setContext() for details.
@@ -344,20 +368,20 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri
344368 // under the hood to create 'sk' so that it is 20 bytes. I cannot vouch
345369 // for how secure this key-stretching is. Worse, it might not be specified
346370 // as to *how* it is done and left to each JCE provider.
347- SecretKey sk = new SecretKeySpec (keyDerivationKey .getEncoded (), "HmacSHA1" );
371+ SecretKey sk = new SecretKeySpec (keyDerivationKey .getEncoded (), prfAlg_ );
348372 Mac mac = null ;
349373
350374 try {
351- mac = Mac .getInstance ("HmacSHA1" );
375+ mac = Mac .getInstance ( prfAlg_ );
352376 mac .init (sk );
353377 } catch ( InvalidKeyException ex ) {
354378 logger .error (Logger .SECURITY_FAILURE ,
355- "Created HmacSHA1 Mac but SecretKey sk has alg " +
379+ "Created " + prfAlg_ + " Mac but SecretKey sk has alg " +
356380 sk .getAlgorithm (), ex );
357381 throw ex ;
358382 }
359383
360- // Repeatedly call of HmacSHA1 hash until we've collected enough bits
384+ // Repeatedly call of PRF Hmac until we've collected enough bits
361385 // for the derived key. The first time through, we calculate the HmacSHA1
362386 // on the "purpose" string, but subsequent calculations are performed
363387 // on the previous result.
@@ -415,6 +439,15 @@ public SecretKey computeDerivedKey(SecretKey keyDerivationKey, int keySize, Stri
415439
416440 // Don't leave remnants of the partial key in memory. (Note: we could
417441 // not do this if tmpKey were declared in the do-while loop.
442+ // Of course, in reality, trying to stomp these bits out is probably not
443+ // realistic because the JIT is likely toing to be smart enough to
444+ // optimze this loop away. We probably could try to outsmart it, by
445+ // (say) writing out the overwritten bits to /dev/null, but then even
446+ // then we'd still probably have to overwrite with random bits rather
447+ // than all null chars. How much is enough? Who knows? But it does point
448+ // to a serious limitation in Java and many other languages that one
449+ // cannot arbitrarily disable the optimizer either at compile time or
450+ // run time because of security reasons. Sigh. At least we've tried.
418451 for ( int i = 0 ; i < tmpKey .length ; i ++ ) {
419452 tmpKey [i ] = '\0' ;
420453 }
0 commit comments