Skip to content

Instantly share code, notes, and snippets.

@kamescg
Created January 13, 2023 17:30
Show Gist options
  • Save kamescg/ec269c6bf29b598a91fe2b62886895b5 to your computer and use it in GitHub Desktop.
Save kamescg/ec269c6bf29b598a91fe2b62886895b5 to your computer and use it in GitHub Desktop.
vereifyVC
import { getResolver } from '@ceramicnetwork/3id-did-resolver';
import { Caip10Link } from '@ceramicnetwork/stream-caip10-link';
import {
recoverTypedSignature,
SignTypedDataVersion,
} from '@metamask/eth-sig-util';
import { decodeJWT, verifyJWS } from 'did-jwt';
import { Resolver } from 'did-resolver';
import {
Missing712DomainException,
Missing712ProofException,
SignatureMismatchException,
} from '@/errors';
// import { ceramicHttpClient } from '@/ceramic';
import { ceramicHttpClient } from '../ceramic';
import { ACCOUNT_ID_SUFFIX } from '../constants';
const JWT_REGEX = /^([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)$/;
const VC_EXAMPLE =
'{"@context":["https://www.w3.org/2018/credentials/v1"],"credentialSchema":{"id":"https://raw.githubusercontent.com/discoxyz/disco-schemas/main/json/OfficialDisconautCredential/1-0-0.json","type":"JsonSchemaValidator2018"},"credentialSubject":{"id":"did:3:kjzl6cwe1jw149lm679w23b6waiflhhcl2129wr1lbh3nfz2cgpu3br8b1ljwnk"},"id":"did:3:kjzl6cwe1jw14a7u9sx3thx9gg9uh7u5tqjkzcnr5pi5zzkap7kiztgsfhzayzt#f560a30c-421f-4a7b-a12c-7dcb0e958859","issuanceDate":"2023-01-09T23:48:09.889Z","issuer":{"id":"did:3:kjzl6cwe1jw14a7u9sx3thx9gg9uh7u5tqjkzcnr5pi5zzkap7kiztgsfhzayzt"},"type":["VerifiableCredential","OfficialDisconautCredential"],"genId":"e0c6aeea-ad3d-497c-be2c-9942fda945cc","proof":{"jwt":"eyJraWQiOiJkaWQ6MzpranpsNmN3ZTFqdzE0YTd1OXN4M3RoeDlnZzl1aDd1NXRxamt6Y25yNXBpNXp6a2FwN2tpenRnc2ZoemF5enQ_dmVyc2lvbi1pZD0wI3BhRGp4Q2VnQjZaeU02SiIsImFsZyI6IkVTMjU2SyJ9.eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2Rpc2NveHl6L2Rpc2NvLXNjaGVtYXMvbWFpbi9qc29uL09mZmljaWFsRGlzY29uYXV0Q3JlZGVudGlhbC8xLTAtMC5qc29uIiwidHlwZSI6Ikpzb25TY2hlbWFWYWxpZGF0b3IyMDE4In0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImlkIjoiZGlkOjM6a2p6bDZjd2UxancxNDlsbTY3OXcyM2I2d2FpZmxoaGNsMjEyOXdyMWxiaDNuZnoyY2dwdTNicjhiMWxqd25rIn0sImlkIjoiZGlkOjM6a2p6bDZjd2UxancxNGE3dTlzeDN0aHg5Z2c5dWg3dTV0cWpremNucjVwaTV6emthcDdraXp0Z3NmaHpheXp0I2Y1NjBhMzBjLTQyMWYtNGE3Yi1hMTJjLTdkY2IwZTk1ODg1OSIsImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDEtMDlUMjM6NDg6MDkuODg5WiIsImlzc3VlciI6eyJpZCI6ImRpZDozOmtqemw2Y3dlMWp3MTRhN3U5c3gzdGh4OWdnOXVoN3U1dHFqa3pjbnI1cGk1enprYXA3a2l6dGdzZmh6YXl6dCJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT2ZmaWNpYWxEaXNjb25hdXRDcmVkZW50aWFsIl19.tFc2yNbsLxA8nWCqcSOHUFTFD5xwJ6udaKtw9iZ0QAoUlvmq7ETFSqykx0nxPwWIu4G_0zHTy7l_QuhcEyNb_Q"},"isPublic":false,"recipient":"did:3:kjzl6cwe1jw149lm679w23b6waiflhhcl2129wr1lbh3nfz2cgpu3br8b1ljwnk","updatedAt":"2023-01-09T23:48:09.889Z"}';
export async function retrieveDidDocument(did: string) {
const threeidresolver = getResolver(ceramicHttpClient);
const resolver = new Resolver(threeidresolver);
const doc = await resolver.resolve(did);
return doc;
}
export async function verifyJwtVc(vc: string) {
const TypedData = JSON.parse(vc);
console.log(TypedData, 'TypedData');
const decoded = decodeJWT(TypedData.proof.jwt).payload;
if (!decoded || !decoded?.issuer) throw new Error('Decoding JWT');
const issuer =
typeof decoded.issuer === 'string' ? decoded.issuer : decoded.issuer.id;
const doc = await retrieveDidDocument(issuer);
if (!doc.didDocument || !doc.didDocument.verificationMethod)
throw new Error('Could not fetch did doc');
const verified = await verifyJWS(vc, doc.didDocument?.verificationMethod!);
return !!verified;
}
export async function verify712Vc(vc: string) {
try {
const TypedData = JSON.parse(vc);
if (!TypedData.proof || !TypedData.proof.proofValue)
throw new Missing712ProofException();
if (
!TypedData.proof.eip712Domain ||
!TypedData.proof.eip712Domain.messageSchema ||
!TypedData.proof.eip712Domain.domain
)
throw new Missing712DomainException();
const { proof, ...signingInput } = TypedData;
const { proofValue, eip712Domain, ...verifyInputProof } = proof;
const verificationMessage = {
...signingInput,
proof: verifyInputProof,
};
const objectToVerify = {
message: verificationMessage,
domain: eip712Domain.domain,
types: eip712Domain.messageSchema,
primaryType: eip712Domain.primaryType,
};
const recovered = recoverTypedSignature({
data: objectToVerify,
signature: proofValue,
version: SignTypedDataVersion.V4,
});
// Get did from address using CAIP 10
const { did } = await Caip10Link.fromAccount(
ceramicHttpClient,
recovered + ACCOUNT_ID_SUFFIX
);
if (did === signingInput.issuer.id) {
return TypedData;
}
// @ts-ignore
throw new SignatureMismatchException(did, signingInput.issuer.id);
} catch (e: any) {
console.log(e);
throw e;
}
}
// @ts-ignore
export async function verifyEIP712VerifiableCredentialV2(_vc: string) {
return VC_EXAMPLE.match(JWT_REGEX)
? verifyJwtVc(VC_EXAMPLE)
: verify712Vc(VC_EXAMPLE);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment