Skip to content

Commit

Permalink
add did resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
volodymyr-basiuk committed Nov 7, 2024
1 parent 5d9d8a4 commit 21cc80a
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 20 deletions.
37 changes: 28 additions & 9 deletions src/iden3comm/handlers/payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
SupportedPaymentProofType
} from '../../verifiable';
import { Signer, ethers } from 'ethers';
import { Resolvable } from 'did-resolver';

/**
* @beta
Expand Down Expand Up @@ -63,7 +64,8 @@ export function createPaymentRequest(
}

export async function verifyEIP712TypedData(
data: Iden3PaymentRailsRequestV1 | Iden3PaymentRailsERC20RequestV1
data: Iden3PaymentRailsRequestV1 | Iden3PaymentRailsERC20RequestV1,
resolver: Resolvable
): Promise<string> {
const paymentData =
data.type === PaymentRequestDataType.Iden3PaymentRailsRequestV1
Expand All @@ -86,12 +88,28 @@ export async function verifyEIP712TypedData(
const typesFetchResult = await fetch(proof.eip712.types);
const types = await typesFetchResult.json();
delete types.EIP712Domain;
const signer = ethers.verifyTypedData(proof.eip712.domain, types, paymentData, proof.proofValue);
const verificationMethodSigner = proof.verificationMethod.split('#')[0].split(':').slice(-1)[0];
if (signer !== verificationMethodSigner) {
throw new Error(`failed request. invalid signature`);
const recovered = ethers.verifyTypedData(
proof.eip712.domain,
types,
paymentData,
proof.proofValue
);

const { didDocument } = await resolver.resolve(proof.verificationMethod);
if (didDocument?.verificationMethod) {
for (const verificationMethod of didDocument.verificationMethod) {
if (
verificationMethod.blockchainAccountId?.split(':').slice(-1)[0].toLowerCase() ===
recovered.toLowerCase()
) {
return recovered;
}
}
} else {
throw new Error('resolver_error: issuer DIDDocument does not contain any verificationMethods');
}
return signer;

throw new Error(`failed request. signature verification failed`);
}

/**
Expand Down Expand Up @@ -225,6 +243,7 @@ export type PaymentHandlerOptions = {
/** @beta PaymentHandlerParams represents payment handler params */
export type PaymentHandlerParams = {
packerParams: PackerParams;
documentResolver: Resolvable;
multiChainPaymentConfig?: MultiChainPaymentConfig[];
/*
* allowed signers for payment request (if not provided, any signer is allowed)
Expand Down Expand Up @@ -506,7 +525,7 @@ export class PaymentHandler
type: SupportedPaymentProofType.EthereumEip712Signature2021,
proofPurpose: 'assertionMethod',
proofValue: signature,
verificationMethod: `did:pkh:eip155:${chainId}:${await signer.getAddress()}#blockchainAccountId`,
verificationMethod: `did:pkh:eip155:${chainId}:${await signer.getAddress()}`,
created: new Date().toISOString(),
eip712: {
types: typeUrl,
Expand Down Expand Up @@ -585,7 +604,7 @@ export class PaymentHandler
if (data.expirationDate && new Date(data.expirationDate) < new Date()) {
throw new Error(`failed request. expired request`);
}
const signer = await verifyEIP712TypedData(data);
const signer = await verifyEIP712TypedData(data, this._params.documentResolver);
if (this._params.allowedSigners && !this._params.allowedSigners.includes(signer)) {
throw new Error(`failed request. signer is not in the allowed signers list`);
}
Expand All @@ -611,7 +630,7 @@ export class PaymentHandler
throw new Error(`failed request. expired request`);
}

const signer = await verifyEIP712TypedData(data);
const signer = await verifyEIP712TypedData(data, this._params.documentResolver);
if (this._params.allowedSigners && !this._params.allowedSigners.includes(signer)) {
throw new Error(`failed request. signer is not in the allowed signers list`);
}
Expand Down
44 changes: 33 additions & 11 deletions tests/handlers/payment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
registerKeyProvidersInMemoryKMS,
createIdentity,
SEED_USER,
SEED_ISSUER,
WALLET_KEY,
RPC_URL
} from '../helpers';
Expand All @@ -53,6 +52,7 @@ import {
import { Contract, ethers, JsonRpcProvider } from 'ethers';
import fetchMock from '@gr2m/fetch-mock';
import { fail } from 'assert';
import { DIDResolutionResult } from 'did-resolver';

describe('payment-request handler', () => {
let packageMgr: IPackageManager;
Expand Down Expand Up @@ -463,8 +463,7 @@ describe('payment-request handler', () => {
proofPurpose: 'assertionMethod',
proofValue:
'0x756e11c55fe8f4d2867c2e14e52a06baba29e4b789b4521aafa1623ad96c67aa23dc042bfebd4711ed2db5f145c853a5487b878d8e113e1ede0c41553f6318dd1c',
verificationMethod:
'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId',
verificationMethod: 'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a',
created: new Date().toISOString(),
eip712: {
types: 'https://schema.iden3.io/core/json/Iden3PaymentRailsRequestV1.json',
Expand Down Expand Up @@ -510,8 +509,7 @@ describe('payment-request handler', () => {
proofPurpose: 'assertionMethod',
proofValue:
'0xcb5a8d39a536768fabaafbf17f24954acf7c7d7a6f9a8b75ad5f9c29d324cdaf63de8cebfde508a5a03b60e1a4b765b21f7f3cd60dfed27ce5208432e3fd4c481b',
verificationMethod:
'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId',
verificationMethod: 'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a',
created: new Date().toISOString(),
eip712: {
types: 'https://schema.iden3.io/core/json/Iden3PaymentRailsERC20RequestV1.json',
Expand Down Expand Up @@ -553,6 +551,34 @@ describe('payment-request handler', () => {
const idWallet = new IdentityWallet(kms, dataStorage, credWallet);

const proofService = new ProofService(idWallet, credWallet, circuitStorage, MOCK_STATE_STORAGE);
const didExampleRecovery = {
'@context': [
'https://www.w3.org/ns/did/v1',
{
EcdsaSecp256k1RecoveryMethod2020:
'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#EcdsaSecp256k1RecoveryMethod2020',
blockchainAccountId: 'https://w3id.org/security#blockchainAccountId'
}
],
id: 'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a',
verificationMethod: [
{
id: 'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId',
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: 'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a',
blockchainAccountId: 'eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a'
}
],
authentication: [
'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId'
],
assertionMethod: [
'did:pkh:eip155:80002:0xE9D7fCDf32dF4772A7EF7C24c76aB40E4A42274a#blockchainAccountId'
]
};
const resolveDIDDocument = {
resolve: () => Promise.resolve({ didDocument: didExampleRecovery } as DIDResolutionResult)
};
packageMgr = await getPackageMgr(
await circuitStorage.loadCircuitData(CircuitId.AuthV2),
proofService.generateAuthV2Inputs.bind(proofService),
Expand All @@ -562,6 +588,7 @@ describe('payment-request handler', () => {
packerParams: {
mediaType: MediaType.PlainMessage
},
documentResolver: resolveDIDDocument,
multiChainPaymentConfig: [
{
chainId: '80002',
Expand Down Expand Up @@ -590,12 +617,7 @@ describe('payment-request handler', () => {
});

userDID = userIdentity.did;

const issuerIdentity = await createIdentity(idWallet, {
seed: SEED_ISSUER
});

issuerDID = issuerIdentity.did;
issuerDID = DID.parse('did:iden3:polygon:amoy:x6x5sor7zpyZX9yNpm8h1rPBDSN9idaEhDj1Qm8Q9');

agentMessageResponse = createProposal(issuerDID, userDID, []);
fetchMock.spy();
Expand Down

0 comments on commit 21cc80a

Please sign in to comment.