docs: documentation, README, .github files, getting ready to publish

This commit is contained in:
Filip Skokan 2019-02-26 23:46:58 +01:00
parent 8dd95426dc
commit b656702276
11 changed files with 1240 additions and 264 deletions

32
.github/ISSUE_TEMPLATE/bug-report.md vendored Normal file
View file

@ -0,0 +1,32 @@
---
name: Bug report
about: There's a bug in @panva/jose I want to report
title: 'bug: '
labels: bug
---
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
Steps to reproduce the behaviour:
1.
2.
3.
**Expected behaviour**
A clear and concise description of what you expected to happen.
**Environment:**
- @panva/jose version: [e.g. v1.0.0]
- node version: [e.g. v11.9.0]
**Additional context**
Add any other context about the problem here.
- [ ] the bug is happening on latest @panva/jose too.
- [ ] i have tried DEBUG (see readme.md) and can see the issue is with the provider and not my code.
- [ ] i have searched the issues tracker on github for similar issues and couldn't find anything related.

View file

@ -0,0 +1,21 @@
---
name: Feature proposal
about: I have an idea for a new @panva/jose feature
title: 'proposal: '
labels: enhancement
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is.
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context about the problem here.
- [ ] i have searched the configuration section for this feature and couldn't find it
- [ ] i have searched the issues tracker on github for similar requests and couldn't find anything related.

6
.github/ISSUE_TEMPLATE/question.md vendored Normal file
View file

@ -0,0 +1,6 @@
---
name: Question
about: I have a question about using @panva/jose
title: 'question: '
labels: question
---

View file

@ -0,0 +1,20 @@
---
name: Security Vulnerability
about: I want to disclose a vulnerability
---
<!--
Do not disclose vulnerabilities this way. Reach out to the project team via e.g. email, we'll
work together on patching the vulnerability and follow some form of Responsible disclosure once
fixed. Thank you. https://en.wikipedia.org/wiki/Responsible_disclosure
-->
<!--
Do not disclose vulnerabilities this way. Reach out to the project team via e.g. email, we'll
work together on patching the vulnerability and follow some form of Responsible disclosure once
fixed. Thank you. https://en.wikipedia.org/wiki/Responsible_disclosure
-->
<!--
Do not disclose vulnerabilities this way. Reach out to the project team via e.g. email, we'll
work together on patching the vulnerability and follow some form of Responsible disclosure once
fixed. Thank you. https://en.wikipedia.org/wiki/Responsible_disclosure
-->

1
.npmrc Normal file
View file

@ -0,0 +1 @@
access = public

9
.travis.yml Normal file
View file

@ -0,0 +1,9 @@
language: node_js
node_js: stable
script: npm run coverage
after_script: npx codecov
jobs:
include:
- stage: Lint
script: npm run lint
after_script: skip

62
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,62 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers
pledge to making participation in our project and our community a harassment-free experience for
everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level
of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behaviour that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behaviour by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit
permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behaviour and are
expected to take appropriate and fair corrective action in response to any instances of unacceptable
behaviour.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits,
code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or
to ban temporarily or permanently any contributor for other behaviours that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is
representing the project or its community. Examples of representing a project or community include
using an official project e-mail address, posting via an official social media account, or acting as
an appointed representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behaviour may be reported by contacting
the project team at panva.ip@gmail.com. The project team will review and investigate all complaints,
and will respond in a way that it deems appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident. Further details of
specific enforcement policies may be posted separately.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at
[http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

30
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,30 @@
# Contributing to @panva/jose
Please note we have a [code of conduct][coc], please follow it in all your interactions with the
project.
When contributing to this project, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this project before proposing a change via a Pull
Request. Use (and follow!) the appropriate [Issue Template][new-issue] to do so. A contribution that
implements something non-standard will most likely be dismissed.
## Rules of the discussions
Remember to be very clear and transparent when discussing any issue in the discussions boards. We
ask that you keep the language to English and keep on track with the issue at hand. Lastly, please
be respectful of our fellow contributors and keep an exemplary level of professionalism at all
times.
## Pull Request Checklist
- No additional runtime dependencies unless previously agreed upon
- `npm run lint` passes
- File names must be snake_case.js
- Add tests covering 100% of the library code you are adding or modifying
- Unless previously agreed upon (i.e. fixing a bug) all contributions must be backwards compatible
- Update the documentation
- Do not commit unnecessary whitespace
[coc]: https://github.com/panva/jose/blob/master/CODE_OF_CONDUCT.md
[new-issue]: https://github.com/panva/jose/issues/new/choose
[standard-version]: https://github.com/conventional-changelog/standard-version

395
README.md
View file

@ -4,12 +4,7 @@
"JSON Web Almost Everything" - JWA, JWS, JWE, JWK, JWKS for Node.js with minimal dependencies
See the [`@panva/jwt`](https://github.com/panva/jwt) package for JWT.
**Table of Contents**
- [Implemented specs & features](#implemented-specs--features)
- [Usage](#usage)
See the [`@panva/jwt`](https://github.com/panva/jwt) (coming soon™) for JWT convenience abstraction.
## Implemented specs & features
@ -30,7 +25,8 @@ implementation is correct.
Legend:
- **✓** Implemented
- **✕** Missing node crypto support / won't implement / not planned / PR welcome
- **✕** Missing node crypto support / won't implement
- **◯** not planned (yet?) / PR / Use-Case first welcome
| JWK Key Types | Supported ||
| -- | -- | -- |
@ -68,327 +64,199 @@ Legend:
---
Remaining tasks:
- ✓ JWKS abstraction
- ✓ `crit` JWE/JWS Header parameter handling
- ✓ `b64` JWS crit support
- ✓ JWE `zip` handling
- ✓ JWE/JWS decrypt/verify algorithm whitelisting
- ◯ JWE/JWS reference (true/false for `kid`, name of the field for other fields)
- ◯ whitelist additional JWK reference fields (`kid`, `jku`, `x5c`, `x5t`, `x5t#S256`, `x5u`)
- ◯ README and documentation
- ◯ .d.ts types
- ◯ .github files (templates, CoC, Contributing)
- ◯ `@panva/jwt`
- `compact` only with convenience methods and options
- `@panva/jose` as a dependency
Pending Node.js Support 🤞:
- [RFC8037][spec-cfrg] (EdDSA, OKP kty, etc)
- `crypto.getCurves().includes('Curve25519')` // => 😢
- `crypto.getCurves().includes('Curve448')` // => 😢
- `openssl ecparam -list_curves` // => 😢
Won't implement:
- ✕ JWS embedded key / referenced verification - won't implement, who needs it can decode the header
and pass the (`x5c`, `jwk`) to `JWK.importKey` and validate with that key, similarly the
application can handle fetching the referenced `x5u` or `jku`
- ✕ JWS detached content - won't implement, who needs it can remove/attach the payload after/before
the respective operation
- ✕ "none" alg support, no crypto, no use, don't bother
- ✕ JWS embedded key / referenced verification
- one can decode the header and pass the (`x5c`, `jwk`) to `JWK.importKey` and validate with that
key, similarly the application can handle fetching and then instantiating the referenced `x5u`
or `jku` in its own code. This way you opt-in to these behaviours and for `x5c` specifically
the recipient is responsible for validating the certificate chain is trusted
- ✕ JWS detached content
- one can remove/attach the payload after/before the respective operation
- ✕ "none" alg support
- no crypto, no use
Not Planned / PR | Use-Case | Discussion Welcome:
- ◯ automatically adding `kid` reference to JWS / JWE Headers
- ◯ `x5c`, `x5t`, `x5t#S256`, `x5u` etc `JWK.Key` fields
</details>
Missing a feature? - If it wasn't already discussed before, [ask for it][suggest-feature].
Found a bug? - [report it][bug].
<br>
<h2>Support</h2>
Have a question about using @panva/jose? - [ask][ask].
Found a bug? - [report it][bug].
Missing a feature? - If it wasn't already discussed before, [ask for it][suggest-feature].
Found a vulnerability? - Reach out to us via email first, see [security vulnerability disclosure][security-vulnerability].
## Support
[<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160" align="right">][support-patreon]
If you or your business use @panva/jose, please consider becoming a [Patron][support-patreon] so I can continue maintaining it and adding new features carefree. You may also donate one-time via [PayPal][support-paypal].
[<img src="https://cdn.jsdelivr.net/gh/gregoiresgt/payment-icons@183140a5ff8f39b5a19d59ebeb2c77f03c3a24d3/Assets/Payment/PayPal/Paypal@2x.png" width="100" align="right">][support-paypal]
## Documentation
- [@panva/jose API Documentation][documentation]
- [JWK (JSON Web Key)][documentation-jwk]
- [JWKS (JSON Web Key Set)][documentation-jwks]
- [JWS (JSON Web Signature)][documentation-jws]
- [JWE (JSON Web Encryption)][documentation-jwe]
## Usage
The minimal Node.js version supported is v11.8.0
The minimal Node.js version required is v11.8.0
Installing @panva/jose
```sh
$ npm install @panva/jose
```
Usage
```js
const jose = require('@panva/jose')
const {
JWE, // JSON Web Encryption (JWE)
JWK, // JSON Web Key (JWK)
JWKS, // JSON Web Key Set (JWKS)
JWS, // JSON Web Signature (JWS)
errors // errors utilized by @panva/jose
} = require('@panva/jose')
} = jose
```
## JWK (JSON Web Key)
#### Keys and KeyStores
All @panva/jose operations require `JWK.Key` or `JWKS.KeyStore` as arguments. Here's
how to get a `JWK.Key`.
#### Class: JWK `<RSAKey>` | `<ECKey>` | `<OctKey>`
`<RSAKey>`, `<ECKey>` and `<OctKey>` represent a key usable for JWS and JWE operations. The
`JWK.importKey()` method is used to retrieve a key representation of an existing key or secret.
`JWK.generate()` method is used to generate a new random key.
#### JWK `#importKey(key[, options])` asymmetric key import
Imports an asymmetric private or public key. Supports importing JWK formatted keys (private, public,
secrets), `pem` and `der` formatted private and public keys, `pem` formatted X.509 certificates.
Private keys may also be passphrase protected.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `key`: `<Object>` | `<string>` | `<Buffer>` | `<KeyObject>`
- `key`: `<string>` | `<Buffer>`
- `format`: `<string>` Must be 'pem' or 'der'. Default: 'pem'.
- `type`: `<string>` Must be 'pkcs1', 'pkcs8' or 'sec1'. This option is required only if the format is 'der' and ignored if it is 'pem'.
- `passphrase`: `<string>` | `<Buffer>` The passphrase to use for decryption.
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- Returns: `<RSAKey>` | `<ECKey>`
See the underlying Node.js API for details on importing private and public keys in the different formats
- [crypto.createPrivateKey(key)](https://nodejs.org/api/crypto.html#crypto_crypto_createprivatekey_key)
- [crypto.createPublicKey(key)](https://nodejs.org/api/crypto.html#crypto_crypto_createpublickey_key)
- [crypto.createSecretKey(key)](https://nodejs.org/api/crypto.html#crypto_crypto_createsecretkey_key)
</details>
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
Prepare your Keys and KeyStores. See the [documentation][documentation-jwk] for more.
```js
const { readFileSync } = require('fs')
const { JWK: { importKey } } = require('@panva/jose')
const key = jose.JWK.importKey(fs.readFileSync('path/to/key/file'))
const key = importKey(readFileSync('path/to/key/file'))
// ECKey {
// kty: 'EC',
// public: true,
// kid: [Getter],
// crv: [Getter],
// x: [Getter],
// y: [Getter] }
const jwk = { kty: 'EC',
kid: 'dl4M_fcI7XoFCsQ22PYrQBkuxZ2pDcbDimcdFmmXM98',
crv: 'P-256',
x: 'v37avifcL-xgh8cy6IFzcINqqmFLc2JF20XUpn4Y2uQ',
y: 'QTwy27XgP7ZMOdGOSopAHB-FU1JMQn3J9GEWGtUXreQ' }
const anotherKey = jose.JWK.importKey(jwk)
const keystore = new jose.JWK.KeyStore(key, key2)
```
</details>
#### Signing
#### JWK `#importKey(secret[, options])` secret key import
Imports a symmetric key.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `secret`: `<string>` | `<Buffer>` | `<KeyObject>`
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- Returns: `<OctKey>`
</details>
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
Sign with a private or symmetric key using compact serialization. See the [documentation][documentation-jws] for more.
```js
const { JWK: { importKey } } = require('@panva/jose')
const key = importKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ'))
// OctKey {
// kty: 'oct',
// kid: [Getter],
// k: [Getter] }
jose.JWS.sign(
{ sub: 'johndoe' },
privateKey
)
```
</details>
#### Verifying
#### JWK `#importKey(jwk)` JWK-formatted key import
Imports a JWK formatted key. This supports JWK formatted EC, RSA and oct keys. Asymmetrical keys
may be both private and public.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `jwk`: `<Object>`
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- `e`, `n` properties as `<string>` for RSA public keys
- `e`, `n`, `d`, `p`, `q`, `dp`, `dq`, `qi` properties as `<string>` for RSA private keys
- `crv`, `x`, `y` properties as `<string>` for EC public keys
- `crv`, `x`, `y`, `d` properties as `<string>` for EC private keys
- `k` properties as `<string>` for secret oct keys
- Returns: `<RSAKey>` | `<ECKey>` | `<OctKey>`
</details>
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
Verify with a public or symmetric key. See the [documentation][documentation-jws] for more.
```js
const { JWK: { importKey } } = require('@panva/jose')
const jwk = {
kty: 'RSA',
kid: 'r1LkbBo3925Rb2ZFFrKyU3MVex9T2817Kx0vbi6i_Kc',
use: 'sig',
e: 'AQAB',
n: 'xwQ72P9z9OYshiQ-ntDYaPnnfwG6u9JAdLMZ5o0dmjlcyrvwQRdoFIKPnO65Q8mh6F_LDSxjxa2Yzo_wdjhbPZLjfUJXgCzm54cClXzT5twzo7lzoAfaJlkTsoZc2HFWqmcri0BuzmTFLZx2Q7wYBm0pXHmQKF0V-C1O6NWfd4mfBhbM-I1tHYSpAMgarSm22WDMDx-WWI7TEzy2QhaBVaENW9BKaKkJklocAZCxk18WhR0fckIGiWiSM5FcU1PY2jfGsTmX505Ub7P5Dz75Ygqrutd5tFrcqyPAtPTFDk8X1InxkkUwpP3nFU5o50DGhwQolGYKPGtQ-ZtmbOfcWQ'
}
const key = importKey(jwk)
// RSAKey {
// kty: 'RSA',
// public: true,
// use: 'sig',
// kid: 'r1LkbBo3925Rb2ZFFrKyU3MVex9T2817Kx0vbi6i_Kc',
// e: [Getter],
// n: [Getter] }
jose.JWS.verify(
'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huZG9lIn0.T_SYLQV3A5_kFDDVNuoadoURSEtuSOR-dG2CMmrP-ULK9xbIf2vYeiHOkvTrnqGlWEGBGxYtsP1VkXmNsi1uOw',
publicKey
)
```
</details>
#### Encrypting
#### JWK `#generate(kty[, crvOrSize[, options[, private]]])` generating new keys
Encrypt using the recipient's public key or a shared symmetrical secret. See the [documentation][documentation-jwe] for more.
Securely generates a new RSA, EC or oct key.
```js
jose.JWE.encrypt(
'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJqb2huZG9lIn0.T_SYLQV3A5_kFDDVNuoadoURSEtuSOR-dG2CMmrP-ULK9xbIf2vYeiHOkvTrnqGlWEGBGxYtsP1VkXmNsi1uOw',
publicKey
)
```
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
#### Verifying
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `crvOrSize`: `<number>` | `<string>` key's bit size or in case of EC keys the curve
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- `private`: `<boolean>` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
- Returns: `Promise<RSAKey>` | `Promise<ECKey>` | `Promise<OctKey>`
Decrypt using the private key or a shared symmetrical secret. See the [documentation][documentation-jwe] for more.
</details>
```js
jose.JWE.decrypt(
'eyJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiYWxnIjoiRUNESC1FUyIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IkVsUGhsN1ljTVZsWkhHM0daSkRoOVJhemNYYlN2VFNheUF6aTBINFFtRUEiLCJ5IjoiM0hDREJTRy12emd6cGtLWmJqMU05UzVuUEJrTDBBdFM4U29ORUxMWE1SayJ9fQ..FhmidRo0twvFA7jcfKFNJw.o112vgiG_qUL1JR5WHpsErcxxgaK_FAa7vCWJ--WulndLpdwdRXHd9k3aL_k8K67xoAThrt10d7dSY2TlPpHdYkw979u0V-C4TNrpzNkv5jpBjU6hHyKpoGZfEsiTD1ivHaFy3ZLCTS69kN_eVKsZGLVf_dkq6Sz6bWE4-ln_fuwukPyMvjTyaTreLjPLBZW.ocKwptCm4Zn437L5hWFnHg',
privateKey
)
```
#### JWK `#generateSync(kty[, crvOrSize[, options[, private]]])`
## FAQ
Synchronous version of JWK `#generate`
#### Semver?
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
**Yes.** Everything that's either exported in the TypeScript definitions file or [documented][documentation]
is subject to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). The rest is to be
considered private API and is subject to change between any versions.
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `crvOrSize`: `<number>` | `<string>` key's bit size or in case of EC keys the curve. **Default** 2048 for RSA, 'P-256' for EC and 256 for oct.
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- `private`: `<boolean>` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
- Returns: `<RSAKey>` | `<ECKey>` | `<OctKey>`
#### How do I use it outside of Node.js
</details>
It is **only built for Node.js** environment - it builds on top of the `crypto` module and requires
the KeyObject API that was added in Node.js v11.6.0.
## JWKS (JSON Web Key Set)
#### How is it different from [`node-jose`][node-jose]
- [Class: KeyStore](#class-keystore)
- [new KeyStore([keys])](#new-keystorekeys)
- [keystore.all([parameters])](#keystoreallparameters)
- [keystore.get([parameters])](#keystoregetparameters)
- [keystore.add(key)](#keystoreaddkey)
- [keystore.remove(key)](#keystoreremovekey)
- [keystore.generate(...)](#keystoregenerate)
- [keystore.generateSync(...)](#keystoregeneratesync)
`node-jose` is built to work in any javascript runtime, to be able to do that it packs a lot of
backfill and javascript implementation code in the form of
[`node-forge`](https://github.com/digitalbazaar/forge), this significantly increases the footprint
of the module with dependencies that either aren't ever used or have native implementation available
in Node.js already, those are often times faster and more reliable.
#### Class: `KeyStore`
#### How is it different from [`node-jws`](https://github.com/brianloveswords/node-jws) or [`node-jwa`](https://github.com/brianloveswords/node-jwa)?
`KeyStore` is an abstraction representing a set of JWKs
- it is providing Key and KeyStore abstractions
- there is JSON Web Encryption support
- there is no asynchronous API since node crypto is ultimately entirely synchronous
- it supports all JWS / JWE Serialization Syntaxes
#### `new KeyStore([keys])`
#### What is the ultimate goal?
Creates a new KeyStore, either empty or populated.
- **No dependencies**, the moment JWK formatted keys are supported by node's `crypto` the direct
dependency count will go down from 1 to 0. 🚀
- Just the API one needs, having used other jose modules for 3+ years I only include what's useful
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
#### Why? Just, why?
- `keys`: `<Key[]>` Array of key keys instantiated by `JWK.importKey`
- Returns: `<KeyStore>`
I was / (still am) using [`node-jose`][node-jose] for [`openid-client`](https://github.com/panva/node-openid-client)
and [`oidc-provider`](https://github.com/panva/node-oidc-provider) and came to realize its shortcomings
in terms of performance and API (not having well defined errors). When Node.js v12 lands in April
2019 I will be releasing new major versions of both those libraries using @panva/jose.
</details>
#### `keystore.all([parameters])`
Retrieves an array of keys matching the provider parameters, returns all if none are provided. The
returned array is sorted by relevance based on the parameters. Keys with the exact algorithm or use
specified by the parameters are first.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `parameters`: `<Object>`
- `kty`: `<string>` Key Type to filter for.
- `alg`: `<string>` Key supported algorithm to filter for.
- `use`: `<string>` Key use to filter for.
- `kid`: `<string>` Key ID to filter for.
- Returns: `<Key[]>` Array of key instances or an empty array when none are matching the parameters.
</details>
#### `keystore.get([parameters])`
Retrieves a single key matching the provider parameters. The most relevant Key based on the
parameters is returned.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `parameters`: `<Object>`
- `kty`: `<string>` Key Type to filter for.
- `alg`: `<string>` Key supported algorithm to filter for.
- `use`: `<string>` Key use to filter for.
- `kid`: `<string>` Key ID to filter for.
- Returns: `<RSAKey>` | `<ECKey>` | `<OctKey>` | `<undefined>`
</details>
#### `keystore.add(key)`
Adds a key instance to the store unless it is already included.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `key`: `<RSAKey>` | `<ECKey>` | `<OctKey>`
</details>
#### `keystore.remove(key)`
Ensures a key is removed from a store.
<details>
<summary><em><strong>API</strong></em> (Click to expand)</summary>
- `key`: `<RSAKey>` | `<ECKey>` | `<OctKey>`
</details>
#### `keystore.generate(...)`
Asynchronously generates new random key and automatically adds it to the store. See `JWK.generate` for the API.
#### `keystore.generateSync(...)`
Synchronous version of `keystore.generate`.
&plus; this was an amazing opportunity to learn JOSE as a whole
#### Where's the performance coming from?
No endless stream of yielded promises, uses KeyObject instances for crypto operations, once a
KeyObject is instantiated the keys do not need to be "prepped" and validated any more in neither
the Node runtime nor the underlying OpenSSL implementation. In some cases this yields 2x throughput
for the actual crypto operation.
[node-jose]: https://github.com/cisco/node-jose
[documentation]: https://github.com/panva/jose/blob/master/docs/README.md
[documentation-jws]: https://github.com/panva/jose/blob/master/docs/README.md#jws-json-web-signature
[documentation-jwe]: https://github.com/panva/jose/blob/master/docs/README.md#jwe-json-web-encryption
[documentation-jwk]: https://github.com/panva/jose/blob/master/docs/README.md#jwk-json-web-key
[documentation-jwks]: https://github.com/panva/jose/blob/master/docs/README.md#jwks-json-web-key-set
[documentation]: https://github.com/panva/jose/blob/master/docs/README.md
[documentation]: https://github.com/panva/jose/blob/master/docs/README.md
[travis-image]: https://api.travis-ci.com/panva/jose.svg?branch=master
[travis-url]: https://travis-ci.com/panva/jose
[codecov-image]: https://img.shields.io/codecov/c/github/panva/jose/master.svg
[codecov-url]: https://codecov.io/gh/panva/jose
[suggest-feature]: https://github.com/panva/jose/issues/new?template=feature-request.md
[bug]: https://github.com/panva/jose/issues/new?template=bug-report.md
[suggest-feature]: https://github.com/panva/jose/issues/new?labels=enhancement&template=feature-request.md&title=proposal%3A+
[bug]: https://github.com/panva/jose/issues/new?labels=bug&template=bug-report.md&title=bug%3A+
[ask]: https://github.com/panva/jose/issues/new?labels=question&template=question.md&title=question%3A+
[security-vulnerability]: https://github.com/panva/jose/issues/new?template=security-vulnerability.md
[support-patreon]: https://www.patreon.com/panva
[support-paypal]: https://www.paypal.me/panva
[spec-jwa]: https://tools.ietf.org/html/rfc7518
@ -396,5 +264,6 @@ Synchronous version of `keystore.generate`.
[spec-jwe]: https://tools.ietf.org/html/rfc7516
[spec-b64]: https://tools.ietf.org/html/rfc7797
[spec-jwk]: https://tools.ietf.org/html/rfc7517
[spec-cfrg]: https://tools.ietf.org/html/rfc8037
[spec-thumbprint]: https://tools.ietf.org/html/rfc7638
[spec-cookbook]: https://tools.ietf.org/html/rfc7520

922
docs/README.md Normal file
View file

@ -0,0 +1,922 @@
# @panva/jose API Documentation
**Table of Contents**
- [JWK (JSON Web Key)](#jwk-json-web-key)
- [JWKS (JSON Web Key Set)](#jwks-json-web-key-set)
- [JWS (JSON Web Signature)](#jws-json-web-signature)
- [JWE (JSON Web Encryption)](#jwe-json-web-encryption)
## Support
[<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160" align="right">][support-patreon]
If you or your business use @panva/jose, please consider becoming a [Patron][support-patreon] so I can continue maintaining it and adding new features carefree. You may also donate one-time via [PayPal][support-paypal].
[<img src="https://cdn.jsdelivr.net/gh/gregoiresgt/payment-icons@183140a5ff8f39b5a19d59ebeb2c77f03c3a24d3/Assets/Payment/PayPal/Paypal@2x.png" width="100" align="right">][support-paypal]
<br>
---
## JWK (JSON Web Key)
<!-- TOC JWK START -->
- [Class: &lt;JWK.Key&gt; and &lt;JWK.RSAKey&gt; | &lt;JWK.ECKey&gt; | &lt;JWK.OctKey&gt;](#class-jwkkey-and-jwkrsakey--jwkeckey--jwkoctkey)
- [key.kty](#keykty)
- [key.alg](#keyalg)
- [key.use](#keyuse)
- [key.kid](#keykid)
- [key.public](#keypublic)
- [key.private](#keyprivate)
- [key.algorithms([operation])](#keyalgorithmsoperation)
- [key.toJWK([private])](#keytojwkprivate)
- JWK.importKey
- [JWK.importKey(key[, options]) asymmetric key import](#jwkimportkeykey-options-asymmetric-key-import)
- [JWK.importKey(secret[, options]) secret key import](#jwkimportkeysecret-options-secret-key-import)
- [JWK.importKey(jwk) JWK-formatted key import](#jwkimportkeyjwk-jwk-formatted-key-import)
- [JWK.generate(kty[, crvOrSize[, options[, private]]]) generating new keys](#jwkgeneratekty-crvorsize-options-private-generating-new-keys)
- [JWK.generateSync(kty[, crvOrSize[, options[, private]]])](#jwkgeneratesynckty-crvorsize-options-private)
- [JWK.isKey(object)](#jwkiskeyobject)
<!-- TOC JWK END -->
All @panva/jose operations require `<JWK.Key>` or `<JWKS.KeyStore>` as arguments. Here's
how to get a `<JWK.Key>` instances generated or instantiated from existing key material.
```js
const { JWK } = require('@panva/jose')
// { importKey: [Function: importKey],
// generate: [AsyncFunction: generate],
// generateSync: [Function: generateSync] }
```
---
#### Class: `<JWK.Key>` and `<JWK.RSAKey>` | `<JWK.ECKey>` | `<JWK.OctKey>`
`<JWK.RSAKey>`, `<JWK.ECKey>` and `<JWK.OctKey>` represent a key usable for JWS and JWE operations. The
`JWK.importKey()` method is used to retrieve a key representation of an existing key or secret.
`JWK.generate()` method is used to generate a new random key.
`<JWK.RSAKey>`, `<JWK.ECKey>` and `<JWK.OctKey>` inherit methods from `<JWK.Key>` and in addition
to the properties documented below have the respective key component properties exported as `<string>`
in their format defined by the specifications.
- `e, n` for Public RSA Keys
- `e, n, d, p, q, dp, dq, qi` for Private RSA Keys
- `crv, x, y` for Public EC Keys
- `crv, x, y, n` for Private EC Keys
- `k` for Symmetric keys
---
#### `key.kty`
Returns the key's JWK Key Type Parameter. 'EC', 'RSA' or 'oct' for the respective supported key types
- `<string>`
---
#### `key.alg`
Returns the key's JWK Algorithm Parameter if set, undefined otherwise. If set the key is only usable
for that one algorithm and will fail when used with others.
- `<string>`
---
#### `key.use`
Returns the key's JWK Key Use Parameter if set, undefined otherwise. Only 'sig' and 'enc' values
are supported. If set the key can only be used for either signing / verification or encryption
related operations (key management or encryption)
- `<string>`
---
#### `key.kid`
Returns the key's JWK Key ID Parameter if set, if not set it will be calculated using the method
defined in [RFC7638][spec-thumbprint]
- `<string>`
---
#### `key.public`
Returns true/false if the key is asymmetric and public. Returns false for symmetric keys.
- `<boolean>`
---
#### `key.private`
Returns true/false if the key is asymmetric and private. Returns false for symmetric keys.
- `<boolean>`
---
#### `key.algorithms([operation])`
Returns a [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)
of algorithms the key may perform.
- `operation`: `<string>` Must be one of 'encrypt', 'decrypt', 'sign', 'verify', 'wrapKey', 'unwrapKey'
- Returns: `Set<string>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK: { generateSync } } = require('@panva/jose')
const privateKey = generateSync('RSA')
privateKey.algorithms()
// Set {
// 'PS256',
// 'RS256',
// 'PS384',
// 'RS384',
// 'PS512',
// 'RS512',
// 'RSA-OAEP',
// 'RSA1_5' }
privateKey.algorithms('wrapKey')
// Set {
// 'RSA-OAEP',
// 'RSA1_5' }
const publicKey = generateSync('RSA', 2048, { use: 'enc' }, false)
publicKey.algorithms('sign')
// Set {}
publicKey.algorithms('unwrapKey')
// Set {}
publicKey.algorithms('wrapKey')
// Set {
// 'RSA-OAEP',
// 'RSA1_5' }
```
</details>
---
#### `key.toJWK([private])`
Exports the key to a JSON Web Key formatted object.
- `private`: `<boolean>` When true exports keys with its private components. **Default:** 'false'
- Returns: `<Object>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK: { generateSync } } = require('@panva/jose')
const key = generateSync('RSA', 2048, { use: 'sig', alg: 'PS256' })
key.toJWK()
// { kty: 'RSA',
// kid: 'UFldqYiAzlc1aGj5SoqxqYnWcv2Nc4us2ryQe3-FsUA',
// e: 'AQAB',
// n:
// 'uKEKEJUrnfBdXr6zmzq91fQHhW_8GFFUAYtvt5Uvj9wzsWDbspfL9MorhJgkPioo9T6QQvyyEJBaAQOLZxLsPORk83vmB9OACQT3PEM2LbSFK7XUoZGwqlf8Anvs7M1GwvypYbc1v1WrCqcsjrbmYF9TZkV8nNsy2cweh9gFNR-lIiZCHWDgnP6PifoeGvC9RxKdusFa66vtUJGUcoVmMoiOM7EDVdYOP91qJtbDBx7NPPywwD-8pt3UVBW0bYvOqHGF6XXky5JiB8AZQ2NdZHWxklaM2fd8Mxu9CT3xSYg51nS0KV7wO9lAh_ynBpxE2Qmr-7nvKkkDMOL1FSoEQw',
// alg: 'PS256',
// use: 'sig' }
key.toJWK(true)
// { kty: 'RSA',
// kid: 'UFldqYiAzlc1aGj5SoqxqYnWcv2Nc4us2ryQe3-FsUA',
// e: 'AQAB',
// n:
// 'uKEKEJUrnfBdXr6zmzq91fQHhW_8GFFUAYtvt5Uvj9wzsWDbspfL9MorhJgkPioo9T6QQvyyEJBaAQOLZxLsPORk83vmB9OACQT3PEM2LbSFK7XUoZGwqlf8Anvs7M1GwvypYbc1v1WrCqcsjrbmYF9TZkV8nNsy2cweh9gFNR-lIiZCHWDgnP6PifoeGvC9RxKdusFa66vtUJGUcoVmMoiOM7EDVdYOP91qJtbDBx7NPPywwD-8pt3UVBW0bYvOqHGF6XXky5JiB8AZQ2NdZHWxklaM2fd8Mxu9CT3xSYg51nS0KV7wO9lAh_ynBpxE2Qmr-7nvKkkDMOL1FSoEQw',
// d:
// 'F9G24bLNAMBM23dQ5prqeNrVyZJL_LspUlWx4QZfL3kiNiUf0uegiYE3ohCaxGZeCF288Nd3BYoKAo15g5--WJDCsWLvp1zS7Nb2KpElQTpD4ALCXuHT3_Yf7hYc1-QX1_oOxCuFxJyBx4sPxY21JQPHV69pRzdEVTLvUWk-Kr8k-kgu8xFOsyqLK0g0IBAtwOX2ksIPLuHT-nGh_VQwfpJowq1MoUZD-y_6Ai5HWAZy9t6gARpG3K4yBcmAQBRIQgoFiGw41BqVB5fJyjVZDsMbvT_iEFKkrHRjifUI6QTNtt1k9xOFIL_Ojzn6aLylm58AGD8oORWZvfpmJJ03yQ',
// p:
// '8lvcv5Ov9rJsa_kaCJBRijeOdz3La11_26o2QDpkINFKKoDNWRpIT0KZNF4P16Z5OXOK6rSezuN2vACAPg3riUHVdbRyFhMI6FvQhlx7unyv07xBBqbnp8dV2NiQv3-rFeNPV_5RqZHJyqQga-VUXvwics3eUzm_2CbrMQG3Klc',
// q:
// 'wwVZ6d5uZm9kj3tWICF1FqCWHwSWMt1wgFZ3DOp2LPuqBHjYPas4zwXd3V4wolaCi8irbTfbL0F6c51yN72-enAjgm6r5yzxedkV9GWk5U0y8VrNwYm55qz1o88LB6PX6RG5Lp2rYZp_34dgCrllQc8T-5YY4KIHy7TaLkKkGfU',
// dp:
// 'sPZseCIxcPOVAT3xSWF_eGnah6zCVJH_4vglBr7cD65h9ij4R-BN_jnFvhwUe0Ud7No2C-x4rN4f-2RuP2FQo3dDkt-AEigx79_iocjzuxaCGBu0a1QBgFunjl-LSZjB5oiEjd6v6B4AdwtidQYNlhGKYcN6W9CmCQFZ5_21rZ8',
// dq:
// 'sokKmGSuUw61U_mIjh-zDoTzCfBsBKLepE8D7AoVJ_c43aE37bT7a-MmCst44JUsLAYIkhMpkKh0DrXb45XMdFCG4ZipvRhS9Ma9J6GKBPXYpkYHyZ9pVfmPY2he456mQdOc4UUsqU0EtcE8NnUlcsq9s3vkyHjthBrMBr-xdaU',
// qi:
// 'jbZrzP8f3y0-ZAjqSQAPbKnQI0Vli952nQTUgffF2Bh2q0dB719PHjmIV7NjwCFOMcNx-2usJFwI9VikgN9GTGauakvG7SFzXD8yHiRzFwcjYvXDuJ-4Q1Yjo1m4JUIW_BLVnzauSg0P9qnxT1dxvchEQRIIfF72FW80BsJD4LQ',
// alg: 'PS256',
// use: 'sig' }
```
</details>
---
#### `JWK.importKey(key[, options])` asymmetric key import
Imports an asymmetric private or public key. Supports importing JWK formatted keys (private, public,
secrets), `pem` and `der` formatted private and public keys, `pem` formatted X.509 certificates.
Private keys may also be passphrase protected.
- `key`: `<Object>` | `<string>` | `<Buffer>` | `<KeyObject>`
- `key`: `<string>` | `<Buffer>`
- `format`: `<string>` Must be 'pem' or 'der'. **Default:** 'pem'.
- `type`: `<string>` Must be 'pkcs1', 'pkcs8' or 'sec1'. This option is required only if the format is 'der' and ignored if it is 'pem'.
- `passphrase`: `<string>` | `<Buffer>` The passphrase to use for decryption.
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- Returns: `<JWK.RSAKey>` | `<JWK.ECKey>`
See the underlying Node.js API for details on importing private and public keys in the different formats
- [crypto.createPrivateKey(key)](https://nodejs.org/api/crypto.html#crypto_crypto_createprivatekey_key)
- [crypto.createPublicKey(key)](https://nodejs.org/api/crypto.html#crypto_crypto_createpublickey_key)
- [crypto.createSecretKey(key)](https://nodejs.org/api/crypto.html#crypto_crypto_createsecretkey_key)
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { readFileSync } = require('fs')
const { JWK: { importKey } } = require('@panva/jose')
const key = importKey(readFileSync('path/to/key/file'))
// ECKey {
// kty: 'EC',
// public: true,
// kid: [Getter],
// crv: [Getter],
// x: [Getter],
// y: [Getter] }
```
</details>
---
#### `JWK.importKey(secret[, options])` secret key import
Imports a symmetric key.
- `secret`: `<string>` | `<Buffer>` | `<KeyObject>`
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- Returns: `<JWK.OctKey>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK: { importKey } } = require('@panva/jose')
const key = importKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ'))
// OctKey {
// kty: 'oct',
// kid: [Getter],
// k: [Getter] }
```
</details>
---
#### `JWK.importKey(jwk)` JWK-formatted key import
Imports a JWK formatted key. This supports JWK formatted EC, RSA and oct keys. Asymmetrical keys
may be both private and public.
- `jwk`: `<Object>`
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- `e`, `n` properties as `<string>` for RSA public keys
- `e`, `n`, `d`, `p`, `q`, `dp`, `dq`, `qi` properties as `<string>` for RSA private keys
- `crv`, `x`, `y` properties as `<string>` for EC public keys
- `crv`, `x`, `y`, `d` properties as `<string>` for EC private keys
- `k` properties as `<string>` for secret oct keys
- Returns: `<JWK.RSAKey>` | `<JWK.ECKey>` | `<JWK.OctKey>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK: { importKey } } = require('@panva/jose')
const jwk = {
kty: 'RSA',
kid: 'r1LkbBo3925Rb2ZFFrKyU3MVex9T2817Kx0vbi6i_Kc',
use: 'sig',
e: 'AQAB',
n: 'xwQ72P9z9OYshiQ-ntDYaPnnfwG6u9JAdLMZ5o0dmjlcyrvwQRdoFIKPnO65Q8mh6F_LDSxjxa2Yzo_wdjhbPZLjfUJXgCzm54cClXzT5twzo7lzoAfaJlkTsoZc2HFWqmcri0BuzmTFLZx2Q7wYBm0pXHmQKF0V-C1O6NWfd4mfBhbM-I1tHYSpAMgarSm22WDMDx-WWI7TEzy2QhaBVaENW9BKaKkJklocAZCxk18WhR0fckIGiWiSM5FcU1PY2jfGsTmX505Ub7P5Dz75Ygqrutd5tFrcqyPAtPTFDk8X1InxkkUwpP3nFU5o50DGhwQolGYKPGtQ-ZtmbOfcWQ'
}
const key = importKey(jwk)
// RSAKey {
// kty: 'RSA',
// public: true,
// use: 'sig',
// kid: 'r1LkbBo3925Rb2ZFFrKyU3MVex9T2817Kx0vbi6i_Kc',
// e: [Getter],
// n: [Getter] }
```
</details>
---
#### `JWK.generate(kty[, crvOrSize[, options[, private]]])` generating new keys
Securely generates a new RSA, EC or oct key.
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `crvOrSize`: `<number>` | `<string>` key's bit size or in case of EC keys the curve
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- `private`: `<boolean>` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
- Returns: `Promise<JWK.RSAKey>` | `Promise<JWK.ECKey>` | `Promise<JWK.OctKey>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK: { generate } } = require('@panva/jose')
(async () => {
const key = await generate('EC', 'P-384', { use: 'sig' })
// ECKey {
// kty: 'EC',
// private: true,
// use: 'sig',
// kid: [Getter],
// crv: [Getter],
// x: [Getter],
// y: [Getter],
// d: [Getter] }
})()
```
</details>
---
#### `JWK.generateSync(kty[, crvOrSize[, options[, private]]])`
Synchronous version of `JWK.generate()`
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `crvOrSize`: `<number>` | `<string>` key's bit size or in case of EC keys the curve. **Default** 2048 for RSA, 'P-256' for EC and 256 for oct.
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- `private`: `<boolean>` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
- Returns: `<JWK.RSAKey>` | `<JWK.ECKey>` | `<JWK.OctKey>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK: { generateSync } } = require('@panva/jose')
const key = generateSync('RSA', 2048, { use: 'enc' })
// RSAKey {
// kty: 'RSA',
// private: true,
// use: 'enc',
// kid: [Getter],
// e: [Getter],
// n: [Getter],
// d: [Getter],
// p: [Getter],
// q: [Getter],
// dp: [Getter],
// dq: [Getter],
// qi: [Getter] }
```
</details>
---
#### `JWK.isKey(object)`
Returns 'true' if the value is an instance of `<JWK.Key>`.
- `object`: `<any>`
- Returns: `<boolean>`
---
## JWKS (JSON Web Key Set)
<!-- TOC JWKS START -->
- [Class: <JWKS.KeyStore>](#class-jwkskeystore)
- [new JWKS.KeyStore([keys])](#new-jwkskeystorekeys)
- [keystore.size](#keystoresize)
- [keystore.all([parameters])](#keystoreallparameters)
- [keystore.get([parameters])](#keystoregetparameters)
- [keystore.add(key)](#keystoreaddkey)
- [keystore.remove(key)](#keystoreremovekey)
- [keystore.generate(...)](#keystoregenerate)
- [keystore.generateSync(...)](#keystoregeneratesync)
- [keystore.toJWKS([private])](#keystoretojwksprivate)
- [JWKS.KeyStore.fromJWKS(jwks)](#jwkskeystorefromjwksjwks)
<!-- TOC JWKS END -->
```js
const { JWKS } = require('@panva/jose')
// { KeyStore: [Function: KeyStore] }
```
#### Class: `<JWKS.KeyStore>`
`JWKS.KeyStore` is an abstraction representing a set of JWKs, a keystore instance may be queried for
keys matching specific parameters. Keystores may be instantiated either populated, or empty and there
are lifecycle `keystore.remove()` and `keystore.add()` methods for adding/removing keys from an existing
store.
---
#### `new JWKS.KeyStore([keys])`
Creates a new KeyStore, either empty or populated.
- `keys`: `<JWK.Key[]>` Array of key keys instantiated by `JWK.importKey()`
- Returns: `<JWKS.KeyStore>`
---
#### `keystore.size`
Returns the number of keys in the keystore.
- `<number>`
---
#### `keystore.all([parameters])`
Retrieves an array of keys matching the provider parameters, returns all if none are provided. The
returned array is sorted by relevance based on the parameters. Keys with the exact algorithm or use
specified by the parameters are first.
- `parameters`: `<Object>`
- `kty`: `<string>` Key Type to filter for.
- `alg`: `<string>` Key supported algorithm to filter for.
- `use`: `<string>` Key use to filter for.
- `kid`: `<string>` Key ID to filter for.
- Returns: `<Key[]>` Array of key instances or an empty array when none are matching the parameters.
---
#### `keystore.get([parameters])`
Retrieves a single key matching the provider parameters. The most relevant Key based on the
parameters is returned.
- `parameters`: `<Object>`
- `kty`: `<string>` Key Type to filter for.
- `alg`: `<string>` Key supported algorithm to filter for.
- `use`: `<string>` Key use to filter for.
- `kid`: `<string>` Key ID to filter for.
- Returns: `<JWK.RSAKey>` | `<JWK.ECKey>` | `<JWK.OctKey>` | `<undefined>`
---
#### `keystore.add(key)`
Adds a key instance to the store unless it is already included.
- `key`: `<JWK.RSAKey>` | `<JWK.ECKey>` | `<JWK.OctKey>`
---
#### `keystore.remove(key)`
Ensures a key is removed from a store.
- `key`: `<JWK.RSAKey>` | `<JWK.ECKey>` | `<JWK.OctKey>`
---
#### `keystore.generate(...)`
Asynchronously generates new random key and automatically adds it to the store. See `JWK.generate()` for the API.
---
#### `keystore.generateSync(...)`
Synchronous version of `keystore.generate()`.
---
#### `keystore.toJWKS([private])`
Exports the keystore to a JSON Web Key Set formatted object.
- `private`: `<boolean>` When true exports keys with their private components. **Default:** 'false'
- Returns: `<Object>`
---
#### `JWKS.KeyStore.fromJWKS(jwks)`
Creates a new KeyStore from a JSON Web Key Set.
- `jwks`: `<Object>` JWKS formatted object (`{ keys: [{ kty: '...', ... }, ...] }`)
- Returns: `<JWKS.KeyStore>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWKS: { KeyStore } } = require('@panva/jose')
const jwks = { keys:
[ { kty: 'RSA',
kid: 'gqUcZ2TjhmNrVOd1d27tedkabhOTs9WghMHIyjIBn7Y',
e: 'AQAB',
n:
'vi1Aui6R0rUL_7pdcFKKMhBF25h4x8WiTZ4w66eNZhwIp48lz-vBuyUUrSR-RwcuvnxlXdjBdSaN-PZkNRDv2bXE3mVtjZgoYyzQlGLJ1wduQaBXIkrQWxc7yzL91MvtP1kWwFHHrQHZRlpiFQQm9gNCy2wXCTbWGT9kjrR1W1bkwhmOKK4rF-hMgaCNDrtEQ6xWknxV8aXW4itouJ0pJv8xplc6J14f_SNq6arVUcAZ26EzJYC2fcvqwsrnKzvW7QxQGQzh-u9Tn82Tl14Omh1KDV8C7Vb_m8XClv_9zOrKBGdaTl1zgINyMEaa_IMophnBgK_kAXvtVvEThQ93GQ',
use: 'enc' } ] }
const ks = KeyStore.fromJWKS(jwks)
// KeyStore {}
ks.size
// 1
```
</details>
---
## JWS (JSON Web Signature)
<!-- TOC JWS START -->
- [Class: &lt;JWS.Sign&gt;](#class-jwssign)
- [new JWS.Sign(payload)](#new-jwssignpayload)
- [sign.recipient(key[, protected[, header]])](#signrecipientkey-protected-header)
- [sign.sign(serialization)](#signsignserialization)
- [JWS.sign(payload, key[, protected])](#jwssignpayload-key-protected)
- [JWS.sign.flattened(payload, key[, protected[, header]])](#jwssignflattenedpayload-key-protected-header)
- [JWS.verify(jws, keyOrStore[, options])](#jwsverifyjws-keyorstore-options)
<!-- TOC JWS END -->
The `<JWS>` module provides methods required to sign or verify JSON Web Signatures in either one of
the defined serializations.
```js
const { JWS } = require('@panva/jose')
// { Sign: [Function: Sign],
// sign:
// { [Function: bound single]
// flattened: [Function: bound single] },
// verify: [Function: bound jwsVerify] }
```
#### Class: `<JWS.Sign>`
`<JWS.Sign>` is the class used when you need to produce a JWS for multiple recipients (with multiple
signatures of the same payload) using the General JWS JSON Serialization Syntax.
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK, JWS } = require('@panva/jose')
const key = JWK.importKey({
kty: 'oct',
k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg'
})
const key2 = JWK.importKey({
kty: 'oct',
k: 'AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8'
})
const payload = {
sub: 'John Doe'
}
const sig = new JWS.Sign(payload)
sig.recipient(key, { alg: 'HS256' }, { foo: 'bar' })
sig.recipient(key2, { alg: 'HS512' }, { foo: 'baz' })
sig.sign('general')
// { payload: 'eyJzdWIiOiJKb2huIERvZSJ9',
// signatures:
// [ { protected: 'eyJhbGciOiJIUzI1NiJ9',
// header: { foo: 'bar' },
// signature: 'mnBcKK-9setCco03NtYws-RMlYXP3LGlDu2RUB7vetQ' },
// { protected: 'eyJhbGciOiJIUzUxMiJ9',
// header: { foo: 'baz' },
// signature:
// 'R7e5ZUkgiZQLh8JagoCbwAY21e9A-Y0rhUGQkhihLOvIU8JG2AyZ9zROOUICaUucf8NQKc2dEaIKdRCXy-fDdQ' } ] }
```
</details>
---
#### `new JWS.Sign(payload)`
Creates a new Sign object for the provided payload, intended for one or more recipients.
- `payload`: `<Object>` | `<string>` | `<Buffer>` The payload that will be signed. When `<Object>` it
will be automatically serialized to JSON before signing
- Returns: `<JWS.Sign>`
---
#### `sign.recipient(key[, protected[, header]])`
Adds a recipient to the JWS, the Algorithm that will be used to sign with is either provided as part
of the Protected or Unprotected Header or inferred from the provided `<JWK.Key>` instance.
- `key`: `<JWK.Key>` The key to sign with.
- `protected`: `<Object>` Protected Header for this recipient
- `header`: `<Object>` Unprotected Header for this recipient
---
#### `sign.sign(serialization)`
Performs the signing operations for each registered recipient and returns the final JWS representation
in the serialization requested. The JWS is validated for conformance during this step. Please note
that only 'general' and 'flattened' serialization supports Unprotected Per-Recipient Header and only
the 'general' serialization supports multiple recipients. See `<JWS.sign>` and `<JWS.sign.flattened>`
for shorthand methods to sign for a single recipient.
- `serialization`: `<string>` JWS Serialization. Must be one of 'general', 'flattened', 'compact'
- Returns: `<string>` | `<Object>`
---
#### `JWS.sign(payload, key[, protected])`
Performs the signing operation and 'compact' JWS serialization of the result. The Algorithm that
will be used to sign with is either provided as part of the Protected Header or inferred from the
provided `<JWK.Key>` instance.
- `payload`: `<Object>` | `<string>` | `<Buffer>` The payload that will be signed. When `<Object>` it
will be automatically serialized to JSON before signing
- `key`: `<JWK.Key>` The key to sign with.
- `protected`: `<Object>` Protected Header
- Returns: `<string>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK, JWS } = require('@panva/jose')
const key = JWK.importKey({
kty: 'oct',
k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg'
})
const payload = {
sub: 'John Doe'
}
JWS.sign(payload, key, { alg: 'HS256' })
// eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2huIERvZSJ9.mnBcKK-9setCco03NtYws-RMlYXP3LGlDu2RUB7vetQ
```
</details>
---
#### `JWS.sign.flattened(payload, key[, protected[, header]])`
Performs the signing operation and 'flattened' JWS serialization of the result. The Algorithm that
will be used to sign with is either provided as part of the Protected or Unprotected Header or inferred from the
provided `<JWK.Key>` instance.
- `payload`: `<Object>` | `<string>` | `<Buffer>` The payload that will be signed. When `<Object>` it
will be automatically serialized to JSON before signing
- `key`: `<JWK.Key>` The key to sign with.
- `protected`: `<Object>` Protected Header
- `header`: `<Object>` Unprotected Header
- Returns: `<Object>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK, JWS } = require('@panva/jose')
const key = JWK.importKey({
kty: 'oct',
k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg'
})
const payload = {
sub: 'John Doe'
}
JWS.sign.flattened(payload, key)
// { payload: 'eyJzdWIiOiJKb2huIERvZSJ9',
// protected: 'eyJhbGciOiJIUzI1NiJ9',
// signature: 'mnBcKK-9setCco03NtYws-RMlYXP3LGlDu2RUB7vetQ' }
```
</details>
---
#### `JWS.verify(jws, keyOrStore[, options])`
Verifies the provided JWS in either serialization with a given `<JWK.Key>` or `<JWKS.KeyStore>`
- `jws`: `<Object>` | `<string>` The JWS to verify. This must be a valid JWS.
- `keyOrStore`: `<JWK.Key>` | `<JWKS.KeyStore>` The key or store to verify with. When `<JWKS.KeyStore>`
instance is provided a selection of possible candidate keys will be done and the operation will
succeed if just one key or signature (in case of General JWS JSON Serialization Syntax) matches.
- `options`: `<Object>`
- `algorithms`: `string[]` Array of Algorithms to accept, when the signature does not use an
algorithm from this list the verification will fail. **Default:** 'undefined' - accepts all
algorithms available on the keys
- `complete`: `<boolean>` When true returns a complete object with the parsed headers and payload
instead of just the verified payload. **Default:** 'false'
- `crit`: `string[]` Array of Critical Header Parameter names to recognize. **Default:** '[]'
- Returns: `<string>` | `<Object>`
<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
```js
const { JWK, JWS, JWKS } = require('@panva/jose')
const key = JWK.importKey({
kty: 'oct',
k: 'hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg'
})
const key2 = JWK.importKey({
kty: 'oct',
k: 'AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8'
})
const compact = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2huIERvZSJ9.mnBcKK-9setCco03NtYws-RMlYXP3LGlDu2RUB7vetQ'
const flattened = { payload: 'eyJzdWIiOiJKb2huIERvZSJ9',
protected: 'eyJhbGciOiJIUzI1NiJ9',
signature: 'mnBcKK-9setCco03NtYws-RMlYXP3LGlDu2RUB7vetQ' }
const general = { payload: 'eyJzdWIiOiJKb2huIERvZSJ9',
signatures:
[ { protected: 'eyJhbGciOiJIUzI1NiJ9',
header: { foo: 'bar' },
signature: 'mnBcKK-9setCco03NtYws-RMlYXP3LGlDu2RUB7vetQ' },
{ protected: 'eyJhbGciOiJIUzUxMiJ9',
header: { foo: 'baz' },
signature:
'R7e5ZUkgiZQLh8JagoCbwAY21e9A-Y0rhUGQkhihLOvIU8JG2AyZ9zROOUICaUucf8NQKc2dEaIKdRCXy-fDdQ' } ] }
JWS.verify(compact, key)
// { sub: 'John Doe' }
JWS.verify(flattened, key2)
// JWSVerificationFailed: signature verification failed
JWS.verify(compact, key, { complete: true })
// { payload: { sub: 'John Doe' }, protected: { alg: 'HS256' }, key: OctKey {} }
JWS.verify(flattened, key, { algorithms: ['PS256'] })
// JOSEAlgNotWhitelisted: alg not whitelisted
JWS.verify(general, key)
// { sub: 'John Doe' }
JWS.verify(general, key2)
// { sub: 'John Doe' }
JWS.verify(general, key, { complete: true })
// { payload: { sub: 'John Doe' },
// protected: { alg: 'HS256' },
// header: { foo: 'bar' },
// key: : OctKey {} } <- key
JWS.verify(general, key2, { complete: true })
// { payload: { sub: 'John Doe' },
// protected: { alg: 'HS512' },
// header: { foo: 'baz' },
// key: : OctKey {} } <- key2
const keystore = new JWKS.KeyStore(key)
JWS.verify(general, keystore, { complete: true })
// { payload: { sub: 'John Doe' },
// protected: { alg: 'HS256' },
// header: { foo: 'bar' },
// key: : OctKey {} } <- key that matched in the keystore
```
</details>
---
## JWE (JSON Web Encryption)
<!-- TOC JWE START -->
- [Class: &lt;JWE.Encrypt&gt;](#class-jweencrypt)
- [new JWE.Encrypt(cleartext[, protected[, unprotected[, aad]]])](#new-jweencryptcleartext-protected-unprotected-aad)
- [encrypt.recipient(key[, header])](#encryptrecipientkey-header)
- [encrypt.encrypt(serialization)](#encryptencryptserialization)
- [JWE.encrypt(cleartext, key[, protected])](#jweencryptcleartext-key-protected)
- [JWE.encrypt.flattened(cleartext, key[, protected[, header[, aad]]])](#jweencryptflattenedcleartext-key-protected-header-aad)
- [JWE.decrypt(jwe, keyOrStore[, options])](#jwedecryptjwe-keyorstore-options)
<!-- TOC JWE END -->
The `<JWE>` module provides methods required to encrypt or decrypt JSON Web Encryptions in either one of
the defined serializations.
```js
const { JWE } = require('@panva/jose')
// { Encrypt: [Function: Encrypt],
// encrypt:
// { [Function: bound single]
// flattened: [Function: bound single] },
// decrypt: [Function: bound jweDecrypt] }
```
#### Class: `<JWE.Encrypt>`
`<JWE.Encrypt>` is the class used when you need to produce a JWE for multiple recipients using the
General JWE JSON Serialization Syntax.
---
#### `new JWE.Encrypt(cleartext[, protected[, unprotected[, aad]]])`
Creates a new Encrypt object for the provided cleartext with optional Protected and Unprotected
Headers and Additional Authenticated Data.
- `cleartext`: `<string>` | `<Buffer>` The cleartext that will be encrypted.
- `protected`: `<Object>` JWE Protected Header
- `unprotected`: `<Object>` JWE Shared Unprotected Header
- `aad`: `<string>` | `<Buffer>` JWE Additional Authenticated Data
- Returns: `<JWE.Encrypt>`
---
#### `encrypt.recipient(key[, header])`
Adds a recipient to the JWE, the Algorithm that will be used to wrap or derive the Content Encryption
Key (CEK) is either provided as part of the combined JWE Header for the recipient or inferred from
the provided `<JWK.Key>` instance.
- `key`: `<JWK.Key>` The key to use for Key Management or Direct Encryption
- `header`: `<Object>` JWE Per-Recipient Unprotected Header
---
#### `encrypt.encrypt(serialization)`
Performs the encryption operations for each registered recipient and returns the final JWE representation
in the serialization requested. The JWE is validated for conformance during this step. Please note
that only 'general' and 'flattened' serialization supports Unprotected Per-Recipient Header and AAD
and only the 'general' serialization supports multiple recipients. See `<JWE.encrypt>` and `<JWE.encrypt.flattened>`
for shorthand methods to encrypt for a single recipient.
- `serialization`: `<string>` JWE Serialization. Must be one of 'general', 'flattened', 'compact'
- Returns: `<string>` | `<Object>`
---
#### `JWE.encrypt(cleartext, key[, protected])`
Performs the encryption operation and 'compact' JWE serialization of the result. The Algorithm that
will be used to wrap or derive the Content Encryption Key (CEK) is either provided as part of the
Protected Header or inferred from the provided `<JWK.Key>` instance.
- `cleartext`: `<string>` | `<Buffer>` The cleartext that will be encrypted.
- `key`: `<JWK.Key>` The key to use for Key Management or Direct Encryption
- `protected`: `<Object>` JWE Protected Header
- Returns: `<string>`
---
#### `JWE.encrypt.flattened(cleartext, key[, protected[, header[, aad]]])`
Performs the encryption operation and 'flattened' JWE serialization of the result. The Algorithm that
will be used to wrap or derive the Content Encryption Key (CEK) is either provided as part of the
combined JWE Header or inferred from the provided `<JWK.Key>` instance.
- `cleartext`: `<string>` | `<Buffer>` The cleartext that will be encrypted.
- `key`: `<JWK.Key>` The key to use for Key Management or Direct Encryption
- `protected`: `<Object>` JWE Protected Header
- `unprotected`: `<Object>` JWE Shared Unprotected Header
- `aad`: `<string>` | `<Buffer>` JWE Additional Authenticated Data
- Returns: `<Object>`
---
#### `JWE.decrypt(jwe, keyOrStore[, options])`
Verifies the provided JWE in either serialization with a given `<JWK.Key>` or `<JWKS.KeyStore>`
- `jwe`: `<Object>` | `<string>` The JWE to decrypt. This must be a valid JWE.
- `keyOrStore`: `<JWK.Key>` | `<JWKS.KeyStore>` The key or store to decrypt with. When `<JWKS.KeyStore>`
instance is provided a selection of possible candidate keys will be done and the operation will
succeed if just one key or signature (in case of General JWE JSON Serialization Syntax) matches.
- `options`: `<Object>`
- `algorithms`: `string[]` Array of Algorithms to accept, when the JWE does not use an
Key Management algorithm from this list the decryption will fail. **Default:** 'undefined' -
accepts all algorithms available on the keys
- `complete`: `<boolean>` When true returns a complete object with the parsed headers, verified AAD
and cleartext instead of just the cleartext. **Default:** 'false'
- Returns: `<string>` | `<Object>`
[spec-thumbprint]: https://tools.ietf.org/html/rfc7638
[support-patreon]: https://www.patreon.com/panva
[support-paypal]: https://www.paypal.me/panva

View file

@ -1,10 +1,13 @@
{
"name": "@panva/jose",
"version": "0.0.1",
"version": "0.8.0",
"description": "JSON Web Almost Everything - JWA, JWS, JWE, JWK, JWKS for Node.js with minimal dependencies",
"keywords": [
"compact",
"decrypt",
"encrypt",
"flattened",
"general",
"jose",
"jwa",
"jwe",
@ -63,6 +66,7 @@
}
},
"nyc": {
"all": true,
"reporter": [
"lcov",
"text-summary"