@@ -1058,6 +1058,116 @@ private void assertWrapSucceeds(ByteBuffer[] buffers, int offset, int length) th
10581058 }
10591059 }
10601060
1061+ /*
1062+ * The preconditions for the unwrap() methods of SSLEngine have a relatively
1063+ * complex mapping from precondition to the exception thrown on failure. This method
1064+ * checks that all the failure cases throw the correct exception and that
1065+ * corner cases which should not throw are also handled correctly.
1066+ */
1067+ @ Test
1068+ public void unwrapPreconditions () throws Exception {
1069+ int bufferSize = 128 ;
1070+ int arrayLength = 5 ;
1071+ ByteBuffer srcBuffer = BufferType .HEAP .newRandomBuffer (bufferSize );
1072+ ByteBuffer dstBuffer = ByteBuffer .allocate (bufferSize );
1073+ ByteBuffer readOnlyDestBuffer = dstBuffer .asReadOnlyBuffer ();
1074+
1075+ ByteBuffer [] dsts = BufferType .HEAP .newBufferArray (arrayLength , bufferSize );
1076+ ByteBuffer [] dstsWithNullEntry = Arrays .copyOf (dsts , dsts .length );
1077+ int nullBufferIndex = 2 ;
1078+ dstsWithNullEntry [nullBufferIndex ] = null ;
1079+ ByteBuffer [] dstsWithReadOnlyEntry = Arrays .copyOf (dsts , dsts .length );
1080+ int readOnlyBufferIndex = 2 ;
1081+ dstsWithReadOnlyEntry [readOnlyBufferIndex ] =
1082+ dstsWithReadOnlyEntry [readOnlyBufferIndex ].asReadOnlyBuffer ();
1083+
1084+ // Failure cases
1085+ // Client/server mode not set => IllegalStateException
1086+ assertThrows (IllegalStateException .class ,
1087+ () -> newUnconnectedEngine ().unwrap (srcBuffer , dstBuffer ));
1088+ assertThrows (IllegalStateException .class ,
1089+ () -> newUnconnectedEngine ().unwrap (srcBuffer , dsts ));
1090+ assertThrows (IllegalStateException .class ,
1091+ () -> newUnconnectedEngine ().unwrap (srcBuffer , dsts , 0 , 1 ));
1092+
1093+ // Read-only destination => ReadOnlyBufferException
1094+ assertThrows (ReadOnlyBufferException .class ,
1095+ () -> newConnectedEngine ().unwrap (srcBuffer , readOnlyDestBuffer ));
1096+ assertThrows (ReadOnlyBufferException .class ,
1097+ () -> newConnectedEngine ().unwrap (srcBuffer , dstsWithReadOnlyEntry ));
1098+ assertThrows (ReadOnlyBufferException .class ,
1099+ () -> newConnectedEngine ().unwrap (
1100+ srcBuffer , dstsWithReadOnlyEntry , 0 , arrayLength ));
1101+
1102+ // Null destination => IllegalArgumentException
1103+ assertThrows (IllegalArgumentException .class ,
1104+ () -> newConnectedEngine ().unwrap (srcBuffer , (ByteBuffer ) null ));
1105+ assertThrows (IllegalArgumentException .class ,
1106+ () -> newConnectedEngine ().unwrap (srcBuffer , (ByteBuffer []) null ));
1107+ assertThrows (IllegalArgumentException .class ,
1108+ () -> newConnectedEngine ().unwrap (srcBuffer , null , 0 , 1 ));
1109+
1110+ // Null source => IllegalArgumentException
1111+ assertThrows (IllegalArgumentException .class ,
1112+ () -> newConnectedEngine ().unwrap (null , dstBuffer ));
1113+ assertThrows (IllegalArgumentException .class ,
1114+ () -> newConnectedEngine ().unwrap (null , dsts ));
1115+ assertThrows (IllegalArgumentException .class ,
1116+ () -> newConnectedEngine ().unwrap (null , dsts , 0 , 1 ));
1117+
1118+ // Null entries in destination buffer array => IllegalArgumentException
1119+ assertThrows (IllegalArgumentException .class ,
1120+ () -> newConnectedEngine ().unwrap (srcBuffer , dstsWithNullEntry ));
1121+ assertThrows (IllegalArgumentException .class ,
1122+ () -> newConnectedEngine ().unwrap (
1123+ srcBuffer , dstsWithNullEntry , 0 , arrayLength ));
1124+
1125+ // Bad offset or length => IndexOutOfBoundsException
1126+ assertThrows (IndexOutOfBoundsException .class ,
1127+ () -> newConnectedEngine ().unwrap (srcBuffer , dsts , 0 , arrayLength + 1 ));
1128+ assertThrows (IndexOutOfBoundsException .class ,
1129+ () -> newConnectedEngine ().unwrap (srcBuffer , dsts , arrayLength , 1 ));
1130+ assertThrows (IndexOutOfBoundsException .class ,
1131+ () -> newConnectedEngine ().unwrap (srcBuffer , dsts , arrayLength - 1 , 2 ));
1132+
1133+ // Corner cases which should not throw
1134+ // Zero length arrays of output buffers should never throw
1135+ assertUnwrapDoesNotThrow (dsts , 0 , 0 );
1136+ assertUnwrapDoesNotThrow (dsts , arrayLength , 0 );
1137+
1138+ // TODO(prb): Add tests for null/read-only entries outside the selected offset and length
1139+ // after https://github.com/google/conscrypt/issues/1372 is fixed.
1140+ }
1141+
1142+ // Asserts that an unwrap call with the given arguments does not throw a precondition
1143+ // exception. Note that the unwrap call itself may not produce plaintext if the selected
1144+ // destination buffers don't have enough capacity, but this is purely a precondition test
1145+ // and actual unwrap functionality is tested elsewhere.
1146+ private void assertUnwrapDoesNotThrow (ByteBuffer [] dsts , int offset , int length ) throws Exception {
1147+ try (TestSSLEnginePair pair = TestSSLEnginePair .create ()) {
1148+ assertConnected (pair );
1149+
1150+ // Wrap some plaintext to get TLS ciphertext to unwrap.
1151+ ByteBuffer plaintext = BufferType .HEAP .newRandomBuffer (128 );
1152+ ByteBuffer ciphertext =
1153+ ByteBuffer .allocate (pair .client .getSession ().getPacketBufferSize ());
1154+ SSLEngineResult wrapResult = pair .client .wrap (plaintext , ciphertext );
1155+ assertEquals (Status .OK , wrapResult .getStatus ());
1156+ ciphertext .flip ();
1157+
1158+ // Reset the selected destination buffers to their initial (empty) state.
1159+ for (int i = offset ; i < offset + length ; i ++) {
1160+ dsts [i ].clear ();
1161+ }
1162+
1163+ // Unwrap: may return BUFFER_OVERFLOW if the selected buffers don't have enough capacity but that's
1164+ // fine for this *precondition* test.
1165+ SSLEngineResult result = pair .server .unwrap (ciphertext , dsts , offset , length );
1166+ assertTrue (result .getStatus () == Status .OK
1167+ || result .getStatus () == Status .BUFFER_OVERFLOW );
1168+ }
1169+ }
1170+
10611171 @ Test
10621172 public void bufferArrayOffsets () throws Exception {
10631173 TestSSLEnginePair pair = TestSSLEnginePair .create ();
0 commit comments