@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
JavaScript
;
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;