add corrupted jar and start working on jar signing parsing
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
2b9ff0d04c
commit
b3180f70e0
5 changed files with 101 additions and 26 deletions
|
@ -1,18 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
|
|
||||||
namespace CubiStore\Web\Utils;
|
|
||||||
|
|
||||||
class APKSignatureInfo
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Which version of signature is used 1, 2, and 3 exist
|
|
||||||
* 1. uses JAR Signing
|
|
||||||
* 2. uses APK Signature Scheme
|
|
||||||
* 3. extended APK Signature Scheme
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
public $version;
|
|
||||||
public $signer;
|
|
||||||
public $signature;
|
|
||||||
}
|
|
|
@ -10,15 +10,21 @@ class ApkSignUtils
|
||||||
const ZIP_END_OF_CENTRAL_DIRECTORY_RECORD = "PK\x05\x06";
|
const ZIP_END_OF_CENTRAL_DIRECTORY_RECORD = "PK\x05\x06";
|
||||||
const ZIP_CENTRAL_DIRECTORY_RECORD = "PK\x01\x02";
|
const ZIP_CENTRAL_DIRECTORY_RECORD = "PK\x01\x02";
|
||||||
|
|
||||||
const ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_LOCATOR = "PK\x07\x06";
|
const ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_LOCATOR = "PK\x06\x07";
|
||||||
const ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD = "PK\x06\x06";
|
const ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD = "PK\x06\x06";
|
||||||
|
|
||||||
public static function findSignature($file)
|
public static function findSignature($file): ?ApkSignatureInfo
|
||||||
{
|
{
|
||||||
self::tryAPKSignatureV2($file);
|
$signature = self::tryAPKSignatureV2($file);
|
||||||
|
|
||||||
|
if ($signature !== null) {
|
||||||
|
return $signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tryAPKSignatureV2($file)
|
return self::tryJARSignature($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tryAPKSignatureV2($file): ?ApkSignatureInfo
|
||||||
{
|
{
|
||||||
// APK Signature Block is located -before- the Central Directory Record
|
// APK Signature Block is located -before- the Central Directory Record
|
||||||
// Try finding offset of that
|
// Try finding offset of that
|
||||||
|
@ -42,11 +48,13 @@ class ApkSignUtils
|
||||||
|
|
||||||
if (substr($data, 8) === "APK Sig Block 42") {
|
if (substr($data, 8) === "APK Sig Block 42") {
|
||||||
$blockSize = Bytes::readUint8LE($data);
|
$blockSize = Bytes::readUint8LE($data);
|
||||||
static::readAPKSignatureInfo($fh, $offset - $blockSize, $blockSize);
|
return static::readAPKSignatureInfo($fh, $offset - $blockSize, $blockSize);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
fclose($fh);
|
fclose($fh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function findStartOfCentralDirectoryRecord($file): int
|
public static function findStartOfCentralDirectoryRecord($file): int
|
||||||
|
@ -197,7 +205,7 @@ class ApkSignUtils
|
||||||
|
|
||||||
$zip64Offset = Bytes::readUint8LE($zip64EOCD, 48);
|
$zip64Offset = Bytes::readUint8LE($zip64EOCD, 48);
|
||||||
|
|
||||||
if ($legacyOffset !== 2 * 32 && $legacyOffset != $zip64Offset) {
|
if ($legacyOffset !== 2 ** 32 && $legacyOffset !== $zip64Offset) {
|
||||||
throw new RuntimeException("Corrupted ZIP, ZIP64 offset and normal offset don't match up.");
|
throw new RuntimeException("Corrupted ZIP, ZIP64 offset and normal offset don't match up.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +214,50 @@ class ApkSignUtils
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function readAPKSignatureInfo($fh, int $offset, int $size)
|
private static function readAPKSignatureInfo($fh, int $offset, int $size): ?ApkSignatureInfo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function tryJARSignature($file)
|
||||||
|
{
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
if ($zip->open($file) !== true) {
|
||||||
|
throw new RuntimeException("Failed to open APK");
|
||||||
|
}
|
||||||
|
|
||||||
|
$manifest = $zip->getFromName("META-INF/MANIFEST.MF");
|
||||||
|
|
||||||
|
if ($manifest === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$found = false;
|
||||||
|
$entryName = "";
|
||||||
|
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||||
|
$entryStat = $zip->statIndex($i);
|
||||||
|
if ($entryStat === false || !isset($entryStat['name'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entryName = $entryStat['name'];
|
||||||
|
|
||||||
|
if (!preg_match("~^META-INF/[^\/]+\.SF$~", $entryName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$found) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$signManifest = $zip->getFromName($entryName);
|
||||||
|
|
||||||
|
if ($signManifest === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
34
src/Utils/ApkSignatureInfo.php
Normal file
34
src/Utils/ApkSignatureInfo.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace CubiStore\Web\Utils;
|
||||||
|
|
||||||
|
class ApkSignatureInfo
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Which version of signature is used 1, 2, and 3 exist
|
||||||
|
* 1. uses JAR Signing
|
||||||
|
* 2. uses APK Signature Scheme
|
||||||
|
* 3. extended APK Signature Scheme
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
public $version;
|
||||||
|
public $signer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* As taken from the fdroidserver code:
|
||||||
|
*
|
||||||
|
* This uses a strange algorithm that was devised at the very
|
||||||
|
* beginning of F-Droid. Since it is only used for checking
|
||||||
|
* signature compatibility, it does not matter much that it uses MD5.
|
||||||
|
*
|
||||||
|
* To get the same MD5 has that fdroidclient gets, we encode the .RSA
|
||||||
|
* certificate in a specific format and pass it hex-encoded to the
|
||||||
|
* md5 digest algorithm. This is not the same as the standard X.509
|
||||||
|
* certificate fingerprint.
|
||||||
|
*
|
||||||
|
* @link https://gitlab.com/fdroid/fdroidserver/blob/master/fdroidserver/update.py#L418
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $fdroidSignerSignature;
|
||||||
|
}
|
|
@ -9,9 +9,17 @@ use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ApkSignUtilsTest extends TestCase
|
class ApkSignUtilsTest extends TestCase
|
||||||
{
|
{
|
||||||
|
function testCorruptedZip64() {
|
||||||
|
$this->expectException(\RuntimeException::class);
|
||||||
|
$this->expectExceptionMessage("Corrupted ZIP, ZIP64 offset and normal offset don't match up.");
|
||||||
|
$utils = new ApkSignUtils();
|
||||||
|
$utils->findSignature(__DIR__ . '/../../var/testdata/signedapks/zip64corrupted.jar');
|
||||||
|
}
|
||||||
|
|
||||||
function testSha1Signature()
|
function testSha1Signature()
|
||||||
{
|
{
|
||||||
$utils = new ApkSignUtils();
|
$utils = new ApkSignUtils();
|
||||||
$utils->findSignature(__DIR__ . '/../../var/testdata/signedapks/apksignv2.apk');
|
$apkSignature = $utils->findSignature(__DIR__ . '/../../var/testdata/signedapks/sha1.apk');
|
||||||
|
$this->assertNotNull($apkSignature, "Couldn't find APK signature");
|
||||||
}
|
}
|
||||||
}
|
}
|
BIN
var/testdata/signedapks/zip64corrupted.jar
vendored
Normal file
BIN
var/testdata/signedapks/zip64corrupted.jar
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue