Skip to content
Open
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ All versions prior to 1.0.0 are untracked.
## [Unreleased]

### Added
- ...
-Added the `digest` subcommand to compute and print a model's digest. This enables other tools to easily pair the attestations with a model directory.

### Changed
- ...
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ This will open an OIDC flow to obtain a short lived token for the certificate.
The identity used during signing and the provider must be reused during
verification.

To only compute and output the digest of the model, you can use the `digest`
subcommand, pointing it to the model directory:

```bash
[...]$ model_signing digest bert-base-uncased
```

The digest subcommand follows the same ignore rules used when signing.

## Using Private Sigstore Instances

To use a private Sigstore setup (e.g. custom Rekor/Fulcio), use the `--trust_config` flag:
Expand Down
45 changes: 45 additions & 0 deletions src/model_signing/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,51 @@ def main(log_level: str) -> None:
sys.exit(1)


@main.command(name="digest")
@_model_path_argument
@_ignore_paths_option
@_ignore_git_paths_option
@_allow_symlinks_option
def _digest(
model_path: pathlib.Path,
ignore_paths: Iterable[pathlib.Path],
ignore_git_paths: bool,
allow_symlinks: bool,
) -> None:
"""Computes the digest of a model.

The digest subcommand serializes a model directory and computes the "root"
digest (hash), the same used when signing and as the attestation subject.

By default, git-related files are ignored (same behavior as the sign
command). Use --no-ignore-git-paths to include them. To ignore other
files from the directory serialization, use --ignore-paths.
"""
from model_signing._hashing import memory

try:
# First, generate the manifest of the model directory
ignored = _resolve_ignore_paths(model_path, list(ignore_paths))
manifest = (
model_signing.hashing.Config()
.set_ignored_paths(paths=ignored, ignore_git_paths=ignore_git_paths)
.set_allow_symlinks(allow_symlinks)
.hash(model_path)
)

# Then, hash the resource descriptors as done when signing
hasher = memory.SHA256()
for descriptor in manifest.resource_descriptors():
hasher.update(descriptor.digest.digest_value)
root_digest = hasher.compute()

click.echo(f"{root_digest.algorithm}:{root_digest.digest_hex}")

except Exception as err:
click.echo(f"Computing digest failed: {err}", err=True)
sys.exit(1)


@main.group(name="sign", subcommand_metavar="PKI_METHOD", cls=_PKICmdGroup)
def _sign() -> None:
"""Sign models.
Expand Down
Loading