import * as ed25519 from '@transmute/did-key-ed25519' ; import base64url from 'base64url' ; import { ReadableStream } from 'web-streams-polyfill/ponyfill' ; import { getRandomValues } from 'isomorphic-webcrypto' ; import path from 'path' ; import jsigs from 'jsonld-signatures' ; import { CapabilityDelegation } from 'ocapld' ; import { documentLoader } from '../client/__fixtures__/documentLoader' ; import { getFastify } from './server' ; import { fixtures , CapabilityInvoker , KeyAgreementKey , Sha256HmacKey2019 , } from '../client' ; const { sign , suites : { Ed25519Signature2018 } , } = jsigs ; const { EdvClient , EdvDocument } = require ( 'edv-client' ) ; let key1 ; let key2 ; let fastifyServer ; let hmac ; let invocationSigner ; let keyAgreementKey ; let vault ; let keyResolver ; let doc1 ; let doc2 ; let doc3 ; function getRandomUint8 ( { size = 50 } = { } ) { return new Uint8Array ( size ) . map ( ( ) => Math . floor ( Math . random ( ) * 255 ) ) ; } beforeAll ( async ( ) => { fastifyServer = await getFastify ( ) ; await fastifyServer . store . resetDatabase ( ) ; await fastifyServer . listen ( 9876 ) ; key1 = await ed25519 . Ed25519KeyPair . generate ( { secureRandom : ( ) => { return base64url . toBuffer ( fixtures . client [ 0 ] . seed ) ; } } ) ; key1 . id = key1 . controller + key1 . id ; invocationSigner = new CapabilityInvoker ( key1 ) ; key2 = await key1 . toX25519KeyPair ( true ) ; key2 . id = key2 . controller + key2 . id ; keyAgreementKey = new KeyAgreementKey ( key2 ) ; hmac = await Sha256HmacKey2019 . create ( fixtures . client [ 0 ] . seed ) ; keyResolver = async ( { id } ) => { const { document } = await documentLoader ( id ) ; const key = document . verificationMethod [ 1 ] ; key . id = key . controller + key . id ; return key ; } ; } ) ; afterAll ( async ( ) => { fastifyServer . close ( ) ; } ) ; it ( 'can create vault' , async ( ) => { vault = await EdvClient . createEdv ( { url : 'http://localhost:9876/edvs' , config : { sequence : 0 , controller : key1 . controller , keyAgreementKey : { id : key2 . id , type : key2 . type } , hmac : { id : hmac . id , type : hmac . type } , referenceId : 'data-model-guide' } } ) ; expect ( vault . id ) . toBeDefined ( ) ; } ) ; it ( 'can create documents with indexes' , async ( ) => { const client = new EdvClient ( { id : vault . id , keyAgreementKey , hmac } ) ; client . ensureIndex ( { attribute : 'content.indexedKey' } ) ; doc1 = { id : await EdvClient . generateId ( ) , content : { someKey : 'someValue' , indexedKey : 'value1' } } ; await client . insert ( { doc : doc1 , invocationSigner , keyResolver } ) ; doc2 = { id : await EdvClient . generateId ( ) , content : { someKey : 'someValue2' , indexedKey : 'value1' } } ; await client . insert ( { doc : doc2 , invocationSigner , keyResolver } ) ; doc3 = { id : await EdvClient . generateId ( ) , content : { someKey : 'someValue' , indexedKey : 'value2' } } ; await client . insert ( { doc : doc3 , invocationSigner , keyResolver } ) ; } ) ; it ( 'can get documents by indexes' , async ( ) => { const client = new EdvClient ( { id : vault . id , keyAgreementKey , hmac } ) ; client . ensureIndex ( { attribute : 'content.indexedKey' } ) ; let res = await client . find ( { equals : { 'content.indexedKey' : 'value1' } , invocationSigner } ) ; expect ( res . documents . length ) . toBe ( 2 ) ; res = await client . find ( { equals : { 'content.indexedKey' : 'value2' } , invocationSigner } ) ; expect ( res . documents . length ) . toBe ( 1 ) ; } ) ; it ( 'can update doc' , async ( ) => { const client = new EdvClient ( { id : vault . id , keyAgreementKey , hmac } ) ; client . ensureIndex ( { attribute : 'content.indexedKey' } ) ; const doc1Updated = { id : doc1 . id , content : { someKey : 'doc1Updated' } } ; await client . update ( { doc : doc1Updated , invocationSigner , keyResolver } ) ; const decrypted = await client . get ( { id : doc1Updated . id , invocationSigner } ) ; expect ( decrypted . content . someKey ) . toBe ( 'doc1Updated' ) ; } ) ; it ( 'can get documents by indexes but they are destroyed by the previous update' , async ( ) => { const client = new EdvClient ( { id : vault . id , keyAgreementKey , hmac } ) ; client . ensureIndex ( { attribute : 'content.indexedKey' } ) ; let res = await client . find ( { equals : { 'content.indexedKey' : 'value1' } , invocationSigner } ) ; expect ( res . documents . length ) . toBe ( 0 ) ; res = await client . find ( { equals : { 'content.indexedKey' : 'value2' } , invocationSigner } ) ; expect ( res . documents . length ) . toBe ( 1 ) ; } ) ; it ( 'should insert a document with a stream' , async ( ) => { const client = new EdvClient ( { id : vault . id , keyAgreementKey , hmac } ) ; client . ensureIndex ( { attribute : 'content.indexedKey' } ) ; const testId = await EdvClient . generateId ( ) ; const doc = { id : testId , content : { someKey : 'someValue' } } ; const data = getRandomUint8 ( ) ; const stream = new ReadableStream ( { pull ( controller ) { controller . enqueue ( data ) ; controller . close ( ) ; } } ) ; const inserted = await client . insert ( { keyResolver , invocationSigner , doc , stream } ) ; const edvDoc = new EdvDocument ( { invocationSigner , id : inserted . id , keyAgreementKey : client . keyAgreementKey , capability : { '@context' : 'https://w3id.org/security/v2' , id : `${ client . id }/zcaps/documents` , invocationTarget : `${ client . id }/documents/${ doc . id }` , invoker : invocationSigner . id , allowedAction : [ 'read' , 'write' ] } } ) ; const result = await edvDoc . read ( ) ; const expectedStream = await edvDoc . getStream ( { doc : result } ) ; const reader = expectedStream . getReader ( ) ; let streamData = new Uint8Array ( 0 ) ; let done = false ; while ( ! done ) { const { value , done : _done } = await reader . read ( ) ; if ( value ) { const next = new Uint8Array ( streamData . length + value . length ) ; next . set ( streamData ) ; next . set ( value , streamData . length ) ; streamData = next ; } done = _done ; } expect ( streamData ) . toEqual ( data ) ; } ) ; it ( 'can create capability' , async ( ) => { const bobInvocationKey = await ed25519 . Ed25519KeyPair . generate ( { secureRandom : ( ) => { return Buffer . from ( getRandomValues ( new Uint8Array ( 32 ) ) ) ; } } ) ; const bobKeyAgreementKey = await bobInvocationKey . toX25519KeyPair ( true ) ; bobInvocationKey . id = bobInvocationKey . controller + bobInvocationKey . id ; bobKeyAgreementKey . id = bobKeyAgreementKey . controller + bobKeyAgreementKey . id ; const testId = await EdvClient . generateId ( ) ; const doc = { id : testId , content : { someKey : 'someValue' } } ; const recipients = [ { header : { kid : keyAgreementKey . id , alg : 'ECDH-ES+A256KW' } } , { header : { kid : bobKeyAgreementKey . id , alg : 'ECDH-ES+A256KW' } } , ] ; const client = new EdvClient ( { id : vault . id , keyAgreementKey , hmac } ) ; client . ensureIndex ( { attribute : 'content.indexedKey' } ) ; const inserted = await client . insert ( { keyResolver , invocationSigner , doc , recipients } ) ; const aliceGrantsBobCapabilityToRead = { '@context' : 'https://w3id.org/security/v2' , id : `urn:uuid:${ await EdvClient . generateId ( ) }` , invocationTarget : `${ client . id }/documents/${ inserted . id }` , invoker : bobInvocationKey . id , allowedAction : 'read' , parentCapability : `${ client . id }/zcaps/documents/${ inserted . id }` } ; const signer = invocationSigner ; const suite = new Ed25519Signature2018 ( { signer , verificationMethod : signer . id } ) ; const purpose = new CapabilityDelegation ( { capabilityChain : [ aliceGrantsBobCapabilityToRead . parentCapability ] } ) ; await sign ( aliceGrantsBobCapabilityToRead , { documentLoader , suite , purpose } ) ; await client . enableCapability ( { capabilityToEnable : aliceGrantsBobCapabilityToRead , invocationSigner : signer } ) ; const delegatedDoc = new EdvDocument ( { client , invocationSigner : new CapabilityInvoker ( bobInvocationKey ) , keyResolver , keyAgreementKey , capability : aliceGrantsBobCapabilityToRead } ) ; const resultOfDelegatedRead = await delegatedDoc . read ( ) ; expect ( resultOfDelegatedRead . content ) . toEqual ( doc . content ) ; await client . revokeCapability ( { capabilityToRevoke : aliceGrantsBobCapabilityToRead , invocationSigner : invocationSigner } ) ; const delegatedDoc2 = new EdvDocument ( { client , invocationSigner : new CapabilityInvoker ( bobInvocationKey ) , keyResolver , keyAgreementKey , capability : aliceGrantsBobCapabilityToRead } ) ; await expect ( delegatedDoc2 . read ( ) ) . rejects . toThrowError ( 'capability invocation verification failed' ) ; } ) ; it ( 'can save database to filesystem' , async ( ) => { const rootDirectory = path . join ( __dirname , '../../../..' ) ; await fastifyServer . storeToFs ( fastifyServer . store , rootDirectory , 'https://identity.foundation/confidential-storage/' ) ; } ) ;