@xevolab/jades
Version:
JAdES Digital Signatures compatible with the ETSI TS 119 182-1 Standard
236 lines (235 loc) • 11.4 kB
JavaScript
/*
* Author : Francesco
* Created at: 2024-06-29 21:20
* Edited by : Francesco
* Edited at : 2025-03-24 23:05
*
* Copyright (c) 2024 Xevolab S.R.L.
*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
import { validateProtected } from "../schemas/schemas.js";
import encodeHeaders from "../utils/encodeHeaders";
/**
* This class is used to manage the protected headers of a token, validating them and returning a
* set of methods to interact with them.
*/
var ProtectedHeaders = /** @class */ (function () {
function ProtectedHeaders(p) {
// Check the signed headers and assign them to the object
if (p && Object.keys(p).length > 0)
this.header = validateProtectedHeaders(p);
}
/**
* The addHeaders method is used to add headers to the protected headers.
*
* @param {object} p The headers to add.
*
* @return {void}
*/
ProtectedHeaders.prototype.addHeaders = function (p) {
this.header = validateProtectedHeaders(__assign(__assign({}, this.header), p));
};
/**
* The detach method is used to set the headers for a detached signature.
* This method is used to set the `sigD` header parameter.
*/
ProtectedHeaders.prototype.setDetached = function (sigD) {
this.header = validateProtectedHeaders(__assign(__assign({}, this.header), { sigD: sigD, cty: undefined }));
};
/**
* Get the object of the protected headers.
*
* @return {Object} The protected headers.
*/
ProtectedHeaders.prototype.getHeaders = function () {
if (!this.header)
throw new Error("Protected headers not set.");
return this.header;
};
/**
* Get the base64url string encoded version of the protected headers.
*
* @return {string} The protected headers in base64url string form.
*/
ProtectedHeaders.prototype.toString = function () {
if (!this.header)
throw new Error("Protected headers not set.");
return encodeHeaders(this.header);
};
return ProtectedHeaders;
}());
export default ProtectedHeaders;
;
function validateProtectedHeaders(_a) {
var _b = _a.alg, alg = _b === void 0 ? "RS256" : _b, _c = _a.cty, cty = _c === void 0 ? null : _c, _d = _a.kid, kid = _d === void 0 ? null : _d, _e = _a.x5u, x5u = _e === void 0 ? null : _e, _f = _a.x5c, x5c = _f === void 0 ? null : _f, _g = _a.x5tS256, x5tS256 = _g === void 0 ? null : _g, _h = _a.x5tO, x5tO = _h === void 0 ? null : _h, _j = _a.sigX5ts, sigX5ts = _j === void 0 ? null : _j,
// These properties are part of the standard but are not directly
// checked by the library. Using the `validate` function, you can
// check if the payload is valid or not against the official schema.
_k = _a.srCms,
// These properties are part of the standard but are not directly
// checked by the library. Using the `validate` function, you can
// check if the payload is valid or not against the official schema.
srCms = _k === void 0 ? null : _k, _l = _a.srAts, srAts = _l === void 0 ? null : _l, _m = _a.sigPl, sigPl = _m === void 0 ? null : _m, _o = _a.sigPId, sigPId = _o === void 0 ? null : _o, _p = _a.sigD, sigD = _p === void 0 ? null : _p, _q = _a.sigT, sigT = _q === void 0 ? null : _q, // = (new Date().toISOString()).slice(0, -5) + "Z",
// These properties are not yet supported by the library but could be
// in the future.
// Please, consider contributing to the project if you want to help.
_r = _a.adoTst, // = (new Date().toISOString()).slice(0, -5) + "Z",
// These properties are not yet supported by the library but could be
// in the future.
// Please, consider contributing to the project if you want to help.
adoTst = _r === void 0 ? null : _r;
/**
* `kid`
* ETSI TS 119 182-1 - Section 5.1.4
* └-> https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.4
*/
if (kid !== null && typeof kid !== "string")
throw new Error("Invalid kid; needs to be a string.");
if (typeof kid === "string" && !/^[a-zA-Z0-9-_]+$/g.test(kid))
throw new Error("Invalid kid; needs to be a base64 string.");
/**
* `x5u`
* ETSI TS 119 182-1 - Section 5.1.5
* └-> https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.5
*
* `x5u` must be a URI that refers to a resource for the X.509 public key certificate or
* certificate chain corresponding to the key used to digitally sign the JWS.
*/
if (x5u !== null && typeof x5u !== "string")
throw new Error("Invalid x5u; needs to be a string.");
if (x5u !== null && !/^(?:https?|ftp):\/\/[^\s/$.?#].[^\s]*$/g.test(x5u))
throw new Error("Invalid x5u; needs to be a valid URI.");
if (x5u !== null && x5c !== null)
throw new Error("Invalid x5u; needs to be null when x5c is not null.");
/**
* `x5c`
* ETSI TS 119 182-1 - Section 5.1.8
* └-> https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.6
*
* `x5c` must be a base64 encoded string of one or more DER-encoded X.509 certificates.
* The certificate chain must be ordered such that each certificate contains the public key of
* the subsequent certificate.
* The first certificate must correspond to the key used to digitally sign the JWS.
*/
if (x5c !== null && x5c.length === undefined)
throw new Error("Invalid x5c; needs to be an array.");
if (x5c !== null && x5c.length === 0)
throw new Error("Invalid x5c; needs to be an array with at least one element.");
if (x5c !== null && !x5c.every(function (t) { return typeof t === "string"; }))
throw new Error("Invalid x5c; needs to be an array of strings.");
if (x5c !== null && !x5c.every(function (t) { return /^[a-zA-Z0-9=/+]+$/g.test(t); }))
throw new Error("Invalid x5c; needs to be an array of base64 strings.");
/**
* `x5t#256`
* ETSI TS 119 182-1 - Section 5.1.7
* └-> https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.8
*
* `x5t#256` must be a base64url encoded SHA-256 thumbprint (a.k.a. digest) of the DER encoding
* of the X.509 certificate corresponding to the key used to digitally sign the JWS.
*/
if (x5tS256 !== null && typeof x5tS256 !== "string")
throw new Error("Invalid x5tS256; needs to be a string.");
if (x5tS256 !== null && !/^[a-zA-Z0-9-_]+$/g.test(x5tS256))
throw new Error("Invalid x5tS256; needs to be a base64url string.");
if (x5tS256 !== null && x5c !== null)
throw new Error("Invalid x5tS256; needs to be null when x5c is not null.");
/**
* `x5t#o`
* ETSI TS 119 182-1 - Section 5.2.2.2
*
* `x5t#o` must be an object with the following properties:
* - `digAlg` must be a string containing the digest algorithm used to compute the thumbprint.
* - `digVal` must be a base64url encoded thumbprint of the DER encoding of the X.509
* certificate corresponding to the key used to digitally sign the JWS.
*/
if (x5tO !== null && typeof x5tO !== "object")
throw new Error("Invalid x5tO; needs to be an object.");
if (x5tO !== null && !("digAlg" in x5tO))
throw new Error("Invalid x5tO; needs to have a property named `digAlg`.");
if (x5tO !== null && !("digVal" in x5tO))
throw new Error("Invalid x5tO; needs to have a property named `digVal`.");
if (x5tO !== null && typeof x5tO.digAlg !== "string")
throw new Error("Invalid x5tO; `digAlg` needs to be a string.");
if (x5tO !== null && typeof x5tO.digVal !== "string")
throw new Error("Invalid x5tO; `digVal` needs to be a string.");
if (x5tO !== null && !/^[a-zA-Z0-9-_]+$/g.test(x5tO.digVal))
throw new Error("Invalid x5tO; `digVal` needs to be a base64url string.");
/**
* `sigX5ts`
* ETSI TS 119 182-1 - Section 5.2.2.3
*
* `sigX5ts` must be an array of at least 2 objects with the same properties as `x5t#o` (see
* above).
* The first object must contain the thumbprint of the certificate used to sign the JWS.
* Each object may contain digest values computed with the algorithm SHA-256.
*/
if (sigX5ts !== null && sigX5ts.length === undefined)
throw new Error("Invalid sigX5ts; needs to be an array.");
if (sigX5ts !== null && sigX5ts.length < 2)
throw new Error("Invalid sigX5ts; needs to be an array with at least 2 elements.");
if (sigX5ts !== null && !sigX5ts.every(function (t) { return typeof t === "object"; }))
throw new Error("Invalid sigX5ts; needs to be an array of objects.");
// ETSI TS 119 182-1 - Section 5.1.7
// A JAdES signature shall have at least one of the following header parameters in its JWS
// Protected Header: x5t#S256, x5c, x5t#o, sigX5ts.
if (x5tS256 === null && x5c === null && x5tO === null && sigX5ts === null)
throw new Error("Invalid JAdES signature; needs to have at least one of the following header parameters in its JWS Protected Header: x5t#S256, x5c, x5t#o, sigX5ts.");
/**
* `sigD`
* ETSI TS 119 182-1 - Section 5.2.8
*
* `sigD` is an object referencing one or more detached data objects, meaning that this property
* is only present when the JWS Payload is detached.
* ⚠️ The `sigD` header parameter is not checked by this library.
*/
// if (sigD !== null && typeof sigD !== "object") throw new Error("Invalid sigD; needs to be an object.");
// if (sigD !== null && !detached) throw new Error("Invalid sigD; needs to be null when detached is false.");
// --> Preparing for the signature
// Creating the JOSE headers
var JOSE = {
alg: alg,
cty: cty,
kid: kid,
x5u: x5u,
x5c: x5c,
"x5t#S256": x5tS256,
"x5t#o": x5tO,
sigX5ts: sigX5ts,
sigD: sigD,
sigPl: sigPl,
sigPId: sigPId,
srCms: srCms,
srAts: srAts,
// Timestamp related headers
sigT: sigT,
adoTst: adoTst,
/**
* `b64`
* ETSI TS 119 182-1 - Section 5.1.10
* └-> https://datatracker.ietf.org/doc/html/rfc7797#section-3
*/
b64: true,
};
// Removing all null or undefined values from the JOSE headers
Object.keys(JOSE)
.forEach(function (key) { return (JOSE[key] === null || JOSE[key] === undefined) && delete JOSE[key]; });
// Calculating the `crit` header parameter
var critParameters = ["x5t#o", "sigX5ts", "sigT", "sigD", "sigPl", "sigPId", "srCms", "srAts", "adoTst", "b64"];
JOSE.crit = critParameters.filter(function (parameter) { return parameter in JOSE; });
// --> Checking against the schema
// Checking the JOSE headers against the schema
var validationResult = validateProtected(JOSE);
if (!validationResult)
throw new Error("Invalid JOSE headers; \n-> ".concat(validateProtected.errors.map(function (error, i) { return "".concat(i, ": ").concat(error.message); }).join(",\n-> ")));
return JOSE;
}