Skip to content

Integrity

integrity

Cryptographic integrity for NUScope files: seals, signatures and verification.

Every file NUScope saves carries a seal, a manifest of SHA-256 hashes of

  • the data array (dtype, shape and bytes),
  • the axes (calibration),
  • the metadata (normalized and original vendor metadata, name, signal type),
  • the full provenance record, whose steps also form a hash chain (see :mod:nuscope.provenance).

The seal can be signed. Signing uses the researcher identity of :mod:EMDataBridge.identity, shared with NUScope-TOMO and NUANCE-MCP: a name, a validated ORCID iD and an Ed25519 key, created once with emdb identity create (or nuscope keys create). The private key stays on your computer; the public key travels inside every signature. Others can add their own signature to the same seal, for example a facility manager countersigning an acquisition (nuscope sign). Keys are trusted with emdb identity trust; your own key is trusted automatically.

Reading a file verifies it: :attr:Dataset.integrity holds the report, a warning is raised if anything changed after sealing, and the result is recorded in the provenance of the reopened dataset.

import nuscope as ns
ns.integrity.create_key("Your Name", orcid="0000-0002-1825-0097")
ns.save(ds, "grain.h5")              # sealed and signed
print(ns.verify("grain.h5"))         # data, metadata, history, signatures

Report dataclass

Report(status: str, seal_id: Optional[str] = None, checks: dict = dict(), signatures: list = list(), problems: list = list(), sealed_at: Optional[str] = None, instruments: list = list(), identifiers: list = list())

Result of checking a file. status is one of signed (intact, at least one valid signature), sealed (intact, not signed), tampered (something changed after sealing) or unsealed (no seal: a vendor file or a file written before NUScope 0.3).

sha256_array

sha256_array(data) -> str

SHA-256 over dtype, shape and C-ordered bytes, so the hash does not depend on the file format.

manifest

manifest(data, doc: dict) -> dict

What a seal pins down. doc is the NUScope document stored next to the data in a file.

instruments_of

instruments_of(prov: dict) -> list[dict]

The registered instruments (with DOIs) among a provenance record's agents.

create_key

create_key(name: str, orcid: Optional[str] = None, *, passphrase: Optional[str] = None, overwrite: bool = False) -> Identity

Create your identity and signing key (see :func:EMDataBridge.identity.create).

my_key

my_key() -> Optional[Identity]

Your identity, or None if you have not created one.

sign_seal

sign_seal(seal: dict, *, role: str = 'author', passphrase: Optional[str] = None) -> dict

Add your signature to a seal (in place) and return the signature entry.

make_seal

make_seal(data, doc: dict, *, sign: Optional[bool] = None, role: str = 'author') -> dict

Seal a document. sign=None signs when you have a key, True requires one, False never signs.

verify_doc

verify_doc(data, doc: dict) -> Report

Check data and document against the seal stored in the document.

verify

verify(path) -> Report

Verify a NUScope file (.nxs, .h5, .zarr or .tif) without loading it as a dataset.

cosign

cosign(path, *, role: str = 'co-signer', passphrase: Optional[str] = None) -> Report

Add your signature to an existing sealed file, after checking that it is intact.