-
Notifications
You must be signed in to change notification settings - Fork 30k
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
crypto: Deprecate createCipher for createCipheriv #13941
Conversation
src/node_crypto.cc
Outdated
|
||
if (encrypt && iv_len != 0) { | ||
return env()->ThrowError("crypto.createCipher() is no longer supported with ciphers that require initialization vectors. Generate a random IV and pass it to crypto.createCipheriv()."); | ||
} |
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.
No comments on whether this is something we should do (ping @nodejs/crypto) ... but this is not the correct way of doing it. There are two kinds of deprecations we use: documentation and runtime. A runtime deprecation uses either the util.deprecate()
or process.emitWarning('...', 'DeprecationWarning')
API to emit a warning at runtime, but the code still continues to operate as it did in every other respect.
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.
My PR intends to immediately prohibit the use of this API for ciphers that require an IV. The deprecation of this API entirely hasn’t been done yet. I’m guessing that would go in lib/crypto.js?
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.
Yep, and that's the point: Our deprecation policy requires that APIs go through a proper deprecation cycle so this PR would not be able to land as is. The first step is to do a docs or runtime deprecation as a semver-major, which would go into Node.js 9.0.0.
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.
We do have provisions for making exceptions for security-related issues but as this is more about protecting users against themselves, it's up for debate whether those provisions apply.
I'm personally leaning towards 'acceptable in node 9' (raising an exception, that is) but I won't hold it against anyone who feels differently.
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 prohibits users from using not only counter modes but also cbc modes. I'm not sure if the cbc mode has a security issue in this API.
src/node_crypto.cc
Outdated
|
||
if (!Buffer::HasInstance(args[2]) && !args[2]->IsNull()) { | ||
return env->ThrowTypeError("IV must be a buffer or null"); | ||
} |
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.
why this change?
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.
Because this API will now accept null as a valid IV.
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 means you don’t need to pass an empty Buffer for ciphers where there is no IV)
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 better approach here then, would be to do the type checking at the js layer and use the internal/errors API to generate a proper ERR_INVALID_ARG_TYPE
error.
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 this about createCipheriv
? Why would any sane person come up with the idea to use a function createCipheriv
for ciphers where there is no IV?
This might be a start, but I don't think we can land this without the other suggested changes. Summary of those changes:
Without the second point implemented, I would not want to land the first one.
Why would we want to deprecate it as a whole? It is perfectly fine to use it with ciphers that do not require an IV. Additionally, we should consider @stouset's #13821 (comment), but for reasons discussed in #13821, we might want to give that function a new name. |
I didn't mean for this to be immediately landed, just for discussion to happen over what's done so far.
Not sure I agree. First, the fact it takes a We could change I guess this all depends on whether or not these APIs are supposed to be low or high level, as @cbarcenas has mentioned. @stouset's proposal probably needs its own PR. |
I was conservative to deprecate this API because it is very old API and I could not imagine how much it is affected like Buffer API.
I agree this. It is a harmful to have md5 KDF at this time. If we can really proceed to make deprecation, I think we had better to deprecate entire |
I see your point, the current key generation is not too secure... I actually expected a way to change the signature such that users could either specify a password or a key, but I don't see such a solution right now. We might actually have to deprecate the whole function, which is terrible, as |
@tniessen Creating ciphers without an IV is inherently dangerous, and in my opinion should not be the easiest way to invoke the library. It's fine for this to be considered a low-level API, but without a very widely-promoted high-level one, a module named ECB is the only mode I can think of that doesn't require instantiation with an IV. Given that, an API like Once |
I've pushed the deprecation warning for One thing I saw is the Also, we could think about using I still need to add the legacy methods, more documentation, and tests. |
Old: > let cipher = crypto.createCipher('aes-128-ecb', 'password');
undefined
> cipher.update('hello');
<Buffer >
> cipher.final('base64');
'LJ6pZaGs8G7A/W5gW+txkw==' New: > var cipher = crypto.createCipheriv("aes-128-ecb", crypto.generateLegacyKey("aes-128-ecb", "password"));
undefined
> cipher.update("hello");
<Buffer >
> cipher.final("base64");
'LJ6pZaGs8G7A/W5gW+txkw==' |
All tests/linters now pass on my machine. Can someone trigger a CI build? Not sure how to go about that. |
This needs a rebase and it seems like not all tests passed. |
I can finish this up soon. I believe there is a bug (with the tests?) when FIPS mode is used. |
Ping @iangcarroll |
doc/api/deprecations.md
Outdated
|
||
Type: End-of-Life | ||
|
||
[`crypto.createCipher()`][] generates keys from strings in an insecure manner, and, when used with a cipher that utilizes an initialization vector, will dangerously re-use initialization vectors. As such, it is immediately marked as End-of-Life when used with ciphers that require initialization vectors, and will be fully removed in a later version. |
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.
Please line wrap at <= 80 chars
doc/api/deprecations.md
Outdated
<a id="DEP00XX"></a> | ||
### DEP00XX: crypto.createCipher() | ||
|
||
Type: End-of-Life |
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 should not be automatically moved to End-of-Life
. This needs to go through a proper deprecation cycle with either a Documentation-only or Runtime stage.
doc/api/deprecations.md
Outdated
|
||
[`crypto.createCipher()`][] generates keys from strings in an insecure manner, and, when used with a cipher that utilizes an initialization vector, will dangerously re-use initialization vectors. As such, it is immediately marked as End-of-Life when used with ciphers that require initialization vectors, and will be fully removed in a later version. | ||
|
||
[`crypto.createCipheriv()`][] should be used in place of [`crypto.createCipher()`][]. Since [`crypto.createCipheriv()`][] will no longer attempt to derive a proper encryption key from a string, you must use a key-derivation function such as [`crypto.pbkdf2()`][] to obtain a valid key if you normally supply a string to [`crypto.createCipher()`][]. |
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.
Please avoid using the informal pronouns you
and your
in the documentation.
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.
Almost there. I'm -1 on going directly to End-of-life
stage. This should sit as a Runtime deprecation first.
Can someone please re-run the CI build? I can't reproduce the failing build locally. Is there any sort of process for deciding the plan for deprecating this? Or is there no hope of directly EOLing it with IV-dependent ciphers? |
@iangcarroll do you still want to pursue this PR? |
Yeah, I have local fixes for the CI failures, there's just one more bug I need to fix with current (mis)uses of > let encrypt = crypto.createCipheriv('BF-ECB', 'SomeRandomBlahz0c5GZVnR', '');
undefined
> encrypt.update('Hello World!', 'ascii', 'hex');
'6d385f424aab0cfb' > let encrypt = crypto.createCipher('BF-ECB', 'SomeRandomBlahz0c5GZVnR');
undefined
> encrypt.update('Hello World!', 'ascii', 'hex');
'b2941213258d679b' |
That's because > crypto.createCipher('bf-ecb', 'pass').update('11223344', 'utf8', 'hex')
'93b5c44c33c68cd1'
> var pass = crypto.createHash('md5').update('pass').digest();
> crypto.createCipheriv('bf-ecb', pass, '').update('11223344', 'utf8', 'hex')
'93b5c44c33c68cd1' |
I have changed the deprecation so it is now a runtime warning for all calls to |
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.
Still not entirely happy with the need to change this, even though I understand the reasoning.
benchmark/crypto/cipher-stream.js
Outdated
const iv = crypto.generateLegacyIV(conf.cipher, alice_secret); | ||
|
||
var alice_cipher = crypto.createCipheriv(conf.cipher, key, iv); | ||
var bob_cipher = crypto.createDecipheriv(conf.cipher, key, iv); |
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.
Use const
instead of var
.
doc/api/crypto.md
Outdated
- `key` {string | Buffer | TypedArray | DataView} | ||
|
||
Creates and returns a [Buffer][`Buffer`] object, with the given `algorithm` and | ||
`key`. |
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.
Your docs seem to miss information about what the functions actually do.
doc/api/deprecations.md
Outdated
|
||
If the previous key-derivation is required for backward compatiability, the new | ||
APIs [`crypto.generateLegacyKey()`][] and [`crypto.generateLegacyIV()`][] have | ||
been added. |
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 really a causality.
doc/api/deprecations.md
Outdated
|
||
Additionally, for ciphers that require an initialization vector, a proper-length | ||
initialization vector must be passed to [`crypto.createCipheriv()`][]. | ||
Initialization vectors must never be re-used, especially in modes such as |
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.
must
→ should
. The word must
indicates that calling the function with such parameters leads to undefined behavior or an error, while this is actually just a strong recommendation.
test/parallel/test-crypto.js
Outdated
@@ -135,6 +135,10 @@ testImmutability(crypto.getCurves); | |||
|
|||
// Regression tests for #5725: hex input that's not a power of two should | |||
// throw, not assert in C++ land. | |||
if (common.hasFipsCrypto) { | |||
|
|||
} |
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 assume you are still working on this part.
Updated. I'll take care of the merge conflicts shortly. |
It's going to be a couple weeks before I can wrap this up. But if there are things that need to be fixed please let me know in the interim. |
@iangcarroll Let us know if you need any help. |
@iangcarroll do you have the time to pursue this further? :-) |
Ping @iangcarroll |
I can take over this if necessary. By the way, I would like to reconsider whether we really want to include |
It's not necessary. I uploaded a script last week for computing legacy keys that can be made into a npm module if so desired. |
Thanks @bnoordhuis, that's exactly what I had in mind. The crypto API is already quite bloated in my opinion, so I would prefer that very much. |
@tniessen do you still would like to work on this? It would be great if you could open a new PR accordingly. I also think your approach not to add a new API is good. |
Closing due to no further progress. @iangcarroll thanks a lot for your work! I think it would really be great to get this in and I hope you or someone else might pick this up again. @tniessen @bnoordhuis I guess one of you two might wants to pick it up? |
@BridgeAR I will start working on an alternative soon. |
This is the start of an implementation of @cbarcenas's proposal and an evolution of #13821, except
createCipher
ends up being deprecated. I believe the poor KDF increateCipher
is also harmful, as it allows arbitrary length keys but only puts them through a round of MD5, hence the removal.crypto.createCipher
is deprecated entirely. Initially, this pull request madecreateCipher
error when passed an IV-dependent cipher, but this has been removed.crypto.createCipheriv
now supports anull
IV for algorithms without an IV.This is my first Node PR, so please let me know if I've done anything wrong.
createCipher
is callednull
IV support intocreateCipheriv
for use with ECBChecklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
crypto, doc