Attesto.Mdoc (Attesto v1.14.0)

Copy Markdown View Source

Issue and verify ISO 18013-5 mdoc credentials and OID4VP mdoc presentations.

Issuance builds the MobileSecurityObject (MSO), binds the holder's device key, and signs issuer authentication with ES256 COSE_Sign1. verify/3 checks issuer signatures, item digests, and validity for a bare IssuerSigned structure. The document type is bound ONLY when :expected_doc_type is supplied — without it, any doc type from a trusted issuer is accepted, so a caller that needs a specific doc type (e.g. a DCQL mso_mdoc query) MUST pass it. Attesto.VpToken threads this via its :query_constraints for the OID4VP path.

verify_device_response/4 additionally verifies an OID4VP presentation: a full ISO 18013-5 DeviceResponse whose DeviceSigned.deviceAuth is an ES256 COSE_Sign1 (detached payload) over DeviceAuthentication = ["DeviceAuthentication", SessionTranscript, DocType, DeviceNameSpacesBytes], where SessionTranscript's Handover is OID4VP's OpenID4VPHandover (redirect-flow form, per the OID4VP 1.0 "Handover and SessionTranscript Definitions"). COSE_Mac0 device authentication is not supported.

peek_doc_type/1 reads an unverified docType out of a DeviceResponse for issuer-key resolution ahead of verification; see Attesto.VpToken.

Summary

Functions

Issue a base64url-encoded ISO 18013-5 IssuerSigned structure.

Peek an OID4VP DeviceResponse's first document docType, supplied as base64url or raw CBOR bytes, without verifying any signature.

Verify an IssuerSigned mdoc supplied as base64url or raw CBOR bytes.

Verify an OID4VP mdoc presentation: a full ISO 18013-5 DeviceResponse supplied as base64url or raw CBOR bytes.

Types

validity()

@type validity() :: %{
  signed: integer(),
  valid_from: integer(),
  valid_until: integer()
}

verified()

@type verified() :: %{
  doc_type: String.t(),
  namespaces: %{required(String.t()) => %{required(String.t()) => term()}},
  device_key: map(),
  validity: validity()
}

verified_presentation()

@type verified_presentation() :: %{
  doc_type: String.t(),
  namespaces: %{required(String.t()) => %{required(String.t()) => term()}},
  device_namespaces: %{
    required(String.t()) => %{required(String.t()) => term()}
  },
  validity: validity()
}

verify_error()

@type verify_error() ::
  :digest_mismatch
  | :expired
  | :invalid_cose
  | :invalid_key
  | :invalid_mdoc
  | :invalid_signature
  | :not_yet_valid
  | :unexpected_doc_type
  | :unsupported_algorithm

Functions

issue(opts)

@spec issue(keyword()) :: {:ok, String.t()} | {:error, :invalid_options}

Issue a base64url-encoded ISO 18013-5 IssuerSigned structure.

Required options are :doc_type, :namespaces, :device_key, :issuer_pem, and :validity. :x5chain optionally carries a list of issuer-certificate DER binaries in the COSE unprotected header.

peek_doc_type(device_response)

@spec peek_doc_type(binary()) :: {:ok, String.t()} | {:error, :invalid_mdoc}

Peek an OID4VP DeviceResponse's first document docType, supplied as base64url or raw CBOR bytes, without verifying any signature.

Mirrors Attesto.JWS.peek_json peeking an SD-JWT's unverified iss: the result is UNVERIFIED input and exists only to select candidate issuer keys ahead of verification (e.g. for Attesto.VpToken's :resolve_issuer callback). It MUST NOT be used to make a network request — the presenter controls this value.

verify(input, trusted, opts \\ [])

@spec verify(binary(), JOSE.JWK.t() | map() | String.t(), keyword()) ::
  {:ok, verified()} | {:error, verify_error()}

Verify an IssuerSigned mdoc supplied as base64url or raw CBOR bytes.

trusted is the issuer's public JWK or PEM. Set :expected_doc_type to bind verification to a requested credential type. A :now option may be supplied as Unix seconds or a DateTime for deterministic clock checks.

verify_device_response(device_response, context, trusted, opts \\ [])

@spec verify_device_response(
  binary(),
  keyword(),
  JOSE.JWK.t() | map() | String.t(),
  keyword()
) ::
  {:ok, [verified_presentation()]} | {:error, verify_error()}

Verify an OID4VP mdoc presentation: a full ISO 18013-5 DeviceResponse supplied as base64url or raw CBOR bytes.

context supplies the OID4VP request values needed to reconstruct the OpenID4VPHandover and SessionTranscript: :client_id, :nonce, and :response_uri are required. :response_encryption_jwk is the Verifier's public response-encryption key (present only for the direct_post.jwt Response Mode) and is folded in as the handover's JWK thumbprint; omit it for unencrypted direct_post.

trusted and opts verify each document's IssuerSigned structure exactly as in verify/3 (:expected_doc_type and :now included). Each document's DeviceSigned.deviceAuth is additionally verified as an ES256 COSE_Sign1 with a detached payload, signed by the device key bound in that document's MSO.

Returns one verified result per document in the DeviceResponse.