From a361f5361c9cb9a729f66c93da13ee4792216dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Mon, 6 Mar 2017 00:41:26 +0100 Subject: [PATCH 01/17] crypto: add sign/verify support for RSASSA-PSS Adds support for the PSS padding scheme. Until now, the sign/verify functions used the old EVP_Sign*/EVP_Verify* OpenSSL API, making it impossible to change the padding scheme. Fixed by first computing the message digest and then signing/verifying with a custom EVP_PKEY_CTX, allowing us to specify options such as the padding scheme and the PSS salt length. Fixes: https://github.com/nodejs/node/issues/1127 --- doc/api/crypto.md | 23 +++- lib/crypto.js | 26 ++-- src/node_crypto.cc | 151 +++++++++++++++++++++-- src/node_crypto.h | 8 +- test/parallel/test-crypto-sign-verify.js | 32 +++++ 5 files changed, 218 insertions(+), 22 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 077c244c431d03..33a4c35fd54a3f 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -961,7 +961,7 @@ pQhByd5eyj3lgZ7m7jbchtdgyOF8Io/1ng== console.log(sign.sign(privateKey).toString('hex')); ``` -### sign.sign(private_key[, output_format]) +### sign.sign(private_key[, output_format][, options]) @@ -984,6 +984,15 @@ The `output_format` can specify one of `'latin1'`, `'hex'` or `'base64'`. If `output_format` is provided a string is returned; otherwise a [`Buffer`][] is returned. +The optional `options` argument is an object which specifies additional +cryptographic parameters. Currently, the following options are supported: + +* `padding` : {String} - RSA padding, either `'pkcs1'` for RSASSA-PKCS1-v1_5 + (default) or `'pss'` for RSASSA-PSS +* `saltLength` : {number} - salt length for RSASSA-PSS. If this is set to `-1`, + the salt length will be set to the digest size. If this is set to `-2` + (default), the salt length will be set to the maximum permissible value. + The `Sign` object can not be again used after `sign.sign()` method has been called. Multiple calls to `sign.sign()` will result in an error being thrown. @@ -1070,7 +1079,7 @@ then `input_encoding` is ignored. This can be called many times with new data as it is streamed. -### verifier.verify(object, signature[, signature_format]) +### verifier.verify(object, signature[, signature_format][, options]) @@ -1087,6 +1096,16 @@ If a `signature_format` is specified, the `signature` is expected to be a string; otherwise `signature` is expected to be a [`Buffer`][] or `Uint8Array`. +The optional `options` argument is an object which specifies additional +cryptographic parameters. Currently, the following options are supported: + +* `padding` : {String} - RSA padding, either `'pkcs1'` for RSASSA-PKCS1-v1_5 + (default) or `'pss'` for RSASSA-PSS +* `saltLength` : {number} - salt length for RSASSA-PSS. If this is set to `-1`, + the salt length will be set to the digest size. A value of `-2` (default) + causes the salt length to be automatically determined based on the PSS block + structure. + Returns `true` or `false` depending on the validity of the signature for the data and public key. diff --git a/lib/crypto.js b/lib/crypto.js index 7ceca8ba26601e..4d5928f3212df1 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -297,13 +297,18 @@ Sign.prototype._write = function _write(chunk, encoding, callback) { Sign.prototype.update = Hash.prototype.update; -Sign.prototype.sign = function sign(options, encoding) { - if (!options) +Sign.prototype.sign = function sign(privateKey, encoding, options) { + if (!privateKey) throw new Error('No key provided to sign'); - var key = options.key || options; - var passphrase = options.passphrase || null; - var ret = this._handle.sign(toBuf(key), null, passphrase); + if (typeof encoding == 'object' && !options) { + options = encoding; + encoding = null; + } + + var key = privateKey.key || privateKey; + var passphrase = privateKey.passphrase || null; + var ret = this._handle.sign(toBuf(key), null, passphrase, options); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') @@ -329,9 +334,16 @@ util.inherits(Verify, stream.Writable); Verify.prototype._write = Sign.prototype._write; Verify.prototype.update = Sign.prototype.update; -Verify.prototype.verify = function verify(object, signature, sigEncoding) { +Verify.prototype.verify = function verify(object, signature, sigEncoding, + options) { + if (typeof sigEncoding == 'object' && !options) { + options = sigEncoding; + sigEncoding = null; + } + sigEncoding = sigEncoding || exports.DEFAULT_ENCODING; - return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding)); + return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding), + null, options); }; function rsaPublic(method, defaultPadding) { diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 7698cf2062c8e1..f0a3fb35a371cc 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -101,6 +101,7 @@ using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::MaybeLocal; using v8::Null; using v8::Object; using v8::Persistent; @@ -3972,6 +3973,41 @@ void SignBase::CheckThrow(SignBase::Error error) { } } +int SignBase::GetRSAOptions(Environment *env, v8::Local options, + int *padding, int *saltlen) { + Local key = String::NewFromUtf8(env->isolate(), "padding"); + MaybeLocal maybePadding = options->Get(key); + if (!maybePadding.IsEmpty()) { + Local paddingValue = maybePadding.ToLocalChecked(); + if (paddingValue->IsString()) { + v8::String::Utf8Value paddingUtf8(paddingValue); + const char *paddingName = *paddingUtf8; + if (strcmp(paddingName, "pkcs1") == 0) { + *padding = RSA_PKCS1_PADDING; + } + else if (strcmp(paddingName, "pss") == 0) { + *padding = RSA_PKCS1_PSS_PADDING; + } + else { + env->ThrowError("Padding must be 'pkcs1' or 'pss'"); + return 0; + } + } + } + + if (*padding == RSA_PKCS1_PSS_PADDING) { + key = String::NewFromUtf8(env->isolate(), "saltLength"); + MaybeLocal maybeSaltlen = options->Get(key); + if (!maybeSaltlen.IsEmpty()) { + Local saltlenValue = maybeSaltlen.ToLocalChecked(); + if (saltlenValue->IsNumber()) { + *saltlen = saltlenValue->Int32Value(); + } + } + } + + return 1; +} @@ -4001,7 +4037,7 @@ SignBase::Error Sign::SignInit(const char* sign_type) { return kSignUnknownDigest; EVP_MD_CTX_init(&mdctx_); - if (!EVP_SignInit_ex(&mdctx_, md, nullptr)) + if (!EVP_DigestInit_ex(&mdctx_, md, nullptr)) return kSignInit; initialised_ = true; @@ -4028,7 +4064,7 @@ void Sign::SignInit(const FunctionCallbackInfo& args) { SignBase::Error Sign::SignUpdate(const char* data, int len) { if (!initialised_) return kSignNotInitialised; - if (!EVP_SignUpdate(&mdctx_, data, len)) + if (!EVP_DigestUpdate(&mdctx_, data, len)) return kSignUpdate; return kSignOk; } @@ -4058,12 +4094,59 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { sign->CheckThrow(err); } +static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, + EVP_PKEY *pkey, int padding, int pss_saltlen) { + unsigned char m[EVP_MAX_MD_SIZE]; + unsigned int m_len; + int i = 0; + EVP_PKEY_CTX *pkctx = nullptr; + + *s = 0; + if (!EVP_DigestFinal_ex(mdctx, &(m[0]), &m_len)) + goto err; + + if (mdctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) { + size_t sltmp = (size_t)EVP_PKEY_size(pkey); + i = 0; + pkctx = EVP_PKEY_CTX_new(pkey, nullptr); + if (!pkctx) + goto err; + if (EVP_PKEY_sign_init(pkctx) <= 0) + goto err; + if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { + if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) + goto err; + if (padding == RSA_PKCS1_PSS_PADDING) + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, pss_saltlen) <= 0) + goto err; + } + if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx->digest) <= 0) + goto err; + if (EVP_PKEY_sign(pkctx, md, &sltmp, m, m_len) <= 0) + goto err; + *s = sltmp; + i = 1; + err: + EVP_PKEY_CTX_free(pkctx); + return i; + } + + if (!mdctx->digest->sign) { + EVPerr(EVP_F_EVP_SIGNFINAL, EVP_R_NO_SIGN_FUNCTION_CONFIGURED); + return 0; + } + + return (mdctx->digest->sign(mdctx->digest->type, m, m_len, md, s, + pkey->pkey.ptr)); +} SignBase::Error Sign::SignFinal(const char* key_pem, int key_pem_len, const char* passphrase, unsigned char** sig, - unsigned int *sig_len) { + unsigned int *sig_len, + int padding, + int saltlen) { if (!initialised_) return kSignNotInitialised; @@ -4109,7 +4192,7 @@ SignBase::Error Sign::SignFinal(const char* key_pem, } #endif // NODE_FIPS_MODE - if (EVP_SignFinal(&mdctx_, *sig, sig_len, pkey)) + if (Node_SignFinal(&mdctx_, *sig, sig_len, pkey, padding, saltlen)) fatal = false; initialised_ = false; @@ -4152,6 +4235,12 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { size_t buf_len = Buffer::Length(args[0]); char* buf = Buffer::Data(args[0]); + int padding = RSA_PKCS1_PADDING; + int saltlen = -2; + if (len >= 4 && args[3]->IsObject()) { + sign->GetRSAOptions(env, Local::Cast(args[3]), &padding, &saltlen); + } + md_len = 8192; // Maximum key size is 8192 bits md_value = new unsigned char[md_len]; @@ -4163,7 +4252,9 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { buf_len, len >= 3 && !args[2]->IsNull() ? *passphrase : nullptr, &md_value, - &md_len); + &md_len, + padding, + saltlen); if (err != kSignOk) { delete[] md_value; md_value = nullptr; @@ -4207,7 +4298,7 @@ SignBase::Error Verify::VerifyInit(const char* verify_type) { return kSignUnknownDigest; EVP_MD_CTX_init(&mdctx_); - if (!EVP_VerifyInit_ex(&mdctx_, md, nullptr)) + if (!EVP_DigestInit_ex(&mdctx_, md, nullptr)) return kSignInit; initialised_ = true; @@ -4235,7 +4326,7 @@ SignBase::Error Verify::VerifyUpdate(const char* data, int len) { if (!initialised_) return kSignNotInitialised; - if (!EVP_VerifyUpdate(&mdctx_, data, len)) + if (!EVP_DigestUpdate(&mdctx_, data, len)) return kSignUpdate; return kSignOk; @@ -4271,6 +4362,8 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, int key_pem_len, const char* sig, int siglen, + int padding, + int saltlen, bool* verify_result) { if (!initialised_) return kSignNotInitialised; @@ -4282,7 +4375,10 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, BIO* bp = nullptr; X509* x509 = nullptr; bool fatal = true; + unsigned char m[EVP_MAX_MD_SIZE]; + unsigned int m_len; int r = 0; + EVP_PKEY_CTX *pkctx = nullptr; bp = BIO_new_mem_buf(const_cast(key_pem), key_pem_len); if (bp == nullptr) @@ -4317,11 +4413,35 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, goto exit; } + if (!EVP_DigestFinal_ex(&mdctx_, m, &m_len)) { + goto exit; + } + fatal = false; - r = EVP_VerifyFinal(&mdctx_, - reinterpret_cast(sig), - siglen, - pkey); + + pkctx = EVP_PKEY_CTX_new(pkey, nullptr); + if (!pkctx) + goto err; + if (EVP_PKEY_verify_init(pkctx) <= 0) + goto err; + if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { + if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) + goto err; + if (padding == RSA_PKCS1_PSS_PADDING) + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, saltlen) <= 0) + goto err; + } + if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx_.digest) <= 0) + goto err; + r = EVP_PKEY_verify(pkctx, + reinterpret_cast(sig), + siglen, + m, + m_len); + fatal = false; + + err: + EVP_PKEY_CTX_free(pkctx); exit: if (pkey != nullptr) @@ -4377,8 +4497,15 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { hbuf = Buffer::Data(args[1]); } + int padding = RSA_PKCS1_PADDING; + int saltlen = -2; + if (args.Length() >= 4 && args[3]->IsObject()) { + verify->GetRSAOptions(env, args[3]->ToObject(), &padding, &saltlen); + } + bool verify_result; - Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, &verify_result); + Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, padding, saltlen, + &verify_result); if (args[1]->IsString()) delete[] hbuf; if (err != kSignOk) diff --git a/src/node_crypto.h b/src/node_crypto.h index 63e6ab684fe2e4..3982a60683a2da 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -577,6 +577,8 @@ class SignBase : public BaseObject { protected: void CheckThrow(Error error); + int GetRSAOptions(Environment *env, v8::Local options, + int *padding, int *saltlen); EVP_MD_CTX mdctx_; /* coverity[member_decl] */ bool initialised_; @@ -592,7 +594,9 @@ class Sign : public SignBase { int key_pem_len, const char* passphrase, unsigned char** sig, - unsigned int *sig_len); + unsigned int *sig_len, + int padding, + int saltlen); protected: static void New(const v8::FunctionCallbackInfo& args); @@ -615,6 +619,8 @@ class Verify : public SignBase { int key_pem_len, const char* sig, int siglen, + int padding, + int saltlen, bool* verify_result); protected: diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 81b2c109b69b20..f5a0f05ea21df7 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -71,6 +71,38 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); assert.strictEqual(verified, true, 'sign and verify (stream)'); } +{ + [ 'RSA-SHA1', 'RSA-SHA256' ].forEach((algo) => { + [ null, -2, -1, 0, 16, 32, 64 ].forEach((saltLength) => { + let verified; + + // Test sign and verify with the given parameters + const s4 = crypto.createSign(algo) + .update('Test123') + .sign(keyPem, null, { padding: 'pss', saltLength }); + verified = crypto.createVerify(algo) + .update('Test') + .update('123') + .verify(certPem, s4, { padding: 'pss', saltLength }); + assert.strictEqual(verified, true, 'sign and verify (buffer, PSS)'); + + // Setting the salt length to -2 should always work for verification + verified = crypto.createVerify(algo) + .update('Test123') + .verify(certPem, s4, { padding: 'pss', saltLength: -2 }); + assert.strictEqual(verified, true, 'sign and verify (buffer, PSS)'); + }); + }); +} + +{ + assert.throws(() => { + crypto.createSign('RSA-SHA1') + .update('Test123') + .sign(keyPem, { padding: 'foo' }); + }, /^Error: Padding must be 'pkcs1' or 'pss'$/); +} + // Test throws exception when key options is null { assert.throws(() => { From e467227106c975ff65d079afcae3f42429b85a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 8 Mar 2017 00:15:01 +0100 Subject: [PATCH 02/17] Various improvements related to PR #11705 --- doc/api/crypto.md | 58 ++++++++++++---------- lib/crypto.js | 26 ++++------ src/env.h | 2 + src/node_constants.cc | 15 ++++++ src/node_crypto.cc | 61 ++++++++++++------------ test/parallel/test-crypto-sign-verify.js | 28 ++++++++--- 6 files changed, 110 insertions(+), 80 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 33a4c35fd54a3f..35a6c426e1163b 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -961,7 +961,7 @@ pQhByd5eyj3lgZ7m7jbchtdgyOF8Io/1ng== console.log(sign.sign(privateKey).toString('hex')); ``` -### sign.sign(private_key[, output_format][, options]) +### sign.sign(private_key[, output_format]) @@ -975,24 +975,20 @@ Calculates the signature on all the data passed through using either The `private_key` argument can be an object or a string. If `private_key` is a string, it is treated as a raw key with no passphrase. If `private_key` is an -object, it is interpreted as a hash containing two properties: +object, it is interpreted as a hash containing some of these properties: -* `key`: {string} - PEM encoded private key +* `key`: {string} - PEM encoded private key (required) * `passphrase`: {string} - passphrase for the private key +* `padding`: {String} - RSA padding, either `RSA_PKCS1_PADDING` (default) or + `RSA_PKCS1_PSS_PADDING` +* `saltLength`: {number} - salt length for RSASSA-PSS. The special value + `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, + `RSA_PSS_SALTLEN_MAX_SIGN` sets it to the maximum permissible value. The `output_format` can specify one of `'latin1'`, `'hex'` or `'base64'`. If `output_format` is provided a string is returned; otherwise a [`Buffer`][] is returned. -The optional `options` argument is an object which specifies additional -cryptographic parameters. Currently, the following options are supported: - -* `padding` : {String} - RSA padding, either `'pkcs1'` for RSASSA-PKCS1-v1_5 - (default) or `'pss'` for RSASSA-PSS -* `saltLength` : {number} - salt length for RSASSA-PSS. If this is set to `-1`, - the salt length will be set to the digest size. If this is set to `-2` - (default), the salt length will be set to the maximum permissible value. - The `Sign` object can not be again used after `sign.sign()` method has been called. Multiple calls to `sign.sign()` will result in an error being thrown. @@ -1079,7 +1075,7 @@ then `input_encoding` is ignored. This can be called many times with new data as it is streamed. -### verifier.verify(object, signature[, signature_format][, options]) +### verifier.verify(object, signature[, signature_format]) @@ -1088,24 +1084,24 @@ added: v0.1.92 - `signature_format` {string} Verifies the provided data using the given `object` and `signature`. -The `object` argument is a string containing a PEM encoded object, which can be -an RSA public key, a DSA public key, or an X.509 certificate. -The `signature` argument is the previously calculated signature for the data, in -the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. -If a `signature_format` is specified, the `signature` is expected to be a -string; otherwise `signature` is expected to be a [`Buffer`][] or -`Uint8Array`. - -The optional `options` argument is an object which specifies additional -cryptographic parameters. Currently, the following options are supported: +The `object` argument can be either a string containing a PEM encoded object, +which can be an RSA public key, a DSA public key, or an X.509 certificate, +or an object with some of the following properties: +>>>>>>> Various improvements related to PR #11705 -* `padding` : {String} - RSA padding, either `'pkcs1'` for RSASSA-PKCS1-v1_5 +* `key`: {string} - PEM encoded private key (required) +* `padding`: {string} - RSA padding, either `'pkcs1'` for RSASSA-PKCS1-v1_5 (default) or `'pss'` for RSASSA-PSS -* `saltLength` : {number} - salt length for RSASSA-PSS. If this is set to `-1`, +* `saltLength`: {number} - salt length for RSASSA-PSS. If this is set to `-1`, the salt length will be set to the digest size. A value of `-2` (default) causes the salt length to be automatically determined based on the PSS block structure. +The `signature` argument is the previously calculated signature for the data, in +the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. +If a `signature_format` is specified, the `signature` is expected to be a +string; otherwise `signature` is expected to be a [`Buffer`][]. + Returns `true` or `false` depending on the validity of the signature for the data and public key. @@ -2063,6 +2059,18 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. RSA_PKCS1_PSS_PADDING + + RSA_PSS_SALTLEN_DIGEST + + + + RSA_PSS_SALTLEN_MAX_SIGN + + + + RSA_PSS_SALTLEN_AUTO + + POINT_CONVERSION_COMPRESSED diff --git a/lib/crypto.js b/lib/crypto.js index 4d5928f3212df1..91193545f4c2ab 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -297,17 +297,12 @@ Sign.prototype._write = function _write(chunk, encoding, callback) { Sign.prototype.update = Hash.prototype.update; -Sign.prototype.sign = function sign(privateKey, encoding, options) { - if (!privateKey) +Sign.prototype.sign = function sign(options, encoding) { + if (!options) throw new Error('No key provided to sign'); - if (typeof encoding == 'object' && !options) { - options = encoding; - encoding = null; - } - - var key = privateKey.key || privateKey; - var passphrase = privateKey.passphrase || null; + var key = options.key || options; + var passphrase = options.passphrase || null; var ret = this._handle.sign(toBuf(key), null, passphrase, options); encoding = encoding || exports.DEFAULT_ENCODING; @@ -334,16 +329,11 @@ util.inherits(Verify, stream.Writable); Verify.prototype._write = Sign.prototype._write; Verify.prototype.update = Sign.prototype.update; -Verify.prototype.verify = function verify(object, signature, sigEncoding, - options) { - if (typeof sigEncoding == 'object' && !options) { - options = sigEncoding; - sigEncoding = null; - } - +Verify.prototype.verify = function verify(options, signature, sigEncoding) { + var key = options.key || options; sigEncoding = sigEncoding || exports.DEFAULT_ENCODING; - return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding), - null, options); + return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding), null, + options); }; function rsaPublic(method, defaultPadding) { diff --git a/src/env.h b/src/env.h index abb0e6d0e5a8a5..1043476031e7f0 100644 --- a/src/env.h +++ b/src/env.h @@ -179,6 +179,7 @@ namespace node { V(output_string, "output") \ V(order_string, "order") \ V(owner_string, "owner") \ + V(padding_string, "padding") \ V(parse_error_string, "Parse Error") \ V(path_string, "path") \ V(pbkdf2_error_string, "PBKDF2 Error") \ @@ -196,6 +197,7 @@ namespace node { V(rename_string, "rename") \ V(replacement_string, "replacement") \ V(retry_string, "retry") \ + V(salt_length_string, "saltLength") \ V(serial_string, "serial") \ V(scopeid_string, "scopeid") \ V(sent_shutdown_string, "sentShutdown") \ diff --git a/src/node_constants.cc b/src/node_constants.cc index 5bde53fcdf1ae5..5419f99a71656a 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -997,6 +997,21 @@ void DefineOpenSSLConstants(Local target) { NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING); #endif +#ifndef RSA_PSS_SALTLEN_DIGEST +#define RSA_PSS_SALTLEN_DIGEST -1 +#endif + NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_DIGEST); + +#ifndef RSA_PSS_SALTLEN_MAX_SIGN +#define RSA_PSS_SALTLEN_MAX_SIGN -2 +#endif + NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_MAX_SIGN); + +#ifndef RSA_PSS_SALTLEN_AUTO +#define RSA_PSS_SALTLEN_AUTO -2 +#endif + NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_AUTO); + #if HAVE_OPENSSL // NOTE: These are not defines NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED); diff --git a/src/node_crypto.cc b/src/node_crypto.cc index f0a3fb35a371cc..7fa24babee1e58 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3975,34 +3975,29 @@ void SignBase::CheckThrow(SignBase::Error error) { int SignBase::GetRSAOptions(Environment *env, v8::Local options, int *padding, int *saltlen) { - Local key = String::NewFromUtf8(env->isolate(), "padding"); - MaybeLocal maybePadding = options->Get(key); - if (!maybePadding.IsEmpty()) { - Local paddingValue = maybePadding.ToLocalChecked(); - if (paddingValue->IsString()) { - v8::String::Utf8Value paddingUtf8(paddingValue); - const char *paddingName = *paddingUtf8; - if (strcmp(paddingName, "pkcs1") == 0) { - *padding = RSA_PKCS1_PADDING; - } - else if (strcmp(paddingName, "pss") == 0) { - *padding = RSA_PKCS1_PSS_PADDING; - } - else { - env->ThrowError("Padding must be 'pkcs1' or 'pss'"); - return 0; - } + MaybeLocal maybePadding = options->Get(env->padding_string()); + if (maybePadding.IsEmpty()) return 0; + + Local paddingValue = maybePadding.ToLocalChecked(); + if (paddingValue->IsNumber()) { + switch (paddingValue->Int32Value()) { + case RSA_PKCS1_PADDING: + case RSA_PKCS1_PSS_PADDING: + *padding = paddingValue->Int32Value(); + break; + default: + env->ThrowError("Padding must be RSA_PKCS1_PADDING or RSA_PKCS1_PSS_PADDING"); + return 0; } } if (*padding == RSA_PKCS1_PSS_PADDING) { - key = String::NewFromUtf8(env->isolate(), "saltLength"); - MaybeLocal maybeSaltlen = options->Get(key); - if (!maybeSaltlen.IsEmpty()) { - Local saltlenValue = maybeSaltlen.ToLocalChecked(); - if (saltlenValue->IsNumber()) { - *saltlen = saltlenValue->Int32Value(); - } + MaybeLocal maybeSaltlen = options->Get(env->salt_length_string()); + if (maybeSaltlen.IsEmpty()) return 0; + + Local saltlenValue = maybeSaltlen.ToLocalChecked(); + if (saltlenValue->IsNumber()) { + *saltlen = saltlenValue->Int32Value(); } } @@ -4103,10 +4098,10 @@ static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, *s = 0; if (!EVP_DigestFinal_ex(mdctx, &(m[0]), &m_len)) - goto err; + return i; if (mdctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) { - size_t sltmp = (size_t)EVP_PKEY_size(pkey); + size_t sltmp = static_cast(EVP_PKEY_size(pkey)); i = 0; pkctx = EVP_PKEY_CTX_new(pkey, nullptr); if (!pkctx) @@ -4116,9 +4111,10 @@ static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) goto err; - if (padding == RSA_PKCS1_PSS_PADDING) + if (padding == RSA_PKCS1_PSS_PADDING) { if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, pss_saltlen) <= 0) goto err; + } } if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx->digest) <= 0) goto err; @@ -4237,8 +4233,9 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { int padding = RSA_PKCS1_PADDING; int saltlen = -2; - if (len >= 4 && args[3]->IsObject()) { - sign->GetRSAOptions(env, Local::Cast(args[3]), &padding, &saltlen); + if (args[3]->IsObject()) { + if (!sign->GetRSAOptions(env, Local::Cast(args[3]), &padding, &saltlen)) + return; } md_len = 8192; // Maximum key size is 8192 bits @@ -4427,9 +4424,10 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) goto err; - if (padding == RSA_PKCS1_PSS_PADDING) + if (padding == RSA_PKCS1_PSS_PADDING) { if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, saltlen) <= 0) goto err; + } } if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx_.digest) <= 0) goto err; @@ -4500,7 +4498,8 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { int padding = RSA_PKCS1_PADDING; int saltlen = -2; if (args.Length() >= 4 && args[3]->IsObject()) { - verify->GetRSAOptions(env, args[3]->ToObject(), &padding, &saltlen); + if (!verify->GetRSAOptions(env, args[3]->ToObject(), &padding, &saltlen)) + return; } bool verify_result; diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index f5a0f05ea21df7..fd133e14e938f3 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -79,17 +79,30 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); // Test sign and verify with the given parameters const s4 = crypto.createSign(algo) .update('Test123') - .sign(keyPem, null, { padding: 'pss', saltLength }); + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength + }); verified = crypto.createVerify(algo) .update('Test') .update('123') - .verify(certPem, s4, { padding: 'pss', saltLength }); + .verify({ + key: certPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength + }, s4); assert.strictEqual(verified, true, 'sign and verify (buffer, PSS)'); - // Setting the salt length to -2 should always work for verification + // Setting the salt length to RSA_PSS_SALTLEN_AUTO should always work for + // verification verified = crypto.createVerify(algo) .update('Test123') - .verify(certPem, s4, { padding: 'pss', saltLength: -2 }); + .verify({ + key: certPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO + }, s4); assert.strictEqual(verified, true, 'sign and verify (buffer, PSS)'); }); }); @@ -99,8 +112,11 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); assert.throws(() => { crypto.createSign('RSA-SHA1') .update('Test123') - .sign(keyPem, { padding: 'foo' }); - }, /^Error: Padding must be 'pkcs1' or 'pss'$/); + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_OAEP_PADDING + }); + }, /^Error: Padding must be RSA_PKCS1_PADDING or RSA_PKCS1_PSS_PADDING$/); } // Test throws exception when key options is null From 5a874a51c20266ecf5f16fe895889a5269b97c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 8 Mar 2017 00:24:40 +0100 Subject: [PATCH 03/17] Minor linting fixes --- src/node_crypto.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 7fa24babee1e58..64600429cc9d4e 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3986,7 +3986,8 @@ int SignBase::GetRSAOptions(Environment *env, v8::Local options, *padding = paddingValue->Int32Value(); break; default: - env->ThrowError("Padding must be RSA_PKCS1_PADDING or RSA_PKCS1_PSS_PADDING"); + env->ThrowError("Padding must be RSA_PKCS1_PADDING or " + "RSA_PKCS1_PSS_PADDING"); return 0; } } @@ -4234,7 +4235,8 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { int padding = RSA_PKCS1_PADDING; int saltlen = -2; if (args[3]->IsObject()) { - if (!sign->GetRSAOptions(env, Local::Cast(args[3]), &padding, &saltlen)) + Local options = Local::Cast(args[3]); + if (!sign->GetRSAOptions(env, options, &padding, &saltlen)) return; } @@ -4498,7 +4500,8 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { int padding = RSA_PKCS1_PADDING; int saltlen = -2; if (args.Length() >= 4 && args[3]->IsObject()) { - if (!verify->GetRSAOptions(env, args[3]->ToObject(), &padding, &saltlen)) + Local options = Local::Cast(args[3]); + if (!verify->GetRSAOptions(env, options, &padding, &saltlen)) return; } From 8a238820f502933642c5c622e6d2ca8e021e413a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 8 Mar 2017 12:14:46 +0100 Subject: [PATCH 04/17] Fix API description, error message --- doc/api/crypto.md | 15 +++++++-------- src/node_constants.cc | 15 ++++++--------- src/node_constants.h | 13 +++++++++++++ src/node_crypto.cc | 4 ++-- test/parallel/test-crypto-sign-verify.js | 2 +- 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 35a6c426e1163b..7f48a6368cf479 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -979,9 +979,9 @@ object, it is interpreted as a hash containing some of these properties: * `key`: {string} - PEM encoded private key (required) * `passphrase`: {string} - passphrase for the private key -* `padding`: {String} - RSA padding, either `RSA_PKCS1_PADDING` (default) or +* `padding`: {integer} - RSA padding, either `RSA_PKCS1_PADDING` (default) or `RSA_PKCS1_PSS_PADDING` -* `saltLength`: {number} - salt length for RSASSA-PSS. The special value +* `saltLength`: {integer} - salt length for RSASSA-PSS. The special value `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, `RSA_PSS_SALTLEN_MAX_SIGN` sets it to the maximum permissible value. @@ -1090,12 +1090,11 @@ or an object with some of the following properties: >>>>>>> Various improvements related to PR #11705 * `key`: {string} - PEM encoded private key (required) -* `padding`: {string} - RSA padding, either `'pkcs1'` for RSASSA-PKCS1-v1_5 - (default) or `'pss'` for RSASSA-PSS -* `saltLength`: {number} - salt length for RSASSA-PSS. If this is set to `-1`, - the salt length will be set to the digest size. A value of `-2` (default) - causes the salt length to be automatically determined based on the PSS block - structure. +* `padding`: {integer} - RSA padding, either `RSA_PKCS1_PADDING` (default) or + `RSA_PKCS1_PSS_PADDING` +* `saltLength`: {integer} - salt length for RSASSA-PSS. The special value + `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, + `RSA_PSS_SALTLEN_AUTO` causes it to be determined automatically. The `signature` argument is the previously calculated signature for the data, in the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. diff --git a/src/node_constants.cc b/src/node_constants.cc index 5419f99a71656a..8bc95392f26bcf 100644 --- a/src/node_constants.cc +++ b/src/node_constants.cc @@ -997,20 +997,17 @@ void DefineOpenSSLConstants(Local target) { NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING); #endif -#ifndef RSA_PSS_SALTLEN_DIGEST -#define RSA_PSS_SALTLEN_DIGEST -1 -#endif +#ifdef RSA_PSS_SALTLEN_DIGEST NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_DIGEST); - -#ifndef RSA_PSS_SALTLEN_MAX_SIGN -#define RSA_PSS_SALTLEN_MAX_SIGN -2 #endif - NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_MAX_SIGN); -#ifndef RSA_PSS_SALTLEN_AUTO -#define RSA_PSS_SALTLEN_AUTO -2 +#ifdef RSA_PSS_SALTLEN_MAX_SIGN + NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_MAX_SIGN); #endif + +#ifdef RSA_PSS_SALTLEN_AUTO NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_AUTO); +#endif #if HAVE_OPENSSL // NOTE: These are not defines diff --git a/src/node_constants.h b/src/node_constants.h index 047d8fc5e7e2f5..1de420e2def571 100644 --- a/src/node_constants.h +++ b/src/node_constants.h @@ -28,6 +28,19 @@ #include "v8.h" #if HAVE_OPENSSL + +#ifndef RSA_PSS_SALTLEN_DIGEST +#define RSA_PSS_SALTLEN_DIGEST -1 +#endif + +#ifndef RSA_PSS_SALTLEN_MAX_SIGN +#define RSA_PSS_SALTLEN_MAX_SIGN -2 +#endif + +#ifndef RSA_PSS_SALTLEN_AUTO +#define RSA_PSS_SALTLEN_AUTO -2 +#endif + #define DEFAULT_CIPHER_LIST_CORE "ECDHE-RSA-AES128-GCM-SHA256:" \ "ECDHE-ECDSA-AES128-GCM-SHA256:" \ "ECDHE-RSA-AES256-GCM-SHA384:" \ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 64600429cc9d4e..464cb534487387 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3986,7 +3986,7 @@ int SignBase::GetRSAOptions(Environment *env, v8::Local options, *padding = paddingValue->Int32Value(); break; default: - env->ThrowError("Padding must be RSA_PKCS1_PADDING or " + env->ThrowError("padding must be RSA_PKCS1_PADDING or " "RSA_PKCS1_PSS_PADDING"); return 0; } @@ -4098,7 +4098,7 @@ static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, EVP_PKEY_CTX *pkctx = nullptr; *s = 0; - if (!EVP_DigestFinal_ex(mdctx, &(m[0]), &m_len)) + if (!EVP_DigestFinal_ex(mdctx, m, &m_len)) return i; if (mdctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) { diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index fd133e14e938f3..a39994ad89bc6e 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -116,7 +116,7 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, /^Error: Padding must be RSA_PKCS1_PADDING or RSA_PKCS1_PSS_PADDING$/); + }, /^Error: padding must be RSA_PKCS1_PADDING or RSA_PKCS1_PSS_PADDING$/); } // Test throws exception when key options is null From 15abaae02c99c2623aae3f097ccc266c0fa8a7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 8 Mar 2017 14:18:33 +0100 Subject: [PATCH 05/17] Use defined constants instead of values --- doc/api/crypto.md | 5 +++-- src/node_crypto.cc | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 7f48a6368cf479..44118529f1fbb5 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -983,7 +983,8 @@ object, it is interpreted as a hash containing some of these properties: `RSA_PKCS1_PSS_PADDING` * `saltLength`: {integer} - salt length for RSASSA-PSS. The special value `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, - `RSA_PSS_SALTLEN_MAX_SIGN` sets it to the maximum permissible value. + `RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the maximum permissible + value. The `output_format` can specify one of `'latin1'`, `'hex'` or `'base64'`. If `output_format` is provided a string is returned; otherwise a [`Buffer`][] is @@ -1094,7 +1095,7 @@ or an object with some of the following properties: `RSA_PKCS1_PSS_PADDING` * `saltLength`: {integer} - salt length for RSASSA-PSS. The special value `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, - `RSA_PSS_SALTLEN_AUTO` causes it to be determined automatically. + `RSA_PSS_SALTLEN_AUTO` (default) causes it to be determined automatically. The `signature` argument is the previously calculated signature for the data, in the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 464cb534487387..6d4ae333634128 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -21,6 +21,7 @@ #include "node.h" #include "node_buffer.h" +#include "node_constants.h" #include "node_crypto.h" #include "node_crypto_bio.h" #include "node_crypto_groups.h" @@ -4233,7 +4234,7 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { char* buf = Buffer::Data(args[0]); int padding = RSA_PKCS1_PADDING; - int saltlen = -2; + int saltlen = RSA_PSS_SALTLEN_MAX_SIGN; if (args[3]->IsObject()) { Local options = Local::Cast(args[3]); if (!sign->GetRSAOptions(env, options, &padding, &saltlen)) @@ -4498,7 +4499,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { } int padding = RSA_PKCS1_PADDING; - int saltlen = -2; + int saltlen = RSA_PSS_SALTLEN_AUTO; if (args.Length() >= 4 && args[3]->IsObject()) { Local options = Local::Cast(args[3]); if (!verify->GetRSAOptions(env, options, &padding, &saltlen)) From d75601ed1a955396f331f5131f36b7a981b0a7e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 8 Mar 2017 19:01:31 +0100 Subject: [PATCH 06/17] Improvements requested by sam-github --- doc/api/crypto.md | 24 ++++---- src/node_crypto.cc | 74 ++++++++++++------------ src/node_crypto.h | 4 +- test/parallel/test-crypto-sign-verify.js | 4 +- 4 files changed, 55 insertions(+), 51 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 44118529f1fbb5..2e71046547d860 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -981,10 +981,10 @@ object, it is interpreted as a hash containing some of these properties: * `passphrase`: {string} - passphrase for the private key * `padding`: {integer} - RSA padding, either `RSA_PKCS1_PADDING` (default) or `RSA_PKCS1_PSS_PADDING` -* `saltLength`: {integer} - salt length for RSASSA-PSS. The special value - `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, - `RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the maximum permissible - value. +* `saltLength`: {integer} - salt length for when padding is + `RSA_PKCS1_PSS_PADDING`. The special value `RSA_PSS_SALTLEN_DIGEST` sets the + salt length to the digest size, `RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it + to the maximum permissible value. The `output_format` can specify one of `'latin1'`, `'hex'` or `'base64'`. If `output_format` is provided a string is returned; otherwise a [`Buffer`][] is @@ -1093,9 +1093,10 @@ or an object with some of the following properties: * `key`: {string} - PEM encoded private key (required) * `padding`: {integer} - RSA padding, either `RSA_PKCS1_PADDING` (default) or `RSA_PKCS1_PSS_PADDING` -* `saltLength`: {integer} - salt length for RSASSA-PSS. The special value - `RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest size, - `RSA_PSS_SALTLEN_AUTO` (default) causes it to be determined automatically. +* `saltLength`: {integer} - salt length for when padding is + `RSA_PKCS1_PSS_PADDING`. The special value `RSA_PSS_SALTLEN_DIGEST` sets the + salt length to the digest size, `RSA_PSS_SALTLEN_AUTO` (default) causes it to + be determined automatically. The `signature` argument is the previously calculated signature for the data, in the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. @@ -2061,15 +2062,18 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. RSA_PSS_SALTLEN_DIGEST - + Sets the salt length for `RSA_PKCS1_PSS_PADDING` to the digest size + when signing or verifying. RSA_PSS_SALTLEN_MAX_SIGN - + Sets the salt length for `RSA_PKCS1_PSS_PADDING` to the maximum + permissible value when signing data. RSA_PSS_SALTLEN_AUTO - + Causes the salt length for `RSA_PKCS1_PSS_PADDING` to be determined + automatically when verifying a signature. POINT_CONVERSION_COMPRESSED diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 6d4ae333634128..840a300fb8f70a 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3974,22 +3974,22 @@ void SignBase::CheckThrow(SignBase::Error error) { } } -int SignBase::GetRSAOptions(Environment *env, v8::Local options, - int *padding, int *saltlen) { +int SignBase::GetRSAOptions(Environment* env, v8::Local options, + int* padding, int* saltlen) { MaybeLocal maybePadding = options->Get(env->padding_string()); if (maybePadding.IsEmpty()) return 0; Local paddingValue = maybePadding.ToLocalChecked(); if (paddingValue->IsNumber()) { - switch (paddingValue->Int32Value()) { - case RSA_PKCS1_PADDING: - case RSA_PKCS1_PSS_PADDING: - *padding = paddingValue->Int32Value(); - break; - default: - env->ThrowError("padding must be RSA_PKCS1_PADDING or " - "RSA_PKCS1_PSS_PADDING"); - return 0; + *padding = paddingValue->Int32Value(); + switch (*padding) { + case RSA_PKCS1_PADDING: + case RSA_PKCS1_PSS_PADDING: + break; + default: + env->ThrowError("padding must be RSA_PKCS1_PADDING or " + "RSA_PKCS1_PSS_PADDING"); + return 0; } } @@ -4091,20 +4091,20 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { sign->CheckThrow(err); } -static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, - EVP_PKEY *pkey, int padding, int pss_saltlen) { +static int Node_SignFinal(EVP_MD_CTX* mdctx, unsigned char* md, + unsigned int* sig_len, EVP_PKEY* pkey, int padding, + int pss_salt_len) { unsigned char m[EVP_MAX_MD_SIZE]; unsigned int m_len; - int i = 0; - EVP_PKEY_CTX *pkctx = nullptr; + int rv = 0; + EVP_PKEY_CTX* pkctx = nullptr; - *s = 0; + *sig_len = 0; if (!EVP_DigestFinal_ex(mdctx, m, &m_len)) - return i; + return rv; if (mdctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) { size_t sltmp = static_cast(EVP_PKEY_size(pkey)); - i = 0; pkctx = EVP_PKEY_CTX_new(pkey, nullptr); if (!pkctx) goto err; @@ -4114,7 +4114,7 @@ static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) goto err; if (padding == RSA_PKCS1_PSS_PADDING) { - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, pss_saltlen) <= 0) + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, pss_salt_len) <= 0) goto err; } } @@ -4122,11 +4122,11 @@ static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, goto err; if (EVP_PKEY_sign(pkctx, md, &sltmp, m, m_len) <= 0) goto err; - *s = sltmp; - i = 1; + *sig_len = sltmp; + rv = 1; err: EVP_PKEY_CTX_free(pkctx); - return i; + return rv; } if (!mdctx->digest->sign) { @@ -4134,7 +4134,7 @@ static int Node_SignFinal(EVP_MD_CTX *mdctx, unsigned char *md, unsigned int *s, return 0; } - return (mdctx->digest->sign(mdctx->digest->type, m, m_len, md, s, + return (mdctx->digest->sign(mdctx->digest->type, m, m_len, md, sig_len, pkey->pkey.ptr)); } @@ -4142,9 +4142,9 @@ SignBase::Error Sign::SignFinal(const char* key_pem, int key_pem_len, const char* passphrase, unsigned char** sig, - unsigned int *sig_len, + unsigned int* sig_len, int padding, - int saltlen) { + int salt_len) { if (!initialised_) return kSignNotInitialised; @@ -4190,7 +4190,7 @@ SignBase::Error Sign::SignFinal(const char* key_pem, } #endif // NODE_FIPS_MODE - if (Node_SignFinal(&mdctx_, *sig, sig_len, pkey, padding, saltlen)) + if (Node_SignFinal(&mdctx_, *sig, sig_len, pkey, padding, salt_len)) fatal = false; initialised_ = false; @@ -4234,10 +4234,10 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { char* buf = Buffer::Data(args[0]); int padding = RSA_PKCS1_PADDING; - int saltlen = RSA_PSS_SALTLEN_MAX_SIGN; + int salt_len = RSA_PSS_SALTLEN_MAX_SIGN; if (args[3]->IsObject()) { Local options = Local::Cast(args[3]); - if (!sign->GetRSAOptions(env, options, &padding, &saltlen)) + if (!sign->GetRSAOptions(env, options, &padding, &salt_len)) return; } @@ -4254,7 +4254,7 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { &md_value, &md_len, padding, - saltlen); + salt_len); if (err != kSignOk) { delete[] md_value; md_value = nullptr; @@ -4378,7 +4378,7 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, unsigned char m[EVP_MAX_MD_SIZE]; unsigned int m_len; int r = 0; - EVP_PKEY_CTX *pkctx = nullptr; + EVP_PKEY_CTX* pkctx = nullptr; bp = BIO_new_mem_buf(const_cast(key_pem), key_pem_len); if (bp == nullptr) @@ -4435,10 +4435,10 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx_.digest) <= 0) goto err; r = EVP_PKEY_verify(pkctx, - reinterpret_cast(sig), - siglen, - m, - m_len); + reinterpret_cast(sig), + siglen, + m, + m_len); fatal = false; err: @@ -4499,15 +4499,15 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { } int padding = RSA_PKCS1_PADDING; - int saltlen = RSA_PSS_SALTLEN_AUTO; + int salt_len = RSA_PSS_SALTLEN_AUTO; if (args.Length() >= 4 && args[3]->IsObject()) { Local options = Local::Cast(args[3]); - if (!verify->GetRSAOptions(env, options, &padding, &saltlen)) + if (!verify->GetRSAOptions(env, options, &padding, &salt_len)) return; } bool verify_result; - Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, padding, saltlen, + Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, padding, salt_len, &verify_result); if (args[1]->IsString()) delete[] hbuf; diff --git a/src/node_crypto.h b/src/node_crypto.h index 3982a60683a2da..5ff4c7fa6365c4 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -577,8 +577,8 @@ class SignBase : public BaseObject { protected: void CheckThrow(Error error); - int GetRSAOptions(Environment *env, v8::Local options, - int *padding, int *saltlen); + int GetRSAOptions(Environment* env, v8::Local options, + int* padding, int* salt_len); EVP_MD_CTX mdctx_; /* coverity[member_decl] */ bool initialised_; diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index a39994ad89bc6e..e6917943657db4 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -72,8 +72,8 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); } { - [ 'RSA-SHA1', 'RSA-SHA256' ].forEach((algo) => { - [ null, -2, -1, 0, 16, 32, 64 ].forEach((saltLength) => { + ['RSA-SHA1', 'RSA-SHA256'].forEach((algo) => { + [null, -2, -1, 0, 16, 32, 64].forEach((saltLength) => { let verified; // Test sign and verify with the given parameters From 5e78d9cc79cc9e0338c74229ececa4003b7a62c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 11 Mar 2017 12:40:43 +0100 Subject: [PATCH 07/17] Changes requested by bnoordhuis --- doc/api/crypto.md | 29 ++++++++++++++------------ src/node_crypto.cc | 52 ++++++++++++++++++++++++++-------------------- src/node_crypto.h | 2 +- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 2e71046547d860..29b17f49a5c6eb 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -975,16 +975,18 @@ Calculates the signature on all the data passed through using either The `private_key` argument can be an object or a string. If `private_key` is a string, it is treated as a raw key with no passphrase. If `private_key` is an -object, it is interpreted as a hash containing some of these properties: +object, it must contain one or more of the following properties: * `key`: {string} - PEM encoded private key (required) * `passphrase`: {string} - passphrase for the private key -* `padding`: {integer} - RSA padding, either `RSA_PKCS1_PADDING` (default) or - `RSA_PKCS1_PSS_PADDING` +* `padding`: {integer} - Optional padding value for RSA, one of the following: + * `crypto.constants.RSA_PKCS1_PADDING` (default) + * `crypto.constants.RSA_PKCS1_PSS_PADDING` * `saltLength`: {integer} - salt length for when padding is - `RSA_PKCS1_PSS_PADDING`. The special value `RSA_PSS_SALTLEN_DIGEST` sets the - salt length to the digest size, `RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it - to the maximum permissible value. + `RSA_PKCS1_PSS_PADDING`. The special value + `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest + size, `crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN` (default) sets it to the + maximum permissible value. The `output_format` can specify one of `'latin1'`, `'hex'` or `'base64'`. If `output_format` is provided a string is returned; otherwise a [`Buffer`][] is @@ -1087,16 +1089,17 @@ added: v0.1.92 Verifies the provided data using the given `object` and `signature`. The `object` argument can be either a string containing a PEM encoded object, which can be an RSA public key, a DSA public key, or an X.509 certificate, -or an object with some of the following properties: ->>>>>>> Various improvements related to PR #11705 +or an object with one or more of the following properties: * `key`: {string} - PEM encoded private key (required) -* `padding`: {integer} - RSA padding, either `RSA_PKCS1_PADDING` (default) or - `RSA_PKCS1_PSS_PADDING` +* `padding`: {integer} - Optional padding value for RSA, one of the following: + * `crypto.constants.RSA_PKCS1_PADDING` (default) + * `crypto.constants.RSA_PKCS1_PSS_PADDING` * `saltLength`: {integer} - salt length for when padding is - `RSA_PKCS1_PSS_PADDING`. The special value `RSA_PSS_SALTLEN_DIGEST` sets the - salt length to the digest size, `RSA_PSS_SALTLEN_AUTO` (default) causes it to - be determined automatically. + `RSA_PKCS1_PSS_PADDING`. The special value + `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest + size, `crypto.constants.RSA_PSS_SALTLEN_AUTO` (default) causes it to be + determined automatically. The `signature` argument is the previously calculated signature for the data, in the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 840a300fb8f70a..9b61b81a314464 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -102,6 +102,7 @@ using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; +using v8::Maybe; using v8::MaybeLocal; using v8::Null; using v8::Object; @@ -3974,14 +3975,18 @@ void SignBase::CheckThrow(SignBase::Error error) { } } -int SignBase::GetRSAOptions(Environment* env, v8::Local options, - int* padding, int* saltlen) { - MaybeLocal maybePadding = options->Get(env->padding_string()); - if (maybePadding.IsEmpty()) return 0; +bool SignBase::GetRSAOptions(Environment* env, v8::Local options, + int* padding, int* salt_len) { + MaybeLocal maybe_padding = options->Get(env->context(), + env->padding_string()); + if (maybe_padding.IsEmpty()) return false; - Local paddingValue = maybePadding.ToLocalChecked(); - if (paddingValue->IsNumber()) { - *padding = paddingValue->Int32Value(); + Local padding_value = maybe_padding.ToLocalChecked(); + if (padding_value->IsInt32()) { + Maybe maybe_padding = padding_value->Int32Value(env->context()); + if (maybe_padding.IsNothing()) return false; + + *padding = maybe_padding.ToChecked(); switch (*padding) { case RSA_PKCS1_PADDING: case RSA_PKCS1_PSS_PADDING: @@ -3989,21 +3994,24 @@ int SignBase::GetRSAOptions(Environment* env, v8::Local options, default: env->ThrowError("padding must be RSA_PKCS1_PADDING or " "RSA_PKCS1_PSS_PADDING"); - return 0; + return false; } } if (*padding == RSA_PKCS1_PSS_PADDING) { - MaybeLocal maybeSaltlen = options->Get(env->salt_length_string()); - if (maybeSaltlen.IsEmpty()) return 0; - - Local saltlenValue = maybeSaltlen.ToLocalChecked(); - if (saltlenValue->IsNumber()) { - *saltlen = saltlenValue->Int32Value(); + MaybeLocal maybe_saltlen = options->Get(env->context(), + env->salt_length_string()); + if (maybe_saltlen.IsEmpty()) return 0; + + Local saltlen_value = maybe_saltlen.ToLocalChecked(); + if (saltlen_value->IsInt32()) { + Maybe maybe_salt_len = saltlen_value->Int32Value(env->context()); + if (maybe_salt_len.IsNothing()) return false; + *salt_len = maybe_salt_len.ToChecked(); } } - return 1; + return true; } @@ -4106,7 +4114,7 @@ static int Node_SignFinal(EVP_MD_CTX* mdctx, unsigned char* md, if (mdctx->digest->flags & EVP_MD_FLAG_PKEY_METHOD_SIGNATURE) { size_t sltmp = static_cast(EVP_PKEY_size(pkey)); pkctx = EVP_PKEY_CTX_new(pkey, nullptr); - if (!pkctx) + if (pkctx == nullptr) goto err; if (EVP_PKEY_sign_init(pkctx) <= 0) goto err; @@ -4129,13 +4137,13 @@ static int Node_SignFinal(EVP_MD_CTX* mdctx, unsigned char* md, return rv; } - if (!mdctx->digest->sign) { + if (mdctx->digest->sign == nullptr) { EVPerr(EVP_F_EVP_SIGNFINAL, EVP_R_NO_SIGN_FUNCTION_CONFIGURED); return 0; } - return (mdctx->digest->sign(mdctx->digest->type, m, m_len, md, sig_len, - pkey->pkey.ptr)); + return mdctx->digest->sign(mdctx->digest->type, m, m_len, md, sig_len, + pkey->pkey.ptr); } SignBase::Error Sign::SignFinal(const char* key_pem, @@ -4236,7 +4244,7 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { int padding = RSA_PKCS1_PADDING; int salt_len = RSA_PSS_SALTLEN_MAX_SIGN; if (args[3]->IsObject()) { - Local options = Local::Cast(args[3]); + Local options = args[3].As(); if (!sign->GetRSAOptions(env, options, &padding, &salt_len)) return; } @@ -4420,7 +4428,7 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, fatal = false; pkctx = EVP_PKEY_CTX_new(pkey, nullptr); - if (!pkctx) + if (pkctx == nullptr) goto err; if (EVP_PKEY_verify_init(pkctx) <= 0) goto err; @@ -4501,7 +4509,7 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { int padding = RSA_PKCS1_PADDING; int salt_len = RSA_PSS_SALTLEN_AUTO; if (args.Length() >= 4 && args[3]->IsObject()) { - Local options = Local::Cast(args[3]); + Local options = args[3].As(); if (!verify->GetRSAOptions(env, options, &padding, &salt_len)) return; } diff --git a/src/node_crypto.h b/src/node_crypto.h index 5ff4c7fa6365c4..cd36b3da7d89c2 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -577,7 +577,7 @@ class SignBase : public BaseObject { protected: void CheckThrow(Error error); - int GetRSAOptions(Environment* env, v8::Local options, + bool GetRSAOptions(Environment* env, v8::Local options, int* padding, int* salt_len); EVP_MD_CTX mdctx_; /* coverity[member_decl] */ From 5e5f2e466133b0ef5973b3f40c2352b400ff093b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 14 Mar 2017 14:12:01 +0100 Subject: [PATCH 08/17] Move application of RSA options out of sign/verify --- src/node_crypto.cc | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 9b61b81a314464..6e25b5554a7803 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4014,6 +4014,19 @@ bool SignBase::GetRSAOptions(Environment* env, v8::Local options, return true; } +static bool ApplyRSAOptions(EVP_PKEY* pkey, EVP_PKEY_CTX* pkctx, int padding, int salt_len) { + if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { + if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) + return false; + if (padding == RSA_PKCS1_PSS_PADDING) { + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, salt_len) <= 0) + return false; + } + } + + return true; +} + void Sign::Initialize(Environment* env, v8::Local target) { @@ -4118,14 +4131,8 @@ static int Node_SignFinal(EVP_MD_CTX* mdctx, unsigned char* md, goto err; if (EVP_PKEY_sign_init(pkctx) <= 0) goto err; - if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { - if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) - goto err; - if (padding == RSA_PKCS1_PSS_PADDING) { - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, pss_salt_len) <= 0) - goto err; - } - } + if (!ApplyRSAOptions(pkey, pkctx, padding, pss_salt_len)) + goto err; if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx->digest) <= 0) goto err; if (EVP_PKEY_sign(pkctx, md, &sltmp, m, m_len) <= 0) @@ -4432,14 +4439,8 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, goto err; if (EVP_PKEY_verify_init(pkctx) <= 0) goto err; - if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { - if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) - goto err; - if (padding == RSA_PKCS1_PSS_PADDING) { - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, saltlen) <= 0) - goto err; - } - } + if (!ApplyRSAOptions(pkey, pkctx, padding, saltlen)) + goto err; if (EVP_PKEY_CTX_set_signature_md(pkctx, mdctx_.digest) <= 0) goto err; r = EVP_PKEY_verify(pkctx, From b9a67799a178fe66f2fe0488a569150e67df456b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 14 Mar 2017 14:24:04 +0100 Subject: [PATCH 09/17] Fix linter error --- src/node_crypto.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 6e25b5554a7803..94e2f73845cadf 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4014,7 +4014,8 @@ bool SignBase::GetRSAOptions(Environment* env, v8::Local options, return true; } -static bool ApplyRSAOptions(EVP_PKEY* pkey, EVP_PKEY_CTX* pkctx, int padding, int salt_len) { +static bool ApplyRSAOptions(EVP_PKEY* pkey, EVP_PKEY_CTX* pkctx, int padding, + int salt_len) { if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { if (EVP_PKEY_CTX_set_rsa_padding(pkctx, padding) <= 0) return false; From fcd705351d3de68943d3ed248fbd592bd0da7528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 14 Mar 2017 20:21:19 +0100 Subject: [PATCH 10/17] Move RSA option parsing to crypto.js --- lib/crypto.js | 21 ++++++- src/env.h | 2 - src/node_crypto.cc | 71 ++++++------------------ src/node_crypto.h | 2 - test/parallel/test-crypto-sign-verify.js | 2 +- 5 files changed, 38 insertions(+), 60 deletions(-) diff --git a/lib/crypto.js b/lib/crypto.js index 91193545f4c2ab..97412380346516 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -303,7 +303,16 @@ Sign.prototype.sign = function sign(options, encoding) { var key = options.key || options; var passphrase = options.passphrase || null; - var ret = this._handle.sign(toBuf(key), null, passphrase, options); + + // Options specific to RSA + var rsaPadding = options.padding || constants.RSA_PKCS1_PADDING; + var pssSaltLength = constants.RSA_PSS_SALTLEN_MAX_SIGN; + if (typeof options.saltLength === 'number') { + pssSaltLength = options.saltLength; + } + + var ret = this._handle.sign(toBuf(key), null, passphrase, rsaPadding, + pssSaltLength); encoding = encoding || exports.DEFAULT_ENCODING; if (encoding && encoding !== 'buffer') @@ -332,8 +341,16 @@ Verify.prototype.update = Sign.prototype.update; Verify.prototype.verify = function verify(options, signature, sigEncoding) { var key = options.key || options; sigEncoding = sigEncoding || exports.DEFAULT_ENCODING; + + // Options specific to RSA + var rsaPadding = options.padding || constants.RSA_PKCS1_PADDING; + var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO; + if (typeof options.saltLength === 'number') { + pssSaltLength = options.saltLength; + } + return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding), null, - options); + rsaPadding, pssSaltLength); }; function rsaPublic(method, defaultPadding) { diff --git a/src/env.h b/src/env.h index 1043476031e7f0..abb0e6d0e5a8a5 100644 --- a/src/env.h +++ b/src/env.h @@ -179,7 +179,6 @@ namespace node { V(output_string, "output") \ V(order_string, "order") \ V(owner_string, "owner") \ - V(padding_string, "padding") \ V(parse_error_string, "Parse Error") \ V(path_string, "path") \ V(pbkdf2_error_string, "PBKDF2 Error") \ @@ -197,7 +196,6 @@ namespace node { V(rename_string, "rename") \ V(replacement_string, "replacement") \ V(retry_string, "retry") \ - V(salt_length_string, "saltLength") \ V(serial_string, "serial") \ V(scopeid_string, "scopeid") \ V(sent_shutdown_string, "sentShutdown") \ diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 94e2f73845cadf..7a9656f2060e86 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3975,45 +3975,6 @@ void SignBase::CheckThrow(SignBase::Error error) { } } -bool SignBase::GetRSAOptions(Environment* env, v8::Local options, - int* padding, int* salt_len) { - MaybeLocal maybe_padding = options->Get(env->context(), - env->padding_string()); - if (maybe_padding.IsEmpty()) return false; - - Local padding_value = maybe_padding.ToLocalChecked(); - if (padding_value->IsInt32()) { - Maybe maybe_padding = padding_value->Int32Value(env->context()); - if (maybe_padding.IsNothing()) return false; - - *padding = maybe_padding.ToChecked(); - switch (*padding) { - case RSA_PKCS1_PADDING: - case RSA_PKCS1_PSS_PADDING: - break; - default: - env->ThrowError("padding must be RSA_PKCS1_PADDING or " - "RSA_PKCS1_PSS_PADDING"); - return false; - } - } - - if (*padding == RSA_PKCS1_PSS_PADDING) { - MaybeLocal maybe_saltlen = options->Get(env->context(), - env->salt_length_string()); - if (maybe_saltlen.IsEmpty()) return 0; - - Local saltlen_value = maybe_saltlen.ToLocalChecked(); - if (saltlen_value->IsInt32()) { - Maybe maybe_salt_len = saltlen_value->Int32Value(env->context()); - if (maybe_salt_len.IsNothing()) return false; - *salt_len = maybe_salt_len.ToChecked(); - } - } - - return true; -} - static bool ApplyRSAOptions(EVP_PKEY* pkey, EVP_PKEY_CTX* pkctx, int padding, int salt_len) { if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { @@ -4249,13 +4210,15 @@ void Sign::SignFinal(const FunctionCallbackInfo& args) { size_t buf_len = Buffer::Length(args[0]); char* buf = Buffer::Data(args[0]); - int padding = RSA_PKCS1_PADDING; - int salt_len = RSA_PSS_SALTLEN_MAX_SIGN; - if (args[3]->IsObject()) { - Local options = args[3].As(); - if (!sign->GetRSAOptions(env, options, &padding, &salt_len)) - return; - } + CHECK(args[3]->IsInt32()); + Maybe maybe_padding = args[3]->Int32Value(env->context()); + CHECK(maybe_padding.IsJust()); + int padding = maybe_padding.ToChecked(); + + CHECK(args[4]->IsInt32()); + Maybe maybe_salt_len = args[4]->Int32Value(env->context()); + CHECK(maybe_salt_len.IsJust()); + int salt_len = maybe_salt_len.ToChecked(); md_len = 8192; // Maximum key size is 8192 bits md_value = new unsigned char[md_len]; @@ -4508,13 +4471,15 @@ void Verify::VerifyFinal(const FunctionCallbackInfo& args) { hbuf = Buffer::Data(args[1]); } - int padding = RSA_PKCS1_PADDING; - int salt_len = RSA_PSS_SALTLEN_AUTO; - if (args.Length() >= 4 && args[3]->IsObject()) { - Local options = args[3].As(); - if (!verify->GetRSAOptions(env, options, &padding, &salt_len)) - return; - } + CHECK(args[3]->IsInt32()); + Maybe maybe_padding = args[3]->Int32Value(env->context()); + CHECK(maybe_padding.IsJust()); + int padding = maybe_padding.ToChecked(); + + CHECK(args[4]->IsInt32()); + Maybe maybe_salt_len = args[4]->Int32Value(env->context()); + CHECK(maybe_salt_len.IsJust()); + int salt_len = maybe_salt_len.ToChecked(); bool verify_result; Error err = verify->VerifyFinal(kbuf, klen, hbuf, hlen, padding, salt_len, diff --git a/src/node_crypto.h b/src/node_crypto.h index cd36b3da7d89c2..ffb8444ce60145 100644 --- a/src/node_crypto.h +++ b/src/node_crypto.h @@ -577,8 +577,6 @@ class SignBase : public BaseObject { protected: void CheckThrow(Error error); - bool GetRSAOptions(Environment* env, v8::Local options, - int* padding, int* salt_len); EVP_MD_CTX mdctx_; /* coverity[member_decl] */ bool initialised_; diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index e6917943657db4..864915152f2d4e 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -116,7 +116,7 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); key: keyPem, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING }); - }, /^Error: padding must be RSA_PKCS1_PADDING or RSA_PKCS1_PSS_PADDING$/); + }, /^Error:.*illegal or unsupported padding mode$/); } // Test throws exception when key options is null From 287e2d65d276062e67906e4fca954ab1f26b2cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 14 Mar 2017 21:17:17 +0100 Subject: [PATCH 11/17] Remove unused using --- src/node_crypto.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 7a9656f2060e86..bb2b38c1ca8478 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -103,7 +103,6 @@ using v8::Integer; using v8::Isolate; using v8::Local; using v8::Maybe; -using v8::MaybeLocal; using v8::Null; using v8::Object; using v8::Persistent; From 8dc383e7392b435329d54e2f891ef0067b245275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 15 Mar 2017 17:15:44 +0100 Subject: [PATCH 12/17] Strict checks and more tests --- lib/crypto.js | 38 ++++-- test/parallel/test-crypto-sign-verify.js | 149 ++++++++++++++++++----- 2 files changed, 147 insertions(+), 40 deletions(-) diff --git a/lib/crypto.js b/lib/crypto.js index 97412380346516..a7cbb6f80ac4e3 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -305,10 +305,22 @@ Sign.prototype.sign = function sign(options, encoding) { var passphrase = options.passphrase || null; // Options specific to RSA - var rsaPadding = options.padding || constants.RSA_PKCS1_PADDING; - var pssSaltLength = constants.RSA_PSS_SALTLEN_MAX_SIGN; - if (typeof options.saltLength === 'number') { - pssSaltLength = options.saltLength; + var rsaPadding = constants.RSA_PKCS1_PADDING; + if (options.hasOwnProperty('padding')) { + if (options.padding === options.padding >> 0) { + rsaPadding = options.padding; + } else { + throw new TypeError('padding must be an integer'); + } + } + + var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO; + if (options.hasOwnProperty('saltLength')) { + if (options.saltLength === options.saltLength >> 0) { + pssSaltLength = options.saltLength; + } else { + throw new TypeError('saltLength must be an integer'); + } } var ret = this._handle.sign(toBuf(key), null, passphrase, rsaPadding, @@ -343,10 +355,22 @@ Verify.prototype.verify = function verify(options, signature, sigEncoding) { sigEncoding = sigEncoding || exports.DEFAULT_ENCODING; // Options specific to RSA - var rsaPadding = options.padding || constants.RSA_PKCS1_PADDING; + var rsaPadding = constants.RSA_PKCS1_PADDING; + if (options.hasOwnProperty('padding')) { + if (options.padding === options.padding >> 0) { + rsaPadding = options.padding; + } else { + throw new TypeError('padding must be an integer'); + } + } + var pssSaltLength = constants.RSA_PSS_SALTLEN_AUTO; - if (typeof options.saltLength === 'number') { - pssSaltLength = options.saltLength; + if (options.hasOwnProperty('saltLength')) { + if (options.saltLength === options.saltLength >> 0) { + pssSaltLength = options.saltLength; + } else { + throw new TypeError('saltLength must be an integer'); + } } return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding), null, diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 864915152f2d4e..512f28dd61a491 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -12,6 +12,7 @@ const crypto = require('crypto'); // Test certificates const certPem = fs.readFileSync(common.fixturesDir + '/test_cert.pem', 'ascii'); const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); +const modSize = 1024; // Test signing and verifying { @@ -71,44 +72,126 @@ const keyPem = fs.readFileSync(common.fixturesDir + '/test_key.pem', 'ascii'); assert.strictEqual(verified, true, 'sign and verify (stream)'); } +// Special tests for RSA_PKCS1_PSS_PADDING { - ['RSA-SHA1', 'RSA-SHA256'].forEach((algo) => { - [null, -2, -1, 0, 16, 32, 64].forEach((saltLength) => { - let verified; - - // Test sign and verify with the given parameters - const s4 = crypto.createSign(algo) - .update('Test123') - .sign({ - key: keyPem, - padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - saltLength - }); - verified = crypto.createVerify(algo) - .update('Test') - .update('123') - .verify({ - key: certPem, - padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - saltLength - }, s4); - assert.strictEqual(verified, true, 'sign and verify (buffer, PSS)'); - - // Setting the salt length to RSA_PSS_SALTLEN_AUTO should always work for - // verification - verified = crypto.createVerify(algo) - .update('Test123') - .verify({ - key: certPem, - padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO - }, s4); - assert.strictEqual(verified, true, 'sign and verify (buffer, PSS)'); + function testPSS(algo, hLen) { + // Maximum permissible salt length + let max = modSize / 8 - hLen - 2; + + function getEffectiveSaltLength(saltLength) { + switch(saltLength) { + case crypto.constants.RSA_PSS_SALTLEN_DIGEST: + return hLen; + case crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN: + return max; + default: + return saltLength; + } + } + + let signSaltLengths = [ + crypto.constants.RSA_PSS_SALTLEN_DIGEST, + getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST), + crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, + getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN), + 0, 16, 32, 64, 128 + ]; + + let verifySaltLengths = [ + crypto.constants.RSA_PSS_SALTLEN_DIGEST, + getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST), + getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN), + 0, 16, 32, 64, 128 + ]; + + signSaltLengths.forEach((signSaltLength) => { + if(signSaltLength > max) { + // If the salt length is too big, an Error should be thrown + assert.throws(() => { + crypto.createSign(algo) + .update('Test123') + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: signSaltLength + }); + }, /^Error:.*data too large for key size$/); + } else { + // Otherwise, a valid signature should be generated + const s4 = crypto.createSign(algo) + .update('Test123') + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: signSaltLength + }); + + let verified; + verifySaltLengths.forEach((verifySaltLength) => { + // Verification should succeed if and only if the salt length is + // correct + verified = crypto.createVerify(algo) + .update('Test123') + .verify({ + key: certPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: verifySaltLength + }, s4); + let saltLengthCorrect = getEffectiveSaltLength(signSaltLength) == + getEffectiveSaltLength(verifySaltLength); + assert.strictEqual(verified, saltLengthCorrect, 'verify (PSS)'); + }); + + // Verification using RSA_PSS_SALTLEN_AUTO should always work + verified = crypto.createVerify(algo) + .update('Test123') + .verify({ + key: certPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO + }, s4); + assert.strictEqual(verified, true, 'verify (PSS with SALTLEN_AUTO)'); + + // Verifying an incorrect message should never work + verified = crypto.createVerify(algo) + .update('Test1234') + .verify({ + key: certPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_AUTO + }, s4); + assert.strictEqual(verified, false, 'verify (PSS, incorrect)'); + } }); - }); + } + + testPSS('RSA-SHA1', 20); + testPSS('RSA-SHA256', 32); } +// Test exceptions for invalid `padding` and `saltLength` values { + [null, undefined, NaN, "boom", {}, []].forEach((invalidValue) => { + assert.throws(() => { + crypto.createSign('RSA-SHA256') + .update('Test123') + .sign({ + key: keyPem, + padding: invalidValue + }); + }, /^TypeError: padding must be an integer$/); + + assert.throws(() => { + crypto.createSign('RSA-SHA256') + .update('Test123') + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: invalidValue + }); + }, /^TypeError: saltLength must be an integer$/); + }); + assert.throws(() => { crypto.createSign('RSA-SHA1') .update('Test123') From 1679e0c6875c01b363d7e496bb5e59cd268f2584 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Wed, 15 Mar 2017 18:09:14 +0100 Subject: [PATCH 13/17] Fix linting --- test/parallel/test-crypto-sign-verify.js | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 512f28dd61a491..a5489a26c4084c 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -76,10 +76,10 @@ const modSize = 1024; { function testPSS(algo, hLen) { // Maximum permissible salt length - let max = modSize / 8 - hLen - 2; + const max = modSize / 8 - hLen - 2; function getEffectiveSaltLength(saltLength) { - switch(saltLength) { + switch (saltLength) { case crypto.constants.RSA_PSS_SALTLEN_DIGEST: return hLen; case crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN: @@ -89,7 +89,7 @@ const modSize = 1024; } } - let signSaltLengths = [ + const signSaltLengths = [ crypto.constants.RSA_PSS_SALTLEN_DIGEST, getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST), crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN, @@ -97,7 +97,7 @@ const modSize = 1024; 0, 16, 32, 64, 128 ]; - let verifySaltLengths = [ + const verifySaltLengths = [ crypto.constants.RSA_PSS_SALTLEN_DIGEST, getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_DIGEST), getEffectiveSaltLength(crypto.constants.RSA_PSS_SALTLEN_MAX_SIGN), @@ -105,16 +105,16 @@ const modSize = 1024; ]; signSaltLengths.forEach((signSaltLength) => { - if(signSaltLength > max) { + if (signSaltLength > max) { // If the salt length is too big, an Error should be thrown assert.throws(() => { crypto.createSign(algo) - .update('Test123') - .sign({ - key: keyPem, - padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - saltLength: signSaltLength - }); + .update('Test123') + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: signSaltLength + }); }, /^Error:.*data too large for key size$/); } else { // Otherwise, a valid signature should be generated @@ -137,8 +137,8 @@ const modSize = 1024; padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: verifySaltLength }, s4); - let saltLengthCorrect = getEffectiveSaltLength(signSaltLength) == - getEffectiveSaltLength(verifySaltLength); + const saltLengthCorrect = getEffectiveSaltLength(signSaltLength) == + getEffectiveSaltLength(verifySaltLength); assert.strictEqual(verified, saltLengthCorrect, 'verify (PSS)'); }); @@ -171,7 +171,7 @@ const modSize = 1024; // Test exceptions for invalid `padding` and `saltLength` values { - [null, undefined, NaN, "boom", {}, []].forEach((invalidValue) => { + [null, undefined, NaN, 'boom', {}, []].forEach((invalidValue) => { assert.throws(() => { crypto.createSign('RSA-SHA256') .update('Test123') From 8b64c04eb59ca1ceaf26dc94d00a9963416ae4fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Thu, 16 Mar 2017 15:52:42 +0100 Subject: [PATCH 14/17] Minor improvements --- src/node_crypto.cc | 1 - test/parallel/test-crypto-sign-verify.js | 41 ++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index bb2b38c1ca8478..ea721164a0e23c 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -4411,7 +4411,6 @@ SignBase::Error Verify::VerifyFinal(const char* key_pem, siglen, m, m_len); - fatal = false; err: EVP_PKEY_CTX_free(pkctx); diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index a5489a26c4084c..671e8888960514 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -171,26 +171,27 @@ const modSize = 1024; // Test exceptions for invalid `padding` and `saltLength` values { - [null, undefined, NaN, 'boom', {}, []].forEach((invalidValue) => { - assert.throws(() => { - crypto.createSign('RSA-SHA256') - .update('Test123') - .sign({ - key: keyPem, - padding: invalidValue - }); - }, /^TypeError: padding must be an integer$/); - - assert.throws(() => { - crypto.createSign('RSA-SHA256') - .update('Test123') - .sign({ - key: keyPem, - padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - saltLength: invalidValue - }); - }, /^TypeError: saltLength must be an integer$/); - }); + [null, undefined, NaN, 'boom', {}, [], true, false] + .forEach((invalidValue) => { + assert.throws(() => { + crypto.createSign('RSA-SHA256') + .update('Test123') + .sign({ + key: keyPem, + padding: invalidValue + }); + }, /^TypeError: padding must be an integer$/); + + assert.throws(() => { + crypto.createSign('RSA-SHA256') + .update('Test123') + .sign({ + key: keyPem, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: invalidValue + }); + }, /^TypeError: saltLength must be an integer$/); + }); assert.throws(() => { crypto.createSign('RSA-SHA1') From 44b71e9f0c2a365008de8a430f86b6dc183866ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Fri, 17 Mar 2017 16:52:46 +0100 Subject: [PATCH 15/17] Note about MGF1, test vectors --- doc/api/crypto.md | 5 + test/parallel/test-crypto-sign-verify.js | 214 +++++++++++++++++++++++ 2 files changed, 219 insertions(+) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index 29b17f49a5c6eb..be6b66e9dc0a94 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -982,6 +982,8 @@ object, it must contain one or more of the following properties: * `padding`: {integer} - Optional padding value for RSA, one of the following: * `crypto.constants.RSA_PKCS1_PADDING` (default) * `crypto.constants.RSA_PKCS1_PSS_PADDING` + Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function + used to sign the message as specified in section 3.1 of [RFC 4055][]. * `saltLength`: {integer} - salt length for when padding is `RSA_PKCS1_PSS_PADDING`. The special value `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest @@ -1095,6 +1097,8 @@ or an object with one or more of the following properties: * `padding`: {integer} - Optional padding value for RSA, one of the following: * `crypto.constants.RSA_PKCS1_PADDING` (default) * `crypto.constants.RSA_PKCS1_PSS_PADDING` + Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function + used to verify the message as specified in section 3.1 of [RFC 4055][]. * `saltLength`: {integer} - salt length for when padding is `RSA_PKCS1_PSS_PADDING`. The special value `crypto.constants.RSA_PSS_SALTLEN_DIGEST` sets the salt length to the digest @@ -2153,6 +2157,7 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL. [publicly trusted list of CAs]: https://mxr.mozilla.org/mozilla/source/security/nss/lib/ckfw/builtins/certdata.txt [RFC 2412]: https://www.rfc-editor.org/rfc/rfc2412.txt [RFC 3526]: https://www.rfc-editor.org/rfc/rfc3526.txt +[RFC 4055]: https://www.rfc-editor.org/rfc/rfc4055.txt [stream]: stream.html [stream-writable-write]: stream.html#stream_writable_write_chunk_encoding_callback [Crypto Constants]: #crypto_crypto_constants_1 diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index 671e8888960514..fe1e35e074057d 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -169,6 +169,220 @@ const modSize = 1024; testPSS('RSA-SHA256', 32); } +// Test vectors for RSA_PKCS1_PSS_PADDING provided by the RSA Laboratories +{ + // We only test verification as we cannot specify explicit salts when signing + function testVerify(cert, vector) { + const verified = crypto.createVerify('RSA-SHA1') + .update(Buffer.from(vector.message, 'hex')) + .verify({ + key: cert, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: vector.salt.length / 2 + }, vector.signature, 'hex'); + assert.strictEqual(verified, true, 'verify (PSS)'); + } + + // Example 1: A 1024-bit RSA Key Pair + const ex01Cert = + '-----BEGIN PUBLIC KEY-----\n' + + 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLs\n' + + 'DjatUqRN/rHmH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2V\n' + + 'CAltWyuLbfXWce9jd8CSHLI8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh\n' + + '4fINDOjP+yJJvZohNwIDAQAB\n' + + '-----END PUBLIC KEY-----'; + [ + { + message: 'cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66' + + 'e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa6509' + + '5786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa8' + + '67693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e0' + + '8f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e3' + + '31170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9' + + 'ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73' + + '318b750a0167d0', + salt: 'dee959c7e06411361420ff80185ed57f3e6776af', + signature: '9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5' + + 'ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e' + + '8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f3' + + '82a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd5' + + '00f1ff2b994d8a4e30cbb33c' + }, + { + message: '851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b' + + '5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e', + salt: 'ef2869fa40c346cb183dab3d7bffc98fd56df42d', + signature: '3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c7299' + + '84f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d' + + '29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193b' + + 'e5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98' + + 'c22c4d4d47724fdb5669e843' + }, + { + message: 'a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd' + + '04ef8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be81' + + '91cf3955fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e' + + '81ca2cab31c745e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3' + + 'ce27fae1d6cb20f4564d66ce4767583d0e5f060215b59017be85ea848939' + + '127bd8c9c4d47b51056c031cf336f17c9980f3b8f5b9b6878e8b797aa43b' + + '882684333e17893fe9caa6aa299f7ed1a18ee2c54864b7b2b99b72618fb0' + + '2574d139ef50f019c9eef416971338e7d470', + salt: '710b9c4747d800d4de87f12afdce6df18107cc77', + signature: '666026fba71bd3e7cf13157cc2c51a8e4aa684af9778f91849f34335d1' + + '41c00154c4197621f9624a675b5abc22ee7d5baaffaae1c9baca2cc373' + + 'b3f33e78e6143c395a91aa7faca664eb733afd14d8827259d99a7550fa' + + 'ca501ef2b04e33c23aa51f4b9e8282efdb728cc0ab09405a91607c6369' + + '961bc8270d2d4f39fce612b1' + }, + { + message: 'bc656747fa9eafb3f0', + salt: '056f00985de14d8ef5cea9e82f8c27bef720335e', + signature: '4609793b23e9d09362dc21bb47da0b4f3a7622649a47d464019b9aeafe' + + '53359c178c91cd58ba6bcb78be0346a7bc637f4b873d4bab38ee661f19' + + '9634c547a1ad8442e03da015b136e543f7ab07c0c13e4225b8de8cce25' + + 'd4f6eb8400f81f7e1833b7ee6e334d370964ca79fdb872b4d75223b5ee' + + 'b08101591fb532d155a6de87' + }, + { + message: 'b45581547e5427770c768e8b82b75564e0ea4e9c32594d6bff706544de0a' + + '8776c7a80b4576550eee1b2acabc7e8b7d3ef7bb5b03e462c11047eadd00' + + '629ae575480ac1470fe046f13a2bf5af17921dc4b0aa8b02bee633491165' + + '1d7f8525d10f32b51d33be520d3ddf5a709955a3dfe78283b9e0ab54046d' + + '150c177f037fdccc5be4ea5f68b5e5a38c9d7edcccc4975f455a6909b4', + salt: '80e70ff86a08de3ec60972b39b4fbfdcea67ae8e', + signature: '1d2aad221ca4d31ddf13509239019398e3d14b32dc34dc5af4aeaea3c0' + + '95af73479cf0a45e5629635a53a018377615b16cb9b13b3e09d671eb71' + + 'e387b8545c5960da5a64776e768e82b2c93583bf104c3fdb23512b7b4e' + + '89f633dd0063a530db4524b01c3f384c09310e315a79dcd3d684022a7f' + + '31c865a664e316978b759fad' + }, + { + message: '10aae9a0ab0b595d0841207b700d48d75faedde3b775cd6b4cc88ae06e46' + + '94ec74ba18f8520d4f5ea69cbbe7cc2beba43efdc10215ac4eb32dc302a1' + + 'f53dc6c4352267e7936cfebf7c8d67035784a3909fa859c7b7b59b8e39c5' + + 'c2349f1886b705a30267d402f7486ab4f58cad5d69adb17ab8cd0ce1caf5' + + '025af4ae24b1fb8794c6070cc09a51e2f9911311e3877d0044c71c57a993' + + '395008806b723ac38373d395481818528c1e7053739282053529510e935c' + + 'd0fa77b8fa53cc2d474bd4fb3cc5c672d6ffdc90a00f9848712c4bcfe46c' + + '60573659b11e6457e861f0f604b6138d144f8ce4e2da73', + salt: 'a8ab69dd801f0074c2a1fc60649836c616d99681', + signature: '2a34f6125e1f6b0bf971e84fbd41c632be8f2c2ace7de8b6926e31ff93' + + 'e9af987fbc06e51e9be14f5198f91f3f953bd67da60a9df59764c3dc0f' + + 'e08e1cbef0b75f868d10ad3fba749fef59fb6dac46a0d6e50436933158' + + '6f58e4628f39aa278982543bc0eeb537dc61958019b394fb273f215858' + + 'a0a01ac4d650b955c67f4c58' + } + ].forEach((vector) => testVerify(ex01Cert, vector)); + + // Example 10: A 2048-bit RSA Key Pair + const ex10Cert = + '-----BEGIN PUBLIC KEY-----\n' + + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd2GesTLAvkLlFfUjBSn\n' + + 'cO+ZHFbDnA7GX9Ea+ok3zqV7m+esc7RcABdhW4LWIuMYdTtgJ8D9FXvhL4CQ/uKn\n' + + 'rc0O73WfiLpJl8ekLVjJqhLLma4AH+UhwTu1QxRFqNWuT15MfpSKwifTYEBx8g5X\n' + + 'fpBfvrFd+vBtHeWuYlPWOmohILMaXaXavJVQYA4g8n03OeJieSX+o8xQnyHf8E5u\n' + + '6kVJxUDWgJ/5MH7t6R//WHM9g4WiN9bTcFoz45GQCZIHDfet8TV89+NwDONmfeg/\n' + + 'F7jfF3jbOB3OCctK0FilEQAac4GY7ifPVaE7dUU5kGWC7IsXS9WNXR89dnxhNyGu\n' + + 'BQIDAQAB\n' + + '-----END PUBLIC KEY-----'; + [ + { + message: '883177e5126b9be2d9a9680327d5370c6f26861f5820c43da67a3ad609', + salt: '04e215ee6ff934b9da70d7730c8734abfcecde89', + signature: '82c2b160093b8aa3c0f7522b19f87354066c77847abf2a9fce542d0e84' + + 'e920c5afb49ffdfdace16560ee94a1369601148ebad7a0e151cf163317' + + '91a5727d05f21e74e7eb811440206935d744765a15e79f015cb66c532c' + + '87a6a05961c8bfad741a9a6657022894393e7223739796c02a77455d0f' + + '555b0ec01ddf259b6207fd0fd57614cef1a5573baaff4ec00069951659' + + 'b85f24300a25160ca8522dc6e6727e57d019d7e63629b8fe5e89e25cc1' + + '5beb3a647577559299280b9b28f79b0409000be25bbd96408ba3b43cc4' + + '86184dd1c8e62553fa1af4040f60663de7f5e49c04388e257f1ce89c95' + + 'dab48a315d9b66b1b7628233876ff2385230d070d07e1666' + }, + { + message: 'dd670a01465868adc93f26131957a50c52fb777cdbaa30892c9e12361164' + + 'ec13979d43048118e4445db87bee58dd987b3425d02071d8dbae80708b03' + + '9dbb64dbd1de5657d9fed0c118a54143742e0ff3c87f74e45857647af3f7' + + '9eb0a14c9d75ea9a1a04b7cf478a897a708fd988f48e801edb0b7039df8c' + + '23bb3c56f4e821ac', + salt: '8b2bdd4b40faf545c778ddf9bc1a49cb57f9b71b', + signature: '14ae35d9dd06ba92f7f3b897978aed7cd4bf5ff0b585a40bd46ce1b42c' + + 'd2703053bb9044d64e813d8f96db2dd7007d10118f6f8f8496097ad75e' + + '1ff692341b2892ad55a633a1c55e7f0a0ad59a0e203a5b8278aec54dd8' + + '622e2831d87174f8caff43ee6c46445345d84a59659bfb92ecd4c81866' + + '8695f34706f66828a89959637f2bf3e3251c24bdba4d4b7649da002221' + + '8b119c84e79a6527ec5b8a5f861c159952e23ec05e1e717346faefe8b1' + + '686825bd2b262fb2531066c0de09acde2e4231690728b5d85e115a2f6b' + + '92b79c25abc9bd9399ff8bcf825a52ea1f56ea76dd26f43baafa18bfa9' + + '2a504cbd35699e26d1dcc5a2887385f3c63232f06f3244c3' + }, + { + message: '48b2b6a57a63c84cea859d65c668284b08d96bdcaabe252db0e4a96cb1ba' + + 'c6019341db6fbefb8d106b0e90eda6bcc6c6262f37e7ea9c7e5d226bd7df' + + '85ec5e71efff2f54c5db577ff729ff91b842491de2741d0c631607df586b' + + '905b23b91af13da12304bf83eca8a73e871ff9db', + salt: '4e96fc1b398f92b44671010c0dc3efd6e20c2d73', + signature: '6e3e4d7b6b15d2fb46013b8900aa5bbb3939cf2c095717987042026ee6' + + '2c74c54cffd5d7d57efbbf950a0f5c574fa09d3fc1c9f513b05b4ff50d' + + 'd8df7edfa20102854c35e592180119a70ce5b085182aa02d9ea2aa90d1' + + 'df03f2daae885ba2f5d05afdac97476f06b93b5bc94a1a80aa9116c4d6' + + '15f333b098892b25fface266f5db5a5a3bcc10a824ed55aad35b727834' + + 'fb8c07da28fcf416a5d9b2224f1f8b442b36f91e456fdea2d7cfe33672' + + '68de0307a4c74e924159ed33393d5e0655531c77327b89821bdedf8801' + + '61c78cd4196b5419f7acc3f13e5ebf161b6e7c6724716ca33b85c2e256' + + '40192ac2859651d50bde7eb976e51cec828b98b6563b86bb' + }, + { + message: '0b8777c7f839baf0a64bbbdbc5ce79755c57a205b845c174e2d2e90546a0' + + '89c4e6ec8adffa23a7ea97bae6b65d782b82db5d2b5a56d22a29a05e7c44' + + '33e2b82a621abba90add05ce393fc48a840542451a', + salt: 'c7cd698d84b65128d8835e3a8b1eb0e01cb541ec', + signature: '34047ff96c4dc0dc90b2d4ff59a1a361a4754b255d2ee0af7d8bf87c9b' + + 'c9e7ddeede33934c63ca1c0e3d262cb145ef932a1f2c0a997aa6a34f8e' + + 'aee7477d82ccf09095a6b8acad38d4eec9fb7eab7ad02da1d11d8e54c1' + + '825e55bf58c2a23234b902be124f9e9038a8f68fa45dab72f66e0945bf' + + '1d8bacc9044c6f07098c9fcec58a3aab100c805178155f030a124c450e' + + '5acbda47d0e4f10b80a23f803e774d023b0015c20b9f9bbe7c91296338' + + 'd5ecb471cafb032007b67a60be5f69504a9f01abb3cb467b260e2bce86' + + '0be8d95bf92c0c8e1496ed1e528593a4abb6df462dde8a0968dffe4683' + + '116857a232f5ebf6c85be238745ad0f38f767a5fdbf486fb' + }, + { + message: 'f1036e008e71e964dadc9219ed30e17f06b4b68a955c16b312b1eddf028b' + + '74976bed6b3f6a63d4e77859243c9cccdc98016523abb02483b35591c33a' + + 'ad81213bb7c7bb1a470aabc10d44256c4d4559d916', + salt: 'efa8bff96212b2f4a3f371a10d574152655f5dfb', + signature: '7e0935ea18f4d6c1d17ce82eb2b3836c55b384589ce19dfe743363ac99' + + '48d1f346b7bfddfe92efd78adb21faefc89ade42b10f374003fe122e67' + + '429a1cb8cbd1f8d9014564c44d120116f4990f1a6e38774c194bd1b821' + + '3286b077b0499d2e7b3f434ab12289c556684deed78131934bb3dd6537' + + '236f7c6f3dcb09d476be07721e37e1ceed9b2f7b406887bd53157305e1' + + 'c8b4f84d733bc1e186fe06cc59b6edb8f4bd7ffefdf4f7ba9cfb9d5706' + + '89b5a1a4109a746a690893db3799255a0cb9215d2d1cd490590e952e8c' + + '8786aa0011265252470c041dfbc3eec7c3cbf71c24869d115c0cb4a956' + + 'f56d530b80ab589acfefc690751ddf36e8d383f83cedd2cc' + }, + { + message: '25f10895a87716c137450bb9519dfaa1f207faa942ea88abf71e9c179800' + + '85b555aebab76264ae2a3ab93c2d12981191ddac6fb5949eb36aee3c5da9' + + '40f00752c916d94608fa7d97ba6a2915b688f20323d4e9d96801d89a72ab' + + '5892dc2117c07434fcf972e058cf8c41ca4b4ff554f7d5068ad3155fced0' + + 'f3125bc04f9193378a8f5c4c3b8cb4dd6d1cc69d30ecca6eaa51e36a0573' + + '0e9e342e855baf099defb8afd7', + salt: 'ad8b1523703646224b660b550885917ca2d1df28', + signature: '6d3b5b87f67ea657af21f75441977d2180f91b2c5f692de82955696a68' + + '6730d9b9778d970758ccb26071c2209ffbd6125be2e96ea81b67cb9b93' + + '08239fda17f7b2b64ecda096b6b935640a5a1cb42a9155b1c9ef7a633a' + + '02c59f0d6ee59b852c43b35029e73c940ff0410e8f114eed46bbd0fae1' + + '65e42be2528a401c3b28fd818ef3232dca9f4d2a0f5166ec59c42396d6' + + 'c11dbc1215a56fa17169db9575343ef34f9de32a49cdc3174922f229c2' + + '3e18e45df9353119ec4319cedce7a17c64088c1f6f52be29634100b391' + + '9d38f3d1ed94e6891e66a73b8fb849f5874df59459e298c7bbce2eee78' + + '2a195aa66fe2d0732b25e595f57d3e061b1fc3e4063bf98f' + } + ].forEach((vector) => testVerify(ex10Cert, vector)); +} + // Test exceptions for invalid `padding` and `saltLength` values { [null, undefined, NaN, 'boom', {}, [], true, false] From 4f695aa1281908685aa4b0e427f81509aaefa0a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Tue, 28 Mar 2017 11:34:56 +0200 Subject: [PATCH 16/17] Add RSASSA-PSS sign test --- doc/api/crypto.md | 4 +- test/fixtures/pss-vectors.json | 89 ++++++++ test/parallel/test-crypto-sign-verify.js | 249 +++++------------------ 3 files changed, 142 insertions(+), 200 deletions(-) create mode 100644 test/fixtures/pss-vectors.json diff --git a/doc/api/crypto.md b/doc/api/crypto.md index be6b66e9dc0a94..ad55955fe0a51f 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -982,6 +982,7 @@ object, it must contain one or more of the following properties: * `padding`: {integer} - Optional padding value for RSA, one of the following: * `crypto.constants.RSA_PKCS1_PADDING` (default) * `crypto.constants.RSA_PKCS1_PSS_PADDING` + Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function used to sign the message as specified in section 3.1 of [RFC 4055][]. * `saltLength`: {integer} - salt length for when padding is @@ -1084,7 +1085,7 @@ This can be called many times with new data as it is streamed. -- `object` {string} +- `object` {string | Object} - `signature` {string | Buffer | Uint8Array} - `signature_format` {string} @@ -1097,6 +1098,7 @@ or an object with one or more of the following properties: * `padding`: {integer} - Optional padding value for RSA, one of the following: * `crypto.constants.RSA_PKCS1_PADDING` (default) * `crypto.constants.RSA_PKCS1_PSS_PADDING` + Note that `RSA_PKCS1_PSS_PADDING` will use MGF1 with the same hash function used to verify the message as specified in section 3.1 of [RFC 4055][]. * `saltLength`: {integer} - salt length for when padding is diff --git a/test/fixtures/pss-vectors.json b/test/fixtures/pss-vectors.json new file mode 100644 index 00000000000000..b540d13a540646 --- /dev/null +++ b/test/fixtures/pss-vectors.json @@ -0,0 +1,89 @@ +{ + "example01": { + "publicKey": [ + "-----BEGIN PUBLIC KEY-----", + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLs", + "DjatUqRN/rHmH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2V", + "CAltWyuLbfXWce9jd8CSHLI8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh", + "4fINDOjP+yJJvZohNwIDAQAB", + "-----END PUBLIC KEY-----" + ], + "tests": [ + { + "message": "cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73318b750a0167d0", + "salt": "dee959c7e06411361420ff80185ed57f3e6776af", + "signature": "9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c" + }, + { + "message": "851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e", + "salt": "ef2869fa40c346cb183dab3d7bffc98fd56df42d", + "signature": "3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c729984f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193be5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98c22c4d4d47724fdb5669e843" + }, + { + "message": "a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd04ef8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be8191cf3955fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e81ca2cab31c745e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3ce27fae1d6cb20f4564d66ce4767583d0e5f060215b59017be85ea848939127bd8c9c4d47b51056c031cf336f17c9980f3b8f5b9b6878e8b797aa43b882684333e17893fe9caa6aa299f7ed1a18ee2c54864b7b2b99b72618fb02574d139ef50f019c9eef416971338e7d470", + "salt": "710b9c4747d800d4de87f12afdce6df18107cc77", + "signature": "666026fba71bd3e7cf13157cc2c51a8e4aa684af9778f91849f34335d141c00154c4197621f9624a675b5abc22ee7d5baaffaae1c9baca2cc373b3f33e78e6143c395a91aa7faca664eb733afd14d8827259d99a7550faca501ef2b04e33c23aa51f4b9e8282efdb728cc0ab09405a91607c6369961bc8270d2d4f39fce612b1" + }, + { + "message": "bc656747fa9eafb3f0", + "salt": "056f00985de14d8ef5cea9e82f8c27bef720335e", + "signature": "4609793b23e9d09362dc21bb47da0b4f3a7622649a47d464019b9aeafe53359c178c91cd58ba6bcb78be0346a7bc637f4b873d4bab38ee661f199634c547a1ad8442e03da015b136e543f7ab07c0c13e4225b8de8cce25d4f6eb8400f81f7e1833b7ee6e334d370964ca79fdb872b4d75223b5eeb08101591fb532d155a6de87" + }, + { + "message": "b45581547e5427770c768e8b82b75564e0ea4e9c32594d6bff706544de0a8776c7a80b4576550eee1b2acabc7e8b7d3ef7bb5b03e462c11047eadd00629ae575480ac1470fe046f13a2bf5af17921dc4b0aa8b02bee6334911651d7f8525d10f32b51d33be520d3ddf5a709955a3dfe78283b9e0ab54046d150c177f037fdccc5be4ea5f68b5e5a38c9d7edcccc4975f455a6909b4", + "salt": "80e70ff86a08de3ec60972b39b4fbfdcea67ae8e", + "signature": "1d2aad221ca4d31ddf13509239019398e3d14b32dc34dc5af4aeaea3c095af73479cf0a45e5629635a53a018377615b16cb9b13b3e09d671eb71e387b8545c5960da5a64776e768e82b2c93583bf104c3fdb23512b7b4e89f633dd0063a530db4524b01c3f384c09310e315a79dcd3d684022a7f31c865a664e316978b759fad" + }, + { + "message": "10aae9a0ab0b595d0841207b700d48d75faedde3b775cd6b4cc88ae06e4694ec74ba18f8520d4f5ea69cbbe7cc2beba43efdc10215ac4eb32dc302a1f53dc6c4352267e7936cfebf7c8d67035784a3909fa859c7b7b59b8e39c5c2349f1886b705a30267d402f7486ab4f58cad5d69adb17ab8cd0ce1caf5025af4ae24b1fb8794c6070cc09a51e2f9911311e3877d0044c71c57a993395008806b723ac38373d395481818528c1e7053739282053529510e935cd0fa77b8fa53cc2d474bd4fb3cc5c672d6ffdc90a00f9848712c4bcfe46c60573659b11e6457e861f0f604b6138d144f8ce4e2da73", + "salt": "a8ab69dd801f0074c2a1fc60649836c616d99681", + "signature": "2a34f6125e1f6b0bf971e84fbd41c632be8f2c2ace7de8b6926e31ff93e9af987fbc06e51e9be14f5198f91f3f953bd67da60a9df59764c3dc0fe08e1cbef0b75f868d10ad3fba749fef59fb6dac46a0d6e504369331586f58e4628f39aa278982543bc0eeb537dc61958019b394fb273f215858a0a01ac4d650b955c67f4c58" + } + ] + }, + "example10": { + "publicKey": [ + "-----BEGIN PUBLIC KEY-----", + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd2GesTLAvkLlFfUjBSn", + "cO+ZHFbDnA7GX9Ea+ok3zqV7m+esc7RcABdhW4LWIuMYdTtgJ8D9FXvhL4CQ/uKn", + "rc0O73WfiLpJl8ekLVjJqhLLma4AH+UhwTu1QxRFqNWuT15MfpSKwifTYEBx8g5X", + "fpBfvrFd+vBtHeWuYlPWOmohILMaXaXavJVQYA4g8n03OeJieSX+o8xQnyHf8E5u", + "6kVJxUDWgJ/5MH7t6R//WHM9g4WiN9bTcFoz45GQCZIHDfet8TV89+NwDONmfeg/", + "F7jfF3jbOB3OCctK0FilEQAac4GY7ifPVaE7dUU5kGWC7IsXS9WNXR89dnxhNyGu", + "BQIDAQAB", + "-----END PUBLIC KEY-----" + ], + "tests": [ + { + "message": "883177e5126b9be2d9a9680327d5370c6f26861f5820c43da67a3ad609", + "salt": "04e215ee6ff934b9da70d7730c8734abfcecde89", + "signature": "82c2b160093b8aa3c0f7522b19f87354066c77847abf2a9fce542d0e84e920c5afb49ffdfdace16560ee94a1369601148ebad7a0e151cf16331791a5727d05f21e74e7eb811440206935d744765a15e79f015cb66c532c87a6a05961c8bfad741a9a6657022894393e7223739796c02a77455d0f555b0ec01ddf259b6207fd0fd57614cef1a5573baaff4ec00069951659b85f24300a25160ca8522dc6e6727e57d019d7e63629b8fe5e89e25cc15beb3a647577559299280b9b28f79b0409000be25bbd96408ba3b43cc486184dd1c8e62553fa1af4040f60663de7f5e49c04388e257f1ce89c95dab48a315d9b66b1b7628233876ff2385230d070d07e1666" + }, + { + "message": "dd670a01465868adc93f26131957a50c52fb777cdbaa30892c9e12361164ec13979d43048118e4445db87bee58dd987b3425d02071d8dbae80708b039dbb64dbd1de5657d9fed0c118a54143742e0ff3c87f74e45857647af3f79eb0a14c9d75ea9a1a04b7cf478a897a708fd988f48e801edb0b7039df8c23bb3c56f4e821ac", + "salt": "8b2bdd4b40faf545c778ddf9bc1a49cb57f9b71b", + "signature": "14ae35d9dd06ba92f7f3b897978aed7cd4bf5ff0b585a40bd46ce1b42cd2703053bb9044d64e813d8f96db2dd7007d10118f6f8f8496097ad75e1ff692341b2892ad55a633a1c55e7f0a0ad59a0e203a5b8278aec54dd8622e2831d87174f8caff43ee6c46445345d84a59659bfb92ecd4c818668695f34706f66828a89959637f2bf3e3251c24bdba4d4b7649da0022218b119c84e79a6527ec5b8a5f861c159952e23ec05e1e717346faefe8b1686825bd2b262fb2531066c0de09acde2e4231690728b5d85e115a2f6b92b79c25abc9bd9399ff8bcf825a52ea1f56ea76dd26f43baafa18bfa92a504cbd35699e26d1dcc5a2887385f3c63232f06f3244c3" + }, + { + "message": "48b2b6a57a63c84cea859d65c668284b08d96bdcaabe252db0e4a96cb1bac6019341db6fbefb8d106b0e90eda6bcc6c6262f37e7ea9c7e5d226bd7df85ec5e71efff2f54c5db577ff729ff91b842491de2741d0c631607df586b905b23b91af13da12304bf83eca8a73e871ff9db", + "salt": "4e96fc1b398f92b44671010c0dc3efd6e20c2d73", + "signature": "6e3e4d7b6b15d2fb46013b8900aa5bbb3939cf2c095717987042026ee62c74c54cffd5d7d57efbbf950a0f5c574fa09d3fc1c9f513b05b4ff50dd8df7edfa20102854c35e592180119a70ce5b085182aa02d9ea2aa90d1df03f2daae885ba2f5d05afdac97476f06b93b5bc94a1a80aa9116c4d615f333b098892b25fface266f5db5a5a3bcc10a824ed55aad35b727834fb8c07da28fcf416a5d9b2224f1f8b442b36f91e456fdea2d7cfe3367268de0307a4c74e924159ed33393d5e0655531c77327b89821bdedf880161c78cd4196b5419f7acc3f13e5ebf161b6e7c6724716ca33b85c2e25640192ac2859651d50bde7eb976e51cec828b98b6563b86bb" + }, + { + "message": "0b8777c7f839baf0a64bbbdbc5ce79755c57a205b845c174e2d2e90546a089c4e6ec8adffa23a7ea97bae6b65d782b82db5d2b5a56d22a29a05e7c4433e2b82a621abba90add05ce393fc48a840542451a", + "salt": "c7cd698d84b65128d8835e3a8b1eb0e01cb541ec", + "signature": "34047ff96c4dc0dc90b2d4ff59a1a361a4754b255d2ee0af7d8bf87c9bc9e7ddeede33934c63ca1c0e3d262cb145ef932a1f2c0a997aa6a34f8eaee7477d82ccf09095a6b8acad38d4eec9fb7eab7ad02da1d11d8e54c1825e55bf58c2a23234b902be124f9e9038a8f68fa45dab72f66e0945bf1d8bacc9044c6f07098c9fcec58a3aab100c805178155f030a124c450e5acbda47d0e4f10b80a23f803e774d023b0015c20b9f9bbe7c91296338d5ecb471cafb032007b67a60be5f69504a9f01abb3cb467b260e2bce860be8d95bf92c0c8e1496ed1e528593a4abb6df462dde8a0968dffe4683116857a232f5ebf6c85be238745ad0f38f767a5fdbf486fb" + }, + { + "message": "f1036e008e71e964dadc9219ed30e17f06b4b68a955c16b312b1eddf028b74976bed6b3f6a63d4e77859243c9cccdc98016523abb02483b35591c33aad81213bb7c7bb1a470aabc10d44256c4d4559d916", + "salt": "efa8bff96212b2f4a3f371a10d574152655f5dfb", + "signature": "7e0935ea18f4d6c1d17ce82eb2b3836c55b384589ce19dfe743363ac9948d1f346b7bfddfe92efd78adb21faefc89ade42b10f374003fe122e67429a1cb8cbd1f8d9014564c44d120116f4990f1a6e38774c194bd1b8213286b077b0499d2e7b3f434ab12289c556684deed78131934bb3dd6537236f7c6f3dcb09d476be07721e37e1ceed9b2f7b406887bd53157305e1c8b4f84d733bc1e186fe06cc59b6edb8f4bd7ffefdf4f7ba9cfb9d570689b5a1a4109a746a690893db3799255a0cb9215d2d1cd490590e952e8c8786aa0011265252470c041dfbc3eec7c3cbf71c24869d115c0cb4a956f56d530b80ab589acfefc690751ddf36e8d383f83cedd2cc" + }, + { + "message": "25f10895a87716c137450bb9519dfaa1f207faa942ea88abf71e9c17980085b555aebab76264ae2a3ab93c2d12981191ddac6fb5949eb36aee3c5da940f00752c916d94608fa7d97ba6a2915b688f20323d4e9d96801d89a72ab5892dc2117c07434fcf972e058cf8c41ca4b4ff554f7d5068ad3155fced0f3125bc04f9193378a8f5c4c3b8cb4dd6d1cc69d30ecca6eaa51e36a05730e9e342e855baf099defb8afd7", + "salt": "ad8b1523703646224b660b550885917ca2d1df28", + "signature": "6d3b5b87f67ea657af21f75441977d2180f91b2c5f692de82955696a686730d9b9778d970758ccb26071c2209ffbd6125be2e96ea81b67cb9b9308239fda17f7b2b64ecda096b6b935640a5a1cb42a9155b1c9ef7a633a02c59f0d6ee59b852c43b35029e73c940ff0410e8f114eed46bbd0fae165e42be2528a401c3b28fd818ef3232dca9f4d2a0f5166ec59c42396d6c11dbc1215a56fa17169db9575343ef34f9de32a49cdc3174922f229c23e18e45df9353119ec4319cedce7a17c64088c1f6f52be29634100b3919d38f3d1ed94e6891e66a73b8fb849f5874df59459e298c7bbce2eee782a195aa66fe2d0732b25e595f57d3e061b1fc3e4063bf98f" + } + ] + } +} \ No newline at end of file diff --git a/test/parallel/test-crypto-sign-verify.js b/test/parallel/test-crypto-sign-verify.js index fe1e35e074057d..241f2b6ee914d4 100644 --- a/test/parallel/test-crypto-sign-verify.js +++ b/test/parallel/test-crypto-sign-verify.js @@ -2,6 +2,8 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); +const path = require('path'); +const exec = require('child_process').exec; if (!common.hasCrypto) { common.skip('missing crypto'); @@ -169,7 +171,8 @@ const modSize = 1024; testPSS('RSA-SHA256', 32); } -// Test vectors for RSA_PKCS1_PSS_PADDING provided by the RSA Laboratories +// Test vectors for RSA_PKCS1_PSS_PADDING provided by the RSA Laboratories: +// https://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-rsa-cryptography-standard.htm { // We only test verification as we cannot specify explicit salts when signing function testVerify(cert, vector) { @@ -183,204 +186,16 @@ const modSize = 1024; assert.strictEqual(verified, true, 'verify (PSS)'); } - // Example 1: A 1024-bit RSA Key Pair - const ex01Cert = - '-----BEGIN PUBLIC KEY-----\n' + - 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQClbkoOcBAXWJpRh9x+qEHRVvLs\n' + - 'DjatUqRN/rHmH3rZkdjFEFb/7bFitMDyg6EqiKOU3/Umq3KRy7MHzqv84LHf1c2V\n' + - 'CAltWyuLbfXWce9jd8CSHLI8Jwpw4lmOb/idGfEFrMLT8Ms18pKA4Thrb2TE7yLh\n' + - '4fINDOjP+yJJvZohNwIDAQAB\n' + - '-----END PUBLIC KEY-----'; - [ - { - message: 'cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66' + - 'e1b62371d45ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa6509' + - '5786cb769757a6563ba958fed0bcc984e8b517a3d5f515b23b8a41e74aa8' + - '67693f90dfb061a6e86dfaaee64472c00e5f20945729cbebe77f06ce78e0' + - '8f4098fba41f9d6193c0317e8b60d4b6084acb42d29e3808a3bc372d85e3' + - '31170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625cab2744fd9' + - 'ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73' + - '318b750a0167d0', - salt: 'dee959c7e06411361420ff80185ed57f3e6776af', - signature: '9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5' + - 'ed2887e57ce7fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e' + - '8d31f5051215df6ebb9c2f9579aa77598a38f914b5b9c1bd83c4e2f9f3' + - '82a0d0aa3542ffee65984a601bc69eb28deb27dca12c82c2d4c3f66cd5' + - '00f1ff2b994d8a4e30cbb33c' - }, - { - message: '851384cdfe819c22ed6c4ccb30daeb5cf059bc8e1166b7e3530c4c233e2b' + - '5f8f71a1cca582d43ecc72b1bca16dfc7013226b9e', - salt: 'ef2869fa40c346cb183dab3d7bffc98fd56df42d', - signature: '3ef7f46e831bf92b32274142a585ffcefbdca7b32ae90d10fb0f0c7299' + - '84f04ef29a9df0780775ce43739b97838390db0a5505e63de927028d9d' + - '29b219ca2c4517832558a55d694a6d25b9dab66003c4cccd907802193b' + - 'e5170d26147d37b93590241be51c25055f47ef62752cfbe21418fafe98' + - 'c22c4d4d47724fdb5669e843' - }, - { - message: 'a4b159941761c40c6a82f2b80d1b94f5aa2654fd17e12d588864679b54cd' + - '04ef8bd03012be8dc37f4b83af7963faff0dfa225477437c48017ff2be81' + - '91cf3955fc07356eab3f322f7f620e21d254e5db4324279fe067e0910e2e' + - '81ca2cab31c745e67a54058eb50d993cdb9ed0b4d029c06d21a94ca661c3' + - 'ce27fae1d6cb20f4564d66ce4767583d0e5f060215b59017be85ea848939' + - '127bd8c9c4d47b51056c031cf336f17c9980f3b8f5b9b6878e8b797aa43b' + - '882684333e17893fe9caa6aa299f7ed1a18ee2c54864b7b2b99b72618fb0' + - '2574d139ef50f019c9eef416971338e7d470', - salt: '710b9c4747d800d4de87f12afdce6df18107cc77', - signature: '666026fba71bd3e7cf13157cc2c51a8e4aa684af9778f91849f34335d1' + - '41c00154c4197621f9624a675b5abc22ee7d5baaffaae1c9baca2cc373' + - 'b3f33e78e6143c395a91aa7faca664eb733afd14d8827259d99a7550fa' + - 'ca501ef2b04e33c23aa51f4b9e8282efdb728cc0ab09405a91607c6369' + - '961bc8270d2d4f39fce612b1' - }, - { - message: 'bc656747fa9eafb3f0', - salt: '056f00985de14d8ef5cea9e82f8c27bef720335e', - signature: '4609793b23e9d09362dc21bb47da0b4f3a7622649a47d464019b9aeafe' + - '53359c178c91cd58ba6bcb78be0346a7bc637f4b873d4bab38ee661f19' + - '9634c547a1ad8442e03da015b136e543f7ab07c0c13e4225b8de8cce25' + - 'd4f6eb8400f81f7e1833b7ee6e334d370964ca79fdb872b4d75223b5ee' + - 'b08101591fb532d155a6de87' - }, - { - message: 'b45581547e5427770c768e8b82b75564e0ea4e9c32594d6bff706544de0a' + - '8776c7a80b4576550eee1b2acabc7e8b7d3ef7bb5b03e462c11047eadd00' + - '629ae575480ac1470fe046f13a2bf5af17921dc4b0aa8b02bee633491165' + - '1d7f8525d10f32b51d33be520d3ddf5a709955a3dfe78283b9e0ab54046d' + - '150c177f037fdccc5be4ea5f68b5e5a38c9d7edcccc4975f455a6909b4', - salt: '80e70ff86a08de3ec60972b39b4fbfdcea67ae8e', - signature: '1d2aad221ca4d31ddf13509239019398e3d14b32dc34dc5af4aeaea3c0' + - '95af73479cf0a45e5629635a53a018377615b16cb9b13b3e09d671eb71' + - 'e387b8545c5960da5a64776e768e82b2c93583bf104c3fdb23512b7b4e' + - '89f633dd0063a530db4524b01c3f384c09310e315a79dcd3d684022a7f' + - '31c865a664e316978b759fad' - }, - { - message: '10aae9a0ab0b595d0841207b700d48d75faedde3b775cd6b4cc88ae06e46' + - '94ec74ba18f8520d4f5ea69cbbe7cc2beba43efdc10215ac4eb32dc302a1' + - 'f53dc6c4352267e7936cfebf7c8d67035784a3909fa859c7b7b59b8e39c5' + - 'c2349f1886b705a30267d402f7486ab4f58cad5d69adb17ab8cd0ce1caf5' + - '025af4ae24b1fb8794c6070cc09a51e2f9911311e3877d0044c71c57a993' + - '395008806b723ac38373d395481818528c1e7053739282053529510e935c' + - 'd0fa77b8fa53cc2d474bd4fb3cc5c672d6ffdc90a00f9848712c4bcfe46c' + - '60573659b11e6457e861f0f604b6138d144f8ce4e2da73', - salt: 'a8ab69dd801f0074c2a1fc60649836c616d99681', - signature: '2a34f6125e1f6b0bf971e84fbd41c632be8f2c2ace7de8b6926e31ff93' + - 'e9af987fbc06e51e9be14f5198f91f3f953bd67da60a9df59764c3dc0f' + - 'e08e1cbef0b75f868d10ad3fba749fef59fb6dac46a0d6e50436933158' + - '6f58e4628f39aa278982543bc0eeb537dc61958019b394fb273f215858' + - 'a0a01ac4d650b955c67f4c58' - } - ].forEach((vector) => testVerify(ex01Cert, vector)); - - // Example 10: A 2048-bit RSA Key Pair - const ex10Cert = - '-----BEGIN PUBLIC KEY-----\n' + - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd2GesTLAvkLlFfUjBSn\n' + - 'cO+ZHFbDnA7GX9Ea+ok3zqV7m+esc7RcABdhW4LWIuMYdTtgJ8D9FXvhL4CQ/uKn\n' + - 'rc0O73WfiLpJl8ekLVjJqhLLma4AH+UhwTu1QxRFqNWuT15MfpSKwifTYEBx8g5X\n' + - 'fpBfvrFd+vBtHeWuYlPWOmohILMaXaXavJVQYA4g8n03OeJieSX+o8xQnyHf8E5u\n' + - '6kVJxUDWgJ/5MH7t6R//WHM9g4WiN9bTcFoz45GQCZIHDfet8TV89+NwDONmfeg/\n' + - 'F7jfF3jbOB3OCctK0FilEQAac4GY7ifPVaE7dUU5kGWC7IsXS9WNXR89dnxhNyGu\n' + - 'BQIDAQAB\n' + - '-----END PUBLIC KEY-----'; - [ - { - message: '883177e5126b9be2d9a9680327d5370c6f26861f5820c43da67a3ad609', - salt: '04e215ee6ff934b9da70d7730c8734abfcecde89', - signature: '82c2b160093b8aa3c0f7522b19f87354066c77847abf2a9fce542d0e84' + - 'e920c5afb49ffdfdace16560ee94a1369601148ebad7a0e151cf163317' + - '91a5727d05f21e74e7eb811440206935d744765a15e79f015cb66c532c' + - '87a6a05961c8bfad741a9a6657022894393e7223739796c02a77455d0f' + - '555b0ec01ddf259b6207fd0fd57614cef1a5573baaff4ec00069951659' + - 'b85f24300a25160ca8522dc6e6727e57d019d7e63629b8fe5e89e25cc1' + - '5beb3a647577559299280b9b28f79b0409000be25bbd96408ba3b43cc4' + - '86184dd1c8e62553fa1af4040f60663de7f5e49c04388e257f1ce89c95' + - 'dab48a315d9b66b1b7628233876ff2385230d070d07e1666' - }, - { - message: 'dd670a01465868adc93f26131957a50c52fb777cdbaa30892c9e12361164' + - 'ec13979d43048118e4445db87bee58dd987b3425d02071d8dbae80708b03' + - '9dbb64dbd1de5657d9fed0c118a54143742e0ff3c87f74e45857647af3f7' + - '9eb0a14c9d75ea9a1a04b7cf478a897a708fd988f48e801edb0b7039df8c' + - '23bb3c56f4e821ac', - salt: '8b2bdd4b40faf545c778ddf9bc1a49cb57f9b71b', - signature: '14ae35d9dd06ba92f7f3b897978aed7cd4bf5ff0b585a40bd46ce1b42c' + - 'd2703053bb9044d64e813d8f96db2dd7007d10118f6f8f8496097ad75e' + - '1ff692341b2892ad55a633a1c55e7f0a0ad59a0e203a5b8278aec54dd8' + - '622e2831d87174f8caff43ee6c46445345d84a59659bfb92ecd4c81866' + - '8695f34706f66828a89959637f2bf3e3251c24bdba4d4b7649da002221' + - '8b119c84e79a6527ec5b8a5f861c159952e23ec05e1e717346faefe8b1' + - '686825bd2b262fb2531066c0de09acde2e4231690728b5d85e115a2f6b' + - '92b79c25abc9bd9399ff8bcf825a52ea1f56ea76dd26f43baafa18bfa9' + - '2a504cbd35699e26d1dcc5a2887385f3c63232f06f3244c3' - }, - { - message: '48b2b6a57a63c84cea859d65c668284b08d96bdcaabe252db0e4a96cb1ba' + - 'c6019341db6fbefb8d106b0e90eda6bcc6c6262f37e7ea9c7e5d226bd7df' + - '85ec5e71efff2f54c5db577ff729ff91b842491de2741d0c631607df586b' + - '905b23b91af13da12304bf83eca8a73e871ff9db', - salt: '4e96fc1b398f92b44671010c0dc3efd6e20c2d73', - signature: '6e3e4d7b6b15d2fb46013b8900aa5bbb3939cf2c095717987042026ee6' + - '2c74c54cffd5d7d57efbbf950a0f5c574fa09d3fc1c9f513b05b4ff50d' + - 'd8df7edfa20102854c35e592180119a70ce5b085182aa02d9ea2aa90d1' + - 'df03f2daae885ba2f5d05afdac97476f06b93b5bc94a1a80aa9116c4d6' + - '15f333b098892b25fface266f5db5a5a3bcc10a824ed55aad35b727834' + - 'fb8c07da28fcf416a5d9b2224f1f8b442b36f91e456fdea2d7cfe33672' + - '68de0307a4c74e924159ed33393d5e0655531c77327b89821bdedf8801' + - '61c78cd4196b5419f7acc3f13e5ebf161b6e7c6724716ca33b85c2e256' + - '40192ac2859651d50bde7eb976e51cec828b98b6563b86bb' - }, - { - message: '0b8777c7f839baf0a64bbbdbc5ce79755c57a205b845c174e2d2e90546a0' + - '89c4e6ec8adffa23a7ea97bae6b65d782b82db5d2b5a56d22a29a05e7c44' + - '33e2b82a621abba90add05ce393fc48a840542451a', - salt: 'c7cd698d84b65128d8835e3a8b1eb0e01cb541ec', - signature: '34047ff96c4dc0dc90b2d4ff59a1a361a4754b255d2ee0af7d8bf87c9b' + - 'c9e7ddeede33934c63ca1c0e3d262cb145ef932a1f2c0a997aa6a34f8e' + - 'aee7477d82ccf09095a6b8acad38d4eec9fb7eab7ad02da1d11d8e54c1' + - '825e55bf58c2a23234b902be124f9e9038a8f68fa45dab72f66e0945bf' + - '1d8bacc9044c6f07098c9fcec58a3aab100c805178155f030a124c450e' + - '5acbda47d0e4f10b80a23f803e774d023b0015c20b9f9bbe7c91296338' + - 'd5ecb471cafb032007b67a60be5f69504a9f01abb3cb467b260e2bce86' + - '0be8d95bf92c0c8e1496ed1e528593a4abb6df462dde8a0968dffe4683' + - '116857a232f5ebf6c85be238745ad0f38f767a5fdbf486fb' - }, - { - message: 'f1036e008e71e964dadc9219ed30e17f06b4b68a955c16b312b1eddf028b' + - '74976bed6b3f6a63d4e77859243c9cccdc98016523abb02483b35591c33a' + - 'ad81213bb7c7bb1a470aabc10d44256c4d4559d916', - salt: 'efa8bff96212b2f4a3f371a10d574152655f5dfb', - signature: '7e0935ea18f4d6c1d17ce82eb2b3836c55b384589ce19dfe743363ac99' + - '48d1f346b7bfddfe92efd78adb21faefc89ade42b10f374003fe122e67' + - '429a1cb8cbd1f8d9014564c44d120116f4990f1a6e38774c194bd1b821' + - '3286b077b0499d2e7b3f434ab12289c556684deed78131934bb3dd6537' + - '236f7c6f3dcb09d476be07721e37e1ceed9b2f7b406887bd53157305e1' + - 'c8b4f84d733bc1e186fe06cc59b6edb8f4bd7ffefdf4f7ba9cfb9d5706' + - '89b5a1a4109a746a690893db3799255a0cb9215d2d1cd490590e952e8c' + - '8786aa0011265252470c041dfbc3eec7c3cbf71c24869d115c0cb4a956' + - 'f56d530b80ab589acfefc690751ddf36e8d383f83cedd2cc' - }, - { - message: '25f10895a87716c137450bb9519dfaa1f207faa942ea88abf71e9c179800' + - '85b555aebab76264ae2a3ab93c2d12981191ddac6fb5949eb36aee3c5da9' + - '40f00752c916d94608fa7d97ba6a2915b688f20323d4e9d96801d89a72ab' + - '5892dc2117c07434fcf972e058cf8c41ca4b4ff554f7d5068ad3155fced0' + - 'f3125bc04f9193378a8f5c4c3b8cb4dd6d1cc69d30ecca6eaa51e36a0573' + - '0e9e342e855baf099defb8afd7', - salt: 'ad8b1523703646224b660b550885917ca2d1df28', - signature: '6d3b5b87f67ea657af21f75441977d2180f91b2c5f692de82955696a68' + - '6730d9b9778d970758ccb26071c2209ffbd6125be2e96ea81b67cb9b93' + - '08239fda17f7b2b64ecda096b6b935640a5a1cb42a9155b1c9ef7a633a' + - '02c59f0d6ee59b852c43b35029e73c940ff0410e8f114eed46bbd0fae1' + - '65e42be2528a401c3b28fd818ef3232dca9f4d2a0f5166ec59c42396d6' + - 'c11dbc1215a56fa17169db9575343ef34f9de32a49cdc3174922f229c2' + - '3e18e45df9353119ec4319cedce7a17c64088c1f6f52be29634100b391' + - '9d38f3d1ed94e6891e66a73b8fb849f5874df59459e298c7bbce2eee78' + - '2a195aa66fe2d0732b25e595f57d3e061b1fc3e4063bf98f' - } - ].forEach((vector) => testVerify(ex10Cert, vector)); + const vectorfile = path.join(common.fixturesDir, 'pss-vectors.json'); + const examples = JSON.parse(fs.readFileSync(vectorfile, { + encoding: 'utf8' + })); + + for (const key in examples) { + const example = examples[key]; + const publicKey = example.publicKey.join('\n'); + example.tests.forEach((test) => testVerify(publicKey, test)); + } } // Test exceptions for invalid `padding` and `saltLength` values @@ -423,3 +238,39 @@ const modSize = 1024; crypto.createSign('RSA-SHA1').update('Test123').sign(null, 'base64'); }, /^Error: No key provided to sign$/); } + +// RSA-PSS Sign test by verifying with 'openssl dgst -verify' +{ + if (!common.opensslCli) { + common.skip('node compiled without OpenSSL CLI.'); + return; + } + + const pubfile = path.join(common.fixturesDir, 'keys/rsa_public_2048.pem'); + const privfile = path.join(common.fixturesDir, 'keys/rsa_private_2048.pem'); + const privkey = fs.readFileSync(privfile); + + const msg = 'Test123'; + const s5 = crypto.createSign('RSA-SHA256') + .update(msg) + .sign({ + key: privkey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING + }); + + common.refreshTmpDir(); + + const sigfile = path.join(common.tmpDir, 's5.sig'); + fs.writeFileSync(sigfile, s5); + const msgfile = path.join(common.tmpDir, 's5.msg'); + fs.writeFileSync(msgfile, msg); + + const cmd = '"' + common.opensslCli + '" dgst -sha256 -verify "' + pubfile + + '" -signature "' + sigfile + + '" -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-2 "' + + msgfile + '"'; + + exec(cmd, common.mustCall((err, stdout, stderr) => { + assert(stdout.includes('Verified OK')); + })); +} From 8772278b5d9b4399537fb110a8a22554543242f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= Date: Sat, 1 Apr 2017 13:19:19 +0200 Subject: [PATCH 17/17] Add changes to doc --- doc/api/crypto.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/api/crypto.md b/doc/api/crypto.md index ad55955fe0a51f..f766ee062e5000 100644 --- a/doc/api/crypto.md +++ b/doc/api/crypto.md @@ -964,6 +964,10 @@ console.log(sign.sign(privateKey).toString('hex')); ### sign.sign(private_key[, output_format]) - `private_key` {string | Object} - `key` {string} @@ -1084,6 +1088,10 @@ This can be called many times with new data as it is streamed. ### verifier.verify(object, signature[, signature_format]) - `object` {string | Object} - `signature` {string | Buffer | Uint8Array} @@ -1110,7 +1118,8 @@ or an object with one or more of the following properties: The `signature` argument is the previously calculated signature for the data, in the `signature_format` which can be `'latin1'`, `'hex'` or `'base64'`. If a `signature_format` is specified, the `signature` is expected to be a -string; otherwise `signature` is expected to be a [`Buffer`][]. +string; otherwise `signature` is expected to be a [`Buffer`][] or +`Uint8Array`. Returns `true` or `false` depending on the validity of the signature for the data and public key.