lighthouse-encryption-sdk-browser
Version:
Encryption SDK: Build your trustless, decentralized and fault resistance Application using distributed key shades with threshold cryptography
806 lines (639 loc) • 26.4 kB
Markdown
# Kavach <img src="https://img.shields.io/badge/BETA-v0.1.6-green"/>
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:
```sh
yarn add @lighthouse-web3/kavach
npm i @lighthouse-web3/kavach
```
## Methods
- ### _generate ( threshold?: number, keyCount?: number)_
This method generates randomized key shards
#### Parameters
| 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_) |
#### returns
| Name | Type | Description |
| --------- | --------------------------- | ---------------------- |
| masterKey | string | 32 byte string or key |
| keyShards | {key:string,index:string}[] | key shards |
#### Demo
```javascript
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
| Name | Type | Description |
| -------- | --------------------------- | ---------------------------------------------------- |
| keyShard | {key:string,index:string}[] | minimum amount of key required to recover master key |
#### returns
| Name | Type | Description |
| --------- | ---------- | --------------------- |
| masterKey | string | 32 byte string or key |
| error | ErrorValue | null |
#### Demo
```javascript
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
| 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_) |
#### returns
| Name | Type | Description |
| ----------- | ---------- | --------------------------------------- |
| isShardable | boolean | return true is the key could be sharded |
| keyShards | keyShard[] | shards |
#### Demo
```javascript
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: keyShard[5] | any[5], shareTo : string[])_
Backup key to lighthouse's Node
#### Parameters
| 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 |
#### returns
| Name | Type | Description |
| --------- | ---------- | -------------------- |
| isSuccess | boolean | return true is saved |
| error | ErrorValue | Errors |
#### Demo
```javascript
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
| 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 |
#### returns
| Name | Type | Description |
| ---------------- | ---------------------------------- | ----------------------------------|
| keyShards | Array<{key:string; index:string}> | key shards recovered fromm nodes |
| error | ErrorValue | Errors |
#### Demo
```javascript
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<string> )_
Share file Key to address
#### Parameters
| 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 |
#### returns
| Name | Type | Description |
| --------- | ---------- | ------------------------- |
| isSuccess | boolean | return true if successful |
| error | ErrorValue | Errors |
#### Demo
```javascript
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<string> )_
revoke access to addresses with direct access
#### Parameters
| 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 |
#### returns
| Name | Type | Description |
| --------- | ---------- | ------------------------- |
| isSuccess | boolean | return true if successful |
| error | ErrorValue | Errors |
#### Demo
```javascript
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
| 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)** |
#### returns
| Name | Type | Description |
| --------- | ---------- | ------------------------- |
| isSuccess | boolean | return true if successful |
| error | ErrorValue | Errors |
#### Demo
```javascript
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
| Name | Type | Default | Description |
| ------- | ------ | ------- | ------------------------------- |
| address | string | | address of the owner of the key |
#### returns
| Name | Type | Description |
| ------- | ---------- | ------------------------ |
| message | string | return consensus message |
| error | ErrorValue | Errors |
```javascript
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
| 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 |
#### returns
| Name | Type | Description |
| ----------------| ---------- | ----------- |
| JWT | string | return JWT |
| refreshToken | string | |
| error | ErrorValue | Errors |
```javascript
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
| 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 |
#### Returns
| Name | Type | Description |
| ---------- | ------ | --------------------------------- |
| result | string | Result of the ownership transfer |
| error | Error | Any error that occurs |
#### Example
```javascript
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);
});
```