27
27
***** END LICENSE BLOCK *****/
28
28
package org .jruby .ext .openssl ;
29
29
30
- import java .security .MessageDigest ;
31
- import java .security .NoSuchAlgorithmException ;
32
- import java .security .NoSuchProviderException ;
33
30
import java .util .ArrayList ;
34
31
import java .util .HashSet ;
35
32
import java .util .List ;
36
33
import java .util .Set ;
37
34
38
- import javax .crypto .spec .IvParameterSpec ;
39
-
35
+ import org .bouncycastle .crypto .BlockCipher ;
36
+ import org .bouncycastle .crypto .BufferedBlockCipher ;
37
+ import org .bouncycastle .crypto .engines .AESEngine ;
38
+ import org .bouncycastle .crypto .engines .BlowfishEngine ;
39
+ import org .bouncycastle .crypto .engines .CAST5Engine ;
40
+ import org .bouncycastle .crypto .engines .CAST6Engine ;
41
+ import org .bouncycastle .crypto .engines .DESEngine ;
42
+ import org .bouncycastle .crypto .engines .DESedeEngine ;
43
+ import org .bouncycastle .crypto .engines .RC2Engine ;
44
+ import org .bouncycastle .crypto .modes .CBCBlockCipher ;
45
+ import org .bouncycastle .crypto .modes .CFBBlockCipher ;
46
+ import org .bouncycastle .crypto .modes .OFBBlockCipher ;
47
+ import org .bouncycastle .crypto .paddings .BlockCipherPadding ;
48
+ import org .bouncycastle .crypto .paddings .ISO10126d2Padding ;
49
+ import org .bouncycastle .crypto .paddings .ISO7816d4Padding ;
50
+ import org .bouncycastle .crypto .paddings .PKCS7Padding ;
51
+ import org .bouncycastle .crypto .paddings .PaddedBufferedBlockCipher ;
52
+ import org .bouncycastle .crypto .params .DESParameters ;
53
+ import org .bouncycastle .crypto .params .KeyParameter ;
54
+ import org .bouncycastle .crypto .params .ParametersWithIV ;
40
55
import org .jruby .Ruby ;
41
56
import org .jruby .RubyClass ;
42
57
import org .jruby .RubyModule ;
@@ -179,14 +194,112 @@ public static IRubyObject ciphers(IRubyObject recv) {
179
194
}
180
195
return recv .getRuntime ().newArray (ciphers );
181
196
}
197
+
198
+ public static BlockCipherPadding getBlockCipherPadding (String pad ) {
199
+ // get appropriate padding
200
+ if (pad .equals ("PKCS5Padding" )) {
201
+ return new PKCS7Padding ();
202
+ } else if (pad .equals ("PKCS7Padding" )) {
203
+ return new PKCS7Padding ();
204
+ } else if (pad .equals ("ISO10126Padding" )) {
205
+ return new ISO10126d2Padding ();
206
+ } else if (pad .equals ("ISO7816Padding" )) {
207
+ return new ISO7816d4Padding ();
208
+ } else if (pad .equals ("NoPadding" )) {
209
+ return null ;
210
+ } else {
211
+ // FIXME should be a ruby exception
212
+ throw new RuntimeException ("Unknown padding: " + pad );
213
+ }
214
+ }
215
+
216
+ public static BlockCipher getBlockCipher (String cipherBase , String cipherVersion , String cipherMode ) {
217
+ BlockCipher cipher ;
218
+
219
+ // get base cipher
220
+ if (cipherBase .equals ("AES" )) {
221
+ cipher = new AESEngine ();
222
+ } else if (cipherBase .equals ("aes" )) {
223
+ cipher = new AESEngine ();
224
+ } else if (cipherBase .equals ("DES" ) || cipherBase .equals ("des" )) {
225
+ if (cipherVersion != null && cipherVersion .equals ("EDE3" )) {
226
+ cipher = new DESedeEngine ();
227
+ } else {
228
+ cipher = new DESEngine ();
229
+ }
230
+ } else if (cipherBase .equals ("Blowfish" )) {
231
+ cipher = new BlowfishEngine ();
232
+ } else if (cipherBase .equals ("BLOWFISH" )) {
233
+ cipher = new BlowfishEngine ();
234
+ } else if (cipherBase .equals ("blowfish" )) {
235
+ cipher = new BlowfishEngine ();
236
+ } else if (cipherBase .equals ("RC2" )) {
237
+ cipher = new RC2Engine ();
238
+ } else if (cipherBase .equals ("rc2" )) {
239
+ cipher = new RC2Engine ();
240
+ } else if (cipherBase .equals ("CAST5" )) {
241
+ cipher = new CAST5Engine ();
242
+ } else if (cipherBase .equals ("cast5" )) {
243
+ cipher = new CAST5Engine ();
244
+ } else if (cipherBase .equals ("CAST6" )) {
245
+ cipher = new CAST6Engine ();
246
+ } else if (cipherBase .equals ("cast6" )) {
247
+ cipher = new CAST6Engine ();
248
+ } else {
249
+ // FIXME should be a ruby exception
250
+ return null ;
251
+ }
252
+
253
+ // Wrap with mode-specific cipher
254
+ if (cipherMode .equalsIgnoreCase ("CBC" )) {
255
+ cipher = new CBCBlockCipher (cipher );
256
+ } else if (cipherMode .equalsIgnoreCase ("CFB" )) {
257
+ // FIXME: I have no number to put here! I'm using 8.
258
+ cipher = new CFBBlockCipher (cipher , 8 );
259
+ } else if (cipherMode .equalsIgnoreCase ("CFB1" )) {
260
+ // FIXME: Does 1 mean 1 * 8?
261
+ cipher = new CFBBlockCipher (cipher , 8 );
262
+ } else if (cipherMode .equalsIgnoreCase ("CFB8" )) {
263
+ // FIXME: Does 8 mean 8 * 8?
264
+ cipher = new CFBBlockCipher (cipher , 8 * 8 );
265
+ } else if (cipherMode .equalsIgnoreCase ("OFB" )) {
266
+ // FIXME: I have no number to put here! I'm using 8.
267
+ cipher = new OFBBlockCipher (cipher , 8 );
268
+ }
269
+
270
+ return cipher ;
271
+ }
272
+
273
+ public static BufferedBlockCipher getCipher (Ruby runtime , String cipherBase , String cipherVersion , String cipherMode , String cipherPad ) {
274
+ BlockCipherPadding padding = getBlockCipherPadding (cipherPad );
275
+ BlockCipher cipher = getBlockCipher (cipherBase , cipherVersion , cipherMode );
276
+
277
+ if (cipher != null ) {
278
+ if (!"ECB" .equalsIgnoreCase (cipherMode ) && padding != null ) {
279
+ return new PaddedBufferedBlockCipher (cipher , padding );
280
+ } else {
281
+ return new BufferedBlockCipher (cipher );
282
+ }
283
+ } else {
284
+ throw runtime .newLoadError ("unsupported cipher algorithm (" + cipherBase + "-" + cipherMode + "-" + cipherPad + ")" );
285
+ }
286
+ }
287
+
288
+ public static KeyParameter getKeyParameter (String cipherBase , byte [] key ) {
289
+ if (cipherBase .equals ("DES" )) {
290
+ return new DESParameters (key );
291
+ } else {
292
+ return new KeyParameter (key );
293
+ }
294
+ }
182
295
183
296
private RubyClass ciphErr ;
184
297
public Cipher (Ruby runtime , RubyClass type ) {
185
298
super (runtime ,type );
186
299
ciphErr = (RubyClass )(((RubyModule )(getRuntime ().getModule ("OpenSSL" ).getConstant ("Cipher" ))).getConstant ("CipherError" ));
187
300
}
188
301
189
- private javax . crypto . Cipher ciph ;
302
+ private BufferedBlockCipher ciph ;
190
303
private String name ;
191
304
private String cryptoBase ;
192
305
private String cryptoVersion ;
@@ -211,15 +324,7 @@ public IRubyObject initialize(IRubyObject str, Block unusedBlock) {
211
324
realName = values [3 ];
212
325
padding_type = values [4 ];
213
326
214
- try {
215
- ciph = javax .crypto .Cipher .getInstance (realName ,"BC" );
216
- } catch (NoSuchAlgorithmException e ) {
217
- throw getRuntime ().newLoadError ("unsupported cipher algorithm (" + realName + ")" );
218
- } catch (NoSuchProviderException e ) {
219
- throw getRuntime ().newLoadError ("unsupported cipher algorithm (" + realName + ")" );
220
- } catch (javax .crypto .NoSuchPaddingException e ) {
221
- throw getRuntime ().newLoadError ("unsupported cipher padding (" + realName + ")" );
222
- }
327
+ ciph = getCipher (getRuntime (), cryptoBase , cryptoVersion , cryptoMode , padding_type );
223
328
224
329
if (hasLen () && null != cryptoVersion ) {
225
330
try {
@@ -228,6 +333,7 @@ public IRubyObject initialize(IRubyObject str, Block unusedBlock) {
228
333
keyLen = -1 ;
229
334
}
230
335
}
336
+
231
337
if (keyLen == -1 ) {
232
338
if ("DES" .equalsIgnoreCase (cryptoBase )) {
233
339
if ("EDE3" .equalsIgnoreCase (cryptoVersion )) {
@@ -281,15 +387,7 @@ public IRubyObject initialize_copy(IRubyObject obj) {
281
387
}
282
388
padding = ((Cipher )obj ).padding ;
283
389
284
- try {
285
- ciph = javax .crypto .Cipher .getInstance (realName ,"BC" );
286
- } catch (NoSuchAlgorithmException e ) {
287
- throw getRuntime ().newLoadError ("unsupported cipher algorithm (" + realName + ")" );
288
- } catch (NoSuchProviderException e ) {
289
- throw getRuntime ().newLoadError ("unsupported cipher algorithm (" + realName + ")" );
290
- } catch (javax .crypto .NoSuchPaddingException e ) {
291
- throw getRuntime ().newLoadError ("unsupported cipher padding (" + realName + ")" );
292
- }
390
+ ciph = getCipher (getRuntime (), cryptoBase , cryptoVersion , cryptoMode , padding_type );
293
391
294
392
return this ;
295
393
}
@@ -378,7 +476,7 @@ public IRubyObject pkcs5_keyivgen(IRubyObject[] args) {
378
476
byte [] salt = null ;
379
477
int iter = 2048 ;
380
478
IRubyObject vdigest = getRuntime ().getNil ();
381
- MessageDigest digest = null ;
479
+ org . bouncycastle . crypto . Digest digest = null ;
382
480
if (args .length >1 ) {
383
481
if (!args [1 ].isNil ()) {
384
482
salt = args [1 ].convertToString ().getBytes ();;
@@ -399,9 +497,9 @@ public IRubyObject pkcs5_keyivgen(IRubyObject[] args) {
399
497
}
400
498
}
401
499
if (vdigest .isNil ()) {
402
- digest = MessageDigest . getInstance ( "MD5" , "BC " );
500
+ digest = Digest . getDigest ( getRuntime (), "MD5 " );
403
501
} else {
404
- digest = MessageDigest . getInstance ((( Digest )vdigest ).getAlgorithm (), "BC" );
502
+ digest = Digest . getDigest ( getRuntime (), (( Digest )vdigest ).getAlgorithm ());
405
503
}
406
504
407
505
OpenSSLImpl .KeyAndIv result = OpenSSLImpl .EVP_BytesToKey (keyLen /8 ,ivLen /8 ,digest ,salt ,pass ,iter );
@@ -420,12 +518,12 @@ public IRubyObject pkcs5_keyivgen(IRubyObject[] args) {
420
518
private void doInitialize () {
421
519
ciphInited = true ;
422
520
try {
423
- assert key .length * 8 = = keyLen : "Key wrong length" ;
424
- assert iv .length * 8 = = ivLen : "IV wrong length" ;
521
+ assert key .length * 8 > = keyLen : "Key wrong length" ;
522
+ assert iv .length * 8 > = ivLen : "IV wrong length" ;
425
523
if (!"ECB" .equalsIgnoreCase (cryptoMode ) && this .iv != null ) {
426
- this .ciph .init (encryptMode ? javax . crypto . Cipher . ENCRYPT_MODE : javax . crypto . Cipher . DECRYPT_MODE , new SimpleSecretKey ( this . key ), new IvParameterSpec ( this . iv ));
524
+ this .ciph .init (encryptMode , new ParametersWithIV ( getKeyParameter ( cryptoBase , key ), iv ));
427
525
} else {
428
- this .ciph .init (encryptMode ? javax . crypto . Cipher . ENCRYPT_MODE : javax . crypto . Cipher . DECRYPT_MODE , new SimpleSecretKey ( this . key ));
526
+ this .ciph .init (encryptMode , getKeyParameter ( cryptoBase , key ));
429
527
}
430
528
} catch (Exception e ) {
431
529
e .printStackTrace ();
@@ -445,17 +543,19 @@ public IRubyObject update(IRubyObject data) {
445
543
}
446
544
447
545
byte [] str = new byte [0 ];
546
+ int count ;
448
547
try {
449
- byte [] out = ciph .update (val );
450
- if (out != null ) {
548
+ byte [] out = new byte [ciph .getUpdateOutputSize (val .length )];
549
+ count = ciph .processBytes (val , 0 , val .length , out , 0 );
550
+ if (count != 0 ) {
451
551
str = out ;
452
552
}
453
553
} catch (Exception e ) {
454
554
e .printStackTrace ();
455
555
throw new RaiseException (getRuntime (), ciphErr , null , true );
456
556
}
457
557
458
- return RubyString .newString (getRuntime (), new ByteList (str ,false ));
558
+ return RubyString .newString (getRuntime (), new ByteList (str , 0 , count , false ));
459
559
}
460
560
461
561
public IRubyObject update_deprecated (IRubyObject data ) {
@@ -470,8 +570,10 @@ public IRubyObject _final() {
470
570
471
571
//TODO: implement correctly
472
572
ByteList str = new ByteList (ByteList .NULL_ARRAY );
573
+ int count ;
473
574
try {
474
- byte [] out = ciph .doFinal ();
575
+ byte [] out = new byte [ciph .getOutputSize (0 )];
576
+ count = ciph .doFinal (out , 0 );
475
577
if (out != null ) {
476
578
str = new ByteList (out ,false );
477
579
}
@@ -480,7 +582,7 @@ public IRubyObject _final() {
480
582
throw new RaiseException (getRuntime (), ciphErr , null , true );
481
583
}
482
584
483
- return getRuntime ().newString (str );
585
+ return getRuntime ().newString (new ByteList ( str , 0 , count ) );
484
586
}
485
587
486
588
public IRubyObject set_padding (IRubyObject padding ) {
@@ -490,7 +592,7 @@ public IRubyObject set_padding(IRubyObject padding) {
490
592
}
491
593
492
594
String getAlgorithm () {
493
- return this .ciph .getAlgorithm ();
595
+ return this .ciph .getUnderlyingCipher (). getAlgorithmName ();
494
596
}
495
597
}
496
598
0 commit comments