Sharing
Class Name |
Sharing |
|---|---|
Extends |
|
Source |
|
Examples |
For getting a better understanding about how Sharings and Multikeys work, have a look at Security in the evan.network wiki.
Following is a sample for a sharing info with these properties:
three users
0x01- owner of a contract0x02- member of a contract0x03- another member with differing permissions
two timestamps
block 82745 - first sharing
block 90000 - splitting data, update sharings
three sections
*generic “catch all” used in first sharingsecret area- available for all memberssuper secret area- available for0x03
Keep in mind, that an actual sharings object only stores the sha3-hashes of every property. For example, sharings for the user 0x01 were actually to be found at the property “0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2”. For the sake of understanding, the following sample uses clear text properties. For an example of an actual sharings object, please refer to the getSharings example section.
{
"0x01": {
"*": {
"82745": {
"private": "secret for 0x01, starting from block 82745 for all data",
"cryptoInfo": {
"originator": "0x01,0x01",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
},
"secret area": {
"90000": {
"private": "secret for 0x01, starting from block 90000 for 'secret area'",
"cryptoInfo": {
"originator": "0x01,0x01",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
},
"super secret area": {
"90000": {
"private": "secret for 0x01, starting from block 90000 for 'super secret area'",
"cryptoInfo": {
"originator": "0x01,0x01",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
}
},
"0x02": {
"*": {
"82745": {
"private": "secret for 0x02, starting from block 82745 for all data",
"cryptoInfo": {
"originator": "0x01,0x02",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
},
"secret area": {
"90000": {
"private": "secret for 0x02, starting from block 90000 for 'secret area'",
"cryptoInfo": {
"originator": "0x01,0x02",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
},
"super secret area": {
"90000": {
"private": "secret for 0x02, starting from block 90000 for 'super secret area'",
"cryptoInfo": {
"originator": "0x01,0x02",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
}
},
"0x03": {
"secret area": {
"90000": {
"private": "secret for 0x03, starting from block 90000 for 'secret area'",
"cryptoInfo": {
"originator": "0x01,0x03",
"keyLength": 256,
"algorithm": "aes-256-cbc"
}
}
}
}
}
More information about sharings can be found at the evan.network wiki.
There are two functions to share keys with another user:
addSharing is used for easily sharing keys to another user. There is no need to explicitly share hash keys to this other user as this is automatically covered here. This approach make up to two transaction (1 for hash key and 1 for the content key), which may sum up to a whole bunch of transactions when sharing multiple keys to multiple users.
extendSharing is used to edit a sharings configuration that has been pulled or “checked out” with getSharingsFromContract. Hash keys have to be shared manually, if required. extendSharing make no transaction, so the contract isn’t updated - this has to be done with saveSharingsToContract. See function documentation below for an example with hash key and storing updates.
Be careful when performing multiple updates to sharings synchronously. As sharings are retrieved as a single file from a smart contract, updated and then saved back to it, doing two or more updates in parallel may overwrite each other and lead to unexpected and most probably undesired results.
Perform sharing updates for the same contracts one after another, this goes for addSharing and for extendSharing. When wishing to speed things up, extendSharing can be used, but its updates need to be performed synchronously as well. Keep in mind, that single updates will be made off-chain and therefore be performed much faster than multiple updates with addSharing.
constructor
new Sharing(options);
Creates a new Sharing instance.
Parameters
options-SharingOptions: options for Sharing constructor.contractLoader-ContractLoader:ContractLoaderinstancecryptoProvider-CryptoProvider:CryptoProviderinstancedescription-Description:Descriptioninstancedfs-DfsInterface:DfsInterfaceinstancekeyProvider-KeyProvider:KeyProviderinstancenameResolver-NameResolver:NameResolverinstancedefaultCryptoAlgo-string(optional): crypto algorith name fromCryptoProvider, defaults toaeslog-Function(optional): function to use for logging:(message, level) => {...}logLevel-LogLevel(optional): messages with this level will be logged withloglogLog-LogLogInterface(optional): container for collecting log messageslogLogLevel-LogLevel(optional): messages with this level will be pushed tologLog
Returns
Sharing instance
Example
const sharing = new Sharing({
contractLoader,
cryptoProvider,
description,
executor,
dfs,
keyProvider,
nameResolver,
defaultCryptoAlgo: 'aes',
});
addSharing
sharing.addSharing(address, originator, partner, section, block, sharingKey[, context, isHashKey, sharingId]);
Add a sharing to a contract or an ENS address.
This function is primarily used for sharing single keys with one other users, when sharing multiple keys and/or sharing with multiple users, have a look at extendSharing.
Parameters
address-string: contract address or ENS addressoriginator-string: Ethereum account id of the sharing userpartner-string: identity or account for which key shall be addedsection-string: data section the key is intended for or ‘*’block-number|string: starting with this block, the key is validsharingKey-string: key to sharecontext-string(optional): context to share key inisHashKey-bool(optional): indicates if given key already is a hash key, defaults tofalsesharingId-string(optional): id of a sharing (when multi-sharings is used)
Returns
Promise returns void: resolved when done
Example
// two sample users, user1 wants to share a key with user2
const user1 = '0x0000000000000000000000000000000000000001';
const user2 = '0x0000000000000000000000000000000000000002';
// create a sample contract
// usually you would have an existing contract, for which you want to manage the sharings
const contract = await executor.createContract('Shared', [], { from: user1, gas: 500000, });
// user1 shares the given key with user2
// this key is shared for all contexts ('*') and valid starting with block 0
await sharing.addSharing(contract.options.address, user1, user2, '*', 0, 'i am the secred that will be shared');
extendSharing
sharing.extendSharing(address, originator, partner, section, block, sharingKey[, context, isHashKey]);
Extend an existing sharing info with given key.
This is done on a sharings object and does not perform a transaction on its own. This function extends a sharing object retrieved from getSharingsFromContract and does not update sharings at the smart contract. For updating smart contracts sharing use saveSharingsToContract.
This function is primarily used to prepare updates for multiple keys and/or multiple users and submitting the result in one single transaction. For simpler sharing scenarios have a look at addSharing.
Parameters
sharings-any: object with sharings infooriginator-string: identity or account of the sharing userpartner-string: identity or account for which key shall be addedsection-string: data section the key is intended for or ‘*’block-number|string: starting with this block, the key is validsharingKey-string: key to sharecontext-string(optional): context to share key in
Returns
Promise returns any: updated sharings info
Example
// two sample users, user1 wants to share a key with user2
const user1 = '0x0000000000000000000000000000000000000001';
const user2 = '0x0000000000000000000000000000000000000002';
// get current sharings
const sharings = await sharing.getSharingsFromContract(contract);
// if receiver of sharing hasn't been added to the contract yet, share hash key as well
const hashKeyToShare = await sharing.getHashKey(contract.options.address, user1);
await sharing.extendSharings(sharings, user1, user2, '*', 'hashKey', hashKeyToShare, null);
// get current block number, keys will be available starting from this block
const blockNr = await web3.eth.getBlockNumber();
// get current key for field or in this case fallback '*'
const contentKey = sharing.getKey(contract.options.address, user1, '*', blockNr);
// share this key
await sharing.extendSharings(sharings, user1, user2, '*', blockNr, contentKey);
// finally store to contract
await sharing.saveSharingsToContract(contract.options.address, sharings, user1);
trimSharings
sharing.trimSharings(sharings, partner[, partner, section, block);
Removes properties from given sharing. If a block is given, the specific blocks key is removed, if no block is given, all keys for this section are removed. The same goes for section and partner. Note that only the last properties can be omitted and not properties in between can be set to null. So for example it is not possible to remove the same field for all identities or accounts by just setting partner to null.
Parameters
sharings-any: sharings to trimpartner-string: identity or account to remove keys forsection-string: data section the key is intended for or ‘*’block-number|string: block to remove keys for
Returns
Promise returns void: resolved when done
Example
// this sample will undo undo the changes from the last example (extendSharings)
// two sample users, user1 wants to share a key with user2
const user1 = '0x0000000000000000000000000000000000000001';
const user2 = '0x0000000000000000000000000000000000000002';
// get current sharings
const sharings = await sharing.getSharingsFromContract(contract);
// remove key from last time
await sharing.trim(sharings, user2, '*', blockNr);
// finally store to contract
await sharing.saveSharingsToContract(contract.options.address, sharings, user1);
bumpSharings
sharing.bumpSharings(address, originator, partners, section, block, sharingKey);
Bump keys for given identity or account by adding given key to it’s sharings. This is basically a shorthand
version for adding the new key for every identity or account in the partners array in a single transaction.
context, hashKeys and sharingId are currently not supported.
Parameters
address-string: contract address or ENS addressoriginator-string: identity or account of the sharing userpartner-string: identity or account for which key shall be addedsection-string: data section the key is intended for or ‘*’block-number|string: starting with this block, the key is validsharingKey-string: key to share
Returns
Promise returns void: resolved when done
Example
// two sample users, user1 wants to bump keys for user2 and user3
const user1 = '0x0000000000000000000000000000000000000001';
const user2 = '0x0000000000000000000000000000000000000002';
const user3 = '0x0000000000000000000000000000000000000003';
// assume we have a contract with sharings for those accounts
const contractId = '0x00000000000000000000000000000000c027rac7';
await sharing.bumpSharings(contractId, user1, [ user2, user3 ], '*', 0, 'i am a bump key');
getKey
sharing.getKey(address, partner, section[, block, sharingId]);
Get a content key from the sharing of a contract.
Parameters
address-string: contract address or ENS addresspartner-string: identity or account for which key shall be retrievedsection-string: data section the key is intended for or ‘*’block-number|string(optional): starting with this block, the key is valid, defaults toNumber.MAX_SAFE_INTEGERsharingId-string(optional): id of a sharing (when multi-sharings is used), defaults tonull
Returns
Promise returns string: matching key
Example
// a sample user
const user2 = '0x0000000000000000000000000000000000000002';
// user2 wants to read a key after receiving a sharing
// the key requested should be valid for all contexts ('*') and valid up to and including block 100
const key = await sharing.getKey(contract.options.address, user2, '*', 100);
getKeyHistory
sharing.getKeyHistory(address, partner, section[, sharingId]);
Get history of keys for an identity or account and a section.
Parameters
address-string: contract address or ENS addresspartner-string: Ethereum account id for which key shall be retrievedsection-string: data section the key is intended for or ‘*’sharingId-string(optional): id of a sharing (when multi-sharings is used), defaults tonull
Returns
Promise returns any: object with key: blockNr, value: key
Example
// a sample user
const user2 = '0x0000000000000000000000000000000000000002';
// user2 wants to retrieve all keys for '*'
const keyHistory = await sharing.getKeyHistory(contract.options.address, user2, '*');
ensureHashKey
sharing.ensureHashKey(address, originator, partner, hashKey[, context, sharingId]);
Give hash key “hashKey” to identity or account “partner”, if this identity or account does not have a hash key already.
Parameters
address-string: contract address or ENS addressoriginator-string: identity or account of the sharing userpartner-string: identity or account for which key shall be addedhashKey-string: key for DFS hashescontext-string(optional): context to share key insharingId-string(optional): id of a sharing (when multi-sharings is used)
Returns
Promise returns void: resolved when done
Example
const hashCryptor = cryptoProvider.getCryptorByCryptoAlgo('aesEcb');
const hashKey = await hashCryptor.generateKey();
await sharing.ensureHashKey(contract.options.address, accounts[0], accounts[1], hashKey);
getHashKey
sharing.getHashKey(address, partner[, sharingid]);
Function description
Parameters
address-string: contract address or ENS addresspartner-string: identity or account for which key shall be retrievedsharingId-string(optional): id of a sharing (when multi-sharings is used)
Returns
Promise returns string: matching key
Example
const hashCryptor = cryptoProvider.getCryptorByCryptoAlgo('aesEcb');
const hashKey = await hashCryptor.generateKey();
await sharing.ensureHashKey(contract.options.address, accounts[0], accounts[1], hashKey);
const retrieved = sharing.getHashKey(contract.options.address, accounts[1]);
console.log(hashKey === retrieved);
// Output:
// true
getSharings
sharing.getSharings(address[, _partner, _section, _block, sharingId]);
Get sharing from a contract, if _partner, _section, _block matches.
Sharings can also be retrieved using ENS address.
Parameters
address-string: contract address or ENS address_partner-string(optional): identity or account for which key shall be retrieved_section-string(optional): data section the key is intended for or ‘*’_block-number(optional): starting with this block, the key is validsharingId-string(optional): id of a sharing (when multi-sharings is used)
Returns
Promise returns any: sharings as an object. For more details, refer to the example at the top of the page.
Example
const randomSecret = `super secret; ${Math.random()}`;
await sharing.addSharing(testAddress, accounts[1], accounts[0], '*', 0, randomSecret);
await sharing.addSharing(testAddress, accounts[1], accounts[0], 'test', 100, randomSecret);
const sharings = await sharing.getSharings(contract.options.address, null, null, null, sharingId);
/* Output:
{
'0x2260228fd705cd9420a07827b8e64e808daba1b6675c3956783cc09fcc56a327': { // sha3(contract owner)
'0x04994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829': { hashKey: [Object] },
'0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658': { '0': [Object] }, // sha3('test')
'0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0': { '0': [Object] } // additional unshared field
},
'0xb45ce1cd2e464ce53a8102a5f855c112a2a384c36923fe5c6e249c2a9286369e': { // sha3(accounts[1])
'0x04994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829': { hashKey: [Object] }, // '*'
'0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658': { // sha3('test')
'100': [Object] // Valid from block 100
}
}
*/
removeSharing
sharing.removeSharing(address, originator, partner, section[, sharingId]);
Remove a sharing key from a contract with sharing info.
Parameters
address-string: contract address or ENS addressoriginator-string: identity or account of the sharing userpartner-string: identity or account for which key shall be removedsection-string: data section of the keysharingId-string(optional): id of a sharing (when multi-sharings is used), defaults tonull
Returns
Promise returns void: resolved when done
Example
await sharing.addSharing(contract.options.address, accounts[0], accounts[1], '*', 0, randomSecret);
let sharings = await sharing.getSharings(contract.options.address);
console.log(Object.keys(sharings[nameResolver.soliditySha3(accounts[1])]).length);
// Output:
// 1
await sharing.removeSharing(contract.options.address, accounts[0], accounts[1], '*');
let sharings = await sharing.getSharings(contract.options.address);
console.log(Object.keys(sharings[nameResolver.soliditySha3(accounts[1])]).length);
// Output:
// 0
getSharingsFromContract
sharing.getSharingsFromContract(contract[, sharingId]);
Get encrypted sharings from smart contract.
The encrypted sharings are usually used in combination with other functions for purposes of adding, removing, extending sharings etc. For Example: This can be used in combination with saveSharingsToContract to bulk editing sharing info.
Parameters
contact-any: contract with sharing infosharingId-string(optional): id of a sharing in mutlisharings, defaults tonull
Returns
Promise returns any: sharings as an object
Example
// get sharings (encrypted)
const sharings = await sharing.getSharingsFromContract(serviceContract, callIdHash);
// Output:
{ '0x6760305476495b089868ae42c2293d5e8c1c7bf9bfe51a9ad85b36d85f4113cb':
{ '0x04994f67dc55b09e814ab7ffc8df3686b4afb2bb53e60eae97ef043fe03fb829': { hashKey: [Object] } }
// make changes to sharing
await sharing.extendSharings(sharings, accountId, target, section, 0, contentKeyToShare, null);
await sharing.extendSharings(sharings, accountId, target, '*', 'hashKey', hashKeyToShare, null);
// commit changes
await sharing.saveSharingsToContract(serviceContract.options.address, sharings, accountId, callIdHash);
saveSharingsToContract
sharing.saveSharingsToContract(contract, sharings, originator[, sharingId]);
Save sharings object with encrypted keys to contract.
This can be used to pull sharings, edit them offline and commit changes in a bulk. See example section for usage.
Parameters
contract-string|any: contract address or instancesharings-any: sharings object with encrypted keysoriginator-string: identity or account of the sharing usersharingId-string(optional): id of a sharing (when multi-sharings is used)
Returns
Promise returns void: resolved when done
Example
// get sharings (encrypted)
const sharings = await sharing.getSharingsFromContract(serviceContract, callIdHash);
// make changes to sharing
await sharing.extendSharings(sharings, accountId, target, section, 0, contentKeyToShare, null);
await sharing.extendSharings(sharings, accountId, target, '*', 'hashKey', hashKeyToShare, null);
// commit changes
await sharing.saveSharingsToContract(serviceContract.options.address, sharings, accountId, callIdHash);
addHashToCache
sharing.addHashToCache(address, sharingHash[, sharingId]);
Add a hash to to cache, can be used to speed up sharing key retrieval, when sharings hash is already known.
Parameters
address-string: contract addresssharingHash-string: bytes32 hash of a sharingsharingId-string(optional): id of a multisharing, defaults tonull
Example
sharing.addHashToCache(contract.options.address, sharingHash, sharingId);
clearCache
sharing.clearCache();
Clear caches and fetch new hashes and sharing on next request.
When sharings are fetched and not all results could be read, the result would stay the same in following requests due to the internal caching mechanism, even if a proper key has been shared with the user later on. To prevent such old values from showing up, the cache can be cleared.
Example
sharing.clearCache();