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

Add DID and x509 for key reference #31

Merged
merged 2 commits into from
May 7, 2024
Merged
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
3 changes: 2 additions & 1 deletion apps/verifier-backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
"passport-http-bearer": "^1.0.1",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"uuid": "^9.0.1"
"uuid": "^9.0.1",
"web-did-resolver": "^2.0.27"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
Expand Down
51 changes: 47 additions & 4 deletions apps/verifier-backend/src/key/key.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { ConflictException, Injectable, OnModuleInit } from '@nestjs/common';
import { ES256 } from '@sd-jwt/crypto-nodejs';
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'node:fs';
import { JWK } from 'jose';
import { JWK, JWTPayload } from 'jose';
import { v4 } from 'uuid';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { encodeDidJWK } from 'src/verifier/did';
import { X509Certificate } from 'node:crypto';
import { Resolver } from 'did-resolver';
import web from 'web-did-resolver';

const webResolver = web.getResolver();
const resolver = new Resolver({
...webResolver,
});

interface IssuerMetadata {
issuer: string;
Expand Down Expand Up @@ -90,18 +98,53 @@ export class KeyService implements OnModuleInit {
return this.privateKey;
}

async resolvePublicKey(issuer: string, kid: string): Promise<JWK> {
/**
* Resolve the public key from the issuer, the function will first check for the x5c header, then for the did document and finally for the issuer metadata.
* @param payload
* @param header
* @returns
*/
async resolvePublicKey(payload: JWTPayload, header: JWK): Promise<JWK> {
if (header.x5c) {
const cert = new X509Certificate(Buffer.from(header.x5c[0], 'base64'));
//TODO: implement the validation of the certificate chain and also the comparison of the identifier
if (cert.subject !== payload.iss) {
throw new Error('Subject and issuer do not match');
}
return cert.publicKey.export({ format: 'jwk' }) as JWK;
}
if (payload.iss.startsWith('did:')) {
const did = await resolver.resolve(payload.iss);
if (!did) {
throw new ConflictException('DID not found');
}
//TODO: header.kid can be relative or absolute, we need to handle this
const key = did.didDocument.verificationMethod.find(
(vm) => vm.id === header.kid
);
if (!key) {
throw new ConflictException('Key not found');
}
if (!key.publicKeyJwk) {
throw new ConflictException(
'Public key not found, we are only supporting JWK keys for now.'
);
}
return key.publicKeyJwk;
}

// lets look for a did
const response = await firstValueFrom(
this.httpService.get<IssuerMetadata>(
`${issuer}/.well-known/jwt-vc-issuer`
`${payload.iss}/.well-known/jwt-vc-issuer`
)
).then(
(r) => r.data,
() => {
throw new ConflictException('Issuer not reachable');
}
);
const key = response.jwks.keys.find((key) => key.kid === kid);
const key = response.jwks.keys.find((key) => key.kid === header.kid);
if (!key) {
throw new Error('Key not found');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ export class RelyingPartyManagerService {
const payload = decodedVC.jwt?.payload as JWTPayload;
const header = decodedVC.jwt?.header as JWK;
const publicKey = await this.keyService.resolvePublicKey(
payload.iss as string,
header.kid as string
payload,
header
);
const verify = await ES256.getVerifier(publicKey);
return verify(data, signature);
Expand Down
45 changes: 31 additions & 14 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.