Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add better error messages #189

Merged
merged 4 commits into from
Jun 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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