Skip to content

Commit

Permalink
Merge pull request #13 from transmute-industries/feedback
Browse files Browse the repository at this point in the history
Apply feedback on validation interface
  • Loading branch information
OR13 authored Jul 8, 2024
2 parents 95c24d8 + c624658 commit d663ff9
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 70 deletions.
58 changes: 32 additions & 26 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions src/cr1/credential/issuer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const coseSign1CredentialIssuer = (issuer: RequestCredentialIssuer) => {
if (issuer.signer === undefined) {
throw new Error('No signer available.')
}
const claims = claimset.parse(decoder.decode(credential.claimset)) as any
const claims = claimset.parse(decoder.decode(credential.claimset))
return issuer.signer.sign(encoder.encode(JSON.stringify(claims)))
}
}
Expand All @@ -25,7 +25,7 @@ const jwtCredentialIssuer = (issuer: RequestCredentialIssuer) => {
if (issuer.signer === undefined) {
throw new Error('No signer available.')
}
const claims = claimset.parse(decoder.decode(credential.claimset)) as any
const claims = claimset.parse(decoder.decode(credential.claimset))
return issuer.signer.sign(encoder.encode(JSON.stringify(claims)))
}
}
Expand Down
25 changes: 21 additions & 4 deletions src/cr1/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,19 +158,28 @@ export type SecuredContentType = {
content: Uint8Array
}

export type VerifierResolutionRequest = {
type: SupportedCredentialFormats | SupportedPresentationFormats | SupportedJwtSignatureFormats | SupportedSdJwtSignatureFormats | SupportedCoseSign1Formats
content: Uint8Array
purpose: ValidatorResolutionPurpose
}

export type VerifierResolver = {
resolve: (req: SecuredContentType) => Promise<PublicKeyWithContentType>
resolve: (req: VerifierResolutionRequest) => Promise<PublicKeyWithContentType>
}

export type RequestVerifier = {
resolver: VerifierResolver
}


export type ValidatorResolutionPurpose = 'schema-validation' | 'status-check' | 'verification-material'

export type ValidatorContentType = {
id?: string
type: any
content?: Uint8Array
purpose: ValidatorResolutionPurpose
}


Expand Down Expand Up @@ -244,11 +253,19 @@ export type ConformanceWarningMessage = {
reference: string
}

export type SchemaValidation = 'succeeded' | 'failed' | 'ignored'

export type StatusCheckResult = {
errors?: StatusListError[]
} & Record<string, any> // because of enumerations.

export type SchemaCheckResult = { validation?: SchemaValidation, errors?: JsonSchemaError[], }

export type ValidationResult = {
valid: boolean
verified: boolean
content: VerifiableCredential
schema: Record<string, { valid: boolean, errors?: JsonSchemaError[] }>
status: Record<string, { valid: boolean, purpose: string, errors?: StatusListError[] }>
schema: Record<string, SchemaCheckResult>
status: Record<string, StatusCheckResult>
warnings: ConformanceWarningMessage[]
}

Expand Down
22 changes: 12 additions & 10 deletions src/cr1/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const validator = ({ resolver }: RequestValidator) => {
validate: async <T = TraceablePresentationValidationResult>({ type, content }: SecuredContentType) => {
const verified = await verifier({ resolver }).verify<VerifiableCredential>({ type, content })
const validation: ValidationResult = {
valid: true,
verified: true,
content: verified,
schema: {},
status: {},
Expand All @@ -41,7 +41,12 @@ export const validator = ({ resolver }: RequestValidator) => {
// prefer to resolve this one by id, instead of content
id: schema.id,
type: 'application/schema+json',
purpose: 'schema-validation'
})
if (credentialSchema === true) {
validation.schema[schema.id] = { validation: 'ignored' }
continue;
}
const schemaContent = decoder.decode(credentialSchema.content)
const parsedSchemaContent = JSON.parse(schemaContent)
let valid: any;
Expand All @@ -58,9 +63,8 @@ export const validator = ({ resolver }: RequestValidator) => {
} catch (e) {
valid = false
}
validation.schema[schema.id] = { valid }
validation.schema[schema.id] = { validation: valid ? 'succeeded' : 'failed' }
if (!valid) {
validation.valid = false
validation.schema[schema.id].errors = compiledSchemaValidator.errors as JsonSchemaError[]
}
}
Expand All @@ -73,23 +77,21 @@ export const validator = ({ resolver }: RequestValidator) => {
const statusListCredential = await resolver.resolve({
// prefer to resolve this one by id, instead of content
id: status.statusListCredential,
type: type // we do not support mixed type credential and status lists!
type: type, // we do not support mixed type credential and status lists!
purpose: 'status-check'
})
const verified = await verifier({ resolver }).verify<BitstringStatusListCredential>(statusListCredential)
// confirm purpose matches
if (status.statusPurpose !== verified.credentialSubject.statusPurpose) {
validation.valid = false
validation.status[`${status.id}`] = {
valid: false, purpose: status.statusPurpose, errors: [{
[status.statusPurpose]: false,
errors: [{
message: 'status list purpose does not match credential status'
}]
}
} else {
const bit = bs(verified.credentialSubject.encodedList).get(parseInt(status.statusListIndex, 10))
if (bit) {
validation.valid = false
}
validation.status[`${status.id}`] = { valid: bit, purpose: status.statusPurpose }
validation.status[`${status.id}`] = { [status.statusPurpose]: bit }
}

}
Expand Down
11 changes: 7 additions & 4 deletions src/cr1/verifier/verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const acceptableAudience = (expectedAud: string, receivedAud: string | string[])
}

const verifyJwt = async ({ resolver }: RequestVerifier, { type, content, audience, nonce }: RequestVerify) => {
const key = await resolver.resolve({ type, content })
const key = await resolver.resolve({ type, content, purpose: 'verification-material' })
const publicKey = await importKeyLike(key)
const jwt = decoder.decode(content)
const { payload } = await jose.jwtVerify(jwt, publicKey, {
Expand All @@ -46,7 +46,8 @@ const verifyCoseSign1
resolve: async () => {
const key = await resolver.resolve({
type,
content
content,
purpose: 'verification-material'
})
return importJWK(key)
}
Expand Down Expand Up @@ -86,7 +87,8 @@ const verifySdJwtCredential = async ({ resolver }: RequestVerifier, { type, cont
resolve: async () => {
const key = await resolver.resolve({
type,
content
content,
purpose: 'verification-material'
})
return importJWK(key)
}
Expand All @@ -106,7 +108,8 @@ const verifySdJwtPresentation = async ({ resolver }: RequestVerifier, { type, co
resolve: async () => {
const key = await resolver.resolve({
type,
content // same a token
content, // same a token
purpose: 'verification-material'
})
return importJWK(key)
}
Expand Down
4 changes: 2 additions & 2 deletions test/json-schema-tests/better-schema-errors.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ credentialSubject:
type: "application/vc+ld+json+jwt",
content: issued,
});
expect(validation1.valid).toBe(false);
expect(validation1.verified).toBe(true);
expect(validation1.schema).toEqual({
"https://vendor.example/api/schemas/product-passport": {
"valid": false,
"validation": "failed",
"errors": [
{
"instancePath": "/credentialSubject",
Expand Down
4 changes: 2 additions & 2 deletions test/json-schema-tests/json-schema-tests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,12 @@ credentialSubject:
type: "application/vc+ld+json+jwt",
content: issued,
});
expect(valid1.valid).toBe(true);
expect(valid1.verified).toBe(true);

const valid2 = await validator.validate({
type: "application/vc+ld+json+jwt",
content: issued,
});
expect(valid2.valid).toBe(true);
expect(valid2.verified).toBe(true);
});
});
Loading

0 comments on commit d663ff9

Please sign in to comment.