Code Monkey home page Code Monkey logo

ciphersweet's People

Contributors

kellerfuchs avatar lekoala avatar luketlancaster avatar mcordingley avatar paragonie-scott avatar paragonie-security avatar philetaylor avatar simzal avatar thisispiers avatar veloxy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ciphersweet's Issues

Fatal error when using PHP 8.0

I get the following error when I update my server to PHP 8.0:

PHP Fatal error:  Uncaught Error: Undefined constant "MB_OVERLOAD_STRING" in /home/user/vendor/paragonie/sodium_compat/src/Core/Util.php:915
Stack trace:
#0 /home/user/vendor/paragonie/sodium_compat/src/Core/Util.php(757): ParagonIE_Sodium_Core_Util::isMbStringOverride()
#1 /home/user/vendor/paragonie/sodium_compat/src/Compat.php(893): ParagonIE_Sodium_Core_Util::strlen()
#2 /home/user/vendor/paragonie/ciphersweet/src/Backend/ModernCrypto.php(54): ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt()
#3 /home/user/vendor/paragonie/ciphersweet/src/EncryptedField.php(106): ParagonIE\CipherSweet\Backend\ModernCrypto->encrypt()
#4 file.php(88): ParagonIE\CipherSweet\EncryptedField->encryptValue()
#5 file.php(86): ParagonIE\CipherSweet\EncryptedField->prepareForStorage()
#6 file.php(35): customFunction()
#7 {main}
  thrown in /home/user/vendor/paragonie/sodium_compat/src/Core/Util.php on line 915
PHP Fatal error:  Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'column' cannot be null in file.php:201
Stack trace:
#0 file.php(201): PDOStatement->execute()
#1 {main}
  thrown in file.php

My code works perfectly when I revert back to PHP 7.4.

Data sorting strategies

I have not been able to find any discussion of data sorting strategies in the docs or in other discussion and knowledge repositories. The best answer that I have seen so far involves first decrypting the data set, then performing the sort on plaintext. However, not only is that a potential security risk, but its extremely inefficient as the entire data set would need to be stored in memory to then only return a subset of the data back to the user. Is there any way to implement deterministic hashing for purposes of sorting? I have blind indexes on the full values of the fields that I'd like to sort on, but on the surface those hashes don't appear to natively preserve the sort indexing of the data. Thoughts? Any info and ideas are greatly appreciated!

Understanding blind indexing security

As I understand, if I want to improve performance of selecting a given encrypted field, I should create database index on another field that contains blind indexes for encrypted records (stored in encrypted field). Thus, I will be able to select encrypted records faster with a help of those database index.

Can you please explain cases when I will need to create more than one blind index for a given encrypted field.

Thanks.

EncryptedField doesn't apply transforms unless it is used in EncryptedRow

I noticed that while the EncryptedRow applies the transforms as it generates the blind indexes, the EncryptedField does not call this function. Calls to getBlindIndex will instead produce an index of the entire number.

I tested this by producing a python port (still in progress), which doesn't perform the "last four" part of the transform, but still gets the same blind index value in my version of this test:

https://github.com/paragonie/ciphersweet/blob/master/tests/EncryptedFieldTest.php#L253

Would it be possible to tag this commit?

Would it be possible to tag f73aaea as a release?

This fixes the "Private methods cannot be final..." issue in PHP 8. I'm hitting this often in 2.0.2.

Would definitely appreciate it! Thank you

Required JSON field name was missing at depth 0 on encryptJSON() in strict mode

Hi

in a Laravel project, I'm trying to store an array of data.
here is the data

$data = [
        [
            'name' => 'test',
            'prompt' => 'Ask for name',
            'who' => 'sender',
            'type' => 'text',
        ],
        [
            'name' => 'test',
            'prompt' => 'Ask for name',
            'who' => 'sender',
            'type' => 'text',
        ],
        [
            'name' => 'test',
            'prompt' => 'Ask for name',
            'who' => 'sender',
            'type' => 'text',
        ],
    ];

the table has 2 columns, name and data

code

        $map = (new JsonFieldMap())
            ->addTextField('name')
            ->addTextField('prompt')
            ->addTextField('who')
            ->addTextField('type');

        $encryptedRow
            ->addField('name')
            ->addBlindIndex('name', new BlindIndex('name_index'))
            ->addJsonField('data', $map);

Explain the Key Hierarchy

To facilitate speedier security audits when someone uses our library, we should explain the Key Hierarchy and all the steps taken between passing a KeyProvider object to a CipherSweet instance and the final underlying cryptography operation (field-encryption or blind-indexing).

isFileEncrypted corrupts file

Hi,

I've been trying to do this

$result = $encFile->isFileEncrypted($path);

which should do the same as this

$input = fopen($path, 'rb');
$result = $encFile->isStreamEncrypted($input);

The issue is that the function getStreamForFile has a default mode of wb which opens in write binary only. How could that work? In fact in my case, it even overwrite the file with empty data maybe due to subsequent stream operation i'm not sure.

the fix is quite easy, simply pass the mode in isFileEncrypted, that should be "rb"

question: blindIndex type

Very nice package,

I am trying DB (sensitive fields) encryption for the first time, and looked for the best option (to allow fast partial search, algorithms and code API), thus CipherSweet seemed to do the trick ;)

In the article dated 2017 the blind index stored in separate table was consisting of a label and the index value. This is clear how to use.

In the newest version, where flatIndex is false by default, blindIndex returns a secondary parameter called type (beside value).

What's it's purpose and how to use it correctly ?

The only thing I could think is to replace the plainText of the label inside DB, meaning to store and filter by type & value, is this correct ?

PS: sorry to ask this on Issues list, but I thought it would be more direct like this instead of asking on SO.

Best regards,

permit Empty

hey guys,
is there any way to set the permitEmpty attribute in the EncryptedRow.php?
Screenshot 2023-03-04 at 10 11 06 PM
because i couldn't find how to set this attribute to true. the problem i have encountered is that our project uses Graphql for the api section, and if i dont select all the encrypted columns it gives me a Field is not defined in row exception.

i can submit a PR and add parameter to the constructor if it will fix this issue

Per user master key

I don't know if this is the right place to ask, but I've been wondering if it would be possible to encrypt the data with a key per user.

Basically my idea would be this. By default, there is only one master key. That means that is it not possible to achieve zero knowledge encryption: as a platform owner, you have access to the master key.

What I'd like to do, is to be able to have a third party key provider that would provide a key per user. Therefore, the data is encrypted and only readable for that given user.

Now, here is my issue : i'd like to be able to have some kind of "sharing" mechanism, a bit like when you would do with a private/public keypair. The idea is to be able to implement something similar to this:
https://developer.virgilsecurity.com/docs/purekit/data-encryption/secure-data-sharing/

Let's say that third party service that provides a user key also serve as a sharing api. A given user (user A) can grant to another user (user B) access to a given set of ressource. Since ciphersweet use a key per table/field, it would be possible to grant to user B a decryption key derived from user A master key that would allow him to decrypt the data without being able to decrypt any other table/field that is not shared.

Also, an advantage of this third party key provider would be that if a user update his master key, we would be able to store any previous key and reencrypt the data. An old key could be then invalidated once all data has been reencrypted with the newer key.

Does that sounds reasonable/feasible @paragonie-staff ? From what I see from the codebase, it doesn't seem to crazy to try to do something like that.

Binary::safeSubstr() does not support null

Just ran into this minor issue

I'm passing an array from the database. It contains null values, something like

Array
(
[ID] => 3
[Name] => demo3
[MyText] => (null)
)

Using the row rotator, I get this error

TypeError: Argument 1 passed to ParagonIE\ConstantTime\Binary::safeSubstr() must be of the type string, null given, called in \vendor\paragonie\ciphersweet\src\Backend\BoringCrypto.php on line 108

\vendor\paragonie\constant_time_encoding\src\Binary.php:67
\vendor\paragonie\ciphersweet\src\Backend\BoringCrypto.php:108
\vendor\paragonie\ciphersweet\src\EncryptedRow.php:342
\vendor\paragonie\ciphersweet\src\KeyRotation\RowRotator.php:44

I'm wondering if this is the desired behavior, because having null values in array doesn't seem that strange. I fixed the issue in the meantime by replacing nulls by empty strings. Either safeSubstr could accept nulls and treat them as empty strings, or maybe a note should be added somewhere in the docs

Remove Asymmetric Cryptography

We built this in early, but it's probably not necessary. It was included in the v0.1.0 release so if anyone ever does need it, they can start with that tag in their fork.

Define EncryptedFieldSet over an entire row

This is mostly a usability ticket. I want users to be able to do something like this:

<?php
/* use ... */

$fieldset = new EncryptedFieldSet($engine, 'contacts');

$fieldset
    ->addTextField('ssn')
    ->addBooleanField('hivstatus');

$fieldset->addCompoundIndex('contact_ssnlast4_hivstatus', ['ssn', 'hivstatus'], 32)
    ->addTransform('ssn', new LastFourDigits());
    // Adds a transformation to only the SSN before calculating that index

Once the fieldset is defined:

$processed = $fieldset->prepareRow($row);
var_dump($processed);
/*array(
...
'ssn' => ciphertext,
'hivstatus' => ciphertext,
'contact_ssnlast4_hivstatus' => blindindex
...
) */

$db->insert('contacts', $processed);

Searching:

$index = $fieldset->getBlindIndex('contact_ssnlast4_hivstatus', ['1234', true]);
$rows = $db->run("SELECT * FROM contacts WHERE contact_ssnlast4_hivstatus = ?", $index);

Decrypting all encrypted fields in a row from the array:

foreach ($rows as $row) {
    $decrypted = $fieldset->decryptRow($row);
}

How to search a single field of the EncrtypedRow data

Hi,
I have an issue to search a single field value & decrypt the single field value. When i tried to get the compound index for this, i got some new index. But in case, when i had given all the field data, then i got the same compound index.

What is the use of Compound index and how to search the single field data.
If i had used the blind index and it works fine like getting the same index.

For Example, i had use the ssn in blind index, and in compound index i had the fields like ssn, email, hivstatus.

$row->addBlindIndex(
    'ssn',
    new BlindIndex(
        'ssn_last_four_idx',
        [new LastFourDigits()],
        32 // 32 bits = 4 bytes
    )
);

$row->addCompoundIndex(
    (
        new CompoundIndex(
            'ssnlast4_hivstatus_cidx',
            ['ssn', 'email', 'hivstatus'],
            32, // 32 bits = 4 bytes
            true // fast hash
        )
    )->addTransform('ssn', new LastFourDigits())
);

$searchinputdata    = array('ssn'    =>  '123-45-6789');

$searchinputdata_cidx = array(''email'     => '[email protected]');


Stored indexex:
ssn_last_four_idx 		1258bc5e                // Blind Index
ssnlast4_hivstatus_cidx	9fbf99d5                  // Compound Index 

To search data,
when i use to get the blind index for ssn, it returns the same index value

$row->getBlindIndex('ssn_last_four_idx',$searchinputdata);

returns Blind Index (ssn_last_four_idx) as 1258bc5e

But when i tried to get compound index only with email, then it returns new compound index

$row->getBlindIndex('ssnlast4_hivstatus_cidx',$searchinputdata_cidx);

returns Compound Index (ssnlast4_hivstatus_cidx) as 1a1937b2

I need the clarification and help on this issue to achieve the search with compound index and decrypt the same.

Thanks

Need some help on bindindex planning

I have been through https://ciphersweet.paragonie.com/php/blind-index-planning over and over but since math was never a strong subject, I'm not able to understand how to calculate these two values addExistingIndex('birth_year_idx', **8**, **14**)

Let's say if I have a column named phone_number where there might be 100K records in next 10 years and each phone number may vary 12 to 15 digits, in that case how to do I calculate what should I put instead of 8 and 14?

Encryption/decryption speed

Hi,

I'm currently working with large import/export of data. Basically, the flow is like this:

  • check if the record exist using a blind index
  • create or update the record (and encrypting the content, of course)

The issue is that doing so is really slow compared to plain queries/inserts in the php loop.

Is there any way to make this faster ? Having blind indexes is great, but if it's not possible to process records in bulk in a reasonable time frame, it defeats the purpose in some cases.

Do we use ciphersweet's ciphertext in place of Halite?

After reading the documentation it seems as though the ciphertext is encrypted text with the benefit of you having separate blind indexes. Can we just use Halite for storing the ciphertext and then use the blind indexes from ciphersweet for lookups? Is there any benefit/drawbacks to doing this?

Thanks!

Is a salt needed with Halite encryption? How do I supply one?

Is there a way to provide a salt value with the encryption for Halite? Or do I even need to (perhaps the salt is included in the encrypted value)? I see you can use a salt to derive a password but I don't see anything about doing symmetric encryption with a salt.

Thanks!

Invalid mac

Hi,

I'm sometimes getting "Invalid mac" exception using the fips encoding. I don't think these are indeed true because it seems to happen randomly and not easy to reproduce.

Could there be any reason why this would fail in some cases ?

Third padding constant C3

For a third information in the database which I want to hash, I want to use a third padding constant C3 for domain seperation concerns. I've read your article regarding hamming distance:

C1 is the byte 0xB4 repeated 32 times.
C2 is the byte 0x7E repeated 32 times.

The constants C1 and C2 were chosen to have a Hamming distance of 32*4 = 128b between them, and are used to achieve domain separation for secure key splitting.

https://ciphersweet.paragonie.com/internals/key-hierarchy

I think 0xE8 would be suitable since it has a hamming distance of 4 to each 0xB4 and 0x7E. However, I am not sure if it is part of any HMAC padding constant as you also have written in your article.

Do you think 0xE8 is fine or would you suggest another constant?

Related scientific articles

Can you please provide a links to scientific articles on searchable encryption topic that you studied and used for CipherSweet creating? What theoretic SSE scheme inspired the most?

Documentation enhancement request (re: Blind Index Planning)

Hi,

Re: The Blind Index Planning page of the docs (https://ciphersweet.paragonie.com/php/blind-index-planning)

It would be helpful for the non-cryptographers amongst us to perhaps provide a plain-english definition of what you mean by the terms output size and input domain.

I have tried asking Mr Google what an index input domain is but he was not being particularly helpful. Therefore whilst I think I know what you mean by the term, I would rather be "safe than sorry" and see a plain-english definition magically appear in the docs. ;-)

Thanks and keep up the fabulous work !

[FEATURE] Blind Index Planner

The information provided here should be more useful for end users.

Ideal interface:

$planner = (new FieldIndexPlanner())
    ->setEstimatedPopulation(1 << 16)
    ->addExistingIndex('name', 8, PHP_INT_MAX)
    ->addExistingIndex('first_initial_last_name', 4, PHP_INT_MAX);

var_dump($planner->getCoincidenceCount()); // float(some number)

Then there needs to be a recommend() API that accepts an optional integer size for the input domain (defaults to PHP_INT_MAX) and provides a minimum and maximum integer value for the bit size such that the coincidence count for an additional index will be between 2 and sqrt(population).

Boring Crypto Incompatible with Node.js

It's not possible to encrypt/decrypt between the PHP library (v3.0.5) and Node library (v2.0.5). I believe this is because they treat AAD differently. In node the AAD is concatenated to the nonce when calculating the MAC. In PHP the AAD is not concatenated.

In node:

       if (aad.length >= 0) {
            if (!Buffer.isBuffer(aad)) {
                aad = await Util.toBuffer(aad);
            }
            aad = Buffer.concat([nonce, aad]);
        } else {
            aad = nonce;
        }

In PHP:

        if (is_null($aad)) {
            $aad = '';
        }

I'm not sure if this is the only source of incompatibility, but as it stands I can't find a way to have portability between PHP and Node. I think care would be needed in any change around this to prevent breaking decrypting of any stored values. Perhaps the behaviour should be configurable.

Edit: I tried making the behaviour configurable and that got me past the MAC check, but decrypting in node a value encrypted from PHP resulted in corrupt data. I believe the inputs to the xchacha20 functions are the same in both languages, but the output I'm getting is different.

In Node:

        const xchacha = new XChaCha20();
        const decrypted = await xchacha.decrypt(
            encrypted,
            nonce,
            (await this.getEncryptionKey(encKey)).getRawKey()
        );

        await sodium.sodium_memzero(encKey);
        return decrypted;

in PHP:

        return \sodium_crypto_stream_xchacha20_xor(
            $encrypted,
            $nonce,
            $this->getEncryptionKey($key)->getRawKey()
        );

To test this hypothesis I've thrown together a quick test case:

echo \sodium_crypto_stream_xchacha20_xor(
    hex2bin('549cad2cd9bc64'),
    hex2bin('9e985dfdfdf85321b3171596a213fd3e819f0d9800fce29f'),
    hex2bin('82a9032e85e88ac6ed8365b8f3280d9ff9d8a88728886d18d9b52dd0fc6c5da1')
);
//outputs "qwertyu"
const { ChaCha20, HChaCha20, XChaCha20 } = require('xchacha20-js');

const xchacha = new XChaCha20();
xchacha.decrypt(
    Buffer.from('549cad2cd9bc64', 'hex'),
    Buffer.from('9e985dfdfdf85321b3171596a213fd3e819f0d9800fce29f', 'hex'),
    Buffer.from('82a9032e85e88ac6ed8365b8f3280d9ff9d8a88728886d18d9b52dd0fc6c5da1', 'hex')
).then(b => console.log(b.toString('utf-8')));
//outputs ")���<\"

Thanks
Tim

Use bit lengths instead of byte lengths for blind indexes?

8 bytes is enough to minimize collisions for a 32-bit primary key.
16 bytes is enough to minimize collisions for a 64-bit primary key.

Anything further is kind of pointless. If you're defining a Bloom filter, you probably want a bit more granularity than 0-16.

Basically: in v0.2.0 and beyond, you would need to multiply these parameters by 8, but then you can get more granular filtering thanks to bitwise AND masking. However, this would cease to be the "output length" (instead, the output length would be ceil($size / 8)).

Undefined array key when decrypting a row which does not include all encrypted fields

I just came across an issue where a exception is thrown when I attempt to decrypt a record which does not include all encrypted fields. This can occur when using ciphersweet in Laravel via spatie/laravel-ciphersweet and not selecting all encrypted DB fields in a query.

The issue seems to be in the decryptRow function of ParagonIE\CipherSweet\EncryptedRow

Here's a snippet to show this issue natively without using Laravel:

<?php

use ParagonIE\CipherSweet\CipherSweet;
use ParagonIE\CipherSweet\KeyProvider\StringProvider;
use ParagonIE\CipherSweet\EncryptedRow;

// Setup encryption
$row = (new EncryptedRow(new CipherSweet(new StringProvider('4e1c44f87b4cdf21808762970b356891db180a9dd9850e7baf2a79ff3ab8a2fc')), 'test'))
    ->addTextField('ssn')
    ->addTextField('name')
    ->addTextField('email');

// Encrypt a dummy record
$prepared = $row->prepareRowForStorage([
    'name' => 'John Smith',
    'ssn' => '123-45-6789',
    'email' => '[email protected]'
]);

// Grab the record part as it would look coming from a DB query
$dbRecord = $prepared[0];

$decrypted = $row->decryptRow($dbRecord);
/*
    Works as expected..
    [
        "name" => "John Smith",
        "ssn" => "123-45-6789",
        "email" => "[email protected]",
    ]
*/

// Now assume we are performing a DB query which only selects
// the name and email (not the ssn)
$dbRecordNew = [
    "name" => $dbRecord['name'],
    "email" => $dbRecord['email'],
];

$decrypted = $row->decryptRow($dbRecordNew);

/*
    WARNING  Undefined array key "ssn" in vendor\paragonie\ciphersweet\src\EncryptedRow.php on line 407.

    [
        "name" => "John Smith",
        "email" => "[email protected]",
        "ssn" => null,
    ]
*/

The issue is from line 407 in the decryptRow function of ParagonIE\CipherSweet\EncryptedRow.

Inserting a check in the decryptRow function to ignoring any non-existent array key would fix this, however I am unsure if this would break anything else.

    $key = $this->engine->getFieldSymmetricKey(
        $this->tableName,
        $field
    );
    //---- add this ----------------------
    if (!array_key_exists($field, $row)) {
        continue;
    }
    //------------------------------------
    if (\is_null($row[$field])) {
        $return[$field] = null;
        continue;
    }

Partial field

Is it possible to create a blind index on a part of the field.
Ie I want to be able to find all email on a certain domain, can I create a blind index on @example.com that returns all records that would include an email like [email protected], [email protected] etc

Currently it looks like the encryption is only possible on the entire field data, or am I missing something ?

EncryptedRow::getBlindIndex throws BlindIndexNotFoundException for non-compound indexes

EncryptedRow::getBlindIndex is implemented as though $this->blindIndexes were an array of BlindIndex indexed by index name. It's actually an array of arrays (of BlindIndex indexed by index name) indexed by column name. When passed the name of a non-compound index it therefore fails to find it the index, and throws a BlindIndexNotFoundException.

sodium_crypto_pwhash() warns "empty password"

sodium_crypto_pwhash() warns "empty password" when the value for a blind index is an empty string or null.

My current workaround is to add this line to the beginning of BoringCrypto::blindIndexSlow()

if ($plaintext === '') { $plaintext = ' '; }

A unique value from a constant might be preferable.

Differences between slow and fast method for blind index

Thank you very much for this awesome work !

I'm currently building a symfony/doctrine bridge package to provide your library as a user friendly feature. My concern is about the Blind Index generation which can take time when we want to build a large list of indexes.
I ran a benchmark to compare both slow and fast methods :

        // Init encryption fields
        // We use the BoringCrypto BackendInterface which calls Libsodium

         $accountNameS = (new EncryptedField($this->engine, RemoteWallet::class, 'account_name'))
            ->addBlindIndex(
                new BlindIndex('account_name'.'_bi', [], EncryptorInterface::DEFAULT_FILTER_BITS)
            );

        $accountNameF = (new EncryptedField($this->engine, RemoteWallet::class, 'account_name'))
            ->addBlindIndex(
                new BlindIndex('account_name'.'_bi', [], EncryptorInterface::DEFAULT_FILTER_BITS, true)
            );

        $startS = microtime(true);
        $idxS = [];
        for ($i = 0; $i < 1000; $i++) {
            $idxS[] = $accountNameS->getBlindIndex((string) $i, 'account_name'.'_bi');
        }
        $endS = microtime(true) - $startS;

        $startF = microtime(true);
        $idxF = [];
        for ($i = 0; $i < 1000; $i++) {
            $idxF[] = $accountNameF->getBlindIndex((string) $i, 'account_name'.'_bi');
        }
        $endF = microtime(true) - $startF;

        $this->io->table(['Method', 'Duration', 'First index'], [['Slow', $endS, $idxS[0]], ['Fast', $endF, $idxF[0]]]);

        $this->io->comment('Are idx tables different ? (of course they should be) '.(array_diff($idxS,$idxF) !== [] ? 'Yes' : 'No'));

Here is the output on my laptop :

 -------- ------------------- ------------- 
  Method   Duration            First index  
 -------- ------------------- ------------- 
  Slow     66.616168022156     cd7882e9     
  Fast     0.039582014083862   1d704b7d     
 -------- ------------------- ------------- 

 // Are idx tables different ? (of course they should be) Yes        

I'd like to know if we can safely use Fast method or if we should prefer the slow one as a good practice.

Optional parameters in EncryptedField constructor

Hi.
In the instantiation of an encrypted field we pass as parameter the table and field name.
Following your example $ssn = (new EncryptedField($engine, 'contacts', 'ssn'));
Those are actually optional parameters. For my needs avoiding using those would simplify a lot (together with naming the blind index with a generic default name).
I'm wondering if those optional fields are actually something that gives an additional salt to our cipher text or if they are not relevant for the security.

SQLSTATE[01000]: Warning: 1265 Data truncated for column 'indexable_id' at row 1

Hi

When using uuid as id column I get this error:

SQLSTATE[01000]: Warning: 1265 Data truncated for column 'indexable_id' at row 1

for me the solution as to change the migration:

from

 $table->morphs('indexable');

to

$table->uuidMorphs('indexable');

not sure if there is a better way, but a note in the documentation could help others :)

Managing Coincidences

Suppose we are executing a query using a blind index and a query against the index returns multiple possible hits. How do we differentiate true positives from false positives? Decrypt and compare raw values?

How to do a string search?

The documentation provides an example for how to do a search on the last four of the SSN, but how do you setup a string search? For something like a last name where the length could be variable - an example would be nice to see.

Thanks!

encryptStream documentation could use a little more code

so i was using this bit of code

$input = \fopen('/tmp/super-secret', 'rb');
$output = \fopen('php://temp', 'wb');
$encFile->encryptStream($input, $output);

then, I expected this to work

file_put_contents($filename, $output);

but I ended up with an empty file! Surely something must be wrong? And yes, because the pointer is at the end of the file !

so either, I need to do this

rewind($output);
file_put_contents($path, $output);

or encryptStream should return a rewinded stream

anyway, it's probably worth mentionning just for other people like me that might otherwise end up scratching their head for no reasons :-)

global blind index implementation?

Hi,
I am making a global search system from my app that allows users to search for encrypted data from multiple tables with differing schemas containing encrypted data. The only commonality between the data being searched are that they are all text searches with the same transformations. what would be the best way to go about doing this?

Recommendations for Zero-downtime key rotation?

Thanks for this great package! I am looking for a little direction, advice, and/or recommendations. I have a database table with >300k rows of data where a couple fields need to be encrypted with blind indexes. The encryption process with blind indexes enabled takes double digit hours (10+) to complete while the encryption process without blind indexes takes <20 minutes.

I understand there is a design pattern for rotating the keys and the package is designed to support a single key ("the key").

With an encryption/key rotation process that takes 10+ hours, it is not feasible to go "down for maintenance" that long and if the rotation process takes place while "up" then it stands to reason that there would be rows that are encrypted with different keys and any one time.

I am endeavoring to find a solution wherein I can have a list of keys available (the newest one, and the previous most recent one) and add some tolerance to the decryption step for attempting all of those keys until one hits. This would, in theory, allow me to gracefully handle the use case I described above without throwing workflow disruption exceptions.

The idea being, we can decrypt with more than one key but upon encryption the newest one would be used.

I am not sure if this use case a) was intentionally avoided (for reasons...?), b) is already supported (and not documented well?), or c) is reasonable (and requires some refactoring?).

If it is "a" or "b", can you provide some context and help?

If it is "c", I am happy to help contribute some ideas/code to help get to that capability - I am just not sure which high-level approach would be more appropriate/preferred to this group.

At the moment, the ParagonIE\CipherSweet\EncryptedRow::decryptRow method has my attention.

public function decryptRow(
#[\SensitiveParameter]
array $row
): array {
/** @var array<string, string|int|float|bool|null|scalar[]> $return */
$return = $row;
$backend = $this->engine->getBackend();
if ($this->engine->isMultiTenantSupported()) {
$tenant = $this->engine->getTenantFromRow($row, $this->tableName);
$this->engine->setActiveTenant($tenant);
}
foreach ($this->fieldsToEncrypt as $field => $type) {
$key = $this->engine->getFieldSymmetricKey(
$this->tableName,
$field
);
if (!array_key_exists($field, $row)) {
if (!$this->permitEmpty) {
throw new EmptyFieldException('Field is not defined in row: ' . $field);
}
continue;
}
if (\is_null($row[$field])) {
$return[$field] = null;
continue;
}
if (
!empty($this->aadSourceField[$field])
&&
\array_key_exists($this->aadSourceField[$field], $row)
) {
$aad = (string) $row[$this->aadSourceField[$field]];
} else {
$aad = '';
}
if ($type === Constants::TYPE_JSON && !empty($this->jsonMaps[$field])) {
// JSON is a special case
$jsonEncryptor = new EncryptedJsonField(
$backend,
$key,
$this->jsonMaps[$field],
$this->jsonStrict[$field]
);
$return[$field] = $jsonEncryptor->decryptJson($row[$field], $aad);
continue;
}
$plaintext = $backend->decrypt($row[$field], $key, $aad);
$return[$field] = $this->convertFromString($plaintext, $type);
}
return $return;
}

What do you think?

EncryptedField does not convert non-string data types to/from string

Hello and thank you for your work on CipherSweet!

I'm currently working on a Ruby port and I noticed a difference between the EncryptedRow and EncryptedField classes: the former uses convertToString and convertFromString but the latter does not. This seems like an oversight (since it makes the implementations not interchangeable to use when using EncryptedRow used non-string types) but I wanted to check in here in case it was intentionally designed this way?

ParagonIE\CipherSweet\KeyProvider\StringProvider::__construct(): Argument #1 ($rawKey) must be of type string, null given

I'm not sure that I'm asking in the right place, but I want to understand and solve the problem, so I'll write it here anyway.

I installed to my project developed with Laravel framework this library https://github.com/spatie/laravel-ciphersweet, which implements Ciphersweet use within Laravel, and tried to encrypt data in my DB with this command
php artisan ciphersweet:encrypt "App\Models\User" da61321c8814fe47f6f647f26330bc34f88fd8c32c1df25af4f0b3a30dff00e9
, but got error
ParagonIE\CipherSweet\KeyProvider\StringProvider::__construct(): Argument #1 ($rawKey) must be of type string, null given, called in /var/www/fastuser/data/www/api.dev.clanhall.net/vendor/spatie/laravel-ciphersweet/src/LaravelCipherSweetServiceProvider.php on line 52
on line 48-55 I see this code:

48 protected function buildKeyProvider(BackendInterface $backend): KeyProviderInterface
49    {
50        return match (config('ciphersweet.provider')) {
51            'file' => new FileProvider(config('ciphersweet.providers.file.path')),
52            'string' => new StringProvider(config('ciphersweet.providers.string.key')),
53            default => new RandomProvider($backend),
54        };
55    }

and then if I go to StringProvider I see

public function __construct(string $rawKey = '')
    {
        if (Binary::safeStrlen($rawKey) === 64) {
            $this->rootSymmetricKey = Hex::decode($rawKey);
        } elseif (Binary::safeStrlen($rawKey) === 44) {
            $this->rootSymmetricKey = Base64UrlSafe::decode($rawKey);
        } elseif (Binary::safeStrlen($rawKey) === 32) {
            $this->rootSymmetricKey = $rawKey;
        } else {
            throw new CryptoOperationException('Invalid key size');
        }
    }

I tried to google why PHP see empty string as null, but without success, and I'm not sure that this is the problem.

What am I doing wrong and how can I try to fix it?
As I see, no one has ansered to my question in that thread, so maybe only me got this error, and maybe I don't know some basics.

I would appreciate your help.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.