Coder Social home page Coder Social logo

sphereon-opensource / pex Goto Github PK

View Code? Open in Web Editor NEW
38.0 6.0 15.0 3.53 MB

A Typescript implementation of the DIF Presentation Exchange specification.

License: Apache License 2.0

JavaScript 0.03% TypeScript 99.97%
typescript dif presentation-exchange verifiable-presentation

pex's Introduction


Sphereon
Presentation Exchange v1 and v2
TypeScript Library

CI codecov NPM Version

Background

The Presentation Exchange (PEX) Library implements the functionality described in the DIF Presentation Exchange specification for both version 1 and 2.

Sphereon's PEX Library is useful for both verifier systems and holders (e.g. wallets) and can be used in client side browsers and mobile applications as well as on server side technology such as REST APIs (e.g. built with NodeJS). It allows anyone to add DIF Presentation Exchange logic to their existing wallets, agents and/or verifiers, without making any further assumptions about technologies like cryptography, credential representations used in their products.

A Presentation Exchange generally goes as follows; The verifier creates a Presentation Definition asking for credentials from the holder. The Presentation Definition for the credentials is sent to the holder, who returns a Verifiable Presentation containing Presentation Submission data that links the Credentials in the Presentation to the received Definition as a response. The Presentation Submission describes the relationship between the Verifiable Presentation and the Presentation Definition. It can either be part of the Verifiable Presentation or be external, like in OpenID4VC specifications. Now the verifier will verify the Verifiable Presentation by checking the signature and other accompanying proofs as well as ensuring the Submission Data fulfills the requirements from the specification.

Presentation Exchange will ensure that the model used by the verifier, can be interpreted by the holder. It then ensures that the correct parts from the holders credentials are used to create the presentation. The PEX-library contains all the logic to interpret the models, therefore removing the need for the verifier and holder to align their specific models.

The Typescript data objects (models) used in PEX are generated from Sphereon's DIF PEX OpenAPI Spec component. The code for the component can be found at PEX-OpenAPI github repository. This allows the generation of the objects in many programming languages and frameworks consistently by configuring the maven plugin.

WARNING: Please be aware that this library does not support the latest V2 specification!. Support will be added as part of a V3 major version of this library

The PEX Library supports the following actions:

  • Creating a presentation definition / request
  • Validating a presentation definition / conforming to the specifications v1 and v2
  • Creating a Presentation
  • Creating a Verifiable Presentation using a callback function
  • Validating a presentation (submission) when received
  • Input evaluations: Verification of presentation submissions conforming to the presentation definition
  • Utilities: to build and use different models compliant with the DIF Presentation Exchange v2.0.0 specification.
  • Support for DIF Presentation Exchange v1.0.0 specification.

Stateful storage, signature support or credential management should be implemented in separate libraries/modules that make use of this library. By keeping these separate, the PEX library will stay platform-agnostic and lean with respect to dependencies.

For PEX Users

The library can be installed directly from npmjs via:

# install via yarn
  yarn add @sphereon/pex

# install via npm
  npm install @sphereon/pex

The core functionality of the DIF Presentation Exchange can be outlined as follows:

Verifier: Create a Presentation Definition object:

Presentation Definitions are objects that articulate what proofs a Verifier requires. These help the Verifier to decide how or whether to interact with a Holder. Presentation Definitions are composed of inputs, which describe the forms and details of the proofs they require, and optional sets of selection rules, to allow Holders flexibility in cases where different types of proofs may satisfy an input requirement. PEX library supports two versions of presentation_definition object. The details of it can be found in @spehereon/pex-models below you can find some tips about querying via a presentation_definition object:

  • Using the constraint field:
    • You can use the constraint field for creating your query:
constraints: {
  fields: [
    {
      path: ['$.credentialSubject.role'],
      filter: {
        type: 'string',
        const: 'admin'
      }
    }
  ];
}
  • for special cases, like querying fields that start with @ you can use the following syntax:
    • You can use the following syntax, PEX will change it to correct query itself:
path: ['$.@context', '$.vc.@context']

For querying the arrays, right now we don't support the json-schema fully, but we do support the following syntax:

  • using [*] like:
{
  fields: [
    {
      path: [
        '$.type.[*]'
      ],
      filter: {
        type: 'string',
        pattern: 'AlumniCredential'
      }
    }
  ]
}
  • using .* like:
{
  fields: [
    {
      path: [
        '$.type.*'
      ],
      filter: {
        type: 'string',
        pattern: 'AlumniCredential'
      }
    }
  ]
}
  • using type: array and contains keyword. PEX currently doesn't support this syntax fully, but if you don't rely on our versionDiscovery functionality and call the specific version of PEX (PEXv1 or PEXv2) yourself, you can use this syntax as well.
{
  "fields": [
    {
      "path": [
        "$.type"
      ],
      "filter": {
        "type": "array",
        "contains": {
          "enum": [
            "https://example.com/type"
          ]
        }
      }
    }
  ]
}

Verifier: Input Evaluation

Input evaluation is the primary mechanism by which a verifier determines whether a Verifiable Presentation and Presentation Submission from a holder matches the requested presentation definition from the request. Obviously a holder/wallet could also use the method to verify whether its submission would be valid, before contacting the verifier.

import { PEX } from '@sphereon/pex';

const pex: PEX = new PEX();

// Example of Presentation Definition V1 (notice the required schema for V1)
const presentationDefinitionV1 = {
  "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
  "input_descriptors": [
    {
      "id": "wa_driver_license",
      "name": "Washington State Business License",
      "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference",
      "schema": [{
        "uri": "https://licenses.example.com/business-license.json"
      }]
    }
  ]
};

// Example of Presentation Definition V2
const presentationDefinitionV2 = {
  "id": "32f54163-7166-48f1-93d8-ff217bdb0653",
  "input_descriptors": [
    {
      "id": "wa_driver_license",
      "name": "Washington State Business License",
      "purpose": "We can only allow licensed Washington State business representatives into the WA Business Conference"
    }
  ]
};

const verifiablePresentation = {
  '@context': [
    "https://www.w3.org/2018/credentials/v1",
    "https://identity.foundation/presentation-exchange/submission/v1"
  ],
  type: [
    "VerifiablePresentation",
    "PresentationSubmission"
  ],
  presentation_submission: { ... },
  verifiableCredential: [...],
  proof: { ... }
}

// We are using the PEX class, to automatically detect the definition version. PEXv1 and PEXv2 are also available to use fixed PEX versions
const { value, warnings, errors } = pex.evaluatePresentation(presentationDefinitionV2, verifiablePresentation);

Holder: Credential Query

A credential query allows holders to filter their set of credentials for matches to a given presentation definition. This filters out any non-matching Credentials that are passed in. Please note that multiple credentials could be satisfying the definition.

import { PEX } from '@sphereon/pex';
import { IVerifiableCredential } from "./SSI.types";

const pex: PEX = new PEX();

// Definition from verifier request
const presentationDefinition = {
  ...
};
// Finding out which version presentationDefinition is this:
// The result is either 'v1', 'v2' or an error
// You only have to do this if you want to apply some custom logic. The PEX class uses feature detection on the definition to determine whether it is v1 or v2 internally
const result = PEX.definitionVersionDiscovery(pdSchema);

// Example for loading credentials from your secure storage
const credentials: IVerifiableCredential[] = await secureStore.getCredentials();

// Find matching credentials
const srMatches = pex.selectFrom(presentationDefinition, credentials, { holderDIDs: [holderDID] });

// An example that selects the first 'count' credentials from
// the matches. in a real scenario, the user has to select which
// credentials to use. PEX did the first filtering,
// but there still could be multiple credentials satisfying a presentation definition
const selectedCredentials = srMatches.map(
  ({ matches, count }) => matches.slice(0, count)
).flat();

Holder: Presentation creation (non-verifiable)

To create a Presentation without Proof (for Proofs, see Verifiable Presentation below) you have to pass in the Presentation Definition, selected Verifiable Credentials and an optional holder (DID). The result will be a Verifiable Presentation, without proofs, so actually a Presentation. It also contains the presentation submission data that the verifier can use.

It is left up to you to sign the Presentation and adding the proof and make it a truly Verifiable Presentation. There are different libraries that allow you to do this. You can also use the callback integration mentioned in the next chapter for this.

import { PEX, IPresentation, PresentationResult } from '@sphereon/pex';

const pex: PEX = new PEX();

// Construct presentation from selected credentials
const presentationResult: PresentationResult = pex.presentationFrom(presentationDefinition, selectedCredentials, { holderDIDs: [holderDID] });
const presentation = presentationResult.presentation
/** presentation object:
 *
 *   {
 *     "@context": [
 *       "https://www.w3.org/2018/credentials/v1",
 *       "https://identity.foundation/presentation-exchange/submission/v1"
 *     ],
 *     "type": [
 *       "VerifiablePresentation",
 *       "PresentationSubmission"
 *     ],
 *     presentation_submission: presentationSubmission,
 *     verifiableCredential: selectedCredentials
 *   };
 */
// Presentation would need to be signed and sent to verifier

Holder: Verifiable Presentation with callback

NOTE: PEX does not support the creation of signatures by itself. That has to do with the fact that we didn't want to rely on all kinds of signature suites and libraries. PEX has minimal dependencies currently, so that it can be used in all kinds of scenarios.

How did we solve this? We have created a callback mechanism, allowing you to supply a callback function that gets all input allowing you to use your library of choice to create the signature. The callback needs to accept a PresentationSignCallBackParams object.

NOTE: The method verifiablePresentationFrom accepts the presentation definition and selected Verifiable Credentials as the first two arguments, just like the presentationFrom method. Next it accepts the callback function as argument and a VerifiablePresentationFromOpts object as last argument. The sign callback params, allow you to control the signature process. You will have access in the callback to these params as well.

Before calling your callback function a few things happen. First, just like the presentationFrom method, it will evaluate whether the supplied credentials conform to the supplied presentation definition. Then it creates a presentation, just like presentationFrom. This presentation is provided for your convenience and can be used in your callback for simple use cases. In more elaborate cases, like for instance with more complex signature suites and/or selective disclosure, you will probably not use the IPresentation directly and make use of other arguments passed into the callback, like the EvaluationResults, PresentationSubmission and Partial<IProof>.

The proofOptions and signatureOptions, allow you to populate proof values directly. in which case the Partial<IProof> will have all fields filled to just add it as a proof to the presentation in your callback. This does mean you would have to create the IPresentation first and sign that, which means you probably have no use for the callback. If you do not provide these values, the Partial<IProof>, will still be populated without the proofValue and jws, based upon your options.

Presentation Sign Options

The options accepted by the verifiablePresentationFrom are:

interface VerifiablePresentationFromOpts {
  /**
   * The optional holderDID of the presentation
   */
  holderDID?: string;

  /**
   * The presentation submission data location.
   *
   * Can be External, which means it is only returned and not embedded into the VP,
   * or Presentation, which means it will become part of the VP
   */
  presentationSubmissionLocation?: PresentationSubmissionLocation;

  /**
   * A base presentation payload. Can be used to provide default values. Be aware that any verifiable credential will always be overwritten
   */
  basePresentationPayload?: IPresentation;

  /**
   * IProof options
   */
  proofOptions?: ProofOptions;

  /**
   * The signature options
   */
  signatureOptions?: SignatureOptions;
}

interface ProofOptions {
  /**
   * The signature type. For instance RsaSignature2018
   */
  type?: ProofType | string;

  /**
   * Type supports selective disclosure?
   */
  typeSupportsSelectiveDisclosure?: boolean;

  /**
   * A challenge protecting against replay attacks
   */
  challenge?: string;

  /**
   * A domain protecting against replay attacks
   */
  domain?: string;

  /**
   * The purpose of this proof, for instance assertionMethod or authentication, see https://www.w3.org/TR/vc-data-model/#proofs-signatures-0
   */
  proofPurpose?: ProofPurpose | string;

  /**
   * The ISO8601 date-time string for creation. You can update the IProof value later in the callback. If not supplied the current date/time will be used
   */
  created?: string;

  /**
   * Similar to challenge. A nonce to protect against replay attacks, used in some ZKP proofs
   */
  nonce?: string;
}

interface SignatureOptions {
  /**
   * The private key
   */
  privateKey?: string;

  /**
   * Key encoding
   */
  keyEncoding?: KeyEncoding;

  /**
   * The verification method value
   */
  verificationMethod?: string;

  /**
   * Can be used if you want to provide the Json-ld proof value directly without relying on the callback function generating it
   */
  proofValue?: string; // One of any number of valid representations of proof values

  /**
   * Can be used if you want to provide the JSW proof value directly without relying on the callback function generating it
   */
  jws?: string; // JWS based proof
}

These options are available in your callback function by accessing the options field in the PresentationSignCallBackParams.

Callback params object

The callback params gets supplied as the single argument to your callback function. It contains the Presentation, a partial 'Proof' typically missing the proofValue/jws signature. It also contains the initially supplied Verifiable Credentials and Presentation Definition as well as your supplied options.

If contains the Presentation Submission object, which is also found in the presentation. You can use this to create your own IPresentation object if you want. Lastly it contains the evaluation results, which includes the mappings and logs about the evaluation.

You can either choose to use the Presentation and partial Proof together with the options, or in more elaborate use cases opt to use the PresentationSubmission, EvaluationResults and the options for instance.

export interface PresentationSignCallBackParams {
  /**
   * The originally supplied presentation sign options
   */
  options: VerifiablePresentationFromOpts;

  /**
   * The presentation definition
   */
  presentationDefinition: PresentationDefinition;

  /**
   * The selected credentials to include in the eventual VP as determined by PEX and/or user
   */
  selectedCredentials: (IVerifiableCredential | JwtWrappedVerifiableCredential | string)[];

  /**
   * The presentation object created from the definition and verifiable credentials.
   * Can be used directly or in more complex situations can be discarded by using the definition, credentials, proof options, submission and evaluation results
   */
  presentation: IPresentation;

  /**
   * A partial proof value the callback can use to complete. If proofValue or JWS was supplied the proof could be complete already
   */
  proof: Partial<IProof>;

  /**
   * The presentation submission data, which can also be found in the presentation itself
   */
  presentationSubmission: PresentationSubmission;

  /**
   * The evaluation results, which the callback function could use to create a VP using the proof(s) using the supplied credentials
   */
  evaluationResults: EvaluationResults;
}

Simple example of the callback function

A simple use case using your library of choice for non-selective disclosure using an ed25519 key and signature.

import {
  PEX,
  IProof,
  ProofPurpose,
  ProofType,
  IVerifiablePresentation,
  PresentationSignOptions,
  KeyEncoding,
} from '@sphereon/pex';

const pex: PEX = new PEX();

const params: VerifiablePresentationFromOpts = {
  holderDID: 'did:example:1234....',
  proofOptions: {
    type: ProofType.Ed25519Signature2018,
    proofPurpose: ProofPurpose.assertionMethod,
  },
  signatureOptions: {
    verificationMethod: 'did:example:"1234......#key',
    keyEncoding: KeyEncoding.Base58,
    privateKey: 'base58 (key encoding type) key here',
  },
};

const vp = await pex.verifiablePresentationFrom(
  presentationDefinition,
  selectedCredentials,
  simpleSignedProofCallback,
  params
);

function simpleSignedProofCallback(callBackParams: PresentationSignCallBackParams): IVerifiablePresentation {
  // Prereq is properly filled out `proofOptions` and `signatureOptions`, together with a `proofValue` or `jws` value.
  // And thus a generated signature
  const { presentation, proof, options } = callBackParams; // The created partial proof and presentation, as well as original supplied options
  const { signatureOptions, proofOptions } = options; // extract the orignially supploed signature and proof Options
  const privateKeyBase58 = signatureOptions.privateKey; // Please check keyEncoding from signatureOptions first!

  /**
   * IProof looks like this:
   * {
   *    type: 'Ed25519Signature2018',
   *    created: '2021-12-01T20:10:45.000Z',
   *    proofPurpose: 'assertionMethod',
   *    verificationMethod: 'did:example:"1234......#key',
   *    .....
   * }
   */

    // Just an example. Obviously your lib will have a different method signature
  const vp = myVPSignLibrary(presentation, { ...proof, privateKeyBase58 });

  return vp;
}

Utilities

In addition to the core functionality above, the underlying validation methods are exposed as low-level helper functions.

import { PEX } from '@sphereon/pex';

const presentationDefinition = {
  ...
};

const result = PEX.definitionVersionDiscovery(presentationDefinition);
const { warnings: pdWarnings, errors: pdErrors } = PEX.validateDefinition(presentationDefinition);

const presentationSubmission = {
  ...
};

const { warnings: psWarnings, errors: psErrors } = PEX.validateSubmission(presentationSubmission);

API

Evaluate

PEX.evaluatePresentation(presentationDefinition, verifiablePresentation);
PEXv1.evaluatePresentation(presentationDefinition, verifiablePresentation);
PEXv2.evaluatePresentation(presentationDefinition, verifiablePresentation);
Description

These three methods are quite similar. The first One receives a presentation definition object, decides the version based upon feature detection and acts accordingly. The other two are specific to their Presentation Exchange definition version.

For more detailed difference between v1 and v2 please read the From V1 to V2 section.

Evaluates whether a presentation submission meets the requested presentation definition Since this method will be used both before and after creating a VerifiablePresentation, we accept both signed and unsigned version of a presentation here.

Parameters

name type description
presentationDefinition PresentationDefinition the presentation definition that initiated the request from the verifier
presentation IPresentation the Presentation object containing the required credentials and a presentation_submission object mapping back to the presentation definition

Return value

If evaluation is successful, value will be a non-null PresentationSubmission mapping the submitted credentials to the requested inputs.

interface EvaluationResults {
  value?: PresentationSubmission;
  warnings?: string[];
  errors?: Error[];
  verifiableCredential: (IVerifiableCredential | JwtWrappedVerifiableCredential | string)[];
}

SelectFrom

PEX.selectFrom(presentationDefinition, credentials, { holderDIDs });
PEXv1.selectFrom(presentationDefinitionV1, credentials, { holderDIDs });
PEXv2.selectFrom(presentationDefinitionV2, credentials, { holderDIDs });
Description

These three methods are quite similar. The first One receives a presentation definition object, decides the version based upon feature detection and acts accordingly. The other two are specific to their version.

For more detailed difference between v1 and v2 of the spec please read the From V1 to V2 section.

Gathers the matching credentials that fit a given presentation definition. Please note that there could be multiple results fitting the same criteria. This basically only filters out the credentials that do not match the definition. You, or rather the user, typically has to do a final selection.

selectFrom Parameters

name type description
presentationDefinition PresentationDefinition the presentation definition that initiated the request from the verifier
credentials (IVerifiableCredential or JwtWrappedVerifiableCredential or string)[] the array of verifiable credentials to select from
{ holderDIDs } string[] the holder's DIDs. this can be found in VerifiablePresentation's holder property note that a wallet can have many holderDIDs retrieved from different places

Return value

  • If the selection was successful or partially successful, the matches array will consist of SubmissionRequirementMatch object(s), representing the matching credentials for each SubmissionRequirement in the presentationDefinition input parameter.
  • If the selection was not successful, the errors array will consist of Checked object(s), representing what has failed in your selection process.
import { Status } from './ConstraintUtils';

interface SelectResults {
  errors?: Checked[];
  matches?: SubmissionRequirementMatch[];
  /**
   * This is the parameter that the PEX library user should look into to determine what to do next
   * Status can have three values:
   *  1. INFO: everything is fine, you can call `presentationFrom` after this method
   *  2. WARN: method was called with more credentials than required.
   *       To enhance credential holderDID's privacy it is recommended to select credentials which are absolutely required.
   *  3. Error: the credentials you've sent didn't satisfy the requirement defined presentationDefinition object. Do not submit!
   */
  areRequiredCredentialsPresent: Status;
  /**
   * All matched/selectable credentials
   */
  verifiableCredential?: (IVerifiableCredential | JwtWrappedVerifiableCredential | string)[];
  /**
   * Following are indexes of the verifiableCredentials passed to the selectFrom method that have been selected.
   */
  vcIndexes?: number[];
  warnings?: Checked[];
}

interface SubmissionRequirementMatch {
  name?: string;
  rule: Rules;
  min?: number;
  count?: number;
  max?: number;
  vc_path: string[];
  from?: string[];
  from_nested?: SubmissionRequirementMatch[]; // VerifiableCredential Address
}

PresentationFrom

PEX.presentationFrom(presentationDefinition, selectedCredentials, { holderDID });
PEXv1.presentationFrom(presentationDefinitionV1, selectedCredentials, { holderDID });
PEXv2.presentationFrom(presentationDefinitionV2, selectedCredentials, { holderDID });
Description

These three methods are quite similar. The first One receives a presentation definition object, decides the version based upon feature detection and acts accordingly. The other two are specific to their version.

**For more detailed difference between v1 and v2 specification please read the From V1 to V2 section **.

Creates the corresponding Presentation Submission object to be included in the Verifiable Presentation response, which maps the submitted credentials to the requested inputs in the presentationDefinition input parameter.

presentationFromV1 Parameters

name type description
presentationDefinition PresentationDefinitionV1 the v1 presentation definition that initiated the request from the verifier
selectedCredentials (IVerifiableCredential or JwtWrappedVerifiableCredential or string)[] the array of verifiable credentials that meet the submission requirements in the presentation definition
{ holderDID } string the holder's DID. This can be found in IVerifiablePresentation's holder property note that a wallet can have many holderDIDs retrieved from different places

presentationFromV2 Parameters

name type description
presentationDefinition PresentationDefinitionV2 the v2 presentation definition that initiated the request from the verifier
selectedCredentials (IVerifiableCredential or JwtWrappedVerifiableCredential or string)[] the array of verifiable credentials that meet the submission requirements in the presentation definition
{ holderDID } string the holder's DID. This can be found in IVerifiablePresentation's holder property note that a wallet can have many holderDIDs retrieved from different places

Return value

If the selected credentials successfully match the submission requirements in the presentation definition, the return value will be a non-null 'Presentation' containing a PresentationSubmission

interface PresentationSubmission {
  id?: string;
  definition_id: string;
  descriptor_map: Descriptor[];
}

Validation

PEX.validateDefinition(objToValidate);
PEXv1.validateDefinition(objToValidate);
PEXv2.validateDefinition(objToValidate);
validateSubmission(objToValidate);

Description

A validation utility function for PresentationDefinition and PresentationSubmission objects. If you know the version of your presentation definition you can call version-specific functions. If not you can call the general one (located in PEX) to first determine the version and then validate the presentation definition object against that version's specific rules.

Parameters

name type description
objToValidate PresentationDefinition | PresentationSubmission the presentation definition or presentation submission to be validated

Return value

The validate method returns a validated results array NonEmptyArray<Checked> , with structure:

interface Checked {
  tag: string;
  status: Status;
  message?: string;
}

status can have following values 'info' | 'warn' | 'error'

Definition Version Discovery

PEX.definitionVersionDiscovery(presentationDefinition);

Description

A utility function for PresentationDefinition objects. This method will determine the version of your presentationDefinition object.

Parameters

name type description
presentationDefinition PresentationDefinition the presentation definition that you need to decide the version for

Return value

The definitionVersionDiscovery method returns a version or an error, with following structure:

interface DiscoveredVersion {
  version?: PEVersion;
  error?: string;
}

enum PEVersion {
  v1 = 'v1',
  v2 = 'v2',
}

From V1 to V2

The following changes has been made in the v2 version of the Presentation Exchange specification. V1 is still the most predominant version, because it is typically used quite a bite in OpenID4VC scenarios and interop profiles.

  1. The required schema has been removed altogether from InputDescriptor properties, because it was confusing and redundant.
  2. presentation_definition has another property called frame and if present, its value MUST be a JSON LD Framing Document object. Used for selective disclosure. Although this library does not use it currently, as we can perform it without frames.
  3. filter has several more options for filtering:
    • formatMaximum
    • formatMinimum
    • formatExclusiveMaximum
    • formatExclusiveMinimum

As a result we introduced the PEX class to replace the former PEJS class. This class does feature detection on the presentation definition to determine whether it is a v1 or v2 spec. Then it delegates the functionality to the PEXv1 or PEXv2 class respectively. See also: v0.6 Breaking changes

WARNING: Please be aware that this library does not support the latest V2 specification!. Support will be added as part of a V3 major version of this library

Workflow Diagram

The below diagram shows how a typical interaction between a verifier and a wallet looks like. It makes no assumptions about the actual transport (DIDComm, SIOPv2/OIDC4VP, CHAPI, REST)

Flow diagram

For PEX developers

This project has been created using:

  • yarn version 1.22.5
  • node version >= 16

Install

yarn install

Build

yarn build

Test

The test command runs:

  • eslint
  • prettier
  • unit

You can also run only a single section of these tests, using for example yarn test:unit.

yarn test

Utility scripts

There are several other utility scripts that help with development.

  • yarn fix - runs eslint --fix as well as prettier to fix code style
  • yarn cov - generates code coverage report

Glossary

Term Definition
Credential A set of one or more claims made by an issuer.
Verifiable Credential Is a tamper-evident credential that has authorship that can be cryptographically verified. Verifiable credentials can be used to build Verifiable Presentations, which can also be cryptographically verified. The claims in a credential can be about different subjects. PEX accepts Verifiable credential in 3 forms: 1. JSON_LD which is know in our system as IVerifiableCredential, 2. JWT-Wrapped VC which is known in our system as JwtWrappedVerifiableCredential or string which is a valid Verifiable credential jwt
Presentation Definition Presentation Definitions are objects that articulate what proofs a Verifier requires.
Holder Holders are entities that have one or more verifiable credentials in their possession. Holders are also the entities that submit proofs to Verifiers to satisfy the requirements described in a Presentation Definition.
Holder's Did Unique ID URI string and PKI metadata document format for describing the cryptographic keys and other fundamental PKI values linked to a unique, user-controlled, self-sovereign identifier in holder's wallet
Verifier Verifiers are entities that define what proofs they require from a Holder (via a Presentation Definition) in order to proceed with an interaction.
Issuer A role an entity can perform by asserting claims about one or more subjects, creating a verifiable credential from these claims, and transmitting the verifiable credential to a holder.
Presentation Data derived from one or more verifiable credentials, issued by one or more issuers
Verifiable Presentation Is a tamper-evident presentation encoded in such a way that authorship of the data can be trusted after a process of cryptographic verification.

Further work:

This implementation fully supports V1 and Vpartially 2 of the Presentation Exchange specification with the following exception:

  • Support for hashlink verification in the schema part of the V1 specification is not fully incorporated as it depends on missing external library support. We generate a warning about the missing verification if we encounter them.
  • Support for complete json-schema in pex-models
    • which means if you're relying on our version-discovery feature (with calling general PEX) and sending presentation_definition object with unsupported fields, you'll get an exception. However if you call specific version of PEX (PEXv1 or PEXv2) it will work.
  • The V2 implementation does not contain latest V2 changes. These will become available in V3 of this library

Breaking changes

v2.0.0 Argument and result objects changed slightly

In V2 of the library we have done some refactoring on the PEX external interfaces, to make them more uniform.

Optional arguments for all PEX methods are now part of an optional opts Object argument. This allows for cleaner future extensions, and also solves the problem of optional argument order in case multiple optional arguments are needed.

Certain non state related methods part of the PEX/PEXv1/PEXv2 classes have moved to static methods, to make the distinction between state related methods and non-state related methods more clear.

presentationFrom and verifiablePresentationFrom now return a result object instead of directly returning a ( Verifiable) Presentation. Reason is that in certain use cases you will want to have an external Presentation Submission object, like for instance in OpenID4VP.

You can now provide an optional argument where the Presentation Submission object should be located. It will always be returned in the methods, as well as an indication whether it is used externally or embedded into the (Verifiable) Presentation.

The (Verifiable) Presentation object is present in the result object in the presentation resp. verifiablePresentation property.

No more need for _const and _enum models/properties in Presentation Definitions. They are now const and enum (fixed in OpenAPI model generation). The code replaces the previous versions to be sure.

v0.6.0: class and package renamed

As part of introducing Presentation Exchange v1 and v2 feature based detection support to our Presentation Exchange library and not reaching version 1.X yet, we decided to change the name of both the package and the main entry class:

  • The package was changed from @sphereon/pe-js to @sphereon/pex
  • The main class was changed from PEJS to PEX. The latter class has internal feature detection support on the provided definition, delegating the actual implementation to the new PEXv1 or PEXv2 class internally. If you don't want the automatic feature detection you can also choose to use the PEXv1 and PEXv2 classes directly.

License

This Presentation Exchange library Open Source software released under the Apache 2.0 license.

Funded & supported by

pex's People

Contributors

auer-martin avatar btencatesphereon avatar cre8 avatar dependabot[bot] avatar hrehman-sphereon avatar jorissphereon avatar jose-gataca avatar ksadjad avatar lukasjhan avatar maikel-maas avatar mistermoe avatar nklomp avatar sanderpostma avatar siacomuzzi avatar sksadjad avatar srmalley-sphereon avatar timoglastra avatar yhuard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

pex's Issues

Can't find pe-js npm package

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [X] question about how to use this project

  • Summary

"npm install pe-js" fails with a 404, not able to find the npm package. How do I install it?

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

$ npm install pe-js
npm ERR! code E404
npm ERR! 404 Not Found - GET https://registry.npmjs.org/pe-js - Not found
npm ERR! 404
npm ERR! 404 'pe-js@*' is not in the npm registry.
npm ERR! 404 You should bug the author to publish it (or use the name yourself!)

The jsonpath package is insecure (code execution + prototype pollution)

This library uses jsonpath under the hood, which makes sense but this library is not secure. Filter expression can lead to arbitrary code execution (albeit, due to a coincidence most common vectors wont work. technically its a bug in jsonpath) or - the one that is definitely active in that library atm - prototype pollution. It is difficult to filter all the possible ways of doing that, and even though it uses static evaluation to run the filters the static-eval library is not secure or intended to be as the author states.

For this reason I'd advise to look for alternatives to jsonpath - or if you can and need this PEX library now, run the pex methods in a complete sandbox where code execution and prototype pollution arent as big of a concern. I've yet to find a jsonpath library on NPM (forks of jsonpath + jsonpath-plus all have these vectors).

If for example this library is run on a server, that can be a serious security issue. At the very least it can cause denial of service using a simple payload. This issue is known and is discussed briefly in the static-eval repo issues

`field.Filter` does not require `type` but `Filter` type in PEX requires it

I'm submitting a ...
[x] bug report
[x] release request
[ ] question about the decisions made in the repository
[ ] question about how to use this project

Summary

Thanks for building this library! We are using it to select credentials and I ran across a small issue while writing tests against test vectors.

In one of my test vectors, I have a Input Descriptor whose filter is a const requiring a specific string. Not having type inside filter object seems to cause a type error, but filter should behave like a JSON schema, and JSON schema does not require type.

          "input_descriptors": [
            {
              "id": "7b928839-f0b1-4237-893d-b27124b57952",
              "constraints": {
                "fields": [
                  {
                    "path": [
                      "$.vc.credentialSubject.name",
                      "$.credentialSubject.name"
                    ],
                    "filter": {
                      "const": "Satoshi Tacomoto"
                    }
                  }
                ]
              }
            }
          ]

When I take the filter object and put it in a JSON Schema validator, the object with just the const key should be a valid JSON schema.
image
Link to validator

It appears type is nullable in the latest version of fieldV2 in this repo, but the latest pex-model released (v2.2.4) does not reflect this change

All that to say - would you be able to release a new version of @sphereon/pex-model so that we can have a nullable type field in filterV2 type?

Thank you!

Rule `pick` with `count` allows multiple credentials for a single input_descriptor

  • I'm submitting a ...
    [x] bug report

  • Summary

The PEX v1 specification mentions:

The Submission Requirement Object MAY contain a count property. If present, its value MUST be an integer greater than zero. This indicates the number of input Descriptors or Submission Requirement Objects to be submitted.

To me this indicates that if you have e.g. a count of 2, at least 2 of the input descriptors need to be submitted. When i query the PEX library using a presentation definition that has a count: 2 it will allow 2 credentials that match the same input descriptor to satisfy the definition requirements. However, I think it should throw an error in this case, as the presentation definition cannot be satisfied. It needs at least two input descriptors, and in this case it will only satisfy one input descriptor, but have two credential for that one input descriptor.

See the JSON documents below for an example of what I mean.

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
Credentials
[
   {
      "@context":[
         "https://www.w3.org/2018/credentials/v1",
         "https://www.w3.org/2018/credentials/examples/v2"
      ],
      "id":"http://example.gov/credentials/1231231",
      "type":[
         "VerifiableCredential",
         "UniversityDegreeCredential"
      ],
      "issuer":"did:web:animo.id",
      "issuanceDate":"2020-03-16T22:37:26.544Z",
      "credentialSubject":{
         "id":"did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd",
         "degree":{
            "type":"BachelorDegree",
            "name":"Bachelor of Fights"
         }
      },
      "proof":{
         "type":"Ed25519Signature2018",
         "created":"2020-04-02T18:28:08Z",
         "verificationMethod":"did:web:animo.id#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
         "proofPurpose":"assertionMethod",
         "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA"
      }
   },
   {
      "@context":[
         "https://www.w3.org/2018/credentials/v1",
         "https://www.w3.org/2018/credentials/examples/v1"
      ],
      "id":"http://example.gov/credentials/131313123",
      "type":[
         "VerifiableCredential",
         "UniversityDegreeCredential"
      ],
      "issuer":"did:web:animo.id",
      "issuanceDate":"2020-03-16T22:37:26.544Z",
      "credentialSubject":{
         "id":"did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd",
         "degree":{
            "type":"BachelorDegree",
            "name":"Bachelor of Something"
         }
      },
      "proof":{
         "type":"Ed25519Signature2018",
         "created":"2020-04-02T18:28:08Z",
         "verificationMethod":"did:web:animo.id#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
         "proofPurpose":"assertionMethod",
         "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA"
      }
   }
]
Presentation Definition
{
   "id":"31e2f0f1-6b70-411d-b239-56aed5321884",
   "purpose":"To check if you have a valid college degree.",
   "input_descriptors":[
      {
         "id":"df2accf9-1ecb-4f4e-af6d-21be152a881b",
         "group":[
            "A"
         ],
         "purpose":"You must have a valid Bachelor Degree issued by Animo.",
         "schema":[
            {
               "uri":"https://www.w3.org/2018/credentials/v1"
            }
         ],
         "constraints":{
            "fields":[
               {
                  "path":[
                     "$.issuer",
                     "$.vc.issuer",
                     "$.iss"
                  ],
                  "filter":{
                     "type":"string",
                     "pattern":"did:web:animo.id"
                  }
               }
            ]
         }
      },
      {
         "id":"867bfe7a-5b91-46b2-9ba4-70028b8d9cc8",
         "purpose":"You must have a valid Bachelor Degree issued by Transmute.",
         "group":[
            "A"
         ],
         "schema":[
            {
               "uri":"https://www.w3.org/2018/credentials/v1"
            }
         ],
         "constraints":{
            "fields":[
               {
                  "path":[
                     "$.issuer",
                     "$.vc.issuer",
                     "$.iss"
                  ],
                  "filter":{
                     "type":"string",
                     "pattern":"did:web:vc.transmute.world"
                  }
               }
            ]
         }
      }
   ],
   "submission_requirements":[
      {
         "name":"Pick Count",
         "rule":"pick",
         "from":"A",
         "count":2
      }
   ]
}
Select Results
{
   "errors":[
      
   ],
   "matches":[
      {
         "rule":"pick",
         "from":[
            "A"
         ],
         "vc_path":[
            "$.verifiableCredential[0]",
            "$.verifiableCredential[1]"
         ],
         "name":"df2accf9-1ecb-4f4e-af6d-21be152a881b",
         "count":2
      }
   ],
   "areRequiredCredentialsPresent":"info",
   "verifiableCredential":[
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://www.w3.org/2018/credentials/examples/v2"
         ],
         "id":"http://example.gov/credentials/1231231",
         "type":[
            "VerifiableCredential",
            "UniversityDegreeCredential"
         ],
         "issuer":"did:web:animo.id",
         "issuanceDate":"2020-03-16T22:37:26.544Z",
         "credentialSubject":{
            "id":"did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd",
            "degree":{
               "type":"BachelorDegree",
               "name":"Bachelor of Fights"
            }
         },
         "proof":{
            "type":"Ed25519Signature2018",
            "created":"2020-04-02T18:28:08Z",
            "verificationMethod":"did:web:animo.id#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
            "proofPurpose":"assertionMethod",
            "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA"
         }
      },
      {
         "@context":[
            "https://www.w3.org/2018/credentials/v1",
            "https://www.w3.org/2018/credentials/examples/v1"
         ],
         "id":"http://example.gov/credentials/131313123",
         "type":[
            "VerifiableCredential",
            "UniversityDegreeCredential"
         ],
         "issuer":"did:web:animo.id",
         "issuanceDate":"2020-03-16T22:37:26.544Z",
         "credentialSubject":{
            "id":"did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd",
            "degree":{
               "type":"BachelorDegree",
               "name":"Bachelor of Something"
            }
         },
         "proof":{
            "type":"Ed25519Signature2018",
            "created":"2020-04-02T18:28:08Z",
            "verificationMethod":"did:web:animo.id#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
            "proofPurpose":"assertionMethod",
            "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA"
         }
      }
   ],
   "warnings":[
      
   ]
}

Not possible to use in a TS backend scope?

  • I'm submitting a ...
    [ X ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    Im trying to find a match and use the pejs library like this

import * as PEJSLIB from '@sphereon/pe-js';
const pejs = PEJSLIB.pejs

const srMatches = pejs.selectFrom(presentationDefinition, credentials, holderDid);

But it says that selectFrom is not a method.
What do I need to import to get it to work?

Latest release does not have the correct validateXXX.js files published

Somehow the latest release still has the old validation .JS files that are generated, and thus boolean are still not supported yet.

If i compare the files (validateFilterV2.js) from the build with what I build locally you can also see the difference.

Not sure how that's possible, but could a new version be published with the updated generated validation schema js files?

cc @nklomp

pe-js installation fails on windows

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [X ] question about how to use this project

  • Summary
    When I try to install pe-js on windows using yarn install pe-js, I get the below error:
    Output: node-pre-gyp info it worked if it ends with ok node-pre-gyp info using [email protected] node-pre-gyp info using [email protected] | win32 | x64 node-pre-gyp WARN Using request for node-pre-gyp https download node-pre-gyp info check checked for "C:\MyDrive\Blockchain-Garage\VC_Workspace\Sources\WickedScan_Source\agency\server\src\lib\node_modules\@mattrglobal\node-bbs-signatures\native\index.node" (not found) node-pre-gyp http GET https://github.com/mattrglobal/node-bbs-signatures/releases/download/0.12.0/node-v83-win32-x64.tar.gz node-pre-gyp http 404 https://github.com/mattrglobal/node-bbs-signatures/releases/download/0.12.0/node-v83-win32-x64.tar.gz node-pre-gyp ERR! install error node-pre-gyp ERR! stack Error: 404 status code downloading tarball https://github.com/mattrglobal/node-bbs-signatures/releases/download/0.12.0/node-v83-win32-x64.tar.gz node-pre-gyp ERR! stack at Request.<anonymous> (C:\MyDrive\Blockchain-Garage\VC_Workspace\Sources\WickedScan_Source\agency\server\src\lib\node_modules\node-pre-gyp\lib\install.js:142:27) node-pre-gyp ERR! stack at Request.emit (events.js:327:22) node-pre-gyp ERR! stack at Request.onRequestResponse (C:\MyDrive\Blockchain-Garage\VC_Workspace\Sources\WickedScan_Source\agency\server\src\lib\node_modules\request\request.js:1059:10) node-pre-gyp ERR! stack at ClientRequest.emit (events.js:315:20) node-pre-gyp ERR! stack at HTTPParser.parserOnIncomingClient [as onIncoming] (_http_client.js:641:27) node-pre-gyp ERR! stack at HTTPParser.parserOnHeadersComplete (_http_common.js:126:17) node-pre-gyp ERR! stack at TLSSocket.socketOnData (_http_client.js:509:22) node-pre-gyp ERR! stack at TLSSocket.emit (events.js:315:20) node-pre-gyp ERR! stack at addChunk (internal/streams/readable.js:309:12) node-pre-gyp ERR! stack at readableAddChunk (internal/streams/readable.js:284:9) node-pre-gyp ERR! System Windows_NT 10.0.19043 node-pre-gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\MyDrive\\Blockchain-Garage\\VC_Workspace\\Sources\\WickedScan_Source\\agency\\server\\src\\lib\\node_modules\\node-pre-gyp\\bin\\node-pre-gyp" "install" "--fallback-to-build=false" node-pre-gyp ERR! cwd C:\MyDrive\Blockchain-Garage\VC_Workspace\Sources\WickedScan_Source\agency\server\src\lib\node_modules\@mattrglobal\node-bbs-signatures node-pre-gyp ERR! node -v v14.15.4 node-pre-gyp ERR! node-pre-gyp -v v0.17.0 node-pre-gyp ERR! not ok 404 status code downloading tarball https://github.com/mattrglobal/node-bbs-signatures/releases/download/0.12.0/node-v83-win32-x64.tar.gz

When I try looked the repo https://github.com/mattrglobal/node-bbs-signatures/releases/download/0.12.0/, there was no tarball available for windows. Will this is available anytime soon? If not, could you suggest an alternative?

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

Allow also async Hasher functions

  • I'm submitting a ...
    [ ] bug report
    [X] feature request
    [X] question about the decisions made in the repository
    [ ] question about how to use this project

Right now the hasher function has to be async which is fine in environments like NodeJS where the native crypto API provides a sync call. Unfortunately this is not the case browser based environments where the webcrypto only offers async calls for non blocking execution. Therefor I would propose to change the hasher type from only Hasher to ยดHasher | AsyncHasher`. This requires updates to the PEX library and also to the ssi-types.

Are there any reasons to stay with an only sync call beside the requirement of refactoring?

How to treat EvaluationResults

  • I'm submitting a ...

  • bug report

  • feature request

  • question about the decisions made in the repository

  • question about how to use this project

  • When EvaluationResult contains errors does this mean that what is expected from presentation is not fulfilled?

I am a bit uncertain how to treat EvaluationResults when all input descriptors are fulfilled but evaluation result contains errors.

presentationDefinition = {
 id: '286bc1e0-f1bd-488a-a873-8d71be3c690e',
 submission_requirements: [
   {
     name: 'Identity requirement',
     rule: 'all',
     from: 'A'
   },
   {
     name: 'Role requirement',
     rule: 'all',
     from: 'B'
   }
 ],
 input_descriptors: [
   {
     id: 'identity_input',
     name: 'Subject identity input',
     group: ['A'],
     purpose: 'Subject should be identifiable',
     constraints: {
       fields: [
         {
           path: ['$.credentialSubject.did'],
           filter: {
             type: 'string',
             const: 'did:example:d23dd687a7dc6787646f2eb98d0'
           }
         }
       ]
     }
   },
   {
     id: 'name_input',
     name: 'Subject name input',
     group: ['A'],
     purpose: 'Subject should have name',
     constraints: {
       fields: [
         {
           path: ['$.credentialSubject.profile.name'],
           filter: {
             type: 'string',
             const: 'John'
           }
         }
       ]
     }
   },
   {
     id: 'role_input',
     name: 'Admin role input',
     group: ['B'],
     purpose: 'Subject should have admin role',
     constraints: {
       fields: [
         {
           path: ['$.credentialSubject.role'],
           filter: {
             type: 'string',
             const: 'admin'
           }
         }
       ]
     }
   }
 ]
};

const evaluationResult = {
 verifiableCredential: [
   InternalVerifiableCredentialJsonLD {
     '@context': [
       'https://www.w3.org/2018/credentials/v1',
       {
         profile: { '@id': 'ctx_id:profile', '@type': 'ctx_id:profile' },
         name: 'ctx_id:name',
         Identity: 'ctx_id:Identity',
         did: 'ctx_id:did',
         ctx_id: 'https://example.org/ld-context-2022#'
       }
     ],
     id: 'urn:uuid:7f94d397-3e70-4a43-945e-1a13069e636f',
     type: [ 'VerifiableCredential', 'Identity' ],
     credentialSubject: {
       did: 'did:example:d23dd687a7dc6787646f2eb98d0',
       profile: { name: 'John' }
     },
     issuer: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF',
     issuanceDate: '2022-03-18T08:57:32.477Z',
     proof: {
       type: 'Ed25519Signature2018',
       proofPurpose: 'assertionMethod',
       verificationMethod: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF#z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF',
       created: '2021-11-16T14:52:19.514Z',
       jws: 'eyJhbGciOiJFZERTQSIsImNyaXQiOlsiYjY0Il0sImI2NCI6ZmFsc2V9..6QqnZoVBfNzNLa6GO8vnLq7YjIxKvL-e1a4NGYFOwjf9GQtJcD6kenu8Sb_DOXERUUYZnVbsaRRrRAIN0YR0DQ'
     }
   },
   InternalVerifiableCredentialJsonLD {
     '@context': [
       'https://www.w3.org/2018/credentials/v1',
       {
         Role: 'ctx_role:Role',
         ctx_role: 'https://example.org/ld-context-2022#',
         role: 'ctx_role:role'
       }
     ],
     id: 'urn:uuid:7f94d397-3e70-4a43-945e-1a13069e636f',
     type: [ 'VerifiableCredential', 'Role' ],
     credentialSubject: { role: 'admin' },
     issuer: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF',
     issuanceDate: '2022-03-18T08:57:32.477Z',
     proof: {
       type: 'Ed25519Signature2018',
       proofPurpose: 'assertionMethod',
       verificationMethod: 'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF#z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF',
       created: '2021-11-16T14:52:19.514Z',
       jws: 'eyJhbGciOiJFZERTQSIsImNyaXQiOlsiYjY0Il0sImI2NCI6ZmFsc2V9..nV_x5AKqH9M0u5wsEt1D_DXxYpOzuO_nqDEj-alIzPA5yi8_yWAhKbWPa2r9GoTLPehvZrpgleUDiDj-9_F6Bg'
     }
   }
 ],
 warnings: [],
 errors: [
   {
     tag: 'FilterEvaluation',
     status: 'error',
     message: 'Input candidate does not contain property: $.input_descriptors[2]: $[0]'
   },
   {
     tag: 'FilterEvaluation',
     status: 'error',
     message: 'Input candidate does not contain property: $.input_descriptors[0]: $[1]'
   },
   {
     tag: 'FilterEvaluation',
     status: 'error',
     message: 'Input candidate does not contain property: $.input_descriptors[1]: $[1]'
   },
   {
     tag: 'MarkForSubmissionEvaluation',
     status: 'error',
     message: 'The input candidate is not eligible for submission: $.input_descriptors[2]: $[0]'
   },
   {
     tag: 'MarkForSubmissionEvaluation',
     status: 'error',
     message: 'The input candidate is not eligible for submission: $.input_descriptors[0]: $[1]'
   },
   {
     tag: 'MarkForSubmissionEvaluation',
     status: 'error',
     message: 'The input candidate is not eligible for submission: $.input_descriptors[1]: $[1]'
   }
 ],
 value: {
   id: '4TNcp1mCA8mlhOjGhteR1',
   definition_id: '286bc1e0-f1bd-488a-a873-8d71be3c690e',
   descriptor_map: [
     {
       id: 'identity_input',
       format: 'ldp_vc',
       path: '$.verifiableCredential[0]'
     },
     {
       id: 'name_input',
       format: 'ldp_vc',
       path: '$.verifiableCredential[0]'
     },
     {
       id: 'role_input',
       format: 'ldp_vc',
       path: '$.verifiableCredential[1]'
     }
   ]
 }
}

Can I assume that in this case presentation is satisfies definition and errors can be safely ignored?

Better error message for when credentials in presentation dont match requirements

Lets say I have the PD of:

{
  "id": "81857b3f-5977-442e-9eae-225ca158d1a5",
  "input_descriptors": [
    {
      "id": "Credential 1",
      "name": "test PD",
      "purpose": "verify",
      "constraints": {
        "fields": [
          {
            "path": [
              "$.credentialSubject.email"
            ]
          }
        ]
      }
    }
  ]
}

And I submit a presentation with a subject like:

{"notEmail": "blah"}

I will get these error messages:

{
  "errors": [
    {
      "tag": "FilterEvaluation",
      "status": "error",
      "message": "Input candidate does not contain property: $.input_descriptors[0]: $.verifiableCredential[0]"
    },
    {
      "tag": "MarkForSubmissionEvaluation",
      "status": "error",
      "message": "The input candidate is not eligible for submission: $.input_descriptors[0]: $.verifiableCredential[0]"
    }
  ]
}

It would be beneficial to be told which field is missing and ideally not to have the Input candidate does not contain property message at all - unless that message is reffering to the email property. The reading of it is that its referring to the path $.input_descriptors[0]: $.verifiableCredential[0].

Allowing to pass multiple sd-jwt credentials

  • I'm submitting a ...
    [ ] bug report
    [x] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    In the code it's mentioned that only one credential can be passed until the referenced issue is solved:

    PEX/lib/PEX.ts

    Line 278 in fb8eb81

    // https://github.com/decentralized-identity/presentation-exchange/issues/462

It seems to be solved decentralized-identity/presentation-exchange#462 @TimoGlastra can we just remove the check or is there something else that needs to be implemented?

Executing `input_descriptors` for specific VCs in a VP

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [x] question about how to use this project

  • **Summary **
    Hi Team, I have been using PEX library for writing a tutorial for VP exchange w.r.t a presentation-definition which makes it necessary for the user to add 2 VCs (inside VP) and present it to verifier.

For a use case, a holder needs to provide one self signed ConsentCredential and authority issued DrivingLicense in the form of single VP to the verifier to verify it.

something like

{
   "type": ["VerifiablePresentation"],
   "verifiableCredentials": [
      ...drivingLicense,
      ...consentCredential
   ]
}

As part of presentation-definition, the input_descriptor checks the $.type path and validates it with the respective type (as mentioned above).

the input_descriptor looks similar to this

"input_descriptors":[
                        {
                           "id":"consent_credential",
                           "name":"Consent Credential",
                           "purpose":"One consent credential is required for this presentation",
                           "constraints":{
                              "subject_is_issuer": "required",
                              "fields":[
                                 {
                                    "path":[
                                       "$.type"
                                    ],
                                    "filter":{
                                       "type":"array",
                                       "contains":{
                                          "type":"string",
                                          "const":"ConsentCredential"
                                       }
                                    }
                                 }
                              ]
                           }
                        }
                     ]

I tried multiple arrangement of input-descriptor, submission_requirements and descriptor_map for this but It didn't worked out.

Would really appreciate if you can help me with this, any sample would also be beneficial.

selectFrom - unknown exception occurred: Cannot read property 'value' of undefined

  • I'm submitting a ...
    [ X] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary

I am getting the following exception on my call to selectFrom:
"unknown exception occurred: Cannot read property 'value' of undefined"

The error passed to my exception handler does not contain a stack trace, so I don't know where this is occurring.

The arguments that I am passing to selectFrom are as follows:

First arg

{
    "submission_requirements": [
        {
            "purpose": "We need your driver's license name",
            "name": "Name on driver's license",
            "rule": "all",
            "from": "A"
        }
    ],
    "id": "32f54163-7166-48f1-93d8-ff217bdb0654",
    "input_descriptors": [
        {
            "constraints": {
                "fields": [
                    {
                        "path": [
                            "$.credentialSubject.familyName"
                        ],
                        "purpose": "The claim must be from one of the specified issuers",
                        "id": "1f44d55f-f161-4938-a659-f8026467f126"
                    },
                    {
                        "path": [
                            "$.credentialSubject.givenName"
                        ],
                        "purpose": "The claim must be from one of the specified issuers"
                    }
                ],
                "limit_disclosure": "required",
                "is_holder": [
                    {
                        "directive": "required",
                        "field_id": [
                            "1f44d55f-f161-4938-a659-f8026467f126"
                        ]
                    }
                ]
            },
            "schema": [
                {
                    "uri": "https://www.w3.org/2018/credentials#VerifiableCredential"
                },
                {
                    "uri": "https://w3id.org/citizenship#PermanentResident"
                }
            ],
            "name": "EU Driver's License",
            "group": [
                "A"
            ],
            "id": "citizenship_input_1"
        }
    ],
    "format": {
        "ldp_vp": {
            "proof_type": [
                "Ed25519VerificationKey2018"
            ]
        }
    }
}

Second arg

[
    {
        "identifier": "83627465",
        "name": "Permanent Resident Card",
        "type": [
            "VerifiableCredential",
            "PermanentResidentCard"
        ],
        "id": "https://issuer.oidp.uscis.gov/credentials/83627465",
        "credentialSubject": {
            "birthCountry": "Bahamas",
            "id": "did:example:b34ca6cd37bbf23",
            "type": [
                "PermanentResident",
                "Person"
            ],
            "gender": "Female",
            "familyName": "SMITH",
            "givenName": "JANE",
            "residentSince": "2015-01-01",
            "lprNumber": "999-999-999",
            "birthDate": "1958-07-17",
            "commuterClassification": "C1",
            "lprCategory": "C09",
            "image": "data:image/png;base64,iVBORw0KGgokJggg=="
        },
        "expirationDate": "2029-12-03T12:19:52Z",
        "description": "Government of Example Permanent Resident Card.",
        "issuanceDate": "2019-12-03T12:19:52Z",
        "@context": [
            "https://www.w3.org/2018/credentials/v1",
            "https://w3id.org/citizenship/v1",
            "https://w3id.org/security/suites/ed25519-2020/v1"
        ],
        "issuer": "did:key:z6MkhfRoL9n7ko9d6LnB5jLB4aejd3ir2q6E2xkuzKUYESig",
        "proof": {
            "type": "Ed25519Signature2020",
            "created": "2021-09-10T15:33:39Z",
            "verificationMethod": "did:key:z6MkhfRoL9n7ko9d6LnB5jLB4aejd3ir2q6E2xkuzKUYESig#key-1",
            "proofPurpose": "assertionMethod",
            "proofValue": "z4CYETTeGFbMz67ocKtRMb2xVG5mm5VcMtUYcs5KTgCAT2LhrwPfrN3ruvXf8DFSz6VtryWYJTPSWtEjspwakmTAY"
        }
    }
]

Third arg:

FAsYneKJhWBP2n5E21ZzdY
  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

Better support for OIDC4VP

  • I'm submitting a ...
    [ ] bug report
    [x] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary

Hi!
In the OIDC4VP specs, the presentation_submission object is either in the id_token or in the vp_token itself. If I want to evaluate a VP using @sphereon/pex, I have to preprocess the verifiablePresentation param of the evaluatePresentation function. IMHO, it would be better to provide 2 separate params: 1 for presentation_submission, the other for verifiablePresentation. But this not my main request.

Right now, the evaluatePresentation requires the second param (verifiablePresentation) to be an IPresentation. How am I supposed to pass a JWT VP then? In my case, I would have a presentation_submission like:

        "presentation_submission": {
            "id": "Selective disclosure example presentation",
            "definition_id": "Selective disclosure example",
            "descriptor_map": [
                {
                    "id": "Ontario Health Insurance Plan",
                    "format": "jwt_vp",
                    "path": "$.presentation",
                    "path_nested": {
                        "format": "jwt_vc",
                        "path": "$.presentation.vp.verifiableCredential[0]"
                    }
                }
            ]
        }

And pass a verifiablePresentation object like this:

{
    "presentation":
        "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImRpZDpleGFtcGxlOmFiZmUxM2Y3MTIxMjA0
        MzFjMjc2ZTEyZWNhYiNrZXlzLTEifQ.eyJzdWIiOiJkaWQ6ZXhhbXBsZTplYmZlYjFmNzEyZWJjNmYxY
        zI3NmUxMmVjMjEiLCJqdGkiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsImlzc
        yI6Imh0dHBzOi8vZXhhbXBsZS5jb20va2V5cy9mb28uandrIiwibmJmIjoxNTQxNDkzNzI0LCJpYXQiO
        jE1NDE0OTM3MjQsImV4cCI6MTU3MzAyOTcyMywibm9uY2UiOiI2NjAhNjM0NUZTZXIiLCJ2YyI6eyJAY
        29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vd
        3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL2V4YW1wbGVzL3YxIl0sInR5cGUiOlsiVmVyaWZpYWJsZ
        UNyZWRlbnRpYWwiLCJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjd
        CI6eyJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IjxzcGFuIGxhbmc9J2ZyL
        UNBJz5CYWNjYWxhdXLDqWF0IGVuIG11c2lxdWVzIG51bcOpcmlxdWVzPC9zcGFuPiJ9fX19.KLJo5GAy
        BND3LDTn9H7FQokEsUEi8jKwXhGvoN3JtRa51xrNDgXDb0cq1UTYB-rK4Ft9YVmR1NI_ZOF8oGc_7wAp
        8PHbF2HaWodQIoOBxxT-4WNqAxft7ET6lkH-4S6Ux3rSGAmczMohEEf8eCeN-jC8WekdPl6zKZQj0YPB
        1rx6X0-xlFBs7cl6Wt8rfBP_tZ9YgVWrQmUWypSioc0MUyiphmyEbLZagTyPlUyflGlEdqrZAv6eSe6R
        txJy6M1-lD7a5HTzanYTWBPAUHDZGyGKXdJw-W_x0IWChBzI8t3kpG253fg6V3tPgHeKXE94fz_QpYfg
        --7kLsyBAfQGbg"
}

In other terms, the evaluatePresentation should not make any assumption about the shape of the verfiablePresentation. It should only process it based on the information given by the new presentationSubmission param.

Or maybe I've missed some features of this lib. If it can already process JWT VP properly, please show me how. :)

Thanks!

Handling of optional property in fields object fails

I'm submitting a ...
[x] bug report
[ ] feature request
[ ] question about the decisions made in the repository
[ ] question about how to use this project

Summary
According to https://identity.foundation/presentation-exchange/#input-descriptor-object:

The fields object MAY contain an optional property. The value of this property MUST be a boolean, wherein true indicates the field is optional, and false or non-presence of the property indicates the field is required.

The library is not able to handle the optional property in the fields object. Filtering or presentation evalutation fails with an InvalidPresentation error.

Other information

Code snippet:

// PEX "version": "3.3.1"
const { PEX } = require("@sphereon/pex");

const pex = new PEX();
const presentationDefinition = {
  id: "test",
  input_descriptors: [
    {
      id: "Test1",
      name: "Testing the optional attribute in the fields object",
      purpose: "Testing purposes",
      constraints: {
        fields: [
          {
            path: ["$.credentialSubject.id"],
          },
          {
            path: ["$.expirationDate"],
            optional: true,
          },
        ],
      },
    },
  ],
};

const result = pex.selectFrom(presentationDefinition, [], []);

Output:

/Users/mzm/test-pex/node_modules/@sphereon/pex/dist/main/lib/types/SSITypesBuilder.js:35
            throw versionResult.error;
            ^
This is not a valid PresentationDefinition
(Use `node --trace-uncaught ...` to show where the exception was thrown)

Node.js v18.17.1

Allow any values for the `format.ldp_vc.proof_type`

Currently the allowed values for the proof_type from ldp_vc format is restricted to a set of known types, namely:

    return [
      'Ed25519VerificationKey2018',
      'Ed25519Signature2018',
      'RsaSignature2018',
      'EcdsaSecp256k1Signature2019',
      'EcdsaSecp256k1RecoverySignature2020',
      'JsonWebSignature2020',
      'GpgSignature2020',
      'JcsEd25519Signature2020',
      'BbsBlsSignature2020',
      'Bls12381G2Key2020',
    ];

Is there a specific reason for this. As PEX doesn't handle the signature stuff, it could support any proof types and it would make the PEX library more flexible towards future signature suites that have yet to be defined.

Incorrect presentation evaluated without errors

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Evaluation of presentation doesn't returns expected errors

  • **Summary

After updating from v1.0.2 to v1.1.0 evaluation of incorrect presentation doesn't returns errors. Tested on this data:

const presentation = {
  '@context': ['https://www.w3.org/2018/credentials/v1'],
  type: ['VerifiablePresentation'],
  verifiableCredential: [
    {
      '@context': [
        'https://www.w3.org/2018/credentials/v1',
        {
          AdminRole: 'ew:AdminRole',
          ew: 'https://energyweb.org/ld-context-2022#',
          role: 'ew:role'
        }
      ],
      id: 'urn:uuid:7f94d397-3e70-4a43-945e-1a13069e636f',
      type: ['VerifiableCredential', 'AdminRole'],
      credentialSubject: {
        id: 'did:example:1234567894ad31s12',
        role: 'user'
      },
      issuer: 'did:example:123456789af312312i',
      issuanceDate: '2022-03-18T08:57:32.477Z',
      proof: {
        type: 'Ed25519Signature2018',
        proofPurpose: 'assertionMethod',
        verificationMethod:
          'did:key:z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF#z6MkoB84PJkXzFpbqtfYV5WqBKHCSDf7A1SeepwzvE36QvCF',
        created: '2021-11-16T14:52:19.514Z',
        jws: 'eyJhbGciOiJFZERTQSIsImtpZCI6InpXTUU5MTNqZFlySUx1WUQtb3QtakRibXpxejM0SHFsQ1VaNkNNZEpueW8iLCJjcml0IjpbImI2NCJdLCJiNjQiOmZhbHNlfQ..b8N7pmZHjWN_QnEdJBXrubP-HOcnkQjkXSUHth6drx3UjEaQpNfGW2lICDWL6qaAcXxcMQX-_GH-8XxtHTdxDQ'
      }
    }
  ]
};
const presentationDefinition = {
  id: '286bc1e0-f1bd-488a-a873-8d71be3c690e',
  input_descriptors: [
    {
      id: 'some_id',
      name: 'Required credential',
      constraints: {
        fields: [
          {
            path: ['$.credentialSubject.role'],
            filter: {
              type: 'string',
              const: 'admin'
            }
          }
        ]
      }
    }
  ]
};

evaluation with [email protected] returns object with errors property as expected

[
  {
    tag: 'FilterEvaluation',
    status: 'error',
    message: 'Input candidate failed filter evaluation: $.input_descriptors[0]: $[0]'
  },
  {
    tag: 'MarkForSubmissionEvaluation',
    status: 'error',
    message: 'The input candidate is not eligible for submission: $.input_descriptors[0]: $[0]'
  }
];

but [email protected] returns no errors

JWT_VP need a proof

Hi All, I have tried to verify the JWT_VC, but i think that according to the specs, JWT_VP does not need a proof, only vp_ldp needs, can I have other workaround for this?

export type IVerifiablePresentation = IPresentation & IHasProof

Cannot filter by [$.type] path in presentation definition

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    I'm able to successfully request and verify VCs using the did-siop-auth and ssi-sdk SIOP authenticator packages. However, when I try defining a Presentation Definition using the [$.type] path the FilterEvaluation fails

    input_descriptors: [
      {
        id: "A specific type of VC",
        name: "A specific type of VC",
        purpose: "We want a VC of this type",
        schema: [
          {
            uri: "https://www.w3.org/2018/credentials/v1",
          },
          {
            uri: "https://www.w3.org/2018/credentials/examples/v1",
          },
        ],
        constraints: {
          fields: [
            {
              path: ["$.type"],
              filter: {
                type: "string",
                pattern: "AlumniCredential",
              },
            },
          ],
        },
      },
    ],

If I pick any other path, such as a claim directly in the credentialSubject I'm able to get it to work. It might be that the pattern should be somewhat different, I'm following the https://identity.foundation/presentation-exchange/spec/v1.0.0/ so I assume it should work?

Sample VC

"verifiableCredential": {
"credentialSubject": {
"alumniOf": {
"id": "did:web:samplesite.com",
"name": [
{
"value": "Example University",
"lang": "en"
},
{
"value": "Exemple d'Universitรฉ",
"lang": "fr"
}
]
},
"id": "did:key:z6Mksg8XF5K6m9wHpKgyJAY4A7GhtomRBY7Dd3RwBZcRKjau"
},
"issuer": {
"id": "did:web:samplesite.com"
},
"type": [
"VerifiableCredential",
"AlumniCredential"
],
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"issuanceDate": "2022-08-11T08:39:49.000Z",
}
}

Error:

Error: Error: message: Could not find VerifiableCredentials matching presentationDefinition object in the provided VC list, details: [{"tag":"FilterEvaluation","status":"error","message":"Input candidate failed filter evaluation: $.input_descriptors[0]: $.verifiableCredential[0]"},{"tag":"FilterEvaluation","status":"error","message":"Input candidate failed filter evaluation: $.input_descriptors[0]: $.verifiableCredential[1]"},{"tag":"FilterEvaluation","status":"error","message":"Input candidate failed filter evaluation: $.input_descriptors[0]: $.verifiableCredential[2]"},{"tag":"MarkForSubmissionEvaluation","status":"error","message":"The input candidate is not eligible for submission: $.input_descriptors[0]: $.verifiableCredential[0]"},{"tag":"MarkForSubmissionEvaluation","status":"error","message":"The input candidate is not eligible for submission: $.input_descriptors[0]: $.verifiableCredential[1]"},{"tag":"MarkForSubmissionEvaluation","status":"error","message":"The input candidate is not eligible for submission: $.input_descriptors[0]: $.verifiableCredential[2]"}]
  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

Calling `verifiablePresentationFrom` with `PresentationSubmissionLocation.EXTERNAL` results in error

  • I'm submitting a ...
    [x] bug report

  • Summary

When calling verifiablePresentationFrom with presentationSubmissionLocation set to PresentationSubmissionLocation.EXTERNAL, this will always result in an error. This is because verifiablePresentationFrom calls evaluatePresentation without passing presentationSubmission to the ops, and because the mode is set to external it also can't extract the submission from the VP.

I think the submission should also be passed to the opts of evaluatePresentation, so this method has access to the presentation submission

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

`selectFrom` method requires all credentials to match all input descriptors

  • I'm submitting a ...
    [x] bug report

  • Summary

It seems that the PEX library (both v1 and v2 it seems), require all credentials to match all input_descriptors. I've created a code sandbox which reproduces the issue: https://replit.com/@timo10/PEX-Playground#index.ts.

It's run with two credentials from two issuers did:web:vc.transmute.world and did:web:animo.id. there's two input descriptors, where the first one requires did:web:animo.id to be the issuer, and the second one did:web:vc.transmute.world. Running it with a single input descriptor, will yield the correct result, as well as changing the input descriptors so a potentional credential will match all input descriptors (so two input_descriptors that match did:web:animo.id.

Maybe I'm completely misunderstanding PEX, but it is not required AFAIK for all credentials to match all input_descriptors. This is how you can request multiple credentials from a holder.

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
Credentials
[
   {
      "@context":[
         "https://www.w3.org/2018/credentials/v1",
         "https://www.w3.org/2018/credentials/examples/v1"
      ],
      "id":"http://example.gov/credentials/3732",
      "type":[
         "VerifiableCredential",
         "UniversityDegreeCredential"
      ],
      "issuer":"did:web:vc.transmute.world",
      "issuanceDate":"2020-03-16T22:37:26.544Z",
      "credentialSubject":{
         "id":"did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd",
         "degree":{
            "type":"BachelorDegree",
            "name":"Bachelor of Science and Arts"
         }
      },
      "proof":{
         "type":"Ed25519Signature2018",
         "created":"2020-04-02T18:28:08Z",
         "verificationMethod":"did:web:vc.transmute.world#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
         "proofPurpose":"assertionMethod",
         "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA"
      }
   },
   {
      "@context":[
         "https://www.w3.org/2018/credentials/v1",
         "https://www.w3.org/2018/credentials/examples/v2"
      ],
      "id":"http://example.gov/credentials/1231231",
      "type":[
         "VerifiableCredential",
         "UniversityDegreeCredential"
      ],
      "issuer":"did:web:animo.id",
      "issuanceDate":"2020-03-16T22:37:26.544Z",
      "credentialSubject":{
         "id":"did:key:z6MkjRagNiMu91DduvCvgEsqLZDVzrJzFrwahc4tXLt9DoHd",
         "degree":{
            "type":"BachelorDegree",
            "name":"Bachelor of Fights"
         }
      },
      "proof":{
         "type":"Ed25519Signature2018",
         "created":"2020-04-02T18:28:08Z",
         "verificationMethod":"did:web:animo.id#z6MksHh7qHWvybLg5QTPPdG2DgEjjduBDArV9EF9mRiRzMBN",
         "proofPurpose":"assertionMethod",
         "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..YtqjEYnFENT7fNW-COD0HAACxeuQxPKAmp4nIl8jYAu__6IH2FpSxv81w-l5PvE1og50tS9tH8WyXMlXyo45CA"
      }
   }
]
Presentation Definition
{
   "id":"31e2f0f1-6b70-411d-b239-56aed5321884",
   "purpose":"To check if you have a valid college degree.",
   "input_descriptors":[
      {
         "id":"df2accf9-1ecb-4f4e-af6d-21be152a881b",
         "purpose":"You must have a valid Bachelor Degree issued by Animo.",
         "schema":[
            {
               "uri":"https://www.w3.org/2018/credentials/v1"
            }
         ],
         "constraints":{
            "fields":[
               {
                  "path":[
                     "$.issuer",
                     "$.vc.issuer",
                     "$.iss"
                  ],
                  "filter":{
                     "type":"string",
                     "pattern":"did:web:animo.id"
                  }
               }
            ]
         }
      },
      {
         "id":"867bfe7a-5b91-46b2-9ba4-70028b8d9cc8",
         "purpose":"You must have a valid Bachelor Degree issued by Transmute.",
         "schema":[
            {
               "uri":"https://www.w3.org/2018/credentials/v1"
            }
         ],
         "constraints":{
            "fields":[
               {
                  "path":[
                     "$.issuer",
                     "$.vc.issuer",
                     "$.iss"
                  ],
                  "filter":{
                     "type":"string",
                     "pattern":"did:web:vc.transmute.world"
                  }
               }
            ]
         }
      }
   ]
}
Result
{
   "errors":[
      {
         "tag":"FilterEvaluation",
         "status":"error",
         "message":"Input candidate failed filter evaluation: $.input_descriptors[0]: $.verifiableCredential[0]"
      },
      {
         "tag":"FilterEvaluation",
         "status":"error",
         "message":"Input candidate failed filter evaluation: $.input_descriptors[1]: $.verifiableCredential[1]"
      },
      {
         "tag":"MarkForSubmissionEvaluation",
         "status":"error",
         "message":"The input candidate is not eligible for submission: $.input_descriptors[0]: $.verifiableCredential[0]"
      },
      {
         "tag":"MarkForSubmissionEvaluation",
         "status":"error",
         "message":"The input candidate is not eligible for submission: $.input_descriptors[1]: $.verifiableCredential[1]"
      }
   ],
   "matches":[
      
   ],
   "areRequiredCredentialsPresent":"error",
   "verifiableCredential":[
      
   ],
   "warnings":[
      
   ]
}

cc @nklomp

submissionFrom with no submission_requirements fails

  • I'm submitting a ...
    [ X] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary

I get the following error when I call submissionFrom without any submission_requirements, but according to https://identity.foundation/presentation-exchange/#presentation-definition, "If no submission_requirements value is present, all inputs listed in the input_descriptors array are required for submission".

2021-08-23T19:26:18.976Z error [gFBAMw0FQV] [agent.jsonldUser] STACK TRACE: TypeError: Cannot read property 'getPresentationSubmission' of null
    at EvaluationClientWrapper.remapVcs (/home/agency/src/lib/node_modules/@sphereon/pe-js/lib/evaluation/evaluationClientWrapper.ts:127:46)
    at EvaluationClientWrapper.submissionFrom (/home/agency/src/lib/node_modules/@sphereon/pe-js/lib/evaluation/evaluationClientWrapper.ts:61:17)
    at PEJS.submissionFrom (/home/agency/src/lib/node_modules/@sphereon/pe-js/lib/pejs.ts:24:42)
  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

PD group names

I'm submitting a ...

[ ] bug report
[ ] feature request
โœ… question about the decisions made in the repository
[ ] question about how to use this project

Summary

Is there a reason why group names in Presentation Definitions must be A, B, C, etc. I spent several hours trying to figure out why I was getting this error:

[
  Checked {
    tag: 'root.presentation_definition',
    status: 'error',
    message: 'input descriptor group should match the from in submission requirements.'
  }
]

The reason was because I gave my groups meaningful names such as "group": ["employmentGroup"]

I know the DIF spec shows "A", "B" as examples for group, but this shouldn't be quite literally the only options for naming groups, should it?

Other information

Invalid PD (due to descriptive group names)
{
  "id": "PD_JobApplication_123456",
  "submission_requirements": [
    {
      "name": "Employment and Academic/Certification Requirement",
      "purpose": "Verify the applicant's employment history, and at least one of academic qualification or professional certification",
      "rule": "pick",
      "min": 2,
      "from_nested": [
        {"rule": "all", "from": "employmentGroup"},
        {"rule": "pick", "min": 1, "from": "academicGroup"},
      ]
    }
  ],
  "input_descriptors": [
    {
      "id": "employmentHistoryVerification",
      "name": "Employment History",
      "purpose": "Verify the applicant's previous employment experiences",
      "group": ["employmentGroup"],
      "constraints": {
        "fields": [
          {
            "path": ["$.credentialSubject.employmentHistory"],
            "filter": {
              "type": "array"
            }
          }
        ]
      }
    },
    {
      "id": "degreeVerification",
      "name": "Degree",
      "purpose": "Confirm the applicant's academic qualification",
      "group": ["academicGroup"],
      "constraints": {
        "fields": [
          {
            "path": ["$.credentialSubject.degree"],
            "filter": {
              "type": "string",
              "pattern": "(Engineering|Computer|Cyber|Security)"
            }
          }
        ]
      }
    },
    {
      "id": "CEH_CertificationVerification",
      "name": "Certified Ethical Hacker Certification",
      "purpose": "Confirm the applicant holds a Certified Ethical Hacker certification",
      "group": ["academicGroup"],
      "constraints": {
        "fields": [
          {
            "path": ["$.credentialSubject.certifications[*].name"],
            "filter": {
              "type": "string",
              "pattern": "Certified Ethical Hacker"
            }
          },
          {
            "path": ["$.credentialSubject.certifications[*].issuer"],
            "filter": {
              "type": "string",
              "pattern": "did:example:123456789abcdefghi"
            }
          }
        ]
      }
    }
  ]
}

Invalid `name` in matches from `SelectResults`

  • I'm submitting a ...
    [x] bug report

  • Summary

When filtering credentials using the PEX library, the name property in the matches in some cases contain an invalid name.

When I define a presentation definition with two submission requirements with rule: pick and count: 1, the matches will always point to the same input descriptor, even though they satisfy another input descriptor.

See this REPL that replicates the issue: https://replit.com/@timo10/PEX-Playground-Match-name-bug

Basically what happens:

  • PD with two submission requirements. Those are satisfied with two different credentials for two different input descriptors. The matches is as follows:
[
    {
      "rule": "pick",
      "from": [
        "A"
      ],
      "vc_path": [
        "$.verifiableCredential[0]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    },
    {
      "rule": "pick",
      "from": [
        "B"
      ],
      "vc_path": [
        "$.verifiableCredential[1]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    }
  ]

As you can see the name property points to the same input descriptor id. If the input descriptor were to have a name, the property here would both be the name of the same input descriptor, even thought they point to different input descriptors (as per the submission requirements).

However, when I leave out some credentials the and the matches can't be satisfied, the name property suddenly switches to the other input descriptor id, so it's wrong the other way around (this is if I only provide the first credential):

[
    {
      "rule": "pick",
      "from": [
        "A"
      ],
      "vc_path": [
        "$.verifiableCredential[0]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea2549",
      "count": 1
    },
    {
      "rule": "pick",
      "from": [
        "B"
      ],
      "vc_path": [],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea2549",
      "count": 1
    }
 ]

Then when I only provide the second credential and not the first, it switches again from wich input descriptor id it uses:

[
    {
      "rule": "pick",
      "from": [
        "A"
      ],
      "vc_path": [],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    },
    {
      "rule": "pick",
      "from": [
        "B"
      ],
      "vc_path": [
        "$.verifiableCredential[0]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    }
 ]

It seems there's a bug in the input_desciptor that is used for the name property, as the other fields (which credential from which group it picks for the submission requirement) are correct in this case.

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
Presentation Definition
{
   "id":"022c2664-68cc-45cc-b291-789ce8b599eb",
   "name":"Presentation Definition",
   "purpose":"We want to know your name and e-mail address (will not be stored)",
   "input_descriptors":[
      {
         "id":"c2834d0e-3c95-4721-b21a-40e3d7ea2549",
         "purpose":"To access this portal your DBC Conference 2023 attendance proof is required.",
         "group":[
            "A"
         ],
         "schema":[
            {
               "uri":"DBCConferenceAttendee",
               "required":true
            }
         ],
         "constraints":{
            "fields":[
               {
                  "path":[
                     "$.credentialSubject.event.name",
                     "$.vc.credentialSubject.event.name"
                  ],
                  "filter":{
                     "type":"string",
                     "pattern":"DBC Conference 2023"
                  }
               }
            ]
         }
      },
      {
         "id":"c2834d0e-3c95-4721-b21a-40e3d7ea25434",
         "purpose":"To access this portal you need to show your JFF Plugfest OpenBadge credential.",
         "group":[
            "B"
         ],
         "schema":[
            {
               "uri":"OpenBadgeCredential",
               "required":true
            }
         ]
      }
   ],
   "submission_requirements":[
      {
         "rule":"pick",
         "count":1,
         "from":"A"
      },
      {
         "rule":"pick",
         "count":1,
         "from":"B"
      }
   ]
}
Credentials
[
	"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDprZXk6ekRuYWVlY3Vacjg2OXZTNTl4R1BSTmRTTnFEVHBvc2pTWlVqQ1E3c1RoUkExeDRDNyN6RG5hZWVjdVpyODY5dlM1OXhHUFJOZFNOcURUcG9zalNaVWpDUTdzVGhSQTF4NEM3In0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiREJDQ29uZmVyZW5jZUF0dGVuZGVlIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImZpcnN0TmFtZSI6IkphbiIsImxhc3ROYW1lIjoiUmlldHZlbGQiLCJlbWFpbCI6ImphbkBhbmltby5pZCIsImV2ZW50Ijp7Im5hbWUiOiJEQkMgQ29uZmVyZW5jZSAyMDIzIiwiZGF0ZSI6IjIwMjMtMDYtMjYifX19LCJpc3MiOiJkaWQ6a2V5OnpEbmFlZWN1WnI4Njl2UzU5eEdQUk5kU05xRFRwb3NqU1pVakNRN3NUaFJBMXg0QzciLCJzdWIiOiJkaWQ6andrOmV5SmpjbllpT2lKUUxUSTFOaUlzSW10MGVTSTZJa1ZESWl3aWVDSTZJbUZqWWtsUmFYVk5jek5wT0Y5MWMzcEZha295ZEhCVWRGSk5ORVZWTTNsNk9URlFTRFpEWkVneVZqQWlMQ0o1SWpvaVgwdGplVXhxT1haWFRYQjBibTFMZEcwME5rZHhSSG80ZDJZM05FazFURXRuY213eVIzcElNMjVUUlNKOSIsIm5iZiI6MTY4NTQ0ODAwMH0.GpNndHFkLQlR7wtl4loorizB7jCXArv6YIPW5ckmFP92BXHd4o_bX13osah_3o2iqjN7SWjwex_L3COmB02ysg",
	"eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbHBoYVRoNlNHWXdUVU5MY0dkTE9IbHhkMVZoTjA5ak5XSlBhV3h0UWpRMGRFUnlZamRPVVRCMlowa2lmUSIsIm5iZiI6MTY4NjA0NjE4OSwiaWF0IjoxNjg2MDQ2MTg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iXSwiaWQiOiJ1cm46dXVpZDpiMDczNmEwMy0wYmVjLTQ2YTYtOTFkYS0zMmFlNWRmYmYxNTYiLCJpc3N1ZXIiOnsiaWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsImltYWdlIjp7ImlkIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0yLTIwMjIvaW1hZ2VzL0pGRi1WQy1FRFUtUExVR0ZFU1QyLWJhZGdlLWltYWdlLnBuZyIsInR5cGUiOiJJbWFnZSJ9LCJuYW1lIjoiSm9icyBmb3IgdGhlIEZ1dHVyZSAoSkZGKSIsInR5cGUiOiJQcm9maWxlIiwidXJsIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0yLTIwMjIvaW1hZ2VzL0pGRi1WQy1FRFUtUExVR0ZFU1QyLWJhZGdlLWltYWdlLnBuZyJ9LCJpc3N1YW5jZURhdGUiOiIyMDIzLTA2LTA2VDEwOjA5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDYtMDZUMTA6MDk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNi0wNlQxMDowOTo0OVoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbHBoYVRoNlNHWXdUVU5MY0dkTE9IbHhkMVZoTjA5ak5XSlBhV3h0UWpRMGRFUnlZamRPVVRCMlowa2lmUSIsImFjaGlldmVtZW50Ijp7ImNyaXRlcmlhIjp7Im5hcnJhdGl2ZSI6IlRoZSBjb2hvcnQgb2YgdGhlIEpGRiBQbHVnZmVzdCAyIGluIEF1Z3VzdC1Ob3ZlbWJlciBvZiAyMDIyIGNvbGxhYm9yYXRlZCB0byBwdXNoIGludGVyb3BlcmFiaWxpdHkgb2YgVkNzIGluIGVkdWNhdGlvbiBmb3J3YXJkLiIsInR5cGUiOiJDcml0ZXJpYSJ9LCJkZXNjcmlwdGlvbiI6IlRoaXMgd2FsbGV0IGNhbiBkaXNwbGF5IHRoaXMgT3BlbiBCYWRnZSAzLjAiLCJpZCI6IjAiLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMi0yMDIyL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMi1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwibmFtZSI6Ik91ciBXYWxsZXQgUGFzc2VkIEpGRiBQbHVnZmVzdCAjMiAyMDIyIiwidHlwZSI6IkFjaGlldmVtZW50In0sInR5cGUiOiJBY2hpZXZlbWVudFN1YmplY3QifSwibmFtZSI6IkFjaGlldmVtZW50IENyZWRlbnRpYWwifSwianRpIjoidXJuOnV1aWQ6YjA3MzZhMDMtMGJlYy00NmE2LTkxZGEtMzJhZTVkZmJmMTU2In0.M7m5_E1hcCp13x4zWqZA6dASMh9sfNB9sr8Dwtm40vdQtPFyJ5PFESzPLhfv0kzyFAe3f_KMqZIbU7VKpsKACw"
]
Select Results (two credentials provided to `selectFrom`)
{
  "errors": [],
  "matches": [
    {
      "rule": "pick",
      "from": [
        "A"
      ],
      "vc_path": [
        "$.verifiableCredential[0]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    },
    {
      "rule": "pick",
      "from": [
        "B"
      ],
      "vc_path": [
        "$.verifiableCredential[1]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    }
  ],
  "areRequiredCredentialsPresent": "info",
  "verifiableCredential": [
    "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDprZXk6ekRuYWVlY3Vacjg2OXZTNTl4R1BSTmRTTnFEVHBvc2pTWlVqQ1E3c1RoUkExeDRDNyN6RG5hZWVjdVpyODY5dlM1OXhHUFJOZFNOcURUcG9zalNaVWpDUTdzVGhSQTF4NEM3In0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiREJDQ29uZmVyZW5jZUF0dGVuZGVlIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImZpcnN0TmFtZSI6IkphbiIsImxhc3ROYW1lIjoiUmlldHZlbGQiLCJlbWFpbCI6ImphbkBhbmltby5pZCIsImV2ZW50Ijp7Im5hbWUiOiJEQkMgQ29uZmVyZW5jZSAyMDIzIiwiZGF0ZSI6IjIwMjMtMDYtMjYifX19LCJpc3MiOiJkaWQ6a2V5OnpEbmFlZWN1WnI4Njl2UzU5eEdQUk5kU05xRFRwb3NqU1pVakNRN3NUaFJBMXg0QzciLCJzdWIiOiJkaWQ6andrOmV5SmpjbllpT2lKUUxUSTFOaUlzSW10MGVTSTZJa1ZESWl3aWVDSTZJbUZqWWtsUmFYVk5jek5wT0Y5MWMzcEZha295ZEhCVWRGSk5ORVZWTTNsNk9URlFTRFpEWkVneVZqQWlMQ0o1SWpvaVgwdGplVXhxT1haWFRYQjBibTFMZEcwME5rZHhSSG80ZDJZM05FazFURXRuY213eVIzcElNMjVUUlNKOSIsIm5iZiI6MTY4NTQ0ODAwMH0.GpNndHFkLQlR7wtl4loorizB7jCXArv6YIPW5ckmFP92BXHd4o_bX13osah_3o2iqjN7SWjwex_L3COmB02ysg",
    "eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbHBoYVRoNlNHWXdUVU5MY0dkTE9IbHhkMVZoTjA5ak5XSlBhV3h0UWpRMGRFUnlZamRPVVRCMlowa2lmUSIsIm5iZiI6MTY4NjA0NjE4OSwiaWF0IjoxNjg2MDQ2MTg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iXSwiaWQiOiJ1cm46dXVpZDpiMDczNmEwMy0wYmVjLTQ2YTYtOTFkYS0zMmFlNWRmYmYxNTYiLCJpc3N1ZXIiOnsiaWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsImltYWdlIjp7ImlkIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0yLTIwMjIvaW1hZ2VzL0pGRi1WQy1FRFUtUExVR0ZFU1QyLWJhZGdlLWltYWdlLnBuZyIsInR5cGUiOiJJbWFnZSJ9LCJuYW1lIjoiSm9icyBmb3IgdGhlIEZ1dHVyZSAoSkZGKSIsInR5cGUiOiJQcm9maWxlIiwidXJsIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0yLTIwMjIvaW1hZ2VzL0pGRi1WQy1FRFUtUExVR0ZFU1QyLWJhZGdlLWltYWdlLnBuZyJ9LCJpc3N1YW5jZURhdGUiOiIyMDIzLTA2LTA2VDEwOjA5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDYtMDZUMTA6MDk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNi0wNlQxMDowOTo0OVoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbHBoYVRoNlNHWXdUVU5MY0dkTE9IbHhkMVZoTjA5ak5XSlBhV3h0UWpRMGRFUnlZamRPVVRCMlowa2lmUSIsImFjaGlldmVtZW50Ijp7ImNyaXRlcmlhIjp7Im5hcnJhdGl2ZSI6IlRoZSBjb2hvcnQgb2YgdGhlIEpGRiBQbHVnZmVzdCAyIGluIEF1Z3VzdC1Ob3ZlbWJlciBvZiAyMDIyIGNvbGxhYm9yYXRlZCB0byBwdXNoIGludGVyb3BlcmFiaWxpdHkgb2YgVkNzIGluIGVkdWNhdGlvbiBmb3J3YXJkLiIsInR5cGUiOiJDcml0ZXJpYSJ9LCJkZXNjcmlwdGlvbiI6IlRoaXMgd2FsbGV0IGNhbiBkaXNwbGF5IHRoaXMgT3BlbiBCYWRnZSAzLjAiLCJpZCI6IjAiLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMi0yMDIyL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMi1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwibmFtZSI6Ik91ciBXYWxsZXQgUGFzc2VkIEpGRiBQbHVnZmVzdCAjMiAyMDIyIiwidHlwZSI6IkFjaGlldmVtZW50In0sInR5cGUiOiJBY2hpZXZlbWVudFN1YmplY3QifSwibmFtZSI6IkFjaGlldmVtZW50IENyZWRlbnRpYWwifSwianRpIjoidXJuOnV1aWQ6YjA3MzZhMDMtMGJlYy00NmE2LTkxZGEtMzJhZTVkZmJmMTU2In0.M7m5_E1hcCp13x4zWqZA6dASMh9sfNB9sr8Dwtm40vdQtPFyJ5PFESzPLhfv0kzyFAe3f_KMqZIbU7VKpsKACw"
  ],
  "warnings": []
}
Select Results (only first credential provided to `selectFrom`)
{
  "errors": [
    {
      "tag": "UriEvaluation",
      "status": "error",
      "message": "@context URI for the of the candidate input MUST be equal to one of the input_descriptors object uri values exactly.: $.input_descriptors[1]: $.verifiableCredential[0]"
    },
    {
      "tag": "MarkForSubmissionEvaluation",
      "status": "error",
      "message": "The input candidate is not eligible for submission: $.input_descriptors[1]: $.verifiableCredential[0]"
    }
  ],
  "matches": [
    {
      "rule": "pick",
      "from": [
        "A"
      ],
      "vc_path": [
        "$.verifiableCredential[0]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea2549",
      "count": 1
    },
    {
      "rule": "pick",
      "from": [
        "B"
      ],
      "vc_path": [],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea2549",
      "count": 1
    }
  ],
  "areRequiredCredentialsPresent": "error",
  "verifiableCredential": [
    "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDprZXk6ekRuYWVlY3Vacjg2OXZTNTl4R1BSTmRTTnFEVHBvc2pTWlVqQ1E3c1RoUkExeDRDNyN6RG5hZWVjdVpyODY5dlM1OXhHUFJOZFNOcURUcG9zalNaVWpDUTdzVGhSQTF4NEM3In0.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiREJDQ29uZmVyZW5jZUF0dGVuZGVlIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7ImZpcnN0TmFtZSI6IkphbiIsImxhc3ROYW1lIjoiUmlldHZlbGQiLCJlbWFpbCI6ImphbkBhbmltby5pZCIsImV2ZW50Ijp7Im5hbWUiOiJEQkMgQ29uZmVyZW5jZSAyMDIzIiwiZGF0ZSI6IjIwMjMtMDYtMjYifX19LCJpc3MiOiJkaWQ6a2V5OnpEbmFlZWN1WnI4Njl2UzU5eEdQUk5kU05xRFRwb3NqU1pVakNRN3NUaFJBMXg0QzciLCJzdWIiOiJkaWQ6andrOmV5SmpjbllpT2lKUUxUSTFOaUlzSW10MGVTSTZJa1ZESWl3aWVDSTZJbUZqWWtsUmFYVk5jek5wT0Y5MWMzcEZha295ZEhCVWRGSk5ORVZWTTNsNk9URlFTRFpEWkVneVZqQWlMQ0o1SWpvaVgwdGplVXhxT1haWFRYQjBibTFMZEcwME5rZHhSSG80ZDJZM05FazFURXRuY213eVIzcElNMjVUUlNKOSIsIm5iZiI6MTY4NTQ0ODAwMH0.GpNndHFkLQlR7wtl4loorizB7jCXArv6YIPW5ckmFP92BXHd4o_bX13osah_3o2iqjN7SWjwex_L3COmB02ysg"
  ],
  "warnings": []
}
Select Results (only second credential provided to `selectFrom`)
{
  "errors": [
    {
      "tag": "UriEvaluation",
      "status": "error",
      "message": "@context URI for the of the candidate input MUST be equal to one of the input_descriptors object uri values exactly.: $.input_descriptors[0]: $.verifiableCredential[0]"
    },
    {
      "tag": "FilterEvaluation",
      "status": "error",
      "message": "Input candidate does not contain property: $.input_descriptors[0]: $.verifiableCredential[0]"
    },
    {
      "tag": "MarkForSubmissionEvaluation",
      "status": "error",
      "message": "The input candidate is not eligible for submission: $.input_descriptors[0]: $.verifiableCredential[0]"
    }
  ],
  "matches": [
    {
      "rule": "pick",
      "from": [
        "A"
      ],
      "vc_path": [],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    },
    {
      "rule": "pick",
      "from": [
        "B"
      ],
      "vc_path": [
        "$.verifiableCredential[0]"
      ],
      "name": "c2834d0e-3c95-4721-b21a-40e3d7ea25434",
      "count": 1
    }
  ],
  "areRequiredCredentialsPresent": "error",
  "verifiableCredential": [
    "eyJraWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCMwIiwidHlwIjoiSldUIiwiYWxnIjoiRWREU0EifQ.eyJpc3MiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsInN1YiI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbHBoYVRoNlNHWXdUVU5MY0dkTE9IbHhkMVZoTjA5ak5XSlBhV3h0UWpRMGRFUnlZamRPVVRCMlowa2lmUSIsIm5iZiI6MTY4NjA0NjE4OSwiaWF0IjoxNjg2MDQ2MTg5LCJ2YyI6eyJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiT3BlbkJhZGdlQ3JlZGVudGlhbCJdLCJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vcHVybC5pbXNnbG9iYWwub3JnL3NwZWMvb2IvdjNwMC9jb250ZXh0Lmpzb24iXSwiaWQiOiJ1cm46dXVpZDpiMDczNmEwMy0wYmVjLTQ2YTYtOTFkYS0zMmFlNWRmYmYxNTYiLCJpc3N1ZXIiOnsiaWQiOiJkaWQ6andrOmV5SnJkSGtpT2lKUFMxQWlMQ0oxYzJVaU9pSnphV2NpTENKamNuWWlPaUpGWkRJMU5URTVJaXdpYTJsa0lqb2lOMlEyWTJKbU1qUTRPV0l6TkRJM05tSXhOekl4T1RBMU5EbGtNak01TVRnaUxDSjRJam9pUm01RlZWVmhkV1J0T1RsT016QmlPREJxY3poV2REUkJiazk0ZGxKM1dIUm5VbU5MY1ROblFrbDFPQ0lzSW1Gc1p5STZJa1ZrUkZOQkluMCIsImltYWdlIjp7ImlkIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0yLTIwMjIvaW1hZ2VzL0pGRi1WQy1FRFUtUExVR0ZFU1QyLWJhZGdlLWltYWdlLnBuZyIsInR5cGUiOiJJbWFnZSJ9LCJuYW1lIjoiSm9icyBmb3IgdGhlIEZ1dHVyZSAoSkZGKSIsInR5cGUiOiJQcm9maWxlIiwidXJsIjoiaHR0cHM6Ly93M2MtY2NnLmdpdGh1Yi5pby92Yy1lZC9wbHVnZmVzdC0yLTIwMjIvaW1hZ2VzL0pGRi1WQy1FRFUtUExVR0ZFU1QyLWJhZGdlLWltYWdlLnBuZyJ9LCJpc3N1YW5jZURhdGUiOiIyMDIzLTA2LTA2VDEwOjA5OjQ5WiIsImlzc3VlZCI6IjIwMjMtMDYtMDZUMTA6MDk6NDlaIiwidmFsaWRGcm9tIjoiMjAyMy0wNi0wNlQxMDowOTo0OVoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpQUzFBaUxDSmpjbllpT2lKRlpESTFOVEU1SWl3aWVDSTZJbHBoYVRoNlNHWXdUVU5MY0dkTE9IbHhkMVZoTjA5ak5XSlBhV3h0UWpRMGRFUnlZamRPVVRCMlowa2lmUSIsImFjaGlldmVtZW50Ijp7ImNyaXRlcmlhIjp7Im5hcnJhdGl2ZSI6IlRoZSBjb2hvcnQgb2YgdGhlIEpGRiBQbHVnZmVzdCAyIGluIEF1Z3VzdC1Ob3ZlbWJlciBvZiAyMDIyIGNvbGxhYm9yYXRlZCB0byBwdXNoIGludGVyb3BlcmFiaWxpdHkgb2YgVkNzIGluIGVkdWNhdGlvbiBmb3J3YXJkLiIsInR5cGUiOiJDcml0ZXJpYSJ9LCJkZXNjcmlwdGlvbiI6IlRoaXMgd2FsbGV0IGNhbiBkaXNwbGF5IHRoaXMgT3BlbiBCYWRnZSAzLjAiLCJpZCI6IjAiLCJpbWFnZSI6eyJpZCI6Imh0dHBzOi8vdzNjLWNjZy5naXRodWIuaW8vdmMtZWQvcGx1Z2Zlc3QtMi0yMDIyL2ltYWdlcy9KRkYtVkMtRURVLVBMVUdGRVNUMi1iYWRnZS1pbWFnZS5wbmciLCJ0eXBlIjoiSW1hZ2UifSwibmFtZSI6Ik91ciBXYWxsZXQgUGFzc2VkIEpGRiBQbHVnZmVzdCAjMiAyMDIyIiwidHlwZSI6IkFjaGlldmVtZW50In0sInR5cGUiOiJBY2hpZXZlbWVudFN1YmplY3QifSwibmFtZSI6IkFjaGlldmVtZW50IENyZWRlbnRpYWwifSwianRpIjoidXJuOnV1aWQ6YjA3MzZhMDMtMGJlYy00NmE2LTkxZGEtMzJhZTVkZmJmMTU2In0.M7m5_E1hcCp13x4zWqZA6dASMh9sfNB9sr8Dwtm40vdQtPFyJ5PFESzPLhfv0kzyFAe3f_KMqZIbU7VKpsKACw"
  ],
  "warnings": []
}

Nest JS import problem. (ESM vs CommonJs problem)

  • I'm submitting a ...
    [x] bug report
    [x] feature request

  • Summary
    This package is structured as ESM and therefore can not be used by Nest JS Typescript Projects, because it only supports Common JS.
    Example import:
    import { IPresentationDefinition, PEX } from "@sphereon/pex";

Error output:

Error [ERR_REQUIRE_ESM]: require() of ES Module {module location} not supported. index.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules. Instead rename index.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in {package location} to treat all .js files as CommonJS (using .mjs for all ES modules instead).

What made the import work is deleting line 5 "type": "module", and lines 90 to 107 "exports": { ".": { "import": { "types": "./dist/module/index.d.ts", "default": "./dist/module/index.js" }, "require": { "types": "./dist/main/index.d.ts", "default": "./dist/main/index.js" }, "browser": { "types": "./dist/browser/index.d.ts", "default": "./dist/browser/index.js" }, "default": "./dist/main/index.js" }, "./package.json": "./package.json" }

Can you please provide alternatives for frameworks, using CommonJS?

The fields object MUST contain a path property.

But in the code Path object is optional:

export interface FieldV2 {
    id?: string;
    path?: Array<string>;
    purpose?: string;
    filter?: FilterV2;
    predicate?: Optionality;
    name?: string;
}

Any reason for this or is this a bug?

build error related to preinstall script: "npx only-allow pnpm"

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    We use PEX with OWF Credo and Bifold. pnpm is not supported by react native, is it possible to not enforce consumers of this library to also use pnpm?
    we are getting a build error:

  โžค YN0000: โ”‚ @sphereon/pex@npm:3.3.3 STDERR sh: 1: only-allow: not found
  โžค YN0009: โ”‚ @sphereon/pex@npm:3.3.3 couldn't be built successfully (exit code 127, logs can be found here: /tmp/xfs-42e00f6b/build.log)
  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

pe-js typescript error in submissionRequirementVB.ts build error

  • I'm submitting a ...
    [X ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary

I'm getting the following typescript error in my build which is coming from pe-js:

node_modules/@sphereon/pe-js/dist/main/lib/validation/bundlers/fieldsVB.d.ts:14:5 - error TS2416: Property 'getValidations' in type 'FieldsVB' is not assignable to the same property in base type 'ValidationBundler<Field[]>'.
  Type '(fields: Field[]) => Validation<Validatable>[]' is not assignable to type '(t: Field[] | Field[][]) => Validation<unknown>[]'.
    Type 'Validation<Validatable>[]' is not assignable to type 'Validation<unknown>[]'.
      Type 'Validation<Validatable>' is not assignable to type 'Validation<unknown>'.
        Types of property 'predicate' are incompatible.
          Type 'ValidationPredicate<Validatable>' is not assignable to type 'ValidationPredicate<unknown>'.
            Type 'unknown' is not assignable to type 'Validatable'.

14     getValidations(fields: Field[]): Validation<Validatable>[];
       ~~~~~~~~~~~~~~

node_modules/@sphereon/pe-js/dist/main/lib/validation/bundlers/submissionRequirementVB.d.ts:13:5 - error TS2416: Property 'getValidations' in type 'SubmissionRequirementVB' is not assignable to the same property in base type 'ValidationBundler<SubmissionRequirement>'.
  Type '(srs: SubmissionRequirement[]) => Validation<Validatable>[]' is not assignable to type '(t: SubmissionRequirement | SubmissionRequirement[]) => Validation<unknown>[]'.
    Type 'Validation<Validatable>[]' is not assignable to type 'Validation<unknown>[]'.

13     getValidations(srs: SubmissionRequirement[]): Validation<Validatable>[];
       ~~~~~~~~~~~~~~


Found 2 errors.

In order to reproduce:

  1. unzip the attachment pe-compile-err.zip
  2. cd pe-compile-err
  3. npm install
  4. tsc

Also, could you please make this or something similar to this a part of your CI/CD pipeline in order to make sure there are no compilations tsc errors going forward?

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

How to call async presentation signing method inside the CallBack function of PEXv1.verifiablePresentationFrom

  • I'm submitting a ...
    [ ] bug report
    [x] feature request
    [ ] question about the decisions made in the repository
    [x] question about how to use this project

  • Summary
    I am trying to sign the presentation using an external Library which is an async call. how can I use that async sign mechanism inside the Callback function of PEXv1.verifiablePresentationFrom()

  • Other information
    I am using the version 1 of the PEX Library

Decoded credential always wants a hasher function for sd-jwt

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    According to the note here you need to decode the sd-jwt in case the hasher function is async (which is in my canse since its executed in the browser via webcrypto). So I am decoding the list of credentials like this:

const vcs: SdJwtDecodedVerifiableCredential[] = [];
      for (const credential of credentials) {
        const decoded = await CredentialMapper.decodeSdJwtVcAsync(
          credential.value,
          getHasher()
        );
        vcs.push(decoded);
      }      
      const pex = new PresentationExchange({        
        allVerifiableCredentials: vcs,
      });

But then I am running in this error Hasher must be provided when creating a presentation with an SD-JWT VC. After logging the credentials and the conditions I get this:

{"compactSdJwtVc":"eyJ0eXAiOiJ2YytzZC1qd3QiLCJhbGciOiJFZERTQSJ9.eyJpYXQiOjE3MTIwOTI0NzU1MjUsImlzcyI6Iklzc3VlciIsInZjdCI6IklkZW50aXR5IiwianRpIjoiNzE2OTBiOTItYjI1My00OGFiLThlNjctMjFkYzAzN2I2YWY0IiwiY25mIjp7ImtpZCI6ImRpZDpqd2s6ZXlKcmRIa2lPaUpGUXlJc0luZ2lPaUpCY1hSQmMzVlFRMnN5WVd4cmNVazBaRkZpVUdaQlpHWnRNMHN5TFRZdGNVbE9WRVJ5YUZoaFJFazBJaXdpZVNJNklsaEJVVWh2UzBWdGVHWnRiVkZXUldoQ0xVcFlVMXBMV0VWa1JHSjVha3BpVEZacE5WbGxRMEZvVHpBaUxDSmpjbllpT2lKUUxUSTFOaUo5IzAifSwiX3NkIjpbIkdEdEIweTg5ZzRmMzJBeUhzU2F4N0pHbDA2VkhlV2IxZVozZXloZ0dsN0UiLCJsdW5zQjJ5a3pQVEowd0NKYlZndlpaV3BsUUktOFpnRW1QNjV2X1o5N1IwIl0sIl9zZF9hbGciOiJTSEEtMjU2In0.wmwlrsfXYa-B15yPRTjCulWNBeWLHxIjVynKN0UgIBatzGcmloWENCVBSLPYt1xc-o0ueYyemKDa8icOaPPu1w~WyJhZmE3Mzg2OTMzOTJlOTE3IiwicHJlbmFtZSIsIk1pcmtvIl0~WyI1NzcxYjhiMDA5YzMxMzMzIiwic3VybmFtZSIsIk1vbGxpayJd~","decodedPayload":{"iat":1712092475525,"iss":"Issuer","vct":"Identity","jti":"71690b92-b253-48ab-8e67-21dc037b6af4","cnf":{"kid":"did:jwk:eyJrdHkiOiJFQyIsIngiOiJBcXRBc3VQQ2syYWxrcUk0ZFFiUGZBZGZtM0syLTYtcUlOVERyaFhhREk0IiwieSI6IlhBUUhvS0VteGZtbVFWRWhCLUpYU1pLWEVkRGJ5akpiTFZpNVllQ0FoTzAiLCJjcnYiOiJQLTI1NiJ9#0"},"prename":"Mirko","surname":"Mollik"},"disclosures":[{"decoded":["afa738693392e917","prename","Mirko"],"digest":"GDtB0y89g4f32AyHsSax7JGl06VHeWb1eZ3eyhgGl7E","encoded":"WyJhZmE3Mzg2OTMzOTJlOTE3IiwicHJlbmFtZSIsIk1pcmtvIl0"},{"decoded":["5771b8b009c31333","surname","Mollik"],"digest":"lunsB2ykzPTJ0wCJbVgvZZWplQI-8ZgEmP65v_Z97R0","encoded":"WyI1NzcxYjhiMDA5YzMxMzMzIiwic3VybmFtZSIsIk1vbGxpayJd"}],"signedPayload":{"iat":1712092475525,"iss":"Issuer","vct":"Identity","jti":"71690b92-b253-48ab-8e67-21dc037b6af4","cnf":{"kid":"did:jwk:eyJrdHkiOiJFQyIsIngiOiJBcXRBc3VQQ2syYWxrcUk0ZFFiUGZBZGZtM0syLTYtcUlOVERyaFhhREk0IiwieSI6IlhBUUhvS0VteGZtbVFWRWhCLUpYU1pLWEVkRGJ5akpiTFZpNVllQ0FoTzAiLCJjcnYiOiJQLTI1NiJ9#0"},"_sd":["GDtB0y89g4f32AyHsSax7JGl06VHeWb1eZ3eyhgGl7E","lunsB2ykzPTJ0wCJbVgvZZWplQI-8ZgEmP65v_Z97R0"],"_sd_alg":"SHA-256"}}

isdecoded true
isEncoded false

So is the intention of if (credentials.some((c) => CredentialMapper.isSdJwtDecodedCredential(c) || CredentialMapper.isSdJwtEncoded(c))) { to only get the sd-jwt-vc credentials, the error seems to be here.

To avoid the hash call in the function, we need to pass the pre calculated sdHash in the SdJwtDecodedVerifiableCredential object.

Or we just make the hasher call async since PresentationExchange.findValidPresentationDefinitions is already async :)

`PEX.validateDefinition` gives different results based on whether it is ran in Node.JS or React Native

  • I'm submitting a ...
    [x] bug report

  • Summary

Calling PEX.validateDefinition gives different results based on whether it is ran in Node.JS or React Native.

When calling validateDefinition on the following definition:

{
   "id":"022c2664-68cc-45cc-b291-789ce8b599eb",
   "purpose":"We want to know your name and e-mail address (will not be stored)",
   "input_descriptors":[
      {
         "id":"c2834d0e-3c95-4721-b21a-40e3d7ea2549",
         "name":"DBC Conference 2023 Attendee",
         "purpose":"To access this portal your DBC Conference 2023 attendance proof is required.",
         "schema":[
            {
               "uri":"DBCConferenceAttendee"
            }
         ],
         "constraints":{
            "fields":[
               {
                  "path":[
                     "$.credentialSubject.event.name",
                     "$.vc.credentialSubject.event.name"
                  ],
                  "filter":{
                     "type":"string",
                     "pattern":"DBC Conference 2023"
                  }
               }
            ]
         }
      }
   ]
}

The result in Node.JS (v18) for me is:

[ Checked { tag: 'root', status: 'info', message: 'ok' } ]

While in React Native I get:

[
   {
      "message":"presentation_definition should be as per json schema.",
      "status":"error",
      "tag":"root.presentation_definition"
   },
   {
      "message":"field object \"filter\" property must be valid json schema",
      "status":"error",
      "tag":"presentation_definition.input_descriptor[0].constraints.fields[0]"
   }
]

Not really an idea why this is happening. Bot environments are using 2.0.1 of PEX

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

Unexpected error on calling submissionFrom

  • I'm submitting a ...
    [X ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Unexpected error on calling submissionFrom
    I'm calling the submissionFrom with the presentationDefinition, and vc list as below:

presentationDefinition: {
  "id": "Insurance Plans",
  "input_descriptors": [
    {
      "id": "Ontario Health Insurance Plan",
      "schema": [
        {
          "uri": "https://did.itsourweb.org:3000/smartcredential/Ontario-Health-Insurance-Plan"
        },
        {
          "uri": "https://www.w3.org/2018/credentials/v1"
        }
      ],
      "constraints": {
        "limit_disclosure": "required",
        "fields": [
          {
            "path": [
              "$.issuer.id"
            ],
            "purpose": "We can only verify bank accounts if they are attested by a source.",
            "filter": {
              "type": "string",
              "pattern": "did:example:issuer"
            }
          }
        ]
      }
    }
  ]
},
verifiableCredentials: [
  {
    "@context": [
      "https://www.w3.org/2018/credentials/v1",
      "https://www.w3.org/2018/credentials/examples/v1"
    ],
    "id": "https://example.com/credentials/1872",
    "type": [
      "VerifiableCredential",
      "IDCardCredential"
    ],
    "issuer": {
      "id": "did:example:issuer"
    },
    "credentialSubject": {
      "given_name": "Fredrik",
      "family_name": "Stremberg",
      "birthdate": "1949-01-22"
    }
  }
]

And I'm getting the error:

Cannot read property 'getPresentationSubmission' of null
TypeError: Cannot read property 'getPresentationSubmission' of null

Seems like we don't throw the error You need to call evaluate() before submissionFrom() which is designed for this situation

Open to add support for new (non-standardized) `di_vc` and `di_vp` format?

We're currently in the process of adopting the new Data Integrity Proofs specification, mainly for work we're doing to make AnonCreds compliant with W3C (using Data Integrity Proofs).

The Data Integrity Proofs spec supersedes the Linked Data Proofs specification, and has some changes in it's requirements, but is overall pretty similar.

One major difference is the usage and recommendation of a general purpose DataIntegrityProof type, with a compaion cryptosuite value.

I've openend a similar issue in the Claim Format Registry repository about how to deal with the ambiguity of DataIntegrityProof type by itself: decentralized-identity/claim-format-registry#8

From the two proposed approaches I feel the most for a new di_vc and di_vp type as the spec is now called like this, and there's some differences between the two. processors will mostly be able to use the ldp_vc and di_vc interchangeably, but there are some subtle differences.

Are you open to implementing this new format, even though it has not been formalized yet? The PEX library is very strict on the formats and thus passing anything that it doesn't support in the format will throw an error, so we can't really build it on top of PEX ourselves (see also #134)

We can also fall back to using di_vc with DataIntegrityProof for now until there is some input of the claim format, but this will make the querying a bit less useful.

Design question: SD-JWT support in PEx

I've been working on SD-JWT support in PEX, and most of the logic is now in place to handle SD-JWT credentials, and also create a derived SD-JWT from it based on limit disclosure.

Now I'm running into some complexities and would like to get the input from the library maintainers on what the way to go is. Basically parsing and handling an SD-JWT is quite complex, especially if you want to map some required fields that should be disclosed into which dislcosures must be present in the presentation.

In addition, to properly parse an SD-JWT (and know which values belong where in the pretty payload (all _sd and ... values replaced with their disclosure values, on this format we do the PEX selection), you need a hasher implementation, to create digests over the disclosures (this is a deliberate design decision: oauth-wg/oauth-selective-disclosure-jwt#381).

So this leaves us with a few paths to go down into.

First in terms of the input credentials to this library

  • Users need to provide a 'parsed' SD-JWT into this library where digests are calculated, and the SD-JWT is decoded into a pretty format. This means we don't need a hasher implementation in this library
  • Users provide an encoded SD-JWT (string) and this library will decode it. But to properly handle it, it will need a hasher implementation. We should allows users to provide this using options in this case.

Then moving on, we need to be able to determine which disclosures are required based on the presentation definition, and disclosure the minimally required fields. This logic is quite advanced, and for this there's again a few paths we can go down with:

  • We parse the required fields and pass that as input to the user, the user will have to figure out which disclosures are required, and create a valid presentation out of it
  • This library determines the required disclosures based on the presentation definition. We need to do some complex logic to determine this

The for both scenarios, if we choose the advanced route (we take care of it for the user) there's is the choice of doing this within the PEX library directly, or to use an existing implementation of SD-JWt. For example, the SD-JWT library we've been working on, contains most of the logic needed to do the parsing and presenting, and it would make the PEX implementation a lot simpler. The library does not depend on any cyrpto/hashing, etc.. implementation and the only dep is buffer for cross-platform support.

If we don't want to depend on an external library for this (which is fair) we would have to duplicate all neccesary code to the PEX library (It would probably be 500-1000 lines of code)

@nklomp would love your input on this!

cc @berendsliedrecht

FilterEvaluation handler: bug/feature request

  • I'm submitting a ...
    [x] bug report
    [x] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary

Working with the latest version of the library I am not able to validate a presentation submission to comply with a presentation definition with multiple input descriptors.

inputDescriptorFilterEvaluationHandler.spec.zip
This is an updated test spec illustrating the issue. I provide it as a file here as I am not sure whether it's a desired feature of the library the current behavior or a bug worth opening a pull request for.

Debugging the corresponding handler I see 2 issues that prevent correct utilization of the library at the moment:

  1. No IDs are taken into account at the moment.

The descriptor_map object MUST include an id property. The value of this property MUST be a string that matches the id property of the Input Descriptor in the Presentation Definition that this Presentation Submission is related to.

Taken from https://identity.foundation/presentation-exchange/spec/v2.0.0/#presentation-submission

Holds true for both jp.nodes(pd, '$..fields[*]'); and wrappedVcs which hold no such context. Thus, it's impossible to extend current logic of the handler to map 1:1 PS to PD without broader surface change in the library. (I think)

  1. Each VC is expected to comply with all input descriptors of the PD

This is more problematic in my opinion. Is it wrong expectation from my side that

{
          id: 'same-device-in-time-credential',
          path: '$',
          format: 'jwt_vp',
          path_nested: {
            id: 'same-device-in-time-credential',
            path: '$.verifiableCredential[0]',
            format: 'jwt_vc',
         }
 }

should be compared only to

 {
          id: 'same-device-in-time-credential',
          constraints: {
            fields: [
              {
                path: ['$.type'],
                filter: {
                  type: 'array',
                  contains: { const: 'CTWalletSameInTime' },
                },
              },
            ],
          }
        }
  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

I made an attempt of a workaround by assuming an index based comparison in hope to find a solution without a major refactoring, but this breaks a lot of existing tests

public handle(pd: IInternalPresentationDefinition, wrappedVcs: WrappedVerifiableCredential[]): void {
    const fields: { path: PathComponent[]; value: FieldV1 | FieldV2 }[] = jp.nodes(pd, '$..fields[*]');
    wrappedVcs.forEach((wvc: WrappedVerifiableCredential, vcIndex: number) => {
      this.createNoFieldResults(pd, vcIndex, wvc);
    });
    fields.forEach((field, inputDescriptorIndex) => {
      const wvc = wrappedVcs[inputDescriptorIndex];
      let inputField: { path: PathComponent[]; value: unknown }[] = [];
      if (field.value.path) {
        inputField = JsonPathUtils.extractInputField(wvc.credential, field.value.path);
      }
      let resultFound = false;
      for (const inputFieldKey of inputField) {
        if (this.evaluateFilter(inputFieldKey, field.value)) {
          resultFound = true;
          const payload = { result: { ...inputField[0] }, valid: true, format: wvc.format };
          this.getResults().push({
            ...this.createResultObject(jp.stringify(field.path.slice(0, 3)), inputDescriptorIndex, payload),
          });
        }
      }
      if (!resultFound) {
        if (!inputField.length) {
          const payload = { valid: false, format: wvc.format };
          this.createResponse(field, inputDescriptorIndex, payload, PexMessages.INPUT_CANDIDATE_DOESNT_CONTAIN_PROPERTY);
        } else {
          const payload = { result: { ...inputField[0] }, valid: false, format: wvc.format };
          this.createResponse(field, inputDescriptorIndex, payload, PexMessages.INPUT_CANDIDATE_FAILED_FILTER_EVALUATION);
        }
      }
    });
    this.updatePresentationSubmission(pd);
  }

Difficult/impossible to match matches against the input_descriptors that satisfied them

  • I'm submitting a ...
    [x] bug report
    [x] feature request

  • Summary

The pex library filters credentials based on the submission requirements and input descriptors, and will return a set of matches based on that that satisfy the definition.

However, I'm having a hard time to to find the input_descriptors that satisfy a match, so we can correctly render this in the wallet.

If the input descriptor doesn't contain a name, the name of the match will be the id of the input descriptor that satisfied it (in case there was a single input descriptor that satisfied the match). However when name is present this isn't the case.

We want the user to be able to select which credentials to share, and the general process I'm following is:

  • loop through the submission requirements
  • for each submission requirement, find the input_descriptors that are related to that submission requirement
  • for each of the input descriptors, find the (potentional) matches for that input descriptors.

Currently the matches group the credentials, which make it hard to figure out which credentials satisfy which input_descriptor.

Maybe there could be a matches for input_descriptors where each input descriptor gets a list of verifiable credentials that match:

{
  "inputDescriptorId": "id-of-input-descriptor"
  // or verifiableCredential
  "vc_path": ["$.verififiableCredential[0]", "$.verififiableCredential[1]"],
},
{
  "inputDescriptorId": "id-of-input-descriptor2"
  "vc_path": ["$.verififiableCredential[2]"],
}

Then separate for that there could be a matches list that takes into account the submission requirements. Where each match points to several input descriptor ids that can satisfy them. E.g.:

This means we have to pick one credential from all of the input descriptors. The library provides you with matches for each of the input descriptors.

{
  "rule": "all",
  "from": "A",
  "inputDescriptorIds": ["id-of-input-descriptor", "id-of-input-descriptor2"],
}

Here is an example with pick. you need to pick one credentials from one of the input descriptors. You can choose if you choose the first or last one. If an inputDescriptor doesn't have any credentials that satisfy it, it won't be included here, as you can't use to to select a credential from.

{
  "rule": "pick",
  "from": "A",
  "count": 1,
  "inputDescriptorIds": ["id-of-input-descriptor", "id-of-input-descriptor2"],
}

I think especially the second example with pick is related to #115, where I don't think the library is currently following the spec, and following it would I think require a change to the structure to be able to determine which credentials to pick.

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

Including PEX in a vite build results in incorrect build output

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    jsonPathUtils.js has mixed import/require which seems to confuse some js compilers. Specifically vite. When I run my app which includes a library which includes PEX I end up with a require() call in the bundled javascript. When I build the library that includes PEX using esbuild I end up with the correct bundled output.

It seems like vite/rollup.js isn't able to handle the mixed import/require. There is an older bug noting this issue rollup/rollup-plugin-commonjs#273 wherein the rollup authors essentially say "don't do that then please".

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)
    This should be able to be fixable via a diff like so:
    @@ -1,10 +1,15 @@
    import { JSONPath as jp } from '@astronautlabs/jsonpath';
    import { PresentationDefinitionV1, PresentationDefinitionV2 } from '@sphereon/pex-models';
    +// eslint-disable-next-line @typescript-eslint/ban-ts-comment
    +// @ts-ignore @eslint-ignore
    +import shim from 'string.prototype.matchall';

import { InputFieldType, IPresentationDefinition, PathComponent } from '../types';

+shim();
+
+
export class JsonPathUtils {

  • static matchAll = require('string.prototype.matchall');
    static REGEX_PATH = /@\w+/g;
    /**

    • @param obj: any object can be found in verifiablePresentation.verifiableCredential[i]
      @@ -114,7 +119,7 @@ export class JsonPathUtils {
      }

    private static modifyPathWithSpecialCharacter(path: string): string {

  • const matches = this.matchAll(path, this.REGEX_PATH);

  • const matches = path.matchAll(this.REGEX_PATH);
    path = this.modifyPathRecursive(matches, path);
    return path;
    }
    @@ -132,7 +137,7 @@ export class JsonPathUtils {
    if (path.substring(atIdx - 2, atIdx) === '..') {
    path = path.substring(0, atIdx - 2) + "..['" + next.value[0] + "']" + path.substring(atIdx + next.value[0].length);
    indexChanged = true;
  •      const matches = this.matchAll(path, this.REGEX_PATH);
    
  •      const matches = path.matchAll(this.REGEX_PATH);
         this.modifyPathRecursive(matches, path);
       } else if (path.charAt(atIdx - 1) === '.') {
         path = path.substring(0, atIdx - 1) + "['" + next.value[0] + "']" + path.substring(atIdx + next.value[0].length);
    

Question on how to use APIs with proof generation

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [X] question about how to use this project

  • Summary

Hi,

I would like to use pe-js with BBS+ in which I generate a proof from a credential. Is this possible? When looking at the example at https://github.com/Sphereon-Opensource/pe-js#credential-query, it is not clear where I make the call to generate the proof. Is it before or after the call to pejs.submissionFrom, or is there some option to that call which allow passing in the function which generates the proof?

Thanks in advance,
Keith

  • Other information (e.g. detailed explanation, stack traces, related issues, suggestions how to fix, links for us to have context, eg. StackOverflow, personal fork, etc.)

Bug(sd-jwt): _sd_hash was renamed to sh_hash

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [ ] question about how to use this project

  • Summary
    After draft 6 the variable was renamed to sd_hash. A simple rename should be fine here:

    PEX/lib/PEX.ts

    Line 312 in 3432a23

    _sd_hash: sdHash,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.