This commit is contained in:
parent
5c5498e65b
commit
cf83bc8c5d
13 changed files with 521 additions and 12 deletions
|
@ -15,3 +15,30 @@ struct buf_mem_st {
|
|||
|
||||
typedef struct buf_mem_st BUF_MEM;
|
||||
typedef long int time_t;
|
||||
|
||||
// Stack functions
|
||||
int sk_num(const _STACK *);
|
||||
|
||||
void *sk_value(const _STACK *, int);
|
||||
void *sk_set(_STACK *, int, void *);
|
||||
|
||||
_STACK *sk_new(int (*cmp)(const void *, const void *));
|
||||
_STACK *sk_new_null(void);
|
||||
void sk_free(_STACK *);
|
||||
void sk_pop_free(_STACK *st, void (*func)(void *));
|
||||
int sk_insert(_STACK *sk, void *data, int where);
|
||||
void *sk_delete(_STACK *st, int loc);
|
||||
void *sk_delete_ptr(_STACK *st, void *p);
|
||||
int sk_find(_STACK *st, void *data);
|
||||
int sk_find_ex(_STACK *st, void *data);
|
||||
int sk_push(_STACK *st, void *data);
|
||||
int sk_unshift(_STACK *st, void *data);
|
||||
void *sk_shift(_STACK *st);
|
||||
void *sk_pop(_STACK *st);
|
||||
void sk_zero(_STACK *st);
|
||||
int (*sk_set_cmp_func(_STACK *sk,
|
||||
int (*c)(const void *, const void *)))(const void *,
|
||||
const void *);
|
||||
_STACK *sk_dup(_STACK *st);
|
||||
void sk_sort(_STACK *st);
|
||||
int sk_is_sorted(const _STACK *st);
|
||||
|
|
|
@ -6,4 +6,7 @@ void OPENSSL_add_all_algorithms_conf(void);
|
|||
void OPENSSL_add_all_algorithms_noconf(void);
|
||||
|
||||
void ERR_load_crypto_strings(void);
|
||||
void ERR_free_strings(void);
|
||||
void ERR_free_strings(void);
|
||||
|
||||
unsigned long ERR_get_error(void);
|
||||
char *ERR_error_string(unsigned long e, char *buf);
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
namespace Cijber;
|
||||
|
||||
use Cijber\OpenSSL\FFIWrapper;
|
||||
use Cijber\OpenSSL\Instance;
|
||||
use FFI;
|
||||
|
||||
|
@ -49,4 +50,20 @@ class OpenSSL
|
|||
{
|
||||
return static::getStdLib()->malloc($size);
|
||||
}
|
||||
|
||||
public static function consumeErrors(): array
|
||||
{
|
||||
$ffi = static::getFFI();
|
||||
$errs = [];
|
||||
while (0 !== ($code = $ffi->ERR_get_error())) {
|
||||
$errs[] = FFI::string($ffi->ERR_error_string($code, null));
|
||||
}
|
||||
|
||||
return $errs;
|
||||
}
|
||||
|
||||
public static function addressOf(FFI\CData $data): int
|
||||
{
|
||||
return FFI::cast("long long", $data)->cdata;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,7 +205,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
public static function new(): BIO
|
||||
{
|
||||
$ffi = OpenSSL::getFFI();
|
||||
$bio = $ffi->BIO_new($ffi->BIO_s_mem());
|
||||
$mem = $ffi->BIO_s_mem();
|
||||
$bio = $ffi->BIO_new($mem);
|
||||
return new BIO($ffi, $bio);
|
||||
}
|
||||
|
||||
|
@ -253,6 +254,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function write(string $data): int
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
$len = $this->ffi->BIO_write($this->cObj, $data, strlen($data));
|
||||
if ($len === -2) {
|
||||
throw new RuntimeException("Can't wrote to this BIO");
|
||||
|
@ -276,6 +279,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function getType(): int
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
return $this->ffi->BIO_method_type($this->cObj);
|
||||
}
|
||||
|
||||
|
@ -287,6 +292,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function read(int $chunkSize = 4096): string
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
$data = OpenSSL\C\Memory::new($chunkSize);
|
||||
$len = $this->ffi->BIO_read($this->cObj, $data->get(), $chunkSize);
|
||||
if ($len === -2) {
|
||||
|
@ -311,6 +318,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function tell()
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
if (($this->getType() & self::TYPE_FILE) !== self::TYPE_FILE) {
|
||||
throw new RuntimeException("Can't tell on non-file BIO");
|
||||
}
|
||||
|
@ -329,6 +338,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
$res = (int)$this->ctrl(self::CTRL_RESET, 0, null);
|
||||
|
||||
if (($this->getType() & self::TYPE_FILE) === self::TYPE_FILE && $res === 0) {
|
||||
|
@ -349,6 +360,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function seek(int $offset)
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
if (($this->getType() & self::TYPE_FILE) !== self::TYPE_FILE) {
|
||||
throw new RuntimeException("Can't seek in non-file BIO");
|
||||
}
|
||||
|
@ -367,6 +380,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function eof(): bool
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
return (int)$this->ctrl(self::CTRL_EOF, 0, null) === 1;
|
||||
}
|
||||
|
||||
|
@ -380,6 +395,8 @@ class BIO extends CBackedObjectWithOwner
|
|||
*/
|
||||
public function ctrl(int $prop, int $larg = 0, $parg = null)
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
return $this->ffi->BIO_ctrl($this->cObj, $prop, $larg, $parg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,15 +11,19 @@ class CBackedObject
|
|||
const TYPE = "void*";
|
||||
|
||||
protected CData $cObj;
|
||||
protected bool $managed = true;
|
||||
protected int $refCount = 0;
|
||||
protected bool $freed = false;
|
||||
|
||||
/**
|
||||
* CBackedObject constructor.
|
||||
* @param CData $cObj
|
||||
* @param bool $managed
|
||||
*/
|
||||
protected function __construct(CData $cObj)
|
||||
protected function __construct(CData $cObj, bool $managed = true)
|
||||
{
|
||||
$this->cObj = $cObj;
|
||||
$this->managed = $managed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,17 +34,30 @@ class CBackedObject
|
|||
$this->freed = true;
|
||||
}
|
||||
|
||||
public function isFreed(): bool
|
||||
{
|
||||
return $this->freed;
|
||||
}
|
||||
|
||||
public function ensureNotFreed()
|
||||
{
|
||||
if ($this->isFreed()) {
|
||||
throw new \RuntimeException("object " . get_class($this) . " already freed, can't be used");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free backing C object, object is useless after this operation
|
||||
*/
|
||||
final public function free()
|
||||
final public function free(): bool
|
||||
{
|
||||
if ($this->freed) {
|
||||
return;
|
||||
if ($this->freed || (!$this->managed || $this->refCount > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->freeObject();
|
||||
$this->freed();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function freeObject()
|
||||
|
@ -52,4 +69,29 @@ class CBackedObject
|
|||
{
|
||||
$this->free();
|
||||
}
|
||||
|
||||
public function unmanaged()
|
||||
{
|
||||
$this->managed = false;
|
||||
}
|
||||
|
||||
public function pushRefCount()
|
||||
{
|
||||
$this->refCount++;
|
||||
}
|
||||
|
||||
public function decreaseRefCount()
|
||||
{
|
||||
$this->refCount--;
|
||||
}
|
||||
|
||||
public function getRefCount()
|
||||
{
|
||||
return $this->refCount;
|
||||
}
|
||||
|
||||
public function managed()
|
||||
{
|
||||
$this->managed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,17 +3,30 @@
|
|||
|
||||
namespace Cijber\OpenSSL\C;
|
||||
|
||||
use Cijber\OpenSSL\FFIWrapper;
|
||||
use FFI;
|
||||
use FFI\CData;
|
||||
|
||||
class CBackedObjectWithOwner extends CBackedObject
|
||||
{
|
||||
static private array $known = [];
|
||||
private int $address = -1;
|
||||
|
||||
protected FFI $ffi;
|
||||
|
||||
protected function __construct(FFI $ffi, CData $cObj)
|
||||
{
|
||||
parent::__construct($cObj);
|
||||
$x = FFI::cast("long long", $cObj);
|
||||
$this->address = $x->cdata;
|
||||
$this->ffi = $ffi;
|
||||
static::$known[$this->address] = $this;
|
||||
}
|
||||
|
||||
public function freed()
|
||||
{
|
||||
unset(static::$known[$this->address]);
|
||||
parent::freed();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +39,15 @@ class CBackedObjectWithOwner extends CBackedObject
|
|||
*/
|
||||
public static function cast(FFI $ffi, CData $cData)
|
||||
{
|
||||
return new static($ffi, FFI::cast(static::TYPE, $cData));
|
||||
/**
|
||||
* Cast first, so it acts like a pointer
|
||||
*/
|
||||
$casted = $ffi->cast(static::TYPE, $cData);
|
||||
$address = FFI::cast("long long", $casted)->cdata;
|
||||
if (array_key_exists($address, static::$known)) {
|
||||
return static::$known[$address];
|
||||
}
|
||||
|
||||
return new static($ffi, $casted);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,15 @@ class Memory extends CBackedObject
|
|||
|
||||
public function get(): CData
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
return $this->cObj;
|
||||
}
|
||||
|
||||
public function string(int $length, int $offset = 0)
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
return substr(FFI::string($this->cObj, $length + $offset), $offset);
|
||||
}
|
||||
|
||||
|
@ -42,6 +46,8 @@ class Memory extends CBackedObject
|
|||
|
||||
public function pointer(): CData
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
return FFI::addr($this->cObj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Cijber\OpenSSL;
|
|||
use Cijber\OpenSSL;
|
||||
use Cijber\OpenSSL\C\Memory;
|
||||
use FFI;
|
||||
use RuntimeException;
|
||||
|
||||
class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
|
||||
{
|
||||
|
@ -33,8 +34,10 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
|
|||
|
||||
public function toSigned(): PKCS7\Signed
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
if ($this->getType() !== self::NID_SIGNED) {
|
||||
throw new \RuntimeException("This PKCS7 isn't of type signed");
|
||||
throw new RuntimeException("This PKCS7 isn't of type signed");
|
||||
}
|
||||
|
||||
return new PKCS7\Signed($this);
|
||||
|
@ -47,6 +50,8 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
|
|||
*/
|
||||
public function toDER(): string
|
||||
{
|
||||
$this->ensureNotFreed();
|
||||
|
||||
// Create NULL uint8_t*
|
||||
$buf = $this->ffi->new("uint8_t*");
|
||||
// Get pointer from it (ptr being now uint8_t**)
|
||||
|
@ -54,7 +59,7 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
|
|||
// Give NULL pointer to OpenSSL and let it fill it up
|
||||
$len = $this->ffi->i2d_PKCS7($this->cObj, $ptr);
|
||||
if ($len < 0) {
|
||||
throw new \RuntimeException("Failed to create DER from PKCS7 object");
|
||||
throw new RuntimeException("Failed to create DER from PKCS7 object");
|
||||
}
|
||||
|
||||
// Read string from pointer
|
||||
|
@ -99,10 +104,10 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
|
|||
$res = $ffi->d2i_PKCS7(null, $mem->pointer(), $derLen);
|
||||
|
||||
if ($res === null) {
|
||||
throw new \RuntimeException("Failed loading DER");
|
||||
throw new RuntimeException("Failed loading DER");
|
||||
}
|
||||
|
||||
$mem->freed();
|
||||
return new static($ffi, $res);
|
||||
return static::cast($ffi, $res);
|
||||
}
|
||||
}
|
||||
|
|
204
src/OpenSSL/Stack.php
Normal file
204
src/OpenSSL/Stack.php
Normal file
|
@ -0,0 +1,204 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Cijber\OpenSSL;
|
||||
|
||||
use ArrayAccess;
|
||||
use Cijber\OpenSSL;
|
||||
use Cijber\OpenSSL\C\CBackedObject;
|
||||
use Cijber\OpenSSL\C\CBackedObjectWithOwner;
|
||||
use Countable;
|
||||
use FFI\CData;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
use RuntimeException;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Class Stack
|
||||
* @package Cijber\OpenSSL
|
||||
*/
|
||||
abstract class Stack extends CBackedObjectWithOwner implements Countable, ArrayAccess, Traversable, IteratorAggregate
|
||||
{
|
||||
const CLASSNAME = CBackedObjectWithOwner::class;
|
||||
|
||||
abstract protected function spawn(CData $cData);
|
||||
|
||||
public static function new()
|
||||
{
|
||||
$ffi = OpenSSL::getFFI();
|
||||
$cObj = $ffi->sk_new_null();
|
||||
return new static($ffi, $cObj);
|
||||
}
|
||||
|
||||
protected function freeObject()
|
||||
{
|
||||
$this->ffi->sk_free($this->cObj);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return $this->ffi->sk_num($this->cObj);
|
||||
}
|
||||
|
||||
public function get(int $offset)
|
||||
{
|
||||
$res = $this->ffi->sk_value($this->cObj, $offset);
|
||||
|
||||
if ($res === null) {
|
||||
throw new RuntimeException("Failed to retrieve item from stack");
|
||||
}
|
||||
|
||||
return $this->spawn($res);
|
||||
}
|
||||
|
||||
public function set(int $offset, CBackedObject $object): void
|
||||
{
|
||||
/***
|
||||
* Make sure ref count is updated for old object
|
||||
*/
|
||||
/** @var CBackedObjectWithOwner $obj */
|
||||
$obj = $this->get($offset);
|
||||
$res = $this->ffi->sk_set($this->cObj, $offset, $object->cObj);
|
||||
|
||||
if ($res === null) {
|
||||
throw new RuntimeException("Failed to set item on stack");
|
||||
}
|
||||
|
||||
$obj->decreaseRefCount();
|
||||
}
|
||||
|
||||
public function shift()
|
||||
{
|
||||
$cObj = $this->ffi->sk_shift($this->cObj);
|
||||
return $this->handleResult($cObj);
|
||||
}
|
||||
|
||||
protected function handleResult(?CData $cObj)
|
||||
{
|
||||
if ($cObj === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var CBackedObjectWithOwner $obj */
|
||||
$obj = $this->spawn($cObj);
|
||||
$obj->decreaseRefCount();
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function pop()
|
||||
{
|
||||
$cObj = $this->ffi->sk_pop($this->cObj);
|
||||
return $this->handleResult($cObj);
|
||||
}
|
||||
|
||||
public function push(CBackedObject $object): int
|
||||
{
|
||||
$this->ensureCorrect($object);
|
||||
$idx = $this->ffi->sk_push($this->cObj, $object->cObj);
|
||||
if ($idx === 0) {
|
||||
throw new RuntimeException("Failed to insert element");
|
||||
}
|
||||
|
||||
$object->pushRefCount();
|
||||
|
||||
return $idx;
|
||||
}
|
||||
|
||||
private function ensureCorrect($object)
|
||||
{
|
||||
$expectedClassName = static::CLASSNAME;
|
||||
|
||||
if (!($object instanceof $expectedClassName)) {
|
||||
throw new InvalidArgumentException("Expected object of class $expectedClassName got object of class " . get_class($object));
|
||||
}
|
||||
}
|
||||
|
||||
public function unshift(CBackedObject $object): int
|
||||
{
|
||||
$this->ensureCorrect($object);
|
||||
|
||||
$idx = $this->ffi->sk_unshift($this->cObj, $object->cObj);
|
||||
if ($idx === 0) {
|
||||
throw new RuntimeException("Failed to insert element");
|
||||
}
|
||||
|
||||
$object->pushRefCount();
|
||||
|
||||
return $idx;
|
||||
}
|
||||
|
||||
public function freeAll()
|
||||
{
|
||||
$this->ffi->sk_pop_free($this->cObj, function (CData $cObj) {
|
||||
/** @var CBackedObject $cObj */
|
||||
$cObj = $this->spawn($cObj);
|
||||
$cObj->decreaseRefCount();
|
||||
$cObj->free();
|
||||
});
|
||||
|
||||
$this->freed();
|
||||
}
|
||||
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return $offset >= 0 && $offset < $this->count();
|
||||
}
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if ($offset === null) {
|
||||
$this->push($value);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
return $this->delete($offset);
|
||||
}
|
||||
|
||||
public function delete($offset)
|
||||
{
|
||||
$obj = $this->ffi->sk_delete($this->cObj, $offset);
|
||||
if ($obj === null) {
|
||||
throw new RuntimeException("Failed to delete element $offset from stack");
|
||||
}
|
||||
|
||||
/** @var CBackedObjectWithOwner $phpObj */
|
||||
$phpObj = $this->spawn($obj);
|
||||
$phpObj->decreaseRefCount();
|
||||
|
||||
return $phpObj;
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
for ($i = 0; $i < $this->count(); $i++) {
|
||||
yield $this[$i];
|
||||
}
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$cObj = $this->ffi->sk_dup($this->cObj);
|
||||
|
||||
if ($cObj === null) {
|
||||
throw new RuntimeException("Failed to clone stack");
|
||||
}
|
||||
|
||||
/** @var CBackedObjectWithOwner $obj */
|
||||
foreach ($this as $obj) {
|
||||
$obj->pushRefCount();
|
||||
}
|
||||
|
||||
return new static($this->ffi, $cObj);
|
||||
}
|
||||
}
|
24
src/OpenSSL/Stack/X509Stack.php
Normal file
24
src/OpenSSL/Stack/X509Stack.php
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Cijber\OpenSSL\Stack;
|
||||
|
||||
use Cijber\OpenSSL\Stack;
|
||||
use Cijber\OpenSSL\X509;
|
||||
use FFI;
|
||||
use FFI\CData;
|
||||
|
||||
class X509Stack extends Stack
|
||||
{
|
||||
const CLASSNAME = X509::class;
|
||||
|
||||
public static function from(FFI $ffi, CData $cObj): X509Stack
|
||||
{
|
||||
return new X509Stack($ffi, $cObj);
|
||||
}
|
||||
|
||||
public function spawn(CData $cData): X509
|
||||
{
|
||||
return X509::cast($this->ffi, $cData);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ namespace Cijber\OpenSSL\Tests;
|
|||
|
||||
use Cijber\OpenSSL\BIO;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use RuntimeException;
|
||||
|
||||
class BIOTest extends TestCase
|
||||
{
|
||||
|
@ -46,4 +47,14 @@ class BIOTest extends TestCase
|
|||
$part = $bio->read(3);
|
||||
$this->assertEquals("Hel", $part);
|
||||
}
|
||||
|
||||
public function testUsingObjectAfterFree()
|
||||
{
|
||||
$bio = BIO::buffer("Hello world");
|
||||
$this->assertTrue($bio->free());
|
||||
|
||||
$this->expectException(RuntimeException::class);
|
||||
$this->expectExceptionMessage("object Cijber\OpenSSL\BIO already freed, can't be used");
|
||||
$bio->getType();
|
||||
}
|
||||
}
|
|
@ -20,8 +20,8 @@ class PKCS7Test extends TestCase
|
|||
$der = file_get_contents(__DIR__ . "/data/pkcs7/1.RSA");
|
||||
$pkcs7 = PKCS7::loadFromDER($der);
|
||||
$newDer = $pkcs7->toDER();
|
||||
$this->assertEquals($der, $newDer);
|
||||
$this->assertEquals(PKCS7::NID_SIGNED, $pkcs7->getType());
|
||||
$this->assertEquals($der, $newDer);
|
||||
}
|
||||
|
||||
public function testLoadingGarbageDER()
|
||||
|
|
131
tests/StackTest.php
Normal file
131
tests/StackTest.php
Normal file
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Cijber\OpenSSL\Tests;
|
||||
|
||||
|
||||
use Cijber\OpenSSL;
|
||||
use Cijber\OpenSSL\Stack;
|
||||
use Cijber\OpenSSL\X509;
|
||||
use PHPStan\Testing\TestCase;
|
||||
|
||||
class StackTest extends TestCase
|
||||
{
|
||||
function testCreationAndDestruction()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$this->assertCount(0, $stack);
|
||||
}
|
||||
|
||||
function testPush()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$x509 = X509::new();
|
||||
$stack->push($x509);
|
||||
$this->assertCount(1, $stack);
|
||||
$x509Item = $stack->get(0);
|
||||
$this->assertSame($x509, $x509Item);
|
||||
}
|
||||
|
||||
function testSet()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$a = X509::new();
|
||||
$b = X509::new();
|
||||
|
||||
$stack->push($a);
|
||||
$stack->set(0, $b);
|
||||
$this->assertNotSame($a, $stack->get(0));
|
||||
$this->assertCount(1, $stack);
|
||||
$this->assertEquals(0, $a->getRefCount());
|
||||
}
|
||||
|
||||
function testDelete()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$a = X509::new();
|
||||
$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);
|
||||
}
|
||||
|
||||
function testRetention()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$x509 = X509::new();
|
||||
$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());
|
||||
}
|
||||
|
||||
function testAddressingCorrect()
|
||||
{
|
||||
$ffi = OpenSSL::getFFI();
|
||||
$x509 = $ffi->X509_new();
|
||||
$address = OpenSSL::addressOf($x509);
|
||||
$st = $ffi->sk_new_null();
|
||||
$ffi->sk_push($st, $x509);
|
||||
$second = $ffi->cast("X509*", $ffi->sk_value($st, 0));
|
||||
$secondAddress = OpenSSL::addressOf($second);
|
||||
$this->assertEquals($address, $secondAddress);
|
||||
}
|
||||
|
||||
function testForeach()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$a = X509::new();
|
||||
$b = X509::new();
|
||||
$c = X509::new();
|
||||
$stack->push($a);
|
||||
$stack->push($b);
|
||||
$stack->push($c);
|
||||
|
||||
$x = 0;
|
||||
foreach ($stack as $x509) {
|
||||
$x++;
|
||||
$this->assertEquals(X509::class, get_class($x509));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function testPop()
|
||||
{
|
||||
$stack = Stack\X509Stack::new();
|
||||
$a = X509::new();
|
||||
$b = X509::new();
|
||||
$c = X509::new();
|
||||
$stack->push($a);
|
||||
$stack->push($b);
|
||||
$stack[] = $c;
|
||||
|
||||
$i = 0;
|
||||
while ($x509 = $stack->pop()) {
|
||||
$this->assertEquals(X509::class, get_class($x509));
|
||||
$i++;
|
||||
}
|
||||
|
||||
$this->assertEquals(3, $i);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue