@@ -235,6 +235,7 @@ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
235235 throws IllegalStateException , ShortBufferException {
236236
237237 byte tmp [] = null ;
238+ int returnLen = 0 ;
238239
239240 if (this .state != EngineState .WC_PUBKEY_DONE )
240241 throw new IllegalStateException (
@@ -248,32 +249,56 @@ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
248249 switch (this .type ) {
249250 case WC_DH :
250251
251- if ((sharedSecret .length - offset ) < this .primeLen ) {
252- throw new ShortBufferException (
253- "Input buffer too small when generating " +
254- "shared secret" );
255- }
256-
257252 /* public key has been stored inside this.dh already */
258253 tmp = this .dh .makeSharedSecret ();
259254 if (tmp == null ) {
260255 throw new RuntimeException ("Error when creating DH " +
261256 "shared secret" );
262257 }
263258
264- if ((sharedSecret .length - offset ) < tmp .length ) {
259+ /* DH shared secrets can vary in length depending on if they
260+ * are padded or not at the beginning with zero bytes to make
261+ * a total output size matching the prime length.
262+ *
263+ * Native wolfCrypt does not prepend zero bytes to DH shared
264+ * secrets, following RFC 5246 (8.1.2) which instructs to
265+ * strip leading zero bytes.
266+ *
267+ * Sun KeyAgreement DH implementations as of after Java 8
268+ * prepend zero bytes if total length is not equal to prime
269+ * length. This was changed with OpenJDK bug fix JDK-7146728.
270+ *
271+ * BouncyCastle also behaves the same way, prepending zero
272+ * bytes if total secret size is not prime length. This
273+ * follows RFC 2631 (2.1.2).
274+ *
275+ * To match Sun and BC behavior, we pad the secret to primeLen
276+ * by prepending zeros for both generateSecret() methods.
277+ */
278+ byte [] paddedSecret = new byte [this .primeLen ];
279+ Arrays .fill (paddedSecret , (byte )0 );
280+ System .arraycopy (tmp , 0 , paddedSecret ,
281+ paddedSecret .length - tmp .length , tmp .length );
282+
283+ if ((sharedSecret .length - offset ) < paddedSecret .length ) {
265284 zeroArray (tmp );
285+ zeroArray (paddedSecret );
266286 throw new ShortBufferException (
267287 "Output buffer too small when generating " +
268288 "DH shared secret" );
269289 }
270290
271- /* copy array back to output offset */
272- System .arraycopy (tmp , 0 , sharedSecret , offset , tmp .length );
291+ /* copy padded array back to output offset */
292+ System .arraycopy (paddedSecret , 0 , sharedSecret , offset ,
293+ paddedSecret .length );
294+
295+ returnLen = this .primeLen ;
273296
274297 /* reset state, using same private info and alg params */
275298 this .state = EngineState .WC_PRIVKEY_DONE ;
276299
300+ zeroArray (paddedSecret );
301+
277302 break ;
278303
279304 case WC_ECDH :
@@ -294,6 +319,8 @@ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
294319 /* copy array back to output ofset */
295320 System .arraycopy (tmp , 0 , sharedSecret , offset , tmp .length );
296321
322+ returnLen = tmp .length ;
323+
297324 /* reset state, using same private info and alg params */
298325 byte [] priv = this .ecPrivate .exportPrivate ();
299326 if (priv == null ) {
@@ -313,15 +340,11 @@ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
313340 break ;
314341 };
315342
316- if (tmp != null ) {
317-
318- log ("generated secret, len: " + tmp .length );
343+ log ("generated secret, len: " + returnLen );
319344
320- zeroArray (tmp );
321- return tmp .length ;
322- }
345+ zeroArray (tmp );
323346
324- return 0 ;
347+ return returnLen ;
325348 }
326349
327350 @ Override
0 commit comments