ExIcaoVds (ex_icao_vds v0.3.2)

Copy Markdown

ICAO Visible Digital Seal (VDS) library.

Implements creation, signing, encoding, decoding, and verification of VDS-style payloads aligned with ICAO Doc 9303 Part 13 concepts. Document profiles, signing backends, trust resolvers, and carrier generation are all replaceable via configuration and behaviours.

Quickstart — issuance

config = %{
  issuing_country: "DEU",
  profile: ExIcaoVds.Profiles.Generic,
  profile_config: %{
    profile_id: :my_doc,
    document_type_category: "A",
    feature_definition_reference: 1,
    fields: [
      %{name: :doc_number, tag: 1, type: :string, encoding: :c40, required?: true, max_length: 20},
      %{name: :valid_until, tag: 2, type: :date, encoding: :date, required?: true}
    ]
  },
  signer: %{
    backend: ExIcaoVds.Signers.LocalKey,
    trust_mode: :public_key,
    private_key: my_ec_private_key_bytes,
    signer_identifier: "MYSGNR",
    key_reference: "key-2026-01"
  }
}

{:ok, seal} = ExIcaoVds.issue(%{doc_number: "X12345", valid_until: ~D[2028-01-01]}, config)
seal.raw_vds  # binary VDS bytes ready for a Data Matrix carrier

Quickstart — verification

verifier_config = %{
  verifier: %{
    trust_resolver: ExIcaoVds.TrustResolvers.StaticKeyStore,
    keys: %{{"MYSGNR", "key-2026-01"} => {public_key_bytes, :secp256r1}}
  },
  profile_config: %{fields: [...]}
}

{:ok, result} = ExIcaoVds.verify(seal.raw_vds, verifier_config)
result.status      # :valid
result.features    # decoded fields

Architecture

The library is intentionally backend-agnostic. Behaviours control:

VDS limits

VDS provides authenticity and integrity — a verifier can confirm the seal was issued by the claimed signer and has not been modified. It does not provide confidentiality (use the Encryptor behaviour for that) and does not prevent seal cloning (compare VDS data to printed document data or backend status to mitigate).

Summary

Functions

Decode raw VDS bytes into header, message zone, and signature zone structs without verifying the signature.

Design-time capacity estimate for profile_config (a map with a :fields list).

Inspect raw VDS bytes — alias for decode/2, intended for diagnostics. Does not verify the signature.

Issue a VDS seal for document_data using the given opts / config.

Runtime preflight capacity check for profile with document_data.

Render raw_vds bytes as a visible carrier symbol.

Verify raw VDS bytes raw_vds_or_carrier.

Functions

decode(raw_vds, opts \\ %{})

@spec decode(binary(), map() | keyword()) ::
  {:ok, map()} | {:error, ExIcaoVds.Error.t()}

Decode raw VDS bytes into header, message zone, and signature zone structs without verifying the signature.

Returns {:ok, %{header, message_zone, signature_zone}} or {:error, %Error{}}.

estimate_capacity(profile_config, opts \\ [])

@spec estimate_capacity(
  map(),
  keyword()
) :: ExIcaoVds.CapacityEstimate.t()

Design-time capacity estimate for profile_config (a map with a :fields list).

Returns a %ExIcaoVds.CapacityEstimate{} using worst-case field sizes. Pass max_carrier_bytes: n in opts to override the default 1558-byte ceiling.

inspect_seal(raw_vds, opts \\ %{})

@spec inspect_seal(binary(), map() | keyword()) ::
  {:ok, map()} | {:error, ExIcaoVds.Error.t()}

Inspect raw VDS bytes — alias for decode/2, intended for diagnostics. Does not verify the signature.

issue(document_data, opts \\ %{})

@spec issue(map(), map() | keyword()) ::
  {:ok, ExIcaoVds.IssuedSeal.t()} | {:error, ExIcaoVds.Error.t()}

Issue a VDS seal for document_data using the given opts / config.

Returns {:ok, %IssuedSeal{}} on success or {:error, %Error{}} on failure.

preflight(profile_mod, document_data, opts \\ [])

@spec preflight(module(), map(), keyword()) :: ExIcaoVds.CapacityPreflight.t()

Runtime preflight capacity check for profile with document_data.

Encodes the document data, then compares actual encoded sizes against the carrier capacity. Returns a %ExIcaoVds.CapacityPreflight{}.

render_carrier(raw_vds, opts \\ %{})

@spec render_carrier(binary(), map() | keyword()) ::
  {:ok, binary()} | {:error, ExIcaoVds.Error.t()}

Render raw_vds bytes as a visible carrier symbol.

Returns {:ok, carrier_binary} or {:error, %Error{}}. Carrier backend, output format, and options are controlled by :carrier in opts.

verify(raw_vds, opts \\ %{})

@spec verify(binary(), map() | keyword()) ::
  {:ok, ExIcaoVds.VerificationResult.t()}
  | {:error, ExIcaoVds.VerificationResult.t()}

Verify raw VDS bytes raw_vds_or_carrier.

Returns {:ok, %VerificationResult{}} when the seal is cryptographically valid, or {:error, %VerificationResult{}} when verification fails.