UNPKG

@sphereon/gx-compliance-client

Version:

<!--suppress HtmlDeprecatedAttribute --> <h1 align="center"> <br> <a href="https://www.sphereon.com"><img src="https://sphereon.com/content/themes/sphereon/assets/img/logo.svg" alt="Sphereon" width="400"></a> <br>Gaia-X Compliance client (Typescript

293 lines (292 loc) 12.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsonWebSignature = void 0; // @ts-ignore const jsonld_1 = __importDefault(require("jsonld")); const web_crypto_key_pair_1 = require("@transmute/web-crypto-key-pair"); const JsonWebKeyWithRSASupport_1 = require("./JsonWebKeyWithRSASupport"); const u8a = __importStar(require("uint8arrays")); const security_context_1 = __importDefault(require("@transmute/security-context")); /** * WARNING: * * This suite is made specifically to be interoperable with Gaia-X. Do not use this suite for other purposes, as the current Gaia-X implementation contains multiple errors and does not conform to JsonWebSignature2020. * If you do need regular JsonWebSignature2020 support, please configure the SphereonWebSignature2020 class when setting up the agent */ const sha256 = (data) => __awaiter(void 0, void 0, void 0, function* () { return Buffer.from(yield web_crypto_key_pair_1.subtle.digest('SHA-256', Buffer.from(data))); }); class JsonWebSignature { constructor(options = {}) { this.useNativeCanonize = false; this.type = 'JsonWebSignature2020'; this.date = options.date; if (options.key) { this.key = options.key; this.verificationMethod = this.key.id; } if (options.verifier) { this.verifier = options.verifier; } } ensureSuiteContext({ document }) { const contextUrl = security_context_1.default.constants.JSON_WEB_SIGNATURE_2020_V1_URL; if (document['@context'] === contextUrl || (Array.isArray(document['@context']) && document['@context'].includes(contextUrl))) { // document already includes the required context return; } // throw new TypeError(`The document to be signed must contain this suite's @context, ` + `"${contextUrl}".`) } canonize(input, { documentLoader }) { return __awaiter(this, void 0, void 0, function* () { return yield jsonld_1.default.canonize(input, { algorithm: 'URDNA2015', format: 'application/n-quads', documentLoader: documentLoader, }); }); } sign({ verifyData, proof }) { var _a; return __awaiter(this, void 0, void 0, function* () { try { const signer = yield ((_a = this.key) === null || _a === void 0 ? void 0 : _a.signer()); const detachedJws = yield signer.sign({ data: verifyData }); proof.jws = detachedJws; return proof; } catch (e) { console.warn('Failed to sign.'); throw e; } }); } createProof({ document, purpose, documentLoader, expansionMap, compactProof }) { return __awaiter(this, void 0, void 0, function* () { let proof; const context = document['@context']; if (this.proof) { // use proof JSON-LD document passed to API proof = yield jsonld_1.default.compact(this.proof, context, { documentLoader, skipExpansion: true, expansionMap, compactToRelative: false, }); } else { // create proof JSON-LD document proof = { '@context': context, }; } // ensure proof type is set proof.type = this.type; // set default `now` date if not given in `proof` or `options` let date = this.date; if (proof.created === undefined && date === undefined) { date = new Date(); } // ensure date is in string format if (date !== undefined && typeof date !== 'string') { date = new Date(date).toISOString(); date = date.substr(0, date.length - 5) + 'Z'; } // add API overrides if (date !== undefined) { proof.created = date; } // `verificationMethod` is for newer suites, `creator` for legacy if (this.verificationMethod !== undefined) { proof.verificationMethod = this.verificationMethod; } // allow purpose to update the proof; the `proof` is in the // SECURITY_CONTEXT_URL `@context` -- therefore the `purpose` must // ensure any added fields are also represented in that same `@context` proof = yield purpose.update(proof, { document, suite: this, documentLoader, expansionMap, }); // create data to sign const verifyData = yield this.createVerifyData({ document, proof, documentLoader, expansionMap, compactProof, }); // sign data proof = yield this.sign({ verifyData, document, proof, documentLoader, expansionMap, }); delete proof['@context']; return proof; }); } getVerificationMethod({ proof, documentLoader, instance }) { return __awaiter(this, void 0, void 0, function* () { let { verificationMethod } = proof; if (!verificationMethod) { // backwards compatibility support for `creator` const { creator } = proof; verificationMethod = creator; } if (typeof verificationMethod === 'object') { verificationMethod = verificationMethod.id; } if (!verificationMethod) { throw new Error('No "verificationMethod" or "creator" found in proof.'); } // Note: `expansionMap` is intentionally not passed; we can safely drop // properties here and must allow for it const { document } = yield documentLoader(verificationMethod); const framed = yield jsonld_1.default.frame(verificationMethod, { '@context': document['@context'], '@embed': '@always', id: verificationMethod, }, { // use the cache of the document we just resolved when framing documentLoader: (iri) => { if (iri.startsWith(document.id)) { return { documentUrl: iri, document, }; } return documentLoader(iri); }, }); if (!instance) { if (!framed || !framed.controller) { throw new Error(`Verification method ${verificationMethod} not found.`); } return framed; } return JsonWebKeyWithRSASupport_1.JsonWebKey.from(document, { signer: false, verifier: this.verifier }); }); } verifySignature({ verifyData, verificationMethod, proof }) { var _a; return __awaiter(this, void 0, void 0, function* () { if (verificationMethod.publicKey) { const key = verificationMethod.publicKey; const signature = proof.jws.split('.')[2]; const headerString = proof.jws.split('.')[0]; const dataBuffer = u8a.fromString(verifyData, 'utf-8'); const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), dataBuffer]); return yield web_crypto_key_pair_1.subtle.verify({ name: ((_a = key.algorithm) === null || _a === void 0 ? void 0 : _a.name) ? key.algorithm.name : 'RSASSA-PKCS1-V1_5', hash: 'SHA-256', }, key, u8a.fromString(signature, 'base64url'), messageBuffer); } const verifier = yield verificationMethod.verifier(); return verifier.verify({ data: verifyData, signature: proof.jws.replace('..', `.${verifyData}.`) }); }); } verifyProof({ proof, document, purpose, documentLoader, expansionMap, compactProof }) { return __awaiter(this, void 0, void 0, function* () { try { // create data to verify delete document.proof; const verifyData = yield this.createVerifyData({ document, proof, documentLoader, expansionMap, compactProof, }); // fetch verification method const verificationMethod = yield this.getVerificationMethod({ proof, document, documentLoader, expansionMap, instance: true, // this means we get a key pair class instance, not just json. }); // verify signature on data const verified = yield this.verifySignature({ verifyData, verificationMethod, document, proof, documentLoader, expansionMap, }); if (!verified) { throw new Error('Invalid signature.'); } // ensure proof was performed for a valid purpose const purposeResult = yield purpose.validate(proof, { document, suite: this, verificationMethod, documentLoader, expansionMap, }); if (!purposeResult.valid) { throw purposeResult.error; } return { verified: true, purposeResult }; } catch (error) { return { verified: false, error }; } }); } createVerifyData({ document, documentLoader }) { return __awaiter(this, void 0, void 0, function* () { // concatenate hash of c14n proof options and hash of c14n document const c14nDocument = yield this.canonize(document, { documentLoader, }); return u8a.toString(yield sha256(c14nDocument), 'base16'); }); } matchProof({ proof }) { return __awaiter(this, void 0, void 0, function* () { return proof.type === 'JsonWebSignature2020'; }); } } exports.JsonWebSignature = JsonWebSignature;