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

test: add test for qr link verification unencrypted data #156

Merged
merged 8 commits into from
Dec 10, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ To test your QR Link / Encryption implementation, follow these steps:
headers: {},
method: 'GET',
},
QrLinkUnencrypted: {
url: 'https://example.com/credential-verifier?q=%7B%22payload%22%3A%7B%22uri%22%3A%22https%3A%2F%2Fapi.vckit.showthething.com%2Fencrypted-storage%2Fencrypted-data%2F0a6031a9-2740-49cd-b12b-1ed02820f01d%22%7D%7D',
headers: {},
method: 'GET',
},
},
};
```
Expand Down
11 changes: 10 additions & 1 deletion packages/vc-test-suite/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ yarn install

## Setup

There are two parts of the test suite: `Render Template 2024` and `QR Link Encryption`. To add your implementation to this test suite you will need to add 2 endpoints to your implementation manifest in `config.ts`:
There are three parts of the test suite: `Render Template 2024`, `QR Link Encryption` and `Storage`. To add your implementation to this test suite you will need to add 2 endpoints to your implementation manifest in `config.ts`:

ldhyen99 marked this conversation as resolved.
Show resolved Hide resolved
- A Render Template 2024 endpoint in the `RenderTemplate2024` property.
- An Encrypted QR Link in the `QrLinkEncrypted` property.
- An Unencrypted QR Link in the `QrLinkUnencrypted` property.
- A Storage endpoint in the `Storage` property.

> **IMPORTANT**: Please change the url in the `config.ts` file to match your implementation.

## Usage

Expand Down Expand Up @@ -40,3 +44,8 @@ Open the generated report HTML file in the reports folder to check the results.
```bash
yarn test:package
```

### Additional Documentation

- [Technical Interoperability](https://uncefact.github.io/tests-untp/docs/test-suites/technical-interoperability/)
- [Verify Link](https://uncefact.github.io/tests-untp/docs/mock-apps/common/verify-link)
9 changes: 7 additions & 2 deletions packages/vc-test-suite/config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
export default {
implementationName: 'VCkit',
implementationName: 'Tier 1 Test Suite',
testSuites: {
QrLinkEncrypted: {
url: 'http://localhost:3001/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2Fc2eb5fee-da3b-411b-9b03-e8f7fb20dc3d.json%22%2C%22key%22%3A%22e5e0d20e35e2786773720cbba011ca26cca0d6fd279a6c3fd00980a5ed1fb5c8%22%2C%22hash%22%3A%2231e96f8b6896b9fc6f9b86267574b2926b5637e1f65027b437119cfd2033f3d7%22%7D%7D',
url: 'http://localhost:3003/verify?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F7a07aa7c-c961-4c74-a714-a6188cadd8f6.json%22%2C%22key%22%3A%222e2a9af227350b73733988ccb93581a5449cafa64394c13ffc7142b3b0b280b6%22%2C%22hash%22%3A%22a813f8aa1cdb5f391ec227d5a1f76a2764df7112bb26cd035b547a30ade03c8e%22%7D%7D',
headers: {},
method: 'GET',
},
QrLinkUnencrypted: {
url: 'http://localhost:3003/verify/?q=%7B%22payload%22%3A%7B%22uri%22%3A%22http%3A%2F%2Flocalhost%3A3334%2Fv1%2Fverifiable-credentials%2F61bdef77-0bcf-47d8-a6e6-00074e530e31.json%22%2C%20%22hash%22%3A%229db052ff7b36aa3be34c4a92fb9444314b786380815984d7aba81af97123ce16%22%7D%7D',
headers: {},
method: 'GET',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { decryptCredential } from '@mock-app/services';
import chai from 'chai';
import config from '../../config';

import { HashAlgorithm, decryptCredential } from '@mock-app/services';
import { computeHash } from '@mock-app/services';

import { reportRow, setupMatrix } from '../../helpers';
ldhyen99 marked this conversation as resolved.
Show resolved Hide resolved
import { request } from '../../httpService';
import { isURLEncoded, parseQRLink } from './helper';
import { computeHash } from '@mock-app/services';
import jwt from 'jsonwebtoken';
import config from '../../config';

const expect = chai.expect;

describe('QR Link Verification', function () {
describe('QR Link Verification with encrypted data', function () {
const { url: qrLink, method, headers } = config.testSuites.QrLinkEncrypted;
const parsedLink = parseQRLink(qrLink);

Expand All @@ -23,11 +24,9 @@ describe('QR Link Verification', function () {
reportRow('Verification page link MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.verify_app_address).to.be.a('string');
});

reportRow('Payload MUST exist and be an object', config.implementationName, function () {
expect(parsedLink.q.payload).to.be.an('object');
});

reportRow('URI MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.uri).to.be.a('string');
});
Expand All @@ -39,53 +38,83 @@ describe('QR Link Verification', function () {
headers,
});

data.should.be.an('object');
data.should.not.be.null;
data.should.not.be.undefined;
data.should.not.be.empty;
});

reportRow('Hash MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.hash).to.be.a('string');
});

reportRow('Hash MUST match the credential hash', config.implementationName, async function () {
const { data } = await request({
url: parsedLink.q.payload.uri,
method,
headers,
});

const stringifyVC = decryptCredential({
...data,
key: parsedLink.q.payload.key,
});

const vc = JSON.parse(stringifyVC);
const credentialHash = computeHash(vc);
const credentialHash = computeHash(vc, HashAlgorithm.SHA256);
expect(parsedLink.q.payload.hash).to.equal(credentialHash);
});

reportRow('Key exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.key).to.be.a('string');
});

reportRow('Key MUST decrypt the encrypted credential', config.implementationName, async function () {
const { data } = await request({
url: parsedLink.q.payload.uri,
method,
headers,
});

const stringifyVC = decryptCredential({
...data,
key: parsedLink.q.payload.key,
});

const vc = JSON.parse(stringifyVC);
// Assert that stringifyVC is not null, empty, or undefined
stringifyVC.should.not.be.null;
stringifyVC.should.not.be.undefined;
stringifyVC.should.not.be.empty;
});
ldhyen99 marked this conversation as resolved.
Show resolved Hide resolved
});

describe('QR Link Verification with unencrypted data', function () {
const { url: qrLink } = config.testSuites.QrLinkUnencrypted;
const parsedLink = parseQRLink(qrLink);

setupMatrix.call(this, [config.implementationName], 'Implementer');

reportRow('QR link MUST be URL encoded', config.implementationName, function () {
const data = isURLEncoded(qrLink);
data.should.be.true;
});

reportRow('Verification page link MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.verify_app_address).to.be.a('string');
});

reportRow('Payload MUST exist and be an object', config.implementationName, function () {
expect(parsedLink.q.payload).to.be.an('object');
});

reportRow('URI MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.uri).to.be.a('string');
});

reportRow('Hash MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.hash).to.be.a('string');
});

// Handle both JWT and regular JSON VC formats
const vcData = vc.id?.startsWith('data:application/vc-ld+jwt,') ? jwt.decode(vc.id.split(',')[1]) : vc;
reportRow('Hash MUST match the credential hash', config.implementationName, async function () {
const { data } = await request({
url: parsedLink.q.payload.uri,
method: 'GET',
headers: {},
});

expect(vcData).to.be.an('object');
vcData.should.have.property('issuer');
vcData.should.have.property('credentialSubject');
const credentialHash = computeHash(data, HashAlgorithm.SHA256);
expect(parsedLink.q.payload.hash).to.equal(credentialHash);
});
});
Loading