Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
0057af2
deps: cherry-pick http_parser_set_max_header_size
cjihrig Nov 29, 2018
c80ac7f
src: add kUInteger parsing
mcollina Dec 18, 2018
edd8bd0
cli: add --max-http-header-size flag
cjihrig Dec 3, 2018
0cde1a4
lib: remove unused NativeModule/NativeModule wraps
joyeecheung Dec 8, 2018
7f34c76
src: remove internalBinding('config').warningFile
joyeecheung Dec 11, 2018
96bdd47
lib: refactor argument validation using validateString
ZYSzys Dec 11, 2018
7b2eefc
child_process: spawn ignores options in case args is undefined
eduardbme Dec 8, 2018
85a1369
perf_hooks: make GC tracking state per-Environment
addaleax Dec 14, 2018
74e08c0
vm: simplify Script constructor options validation
cjihrig Dec 15, 2018
09a99c6
src: mark some global state as const
addaleax Dec 14, 2018
ed3303b
tools: enable no-useless-constructor lint rule
cjihrig Dec 15, 2018
30b6155
test: merge test with unnecessary child process
sam-github Dec 13, 2018
4513516
build: add a space to clarify skipping crypto msg
danbev Dec 13, 2018
2516e9c
doc,lib,test: capitalize comment sentences
BridgeAR Dec 10, 2018
e340b8f
tls: re-define max supported version as 1.2
sam-github Nov 28, 2018
a4505c6
src: extract common Bind method
maclover7 Aug 14, 2018
9ad6bc2
test: remove magic numbers in test-gc-http-client-onerror
Trott Dec 10, 2018
3f82144
process: move environment variable proxy code into node_env_var.cc
joyeecheung Dec 15, 2018
4561e2c
doc: revise "Breaking Changes" section of Collaborator Guide
Trott Dec 16, 2018
a5bccc2
tools: make apilinks building more robust
joyeecheung Dec 13, 2018
5eb5d1d
test: test internal/util/types in vm
ZYSzys Dec 15, 2018
a9ab28d
assert: inspect getters
BridgeAR Dec 13, 2018
3e1fe19
test: add missing tmpdir.refresh() in recently-added test
Trott Dec 17, 2018
6f3b421
src: schedule destroy hooks in BeforeExit early during bootstrap
joyeecheung Dec 13, 2018
1f45b23
test: add signal check to test-esm-cjs-main
Trott Dec 16, 2018
4f28da8
worker: fix nullptr deref after MessagePort deser failure
addaleax Dec 16, 2018
155d1d5
deps: upgrade to libuv 1.24.1
cjihrig Dec 16, 2018
8279826
test: verify input flags
BridgeAR Dec 6, 2018
d09e333
test: remove obsolete eslint comments
cjihrig Dec 17, 2018
d1a98a8
events: simplify stack compare function
BridgeAR Nov 18, 2018
c6388ed
src: handle empty Maybe in uv binding initialize
addaleax Dec 17, 2018
7df59f8
vm: reuse validateString of internal/validators
ZYSzys Dec 16, 2018
fd0361b
src: mark options parsers as const
addaleax Dec 15, 2018
54e42f0
src: port GetLoadedLibraries for freebsd
gireeshpunathil Dec 18, 2018
2fc43fb
lib: switch to object spread where possible
BridgeAR Dec 18, 2018
ae50f48
http: add maxHeaderSize property
cjihrig Dec 6, 2018
add566e
os: use uv_os_gethostname() in hostname()
cjihrig Dec 18, 2018
b801b03
src: use std::vector for setting up process.execPath
addaleax Dec 15, 2018
9b38bbf
build: correct fi indentation in Makefile
danbev Dec 18, 2018
9b941da
tools: update certdata.txt
sam-github Dec 18, 2018
6f6f339
crypto: update root certificates
sam-github Dec 18, 2018
4ca0951
doc: describe root cert update process
sam-github Dec 18, 2018
6a690ee
doc: revise "Breaking Changes and Deprecations"
Trott Dec 18, 2018
175f7b6
test: remove unnecessary eslint-disable comments
Trott Dec 19, 2018
073a512
tools: report unused disable-directives for ESLint
Trott Dec 19, 2018
b3f45da
lib: make internal API warning more direct
Trott Dec 19, 2018
a28cae0
test: add hasCrypto check to common flags check
danbev Dec 20, 2018
db54531
test: remove Files: comment processing from Python test runner
Trott Dec 22, 2018
c6bfa66
buffer: simplify code
BridgeAR Dec 20, 2018
c9f809e
src: add DCHECK macros
kiyomizumia Nov 14, 2018
b78d487
doc: fix links in test/common/README.md
vsemozhetbyt Dec 21, 2018
3b53df0
crypto: add key object API
tniessen Sep 20, 2018
e6c1e8d
crypto: always accept certificates as public keys
tniessen Dec 19, 2018
c7fa132
tools: alphabetize IGNORED_SUITES in tools/test.py
Trott Dec 22, 2018
6557ea1
test: mark test-trace-events-api-worker-disabled flaky
Trott Dec 23, 2018
8ab0a48
tools: update ESLint to 5.11.0
cjihrig Dec 23, 2018
45d4851
test: fix test-tls-session-timeout
Trott Dec 23, 2018
e855018
deps: upgrade npm to 6.5.0
Nov 29, 2018
968e901
2018-12-26, Version 11.6.0 (Current)
MylesBorins Dec 21, 2018
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 key object API
This commit makes multiple important changes:

1. A new key object API is introduced. The KeyObject class itself is
   not exposed to users, instead, several new APIs can be used to
   construct key objects: createSecretKey, createPrivateKey and
   createPublicKey. The new API also allows to convert between
   different key formats, and even though the API itself is not
   compatible to the WebCrypto standard in any way, it makes
   interoperability much simpler.

2. Key objects can be used instead of the raw key material in all
   relevant crypto APIs.

3. The handling of asymmetric keys has been unified and greatly
   improved. Node.js now fully supports both PEM-encoded and
   DER-encoded public and private keys.

4. Conversions between buffers and strings have been moved to native
   code for sensitive data such as symmetric keys due to security
   considerations such as zeroing temporary buffers.

5. For compatibility with older versions of the crypto API, this
   change allows to specify Buffers and strings as the "passphrase"
   option when reading or writing an encoded key. Note that this
   can result in unexpected behavior if the password contains a
   null byte.

PR-URL: #24234
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
tniessen authored and MylesBorins committed Dec 26, 2018
commit 3b53df07486b0d5881c84d768108d45303cc49a7
278 changes: 218 additions & 60 deletions doc/api/crypto.md

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions doc/api/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,11 @@ The selected public or private key encoding is incompatible with other options.

An invalid [crypto digest algorithm][] was specified.

<a id="ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE"></a>
### ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE

The given crypto key object's type is invalid for the attempted operation.

<a id="ERR_CRYPTO_INVALID_STATE"></a>
### ERR_CRYPTO_INVALID_STATE

Expand Down
8 changes: 8 additions & 0 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ const {
generateKeyPair,
generateKeyPairSync
} = require('internal/crypto/keygen');
const {
createSecretKey,
createPublicKey,
createPrivateKey
} = require('internal/crypto/keys');
const {
DiffieHellman,
DiffieHellmanGroup,
Expand Down Expand Up @@ -149,6 +154,9 @@ module.exports = exports = {
createECDH,
createHash,
createHmac,
createPrivateKey,
createPublicKey,
createSecretKey,
createSign,
createVerify,
getCiphers,
Expand Down
33 changes: 20 additions & 13 deletions lib/internal/crypto/cipher.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ const {
} = require('internal/errors').codes;
const { validateString } = require('internal/validators');

const {
preparePrivateKey,
preparePublicOrPrivateKey,
prepareSecretKey
} = require('internal/crypto/keys');
const {
getDefaultEncoding,
kHandle,
Expand All @@ -38,19 +43,25 @@ const { deprecate, normalizeEncoding } = require('internal/util');
// Lazy loaded for startup performance.
let StringDecoder;

function rsaFunctionFor(method, defaultPadding) {
function rsaFunctionFor(method, defaultPadding, keyType) {
return (options, buffer) => {
const key = options.key || options;
const { format, type, data, passphrase } =
keyType === 'private' ?
preparePrivateKey(options) :
preparePublicOrPrivateKey(options);
const padding = options.padding || defaultPadding;
const passphrase = options.passphrase || null;
return method(toBuf(key), buffer, padding, passphrase);
return method(data, format, type, passphrase, buffer, padding);
};
}

const publicEncrypt = rsaFunctionFor(_publicEncrypt, RSA_PKCS1_OAEP_PADDING);
const publicDecrypt = rsaFunctionFor(_publicDecrypt, RSA_PKCS1_PADDING);
const privateEncrypt = rsaFunctionFor(_privateEncrypt, RSA_PKCS1_PADDING);
const privateDecrypt = rsaFunctionFor(_privateDecrypt, RSA_PKCS1_OAEP_PADDING);
const publicEncrypt = rsaFunctionFor(_publicEncrypt, RSA_PKCS1_OAEP_PADDING,
'public');
const publicDecrypt = rsaFunctionFor(_publicDecrypt, RSA_PKCS1_PADDING,
'private');
const privateEncrypt = rsaFunctionFor(_privateEncrypt, RSA_PKCS1_PADDING,
'private');
const privateDecrypt = rsaFunctionFor(_privateDecrypt, RSA_PKCS1_OAEP_PADDING,
'public');

function getDecoder(decoder, encoding) {
encoding = normalizeEncoding(encoding);
Expand Down Expand Up @@ -105,11 +116,7 @@ function createCipher(cipher, password, options, decipher) {

function createCipherWithIV(cipher, key, options, decipher, iv) {
validateString(cipher, 'cipher');
key = toBuf(key);
if (!isArrayBufferView(key)) {
throw invalidArrayBufferView('key', key);
}

key = prepareSecretKey(key);
iv = toBuf(iv);
if (iv !== null && !isArrayBufferView(iv)) {
throw invalidArrayBufferView('iv', iv);
Expand Down
9 changes: 5 additions & 4 deletions lib/internal/crypto/hash.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ const {
toBuf
} = require('internal/crypto/util');

const {
prepareSecretKey
} = require('internal/crypto/keys');

const { Buffer } = require('buffer');

const {
Expand Down Expand Up @@ -88,10 +92,7 @@ function Hmac(hmac, key, options) {
if (!(this instanceof Hmac))
return new Hmac(hmac, key, options);
validateString(hmac, 'hmac');
if (typeof key !== 'string' && !isArrayBufferView(key)) {
throw new ERR_INVALID_ARG_TYPE('key',
['string', 'TypedArray', 'DataView'], key);
}
key = prepareSecretKey(key);
this[kHandle] = new _Hmac();
this[kHandle].init(hmac, toBuf(key));
this[kState] = {
Expand Down
135 changes: 46 additions & 89 deletions lib/internal/crypto/keygen.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,32 @@ const {
generateKeyPairDSA,
generateKeyPairEC,
OPENSSL_EC_NAMED_CURVE,
OPENSSL_EC_EXPLICIT_CURVE,
PK_ENCODING_PKCS1,
PK_ENCODING_PKCS8,
PK_ENCODING_SPKI,
PK_ENCODING_SEC1,
PK_FORMAT_DER,
PK_FORMAT_PEM
OPENSSL_EC_EXPLICIT_CURVE
} = internalBinding('crypto');
const {
parsePublicKeyEncoding,
parsePrivateKeyEncoding,

PublicKeyObject,
PrivateKeyObject
} = require('internal/crypto/keys');
const { customPromisifyArgs } = require('internal/util');
const { isUint32, validateString } = require('internal/validators');
const {
ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_CALLBACK,
ERR_INVALID_OPT_VALUE
} = require('internal/errors').codes;

const { isArrayBufferView } = require('internal/util/types');

function wrapKey(key, ctor) {
if (typeof key === 'string' || isArrayBufferView(key))
return key;
return new ctor(key);
}

function generateKeyPair(type, options, callback) {
if (typeof options === 'function') {
callback = options;
Expand All @@ -38,6 +46,9 @@ function generateKeyPair(type, options, callback) {
const wrap = new AsyncWrap(Providers.KEYPAIRGENREQUEST);
wrap.ondone = (ex, pubkey, privkey) => {
if (ex) return callback.call(wrap, ex);
// If no encoding was chosen, return key objects instead.
pubkey = wrapKey(pubkey, PublicKeyObject);
privkey = wrapKey(privkey, PrivateKeyObject);
callback.call(wrap, null, pubkey, privkey);
};

Expand Down Expand Up @@ -69,86 +80,32 @@ function handleError(impl, wrap) {
function parseKeyEncoding(keyType, options) {
const { publicKeyEncoding, privateKeyEncoding } = options;

if (publicKeyEncoding == null || typeof publicKeyEncoding !== 'object')
throw new ERR_INVALID_OPT_VALUE('publicKeyEncoding', publicKeyEncoding);

const { format: strPublicFormat, type: strPublicType } = publicKeyEncoding;

let publicType;
if (strPublicType === 'pkcs1') {
if (keyType !== 'rsa') {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
strPublicType, 'can only be used for RSA keys');
}
publicType = PK_ENCODING_PKCS1;
} else if (strPublicType === 'spki') {
publicType = PK_ENCODING_SPKI;
let publicFormat, publicType;
if (publicKeyEncoding == null) {
publicFormat = publicType = undefined;
} else if (typeof publicKeyEncoding === 'object') {
({
format: publicFormat,
type: publicType
} = parsePublicKeyEncoding(publicKeyEncoding, keyType,
'publicKeyEncoding'));
} else {
throw new ERR_INVALID_OPT_VALUE('publicKeyEncoding.type', strPublicType);
throw new ERR_INVALID_OPT_VALUE('publicKeyEncoding', publicKeyEncoding);
}

let publicFormat;
if (strPublicFormat === 'der') {
publicFormat = PK_FORMAT_DER;
} else if (strPublicFormat === 'pem') {
publicFormat = PK_FORMAT_PEM;
let privateFormat, privateType, cipher, passphrase;
if (privateKeyEncoding == null) {
privateFormat = privateType = undefined;
} else if (typeof privateKeyEncoding === 'object') {
({
format: privateFormat,
type: privateType,
cipher,
passphrase
} = parsePrivateKeyEncoding(privateKeyEncoding, keyType,
'privateKeyEncoding'));
} else {
throw new ERR_INVALID_OPT_VALUE('publicKeyEncoding.format',
strPublicFormat);
}

if (privateKeyEncoding == null || typeof privateKeyEncoding !== 'object')
throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding', privateKeyEncoding);

const {
cipher,
passphrase,
format: strPrivateFormat,
type: strPrivateType
} = privateKeyEncoding;

let privateType;
if (strPrivateType === 'pkcs1') {
if (keyType !== 'rsa') {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
strPrivateType, 'can only be used for RSA keys');
}
privateType = PK_ENCODING_PKCS1;
} else if (strPrivateType === 'pkcs8') {
privateType = PK_ENCODING_PKCS8;
} else if (strPrivateType === 'sec1') {
if (keyType !== 'ec') {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
strPrivateType, 'can only be used for EC keys');
}
privateType = PK_ENCODING_SEC1;
} else {
throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding.type', strPrivateType);
}

let privateFormat;
if (strPrivateFormat === 'der') {
privateFormat = PK_FORMAT_DER;
} else if (strPrivateFormat === 'pem') {
privateFormat = PK_FORMAT_PEM;
} else {
throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding.format',
strPrivateFormat);
}

if (cipher != null) {
if (typeof cipher !== 'string')
throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding.cipher', cipher);
if (privateFormat === PK_FORMAT_DER &&
(privateType === PK_ENCODING_PKCS1 ||
privateType === PK_ENCODING_SEC1)) {
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
strPrivateType, 'does not support encryption');
}
if (typeof passphrase !== 'string') {
throw new ERR_INVALID_OPT_VALUE('privateKeyEncoding.passphrase',
passphrase);
}
}

return {
Expand Down Expand Up @@ -181,8 +138,8 @@ function check(type, options, callback) {
}

impl = (wrap) => generateKeyPairRSA(modulusLength, publicExponent,
publicType, publicFormat,
privateType, privateFormat,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
Expand All @@ -200,8 +157,8 @@ function check(type, options, callback) {
}

impl = (wrap) => generateKeyPairDSA(modulusLength, divisorLength,
publicType, publicFormat,
privateType, privateFormat,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
Expand All @@ -219,8 +176,8 @@ function check(type, options, callback) {
throw new ERR_INVALID_OPT_VALUE('paramEncoding', paramEncoding);

impl = (wrap) => generateKeyPairEC(namedCurve, paramEncoding,
publicType, publicFormat,
privateType, privateFormat,
publicFormat, publicType,
privateFormat, privateType,
cipher, passphrase, wrap);
}
break;
Expand Down
Loading