Skip to content

Instantly share code, notes, and snippets.

@misterAnderson90
Created December 22, 2021 23:06
Show Gist options
  • Save misterAnderson90/79b66ac028b7b90a264ee4b4412c1ed8 to your computer and use it in GitHub Desktop.
Save misterAnderson90/79b66ac028b7b90a264ee4b4412c1ed8 to your computer and use it in GitHub Desktop.

CogniCrypt (report 8) for activeTAN-Android

  • 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.

Code

    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();
        }
    }

Questions

  1. 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;

  2. Are you likely to accept a patch that fixes this particular issue?

@oheim
Copy link

oheim commented Dec 23, 2021

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 the init method.

For the purpose of code maintainability we would like to separate the initialization code from the part where the cipher is actually used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment