Skip to content

Commit

Permalink
Passwordless authentication with WebAuthn
Browse files Browse the repository at this point in the history
Credentials encryption
  • Loading branch information
Nicholas K. Dionysopoulos committed Jul 25, 2019
1 parent 7d6f519 commit d081d0b
Showing 1 changed file with 77 additions and 7 deletions.
84 changes: 77 additions & 7 deletions plugins/system/webauthn/Webauthn/CredentialRepository.php
Expand Up @@ -12,6 +12,7 @@
use Exception;
use InvalidArgumentException;
use Joomla\CMS\Application\CMSApplication;
use Joomla\CMS\Encrypt\Aes;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\User\UserFactoryInterface;
Expand Down Expand Up @@ -54,13 +55,15 @@ public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKey
->where($db->qn('id') . ' = :credentialId')
->bind(':credentialId', $credentialId);

$json = $db->setQuery($query)->loadResult();
$encrypted = $db->setQuery($query)->loadResult();

if (empty($json))
if (empty($encrypted))
{
return null;
}

$json = $this->decryptCredential($encrypted);

try
{
return PublicKeyCredentialSource::createFromArray(json_decode($json, true));
Expand Down Expand Up @@ -102,7 +105,8 @@ public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCre
$records = array_map(function ($record) {
try
{
$data = json_decode($record['credential'], true);
$json = $this->decryptCredential($record['credential']);
$data = json_decode($json, true);
}
catch (JsonException $e)
{
Expand Down Expand Up @@ -186,6 +190,8 @@ public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredent
{
}

$o->credential = $this->encryptCredential($o->credential);

if ($update)
{
$db->updateObject('#__webauthn_credentials', $o, ['id']);
Expand Down Expand Up @@ -364,7 +370,73 @@ public function getUserHandleFor(string $credentialId): string
*/
public function getHandleFromUserId(int $id): string
{
/** @var CMSApplication $app */
$key = $this->getEncryptionKey();
$data = sprintf('%010u', $id);

return hash_hmac('sha256', $data, $key, false);
}

/**
* Encrypt the credential source before saving it to the database
*
* @param string $credential The unencrypted, JSON-encoded credential source
*
* @return string The encrypted credential source, base64 encoded
*
* @since 4.0.0
*/
private function encryptCredential(string $credential): string
{
$key = $this->getEncryptionKey();

if (empty($key))
{
return $credential;
}

$aes = new Aes($key, 256);

return $aes->encryptString($credential);
}

/**
* Decrypt the credential source if it was already encrypted in the database
*
* @param string $credential The encrypted credential source, base64 encoded
*
* @return string The decrypted, JSON-encoded credential source
*
* @since 4.0.0
*/
private function decryptCredential(string $credential): string
{
$key = $this->getEncryptionKey();

if (empty($key))
{
return $credential;
}

// Was the credential stored unencrypted (e.g. the site's secret was empty)?
if ((strpos($credential, '{') !== false) && (strpos($credential, '"publicKeyCredentialId"') !== false))
{
return $credential;
}

$aes = new Aes($key, 256);

return $aes->decryptString($credential);
}

/**
* Get the site's secret, used as an encryption key
*
* @return string
*
* @since 4.0.0
*/
private function getEncryptionKey(): string
{
try
{
$app = Factory::getApplication();
Expand All @@ -375,8 +447,6 @@ public function getHandleFromUserId(int $id): string
$secret = '';
}

$data = sprintf('%010u', $id);

return hash_hmac('sha256', $data, $secret, false);
return $secret;
}
}

0 comments on commit d081d0b

Please sign in to comment.