Skip to main content

Encryption and Decryption

The LigoAgreement and LigoAgreementState objects are stored in IPFS using the DAG-JOSE codec. They are signed by both parties and encrypted.

This document describes how the encryption and decryption of these objects works.

See a working example here.

Encryption

1. Create Lit symmetric key

Lit Protocol is used to encrypt content that only the parties on an agreement can access.

import LitJsSdk from "@lit-protocol/sdk-browser";

// 1. Setup Lit SDK
const client = new LitJsSdk.LitNodeClient();
await client.connect();

// 2. Generate a symmetric key without encryption anything yet
const { symmetricKey } = await LitJsSdk.encryptString("");

2. Derive Access Control Conditions

The access control conditions define who has access to the symmetric key. This can be derived given the address of the Safe for the agreement (<safeAddress>).

const accessControlConditions = [
{
contractAddress: <safeAddress>,
functionName: "isOwner",
functionAbi: {
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
],
name: "isOwner",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "view",
type: "function",
},
chain: <chain where safe lives>,
functionParams: [":userAddress"],
returnValueTest: {
key: "",
comparator: "=",
value: "true",
},
},
]

3. Save key in Lit

Given the symmetric key and derived access control conditions, the key can now be saved in the Lit network.

import LitJsSdk from "@lit-protocol/sdk-browser";

// 1. Setup Lit SDK
const client = new LitJsSdk.LitNodeClient();
await client.connect();

// 2. Generate auth sig from user wallet
const authSig = await LitJsSdk.checkAndSignAuthMessage({ chain: <chain where safe lives> });

const encryptedSymmetricKey = await client.saveEncryptionKey({
accessControlConditions,
symmetricKey,
authSig,
chain: <chain where safe lives>,
});

4. Save object in IPFS

Given a LigoAgreement or LigoAgreementState object and the encryptedSymmetricKey, the object can be encrypted and stored in IPFS. Taken from the js-dag-jose example:

import { xc20pDirEncrypter, createJWE } from "did-jwt";
import { prepareCleartext } from "dag-jose-utils";

const agreement: LigoAgreement = ...

// 1. Prepare cleartext
const dirEncrypter = xc20pDirEncrypter(symmetricKey)
const cleartext = await prepareCleartext(agreement)

// 2. Encrypt + add encrypted key to protected header
const jwe = await createJWE(cleartext, [dirEncrypter], { encryptedSymmetricKey })

// 3. Put encrypted object in IPFS
const cid = await ipfs.dag.put(jwe, { format: dagJoseIpldFormat.codec, hashAlg: 'sha2-256' })

Decryption

1. Fetch object from IPFS

Given the CID, the encrypted agreement can be fetched from IPFS.

const retrieved = await ipfs.dag.get(cid);

const protHeader = JSON.parse(
u8a.toString(base64ToBytes(retrieved.value.protected))
);

2. Derive Access Control Conditions

The access control conditions define who has access to the symmetric key. This can be derived given the address of the Safe for the agreement (<safeAddress>).

const accessControlConditions = [
{
contractAddress: <safeAddress>,
functionName: "isOwner",
functionAbi: {
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
],
name: "isOwner",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "view",
type: "function",
},
chain: <chain where safe lives>,
functionParams: [":userAddress"],
returnValueTest: {
key: "",
comparator: "=",
value: "true",
},
},
]

3. Retrieve key from Lit

Given the symmetric key and derived access control conditions, the key can now be retrieved from the Lit network.

import LitJsSdk from "@lit-protocol/sdk-browser";
import {
base64ToBytes,
} from "did-jwt";

// 1. Setup Lit SDK
const client = new LitJsSdk.LitNodeClient();
await client.connect();

// 2. Generate auth sig from user wallet
const authSig = await LitJsSdk.checkAndSignAuthMessage({ chain: <chain where safe lives> });

const symmetricKey = await window.litNodeClient.getEncryptionKey({
accessControlConditions,
toDecrypt: base64ToBytes(protHeader.encryptedSymmetricKey),
chain: <chain where safe lives>,
authSig,
});

4. Decrypt object

import {
xc20pDirDecrypter,
decryptJWE,
} from "did-jwt";
import { decodeCleartext } from "dag-jose-utils";

const dirDecrypter = xc20pDirDecrypter(symmetricKey);
const decryptedData = await decryptJWE(retrieved.value, dirDecrypter);
const decryptedAgreement = decodeCleartext(decryptedData);