-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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: refactor crypto module #15231
Conversation
Should this be labeled |
haven't decided yet lol.... the rest of what I want to do here should likely go into a separate PR. It's rather extensive |
ping @nodejs/crypto |
@jasnell why is this semver-major? |
While moving things around it also updates to use internal/errors |
Is it something that you would like to land before Node 9? |
I'd like to yes. |
lib/internal/errors.js
Outdated
@@ -236,6 +236,7 @@ E('ERR_NO_CRYPTO', 'Node.js is not compiled with OpenSSL crypto support'); | |||
E('ERR_NO_ICU', '%s is not supported on Node.js compiled without ICU'); | |||
E('ERR_NO_LONGER_SUPPORTED', '%s is no longer supported'); | |||
E('ERR_OUTOFMEMORY', 'Out of memory'); | |||
E('ERR_OUT_OF_RANGE', (name) => `The "${name}" argument is out of range`); |
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.
nit: use printf-like syntax?
|
||
Certificate.exportChallenge = exportChallenge; | ||
Certificate.exportPublicKey = exportPublicKey; | ||
Certificate.verifySpkac = verifySpkac; |
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 should probably document these and recommend their usage since it's useless to create an instance of the class. That can be done in another PR.
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.
Might as well do it in this PR
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.
Done!
lib/internal/crypto/pbkdf2.js
Outdated
|
||
function _pbkdf2(password, salt, iterations, keylen, digest, callback) { | ||
|
||
if (typeof digest !== 'string') |
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.
If I'm reading the C++ code correctly, with the previous undefined check, if a digest that is neither undefined
nor a string is passed, a default value would be used. That includes null
. Is this something that we want to break?
Lines 5392 to 5403 in bf1ca8f
if (args[4]->IsString()) { | |
node::Utf8Value digest_name(env->isolate(), args[4]); | |
digest = EVP_get_digestbyname(*digest_name); | |
if (digest == nullptr) { | |
type_error = "Bad digest name"; | |
goto err; | |
} | |
} | |
if (digest == nullptr) { | |
digest = EVP_sha1(); | |
} |
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.
you're right. this should allow digest
to be null
so it will default. Will fix.
lib/internal/errors.js
Outdated
@@ -110,6 +110,8 @@ E('ERR_CHILD_CLOSED_BEFORE_REPLY', 'Child closed before reply received'); | |||
E('ERR_CONSOLE_WRITABLE_STREAM', | |||
'Console expects a writable stream instance for %s'); | |||
E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s'); | |||
E('ERR_CRYPTO_ECDH_INVALID_FORMAT', | |||
(format) => `Invalid ECDH format: ${format}`); |
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.
nit: use printf-like syntax?
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 can, yeah, I tend to prefer this way but it's no biggie for me :-)
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 don't mind keeping it this way if that's what you prefer 👍
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.
nah... I'll change it. It's no biggie and I don't feel strongly about it
@targos .. updated! |
Thanks. LGTM. A CITGM run wouldn't hurt IMO. |
Not seeing anything of significant concern in CITGM. regular CI is still running. |
Btw, all... A big reason I'm going through all this is to work towards support for async crypto API variants. I will likely only pursue it, however, once async iterators have landed. The following is an example of what I'm looking to achieve: const crypto = require('crypto');
async function hash(alg, data, enc) {
const hash = crypto.createHash(alg);
for await (const chunk of data) {
hash.update(chunk);
}
return hash.digest(enc);
}
var n = 0;
async function* dataIterator() {
while (n++ < 10)
yield `testing${n}`;
}
const p = hash('sha256', dataIterator(), 'hex');
p.then(console.log).catch(console.error); The async This, of course, does not really do much to help with performance on the TLS side, but it does provide a nice Promise-oriented API. |
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.
One question, otherwise LGTM.
src/node_crypto.cc
Outdated
@@ -5568,7 +5568,9 @@ void RandomBytesCheck(RandomBytesRequest* req, Local<Value> (*argv)[2]) { | |||
memcpy(buf, data, req->size()); | |||
(*argv)[1] = buffer; | |||
} else { | |||
(*argv)[1] = Buffer::New(req->env(), data, size).ToLocalChecked(); | |||
auto cb = [](char* data, void* hint) {}; | |||
(*argv)[1] = Buffer::New(req->env(), data, size, cb, nullptr) |
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.
Huh? Why?
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 it segfaults with a double free without it!
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.
so this particular line of code was not being exercised previously because the input was always a Uint8Array. The if check above would always be true. If any of thing is passed, however, the Buffer would segfault when it tried to GC due to a double free.
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 will say that I'm not entirely sure this is the right approach to take on this! :-) (Copy might be better but I'm not entirely sure about that either)
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 challenge here, btw, is that we have two TypedArray instances being created that cover the same data space... thinking about it now, copying the data would be best.)
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.
@jasnell What happens if the original Uint8Array
is freed before this one?
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, that's the exact weirdness I'm looking at. Looking at it again, I've just changed the if block above to s/isUint8Array/isArrayBufferView and everything works as it should :-)
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.
LGTM
small linting error fixed. Landing shortly. |
* Split single monolithic file into multiple * Make Certificate methods static * Allow randomFill(Sync) to use any ArrayBufferView * Use internal/errors throughout * Improve arg validation in Hash/Hmac * Doc updates
* Split single monolithic file into multiple * Make Certificate methods static * Allow randomFill(Sync) to use any ArrayBufferView * Use internal/errors throughout * Improve arg validation in Hash/Hmac * Doc updates PR-URL: #15231 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Fedor Indutny <[email protected]>
Landed in c75f87c |
The "notable change" here is that |
* Split single monolithic file into multiple * Make Certificate methods static * Allow randomFill(Sync) to use any ArrayBufferView * Use internal/errors throughout * Improve arg validation in Hash/Hmac * Doc updates PR-URL: nodejs/node#15231 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Fedor Indutny <[email protected]>
* Split single monolithic file into multiple * Make Certificate methods static * Allow randomFill(Sync) to use any ArrayBufferView * Use internal/errors throughout * Improve arg validation in Hash/Hmac * Doc updates PR-URL: nodejs/node#15231 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Fedor Indutny <[email protected]>
Beginning of a larger bit of work on the crypto API and implementation.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
crypto