Skip to content

Commit

Permalink
refactor: messaging (#18)
Browse files Browse the repository at this point in the history
## fixes KILTprotocol/ticket#2715
 
Co-authored-by: Raphael Flechtner <[email protected]>
  • Loading branch information
Ad96el authored Oct 5, 2023
1 parent 60e743d commit 3322558
Show file tree
Hide file tree
Showing 26 changed files with 3,025 additions and 663 deletions.
10 changes: 4 additions & 6 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
"plugin:@typescript-eslint/recommended",
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
Expand All @@ -16,10 +17,7 @@
"@typescript-eslint"
],
"rules": {
"indent": [
"error",
2
],

"linebreak-style": [
"error",
"unix"
Expand All @@ -33,4 +31,4 @@
"never"
]
}
}
}
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
node-version: 16
cache: 'yarn'

- name: yarn install
- name: yarn install
run: yarn install --immutable

- name: Configure AWS credentials
Expand All @@ -45,7 +45,7 @@ jobs:
TESTCONTAINERS_NODE_IMG: ${{ env.IMG_NAME }}
run: |
yarn test
- name: yarn build
run: yarn build

Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/node_modules
yarn.lock
yarn-error.log
/.vscode

*.js
*.d.ts
*.d.ts
7 changes: 4 additions & 3 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"trailingComma": "es5",
"semi": false,
"singleQuote": true
"trailingComma": "es5",
"semi": false,
"singleQuote": true,
"tabWidth": 2
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"eslint": ">=8.14.0",
"eslint-config-prettier": "^9.0.0",
"jest": "^28.0.0",
"react": "^18.2.0",
"testcontainers": "^9.5.0",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { getExtensions, watchExtensions, initializeKiltExtensionAPI } from './getExtension'
export * as Message from './messaging'
export * as Quote from './quote'
112 changes: 112 additions & 0 deletions src/messaging/CredentialApiMessageType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/**
* 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 { Attestation, Claim, Credential, CType, Quote } from '@kiltprotocol/core'
import { DataUtils } from '@kiltprotocol/utils'
import * as Did from '@kiltprotocol/did'
import { isHex } from '@polkadot/util'

import {
isSubmitTerms,
isRequestAttestation,
isSubmitAttestation,
isRejectAttestation,
isSubmitCredential,
isIRequestCredential,
} from '../utils'
import { verifyMessageEnvelope } from './MessageEnvelope'
import * as MessageError from './Error'
import type { IMessage, CredentialApiMessageBody } from '../types'

/**
* Checks if the message body is well-formed.
*
* @param body The message body.
*/
export function assertKnownMessageBody(message: IMessage): void {
if (isSubmitTerms(message)) {
const { body } = message
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))
}
} else if (isRequestAttestation(message)) {
Credential.verifyDataStructure(message.body.content.credential)
if (message.body.content.quote) {
Quote.validateQuoteSchema(Quote.QuoteSchema, message.body.content.quote)
}
} else if (isSubmitAttestation(message)) {
Attestation.verifyDataStructure(message.body.content.attestation)
} else if (isRejectAttestation(message)) {
if (!isHex(message.body.content)) {
throw new MessageError.HashMalformedError()
}
} else if (isIRequestCredential(message)) {
message.body.content.cTypes.forEach(({ cTypeHash, trustedAttesters, requiredProperties }) => {
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')
})
})
} else if (isSubmitCredential(message)) {
message.body.content.forEach((presentation) => {
Credential.verifyDataStructure(presentation)
if (!Did.isDidSignature(presentation.claimerSignature)) {
throw new MessageError.SignatureMalformedError()
}
})
} else {
throw new MessageError.UnknownMessageBodyTypeError()
}
}

/**
* 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(message: IMessage): void {
if (isRequestAttestation(message)) {
if (!Did.isSameSubject(message.body.content.credential.claim.owner, message.sender)) {
throw new MessageError.IdentityMismatchError('Claim', 'Sender')
}
} else if (isSubmitAttestation(message)) {
if (!Did.isSameSubject(message.body.content.attestation.owner, message.sender)) {
throw new MessageError.IdentityMismatchError('Attestation', 'Sender')
}
} else if (isSubmitCredential(message)) {
message.body.content.forEach((presentation) => {
if (!Did.isSameSubject(presentation.claim.owner, message.sender)) {
throw new MessageError.IdentityMismatchError('Claims', 'Sender')
}
})
}
}

/**
* 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 assertKnownMessage(decryptedMessage: IMessage): CredentialApiMessageBody {
assertKnownMessageBody(decryptedMessage)
verifyMessageEnvelope(decryptedMessage)
ensureOwnerIsSender(decryptedMessage)
return decryptedMessage.body as CredentialApiMessageBody
}
23 changes: 23 additions & 0 deletions src/messaging/Error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* 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 declare class MessageError extends Error {}
export declare class HashMalformedError extends MessageError {
constructor(hash?: string, type?: string)
}

export declare class SignatureMalformedError extends MessageError {}
export declare class UnknownMessageBodyTypeError extends MessageError {}
export declare class DecodingMessageError extends MessageError {}
export declare class CTypeUnknownPropertiesError extends MessageError {}
export declare class InvalidDidFormatError extends MessageError {}
export declare class DidError extends MessageError {
constructor(context?: string, type?: string)
}
export declare class IdentityMismatchError extends MessageError {
constructor(context?: string, type?: string)
}
Loading

0 comments on commit 3322558

Please sign in to comment.