Skip to content
This repository has been archived by the owner on Jan 29, 2019. It is now read-only.

Commit

Permalink
Merge pull request #12 from salesupply/feature/add-hashing-support
Browse files Browse the repository at this point in the history
Feature/add hashing support
  • Loading branch information
rkeet-salesupply authored Apr 16, 2018
2 parents 04e37a4 + 3b6ecfe commit 3ef95fa
Show file tree
Hide file tree
Showing 13 changed files with 724 additions and 150 deletions.
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ If these are filled in, it works out of the box using [Halite](https://github.co
However, must be said, at the moment of writing this ReadMe, the Halite module contains duplicate `const` declarations,
as such, you must disable your `E_NOTICE` warnings in your PHP config :(

# Usage example
# Examples

### Encryption

Simple, consider that you have an `Address` Entity, which under upcoming [EU GDPR regulation](https://www.eugdpr.org/)
requires parts of the address, such as the street, to be encrypted. This uses the key & salt required for the config
Expand All @@ -52,20 +54,32 @@ To encrypt a street name, add `@Encrypted` like so:
*/
protected $street;

If you need make sure that encryption is done in a unique way, such as for passwords or keys, which need decryption
(e.g. in the case that you need to create a connection string for an external API), you can provide some options.
Options provided are `spices`, `salt` and `pepper`. These must point to a property of the Entity you're encrypting, from
which then either the Salt or the Pepper or both of them are gotten and used.
By default the Encryption service assumes that the data to be encrypted is of the type `string`. However, you could have
a requirement to encrypt another type of data, such as a house number. Non-string types are supported, but the type of data
must be provided if not a string. You can do this like so:

/**
* @var string
* @ORM\Column(name="street", type="string", length=255, nullable=true)
* @Encrypted(spices="encryption")
* @var int
* @ORM\Column(name="house_number", type="integer", length=20, nullable=false)
* @Encrypted(type="int")
*/
protected $street;
protected $houseNumber;

The above example expects a property `relation` to be present. The value is given for the option `spices`, as such,
the returned Entity when using `getEncryption` must implement the `SpicyInterface`.
Supported types are [found here](http://php.net/settype).

### Hashing

Say you'd like to store a password, it should work in much of the same way as the above. However, it is data that should
not be de-cryptable (and there's no need for it to ever be decrypted), thus you should hash it instead.

**NOTE**: The option `spices` *may not be used* in conjunction with either `salt` or `pepper`. If you're *not* using
`spices`, you may use both `salt` and `pepper`.
To hash something, like a password, add the `@Hashed` Annotation. See the example below.

/**
* @var string
* @ORM\Column(name="password", type="text", nullable=false)
* @Hashed
*/
protected $password;

**Note** that, unlike `@Encrypted`, there aren't options to give a type. As we can't decrypt the data (it's one-way),
there's no need to know what the original type was. The response will always be string value.
15 changes: 14 additions & 1 deletion config/global.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@

use Doctrine\Common\Annotations\AnnotationReader;
use ZfDoctrineEncryptModule\Adapter\HaliteEncryptionAdapter;
use ZfDoctrineEncryptModule\Adapter\HaliteHashingAdapter;
use ZfDoctrineEncryptModule\Factory\HaliteEncryptionAdapterFactory;
use ZfDoctrineEncryptModule\Factory\HaliteHashingAdapterFactory;
use ZfDoctrineEncryptModule\Factory\ZfDoctrineEncryptedServiceFactory;
use ZfDoctrineEncryptModule\Factory\ZfDoctrineHashedServiceFactory;

return [
'doctrine_factories' => [
'encryption' => ZfDoctrineEncryptedServiceFactory::class,
'hashing' => ZfDoctrineHashedServiceFactory::class,
],
'doctrine' => [
'encryption' => [
Expand All @@ -18,21 +22,30 @@
'reader' => AnnotationReader::class,
],
],
'hashing' => [
'orm_default' => [
'adapter' => 'hashing_adapter',
'reader' => AnnotationReader::class,
],
],
'eventmanager' => [
'orm_default' => [
'subscribers' => [
'doctrine.encryption.orm_default',
'doctrine.hashing.orm_default',
],
],
],
],
'service_manager' => [
'aliases' => [
'encryption_adapter' => HaliteEncryptionAdapter::class,
'hashing_adapter' => HaliteHashingAdapter::class,
],
'factories' => [
// Using aliases so someone else can use own adapter/factory
HaliteEncryptionAdapter::class => HaliteEncryptionAdapterFactory::class
HaliteEncryptionAdapter::class => HaliteEncryptionAdapterFactory::class,
HaliteHashingAdapter::class => HaliteHashingAdapterFactory::class,
],
],
];
6 changes: 6 additions & 0 deletions config/local.config.php.dist
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,11 @@ return [
'key' => '', // Must be 32 characters - Halite requirement
],
],
'hashing' => [
'orm_default' => [
'pepper' => '', // Must be 32 characters - Halite requirement
'key' => '', // Must be 32 characters - Halite requirement
],
],
],
];
105 changes: 0 additions & 105 deletions src/Adapter/HaliteAdapter.php

This file was deleted.

99 changes: 99 additions & 0 deletions src/Adapter/HaliteHashingAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace ZfDoctrineEncryptModule\Adapter;

use ParagonIE\ConstantTime\Binary;
use ParagonIE\Halite\Alerts\InvalidKey;
use ParagonIE\Halite\HiddenString;
use ParagonIE\Halite\Password;
use ParagonIE\Halite\Symmetric\EncryptionKey;
use ZfDoctrineEncryptModule\Interfaces\HashInterface;

class HaliteHashingAdapter implements HashInterface
{
/**
* @var EncryptionKey
*/
private $key;

/**
* @var string
*/
private $pepper;

/**
* HaliteAdapter constructor.
* @param $key
* @throws InvalidKey
* @throws \TypeError
*/
public function __construct($key, $pepper)
{
if (Binary::safeStrlen($key) !== \Sodium\CRYPTO_STREAM_KEYBYTES) {

throw new InvalidKey(
'Encryption key used for ' . __CLASS__ . '::' . __FUNCTION__ . ' must be exactly ' . \Sodium\CRYPTO_STREAM_KEYBYTES . ' characters long'
);
}

if (Binary::safeStrlen($pepper) !== \Sodium\CRYPTO_STREAM_KEYBYTES) {

throw new InvalidKey(
'Encryption pepper used for ' . __CLASS__ . '::' . __FUNCTION__ . ' must be exactly ' . \Sodium\CRYPTO_STREAM_KEYBYTES . ' characters long'
);
}

$this->setKey((new EncryptionKey((new HiddenString($key)))));
$this->setPepper($pepper);
}

/**
* @param string $data
* @return string
* @throws \ParagonIE\Halite\Alerts\CannotPerformOperation
* @throws \ParagonIE\Halite\Alerts\InvalidDigestLength
* @throws \ParagonIE\Halite\Alerts\InvalidMessage
* @throws \ParagonIE\Halite\Alerts\InvalidType
*/
public function hash(string $data): string
{
return Password::hash(new HiddenString($data . $this->getPepper()), $this->getKey());
}

/**
* @return EncryptionKey
*/
public function getKey(): EncryptionKey
{
return $this->key;
}

/**
* @param EncryptionKey $key
* @return HaliteHashingAdapter
*/
public function setKey(EncryptionKey $key): HaliteHashingAdapter
{
$this->key = $key;
return $this;
}

/**
* @return string
*/
public function getPepper(): string
{
return $this->pepper;
}

/**
* @param string $pepper
* @return HaliteHashingAdapter
*/
public function setPepper(string $pepper): HaliteHashingAdapter
{
$this->pepper = $pepper;
return $this;
}

}
37 changes: 37 additions & 0 deletions src/Annotation/Hashed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace ZfDoctrineEncryptModule\Annotation;

use Doctrine\Common\Annotations\Annotation\Target;

/**
* The below register the class as to be used as Doctrine's Annotation and only on properties.
*
* @Annotation
* @Target("PROPERTY")
*/
class Hashed
{
/**
* @var string linked property which implements \ZfDoctrineEncryptModule\Interfaces\SaltInterface
*/
public $salt;

/**
* @return null|string
*/
public function getSalt(): ?string
{
return $this->salt;
}

/**
* @param null|string $salt
* @return Hashed
*/
public function setSalt(?string $salt): Hashed
{
$this->salt = $salt;
return $this;
}
}
Loading

0 comments on commit 3ef95fa

Please sign in to comment.