Skip to content

Commit

Permalink
update tests and readme
Browse files Browse the repository at this point in the history
  • Loading branch information
jchartrand committed Jan 8, 2025
1 parent dfc8c27 commit 584f9a9
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 36 deletions.
235 changes: 222 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,237 @@
[![Build status](https://img.shields.io/github/actions/workflow/status/digitalcredentials/verifier-core/main.yml?branch=main)](https://github.com/digitalcredentials/verifier-core/actions?query=workflow%3A%22Node.js+CI%22)
[![NPM Version](https://img.shields.io/npm/v/@digitalcredentials/verifier-core.svg)](https://npm.im/@digitalcredentials/verifier-core)

> For verifying Verifiable Credentials in the browser, Node.js, and React Native.
> Verifies W3C Verifiable Credentials in the browser, Node.js, and React Native.
## Table of Contents

- [Background](#background)
- [Security](#security)
- [Overview](#overview)
- [API](#api)
- [Install](#install)
- [Usage](#usage)
- [Contribute](#contribute)
- [License](#license)

## Background
## Overview

Verifies the following versions of W3C Verifiable Credentials:

* [1.0](https://www.w3.org/TR/2019/REC-vc-data-model-20191119/)
* [1.1](https://www.w3.org/TR/2022/REC-vc-data-model-20220303/)
* [2.0](https://www.w3.org/TR/vc-data-model-2.0/)

The verification checks that the credential:

* has a valid signature (i.e, that the credential hasn't been tampered with)
* hasn't expired
* hasn't been revoked
* was signed by a trusted issuer

As of January 2025 issuers are trusted if they are listed in one of the Digital Credentials Issuer Registries:

```
{
name: 'DCC Pilot Registry',
url: 'https://digitalcredentials.github.io/issuer-registry/registry.json'
},
{
name: 'DCC Sandbox Registry',
url: 'https://digitalcredentials.github.io/sandbox-registry/registry.json'
},
{
name: 'DCC Community Registry',
url: 'https://digitalcredentials.github.io/community-registry/registry.json'
},
{
name: 'DCC Registry',
url: 'https://digitalcredentials.github.io/dcc-registry/registry.json'
}
```

The DCC is actively working on a new trust registry model that will likely extend the registry scope.

## API

This package exports two methods:

* verifyCredential
* verifyPresentation

### verifyCredential

The Digital Credentials Consortium has a few applications that verify credentials in essentially the same way, with consequent code duplication. This package extracts that common functionality to a single shared package to make ongoing maintenance easier.
```verifyCredential({credential, reloadIssuerRegistry = true})```

## Security
#### arguments

TBD
* credential - The W3C Verifiable Credential to be verified.
* reloadIssuerRegistry - A boolean (true/false) indication whether or not to refresh the cached copy of the registry.

#### result

The typescript definitions for the result can be found [here](./src/types/result.ts)

##### successful verification

A verification is successful if the signature is valid (the credential hasn't been tampered with), hasn't expired, hasn't been revoked, and was signed by a trusted issuer.

A successful verification might look like this example:

```
{
"verified": true,
"isFatal": false,
"credential": {the supplied vc - left out here for brevity/clarity},
"log": [
{
"id": "valid_signature",
"valid": true
},
{
"id": "issuer_did_resolves",
"valid": true
},
{
"id": "expiration",
"valid": true
},
{
"id": "revocation_status",
"valid": true
},
{
"id": "registered_issuer",
"valid": true,
"foundInRegistries": [
"DCC Sandbox Registry"
]
}
]
}
```

##### unsucessful verification

An unsuccessful verification means that one of the steps (other than the 'valid_signature' step) returned false, so the credential has expired, and/or been revoked, and/or can't be confirmed to be signed by a known issuer. Note that an invalid signature is considered fatal because it means that the revocation status, expiry data, or issuer id may have been changed so we can't say anything conclusive about any of them.

An unsuccessful verification might look like this example:

```
{
"verified": false,
"isFatal": false,
"credential": {the supplied vc - left out here for brevity/clarity},
"log": [
{
"id": "valid_signature",
"valid": true
},
{
"id": "issuer_did_resolves",
"valid": true
},
{
"id": "expiration",
"valid": false
},
"id": "revocation_status",
"valid": true
},
{
"id": "registered_issuer",
"valid": true,
"foundInRegistries": [
"DCC Sandbox Registry"
]
}
]
}
```

##### partially successful verification

A verification might partly succeed if it can verify the signature and the expiry date, but can't retrieve any of the revocation status, the issuer registry, or the issuer's DID document from the network to verify the revocation status and issuer identity.

For those steps that we couldn't verify conclusively one way or the other (true or false) we return an 'error' propery rather than a 'valid' property.

A partially successful verification might look like this example:

```
{
"verified": false,
"isFatal": false,
"credential": {the supplied vc - left out here for brevity/clarity},
"log": [
{
"id": "valid_signature",
"valid": true
},
{
"id": "expiration",
"valid": true
},
{
"id": "revocation_status",
"error": {
"name": "network-error",
"message": "Could not retrieve the revocation status list."
}
},
{
"id": "registered_issuer",
"error": {
"name": "network-error",
"message": "Could not retrieve the issuer registry."
}
},
{
"id": "issuer_did_resolves",
"error": {
"name": "network-error",
"message": "Could not retrieve the issuer DID."
}
}
]
}
```

##### fatal error

Fatal errors are errors that prevent us from saying anything conclusive about the credential, and so we don't list the results of each step (the 'log') because we can't say decisively one way or the other if any are true or false. Reverting to saying they are all false would be misleading, because that could be interepreted to mean that the credential was, for example, revoked when really we just don't know one way or the other.

```
{
"credential": {the vc goes here},
"isFatal": true,
"verified": false,
"errors": [
{
"name": "invalidSignature",
"message": "The signature is not valid."
}
]
}
```
Examples of fatal errors:

* invalid signature
Fatal because if the signature is invalid it means any part of the credential could have been tampered with, including the revocation status, expiration, and issuer identity

* software problem
A software error might prevent verification

* malformed credential
The supplied credential may not conform to the VerifiableCredential or LinkedData specifications(possibly because it follows some older convention, or maybe hasn't yet been signed) and might not even be a Verifiable Credential at all.



### verifyPresentation

```verifyPresentation({presentation, reloadIssuerRegistry = true})```

#### arguments

* presentation - The W3C Verifiable Presentation to be verified.
* reloadIssuerRegistry - Whether or not to refresh the cached copy of the registry.

#### result

## Install

Expand All @@ -44,10 +257,6 @@ cd verifier-core
npm install
```

## Usage

TBD

## Contribute

PRs accepted.
Expand Down
20 changes: 1 addition & 19 deletions src/Verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getCredentialStatusChecker } from './credentialStatus';
import { addTrustedIssuersToVerificationResponse } from './issuerRegistries';

import { Credential } from './types/credential';
import { VerificationResponse } from './types/result';

/*
// the new eddsa-rdfc-2022-cryptosuite
Expand All @@ -19,26 +20,7 @@ const suite = new DataIntegrityProof({
const documentLoader = securityLoader({ fetchRemoteContexts: true }).build();
const suite = new Ed25519Signature2020();

export interface VerificationError {
"message": string,
"isFatal": boolean,
"name"?: string,
stackTrace?: string
}

export interface Step {
"id": string,
"valid": boolean,
"foundInRegistries"?: string[],
}

export interface VerificationResponse {
"verified": boolean,
"isFatal": boolean,
"credential": object,
"errors"?: VerificationError[],
"log"?: Step[]
}


export async function verifyCredential({credential, reloadIssuerRegistry = true}:{credential: Credential, reloadIssuerRegistry: boolean}): Promise<VerificationResponse> {
Expand Down
2 changes: 1 addition & 1 deletion src/issuerRegistries.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {RegistryClient} from '@digitalcredentials/issuer-registry-client';

Check failure on line 1 in src/issuerRegistries.ts

View workflow job for this annotation

GitHub Actions / lint (18.x)

Cannot find module './types/result' or its corresponding type declarations.

Check failure on line 1 in src/issuerRegistries.ts

View workflow job for this annotation

GitHub Actions / test-node (18.x)

Cannot find module './types/result' or its corresponding type declarations.

Check failure on line 1 in src/issuerRegistries.ts

View workflow job for this annotation

GitHub Actions / test-karma (18.x)

Cannot find module './types/result' or its corresponding type declarations.
import {knownDidRegistries} from '../.knownDidRegistries'
import { VerificationResponse } from './Verify';
import { VerificationResponse } from './types/result';
const registries = new RegistryClient()
const registryNotYetLoaded = true;
/**
Expand Down
7 changes: 4 additions & 3 deletions test/Verify.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { verifyCredential } from '../src/Verify'
import { getSignedUnrevokedVC2, getTamperedVC1, getExpiredVC2 } from '../src/test-fixtures/vc'

describe('Verify', () => {
it('verifies with valid status', async () => {
it.only('verifies with valid status', async () => {
//const signedVC : any = getSignedVC()
const signedVC2Unrevoked : any = getSignedUnrevokedVC2()
const result = await verifyCredential({credential: signedVC2Unrevoked, reloadIssuerRegistry: true})
Expand All @@ -14,7 +14,7 @@ describe('Verify', () => {
//expect(result.verified).to.be.true
})

it.only('returns fatal error when tampered', async () => {
it('returns fatal error when tampered', async () => {
const tamperedVC1 : any = getTamperedVC1()
const result = await verifyCredential({credential: tamperedVC1, reloadIssuerRegistry: true})
console.log("result returned from verifyCredential call:")
Expand All @@ -23,12 +23,13 @@ describe('Verify', () => {
//expect(result.verified).to.be.true
})

it('returns fatal error when tampered', async () => {
it('returns unverified when expired', async () => {
const expiredVC2 : any = getExpiredVC2()
const result = await verifyCredential({credential: expiredVC2, reloadIssuerRegistry: true})
console.log("result returned from verifyCredential call:")
console.log(JSON.stringify(result,null,2))
assert.ok(result.verified === false);
assert.ok(result.log);
//expect(result.verified).to.be.true
})

Expand Down

0 comments on commit 584f9a9

Please sign in to comment.