OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.webapk.lib.client; | 5 package org.chromium.webapk.lib.client; |
6 | 6 |
7 import static java.nio.ByteOrder.LITTLE_ENDIAN; | 7 import static java.nio.ByteOrder.LITTLE_ENDIAN; |
8 | 8 |
9 import android.support.annotation.IntDef; | 9 import android.support.annotation.IntDef; |
10 import android.util.Log; | 10 import android.util.Log; |
11 | 11 |
12 import java.nio.ByteBuffer; | 12 import java.nio.ByteBuffer; |
13 import java.security.PublicKey; | 13 import java.security.PublicKey; |
14 import java.security.Signature; | 14 import java.security.Signature; |
15 import java.util.ArrayList; | 15 import java.util.ArrayList; |
16 import java.util.Collections; | 16 import java.util.Collections; |
| 17 import java.util.Comparator; |
17 import java.util.regex.Matcher; | 18 import java.util.regex.Matcher; |
18 import java.util.regex.Pattern; | 19 import java.util.regex.Pattern; |
19 | 20 |
20 /** | 21 /** |
21 * WebApkVerifySignature reads in the APK file and verifies the WebApk signature
. It reads the | 22 * WebApkVerifySignature reads in the APK file and verifies the WebApk signature
. It reads the |
22 * signature from the zip comment and verifies that it was signed by the public
key passed. | 23 * signature from the zip comment and verifies that it was signed by the public
key passed. |
23 */ | 24 */ |
24 public class WebApkVerifySignature { | 25 public class WebApkVerifySignature { |
25 /** Errors codes. */ | 26 /** Errors codes. */ |
26 @IntDef({ | 27 @IntDef({ |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 mHeaderSize = 0; | 120 mHeaderSize = 0; |
120 mCompressedSize = compressedSize; | 121 mCompressedSize = compressedSize; |
121 } | 122 } |
122 | 123 |
123 /** Added for Comparable, sort lexicographically. */ | 124 /** Added for Comparable, sort lexicographically. */ |
124 @Override | 125 @Override |
125 public int compareTo(Block o) { | 126 public int compareTo(Block o) { |
126 return mFilename.compareTo(o.mFilename); | 127 return mFilename.compareTo(o.mFilename); |
127 } | 128 } |
128 | 129 |
| 130 /** Comparator for sorting the list by position ascending. */ |
| 131 public static Comparator<Block> positionComparator = new Comparator<Bloc
k>() { |
| 132 @Override |
| 133 public int compare(Block b1, Block b2) { |
| 134 return b1.mPosition - b2.mPosition; |
| 135 } |
| 136 }; |
| 137 |
129 @Override | 138 @Override |
130 public boolean equals(Object o) { | 139 public boolean equals(Object o) { |
131 if (!(o instanceof Block)) return false; | 140 if (!(o instanceof Block)) return false; |
132 return mFilename.equals(((Block) o).mFilename); | 141 return mFilename.equals(((Block) o).mFilename); |
133 } | 142 } |
134 | 143 |
135 @Override | 144 @Override |
136 public int hashCode() { | 145 public int hashCode() { |
137 return mFilename.hashCode(); | 146 return mFilename.hashCode(); |
138 } | 147 } |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 return ERROR_COMMENT_TOO_LARGE; | 326 return ERROR_COMMENT_TOO_LARGE; |
318 } | 327 } |
319 mBlocks.add(new Block(filename, offset, compressedSize)); | 328 mBlocks.add(new Block(filename, offset, compressedSize)); |
320 } | 329 } |
321 | 330 |
322 if (mBuffer.position() != mEndOfCentralDirOffset) { | 331 if (mBuffer.position() != mEndOfCentralDirOffset) { |
323 // At this point we should be exactly at the EOCD start. | 332 // At this point we should be exactly at the EOCD start. |
324 return ERROR_BAD_BLANK_SPACE; | 333 return ERROR_BAD_BLANK_SPACE; |
325 } | 334 } |
326 | 335 |
327 boolean hasBlockAtStart = false; | 336 // We need blocks to be sorted by position at this point. |
328 long positionOfLastByteOfLastBlock = 0; | 337 Collections.sort(mBlocks, Block.positionComparator); |
| 338 int lastByte = 0; |
329 | 339 |
330 // Read the 'local file header' block to the size of the header in bytes
. | 340 // Read the 'local file header' block to the size of the header in bytes
. |
331 for (Block block : mBlocks) { | 341 for (Block block : mBlocks) { |
| 342 if (block.mPosition != lastByte) { |
| 343 return ERROR_BAD_BLANK_SPACE; |
| 344 } |
| 345 |
332 seek(block.mPosition); | 346 seek(block.mPosition); |
333 if (block.mPosition == 0) { | |
334 hasBlockAtStart = true; | |
335 } | |
336 int signature = read4(); | 347 int signature = read4(); |
337 if (signature != LFH_SIG) { | 348 if (signature != LFH_SIG) { |
338 Log.d(TAG, "LFH Signature missing"); | 349 Log.d(TAG, "LFH Signature missing"); |
339 return ERROR_BAD_APK; | 350 return ERROR_BAD_APK; |
340 } | 351 } |
341 // ReaderVersion(2) | 352 // ReaderVersion(2) |
342 seekDelta(2); | 353 seekDelta(2); |
343 int flags = read2(); | 354 int flags = read2(); |
344 // CompressionMethod(2), ModifiedTime (2), ModifiedDate(2), CRC32(4)
, CompressedSize(4), | 355 // CompressionMethod(2), ModifiedTime (2), ModifiedDate(2), CRC32(4)
, CompressedSize(4), |
345 // UncompressedSize(4) = 18 bytes | 356 // UncompressedSize(4) = 18 bytes |
346 seekDelta(18); | 357 seekDelta(18); |
347 int fileNameLength = read2(); | 358 int fileNameLength = read2(); |
348 int extraFieldLength = read2(); | 359 int extraFieldLength = read2(); |
349 if (extraFieldLength > MAX_EXTRA_LENGTH) { | 360 if (extraFieldLength > MAX_EXTRA_LENGTH) { |
350 return ERROR_EXTRA_FIELD_TOO_LARGE; | 361 return ERROR_EXTRA_FIELD_TOO_LARGE; |
351 } | 362 } |
352 | 363 |
353 block.mHeaderSize = | 364 block.mHeaderSize = |
354 (mBuffer.position() - block.mPosition) + fileNameLength + ex
traFieldLength; | 365 (mBuffer.position() - block.mPosition) + fileNameLength + ex
traFieldLength; |
355 | 366 |
356 int lastByte = block.mPosition + block.mHeaderSize + block.mCompress
edSize; | 367 lastByte = block.mPosition + block.mHeaderSize + block.mCompressedSi
ze; |
357 if ((flags & 0x8) != 0) { | 368 if ((flags & 0x8) != 0) { |
358 seek(lastByte); | 369 seek(lastByte); |
359 if (read4() == DATA_DESCRIPTOR_SIG) { | 370 if (read4() == DATA_DESCRIPTOR_SIG) { |
360 // Data descriptor, style 1: sig(4), crc-32(4), compressed s
ize(4), | 371 // Data descriptor, style 1: sig(4), crc-32(4), compressed s
ize(4), |
361 // uncompressed size(4) = 16 bytes | 372 // uncompressed size(4) = 16 bytes |
362 lastByte += 16; | 373 lastByte += 16; |
363 } else { | 374 } else { |
364 // Data descriptor, style 2: crc-32(4), compressed size(4), | 375 // Data descriptor, style 2: crc-32(4), compressed size(4), |
365 // uncompressed size(4) = 12 bytes | 376 // uncompressed size(4) = 12 bytes |
366 lastByte += 12; | 377 lastByte += 12; |
367 } | 378 } |
368 } | 379 } |
369 if (lastByte > positionOfLastByteOfLastBlock) { | |
370 positionOfLastByteOfLastBlock = lastByte; | |
371 } | |
372 } | 380 } |
373 if (!hasBlockAtStart) { | 381 if (lastByte != mCentralDirOffset) { |
374 return ERROR_BAD_BLANK_SPACE; | |
375 } | |
376 if (positionOfLastByteOfLastBlock != mCentralDirOffset) { | |
377 seek(mCentralDirOffset - V2_SIGNING_MAGIC.length()); | 382 seek(mCentralDirOffset - V2_SIGNING_MAGIC.length()); |
378 String magic = readString(V2_SIGNING_MAGIC.length()); | 383 String magic = readString(V2_SIGNING_MAGIC.length()); |
379 if (V2_SIGNING_MAGIC.equals(magic)) { | 384 if (V2_SIGNING_MAGIC.equals(magic)) { |
380 // Only if we have a v2 signature do we allow medium sized gap b
etween the last | 385 // Only if we have a v2 signature do we allow medium sized gap b
etween the last |
381 // block and the start of the central directory. | 386 // block and the start of the central directory. |
382 if (mCentralDirOffset - positionOfLastByteOfLastBlock > MAX_V2_S
IGNING_BLOCK_SIZE) { | 387 if (mCentralDirOffset - lastByte > MAX_V2_SIGNING_BLOCK_SIZE) { |
383 return ERROR_BAD_V2_SIGNING_BLOCK; | 388 return ERROR_BAD_V2_SIGNING_BLOCK; |
384 } | 389 } |
385 } else { | 390 } else { |
386 return ERROR_BAD_BLANK_SPACE; | 391 return ERROR_BAD_BLANK_SPACE; |
387 } | 392 } |
388 } | 393 } |
389 return ERROR_OK; | 394 return ERROR_OK; |
390 } | 395 } |
391 | 396 |
392 /** | 397 /** |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
461 return null; | 466 return null; |
462 } | 467 } |
463 byte[] data = new byte[len / 2]; | 468 byte[] data = new byte[len / 2]; |
464 for (int i = 0; i < len; i += 2) { | 469 for (int i = 0; i < len; i += 2) { |
465 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) | 470 data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) |
466 + Character.digit(s.charAt(i + 1), 16)); | 471 + Character.digit(s.charAt(i + 1), 16)); |
467 } | 472 } |
468 return data; | 473 return data; |
469 } | 474 } |
470 } | 475 } |
OLD | NEW |