1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /** 19 * @author Vera Y. Petrashkova 20 * @version $Revision$ 21 */ 22 23 package org.apache.harmony.crypto.tests.javax.crypto; 24 25 import java.nio.DirectByteBuffer; 26 import java.security.spec.AlgorithmParameterSpec; 27 import java.security.InvalidAlgorithmParameterException; 28 import java.security.InvalidKeyException; 29 import java.security.Key; 30 import java.security.NoSuchAlgorithmException; 31 import java.security.SecureRandom; 32 import java.security.AlgorithmParameters; 33 import javax.crypto.BadPaddingException; 34 import javax.crypto.IllegalBlockSizeException; 35 import javax.crypto.NoSuchPaddingException; 36 import javax.crypto.ShortBufferException; 37 import javax.crypto.CipherSpi; 38 import java.nio.ByteBuffer; 39 import java.util.concurrent.atomic.AtomicInteger; 40 41 import junit.framework.TestCase; 42 43 44 /** 45 * Tests for <code>CipherSpi</code> class constructors and methods. 46 * 47 */ 48 public class CipherSpiTest extends TestCase { 49 class Mock_CipherSpi extends myCipherSpi { 50 51 @Override 52 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 53 throws IllegalBlockSizeException, BadPaddingException { 54 return super.engineDoFinal(input, inputOffset, inputLen); 55 } 56 57 @Override 58 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, 59 int outputOffset) throws ShortBufferException, IllegalBlockSizeException, 60 BadPaddingException { 61 return super.engineDoFinal(input, inputOffset, inputLen, output, outputOffset); 62 } 63 64 @Override 65 protected int engineGetBlockSize() { 66 return super.engineGetBlockSize(); 67 } 68 69 @Override 70 protected byte[] engineGetIV() { 71 return super.engineGetIV(); 72 } 73 74 @Override 75 protected int engineGetOutputSize(int inputLen) { 76 return super.engineGetOutputSize(inputLen); 77 } 78 79 @Override 80 protected AlgorithmParameters engineGetParameters() { 81 return super.engineGetParameters(); 82 } 83 84 @Override 85 protected void engineInit(int opmode, Key key, SecureRandom random) 86 throws InvalidKeyException { 87 super.engineInit(opmode, key, random); 88 } 89 90 @Override 91 protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, 92 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 93 super.engineInit(opmode, key, params, random); 94 } 95 96 @Override 97 protected void engineInit(int opmode, Key key, AlgorithmParameters params, 98 SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { 99 super.engineInit(opmode, key, params, random); 100 } 101 102 @Override 103 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 104 super.engineSetMode(mode); 105 } 106 107 @Override 108 protected void engineSetPadding(String padding) throws NoSuchPaddingException { 109 super.engineSetPadding(padding); 110 } 111 112 @Override 113 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 114 return super.engineUpdate(input, inputOffset, inputLen); 115 } 116 117 @Override 118 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, 119 int outputOffset) throws ShortBufferException { 120 return super.engineUpdate(input, inputOffset, inputLen, output, outputOffset); 121 } 122 123 @Override 124 protected int engineGetKeySize(Key key) throws InvalidKeyException { 125 return super.engineGetKeySize(key); 126 } 127 128 @Override 129 protected byte[] engineWrap(Key key) throws InvalidKeyException, IllegalBlockSizeException { 130 return super.engineWrap(key); 131 } 132 133 @Override 134 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType) 135 throws InvalidKeyException, NoSuchAlgorithmException { 136 return super.engineUnwrap(wrappedKey, wrappedKeyAlgorithm, wrappedKeyType); 137 } 138 } 139 140 /** 141 * Test for <code>CipherSpi</code> constructor 142 * Assertion: constructs CipherSpi 143 */ 144 public void testCipherSpiTests01() throws IllegalBlockSizeException, 145 BadPaddingException, ShortBufferException { 146 147 Mock_CipherSpi cSpi = new Mock_CipherSpi(); 148 assertEquals("BlockSize is not 0", cSpi.engineGetBlockSize(), 0); 149 assertEquals("OutputSize is not 0", cSpi.engineGetOutputSize(1), 0); 150 byte[] bb = cSpi.engineGetIV(); 151 assertEquals("Length of result byte array is not 0", bb.length, 0); 152 assertNull("Not null result", cSpi.engineGetParameters()); 153 byte[] bb1 = new byte[10]; 154 byte[] bb2 = new byte[10]; 155 bb = cSpi.engineUpdate(bb1, 1, 2); 156 assertEquals("Incorrect result of engineUpdate(byte, int, int)", 157 bb.length, 2); 158 bb = cSpi.engineDoFinal(bb1, 1, 2); 159 assertEquals("Incorrect result of engineDoFinal(byte, int, int)", 2, 160 bb.length); 161 assertEquals( 162 "Incorrect result of engineUpdate(byte, int, int, byte, int)", 163 cSpi.engineUpdate(bb1, 1, 2, bb2, 7), 2); 164 assertEquals( 165 "Incorrect result of engineDoFinal(byte, int, int, byte, int)", 166 2, cSpi.engineDoFinal(bb1, 1, 2, bb2, 0)); 167 } 168 169 /** 170 * Test for <code>engineGetKeySize(Key)</code> method 171 * Assertion: It throws UnsupportedOperationException if it is not overridden 172 */ 173 public void testCipherSpi02() throws Exception { 174 Mock_CipherSpi cSpi = new Mock_CipherSpi(); 175 try { 176 cSpi.engineGetKeySize(null); 177 fail("UnsupportedOperationException must be thrown"); 178 } catch (UnsupportedOperationException e) { 179 } 180 } 181 182 /** 183 * Test for <code>engineWrap(Key)</code> method 184 * Assertion: It throws UnsupportedOperationException if it is not overridden 185 */ 186 public void testCipherSpi03() throws Exception { 187 Mock_CipherSpi cSpi = new Mock_CipherSpi(); 188 try { 189 cSpi.engineWrap(null); 190 fail("UnsupportedOperationException must be thrown"); 191 } catch (UnsupportedOperationException e) { 192 } 193 } 194 195 /** 196 * Test for <code>engineUnwrap(byte[], String, int)</code> method 197 * Assertion: It throws UnsupportedOperationException if it is not overridden 198 */ 199 public void testCipherSpi04() throws Exception { 200 Mock_CipherSpi cSpi = new Mock_CipherSpi(); 201 try { 202 cSpi.engineUnwrap(new byte[0], "", 0); 203 fail("UnsupportedOperationException must be thrown"); 204 } catch (UnsupportedOperationException e) { 205 } 206 } 207 208 /** 209 * Test for <code>engineUpdate(ByteBuffer, ByteBuffer)</code> method 210 * Assertions: 211 * throws NullPointerException if one of these buffers is null; 212 * throws ShortBufferException is there is no space in output to hold result 213 */ 214 public void testCipherSpi05() throws ShortBufferException { 215 Mock_CipherSpi cSpi = new Mock_CipherSpi(); 216 byte[] bb = { (byte) 0, (byte) 1, (byte) 2, (byte) 3, (byte) 4, 217 (byte) 5, (byte) 6, (byte) 7, (byte) 8, (byte) 9, (byte) 10 }; 218 int pos = 5; 219 int len = bb.length; 220 ByteBuffer bbNull = null; 221 ByteBuffer bb1 = ByteBuffer.allocate(len); 222 bb1.put(bb); 223 bb1.position(0); 224 try { 225 cSpi.engineUpdate(bbNull, bb1); 226 fail("NullPointerException must be thrown"); 227 } catch (NullPointerException e) { 228 } 229 try { 230 cSpi.engineUpdate(bb1, bbNull); 231 fail("NullPointerException must be thrown"); 232 } catch (NullPointerException e) { 233 } 234 ByteBuffer bb2 = ByteBuffer.allocate(bb.length); 235 bb1.position(len); 236 assertEquals("Incorrect number of stored bytes", 0, cSpi.engineUpdate( 237 bb1, bb2)); 238 239 bb1.position(0); 240 bb2.position(len - 2); 241 try { 242 cSpi.engineUpdate(bb1, bb2); 243 fail("ShortBufferException bust be thrown. Output buffer remaining: " 244 .concat(Integer.toString(bb2.remaining()))); 245 } catch (ShortBufferException e) { 246 } 247 bb1.position(10); 248 bb2.position(0); 249 assertTrue("Incorrect number of stored bytes", cSpi.engineUpdate(bb1, 250 bb2) > 0); 251 bb1.position(bb.length); 252 cSpi.engineUpdate(bb1, bb2); 253 254 bb1.position(pos); 255 bb2.position(0); 256 int res = cSpi.engineUpdate(bb1, bb2); 257 assertTrue("Incorrect result", res > 0); 258 } 259 260 /** 261 * Test for <code>engineDoFinal(ByteBuffer, ByteBuffer)</code> method 262 * Assertions: 263 * throws NullPointerException if one of these buffers is null; 264 * throws ShortBufferException is there is no space in output to hold result 265 */ 266 public void testCipherSpi06() throws BadPaddingException, 267 ShortBufferException, IllegalBlockSizeException { 268 Mock_CipherSpi cSpi = new Mock_CipherSpi(); 269 int len = 10; 270 byte[] bbuf = new byte[len]; 271 for (int i = 0; i < bbuf.length; i++) { 272 bbuf[i] = (byte) i; 273 } 274 ByteBuffer bb1 = ByteBuffer.wrap(bbuf); 275 ByteBuffer bbNull = null; 276 try { 277 cSpi.engineDoFinal(bbNull, bb1); 278 fail("NullPointerException must be thrown"); 279 } catch (NullPointerException e) { 280 } 281 try { 282 cSpi.engineDoFinal(bb1, bbNull); 283 fail("NullPointerException must be thrown"); 284 } catch (NullPointerException e) { 285 } 286 ByteBuffer bb2 = ByteBuffer.allocate(len); 287 bb1.position(bb1.limit()); 288 assertEquals("Incorrect result", 0, cSpi.engineDoFinal(bb1, bb2)); 289 290 bb1.position(0); 291 bb2.position(len - 2); 292 try { 293 cSpi.engineDoFinal(bb1, bb2); 294 fail("ShortBufferException must be thrown. Output buffer remaining: " 295 .concat(Integer.toString(bb2.remaining()))); 296 } catch (ShortBufferException e) { 297 } 298 int pos = 5; 299 bb1.position(pos); 300 bb2.position(0); 301 assertTrue("Incorrect result", cSpi.engineDoFinal(bb1, bb2) > 0); 302 } 303 304 public void testCrypt_doNotCallPositionInNonArrayBackedInputBuffer() throws Exception { 305 ByteBuffer nonArrayBackedInputBuffer = new MockNonArrayBackedByteBuffer(10, false); 306 ByteBuffer nonArrayBackedOutputBuffer = new MockNonArrayBackedByteBuffer(10, false); 307 Mock_CipherSpi cipherSpi = new Mock_CipherSpi() { 308 public int engineGetOutputSize(int inputLength) { 309 return inputLength; 310 } 311 }; 312 cipherSpi.engineUpdate(nonArrayBackedInputBuffer, nonArrayBackedOutputBuffer); 313 assertEquals(0, nonArrayBackedInputBuffer.position()); 314 } 315 316 public void testCrypt_doNotCallPutForZeroLengthOutput() throws Exception { 317 ByteBuffer nonArrayBackedInputBuffer = new MockNonArrayBackedByteBuffer(10, false); 318 ByteBuffer nonArrayBackedOutputBuffer = new MockNonArrayBackedByteBuffer(10, false) { 319 @Override 320 public ByteBuffer put(byte[] dst, int offset, int length) { 321 if (length == 0) { 322 throw new IllegalStateException("put shouldn't be called with length 0"); 323 } 324 return this; 325 } 326 }; 327 328 Mock_CipherSpi cipherSpi = new Mock_CipherSpi() { 329 public int engineUpdate( 330 byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { 331 return 0; 332 } 333 }; 334 335 // The put method is not called in the output buffer and so the test passes. 336 cipherSpi.engineUpdate(nonArrayBackedInputBuffer, nonArrayBackedOutputBuffer); 337 } 338 339 // In case a call to engineGetOutputSize returns 0 for the whole input size, but a positive 340 // value for the chunk size to be written, check that the positive output size is used in the 341 // second attempt to read from the the buffer. 342 public void testCrypt_outputSizeUpdatedAfterShortBufferException() 343 throws Exception { 344 345 // 4096 is the value hardcoded for a maximum array allocation in CipherSpi#getTempArraySize 346 final int maxInternalArrayAllocation = 4096; 347 // The length of the input is greater than the max chunk allowed, so the size of the chunk 348 // and the size of the input will differ. 349 final int testInputLength = maxInternalArrayAllocation + 1; 350 // Length to be returned the second time engineGetOutputSize is called (that is, when it's 351 // called with maxInternalArrayAllocation). First length returned (that is, when it's 352 // called with testInputLength) is 0. 353 final int testSecondOutputLength = 1000; 354 355 final AtomicInteger firstGetLength = new AtomicInteger(0); 356 final AtomicInteger secondGetLength = new AtomicInteger(0); 357 358 ByteBuffer inputBuffer = new MockNonArrayBackedByteBuffer(testInputLength, false) { 359 private boolean getWasCalled = false; 360 361 @Override 362 public ByteBuffer get(byte[] dst, int offset, int length) { 363 if (!getWasCalled) { 364 getWasCalled = true; 365 firstGetLength.set(length); 366 } else { 367 if (secondGetLength.get() == 0) { 368 secondGetLength.set(length); 369 } 370 } 371 return this; 372 } 373 }; 374 375 ByteBuffer outputBuffer = new MockNonArrayBackedByteBuffer(10, false); 376 377 Mock_CipherSpi cipherSpi = new Mock_CipherSpi() { 378 @Override 379 public int engineGetOutputSize(int inputLength) { 380 if (inputLength == testInputLength) { 381 return 0; 382 } else if (inputLength == maxInternalArrayAllocation) { 383 return testSecondOutputLength; 384 } else { 385 throw new IllegalStateException("Unexpected value " + inputLength); 386 } 387 } 388 389 @Override 390 public int engineUpdate( 391 byte[] inArray, int inOfs, int inLen, byte[] outArray, int outputOffset) 392 throws ShortBufferException { 393 if (inLen == maxInternalArrayAllocation) { 394 throw new ShortBufferException("to be caught in order to retry with a new" 395 + "output size"); 396 } 397 return 0; 398 } 399 }; 400 401 cipherSpi.engineUpdate(inputBuffer, outputBuffer); 402 403 assertEquals( 404 "first call to get must use the input length, as the output length " 405 + "from engineGetOutputSize is 0", 406 maxInternalArrayAllocation, 407 firstGetLength.get()); 408 409 assertEquals( 410 "second call to get must use the new output length", 411 testSecondOutputLength, 412 secondGetLength.get()); 413 } 414 415 // The tests using ByteBuffer depend on final methods (like hasArray) that cannot be mocked in 416 // Mockito, so the mock is done manually. ByteBuffer has abstract methods that are 417 // package-private, so extending DirectByteBuffer. It happens to be not backed by an array, so 418 // we use it when we need a byte buffer not array-backed. 419 private class MockNonArrayBackedByteBuffer extends DirectByteBuffer { 420 public MockNonArrayBackedByteBuffer(int capacity, boolean isReadOnly) { 421 super(capacity, 0 /* addr */, null /* fd */, null /* unmapper */, isReadOnly); 422 } 423 424 @Override 425 public ByteBuffer get(byte[] dst, int offset, int length) { 426 return this; 427 } 428 429 @Override 430 public ByteBuffer put(byte[] dst, int offset, int length) { 431 return this; 432 } 433 } 434 } 435 /** 436 * 437 * Additional class for CipherGeneratorSpi constructor verification 438 */ 439 440 class myCipherSpi extends CipherSpi { 441 private byte[] initV; 442 443 private static byte[] resV = { (byte) 7, (byte) 6, (byte) 5, (byte) 4, 444 (byte) 3, (byte) 2, (byte) 1, (byte) 0 }; 445 446 public myCipherSpi() { 447 this.initV = new byte[0]; 448 } 449 450 protected void engineSetMode(String mode) throws NoSuchAlgorithmException { 451 } 452 453 protected void engineSetPadding(String padding) 454 throws NoSuchPaddingException { 455 } 456 457 protected int engineGetBlockSize() { 458 return 0; 459 } 460 461 protected int engineGetOutputSize(int inputLen) { 462 return 0; 463 } 464 465 protected byte[] engineGetIV() { 466 return new byte[0]; 467 } 468 469 protected AlgorithmParameters engineGetParameters() { 470 return null; 471 } 472 473 protected void engineInit(int opmode, Key key, SecureRandom random) 474 throws InvalidKeyException { 475 } 476 477 protected void engineInit(int opmode, Key key, 478 AlgorithmParameterSpec params, SecureRandom random) 479 throws InvalidKeyException, InvalidAlgorithmParameterException { 480 } 481 482 protected void engineInit(int opmode, Key key, AlgorithmParameters params, 483 SecureRandom random) throws InvalidKeyException, 484 InvalidAlgorithmParameterException { 485 } 486 487 protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { 488 if (initV.length < inputLen) { 489 initV = new byte[inputLen]; 490 } 491 for (int i = 0; i < inputLen; i++) { 492 initV[i] = input[inputOffset + i]; 493 } 494 return initV; 495 } 496 497 protected int engineUpdate(byte[] input, int inputOffset, int inputLen, 498 byte[] output, int outputOffset) throws ShortBufferException { 499 byte []res = engineUpdate(input, inputOffset, inputLen); 500 int t = res.length; 501 if ((output.length - outputOffset) < t) { 502 throw new ShortBufferException("Update"); 503 } 504 for (int i = 0; i < t; i++) { 505 output[i + outputOffset] = initV[i]; 506 } 507 return t; 508 } 509 510 protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) 511 throws IllegalBlockSizeException, BadPaddingException { 512 if (resV.length > inputLen) { 513 byte[] bb = new byte[inputLen]; 514 for (int i = 0; i < inputLen; i++) { 515 bb[i] = resV[i]; 516 } 517 return bb; 518 } 519 return resV; 520 } 521 522 protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, 523 byte[] output, int outputOffset) throws ShortBufferException, 524 IllegalBlockSizeException, BadPaddingException { 525 byte[] res = engineDoFinal(input, inputOffset, inputLen); 526 527 int t = res.length; 528 if ((output.length - outputOffset) < t) { 529 throw new ShortBufferException("DoFinal"); 530 } 531 for (int i = 0; i < t; i++) { 532 output[i + outputOffset] = res[i]; 533 } 534 return t; 535 } 536 537 538 protected int engineUpdate(ByteBuffer input, ByteBuffer output) 539 throws ShortBufferException { 540 return super.engineUpdate(input, output); 541 } 542 protected int engineDoFinal(ByteBuffer input, ByteBuffer output) 543 throws ShortBufferException, IllegalBlockSizeException, 544 BadPaddingException { 545 return super.engineDoFinal(input, output); 546 } 547 } 548