@@ -53,7 +53,7 @@ public class CipherBlockChainingCheck extends AbstractMethodDetection {
5353 // * ...
5454 //
5555 // In other words, there will be FPs when secure IV generating methods are used that are defined across (top-level) classes or files.
56- // This could be handled by doing a pass of SecureByteArrayFactoryFinder across all classes and files first, before performing
56+ // This could be handled by doing a pass of IvFactoryFinderImpl across all classes and files first, before performing
5757 // CipherBlockChainingCheck.
5858 //
5959 // However, this is likely not worth the additional complexity.
@@ -63,10 +63,8 @@ public class CipherBlockChainingCheck extends AbstractMethodDetection {
6363 //
6464 // Furthermore, we can not simply mute this check as soon as a call is involved in generating IVs because then we will lose a lot of the
6565 // cases that are already detected by previous iterations of this check.
66- private final SecureByteArrayFactoryFinder secureByteArrayFactoryFinder = new SecureByteArrayFactoryFinder ();
67- private final SecureByteArrayGeneratorDetector secureByteArrayGeneratorDetector = new SecureByteArrayGeneratorDetector ()
68- .countParametersAsDynamic ()
69- .detectCustomFactories (secureByteArrayFactoryFinder );
66+ private final IvFactoryFinderImpl ivFactoryFinder = new IvFactoryFinderImpl ();
67+ private final IvInitializationAnalysis ivInitializationAnalysis = IvInitializationAnalysis .crossProcedural (ivFactoryFinder );
7068
7169 private static final MethodMatchers SECURE_RANDOM_GENERATE_SEED = MethodMatchers .create ()
7270 .ofTypes ("java.security.SecureRandom" )
@@ -89,10 +87,10 @@ public List<Tree.Kind> nodesToVisit() {
8987 @ Override
9088 public void visitNode (Tree tree ) {
9189 if (outermostClass == null && tree .is (Tree .Kind .CLASS )) {
92- // We only need run SecureByteArrayFactoryFinder once on the outermost class to find all secure IV byte array factory methods.
93- // If we apply the finder again to nested classes then we explore the same sub-trees multiple times.
90+ // We only need run IvFactoryFinderImpl once on the outermost class to find all secure IV byte array factory methods.
91+ // If we apply the finder again to nested classes then we would explore the same sub-trees multiple times.
9492 outermostClass = tree ;
95- tree .accept (secureByteArrayFactoryFinder );
93+ tree .accept (ivFactoryFinder );
9694 }
9795
9896 super .visitNode (tree );
@@ -101,7 +99,7 @@ public void visitNode(Tree tree) {
10199 @ Override
102100 public void leaveNode (Tree tree ) {
103101 if (tree == outermostClass ) {
104- secureByteArrayFactoryFinder .clear ();
102+ ivFactoryFinder .clear ();
105103 }
106104 super .leaveNode (tree );
107105 }
@@ -115,7 +113,7 @@ protected MethodMatchers getMethodInvocationMatchers() {
115113
116114 @ Override
117115 protected void onConstructorFound (NewClassTree newClassTree ) {
118- if (newClassTree .arguments ().isEmpty () || secureByteArrayGeneratorDetector .isDynamicallyGenerated (newClassTree .arguments ().get (0 ))) {
116+ if (newClassTree .arguments ().isEmpty () || ivInitializationAnalysis .isDynamicallyGenerated (newClassTree .arguments ().get (0 ))) {
119117 return ;
120118 }
121119
@@ -264,30 +262,47 @@ private static Symbol ivSymbol(NewClassTree newClassTree) {
264262 }
265263 }
266264
267- private static class SecureByteArrayGeneratorDetector {
268- private @ Nullable SecureByteArrayFactoryFinder secureByteArrayFactoryFinder = null ;
269- private boolean shouldCountParametersAsDynamic = false ;
265+ private interface IvFactoryFinder {
266+ boolean producesSecureBytesArray (MethodInvocationTree methodInvocation );
270267
271- /**
272- * When called, this detector will treat all byte arrays produced by method calls to secure IV-generating functions as secure.
273- * It uses the given {@link SecureByteArrayFactoryFinder} to judge whether a method call is secure.
274- */
275- public SecureByteArrayGeneratorDetector detectCustomFactories (SecureByteArrayFactoryFinder secureByteArrayFactoryFinder ) {
276- this .secureByteArrayFactoryFinder = secureByteArrayFactoryFinder ;
277- return this ;
268+ static IvFactoryFinder disabled () {
269+ return methodInvocation -> false ;
270+ }
271+ }
272+
273+ private interface IvInitializationAnalysis {
274+ boolean isDynamicallyGenerated (ExpressionTree tree );
275+
276+ static IvInitializationAnalysis crossProcedural (IvFactoryFinder ivFactoryFinder ) {
277+ return new IvInitializationAnalysisImpl (ivFactoryFinder , true );
278278 }
279279
280+ static IvInitializationAnalysis intraProcedural () {
281+ return new IvInitializationAnalysisImpl (IvFactoryFinder .disabled (), false );
282+ }
283+ }
284+
285+ private static class IvInitializationAnalysisImpl implements IvInitializationAnalysis {
286+ private final IvFactoryFinder ivFactoryFinder ;
287+ private final boolean shouldCountParametersAsDynamic ;
288+
280289 /**
281- * If called, this detector will treat parameters as secure byte arrays.
282- * This is optional, since we can not trace arguments across methods and hence can not always assume them to be secure.
283- * However, for the methods where we see parameters being directly passed into IvParameterSpec, we do want to treat them as secure
284- * to match the behaviour of previous iterations of CipherBlockChainingCheck
290+ *
291+ * @param ivFactoryFinder when IV byte arrays are initialized using a method call, then this {@link IvFactoryFinder} will be used to
292+ * judge whether the method produces a secure byte array.
293+ * @param shouldCountParametersAsDynamic when set to {@code true}, this detector will treat parameters as secure byte arrays.
294+ * This is optional, since we can not trace arguments across methods and hence can not always
295+ * assume them to be secure.
296+ * However, for the methods where we see parameters being directly passed into IvParameterSpec, we
297+ * do want to treat them as secure to match the behaviour of previous iterations of
298+ * CipherBlockChainingCheck.
285299 */
286- public SecureByteArrayGeneratorDetector countParametersAsDynamic ( ) {
287- this .shouldCountParametersAsDynamic = true ;
288- return this ;
300+ IvInitializationAnalysisImpl ( IvFactoryFinder ivFactoryFinder , boolean shouldCountParametersAsDynamic ) {
301+ this .ivFactoryFinder = ivFactoryFinder ;
302+ this . shouldCountParametersAsDynamic = shouldCountParametersAsDynamic ;
289303 }
290304
305+ @ Override
291306 public boolean isDynamicallyGenerated (ExpressionTree tree ) {
292307 if (shouldCountParametersAsDynamic && tree instanceof IdentifierTree identifierTree ) {
293308 Symbol symbol = identifierTree .symbol ();
@@ -302,11 +317,7 @@ public boolean isDynamicallyGenerated(ExpressionTree tree) {
302317 return true ;
303318 }
304319
305- if (secureByteArrayFactoryFinder == null ) {
306- return false ;
307- }
308-
309- return secureByteArrayFactoryFinder .producesSecureBytesArray (methodInvocationTree );
320+ return ivFactoryFinder .producesSecureBytesArray (methodInvocationTree );
310321 });
311322 }
312323
@@ -340,18 +351,20 @@ private static Stream<MethodInvocationTree> findConstructingMethods(ExpressionTr
340351
341352 /**
342353 * Collects all methods that construct IV byte arrays in a secure way.
343- * After applying this visitor to a class tree, {@link SecureByteArrayFactoryFinder #producesSecureBytesArray(MethodInvocationTree)} can
354+ * After applying this visitor to a class tree, {@link IvFactoryFinder #producesSecureBytesArray(MethodInvocationTree)} can
344355 * be used to check whether the invocation of a method belonging to that tree will return such a secure array.
345356 */
346- private static class SecureByteArrayFactoryFinder extends BaseTreeVisitor {
357+ private static class IvFactoryFinderImpl extends BaseTreeVisitor implements IvFactoryFinder {
347358 private @ Nullable MethodTree currentMethodTree = null ;
348359 private final Set <String > secureByteArrayFactories = new HashSet <>();
349- // Note, that we do not call detectCustomFactories() on secureByteArrayGeneratorDetector.
360+
361+ // Note, that we use the intra-procedural analysis here.
350362 // Meaning, that we will not trace whether methods called inside IV factories are secure.
351363 // In other words, CipherBlockChainingCheck supports a call-depth of at most 1.
352364 // See also the note on cross-procedural FPs in the surrounding class.
353- private final SecureByteArrayGeneratorDetector secureByteArrayGeneratorDetector = new SecureByteArrayGeneratorDetector ();
365+ private final IvInitializationAnalysis ivInitializationAnalysis = IvInitializationAnalysis . intraProcedural ();
354366
367+ @ Override
355368 public boolean producesSecureBytesArray (MethodInvocationTree methodInvocation ) {
356369 return secureByteArrayFactories .contains (methodInvocation .methodSymbol ().signature ());
357370 }
@@ -386,7 +399,7 @@ public void visitReturnStatement(ReturnStatementTree tree) {
386399 return ;
387400 }
388401
389- if (secureByteArrayGeneratorDetector .isDynamicallyGenerated (returnedExpression )) {
402+ if (ivInitializationAnalysis .isDynamicallyGenerated (returnedExpression )) {
390403 markAsSecureFactory ();
391404 return ;
392405 }
0 commit comments