Compare commits

...

2 commits

Author SHA1 Message Date
c0e61c21f0
add ability export PKCS7 to DER
All checks were successful
continuous-integration/drone/push Build is passing
2019-11-25 23:05:11 +01:00
c5f63d84ad
add comments and clean up code 2019-11-25 23:04:48 +01:00
5 changed files with 107 additions and 17 deletions

View file

@ -1,3 +1,5 @@
void CRYPTO_free(void *addr);
typedef struct crypto_ex_data_st CRYPTO_EX_DATA;
struct crypto_ex_data_st {
struct stack_st_void *sk;

View file

@ -6,4 +6,4 @@ 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);

View file

@ -196,33 +196,60 @@ class BIO extends CBackedObjectWithOwner
const FLAG_RWS = self::FLAG_READ | self::FLAG_WRITE | self::FLAG_IO_SPECIAL;
const FLAG_SHOULD_RETRY = 0x08;
public static function new()
/**
* Create new memory based BIO
*
* @return BIO
*/
public static function new(): BIO
{
$ffi = OpenSSL::getFFI();
$bio = $ffi->BIO_new($ffi->BIO_s_mem());
return new BIO($ffi, $bio);
}
public static function buffer(string $data)
/**
* Create new memory based BIO pre-filled with data
*
* @param string $data
* @return BIO
*/
public static function buffer(string $data): BIO
{
$ffi = OpenSSL::getFFI();
$bio = $ffi->BIO_new_mem_buf($data, strlen($data));
return new BIO($ffi, $bio);
}
public static function open($fileName, $flags)
/**
* Create new file BIO with given mode
*
* @param string $fileName which file to open
* @param string $mode mode to open file with see fopen(3)
* @return BIO
* @see fopen
*/
public static function open(string $fileName, string $mode): BIO
{
$ffi = OpenSSL::getFFI();
$bio = $ffi->BIO_new_file($fileName, $flags);
$bio = $ffi->BIO_new_file($fileName, $mode);
return new BIO($ffi, $bio);
}
/**
* @inheritDoc
*/
protected function freeObject()
{
$this->ffi->BIO_free($this->cObj);
}
/**
* Write given data to BIO, returns amount of bytes written
*
* @param string $data
* @return int
*/
function write(string $data): int
{
$len = $this->ffi->BIO_write($this->cObj, $data, strlen($data));
@ -241,11 +268,22 @@ class BIO extends CBackedObjectWithOwner
return $len;
}
/**
* Get type of BIO, indicating if this is e.g. a file see BIO::TYPE_* constants
*
* @return int
*/
function getType(): int
{
return $this->ffi->BIO_method_type($this->cObj);
}
/**
* Read from BIO
*
* @param int $chunkSize max amount of bytes to read in this operation
* @return string
*/
function read(int $chunkSize = 4096): string
{
$data = OpenSSL\C\Memory::new($chunkSize);
@ -265,6 +303,11 @@ class BIO extends CBackedObjectWithOwner
return $data->string($len);
}
/**
* Get location in file pointer, this only works with file BIO's
*
* @return int
*/
function tell()
{
if (($this->getType() & self::TYPE_FILE) !== self::TYPE_FILE) {
@ -280,7 +323,10 @@ class BIO extends CBackedObjectWithOwner
return $pos;
}
function reset()
/**
* Reset position in BIO
*/
function reset(): void
{
$res = (int)$this->ctrl(self::CTRL_RESET, 0, null);
@ -295,6 +341,11 @@ class BIO extends CBackedObjectWithOwner
throw new RuntimeException("Failed to reset BIO");
}
/**
* Seek in BIO, only works on file BIO's
*
* @param int $offset
*/
function seek(int $offset)
{
if (($this->getType() & self::TYPE_FILE) !== self::TYPE_FILE) {
@ -308,12 +359,25 @@ class BIO extends CBackedObjectWithOwner
}
}
/**
* returns true if we're at EOF of this BIO
*
* @return bool
*/
function eof(): bool
{
return (int)$this->ctrl(self::CTRL_EOF, 0, null) === 1;
}
function ctrl($prop, ?int $larg, $parg)
/**
* Send control command to BIO
*
* @param int $prop
* @param int $larg
* @param mixed $parg
* @return mixed
*/
function ctrl(int $prop, int $larg = 0, $parg = null)
{
return $this->ffi->BIO_ctrl($this->cObj, $prop, $larg, $parg);
}

View file

@ -36,8 +36,20 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
if (!in_array($type, [PKCS7::NID_DIGEST, self::NID_SIGNED, self::NID_SIGNED_AND_ENVELOPED])) {
throw new \RuntimeException("Can only verify signed or digested data");
}
}
public function toDER(): string
{
$buf = $this->ffi->new("uint8_t*");
$ptr = FFI::addr($buf);
$len = $this->ffi->i2d_PKCS7($this->cObj, $ptr);
if ($len < 0) {
throw new \RuntimeException("Failed to create DER from PKCS7 object");
}
$val = FFI::string($buf, $len);
$this->ffi->CRYPTO_free($buf);
return $val;
}
public function freeObject()
@ -54,16 +66,16 @@ class PKCS7 extends OpenSSL\C\CBackedObjectWithOwner
public static function loadFromDER(string $der): PKCS7
{
$pkcs = static::new();
$pkcs->loadDER($der);
return $pkcs;
}
private function loadDER(string $der)
{
$ffi = OpenSSL::getFFI();
$derLen = strlen($der);
$mem = Memory::buffer($der);
$this->ffi->d2i_PKCS7(FFI::addr($this->cObj), $mem->pointer(), $derLen);
$res = $ffi->d2i_PKCS7(null, $mem->pointer(), $derLen);
if ($res === null) {
throw new \RuntimeException("Failed loading DER");
}
$mem->freed();
return new static($ffi, $res);
}
}

View file

@ -4,6 +4,7 @@ namespace Cijber\OpenSSL\Tests;
use Cijber\OpenSSL\PKCS7;
use PHPUnit\Framework\TestCase;
use RuntimeException;
class PKCS7Test extends TestCase
{
@ -14,9 +15,20 @@ class PKCS7Test extends TestCase
unset($pkcs7);
}
public function testLoadDER() {
public function testLoadDER()
{
$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());
}
public function testLoadingGarbageDER()
{
$this->expectException(RuntimeException::class);
$this->expectExceptionMessage("Failed loading DER");
PKCS7::loadFromDER("blaat");
}
}