@Test public void givenASecureDexClassLoaderWithAContainerAndNoCertificateForItsPackageNameForVerification_whenLoadClassInTheContainerWithLazyEvaluation_thenImportsRetrievedContainerButReturnsNull() throws Exception { SecureDexClassLoader secureDexClassLoader = initializeSecureDexClassLoaderWithTestValues( downloadRemoteContainerIntoTemporaryImportedContainersFolder( TEST_REMOTE_CONTAINER_URL_AS_STRING), ImmutableMap.of( TEST_A_PACKAGE_NAME_NOT_IN_THE_CONTAINER, new URL(TEST_REMOTE_CERTIFICATE_URL_AS_STRING)), LAZY_EVALUATION); Class<?> loadedClass = secureDexClassLoader.loadClass(TEST_CLASS_TO_LOAD); assertThatOnlyOneFileIsPresentInTheImportedContainersFolderMatchingTheFilter( TEST_FILE_FILTER_MATCHING_FILE_NAMED_AS_THE_TEST_CONTAINER); assertThat(loadedClass, is(nullValue())); }
public Class<?> loadClass(@NonNull String className) throws ClassNotFoundException { checkNotNull(className, "The name of the class to load must be not null"); checkArgument(!className.isEmpty(), "The name of the class to load must be not empty"); if (hasBeenWipedOut) return null; String packageName = className.substring(0, className.lastIndexOf('.')); String containerPath; synchronized (packageNameToContainerPathMap) { containerPath = packageNameToContainerPathMap.get(packageName); } if (containerPath == null) return null; if (performLazyEvaluation) { boolean alreadyVerifiedPackageName; synchronized (lazyAlreadyVerifiedPackageNameSet) { alreadyVerifiedPackageName = lazyAlreadyVerifiedPackageNameSet.contains(packageName); } if (alreadyVerifiedPackageName) { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); if (optionalRootPackageNameWithCertificate.isPresent()) { return dexClassLoader.loadClass(className); } } else { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); X509Certificate verifiedCertificate = null; if (optionalRootPackageNameWithCertificate.isPresent()) { verifiedCertificate = importCertificateFromPackageName( optionalRootPackageNameWithCertificate.get()); } if (verifiedCertificate != null) { boolean signatureCheckIsSuccessful = containerSignatureVerifier.verifyContainerSignatureAgainstCertificate( containerPath, verifiedCertificate); if (signatureCheckIsSuccessful) { synchronized (lazyAlreadyVerifiedPackageNameSet) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) { lazyAlreadyVerifiedPackageNameSet.add(currentPackageName); } } } return dexClassLoader.loadClass(className); } File containerToRemove = new File(containerPath); if (!containerToRemove.delete()) Log.i(TAG_SECURE_DEX_CLASS_LOADER, "It was impossible to delete " + containerPath); synchronized (packageNameToContainerPathMap) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) packageNamesIterator.remove(); } } return null; } return null; } } return dexClassLoader.loadClass(className); }
SecureDexClassLoader { public Class<?> loadClass(@NonNull String className) throws ClassNotFoundException { checkNotNull(className, "The name of the class to load must be not null"); checkArgument(!className.isEmpty(), "The name of the class to load must be not empty"); if (hasBeenWipedOut) return null; String packageName = className.substring(0, className.lastIndexOf('.')); String containerPath; synchronized (packageNameToContainerPathMap) { containerPath = packageNameToContainerPathMap.get(packageName); } if (containerPath == null) return null; if (performLazyEvaluation) { boolean alreadyVerifiedPackageName; synchronized (lazyAlreadyVerifiedPackageNameSet) { alreadyVerifiedPackageName = lazyAlreadyVerifiedPackageNameSet.contains(packageName); } if (alreadyVerifiedPackageName) { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); if (optionalRootPackageNameWithCertificate.isPresent()) { return dexClassLoader.loadClass(className); } } else { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); X509Certificate verifiedCertificate = null; if (optionalRootPackageNameWithCertificate.isPresent()) { verifiedCertificate = importCertificateFromPackageName( optionalRootPackageNameWithCertificate.get()); } if (verifiedCertificate != null) { boolean signatureCheckIsSuccessful = containerSignatureVerifier.verifyContainerSignatureAgainstCertificate( containerPath, verifiedCertificate); if (signatureCheckIsSuccessful) { synchronized (lazyAlreadyVerifiedPackageNameSet) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) { lazyAlreadyVerifiedPackageNameSet.add(currentPackageName); } } } return dexClassLoader.loadClass(className); } File containerToRemove = new File(containerPath); if (!containerToRemove.delete()) Log.i(TAG_SECURE_DEX_CLASS_LOADER, "It was impossible to delete " + containerPath); synchronized (packageNameToContainerPathMap) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) packageNamesIterator.remove(); } } return null; } return null; } } return dexClassLoader.loadClass(className); } }
SecureDexClassLoader { public Class<?> loadClass(@NonNull String className) throws ClassNotFoundException { checkNotNull(className, "The name of the class to load must be not null"); checkArgument(!className.isEmpty(), "The name of the class to load must be not empty"); if (hasBeenWipedOut) return null; String packageName = className.substring(0, className.lastIndexOf('.')); String containerPath; synchronized (packageNameToContainerPathMap) { containerPath = packageNameToContainerPathMap.get(packageName); } if (containerPath == null) return null; if (performLazyEvaluation) { boolean alreadyVerifiedPackageName; synchronized (lazyAlreadyVerifiedPackageNameSet) { alreadyVerifiedPackageName = lazyAlreadyVerifiedPackageNameSet.contains(packageName); } if (alreadyVerifiedPackageName) { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); if (optionalRootPackageNameWithCertificate.isPresent()) { return dexClassLoader.loadClass(className); } } else { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); X509Certificate verifiedCertificate = null; if (optionalRootPackageNameWithCertificate.isPresent()) { verifiedCertificate = importCertificateFromPackageName( optionalRootPackageNameWithCertificate.get()); } if (verifiedCertificate != null) { boolean signatureCheckIsSuccessful = containerSignatureVerifier.verifyContainerSignatureAgainstCertificate( containerPath, verifiedCertificate); if (signatureCheckIsSuccessful) { synchronized (lazyAlreadyVerifiedPackageNameSet) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) { lazyAlreadyVerifiedPackageNameSet.add(currentPackageName); } } } return dexClassLoader.loadClass(className); } File containerToRemove = new File(containerPath); if (!containerToRemove.delete()) Log.i(TAG_SECURE_DEX_CLASS_LOADER, "It was impossible to delete " + containerPath); synchronized (packageNameToContainerPathMap) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) packageNamesIterator.remove(); } } return null; } return null; } } return dexClassLoader.loadClass(className); } @VisibleForTesting  SecureDexClassLoader(
            @NonNull String dexPath,
            @NonNull DexClassLoader dexClassLoader,
            @NonNull Context parentContext,
            @NonNull Map<String, URL> sanitizePackageNameToCertificateMap,
			boolean performLazyEvaluation,
            @NonNull ContainerSignatureVerifier containerSignatureVerifier,
            @NonNull CertificateFactory certificateFactory); }
SecureDexClassLoader { public Class<?> loadClass(@NonNull String className) throws ClassNotFoundException { checkNotNull(className, "The name of the class to load must be not null"); checkArgument(!className.isEmpty(), "The name of the class to load must be not empty"); if (hasBeenWipedOut) return null; String packageName = className.substring(0, className.lastIndexOf('.')); String containerPath; synchronized (packageNameToContainerPathMap) { containerPath = packageNameToContainerPathMap.get(packageName); } if (containerPath == null) return null; if (performLazyEvaluation) { boolean alreadyVerifiedPackageName; synchronized (lazyAlreadyVerifiedPackageNameSet) { alreadyVerifiedPackageName = lazyAlreadyVerifiedPackageNameSet.contains(packageName); } if (alreadyVerifiedPackageName) { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); if (optionalRootPackageNameWithCertificate.isPresent()) { return dexClassLoader.loadClass(className); } } else { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); X509Certificate verifiedCertificate = null; if (optionalRootPackageNameWithCertificate.isPresent()) { verifiedCertificate = importCertificateFromPackageName( optionalRootPackageNameWithCertificate.get()); } if (verifiedCertificate != null) { boolean signatureCheckIsSuccessful = containerSignatureVerifier.verifyContainerSignatureAgainstCertificate( containerPath, verifiedCertificate); if (signatureCheckIsSuccessful) { synchronized (lazyAlreadyVerifiedPackageNameSet) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) { lazyAlreadyVerifiedPackageNameSet.add(currentPackageName); } } } return dexClassLoader.loadClass(className); } File containerToRemove = new File(containerPath); if (!containerToRemove.delete()) Log.i(TAG_SECURE_DEX_CLASS_LOADER, "It was impossible to delete " + containerPath); synchronized (packageNameToContainerPathMap) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) packageNamesIterator.remove(); } } return null; } return null; } } return dexClassLoader.loadClass(className); } @VisibleForTesting  SecureDexClassLoader(
            @NonNull String dexPath,
            @NonNull DexClassLoader dexClassLoader,
            @NonNull Context parentContext,
            @NonNull Map<String, URL> sanitizePackageNameToCertificateMap,
			boolean performLazyEvaluation,
            @NonNull ContainerSignatureVerifier containerSignatureVerifier,
            @NonNull CertificateFactory certificateFactory); Class<?> loadClass(@NonNull String className); void wipeOutPrivateAppCachedData(boolean containerPrivateFolder, boolean certificatePrivateFolder); }
SecureDexClassLoader { public Class<?> loadClass(@NonNull String className) throws ClassNotFoundException { checkNotNull(className, "The name of the class to load must be not null"); checkArgument(!className.isEmpty(), "The name of the class to load must be not empty"); if (hasBeenWipedOut) return null; String packageName = className.substring(0, className.lastIndexOf('.')); String containerPath; synchronized (packageNameToContainerPathMap) { containerPath = packageNameToContainerPathMap.get(packageName); } if (containerPath == null) return null; if (performLazyEvaluation) { boolean alreadyVerifiedPackageName; synchronized (lazyAlreadyVerifiedPackageNameSet) { alreadyVerifiedPackageName = lazyAlreadyVerifiedPackageNameSet.contains(packageName); } if (alreadyVerifiedPackageName) { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); if (optionalRootPackageNameWithCertificate.isPresent()) { return dexClassLoader.loadClass(className); } } else { Optional<String> optionalRootPackageNameWithCertificate = packageNameTrie.getPackageNameWithAssociatedCertificate(packageName); X509Certificate verifiedCertificate = null; if (optionalRootPackageNameWithCertificate.isPresent()) { verifiedCertificate = importCertificateFromPackageName( optionalRootPackageNameWithCertificate.get()); } if (verifiedCertificate != null) { boolean signatureCheckIsSuccessful = containerSignatureVerifier.verifyContainerSignatureAgainstCertificate( containerPath, verifiedCertificate); if (signatureCheckIsSuccessful) { synchronized (lazyAlreadyVerifiedPackageNameSet) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) { lazyAlreadyVerifiedPackageNameSet.add(currentPackageName); } } } return dexClassLoader.loadClass(className); } File containerToRemove = new File(containerPath); if (!containerToRemove.delete()) Log.i(TAG_SECURE_DEX_CLASS_LOADER, "It was impossible to delete " + containerPath); synchronized (packageNameToContainerPathMap) { Iterator<String> packageNamesIterator = packageNameToContainerPathMap.keySet().iterator(); while (packageNamesIterator.hasNext()) { String currentPackageName = packageNamesIterator.next(); if (packageNameToContainerPathMap.get(currentPackageName).equals(containerPath)) packageNamesIterator.remove(); } } return null; } return null; } } return dexClassLoader.loadClass(className); } @VisibleForTesting  SecureDexClassLoader(
            @NonNull String dexPath,
            @NonNull DexClassLoader dexClassLoader,
            @NonNull Context parentContext,
            @NonNull Map<String, URL> sanitizePackageNameToCertificateMap,
			boolean performLazyEvaluation,
            @NonNull ContainerSignatureVerifier containerSignatureVerifier,
            @NonNull CertificateFactory certificateFactory); Class<?> loadClass(@NonNull String className); void wipeOutPrivateAppCachedData(boolean containerPrivateFolder, boolean certificatePrivateFolder);  }