Skip to content
This repository has been archived by the owner on Dec 30, 2020. It is now read-only.

Commit

Permalink
Storable JWKSet (#126)
Browse files Browse the repository at this point in the history
* Storable and Rotatable JWKSet added
* Tests added
* Documentation updated
  • Loading branch information
Spomky authored Sep 22, 2016
1 parent fa19b85 commit 4d719b7
Show file tree
Hide file tree
Showing 19 changed files with 704 additions and 55 deletions.
14 changes: 8 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
language: php

sudo: false
sudo: true

matrix:
fast_finish: true
Expand All @@ -22,15 +22,14 @@ matrix:
before_script:
- composer self-update
- sh -c 'if [ "$WITH_CRYPTO" != "" ]; then pecl install crypto-0.2.2; fi;'
- mkdir -p build/logs
- chmod +x tests/ci/install_php_ext.sh
- if [ "$TRAVIS_PHP_VERSION" != 'hhvm' ]; then ./tests/ci/install_php_ext.sh; fi
- if [[ $deps = low ]]; then composer update --no-interaction --prefer-lowest ; fi
- if [[ !$deps ]]; then composer install --no-interaction ; fi
- mkdir -p build/logs

script:
- composer test-with-coverage

after_script:
- vendor/bin/coveralls --no-interaction
- ./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
- php ./examples/Encrypt1.php
- php ./examples/Encrypt2.php
- php ./examples/Signature1.php
Expand All @@ -39,3 +38,6 @@ after_script:
- php ./examples/Load2.php
- php ./examples/Load3.php
- php ./examples/Load4.php

after_success:
- vendor/bin/coveralls --no-interaction
6 changes: 1 addition & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,9 @@
"ext-ed25519": "For EdDSA with Ed25519 curves support.",
"ext-curve25519": "For EdDSA with X25519 curves support."
},
"scripts":{
"test": "phpunit",
"test-with-coverage": "phpunit --verbose --coverage-clover build/logs/clover.xml"
},
"extra": {
"branch-alias": {
"dev-master": "5.1.x-dev"
"dev-master": "5.2.x-dev"
}
}
}
32 changes: 32 additions & 0 deletions doc/object/jwk.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,35 @@ $rotatable_key = JWKFactory::createRotatableKey(

The key can be used like any other keys. After 3600 seconds, the values of that key will be updated.
If the key exists in the storage and is not expired then it is loaded.


## Create a Rotatable Key Set

Some applications may require a key set with keys that are updated after a period of time.
To continue to validate JWS or decrypt JWE, the old keys should be able for another period of time.

That is the purpose of the Rotatable Key Set.

You have to define which type of key you want to have (onlly one type per JWKSet allowed), how many keys in the key set and a period of time.
Keys are automatically created and rotation is performed after the period of time.

You can manipulate that key set as any other key sets, however we recommend you to never add or remove keys. All changes will be erased we keys are rotated.
We also recommend you to use the first key of that key set to perform your signature/encryption operations.

Except when the key set is created, all keys will be available at least during `number of key * period of time`.

```php
use Jose\Factory\JWKFactory;

$rotatable_key_set = JWKFactory::createRotatableKeySet(
'/path/to/the/storage/file.keyset', // The file which will contain the key set
[
'kty' => 'OKP',
'crv' => 'X25519',
'alg' => 'ECDH-ES',
'use' => 'enc',
],
3, // Number of keys in that key set
3600 // This key set will rotate all keys after 3600 seconds (1 hour)
);
```
2 changes: 1 addition & 1 deletion src/Algorithm/ContentEncryption/AESCBCHS.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,6 @@ public function getIVSize()
*/
private function getMode($k)
{
return 'aes-'.(8 * mb_strlen($k, '8bit')).'-cbc';
return 'aes-'.(8 * mb_strlen($k, '8bit')).'-cbc';
}
}
2 changes: 1 addition & 1 deletion src/Algorithm/ContentEncryption/AESGCM.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public function decryptContent($data, $cek, $iv, $aad, $encoded_protected_header
*/
private function getMode($k)
{
return 'aes-'.(8 * mb_strlen($k, '8bit')).'-gcm';
return 'aes-'.(8 * mb_strlen($k, '8bit')).'-gcm';
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Algorithm/KeyEncryption/AESGCMKW.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public function unwrapKey(JWKInterface $key, $encrypted_cek, array $header)
*/
private function getMode($k)
{
return 'aes-'.(8 * mb_strlen($k, '8bit')).'-gcm';
return 'aes-'.(8 * mb_strlen($k, '8bit')).'-gcm';
}

/**
Expand Down
35 changes: 32 additions & 3 deletions src/Factory/JWKFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
use Jose\KeyConverter\RSAKey;
use Jose\Object\JWK;
use Jose\Object\JWKSet;
use Jose\Object\RotatableJWK;
use Jose\Object\RotatableJWKSet;
use Jose\Object\StorableJWK;
use Jose\Object\StorableJWKSet;
use Mdanter\Ecc\Curves\CurveFactory;
use Mdanter\Ecc\Curves\NistCurve;
use Mdanter\Ecc\EccFactory;
Expand All @@ -33,6 +36,30 @@ public static function createStorableKey($filename, array $parameters)
return new StorableJWK($filename, $parameters);
}

/**
* {@inheritdoc}
*/
public static function createRotatableKey($filename, array $parameters, $ttl)
{
return new RotatableJWK($filename, $parameters, $ttl);
}

/**
* {@inheritdoc}
*/
public static function createRotatableKeySet($filename, array $parameters, $nb_keys, $ttl)
{
return new RotatableJWKSet($filename, $parameters, $nb_keys, $ttl);
}

/**
* {@inheritdoc}
*/
public static function createStorableKeySet($filename, array $parameters, $nb_keys)
{
return new StorableJWKSet($filename, $parameters, $nb_keys);
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -318,17 +345,19 @@ public static function createFromX5U($x5u, $allow_unsecured_connection = false,
* @param string $url
* @param bool $allow_unsecured_connection
* @param \Psr\Cache\CacheItemPoolInterface|null $cache
* @param int $ttl
*
* @return array
*/
private static function getContent($url, $allow_unsecured_connection, CacheItemPoolInterface $cache = null)
private static function getContent($url, $allow_unsecured_connection, CacheItemPoolInterface $cache = null, $ttl = 300)
{
$cache_key = sprintf('%s-%s', 'JWKFactory-Content', hash('sha512', $url));
$cache_key = sprintf('JWKFactory-Content-%s', hash('sha512', $url));
if (null !== $cache) {
$item = $cache->getItem($cache_key);
if (!$item->isHit()) {
$content = self::downloadContent($url, $allow_unsecured_connection);
$item->set($content);
$item->expiresAfter($ttl);
$cache->save($item);

return $content;
Expand Down Expand Up @@ -367,7 +396,7 @@ private static function downloadContent($url, $allow_unsecured_connection)
'Invalid URL.'
);
Assertion::false(
false === $allow_unsecured_connection && 'https://' !== mb_substr($url, 0, 8, '8bit'),
false === $allow_unsecured_connection && 'https://' !== mb_substr($url, 0, 8, '8bit'),
'Unsecured connection.'
);

Expand Down
34 changes: 34 additions & 0 deletions src/Factory/JWKFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,40 @@

interface JWKFactoryInterface
{
/**
* RotatableJWK constructor.
*
* @param string $filename
* @param array $parameters
* @param int $nb_keys
*
* @return \Jose\Object\JWKSetInterface
*/
public static function createStorableKeySet($filename, array $parameters, $nb_keys);

/**
* RotatableJWK constructor.
*
* @param string $filename
* @param array $parameters
* @param int $nb_keys
* @param int $ttl
*
* @return \Jose\Object\JWKSetInterface
*/
public static function createRotatableKeySet($filename, array $parameters, $nb_keys, $ttl);

/**
* RotatableJWK constructor.
*
* @param string $filename
* @param array $parameters
* @param int $ttl
*
* @return \Jose\Object\JWKInterface
*/
public static function createRotatableKey($filename, array $parameters, $ttl);

/**
* RotatableJWK constructor.
*
Expand Down
48 changes: 48 additions & 0 deletions src/Object/RotatableJWK.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

namespace Jose\Object;

use Assert\Assertion;

/**
* Class RotatableJWK.
*/
final class RotatableJWK extends StorableJWK implements RotatableJWKInterface
{
/**
* @var int
*/
protected $ttl;

public function __construct($filename, array $parameters, $ttl)
{
Assertion::integer($ttl);
Assertion::greaterThan($ttl, 0, 'The parameter TTL must be at least 0.');
$this->ttl = $ttl;
parent::__construct($filename, $parameters);
}

/**
* @return \Jose\Object\JWKInterface
*/
protected function getJWK()
{
if (file_exists($this->getFilename())) {
$mtime = filemtime($this->getFilename());
if ($mtime + $this->ttl <= time()) {
unlink($this->getFilename());
}
}

return parent::getJWK();
}
}
19 changes: 19 additions & 0 deletions src/Object/RotatableJWKInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

namespace Jose\Object;

/**
* Interface RotatableJWKInterface.
*/
interface RotatableJWKInterface extends StorableJWKInterface
{
}
63 changes: 63 additions & 0 deletions src/Object/RotatableJWKSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

namespace Jose\Object;

use Assert\Assertion;

/**
* Class RotatableJWKSet.
*/
final class RotatableJWKSet extends StorableJWKSet implements RotatableJWKSetInterface
{
/**
* @var int
*/
protected $ttl;

/**
* RotatableJWKSet constructor.
*
* @param string $filename
* @param array $parameters
* @param int $nb_keys
* @param int $ttl
*/
public function __construct($filename, array $parameters, $nb_keys, $ttl)
{
Assertion::integer($ttl);
Assertion::greaterThan($ttl, 0, 'The parameter TTL must be at least 0.');
$this->ttl = $ttl;
parent::__construct($filename, $parameters, $nb_keys);
}

/**
* @return \Jose\Object\JWKSetInterface
*/
protected function getJWKSet()
{
$mtime = $this->getLastModificationTime();
if (null !== $mtime) {
if ($mtime + $this->ttl <= time()) {
$keys = $this->jwkset->getKeys();
unset($keys[count($keys) - 1]);
$this->jwkset = new JWKSet();
$this->jwkset->addKey($this->createJWK());
foreach ($keys as $key) {
$this->jwkset->addKey($key);
}
$this->save();
}
}

return parent::getJWKSet();
}
}
19 changes: 19 additions & 0 deletions src/Object/RotatableJWKSetInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2016 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

namespace Jose\Object;

/**
* Interface RotatableJWKSetInterface.
*/
interface RotatableJWKSetInterface extends StorableJWKSetInterface
{
}
Loading

0 comments on commit 4d719b7

Please sign in to comment.