Skip to content

Commit

Permalink
fix: add better error messages (#189)
Browse files Browse the repository at this point in the history
* fix: throw more explicit errors

* chore: export Recipient interfaces

* chore(deps): update dependencies

* docs: update links in readme
  • Loading branch information
mirceanis authored Jun 25, 2021
1 parent ee8dbb4 commit db8f93a
Show file tree
Hide file tree
Showing 26 changed files with 2,983 additions and 5,291 deletions.
4 changes: 2 additions & 2 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"jsxBracketSameLine": false,
"trailingComma": "none",
"tabWidth": 2,
"printWidth": 120,
"singleQuote": true,
"trailingComma": "es5",
"semi": false
}
}
30 changes: 15 additions & 15 deletions .releaserc
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"tagFormat": "${version}",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/changelog", {
"changelogFile": "CHANGELOG.md"
}],
"@semantic-release/npm",
["@semantic-release/git", {
"assets": ["CHANGELOG.md", "docs", "package.json", "package-lock.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}],
"@semantic-release/github"
],
"branch": "master"
"tagFormat": "${version}",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/changelog", {
"changelogFile": "CHANGELOG.md"
}],
"@semantic-release/npm",
["@semantic-release/git", {
"assets": ["CHANGELOG.md", "docs", "package.json", "yarn.lock"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}],
"@semantic-release/github"
],
"branch": "master"
}
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
# did-jwt

[![npm](https://img.shields.io/npm/dt/did-jwt.svg)](https://www.npmjs.com/package/did-jwt)
[![npm](https://img.shields.io/npm/v/did-jwt.svg)](https://www.npmjs.com/package/did-jwt)
[![Twitter Follow](https://img.shields.io/twitter/follow/uport_me.svg?style=social&label=Follow)](https://twitter.com/uport_me)
[![codecov](https://codecov.io/gh/decentralized-identity/did-jwt/branch/master/graph/badge.svg)](https://codecov.io/gh/decentralized-identity/did-jwt)

# did-jwt

The did-JWT library allows you to sign and verify [JSON Web Tokens (JWT)](https://tools.ietf.org/html/rfc7519) using `ES256K` and `Ed25519` algorithms.
The non-standard `ES256K-R` is also supported for backward compatibility reasons.
The did-JWT library allows you to sign and verify [JSON Web Tokens (JWT)](https://tools.ietf.org/html/rfc7519)
using `ES256K` and `EdDSA` algorithms. The non-standard `ES256K-R` is also supported for backward compatibility
reasons, as well as the `Ed25519` legacy name for `EdDSA`.

Public keys are resolved using the [Decentralized ID (DID)](https://w3c-ccg.github.io/did-spec/#decentralized-identifiers-dids) of the signing identity of the token, which is passed as the `iss` attribute of the JWT payload.
Public keys are resolved using the [Decentralized ID (DID)](https://w3c.github.io/did-core/#identifier) of the signing
identity of the token, which is passed as the `iss` attribute of the JWT payload.

## DID methods

All DID methods that can be resolved using the [`did-resolver'](https://github.com/uport-project/did-resolver) interface are supported for verification.
All DID methods that can be resolved using the [`did-resolver'](https://github.com/decentralized-identity/did-resolver)
interface are supported for verification.

If your DID method requires a different signing algorithm than what is already supported, please create an issue.

Expand All @@ -31,8 +35,8 @@ yarn add did-jwt

### 1. Create a did-JWT

In practice, you must secure the key passed to ES256KSigner.
The key provided in code below is for informational purposes only.
In practice, you must secure the key passed to ES256KSigner. The key provided in code below is for informational
purposes only.

```js
const didJWT = require('did-jwt')
Expand Down Expand Up @@ -75,9 +79,9 @@ Once decoded a did-JWT will resemble:

### 3. Verify a did-JWT

You need to provide a did-resolver for the verify function.
For this example we will use `did:ethr`, but there are other methods available.
For more information on configuring the Resolver object please see [did-resolver](https://github.com/decentralized-identity/did-resolver#configure-resolver-object)
You need to provide a did-resolver for the verify function. For this example we will use `did:ethr`, but there are other
methods available. For more information on configuring the Resolver object please
see [did-resolver](https://github.com/decentralized-identity/did-resolver#configure-resolver-object)

```bash
npm install ethr-did-resolver
Expand Down
40 changes: 20 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"build": "yarn build:js && yarn test && yarn build:browser",
"build:docs": "echo 'PLEASE UPDATE REFERENCE DOCS MANUALLY'",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "eslint src/ --ext .ts",
"lint": "eslint --ignore-pattern \"src/**/*.test.[jt]s\" \"src/**/*.[jt]s\"",
"prepublishOnly": "yarn test:ci && yarn format && yarn lint",
"prepare": "yarn build",
"release": "semantic-release --debug"
Expand Down Expand Up @@ -51,43 +51,43 @@
]
},
"devDependencies": {
"@babel/core": "7.14.3",
"@babel/preset-env": "7.14.4",
"@babel/preset-typescript": "7.13.0",
"@babel/core": "7.14.6",
"@babel/preset-env": "7.14.7",
"@babel/preset-typescript": "7.14.5",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/git": "9.0.0",
"@types/elliptic": "6.4.12",
"@types/jest": "26.0.23",
"@typescript-eslint/eslint-plugin": "4.25.0",
"@typescript-eslint/parser": "4.25.0",
"@typescript-eslint/eslint-plugin": "4.28.0",
"@typescript-eslint/parser": "4.28.0",
"codecov": "3.8.2",
"eslint": "7.27.0",
"eslint": "7.29.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-jest": "24.3.6",
"eslint-plugin-prettier": "3.4.0",
"jest": "27.0.3",
"jest": "27.0.5",
"jsontokens": "3.0.0",
"microbundle": "0.13.1",
"microbundle": "0.13.3",
"mockdate": "3.0.5",
"prettier": "2.3.0",
"prettier": "2.3.1",
"regenerator-runtime": "0.13.7",
"semantic-release": "17.4.3",
"semantic-release": "17.4.4",
"tweetnacl": "1.0.3",
"typescript": "4.3.2",
"webpack": "5.38.1",
"webpack-cli": "4.7.0"
"typescript": "4.3.4",
"webpack": "5.40.0",
"webpack-cli": "4.7.2"
},
"dependencies": {
"@stablelib/ed25519": "^1.0.1",
"@stablelib/random": "^1.0.0",
"@stablelib/sha256": "^1.0.0",
"@stablelib/x25519": "^1.0.0",
"@stablelib/xchacha20poly1305": "^1.0.0",
"@stablelib/ed25519": "^1.0.2",
"@stablelib/random": "^1.0.1",
"@stablelib/sha256": "^1.0.1",
"@stablelib/x25519": "^1.0.1",
"@stablelib/xchacha20poly1305": "^1.0.1",
"canonicalize": "^1.0.5",
"did-resolver": "^3.1.0",
"elliptic": "^6.5.4",
"js-sha3": "^0.8.0",
"uint8arrays": "^2.1.3"
"uint8arrays": "^2.1.5"
},
"eslintIgnore": [
"*.test.ts"
Expand Down
2 changes: 1 addition & 1 deletion renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
]
}
]
}
}
2 changes: 1 addition & 1 deletion src/Digest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function concatKDF(
lengthAndInput(u8a.fromString(alg)),
lengthAndInput(typeof producerInfo === 'undefined' ? new Uint8Array(0) : producerInfo), // apu
lengthAndInput(typeof consumerInfo === 'undefined' ? new Uint8Array(0) : consumerInfo), // apv
writeUint32BE(keyLen)
writeUint32BE(keyLen),
])

// since our key lenght is 256 we only have to do one round
Expand Down
21 changes: 11 additions & 10 deletions src/JWE.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { base64ToBytes, bytesToBase64url, decodeBase64url, toSealed } from './util'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ProtectedHeader = Record<string, any>
export type ProtectedHeader = Record<string, any> & Partial<RecipientHeader>

/**
* The JWK representation of an ephemeral public key.
Expand All @@ -18,7 +18,7 @@ interface EphemeralPublicKey {
e?: string
}

interface RecipientHeader {
export interface RecipientHeader {
alg: string
iv: string
tag: string
Expand Down Expand Up @@ -66,12 +66,12 @@ export interface Decrypter {

function validateJWE(jwe: JWE) {
if (!(jwe.protected && jwe.iv && jwe.ciphertext && jwe.tag)) {
throw new Error('Invalid JWE')
throw new Error('bad_jwe: missing properties')
}
if (jwe.recipients) {
jwe.recipients.map((rec) => {
if (!(rec.header && rec.encrypted_key)) {
throw new Error('Invalid JWE')
throw new Error('bad_jwe: malformed recipients')
}
})
}
Expand All @@ -82,7 +82,7 @@ function encodeJWE({ ciphertext, tag, iv, protectedHeader, recipient }: Encrypti
protected: <string>protectedHeader,
iv: bytesToBase64url(iv),
ciphertext: bytesToBase64url(ciphertext),
tag: bytesToBase64url(tag)
tag: bytesToBase64url(tag),
}
if (aad) jwe.aad = bytesToBase64url(aad)
if (recipient) jwe.recipients = [recipient]
Expand All @@ -96,13 +96,13 @@ export async function createJWE(
aad?: Uint8Array
): Promise<JWE> {
if (encrypters[0].alg === 'dir') {
if (encrypters.length > 1) throw new Error('Can only do "dir" encryption to one key.')
if (encrypters.length > 1) throw new Error('not_supported: Can only do "dir" encryption to one key.')
const encryptionResult = await encrypters[0].encrypt(cleartext, protectedHeader, aad)
return encodeJWE(encryptionResult, aad)
} else {
const tmpEnc = encrypters[0].enc
if (!encrypters.reduce((acc, encrypter) => acc && encrypter.enc === tmpEnc, true)) {
throw new Error('Incompatible encrypters passed')
throw new Error('invalid_argument: Incompatible encrypters passed')
}
let cek
let jwe
Expand All @@ -125,14 +125,15 @@ export async function createJWE(
export async function decryptJWE(jwe: JWE, decrypter: Decrypter): Promise<Uint8Array> {
validateJWE(jwe)
const protHeader = JSON.parse(decodeBase64url(jwe.protected))
if (protHeader.enc !== decrypter.enc) throw new Error(`Decrypter does not support: '${protHeader.enc}'`)
if (protHeader.enc !== decrypter.enc)
throw new Error(`not_supported: Decrypter does not supported: '${protHeader.enc}'`)
const sealed = toSealed(jwe.ciphertext, jwe.tag)
const aad = new Uint8Array(Buffer.from(jwe.aad ? `${jwe.protected}.${jwe.aad}` : jwe.protected))
let cleartext = null
if (protHeader.alg === 'dir' && decrypter.alg === 'dir') {
cleartext = await decrypter.decrypt(sealed, base64ToBytes(jwe.iv), aad)
} else if (!jwe.recipients || jwe.recipients.length === 0) {
throw new Error('Invalid JWE')
throw new Error('bad_jwe: missing recipients')
} else {
for (let i = 0; !cleartext && i < jwe.recipients.length; i++) {
const recipient = jwe.recipients[i]
Expand All @@ -142,6 +143,6 @@ export async function decryptJWE(jwe: JWE, decrypter: Decrypter): Promise<Uint8A
}
}
}
if (cleartext === null) throw new Error('Failed to decrypt')
if (cleartext === null) throw new Error('failure: Failed to decrypt')
return cleartext
}
Loading

0 comments on commit db8f93a

Please sign in to comment.