Skip to content

Commit

Permalink
Merge pull request #124 from includable/feat/add-basic-test
Browse files Browse the repository at this point in the history
  • Loading branch information
tschoffelen committed Apr 21, 2023
2 parents dddfca2 + c0675cf commit 9b4db7c
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 27 deletions.
9 changes: 0 additions & 9 deletions .github/lock.yml

This file was deleted.

36 changes: 36 additions & 0 deletions .github/workflows/test.yml
@@ -0,0 +1,36 @@
name: PHPUnit Tests on Multiple PHP Versions

on: [ push, pull_request ]

jobs:
tests:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
php: [ "7.4", "8.0", "8.1", "8.2" ]
openssl_legacy: [ false, true ]

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Use OpenSSL legacy mode
if: ${{ matrix.openssl_legacy }}
run: |
printf "openssl_conf = openssl_init\n[openssl_init]\nproviders = provider_sect\n[provider_sect]\ndefault = default_sect\nlegacy = legacy_sect\n[default_sect]\nactivate = 1\n[legacy_sect]\nactivate = 1" > openssl.cnf
cat openssl.cnf
export OPENSSL_CONF=openssl.cnf
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: zip, openssl

- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress --no-interaction

- name: Run PHPUnit
run: vendor/bin/phpunit tests --coverage-text
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,3 +1,4 @@
/vendor
composer.lock
/Certificates.p12
/Certificates.p12
.phpunit.result.cache
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -81,6 +81,10 @@ left, you can select your iPhone. You will then be able to inspect any errors th

## Changelog

**Version 2.1.0 - April 2023**

* Add alternative method for extracting P12 contents to circumvent issues in recent versions of OpenSSL.

**Version 2.0.2 - October 2022**

* Switch to `ZipArchive::OVERWRITE` method of opening ZIP due to PHP 8 deprecation ([#120](https://github.com/includable/php-pkpass/pull/120)).
Expand Down
13 changes: 8 additions & 5 deletions composer.json
Expand Up @@ -20,15 +20,18 @@
"email": "thomas@includable.com"
}
],
"autoload": {
"psr-4": {
"PKPass\\": "src"
}
},
"require": {
"php": ">=5.6",
"php": ">=7.0",
"ext-zip": "*",
"ext-json": "*",
"ext-openssl": "*"
},
"autoload": {
"psr-4": {
"PKPass\\": "src"
}
"require-dev": {
"phpunit/phpunit": "^9.6"
}
}
77 changes: 65 additions & 12 deletions src/PKPass.php
Expand Up @@ -418,29 +418,82 @@ protected function convertPEMtoDER($signature)
}

/**
* Creates a signature and saves it.
* Read a PKCS12 certificate string and turn it into an array.
*
* @param string $manifest
* @return array
* @throws PKPassException
*/
protected function createSignature($manifest)
protected function readP12()
{
$manifest_path = tempnam($this->tempPath, 'pkpass');
$signature_path = tempnam($this->tempPath, 'pkpass');
file_put_contents($manifest_path, $manifest);

// Use the built-in reader first
if (!$pkcs12 = file_get_contents($this->certPath)) {
throw new PKPassException('Could not read the certificate.');
}

$certs = [];
if (!openssl_pkcs12_read($pkcs12, $certs, $this->certPass)) {
if (openssl_pkcs12_read($pkcs12, $certs, $this->certPass)) {
return $certs;
}

// That failed, let's check why
$error = '';
while ($text = openssl_error_string()) {
$error .= $text;
}

// General error
if (!strstr($error, 'digital envelope routines::unsupported')) {
throw new PKPassException(
'Invalid certificate file. Make sure you have a ' .
'P12 certificate that also contains a private key, and you ' .
'have specified the correct password!'
'have specified the correct password!' . PHP_EOL . PHP_EOL .
'OpenSSL error: ' . $error
);
}

// Try an alternative route using shell_exec
try {
$value = @shell_exec(
"openssl pkcs12 -in " . escapeshellarg($this->certPath) .
" -passin " . escapeshellarg("pass:" . $this->certPass) .
" -passout " . escapeshellarg("pass:" . $this->certPass) .
" -legacy"
);
if ($value) {
$cert = substr($value, strpos($value, '-----BEGIN CERTIFICATE-----'));
$cert = substr($cert, 0, strpos($cert, '-----END CERTIFICATE-----') + 25);
$key = substr($value, strpos($value, '-----BEGIN ENCRYPTED PRIVATE KEY-----'));
$key = substr($key, 0, strpos($key, '-----END ENCRYPTED PRIVATE KEY-----') + 35);
if (strlen($cert) > 0 && strlen($key) > 0) {
$certs['cert'] = $cert;
$certs['pkey'] = $key;
return $certs;
}
}
} catch (\Throwable $e) {
// no need to do anything
}

throw new PKPassException(
'Could not read certificate file. This might be related ' .
'to using an OpenSSL version that has deprecated some older ' .
'hashes. More info here: https://schof.link/2Et6z3m ' . PHP_EOL . PHP_EOL .
'OpenSSL error: ' . $error
);
}

/**
* Creates a signature and saves it.
*
* @param string $manifest
* @throws PKPassException
*/
protected function createSignature($manifest)
{
$manifest_path = tempnam($this->tempPath, 'pkpass');
$signature_path = tempnam($this->tempPath, 'pkpass');
file_put_contents($manifest_path, $manifest);

$certs = $this->readP12();
$certdata = openssl_x509_read($certs['cert']);
$privkey = openssl_pkey_get_private($certs['pkey'], $this->certPass);

Expand Down Expand Up @@ -506,11 +559,11 @@ protected function createZip($manifest, $signature)
$download_file = file_get_contents($url);
$zip->addFromString($name, $download_file);
}

foreach ($this->files_content as $name => $content) {
$zip->addFromString($name, $content);
}

$zip->close();

if (!file_exists($filename) || filesize($filename) < 1) {
Expand Down
65 changes: 65 additions & 0 deletions tests/BasicTest.php
@@ -0,0 +1,65 @@
<?php declare(strict_types=1);

use PHPUnit\Framework\TestCase;
use PKPass\PKPass;

final class BasicTest extends TestCase
{
private function validatePass($pass, $expected_files = [])
{
// basic string validation
$this->assertIsString($pass);
$this->assertGreaterThan(100, strlen($pass));
$this->assertStringContainsString('icon.png', $pass);
$this->assertStringContainsString('manifest.json', $pass);

// try to read the ZIP file
$temp_name = tempnam(sys_get_temp_dir(), 'pkpass');
file_put_contents($temp_name, $pass);
$zip = new ZipArchive();
$res = $zip->open($temp_name);
$this->assertTrue($res, 'Invalid ZIP file.');
$this->assertEquals(count($expected_files), $zip->numFiles);

// extract zip to temp dir
$temp_dir = $temp_name . '_dir';
mkdir($temp_dir);
$zip->extractTo($temp_dir);
$zip->close();
echo $temp_dir;
foreach ($expected_files as $file) {
$this->assertFileExists($temp_dir . DIRECTORY_SEPARATOR . $file);
}
}

public function testBasicGeneration()
{
$pass = new PKPass(__DIR__ . '/fixtures/example-certificate.p12', 'password');
$data = [
'description' => 'Demo pass',
'formatVersion' => 1,
'organizationName' => 'Flight Express',
'passTypeIdentifier' => 'pass.com.scholica.flights', // Change this!
'serialNumber' => '12345678',
'teamIdentifier' => 'KN44X8ZLNC', // Change this!
'barcode' => [
'format' => 'PKBarcodeFormatQR',
'message' => 'Flight-GateF12-ID6643679AH7B',
'messageEncoding' => 'iso-8859-1',
],
'backgroundColor' => 'rgb(32,110,247)',
'logoText' => 'Flight info',
'relevantDate' => date('Y-m-d\TH:i:sP')
];
$pass->setData($data);
$pass->addFile(__DIR__ . '/fixtures/icon.png');
$value = $pass->create();

$this->validatePass($value, [
'icon.png',
'manifest.json',
'pass.json',
'signature',
]);
}
}
Binary file added tests/fixtures/example-certificate.p12
Binary file not shown.
Binary file added tests/fixtures/icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 9b4db7c

Please sign in to comment.