crypto: add CFRG curves to Web Crypto API
PR-URL: https://github.com/nodejs/node/pull/42507 Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
parent
dfa896f926
commit
7e5da97d15
@ -1,5 +1,21 @@
|
|||||||
# Web Crypto API
|
# Web Crypto API
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'Ed25519'`, `'Ed448'`, `'X25519'`, and `'X448'`
|
||||||
|
algorithms.
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Removed proprietary `'NODE-ED25519'` and `'NODE-ED448'`
|
||||||
|
algorithms.
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Removed proprietary `'NODE-X25519'` and `'NODE-X448'` named
|
||||||
|
curves from the `'ECDH'` algorithm.
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- introduced_in=v15.0.0 -->
|
<!-- introduced_in=v15.0.0 -->
|
||||||
|
|
||||||
> Stability: 1 - Experimental
|
> Stability: 1 - Experimental
|
||||||
@ -69,22 +85,22 @@ async function generateEcKey(namedCurve = 'P-521') {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### ED25519/ED448/X25519/X448 key pairs
|
#### Ed25519/Ed448/X25519/X448 key pairs
|
||||||
|
|
||||||
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { subtle } = require('node:crypto').webcrypto;
|
const { subtle } = require('node:crypto').webcrypto;
|
||||||
|
|
||||||
async function generateEd25519Key() {
|
async function generateEd25519Key() {
|
||||||
return subtle.generateKey({
|
return subtle.generateKey({
|
||||||
name: 'NODE-ED25519',
|
name: 'Ed25519',
|
||||||
namedCurve: 'NODE-ED25519',
|
|
||||||
}, true, ['sign', 'verify']);
|
}, true, ['sign', 'verify']);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generateX25519Key() {
|
async function generateX25519Key() {
|
||||||
return subtle.generateKey({
|
return subtle.generateKey({
|
||||||
name: 'ECDH',
|
name: 'X25519',
|
||||||
namedCurve: 'NODE-X25519',
|
|
||||||
}, true, ['deriveKey']);
|
}, true, ['deriveKey']);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -315,7 +331,11 @@ implementation and the APIs supported for each:
|
|||||||
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
| `'RSA-PSS'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
||||||
| `'RSA-OAEP'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
| `'RSA-OAEP'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
||||||
| `'ECDSA'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
| `'ECDSA'` | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
||||||
|
| `'Ed25519'`[^2] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
||||||
|
| `'Ed448'`[^2] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
||||||
| `'ECDH'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
|
| `'ECDH'` | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
|
||||||
|
| `'X25519'`[^2] | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
|
||||||
|
| `'X448'`[^2] | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
|
||||||
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
| `'AES-CTR'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
||||||
| `'AES-CBC'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
| `'AES-CBC'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
||||||
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
| `'AES-GCM'` | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | | | | |
|
||||||
@ -329,8 +349,6 @@ implementation and the APIs supported for each:
|
|||||||
| `'SHA-512'` | | | | | | | | | | | | ✔ |
|
| `'SHA-512'` | | | | | | | | | | | | ✔ |
|
||||||
| `'NODE-DSA'`[^1] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
| `'NODE-DSA'`[^1] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
||||||
| `'NODE-DH'`[^1] | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
|
| `'NODE-DH'`[^1] | ✔ | ✔ | ✔ | | | | | ✔ | ✔ | | | |
|
||||||
| `'NODE-ED25519'`[^1] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
|
||||||
| `'NODE-ED448'`[^1] | ✔ | ✔ | ✔ | | | | | | | ✔ | ✔ | |
|
|
||||||
|
|
||||||
## Class: `Crypto`
|
## Class: `Crypto`
|
||||||
|
|
||||||
@ -394,7 +412,7 @@ added: v15.0.0
|
|||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* Type: {AesKeyGenParams|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|NodeDsaKeyGenParams|NodeDhKeyGenParams|NodeEdKeyGenParams}
|
* Type: {AesKeyGenParams|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|NodeDsaKeyGenParams|NodeDhKeyGenParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
@ -459,7 +477,11 @@ Valid key usages depend on the key algorithm (identified by
|
|||||||
| `'AES-GCM'` | ✔ | ✔ | | | | | ✔ | ✔ |
|
| `'AES-GCM'` | ✔ | ✔ | | | | | ✔ | ✔ |
|
||||||
| `'AES-KW'` | | | | | | | ✔ | ✔ |
|
| `'AES-KW'` | | | | | | | ✔ | ✔ |
|
||||||
| `'ECDH'` | | | | | ✔ | ✔ | | |
|
| `'ECDH'` | | | | | ✔ | ✔ | | |
|
||||||
|
| `'X25519'`[^2] | | | | | ✔ | ✔ | | |
|
||||||
|
| `'X448'`[^2] | | | | | ✔ | ✔ | | |
|
||||||
| `'ECDSA'` | | | ✔ | ✔ | | | | |
|
| `'ECDSA'` | | | ✔ | ✔ | | | | |
|
||||||
|
| `'Ed25519'`[^2] | | | ✔ | ✔ | | | | |
|
||||||
|
| `'Ed448'`[^2] | | | ✔ | ✔ | | | | |
|
||||||
| `'HDKF'` | | | | | ✔ | ✔ | | |
|
| `'HDKF'` | | | | | ✔ | ✔ | | |
|
||||||
| `'HMAC'` | | | ✔ | ✔ | | | | |
|
| `'HMAC'` | | | ✔ | ✔ | | | | |
|
||||||
| `'PBKDF2'` | | | | | ✔ | ✔ | | |
|
| `'PBKDF2'` | | | | | ✔ | ✔ | | |
|
||||||
@ -469,8 +491,6 @@ Valid key usages depend on the key algorithm (identified by
|
|||||||
| `'NODE-DSA'`[^1] | | | ✔ | ✔ | | | | |
|
| `'NODE-DSA'`[^1] | | | ✔ | ✔ | | | | |
|
||||||
| `'NODE-DH'`[^1] | | | | | ✔ | ✔ | | |
|
| `'NODE-DH'`[^1] | | | | | ✔ | ✔ | | |
|
||||||
| `'NODE-SCRYPT'`[^1] | | | | | ✔ | ✔ | | |
|
| `'NODE-SCRYPT'`[^1] | | | | | ✔ | ✔ | | |
|
||||||
| `'NODE-ED25519'`[^1] | | | ✔ | ✔ | | | | |
|
|
||||||
| `'NODE-ED448'`[^1] | | | ✔ | ✔ | | | | |
|
|
||||||
|
|
||||||
## Class: `CryptoKeyPair`
|
## Class: `CryptoKeyPair`
|
||||||
|
|
||||||
@ -530,11 +550,15 @@ The algorithms currently supported include:
|
|||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'X25519'`, and `'X448'` algorithms.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm`: {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|NodeDhDeriveBitsParams|NodeScryptParams}
|
* `algorithm`: {AlgorithmIdentifier|EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|NodeDhDeriveBitsParams|NodeScryptParams}
|
||||||
* `baseKey`: {CryptoKey}
|
* `baseKey`: {CryptoKey}
|
||||||
* `length`: {number}
|
* `length`: {number}
|
||||||
* Returns: {Promise} containing {ArrayBuffer}
|
* Returns: {Promise} containing {ArrayBuffer}
|
||||||
@ -559,11 +583,15 @@ The algorithms currently supported include:
|
|||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'X25519'`, and `'X448'` algorithms.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm`: {EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|NodeDhDeriveBitsParams|NodeScryptParams}
|
* `algorithm`: {AlgorithmIdentifier|EcdhKeyDeriveParams|HkdfParams|Pbkdf2Params|NodeDhDeriveBitsParams|NodeScryptParams}
|
||||||
* `baseKey`: {CryptoKey}
|
* `baseKey`: {CryptoKey}
|
||||||
* `derivedKeyAlgorithm`: {HmacKeyGenParams|AesKeyGenParams}
|
* `derivedKeyAlgorithm`: {HmacKeyGenParams|AesKeyGenParams}
|
||||||
* `extractable`: {boolean}
|
* `extractable`: {boolean}
|
||||||
@ -640,6 +668,10 @@ The algorithms currently supported include:
|
|||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'Ed25519'`, `'Ed448'`, `'X25519'`, and `'X448'`
|
||||||
|
algorithms.
|
||||||
- version: v15.9.0
|
- version: v15.9.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/37203
|
pr-url: https://github.com/nodejs/node/pull/37203
|
||||||
description: Removed `'NODE-DSA'` JWK export.
|
description: Removed `'NODE-DSA'` JWK export.
|
||||||
@ -674,6 +706,8 @@ extension that allows converting a {CryptoKey} into a Node.js {KeyObject}.
|
|||||||
| `'AES-KW'` | | | ✔ | ✔ |
|
| `'AES-KW'` | | | ✔ | ✔ |
|
||||||
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
|
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
|
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
| `'Ed25519'`[^2] | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
| `'Ed448'`[^2] | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'HDKF'` | | | | |
|
| `'HDKF'` | | | | |
|
||||||
| `'HMAC'` | | | ✔ | ✔ |
|
| `'HMAC'` | | | ✔ | ✔ |
|
||||||
| `'PBKDF2'` | | | | |
|
| `'PBKDF2'` | | | | |
|
||||||
@ -683,8 +717,6 @@ extension that allows converting a {CryptoKey} into a Node.js {KeyObject}.
|
|||||||
| `'NODE-DSA'`[^1] | ✔ | ✔ | | |
|
| `'NODE-DSA'`[^1] | ✔ | ✔ | | |
|
||||||
| `'NODE-DH'`[^1] | ✔ | ✔ | | |
|
| `'NODE-DH'`[^1] | ✔ | ✔ | | |
|
||||||
| `'NODE-SCRYPT'`[^1] | | | | |
|
| `'NODE-SCRYPT'`[^1] | | | | |
|
||||||
| `'NODE-ED25519'`[^1] | ✔ | ✔ | ✔ | ✔ |
|
|
||||||
| `'NODE-ED448'`[^1] | ✔ | ✔ | ✔ | ✔ |
|
|
||||||
|
|
||||||
### `subtle.generateKey(algorithm, extractable, keyUsages)`
|
### `subtle.generateKey(algorithm, extractable, keyUsages)`
|
||||||
|
|
||||||
@ -694,7 +726,7 @@ added: v15.0.0
|
|||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm`: {RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|AesKeyGenParams|NodeDsaKeyGenParams|NodeDhKeyGenParams|NodeEdKeyGenParams}
|
* `algorithm`: {AlgorithmIdentifier|RsaHashedKeyGenParams|EcKeyGenParams|HmacKeyGenParams|AesKeyGenParams|NodeDsaKeyGenParams|NodeDhKeyGenParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
@ -713,11 +745,13 @@ include:
|
|||||||
* `'RSA-PSS'`
|
* `'RSA-PSS'`
|
||||||
* `'RSA-OAEP'`
|
* `'RSA-OAEP'`
|
||||||
* `'ECDSA'`
|
* `'ECDSA'`
|
||||||
|
* `'Ed25519'`[^2]
|
||||||
|
* `'Ed448'`[^2]
|
||||||
* `'ECDH'`
|
* `'ECDH'`
|
||||||
|
* `'X25519'`[^2]
|
||||||
|
* `'X448'`[^2]
|
||||||
* `'NODE-DSA'`[^1]
|
* `'NODE-DSA'`[^1]
|
||||||
* `'NODE-DH'`[^1]
|
* `'NODE-DH'`[^1]
|
||||||
* `'NODE-ED25519'`[^1]
|
|
||||||
* `'NODE-ED448'`[^1]
|
|
||||||
|
|
||||||
The {CryptoKey} (secret key) generating algorithms supported include:
|
The {CryptoKey} (secret key) generating algorithms supported include:
|
||||||
|
|
||||||
@ -732,6 +766,10 @@ The {CryptoKey} (secret key) generating algorithms supported include:
|
|||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
changes:
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'Ed25519'`, `'Ed448'`, `'X25519'`, and `'X448'`
|
||||||
|
algorithms.
|
||||||
- version: v15.9.0
|
- version: v15.9.0
|
||||||
pr-url: https://github.com/nodejs/node/pull/37203
|
pr-url: https://github.com/nodejs/node/pull/37203
|
||||||
description: Removed `'NODE-DSA'` JWK import.
|
description: Removed `'NODE-DSA'` JWK import.
|
||||||
@ -743,7 +781,7 @@ changes:
|
|||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm`: {RsaHashedImportParams|EcKeyImportParams|HmacImportParams|AesImportParams|Pbkdf2ImportParams|NodeDsaImportParams|NodeDhImportParams|NodeScryptImportParams|NodeEdKeyImportParams}
|
* `algorithm`: {AlgorithmIdentifier|RsaHashedImportParams|EcKeyImportParams|HmacImportParams|NodeDsaImportParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
@ -770,7 +808,11 @@ The algorithms currently supported include:
|
|||||||
| `'AES-GCM'` | | | ✔ | ✔ |
|
| `'AES-GCM'` | | | ✔ | ✔ |
|
||||||
| `'AES-KW'` | | | ✔ | ✔ |
|
| `'AES-KW'` | | | ✔ | ✔ |
|
||||||
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
|
| `'ECDH'` | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
| `'X25519'`[^2] | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
| `'X448'`[^2] | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
|
| `'ECDSA'` | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
| `'Ed25519'`[^2] | ✔ | ✔ | ✔ | ✔ |
|
||||||
|
| `'Ed448'`[^2] | ✔ | ✔ | ✔ | ✔ |
|
||||||
| `'HDKF'` | | | | ✔ |
|
| `'HDKF'` | | | | ✔ |
|
||||||
| `'HMAC'` | | | ✔ | ✔ |
|
| `'HMAC'` | | | ✔ | ✔ |
|
||||||
| `'PBKDF2'` | | | | ✔ |
|
| `'PBKDF2'` | | | | ✔ |
|
||||||
@ -780,18 +822,20 @@ The algorithms currently supported include:
|
|||||||
| `'NODE-DSA'`[^1] | ✔ | ✔ | | |
|
| `'NODE-DSA'`[^1] | ✔ | ✔ | | |
|
||||||
| `'NODE-DH'`[^1] | ✔ | ✔ | | |
|
| `'NODE-DH'`[^1] | ✔ | ✔ | | |
|
||||||
| `'NODE-SCRYPT'`[^1] | | | | ✔ |
|
| `'NODE-SCRYPT'`[^1] | | | | ✔ |
|
||||||
| `'NODE-ED25519'`[^1] | ✔ | ✔ | ✔ | ✔ |
|
|
||||||
| `'NODE-ED448'`[^1] | ✔ | ✔ | ✔ | ✔ |
|
|
||||||
|
|
||||||
### `subtle.sign(algorithm, key, data)`
|
### `subtle.sign(algorithm, key, data)`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'Ed25519'`, and `'Ed448'` algorithms.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm`: {RsaSignParams|RsaPssParams|EcdsaParams|HmacParams|NodeDsaSignParams}
|
* `algorithm`: {AlgorithmIdentifier|RsaPssParams|EcdsaParams|Ed448Params}
|
||||||
* `key`: {CryptoKey}
|
* `key`: {CryptoKey}
|
||||||
* `data`: {ArrayBuffer|TypedArray|DataView|Buffer}
|
* `data`: {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||||
* Returns: {Promise} containing {ArrayBuffer}
|
* Returns: {Promise} containing {ArrayBuffer}
|
||||||
@ -808,10 +852,10 @@ The algorithms currently supported include:
|
|||||||
* `'RSASSA-PKCS1-v1_5'`
|
* `'RSASSA-PKCS1-v1_5'`
|
||||||
* `'RSA-PSS'`
|
* `'RSA-PSS'`
|
||||||
* `'ECDSA'`
|
* `'ECDSA'`
|
||||||
|
* `'Ed25519'`[^2]
|
||||||
|
* `'Ed448'`[^2]
|
||||||
* `'HMAC'`
|
* `'HMAC'`
|
||||||
* `'NODE-DSA'`[^1]
|
* `'NODE-DSA'`[^1]
|
||||||
* `'NODE-ED25519'`[^1]
|
|
||||||
* `'NODE-ED448'`[^1]
|
|
||||||
|
|
||||||
### `subtle.unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgo, unwrappedKeyAlgo, extractable, keyUsages)`
|
### `subtle.unwrapKey(format, wrappedKey, unwrappingKey, unwrapAlgo, unwrappedKeyAlgo, extractable, keyUsages)`
|
||||||
|
|
||||||
@ -825,8 +869,8 @@ added: v15.0.0
|
|||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `unwrapAlgo`: {RsaOaepParams|AesCtrParams|AesCbcParams|AesGcmParams|AesKwParams}
|
* `unwrapAlgo`: {AlgorithmIdentifier|RsaOaepParams|AesCtrParams|AesCbcParams|AesGcmParams}
|
||||||
* `unwrappedKeyAlgo`: {RsaHashedImportParams|EcKeyImportParams|HmacImportParams|AesImportParams}
|
* `unwrappedKeyAlgo`: {AlgorithmIdentifier|RsaHashedImportParams|EcKeyImportParams|HmacImportParams}
|
||||||
|
|
||||||
<!--lint enable maximum-line-length remark-lint-->
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
@ -870,11 +914,15 @@ The unwrapped key algorithms supported include:
|
|||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
|
changes:
|
||||||
|
- version: REPLACEME
|
||||||
|
pr-url: https://github.com/nodejs/node/pull/42507
|
||||||
|
description: Added `'Ed25519'`, and `'Ed448'` algorithms.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<!--lint disable maximum-line-length remark-lint-->
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `algorithm`: {RsaSignParams|RsaPssParams|EcdsaParams|HmacParams|NodeDsaSignParams}
|
* `algorithm`: {AlgorithmIdentifier|RsaPssParams|EcdsaParams|Ed448Params}
|
||||||
* `key`: {CryptoKey}
|
* `key`: {CryptoKey}
|
||||||
* `signature`: {ArrayBuffer|TypedArray|DataView|Buffer}
|
* `signature`: {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||||
* `data`: {ArrayBuffer|TypedArray|DataView|Buffer}
|
* `data`: {ArrayBuffer|TypedArray|DataView|Buffer}
|
||||||
@ -892,10 +940,10 @@ The algorithms currently supported include:
|
|||||||
* `'RSASSA-PKCS1-v1_5'`
|
* `'RSASSA-PKCS1-v1_5'`
|
||||||
* `'RSA-PSS'`
|
* `'RSA-PSS'`
|
||||||
* `'ECDSA'`
|
* `'ECDSA'`
|
||||||
|
* `'Ed25519'`[^2]
|
||||||
|
* `'Ed448'`[^2]
|
||||||
* `'HMAC'`
|
* `'HMAC'`
|
||||||
* `'NODE-DSA'`[^1]
|
* `'NODE-DSA'`[^1]
|
||||||
* `'NODE-ED25519'`[^1]
|
|
||||||
* `'NODE-ED448'`[^1]
|
|
||||||
|
|
||||||
### `subtle.wrapKey(format, key, wrappingKey, wrapAlgo)`
|
### `subtle.wrapKey(format, key, wrappingKey, wrapAlgo)`
|
||||||
|
|
||||||
@ -903,12 +951,16 @@ The algorithms currently supported include:
|
|||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<!--lint disable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
* `format`: {string} Must be one of `'raw'`, `'pkcs8'`, `'spki'`, or `'jwk'`.
|
* `format`: {string} Must be one of `'raw'`, `'pkcs8'`, `'spki'`, or `'jwk'`.
|
||||||
* `key`: {CryptoKey}
|
* `key`: {CryptoKey}
|
||||||
* `wrappingKey`: {CryptoKey}
|
* `wrappingKey`: {CryptoKey}
|
||||||
* `wrapAlgo`: {RsaOaepParams|AesCtrParams|AesCbcParams|AesGcmParams|AesKwParams}
|
* `wrapAlgo`: {AlgorithmIdentifier|RsaOaepParams|AesCtrParams|AesCbcParams|AesGcmParams}
|
||||||
* Returns: {Promise} containing {ArrayBuffer}
|
* Returns: {Promise} containing {ArrayBuffer}
|
||||||
|
|
||||||
|
<!--lint enable maximum-line-length remark-lint-->
|
||||||
|
|
||||||
In cryptography, "wrapping a key" refers to exporting and then encrypting the
|
In cryptography, "wrapping a key" refers to exporting and then encrypting the
|
||||||
keying material. The `subtle.wrapKey()` method exports the keying material into
|
keying material. The `subtle.wrapKey()` method exports the keying material into
|
||||||
the format identified by `format`, then encrypts it using the method and
|
the format identified by `format`, then encrypts it using the method and
|
||||||
@ -933,6 +985,20 @@ The algorithm parameter objects define the methods and parameters used by
|
|||||||
the various {SubtleCrypto} methods. While described here as "classes", they
|
the various {SubtleCrypto} methods. While described here as "classes", they
|
||||||
are simple JavaScript dictionary objects.
|
are simple JavaScript dictionary objects.
|
||||||
|
|
||||||
|
### Class: `AlgorithmIdentifier`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### `algorithmIdentifier.name`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {string}
|
||||||
|
|
||||||
### Class: `AesCbcParams`
|
### Class: `AesCbcParams`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1047,21 +1113,6 @@ added: v15.0.0
|
|||||||
This values must be one of `32`, `64`, `96`, `104`, `112`, `120`, or
|
This values must be one of `32`, `64`, `96`, `104`, `112`, `120`, or
|
||||||
`128`. **Default:** `128`.
|
`128`. **Default:** `128`.
|
||||||
|
|
||||||
### Class: `AesImportParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### `aesImportParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be one of `'AES-CTR'`, `'AES-CBC'`, `'AES-GCM'`, or
|
|
||||||
`'AES-KW'`.
|
|
||||||
|
|
||||||
### Class: `AesKeyGenParams`
|
### Class: `AesKeyGenParams`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1088,20 +1139,6 @@ added: v15.0.0
|
|||||||
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
|
* Type: {string} Must be one of `'AES-CBC'`, `'AES-CTR'`, `'AES-GCM'`, or
|
||||||
`'AES-KW'`
|
`'AES-KW'`
|
||||||
|
|
||||||
### Class: `AesKwParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### `aesKwParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'AES-KW'`.
|
|
||||||
|
|
||||||
### Class: `EcdhKeyDeriveParams`
|
### Class: `EcdhKeyDeriveParams`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1114,7 +1151,7 @@ added: v15.0.0
|
|||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* Type: {string} Must be `'ECDH'`.
|
* Type: {string} Must be `'ECDH'`, `'X25519'`, or `'X448'`.
|
||||||
|
|
||||||
#### `ecdhKeyDeriveParams.public`
|
#### `ecdhKeyDeriveParams.public`
|
||||||
|
|
||||||
@ -1181,8 +1218,7 @@ added: v15.0.0
|
|||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* Type: {string} Must be one of `'P-256'`, `'P-384'`, `'P-521'`,
|
* Type: {string} Must be one of `'P-256'`, `'P-384'`, `'P-521'`.
|
||||||
`'NODE-ED25519'`, `'NODE-ED448'`, `'NODE-X25519'`, or `'NODE-X448'`.
|
|
||||||
|
|
||||||
### Class: `EcKeyImportParams`
|
### Class: `EcKeyImportParams`
|
||||||
|
|
||||||
@ -1204,8 +1240,34 @@ added: v15.0.0
|
|||||||
added: v15.0.0
|
added: v15.0.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
* Type: {string} Must be one of `'P-256'`, `'P-384'`, `'P-521'`,
|
* Type: {string} Must be one of `'P-256'`, `'P-384'`, `'P-521'`.
|
||||||
`'NODE-ED25519'`, `'NODE-ED448'`, `'NODE-X25519'`, or `'NODE-X448'`.
|
|
||||||
|
### Class: `Ed448Params`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: v15.0.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
#### `ed448Params.name`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {string} Must be `'Ed448'`.
|
||||||
|
|
||||||
|
#### `ed448Params.context`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
* Type: {ArrayBuffer|TypedArray|DataView|Buffer|undefined}
|
||||||
|
|
||||||
|
The `context` member represents the optional context data to associate with
|
||||||
|
the message.
|
||||||
|
The Node.js Web Crypto API implementation only supports zero-length context
|
||||||
|
which is equivalent to not providing context at all.
|
||||||
|
|
||||||
### Class: `HkdfParams`
|
### Class: `HkdfParams`
|
||||||
|
|
||||||
@ -1350,34 +1412,6 @@ added: v15.0.0
|
|||||||
|
|
||||||
* Type: {string} Must be `'HMAC'`.
|
* Type: {string} Must be `'HMAC'`.
|
||||||
|
|
||||||
### Class: `HmacParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### `hmacParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'HMAC'`.
|
|
||||||
|
|
||||||
### Class: `Pbkdf2ImportParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### `pbkdf2ImportParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'PBKDF2'`
|
|
||||||
|
|
||||||
### Class: `Pbkdf2Params`
|
### Class: `Pbkdf2Params`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1572,20 +1606,6 @@ added: v15.0.0
|
|||||||
|
|
||||||
The length (in bytes) of the random salt to use.
|
The length (in bytes) of the random salt to use.
|
||||||
|
|
||||||
### Class: `RsaSignParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### `rsaSignParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'RSASSA-PKCS1-v1_5'`
|
|
||||||
|
|
||||||
## Node.js-specific extensions
|
## Node.js-specific extensions
|
||||||
|
|
||||||
The Node.js Web Crypto API extends various aspects of the Web Crypto API.
|
The Node.js Web Crypto API extends various aspects of the Web Crypto API.
|
||||||
@ -1607,20 +1627,6 @@ added: v15.0.0
|
|||||||
The `NODE-DH` algorithm is the common implementation of Diffie-Hellman
|
The `NODE-DH` algorithm is the common implementation of Diffie-Hellman
|
||||||
key agreement.
|
key agreement.
|
||||||
|
|
||||||
#### Class: `NodeDhImportParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
##### `nodeDhImportParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'NODE-DH'`.
|
|
||||||
|
|
||||||
#### Class: `NodeDhKeyGenParams`
|
#### Class: `NodeDhKeyGenParams`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1767,85 +1773,6 @@ added: v15.0.0
|
|||||||
|
|
||||||
* Type: {string} Must be `'NODE-DSA'`.
|
* Type: {string} Must be `'NODE-DSA'`.
|
||||||
|
|
||||||
#### Class: `NodeDsaSignParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
##### `nodeDsaSignParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'NODE-DSA'`
|
|
||||||
|
|
||||||
### `NODE-ED25519` and `NODE-ED448` Algorithms
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### Class: `NodeEdKeyGenParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
##### `nodeEdKeyGenParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be one of `'NODE-ED25519'`, `'NODE-ED448'` or `'ECDH'`.
|
|
||||||
|
|
||||||
##### `nodeEdKeyGenParams.namedCurve`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be one of `'NODE-ED25519'`, `'NODE-ED448'`,
|
|
||||||
`'NODE-X25519'`, or `'NODE-X448'`.
|
|
||||||
|
|
||||||
#### Class: `NodeEdKeyImportParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
##### `nodeEdKeyImportParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be one of `'NODE-ED25519'` or `'NODE-ED448'`
|
|
||||||
if importing an `Ed25519` or `Ed448` key, or `'ECDH'` if importing
|
|
||||||
an `X25519` or `X448` key.
|
|
||||||
|
|
||||||
##### `nodeEdKeyImportParams.namedCurve`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be one of `'NODE-ED25519'`, `'NODE-ED448'`,
|
|
||||||
`'NODE-X25519'`, or `'NODE-X448'`.
|
|
||||||
|
|
||||||
##### `nodeEdKeyImportParams.public`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.8.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {boolean}
|
|
||||||
|
|
||||||
The `public` parameter is used to specify that the `'raw'` format key is to be
|
|
||||||
interpreted as a public key. **Default:** `false`.
|
|
||||||
|
|
||||||
### `NODE-SCRYPT` Algorithm
|
### `NODE-SCRYPT` Algorithm
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1855,20 +1782,6 @@ added: v15.0.0
|
|||||||
The `NODE-SCRYPT` algorithm is the common implementation of the scrypt key
|
The `NODE-SCRYPT` algorithm is the common implementation of the scrypt key
|
||||||
derivation algorithm.
|
derivation algorithm.
|
||||||
|
|
||||||
#### Class: `NodeScryptImportParams`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
##### `nodeScryptImportParams.name`
|
|
||||||
|
|
||||||
<!-- YAML
|
|
||||||
added: v15.0.0
|
|
||||||
-->
|
|
||||||
|
|
||||||
* Type: {string} Must be `'NODE-SCRYPT'`.
|
|
||||||
|
|
||||||
#### Class: `NodeScryptParams`
|
#### Class: `NodeScryptParams`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
@ -1927,8 +1840,12 @@ added: v15.0.0
|
|||||||
|
|
||||||
[^1]: Non-standard Node.js-specific extension
|
[^1]: Non-standard Node.js-specific extension
|
||||||
|
|
||||||
|
[^2]: An experimental implementation of
|
||||||
|
[Secure Curves in the Web Cryptography API][] as of 05 May 2022
|
||||||
|
|
||||||
[JSON Web Key]: https://tools.ietf.org/html/rfc7517
|
[JSON Web Key]: https://tools.ietf.org/html/rfc7517
|
||||||
[Key usages]: #cryptokeyusages
|
[Key usages]: #cryptokeyusages
|
||||||
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
[NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||||
[RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt
|
[RFC 4122]: https://www.rfc-editor.org/rfc/rfc4122.txt
|
||||||
|
[Secure Curves in the Web Cryptography API]: https://wicg.github.io/webcrypto-secure-curves/
|
||||||
[Web Crypto API]: https://www.w3.org/TR/WebCryptoAPI/
|
[Web Crypto API]: https://www.w3.org/TR/WebCryptoAPI/
|
||||||
|
369
lib/internal/crypto/cfrg.js
Normal file
369
lib/internal/crypto/cfrg.js
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {
|
||||||
|
Promise,
|
||||||
|
SafeSet,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
|
const { Buffer } = require('buffer');
|
||||||
|
|
||||||
|
const {
|
||||||
|
ECKeyExportJob,
|
||||||
|
KeyObjectHandle,
|
||||||
|
SignJob,
|
||||||
|
kCryptoJobAsync,
|
||||||
|
kKeyTypePrivate,
|
||||||
|
kKeyTypePublic,
|
||||||
|
kSignJobModeSign,
|
||||||
|
kSignJobModeVerify,
|
||||||
|
} = internalBinding('crypto');
|
||||||
|
|
||||||
|
const {
|
||||||
|
codes: {
|
||||||
|
ERR_INVALID_ARG_TYPE,
|
||||||
|
},
|
||||||
|
} = require('internal/errors');
|
||||||
|
|
||||||
|
const {
|
||||||
|
getArrayBufferOrView,
|
||||||
|
getUsagesUnion,
|
||||||
|
hasAnyNotIn,
|
||||||
|
jobPromise,
|
||||||
|
validateKeyOps,
|
||||||
|
kHandle,
|
||||||
|
kKeyObject,
|
||||||
|
} = require('internal/crypto/util');
|
||||||
|
|
||||||
|
const {
|
||||||
|
emitExperimentalWarning,
|
||||||
|
lazyDOMException,
|
||||||
|
} = require('internal/util');
|
||||||
|
|
||||||
|
const {
|
||||||
|
generateKeyPair,
|
||||||
|
} = require('internal/crypto/keygen');
|
||||||
|
|
||||||
|
const {
|
||||||
|
InternalCryptoKey,
|
||||||
|
PrivateKeyObject,
|
||||||
|
PublicKeyObject,
|
||||||
|
createPrivateKey,
|
||||||
|
createPublicKey,
|
||||||
|
isKeyObject,
|
||||||
|
} = require('internal/crypto/keys');
|
||||||
|
|
||||||
|
function verifyAcceptableCfrgKeyUse(name, type, usages) {
|
||||||
|
let checkSet;
|
||||||
|
switch (name) {
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
checkSet = ['deriveKey', 'deriveBits'];
|
||||||
|
break;
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
switch (type) {
|
||||||
|
case 'private':
|
||||||
|
checkSet = ['sign'];
|
||||||
|
break;
|
||||||
|
case 'public':
|
||||||
|
checkSet = ['verify'];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasAnyNotIn(usages, checkSet)) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`Unsupported key usage for a ${name} key`,
|
||||||
|
'SyntaxError');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createECPublicKeyRaw(name, keyData) {
|
||||||
|
const handle = new KeyObjectHandle();
|
||||||
|
keyData = getArrayBufferOrView(keyData, 'keyData');
|
||||||
|
if (handle.initECRaw(name.toLowerCase(), keyData))
|
||||||
|
return new PublicKeyObject(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCFRGRawKey(name, keyData, isPublic) {
|
||||||
|
const handle = new KeyObjectHandle();
|
||||||
|
keyData = getArrayBufferOrView(keyData, 'keyData');
|
||||||
|
|
||||||
|
switch (name) {
|
||||||
|
case 'Ed25519':
|
||||||
|
case 'X25519':
|
||||||
|
if (keyData.byteLength !== 32) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`${name} raw keys must be exactly 32-bytes`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'Ed448':
|
||||||
|
if (keyData.byteLength !== 57) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`${name} raw keys must be exactly 57-bytes`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'X448':
|
||||||
|
if (keyData.byteLength !== 56) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`${name} raw keys must be exactly 56-bytes`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
|
||||||
|
if (!handle.initEDRaw(name, keyData, keyType)) {
|
||||||
|
throw lazyDOMException('Failure to generate key object');
|
||||||
|
}
|
||||||
|
|
||||||
|
return isPublic ? new PublicKeyObject(handle) : new PrivateKeyObject(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cfrgGenerateKey(algorithm, extractable, keyUsages) {
|
||||||
|
const { name } = algorithm;
|
||||||
|
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
|
||||||
|
|
||||||
|
const usageSet = new SafeSet(keyUsages);
|
||||||
|
switch (name) {
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`Unsupported key usage for an ${name} key`,
|
||||||
|
'SyntaxError');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
if (hasAnyNotIn(usageSet, ['deriveKey', 'deriveBits'])) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
`Unsupported key usage for an ${name} key`,
|
||||||
|
'SyntaxError');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let genKeyType;
|
||||||
|
switch (name) {
|
||||||
|
case 'Ed25519':
|
||||||
|
genKeyType = 'ed25519';
|
||||||
|
break;
|
||||||
|
case 'Ed448':
|
||||||
|
genKeyType = 'ed448';
|
||||||
|
break;
|
||||||
|
case 'X25519':
|
||||||
|
genKeyType = 'x25519';
|
||||||
|
break;
|
||||||
|
case 'X448':
|
||||||
|
genKeyType = 'x448';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
generateKeyPair(genKeyType, undefined, (err, pubKey, privKey) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(lazyDOMException(
|
||||||
|
'The operation failed for an operation-specific reason',
|
||||||
|
'OperationError'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const algorithm = { name };
|
||||||
|
|
||||||
|
let publicUsages;
|
||||||
|
let privateUsages;
|
||||||
|
switch (name) {
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
publicUsages = getUsagesUnion(usageSet, 'verify');
|
||||||
|
privateUsages = getUsagesUnion(usageSet, 'sign');
|
||||||
|
break;
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
publicUsages = [];
|
||||||
|
privateUsages = getUsagesUnion(usageSet, 'deriveKey', 'deriveBits');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicKey =
|
||||||
|
new InternalCryptoKey(
|
||||||
|
pubKey,
|
||||||
|
algorithm,
|
||||||
|
publicUsages,
|
||||||
|
true);
|
||||||
|
|
||||||
|
const privateKey =
|
||||||
|
new InternalCryptoKey(
|
||||||
|
privKey,
|
||||||
|
algorithm,
|
||||||
|
privateUsages,
|
||||||
|
extractable);
|
||||||
|
|
||||||
|
resolve({ publicKey, privateKey });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cfrgExportKey(key, format) {
|
||||||
|
emitExperimentalWarning(`The ${key.algorithm.name} Web Crypto API algorithm`);
|
||||||
|
return jobPromise(new ECKeyExportJob(
|
||||||
|
kCryptoJobAsync,
|
||||||
|
format,
|
||||||
|
key[kKeyObject][kHandle]));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cfrgImportKey(
|
||||||
|
format,
|
||||||
|
keyData,
|
||||||
|
algorithm,
|
||||||
|
extractable,
|
||||||
|
keyUsages) {
|
||||||
|
|
||||||
|
const { name } = algorithm;
|
||||||
|
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
|
||||||
|
let keyObject;
|
||||||
|
const usagesSet = new SafeSet(keyUsages);
|
||||||
|
switch (format) {
|
||||||
|
case 'node.keyObject': {
|
||||||
|
if (!isKeyObject(keyData))
|
||||||
|
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
|
||||||
|
if (keyData.type === 'secret')
|
||||||
|
throw lazyDOMException('Invalid key type', 'InvalidAccessException');
|
||||||
|
verifyAcceptableCfrgKeyUse(name, keyData.type, usagesSet);
|
||||||
|
keyObject = keyData;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'spki': {
|
||||||
|
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
|
||||||
|
keyObject = createPublicKey({
|
||||||
|
key: keyData,
|
||||||
|
format: 'der',
|
||||||
|
type: 'spki'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'pkcs8': {
|
||||||
|
verifyAcceptableCfrgKeyUse(name, 'private', usagesSet);
|
||||||
|
keyObject = createPrivateKey({
|
||||||
|
key: keyData,
|
||||||
|
format: 'der',
|
||||||
|
type: 'pkcs8'
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'jwk': {
|
||||||
|
if (keyData == null || typeof keyData !== 'object')
|
||||||
|
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||||
|
if (keyData.kty !== 'OKP')
|
||||||
|
throw lazyDOMException('Invalid key type', 'DataError');
|
||||||
|
const isPublic = keyData.d === undefined;
|
||||||
|
|
||||||
|
if (usagesSet.size > 0 && keyData.use !== undefined) {
|
||||||
|
let checkUse;
|
||||||
|
switch (name) {
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
checkUse = 'sig';
|
||||||
|
break;
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
checkUse = 'enc';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (keyData.use !== checkUse)
|
||||||
|
throw lazyDOMException('Invalid use type', 'DataError');
|
||||||
|
}
|
||||||
|
|
||||||
|
validateKeyOps(keyData.key_ops, usagesSet);
|
||||||
|
|
||||||
|
if (keyData.ext !== undefined &&
|
||||||
|
keyData.ext === false &&
|
||||||
|
extractable === true) {
|
||||||
|
throw lazyDOMException('JWK is not extractable', 'DataError');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyData.alg !== undefined) {
|
||||||
|
if (typeof keyData.alg !== 'string')
|
||||||
|
throw lazyDOMException('Invalid alg', 'DataError');
|
||||||
|
if (
|
||||||
|
(name === 'Ed25519' || name === 'Ed448') &&
|
||||||
|
keyData.alg !== 'EdDSA'
|
||||||
|
) {
|
||||||
|
throw lazyDOMException('Invalid alg', 'DataError');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyAcceptableCfrgKeyUse(
|
||||||
|
name,
|
||||||
|
isPublic ? 'public' : 'private',
|
||||||
|
usagesSet);
|
||||||
|
keyObject = createCFRGRawKey(
|
||||||
|
name,
|
||||||
|
Buffer.from(
|
||||||
|
isPublic ? keyData.x : keyData.d,
|
||||||
|
'base64'),
|
||||||
|
isPublic);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'raw': {
|
||||||
|
verifyAcceptableCfrgKeyUse(name, 'public', usagesSet);
|
||||||
|
keyObject = createECPublicKeyRaw(name, keyData);
|
||||||
|
if (keyObject === undefined)
|
||||||
|
throw lazyDOMException('Unable to import CFRG key', 'OperationError');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyObject.asymmetricKeyType !== name.toLowerCase()) {
|
||||||
|
throw lazyDOMException('Invalid key type', 'DataError');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InternalCryptoKey(
|
||||||
|
keyObject,
|
||||||
|
{ name },
|
||||||
|
keyUsages,
|
||||||
|
extractable);
|
||||||
|
}
|
||||||
|
|
||||||
|
function eddsaSignVerify(key, data, { name, context }, signature) {
|
||||||
|
emitExperimentalWarning(`The ${name} Web Crypto API algorithm`);
|
||||||
|
const mode = signature === undefined ? kSignJobModeSign : kSignJobModeVerify;
|
||||||
|
const type = mode === kSignJobModeSign ? 'private' : 'public';
|
||||||
|
|
||||||
|
if (key.type !== type)
|
||||||
|
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
|
||||||
|
|
||||||
|
if (name === 'Ed448' && context !== undefined) {
|
||||||
|
context =
|
||||||
|
getArrayBufferOrView(context, 'algorithm.context');
|
||||||
|
if (context.byteLength !== 0) {
|
||||||
|
throw lazyDOMException(
|
||||||
|
'Non zero-length context is not yet supported.', 'NotSupportedError');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jobPromise(new SignJob(
|
||||||
|
kCryptoJobAsync,
|
||||||
|
mode,
|
||||||
|
key[kKeyObject][kHandle],
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
data,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
cfrgExportKey,
|
||||||
|
cfrgImportKey,
|
||||||
|
cfrgGenerateKey,
|
||||||
|
eddsaSignVerify,
|
||||||
|
};
|
@ -447,8 +447,12 @@ async function asyncDeriveBitsECDH(algorithm, baseKey, length) {
|
|||||||
'baseKey must be a private key', 'InvalidAccessError');
|
'baseKey must be a private key', 'InvalidAccessError');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.algorithm.name !== 'ECDH') {
|
if (
|
||||||
throw lazyDOMException('Keys must be ECDH keys', 'InvalidAccessError');
|
key.algorithm.name !== 'ECDH' &&
|
||||||
|
key.algorithm.name !== 'X25519' &&
|
||||||
|
key.algorithm.name !== 'X448'
|
||||||
|
) {
|
||||||
|
throw lazyDOMException('Keys must be ECDH, X25519, or X448 keys', 'InvalidAccessError');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.algorithm.name !== baseKey.algorithm.name) {
|
if (key.algorithm.name !== baseKey.algorithm.name) {
|
||||||
@ -457,12 +461,16 @@ async function asyncDeriveBitsECDH(algorithm, baseKey, length) {
|
|||||||
'InvalidAccessError');
|
'InvalidAccessError');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key.algorithm.namedCurve !== baseKey.algorithm.namedCurve)
|
if (
|
||||||
|
key.algorithm.name === 'ECDH' &&
|
||||||
|
key.algorithm.namedCurve !== baseKey.algorithm.namedCurve
|
||||||
|
) {
|
||||||
throw lazyDOMException('Named curve mismatch', 'InvalidAccessError');
|
throw lazyDOMException('Named curve mismatch', 'InvalidAccessError');
|
||||||
|
}
|
||||||
|
|
||||||
const bits = await new Promise((resolve, reject) => {
|
const bits = await new Promise((resolve, reject) => {
|
||||||
deriveBitsECDH(
|
deriveBitsECDH(
|
||||||
baseKey.algorithm.namedCurve,
|
key.algorithm.name === 'ECDH' ? baseKey.algorithm.namedCurve : baseKey.algorithm.name,
|
||||||
key[kKeyObject][kHandle],
|
key[kKeyObject][kHandle],
|
||||||
baseKey[kKeyObject][kHandle], (err, bits) => {
|
baseKey[kKeyObject][kHandle], (err, bits) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
@ -6,22 +6,18 @@ const {
|
|||||||
SafeSet,
|
SafeSet,
|
||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
const { Buffer } = require('buffer');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ECKeyExportJob,
|
ECKeyExportJob,
|
||||||
KeyObjectHandle,
|
KeyObjectHandle,
|
||||||
SignJob,
|
SignJob,
|
||||||
kCryptoJobAsync,
|
kCryptoJobAsync,
|
||||||
kKeyTypePrivate,
|
kKeyTypePrivate,
|
||||||
kKeyTypePublic,
|
|
||||||
kSignJobModeSign,
|
kSignJobModeSign,
|
||||||
kSignJobModeVerify,
|
kSignJobModeVerify,
|
||||||
kSigEncP1363,
|
kSigEncP1363,
|
||||||
} = internalBinding('crypto');
|
} = internalBinding('crypto');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
validateBoolean,
|
|
||||||
validateOneOf,
|
validateOneOf,
|
||||||
validateString,
|
validateString,
|
||||||
} = require('internal/validators');
|
} = require('internal/validators');
|
||||||
@ -68,10 +64,6 @@ function verifyAcceptableEcKeyUse(name, type, usages) {
|
|||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
checkSet = ['deriveKey', 'deriveBits'];
|
checkSet = ['deriveKey', 'deriveBits'];
|
||||||
break;
|
break;
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
// Fall through
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'private':
|
case 'private':
|
||||||
@ -96,40 +88,6 @@ function createECPublicKeyRaw(namedCurve, keyData) {
|
|||||||
return new PublicKeyObject(handle);
|
return new PublicKeyObject(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createECRawKey(namedCurve, keyData, isPublic) {
|
|
||||||
const handle = new KeyObjectHandle();
|
|
||||||
keyData = getArrayBufferOrView(keyData, 'keyData');
|
|
||||||
|
|
||||||
switch (namedCurve) {
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
case 'NODE-X25519':
|
|
||||||
if (keyData.byteLength !== 32) {
|
|
||||||
throw lazyDOMException(
|
|
||||||
`${namedCurve} raw keys must be exactly 32-bytes`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (keyData.byteLength !== 57) {
|
|
||||||
throw lazyDOMException(
|
|
||||||
`${namedCurve} raw keys must be exactly 57-bytes`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'NODE-X448':
|
|
||||||
if (keyData.byteLength !== 56) {
|
|
||||||
throw lazyDOMException(
|
|
||||||
`${namedCurve} raw keys must be exactly 56-bytes`);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
|
|
||||||
if (!handle.initEDRaw(namedCurve, keyData, keyType)) {
|
|
||||||
throw lazyDOMException('Failure to generate key object');
|
|
||||||
}
|
|
||||||
|
|
||||||
return isPublic ? new PublicKeyObject(handle) : new PrivateKeyObject(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
||||||
const { name, namedCurve } = algorithm;
|
const { name, namedCurve } = algorithm;
|
||||||
validateString(namedCurve, 'algorithm.namedCurve');
|
validateString(namedCurve, 'algorithm.namedCurve');
|
||||||
@ -141,16 +99,6 @@ async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
|||||||
const usageSet = new SafeSet(keyUsages);
|
const usageSet = new SafeSet(keyUsages);
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
if (namedCurve === 'NODE-ED25519' ||
|
|
||||||
namedCurve === 'NODE-ED448' ||
|
|
||||||
namedCurve === 'NODE-X25519' ||
|
|
||||||
namedCurve === 'NODE-X448') {
|
|
||||||
throw lazyDOMException('Unsupported named curves for ECDSA');
|
|
||||||
}
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
if (hasAnyNotIn(usageSet, ['sign', 'verify'])) {
|
||||||
throw lazyDOMException(
|
throw lazyDOMException(
|
||||||
'Unsupported key usage for an ECDSA key',
|
'Unsupported key usage for an ECDSA key',
|
||||||
@ -163,33 +111,10 @@ async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
|||||||
'Unsupported key usage for an ECDH key',
|
'Unsupported key usage for an ECDH key',
|
||||||
'SyntaxError');
|
'SyntaxError');
|
||||||
}
|
}
|
||||||
if (namedCurve === 'NODE-ED25519' || namedCurve === 'NODE-ED448') {
|
|
||||||
throw lazyDOMException('Unsupported named curves for ECDH');
|
|
||||||
}
|
|
||||||
// Fall through
|
// Fall through
|
||||||
}
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let genKeyType;
|
generateKeyPair('ec', { namedCurve }, (err, pubKey, privKey) => {
|
||||||
let genOpts;
|
|
||||||
switch (namedCurve) {
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
genKeyType = 'ed25519';
|
|
||||||
break;
|
|
||||||
case 'NODE-ED448':
|
|
||||||
genKeyType = 'ed448';
|
|
||||||
break;
|
|
||||||
case 'NODE-X25519':
|
|
||||||
genKeyType = 'x25519';
|
|
||||||
break;
|
|
||||||
case 'NODE-X448':
|
|
||||||
genKeyType = 'x448';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
genKeyType = 'ec';
|
|
||||||
genOpts = { namedCurve };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
generateKeyPair(genKeyType, genOpts, (err, pubKey, privKey) => {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return reject(lazyDOMException(
|
return reject(lazyDOMException(
|
||||||
'The operation failed for an operation-specific reason',
|
'The operation failed for an operation-specific reason',
|
||||||
@ -201,10 +126,6 @@ async function ecGenerateKey(algorithm, extractable, keyUsages) {
|
|||||||
let publicUsages;
|
let publicUsages;
|
||||||
let privateUsages;
|
let privateUsages;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
// Fall through
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
publicUsages = getUsagesUnion(usageSet, 'verify');
|
publicUsages = getUsagesUnion(usageSet, 'verify');
|
||||||
privateUsages = getUsagesUnion(usageSet, 'sign');
|
privateUsages = getUsagesUnion(usageSet, 'sign');
|
||||||
@ -256,29 +177,12 @@ async function ecImportKey(
|
|||||||
ObjectKeys(kNamedCurveAliases));
|
ObjectKeys(kNamedCurveAliases));
|
||||||
let keyObject;
|
let keyObject;
|
||||||
const usagesSet = new SafeSet(keyUsages);
|
const usagesSet = new SafeSet(keyUsages);
|
||||||
let checkNamedCurve = true;
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case 'node.keyObject': {
|
case 'node.keyObject': {
|
||||||
if (!isKeyObject(keyData))
|
if (!isKeyObject(keyData))
|
||||||
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
|
throw new ERR_INVALID_ARG_TYPE('keyData', 'KeyObject', keyData);
|
||||||
if (keyData.type === 'secret')
|
if (keyData.type === 'secret')
|
||||||
throw lazyDOMException('Invalid key type', 'InvalidAccessException');
|
throw lazyDOMException('Invalid key type', 'InvalidAccessException');
|
||||||
|
|
||||||
switch (namedCurve) {
|
|
||||||
case 'NODE-X25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-X448':
|
|
||||||
if (algorithm.name !== 'ECDH')
|
|
||||||
throw lazyDOMException('Invalid algorithm name.', 'DataError');
|
|
||||||
break;
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (algorithm.name !== namedCurve)
|
|
||||||
throw lazyDOMException('Invalid algorithm name.', 'DataError');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyAcceptableEcKeyUse(name, keyData.type, usagesSet);
|
verifyAcceptableEcKeyUse(name, keyData.type, usagesSet);
|
||||||
keyObject = keyData;
|
keyObject = keyData;
|
||||||
break;
|
break;
|
||||||
@ -305,40 +209,6 @@ async function ecImportKey(
|
|||||||
let curve;
|
let curve;
|
||||||
if (keyData == null || typeof keyData !== 'object')
|
if (keyData == null || typeof keyData !== 'object')
|
||||||
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
throw lazyDOMException('Invalid JWK keyData', 'DataError');
|
||||||
switch (keyData.kty) {
|
|
||||||
case 'OKP': {
|
|
||||||
const isPublic = keyData.d === undefined;
|
|
||||||
|
|
||||||
let type;
|
|
||||||
switch (namedCurve) {
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
type = `NODE-${keyData.crv.toUpperCase()}`;
|
|
||||||
break;
|
|
||||||
case 'NODE-X25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-X448':
|
|
||||||
type = 'ECDH';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (algorithm.name !== type)
|
|
||||||
throw lazyDOMException('Invalid algorithm name.', 'DataError');
|
|
||||||
|
|
||||||
verifyAcceptableEcKeyUse(
|
|
||||||
type,
|
|
||||||
isPublic ? 'public' : 'private',
|
|
||||||
usagesSet);
|
|
||||||
keyObject = createECRawKey(
|
|
||||||
namedCurve,
|
|
||||||
Buffer.from(
|
|
||||||
isPublic ? keyData.x : keyData.d,
|
|
||||||
'base64'),
|
|
||||||
isPublic);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
if (keyData.kty !== 'EC')
|
if (keyData.kty !== 'EC')
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
throw lazyDOMException('Invalid key type', 'DataError');
|
||||||
|
|
||||||
@ -382,42 +252,11 @@ async function ecImportKey(
|
|||||||
keyObject = type === kKeyTypePrivate ?
|
keyObject = type === kKeyTypePrivate ?
|
||||||
new PrivateKeyObject(handle) :
|
new PrivateKeyObject(handle) :
|
||||||
new PublicKeyObject(handle);
|
new PublicKeyObject(handle);
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'raw': {
|
case 'raw': {
|
||||||
switch (namedCurve) {
|
|
||||||
case 'NODE-X25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-X448':
|
|
||||||
if (algorithm.public !== undefined)
|
|
||||||
validateBoolean(algorithm.public, 'algorithm.public');
|
|
||||||
if (algorithm.name !== 'ECDH')
|
|
||||||
throw lazyDOMException('Invalid algorithm name.', 'DataError');
|
|
||||||
verifyAcceptableEcKeyUse(
|
|
||||||
algorithm.name,
|
|
||||||
algorithm.public === true ? 'public' : 'private',
|
|
||||||
usagesSet);
|
|
||||||
keyObject = createECRawKey(namedCurve, keyData, algorithm.public);
|
|
||||||
break;
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (algorithm.public !== undefined)
|
|
||||||
validateBoolean(algorithm.public, 'algorithm.public');
|
|
||||||
if (algorithm.name !== namedCurve)
|
|
||||||
throw lazyDOMException('Invalid algorithm name.', 'DataError');
|
|
||||||
verifyAcceptableEcKeyUse(
|
|
||||||
algorithm.name,
|
|
||||||
algorithm.public === true ? 'public' : 'private',
|
|
||||||
usagesSet);
|
|
||||||
keyObject = createECRawKey(namedCurve, keyData, algorithm.public);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
|
verifyAcceptableEcKeyUse(name, 'public', usagesSet);
|
||||||
keyObject = createECPublicKeyRaw(namedCurve, keyData);
|
keyObject = createECPublicKeyRaw(namedCurve, keyData);
|
||||||
}
|
|
||||||
if (keyObject === undefined)
|
if (keyObject === undefined)
|
||||||
throw lazyDOMException('Unable to import EC key', 'OperationError');
|
throw lazyDOMException('Unable to import EC key', 'OperationError');
|
||||||
break;
|
break;
|
||||||
@ -426,41 +265,18 @@ async function ecImportKey(
|
|||||||
|
|
||||||
switch (algorithm.name) {
|
switch (algorithm.name) {
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
|
// Fall through
|
||||||
|
case 'ECDH':
|
||||||
if (keyObject.asymmetricKeyType !== 'ec')
|
if (keyObject.asymmetricKeyType !== 'ec')
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
throw lazyDOMException('Invalid key type', 'DataError');
|
||||||
break;
|
break;
|
||||||
case 'ECDH':
|
|
||||||
if (algorithm.namedCurve === 'NODE-X25519') {
|
|
||||||
if (keyObject.asymmetricKeyType !== 'x25519')
|
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
|
||||||
checkNamedCurve = false;
|
|
||||||
} else if (algorithm.namedCurve === 'NODE-X448') {
|
|
||||||
if (keyObject.asymmetricKeyType !== 'x448')
|
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
|
||||||
checkNamedCurve = false;
|
|
||||||
} else if (keyObject.asymmetricKeyType !== 'ec') {
|
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
if (keyObject.asymmetricKeyType !== 'ed25519')
|
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
|
||||||
checkNamedCurve = false;
|
|
||||||
break;
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (keyObject.asymmetricKeyType !== 'ed448')
|
|
||||||
throw lazyDOMException('Invalid key type', 'DataError');
|
|
||||||
checkNamedCurve = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkNamedCurve) {
|
|
||||||
const {
|
const {
|
||||||
namedCurve: checkNamedCurve
|
namedCurve: checkNamedCurve
|
||||||
} = keyObject[kHandle].keyDetail({});
|
} = keyObject[kHandle].keyDetail({});
|
||||||
if (kNamedCurveAliases[namedCurve] !== checkNamedCurve)
|
if (kNamedCurveAliases[namedCurve] !== checkNamedCurve)
|
||||||
throw lazyDOMException('Named curve mismatch', 'DataError');
|
throw lazyDOMException('Named curve mismatch', 'DataError');
|
||||||
}
|
|
||||||
|
|
||||||
return new InternalCryptoKey(
|
return new InternalCryptoKey(
|
||||||
keyObject,
|
keyObject,
|
||||||
@ -476,19 +292,9 @@ function ecdsaSignVerify(key, data, { name, hash }, signature) {
|
|||||||
if (key.type !== type)
|
if (key.type !== type)
|
||||||
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
|
throw lazyDOMException(`Key must be a ${type} key`, 'InvalidAccessError');
|
||||||
|
|
||||||
let hashname;
|
|
||||||
switch (name) {
|
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (hash !== undefined)
|
|
||||||
throw lazyDOMException(`Hash is not permitted for ${name}`);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (hash === undefined)
|
if (hash === undefined)
|
||||||
throw new ERR_MISSING_OPTION('algorithm.hash');
|
throw new ERR_MISSING_OPTION('algorithm.hash');
|
||||||
hashname = normalizeHashName(hash.name);
|
const hashname = normalizeHashName(hash.name);
|
||||||
}
|
|
||||||
|
|
||||||
return jobPromise(new SignJob(
|
return jobPromise(new SignJob(
|
||||||
kCryptoJobAsync,
|
kCryptoJobAsync,
|
||||||
|
@ -448,7 +448,7 @@ function getKeyObjectHandleFromJwk(key, ctx) {
|
|||||||
const handle = new KeyObjectHandle();
|
const handle = new KeyObjectHandle();
|
||||||
|
|
||||||
const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
|
const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
|
||||||
if (!handle.initEDRaw(`NODE-${key.crv.toUpperCase()}`, keyData, keyType)) {
|
if (!handle.initEDRaw(key.crv, keyData, keyType)) {
|
||||||
throw new ERR_CRYPTO_INVALID_JWK();
|
throw new ERR_CRYPTO_INVALID_JWK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,10 +143,6 @@ const kNamedCurveAliases = {
|
|||||||
'P-256': 'prime256v1',
|
'P-256': 'prime256v1',
|
||||||
'P-384': 'secp384r1',
|
'P-384': 'secp384r1',
|
||||||
'P-521': 'secp521r1',
|
'P-521': 'secp521r1',
|
||||||
'NODE-ED25519': 'ed25519',
|
|
||||||
'NODE-ED448': 'ed448',
|
|
||||||
'NODE-X25519': 'x25519',
|
|
||||||
'NODE-X448': 'x448',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const kAesKeyLengths = [128, 192, 256];
|
const kAesKeyLengths = [128, 192, 256];
|
||||||
@ -170,13 +166,15 @@ const kAlgorithms = {
|
|||||||
'sha-512': 'SHA-512',
|
'sha-512': 'SHA-512',
|
||||||
'hkdf': 'HKDF',
|
'hkdf': 'HKDF',
|
||||||
'pbkdf2': 'PBKDF2',
|
'pbkdf2': 'PBKDF2',
|
||||||
|
'ed25519': 'Ed25519',
|
||||||
|
'ed448': 'Ed448',
|
||||||
|
'x25519': 'X25519',
|
||||||
|
'x448': 'X448',
|
||||||
// Following here are Node.js specific extensions. All
|
// Following here are Node.js specific extensions. All
|
||||||
// should be prefixed with 'node-'
|
// should be prefixed with 'node-'
|
||||||
'node-dsa': 'NODE-DSA',
|
'node-dsa': 'NODE-DSA',
|
||||||
'node-dh': 'NODE-DH',
|
'node-dh': 'NODE-DH',
|
||||||
'node-scrypt': 'NODE-SCRYPT',
|
'node-scrypt': 'NODE-SCRYPT',
|
||||||
'node-ed25519': 'NODE-ED25519',
|
|
||||||
'node-ed448': 'NODE-ED448',
|
|
||||||
};
|
};
|
||||||
const kAlgorithmsKeys = ObjectKeys(kAlgorithms);
|
const kAlgorithmsKeys = ObjectKeys(kAlgorithms);
|
||||||
|
|
||||||
|
@ -98,10 +98,15 @@ async function generateKey(
|
|||||||
case 'RSA-OAEP':
|
case 'RSA-OAEP':
|
||||||
return lazyRequire('internal/crypto/rsa')
|
return lazyRequire('internal/crypto/rsa')
|
||||||
.rsaKeyGenerate(algorithm, extractable, keyUsages);
|
.rsaKeyGenerate(algorithm, extractable, keyUsages);
|
||||||
case 'NODE-ED25519':
|
case 'Ed25519':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'NODE-ED448':
|
case 'Ed448':
|
||||||
// Fall through
|
// Fall through
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
return lazyRequire('internal/crypto/cfrg')
|
||||||
|
.cfrgGenerateKey(algorithm, extractable, keyUsages);
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
@ -146,6 +151,10 @@ async function deriveBits(algorithm, baseKey, length) {
|
|||||||
if (baseKey.algorithm.name !== algorithm.name)
|
if (baseKey.algorithm.name !== algorithm.name)
|
||||||
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
|
throw lazyDOMException('Key algorithm mismatch', 'InvalidAccessError');
|
||||||
switch (algorithm.name) {
|
switch (algorithm.name) {
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
return lazyRequire('internal/crypto/diffiehellman')
|
return lazyRequire('internal/crypto/diffiehellman')
|
||||||
.asyncDeriveBitsECDH(algorithm, baseKey, length);
|
.asyncDeriveBitsECDH(algorithm, baseKey, length);
|
||||||
@ -227,6 +236,10 @@ async function deriveKey(
|
|||||||
const length = getKeyLength(derivedKeyAlgorithm);
|
const length = getKeyLength(derivedKeyAlgorithm);
|
||||||
let bits;
|
let bits;
|
||||||
switch (algorithm.name) {
|
switch (algorithm.name) {
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
bits = await lazyRequire('internal/crypto/diffiehellman')
|
bits = await lazyRequire('internal/crypto/diffiehellman')
|
||||||
.asyncDeriveBitsECDH(algorithm, baseKey, length);
|
.asyncDeriveBitsECDH(algorithm, baseKey, length);
|
||||||
@ -270,10 +283,6 @@ async function exportKeySpki(key) {
|
|||||||
.rsaExportKey(key, kWebCryptoKeyFormatSPKI);
|
.rsaExportKey(key, kWebCryptoKeyFormatSPKI);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
// Fall through
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
@ -282,6 +291,18 @@ async function exportKeySpki(key) {
|
|||||||
.ecExportKey(key, kWebCryptoKeyFormatSPKI);
|
.ecExportKey(key, kWebCryptoKeyFormatSPKI);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
// Fall through
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
if (key.type === 'public') {
|
||||||
|
return lazyRequire('internal/crypto/cfrg')
|
||||||
|
.cfrgExportKey(key, kWebCryptoKeyFormatSPKI);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'NODE-DSA':
|
case 'NODE-DSA':
|
||||||
if (key.type === 'public') {
|
if (key.type === 'public') {
|
||||||
return lazyRequire('internal/crypto/dsa')
|
return lazyRequire('internal/crypto/dsa')
|
||||||
@ -313,10 +334,6 @@ async function exportKeyPkcs8(key) {
|
|||||||
.rsaExportKey(key, kWebCryptoKeyFormatPKCS8);
|
.rsaExportKey(key, kWebCryptoKeyFormatPKCS8);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
// Fall through
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
@ -325,6 +342,18 @@ async function exportKeyPkcs8(key) {
|
|||||||
.ecExportKey(key, kWebCryptoKeyFormatPKCS8);
|
.ecExportKey(key, kWebCryptoKeyFormatPKCS8);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
// Fall through
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
if (key.type === 'private') {
|
||||||
|
return lazyRequire('internal/crypto/cfrg')
|
||||||
|
.cfrgExportKey(key, kWebCryptoKeyFormatPKCS8);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'NODE-DSA':
|
case 'NODE-DSA':
|
||||||
if (key.type === 'private') {
|
if (key.type === 'private') {
|
||||||
return lazyRequire('internal/crypto/dsa')
|
return lazyRequire('internal/crypto/dsa')
|
||||||
@ -346,14 +375,6 @@ async function exportKeyPkcs8(key) {
|
|||||||
|
|
||||||
async function exportKeyRaw(key) {
|
async function exportKeyRaw(key) {
|
||||||
switch (key.algorithm.name) {
|
switch (key.algorithm.name) {
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
if (key.type === 'public') {
|
|
||||||
return lazyRequire('internal/crypto/ec')
|
|
||||||
.ecExportKey(key, kWebCryptoKeyFormatRaw);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
@ -362,6 +383,18 @@ async function exportKeyRaw(key) {
|
|||||||
.ecExportKey(key, kWebCryptoKeyFormatRaw);
|
.ecExportKey(key, kWebCryptoKeyFormatRaw);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
// Fall through
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
if (key.type === 'public') {
|
||||||
|
return lazyRequire('internal/crypto/cfrg')
|
||||||
|
.cfrgExportKey(key, kWebCryptoKeyFormatRaw);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'AES-CTR':
|
case 'AES-CTR':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'AES-CBC':
|
case 'AES-CBC':
|
||||||
@ -405,6 +438,17 @@ async function exportKeyJWK(key) {
|
|||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
jwk.crv ||= key.algorithm.namedCurve;
|
jwk.crv ||= key.algorithm.namedCurve;
|
||||||
return jwk;
|
return jwk;
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
jwk.crv ||= key.algorithm.name;
|
||||||
|
return jwk;
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
jwk.crv ||= key.algorithm.name;
|
||||||
|
jwk.alg = 'EdDSA';
|
||||||
|
return jwk;
|
||||||
case 'AES-CTR':
|
case 'AES-CTR':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'AES-CBC':
|
case 'AES-CBC':
|
||||||
@ -420,10 +464,6 @@ async function exportKeyJWK(key) {
|
|||||||
key.algorithm.hash.name,
|
key.algorithm.hash.name,
|
||||||
normalizeHashName.kContextJwkHmac);
|
normalizeHashName.kContextJwkHmac);
|
||||||
return jwk;
|
return jwk;
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
return jwk;
|
|
||||||
default:
|
default:
|
||||||
// Fall through
|
// Fall through
|
||||||
}
|
}
|
||||||
@ -530,15 +570,20 @@ async function importKey(
|
|||||||
case 'RSA-OAEP':
|
case 'RSA-OAEP':
|
||||||
return lazyRequire('internal/crypto/rsa')
|
return lazyRequire('internal/crypto/rsa')
|
||||||
.rsaImportKey(format, keyData, algorithm, extractable, keyUsages);
|
.rsaImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
// Fall through
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
// Fall through
|
// Fall through
|
||||||
case 'ECDH':
|
case 'ECDH':
|
||||||
return lazyRequire('internal/crypto/ec')
|
return lazyRequire('internal/crypto/ec')
|
||||||
.ecImportKey(format, keyData, algorithm, extractable, keyUsages);
|
.ecImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
// Fall through
|
||||||
|
case 'X25519':
|
||||||
|
// Fall through
|
||||||
|
case 'X448':
|
||||||
|
return lazyRequire('internal/crypto/cfrg')
|
||||||
|
.cfrgImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||||
case 'HMAC':
|
case 'HMAC':
|
||||||
return lazyRequire('internal/crypto/mac')
|
return lazyRequire('internal/crypto/mac')
|
||||||
.hmacImportKey(format, keyData, algorithm, extractable, keyUsages);
|
.hmacImportKey(format, keyData, algorithm, extractable, keyUsages);
|
||||||
@ -659,13 +704,15 @@ function signVerify(algorithm, key, data, signature) {
|
|||||||
case 'RSASSA-PKCS1-v1_5':
|
case 'RSASSA-PKCS1-v1_5':
|
||||||
return lazyRequire('internal/crypto/rsa')
|
return lazyRequire('internal/crypto/rsa')
|
||||||
.rsaSignVerify(key, data, algorithm, signature);
|
.rsaSignVerify(key, data, algorithm, signature);
|
||||||
case 'NODE-ED25519':
|
|
||||||
// Fall through
|
|
||||||
case 'NODE-ED448':
|
|
||||||
// Fall through
|
|
||||||
case 'ECDSA':
|
case 'ECDSA':
|
||||||
return lazyRequire('internal/crypto/ec')
|
return lazyRequire('internal/crypto/ec')
|
||||||
.ecdsaSignVerify(key, data, algorithm, signature);
|
.ecdsaSignVerify(key, data, algorithm, signature);
|
||||||
|
case 'Ed25519':
|
||||||
|
// Fall through
|
||||||
|
case 'Ed448':
|
||||||
|
// Fall through
|
||||||
|
return lazyRequire('internal/crypto/cfrg')
|
||||||
|
.eddsaSignVerify(key, data, algorithm, signature);
|
||||||
case 'HMAC':
|
case 'HMAC':
|
||||||
return lazyRequire('internal/crypto/mac')
|
return lazyRequire('internal/crypto/mac')
|
||||||
.hmacSignVerify(key, data, algorithm, signature);
|
.hmacSignVerify(key, data, algorithm, signature);
|
||||||
|
@ -45,13 +45,13 @@ int GetCurveFromName(const char* name) {
|
|||||||
|
|
||||||
int GetOKPCurveFromName(const char* name) {
|
int GetOKPCurveFromName(const char* name) {
|
||||||
int nid;
|
int nid;
|
||||||
if (strcmp(name, "NODE-ED25519") == 0) {
|
if (strcmp(name, "Ed25519") == 0) {
|
||||||
nid = EVP_PKEY_ED25519;
|
nid = EVP_PKEY_ED25519;
|
||||||
} else if (strcmp(name, "NODE-ED448") == 0) {
|
} else if (strcmp(name, "Ed448") == 0) {
|
||||||
nid = EVP_PKEY_ED448;
|
nid = EVP_PKEY_ED448;
|
||||||
} else if (strcmp(name, "NODE-X25519") == 0) {
|
} else if (strcmp(name, "X25519") == 0) {
|
||||||
nid = EVP_PKEY_X25519;
|
nid = EVP_PKEY_X25519;
|
||||||
} else if (strcmp(name, "NODE-X448") == 0) {
|
} else if (strcmp(name, "X448") == 0) {
|
||||||
nid = EVP_PKEY_X448;
|
nid = EVP_PKEY_X448;
|
||||||
} else {
|
} else {
|
||||||
nid = NID_undef;
|
nid = NID_undef;
|
||||||
|
51
test/fixtures/crypto/eddsa.js
vendored
Normal file
51
test/fixtures/crypto/eddsa.js
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function() {
|
||||||
|
const pkcs8 = {
|
||||||
|
'Ed25519': Buffer.from(
|
||||||
|
'302e020100300506032b657004220420f3c8f4c48df878146e8cd3bf6df4e50e389b' +
|
||||||
|
'a7074e15c2352dcd5d308d4ca81f', 'hex'),
|
||||||
|
'Ed448': Buffer.from(
|
||||||
|
'3047020100300506032b6571043b04390eff03458c28e0179c521de312c969b78343' +
|
||||||
|
'48ecab991a60e3b2e9a79e4cd9e480ef291712d2c83d047272d5c9f428664f696d26' +
|
||||||
|
'70458f1d2e', 'hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
const spki = {
|
||||||
|
'Ed25519': Buffer.from(
|
||||||
|
'302a300506032b6570032100d8e18963d809d487d9549accaec6742e7eeba24d8a0d' +
|
||||||
|
'3b14b7e3caea06893dcc', 'hex'),
|
||||||
|
'Ed448': Buffer.from(
|
||||||
|
'3043300506032b6571033a00ab4bb885fd7d2c5af24e83710cffa0c74a57e274801d' +
|
||||||
|
'b2057b0bdc5ea032b6fe6bc78b8045365aeb26e86e1f14fd349d07c48495f5a46a5a' +
|
||||||
|
'80', 'hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = Buffer.from(
|
||||||
|
'2b7ed0bc7795694ab4acd35903fe8cd7d80f6a1c8688a6c3414409457514a1457855bb' +
|
||||||
|
'b219e30a1beea8fe869082d99fc8282f9050d024e59eaf0730ba9db70a', 'hex');
|
||||||
|
|
||||||
|
// For verification tests.
|
||||||
|
const signatures = {
|
||||||
|
'Ed25519': Buffer.from(
|
||||||
|
'3d90de5e5743dfc28225bfadb341b116cbf8a3f1ceedbf4adc350ef5d3471843a418' +
|
||||||
|
'614dcb6e614862614cf7af1496f9340b3c844ea4dceab1d3d155eb7ecc00', 'hex'),
|
||||||
|
'Ed448': Buffer.from(
|
||||||
|
'76897e8c50ac6b1132735c09c55f506c0149d2677c75664f8bc10b826fbd9df0a03c' +
|
||||||
|
'd986bce8339e64c7d1720ea9361784dc73837765ac2980c0dac0814a8bc187d1c9c9' +
|
||||||
|
'07c5dcc07956f85b70930fe42de764177217cb2d52bab7c1debe0ca89ccecbcd63f7' +
|
||||||
|
'025a2a5a572b9d23b0642f00', 'hex')
|
||||||
|
}
|
||||||
|
|
||||||
|
const algorithms = ['Ed25519', 'Ed448'];
|
||||||
|
|
||||||
|
const vectors = algorithms.map((algorithm) => ({
|
||||||
|
publicKeyBuffer: spki[algorithm],
|
||||||
|
privateKeyBuffer: pkcs8[algorithm],
|
||||||
|
name: algorithm,
|
||||||
|
data,
|
||||||
|
signature: signatures[algorithm],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return vectors;
|
||||||
|
}
|
@ -1715,22 +1715,6 @@ generateKeyPair('rsa', {
|
|||||||
assert.strictEqual(typeof privateKey, 'string');
|
assert.strictEqual(typeof privateKey, 'string');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
{
|
|
||||||
// Proprietary Web Cryptography API ECDH/ECDSA namedCurve parameters
|
|
||||||
// should not be recognized in this API.
|
|
||||||
// See https://github.com/nodejs/node/issues/37055
|
|
||||||
const curves = ['NODE-ED25519', 'NODE-ED448', 'NODE-X25519', 'NODE-X448'];
|
|
||||||
for (const namedCurve of curves) {
|
|
||||||
assert.throws(
|
|
||||||
() => generateKeyPair('ec', { namedCurve }, common.mustNotCall()),
|
|
||||||
{
|
|
||||||
name: 'TypeError',
|
|
||||||
message: 'Invalid EC curve name'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// This test creates EC key pairs on curves without associated OIDs.
|
// This test creates EC key pairs on curves without associated OIDs.
|
||||||
// Specifying a key encoding should not crash.
|
// Specifying a key encoding should not crash.
|
||||||
|
213
test/parallel/test-webcrypto-derivebits-cfrg.js
Normal file
213
test/parallel/test-webcrypto-derivebits-cfrg.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { webcrypto } = require('crypto');
|
||||||
|
const { subtle } = webcrypto;
|
||||||
|
|
||||||
|
const kTests = [
|
||||||
|
{
|
||||||
|
name: 'X25519',
|
||||||
|
size: 32,
|
||||||
|
pkcs8: '302e020100300506032b656e04220420c8838e76d057dfb7d8c95a69e138160ad' +
|
||||||
|
'd6373fd71a4d276bb56e3a81b64ff61',
|
||||||
|
spki: '302a300506032b656e0321001cf2b1e6022ec537371ed7f53e54fa1154d83e98eb' +
|
||||||
|
'64ea51fae5b3307cfe9706',
|
||||||
|
result: '2768409dfab99ec23b8c89b93ff5880295f76176088f89e43dfebe7ea1950008'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
size: 56,
|
||||||
|
pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
|
||||||
|
'd8a42d8f17176272b89f2272d1819295c6525c0829671b052ef0727530f188e31' +
|
||||||
|
'd0cc53bf26929e',
|
||||||
|
spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
|
||||||
|
'ca615edc53633efb52ea31e6e6a0a1dbacc6e76cbce6482d7e4ba3d55d9e802765' +
|
||||||
|
'ce6f',
|
||||||
|
result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b' +
|
||||||
|
'726e0f6dcf810eb594dca97b4882bd44c43ea7dc67f49a4e',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function prepareKeys() {
|
||||||
|
const keys = {};
|
||||||
|
await Promise.all(
|
||||||
|
kTests.map(async ({ name, size, pkcs8, spki, result }) => {
|
||||||
|
const [
|
||||||
|
privateKey,
|
||||||
|
publicKey,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
Buffer.from(pkcs8, 'hex'),
|
||||||
|
{ name },
|
||||||
|
true,
|
||||||
|
['deriveKey', 'deriveBits']),
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
Buffer.from(spki, 'hex'),
|
||||||
|
{ name },
|
||||||
|
true,
|
||||||
|
['deriveKey', 'deriveBits']),
|
||||||
|
]);
|
||||||
|
keys[name] = {
|
||||||
|
privateKey,
|
||||||
|
publicKey,
|
||||||
|
size,
|
||||||
|
result,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const keys = await prepareKeys();
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.keys(keys).map(async (name) => {
|
||||||
|
const { size, result, privateKey, publicKey } = keys[name];
|
||||||
|
|
||||||
|
{
|
||||||
|
// Good parameters
|
||||||
|
const bits = await subtle.deriveBits({
|
||||||
|
name,
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, 8 * size);
|
||||||
|
|
||||||
|
assert(bits instanceof ArrayBuffer);
|
||||||
|
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Case insensitivity
|
||||||
|
const bits = await subtle.deriveBits({
|
||||||
|
name: name.toLowerCase(),
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, 8 * size);
|
||||||
|
|
||||||
|
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Null length
|
||||||
|
const bits = await subtle.deriveBits({
|
||||||
|
name,
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, null);
|
||||||
|
|
||||||
|
assert.strictEqual(Buffer.from(bits).toString('hex'), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Short Result
|
||||||
|
const bits = await subtle.deriveBits({
|
||||||
|
name,
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, 8 * size - 32);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(bits).toString('hex'),
|
||||||
|
result.slice(0, -8));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Too long result
|
||||||
|
await assert.rejects(subtle.deriveBits({
|
||||||
|
name,
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, 8 * size + 8), {
|
||||||
|
message: /derived bit length is too small/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Non-multiple of 8
|
||||||
|
const bits = await subtle.deriveBits({
|
||||||
|
name,
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, 8 * size - 11);
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(bits).toString('hex'),
|
||||||
|
result.slice(0, -4));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Error tests
|
||||||
|
{
|
||||||
|
// Missing public property
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveBits(
|
||||||
|
{ name: 'X448' },
|
||||||
|
keys.X448.privateKey,
|
||||||
|
8 * keys.X448.size),
|
||||||
|
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// The public property is not a CryptoKey
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveBits(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: { message: 'Not a CryptoKey' }
|
||||||
|
},
|
||||||
|
keys.X448.privateKey,
|
||||||
|
8 * keys.X448.size),
|
||||||
|
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Mismatched types
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveBits(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: keys.X25519.publicKey
|
||||||
|
},
|
||||||
|
keys.X448.privateKey,
|
||||||
|
8 * keys.X448.size),
|
||||||
|
{ message: 'The public and private keys must be of the same type' });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Base key is not a private key
|
||||||
|
await assert.rejects(subtle.deriveBits({
|
||||||
|
name: 'X448',
|
||||||
|
public: keys.X448.publicKey
|
||||||
|
}, keys.X448.publicKey, null), {
|
||||||
|
message: /baseKey must be a private key/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Base key is not a private key
|
||||||
|
await assert.rejects(subtle.deriveBits({
|
||||||
|
name: 'X448',
|
||||||
|
public: keys.X448.privateKey
|
||||||
|
}, keys.X448.publicKey, null), {
|
||||||
|
message: /algorithm\.public must be a public key/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Public is a secret key
|
||||||
|
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
|
||||||
|
const key = await subtle.importKey(
|
||||||
|
'raw',
|
||||||
|
keyData,
|
||||||
|
{ name: 'AES-CBC', length: 256 },
|
||||||
|
false, ['encrypt']);
|
||||||
|
|
||||||
|
await assert.rejects(subtle.deriveBits({
|
||||||
|
name: 'X448',
|
||||||
|
public: key
|
||||||
|
}, keys.X448.publicKey, null), {
|
||||||
|
message: /algorithm\.public must be a public key/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})().then(common.mustCall());
|
@ -207,7 +207,7 @@ async function prepareKeys() {
|
|||||||
name: 'ECDH',
|
name: 'ECDH',
|
||||||
public: publicKey
|
public: publicKey
|
||||||
}, keys['P-521'].privateKey, null), {
|
}, keys['P-521'].privateKey, null), {
|
||||||
message: /Keys must be ECDH keys/
|
message: /Keys must be ECDH, X25519, or X448 keys/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,3 +132,29 @@ if (typeof internalBinding('crypto').ScryptJob === 'function') {
|
|||||||
|
|
||||||
tests.then(common.mustCall());
|
tests.then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test X25519 and X448 bit derivation
|
||||||
|
{
|
||||||
|
async function test(name) {
|
||||||
|
const [alice, bob] = await Promise.all([
|
||||||
|
subtle.generateKey({ name }, true, ['deriveBits']),
|
||||||
|
subtle.generateKey({ name }, true, ['deriveBits']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [secret1, secret2] = await Promise.all([
|
||||||
|
subtle.deriveBits({
|
||||||
|
name, public: alice.publicKey
|
||||||
|
}, bob.privateKey, 128),
|
||||||
|
subtle.deriveBits({
|
||||||
|
name, public: bob.publicKey
|
||||||
|
}, alice.privateKey, 128),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert(secret1 instanceof ArrayBuffer);
|
||||||
|
assert(secret2 instanceof ArrayBuffer);
|
||||||
|
assert.deepStrictEqual(secret1, secret2);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('X25519').then(common.mustCall());
|
||||||
|
test('X448').then(common.mustCall());
|
||||||
|
}
|
||||||
|
188
test/parallel/test-webcrypto-derivekey-cfrg.js
Normal file
188
test/parallel/test-webcrypto-derivekey-cfrg.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { webcrypto } = require('crypto');
|
||||||
|
const { subtle } = webcrypto;
|
||||||
|
|
||||||
|
const kTests = [
|
||||||
|
{
|
||||||
|
name: 'X25519',
|
||||||
|
size: 32,
|
||||||
|
pkcs8: '302e020100300506032b656e04220420c8838e76d057dfb7d8c95a69e138160ad' +
|
||||||
|
'd6373fd71a4d276bb56e3a81b64ff61',
|
||||||
|
spki: '302a300506032b656e0321001cf2b1e6022ec537371ed7f53e54fa1154d83e98eb' +
|
||||||
|
'64ea51fae5b3307cfe9706',
|
||||||
|
result: '2768409dfab99ec23b8c89b93ff5880295f76176088f89e43dfebe7ea1950008'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
size: 56,
|
||||||
|
pkcs8: '3046020100300506032b656f043a043858c7d29a3eb519b29d00cfb191bb64fc6' +
|
||||||
|
'd8a42d8f17176272b89f2272d1819295c6525c0829671b052ef0727530f188e31' +
|
||||||
|
'd0cc53bf26929e',
|
||||||
|
spki: '3042300506032b656f033900b604a1d1a5cd1d9426d561ef630a9eb16cbe69d5b9' +
|
||||||
|
'ca615edc53633efb52ea31e6e6a0a1dbacc6e76cbce6482d7e4ba3d55d9e802765' +
|
||||||
|
'ce6f',
|
||||||
|
result: 'f0f6c5f17f94f4291eab7178866d37ec8906dd6c514143dc85be7cf28deff39b'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function prepareKeys() {
|
||||||
|
const keys = {};
|
||||||
|
await Promise.all(
|
||||||
|
kTests.map(async ({ name, size, pkcs8, spki, result }) => {
|
||||||
|
const [
|
||||||
|
privateKey,
|
||||||
|
publicKey,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
Buffer.from(pkcs8, 'hex'),
|
||||||
|
{ name },
|
||||||
|
true,
|
||||||
|
['deriveKey', 'deriveBits']),
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
Buffer.from(spki, 'hex'),
|
||||||
|
{ name },
|
||||||
|
true,
|
||||||
|
['deriveKey', 'deriveBits']),
|
||||||
|
]);
|
||||||
|
keys[name] = {
|
||||||
|
privateKey,
|
||||||
|
publicKey,
|
||||||
|
size,
|
||||||
|
result,
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const keys = await prepareKeys();
|
||||||
|
const otherArgs = [
|
||||||
|
{ name: 'HMAC', hash: 'SHA-256', length: 256 },
|
||||||
|
true,
|
||||||
|
['sign', 'verify']];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.keys(keys).map(async (name) => {
|
||||||
|
const { result, privateKey, publicKey } = keys[name];
|
||||||
|
|
||||||
|
{
|
||||||
|
// Good parameters
|
||||||
|
const key = await subtle.deriveKey({
|
||||||
|
name,
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, ...otherArgs);
|
||||||
|
|
||||||
|
const raw = await subtle.exportKey('raw', key);
|
||||||
|
|
||||||
|
assert.strictEqual(Buffer.from(raw).toString('hex'), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Case insensitivity
|
||||||
|
const key = await subtle.deriveKey({
|
||||||
|
name: name.toLowerCase(),
|
||||||
|
public: publicKey
|
||||||
|
}, privateKey, {
|
||||||
|
name: 'HmAc',
|
||||||
|
hash: 'SHA-256',
|
||||||
|
length: 256
|
||||||
|
}, true, ['sign', 'verify']);
|
||||||
|
|
||||||
|
const raw = await subtle.exportKey('raw', key);
|
||||||
|
|
||||||
|
assert.strictEqual(Buffer.from(raw).toString('hex'), result);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Error tests
|
||||||
|
{
|
||||||
|
// Missing public property
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveKey(
|
||||||
|
{ name: 'X448' },
|
||||||
|
keys.X448.privateKey,
|
||||||
|
...otherArgs),
|
||||||
|
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// The public property is not a CryptoKey
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: { message: 'Not a CryptoKey' }
|
||||||
|
},
|
||||||
|
keys.X448.privateKey,
|
||||||
|
...otherArgs),
|
||||||
|
{ code: 'ERR_INVALID_ARG_TYPE' });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Mismatched named curves
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: keys.X25519.publicKey
|
||||||
|
},
|
||||||
|
keys.X448.privateKey,
|
||||||
|
...otherArgs),
|
||||||
|
{ message: 'The public and private keys must be of the same type' });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Base key is not a private key
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: keys.X448.publicKey
|
||||||
|
},
|
||||||
|
keys.X448.publicKey,
|
||||||
|
...otherArgs),
|
||||||
|
{ message: /baseKey must be a private key/ });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Base key is not a private key
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: keys.X448.privateKey
|
||||||
|
},
|
||||||
|
keys.X448.publicKey,
|
||||||
|
...otherArgs),
|
||||||
|
{ message: /algorithm\.public must be a public key/ });
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Public is a secret key
|
||||||
|
const keyData = webcrypto.getRandomValues(new Uint8Array(32));
|
||||||
|
const key = await subtle.importKey(
|
||||||
|
'raw',
|
||||||
|
keyData,
|
||||||
|
{ name: 'AES-CBC', length: 256 },
|
||||||
|
false, ['encrypt']);
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.deriveKey(
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
public: key
|
||||||
|
},
|
||||||
|
keys.X448.publicKey,
|
||||||
|
...otherArgs),
|
||||||
|
{ message: /algorithm\.public must be a public key/ });
|
||||||
|
}
|
||||||
|
})().then(common.mustCall());
|
@ -175,7 +175,7 @@ async function prepareKeys() {
|
|||||||
},
|
},
|
||||||
keys['P-521'].privateKey,
|
keys['P-521'].privateKey,
|
||||||
...otherArgs),
|
...otherArgs),
|
||||||
{ message: /Keys must be ECDH keys/ });
|
{ message: /Keys must be ECDH, X25519, or X448 keys/ });
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ const { internalBinding } = require('internal/test/binding');
|
|||||||
// This is only a partial test. The WebCrypto Web Platform Tests
|
// This is only a partial test. The WebCrypto Web Platform Tests
|
||||||
// will provide much greater coverage.
|
// will provide much greater coverage.
|
||||||
|
|
||||||
// Test ECDH bit derivation
|
// Test ECDH key derivation
|
||||||
{
|
{
|
||||||
async function test(namedCurve) {
|
async function test(namedCurve) {
|
||||||
const [alice, bob] = await Promise.all([
|
const [alice, bob] = await Promise.all([
|
||||||
@ -48,7 +48,7 @@ const { internalBinding } = require('internal/test/binding');
|
|||||||
test('P-521').then(common.mustCall());
|
test('P-521').then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test HKDF bit derivation
|
// Test HKDF key derivation
|
||||||
{
|
{
|
||||||
async function test(pass, info, salt, hash, expected) {
|
async function test(pass, info, salt, hash, expected) {
|
||||||
const ec = new TextEncoder();
|
const ec = new TextEncoder();
|
||||||
@ -85,7 +85,7 @@ const { internalBinding } = require('internal/test/binding');
|
|||||||
tests.then(common.mustCall());
|
tests.then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test PBKDF2 bit derivation
|
// Test PBKDF2 key derivation
|
||||||
{
|
{
|
||||||
async function test(pass, salt, iterations, hash, expected) {
|
async function test(pass, salt, iterations, hash, expected) {
|
||||||
const ec = new TextEncoder();
|
const ec = new TextEncoder();
|
||||||
@ -121,7 +121,7 @@ const { internalBinding } = require('internal/test/binding');
|
|||||||
tests.then(common.mustCall());
|
tests.then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test Scrypt bit derivation
|
// Test Scrypt key derivation
|
||||||
if (typeof internalBinding('crypto').ScryptJob === 'function') {
|
if (typeof internalBinding('crypto').ScryptJob === 'function') {
|
||||||
async function test(pass, salt, expected) {
|
async function test(pass, salt, expected) {
|
||||||
const ec = new TextEncoder();
|
const ec = new TextEncoder();
|
||||||
@ -184,3 +184,38 @@ if (typeof internalBinding('crypto').ScryptJob === 'function') {
|
|||||||
}
|
}
|
||||||
})().then(common.mustCall());
|
})().then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test X25519 and X448 key derivation
|
||||||
|
{
|
||||||
|
async function test(name) {
|
||||||
|
const [alice, bob] = await Promise.all([
|
||||||
|
subtle.generateKey({ name }, true, ['deriveKey']),
|
||||||
|
subtle.generateKey({ name }, true, ['deriveKey']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [secret1, secret2] = await Promise.all([
|
||||||
|
subtle.deriveKey({
|
||||||
|
name, public: alice.publicKey
|
||||||
|
}, bob.privateKey, {
|
||||||
|
name: 'AES-CBC',
|
||||||
|
length: 256
|
||||||
|
}, true, ['encrypt']),
|
||||||
|
subtle.deriveKey({
|
||||||
|
name, public: bob.publicKey
|
||||||
|
}, alice.privateKey, {
|
||||||
|
name: 'AES-CBC',
|
||||||
|
length: 256
|
||||||
|
}, true, ['encrypt']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [raw1, raw2] = await Promise.all([
|
||||||
|
subtle.exportKey('raw', secret1),
|
||||||
|
subtle.exportKey('raw', secret2),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(raw1, raw2);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('X25519').then(common.mustCall());
|
||||||
|
test('X448').then(common.mustCall());
|
||||||
|
}
|
||||||
|
@ -1,481 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const common = require('../common');
|
|
||||||
|
|
||||||
if (!common.hasCrypto)
|
|
||||||
common.skip('missing crypto');
|
|
||||||
|
|
||||||
const assert = require('assert');
|
|
||||||
const {
|
|
||||||
generateKeyPairSync,
|
|
||||||
webcrypto: { subtle }
|
|
||||||
} = require('crypto');
|
|
||||||
|
|
||||||
async function generateKey(namedCurve) {
|
|
||||||
return subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: namedCurve,
|
|
||||||
namedCurve
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['sign', 'verify']);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function test1(namedCurve) {
|
|
||||||
const {
|
|
||||||
publicKey,
|
|
||||||
privateKey,
|
|
||||||
} = await generateKey(namedCurve);
|
|
||||||
|
|
||||||
const data = Buffer.from('hello world');
|
|
||||||
|
|
||||||
assert(publicKey);
|
|
||||||
assert(privateKey);
|
|
||||||
|
|
||||||
const sig = await subtle.sign(
|
|
||||||
{ name: namedCurve },
|
|
||||||
privateKey,
|
|
||||||
data
|
|
||||||
);
|
|
||||||
|
|
||||||
assert(sig);
|
|
||||||
|
|
||||||
assert(await subtle.verify(
|
|
||||||
{ name: namedCurve },
|
|
||||||
publicKey,
|
|
||||||
sig,
|
|
||||||
data
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
test1('NODE-ED25519'),
|
|
||||||
test1('NODE-ED448'),
|
|
||||||
]).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
Buffer.alloc(10),
|
|
||||||
{
|
|
||||||
name: 'NODE-ED25519',
|
|
||||||
namedCurve: 'NODE-ED25519'
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
['sign']),
|
|
||||||
{
|
|
||||||
message: /NODE-ED25519 raw keys must be exactly 32-bytes/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
Buffer.alloc(10),
|
|
||||||
{
|
|
||||||
name: 'NODE-ED448',
|
|
||||||
namedCurve: 'NODE-ED448'
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
['sign']),
|
|
||||||
{
|
|
||||||
message: /NODE-ED448 raw keys must be exactly 57-bytes/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
const testVectors = {
|
|
||||||
'NODE-ED25519': [
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a',
|
|
||||||
'hex'),
|
|
||||||
message: Buffer.alloc(0),
|
|
||||||
sig:
|
|
||||||
Buffer.from(
|
|
||||||
'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155' +
|
|
||||||
'5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b',
|
|
||||||
'hex'),
|
|
||||||
crv: 'Ed25519',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c',
|
|
||||||
'hex'),
|
|
||||||
message: Buffer.from('72', 'hex'),
|
|
||||||
sig:
|
|
||||||
Buffer.from(
|
|
||||||
'92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da' +
|
|
||||||
'085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00',
|
|
||||||
'hex'),
|
|
||||||
crv: 'Ed25519',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025',
|
|
||||||
'hex'),
|
|
||||||
message: Buffer.from('af82', 'hex'),
|
|
||||||
sig:
|
|
||||||
Buffer.from(
|
|
||||||
'6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac' +
|
|
||||||
'18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a',
|
|
||||||
'hex'),
|
|
||||||
crv: 'Ed25519',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e',
|
|
||||||
'hex'),
|
|
||||||
message: Buffer.from(
|
|
||||||
'08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98' +
|
|
||||||
'fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d8' +
|
|
||||||
'79de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d' +
|
|
||||||
'658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc' +
|
|
||||||
'1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4fe' +
|
|
||||||
'ba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e' +
|
|
||||||
'06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbef' +
|
|
||||||
'efd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7' +
|
|
||||||
'aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed1' +
|
|
||||||
'85ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2' +
|
|
||||||
'd17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24' +
|
|
||||||
'554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f270' +
|
|
||||||
'88d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc' +
|
|
||||||
'2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b07' +
|
|
||||||
'07e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128ba' +
|
|
||||||
'b27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51a' +
|
|
||||||
'ddd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429e' +
|
|
||||||
'c96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb7' +
|
|
||||||
'51fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c' +
|
|
||||||
'42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8' +
|
|
||||||
'ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34df' +
|
|
||||||
'f7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08' +
|
|
||||||
'd78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649' +
|
|
||||||
'de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e4' +
|
|
||||||
'88acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a3' +
|
|
||||||
'2ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e' +
|
|
||||||
'6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5f' +
|
|
||||||
'b93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b5' +
|
|
||||||
'0d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1' +
|
|
||||||
'369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380d' +
|
|
||||||
'b2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c' +
|
|
||||||
'0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0',
|
|
||||||
'hex'),
|
|
||||||
sig: Buffer.from(
|
|
||||||
'0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350' +
|
|
||||||
'aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03',
|
|
||||||
'hex'),
|
|
||||||
crv: 'Ed25519',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'NODE-ED448': [
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3' +
|
|
||||||
'528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b', 'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778' +
|
|
||||||
'edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180', 'hex'),
|
|
||||||
message: Buffer.alloc(0),
|
|
||||||
sig:
|
|
||||||
Buffer.from(
|
|
||||||
'533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f' +
|
|
||||||
'2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a' +
|
|
||||||
'9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4db' +
|
|
||||||
'b61149f05a7363268c71d95808ff2e652600', 'hex'),
|
|
||||||
crv: 'Ed448',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463a' +
|
|
||||||
'fbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e', 'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c086' +
|
|
||||||
'6aea01eb00742802b8438ea4cb82169c235160627b4c3a9480', 'hex'),
|
|
||||||
message: Buffer.from('03', 'hex'),
|
|
||||||
sig:
|
|
||||||
Buffer.from(
|
|
||||||
'26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f435' +
|
|
||||||
'2541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cb' +
|
|
||||||
'cee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0f' +
|
|
||||||
'f3348ab21aa4adafd1d234441cf807c03a00', 'hex'),
|
|
||||||
crv: 'Ed448',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffd' +
|
|
||||||
'f60500553abc0e05cd02184bdb89c4ccd67e187951267eb328', 'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e3' +
|
|
||||||
'65fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400', 'hex'),
|
|
||||||
message: Buffer.from('0c3e544074ec63b0265e0c', 'hex'),
|
|
||||||
sig:
|
|
||||||
Buffer.from(
|
|
||||||
'1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d3' +
|
|
||||||
'89dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b05' +
|
|
||||||
'1068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5' +
|
|
||||||
'028961c9bf8ffd973fe5d5c206492b140e00', 'hex'),
|
|
||||||
crv: 'Ed448',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
async function test2(namedCurve) {
|
|
||||||
const vectors = testVectors[namedCurve];
|
|
||||||
await Promise.all(vectors.map(async (vector) => {
|
|
||||||
const [
|
|
||||||
privateKey,
|
|
||||||
publicKey,
|
|
||||||
] = await Promise.all([
|
|
||||||
subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
vector.privateKey,
|
|
||||||
{
|
|
||||||
name: namedCurve,
|
|
||||||
namedCurve
|
|
||||||
},
|
|
||||||
true, ['sign']),
|
|
||||||
subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
vector.publicKey,
|
|
||||||
{
|
|
||||||
name: namedCurve,
|
|
||||||
namedCurve,
|
|
||||||
public: true
|
|
||||||
},
|
|
||||||
true, ['verify']),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const rawPublicKey = await subtle.exportKey('raw', publicKey);
|
|
||||||
assert.deepStrictEqual(Buffer.from(rawPublicKey), vector.publicKey);
|
|
||||||
|
|
||||||
assert.rejects(subtle.exportKey('raw', privateKey), {
|
|
||||||
message: new RegExp(`Unable to export a raw ${namedCurve} private key`)
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
const sig = await subtle.sign(
|
|
||||||
{ name: namedCurve },
|
|
||||||
privateKey,
|
|
||||||
vector.message
|
|
||||||
);
|
|
||||||
|
|
||||||
assert(sig);
|
|
||||||
|
|
||||||
assert(await subtle.verify(
|
|
||||||
{ name: namedCurve },
|
|
||||||
publicKey,
|
|
||||||
vector.sig,
|
|
||||||
vector.message
|
|
||||||
));
|
|
||||||
|
|
||||||
const [
|
|
||||||
publicKeyJwk,
|
|
||||||
privateKeyJwk,
|
|
||||||
] = await Promise.all([
|
|
||||||
subtle.exportKey('jwk', publicKey),
|
|
||||||
subtle.exportKey('jwk', privateKey),
|
|
||||||
]);
|
|
||||||
assert.strictEqual(publicKeyJwk.kty, 'OKP');
|
|
||||||
assert.strictEqual(privateKeyJwk.kty, 'OKP');
|
|
||||||
assert.strictEqual(publicKeyJwk.crv, vector.crv);
|
|
||||||
assert.strictEqual(privateKeyJwk.crv, vector.crv);
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(publicKeyJwk.x, 'base64'),
|
|
||||||
vector.publicKey);
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(privateKeyJwk.x, 'base64'),
|
|
||||||
vector.publicKey);
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(privateKeyJwk.d, 'base64'),
|
|
||||||
vector.privateKey);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
test2('NODE-ED25519'),
|
|
||||||
test2('NODE-ED448'),
|
|
||||||
]).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDSA',
|
|
||||||
namedCurve: 'NODE-X25519'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['sign', 'verify']),
|
|
||||||
{
|
|
||||||
message: /Unsupported named curves for ECDSA/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDSA',
|
|
||||||
namedCurve: 'NODE-X448'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['sign', 'verify']),
|
|
||||||
{
|
|
||||||
message: /Unsupported named curves for ECDSA/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDSA',
|
|
||||||
namedCurve: 'NODE-ED25519'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['sign', 'verify']),
|
|
||||||
{
|
|
||||||
message: /Unsupported named curves for ECDSA/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDSA',
|
|
||||||
namedCurve: 'NODE-ED448'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['sign', 'verify']),
|
|
||||||
{
|
|
||||||
message: /Unsupported named curves for ECDSA/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
{
|
|
||||||
for (const asymmetricKeyType of ['ed25519', 'ed448']) {
|
|
||||||
const { publicKey, privateKey } = generateKeyPairSync(asymmetricKeyType);
|
|
||||||
for (const keyObject of [publicKey, privateKey]) {
|
|
||||||
const namedCurve = `NODE-${asymmetricKeyType.toUpperCase()}`;
|
|
||||||
subtle.importKey(
|
|
||||||
'node.keyObject',
|
|
||||||
keyObject,
|
|
||||||
{ name: namedCurve, namedCurve },
|
|
||||||
true,
|
|
||||||
keyObject.type === 'private' ? ['sign'] : ['verify'],
|
|
||||||
).then((cryptoKey) => {
|
|
||||||
assert.strictEqual(cryptoKey.type, keyObject.type);
|
|
||||||
assert.strictEqual(cryptoKey.algorithm.name, namedCurve);
|
|
||||||
}, common.mustNotCall());
|
|
||||||
|
|
||||||
subtle.importKey(
|
|
||||||
keyObject.type === 'private' ? 'pkcs8' : 'spki',
|
|
||||||
keyObject.export({
|
|
||||||
format: 'der',
|
|
||||||
type: keyObject.type === 'private' ? 'pkcs8' : 'spki',
|
|
||||||
}),
|
|
||||||
{ name: namedCurve, namedCurve },
|
|
||||||
true,
|
|
||||||
keyObject.type === 'private' ? ['sign'] : ['verify'],
|
|
||||||
).then((cryptoKey) => {
|
|
||||||
assert.strictEqual(cryptoKey.type, keyObject.type);
|
|
||||||
assert.strictEqual(cryptoKey.algorithm.name, namedCurve);
|
|
||||||
assert.strictEqual(cryptoKey.algorithm.namedCurve, namedCurve);
|
|
||||||
}, common.mustNotCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.importKey(
|
|
||||||
'node.keyObject',
|
|
||||||
keyObject,
|
|
||||||
{
|
|
||||||
name: 'ECDSA',
|
|
||||||
namedCurve,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
keyObject.type === 'private' ? ['sign'] : ['verify']
|
|
||||||
),
|
|
||||||
{
|
|
||||||
message: /Invalid algorithm name/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.importKey(
|
|
||||||
'node.keyObject',
|
|
||||||
keyObject,
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
namedCurve,
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
keyObject.type === 'private' ? ['deriveBits', 'deriveKey'] : [],
|
|
||||||
),
|
|
||||||
{
|
|
||||||
message: /Invalid algorithm name/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
// See: https://github.com/nodejs/node/pull/40300
|
|
||||||
for (const namedCurve of ['NODE-ED25519', 'NODE-ED448']) {
|
|
||||||
assert.rejects(
|
|
||||||
(async () => {
|
|
||||||
const { privateKey } = await generateKey(namedCurve);
|
|
||||||
return subtle.sign(
|
|
||||||
{
|
|
||||||
name: namedCurve,
|
|
||||||
hash: 'SHA-256'
|
|
||||||
},
|
|
||||||
privateKey,
|
|
||||||
Buffer.from('abc')
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
(err) => {
|
|
||||||
assert.strictEqual(err.message, `Hash is not permitted for ${namedCurve}`);
|
|
||||||
assert(err instanceof DOMException);
|
|
||||||
return true;
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
(async () => {
|
|
||||||
const { publicKey, privateKey } = await generateKey(namedCurve);
|
|
||||||
const signature = await subtle.sign(
|
|
||||||
{
|
|
||||||
name: namedCurve,
|
|
||||||
},
|
|
||||||
privateKey,
|
|
||||||
Buffer.from('abc')
|
|
||||||
).catch(common.mustNotCall());
|
|
||||||
|
|
||||||
return subtle.verify(
|
|
||||||
{
|
|
||||||
name: namedCurve,
|
|
||||||
hash: 'SHA-256',
|
|
||||||
},
|
|
||||||
publicKey,
|
|
||||||
signature,
|
|
||||||
Buffer.from('abc')
|
|
||||||
);
|
|
||||||
})(),
|
|
||||||
(err) => {
|
|
||||||
assert.strictEqual(err.message, `Hash is not permitted for ${namedCurve}`);
|
|
||||||
assert(err instanceof DOMException);
|
|
||||||
return true;
|
|
||||||
}).then(common.mustCall());
|
|
||||||
}
|
|
||||||
}
|
|
308
test/parallel/test-webcrypto-export-import-cfrg.js
Normal file
308
test/parallel/test-webcrypto-export-import-cfrg.js
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const { subtle } = crypto.webcrypto;
|
||||||
|
|
||||||
|
const keyData = {
|
||||||
|
'Ed25519': {
|
||||||
|
jwsAlg: 'EdDSA',
|
||||||
|
spki: Buffer.from(
|
||||||
|
'302a300506032b6570032100a054b618c12b26c8d43595a5c38dd2b0140b944a' +
|
||||||
|
'151f75003278c2b6c58ec08f', 'hex'),
|
||||||
|
pkcs8: Buffer.from(
|
||||||
|
'302e020100300506032b657004220420d53150bdcd17b4d4b21ae756d4965639' +
|
||||||
|
'd75b28f56ff9111b1f88326913e445bc', 'hex'),
|
||||||
|
jwk: {
|
||||||
|
kty: 'OKP',
|
||||||
|
crv: 'Ed25519',
|
||||||
|
x: 'oFS2GMErJsjUNZWlw43SsBQLlEoVH3UAMnjCtsWOwI8',
|
||||||
|
d: '1TFQvc0XtNSyGudW1JZWOddbKPVv-REbH4gyaRPkRbw'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Ed448': {
|
||||||
|
jwsAlg: 'EdDSA',
|
||||||
|
spki: Buffer.from(
|
||||||
|
'3043300506032b6571033a0008cc38160c85bca5656ac4924af7ea97a9161b20' +
|
||||||
|
'2528273dcb84afd2eeb99ac912a401b34ef15ef4d9486406a6eecc31e5909219' +
|
||||||
|
'bd54866800', 'hex'),
|
||||||
|
pkcs8: Buffer.from(
|
||||||
|
'3047020100300506032b6571043b0439afd05b2fbb153b47c18dfa66baaed0de' +
|
||||||
|
'fb4e88c651487cdee0fafc40fa3d048fe1cd145a44143243c0468166b5bc161a' +
|
||||||
|
'82e3b904f3e2fcaaf9', 'hex'),
|
||||||
|
jwk: {
|
||||||
|
kty: 'OKP',
|
||||||
|
crv: 'Ed448',
|
||||||
|
x: 'CMw4FgyFvKVlasSSSvfql6kWGyAlKCc9y4Sv0u65mskSpAGzTvFe9NlIZAam7' +
|
||||||
|
'swx5ZCSGb1UhmgA',
|
||||||
|
d: 'r9BbL7sVO0fBjfpmuq7Q3vtOiMZRSHze4Pr8QPo9BI_hzRRaRBQyQ8BGgWa1v' +
|
||||||
|
'BYaguO5BPPi_Kr5'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'X25519': {
|
||||||
|
jwsAlg: 'ECDH-ES',
|
||||||
|
spki: Buffer.from(
|
||||||
|
'302a300506032b656e032100f38d9f4e621a44e0428176a4c8a534b34f07f8db' +
|
||||||
|
'30152f9ca0167aabf598fe65', 'hex'),
|
||||||
|
pkcs8: Buffer.from(
|
||||||
|
'302e020100300506032b656e04220420a8327850317b4b03a5a8b4e923413b1d' +
|
||||||
|
'a4a642e0d6f7a72cf4d16a549e628a5f', 'hex'),
|
||||||
|
jwk: {
|
||||||
|
kty: 'OKP',
|
||||||
|
crv: 'X25519',
|
||||||
|
x: '842fTmIaROBCgXakyKU0s08H-NswFS-coBZ6q_WY_mU',
|
||||||
|
d: 'qDJ4UDF7SwOlqLTpI0E7HaSmQuDW96cs9NFqVJ5iil8'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'X448': {
|
||||||
|
jwsAlg: 'ECDH-ES',
|
||||||
|
spki: Buffer.from(
|
||||||
|
'3042300506032b656f0339001d451c8c0c369a42eadfc2875cd44953caeb46c4' +
|
||||||
|
'66dc86568280bfdbbb01f4709a1b0b1e0dd66cf7b11c84119ddc98890db72891' +
|
||||||
|
'29e30da4', 'hex'),
|
||||||
|
pkcs8: Buffer.from(
|
||||||
|
'3046020100300506032b656f043a0438fc818f6546a81f963c27765dc1c05bfd' +
|
||||||
|
'b169667e5e0cf45318ed1cb93872217ab0d9004e0c7dd0dcb00192f72039cc1a' +
|
||||||
|
'1dff750ec31c8afb', 'hex'),
|
||||||
|
jwk: {
|
||||||
|
kty: 'OKP',
|
||||||
|
crv: 'X448',
|
||||||
|
x: 'HUUcjAw2mkLq38KHXNRJU8rrRsRm3IZWgoC_27sB9HCaGwseDdZs97EchBGd3' +
|
||||||
|
'JiJDbcokSnjDaQ',
|
||||||
|
d: '_IGPZUaoH5Y8J3ZdwcBb_bFpZn5eDPRTGO0cuThyIXqw2QBODH3Q3LABkvcgO' +
|
||||||
|
'cwaHf91DsMcivs'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const testVectors = [
|
||||||
|
{
|
||||||
|
name: 'Ed25519',
|
||||||
|
privateUsages: ['sign'],
|
||||||
|
publicUsages: ['verify']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Ed448',
|
||||||
|
privateUsages: ['sign'],
|
||||||
|
publicUsages: ['verify']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'X25519',
|
||||||
|
privateUsages: ['deriveKey', 'deriveBits'],
|
||||||
|
publicUsages: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'X448',
|
||||||
|
privateUsages: ['deriveKey', 'deriveBits'],
|
||||||
|
publicUsages: []
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function testImportSpki({ name, publicUsages }, extractable) {
|
||||||
|
const key = await subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
keyData[name].spki,
|
||||||
|
{ name },
|
||||||
|
extractable,
|
||||||
|
publicUsages);
|
||||||
|
assert.strictEqual(key.type, 'public');
|
||||||
|
assert.strictEqual(key.extractable, extractable);
|
||||||
|
assert.deepStrictEqual(key.usages, publicUsages);
|
||||||
|
assert.deepStrictEqual(key.algorithm.name, name);
|
||||||
|
|
||||||
|
if (extractable) {
|
||||||
|
// Test the roundtrip
|
||||||
|
const spki = await subtle.exportKey('spki', key);
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(spki).toString('hex'),
|
||||||
|
keyData[name].spki.toString('hex'));
|
||||||
|
} else {
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.exportKey('spki', key), {
|
||||||
|
message: /key is not extractable/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bad usage
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
keyData[name].spki,
|
||||||
|
{ name },
|
||||||
|
extractable,
|
||||||
|
['wrapKey']),
|
||||||
|
{ message: /Unsupported key usage/ });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testImportPkcs8({ name, privateUsages }, extractable) {
|
||||||
|
const key = await subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
keyData[name].pkcs8,
|
||||||
|
{ name },
|
||||||
|
extractable,
|
||||||
|
privateUsages);
|
||||||
|
assert.strictEqual(key.type, 'private');
|
||||||
|
assert.strictEqual(key.extractable, extractable);
|
||||||
|
assert.deepStrictEqual(key.usages, privateUsages);
|
||||||
|
assert.deepStrictEqual(key.algorithm.name, name);
|
||||||
|
|
||||||
|
if (extractable) {
|
||||||
|
// Test the roundtrip
|
||||||
|
const pkcs8 = await subtle.exportKey('pkcs8', key);
|
||||||
|
assert.strictEqual(
|
||||||
|
Buffer.from(pkcs8).toString('hex'),
|
||||||
|
keyData[name].pkcs8.toString('hex'));
|
||||||
|
} else {
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.exportKey('pkcs8', key), {
|
||||||
|
message: /key is not extractable/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testImportJwk({ name, publicUsages, privateUsages }, extractable) {
|
||||||
|
|
||||||
|
const jwk = keyData[name].jwk;
|
||||||
|
|
||||||
|
const [
|
||||||
|
publicKey,
|
||||||
|
privateKey,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{
|
||||||
|
kty: jwk.kty,
|
||||||
|
crv: jwk.crv,
|
||||||
|
x: jwk.x,
|
||||||
|
},
|
||||||
|
{ name },
|
||||||
|
extractable, publicUsages),
|
||||||
|
subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
jwk,
|
||||||
|
{ name },
|
||||||
|
extractable,
|
||||||
|
privateUsages),
|
||||||
|
subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{
|
||||||
|
alg: keyData[name].jwsAlg,
|
||||||
|
kty: jwk.kty,
|
||||||
|
crv: jwk.crv,
|
||||||
|
x: jwk.x,
|
||||||
|
},
|
||||||
|
{ name },
|
||||||
|
extractable, publicUsages),
|
||||||
|
subtle.importKey(
|
||||||
|
'jwk',
|
||||||
|
{
|
||||||
|
...jwk,
|
||||||
|
alg: keyData[name].jwsAlg,
|
||||||
|
},
|
||||||
|
{ name },
|
||||||
|
extractable,
|
||||||
|
privateUsages),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.strictEqual(publicKey.type, 'public');
|
||||||
|
assert.strictEqual(privateKey.type, 'private');
|
||||||
|
assert.strictEqual(publicKey.extractable, extractable);
|
||||||
|
assert.strictEqual(privateKey.extractable, extractable);
|
||||||
|
assert.deepStrictEqual(publicKey.usages, publicUsages);
|
||||||
|
assert.deepStrictEqual(privateKey.usages, privateUsages);
|
||||||
|
assert.strictEqual(publicKey.algorithm.name, name);
|
||||||
|
assert.strictEqual(privateKey.algorithm.name, name);
|
||||||
|
|
||||||
|
if (extractable) {
|
||||||
|
// Test the round trip
|
||||||
|
const [
|
||||||
|
pubJwk,
|
||||||
|
pvtJwk,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.exportKey('jwk', publicKey),
|
||||||
|
subtle.exportKey('jwk', privateKey),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(pubJwk.key_ops, publicUsages);
|
||||||
|
assert.strictEqual(pubJwk.ext, true);
|
||||||
|
assert.strictEqual(pubJwk.kty, 'OKP');
|
||||||
|
assert.strictEqual(pubJwk.x, jwk.x);
|
||||||
|
assert.strictEqual(pubJwk.crv, jwk.crv);
|
||||||
|
|
||||||
|
assert.deepStrictEqual(pvtJwk.key_ops, privateUsages);
|
||||||
|
assert.strictEqual(pvtJwk.ext, true);
|
||||||
|
assert.strictEqual(pvtJwk.kty, 'OKP');
|
||||||
|
assert.strictEqual(pvtJwk.x, jwk.x);
|
||||||
|
assert.strictEqual(pvtJwk.crv, jwk.crv);
|
||||||
|
assert.strictEqual(pvtJwk.d, jwk.d);
|
||||||
|
|
||||||
|
if (jwk.crv.startsWith('Ed')) {
|
||||||
|
assert.strictEqual(pubJwk.alg, 'EdDSA');
|
||||||
|
assert.strictEqual(pvtJwk.alg, 'EdDSA');
|
||||||
|
} else {
|
||||||
|
assert.strictEqual(pubJwk.alg, undefined);
|
||||||
|
assert.strictEqual(pvtJwk.alg, undefined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.exportKey('jwk', publicKey), {
|
||||||
|
message: /key is not extractable/
|
||||||
|
});
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.exportKey('jwk', privateKey), {
|
||||||
|
message: /key is not extractable/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const tests = [];
|
||||||
|
testVectors.forEach((vector) => {
|
||||||
|
[true, false].forEach((extractable) => {
|
||||||
|
tests.push(testImportSpki(vector, extractable));
|
||||||
|
tests.push(testImportPkcs8(vector, extractable));
|
||||||
|
tests.push(testImportJwk(vector, extractable));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(tests);
|
||||||
|
})().then(common.mustCall());
|
||||||
|
|
||||||
|
{
|
||||||
|
const rsaPublic = crypto.createPublicKey(
|
||||||
|
fixtures.readKey('rsa_public_2048.pem'));
|
||||||
|
const rsaPrivate = crypto.createPrivateKey(
|
||||||
|
fixtures.readKey('rsa_private_2048.pem'));
|
||||||
|
|
||||||
|
for (const [name, [publicUsage, privateUsage]] of Object.entries({
|
||||||
|
'Ed25519': ['verify', 'sign'],
|
||||||
|
'X448': ['deriveBits', 'deriveBits'],
|
||||||
|
})) {
|
||||||
|
assert.rejects(subtle.importKey(
|
||||||
|
'node.keyObject',
|
||||||
|
rsaPublic,
|
||||||
|
{ name },
|
||||||
|
true, [publicUsage]), { message: /Invalid key type/ });
|
||||||
|
assert.rejects(subtle.importKey(
|
||||||
|
'node.keyObject',
|
||||||
|
rsaPrivate,
|
||||||
|
{ name },
|
||||||
|
true, [privateUsage]), { message: /Invalid key type/ });
|
||||||
|
assert.rejects(subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
rsaPublic.export({ format: 'der', type: 'spki' }),
|
||||||
|
{ name },
|
||||||
|
true, [publicUsage]), { message: /Invalid key type/ });
|
||||||
|
assert.rejects(subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
rsaPrivate.export({ format: 'der', type: 'pkcs8' }),
|
||||||
|
{ name },
|
||||||
|
true, [privateUsage]), { message: /Invalid key type/ });
|
||||||
|
}
|
||||||
|
}
|
@ -129,6 +129,40 @@ const vectors = {
|
|||||||
'deriveBits',
|
'deriveBits',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
'Ed25519': {
|
||||||
|
usages: [
|
||||||
|
'sign',
|
||||||
|
'verify',
|
||||||
|
],
|
||||||
|
mandatoryUsages: ['sign']
|
||||||
|
},
|
||||||
|
'Ed448': {
|
||||||
|
usages: [
|
||||||
|
'sign',
|
||||||
|
'verify',
|
||||||
|
],
|
||||||
|
mandatoryUsages: ['sign']
|
||||||
|
},
|
||||||
|
'X25519': {
|
||||||
|
usages: [
|
||||||
|
'deriveKey',
|
||||||
|
'deriveBits',
|
||||||
|
],
|
||||||
|
mandatoryUsages: [
|
||||||
|
'deriveKey',
|
||||||
|
'deriveBits',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'X448': {
|
||||||
|
usages: [
|
||||||
|
'deriveKey',
|
||||||
|
'deriveBits',
|
||||||
|
],
|
||||||
|
mandatoryUsages: [
|
||||||
|
'deriveKey',
|
||||||
|
'deriveBits',
|
||||||
|
]
|
||||||
|
},
|
||||||
'NODE-DSA': {
|
'NODE-DSA': {
|
||||||
algorithm: { modulusLength: 1024, hash: 'SHA-256' },
|
algorithm: { modulusLength: 1024, hash: 'SHA-256' },
|
||||||
usages: [
|
usages: [
|
||||||
@ -665,3 +699,63 @@ assert.throws(() => new CryptoKey(), { code: 'ERR_ILLEGAL_CONSTRUCTOR' });
|
|||||||
assert(!isCryptoKey(buffer));
|
assert(!isCryptoKey(buffer));
|
||||||
assert(!isCryptoKey(keyObject));
|
assert(!isCryptoKey(keyObject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test OKP Key Generation
|
||||||
|
{
|
||||||
|
async function test(
|
||||||
|
name,
|
||||||
|
privateUsages,
|
||||||
|
publicUsages = privateUsages) {
|
||||||
|
|
||||||
|
let usages = privateUsages;
|
||||||
|
if (publicUsages !== privateUsages)
|
||||||
|
usages = usages.concat(publicUsages);
|
||||||
|
|
||||||
|
const { publicKey, privateKey } = await subtle.generateKey({
|
||||||
|
name,
|
||||||
|
}, true, usages);
|
||||||
|
|
||||||
|
assert(publicKey);
|
||||||
|
assert(privateKey);
|
||||||
|
assert(isCryptoKey(publicKey));
|
||||||
|
assert(isCryptoKey(privateKey));
|
||||||
|
|
||||||
|
assert.strictEqual(publicKey.type, 'public');
|
||||||
|
assert.strictEqual(privateKey.type, 'private');
|
||||||
|
assert.strictEqual(publicKey.extractable, true);
|
||||||
|
assert.strictEqual(privateKey.extractable, true);
|
||||||
|
assert.deepStrictEqual(publicKey.usages, publicUsages);
|
||||||
|
assert.deepStrictEqual(privateKey.usages, privateUsages);
|
||||||
|
assert.strictEqual(publicKey.algorithm.name, name);
|
||||||
|
assert.strictEqual(privateKey.algorithm.name, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const kTests = [
|
||||||
|
[
|
||||||
|
'Ed25519',
|
||||||
|
['sign'],
|
||||||
|
['verify'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Ed448',
|
||||||
|
['sign'],
|
||||||
|
['verify'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'X25519',
|
||||||
|
['deriveKey', 'deriveBits'],
|
||||||
|
[],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'X448',
|
||||||
|
['deriveKey', 'deriveBits'],
|
||||||
|
[],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
const tests = kTests.map((args) => test(...args));
|
||||||
|
|
||||||
|
// Test bad parameters
|
||||||
|
|
||||||
|
Promise.all(tests).then(common.mustCall());
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ async function testVerify({ name,
|
|||||||
privateKey,
|
privateKey,
|
||||||
hmacKey,
|
hmacKey,
|
||||||
rsaKeys,
|
rsaKeys,
|
||||||
|
okpKeys,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
subtle.importKey(
|
subtle.importKey(
|
||||||
'spki',
|
'spki',
|
||||||
@ -55,6 +56,12 @@ async function testVerify({ name,
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
['sign']),
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'Ed25519',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert(await subtle.verify({ name, hash }, publicKey, signature, plaintext));
|
assert(await subtle.verify({ name, hash }, publicKey, signature, plaintext));
|
||||||
@ -89,6 +96,11 @@ async function testVerify({ name,
|
|||||||
message: /Unable to use this key to verify/
|
message: /Unable to use this key to verify/
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify({ name, hash }, okpKeys.publicKey, signature, plaintext), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
// Test failure when signature is altered
|
// Test failure when signature is altered
|
||||||
{
|
{
|
||||||
const copy = Buffer.from(signature);
|
const copy = Buffer.from(signature);
|
||||||
@ -140,6 +152,7 @@ async function testSign({ name,
|
|||||||
privateKey,
|
privateKey,
|
||||||
hmacKey,
|
hmacKey,
|
||||||
rsaKeys,
|
rsaKeys,
|
||||||
|
okpKeys,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
subtle.importKey(
|
subtle.importKey(
|
||||||
'spki',
|
'spki',
|
||||||
@ -172,6 +185,12 @@ async function testSign({ name,
|
|||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
['sign']),
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'Ed25519',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -210,6 +229,11 @@ async function testSign({ name,
|
|||||||
subtle.sign({ name, hash }, rsaKeys.privateKey, plaintext), {
|
subtle.sign({ name, hash }, rsaKeys.privateKey, plaintext), {
|
||||||
message: /Unable to use this key to sign/
|
message: /Unable to use this key to sign/
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign({ name, hash }, okpKeys.privateKey, plaintext), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
(async function() {
|
(async function() {
|
||||||
|
263
test/parallel/test-webcrypto-sign-verify-eddsa.js
Normal file
263
test/parallel/test-webcrypto-sign-verify-eddsa.js
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
|
||||||
|
if (!common.hasCrypto)
|
||||||
|
common.skip('missing crypto');
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const { subtle } = require('crypto').webcrypto;
|
||||||
|
|
||||||
|
const vectors = require('../fixtures/crypto/eddsa')();
|
||||||
|
|
||||||
|
async function testVerify({ name,
|
||||||
|
publicKeyBuffer,
|
||||||
|
privateKeyBuffer,
|
||||||
|
signature,
|
||||||
|
data }) {
|
||||||
|
const [
|
||||||
|
publicKey,
|
||||||
|
noVerifyPublicKey,
|
||||||
|
privateKey,
|
||||||
|
hmacKey,
|
||||||
|
rsaKeys,
|
||||||
|
ecKeys,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
publicKeyBuffer,
|
||||||
|
{ name },
|
||||||
|
false,
|
||||||
|
['verify']),
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
publicKeyBuffer,
|
||||||
|
{ name },
|
||||||
|
false,
|
||||||
|
[ /* No usages */ ]),
|
||||||
|
subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
privateKeyBuffer,
|
||||||
|
{ name },
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{ name: 'HMAC', hash: 'SHA-256' },
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'RSA-PSS',
|
||||||
|
modulusLength: 1024,
|
||||||
|
publicExponent: new Uint8Array([1, 0, 1]),
|
||||||
|
hash: 'SHA-256',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'ECDSA',
|
||||||
|
namedCurve: 'P-256'
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert(await subtle.verify({ name }, publicKey, signature, data));
|
||||||
|
|
||||||
|
// Test verification with altered buffers
|
||||||
|
const copy = Buffer.from(data);
|
||||||
|
const sigcopy = Buffer.from(signature);
|
||||||
|
const p = subtle.verify({ name }, publicKey, sigcopy, copy);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
sigcopy[0] = 255 - sigcopy[0];
|
||||||
|
assert(await p);
|
||||||
|
|
||||||
|
// Test failure when using wrong key
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify({ name }, privateKey, signature, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify({ name }, noVerifyPublicKey, signature, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when using the wrong algorithms
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify({ name }, hmacKey, signature, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify({ name }, rsaKeys.publicKey, signature, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.verify({ name }, ecKeys.publicKey, signature, data), {
|
||||||
|
message: /Unable to use this key to verify/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when signature is altered
|
||||||
|
{
|
||||||
|
const copy = Buffer.from(signature);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
assert(!(await subtle.verify(
|
||||||
|
{ name },
|
||||||
|
publicKey,
|
||||||
|
copy,
|
||||||
|
data)));
|
||||||
|
assert(!(await subtle.verify(
|
||||||
|
{ name },
|
||||||
|
publicKey,
|
||||||
|
copy.slice(1),
|
||||||
|
data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failure when data is altered
|
||||||
|
{
|
||||||
|
const copy = Buffer.from(data);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
assert(!(await subtle.verify({ name }, publicKey, signature, copy)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testSign({ name,
|
||||||
|
publicKeyBuffer,
|
||||||
|
privateKeyBuffer,
|
||||||
|
signature,
|
||||||
|
data }) {
|
||||||
|
const [
|
||||||
|
publicKey,
|
||||||
|
noSignPrivateKey,
|
||||||
|
privateKey,
|
||||||
|
hmacKey,
|
||||||
|
rsaKeys,
|
||||||
|
ecKeys,
|
||||||
|
] = await Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
publicKeyBuffer,
|
||||||
|
{ name },
|
||||||
|
false,
|
||||||
|
['verify']),
|
||||||
|
subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
privateKeyBuffer,
|
||||||
|
{ name },
|
||||||
|
false,
|
||||||
|
[ /* No usages */ ]),
|
||||||
|
subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
privateKeyBuffer,
|
||||||
|
{ name },
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{ name: 'HMAC', hash: 'SHA-256' },
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'RSA-PSS',
|
||||||
|
modulusLength: 1024,
|
||||||
|
publicExponent: new Uint8Array([1, 0, 1]),
|
||||||
|
hash: 'SHA-256',
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.generateKey(
|
||||||
|
{
|
||||||
|
name: 'ECDSA',
|
||||||
|
namedCurve: 'P-256'
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
]);
|
||||||
|
|
||||||
|
{
|
||||||
|
const sig = await subtle.sign({ name }, privateKey, data);
|
||||||
|
assert.strictEqual(sig.byteLength, signature.byteLength);
|
||||||
|
assert(await subtle.verify({ name }, publicKey, sig, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const copy = Buffer.from(data);
|
||||||
|
const p = subtle.sign({ name }, privateKey, copy);
|
||||||
|
copy[0] = 255 - copy[0];
|
||||||
|
const sig = await p;
|
||||||
|
assert(await subtle.verify({ name }, publicKey, sig, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test failure when using wrong key
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign({ name }, publicKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when no sign usage
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign({ name }, noSignPrivateKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test failure when using the wrong algorithms
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign({ name }, hmacKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign({ name }, rsaKeys.privateKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
subtle.sign({ name }, ecKeys.privateKey, data), {
|
||||||
|
message: /Unable to use this key to sign/
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
(async function() {
|
||||||
|
const variations = [];
|
||||||
|
|
||||||
|
vectors.forEach((vector) => {
|
||||||
|
variations.push(testVerify(vector));
|
||||||
|
variations.push(testSign(vector));
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(variations);
|
||||||
|
})().then(common.mustCall());
|
||||||
|
|
||||||
|
// Ed448 context
|
||||||
|
{
|
||||||
|
const vector = vectors.find(({ name }) => name === 'Ed448');
|
||||||
|
Promise.all([
|
||||||
|
subtle.importKey(
|
||||||
|
'pkcs8',
|
||||||
|
vector.privateKeyBuffer,
|
||||||
|
{ name: 'Ed448' },
|
||||||
|
false,
|
||||||
|
['sign']),
|
||||||
|
subtle.importKey(
|
||||||
|
'spki',
|
||||||
|
vector.publicKeyBuffer,
|
||||||
|
{ name: 'Ed448' },
|
||||||
|
false,
|
||||||
|
['verify']),
|
||||||
|
]).then(async ([privateKey, publicKey]) => {
|
||||||
|
const sig = await subtle.sign({ name: 'Ed448', context: Buffer.alloc(0) }, privateKey, vector.data);
|
||||||
|
assert.deepStrictEqual(Buffer.from(sig), vector.signature);
|
||||||
|
assert.strictEqual(
|
||||||
|
await subtle.verify({ name: 'Ed448', context: Buffer.alloc(0) }, publicKey, sig, vector.data), true);
|
||||||
|
|
||||||
|
await assert.rejects(subtle.sign({ name: 'Ed448', context: Buffer.alloc(1) }, privateKey, vector.data), {
|
||||||
|
message: /Non zero-length context is not yet supported/
|
||||||
|
});
|
||||||
|
await assert.rejects(subtle.verify({ name: 'Ed448', context: Buffer.alloc(1) }, publicKey, sig, vector.data), {
|
||||||
|
message: /Non zero-length context is not yet supported/
|
||||||
|
});
|
||||||
|
}).then(common.mustCall());
|
||||||
|
}
|
@ -104,3 +104,43 @@ const { subtle } = require('crypto').webcrypto;
|
|||||||
|
|
||||||
test('hello world').then(common.mustCall());
|
test('hello world').then(common.mustCall());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test Sign/Verify Ed25519
|
||||||
|
{
|
||||||
|
async function test(data) {
|
||||||
|
const ec = new TextEncoder();
|
||||||
|
const { publicKey, privateKey } = await subtle.generateKey({
|
||||||
|
name: 'Ed25519',
|
||||||
|
}, true, ['sign', 'verify']);
|
||||||
|
|
||||||
|
const signature = await subtle.sign({
|
||||||
|
name: 'Ed25519',
|
||||||
|
}, privateKey, ec.encode(data));
|
||||||
|
|
||||||
|
assert(await subtle.verify({
|
||||||
|
name: 'Ed25519',
|
||||||
|
}, publicKey, signature, ec.encode(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
test('hello world').then(common.mustCall());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Sign/Verify Ed448
|
||||||
|
{
|
||||||
|
async function test(data) {
|
||||||
|
const ec = new TextEncoder();
|
||||||
|
const { publicKey, privateKey } = await subtle.generateKey({
|
||||||
|
name: 'Ed448',
|
||||||
|
}, true, ['sign', 'verify']);
|
||||||
|
|
||||||
|
const signature = await subtle.sign({
|
||||||
|
name: 'Ed448',
|
||||||
|
}, privateKey, ec.encode(data));
|
||||||
|
|
||||||
|
assert(await subtle.verify({
|
||||||
|
name: 'Ed448',
|
||||||
|
}, publicKey, signature, ec.encode(data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
test('hello world').then(common.mustCall());
|
||||||
|
}
|
||||||
|
@ -1,315 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const common = require('../common');
|
|
||||||
|
|
||||||
if (!common.hasCrypto)
|
|
||||||
common.skip('missing crypto');
|
|
||||||
|
|
||||||
const assert = require('assert');
|
|
||||||
|
|
||||||
const {
|
|
||||||
generateKeyPairSync,
|
|
||||||
webcrypto: { subtle }
|
|
||||||
} = require('crypto');
|
|
||||||
|
|
||||||
// X25519 and X448 are ECDH named curves that should work
|
|
||||||
// with the existing ECDH mechanisms with no additional
|
|
||||||
// changes.
|
|
||||||
|
|
||||||
async function generateKeys(namedCurve, ...usages) {
|
|
||||||
return subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
namedCurve
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
usages);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deriveKey(publicKey, privateKey, length = 256) {
|
|
||||||
return subtle.deriveKey(
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
public: publicKey,
|
|
||||||
},
|
|
||||||
privateKey,
|
|
||||||
{
|
|
||||||
name: 'HMAC',
|
|
||||||
length,
|
|
||||||
hash: 'SHA-512',
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['sign', 'verify']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function exportKey(secretKey) {
|
|
||||||
return subtle.exportKey('raw', secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function importKey(namedCurve, keyData, isPublic = false) {
|
|
||||||
return subtle.importKey(
|
|
||||||
'raw',
|
|
||||||
keyData,
|
|
||||||
{ name: 'ECDH', namedCurve, public: isPublic },
|
|
||||||
true,
|
|
||||||
['deriveKey']
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.rejects(importKey('NODE-X25519', Buffer.alloc(10), true), {
|
|
||||||
message: /NODE-X25519 raw keys must be exactly 32-bytes/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
assert.rejects(importKey('NODE-X448', Buffer.alloc(10), true), {
|
|
||||||
message: /NODE-X448 raw keys must be exactly 56-bytes/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
async function test1(namedCurve) {
|
|
||||||
const {
|
|
||||||
publicKey: publicKey1,
|
|
||||||
privateKey: privateKey1,
|
|
||||||
} = await generateKeys(namedCurve, 'deriveKey', 'deriveBits');
|
|
||||||
|
|
||||||
const {
|
|
||||||
publicKey: publicKey2,
|
|
||||||
privateKey: privateKey2,
|
|
||||||
} = await generateKeys(namedCurve, 'deriveKey', 'deriveBits');
|
|
||||||
|
|
||||||
assert(publicKey1);
|
|
||||||
assert(privateKey1);
|
|
||||||
assert(publicKey2);
|
|
||||||
assert(privateKey2);
|
|
||||||
|
|
||||||
assert.strictEqual(publicKey1.algorithm.namedCurve, namedCurve);
|
|
||||||
assert.strictEqual(privateKey1.algorithm.namedCurve, namedCurve);
|
|
||||||
assert.strictEqual(publicKey2.algorithm.namedCurve, namedCurve);
|
|
||||||
assert.strictEqual(privateKey2.algorithm.namedCurve, namedCurve);
|
|
||||||
|
|
||||||
const [key1, key2] = await Promise.all([
|
|
||||||
deriveKey(publicKey1, privateKey2),
|
|
||||||
deriveKey(publicKey2, privateKey1),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert(key1);
|
|
||||||
assert(key2);
|
|
||||||
|
|
||||||
const [secret1, secret2] = await Promise.all([
|
|
||||||
exportKey(key1),
|
|
||||||
exportKey(key2),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert.deepStrictEqual(secret1, secret2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
test1('NODE-X25519'),
|
|
||||||
test1('NODE-X448'),
|
|
||||||
]).then(common.mustCall());
|
|
||||||
|
|
||||||
const testVectors = {
|
|
||||||
'NODE-X25519': {
|
|
||||||
alice: {
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a',
|
|
||||||
'hex'),
|
|
||||||
},
|
|
||||||
bob: {
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f',
|
|
||||||
'hex'),
|
|
||||||
},
|
|
||||||
sharedSecret:
|
|
||||||
Buffer.from(
|
|
||||||
'4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742',
|
|
||||||
'hex'),
|
|
||||||
},
|
|
||||||
'NODE-X448': {
|
|
||||||
alice: {
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d' +
|
|
||||||
'd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c' +
|
|
||||||
'22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0',
|
|
||||||
'hex'),
|
|
||||||
},
|
|
||||||
bob: {
|
|
||||||
privateKey:
|
|
||||||
Buffer.from(
|
|
||||||
'1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d' +
|
|
||||||
'6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d',
|
|
||||||
'hex'),
|
|
||||||
publicKey:
|
|
||||||
Buffer.from(
|
|
||||||
'3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430' +
|
|
||||||
'27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609',
|
|
||||||
'hex'),
|
|
||||||
},
|
|
||||||
sharedSecret:
|
|
||||||
Buffer.from(
|
|
||||||
'07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282b' +
|
|
||||||
'b60c0b56fd2464c335543936521c24403085d59a449a5037514a879d',
|
|
||||||
'hex'),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
async function test2(namedCurve, length) {
|
|
||||||
const [
|
|
||||||
publicKey1,
|
|
||||||
publicKey2,
|
|
||||||
privateKey1,
|
|
||||||
privateKey2,
|
|
||||||
] = await Promise.all([
|
|
||||||
importKey(namedCurve, testVectors[namedCurve].alice.publicKey, true),
|
|
||||||
importKey(namedCurve, testVectors[namedCurve].bob.publicKey, true),
|
|
||||||
importKey(namedCurve, testVectors[namedCurve].alice.privateKey),
|
|
||||||
importKey(namedCurve, testVectors[namedCurve].bob.privateKey),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [key1, key2] = await Promise.all([
|
|
||||||
deriveKey(publicKey1, privateKey2, length),
|
|
||||||
deriveKey(publicKey2, privateKey1, length),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert(key1);
|
|
||||||
assert(key2);
|
|
||||||
|
|
||||||
const [secret1, secret2] = await Promise.all([
|
|
||||||
exportKey(key1),
|
|
||||||
exportKey(key2),
|
|
||||||
]);
|
|
||||||
|
|
||||||
assert.deepStrictEqual(secret1, secret2);
|
|
||||||
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(secret1),
|
|
||||||
testVectors[namedCurve].sharedSecret);
|
|
||||||
|
|
||||||
const [
|
|
||||||
publicKeyJwk,
|
|
||||||
privateKeyJwk,
|
|
||||||
] = await Promise.all([
|
|
||||||
subtle.exportKey('jwk', publicKey1),
|
|
||||||
subtle.exportKey('jwk', privateKey1),
|
|
||||||
]);
|
|
||||||
assert.strictEqual(publicKeyJwk.kty, 'OKP');
|
|
||||||
assert.strictEqual(privateKeyJwk.kty, 'OKP');
|
|
||||||
assert.strictEqual(publicKeyJwk.crv, namedCurve.slice(5));
|
|
||||||
assert.strictEqual(privateKeyJwk.crv, namedCurve.slice(5));
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(publicKeyJwk.x, 'base64'),
|
|
||||||
testVectors[namedCurve].alice.publicKey);
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(privateKeyJwk.x, 'base64'),
|
|
||||||
testVectors[namedCurve].alice.publicKey);
|
|
||||||
assert.deepStrictEqual(
|
|
||||||
Buffer.from(privateKeyJwk.d, 'base64'),
|
|
||||||
testVectors[namedCurve].alice.privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all([
|
|
||||||
test2('NODE-X25519', 256),
|
|
||||||
test2('NODE-X448', 448),
|
|
||||||
]).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
namedCurve: 'NODE-ED25519'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['deriveBits']),
|
|
||||||
{
|
|
||||||
message: /Unsupported named curves for ECDH/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
assert.rejects(
|
|
||||||
subtle.generateKey(
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
namedCurve: 'NODE-ED448'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['deriveBits']),
|
|
||||||
{
|
|
||||||
message: /Unsupported named curves for ECDH/
|
|
||||||
}).then(common.mustCall());
|
|
||||||
|
|
||||||
{
|
|
||||||
// Private JWK import
|
|
||||||
subtle.importKey(
|
|
||||||
'jwk',
|
|
||||||
{
|
|
||||||
crv: 'X25519',
|
|
||||||
d: '8CE-XY7cvbR-Pu7mILHq8YZ4hLGAA2-RD01he5q2wUA',
|
|
||||||
x: '42IbTo34ZYANub5o42547vB6OxdEd44ztwZewoRch0Q',
|
|
||||||
kty: 'OKP'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
namedCurve: 'NODE-X25519'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
['deriveBits']).then(common.mustCall(), common.mustNotCall());
|
|
||||||
|
|
||||||
// Public JWK import
|
|
||||||
subtle.importKey(
|
|
||||||
'jwk',
|
|
||||||
{
|
|
||||||
crv: 'X25519',
|
|
||||||
x: '42IbTo34ZYANub5o42547vB6OxdEd44ztwZewoRch0Q',
|
|
||||||
kty: 'OKP'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ECDH',
|
|
||||||
namedCurve: 'NODE-X25519'
|
|
||||||
},
|
|
||||||
true,
|
|
||||||
[]).then(common.mustCall(), common.mustNotCall());
|
|
||||||
|
|
||||||
for (const asymmetricKeyType of ['x25519', 'x448']) {
|
|
||||||
const { publicKey, privateKey } = generateKeyPairSync(asymmetricKeyType);
|
|
||||||
for (const keyObject of [publicKey, privateKey]) {
|
|
||||||
const namedCurve = `NODE-${asymmetricKeyType.toUpperCase()}`;
|
|
||||||
subtle.importKey(
|
|
||||||
'node.keyObject',
|
|
||||||
keyObject,
|
|
||||||
{ name: 'ECDH', namedCurve },
|
|
||||||
true,
|
|
||||||
keyObject.type === 'private' ? ['deriveBits', 'deriveKey'] : [],
|
|
||||||
).then((cryptoKey) => {
|
|
||||||
assert.strictEqual(cryptoKey.type, keyObject.type);
|
|
||||||
assert.strictEqual(cryptoKey.algorithm.name, 'ECDH');
|
|
||||||
}, common.mustNotCall());
|
|
||||||
|
|
||||||
subtle.importKey(
|
|
||||||
keyObject.type === 'private' ? 'pkcs8' : 'spki',
|
|
||||||
keyObject.export({
|
|
||||||
format: 'der',
|
|
||||||
type: keyObject.type === 'private' ? 'pkcs8' : 'spki',
|
|
||||||
}),
|
|
||||||
{ name: 'ECDH', namedCurve },
|
|
||||||
true,
|
|
||||||
keyObject.type === 'private' ? ['deriveBits'] : [],
|
|
||||||
).then((cryptoKey) => {
|
|
||||||
assert.strictEqual(cryptoKey.type, keyObject.type);
|
|
||||||
assert.strictEqual(cryptoKey.algorithm.name, 'ECDH');
|
|
||||||
assert.strictEqual(cryptoKey.algorithm.namedCurve, namedCurve);
|
|
||||||
}, common.mustNotCall());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -84,10 +84,10 @@ const customTypesMap = {
|
|||||||
'Crypto': 'webcrypto.html#class-crypto',
|
'Crypto': 'webcrypto.html#class-crypto',
|
||||||
'SubtleCrypto': 'webcrypto.html#class-subtlecrypto',
|
'SubtleCrypto': 'webcrypto.html#class-subtlecrypto',
|
||||||
'RsaOaepParams': 'webcrypto.html#class-rsaoaepparams',
|
'RsaOaepParams': 'webcrypto.html#class-rsaoaepparams',
|
||||||
|
'AlgorithmIdentifier': 'webcrypto.html#class-algorithmidentifier',
|
||||||
'AesCtrParams': 'webcrypto.html#class-aesctrparams',
|
'AesCtrParams': 'webcrypto.html#class-aesctrparams',
|
||||||
'AesCbcParams': 'webcrypto.html#class-aescbcparams',
|
'AesCbcParams': 'webcrypto.html#class-aescbcparams',
|
||||||
'AesGcmParams': 'webcrypto.html#class-aesgcmparams',
|
'AesGcmParams': 'webcrypto.html#class-aesgcmparams',
|
||||||
'AesKwParams': 'webcrypto.html#class-aeskwparams',
|
|
||||||
'EcdhKeyDeriveParams': 'webcrypto.html#class-ecdhkeyderiveparams',
|
'EcdhKeyDeriveParams': 'webcrypto.html#class-ecdhkeyderiveparams',
|
||||||
'HkdfParams': 'webcrypto.html#class-hkdfparams',
|
'HkdfParams': 'webcrypto.html#class-hkdfparams',
|
||||||
'Pbkdf2Params': 'webcrypto.html#class-pbkdf2params',
|
'Pbkdf2Params': 'webcrypto.html#class-pbkdf2params',
|
||||||
@ -100,26 +100,15 @@ const customTypesMap = {
|
|||||||
'webcrypto.html#class-rsahashedimportparams',
|
'webcrypto.html#class-rsahashedimportparams',
|
||||||
'EcKeyImportParams': 'webcrypto.html#class-eckeyimportparams',
|
'EcKeyImportParams': 'webcrypto.html#class-eckeyimportparams',
|
||||||
'HmacImportParams': 'webcrypto.html#class-hmacimportparams',
|
'HmacImportParams': 'webcrypto.html#class-hmacimportparams',
|
||||||
'AesImportParams': 'webcrypto.html#class-aesimportparams',
|
|
||||||
'Pbkdf2ImportParams': 'webcrypto.html#class-pbkdf2importparams',
|
|
||||||
'HmacParams': 'webcrypto.html#class-hmacparams',
|
|
||||||
'EcdsaParams': 'webcrypto.html#class-ecdsaparams',
|
'EcdsaParams': 'webcrypto.html#class-ecdsaparams',
|
||||||
'RsaPssParams': 'webcrypto.html#class-rsapssparams',
|
'RsaPssParams': 'webcrypto.html#class-rsapssparams',
|
||||||
'RsaSignParams': 'webcrypto.html#class-rsasignparams',
|
'Ed448Params': 'webcrypto.html#class-ed448params',
|
||||||
'NodeDhImportParams': 'webcrypto.html#class-nodedhimportparams',
|
|
||||||
'NodeDhKeyGenParams': 'webcrypto.html#class-nodedhkeygenparams',
|
'NodeDhKeyGenParams': 'webcrypto.html#class-nodedhkeygenparams',
|
||||||
'NodeDhDeriveBitsParams':
|
'NodeDhDeriveBitsParams':
|
||||||
'webcrypto.html#class-nodedhderivebitsparams',
|
'webcrypto.html#class-nodedhderivebitsparams',
|
||||||
'NodeDsaImportParams': 'webcrypto.html#class-nodedsaimportparams',
|
'NodeDsaImportParams': 'webcrypto.html#class-nodedsaimportparams',
|
||||||
'NodeDsaKeyGenParams': 'webcrypto.html#class-nodedsakeygenparams',
|
'NodeDsaKeyGenParams': 'webcrypto.html#class-nodedsakeygenparams',
|
||||||
'NodeDsaSignParams': 'webcrypto.html#class-nodedsasignparams',
|
|
||||||
'NodeScryptImportParams':
|
|
||||||
'webcrypto.html#class-nodescryptimportparams',
|
|
||||||
'NodeScryptParams': 'webcrypto.html#class-nodescryptparams',
|
'NodeScryptParams': 'webcrypto.html#class-nodescryptparams',
|
||||||
'NodeEdKeyImportParams':
|
|
||||||
'webcrypto.html#class-nodeedkeyimportparams',
|
|
||||||
'NodeEdKeyGenParams':
|
|
||||||
'webcrypto.html#class-nodeedkeygenparams',
|
|
||||||
|
|
||||||
'dgram.Socket': 'dgram.html#class-dgramsocket',
|
'dgram.Socket': 'dgram.html#class-dgramsocket',
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user