Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: implement the changes for the dash7, draft 2024 #16

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 70 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

[ISO 18013-5](https://www.iso.org/standard/69084.html) defines mDL (mobile Driver Licenses): an ISO standard for digital driver licenses.

This is a Node.js library to issue and verify mDL [CBOR encoded](https://cbor.io/) documents in accordance with **ISO 18013-7 (draft's date: 2023-08-02)**.
This is a Node.js library to issue and verify mDL [CBOR encoded](https://cbor.io/) documents in accordance with **ISO 18013-7 (draft's date: 2024-02-13)**.

> If you are working with the **2023 draft** make sure to use the `@auth0/mdl@v1` version.

## Installation

Expand All @@ -24,20 +26,41 @@ import fs from "node:fs";

const trustedCerts = [fs.readFileSync('./caCert1.pem')/*, ... */];
const verifier = new Verifier(trustedCerts);
const mdoc = await verifier.verify(encodedDeviceResponse, {
ephemeralReaderKey,
encodedSessionTranscript,
});

//at this point the issuer and device signature are valids.
let mdoc;

/** ... using OID4VP protocol (Annex B): */
{
mdoc = await verifier
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptForOID4VP(
mdocGeneratedNonce, //
clientId, // Parameters coming from
responseUri, // the OID4VP transaction
verifierGeneratedNonce //
)
.verify(encodedDeviceResponse);
}

/** ... OR ALTERNATIVELY using the Web API protocol (Annex A): */
{
mdoc = await verifier
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptForWebAPI(
encodedDeviceEngagement, // CBOR as received from the reader
encodedReaderEngagement, // CBOR as sent to the reader
encodedReaderPublicKey, // as found in the ReaderEngagement
)
.verify(encodedDeviceResponse);
}

//at this point the issuer and device signature are valid.
inspect(mdoc);
})();
```

## Getting diagnostic information



```javascript
import { Verifier } from "@auth0/mdl";
import { inspect } from "node:util";
Expand All @@ -51,10 +74,32 @@ import fs from "node:fs";
const trustedCerts = [fs.readFileSync('./caCert1.pem')/*, ... */];
const verifier = new Verifier(trustedCerts);

const diagnosticInfo = await verifier.getDiagnosticInformation(encodedDeviceResponse, {
ephemeralReaderKey,
encodedSessionTranscript,
});
let diagnosticInfo;

/** ... using OID4VP protocol (Annex B): */
{
mdoc = await verifier
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptForOID4VP(
mdocGeneratedNonce, //
clientId, // Parameters coming from
responseUri, // the OID4VP transaction
verifierGeneratedNonce //
)
.getDiagnosticInformation(encodedDeviceResponse);
}

/** ... OR ALTERNATIVELY using the Web API protocol (Annex A): */
{
mdoc = await verifier
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptForWebAPI(
encodedDeviceEngagement, // CBOR as received from the reader
encodedReaderEngagement, // CBOR as sent to the reader
encodedReaderPublicKey, // as found in the ReaderEngagement
)
.getDiagnosticInformation(encodedDeviceResponse);
}

inspect(diagnosticInfo);
})();
Expand Down Expand Up @@ -149,39 +194,29 @@ import { createHash } from 'node:crypto';
],
};

/** ... using a OID4VP handover: */
/** ... using OID4VP protocol (Annex B): */
{
// Parameters coming from the OID4VP transaction
let mdocGeneratedNonce, clientId, responseUri, verifierGeneratedNonce;

deviceResponseMDoc = await DeviceResponse.from(issuerMDoc)
.usingPresentationDefinition(presentationDefinition)
.usingSessionTranscriptForOID4VP(mdocGeneratedNonce, clientId, responseUri, verifierGeneratedNonce)
.usingSessionTranscriptForOID4VP(
mdocGeneratedNonce, //
clientId, // Parameters coming from
responseUri, // the OID4VP transaction
verifierGeneratedNonce //
)
.authenticateWithSignature(devicePrivateKey, 'ES256')
.sign();
}

/** ... OR ALTERNATIVELY using an "Annex A" transcript: */
/** ... OR ALTERNATIVELY using the Web API protocol (Annex A): */
{
let encodedReaderEngagement; // CBOR as received from the reader
let encodedDeviceEngagement; // CBOR as sent to the reader
let encodedReaderPublicKey; // as found in the ReaderEngagement

const engagementToApp = Buffer.from(
createHash('sha256').update(encodedReaderEngagement).digest('hex'),
'hex',
);
const sessionTranscriptBytes = cborEncode(
DataItem.fromData([
new DataItem({ buffer: encodedDeviceEngagement }),
new DataItem({ buffer: encodedReaderPublicKey }),
engagementToApp,
]),
);

deviceResponseMDoc = await DeviceResponse.from(issuerMDoc)
.usingPresentationDefinition(presentationDefinition)
.usingSessionTranscriptForWebAPI(encodedDeviceEngagement, encodedReaderEngagement, encodedReaderPublicKey)
.usingSessionTranscriptForWebAPI(
encodedDeviceEngagement, // CBOR as received from the reader
encodedReaderEngagement, // CBOR as sent to the reader
encodedReaderPublicKey, // as found in the ReaderEngagement
)
.authenticateWithSignature(devicePrivateKey, 'ES256')
.sign();
}
Expand Down
10 changes: 8 additions & 2 deletions __tests__/diagnostic.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ describe('diagnostic info', () => {
let diagnosticInfo: DiagnosticInformation;

beforeAll(async () => {
diagnosticInfo = await verifier.getDiagnosticInformation(deviceResponse, { ephemeralReaderKey, encodedSessionTranscript });
diagnosticInfo = await verifier
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptBytes(encodedSessionTranscript)
.getDiagnosticInformation(deviceResponse);
});

it('should return the version', async () => {
Expand All @@ -34,7 +37,10 @@ describe('diagnostic info', () => {

it('should return the invalid signature reason when not providing the root certs', async () => {
const verifier2 = new Verifier([]);
diagnosticInfo = await verifier2.getDiagnosticInformation(deviceResponse, { ephemeralReaderKey, encodedSessionTranscript });
diagnosticInfo = await verifier2
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptBytes(encodedSessionTranscript)
.getDiagnosticInformation(deviceResponse);
expect(diagnosticInfo.issuerSignature.reasons).toEqual([
'No valid certificate paths found',
]);
Expand Down
22 changes: 11 additions & 11 deletions __tests__/example/disableCertVerification.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,22 @@ describe('example 1: valid device response with full disclosure', () => {
const ephemeralReaderKey = hex`534b526561646572`;
const encodedSessionTranscript = hex`d818589e83f6f68466313233343536782b437131616e506238765a55356a354330643768637362754a4c4270496177554a4944515269324562776234785c687474703a2f2f6c6f63616c686f73743a343030302f6170692f70726573656e746174696f6e5f726571756573742f64633839393964662d643665612d346338342d393938352d3337613862383161383265632f63616c6c6261636b6761626364656667`;
const deviceResponse = hex`b900036776657273696f6e63312e3069646f63756d656e747381b9000367646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c6973737565725369676e6564b900026a6e616d65537061636573b90001716f72672e69736f2e31383031332e352e318cd8185867b90004686469676573744944006672616e646f6d5820fad69ca9e366c6a46ed929673544229d1156f3796dc41ffb72165d545524ce9971656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456616c756565536d697468d8185865b90004686469676573744944016672616e646f6d5820983235f38aa7fd6fe8d5412ae57d2a5d356a9969e7363e3e2bb82e0773efd92971656c656d656e744964656e7469666965726a676976656e5f6e616d656c656c656d656e7456616c7565644a6f686ed818586eb90004686469676573744944026672616e646f6d58201e7fcaec40dec4e9d926bf535052dfded9402925c49b3eb11142817273f0333f71656c656d656e744964656e7469666965726a62697274685f646174656c656c656d656e7456616c7565d903ec6a313938302d30362d3135d818586eb90004686469676573744944036672616e646f6d5820e51bbab7c72f05cbe7ff15678423bc871463b87fa4d403ad857cd8cbe6e4c6d371656c656d656e744964656e7469666965726a69737375655f646174656c656c656d656e7456616c7565d903ec6a323032332d30332d3031d818586fb90004686469676573744944046672616e646f6d5820e4a404254912d132825ca342eded2244eda47dd17d57f6896a106a3ff047891971656c656d656e744964656e7469666965726b6578706972795f646174656c656c656d656e7456616c7565d903ec6a323032382d30332d3331d8185868b90004686469676573744944056672616e646f6d58204b330bd5f9d3aa04e02cf3d4c1145121540a24782fc9cea7bb151691da8aaf7571656c656d656e744964656e7469666965726f69737375696e675f636f756e7472796c656c656d656e7456616c7565625553d818586eb90004686469676573744944066672616e646f6d5820c01e8d0f5e092a2aaa30b8253df565a30007dad4bc6102e1e1773e40b184619b71656c656d656e744964656e7469666965727169737375696e675f617574686f726974796c656c656d656e7456616c7565664e5920444d56d8185873b90004686469676573744944076672616e646f6d58204ffc6ec7a24204bcd27227c0910f9e9a2e8074cee956755e7a7c67c6d490a8d971656c656d656e744964656e7469666965727469737375696e675f6a7572697364696374696f6e6c656c656d656e7456616c7565684e657720596f726bd8185871b90004686469676573744944086672616e646f6d5820265e44bd5303560af9565f4c69e871062d1e8c599800cc0d795e81f8c59b5fbc71656c656d656e744964656e7469666965726f646f63756d656e745f6e756d6265726c656c656d656e7456616c75656b30312d3333332d37303730d8185863b90004686469676573744944096672616e646f6d5820cfe31db70e2dc9275f4d8314a895358899e9adc0d0e51920e028285536e833bd71656c656d656e744964656e74696669657268706f7274726169746c656c656d656e7456616c75656462737472d81858b1b900046864696765737449440a6672616e646f6d5820c4916d6ea767ee1fe70fea90a46f9c6349cba3be36781271c1ef00dbdf55ce0f71656c656d656e744964656e7469666965727264726976696e675f70726976696c656765736c656c656d656e7456616c756581b900037576656869636c655f63617465676f72795f636f646561436a69737375655f646174656a323032332d30332d30316b6578706972795f646174656a323032382d30332d3331d818587ab900046864696765737449440b6672616e646f6d58204be3c184ead50cd540b79a3e854eb007371de1c8c84af62cbf049bf180e56af371656c656d656e744964656e74696669657276756e5f64697374696e6775697368696e675f7369676e6c656c656d656e7456616c75656d7462642d75732e6e792e646d766a697373756572417574688443a10126a20442313118218159022e3082022a308201d0a003020102021457c6ccd308bde43eca3744f2a87138dabbb884e8300a06082a8648ce3d0403023053310b30090603550406130255533111300f06035504080c084e657720596f726b310f300d06035504070c06416c62616e79310f300d060355040a0c064e5920444d56310f300d060355040b0c064e5920444d56301e170d3233303931343134353531385a170d3333303931313134353531385a3053310b30090603550406130255533111300f06035504080c084e657720596f726b310f300d06035504070c06416c62616e79310f300d060355040a0c064e5920444d56310f300d060355040b0c064e5920444d563059301306072a8648ce3d020106082a8648ce3d03010703420004893c2d8347906dc6cd69b7f636af4bfd533f96184f0aadacd10830da4471dbdb60ac170d1cfc534fae2d9dcd488f7747fdf978d925ea31e9e9083c382ba9ed53a38181307f301d0603551d0e04160414ab6d2e03b91d492240338fbccadefd9333eaf6c7301f0603551d23041830168014ab6d2e03b91d492240338fbccadefd9333eaf6c7300f0603551d130101ff040530030101ff302c06096086480186f842010d041f161d4f70656e53534c2047656e657261746564204365727469666963617465300a06082a8648ce3d0403020348003045022009fd0cab97b03e78f64e74d7dcee88668c476a0afc5aa2cebffe07d3be772ea9022100da38abc98a080f49f24ffece1fffc8a6cdd5b2c0b5da8fc7b767ac3a95dcb83e590319d818590314b900066776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473b900026f6f72672e637573746f6d2e74657374a10058203c5116109c068fa7fbf3186baec13ee6cbe48cc6c4a00fd349b9faf0a04f2ffd716f72672e69736f2e31383031332e352e31ac00582083c653c2e7c7d2e0f072f4269a20f4eb35d438f97a1c93026aa124fb707e1479015820a70c14943a002ba0675efa295e985e356197764f4591656cb6b3241b6cbb5f1e025820f0c5dcf0eac39eda62f4f52e52692ffdc09c133f4406008696843501e5364ef903582094f3cc14be210ace4f9d4545948074faf3a0138a2d1041adac45ccae1f1208d2045820c07ad9922535003e47887654eaae055667565c7bd19a8975f0336865f82bf0f505582063cb0912892fbcd761e85b8048caa954bc0db60e3678a9759020445b09a4623e065820052c825478b90e5018e857a3ae90c8dde34f5fe5056a143db77d7cd6107df7c9075820a00f3e3094c3d2ea5d4360f260d46f48ddadabcb49e2c2b2850f2913fdc4e8aa085820aea1b320b97e7922476c68008d275ecf4904ea09d72b950de7a6054bda6e78b7095820f764a3f5d0d998b73505ef1dc18eb15d77acd12b2e2100e59001ab9c0685da540a5820a1dd0ffbd7cff2fe1ee44523d35dbbbdd5c9b59c6ae00ad6dd785554978898c90b5820fd2c88594d6594872b87ac5d5c43d97211a73ffd348a0aaea71e54ef1dc2fbc96d6465766963654b6579496e666fb90001696465766963654b6579a40102215820881879ca7a238b19bf0f4c1f8c00e9a2e19ba7a6f73eae92b851d4de1b508559225820a314b538039127b5cd50735f54519e33c134450545c5603ad9f263facc56d377200167646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fb90003667369676e6564c074323032332d30392d32395431353a34313a35395a6976616c696446726f6dc074323032332d30392d32395431353a34313a35395a6a76616c6964556e74696cc074323037332d30392d32395431353a34313a35395a5840d82c982b492614bc04241484206da61b1cff64bfa0d31b550c09096bc62f26c13b4ef2cad4b87d9e73717620001db2917e78da85896d046e253595af9f42a5946c6465766963655369676e6564b900026a6e616d65537061636573d81841a06a64657669636541757468b900016f6465766963655369676e61747572658443a10126a10442313158d2d81858ce847444657669636541757468656e7469636174696f6e83f6f68466313233343536782b437131616e506238765a55356a354330643768637362754a4c4270496177554a4944515269324562776234785c687474703a2f2f6c6f63616c686f73743a343030302f6170692f70726573656e746174696f6e5f726571756573742f64633839393964662d643665612d346338342d393938352d3337613862383161383265632f63616c6c6261636b6761626364656667756f72672e69736f2e31383031332e352e312e6d444cd81841a05840a97b5f9b59d193eda662542e71cb7daf6fa8952ac8b0cce82f22e7834d1fa8f0d2cab25a708658226c03d96950d00c3516fce62a220748d4f0a696072243b5026673746174757300`;
const verifier = new Verifier([]);

it('should verify properly', async () => {
await verifier.verify(deviceResponse, {
ephemeralReaderKey,
encodedSessionTranscript,
disableCertificateChainValidation: true,
});
await new Verifier([])
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptBytes(encodedSessionTranscript)
.disableCertificateChainValidation()
.verify(deviceResponse);
});

it('should return blabla ', async () => {
const diagnostic = await verifier.getDiagnosticInformation(deviceResponse, {
ephemeralReaderKey,
encodedSessionTranscript,
disableCertificateChainValidation: true,
});
const diagnostic =
await new Verifier([])
.usingEphemeralReaderKey(ephemeralReaderKey)
.usingSessionTranscriptBytes(encodedSessionTranscript)
.disableCertificateChainValidation()
.getDiagnosticInformation(deviceResponse);
expect(diagnostic.issuerSignature.isValid).toBe(true);
});
});
Loading