1 /* 2 * Copyright (C) 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.util.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.support.test.InstrumentationRegistry; 26 import android.support.test.filters.SmallTest; 27 import android.support.test.runner.AndroidJUnit4; 28 import android.system.OsConstants; 29 import android.util.jar.StrictJarFile; 30 31 import libcore.io.IoBridge; 32 import libcore.io.Streams; 33 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.io.File; 39 import java.io.FileDescriptor; 40 import java.io.FileOutputStream; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.nio.charset.Charset; 44 import java.util.HashMap; 45 import java.util.Iterator; 46 import java.util.zip.ZipEntry; 47 48 @SmallTest 49 @RunWith(AndroidJUnit4.class) 50 public class StrictJarFileTest { 51 // A well formed jar file with 6 entries. 52 private static final String JAR_1 = "hyts_patch.jar"; 53 54 private File mResourcesFile; 55 56 @Before 57 public void setup() { 58 try { 59 mResourcesFile = File.createTempFile("sjf_resources", "", null); 60 mResourcesFile.delete(); 61 mResourcesFile.mkdirs(); 62 } catch (IOException e) { 63 throw new RuntimeException("Unable to create temp folder", e); 64 } 65 mResourcesFile.deleteOnExit(); 66 } 67 68 @Test(expected=IOException.class) 69 public void testConstructorWrongFile() throws IOException { 70 new StrictJarFile("Wrong.file"); 71 } 72 73 @Test(expected=IOException.class) 74 public void testConstructorWrongFile_FD() throws IOException { 75 new StrictJarFile(new FileDescriptor()); 76 } 77 78 @Test(expected=NullPointerException.class) 79 public void testConstructorWrongFile_FD_null() throws IOException { 80 new StrictJarFile((FileDescriptor) null); 81 } 82 83 @Test 84 public void testConstructor() throws Exception { 85 copyFile(JAR_1); 86 String fileName = (new File(mResourcesFile, JAR_1)).getCanonicalPath(); 87 StrictJarFile jarFile = new StrictJarFile(fileName); 88 jarFile.close(); 89 } 90 91 @Test 92 public void testConstructor_FD() throws Exception { 93 copyFile(JAR_1); 94 FileDescriptor fd = IoBridge.open( 95 new File(mResourcesFile, JAR_1).getAbsolutePath(), OsConstants.O_RDONLY); 96 StrictJarFile jarFile = new StrictJarFile(fd); 97 jarFile.close(); 98 } 99 100 @Test 101 public void testIteration() throws Exception { 102 copyFile(JAR_1); 103 StrictJarFile jarFile = 104 new StrictJarFile(new File(mResourcesFile, JAR_1).getAbsolutePath()); 105 checkIteration(jarFile); 106 } 107 108 @Test 109 public void testIteration_FD() throws Exception { 110 copyFile(JAR_1); 111 FileDescriptor fd = IoBridge.open( 112 new File(mResourcesFile, JAR_1).getAbsolutePath(), OsConstants.O_RDONLY); 113 StrictJarFile jarFile = new StrictJarFile(fd); 114 checkIteration(jarFile); 115 } 116 117 private static void checkIteration(StrictJarFile jarFile) throws Exception { 118 Iterator<ZipEntry> it = jarFile.iterator(); 119 HashMap<String, ZipEntry> entries = new HashMap<>(); 120 while (it.hasNext()) { 121 final ZipEntry ze = it.next(); 122 entries.put(ze.getName(), ze); 123 } 124 125 assertEquals(6, entries.size()); 126 assertTrue(entries.containsKey("META-INF/")); 127 128 assertTrue(entries.containsKey("META-INF/MANIFEST.MF")); 129 ZipEntry ze = entries.get("META-INF/MANIFEST.MF"); 130 assertEquals(62, ze.getSize()); 131 assertEquals(ZipEntry.DEFLATED, ze.getMethod()); 132 assertEquals(61, ze.getCompressedSize()); 133 134 assertTrue(entries.containsKey("Blah.txt")); 135 ze = entries.get("Blah.txt"); 136 assertEquals(4, ze.getSize()); 137 assertEquals(ZipEntry.DEFLATED, ze.getMethod()); 138 assertEquals(6, ze.getCompressedSize()); 139 assertEquals("Blah", new String(Streams.readFully(jarFile.getInputStream(ze)), 140 Charset.forName("UTF-8"))); 141 142 assertTrue(entries.containsKey("foo/")); 143 assertTrue(entries.containsKey("foo/bar/")); 144 assertTrue(entries.containsKey("foo/bar/A.class")); 145 ze = entries.get("foo/bar/A.class"); 146 assertEquals(311, ze.getSize()); 147 assertEquals(ZipEntry.DEFLATED, ze.getMethod()); 148 assertEquals(225, ze.getCompressedSize()); 149 } 150 151 @Test 152 public void testFindEntry() throws Exception { 153 copyFile(JAR_1); 154 StrictJarFile jarFile = 155 new StrictJarFile(new File(mResourcesFile, JAR_1).getAbsolutePath()); 156 checkFindEntry(jarFile); 157 } 158 159 @Test 160 public void testFindEntry_FD() throws Exception { 161 copyFile(JAR_1); 162 FileDescriptor fd = IoBridge.open( 163 new File(mResourcesFile, JAR_1).getAbsolutePath(), OsConstants.O_RDONLY); 164 StrictJarFile jarFile = new StrictJarFile(fd); 165 checkFindEntry(jarFile); 166 } 167 168 private static void checkFindEntry(StrictJarFile jarFile) throws Exception { 169 assertNull(jarFile.findEntry("foobar")); 170 assertNull(jarFile.findEntry("blah.txt")); 171 assertNotNull(jarFile.findEntry("Blah.txt")); 172 final ZipEntry ze = jarFile.findEntry("Blah.txt"); 173 assertEquals(4, ze.getSize()); 174 assertEquals(ZipEntry.DEFLATED, ze.getMethod()); 175 assertEquals(6, ze.getCompressedSize()); 176 assertEquals("Blah", new String(Streams.readFully(jarFile.getInputStream(ze)), 177 Charset.forName("UTF-8"))); 178 } 179 180 @Test 181 public void testGetManifest() throws Exception { 182 copyFile(JAR_1); 183 StrictJarFile jarFile = 184 new StrictJarFile(new File(mResourcesFile, JAR_1).getAbsolutePath()); 185 checkGetManifest(jarFile); 186 } 187 188 @Test 189 public void testGetManifest_FD() throws Exception { 190 copyFile(JAR_1); 191 FileDescriptor fd = IoBridge.open( 192 new File(mResourcesFile, JAR_1).getAbsolutePath(), OsConstants.O_RDONLY); 193 StrictJarFile jarFile = new StrictJarFile(fd); 194 checkGetManifest(jarFile); 195 } 196 197 private static void checkGetManifest(StrictJarFile jarFile) throws Exception { 198 assertNotNull(jarFile.getManifest()); 199 assertEquals("1.4.2 (IBM Corporation)", 200 jarFile.getManifest().getMainAttributes().getValue("Created-By")); 201 } 202 203 @Test 204 public void testJarSigning_wellFormed() throws IOException { 205 copyFile("Integrate.jar"); 206 StrictJarFile jarFile = 207 new StrictJarFile(new File(mResourcesFile, "Integrate.jar").getAbsolutePath()); 208 checkJarSigning_wellFormed(jarFile); 209 } 210 211 @Test 212 public void testJarSigning_wellFormed_FD() throws IOException { 213 copyFile("Integrate.jar"); 214 FileDescriptor fd = IoBridge.open( 215 new File(mResourcesFile, "Integrate.jar").getAbsolutePath(), 216 OsConstants.O_RDONLY); 217 StrictJarFile jarFile = new StrictJarFile(fd); 218 checkJarSigning_wellFormed(jarFile); 219 } 220 221 private static void checkJarSigning_wellFormed(StrictJarFile jarFile) throws IOException { 222 Iterator<ZipEntry> entries = jarFile.iterator(); 223 while (entries.hasNext()) { 224 ZipEntry zipEntry = entries.next(); 225 jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE); 226 if ("Test.class".equals(zipEntry.getName())) { 227 assertNotNull(jarFile.getCertificates(zipEntry)); 228 assertNotNull(jarFile.getCertificateChains(zipEntry)); 229 } 230 } 231 } 232 233 @Test 234 public void testJarSigning_fudgedEntry() throws IOException { 235 copyFile("Integrate.jar"); 236 StrictJarFile jarFile = new StrictJarFile( 237 new File(mResourcesFile, "Integrate.jar").getAbsolutePath()); 238 checkJarSigning_fudgedEntry(jarFile); 239 } 240 241 @Test 242 public void testJarSigning_fudgedEntry_FD() throws IOException { 243 copyFile("Integrate.jar"); 244 FileDescriptor fd = IoBridge.open( 245 new File(mResourcesFile, "Integrate.jar").getAbsolutePath(), 246 OsConstants.O_RDONLY); 247 StrictJarFile jarFile = new StrictJarFile(fd); 248 checkJarSigning_fudgedEntry(jarFile); 249 } 250 251 private static void checkJarSigning_fudgedEntry(StrictJarFile jarFile) throws IOException { 252 ZipEntry ze = jarFile.findEntry("Test.class"); 253 jarFile.getInputStream(ze).skip(Long.MAX_VALUE); 254 255 // Fudge the size so that certificates do not match. 256 ze.setSize(ze.getSize() - 1); 257 try { 258 jarFile.getInputStream(ze).skip(Long.MAX_VALUE); 259 fail(); 260 } catch (SecurityException expected) { 261 } 262 } 263 264 @Test 265 public void testJarSigning_modifiedClass() throws IOException { 266 copyFile("Modified_Class.jar"); 267 StrictJarFile jarFile = new StrictJarFile( 268 new File(mResourcesFile, "Modified_Class.jar").getAbsolutePath()); 269 checkJarSigning_modifiedClass(jarFile); 270 } 271 272 @Test 273 public void testJarSigning_modifiedClass_FD() throws IOException { 274 copyFile("Modified_Class.jar"); 275 FileDescriptor fd = IoBridge.open( 276 new File(mResourcesFile, "Modified_Class.jar").getAbsolutePath(), 277 OsConstants.O_RDONLY); 278 StrictJarFile jarFile = new StrictJarFile(fd); 279 checkJarSigning_modifiedClass(jarFile); 280 } 281 282 private static void checkJarSigning_modifiedClass(StrictJarFile jarFile) 283 throws IOException { 284 ZipEntry ze = jarFile.findEntry("Test.class"); 285 try { 286 jarFile.getInputStream(ze).skip(Long.MAX_VALUE); 287 fail(); 288 } catch (SecurityException expected) { 289 } 290 } 291 292 @Test 293 public void testJarSigning_brokenMainAttributes() throws Exception { 294 verifyThrowsOnInit("Modified_Manifest_MainAttributes.jar"); 295 } 296 297 @Test 298 public void testJarSigning_brokenMainAttributes_FD() throws Exception { 299 verifyThrowsOnInitFD("Modified_Manifest_MainAttributes.jar"); 300 } 301 302 @Test 303 public void testJarSigning_brokenEntryAttributes() throws Exception { 304 verifyThrowsOnInit("Modified_Manifest_EntryAttributes.jar"); 305 } 306 307 @Test 308 public void testJarSigning_brokenEntryAttributes_FD() throws Exception { 309 verifyThrowsOnInitFD("Modified_Manifest_EntryAttributes.jar"); 310 } 311 312 @Test 313 public void testJarSigning_brokenSignatureFile() throws Exception { 314 verifyThrowsOnInit("Modified_SF_EntryAttributes.jar"); 315 } 316 317 @Test 318 public void testJarSigning_brokenSignatureFile_FD() throws Exception { 319 verifyThrowsOnInitFD("Modified_SF_EntryAttributes.jar"); 320 } 321 322 @Test 323 public void testJarSigning_removedEntry() throws Exception { 324 verifyThrowsOnInit("removed.jar"); 325 } 326 327 @Test 328 public void testJarSigning_removedEntry_FD() throws Exception { 329 verifyThrowsOnInitFD("removed.jar"); 330 } 331 332 private void verifyThrowsOnInit(String name) throws Exception { 333 copyFile(name); 334 try { 335 new StrictJarFile(new File(mResourcesFile, name).getAbsolutePath()); 336 fail(); 337 } catch (SecurityException expected) { 338 } 339 } 340 341 private void verifyThrowsOnInitFD(String name) throws Exception { 342 copyFile(name); 343 FileDescriptor fd = IoBridge.open( 344 new File(mResourcesFile, name).getAbsolutePath(), 345 OsConstants.O_RDONLY); 346 try { 347 new StrictJarFile(fd); 348 fail(); 349 } catch (SecurityException expected) { 350 } 351 } 352 353 private File copyFile(String file) { 354 File dest = new File(mResourcesFile.toString() + "/" + file); 355 356 if (!dest.exists()) { 357 try { 358 InputStream in = InstrumentationRegistry.getTargetContext().getAssets().open(file); 359 FileOutputStream out = new FileOutputStream(dest); 360 byte[] buffer = new byte[8192]; 361 int c; 362 while ((c = in.read(buffer)) != -1) { 363 out.write(buffer, 0, c); 364 } 365 out.close(); 366 dest.deleteOnExit(); 367 in.close(); 368 } catch (IOException e) { 369 throw new RuntimeException("Unable to copy file from resource " + file 370 + " to file " + dest, e); 371 } 372 } 373 return dest; 374 } 375 } 376