diff --git a/__tests__/issuing/deviceResponse.tests.ts b/__tests__/issuing/deviceResponse.tests.ts index 68924c4..6a7d4a5 100644 --- a/__tests__/issuing/deviceResponse.tests.ts +++ b/__tests__/issuing/deviceResponse.tests.ts @@ -1,6 +1,5 @@ -import { createHash, randomFillSync } from 'node:crypto'; +import { randomFillSync } from 'node:crypto'; import * as jose from 'jose'; -import { COSEKeyFromJWK } from 'cose-kit'; import { MDoc, Document, @@ -11,7 +10,6 @@ import { } from '../../src'; import { DEVICE_JWK, ISSUER_CERTIFICATE, ISSUER_PRIVATE_KEY_JWK, PRESENTATION_DEFINITION_1 } from './config'; import { DataItem, cborEncode } from '../../src/cbor'; -import COSEKeyToRAW from '../../src/cose/coseKey'; const { d, ...publicKeyJWK } = DEVICE_JWK as jose.JWK; @@ -20,6 +18,10 @@ describe('issuing a device response', () => { let parsedDocument: DeviceSignedDocument; let mdoc: MDoc; + const signed = new Date('2023-10-24T14:55:18Z'); + const validUntil = new Date(signed); + validUntil.setFullYear(signed.getFullYear() + 30); + beforeAll(async () => { const issuerPrivateKey = ISSUER_PRIVATE_KEY_JWK; @@ -31,7 +33,7 @@ describe('issuing a device response', () => { given_name: 'Ava', birth_date: '2007-03-25', issue_date: '2023-09-01', - expiry_date: '2028-09-31', + expiry_date: '2028-09-30', issuing_country: 'US', issuing_authority: 'NY DMV', document_number: '01-856-5050', @@ -39,8 +41,8 @@ describe('issuing a device response', () => { driving_privileges: [ { vehicle_category_code: 'C', - issue_date: '2023-09-01', - expiry_date: '2028-09-31', + issue_date: '2022-09-02', + expiry_date: '2027-09-20', }, ], un_distinguishing_sign: 'tbd-us.ny.dmv', @@ -59,8 +61,8 @@ describe('issuing a device response', () => { }) .useDigestAlgorithm('SHA-512') .addValidityInfo({ - signed: new Date('2023-10-24'), - validUntil: new Date('2050-10-24'), + signed, + validUntil, }) .addDeviceKeyInfo({ deviceKey: publicKeyJWK }) .sign({ @@ -133,9 +135,10 @@ describe('issuing a device response', () => { it('should contain the validity info', () => { const { validityInfo } = parsedDocument.issuerSigned.issuerAuth.decodedPayload; expect(validityInfo).toBeDefined(); - expect(validityInfo.signed).toEqual(new Date('2023-10-24')); - expect(validityInfo.validFrom).toEqual(new Date('2023-10-24')); - expect(validityInfo.validUntil).toEqual(new Date('2050-10-24')); + expect(validityInfo.signed).toEqual(signed); + expect(validityInfo.validFrom).toEqual(signed); + expect(validityInfo.validUntil).toEqual(validUntil); + expect(validityInfo.expectedUpdate).toBeUndefined(); }); it('should contain the device namespaces', () => { @@ -217,9 +220,10 @@ describe('issuing a device response', () => { it('should contain the validity info', () => { const { validityInfo } = parsedDocument.issuerSigned.issuerAuth.decodedPayload; expect(validityInfo).toBeDefined(); - expect(validityInfo.signed).toEqual(new Date('2023-10-24')); - expect(validityInfo.validFrom).toEqual(new Date('2023-10-24')); - expect(validityInfo.validUntil).toEqual(new Date('2050-10-24')); + expect(validityInfo.signed).toEqual(signed); + expect(validityInfo.validFrom).toEqual(signed); + expect(validityInfo.validUntil).toEqual(validUntil); + expect(validityInfo.expectedUpdate).toBeUndefined(); }); it('should contain the device namespaces', () => { diff --git a/__tests__/issuing/deviceResponseWithMac.tests.ts b/__tests__/issuing/deviceResponseWithMac.tests.ts index f2a5a01..da6baa8 100644 --- a/__tests__/issuing/deviceResponseWithMac.tests.ts +++ b/__tests__/issuing/deviceResponseWithMac.tests.ts @@ -22,6 +22,10 @@ describe('issuing a device response with MAC authentication', () => { let ephemeralPrivateKey: Uint8Array; let ephemeralPublicKey: Uint8Array; + const signed = new Date('2023-10-24T14:55:18Z'); + const validUntil = new Date(signed); + validUntil.setFullYear(signed.getFullYear() + 30); + beforeAll(async () => { const issuerPrivateKey = ISSUER_PRIVATE_KEY_JWK; @@ -33,7 +37,7 @@ describe('issuing a device response with MAC authentication', () => { given_name: 'Ava', birth_date: '2007-03-25', issue_date: '2023-09-01', - expiry_date: '2028-09-31', + expiry_date: '2028-09-30', issuing_country: 'US', issuing_authority: 'NY DMV', document_number: '01-856-5050', @@ -41,8 +45,8 @@ describe('issuing a device response with MAC authentication', () => { driving_privileges: [ { vehicle_category_code: 'C', - issue_date: '2023-09-01', - expiry_date: '2028-09-31', + issue_date: '2022-09-01', + expiry_date: '2027-09-30', }, ], un_distinguishing_sign: 'tbd-us.ny.dmv', @@ -61,8 +65,8 @@ describe('issuing a device response with MAC authentication', () => { }) .useDigestAlgorithm('SHA-512') .addValidityInfo({ - signed: new Date('2023-10-24'), - validUntil: new Date('2050-10-24'), + signed, + validUntil, }) .addDeviceKeyInfo({ deviceKey: publicKeyJWK }) .sign({ @@ -148,9 +152,10 @@ describe('issuing a device response with MAC authentication', () => { it('should contain the validity info', () => { const { validityInfo } = parsedDocument.issuerSigned.issuerAuth.decodedPayload; expect(validityInfo).toBeDefined(); - expect(validityInfo.signed).toEqual(new Date('2023-10-24')); - expect(validityInfo.validFrom).toEqual(new Date('2023-10-24')); - expect(validityInfo.validUntil).toEqual(new Date('2050-10-24')); + expect(validityInfo.signed).toEqual(signed); + expect(validityInfo.validFrom).toEqual(signed); + expect(validityInfo.validUntil).toEqual(validUntil); + expect(validityInfo.expectedUpdate).toBeUndefined(); }); it('should contain the device namespaces', () => { @@ -239,9 +244,10 @@ describe('issuing a device response with MAC authentication', () => { it('should contain the validity info', () => { const { validityInfo } = parsedDocument.issuerSigned.issuerAuth.decodedPayload; expect(validityInfo).toBeDefined(); - expect(validityInfo.signed).toEqual(new Date('2023-10-24')); - expect(validityInfo.validFrom).toEqual(new Date('2023-10-24')); - expect(validityInfo.validUntil).toEqual(new Date('2050-10-24')); + expect(validityInfo.signed).toEqual(signed); + expect(validityInfo.validFrom).toEqual(signed); + expect(validityInfo.validUntil).toEqual(validUntil); + expect(validityInfo.expectedUpdate).toBeUndefined(); }); it('should contain the device namespaces', () => { diff --git a/__tests__/issuing/issuingMDoc.tests.ts b/__tests__/issuing/issuingMDoc.tests.ts index 9597af1..39b430d 100644 --- a/__tests__/issuing/issuingMDoc.tests.ts +++ b/__tests__/issuing/issuingMDoc.tests.ts @@ -15,6 +15,12 @@ describe('issuing an MDOC', () => { let encoded: Uint8Array; let parsedDocument: IssuerSignedDocument; + const signed = new Date('2023-10-24T14:55:18Z'); + const validFrom = new Date(signed); + validFrom.setMinutes(signed.getMinutes() + 5); + const validUntil = new Date(signed); + validUntil.setFullYear(signed.getFullYear() + 30); + beforeAll(async () => { const issuerPrivateKey = ISSUER_PRIVATE_KEY_JWK; @@ -23,11 +29,30 @@ describe('issuing an MDOC', () => { family_name: 'Jones', given_name: 'Ava', birth_date: '2007-03-25', + issue_date: '2023-09-01', + expiry_date: '2028-09-30', + issuing_country: 'US', + issuing_authority: 'NY DMV', + document_number: '01-856-5050', + portrait: 'bstr', + driving_privileges: [ + { + vehicle_category_code: 'A', + issue_date: '2021-09-02', + expiry_date: '2026-09-20', + }, + { + vehicle_category_code: 'B', + issue_date: '2022-09-02', + expiry_date: '2027-09-20', + }, + ], }) .useDigestAlgorithm('SHA-512') .addValidityInfo({ - signed: new Date('2023-10-24'), - validUntil: new Date('2050-10-24'), + signed, + validFrom, + validUntil, }) .addDeviceKeyInfo({ deviceKey: publicKeyJWK }) .sign({ @@ -58,9 +83,10 @@ describe('issuing an MDOC', () => { it('should contain the validity info', () => { const { validityInfo } = parsedDocument.issuerSigned.issuerAuth.decodedPayload; expect(validityInfo).toBeDefined(); - expect(validityInfo.signed).toEqual(new Date('2023-10-24')); - expect(validityInfo.validFrom).toEqual(new Date('2023-10-24')); - expect(validityInfo.validUntil).toEqual(new Date('2050-10-24')); + expect(validityInfo.signed).toEqual(signed); + expect(validityInfo.validFrom).toEqual(validFrom); + expect(validityInfo.validUntil).toEqual(validUntil); + expect(validityInfo.expectedUpdate).toBeUndefined(); }); it('should use the correct digest alg', () => { @@ -84,9 +110,27 @@ describe('issuing an MDOC', () => { { "age_over_${currentAge}": true, "age_over_21": ${currentAge >= 21}, - "birth_date": "2007-03-25", + "birth_date": 2007-03-25T00:00:00.000Z, + "document_number": "01-856-5050", + "driving_privileges": [ + Map { + "vehicle_category_code" => "A", + "issue_date" => 2021-09-02T00:00:00.000Z, + "expiry_date" => 2026-09-20T00:00:00.000Z, + }, + Map { + "vehicle_category_code" => "B", + "issue_date" => 2022-09-02T00:00:00.000Z, + "expiry_date" => 2027-09-20T00:00:00.000Z, + }, + ], + "expiry_date": 2028-09-30T00:00:00.000Z, "family_name": "Jones", "given_name": "Ava", + "issue_date": 2023-09-01T00:00:00.000Z, + "issuing_authority": "NY DMV", + "issuing_country": "US", + "portrait": "bstr", } `); }); diff --git a/package-lock.json b/package-lock.json index 7125e34..fb34977 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@panva/hkdf": "^1.1.1", "@peculiar/x509": "^1.9.5", "buffer": "^6.0.3", - "cbor-x": "^1.5.4", + "cbor-x": "^1.5.9", "compare-versions": "^6.0.0", "cose-kit": "^1.7.1", "debug": "^4.3.4", @@ -722,72 +722,78 @@ "dev": true }, "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.1.1.tgz", - "integrity": "sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@cbor-extract/cbor-extract-darwin-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.1.1.tgz", - "integrity": "sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@cbor-extract/cbor-extract-linux-arm": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.1.1.tgz", - "integrity": "sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", "cpu": [ "arm" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@cbor-extract/cbor-extract-linux-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.1.1.tgz", - "integrity": "sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@cbor-extract/cbor-extract-linux-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.1.1.tgz", - "integrity": "sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@cbor-extract/cbor-extract-win32-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.1.1.tgz", - "integrity": "sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" @@ -3509,32 +3515,34 @@ ] }, "node_modules/cbor-extract": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.1.1.tgz", - "integrity": "sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", + "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { - "node-gyp-build-optional-packages": "5.0.3" + "node-gyp-build-optional-packages": "5.1.1" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@cbor-extract/cbor-extract-darwin-arm64": "2.1.1", - "@cbor-extract/cbor-extract-darwin-x64": "2.1.1", - "@cbor-extract/cbor-extract-linux-arm": "2.1.1", - "@cbor-extract/cbor-extract-linux-arm64": "2.1.1", - "@cbor-extract/cbor-extract-linux-x64": "2.1.1", - "@cbor-extract/cbor-extract-win32-x64": "2.1.1" + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", + "@cbor-extract/cbor-extract-linux-x64": "2.2.0", + "@cbor-extract/cbor-extract-win32-x64": "2.2.0" } }, "node_modules/cbor-x": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.5.4.tgz", - "integrity": "sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==", + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.5.9.tgz", + "integrity": "sha512-OEI5rEu3MeR0WWNUXuIGkxmbXVhABP+VtgAXzm48c9ulkrsvxshjjk94XSOGphyAKeNGLPfAxxzEtgQ6rEVpYQ==", + "license": "MIT", "optionalDependencies": { - "cbor-extract": "^2.1.1" + "cbor-extract": "^2.2.0" } }, "node_modules/chalk": { @@ -3865,6 +3873,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -6552,10 +6570,14 @@ "peer": true }, "node_modules/node-gyp-build-optional-packages": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz", - "integrity": "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "license": "MIT", "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", @@ -8867,39 +8889,39 @@ "dev": true }, "@cbor-extract/cbor-extract-darwin-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.1.1.tgz", - "integrity": "sha512-blVBy5MXz6m36Vx0DfLd7PChOQKEs8lK2bD1WJn/vVgG4FXZiZmZb2GECHFvVPA5T7OnODd9xZiL3nMCv6QUhA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", "optional": true }, "@cbor-extract/cbor-extract-darwin-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.1.1.tgz", - "integrity": "sha512-h6KFOzqk8jXTvkOftyRIWGrd7sKQzQv2jVdTL9nKSf3D2drCvQB/LHUxAOpPXo3pv2clDtKs3xnHalpEh3rDsw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", "optional": true }, "@cbor-extract/cbor-extract-linux-arm": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.1.1.tgz", - "integrity": "sha512-ds0uikdcIGUjPyraV4oJqyVE5gl/qYBpa/Wnh6l6xLE2lj/hwnjT2XcZCChdXwW/YFZ1LUHs6waoYN8PmK0nKQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", "optional": true }, "@cbor-extract/cbor-extract-linux-arm64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.1.1.tgz", - "integrity": "sha512-SxAaRcYf8S0QHaMc7gvRSiTSr7nUYMqbUdErBEu+HYA4Q6UNydx1VwFE68hGcp1qvxcy9yT5U7gA+a5XikfwSQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", "optional": true }, "@cbor-extract/cbor-extract-linux-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.1.1.tgz", - "integrity": "sha512-GVK+8fNIE9lJQHAlhOROYiI0Yd4bAZ4u++C2ZjlkS3YmO6hi+FUxe6Dqm+OKWTcMpL/l71N6CQAmaRcb4zyJuA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", "optional": true }, "@cbor-extract/cbor-extract-win32-x64": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.1.1.tgz", - "integrity": "sha512-2Niq1C41dCRIDeD8LddiH+mxGlO7HJ612Ll3D/E73ZWBmycued+8ghTr/Ho3CMOWPUEr08XtyBMVXAjqF+TcKw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", "optional": true }, "@commitlint/cli": { @@ -10905,26 +10927,26 @@ "dev": true }, "cbor-extract": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.1.1.tgz", - "integrity": "sha512-1UX977+L+zOJHsp0mWFG13GLwO6ucKgSmSW6JTl8B9GUvACvHeIVpFqhU92299Z6PfD09aTXDell5p+lp1rUFA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", + "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", "optional": true, "requires": { - "@cbor-extract/cbor-extract-darwin-arm64": "2.1.1", - "@cbor-extract/cbor-extract-darwin-x64": "2.1.1", - "@cbor-extract/cbor-extract-linux-arm": "2.1.1", - "@cbor-extract/cbor-extract-linux-arm64": "2.1.1", - "@cbor-extract/cbor-extract-linux-x64": "2.1.1", - "@cbor-extract/cbor-extract-win32-x64": "2.1.1", - "node-gyp-build-optional-packages": "5.0.3" + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", + "@cbor-extract/cbor-extract-linux-x64": "2.2.0", + "@cbor-extract/cbor-extract-win32-x64": "2.2.0", + "node-gyp-build-optional-packages": "5.1.1" } }, "cbor-x": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.5.4.tgz", - "integrity": "sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==", + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.5.9.tgz", + "integrity": "sha512-OEI5rEu3MeR0WWNUXuIGkxmbXVhABP+VtgAXzm48c9ulkrsvxshjjk94XSOGphyAKeNGLPfAxxzEtgQ6rEVpYQ==", "requires": { - "cbor-extract": "^2.1.1" + "cbor-extract": "^2.2.0" } }, "chalk": { @@ -11156,6 +11178,12 @@ "object-keys": "^1.1.1" } }, + "detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -13128,10 +13156,13 @@ "peer": true }, "node-gyp-build-optional-packages": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz", - "integrity": "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==", - "optional": true + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "optional": true, + "requires": { + "detect-libc": "^2.0.1" + } }, "node-int64": { "version": "0.4.0", diff --git a/package.json b/package.json index 5b405a0..503e365 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@panva/hkdf": "^1.1.1", "@peculiar/x509": "^1.9.5", "buffer": "^6.0.3", - "cbor-x": "^1.5.4", + "cbor-x": "^1.5.9", "compare-versions": "^6.0.0", "cose-kit": "^1.7.1", "debug": "^4.3.4", diff --git a/src/cbor/index.ts b/src/cbor/index.ts index a7d8f94..3d1b9cc 100644 --- a/src/cbor/index.ts +++ b/src/cbor/index.ts @@ -4,6 +4,26 @@ import { Options, } from 'cbor-x'; +const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom'); + +export class DateOnly extends Date { + constructor(strDate?: string) { + super(strDate); + } + + toISOString(): string { + return super.toISOString().split('T')[0]; + } + + toString(): string { + return this.toISOString(); + } + + [customInspectSymbol](): string { + return this.toISOString(); + } +} + const encoderDefaults: Options = { tagUint8Array: false, useRecords: false, @@ -12,16 +32,22 @@ const encoderDefaults: Options = { useTag259ForMaps: false, }; +// tdate data item shall contain a date-time string as specified in RFC 3339 (with no fraction of seconds) +// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 addExtension({ Class: Date, + tag: 0, + encode: (date: Date, encode) => encode(`${date.toISOString().split('.')[0]}Z`), + decode: (isoStringDateTime: any) => new Date(isoStringDateTime), +}); + +// full-date data item shall contain a full-date string as specified in RFC 3339 +// see https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 +addExtension({ + Class: DateOnly, tag: 1004, - encode: (instance: Date, encode) => { - const str = instance.toISOString().split('T')[0]; - return encode(str); - }, - decode: (val: any): Object => { - return new Date(val); - }, + encode: (date: DateOnly, encode) => encode(date.toISOString()), + decode: (isoStringDate: any): Object => new DateOnly(isoStringDate), }); export const cborDecode = ( diff --git a/src/mdoc/model/Document.ts b/src/mdoc/model/Document.ts index 39099ca..90aef9f 100644 --- a/src/mdoc/model/Document.ts +++ b/src/mdoc/model/Document.ts @@ -1,7 +1,7 @@ import * as jose from 'jose'; import { COSEKeyFromJWK, COSEKeyToJWK, ProtectedHeaders, UnprotectedHeaders } from 'cose-kit'; import { fromPEM } from '../utils'; -import { DataItem, cborDecode, cborEncode } from '../../cbor'; +import { DataItem, DateOnly, cborDecode, cborEncode } from '../../cbor'; import { IssuerSignedItem } from '../IssuerSignedItem'; import IssuerAuth from './IssuerAuth'; import { DeviceKeyInfo, DigestAlgorithm, DocType, IssuerNameSpaces, MSO, SupportedAlgs, ValidityInfo } from './types'; @@ -66,8 +66,24 @@ export class Document { this.#issuerNameSpaces[namespace] = this.#issuerNameSpaces[namespace] ?? []; const addAttribute = (key: string, value: any) => { + 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 DateOnly(value); + } + + if (key === 'driving_privileges' && Array.isArray(value)) { + value.forEach((v, i) => { + if (typeof v.issue_date === 'string') { elementValue[i].issue_date = new DateOnly(v.issue_date); } + if (typeof v.expiry_date === 'string') { elementValue[i].expiry_date = new DateOnly(v.expiry_date); } + }); + } + } + const digestID = this.#issuerNameSpaces[namespace].length; - const issuerSignedItem = IssuerSignedItem.create(digestID, key, value); + const issuerSignedItem = IssuerSignedItem.create(digestID, key, elementValue); this.#issuerNameSpaces[namespace].push(issuerSignedItem); };