PageRenderTime 302ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/test/java/org/bouncycastle/crypto/test/CipherStreamTest.java

https://gitlab.com/edgardo001/bc-java
Java | 706 lines | 577 code | 101 blank | 28 comment | 78 complexity | e4c09bb1c372f5443495e576241fb6fc MD5 | raw file
  1. package org.bouncycastle.crypto.test;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.InputStream;
  5. import java.io.OutputStream;
  6. import java.security.SecureRandom;
  7. import org.bouncycastle.crypto.BlockCipher;
  8. import org.bouncycastle.crypto.BufferedBlockCipher;
  9. import org.bouncycastle.crypto.CipherParameters;
  10. import org.bouncycastle.crypto.StreamCipher;
  11. import org.bouncycastle.crypto.engines.AESEngine;
  12. import org.bouncycastle.crypto.engines.BlowfishEngine;
  13. import org.bouncycastle.crypto.engines.CAST5Engine;
  14. import org.bouncycastle.crypto.engines.CAST6Engine;
  15. import org.bouncycastle.crypto.engines.CamelliaEngine;
  16. import org.bouncycastle.crypto.engines.ChaChaEngine;
  17. import org.bouncycastle.crypto.engines.DESEngine;
  18. import org.bouncycastle.crypto.engines.DESedeEngine;
  19. import org.bouncycastle.crypto.engines.Grain128Engine;
  20. import org.bouncycastle.crypto.engines.Grainv1Engine;
  21. import org.bouncycastle.crypto.engines.HC128Engine;
  22. import org.bouncycastle.crypto.engines.HC256Engine;
  23. import org.bouncycastle.crypto.engines.NoekeonEngine;
  24. import org.bouncycastle.crypto.engines.RC2Engine;
  25. import org.bouncycastle.crypto.engines.RC4Engine;
  26. import org.bouncycastle.crypto.engines.RC6Engine;
  27. import org.bouncycastle.crypto.engines.SEEDEngine;
  28. import org.bouncycastle.crypto.engines.Salsa20Engine;
  29. import org.bouncycastle.crypto.engines.SerpentEngine;
  30. import org.bouncycastle.crypto.engines.TEAEngine;
  31. import org.bouncycastle.crypto.engines.ThreefishEngine;
  32. import org.bouncycastle.crypto.engines.TwofishEngine;
  33. import org.bouncycastle.crypto.engines.XSalsa20Engine;
  34. import org.bouncycastle.crypto.engines.XTEAEngine;
  35. import org.bouncycastle.crypto.io.CipherInputStream;
  36. import org.bouncycastle.crypto.io.CipherOutputStream;
  37. import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
  38. import org.bouncycastle.crypto.modes.AEADBlockCipher;
  39. import org.bouncycastle.crypto.modes.CBCBlockCipher;
  40. import org.bouncycastle.crypto.modes.CCMBlockCipher;
  41. import org.bouncycastle.crypto.modes.CFBBlockCipher;
  42. import org.bouncycastle.crypto.modes.CTSBlockCipher;
  43. import org.bouncycastle.crypto.modes.EAXBlockCipher;
  44. import org.bouncycastle.crypto.modes.GCMBlockCipher;
  45. import org.bouncycastle.crypto.modes.NISTCTSBlockCipher;
  46. import org.bouncycastle.crypto.modes.OCBBlockCipher;
  47. import org.bouncycastle.crypto.modes.OFBBlockCipher;
  48. import org.bouncycastle.crypto.modes.SICBlockCipher;
  49. import org.bouncycastle.crypto.paddings.PKCS7Padding;
  50. import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
  51. import org.bouncycastle.crypto.params.KeyParameter;
  52. import org.bouncycastle.crypto.params.ParametersWithIV;
  53. import org.bouncycastle.util.Arrays;
  54. import org.bouncycastle.util.test.SimpleTest;
  55. public class CipherStreamTest
  56. extends SimpleTest
  57. {
  58. private int streamSize;
  59. public String getName()
  60. {
  61. return "CipherStreamTest";
  62. }
  63. private void testMode(Object cipher, CipherParameters params)
  64. throws Exception
  65. {
  66. testWriteRead(cipher, params, false);
  67. testWriteRead(cipher, params, true);
  68. testReadWrite(cipher, params, false);
  69. testReadWrite(cipher, params, true);
  70. if (!(cipher instanceof CTSBlockCipher || cipher instanceof NISTCTSBlockCipher))
  71. {
  72. testWriteReadEmpty(cipher, params, false);
  73. testWriteReadEmpty(cipher, params, true);
  74. }
  75. if (cipher instanceof AEADBlockCipher)
  76. {
  77. testTamperedRead((AEADBlockCipher)cipher, params);
  78. testTruncatedRead((AEADBlockCipher)cipher, params);
  79. testTamperedWrite((AEADBlockCipher)cipher, params);
  80. }
  81. }
  82. private OutputStream createCipherOutputStream(OutputStream output, Object cipher)
  83. {
  84. if (cipher instanceof BufferedBlockCipher)
  85. {
  86. return new CipherOutputStream(output, (BufferedBlockCipher)cipher);
  87. }
  88. else if (cipher instanceof AEADBlockCipher)
  89. {
  90. return new CipherOutputStream(output, (AEADBlockCipher)cipher);
  91. }
  92. else
  93. {
  94. return new CipherOutputStream(output, (StreamCipher)cipher);
  95. }
  96. }
  97. private InputStream createCipherInputStream(byte[] data, Object cipher)
  98. {
  99. ByteArrayInputStream input = new ByteArrayInputStream(data);
  100. if (cipher instanceof BufferedBlockCipher)
  101. {
  102. return new CipherInputStream(input, (BufferedBlockCipher)cipher);
  103. }
  104. else if (cipher instanceof AEADBlockCipher)
  105. {
  106. return new CipherInputStream(input, (AEADBlockCipher)cipher);
  107. }
  108. else
  109. {
  110. return new CipherInputStream(input, (StreamCipher)cipher);
  111. }
  112. }
  113. /**
  114. * Test tampering of ciphertext followed by read from decrypting CipherInputStream
  115. */
  116. private void testTamperedRead(AEADBlockCipher cipher, CipherParameters params)
  117. throws Exception
  118. {
  119. cipher.init(true, params);
  120. byte[] ciphertext = new byte[cipher.getOutputSize(streamSize)];
  121. cipher.doFinal(ciphertext, cipher.processBytes(new byte[streamSize], 0, streamSize, ciphertext, 0));
  122. // Tamper
  123. ciphertext[0] += 1;
  124. cipher.init(false, params);
  125. InputStream input = createCipherInputStream(ciphertext, cipher);
  126. try
  127. {
  128. while (input.read() >= 0)
  129. {
  130. }
  131. fail("Expected invalid ciphertext after tamper and read : " + cipher.getAlgorithmName());
  132. }
  133. catch (InvalidCipherTextIOException e)
  134. {
  135. // Expected
  136. }
  137. try
  138. {
  139. input.close();
  140. }
  141. catch (Exception e)
  142. {
  143. fail("Unexpected exception after tamper and read : " + cipher.getAlgorithmName());
  144. }
  145. }
  146. /**
  147. * Test truncation of ciphertext to make tag calculation impossible, followed by read from
  148. * decrypting CipherInputStream
  149. */
  150. private void testTruncatedRead(AEADBlockCipher cipher, CipherParameters params)
  151. throws Exception
  152. {
  153. cipher.init(true, params);
  154. byte[] ciphertext = new byte[cipher.getOutputSize(streamSize)];
  155. cipher.doFinal(ciphertext, cipher.processBytes(new byte[streamSize], 0, streamSize, ciphertext, 0));
  156. // Truncate to just smaller than complete tag
  157. byte[] truncated = new byte[ciphertext.length - streamSize - 1];
  158. System.arraycopy(ciphertext, 0, truncated, 0, truncated.length);
  159. cipher.init(false, params);
  160. InputStream input = createCipherInputStream(truncated, cipher);
  161. while (true)
  162. {
  163. int read = 0;
  164. try
  165. {
  166. read = input.read();
  167. }
  168. catch (InvalidCipherTextIOException e)
  169. {
  170. // Expected
  171. break;
  172. }
  173. catch (Exception e)
  174. {
  175. fail("Unexpected exception on truncated read : " + cipher.getAlgorithmName());
  176. break;
  177. }
  178. if (read < 0)
  179. {
  180. fail("Expected invalid ciphertext after truncate and read : " + cipher.getAlgorithmName());
  181. break;
  182. }
  183. }
  184. try
  185. {
  186. input.close();
  187. }
  188. catch (Exception e)
  189. {
  190. fail("Unexpected exception after truncate and read : " + cipher.getAlgorithmName());
  191. }
  192. }
  193. /**
  194. * Test tampering of ciphertext followed by write to decrypting CipherOutputStream
  195. */
  196. private void testTamperedWrite(AEADBlockCipher cipher, CipherParameters params)
  197. throws Exception
  198. {
  199. cipher.init(true, params);
  200. byte[] ciphertext = new byte[cipher.getOutputSize(streamSize)];
  201. cipher.doFinal(ciphertext, cipher.processBytes(new byte[streamSize], 0, streamSize, ciphertext, 0));
  202. // Tamper
  203. ciphertext[0] += 1;
  204. cipher.init(false, params);
  205. ByteArrayOutputStream plaintext = new ByteArrayOutputStream();
  206. OutputStream output = createCipherOutputStream(plaintext, cipher);
  207. for (int i = 0; i < ciphertext.length; i++)
  208. {
  209. output.write(ciphertext[i]);
  210. }
  211. try
  212. {
  213. output.close();
  214. fail("Expected invalid ciphertext after tamper and write : " + cipher.getAlgorithmName());
  215. }
  216. catch (InvalidCipherTextIOException e)
  217. {
  218. // Expected
  219. }
  220. }
  221. /**
  222. * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
  223. */
  224. private void testWriteRead(Object cipher, CipherParameters params, boolean blocks)
  225. throws Exception
  226. {
  227. byte[] data = new byte[streamSize];
  228. for (int i = 0; i < data.length; i++)
  229. {
  230. data[i] = (byte)(i % 255);
  231. }
  232. testWriteRead(cipher, params, blocks, data);
  233. }
  234. /**
  235. * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
  236. */
  237. private void testWriteReadEmpty(Object cipher, CipherParameters params, boolean blocks)
  238. throws Exception
  239. {
  240. byte[] data = new byte[0];
  241. testWriteRead(cipher, params, blocks, data);
  242. }
  243. private void testWriteRead(Object cipher, CipherParameters params, boolean blocks, byte[] data)
  244. {
  245. ByteArrayOutputStream bOut = new ByteArrayOutputStream();
  246. try
  247. {
  248. init(cipher, true, params);
  249. OutputStream cOut = createCipherOutputStream(bOut, cipher);
  250. if (blocks)
  251. {
  252. int chunkSize = Math.max(1, data.length / 8);
  253. for (int i = 0; i < data.length; i += chunkSize)
  254. {
  255. cOut.write(data, i, Math.min(chunkSize, data.length - i));
  256. }
  257. }
  258. else
  259. {
  260. for (int i = 0; i < data.length; i++)
  261. {
  262. cOut.write(data[i]);
  263. }
  264. }
  265. cOut.close();
  266. byte[] cipherText = bOut.toByteArray();
  267. bOut.reset();
  268. init(cipher, false, params);
  269. InputStream cIn = createCipherInputStream(cipherText, cipher);
  270. if (blocks)
  271. {
  272. byte[] block = new byte[getBlockSize(cipher) + 1];
  273. int c;
  274. while ((c = cIn.read(block)) >= 0)
  275. {
  276. bOut.write(block, 0, c);
  277. }
  278. }
  279. else
  280. {
  281. int c;
  282. while ((c = cIn.read()) >= 0)
  283. {
  284. bOut.write(c);
  285. }
  286. }
  287. cIn.close();
  288. }
  289. catch (Exception e)
  290. {
  291. fail("Unexpected exception " + getName(cipher), e);
  292. }
  293. byte[] decrypted = bOut.toByteArray();
  294. if (!Arrays.areEqual(data, decrypted))
  295. {
  296. fail("Failed - decrypted data doesn't match: " + getName(cipher));
  297. }
  298. }
  299. private String getName(Object cipher)
  300. {
  301. if (cipher instanceof BufferedBlockCipher)
  302. {
  303. return ((BufferedBlockCipher)cipher).getUnderlyingCipher().getAlgorithmName();
  304. }
  305. else if (cipher instanceof AEADBlockCipher)
  306. {
  307. return ((AEADBlockCipher)cipher).getUnderlyingCipher().getAlgorithmName();
  308. }
  309. else if (cipher instanceof StreamCipher)
  310. {
  311. return ((StreamCipher)cipher).getAlgorithmName();
  312. }
  313. return null;
  314. }
  315. private int getBlockSize(Object cipher)
  316. {
  317. if (cipher instanceof BlockCipher)
  318. {
  319. return ((BlockCipher)cipher).getBlockSize();
  320. }
  321. else if (cipher instanceof BufferedBlockCipher)
  322. {
  323. return ((BufferedBlockCipher)cipher).getBlockSize();
  324. }
  325. else if (cipher instanceof AEADBlockCipher)
  326. {
  327. return ((AEADBlockCipher)cipher).getUnderlyingCipher().getBlockSize();
  328. }
  329. else if (cipher instanceof StreamCipher)
  330. {
  331. return 1;
  332. }
  333. return 0;
  334. }
  335. private void init(Object cipher, boolean forEncrypt, CipherParameters params)
  336. {
  337. if (cipher instanceof BufferedBlockCipher)
  338. {
  339. ((BufferedBlockCipher)cipher).init(forEncrypt, params);
  340. }
  341. else if (cipher instanceof AEADBlockCipher)
  342. {
  343. ((AEADBlockCipher)cipher).init(forEncrypt, params);
  344. }
  345. else if (cipher instanceof StreamCipher)
  346. {
  347. ((StreamCipher)cipher).init(forEncrypt, params);
  348. }
  349. }
  350. protected void fail(String message, boolean authenticated, boolean bc)
  351. {
  352. if (bc || !authenticated)
  353. {
  354. super.fail(message);
  355. }
  356. else
  357. {
  358. // javax.crypto.CipherInputStream/CipherOutputStream
  359. // are broken wrt handling AEAD failures
  360. System.err.println("Broken JCE Streams: " + message);
  361. }
  362. }
  363. /**
  364. * Test CipherInputStream in ENCRYPT_MODE, CipherOutputStream in DECRYPT_MODE
  365. */
  366. private void testReadWrite(Object cipher, CipherParameters params, boolean blocks)
  367. throws Exception
  368. {
  369. String lCode = "ABCDEFGHIJKLMNOPQRSTU";
  370. ByteArrayOutputStream bOut = new ByteArrayOutputStream();
  371. try
  372. {
  373. init(cipher, true, params);
  374. InputStream cIn = createCipherInputStream(lCode.getBytes(), cipher);
  375. ByteArrayOutputStream ct = new ByteArrayOutputStream();
  376. if (blocks)
  377. {
  378. byte[] block = new byte[getBlockSize(cipher) + 1];
  379. int c;
  380. while ((c = cIn.read(block)) >= 0)
  381. {
  382. ct.write(block, 0, c);
  383. }
  384. }
  385. else
  386. {
  387. int c;
  388. while ((c = cIn.read()) >= 0)
  389. {
  390. ct.write(c);
  391. }
  392. }
  393. cIn.close();
  394. init(cipher, false, params);
  395. ByteArrayInputStream dataIn = new ByteArrayInputStream(ct.toByteArray());
  396. OutputStream cOut = createCipherOutputStream(bOut, cipher);
  397. if (blocks)
  398. {
  399. byte[] block = new byte[getBlockSize(cipher) + 1];
  400. int c;
  401. while ((c = dataIn.read(block)) >= 0)
  402. {
  403. cOut.write(block, 0, c);
  404. }
  405. }
  406. else
  407. {
  408. int c;
  409. while ((c = dataIn.read()) >= 0)
  410. {
  411. cOut.write(c);
  412. }
  413. }
  414. cOut.flush();
  415. cOut.close();
  416. }
  417. catch (Exception e)
  418. {
  419. fail("Unexpected exception " + getName(cipher), e);
  420. }
  421. String res = new String(bOut.toByteArray());
  422. if (!res.equals(lCode))
  423. {
  424. fail("Failed read/write - decrypted data doesn't match: " + getName(cipher), lCode, res);
  425. }
  426. }
  427. public void performTest()
  428. throws Exception
  429. {
  430. int[] testSizes = new int[]{0, 1, 7, 8, 9, 15, 16, 17, 1023, 1024, 1025, 2047, 2048, 2049, 4095, 4096, 4097};
  431. for (int i = 0; i < testSizes.length; i++)
  432. {
  433. this.streamSize = testSizes[i];
  434. performTests();
  435. }
  436. }
  437. private void performTests()
  438. throws Exception
  439. {
  440. testModes(new BlowfishEngine(), new BlowfishEngine(), 16);
  441. testModes(new DESEngine(), new DESEngine(), 8);
  442. testModes(new DESedeEngine(), new DESedeEngine(), 24);
  443. testModes(new TEAEngine(), new TEAEngine(), 16);
  444. testModes(new CAST5Engine(), new CAST5Engine(), 16);
  445. testModes(new RC2Engine(), new RC2Engine(), 16);
  446. testModes(new XTEAEngine(), new XTEAEngine(), 16);
  447. testModes(new AESEngine(), new AESEngine(), 16);
  448. testModes(new NoekeonEngine(), new NoekeonEngine(), 16);
  449. testModes(new TwofishEngine(), new TwofishEngine(), 16);
  450. testModes(new CAST6Engine(), new CAST6Engine(), 16);
  451. testModes(new SEEDEngine(), new SEEDEngine(), 16);
  452. testModes(new SerpentEngine(), new SerpentEngine(), 16);
  453. testModes(new RC6Engine(), new RC6Engine(), 16);
  454. testModes(new CamelliaEngine(), new CamelliaEngine(), 16);
  455. testModes(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512),
  456. new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512), 64);
  457. testMode(new RC4Engine(), new KeyParameter(new byte[16]));
  458. testMode(new Salsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
  459. testMode(new XSalsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[32]), new byte[24]));
  460. testMode(new ChaChaEngine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
  461. testMode(new Grainv1Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
  462. testMode(new Grain128Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[12]));
  463. testMode(new HC128Engine(), new KeyParameter(new byte[16]));
  464. testMode(new HC256Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
  465. testSkipping(new Salsa20Engine(), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[8]));
  466. testSkipping(new SICBlockCipher(new AESEngine()), new ParametersWithIV(new KeyParameter(new byte[16]), new byte[16]));
  467. }
  468. private void testModes(BlockCipher cipher1, BlockCipher cipher2, int keySize)
  469. throws Exception
  470. {
  471. final KeyParameter key = new KeyParameter(new byte[keySize]);
  472. final int blockSize = getBlockSize(cipher1);
  473. final CipherParameters withIv = new ParametersWithIV(key, new byte[blockSize]);
  474. if (blockSize > 1)
  475. {
  476. testMode(new PaddedBufferedBlockCipher(cipher1, new PKCS7Padding()), key);
  477. testMode(new PaddedBufferedBlockCipher(new CBCBlockCipher(cipher1), new PKCS7Padding()), withIv);
  478. testMode(new BufferedBlockCipher(new OFBBlockCipher(cipher1, blockSize)), withIv);
  479. testMode(new BufferedBlockCipher(new CFBBlockCipher(cipher1, blockSize)), withIv);
  480. testMode(new BufferedBlockCipher(new SICBlockCipher(cipher1)), withIv);
  481. }
  482. // CTS requires at least one block
  483. if (blockSize <= 16 && streamSize >= blockSize)
  484. {
  485. testMode(new CTSBlockCipher(cipher1), key);
  486. }
  487. if (blockSize <= 16 && streamSize >= blockSize)
  488. {
  489. testMode(new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, cipher1), key);
  490. testMode(new NISTCTSBlockCipher(NISTCTSBlockCipher.CS2, cipher1), key);
  491. testMode(new NISTCTSBlockCipher(NISTCTSBlockCipher.CS3, cipher1), key);
  492. }
  493. if (blockSize == 8 || blockSize == 16)
  494. {
  495. testMode(new EAXBlockCipher(cipher1), withIv);
  496. }
  497. if (blockSize == 16)
  498. {
  499. testMode(new CCMBlockCipher(cipher1), new ParametersWithIV(key, new byte[7]));
  500. testMode(new GCMBlockCipher(cipher1), withIv);
  501. testMode(new OCBBlockCipher(cipher1, cipher2), new ParametersWithIV(key, new byte[15]));
  502. }
  503. }
  504. private void testSkipping(StreamCipher cipher, CipherParameters params)
  505. throws Exception
  506. {
  507. ByteArrayOutputStream bOut = new ByteArrayOutputStream();
  508. init(cipher, true, params);
  509. OutputStream cOut = createCipherOutputStream(bOut, cipher);
  510. byte[] data = new byte[5000];
  511. new SecureRandom().nextBytes(data);
  512. cOut.write(data);
  513. cOut.close();
  514. init(cipher, false, params);
  515. InputStream cIn = createCipherInputStream(bOut.toByteArray(), cipher);
  516. long skip = cIn.skip(50);
  517. if (skip != 50)
  518. {
  519. fail("wrong number of bytes skipped: " + skip);
  520. }
  521. byte[] block = new byte[50];
  522. cIn.read(block);
  523. if (!areEqual(data, 50, block, 0))
  524. {
  525. fail("initial skip mismatch");
  526. }
  527. skip = cIn.skip(3000);
  528. if (skip != 3000)
  529. {
  530. fail("wrong number of bytes skipped: " + skip);
  531. }
  532. cIn.read(block);
  533. if (!areEqual(data, 3100, block, 0))
  534. {
  535. fail("second skip mismatch");
  536. }
  537. cipher.reset();
  538. cIn = createCipherInputStream(bOut.toByteArray(), cipher);
  539. if (!cIn.markSupported())
  540. {
  541. fail("marking not supported");
  542. }
  543. cIn.mark(100);
  544. cIn.read(block);
  545. if (!areEqual(data, 0, block, 0))
  546. {
  547. fail("initial mark read failed");
  548. }
  549. cIn.reset();
  550. cIn.read(block);
  551. if (!areEqual(data, 0, block, 0))
  552. {
  553. fail(cipher.getAlgorithmName() + " initial reset read failed");
  554. }
  555. cIn.reset();
  556. cIn.read(block);
  557. cIn.mark(100);
  558. cIn.read(block);
  559. if (!areEqual(data, 50, block, 0))
  560. {
  561. fail("second mark read failed");
  562. }
  563. cIn.reset();
  564. cIn.read(block);
  565. if (!areEqual(data, 50, block, 0))
  566. {
  567. fail(cipher.getAlgorithmName() + " second reset read failed");
  568. }
  569. cIn.mark(3000);
  570. skip = cIn.skip(2050);
  571. if (skip != 2050)
  572. {
  573. fail("wrong number of bytes skipped: " + skip);
  574. }
  575. cIn.reset();
  576. cIn.read(block);
  577. if (!areEqual(data, 100, block, 0))
  578. {
  579. fail(cipher.getAlgorithmName() + " third reset read failed");
  580. }
  581. cIn.read(new byte[2150]);
  582. cIn.reset();
  583. cIn.read(block);
  584. if (!areEqual(data, 100, block, 0))
  585. {
  586. fail(cipher.getAlgorithmName() + " fourth reset read failed");
  587. }
  588. cIn.close();
  589. }
  590. private boolean areEqual(byte[] a, int aOff, byte[] b, int bOff)
  591. {
  592. for (int i = bOff; i != b.length; i++)
  593. {
  594. if (a[aOff + i - bOff] != b[i])
  595. {
  596. return false;
  597. }
  598. }
  599. return true;
  600. }
  601. public static void main(String[] args)
  602. {
  603. runTest(new CipherStreamTest());
  604. }
  605. }