-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Encryption] New component #39344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Encryption] New component #39344
Conversation
src/Symfony/Component/Security/Core/Encryption/SymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks georgous and simple! Thank you for doing it. Sorry for the not so useful comments, but I've focused on the text instead of the code for this first review.
Two questions that come to mind:
Would it make sense to have a dedicated component instead of having it in security? I can see many usages for which I would not need anything else from the Security components.
We are using "encryption" in the secret feature (part of framework bundle) and in the Mime component. Any code that could be moved to this new hypothetical encryption component?
src/Symfony/Component/Security/Core/Encryption/AsymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/AsymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/AsymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/AsymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/SodiumEncryption.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/SymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/SymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Exception/EncryptionException.php
Outdated
Show resolved
Hide resolved
Thank you for the review. I've updated the PR accordingly.
Yes, it will be a small component, but I do think so.
Looking at the In the MimeComponent we have a The current implementation is an abstraction |
There is no such thing as a "small component" :) History showed us that most small components grew over time with new needs are discovered. |
👍 for a new component (I would even prefer a component outside the There already are a couple features in the framework bundle that can benefit from this new component I think, e.g. encrypting cache values (refs #35019) and encrypting session data (refs #35804). |
src/Symfony/Component/Security/Core/Encryption/AsymmetricEncryptionInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Encryption/PhpseclibEncryption.php
Outdated
Show resolved
Hide resolved
I need some more work to support JWE. Using JWE has the benefit that we more easily can share the encrypted strings with other applications. JWE exists of 5 parts: header: a json object defining what algorithms we use So we use symmetric encryption for the payload and asymmetric to encrypt the symmetric key. |
So, we are compatible with the JWE format.. almost. I cannot figure out the Here is an example for RFC7516:
So basically: "Take a bunch of data, encrypt it with AES GCM and from that encryption you get ciphertext X and Authentication Tag Y". But when I look at ie sodium or libsecphp, I find no encryption algoritm where I can get two outputs.. Am I missing something? However, We are following the same protocol and the ciphertext is secure, but it is not according to the spec (not so good for portability) |
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Encryption/Provider/PhpseclibEncryption.php
Outdated
Show resolved
Hide resolved
I liked JWE because it mean that our payload could be portable. However, JWE introduced a lot of complexity and I didn't manage to add a Authentication Tag according to the spec. So.. just complexity and no probability. I changed back to a more simple implementation. |
Im trying out something new. Im making the API more explicit by introducing a KeyInterface. See 12804bb |
Sorry for this harsh comment, but it's noting personal: DON'T EVER ROLL YOUR OWN ENCRYPTION! I'm no crypto expert but I already see some things that are potentially dangerous.
Why do you think Sodium was created? 😄 Cryptographic is a complex topic and with my limited knowledge I can already spot some things I'm not entirely sure of, but I know have caused major problems in the past. If you have sodium, use it. PHP has it bundled already, and it is the most secure standard of today. RSA and DSA should only used for legacy systems or by experts who know how the handle these complexities. ping @paragonie-scott |
We are not creating our own encryption here but just creating a wrapper that relies on other lower level encryption libraries (including Sodium). |
….php Co-authored-by: Robin Chalas <chalasr@users.noreply.github.com>
return $key; | ||
} | ||
|
||
public static function fromPrivateAndPublicKeys(string $privateKey, string $publicKey): self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not used and class is internal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe these method will be very useful when we use the encryption component for the "symfony secrets".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have the feeling that having some usages of this new component in core would help moving forward here.
@Nyholm Maybe could you open a PR doing so based on this PR? Or commit here directly?
return $key; | ||
} | ||
|
||
public static function fromKeypair(string $keypair): self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation needs work before it can be a general-purpose encryption component for all Symfony users.
$key->secret = $secret; | ||
} else { | ||
// Trim the key to a good size | ||
$key->secret = substr(sha1($secret), 0, \SODIUM_CRYPTO_SECRETBOX_KEYBYTES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, sha1()
is not an appropriate function to use here.
But even worse, this is hex-encoded so you're getting roughly half of the expected entropy out of your secret from this sha1 invocation.
- $key->secret = substr(sha1($secret), 0, \SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
+ $key->secret = mb_substr(hash('sha256', $secret, true), 0, \SODIUM_CRYPTO_SECRETBOX_KEYBYTES, '8bit');
|
||
// Check if data has been modified | ||
$hash = hash('sha256', $headersString.$ciphertext); | ||
if (!hash_equals($hash, $hashSignature)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misnomer: $hashSignature
isn't, at all, a hash signature.
If you were doing the right thing, it would be a MAC, not a signature, but this just a SHA256 hash without any secrets. This is trivially bypassed: Just calculate hash('sha256', $headersString.$ciphertext)
for any arbitrary data you want to feed in.
You're probably better off calling this a checksum, or equivalent, so as to not over-sell the security properties you're shooting for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This setup also looks susceptible to canonicalization attacks. If the intent here is to have something like HMAC, you'll want to also revisit this.
@Nyholm What do we do here? |
Im gathering courage to make this mergable. I will not stall more than another month. |
Must target 6.1 then, right? |
throw new DecryptionException(sprintf('Failed to decrypt message with algorithm "%s".', $algorithm), $exception); | ||
} | ||
|
||
if (false === $output) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!is_string()
would be a better condition for static analysis.
s/encryption/encryptor/ everywhere? 😅 |
Let's close for now |
This PR introduces a new Encryption component. But wait, NO. That is not what we are doing.
We are not doing encryption
This component does not do encryption. We do not invent a way to protect your data. This is an encryption abstraction component. So the only thing we do is to define two interfaces and provide a sodium implementation of them.
The interfaces
There is an KeyInterface and a EncryptionInterface. They look something like this:
There are some documentation on the interfaces, they can be good to read if one is not sure what the methods are doing.
Keys
The idea behind these interfaces is that you create a
Key
. That key can be used for both symmetric and asymmetric encryption. That key can be serialized and stored on disk or in database. You can extract the public part of yourKey
.You as a user need to know what type of key to have. You cannot load a random key from disk and use it with
encryptFor()
. You need to know that it was Alice's private key. Ie, Key management is out of scope of the component.Keys cannot be transfered to a different implementation. (Just like PSR-6). So you cannot use
SodiumEncryption
to create a Key and then use it withPhpseclibEncryption
.Encryption
The encryption interface provides an explicit way how to handle encryption. There are 3 scenarios:
For someone who worked with encryption they know that this is no rocket science. But for someone who takes their first step into encrypting in PHP, they probably look at PHP's Sodium "documentation" and have no idea where to start. This
EncryptionInterface
will be super helpful for them.Portability
( This section is where I accept all kinds of "you are doing it wrong" 😄 )
The output of all
EncryptionInterface::encrypt*()
methods is a string. That string has a specific format.The format is implemented in the
Ciphertext
class. Here is how the final string look like:With some psudo-code:
But why do we need this format?
We do need a format. We need to store some meta data, the ciphertext and the nonce somewhere. To concatenate these together seams like the simplest solution.
The
alg
header is basically a routing key to tell the difference between “Algorithm not supported” and “Wrong key was used to decrypt message”. Also to make the keys somewhat portable. Ie, it is possible for a non-encryption-component-application to decrypt the key. Note that there is no algorithm negotiation. Note also that the hash here is not to make things "more secure", it is just for an integrity check.This format will also fall under or BC policy.
I have looked at using JWE, but I was not able to follow the specification to 100% to create the Authentication Tag (Im not sure it is possible in PHP...) and I've also seen posts about not to use JWE. JWE also made things much more complex.
The name of the component
I am not a big fan of the name of this component. What I've noticed that just because it is called "Encryption", then people think that we are doing advanced security stuff. This component will never compete with Sodium, Phpseclib or Halite. It will only make a very thin abstraction on top of it. This abstraction will work for 80% of your encryption needs. For the other 20% you need more flexibility or customisation. That is where you use Sodium, Phpseclib, Halite etc directly.
Ie, this component is not a good fit for "That other team's Java application sends us encrypted data, can we decrypt it?".