summaryrefslogtreecommitdiff
path: root/wrappers/php/src
diff options
context:
space:
mode:
Diffstat (limited to 'wrappers/php/src')
-rw-r--r--wrappers/php/src/Discovery.php72
-rw-r--r--wrappers/php/src/Impl/GoSlice.php43
-rw-r--r--wrappers/php/src/InvalidSignatureException.php12
-rw-r--r--wrappers/php/src/InvalidSignatureUnknownKeyException.php12
-rw-r--r--wrappers/php/src/SignatureTooOldException.php12
-rw-r--r--wrappers/php/src/UnknownVerifyException.php12
-rw-r--r--wrappers/php/src/VerifyException.php14
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);
+ }
+}