dag-jose
This library provides a TypeScript implementation of the DAG-JOSE codec for IPLD.
It supports the new multiformats library in order to be compatible with both the current and future js-ipfs implementations.
To create and work with DAG-JOSE compatible JOSE objects we recommend using the dag-jose-utils library.
JWS Signing Usage
The following example is available in complete form in example-signing-ipld.mjs.
For independent usage as an IPLD codec:
import * as Block from 'multiformats/block'
import { sha256 } from 'multiformats/hashes/sha2'
import * as dagCbor from '@ipld/dag-cbor'
import * as dagJose from 'dag-jose'
Import additional libraries for JWS handling:
import {
ES256KSigner,
createJWS,
verifyJWS
} from 'did-jwt'
import {
encodePayload,
toJWSPayload,
toJWSStrings
} from 'dag-jose-utils'
Given a keypair:
const pubkey = '03fdd57adec3d438ea237fe46b33ee1e016eda6b585c3e27ea66686c2ea5358479'
const privkey = '278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f'
Create a signed envelope block:
const signer = ES256KSigner(privkey)
const payloadBlock = await encodePayload(payload)
const jws = await createJWS(toJWSPayload(payloadBlock), signer)
const jwsBlock = await Block.encode({ value: jws, codec: dagJose, hasher: sha256 })
Given a DagJWS envelope block CID, load its bytes, verify the signature and load the linked payload block:
const jwsBlock = await Block.create({ bytes, cid, codec: dagJose, hasher: sha256 })
const jwsStrings = toJWSStrings(jwsBlock.value)
for (const jws of jwsStrings) {
const verifiedKey = verifyJWS(jws, [{ publicKeyHex: pubkey }])
console.log(`Verified JWS envelope \u001b[32m${cid}\u001b[39m with public key:\n\t${verifiedKey.publicKeyHex}`)
}
const payloadCid = jwsBlock.value.link
const payloadBytes = store.get(payloadCid.toString())
const payloadBlock = await Block.create({ bytes: payloadBytes, cid: payloadCid, codec: dagCbor, hasher: sha256 })
JWE Encryption Usage
When using DAG-JOSE (for JWE or JWS) with js-IPFS, you will need to convert it from a raw multiformats style codec to a legacy IPLD codec using blockcodec-to-ipld-format.
The following example is available in complete form in example-ipfs.mjs.
A plain IPLD (without IPFS, for cases where you are managing the block store) version is available in example-ipld.mjs.
import { create as createIpfs } from 'ipfs'
import { convert as toLegacyIpld } from 'blockcodec-to-ipld-format'
import * as dagJose from 'dag-jose'
import {
xc20pDirEncrypter,
xc20pDirDecrypter,
x25519Encrypter,
x25519Decrypter,
decryptJWE,
createJWE
} from 'did-jwt'
import {
decodeCleartext,
prepareCleartext
} from 'dag-jose-utils'
import { randomBytes } from '@stablelib/random'
import { generateKeyPairFromSeed } from '@stablelib/x25519'
Set up js-IPFS:
const dagJoseIpldFormat = toLegacyIpld(dagJose)
async function setup () {
console.log('Starting IPFS ...')
ipfs = await createIpfs({ ipld: { formats: [dagJoseIpldFormat] } })
}
Symmetric encryption
Encrypt and store a payload using a secret key:
const storeEncrypted = async (payload, key) => {
const dirEncrypter = xc20pDirEncrypter(key)
const cleartext = await prepareCleartext(payload)
const jwe = await createJWE(cleartext, [dirEncrypter])
const cid = await ipfs.dag.put(jwe, { format: dagJoseIpldFormat.codec, hashAlg: 'sha2-256' })
console.log(`Encrypted block CID: \u001b[32m${cid}\u001b[39m`)
return cid
}
Load an encrypted block from a CID and decrypt the payload using a secret key:
const loadEncrypted = async (cid, key) => {
const dirDecrypter = xc20pDirDecrypter(key)
const retrieved = await ipfs.dag.get(cid)
const decryptedData = await decryptJWE(retrieved.value, dirDecrypter)
return decodeCleartext(decryptedData)
}
Create a key, encrypt and store a block, then load and decrypt it:
const key = randomBytes(32)
const secretz = { my: 'secret message' }
console.log('Encrypting and storing secret:\u001b[1m', secretz, '\u001b[22m')
const cid = await storeEncrypted(secretz, key)
const decoded = await loadEncrypted(cid, key)
console.log('Loaded and decrypted block content:\u001b[1m', decoded, '\u001b[22m')
Asymmetric encryption
Encrypt and store a payload using a public key:
const storeEncrypted = async (payload, pubkey) => {
const asymEncrypter = x25519Encrypter(pubkey)
const cleartext = await prepareCleartext(payload)
const jwe = await createJWE(cleartext, [asymEncrypter])
const cid = await ipfs.dag.put(jwe, { format: dagJoseIpldFormat.codec, hashAlg: 'sha2-256' })
console.log(`Encrypted block CID: \u001b[32m${cid}\u001b[39m`)
return cid
}
Load an encrypted block from a CID and decrypt the payload using a secret key:
const loadEncrypted = async (cid, privkey) => {
const asymDecrypter = x25519Decrypter(privkey)
const retrieved = await ipfs.dag.get(cid)
const decryptedData = await decryptJWE(retrieved.value, asymDecrypter)
return decodeCleartext(decryptedData)
}
Create a key pair, encrypt and store a block using the public key, then load and decrypt it using the private key:
const privkey = randomBytes(32)
const pubkey = generateKeyPairFromSeed(privkey).publicKey
const secretz = { my: 'secret message' }
console.log('Encrypting and storing secret with public key:\u001b[1m', secretz, '\u001b[22m')
const cid = await storeEncrypted(secretz, pubkey)
const decoded = await loadEncrypted(cid, privkey)
console.log('Loaded and decrypted block content with private key:\u001b[1m', decoded, '\u001b[22m')
Encrypt and decrypt using other jose library
The did-jwt
library only supports x25519
key exchange and XChacha20Poly1305
. If you want to use the dag-jose
codec with other less secure algorithms you can encrypt another library and put the resulting JWE into the dag. Below is an example using the jose library.
const jwk = jose.JWK.generateSync('oct', 256)
const cleartext = prepareCleartext({ my: 'secret message' })
const jwe = jose.JWE.encrypt.flattened(cleartext, jwk, { alg: 'dir', enc: 'A128CBC-HS256' })
const cid = await ipfs.dag.put(jwe, { format: format.codec, hashAlg: 'sha2-256' })
const retrived = await ipfs.dag.get(cid)
const decryptedData = jose.JWE.decrypt(retrived, jwk)
console.log(decodeCleartext(decryptedData))
Maintainer
Joel Thorstensson
License