OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.webapk.lib.client; |
| 6 |
| 7 import static org.junit.Assert.assertArrayEquals; |
| 8 import static org.junit.Assert.assertEquals; |
| 9 import static org.junit.Assert.assertTrue; |
| 10 |
| 11 import org.junit.Test; |
| 12 import org.junit.runner.RunWith; |
| 13 import org.robolectric.annotation.Config; |
| 14 |
| 15 import org.chromium.testing.local.LocalRobolectricTestRunner; |
| 16 import org.chromium.webapk.lib.client.WebApkVerifySignature.Error; |
| 17 |
| 18 import java.io.File; |
| 19 import java.io.RandomAccessFile; |
| 20 import java.nio.MappedByteBuffer; |
| 21 import java.nio.channels.FileChannel; |
| 22 import java.nio.file.Files; |
| 23 import java.nio.file.Paths; |
| 24 import java.security.KeyFactory; |
| 25 import java.security.PublicKey; |
| 26 import java.security.SignatureException; |
| 27 import java.security.spec.X509EncodedKeySpec; |
| 28 |
| 29 /** Unit tests for WebApkVerifySignature for Android. */ |
| 30 @RunWith(LocalRobolectricTestRunner.class) |
| 31 @Config(manifest = Config.NONE) |
| 32 public class WebApkVerifySignatureTest { |
| 33 /** Elliptical Curves, Digital Signature Algorithm */ |
| 34 private static final String KEY_FACTORY = "EC"; |
| 35 |
| 36 private static final String TEST_DATA_DIR = "chrome/test/data/webapks/"; |
| 37 |
| 38 static PublicKey readPublicKey(String publicDER) throws Exception { |
| 39 return createPublicKey(Files.readAllBytes(Paths.get(publicDER))); |
| 40 } |
| 41 |
| 42 private static PublicKey createPublicKey(byte[] bytes) throws Exception { |
| 43 return KeyFactory.getInstance(KEY_FACTORY).generatePublic(new X509Encode
dKeySpec(bytes)); |
| 44 } |
| 45 |
| 46 @Test |
| 47 public void testHexToBytes() throws Exception { |
| 48 assertEquals(null, WebApkVerifySignature.hexToBytes("")); |
| 49 byte[] test = {(byte) 0xFF, (byte) 0xFE, 0x00, 0x01}; |
| 50 assertArrayEquals(test, WebApkVerifySignature.hexToBytes("fffe0001")); |
| 51 assertArrayEquals(test, WebApkVerifySignature.hexToBytes("FFFE0001")); |
| 52 assertEquals(null, WebApkVerifySignature.hexToBytes("f")); // Odd number
of nibbles. |
| 53 } |
| 54 |
| 55 @Test |
| 56 public void testCommentHash() throws Exception { |
| 57 byte[] bytes = hexStringToByteArray("decafbad"); |
| 58 assertEquals(null, WebApkVerifySignature.parseCommentSignature("weapk:de
cafbad")); |
| 59 assertEquals(null, WebApkVerifySignature.parseCommentSignature("webapk:"
)); |
| 60 assertArrayEquals(bytes, WebApkVerifySignature.parseCommentSignature("we
bapk:decafbad")); |
| 61 assertArrayEquals(bytes, WebApkVerifySignature.parseCommentSignature("we
bapk:12:decafbad")); |
| 62 assertArrayEquals( |
| 63 bytes, WebApkVerifySignature.parseCommentSignature("webapk:02: d
ecafbad")); |
| 64 assertArrayEquals( |
| 65 bytes, WebApkVerifySignature.parseCommentSignature("webapk: 02:
decafbad")); |
| 66 assertArrayEquals( |
| 67 bytes, WebApkVerifySignature.parseCommentSignature("XXXwebapk:de
cafbadXXX")); |
| 68 assertArrayEquals( |
| 69 bytes, WebApkVerifySignature.parseCommentSignature("\n\nwebapk:d
ecafbad\n\n")); |
| 70 } |
| 71 |
| 72 public static String testFilename(String fname) { |
| 73 return new File(TEST_DATA_DIR + fname).getPath(); |
| 74 } |
| 75 |
| 76 @Test |
| 77 public void testVerifyFiles() throws Exception { |
| 78 PublicKey pub = readPublicKey(testFilename("public.der")); |
| 79 String[] filenames = {"example.apk", "java-example.apk"}; |
| 80 for (String filename : filenames) { |
| 81 RandomAccessFile file = new RandomAccessFile(testFilename(filename),
"r"); |
| 82 FileChannel inChannel = file.getChannel(); |
| 83 MappedByteBuffer buf = |
| 84 inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.si
ze()); |
| 85 buf.load(); |
| 86 WebApkVerifySignature v = new WebApkVerifySignature(buf); |
| 87 assertEquals(Error.OK, v.read()); |
| 88 assertEquals(Error.OK, v.verifySignature(pub)); |
| 89 buf.clear(); |
| 90 inChannel.close(); |
| 91 file.close(); |
| 92 } |
| 93 } |
| 94 |
| 95 @Test |
| 96 public void testBadVerifyFiles() throws Exception { |
| 97 PublicKey pub = readPublicKey(testFilename("public.der")); |
| 98 String[] filenames = { |
| 99 "bad-sig.apk", "bad-utf8-fname.apk", "empty.apk", "extra-len-too
-large.apk", |
| 100 "fcomment-too-large.apk", "no-cd.apk", "no-comment.apk", "no-eoc
d.apk", |
| 101 "no-lfh.apk", "not-an.apk", "too-many-metainf.apk", "truncated.a
pk", "zeros.apk", |
| 102 "zeros-at-end.apk", |
| 103 }; |
| 104 for (String filename : filenames) { |
| 105 RandomAccessFile file = new RandomAccessFile(testFilename(filename),
"r"); |
| 106 FileChannel inChannel = file.getChannel(); |
| 107 MappedByteBuffer buf = |
| 108 inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.si
ze()); |
| 109 buf.load(); |
| 110 WebApkVerifySignature v = new WebApkVerifySignature(buf); |
| 111 try { |
| 112 if (v.read() == Error.OK) { |
| 113 assertTrue(v.verifySignature(pub) != WebApkVerifySignature.E
rror.OK); |
| 114 } |
| 115 } catch (SignatureException | IllegalArgumentException e) { |
| 116 // expected |
| 117 } |
| 118 buf.clear(); |
| 119 inChannel.close(); |
| 120 file.close(); |
| 121 } |
| 122 } |
| 123 |
| 124 private static byte[] hexStringToByteArray(String s) { |
| 125 int len = s.length(); |
| 126 if (len % 2 == 1) { |
| 127 // Expect an even number of nibbles. |
| 128 return null; |
| 129 } |
| 130 byte[] data = new byte[len / 2]; |
| 131 for (int i = 0; i < len; i += 2) { |
| 132 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) |
| 133 | Character.digit(s.charAt(i + 1), 16)); |
| 134 } |
| 135 return data; |
| 136 } |
| 137 } |
OLD | NEW |