From 71e6a20b3cc946c019ffe16e2ac20f286b9f4ed8 Mon Sep 17 00:00:00 2001 From: John Nabil Date: Wed, 18 Jan 2023 16:47:39 +0200 Subject: [PATCH 1/9] fixing json field data with ciphersweet issue --- src/EncryptedRow.php | 92 ++++++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 38 deletions(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 4301dd7..1c7aeb1 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -1,5 +1,6 @@ engine = $engine; $this->tableName = $tableName; $this->typedIndexes = $useTypedIndexes; @@ -103,7 +105,8 @@ public function addField( string $fieldName, string $type = Constants::TYPE_TEXT, string $aadSource = '' - ): static { + ): static + { $this->fieldsToEncrypt[$fieldName] = $type; if ($aadSource) { $this->aadSourceField[$fieldName] = $aadSource; @@ -157,11 +160,12 @@ public function addIntegerField(string $fieldName, string $aadSource = ''): stat * @return static */ public function addJsonField( - string $fieldName, + string $fieldName, JsonFieldMap $fieldMap, - string $aadSource = '', - bool $strict = true - ): static { + string $aadSource = '', + bool $strict = true + ): static + { $this->jsonMaps[$fieldName] = $fieldMap; $this->jsonStrict[$fieldName] = $strict; return $this->addField($fieldName, Constants::TYPE_JSON, $aadSource); @@ -216,11 +220,12 @@ public function addCompoundIndex(CompoundIndex $index): static */ public function createCompoundIndex( string $name, - array $columns = [], - int $filterBits = 256, - bool $fastHash = false, - array $hashConfig = [] - ): CompoundIndex { + array $columns = [], + int $filterBits = 256, + bool $fastHash = false, + array $hashConfig = [] + ): CompoundIndex + { $index = new CompoundIndex( $name, $columns, @@ -247,8 +252,9 @@ public function createCompoundIndex( public function getBlindIndex( string $indexName, #[\SensitiveParameter] - array $row - ): string|array { + array $row + ): string|array + { foreach ($this->blindIndexes as $column => $blindIndexes) { if (isset($blindIndexes[$indexName])) { /** @var BlindIndex $blindIndex */ @@ -280,7 +286,8 @@ public function getBlindIndex( public function getAllBlindIndexes( #[\SensitiveParameter] array $row - ): array { + ): array + { /** @var array|string> $return */ $return = []; foreach ($this->blindIndexes as $column => $blindIndexes) { @@ -397,7 +404,8 @@ public function getJsonFieldMap(string $name): JsonFieldMap public function decryptRow( #[\SensitiveParameter] array $row - ): array { + ): array + { /** @var array $return */ $return = $row; $backend = $this->engine->getBackend(); @@ -422,10 +430,10 @@ public function decryptRow( } if ( !empty($this->aadSourceField[$field]) - && + && \array_key_exists($this->aadSourceField[$field], $row) ) { - $aad = (string) $row[$this->aadSourceField[$field]]; + $aad = (string)$row[$this->aadSourceField[$field]]; } else { $aad = ''; } @@ -464,7 +472,8 @@ public function decryptRow( public function encryptRow( #[\SensitiveParameter] array $row - ): array { + ): array + { /** @var array $return */ $return = $row; $backend = $this->engine->getBackend(); @@ -472,7 +481,7 @@ public function encryptRow( if (!\array_key_exists($field, $row)) { throw new ArrayKeyException( 'Expected value for column ' . - $field . + $field . ' on array, nothing given.' ); } @@ -482,14 +491,16 @@ public function encryptRow( ); if ( !empty($this->aadSourceField[$field]) - && + && \array_key_exists($this->aadSourceField[$field], $row) ) { - $aad = (string) $row[$this->aadSourceField[$field]]; + $aad = (string)$row[$this->aadSourceField[$field]]; } else { $aad = ''; } if ($type === Constants::TYPE_JSON && !empty($this->jsonMaps[$field])) { + //decode json field then to take key from it to encrypt it + $row[$field] = isset($row[$field]) ? (array)json_decode($row[$field]) : []; // JSON is a special case $jsonEncryptor = new EncryptedJsonField( $backend, @@ -532,7 +543,8 @@ public function encryptRow( public function prepareRowForStorage( #[\SensitiveParameter] array $row - ): array { + ): array + { return [ $this->encryptRow($row), $this->getAllBlindIndexes($row) @@ -576,10 +588,11 @@ public function setAadSourceField(string $fieldName, string $aadSource): static */ protected function calcBlindIndex( #[\SensitiveParameter] - array $row, - string $column, + array $row, + string $column, BlindIndex $index - ): string|array { + ): string|array + { $name = $index->getName(); $key = $this->engine->getBlindIndexRootKey( $this->tableName, @@ -626,9 +639,10 @@ protected function calcBlindIndex( */ protected function calcCompoundIndex( #[\SensitiveParameter] - array $row, + array $row, CompoundIndex $index - ): string|array { + ): string|array + { $name = $index->getName(); $key = $this->engine->getBlindIndexRootKey( $this->tableName, @@ -675,11 +689,12 @@ protected function calcCompoundIndex( */ protected function calcBlindIndexRaw( #[\SensitiveParameter] - array $row, - string $column, - BlindIndex $index, + array $row, + string $column, + BlindIndex $index, SymmetricKey $key = null - ): string { + ): string + { if (!$key) { $key = $this->engine->getBlindIndexRootKey( $this->tableName, @@ -734,17 +749,18 @@ protected function calcBlindIndexRaw( * @param CompoundIndex $index * @param SymmetricKey|null $key * @return string - * @internal - * * @throws \Exception * @throws Exception\CryptoOperationException + * @internal + * */ protected function calcCompoundIndexRaw( #[\SensitiveParameter] - array $row, + array $row, CompoundIndex $index, - SymmetricKey $key = null - ): string { + SymmetricKey $key = null + ): string + { if (!$key) { $key = $this->engine->getBlindIndexRootKey( $this->tableName, From edf9b368b3f9c4520494c1142f366f2e7496dbd6 Mon Sep 17 00:00:00 2001 From: John Nabil Date: Sun, 29 Jan 2023 12:00:35 +0200 Subject: [PATCH 2/9] adding higher level method to format json field and revert code style --- src/EncryptedRow.php | 107 +++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 1c7aeb1..6bce604 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -1,6 +1,5 @@ engine = $engine; $this->tableName = $tableName; $this->typedIndexes = $useTypedIndexes; @@ -105,8 +103,7 @@ public function addField( string $fieldName, string $type = Constants::TYPE_TEXT, string $aadSource = '' - ): static - { + ): static { $this->fieldsToEncrypt[$fieldName] = $type; if ($aadSource) { $this->aadSourceField[$fieldName] = $aadSource; @@ -160,12 +157,11 @@ public function addIntegerField(string $fieldName, string $aadSource = ''): stat * @return static */ public function addJsonField( - string $fieldName, + string $fieldName, JsonFieldMap $fieldMap, - string $aadSource = '', - bool $strict = true - ): static - { + string $aadSource = '', + bool $strict = true + ): static { $this->jsonMaps[$fieldName] = $fieldMap; $this->jsonStrict[$fieldName] = $strict; return $this->addField($fieldName, Constants::TYPE_JSON, $aadSource); @@ -220,12 +216,11 @@ public function addCompoundIndex(CompoundIndex $index): static */ public function createCompoundIndex( string $name, - array $columns = [], - int $filterBits = 256, - bool $fastHash = false, - array $hashConfig = [] - ): CompoundIndex - { + array $columns = [], + int $filterBits = 256, + bool $fastHash = false, + array $hashConfig = [] + ): CompoundIndex { $index = new CompoundIndex( $name, $columns, @@ -252,9 +247,8 @@ public function createCompoundIndex( public function getBlindIndex( string $indexName, #[\SensitiveParameter] - array $row - ): string|array - { + array $row + ): string|array { foreach ($this->blindIndexes as $column => $blindIndexes) { if (isset($blindIndexes[$indexName])) { /** @var BlindIndex $blindIndex */ @@ -286,8 +280,7 @@ public function getBlindIndex( public function getAllBlindIndexes( #[\SensitiveParameter] array $row - ): array - { + ): array { /** @var array|string> $return */ $return = []; foreach ($this->blindIndexes as $column => $blindIndexes) { @@ -404,8 +397,7 @@ public function getJsonFieldMap(string $name): JsonFieldMap public function decryptRow( #[\SensitiveParameter] array $row - ): array - { + ): array { /** @var array $return */ $return = $row; $backend = $this->engine->getBackend(); @@ -430,10 +422,10 @@ public function decryptRow( } if ( !empty($this->aadSourceField[$field]) - && + && \array_key_exists($this->aadSourceField[$field], $row) ) { - $aad = (string)$row[$this->aadSourceField[$field]]; + $aad = (string) $row[$this->aadSourceField[$field]]; } else { $aad = ''; } @@ -472,8 +464,7 @@ public function decryptRow( public function encryptRow( #[\SensitiveParameter] array $row - ): array - { + ): array { /** @var array $return */ $return = $row; $backend = $this->engine->getBackend(); @@ -481,7 +472,7 @@ public function encryptRow( if (!\array_key_exists($field, $row)) { throw new ArrayKeyException( 'Expected value for column ' . - $field . + $field . ' on array, nothing given.' ); } @@ -491,16 +482,15 @@ public function encryptRow( ); if ( !empty($this->aadSourceField[$field]) - && + && \array_key_exists($this->aadSourceField[$field], $row) ) { - $aad = (string)$row[$this->aadSourceField[$field]]; + $aad = (string) $row[$this->aadSourceField[$field]]; } else { $aad = ''; } if ($type === Constants::TYPE_JSON && !empty($this->jsonMaps[$field])) { - //decode json field then to take key from it to encrypt it - $row[$field] = isset($row[$field]) ? (array)json_decode($row[$field]) : []; + $row[$field] = $this->formatJson($row[$field]); // JSON is a special case $jsonEncryptor = new EncryptedJsonField( $backend, @@ -522,6 +512,20 @@ public function encryptRow( return $return; } + /** + * Decoding json field + * + * @param string|null $field + * @return array + */ + public function formatJson( + $field + ): array { + //decode json field then to take key from it to encrypt it + $field = isset($field) ? (array)json_decode($field) : []; + return $field; + } + /** * Process an entire row, which means: * @@ -543,8 +547,7 @@ public function encryptRow( public function prepareRowForStorage( #[\SensitiveParameter] array $row - ): array - { + ): array { return [ $this->encryptRow($row), $this->getAllBlindIndexes($row) @@ -588,11 +591,10 @@ public function setAadSourceField(string $fieldName, string $aadSource): static */ protected function calcBlindIndex( #[\SensitiveParameter] - array $row, - string $column, + array $row, + string $column, BlindIndex $index - ): string|array - { + ): string|array { $name = $index->getName(); $key = $this->engine->getBlindIndexRootKey( $this->tableName, @@ -639,10 +641,9 @@ protected function calcBlindIndex( */ protected function calcCompoundIndex( #[\SensitiveParameter] - array $row, + array $row, CompoundIndex $index - ): string|array - { + ): string|array { $name = $index->getName(); $key = $this->engine->getBlindIndexRootKey( $this->tableName, @@ -689,12 +690,11 @@ protected function calcCompoundIndex( */ protected function calcBlindIndexRaw( #[\SensitiveParameter] - array $row, - string $column, - BlindIndex $index, + array $row, + string $column, + BlindIndex $index, SymmetricKey $key = null - ): string - { + ): string { if (!$key) { $key = $this->engine->getBlindIndexRootKey( $this->tableName, @@ -749,18 +749,17 @@ protected function calcBlindIndexRaw( * @param CompoundIndex $index * @param SymmetricKey|null $key * @return string - * @throws \Exception - * @throws Exception\CryptoOperationException * @internal * + * @throws \Exception + * @throws Exception\CryptoOperationException */ protected function calcCompoundIndexRaw( #[\SensitiveParameter] - array $row, + array $row, CompoundIndex $index, - SymmetricKey $key = null - ): string - { + SymmetricKey $key = null + ): string { if (!$key) { $key = $this->engine->getBlindIndexRootKey( $this->tableName, From 9aefd575610f5d3d7615eae09d9b9882f27d1b02 Mon Sep 17 00:00:00 2001 From: John Nabil Date: Sun, 19 Feb 2023 12:53:04 +0200 Subject: [PATCH 3/9] fixing some issues with psalm --- src/EncryptedRow.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 6bce604..16b7888 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -422,7 +422,7 @@ public function decryptRow( } if ( !empty($this->aadSourceField[$field]) - && + && \array_key_exists($this->aadSourceField[$field], $row) ) { $aad = (string) $row[$this->aadSourceField[$field]]; @@ -472,7 +472,7 @@ public function encryptRow( if (!\array_key_exists($field, $row)) { throw new ArrayKeyException( 'Expected value for column ' . - $field . + $field . ' on array, nothing given.' ); } @@ -482,7 +482,7 @@ public function encryptRow( ); if ( !empty($this->aadSourceField[$field]) - && + && \array_key_exists($this->aadSourceField[$field], $row) ) { $aad = (string) $row[$this->aadSourceField[$field]]; From 1ca896f81cda1d005e1cea2d45d8725cca33c0b4 Mon Sep 17 00:00:00 2001 From: John Nabil Date: Tue, 14 Mar 2023 11:09:27 +0200 Subject: [PATCH 4/9] making format json toggable form encrypt row function --- src/EncryptedRow.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 16b7888..47ce796 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -454,6 +454,7 @@ public function decryptRow( * will be encrypted in-place in the returned array. * * @param array $row + * @param bool|false $decode_json * @return array * * @throws ArrayKeyException @@ -463,7 +464,8 @@ public function decryptRow( */ public function encryptRow( #[\SensitiveParameter] - array $row + array $row, + bool $decode_json = false, ): array { /** @var array $return */ $return = $row; @@ -490,7 +492,10 @@ public function encryptRow( $aad = ''; } if ($type === Constants::TYPE_JSON && !empty($this->jsonMaps[$field])) { - $row[$field] = $this->formatJson($row[$field]); + // checks decode json option + if ($decode_json) { + $row[$field] = $this->formatJson($row[$field]); + } // JSON is a special case $jsonEncryptor = new EncryptedJsonField( $backend, From 6727618dddccde77197088362c6d3f50ebfc1e93 Mon Sep 17 00:00:00 2001 From: John Nabil Date: Mon, 27 Mar 2023 15:09:34 +0200 Subject: [PATCH 5/9] fixing some fields issues and types caused by psalm --- src/EncryptedRow.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 47ce796..2504f7f 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -492,9 +492,10 @@ public function encryptRow( $aad = ''; } if ($type === Constants::TYPE_JSON && !empty($this->jsonMaps[$field])) { + $return[$field] = $row[$field]; // checks decode json option if ($decode_json) { - $row[$field] = $this->formatJson($row[$field]); + $return[$field] = $this->formatJson($row[$field]); } // JSON is a special case $jsonEncryptor = new EncryptedJsonField( @@ -504,7 +505,7 @@ public function encryptRow( $this->jsonStrict[$field] ); /** @psalm-suppress InvalidArgument */ - $return[$field] = $jsonEncryptor->encryptJson($row[$field], $aad); + $return[$field] = $jsonEncryptor->encryptJson($return[$field], $aad); continue; } $plaintext = $this->convertToString($row[$field], $type); @@ -520,14 +521,14 @@ public function encryptRow( /** * Decoding json field * - * @param string|null $field - * @return array + * @param array|mixed|null $field + * @return array */ public function formatJson( $field ): array { //decode json field then to take key from it to encrypt it - $field = isset($field) ? (array)json_decode($field) : []; + $field = isset($field) ? (is_string($field) ? (array)json_decode($field) : $field) : []; return $field; } From 41eaf384ad769a9e65c64824314a2f30ffe079fe Mon Sep 17 00:00:00 2001 From: John Nabil Date: Tue, 4 Apr 2023 12:58:43 +0200 Subject: [PATCH 6/9] merge master branch --- src/EncryptedRow.php | 79 ++++++++++++++++++++++++++++++++++++--- src/FastCompoundIndex.php | 38 +++++++++++++++++++ 2 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 src/FastCompoundIndex.php diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 2504f7f..2cd810c 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -14,6 +14,7 @@ }; use ParagonIE\ConstantTime\Hex; use SodiumException; +use TypeError; /** * Class EncryptedRow @@ -232,6 +233,34 @@ public function createCompoundIndex( return $index; } + /** + * Create a fast compound blind index then add it to this EncryptedRow object. + * + * @param string $name + * @param array $columns + * @param int $filterBits + * @param array $hashConfig + * @return CompoundIndex + * + * @throws CipherSweetException + */ + public function createFastCompoundIndex( + string $name, + array $columns = [], + int $filterBits = 256, + array $hashConfig = [] + ): CompoundIndex { + $index = new FastCompoundIndex( + $name, + $columns, + $filterBits, + true, + $hashConfig + ); + $this->addCompoundIndex($index); + return $index; + } + /** * Calculate a blind index (or compound blind index) output for this row. * @@ -425,7 +454,7 @@ public function decryptRow( && \array_key_exists($this->aadSourceField[$field], $row) ) { - $aad = (string) $row[$this->aadSourceField[$field]]; + $aad = $this->coaxAadToString($row[$this->aadSourceField[$field]]); } else { $aad = ''; } @@ -453,7 +482,7 @@ public function decryptRow( * If any columns are defined in this object to be encrypted, the value * will be encrypted in-place in the returned array. * - * @param array $row + * @param array $row * @param bool|false $decode_json * @return array * @@ -487,7 +516,7 @@ public function encryptRow( && \array_key_exists($this->aadSourceField[$field], $row) ) { - $aad = (string) $row[$this->aadSourceField[$field]]; + $aad = $this->coaxAadToString($row[$this->aadSourceField[$field]]); } else { $aad = ''; } @@ -504,10 +533,12 @@ public function encryptRow( $this->jsonMaps[$field], $this->jsonStrict[$field] ); - /** @psalm-suppress InvalidArgument */ - $return[$field] = $jsonEncryptor->encryptJson($return[$field], $aad); + $return[$field] = $jsonEncryptor->encryptJson($this->coaxToArray($return[$field]), $aad); continue; } + if (!is_scalar($row[$field])) { + throw new TypeError('Invalid type for ' . $field); + } $plaintext = $this->convertToString($row[$field], $type); $return[$field] = $backend->encrypt($plaintext, $key, $aad); } @@ -869,4 +900,42 @@ public function setTypedIndexes(bool $bool): static $this->typedIndexes = $bool; return $this; } + + /** + * @param mixed $input + * @return array + */ + protected function coaxToArray(mixed $input): array + { + if (is_array($input)) { + return $input; + } + if (is_null($input)) { + return []; + } + if (is_object($input)) { + /** psalm-suppress PossiblyInvalidCast */ + return (array) $input; + } + if (is_string($input)) { + return json_decode($input, true); + } + throw new TypeError("Cannot coax to array: " . gettype($input)); + } + + /** + * @param mixed $input + * @return string + */ + protected function coaxAadToString(mixed $input): string + { + if (is_string($input)) { + return $input; + } + if (is_numeric($input)) { + return '' . $input; + } + /** psalm-suppress PossiblyInvalidCast */ + return (string) $input; + } } diff --git a/src/FastCompoundIndex.php b/src/FastCompoundIndex.php new file mode 100644 index 0000000..5210a05 --- /dev/null +++ b/src/FastCompoundIndex.php @@ -0,0 +1,38 @@ + $columns + * @param int $filterBits + * @param bool $fastHash + * @param array $hashConfig + * + * @throws CipherSweetException + */ + public function __construct( + string $name, + array $columns = [], + int $filterBits = 256, + bool $fastHash = true, + array $hashConfig = [] + ) { + if (!$fastHash) { + throw new CipherSweetException("FastCompoundIndex cannot be turned slow"); + } + return parent::__construct( + $name, + $columns, + $filterBits, + $fastHash, + $hashConfig + ); + } +} From 1410880ab39713e4d3fb8c9645753c34aa61933b Mon Sep 17 00:00:00 2001 From: John Nabil Date: Tue, 4 Apr 2023 13:07:33 +0200 Subject: [PATCH 7/9] resolve conflicts --- src/EncryptedRow.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 2cd810c..327dc52 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -482,8 +482,8 @@ public function decryptRow( * If any columns are defined in this object to be encrypted, the value * will be encrypted in-place in the returned array. * - * @param array $row * @param bool|false $decode_json + * @param array $row * @return array * * @throws ArrayKeyException @@ -524,7 +524,7 @@ public function encryptRow( $return[$field] = $row[$field]; // checks decode json option if ($decode_json) { - $return[$field] = $this->formatJson($row[$field]); + $row[$field] = $this->formatJson($row[$field]); } // JSON is a special case $jsonEncryptor = new EncryptedJsonField( @@ -533,7 +533,7 @@ public function encryptRow( $this->jsonMaps[$field], $this->jsonStrict[$field] ); - $return[$field] = $jsonEncryptor->encryptJson($this->coaxToArray($return[$field]), $aad); + $return[$field] = $jsonEncryptor->encryptJson($this->coaxToArray($row[$field]), $aad); continue; } if (!is_scalar($row[$field])) { From 6f1f09294b5c4ba3e69b424962d61e60c6a487db Mon Sep 17 00:00:00 2001 From: John Nabil Date: Tue, 4 Apr 2023 13:10:34 +0200 Subject: [PATCH 8/9] resolve conflicts --- src/EncryptedRow.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 327dc52..61b3db3 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -482,7 +482,7 @@ public function decryptRow( * If any columns are defined in this object to be encrypted, the value * will be encrypted in-place in the returned array. * - * @param bool|false $decode_json + * * @param array $row * @return array * From 94123513981bcdf39828c078dabd1b3cadeac159 Mon Sep 17 00:00:00 2001 From: John Nabil Date: Tue, 4 Apr 2023 13:11:04 +0200 Subject: [PATCH 9/9] resolve conflicts --- src/EncryptedRow.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/EncryptedRow.php b/src/EncryptedRow.php index 61b3db3..8b087ab 100644 --- a/src/EncryptedRow.php +++ b/src/EncryptedRow.php @@ -482,7 +482,6 @@ public function decryptRow( * If any columns are defined in this object to be encrypted, the value * will be encrypted in-place in the returned array. * - * * @param array $row * @return array *