vess-mdl
Version:
Parse and and validate MDOC CBOR encoded binaries according to ISO 18013-5.
249 lines • 19.6 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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _Document_issuerNameSpaces, _Document_deviceKeyInfo, _Document_validityInfo, _Document_digestAlgorithm;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Document = void 0;
const jose = __importStar(require("jose"));
const cose_kit_1 = require("cose-kit");
const utils_1 = require("../utils");
const cbor_1 = require("../../cbor");
const IssuerSignedItem_1 = require("../IssuerSignedItem");
const IssuerAuth_1 = __importDefault(require("./IssuerAuth"));
const IssuerSignedDocument_1 = require("./IssuerSignedDocument");
const DEFAULT_NS = 'org.iso.18013.5.1';
const addYears = (date, years) => {
const r = new Date(date.getTime());
r.setFullYear(date.getFullYear() + years);
return r;
};
/**
* Use this class when building new documents.
*
* This class allow you to build a document and sign it with the issuer's private key.
*/
class Document {
constructor(doc = 'org.iso.18013.5.1.mDL') {
_Document_issuerNameSpaces.set(this, {});
_Document_deviceKeyInfo.set(this, void 0);
_Document_validityInfo.set(this, {
signed: new Date(),
validFrom: new Date(),
validUntil: addYears(new Date(), 1),
});
_Document_digestAlgorithm.set(this, 'SHA-256');
this.docType = doc;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validateValues(values) {
// TODO
// validate required fields, no extra fields, data types, etc...
}
/**
* Add a namespace to an unsigned document.
*
* @param {string} namespace - The namespace to add.
* @param {Record<string, any>} values - The values to add to the namespace.
* @returns {Document} - The document
*/
addIssuerNameSpace(namespace, values) {
if (namespace === DEFAULT_NS) {
this.validateValues(values);
}
__classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace] = __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace] ?? [];
const addAttribute = (key, value) => {
let elementValue = value;
if (namespace === DEFAULT_NS) {
// the following namespace attributes must be a full-date as specified in RFC 3339
if (['birth_date', 'issue_date', 'expiry_date'].includes(key) && typeof value === 'string') {
elementValue = new cbor_1.DateOnly(value);
}
if (key === 'driving_privileges' && Array.isArray(value)) {
value.forEach((v, i) => {
if (typeof v.issue_date === 'string') {
elementValue[i].issue_date = new cbor_1.DateOnly(v.issue_date);
}
if (typeof v.expiry_date === 'string') {
elementValue[i].expiry_date = new cbor_1.DateOnly(v.expiry_date);
}
});
}
}
const digestID = __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace].length;
const issuerSignedItem = IssuerSignedItem_1.IssuerSignedItem.create(digestID, key, elementValue);
__classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace].push(issuerSignedItem);
};
for (const [key, value] of Object.entries(values)) {
addAttribute(key, value);
}
return this;
}
/**
* Get the values in a namespace.
*
* @param {string} namespace - The namespace to add.
* @returns {Record<string, any>} - The values in the namespace as an object
*/
getIssuerNameSpace(namespace) {
const nameSpace = __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")[namespace];
return Object.fromEntries(nameSpace.map((item) => [item.elementIdentifier, item.elementValue]));
}
/**
* Add the device public key which will be include in the issuer signature.
* The device public key could be in JWK format or as COSE_Key format.
*
* @param params
* @param {jose.JWK | Uint8Array} params.devicePublicKey - The device public key.
*/
addDeviceKeyInfo({ deviceKey }) {
const deviceKeyCOSEKey = deviceKey instanceof Uint8Array ?
deviceKey :
(0, cose_kit_1.COSEKeyFromJWK)(deviceKey);
const decodedCoseKey = (0, cbor_1.cborDecode)(deviceKeyCOSEKey);
__classPrivateFieldSet(this, _Document_deviceKeyInfo, {
deviceKey: decodedCoseKey,
}, "f");
return this;
}
/**
* Add validity info to the document that will be used in the issuer signature.
*
* @param info - the validity info
* @param {Date} [info.signed] - The date the document is signed. default: now
* @param {Date} [info.validFrom] - The date the document is valid from. default: signed
* @param {Date} [info.validUntil] - The date the document is valid until. default: signed + 1 year
* @param {Date} [info.expectedUpdate] - [Optional] The date the document is expected to be re-signed and potentially have its data updated.
* @returns
*/
addValidityInfo(info = {}) {
const signed = info.signed ?? new Date();
const validFrom = info.validFrom ?? signed;
const validUntil = info.validUntil ?? addYears(signed, 1);
__classPrivateFieldSet(this, _Document_validityInfo, {
signed,
validFrom,
validUntil,
}, "f");
if (info.expectedUpdate) {
__classPrivateFieldGet(this, _Document_validityInfo, "f").expectedUpdate = info.expectedUpdate;
}
return this;
}
/**
* Set the digest algorithm used for the value digests in the issuer signature.
*
* The default is SHA-256.
*
* @param {DigestAlgorithm} digestAlgorithm - The digest algorithm to use.
* @returns
*/
useDigestAlgorithm(digestAlgorithm) {
__classPrivateFieldSet(this, _Document_digestAlgorithm, digestAlgorithm, "f");
return this;
}
/**
* Generate the issuer signature for the document.
*
* @param {Object} params - The parameters object
* @param {jose.JWK | Uint8Array} params.issuerPrivateKey - The issuer's private key either in JWK format or COSE_KEY format as buffer.
* @param {string | Uint8Array | Array<string | Uint8Array>} params.issuerCertificate - The issuer's certificate in pem format, as a buffer, or an array.
* @param {SupportedAlgs} params.alg - The algorhitm used for the MSO signature.
* @param {string | Uint8Array} [params.kid] - The key id of the issuer's private key. default: issuerPrivateKey.kid
* @returns {Promise<IssuerSignedDoc>} - The signed document
*/
async sign(params) {
if (!__classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")) {
throw new Error('No namespaces added');
}
let issuerCertificateChain;
if (Array.isArray(params.issuerCertificate)) {
issuerCertificateChain = params.issuerCertificate.flatMap((cert) => (typeof cert === 'string' ? (0, utils_1.fromPEM)(cert) : [cert]));
}
else if (typeof params.issuerCertificate === 'string') {
issuerCertificateChain = (0, utils_1.fromPEM)(params.issuerCertificate);
}
else {
issuerCertificateChain = [params.issuerCertificate];
}
const issuerPrivateKeyJWK = params.issuerPrivateKey instanceof Uint8Array ?
(0, cose_kit_1.COSEKeyToJWK)(params.issuerPrivateKey) :
params.issuerPrivateKey;
const issuerPrivateKey = await jose.importJWK(issuerPrivateKeyJWK);
const valueDigests = new Map(await Promise.all(Object.entries(__classPrivateFieldGet(this, _Document_issuerNameSpaces, "f")).map(async ([namespace, items]) => {
const digestMap = new Map();
await Promise.all(items.map(async (item, index) => {
const hash = await item.calculateDigest(__classPrivateFieldGet(this, _Document_digestAlgorithm, "f"));
digestMap.set(index, new Uint8Array(hash));
}));
return [namespace, digestMap];
})));
const mso = {
version: '1.0',
digestAlgorithm: __classPrivateFieldGet(this, _Document_digestAlgorithm, "f"),
valueDigests,
deviceKeyInfo: __classPrivateFieldGet(this, _Document_deviceKeyInfo, "f"),
docType: this.docType,
validityInfo: __classPrivateFieldGet(this, _Document_validityInfo, "f"),
};
const payload = (0, cbor_1.cborEncode)(cbor_1.DataItem.fromData(mso));
const protectedHeader = { alg: params.alg };
const unprotectedHeader = {
kid: params.kid ?? issuerPrivateKeyJWK.kid,
x5chain: issuerCertificateChain.length === 1 ? issuerCertificateChain[0] : issuerCertificateChain,
};
const issuerAuth = await IssuerAuth_1.default.sign(protectedHeader, unprotectedHeader, payload, issuerPrivateKey);
const issuerSigned = {
issuerAuth,
nameSpaces: __classPrivateFieldGet(this, _Document_issuerNameSpaces, "f"),
};
return new IssuerSignedDocument_1.IssuerSignedDocument(this.docType, issuerSigned);
}
}
exports.Document = Document;
_Document_issuerNameSpaces = new WeakMap(), _Document_deviceKeyInfo = new WeakMap(), _Document_validityInfo = new WeakMap(), _Document_digestAlgorithm = new WeakMap();
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRG9jdW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbWRvYy9tb2RlbC9Eb2N1bWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSwyQ0FBNkI7QUFDN0IsdUNBQThGO0FBQzlGLG9DQUFtQztBQUNuQyxxQ0FBd0U7QUFDeEUsMERBQXVEO0FBQ3ZELDhEQUFzQztBQUV0QyxpRUFBOEQ7QUFFOUQsTUFBTSxVQUFVLEdBQUcsbUJBQW1CLENBQUM7QUFFdkMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxJQUFVLEVBQUUsS0FBYSxFQUFRLEVBQUU7SUFDbkQsTUFBTSxDQUFDLEdBQUcsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7SUFDbkMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEdBQUcsS0FBSyxDQUFDLENBQUM7SUFDMUMsT0FBTyxDQUFDLENBQUM7QUFDWCxDQUFDLENBQUM7QUFFRjs7OztHQUlHO0FBQ0gsTUFBYSxRQUFRO0lBV25CLFlBQVksTUFBZSx1QkFBdUI7UUFUbEQscUNBQXNDLEVBQUUsRUFBQztRQUN6QywwQ0FBOEI7UUFDOUIsaUNBQThCO1lBQzVCLE1BQU0sRUFBRSxJQUFJLElBQUksRUFBRTtZQUNsQixTQUFTLEVBQUUsSUFBSSxJQUFJLEVBQUU7WUFDckIsVUFBVSxFQUFFLFFBQVEsQ0FBQyxJQUFJLElBQUksRUFBRSxFQUFFLENBQUMsQ0FBQztTQUNwQyxFQUFDO1FBQ0Ysb0NBQW9DLFNBQVMsRUFBQztRQUc1QyxJQUFJLENBQUMsT0FBTyxHQUFHLEdBQUcsQ0FBQztJQUNyQixDQUFDO0lBRUQsNkRBQTZEO0lBQ3JELGNBQWMsQ0FBQyxNQUEyQjtRQUNoRCxPQUFPO1FBQ1AsZ0VBQWdFO0lBQ2xFLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFD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