Skip to content
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

Closed
wants to merge 1 commit into from
Closed

Conversation

jasnell
Copy link
Member

@jasnell jasnell commented Sep 6, 2017

Beginning of a larger bit of work on the crypto API and implementation.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

crypto

@nodejs-github-bot nodejs-github-bot added the lib / src Issues and PRs related to general changes in the lib or src directory. label Sep 6, 2017
@jasnell jasnell added semver-major PRs that contain breaking changes and should be released in the next major version. crypto Issues and PRs related to the crypto subsystem. lib / src Issues and PRs related to general changes in the lib or src directory. and removed lib / src Issues and PRs related to general changes in the lib or src directory. labels Sep 6, 2017
@Trott
Copy link
Member

Trott commented Sep 7, 2017

Should this be labeled in progress?

@jasnell
Copy link
Member Author

jasnell commented Sep 7, 2017

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

@jasnell
Copy link
Member Author

jasnell commented Sep 12, 2017

ping @nodejs/crypto
ping @nodejs/tsc

@mcollina
Copy link
Member

@jasnell why is this semver-major?

@jasnell
Copy link
Member Author

jasnell commented Sep 12, 2017

While moving things around it also updates to use internal/errors

@targos
Copy link
Member

targos commented Sep 12, 2017

Is it something that you would like to land before Node 9?

@jasnell
Copy link
Member Author

jasnell commented Sep 13, 2017

I'd like to yes.

@@ -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`);
Copy link
Member

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;
Copy link
Member

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.

Copy link
Member Author

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!


function _pbkdf2(password, salt, iterations, keylen, digest, callback) {

if (typeof digest !== 'string')
Copy link
Member

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?

node/src/node_crypto.cc

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();
}

Copy link
Member Author

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.

@@ -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}`);
Copy link
Member

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?

Copy link
Member Author

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 :-)

Copy link
Member

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 👍

Copy link
Member Author

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

@jasnell
Copy link
Member Author

jasnell commented Sep 13, 2017

@targos .. updated!

@targos
Copy link
Member

targos commented Sep 13, 2017

Thanks. LGTM.

A CITGM run wouldn't hurt IMO.

@jasnell
Copy link
Member Author

jasnell commented Sep 13, 2017

@jasnell
Copy link
Member Author

jasnell commented Sep 13, 2017

Not seeing anything of significant concern in CITGM. regular CI is still running.

@jasnell
Copy link
Member Author

jasnell commented Sep 13, 2017

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 hash function above returns a Promise that uses an async iterator to feed data into the hash. async variants of hash, hmac, sign, verify, cipher and decipher are all trivially possible with this approach.

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.

Copy link
Member

@indutny indutny left a 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.

@@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh? Why?

Copy link
Member Author

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!

Copy link
Member Author

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.

Copy link
Member Author

@jasnell jasnell Sep 14, 2017

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)

Copy link
Member Author

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.)

Copy link
Member

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?

Copy link
Member Author

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 :-)

@addaleax addaleax mentioned this pull request Sep 14, 2017
3 tasks
@jasnell
Copy link
Member Author

jasnell commented Sep 14, 2017

@indutny and @addaleax ... updated!

Copy link
Member

@indutny indutny left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jasnell
Copy link
Member Author

jasnell commented Sep 15, 2017

Thank you @indutny and @targos ... I will land this on Monday if there are no objections!

@jasnell
Copy link
Member Author

jasnell commented Sep 18, 2017

@jasnell
Copy link
Member Author

jasnell commented Sep 18, 2017

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
jasnell added a commit that referenced this pull request Sep 18, 2017
* 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 <targos@protonmail.com>
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
@jasnell
Copy link
Member Author

jasnell commented Sep 18, 2017

Landed in c75f87c

@jasnell jasnell closed this Sep 18, 2017
@jasnell jasnell added the notable-change PRs with changes that should be highlighted in changelogs. label Sep 18, 2017
@jasnell
Copy link
Member Author

jasnell commented Sep 18, 2017

The "notable change" here is that randomFill and randomFillSync can now fill any TypedArray with random data, it's not just limited to Buffer and Uint8Array any more.

Qard pushed a commit to Qard/ayo that referenced this pull request Sep 21, 2017
* 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 <targos@protonmail.com>
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Qard pushed a commit to Qard/ayo that referenced this pull request Sep 21, 2017
* 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 <targos@protonmail.com>
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
@bengl bengl mentioned this pull request Sep 28, 2017
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
crypto Issues and PRs related to the crypto subsystem. lib / src Issues and PRs related to general changes in the lib or src directory. notable-change PRs with changes that should be highlighted in changelogs. semver-major PRs that contain breaking changes and should be released in the next major version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants