diff --git a/env-example b/env-example deleted file mode 100644 index a8c0618..0000000 --- a/env-example +++ /dev/null @@ -1,7 +0,0 @@ -PORT=8000 -COMPLIANCE_SERVICE="https://compliance.lab.gaia-x.eu/development/api/credential-offers" -REGISTRY_TRUST_ANCHOR_URL="https://registry.lab.gaia-x.eu/main/api/trustAnchor/chain" -KAFKA_BROKERS=broker.com:20186 -SSL_CA="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" -SSL_CERTIFICATE="-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" -SSL_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----" \ No newline at end of file diff --git a/src/assets/credential.json b/src/assets/credential.json new file mode 100644 index 0000000..83fa640 --- /dev/null +++ b/src/assets/credential.json @@ -0,0 +1,315 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "VerifiableCredential": { + "@id": "https://www.w3.org/2018/credentials#VerifiableCredential", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "credentialSchema": { + "@id": "cred:credentialSchema", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "cred": "https://www.w3.org/2018/credentials#", + "JsonSchemaValidator2018": "cred:JsonSchemaValidator2018" + } + }, + "credentialStatus": { + "@id": "cred:credentialStatus", + "@type": "@id" + }, + "credentialSubject": { + "@id": "cred:credentialSubject", + "@type": "@id" + }, + "evidence": { + "@id": "cred:evidence", + "@type": "@id" + }, + "expirationDate": { + "@id": "cred:expirationDate", + "@type": "xsd:dateTime" + }, + "holder": { + "@id": "cred:holder", + "@type": "@id" + }, + "issued": { + "@id": "cred:issued", + "@type": "xsd:dateTime" + }, + "issuer": { + "@id": "cred:issuer", + "@type": "@id" + }, + "issuanceDate": { + "@id": "cred:issuanceDate", + "@type": "xsd:dateTime" + }, + "proof": { + "@id": "sec:proof", + "@type": "@id", + "@container": "@graph" + }, + "refreshService": { + "@id": "cred:refreshService", + "@type": "@id", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "cred": "https://www.w3.org/2018/credentials#", + "ManualRefreshService2018": "cred:ManualRefreshService2018" + } + }, + "termsOfUse": { + "@id": "cred:termsOfUse", + "@type": "@id" + }, + "validFrom": { + "@id": "cred:validFrom", + "@type": "xsd:dateTime" + }, + "validUntil": { + "@id": "cred:validUntil", + "@type": "xsd:dateTime" + } + } + }, + "VerifiablePresentation": { + "@id": "https://www.w3.org/2018/credentials#VerifiablePresentation", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "cred": "https://www.w3.org/2018/credentials#", + "sec": "https://w3id.org/security#", + "holder": { + "@id": "cred:holder", + "@type": "@id" + }, + "proof": { + "@id": "sec:proof", + "@type": "@id", + "@container": "@graph" + }, + "verifiableCredential": { + "@id": "cred:verifiableCredential", + "@type": "@id", + "@container": "@graph" + } + } + }, + "EcdsaSecp256k1Signature2019": { + "@id": "https://w3id.org/security#EcdsaSecp256k1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "challenge": "sec:challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime" + }, + "domain": "sec:domain", + "expires": { + "@id": "sec:expiration", + "@type": "xsd:dateTime" + }, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "assertionMethod": { + "@id": "sec:assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "sec:authenticationMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": { + "@id": "sec:verificationMethod", + "@type": "@id" + } + } + }, + "EcdsaSecp256r1Signature2019": { + "@id": "https://w3id.org/security#EcdsaSecp256r1Signature2019", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "challenge": "sec:challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime" + }, + "domain": "sec:domain", + "expires": { + "@id": "sec:expiration", + "@type": "xsd:dateTime" + }, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "assertionMethod": { + "@id": "sec:assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "sec:authenticationMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": { + "@id": "sec:verificationMethod", + "@type": "@id" + } + } + }, + "Ed25519Signature2018": { + "@id": "https://w3id.org/security#Ed25519Signature2018", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "challenge": "sec:challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime" + }, + "domain": "sec:domain", + "expires": { + "@id": "sec:expiration", + "@type": "xsd:dateTime" + }, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "assertionMethod": { + "@id": "sec:assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "sec:authenticationMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": { + "@id": "sec:verificationMethod", + "@type": "@id" + } + } + }, + "RsaSignature2018": { + "@id": "https://w3id.org/security#RsaSignature2018", + "@context": { + "@version": 1.1, + "@protected": true, + "challenge": "sec:challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "xsd:dateTime" + }, + "domain": "sec:domain", + "expires": { + "@id": "sec:expiration", + "@type": "xsd:dateTime" + }, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + "id": "@id", + "type": "@type", + "sec": "https://w3id.org/security#", + "assertionMethod": { + "@id": "sec:assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "sec:authenticationMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": { + "@id": "sec:verificationMethod", + "@type": "@id" + } + } + }, + "proof": { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph" + } + } +} diff --git a/src/assets/jws-2020.json b/src/assets/jws-2020.json new file mode 100644 index 0000000..04e0d5c --- /dev/null +++ b/src/assets/jws-2020.json @@ -0,0 +1,78 @@ +{ + "@context": { + "privateKeyJwk": { + "@id": "https://w3id.org/security#privateKeyJwk", + "@type": "@json" + }, + "JsonWebKey2020": { + "@id": "https://w3id.org/security#JsonWebKey2020", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "publicKeyJwk": { + "@id": "https://w3id.org/security#publicKeyJwk", + "@type": "@json" + } + } + }, + "JsonWebSignature2020": { + "@id": "https://w3id.org/security#JsonWebSignature2020", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "challenge": "https://w3id.org/security#challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "domain": "https://w3id.org/security#domain", + "expires": { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "jws": "https://w3id.org/security#jws", + "nonce": "https://w3id.org/security#nonce", + "proofPurpose": { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } + } + } +} diff --git a/src/routes/private.ts b/src/routes/private.ts index 53a8ef5..2b0291c 100644 --- a/src/routes/private.ts +++ b/src/routes/private.ts @@ -1,16 +1,19 @@ +/* eslint-disable no-case-declarations */ +import axios from 'axios' +import crypto, { X509Certificate } from 'crypto' +import { createHash } from 'crypto' +import { Resolver } from 'did-resolver' import express, { Request, Response } from 'express' +import { check, validationResult } from 'express-validator' +import * as he from 'he' import * as jose from 'jose' import jsonld from 'jsonld' -import crypto from 'crypto' -import axios from 'axios' +import typer from 'media-typer' +import web from 'web-did-resolver' + import { Utils } from '../utils/common-functions' import { AppConst, AppMessages } from '../utils/constants' -import { check, validationResult } from 'express-validator' -import * as he from 'he' -import web from 'web-did-resolver' -import { Resolver } from 'did-resolver' -import typer from 'media-typer' -import { createHash } from 'crypto' + // import { PublisherService } from '../utils/service/publisher.service' export const privateRoute = express.Router() @@ -72,12 +75,13 @@ privateRoute.post( .trim() .escape() .matches(/^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/), - check('templateId').not().isEmpty().trim().escape().isIn([AppConst.LEGAL_PARTICIPANT, AppConst.SERVICE_OFFER]), + check('templateId').not().isEmpty().trim().escape().isIn([AppConst.LEGAL_PARTICIPANT, AppConst.SERVICE_OFFER, AppConst.RESOURCE_CREATION]), check('privateKeyUrl').not().isEmpty().trim().escape(), check('data').isObject(), async (req: Request, res: Response): Promise => { try { const { domain, tenant, templateId, privateKeyUrl } = req.body + if (templateId === AppConst.LEGAL_PARTICIPANT) { await check('data.legalName').not().isEmpty().trim().escape().run(req) await check('data.legalRegistrationType').not().isEmpty().trim().escape().run(req) @@ -100,6 +104,12 @@ privateRoute.post( .run(req) await check('data.accessType').not().isEmpty().trim().escape().isIn(AppConst.ACCESS_TYPES).run(req) await check('data.requestType').not().isEmpty().trim().escape().isIn(AppConst.REQUEST_TYPES).run(req) + await check('data.resource.name').not().optional().isEmpty().trim().escape().run(req) + await check('data.resource.description').optional().not().isEmpty().trim().escape().run(req) + await check('data.resource.containsPII').optional().not().isEmpty().trim().escape().run(req) + await check('data.resource.policy').optional().not().isEmpty().trim().escape().run(req) + await check('data.resource.license').optional().not().isEmpty().trim().escape().run(req) + await check('data.resource.copyrightOwnedBy').optional().not().isEmpty().trim().escape().run(req) } const errors = validationResult(req) if (!errors.isEmpty()) { @@ -112,42 +122,47 @@ privateRoute.post( const didId = tenant ? `did:web:${domain}:${tenant}` : `did:web:${domain}` const participantURL = tenant ? `https://${domain}/${tenant}/participant.json` : `https://${domain}/.well-known/participant.json` let selfDescription: any = null + if (templateId === AppConst.LEGAL_PARTICIPANT) { const { legalName, legalRegistrationType, legalRegistrationNumber, headquarterAddress, legalAddress } = req.body.data - selfDescription = Utils.generateLegalPerson(participantURL, didId, legalName, legalRegistrationType, legalRegistrationNumber, headquarterAddress, legalAddress) + const legalRegistrationNumberVCUrl = participantURL + "#1" + selfDescription = Utils.generateLegalPerson(participantURL, didId, legalName, headquarterAddress, legalAddress, legalRegistrationNumberVCUrl) + const regVC = await Utils.generateRegistrationNumber(axios, didId, legalRegistrationType, legalRegistrationNumber, legalRegistrationNumberVCUrl) + const tandcsURL = participantURL + "#2" + const termsVC = await Utils.generateTermsAndConditions(axios, didId, tandcsURL) + selfDescription['verifiableCredential'].push(regVC, termsVC) } else if (templateId === AppConst.SERVICE_OFFER) { const data = JSON.parse(he.decode(JSON.stringify(req.body.data))) + const serviceComplianceUrl = tenant ? `https://${domain}/${tenant}/${data.fileName}` : `https://${domain}/.well-known/${data.fileName}` - selfDescription = Utils.generateServiceOffer(participantURL, didId, serviceComplianceUrl, data) + if (data.resource) { + const resourceComplianceUrl = serviceComplianceUrl + '#1' + selfDescription = Utils.generateServiceOffer(participantURL, didId, serviceComplianceUrl, data, data.resource, resourceComplianceUrl) + } else { + selfDescription = Utils.generateServiceOffer(participantURL, didId, serviceComplianceUrl, data) + } const { selfDescriptionCredential } = (await axios.get(participantURL)).data - selfDescription.verifiableCredential.push(selfDescriptionCredential.verifiableCredential[0]) + + for (let index = 0; index < selfDescriptionCredential.verifiableCredential.length; index++) { + const vc = selfDescriptionCredential.verifiableCredential[index] + selfDescription.verifiableCredential.push(vc) + } } else { res.status(422).json({ error: `Type Not Supported`, message: AppMessages.DID_VALIDATION }) } - const canonizedSD = await Utils.normalize( - jsonld, - // eslint-disable-next-line - selfDescription['verifiableCredential'][0] - ) - const hash = Utils.sha256(crypto, canonizedSD) - console.log(`📈 Hashed canonized SD ${hash}`) - - const privateKey = (await axios.get(he.decode(privateKeyUrl))).data as string - // const privateKey = process.env.PRIVATE_KEY as string - const proof = await Utils.createProof(jose, didId, AppConst.RSA_ALGO, hash, privateKey) - console.log(proof ? '🔒 SD signed successfully' : '❌ SD signing failed') - const x5uURL = tenant ? `https://${domain}/${tenant}/x509CertificateChain.pem` : `https://${domain}/.well-known/x509CertificateChain.pem` - const certificate = (await axios.get(x5uURL)).data as string - const publicKeyJwk = await Utils.generatePublicJWK(jose, AppConst.RSA_ALGO, certificate, x5uURL) - - const verificationResult = await Utils.verify(jose, proof.jws.replace('..', `.${hash}.`), AppConst.RSA_ALGO, publicKeyJwk, hash) - console.log(verificationResult ? '✅ Verification successful' : '❌ Verification failed') - - selfDescription['verifiableCredential'][0].proof = proof + for (let index = 0; index < selfDescription['verifiableCredential'].length; index++) { + const vc = selfDescription['verifiableCredential'][index] + if (!selfDescription['verifiableCredential'][index].hasOwnProperty('proof')) { + const proof = await Utils.generateProof(jsonld, he, axios, jose, crypto, vc, privateKeyUrl, didId, domain, tenant, AppConst.RSA_ALGO) + selfDescription['verifiableCredential'][index].proof = proof + } + } + console.log(JSON.stringify(selfDescription)) const complianceCredential = (await axios.post(process.env.COMPLIANCE_SERVICE as string, selfDescription)).data + // const complianceCredential = {} console.log(complianceCredential ? '🔒 SD signed successfully (compliance service)' : '❌ SD signing failed (compliance service)') // await publisherService.publishVP(complianceCredential); @@ -155,7 +170,7 @@ privateRoute.post( selfDescriptionCredential: selfDescription, complianceCredential: complianceCredential } - + Utils.CESCompliance(axios, complianceCredential) res.status(200).json({ data: { verifiableCredential: completeSd }, message: AppMessages.VP_SUCCESS @@ -165,7 +180,7 @@ privateRoute.post( if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx - console.log(error.response.data) + console.log(JSON.stringify(error.response.data)) console.log(error.response.status) console.log(error.response.headers) } else if (error.request) { @@ -214,10 +229,9 @@ privateRoute.post( subjectDid, issuerDid, credentialOffer?.legalName, - credentialOffer?.legalRegistrationType, - credentialOffer?.legalRegistrationNumber, credentialOffer?.headquarterAddress, - credentialOffer?.legalAddress + credentialOffer?.legalAddress, + '' ) } // normalise @@ -231,6 +245,7 @@ privateRoute.post( // retrieve private key const privateKey = (await axios.get(he.decode(privateKeyUrl))).data as string // const privateKey = process.env.PRIVATE_KEY as string + // create proof const proof = await Utils.createProof(jose, issuerDid, AppConst.RSA_ALGO, hash, privateKey) // attach proof to vc @@ -396,7 +411,7 @@ privateRoute.post( case AppConst.VERIFY_POLICIES[1]: //policy2 console.log(`Executing ${policy} policy...`) - let gxComplianceCheck = await verifyGxCompliance(credentialContent, res) + const gxComplianceCheck = await verifyGxCompliance(credentialContent, res) responseObj.gxCompliance = gxComplianceCheck break @@ -486,7 +501,11 @@ async function verification(credentialContent: any, proof: any, res: Response, c const certificates = (await axios.get(x5u)).data as string if (checkSSLwithRegistry) { // signature check against Gaia-x registry - const registryRes = await Utils.validateSslFromRegistry(certificates, axios) + const registryRes = await Utils.validateSslFromRegistryWithUri(x5u, axios) + if (!registryRes) { + throw new Error('Certificate validation failed') + } + if (!registryRes) { res.status(400).json({ error: `Certificates validation Failed`, @@ -559,3 +578,273 @@ async function verifyGxCompliance(credentialContent: any, res: Response) { return signVerify && integrityCheck } + +privateRoute.post( + '/get/trust-index', + check('participant_json_url').not().isEmpty().trim(), + check('so_json_url').not().isEmpty().trim(), + async (req: Request, res: Response): Promise => { + try { + const { participant_json_url: participantUrl, so_json_url: soUrl } = req.body + let veracityResult + try { + veracityResult = await calcVeracity(participantUrl) + } catch (error) { + res.status(500).json({ + error: 'Error', + message: AppMessages.PARTICIPANT_DID_FETCH_FAILED + }) + return + } + const { veracity, certificateDetails } = veracityResult + let transparency = 1 + try { + transparency = await calcTansperency(soUrl) + console.log('transparency :-', transparency) + } catch (error) { + res.status(500).json({ + error: 'Error', + message: AppMessages.SO_SD_FETCH_FAILED + }) + return + } + + const trustIndex = calcTrustIndex(veracity, transparency) + console.log('trustIndex :-', trustIndex) + + res.status(200).json({ + message: 'Success', + data: { + veracity, + transparency, + trustIndex, + certificateDetails + } + }) + } catch (error) { + console.log(error) + res.status(500).json({ + error: (error as Error).message, + message: AppMessages.PARTICIPANT_VC_FOUND_FAILED + }) + } + } +) + +privateRoute.post( + '/label-level', + check('privateKeyUrl').not().isEmpty().trim().escape(), + check('issuer').not().isEmpty().trim().escape(), + check('verificationMethod').not().isEmpty().trim().escape(), + check('vcs.labelLevel').isObject(), + async (req: Request, res: Response): Promise => { + try { + let { privateKeyUrl } = req.body + const { + verificationMethod, + issuer: issuerDID, + vcs: { labelLevel } + } = req.body + // Get DID document of issuer from issuer DID + const ddo = await Utils.getDDOfromDID(issuerDID, resolver) + if (!ddo) { + console.error(__filename, 'LabelLevel', `❌ DDO not found for given did: '${issuerDID}' in proof`) + res.status(400).json({ + error: `DDO not found for given did: '${issuerDID}' in proof`, + message: AppMessages.VP_FAILED + }) + return + } + + const { credentialSubject: labelLevelCS } = labelLevel + if (!labelLevelCS) { + console.error(__filename, 'LabelLevel', 'labelLevelCS') + res.status(400).json({ + error: AppMessages.VP_FAILED, + message: AppMessages.VP_FAILED + }) + return + } + + // Calculate LabelLevel + const labelLevelResult = await Utils.calcLabelLevel(labelLevelCS) + if (labelLevelResult === '') { + console.error(__filename, 'LabelLevel', 'labelLevelResult') + res.status(400).json({ + error: AppMessages.VP_FAILED, + message: AppMessages.VP_FAILED + }) + return + } + labelLevelCS['gx:labelLevel'] = labelLevelResult + console.debug(__filename, 'LabelLevel', '🔒 labelLevel calculated') + + // Extract certificate url from did document + const { x5u } = await Utils.getPublicKeys(ddo.didDocument) + if (!x5u || x5u == '') { + console.error(__filename, 'LabelLevel', 'x5u') + res.status(400).json({ + error: 'x5u', + message: AppMessages.VP_FAILED + }) + return + } + + // Decrypt private key(received in request) from base64 to raw string + const privateKey = (await axios.get(he.decode(privateKeyUrl))).data as string + // const privateKey = process.env.PRIVATE_KEY as string + // Sign service offering self description with private key(received in request) + const proof = await Utils.addProof(jsonld, axios, jose, crypto, labelLevel, privateKey, verificationMethod, AppConst.RSA_ALGO, x5u) + labelLevel.proof = proof + + const completeSD = { + selfDescriptionCredential: labelLevel, + complianceCredential: {} + } + res.status(200).json({ + data: completeSD, + message: AppMessages.VP_SUCCESS + }) + } catch (error) { + console.error(__filename, 'LabelLevel', `❌ ${AppMessages.VP_FAILED}`) + res.status(500).json({ + error: (error as Error).message, + message: AppMessages.VP_FAILED + }) + } + } +) + +const calcVeracity = async (participantUrl: any) => { + // get the json document of participant + let veracity = 1 + let certificateDetails = null + try { + const participantJson = (await axios.get(participantUrl)).data + if (participantJson && participantJson.verifiableCredential.length) { + const participantVC = participantJson.verifiableCredential[0] + const { + id: holderDID, + proof: { verificationMethod: participantVM } + } = participantVC + console.log(`holderDID :-${holderDID} holderDID :- ${participantVM}`) + + const ddo = await Utils.getDDOfromDID(holderDID, resolver) + if (!ddo) { + console.log(`❌ DDO not found for given did: '${holderDID}' in proof`) + return { veracity, certificateDetails } + } + const { + didDocument: { verificationMethod: verificationMethodArray } + } = ddo + + for (const verificationMethod of verificationMethodArray) { + if (verificationMethod.id === participantVM && verificationMethod.type === 'JsonWebKey2020') { + const x5u = ddo.didDocument.verificationMethod[0].publicKeyJwk.x5u + + // get the SSL certificates from x5u url + const certificates = (await axios.get(x5u)).data as string + // console.log('certificates :- ', certificates) + + const certArray = certificates.match(/-----BEGIN CERTIFICATE-----[\s\S]*?-----END CERTIFICATE-----/g) + let keypairDepth = 1 + if (certArray?.length) { + keypairDepth = certArray?.length + } + + // getting object of a PEM encoded X509 Certificate. + const certificate = new X509Certificate(certificates) + certificateDetails = parseCertificate(certificate) + + veracity = +(1 / keypairDepth).toFixed(2) //veracity = 1 / sum(len(keychain)) + break + } + console.log(`❌ Participant proof verification method and did verification method id not matched`) + } + } else { + console.log(`❌ Verifiable Credential array not found in participant vc`) + } + } catch (error) { + console.error(`❌ Invalid participant vc url :- error \n`, error) + } + return { veracity, certificateDetails } +} + +/* + Formula: count(properties) / count(mandatoryproperties) + Provided By Mandatory (gx-service-offering:providedBy) + Aggregation Of Mandatory (gx-service-offering:aggreationOf) + Terms and Conditions Mandatory (gx-service-offering:termsAndConditions) + Policy Mandatory (gx-service-offering:policy) + Data Account Export Mandatory (gx-service-offering:dataExport) + Name Optional (gx-service-offering:name) + Depends On Optional (gx-service-offering:dependsOn) + Data Protection Regime Optional (gx-service-offering:dataProtectionRegime) +*/ +const calcTansperency = async (soUrl: any) => { + const optionalProps = ['gx-service-offering:name', 'gx-service-offering:dependsOn', 'gx-service-offering:dataProtectionRegime'] + const totalMandatoryProps = 5 + let availOptProps = 0 + try { + // get the json document of service offering + const { + selfDescriptionCredential: { credentialSubject } + } = (await axios.get(soUrl)).data + + for (let index = 0; index < optionalProps.length; index++) { + if (credentialSubject[optionalProps[index]]) { + availOptProps++ + } + } + const tansperency = (totalMandatoryProps + availOptProps) / totalMandatoryProps + return tansperency + } catch (error) { + return 0 + } +} + +const calcTrustIndex = (veracity: number, transparency: number) => { + const trustIndex = (veracity + transparency) / 2 + return trustIndex +} + +const parseCertificate = (certificate: X509Certificate) => { + const issuerFieldsString = certificate.issuer + const issuerFieldsArray = issuerFieldsString.split('\n') + + const extractFieldValue = (fieldArray: string[], fieldName: string) => { + const field = fieldArray.find((line: any) => line.startsWith(`${fieldName}=`)) + if (field) { + return field.slice(fieldName.length + 1) + } + return null + } + // Extract individual fields from the subject string + const subjectFieldsString = certificate.subject + const subjectFieldsArray = subjectFieldsString.split('\n') + const certificateDetails = { + validFrom: certificate.validFrom, + validTo: certificate.validTo, + subject: { + jurisdictionCountry: extractFieldValue(subjectFieldsArray, 'jurisdictionC'), + jurisdictionSate: extractFieldValue(subjectFieldsArray, 'jurisdictionST'), + jurisdictionLocality: extractFieldValue(subjectFieldsArray, 'jurisdictionL'), + businessCategory: extractFieldValue(subjectFieldsArray, 'businessCategory'), + serialNumber: extractFieldValue(subjectFieldsArray, 'serialNumber'), + country: extractFieldValue(subjectFieldsArray, 'C'), + state: extractFieldValue(subjectFieldsArray, 'ST'), + locality: extractFieldValue(subjectFieldsArray, 'L'), + organization: extractFieldValue(subjectFieldsArray, 'O'), + commonName: extractFieldValue(subjectFieldsArray, 'CN') + }, + issuer: { + commonName: extractFieldValue(issuerFieldsArray, 'CN'), + organization: extractFieldValue(issuerFieldsArray, 'O'), + organizationalUnit: extractFieldValue(issuerFieldsArray, 'OU'), + locality: extractFieldValue(issuerFieldsArray, 'L'), + state: extractFieldValue(issuerFieldsArray, 'ST'), + country: extractFieldValue(issuerFieldsArray, 'C') + } + } + return certificateDetails +} diff --git a/src/swagger.json b/src/swagger.json index e6e72a4..fe67b05 100644 --- a/src/swagger.json +++ b/src/swagger.json @@ -4,7 +4,6 @@ "title": "REST API for SmartSense Gaia-X Signer Tool", "version": "1.0.0" }, - "schemes": ["http"], "servers": [{ "url": "http://localhost:8000/" }], "paths": { "/createWebDID": { @@ -70,19 +69,160 @@ "examples": { "LegalParticipant": { "value": { - "domain": "dev.smartproof.in", - "tenant": "smart", + "domain": "greenworld.proofsense.in", "templateId": "LegalParticipant", "privateKeyUrl": "https://example.com", "data": { - "legalName": "Smart Proof", - "legalRegistrationType": "taxID", - "legalRegistrationNumber": "0762747721", + "legalName": "Green World", + "legalRegistrationType": "vatID", + "legalRegistrationNumber": "FR79537407926", "headquarterAddress": "BE-BRU", "legalAddress": "BE-BRU" } } }, + "ServiceOffering": { + "value": { + "domain": "dev.smartproof.in", + "tenant": "smart", + "templateId": "ServiceOffering", + "privateKeyUrl": "https://example.com", + "data": { + "name": "AWS S3 Service", + "fileName": "s3Service.json", + "description": "Amazon S3 bucket is a public cloud storage resource.", + "policyUrl": "https://aws.amazon.com/privacy/?nc1=f_pr", + "termsAndConditionsUrl": "https://aws.amazon.com/service-terms/", + "termsAndConditionsHash": "4f598faf41fb29a44d63ec413730d55fabd2d249f1976fee1c8eec8003d7f539", + "requestType": "API", + "accessType": "digital", + "formatType": "application/json" + } + } + }, + "ServiceOfferingWithResource": { + "value": { + "domain": "smart99.proofsense.in", + "templateId": "ServiceOffering", + "privateKeyUrl": "https://example.com", + "data": { + "name": "AWS S3 Service", + "fileName": "s3Service.json", + "description": "Amazon S3 bucket is a public cloud storage resource.", + "policyUrl": "https://aws.amazon.com/privacy/?nc1=f_pr", + "termsAndConditionsUrl": "https://aws.amazon.com/service-terms/", + "termsAndConditionsHash": "4f598faf41fb29a44d63ec413730d55fabd2d249f1976fee1c8eec8003d7f539", + "requestType": "API", + "accessType": "digital", + "formatType": "application/json", + "resource": { + "name": "CES Data Resource", + "description": "Contains GX compliant credentials", + "containsPII": false, + "policy": "default: allow", + "license": "EPL-2.0", + "copyrightOwnedBy": "original owner" + } + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessSchemaResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorSchemaResponse" + } + } + } + } + } + } + }, + + "/v1/onBoardToGaiaX": { + "post": { + "summary": "On board to Gaia-X v1", + "description": "Generate Legal Person and Service Offer Credentials", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OnBoardGaiaXSchemaBody" + }, + "examples": { + "LegalParticipant": { + "value": { + "templateId": "LegalParticipant", + "privateKeyUrl": "https://example.com", + "vcs": [ + { + "@context": ["https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/participant"], + "type": "gx:legalRegistrationNumber", + "id": "did:web:gaia-x.eu:legalRegistrationNumber.json", + "gx:vatID": "FR79537407926" + }, + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/jws-2020/v1", + "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "type": ["VerifiableCredential"], + "id": "did:web:dev.smartproof.in", + "issuer": "did:web:dev.smartproof.in", + "issuanceDate": "2023-07-12T08:58:07.859Z", + "credentialSubject": { + "type": "gx:LegalParticipant", + "gx:legalName": "Gaia-X European Association for Data and Cloud AISBL", + "gx:legalRegistrationNumber": { + "id": "https://gaia-x.eu/legalRegistrationNumberVC.json" + }, + "gx:headquarterAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "gx:legalAddress": { + "gx:countrySubdivisionCode": "BE-BRU" + }, + "id": "https://dev.smartproof.in/.well-known/participant.json" + } + }, + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/jws-2020/v1", + "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "type": ["VerifiableCredential"], + "id": "did:web:dev.smartproof.in", + "issuer": "did:web:dev.smartproof.in", + "issuanceDate": "2023-07-12T09:11:30.604Z", + "credentialSubject": { + "@context": "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#", + "type": "gx:GaiaXTermsAndConditions", + "id": "https://dev.smartproof.in/.well-known/did.json", + "gx:termsAndConditions": "The PARTICIPANT signing the Self-Description agrees as follows:\n- to update its descriptions about any changes, be it technical, organizational, or legal - especially but not limited to contractual in regards to the indicated attributes present in the descriptions.\n\nThe keypair used to sign Verifiable Credentials will be revoked where Gaia-X Association becomes aware of any inaccurate statements in regards to the claims which result in a non-compliance with the Trust Framework and policy rules defined in the Policy Rules and Labelling Document (PRLD)." + } + } + ] + } + }, "ServiceOffering": { "value": { "domain": "dev.smartproof.in", @@ -461,9 +601,9 @@ "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..IIhb0E7Zhnh6bj9vuBaKoX2UaR9ZB248HwBPq-1RilmJmHzUQR19556b6XdosXl4WNJ8f0GBnACbUSETYsUOVcEe91YAhqQSF7RYl7-flGmtDsbpKtWR8XXCW7WCBQCQ2-LY0l7bvOy1_92YqiL60JQeo-6HAGb7xXwaREfLI2DD5kRLb7dCd3_UXtIRo9_qTawH4BWCxWT8QjUKcBregNd0derLvyaxGEokJacpQs3jUsFMlqfbpxYhmBpBYP6A3I_Wyzu5CtQNSM7FkKbsR8dsbd0jAU1MuMp4JO4TAIdZ1AgjbyF5vQr6ZSsvISRG1V_GVkNlqhaZVpiHwEQGBw" } } - } - }, - "message": "VP created successfully." + }, + "message": "VP created successfully." + } } } } @@ -732,6 +872,650 @@ } } } + }, + "/label-level": { + "post": { + "summary": "Label level calculation", + "description": "Calculate label level value", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ComplineLLSchemaBody" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessSchemaResponse" + }, + "examples": { + "serviceOffering": { + "summary": "Sign Service offering SD from compliance service", + "value": { + "data": { + "selfDescriptionCredential": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/jws-2020/v1", + "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "type": ["VerifiableCredential"], + "id": "https://wizard.lab.gaia-x.eu/api/credentials/2d37wbGvQzbAQ84yRouh2m2vBKkN8s5AfH9Q75HZRCUQmJW7yAVSNKzjJj6gcjE2mDNDUHCichXWdMH3S2c8AaDLm3kXmf5R8DFPWTYo5iRYkn8kvgU3AjMXc2qTbhuMHCpucKGgT1ZMkcHUygZkt11iD3T8VJNKYwsdk4MGoZwdqoFUuTKVcsXVTBA4ofD1Dtqzjavyng5WUpvJf4gRyfGkMvYYuHCgay8TK8Dayt6Rhcs3r2d1gRCg2UV419S9CpWZGwKQNEXdYbaB2eTiNbQ83KMd4mj1oSJgF7LLDZLJtKJbhwLzR3x35QUqEGevRxnRDKoPdHrEZN7r9TVAmvr9rt7Xq8eB4zGMTza59hisEAUaHsmWQNaVDorqFyZgN5bXswMK1irVQ5SVR9osCCRrKUKkntxfakjmSqapPfveMP39vkgTXfEhsfLUZXGwFcpgLpWxWRn1QLnJY11BVymS7DyaSvbSKotNFQxyV6vghfM2Jetw1mLxU5qsQqDYnDYJjPZQSmkwxjX3yenPVCz6N2ox83tj9AuuQrzg5p2iukNdunDd2QCsHaMEtTq9JVLzXtWs2eZbPkxCBEQwoKTGGVhKu5yxZjCtQGc?vcid=tsandcsVC", + "issuer": "did:web:wizard.lab.gaia-x.eu:api:credentials:2d37wbGvQzbAQ84yRouh2m2vBKkN8s5AfH9Q75HZRCUQmJW7yAVSNKzjJj6gcjE2mDNDUHCichXWdMH3S2c8AaDLm3kXmf5R8DFPWTYo5iRYkn8kvgU3AjMXc2qTbhuMHCpucKGgT1ZMkcHUygZkt11iD3T8VJNKYwsdk4MGoZwdqoFUuTKVcsXVTBA4ofD1Dtqzjavyng5WUpvJf4gRyfGkMvYYuHCgay8TK8Dayt6Rhcs3r2d1gRCg2UV419S9CpWZGwKQNEXdYbaB2eTiNbQ83KMd4mj1oSJgF7LLDZLJtKJbhwLzR3x35QUqEGevRxnRDKoPdHrEZN7r9TVAmvr9rt7Xq8eB4zGMTza59hisEAUaHsmWQNaVDorqFyZgN5bXswMK1irVQ5SVR9osCCRrKUKkntxfakjmSqapPfveMP39vkgTXfEhsfLUZXGwFcpgLpWxWRn1QLnJY11BVymS7DyaSvbSKotNFQxyV6vghfM2Jetw1mLxU5qsQqDYnDYJjPZQSmkwxjX3yenPVCz6N2ox83tj9AuuQrzg5p2iukNdunDd2QCsHaMEtTq9JVLzXtWs2eZbPkxCBEQwoKTGGVhKu5yxZjCtQGc", + "issuanceDate": "2023-07-12T09:11:30.604Z", + "credentialSubject": { + "@context": "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#", + "type": "gx:ServiceOfferingLabel", + "id": "https://wizard-api.smart-x.smartsenselabs.com/12081064-8878-477e-8092-564a240c69e2/service-offering-label-level.json", + "gx:assignedTo": "https://wizard-api.smart-x.smartsenselabs.com/12081064-8878-477e-8092-564a240c69e2/service_hYfy.json", + "gx:criteria": { + "P1.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.1.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.8": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.9": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.10": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.3.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.3.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.3.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.8": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.9": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.10": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.11": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.12": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.13": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.14": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.15": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.16": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.17": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.18": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.19": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.20": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P4.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P4.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.2.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + } + }, + "gx:labelLevel": "L1" + }, + "proof": { + "type": "JsonWebSignature2020", + "created": "2023-09-06T10:28:46.799Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:web:casio34.smart-x.smartsenselabs.com", + "jws": "eyJhbGciOiJQUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..Ir0jfauSoibBKhNLj_hXqz2LwmpjqipEG4P2HAnPrwBX2uy4MgRc4xP7srZ-WvplKdhR1gqHTn4phxZiZAQbxjcTv2X_6H5Cc60BOhkeYFceMwt3u3s39aQjd5ZXINusbzqZcFIqL-3YVrrta0gZ_Hc5fjLJpyYkAo_wULNcRdoFyy8zdZgRRrONQY_9CMQSijevHESseyhxtd3tfveMhmvBcOvhYXhKyj4gW6Q9y3wq-XrDxD8-7pfWovDAbnJoCJJ62dwk3YgeC4jOgoNufYmYspuuBOGiUo7wdE-m0F9HjNCPL_E-QayieXp51oGC--W6S_pY-s1o-wyulTbRVg" + } + }, + "complianceCredential": {} + }, + "message": "VP created successfully." + } + } + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorSchemaResponse" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorSchemaResponse" + } + } + } + } + } + } } }, "components": { @@ -790,11 +1574,11 @@ "properties": { "domain": { "type": "string", - "example": "dev.smartproof.in" + "example": "gaiaxapi.proofsense.in" }, "tenant": { "type": "string", - "example": "smart" + "example": "Hella" }, "templateId": { "type": "string", @@ -814,11 +1598,11 @@ "properties": { "issuerDid": { "type": "string", - "example": "did:web:dev.smartproof.in" + "example": "did:web:gaiaxapi.proofsense.in:Hella" }, "subjectDid": { "type": "string", - "example": "did:web:dev.smartproof.in" + "example": "did:web:gaiaxapi.proofsense.in:Hella" }, "templateId": { @@ -835,15 +1619,15 @@ "properties": { "legalName": { "type": "string", - "example": "Smart Proof" + "example": "Hella" }, "legalRegistrationType": { "type": "string", - "example": "taxID" + "example": "vatID" }, "legalRegistrationNumber": { "type": "string", - "example": "0762747721" + "example": "FR79537407926" }, "headquarterAddress": { "type": "string", @@ -965,6 +1749,605 @@ } } } + }, + "ComplineLLSchemaBody": { + "required": ["privateKeyUrl", "issuer", "verificationMethod", "vcs"], + "properties": { + "privateKeyUrl": { + "type": "string", + "example": "https://example.com" + }, + "issuer": { + "type": "string", + "example": "did:web:casio34.smart-x.smartsenselabs.com" + }, + "verificationMethod": { + "type": "string", + "example": "did:web:casio34.smart-x.smartsenselabs.com" + }, + "vcs": { + "type": "object", + "example": { + "labelLevel": { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/security/suites/jws-2020/v1", + "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#" + ], + "type": ["VerifiableCredential"], + "id": "https://wizard.lab.gaia-x.eu/api/credentials/2d37wbGvQzbAQ84yRouh2m2vBKkN8s5AfH9Q75HZRCUQmJW7yAVSNKzjJj6gcjE2mDNDUHCichXWdMH3S2c8AaDLm3kXmf5R8DFPWTYo5iRYkn8kvgU3AjMXc2qTbhuMHCpucKGgT1ZMkcHUygZkt11iD3T8VJNKYwsdk4MGoZwdqoFUuTKVcsXVTBA4ofD1Dtqzjavyng5WUpvJf4gRyfGkMvYYuHCgay8TK8Dayt6Rhcs3r2d1gRCg2UV419S9CpWZGwKQNEXdYbaB2eTiNbQ83KMd4mj1oSJgF7LLDZLJtKJbhwLzR3x35QUqEGevRxnRDKoPdHrEZN7r9TVAmvr9rt7Xq8eB4zGMTza59hisEAUaHsmWQNaVDorqFyZgN5bXswMK1irVQ5SVR9osCCRrKUKkntxfakjmSqapPfveMP39vkgTXfEhsfLUZXGwFcpgLpWxWRn1QLnJY11BVymS7DyaSvbSKotNFQxyV6vghfM2Jetw1mLxU5qsQqDYnDYJjPZQSmkwxjX3yenPVCz6N2ox83tj9AuuQrzg5p2iukNdunDd2QCsHaMEtTq9JVLzXtWs2eZbPkxCBEQwoKTGGVhKu5yxZjCtQGc?vcid=tsandcsVC", + "issuer": "did:web:wizard.lab.gaia-x.eu:api:credentials:2d37wbGvQzbAQ84yRouh2m2vBKkN8s5AfH9Q75HZRCUQmJW7yAVSNKzjJj6gcjE2mDNDUHCichXWdMH3S2c8AaDLm3kXmf5R8DFPWTYo5iRYkn8kvgU3AjMXc2qTbhuMHCpucKGgT1ZMkcHUygZkt11iD3T8VJNKYwsdk4MGoZwdqoFUuTKVcsXVTBA4ofD1Dtqzjavyng5WUpvJf4gRyfGkMvYYuHCgay8TK8Dayt6Rhcs3r2d1gRCg2UV419S9CpWZGwKQNEXdYbaB2eTiNbQ83KMd4mj1oSJgF7LLDZLJtKJbhwLzR3x35QUqEGevRxnRDKoPdHrEZN7r9TVAmvr9rt7Xq8eB4zGMTza59hisEAUaHsmWQNaVDorqFyZgN5bXswMK1irVQ5SVR9osCCRrKUKkntxfakjmSqapPfveMP39vkgTXfEhsfLUZXGwFcpgLpWxWRn1QLnJY11BVymS7DyaSvbSKotNFQxyV6vghfM2Jetw1mLxU5qsQqDYnDYJjPZQSmkwxjX3yenPVCz6N2ox83tj9AuuQrzg5p2iukNdunDd2QCsHaMEtTq9JVLzXtWs2eZbPkxCBEQwoKTGGVhKu5yxZjCtQGc", + "issuanceDate": "2023-07-12T09:11:30.604Z", + "credentialSubject": { + "@context": "https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#", + "type": "gx:ServiceOfferingLabel", + "id": "https://wizard-api.smart-x.smartsenselabs.com/12081064-8878-477e-8092-564a240c69e2/service-offering-label-level.json", + "gx:assignedTo": "https://wizard-api.smart-x.smartsenselabs.com/12081064-8878-477e-8092-564a240c69e2/service_hYfy.json", + "gx:criteria": { + "P1.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.1.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.8": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.9": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.2.10": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P1.3.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.2.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.3.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.3.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P2.3.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.8": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.9": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.10": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.11": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.12": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.13": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.14": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.15": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.16": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.17": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.18": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.19": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P3.1.20": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P4.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P4.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.2": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.3": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.4": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.5": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.6": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.1.7": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + }, + "P5.2.1": { + "evidence": { + "website": "", + "pdf": {}, + "vc": {} + }, + "response": "Confirm", + "reason": "" + } + } + } + } + } + } + } } } } diff --git a/src/utils/common-functions.ts b/src/utils/common-functions.ts index 6a820db..2e089c7 100644 --- a/src/utils/common-functions.ts +++ b/src/utils/common-functions.ts @@ -1,4 +1,6 @@ -import { Service, DidDocument } from '../interface/interface' +import { DidDocument, Service } from '../interface/interface' +import { AppMessages, LABEL_LEVEL_RULE, W3C_CONTEXT } from './constants' + namespace CommonFunctions { export class Utils { generateDID(didId: string, publicKeyJwk: any, services: Service[]): unknown { @@ -14,7 +16,7 @@ namespace CommonFunctions { publicKeyJwk: publicKeyJwk } ], - assertionMethod: [`${didId}#JWK2020-RSA`] + assertionMethod: [`${didId}`] } if (services) { for (let index = 0; index < services.length; index++) { @@ -25,28 +27,32 @@ namespace CommonFunctions { service['id'] = `${didId}#${services[index].type.toLocaleLowerCase()}` did.service?.push(service) } + } else { + did['service'] = [ + { + id: `${didId}#credentialregistry`, + serviceEndpoint: 'https://wizard-api.dev.smart-x.smartsenselabs.com/public/policy/evaluate', + type: 'CredentialRegistry' + } + ] } // const data = JSON.stringify(did, null, 2); return did } - generateLegalPerson( - participantURL: string, - didId: string, - legalName: string, - legalRegistrationType: string, - legalRegistrationNumber: string, - headquarterAddress: string, - legalAddress: string - ): object { + generateLegalPerson(participantURL: string, didId: string, legalName: string, headquarterAddress: string, legalAddress: string, legalRegistrationNumberVCUrl: string): object { const selfDescription = { '@context': 'https://www.w3.org/2018/credentials/v1', type: ['VerifiablePresentation'], verifiableCredential: [ { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#'], + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/suites/jws-2020/v1', + 'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#' + ], type: ['VerifiableCredential'], - id: didId, + id: participantURL, issuer: didId, issuanceDate: new Date().toISOString(), credentialSubject: { @@ -54,15 +60,14 @@ namespace CommonFunctions { type: 'gx:LegalParticipant', 'gx:legalName': legalName, 'gx:legalRegistrationNumber': { - [`gx:${legalRegistrationType}`]: legalRegistrationNumber + id: legalRegistrationNumberVCUrl }, 'gx:headquarterAddress': { 'gx:countrySubdivisionCode': headquarterAddress }, 'gx:legalAddress': { 'gx:countrySubdivisionCode': legalAddress - }, - 'gx-terms-and-conditions:gaiaxTermsAndConditions': '70c1d713215f95191a11d38fe2341faed27d19e083917bc8732ca4fea4976700' + } } } ] @@ -70,43 +75,143 @@ namespace CommonFunctions { return selfDescription } - generateServiceOffer(participantURL: string, didId: string, serviceComplianceUrl: string, data: any): object { - const { serviceName, description, policyUrl, termsAndConditionsUrl, termsAndConditionsHash, formatType, accessType, requestType } = data - const selfDescription = { + async generateTermsAndConditions(axios: any, didId: string, tandcsURL: string) { + // const { text } = (await axios.get(`${process.env.REGISTRY_TRUST_ANCHOR_URL as string}/termsAndConditions`)).data + const verifiableCredential = { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://w3id.org/security/suites/jws-2020/v1', + 'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#' + ], + type: ['VerifiableCredential'], + issuanceDate: new Date().toISOString(), + credentialSubject: { + '@context': 'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#', + type: 'gx:GaiaXTermsAndConditions', + // 'gx:termsAndConditions': text, + 'gx:termsAndConditions': + 'The PARTICIPANT signing the Self-Description agrees as follows:\n- to update its descriptions about any changes, be it technical, organizational, or legal - especially but not limited to contractual in regards to the indicated attributes present in the descriptions.\n\nThe keypair used to sign Verifiable Credentials will be revoked where Gaia-X Association becomes aware of any inaccurate statements in regards to the claims which result in a non-compliance with the Trust Framework and policy rules defined in the Policy Rules and Labelling Document (PRLD).', + id: tandcsURL + }, + issuer: didId, + id: tandcsURL + } + return verifiableCredential + } + + async generateRegistrationNumber(axios: any, didId: string, legalRegistrationType: string, legalRegistrationNumber: string, legalRegistrationNumberVCUrl: string) { + try { + const request = { + '@context': ['https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/participant'], + type: 'gx:legalRegistrationNumber', + id: legalRegistrationNumberVCUrl, + [`gx:${legalRegistrationType}`]: legalRegistrationNumber + } + legalRegistrationNumberVCUrl = legalRegistrationNumberVCUrl.replace('#', '%23') + console.log(request) + const regVC = await axios.post(`${process.env.REGISTRATION_SERVICE as string}?vcid=${legalRegistrationNumberVCUrl}`, request) + // console.log(JSON.stringify(regVC.data)) + return regVC.data + } catch (error) { + console.log(`❌ RegistrationNumber failed | Error: ${error}`) + return null + } + } + + generateServiceOffer(participantURL: string, didId: string, serviceComplianceUrl: string, data: any, resource?: any, resourceComplianceUrl?: string): object { + const { name, description, policyUrl, termsAndConditionsUrl, termsAndConditionsHash, formatType, accessType, requestType } = data + + const selfDescription: any = { '@context': 'https://www.w3.org/2018/credentials/v1', type: ['VerifiablePresentation'], verifiableCredential: [ { '@context': ['https://www.w3.org/2018/credentials/v1', 'https://registry.lab.gaia-x.eu/development/api/trusted-shape-registry/v1/shapes/jsonld/trustframework#'], type: ['VerifiableCredential'], - id: didId, + id: serviceComplianceUrl, issuer: didId, issuanceDate: new Date().toISOString(), - credentialSubject: { - id: serviceComplianceUrl, - 'gx:name': serviceName, - 'gx:description': description, - type: 'gx:ServiceOffering', - 'gx:providedBy': { - id: participantURL - }, - 'gx:policy': policyUrl, - 'gx:termsAndConditions': { - 'gx:URL': termsAndConditionsUrl, - 'gx:hash': termsAndConditionsHash - }, - 'gx:dataAccountExport': { - 'gx:requestType': requestType, - 'gx:accessType': accessType, - 'gx:formatType': formatType + credentialSubject: [ + { + id: serviceComplianceUrl, + 'gx:name': name, + 'gx:description': description, + type: 'gx:ServiceOffering', + 'gx:providedBy': { + id: participantURL + }, + 'gx:policy': [policyUrl], + 'gx:termsAndConditions': { + 'gx:URL': termsAndConditionsUrl, + 'gx:hash': termsAndConditionsHash + }, + 'gx:dataAccountExport': { + 'gx:requestType': requestType, + 'gx:accessType': accessType, + 'gx:formatType': [formatType] + } } - } + ] } ] } + + if (resource) { + selfDescription.verifiableCredential[0].credentialSubject.push({ + id: resourceComplianceUrl, + type: 'gx:DataResource', + 'gx:name': resource.name, + 'gx:description': resource.description, + 'gx:containsPII': resource.containsPII == 'true', + 'gx:policy': resource.policy, + 'gx:license': resource.license, + 'gx:copyrightOwnedBy': resource.copyrightOwnedBy, + 'gx:producedBy': { + id: participantURL + }, + 'gx:exposedThrough': { + id: serviceComplianceUrl + } + }) + } return selfDescription } + async generateProof( + jsonld: any, + he: any, + axios: any, + jose: any, + crypto: any, + verifiableCredential: any, + privateKeyUrl: string, + didId: string, + domain: string, + tenant: string, + rsaAlso: string + ) { + const canonizedSD = await this.normalize( + jsonld, + // eslint-disable-next-line + verifiableCredential + ) + const hash = this.sha256(crypto, canonizedSD) + console.log(`📈 Hashed canonized SD ${hash}`) + + const privateKey = (await axios.get(he.decode(privateKeyUrl))).data as string + // const privateKey = process.env.PRIVATE_KEY as string + const proof = await this.createProof(jose, didId, rsaAlso, hash, privateKey) + console.log(proof ? '🔒 SD signed successfully' : '❌ SD signing failed') + const x5uURL = tenant ? `https://${domain}/${tenant}/x509CertificateChain.pem` : `https://${domain}/.well-known/x509CertificateChain.pem` + console.log(x5uURL) + const certificate = (await axios.get(x5uURL)).data as string + const publicKeyJwk = await this.generatePublicJWK(jose, rsaAlso, certificate, x5uURL) + + const verificationResult = await this.verify(jose, proof.jws.replace('..', `.${hash}.`), rsaAlso, publicKeyJwk, hash) + console.log(verificationResult ? '✅ Verification successful' : '❌ Verification failed') + return proof + } + async generatePublicJWK(jose: any, algorithm: string, certificate: string, x5uURL: string): Promise { const x509 = await jose.importX509(certificate, algorithm) const publicKeyJwk = await jose.exportJWK(x509) @@ -117,10 +222,27 @@ namespace CommonFunctions { async normalize(jsonld: any, payload: object) { try { - const canonized = await jsonld.canonize(payload, { - algorithm: 'URDNA2015', - format: 'application/n-quads' - }) + const nodeDocumentLoader = jsonld.documentLoaders.node() + const customLoader = async (url: string) => { + if (url in W3C_CONTEXT) { + return { + contextUrl: null, // this is for a context via a link header + document: W3C_CONTEXT[url], // this is the actual document that was loaded + documentUrl: url // this is the actual context URL after redirects + } + } + // call the default documentLoader + return nodeDocumentLoader(url) + } + jsonld.documentLoader = customLoader + const canonized = await jsonld.canonize( + payload, + { + algorithm: 'URDNA2015', + format: 'application/n-quads' + }, + { nodeDocumentLoader: customLoader } + ) if (canonized === '') throw new Error('Canonized SD is empty') return canonized } catch (error) { @@ -200,7 +322,7 @@ namespace CommonFunctions { async validateSslFromRegistry(certificates: any, axios: any) { try { certificates = certificates.replace(/\n/gm, '') || undefined - const registryRes = await axios.post(process.env.REGISTRY_TRUST_ANCHOR_URL as string, { certs: certificates }) + const registryRes = await axios.post(`${process.env.REGISTRY_TRUST_ANCHOR_URL as string}/trustAnchor/chain`, { certs: certificates }) return registryRes.status === 200 } catch (error) { console.log(`❌ Validation from registry failed for certificates | error: ${error}`) @@ -208,6 +330,16 @@ namespace CommonFunctions { } } + async validateSslFromRegistryWithUri(uri: string, axios: any) { + try { + const registryRes = await axios.post(`${process.env.REGISTRY_TRUST_ANCHOR_URL as string}/trustAnchor/chain/file`, { uri: uri }) + const result = registryRes?.data?.result + return result + } catch (error) { + console.error(__filename, 'validateSslFromRegistryWithUri', `❌ Validation from registry failed for certificates | error: ${error}`, '') + return false + } + } async comparePubKeys(certificates: string, publicKeyJwk: any, jose: any) { try { const pk = await jose.importJWK(publicKeyJwk) @@ -222,6 +354,98 @@ namespace CommonFunctions { return false } } + + async getPublicKeys(ddo: any) { + const { verificationMethod, id } = ddo + const jwk = verificationMethod.find((method: any) => method.id.startsWith(id)) + if (!jwk) throw new Error(`verificationMethod ${verificationMethod} not found in did document`) + + const { publicKeyJwk } = jwk + if (!publicKeyJwk) throw new Error(`Could not load JWK for ${verificationMethod}`) + + const { x5u } = publicKeyJwk + if (!publicKeyJwk.x5u) throw new Error(`The x5u parameter is expected to be set in the JWK for ${verificationMethod}`) + + return { x5u, publicKeyJwk } + } + + async addProof(jsonld: any, axios: any, jose: any, crypto: any, verifiableCredential: any, privateKey: string, verificationMethod: string, rsaAlso: string, x5uURL: string) { + const canonizedSD = await this.normalize( + jsonld, + // eslint-disable-next-line + verifiableCredential + ) + const hash = this.sha256(crypto, canonizedSD) + console.info(__filename, 'addProof', `📈 Hashed canonized SD ${hash}`, '') + + const proof = await this.createProof(jose, verificationMethod, rsaAlso, hash, privateKey) + console.info(__filename, 'addProof', proof ? '🔒 SD signed successfully' : '❌ SD signing failed', x5uURL) + + const certificate = (await axios.get(x5uURL)).data as string + const publicKeyJwk = await this.generatePublicJWK(jose, rsaAlso, certificate, x5uURL) + + const verificationResult = await this.verify(jose, proof.jws.replace('..', `.${hash}.`), rsaAlso, publicKeyJwk, hash) + console.info(__filename, 'addProof', verificationResult ? '✅ Verification successful' : '❌ Verification failed', '') + return proof + } + + /** + * @formula trust_index = mean(veracity, transparency) + * @dev takes the veracity and transparency as input and calculates trust index + * @param veracity Veracity value + * @param transparency Transparency value + * @returns number - Trust index value + */ + calcLabelLevel = (credentialSubject: any) => { + let resultLabelLevel = '' + + // Label level response by user + const criteria = credentialSubject['gx:criteria'] + + // Constant Rules + for (const labelLevel in LABEL_LEVEL_RULE) { + // Rule of Specific label level + const levelRules = LABEL_LEVEL_RULE[labelLevel] + // Iterate level rules + for (const rulePoint of levelRules) { + // eslint-disable-next-line no-prototype-builtins + if (criteria.hasOwnProperty(rulePoint)) { + const { response } = criteria[rulePoint] + // Loop will break if any single response found not confirmed and will return last label level + if (response !== 'Confirm') { + return resultLabelLevel + } + } else { + console.error(__filename, 'LabelLevel', AppMessages.LABEL_LEVEL_CALC_FAILED_INVALID_KEY + rulePoint, '') + throw new Error(AppMessages.LABEL_LEVEL_CALC_FAILED_INVALID_KEY + rulePoint) + } + } + resultLabelLevel = labelLevel + } + + return resultLabelLevel + } + + CESCompliance = async (axios: any, complianceCred: any) => { + try { + const reqBody = { + specversion: '1.0', + type: 'eu.gaia-x.credential', + source: '/mycontext', + time: complianceCred.issuanceDate, + datacontenttype: 'application/json', + data: complianceCred + } + const response = await axios.post(process.env.CES_COMPLIANCE + '/credentials-events', reqBody) + if (response.status == 201) { + console.log('successfully created compliance') + } else { + console.error('❌ error in getting compliance') + } + } catch (err) { + console.error('❌ error in getting compliance', err) + } + } } } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 564333d..bc5bfbc 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -1,7 +1,10 @@ +import W3C_Credential from '../assets/credential.json' +import W3C_JWS from '../assets/jws-2020.json' export class AppConst { static readonly RSA_ALGO = 'PS256' static readonly LEGAL_PARTICIPANT = 'LegalParticipant' static readonly SERVICE_OFFER = 'ServiceOffering' + static readonly RESOURCE_CREATION = 'ServiceOfferingWithResource' static readonly FLATTEN_ENCRYPT_ALGORITHM = 'RSA-OAEP-256' static readonly FLATTEN_ENCRYPT_ENCODING = 'A256GCM' static readonly VERIFY_POLICIES = ['checkSignature', 'gxCompliance'] @@ -27,4 +30,71 @@ export class AppMessages { static readonly CERT_VALIDATION_FAILED = 'Certificates verification failed against the Gaia-x Registry' static readonly PUB_KEY_MISMATCH = 'Public Key from did and SSL certificates do not match' static readonly ONLY_JWS2020 = 'Only JsonWebSignature2020 is supported' + static readonly PARTICIPANT_DID_FETCH_FAILED = 'Participant DID fetching failed' + static readonly PARTICIPANT_VC_FOUND_FAILED = 'Participant VC not found' + static readonly SO_SD_FETCH_FAILED = 'Service offering self description fetching failed' + static readonly LABEL_LEVEL_CALC_FAILED_INVALID_KEY = 'Rule point key not found in criteria json - ' +} + +export const LABEL_LEVEL_RULE: any = { + BC: [ + 'P1.1.1', + 'P1.1.3', + 'P1.1.4', + 'P1.2.1', + 'P1.2.2', + 'P1.2.3', + 'P1.2.4', + 'P1.2.5', + 'P1.2.6', + 'P1.2.7', + 'P1.2.8', + 'P1.2.9', + 'P1.2.10', + 'P1.3.1', + 'P1.3.2', + 'P1.3.3', + 'P1.3.4', + 'P1.3.5', + 'P2.1.2', + 'P2.1.3', + 'P2.2.1', + 'P2.2.2', + 'P2.2.3', + 'P2.2.5', + 'P2.2.6', + 'P2.2.7', + 'P2.3.2', + 'P2.3.3', + 'P3.1.1', + 'P3.1.2', + 'P3.1.3', + 'P3.1.4', + 'P3.1.5', + 'P3.1.6', + 'P3.1.7', + 'P3.1.8', + 'P3.1.9', + 'P3.1.10', + 'P3.1.11', + 'P3.1.12', + 'P3.1.13', + 'P3.1.14', + 'P3.1.15', + 'P3.1.16', + 'P3.1.17', + 'P3.1.18', + 'P3.1.19', + 'P3.1.20', + 'P4.1.1', + 'P4.1.2', + 'P5.2.1' + ], + L1: ['P1.1.2', 'P2.1.1', 'P2.2.4', 'P2.3.1'] + // L2: ['P5.1.1'], + // L3: ['P5.1.2', 'P5.1.3', 'P5.1.4', 'P5.1.5', 'P5.1.6', 'P5.1.7'] +} +export const W3C_CONTEXT: Record = { + 'https://www.w3.org/2018/credentials/v1': W3C_Credential, + 'https://w3id.org/security/suites/jws-2020/v1': W3C_JWS }