diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
new file mode 100644
index 00000000..d4dc1e2d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -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**
+
+
+
+**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.
diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md
new file mode 100644
index 00000000..536323b1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request.md
@@ -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.
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
new file mode 100644
index 00000000..3504b5e1
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -0,0 +1,6 @@
+---
+name: Question
+about: I have a question about using @panva/jose
+title: 'question: '
+labels: question
+---
diff --git a/.github/ISSUE_TEMPLATE/security-vulnerability.md b/.github/ISSUE_TEMPLATE/security-vulnerability.md
new file mode 100644
index 00000000..4744b7c9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/security-vulnerability.md
@@ -0,0 +1,20 @@
+---
+name: Security Vulnerability
+about: I want to disclose a vulnerability
+---
+
+
+
+
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 00000000..dfda0737
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+access = public
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..f50fb737
--- /dev/null
+++ b/.travis.yml
@@ -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
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..9bf6722e
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -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/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..6b21ff9e
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -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
diff --git a/README.md b/README.md
index 5f1ec37c..a84d8edf 100644
--- a/README.md
+++ b/README.md
@@ -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
-Missing a feature? - If it wasn't already discussed before, [ask for it][suggest-feature].
-Found a bug? - [report it][bug].
+
-
Support
+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
[][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].
[][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 `` | `` | ``
-
-``, `` and `` 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.
-
-
- API (Click to expand)
-
-- `key`: `
-
-
- Example (Click to expand)
+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)
```
-
+#### Signing
-#### JWK `#importKey(secret[, options])` secret key import
-
-Imports a symmetric key.
-
-
- API (Click to expand)
-
-- `secret`: `` | `` | ``
-- `options`: ``
- - `alg`: `` option identifies the algorithm intended for use with the key.
- - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
-- Returns: ``
-
-
-
-
- Example (Click to expand)
+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
+)
```
-
+#### 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.
-
-
- API (Click to expand)
-
-- `jwk`: ``
- - `kty`: `` Key type. Must be 'RSA', 'EC' or 'oct'.
- - `alg`: `` option identifies the algorithm intended for use with the key.
- - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
- - `e`, `n` properties as `` for RSA public keys
- - `e`, `n`, `d`, `p`, `q`, `dp`, `dq`, `qi` properties as `` for RSA private keys
- - `crv`, `x`, `y` properties as `` for EC public keys
- - `crv`, `x`, `y`, `d` properties as `` for EC private keys
- - `k` properties as `` for secret oct keys
-- Returns: `` | `` | ``
-
-
-
-
-Example (Click to expand)
+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
+)
```
-
+#### 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
+)
+```
-
- API (Click to expand)
+#### Verifying
-- `kty`: `` Key type. Must be 'RSA', 'EC' or 'oct'.
-- `crvOrSize`: `` | `` key's bit size or in case of EC keys the curve
-- `options`: ``
- - `alg`: `` option identifies the algorithm intended for use with the key.
- - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
-- `private`: `` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
-- Returns: `Promise` | `Promise` | `Promise`
+Decrypt using the private key or a shared symmetrical secret. See the [documentation][documentation-jwe] for more.
-
+```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?
-
- API (Click to expand)
+**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`: `` Key type. Must be 'RSA', 'EC' or 'oct'.
-- `crvOrSize`: `` | `` 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`: ``
- - `alg`: `` option identifies the algorithm intended for use with the key.
- - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
- - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
-- `private`: `` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
-- Returns: `` | `` | ``
+#### How do I use it outside of Node.js
-
+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
-
- API (Click to expand)
+#### Why? Just, why?
-- `keys`: `` Array of key keys instantiated by `JWK.importKey`
-- Returns: ``
+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.
-
-
-#### `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.
-
-
- API (Click to expand)
-
-- `parameters`: ``
- - `kty`: `` Key Type to filter for.
- - `alg`: `` Key supported algorithm to filter for.
- - `use`: `` Key use to filter for.
- - `kid`: `` Key ID to filter for.
-- Returns: `` 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.
-
-
- API (Click to expand)
-
-- `parameters`: ``
- - `kty`: `` Key Type to filter for.
- - `alg`: `` Key supported algorithm to filter for.
- - `use`: `` Key use to filter for.
- - `kid`: `` Key ID to filter for.
-- Returns: `` | `` | `` | ``
-
-
-
-#### `keystore.add(key)`
-
-Adds a key instance to the store unless it is already included.
-
-
- API (Click to expand)
-
-- `key`: `` | `` | ``
-
-
-
-#### `keystore.remove(key)`
-
-Ensures a key is removed from a store.
-
-
- API (Click to expand)
-
-- `key`: `` | `` | ``
-
-
-
-#### `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`.
++ 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
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..b7c9ac3a
--- /dev/null
+++ b/docs/README.md
@@ -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
+
+[][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].
+[][support-paypal]
+
+
+
+---
+
+## JWK (JSON Web Key)
+
+
+- [Class: <JWK.Key> and <JWK.RSAKey> | <JWK.ECKey> | <JWK.OctKey>](#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)
+
+
+All @panva/jose operations require `` or `` as arguments. Here's
+how to get a `` instances generated or instantiated from existing key material.
+
+
+```js
+const { JWK } = require('@panva/jose')
+// { importKey: [Function: importKey],
+// generate: [AsyncFunction: generate],
+// generateSync: [Function: generateSync] }
+```
+
+---
+
+#### Class: `` and `` | `` | ``
+
+``, `` and `` 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.
+
+``, `` and `` inherit methods from `` and in addition
+to the properties documented below have the respective key component properties exported as ``
+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
+
+- ``
+
+---
+
+#### `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.
+
+- ``
+
+---
+
+#### `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)
+
+- ``
+
+---
+
+#### `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]
+
+- ``
+
+---
+
+#### `key.public`
+
+Returns true/false if the key is asymmetric and public. Returns false for symmetric keys.
+
+- ``
+
+---
+
+#### `key.private`
+
+Returns true/false if the key is asymmetric and private. Returns false for symmetric keys.
+
+- ``
+
+---
+
+#### `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`: `` Must be one of 'encrypt', 'decrypt', 'sign', 'verify', 'wrapKey', 'unwrapKey'
+- Returns: `Set`
+
+
+ Example (Click to expand)
+
+```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' }
+```
+
+
+---
+
+#### `key.toJWK([private])`
+
+Exports the key to a JSON Web Key formatted object.
+
+- `private`: `` When true exports keys with its private components. **Default:** 'false'
+- Returns: ``
+
+
+ Example (Click to expand)
+
+```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' }
+```
+
+
+---
+
+#### `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`: `` | `` | `` | ``
+ - `key`: `` | ``
+ - `format`: `` Must be 'pem' or 'der'. **Default:** 'pem'.
+ - `type`: `` Must be 'pkcs1', 'pkcs8' or 'sec1'. This option is required only if the format is 'der' and ignored if it is 'pem'.
+ - `passphrase`: `` | `` The passphrase to use for decryption.
+- `options`: ``
+ - `alg`: `` option identifies the algorithm intended for use with the key.
+ - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
+ - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
+- Returns: `` | ``
+
+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)
+
+
+ Example (Click to expand)
+
+```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] }
+```
+
+
+---
+
+#### `JWK.importKey(secret[, options])` secret key import
+
+Imports a symmetric key.
+
+- `secret`: `` | `` | ``
+- `options`: ``
+ - `alg`: `` option identifies the algorithm intended for use with the key.
+ - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
+ - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
+- Returns: ``
+
+
+ Example (Click to expand)
+
+```js
+const { JWK: { importKey } } = require('@panva/jose')
+
+const key = importKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ'))
+// OctKey {
+// kty: 'oct',
+// kid: [Getter],
+// k: [Getter] }
+```
+
+
+---
+
+#### `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`: ``
+ - `kty`: `` Key type. Must be 'RSA', 'EC' or 'oct'.
+ - `alg`: `` option identifies the algorithm intended for use with the key.
+ - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
+ - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
+ - `e`, `n` properties as `` for RSA public keys
+ - `e`, `n`, `d`, `p`, `q`, `dp`, `dq`, `qi` properties as `` for RSA private keys
+ - `crv`, `x`, `y` properties as `` for EC public keys
+ - `crv`, `x`, `y`, `d` properties as `` for EC private keys
+ - `k` properties as `` for secret oct keys
+- Returns: `` | `` | ``
+
+
+Example (Click to expand)
+
+```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] }
+```
+
+
+---
+
+#### `JWK.generate(kty[, crvOrSize[, options[, private]]])` generating new keys
+
+Securely generates a new RSA, EC or oct key.
+
+- `kty`: `` Key type. Must be 'RSA', 'EC' or 'oct'.
+- `crvOrSize`: `` | `` key's bit size or in case of EC keys the curve
+- `options`: ``
+ - `alg`: `` option identifies the algorithm intended for use with the key.
+ - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
+ - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
+- `private`: `` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
+- Returns: `Promise` | `Promise` | `Promise`
+
+
+Example (Click to expand)
+
+```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] }
+})()
+```
+
+
+---
+
+#### `JWK.generateSync(kty[, crvOrSize[, options[, private]]])`
+
+Synchronous version of `JWK.generate()`
+
+- `kty`: `` Key type. Must be 'RSA', 'EC' or 'oct'.
+- `crvOrSize`: `` | `` 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`: ``
+ - `alg`: `` option identifies the algorithm intended for use with the key.
+ - `use`: `` option indicates whether the key is to be used for encrypting & decrypting data or signing & verifying data. Must be 'sig' or 'enc'.
+ - `kid`: `` Key ID Parameter. When not provided is computed using the method defined in [RFC7638][spec-thumbprint]
+- `private`: `` **Default** 'true'. Is the resulting key private or public (when asymmetrical)
+- Returns: `` | `` | ``
+
+
+Example (Click to expand)
+
+```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] }
+```
+
+
+---
+
+#### `JWK.isKey(object)`
+
+Returns 'true' if the value is an instance of ``.
+
+- `object`: ``
+- Returns: ``
+
+---
+
+## JWKS (JSON Web Key Set)
+
+
+- [Class: ](#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)
+
+
+```js
+const { JWKS } = require('@panva/jose')
+// { KeyStore: [Function: KeyStore] }
+```
+
+#### Class: ``
+
+`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`: `` Array of key keys instantiated by `JWK.importKey()`
+- Returns: ``
+
+---
+
+#### `keystore.size`
+
+Returns the number of keys in the keystore.
+
+- ``
+---
+
+#### `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`: ``
+ - `kty`: `` Key Type to filter for.
+ - `alg`: `` Key supported algorithm to filter for.
+ - `use`: `` Key use to filter for.
+ - `kid`: `` Key ID to filter for.
+- Returns: `` 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`: ``
+ - `kty`: `` Key Type to filter for.
+ - `alg`: `` Key supported algorithm to filter for.
+ - `use`: `` Key use to filter for.
+ - `kid`: `` Key ID to filter for.
+- Returns: `` | `` | `` | ``
+
+---
+
+#### `keystore.add(key)`
+
+Adds a key instance to the store unless it is already included.
+
+- `key`: `` | `` | ``
+
+---
+
+#### `keystore.remove(key)`
+
+Ensures a key is removed from a store.
+
+- `key`: `` | `` | ``
+
+---
+
+#### `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`: `` When true exports keys with their private components. **Default:** 'false'
+- Returns: ``
+
+---
+
+#### `JWKS.KeyStore.fromJWKS(jwks)`
+
+Creates a new KeyStore from a JSON Web Key Set.
+
+- `jwks`: `` JWKS formatted object (`{ keys: [{ kty: '...', ... }, ...] }`)
+- Returns: ``
+
+
+Example (Click to expand)
+
+```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
+```
+
+
+---
+
+## JWS (JSON Web Signature)
+
+
+- [Class: <JWS.Sign>](#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)
+
+
+The `` 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: ``
+
+`` 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.
+
+
+Example (Click to expand)
+
+```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' } ] }
+
+```
+
+
+---
+
+#### `new JWS.Sign(payload)`
+
+Creates a new Sign object for the provided payload, intended for one or more recipients.
+
+- `payload`: `` | `` | `` The payload that will be signed. When `` it
+ will be automatically serialized to JSON before signing
+- Returns: ``
+
+---
+
+#### `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 `` instance.
+
+- `key`: `` The key to sign with.
+- `protected`: `` Protected Header for this recipient
+- `header`: `` 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 `` and ``
+for shorthand methods to sign for a single recipient.
+
+- `serialization`: `` JWS Serialization. Must be one of 'general', 'flattened', 'compact'
+- Returns: `` | ``
+
+---
+
+#### `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 `` instance.
+
+- `payload`: `` | `` | `` The payload that will be signed. When `` it
+ will be automatically serialized to JSON before signing
+- `key`: `` The key to sign with.
+- `protected`: `` Protected Header
+- Returns: ``
+
+
+Example (Click to expand)
+
+```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
+```
+
+
+---
+
+#### `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 `` instance.
+
+- `payload`: `` | `` | `` The payload that will be signed. When `` it
+ will be automatically serialized to JSON before signing
+- `key`: `` The key to sign with.
+- `protected`: `` Protected Header
+- `header`: `` Unprotected Header
+- Returns: ``
+
+
+Example (Click to expand)
+
+```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' }
+```
+
+
+---
+
+#### `JWS.verify(jws, keyOrStore[, options])`
+
+Verifies the provided JWS in either serialization with a given `` or ``
+
+- `jws`: `` | `` The JWS to verify. This must be a valid JWS.
+- `keyOrStore`: `` | `` The key or store to verify with. When ``
+ 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`: ``
+ - `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`: `` 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: `` | ``
+
+
+Example (Click to expand)
+
+```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
+```
+
+
+---
+
+## JWE (JSON Web Encryption)
+
+
+- [Class: <JWE.Encrypt>](#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)
+
+
+The `` 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: ``
+
+`` 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`: `` | `` The cleartext that will be encrypted.
+- `protected`: `` JWE Protected Header
+- `unprotected`: `` JWE Shared Unprotected Header
+- `aad`: `` | `` JWE Additional Authenticated Data
+- Returns: ``
+
+---
+
+#### `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 `` instance.
+
+- `key`: `` The key to use for Key Management or Direct Encryption
+- `header`: `` 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 `` and ``
+for shorthand methods to encrypt for a single recipient.
+
+- `serialization`: `` JWE Serialization. Must be one of 'general', 'flattened', 'compact'
+- Returns: `` | ``
+
+---
+
+#### `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 `` instance.
+
+- `cleartext`: `` | `` The cleartext that will be encrypted.
+- `key`: `` The key to use for Key Management or Direct Encryption
+- `protected`: `` JWE Protected Header
+- Returns: ``
+
+---
+
+#### `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 `` instance.
+
+- `cleartext`: `` | `` The cleartext that will be encrypted.
+- `key`: `` The key to use for Key Management or Direct Encryption
+- `protected`: `` JWE Protected Header
+- `unprotected`: `` JWE Shared Unprotected Header
+- `aad`: `` | `` JWE Additional Authenticated Data
+- Returns: ``
+
+---
+
+#### `JWE.decrypt(jwe, keyOrStore[, options])`
+
+Verifies the provided JWE in either serialization with a given `` or ``
+
+- `jwe`: `` | `` The JWE to decrypt. This must be a valid JWE.
+- `keyOrStore`: `` | `` The key or store to decrypt with. When ``
+ 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`: ``
+ - `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`: `` When true returns a complete object with the parsed headers, verified AAD
+ and cleartext instead of just the cleartext. **Default:** 'false'
+- Returns: `` | ``
+
+
+[spec-thumbprint]: https://tools.ietf.org/html/rfc7638
+[support-patreon]: https://www.patreon.com/panva
+[support-paypal]: https://www.paypal.me/panva
diff --git a/package.json b/package.json
index d55d4a10..c74fccf6 100644
--- a/package.json
+++ b/package.json
@@ -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"