few updates
continuous-integration/drone/push Build encountered an error
Details
continuous-integration/drone/push Build encountered an error
Details
parent
d16ccd468c
commit
0950740480
@ -1,12 +1,37 @@
|
||||
---
|
||||
name: default
|
||||
kind: pipeline
|
||||
type: docker
|
||||
|
||||
steps:
|
||||
- name: code checks
|
||||
- name: setup
|
||||
image: composer
|
||||
volumes:
|
||||
- name: composer
|
||||
path: /composer
|
||||
environment:
|
||||
COMPOSER_HOME: /composer
|
||||
commands:
|
||||
- composer install --no-ansi --no-interaction
|
||||
- ./bin/setup
|
||||
|
||||
- name: code styling
|
||||
image: php
|
||||
commands:
|
||||
- ./vendor/bin/php-cs-fixer fix --dry-run src
|
||||
- ./vendor/bin/phpstan analyse -a config/autoload_with_route.php src
|
||||
- name: static analysis
|
||||
image: php
|
||||
commands:
|
||||
- ./vendor/bin/phpstan analyse -l max -a config/autoload_with_route.php src
|
||||
- name: testing
|
||||
image: php
|
||||
commands:
|
||||
- ./vendor/bin/phpunit
|
||||
|
||||
volumes:
|
||||
- name: composer
|
||||
host:
|
||||
path: composer
|
||||
---
|
||||
kind: signature
|
||||
hmac: 9dee19d61430e7e305581f66554fbefc139db4b0e7bc9eda1c7fdcd57a6c5ddc
|
@ -1,5 +1,7 @@
|
||||
/vendor/
|
||||
/var
|
||||
/var/*
|
||||
!/var/testdata
|
||||
/certs
|
||||
/.idea/
|
||||
.php_cs.cache
|
||||
.phpunit.result.cache
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.3/phpunit.xsd"
|
||||
bootstrap="config/autoload_with_route.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="suite">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
@ -0,0 +1,18 @@
|
||||
<?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;
|
||||
}
|
@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace CubiStore\Web\Utils;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class ApkSignUtils
|
||||
{
|
||||
const ZIP_END_OF_CENTRAL_DIRECTORY_RECORD = "PK\x05\x06";
|
||||
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 = "PK\x06\x06";
|
||||
|
||||
public static function findSignature($file)
|
||||
{
|
||||
self::tryAPKSignatureV2($file);
|
||||
}
|
||||
|
||||
public static function tryAPKSignatureV2($file)
|
||||
{
|
||||
// APK Signature Block is located -before- the Central Directory Record
|
||||
// Try finding offset of that
|
||||
$offset = static::findStartOfCentralDirectoryRecord($file);
|
||||
|
||||
$fh = fopen($file, 'r');
|
||||
if ($fh === false) {
|
||||
throw new RuntimeException("Couldn't open file '$file'");
|
||||
}
|
||||
|
||||
try {
|
||||
if (fseek($fh, $offset - 24) !== 0) {
|
||||
throw new RuntimeException("Couldn't seek in file '$file'");
|
||||
}
|
||||
|
||||
$data = fread($fh, 24);
|
||||
|
||||
if ($data === false) {
|
||||
throw new RuntimeException("Couldn't read file '$file'");
|
||||
}
|
||||
|
||||
if (substr($data, 8) === "APK Sig Block 42") {
|
||||
$blockSize = Bytes::readUint8LE($data);
|
||||
static::readAPKSignatureInfo($fh, $offset - $blockSize, $blockSize);
|
||||
}
|
||||
} finally {
|
||||
fclose($fh);
|
||||
}
|
||||
}
|
||||
|
||||
public static function findStartOfCentralDirectoryRecord($file): int
|
||||
{
|
||||
$size = filesize($file);
|
||||
if ($size === false) {
|
||||
throw new RuntimeException("Couldn't determine file size of file '$file'");
|
||||
}
|
||||
|
||||
$chunkSize = 4096;
|
||||
// Make sure we keep above 0
|
||||
$offset = max(0, $size - $chunkSize);
|
||||
|
||||
$fh = fopen($file, 'r');
|
||||
if ($fh === false) {
|
||||
throw new RuntimeException("Couldn't open file '$file'");
|
||||
}
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
if (fseek($fh, $offset) !== 0) {
|
||||
throw new RuntimeException("Couldn't seek in file '$file'");
|
||||
}
|
||||
|
||||
// Read chunk of data
|
||||
$data = fread($fh, $chunkSize);
|
||||
|
||||
if ($data === false) {
|
||||
throw new RuntimeException("Couldn't read file '$file'");
|
||||
}
|
||||
|
||||
$signatureOffset = 0;
|
||||
// Try resolving central directory offset from this chunk
|
||||
if (static::tryResolveCentralDirectoryOffset($fh, $file, $data, $chunkSize, $signatureOffset)) {
|
||||
// If found seek to offset
|
||||
if (fseek($fh, $signatureOffset) !== 0) {
|
||||
throw new RuntimeException("Couldn't seek in file '$file'");
|
||||
}
|
||||
|
||||
// Read header of block
|
||||
$centralDirectorySignature = fread($fh, 4);
|
||||
|
||||
if ($centralDirectorySignature === false) {
|
||||
throw new RuntimeException("Couldn't read file '$file'");
|
||||
}
|
||||
|
||||
// Check if block is Central Directory Record
|
||||
if ($centralDirectorySignature === static::ZIP_CENTRAL_DIRECTORY_RECORD) {
|
||||
return $signatureOffset;
|
||||
}
|
||||
}
|
||||
|
||||
$offset -= $chunkSize;
|
||||
|
||||
// Offset is set to 0 if it's negative
|
||||
// but 0 - chunkSize = -chunkSize
|
||||
// which means that we couldn't find it
|
||||
if ($offset <= -$chunkSize) {
|
||||
throw new RuntimeException("Can't EndOfCentralDirectoryRecord in '$file");
|
||||
}
|
||||
|
||||
if ($offset < 0) {
|
||||
$offset = 0;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unreachable code reached");
|
||||
}
|
||||
|
||||
protected static function tryResolveCentralDirectoryOffset($fh, string $file, string $data, int $chunkSize, int &$signatureOffset): bool
|
||||
{
|
||||
// Try to find signature
|
||||
$sigOffset = strrpos($data, static::ZIP_END_OF_CENTRAL_DIRECTORY_RECORD);
|
||||
|
||||
// Found signature, check if valid, or part of comment
|
||||
if ($sigOffset === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Minimal size of EOCD is 22 bytes.
|
||||
if ((strlen($data) - $sigOffset) < 22) {
|
||||
$moreData = fread($fh, $chunkSize);
|
||||
|
||||
if ($moreData === false) {
|
||||
throw new RuntimeException("Couldn't read file '$file'");
|
||||
}
|
||||
|
||||
$data .= $moreData;
|
||||
|
||||
if ((strlen($data) - $sigOffset) < 22) {
|
||||
// This can't be it, there's no space.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// read offset of Central Directory Record
|
||||
$offset = Bytes::readUint4LE($data, $sigOffset + 16);
|
||||
|
||||
//
|
||||
// - Fetch zip64 end of central directory record locator
|
||||
//
|
||||
|
||||
// if offset is less than 20, we need more data to fetch the zip64 end of central directory record locator
|
||||
if ($sigOffset < 20) {
|
||||
if (fseek($fh, $sigOffset - 20) !== 0) {
|
||||
throw new RuntimeException("Couldn't seek in file '$file'");
|
||||
}
|
||||
|
||||
$zip64Locator = fread($fh, 20);
|
||||
|
||||
if ($zip64Locator === false) {
|
||||
throw new RuntimeException("Couldn't read file '$file'");
|
||||
}
|
||||
} else {
|
||||
$zip64Locator = substr($data, $sigOffset - 20, 20);
|
||||
}
|
||||
|
||||
// Check if block is zip64 end of central directory record locator if so, read that.
|
||||
if (strlen($zip64Locator) === 20 && substr($zip64Locator, 0, 4) === static::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD_LOCATOR) {
|
||||
$zip64EOCDOffset = Bytes::readUint8LE($zip64Locator, 8);
|
||||
return static::readZip64EOCD($fh, $file, $zip64EOCDOffset, $signatureOffset, $offset);
|
||||
}
|
||||
|
||||
// Set offset into reference
|
||||
$signatureOffset = $offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function readZip64EOCD($fh, string $file, int $zip64EOCDOffset, int &$signatureOffset, int $legacyOffset): bool
|
||||
{
|
||||
if (fseek($fh, $zip64EOCDOffset) !== 0) {
|
||||
throw new RuntimeException("Couldn't seek in file '$file'");
|
||||
}
|
||||
|
||||
$zip64EOCD = fread($fh, 56);
|
||||
|
||||
if ($zip64EOCD === false) {
|
||||
throw new RuntimeException("Couldn't read file '$file'");
|
||||
}
|
||||
|
||||
// Check if offset from locator is zip64 end of central directory record
|
||||
if (substr($zip64EOCD, 0, 4) !== static::ZIP64_END_OF_CENTRAL_DIRECTORY_RECORD) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$zip64Offset = Bytes::readUint8LE($zip64EOCD, 48);
|
||||
|
||||
if ($legacyOffset !== 2 * 32 && $legacyOffset != $zip64Offset) {
|
||||
throw new RuntimeException("Corrupted ZIP, ZIP64 offset and normal offset don't match up.");
|
||||
}
|
||||
|
||||
// Read central directory record offset
|
||||
$signatureOffset = $zip64Offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function readAPKSignatureInfo($fh, int $offset, int $size)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace CubiStore\Web\Utils;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class Bytes
|
||||
{
|
||||
public static function readUint2LE($bytes, $offset = 0)
|
||||
{
|
||||
return static::unpackFirst('v', $bytes, $offset, 'int2');
|
||||
}
|
||||
|
||||
public static function readUint4LE($bytes, $offset = 0)
|
||||
{
|
||||
return static::unpackFirst('V', $bytes, $offset, 'int4');
|
||||
}
|
||||
|
||||
public static function readUint8LE($bytes, $offset = 0)
|
||||
{
|
||||
return static::unpackFirst('P', $bytes, $offset, 'int8');
|
||||
}
|
||||
|
||||
|
||||
private static function unpackFirst($format, $data, $offset, $what = 'bytes')
|
||||
{
|
||||
$result = unpack($format, $data, $offset);
|
||||
|
||||
if (!$result) {
|
||||
throw new RuntimeException("Failed reading $what from buffer");
|
||||
}
|
||||
|
||||
return reset($result);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace CubiStore\Tests\Web\Utils;
|
||||
|
||||
|
||||
use CubiStore\Web\Utils\ApkSignUtils;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ApkSignUtilsTest extends TestCase
|
||||
{
|
||||
function testSha1Signature()
|
||||
{
|
||||
$utils = new ApkSignUtils();
|
||||
$utils->findSignature(__DIR__ . '/../../var/testdata/signedapks/apksignv2.apk');
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue