Compare commits

..

No commits in common. "eeece17518c0776644eddb0494700c26b2a3c61b" and "cf83bc8c5d7b19fd4cc420ed05d658ecb8829a77" have entirely different histories.

17 changed files with 35 additions and 210 deletions

View file

@ -4,8 +4,7 @@
"type": "library", "type": "library",
"require": { "require": {
"ext-FFI": "*", "ext-FFI": "*",
"php": "^7.4", "php": "^7.4"
"ext-openssl": "*"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^0.11.19", "phpstan/phpstan": "^0.11.19",

View file

@ -58,8 +58,11 @@ typedef struct pkcs7_signedandenveloped_st {
ASN1_INTEGER *version; ASN1_INTEGER *version;
struct stack_st_X509_ALGOR *md_algs; struct stack_st_X509_ALGOR *md_algs;
struct stack_st_X509 *cert; struct stack_st_X509 *cert;
struct stack_st_X509_CRL *crl; struct stack_st_X509_CRL *crl;
struct stack_st_PKCS7_SIGNER_INFO *signer_info; struct stack_st_PKCS7_SIGNER_INFO *signer_info;
PKCS7_ENC_CONTENT *enc_data; PKCS7_ENC_CONTENT *enc_data;

View file

@ -234,56 +234,8 @@ struct stack_st_X509_CRL {
_STACK stack; _STACK stack;
}; };
typedef struct x509_lookup_st X509_LOOKUP;
typedef struct x509_object_st
{
int type;
union {
char *ptr;
X509 *x509;
X509_CRL *crl;
EVP_PKEY *pkey;
} data;
} X509_OBJECT;
struct stack_st_X509_LOOKUP { _STACK stack; };
struct stack_st_X509_OBJECT { _STACK stack; };
typedef struct x509_lookup_method_st {
const char *name;
int (*new_item)(X509_LOOKUP *ctx);
void (*free)(X509_LOOKUP *ctx);
int (*init)(X509_LOOKUP *ctx);
int (*shutdown)(X509_LOOKUP *ctx);
int (*ctrl)(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
char **ret);
int (*get_by_subject)(X509_LOOKUP *ctx, int type, X509_NAME *name,
X509_OBJECT *ret);
int (*get_by_issuer_serial)(X509_LOOKUP *ctx, int type, X509_NAME *name,
ASN1_INTEGER *serial, X509_OBJECT *ret);
int (*get_by_fingerprint)(X509_LOOKUP *ctx, int type,
const unsigned char *bytes, int len,
X509_OBJECT *ret);
int (*get_by_alias)(X509_LOOKUP *ctx, int type, const char *str, int len,
X509_OBJECT *ret);
} X509_LOOKUP_METHOD;
X509 *X509_new(void); X509 *X509_new(void);
X509 *X509_dup(X509 *x509);
void X509_free(X509 *a); void X509_free(X509 *a);
X509_STORE *X509_STORE_new(void); X509_STORE *X509_STORE_new(void);
void X509_STORE_free(X509_STORE *v); void X509_STORE_free(X509_STORE *v);
X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m);
X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void);
X509_LOOKUP_METHOD *X509_LOOKUP_file(void);
X509_LOOKUP_METHOD *X509_LOOKUP_mem(void);
int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type);
int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
long argl, char **ret);

View file

@ -5,7 +5,6 @@ namespace Cijber;
use Cijber\OpenSSL\FFIWrapper; use Cijber\OpenSSL\FFIWrapper;
use Cijber\OpenSSL\Instance; use Cijber\OpenSSL\Instance;
use Cijber\OpenSSL\X509Store;
use FFI; use FFI;
/** /**
@ -57,8 +56,7 @@ class OpenSSL
$ffi = static::getFFI(); $ffi = static::getFFI();
$errs = []; $errs = [];
while (0 !== ($code = $ffi->ERR_get_error())) { while (0 !== ($code = $ffi->ERR_get_error())) {
$ptr = $ffi->ERR_error_string($code, null); $errs[] = FFI::string($ffi->ERR_error_string($code, null));
$errs[] = FFI::string($ptr);
} }
return $errs; return $errs;
@ -68,15 +66,4 @@ class OpenSSL
{ {
return FFI::cast("long long", $data)->cdata; return FFI::cast("long long", $data)->cdata;
} }
public static $caStore = null;
public static function CAStore()
{
if (static::$caStore === null) {
static::$caStore = X509Store::default();
}
return static::$caStore;
}
} }

View file

@ -94,9 +94,4 @@ class CBackedObject
{ {
$this->managed = true; $this->managed = true;
} }
public function getCData(): CData
{
return $this->cObj;
}
} }

View file

@ -3,14 +3,13 @@
namespace Cijber\OpenSSL\C; namespace Cijber\OpenSSL\C;
use Cijber\OpenSSL;
use Cijber\OpenSSL\FFIWrapper; use Cijber\OpenSSL\FFIWrapper;
use FFI; use FFI;
use FFI\CData; use FFI\CData;
class CBackedObjectWithOwner extends CBackedObject class CBackedObjectWithOwner extends CBackedObject
{ {
private static array $known = []; static private array $known = [];
private int $address = -1; private int $address = -1;
protected FFI $ffi; protected FFI $ffi;
@ -44,7 +43,7 @@ class CBackedObjectWithOwner extends CBackedObject
* Cast first, so it acts like a pointer * Cast first, so it acts like a pointer
*/ */
$casted = $ffi->cast(static::TYPE, $cData); $casted = $ffi->cast(static::TYPE, $cData);
$address = OpenSSL::addressOf($casted); $address = FFI::cast("long long", $casted)->cdata;
if (array_key_exists($address, static::$known)) { if (array_key_exists($address, static::$known)) {
return static::$known[$address]; return static::$known[$address];
} }

View file

@ -32,7 +32,7 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
return $this->ffi->OBJ_obj2nid($this->cObj->type); return $this->ffi->OBJ_obj2nid($this->cObj->type);
} }
public function asSigned(): PKCS7\Signed public function toSigned(): PKCS7\Signed
{ {
$this->ensureNotFreed(); $this->ensureNotFreed();
@ -40,7 +40,7 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
throw new RuntimeException("This PKCS7 isn't of type signed"); throw new RuntimeException("This PKCS7 isn't of type signed");
} }
return PKCS7\Signed::fromPKCS7($this, $this->ffi, $this->cObj->d->sign, $this->cObj); return new PKCS7\Signed($this);
} }
/** /**

View file

@ -4,28 +4,10 @@
namespace Cijber\OpenSSL\PKCS7; namespace Cijber\OpenSSL\PKCS7;
use Cijber\OpenSSL\PKCS7; use Cijber\OpenSSL\PKCS7;
use FFI;
use FFI\CData;
trait Helpers trait Helpers
{ {
protected PKCS7 $pkcs7; 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 public function toDER(): string
{ {
@ -39,4 +21,9 @@ trait Helpers
{ {
return $this->pkcs7; return $this->pkcs7;
} }
public function __construct(PKCS7 $pkcs7)
{
$this->pkcs7 = $pkcs7;
}
} }

View file

@ -3,31 +3,11 @@
namespace Cijber\OpenSSL\PKCS7; namespace Cijber\OpenSSL\PKCS7;
use Cijber\OpenSSL;
use Cijber\OpenSSL\BIO;
use Cijber\OpenSSL\Stack\X509Stack;
use Cijber\OpenSSL\X509Store;
class Signed class Signed
{ {
use Helpers; 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, int $flags, ?X509Store $x509Store = null)
{
if ($x509Store === null) {
$x509Store = OpenSSL::CAStore();
}
$buffer = BIO::buffer($plain);
$x = $this->ffi->PKCS7_verify($this->parent, null, $x509Store->getCData(), $buffer->getCData(), null, $flags);
return $x === 1;
} }
} }

View file

@ -1,9 +0,0 @@
<?php
namespace Cijber\OpenSSL\PKCS7;
class SignerInfo
{
const TYPE = "PKCS7_SIGNER_INFO*";
}

View file

@ -1,16 +0,0 @@
<?php
namespace Cijber\OpenSSL\PKCS7\Stack;
use Cijber\OpenSSL\PKCS7\SignerInfo;
use Cijber\OpenSSL\Stack;
use FFI\CData;
class SignerInfoStack extends Stack
{
protected function spawn(CData $cData): SignerInfo
{
// TODO: Implement spawn() method.
}
}

View file

@ -8,7 +8,6 @@ use Cijber\OpenSSL;
use Cijber\OpenSSL\C\CBackedObject; use Cijber\OpenSSL\C\CBackedObject;
use Cijber\OpenSSL\C\CBackedObjectWithOwner; use Cijber\OpenSSL\C\CBackedObjectWithOwner;
use Countable; use Countable;
use FFI;
use FFI\CData; use FFI\CData;
use InvalidArgumentException; use InvalidArgumentException;
use IteratorAggregate; use IteratorAggregate;
@ -22,18 +21,9 @@ use Traversable;
abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayAccess, Traversable, IteratorAggregate abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayAccess, Traversable, IteratorAggregate
{ {
const CLASSNAME = CBackedObjectWithOwner::class; const CLASSNAME = CBackedObjectWithOwner::class;
const TYPE = "struct stack_st*";
abstract protected function spawn(CData $cData); abstract protected function spawn(CData $cData);
protected $owner;
protected function __construct(FFI $ffi, CData $cObj, $owner = null)
{
parent::__construct($ffi, $ffi->cast(self::TYPE, $cObj));
$this->owner = $owner;
}
public static function new() public static function new()
{ {
$ffi = OpenSSL::getFFI(); $ffi = OpenSSL::getFFI();
@ -110,6 +100,8 @@ abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayA
throw new RuntimeException("Failed to insert element"); throw new RuntimeException("Failed to insert element");
} }
$object->pushRefCount();
return $idx; return $idx;
} }
@ -196,9 +188,9 @@ abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayA
public function __clone() public function __clone()
{ {
$this->cObj = $this->ffi->sk_dup($this->cObj); $cObj = $this->ffi->sk_dup($this->cObj);
if ($this->cObj === null) { if ($cObj === null) {
throw new RuntimeException("Failed to clone stack"); throw new RuntimeException("Failed to clone stack");
} }
@ -206,5 +198,7 @@ abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayA
foreach ($this as $obj) { foreach ($this as $obj) {
$obj->pushRefCount(); $obj->pushRefCount();
} }
return new static($this->ffi, $cObj);
} }
} }

View file

@ -12,18 +12,13 @@ class X509Stack extends Stack
{ {
const CLASSNAME = X509::class; const CLASSNAME = X509::class;
public static function from(FFI $ffi, CData $cObj, $owner = null): X509Stack public static function from(FFI $ffi, CData $cObj): X509Stack
{ {
return new X509Stack($ffi, $cObj, $owner = null); return new X509Stack($ffi, $cObj);
} }
public function spawn(CData $cData): X509 public function spawn(CData $cData): X509
{ {
$obj = $this->ffi->cast(X509::TYPE, $cData); return X509::cast($this->ffi, $cData);
$clone = $this->ffi->X509_dup($obj);
if ($clone === null) {
return X509::new();
}
return X509::cast($this->ffi, $clone);
} }
} }

View file

@ -5,12 +5,9 @@ namespace Cijber\OpenSSL;
use Cijber\OpenSSL; use Cijber\OpenSSL;
use Cijber\OpenSSL\C\CBackedObjectWithOwner; use Cijber\OpenSSL\C\CBackedObjectWithOwner;
use FFI;
class X509 extends CBackedObjectWithOwner class X509 extends CBackedObjectWithOwner
{ {
const FILETYPE_DEFAULT = 3;
const TYPE = "X509*"; const TYPE = "X509*";
public static function new(): X509 public static function new(): X509
@ -24,15 +21,4 @@ class X509 extends CBackedObjectWithOwner
{ {
$this->ffi->X509_free($this->cObj); $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;
}
} }

View file

@ -15,24 +15,6 @@ class X509Store extends CBackedObjectWithOwner
return new X509Store($ffi, $x509); return new X509Store($ffi, $x509);
} }
public static function default(): X509Store
{
$store = X509Store::new();
$obj = $store->cObj;
$ffi = $store->ffi;
$lookup = $ffi->X509_STORE_add_lookup($obj, $ffi->X509_LOOKUP_file());
if ($lookup === null || !$ffi->X509_LOOKUP_ctrl($lookup, 1, null, X509::FILETYPE_DEFAULT, null)) {
throw new \RuntimeException("Couldn't load default CA files");
}
$lookup = $ffi->X509_STORE_add_lookup($obj, $ffi->X509_LOOKUP_hash_dir());
if ($lookup === null || !$ffi->X509_LOOKUP_ctrl($lookup, 2, null, X509::FILETYPE_DEFAULT, null)) {
throw new \RuntimeException("Couldn't load default CA files");
}
return $store;
}
public function freeObject() public function freeObject()
{ {
$this->ffi->X509_STORE_free($this->cObj); $this->ffi->X509_STORE_free($this->cObj);

View file

@ -3,7 +3,6 @@
namespace Cijber\OpenSSL\Tests; namespace Cijber\OpenSSL\Tests;
use Cijber\OpenSSL\PKCS7; use Cijber\OpenSSL\PKCS7;
use Cijber\OpenSSL\X509;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use RuntimeException; use RuntimeException;
@ -23,22 +22,6 @@ class PKCS7Test extends TestCase
$newDer = $pkcs7->toDER(); $newDer = $pkcs7->toDER();
$this->assertEquals(PKCS7::NID_SIGNED, $pkcs7->getType()); $this->assertEquals(PKCS7::NID_SIGNED, $pkcs7->getType());
$this->assertEquals($der, $newDer); $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, PKCS7_NOVERIFY);
$this->assertTrue($result);
} }
public function testLoadingGarbageDER() public function testLoadingGarbageDER()

View file

@ -24,7 +24,7 @@ class StackTest extends TestCase
$stack->push($x509); $stack->push($x509);
$this->assertCount(1, $stack); $this->assertCount(1, $stack);
$x509Item = $stack->get(0); $x509Item = $stack->get(0);
$this->assertNotSame($x509, $x509Item); $this->assertSame($x509, $x509Item);
} }
function testSet() function testSet()
@ -47,10 +47,14 @@ class StackTest extends TestCase
$c = X509::new(); $c = X509::new();
$stack->push($a); $stack->push($a);
$stack->push($c); $stack->push($c);
$this->assertEquals(1, $a->getRefCount());
$b = $stack->delete(0); $b = $stack->delete(0);
$this->assertSame($a, $b);
$this->assertCount(1, $stack); $this->assertCount(1, $stack);
$this->assertEquals(0, $a->getRefCount());
unset($stack[0]); unset($stack[0]);
$this->assertCount(0, $stack); $this->assertCount(0, $stack);
$this->assertEquals(0, $c->getRefCount());
$this->expectException(\RuntimeException::class); $this->expectException(\RuntimeException::class);
$stack->delete(1); $stack->delete(1);
@ -63,13 +67,17 @@ class StackTest extends TestCase
$stack->push($x509); $stack->push($x509);
$this->assertCount(1, $stack); $this->assertCount(1, $stack);
$x509Item = $stack->get(0); $x509Item = $stack->get(0);
$this->assertSame($x509, $x509Item);
$this->assertEquals(1, $x509->getRefCount());
unset($x509Item, $x509); unset($x509Item, $x509);
/** @var X509 $x509 */ /** @var X509 $x509 */
$x509 = $stack->get(0); $x509 = $stack->get(0);
$this->assertFalse($x509->free());
$stack->freeAll(); $stack->freeAll();
$this->assertEquals(0, $x509->getRefCount());
$this->assertTrue($stack->isFreed()); $this->assertTrue($stack->isFreed());
$this->assertFalse($x509->isFreed()); $this->assertTrue($x509->isFreed());
} }
function testAddressingCorrect() function testAddressingCorrect()