diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 755b92e6d..306f9914b 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -16,7 +16,6 @@ export * as Claim from './claim/index.js' export * as CType from './ctype/index.js' export { DelegationNode, DelegationNodeUtils } from './delegation/index.js' export * as PublicCredential from './publicCredential/index.js' -export * as Quote from './quote/index.js' export { connect, disconnect, init } from './kilt/index.js' export { SDKErrors } from '@kiltprotocol/utils' diff --git a/packages/core/src/quote/Quote.spec.ts b/packages/core/src/quote/Quote.spec.ts deleted file mode 100644 index 8c5701a71..000000000 --- a/packages/core/src/quote/Quote.spec.ts +++ /dev/null @@ -1,266 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import * as Did from '@kiltprotocol/did' -import type { - DidDocument, - DidResourceUri, - ICType, - IClaim, - ICostBreakdown, - ICredential, - IQuote, - IQuoteAgreement, - IQuoteAttesterSigned, - ResolvedDidKey, -} from '@kiltprotocol/types' -import { Crypto, SDKErrors } from '@kiltprotocol/utils' - -import { - createLocalDemoFullDidFromKeypair, - makeSigningKeyTool, -} from '../../../../tests/testUtils' -import * as Credential from '../credential' -import * as CType from '../ctype' -import * as Quote from './Quote' -import { QuoteSchema } from './QuoteSchema' - -describe('Quote', () => { - let claimerIdentity: DidDocument - const claimer = makeSigningKeyTool('ed25519') - - let attesterIdentity: DidDocument - const attester = makeSigningKeyTool('ed25519') - - let invalidCost: ICostBreakdown - let date: string - let testCType: ICType - let claim: IClaim - let credential: ICredential - let invalidCostQuoteData: IQuote - let invalidPropertiesQuoteData: IQuote - let validQuoteData: IQuote - let validAttesterSignedQuote: IQuoteAttesterSigned - let quoteBothAgreed: IQuoteAgreement - let invalidPropertiesQuote: IQuote - let invalidCostQuote: IQuote - - async function mockResolveKey( - keyUri: DidResourceUri - ): Promise { - const { did } = Did.parse(keyUri) - const document = [claimerIdentity, attesterIdentity].find( - ({ uri }) => uri === did - ) - if (!document) throw new Error('Cannot resolve mocked DID') - return Did.keyToResolvedKey(document.authentication[0], did) - } - - beforeAll(async () => { - claimerIdentity = await createLocalDemoFullDidFromKeypair(claimer.keypair) - - attesterIdentity = await createLocalDemoFullDidFromKeypair(attester.keypair) - - invalidCost = { - gross: 233, - tax: { vat: 3.3 }, - } as unknown as ICostBreakdown - date = new Date(2019, 11, 10).toISOString() - - testCType = CType.fromProperties('Quote Information', { - name: { type: 'string' }, - }) - - claim = { - cTypeHash: CType.idToHash(testCType.$id), - contents: {}, - owner: claimerIdentity.uri, - } - - // build credential with legitimations - credential = Credential.fromClaim(claim) - - // @ts-ignore - invalidCostQuoteData = { - cTypeHash: '0x12345678', - cost: invalidCost, - currency: 'Euro', - timeframe: date, - termsAndConditions: 'Lots of these', - } as IQuote - - invalidPropertiesQuoteData = { - cTypeHash: '0x12345678', - cost: { - gross: 233, - net: 23.3, - tax: { vat: 3.3 }, - }, - timeframe: date, - currency: 'Euro', - termsAndConditions: 'Lots of these', - } as unknown as IQuote - - validQuoteData = { - attesterDid: attesterIdentity.uri, - cTypeHash: '0x12345678', - cost: { - gross: 233, - net: 23.3, - tax: { vat: 3.3 }, - }, - currency: 'Euro', - timeframe: new Date('12-04-2020').toISOString(), - termsAndConditions: 'Lots of these', - } - validAttesterSignedQuote = await Quote.createAttesterSignedQuote( - validQuoteData, - attester.getSignCallback(attesterIdentity) - ) - quoteBothAgreed = await Quote.createQuoteAgreement( - validAttesterSignedQuote, - credential.rootHash, - claimer.getSignCallback(claimerIdentity), - claimerIdentity.uri, - { - didResolveKey: mockResolveKey, - } - ) - invalidPropertiesQuote = invalidPropertiesQuoteData - invalidCostQuote = invalidCostQuoteData - }) - - it('tests created quote data against given data', async () => { - expect(validQuoteData.attesterDid).toEqual(attesterIdentity.uri) - const sign = claimer.getSignCallback(claimerIdentity) - const signature = Did.signatureToJson( - await sign({ - data: Crypto.hash( - Crypto.encodeObjectAsStr({ - ...validAttesterSignedQuote, - claimerDid: claimerIdentity.uri, - rootHash: credential.rootHash, - }) - ), - did: claimerIdentity.uri, - keyRelationship: 'authentication', - }) - ) - expect(signature).toEqual(quoteBothAgreed.claimerSignature) - - const { fragment: attesterKeyId } = Did.parse( - validAttesterSignedQuote.attesterSignature.keyUri - ) - - expect(() => - Crypto.verify( - Crypto.hashStr( - Crypto.encodeObjectAsStr({ - attesterDid: validQuoteData.attesterDid, - cTypeHash: validQuoteData.cTypeHash, - cost: validQuoteData.cost, - currency: validQuoteData.currency, - timeframe: validQuoteData.timeframe, - termsAndConditions: validQuoteData.termsAndConditions, - }) - ), - validAttesterSignedQuote.attesterSignature.signature, - Did.getKey(attesterIdentity, attesterKeyId!)?.publicKey || - new Uint8Array() - ) - ).not.toThrow() - await expect( - Quote.verifyAttesterSignedQuote(validAttesterSignedQuote, { - didResolveKey: mockResolveKey, - }) - ).resolves.not.toThrow() - await expect( - Quote.verifyQuoteAgreement(quoteBothAgreed, { - didResolveKey: mockResolveKey, - }) - ).resolves.not.toThrow() - expect( - await Quote.createAttesterSignedQuote( - validQuoteData, - attester.getSignCallback(attesterIdentity) - ) - ).toEqual(validAttesterSignedQuote) - }) - it('validates created quotes against QuoteSchema', () => { - expect(Quote.validateQuoteSchema(QuoteSchema, validQuoteData)).toBe(true) - expect(Quote.validateQuoteSchema(QuoteSchema, invalidCostQuote)).toBe(false) - expect(Quote.validateQuoteSchema(QuoteSchema, invalidPropertiesQuote)).toBe( - false - ) - }) - - it('detects tampering', async () => { - const messedWithCurrency: IQuoteAttesterSigned = { - ...validAttesterSignedQuote, - currency: 'Bananas', - } - await expect( - Quote.verifyAttesterSignedQuote(messedWithCurrency, { - didResolveKey: mockResolveKey, - }) - ).rejects.toThrow(SDKErrors.SignatureUnverifiableError) - const messedWithRootHash: IQuoteAgreement = { - ...quoteBothAgreed, - rootHash: '0x1234', - } - await expect( - Quote.verifyQuoteAgreement(messedWithRootHash, { - didResolveKey: mockResolveKey, - }) - ).rejects.toThrow(SDKErrors.SignatureUnverifiableError) - }) - - it('complains if attesterDid does not match attester signature', async () => { - const sign = claimer.getSignCallback(claimerIdentity) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { attesterSignature, ...attesterSignedQuote } = - validAttesterSignedQuote - const wrongSignerAttester: IQuoteAttesterSigned = { - ...attesterSignedQuote, - attesterSignature: Did.signatureToJson( - await sign({ - data: Crypto.hash(Crypto.encodeObjectAsStr(attesterSignedQuote)), - did: claimerIdentity.uri, - keyRelationship: 'authentication', - }) - ), - } - - await expect( - Quote.verifyAttesterSignedQuote(wrongSignerAttester, { - didResolveKey: mockResolveKey, - }) - ).rejects.toThrow(SDKErrors.DidSubjectMismatchError) - }) - - it('complains if claimerDid does not match claimer signature', async () => { - const sign = attester.getSignCallback(attesterIdentity) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { claimerSignature, ...restQuote } = quoteBothAgreed - const wrongSignerClaimer: IQuoteAgreement = { - ...restQuote, - claimerSignature: Did.signatureToJson( - await sign({ - data: Crypto.hash(Crypto.encodeObjectAsStr(restQuote)), - did: attesterIdentity.uri, - keyRelationship: 'authentication', - }) - ), - } - - await expect( - Quote.verifyQuoteAgreement(wrongSignerClaimer, { - didResolveKey: mockResolveKey, - }) - ).rejects.toThrow(SDKErrors.DidSubjectMismatchError) - }) -}) diff --git a/packages/core/src/quote/Quote.ts b/packages/core/src/quote/Quote.ts deleted file mode 100644 index 3fdbd7752..000000000 --- a/packages/core/src/quote/Quote.ts +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -/** - * [[Quote]] constructs a framework for Attesters to make an offer for building a [[Claim]] on a [[CType]] in which it includes a price and other terms & conditions upon which a claimer can agree. - * - * A [[Quote]] object represents a legal **offer** for the closure of a contract attesting a [[Claim]] from the [[CType]] specified within the offer. - * - * A [[Quote]] comes with a versionable spec, allowing different [[Quote]] specs to exist over time and tracks under which [[Quote]] a contract was closed. - * - * @packageDocumentation - */ - -import type { - IQuote, - IQuoteAgreement, - IQuoteAttesterSigned, - ICredential, - SignCallback, - DidResolveKey, - DidUri, -} from '@kiltprotocol/types' -import { Crypto, JsonSchema, SDKErrors } from '@kiltprotocol/utils' -import { - resolveKey, - verifyDidSignature, - signatureToJson, - signatureFromJson, -} from '@kiltprotocol/did' -import { QuoteSchema } from './QuoteSchema.js' - -/** - * Validates the quote against the meta schema and quote data against the provided schema. - * - * @param schema A [[Quote]] schema object. - * @param validate [[Quote]] data to be validated against the provided schema. - * @param messages The errors messages are listed in an array. - * - * @returns Whether the quote schema is valid. - */ -export function validateQuoteSchema( - schema: JsonSchema.Schema, - validate: unknown, - messages?: string[] -): boolean { - const validator = new JsonSchema.Validator(schema) - if (schema.$id !== QuoteSchema.$id) { - validator.addSchema(QuoteSchema) - } - const result = validator.validate(validate) - if (!result.valid && messages) { - result.errors.forEach((error) => { - messages.push(error.error) - }) - } - return result.valid -} - -// TODO: should have a "create quote" function. - -/** - * Signs a [[Quote]] object as an Attester. - * - * @param quoteInput A [[Quote]] object. - * @param sign The callback to sign with the private key. - * @returns A signed [[Quote]] object. - */ -export async function createAttesterSignedQuote( - quoteInput: IQuote, - sign: SignCallback -): Promise { - if (!validateQuoteSchema(QuoteSchema, quoteInput)) { - throw new SDKErrors.QuoteUnverifiableError() - } - - const signature = await sign({ - data: Crypto.hash(Crypto.encodeObjectAsStr(quoteInput)), - did: quoteInput.attesterDid, - keyRelationship: 'authentication', - }) - return { - ...quoteInput, - attesterSignature: signatureToJson(signature), - } -} - -/** - * Verifies a [[IQuoteAttesterSigned]] object. - * - * @param quote The object which to be verified. - * @param options Optional settings. - * @param options.didResolveKey Resolve function used in the process of verifying the attester signature. - */ -export async function verifyAttesterSignedQuote( - quote: IQuoteAttesterSigned, - { - didResolveKey = resolveKey, - }: { - didResolveKey?: DidResolveKey - } = {} -): Promise { - const { attesterSignature, ...basicQuote } = quote - await verifyDidSignature({ - ...signatureFromJson(attesterSignature), - message: Crypto.hashStr(Crypto.encodeObjectAsStr(basicQuote)), - expectedSigner: basicQuote.attesterDid, - expectedVerificationMethod: 'authentication', - didResolveKey, - }) - - const messages: string[] = [] - if (!validateQuoteSchema(QuoteSchema, basicQuote, messages)) { - throw new SDKErrors.QuoteUnverifiableError() - } -} - -/** - * Creates a [[Quote]] signed by the Attester and the Claimer. - * - * @param attesterSignedQuote A [[Quote]] object signed by an Attester. - * @param credentialRootHash A root hash of the entire object. - * @param sign The callback to sign with the private key. - * @param claimerDid The DID of the Claimer, who has to sign. - * @param options Optional settings. - * @param options.didResolveKey Resolve function used in the process of verifying the attester signature. - * @returns A [[Quote]] agreement signed by both the Attester and Claimer. - */ -export async function createQuoteAgreement( - attesterSignedQuote: IQuoteAttesterSigned, - credentialRootHash: ICredential['rootHash'], - sign: SignCallback, - claimerDid: DidUri, - { - didResolveKey = resolveKey, - }: { - didResolveKey?: DidResolveKey - } = {} -): Promise { - const { attesterSignature, ...basicQuote } = attesterSignedQuote - - await verifyDidSignature({ - ...signatureFromJson(attesterSignature), - message: Crypto.hashStr(Crypto.encodeObjectAsStr(basicQuote)), - expectedVerificationMethod: 'authentication', - didResolveKey, - }) - - const quoteAgreement = { - ...attesterSignedQuote, - rootHash: credentialRootHash, - claimerDid, - } - const signature = await sign({ - data: Crypto.hash(Crypto.encodeObjectAsStr(quoteAgreement)), - did: claimerDid, - keyRelationship: 'authentication', - }) - - return { - ...quoteAgreement, - claimerSignature: signatureToJson(signature), - } -} - -/** - * Verifies a [[IQuoteAgreement]] object. - * - * @param quote The object to be verified. - * @param options Optional settings. - * @param options.didResolveKey Resolve function used in the process of verifying the attester signature. - */ -export async function verifyQuoteAgreement( - quote: IQuoteAgreement, - { - didResolveKey = resolveKey, - }: { - didResolveKey?: DidResolveKey - } = {} -): Promise { - const { claimerSignature, claimerDid, rootHash, ...attesterSignedQuote } = - quote - // verify attester signature - await verifyAttesterSignedQuote(attesterSignedQuote, { didResolveKey }) - // verify claimer signature - await verifyDidSignature({ - ...signatureFromJson(claimerSignature), - message: Crypto.hashStr( - Crypto.encodeObjectAsStr({ ...attesterSignedQuote, claimerDid, rootHash }) - ), - expectedSigner: claimerDid, - expectedVerificationMethod: 'authentication', - didResolveKey, - }) -} diff --git a/packages/core/src/quote/QuoteSchema.ts b/packages/core/src/quote/QuoteSchema.ts deleted file mode 100644 index d7d0d9394..000000000 --- a/packages/core/src/quote/QuoteSchema.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import { JsonSchema } from '@kiltprotocol/utils' - -export const QuoteSchema: JsonSchema.Schema = { - $schema: 'http://json-schema.org/draft-07/schema#', - $id: 'kilt:quote:v1', - type: 'object', - title: 'Quote', - properties: { - attesterDid: { - type: 'string', - }, - cTypeHash: { - type: 'string', - }, - cost: { - type: 'object', - required: ['net', 'gross', 'tax'], - properties: { - net: { - type: 'number', - }, - gross: { - type: 'number', - }, - tax: { - type: 'object', - }, - }, - }, - currency: { - type: 'string', - }, - termsAndConditions: { - type: 'string', - }, - timeframe: { - type: 'string', - format: 'date-time', - }, - }, - required: [ - 'attesterDid', - 'cTypeHash', - 'cost', - 'currency', - 'termsAndConditions', - 'timeframe', - ], -} diff --git a/packages/core/src/quote/index.ts b/packages/core/src/quote/index.ts deleted file mode 100644 index 5f5d3cb2a..000000000 --- a/packages/core/src/quote/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -export * from './Quote.js' -export { QuoteSchema } from './QuoteSchema.js' diff --git a/packages/messaging/README.md b/packages/messaging/README.md deleted file mode 100644 index b87450174..000000000 --- a/packages/messaging/README.md +++ /dev/null @@ -1,24 +0,0 @@ -[![](https://user-images.githubusercontent.com/39338561/122415864-8d6a7c00-cf88-11eb-846f-a98a936f88da.png) -](https://kilt.io) - -![Lint and Test](https://github.com/KILTprotocol/sdk-js/workflows/Lint%20and%20Test/badge.svg) - -# KILT Messaging - -This package provides tools to create messages, encrypt, decrypt and verify them. - -For more information, please visit our [official SDK documentation](https://docs.kilt.io/docs/sdk/introduction). - -## Installation - -NPM: - -``` -npm install @kiltprotocol/messaging -``` - -YARN: - -``` -yarn add @kiltprotocol/messaging -``` diff --git a/packages/messaging/package.json b/packages/messaging/package.json deleted file mode 100644 index b041b5c95..000000000 --- a/packages/messaging/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@kiltprotocol/messaging", - "version": "0.33.2-6", - "description": "", - "main": "./lib/cjs/index.js", - "module": "./lib/esm/index.js", - "types": "./lib/cjs/index.d.ts", - "exports": { - ".": { - "import": "./lib/esm/index.js", - "require": "./lib/cjs/index.js" - } - }, - "files": [ - "lib/**/*" - ], - "scripts": { - "clean": "rimraf ./lib", - "build": "yarn clean && yarn build:ts", - "build:ts": "yarn build:cjs && yarn build:esm", - "build:cjs": "tsc --declaration -p tsconfig.build.json && echo '{\"type\":\"commonjs\"}' > ./lib/cjs/package.json", - "build:esm": "tsc --declaration -p tsconfig.esm.json && echo '{\"type\":\"module\"}' > ./lib/esm/package.json" - }, - "repository": "github:kiltprotocol/sdk-js", - "engines": { - "node": ">=16.0" - }, - "author": "", - "license": "BSD-4-Clause", - "bugs": "https://github.com/KILTprotocol/sdk-js/issues", - "homepage": "https://github.com/KILTprotocol/sdk-js#readme", - "devDependencies": { - "rimraf": "^3.0.2", - "typescript": "^4.8.3" - }, - "dependencies": { - "@kiltprotocol/core": "workspace:*", - "@kiltprotocol/did": "workspace:*", - "@kiltprotocol/types": "workspace:*", - "@kiltprotocol/utils": "workspace:*", - "@polkadot/util": "^12.0.0" - } -} diff --git a/packages/messaging/src/Message.spec.ts b/packages/messaging/src/Message.spec.ts deleted file mode 100644 index ea26bbaa1..000000000 --- a/packages/messaging/src/Message.spec.ts +++ /dev/null @@ -1,1336 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import { u8aToHex } from '@polkadot/util' - -import { - Attestation, - CType, - Claim, - Credential, - Quote, -} from '@kiltprotocol/core' -import * as Did from '@kiltprotocol/did' -import type { - DidDocument, - DidResourceUri, - DidUri, - IAcceptCredential, - IAttestation, - ICType, - IClaim, - ICredential, - ICredentialPresentation, - IDelegationData, - IEncryptedMessage, - IInformCreateDelegation, - IInformDelegationCreation, - IMessage, - IQuote, - IQuoteAgreement, - IQuoteAttesterSigned, - IRejectAcceptDelegation, - IRejectAttestation, - IRejectCredential, - IRejectTerms, - IRequestAcceptDelegation, - IRequestAttestation, - IRequestAttestationContent, - IRequestCredential, - IRequestCredentialContent, - IRequestDelegationApproval, - IRequestTerms, - ISubmitAcceptDelegation, - ISubmitAttestation, - ISubmitAttestationContent, - ISubmitCredential, - ISubmitDelegationApproval, - ISubmitTerms, - ITerms, - MessageBody, - PartialClaim, - ResolvedDidKey, -} from '@kiltprotocol/types' -import { Crypto, SDKErrors } from '@kiltprotocol/utils' - -import { - KeyTool, - KeyToolSignCallback, - createLocalDemoFullDidFromKeypair, - createLocalDemoFullDidFromLightDid, - makeEncryptionKeyTool, - makeSigningKeyTool, -} from '../../../tests/testUtils' -import * as Message from './Message' - -describe('Messaging', () => { - let aliceLightDid: DidDocument - let aliceLightDidWithDetails: DidDocument - let aliceFullDid: DidDocument - let aliceSign: KeyToolSignCallback - const aliceEncKey = makeEncryptionKeyTool('Alice//enc') - - let bobLightDid: DidDocument - let bobLightDidWithDetails: DidDocument - let bobFullDid: DidDocument - let bobSign: KeyToolSignCallback - const bobEncKey = makeEncryptionKeyTool('Bob//enc') - - async function resolveKey( - keyUri: DidResourceUri, - keyRelationship = 'authentication' - ): Promise { - const { did } = Did.parse(keyUri) - const document = [ - aliceLightDidWithDetails, - aliceLightDid, - aliceFullDid, - bobLightDidWithDetails, - bobLightDid, - bobFullDid, - ].find(({ uri }) => uri === did) - if (!document) throw new Error('Cannot resolve mocked DID') - return Did.keyToResolvedKey(document[keyRelationship][0], did) - } - - beforeAll(async () => { - const aliceAuthKey = makeSigningKeyTool('ed25519') - aliceSign = aliceAuthKey.getSignCallback - aliceLightDid = Did.createLightDidDocument({ - authentication: aliceAuthKey.authentication, - keyAgreement: aliceEncKey.keyAgreement, - }) - aliceLightDidWithDetails = Did.createLightDidDocument({ - authentication: aliceAuthKey.authentication, - keyAgreement: aliceEncKey.keyAgreement, - service: [ - { id: '#id-1', type: ['type-1'], serviceEndpoint: ['x:url-1'] }, - ], - }) - aliceFullDid = await createLocalDemoFullDidFromLightDid(aliceLightDid) - - const bobAuthKey = makeSigningKeyTool('ed25519') - bobSign = bobAuthKey.getSignCallback - bobLightDid = Did.createLightDidDocument({ - authentication: bobAuthKey.authentication, - keyAgreement: bobEncKey.keyAgreement, - }) - bobLightDidWithDetails = Did.createLightDidDocument({ - authentication: bobAuthKey.authentication, - keyAgreement: bobEncKey.keyAgreement, - service: [ - { id: '#id-1', type: ['type-1'], serviceEndpoint: ['x:url-1'] }, - ], - }) - bobFullDid = await createLocalDemoFullDidFromLightDid(bobLightDid) - }) - it('verify message encryption and signing', async () => { - const message = Message.fromBody( - { - type: 'request-credential', - content: { - cTypes: [{ cTypeHash: `${Crypto.hashStr('0x12345678')}` }], - }, - }, - aliceLightDid.uri, - bobLightDid.uri - ) - const encryptedMessage = await Message.encrypt( - message, - aliceEncKey.encrypt(aliceLightDid), - `${bobLightDid.uri}#encryption`, - { resolveKey } - ) - - const decryptedMessage = await Message.decrypt( - encryptedMessage, - bobEncKey.decrypt, - { resolveKey } - ) - expect(JSON.stringify(message.body)).toEqual( - JSON.stringify(decryptedMessage.body) - ) - - expect(() => Message.verify(decryptedMessage)).not.toThrow() - - const encryptedMessageWrongContent = JSON.parse( - JSON.stringify(encryptedMessage) - ) as IEncryptedMessage - const messedUpContent = Crypto.coToUInt8( - encryptedMessageWrongContent.ciphertext - ) - messedUpContent.set(Crypto.hash('1234'), 10) - encryptedMessageWrongContent.ciphertext = u8aToHex(messedUpContent) - - await expect(() => - Message.decrypt(encryptedMessageWrongContent, bobEncKey.decrypt, { - resolveKey, - }) - ).rejects.toThrowError(SDKErrors.DecodingMessageError) - - const encryptedWrongBody = await aliceEncKey.encrypt(aliceLightDid)({ - data: Crypto.coToUInt8('{ wrong JSON'), - peerPublicKey: bobLightDid.keyAgreement![0].publicKey, - did: aliceLightDid.uri, - }) - const encryptedMessageWrongBody: IEncryptedMessage = { - ciphertext: u8aToHex(encryptedWrongBody.data), - nonce: u8aToHex(encryptedWrongBody.nonce), - senderKeyUri: `${aliceLightDid.uri}${aliceLightDid.keyAgreement![0].id}`, - receiverKeyUri: `${bobLightDid.uri}${bobLightDid.keyAgreement![0].id}`, - } - await expect(() => - Message.decrypt(encryptedMessageWrongBody, bobEncKey.decrypt, { - resolveKey, - }) - ).rejects.toThrowError(SyntaxError) - }) - - it('verifies the message with sender is the owner (as full DID)', async () => { - const credential = Credential.fromClaim({ - cTypeHash: `${Crypto.hashStr('0x12345678')}`, - owner: aliceFullDid.uri, - contents: {}, - }) - - const presentation = await Credential.createPresentation({ - credential, - signCallback: aliceSign(aliceFullDid), - }) - - const date = new Date(2019, 11, 10).toISOString() - - const quoteData: IQuote = { - attesterDid: bobFullDid.uri, - cTypeHash: `${Crypto.hashStr('0x12345678')}`, - cost: { - tax: { vat: 3.3 }, - net: 23.4, - gross: 23.5, - }, - currency: 'Euro', - termsAndConditions: 'https://coolcompany.io/terms.pdf', - timeframe: date, - } - const quoteAttesterSigned = await Quote.createAttesterSignedQuote( - quoteData, - bobSign(bobFullDid) - ) - const bothSigned = await Quote.createQuoteAgreement( - quoteAttesterSigned, - credential.rootHash, - aliceSign(aliceFullDid), - aliceFullDid.uri, - { didResolveKey: resolveKey } - ) - const requestAttestationBody: IRequestAttestation = { - content: { - credential, - quote: bothSigned, - }, - type: 'request-attestation', - } - - // Should not throw if the owner and sender DID is the same. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBody, - aliceFullDid.uri, - bobFullDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the sender is the light DID version of the owner. - // This is technically not to be allowed but message verification is not concerned with that. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBody, - aliceLightDid.uri, - bobFullDid.uri - ) - ) - ).not.toThrow() - - // Should throw if the sender and the owner are two different entities. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBody, - bobFullDid.uri, - aliceFullDid.uri - ) - ) - ).toThrowError(SDKErrors.IdentityMismatchError) - - const attestation = { - delegationId: null, - claimHash: requestAttestationBody.content.credential.rootHash, - cTypeHash: Crypto.hashStr('0x12345678'), - owner: bobFullDid.uri, - revoked: false, - } - - const submitAttestationBody: ISubmitAttestation = { - content: { - attestation, - }, - type: 'submit-attestation', - } - - // Should not throw if the owner and sender DID is the same. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - bobFullDid.uri, - aliceFullDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the sender is the light DID version of the owner. - // This is technically not to be allowed but message verification is not concerned with that. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - bobLightDid.uri, - aliceFullDid.uri - ) - ) - ).not.toThrow() - - // Should throw if the sender and the owner are two different entities. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - aliceFullDid.uri, - bobFullDid.uri - ) - ) - ).toThrowError(SDKErrors.IdentityMismatchError) - - const submitClaimsForCTypeBody: ISubmitCredential = { - content: [presentation], - type: 'submit-credential', - } - - // Should not throw if the owner and sender DID is the same. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - aliceFullDid.uri, - bobFullDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the sender is the light DID version of the owner. - // This is technically not to be allowed but message verification is not concerned with that. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - aliceLightDid.uri, - bobFullDid.uri - ) - ) - ).not.toThrow() - - // Should throw if the sender and the owner are two different entities. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - bobFullDid.uri, - aliceFullDid.uri - ) - ) - ).toThrowError(SDKErrors.IdentityMismatchError) - }) - - it('verifies the message with sender is the owner (as light DID)', async () => { - // Create request for attestation to the light DID with no encoded details - const credential = Credential.fromClaim({ - cTypeHash: `${Crypto.hashStr('0x12345678')}`, - owner: aliceLightDid.uri, - contents: {}, - }) - - const presentation = await Credential.createPresentation({ - credential, - signCallback: aliceSign(aliceLightDid), - }) - - const date = new Date(2019, 11, 10).toISOString() - const quoteData: IQuote = { - attesterDid: bobLightDid.uri, - cTypeHash: `${Crypto.hashStr('0x12345678')}`, - cost: { - tax: { vat: 3.3 }, - net: 23.4, - gross: 23.5, - }, - currency: 'Euro', - termsAndConditions: 'https://coolcompany.io/terms.pdf', - timeframe: date, - } - const quoteAttesterSigned = await Quote.createAttesterSignedQuote( - quoteData, - bobSign(bobLightDid) - ) - const bothSigned = await Quote.createQuoteAgreement( - quoteAttesterSigned, - credential.rootHash, - aliceSign(aliceLightDid), - aliceLightDid.uri, - { didResolveKey: resolveKey } - ) - const requestAttestationBody: IRequestAttestation = { - content: { - credential, - quote: bothSigned, - }, - type: 'request-attestation', - } - - // Create request for attestation to the light DID with encoded details - const contentWithEncodedDetails = await Credential.createPresentation({ - credential: Credential.fromClaim({ - cTypeHash: `${Crypto.hashStr('0x12345678')}`, - owner: aliceLightDidWithDetails.uri, - contents: {}, - }), - signCallback: aliceSign(aliceLightDidWithDetails), - }) - - const quoteDataEncodedDetails: IQuote = { - attesterDid: bobLightDidWithDetails.uri, - cTypeHash: `${Crypto.hashStr('0x12345678')}`, - cost: { - tax: { vat: 3.3 }, - net: 23.4, - gross: 23.5, - }, - currency: 'Euro', - termsAndConditions: 'https://coolcompany.io/terms.pdf', - timeframe: date, - } - const quoteAttesterSignedEncodedDetails = - await Quote.createAttesterSignedQuote( - quoteDataEncodedDetails, - bobSign(bobLightDidWithDetails) - ) - const bothSignedEncodedDetails = await Quote.createQuoteAgreement( - quoteAttesterSignedEncodedDetails, - credential.rootHash, - aliceSign(aliceLightDidWithDetails), - aliceLightDidWithDetails.uri, - { didResolveKey: resolveKey } - ) - const requestAttestationBodyWithEncodedDetails: IRequestAttestation = { - content: { - credential: contentWithEncodedDetails, - quote: bothSignedEncodedDetails, - }, - type: 'request-attestation', - } - - // Should not throw if the owner and sender DID is the same. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBody, - aliceLightDid.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the owner has no additional details and the sender does. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBodyWithEncodedDetails, - aliceLightDidWithDetails.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the owner has additional details and the sender does not. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBodyWithEncodedDetails, - aliceLightDid.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the sender is the full DID version of the owner. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBody, - aliceFullDid.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should throw if the sender and the owner are two different entities. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - requestAttestationBody, - bobLightDid.uri, - aliceLightDid.uri - ) - ) - ).toThrowError(SDKErrors.IdentityMismatchError) - - const attestation = { - delegationId: null, - claimHash: requestAttestationBody.content.credential.rootHash, - cTypeHash: Crypto.hashStr('0x12345678'), - owner: bobLightDid.uri, - revoked: false, - } - - const submitAttestationBody: ISubmitAttestation = { - content: { - attestation, - }, - type: 'submit-attestation', - } - - const attestationWithEncodedDetails = { - delegationId: null, - claimHash: requestAttestationBody.content.credential.rootHash, - cTypeHash: Crypto.hashStr('0x12345678'), - owner: bobLightDidWithDetails.uri, - revoked: false, - } - - const submitAttestationBodyWithEncodedDetails: ISubmitAttestation = { - content: { - attestation: attestationWithEncodedDetails, - }, - type: 'submit-attestation', - } - - // Should not throw if the owner and sender DID is the same. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - bobLightDid.uri, - aliceLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the owner has no additional details and the sender does. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - bobLightDidWithDetails.uri, - aliceLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the owner has additional details and the sender does not. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBodyWithEncodedDetails, - bobLightDid.uri, - aliceLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the sender is the full DID version of the owner. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - bobFullDid.uri, - aliceLightDid.uri - ) - ) - ).not.toThrow() - - // Should throw if the sender and the owner are two different entities. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitAttestationBody, - aliceLightDid.uri, - bobLightDid.uri - ) - ) - ).toThrowError(SDKErrors.IdentityMismatchError) - - const submitClaimsForCTypeBody: ISubmitCredential = { - content: [presentation], - type: 'submit-credential', - } - - const submitClaimsForCTypeBodyWithEncodedDetails: ISubmitCredential = { - content: [contentWithEncodedDetails], - type: 'submit-credential', - } - - // Should not throw if the owner and sender DID is the same. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - aliceLightDid.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the owner has no additional details and the sender does. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - aliceLightDidWithDetails.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the owner has additional details and the sender does not. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBodyWithEncodedDetails, - aliceLightDid.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should not throw if the sender is the full DID version of the owner. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - aliceFullDid.uri, - bobLightDid.uri - ) - ) - ).not.toThrow() - - // Should throw if the sender and the owner are two different entities. - expect(() => - Message.ensureOwnerIsSender( - Message.fromBody( - submitClaimsForCTypeBody, - bobLightDid.uri, - aliceLightDid.uri - ) - ) - ).toThrowError(SDKErrors.IdentityMismatchError) - }) -}) - -describe('Error checking / Verification', () => { - // TODO: Duplicated code, would be nice to have as a seperated test package with similar helpers - async function buildCredential( - claimerDid: DidUri, - attesterDid: DidUri, - contents: IClaim['contents'], - legitimations: ICredential[] - ): Promise<[ICredential, IAttestation]> { - // create claim - - const testCType = CType.fromProperties('Credential', { - name: { type: 'string' }, - }) - - const claim = Claim.fromCTypeAndClaimContents( - testCType, - contents, - claimerDid - ) - // build credential with legitimations - const credential = Credential.fromClaim(claim, { - legitimations, - }) - // build attestation - const testAttestation = Attestation.fromCredentialAndDid( - credential, - attesterDid - ) - return [credential, testAttestation] - } - - let identityAlice: DidDocument - let keyAlice: KeyTool - - let identityBob: DidDocument - let keyBob: KeyTool - - let date: string - let testCType: ICType - let testCTypeWithMultipleProperties: ICType - let claim: IClaim - let claimContents: IClaim['contents'] - let quoteData: IQuote - let quoteAttesterSigned: IQuoteAttesterSigned - let bothSigned: IQuoteAgreement - let legitimation: ICredential - let requestTermsBody: IRequestTerms - let requestTermsContent: PartialClaim - let submitTermsBody: ISubmitTerms - let submitTermsContent: ITerms - let rejectTermsBody: IRejectTerms - let rejectTermsContent: Pick< - ITerms, - 'claim' | 'legitimations' | 'delegationId' - > - let requestAttestationBody: IRequestAttestation - let requestAttestationContent: IRequestAttestationContent - let submitAttestationContent: ISubmitAttestationContent - let submitAttestationBody: ISubmitAttestation - let rejectAttestationForClaimBody: IRejectAttestation - let requestCredentialBody: IRequestCredential - let requestCredentialContent: IRequestCredentialContent - let submitCredentialBody: ISubmitCredential - let submitCredentialContent: ICredentialPresentation[] - let acceptCredentialBody: IAcceptCredential - let rejectCredentialBody: IRejectCredential - let requestAcceptDelegationBody: IRequestAcceptDelegation - let requestAcceptDelegationContent: IRequestDelegationApproval - let submitAcceptDelegationBody: ISubmitAcceptDelegation - let submitAcceptDelegationContent: ISubmitDelegationApproval - let rejectAcceptDelegationBody: IRejectAcceptDelegation - let rejectAcceptDelegationContent: IDelegationData - let informCreateDelegationBody: IInformCreateDelegation - let informCreateDelegationContent: IInformDelegationCreation - let messageRequestTerms: IMessage - let messageSubmitTerms: IMessage - let messageRejectTerms: IMessage - let messageRequestAttestationForClaim: IMessage - let messageSubmitAttestationForClaim: IMessage - let messageRequestCredential: IMessage - let messageRejectAttestationForClaim: IMessage - let messageSubmitCredential: IMessage - let messageAcceptCredential: IMessage - let messageRejectCredential: IMessage - let messageRequestAcceptDelegation: IMessage - let messageSubmitAcceptDelegation: IMessage - let messageRejectAcceptDelegation: IMessage - let messageInformCreateDelegation: IMessage - - beforeAll(async () => { - keyAlice = makeSigningKeyTool() - identityAlice = await createLocalDemoFullDidFromKeypair(keyAlice.keypair) - keyBob = makeSigningKeyTool() - identityBob = await createLocalDemoFullDidFromKeypair(keyBob.keypair) - - date = new Date(2019, 11, 10).toISOString() - claimContents = { - name: 'Bob', - } - - async function didResolveKey( - keyUri: DidResourceUri - ): Promise { - const { did } = Did.parse(keyUri) - const document = [identityAlice, identityBob].find( - ({ uri }) => uri === did - ) - if (!document) throw new Error('Cannot resolve mocked DID') - return Did.keyToResolvedKey(document.authentication[0], did) - } - - // CType - testCType = CType.fromProperties('ClaimCtype', { - name: { type: 'string' }, - }) - testCTypeWithMultipleProperties = CType.fromProperties( - 'Drivers license Claim', - { - name: { type: 'string' }, - id: { type: 'string' }, - age: { type: 'string' }, - } - ) - - // Claim - claim = Claim.fromCTypeAndClaimContents( - testCType, - claimContents, - identityAlice.uri - ) - // Legitimation - ;[legitimation] = await buildCredential( - identityAlice.uri, - identityBob.uri, - {}, - [] - ) - // Quote Data - quoteData = { - attesterDid: identityAlice.uri, - cTypeHash: claim.cTypeHash, - cost: { - tax: { vat: 3.3 }, - net: 23.4, - gross: 23.5, - }, - currency: 'Euro', - termsAndConditions: 'https://coolcompany.io/terms.pdf', - timeframe: date, - } - // Quote signed by attester - quoteAttesterSigned = await Quote.createAttesterSignedQuote( - quoteData, - keyAlice.getSignCallback(identityAlice) - ) - // Quote agreement - bothSigned = await Quote.createQuoteAgreement( - quoteAttesterSigned, - legitimation.rootHash, - keyBob.getSignCallback(identityBob), - identityBob.uri, - { didResolveKey } - ) - // Request Terms content - requestTermsContent = { - cTypeHash: claim.cTypeHash, - } - // Submit Terms content - submitTermsContent = { - claim: { - cTypeHash: claim.cTypeHash, - }, - legitimations: [legitimation], - delegationId: undefined, - quote: quoteAttesterSigned, - cTypes: undefined, - } - // Reject terms Content - rejectTermsContent = { - claim: { - cTypeHash: claim.cTypeHash, - }, - legitimations: [legitimation], - } - - // Request Attestation Content - requestAttestationContent = { - credential: legitimation, - quote: bothSigned, - } - - // Submit Attestation content - submitAttestationContent = { - attestation: { - delegationId: null, - claimHash: requestAttestationContent.credential.rootHash, - cTypeHash: claim.cTypeHash, - owner: identityBob.uri, - revoked: false, - }, - } - - // Request Credential content - requestCredentialContent = { - cTypes: [ - { - cTypeHash: claim.cTypeHash, - trustedAttesters: [identityAlice.uri], - requiredProperties: ['id', 'name'], - }, - ], - challenge: '1234', - } - // Submit Credential content - submitCredentialContent = [ - { - ...legitimation, - claimerSignature: { - signature: '0x1234', - keyUri: `${legitimation.claim.owner}#0x1234`, - }, - }, - ] - // Request Accept delegation content - requestAcceptDelegationContent = { - delegationData: { - account: identityAlice.uri, - id: Crypto.hashStr('0x12345678'), - parentId: Crypto.hashStr('0x12345678'), - permissions: [1], - isPCR: false, - }, - metaData: {}, - signatures: { - inviter: Did.signatureToJson( - await keyAlice.getSignCallback(identityAlice)({ - data: Crypto.coToUInt8('signature'), - did: identityAlice.uri, - keyRelationship: 'authentication', - }) - ), - }, - } - // Submit Accept delegation content - submitAcceptDelegationContent = { - delegationData: { - account: identityAlice.uri, - id: Crypto.hashStr('0x12345678'), - parentId: Crypto.hashStr('0x12345678'), - permissions: [1], - isPCR: false, - }, - signatures: { - inviter: Did.signatureToJson( - await keyAlice.getSignCallback(identityAlice)({ - data: Crypto.coToUInt8('signature'), - did: identityAlice.uri, - keyRelationship: 'authentication', - }) - ), - invitee: Did.signatureToJson( - await keyBob.getSignCallback(identityBob)({ - data: Crypto.coToUInt8('signature'), - did: identityBob.uri, - keyRelationship: 'authentication', - }) - ), - }, - } - // Reject Accept Delegation content - rejectAcceptDelegationContent = { - account: identityAlice.uri, - id: Crypto.hashStr('0x12345678'), - parentId: Crypto.hashStr('0x12345678'), - permissions: [1], - isPCR: false, - } - - informCreateDelegationContent = { - delegationId: Crypto.hashStr('0x12345678'), - isPCR: false, - } - - requestTermsBody = { - content: requestTermsContent, - type: 'request-terms', - } - - submitTermsBody = { - content: submitTermsContent, - type: 'submit-terms', - } - - rejectTermsBody = { - content: rejectTermsContent, - type: 'reject-terms', - } - - requestAttestationBody = { - content: requestAttestationContent, - type: 'request-attestation', - } - - submitAttestationBody = { - content: submitAttestationContent, - type: 'submit-attestation', - } - - rejectAttestationForClaimBody = { - content: requestAttestationContent.credential.rootHash, - type: 'reject-attestation', - } - requestCredentialBody = { - content: requestCredentialContent, - type: 'request-credential', - } - - submitCredentialBody = { - content: submitCredentialContent, - type: 'submit-credential', - } - - acceptCredentialBody = { - content: [claim.cTypeHash], - type: 'accept-credential', - } - - rejectCredentialBody = { - content: [claim.cTypeHash], - type: 'reject-credential', - } - - requestAcceptDelegationBody = { - content: requestAcceptDelegationContent, - type: 'request-accept-delegation', - } - - submitAcceptDelegationBody = { - content: submitAcceptDelegationContent, - type: 'submit-accept-delegation', - } - - rejectAcceptDelegationBody = { - content: rejectAcceptDelegationContent, - type: 'reject-accept-delegation', - } - - informCreateDelegationBody = { - content: informCreateDelegationContent, - type: 'inform-create-delegation', - } - }) - - it('Checking required properties for given CType', () => { - expect(() => - Message.verifyRequiredCTypeProperties(['id', 'name'], testCType) - ).toThrowError(SDKErrors.CTypeUnknownPropertiesError) - - expect(() => - Message.verifyRequiredCTypeProperties( - ['id', 'name'], - testCTypeWithMultipleProperties - ) - ).not.toThrowError(SDKErrors.CTypeUnknownPropertiesError) - - expect(() => - Message.verifyRequiredCTypeProperties( - ['id', 'name'], - testCTypeWithMultipleProperties - ) - ).not.toThrowError() - }) - - beforeAll(async () => { - messageRequestTerms = Message.fromBody( - requestTermsBody, - identityAlice.uri, - identityBob.uri - ) - messageSubmitTerms = Message.fromBody( - submitTermsBody, - identityAlice.uri, - identityBob.uri - ) - messageRejectTerms = Message.fromBody( - rejectTermsBody, - identityAlice.uri, - identityBob.uri - ) - messageRequestAttestationForClaim = Message.fromBody( - requestAttestationBody, - identityAlice.uri, - identityBob.uri - ) - messageSubmitAttestationForClaim = Message.fromBody( - submitAttestationBody, - identityAlice.uri, - identityBob.uri - ) - - messageRejectAttestationForClaim = Message.fromBody( - rejectAttestationForClaimBody, - identityAlice.uri, - identityBob.uri - ) - messageRequestCredential = Message.fromBody( - requestCredentialBody, - identityAlice.uri, - identityBob.uri - ) - messageSubmitCredential = Message.fromBody( - submitCredentialBody, - identityAlice.uri, - identityBob.uri - ) - messageAcceptCredential = Message.fromBody( - acceptCredentialBody, - identityAlice.uri, - identityBob.uri - ) - messageRejectCredential = Message.fromBody( - rejectCredentialBody, - identityAlice.uri, - identityBob.uri - ) - messageRequestAcceptDelegation = Message.fromBody( - requestAcceptDelegationBody, - identityAlice.uri, - identityBob.uri - ) - messageSubmitAcceptDelegation = Message.fromBody( - submitAcceptDelegationBody, - identityAlice.uri, - identityBob.uri - ) - messageRejectAcceptDelegation = Message.fromBody( - rejectAcceptDelegationBody, - identityAlice.uri, - identityBob.uri - ) - messageInformCreateDelegation = Message.fromBody( - informCreateDelegationBody, - identityAlice.uri, - identityBob.uri - ) - }) - it('message body verifier should not throw errors on correct bodies', () => { - expect(() => Message.verifyMessageBody(requestTermsBody)).not.toThrowError() - expect(() => Message.verifyMessageBody(submitTermsBody)).not.toThrowError() - expect(() => Message.verifyMessageBody(rejectTermsBody)).not.toThrowError() - expect(() => - Message.verifyMessageBody(requestAttestationBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(submitAttestationBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(rejectAttestationForClaimBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(requestCredentialBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(submitCredentialBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(acceptCredentialBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(rejectCredentialBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(requestAcceptDelegationBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(submitAcceptDelegationBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(rejectAcceptDelegationBody) - ).not.toThrowError() - expect(() => - Message.verifyMessageBody(informCreateDelegationBody) - ).not.toThrowError() - }) - it('message envelope verifier should not throw errors on correct envelopes', () => { - expect(() => - Message.verifyMessageEnvelope(messageRequestTerms) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageSubmitTerms) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRejectTerms) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRequestAttestationForClaim) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageSubmitAttestationForClaim) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRejectAttestationForClaim) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRequestCredential) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageSubmitCredential) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageAcceptCredential) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRejectCredential) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRequestAcceptDelegation) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageSubmitAcceptDelegation) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageRejectAcceptDelegation) - ).not.toThrowError() - expect(() => - Message.verifyMessageEnvelope(messageInformCreateDelegation) - ).not.toThrowError() - }) - it('message envelope verifier should throw errors on faulty envelopes', () => { - messageRequestTerms.receiver = - 'did:kilt:thisisnotareceiveraddress' as DidUri - expect(() => - Message.verifyMessageEnvelope(messageRequestTerms) - ).toThrowError(SDKErrors.InvalidDidFormatError) - // @ts-ignore - messageSubmitTerms.sender = 'this is not a sender did' - expect(() => - Message.verifyMessageEnvelope(messageSubmitTerms) - ).toThrowError(SDKErrors.InvalidDidFormatError) - // @ts-ignore - messageRejectTerms.sender = 'this is not a sender address' - expect(() => - Message.verifyMessageEnvelope(messageRejectTerms) - ).toThrowError(SDKErrors.InvalidDidFormatError) - // @ts-ignore - messageRequestAttestationForClaim.messageId = 12 - expect(() => - Message.verifyMessageEnvelope(messageRequestAttestationForClaim) - ).toThrowError(TypeError) - // @ts-ignore - messageSubmitAttestationForClaim.createdAt = '123456' - expect(() => - Message.verifyMessageEnvelope(messageSubmitAttestationForClaim) - ).toThrowError(TypeError) - // @ts-ignore - messageRejectAttestationForClaim.receivedAt = '123456' - expect(() => - Message.verifyMessageEnvelope(messageRejectAttestationForClaim) - ).toThrowError(TypeError) - // @ts-ignore - messageRequestCredential.inReplyTo = 123 - expect(() => - Message.verifyMessageEnvelope(messageRequestCredential) - ).toThrowError(TypeError) - }) - it('message body verifier should throw errors on faulty bodies', () => { - // @ts-ignore - requestTermsBody.content.cTypeHash = 'this is not a ctype hash' - expect(() => Message.verifyMessageBody(requestTermsBody)).toThrowError( - SDKErrors.HashMalformedError - ) - submitTermsBody.content.delegationId = 'this is not a delegation id' - expect(() => Message.verifyMessageBody(submitTermsBody)).toThrowError( - SDKErrors.HashMalformedError - ) - - rejectTermsBody.content.delegationId = 'this is not a delegation id' - expect(() => Message.verifyMessageBody(rejectTermsBody)).toThrowError( - SDKErrors.HashMalformedError - ) - // @ts-expect-error - delete rejectTermsBody.content.claim.cTypeHash - expect(() => Message.verifyMessageBody(rejectTermsBody)).toThrowError( - SDKErrors.CTypeHashMissingError - ) - submitCredentialBody.content[0].claimerSignature = { - signature: 'this is not the claimers signature', - // @ts-ignore - keyUri: 'this is not a key id', - } - expect(() => Message.verifyMessageBody(submitCredentialBody)).toThrowError() - // @ts-ignore - submitAttestationBody.content.attestation.claimHash = - 'this is not the claim hash' - expect(() => Message.verifyMessageBody(submitAttestationBody)).toThrowError( - SDKErrors.HashMalformedError - ) - // @ts-ignore - rejectAttestationForClaimBody.content = 'this is not the root hash' - expect(() => - Message.verifyMessageBody(rejectAttestationForClaimBody) - ).toThrowError(SDKErrors.HashMalformedError) - // @ts-ignore - requestCredentialBody.content.cTypes[0].cTypeHash = - 'this is not a cTypeHash' - expect(() => Message.verifyMessageBody(requestCredentialBody)).toThrowError( - SDKErrors.HashMalformedError - ) - // @ts-ignore - acceptCredentialBody.content[0] = 'this is not a cTypeHash' - expect(() => Message.verifyMessageBody(acceptCredentialBody)).toThrowError( - SDKErrors.HashMalformedError - ) - // @ts-ignore - rejectCredentialBody.content[0] = 'this is not a cTypeHash' - expect(() => Message.verifyMessageBody(rejectCredentialBody)).toThrowError( - SDKErrors.HashMalformedError - ) - delete requestAcceptDelegationBody.content.metaData - expect(() => - Message.verifyMessageBody(requestAcceptDelegationBody) - ).toThrowError(SDKErrors.ObjectUnverifiableError) - requestAcceptDelegationBody.content.signatures.inviter.signature = - 'this is not a signature' - expect(() => - Message.verifyMessageBody(requestAcceptDelegationBody) - ).toThrowError(SDKErrors.SignatureMalformedError) - // @ts-ignore - submitAcceptDelegationBody.content.signatures.invitee.keyUri = - 'this is not a key id' - expect(() => - Message.verifyMessageBody(submitAcceptDelegationBody) - ).toThrowError(SDKErrors.SignatureMalformedError) - submitAcceptDelegationBody.content.delegationData.parentId = - 'this is not a parent id hash' - expect(() => - Message.verifyMessageBody(submitAcceptDelegationBody) - ).toThrowError(SDKErrors.DelegationIdTypeError) - // @ts-expect-error - delete rejectAcceptDelegationBody.content.account - expect(() => - Message.verifyMessageBody(rejectAcceptDelegationBody) - ).toThrowError(SDKErrors.OwnerMissingError) - informCreateDelegationBody.content.delegationId = - 'this is not a delegation id' - expect(() => - Message.verifyMessageBody(informCreateDelegationBody) - ).toThrowError(SDKErrors.HashMalformedError) - expect(() => Message.verifyMessageBody({} as MessageBody)).toThrowError( - SDKErrors.UnknownMessageBodyTypeError - ) - }) - it('delegation data structure verifier should throw on faulty delegation data', () => { - // @ts-expect-error - delete requestAcceptDelegationBody.content.delegationData.isPCR - expect(() => - Message.verifyDelegationStructure( - requestAcceptDelegationBody.content.delegationData - ) - ).toThrowError(TypeError('isPCR is expected to be a boolean')) - requestAcceptDelegationBody.content.delegationData.id = - 'this is not a delegation id' - expect(() => - Message.verifyDelegationStructure( - requestAcceptDelegationBody.content.delegationData - ) - ).toThrowError(SDKErrors.DelegationIdTypeError) - submitAcceptDelegationBody.content.delegationData.permissions = [] - expect(() => - Message.verifyDelegationStructure( - submitAcceptDelegationBody.content.delegationData - ) - ).toThrowError(SDKErrors.UnauthorizedError) - // @ts-expect-error - delete submitAcceptDelegationBody.content.delegationData.id - expect(() => - Message.verifyDelegationStructure( - submitAcceptDelegationBody.content.delegationData - ) - ).toThrowError(SDKErrors.DelegationIdMissingError) - }) -}) diff --git a/packages/messaging/src/Message.ts b/packages/messaging/src/Message.ts deleted file mode 100644 index ccf3eb3df..000000000 --- a/packages/messaging/src/Message.ts +++ /dev/null @@ -1,452 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import type { - DecryptCallback, - DidResolveKey, - DidResourceUri, - EncryptCallback, - IEncryptedMessage, - IEncryptedMessageContents, - ICType, - IDelegationData, - IMessage, - MessageBody, -} from '@kiltprotocol/types' -import { - Attestation, - Claim, - Credential, - CType, - Quote, -} from '@kiltprotocol/core' -import { DataUtils, SDKErrors, UUID } from '@kiltprotocol/utils' -import * as Did from '@kiltprotocol/did' -import { - hexToU8a, - stringToU8a, - u8aToHex, - u8aToString, - isHex, - isJsonObject, -} from '@polkadot/util' - -/** - * Checks if delegation data is well formed. - * - * @param delegationData Delegation data to check. - */ -export function verifyDelegationStructure( - delegationData: IDelegationData -): void { - const { permissions, id, parentId, isPCR, account } = delegationData - - if (!id) { - throw new SDKErrors.DelegationIdMissingError() - } else if (typeof id !== 'string' || !isHex(id)) { - throw new SDKErrors.DelegationIdTypeError() - } - - if (!account) { - throw new SDKErrors.OwnerMissingError() - } - Did.validateUri(account, 'Did') - - if (typeof isPCR !== 'boolean') { - throw new TypeError('isPCR is expected to be a boolean') - } - - if (permissions.length === 0 || permissions.length > 3) { - throw new SDKErrors.UnauthorizedError( - 'Must have at least one permission and no more then two' - ) - } - - if (parentId && (typeof parentId !== 'string' || !isHex(parentId))) { - throw new SDKErrors.DelegationIdTypeError() - } -} - -/** - * Checks if the message body is well-formed. - * - * @param body The message body. - */ -export function verifyMessageBody(body: MessageBody): void { - switch (body.type) { - case 'request-terms': { - Claim.verifyDataStructure(body.content) - break - } - case 'submit-terms': { - Claim.verifyDataStructure(body.content.claim) - body.content.legitimations.forEach((credential) => - Credential.verifyDataStructure(credential) - ) - if (body.content.delegationId) { - DataUtils.verifyIsHex(body.content.delegationId) - } - if (body.content.quote) { - Quote.validateQuoteSchema(Quote.QuoteSchema, body.content.quote) - } - if (body.content.cTypes) { - body.content.cTypes.forEach((val) => CType.verifyDataStructure(val)) - } - break - } - case 'reject-terms': { - Claim.verifyDataStructure(body.content.claim) - if (body.content.delegationId) { - DataUtils.verifyIsHex(body.content.delegationId) - } - body.content.legitimations.forEach((val) => - Credential.verifyDataStructure(val) - ) - break - } - case 'request-attestation': { - Credential.verifyDataStructure(body.content.credential) - if (body.content.quote) { - Quote.validateQuoteSchema(Quote.QuoteSchema, body.content.quote) - } - break - } - case 'submit-attestation': { - Attestation.verifyDataStructure(body.content.attestation) - break - } - case 'reject-attestation': { - if (!isHex(body.content)) { - throw new SDKErrors.HashMalformedError() - } - break - } - case 'request-credential': { - body.content.cTypes.forEach( - ({ cTypeHash, trustedAttesters, requiredProperties }): void => { - DataUtils.verifyIsHex(cTypeHash) - trustedAttesters?.forEach((did) => Did.validateUri(did, 'Did')) - requiredProperties?.forEach((requiredProps) => { - if (typeof requiredProps !== 'string') - throw new TypeError( - 'Required properties is expected to be a string' - ) - }) - } - ) - break - } - case 'submit-credential': { - body.content.forEach((presentation) => { - Credential.verifyDataStructure(presentation) - if (!Did.isDidSignature(presentation.claimerSignature)) { - throw new SDKErrors.SignatureMalformedError() - } - }) - break - } - case 'accept-credential': { - body.content.forEach((cTypeHash) => DataUtils.verifyIsHex(cTypeHash)) - break - } - case 'reject-credential': { - body.content.forEach((cTypeHash) => DataUtils.verifyIsHex(cTypeHash)) - break - } - case 'request-accept-delegation': { - verifyDelegationStructure(body.content.delegationData) - if (!Did.isDidSignature(body.content.signatures.inviter)) { - throw new SDKErrors.SignatureMalformedError() - } - if (!isJsonObject(body.content.metaData)) { - throw new SDKErrors.ObjectUnverifiableError() - } - break - } - case 'submit-accept-delegation': { - verifyDelegationStructure(body.content.delegationData) - if ( - !Did.isDidSignature(body.content.signatures.inviter) || - !Did.isDidSignature(body.content.signatures.invitee) - ) { - throw new SDKErrors.SignatureMalformedError() - } - break - } - - case 'reject-accept-delegation': { - verifyDelegationStructure(body.content) - break - } - case 'inform-create-delegation': { - DataUtils.verifyIsHex(body.content.delegationId) - break - } - - default: - throw new SDKErrors.UnknownMessageBodyTypeError() - } -} - -/** - * Checks if the message object is well-formed. - * - * @param message The message object. - */ -export function verifyMessageEnvelope(message: IMessage): void { - const { messageId, createdAt, receiver, sender, receivedAt, inReplyTo } = - message - if (messageId !== undefined && typeof messageId !== 'string') { - throw new TypeError('Message id is expected to be a string') - } - if (createdAt !== undefined && typeof createdAt !== 'number') { - throw new TypeError('Created at is expected to be a number') - } - if (receivedAt !== undefined && typeof receivedAt !== 'number') { - throw new TypeError('Received at is expected to be a number') - } - Did.validateUri(sender, 'Did') - Did.validateUri(receiver, 'Did') - if (inReplyTo && typeof inReplyTo !== 'string') { - throw new TypeError('In reply to is expected to be a string') - } -} - -/** - * Verifies required properties for a given [[CType]] before sending or receiving a message. - * - * @param requiredProperties The list of required properties that need to be verified against a [[CType]]. - * @param cType A [[CType]] used to verify the properties. - */ -export function verifyRequiredCTypeProperties( - requiredProperties: string[], - cType: ICType -): void { - CType.verifyDataStructure(cType as ICType) - - const unknownProperties = requiredProperties.find( - (property) => !(property in cType.properties) - ) - if (unknownProperties) { - throw new SDKErrors.CTypeUnknownPropertiesError() - } -} - -/** - * Verifies that the sender of a [[Message]] is also the owner of it, e.g the owner's and sender's DIDs refer to the same subject. - * - * @param message The [[Message]] object which needs to be decrypted. - * @param message.body The body of the [[Message]] which depends on the [[BodyType]]. - * @param message.sender The sender's DID taken from the [[IMessage]]. - */ -export function ensureOwnerIsSender({ body, sender }: IMessage): void { - switch (body.type) { - case 'request-attestation': - { - const requestAttestation = body - if ( - !Did.isSameSubject( - requestAttestation.content.credential.claim.owner, - sender - ) - ) { - throw new SDKErrors.IdentityMismatchError('Claim', 'Sender') - } - } - break - case 'submit-attestation': - { - const submitAttestation = body - if ( - !Did.isSameSubject( - submitAttestation.content.attestation.owner, - sender - ) - ) { - throw new SDKErrors.IdentityMismatchError('Attestation', 'Sender') - } - } - break - case 'submit-credential': - { - const submitClaimsForCtype = body - submitClaimsForCtype.content.forEach((presentation) => { - if (!Did.isSameSubject(presentation.claim.owner, sender)) { - throw new SDKErrors.IdentityMismatchError('Claims', 'Sender') - } - }) - } - break - default: - } -} - -/** - * Symmetrically decrypts the result of [[encrypt]]. - * - * @param encrypted The encrypted message. - * @param decryptCallback The callback to decrypt with the secret key. - * @param decryptionOptions Options to perform the decryption operation. - * @param decryptionOptions.resolveKey The DID key resolver to use. - * @returns The original [[Message]]. - */ -export async function decrypt( - encrypted: IEncryptedMessage, - decryptCallback: DecryptCallback, - { - resolveKey = Did.resolveKey, - }: { - resolveKey?: DidResolveKey - } = {} -): Promise { - const { senderKeyUri, receiverKeyUri, ciphertext, nonce, receivedAt } = - encrypted - - const senderKeyDetails = await resolveKey(senderKeyUri, 'keyAgreement') - - const { fragment } = Did.parse(receiverKeyUri) - if (!fragment) { - throw new SDKErrors.DidError( - `No fragment for the receiver key ID "${receiverKeyUri}"` - ) - } - - let data: Uint8Array - try { - data = ( - await decryptCallback({ - peerPublicKey: senderKeyDetails.publicKey, - data: hexToU8a(ciphertext), - nonce: hexToU8a(nonce), - keyUri: receiverKeyUri, - }) - ).data - } catch (cause) { - throw new SDKErrors.DecodingMessageError(undefined, { - cause: cause as Error, - }) - } - - const decoded = u8aToString(data) - - const { - body, - createdAt, - messageId, - inReplyTo, - references, - sender, - receiver, - } = JSON.parse(decoded) as IEncryptedMessageContents - const decrypted: IMessage = { - receiver, - sender, - createdAt, - body, - messageId, - receivedAt, - inReplyTo, - references, - } - - if (sender !== senderKeyDetails.controller) { - throw new SDKErrors.IdentityMismatchError('Encryption key', 'Sender') - } - - return decrypted -} - -/** - * Checks the message structure and body contents (e.g. Hashes match, ensures the owner is the sender). - * Throws, if a check fails. - * - * @param decryptedMessage The decrypted message to check. - */ -export function verify(decryptedMessage: IMessage): void { - verifyMessageBody(decryptedMessage.body) - verifyMessageEnvelope(decryptedMessage) - ensureOwnerIsSender(decryptedMessage) -} - -/** - * Constructs a message from a message body. - * This should be encrypted with [[encrypt]] before sending to the receiver. - * - * @param body The body of the message. - * @param sender The DID of the sender. - * @param receiver The DID of the receiver. - * @returns The message created. - */ -export function fromBody( - body: MessageBody, - sender: IMessage['sender'], - receiver: IMessage['receiver'] -): IMessage { - return { - body, - createdAt: Date.now(), - receiver, - sender, - messageId: UUID.generate(), - } -} - -/** - * Encrypts the [[Message]] as a string. This can be reversed with [[decrypt]]. - * - * @param message The message to encrypt. - * @param encryptCallback The callback to encrypt with the secret key. - * @param receiverKeyUri The key URI of the receiver. - * @param encryptionOptions Options to perform the encryption operation. - * @param encryptionOptions.resolveKey The DID key resolver to use. - * - * @returns The encrypted version of the original [[Message]], see [[IEncryptedMessage]]. - */ -export async function encrypt( - message: IMessage, - encryptCallback: EncryptCallback, - receiverKeyUri: DidResourceUri, - { - resolveKey = Did.resolveKey, - }: { - resolveKey?: DidResolveKey - } = {} -): Promise { - const receiverKey = await resolveKey(receiverKeyUri, 'keyAgreement') - if (message.receiver !== receiverKey.controller) { - throw new SDKErrors.IdentityMismatchError('receiver public key', 'receiver') - } - - const toEncrypt: IEncryptedMessageContents = { - body: message.body, - createdAt: message.createdAt, - sender: message.sender, - receiver: message.receiver, - messageId: message.messageId, - inReplyTo: message.inReplyTo, - references: message.references, - } - - const serialized = stringToU8a(JSON.stringify(toEncrypt)) - - const encrypted = await encryptCallback({ - did: message.sender, - data: serialized, - peerPublicKey: receiverKey.publicKey, - }) - - const ciphertext = u8aToHex(encrypted.data) - const nonce = u8aToHex(encrypted.nonce) - - return { - receivedAt: message.receivedAt, - ciphertext, - nonce, - senderKeyUri: encrypted.keyUri, - receiverKeyUri: receiverKey.id, - } -} diff --git a/packages/messaging/src/index.ts b/packages/messaging/src/index.ts deleted file mode 100644 index fc34fe781..000000000 --- a/packages/messaging/src/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -/** - * KILT participants can communicate via a 1:1 messaging system. - * - * All messages are **encrypted** with the encryption keys of the involved identities. - * Messages are encrypted using authenticated encryption: the two parties authenticate to each other, but the message authentication provides repudiation possibilities. - * - * The [[Message]] class exposes methods to construct and verify messages. - * - * @module @kiltprotocol/messaging - */ - -export * from './Message.js' diff --git a/packages/messaging/tsconfig.build.json b/packages/messaging/tsconfig.build.json deleted file mode 100644 index ab24dae00..000000000 --- a/packages/messaging/tsconfig.build.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../../tsconfig.build.json", - - "compilerOptions": { - "module": "CommonJS", - "outDir": "./lib/cjs" - }, - - "include": [ - "src/**/*.ts", "src/**/*.js" - ], - - "exclude": [ - "coverage", - "**/*.spec.ts", - ] -} diff --git a/packages/messaging/tsconfig.esm.json b/packages/messaging/tsconfig.esm.json deleted file mode 100644 index e1f3b73b6..000000000 --- a/packages/messaging/tsconfig.esm.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig.build.json", - "compilerOptions": { - "module": "ES6", - "outDir": "./lib/esm" - } -} diff --git a/packages/sdk-js/package.json b/packages/sdk-js/package.json index e7ee8f930..3e6bcbf88 100644 --- a/packages/sdk-js/package.json +++ b/packages/sdk-js/package.json @@ -43,7 +43,6 @@ "@kiltprotocol/config": "workspace:*", "@kiltprotocol/core": "workspace:*", "@kiltprotocol/did": "workspace:*", - "@kiltprotocol/messaging": "workspace:*", "@kiltprotocol/types": "workspace:*", "@kiltprotocol/utils": "workspace:*" } diff --git a/packages/sdk-js/src/index.ts b/packages/sdk-js/src/index.ts index dfc83c64d..d4266c502 100644 --- a/packages/sdk-js/src/index.ts +++ b/packages/sdk-js/src/index.ts @@ -11,7 +11,6 @@ export * from '@kiltprotocol/core' export { ConfigService } from '@kiltprotocol/config' -export * as Message from '@kiltprotocol/messaging' export { Blockchain } from '@kiltprotocol/chain-helpers' export * as ChainHelpers from '@kiltprotocol/chain-helpers' export * as Did from '@kiltprotocol/did' diff --git a/packages/types/src/Message.ts b/packages/types/src/Message.ts deleted file mode 100644 index b4264dc76..000000000 --- a/packages/types/src/Message.ts +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import type { AnyJson } from './Imported' -import type { DidResourceUri, DidSignature, DidUri } from './DidDocument.js' -import type { IAttestation } from './Attestation.js' -import type { PartialClaim } from './Claim.js' -import type { IDelegationNode } from './Delegation.js' -import type { IQuoteAgreement } from './Quote.js' -import type { ICredential, ICredentialPresentation } from './Credential.js' -import type { ITerms } from './Terms.js' -import type { CTypeHash } from './CType.js' - -export type MessageBodyType = - | 'error' - | 'reject' - | 'request-terms' - | 'submit-terms' - | 'reject-terms' - | 'request-attestation' - | 'submit-attestation' - | 'reject-attestation' - | 'request-payment' - | 'confirm-payment' - | 'request-credential' - | 'submit-credential' - | 'accept-credential' - | 'reject-credential' - | 'request-accept-delegation' - | 'submit-accept-delegation' - | 'reject-accept-delegation' - | 'inform-create-delegation' - -interface IMessageBodyBase { - content: any - type: MessageBodyType -} - -export interface IError extends IMessageBodyBase { - content: { - /** Optional machine-readable type of the error. */ - name?: string - /** Optional human-readable description of the error. */ - message?: string - } - type: 'error' -} - -export interface IReject extends IMessageBodyBase { - content: { - /** Optional machine-readable type of the rejection. */ - name?: string - /** Optional human-readable description of the rejection. */ - message?: string - } - type: 'reject' -} - -export interface IRequestTerms extends IMessageBodyBase { - content: PartialClaim - type: 'request-terms' -} - -export interface ISubmitTerms extends IMessageBodyBase { - content: ITerms - type: 'submit-terms' -} - -export interface IRejectTerms extends IMessageBodyBase { - content: { - claim: PartialClaim - legitimations: ICredential[] - delegationId?: IDelegationNode['id'] - } - type: 'reject-terms' -} - -export interface ISubmitCredential extends IMessageBodyBase { - content: ICredentialPresentation[] - type: 'submit-credential' -} - -export interface IAcceptCredential extends IMessageBodyBase { - content: CTypeHash[] - type: 'accept-credential' -} - -export interface IRejectCredential extends IMessageBodyBase { - content: CTypeHash[] - type: 'reject-credential' -} - -export interface IRequestAttestationContent { - credential: ICredential - quote?: IQuoteAgreement -} - -export interface IRequestAttestation extends IMessageBodyBase { - content: IRequestAttestationContent - type: 'request-attestation' -} - -export interface ISubmitAttestationContent { - attestation: IAttestation -} - -export interface ISubmitAttestation extends IMessageBodyBase { - content: ISubmitAttestationContent - type: 'submit-attestation' -} - -export interface IRejectAttestation extends IMessageBodyBase { - content: ICredential['rootHash'] - type: 'reject-attestation' -} - -export interface IRequestPaymentContent { - // Same as the `rootHash` value of the `'request-attestation'` message */ - claimHash: string -} - -export interface IConfirmPaymentContent { - // Same as the `rootHash` value of the `'request-attestation'` message - claimHash: string - // Hash of the payment transaction */ - txHash: string - // hash of the block which includes the payment transaction - blockHash: string -} - -export interface IConfirmPayment extends IMessageBodyBase { - content: IConfirmPaymentContent - type: 'confirm-payment' -} - -export interface IRequestCredentialContent { - cTypes: Array<{ - cTypeHash: CTypeHash - trustedAttesters?: DidUri[] - requiredProperties?: string[] - }> - challenge?: string -} - -export interface IRequestCredential extends IMessageBodyBase { - content: IRequestCredentialContent - type: 'request-credential' -} - -export interface IRequestPayment extends IMessageBodyBase { - content: IRequestPaymentContent - type: 'request-payment' -} - -export interface IDelegationData { - account: IDelegationNode['account'] - id: IDelegationNode['id'] - parentId: IDelegationNode['id'] - permissions: IDelegationNode['permissions'] - isPCR: boolean -} - -export interface IRejectAcceptDelegation extends IMessageBodyBase { - content: IDelegationData - type: 'reject-accept-delegation' -} - -export interface IRequestDelegationApproval { - delegationData: IDelegationData - metaData?: AnyJson - signatures: { - inviter: DidSignature - } -} - -export interface IRequestAcceptDelegation extends IMessageBodyBase { - content: IRequestDelegationApproval - type: 'request-accept-delegation' -} - -export interface ISubmitDelegationApproval { - delegationData: IDelegationData - signatures: { - inviter: DidSignature - invitee: DidSignature - } -} - -export interface ISubmitAcceptDelegation extends IMessageBodyBase { - content: ISubmitDelegationApproval - type: 'submit-accept-delegation' -} - -export interface IInformDelegationCreation { - delegationId: IDelegationNode['id'] - isPCR: boolean -} - -export interface IInformCreateDelegation extends IMessageBodyBase { - content: IInformDelegationCreation - type: 'inform-create-delegation' -} - -export type MessageBody = - | IError - | IReject - // - | IRequestTerms - | ISubmitTerms - | IRejectTerms - // - | IRequestAttestation - | ISubmitAttestation - | IRejectAttestation - // - | IRequestCredential - | ISubmitCredential - | IAcceptCredential - | IRejectCredential - // - | IRequestAcceptDelegation - | ISubmitAcceptDelegation - | IRejectAcceptDelegation - | IInformCreateDelegation - -/** - * - `body` - The body of the message, see [[MessageBody]]. - * - `createdAt` - The timestamp of the message construction. - * - `sender` - The DID of the sender. - * - `receiver` - The DID of the receiver. - * - `messageId` - The message id. - * - `receivedAt` - The timestamp of the message reception. - * - `inReplyTo` - The id of the parent-message. - * - `references` - The references or the in-reply-to of the parent-message followed by the message-id of the parent-message. - */ -export interface IMessage { - body: MessageBody - createdAt: number - sender: DidUri - receiver: DidUri - messageId?: string - receivedAt?: number - inReplyTo?: IMessage['messageId'] - references?: Array -} - -/** - * Everything which is part of the encrypted and protected part of the [[IMessage]]. - */ -export type IEncryptedMessageContents = Omit - -/** - * Removes the plaintext [[IEncryptedMessageContents]] from an [[IMessage]] and instead includes them in encrypted form. - * This adds the following fields: - * - `ciphertext` - The encrypted message content. - * - `nonce` - The encryption nonce. - * - `receiverKeyUri` - The URI of the receiver's encryption key. - * - `senderKeyUri` - The URI of the sender's encryption key. - */ -export type IEncryptedMessage = Pick & { - receiverKeyUri: DidResourceUri - senderKeyUri: DidResourceUri - ciphertext: string - nonce: string -} diff --git a/packages/types/src/Quote.ts b/packages/types/src/Quote.ts deleted file mode 100644 index 2a3f0ed6d..000000000 --- a/packages/types/src/Quote.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import type { DidSignature, DidUri } from './DidDocument' -import type { ICredential } from './Credential' -import type { CTypeHash } from './CType' - -export interface ICostBreakdown { - tax: Record - net: number - gross: number -} -export interface IQuote { - attesterDid: DidUri - cTypeHash: CTypeHash - cost: ICostBreakdown - currency: string - timeframe: string - termsAndConditions: string -} -export interface IQuoteAttesterSigned extends IQuote { - attesterSignature: DidSignature -} - -export interface IQuoteAgreement extends IQuoteAttesterSigned { - rootHash: ICredential['rootHash'] - claimerDid: DidUri - claimerSignature: DidSignature -} diff --git a/packages/types/src/Terms.ts b/packages/types/src/Terms.ts deleted file mode 100644 index e4578aae8..000000000 --- a/packages/types/src/Terms.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) 2018-2023, BOTLabs GmbH. - * - * This source code is licensed under the BSD 4-Clause "Original" license - * found in the LICENSE file in the root directory of this source tree. - */ - -import type { ICType } from './CType' -import type { IDelegationNode } from './Delegation' -import type { IQuoteAttesterSigned } from './Quote' -import type { PartialClaim } from './Claim' -import type { ICredential } from './Credential' - -export interface ITerms { - claim: PartialClaim - legitimations: ICredential[] - delegationId?: IDelegationNode['id'] - quote?: IQuoteAttesterSigned - cTypes?: ICType[] -} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 16c6bf418..be510ee7a 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -20,10 +20,7 @@ export * from './Claim.js' export * from './Deposit.js' export * from './Delegation.js' export * from './Address.js' -export * from './Message.js' -export * from './Quote.js' export * from './Credential.js' -export * from './Terms.js' export * from './DidDocument.js' export * from './CryptoCallbacks.js' export * from './DidResolver.js' diff --git a/tests/breakingChanges/BreakingChanges.spec.ts b/tests/breakingChanges/BreakingChanges.spec.ts index 2c73a6772..0f97a6025 100644 --- a/tests/breakingChanges/BreakingChanges.spec.ts +++ b/tests/breakingChanges/BreakingChanges.spec.ts @@ -5,21 +5,7 @@ * found in the LICENSE file in the root directory of this source tree. */ -import { - Attestation, - Claim, - Credential, - CType, - Did, - DidDocument, - DidKey, - DidResourceUri, - ICType, - Message, - MessageBody, - ResolvedDidKey, - Utils, -} from '@kiltprotocol/sdk-js' +import { Did, Utils } from '@kiltprotocol/sdk-js' import nacl from 'tweetnacl' import { v4 } from 'uuid' @@ -52,21 +38,6 @@ function makeLightDidFromSeed(seed: string) { return { did, encrypt, decrypt } } -function makeResolveKey(document: DidDocument) { - return async function resolveKey( - keyUri: DidResourceUri - ): Promise { - const { fragment } = Did.parse(keyUri) - const key = Did.getKey(document, fragment!) as DidKey - return { - controller: document!.uri, - id: keyUri!, - publicKey: key.publicKey, - type: key.type, - } - } -} - describe('Breaking Changes', () => { describe('Light DID', () => { it('does not break the light did uri generation', () => { @@ -85,149 +56,4 @@ describe('Breaking Changes', () => { ).toMatchSnapshot() }) }) - describe('Messages', () => { - it('does not break Message & EncryptedMessage structure', async () => { - const { did: aliceDid, encrypt } = makeLightDidFromSeed( - '0xdc6f4d21a91848eeeac1811c73a2323060ef2d8d4a07ece2f216d5b8f977520b' - ) - const { did: bobDid, decrypt } = makeLightDidFromSeed( - '0xa748f38e896ddc52b6e5cc5baa754f7f841381ef32bf1d86d51026857c6c05dc' - ) - - // Mock Date object for message.createdAt property - jest.useFakeTimers().setSystemTime(1657727664899) - - const message = Message.fromBody( - { - type: 'request-terms', - content: { - cTypeHash: '0x1234', - }, - }, - aliceDid.uri, - bobDid.uri - ) - - expect(message).toMatchSnapshot() - - const encrypted = await Message.encrypt( - message, - encrypt(aliceDid), - `${bobDid.uri}#encryption`, - { - resolveKey: makeResolveKey(bobDid), - } - ) - - expect(encrypted).toMatchSnapshot() - - const decrypted = await Message.decrypt(encrypted, decrypt, { - resolveKey: makeResolveKey(aliceDid), - }) - - expect(decrypted).toMatchObject(message) - }) - - it('does not break for attestation flow', async () => { - // attestation flow - - const attester = makeLightDidFromSeed( - '0xdc6f4d21a91848eeeac1811c73a2323060ef2d8d4a07ece2f216d5b8f977520b' - ) - const user = makeLightDidFromSeed( - '0xa748f38e896ddc52b6e5cc5baa754f7f841381ef32bf1d86d51026857c6c05dc' - ) - - const cType: ICType = { - $id: 'kilt:ctype:0xd5301762c62114f6455e0b373cccce20631c2a717004a98f8953e738e17c5d3c', - $schema: 'http://kilt-protocol.org/draft-01/ctype#', - title: 'CtypeModel 2', - properties: { - name: { type: 'string' }, - }, - type: 'object', - } - - const requestTerms: MessageBody = { - type: 'request-terms', - content: { - cTypeHash: CType.idToHash(cType.$id), - }, - } - expect(requestTerms).toMatchSnapshot('request-terms') - - const claim = Claim.fromCTypeAndClaimContents( - cType, - { name: 'Bob' }, - attester.did.uri - ) - expect(claim).toMatchSnapshot('claim') - - const submitTerms: MessageBody = { - type: 'submit-terms', - content: { - claim, - legitimations: [], - }, - } - expect(submitTerms).toMatchSnapshot('submit-terms') - - claim.owner = user.did.uri - const credential = Credential.fromClaim(claim, { legitimations: [] }) - expect(credential).toMatchSnapshot('credential') - - const requestAttestation: MessageBody = { - type: 'request-attestation', - content: { credential }, - } - expect(requestAttestation).toMatchSnapshot('request-attestation') - - const attestation = Attestation.fromCredentialAndDid( - credential, - attester.did.uri - ) - expect(attestation).toMatchSnapshot('attestation') - - const submitAttestation: MessageBody = { - type: 'submit-attestation', - content: { attestation }, - } - expect(submitAttestation).toMatchSnapshot('submit-attestation') - - // verification flow - - const challenge = '0xCAFE' - const requestCredential: MessageBody = { - type: 'request-credential', - content: { - cTypes: [ - { - cTypeHash: CType.idToHash(cType.$id), - requiredProperties: ['name'], - trustedAttesters: [attester.did.uri], - }, - ], - challenge, - }, - } - expect(requestCredential).toMatchSnapshot('request-credential') - - const presentation = await Credential.createPresentation({ - credential, - challenge, - signCallback: async () => ({ - signature: new Uint8Array(32).fill(0), - keyUri: `${user.did.uri}${user.did.authentication[0].id}`, - keyType: user.did.authentication[0].type, - }), - }) - expect(presentation).toMatchSnapshot('presentation') - - const submitCredential: MessageBody = { - type: 'submit-credential', - content: [presentation], - } - expect(submitCredential).toMatchSnapshot('submit-credential') - }) - }) }) diff --git a/tests/bundle/bundle-test.ts b/tests/bundle/bundle-test.ts index df39e0397..5e0c00cb9 100644 --- a/tests/bundle/bundle-test.ts +++ b/tests/bundle/bundle-test.ts @@ -8,9 +8,7 @@ /// import type { - DecryptCallback, DidDocument, - EncryptCallback, KeyringPair, KiltEncryptionKeypair, KiltKeyringPair, @@ -29,7 +27,6 @@ const { Did, Blockchain, Utils: { Crypto, ss58Format }, - Message, BalanceUtils, } = kilt @@ -95,39 +92,6 @@ function makeEncryptionKeypair(seed: string): KiltEncryptionKeypair { } } -function makeEncryptCallback({ - secretKey, -}: KiltEncryptionKeypair): (didDocument: DidDocument) => EncryptCallback { - return (didDocument) => { - return async function encryptCallback({ data, peerPublicKey }) { - const keyId = didDocument.keyAgreement?.[0].id - if (!keyId) { - throw new Error(`Encryption key not found in did "${didDocument.uri}"`) - } - const { box, nonce } = Crypto.encryptAsymmetric( - data, - peerPublicKey, - secretKey - ) - return { nonce, data: box, keyUri: `${didDocument.uri}${keyId}` } - } - } -} - -function makeDecryptCallback({ - secretKey, -}: KiltEncryptionKeypair): DecryptCallback { - return async function decryptCallback({ data, nonce, peerPublicKey }) { - const decrypted = Crypto.decryptAsymmetric( - { box: data, nonce }, - peerPublicKey, - secretKey - ) - if (decrypted === false) throw new Error('Decryption failed') - return { data: decrypted } - } -} - async function createFullDidFromKeypair( payer: KiltKeyringPair, keypair: KiltKeyringPair, @@ -168,7 +132,6 @@ async function runAll() { const { keypair: aliceKeypair, getSignCallback: aliceSign } = makeSigningKeypair('//Alice') const aliceEncryptionKey = makeEncryptionKeypair('//Alice//enc') - const aliceDecryptCallback = makeDecryptCallback(aliceEncryptionKey) const alice = await createFullDidFromKeypair( payer, aliceKeypair, @@ -181,7 +144,6 @@ async function runAll() { const { keypair: bobKeypair, getSignCallback: bobSign } = makeSigningKeypair('//Bob') const bobEncryptionKey = makeEncryptionKeypair('//Bob//enc') - const bobEncryptCallback = makeEncryptCallback(bobEncryptionKey) const bob = await createFullDidFromKeypair( payer, bobKeypair, @@ -303,31 +265,6 @@ async function runAll() { await Credential.verifySignature(presentation) console.info('Presentation signature verified') - console.log('Test Messaging with encryption + decryption') - const message = Message.fromBody( - { - content: { - credential, - }, - type: 'request-attestation', - }, - bob.uri, - alice.uri - ) - const encryptedMessage = await Message.encrypt( - message, - bobEncryptCallback(bob), - `${alice.uri}${alice.keyAgreement[0].id}` - ) - - const decryptedMessage = await Message.decrypt( - encryptedMessage, - aliceDecryptCallback - ) - if (JSON.stringify(message.body) !== JSON.stringify(decryptedMessage.body)) { - throw new Error('Original and decrypted message are not the same') - } - const attestation = Attestation.fromCredentialAndDid(credential, alice.uri) Attestation.verifyAgainstCredential(attestation, credential) console.info('Attestation Data verified') diff --git a/tests/bundle/bundle.spec.ts b/tests/bundle/bundle.spec.ts index 88da2475f..3ced1769f 100644 --- a/tests/bundle/bundle.spec.ts +++ b/tests/bundle/bundle.spec.ts @@ -37,10 +37,12 @@ test('html bundle integration test', async ({ page }) => { const fileUrl = url.pathToFileURL( path.join(__dirname, 'bundle-test.html') ).href + page.on('pageerror', (exception) => { console.error(exception) throw new Error('-1') }) + page.on('console', async (msg) => { console.log(msg.text()) }) diff --git a/tsconfig.json b/tsconfig.json index a447ff164..7faeabab6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,6 @@ "@kiltprotocol/chain-helpers": ["chain-helpers/src"], "@kiltprotocol/config": ["config/src"], "@kiltprotocol/core": ["core/src"], - "@kiltprotocol/messaging": ["messaging/src"], "@kiltprotocol/sdk-js": ["sdk-js/src"], "@kiltprotocol/types": ["types/src"], "@kiltprotocol/utils": ["utils/src"], @@ -21,4 +20,4 @@ "@kiltprotocol/type-definitions": ["type-definitions/src"], } }, -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index db034755f..6682b1f48 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2019,20 +2019,6 @@ __metadata: languageName: unknown linkType: soft -"@kiltprotocol/messaging@workspace:*, @kiltprotocol/messaging@workspace:packages/messaging": - version: 0.0.0-use.local - resolution: "@kiltprotocol/messaging@workspace:packages/messaging" - dependencies: - "@kiltprotocol/core": "workspace:*" - "@kiltprotocol/did": "workspace:*" - "@kiltprotocol/types": "workspace:*" - "@kiltprotocol/utils": "workspace:*" - "@polkadot/util": ^12.0.0 - rimraf: ^3.0.2 - typescript: ^4.8.3 - languageName: unknown - linkType: soft - "@kiltprotocol/sdk-js@workspace:packages/sdk-js": version: 0.0.0-use.local resolution: "@kiltprotocol/sdk-js@workspace:packages/sdk-js" @@ -2041,7 +2027,6 @@ __metadata: "@kiltprotocol/config": "workspace:*" "@kiltprotocol/core": "workspace:*" "@kiltprotocol/did": "workspace:*" - "@kiltprotocol/messaging": "workspace:*" "@kiltprotocol/types": "workspace:*" "@kiltprotocol/utils": "workspace:*" rimraf: ^3.0.2