mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
793c037ded | ||
|
|
9f476217f5 | ||
|
|
49111d613b | ||
|
|
26d2e2d786 | ||
|
|
7bd159de1d | ||
|
|
605680d6fd | ||
|
|
f1a479a794 | ||
|
|
fa2d68e80c | ||
|
|
e1730585e9 | ||
|
|
ef55a5d677 | ||
|
|
bc4b7475f7 | ||
|
|
abcb93aacb | ||
|
|
b09a9b3490 | ||
|
|
caaf2b2116 | ||
|
|
382ec368fc | ||
|
|
9dd96144d4 | ||
|
|
efd41ca435 | ||
|
|
7bc12d0202 | ||
|
|
b49cd13d16 | ||
|
|
56e32dfcbb | ||
|
|
833a8e7309 | ||
|
|
a8eb2bd4e7 | ||
|
|
5276d408fd | ||
|
|
95bf7eb285 | ||
|
|
fdf66dc148 | ||
|
|
5ce79ea667 | ||
|
|
8c4b2872b8 | ||
|
|
7be364aa84 | ||
|
|
43858063ca | ||
|
|
739761f0d2 | ||
|
|
a8a3e7d49c | ||
|
|
ff04f76257 | ||
|
|
93216d916c | ||
|
|
256289a30a | ||
|
|
93da795520 | ||
|
|
a14f9d4edc | ||
|
|
fd024387d7 | ||
|
|
4af137f4fa | ||
|
|
01cd81ecf5 | ||
|
|
69eb06f39e | ||
|
|
d1636168b2 | ||
|
|
1e44ace41d | ||
|
|
a9b8caa411 | ||
|
|
6e48c5ebaa | ||
|
|
944581eaef | ||
|
|
3f6572e901 | ||
|
|
6520070a46 | ||
|
|
3b8fdcc892 | ||
|
|
352e2fe5d3 | ||
|
|
37b7b68f1b | ||
|
|
3db20bccef | ||
|
|
560b276f7e | ||
|
|
a19c43f613 | ||
|
|
5b6d7a467d | ||
|
|
b1fdbac5f9 | ||
|
|
cf51ac7860 | ||
|
|
cf5560a100 | ||
|
|
dff965465c | ||
|
|
79de37bbc8 | ||
|
|
e5c2fba0a8 | ||
|
|
04267470b1 | ||
|
|
cf7060ea08 | ||
|
|
f340a96fdf | ||
|
|
798bd752cf | ||
|
|
638f0aa4f9 | ||
|
|
d3b7581669 |
@@ -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
|
||||
|
||||
32
CHANGELOG.md
32
CHANGELOG.md
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
303
genprotimg/samples/check_hostkeydoc
Executable file
303
genprotimg/samples/check_hostkeydoc
Executable 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
493
rust/Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -26,6 +26,3 @@ unstable_features = "warn"
|
||||
unused = "warn"
|
||||
unused_import_braces = "warn"
|
||||
unused_qualifications = "warn"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
85
rust/pv/src/openssl_extensions/bio.rs
Normal file
85
rust/pv/src/openssl_extensions/bio.rs
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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
222
rust/pv/src/pem_utils.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,5 @@
|
||||
pub mod asrcb;
|
||||
pub mod ext_secret;
|
||||
pub mod guest_secret;
|
||||
pub mod retr_secret;
|
||||
pub mod user_data;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
234
rust/pv/src/uvsecret/retr_secret.rs
Normal file
234
rust/pv/src/uvsecret/retr_secret.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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
|
||||
|
||||
392
rust/pv_core/src/uvdevice/retr_secret.rs
Normal file
392
rust/pv_core/src/uvdevice/retr_secret.rs
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" }
|
||||
|
||||
28
rust/pvattest/tests/assets/host.pem.crt
Normal file
28
rust/pvattest/tests/assets/host.pem.crt
Normal 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-----
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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)?)?;
|
||||
|
||||
@@ -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,
|
||||
|
||||
|
||||
@@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
1
rust/pvimg/tools/check_hostkeydoc
Symbolic link
1
rust/pvimg/tools/check_hostkeydoc
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../genprotimg/samples/check_hostkeydoc
|
||||
@@ -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" }
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
74
rust/pvsecret/man/pvsecret-create-retrievable.1
Normal file
74
rust/pvsecret/man/pvsecret-create-retrievable.1
Normal 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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
77
rust/pvsecret/man/pvsecret-retrieve.1
Normal file
77
rust/pvsecret/man/pvsecret-retrieve.1
Normal 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)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] = &[];
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
68
rust/pvsecret/src/cmd/retr.rs
Normal file
68
rust/pvsecret/src/cmd/retr.rs
Normal 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(())
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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}" \
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
207
zkey/keystore.c
207
zkey/keystore.c
@@ -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);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user