From 0c7ba987540e0684e803ca8d722aa4b0b5d04fde Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 13 May 2024 16:09:34 +0100 Subject: [PATCH] feat: export method `isDataAuthentic` as static, single method or member of class --- .../methods/external-data-source-utilities.md | 179 +++++++++++++++++- src/index.ts | 22 ++- src/lib/getDataFromExternalSources.test.ts | 130 +++++++++++++ src/lib/getDataFromExternalSources.ts | 2 + src/lib/utils.ts | 2 +- 5 files changed, 331 insertions(+), 4 deletions(-) diff --git a/docs/methods/external-data-source-utilities.md b/docs/methods/external-data-source-utilities.md index ef1c824b..503c1345 100644 --- a/docs/methods/external-data-source-utilities.md +++ b/docs/methods/external-data-source-utilities.md @@ -204,6 +204,181 @@ getVerificationMethod('0x6f357c6a'); */ ``` -## hashData - ## isDataAuthentic + +```js +const myErc725 = new ERC725(); +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +ERC725.isDataAuthentic(data, verificationOptions); +``` + +```js +import { isDataAuthentic } from '@erc725/erc725.js'; + +isDataAuthentic(data, verificationOptions); +``` + +Hashes the `data` passed as parameter using the specified hashing functions (available under `method` in the `verificationOption` object) and compares the result with the provided hash. + +:::info +This method will console an error if the hash provided as `data` and the expected hash obtained using the verification method do not match. +::: + +#### Parameters + +| Name | Type | Description | +| :-------------------- | :----------------------- | :---------------------------------- | +| `data` | `string` or `Uint8Array` | The data to be hashed and verified. | +| `verificationOptions` | `Verification` | An object as defined below | + +
+ Types details + +```js + KECCAK256_UTF8 = , + KECCAK256_BYTES = , + HASH_KECCAK256_UTF8 = , + HASH_KECCAK256_BYTES = , + +export interface Verification { + data: string; + method: 'keccak256(utf8)' | 'keccak256(bytes)' | '0x6f357c6a' | '0x8019f9b1' | string; + source?: string; +} +``` + +
+ +#### Returns + +| Name | Type | Description | +| :--- | :-------- | :-------------------------------------------------------------------------------------------- | +| | `boolean` | `true` if the data is authentic according to the verification method used, `false` otherwise. | + +### Example + +
+ JSON data to verify from data.json + +```json +[ + { + "name": "LSP3Profile", + "key": "0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5", + "value": { + "LSP3Profile": { + "name": "test", + "description": "", + "tags": ["profile"], + "links": [], + "profileImage": [ + { + "width": 1024, + "height": 709, + "verification": { + "method": "keccak256(bytes)", + "data": "0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c" + }, + "url": "ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C" + }, + { + "width": 640, + "height": 443, + "verification": { + "method": "keccak256(bytes)", + "data": "0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82" + }, + "url": "ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW" + }, + { + "width": 320, + "height": 221, + "verification": { + "method": "keccak256(bytes)", + "data": "0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5" + }, + "url": "ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH" + }, + { + "width": 180, + "height": 124, + "verification": { + "method": "keccak256(bytes)", + "data": "0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810" + }, + "url": "ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ" + } + ], + "backgroundImage": [ + { + "width": 1800, + "height": 1012, + "verification": { + "method": "keccak256(bytes)", + "data": "0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8" + }, + "url": "ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n" + }, + { + "width": 1024, + "height": 576, + "verification": { + "method": "keccak256(bytes)", + "data": "0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f" + }, + "url": "ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9" + }, + { + "width": 640, + "height": 360, + "verification": { + "method": "keccak256(bytes)", + "data": "0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967" + }, + "url": "ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV" + }, + { + "width": 320, + "height": 180, + "verification": { + "method": "keccak256(bytes)", + "data": "0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509" + }, + "url": "ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz" + }, + { + "width": 180, + "height": 101, + "verification": { + "method": "keccak256(bytes)", + "data": "0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475" + }, + "url": "ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm" + } + ] + } + } + } +] +``` + +
+ +```typescript title="isDataAuthentic example" +import jsonData from './data.json'; + +isDataAuthentic(jsonData, { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', +}); +// true + +isDataAuthentic(jsonData, { + data: '0xdeadbeef00000000000000000000000000000000000000000000000000000000', + method: 'keccak256(utf8)', +}); +// false +``` diff --git a/src/index.ts b/src/index.ts index 8b118d1f..eef1967f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import { generateSchemasFromDynamicKeys, duplicateMultiTypeERC725SchemaEntry, getVerificationMethod, + isDataAuthentic, } from './lib/utils'; import { getSchema } from './lib/schemaParser'; @@ -93,7 +94,12 @@ export { }; export { ERC725Config, KeyValuePair, ProviderTypes } from './types'; -export { encodeData, encodeArrayKey, getVerificationMethod } from './lib/utils'; +export { + encodeData, + encodeArrayKey, + getVerificationMethod, + isDataAuthentic, +} from './lib/utils'; export { decodeData } from './lib/decodeData'; export { encodeKeyName, isDynamicKeyName } from './lib/encodeKeyName'; export { decodeMappingKey } from './lib/decodeMappingKey'; @@ -797,6 +803,20 @@ export class ERC725 { | undefined { return getVerificationMethod(nameOrSig); } + + static isDataAuthentic( + data: string | Uint8Array, + verificationOptions: Verification, + ): boolean { + return isDataAuthentic(data, verificationOptions); + } + + isDataAuthentic( + data: string | Uint8Array, + verificationOptions: Verification, + ): boolean { + return isDataAuthentic(data, verificationOptions); + } } export default ERC725; diff --git a/src/lib/getDataFromExternalSources.test.ts b/src/lib/getDataFromExternalSources.test.ts index c8cd7659..2c3992e9 100644 --- a/src/lib/getDataFromExternalSources.test.ts +++ b/src/lib/getDataFromExternalSources.test.ts @@ -52,4 +52,134 @@ describe('getDataFromExternalSources', () => { ); }).to.not.throw(); }); + + it("should return the right data when the schema's valueContent is VerifiableURI", async () => { + const schema: ERC725JSONSchema = { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + keyType: 'Singleton', + valueType: 'bytes', + valueContent: 'VerifiableURI', + }; + + const dataFromChain: DecodeDataOutput[] = [ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + verification: { + data: '0xdb864ed42104cee179785036cb4ff1183ebc57e5532ae766ad8533fa48acfbb3', + method: 'keccak256(utf8)', + }, + url: 'ipfs://QmdMGUxuQsm1U9Qs8oJSn5PfY4B1apGG75YBRxQPybtRVm', + }, + }, + ]; + + const result = await getDataFromExternalSources( + [schema], + dataFromChain, + 'https://api.universalprofile.cloud/ipfs/', + ); + + expect(result).to.deep.equal([ + { + name: 'LSP3Profile', + key: '0x5ef83ad9559033e6e941db7d7c495acdce616347d28e90c7ce47cbfcfcad3bc5', + value: { + LSP3Profile: { + name: 'test', + description: '', + tags: ['profile'], + links: [], + profileImage: [ + { + width: 1024, + height: 709, + verification: { + method: 'keccak256(bytes)', + data: '0x6a0a28680d65b69f5696859be7e0fcebbbcf0df47f1f767926de35402c7d525c', + }, + url: 'ipfs://QmVUYyft3j2JVrG4RzDe1Qx7K5gNtJGFhrExHQFeiRXz1C', + }, + { + width: 640, + height: 443, + verification: { + method: 'keccak256(bytes)', + data: '0x7cd399f2a2552aa5cd21b1584a98db3efa39c701c311c38a60c680343cfa6d82', + }, + url: 'ipfs://QmeU8FUZC9F1qMYmcWyBhfGqaf7g3kLzGb4xBpoCfyVLZW', + }, + { + width: 320, + height: 221, + verification: { + method: 'keccak256(bytes)', + data: '0x272d2e57ae1710ac7c5e3d1c9f9d24f48954ad43d0e821f8bd041a4734e309a5', + }, + url: 'ipfs://QmdViKPWYhZv7u86z7HBTgAkTAwEkNSRi1VkYEU8K5yUsH', + }, + { + width: 180, + height: 124, + verification: { + method: 'keccak256(bytes)', + data: '0x1a464ff7e0eff05da98ed309a25195d8666b6211a5dfa2214865c3fd50ead810', + }, + url: 'ipfs://QmXZUCW6MqCNfYJEFsi54Vkj6PRrUoiPjzTuA2mWtas3RJ', + }, + ], + backgroundImage: [ + { + width: 1800, + height: 1012, + verification: { + method: 'keccak256(bytes)', + data: '0x3f6be73b35d348fb8f0b87a47d8c8b6b9db8858ee044cb13734cdfe5d28031d8', + }, + url: 'ipfs://QmfLCPmL31f31RRB4R7yoTg3Hsk5PjrWyS3ZaaYyhRPT4n', + }, + { + width: 1024, + height: 576, + verification: { + method: 'keccak256(bytes)', + data: '0xcb57ed802bcd7dc4964395a609b3a0f557c5f46a602b28b058b9587bb77bb54f', + }, + url: 'ipfs://QmPoPEaoGNVYhiMTwBWp6XzLPRXyuLjZWnuMobdCbfqsU9', + }, + { + width: 640, + height: 360, + verification: { + method: 'keccak256(bytes)', + data: '0x57e8039288c3e1a7f891c839e03805984ab36524b710656f072492c1c8ebd967', + }, + url: 'ipfs://QmU3pDA4eDNPMeARsJXxKaZsMC5MgFLgzGQccnydbU9WLV', + }, + { + width: 320, + height: 180, + verification: { + method: 'keccak256(bytes)', + data: '0x2bebf9baac33d719bbd3b481b1af468701409ad7578f84be04e8f7563d5a1509', + }, + url: 'ipfs://QmcKtenPsRvrqZJQ1gLCdUFkex4i9DGp7RFvucb9nbkzsz', + }, + { + width: 180, + height: 101, + verification: { + method: 'keccak256(bytes)', + data: '0xe32154c03c892d7c41c91220b8757ec5b7847eb2dd91413f7238b0c25f55b475', + }, + url: 'ipfs://QmU7ueJ467E9HRahaqQmSPhvkTkMhCLXRxV45P4kmMk6vm', + }, + ], + }, + }, + }, + ]); + }); }); diff --git a/src/lib/getDataFromExternalSources.ts b/src/lib/getDataFromExternalSources.ts index 7c898417..f7bde427 100644 --- a/src/lib/getDataFromExternalSources.ts +++ b/src/lib/getDataFromExternalSources.ts @@ -129,12 +129,14 @@ export const getDataFromExternalSources = ( if (/^(\[.*\]|\{.*\})\s*$/s.test(key)) { const json = arrToBufArr(receivedData).toString(); const value = JSON.parse(json); + if (isDataAuthentic(value, urlDataWithHash.verification)) { return { ...dataEntry, value }; } if (isDataAuthentic(receivedData, urlDataWithHash.verification)) { return { ...dataEntry, value }; } + throw new Error('result did not correctly validate'); } } catch { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4c4b6386..bb1ad658 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -158,7 +158,7 @@ export function encodeKeyValue( /** * - * @param key A data key either as a 32 bytes long value, or a data key name of keyType = 'Array' + * @param key The schema key of a schema with keyType = 'Array' * @param index An integer representing the intended array index * @return The raw bytes key for the array element */