diff --git a/resources/pkcs7.h b/resources/pkcs7.h index ce25b8d..e761b54 100644 --- a/resources/pkcs7.h +++ b/resources/pkcs7.h @@ -58,11 +58,8 @@ typedef struct pkcs7_signedandenveloped_st { ASN1_INTEGER *version; struct stack_st_X509_ALGOR *md_algs; - struct stack_st_X509 *cert; - struct stack_st_X509_CRL *crl; - struct stack_st_PKCS7_SIGNER_INFO *signer_info; PKCS7_ENC_CONTENT *enc_data; diff --git a/resources/x509.h b/resources/x509.h index 4ae2bef..8c596d1 100644 --- a/resources/x509.h +++ b/resources/x509.h @@ -235,7 +235,13 @@ struct stack_st_X509_CRL { }; X509 *X509_new(void); +X509 *X509_dup(X509 *x509); void X509_free(X509 *a); X509_STORE *X509_STORE_new(void); -void X509_STORE_free(X509_STORE *v); \ No newline at end of file +void X509_STORE_free(X509_STORE *v); + +X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void); + X509_LOOKUP_METHOD *X509_LOOKUP_file(void); + + int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type); \ No newline at end of file diff --git a/src/OpenSSL.php b/src/OpenSSL.php index 0b85c09..79b0a78 100644 --- a/src/OpenSSL.php +++ b/src/OpenSSL.php @@ -66,4 +66,11 @@ class OpenSSL { return FFI::cast("long long", $data)->cdata; } + + static $caStore = null; + + public static function CAStore() + { + + } } diff --git a/src/OpenSSL/C/CBackedObject.php b/src/OpenSSL/C/CBackedObject.php index 923c870..6fb2215 100644 --- a/src/OpenSSL/C/CBackedObject.php +++ b/src/OpenSSL/C/CBackedObject.php @@ -94,4 +94,9 @@ class CBackedObject { $this->managed = true; } + + public function getCData(): CData + { + return $this->cObj; + } } diff --git a/src/OpenSSL/C/CBackedObjectWithOwner.php b/src/OpenSSL/C/CBackedObjectWithOwner.php index abba79d..c016d66 100644 --- a/src/OpenSSL/C/CBackedObjectWithOwner.php +++ b/src/OpenSSL/C/CBackedObjectWithOwner.php @@ -3,6 +3,7 @@ namespace Cijber\OpenSSL\C; +use Cijber\OpenSSL; use Cijber\OpenSSL\FFIWrapper; use FFI; use FFI\CData; @@ -43,7 +44,7 @@ class CBackedObjectWithOwner extends CBackedObject * Cast first, so it acts like a pointer */ $casted = $ffi->cast(static::TYPE, $cData); - $address = FFI::cast("long long", $casted)->cdata; + $address = OpenSSL::addressOf($casted); if (array_key_exists($address, static::$known)) { return static::$known[$address]; } diff --git a/src/OpenSSL/PKCS7.php b/src/OpenSSL/PKCS7.php index 6410537..9a65cac 100644 --- a/src/OpenSSL/PKCS7.php +++ b/src/OpenSSL/PKCS7.php @@ -32,7 +32,7 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner return $this->ffi->OBJ_obj2nid($this->cObj->type); } - public function toSigned(): PKCS7\Signed + public function asSigned(): PKCS7\Signed { $this->ensureNotFreed(); @@ -40,7 +40,7 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner throw new RuntimeException("This PKCS7 isn't of type signed"); } - return new PKCS7\Signed($this); + return PKCS7\Signed::fromPKCS7($this, $this->ffi, $this->cObj->d->sign, $this->cObj); } /** diff --git a/src/OpenSSL/PKCS7/Helpers.php b/src/OpenSSL/PKCS7/Helpers.php index 2269296..fb09076 100644 --- a/src/OpenSSL/PKCS7/Helpers.php +++ b/src/OpenSSL/PKCS7/Helpers.php @@ -4,10 +4,28 @@ namespace Cijber\OpenSSL\PKCS7; use Cijber\OpenSSL\PKCS7; +use FFI; +use FFI\CData; trait Helpers { protected PKCS7 $pkcs7; + protected CData $data; + protected CData $parent; + protected FFI $ffi; + + public function __construct(PKCS7 $pkcs7, FFI $ffi, CData $data, CData $parent) + { + $this->pkcs7 = $pkcs7; + $this->ffi = $ffi; + $this->data = $data; + $this->parent = $parent; + } + + public static function fromPKCS7(PKCS7 $param, FFI $ffi, CData $data, CData $parent) + { + return new static($param, $ffi, $data, $parent); + } public function toDER(): string { @@ -21,9 +39,4 @@ trait Helpers { return $this->pkcs7; } - - public function __construct(PKCS7 $pkcs7) - { - $this->pkcs7 = $pkcs7; - } } diff --git a/src/OpenSSL/PKCS7/Signed.php b/src/OpenSSL/PKCS7/Signed.php index b483ddc..432476d 100644 --- a/src/OpenSSL/PKCS7/Signed.php +++ b/src/OpenSSL/PKCS7/Signed.php @@ -3,11 +3,36 @@ namespace Cijber\OpenSSL\PKCS7; +use Cijber\OpenSSL; +use Cijber\OpenSSL\BIO; +use Cijber\OpenSSL\PKCS7; +use Cijber\OpenSSL\Stack\X509Stack; +use Cijber\OpenSSL\X509Store; +use FFI; +use FFI\CData; + class Signed { use Helpers; - public function getSigners() + + + public function getCerts(): X509Stack + { + + $stack = X509Stack::from($this->ffi, $this->data->cert, $this); + $stack->unmanaged(); + return $stack; + } + + public function verify(string $plain, ?X509Store $x509Store = null) { + if ($x509Store === null) { + $x509Store = OpenSSL::CAStore(); + } + + $buffer = BIO::buffer($plain); + $x = $this->ffi->PKCS7_verify($this->parent, null, null, $buffer->getCData(), null, 0); + return $x === 1; } } diff --git a/src/OpenSSL/PKCS7/SignerInfo.php b/src/OpenSSL/PKCS7/SignerInfo.php new file mode 100644 index 0000000..6d83538 --- /dev/null +++ b/src/OpenSSL/PKCS7/SignerInfo.php @@ -0,0 +1,9 @@ +cast(self::TYPE, $cObj)); + $this->owner = $owner; + } + public static function new() { $ffi = OpenSSL::getFFI(); @@ -100,8 +110,6 @@ abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayA throw new RuntimeException("Failed to insert element"); } - $object->pushRefCount(); - return $idx; } diff --git a/src/OpenSSL/Stack/X509Stack.php b/src/OpenSSL/Stack/X509Stack.php index 827b922..1d51298 100644 --- a/src/OpenSSL/Stack/X509Stack.php +++ b/src/OpenSSL/Stack/X509Stack.php @@ -12,13 +12,18 @@ class X509Stack extends Stack { const CLASSNAME = X509::class; - public static function from(FFI $ffi, CData $cObj): X509Stack + public static function from(FFI $ffi, CData $cObj, $owner = null): X509Stack { - return new X509Stack($ffi, $cObj); + return new X509Stack($ffi, $cObj, $owner = null); } public function spawn(CData $cData): X509 { - return X509::cast($this->ffi, $cData); + $obj = $this->ffi->cast(X509::TYPE, $cData); + $clone = $this->ffi->X509_dup($obj); + if ($clone === null) { + return X509::new(); + } + return X509::cast($this->ffi, $clone); } } diff --git a/src/OpenSSL/X509.php b/src/OpenSSL/X509.php index 0d5b4d3..29cfbf9 100644 --- a/src/OpenSSL/X509.php +++ b/src/OpenSSL/X509.php @@ -5,6 +5,7 @@ namespace Cijber\OpenSSL; use Cijber\OpenSSL; use Cijber\OpenSSL\C\CBackedObjectWithOwner; +use FFI; class X509 extends CBackedObjectWithOwner { @@ -21,4 +22,15 @@ class X509 extends CBackedObjectWithOwner { $this->ffi->X509_free($this->cObj); } + + public function getName() + { + return FFI::string($this->cObj->name); + } + + public function getSHA1Hash() + { + $hash = FFI::string($this->cObj->sha1_hash, 20); + return $hash; + } } diff --git a/tests/PKCS7Test.php b/tests/PKCS7Test.php index 66f9434..f80237c 100644 --- a/tests/PKCS7Test.php +++ b/tests/PKCS7Test.php @@ -3,6 +3,7 @@ namespace Cijber\OpenSSL\Tests; use Cijber\OpenSSL\PKCS7; +use Cijber\OpenSSL\X509; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -22,6 +23,21 @@ class PKCS7Test extends TestCase $newDer = $pkcs7->toDER(); $this->assertEquals(PKCS7::NID_SIGNED, $pkcs7->getType()); $this->assertEquals($der, $newDer); + $signed = $pkcs7->asSigned(); + $certs = $signed->getCerts(); + $this->assertCount(1, $certs); + /** @var X509 $x509 */ + $x509 = $certs[0]; + $x = $x509->getName(); + $this->assertEquals("/C=US/ST=New York/L=New York/OU=FDroid Repo/O=Guardian Project/CN=guardianproject.info/emailAddress=root@guardianproject.info", $x); + } + + public function testVerify() { + $der = file_get_contents(__DIR__ . "/data/pkcs7/1.RSA"); + $plain = file_get_contents(__DIR__ . "/data/pkcs7/1.SF"); + $pkcs7 = PKCS7::loadFromDER($der); + $signed = $pkcs7->asSigned(); + $result = $signed->verify($plain); } public function testLoadingGarbageDER() diff --git a/tests/StackTest.php b/tests/StackTest.php index a6bc604..ba45dd5 100644 --- a/tests/StackTest.php +++ b/tests/StackTest.php @@ -24,7 +24,7 @@ class StackTest extends TestCase $stack->push($x509); $this->assertCount(1, $stack); $x509Item = $stack->get(0); - $this->assertSame($x509, $x509Item); + $this->assertNotSame($x509, $x509Item); } function testSet() @@ -47,14 +47,10 @@ class StackTest extends TestCase $c = X509::new(); $stack->push($a); $stack->push($c); - $this->assertEquals(1, $a->getRefCount()); $b = $stack->delete(0); - $this->assertSame($a, $b); $this->assertCount(1, $stack); - $this->assertEquals(0, $a->getRefCount()); unset($stack[0]); $this->assertCount(0, $stack); - $this->assertEquals(0, $c->getRefCount()); $this->expectException(\RuntimeException::class); $stack->delete(1); @@ -67,17 +63,13 @@ class StackTest extends TestCase $stack->push($x509); $this->assertCount(1, $stack); $x509Item = $stack->get(0); - $this->assertSame($x509, $x509Item); - $this->assertEquals(1, $x509->getRefCount()); unset($x509Item, $x509); /** @var X509 $x509 */ $x509 = $stack->get(0); - $this->assertFalse($x509->free()); $stack->freeAll(); - $this->assertEquals(0, $x509->getRefCount()); $this->assertTrue($stack->isFreed()); - $this->assertTrue($x509->isFreed()); + $this->assertFalse($x509->isFreed()); } function testAddressingCorrect()