From facd046d467162da6391edea8196b7dbe5f704f5 Mon Sep 17 00:00:00 2001 From: Phan Shi Yu Date: Mon, 13 May 2024 16:30:22 +0800 Subject: [PATCH] fix: obfuscate typing and guard exports (#292) * fix: obfuscate typing refused to accept document that could be multiple version * fix: improve consistency of utils guard * fix: simplify obfuscateVerifiableCredential typing --- src/4.0/obfuscate.ts | 6 +++--- src/index.ts | 33 +++++++++++-------------------- src/shared/utils/guard.ts | 41 +++++---------------------------------- src/shared/utils/utils.ts | 20 +++++++++---------- 4 files changed, 29 insertions(+), 71 deletions(-) diff --git a/src/4.0/obfuscate.ts b/src/4.0/obfuscate.ts index 9164f34d..7edfb8b3 100644 --- a/src/4.0/obfuscate.ts +++ b/src/4.0/obfuscate.ts @@ -66,9 +66,9 @@ const obfuscate = (_data: V4WrappedDocument, fields: string[] | string) => { }; }; -export type ObfuscateVerifiableCredentialResult = Override< - T extends V4SignedWrappedDocument ? V4SignedWrappedDocument : V4WrappedDocument, - Required>> +export type ObfuscateVerifiableCredentialResult = Override< + T, + { credentialSubject: PartialDeep } >; export const obfuscateVerifiableCredential = ( document: T, diff --git a/src/index.ts b/src/index.ts index 46276443..73743b9a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,7 +2,6 @@ import { ethers, Signer } from "ethers"; import { getSchema } from "./shared/ajv"; import * as utils from "./shared/utils"; -import { SchemaValidationError } from "./shared/utils"; import { validateSchema as validate } from "./shared/validate"; import { SchemaId, WrappedDocument, OpenAttestationDocument, SignedWrappedDocument } from "./shared/@types/document"; import { WrapDocumentOptionV2, WrapDocumentOptionV3 } from "./shared/@types/wrap"; @@ -31,7 +30,7 @@ import { obfuscateVerifiableCredential as obfuscateVerifiableCredentialV4, } from "./4.0/obfuscate"; import { v4Diagnose } from "./4.0/diagnose"; -import { V4WrappedDocument } from "./4.0/types"; +import { V4WrappedDocument, isV4WrappedDocument } from "./4.0/types"; export function wrapDocument( data: T, @@ -66,7 +65,7 @@ export const validateSchema = (document: WrappedDocument): boolean => { return validate(document, getSchema(SchemaId.v2)).length === 0; else if (utils.isWrappedV3Document(document) || document?.version === SchemaId.v3) return validate(document, getSchema(SchemaId.v3)).length === 0; - else if (utils.isWrappedV4OpenAttestationDocument(document)) { + else if (isV4WrappedDocument(document)) { return v4Diagnose({ document, kind: "wrapped", debug: false, mode: "strict" }).length === 0; } @@ -76,7 +75,7 @@ export const validateSchema = (document: WrappedDocument): boolean => { export function verifySignature>(document: T) { if (utils.isWrappedV2Document(document)) return verify(document); else if (utils.isWrappedV3Document(document)) return verifyV3(document); - else if (utils.isWrappedV4OpenAttestationDocument(document)) return verifyV4(document); + else if (isV4WrappedDocument(document)) return verifyV4(document); throw new Error("Unsupported document type: Only OpenAttestation v2, v3 or v4 documents can be signature verified"); } @@ -88,30 +87,20 @@ export function digest(document: OpenAttestationDocumentV3, salts: v3.Salt[], ob ); } -export function obfuscate( - document: WrappedDocument, - fields: string[] | string -): WrappedDocument; -export function obfuscate( - document: WrappedDocument, - fields: string[] | string -): WrappedDocument; -export function obfuscate( +type ObfuscateReturn = T extends V4WrappedDocument ? ObfuscateVerifiableCredentialResult : T; +export function obfuscate>( document: T, fields: string[] | string -): ObfuscateVerifiableCredentialResult; -export function obfuscate(document: WrappedDocument, fields: string[] | string) { - if (utils.isWrappedV2Document(document)) return obfuscateDocumentV2(document, fields); - else if (utils.isWrappedV3Document(document)) return obfuscateVerifiableCredentialV3(document, fields); - else if (utils.isWrappedV4OpenAttestationDocument(document)) return obfuscateVerifiableCredentialV4(document, fields); +): ObfuscateReturn { + if (utils.isWrappedV2Document(document)) return obfuscateDocumentV2(document, fields) as ObfuscateReturn; + else if (utils.isWrappedV3Document(document)) + return obfuscateVerifiableCredentialV3(document, fields) as ObfuscateReturn; + else if (isV4WrappedDocument(document)) + return obfuscateVerifiableCredentialV4(document, fields) as ObfuscateReturn; throw new Error("Unsupported document type: Only OpenAttestation v2, v3 or v4 documents can be obfuscated"); } -export const isSchemaValidationError = (error: any): error is SchemaValidationError => { - return !!error.validationErrors; -}; - export async function signDocument( document: WrappedDocument | SignedWrappedDocument, algorithm: SUPPORTED_SIGNING_ALGORITHM, diff --git a/src/shared/utils/guard.ts b/src/shared/utils/guard.ts index 87af70a6..cfb52b3c 100644 --- a/src/shared/utils/guard.ts +++ b/src/shared/utils/guard.ts @@ -7,7 +7,6 @@ import { OpenAttestationDocument as OpenAttestationDocumentV3, WrappedDocument as WrappedDocumentV3, } from "../../3.0/types"; -import { V4WrappedDocument, V4SignedWrappedDocument, V4OpenAttestationDocument } from "../../4.0/types"; import { diagnose } from "./diagnose"; import { Mode } from "./@types/diagnose"; @@ -35,18 +34,6 @@ export const isRawV3Document = ( return diagnose({ version: "3.0", kind: "raw", document, debug: false, mode }).length === 0; }; -/** - * - * @param document - * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. - */ -export const isRawV4OpenAttestationDocument = ( - document: any, - { mode }: { mode: Mode } = { mode: "non-strict" } -): document is V4OpenAttestationDocument => { - return diagnose({ version: "4.0", kind: "raw", document, debug: false, mode }).length === 0; -}; - /** * * @param document @@ -71,18 +58,6 @@ export const isWrappedV3Document = ( - document: unknown, - { mode }: { mode: Mode } = { mode: "non-strict" } -): document is V4WrappedDocument => { - return diagnose({ version: "4.0", kind: "wrapped", document, debug: false, mode }).length === 0; -}; - /** * * @param document @@ -107,14 +82,8 @@ export const isSignedWrappedV3Document = ( return diagnose({ version: "3.0", kind: "signed", document, debug: false, mode }).length === 0; }; -/** - * - * @param document - * @param mode strict or non-strict. Strict will perform additional check on the data. For instance strict validation will ensure that a target hash is a 32 bytes hex string while non strict validation will just check that target hash is a string. - */ -export const isSignedWrappedV4OpenAttestationDocument = ( - document: unknown, - { mode }: { mode: Mode } = { mode: "non-strict" } -): document is T => { - return diagnose({ version: "4.0", kind: "signed", document, debug: false, mode }).length === 0; -}; +export { + isV4OpenAttestationDocument as isRawV4Document, + isV4WrappedDocument as isWrappedV4Document, + isV4SignedWrappedDocument as isSignedWrappedV4Document, +} from "../../4.0/types"; diff --git a/src/shared/utils/utils.ts b/src/shared/utils/utils.ts index bd947afa..145b9ff5 100644 --- a/src/shared/utils/utils.ts +++ b/src/shared/utils/utils.ts @@ -18,8 +18,8 @@ import { isWrappedV2Document, isRawV3Document, isWrappedV3Document, - isRawV4OpenAttestationDocument, - isWrappedV4OpenAttestationDocument, + isWrappedV4Document, + isRawV4Document, } from "./guard"; import { Version } from "./diagnose"; @@ -31,7 +31,7 @@ export function getIssuerAddress(document: any): any { return document.openAttestationMetadata.proof.value; } // TODO: OA v4 proof schema not updated to support document store issuance yet - // else if (isWrappedV4OpenAttestationDocument(document)) { + // else if (isWrappedV4Document(document)) { // return document.proof.? // } throw new Error( @@ -42,7 +42,7 @@ export function getIssuerAddress(document: any): any { export const getMerkleRoot = (document: any): string => { if (isWrappedV2Document(document)) return document.signature.merkleRoot; else if (isWrappedV3Document(document)) return document.proof.merkleRoot; - else if (isWrappedV4OpenAttestationDocument(document)) return document.proof.merkleRoot; + else if (isWrappedV4Document(document)) return document.proof.merkleRoot; throw new Error( "Unsupported document type: Only can retrieve merkle root from wrapped OpenAttestation v2, v3 & v4 documents." @@ -52,7 +52,7 @@ export const getMerkleRoot = (document: any): string => { export const getTargetHash = (document: any): string => { if (isWrappedV2Document(document)) return document.signature.targetHash; else if (isWrappedV3Document(document)) return document.proof.targetHash; - else if (isWrappedV4OpenAttestationDocument(document)) return document.proof.targetHash; + else if (isWrappedV4Document(document)) return document.proof.targetHash; throw new Error( "Unsupported document type: Only can retrieve target hash from wrapped OpenAttestation v2, v3 & v4 documents." @@ -70,7 +70,7 @@ export const getTemplateURL = (document: any): string | undefined => { else return document.$template?.url; } else if (isRawV3Document(document) || isWrappedV3Document(document)) { return document.openAttestationMetadata.template?.url; - } else if (isRawV4OpenAttestationDocument(document) || isWrappedV4OpenAttestationDocument(document)) { + } else if (isRawV4Document(document) || isWrappedV4Document(document)) { return document.renderMethod && document.renderMethod[0].id; } @@ -80,7 +80,7 @@ export const getTemplateURL = (document: any): string | undefined => { }; export const getDocumentData = (document: WrappedDocument): OpenAttestationDocument => { - if (isWrappedV3Document(document) || isWrappedV4OpenAttestationDocument(document)) { + if (isWrappedV3Document(document) || isWrappedV4Document(document)) { const omit = (keys: any, obj: any): any => Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k))); return omit(["proof"], document); @@ -123,7 +123,7 @@ export const isDocumentRevokable = (document: any): boolean => { !!document.openAttestationMetadata.proof.value; return isDocumentStoreRevokableV3 || isDidRevokableV3; - } else if (isWrappedV4OpenAttestationDocument(document)) { + } else if (isWrappedV4Document(document)) { if (typeof document.issuer === "string" || !document.credentialStatus) return false; const isDidRevokableV4 = document.issuer.identityProof?.identityProofType === "DNS-DID" @@ -170,7 +170,7 @@ export const isObfuscated = ( return !!document.privacy?.obfuscatedData?.length; } else if (isWrappedV3Document(document)) { return !!document.proof.privacy.obfuscated.length; - } else if (isWrappedV4OpenAttestationDocument(document)) { + } else if (isWrappedV4Document(document)) { return !!document.proof.privacy.obfuscated.length; } @@ -189,7 +189,7 @@ export const getObfuscatedData = ( return document.privacy?.obfuscatedData || []; } else if (isWrappedV3Document(document)) { return document.proof.privacy.obfuscated || []; - } else if (isWrappedV4OpenAttestationDocument(document)) { + } else if (isWrappedV4Document(document)) { return document.proof.privacy.obfuscated || []; }