Skip to content

bowhead/CLAM

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

140 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CLAM

About

CLAM stands for Consent and Content Access Management Library. Prioritizing user data privacy, CLAM is an extensible tool that abstracts the complexities of various technologies, including blockchain and IPFS.

With blockchain as its foundation, CLAM allows developers to concentrate on implementing their solutions without getting bogged down by the intricate technical details of handling sensitive data. It addresses two primary concerns:

  • Consent: Acquiring user consent, especially when they're required to share sensitive data, poses significant challenges. By offering anonymous, decentralized, and immutable consent, CLAM boosts user enrollment. Users have the flexibility to withdraw their consent whenever they wish, prompting changes across all linked systems effortlessly.
  • Content Access Management: Users have the autonomy to determine when and with whom their information is shared. This process is managed through smart contracts, centralizing the logic and ensuring transparency for all stakeholders. The information remains encrypted at all times and is accessible solely by the owner and those they choose to share it with. This could range from doctors and researchers to any trusted entity the user designates.

Table of Contents

Requirements

  • NodeJS
  • npm (Node.js package manager)

Getting started

Installing

Using npm:

$ npm install bowhead-clam

Usage

ES6:

import * as clam from 'bowhead-clam';

Modular include:

const { IdentityManager } = require('bowhead-clam');

Including all libraries,

const clam = require('bowhead-clam');

Examples

How to create an identity

// Load IdentityManager module
import { IdentityManager } from "bowhead-clam";

const  generateIdentityMethod  =  async  ()  =>  {
	try  {
		const  identity : IdentityManager = new IdentityManager();

		await  identity.generateIdentity();
	}  catch  (error)  {
		console.log(error);
	}
}

How to accept a consent

Load modules, create instances and initialized them.

// Load FactoryWeb3Interaction, FactoryIdentity and FactoryInteraction modules
import { FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction } from 'bowhead-clam';

// Create  new FactoryIdentity instance
const factoryIdentity = new FactoryIdentity();
// Create new FactoryInteraction instance
const factoryInteraction = new FactoryInteraction();
// Create instance to interact with smart contracts
const cotractInteraction = factoryInteraction.generateInteraction('clam', 'clam', 'clam');

// Get identity instance that will use Open PGP to encrypt files
const identity = factoryIdentity.generateIdentity('PGP', 'PGP');

// Generate identity info
identity.generateIdentity();

// Get web3 provider instance
const factoryWeb3Provider = FactoryWeb3Interaction.getInstance();

Set Web3 provider configuration.

// Provider configuration
const interactionConfig = {
	// Set URL provider
	provider: 'http://localhost:8545',
	// Set chain ID
	chainId: 13,
	// Set address and ABI for Consent contract
	consent: { address: '0x09Fe1b1A9Cd73F35945Bfdc0378c9aCC227c0DBF', abi: ABIConsent },
	// Set address and ABI for Access contract
	access: { address: '0x82E54b8B226b007704D1203f0951138338CB921F', abi: ABIAccess },
	// Set address and ABI for Consent Resource contract
	consentResource: { address: '0x639c9197aB9be745A6D2CB6cB8c2d46D7BB9A412'), abi: ABIConsentResource },
	// Set address and ABI for IPFS management contract
	ipfs: { address: '0xB19Fb08e183fF19989792ceD10325BF0C45CCd27', abi: ABIIPFSManagement }
}

// Set Web3 provider configuration
factoryWeb3Provider.setConfig(interactionConfig);

Save consent approval.

// Consent identifier
const consentId = 'AAA1'

// Call saveConsent that it's contained in consentInteraction
await cotractInteraction.consentInteraction.saveConsent(consentId, identity);

How to share a document with other accounts

Load modules, create instances and initialized them.

// Load FactoryWeb3Interaction, FactoryIdentity and FactoryInteraction modules
import { FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction } from 'bowhead-clam';

// Create  new FactoryIdentity instance
const factoryIdentity = new FactoryIdentity();
// Create new FactoryInteraction instance
const factoryInteraction = new FactoryInteraction();
// Create instance to interact with smart contracts
const cotractInteraction = factoryInteraction.generateInteraction('clam', 'clam', 'clam');

// Get identity instance that will use Open PGP to encrypt files
const identity = factoryIdentity.generateIdentity('PGP', 'PGP');

// Generate identity info
identity.generateIdentity();

// Get web3 provider instance
const factoryWeb3Provider = FactoryWeb3Interaction.getInstance();

Set Web3 provider configuration.

// Provider configuration
const interactionConfig = {
	// Set URL provider
	provider: 'http://localhost:8545',
	// Set chain ID
	chainId: 13,
	// Set address and ABI for Consent contract
	consent: { address: '0x09Fe1b1A9Cd73F35945Bfdc0378c9aCC227c0DBF', abi: ABIConsent },
	// Set address and ABI for Access contract
	access: { address: '0x82E54b8B226b007704D1203f0951138338CB921F', abi: ABIAccess },
	// Set address and ABI for Consent Resource contract
	consentResource: { address: '0x639c9197aB9be745A6D2CB6cB8c2d46D7BB9A412'), abi: ABIConsentResource },
	// Set address and ABI for IPFS management contract
	ipfs: { address: '0xB19Fb08e183fF19989792ceD10325BF0C45CCd27', abi: ABIIPFSManagement }
}

// Set Web3 provider configuration
factoryWeb3Provider.setConfig(interactionConfig);

Add new user to allowed list to share documents by consent.

// Consent Identifier
const consentId = 'AAA1'
// Address of the user to add
const addressUserToShare = '0x93120bA8FBb9eF2f6744C7d50803A4390E4eF961'
// PGP public key of the user to add
const publicPGPKeyUserToShare = '-----BEGIN PGP PUBLIC KEY BLOCK----- ...'

// Call addKey that it's contained in consentInteraction
await cotractInteraction.consentInteraction.addKey(consentId, addressUserToShare, publicPGPKeyUserToShare, identity);

How to decrypt a shared document by other account

Load modules, create instances and initialized them.

// Load FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction, StorageEngine and DocumentSharing modules
import { FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction, StorageEngine, DocumentSharing } from 'bowhead-clam';

// Create  new FactoryIdentity instance
const factoryIdentity = new FactoryIdentity();
// Create new FactoryInteraction instance
const factoryInteraction = new FactoryInteraction();
// Create instance to interact with smart contracts
const cotractInteraction = factoryInteraction.generateInteraction('clam', 'clam', 'clam');

// Get identity instance that will use Open PGP to encrypt files
const identity = factoryIdentity.generateIdentity('PGP', 'PGP');

// Generate identity info
identity.generateIdentity();

// Get web3 provider instance
const factoryWeb3Provider = FactoryWeb3Interaction.getInstance();

// Create a new instance of StorageEngine and pass the configuration
const storageEngineFactory = new StorageEngine();
const storageEngine = storageEngineFactory.getStorageEngine();
storageEngine.setConfiguration({
	URL: 'http://localhost:3000',
	ApiKey: 'wXW9c5NObnsrZIY1J3Tqhvz4cZ7YQrrKnbJpo9xOqJM=',
	timeout: 2000
});

// Create DocumentSharing instance
const documentSharing = new DocumentSharing(storageEngine);

Set Web3 provider configuration.

// Provider configuration
const interactionConfig = {
	// Set URL provider
	provider: 'http://localhost:8545',
	// Set chain ID
	chainId: 13,
	// Set address and ABI for Consent contract
	consent: { address: '0x09Fe1b1A9Cd73F35945Bfdc0378c9aCC227c0DBF', abi: ABIConsent },
	// Set address and ABI for Access contract
	access: { address: '0x82E54b8B226b007704D1203f0951138338CB921F', abi: ABIAccess },
	// Set address and ABI for Consent Resource contract
	consentResource: { address: '0x639c9197aB9be745A6D2CB6cB8c2d46D7BB9A412'), abi: ABIConsentResource },
	// Set address and ABI for IPFS management contract
	ipfs: { address: '0xB19Fb08e183fF19989792ceD10325BF0C45CCd27', abi: ABIIPFSManagement }
}

// Set Web3 provider configuration
factoryWeb3Provider.setConfig(interactionConfig);

Get shared file.

const options = {
	cid: 'fe5c3e7fa0f43b8cbfed5e69c9a19c722c1900ff893ce7fa6b40646b88e46f48.txt', // CID file
	owner: '0x93120bA8FBb9eF2f6744C7d50803A4390E4eF961', // File owner address
	contractInteraction: cotractInteraction, // ContractInteraction instance
	consentId: 'SHARED' // Consent identifier
};

// Call getSharedFile function, return file in base64
const file = await documentSharing.getSharedFile(identity, options); // => 'dGVzdFYxMA=='

How to encrypt, save and decrypt a document using AES

Load modules, create instances and initialized them.

// Load FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction, StorageEngine and DocumentSharing modules
import { FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction, StorageEngine, DocumentSharing } from 'bowhead-clam';

// Create  new FactoryIdentity instance
const factoryIdentity = new FactoryIdentity();
// Create new FactoryInteraction instance
const factoryInteraction = new FactoryInteraction();
// Create instance to interact with smart contracts
const cotractInteraction = factoryInteraction.generateInteraction('clam', 'clam', 'clam');

// Get identity instance that will use AES to encrypt files
const identity = factoryIdentity.generateIdentity('AES', 'PGP');

// Generate identity info
identity.generateIdentity();

// Get web3 provider instance
const factoryWeb3Provider = FactoryWeb3Interaction.getInstance();

// Create a new instance of StorageEngine and pass the configuration
const storageEngineFactory = new StorageEngine();
const storageEngine = storageEngineFactory.getStorageEngine();
storageEngine.setConfiguration({
	URL: 'http://localhost:3000',
	ApiKey: 'wXW9c5NObnsrZIY1J3Tqhvz4cZ7YQrrKnbJpo9xOqJM=',
	timeout: 2000
});

// Create DocumentSharing instance
const documentSharing = new DocumentSharing(storageEngine);

Set Web3 provider configuration.

// Provider configuration
const interactionConfig = {
	// Set URL provider
	provider: 'http://localhost:8545',
	// Set chain ID
	chainId: 13,
	// Set address and ABI for Consent contract
	consent: { address: '0x09Fe1b1A9Cd73F35945Bfdc0378c9aCC227c0DBF', abi: ABIConsent },
	// Set address and ABI for Access contract
	access: { address: '0x82E54b8B226b007704D1203f0951138338CB921F', abi: ABIAccess },
	// Set address and ABI for Consent Resource contract
	consentResource: { address: '0x639c9197aB9be745A6D2CB6cB8c2d46D7BB9A412'), abi: ABIConsentResource },
	// Set address and ABI for IPFS management contract
	ipfs: { address: '0xB19Fb08e183fF19989792ceD10325BF0C45CCd27', abi: ABIIPFSManagement }
}

// Set Web3 provider configuration
factoryWeb3Provider.setConfig(interactionConfig);

Save encrypted file with AES.

const options = {
	file: 'dGVzdFYxMA==', // File in base64
	fileName: 'test.txt', // File name
	contractInteraction: cotractInteraction, // ContractInteraction instance
	consentId: 'AAA1' // Consent identifier
};

// Call save file function
const cid = await documentSharing.saveFile(identity, options);

// Save file register on IPFS Management contract
await contractInteraction.IPFSManagementInteraction.addFile(cid, options.fileName, identity);

Get encrypted file with AES.

const options = {
	cid: cid // CID file
};

// Call getFile function, return file in base64
const file = await documentSharing.getFile(identity, options);

How to encrypt, save and decrypt a document using Open PGP

Load modules, create instances and initialized them.

// Load FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction, StorageEngine and DocumentSharing modules
import { FactoryWeb3Interaction, FactoryIdentity, FactoryInteraction, StorageEngine, DocumentSharing } from 'bowhead-clam';

// Create  new FactoryIdentity instance
const factoryIdentity = new FactoryIdentity();
// Create new FactoryInteraction instance
const factoryInteraction = new FactoryInteraction();
// Create instance to interact with smart contracts
const cotractInteraction = factoryInteraction.generateInteraction('clam', 'clam', 'clam');

// Get identity instance that will use Open PGP to encrypt files
const identity = factoryIdentity.generateIdentity('PGP', 'PGP');

// Generate identity info
identity.generateIdentity();

// Get web3 provider instance
const factoryWeb3Provider = FactoryWeb3Interaction.getInstance();

// Create a new instance of StorageEngine and pass the configuration
const storageEngineFactory = new StorageEngine();
const storageEngine = storageEngineFactory.getStorageEngine();
storageEngine.setConfiguration({
	URL: 'http://localhost:3000',
	ApiKey: 'wXW9c5NObnsrZIY1J3Tqhvz4cZ7YQrrKnbJpo9xOqJM=',
	timeout: 2000
});

// Create DocumentSharing instance
const documentSharing = new DocumentSharing(storageEngine);

Set Web3 provider configuration.

// Provider configuration
const interactionConfig = {
	// Set URL provider
	provider: 'http://localhost:8545',
	// Set chain ID
	chainId: 13,
	// Set address and ABI for Consent contract
	consent: { address: '0x09Fe1b1A9Cd73F35945Bfdc0378c9aCC227c0DBF', abi: ABIConsent },
	// Set address and ABI for Access contract
	access: { address: '0x82E54b8B226b007704D1203f0951138338CB921F', abi: ABIAccess },
	// Set address and ABI for Consent Resource contract
	consentResource: { address: '0x639c9197aB9be745A6D2CB6cB8c2d46D7BB9A412'), abi: ABIConsentResource },
	// Set address and ABI for IPFS management contract
	ipfs: { address: '0xB19Fb08e183fF19989792ceD10325BF0C45CCd27', abi: ABIIPFSManagement }
}

// Set Web3 provider configuration
factoryWeb3Provider.setConfig(interactionConfig);

Save encrypted file with Open PGP

const options = {
	file: 'dGVzdFYxMA==', // File in base64
	fileName: 'test.txt', // File name
	contractInteraction: cotractInteraction, // ContractInteraction instance
	consentId: 'AAA1' // Consent identifier
};

// Get the list of users allowed to share files by consent
const usersToShare = await contractInteraction.consentInteraction.getKeys(options.consentId, options.identity);

// Save file encrypted with Open GPG
const cid = await documentSharing.sharedFile(identity, options, usersToShare.pgpKeys);

// Save file register on IPFS Management contract
await contractInteraction.IPFSManagementInteraction.addFile(cid, options.fileName, identity);

// Add the address of the users who can get the file in the Access contract
await contractInteraction.acccessInteraction.giveAccess(cid, options.consentId, [options.identity.address], options.fileName, options.identity);

// Get encrypted file with Open PGP

const getOptions = {
	cid: cid, // CID file
	owner: '0x93120bA8FBb9eF2f6744C7d50803A4390E4eF961', // File owner address
	contractInteraction: cotractInteraction, // ContractInteraction instance
	consentId: 'SHARED1' // Consent identifier
};

// Call getSharedFile function, return file in base64
const fileShared1 = await documentSharing.getSharedFile(identity, getOptions);

Use a custom encryption algorithm

Create new implementation based on IEncryptionLayer

import { IEncryptionLayer } from 'bowhead-clam'

/**
 * Class EncryptionLayerLULU
 */
class EncryptionLayerLULU implements IEncryptionLayer {
    /**
     * Code
     * 
     * @param {string} key parameter
     * @param {string} data parameter
     * @returns {Promise<string>} return string
     */
    async ecryptData(key: string, data: string): Promise<string> {
        const dataEncrypted: string = key + '-' + data;
        return dataEncrypted;
    }

    /**
     * Code
     * 
     * @param {string} key parameter
     * @param {string} data parameter
     * @returns {Promise<string>} return string 
     */
    async decryptData(key: string, data: string): Promise<string> {
        console.log(key);
        const dataDecrypted: string = data.split('-')[1];
        return dataDecrypted;
    }

}

Register the new implementation

// Load FactoryIndentity module
import { FactoryIdentity } from 'bowhead-clam'

// Create new FactoryIdentity instance
const factoryIdentity = new FactoryIdentity();

// Register a new encryption implementation
factoryIdentity.setOptionEncryption({ name: 'LULU', option: EncryptionLayerLULU });

// Get an identity instance that will use the new encryption implementation
const identity: IdentityManager = factoryIdentity.generateIdentity('LULU', 'PGP');

Use a custom contract interaction

Create new implementation for consent based on IConsentInteraction

// Load IConsentInteraction module
import { IConsentInteraction } from 'bowhead-clam'

class ConsentInteractionOther implements IConsentInteraction {
    /**
     * This function saves a consent with the information passed as parameters. 
     * @param {string} consentId This parameter is the consentID to identify consent.
     * @param {IdentityManager} identity This parameter is the Identity to configurate the smart contract interaction.
     * @returns {Promise<boolean>} return the address of the transaction.
     */
    async saveConsent(consentId: string, identity: IdentityManager): Promise<boolean> {
        return consentId && identity ? true : false;
    }
    /**
     * This function cancel a consent based in the consentID passed in th eparameter.
     * @param {string} consentId This parameter is the consentID to identify consent.
     * @param {IdentityManager} identity This parameter is the Identity to configurate the smart contract interaction.
     * @returns {Promise<boolean>} return the address of the transaction.
     */
    async cancelConsent(consentId: string, identity: IdentityManager): Promise<boolean> {
        return consentId && identity? true : false;
    }

    /**
     * This function return the consent status based in the consentID passed in the parameter.
     * @param {string} consentId This parameters is the consentID to indentify the consent.
     * @param {string} owner This parameter is the owner addres.
     * @param {IdentityManager} identity This parameter is the Identity to configurate the smart contract interaction.
     * @returns {Promise<boolean>} return the consent status. 
     */
    async getConsentById(consentId: string, owner: string, identity: IdentityManager): Promise<boolean> {
        return consentId && owner && identity ? true : false;
    }

    /**
     * This funtion add a key in a consent based in the consentID in the case if the consent has already been acepted.
     * @param {string} consentId This parameter is the consentID to indentify the consent.
     * @param {string} addressConsent This parameter is the adressConsent to indentify the consent.
     * @param {string} key  This parameter is the key to be added in the consent.
     * @param {IdentityManager} identity This parameter is the Identity to configurate the smart contract interaction. 
     * @returns {Promise<boolean>}  return the address of the transaction.  
     */
    async addKey(consentId: string, addressConsent: string, key: string, identity: IdentityManager): Promise<boolean> {
        return consentId && addressConsent && identity && key? true : false;
    }

    /**
     * This funtion return the addres's and keys's the consent based in the consentId.
     * @param {string} consentId This parameter is the consentID to indentify the consent.
     * @param {IdentityManager} identity This parameter is the Identity to configurate the smart contract interaction. 
     * @returns {Promise<any>} return the addres's and keys's
     */
    async getKeys(consentId: string, identity: IdentityManager): Promise<IConsentKeys> {
        return {'0': [consentId], '1': [identity? '': '']};
    }
}

Register the new implementation

// Load FactoryInteraction
import { FactoryInteraction } from 'bowhead-clam';

// Create a factory interaction instance
const factoryInteraction = new FactoryInteraction();

// Set the new implementation
factoryInteraction.setOptionConsentInteraction({ name: 'other', option: ConsentInteractionOther });

// Get the contract interaction and pass the name of the new implementation as a parameter
const contractInteraciton = factoryInteraction.generateInteraction('other', 'clam', 'clam');

NOTE: The function generateInteraction receive three parameters, the first corresponds to ConsentInteraction, the second to AccessInteraction and the third to IPFSManagementInteraction.

Pass 'clam' to keep the deault implementation.

Use a custom storage engine

Create new implementation based on IStorageEngine

// Load IStorageEngine module
import { IStorageEngine } from 'bowhead-clam'

/**
 * Temporal file type
 */
class TemporalFile {
	name: string;
	cid: string;
	file: string;
}

/**
 * Temporal file search
 */
class TemporalFileSearch {
	cid: string;
}

/**
 * Temporal storage engine
 */
class TemporalEngine implements IStorageEngine {
	private files = new Array<TemporalFile>;

	/**
	 * Save temporal file
	 * @param {object} options - File to save and additional parameters
	 * @returns {string} returns the file identifier or location
	 */
	async saveFile(options: object): Promise<string> {
		const fileInfo = options as TemporalFile;
		const cid = (Math.random() + 1).toString(36).substring(7);
		fileInfo.cid = cid;
		this.files.push(fileInfo);
		return cid;
	}

	/**
	 * Get file from storage engine
	 * @param {object} options - File identifier or location and additional parameters
	 * @returns {string} returns the file
	 */
	async getFile(options: object): Promise<string> {
		const fileInfo = options as TemporalFileSearch;
		
		const file = this.files.find(file => {
			return file.cid === fileInfo.cid;
		});

		return file?.file || '';
	}

	/**
	 * Update file stored
	 * @param {object} options - File identifier or location and additional parameters
	 */
	async updateFile(options: object): Promise<void> {
		const fileInfo = options as TemporalFile;

		const index = this.files.findIndex((file => file.cid === fileInfo.cid));

		this.files[index].file = fileInfo.file;
	}

	/**
	 * Delete file from storage engine
	 * @param {object} options - Identifier of the file to delete and additional parameters
	 */
	async deleteFile(options: object): Promise<void> {
		const fileInfo = options as TemporalFileSearch;

		const index = this.files.map(file => file.cid).indexOf(fileInfo.cid);

		this.files.splice(index, 1);
	}

	/**
	 * Set storage options
	 * @param {object} options - Configuration options
	 */
	setConfiguration(options: object): void {
		console.error(options);
	}
}

Register the new implementation

// Load StorageEngine module
import { StorageEngine } from 'bowhead-clam';

// Create a storage engine instance and pass the new implementation as a parameter in the constructor
const storageEngineFactory = new StorageEngine(TemporalEngine);

// Get storage engine
const storageEngine = storageEngineFactory.getStorageEngine();

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •