Skip to content

Commit

Permalink
feat: add OKP Key and EdDSA sign/verify support
Browse files Browse the repository at this point in the history
BREAKING CHANGE: node.js minimal version is now v12.0.0 due to its
added EdDSA support (crypto.sign, crypto.verify and eddsa key objects)

resolves #12
  • Loading branch information
panva committed Apr 23, 2019
1 parent d51cfb5 commit 2dbd3ed
Show file tree
Hide file tree
Showing 41 changed files with 1,090 additions and 109 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ 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]
- node version: [e.g. v12.0.0]

**Additional context**
Add any other context about the problem here.
Expand Down
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ matrix:
language: node_js
node_js: stable
script: npm run lint
- name: "Test Suite + coverage - 11.8.0" #min
- name: "Test Suite + coverage - 12.0.0" #min
language: node_js
node_js: 11.8.0
node_js: 12.0.0
script: npm run coverage
after_script: npx codecov
- name: "Test Suite + coverage - stable"
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The following specifications are implemented by @panva/jose
- JSON Web Token (JWT) - [RFC7519][spec-jwt]
- JSON Web Key (JWK) Thumbprint - [RFC7638][spec-thumbprint]
- JWS Unencoded Payload Option - [RFC7797][spec-b64]
- CFRG Elliptic Curve Signatures (EdDSA) - [RFC8037][spec-okp]

The test suite utilizes examples defined in [RFC7520][spec-cookbook] to confirm its JOSE
implementation is correct.
Expand All @@ -31,6 +32,7 @@ Legend:
| -- | -- | -- |
| RSA || RSA |
| Elliptic Curve || EC |
| Octet Key Pair || OKP |
| Octet sequence || oct |

| Serialization | JWS Sign | JWS Verify | JWE Encrypt | JWE Decrypt |
Expand All @@ -44,6 +46,7 @@ Legend:
| RSASSA-PKCS1-v1_5 || RS256, RS384, RS512 |
| RSASSA-PSS || PS256, PS384, PS512 |
| ECDSA || ES256, ES384, ES512 |
| Edwards-curve DSA || EdDSA |
| HMAC with SHA-2 || HS256, HS384, HS512 |

| JWE Key Management Algorithms | Supported ||
Expand All @@ -64,7 +67,7 @@ Legend:
---

Pending Node.js Support 🤞:
- [RFC8037][spec-cfrg] (EdDSA, OKP kty, etc). See [#12](https://github.com/panva/jose/issues/12)
- ECDH-ES with X25519 and X448

Won't implement:
- ✕ JWS embedded key / referenced verification
Expand Down Expand Up @@ -107,8 +110,7 @@ If you or your business use @panva/jose, please consider becoming a [Patron][sup

## Usage

⚠️ Minimal Node.js version required is **v11.8.0** ⚠️ The plan is to release v1.0.0 when Node.js
v12.0.0 releases in April 2019
For its improvements in the crypto module ⚠️ the minimal Node.js version required is **v12.0.0** ⚠️

Installing @panva/jose

Expand Down Expand Up @@ -255,7 +257,7 @@ private API and is subject to change between any versions.
#### 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.
the KeyObject API that was added in Node.js v11.6.0 and one-shot sign/verify API added in v12.0.0

#### How is it different from [`node-jose`][node-jose]

Expand Down Expand Up @@ -304,13 +306,13 @@ in terms of performance and API (not having well defined errors). When Node.js v
[node-jose]: https://github.com/cisco/node-jose
[security-vulnerability]: https://github.com/panva/jose/issues/new?template=security-vulnerability.md
[spec-b64]: https://tools.ietf.org/html/rfc7797
[spec-cfrg]: https://tools.ietf.org/html/rfc8037
[spec-cookbook]: https://tools.ietf.org/html/rfc7520
[spec-jwa]: https://tools.ietf.org/html/rfc7518
[spec-jwe]: https://tools.ietf.org/html/rfc7516
[spec-jwk]: https://tools.ietf.org/html/rfc7517
[spec-jws]: https://tools.ietf.org/html/rfc7515
[spec-jwt]: https://tools.ietf.org/html/rfc7519
[spec-okp]: https://tools.ietf.org/html/rfc8037
[spec-thumbprint]: https://tools.ietf.org/html/rfc7638
[suggest-feature]: https://github.com/panva/jose/issues/new?labels=enhancement&template=feature-request.md&title=proposal%3A+
[support-patreon]: https://www.patreon.com/panva
Expand Down
49 changes: 27 additions & 22 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ I can continue maintaining it and adding new features carefree. You may also don
## JWK (JSON Web Key)

<!-- TOC JWK START -->
- [Class: &lt;JWK.Key&gt; and &lt;JWK.RSAKey&gt; &vert; &lt;JWK.ECKey&gt; &vert; &lt;JWK.OctKey&gt;](#class-jwkkey-and-jwkrsakey--jwkeckey--jwkoctkey)
- [Class: &lt;JWK.Key&gt; and &lt;JWK.RSAKey&gt; &vert; &lt;JWK.ECKey&gt; &vert; &lt;JWK.OKPKey&gt; &vert; &lt;JWK.OctKey&gt;](#class-jwkkey-and-jwkrsakey--jwkeckey--jwkokpkey--jwkoctkey)
- [key.kty](#keykty)
- [key.alg](#keyalg)
- [key.use](#keyuse)
Expand Down Expand Up @@ -58,28 +58,30 @@ const { JWK } = require('@panva/jose')

---

#### Class: `<JWK.Key>` and `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OctKey>`
#### Class: `<JWK.Key>` and `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>` &vert; `<JWK.OctKey>`

`<JWK.RSAKey>`, `<JWK.ECKey>` and `<JWK.OctKey>` represent a key usable for JWS and JWE operations.
`<JWK.RSAKey>`, `<JWK.ECKey>`, `<JWK.OKPKey>` 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
`<JWK.RSAKey>`, `<JWK.ECKey>`, `<JWK.OKPKey>` 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
- `crv, x` for Public OKP Keys
- `crv, x, n` for Private OKP 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.
Returns the key's JWK Key Type Parameter. 'EC', 'RSA', 'OKP' or 'oct' for the respective supported
key types.

- `<string>`

Expand Down Expand Up @@ -262,7 +264,7 @@ Private keys may also be passphrase protected.
[RFC7638][spec-thumbprint]
- `use`: `<string>` option indicates whether the key is to be used for encrypting & decrypting
data or signing & verifying data. Must be 'sig' or 'enc'.
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>`
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>`

See the underlying Node.js API for details on importing private and public keys in the different
formats
Expand Down Expand Up @@ -321,11 +323,11 @@ const key = importKey(Buffer.from('8yHym6h5CG5FylbzrCn8fhxEbp3kOaTsgLaawaaJ'))

#### `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.
Imports a JWK formatted key. This supports JWK formatted RSA, EC, OKP and oct keys. Asymmetrical
keys may be both private and public.

- `jwk`: `<Object>`
- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `kty`: `<string>` Key type. Must be 'RSA', 'EC', 'OKP' 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'.
Expand All @@ -335,8 +337,10 @@ may be both private and public.
- `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
- `crv`, `x`, properties as `<string>` for OKP public keys
- `crv`, `x`, `d` properties as `<string>` for OKP private keys
- `k` properties as `<string>` for secret oct keys
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OctKey>`
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>` &vert; `<JWK.OctKey>`

<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
Expand Down Expand Up @@ -366,10 +370,11 @@ const key = importKey(jwk)

#### `JWK.generate(kty[, crvOrSize[, options[, private]]])` generating new keys

Securely generates a new RSA, EC or oct key.
Securely generates a new RSA, EC, OKP or oct key.

- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `crvOrSize`: `<number>` &vert; `<string>` key's bit size or in case of EC keys the curve
- `kty`: `<string>` Key type. Must be 'RSA', 'EC', 'OKP' or 'oct'.
- `crvOrSize`: `<number>` &vert; `<string>` key's bit size or in case of OKP and EC keys the curve
**Default:** 2048 for RSA, 'P-256' for EC, 'Ed25519' for OKP and 256 for oct.
- `options`: `<Object>`
- `alg`: `<string>` option identifies the algorithm intended for use with the key.
- `kid`: `<string>` Key ID Parameter. When not provided is computed using the method defined in
Expand All @@ -378,7 +383,7 @@ Securely generates a new RSA, EC or oct key.
data or signing & verifying data. Must be 'sig' or 'enc'.
- `private`: `<boolean>` **Default** 'true'. Is the resulting key private or public (when
asymmetrical)
- Returns: `Promise<JWK.RSAKey>` &vert; `Promise<JWK.ECKey>` &vert; `Promise<JWK.OctKey>`
- Returns: `Promise<JWK.RSAKey>` &vert; `Promise<JWK.ECKey>` &vert; `Promise<JWK.OKPKey>` &vert; `Promise<JWK.OctKey>`

<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
Expand Down Expand Up @@ -406,9 +411,9 @@ const { JWK: { generate } } = require('@panva/jose')

Synchronous version of `JWK.generate()`

- `kty`: `<string>` Key type. Must be 'RSA', 'EC' or 'oct'.
- `crvOrSize`: `<number>` &vert; `<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.
- `kty`: `<string>` Key type. Must be 'RSA', 'EC', 'OKP' or 'oct'.
- `crvOrSize`: `<number>` &vert; `<string>` key's bit size or in case of OKP and EC keys the curve.
**Default:** 2048 for RSA, 'P-256' for EC, 'Ed25519' for OKP 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
Expand All @@ -417,7 +422,7 @@ Synchronous version of `JWK.generate()`
[RFC7638][spec-thumbprint]
- `private`: `<boolean>` **Default** 'true'. Is the resulting key private or public (when
asymmetrical)
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OctKey>`
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>` &vert; `<JWK.OctKey>`

<details>
<summary><em><strong>Example</strong></em> (Click to expand)</summary>
Expand Down Expand Up @@ -527,23 +532,23 @@ parameters is returned.
- `kid`: `<string>` Key ID to filter for.
- `operation`: `<string>` Further specify the operation a given alg must be valid for. Must be one
of 'encrypt', 'decrypt', 'sign', 'verify', 'wrapKey', 'unwrapKey'
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OctKey>` &vert; `<undefined>`
- Returns: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>` &vert; `<JWK.OctKey>` &vert; `<undefined>`

---

#### `keystore.add(key)`

Adds a key instance to the store unless it is already included.

- `key`: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OctKey>`
- `key`: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>` &vert; `<JWK.OctKey>`

---

#### `keystore.remove(key)`

Ensures a key is removed from a store.

- `key`: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OctKey>`
- `key`: `<JWK.RSAKey>` &vert; `<JWK.ECKey>` &vert; `<JWK.OKPKey>` &vert; `<JWK.OctKey>`

---

Expand Down
6 changes: 6 additions & 0 deletions lib/help/asn1/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ types.set('PrivateKeyInfo', PrivateKeyInfo)
const PublicKeyInfo = asn1.define('PublicKeyInfo', require('./public_key_info')(AlgorithmIdentifier))
types.set('PublicKeyInfo', PublicKeyInfo)

const PrivateKey = asn1.define('PrivateKey', require('./private_key'))
types.set('PrivateKey', PrivateKey)

const OneAsymmetricKey = asn1.define('OneAsymmetricKey', require('./one_asymmetric_key')(AlgorithmIdentifier, PrivateKey))
types.set('OneAsymmetricKey', OneAsymmetricKey)

const RSAPrivateKey = asn1.define('RSAPrivateKey', require('./rsa_private_key'))
types.set('RSAPrivateKey', RSAPrivateKey)

Expand Down
7 changes: 7 additions & 0 deletions lib/help/asn1/one_asymmetric_key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = (AlgorithmIdentifier, PrivateKey) => function () {
this.seq().obj(
this.key('version').int(),
this.key('algorithm').use(AlgorithmIdentifier),
this.key('privateKey').use(PrivateKey)
)
}
5 changes: 5 additions & 0 deletions lib/help/asn1/private_key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = function () {
this.octstr().contains().obj(
this.key('privateKey').octstr()
)
}
8 changes: 0 additions & 8 deletions lib/help/key_object.js

This file was deleted.

Loading

0 comments on commit 2dbd3ed

Please sign in to comment.