voa 0.4.2

Command line interface and library for interacting with the File Hierarchy for the Verification of OS Artifacts (VOA)
Documentation
# VOA

A command line interface and library for interacting with the "File Hierarchy for the **V**erification of **O**S **A**rtifacts" ([VOA]).

## Documentation

- <https://voa.archlinux.page/rustdoc/voa/> for development version of the crate
- <https://docs.rs/voa/latest/voa/> for released versions of the crate

## Examples

### Library

#### Import and search

```rust
use std::io::Write;

use tempfile::{NamedTempFile, tempdir};
use voa::commands::{load_verifier, search_verifiers, write_verifier_to_hierarchy};

# fn main() -> testresult::TestResult {
// Write a generic OpenPGP certificate to a temporary file.
let cert = r#"-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEaNBDAhYJKwYBBAHaRw8BAQdAzjzrpQ/AEteCmzjd1xTdXGaHV0VKSm4HLy6l
HVcmWT3NH0pvaG4gRG9lIDxqb2huLmRvZUBleGFtcGxlLm9yZz7CmgQQFggAQgUC
aNBDAhYhBEauMg3lOimFWKbyoPtSEBy0DfYKAhsDAh4BBAsJCAcGFQ4KCQwIARYN
JwkCCAIHAgkBCAEHAQIZAQAKCRD7UhActA32CkhIAP9bhoLJeZRCAc+q1kFEkstT
uXBPlzHagF6ghuUfToMmVQD+KaakONKSekglKR4rJxzhleQJ4qsptt1gjXX13QgF
Xwo=
=Pkv9
-----END PGP PUBLIC KEY BLOCK-----"#;
let mut temp_file = NamedTempFile::new()?;
write!(temp_file, "{cert}")?;
let input_path = temp_file.path();

// Load OpenPGP verifier from file.
let verifier = load_verifier(Some(input_path.try_into()?), "openpgp".parse()?)?;

// Prepare a temporary output directory.
let temp_dir = tempdir()?;

// Write a verifier to a location in a temporary VOA hierarchy.
write_verifier_to_hierarchy(verifier, temp_dir, "os".parse()?, "packages".parse()?, None)?;

// Search for verifiers by relevant identifier information.
let verifiers = search_verifiers("os".parse()?, "packages".parse()?, None, None)?;

for verifier in verifiers.keys() {
  println!("{verifier:?}");
}
# Ok(())
# }
```

#### Verification

⚠️ **DO NOT USE IN PRODUCTION**: Artifact verification is still in an early experimental development stage.

```rust no_run
use std::{collections::HashSet, str::FromStr};
use voa::{
    commands::{
        openpgp_verify,
        read_openpgp_signatures,
        read_openpgp_verifiers,
    },
    utils::RegularFile,
};

# fn main() -> testresult::TestResult {
let signatures = read_openpgp_signatures(
    &HashSet::from_iter(vec![RegularFile::from_str("/some/path/to/an/file.tar.zst.sig")?])
)?;
let certs = read_openpgp_verifiers("os".parse()?, "purpose".parse()?, "context".parse()?);

openpgp_verify(
    &certs,
    &signatures,
    &RegularFile::from_str("/some/path/to/an/file.tar.zst")?,
)?;
# Ok(())
# }
```

### CLI

The `voa` CLI offers a simple interface for dealing with data in a VOA hierarchy.

#### Import verifiers

Verifiers can be imported using the `voa import` subcommand.

<!--
Create a temporary directory for example files and create an OpenPGP certificate for import.

```bash
tempdir="$(mktemp --directory --suffix '.voa-test')"

# Create an OpenPGP TSK/cert and expose the cert path in the OPENPGP_CERT environment variable.
rsop generate-key --signing-only "John Doe <john.doe@example.org>" > "$tempdir/key.tsk"
rsop extract-cert < "$tempdir/key.tsk" > "$tempdir/cert.cert"
export OPENPGP_CERT="$tempdir/cert.cert"

# Create a directory for the use as VOA directory and expose the path in the VOA_DIR environment variable.
mkdir --parents "$tempdir/voa"
export VOA_DIR="$tempdir/voa/"
```
-->

Assuming that the environment variable `OPENPGP_CERT` contains the path to an [OpenPGP certificate] with [signing capabilities], we can import it to a VOA hierarchy directory (represented by the `VOA_DIR` environment variable).

The following imports the [OpenPGP certificate] to the directory `os/packages/openpgp/` in `VOA_DIR`, implying that this verifier is to be used for the verification of package files on the OS `os`.

```bash
rpgp show "$OPENPGP_CERT"
voa import os packages openpgp --input "$OPENPGP_CERT" --base-path "$VOA_DIR"
# 🔐 EdDSA/Curve25519 v4 f992bda338ded64fe062302b5bd40d64577b8ea2
#  ⏱ Created 2025-09-20 06:13:33 UTC
#
#   🪪 ID "John Doe <john.doe@example.org>"
#     🖋 CertGeneric 2025-09-20 06:13:33 UTC, by 5bd40d64577b8ea2 [EdDSALegacy, SHA256, V4]
#
tree "$VOA_DIR"
# .
# └── os
#     └── packages
#         └── default
#             └── openpgp
#                 └── f992bda338ded64fe062302b5bd40d64577b8ea2.openpgp
rpgp show "$VOA_DIR"/os/packages/default/openpgp/*.openpgp
# 🔐 EdDSA/Curve25519 v4 f992bda338ded64fe062302b5bd40d64577b8ea2
#  ⏱ Created 2025-09-20 06:13:33 UTC
#
#   🪪 ID "John Doe <john.doe@example.org>"
#     🖋 CertGeneric 2025-09-20 06:13:33 UTC, by 5bd40d64577b8ea2 [EdDSALegacy, SHA256, V4]
#
```

#### List verifiers

Verifiers can be listed using the `voa list` subcommand.

```bash
voa list os packages
# /home/user/.config/voa/os/packages/default/openpgp/f992bda338ded64fe062302b5bd40d64577b8ea2.openpgp
voa list os packages --output-format json
# [{"load_path":{"load_path":"/home/user/.config/voa","writable":true,"ephemeral":false},"verifier_path":"/home/user/.config/voa/arch/packages/default/openpgp/f992bda338ded64fe062302b5bd40d64577b8ea2.openpgp","os":{"id":"arch","version_id":null,"variant_id":null,"image_id":null,"image_version":null},"purpose":{"role":"packages","mode":""},"context":"default","technology":"openpgp"}]
```

<!--
Remove the temporary directory.

```bash
rm -r -- "$tempdir"
```
-->

#### Verify artifacts

⚠️ **DO NOT USE IN PRODUCTION**: Artifact verification is still in an early experimental development stage.

OS artifacts can be verified using the `voa verify` subcommand.
Currently, only OpenPGP verification is available.

```bash no_run
voa verify os image default /path/to/an/image-1.2.3.tar.zst /path/to/an/image-1.2.3.tar.zst.sig
# ✅ /path/to/an/image-1.2.3.tar.zst.sig 1734644649 f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 e242ed3bffccdf271b7fbaf34ed72d089537b42f
voa verify os image default /path/to/an/image-1.2.3.tar.zst /path/to/an/image-1.2.3.tar.zst.sig --output-format json
# [{"signature":"/path/to/an/image-1.2.3.tar.zst.sig","result":{"valid":{"signature_creation_time":1734644649,"primary_key_fingerprint":"f1d2d2f924e986ac86fdf7b36c94bcdf32beec15","verifying_component_key_fingerprint":"e242ed3bffccdf271b7fbaf34ed72d089537b42f"}}}]
```

## Building from source

This crate offers a command line interface, which is hidden behind the `cli` feature.
To build the CLI, pass in the feature:

```bash no_run
cargo build --features cli
```

## Contributing

Please refer to the [contribution guidelines] to learn how to contribute to this project.

## License

This project can be used under the terms of the [Apache-2.0] or [MIT].
Contributions to this project, unless noted otherwise, are automatically licensed under the terms of both of those licenses.

[Apache-2.0]: ../LICENSES/Apache-2.0.txt
[MIT]: ../LICENSES/MIT.txt
[OpenPGP certificate]: https://openpgp.dev/book/certificates.html
[VOA]: https://uapi-group.org/specifications/specs/file_hierarchy_for_the_verification_of_os_artifacts/
[contribution guidelines]: ../CONTRIBUTING.md
[signing capabilities]: https://openpgp.dev/book/certificates.html#defining-operational-capabilities-of-component-keys-with-key-flags