summaryrefslogtreecommitdiff
path: root/lib/internal/crypto/keygen.js
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2018-09-20 19:53:44 +0200
committerTobias Nießen <tniessen@tnie.de>2018-12-24 14:50:16 +0100
commit823d86c47ce15fba8875fcebd412593b02aab362 (patch)
treea023dac2cbda38b6e194c7324e501e4054715af5 /lib/internal/crypto/keygen.js
parent5570df407aae1e329955b1093e0e5b8bde270213 (diff)
downloadandroid-node-v8-823d86c47ce15fba8875fcebd412593b02aab362.tar.gz
android-node-v8-823d86c47ce15fba8875fcebd412593b02aab362.tar.bz2
android-node-v8-823d86c47ce15fba8875fcebd412593b02aab362.zip
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: https://github.com/nodejs/node/pull/24234 Reviewed-By: Refael Ackermann <refack@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib/internal/crypto/keygen.js')
-rw-r--r--lib/internal/crypto/keygen.js135
1 files changed, 46 insertions, 89 deletions
diff --git a/lib/internal/crypto/keygen.js b/lib/internal/crypto/keygen.js
index 7222d301f0..7c0c411043 100644
--- a/lib/internal/crypto/keygen.js
+++ b/lib/internal/crypto/keygen.js
@@ -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;
@@ -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);
};
@@ -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 {
@@ -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;
@@ -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;
@@ -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;