0.1.6 • Published 7 days ago

@lighthouse-web3/kavach v0.1.6

Weekly downloads
-
License
MIT
Repository
github
Last release
7 days ago

Kavach

Kavach is an encryption SDK that allows you to build your trustless, decentralized and fault-tolerant Applications using distributed key shards with threshold cryptography

Features

  • 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)

Install

Just use your favorite package manager to add `lighthouse-kavach to your project:

yarn add @lighthouse-web3/kavach

npm i @lighthouse-web3/kavach

Methods

  • generate ( threshold?: number, keyCount?: number)

This method generates randomized key shards

Parameters

NameTypeDefaultDescription
thresholdnumber(optional)3minimum amount of key required to recover master key
keyCountnumber(optional)5number of shades to be generated (Note: must be greater than or equal to threshold)

returns

NameTypeDescription
masterKeystring32 byte string or key
keyShards{key:string,index:string}[]key shards

Demo

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);
  });
  • recoverKey (keyShards: keyShard[])

This method recovers the master key from the shards generated

Parameters

NameTypeDescription
keyShard{key:string,index:string}[]minimum amount of key required to recover master key

returns

NameTypeDescription
masterKeystring32 byte string or key
errorErrorValuenull

Demo

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);
  });
  • shardKey (key: string,threshold?: number, keyCount?: number)

shard existing Key into shards

Parameters

NameTypeDefaultDescription
keystring32 byte string or key
thresholdnumber(optional)3minimum amount of key required to recover master key
keyCountnumber(optional)5number of shades to be generated (Note: must be greater than or equal to threshold)

returns

NameTypeDescription
isShardablebooleanreturn true is the key could be sharded
keyShardskeyShard[]shards

Demo

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);
  });
  • saveShards( address: string, cid: string, auth_token: string, keyShards: keyShard5 | any5, shareTo : string[])

Backup key to lighthouse's Node

Parameters

NameTypeDefaultDescription
addressstringaddress of the owner of the key
cidstringunique id or file CID
auth_tokenstringsigned Message gotten from getAuthMessage/JWT
keyShardsArray<{key:string; index:string}>An array of 5 key shards/ element
shareToArray< address >(Optional)[]An array of address

returns

NameTypeDescription
isSuccessbooleanreturn true is saved
errorErrorValueErrors

Demo

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);
  });
  • recoverShards( address: string, cid: string, auth_token: string, dynamicData:{},)

recover key shards to lighthouse's Node

Parameters

NameTypeDefaultDescription
addressstringaddress of the owner of the key
cidstringunique id or file CID
auth_tokenstringsigned Message gotten from getAuthMessage/JWT
keyCountnumber(optional)3number of nodes to ping for shards (Note: must be less than or equal to 5)
dynamicDataobject<{conditionID.parameterName: value} >(Optional){}This is used to pass additional or dynamic data like a signature during key recovery with AccessControl

returns

NameTypeDescription
keyShardsArray<{key:string; index:string}>key shards recovered fromm nodes
errorErrorValueErrors

Demo

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);
  });
  • shareToAddress(address: string, cid: string, auth_token: string, shareTo: Array )

Share file Key to address

Parameters

NameTypeDefaultDescription
addressstringaddress of the owner of the key
cidstringunique id or file CID
auth_tokenstringsigned Message gotten from getAuthMessage/ JWT
shareToArray< address >(Optional)[]An array of address to share file key shards to

returns

NameTypeDescription
isSuccessbooleanreturn true if successful
errorErrorValueErrors

Demo

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);
  });
  • revokeAccess(address: string, cid: string, auth_token: string, revokeTo: Array )

revoke access to addresses with direct access

Parameters

NameTypeDefaultDescription
addressstringaddress of the owner of the key
cidstringunique id or file CID
auth_tokenstringsigned Message gotten from getAuthMessage /JWT
revokeToArray< address >(Optional)[]An array of address to remove for Direct access

returns

NameTypeDescription
isSuccessbooleanreturn true if successful
errorErrorValueErrors

Demo

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);
  });
  • accessCondition( address: string, cid: string, auth_token: string, conditions: Condition[], aggregator?: string,chainType?: ChainType, keyShards? : Array<{key:string; index:string}>, decryptionType? : string )

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

Parameters

NameTypeDescription
addressstringAddress of the owner of the key
cidstringUnique id or file CID
auth_tokenstringSigned Message gotten from getAuthMessage /JWT
conditionsArray< Condition >This Array contains a list of conditions to be tested on chain
aggregatorstringThis is a template string that structures how the conditions should be computed
chainTypestringThis 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?stringThis value can be set to ACCESS_CONDITIONS to first time shard is added, WARNING: This sets Owner to address zero(0x0000000000000000000000000000)

returns

NameTypeDescription
isSuccessbooleanreturn true if successful
errorErrorValueErrors

Demo

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);
  });

Auth Methods

  • getAuthMessage( address: string)

Get Consensus Message to Sign

Parameters

NameTypeDefaultDescription
addressstringaddress of the owner of the key

returns

NameTypeDescription
messagestringreturn consensus message
errorErrorValueErrors
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);
  });
  • getJWT(address:string,signedMessage: SignedMessage)

Get Consensus Message to Sign

Parameters

NameTypeDefaultDescription
addressstringaddress of the owner of the key
payloadstringsigned consensus message or refresh Token
useAsRefreshTokenbooleanfalseIf payload is refreshToken this should be set to true

returns

NameTypeDescription
JWTstringreturn JWT
refreshTokenstring
errorErrorValueErrors
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);
  });
  • transferOwnership(address: string, cid: string, newOwner: string, auth_token: string, resetSharedTo: boolean = true)

Transfer Ownership of a Resource

Parameters

NameTypeDefaultDescription
addressstringAddress of the current owner of the resource
cidstringContent ID (CID) of the resource
newOwnerstringAddress of the new owner for the resource
auth_tokenstringAuthentication payload or token
resetSharedTobooleantrueReset shared permissions when ownership changes

Returns

NameTypeDescription
resultstringResult of the ownership transfer
errorErrorAny error that occurs

Example

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);
  });