Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: external signature suite provider interface #2835

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions aries_cloudagent/vc/vc_ld/external_suite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Plugin hook for providing an external signature suite implementation.

This enables greater control over where JSON-LD credentials are signed without
requiring knowledge of the complexities of the JSON-LD/VC-LDP subsystem.
"""

from abc import ABC, abstractmethod
from typing import Optional

from ...core.error import BaseError
from ...core.profile import Profile
from ...wallet.did_info import DIDInfo
from ..ld_proofs.suites.linked_data_proof import LinkedDataProof


class ExternalSuiteError(BaseError):
"""Raised when an error occurs in an external signature suite provider."""


class ExternalSuiteNotFoundError(ExternalSuiteError):
"""Raised when an external signature suite provider is not found.

This should be raised to prevent falling back to built in suites, if desired.
"""


class ExternalSuiteProvider(ABC):
"""Plugin hook for providing an external signature suite implementation."""

@abstractmethod
async def get_suite(
self,
profile: Profile,
proof_type: str,
proof: dict,
verification_method: str,
did_info: DIDInfo,
) -> Optional[LinkedDataProof]:
"""Get a signature suite for the given proof type and verification method.

Implementing classes should raise ExternalSuiteNotFoundError if preventing
fallback to built-in suites is desired. Otherwise, return None to indicate
that the implementing class does not support the given proof type.
"""
28 changes: 22 additions & 6 deletions aries_cloudagent/vc/vc_ld/manager.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
"""Manager for performing Linked Data Proof signatures over JSON-LD formatted W3C VCs."""

from typing import Dict, List, Optional, Type, Union, cast

from pyld import jsonld
from pyld.jsonld import JsonLdProcessor

from ...core.profile import Profile
from ...storage.vc_holder.base import VCHolder
from ...storage.vc_holder.vc_record import VCRecord
from ...wallet.base import BaseWallet
from ...wallet.default_verification_key_strategy import BaseVerificationKeyStrategy
from ...wallet.did_info import DIDInfo
from ...wallet.error import WalletNotFoundError
from ...wallet.key_type import BLS12381G2, ED25519, KeyType
from ...storage.vc_holder.base import VCHolder
from ...storage.vc_holder.vc_record import VCRecord
from ..ld_proofs.constants import (
SECURITY_CONTEXT_BBS_URL,
SECURITY_CONTEXT_ED25519_2020_URL,
Expand All @@ -29,11 +30,12 @@
from ..ld_proofs.validation_result import DocumentVerificationResult
from ..vc_ld.models.presentation import VerifiablePresentation
from ..vc_ld.validation_result import PresentationVerificationResult
from .external_suite import ExternalSuiteNotFoundError, ExternalSuiteProvider
from .issue import issue as ldp_issue
from .prove import sign_presentation
from .models.credential import VerifiableCredential
from .models.linked_data_proof import LDProof
from .models.options import LDProofVCOptions
from .prove import sign_presentation
from .verify import verify_credential, verify_presentation

SignatureTypes = Union[
Expand Down Expand Up @@ -177,11 +179,25 @@ async def _get_suite(
self,
*,
proof_type: str,
verification_method: Optional[str] = None,
proof: Optional[dict] = None,
did_info: Optional[DIDInfo] = None,
verification_method: str,
proof: dict,
did_info: DIDInfo,
):
"""Get signature suite for issuance of verification."""
# Try to get suite from external provider first
try:
if (provider := self.profile.inject_or(ExternalSuiteProvider)) and (
suite := await provider.get_suite(
self.profile, proof_type, proof, verification_method, did_info
)
):
return suite
except ExternalSuiteNotFoundError as error:
raise VcLdpManagerError(
f"Unable to get signature suite for proof type {proof_type} "
"using external provider."
) from error

# Get signature class based on proof type
SignatureClass = PROOF_TYPE_SIGNATURE_SUITE_MAPPING[proof_type]

Expand Down
15 changes: 12 additions & 3 deletions docs/features/JsonLdCredentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,17 @@ For the remainder of this guide, we will be using the example `UniversityDegreeC

### Signature Suite

Before issuing a credential you must determine a signature suite to use. ACA-Py currently supports two signature suites for issuing credentials:
Before issuing a credential you must determine a signature suite to use. ACA-Py currently supports three signature suites for issuing credentials:

- [`Ed25519Signature2018`](https://w3c-ccg.github.io/lds-ed25519-2018/) - Very well supported. No zero knowledge proofs or selective disclosure.
- [`Ed25519Signature2020`](https://w3c.github.io/vc-di-eddsa/#ed25519signature2020-0) - Updated version of 2018 suite.
- [`BbsBlsSignature2020`](https://w3c-ccg.github.io/ldp-bbs2020/) - Newer, but supports zero knowledge proofs and selective disclosure.

Generally you should always use `BbsBlsSignature2020` as it allows the holder to derive a new credential during the proving, meaning it doesn't have to disclose all fields and doesn't have to reveal the signature.

### Did Method
### DID Method

Besides the JSON-LD context, we need a did to use for issuing the credential. ACA-Py currently supports two did methods for issuing credentials:
Besides the JSON-LD context, we need a DID to use for issuing the credential. ACA-Py currently supports two did methods for issuing credentials:

- `did:sov` - Can only be used for `Ed25519Signature2018` signature suite.
- `did:key` - Can be used for both `Ed25519Signature2018` and `BbsBlsSignature2020` signature suites.
Expand Down Expand Up @@ -227,3 +228,11 @@ These endpoints include:
- `POST /vc/presentations/verify` -> verifies a presentation

To learn more about using these endpoints, please refer to the available [postman collection](../demo/AriesPostmanDemo.md#experimenting-with-the-vc-api-endpoints).

## External Suite Provider

It is possible to extend the signature suite support, including outsourcing signing JSON-LD Credentials to some other component (KMS, HSM, etc.), using the [`ExternalSuiteProvider` interface](https://github.com/hyperledger/aries-cloudagent-python/blob/d3ee92b1b86aff076b52f31eaecea59c18005079/aries_cloudagent/vc/vc_ld/external_suite.py#L27). This interface can be implemented and registered via plugin. The plugged in provider will be used by ACA-Py's LDP-VC subsystem to create a `LinkedDataProof` object, which is responsible for signing normalized credential values.

This interface enables taking advantage of ACA-Py's JSON-LD processing to construct and format the credential while exposing a simple interface to a plugin to make it responsible for signatures. This can also be combined with plugged in DID Methods, `VerificationKeyStrategy`, and other pluggable components.

See this example project here for more details on the interface and its usage: https://github.com/dbluhm/acapy-ld-signer