-
Class: de.efdis.tangenerator.api.SecuredRestApiEndpoint
-
Method: performRequest
-
Line: 258
-
Issue details: TypestateError-2
-
TypestateError violating CrySL rule for javax.crypto.Cipher.
-
Unexpected call to method doFinal on object of type javax.crypto.Cipher. Expect a call to one of the following methods init,updateAAD.
-
public synchronized byte[] performRequest(@NonNull byte[] postData)
throws CallFailedException, ConnectException, IncompatibleClientException {
prepareCrypto();
HttpsURLConnection connection;
try {
connection = prepareAndOpenConnection();
} catch (IOException e) {
throw new ConnectException("Connecting to api failed", e);
}
try {
try (OutputStream os = connection.getOutputStream()) {
byte[] encryptedPostData = cipher.doFinal(postData);
os.write(encryptedPostData);
signature.update(encryptedPostData);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new ApiConfigurationException("Could not encrypt request data", e);
} catch (SignatureException e) {
throw new ApiConfigurationException("Could not verify request data", e);
}
switch (connection.getResponseCode()) {
case HttpURLConnection.HTTP_OK:
case HttpURLConnection.HTTP_CREATED:
// success
break;
case HttpURLConnection.HTTP_BAD_REQUEST:
case HttpURLConnection.HTTP_FORBIDDEN:
case HttpURLConnection.HTTP_GONE:
case HttpURLConnection.HTTP_UNSUPPORTED_TYPE:
throw new IncompatibleClientException(
"HTTP request did not succeed, this client should be updated");
case 429: // Too many requests
throw new CallFailedException(
"Backend is busy, try again later");
default:
throw new IOException(
"HTTP request did not succeed, response code = "
+ connection.getResponseCode());
}
byte[] responseData = new byte[Math.max(0, connection.getContentLength())];
if (responseData.length > 0) {
try (InputStream is = connection.getInputStream()) {
if (responseData.length != is.read(responseData)) {
throw new IOException(
"Unexpected end of content stream");
}
}
}
byte[] sigData;
{
String sigBase64 = connection.getHeaderField(API_SIGNATURE_HEADER);
if (sigBase64 == null) {
throw new IOException("Missing signature for api response");
}
try {
sigData = Base64.decode(sigBase64, Base64.DEFAULT);
} catch (IllegalArgumentException e) {
throw new IOException("Illegal format of api response signature", e);
}
}
try {
signature.update(responseData);
} catch (SignatureException e) {
throw new ApiConfigurationException("Unable to verify api response data");
}
try {
if (!signature.verify(sigData)) {
throw new IOException("Invalid api response signature");
}
} catch (SignatureException e) {
throw new IOException("Unable to verify api response signature", e);
}
return responseData;
} catch (IOException e) {
throw new CallFailedException(
"Connection error or illegal response during api call", e);
} finally {
connection.disconnect();
}
}
-
How likely might this warning reveal a security threat to this app? a. Very unlikely; b. Unlikely; c. I cannot evaluate this; d. Likely; e. Very likely;
-
Are you likely to accept a patch that fixes this particular issue?
The initialization code of the cipher has been moved to the method
prepareCrypto
, which is called as the first instruction of this method. If the analyzer could inline the other method, it would see the call to theinit
method.For the purpose of code maintainability we would like to separate the initialization code from the part where the cipher is actually used.