Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: add sign/verify support for RSASSA-PSS #11705

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,10 @@ console.log(sign.sign(privateKey).toString('hex'));
### sign.sign(private_key[, output_format])
<!-- YAML
added: v0.1.92
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/11705
description: Support for RSASSA-PSS and additional options was added.
-->
- `private_key` {string | Object}
- `key` {string}
Expand All @@ -975,10 +979,21 @@ 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 must contain one or more of the following properties:

* `key`: {string} - PEM encoded private key
* `key`: {string} - PEM encoded private key (required)
* `passphrase`: {string} - passphrase for the private key
* `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
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
Expand Down Expand Up @@ -1073,14 +1088,33 @@ This can be called many times with new data as it is streamed.
### verifier.verify(object, signature[, signature_format])
<!-- YAML
added: v0.1.92
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/11705
description: Support for RSASSA-PSS and additional options was added.
-->
- `object` {string}
- `object` {string | Object}
- `signature` {string | Buffer | Uint8Array}
- `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 `object` argument can be either a string containing a PEM encoded object,
Copy link
Contributor

Choose a reason for hiding this comment

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

This change is also needed.

 - `object` {string | Object}

which can be an RSA public key, a DSA public key, or an X.509 certificate,
or an object with one or more of the following properties:

* `key`: {string} - PEM encoded private key (required)
* `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
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'`.
If a `signature_format` is specified, the `signature` is expected to be a
Expand Down Expand Up @@ -2044,6 +2078,21 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL.
<td><code>RSA_PKCS1_PSS_PADDING</code></td>
<td></td>
</tr>
<tr>
<td><code>RSA_PSS_SALTLEN_DIGEST</code></td>
<td>Sets the salt length for `RSA_PKCS1_PSS_PADDING` to the digest size
when signing or verifying.</td>
</tr>
<tr>
<td><code>RSA_PSS_SALTLEN_MAX_SIGN</code></td>
<td>Sets the salt length for `RSA_PKCS1_PSS_PADDING` to the maximum
permissible value when signing data.</td>
</tr>
<tr>
<td><code>RSA_PSS_SALTLEN_AUTO</code></td>
<td>Causes the salt length for `RSA_PKCS1_PSS_PADDING` to be determined
automatically when verifying a signature.</td>
</tr>
<tr>
<td><code>POINT_CONVERSION_COMPRESSED</code></td>
<td></td>
Expand Down Expand Up @@ -2119,6 +2168,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
49 changes: 46 additions & 3 deletions lib/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,28 @@ 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 specific to RSA
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');
}
}
Copy link
Member

Choose a reason for hiding this comment

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

You could perhaps factor this out into a separate function but OTOH, this is fine too.


var ret = this._handle.sign(toBuf(key), null, passphrase, rsaPadding,
pssSaltLength);

encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
Expand All @@ -329,9 +350,31 @@ 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(options, signature, sigEncoding) {
var key = options.key || options;
sigEncoding = sigEncoding || exports.DEFAULT_ENCODING;
return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding));

// Options specific to RSA
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');
}
}

return this._handle.verify(toBuf(key), toBuf(signature, sigEncoding), null,
rsaPadding, pssSaltLength);
};

function rsaPublic(method, defaultPadding) {
Expand Down
12 changes: 12 additions & 0 deletions src/node_constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,18 @@ void DefineOpenSSLConstants(Local<Object> target) {
NODE_DEFINE_CONSTANT(target, RSA_PKCS1_PSS_PADDING);
#endif

#ifdef RSA_PSS_SALTLEN_DIGEST
NODE_DEFINE_CONSTANT(target, RSA_PSS_SALTLEN_DIGEST);
#endif

#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
NODE_DEFINE_CONSTANT(target, POINT_CONVERSION_COMPRESSED);
Expand Down
13 changes: 13 additions & 0 deletions src/node_constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:" \
Expand Down
Loading