Skip to content

Commit

Permalink
Add support for algorithm "EdDSA" and add tests.
Browse files Browse the repository at this point in the history
Signed-off-by: Joachim Bauch <[email protected]>
  • Loading branch information
fancycode committed Jul 7, 2022
1 parent fdd9a0d commit 39715df
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 7 deletions.
22 changes: 15 additions & 7 deletions lib/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,25 +404,33 @@ private function ensureSignalingTokenKeys(string $alg): void {
return;
}

if (substr($alg, 0, 2) === 'EC') {
if (substr($alg, 0, 2) === 'ES') {
$privKey = openssl_pkey_new([
'curve_name' => 'prime256v1',
'private_key_type' => OPENSSL_KEYTYPE_EC,
]);
$pubKey = openssl_pkey_get_details($privKey);
$public = $pubKey['key'];
if (!openssl_pkey_export($privKey, $secret)) {
throw new \Exception('Could not export private key');
}
} elseif (substr($alg, 0, 2) === 'RS') {
$privKey = openssl_pkey_new([
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
$pubKey = openssl_pkey_get_details($privKey);
$public = $pubKey['key'];
if (!openssl_pkey_export($privKey, $secret)) {
throw new \Exception('Could not export private key');
}
} elseif ($alg === 'EdDSA') {
$privKey = sodium_crypto_sign_keypair();
$public = base64_encode(sodium_crypto_sign_publickey($privKey));
$secret = base64_encode(sodium_crypto_sign_secretkey($privKey));
} else {
throw new \Exception('Unsupported algorithm ' . $alg);
}
$pubKey = openssl_pkey_get_details($privKey);
$public = $pubKey['key'];

if (!openssl_pkey_export($privKey, $secret)) {
throw new \Exception('Could not export private key');
}

$this->config->setAppValue('spreed', 'signaling_token_privkey_' . strtolower($alg), $secret);
$this->config->setAppValue('spreed', 'signaling_token_pubkey_' . strtolower($alg), $public);
Expand Down
17 changes: 17 additions & 0 deletions tests/php/CapabilitiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,21 @@ public function testGetCapabilitiesUserDisallowed(): void {
$this->assertInstanceOf(IPublicCapability::class, $capabilities);
$this->assertSame([], $capabilities->getCapabilities());
}

public function testCapabilitiesHelloV2Key(): void {
$capabilities = new Capabilities(
$this->serverConfig,
$this->talkConfig,
$this->commentsManager,
$this->userSession,
$this->appManager
);

$this->talkConfig->expects($this->once())
->method('getSignalingTokenPublicKey')
->willReturn('this-is-the-key');

$data = $capabilities->getCapabilities();
$this->assertEquals('this-is-the-key', $data['spreed']['config']['signaling']['hello-v2-token-key']);
}
}
126 changes: 126 additions & 0 deletions tests/php/ConfigTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
*/
namespace OCA\Talk\Tests\php;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

use OCA\Talk\Config;
use OCA\Talk\Events\GetTurnServersEvent;
use OCA\Talk\Tests\php\Mocks\GetTurnServerListener;
Expand All @@ -28,6 +31,7 @@
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Security\ISecureRandom;
use PHPUnit\Framework\MockObject\MockObject;
Expand Down Expand Up @@ -330,4 +334,126 @@ public function testGetWebSocketDomainForSignalingServer($url, $expectedWebSocke
self::invokePrivate($helper, 'getWebSocketDomainForSignalingServer', [$url])
);
}

public function dataTicketV2Algorithm() {
return [
['ES384'],
['ES256'],
['RS256'],
['RS384'],
['RS512'],
['EdDSA'],
];
}

/**
* @dataProvider dataTicketV2Algorithm
* @param string $algo
*/
public function testSignalingTicketV2User(string $algo): void {
/** @var IConfig $config */
$config = \OC::$server->getConfig();
/** @var MockObject|ITimeFactory $timeFactory */
$timeFactory = $this->createMock(ITimeFactory::class);
/** @var MockObject|ISecureRandom $secureRandom */
$secureRandom = $this->createMock(ISecureRandom::class);
/** @var MockObject|IGroupManager $groupManager */
$groupManager = $this->createMock(IGroupManager::class);
/** @var MockObject|IUserManager $userManager */
$userManager = $this->createMock(IUserManager::class);
/** @var MockObject|IURLGenerator $urlGenerator */
$urlGenerator = $this->createMock(IURLGenerator::class);
/** @var MockObject|IEventDispatcher $dispatcher */
$dispatcher = $this->createMock(IEventDispatcher::class);
/** @var MockObject|IUser $user */
$user = $this->createMock(IUser::class);

$now = time();
$timeFactory
->expects($this->once())
->method('getTime')
->willReturn($now);
$urlGenerator
->expects($this->once())
->method('getAbsoluteURL')
->with('')
->willReturn('https://domain.invalid/nextcloud');
$userManager
->expects($this->once())
->method('get')
->with('user1')
->willReturn($user);
$user
->expects($this->once())
->method('getUID')
->willReturn('user1');
$user
->expects($this->once())
->method('getDisplayName')
->willReturn('Jane Doe');

$helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);

$config->setAppValue('spreed', 'signaling_token_alg', $algo);
// Make sure new keys are generated.
$config->deleteAppValue('spreed', 'signaling_token_privkey_' . strtolower($algo));
$config->deleteAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo));
$ticket = $helper->getSignalingTicket(Config::SIGNALING_TICKET_V2, 'user1');
$this->assertNotNull($ticket);

$key = new Key($config->getAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)), $algo);
$decoded = JWT::decode($ticket, $key);

$this->assertEquals($now, $decoded->iat);
$this->assertEquals('https://domain.invalid/nextcloud', $decoded->iss);
$this->assertEquals('user1', $decoded->sub);
$this->assertSame(['displayname' => 'Jane Doe'], (array) $decoded->userdata);
}

/**
* @dataProvider dataTicketV2Algorithm
* @param string $algo
*/
public function testSignalingTicketV2Anonymous(string $algo): void {
/** @var IConfig $config */
$config = \OC::$server->getConfig();
/** @var MockObject|ITimeFactory $timeFactory */
$timeFactory = $this->createMock(ITimeFactory::class);
/** @var MockObject|ISecureRandom $secureRandom */
$secureRandom = $this->createMock(ISecureRandom::class);
/** @var MockObject|IGroupManager $groupManager */
$groupManager = $this->createMock(IGroupManager::class);
/** @var MockObject|IUserManager $userManager */
$userManager = $this->createMock(IUserManager::class);
/** @var MockObject|IURLGenerator $urlGenerator */
$urlGenerator = $this->createMock(IURLGenerator::class);
/** @var MockObject|IEventDispatcher $dispatcher */
$dispatcher = $this->createMock(IEventDispatcher::class);

$now = time();
$timeFactory
->expects($this->once())
->method('getTime')
->willReturn($now);
$urlGenerator
->expects($this->once())
->method('getAbsoluteURL')
->with('')
->willReturn('https://domain.invalid/nextcloud');

$helper = new Config($config, $secureRandom, $groupManager, $userManager, $urlGenerator, $timeFactory, $dispatcher);

$config->setAppValue('spreed', 'signaling_token_alg', $algo);
// Make sure new keys are generated.
$config->deleteAppValue('spreed', 'signaling_token_privkey_' . strtolower($algo));
$config->deleteAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo));
$ticket = $helper->getSignalingTicket(Config::SIGNALING_TICKET_V2, null);
$this->assertNotNull($ticket);

$key = new Key($config->getAppValue('spreed', 'signaling_token_pubkey_' . strtolower($algo)), $algo);
$decoded = JWT::decode($ticket, $key);

$this->assertEquals($now, $decoded->iat);
$this->assertEquals('https://domain.invalid/nextcloud', $decoded->iss);
}
}

0 comments on commit 39715df

Please sign in to comment.