@auth0/mdl
Version:
Parse and and validate MDOC CBOR encoded binaries according to ISO 18013-5.
232 lines • 18.2 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 __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} params.issuerCertificate - The issuer's certificate in pem format or as a buffer.
* @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');
}
const issuerPublicKeyBuffer = typeof params.issuerCertificate === 'string' ?
(0, utils_1.fromPEM)(params.issuerCertificate) :
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: [issuerPublicKeyBuffer],
};
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRG9jdW1lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbWRvYy9tb2RlbC9Eb2N1bWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLDJDQUE2QjtBQUM3Qix1Q0FBOEY7QUFDOUYsb0NBQW1DO0FBQ25DLHFDQUF3RTtBQUN4RSwwREFBdUQ7QUFDdkQsOERBQXNDO0FBRXRDLGlFQUE4RDtBQUU5RCxNQUFNLFVBQVUsR0FBRyxtQkFBbUIsQ0FBQztBQUV2QyxNQUFNLFFBQVEsR0FBRyxDQUFDLElBQVUsRUFBRSxLQUFhLEVBQVEsRUFBRTtJQUNuRCxNQUFNLENBQUMsR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztJQUNuQyxDQUFDLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsR0FBRyxLQUFLLENBQUMsQ0FBQztJQUMxQyxPQUFPLENBQUMsQ0FBQztBQUNYLENBQUMsQ0FBQztBQUVGOzs7O0dBSUc7QUFDSCxNQUFhLFFBQVE7SUFXbkIsWUFBWSxNQUFlLHVCQUF1QjtRQVRsRCxxQ0FBc0MsRUFBRSxFQUFDO1FBQ3pDLDBDQUE4QjtRQUM5QixpQ0FBOEI7WUFDNUIsTUFBTSxFQUFFLElBQUksSUFBSSxFQUFFO1lBQ2xCLFNBQVMsRUFBRSxJQUFJLElBQUksRUFBRTtZQUNyQixVQUFVLEVBQUUsUUFBUSxDQUFDLElBQUksSUFBSSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1NBQ3BDLEVBQUM7UUFDRixvQ0FBb0MsU0FBUyxFQUFDO1FBRzVDLElBQUksQ0FBQyxPQUFPLEdBQUcsR0FBRyxDQUFDO0lBQ3JCLENBQUM7SUFFRCw2REFBNkQ7SUFDckQsY0FBYyxDQUFDLE1BQTJCO1FBQ2hELE9BQU87UUFDUCxnRUFBZ0U7SUFDbEUsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNILGtCQUFrQixD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