From 6eb73da4c104269be3512cbb6280750d3cc14ff8 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Sat, 16 Nov 2024 18:34:14 -0100 Subject: [PATCH] fix(ocm): switching to IdentityProof Signed-off-by: Maxence Lange --- .../cloud_federation_api/lib/Capabilities.php | 4 +- .../Controller/RequestHandlerController.php | 15 +- .../cleanup-remote-storage.feature | 3 - lib/composer/composer/autoload_classmap.php | 7 - lib/composer/composer/autoload_static.php | 7 - .../CloudFederationProviderManager.php | 109 +++++------ lib/private/Files/Storage/DAV.php | 7 - lib/private/OCM/Model/OCMProvider.php | 4 +- lib/private/OCM/OCMDiscoveryService.php | 8 + lib/private/OCM/OCMSignatoryManager.php | 20 +- .../Security/IdentityProof/Manager.php | 51 ++++- .../PublicPrivateKeyPairs/KeyPairManager.php | 182 ------------------ .../PublicPrivateKeyPairs/Model/KeyPair.php | 114 ----------- .../Security/Signature/SignatureManager.php | 7 +- lib/private/Server.php | 3 - lib/public/OCM/IOCMProvider.php | 4 +- .../Exceptions/KeyPairConflictException.php | 18 -- .../Exceptions/KeyPairException.php | 20 -- .../Exceptions/KeyPairNotFoundException.php | 16 -- .../PublicPrivateKeyPairs/IKeyPairManager.php | 80 -------- .../PublicPrivateKeyPairs/Model/IKeyPair.php | 85 -------- version.php | 2 +- 22 files changed, 120 insertions(+), 646 deletions(-) delete mode 100644 lib/private/Security/PublicPrivateKeyPairs/KeyPairManager.php delete mode 100644 lib/private/Security/PublicPrivateKeyPairs/Model/KeyPair.php delete mode 100644 lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairConflictException.php delete mode 100644 lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairException.php delete mode 100644 lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairNotFoundException.php delete mode 100644 lib/unstable/Security/PublicPrivateKeyPairs/IKeyPairManager.php delete mode 100644 lib/unstable/Security/PublicPrivateKeyPairs/Model/IKeyPair.php diff --git a/apps/cloud_federation_api/lib/Capabilities.php b/apps/cloud_federation_api/lib/Capabilities.php index 1910a03233791..08806caa5e3e3 100644 --- a/apps/cloud_federation_api/lib/Capabilities.php +++ b/apps/cloud_federation_api/lib/Capabilities.php @@ -8,7 +8,7 @@ */ namespace OCA\CloudFederationAPI; -use NCU\Security\PublicPrivateKeyPairs\Exceptions\KeyPairException; +use NCU\Security\Signature\Exceptions\IdentityNotFoundException; use NCU\Security\Signature\Exceptions\SignatoryException; use OC\OCM\OCMSignatoryManager; use OCP\Capabilities\ICapability; @@ -79,7 +79,7 @@ public function getCapabilities() { } else { $this->logger->debug('ocm public key feature disabled'); } - } catch (SignatoryException|KeyPairException $e) { + } catch (SignatoryException|IdentityNotFoundException $e) { $this->logger->warning('cannot generate local signatory', ['exception' => $e]); } diff --git a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php index db7f81d559675..bb5b3c2e1f1a6 100644 --- a/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php +++ b/apps/cloud_federation_api/lib/Controller/RequestHandlerController.php @@ -5,6 +5,7 @@ */ namespace OCA\CloudFederationAPI\Controller; +use NCU\Security\Signature\Exceptions\IdentityNotFoundException; use NCU\Security\Signature\Exceptions\IncomingRequestException; use NCU\Security\Signature\Exceptions\SignatoryNotFoundException; use NCU\Security\Signature\Exceptions\SignatureException; @@ -427,16 +428,10 @@ private function getHostFromFederationId(string $entry): string { } [, $rightPart] = explode('@', $entry, 2); - $host = parse_url($rightPart, PHP_URL_HOST); - $port = parse_url($rightPart, PHP_URL_PORT); - if ($port !== null && $port !== false) { - $host .= ':' . $port; - } - - if (is_string($host) && $host !== '') { - return $host; + try { + return $this->signatureManager->extractIdentityFromUri($rightPart); + } catch (IdentityNotFoundException) { + throw new IncomingRequestException('invalid host within federation id: ' . $entry); } - - throw new IncomingRequestException('host is empty'); } } diff --git a/build/integration/federation_features/cleanup-remote-storage.feature b/build/integration/federation_features/cleanup-remote-storage.feature index a017b59bcf4cf..6339edb60b66e 100644 --- a/build/integration/federation_features/cleanup-remote-storage.feature +++ b/build/integration/federation_features/cleanup-remote-storage.feature @@ -35,9 +35,6 @@ Feature: cleanup-remote-storage # server may have its own /textfile0.txt" file) And User "user1" copies file "/textfile0.txt" to "/remote-share.txt" And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL" - And As an "user1" - And sending "GET" to "/apps/files_sharing/api/v1/shares" - And the list of returned shares has 1 shares And Using server "LOCAL" # Accept and download the file to ensure that a storage is created for the # federated share diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index cce9c7353aecb..36d1df30e5e4d 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -7,11 +7,6 @@ return array( 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Exceptions\\KeyPairConflictException' => $baseDir . '/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairConflictException.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Exceptions\\KeyPairException' => $baseDir . '/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairException.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Exceptions\\KeyPairNotFoundException' => $baseDir . '/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairNotFoundException.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\IKeyPairManager' => $baseDir . '/lib/unstable/Security/PublicPrivateKeyPairs/IKeyPairManager.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Model\\IKeyPair' => $baseDir . '/lib/unstable/Security/PublicPrivateKeyPairs/Model/IKeyPair.php', 'NCU\\Security\\Signature\\Exceptions\\IdentityNotFoundException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/IdentityNotFoundException.php', 'NCU\\Security\\Signature\\Exceptions\\IncomingRequestException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/IncomingRequestException.php', 'NCU\\Security\\Signature\\Exceptions\\IncomingRequestNotFoundException' => $baseDir . '/lib/unstable/Security/Signature/Exceptions/IncomingRequestNotFoundException.php', @@ -1920,8 +1915,6 @@ 'OC\\Security\\Ip\\Range' => $baseDir . '/lib/private/Security/Ip/Range.php', 'OC\\Security\\Ip\\RemoteAddress' => $baseDir . '/lib/private/Security/Ip/RemoteAddress.php', 'OC\\Security\\Normalizer\\IpAddress' => $baseDir . '/lib/private/Security/Normalizer/IpAddress.php', - 'OC\\Security\\PublicPrivateKeyPairs\\KeyPairManager' => $baseDir . '/lib/private/Security/PublicPrivateKeyPairs/KeyPairManager.php', - 'OC\\Security\\PublicPrivateKeyPairs\\Model\\KeyPair' => $baseDir . '/lib/private/Security/PublicPrivateKeyPairs/Model/KeyPair.php', 'OC\\Security\\RateLimiting\\Backend\\DatabaseBackend' => $baseDir . '/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php', 'OC\\Security\\RateLimiting\\Backend\\IBackend' => $baseDir . '/lib/private/Security/RateLimiting/Backend/IBackend.php', 'OC\\Security\\RateLimiting\\Backend\\MemoryCacheBackend' => $baseDir . '/lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index bd7e92bb0712c..a21c1f1e09d9c 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -48,11 +48,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 public static $classMap = array ( 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Exceptions\\KeyPairConflictException' => __DIR__ . '/../../..' . '/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairConflictException.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Exceptions\\KeyPairException' => __DIR__ . '/../../..' . '/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairException.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Exceptions\\KeyPairNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairNotFoundException.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\IKeyPairManager' => __DIR__ . '/../../..' . '/lib/unstable/Security/PublicPrivateKeyPairs/IKeyPairManager.php', - 'NCU\\Security\\PublicPrivateKeyPairs\\Model\\IKeyPair' => __DIR__ . '/../../..' . '/lib/unstable/Security/PublicPrivateKeyPairs/Model/IKeyPair.php', 'NCU\\Security\\Signature\\Exceptions\\IdentityNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/IdentityNotFoundException.php', 'NCU\\Security\\Signature\\Exceptions\\IncomingRequestException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/IncomingRequestException.php', 'NCU\\Security\\Signature\\Exceptions\\IncomingRequestNotFoundException' => __DIR__ . '/../../..' . '/lib/unstable/Security/Signature/Exceptions/IncomingRequestNotFoundException.php', @@ -1961,8 +1956,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Security\\Ip\\Range' => __DIR__ . '/../../..' . '/lib/private/Security/Ip/Range.php', 'OC\\Security\\Ip\\RemoteAddress' => __DIR__ . '/../../..' . '/lib/private/Security/Ip/RemoteAddress.php', 'OC\\Security\\Normalizer\\IpAddress' => __DIR__ . '/../../..' . '/lib/private/Security/Normalizer/IpAddress.php', - 'OC\\Security\\PublicPrivateKeyPairs\\KeyPairManager' => __DIR__ . '/../../..' . '/lib/private/Security/PublicPrivateKeyPairs/KeyPairManager.php', - 'OC\\Security\\PublicPrivateKeyPairs\\Model\\KeyPair' => __DIR__ . '/../../..' . '/lib/private/Security/PublicPrivateKeyPairs/Model/KeyPair.php', 'OC\\Security\\RateLimiting\\Backend\\DatabaseBackend' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/DatabaseBackend.php', 'OC\\Security\\RateLimiting\\Backend\\IBackend' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/IBackend.php', 'OC\\Security\\RateLimiting\\Backend\\MemoryCacheBackend' => __DIR__ . '/../../..' . '/lib/private/Security/RateLimiting/Backend/MemoryCacheBackend.php', diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php index eeb161c3b2514..74935ead40144 100644 --- a/lib/private/Federation/CloudFederationProviderManager.php +++ b/lib/private/Federation/CloudFederationProviderManager.php @@ -18,6 +18,7 @@ use OCP\Federation\ICloudFederationProviderManager; use OCP\Federation\ICloudFederationShare; use OCP\Federation\ICloudIdManager; +use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; use OCP\IAppConfig; @@ -105,25 +106,11 @@ public function getCloudFederationProvider($resourceType) { public function sendShare(ICloudFederationShare $share) { $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); try { - $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); - } catch (OCMProviderException $e) { - return false; - } - - $client = $this->httpClientService->newClient(); - try { - // signing the payload using OCMSignatoryManager before initializing the request - $uri = $ocmProvider->getEndPoint() . '/shares'; - $payload = array_merge($this->getDefaultRequestOptions(), ['body' => json_encode($share->getShare())]); - if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) { - $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload( - $this->signatoryManager, - $payload, - 'post', $uri - ); + try { + $response = $this->postOcmPayload($cloudID->getRemote(), '/shares', json_encode($share->getShare())); + } catch (OCMProviderException) { + return false; } - $response = $client->post($uri, $signedPayload ?? $payload); - if ($response->getStatusCode() === Http::STATUS_CREATED) { $result = json_decode($response->getBody(), true); return (is_array($result)) ? $result : []; @@ -149,22 +136,9 @@ public function sendShare(ICloudFederationShare $share) { */ public function sendCloudShare(ICloudFederationShare $share): IResponse { $cloudID = $this->cloudIdManager->resolveCloudId($share->getShareWith()); - $ocmProvider = $this->discoveryService->discover($cloudID->getRemote()); - $client = $this->httpClientService->newClient(); try { - // signing the payload using OCMSignatoryManager before initializing the request - $uri = $ocmProvider->getEndPoint() . '/shares'; - $payload = array_merge($this->getDefaultRequestOptions(), ['body' => json_encode($share->getShare())]); - if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) { - $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload( - $this->signatoryManager, - $payload, - 'post', $uri - ); - } - - return $client->post($uri, $signedPayload ?? $payload); + return $this->postOcmPayload($cloudID->getRemote(), '/shares', json_encode($share->getShare()), $client); } catch (\Throwable $e) { $this->logger->error('Error while sending share to federation server: ' . $e->getMessage(), ['exception' => $e]); try { @@ -183,26 +157,11 @@ public function sendCloudShare(ICloudFederationShare $share): IResponse { */ public function sendNotification($url, ICloudFederationNotification $notification) { try { - $ocmProvider = $this->discoveryService->discover($url); - } catch (OCMProviderException $e) { - return false; - } - - $client = $this->httpClientService->newClient(); - try { - - // signing the payload using OCMSignatoryManager before initializing the request - $uri = $ocmProvider->getEndPoint() . '/notifications'; - $payload = array_merge($this->getDefaultRequestOptions(), ['body' => json_encode($notification->getMessage())]); - if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) { - $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload( - $this->signatoryManager, - $payload, - 'post', $uri - ); + try { + $response = $this->postOcmPayload($url, '/notifications', json_encode($notification->getMessage())); + } catch (OCMProviderException) { + return false; } - $response = $client->post($uri, $signedPayload ?? $payload); - if ($response->getStatusCode() === Http::STATUS_CREATED) { $result = json_decode($response->getBody(), true); return (is_array($result)) ? $result : []; @@ -222,21 +181,9 @@ public function sendNotification($url, ICloudFederationNotification $notificatio * @throws OCMProviderException */ public function sendCloudNotification(string $url, ICloudFederationNotification $notification): IResponse { - $ocmProvider = $this->discoveryService->discover($url); - $client = $this->httpClientService->newClient(); try { - // signing the payload using OCMSignatoryManager before initializing the request - $uri = $ocmProvider->getEndPoint() . '/notifications'; - $payload = array_merge($this->getDefaultRequestOptions(), ['body' => json_encode($notification->getMessage())]); - if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) { - $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload( - $this->signatoryManager, - $payload, - 'post', $uri - ); - } - return $client->post($uri, $signedPayload ?? $payload); + return $this->postOcmPayload($url, '/notifications', json_encode($notification->getMessage()), $client); } catch (\Throwable $e) { $this->logger->error('Error while sending notification to federation server: ' . $e->getMessage(), ['exception' => $e]); try { @@ -256,6 +203,40 @@ public function isReady() { return $this->appManager->isEnabledForUser('cloud_federation_api'); } + /** + * @param string $cloudId + * @param string $uri + * @param string $payload + * + * @return IResponse + * @throws OCMProviderException + */ + private function postOcmPayload(string $cloudId, string $uri, string $payload, ?IClient $client = null): IResponse { + $ocmProvider = $this->discoveryService->discover($cloudId); + $uri = $ocmProvider->getEndPoint() . '/' . ltrim($uri, '/'); + $client = $client ?? $this->httpClientService->newClient(); + return $client->post($uri, $this->prepareOcmPayload($uri, $payload)); + } + + /** + * @param string $uri + * @param string $payload + * + * @return array + */ + private function prepareOcmPayload(string $uri, string $payload): array { + $payload = array_merge($this->getDefaultRequestOptions(), ['body' => $payload]); + if (!$this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_DISABLED, lazy: true)) { + $signedPayload = $this->signatureManager->signOutgoingRequestIClientPayload( + $this->signatoryManager, + $payload, + 'post', $uri + ); + } + + return $signedPayload ?? $payload; + } + private function getDefaultRequestOptions(): array { return [ 'headers' => ['content-type' => 'application/json'], diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php index 597b3f4748888..10670d6331a0e 100644 --- a/lib/private/Files/Storage/DAV.php +++ b/lib/private/Files/Storage/DAV.php @@ -64,7 +64,6 @@ class DAV extends Common { protected $httpClientService; /** @var ICertificateManager */ protected $certManager; - protected bool $verify = true; protected LoggerInterface $logger; protected IEventLogger $eventLogger; protected IMimeTypeDetector $mimeTypeDetector; @@ -104,7 +103,6 @@ public function __construct(array $parameters) { if (isset($parameters['authType'])) { $this->authType = $parameters['authType']; } - $this->verify = (($parameters['verify'] ?? true) !== false); if (isset($parameters['secure'])) { if (is_string($parameters['secure'])) { $this->secure = ($parameters['secure'] === 'true'); @@ -164,11 +162,6 @@ protected function init(): void { } } - if (!$this->verify) { - $this->client->addCurlSetting(CURLOPT_SSL_VERIFYHOST, 0); - $this->client->addCurlSetting(CURLOPT_SSL_VERIFYPEER, false); - } - $lastRequestStart = 0; $this->client->on('beforeRequest', function (RequestInterface $request) use (&$lastRequestStart) { $this->logger->debug('sending dav ' . $request->getMethod() . ' request to external storage: ' . $request->getAbsoluteUrl(), ['app' => 'dav']); diff --git a/lib/private/OCM/Model/OCMProvider.php b/lib/private/OCM/Model/OCMProvider.php index cd4e9c49c3b29..95ba83882f22b 100644 --- a/lib/private/OCM/Model/OCMProvider.php +++ b/lib/private/OCM/Model/OCMProvider.php @@ -210,11 +210,11 @@ private function looksValid(): bool { * apiVersion: '1.0-proposal1', * endPoint: string, * publicKey: ISignatory|null, - * resourceTypes: array{ + * resourceTypes: list, * protocols: array - * }[], + * }>, * version: string * } */ diff --git a/lib/private/OCM/OCMDiscoveryService.php b/lib/private/OCM/OCMDiscoveryService.php index f39d0e2382177..12a28bd77fb78 100644 --- a/lib/private/OCM/OCMDiscoveryService.php +++ b/lib/private/OCM/OCMDiscoveryService.php @@ -46,6 +46,14 @@ public function __construct( */ public function discover(string $remote, bool $skipCache = false): IOCMProvider { $remote = rtrim($remote, '/'); + if (!str_starts_with($remote, 'http')) { + // if scheme not specified, we test both; + try { + return $this->discover('https://' . $remote, $skipCache); + } catch (OCMProviderException) { + return $this->discover('http://' . $remote, $skipCache); + } + } if (!$skipCache) { try { diff --git a/lib/private/OCM/OCMSignatoryManager.php b/lib/private/OCM/OCMSignatoryManager.php index 1508c1db1ef1b..ac50afee02dd2 100644 --- a/lib/private/OCM/OCMSignatoryManager.php +++ b/lib/private/OCM/OCMSignatoryManager.php @@ -8,15 +8,13 @@ */ namespace OC\OCM; -use NCU\Security\PublicPrivateKeyPairs\Exceptions\KeyPairConflictException; -use NCU\Security\PublicPrivateKeyPairs\Exceptions\KeyPairNotFoundException; -use NCU\Security\PublicPrivateKeyPairs\IKeyPairManager; use NCU\Security\Signature\Exceptions\IdentityNotFoundException; use NCU\Security\Signature\ISignatoryManager; use NCU\Security\Signature\ISignatureManager; use NCU\Security\Signature\Model\IIncomingSignedRequest; use NCU\Security\Signature\Model\ISignatory; use NCU\Security\Signature\Model\SignatoryType; +use OC\Security\IdentityProof\Manager; use OC\Security\Signature\Model\Signatory; use OCP\IAppConfig; use OCP\IURLGenerator; @@ -40,7 +38,7 @@ public function __construct( private readonly IAppConfig $appConfig, private readonly ISignatureManager $signatureManager, private readonly IURLGenerator $urlGenerator, - private readonly IKeyPairManager $keyPairManager, + private readonly Manager $identityProofManager, private readonly OCMDiscoveryService $ocmDiscoveryService, ) { } @@ -69,7 +67,6 @@ public function getOptions(): array { * @inheritDoc * * @return ISignatory - * @throws KeyPairConflictException * @throws IdentityNotFoundException * @since 31.0.0 */ @@ -85,13 +82,16 @@ public function getLocalSignatory(): ISignatory { $keyId = $this->generateKeyId(); } - try { - $keyPair = $this->keyPairManager->getKeyPair('core', 'ocm_external'); - } catch (KeyPairNotFoundException) { - $keyPair = $this->keyPairManager->generateKeyPair('core', 'ocm_external'); + if (!$this->identityProofManager->hasAppKey('core', 'ocm_external')) { + $this->identityProofManager->generateAppKey('core', 'ocm_external', [ + 'digest_alg' => 'rsa', + 'private_key_bits' => 2048, + 'private_key_type' => OPENSSL_KEYTYPE_RSA, + ]); } + $keyPair = $this->identityProofManager->getAppKey('core', 'ocm_external'); - return new Signatory($keyId, $keyPair->getPublicKey(), $keyPair->getPrivateKey(), local: true); + return new Signatory($keyId, $keyPair->getPublic(), $keyPair->getPrivate(), local: true); } /** diff --git a/lib/private/Security/IdentityProof/Manager.php b/lib/private/Security/IdentityProof/Manager.php index 0ce760ccc63b3..de0b3fe6bd16e 100644 --- a/lib/private/Security/IdentityProof/Manager.php +++ b/lib/private/Security/IdentityProof/Manager.php @@ -10,6 +10,7 @@ use OC\Files\AppData\Factory; use OCP\Files\IAppData; +use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IUser; use OCP\Security\ICrypto; @@ -31,18 +32,20 @@ public function __construct( * Calls the openssl functions to generate a public and private key. * In a separate function for unit testing purposes. * + * @param array $options config options to generate key {@see openssl_csr_new} + * * @return array [$publicKey, $privateKey] * @throws \RuntimeException */ - protected function generateKeyPair(): array { + protected function generateKeyPair(array $options = []): array { $config = [ - 'digest_alg' => 'sha512', - 'private_key_bits' => 2048, + 'digest_alg' => $options['algorithm'] ?? 'sha512', + 'private_key_bits' => $options['bits'] ?? 2048, + 'private_key_type' => $options['type'] ?? OPENSSL_KEYTYPE_RSA, ]; // Generate new key $res = openssl_pkey_new($config); - if ($res === false) { $this->logOpensslError(); throw new \RuntimeException('OpenSSL reported a problem'); @@ -65,15 +68,17 @@ protected function generateKeyPair(): array { * Note: If a key already exists it will be overwritten * * @param string $id key id + * @param array $options config options to generate key {@see openssl_csr_new} + * * @throws \RuntimeException */ - protected function generateKey(string $id): Key { - [$publicKey, $privateKey] = $this->generateKeyPair(); + protected function generateKey(string $id, array $options = []): Key { + [$publicKey, $privateKey] = $this->generateKeyPair($options); // Write the private and public key to the disk try { $this->appData->newFolder($id); - } catch (\Exception $e) { + } catch (\Exception) { } $folder = $this->appData->getFolder($id); $folder->newFile('private') @@ -125,6 +130,38 @@ public function getSystemKey(): Key { return $this->retrieveKey('system-' . $instanceId); } + public function hasAppKey(string $app, string $name): bool { + $id = $this->generateAppKeyId($app, $name); + try { + $this->appData->getFolder($id); + return true; + } catch (NotFoundException) { + return false; + } + } + + public function getAppKey(string $app, string $name): Key { + return $this->retrieveKey($this->generateAppKeyId($app, $name)); + } + + public function generateAppKey(string $app, string $name, array $options = []): Key { + return $this->generateKey($this->generateAppKeyId($app, $name), $options); + } + + public function deleteAppKey(string $app, string $name): bool { + try { + $folder = $this->appData->getFolder($this->generateAppKeyId($app, $name)); + } catch (NotFoundException) { + return false; + } + $folder->delete(); + return true; + } + + private function generateAppKeyId(string $app, string $name): string { + return 'app-' . $app . '-' . $name; + } + private function logOpensslError(): void { $errors = []; while ($error = openssl_error_string()) { diff --git a/lib/private/Security/PublicPrivateKeyPairs/KeyPairManager.php b/lib/private/Security/PublicPrivateKeyPairs/KeyPairManager.php deleted file mode 100644 index 0af960b3a30a8..0000000000000 --- a/lib/private/Security/PublicPrivateKeyPairs/KeyPairManager.php +++ /dev/null @@ -1,182 +0,0 @@ -hasKeyPair($app, $name)) { - throw new KeyPairConflictException('key pair already exist'); - } - - $keyPair = new KeyPair($app, $name); - - [$publicKey, $privateKey] = $this->generateKeys($options); - $keyPair->setPublicKey($publicKey) - ->setPrivateKey($privateKey) - ->setOptions($options); - - $this->appConfig->setValueArray( - $app, $this->generateAppConfigKey($name), - [ - 'public' => $keyPair->getPublicKey(), - 'private' => $keyPair->getPrivateKey(), - 'options' => $keyPair->getOptions() - ], - lazy: true, - sensitive: true - ); - - return $keyPair; - } - - /** - * @inheritDoc - * - * @param string $app appId - * @param string $name key name - * - * @return bool TRUE if key pair exists in database - * @since 31.0.0 - */ - public function hasKeyPair(string $app, string $name): bool { - $key = $this->generateAppConfigKey($name); - return $this->appConfig->hasKey($app, $key, lazy: true); - } - - /** - * @inheritDoc - * - * @param string $app appId - * @param string $name key name - * - * @return IKeyPair - * @throws KeyPairNotFoundException if key pair is not known - * @since 31.0.0 - */ - public function getKeyPair(string $app, string $name): IKeyPair { - if (!$this->hasKeyPair($app, $name)) { - throw new KeyPairNotFoundException('unknown key pair'); - } - - $key = $this->generateAppConfigKey($name); - $stored = $this->appConfig->getValueArray($app, $key, lazy: true); - if (!array_key_exists('public', $stored) || - !array_key_exists('private', $stored)) { - throw new KeyPairNotFoundException('corrupted key pair'); - } - - $keyPair = new KeyPair($app, $name); - return $keyPair->setPublicKey($stored['public']) - ->setPrivateKey($stored['private']) - ->setOptions($stored['options'] ?? []); - } - - /** - * @inheritDoc - * - * @param string $app appid - * @param string $name key name - * - * @since 31.0.0 - */ - public function deleteKeyPair(string $app, string $name): void { - $this->appConfig->deleteKey('core', $this->generateAppConfigKey($name)); - } - - /** - * @inheritDoc - * - * @param IKeyPair $keyPair keypair to test - * - * @return bool - * @since 31.0.0 - */ - public function testKeyPair(IKeyPair $keyPair): bool { - $clear = md5((string)time()); - - // signing with private key - openssl_sign($clear, $signed, $keyPair->getPrivateKey(), OPENSSL_ALGO_SHA256); - $encoded = base64_encode($signed); - - // verify with public key - $signed = base64_decode($encoded); - return (openssl_verify($clear, $signed, $keyPair->getPublicKey(), 'sha256') === 1); - } - - /** - * return appconfig key based on name of the key pair - * - * @param string $name - * - * @return string - */ - private function generateAppConfigKey(string $name): string { - return self::CONFIG_PREFIX . $name; - } - - /** - * generate the key pair, based on $options with the following default values: - * [ - * 'algorithm' => 'rsa', - * 'bits' => 2048, - * 'type' => OPENSSL_KEYTYPE_RSA - * ] - * - * @param array $options - * - * @return array - */ - private function generateKeys(array $options = []): array { - $res = openssl_pkey_new( - [ - 'digest_alg' => $options['algorithm'] ?? 'rsa', - 'private_key_bits' => $options['bits'] ?? 2048, - 'private_key_type' => $options['type'] ?? OPENSSL_KEYTYPE_RSA, - ] - ); - - openssl_pkey_export($res, $privateKey); - $publicKey = openssl_pkey_get_details($res)['key']; - - return [$publicKey, $privateKey]; - } -} diff --git a/lib/private/Security/PublicPrivateKeyPairs/Model/KeyPair.php b/lib/private/Security/PublicPrivateKeyPairs/Model/KeyPair.php deleted file mode 100644 index 523f7c1c38083..0000000000000 --- a/lib/private/Security/PublicPrivateKeyPairs/Model/KeyPair.php +++ /dev/null @@ -1,114 +0,0 @@ -app; - } - - /** - * @inheritDoc - * - * @return string - * @since 31.0.0 - */ - public function getName(): string { - return $this->name; - } - - /** - * @inheritDoc - * - * @param string $publicKey - * @return IKeyPair - * @since 31.0.0 - */ - public function setPublicKey(string $publicKey): IKeyPair { - $this->publicKey = $publicKey; - return $this; - } - - /** - * @inheritDoc - * - * @return string - * @since 31.0.0 - */ - public function getPublicKey(): string { - return $this->publicKey; - } - - /** - * @inheritDoc - * - * @param string $privateKey - * @return IKeyPair - * @since 31.0.0 - */ - public function setPrivateKey(string $privateKey): IKeyPair { - $this->privateKey = $privateKey; - return $this; - } - - /** - * @inheritDoc - * - * @return string - * @since 31.0.0 - */ - public function getPrivateKey(): string { - return $this->privateKey; - } - - /** - * @inheritDoc - * - * @param array $options - * @return IKeyPair - * @since 31.0.0 - */ - public function setOptions(array $options): IKeyPair { - $this->options = $options; - return $this; - } - - /** - * @inheritDoc - * - * @return array - * @since 31.0.0 - */ - public function getOptions(): array { - return $this->options; - } -} diff --git a/lib/private/Security/Signature/SignatureManager.php b/lib/private/Security/Signature/SignatureManager.php index d087e8ebdeb0c..8717171f4b415 100644 --- a/lib/private/Security/Signature/SignatureManager.php +++ b/lib/private/Security/Signature/SignatureManager.php @@ -112,9 +112,7 @@ public function getIncomingSignedRequest( $this->prepIncomingSignatureHeader($signedRequest); $this->verifyIncomingSignatureHeader($signedRequest); $this->prepEstimatedSignature($signedRequest, $options['extraSignatureHeaders'] ?? []); - $this->verifyIncomingRequestSignature( - $signedRequest, $signatoryManager, $options['ttlSignatory'] ?? self::SIGNATORY_TTL - ); + $this->verifyIncomingRequestSignature($signedRequest, $signatoryManager, $options['ttlSignatory'] ?? self::SIGNATORY_TTL); } catch (SignatureException $e) { $this->logger->warning( 'signature could not be verified', [ @@ -724,7 +722,6 @@ private function updateKnownSignatory(ISignatory $signatory): void { case SignatoryType::FORGIVABLE: $this->deleteSignatory($knownSignatory->getKeyId()); $this->insertSignatory($signatory); - return; case SignatoryType::REFRESHABLE: @@ -735,12 +732,10 @@ private function updateKnownSignatory(ISignatory $signatory): void { case SignatoryType::TRUSTED: // TODO: send notice to admin throw new SignatoryConflictException(); - break; case SignatoryType::STATIC: // TODO: send warning to admin throw new SignatoryConflictException(); - break; } } diff --git a/lib/private/Server.php b/lib/private/Server.php index 6f7a56b6344c5..468529b30307a 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -7,7 +7,6 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; -use NCU\Security\PublicPrivateKeyPairs\IKeyPairManager; use NCU\Security\Signature\ISignatureManager; use OC\Accounts\AccountManager; use OC\App\AppManager; @@ -102,7 +101,6 @@ use OC\Security\CSRF\TokenStorage\SessionStorage; use OC\Security\Hasher; use OC\Security\Ip\RemoteAddress; -use OC\Security\PublicPrivateKeyPairs\KeyPairManager; use OC\Security\RateLimiting\Limiter; use OC\Security\SecureRandom; use OC\Security\Signature\SignatureManager; @@ -1288,7 +1286,6 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(IRichTextFormatter::class, \OC\RichObjectStrings\RichTextFormatter::class); - $this->registerAlias(IKeyPairManager::class, KeyPairManager::class); $this->registerAlias(ISignatureManager::class, SignatureManager::class); $this->connectDispatcher(); diff --git a/lib/public/OCM/IOCMProvider.php b/lib/public/OCM/IOCMProvider.php index 789462efd7828..dd36a1c605715 100644 --- a/lib/public/OCM/IOCMProvider.php +++ b/lib/public/OCM/IOCMProvider.php @@ -154,11 +154,11 @@ public function import(array $data): static; * apiVersion: '1.0-proposal1', * endPoint: string, * publicKey: ISignatory|null, - * resourceTypes: array{ + * resourceTypes: list, * protocols: array - * }[], + * }>, * version: string * } * @since 28.0.0 diff --git a/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairConflictException.php b/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairConflictException.php deleted file mode 100644 index b80834264dc16..0000000000000 --- a/lib/unstable/Security/PublicPrivateKeyPairs/Exceptions/KeyPairConflictException.php +++ /dev/null @@ -1,18 +0,0 @@ -