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

v15.9.0 proposal #37405

Closed
wants to merge 108 commits into from
Closed
Changes from 1 commit
Commits
Show all changes
108 commits
Select commit Hold shift + click to select a range
d2a487e
Revert "stream: fix .end() error propagation"
mcollina Jan 25, 2021
67c9a8e
doc: improve promise terminology
benjamingr Feb 2, 2021
9946c11
src: read exactly two tokens from Linux THP sysfs config
jayaddison Jan 25, 2021
13fe17c
test: split heap snapshot limit tests
Trott Feb 2, 2021
dc84c18
tls: add ability to get cert/peer cert as X509Certificate object
jasnell Jan 26, 2021
011910b
crypto: add keyObject.export() 'jwk' format option
panva Jan 26, 2021
e63b380
deps: upgrade npm to 7.5.2
ruyadorno Feb 2, 2021
1fea051
stream: improve Readable.from error handling
benjamingr Jan 31, 2021
ff02e5e
doc: fix 404 links in module.md
aduh95 Feb 3, 2021
3f815d9
doc: fix pr-url for DEP0148
aduh95 Feb 3, 2021
745aad7
tools: update ESLint to 7.19.0
cjihrig Jan 31, 2021
bfe0b46
src: simplify calls to BN_bin2bn in prime gen
tniessen Feb 1, 2021
4ebe38b
timers: introduce setInterval async iterator
Linkgoron Jan 30, 2021
5157525
test: remove flaky designation for test-http2-large-file
Trott Jan 31, 2021
b87c0d6
doc: fix typo in cli.md
0xkalvin Feb 3, 2021
4cdc5ea
http: fix ClientRequest unhandled errors
ronag Jan 16, 2021
48ce1eb
src: fix warning in string_search.h
RaisinTen Jan 30, 2021
eb08afd
doc: fix typo in globals.md
RaisinTen Feb 4, 2021
eb7ec1b
timers: remove flaky setInterval test
Feb 4, 2021
c377834
repl: refactor to avoid unsafe array iteration
aduh95 Feb 2, 2021
52c40c7
doc: fix CHANGELOG_ARCHIVE table of contents
aduh95 Feb 4, 2021
5906e85
doc: fix version number for DEP006
aduh95 Feb 4, 2021
acd087d
fs: add AbortSignal support to watch
benjamingr Feb 2, 2021
2a5f67b
src: refactor bookkeeping of bootstrap status
joyeecheung Jan 28, 2021
337b4e7
src: put (de)serialization code into node_snapshotable.h/cc
joyeecheung Jan 28, 2021
8a25405
doc: fix accommodate typos
cjihrig Feb 4, 2021
66a14d3
vm: add importModuleDynamically option to compileFunction
devsnek Sep 25, 2020
655d196
fs: use a default callback for fs.close()
jasnell Feb 1, 2021
0702d60
doc: fix webcrypto HMAC generateKey example
panva Feb 3, 2021
596bfb3
doc: mention CryptoKey in port.postMessage()
panva Feb 3, 2021
aebe532
doc: consistent webcrypto `node.keyObject` format
panva Feb 3, 2021
760f126
doc: mark Certificate methods as static, add missing KeyObject.from
panva Feb 3, 2021
3e2746f
crypto: remove webcrypto "DSA" JWK Key Type operations
panva Feb 3, 2021
48a634e
test: rename n-api to node-api
Feb 3, 2021
c302450
fs: allow passing negative zero fd
RaisinTen Jan 29, 2021
3fee5b2
repl: add auto‑completion for dynamic import calls
ExE-Boss Feb 2, 2021
dc38dd2
timers: fix unsafe array iteration
RaisinTen Feb 4, 2021
9dac99a
crypto: fix and simplify prime option validation
tniessen Feb 1, 2021
acabe08
lib: add weak event handlers
benjamingr Feb 4, 2021
354df9e
src: use make_shared for safe allocation
yashLadha Jan 30, 2021
a6053dc
src: add context for TODO comment in env.cc
yashLadha Jan 30, 2021
20c65b0
deps: V8: backport dfcf1e86fac0
targos Feb 7, 2021
eb0daae
tools: fix d8 macOS build
targos Feb 3, 2021
3079a78
src: avoid implicit type conversions
targos Jan 30, 2021
54d36b0
src: rename binding_data_name to type_name in BindingData
joyeecheung Jan 28, 2021
1a9bcdf
src: refactor v8 binding
joyeecheung Jan 28, 2021
d8d851a
tools: bump remark-present-lint-node from 2.0.0 to 2.0.1
Trott Feb 8, 2021
ebf3597
doc: rename N-API to Node-API
Feb 7, 2021
7c51cec
util: use assert for unreachable code
Trott Feb 6, 2021
7da1c9b
doc: use sentence case for headers in BUILDING.md
Trott Feb 6, 2021
47c4f1f
doc: use sentence case in README headers
Trott Feb 6, 2021
0e887ca
doc: fix typo in console.md
marsonya Feb 8, 2021
5a4288e
doc: fix typo in crypto.md
marsonya Feb 8, 2021
cd50e93
doc: warn about using strings as inputs in crypto
tniessen Feb 6, 2021
a693baa
module: use optional chaining in cjs/loader.js
RaisinTen Feb 5, 2021
55fd6b6
crypto: avoid infinite loops in prime generation
tniessen Feb 3, 2021
8483de4
test: only run prime test with supported OpenSSL
tniessen Feb 5, 2021
f6f9af6
lib: fix WebIDL `object` and dictionary type conversion
ExE-Boss Jan 24, 2021
6db5e79
tools: add GitHub Action linter for pr-url
aduh95 Feb 4, 2021
d1c1724
deps: upgrade npm to 7.5.3
ruyadorno Feb 8, 2021
85bed2e
doc: fix misnamed SHASUMS256.txt name in README.md
marsonya Feb 7, 2021
e28ea89
crypto: fix subtle.importKey JWK OKP public key import
panva Feb 6, 2021
42cc33c
src: add mutex to ManagedEVPPKey class
danbev Jan 7, 2021
061939d
node-api: allow retrieval of add-on file name
Feb 3, 2021
d63ac28
http: explain the unused argument in IncomingMessage._read
Ayase-252 Feb 8, 2021
f5d1bf9
http: explain the possibilty of refactor unused argument
Ayase-252 Feb 10, 2021
e28fa6c
src: fix return type of method in string_search.h
RaisinTen Feb 1, 2021
3464c9f
doc: discourage error event
benjamingr Feb 7, 2021
50e81ba
deps: V8: cherry-pick 0c8b6e415c30
matinzd Feb 8, 2021
facf3a5
doc: fix description of hasSubscribers
tniessen Feb 11, 2021
7d8fd3f
doc: refactor fs docs structure
jasnell Feb 1, 2021
d96a97a
module: make synthetic module evaluation steps return a Promise to su…
dandclark Feb 9, 2021
1ff375b
tools: avoid pending deprecation in doc generator
targos Feb 7, 2021
8487184
child_process: fix bad abort signal leak
Feb 6, 2021
15804e0
errors: align source-map stacks with spec
bcoe Feb 6, 2021
1fc8307
test: re-implement promises.setInterval() test robustly
Trott Feb 4, 2021
d0f1ff5
async_hooks: set unhandledRejection async context
Feb 7, 2021
fbcab10
url: move `URLSearchParams` method definitions
ExE-Boss Jan 6, 2021
676f696
url: fix definitions of `URL`/`SearchParams` methods and accessors
ExE-Boss Jan 5, 2021
a483c28
tools: fix lint-pr-url message
aduh95 Feb 10, 2021
cdd2fe5
doc: fix typo in /api/dns.md
marsonya Feb 10, 2021
804e7ae
doc: add version metadata for packages features
aduh95 Feb 9, 2021
f253cb9
doc: fix typo in buffer.md
marsonya Feb 7, 2021
85b1476
doc: fix typo in deprecations.md
marsonya Feb 8, 2021
c4faa39
perf_hooks: introduce createHistogram
jasnell Jan 30, 2021
610b29b
doc: fix performanceEntry.flags style format
chengcyber Feb 8, 2021
189ce39
doc: apply sentence case to release doc headers
Trott Feb 13, 2021
62b2648
doc: apply sentence-consistently in C++ style guide
Trott Feb 13, 2021
392c86d
doc: use sentence case in benchmark doc
Trott Feb 13, 2021
1e99175
doc: alphabetize crypto.* methods
Trott Feb 13, 2021
3db1b30
meta: update README releases section
Feb 11, 2021
e2a2bab
doc: link PACKAGE_EXPORTS_RESOLVE to ESM section
ugultopu Jan 30, 2021
f5e4625
doc: fix backticks in crypto API docs
tniessen Feb 8, 2021
c188466
doc: fix quotes in stream docs
tniessen Feb 8, 2021
dd054ca
doc: optimize HTML rendering
aduh95 Feb 9, 2021
c160d88
buffer: add @@toStringTag to Blob
cjihrig Feb 12, 2021
b2b6411
tools: update ESLint to 7.20.0
cjihrig Feb 12, 2021
88d3f74
fs: add fsPromises.watch()
jasnell Feb 1, 2021
799b2d5
policy: fix cascade getting scope
bmeck Feb 9, 2021
6d53e79
repl: refactor to avoid unsafe array iteration
aduh95 Feb 12, 2021
05a16e7
worker: refactor to avoid unsafe array iteration
aduh95 Feb 12, 2021
c0eadef
deps: upgrade to libuv 1.41.0
cjihrig Feb 13, 2021
b09d21b
test: enable no-restricted-syntax rule for test-timers-promisified
Trott Feb 13, 2021
d808db2
doc: add dmabupt to collaborators
dmabupt Feb 15, 2021
c6198fd
lib: simplify check in child_process
RaisinTen Feb 14, 2021
2e1f1c6
tools: refactor prefer-primordials
aduh95 Nov 7, 2020
b5692b4
test: fix test-doctool-html
aduh95 Feb 16, 2021
5133659
2021-02-16, Version 15.9.0 (Current)
danielleadams Feb 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
crypto: add keyObject.export() 'jwk' format option
Adds [JWK](https://tools.ietf.org/html/rfc7517) keyObject.export format
option.

Supported key types: `ec`, `rsa`, `ed25519`, `ed448`, `x25519`, `x448`,
and symmetric keys, resulting in JWK `kty` (Key Type) values `EC`,
`RSA`, `OKP`, and `oct`.

`rsa-pss` is not supported since the JWK format does not support
PSS Parameters.

`EC` JWK curves supported are `P-256`, `secp256k1`, `P-384`, and `P-521`

PR-URL: #37081
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Tobias Nießen <[email protected]>
panva authored and danielleadams committed Feb 16, 2021

Verified

This commit was signed with the committer’s verified signature.
santigimeno Santiago Gimeno
commit 011910b424111dec4dcd6915624d55c14ab89edd
25 changes: 16 additions & 9 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
@@ -1348,35 +1348,41 @@ keys.
### `keyObject.export([options])`
<!-- YAML
added: v11.6.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/37081
description: Added support for `'jwk'` format.
-->

* `options`: {Object}
* Returns: {string | Buffer}
* Returns: {string | Buffer | Object}

For symmetric keys, this function allocates a `Buffer` containing the key
material and ignores any options.
For symmetric keys, the following encoding options can be used:

For asymmetric keys, the `options` parameter is used to determine the export
format.
* `format`: {string} Must be `'buffer'` (default) or `'jwk'`.

For public keys, the following encoding options can be used:

* `type`: {string} Must be one of `'pkcs1'` (RSA only) or `'spki'`.
* `format`: {string} Must be `'pem'` or `'der'`.
* `format`: {string} Must be `'pem'`, `'der'`, or `'jwk'`.

For private keys, the following encoding options can be used:

* `type`: {string} Must be one of `'pkcs1'` (RSA only), `'pkcs8'` or
`'sec1'` (EC only).
* `format`: {string} Must be `'pem'` or `'der'`.
* `format`: {string} Must be `'pem'`, `'der'`, or `'jwk'`.
* `cipher`: {string} If specified, the private key will be encrypted with
the given `cipher` and `passphrase` using PKCS#5 v2.0 password based
encryption.
* `passphrase`: {string | Buffer} The passphrase to use for encryption, see
`cipher`.

When PEM encoding was selected, the result will be a string, otherwise it will
be a buffer containing the data encoded as DER.
The result type depends on the selected encoding format, when PEM the
result is a string, when DER it will be a buffer containing the data
encoded as DER, when [JWK][] it will be an object.

When [JWK][] encoding format was selected, all other encoding options are
ignored.

PKCS#1, SEC1, and PKCS#8 type keys can be encrypted by using a combination of
the `cipher` and `format` options. The PKCS#8 `type` can be used with any
@@ -4355,6 +4361,7 @@ See the [list of SSL OP Flags][] for details.
[Crypto constants]: #crypto_crypto_constants_1
[HTML 5.2]: https://www.w3.org/TR/html52/changes.html#features-removed
[HTML5's `keygen` element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/keygen
[JWK]: https://tools.ietf.org/html/rfc7517
[NIST SP 800-131A]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar1.pdf
[NIST SP 800-132]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
14 changes: 14 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
@@ -919,6 +919,18 @@ added: v15.0.0

Initialization of an asynchronous crypto operation failed.

<a id="ERR_CRYPTO_JWK_UNSUPPORTED_CURVE"></a>
### `ERR_CRYPTO_JWK_UNSUPPORTED_CURVE`

Key's Elliptic Curve is not registered for use in the
[JSON Web Key Elliptic Curve Registry][].

<a id="ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE"></a>
### `ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE`

Key's Asymmetric Key Type is not registered for use in the
[JSON Web Key Types Registry][].

<a id="ERR_CRYPTO_OPERATION_FAILED"></a>
### `ERR_CRYPTO_OPERATION_FAILED`
<!-- YAML
@@ -2716,6 +2728,8 @@ The native call from `process.cpuUsage` could not be processed.

[ES Module]: esm.md
[ICU]: intl.md#intl_internationalization_support
[JSON Web Key Elliptic Curve Registry]: https://www.iana.org/assignments/jose/jose.xhtml#web-key-elliptic-curve
[JSON Web Key Types Registry]: https://www.iana.org/assignments/jose/jose.xhtml#web-key-types
[Node.js error codes]: #nodejs-error-codes
[RFC 7230 Section 3]: https://tools.ietf.org/html/rfc7230#section-3
[Subresource Integrity specification]: https://www.w3.org/TR/SRI/#the-integrity-attribute
60 changes: 55 additions & 5 deletions lib/internal/crypto/keys.js
Original file line number Diff line number Diff line change
@@ -22,6 +22,11 @@ const {
kKeyEncodingSEC1,
} = internalBinding('crypto');

const {
validateObject,
validateOneOf,
} = require('internal/validators');

const {
codes: {
ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS,
@@ -30,6 +35,8 @@ const {
ERR_INVALID_ARG_VALUE,
ERR_OUT_OF_RANGE,
ERR_OPERATION_FAILED,
ERR_CRYPTO_JWK_UNSUPPORTED_CURVE,
ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE,
}
} = require('internal/errors');

@@ -124,13 +131,22 @@ const [
return this[kHandle].getSymmetricKeySize();
}

export() {
export(options) {
if (options !== undefined) {
validateObject(options, 'options');
validateOneOf(
options.format, 'options.format', [undefined, 'buffer', 'jwk']);
if (options.format === 'jwk') {
return this[kHandle].exportJwk({});
}
}
return this[kHandle].export();
}
}

const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails');
const kAsymmetricKeyJWKProperties = Symbol('kAsymmetricKeyJWKProperties');

function normalizeKeyDetails(details = {}) {
if (details.publicExponent !== undefined) {
@@ -163,18 +179,44 @@ const [
return {};
}
}

[kAsymmetricKeyJWKProperties]() {
switch (this.asymmetricKeyType) {
case 'rsa': return {};
case 'ec':
switch (this.asymmetricKeyDetails.namedCurve) {
case 'prime256v1': return { crv: 'P-256' };
case 'secp256k1': return { crv: 'secp256k1' };
case 'secp384r1': return { crv: 'P-384' };
case 'secp521r1': return { crv: 'P-521' };
default:
throw new ERR_CRYPTO_JWK_UNSUPPORTED_CURVE(
this.asymmetricKeyDetails.namedCurve);
}
case 'ed25519': return { crv: 'Ed25519' };
case 'ed448': return { crv: 'Ed448' };
case 'x25519': return { crv: 'X25519' };
case 'x448': return { crv: 'X448' };
default:
throw new ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE();
}
}
}

class PublicKeyObject extends AsymmetricKeyObject {
constructor(handle) {
super('public', handle);
}

export(encoding) {
export(options) {
if (options && options.format === 'jwk') {
const properties = this[kAsymmetricKeyJWKProperties]();
return this[kHandle].exportJwk(properties);
}
const {
format,
type
} = parsePublicKeyEncoding(encoding, this.asymmetricKeyType);
} = parsePublicKeyEncoding(options, this.asymmetricKeyType);
return this[kHandle].export(format, type);
}
}
@@ -184,13 +226,21 @@ const [
super('private', handle);
}

export(encoding) {
export(options) {
if (options && options.format === 'jwk') {
if (options.passphrase !== undefined) {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
'jwk', 'does not support encryption');
}
const properties = this[kAsymmetricKeyJWKProperties]();
return this[kHandle].exportJwk(properties);
}
const {
format,
type,
cipher,
passphrase
} = parsePrivateKeyEncoding(encoding, this.asymmetricKeyType);
} = parsePrivateKeyEncoding(options, this.asymmetricKeyType);
return this[kHandle].export(format, type, cipher, passphrase);
}
}
2 changes: 2 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
@@ -839,6 +839,8 @@ E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s', TypeError);
E('ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE',
'Invalid key object type %s, expected %s.', TypeError);
E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s', Error);
E('ERR_CRYPTO_JWK_UNSUPPORTED_CURVE', 'Unsupported JWK EC curve: %s.', Error);
E('ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE', 'Unsupported JWK Key Type.', Error);
E('ERR_CRYPTO_PBKDF2_ERROR', 'PBKDF2 error', Error);
E('ERR_CRYPTO_SCRYPT_INVALID_PARAMETER', 'Invalid scrypt parameter', Error);
E('ERR_CRYPTO_SCRYPT_NOT_SUPPORTED', 'Scrypt algorithm not supported', Error);
42 changes: 41 additions & 1 deletion test/fixtures/keys/Makefile
Original file line number Diff line number Diff line change
@@ -75,6 +75,14 @@ all: \
ed448_public.pem \
x448_private.pem \
x448_public.pem \
ec_p256_private.pem \
ec_p256_public.pem \
ec_p384_private.pem \
ec_p384_public.pem \
ec_p521_private.pem \
ec_p521_public.pem \
ec_secp256k1_private.pem \
ec_secp256k1_public.pem \

#
# Create Certificate Authority: ca1
@@ -663,7 +671,7 @@ rsa_cert_foafssl_b.modulus: rsa_cert_foafssl_b.crt

# Have to parse out the hex exponent
rsa_cert_foafssl_b.exponent: rsa_cert_foafssl_b.crt
openssl x509 -in rsa_cert_foafssl_b.crt -text | grep -o 'Exponent:.*' | sed 's/\(.*(\|).*\)//g' > rsa_cert_foafssl_b.exponent
openssl x509 -in rsa_cert_foafssl_b.crt -text | grep -o 'Exponent:.*' | sed 's/\(.*(\|).*\)//g' > rsa_cert_foafssl_b.exponent

# openssl outputs `SPKAC=[SPKAC]`. That prefix needs to be removed to work with node
rsa_spkac.spkac: rsa_private.pem
@@ -733,6 +741,38 @@ x448_private.pem:
x448_public.pem: x448_private.pem
openssl pkey -in x448_private.pem -pubout -out x448_public.pem

ec_p256_private.pem:
openssl ecparam -name prime256v1 -genkey -noout -out sec1_ec_p256_private.pem
openssl pkcs8 -topk8 -nocrypt -in sec1_ec_p256_private.pem -out ec_p256_private.pem
rm sec1_ec_p256_private.pem

ec_p256_public.pem: ec_p256_private.pem
openssl ec -in ec_p256_private.pem -pubout -out ec_p256_public.pem

ec_p384_private.pem:
openssl ecparam -name secp384r1 -genkey -noout -out sec1_ec_p384_private.pem
openssl pkcs8 -topk8 -nocrypt -in sec1_ec_p384_private.pem -out ec_p384_private.pem
rm sec1_ec_p384_private.pem

ec_p384_public.pem: ec_p384_private.pem
openssl ec -in ec_p384_private.pem -pubout -out ec_p384_public.pem

ec_p521_private.pem:
openssl ecparam -name secp521r1 -genkey -noout -out sec1_ec_p521_private.pem
openssl pkcs8 -topk8 -nocrypt -in sec1_ec_p521_private.pem -out ec_p521_private.pem
rm sec1_ec_p521_private.pem

ec_p521_public.pem: ec_p521_private.pem
openssl ec -in ec_p521_private.pem -pubout -out ec_p521_public.pem

ec_secp256k1_private.pem:
openssl ecparam -name secp256k1 -genkey -noout -out sec1_ec_secp256k1_private.pem
openssl pkcs8 -topk8 -nocrypt -in sec1_ec_secp256k1_private.pem -out ec_secp256k1_private.pem
rm sec1_ec_secp256k1_private.pem

ec_secp256k1_public.pem: ec_secp256k1_private.pem
openssl ec -in ec_secp256k1_private.pem -pubout -out ec_secp256k1_public.pem

clean:
rm -f *.pfx *.pem *.srl ca2-database.txt ca2-serial fake-startcom-root-serial *.print *.old fake-startcom-root-issued-certs/*.pem
@> fake-startcom-root-database.txt
5 changes: 5 additions & 0 deletions test/fixtures/keys/ec_p256_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgDxBsPQPIgMuMyQbx
zbb9toew6Ev6e9O6ZhpxLNgmAEqhRANCAARfSYxhH+6V5lIg+M3O0iQBLf+53kuE
2luIgWnp81/Ya1Gybj8tl4tJVu1GEwcTyt8hoA7vRACmCHnI5B1+bNpS
-----END PRIVATE KEY-----
4 changes: 4 additions & 0 deletions test/fixtures/keys/ec_p256_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEX0mMYR/uleZSIPjNztIkAS3/ud5L
hNpbiIFp6fNf2GtRsm4/LZeLSVbtRhMHE8rfIaAO70QApgh5yOQdfmzaUg==
-----END PUBLIC KEY-----
6 changes: 6 additions & 0 deletions test/fixtures/keys/ec_p384_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PRIVATE KEY-----
MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDB3B+4e4C1OUxGftkEI
Gb/SCulzUP/iE940CB6+B6WWO4LT76T8sMWiwOAGUsuZmyKhZANiAASE43efMYmC
/7Tx90elDGBEkVnOUr4ZkMZrl/cqe8zfVy++MmayPhR46Ah3LesMCNV+J0eG15w0
IYJ8uqasuMN6drU1LNbNYfW7+hR0woajldJpvHMPv7wlnGOlzyxH1yU=
-----END PRIVATE KEY-----
5 changes: 5 additions & 0 deletions test/fixtures/keys/ec_p384_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEhON3nzGJgv+08fdHpQxgRJFZzlK+GZDG
a5f3KnvM31cvvjJmsj4UeOgIdy3rDAjVfidHhtecNCGCfLqmrLjDena1NSzWzWH1
u/oUdMKGo5XSabxzD7+8JZxjpc8sR9cl
-----END PUBLIC KEY-----
8 changes: 8 additions & 0 deletions test/fixtures/keys/ec_p521_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAEghuafcab9jXW4gO
QLeDaKOlHEiskQFjiL8klijk6i6DNOXcFfaJ9GW48kxpodw16ttAf9Z1WQstfzpK
GUetHImhgYkDgYYABAGixYI8Gbc5zNze6rH2/OmsFV3unOnY1GDqG9RTfpJZXpL9
ChF1dG8HA4zxkM+X+jMSwm4THh0Wr1Euj9dK7E7QZwHd35XsQXgH13Hjc0QR9dvJ
BWzlg+luNTY8CkaqiBdur5oFv/AjpXRimYxZDkhAEsTwXLwNohSUVMkN8IQtNI9D
aQ==
-----END PRIVATE KEY-----
6 changes: 6 additions & 0 deletions test/fixtures/keys/ec_p521_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBosWCPBm3Oczc3uqx9vzprBVd7pzp
2NRg6hvUU36SWV6S/QoRdXRvBwOM8ZDPl/ozEsJuEx4dFq9RLo/XSuxO0GcB3d+V
7EF4B9dx43NEEfXbyQVs5YPpbjU2PApGqogXbq+aBb/wI6V0YpmMWQ5IQBLE8Fy8
DaIUlFTJDfCELTSPQ2k=
-----END PUBLIC KEY-----
5 changes: 5 additions & 0 deletions test/fixtures/keys/ec_secp256k1_private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgc34ocwTwpFa9NZZh3l88
qXyrkoYSxvC0FEsU5v1v4IOhRANCAARw7OEVKlbGFqUJtY10/Yf/JSR0LzUL1PZ1
4Ol/ErujAPgNwwGU5PSD6aTfn9NycnYB2hby9XwB2qF3+El+DV8q
-----END PRIVATE KEY-----
4 changes: 4 additions & 0 deletions test/fixtures/keys/ec_secp256k1_public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcOzhFSpWxhalCbWNdP2H/yUkdC81C9T2
deDpfxK7owD4DcMBlOT0g+mk35/TcnJ2AdoW8vV8Adqhd/hJfg1fKg==
-----END PUBLIC KEY-----
241 changes: 231 additions & 10 deletions test/parallel/test-crypto-key-objects.js
Original file line number Diff line number Diff line change
@@ -18,7 +18,9 @@ const {
publicDecrypt,
publicEncrypt,
privateDecrypt,
privateEncrypt
privateEncrypt,
getCurves,
generateKeyPairSync
} = require('crypto');

const fixtures = require('../common/fixtures');
@@ -156,6 +158,56 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
});
}

const jwk = {
e: 'AQAB',
n: 't9xYiIonscC3vz_A2ceR7KhZZlDu_5bye53nCVTcKnWd2seY6UAdKersX6njr83Dd5OVe' +
'1BW_wJvp5EjWTAGYbFswlNmeD44edEGM939B6Lq-_8iBkrTi8mGN4YCytivE24YI0D4XZ' +
'MPfkLSpab2y_Hy4DjQKBq1ThZ0UBnK-9IhX37Ju_ZoGYSlTIGIhzyaiYBh7wrZBoPczIE' +
'u6et_kN2VnnbRUtkYTF97ggcv5h-hDpUQjQW0ZgOMcTc8n-RkGpIt0_iM_bTjI3Tz_gsF' +
'di6hHcpZgbopPL630296iByyigQCPJVzdusFrQN5DeC-zT_nGypQkZanLb4ZspSx9Q',
d: 'ktnq2LvIMqBj4txP82IEOorIRQGVsw1khbm8A-cEpuEkgM71Yi_0WzupKktucUeevQ5i0' +
'Yh8w9e1SJiTLDRAlJz66kdky9uejiWWl6zR4dyNZVMFYRM43ijLC-P8rPne9Fz16IqHFW' +
'5VbJqA1xCBhKmuPMsD71RNxZ4Hrsa7Kt_xglQTYsLbdGIwDmcZihId9VGXRzvmCPsDRf2' +
'fCkAj7HDeRxpUdEiEDpajADc-PWikra3r3b40tVHKWm8wxJLivOIN7GiYXKQIW6RhZgH-' +
'Rk45JIRNKxNagxdeXUqqyhnwhbTo1Hite0iBDexN9tgoZk0XmdYWBn6ElXHRZ7VCDQ',
p: '8UovlB4nrBm7xH-u7XXBMbqxADQm5vaEZxw9eluc-tP7cIAI4sglMIvL_FMpbd2pEeP_B' +
'kR76NTDzzDuPAZvUGRavgEjy0O9j2NAs_WPK4tZF-vFdunhnSh4EHAF4Ij9kbsUi90NOp' +
'bGfVqPdOaHqzgHKoR23Cuusk9wFQ2XTV8',
q: 'wxHdEYT9xrpfrHPqSBQPpO0dWGKJEkrWOb-76rSfuL8wGR4OBNmQdhLuU9zTIh22pog-X' +
'PnLPAecC-4yu_wtJ2SPCKiKDbJBre0CKPyRfGqzvA3njXwMxXazU4kGs-2Fg-xu_iKbaI' +
'jxXrclBLhkxhBtySrwAFhxxOk6fFcPLSs',
dp: 'qS_Mdr5CMRGGMH0bKhPUWEtAixUGZhJaunX5wY71Xoc_Gh4cnO-b7BNJ_-5L8WZog0vr' +
'6PgiLhrqBaCYm2wjpyoG2o2wDHm-NAlzN_wp3G2EFhrSxdOux-S1c0kpRcyoiAO2n29rN' +
'Da-jOzwBBcU8ACEPdLOCQl0IEFFJO33tl8',
dq: 'WAziKpxLKL7LnL4dzDcx8JIPIuwnTxh0plCDdCffyLaT8WJ9lXbXHFTjOvt8WfPrlDP_' +
'Ylxmfkw5BbGZOP1VLGjZn2DkH9aMiwNmbDXFPdG0G3hzQovx_9fajiRV4DWghLHeT9wzJ' +
'fZabRRiI0VQR472300AVEeX4vgbrDBn600',
qi: 'k7czBCT9rHn_PNwCa17hlTy88C4vXkwbz83Oa-aX5L4e5gw5lhcR2ZuZHLb2r6oMt9rl' +
'D7EIDItSs-u21LOXWPTAlazdnpYUyw_CzogM_PN-qNwMRXn5uXFFhmlP2mVg2EdELTahX' +
'ch8kWqHaCSX53yvqCtRKu_j76V31TfQZGM',
kty: 'RSA',
};

for (const keyObject of [publicKey, derivedPublicKey]) {
assert.deepStrictEqual(
keyObject.export({ format: 'jwk' }),
{ kty: 'RSA', n: jwk.n, e: jwk.e }
);
}
assert.deepStrictEqual(
privateKey.export({ format: 'jwk' }),
jwk
);

// Exporting the key using JWK should not work since this format does not
// support key encryption
assert.throws(() => {
privateKey.export({ format: 'jwk', passphrase: 'secret' });
}, {
message: 'The selected key encoding jwk does not support encryption.',
code: 'ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS'
});

const publicDER = publicKey.export({
format: 'der',
type: 'pkcs1'
@@ -254,36 +306,150 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
[
{ private: fixtures.readKey('ed25519_private.pem', 'ascii'),
public: fixtures.readKey('ed25519_public.pem', 'ascii'),
keyType: 'ed25519' },
keyType: 'ed25519',
jwk: {
crv: 'Ed25519',
x: 'K1wIouqnuiA04b3WrMa-xKIKIpfHetNZRv3h9fBf768',
d: 'wVK6M3SMhQh3NK-7GRrSV-BVWQx1FO5pW8hhQeu_NdA',
kty: 'OKP'
} },
{ private: fixtures.readKey('ed448_private.pem', 'ascii'),
public: fixtures.readKey('ed448_public.pem', 'ascii'),
keyType: 'ed448' },
keyType: 'ed448',
jwk: {
crv: 'Ed448',
x: 'oX_ee5-jlcU53-BbGRsGIzly0V-SZtJ_oGXY0udf84q2hTW2RdstLktvwpkVJOoNb7o' +
'Dgc2V5ZUA',
d: '060Ke71sN0GpIc01nnGgMDkp0sFNQ09woVo4AM1ffax1-mjnakK0-p-S7-Xf859QewX' +
'jcR9mxppY',
kty: 'OKP'
} },
{ private: fixtures.readKey('x25519_private.pem', 'ascii'),
public: fixtures.readKey('x25519_public.pem', 'ascii'),
keyType: 'x25519' },
keyType: 'x25519',
jwk: {
crv: 'X25519',
x: 'aSb8Q-RndwfNnPeOYGYPDUN3uhAPnMLzXyfi-mqfhig',
d: 'mL_IWm55RrALUGRfJYzw40gEYWMvtRkesP9mj8o8Omc',
kty: 'OKP'
} },
{ private: fixtures.readKey('x448_private.pem', 'ascii'),
public: fixtures.readKey('x448_public.pem', 'ascii'),
keyType: 'x448' },
keyType: 'x448',
jwk: {
crv: 'X448',
x: 'ioHSHVpTs6hMvghosEJDIR7ceFiE3-Xccxati64oOVJ7NWjfozE7ae31PXIUFq6cVYg' +
'vSKsDFPA',
d: 'tMNtrO_q8dlY6Y4NDeSTxNQ5CACkHiPvmukidPnNIuX_EkcryLEXt_7i6j6YZMKsrWy' +
'S0jlSYJk',
kty: 'OKP'
} },
].forEach((info) => {
const keyType = info.keyType;

{
const exportOptions = { type: 'pkcs8', format: 'pem' };
const key = createPrivateKey(info.private);
assert.strictEqual(key.type, 'private');
assert.strictEqual(key.asymmetricKeyType, keyType);
assert.strictEqual(key.symmetricKeySize, undefined);
assert.strictEqual(key.export(exportOptions), info.private);
assert.strictEqual(
key.export({ type: 'pkcs8', format: 'pem' }), info.private);
assert.deepStrictEqual(
key.export({ format: 'jwk' }), info.jwk);
}

{
const exportOptions = { type: 'spki', format: 'pem' };
[info.private, info.public].forEach((pem) => {
const key = createPublicKey(pem);
assert.strictEqual(key.type, 'public');
assert.strictEqual(key.asymmetricKeyType, keyType);
assert.strictEqual(key.symmetricKeySize, undefined);
assert.strictEqual(key.export(exportOptions), info.public);
assert.strictEqual(
key.export({ type: 'spki', format: 'pem' }), info.public);
const jwk = { ...info.jwk };
delete jwk.d;
assert.deepStrictEqual(
key.export({ format: 'jwk' }), jwk);
});
}
});

[
{ private: fixtures.readKey('ec_p256_private.pem', 'ascii'),
public: fixtures.readKey('ec_p256_public.pem', 'ascii'),
keyType: 'ec',
namedCurve: 'prime256v1',
jwk: {
crv: 'P-256',
d: 'DxBsPQPIgMuMyQbxzbb9toew6Ev6e9O6ZhpxLNgmAEo',
kty: 'EC',
x: 'X0mMYR_uleZSIPjNztIkAS3_ud5LhNpbiIFp6fNf2Gs',
y: 'UbJuPy2Xi0lW7UYTBxPK3yGgDu9EAKYIecjkHX5s2lI'
} },
{ private: fixtures.readKey('ec_secp256k1_private.pem', 'ascii'),
public: fixtures.readKey('ec_secp256k1_public.pem', 'ascii'),
keyType: 'ec',
namedCurve: 'secp256k1',
jwk: {
crv: 'secp256k1',
d: 'c34ocwTwpFa9NZZh3l88qXyrkoYSxvC0FEsU5v1v4IM',
kty: 'EC',
x: 'cOzhFSpWxhalCbWNdP2H_yUkdC81C9T2deDpfxK7owA',
y: '-A3DAZTk9IPppN-f03JydgHaFvL1fAHaoXf4SX4NXyo'
} },
{ private: fixtures.readKey('ec_p384_private.pem', 'ascii'),
public: fixtures.readKey('ec_p384_public.pem', 'ascii'),
keyType: 'ec',
namedCurve: 'secp384r1',
jwk: {
crv: 'P-384',
d: 'dwfuHuAtTlMRn7ZBCBm_0grpc1D_4hPeNAgevgelljuC0--k_LDFosDgBlLLmZsi',
kty: 'EC',
x: 'hON3nzGJgv-08fdHpQxgRJFZzlK-GZDGa5f3KnvM31cvvjJmsj4UeOgIdy3rDAjV',
y: 'fidHhtecNCGCfLqmrLjDena1NSzWzWH1u_oUdMKGo5XSabxzD7-8JZxjpc8sR9cl'
} },
{ private: fixtures.readKey('ec_p521_private.pem', 'ascii'),
public: fixtures.readKey('ec_p521_public.pem', 'ascii'),
keyType: 'ec',
namedCurve: 'secp521r1',
jwk: {
crv: 'P-521',
d: 'ABIIbmn3Gm_Y11uIDkC3g2ijpRxIrJEBY4i_JJYo5OougzTl3BX2ifRluPJMaaHcNer' +
'bQH_WdVkLLX86ShlHrRyJ',
kty: 'EC',
x: 'AaLFgjwZtznM3N7qsfb86awVXe6c6djUYOob1FN-kllekv0KEXV0bwcDjPGQz5f6MxL' +
'CbhMeHRavUS6P10rsTtBn',
y: 'Ad3flexBeAfXceNzRBH128kFbOWD6W41NjwKRqqIF26vmgW_8COldGKZjFkOSEASxPB' +
'cvA2iFJRUyQ3whC00j0Np'
} },
].forEach((info) => {
const { keyType, namedCurve } = info;

{
const key = createPrivateKey(info.private);
assert.strictEqual(key.type, 'private');
assert.strictEqual(key.asymmetricKeyType, keyType);
assert.deepStrictEqual(key.asymmetricKeyDetails, { namedCurve });
assert.strictEqual(key.symmetricKeySize, undefined);
assert.strictEqual(
key.export({ type: 'pkcs8', format: 'pem' }), info.private);
assert.deepStrictEqual(
key.export({ format: 'jwk' }), info.jwk);
}

{
[info.private, info.public].forEach((pem) => {
const key = createPublicKey(pem);
assert.strictEqual(key.type, 'public');
assert.strictEqual(key.asymmetricKeyType, keyType);
assert.deepStrictEqual(key.asymmetricKeyDetails, { namedCurve });
assert.strictEqual(key.symmetricKeySize, undefined);
assert.strictEqual(
key.export({ type: 'spki', format: 'pem' }), info.public);
const jwk = { ...info.jwk };
delete jwk.d;
assert.deepStrictEqual(
key.export({ format: 'jwk' }), jwk);
});
}
});
@@ -321,6 +487,9 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
assert.strictEqual(publicKey.type, 'public');
assert.strictEqual(publicKey.asymmetricKeyType, 'dsa');
assert.strictEqual(publicKey.symmetricKeySize, undefined);
assert.throws(
() => publicKey.export({ format: 'jwk' }),
{ code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' });

const privateKey = createPrivateKey({
key: privateDsa,
@@ -330,7 +499,9 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'dsa');
assert.strictEqual(privateKey.symmetricKeySize, undefined);

assert.throws(
() => privateKey.export({ format: 'jwk' }),
{ code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' });
}

{
@@ -350,6 +521,13 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
assert.strictEqual(privateKey.type, 'private');
assert.strictEqual(privateKey.asymmetricKeyType, 'rsa-pss');

assert.throws(
() => publicKey.export({ format: 'jwk' }),
{ code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' });
assert.throws(
() => privateKey.export({ format: 'jwk' }),
{ code: 'ERR_CRYPTO_JWK_UNSUPPORTED_KEY_TYPE' });

for (const key of [privatePem, privateKey]) {
// Any algorithm should work.
for (const algo of ['sha1', 'sha256']) {
@@ -485,3 +663,46 @@ const privateDsa = fixtures.readKey('dsa_private_encrypted_1025.pem',
message: "The property 'options.cipher' is invalid. Received undefined"
});
}

{
// SecretKeyObject export buffer format (default)
const buffer = Buffer.from('Hello World');
const keyObject = createSecretKey(buffer);
assert.deepStrictEqual(keyObject.export(), buffer);
assert.deepStrictEqual(keyObject.export({}), buffer);
assert.deepStrictEqual(keyObject.export({ format: 'buffer' }), buffer);
assert.deepStrictEqual(keyObject.export({ format: undefined }), buffer);
}

{
// Exporting an "oct" JWK from a SecretKeyObject
const buffer = Buffer.from('Hello World');
const keyObject = createSecretKey(buffer);
assert.deepStrictEqual(
keyObject.export({ format: 'jwk' }),
{ kty: 'oct', k: 'SGVsbG8gV29ybGQ' }
);
}

{
// Exporting a JWK unsupported curve EC key
const supported = ['prime256v1', 'secp256k1', 'secp384r1', 'secp521r1'];
// Find an unsupported curve regardless of whether a FIPS compliant crypto
// provider is currently in use.
const namedCurve = getCurves().find((curve) => !supported.includes(curve));
assert(namedCurve);
const keyPair = generateKeyPairSync('ec', { namedCurve });
const { publicKey, privateKey } = keyPair;
assert.throws(
() => publicKey.export({ format: 'jwk' }),
{
code: 'ERR_CRYPTO_JWK_UNSUPPORTED_CURVE',
message: `Unsupported JWK EC curve: ${namedCurve}.`
});
assert.throws(
() => privateKey.export({ format: 'jwk' }),
{
code: 'ERR_CRYPTO_JWK_UNSUPPORTED_CURVE',
message: `Unsupported JWK EC curve: ${namedCurve}.`
});
}