@animo-id/mdoc
Version:
Animo Mdoc and MDL
228 lines (186 loc) • 6.38 kB
Markdown
<p align="center">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://res.cloudinary.com/animo-solutions/image/upload/v1656578320/animo-logo-light-no-text_ok9auy.svg">
<source media="(prefers-color-scheme: dark)" srcset="https://res.cloudinary.com/animo-solutions/image/upload/v1656578320/animo-logo-dark-no-text_fqqdq9.svg">
<img alt="Animo Logo" height="250px" />
</picture>
</p>
<h1 align="center" ><b>mDOC and mDL - TypeScript</b></h1>
[ISO 18013-5](https://www.iso.org/standard/69084.html) defines mDL (mobile Driver’s Licenses): an ISO standard for digital driver licenses.
This is a JavaScript library for Node.JS, browers and React Native to issue and verify mDL [CBOR encoded](https://cbor.io/) documents in accordance with **ISO 18013-7 (draft's date: 2024-03-12)**.
<h4 align="center">Powered by
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://res.cloudinary.com/animo-solutions/image/upload/v1656579715/animo-logo-light-text_cma2yo.svg">
<source media="(prefers-color-scheme: dark)" srcset="https://res.cloudinary.com/animo-solutions/image/upload/v1656579715/animo-logo-dark-text_uccvqa.svg">
<img alt="Animo Logo" height="12px" />
</picture>
</h4><br>
<p align="center">
<a href="https://typescriptlang.org">
<img src="https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg" />
</a>
</p>
<p align="center">
<a href="#installation">Installation</a>
|
<a href="#usage">Usage</a>
|
<a href="#contributing">Contributing</a>
|
<a href="#license">License</a>
|
<a href="#credits">Credits</a>
</p>
## Installation
```bash
npm i @animo-id/mdoc
```
## Usage
### Verifying a credential
```javascript
import { Verifier } from "@animo-id/mdoc";
import { inspect } from "node:util";
import fs from "node:fs";
(async () => {
const encodedDeviceResponse = Buffer.from(encodedDeviceResponseHex, "hex");
const encodedSessionTranscript = Buffer.from(
encodedSessionTranscriptHex,
"hex"
);
const ephemeralReaderKey = Buffer.from(ephemeralReaderKeyHex, "hex");
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.
inspect(mdoc);
})();
```
### Getting diagnostic information
```javascript
import { Verifier } from "@animo-id/mdoc";
import { inspect } from "node:util";
import fs from "node:fs";
(async () => {
const encodedDeviceResponse = Buffer.from(encodedDeviceResponseHex, "hex");
const encodedSessionTranscript = Buffer.from(
encodedSessionTranscriptHex,
"hex"
);
const ephemeralReaderKey = Buffer.from(ephemeralReaderKeyHex, "hex");
const trustedCerts = [fs.readFileSync("./caCert1.pem") /*, ... */];
const verifier = new Verifier(trustedCerts);
const diagnosticInfo = await verifier.getDiagnosticInformation(
encodedDeviceResponse,
{
ephemeralReaderKey,
encodedSessionTranscript,
}
);
inspect(diagnosticInfo);
})();
```
##$ Issuing a credential
```js
import { MDoc, Document } from "@animo-id/mdoc";
import { inspect } from "node:util";
(async () => {
const document = await new Document("org.iso.18013.5.1.mDL")
.addIssuerNameSpace("org.iso.18013.5.1", {
family_name: "Jones",
given_name: "Ava",
birth_date: "2007-03-25",
})
.useDigestAlgorithm("SHA-256")
.addValidityInfo({
signed: new Date(),
})
.addDeviceKeyInfo({ deviceKey: publicKeyJWK })
.sign({
issuerPrivateKey,
issuerCertificate,
});
const mdoc = new MDoc([document]).encode();
inspect(encoded);
})();
```
##$ Generating a device response
```js
import { DeviceResponse, MDoc } from "@animo-id/mdoc";
(async () => {
let issuerMDoc;
let deviceResponseMDoc;
/**
* This is what the MDL issuer does to generate a credential:
*/
{
let issuerPrivateKey;
let issuerCertificate;
let devicePublicKey; // the public key for the device, as a JWK
const document = await new Document("org.iso.18013.5.1.mDL")
.addIssuerNameSpace("org.iso.18013.5.1", {
family_name: "Jones",
given_name: "Ava",
birth_date: "2007-03-25",
})
.useDigestAlgorithm("SHA-256")
.addValidityInfo({
signed: new Date(),
})
.addDeviceKeyInfo({ deviceKey: devicePublicKey })
.sign({
issuerPrivateKey,
issuerCertificate,
alg: "ES256",
});
issuerMDoc = new MDoc([document]).encode();
}
/**
* This is what the DEVICE does to generate a response...
*/
{
let devicePrivateKey; // the private key for the device, as a JWK
// Parameters coming from the OID4VP transaction
let mdocGeneratedNonce, clientId, responseUri, verifierGeneratedNonce;
let presentationDefinition = {
id: "family_name_only",
input_descriptors: [
{
id: "org.iso.18013.5.1.mDL",
format: { mso_mdoc: { alg: ["EdDSA", "ES256"] } },
constraints: {
limit_disclosure: "required",
fields: [
{
path: ["$['org.iso.18013.5.1']['family_name']"],
intent_to_retain: false,
},
],
},
},
],
};
deviceResponseMDoc = await DeviceResponse.from(issuerMDoc)
.usingPresentationDefinition(presentationDefinition)
.usingSessionTranscriptForOID4VP(
mdocGeneratedNonce,
clientId,
responseUri,
verifierGeneratedNonce
)
.authenticateWithSignature(devicePrivateKey, "ES256")
.sign();
}
})();
```
## Contributing
Is there something you'd like to fix or add? Great, we love community
contributions! To get involved, please follow our [contribution guidelines](./CONTRIBUTING.md).
## License
This project is licensed under the Apache License Version 2.0 (Apache-2.0).
## Credits
Thanks to:
- [auth0/mdl](https://github.com/auth0-lab/mdl) for the mdl implementation on which this repository is based.
- [auer-martin](https://github.com/auer-martin) for removing node.js dependencies and providing a pluggable crypto interface