diff options
Diffstat (limited to 'wrappers/php/src')
| -rw-r--r-- | wrappers/php/src/Discovery.php | 72 | ||||
| -rw-r--r-- | wrappers/php/src/Impl/GoSlice.php | 43 | ||||
| -rw-r--r-- | wrappers/php/src/InvalidSignatureException.php | 12 | ||||
| -rw-r--r-- | wrappers/php/src/InvalidSignatureUnknownKeyException.php | 12 | ||||
| -rw-r--r-- | wrappers/php/src/SignatureTooOldException.php | 12 | ||||
| -rw-r--r-- | wrappers/php/src/UnknownVerifyException.php | 12 | ||||
| -rw-r--r-- | wrappers/php/src/VerifyException.php | 14 |
7 files changed, 177 insertions, 0 deletions
diff --git a/wrappers/php/src/Discovery.php b/wrappers/php/src/Discovery.php new file mode 100644 index 0000000..3ae7010 --- /dev/null +++ b/wrappers/php/src/Discovery.php @@ -0,0 +1,72 @@ +<?php declare(strict_types=1); + +namespace EduVpn\Common; + +use EduVpn\Common\Impl\GoSlice; +use Error; +use FFI; +use InvalidArgumentException; + +final class Discovery +{ + public function __construct() { } + + private static ?FFI $ffi = null; + + private static function ffi(): FFI + { + if (!self::$ffi) { + if (!(self::$ffi = FFI::load(__DIR__ . '/headers/eduvpn_verify_php.h'))) + throw new Error('failed to load eduvpn_verify'); + } + return self::$ffi; + } + + /** + * Verifies the signature on the JSON server_list.json/organization_list.json file. + * If the function returns, the signature is valid for the given file type. + * + * @param string $signature .minisig signature file contents. + * @param string $signedJson Signed .json file contents. + * @param string $expectedFileName The file type to be verified, one of "server_list.json" or + * "organization_list.json". + * @param int $minSignTime Minimum time for signature. Should be set to at least the time in a previously + * retrieved file. + * @return void + * @throws InvalidArgumentException If expectedFileName is not one of the allowed values. + * @throws VerifyException If signature verification fails. + */ + public static function verify(string $signature, string $signedJson, string $expectedFileName, + int $minSignTime): void + { + $ffi = self::ffi(); + $signatureData = new GoSlice($ffi, $signature); + $jsonData = new GoSlice($ffi, $signedJson); + $expectedNameData = new GoSlice($ffi, $expectedFileName); + + $result = $ffi->Verify($signatureData->slice(), $jsonData->slice(), $expectedNameData->slice(), $minSignTime); + + switch ($result) { + case 0: + return; + case 1: + throw new InvalidArgumentException('unknown expected file name', $result); + case 2: + throw new InvalidSignatureException(); + case 3: + throw new InvalidSignatureUnknownKeyException(); + case 4: + throw new SignatureTooOldException(); + default: + throw new UnknownVerifyException($result); + } + } + + /** @internal Use for testing only, see Go documentation. */ + public static function insecureTestingSetExtraKey(string $keyString): void + { + $ffi = self::ffi(); + $keyData = new GoSlice($ffi, $keyString); + $ffi->InsecureTestingSetExtraKey($keyData->slice()); + } +} diff --git a/wrappers/php/src/Impl/GoSlice.php b/wrappers/php/src/Impl/GoSlice.php new file mode 100644 index 0000000..441b460 --- /dev/null +++ b/wrappers/php/src/Impl/GoSlice.php @@ -0,0 +1,43 @@ +<?php declare(strict_types=1); + +/** @internal */ + +namespace EduVpn\Common\Impl; + +use FFI; +use FFI\CData; +use RuntimeException; + +/** @internal */ +class GoSlice +{ + // Will be destroyed along with this GoSlice + private CData $cData, $slice; + + public function __construct(FFI $ffi, string $data) + { + $len = strlen($data); + $cData = FFI::new(FFI::arrayType(FFI::type('char'), [$len]), false); + if (!$cData) throw new RuntimeException('error allocating buffer'); + $this->cData = $cData; + FFI::memcpy($cData, $data, $len); + + $slice = $ffi->new('GoSlice'); + if (!$slice) throw new RuntimeException('error allocating buffer'); + $this->slice = $slice; + $slice->data = FFI::addr($cData); // $cData must not be destroyed while $slice is in use + $slice->cap = $slice->len = $len; + } + + public function slice(): CData + { + return $this->slice; + } + + public function __destruct() + { + // Make sure we do not unknowingly use a slice with deallocated data + $this->slice->data = null; + $this->slice->cap = $this->slice->len = 0; + } +} diff --git a/wrappers/php/src/InvalidSignatureException.php b/wrappers/php/src/InvalidSignatureException.php new file mode 100644 index 0000000..d5d4164 --- /dev/null +++ b/wrappers/php/src/InvalidSignatureException.php @@ -0,0 +1,12 @@ +<?php declare(strict_types=1); + +namespace EduVpn\Common; + +/** Signature is invalid (for the expected file type). */ +final class InvalidSignatureException extends VerifyException +{ + public function __construct() + { + parent::__construct('invalid signature', 2); + } +} diff --git a/wrappers/php/src/InvalidSignatureUnknownKeyException.php b/wrappers/php/src/InvalidSignatureUnknownKeyException.php new file mode 100644 index 0000000..73a535e --- /dev/null +++ b/wrappers/php/src/InvalidSignatureUnknownKeyException.php @@ -0,0 +1,12 @@ +<?php declare(strict_types=1); + +namespace EduVpn\Common; + +/** Signature was created with an unknown key and has not been verified. */ +final class InvalidSignatureUnknownKeyException extends VerifyException +{ + public function __construct() + { + parent::__construct('invalid signature (unknown key)', 3); + } +} diff --git a/wrappers/php/src/SignatureTooOldException.php b/wrappers/php/src/SignatureTooOldException.php new file mode 100644 index 0000000..bbae949 --- /dev/null +++ b/wrappers/php/src/SignatureTooOldException.php @@ -0,0 +1,12 @@ +<?php declare(strict_types=1); + +namespace EduVpn\Common; + +/** Signature has a timestamp lower than the specified minimum signing time. */ +final class SignatureTooOldException extends VerifyException +{ + public function __construct() + { + parent::__construct('replay of previous signature (rollback)', 4); + } +} diff --git a/wrappers/php/src/UnknownVerifyException.php b/wrappers/php/src/UnknownVerifyException.php new file mode 100644 index 0000000..1290cc3 --- /dev/null +++ b/wrappers/php/src/UnknownVerifyException.php @@ -0,0 +1,12 @@ +<?php declare(strict_types=1); + +namespace EduVpn\Common; + +/** Other unknown error. */ +final class UnknownVerifyException extends VerifyException +{ + public function __construct(int $code) + { + parent::__construct("unknown verify error ($code)", $code); + } +} diff --git a/wrappers/php/src/VerifyException.php b/wrappers/php/src/VerifyException.php new file mode 100644 index 0000000..101f728 --- /dev/null +++ b/wrappers/php/src/VerifyException.php @@ -0,0 +1,14 @@ +<?php declare(strict_types=1); + +namespace EduVpn\Common; + +use Exception; + +/** Verification failed, do not trust the file. */ +abstract class VerifyException extends Exception +{ + public function __construct(string $message, int $code) + { + parent::__construct($message, $code); + } +} |
