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: add support for AES-CCM #18138

Closed
wants to merge 5 commits into from

Conversation

tniessen
Copy link
Member

@tniessen tniessen commented Jan 14, 2018

CCM ("Counter with Cipher Block Chaining-Message Authentication Code")

OpenSSL currently supports two AEAD algorithms, GCM and CCM. While node has supported GCM for years, CCM is a bit more difficult to implement and use.

Currently, this PR implements an API similar to what was proposed by @brycekahle in #2383:

  • createCipher, createDecipher, createCipheriv and createDecipheriv accept an option authTagLength. While the tag length is not relevant for GCM, it is for CCM. (And yes, using CCM with createCipher / createDecipher is inherently insecure.)
  • setAAD accepts additional options as an optional second argument. The only currently recognized option is plaintextLength, which specifies the length of the plaintext / ciphertext in bytes.

There are some critical aspects:

  • The current implementation always requires the authTagLength option when using CCM. Not providing this option causes an error to be thrown by create(De|C)ipher(iv)?.
  • Similarly, the current implementation requires the plaintextLength option when using CCM if and only if AAD is provided. The explanation is simple, the length of the plaintext is encoded into the first block of the cipher, and thus must be available before performing an update (and before encoding the AAD).
  • update() can only be called once. Again, the reason is very simple, CCM operates in what is sometimes called a "packet" or "offline mode", meaning that all data is processed at once.
  • It seems like OpenSSL does not support CCM decryption without the authentication tag.

Some of these points can be relaxed:

  • By providing a default value for the authTagLength option, we could simplify the migration to this "new" AEAD. We could stick with the default value used within OpenSSL (12 bytes) or upgrade to 16 bytes (as e.g. pycryptodome does).
  • By caching the data passed to setAAD until update is called, we take away the need to specify the plaintextLength. All data still needs to be passed to update in a single call.

We should discuss both, but it should be possible to add these features later on if they turn out to be good ideas (as long as they don't break anything). Aside from that, I am always happy about feedback and suggestions!

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

crypto

@tniessen tniessen added semver-major PRs that contain breaking changes and should be released in the next major version. notable-change PRs with changes that should be highlighted in changelogs. labels Jan 14, 2018
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Jan 14, 2018
@tniessen tniessen changed the title [WIP] crypto: add support for AES-CCM crypto: add support for AES-CCM Jan 15, 2018
@tniessen tniessen added the crypto Issues and PRs related to the crypto subsystem. label Jan 17, 2018
@tniessen
Copy link
Member Author

Rebased due to a conflict with #18017. @nodejs/crypto and @nodejs/tsc, is there anything I can do to aid in discussing and reviewing this change?

Copy link
Member

@jasnell jasnell left a comment

Choose a reason for hiding this comment

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

Generally LGTM but needs docs.

@tniessen
Copy link
Member Author

@jasnell Thanks! Yes, documentation is still pending, I just want to discuss the general API before documenting its details :)

@jasnell
Copy link
Member

jasnell commented Jan 22, 2018

ping @nodejs/tsc

doc/api/crypto.md Outdated Show resolved Hide resolved
doc/api/crypto.md Outdated Show resolved Hide resolved
doc/api/crypto.md Outdated Show resolved Hide resolved
lib/internal/crypto/cipher.js Outdated Show resolved Hide resolved
lib/internal/crypto/cipher.js Outdated Show resolved Hide resolved
src/node_crypto.cc Outdated Show resolved Hide resolved
src/node_crypto.cc Outdated Show resolved Hide resolved
src/node_crypto.cc Outdated Show resolved Hide resolved
src/node_crypto.cc Outdated Show resolved Hide resolved
src/node_crypto.cc Outdated Show resolved Hide resolved
@tniessen tniessen force-pushed the ccm-is-still-weird branch 2 times, most recently from be707a9 to 78d0441 Compare January 23, 2018 23:55
@tniessen
Copy link
Member Author

@bnoordhuis Thank you for your thorough review! I tried to address all suggestions (except #18138 (comment)), PTAL if you can.

src/node_crypto.cc Outdated Show resolved Hide resolved
src/node_crypto.cc Outdated Show resolved Hide resolved
@tniessen
Copy link
Member Author

tniessen commented Jan 26, 2018

@tniessen
Copy link
Member Author

tniessen commented Jan 29, 2018

Seems like I found the cause of the FIPS failure thanks to gdb. openssl/openssl@9cca7be introduced a slight change to the CCM API back in 2015, and this change never made it into the FIPS release. Without this commit, the authentication tag must be specified along with its length when decrypting, and I believe this makes it incompatible with our current design. The only option I can think of right now is to make CCM unsupported when in FIPS mode and to throw an error in create(De|C)ipher(iv)?, what do you think @bnoordhuis @jasnell? Is there any way to amend the proposed implementation to work around this problem?

This is the conflicting line:

(gdb) f
#0  aes_ccm_ctrl (c=0x3d1b490, type=17, arg=8, ptr=0x0) at e_aes.c:1284
1284                    if ((c->encrypt && ptr) || (!c->encrypt && !ptr))
(gdb) next
1285                            return 0;

There is one other thing I came across, OpenSSL 1.1.0 seems to have fixed the problem with EVP_CipherFinal in CCM mode, see openssl/openssl#2550 (comment). We should revisit that part of CCM once we transition to a newer version.

@bnoordhuis
Copy link
Member

make CCM unsupported when in FIPS mode

I'm okay with that.

@jasnell
Copy link
Member

jasnell commented Jan 29, 2018

I'm good with not supporting CCM in FIPS also.

@tniessen
Copy link
Member Author

For now, I only disabled CCM decryption in FIPS mode, PTAL.

@bluetiger30
Copy link

great work guys , I was waiting for this
when will this be added to node ?

@tniessen
Copy link
Member Author

@bluetiger30 This change will most likely be included in all releases beginning with node 10.0.0, but not in previous releases.

@tniessen
Copy link
Member Author

tniessen commented Apr 4, 2018

@addaleax I suggested to make it semver-minor in #18138 (comment) without response, I am okay with that. The only reason I marked this semver-major is that it can break code which incorrectly used CCM before, because we did not account for that in previous releases, e.g., if you pass invalid CCM options to createCipheriv(). That would have worked in older releases as those options would have been ignored, but it will throw now.

@Trott Trott removed the tsc-agenda Issues and PRs to discuss during the meetings of the TSC. label Apr 4, 2018
@rvagg
Copy link
Member

rvagg commented Apr 5, 2018

Yes, I'm fine with this addition and the API and being conservative on semver-major is probably a good idea although I wouldn't say it's essential.

However #19794 is more important than getting this landed for Node 10 and that PR is going to heavily impact this one. AES-CCM is going to have fairly minimal use due to the necessarily awkward API so this is pretty low priority (sorry @tniessen but I suspect you already understand this). Getting #19794 safely landed without holding it up even further is critical so I'd rather see this PR have to change to fit that PR rather than the other way around.

@tniessen could you do a quick investigation to see what changes might be required to sit on top of the 1.1.0 support @shigeki is adding?

I wouldn't discard the exact code in this PR btw, we could possibly relax the semver-major and land this on 8.x as it is now so it's not entirely useless as it is now. I think I'd prefer to wait until after 10.x goes live to make a call on 8.x support for CCM.

@tniessen
Copy link
Member Author

tniessen commented Apr 5, 2018

the necessarily awkward API

Side note: On a par with OpenSSL.

so this is pretty low priority

It indeed seems to be, the lack of TSC attention was noticeable long before 1.1.0h was even released. There haven't been any changes to this PR in weeks.

could you do a quick investigation to see what changes might be required to sit on top of the 1.1.0 support @shigeki is adding?

It lands cleanly and lite CI passes: https://ci.nodejs.org/job/node-test-commit-lite/589/

@Trott
Copy link
Member

Trott commented Apr 5, 2018

R=@danbev perhaps?

@rvagg
Copy link
Member

rvagg commented Apr 5, 2018

beautiful, if this lands cleanly with 1.1.0 then I see no other holdup, lgtm

@Trott
Copy link
Member

Trott commented Apr 5, 2018

Only CI that didn't finish last time was Linux, so here's a re-run of just that: https://ci.nodejs.org/job/node-test-commit-linux/17674/

tniessen pushed a commit to tniessen/node that referenced this pull request Apr 6, 2018
Commit 8fb4ea9 ("test: add deprecation code to expectWarning") did
not take into account that the same warning could be expected multiple
times. This bug was discovered in
nodejs#18138 and this commit adds a fix for
this issue.

Refs: nodejs#18138
@tniessen
Copy link
Member Author

tniessen commented Apr 6, 2018

Thank you, @rvagg and @Trott!

I created tniessen@a191f6b by "landing" #19766, #18138 (this PR) and #19794 (in this order) on top of the current master. Again, there were no conflicts.

Additional CI for that commit: https://ci.nodejs.org/job/node-test-commit/17477/

Unless someone objects, I intend to land this along with #19766 today.

danbev added a commit that referenced this pull request Apr 6, 2018
Commit 8fb4ea9 ("test: add deprecation code to expectWarning") did
not take into account that the same warning could be expected multiple
times. This bug was discovered in
#18138 and this commit adds a fix for
this issue.

PR-URL: #19766
Refs: #18138
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
@rvagg
Copy link
Member

rvagg commented Apr 6, 2018

CI is a mess, but not because of these changes. lgtm, let's get it merged, thanks for the hard work on this @tniessen.

tniessen added a commit that referenced this pull request Apr 6, 2018
This commit adds support for another AEAD algorithm and introduces
required API changes and extensions. Due to the design of CCM itself and
the way OpenSSL implements it, there are some restrictions when using
this mode as outlined in the updated documentation.

PR-URL: #18138
Fixes: #2383
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Shigeki Ohtsu <ohtsu@ohtsu.org>
Reviewed-By: Rod Vagg <rod@vagg.org>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
@tniessen
Copy link
Member Author

tniessen commented Apr 6, 2018

Landed in 1e07acd, there are no conflicts with #19794 according to GH interface. Thank you, everyone!

@tniessen tniessen closed this Apr 6, 2018
@tniessen tniessen added the semver-major PRs that contain breaking changes and should be released in the next major version. label Apr 6, 2018
@tniessen
Copy link
Member Author

tniessen commented Apr 6, 2018

@Trott I marked this PR as semver-major again given that multiple TSC members approved after nodejs/TSC#516 and that it landed in time for node 10. At the very least, this is semver-minor.

tniessen added a commit to tniessen/node that referenced this pull request Apr 11, 2018
tniessen added a commit that referenced this pull request Apr 13, 2018
PR-URL: #19945
Refs: #18138
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
jasnell pushed a commit that referenced this pull request Apr 16, 2018
PR-URL: #19945
Refs: #18138
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
tniessen added a commit to tniessen/node that referenced this pull request Apr 26, 2018
This change replaces some constants with better alternatives which were
unavailable in OpenSSL 1.0.2.

Refs: nodejs#19794
Refs: nodejs#18138
tniessen added a commit that referenced this pull request Apr 30, 2018
This change replaces some constants with better alternatives which were
unavailable in OpenSSL 1.0.2.

PR-URL: #20339
Refs: #19794
Refs: #18138
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request May 4, 2018
This change replaces some constants with better alternatives which were
unavailable in OpenSSL 1.0.2.

PR-URL: #20339
Refs: #19794
Refs: #18138
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@tniessen
Copy link
Member Author

openssl/openssl@67c81ec just landed and slightly relaxes one of the difficulties in using CCM, thus reducing the API differences between CCM and other modes slightly. I am not sure if it is worth cherry-picking since I assume we usually only cherry-pick security stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. 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.