Skip to content

Harbour Credentials Integration

The harbour-credentials library provides the cryptographic foundation for SimpulseID credentials — keypair generation, JOSE signing/verification, and SD-JWT-VC issuance.

Setup

harbour-credentials is included as a git submodule and installed during setup:

make setup

Or install manually:

pip install -e submodules/harbour-credentials[dev]

Keypair Generation

Generate a P-256 keypair and derive a did:key identifier:

from harbour.keys import generate_p256_keypair, p256_public_key_to_did_key

private_key, public_key = generate_p256_keypair()
did = p256_public_key_to_did_key(public_key)
kid = f"{did}#{did.split(':')[-1]}"

The kid (Key ID) follows the did:key fragment convention per VC-JOSE-COSE §3.3.2.

Signing Credentials (JOSE)

Sign a W3C VCDM v2 credential as a vc+jwt:

from harbour.signer import sign_vc_jose

signed_jwt = sign_vc_jose(credential, private_key, kid=kid)

The result is a compact JWT with:

  • Header: {"alg": "ES256", "typ": "vc+jwt", "kid": "<did>#<fragment>"}
  • Payload: The credential JSON

Verifying Credentials

from harbour.verifier import verify_vc_jose

payload = verify_vc_jose(signed_jwt, public_key)

Evidence VP Signing

Credentials with evidence (e.g., delegation proofs) require signing the evidence Verifiable Presentation before signing the outer credential:

from harbour.signer import sign_vp_jose

# Sign the evidence VP with the holder's key
evidence_vp_jwt = sign_vp_jose(
    evidence_vp,
    holder_private_key,
    holder_kid=holder_kid,
    nonce=nonce,
)

The nonce uses the HARBOUR_DELEGATE challenge format from harbour's delegation module, binding the evidence to the specific credential issuance. The challenge is <random> HARBOUR_DELEGATE <SHA-256(TransactionData)> with the credential.issue action type. This follows EVES-009 and OID4VP §8.4.

SD-JWT-VC Structured Selective Disclosure

Harbour's SD-JWT-VC implementation supports structured (nested) selective disclosure per RFC 9901 §6.2. Credentials keep their nested W3C VCDM structure — only individual attribute values are hidden:

from harbour.sd_jwt import issue_sd_jwt_vc

sd_jwt = issue_sd_jwt_vc(
    credential["credentialSubject"],
    private_key,
    vct="https://w3id.org/ascs-ev/simpulse-id/v1/ParticipantCredential",
    disclosable=["credentialSubject.email", "credentialSubject.duns"],
)

The credential structure (types, nesting) stays visible — only sensitive values (email, DUNS, etc.) are hidden behind _sd digests at the appropriate nesting level.

See individual credential type pages for disclosure policy details:

Signing Examples

The sign_examples.py script processes all example credentials:

python3 src/sign_examples.py

This generates signed JWTs in examples/signed/, replacing evidence nonce placeholders with computed values.

See Also