Compare commits

..

66 Commits

Author SHA1 Message Date
Jan Höppner
793c037ded New release s390-tools-2.37.0
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-07 15:47:07 +01:00
Jan Höppner
9f476217f5 rust: Upgrade dependencies
Upgrade all dependencies by running 'cargo upgrade' from cargo-edit.

Link: https://crates.io/crates/cargo-edit
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-07 14:25:11 +01:00
Jan Höppner
49111d613b rust/pvattest/tests: Add missing unittest asset host.pem.crt
The host.pem.crt file required by pvattest/src/cmd/check/host_key.rs for
unittesting is missing upstream. Add it back in.

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/182
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-07 14:25:11 +01:00
Steffen Eiden
26d2e2d786 rust/pv: Fix padding of retrievable private keys
Fix the padding of keys that are smaller than the architected slot for
the retrievable key. Previously the keys where appended with zeros.
However, processing software expects a left-padding.
Affected key types:
  * Ed448
  * SecP521

While at it, fix documentation in the retrievable key struct.

Fixes: fd024387d7 ("rust/pv: Retrievable secrets support")
Acked-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-02-06 13:18:32 +01:00
Mikhail Zaslonko
7bd159de1d zdump: Add support for eckd_mv dumper v2
Use STAGE2_DUMPER_SIZE_MV size for eckd_mv dumper v2
in df_s390_dumper_read() to verify the dump tool.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Mikhail Zaslonko
605680d6fd zipl/boot: Increase section size for eckd_mv dumper
Update eckd2dump linker script to remove stage2 space constraints and
avoid linker section overlaps when using 'unfortunate' compiler
version & flags combination.
Since Multi-volume dumper size changes, we also update the dump-tool version
number for 'zgetdump' to distinguish.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/171
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Finn Callies
f1a479a794 capcfstats: Update manpage to MSA 10 and MSA 11
The tooling already supports the PAI counters to the respective CPACF
functions introduced with MSA 10 and MSA 11 but the manpage did not
reflect that until now. The list of pai counters is moved to a new
section called APPENDIX to not block any important information with the
list.
Additionally a few double space after end of sentences are removed.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Finn Callies
fa2d68e80c capcfstatsd: Update service unit to build on z/VM and KVM
cpacfstatsd can be run successfully on z/VM and KVM mashines, but the
service unit did not allow that. A user would have to start the
cpacfstats daemon by hand every time until now.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen
e1730585e9 dbginfo.sh: Adding kdump info
Collect kdump.log to analyse kdump issues.

Suggested-by: Michael Storzer <MSTORZER@de.ibm.com>
Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen
ef55a5d677 dbginfo.sh: Add details on CPU-measurement
Adding lscpumf command to get details on the setup for CPU-measurement.

Suggested-by: Eberhard Pasch <epasch@de.ibm.com>
Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen
bc4b7475f7 Removing email references to linux390@de.ibm.com
The Email address linux390@de.ibm.com is about to be sunset.
Cleaning up the last references in man page/help function for:
- mon_tools
- dbginfo.sh

Reviewed-by: Michael Storzer <MSTORZER@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Dan Horák
abcb93aacb zipl: Update inline assembly for GCC 15
Properly escape % (as %%) for extended assembly in stage3.c in
start_kernel().

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/179
Closes: https://github.com/ibm-s390-linux/s390-tools/pull/180
Signed-off-by: Dan Horák <dan@danny.cz>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Eduard Shishkin
b09a9b3490 zipl_helper.device-mapper: Fix logical device resolution
Reported issue:

\# lsblk
sdc                                8:32   0   20G  0 disk
|-sdc1                             8:33   0   20G  0 part
`-mpatha                         253:1    0   20G  0 mpath
  `-mpatha1                      253:17   0   20G  0 part
sdd                                8:48   0   20G  0 disk
|-sdd1                             8:49   0   20G  0 part
`-mpatha                         253:1    0   20G  0 mpath
  `-mpatha1                      253:17   0   20G  0 part

\# zipl -d /dev/mapper/mpatha1
Success
\# zgetdump -d /dev/mapper/mpatha
zgetdump: No dump tool found on "/dev/mapper/mpatha"

The root cause is in incorrect logical device resolution:

\# zipl_helper.device-mapper 253:17

Expected result:

targetbase=253:1
targettype=SCSI
targetblocksize=512
targetoffset=2048

Actual result:

targetbase=8:48
targettype=SCSI
targetblocksize=512
targetoffset=2048

Do not resolve dm device which provides access to boot sectors.

Fixes: 3db20bccef ("zipl_helper.device-mapper: Add missed step in logical device resolution")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Reported-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Tested-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Acked-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Jakob Naucke
caaf2b2116 rust/pvimg: Fix flag parsing for allowing dump
Parsing of pvimg flags did not pick up allowing dumping correctly.

Fixes: f4cf4ae6eb (rust: Add a new tool called 'pvimg')
Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jakob Naucke <naucke@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-01-28 17:25:19 +01:00
Joern Siglen
382ec368fc dbginfo.sh: Add new crypto command
s390tools has a new tool to view the CPACF data, now added
to our data collection

Suggested-by: Mario Held <mario.held@de.ibm.com>
Suggested-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Joern Siglen
9dd96144d4 dbginfo.sh: Rework network section
Define the network devices as global variable with closer selection
to avoid repeated calls with undefined interfaces.
Relable the OSA part of networks.

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Joern Siglen
efd41ca435 dbginfo.sh: Update copyright 2nd year
change second year of copyright to 2025

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Joern Siglen <siglen@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Marc Hartmayer
7bc12d0202 rust/pvimg: Document the change from '--comm-key' to '--cck' in the help message
This fixes problems when users search for '--comm-key' in the help
message.

Fixes: 5b6d7a467d ("rust/pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias")
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Nico Boehr <nrb@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Marc Hartmayer
b49cd13d16 cpumf/man: Fix whatis-entries in manpages
Fix whatis-entries in the lshwc, lspai, and pai manpages by inlining the
tool name. The following command line can be used to test a local
manpage:

$ lexgrog -w -m cpumf/man/lshwc.8
cpumf/man/lshwc.8: "lshwc - extract CPU Measurement Facilities counter sets"

Github-ID: https://github.com/ibm-s390-linux/s390-tools/issues/170
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Marc Hartmayer
56e32dfcbb rust/pvcore/uvdevice: Fix typo
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
833a8e7309 zkey: Support validation of key of type PVSECRET-AES
Keys of type PVSECRET-AES can also be verified via the pkey IOCTL
PKEY_VERIFYKEY2, but the card and domain fields must be zero, because such
a key does not use a crypto card. Also XTS keys of type PVSRCRET-AES are
not represented by 2 concatenated keys but by just one key of type
PVSECRET-AES. Thus, special handling is required for XTS keys.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
a8eb2bd4e7 zkey: Reject re-enciphering of PVSECRET-AES keys
Keys of type PVSECRET-AES can not be reenciphered using 'zkey reencipher'
or 'zkey-cryptsetup reencipher'. Reject that with a proper error message.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
5276d408fd zkey: Reject key generation and APQN association for PVSECRET-AES keys
Keys of type PVSECRET-AES can not be generated using 'zkey generate'.
Furthermore, APQNs can not be associated with keys of type PVSECRET-AES
via 'zkey change'. Reject that with a proper error message.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
95bf7eb285 zkey: Add the 'pvsecrets import' command
The 'pvsecrets import' command imports a protected virtualization secret
into the zkey key repository. Like other key import or key generation
commands, additional information can be associated with the imported key,
such as a textual description, the volume to encrypt with together with
the volume type, the sector size, and a dummy passphrase. You can not
associate a set of APQNs, since a protected virtualization secret does
not need or use a crypto card.

This command only works when running in a secure execution guest.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
fdf66dc148 zkey: Add PVSECRETS-AES key type
Add the definitions and utility functions for the PVSECRETS-AES key type.
A PVSECRETS-AES key token contains the secret id of a protected
virtualization secret. It does not contain the key material, just a
reference to the key in the ultravisor.

When such a key token is used to perform crypto operations later on, the
PAES kernel cipher will obtain the protected key belonging to this secret
id with the help of the pkey kernel module.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
5ce79ea667 zkey: Add the 'pvsecrets list' command
The 'pvsecrets list' command lists the available protected virtualization
secrets. By default, only those pvsecret types are listed, that can be used
with zkey. If option '--all/-a' is specified, then all pvsecret types are
listed. Nevertheless, pvsecret types not supported by zkey can not be used
with zkey.

This command only works when running in a secure execution guest.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Ingo Franzki
8c4b2872b8 zkey: Add support for retrieving a list of ultravisor secrets
Add functions to interface with the ultravisor device (/dev/uv) when
running in a secure execution guest to retrieve a list of available
secrets.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
2025-01-10 16:38:45 +01:00
Marc Hartmayer
7be364aa84 rust/pvimg/man: Fix whatis-entries in manpages
Before this change:
$ lexgrog -w -m man/pvimg-create.1
man/pvimg-create.1: parse failed

After this change:

$ lexgrog -w -m man/pvimg-create.1
man/pvimg-create.1: "pvimg-create - Create an IBM Secure Execution image"

In addition, pvimg is highlighted in the manpage description section.

Github-ID: https://github.com/ibm-s390-linux/s390-tools/issues/170
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:12:34 +01:00
Marc Hartmayer
43858063ca rust: Upgrade 'thiserror' to '2.0.6'
Since there have been breaking changes, let's fix them. Fortunately,
there was only one problem to fix.

Command line used:

  $ cargo upgrade --incompatible -p 'thiserror'

Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Marc Hartmayer
739761f0d2 rust: Update Rust depenencies
Command line used (https://crates.io/crates/cargo-edit):

 $ cargo upgrade

Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
a8a3e7d49c rust/pvsecret: Update manuals and README
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
ff04f76257 rust/pv*: Allow the use of non-hashes secret IDs
Secret IDs identify a secret in the store. Tooling (pvsecret) calculates
them by hashing a user-defined string. With this patch it is now
possible to skip the hash step and directly use the input string as the
ID. Up to the first 31 bytes of the input ASCII-string are used. The last byte
is the NUL char. During list pvsecret tries to interpret the secret
as ASCII string and if possible displays the ASCII characters alongside
the hex number.

Also, use the Upper/Lower Hex formatters for the hexstring formatting of
SecretId. Display will, additionally show the ASCII representation if
applicable.

While at it, use Self wherever possible.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
93216d916c rust/pv*: Support longer secret lists
Make use of the enhanced list secrets UAPI for the uvdevice in the latest kernel
version. This allows fetching secret lists with more than 85 entries via
reserving more userspace memory in the IOCTL argument.

While at it, move the errno readout next to the ioctl-syscall.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
256289a30a rust/pv_core: Refactor secret list
Improve the secret list implementation. Use structs+{As,From}Bytes
instead of arbitrary seeks and reads/writes to parse the secret list.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
93da795520 rust/pvsecret: Add support for retrievable secrets
Support for creating and retrieving retrievable secrets.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
a14f9d4edc rust/pvsecret: Improve CLI
Improve the wording of the help/man text/

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
fd024387d7 rust/pv: Retrievable secrets support
Support retrievable secret for Add-Secret requests.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
4af137f4fa rust/pv_core: Support for listing Retrievable Secrets
Add support for listing retrievable secrets in the List Secrets UVC.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
01cd81ecf5 rust/pv_core: Retrieve Secret UVC
Create the uvdevice-IOCTL functionality for the new Retrieve Secret UVC.

Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
69eb06f39e rust/pv_core: Update ffi.rs to linux/uvdevice.h v6.13
While at it, add a file global #[allow(dead_code)].
The file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
and there might be things that are not needed here but are defined in that header.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
d1636168b2 rust/pv: Support for writing data in PEM format
Use existing OpenSSL functionalities to create PEM files containing
arbitrary data.

Acked-by: Marc Hartmayer <marc@linux.ibm.com>
Acked-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Steffen Eiden
1e44ace41d rust/pvsecret: Refactor writing secret
Refactor the writing of secret-type dependent output files to ease
extensions.

Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Christoph Schlameuss <schlameuss@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Marc Hartmayer
a9b8caa411 genprotimg/sample/check_hostkeydoc: Fix documentation link
Move the script to 'genprotimg/samples/check_hostkeydoc' and create a
symlink at 'rust/pvimg/tools/check_hostkeydoc' in order to keep
compatibility with existing documentation. The problem with the original
fix was that the github.com website does not follow symbolic links.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Marc Hartmayer
6e48c5ebaa pvimg: info: Rename '--key' into '--hdr-key' and use '--key' as an alias
Rename '--key' into '--hdr-key' and use '--key' as an (non-visible)
alias for '--hdr-key' in order to keep the command line backwards
compatible. The chances of someone using '--key' are very low, as this
version has not yet been released by any OS distribution.

This change makes the command line options for the different subcommands
more consistent and therefore easier to use.

Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Marc Hartmayer
944581eaef rust/pvimg: Add upper estimates for the Secure Execution header
A Secure Execution header V1 can be at maximum two pages large, optional
items are not supported, and the size of the encrypted part cannot be
larger than the total size of the Secure Execution header add this as
Deku assertions and additional conditions to the code. In addition, add
a check for the number of key slots.

Fixes: f4cf4ae6eb ("rust: Add a new tool called 'pvimg'")
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-20 13:01:15 +01:00
Marc Hartmayer
3f6572e901 rust/utils: mkdtemp: fix memory leak
Fix memory leak of @template_raw. The documentation of CString::into_raw
reads:

"Consumes the CString and transfers ownership of the string to a C
caller.
...
Failure to call CString::from_raw will lead to a memory leak." [1]

Let's fix the memory leak by always calling `CString::from_raw` and
therefore reclaim the ownership.

[1] https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw

Fixes: e56acf4f14 ("pv_core: add `TemporaryDirectory`")
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-19 21:45:37 +01:00
Peter Oberparleiter
6520070a46 chpstat: Fix invalid utilization data on older kernels
On kernels without support for the new "measurement_chars_full" CHPID
sysfs attribute, chpstat displays invalid utilization numbers (nan/inf).
This is due to an invalid buffer address calculation when reading the
old "measurement_chars" attribute.

Fix this by using the correct buffer address calculation.

Fixes: 026ecbafea ("chpstat: Add support for full CMCB")
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-19 21:45:37 +01:00
Marc Hartmayer
3b8fdcc892 pvimg: Add '--hdr-key' command line option to 'pvimg create'
Add '--hdr-key <FILE>' as a command line option to the 'pvimg create'
command. This key can then be used later to decrypt the Secure Execution
header of a Secure Execution image, e.g. 'pvimg info --key <FILE>
--format json <SE_IMG>'. While updating the manpages, add missing hyphen
escapes in the manpages.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Eduard Shishkin
352e2fe5d3 zipl/src: Fix incorrect installation of zipl_helper.md
Dereferencing zipl.helper.md by 'make install' causes "duplicate
BUILD-IDs" conflict for distro partners.

Install zipl_helper.device-mapper as a regular file and all other
helpers - as symlinks to the zipl_helper.device-mapper

Fixes: 3296d85e35 ("zipl/src: add support of md-mirrors")
Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/178
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Eduard Shishkin
37b7b68f1b zipl/src: Fix imprecise check that file locates on specified device
In case when target parameters are specified by user, the check
that a file locates on a specified device, compares a logical
device with a base disk, which is incorrect.

Fix the check to compare base disks (a specified one with the base
disk determined by disk_get_info() procedure called w/o any user
hints).

Fixes: c0f02d2f68 ("zipl/src: Fix problems when target parameters are specified by user")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Eduard Shishkin
3db20bccef zipl_helper.device-mapper: Add missed step in logical device resolution
Steps to reproduce the problem:

\# lsblk

NAME                             MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINT
loop0                              7:0    0    5G  0 loop
|-loop0p1                        253:15   0  128M  0 part
`-loop0p2                        253:16   0  4.9G  0 part  /mnt

\# ./zipl_helper.device-mapper 253:16

Expected result:

targetbase=7:0
targettype=SCSI
targetblocksize=4096
targetoffset=32784

Actual result:

targetbase=253:16
targettype=SCSI
targetblocksize=4096
targetoffset=32784

The problem is in a missed step which resolves the uppermost
logical dm-device to a physical device. Reproducible only for loop
devices.

Add missing step to correctly resolve to a physical device.

Fixes: 670bf3e870 ("zipl: refactor zipl_helper.device-mapper")
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:51 +01:00
Marc Hartmayer
560b276f7e rust/pvimg: Fix possible 'range start index out of range for slice' error
Fix possible 'range start index 16 out of range for slice of length 0'
error by adding a check of the slice data length.

Fixes: f4cf4ae6eb ("rust: Add a new tool called 'pvimg'")
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:50 +01:00
Marc Hartmayer
a19c43f613 rust/Cargo.toml: Change to "thin local" LTO for faster release builds
"fat" LTO takes much longer to compile than with "thin local" LTO, so
use the Rust release build defaults, which is "thin local" LTO [1-2].

If someone wants to enable "fat" LTO, they can use one of the common
methods to adjust the Rust build process, for example:

  $ cargo build --config 'profile.release.lto=true' --release

or

  $ export CARGO_PROFILE_RELEASE_LTO=true
  $ cargo build --release

[1] https://doc.rust-lang.org/cargo/reference/profiles.html#release
[2] https://doc.rust-lang.org/cargo/reference/profiles.html#lto

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:50 +01:00
Marc Hartmayer
5b6d7a467d rust/pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias
Add '--cck <FILE>' as an command line option and make '--comm-key' an
alias of it. This makes the command line more similar to the other
Secure Execution related PV-tools (e.g. pvattest and pvsecret).

Suggested-by: Reinhard Bündgen <buendgen@de.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:50 +01:00
Marc Hartmayer
b1fdbac5f9 rust/pvimg/man: Document command line option aliases in the manpages
Document the visible command line option aliases in the manpages.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-18 17:08:50 +01:00
Marc Hartmayer
cf51ac7860 rust/pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create'
With runtime attestation it might be useful to have non-encrypted Secure
Execution images. This patch adds the support for this to the 'pvimg
create' and 'genprotimg' commands.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Niklas Schnelle
cf5560a100 libzpci: opticsmon: Refactor on_link_change() using new zpci_find_by_netdev()
Finding a PCI device given the name of a netdev seems generally useful
so pull this out into a new zpci_find_by_netdev() function in libzpci
and use this to simplify on_link_change() removing the need for
backwards goto.

Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Niklas Schnelle
dff965465c opticsmon: Fix runaway loop in on_link_change()
When on_link_change() gets called with a netdev that would be monitored
but hasn't entered zpci_list yet, reloads is 1 after the loops and
a reload occurs. Then the netdev is found in the list and reloads
becomes -1 which incorrectly triggers more reloads until underflow.
Fix this by returning once the device is found. Also just check for
reloads being larger than zero.

Fixes: c34adb9cab ("opticsmon: Introduce opticsmon tool")
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Signed-off-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Marc Hartmayer
79de37bbc8 genprotimg/samples/check_hostkeydoc: Create symlink to the new location
Create a symbolic link to the new location of the 'check_hostkeydoc'
script in order to keep compatibility with existing documentation.

Reported-by: Stephan Hartig <hartig@de.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov
e5c2fba0a8 ziomon/ziorep_utilization: introduce parameter for reports separation
The output of ziorep_utilization tool currently contains reports for both
the physical and the virtual adapter. As binding of physical and virtual
adapter reports together can be confusing for evaluation of results, introduce
--fcp-device tool parameter for reports separation. This parameter was
introduced to mark virtual adapter report as it represents utilization report
of FCP devices.

So, if --fcp-device was specified by the user, virtual adapter report is
printed. Otherwise physical adapter report is printed.

Parameter --fcp-device has no influence on CSV format report printing.

Refactor also print_reports() function for both reports for better
--fcp-device parameter handling.

Add clarification messages on how to use --fcp-device parameter.

Delete empty separator line between former two reports.

Add description of --fcp-device parameter to man pages.

Add examples for using of ziorep_utilization tool to man pages.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov
04267470b1 ziomon/ziorep_utilization: swap Bus-ID and CHPID columns in virt report
As virtual adapter report in ziorep_utilization represents utilization
values of FCP devices, swap Bus-ID and CHPID columns in virtual
adapter report to avoid confusion during evaluation. Also use
print_delimiter() function instead of slash for Bus-ID and CHPID fields
separation.

Before patch virtual adapter report looks like:
...
CHP Bus-ID  |qdio util.%|queu|fail|-thp in MB/s-|I/O reqs-|
 ID            max   avg full  erc     rd    wrt   rd  wrt
2020-05-14 14:00:41
 60/0.0.1906   2.3   0.8    0    0   0.0  329.8    30 192K
 61/0.0.1946   0.8   0.7    0    0   0.0  341.8    30 192K
...

After patch application virtual adapter report looks like:
...
Bus-ID  |CHP|qdio util.%|queu|fail|-thp in MB/s-|I/O reqs-|
          ID   max   avg full  erc     rd    wrt   rd  wrt
2020-05-14 14:00:41
0.0.1906  60   0.0   0.8    0    0   0.0  331.2     0 1.3K
0.0.1946  61   0.0   0.7    0    0   0.0  344.0     0 1.3K
...

Note, that this patch does not change CSV-format report.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov
cf7060ea08 ziomon/ziorep_utilization: add pchid column to physical adapter report
In ziorep_utilization tool physical adapter report represents PCHID
scope, rather than CHPID. PCHID column is added as a very first column of
physical adapter report for non-CSV format report.

Old version of physical adapter report with CHPID column only
looks like:
...
CHP|adapter in %-|--bus in %---|--cpu in %---|
 ID min max   avg min max   avg min max   avg
2020-05-14 14:00:41
 60   0   0   0.0   1   1   1.0   0   0   0.0
 61   0   0   0.0   1   1   1.0   0   0   0.0
...

New version of physical adapter report with additional PCHID column
looks like:
...
PCH |CHP|adapter in %-|--bus in %---|--cpu in %---|
 ID   ID min max   avg min max   avg min max   avg
2020-05-14 14:00:41
01c0  60   0   0   0.0   1   1   1.0   0   0   0.0
01c1  61   0   0   0.0   1   1   1.0   0   0   0.0
...

For CSV-format report PCHID column is appended to the end of each record line.

For older kernel releases, where PCHID sysfs entry is not available, replace
PCHID column value with "n/a".

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov
f340a96fdf ziomon/ziorep_cfgreader: extract PCHID from .config file
Add an ability for ziorep_config tool to extract PCHID additional adapter
parameter from .config file. With this patch device_info structure will have
pchid field, which can be used by different printers classes. Also add
function for querying PCHID by using of devno for providing reports. Translate
"n/a" field from .config file record into invalid PCHID number.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov
798bd752cf ziomon/ziorep_config: extract PCHID and add to internal report
The ziomon_fcpconf saves a lot of information about adapter into .cfg file.
With this patch ziorep_config tool will extract PCHID additional adapter
parameter from .cfg file and put it into .config file for future evaluation by
ziorep_* tools group.

This patch also adds notification for cases, when chid sysfs entry does not
exist for specified CHPID, for example for old kernels.

Before patch application typical line example from .config file looked like:
... 253:0 /dev/sde 8388672 8:64 Disk

After application of the patch typical line example from .config file
looks like:
... 253:0 /dev/sde 8388672 8:64 Disk 01c0

On older distros, which do not contain pchid sysfs entry, typical line
example from .config file looks like:
... 253:0 /dev/sde 8388672 8:64 Disk n/a

As PCHID value of FCP adapter can now be extracted from .config file,
add PCHID field to report of ziorep_config tool.

If there was no PCHID sysfs entry on the system, print following message into
ziorep_config command output:
...
PCHID:   there is no PCHID entry in data source
...

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Fedor Loshakov
638f0aa4f9 ziomon/ziorep_traffic: disambiguate paths with device bus-ID
Add device bus-ID to ziorep_traffic output for better distinguishing
of paths in setups, which have paths in the same pathgroup with same
WWPN and LUN.

Output without device bus-ID column:
$ ziorep_traffic log
       WWPN                LUN       ...
                                        ...
2019-10-21 11:44:38
0x50050763071bc5e3:0x4006404f00000000   ...
0x50050763071bc5e3:0x4006404f00000000   ...

Output with device bus-ID column:
$ ziorep_traffic log
DEVBUSID        WWPN                LUN       ...
                                                 ...
2019-10-21 11:44:38
0.0.1980:0x50050763071bc5e3:0x4006404f00000000   ...
0.0.19c0:0x50050763071bc5e3:0x4006404f00000000   ...

After patch application device bus-ID can also be seen in detailed
(with -D option) output and in CSV output format (with -x option).

Also adjust output headings correspondingly.

Signed-off-by: Fedor Loshakov <loshakov@linux.ibm.com>
Reviewed-by: Steffen Maier <maier@linux.ibm.com>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Reviewed-by: Sakshi Singh <005c7w@linux.ibm.com>
Reviewed-by: M Nikhil <nikh1092@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-13 16:26:35 +01:00
Jan Höppner
d3b7581669 Prepare for next release
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2024-12-06 12:26:08 +01:00
107 changed files with 5793 additions and 1543 deletions

View File

@@ -59,6 +59,7 @@ List of all individuals having contributed content to s390-tools
- Horst Hummel
- Ingo Franzki
- Ingo Tuchscherer
- Jakob Naucke
- Jakub Čajka
- Jan Glauber
- Jan Höppner

View File

@@ -1,6 +1,38 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.37.0 (2025-02-07)__
For Linux kernel version: 6.13
Changes of existing tools:
- dbginfo.sh: Add details on CPU-measurement
- dbginfo.sh: Add new crypto command
- dbginfo.sh: Add overview commands and crypto update
- dbginfo.sh: Adding kdump info
- dbginfo.sh: Removing outdated email references
- dbginfo.sh: Rework network section
- dbginfo.sh: Update copyright 2nd year
- pvimg: Add '--(enable|disable)-image-encryption' flags to 'pvimg create'
- pvimg: Add '--cck <FILE>' command line option and make '--comm-key' an alias
- pvimg: Add '--hdr-key' command line option to 'pvimg create'
- pvimg: Rename '--key' into '--hdr-key' and use '--key' as an alias (for 'pvimg info')
- pvsecret: Add support for retrievable secrets
- ziorep_config: Add PCHID field to adapter report
- ziorep_traffic: Add DEVBUSID column to traffic report
- ziorep_utilization: Add --fcp-device parameter to print virtual adapter report
- ziorep_utilization: Add PCHID column to physical adapter report
- ziorep_utilization: Now prints only physical adapter report by default
- ziorep_utilization: Swap Bus-ID and CHPID columns in virtual adapter report
- zipl/boot: Increase section size for eckd_mv dumper
- zkey: Add support for listing and importing protected virtualization secrets
Bug Fixes:
- chpstat: Fix invalid utilization data on older kernels
- opticsmon: Fix runaway loop in on_link_change()
- zipl: Update inline assembly for GCC 15
- zipl_helper.device-mapper: Add missed step in logical device resolution
* __v2.36.0 (2024-12-06)__
For Linux kernel version: 6.12

View File

@@ -32,7 +32,7 @@ endif
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
VERSION := 2
RELEASE := 36
RELEASE := 37
PATCHLEVEL := 0
DISTRELEASE := build-$(shell date +%Y%m%d)
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)

View File

@@ -46,8 +46,8 @@ All counters are initially disabled and must be switched on to measure
CPACF activities of the system. There is a slight performance penalty with
CPACF counters enabled.
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
counters are only available on LPARs and have to be authorized. If
CPACF activity counters come in two flavors: CPU-MF and PAI. CPU-MF
counters are only available on LPARs and have to be authorized. If
they are available, the counters
.B des,
.B aes,
@@ -55,14 +55,131 @@ they are available, the counters
.B rng,
and
.B ecc
are made available. These counters can individually be activated,
reset, printed, or deactivated. PAI counters are a lot more detailed.
are made available. These counters can individually be activated,
reset, printed, or deactivated. PAI counters are a lot more detailed.
The user interface only offers the counters
.B pai_user
and
.B pai_kernel
to count CPACF usage in user-space or kernel-space. When printing
these counters, detailed counters are shown. The detailed names are:
to count CPACF usage in user-space or kernel-space. When printing
these counters, detailed counters are shown.
A complete list of counters can be found at the end of this manpage.
Note that the counters starting with PCKMO and Reserved are only
available in the pai_kernel set. Also note that the counters are
designed to count successful operations. In the case of KMA this
means only complete GCM operations including final hashing are
counted.
Note: CPU-MF based CPACF performance counters are available on LPARs
only. PAI counters are available on all hypervisors. For security
reasons only members of the group \fIcpacfstats\fR are allowed to run
the cpacfstats client application.
Example usage scenario:
.P
1. Start the cpacfstatsd daemon with root privileges.
.P
2. Check for successful startup by using the ps and syslog commands.
.P
3. Enable the CPACF counters of interest. For example, enable all counters
by issuing cpacfstats -e.
.P
4. Run your applications.
.P
5. Display counter values by using the cpacfstats command. Reset the
cryptographic counters as required. To reset, use, for example, cpacfstats
-r.
.P
6. Disable all the CPACF measurements, for example, by using cpacfstats
-d.
.P
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
.SH OPTIONS
.TP
\fB\-h\fR or \fB\-\-help\fR
Display help information for the command.
.TP
\fB\-v\fR or \fB\-\-version\fR
Display version and copyright information for the command.
.TP
\fB\-e\fR or \fB\-\-enable\fR [counter]
Enable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are enabled. Enabling a counter does not reset
it. New events are added to the current counter value.
.TP
\fB\-d\fR or \fB\-\-disable\fR [counter]
Disable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are disabled. Disabling a counter does not reset
it. The counter value is preserved when a counter is disabled, and
counting will resume using the preserved value when the counter is
re-enabled.
.TP
\fB\-r\fR or \fB\-\-reset\fR [counter]
Reset one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are reset to 0.
.TP
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
Display the value of one or all CPACF performance counters. The
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
\fBall\fR. If the counter argument is omitted or if there is no
argument, all performance counters are displayed. If the optional
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
counters that have a non-zero value are printed.
.TP
\fB\-j\fR or \fB\-\-json\fR
Display all activated counters in JSON format. The JSON contains an
array of counter objects. Each object contains the property
.B counter
specifying either a CPU-MF counter of one of the detailed PAI counter.
Additional properties include
.B error
an error number if the counter could not be read,
.B value
the counter value if the counter could be read,
.B space
for PAI counters to specify
.B user
or
.B kernel
space counter set, and
.B counterid
for PAI counters to specify the PAI counter number as specified in the
Principles of Operation.
.TP
The default command is --print all.
.
.SH FILES
.nf
/run/cpacfstatsd_socket
.fi
.
.SH RETURN VALUE
.IP 0
Successful program execution.
.IP 1
An error occurred, reasons include: invalid argument, cpacfstatsd could not
be reached (check that the daemon is running), insufficient access rights,
version mismatch between client and daemon, or the application is out of
memory. The application prints a message with the details of the error and
the errno value.
.
.SH NOTES
ECC counters are only available since z15. cpacfstats will show the
counters as \fIunsupported\fR if the hardware does not support ECC
counters.
.
.SH APPENDIX
The detailed pai counter names are:
.RS
.IP \(bu
KM DES,
@@ -376,118 +493,39 @@ PCKMO ECC Ed448,
Reserved 1, and
.IP \(bu
Reserved 2.
.IP \(bu
KM AES-XTS (full) 128bit
.IP \(bu
KM AES-XTS (full) 256bit
.IP \(bu
KM AES-XTS (full) 128bit protected key
.IP \(bu
KM AES-XTS (full) 256bit protected key
.IP \(bu
KMAC HMAC SHA 224
.IP \(bu
KMAC HMAC SHA 256
.IP \(bu
KMAC HMAC SHA 384
.IP \(bu
KMAC HMAC SHA 512
.IP \(bu
KMAC HMAC SHA 224 protected key
.IP \(bu
KMAC HMAC SHA 256 protected key
.IP \(bu
KMAC HMAC SHA 384 protected key
.IP \(bu
KMAC HMAC SHA 512 protected key
.IP \(bu
PCKMO HMAC 512 protected key
.IP \(bu
PCKMO HMAC 1024 protected key
.IP \(bu
PCKMO AES-XTS 128bit double key protected key
.IP \(bu
PCKMO AES-XTS 256bit double key protected key
.RE
Note that the counters starting with PCKMO and Reserved are only
available in the pai_kernel set. Also note that the counters are
designed to count successful operations. In the case of KMA this
means only complete GCM operations including final hashing are
counted.
Note: CPU-MF based CPACF performance counters are available on LPARs
only. PAI counters are available on all hypervisors. For security
reasons only members of the group \fIcpacfstats\fR are allowed to run
the cpacfstats client application.
Example usage scenario:
.P
1. Start the cpacfstatsd daemon with root privileges.
.P
2. Check for successful startup by using the ps and syslog commands.
.P
3. Enable the CPACF counters of interest. For example, enable all counters
by issuing cpacfstats -e.
.P
4. Run your applications.
.P
5. Display counter values by using the cpacfstats command. Reset the
cryptographic counters as required. To reset, use, for example, cpacfstats
-r.
.P
6. Disable all the CPACF measurements, for example, by using cpacfstats
-d.
.P
7. Shutdown the cpacfstatsd daemon by using killall cpacfstatsd.
.SH OPTIONS
.TP
\fB\-h\fR or \fB\-\-help\fR
Display help information for the command.
.TP
\fB\-v\fR or \fB\-\-version\fR
Display version and copyright information for the command.
.TP
\fB\-e\fR or \fB\-\-enable\fR [counter]
Enable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are enabled. Enabling a counter does not reset
it. New events are added to the current counter value.
.TP
\fB\-d\fR or \fB\-\-disable\fR [counter]
Disable one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are disabled. Disabling a counter does not reset
it. The counter value is preserved when a counter is disabled, and
counting will resume using the preserved value when the counter is
re-enabled.
.TP
\fB\-r\fR or \fB\-\-reset\fR [counter]
Reset one or all CPACF performance counters. The optional counter
argument can be one of: \fBdes\fR, \fBaes\fR, \fBsha\fR, \fBprng\fR,
\fBecc\fR, or \fBall\fR. If the counter argument is omitted, all
performance counters are reset to 0.
.TP
\fB\-p\fR or \fB\-\-print\fR [\fB\-n\fR or \fB\-\-nonzero\fR] [counter]
Display the value of one or all CPACF performance counters. The
optional counter argument can be one of: \fBdes\fR, \fBaes\fR,
\fBsha\fR, \fBprng\fR, \fBecc\fR, \fBpai_user\fR, \fBpai_kernel\fR, or
\fBall\fR. If the counter argument is omitted or if there is no
argument, all performance counters are displayed. If the optional
\fB\-n\fR or \fB\-\-nonzero\fR argument is given, then only PAI
counters that have a non-zero value are printed.
.TP
\fB\-j\fR or \fB\-\-json\fR
Display all activated counters in JSON format. The JSON contains an
array of counter objects. Each object contains the property
.B counter
specifying either a CPU-MF counter of one of the detailed PAI counter.
Additional properties include
.B error
an error number if the counter could not be read,
.B value
the counter value if the counter could be read,
.B space
for PAI counters to specify
.B user
or
.B kernel
space counter set, and
.B counterid
for PAI counters to specify the PAI counter number as specified in the
Principles of Operation.
.TP
The default command is --print all.
.
.SH FILES
.nf
/run/cpacfstatsd_socket
.fi
.
.SH RETURN VALUE
.IP 0
Successful program execution.
.IP 1
An error occurred, reasons include: invalid argument, cpacfstatsd could not
be reached (check that the daemon is running), insufficient access rights,
version mismatch between client and daemon, or the application is out of
memory. The application prints a message with the details of the error and
the errno value.
.
.SH NOTES
ECC counters are only available since z15. cpacfstats will show the
counters as \fIunsupported\fR if the hardware does not support ECC
counters.
.
.SH SEE ALSO
.BR cpacfstatsd (8)

View File

@@ -10,7 +10,7 @@
.TH \*c "8" "May 2022" "s390-tools" "CPU-MF management programs"
.
.SH NAME
\*c \- extract CPU Measurement Facilities counter sets
lshwc \- extract CPU Measurement Facilities counter sets
.
.SH SYNOPSIS
\*c

View File

@@ -10,7 +10,7 @@
.TH \*c "8" "August 2023" "s390-tools" "CPU-MF management programs"
.
.SH NAME
\*c \- list Processor Activity Instrumentation (PAI) counters
lspai \- list Processor Activity Instrumentation (PAI) counters
.
.SH SYNOPSIS
\*c

View File

@@ -10,7 +10,7 @@
.TH \*c "8" "May 2022" "s390-tools" "PAI Management Programs"
.
.SH NAME
\*c \- record and report Processor Activity Instrumentation (PAI) counters
pai \- record and report Processor Activity Instrumentation (PAI) counters
.
.SH SYNOPSIS
\*c

View File

@@ -0,0 +1,303 @@
#!/bin/sh
#
# check_hostkeydoc - Verify an IBM Secure Execution host key document
#
# Sample script to verify that a host key document is genuine by
# verifying the issuer, the validity date and the signature.
# Optionally verify the full trust chain using a CA certificate.
#
# Sample invocation:
#
# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl
#
# Copyright IBM Corp. 2020
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
# Allocate temporary files
ISSUER_PUBKEY_FILE=$(mktemp)
SIGNATURE_FILE=$(mktemp)
BODY_FILE=$(mktemp)
ISSUER_DN_FILE=$(mktemp)
SUBJECT_DN_FILE=$(mktemp)
DEF_ISSUER_ARMONK_DN_FILE=$(mktemp)
DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp)
CANONICAL_ISSUER_DN_FILE=$(mktemp)
CRL_SERIAL_FILE=$(mktemp)
# Cleanup on exit
cleanup()
{
rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \
"$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \
"$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE"
}
trap cleanup EXIT
# Enhanced error checking for bash
if [ -n "${BASH}" ]; then
# shellcheck disable=SC3040
set -o posix
# shellcheck disable=SC3040
set -o pipefail
# shellcheck disable=SC3040
set -o nounset
fi
set -e
# Usage
usage()
{
cat <<-EOF
Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
Verify an IBM Secure Execution host key document against
a signing key.
Options:
-d disable default issuer check of host-key-doc
-c CA-cert trusted CA certificate
-r CRL list of revoked host-key-docs
Note that in order to have the full trust chain verified
it is necessary to provide the issuing CA's certificate.
The default issuer check may be disabled if a non-default
signing key certificate needs to be verified against the
CA certificate.
EOF
}
check_verify_chain()
{
# Verify certificate chain in case a CA certificate file/bundle
# was specified on the command line.
if [ -z "$2" ]; then
cat >&2 <<-EOF
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
No CA certificate specified! Skipping trust chain verification.
Make sure that '$1' is a valid certificate.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOF
else
openssl verify -crl_download -crl_check "$2" &&
openssl verify -crl_download -crl_check -untrusted "$2" "$1" ||
exit 1
fi
}
extract_pubkey()
{
openssl x509 -in "$1" -pubkey -noout >"$2"
}
extract_signature()
{
# Assuming that the last field is the signature
SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
extract_body()
{
# Assuming that the first field is the full cert body
SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
verify_signature()
{
# Assuming that the signature algorithm is SHA512 with RSA
openssl sha512 -verify "$1" -signature "$2" "$3"
}
canonical_dn()
{
OBJTYPE=$1
OBJ=$2
DNTYPE=$3
OUTPUT=$4
openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline |
LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT"
}
default_issuer_armonk()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Armonk
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
default_issuer_pougkeepsie()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Poughkeepsie
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
# As organizationalUnitName can have an arbitrary prefix but must
# end with "Key Signing Service" let's normalize the OU name by
# stripping off the prefix
verify_default_issuer()
{
default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE"
default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE"
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
"$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE"
if ! {
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" ||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE"
} >/dev/null 2>&1; then
echo Incorrect default issuer >&2 && exit 1
fi
}
verify_issuer_files()
{
if [ "$1" -eq 1 ]; then
verify_default_issuer
fi
}
cert_time()
{
DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
crl_time()
{
DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
verify_dates()
{
START="$1"
END="$2"
MSG="${3:-Certificate}"
NOW=$(date +%s)
if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then
echo "${MSG} dates are OK"
else
echo "${MSG} date verification failed" >&2 && exit 1
fi
}
crl_serials()
{
openssl crl -in "$1" -text -noout |
grep "Serial Number" >"$CRL_SERIAL_FILE"
}
check_serial()
{
CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2)
grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE"
}
check_file()
{
[ -e "$1" ] ||
(echo "File '$1' not found" >&2 && exit 1)
}
# check args
CRL_FILE=
CA_FILE=
CHECK_DEFAULT_ISSUER=1
while getopts 'dr:c:h' opt; do
case $opt in
d) CHECK_DEFAULT_ISSUER=0 ;;
r) CRL_FILE=$OPTARG ;;
c) CA_FILE=$OPTARG ;;
h)
usage "$0"
exit 0
;;
?)
usage "$0"
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ $# -ne 2 ]; then
usage "$0" >&2
exit 1
fi
HKD_FILE=$1
HKSK_FILE=$2
# Check whether all specified files exist
check_file "$HKD_FILE"
check_file "$HKSK_FILE"
# CA and CRL are optional arguments
[ -n "$CA_FILE" ] && check_file "$CA_FILE"
[ -n "$CRL_FILE" ] && check_file "$CRL_FILE"
# Check trust chain
check_verify_chain "$HKSK_FILE" "$CA_FILE"
# Verify host key document signature
printf "Checking host key document signature: "
extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" &&
extract_signature "$HKD_FILE" "$SIGNATURE_FILE" &&
extract_body "$HKD_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
# Verify the issuer
canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
# Verify dates
verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)"
# Check CRL if specified
if [ -n "$CRL_FILE" ]; then
printf "Checking CRL signature: "
extract_signature "$CRL_FILE" "$SIGNATURE_FILE" &&
extract_body "$CRL_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
printf "CRL "
canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL'
crl_serials "$CRL_FILE"
check_serial "$HKD_FILE" &&
echo "Certificate is revoked, do not use it anymore!" >&2 &&
exit 1
fi
# We made it
echo All checks requested for \'"$HKD_FILE"\' were successful

View File

@@ -21,7 +21,7 @@
#define STAGE2_DESC _AC(0x78, UL)
#define STAGE2_ENTRY _AC(0x2018, UL)
#define ECKD2DUMP_MV_TAIL_ADDRESS _AC(0x5000, UL)
#define ECKD2DUMP_MV_TAIL_ADDRESS _AC(0x6000, UL)
#define STAGE2_HEAP_ADDRESS _AC(0x6000, UL)
#define ECKD2DUMP_HEAP_ADDRESS _AC(0xb000, UL)
#define STAGE2_HEAP_SIZE _AC(0x3000, UL)
@@ -34,7 +34,7 @@
#define STAGE2_DUMPER_SIZE_V1 _AC(0x1000, UL)
#define STAGE2_DUMPER_SIZE_V2 _AC(0x2000, UL)
#define STAGE2_DUMPER_SIZE_V3 _AC(0x3000, UL)
#define STAGE2_DUMPER_SIZE_MV _AC(0x3000, UL)
#define STAGE2_DUMPER_SIZE_MV _AC(0x4000, UL)
#define STAGE2_DUMPER_SIZE_ZLIB _AC(0x8000, UL)
#define STAGE3_ENTRY _AC(0xa000, UL)

View File

@@ -93,4 +93,7 @@ const char *zpci_pft_str(struct zpci_dev *zdev);
const char *zpci_operstate_str(operstate_t state);
operstate_t zpci_operstate_from_str(const char *oper_str);
struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
struct zpci_netdev **netdev);
#endif /* LIB_ZPCI_PCI_LIST_H */

View File

@@ -356,3 +356,34 @@ void zpci_free_dev_list(struct util_list *zpci_list)
}
util_list_free(zpci_list);
}
/**
* Find a PCI device given the name of a netdev
*
* This function allows finding a PCI device when only the name of one
* of its netdevs is known.
*
* @param[in] zpci_list The device list to search
* @param[in] netdev_name The name of the netdev
* @param[out] netdev Pointer to store the netdev or NULL if
* only the PCI device is needed
*
* @return The PCI device if one is found NULL otherwise
*/
struct zpci_dev *zpci_find_by_netdev(struct util_list *zpci_list, char *netdev_name,
struct zpci_netdev **netdev)
{
struct zpci_dev *zdev = NULL;
int i;
util_list_iterate(zpci_list, zdev) {
for (i = 0; i < zdev->num_netdevs; i++) {
if (!strcmp(zdev->netdevs[i].name, netdev_name)) {
if (netdev)
*netdev = &zdev->netdevs[i];
return zdev;
}
}
}
return NULL;
}

View File

@@ -171,8 +171,6 @@ static const char help_text[] =
"-h, --help Print this help, then exit\n"
"-v, --version Print version information, then exit\n"
"-a, --attach Run in foreground\n"
"-i, --interval=<seconds> Sample interval\n"
"\n"
"Please report bugs to: linux390@de.ibm.com\n";
"-i, --interval=<seconds> Sample interval\n";
#endif

View File

@@ -274,38 +274,33 @@ static int oneshot_mode(struct opticsmon_ctx *ctx)
void on_link_change(struct zpci_netdev *netdev, void *arg)
{
struct opticsmon_ctx *ctx = arg;
struct zpci_dev *zdev;
int i, reloads = 1;
struct zpci_netdev *found_netdev;
struct zpci_dev *zdev = NULL;
int reloads = 1;
if (!ctx->zpci_list || util_list_is_empty(ctx->zpci_list))
zpci_list_reload(&ctx->zpci_list);
reload:
util_list_iterate(ctx->zpci_list, zdev) {
for (i = 0; i < zdev->num_netdevs; i++) {
if (!strcmp(zdev->netdevs[i].name, netdev->name)) {
reloads--;
do {
if (ctx->zpci_list) {
zdev = zpci_find_by_netdev(ctx->zpci_list, netdev->name, &found_netdev);
if (zdev) {
/* Skip data collection if operational state is
* unchanged
*/
if (zdev->netdevs[i].operstate == netdev->operstate)
continue;
if (found_netdev->operstate == netdev->operstate)
return;
/* Update operation state for VFs even though
* they are skipped just for a consistent view
*/
zdev->netdevs[i].operstate = netdev->operstate;
found_netdev->operstate = netdev->operstate;
/* Only collect optics data for PFs */
if (!zpci_is_vf(zdev))
dump_adapter_data(ctx, zdev);
return;
}
}
}
/* Might be a new device, reload list of devices and retry */
if (reloads) {
/* Could be uninitalized list or a new device, retry after reload */
zpci_list_reload(&ctx->zpci_list);
reloads--;
goto reload;
}
} while (reloads > 0);
}
#define MAX_EVENTS 8

493
rust/Cargo.lock generated
View File

@@ -4,18 +4,18 @@ version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "anstream"
version = "0.6.15"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -34,43 +34,43 @@ checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anstyle-parse"
version = "0.2.0"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.4"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.71"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
[[package]]
name = "autocfg"
version = "1.1.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "base64"
@@ -80,30 +80,24 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bit-set"
version = "0.5.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "1.3.2"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
[[package]]
name = "bitvec"
@@ -119,15 +113,18 @@ dependencies = [
[[package]]
name = "byteorder"
version = "1.4.3"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.79"
version = "1.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
@@ -137,9 +134,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.18"
version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
dependencies = [
"clap_builder",
"clap_derive",
@@ -147,9 +144,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.18"
version = "4.5.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
dependencies = [
"anstream",
"anstyle",
@@ -160,9 +157,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.5.34"
version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e16bb32eaa759f7fe76c59793c4e00dad6d11705f74ddfe4887e62f459536f16"
checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
dependencies = [
"clap",
]
@@ -181,15 +178,15 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.7.3"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.0"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "cpacfinfo"
@@ -208,9 +205,9 @@ dependencies = [
[[package]]
name = "curl"
version = "0.4.44"
version = "0.4.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22"
checksum = "d9fb4d13a1be2b58f14d60adba57c9834b78c62fd86c3e76a148f732686e9265"
dependencies = [
"curl-sys",
"libc",
@@ -218,14 +215,14 @@ dependencies = [
"openssl-sys",
"schannel",
"socket2",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
name = "curl-sys"
version = "0.4.72+curl-8.6.0"
version = "0.4.78+curl-8.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea"
checksum = "8eec768341c5c7789611ae51cf6c459099f22e64a5d5d0ce4892434e33821eaf"
dependencies = [
"cc",
"libc",
@@ -316,30 +313,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.1"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "fastrand"
version = "2.2.0"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fnv"
@@ -370,9 +356,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "getrandom"
version = "0.2.10"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
@@ -437,27 +423,21 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "lazy_static"
version = "1.4.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.146"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
[[package]]
name = "libm"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "libz-sys"
version = "1.1.9"
version = "1.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
checksum = "d2d16453e800a8cf6dd2fc3eb4bc99b786a9b90c663b8559a5b1a041bf89e472"
dependencies = [
"cc",
"libc",
@@ -467,21 +447,21 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "log"
version = "0.4.19"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "memchr"
version = "2.6.4"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "no_std_io2"
@@ -499,22 +479,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
"libm",
]
[[package]]
name = "once_cell"
version = "1.19.0"
version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]]
name = "openssl"
version = "0.10.68"
version = "0.10.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6"
dependencies = [
"bitflags 2.4.1",
"bitflags",
"cfg-if",
"foreign-types",
"libc",
@@ -542,9 +521,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.104"
version = "0.9.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
dependencies = [
"cc",
"libc",
@@ -554,15 +533,18 @@ dependencies = [
[[package]]
name = "pkg-config"
version = "0.3.27"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro-crate"
@@ -575,22 +557,22 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.75"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proptest"
version = "1.5.0"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50"
dependencies = [
"bit-set",
"bit-vec",
"bitflags 2.4.1",
"bitflags",
"lazy_static",
"num-traits",
"rand",
@@ -679,9 +661,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.35"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
@@ -731,20 +713,11 @@ dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "regex"
version = "1.10.2"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
@@ -754,9 +727,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.3"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
@@ -765,21 +738,21 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.2"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustix"
version = "0.38.3"
version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4"
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [
"bitflags 2.4.1",
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -840,27 +813,27 @@ dependencies = [
[[package]]
name = "schannel"
version = "0.1.21"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
dependencies = [
"windows-sys 0.42.0",
"windows-sys 0.59.0",
]
[[package]]
name = "serde"
version = "1.0.164"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
@@ -880,9 +853,9 @@ dependencies = [
[[package]]
name = "serde_test"
version = "1.0.176"
version = "1.0.177"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab"
checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed"
dependencies = [
"serde",
]
@@ -901,13 +874,19 @@ dependencies = [
]
[[package]]
name = "socket2"
version = "0.4.9"
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
@@ -918,9 +897,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.47"
version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb"
checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [
"proc-macro2",
"quote",
@@ -935,41 +914,42 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tempfile"
version = "3.8.0"
version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"getrandom",
"once_cell",
"rustix",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
name = "terminal_size"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
dependencies = [
"rustix",
"windows-sys 0.48.0",
"windows-sys 0.59.0",
]
[[package]]
name = "thiserror"
version = "1.0.40"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
dependencies = [
"proc-macro2",
"quote",
@@ -1001,9 +981,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unicode-ident"
version = "1.0.9"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
[[package]]
name = "unsafe-libyaml"
@@ -1013,9 +993,9 @@ checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6"
[[package]]
name = "utf8parse"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "utils"
@@ -1049,216 +1029,87 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.4",
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
@@ -1280,9 +1131,9 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.7.32"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive",
@@ -1290,9 +1141,9 @@ dependencies = [
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -26,6 +26,3 @@ unstable_features = "warn"
unused = "warn"
unused_import_braces = "warn"
unused_qualifications = "warn"
[profile.release]
lto = true

View File

@@ -6,15 +6,15 @@ license.workspace = true
rust-version.workspace = true
[dependencies]
anyhow = "1.0.71"
clap = { version = "4.1", features = ["derive"] }
anyhow = "1.0.95"
clap = { version = "4.5", features = ["derive"] }
libc = "0.2"
serde = { version = "1.0.164", features = ["derive"] }
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0"
utils = { path = "../utils" }
zerocopy = { version="0.7", features = ["derive"] }
[build-dependencies]
cc = "1.0"
clap = { version ="4.1", features = ["derive"]}
lazy_static = "1.1"
cc = "1.2"
clap = { version ="4.5", features = ["derive"]}
lazy_static = "1.5"

View File

@@ -14,18 +14,18 @@ readme = "README.md"
workspace = true
[dependencies]
byteorder = "1.3"
curl = "0.4.44"
byteorder = "1.5"
curl = "0.4.47"
enum_dispatch = "0.3.13"
foreign-types = "0.3.1"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
openssl = "0.10.66"
openssl-sys = "0.9.92"
serde = { version = "1.0.139", features = ["derive"] }
thiserror = "1.0.33"
foreign-types = "0.3.2"
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
openssl = "0.10.70"
openssl-sys = "0.9.105"
serde = { version = "1.0.217", features = ["derive"] }
thiserror = "2.0.11"
zerocopy = { version="0.7", features = ["derive"] }
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.11.0" }
[dev-dependencies]
serde_test = "1.0.139"
serde_test = "1.0.177"

View File

@@ -29,7 +29,6 @@ pub type Aes256XtsKey = Confidential<[u8; SymKeyType::AES_256_XTS_KEY_LEN]>;
/// SHA-512 digest length (in bytes)
pub const SHA_512_HASH_LEN: usize = 64;
#[allow(dead_code)]
pub(crate) const SHA_256_HASH_LEN: u32 = 32;
#[allow(dead_code)]
@@ -60,6 +59,8 @@ impl SymKeyType {
pub const AES_256_XTS_KEY_LEN: usize = 64;
/// AES256-XTS tweak length (in bytes)
pub const AES_256_XTS_TWEAK_LEN: usize = 16;
/// AES256 GCM Block length
pub const AES_256_GCM_BLOCK_LEN: usize = 16;
/// Returns the tag length of the [`SymKeyType`] if it is an AEAD key
pub const fn tag_len(&self) -> Option<usize> {

View File

@@ -55,7 +55,7 @@ pub enum Error {
#[error("Decryption failed. Probably due to a GCM tag mismatch.")]
GcmTagMismatch,
#[error("Invalid {0} user-data for signing provided. Max {} bytes allowed", .0.max())]
#[error("Invalid {0} user-data for signing provided. Max {max} bytes allowed", max=.0.max())]
AsrcbInvSgnUserData(UserDataType),
#[error("Unsupported user data signing key provided. Only EC(secp521r1) and RSA(2048 & 3072 bit) are supported")]
@@ -106,6 +106,20 @@ pub enum Error {
)]
AddDataMissing(&'static str),
#[error("An ASCII string was expected, but non-ASCII characters were received.")]
NonAscii,
#[error("Incorrect {what} for a {kind}. Is: {value}; expected: {exp}")]
RetrInvKey {
what: &'static str,
kind: String,
value: String,
exp: String,
},
#[error("Invalid data from OpenSSL")]
InvalSslData,
// errors from other crates
#[error(transparent)]
PvCore(#[from] pv_core::Error),

View File

@@ -37,6 +37,7 @@ mod brcb;
mod crypto;
mod error;
mod openssl_extensions;
mod pem_utils;
mod req;
mod utils;
mod uvattest;
@@ -71,6 +72,11 @@ pub mod attest {
};
}
/// Definitions and functions to write objects in PEM format
pub mod pem {
pub use crate::pem_utils::Pem;
}
/// Miscellaneous functions and definitions
pub mod misc {
pub use pv_core::misc::*;
@@ -98,7 +104,12 @@ pub mod request {
/// Reexports some useful OpenSSL symbols
pub mod openssl {
pub use openssl::{error::ErrorStack, hash::DigestBytes, pkey, x509};
pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, x509};
// rust-OpenSSL does not define these NIDs
#[allow(missing_docs)]
pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
#[allow(missing_docs)]
pub const NID_ED448: Nid = Nid::from_raw(openssl_sys::NID_ED448);
}
pub use pv_core::request::*;
@@ -112,6 +123,7 @@ pub mod secret {
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
ext_secret::ExtSecret,
guest_secret::GuestSecret,
retr_secret::{IbmProtectedKey, RetrievedSecret},
user_data::verify_asrcb_and_get_user_data,
};
}

View File

@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use core::slice;
use openssl::error::ErrorStack;
use openssl_sys::BIO_new_mem_buf;
use std::ffi::c_int;
use std::{marker::PhantomData, ptr};
pub struct BioMem(*mut openssl_sys::BIO);
impl Drop for BioMem {
fn drop(&mut self) {
// SAFETY: Pointer is valid. The pointer value is dropped after the free.
unsafe {
openssl_sys::BIO_free_all(self.0);
}
}
}
impl BioMem {
pub fn new() -> Result<Self, ErrorStack> {
openssl_sys::init();
// SAFETY: Returns a valid pointer or null. null-case is tested right after this.
let bio = unsafe { openssl_sys::BIO_new(openssl_sys::BIO_s_mem()) };
match bio.is_null() {
true => Err(ErrorStack::get()),
false => Ok(Self(bio)),
}
}
pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
self.0
}
/// Copies the content of this slice into a Vec
pub fn to_vec(&self) -> Vec<u8> {
let buf;
// SAFTEY: BIO provides a continuous memory that can be used to build a slice.
unsafe {
let mut ptr = ptr::null_mut();
let len = openssl_sys::BIO_get_mem_data(self.0, &mut ptr);
buf = slice::from_raw_parts(ptr as *const _ as *const _, len as usize)
}
buf.to_vec()
}
}
pub struct BioMemSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
impl Drop for BioMemSlice<'_> {
fn drop(&mut self) {
// SAFETY: Pointer is valid. The pointer value is dropped after the free.
unsafe {
openssl_sys::BIO_free_all(self.0);
}
}
}
impl<'a> BioMemSlice<'a> {
pub fn new(buf: &'a [u8]) -> Result<BioMemSlice<'a>, ErrorStack> {
openssl_sys::init();
// SAFETY: `buf` is a slice (i.e. pointer+size) pointing to a valid memory region.
// So the resulting bio is valid. Lifetime of the slice is connected by this Rust
// structure.
assert!(buf.len() <= c_int::MAX as usize);
let bio = unsafe {
{
let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
match r.is_null() {
true => Err(ErrorStack::get()),
false => Ok(r),
}
}?
};
Ok(BioMemSlice(bio, PhantomData))
}
pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
self.0
}
}

View File

@@ -6,8 +6,10 @@
/// Extensions to the rust-openssl crate
mod akid;
mod bio;
mod crl;
mod stackable_crl;
pub use akid::*;
pub use bio::*;
pub use crl::*;

View File

@@ -2,16 +2,14 @@
//
// Copyright IBM Corp. 2023
use std::{marker::PhantomData, ptr};
use crate::openssl_extensions::bio::BioMemSlice;
use foreign_types::{ForeignType, ForeignTypeRef};
use openssl::{
error::ErrorStack,
stack::Stackable,
x509::{X509Crl, X509CrlRef},
};
use openssl_sys::BIO_new_mem_buf;
use std::ffi::c_int;
use std::ptr;
#[derive(Debug)]
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
@@ -62,44 +60,11 @@ impl Stackable for StackableX509Crl {
type StackType = openssl_sys::stack_st_X509_CRL;
}
pub struct MemBioSlice<'a>(*mut openssl_sys::BIO, PhantomData<&'a [u8]>);
impl Drop for MemBioSlice<'_> {
fn drop(&mut self) {
unsafe {
openssl_sys::BIO_free_all(self.0);
}
}
}
impl<'a> MemBioSlice<'a> {
pub fn new(buf: &'a [u8]) -> Result<MemBioSlice<'a>, ErrorStack> {
openssl_sys::init();
assert!(buf.len() <= c_int::MAX as usize);
let bio = unsafe {
{
let r = BIO_new_mem_buf(buf.as_ptr() as *const _, buf.len() as c_int);
if r.is_null() {
Err(ErrorStack::get())
} else {
Ok(r)
}
}?
};
Ok(MemBioSlice(bio, PhantomData))
}
pub fn as_ptr(&self) -> *mut openssl_sys::BIO {
self.0
}
}
impl StackableX509Crl {
pub fn stack_from_pem(pem: &[u8]) -> Result<Vec<X509Crl>, ErrorStack> {
unsafe {
openssl_sys::init();
let bio = MemBioSlice::new(pem)?;
let bio = BioMemSlice::new(pem)?;
let mut crls = vec![];
loop {

222
rust/pv/src/pem_utils.rs Normal file
View File

@@ -0,0 +1,222 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use crate::Result;
use crate::{openssl_extensions::BioMem, Error};
use openssl::error::ErrorStack;
use pv_core::request::Confidential;
use std::{
ffi::{c_char, CString},
fmt::Display,
};
mod ffi {
use openssl_sys::BIO;
use std::ffi::{c_char, c_int, c_long, c_uchar};
extern "C" {
pub fn PEM_write_bio(
bio: *mut BIO,
name: *const c_char,
header: *const c_char,
data: *const c_uchar,
len: c_long,
) -> c_int;
}
}
/// Thin wrapper around [`CString`] only containing ASCII chars.
#[derive(Debug)]
struct AsciiCString(CString);
impl AsciiCString {
/// Convert from string
///
/// # Returns
/// Error if string is not ASCII or contains null chars
pub(crate) fn from_str(s: &str) -> Result<Self> {
match s.is_ascii() {
true => Ok(Self(CString::new(s).map_err(|_| Error::NonAscii)?)),
false => Err(Error::NonAscii),
}
}
fn as_ptr(&self) -> *const c_char {
self.0.as_ptr()
}
}
/// Helper struct to construct the PEM format
#[derive(Debug)]
struct InnerPem<'d> {
name: AsciiCString,
header: Option<AsciiCString>,
data: &'d [u8],
}
impl<'d> InnerPem<'d> {
fn new(name: &str, header: Option<String>, data: &'d [u8]) -> Result<Self> {
Ok(Self {
name: AsciiCString::from_str(name)?,
header: match header {
Some(h) => Some(AsciiCString::from_str(&h)?),
None => None,
},
data,
})
}
/// Generate PEM representation of the data
fn to_pem(&self) -> Result<Vec<u8>> {
let bio = BioMem::new()?;
let hdr_ptr = match self.header {
// avoid moving variable -> use reference
Some(ref h) => h.as_ptr(),
None => std::ptr::null(),
};
// SAFETY:
// All pointers point to valid C strings or memory regions
let rc = unsafe {
ffi::PEM_write_bio(
bio.as_ptr(),
self.name.as_ptr(),
hdr_ptr,
self.data.as_ptr(),
self.data.len() as std::ffi::c_long,
)
};
match rc {
1 => Err(Error::InternalSsl("Could not write PEM", ErrorStack::get())),
_ => Ok(bio.to_vec()),
}
}
}
/// Data in PEM format
///
/// Displays into a printable PEM structure.
/// Must be constructed from another structure in this library.
///
/// ```rust,ignore
/// let pem: Pem = ...;
/// println!("PEM {pem}");
/// ```
/// ```PEM
///-----BEGIN <name>-----
///<header>
///
///<Base64 formatted binary data>
///-----END <name>-----
#[derive(Debug)]
pub struct Pem {
pem: Confidential<String>,
}
#[allow(unused)]
impl Pem {
/// Create a new PEM structure.
///
/// # Errors
///
/// This function will return an error if name or header contain non-ASCII chars, or OpenSSL
/// could not generate the PEM (very likely due to OOM).
pub(crate) fn new<D, H>(name: &str, header: H, data: D) -> Result<Self>
where
D: AsRef<[u8]>,
H: Into<Option<String>>,
{
let mut header = header.into();
let header = match header {
Some(h) if h.ends_with('\n') => Some(h),
Some(h) if h.is_empty() => None,
Some(mut h) => {
h.push('\n');
Some(h)
}
None => None,
};
let inner_pem = InnerPem::new(name, header, data.as_ref())?;
// Create the PEM format eagerly so that to_string/display cannot fail because of ASCII or OpenSSL Errors
// Both error should be very unlikely
// OpenSSL should be able to create PEM if there is enough memory and produce a non-null
// terminated ASCII-string
// Unwrap succeeds it's all ASCII
// Std lib implements all the conversations without a copy
let pem = CString::new(inner_pem.to_pem()?)
.map_err(|_| Error::NonAscii)?
.into_string()
.unwrap()
.into();
Ok(Self { pem })
}
/// Converts the PEM-data into a byte vector.
///
/// This consumes the `PEM`.
#[inline]
#[must_use = "`self` will be dropped if the result is not used"]
pub fn into_bytes(self) -> Confidential<Vec<u8>> {
self.pem.into_inner().into_bytes().into()
}
}
impl Display for Pem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.pem.value().fmt(f)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn no_data() {
const EXP: &str =
"-----BEGIN PEM test-----\ntest hdr value: 17\n\n-----END PEM test-----\n";
let test_pem = Pem::new("PEM test", "test hdr value: 17".to_string(), []).unwrap();
let pem_str = test_pem.to_string();
assert_eq!(pem_str, EXP);
}
#[test]
fn no_hdr() {
const EXP: &str =
"-----BEGIN PEM test-----\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
let test_pem = Pem::new("PEM test", None, "very secret key").unwrap();
let pem_str = test_pem.to_string();
assert_eq!(pem_str, EXP);
}
#[test]
fn some_data() {
const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
let test_pem = Pem::new(
"PEM test",
"test hdr value: 17".to_string(),
"very secret key",
)
.unwrap();
let pem_str = test_pem.to_string();
assert_eq!(pem_str, EXP);
}
#[test]
fn data_linebreak() {
const EXP: &str= "-----BEGIN PEM test-----\ntest hdr value: 17\n\ndmVyeSBzZWNyZXQga2V5\n-----END PEM test-----\n";
let test_pem = Pem::new(
"PEM test",
"test hdr value: 17\n".to_string(),
"very secret key",
)
.unwrap();
let pem_str = test_pem.to_string();
assert_eq!(pem_str, EXP);
}
}

View File

@@ -10,4 +10,5 @@
pub mod asrcb;
pub mod ext_secret;
pub mod guest_secret;
pub mod retr_secret;
pub mod user_data;

View File

@@ -4,20 +4,34 @@
#[allow(unused_imports)] // used for more convenient docstring
use super::asrcb::AddSecretRequest;
use crate::assert_size;
use crate::{
crypto::{hash, random_array},
request::Confidential,
Result,
assert_size,
crypto::{hash, random_array, SymKeyType},
request::{
openssl::{NID_ED25519, NID_ED448},
Confidential,
},
uv::{
AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
RetrieveCmd, SecretId,
},
Error, Result,
};
use byteorder::BigEndian;
use openssl::hash::MessageDigest;
use pv_core::uv::{ListableSecretType, SecretId};
use openssl::{
hash::MessageDigest,
nid::Nid,
pkey::{Id, PKey, PKeyRef, Private},
};
use pv_core::static_assert;
use serde::{Deserialize, Serialize};
use std::{convert::TryInto, fmt::Display};
use std::fmt::Display;
use zerocopy::{AsBytes, U16, U32};
const ASSOC_SECRET_SIZE: usize = 32;
/// Maximum size of a plain-text secret payload (8190)
pub(crate) const MAX_SIZE_PLAIN_PAYLOAD: usize = RetrieveCmd::MAX_SIZE - 2;
static_assert!(MAX_SIZE_PLAIN_PAYLOAD == 8190);
/// A Secret to be added in [`AddSecretRequest`]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
@@ -36,13 +50,61 @@ pub enum GuestSecret {
#[serde(skip)]
secret: Confidential<[u8; ASSOC_SECRET_SIZE]>,
},
/// Retrievable key
///
/// Create Retrievables using [`GuestSecret::retrievable`]
/// Secret size is always valid for the type/kind
Retrievable {
/// Retrievable secret type
kind: RetrievableSecret,
/// Name of the secret
name: String,
/// SHA256 hash of [`GuestSecret::RetrievableKey::name`]
id: SecretId,
/// Confidential actual retrievable secret
#[serde(skip)]
secret: Confidential<Vec<u8>>,
},
}
macro_rules! retr_constructor {
($(#[$err:meta])* | $(#[$kind:meta])* => $type: ty, $func: ident) => {
/// Create a new
$(#[$kind])*
/// [`GuestSecret::Retrievable`] secret.
///
/// * `name` - Name of the secret. Will be hashed into a 32 byte id
/// * `secret` - the secret value
///
/// # Errors
///
$(#[$err])*
pub fn $func(name: &str, secret: $type) -> Result<Self> {
let (kind, secret) = $func(secret)?;
Ok(Self::Retrievable {
kind,
name: name.to_string(),
id: Self::name_to_id(name)?,
secret,
})
}
};
}
impl GuestSecret {
/// Hashes the name with sha256
pub fn name_to_id(name: &str) -> Result<SecretId> {
let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
.to_vec()
.try_into()
.unwrap();
Ok(id.into())
}
/// Create a new [`GuestSecret::Association`].
///
/// * `name` - Name of the secret. Will be hashed into a 32 byte id
/// * `secret` - Value of the secret. Ranom if [`Option::None`]
/// * `secret` - Value of the secret. Random if [`Option::None`]
///
/// # Errors
///
@@ -51,10 +113,6 @@ impl GuestSecret {
where
O: Into<Option<[u8; ASSOC_SECRET_SIZE]>>,
{
let id: [u8; SecretId::ID_SIZE] = hash(MessageDigest::sha256(), name.as_bytes())?
.to_vec()
.try_into()
.unwrap();
let secret = match secret.into() {
Some(s) => s,
None => random_array()?,
@@ -62,16 +120,41 @@ impl GuestSecret {
Ok(Self::Association {
name: name.to_string(),
id: id.into(),
id: Self::name_to_id(name)?,
secret: secret.into(),
})
}
retr_constructor!(#[doc = r"This function will return an error if the secret is larger than 8 pages"]
| #[doc = r"plaintext"] => Confidential<Vec<u8>>, plaintext);
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
| #[doc = r"AES Key"] => Confidential<Vec<u8>>, aes);
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
| #[doc = r"AES-XTS Key"] => Confidential<Vec<u8>>, aes_xts);
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the secret size is invalid"]
| #[doc = r"HMAC-SHA Key"] => Confidential<Vec<u8>>, hmac_sha);
retr_constructor!(#[doc = r"This function will return an error if OpenSSL cannot create a hash or the curve is invalid"]
| #[doc = r"EC PRIVATE Key"] => PKey<Private>, ec);
/// Use the name as ID, do not hash it
pub fn no_hash_name(&mut self) {
match self {
Self::Null => (),
Self::Association {
name, ref mut id, ..
}
| Self::Retrievable {
name, ref mut id, ..
} => id.clone_from(&SecretId::from_string(name)),
}
}
/// Reference to the confidential data
pub(crate) fn confidential(&self) -> &[u8] {
pub fn confidential(&self) -> &[u8] {
match &self {
Self::Null => &[],
Self::Association { secret, .. } => secret.value().as_slice(),
Self::Retrievable { secret, .. } => secret.value(),
}
}
@@ -79,7 +162,7 @@ impl GuestSecret {
pub(crate) fn auth(&self) -> SecretAuth {
match &self {
Self::Null => SecretAuth::Null,
// Panic: every non null secret type is listable -> no panic
// Panic: every non null secret type is list-able -> no panic
listable => {
SecretAuth::Listable(ListableSecretHdr::from_guest_secret(listable).unwrap())
}
@@ -92,6 +175,7 @@ impl GuestSecret {
// Null is not listable, but the ListableSecretType provides the type constant (1)
Self::Null => ListableSecretType::NULL,
Self::Association { .. } => ListableSecretType::ASSOCIATION,
Self::Retrievable { kind, .. } => kind.into(),
}
}
@@ -100,6 +184,7 @@ impl GuestSecret {
match self {
Self::Null => 0,
Self::Association { secret, .. } => secret.value().len() as u32,
Self::Retrievable { secret, .. } => secret.value().len() as u32,
}
}
@@ -107,18 +192,172 @@ impl GuestSecret {
fn id(&self) -> Option<SecretId> {
match self {
Self::Null => None,
Self::Association { id, .. } => Some(id.to_owned()),
Self::Association { id, .. } | Self::Retrievable { id, .. } => Some(id.to_owned()),
}
}
}
type RetrKeyInfo = (RetrievableSecret, Confidential<Vec<u8>>);
fn extend_to_multiple(mut key: Vec<u8>, multiple: usize) -> Confidential<Vec<u8>> {
match key.len().checked_rem(multiple) {
Some(0) | None => key,
Some(m) => {
key.resize(key.len() + multiple - m, 0);
key
}
}
.into()
}
/// Get a plain-text key
///
/// ```none
/// size U16<BigEndian> | payload (0-8190) bytes
/// ```
fn plaintext(inp: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
let key_len = inp.value().len();
if key_len > RetrieveCmd::MAX_SIZE {
return Err(Error::RetrInvKey {
what: "key size",
value: key_len.to_string(),
kind: RetrievableSecret::PlainText.to_string(),
exp: RetrievableSecret::PlainText.expected(),
});
}
let mut key = Vec::with_capacity(2 + inp.value().len());
let key_len: U16<BigEndian> = (key_len as u16).into();
key.extend_from_slice(key_len.as_bytes());
key.extend_from_slice(inp.value());
let key = extend_to_multiple(key, SymKeyType::AES_256_GCM_BLOCK_LEN);
Ok((RetrievableSecret::PlainText, key))
}
/// Get an AES-key
fn aes(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
let key_len = key.value().len() as u32;
let bit_size = bitsize(key_len);
match AesSizes::from_bits(bit_size) {
Some(size) => Ok((RetrievableSecret::Aes(size), key)),
None => {
// Use some AES type to get exp sizes and name
let kind = RetrievableSecret::Aes(AesSizes::Bits128);
Err(Error::RetrInvKey {
what: "key size",
value: bit_size.to_string(),
kind: format!("{kind:#}"),
exp: kind.expected(),
})
}
}
}
/// Get an AES-XTS-key
fn aes_xts(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
let key_len = key.value().len() as u32;
let bit_size = bitsize(key_len / 2);
match AesXtsSizes::from_bits(bit_size) {
Some(size) => Ok((RetrievableSecret::AesXts(size), key)),
None => {
// Use some AES-XTS type to get exp sizes and name
let kind = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
Err(Error::RetrInvKey {
what: "key size",
value: bit_size.to_string(),
kind: format!("{kind:#}"),
exp: kind.expected(),
})
}
}
}
/// Get an HMAC-SHA-key
fn hmac_sha(key: Confidential<Vec<u8>>) -> Result<RetrKeyInfo> {
let key_len = key.value().len() as u32;
let size = bitsize(key_len / 2);
match HmacShaSizes::from_sha_size(size) {
Some(size) => Ok((RetrievableSecret::HmacSha(size), key)),
None => {
// Use some HMAC type to get exp sizes and name
let kind = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
Err(Error::RetrInvKey {
what: "key size",
value: size.to_string(),
kind: format!("{kind:#}"),
exp: kind.expected(),
})
}
}
}
/// Get an EC-private-key
fn ec(key: PKey<Private>) -> Result<RetrKeyInfo> {
// reads & left-pads Edward EC keys
fn pad_ed_key(pkey: &PKeyRef<Private>, curve: &EcCurves) -> Result<Vec<u8>> {
let raw_key = pkey.raw_private_key()?;
match raw_key.len().cmp(&curve.exp_key_size()) {
std::cmp::Ordering::Less => {
let mut key = Vec::with_capacity(curve.exp_key_size());
key.extend_from_slice(&vec![0u8; curve.exp_key_size() - raw_key.len()]);
key.extend_from_slice(&raw_key);
Ok(key)
}
std::cmp::Ordering::Equal => Ok(raw_key),
std::cmp::Ordering::Greater => Err(Error::InvalSslData),
}
}
let nid = match key.id() {
Id::EC => key.ec_key()?.group().curve_name().unwrap_or(Nid::UNDEF),
id @ (Id::ED25519 | Id::ED448) => Nid::from_raw(id.as_raw()),
_ => Nid::UNDEF,
};
let kind = match nid {
Nid::X9_62_PRIME256V1 => EcCurves::Secp256R1,
Nid::SECP384R1 => EcCurves::Secp384R1,
Nid::SECP521R1 => EcCurves::Secp521R1,
NID_ED25519 => EcCurves::Ed25519,
NID_ED448 => EcCurves::Ed448,
nid => {
// Use some EC type to get exp sizes and name
let ec = RetrievableSecret::Ec(EcCurves::Secp521R1);
return Err(Error::RetrInvKey {
what: "curve or format",
kind: format!("{ec:#}"),
value: nid.long_name()?.to_string(),
exp: ec.expected(),
});
}
};
let key = match key.id() {
Id::EC => key
.ec_key()?
.private_key()
.to_vec_padded(kind.exp_key_size() as i32)?,
// ED keys are not handled via the EC struct in OpenSSL.
Id::ED25519 | Id::ED448 => pad_ed_key(&key, &kind)?,
_ => unreachable!(),
};
Ok((RetrievableSecret::Ec(kind), key.into()))
}
#[inline(always)]
const fn bitsize(bytesize: u32) -> u32 {
bytesize * 8
}
impl Display for GuestSecret {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Null => write!(f, "Meta"),
gs => {
let kind: U16<BigEndian> = gs.kind().into();
let st: ListableSecretType = kind.into();
let st: ListableSecretType = kind.get().into();
write!(f, "{st}")
}
}
@@ -153,20 +392,24 @@ assert_size!(ListableSecretHdr, 0x30);
impl ListableSecretHdr {
fn from_guest_secret(gs: &GuestSecret) -> Option<Self> {
let id = gs.id()?;
Some(Self {
res0: 0,
kind: gs.kind().into(),
secret_len: gs.secret_len().into(),
res8: 0,
id,
id: gs.id()?,
})
}
}
#[cfg(test)]
mod test {
use super::HmacShaSizes as HmacSizes;
use super::RetrievableSecret::*;
use super::*;
use openssl::ec::{EcGroup, EcKey};
use pv_core::uv::AesSizes;
use serde_test::{assert_tokens, Token};
#[test]
@@ -187,8 +430,117 @@ mod test {
assert_eq!(secret, exp);
}
macro_rules! retr_test {
($name: ident, $func: ident, $size: expr, $exp_kind: expr) => {
#[test]
fn $name() {
let secret_value = vec![0x11; $size];
let name = "test retr secret".to_string();
let secret = GuestSecret::$func(&name, secret_value.clone().into()).unwrap();
let exp_id = [
0x61, 0x2c, 0xd6, 0x3e, 0xa8, 0xf2, 0xc1, 0x15, 0xc1, 0xe, 0x15, 0xb8, 0x8a,
0x90, 0x16, 0xc1, 0x55, 0xef, 0x9c, 0x7c, 0x2c, 0x8e, 0x56, 0xd0, 0x78, 0x4c,
0x8a, 0x1d, 0xc9, 0x3a, 0x80, 0xba,
];
let exp = GuestSecret::Retrievable {
kind: $exp_kind,
name,
id: exp_id.into(),
secret: secret_value.into(),
};
assert_eq!(exp, secret);
}
};
}
retr_test!(retr_aes_128, aes, 16, Aes(AesSizes::Bits128));
retr_test!(retr_aes_192, aes, 24, Aes(AesSizes::Bits192));
retr_test!(retr_aes_256, aes, 32, Aes(AesSizes::Bits256));
retr_test!(retr_aes_xts_128, aes_xts, 32, AesXts(AesXtsSizes::Bits128));
retr_test!(retr_aes_xts_256, aes_xts, 64, AesXts(AesXtsSizes::Bits256));
retr_test!(retr_aes_hmac_256, hmac_sha, 64, HmacSha(HmacSizes::Sha256));
retr_test!(retr_aes_hmac_512, hmac_sha, 128, HmacSha(HmacSizes::Sha512));
#[test]
fn ap_asc_parse() {
fn plaintext_no_pad() {
let key = vec![0, 14, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7];
let name = "PLAINTEXT_PAD".to_string();
let secret = GuestSecret::plaintext(&name, key[2..].to_vec().into()).unwrap();
let exp_id = [
15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
];
let exp = GuestSecret::Retrievable {
kind: PlainText,
name,
id: exp_id.into(),
secret: key.into(),
};
assert_eq!(secret, exp);
}
#[test]
fn plaintext_pad() {
let key = vec![0, 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0];
let name = "PLAINTEXT_PAD".to_string();
let secret = GuestSecret::plaintext(&name, key[2..12].to_vec().into()).unwrap();
let exp_id = [
15, 123, 176, 210, 135, 231, 220, 232, 148, 93, 198, 195, 165, 212, 214, 129, 45, 1,
94, 11, 167, 18, 151, 15, 120, 254, 13, 109, 173, 186, 37, 74,
];
let exp = GuestSecret::Retrievable {
kind: PlainText,
name,
id: exp_id.into(),
secret: key.into(),
};
assert_eq!(secret, exp);
}
#[track_caller]
fn gen_ec(nid: Nid) -> PKey<Private> {
let group = EcGroup::from_curve_name(nid).unwrap();
let key = EcKey::generate(&group).unwrap();
PKey::from_ec_key(key).unwrap()
}
#[track_caller]
fn test_ec(grp: Nid, exp_kind: EcCurves, exp_len: usize) {
let key = match grp {
NID_ED25519 => PKey::generate_ed25519().unwrap(),
NID_ED448 => PKey::generate_ed448().unwrap(),
nid => gen_ec(nid),
};
let (kind, key) = ec(key).unwrap();
assert_eq!(kind, Ec(exp_kind));
assert_eq!(key.value().len(), exp_len);
}
#[test]
fn retr_ec() {
test_ec(Nid::X9_62_PRIME256V1, EcCurves::Secp256R1, 32);
test_ec(Nid::SECP384R1, EcCurves::Secp384R1, 48);
test_ec(Nid::SECP521R1, EcCurves::Secp521R1, 80);
test_ec(NID_ED25519, EcCurves::Ed25519, 32);
test_ec(NID_ED448, EcCurves::Ed448, 64);
}
#[test]
fn retr_ec_pad() {
let pkey = PKey::generate_ed448().unwrap();
let (_, key) = ec(pkey).unwrap();
assert_eq!(key.value()[..7], [0; 7]);
let pkey = gen_ec(Nid::SECP521R1);
let (_, key) = ec(pkey).unwrap();
assert_eq!(key.value()[..14], [0; 14]);
}
#[test]
fn asc_parse() {
let id = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
@@ -217,6 +569,39 @@ mod test {
);
}
#[test]
fn retrievable_parse() {
let id = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab,
0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
];
let asc = GuestSecret::Retrievable {
kind: PlainText,
name: "test123".to_string(),
id: id.into(),
secret: vec![].into(),
};
assert_tokens(
&asc,
&[
Token::StructVariant {
name: "GuestSecret",
variant: "Retrievable",
len: 3,
},
Token::String("kind"),
Token::String("3 (PLAINTEXT)"),
Token::String("name"),
Token::String("test123"),
Token::String("id"),
Token::String("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
Token::StructVariantEnd,
],
);
}
#[test]
fn guest_secret_bin_null() {
let gs = GuestSecret::Null;
@@ -228,7 +613,7 @@ mod test {
}
#[test]
fn guest_secret_bin_ap() {
fn guest_secret_bin_asoc() {
let gs = GuestSecret::Association {
name: "test".to_string(),
id: [1; 32].into(),
@@ -241,4 +626,21 @@ mod test {
assert_eq!(exp, gs_bytes_auth.get());
assert_eq!(&[2; 32], gs.confidential());
}
#[test]
fn guest_secret_bin_retr() {
let gs = GuestSecret::Retrievable {
kind: PlainText,
name: "test".to_string(),
id: [1; 32].into(),
secret: vec![2; 32].into(),
};
let auth = gs.auth();
let gs_bytes_auth = auth.get();
let mut exp = vec![0u8, 0, 0, 3, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, 0, 0, 0];
exp.extend([1; 32]);
assert_eq!(exp, gs_bytes_auth);
assert_eq!(&[2; 32], gs.confidential());
}
}

View File

@@ -0,0 +1,234 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use crate::{pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
use byteorder::BigEndian;
use log::warn;
use pv_core::{
request::Confidential,
uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
};
use zerocopy::{FromBytes, U16};
/// An IBM Protected Key
///
/// A protected key, writeable as pem.
///
/// Will convert into PEM as:
/// ```PEM
///-----BEGIN IBM PROTECTED KEY-----
///kind: <name>
///
///<protected key in base64>
///-----END IBM PROTECTED KEY-----
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct IbmProtectedKey {
kind: ListableSecretType,
key: Confidential<Vec<u8>>,
}
impl IbmProtectedKey {
/// Get the binary representation of the key.
pub fn data(&self) -> &[u8] {
self.key.value()
}
/// Converts a [`IbmProtectedKey`] into a vector.
pub fn into_bytes(self) -> Confidential<Vec<u8>> {
self.key
}
/// Get the data in PEM format.
///
/// # Errors
///
/// This function will return an error if the PEM conversion failed (very unlikely).
pub fn to_pem(&self) -> Result<Pem> {
Pem::new(
"IBM PROTECTED KEY",
format!("kind: {}", self.kind),
self.key.value(),
)
}
fn new<K>(kind: ListableSecretType, key: K) -> Self
where
K: Into<Confidential<Vec<u8>>>,
{
Self {
kind,
key: key.into(),
}
}
}
impl From<RetrieveCmd> for RetrievedSecret {
fn from(value: RetrieveCmd) -> Self {
let kind = value.meta_data().stype();
let key = value.into_key();
match kind {
ListableSecretType::Retrievable(RetrievableSecret::PlainText) => {
// Will not run into default, retrieve has a granularity of 16 bytes and 16 bytes is the
// minimum size
let len = U16::<BigEndian>::read_from_prefix(key.value())
.unwrap_or_default()
.get() as usize;
// Test if the plain text secret has a size:
// 1. len <= 8190
// 2. first two bytes are max 15 less than buffer-size+2
// 3. bytes after len + 2 are zero
match len <= MAX_SIZE_PLAIN_PAYLOAD
&& key.value().len() - (len + 2) < 15
&& key.value()[len + 2..].iter().all(|c| *c == 0)
{
false => Self::Plaintext(key),
true => Self::Plaintext(key.value()[2..len + 2].to_vec().into()),
}
}
kind => {
match kind {
ListableSecretType::Retrievable(_) => (),
_ => warn!("Retrieved an unretrievable Secret! Will continue; interpreting it as a protected key."),
}
Self::ProtectedKey(IbmProtectedKey::new(kind, key))
}
}
}
}
/// A retrieved Secret.
#[derive(Debug, PartialEq, Eq)]
pub enum RetrievedSecret {
/// A plaintext secret
Plaintext(Confidential<Vec<u8>>),
/// An [`IbmProtectedKey`]
ProtectedKey(IbmProtectedKey),
}
impl RetrievedSecret {
/// Create a new IBM PROTECTED KEY object
pub fn from_cmd(cmd: RetrieveCmd) -> Self {
cmd.into()
}
/// Get the binary representation of the key.
pub fn data(&self) -> &[u8] {
match self {
RetrievedSecret::Plaintext(p) => p.value(),
RetrievedSecret::ProtectedKey(p) => p.data(),
}
}
/// Converts a [`IbmProtectedKey`] into a vector.
pub fn into_bytes(self) -> Confidential<Vec<u8>> {
match self {
RetrievedSecret::Plaintext(p) => p,
RetrievedSecret::ProtectedKey(p) => p.into_bytes(),
}
}
/// Get the data in PEM format.
///
/// # Errors
///
/// This function will return an error if the PEM conversion failed (very unlikely).
pub fn to_pem(&self) -> Result<Pem> {
match self {
RetrievedSecret::Plaintext(p) => Pem::new("PLAINTEXT SECRET", None, p.value()),
RetrievedSecret::ProtectedKey(p) => p.to_pem(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use pv_core::uv::*;
fn mk_retr(secret: &[u8]) -> RetrievedSecret {
let entry = SecretEntry::new(
0,
ListableSecretType::Retrievable(RetrievableSecret::PlainText),
SecretId::default(),
secret.len() as u32,
);
let mut cmd = RetrieveCmd::from_entry(entry).unwrap();
cmd.data().unwrap().copy_from_slice(secret);
RetrievedSecret::from_cmd(cmd)
}
#[test]
fn from_retr_cmd() {
let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0];
let prot_key = mk_retr(&secret);
let exp = RetrievedSecret::Plaintext(secret[2..12].to_vec().into());
assert_eq!(prot_key, exp);
}
#[test]
fn from_retr_inv_size() {
let secret = vec![0x20; 32];
let prot_key = mk_retr(&secret);
let exp = RetrievedSecret::Plaintext(secret.into());
assert_eq!(prot_key, exp);
}
#[test]
fn from_retr_inv_no_zero_after_end() {
let secret = vec![0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 1, 0, 0, 0];
let prot_key = mk_retr(&secret);
let exp = RetrievedSecret::Plaintext(secret.into());
assert_eq!(prot_key, exp);
}
#[test]
fn from_retr_inv_to_much_padding() {
let secret = vec![
0, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0,
];
let prot_key = mk_retr(&secret);
let exp = RetrievedSecret::Plaintext(secret.into());
assert_eq!(prot_key, exp);
}
#[test]
fn from_retr_0_size() {
let secret = vec![0x00; 32];
let prot_key = mk_retr(&secret);
let exp = RetrievedSecret::Plaintext(secret.into());
assert_eq!(prot_key, exp);
}
#[test]
fn plain_text_pem() {
let exp = "\
-----BEGIN PLAINTEXT SECRET-----\n\
ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
-----END PLAINTEXT SECRET-----\n";
let prot = RetrievedSecret::Plaintext(vec![17; 48].into());
let pem = prot.to_pem().unwrap();
let pem_str = pem.to_string();
assert_eq!(pem_str, exp);
}
#[test]
fn prot_key_pem() {
let exp = "\
-----BEGIN IBM PROTECTED KEY-----\n\
kind: AES-128-KEY\n\n\
ERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER\n\
-----END IBM PROTECTED KEY-----\n";
let prot = IbmProtectedKey::new(
ListableSecretType::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
vec![17; 48],
);
let pem = prot.to_pem().unwrap();
let pem_str = pem.to_string();
assert_eq!(pem_str, exp);
}
}

View File

@@ -16,13 +16,13 @@ readme = "README.md"
workspace = true
[dependencies]
libc = "0.2.49"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
thiserror = "1.0.33"
libc = "0.2.169"
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
thiserror = "2.0.11"
zerocopy = {version = "0.7", features = ["derive"]}
serde = { version = "1.0.139", features = ["derive"]}
byteorder = "1.3"
serde = { version = "1.0.217", features = ["derive"]}
byteorder = "1.5"
[dev-dependencies]
serde_test = "1.0.139"
lazy_static = "1.1"
serde_test = "1.0.177"
lazy_static = "1.5"

View File

@@ -4,6 +4,8 @@
use std::path::PathBuf;
use crate::uv::SecretId;
/// Result type for this crate
pub type Result<T, E = Error> = std::result::Result<T, E>;
@@ -70,6 +72,11 @@ pub enum Error {
#[error("The attestation request does not specify a measurement size or measurement data.")]
BinArcbNoMeasurement,
#[error(
"The secret with the ID {id} cannot be retrieved. The requested size is too large ({size})"
)]
InvalidRetrievableSecretType { id: SecretId, size: usize },
// errors from other crates
#[error(transparent)]
Io(#[from] std::io::Error),

View File

@@ -32,7 +32,9 @@ pub mod misc {
/// [`crate::uv::UvCmd`]
pub mod uv {
pub use crate::uvdevice::attest::AttestationCmd;
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd};
pub use crate::uvdevice::retr_secret::RetrievableSecret;
pub use crate::uvdevice::retr_secret::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes};
pub use crate::uvdevice::secret::{AddCmd, ListCmd, LockCmd, RetrieveCmd};
pub use crate::uvdevice::secret_list::{ListableSecretType, SecretEntry, SecretId, SecretList};
pub use crate::uvdevice::{ConfigUid, UvCmd, UvDevice, UvDeviceInfo, UvFlags, UvcSuccess};
}

View File

@@ -25,6 +25,7 @@ mod info;
mod test;
pub(crate) use ffi::uv_ioctl;
pub mod attest;
pub mod retr_secret;
pub mod secret;
pub mod secret_list;
@@ -58,11 +59,13 @@ fn ioctl_raw(raw_fd: RawFd, cmd: c_ulong, cb: &mut IoctlCb) -> Result<()> {
rc = ioctl(raw_fd, cmd, cb.as_ptr_mut());
}
// NOTE io::Error handles all errnos ioctl uses
let errno = std::io::Error::last_os_error();
debug!("ioctl resulted with {cb:?}");
match rc {
0 => Ok(()),
// NOTE io::Error handles all errnos ioctl uses
_ => Err(std::io::Error::last_os_error().into()),
_ => Err(errno.into()),
}
}
@@ -73,7 +76,7 @@ fn rc_fmt<C: UvCmd>(rc: u16, rrc: u16, cmd: &mut C) -> &'static str {
(0x0002, _) => Some("invalid UV command"),
(0x0005, _) => Some("request has an invalid size"),
(0x0030, _) => Some("home address space control bit has R-bit set to one. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0031, _) => Some("adress translation exception. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0031, _) => Some("address translation exception. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(0x0032, _) => Some("request contains virtual address translating to an invalid address. This may be due to a corrupted host or a guest kernel bug. STOP using this guest"),
(UvDevice::RC_MORE_DATA, _) => unreachable!("This is no Error!!!!"),
(UvDevice::RC_SUCCESS, _) => unreachable!("This is no Error!!!!"),
@@ -162,6 +165,16 @@ pub enum UvcSuccess {
RC_MORE_DATA = UvDevice::RC_MORE_DATA,
}
impl UvcSuccess {
/// Returns true if there is more data available
pub fn more_data(&self) -> bool {
match self {
Self::RC_SUCCESS => false,
Self::RC_MORE_DATA => true,
}
}
}
/// The `UvDevice` is a (virtual) device on s390 machines to send Ultravisor commands(UVCs) from
/// userspace.
///

View File

@@ -2,6 +2,13 @@
//
// Copyright IBM Corp. 2023
// This file is a rustified copy of linux/arch/s390/include/uapi/asm/uvdevice.h
// There might be things that are not needed here but nontheless defined in that header.
// Those two files should be in sync -> there might be unused/dead code.
//
// The `UVIO_IOCTL_*` and `UVIO_SUPP_*` macros
#![allow(dead_code)]
use std::mem::size_of;
use crate::{assert_size, static_assert};
@@ -11,9 +18,8 @@ pub const UVIO_ATT_ARCB_MAX_LEN: usize = 0x100000;
pub const UVIO_ATT_MEASUREMENT_MAX_LEN: usize = 0x8000;
pub const UVIO_ATT_ADDITIONAL_MAX_LEN: usize = 0x8000;
pub const UVIO_ADD_SECRET_MAX_LEN: usize = 0x100000;
#[allow(unused)]
// here for completeness
pub const UVIO_LIST_SECRETS_LEN: usize = 0x1000;
pub const UVIO_RETR_SECRET_MAX_LEN: usize = 0x2000;
// equal to ascii 'u'
pub const UVIO_TYPE_UVC: u8 = 117u8;
@@ -23,6 +29,7 @@ pub const UVIO_IOCTL_ATT_NR: u8 = 1;
pub const UVIO_IOCTL_ADD_SECRET_NR: u8 = 2;
pub const UVIO_IOCTL_LIST_SECRETS_NR: u8 = 3;
pub const UVIO_IOCTL_LOCK_SECRETS_NR: u8 = 4;
pub const UVIO_IOCTL_RETR_SECRET_NR: u8 = 5;
/// Uvdevice IOCTL control block
/// Programs can use this struct to communicate with the uvdevice via IOCTLs

View File

@@ -0,0 +1,392 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use crate::uv::{ListableSecretType, RetrieveCmd};
use serde::{Deserialize, Serialize, Serializer};
use std::fmt::Display;
/// Allowed sizes for AES keys
#[non_exhaustive]
#[derive(PartialEq, Eq, Debug)]
pub enum AesSizes {
/// 128 bit key
Bits128,
/// 192 bit key
Bits192,
/// 256 bit key
Bits256,
}
impl AesSizes {
/// Construct the key-size from the bit-size.
///
/// Returns [`None`] if the bit-size is not supported.
pub fn from_bits(bits: u32) -> Option<Self> {
match bits {
128 => Some(Self::Bits128),
192 => Some(Self::Bits192),
256 => Some(Self::Bits256),
_ => None,
}
}
/// Returns the bit-size for the key-type
const fn bit_size(&self) -> u32 {
match self {
Self::Bits128 => 128,
Self::Bits192 => 192,
Self::Bits256 => 256,
}
}
}
impl Display for AesSizes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.bit_size())
}
}
/// Allowed sizes for AES-XTS keys
#[non_exhaustive]
#[derive(PartialEq, Eq, Debug)]
pub enum AesXtsSizes {
/// Two AES 128 bit keys
Bits128,
/// Two AES 256 bit keys
Bits256,
}
impl AesXtsSizes {
/// Construct the key-size from the bit-size.
///
/// It's a key containing two keys; bit-size is half the number of bits it has
/// Returns [`None`] if the bit-size is not supported.
pub fn from_bits(bits: u32) -> Option<Self> {
match bits {
128 => Some(Self::Bits128),
256 => Some(Self::Bits256),
_ => None,
}
}
/// Returns the bit-size for the key-type
///
/// It's a key containing two keys: bit-size is half the number of bits it has
const fn bit_size(&self) -> u32 {
match self {
Self::Bits128 => 128,
Self::Bits256 => 256,
}
}
}
impl Display for AesXtsSizes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.bit_size())
}
}
/// Allowed sizes for HMAC-SHA keys
#[non_exhaustive]
#[derive(PartialEq, Eq, Debug)]
pub enum HmacShaSizes {
/// SHA 256 bit
Sha256,
/// SHA 512 bit
Sha512,
}
impl HmacShaSizes {
/// Construct the key-size from the sha-size.
///
/// FW expects maximum resistance keys (double the SHA size).
/// The `sha_size` is half of the number of bits in the key
/// Returns [`None`] if the `sha_size` is not supported.
pub fn from_sha_size(sha_size: u32) -> Option<Self> {
match sha_size {
256 => Some(Self::Sha256),
512 => Some(Self::Sha512),
_ => None,
}
}
/// Returns the sha-size for the key-type
///
/// FW expects maximum resistance keys (double the SHA size).
/// The `sha_size` is half of the number of bits in the key
const fn sha_size(&self) -> u32 {
match self {
Self::Sha256 => 256,
Self::Sha512 => 512,
}
}
}
impl Display for HmacShaSizes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.sha_size())
}
}
/// Allowed curves for EC private keys
#[non_exhaustive]
#[derive(PartialEq, Eq, Debug)]
pub enum EcCurves {
/// secp256r1 or prime256v1 curve
Secp256R1,
/// secp384p1 curve
Secp384R1,
/// secp521r1 curve
Secp521R1,
/// ed25519 curve
Ed25519,
/// ed448 curve
Ed448,
}
impl EcCurves {
/// Returns the expected key-byte-size for this curve.
pub const fn exp_key_size(&self) -> usize {
match self {
Self::Secp256R1 => 32,
Self::Secp384R1 => 48,
Self::Secp521R1 => 80,
Self::Ed25519 => 32,
Self::Ed448 => 64,
}
}
}
// The names have to stay constant, otherwise the PEM contains invalid types
impl Display for EcCurves {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Secp256R1 => write!(f, "SECP256R1"),
Self::Secp384R1 => write!(f, "SECP384R1"),
Self::Secp521R1 => write!(f, "SECP521R1"),
Self::Ed25519 => write!(f, "ED25519"),
Self::Ed448 => write!(f, "ED448"),
}
}
}
/// Retrievable Secret types
#[non_exhaustive]
#[derive(PartialEq, Eq, Debug)]
pub enum RetrievableSecret {
/// Plain-text secret
PlainText,
/// Protected AES key
Aes(AesSizes),
/// Protected AES-XTS key
AesXts(AesXtsSizes),
/// Protected HMAC-SHA key
HmacSha(HmacShaSizes),
/// Protected EC-private key
Ec(EcCurves),
}
// The names have to stay constant, otherwise the PEM contains invalid/unknown types
impl Display for RetrievableSecret {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Alternate representation: Omit sizes/curves
if f.alternate() {
match self {
Self::PlainText => write!(f, "PLAINTEXT"),
Self::Aes(_) => write!(f, "AES-KEY"),
Self::AesXts(_) => write!(f, "AES-XTS-KEY"),
Self::HmacSha(_) => write!(f, "HMAC-SHA-KEY"),
Self::Ec(_) => write!(f, "EC-PRIVATE-KEY"),
}
} else {
match self {
Self::PlainText => write!(f, "PLAINTEXT"),
Self::Aes(s) => write!(f, "AES-{s}-KEY"),
Self::AesXts(s) => write!(f, "AES-XTS-{s}-KEY"),
Self::HmacSha(s) => write!(f, "HMAC-SHA-{s}-KEY"),
Self::Ec(c) => write!(f, "EC-{c}-PRIVATE-KEY"),
}
}
}
}
impl RetrievableSecret {
/// Report expected input types
pub fn expected(&self) -> String {
match self {
Self::PlainText => format!("less than {}", RetrieveCmd::MAX_SIZE),
Self::Aes(_) => "128, 192, or 256".to_string(),
Self::AesXts(_) => "128 or 256".to_string(),
Self::HmacSha(_) => "256 or 512".to_string(),
Self::Ec(_) => "secp256r1, secp384r1, secp521r1, ed25519, or ed448".to_string(),
}
}
}
impl From<&RetrievableSecret> for u16 {
fn from(value: &RetrievableSecret) -> Self {
match value {
RetrievableSecret::PlainText => ListableSecretType::PLAINTEXT,
RetrievableSecret::Aes(AesSizes::Bits128) => ListableSecretType::AES_128_KEY,
RetrievableSecret::Aes(AesSizes::Bits192) => ListableSecretType::AES_192_KEY,
RetrievableSecret::Aes(AesSizes::Bits256) => ListableSecretType::AES_256_KEY,
RetrievableSecret::AesXts(AesXtsSizes::Bits128) => ListableSecretType::AES_128_XTS_KEY,
RetrievableSecret::AesXts(AesXtsSizes::Bits256) => ListableSecretType::AES_256_XTS_KEY,
RetrievableSecret::HmacSha(HmacShaSizes::Sha256) => {
ListableSecretType::HMAC_SHA_256_KEY
}
RetrievableSecret::HmacSha(HmacShaSizes::Sha512) => {
ListableSecretType::HMAC_SHA_512_KEY
}
RetrievableSecret::Ec(EcCurves::Secp256R1) => ListableSecretType::ECDSA_P256_KEY,
RetrievableSecret::Ec(EcCurves::Secp384R1) => ListableSecretType::ECDSA_P384_KEY,
RetrievableSecret::Ec(EcCurves::Secp521R1) => ListableSecretType::ECDSA_P521_KEY,
RetrievableSecret::Ec(EcCurves::Ed25519) => ListableSecretType::ECDSA_ED25519_KEY,
RetrievableSecret::Ec(EcCurves::Ed448) => ListableSecretType::ECDSA_ED448_KEY,
}
}
}
// serializes to: <secret type nb> (String name)
impl Serialize for RetrievableSecret {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let id: u16 = self.into();
serializer.serialize_str(&format!("{id} ({self})"))
}
}
/// deserializes from the secret type nb only
impl<'de> Deserialize<'de> for RetrievableSecret {
fn deserialize<D>(de: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct RetrSecretVisitor;
impl<'de> serde::de::Visitor<'de> for RetrSecretVisitor {
type Value = RetrievableSecret;
fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt.write_str(
"a retrievable secret type: `<number> (String name)` number in [3,10]|[17,21]",
)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let (n, _) = s.split_once(' ').ok_or(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(s),
&self,
))?;
let id: u16 = n.parse().map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(n), &self)
})?;
let listable: ListableSecretType = id.into();
match listable {
ListableSecretType::Retrievable(r) => Ok(r),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Unsigned(id.into()),
&self,
)),
}
}
}
de.deserialize_str(RetrSecretVisitor)
}
}
#[cfg(test)]
mod test {
use serde_test::{assert_tokens, Token};
use super::*;
#[test]
fn retr_serde_plain() {
let retr = RetrievableSecret::PlainText;
assert_tokens(&retr, &[Token::Str("3 (PLAINTEXT)")]);
}
#[test]
fn retr_serde_aes() {
let retr = RetrievableSecret::Aes(AesSizes::Bits192);
assert_tokens(&retr, &[Token::Str("5 (AES-192-KEY)")]);
}
#[test]
fn retr_serde_aes_xts() {
let retr = RetrievableSecret::AesXts(AesXtsSizes::Bits128);
assert_tokens(&retr, &[Token::Str("7 (AES-XTS-128-KEY)")]);
}
#[test]
fn retr_serde_hmac() {
let retr = RetrievableSecret::HmacSha(HmacShaSizes::Sha256);
assert_tokens(&retr, &[Token::Str("9 (HMAC-SHA-256-KEY)")]);
}
#[test]
fn retr_serde_es() {
let retr = RetrievableSecret::Ec(EcCurves::Secp521R1);
assert_tokens(&retr, &[Token::Str("19 (EC-SECP521R1-PRIVATE-KEY)")]);
}
// Ensure that the string representation of the retrievable types stay constant, or PEM will have
// different, incompatible types
#[test]
fn stable_type_names() {
assert_eq!("PLAINTEXT", RetrievableSecret::PlainText.to_string());
assert_eq!(
"AES-128-KEY",
RetrievableSecret::Aes(AesSizes::Bits128).to_string()
);
assert_eq!(
"AES-192-KEY",
RetrievableSecret::Aes(AesSizes::Bits192).to_string()
);
assert_eq!(
"AES-256-KEY",
RetrievableSecret::Aes(AesSizes::Bits256).to_string()
);
assert_eq!(
"AES-XTS-128-KEY",
RetrievableSecret::AesXts(AesXtsSizes::Bits128).to_string()
);
assert_eq!(
"AES-XTS-256-KEY",
RetrievableSecret::AesXts(AesXtsSizes::Bits256).to_string()
);
assert_eq!(
"HMAC-SHA-256-KEY",
RetrievableSecret::HmacSha(HmacShaSizes::Sha256).to_string()
);
assert_eq!(
"HMAC-SHA-512-KEY",
RetrievableSecret::HmacSha(HmacShaSizes::Sha512).to_string()
);
assert_eq!(
"EC-SECP256R1-PRIVATE-KEY",
RetrievableSecret::Ec(EcCurves::Secp256R1).to_string()
);
assert_eq!(
"EC-SECP384R1-PRIVATE-KEY",
RetrievableSecret::Ec(EcCurves::Secp384R1).to_string()
);
assert_eq!(
"EC-SECP521R1-PRIVATE-KEY",
RetrievableSecret::Ec(EcCurves::Secp521R1).to_string()
);
assert_eq!(
"EC-ED25519-PRIVATE-KEY",
RetrievableSecret::Ec(EcCurves::Ed25519).to_string()
);
assert_eq!(
"EC-ED448-PRIVATE-KEY",
RetrievableSecret::Ec(EcCurves::Ed448).to_string()
);
}
}

View File

@@ -3,8 +3,15 @@
// Copyright IBM Corp. 2023
use super::ffi;
use crate::{request::MagicValue, uv::UvCmd, uvsecret::AddSecretMagic, Error, Result, PAGESIZE};
use std::io::Read;
use crate::{
request::{Confidential, MagicValue},
uv::{SecretEntry, UvCmd},
uvsecret::AddSecretMagic,
Error, Result, PAGESIZE,
};
use log::debug;
use std::{io::Read, mem::size_of_val};
use zerocopy::AsBytes;
/// _List Secrets_ Ultravisor command.
///
@@ -17,6 +24,17 @@ impl ListCmd {
Self(vec![0; size])
}
/// Create a new list secrets command with `pages` capacity.
///
/// * `pages` - number pf pages to allocate for this IOCTL
///
/// # Panic
/// This function will trigger a panic if the allocation size is larger than [`usize::MAX`].
/// Very likely an OOM situation occurs way before this!
pub fn with_pages(pages: usize) -> Self {
Self::with_size(pages * PAGESIZE)
}
/// Create a new list secrets command with a one page capacity
pub fn new() -> Self {
Self::with_size(PAGESIZE)
@@ -116,3 +134,89 @@ impl UvCmd for LockCmd {
}
}
}
/// Retrieve a secret value from UV store
#[derive(Debug)]
pub struct RetrieveCmd {
entry: SecretEntry,
key: Confidential<Vec<u8>>,
}
impl RetrieveCmd {
/// Maximum size of a retrieved key (=2 pages)
pub const MAX_SIZE: usize = ffi::UVIO_RETR_SECRET_MAX_LEN;
/// Create a retrieve-secret UVC from a [`SecretEntry`].
///
/// This uses the index of the secret entry for the UVC.
pub fn from_entry(entry: SecretEntry) -> Result<Self> {
entry.try_into()
}
/// Transform a [`RetrieveCmd`] into a key-vector.
///
/// Only makes sense to call after a successful UVC execution.
pub fn into_key(self) -> Confidential<Vec<u8>> {
self.key
}
/// Get the secret entry
///
/// Get the secret entry that is used as metadata to retrieve the secret
pub fn meta_data(&self) -> &SecretEntry {
&self.entry
}
}
impl TryFrom<SecretEntry> for RetrieveCmd {
type Error = Error;
fn try_from(entry: SecretEntry) -> Result<Self> {
let len = entry.secret_size() as usize;
// Next to impossible if the secret entry is a valid response from UV
if len > Self::MAX_SIZE {
return Err(Error::InvalidRetrievableSecretType {
id: entry.secret_id().to_owned(),
size: len,
});
}
// Ensure that an u16 fits into the buffer.
let size = std::cmp::max(size_of_val(&entry.index()), len);
debug!("Create a buf with {} elements", size);
let mut buf = vec![0; size];
// The IOCTL expects the secret index in the first two bytes of the buffer. They will be
// overwritten in the response
entry.index_be().write_to_prefix(&mut buf).unwrap();
Ok(Self {
entry,
key: buf.into(),
})
}
}
impl UvCmd for RetrieveCmd {
const UV_IOCTL_NR: u8 = ffi::UVIO_IOCTL_RETR_SECRET_NR;
fn rc_fmt(&self, rc: u16, _: u16) -> Option<&'static str> {
match rc {
// should not appear (TM), software creates request from a list item
0x0009 => Some("the allocated buffer is to small to store the secret"),
// should not appear (TM), kernel allocates the memory
0x0102 => {
Some("access exception recognized when accessing retrieved secret storage area")
}
// should not appear (TM), software creates request from a list item
0x010f => Some("the Secret Store is empty"),
// should not appear (TM), software creates request from a list item
0x0110 => Some("the Secret Store does not contain a secret with the specified index"),
0x0111 => Some("the secret is not retrievable"),
_ => None,
}
}
fn data(&mut self) -> Option<&mut [u8]> {
Some(self.key.value_mut())
}
}

View File

@@ -2,13 +2,20 @@
//
// Copyright IBM Corp. 2024
use crate::assert_size;
use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::{
assert_size,
uv::{AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListCmd, RetrievableSecret},
uvdevice::UvCmd,
Error, Result,
};
use byteorder::{BigEndian, ByteOrder};
use serde::{Deserialize, Serialize, Serializer};
use std::{
fmt::Display,
cmp::min,
ffi::CStr,
fmt::{Debug, Display, LowerHex, UpperHex},
io::{Cursor, Read, Seek, Write},
mem::size_of,
slice::Iter,
vec::IntoIter,
};
@@ -18,7 +25,7 @@ use zerocopy::{AsBytes, FromBytes, FromZeroes, U16, U32};
///
/// (de)serializes itself in/from a hex-string
#[repr(C)]
#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone)]
#[derive(PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Debug, Clone, Default)]
pub struct SecretId([u8; Self::ID_SIZE]);
assert_size!(SecretId, SecretId::ID_SIZE);
@@ -26,10 +33,37 @@ impl SecretId {
/// Size in bytes of the [`SecretId`]
pub const ID_SIZE: usize = 32;
/// Create a [`SecretId`] forom a buffer.
/// Create a [`SecretId`] from a buffer.
pub fn from(buf: [u8; Self::ID_SIZE]) -> Self {
buf.into()
}
/// Create a Id from a string
///
/// Uses the first 31 bytes from `name` as id
/// Does not hash anything. Byte 32 is the NUL char
pub fn from_string(name: &str) -> Self {
let len = min(name.len(), Self::ID_SIZE - 1);
let mut res = Self::default();
res.0[0..len].copy_from_slice(&name.as_bytes()[0..len]);
res
}
/// Tries to represent the Id as printable-ASCII string
pub fn as_ascii(&self) -> Option<&str> {
if let Ok(t) = CStr::from_bytes_until_nul(&self.0) {
if let Ok(t) = t.to_str() {
if !t.is_empty()
&& t.chars()
.all(|c| c.is_ascii_whitespace() | c.is_ascii_graphic())
&& self.0[t.len()..].iter().all(|b| *b == 0)
{
return Some(t);
}
}
};
None
}
}
impl Serialize for SecretId {
@@ -37,8 +71,8 @@ impl Serialize for SecretId {
where
S: Serializer,
{
// calls Display at one point
ser.serialize_str(&self.to_string())
// calls LowerHex at one point
ser.serialize_str(&format!("{self:#x}"))
}
}
@@ -51,12 +85,36 @@ impl<'de> Deserialize<'de> for SecretId {
}
}
impl UpperHex for SecretId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "0x")?;
}
for b in self.0 {
write!(f, "{b:02X}")?;
}
Ok(())
}
}
impl LowerHex for SecretId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() {
write!(f, "0x")?;
}
for b in self.0 {
write!(f, "{b:02x}")?;
}
Ok(())
}
}
impl Display for SecretId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut s = String::with_capacity(32 * 2 + 2);
s.push_str("0x");
let s = self.0.iter().fold(s, |acc, e| acc + &format!("{e:02x}"));
write!(f, "{s}")
if let Some(s) = self.as_ascii() {
write!(f, "{s} | ")?;
}
write!(f, "{self:#x}")
}
}
@@ -74,7 +132,7 @@ impl AsRef<[u8]> for SecretId {
/// A secret in a [`SecretList`]
#[repr(C)]
#[derive(Debug, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
#[derive(Debug, Clone, PartialEq, Eq, AsBytes, FromZeroes, FromBytes, Serialize)]
pub struct SecretEntry {
#[serde(serialize_with = "ser_u16")]
index: U16<BigEndian>,
@@ -94,11 +152,11 @@ impl SecretEntry {
/// Create a new entry for a [`SecretList`].
///
/// The content of this entry will very likely not represent the status of the guest in the
/// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
/// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encouraged.
pub fn new(index: u16, stype: ListableSecretType, id: SecretId, secret_len: u32) -> Self {
Self {
index: index.into(),
stype: stype.into(),
stype: U16::new(stype.into()),
len: secret_len.into(),
res_8: 0,
id,
@@ -110,9 +168,14 @@ impl SecretEntry {
self.index.get()
}
/// Returns the secret type of this [`SecretEntry`].
/// Returns the index of this [`SecretEntry`] in BE.
pub(crate) fn index_be(&self) -> &U16<BigEndian> {
&self.index
}
/// Returns the secret type of this [`SecretEntry`]
pub fn stype(&self) -> ListableSecretType {
self.stype.into()
self.stype.get().into()
}
/// Returns a reference to the id of this [`SecretEntry`].
@@ -127,26 +190,65 @@ impl SecretEntry {
pub fn id(&self) -> &[u8] {
self.id.as_ref()
}
/// Get the id as [`SecretId`] reference
pub(crate) fn secret_id(&self) -> &SecretId {
&self.id
}
/// Returns the secret size of this [`SecretEntry`].
pub fn secret_size(&self) -> u32 {
self.len.get()
}
}
impl Display for SecretEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let stype: ListableSecretType = self.stype.into();
let stype: ListableSecretType = self.stype.get().into();
writeln!(f, "{} {}:", self.index, stype)?;
write!(f, " ")?;
for b in self.id.as_ref() {
write!(f, "{b:02x}")?;
}
Ok(())
write!(f, " {}", self.id)
}
}
#[repr(C)]
#[derive(Debug, FromBytes, AsBytes, FromZeroes, Clone, PartialEq, Eq, Default, Serialize)]
struct SecretListHdr {
#[serde(skip)]
num_secrets_stored: U16<BigEndian>,
#[serde(serialize_with = "ser_u16")]
total_num_secrets: U16<BigEndian>,
#[serde(skip)]
next_secret_idx: U16<BigEndian>,
#[serde(skip)]
reserved_06: u16,
#[serde(skip)]
reserved_08: u64,
}
impl SecretListHdr {
fn new(num_secrets_stored: u16, total_num_secrets: u16, next_secret_idx: u16) -> Self {
Self {
num_secrets_stored: num_secrets_stored.into(),
total_num_secrets: total_num_secrets.into(),
next_secret_idx: next_secret_idx.into(),
reserved_06: 0,
reserved_08: 0,
}
}
}
assert_size!(SecretListHdr, 16);
/// List of secrets used to parse the [`crate::uv::ListCmd`] result.
///
/// The list should not hold more than 0xffffffff elements
#[derive(Debug, PartialEq, Eq, Serialize)]
/// The list should ONLY be created from an UV-Call result using either:
/// - [`TryInto::try_into`] from [`ListCmd`]
/// - [`SecretList::decode`]
/// Any other ways can create invalid lists that do not represent the UV secret store.
/// The list must not hold more than [`u32::MAX`] elements
#[derive(Debug, PartialEq, Eq, Serialize, Default)]
pub struct SecretList {
total_num_secrets: usize,
#[serde(flatten)]
hdr: SecretListHdr,
secrets: Vec<SecretEntry>,
}
@@ -182,10 +284,14 @@ impl SecretList {
/// The content of this list will very likely not represent the status of the guest in the
/// Ultravisor. Use of [`SecretList::decode`] in any non-test environments is encuraged.
pub fn new(total_num_secrets: u16, secrets: Vec<SecretEntry>) -> Self {
Self {
total_num_secrets: total_num_secrets as usize,
Self::new_with_hdr(
SecretListHdr::new(total_num_secrets, total_num_secrets, 0),
secrets,
}
)
}
fn new_with_hdr(hdr: SecretListHdr, secrets: Vec<SecretEntry>) -> Self {
Self { hdr, secrets }
}
/// Returns an iterator over the slice.
@@ -209,19 +315,17 @@ impl SecretList {
///
/// This number may be not equal to the provided number of [`SecretEntry`]
pub fn total_num_secrets(&self) -> usize {
self.total_num_secrets
self.hdr.total_num_secrets.get() as usize
}
/// Find the first [`SecretEntry`] that has the provided [`SecretId`]
pub fn find(&self, id: &SecretId) -> Option<SecretEntry> {
self.iter().find(|e| e.id() == id.as_ref()).cloned()
}
/// Encodes the list in the same binary format the UV would do
pub fn encode<T: Write>(&self, w: &mut T) -> Result<()> {
let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?;
w.write_u16::<BigEndian>(num_s)?;
w.write_u16::<BigEndian>(
self.total_num_secrets
.try_into()
.map_err(|_| Error::ManySecrets)?,
)?;
w.write_all(&[0u8; 12])?;
w.write_all(self.hdr.as_bytes())?;
for secret in &self.secrets {
w.write_all(secret.as_bytes())?;
}
@@ -230,19 +334,20 @@ impl SecretList {
/// Decodes the list from the binary format of the UV into this internal representation
pub fn decode<R: Read + Seek>(r: &mut R) -> std::io::Result<Self> {
let num_s = r.read_u16::<BigEndian>()?;
let total_num_secrets = r.read_u16::<BigEndian>()? as usize;
let mut v: Vec<SecretEntry> = Vec::with_capacity(num_s as usize);
r.seek(std::io::SeekFrom::Current(12))?; // skip reserved bytes
let mut buf = [0u8; size_of::<SecretListHdr>()];
r.read_exact(&mut buf)?;
let hdr = SecretListHdr::ref_from(&buf).unwrap();
let mut buf = [0u8; SecretEntry::STRUCT_SIZE];
for _ in 0..num_s {
let mut v = Vec::with_capacity(hdr.num_secrets_stored.get() as usize);
for _ in 0..hdr.num_secrets_stored.get() {
r.read_exact(&mut buf)?;
// cannot fail. buffer has the same size as the secret entry
let secr = SecretEntry::read_from(buf.as_slice()).unwrap();
v.push(secr);
}
Ok(Self {
total_num_secrets,
hdr: hdr.clone(),
secrets: v,
})
}
@@ -258,7 +363,7 @@ impl TryFrom<ListCmd> for SecretList {
impl Display for SecretList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "Total number of secrets: {}", self.total_num_secrets)?;
writeln!(f, "Total number of secrets: {}", self.total_num_secrets())?;
if !self.secrets.is_empty() {
writeln!(f)?;
}
@@ -283,51 +388,115 @@ fn ser_u16<S: Serializer>(v: &U16<BigEndian>, ser: S) -> Result<S::Ok, S::Error>
pub enum ListableSecretType {
/// Association Secret
Association,
/// Retrievable key
Retrievable(RetrievableSecret),
/// Invalid secret type, that should never appear in a list
///
/// 0 is reserved
/// 1 is Null secret, with no id and not listable
/// 1 is Null secret, with no id and not list-able
Invalid(u16),
/// Unknown secret type
Unknown(u16),
}
impl ListableSecretType {
/// UV type id for an association secret
pub const ASSOCIATION: u16 = 0x0002;
/// UV type id for a null secret
pub const NULL: u16 = 0x0001;
const RESERVED_0: u16 = 0x0000;
/// UV secret-type id for a null secret
pub const NULL: u16 = 0x0001;
/// UV secret-type id for an association secret
pub const ASSOCIATION: u16 = 0x0002;
/// UV secret-type id for a plain text secret
pub const PLAINTEXT: u16 = 0x0003;
/// UV secret-type id for an aes-128-key secret
pub const AES_128_KEY: u16 = 0x0004;
/// UV secret-type id for an aes-192-key secret
pub const AES_192_KEY: u16 = 0x0005;
/// UV secret-type id for an aes-256-key secret
pub const AES_256_KEY: u16 = 0x0006;
/// UV secret-type id for an aes-xts-128-key secret
pub const AES_128_XTS_KEY: u16 = 0x0007;
/// UV secret-type id for an aes-xts-256-key secret
pub const AES_256_XTS_KEY: u16 = 0x0008;
/// UV secret-type id for an hmac-sha-256-key secret
pub const HMAC_SHA_256_KEY: u16 = 0x0009;
/// UV secret-type id for an hmac-sha-512-key secret
pub const HMAC_SHA_512_KEY: u16 = 0x000a;
// 0x000b - 0x0010 reserved
/// UV secret-type id for an ecdsa-p256-private-key secret
pub const ECDSA_P256_KEY: u16 = 0x0011;
/// UV secret-type id for an ecdsa-p384-private-key secret
pub const ECDSA_P384_KEY: u16 = 0x0012;
/// UV secret-type id for an ecdsa-p521-private-key secret
pub const ECDSA_P521_KEY: u16 = 0x0013;
/// UV secret-type id for an ed25519-private-key secret
pub const ECDSA_ED25519_KEY: u16 = 0x0014;
/// UV secret-type id for an ed448-private-key secret
pub const ECDSA_ED448_KEY: u16 = 0x0015;
}
impl Display for ListableSecretType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Association => write!(f, "Association"),
Self::Invalid(n) => write!(f, "Invalid({n})"),
Self::Unknown(n) => write!(f, "Unknown({n})"),
Self::Invalid(n) => write!(f, "Invalid(0x{n:04x})"),
Self::Unknown(n) => write!(f, "Unknown(0x{n:04x})"),
Self::Retrievable(r) => write!(f, "{r}"),
}
}
}
impl From<U16<BigEndian>> for ListableSecretType {
fn from(value: U16<BigEndian>) -> Self {
match value.get() {
impl<O: ByteOrder> From<U16<O>> for ListableSecretType {
fn from(value: U16<O>) -> Self {
value.get().into()
}
}
impl From<u16> for ListableSecretType {
fn from(value: u16) -> Self {
match value {
Self::RESERVED_0 => Self::Invalid(Self::RESERVED_0),
Self::NULL => Self::Invalid(Self::NULL),
Self::ASSOCIATION => Self::Association,
Self::PLAINTEXT => Self::Retrievable(RetrievableSecret::PlainText),
Self::AES_128_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits128)),
Self::AES_192_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits192)),
Self::AES_256_KEY => Self::Retrievable(RetrievableSecret::Aes(AesSizes::Bits256)),
Self::AES_128_XTS_KEY => {
Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits128))
}
Self::AES_256_XTS_KEY => {
Self::Retrievable(RetrievableSecret::AesXts(AesXtsSizes::Bits256))
}
Self::HMAC_SHA_256_KEY => {
Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha256))
}
Self::HMAC_SHA_512_KEY => {
Self::Retrievable(RetrievableSecret::HmacSha(HmacShaSizes::Sha512))
}
Self::ECDSA_P256_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp256R1)),
Self::ECDSA_P384_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp384R1)),
Self::ECDSA_P521_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Secp521R1)),
Self::ECDSA_ED25519_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed25519)),
Self::ECDSA_ED448_KEY => Self::Retrievable(RetrievableSecret::Ec(EcCurves::Ed448)),
n => Self::Unknown(n),
}
}
}
impl From<ListableSecretType> for U16<BigEndian> {
impl<O: ByteOrder> From<ListableSecretType> for U16<O> {
fn from(value: ListableSecretType) -> Self {
Self::new(value.into())
}
}
impl From<ListableSecretType> for u16 {
fn from(value: ListableSecretType) -> Self {
match value {
ListableSecretType::Association => ListableSecretType::ASSOCIATION,
ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n,
ListableSecretType::Retrievable(r) => (&r).into(),
}
.into()
}
}
@@ -341,15 +510,18 @@ where
type Value = [u8; SecretId::ID_SIZE];
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a `32 bytes long hexstring` prepended with 0x")
formatter.write_str("a `32 bytes (=64 character) long hexstring` prepended with 0x")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if s.len() != SecretId::ID_SIZE * 2 + 2 {
return Err(serde::de::Error::invalid_length(s.len(), &self));
if s.len() != SecretId::ID_SIZE * 2 + "0x".len() {
return Err(serde::de::Error::invalid_length(
s.len().saturating_sub("0x".len()),
&self,
));
}
let nb = s.strip_prefix("0x").ok_or_else(|| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(s), &self)
@@ -370,7 +542,6 @@ mod test {
use super::*;
use std::io::{BufReader, BufWriter, Cursor};
#[test]
fn dump_secret_entry() {
const EXP: &[u8] = &[
@@ -398,8 +569,8 @@ mod test {
let buf = [
0x00u8, 0x01, // num secr stored
0x01, 0x12, // total num secrets
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, // reserved
0x01, 0x01, // next valid idx
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
// secret
0x00, 0x01, 0x00, 0x02, // idx + type
0x00, 0x00, 0x00, 0x20, // len
@@ -410,16 +581,16 @@ mod test {
0x00, 0x00, 0x00, 0x00,
];
let exp = SecretList {
total_num_secrets: 0x112,
secrets: vec![SecretEntry {
let exp = SecretList::new_with_hdr(
SecretListHdr::new(0x001, 0x112, 0x101),
vec![SecretEntry {
index: 1.into(),
stype: 2.into(),
len: 32.into(),
res_8: 0,
id: SecretId::from([0; 32]),
}],
};
);
let mut br = BufReader::new(Cursor::new(buf));
let sl = SecretList::decode(&mut br).unwrap();
@@ -431,8 +602,8 @@ mod test {
const EXP: &[u8] = &[
0x00, 0x01, // num secr stored
0x01, 0x12, // total num secrets
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, // reserved
0x01, 0x01, // next valid idx
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
// secret
0x00, 0x01, 0x00, 0x02, // idx + type
0x00, 0x00, 0x00, 0x20, // len
@@ -443,16 +614,16 @@ mod test {
0x00, 0x00, 0x00, 0x00,
];
let sl = SecretList {
total_num_secrets: 0x112,
secrets: vec![SecretEntry {
let sl = SecretList::new_with_hdr(
SecretListHdr::new(0x001, 0x112, 0x101),
vec![SecretEntry {
index: 1.into(),
stype: 2.into(),
len: 32.into(),
res_8: 0,
id: SecretId::from([0; 32]),
}],
};
);
let mut buf = [0u8; 0x40];
{
@@ -501,4 +672,120 @@ mod test {
)],
)
}
#[test]
fn secret_list_ser() {
let list = SecretList::new_with_hdr(
SecretListHdr::new(0x001, 0x112, 0x101),
vec![SecretEntry {
index: 1.into(),
stype: 2.into(),
len: 32.into(),
res_8: 0,
id: SecretId::from([0; 32]),
}],
);
assert_ser_tokens(
&list,
&[
Token::Map { len: None },
Token::String("total_num_secrets"),
Token::U16(0x112),
Token::String("secrets"),
Token::Seq { len: Some(1) },
Token::Struct {
name: "SecretEntry",
len: (4),
},
Token::String("index"),
Token::U16(1),
Token::String("stype"),
Token::U16(2),
Token::String("len"),
Token::U32(32),
Token::String("id"),
Token::String("0x0000000000000000000000000000000000000000000000000000000000000000"),
Token::StructEnd,
Token::SeqEnd,
Token::MapEnd,
],
)
}
#[test]
fn secret_id_display() {
let text = "Fancy secret ID";
let id = SecretId::from_string(text);
let exp =
"Fancy secret ID | 0x46616e6379207365637265742049440000000000000000000000000000000000";
assert_eq!(id.to_string(), exp);
}
#[test]
fn secret_id_long_name() {
let text = "the most fanciest secret ID you ever seen in the time the universe exists";
let id = SecretId::from_string(text);
let exp =
"the most fanciest secret ID you | 0x746865206d6f73742066616e63696573742073656372657420494420796f7500";
assert_eq!(id.to_string(), exp);
}
#[test]
fn secret_id_no_ascii_name() {
let text = [0; 32];
let id = SecretId::from(text);
let exp = "0x0000000000000000000000000000000000000000000000000000000000000000";
assert_eq!(id.to_string(), exp);
}
#[test]
fn secret_id_no_ascii_name2() {
let text = [
0x25, 0x55, 3, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
0, 0, 0, 0, 0, 0, 0,
];
let id = SecretId::from(text);
assert_eq!(id.as_ascii(), None);
}
#[test]
fn secret_id_no_ascii_name3() {
let text = [
0x25, 0x55, 0, 4, 50, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 90, 0, 0xa, 0, 0, 0, 0, 0xf, 0,
0, 0, 0, 0, 0, 0, 0,
];
let id = SecretId::from(text);
assert_eq!(id.as_ascii(), None);
}
#[test]
fn secret_id_hex() {
let id_str = "Nice Test 123";
let id = SecretId::from_string(id_str);
let s = format!("{id:#x}");
assert_eq!(
s,
"0x4e69636520546573742031323300000000000000000000000000000000000000"
);
let s = format!("{id:x}");
assert_eq!(
s,
"4e69636520546573742031323300000000000000000000000000000000000000"
);
let s = format!("{id:#X}");
assert_eq!(
s,
"0x4E69636520546573742031323300000000000000000000000000000000000000"
);
let s = format!("{id:X}");
assert_eq!(
s,
"4E69636520546573742031323300000000000000000000000000000000000000"
);
}
}

View File

@@ -8,17 +8,17 @@ license.workspace = true
rust-version.workspace = true
[dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]}
lazy_static = "1.1"
openssl = { version = "0.10.66" }
clap = { version ="4.5", features = ["derive", "wrap_help"]}
lazy_static = "1.5"
openssl = { version = "0.10.70" }
pv_core = { path = "../pv_core", package = "s390_pv_core"}
rand = "0.8"
regex = "1.7"
serde = { version = "1.0.139", features = ["derive"] }
regex = "1.11"
serde = { version = "1.0.217", features = ["derive"] }
serde_yaml = "0.9"
utils = { path = "../utils" }
[build-dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]}
clap_complete = "4.1"
lazy_static = "1.1"
clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.5"
lazy_static = "1.5"

View File

@@ -9,14 +9,14 @@ rust-version.workspace = true
workspace = true
[dependencies]
anyhow = { version = "1.0.70", features = ["std"] }
anyhow = { version = "1.0.95", features = ["std"] }
base64 = "0.22.1"
byteorder = "1.3"
clap = { version ="4.1", features = ["derive", "wrap_help"]}
curl = "0.4.44"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
openssl = "0.10.66"
serde = { version = "1.0.139", features = ["derive"]}
byteorder = "1.5"
clap = { version ="4.5", features = ["derive", "wrap_help"]}
curl = "0.4.47"
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
openssl = "0.10.70"
serde = { version = "1.0.217", features = ["derive"]}
serde_json = "1.0"
serde_yaml = "0.9"
zerocopy = { version="0.7", features = ["derive"] }
@@ -25,8 +25,8 @@ pv = { path = "../pv", package = "s390_pv" }
utils = { path = "../utils" }
[build-dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]}
clap_complete = "4.1"
clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.5"
log = { version = "0.4", features = ["std", "release_max_level_debug"] }
utils = { path = "../utils" }

View File

@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEvDCCAqSgAwIBAgIUaAbjRvw8jn+JnUpX8FvywTRln98wDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjEwNjAxMDkxODA3WhgPMjI5NTAzMTcwOTE4
MDdaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAEm7h/+hliO9pRO+nuDpkzuvdAlcMaiO/B4
Fa7gDHP59/E5s86G9GDBr1dqGNeJeB0wX8FTxZI2YJPv+T9kJDxaJQGenakJOdGe
SkkVjcdf4+65leLN2CzUMl0/csUu8jTZNsdFIL2vVMuY9ruT2se6tPdR4YhuoQRj
ADli4nUOsj/wcqMwMC4wHAYDVR0fBBUwEzARoA+gDYYLaWJtLmNydC5wZW0wDgYD
VR0PAQH/BAQDAgMIMA0GCSqGSIb3DQEBDQUAA4ICAQBZNR9MypBMNgT44FsKKQDG
Cduozb/NbcQFVsY7EV5VaeOjMzYSpKYNHZhJekVT8FRzRNrqcsaIvJMixPyx773g
GlYbE23Y6ktFvjwgbPrllBqsepoowCk04e3kt4yGYoo8MKQhYmSizcfIa8rXIjon
3INJHysh50hlWQgGScgOP1IK6vweKn+O9UcBxHuO9xm67NKmWVyEJkABB048eGFd
P10becukiN7XjBacxGnT8haKkinB0hoj1AzXHORZYaG3JM8KF+G/tcBhJcxc9qJd
XpGsvEIiHXg5JvaKmk4wX4y36O3koFc5fzm1fNEFt4k5OaKxI5jglmws/Tf7Q8qq
QR2WBcQ3dfqToFeDL7PXH4bgLkiAz1PFZpJGqCxKwI1dFc57fhpwGqoVDynH36mE
QSFMHGxSzigZYWCAlk/vYTDF10IuKwxaseuuaEXSfGfIl8Nmw/k5w4kBhk7i3vh7
QasnhCaDir13QjCZ/uPbXEc5RlgQ+fZVmG9HoZv9rnKlxlLzYYxHR5idKku1D789
bn2YsN4J9yxBGdSsG1qWD+pUbtn9vn7LbD5eXFmIiKEn3aDgyUBBFTdB24OgKRVn
tbBNLGc+yMEBH5BCGVHM5Y7tHo4cgCCbmD+PStQ9w+0+WxBwYt1n7La2Sx22K7e8
FLwjv+dXJRmO54vY3laicQ==
-----END CERTIFICATE-----

View File

@@ -9,28 +9,28 @@ rust-version.workspace = true
workspace = true
[dependencies]
anyhow = { version = "1.0.70", features = ["std"] }
clap = { version ="4.1", features = ["derive", "wrap_help"]}
anyhow = { version = "1.0.95", features = ["std"] }
clap = { version ="4.5", features = ["derive", "wrap_help"]}
deku = "0.18"
deku_derive = "0.18"
enum_dispatch = "0.3.13"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
openssl = "0.10.66"
serde = { version = "1.0.139", features = ["derive"]}
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
openssl = "0.10.70"
serde = { version = "1.0.217", features = ["derive"]}
serde_json = "1.0"
thiserror = "1.0.33"
thiserror = "2.0.11"
pv = { path = "../pv", package = "s390_pv" }
utils = { path = "../utils" }
[build-dependencies]
anyhow = { version = "1.0.70", features = ["std"] }
clap = { version ="4.1", features = ["derive", "wrap_help"]}
anyhow = { version = "1.0.95", features = ["std"] }
clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.5"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
pv = { path = "../pv", package = "s390_pv" }
utils = { path = "../utils" }
[dev-dependencies]
proptest = "1.3"
proptest = "1.6"

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH genprotimg 1 "2024-12-05" "s390-tools" "Genprotimg Manual"
.TH "GENPROTIMG" "1" "2024-12-19" "s390-tools" "Genprotimg Manual"
.nh
.ad l
.SH NAME
\fBgenprotimg\fP - Create an IBM Secure Execution image
\fB
genprotimg \- Create an IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
@@ -37,7 +36,7 @@ QEMU.
.SH OPTIONS
.PP
\-i, \-\-kernel <FILE>
\-i, \-\-kernel, \-\-image <FILE>
.RS 4
Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a
raw binary s390x Linux kernel. The ELF format is not supported.
@@ -123,17 +122,25 @@ Overwrite an existing Secure Execution boot image.
.RE
.RE
.PP
\-\-comm\-key <FILE>
\-\-cck, \-\-comm\-key <FILE>
.RS 4
Use the content of FILE as the customer\-communication key (CCK). The file must
contain exactly 32 bytes of data.
.RE
.RE
.PP
\-\-hdr\-key <FILE>
.RS 4
Use the content of FILE as the Secure Execution header protection key. The file
must contain exactly 32 bytes of data. If the option is not specified, the
Secure Execution header protection key is a randomly generated key.
.RE
.RE
.PP
\-\-enable\-dump
.RS 4
Enable Secure Execution guest dump support. This option requires the
\fB\-\-comm\-key\fR option.
\fB\-\-cck\fR option.
.RE
.RE
.PP
@@ -146,8 +153,7 @@ Disable Secure Execution guest dump support (default).
\-\-enable\-cck\-extension\-secret
.RS 4
Add\-secret requests must provide an extension secret that matches the
CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
option.
CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
.RE
.RE
.PP
@@ -196,6 +202,22 @@ Disable the support for backup target keys (default).
.RE
.RE
.PP
\-\-enable\-image\-encryption
.RS 4
Enable encryption of the image components (default). The image components are:
the kernel, ramdisk, and kernel command line.
.RE
.RE
.PP
\-\-disable\-image\-encryption
.RS 4
Disable encryption of the image components. The image components are: the
kernel, ramdisk, and kernel command line. Use only if the components used do not
contain any confidential content (for example, secrets like non\-public
cryptographic keys).
.RE
.RE
.PP
\-v, \-\-verbose
.RS 4
Provide more detailed output.
@@ -222,37 +244,37 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS
.TP 8
.B 0 - Program finished successfully
.B 0 \- Program finished successfully
The command was executed successfully.
.RE
.TP 8
.B 1 - Generic error
.B 1 \- Generic error
Something went wrong during the operation. Refer to the error
message.
.RE
.TP 8
.B 2 - Usage error
.B 2 \- Usage error
The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments.
.RE
.SH EXAMPLES
These are examples of how to generate an IBM Secure Execution image in
\fI\,/boot/secure-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
\fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z
signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the host-key document
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm-key\/\fR,
signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host-key document
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm\-key\/\fR,
which is used when creating a Secure Execution image with guest dump support
enabled in the second example.
Generate an IBM Secure Execution image:
.PP
.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR
.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR
Generate an IBM Secure Execution image with Secure Execution guest dump support:
.PP
.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
.B genprotimg \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm-key\fR
.SH NOTES
.IP "1." 4
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvimg-create 1 "2024-12-05" "s390-tools" "Pvimg Manual"
.TH "PVIMG-CREATE" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
\fBpvimg create\fP - Create an IBM Secure Execution image
\fB
pvimg-create \- Create an IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
@@ -36,7 +35,7 @@ QEMU.
.SH OPTIONS
.PP
\-i, \-\-kernel <FILE>
\-i, \-\-kernel, \-\-image <FILE>
.RS 4
Use the content of FILE as a raw binary Linux kernel. The Linux kernel must be a
raw binary s390x Linux kernel. The ELF format is not supported.
@@ -122,17 +121,25 @@ Overwrite an existing Secure Execution boot image.
.RE
.RE
.PP
\-\-comm\-key <FILE>
\-\-cck, \-\-comm\-key <FILE>
.RS 4
Use the content of FILE as the customer\-communication key (CCK). The file must
contain exactly 32 bytes of data.
.RE
.RE
.PP
\-\-hdr\-key <FILE>
.RS 4
Use the content of FILE as the Secure Execution header protection key. The file
must contain exactly 32 bytes of data. If the option is not specified, the
Secure Execution header protection key is a randomly generated key.
.RE
.RE
.PP
\-\-enable\-dump
.RS 4
Enable Secure Execution guest dump support. This option requires the
\fB\-\-comm\-key\fR option.
\fB\-\-cck\fR option.
.RE
.RE
.PP
@@ -145,8 +152,7 @@ Disable Secure Execution guest dump support (default).
\-\-enable\-cck\-extension\-secret
.RS 4
Add\-secret requests must provide an extension secret that matches the
CCK\-derived extension secret. This option requires the \fB\-\-comm\-key\fR
option.
CCK\-derived extension secret. This option requires the \fB\-\-cck\fR option.
.RE
.RE
.PP
@@ -195,6 +201,22 @@ Disable the support for backup target keys (default).
.RE
.RE
.PP
\-\-enable\-image\-encryption
.RS 4
Enable encryption of the image components (default). The image components are:
the kernel, ramdisk, and kernel command line.
.RE
.RE
.PP
\-\-disable\-image\-encryption
.RS 4
Disable encryption of the image components. The image components are: the
kernel, ramdisk, and kernel command line. Use only if the components used do not
contain any confidential content (for example, secrets like non\-public
cryptographic keys).
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help (see a summary with \fB\-h\fR).
@@ -203,37 +225,37 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS
.TP 8
.B 0 - Program finished successfully
.B 0 \- Program finished successfully
The command was executed successfully.
.RE
.TP 8
.B 1 - Generic error
.B 1 \- Generic error
Something went wrong during the operation. Refer to the error
message.
.RE
.TP 8
.B 2 - Usage error
.B 2 \- Usage error
The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments.
.RE
.SH EXAMPLES
These are examples of how to generate an IBM Secure Execution image in
\fI\,/boot/secure-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
\fI\,/boot/secure\-linux\/\fR, using the kernel file \fI\,/boot/vmlinuz\/\fR, the
initrd in \fI\,/boot/initrd.img\/\fR, the kernel parameters contained in
\fI\,parmfile\/\fR, the intermediate CA in \fI\,DigiCertCA.crt\/\fR, the IBM Z
signing key in \fI\,ibm-z-host-key-signing.crt\/\fR, and the host-key document
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm-key\/\fR,
signing key in \fI\,ibm\-z\-host\-key\-signing.crt\/\fR, and the host-key document
in \fI\,host_key.crt\/\fR. An AES-256 GCM key is stored in \fI\,comm\-key\/\fR,
which is used when creating a Secure Execution image with guest dump support
enabled in the second example.
Generate an IBM Secure Execution image:
.PP
.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR
.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR
Generate an IBM Secure Execution image with Secure Execution guest dump support:
.PP
.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm-z-host-key-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure-linux\/\fR \-\-enable\-dump \-\-comm\-key \fI\,comm-key\fR
.B pvimg create \-i \fI\,/boot/vmlinuz\/\fR \-r \fI\,/boot/initrd.img\/\fR \-p \fI\,parmfile\/\fR \-k \fI\,host_key.crt\/\fR \-C \fI\,ibm\-z\-host\-key\-signing.crt\/\fR \-C \fI\,DigiCertCA.crt\fR \-o \fI\,/boot/secure\-linux\/\fR \-\-enable\-dump \-\-cck \fI\,comm\-key\fR
.SH NOTES
.IP "1." 4
The \fBgenprotimg\fR(1) command is a symbolic link to the \fBpvimg-create\fR(1) command.

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvimg-info 1 "2024-12-05" "s390-tools" "Pvimg Manual"
.TH "PVIMG-INFO" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
\fBpvimg info\fP - Print information about the IBM Secure Execution image
\fB
pvimg-info \- Print information about the IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
@@ -37,9 +36,11 @@ Possible values:
.RE
.RE
.PP
\-\-key <FILE>
\-\-hdr\-key <FILE>
.RS 4
Use the key in FILE to decrypt the Secure Execution header.
Use the key in FILE to decrypt the Secure Execution header. It is the key that
was specified with the command line option \fB\-\-hdr\-key\fR at the Secure
Execution image creation.
.RE
.RE
.PP
@@ -51,16 +52,16 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS
.TP 8
.B 0 - Program finished successfully
.B 0 \- Program finished successfully
The command was executed successfully.
.RE
.TP 8
.B 1 - Generic error
.B 1 \- Generic error
Something went wrong during the operation. Refer to the error
message.
.RE
.TP 8
.B 2 - Usage error
.B 2 \- Usage error
The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments.
.RE

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvimg-test 1 "2024-12-05" "s390-tools" "Pvimg Manual"
.TH "PVIMG-TEST" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
\fBpvimg test\fP - Test different aspects of an existing IBM Secure Execution image
\fB
pvimg-test \- Test different aspects of an existing IBM Secure Execution image
.SH SYNOPSIS
.nf
.fam C
@@ -54,16 +53,16 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS
.TP 8
.B 0 - Program finished successfully
.B 0 \- Program finished successfully
The command was executed successfully.
.RE
.TP 8
.B 1 - Generic error
.B 1 \- Generic error
Something went wrong during the operation. Refer to the error
message.
.RE
.TP 8
.B 2 - Usage error
.B 2 \- Usage error
The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments.
.RE

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvimg 1 "2024-12-05" "s390-tools" "Pvimg Manual"
.TH "PVIMG" "1" "2024-12-19" "s390-tools" "Pvimg Manual"
.nh
.ad l
.SH NAME
\fBpvimg\fP - Create and inspect IBM Secure Execution images
\fB
pvimg \- Create and inspect IBM Secure Execution images
.SH SYNOPSIS
.nf
.fam C
@@ -16,9 +15,9 @@ pvimg [OPTIONS] <COMMAND>
.fam C
.fi
.SH DESCRIPTION
Use pvimg to create an IBM Secure Execution image, which can be loaded using
zipl or QEMU. pvimg can also be used to inspect existing Secure Execution
images.
Use \fBpvimg\fP to create an IBM Secure Execution image, which can be loaded
using zipl or QEMU. \fBpvimg\fP can also be used to inspect existing Secure
Execution images.
.SH "PVIMG COMMANDS"
.PP
@@ -69,16 +68,16 @@ Print help (see a summary with \fB\-h\fR).
.SH EXIT STATUS
.TP 8
.B 0 - Program finished successfully
.B 0 \- Program finished successfully
The command was executed successfully.
.RE
.TP 8
.B 1 - Generic error
.B 1 \- Generic error
Something went wrong during the operation. Refer to the error
message.
.RE
.TP 8
.B 2 - Usage error
.B 2 \- Usage error
The command was used incorrectly, for example: unsupported command
line flag, or wrong number of arguments.
.RE

View File

@@ -96,8 +96,8 @@ pub struct ComponentPaths {
#[command(group(ArgGroup::new("header-flags").multiple(true).conflicts_with_all(["x_pcf", "x_scf"])))]
pub struct CreateBootImageLegacyFlags {
/// Enable Secure Execution guest dump support. This option requires the
/// '--comm-key' option.
#[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
/// '--cck' option.
#[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
pub enable_dump: Option<bool>,
/// Disable Secure Execution guest dump support (default).
@@ -105,9 +105,9 @@ pub struct CreateBootImageLegacyFlags {
pub disable_dump: Option<bool>,
/// Add-secret requests must provide an extension secret that matches the
/// CCK-derived extension secret. This option requires the '--comm-key'
/// CCK-derived extension secret. This option requires the '--cck'
/// option.
#[arg(long, action = clap::ArgAction::SetTrue, requires="comm_key", group="header-flags")]
#[arg(long, action = clap::ArgAction::SetTrue, requires="cck", group="header-flags")]
pub enable_cck_extension_secret: Option<bool>,
/// Add-secret requests don't have to provide the CCK-derived extension
@@ -140,6 +140,20 @@ pub struct CreateBootImageLegacyFlags {
/// Disable the support for backup target keys (default).
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_backup_keys", group="header-flags")]
pub disable_backup_keys: Option<bool>,
/// Enable encryption of the image components (default).
///
/// The image components are: the kernel, ramdisk, and kernel command line.
#[arg(long, action = clap::ArgAction::SetTrue, group="header-flags")]
pub enable_image_encryption: Option<bool>,
/// Disable encryption of the image components.
///
/// The image components are: the kernel, ramdisk, and kernel command line.
/// Use only if the components used do not contain any confidential content
/// (for example, secrets like non-public cryptographic keys).
#[arg(long, action = clap::ArgAction::SetTrue, conflicts_with="enable_image_encryption", group="header-flags")]
pub disable_image_encryption: Option<bool>,
}
#[non_exhaustive]
@@ -178,8 +192,11 @@ pub struct InfoArgs {
pub format: OutputFormat,
/// Use the key in FILE to decrypt the Secure Execution header.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
pub key: Option<PathBuf>,
///
/// It is the key that was specified with the command line option
/// '--hdr-key' at the Secure Execution image creation.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, alias = "key")]
pub hdr_key: Option<PathBuf>,
}
#[derive(Args, Debug)]
@@ -313,9 +330,18 @@ pub struct CreateBootImageArgs {
/// Use the content of FILE as the customer-communication key (CCK).
///
/// The file must contain exactly 32 bytes of data.
#[arg(long, value_name = "FILE")]
pub comm_key: Option<PathBuf>,
/// The file must contain exactly 32 bytes of data. This option used to be
/// called '--comm-key' in previous versions.
#[arg(long, value_name = "FILE", visible_alias = "comm-key")]
pub cck: Option<PathBuf>,
/// Use the content of FILE as the Secure Execution header protection key.
///
/// The file must contain exactly 32 bytes of data. If the option is not
/// specified, the Secure Execution header protection key is a randomly
/// generated key.
#[arg(long, value_name = "FILE", alias = "x-header-key")]
pub hdr_key: Option<PathBuf>,
#[clap(flatten)]
pub legacy_flags: CreateBootImageLegacyFlags,
@@ -338,11 +364,6 @@ pub struct CreateBootImageExperimentalArgs {
#[arg(long, value_name = "FILE", hide(true))]
pub x_comp_key: Option<PathBuf>,
/// Manually set the Secure Execution header protection key (experimental option).
// Hidden in user documentation.
#[arg(long, value_name = "FILE", hide(true))]
pub x_header_key: Option<PathBuf>,
/// Manually set the PSW address used for the Secure Execution header (experimental option).
// Hidden in user documentation.
#[arg(long, value_name = "ADDRESS", hide(true))]
@@ -467,6 +488,8 @@ mod test {
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("parmfile", ["--parmfile", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
CliOption::new("comm-key", ["--cck", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"]),
CliOption::new("comm-key", ["--comm-key", "/dev/null"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-pcf", ["--x-pcf", "0x0"]),
@@ -476,6 +499,10 @@ mod test {
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo-hmac", ["--enable-pckmo-hmac"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-backup-keys", ["--enable-backup-keys"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--x-header-key", "/dev/null"]),])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key", "/dev/null"]),])),
];
let invalid_create_args = [
flat_map_collect(remove(mvcanv.clone(), "no-verify")),
@@ -501,6 +528,9 @@ mod test {
CliOption::new("x-pcf2", ["--x-pcf", "0x0"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-pckmo", ["--enable-pckmo"]),
CliOption::new("disable-pckmo", ["--disable-pckmo"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-image-encryption", ["--enable-image-encryption"]),
CliOption::new("disable-image-encryption", ["--disable-image-encryption"])])),
flat_map_collect(insert(mvca.clone(), vec![CliOption::new("x-header-key", ["--hdr-key"]),])),
];
let mut genprotimg_valid_args = vec![
@@ -692,6 +722,22 @@ mod test {
CliOption::new("image", ["/dev/null"]),
],
)),
flat_map_collect(insert(
args.clone(),
vec![
CliOption::new("hdr-key", ["--hdr-key", "/dev/null"]),
CliOption::new("format", ["--format=json"]),
CliOption::new("image", ["/dev/null"]),
],
)),
flat_map_collect(insert(
args.clone(),
vec![
CliOption::new("hdr-key", ["--key", "/dev/null"]),
CliOption::new("format", ["--format=json"]),
CliOption::new("image", ["/dev/null"]),
],
)),
// separation between keyword and positional args works
flat_map_collect(insert(
args.clone(),
@@ -732,7 +778,7 @@ mod test {
// Test for invalid combinations
// Input is missing
let mut pvimg_invalid_args = vec![vec!["pvimg", "test"]];
let mut pvimg_invalid_args = vec![vec!["pvimg", "info"]];
for create_args in &valid_test_args {
pvimg_valid_args.push(

View File

@@ -25,6 +25,7 @@ pub struct UserProvidedKeys {
/// Reads all user provided keys.
pub fn read_user_provided_keys(
cck_path: Option<&Path>,
hdr_key_path: Option<&Path>,
experimental_args: &CreateBootImageExperimentalArgs,
) -> Result<UserProvidedKeys> {
let components_key = {
@@ -43,7 +44,7 @@ pub fn read_user_provided_keys(
}
};
let aead_key = {
match &experimental_args.x_header_key {
match hdr_key_path {
Some(key_path) => {
info!(
"Use file '{}' as the Secure Execution header protection",

View File

@@ -55,7 +55,7 @@ fn parse_flags(
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
lf.enable_dump
.filter(|x| *x)
.and(Some(PcfV1::all_disabled([PcfV1::AllowDumping]))),
.and(Some(PcfV1::all_enabled([PcfV1::AllowDumping]))),
lf.disable_pckmo
.filter(|x| *x)
.and(Some(PcfV1::all_disabled([
@@ -80,6 +80,12 @@ fn parse_flags(
lf.enable_backup_keys
.filter(|x| *x)
.and(Some(PcfV1::all_enabled([PcfV1::BackupTargetKeys]))),
lf.disable_image_encryption
.filter(|x| *x)
.and(Some(PcfV1::all_enabled([PcfV1::NoComponentEncryption]))),
lf.enable_image_encryption
.filter(|x| *x)
.and(Some(PcfV1::all_disabled([PcfV1::NoComponentEncryption]))),
]
.into_iter()
.flatten()
@@ -131,10 +137,17 @@ pub fn create(opt: &CreateBootImageArgs) -> Result<OwnExitCode> {
let verified_host_keys = opt
.certificate_args
.get_verified_hkds("Secure Execution image")?;
let user_provided_keys =
read_user_provided_keys(opt.comm_key.as_deref(), &opt.experimental_args)?;
let user_provided_keys = read_user_provided_keys(
opt.cck.as_deref(),
opt.hdr_key.as_deref(),
&opt.experimental_args,
)?;
let (plaintext_flags, secret_flags) = parse_flags(opt)?;
if plaintext_flags.is_set(PcfV1::NoComponentEncryption) {
warn!("The components encryption is disabled, make sure that the components do not contain any confidential content.");
}
let mut components = components(&opt.component_paths)?;
if opt.no_component_check {
warn!("The component check is turned off!");

View File

@@ -27,7 +27,7 @@ pub fn info(opt: &InfoArgs) -> Result<OwnExitCode> {
SeHdr::seek_sehdr(&mut input, None)?;
let hdr = SeHdr::try_from_io(input)?;
if let Some(key_path) = &opt.key {
if let Some(key_path) = &opt.hdr_key {
let key =
SymKey::try_from_data(hdr.key_type(), read_file(key_path, "Reading key")?.into())?;
serde_json::to_writer_pretty(&mut output, &hdr.decrypt(&key)?)?;

View File

@@ -30,6 +30,9 @@ pub enum Error {
#[error("Invalid Secure Execution header")]
InvalidSeHdr,
#[error("Secure Execution header size {given} is larger than the maximum of {maximum} bytes")]
InvalidSeHdrTooLarge { given: usize, maximum: usize },
#[error("Invalid component metadata.")]
InvalidComponentMetadata,

View File

@@ -171,8 +171,8 @@ impl AeadCipherTrait for SeHdr {
}
impl AeadDataTrait for SeHdr {
fn aad(&self) -> Vec<u8> {
[serialize_to_bytes(&self.common).unwrap(), self.data.aad()].concat()
fn aad(&self) -> Result<Vec<u8>> {
Ok([serialize_to_bytes(&self.common)?, self.data.aad()?].concat())
}
fn data(&self) -> Vec<u8> {
@@ -259,9 +259,13 @@ impl SeHdr {
return Err(Error::InvalidSeHdr);
}
if sehs <= common_size {
return Err(Error::InvalidSeHdr);
}
data.resize(sehs, 0);
reader.read_exact(&mut data[common_size..])?;
Self::try_from_data(&data)
Self::try_from_data(&data).map_err(|_| Error::InvalidSeHdr)
}
}
@@ -338,13 +342,13 @@ impl UvDataPlainTrait for SeHdrPlain {
}
impl AeadPlainDataTrait for SeHdrPlain {
fn aad(&self) -> Vec<u8> {
let data_aad = self.data.aad();
fn aad(&self) -> Result<Vec<u8>> {
let data_aad = self.data.aad()?;
[serialize_to_bytes(&self.common).unwrap(), data_aad].concat()
Ok([serialize_to_bytes(&self.common)?, data_aad].concat())
}
fn data(&self) -> Confidential<Vec<u8>> {
fn data(&self) -> Result<Confidential<Vec<u8>>> {
self.data.data()
}
@@ -366,3 +370,58 @@ impl AeadCipherTrait for SeHdrPlain {
self.data.aead_tag_size()
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::SeHdr;
use crate::error::Error;
#[test]
fn test_sehdr_try_from_io() {
// Invalid SeHdr as `sehs` is set to 0
assert!(matches!(
SeHdr::try_from_io(Cursor::new([
73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 8
])),
Err(Error::InvalidSeHdr)
));
// Invalid SeHdr as the `sehs` is too large.
assert!(matches!(
SeHdr::try_from_io(Cursor::new([
73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0, 0, 1, 255, 65, 65, 65, 65, 67, 0,
65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 241, 241,
241, 241, 241, 91, 91, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80,
112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
112, 112, 112, 112, 91, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65,
65, 65, 91, 91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 241, 241, 241, 241, 241, 91, 91, 91, 112,
112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112,
112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 80, 112, 112, 112, 112, 112, 112,
112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 91, 112, 112,
112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120,
0, 112, 112, 0, 1, 0, 0, 0, 0, 101, 99, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 65, 65, 65, 65, 67, 0, 65, 17, 65, 0, 65, 65, 65, 65, 65, 65, 91,
91, 180, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,
91, 91, 112, 112, 112, 112, 112, 73, 66, 77, 83, 101, 99, 69, 120, 0, 0, 1, 0, 0,
0, 0, 48, 53, 53, 53, 53, 53, 53, 53, 91, 91, 91, 241, 241, 46, 49, 49, 0, 49, 49,
0, 0, 112, 112, 112, 91, 0, 0, 0, 0, 9, 0, 49, 50, 22, 241, 241, 241, 241, 241,
241, 241, 241, 241, 241, 241, 91, 91, 91, 91, 91, 255, 251, 0, 0, 91, 91, 91, 91,
91, 91, 91, 91, 91, 91, 91, 0, 0, 91, 0, 0, 10, 91, 91, 91, 65, 65, 65, 65
])),
Err(Error::InvalidSeHdr)
));
}
}

View File

@@ -230,8 +230,14 @@ mod tests {
let decrypted = bin.decrypt(&prot_key).expect("BUG");
assert_eq!(bin.common, decrypted.common);
assert_eq!(bin.aad(), decrypted.aad());
assert_ne!(&bin.data(), decrypted.data().value());
assert_eq!(
bin.aad().expect("should not fail"),
decrypted.aad().expect("should not fail")
);
assert_ne!(
&bin.data(),
decrypted.data().expect("should not fail").value()
);
let _decrypted_hdrv1: SeHdrDataV1 = decrypted.data.try_into().expect("BUG");
}

View File

@@ -19,6 +19,7 @@ use serde::{Serialize, Serializer};
use super::keys::phkh_v1;
use crate::{
error::Error,
misc::PAGESIZE,
pv_utils::{
error::Result,
se_hdr::{
@@ -51,11 +52,14 @@ struct HdrSizesV1 {
#[derive(Debug, Clone, PartialEq, Eq, DekuRead, DekuWrite, Serialize)]
#[deku(endian = "endian", ctx = "endian: Endian", ctx_default = "Endian::Big")]
struct SeHdrAadV1 {
#[deku(assert = "*sehs <= SeHdrDataV1::MAX_SIZE.try_into().unwrap()")]
sehs: u32,
#[serde(serialize_with = "ser_hex")]
iv: [u8; SymKeyType::AES_256_GCM_IV_LEN],
res1: u32,
#[deku(assert = "*nks <= (*sehs).into()", update = "self.keyslots.len()")]
nks: u64,
#[deku(assert = "*sea <= (*sehs).into()")]
sea: u64,
nep: u64,
#[serde(serialize_with = "ser_lower_hex")]
@@ -118,6 +122,7 @@ pub struct SeHdrConfV1 {
psw: PSW,
#[serde(serialize_with = "ser_lower_hex")]
scf: u64,
#[deku(assert_eq = "0")]
noi: u32,
res2: u32,
#[deku(count = "noi")]
@@ -200,6 +205,7 @@ where
}
impl SeHdrDataV1 {
const MAX_SIZE: usize = 2 * PAGESIZE;
const PCF_DEFAULT: u64 = 0x0;
const SCF_DEFAULT: u64 = 0x0;
@@ -241,7 +247,14 @@ impl SeHdrDataV1 {
tag: SeHdrTagV1::default(),
};
let hdr_size = ret.size()?;
ret.aad.sehs = hdr_size.phs.try_into()?;
let phs = hdr_size.phs.try_into()?;
if phs > Self::MAX_SIZE {
return Err(Error::InvalidSeHdrTooLarge {
given: phs,
maximum: Self::MAX_SIZE,
});
}
ret.aad.sehs = phs.try_into()?;
ret.aad.sea = hdr_size.sea;
Ok(ret)
}
@@ -494,8 +507,8 @@ impl KeyExchangeTrait for SeHdrBinV1 {
}
impl AeadDataTrait for SeHdrBinV1 {
fn aad(&self) -> Vec<u8> {
serialize_to_bytes(&self.aad).unwrap()
fn aad(&self) -> Result<Vec<u8>> {
serialize_to_bytes(&self.aad)
}
fn data(&self) -> Vec<u8> {
@@ -508,12 +521,12 @@ impl AeadDataTrait for SeHdrBinV1 {
}
impl AeadPlainDataTrait for SeHdrDataV1 {
fn aad(&self) -> Vec<u8> {
serialize_to_bytes(&self.aad).unwrap()
fn aad(&self) -> Result<Vec<u8>> {
serialize_to_bytes(&self.aad)
}
fn data(&self) -> Confidential<Vec<u8>> {
serialize_to_bytes(self.data.value()).unwrap().into()
fn data(&self) -> Result<Confidential<Vec<u8>>> {
Ok(serialize_to_bytes(self.data.value())?.into())
}
fn tag(&self) -> Vec<u8> {
@@ -610,4 +623,48 @@ mod tests {
assert_eq!(psw, hdr_data_v1.data.value().psw);
assert_eq!(cck.value(), hdr_data_v1.data.value().cck.value());
}
#[test]
fn max_size_sehdr_test() {
const MAX_HOST_KEYS: usize = 95;
let (_, host_key) = get_test_key_and_cert();
let pub_key = host_key.public_key().unwrap();
let host_keys_max: Vec<_> = (0..MAX_HOST_KEYS).map(|_| pub_key.clone()).collect();
let too_many_host_keys: Vec<_> = (0..MAX_HOST_KEYS + 1).map(|_| pub_key.clone()).collect();
let xts_key = Confidential::new([0x3; SymKeyType::AES_256_XTS_KEY_LEN]);
let meta = ComponentMetadataV1 {
ald: [0x1; SHA_512_HASH_LEN],
pld: [0x2; SHA_512_HASH_LEN],
tld: [0x3; SHA_512_HASH_LEN],
nep: 3,
key: xts_key,
};
let psw = PSW {
addr: 1234,
mask: 5678,
};
let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
.expect("should not fail");
builder
.add_hostkeys(&host_keys_max)
.expect("should not fail")
.with_components(meta.clone())
.expect("should not fail");
let bin = builder.build().expect("should not fail");
assert_eq!(bin.common.version, SeHdrVersion::V1);
let hdr_v1: SeHdrBinV1 = bin.data.try_into().expect("should not fail");
assert_eq!(hdr_v1.aad.sehs, 8160);
let mut builder = SeHdrBuilder::new(SeHdrVersion::V1, psw.clone(), meta.clone())
.expect("should not fail");
builder
.add_hostkeys(&too_many_host_keys)
.expect("should not fail")
.with_components(meta)
.expect("should not fail");
assert!(matches!(builder.build(), Err(Error::InvalidSeHdr)));
}
}

View File

@@ -34,7 +34,7 @@ pub trait AeadCipherTrait {
#[enum_dispatch]
pub trait AeadDataTrait {
/// Returns the authenticated associated data.
fn aad(&self) -> Vec<u8>;
fn aad(&self) -> Result<Vec<u8>>;
/// Returns the encrypted data.
fn data(&self) -> Vec<u8>;
@@ -47,10 +47,10 @@ pub trait AeadDataTrait {
#[enum_dispatch]
pub trait AeadPlainDataTrait {
/// Returns the authenticated associated data.
fn aad(&self) -> Vec<u8>;
fn aad(&self) -> Result<Vec<u8>>;
/// Returns the unencrypted data.
fn data(&self) -> Confidential<Vec<u8>>;
fn data(&self) -> Result<Confidential<Vec<u8>>>;
/// Returns the tag data.
fn tag(&self) -> Vec<u8>;
@@ -124,8 +124,14 @@ pub trait UvDataPlainTrait:
expected: self.aead_key_type().to_string(),
});
}
let aad = self.aad();
let unecrypted_data = self.data();
let aad = self.aad().map_err(|err| match err {
Error::Deku(_) => Error::InvalidSeHdr,
err => err,
})?;
let unecrypted_data = self.data().map_err(|err| match err {
Error::Deku(_) => Error::InvalidSeHdr,
err => err,
})?;
let iv = self.iv();
let result = encrypt_aead(key, iv, &aad, unecrypted_data.value())?;
Self::C::try_from_data(&result.into_buf())
@@ -169,7 +175,7 @@ pub trait UvDataTrait: AeadDataTrait + AeadCipherTrait + KeyExchangeTrait + Clon
}
let tag_size = self.aead_tag_size();
let aad = self.aad();
let aad = self.aad()?;
let unecrypted_data = self.data();
let iv = self.iv();
let tag = self.tag();

View File

@@ -1,303 +0,0 @@
#!/bin/sh
#
# check_hostkeydoc - Verify an IBM Secure Execution host key document
#
# Sample script to verify that a host key document is genuine by
# verifying the issuer, the validity date and the signature.
# Optionally verify the full trust chain using a CA certificate.
#
# Sample invocation:
#
# ./check_hostkeydoc HKD1234.crt ibm-z-host-key-signing.crt -c DigiCertCA.crt -r ibm-z-host-key.crl
#
# Copyright IBM Corp. 2020
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
# Allocate temporary files
ISSUER_PUBKEY_FILE=$(mktemp)
SIGNATURE_FILE=$(mktemp)
BODY_FILE=$(mktemp)
ISSUER_DN_FILE=$(mktemp)
SUBJECT_DN_FILE=$(mktemp)
DEF_ISSUER_ARMONK_DN_FILE=$(mktemp)
DEF_ISSUER_POUGHKEEPSIE_DN_FILE=$(mktemp)
CANONICAL_ISSUER_DN_FILE=$(mktemp)
CRL_SERIAL_FILE=$(mktemp)
# Cleanup on exit
cleanup()
{
rm -f "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" \
"$ISSUER_DN_FILE" "$SUBJECT_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" \
"$CANONICAL_ISSUER_DN_FILE" "$CRL_SERIAL_FILE"
}
trap cleanup EXIT
# Enhanced error checking for bash
if [ -n "${BASH}" ]; then
# shellcheck disable=SC3040
set -o posix
# shellcheck disable=SC3040
set -o pipefail
# shellcheck disable=SC3040
set -o nounset
fi
set -e
# Usage
usage()
{
cat <<-EOF
Usage: $(basename "$1") [-d] [-c CA-cert] [-r CRL] host-key-doc signing-key-cert
Verify an IBM Secure Execution host key document against
a signing key.
Options:
-d disable default issuer check of host-key-doc
-c CA-cert trusted CA certificate
-r CRL list of revoked host-key-docs
Note that in order to have the full trust chain verified
it is necessary to provide the issuing CA's certificate.
The default issuer check may be disabled if a non-default
signing key certificate needs to be verified against the
CA certificate.
EOF
}
check_verify_chain()
{
# Verify certificate chain in case a CA certificate file/bundle
# was specified on the command line.
if [ -z "$2" ]; then
cat >&2 <<-EOF
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
No CA certificate specified! Skipping trust chain verification.
Make sure that '$1' is a valid certificate.
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
EOF
else
openssl verify -crl_download -crl_check "$2" &&
openssl verify -crl_download -crl_check -untrusted "$2" "$1" ||
exit 1
fi
}
extract_pubkey()
{
openssl x509 -in "$1" -pubkey -noout >"$2"
}
extract_signature()
{
# Assuming that the last field is the signature
SIGOFFSET=$(openssl asn1parse -in "$1" | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
extract_body()
{
# Assuming that the first field is the full cert body
SIGOFFSET=$(openssl asn1parse -in "$1" | head -2 | tail -1 | cut -d : -f 1)
openssl asn1parse -in "$1" -out "$2" -strparse "$SIGOFFSET" -noout
}
verify_signature()
{
# Assuming that the signature algorithm is SHA512 with RSA
openssl sha512 -verify "$1" -signature "$2" "$3"
}
canonical_dn()
{
OBJTYPE=$1
OBJ=$2
DNTYPE=$3
OUTPUT=$4
openssl "$OBJTYPE" -in "$OBJ" -"$DNTYPE" -noout -nameopt multiline |
LC_ALL=C sort | grep -v "$DNTYPE"= >"$OUTPUT"
}
default_issuer_armonk()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Armonk
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
default_issuer_pougkeepsie()
{
cat <<-EOF
commonName = International Business Machines Corporation
countryName = US
localityName = Poughkeepsie
organizationName = International Business Machines Corporation
organizationalUnitName = Key Signing Service
stateOrProvinceName = New York
EOF
}
# As organizationalUnitName can have an arbitrary prefix but must
# end with "Key Signing Service" let's normalize the OU name by
# stripping off the prefix
verify_default_issuer()
{
default_issuer_pougkeepsie >"$DEF_ISSUER_POUGHKEEPSIE_DN_FILE"
default_issuer_armonk >"$DEF_ISSUER_ARMONK_DN_FILE"
sed "s/\(^[ ]*organizationalUnitName[ ]*=[ ]*\).*\(Key Signing Service$\)/\1\2/" \
"$ISSUER_DN_FILE" >"$CANONICAL_ISSUER_DN_FILE"
if ! {
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_POUGHKEEPSIE_DN_FILE" ||
diff "$CANONICAL_ISSUER_DN_FILE" "$DEF_ISSUER_ARMONK_DN_FILE"
} >/dev/null 2>&1; then
echo Incorrect default issuer >&2 && exit 1
fi
}
verify_issuer_files()
{
if [ "$1" -eq 1 ]; then
verify_default_issuer
fi
}
cert_time()
{
DATE=$(openssl x509 -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
crl_time()
{
DATE=$(openssl crl -in "$1" -"$2" -noout | sed "s/^.*=//")
date -d "$DATE" +%s
}
verify_dates()
{
START="$1"
END="$2"
MSG="${3:-Certificate}"
NOW=$(date +%s)
if [ "$START" -le "$NOW" ] && [ "$NOW" -le "$END" ]; then
echo "${MSG} dates are OK"
else
echo "${MSG} date verification failed" >&2 && exit 1
fi
}
crl_serials()
{
openssl crl -in "$1" -text -noout |
grep "Serial Number" >"$CRL_SERIAL_FILE"
}
check_serial()
{
CERT_SERIAL=$(openssl x509 -in "$1" -noout -serial | cut -d = -f 2)
grep -q "$CERT_SERIAL" "$CRL_SERIAL_FILE"
}
check_file()
{
[ -e "$1" ] ||
(echo "File '$1' not found" >&2 && exit 1)
}
# check args
CRL_FILE=
CA_FILE=
CHECK_DEFAULT_ISSUER=1
while getopts 'dr:c:h' opt; do
case $opt in
d) CHECK_DEFAULT_ISSUER=0 ;;
r) CRL_FILE=$OPTARG ;;
c) CA_FILE=$OPTARG ;;
h)
usage "$0"
exit 0
;;
?)
usage "$0"
exit 1
;;
esac
done
shift "$((OPTIND - 1))"
if [ $# -ne 2 ]; then
usage "$0" >&2
exit 1
fi
HKD_FILE=$1
HKSK_FILE=$2
# Check whether all specified files exist
check_file "$HKD_FILE"
check_file "$HKSK_FILE"
# CA and CRL are optional arguments
[ -n "$CA_FILE" ] && check_file "$CA_FILE"
[ -n "$CRL_FILE" ] && check_file "$CRL_FILE"
# Check trust chain
check_verify_chain "$HKSK_FILE" "$CA_FILE"
# Verify host key document signature
printf "Checking host key document signature: "
extract_pubkey "$HKSK_FILE" "$ISSUER_PUBKEY_FILE" &&
extract_signature "$HKD_FILE" "$SIGNATURE_FILE" &&
extract_body "$HKD_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
# Verify the issuer
canonical_dn x509 "$HKD_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
# Verify dates
verify_dates "$(cert_time "$HKD_FILE" startdate)" "$(cert_time "$HKD_FILE" enddate)"
# Check CRL if specified
if [ -n "$CRL_FILE" ]; then
printf "Checking CRL signature: "
extract_signature "$CRL_FILE" "$SIGNATURE_FILE" &&
extract_body "$CRL_FILE" "$BODY_FILE" &&
verify_signature "$ISSUER_PUBKEY_FILE" "$SIGNATURE_FILE" "$BODY_FILE" ||
exit 1
printf "CRL "
canonical_dn crl "$CRL_FILE" issuer "$ISSUER_DN_FILE"
canonical_dn x509 "$HKSK_FILE" subject "$SUBJECT_DN_FILE"
verify_issuer_files $CHECK_DEFAULT_ISSUER
verify_dates "$(crl_time "$CRL_FILE" lastupdate)" "$(crl_time "$CRL_FILE" nextupdate)" 'CRL'
crl_serials "$CRL_FILE"
check_serial "$HKD_FILE" &&
echo "Certificate is revoked, do not use it anymore!" >&2 &&
exit 1
fi
# We made it
echo All checks requested for \'"$HKD_FILE"\' were successful

View File

@@ -0,0 +1 @@
../../../genprotimg/samples/check_hostkeydoc

View File

@@ -9,17 +9,17 @@ rust-version.workspace = true
workspace = true
[dependencies]
anyhow = { version = "1.0.70", features = ["std"] }
clap = { version ="4.1", features = ["derive", "wrap_help"]}
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
anyhow = { version = "1.0.95", features = ["std"] }
clap = { version ="4.5", features = ["derive", "wrap_help"]}
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
serde_yaml = "0.9"
pv = { path = "../pv" , package = "s390_pv" }
utils = { path = "../utils"}
[build-dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"]}
clap_complete = "4.1"
clap = { version ="4.5", features = ["derive", "wrap_help"]}
clap_complete = "4.5"
log = { version = "0.4", features = ["std", "release_max_level_debug"] }
utils = { path = "../utils" }

View File

@@ -32,7 +32,7 @@ Create a new add-secret request
- **add**
<ul>
Perform an add-secret request (s390x only)
Submit an add-secret request to the Ultravisor (s390x only)
</ul>
- **lock**
@@ -50,23 +50,34 @@ List all ultravisor secrets (s390x only)
Verify that an add-secret request is sane
</ul>
- **retrieve**
<ul>
Retrieve a secret from the UV secret store (s390x only)
</ul>
## Options
`-v`, `--verbose`
<ul>
Provide more detailed output
Provide more detailed output.
</ul>
`-q`, `--quiet`
<ul>
Provide less output.
</ul>
`--version`
<ul>
Print version information and exit
Print version information and exit.
</ul>
`-h`, `--help`
<ul>
Print help
Print help (see a summary with '-h').
</ul>
@@ -95,12 +106,17 @@ Create a meta secret
Create an association secret
</ul>
- **retrievable**
<ul>
Create a retrievable secret
</ul>
### Options
`-k`, `--host-key-document <FILE>`
<ul>
Use FILE as a host-key document. Can be specified multiple times and must be
used at least once.
specified at least once.
</ul>
@@ -114,7 +130,7 @@ the host-key document beforehand.
`-C`, `--cert <FILE>`
<ul>
Use FILE as a certificate to verify the host key or keys. The certificates are
Use FILE as a certificate to verify the host-key or keys. The certificates are
used to establish a chain of trust for the verification of the host-key
documents. Specify this option twice to specify the IBM Z signing key and the
intermediate CA certificate (signed by the root CA).
@@ -123,15 +139,15 @@ intermediate CA certificate (signed by the root CA).
`--crl <FILE>`
<ul>
Use FILE as a certificate revocation list. The list is used to check whether a
certificate of the chain of trust is revoked. Specify this option multiple times
to use multiple CRLs.
Use FILE as a certificate revocation list (CRL). The list is used to check
whether a certificate of the chain of trust is revoked. Specify this option
multiple times to use multiple CRLs.
</ul>
`--offline`
<ul>
Make no attempt to download CRLs
Make no attempt to download CRLs.
</ul>
@@ -146,8 +162,7 @@ specified certificate.
`--hdr <FILE>`
<ul>
Specifies the header of the guest image. Can be an IBM Secure Execution image
created by genprotimg or an extracted IBM Secure Execution header. The header
must start at a page boundary.
created by 'pvimg/genprotimg' or an extracted IBM Secure Execution header.
</ul>
@@ -162,7 +177,7 @@ behavior.
`-o`, `--output <FILE>`
<ul>
Write the generated request to FILE
Write the generated request to FILE.
</ul>
@@ -209,15 +224,15 @@ the request.
`--flags <FLAGS>`
<ul>
Flags for the add-secret request
Flags for the add-secret request.
Possible values:
- **disable-dump**: Disables host-initiated dumping for the target guest instance
- **disable-dump**: Disables host-initiated dumping for the target guest instance.
</ul>
`--user-data <FILE>`
<ul>
Use the content of FILE as user-data. Passes user data defined in <FILE> through
Use the content of FILE as user-data. Passes user data defined in FILE through
the add-secret request to the ultravisor. The user data can be up to 512 bytes
of arbitrary data, and the maximum size depends on the size of the user-signing
key:
@@ -236,19 +251,25 @@ Optional. No user-data by default.
`--user-sign-key <FILE>`
<ul>
Use the content of FILE as user signing key. Adds a signature calculated from
the key in <FILE> to the add-secret request. The file must be in DER or PEM
format containing a private key. Supported are RSA 2048 & 3072-bit and
EC(secp521r1) keys. The firmware ignores the content, but the request tag
protects the signature. The user-signing key signs the request. The location of
the signature is filled with zeros during the signature calculation. The request
tag also secures the signature. See man pvsecret verify for more details.
Optional. No signature by default.
the key in FILE to the add-secret request. The file must be in DER or PEM format
containing a private key. Supported are RSA 2048 & 3072-bit and EC(secp521r1)
keys. The firmware ignores the content, but the request tag protects the
signature. The user-signing key signs the request. The location of the signature
is filled with zeros during the signature calculation. The request tag also
secures the signature. See man pvsecret verify for more details. Optional. No
signature by default.
</ul>
`--use-name`
<ul>
Do not hash the name, use it directly as secret ID. Ignored for meta-secrets.
</ul>
`-h`, `--help`
<ul>
Print help
Print help (see a summary with '-h').
</ul>
@@ -265,13 +286,62 @@ of secrets.
`pvsecret create association [OPTIONS] <NAME>`
#### Description
Create an association secret. Use an association secret to connect a trusted I/O
device to a guest. The `pvapconfig` tool provides more information about
device to a guest. The 'pvapconfig' tool provides more information about
association secrets.
#### Arguments
`<NAME>`
<ul>
String to identify the new secret. The actual secret is set with --input-secret.
String that identifies the new secret. The actual secret is set with
'--input-secret'. The name is saved in `NAME.yaml` with white-spaces mapped to
`_`.
</ul>
#### Options
`--stdout`
<ul>
Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
</ul>
`--input-secret <SECRET-FILE>`
<ul>
Path from which to read the plaintext secret. Uses a random secret if not
specified.
</ul>
`--output-secret <SECRET-FILE>`
<ul>
Save the generated secret as plaintext in SECRET-FILE. The generated secret can
be used to generate add-secret requests for a different guest with the same
secret using '--input-secret'. Destroy the secret when it is not used anymore.
</ul>
`-h`, `--help`
<ul>
Print help (see a summary with '-h').
</ul>
### pvsecret create retrievable
#### Synopsis
`pvsecret create retrievable [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>`
`pvsecret create retr [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>`
#### Description
Create a retrievable secret. A retrievable secret is stored in the per-guest
storage of the Ultravisor. A SE-guest can retrieve the secret at runtime and use
it. All retrievable secrets, but the plaintext secret, are retrieved as
wrapped/protected key objects and only usable inside the current, running
SE-guest instance.
#### Arguments
`<NAME>`
<ul>
String that identifies the new secret. The actual secret is set with '--secret'.
The name is saved in `NAME.yaml` with white-spaces mapped to `_`.
</ul>
@@ -284,24 +354,28 @@ Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
</ul>
`--input-secret <FILE>`
`--secret <SECRET-FILE>`
<ul>
Path from which to read the plaintext secret. Uses a random secret if not
specified
Use SECRET-FILE as retrievable secret.
</ul>
`--output-secret <FILE>`
`--type <TYPE>`
<ul>
Save the generated secret as plaintext in FILE. The generated secret can be used
to generate add-secret requests for a different guest with the same secret using
--input-secret. Destroy the secret when it is not used anymore.
Specify the secret type. Limitations to the input data apply depending on the
secret type.
Possible values:
- **plain**: A plaintext secret. Can be any file up to 8190 bytes long.
- **aes**: An AES key. Must be a plain byte file 128, 192, or 256 bit long.
- **aes-xts**: An AES-XTS key. Must be a plain byte file 512, or 1024 bit long.
- **hmac-sha**: A HMAC-SHA key. Must be a plain byte file 512, or 1024 bit long.
- **ec**: An elliptic curve private key. Must be a PEM or DER file.
</ul>
`-h`, `--help`
<ul>
Print help
Print help (see a summary with '-h').
</ul>
@@ -309,13 +383,14 @@ Print help
### Synopsis
`pvsecret add <FILE>`
### Description
Perform an add-secret request (s390x only). Perform an add-secret request using
a previously generated add-secret request. Only available on s390x.
Submit an add-secret request to the Ultravisor (s390x only). Perform an
add-secret request using a previously generated add-secret request. Only
available on s390x.
### Arguments
`<FILE>`
<ul>
Specify the request to be sent
Specify the request to be sent.
</ul>
@@ -325,8 +400,8 @@ Specify the request to be sent
`pvsecret lock`
### Description
Lock the secret-store (s390x only). Lock the secret store (s390x only). After
this command executed successfully, all add-secret requests will fail. Only
available on s390x.
this command executed successfully, all subsequent add-secret requests will
fail. Only available on s390x.
## pvsecret list
### Synopsis
@@ -339,7 +414,7 @@ Execution guest. Only available on s390x.
`<FILE>`
<ul>
Store the result in FILE
Store the result in FILE.
Default value: '-'
</ul>
@@ -348,18 +423,18 @@ Store the result in FILE
`--format <FORMAT>`
<ul>
Define the output format of the list
Define the output format of the list.
Default value: 'human'
Possible values:
- **human**: Human-focused, non-parsable output format
- **yaml**: Use yaml format
- **bin**: Use the format the ultravisor uses to pass the list
- **human**: Human-focused, non-parsable output format.
- **yaml**: Use yaml format.
- **bin**: Use the format the ultravisor uses to pass the list.
</ul>
`-h`, `--help`
<ul>
Print help
Print help (see a summary with '-h').
</ul>
@@ -407,7 +482,7 @@ The verification process works as follows:
`<FILE>`
<ul>
Specify the request to be checked
Specify the request to be checked.
</ul>
@@ -435,5 +510,58 @@ contains this user-data with padded zeros if available.
`-h`, `--help`
<ul>
Print help
Print help (see a summary with '-h').
</ul>
## pvsecret retrieve
### Synopsis
`pvsecret retrieve [OPTIONS] <ID>`
`pvsecret retr [OPTIONS] <ID>`
### Description
Retrieve a secret from the UV secret store (s390x only)
### Arguments
`<ID>`
<ul>
Specify the secret ID to be retrieved. Input type depends on '--inform'. If
`yaml` (default) is specified, it must be a yaml created by the create
subcommand of this tool. If `hex` is specified, it must be a hex 32-byte
unsigned big endian number string. Leading zeros are required.
</ul>
### Options
`-o`, `--output <FILE>`
<ul>
Specify the output path to place the secret value.
Default value: '-'
</ul>
`--inform <INFORM>`
<ul>
Define input type for the Secret ID.
Default value: 'yaml'
Possible values:
- **yaml**: Use a yaml file.
- **hex**: Use a hex string.
- **name**: Use a name-string. Will hash it if no secret with the name found.
</ul>
`--outform <OUTFORM>`
<ul>
Define the output format for the retrieved secret.
Default value: 'pem'
Possible values:
- **pem**: Write the secret as PEM.
- **bin**: Write the secret in binary.
</ul>
`-h`, `--help`
<ul>
Print help (see a summary with '-h').
</ul>

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-add 1 "2024-05-21" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-ADD" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret add\fP - Perform an add-secret request (s390x only)
\fB
pvsecret-add \- Submit an add-secret request to the Ultravisor (s390x only)
.SH SYNOPSIS
.nf
.fam C
@@ -16,7 +15,7 @@ pvsecret add <FILE>
.fam C
.fi
.SH DESCRIPTION
Perform an add-secret request using a previously generated add-secret request.
Perform an add\-secret request using a previously generated add\-secret request.
Only available on s390x.
.SH OPTIONS
.PP
@@ -29,7 +28,7 @@ Specify the request to be sent.
.PP
\-h, \-\-help
.RS 4
Print help.
Print help (see a summary with \fB\-h\fR).
.RE
.RE

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-create-association 1 "2024-05-21" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-CREATE-ASSOCIATION" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret create association\fP - Create an association secret
\fB
pvsecret-create-association \- Create an association secret
.SH SYNOPSIS
.nf
.fam C
@@ -17,14 +16,14 @@ pvsecret create association [OPTIONS] <NAME>
.fi
.SH DESCRIPTION
Use an association secret to connect a trusted I/O device to a guest. The
`pvapconfig` tool provides more information about association secrets.
\fBpvapconfig\fR tool provides more information about association secrets.
.SH OPTIONS
.PP
<NAME>
.RS 4
String to identify the new secret. The actual secret is set with
\fB--input-secret\fR. The name is saved in `NAME.yaml` with white-spaces mapped
to `_`.
String that identifies the new secret. The actual secret is set with
\fB\-\-input\-secret\fR. The name is saved in `NAME.yaml` with white\-spaces
mapped to `_`.
.RE
.RE
@@ -35,24 +34,25 @@ Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
.RE
.RE
.PP
\-\-input-secret <FILE>
\-\-input\-secret <SECRET-FILE>
.RS 4
Path from which to read the plaintext secret. Uses a random secret if not
specified.
.RE
.RE
.PP
\-\-output-secret <FILE>
\-\-output\-secret <SECRET-FILE>
.RS 4
Save the generated secret as plaintext in FILE. The generated secret can be used
to generate add-secret requests for a different guest with the same secret using
\fB--input-secret\fR. Destroy the secret when it is not used anymore.
Save the generated secret as plaintext in SECRET\-FILE. The generated secret can
be used to generate add\-secret requests for a different guest with the same
secret using \fB\-\-input\-secret\fR. Destroy the secret when it is not used
anymore.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help.
Print help (see a summary with \fB\-h\fR).
.RE
.RE

View File

@@ -1,14 +1,13 @@
.\" Copyright 2023 IBM Corp.
.\" Copyright 2023, 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-create-meta 1 "2024-01-30" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-CREATE-META" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret create meta\fP - Create a meta secret
\fB
pvsecret-create-meta \- Create a meta secret
.SH SYNOPSIS
.nf
.fam C

View File

@@ -0,0 +1,74 @@
.\" Copyright 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH "PVSECRET-CREATE-RETRIEVABLE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
pvsecret-create-retrievable \- Create a retrievable secret
.SH SYNOPSIS
.nf
.fam C
pvsecret create retrievable [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>
pvsecret create retr [OPTIONS] --secret <SECRET-FILE> --type <TYPE> <NAME>
.fam C
.fi
.SH DESCRIPTION
A retrievable secret is stored in the per\-guest storage of the Ultravisor. A
SE\-guest can retrieve the secret at runtime and use it. All retrievable
secrets, but the plaintext secret, are retrieved as wrapped/protected key
objects and only usable inside the current, running SE\-guest instance.
.SH OPTIONS
.PP
<NAME>
.RS 4
String that identifies the new secret. The actual secret is set with
\fB\-\-secret\fR. The name is saved in `NAME.yaml` with white\-spaces mapped to
`_`.
.RE
.RE
.PP
\-\-stdout
.RS 4
Print the hashed name to stdout. The hashed name is not written to `NAME.yaml`
.RE
.RE
.PP
\-\-secret <SECRET-FILE>
.RS 4
Use SECRET\-FILE as retrievable secret.
.RE
.RE
.PP
\-\-type <TYPE>
.RS 4
Specify the secret type. Limitations to the input data apply depending on the
secret type.
Possible values:
.RS 4
\- \fBplain\fP: A plaintext secret. Can be any file up to 8190 bytes long.
\- \fBaes\fP: An AES key. Must be a plain byte file 128, 192, or 256 bit long.
\- \fBaes-xts\fP: An AES-XTS key. Must be a plain byte file 512, or 1024 bit long.
\- \fBhmac-sha\fP: A HMAC-SHA key. Must be a plain byte file 512, or 1024 bit long.
\- \fBec\fP: An elliptic curve private key. Must be a PEM or DER file.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help (see a summary with \fB\-h\fR).
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create\fR(1)

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-create 1 "2024-05-21" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-CREATE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret create\fP - Create a new add-secret request
\fB
pvsecret-create \- Create a new add-secret request
.SH SYNOPSIS
.nf
.fam C
@@ -29,39 +28,46 @@ bound to the Configuration Unique ID from \fBpvattest\fR using \fB--cuid\fR
.SH "PVSECRET CREATE COMMANDS"
.PP
\fBmeta\fR
\fBpvsecret create-meta(1)\fR
.RS 4
Create a meta secret
.RE
.PP
\fBassociation\fR
\fBpvsecret create-association(1)\fR
.RS 4
Create an association secret
.RE
.PP
\fBpvsecret create-retrievable(1)\fR
.RS 4
Create a retrievable secret
.RE
.SH OPTIONS
.PP
\-k, \-\-host-key-document <FILE>
\-k, \-\-host\-key\-document <FILE>
.RS 4
Use FILE as a host-key document. Can be specified multiple times and must be
used at least once.
Use FILE as a host\-key document. Can be specified multiple times and must be
specified at least once.
.RE
.RE
.PP
\-\-no-verify
\-\-no\-verify
.RS 4
Disable the host-key document verification. Does not require the host-key
Disable the host\-key document verification. Does not require the host\-key
documents to be valid. Do not use for a production request unless you verified
the host-key document beforehand.
the host\-key document beforehand.
.RE
.RE
.PP
\-C, \-\-cert <FILE>
.RS 4
Use FILE as a certificate to verify the host key or keys. The certificates are
used to establish a chain of trust for the verification of the host-key
Use FILE as a certificate to verify the host\-key or keys. The certificates are
used to establish a chain of trust for the verification of the host\-key
documents. Specify this option twice to specify the IBM Z signing key and the
intermediate CA certificate (signed by the root CA).
.RE
@@ -69,9 +75,9 @@ intermediate CA certificate (signed by the root CA).
.PP
\-\-crl <FILE>
.RS 4
Use FILE as a certificate revocation list. The list is used to check whether a
certificate of the chain of trust is revoked. Specify this option multiple times
to use multiple CRLs.
Use FILE as a certificate revocation list (CRL). The list is used to check
whether a certificate of the chain of trust is revoked. Specify this option
multiple times to use multiple CRLs.
.RE
.RE
.PP
@@ -81,27 +87,26 @@ Make no attempt to download CRLs.
.RE
.RE
.PP
\-\-root-ca <ROOT_CA>
\-\-root\-ca <ROOT_CA>
.RS 4
Use FILE as the root-CA certificate for the verification. If omitted, the system
wide-root CAs installed on the system are used. Use this only if you trust the
specified certificate.
Use FILE as the root\-CA certificate for the verification. If omitted, the
system wide\-root CAs installed on the system are used. Use this only if you
trust the specified certificate.
.RE
.RE
.PP
\-\-hdr <FILE>
.RS 4
Specifies the header of the guest image. Can be an IBM Secure Execution image
created by genprotimg or an extracted IBM Secure Execution header. The header
must start at a page boundary.
created by \fBpvimg/genprotimg\fR or an extracted IBM Secure Execution header.
.RE
.RE
.PP
\-f, \-\-force
.RS 4
Force the generation of add-secret requests on IBM Secure Execution guests. If
Force the generation of add\-secret requests on IBM Secure Execution guests. If
the program detects that it is running on an IBM Secure Execution guest, it
denies the generation of add-secret requests. The force flag overwrites this
denies the generation of add\-secret requests. The force flag overwrites this
behavior.
.RE
.RE
@@ -112,7 +117,7 @@ Write the generated request to FILE.
.RE
.RE
.PP
\-\-extension-secret <FILE>
\-\-extension\-secret <FILE>
.RS 4
Use the content of FILE as an extension secret. The file must be exactly 32
bytes long. If this request is the first, all subsequent requests must have the
@@ -124,7 +129,7 @@ request.
.PP
\-\-cck <FILE>
.RS 4
Use the content of FILE as the customer-communication key (CCK) to derive the
Use the content of FILE as the customer\-communication key (CCK) to derive the
extension secret. The file must contain exactly 32 bytes of data. If the target
guest was started with bit 1 of the secret control flag set, the ultravisor also
derives the secret from the CCK. Otherwise, the ultravisor interprets the
@@ -133,13 +138,13 @@ all requests.
.RE
.RE
.PP
\-\-cuid-hex <HEXSTRING>
\-\-cuid\-hex <HEXSTRING>
.RS 4
Use HEXSTRING as the Configuration Unique ID. Must be a hex 128-bit unsigned big
endian number string. Leading zeros must be provided. If specified, the value
must match with the Config-UID from the attestation result of that guest. If not
specified, the CUID will be ignored by the ultravisor during the verification of
the request.
Use HEXSTRING as the Configuration Unique ID. Must be a hex 128\-bit unsigned
big endian number string. Leading zeros must be provided. If specified, the
value must match with the Config\-UID from the attestation result of that guest.
If not specified, the CUID will be ignored by the ultravisor during the
verification of the request.
.RE
.RE
.PP
@@ -147,7 +152,7 @@ the request.
.RS 4
Use the content of FILE as the Configuration Unique ID. The file must contain
exactly 128 bit of data or a yaml with a `cuid` entry. If specified, the value
must match the Config-UID from the attestation result of that guest. If not
must match the Config\-UID from the attestation result of that guest. If not
specified, the CUID will be ignored by the Ultravisor during the verification of
the request.
.RE
@@ -155,52 +160,58 @@ the request.
.PP
\-\-flags <FLAGS>
.RS 4
Flags for the add-secret request.
Flags for the add\-secret request.
Possible values:
.RS 4
- \fBdisable-dump\fP: Disables host-initiated dumping for the target guest instance.
\- \fBdisable-dump\fP: Disables host-initiated dumping for the target guest instance.
.RE
.RE
.PP
\-\-user-data <FILE>
\-\-user\-data <FILE>
.RS 4
Use the content of FILE as user-data. Passes user data defined in <FILE> through
the add-secret request to the ultravisor. The user data can be up to 512 bytes
of arbitrary data, and the maximum size depends on the size of the user-signing
Use the content of FILE as user\-data. Passes user data defined in FILE through
the add\-secret request to the ultravisor. The user data can be up to 512 bytes
of arbitrary data, and the maximum size depends on the size of the user\-signing
key:
- No key: user data can be 512 bytes.
\- No key: user data can be 512 bytes.
- EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes.
\- EC(secp521r1) or RSA 2048 keys: user data can be 256 bytes.
- RSA 3072 key: user data can be 128 bytes.
\- RSA 3072 key: user data can be 128 bytes.
The firmware ignores this data, but the request tag protects the user-data.
Optional. No user-data by default.
The firmware ignores this data, but the request tag protects the user\-data.
Optional. No user\-data by default.
.RE
.RE
.PP
\-\-user-sign-key <FILE>
\-\-user\-sign\-key <FILE>
.RS 4
Use the content of FILE as user signing key. Adds a signature calculated from
the key in <FILE> to the add-secret request. The file must be in DER or PEM
format containing a private key. Supported are RSA 2048 & 3072-bit and
the key in FILE to the add\-secret request. The file must be in DER or PEM
format containing a private key. Supported are RSA 2048 & 3072\-bit and
EC(secp521r1) keys. The firmware ignores the content, but the request tag
protects the signature. The user-signing key signs the request. The location of
protects the signature. The user\-signing key signs the request. The location of
the signature is filled with zeros during the signature calculation. The request
tag also secures the signature. See man pvsecret verify for more details.
Optional. No signature by default.
.RE
.RE
.PP
\-\-use\-name
.RS 4
Do not hash the name, use it directly as secret ID. Ignored for meta\-secrets.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help.
Print help (see a summary with \fB\-h\fR).
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1) \fBpvsecret-create-meta\fR(1) \fBpvsecret-create-association\fR(1)
\fBpvsecret\fR(1) \fBpvsecret-create-meta\fR(1) \fBpvsecret-create-association\fR(1) \fBpvsecret-create-retrievable\fR(1)

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-list 1 "2024-05-21" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-LIST" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret list\fP - List all ultravisor secrets (s390x only)
\fB
pvsecret-list \- List all ultravisor secrets (s390x only)
.SH SYNOPSIS
.nf
.fam C
@@ -16,8 +15,8 @@ pvsecret list [OPTIONS] [FILE]
.fam C
.fi
.SH DESCRIPTION
Lists the IDs of all non-null secrets currently stored in the ultravisor for the
currently running IBM Secure Execution guest. Only available on s390x.
Lists the IDs of all non\-null secrets currently stored in the ultravisor for
the currently running IBM Secure Execution guest. Only available on s390x.
.SH OPTIONS
.PP
<FILE>
@@ -35,18 +34,18 @@ Define the output format of the list.
Possible values:
.RS 4
- \fBhuman\fP: Human-focused, non-parsable output format.
\- \fBhuman\fP: Human-focused, non-parsable output format.
- \fByaml\fP: Use yaml format.
\- \fByaml\fP: Use yaml format.
- \fBbin\fP: Use the format the ultravisor uses to pass the list.
\- \fBbin\fP: Use the format the ultravisor uses to pass the list.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help.
Print help (see a summary with \fB\-h\fR).
.RE
.RE

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-lock 1 "2024-05-15" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-LOCK" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret lock\fP - Lock the secret-store (s390x only)
\fB
pvsecret-lock \- Lock the secret-store (s390x only)
.SH SYNOPSIS
.nf
.fam C
@@ -17,7 +16,7 @@ pvsecret lock
.fi
.SH DESCRIPTION
Lock the secret store (s390x only). After this command executed successfully,
all add-secret requests will fail. Only available on s390x.
all subsequent add\-secret requests will fail. Only available on s390x.
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)

View File

@@ -0,0 +1,77 @@
.\" Copyright 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH "PVSECRET-RETRIEVE" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
pvsecret-retrieve \- Retrieve a secret from the UV secret store (s390x only)
.SH SYNOPSIS
.nf
.fam C
pvsecret retrieve [OPTIONS] <ID>
pvsecret retr [OPTIONS] <ID>
.fam C
.fi
.SH DESCRIPTION
Retrieve a secret from the UV secret store (s390x only)
.SH OPTIONS
.PP
<ID>
.RS 4
Specify the secret ID to be retrieved. Input type depends on \fB\-\-inform\fR.
If `yaml` (default) is specified, it must be a yaml created by the create
subcommand of this tool. If `hex` is specified, it must be a hex 32\-byte
unsigned big endian number string. Leading zeros are required.
.RE
.RE
.PP
\-o, \-\-output <FILE>
.RS 4
Specify the output path to place the secret value.
[default: '-']
.RE
.RE
.PP
\-\-inform <INFORM>
.RS 4
Define input type for the Secret ID.
[default: 'yaml']
Possible values:
.RS 4
\- \fByaml\fP: Use a yaml file.
\- \fBhex\fP: Use a hex string.
\- \fBname\fP: Use a name-string. Will hash it if no secret with the name found.
.RE
.RE
.PP
\-\-outform <OUTFORM>
.RS 4
Define the output format for the retrieved secret.
[default: 'pem']
Possible values:
.RS 4
\- \fBpem\fP: Write the secret as PEM.
\- \fBbin\fP: Write the secret in binary.
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help (see a summary with \fB\-h\fR).
.RE
.RE
.SH "SEE ALSO"
.sp
\fBpvsecret\fR(1)

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret-verify 1 "2024-05-21" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET-VERIFY" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret verify\fP - Verify that an add-secret request is sane
\fB
pvsecret-verify \- Verify that an add-secret request is sane
.SH SYNOPSIS
.nf
.fam C
@@ -89,12 +88,12 @@ Specify the request to be checked.
.RE
.PP
\-\-user-cert <FILE>
\-\-user\-cert <FILE>
.RS 4
Certificate containing a public key used to verify the user data signature.
Specifies a public key used to verify the user-data signature. The file must be
Specifies a public key used to verify the user\-data signature. The file must be
a X509 certificate in DSA or PEM format. The certificate must hold the public
EC, RSA 2048, or RSA 3072 key corresponding to the private user-key used during
EC, RSA 2048, or RSA 3072 key corresponding to the private user\-key used during
`create`. No chain of trust is established. Ensuring that the certificate can be
trusted is the responsibility of the user. The EC key must use the NIST/SECG
curve over a 521 bit prime field (secp521r1).
@@ -103,15 +102,15 @@ curve over a 521 bit prime field (secp521r1).
.PP
\-o, \-\-output <FILE>
.RS 4
Store the result in FILE If the request contained abirtary user-data the output
contains this user-data with padded zeros if available.
Store the result in FILE If the request contained abirtary user\-data the output
contains this user\-data with padded zeros if available.
[default: '-']
.RE
.RE
.PP
\-h, \-\-help
.RS 4
Print help.
Print help (see a summary with \fB\-h\fR).
.RE
.RE

View File

@@ -3,12 +3,11 @@
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
.TH pvsecret 1 "2024-05-21" "s390-tools" "UV-Secret Manual"
.TH "PVSECRET" "1" "2024-12-19" "s390-tools" "UV-Secret Manual"
.nh
.ad l
.SH NAME
\fBpvsecret\fP - Manage secrets for IBM Secure Execution guests
\fB
pvsecret \- Manage secrets for IBM Secure Execution guests
.SH SYNOPSIS
.nf
.fam C
@@ -36,39 +35,46 @@ both the PEM and DER input formats are supported.
.SH "PVSECRET COMMANDS"
.PP
\fBcreate\fR
\fBpvsecret-create(1)\fR
.RS 4
Create a new add-secret request
.RE
.PP
\fBadd\fR
\fBpvsecret-add(1)\fR
.RS 4
Perform an add-secret request (s390x only)
Submit an add-secret request to the Ultravisor (s390x only)
.RE
.PP
\fBlock\fR
\fBpvsecret-lock(1)\fR
.RS 4
Lock the secret-store (s390x only)
.RE
.PP
\fBlist\fR
\fBpvsecret-list(1)\fR
.RS 4
List all ultravisor secrets (s390x only)
.RE
.PP
\fBverify\fR
\fBpvsecret-verify(1)\fR
.RS 4
Verify that an add-secret request is sane
.RE
.PP
\fBpvsecret-retrieve(1)\fR
.RS 4
Retrieve a secret from the UV secret store (s390x only)
.RE
.SH OPTIONS
.PP
\-v, \-\-verbose
@@ -77,6 +83,12 @@ Provide more detailed output.
.RE
.RE
.PP
\-q, \-\-quiet
.RS 4
Provide less output.
.RE
.RE
.PP
\-\-version
.RS 4
Print version information and exit.
@@ -85,7 +97,7 @@ Print version information and exit.
.PP
\-h, \-\-help
.RS 4
Print help.
Print help (see a summary with \fB\-h\fR).
.RE
.RE
@@ -138,4 +150,4 @@ On the SE-guest, \fIlock\fP the secret store.
.fi
.SH "SEE ALSO"
.sp
\fBpvsecret-create\fR(1) \fBpvsecret-add\fR(1) \fBpvsecret-lock\fR(1) \fBpvsecret-list\fR(1) \fBpvsecret-verify\fR(1)
\fBpvsecret-create\fR(1) \fBpvsecret-add\fR(1) \fBpvsecret-lock\fR(1) \fBpvsecret-list\fR(1) \fBpvsecret-verify\fR(1) \fBpvsecret-retrieve\fR(1)

View File

@@ -1,7 +1,10 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
// Copyright IBM Corp. 2023, 2024
use std::fmt::Display;
use clap::error::ErrorKind::ValueValidation;
use clap::{ArgGroup, Args, CommandFactory, Parser, Subcommand, ValueEnum, ValueHint};
use utils::{CertificateOptions, DeprecatedVerbosityOptions, STDOUT};
@@ -37,8 +40,8 @@ pub struct CreateSecretOpt {
/// Specifies the header of the guest image.
///
/// Can be an IBM Secure Execution image created by genprotimg or an extracted IBM Secure
/// Execution header. The header must start at a page boundary.
/// Can be an IBM Secure Execution image created by 'pvimg/genprotimg' or an
/// extracted IBM Secure Execution header.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath)]
pub hdr: String,
@@ -138,6 +141,12 @@ pub struct CreateSecretOpt {
/// by default.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
pub user_sign_key: Option<String>,
/// Do not hash the name, use it directly as secret ID.
///
/// Ignored for meta-secrets.
#[arg(long)]
pub use_name: bool,
}
#[derive(Subcommand, Debug)]
@@ -150,12 +159,12 @@ pub enum AddSecretType {
/// Create an association secret.
///
/// Use an association secret to connect a trusted I/O device to a guest. The `pvapconfig` tool
/// Use an association secret to connect a trusted I/O device to a guest. The 'pvapconfig' tool
/// provides more information about association secrets.
Association {
/// String to identify the new secret.
/// String that identifies the new secret.
///
/// The actual secret is set with --input-secret. The name is saved in `NAME.yaml` with
/// The actual secret is set with '--input-secret'. The name is saved in `NAME.yaml` with
/// white-spaces mapped to `_`.
name: String,
@@ -166,17 +175,83 @@ pub enum AddSecretType {
stdout: bool,
/// Path from which to read the plaintext secret. Uses a random secret if not specified.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
#[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath, conflicts_with("output_secret"))]
input_secret: Option<String>,
/// Save the generated secret as plaintext in FILE.
/// Save the generated secret as plaintext in SECRET-FILE.
///
/// The generated secret can be used to generate add-secret requests for a different guest
/// with the same secret using --input-secret. Destroy the secret when it is not used
/// with the same secret using '--input-secret'. Destroy the secret when it is not used
/// anymore.
#[arg(long, value_name = "FILE", value_hint = ValueHint::FilePath,)]
#[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath,)]
output_secret: Option<String>,
},
/// Create a retrievable secret.
///
/// A retrievable secret is stored in the per-guest storage of the Ultravisor. A SE-guest can
/// retrieve the secret at runtime and use it. All retrievable secrets, but the plaintext
/// secret, are retrieved as wrapped/protected key objects and only usable inside the current,
/// running SE-guest instance.
#[command(visible_alias = "retr")]
Retrievable {
/// String that identifies the new secret.
///
/// The actual secret is set with '--secret'. The name is saved in `NAME.yaml` with
/// white-spaces mapped to `_`.
name: String,
/// Print the hashed name to stdout.
///
/// The hashed name is not written to `NAME.yaml`
#[arg(long)]
stdout: bool,
/// Use SECRET-FILE as retrievable secret
#[arg(long, value_name = "SECRET-FILE", value_hint = ValueHint::FilePath)]
secret: String,
/// Specify the secret type.
///
/// Limitations to the input data apply depending on the secret type.
#[arg(long = "type", value_name = "TYPE")]
kind: RetrieveableSecretInpKind,
},
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
pub enum RetrieveableSecretInpKind {
/// A plaintext secret.
/// Can be any file up to 8190 bytes long
Plain,
/// An AES key.
/// Must be a plain byte file 128, 192, or 256 bit long.
Aes,
/// An AES-XTS key.
/// Must be a plain byte file 512, or 1024 bit long.
AesXts,
/// A HMAC-SHA key.
/// Must be a plain byte file 512, or 1024 bit long.
HmacSha,
/// An elliptic curve private key.
/// Must be a PEM or DER file.
Ec,
}
impl Display for RetrieveableSecretInpKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Plain => "PLAINTEXT",
Self::Aes => "AES KEY",
Self::AesXts => "AES-XTS KEY",
Self::HmacSha => "HMAC-SHA KEY",
Self::Ec => "EC PRIVATE KEY",
}
)
}
}
// all members s390x only
@@ -238,18 +313,70 @@ pub struct VerifyOpt {
pub output: String,
}
// all members s390x only
#[derive(Args, Debug)]
pub struct RetrSecretOptions {
/// Specify the secret ID to be retrieved.
///
/// Input type depends on '--inform'. If `yaml` (default) is specified, it must be a yaml
/// created by the create subcommand of this tool. If `hex` is specified, it must be a hex
/// 32-byte unsigned big endian number string. Leading zeros are required.
#[cfg(target_arch = "s390x")]
#[arg(value_name = "ID", value_hint = ValueHint::FilePath)]
pub input: String,
/// Specify the output path to place the secret value
#[cfg(target_arch = "s390x")]
#[arg(short, long, value_name = "FILE", default_value = STDOUT, value_hint = ValueHint::FilePath)]
pub output: String,
/// Define input type for the Secret ID
#[cfg(target_arch = "s390x")]
#[arg(long, value_enum, default_value_t)]
pub inform: RetrInpFmt,
/// Define the output format for the retrieved secret
#[cfg(target_arch = "s390x")]
#[arg(long, value_enum, default_value_t)]
pub outform: RetrOutFmt,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
pub enum RetrInpFmt {
/// Use a yaml file
#[default]
Yaml,
/// Use a hex string.
Hex,
/// Use a name-string. Will hash it if no secret with the name found.
Name,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default)]
pub enum RetrOutFmt {
/// Write the secret as PEM.
///
/// File starts with `-----BEGIN IBM PROTECTED KEY----` and `-----BEGIN
/// PLAINTEXT SECRET-----` for plaintext secrets it contains one header
/// line with the type information and the base64 protected key
#[default]
Pem,
/// Write the secret in binary.
Bin,
}
#[derive(Subcommand, Debug)]
pub enum Command {
/// Create a new add-secret request.
///
/// Create add-secret requests for IBM Secure Execution guests. Only create these requests in a
/// trusted environment, such as your workstation. The `pvattest create` command creates a
/// trusted environment, such as your workstation. The 'pvattest create' command creates a
/// randomly generated key to protect the request. The generated requests can then be added on
/// an IBM Secure Execution guest using `pvsecret add`. The guest can then use the secrets with
/// an IBM Secure Execution guest using 'pvsecret add'. The guest can then use the secrets with
/// the use case depending on the secret type.
Create(Box<CreateSecretOpt>),
/// Perform an add-secret request (s390x only).
/// Submit an add-secret request to the Ultravisor (s390x only).
///
/// Perform an add-secret request using a previously generated add-secret request. Only
/// available on s390x.
@@ -258,7 +385,7 @@ pub enum Command {
/// Lock the secret-store (s390x only).
///
/// Lock the secret store (s390x only). After this command executed successfully, all
/// add-secret requests will fail. Only available on s390x.
/// subsequent add-secret requests will fail. Only available on s390x.
Lock,
/// List all ultravisor secrets (s390x only).
@@ -274,6 +401,10 @@ pub enum Command {
/// provided key. Outputs the arbitrary user-data.
Verify(VerifyOpt),
/// Retrieve a secret from the UV secret store (s390x only).
#[command(visible_alias = "retr")]
Retrieve(RetrSecretOptions),
/// Print version information and exit.
#[command(aliases(["--version"]), hide(true))]
Version,
@@ -294,13 +425,13 @@ pub fn validate_cli(cli: &CliOptions) -> Result<(), clap::Error> {
}
if secret_out == &Some(format!("{name}.yaml")) {
return Err(CliOptions::command().error(
clap::error::ErrorKind::ValueValidation,
ValueValidation,
format!("Secret output file and the secret name '{name}.yaml' are the same."),
));
}
if format!("{name}.yaml") == opt.output {
return Err(CliOptions::command().error(
clap::error::ErrorKind::ValueValidation,
ValueValidation,
format!(
"output file and the secret name '{}' are the same.",
&opt.output

View File

@@ -16,6 +16,8 @@ mod add;
mod list;
#[cfg(target_arch = "s390x")]
mod lock;
#[cfg(target_arch = "s390x")]
mod retr;
// Commands (directly) related to UVCs are only available on s389x
#[cfg(target_arch = "s390x")]
@@ -24,12 +26,13 @@ mod uv_cmd {
pub use add::add;
pub use list::list;
pub use lock::lock;
pub use retr::retr;
pub const UV_CMD_FN: &[&str] = &["+add", "+lock", "+list"];
}
#[cfg(not(target_arch = "s390x"))]
mod uv_cmd {
use crate::cli::{AddSecretOpt, ListSecretOpt};
use crate::cli::{AddSecretOpt, ListSecretOpt, RetrSecretOptions};
use anyhow::{bail, Result};
macro_rules! not_supp {
($name: ident $( ,$opt: ty )?) => {
@@ -40,6 +43,7 @@ mod uv_cmd {
}
not_supp!(add, AddSecretOpt);
not_supp!(list, ListSecretOpt);
not_supp!(retr, RetrSecretOptions);
not_supp!(lock);
pub const UV_CMD_FN: &[&str] = &[];
}

View File

@@ -4,7 +4,6 @@
use std::path::Path;
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt};
use anyhow::{anyhow, bail, Context, Error, Result};
use log::{debug, info, trace, warn};
use pv::{
@@ -22,6 +21,8 @@ use pv::{
use serde_yaml::Value;
use utils::get_writer_from_cli_file_arg;
use crate::cli::{AddSecretType, CreateSecretFlags, CreateSecretOpt, RetrieveableSecretInpKind};
fn write_out<P, D>(path: &P, data: D, ctx: &str) -> pv::Result<()>
where
P: AsRef<Path>,
@@ -32,6 +33,23 @@ where
Ok(())
}
fn retrievable(name: &str, secret: &str, kind: &RetrieveableSecretInpKind) -> Result<GuestSecret> {
let secret_data = read_file(secret, &format!("retrievable {kind}"))?.into();
match kind {
RetrieveableSecretInpKind::Plain => GuestSecret::plaintext(name, secret_data),
RetrieveableSecretInpKind::Aes => GuestSecret::aes(name, secret_data),
RetrieveableSecretInpKind::AesXts => GuestSecret::aes_xts(name, secret_data),
RetrieveableSecretInpKind::HmacSha => GuestSecret::hmac_sha(name, secret_data),
RetrieveableSecretInpKind::Ec => GuestSecret::ec(
name,
read_private_key(secret_data.value())
.with_context(|| format!("Cannot read {secret} as {kind} from PEM or DER"))?,
),
}
.map_err(Error::from)
}
/// Prepare an add-secret request
pub fn create(opt: &CreateSecretOpt) -> Result<()> {
if pv_guest_bit_set() {
@@ -62,7 +80,7 @@ pub fn create(opt: &CreateSecretOpt) -> Result<()> {
write_out(&opt.output, ser_asrbc, "add-secret request")?;
info!("Successfully wrote the request to '{}'", &opt.output);
write_secret(&opt.secret, &asrcb, &opt.output)
write_secret(&opt.secret, asrcb.guest_secret(), &opt.output)
}
/// Read+parse the first key from the buffer.
@@ -76,7 +94,7 @@ fn read_private_key(buf: &[u8]) -> Result<PKey<Private>> {
fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
debug!("Build add-secret request");
let secret = match &opt.secret {
let mut secret = match &opt.secret {
AddSecretType::Meta => GuestSecret::Null,
AddSecretType::Association {
name,
@@ -88,9 +106,14 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
input_secret: None,
..
} => GuestSecret::association(name, None)?,
AddSecretType::Retrievable {
name, secret, kind, ..
} => retrievable(name, secret, kind)?,
};
trace!("AddSecret: {secret:x?}");
opt.use_name.then(|| secret.no_hash_name());
let mut flags = match &opt.pcf {
Some(v) => (&try_parse_u64(v, "pcf")?).into(),
None => AddSecretFlags::default(),
@@ -136,7 +159,9 @@ fn build_asrcb(opt: &CreateSecretOpt) -> Result<AddSecretRequest> {
.as_ref()
.map(|p| read_file(p, "User-signing key"))
.transpose()?
.map(|buf| read_private_key(&buf))
.map(|buf| {
read_private_key(&buf).context("Cannot read {secret} as private key from PEM or DER")
})
.transpose()?;
if user_data.is_some() || user_key.is_some() {
@@ -206,54 +231,62 @@ fn read_cuid(asrcb: &mut AddSecretRequest, opt: &CreateSecretOpt) -> Result<()>
Ok(())
}
// Write non confidential data (=name+id) to a yaml stdout
fn write_yaml<P: AsRef<Path>>(
name: &str,
guest_secret: &GuestSecret,
stdout: &bool,
outp_path: P,
) -> Result<()> {
debug!("Non-confidential secret information: {guest_secret:x?}");
let secret_info = serde_yaml::to_string(guest_secret)?;
if stdout.to_owned() {
println!("{secret_info}");
return Ok(());
}
let gen_name: String = name
.chars()
.map(|c| if c.is_whitespace() { '_' } else { c })
.collect();
let mut yaml_path = outp_path
.as_ref()
.parent()
.with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
.to_owned();
yaml_path.push(gen_name);
yaml_path.set_extension("yaml");
write_out(&yaml_path, secret_info, "secret information")?;
warn!(
"Successfully wrote secret info to '{}'",
yaml_path.display().to_string()
);
Ok(())
}
/// Write the generated secret (if any) to the specified output stream
fn write_secret<P: AsRef<Path>>(
secret: &AddSecretType,
asrcb: &AddSecretRequest,
guest_secret: &GuestSecret,
outp_path: P,
) -> Result<()> {
if let AddSecretType::Association {
name,
stdout,
output_secret: secret_out,
..
} = secret
{
let gen_name: String = name
.chars()
.map(|c| if c.is_whitespace() { '_' } else { c })
.collect();
let mut gen_path = outp_path
.as_ref()
.parent()
.with_context(|| format!("Cannot open directory of {:?}", outp_path.as_ref()))?
.to_owned();
gen_path.push(format!("{gen_name}.yaml"));
// write non confidential data (=name+id) to a yaml
let secret_info = serde_yaml::to_string(asrcb.guest_secret())?;
if stdout.to_owned() {
println!("{secret_info}");
} else {
write_out(&gen_path, secret_info, "association secret info")?;
debug!(
"Non-confidential secret information: {:x?}",
asrcb.guest_secret()
);
warn!(
"Successfully wrote association info to '{}'",
gen_path.display()
);
}
if let Some(path) = secret_out {
if let GuestSecret::Association { secret, .. } = asrcb.guest_secret() {
write_out(path, secret.value(), "Association secret")?
} else {
unreachable!("The secret type has to be `association` at this point (bug)!")
match secret {
AddSecretType::Association {
name,
stdout,
output_secret,
..
} => {
write_yaml(name, guest_secret, stdout, outp_path)?;
if let Some(path) = output_secret {
write_out(path, guest_secret.confidential(), "Association secret")?
}
info!("Successfully wrote generated association secret to '{path}'");
}
AddSecretType::Retrievable { name, stdout, .. } => {
write_yaml(name, guest_secret, stdout, outp_path)?
}
_ => (),
};
Ok(())
}

View File

@@ -2,22 +2,40 @@
//
// Copyright IBM Corp. 2023
use std::io::ErrorKind;
use crate::cli::{ListSecretOpt, ListSecretOutputType};
use anyhow::{Context, Result};
use log::warn;
use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess};
use anyhow::{Context, Error, Result};
use log::{info, warn};
use pv::uv::{ListCmd, SecretList, UvDevice};
use utils::{get_writer_from_cli_file_arg, STDOUT};
const SECRET_LIST_BUF_SIZE: usize = 4;
/// Do a List Secrets UVC
pub fn list_uvc(uv: &UvDevice) -> Result<SecretList> {
let mut cmd = ListCmd::with_pages(SECRET_LIST_BUF_SIZE);
let more_data = match uv.send_cmd(&mut cmd) {
Ok(v) => Ok(v),
Err(pv::PvCoreError::Io(e)) if e.kind() == ErrorKind::InvalidInput => {
info!("Uvdevice does not suport longer list. Fallback to one page list.");
cmd = ListCmd::default();
uv.send_cmd(&mut cmd)
}
Err(e) => Err(e),
}?
.more_data();
if more_data {
warn!("The secret list contains more data but the uvdevice cannot show all.");
}
cmd.try_into().map_err(Error::new)
}
/// Do a List Secrets UVC and output the list in the requested format
pub fn list(opt: &ListSecretOpt) -> Result<()> {
let uv = UvDevice::open()?;
let mut cmd = ListCmd::default();
match uv.send_cmd(&mut cmd)? {
UvcSuccess::RC_SUCCESS => (),
UvcSuccess::RC_MORE_DATA => warn!("There is more data available than expected"),
};
let secret_list: SecretList = cmd.try_into()?;
let secret_list = list_uvc(&uv)?;
let mut wr_out = get_writer_from_cli_file_arg(&opt.output)?;
match &opt.format {

View File

@@ -0,0 +1,68 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2024
use super::list::list_uvc;
use crate::cli::{RetrInpFmt, RetrOutFmt, RetrSecretOptions};
use anyhow::{anyhow, bail, Context, Result};
use log::{debug, info};
use pv::{
misc::open_file,
misc::write,
secret::{GuestSecret, RetrievedSecret},
uv::{RetrieveCmd, SecretId, UvDevice},
};
use utils::get_writer_from_cli_file_arg;
fn retrieve(id: &SecretId) -> Result<RetrievedSecret> {
let uv = UvDevice::open()?;
let secrets = list_uvc(&uv)?;
let secret = match secrets.find(id) {
Some(s) => s,
// hash it + try again if it is ASCII-representable
None => match id.as_ascii() {
Some(s) => secrets.find(&GuestSecret::name_to_id(s)?),
None => None,
}
.ok_or(anyhow!(
"The UV secret-store has no secret with the ID {id}"
))?,
};
info!("Try to retrieve secret at index: {}", secret.index());
debug!("Try to retrieve: {secret:?}");
let mut uv_cmd = RetrieveCmd::from_entry(secret)?;
uv.send_cmd(&mut uv_cmd)?;
Ok(RetrievedSecret::from_cmd(uv_cmd))
}
pub fn retr(opt: &RetrSecretOptions) -> Result<()> {
let mut output = get_writer_from_cli_file_arg(&opt.output)?;
let id = match &opt.inform {
RetrInpFmt::Yaml => match serde_yaml::from_reader(&mut open_file(&opt.input)?)? {
GuestSecret::Retrievable { id, .. } => id,
gs => bail!("The file contains a {gs}-secret, which is not retrievable."),
},
RetrInpFmt::Hex => {
serde_yaml::from_str(&opt.input).context("Cannot parse SecretId information")?
}
RetrInpFmt::Name => SecretId::from_string(&opt.input),
};
let retr_secret =
retrieve(&id).context("Could not retrieve the secret from the UV secret store.")?;
let out_data = match opt.outform {
RetrOutFmt::Bin => retr_secret.into_bytes(),
RetrOutFmt::Pem => retr_secret.to_pem()?.into_bytes(),
};
write(
&mut output,
out_data.value(),
&opt.output,
"IBM Protected Key",
)?;
Ok(())
}

View File

@@ -45,6 +45,7 @@ fn main() -> ExitCode {
Command::Create(opt) => cmd::create(opt),
Command::Version => Ok(print_version!("2024", log_level; FEATURES.concat())),
Command::Verify(opt) => cmd::verify(opt),
Command::Retrieve(opt) => cmd::retr(opt),
};
match res {

View File

@@ -5,8 +5,8 @@ edition.workspace = true
license.workspace = true
[dependencies]
clap = { version ="4.1", features = ["derive", "wrap_help"] }
libc = "0.2.49"
log = { version = "0.4.6", features = ["std", "release_max_level_debug"] }
clap = { version ="4.5", features = ["derive", "wrap_help"] }
libc = "0.2.169"
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
pv = { path = "../pv", package = "s390_pv" }
serde = { version = "1.0.139"}
serde = { version = "1.0.217"}

View File

@@ -16,13 +16,14 @@ fn mkdtemp<P: AsRef<Path>>(template: P) -> Result<PathBuf, std::io::Error> {
// SAFETY: template_raw is a valid CString because it was generated by
// the `CString::new`.
let ret = libc::mkdtemp(template_raw);
// SAFETY: `template_raw` is still a valid CString because it was
// generated by `CString::new` and modified by `libc::mkdtemp`.
let path_cstr = std::ffi::CString::from_raw(template_raw);
if ret.is_null() {
drop(path_cstr);
Err(std::io::Error::last_os_error())
} else {
// SAFETY: `template_raw` is still a valid CString because it was
// generated by `CString::new` and modified by `libc::mkdtemp`.
let path_cstr = std::ffi::CString::from_raw(template_raw);
let path = OsStr::from_bytes(path_cstr.as_bytes());
let path = std::path::PathBuf::from(path);

View File

@@ -2,7 +2,7 @@
#
# dbginfo.sh - Tool to collect runtime, configuration, and trace information
#
# Copyright IBM Corp. 2002, 2024
# Copyright IBM Corp. 2002, 2025
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
@@ -38,6 +38,8 @@ readonly LOG_FILE_SIZE_CHECK=50 # max logfile size in MB
readonly LOG_FILE_AGE_CHECK=7 # age in days to include for size checking
# Mount point of the debug file system
readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug"
# Network devices
readonly NETWORK_DEVS=$(cd /sys/class/net; ls -d */ 2>/dev/null | sed 's/\///g')
# distro info
readonly OSPRETTY="$(cat /etc/os* 2>/dev/null | grep -m1 PRETTY_NAME | sed 's/\"//g')"
readonly OS_NAME="${OSPRETTY##*=}"
@@ -81,7 +83,7 @@ paramWORKDIR_BASE="/tmp" # initial default path
print_version() {
cat <<EOF
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
Copyright IBM Corp. 2002, 2024
Copyright IBM Corp. 2002, 2025
EOF
}
@@ -117,8 +119,6 @@ Options:
-v|--version print version information
-c|--check online base system information (no data collection)
Please report bugs to: linux390@de.ibm.com
EOF
}
@@ -374,6 +374,7 @@ LOGFILES="\
/var/log/dracut.log*\
/var/log/IBMtape.trace\
/var/log/IBMtape.errorlog\
/var/log/kdump.log\
/var/log/libvirt\
/var/log/lin_tape.trace\
/var/log/lin_tape.errorlog\
@@ -475,6 +476,7 @@ CMDS="uname -a\
:last\
:lscpu -ae\
:lscpu -ye\
:lscpumf -i\
:lsmem\
:lsmod\
:lsshut\
@@ -542,6 +544,8 @@ CMDS="${CMDS}\
# crypto specific commands
CMDS="${CMDS}\
:cpacfinfo # for MSA 13 info\
:cpacfinfo -m -f -a -n\
:ep11info -D\
:ep11info -H\
:ep11info -M\
@@ -1014,20 +1018,20 @@ collect_initrd_configfiles() {
########################################
collect_osaoat() {
local network_devices
local network_device
local osa_devices
local osa_device
network_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g' \
| sed 's/[()]//g' )
if type qethqoat >/dev/null; then
if test -n "${network_devices}"; then
osa_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/D.*:[[:space:]]*\([^[:space:]]*\)[[:space:]]\+/\1/g' \
| sed 's/[()]//g' )
if test -n "${osa_devices}"; then
pr_collect_output "osa oat"
for network_device in ${network_devices}; do
call_run_command "qethqoat ${network_device}" \
for osa_device in ${osa_devices}; do
call_run_command "qethqoat ${osa_device}" \
"${OUTPUT_FILE_OSAOAT}.out" &&
call_run_command "qethqoat -r ${network_device}" \
"${OUTPUT_FILE_OSAOAT}_${network_device}.raw"
call_run_command "qethqoat -r ${osa_device}" \
"${OUTPUT_FILE_OSAOAT}_${osa_device}.raw"
done
else
pr_skip "osa oat: no devices"
@@ -1039,14 +1043,12 @@ collect_osaoat() {
########################################
collect_ethtool() {
local network_devices
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type ethtool >/dev/null; then
if test -n "${network_devices}"; then
if test -n "${NETWORK_DEVS}"; then
pr_collect_output "ethtool"
for network_device in ${network_devices}; do
for network_device in ${NETWORK_DEVS}; do
call_run_command "ethtool ${network_device}" \
"${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -k ${network_device}" \
@@ -1082,14 +1084,12 @@ collect_ethtool() {
########################################
collect_tc() {
local network_devices
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type tc >/dev/null; then
if test -n "${network_devices}"; then
if test -n "${NETWORK_DEVS}"; then
pr_collect_output "Trafic Control"
for network_device in ${network_devices}; do
for network_device in ${NETWORK_DEVS}; do
call_run_command "tc -s qdisc show dev ${network_device}" \
"${OUTPUT_FILE_TC}"
done
@@ -1103,14 +1103,12 @@ collect_tc() {
########################################
collect_bridge() {
local network_devices
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if type bridge >/dev/null; then
if test -n "${network_devices}"; then
if test -n "${NETWORK_DEVS}"; then
pr_collect_output "bridge"
for network_device in ${network_devices}; do
for network_device in ${NETWORK_DEVS}; do
call_run_command "bridge -d link show dev ${network_device}" \
"${OUTPUT_FILE_BRIDGE}"
call_run_command "bridge -s fdb show dev ${network_device}" \

View File

@@ -125,5 +125,3 @@ Run the script with root authority.
.br
For Linux on z/VM, only z/VM guest virtual machines with class B privileges
yield the complete debug information.
.SH AUTHOR
Linux on IBM Z development <linux390@de.ibm.com>

View File

@@ -10,7 +10,8 @@
[Unit]
Description=CPACF statistics collection daemon process for Linux on System z
Documentation=man:cpacfstatsd(8)
ConditionVirtualization=no
ConditionVirtualization=|no
ConditionVirtualization=|vm
[Service]
Type=forking

View File

@@ -719,7 +719,7 @@ static bool read_cmcb(int chpid, cmcb_t *cmcb)
free(path);
path = get_chpid_path(chpid, "measurement_chars");
/* Partial CMCB contains data starting at word 3. */
read_bin(path, &cmcb[PARTIAL_CMCB_OFFSET],
read_bin(path, (char *)cmcb + PARTIAL_CMCB_OFFSET,
PARTIAL_CMCB_SIZE, false);
}
free(path);

View File

@@ -17,8 +17,8 @@
#include "column.h"
#define CMCB_SIZE (8 * sizeof(u32))
#define PARTIAL_CMCB_OFFSET 3
#define PARTIAL_CMCB_SIZE (CMCB_SIZE - PARTIAL_CMCB_OFFSET * sizeof(u32))
#define PARTIAL_CMCB_OFFSET (3 * sizeof(u32))
#define PARTIAL_CMCB_SIZE (CMCB_SIZE - PARTIAL_CMCB_OFFSET)
#define CUE_SIZE (8 * sizeof(u32))
#define EXT_CUE_SIZE (16 * sizeof(u32))
#define METRICS_SIZE (18 * sizeof(double))

View File

@@ -165,6 +165,9 @@ void df_s390_dumper_read(struct zg_fh *fh, int blk_size,
if (strncmp(dumper->magic, DF_S390_DUMPER_MAGIC_EXT,
DF_S390_DUMPER_MAGIC_SIZE) == 0)
dumper->size = STAGE2_DUMPER_SIZE_ZLIB;
else if (strncmp(dumper->magic, DF_S390_DUMPER_MAGIC_MV_EXT,
DF_S390_DUMPER_MAGIC_SIZE) == 0)
dumper->size = STAGE2_DUMPER_SIZE_MV;
else
dumper->size = STAGE2_DUMPER_SIZE_V2;
break;

View File

@@ -3,7 +3,7 @@
*
* Utility classes to read and access FCP configuration information
*
* Copyright IBM Corp. 2008, 2017
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -32,6 +32,7 @@ ConfigReader::ConfigReader(int *rc, const char *filename)
FILE *fp = NULL;
struct device_info new_elem;
char *line = NULL;
char *cfg_pchid = NULL;
size_t line_len;
int lrc;
int line_idx = 1;
@@ -67,7 +68,8 @@ ConfigReader::ConfigReader(int *rc, const char *filename)
new_elem.device = (char*)malloc(lrc + 1);
new_elem.type = (char*)malloc(lrc + 1);
new_elem.multipath_device = (char*)malloc(lrc + 1);
lrc = sscanf(line, "%x %u:%u:%u:%u %x.%x.%x:%x.%x.%x:%Lx:%Lx %s %u %u:%u %s %u %u:%u %s",
cfg_pchid = (char*)malloc(lrc + 1);
lrc = sscanf(line, "%x %u:%u:%u:%u %x.%x.%x:%x.%x.%x:%Lx:%Lx %s %u %u:%u %s %u %u:%u %s %s",
&new_elem.chpid,
&new_elem.hctl_identifier.host,
&new_elem.hctl_identifier.channel,
@@ -82,10 +84,13 @@ ConfigReader::ConfigReader(int *rc, const char *filename)
&new_elem.mp_minor, new_elem.device,
&new_elem.mm_internal,
&new_elem.major, &new_elem.minor,
new_elem.type);
new_elem.type,
cfg_pchid);
*rc = parse_pchid_str(cfg_pchid, &new_elem.pchid);
free(cfg_pchid);
free(line);
line = NULL;
if (lrc != 22) {
if (lrc != 23 || *rc != 0) {
fprintf(stderr, "%s: Could not parse line %d"
" - configuration file broken?\n", toolname, line_idx);
*rc = -1;
@@ -465,6 +470,14 @@ __u32 ConfigReader::get_chpid_by_host_id(__u32 host, int *rc) const
return 0;
}
__u32 ConfigReader::get_pchid_by_host_id(__u32 host, int *rc) const
{
search_for(hctl_identifier.host, host, pchid);
host_id_not_found_error(host, rc);
return 0;
}
__u32 ConfigReader::get_chpid_by_devno(__u32 d, int *rc) const
{
@@ -861,7 +874,7 @@ void ConfigReader::dump(FILE *fp) const
fprintf(fp, "dumping cfg....\n");
for (list<struct device_info>::const_iterator i = m_devices.begin();
i != m_devices.end(); ++i) {
fprintf(fp, "%x %u %u:%u:%u:%u %x.%x.%04x:%x.%x.%04x:%016Lx:%016Lx %s %u:%u %s %u:%u %s\n",
fprintf(fp, "%x %u %u:%u:%u:%u %x.%x.%04x:%x.%x.%04x:%016Lx:%016Lx %s %u:%u %s %u:%u %s %x\n",
(*i).chpid, (*i).mm_internal,
(*i).hctl_identifier.host,
(*i).hctl_identifier.channel,
@@ -874,7 +887,8 @@ void ConfigReader::dump(FILE *fp) const
((*i).multipath_device ? (*i).multipath_device : "n/a"), (*i).mp_major,
(*i).mp_minor, (*i).device,
(*i).major, (*i).minor,
(*i).type);
(*i).type,
(*i).pchid);
}
}

View File

@@ -3,7 +3,7 @@
*
* Utility classes to read and access FCP configuration information
*
* Copyright IBM Corp. 2008, 2017
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -39,6 +39,8 @@ public:
/// rc is only set on error!
__u32 get_chpid_by_host_id(__u32 h, int *rc) const;
/// rc is only set on error!
__u32 get_pchid_by_host_id(__u32 h, int *rc) const;
/// rc is only set on error!
__u32 get_chpid_by_devno(__u32 devno, int *rc) const;
/// rc is only set on error!
__u32 get_chpid_by_ident(const struct hctl_ident *ident, int *rc) const;
@@ -153,6 +155,8 @@ private:
bool cached_config_exists(const char *fname);
struct device_info {
// pchid, e.g. 01c0
__u32 pchid;
// chpid, e.g. 43 (hex)
__u32 chpid;

View File

@@ -70,6 +70,7 @@ sub get_sub_ch_data
$c_src = catdir($base_dir, S_DIR1, $host);
my $sub_ch;
my $adapter;
my $chp;
if (-l $c_src) {
$sub_ch = (split("/", readlink($c_src)))[-5];
$adapter = (split("/", readlink($c_src)))[-4];
@@ -90,6 +91,13 @@ sub get_sub_ch_data
"Offline";
$c_src = catdir($base_dir, S_DIR2, $sub_ch);
$sub_ch{$adapter}{chpid} = substr(get_line("chpids"), 0, 2);
$chp = "chp0." . $sub_ch{$adapter}{chpid};
$c_src = catdir($base_dir, S_DIR2, $chp);
if (-e catdir($base_dir, S_DIR2, $chp, "chid")) {
$sub_ch{$adapter}{pchid} = get_line("chid");
} else {
$sub_ch{$adapter}{pchid} = "n/a"
}
}
}
@@ -215,6 +223,7 @@ sub mapping_table
push @out_str, $devices{$line}{mm};
}
push @out_str, $devices{$line}{type};
push @out_str, $sub_ch{$devices{$line}{hba_id}}{pchid};
print "@out_str\n";
}
}
@@ -227,6 +236,11 @@ sub adapter_report
next if (@adapters && "@adapters" !~ /\b$a\b/);
my @out_str;
push @out_str, "Host: $sub_ch{$a}{host}\n";
if ($sub_ch{$a}{pchid} eq "n/a") {
push @out_str, "PCHID: there is no PCHID entry in data source\n";
} else {
push @out_str, "PCHID: $sub_ch{$a}{pchid}\n";
}
push @out_str, "CHPID: $sub_ch{$a}{chpid}\n";
push @out_str, "Adapter: $a\n";
push @out_str, "Sub-Ch.: $sub_ch{$a}{sub_ch}\n";

View File

@@ -3,7 +3,7 @@
*
* Utility classes to print framsets
*
* Copyright IBM Corp. 2008, 2021
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -309,6 +309,22 @@ void PhysAdapterPrinter::print_phys_adpt(FILE *fp, __u32 host_id, int *rc)
fprintf(fp, "%3x", chpid);
}
void PhysAdapterPrinter::print_pchid(FILE *fp, __u32 host_id, int *rc)
{
__u32 pchid = m_cfg->get_pchid_by_host_id(host_id, rc);
if (m_csv)
if (pchid != ZIOREP_PCHID_NA)
fprintf(fp, "%x", pchid);
else
fprintf(fp, "%s", "n/a");
else
if (pchid != ZIOREP_PCHID_NA)
fprintf(fp, "%04x", pchid);
else
fprintf(fp, "%s", "n/a ");
}
void PhysAdapterPrinter::print_utilization(FILE *fp,
const struct abbrev_stat *stat,
@@ -361,10 +377,10 @@ void PhysAdapterPrinter::print_topline(FILE *fp)
if (m_csv)
fprintf(fp, "timestamp,aggregated,CHPID,adapter min %%,"
"adapter max %%,adapter avg %%,bus min %%,bus max %%,"
"bus avg %%,cpu min %%,cpu max %%,cpu avg %%\n");
"bus avg %%,cpu min %%,cpu max %%,cpu avg %%,PCHID\n");
else {
fprintf(fp, "CHP|adapter in %%-|--bus in %%---|--cpu in %%---|\n");
fprintf(fp, " ID min max avg min max avg min max avg\n");
fprintf(fp, "PCH |CHP|adapter in %%-|--bus in %%---|--cpu in %%---|\n");
fprintf(fp, " ID ID min max avg min max avg min max avg\n");
}
}
@@ -387,6 +403,12 @@ int PhysAdapterPrinter::print_frame(FILE *fp,
// print timestamp for every line in CSV mode
timestamp_printed = true;
}
if (!m_csv) {
print_pchid(fp, *i, &lrc);
if (lrc)
return -1;
print_delimiter(fp);
}
print_phys_adpt(fp, *i, &lrc);
if (lrc)
return -1;
@@ -402,6 +424,12 @@ int PhysAdapterPrinter::print_frame(FILE *fp,
print_utilization(fp, &util->stats.cpu,
util->stats.count,
util->valid);
if (m_csv) {
print_delimiter(fp);
print_pchid(fp, *i, &lrc);
if (lrc)
return -1;
}
fputc('\n', fp);
}
@@ -423,10 +451,11 @@ void VirtAdapterPrinter::print_virt_adpt(FILE *fp, __u32 devno,
fprintf(fp, "%x,%x.%x.%04x",
m_cfg->get_chpid_by_devno(devno, rc),
ZIOREP_BUSID_UNPACKED(devno));
else
fprintf(fp, "%3x/%x.%x.%04x",
m_cfg->get_chpid_by_devno(devno, rc),
ZIOREP_BUSID_UNPACKED(devno));
else {
fprintf(fp, "%x.%x.%04x", ZIOREP_BUSID_UNPACKED(devno));
print_delimiter(fp);
fprintf(fp, "%3x", m_cfg->get_chpid_by_devno(devno, rc));
}
}
void VirtAdapterPrinter::print_queue_fill(FILE *fp,
@@ -520,8 +549,8 @@ void VirtAdapterPrinter::print_topline(FILE *fp)
if (m_csv)
fprintf(fp, "timestamp,aggregated,CHPID,Bus-ID,qdio utilization max %%,qdio utilization avg %%,queue full,fail erc,throughput read / MS/s,throughput write / MS/s,I/O requests read,I/O requests write\n");
else {
fprintf(fp, "CHP Bus-ID |qdio util.%%|queu|fail|-thp in MB/s-|I/O reqs-|\n");
fprintf(fp, " ID max avg full erc rd wrt rd wrt\n");
fprintf(fp, "Bus-ID |CHP|qdio util.%%|queu|fail|-thp in MB/s-|I/O reqs-|\n");
fprintf(fp, " ID max avg full erc rd wrt rd wrt\n");
}
}
@@ -638,9 +667,9 @@ void TrafficPrinter::print_topline_prefix1(FILE *fp)
switch (m_agg_crit) {
case none:
if (m_csv)
str = "WWPN,LUN";
str = "DEVBUSID,WWPN,LUN";
else
str = " WWPN LUN ";
str = "DEVBUSID WWPN LUN ";
break;
case chpid:
if (m_csv)
@@ -677,7 +706,7 @@ void TrafficPrinter::print_topline_prefix2(FILE *fp)
switch (m_agg_crit) {
case none:
str = " ";
str = " ";
break;
case chpid:
str = " ID";
@@ -705,7 +734,7 @@ void TrafficPrinter::print_topline_whitespace(FILE *fp)
switch (m_agg_crit) {
case none:
str = " ";
str = " ";
break;
case chpid:
str = " ";
@@ -788,12 +817,16 @@ void TrafficPrinter::print_device_mp_mm(FILE *fp, __u32 mp_mm,
void TrafficPrinter::print_device(FILE *fp, __u32 dev,
const ConfigReader &cfg, int *rc)
{
__u32 devno = cfg.get_devno_by_mm_internal(dev, rc);
print_device_devno(fp, devno);
if (m_csv)
fprintf(fp, "0x%016Lx,0x%016Lx",
fprintf(fp, ",0x%016Lx,0x%016Lx",
(long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc),
(long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc));
else
fprintf(fp, "0x%016Lx:0x%016Lx",
fprintf(fp, ":0x%016Lx:0x%016Lx",
(long long unsigned int)cfg.get_wwpn_by_mm_internal(dev, rc),
(long long unsigned int)cfg.get_lun_by_mm_internal(dev, rc));
}

View File

@@ -3,7 +3,7 @@
*
* Utility classes to print framsets
*
* Copyright IBM Corp. 2008, 2021
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -108,6 +108,8 @@ public:
private:
void print_phys_adpt(FILE *fp, __u32 host_id,
int *rc);
void print_pchid(FILE *fp, __u32 host_id,
int *rc);
void print_utilization(FILE *fp, const struct abbrev_stat *stat,
__u64 count, bool valid);

View File

@@ -1,4 +1,4 @@
.\" Copyright 2017 IBM Corp.
.\" Copyright 2024 IBM Corp.
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
@@ -58,6 +58,15 @@ Print a summary of the data, then exit.
Only consider the specified physical adapter. Adapters must be specified in hex.
If multiple adapters should be specified, specify each one separately.
.TP
.BR "\-f" " or " "\-\-fcp-device"
Print a utilization report for an FCP device. If this option is specified, then
a virtual adapter report is printed, otherwise a physical adapter report with FCP
channel scope is printed. This parameter has no influence on CSV format report printing.
Using the --export-csv option causes both physical and virtual adapter utilization
reports to be printed into two separate CSV-formatted files independent of the
--fcp-device parameter.
.TP
.BR "\-x" " or " "\-\-export-csv"
Write data to file(s) in CSV format. Output filenames will be based on the data filename.
@@ -127,14 +136,23 @@ give the values for read and write requests respectively.
.SH EXAMPLES
Print a utilization report using
Print a physical adapter utilization report only using
.IR sample.log
, considering adapters 4e and 4f only.
Only data between 8:57 April 5, 2008, and 17:09 June 21, 2008, should be considered.
Only data between 8:57 April 5, 2024, and 17:09 June 21, 2024, should be considered.
Data should be aggregated to 60 second intervals.
ziorep_utilization -c 4e -c 4f -i 60 -b "2008-04-05 08:57" -e "2008-06-21 17:09"
ziorep_utilization sample.log -c 4e -c 4f -i 60 -b "2024-04-05 08:57" -e "2024-06-21 17:09"
Print virtual adapter report only using sample.log. Consider adapter 4f only. Data should be
aggregated to 3 seconds interval. Print topline after every 2nd frame.
ziorep_utilization sample.log -c 4f -i 3 -t 2 --fcp-device
Print both physical and virtual adapter reports using sample.log in two separate CSV-formatted
files with the default interval. Considering adapter 4f only. Print topline after every 2nd frame.
ziorep_utilization sample.log -c 4f -t 2 --export-csv
.SH "SEE ALSO"
.BR ziorep_config (8),

View File

@@ -3,7 +3,7 @@
*
* Utilization report program
*
* Copyright IBM Corp. 2008, 2017
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -49,6 +49,7 @@ struct options {
char* filename;
bool print_summary;
bool csv_export;
bool fcp_device;
};
@@ -61,6 +62,7 @@ static void init_opts(struct options *opts)
opts->filename = NULL;
opts->print_summary = false;
opts->csv_export = false;
opts->fcp_device = false;
}
@@ -85,6 +87,8 @@ static const char help_text[] =
"-s, --summary Show a summary of the data.\n"
"-c, --chpid <chpid> Select physical adapter in hex.\n"
" E.g. '-c 32a'\n"
"-f, --fcp-device Print utilization report for FCP device scope.\n"
" Otherwise for FCP channel scope.\n"
"-x, --export-csv Export data to files in CSV format.\n"
"-t, --topline <num> Repeat topline after every 'num' frames.\n"
" 0 for no repeat (default).\n";
@@ -99,7 +103,7 @@ static void print_help()
static void print_version()
{
printf("%s: Utilization report generator version %s\n"
"Copyright IBM Corp. 2008, 2017\n", toolname, RELEASE_STRING);
"Copyright IBM Corp. 2008, 2024\n", toolname, RELEASE_STRING);
}
@@ -121,6 +125,7 @@ static int parse_params(int argc, char **argv, struct options *opts)
{ "chpid", required_argument, NULL, 'c'},
{ "export-csv", no_argument, NULL, 'x'},
{ "topline", required_argument, NULL, 't'},
{ "fcp-device", no_argument, NULL, 'f'},
{ 0, 0, 0, 0 }
};
@@ -130,7 +135,7 @@ static int parse_params(int argc, char **argv, struct options *opts)
}
assert(sizeof(long long int) == sizeof(__u64));
while ((c = getopt_long(argc, argv, "b:e:i:c:t:xshvV",
while ((c = getopt_long(argc, argv, "b:e:i:c:t:xshvVf",
long_options, &index)) != EOF) {
switch (c) {
case 'V':
@@ -183,6 +188,9 @@ static int parse_params(int argc, char **argv, struct options *opts)
if (parse_topline_arg(optarg, &opts->topline))
return -1;
break;
case 'f':
opts->fcp_device = true;
break;
default:
fprintf(stderr, "%s: Try '%s --help' for"
" more information.\n", toolname, toolname);
@@ -297,43 +305,65 @@ static int print_reports(struct options *opts, ConfigReader &cfg)
type_flt.push_back(utilization);
// physical adapter report
if (opts->csv_export) {
fp = open_csv_output_file(opts->filename,
"_util_phys_adpt.csv", &rc);
if (!fp)
goto out;
}
else
} else {
fp = stdout;
if ( (rc = print_report(fp, opts->begin, opts->end,
opts->interval, opts->filename, opts->topline,
&type_flt, dev_filt, noop_col,
physPrnt)) < 0 ) {
rc = -3;
goto out1;
}
if (rc == 0)
fprintf(stderr, "%s: No eligible data found.\n", toolname);
if (!opts->fcp_device || opts->csv_export) {
rc = print_report(fp, opts->begin, opts->end,
opts->interval, opts->filename, opts->topline,
&type_flt, dev_filt, noop_col, physPrnt);
if (rc < 0) {
rc = -3;
goto out1;
} else if (rc == 0) {
fprintf(stderr, "%s: No eligible data found.\n",
toolname);
}
if (!opts->csv_export)
fprintf(stderr,
"%s: The FCP channel utilization report was "
"printed. To print the FCP device utilization "
"report, use the -f/--fcp-device option.\n",
toolname);
}
// virtual adapter report
if (opts->csv_export) {
fclose(fp);
fp = open_csv_output_file(opts->filename,
"_util_virt_adpt.csv", &rc);
if (!fp)
goto out;
}
else {
} else {
fp = stdout;
fputc('\n', fp);
}
if (print_report(fp, opts->begin, opts->end, opts->interval,
opts->filename, opts->topline, NULL, dev_filt,
*col, virtPrnt)) {
rc = -4;
goto out1;
if (opts->fcp_device || opts->csv_export) {
rc = print_report(fp, opts->begin, opts->end, opts->interval,
opts->filename, opts->topline, NULL, dev_filt,
*col, virtPrnt);
if (rc < 0) {
rc = -4;
goto out1;
} else if (rc == 0) {
fprintf(stderr, "%s: No eligible data found.\n",
toolname);
}
if (!opts->csv_export)
fprintf(stderr,
"%s: The FCP device utilization report was "
"printed. To print the FCP channel utilization "
"report, omit the -f/--fcp-device option.\n",
toolname);
}
out1:

View File

@@ -3,7 +3,7 @@
*
* Utility functions
*
* Copyright IBM Corp. 2008, 2017
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -552,6 +552,36 @@ FILE* open_csv_output_file(const char *filename, const char *extension,
return fp;
}
/**
* parse_pchid_str - parse PCHID from string to 32-bit number
* @pchid_str: PCHID string
* @pchid: 32-bit PCHID number
*
* Reads PCHID string, checks for corner cases, converts to 32-bit
* PCHID number, checks for errors, writes resulted PCHID to specified
* location.
*/
int parse_pchid_str(const char *const pchid_str, __u32 *const pchid)
{
unsigned long parsed;
char *end;
if (strcmp(pchid_str, "n/a") == 0) {
parsed = ZIOREP_PCHID_NA;
goto out;
}
parsed = strtoul(pchid_str, &end, 16);
if (parsed > 0xffff) {
fprintf(stderr, "%s: PCHID %s exceeds maximum possible value for a PCHID.\n",
toolname, pchid_str);
return -1;
} else if (parsed == 0 && end == &pchid_str[0]) {
fprintf(stderr, "%s: PCHID %s could not be converted.\n", toolname, pchid_str);
return -1;
}
out:
*pchid = (__u32)parsed;
return 0;
}

View File

@@ -3,7 +3,7 @@
*
* Utility functions
*
* Copyright IBM Corp. 2008, 2017
* Copyright IBM Corp. 2008, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -24,6 +24,8 @@ extern "C" {
#include <linux/types.h>
#define ZIOREP_PCHID_NA 65536
/**
* Parse date provided in 'str' and store as seconds since 1970 in 'tgt'. */
int get_datetime_val(const char *str, __u64 *tgt);
@@ -82,6 +84,10 @@ int parse_topline_arg(char *str, __u64 *arg);
FILE* open_csv_output_file(const char *filename, const char *extension,
int *rc);
/**
* Parse PCHID from string to 32-bit number
*/
int parse_pchid_str(const char *const pchid_str, __u32 *const pchid);
/**
* accessors for internal representation of device and subchannel bus-IDs

View File

@@ -57,7 +57,7 @@ eckd2dump_sv.lds: eckd2dump.lds.S
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x9ff0 \
-P -C -o $@ $<
eckd2dump_mv.lds: eckd2dump.lds.S
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x4ff0 \
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x5ff0 \
-D ECKD2DUMP_MV -P -C -o $@ $<
stage2.lds: stage2.lds.S
$(CPP) -Wp,-MD,.$@.d,-MT,$@ $(INCLUDE_PARMS) -D STAGE2DUMP_TAIL_ADDRESS=0x4ff0 \

View File

@@ -17,8 +17,8 @@
*
* 0x78 Stage 2 description parameters
* 0x2018 Stage 2 entry point
* 0x4ff0 Stage 2 multi-volume dump parameters (eckd2dump_mv)
* 0x5000 Multi-volume dump parameters table (eckd2dump_mv)
* 0x5ff0 Stage 2 multi-volume dump parameters (eckd2dump_mv)
* 0x6000 Multi-volume dump parameters table (eckd2dump_mv)
* 0x9ff0 Stage 2 single volume dump parameters (eckd2dump_sv)
*/

View File

@@ -22,7 +22,7 @@
/*
* Magic number at start of dump record
*/
const uint64_t __section(.stage2.head) magic = 0x584d554c54363401ULL; /* XMULT64, version 1 */
const uint64_t __section(.stage2.head) magic = 0x584d554c54363402ULL; /* XMULT64, version 2 */
/*
* Parameter format for ECKD MV dumper (13 bytes):

View File

@@ -48,9 +48,9 @@ static inline void __noreturn start_kernel(void)
" diag %[code],%[code],0x308\n"
".no_diag308:\n"
" sam31\n"
" sr %r1,%r1\n"
" sr %r2,%r2\n"
" sigp %r1,%r2,%[order]\n"
" sr %%r1,%%r1\n"
" sr %%r2,%%r2\n"
" sigp %%r1,%%r2,%[order]\n"
" lpsw 0\n"
: [addr] "=&d" (addr),
[code] "+&d" (code)

View File

@@ -38,9 +38,10 @@ install: all
$(INSTALL) -d -m 755 $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl $(DESTDIR)$(BINDIR)
$(INSTALL) -c zipl-editenv $(DESTDIR)$(BINDIR)
$(INSTALL) -m 755 $(zipl_helpers) $(chreipl_helpers) \
$(DESTDIR)$(TOOLS_LIBDIR)
$(CP) --no-dereference $(chreipl_helpers) $(DESTDIR)$(TOOLS_LIBDIR)
$(INSTALL) -m 755 zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)
ln -f -s zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)/zipl_helper.md
ln -f -s zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)/chreipl_helper.md
ln -f -s zipl_helper.device-mapper $(DESTDIR)$(TOOLS_LIBDIR)/chreipl_helper.device-mapper
clean:
rm -f *.o $(zipl_helpers) $(chreipl_helpers) zipl zipl-editenv

View File

@@ -299,14 +299,15 @@ create_component_header(void* buffer, component_header_type type)
}
/*
* Not precise check that the file FILENAME locates on specified physical DISK.
* Not precise check that the file FILENAME locates on the physical
* disk specified by WHERE.
*
* Try to auto-detect parameters of the disk which the file locates on
* and compare found device-ID with DISK.
* Return 0, if auto-detection succeeded, and it is proven that the
* file does NOT locate on DISK. Otherwise, return 1.
*/
static int file_is_on_disk(const char *filename, dev_t disk)
static int file_is_on_disk(const char *filename, struct disk_info *where)
{
/*
* Retrieve info of the underlying disk without any user hints
@@ -331,7 +332,7 @@ static int file_is_on_disk(const char *filename, dev_t disk)
"Warning: Preparing a logical device for boot might fail\n");
return 1;
}
if (info->device != disk) {
if (info->basedisks[0] != where->basedisks[0]) {
disk_free_info(info);
return 0;
}
@@ -378,7 +379,7 @@ static int add_component_file_range(struct install_set *bis,
return -1;
}
} else {
if (!file_is_on_disk(filename, bis->info->device)) {
if (!file_is_on_disk(filename, bis->info)) {
error_reason("File is not on target device");
return -1;
}

View File

@@ -1306,13 +1306,20 @@ static int complete_physical_device(struct physical_device *pd, dev_t *base_dev)
*base_dev = base_entry->dev.dev;
} else {
/*
* In this case base device is the uppermost logical
* In this case base device is the uppermost physical
* device which provides access to boot sectors
*/
base_entry = find_base_entry(pd->dmpath, dc->bootsectors);
if (!base_entry)
return -1;
*base_dev = base_entry->dev.dev;
if (target_get_start(base_entry->target) == 0) {
/* base device is a dm device */
*base_dev = base_entry->dev.dev;
} else {
/* base device is a non-dm device */
*base_dev =
first_device_by_target_data(base_entry->target);
}
}
/* Check for valid offset of filesystem */
if ((pd->offset % (dc->blocksize / SECTOR_SIZE)) != 0) {

View File

@@ -92,9 +92,11 @@ keystore.o: keystore.c keystore.h properties.h pkey.h cca.h ep11.h utils.h
zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \
ep11.h misc.h utils.h
kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h
pvsecrets.o: pvsecrets.h
zkey: LDLIBS = -ldl -lcrypto
zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o $(libs)
zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o \
pvsecrets.o $(libs)
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto

View File

@@ -3,7 +3,7 @@
*
* Keystore handling functions
*
* Copyright IBM Corp. 2018, 2020
* Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -360,6 +360,8 @@ static int _keystore_valid_key_type(const char *key_type)
return 1;
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
return 1;
if (strcasecmp(key_type, KEY_TYPE_PVSECRET_AES) == 0)
return 1;
return 0;
}
@@ -2007,6 +2009,12 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
return -EINVAL;
}
if (!is_secure_key_type(key_type)) {
warnx("Keys of type %s can not be generated. Use 'zkey "
"pvsecret import' instead", key_type);
return -EINVAL;
}
rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0)
goto out_free_key_filenames;
@@ -2257,9 +2265,11 @@ out_free_key_filenames:
}
/**
* Imports a secure key from a file and adds it to the key store
* Imports a secure key from a buffer and adds it to the key store
*
* @param[in] keystore the key store
* @param[in] secure_key the buffer containing the key
* @param[in] secure_key_size the size of the key
* @param[in] name the name of the key
* @param[in] description textual description of the key (optional, can be NULL)
* @param[in] volumes a comma separated list of volumes associated with this
@@ -2272,7 +2282,6 @@ out_free_key_filenames:
* of two and in range 512 - 4096 bytes. 0 means that
* the sector size is not specified and the system
* default is used.
* @param[in] import_file The name of a secure key containing the key to import
* @param[in] volume_type the type of volume
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase
@@ -2281,25 +2290,23 @@ out_free_key_filenames:
*
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type,
bool gen_passphrase, const char *passphrase_file,
struct ext_lib *lib)
int keystore_import(struct keystore *keystore, unsigned char *secure_key,
size_t secure_key_size, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *volume_type, bool gen_passphrase,
const char *passphrase_file, struct ext_lib *lib)
{
struct key_filenames file_names = { 0 };
struct properties *key_props = NULL;
size_t secure_key_size;
const char *key_type;
u8 mkvp[MKVP_LENGTH];
int selected = 1;
u8 *secure_key;
int rc;
util_assert(keystore != NULL, "Internal error: keystore is NULL");
util_assert(name != NULL, "Internal error: name is NULL");
util_assert(import_file != NULL, "Internal error: import_file is NULL");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0)
@@ -2309,27 +2316,29 @@ int keystore_import_key(struct keystore *keystore, const char *name,
if (rc != 0)
goto out_free_key_filenames;
secure_key = read_secure_key(import_file, &secure_key_size,
keystore->verbose);
if (secure_key == NULL) {
rc = -ENOENT;
goto out_free_key_filenames;
}
key_type = get_key_type(secure_key, secure_key_size);
if (key_type == NULL) {
warnx("Key '%s' is not a valid secure key", name);
free(secure_key);
rc = -EINVAL;
goto out_free_key_filenames;
}
if (!is_secure_key(secure_key, secure_key_size)) {
if (apqns != NULL) {
warnx("No APQNs can be associated with keys of type %s",
key_type);
rc = -EINVAL;
goto out_free_props;
}
goto write_key;
}
rc = get_master_key_verification_pattern(secure_key, secure_key_size,
mkvp, keystore->verbose);
if (rc != 0) {
warnx("Failed to get the master key verification pattern: %s",
strerror(-rc));
goto out_free_key;
goto out_free_props;
}
rc = cross_check_apqns(apqns, mkvp,
@@ -2338,17 +2347,17 @@ int keystore_import_key(struct keystore *keystore, const char *name,
get_card_type_for_keytype(key_type),
true, keystore->verbose);
if (rc == -EINVAL)
goto out_free_key;
goto out_free_props;
if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
warnx("Your master key setup is improper");
goto out_free_key;
goto out_free_props;
}
if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
if (lib->cca->lib_csulcca == NULL) {
rc = load_cca_library(lib->cca, keystore->verbose);
if (rc != 0)
goto out_free_key;
goto out_free_props;
}
rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns,
@@ -2363,7 +2372,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
warnx("No APQN found that is suitable for "
"working with the secure AES key '%s'", name);
rc = 0;
goto out_free_key;
goto out_free_props;
}
rc = restrict_key_export(lib->cca, secure_key, secure_key_size,
@@ -2373,7 +2382,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"key: %s", strerror(-rc));
if (!selected)
print_msg_for_cca_envvars("secure AES key");
goto out_free_key;
goto out_free_props;
}
rc = check_aes_cipher_key(secure_key, secure_key_size);
@@ -2384,15 +2393,14 @@ int keystore_import_key(struct keystore *keystore, const char *name,
if (!prompt_for_yes(keystore->verbose)) {
warnx("Operation aborted");
rc = -ECANCELED;
goto out_free_key;
goto out_free_props;
}
}
}
write_key:
rc = write_secure_key(file_names.skey_filename, secure_key,
secure_key_size, keystore->verbose);
free(secure_key);
secure_key = NULL;
if (rc != 0)
goto out_free_props;
@@ -2412,9 +2420,6 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"Successfully imported a secure key in '%s' and key info in '%s'",
file_names.skey_filename, file_names.info_filename);
out_free_key:
if (secure_key != NULL)
free(secure_key);
out_free_props:
if (key_props != NULL)
properties_free(key_props);
@@ -2429,6 +2434,59 @@ out_free_key_filenames:
return rc;
}
/**
* Imports a secure key from a file and adds it to the key store
*
* @param[in] keystore the key store
* @param[in] name the name of the key
* @param[in] description textual description of the key (optional, can be NULL)
* @param[in] volumes a comma separated list of volumes associated with this
* key (optional, can be NULL)
* @param[in] apqns a comma separated list of APQNs associated with this
* key (optional, can be NULL)
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
* existence and type.
* @param[in] sector_size the sector size to use with dm-crypt. It must be a
* power of two and in range 512 - 4096 bytes. 0 means
* that the sector size is not specified and the system
* default is used.
* @param[in] import_file The name of a secure key containing the key to import
* @param[in] volume_type the type of volume
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase
* for LUKS2 (optional, can be NULL)
* @param[in] lib the external library struct
*
* @returns 0 for success or a negative errno in case of an error
*/
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *import_file, const char *volume_type,
bool gen_passphrase, const char *passphrase_file,
struct ext_lib *lib)
{
size_t secure_key_size;
u8 *secure_key;
int rc;
util_assert(import_file != NULL, "Internal error: import_file is NULL");
secure_key = read_secure_key(import_file, &secure_key_size,
keystore->verbose);
if (secure_key == NULL)
return -ENOENT;
rc = keystore_import(keystore, secure_key, secure_key_size, name,
description, volumes, apqns, noapqncheck,
sector_size, volume_type, gen_passphrase,
passphrase_file, lib);
if (secure_key != NULL)
free(secure_key);
return rc;
}
/**
* Changes properties of a key in the keystore.
@@ -2483,9 +2541,9 @@ int keystore_change_key(struct keystore *keystore, const char *name,
const char *null_ptr = NULL;
char *upd_volumes = NULL;
size_t secure_key_size;
u8 *secure_key = NULL;
u8 mkvp[MKVP_LENGTH];
char sect_size[30];
u8 *secure_key;
bool kms_bound;
int rc;
@@ -2537,13 +2595,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
}
rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
apqns, "APQN",
_keystore_apqn_check,
&apqn_check);
if (rc != 0)
goto out;
secure_key = read_secure_key(file_names.skey_filename,
&secure_key_size,
keystore->verbose);
@@ -2552,11 +2603,24 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
}
if (!is_secure_key(secure_key, secure_key_size)) {
warnx("No APQNs can be associated with keys of type %s",
get_key_type(secure_key, secure_key_size));
rc = -EINVAL;
goto out;
}
rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
apqns, "APQN",
_keystore_apqn_check,
&apqn_check);
if (rc != 0)
goto out;
rc = get_master_key_verification_pattern(secure_key,
secure_key_size,
mkvp,
keystore->verbose);
free(secure_key);
if (rc)
goto out;
@@ -2690,6 +2754,8 @@ out:
free(upd_volumes);
if (upd_volume_type != NULL)
free(upd_volume_type);
if (secure_key != NULL)
free(secure_key);
if (rc != 0)
pr_verbose(keystore, "Failed to change key '%s': %s",
@@ -2989,19 +3055,25 @@ static void _keystore_print_record(struct util_rec *rec,
util_rec_set(rec, REC_XTS, is_xts ? "Yes" : "No");
util_rec_set(rec, REC_KEY_TYPE, key_type);
if (validation) {
if (valid)
util_rec_set(rec, REC_MASTERKEY,
"%s master key (MKVP: %s)",
is_old_mk ? "OLD" : "CURRENT",
printable_mkvp(
get_card_type_for_keytype(key_type),
mkvp));
else
util_rec_set(rec, REC_MASTERKEY,
"(unknown, MKVP: %s)",
printable_mkvp(
get_card_type_for_keytype(key_type),
mkvp));
if (mkvp != NULL) {
if (valid)
util_rec_set(rec, REC_MASTERKEY,
"%s master key (MKVP: %s)",
is_old_mk ? "OLD" : "CURRENT",
printable_mkvp(
get_card_type_for_keytype(
key_type),
mkvp));
else
util_rec_set(rec, REC_MASTERKEY,
"(unknown, MKVP: %s)",
printable_mkvp(
get_card_type_for_keytype(
key_type),
mkvp));
} else {
util_rec_set(rec, REC_MASTERKEY, "(none)");
}
}
if (volumes_argz != NULL)
util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
@@ -3228,17 +3300,22 @@ static int _keystore_process_validate(struct keystore *keystore,
valid = 1;
}
rc = get_master_key_verification_pattern(secure_key, secure_key_size,
mkvp, keystore->verbose);
if (rc != 0)
goto out;
if (is_secure_key(secure_key, secure_key_size)) {
rc = get_master_key_verification_pattern(secure_key,
secure_key_size,
mkvp,
keystore->verbose);
if (rc != 0)
goto out;
}
_keystore_print_record(info->rec, name, properties, 1,
file_names->skey_filename, secure_key_size,
is_xts_key(secure_key, secure_key_size),
clear_key_bitsize, valid, is_old_mk,
_keystore_reencipher_key_exists(file_names),
mkvp,
is_secure_key(secure_key, secure_key_size) ?
mkvp : NULL,
_keystore_passphrase_file_exists(file_names) ?
file_names->pass_filename : NULL);
@@ -3250,7 +3327,8 @@ static int _keystore_process_validate(struct keystore *keystore,
"master key\n", 0);
info->num_warnings++;
}
if (info->noapqncheck == 0)
if (info->noapqncheck == 0 &&
is_secure_key(secure_key, secure_key_size))
if (_keystore_display_apqn_status(keystore, properties,
mkvp) != 0)
info->num_warnings++;
@@ -3501,6 +3579,15 @@ static int _keystore_process_reencipher(struct keystore *keystore,
goto out;
}
if (!is_secure_key(secure_key, secure_key_size)) {
warnx("Key '%s' is of type %s and can not be re-enciphered, "
"skipping", name, get_key_type(secure_key,
secure_key_size));
info->num_skipped++;
rc = 0;
goto out;
}
apqns = properties_get(properties, PROP_NAME_APQNS);
if (apqns != NULL)
apqn_list = str_list_split(apqns);

View File

@@ -3,7 +3,7 @@
*
* Keystore handling functions
*
* Copyright IBM Corp. 2018, 2020
* Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -65,6 +65,13 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
struct kms_option *kms_options,
size_t num_kms_options);
int keystore_import(struct keystore *keystore, unsigned char *secure_key,
size_t secure_key_size, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,
const char *volume_type, bool gen_passphrase,
const char *passphrase_file, struct ext_lib *lib);
int keystore_import_key(struct keystore *keystore, const char *name,
const char *description, const char *volumes,
const char *apqns, bool noapqncheck, size_t sector_size,

Some files were not shown because too many files have changed in this diff Show More