Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/continuous-delivery.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ jobs:
tags: |
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.SERVICE }}:${{ env.TAG }}
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.SERVICE }}:latest


132 changes: 128 additions & 4 deletions src/controllers/multi-tenancy/MultiTenancyController.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable prettier/prettier */
import type { RestAgentModules, RestMultiTenantAgentModules } from '../../cliAgent'
import type { Version } from '../examples'
import type { RecipientKeyOption, SchemaMetadata } from '../types'
import type { CustomW3cJsonLdSignCredentialOptions, RecipientKeyOption, SafeW3cJsonLdVerifyCredentialOptions, SchemaMetadata } from '../types'
import type { PolygonDidCreateOptions } from '@ayanworks/credo-polygon-w3c-module/build/dids'
import type {
AcceptProofRequestOptions,
Expand All @@ -15,7 +15,8 @@ import type {
ProofExchangeRecordProps,
ProofsProtocolVersionType,
Routing,
} from '@credo-ts/core'
W3cJsonLdSignCredentialOptions,
W3cVerifiableCredential} from '@credo-ts/core'
import type { IndyVdrDidCreateOptions, IndyVdrDidCreateResult } from '@credo-ts/indy-vdr'
import type { QuestionAnswerRecord, ValidResponse } from '@credo-ts/question-answer'
import type { TenantRecord } from '@credo-ts/tenants'
Expand All @@ -27,6 +28,7 @@ import {
parseIndyCredentialDefinitionId,
parseIndySchemaId,
} from '@credo-ts/anoncreds'
import { assertAskarWallet } from '@credo-ts/askar/build/utils/assertAskarWallet'
import {
AcceptCredentialOfferOptions,
Agent,
Expand All @@ -45,7 +47,9 @@ import {
injectable,
createPeerDidDocumentFromServices,
PeerDidNumAlgo,
} from '@credo-ts/core'
W3cJsonLdVerifiableCredential,
W3cCredential,
ClaimFormat} from '@credo-ts/core'
import { QuestionAnswerRole, QuestionAnswerState } from '@credo-ts/question-answer'
import axios from 'axios'
import * as fs from 'fs'
Expand Down Expand Up @@ -89,7 +93,7 @@ import {
CreateProofRequestOobOptions,
CreateOfferOobOptions,
CreateSchemaInput,
} from '../types'
VerifyDataOptions , SignDataOptions } from '../types'

import { Body, Controller, Delete, Get, Post, Query, Route, Tags, Path, Example, Security, Response } from 'tsoa'

Expand Down Expand Up @@ -1913,4 +1917,124 @@ export class MultiTenancyController extends Controller {
throw ErrorHandlingService.handle(error)
}
}

/**
* Verify data using a key
*
* @param tenantId Tenant identifier
* @param request Verify options
* data - Data has to be in base64 format
* publicKeyBase58 - Public key in base58 format
* signature - Signature in base64 format
* @returns isValidSignature - true if signature is valid, false otherwise
*/
@Security('apiKey')
@Post('/verify/:tenantId')
public async verify(@Path('tenantId') tenantId: string, @Body() request: VerifyDataOptions) {
try {
const isValidSignature = await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => {
assertAskarWallet(tenantAgent.context.wallet)
const isValidSignature = await tenantAgent.context.wallet.verify({
data: TypedArrayEncoder.fromBase64(request.data),
key: Key.fromPublicKeyBase58(request.publicKeyBase58, request.keyType),
signature: TypedArrayEncoder.fromBase64(request.signature),
})
return isValidSignature
})
return isValidSignature
} catch (error) {
throw ErrorHandlingService.handle(error)
}
}

@Security('apiKey')
@Post('/credential/sign/:tenantId')
public async signCredential(
@Path('tenantId') tenantId: string,
@Query('storeCredential') storeCredential: boolean,
@Query('dataTypeToSign') dataTypeToSign: 'rawData' | 'jsonLd',
@Body() data: CustomW3cJsonLdSignCredentialOptions | SignDataOptions | any
) {
try {
return await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => {
// JSON-LD VC Signing
if (dataTypeToSign === 'jsonLd') {
const credentialData = data as unknown as W3cJsonLdSignCredentialOptions
credentialData.format = ClaimFormat.LdpVc

const signedCredential = await tenantAgent.w3cCredentials.signCredential(credentialData) as W3cJsonLdVerifiableCredential

if (storeCredential) {
return await tenantAgent.w3cCredentials.storeCredential({ credential: signedCredential })
}

return signedCredential.toJson()
}

// Raw Data Signing
const rawData = data as SignDataOptions

if (!rawData.data) throw new BadRequestError('Missing "data" for raw data signing.')

const hasDidOrMethod = rawData.did || rawData.method
const hasPublicKey = rawData.publicKeyBase58 && rawData.keyType

if (!hasDidOrMethod && !hasPublicKey) {
throw new BadRequestError('Either (did or method) OR (publicKeyBase58 and keyType) must be provided.')
}

let keyToUse: Key

if (hasDidOrMethod) {
const dids = await tenantAgent.dids.getCreatedDids({
method: rawData.method || undefined,
did: rawData.did || undefined,
})

const verificationMethod = dids[0]?.didDocument?.verificationMethod?.[0]?.publicKeyBase58
if (!verificationMethod) {
throw new BadRequestError('No publicKeyBase58 found for the given DID or method.')
}

keyToUse = Key.fromPublicKeyBase58(verificationMethod, rawData.keyType)
} else {
keyToUse = Key.fromPublicKeyBase58(rawData.publicKeyBase58, rawData.keyType)
}

if (!keyToUse) {
throw new Error('Unable to construct signing key.')
}

const signature = await tenantAgent.context.wallet.sign({
data: TypedArrayEncoder.fromBase64(rawData.data),
key: keyToUse,
})

return TypedArrayEncoder.toBase64(signature)
})
} catch (error) {
throw ErrorHandlingService.handle(error)
}
}

@Security('apiKey')
@Post('/credential/verify/:tenantId')
public async verifyCredential(
@Path('tenantId') tenantId: string,
@Body() credentialToVerify: SafeW3cJsonLdVerifyCredentialOptions | any
) {
let formattedCredential
try {
await this.agent.modules.tenants.withTenantAgent({ tenantId }, async (tenantAgent) => {
const {credential, ...credentialOptions}= credentialToVerify
const transformedCredential = JsonTransformer.fromJSON(credentialToVerify?.credential, W3cJsonLdVerifiableCredential)
const signedCred = await tenantAgent.w3cCredentials.verifyCredential({credential: transformedCredential, ...credentialOptions})
formattedCredential = signedCred
})
return formattedCredential
} catch (error) {
throw ErrorHandlingService.handle(error)
}
}
}

57 changes: 53 additions & 4 deletions src/controllers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,16 @@ import type {
Attachment,
KeyType,
JsonLdCredentialFormat,
JsonObject,
W3cJsonLdVerifyCredentialOptions,
DataIntegrityProofOptions,
W3cJsonLdSignCredentialOptions,
W3cCredential,
W3cCredentialSubject,
} from '@credo-ts/core'
import type { SingleOrArray } from '@credo-ts/core/build/utils'
import type { DIDDocument } from 'did-resolver'
import { LinkedDataProofOptions } from '@credo-ts/core/build/modules/vc/data-integrity/models/LinkedDataProof'

export type TenantConfig = Pick<InitConfig, 'label' | 'connectionImageUrl'> & {
walletConfig: Pick<WalletConfig, 'id' | 'key' | 'keyDerivationMethod'>
Expand All @@ -37,10 +45,6 @@ export interface AgentInfo {
endpoints: string[]
isInitialized: boolean
publicDid: void
// publicDid?: {
// did: string
// verkey: string
// }
}

export interface AgentMessageType {
Expand Down Expand Up @@ -388,3 +392,48 @@ export interface SchemaMetadata {
* @example "ea4e5e69-fc04-465a-90d2-9f8ff78aa71d"
*/
export type ThreadId = string

export type SignDataOptions = {
data: string
keyType: KeyType
publicKeyBase58: string
did?: string
method?: string
}

export type VerifyDataOptions = {
data: string
keyType: KeyType
publicKeyBase58: string
signature: string
}

export interface jsonLdCredentialOptions {
'@context': Array<string | JsonObject>
type: Array<string>
credentialSubject: SingleOrArray<JsonObject>
proofType: string
}

export interface credentialPayloadToSign {
issuerDID: string
method: string
credential: jsonLdCredentialOptions // TODO: add support for other credential format
}
export interface SafeW3cJsonLdVerifyCredentialOptions extends W3cJsonLdVerifyCredentialOptions {
// Ommited due to issues with TSOA
proof: SingleOrArray<Omit<LinkedDataProofOptions, "cryptosuite"> | DataIntegrityProofOptions>
}

export type ExtensibleW3cCredentialSubject = W3cCredentialSubject & {
[key: string]: unknown
}

export type ExtensibleW3cCredential = W3cCredential & {
[key: string]: unknown
credentialSubject: SingleOrArray<ExtensibleW3cCredentialSubject>
}

export type CustomW3cJsonLdSignCredentialOptions = Omit<W3cJsonLdSignCredentialOptions, 'format'> & {
[key: string]: unknown
}
Loading