Kavach is an encryption SDK that allows you to build your trustless, decentralized and fault-tolerant Applications using distributed key shards with threshold cryptography
- Randomized key shard generation
- Shard Key support for privateKey and other security keys
- Key Reconstruction from shards
- Fully typed, support in TypeScript
- Lighthouse Encryption Key storage(Optional 5 nodes)
Just use your favorite package manager to add `lighthouse-kavach to your project:
yarn add @lighthouse-web3/kavach
npm i @lighthouse-web3/kavach
This method generates randomized key shards
| Name | Type | Default | Description | 
|---|---|---|---|
| threshold | number(optional) | 3 | minimum amount of key required to recover master key | 
| keyCount | number(optional) | 5 | number of shades to be generated (Note: must be greater than or equal to threshold) | 
| Name | Type | Description | 
|---|---|---|
| masterKey | string | 32 byte string or key | 
| keyShards | {key:string,index:string}[] | key shards | 
import { generate } from "@lighthouse-web3/kavach";
async function main() {
  const { masterKey, keyShards } = await generate();
  console.log(`masterKey: ${masterKey}`);
  console.log(`keyShards:`, keyShards);
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });This method recovers the master key from the shards generated
| Name | Type | Description | 
|---|---|---|
| keyShard | {key:string,index:string}[] | minimum amount of key required to recover master key | 
| Name | Type | Description | 
|---|---|---|
| masterKey | string | 32 byte string or key | 
| error | ErrorValue | null | 
import { generate, recoverKey } from "@lighthouse-web3/kavach";
async function main() {
  const { masterKey, keyShards } = await generate();
  const { masterKey: recoveredKey } = await recoverKey(keyShards);
  console.log(masterKey === recoveredKey); //true
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });shard existing Key into shards
| Name | Type | Default | Description | 
|---|---|---|---|
| key | string | 32 byte string or key | |
| threshold | number(optional) | 3 | minimum amount of key required to recover master key | 
| keyCount | number(optional) | 5 | number of shades to be generated (Note: must be greater than or equal to threshold) | 
| Name | Type | Description | 
|---|---|---|
| isShardable | boolean | return true is the key could be sharded | 
| keyShards | keyShard[] | shards | 
import { shardKey, recoverKey } from "@lighthouse-web3/kavach";
async function main() {
  // known key customly generated or from ether random wallet privateKey
  // Note: Not all keys are shardable
  const knownKey =
    "554f886019b74852ab679258eb3cddf72f12f84dd6a946f8afc4283e48cc9467";
  const { isShardable, keyShards } = await shardKey(knownKey);
  console.log(isShardable); // true
  //recover keys from shards
  const { masterKey } = await recoverKey(keyShards);
  //check if the key recovered was recovered
  console.log(masterKey === knownKey); //true
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });Backup key to lighthouse's Node
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | address of the owner of the key | |
| cid | string | unique id or file CID | |
| auth_token | string | signed Message gotten from getAuthMessage/JWT | |
| keyShards | Array<{key:string; index:string}> | An array of 5 key shards/ element | |
| shareTo | Array< address >(Optional) | [] | An array of address | 
| Name | Type | Description | 
|---|---|---|
| isSuccess | boolean | return true is saved | 
| error | ErrorValue | Errors | 
import { getAuthMessage, saveShards, generate } from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  const signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  const { masterKey, keyShards } = await generate();
  const authMessage = await getAuthMessage(signer.address);
  const signedMessage = await signer.signMessage(authMessage.message);
  const { error, isSuccess } = await saveShards(
    signer.address,
    "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH",
    signedMessage,
    keyShards
  );
  console.log(error === null); // true;
  console.log(isSuccess === true); //true;
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });recover key shards to lighthouse's Node
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | address of the owner of the key | |
| cid | string | unique id or file CID | |
| auth_token | string | signed Message gotten from getAuthMessage/JWT | |
| keyCount | number(optional) | 3 | number of nodes to ping for shards (Note: must be less than or equal to 5) | 
| dynamicData | object<{[ conditionID.parameterName]: value} >(Optional) | {} | This is used to pass additional or dynamic data like a signature during key recovery with AccessControl | 
| Name | Type | Description | 
|---|---|---|
| keyShards | Array<{key:string; index:string}> | key shards recovered fromm nodes | 
| error | ErrorValue | Errors | 
import {
  getAuthMessage,
  saveShards,
  generate,
  recoverShards,
} from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  const signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  const { masterKey, keyShards } = await generate();
  let authMessage = await getAuthMessage(signer.address);
  let signedMessage = await signer.signMessage(authMessage.message);
  const { error, isSuccess } = await saveShards(
    signer.address,
    "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH",
    signedMessage,
    keyShards
  );
  console.log(error === null); // true;
  console.log(isSuccess === true); //true;
  authMessage = await getAuthMessage(signer.address);
  signedMessage = await signer.signMessage(authMessage.message);
  //retrieve 3 keys
  const { error, shards } = await recoverShards(
    signer.address,
    cid,
    signedMessage,
    3
  );
  console.log(error == null); //true;
  console.log(shards.length === 3); // true;
  const { masterKey: recoveredKey } = await recoverKey(shards);
  console.log(masterKey === recoveredKey); //true
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });Share file Key to address
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | address of the owner of the key | |
| cid | string | unique id or file CID | |
| auth_token | string | signed Message gotten from getAuthMessage/ JWT | |
| shareTo | Array< address >(Optional) | [] | An array of address to share file key shards to | 
| Name | Type | Description | 
|---|---|---|
| isSuccess | boolean | return true if successful | 
| error | ErrorValue | Errors | 
import {
  recoverShards,
  getAuthMessage,
  saveShards,
  AuthMessage,
  shareToAddress,
  generate,
} from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  let signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  let signer2 = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c99"
  );
  const cid = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwAV";
  const { masterKey, keyShards } = await generate();
  //save file
  {
    const authMessage: AuthMessage = await getAuthMessage(signer.address);
    const signedMessage = await signer.signMessage(authMessage.message);
    const { error, isSuccess } = await saveShards(
      signer.address,
      cid,
      signedMessage,
      keyShards
    );
    console.log(error == null); //true;
    console.log(isSuccess == true); //true;
  }
  //share file key to address address
  {
    const authMessage: AuthMessage = await getAuthMessage(signer.address);
    const signedMessage = await signer.signMessage(authMessage.message);
    const { error, isSuccess } = await shareToAddress(
      signer.address,
      cid,
      signedMessage,
      [signer2.address]
    );
    console.log(error == null); // true;
    console.log(isSuccess == true); //true;
  }
  //recover shared from address shared to
  {
    const authMessage: AuthMessage = await getAuthMessage(signer2.address);
    const signedMessage = await signer2.signMessage(authMessage.message);
    //retrieve 3 keys
    const { error, shards } = await recoverShards(
      signer2.address,
      cid,
      signedMessage,
      3
    );
    console.log(error == null); //true;
    console.log(shards.length === 3); // true;
  }
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });revoke access to addresses with direct access
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | address of the owner of the key | |
| cid | string | unique id or file CID | |
| auth_token | string | signed Message gotten from getAuthMessage /JWT | |
| revokeTo | Array< address >(Optional) | [] | An array of address to remove for Direct access | 
| Name | Type | Description | 
|---|---|---|
| isSuccess | boolean | return true if successful | 
| error | ErrorValue | Errors | 
import {
  recoverShards,
  getAuthMessage,
  saveShards,
  AuthMessage,
  revokeAccess,
  generate,
} from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  let signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  let signer2 = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c99"
  );
  const cid = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwVV";
  const { masterKey, keyShards } = await generate();
  //save file
  {
    const authMessage: AuthMessage = await getAuthMessage(signer.address);
    const signedMessage = await signer.signMessage(authMessage.message);
    const { error, isSuccess } = await saveShards(
      signer.address,
      cid,
      signedMessage,
      keyShards,
      [
        "0x95CF5354519a6ad2bD7e53fe7763201dfB24bFE4",
        "0xb46D27B3BfC07D27702EBddbe197Fc9276b70581",
        signer2.address,
      ]
    );
    console.log(error == null); //true;
    console.log(isSuccess == true); //true;
  }
  //recover shared from address shared to
  {
    const authMessage: AuthMessage = await getAuthMessage(signer2.address);
    const signedMessage = await signer2.signMessage(authMessage.message);
    //retrieve 3 keys
    const { error, shards } = await recoverShards(
      signer2.address,
      cid,
      signedMessage,
      3
    );
    console.log(error == null); //true;
    console.log(shards.length === 3); // true;
  }
  //revoke access to direct shared address
  {
    const authMessage: AuthMessage = await getAuthMessage(signer.address);
    const signedMessage = await signer.signMessage(authMessage.message);
    const { error, isSuccess } = await revokeAccess(
      signer.address,
      cid,
      signedMessage,
      [signer2.address]
    );
    console.log(error == null); // true;
    console.log(isSuccess == true); //true;
  }
  //recover shared from address shared to
  {
    const authMessage: AuthMessage = await getAuthMessage(signer2.address);
    const signedMessage = await signer2.signMessage(authMessage.message);
    //retrieve 3 keys
    const { error, shards } = await recoverShards(
      signer2.address,
      cid,
      signedMessage,
      3
    );
    console.log(error); // { message: "you don't have access", data: {} };
    console.log(shards.length === 0); // true ;
  }
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });Add more granular access Conditions based on on-Chain Data, this supports custom EVM contracts, block timestamps and so on. with support for over 15 Ethereum Virtual Machine (EVM) based networks and based Solana RPC calls
- Ethereum
- Rinkeby
- Polygon
- Fantom
- FantomTest
- AVAX
- Fuji
- BSC
- BSCTest
- Optimism
- OptimismGoerli
- OptimismKovan
- Mumbai
- FVM
- Wallaby
- Calibration
- Shardeum
- Goerli
- Hyperspace
- BTTC
- BTTC_Testnet
- Sepolia_PGN
- Arbitrum_Sepolia
- Sepolia:
- BASE_Goerli
Solana
- DEVNET
- TESTNET
- MAINNET
| Name | Type | Description | 
|---|---|---|
| address | string | Address of the owner of the key | 
| cid | string | Unique id or file CID | 
| auth_token | string | Signed Message gotten from getAuthMessage /JWT | 
| conditions | Array< Condition > | This Array contains a list of conditions to be tested on chain | 
| aggregator | string | This is a template string that structures how the conditions should be computed | 
| chainType | string | This defaults to EVM and can be set to Solana for Solana conditions | 
| keyShards? | Array<{key:string; index:string}> | This Field is optional, you can use it to set, overWrite or rotate key shards | 
| decryptionType? | string | This value can be set to ACCESS_CONDITIONS to first time shard is added, WARNING: This sets Owner to address zero(0x0000000000000000000000000000) | 
| Name | Type | Description | 
|---|---|---|
| isSuccess | boolean | return true if successful | 
| error | ErrorValue | Errors | 
import {
  recoverShards,
  getAuthMessage,
  saveShards,
  AuthMessage,
  accessControl,
  generate,
} from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  let signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  let signer2 = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c99"
  );
  const cid = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwVM";
  //save file
  {
    const authMessage: AuthMessage = await getAuthMessage(signer.address);
    const signedMessage = await signer.signMessage(authMessage.message);
    const { masterKey, keyShards } = await generate();
    const { error, isSuccess } = await saveShards(
      signer.address,
      cid,
      signedMessage,
      keyShards
    );
    console.log(error == null); //true;
    console.log(isSuccess == true); //true;
  }
  // add access control to cid direct shared address
  {
    const authMessage: AuthMessage = await getAuthMessage(signer.address);
    const signedMessage = await signer.signMessage(authMessage.message);
    const { error, isSuccess } = await accessControl(
      signer.address,
      cid,
      signedMessage,
      [
        {
          id: 3,
          chain: "Polygon",
          method: "getBlockNumber",
          standardContractType: "",
          returnValueTest: { comparator: ">=", value: "0" },
        },
        {
          id: 2,
          chain: "Optimism",
          method: "getBalance",
          standardContractType: "",
          returnValueTest: { comparator: ">=", value: "0" },
        },
        {
          id: 1,
          chain: "FantomTest",
          method: "balanceOf",
          standardContractType: "ERC20",
          contractAddress: "0xF0Bc72fA04aea04d04b1fA80B359Adb566E1c8B1",
          returnValueTest: { comparator: ">=", value: "0" },
          parameters: [":userAddress"],
        },
      ],
      "([2] and [1]) or [3]"
    );
    console.log(error == null);
    console.log(isSuccess == true);
  }
  // recover shared from an address that matches the above condition
  // that is
  // has a balance equal to or greater than Zero on the Optimism mainnet and has a token balance greater than equal to zero of the token 0xF0Bc72fA04aea04d04b1fA80B359Adb566E1c8B1 on fantom's testnet
  // or if block height is greater than zero
  {
    const authMessage: AuthMessage = await getAuthMessage(signer2.address);
    const signedMessage = await signer2.signMessage(authMessage.message);
    console.log(signer2.address);
    //retrieve 3 keys
    const { error, shards } = await recoverShards(
      signer2.address,
      cid,
      signedMessage,
      3,
      dynamicData
    );
    console.log(error == null); //true;
    console.log(shards.length === 3); // true;
  }
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });Get Consensus Message to Sign
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | address of the owner of the key | 
| Name | Type | Description | 
|---|---|---|
| message | string | return consensus message | 
| error | ErrorValue | Errors | 
import { getAuthMessage, AuthMessage } from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  let signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  // get consensus message
  const authMessage: AuthMessage = await getAuthMessage(signer.address);
  const signedMessage = await signer.signMessage(authMessage.message);
  console.log(typeof authMessage.message == "string"); //true;
  console.log(authMessage.error == null); //true;
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });Get Consensus Message to Sign
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | address of the owner of the key | |
| payload | string | signed consensus message or refresh Token | |
| useAsRefreshToken | boolean | false | If payload is refreshToken this should be set to true | 
| Name | Type | Description | 
|---|---|---|
| JWT | string | return JWT | 
| refreshToken | string | |
| error | ErrorValue | Errors | 
import { getAuthMessage, AuthMessage, getJWT } from "@lighthouse-web3/kavach";
import { ethers } from "ethers";
async function main() {
  let signer = new ethers.Wallet(
    "0x8218aa5dbf4dbec243142286b93e26af521b3e91219583595a06a7765abc9c8b"
  );
  // get consensus message
  const authMessage: AuthMessage = await getAuthMessage(signer.address);
  const signedMessage = await signer.signMessage(authMessage.message);
  const { JWT, error } = await getJWT(signer.address, signedMessage);
  console.log(typeof JWT == "string"); //true;
  console.log(error == null); //true;
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });Transfer Ownership of a Resource
| Name | Type | Default | Description | 
|---|---|---|---|
| address | string | Address of the current owner of the resource | |
| cid | string | Content ID (CID) of the resource | |
| newOwner | string | Address of the new owner for the resource | |
| auth_token | string | Authentication payload or token | |
| resetSharedTo | boolean | true | Reset shared permissions when ownership changes | 
| Name | Type | Description | 
|---|---|---|
| result | string | Result of the ownership transfer | 
| error | Error | Any error that occurs | 
import { transferOwnership } from "@lighthouse-web3/kavach";
// Example usage of transferOwnership function
async function main() {
  const currentOwner = "0x1234567890abcdef";
  const resourceId = "QmXyZAbCdEfGhIjK";
  const newOwner = "0x9876543210fedcba";
  const authPayload = "your-authentication-token";
  const { result, error } = await transferOwnership(
    currentOwner,
    resourceId,
    newOwner,
    authPayload
  );
  if (error) {
    console.error("Error:", error);
  } else {
    console.log("Ownership transfer result:", result);
  }
}
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });