Skip to content

Commit

Permalink
rules bugs fixes, oauth, jwt, authentications and protections
Browse files Browse the repository at this point in the history
  • Loading branch information
steevenz committed Jul 19, 2018
1 parent 58aa4ce commit c694f7d
Show file tree
Hide file tree
Showing 22 changed files with 1,028 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@

// ------------------------------------------------------------------------

namespace O2System\Security\Protections;
namespace O2System\Security\Authentication;

// ------------------------------------------------------------------------

/**
* Class HttpAuthentication
*
* @package O2System\Security\Protections
* @package O2System\Security\Authentication
*/
class HttpAuthentication
class Http
{
/**
* HttpAuthentication::AUTH_BASIC
Expand Down
93 changes: 93 additions & 0 deletions src/Authentication/Jwt/Signature.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php
/**
* This file is part of the O2System PHP Framework package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Steeve Andrian Salim
* @copyright Copyright (c) Steeve Andrian Salim
*/

// ------------------------------------------------------------------------

namespace O2System\Security\Authentication\Jwt;

// ------------------------------------------------------------------------
use O2System\Spl\Exceptions\Logic\DomainException;

/**
* Class Signature
* @package O2System\Security\Generators
*/
class Signature
{
public static $supportedAlgorithms = [
'HS256' => ['hash_hmac', 'sha256'],
'HS512' => ['hash_hmac', 'sha512'],
'HS384' => ['hash_hmac', 'sha384'],
'RS256' => ['openssl', OPENSSL_ALGO_SHA256],
'RS384' => ['openssl', OPENSSL_ALGO_SHA384],
'RS512' => ['openssl', OPENSSL_ALGO_SHA512],
];

public static function validAlgorithm($algorithm)
{
$algorithm = strtoupper($algorithm);

return (bool)array_key_exists($algorithm, static::$supportedAlgorithms);
}

public static function generate(array $segments, $key, $algorithm = 'HS256')
{
if (count($segments) == 2) {
$data = implode('.', $segments);

if (static::validAlgorithm($algorithm)) {
list($function, $algorithm) = static::$supportedAlgorithms[ $algorithm ];

switch ($function) {
case 'hash_hmac':
return hash_hmac($algorithm, $data, $key, true);
case 'openssl':
if (false === ($success = openssl_sign($data, $signature, $key, $algorithm))) {
throw new DomainException("OpenSSL unable to sign data");
}
}
}
}

return false;
}

public static function verify($token, $signature, $key, $algorithm = 'HS256')
{
$segments = explode('.', $token);
$segments = array_map('trim', $segments);

if (count($segments) == 3) {
array_pop($segments);
$data = implode('.', $segments);

if (static::validAlgorithm($algorithm)) {
list($function, $algorithm) = static::$supportedAlgorithms[ $algorithm ];

switch ($function) {
case 'hash_hmac':
return hash_hmac($algorithm, $data, $key, true) === $signature;
case 'openssl':
switch ($algorithm) {
case 'RS256':
return (bool)openssl_verify($data, $signature, $key, OPENSSL_ALGO_SHA256);
case 'RS384':
return (bool)openssl_verify($data, $signature, $key, OPENSSL_ALGO_SHA384);
case 'RS512':
return (bool)openssl_verify($data, $signature, $key, OPENSSL_ALGO_SHA512);
}
}
}
}

return false;
}
}
242 changes: 242 additions & 0 deletions src/Authentication/Jwt/Token.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
<?php
/**
* This file is part of the O2System PHP Framework package.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Steeve Andrian Salim
* @copyright Copyright (c) Steeve Andrian Salim
*/

// ------------------------------------------------------------------------

namespace O2System\Security\Authentication\Jwt;

// ------------------------------------------------------------------------

use O2System\Security\Encoders\Base64;
use O2System\Security\Encoders\Json;
use O2System\Spl\Traits\Collectors\ErrorCollectorTrait;

/**
* Class Token
* @package O2System\Security\Authentication\Jwt
*/
class Token
{
use ErrorCollectorTrait;

protected $key = null;
protected $keyId;

/**
* When checking nbf, iat or expiration times,
* we want to provide some extra leeway time to
* account for clock skew.
*/
protected $leeway = 0;

/**
* Allow the current timestamp to be specified.
* Useful for fixing a value within unit testing.
*
* Will default to PHP time() value if null.
*/
protected $timestamp;
protected $algorithm = 'HS256';
protected $headers = [
'typ' => 'JWT',
];

// ------------------------------------------------------------------------

public function setKey($key)
{
$this->key = $key;

return $this;
}

// ------------------------------------------------------------------------

public function setKeyId($keyId)
{
$this->keyId = $keyId;

return $this;
}

// ------------------------------------------------------------------------

public function setAlgorithm($algorithm)
{
$algorithm = strtoupper($algorithm);

if (Signature::validAlgorithm($algorithm)) {
$this->algorithm = $algorithm;
}

return $this;
}

// ------------------------------------------------------------------------

public function setLeeway($leeway)
{
$this->leeway = intval($leeway);

return $this;
}

// ------------------------------------------------------------------------

public function setTimestamp($timestamp)
{
$this->timestamp = is_numeric($timestamp) ? $timestamp : strtotime($timestamp);

return $this;
}

// ------------------------------------------------------------------------

public function addHeader($key, $value)
{
$this->headers[ $key ] = $value;
}

// ------------------------------------------------------------------------

public function encode(array $payload, $key = null)
{
$key = empty($key) ? $this->key : $key;
if (is_null($key)) {
if (class_exists('O2System\Framework', false)) {
$key = config()->getItem('security')->encryptionKey;
}
}

$this->addHeader('alg', $this->algorithm);

if ( ! empty($this->keyId)) {
$this->addHeader('kid', $this->keyId);
}

// Create Header Segment
$segments[] = Base64::encode(Json::encode($this->headers));

// Create Payload Segment
$segments[] = Base64::encode(Json::encode($payload));

// Create Signature Segment
$segments[] = Base64::encode(Signature::generate($segments, $key, $this->algorithm));

return implode('.', $segments);
}

// ------------------------------------------------------------------------

public function decode($token, $key = null)
{
$key = empty($key) ? $this->key : $key;
if (is_null($key)) {
if (class_exists('O2System\Framework', false)) {
$key = config()->getItem('security')->encryptionKey;
}
}

$timestamp = empty($this->timestamp) ? time() : $this->timestamp;

$segments = explode('.', $token);
$segments = array_map('trim', $segments);

if (count($segments) == 3) {
list($headers, $payload, $signature) = $segments;

// Base64 decode headers
if(false === ($headers = Base64::decode($headers))) {
$this->errors[] = 'Invalid header base64 decoding';
return false;
}

// Json decode headers
if (null === ($headers = Json::decode($headers))) {
$this->errors[] = 'Invalid header json decoding';
return false;
}

// Validate algorithm header
if (empty($headers->alg)) {
$this->errors[] = 'Invalid algorithm';

return false;
} elseif ( ! Signature::validAlgorithm($headers->alg)) {
$this->errors[] = 'Unsupported algorithm';

return false;
}

// Validate algorithm key id
if (is_array($key) or $key instanceof \ArrayAccess) {
if (isset($headers->kid)) {
if ( ! isset($key[ $headers->kid ])) {
$this->errors[] = 'Invalid Key Id';
return false;
}

$key = $key[ $headers->kid ];
} else {
$this->errors[] = 'Empty Key id';
return false;
}
}

// Base64 decode payload
if(false === ($payload = Base64::decode($payload))) {
$this->errors[] = 'Invalid payload base64 decoding';
return false;
}

// Json decode payload
if (null === ($payload = Json::decode($payload))) {
$this->errors[] = 'Invalid payload json decoding';
return false;
}

// Base64 decode payload
if(false === ($signature = Base64::decode($signature))) {
$this->errors[] = 'Invalid signature base64 decoding';
return false;
}

if(Signature::verify($token, $signature, $key, $headers->alg) === false) {
$this->errors[] = 'Invalid signature';
return false;
}

// Check if the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + $this->leeway)) {
$this->errors[] = 'Cannot handle token prior to ' . date(\DateTime::ISO8601, $payload->nbf);
return false;
}

// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + $this->leeway)) {
$this->errors[] = 'Cannot handle token prior to ' . date(\DateTime::ISO8601, $payload->iat);
return false;
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - $this->leeway) >= $payload->exp) {
$this->errors[] = 'Expired token';
return false;
}

return $payload;
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

// ------------------------------------------------------------------------

namespace O2System\Security\Protections\Oauth;
namespace O2System\Security\Authentication\Oauth;

// ------------------------------------------------------------------------

/**
* Class Consumer
* @package O2System\Security\Protections\Oauth
* @package O2System\Security\Authentication\Oauth
*/
class Consumer
{
Expand Down Expand Up @@ -133,11 +133,11 @@ public function getAuthorizationHeader($httpUrl = null, $httpMethod = 'GET')
$signatureMethod = OAUTH_SIG_METHOD_HMACSHA1;

$parameters = [
'oauth_nonce' => Oauth::generateNonce(),
'oauth_nonce' => Nonce::generate($signatureMethod),
'oauth_signature_method' => $signatureMethod,
'oauth_timestamp' => time(),
'oauth_consumer_key' => $this->key,
'oauth_version' => Oauth::VERSION,
'oauth_version' => '1.0',
];

if (isset($httpUrl)) {
Expand All @@ -152,6 +152,7 @@ public function getAuthorizationHeader($httpUrl = null, $httpMethod = 'GET')
$parameters[ 'callback' ] = $this->callbackUrl;
}

$parts = [];
foreach ($parameters as $key => $value) {
$parts[] = $key . '="' . $value . '"';
}
Expand Down
Loading

0 comments on commit c694f7d

Please sign in to comment.