import { ExpirationValidator } from './expirationValidator.js'; import { RevocationValidator } from './revocationValidator.js'; import { StatusListCredentialFetcher } from '../../resolvers/statusListCredentialFetcher.js'; import { NotYetValidValidator } from './notYetValidValidator.js'; import { DecodedVerifiableCredentialDto } from '../../shared/dto/decodedVerifiableCredential.dto.js'; import { CredentialValidationOptions } from '../../shared/dto/validationOptions.dto.js'; import { Validator } from '../credentialValidatorFactory.js'; import joseWrapper from '../../shared/middleware/joseWrapper.js'; import { DidPublicKeyResolver } from '../../resolvers/didPublicKeyResolver.js'; import { SignatureValidator } from './signatureValidator.js'; import { CredentialValidator } from './credentialValidator.interface.js'; import { ValidationResult } from '../../shared/dto/validationResult.dto.js'; import { VerifiablePresentationValidationReport } from '../presentation/verifiablePresentationValidationReport.js'; import { JsonWebKey } from '../../resolvers/didDocument.dto.js'; import { HttpPublicKeyResolver } from '../../resolvers/httpPublicKeyResolver.js'; export class VerifiableCredentialsValidator implements Validator { constructor( private didPublicKeyResolver: DidPublicKeyResolver, private httpPublicKeyResolver: HttpPublicKeyResolver, private signatureValidator: SignatureValidator, private verifiablePresentationValidationReport?: VerifiablePresentationValidationReport, ) {} async validate( credential: string, opts?: CredentialValidationOptions, ): Promise { const decodedCredential = joseWrapper.decodeJWT( credential, ) as DecodedVerifiableCredentialDto; const validators: CredentialValidator[] = [ new ExpirationValidator(), new RevocationValidator(new StatusListCredentialFetcher()), new NotYetValidValidator(), ]; const credentialValidatorsResult: ValidationResult = { valid: true, }; const result = ( await Promise.all( validators.map(async (validator) => { const result = await validator.validate( decodedCredential as DecodedVerifiableCredentialDto, ); this.verifiablePresentationValidationReport?.updateCredentialInformation( validator.getValidationType(), decodedCredential.vc.id, result, ); return result; }), ) ) .filter((result) => !result.valid) .reduce((acc, curr) => { acc.valid = acc.valid && curr.valid; if (!acc.messages) acc.messages = []; acc.messages.push(curr.message); return acc; }, credentialValidatorsResult); if (!result.valid && !opts?.report) { return result; } const decodedJwtHeader = joseWrapper.decodeJwtProtectedHeader(credential); const issuerKid: string = decodedJwtHeader.kid; let publicKey: JsonWebKey; if (issuerKid.startsWith('did:')) { publicKey = await this.didPublicKeyResolver.getPublicKeyJwk( issuerKid, opts, ); } if (!issuerKid.startsWith('did:')) { publicKey = await this.httpPublicKeyResolver.getPublicKeyJwk( issuerKid, decodedCredential.iss, ); } const signatureValidationResult = await this.signatureValidator.validate( credential, publicKey, decodedJwtHeader.alg, decodedCredential.vc.id, ); this.verifiablePresentationValidationReport?.updateCredentialInformation( this.signatureValidator.getValidationType(), decodedCredential.vc.id, { valid: signatureValidationResult.valid, ...(signatureValidationResult.messages && { message: signatureValidationResult.messages.join(), }), }, ); const errorMessages = [ ...(result?.messages || []), ...(signatureValidationResult?.messages || []), ]; return { valid: result.valid && signatureValidationResult.valid, ...(errorMessages.length && { messages: errorMessages }), }; } }