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

[v6.x backport] crypto: add sign/verify support for RSASSA-PSS #14376

Closed
wants to merge 6 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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ documentation of the latest stable version.

### Verifying Binaries

Current, LTS and Nightly download directories all contain a _SHASUM256.txt_
Current, LTS and Nightly download directories all contain a _SHASUMS256.txt_
file that lists the SHA checksums for each file available for
download.

The _SHASUM256.txt_ can be downloaded using curl.
The _SHASUMS256.txt_ can be downloaded using curl.

```console
$ curl -O https://nodejs.org/dist/vx.y.z/SHASUMS256.txt
Expand All @@ -135,10 +135,10 @@ _(Where "node-vx.y.z.tar.gz" is the name of the file you have
downloaded)_

Additionally, Current and LTS releases (not Nightlies) have GPG signed
copies of SHASUM256.txt files available as SHASUM256.txt.asc. You can use
copies of SHASUMS256.txt files available as SHASUMS256.txt.asc. You can use
`gpg` to verify that the file has not been tampered with.

To verify a SHASUM256.txt.asc, you will first need to import all of
To verify a SHASUMS256.txt.asc, you will first need to import all of
the GPG keys of individuals authorized to create releases. They are
listed at the bottom of this README under [Release Team](#release-team).
Use a command such as this to import the keys:
Expand Down
12 changes: 10 additions & 2 deletions deps/openssl/config/opensslconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
| solaris | x64 | solaris64-x86_64-gcc | o |
| freebsd | ia32 | BSD-x86 | o |
| freebsd | x64 | BSD-x86_64 | o |
| netbsd | ia32 | BSD-x86 | o |
| netbsd | x64 | BSD-x86_64 | o |
| openbsd | ia32 | BSD-x86 | - |
| openbsd | x64 | BSD-x86_64 | - |
| others | others | linux-elf | - |
Expand All @@ -51,6 +53,7 @@
| mac | __APPLE__ && __MACH__ |
| solaris | __sun |
| freebsd | __FreeBSD__ |
| netbsd | __NetBSD__ |
| openbsd | __OpenBSD__ |
| linux (not andorid)| __linux__ && !__ANDROID__ |
| android | __ANDROID__ |
Expand Down Expand Up @@ -94,6 +97,11 @@
# define OPENSSL_LINUX 1
#endif

#undef OPENSSL_BSD
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# define OPENSSL_BSD 1
#endif

#if defined(OPENSSL_LINUX) && defined(__i386__)
# include "./archs/linux-elf/opensslconf.h"
#elif defined(OPENSSL_LINUX) && defined(__ILP32__)
Expand All @@ -112,9 +120,9 @@
# include "./archs/VC-WIN32/opensslconf.h"
#elif defined(_WIN32) && defined(_M_X64)
# include "./archs/VC-WIN64A/opensslconf.h"
#elif (defined(__FreeBSD__) || defined(__OpenBSD__)) && defined(__i386__)
#elif defined(OPENSSL_BSD) && defined(__i386__)
# include "./archs/BSD-x86/opensslconf.h"
#elif (defined(__FreeBSD__) || defined(__OpenBSD__)) && defined(__x86_64__)
#elif defined(OPENSSL_BSD) && defined(__x86_64__)
# include "./archs/BSD-x86_64/opensslconf.h"
#elif defined(__sun) && defined(__i386__)
# include "./archs/solaris-x86-gcc/opensslconf.h"
Expand Down
61 changes: 57 additions & 4 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -896,17 +896,32 @@ 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.
-->

Calculates the signature on all the data passed through using either
[`sign.update()`][] or [`sign.write()`][stream-writable-write].

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 @@ -989,11 +1004,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}
- `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
one 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,
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 public 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 @@ -1900,6 +1937,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 @@ -1975,6 +2027,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 @@ -283,7 +283,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');
}
}

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

encoding = encoding || exports.DEFAULT_ENCODING;
if (encoding && encoding !== 'buffer')
Expand All @@ -309,9 +330,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
18 changes: 6 additions & 12 deletions lib/internal/bootstrap_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -317,20 +317,14 @@
}

function tryGetCwd(path) {
var threw = true;
var cwd;
try {
cwd = process.cwd();
threw = false;
} finally {
if (threw) {
// getcwd(3) can fail if the current working directory has been deleted.
// Fall back to the directory name of the (absolute) executable path.
// It's not really correct but what are the alternatives?
return path.dirname(process.execPath);
}
return process.cwd();
} catch (ex) {
// getcwd(3) can fail if the current working directory has been deleted.
// Fall back to the directory name of the (absolute) executable path.
// It's not really correct but what are the alternatives?
return path.dirname(process.execPath);
}
return cwd;
}

function evalScript(name) {
Expand Down
14 changes: 12 additions & 2 deletions lib/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ function normalizeStringWin32(path, allowAboveRoot) {
var lastSlash = -1;
var dots = 0;
var code;
var isAboveRoot = false;
for (var i = 0; i <= path.length; ++i) {
if (i < path.length)
code = path.charCodeAt(i);
Expand All @@ -25,7 +26,7 @@ function normalizeStringWin32(path, allowAboveRoot) {
if (lastSlash === i - 1 || dots === 1) {
// NOOP
} else if (lastSlash !== i - 1 && dots === 2) {
if (res.length < 2 ||
if (res.length < 2 || !isAboveRoot ||
res.charCodeAt(res.length - 1) !== 46/*.*/ ||
res.charCodeAt(res.length - 2) !== 46/*.*/) {
if (res.length > 2) {
Expand All @@ -42,12 +43,14 @@ function normalizeStringWin32(path, allowAboveRoot) {
res = res.slice(0, j);
lastSlash = i;
dots = 0;
isAboveRoot = false;
continue;
}
} else if (res.length === 2 || res.length === 1) {
res = '';
lastSlash = i;
dots = 0;
isAboveRoot = false;
continue;
}
}
Expand All @@ -56,12 +59,14 @@ function normalizeStringWin32(path, allowAboveRoot) {
res += '\\..';
else
res = '..';
isAboveRoot = true;
}
} else {
if (res.length > 0)
res += '\\' + path.slice(lastSlash + 1, i);
else
res = path.slice(lastSlash + 1, i);
isAboveRoot = false;
}
lastSlash = i;
dots = 0;
Expand All @@ -80,6 +85,7 @@ function normalizeStringPosix(path, allowAboveRoot) {
var lastSlash = -1;
var dots = 0;
var code;
var isAboveRoot = false;
for (var i = 0; i <= path.length; ++i) {
if (i < path.length)
code = path.charCodeAt(i);
Expand All @@ -91,7 +97,7 @@ function normalizeStringPosix(path, allowAboveRoot) {
if (lastSlash === i - 1 || dots === 1) {
// NOOP
} else if (lastSlash !== i - 1 && dots === 2) {
if (res.length < 2 ||
if (res.length < 2 || !isAboveRoot ||
res.charCodeAt(res.length - 1) !== 46/*.*/ ||
res.charCodeAt(res.length - 2) !== 46/*.*/) {
if (res.length > 2) {
Expand All @@ -108,12 +114,14 @@ function normalizeStringPosix(path, allowAboveRoot) {
res = res.slice(0, j);
lastSlash = i;
dots = 0;
isAboveRoot = false;
continue;
}
} else if (res.length === 2 || res.length === 1) {
res = '';
lastSlash = i;
dots = 0;
isAboveRoot = false;
continue;
}
}
Expand All @@ -122,12 +130,14 @@ function normalizeStringPosix(path, allowAboveRoot) {
res += '/..';
else
res = '..';
isAboveRoot = true;
}
} else {
if (res.length > 0)
res += '/' + path.slice(lastSlash + 1, i);
else
res = path.slice(lastSlash + 1, i);
isAboveRoot = false;
}
lastSlash = i;
dots = 0;
Expand Down
12 changes: 12 additions & 0 deletions src/node_constants.cc
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,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 @@ -7,6 +7,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