Compare commits

..

152 Commits

Author SHA1 Message Date
Jan Höppner
2a6657dfd1 New release s390-tools-2.43.1
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 18:46:13 +02:00
Hendrik Brueckner
b3dd05e16d iucvterm/ts-shell: Add Perl taint tests for auditdir and getpwuid()
The audit directory ts-shell configuration and also the output of
getpwuid() are considered tainted when running in Perl taint (-T) mode.

Provide untaint checks with validating the content.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
f216862052 iucvterm/ts-shell: Split reading and checking configuration
The updateConfiguration() function updates and checks the ts-shell
configuration for each line in the ts-shell configuration file.

Run the checks just once after the configuration is completely read.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
e4aeeef8eb iucvterm/ts-shell: Introduce config parameter to specify iucvconn binary
Introduce a configuration setting to allow administrators to specify
path to the iucvconn binary as part of the ts-shell configuration.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
c911419121 iucvterm/ts-shell: Validate guest name before constructing the audit file
The guest name is provided on the CLI and needs to be validated before
the file path to the session audit file is constructed.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
e6de495daa iucvterm/ts-shell: Improve regex for matching terminal identifier
Anchor the regex to no longer match a part within the terminal
identifier.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
419d93bef7 iucvterm/ts-shell: Corrections to run in Perl Taint (-T) mode
Perl provides a Taint (-T) mode which tracks data that is obtained
by external means (e.g. arguments, reading file contents, environment
variables, ...)

Introduce a first set of corrections to clear and validate data
obtained from external sources.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
2714f1d90e iucvterm/ts-shell: Introduce pager config to replace env variable
Do not obtain the pager program through the PAGER environment
variable.  Instead, introduce a configuration setting to specify
the pager program as part of the ts-shell configuration.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
10ec3f18fc iucvterm/ts-shell: Correct user/group names being interpreted as regex
When ts-shell creates the authorization for current user, the
user name and group names are interpreted as regex.  This might
create additional authorizations which are not permitted for
current user.

Correct this behavior by quoting the user name in the regex.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
0c771423b4 iucvterm/iucvtty: Ensure PTY and server fd's are closed at exec
Check the return code of fcntl() calls to ensure the close-on-exec
setting succeeded.  Otherwise, the PTY and server file descriptors
would become available to the forked client process.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
f67fa03581 iucvterm/iucvtty: Prevent connection stalls when receiving TERM env
iucvtty expects to receive the TERM environment information right
after a connection has been established. Reading this information
can lead to a connection stall (DoS).  Introduce a temporary timeout
and fall back to the default TERM environment.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
29db9032b3 iucvterm: Improve received message type and length checking
The iucvtty_read_msg() now receives the entire message header.
Perform message header checks for specific message types where the
payload length is clearly defined.  Also this needs to be done prior
starting the message chunk processing because the message datalen
field will be adjusted based on the read chunks.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:06 +02:00
Hendrik Brueckner
56a55901c1 iucvterm/tests: Update test cases to use iucvtty_skip_msg_chunk()
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:45:04 +02:00
Jan Höppner
43edafd088 iucvterm/tests: Remove unused termio.h
The inclusion of termio.h leads to the following compilation error:

  CC          iucvterm/tests/test_functions.o
test_functions.c:16:10: fatal error: termio.h: No such file or directory
   16 | #include <termio.h>
      |          ^~~~~~~~~~

The termio ioctl interface has been obsolete for a very long time and
was replaced by termios. termio.h was still present for compatibility
but was removed with glibc 2.42.
iucvterm uses termios.h correctly in the tool. The test however not only
includes termio.h erroneously, it doesn't even require it. Remove the
inclusion of termio.h.

Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Hendrik Brueckner
b1e3ee255f iucvterm: Improve receiving of IUCV terminal messages
If iucvtty_read_data() receives message data and reads less than
MSG_DATA_OFFSET, out-of-bound reads will happen.

Correct and improve the receiving path by always reading the entire
message header. Also improve the "residual" message data handling by
replacing and processing every message now as chunk.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Hendrik Brueckner
a69a46eafd iucvterm/iucvtty: Close PTY slave fd after fork
The PTY slave file descriptor is the controlling terminal
allocated for the child process.  When all file descriptors
are being closed, a SIGHUP is triggered for the child process.

Keeping the file descriptor in the parent open would prevent
the SIGHUP to be delivered to the child process.

For proper SIGHUP processing, close the file descriptor in
the parent process.

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Hendrik Brueckner
c85b4e54dd iucvterm/iucvtty: Validate TERM environment name
Improve handling of TERM environment processing by validating
the received terminal name.  If the terminal name is not valid,
a message will be displayed and the default terminal will be used.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Finn Callies
da4b881eac 95sel-ebc: Add umount to boot service
Explicitly unmount the root partition on unit stopping to prevent still
mounted boot partition on switch root which results in boot being unable
to be mounted after switch root.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Finn Callies
9f03ddf519 95sel-ebc: Add udev-settle dependency
Since this unit cannot have a dependency on the actual needed
dev-disk-by2xdlabel-boot.device unit because it might not exist if the
label is never set but this unit has to run after it when it does exist
wait for udev-settle which creates the symlink if it does exist.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Aswin Karuvally
a05935cca9 osasnmpd: Fix SNMP non-compliance
Update osasnmpd to support Object ID Sub-IDs with length up-to and
including 10 digits, in line with SNMP specification. While at it, also
replace sprintf() calls with snprintf(). Together, these changes
contribute to better memory safety.

Suggested-by: Alexandra Winter <wintera@linux.ibm.com>
Reviewed-by: Hidayath Khan <hidayath@linux.ibm.com>
Signed-off-by: Aswin Karuvally <aswin@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Marc Hartmayer
6f9f846bc0 pv: verify_chain: Use error statements instead of debug
These messages indicate verification failures and are therefore reported
as errors rather than debug output. Unfortunately,
X509StoreContext::init(...) expects the callback to return an OpenSSL
ErrorStack, so it's not possible to propagate these failures through our
own error hierarchy.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Marc Hartmayer
603a2b1762 pv: Increase flexibility of verify_chain()
Increase the flexibility of verify_chain and reduce the need of using
clone.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Marc Hartmayer
dcece3f980 pv: Fix typo
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Marc Hartmayer
ff19180f03 pv: Add root CA organization pinning to certificate verification
Enforce root CA organization name requirements during certificate chain
validation to prevent accepting certificates from unexpected root CAs.

Add RootCaVerification enum with RootCaOrganizationPinning and
SkipPinning variants. Extend verify_chain() to validate root CA
organization. Default to pinning "DigiCert" for standard trust chains,
skip pinning when custom root CA is provided.

Assisted-by: IBM Bob:1.0.6
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-22 17:44:10 +02:00
Gerald Schaefer
42eba67268 mon_procd: Fix possible static buffer overflow
The command line length value is not properly checked and limited to the
documented 1024 bytes, even though a MAX_CMD_LEN is already defined but
not used.

With this, an overflow of the static char mon_record[] buffer is possible
in read_cmdline(), corrupting adjacent .bss data. The data is sanitized to
printable ASCII bytes, but in theory a crash of the mon_procd daemon could
be possible.

Fix it by adding a check and truncation similar to the other restricted
fields like e.g. ruser_len or cmd_len.

Reviewed-by: Ilya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: Gerald Schaefer <gerald.schaefer@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Peter Oberparleiter
3cf95a1d92 zdev: Harden against invalid udev data
Reduce chances of unintended side-effects when importing udev
data which might have been corrupted.

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Peter Oberparleiter
5e93b53f78 zdev: Harden against invalid import data
Reduce chances of unintended side-effects when importing device
configuration data which might have been corrupted.

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Peter Oberparleiter
360e8d4156 zdev: Harden against invalid firmware data
Reduce chances of unintended side-effects when evaluating firmware
data which might have been corrupted.

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Peter Oberparleiter
a048670bec zdev: Harden against invalid hypervisor data
Reduce chances of unintended side-effects when evaluating hypervisor
data which might have been corrupted.

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Volkan Unal
4ca93aa808 fdasd: Fix memory leak in yes_no() function
The yes_no() function was leaking memory when returning
early from the loop, as the 'answer' buffer allocated by
getline() was not freed before the return statements.

Restructure the function to use a single exit point, ensuring
free(answer) is always called before returning.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Volkan Unal <vunal@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Eduard Shishkin
9a5886c0b3 zipl/src: Check keyword duplications in BLS entries
sort_bls_entries() already checks for the keyword "title"
duplication. Add also checks for keywords "linux", "initrd", and
"options" duplication (Rule #5).

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Eduard Shishkin
78676df2f0 zipl-editenv: Check in-bootmap environment block validity
Check decimal prefixes, representing site values in on-disk
environment block. Reject any values different from {0, ..., 9}
as invalid ones.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Eduard Shishkin
224e35b600 zipl/src: Check file trailer
Before extracting a file trailer, check that the file is big enough;
Check that signature length stored in the extracted trailer doesn't
exceed the size of the file without the trailer. Treat the case of
the failed checks as unsigned file.

Without the checks, memcmp() may read the area before the allocated
@buffer, dumping heap into bootmap or crashing.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Eduard Shishkin
bd52e14740 zipl/boot: Check in-bootmap environment block syntax
When parsing environment block, check each its line for the
compliance with the 'foo=bar\n' pattern. In case of missing '=',
or '\n' abort the parsing procedure with a warning message.

Without this check, stage3 may write a zero byte to the absolute
address 0 (robustness defect)

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Eduard Shishkin
dd380942ed zipl/boot: Limit the hash table size when parsing environment block
When parsing environment block, limit the number of the hash table
entries, so that total size of all records doesn't exceed PAGE_SIZE
bytes allocated for that hash table.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Eduard Shishkin
9ce9ed72a9 zipl/boot: Fix the bounds check in the command line processing
By design, before replacement of '${FOO}' with its value, the
procedure checks that the resulted command line doesn't exceed the
maximum one.
The old check used the never updated length of the original command
line, which is incorrect. Instead, use its current length resulting
from the replacement happened at the previous iteration.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Mikhail Zaslonko
a8a87779c0 zdump/dfi_lkcd: Validate LKCD page address against mem_end
mem_init_flex() allocates the page header index array sized for
ADDR_TO_IDX(mem_end) + 1 entries, but never checked that each
page record's address falls within [0, mem_end).
A crafted LKCD page record with addr >= mem_end writes 8 bytes
beyond the allocation.

Fix by rejecting any page record whose address is > mem_end - PAGE_SIZE
with ERR_EXIT(), immediately after the end-of-dump marker check.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Mikhail Zaslonko
cdabf280ac zdump/dfi_lkcd: Validate LKCD page size before buffer read
read_page_buf() uses the file-controlled pg_hdr->size directly as the
byte count for zg_read() into fixed stack buffers of PAGE_SIZE.
A crafted LKCD dump with pg_hdr->size > PAGE_SIZE overflows the buffer
and smashes the stack frame. A size of 0 for a raw page silently
produces uninitialised data.

Enforce page header size constraints following crash-utility's logic:
- Compressed or Raw pages with size > PAGE_SIZE are invalid;
  exit with an error.
- Raw pages with size 0 contain no data in the file; fill the
  output buffer with zeros without reading.
- Compressed pages with size 0 are caught by the uncompress() return
  code check.
- Raw pages with size != PAGE_SIZE are invalid; exit with an error.

Check the return code of uncompress() and exit with an error if
decompression fails, rather than silently proceeding with an incomplete
output buffer.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Marc Hartmayer
33c75584cd zipl: Fix -Wstringop-overread warning in IMPORT_DATA macro
Declare binary data symbols as arrays to correctly represent
linker-provided symbols of unknown size. This resolves compiler warnings
about reading 22-24 bytes from a 1-byte region when including stage*
loaders.

DEBUG: In function ‘memcpy’,
DEBUG:     inlined from ‘boot_init_eckd_ldl_stage0’ at boot.c:181:2,
DEBUG:     inlined from ‘install_svdump_eckd_ldl’ at install.c:891:2,
DEBUG:     inlined from ‘install_dump’ at install.c:1221:9,
DEBUG:     inlined from ‘main’ at zipl.c:185:9:
DEBUG: /usr/include/bits/string_fortified.h:29:10: warning: ‘__builtin_memcpy’ reading 22 bytes from a region of size 1 [-Wstringop-overread]
DEBUG:    29 |   return __builtin___memcpy_chk (__dest, __src, __len,
DEBUG:       |          ^

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Alexander Egorenkov
1f68c1aaf3 zdump/ngdump: Perform sanity checks on path to dump image
Ensure that the path to a dump image specified in the NGDump meta file
of a dump device points to a valid location within the dump device.
Especially, disallow escaping from a dump device with a dump image path
using references to '..' or symbolic links pointing outside of the dump
device.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Ilya Leoshkevich <iii@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Alexander Egorenkov
6c6938d1e2 zdump/ngdump: Use OpenSSL's SHA256 to compute digest of dump image
Replace the popen() call to the external tool sha256sum with OpenSSL's
SHA256 digest algorithm to compute the checksum of a dump image
to improve security of NGDump.

The call to popen() is inherently insecure because a malicious user
controlling a dump device could manipulate the latter and let the user
applying zgetdump to the dump device execute arbitrary shell code.

Furthermore, getting rid of process and shell spawning performed by
popen() shall improve performance of calculation of dump image
checksums.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Alexander Egorenkov
90ad42d814 zdump/zg: Use fstat() to obtain file information in zg_open()
Use the file descriptor opened in zg_open() before to obtain information
about the file. Theoretically, there is a tiny window for a race
condition between opening a file and obtaining its information with
stat(). A malicious user could use it to entice incorrect behavior
from zgetdump. Using the same file descriptor for both operations
prevents such situations.

Suggested-by: Peter Jin <pjin@linux.ibm.com>
Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Alexander Egorenkov
cf7c74b2dc zdump/ngdump: Include header zg.h in ngdump.h
Add the missing header include statement because some function
declarations use struct zg_fh from zg.h.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Alexander Egorenkov
4388a6d299 zdump/ngdump: Remove prefix 'ngdump_' from the name of private functions
The name of a private NGDump function does not require to have a unique
prefix to avoid naming conflicts with public functions of other C modules.
Furthermore, be consistent with private NGDump functions not having
the prefix 'ngdump_'.

Signed-off-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Mikhail Zaslonko
44821d0586 zdump/dfo_elf: Include vmcoreinfo note size in ELF header allocation
dfo_elf_init() allocates the output ELF header buffer based on
HDR_BASE_SIZE, per-CPU note sizes, and per-memory-chunk overhead,
but does not account for the vmcoreinfo note. notes_init() then
writes the full vmcoreinfo string into the buffer via nt_vmcoreinfo(),
overflowing the heap allocation for any dump with a vmcoreinfo blob
larger than what fits in HDR_BASE_SIZE. The following ABORT check
fires after the heap was already corrupted.

Fix by extracting the allocation size calculation into a new
hdr_alloc_size() function that explicitly accounts for all items
written into the header buffer.
Remove the opaque HDR_BASE_SIZE and HDR_PER_MEMC_SIZE macros.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Mikhail Zaslonko
405b2da3f3 zdump/dfi_vmcoreinfo: Validate vmcoreinfo value length before memcpy
vmcoreinfo_item() copies a key's value string from the vmcoreinfo heap
blob into the caller's buffer using memcpy() without checking the source
length. The length is derived from the distance between the '=' separator
and the next '\n' (or '\0') in the blob, which is bounded only by the
total vmcoreinfo size. A crafted dump with a vmcoreinfo value >= 1024
bytes would overflow the buffer.
Additionally, the len parameter of vmcoreinfo_item() was declared UNUSED
and never checked.
Fix by computing val_len before the copy and returning -1 if val_len >= len.
Change len type from int to size_t, which is natural for a buffer size,
drops the need for a negativity guard, and makes the call site passing
sizeof(str) type-consistent.
Write directly into the caller's buf, instead of going through the
intermediate str[].
Replace two strchr() calls with a single strchrnul().

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Mikhail Zaslonko
d3ac9f3365 zdump/dfi_vmdump: Validate ADSR sec5_len before buffer read
The 16-bit sec5_len field from the ADSR record was used directly as
the byte count for zg_read() and ebc_2_asc() into a pair of 1024-byte
stack buffers.  A crafted VMDUMP file with sec5_len > 1024 would
overflow both buffers and write past the stack frame.

The overflow is only reachable when zgetdump is invoked with the -V
(verbose) flag, but that is a common diagnostic usage.

Section 5 contains a human-readable dump symptom string used only for
display purposes.  Truncate sec5_len to sizeof(buf) - 1 so that
oversized values are silently clamped rather than causing an abort,
and the symptom string is still printed up to the buffer limit.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
7535682b1b zkey: Silence false positive -Wstringop-overflow= warning
Building with ASAN=1 produces the following warning:

    utils.c: In function 'sysfs_get_mkvps':
    utils.c:416:9: warning: writing 8 bytes into a region of size 0
                                                [-Wstringop-overflow=]
      416 |         memcpy(mk_reg->mkvp, &mkvp, sizeof(mkvp));
          |         ^
    In file included from utils.c:32:
    utils.h:51:17: note: at offset 4 into destination object 'mk_state'
                                                 of size 4
       51 |         int     mk_state;

This is a false positive, field mk_reg->mkvp is defined as an array of
u8 with a length of 16 elements, thus it is 16 bytes in size, and thus
more than long enough to copy 8 bytes into it.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
72f1a2e0a9 zkey: Fix typo
The word 'device' is spelled wrongly at 2 places.

Reported-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
220f51fb98 libseckey: Fix integer underflow for malformed SPKIs
When the BER encoded BITRSTRING field has a zero length, then skipping
the unused-bits byte that a BITRSTING normally contains will cause a
underflow and thus may cause an out of bounds read by the caller of
sk_ep11_parse_spki().

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
5ab3473442 libseckey: Fix length check in sk_cca_get_public_from_rsa_key()
Check for the size of struct cca_rsa_pub_key_section, instead of
struct cca_ec_pub_key_section.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
cf51aa1421 libseckey: Fix incorrect flag checking related to RSA X9.31
Use a bit-wise AND, not a logical AND to mask the flag out of the flags
field.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
b1d44876bd libseckey: FIx memory leak in error path (OpenSSL 1.1.1 only)
When EVP_PKEY_get0_EC_KEY(), EC_KEY_set_ex_data(), EVP_PKEY_get0_RSA()
or RSA_set_ex_data() fails, the function returns -EIO without freeing
the data struct or the data->key_blob that were allocated before.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
c006800bd5 libseckey: Fix length check of SPKI
Field spki_size is defined as size_t, so it can never be negative.
Before the check, sk_ep11_valid_ep11_blob() ensures that hdr->len is
never greater than key_token_length, so the subtraction can never
overflow the result, but it still can be zero.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:59 +02:00
Ingo Franzki
634095aa24 libseckey: Fix possible out of bounds read in sk_ep11_parse_der_tag()
For long-length field encoding, check if the number of length bytes
exceed the data size.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
b4f575abc4 libseckey: Fix possible endless loop for malformed CCA key tokens
A malformed CCA key token with a zero size section can cause an endless
loop in sk_cca_get_pka_section().

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
6c729fba9e libseckey: Fix error checking in SK_OPENSSL_init() (OpenSSL 1.1.1 only)
Fix the NULL-checks to test the variable that was just assigned.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
71503c0e7d libseckey: Fix wrong RSA public exponent value 257 assignment
Array element key_value_structure.public_exponent[0] is written twice,
but key_value_structure.public_exponent[1] is not set, leading to a
wrong public exponent value (i.e. 0x01) in the key value structure.

CCA key generation will fail is this case.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
5c8bd68f6f libseckey: Fix and document size query call with NULL key_token
Functions SK_CCA_generate_ec_key_pair(), SK_CCA_generate_ec_key_pair(),
SK_EP11_generate_ec_key_pair() and SK_EP11_generate_rsa_key_pair() are
supposed to allow parameter key_token to be NULL. In this case the
required buffer size is returned in key_token_length only.

This size query does not work because the parameter checking rejects
the call with a NULL key_token returning -EINVAL.

Currently the size query is not used by any known caller, but lets
correct it anyway.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
927f901969 libekmfweb: Fix size check of the response party info
The party info contained in the response might be of an arbitrary size.
Check for an overflow before allocating the party info buffer for the
combined request and response party info data. With a very large
response party info size, the combined size might overflow (i.e. wrap
around), causing a too small buffer being allocated and the following
memcpy's would cause an out of bounds write.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
35cc35894b libekmfweb: Fix integer overflow errors in base64 encode/decode functions
On large data sizes the calculation may cause an overflow, if done with
int type. Do the calculation with size_t instead to avoid an overflow.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
dfaa1791b7 libekmfweb: Fix error checking typos
Fix the error checking to check the correct variables using the right
conditions.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
52158e73fa libekmfweb: Fix invalid free in error path of parse_json_web_token()
Don't attempt to free the local function parameter 'signature', but the
memory where *signature points to.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
cc6f1a6d7b libkmipclient: Fix kmip_parse_decimal_uint() to reject negative numbers
Function strtoull() silently accepts a leading '-' and returns a large
positive number (two's complement wrap). Reject this by checking for
the minus character.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
866324bcd2 libkmipclient: Fix possible NULL pointer dereference
Function gmtime() might return NULL which would then be passed to
strftime(). Return an error in case gmtime() return NULL.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
7fd2421a2c libkmipclient: Detect possible length overflow situations
A deeply nested or pathologically large KMIP node tree crafted by a
malicious server can wrap the length calculation around to a small
value. Detect this and return an error in this case.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
66581d57f3 libkmipclient: Fix error checking in kmip_node_clone()
Check the right pointer after strdup.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
de688f350f libkmipclient: Fix invalid return of -1 for an unsigned int
Functions kmip_node_get_structure_element_count() and
kmip_node_get_structure_element_by_tag_count() return an unsigned int,
but the error case returns -1. This leads to a wrap around actually
returning 0xffffffff which the caller might interpret as a very large
number of elements.

Return 0 in case of an error instead.

Also fix some callers to not unconditionally subtract -1 from the
returned value,  but return an error if the returned value is zero.
These callers check the preconditions upfront, so the error case
won't be hit anyway.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
958ccb46c5 libkmipclient: Fix parsing of optional 'name' element
Duplicate the 'name_obj' string, not the 'tag_obj' that was previously
used.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
3ea90a2258 libkmipclient: Fix integer overflow with large value length
Limit the value_len to not be larger than INT_MAX, because later on
BIO_read() is called with value_len and it uses the int type for
length parameter and return value.

This check also prevents the 'value_len + 1' from overflow, because
value_len is a size_t and this accepts larger values than int (even
on 32 bit architectures).

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
7d56dde24a libkmipclient: Fix size underflow when padding bytes exceed remaining size
When decoding a value that is exactly at the boundary of the available size
(i.e., *size == value_len), and the value length is not a multiple of the
TTLV block length, then *size wraps to a huge value when the pad_len is
subtracted.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
c7d0d3c1b9 libkmipclient: Fix integer overflow in kmip_format_hex()
If length is >= 0x80000000 (2 GB), length * 2 wraps around to a small
value, calloc allocates a too small buffer, then the loop writes
length * 2 bytes into it causing a heap buffer overflow.

Fix this by using a size_t for size calculation, and also checking
the length before multiplication (needed on 32 bit platforms).

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
d7648875cc libkmipclient: Fix signed-integer overflow in TTLV tag/length byte-shift
In C, operands of '<<' are subject to integer promotion. So the unsigned
char array elements are promoted to signed int and then shifted producing
signed overflow — undefined behaviour in C.

Fix this by first casting to uint32_t and then shift.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
5fdeaab3d0 libkmipclient: Limit the nesting level of KMIP STRUCTURE recursion
KMIP STRUCTURE elements can be nested, which causes a recursion of
functions kmip_decode_ttlv(), kmip_decode_xml(), and kmip_decode_json().
A malformed KMIP response may thus cause stack exhaustion.

Limit the KMIP STRUCTURE nesting level to 32 levels. This is more than
enough for currently defined KMIP responses. The practically used
nesting level is 8 or 9, dependent on the type of KMIP response.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
f0bf1985c3 libkmipclient: Protect from symlink-following attacks
When creating or writing files, make sure that the file is not a
sysmlink. Such files created by libkmipclient are typically stored inside
the zkey repository and the owner and mode of them are changed to.
allow read/write for the owner user and the 'zkeyadm' group. It
would allow a symlink-following attack if the file being created are
symlinks. Make sure to open such files with the 'O_NOFOLLOW' flag.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
a3a2d70603 libekmfweb: Protect from symlink-following attacks
When creating or writing files, make sure that the file is not a
sysmlink. Such files created by libekmfweb are typically stored inside
the zkey repository and the owner and mode of them are changed to.
allow read/write for the owner user and the 'zkeyadm' group. It
would allow a symlink-following attack if the file being created are
symlinks. Make sure to open such files with the 'O_NOFOLLOW' flag.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
226728731c libseckey: Protect from symlink-following attacks
When creating or writing files, make sure that the file is not a
sysmlink. Such files created by libseckey are typically stored inside
the zkey repository and the owner and mode of them are changed to.
allow read/write for the owner user and the 'zkeyadm' group. It
would allow a symlink-following attack if the file being created are
symlinks. Make sure to open such files with the 'O_NOFOLLOW' flag.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
94292dac54 zkey: Protect from symlink-following attacks
Files in the zkey repository can be created by any member of the
'zkeyadm' group as well as 'root'. Such files are owned by the creator
and the 'zkeyadm' group, and allow read and write for the owner user
and owner group.

When creating or writing files inside the zkey repository, make sure
that the file is not a sysmlink. That way, only files within the zkey
repository are set to be read/write for the owner user and members of
the 'zkeyadm' group. Make sure to open such files with the 'O_NOFOLLOW'
flag, and use 'lstat()' to check for files and directories.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Ingo Franzki
278f4f6fd5 zkey: Check values before constructing a command to execute
Check the values that are incorporated into commands that might get
executed to not contain any characters that could be used for
shell-injection attacks.

Assisted-by: IBM Bob:2.0.0
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Finn Callies
17007ab121 95sel-ebc: Harden boot mount service
Pin the supported filesystem type to ext4 to prevent the risk of auto fs
parsing bugs. Additionally mount the boot partition with more
restrictive options.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Finn Callies
4f9e5d7842 95sel-ebc: Reorder PAES enforcement service
Fix the sel-ebc-paes-enforce.service to run AFTER the sysroot.mount
which prevents a TOCTOU situation.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Finn Callies
8a52693acc 95sel-ebc: Fix SICS existence check
Fix the sel-ebc-pvebc.service unit to execute the failure action when
the sics directory does not exist instead of getting skipped.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
a93146d976 pv: BinReqValues: Fix length validation in get()
Return an error when the provided request data is smaller than the
expected request length. The previous condition used the wrong
comparison, which could lead to an out-of-bounds slice and panic.

Add a regression test.

Fixes: 34bef977e8 ("rust/pv: User-data signing and verifying")
Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
187437c6c8 pv: Defer CRL downloads until certificate validation succeeds
Perform certificate verification in two stages. First, verify the
certificate chain without CRL checks. Once the chain has been validated,
download the referenced CRLs and repeat the verification with CRL
checking enabled.

Fixes: c6f621d0dc ("rust: Add library for pv tools")
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
cdc787d92d pv: download_first_crl_from_x509: Increase the timeout to 10s
3-second timeout might be too short for slow networks therefore increase
it to 10s. In addition move this constant to a more prominent position
and add documentation.

Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
5feee12827 pv: Limit CRL download size
Add a dedicated HKD verification error for CRL downloads that exceed the
maximum file size of 10 MiB and cover the max_filesize behavior in the
helper tests and add tests for it.

This commit adds a new development/test dependency as it verifies that
the correct messages are being logged.

Fixes: c6f621d0dc ("rust: Add library for pv tools")
Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
cf70a27d76 pv: Restrict CRL downloads to HTTP(S) and limit redirects
Only allow CRL downloads over HTTP and HTTPS to avoid accessing
unexpected protocol handlers.

The Rust curl bindings do not expose support for configuring allowed
protocols or the maximum number of redirects [1][2][3]. Therefore,
redirect handling is implemented manually, validating each redirect
target and enforcing a maximum of five redirects.

The redirect limit also prevents infinite redirect loops.

[1] https://curl.se/libcurl/c/CURLOPT_PROTOCOLS_STR.html
[2] https://curl.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS_STR.html
[3] https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html

Fixes: c6f621d0dc ("rust: Add library for pv tools")
Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
5552b476bd pv: tests: Regenerate the fake certificates using http:// URL
Adapt the script to generate the test certificates to use the prefix
'http://' for the CRL distribution points as otherwise the test would
fail with an upcoming change. Regenerate the certificates and adapt the
test case dist_points' accordingly as the distribution point has
changed.

Note: The files der.crt and der.crl are skipped as that would result in
      binary changes which caues trouble with quilt. They are only used
      by some basic tests read_crls and read_crts anyway

Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
7510b96238 pv: Fix script to generate the test certificates
Fix datetime.datetime.utcnow() deprecation and fix a TypeError.

  File "create_certs.py", line 16, in createEcKeyPair
    return ec.generate_private_key(curve=curve, backend=default_backend())
           ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  TypeError: curve must be an EllipticCurve instance

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:58 +02:00
Marc Hartmayer
778d9e436a pv: Add more tests for testing the CRL download functionality
Add more tests for testing the CRL download functionality.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Marc Hartmayer
692af4058c pv: Rewrite CRL download tests
Replace the existing mocks with a trait-based test infrastructure. This
allows testing download_first_crl_from_x509() functionally while
avoiding actual network access, resulting in more comprehensive and
realistic test coverage.

Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Marc Hartmayer
2565a4ca99 pv: verify: helper: Rename module test into tests
Rename the module to the more common test module name tests and remove a
superfluous comment.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Marc Hartmayer
6d5f4a99de pv: Refactor CRL download code for easier testing
Use a trait-based abstraction for CRL downloads so the download
implementation can be replaced more easily, for example in tests that
should avoid real network calls.

Assisted-by: IBM Bob:1.0.5
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Marc Hartmayer
a9ed19285a pv: download_first_crl_from_x509: Fix comment
The comment mentions certificate but it's a certificate revocation
list (CRL). In addition, use 1200 bytes as vector capacity as the
comment says 1200.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Marc Hartmayer
4eb18bd6b7 pv: download_first_crl_from_x509: Log errors
Although most errors are ignored and the next CRL distribution point is
tried, log the errors as debug messages.

Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Finn Callies
cf45a6e108 pvics: Fail on non-existend BLE
Issue an error if the specified boot loader entry cannot be found.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-20 15:25:57 +02:00
Marc Hartmayer
a879036e3f pv/error: Fix typo in BinArcbSeaSmall error message
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Marc Hartmayer
5340d11165 pv: test_utils: Add TEST-RAND generator for testing
Implement a test random number generator (RNG) using the TEST-RAND [1]
generator provided by OpenSSL. This allows writing tests for OpenSSL
functionality where OpenSSL internally uses RNGs, e.g. used for ML-KEM.

The implementation uses RAND_set0_public [2] to set the
thread-local RNG, which requires OpenSSL >= 3.1.

[1] https://docs.openssl.org/3.0/man7/EVP_RAND-TEST-RAND/
[2] https://docs.openssl.org/3.1/man3/RAND_set0_public

Assisted-by: IBM Bob:1.0.5
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Acked-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Sven Schnelle
162a041077 cmsfs-fuse: Fix strip_right() function
strip_right() strips spaces beginning at the end of the string moving
to the beginning. However, it doesn't check whether it's at beginning
of the array, and would continue reading if the string only contains
spaces.

Fix this by adding the necessary check.

Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Reviewed-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Sven Schnelle
b38d5833fa hmcdrvfs: Fix hmcdrv_path_copy()
While iterating over the string, hmcdrv_path_copy() subtracts one byte
of the length to account for the NUL byte, but fails to account for
the '/' character it might add. Change the test to leave two bytes of
room.

Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Eduard Shishkin
ee8897f9db zipl/src: Add ->set_location() private method of program component
Add/use ->set_location() private method of struct component_footer
instead of checking component types every time when operating with
program components.

Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Eduard Shishkin
4f622325ef zipl/src: Fix add_component_file_range()
Fix add_component_file_range() to process components of all types
(not only of COMPONENT_TYPE_LOAD), similar to how it is going in
add_component_buffer().

Earlier components of COMPONENT_TYPE_SIGNATURE were processed only
by add_component_buffer(). Starting from 60bc1e3, signatures for
mirrors (i.e. base disks of #1 and larger ID) are processed also by
the function add_component_file_range(), which works incorrectly for
such type of components.

Fixes: 60bc1e38d0 ("zipl/src: Reuse data of buffer components in bootmap")
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Eduard Shishkin
f2902a1990 zipl/src: Fix alignment of componets location in bootmap file
Calculate the alignment value as maximum of physical block sizes of
disks participating in the mirrored setup. Earlier the alignment was
calculated as a physical block size of the first disk of a mirrored
setup.

Without this change, the assertion in disk_get_blocklist_from_file()
'assert(reg->offset % info->phy_block_size == 0)' is triggered in case
when blocksize of base disk #J > blocksize of base disk #I && (J>I).
To reproduce the problem, build a mixed mirrored setup with first SCSI
disk, second DASD disk and prepare it for IPL.

Fixes: 731f00202c ("zipl/src: Enable heterogeneous mirrors support")
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Eduard Shishkin
9882aabe44 zipl/src: Update format of zipl helpers output
Change the format of zipl helpers output to support hybrid mirrors
(i.e. composed of disks of different types and geometry).
The new format imposes an additional requirement that any key-value
pair "targetbase=X:Y" titles a new section with a set of target
parmeters for the base disk identified by that pair.

Without this change, zipl reports "Inconsistent script output".
To reproduce the problem, build a mixed mirrored setup with first
SCSI disk and second DASD disk, and prepare it for IPL.

Fixes: 731f00202c ("zipl/src: Enable heterogeneous mirrors support")
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Marc Hartmayer
d06d197522 pv: Fix unfulfilled_lint_expectations warning
The test cases uses the ml_kem functions, therefore disable the
directive for tests.

$ cargo test
...
 --> pv/src/openssl_extensions/ml_kem.rs:5:11
  |
5 | #![expect(unused)]
  |           ^^^^^^
  |
  = note: `#[warn(unfulfilled_lint_expectations)]` on by default

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Mikhail Zaslonko
3fd07bb7f2 zdump/df_vmdump: Add a documentation link to the header file
Add a link to 'CP Data Areas and Control Blocks' manual.

Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:06 +02:00
Ingo Franzki
58ede126cc zkey: Retry PKEY_KBLOB2PROTK3 ioctl in case of EBUSY
After a master key change, it can happen that the PKEY_KBLOB2PROTK3 ioctl
returns EBUSY. This is a temporary situation and the operation will
succeed, once the firmware has completed some internal processing related
with the master key change. Delay 1 second and retry up to 10 times.

A similar retry loop was previously used for the AF_ALG-based handling,
but the retry logic was not included for the new ioctl-based handling.

Fixes: 7fffdcfe8c ("zkey: Remove the use of AF_ALG for calculating key verification patterns")
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:05:03 +02:00
Volkan Unal
e4a03c6f71 dasdfmt: Check disk type before prompting for blocksize
Move check_disk() call before user input to fail fast
on unsupported or unavailable devices (FBA, read-only,
in-use, raw-track mode).

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Volkan Unal <vunal@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-07-03 12:01:58 +02:00
Jan Höppner
c66495888d Prepare for next release
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:49:06 +02:00
Jan Höppner
f85c8ebba0 New release s390-tools-2.43.0
Signed-off by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:49:06 +02:00
Marc Hartmayer
e4e455630b pv + tools: Introduce versioned HostKey and Keyslot enums
Add a HostKey enum (currently V1(PKey<Public>)) and introduce a
versioned Keyslot enum (V1(KeyslotV1)). Rename the existing Keyslot type
to KeyslotV1 to prepare for future format extensions.

Update pv, pvattest, pvimg, and pvsecret to use the new enums.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:45 +02:00
Timo Keller
d82beef937 pv: Implement Display for SymKey based on key_type()
Add a Display implementation for SymKey that formats values as
`SymKey(<type>)`, delegating to key_type(). This provides a clearer
and stable human-readable representation for logs and CLI output.

Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Marc Hartmayer
c3e61e5e0f pv: from_se_image: Return SE header version in addition to tags
Return the Secure Execution header version in addition to tags. This
comes handy as soon as multiple Secure Execution header versions are
supported.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Marc Hartmayer
f04e46baf0 pv: req: Improve documentation and fix typos
Improve documentation and fix typos.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Marc Hartmayer
e93bd7e61c pv: req: Split into multiple files
Split req.rs into multiple files so that additions can be done more
easily.

Assisted-by: IBM Bob:1.0.4
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Timo Keller
edf40976f0 pv: Add OpenSSL ML-KEM bindings
Add interfaces for creating an ML-KEM keypair and encapsulation and
decapsulation.

The public part of it can be used to encapsulate a shared secret in a
ciphertext. The latter can be decapsulated with the private part of the
keypair to recover the shared secret.

In terms of code, a keypair of type `PKey<Private>` can be generated
with `generate_ml_kem` specifying `KeyType::ML_KEM_512`,
`KeyType::ML_KEM_768`, or `KeyType::ML_KEM_1024`.

To encapsulate a shared secret, generate a `PkeyCtx<Public>` with
`PkeyCtx::new`, initialize it with `encapsulate_init`, and encapsulate
with `encapsulate_to_vec`.

To decapsulate the ciphertext to the shared secret, generate a
`PkeyCtx<Private>` with `PkeyCtx::new`, initialize it with
`decapsulate_init`, and decapsulate with `decapsulate_to_vec`.

Note that when https://github.com/rust-openssl/rust-openssl/pull/2532
is getting merged into `rust-openssl`, these changes will become
unnecessary.

Assisted-by: IBM Bob:1.0.4
Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Timo Keller <tkeller@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Marc Hartmayer
e557f41c6e pvimg: Unignore a Rust doctest
While at it, improve it.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Marc Hartmayer
68fe816da9 pvimg: Add more CCK tests
Add more CCK tests to make sure that new changes do not introduce a
regression.

Assisted-by: IBM Bob:1.0.4
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Marc Hartmayer
367ca6e16b pvimg: Add comment why cck and not cck-available is required
Add a comment to the code to explain why cck and not cck-available is
required.

Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Volkan Unal
6c67f05ca7 dasdinfo: Fix memory leak in dinfo_get_uid_from_devnode()
The function dinfo_get_uid_from_devnode() allocates memory for
readbuf but fails to free it in two code paths:
1. When the device name is truncated (error path)
2. At the successful function exit

Add the missing free(readbuf) calls to prevent memory leaks in
both paths.

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Volkan Unal <vunal@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Jörn Siglen
4887e80995 dbginfo.sh: Add IBM appliance specific files
Some IBM appliance has additional config data /etc/fpc files or directory

Reviewed-by: Mario Held <mario.held@de.ibm.com>
Signed-off-by: Jörn Siglen <siglen@de.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Steffen Eiden
74a338edfc pvattest: Expand HTTPS timeout to 2 min
Match the clients timeout to the servers timeout.

Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Steffen Eiden
8878ba4fd2 pvattest: Add firmware check version 2
Add Response version 2 which includes more details about the
verification process.

Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Steffen Eiden
e53f5ccfea pvattest: Refactor firmware checking
Refactor the firmware verification client such that adding a new
request/response versions is simpler.

Reviewed-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Jan Polensky
6e7eb62ea1 cpumf/lshwc: Show explicitly selected unnamed counters with --hide
When an unnamed counter is explicitly selected by numeric ID via
--counters (for example, --counters=218), show it even when --hide is
enabled.

Keep --hide effective for unnamed counters that were not explicitly
selected. To do that, evaluate the counter filter once per counter and
reuse the result to distinguish between a generic match and an explicit
numeric-ID match.

Suggested-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:44 +02:00
Eduard Shishkin
2bb61f96c0 zipl/src: Make use of newly introduced verbosity classes/levels
Assign VERBOSITY_DEBUG class to the message printing information
about zipl helpers called by zipl tool.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Eduard Shishkin
a902fd9afe zipl/src: Introduce verbosity levels of zipl session
Introduce verbosity levels of zipl session and verbosity classes of
messages. This is used by the next patches in the series to suppress
information not corresponding the default output of zipl tool that
could be confusing for user.

Add a new option "--debug" of zipl tool to set up verbosity level 2
(and higher) of zipl session.

Reviewed-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Eduard Shishkin <edward6@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Finn Callies
dc42460d82 scripts/pvics: Fix virt-resize permission error
Although the script is to be run with root privileges virt-resize starts
as user qemu under the hood which depending on image location may cause
permission errors. To fix this use `LIBGUESTFS_BACKEND=direct`.

Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Marc Hartmayer
6689e25865 rust: Fix all cargo clippy findings
- Remove useless type conversion in uvdevice.rs
- Replace useless comparison in hostname.rs
- Replace unnecessary unwrap patterns in pvapconfig
- Use sort_by_key instead of sort_by in pvimg example

Command line used to get the findings:

  $ clippy --all-features -- --cap-lints=warn
  warning: useless conversion to the same type: `u64`
    --> pv_core/src/uvdevice.rs:56:28
     |
  56 |         rc = ioctl(raw_fd, cmd.try_into().unwrap(), cb.as_ptr_mut());
     |                            ^^^^^^^^^^^^^^
     |
     = help: consider removing `.try_into()`
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
     = note: `#[warn(clippy::useless_conversion)]` on by default

  warning: this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
    --> utils/src/hostname.rs:60:13
     |
  60 |     assert!(isize::try_from(buf_len).unwrap() <= isize::MAX);
     |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: because `isize::MAX` is the maximum value for this type, this comparison is always true
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
     = note: `#[warn(clippy::absurd_extreme_comparisons)]` on by default

  warning: `utils` (lib) generated 1 warning
      Checking pvebc v0.12.0 (/home/mhartmay/git/s390-tools/rust/pvebc)
  warning: consider using `sort_unstable_by_key`
     --> pvapconfig/src/ap.rs:177:9
      |
  177 |         self.0.sort_unstable_by(|a, b| b.gen.cmp(&a.gen));
      |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      |
      = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
      = note: `#[warn(clippy::unnecessary_sort_by)]` on by default
  help: try
      |
  177 -         self.0.sort_unstable_by(|a, b| b.gen.cmp(&a.gen));
  177 +         self.0.sort_unstable_by_key(|b| std::cmp::Reverse(b.gen));

  warning: called `unwrap_err` on `r` after checking its variant with `is_err`
    --> pvapconfig/src/main.rs:55:29
     |
  54 |         if $r.is_err() {
     |         -------------- help: try: `if let Err(<item>) = r`
  55 |             eprintln!("{}", $r.unwrap_err());
     |                             ^^^^^^^^^^^^^^^
  ...
  87 |     on_error_print_and_exit!(r);
     |     --------------------------- in this macro invocation
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
     = note: this warning originates in the macro `on_error_print_and_exit` (in Nightly builds, run with -Z macro-backtrace for more info)

  warning: this `repeat().take()` can be written more concisely
    --> pvimg/src/se_img_comps/bootloader/ipl.rs:95:21
     |
  95 |           let comps = iter::repeat(ipl_pb0_pv_comp::default())
     |  _____________________^
  96 | |             .take(num_comp)
     | |___________________________^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(ipl_pb0_pv_comp::default(), num_comp)`
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n
     = note: `#[warn(clippy::manual_repeat_n)]` on by default

  warning: this `repeat().take()` can be written more concisely
     --> pvimg/src/se_img_comps/bootloader/ipl.rs:113:21
      |
  113 |         let comps = iter::repeat(comp).take(num_comp).collect();
      |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(comp, num_comp)`
      |
      = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n

Reviewed-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Marc Hartmayer
20f6791fb4 pvapconfig: Suppress unnecessary unwrap warning
The current code is easier to read as the suggested fix by Clippy.
Therefore adding directives to allow the unwrap.

  warning: called `unwrap` on `a1.info` after checking its variant with `is_some`
     --> pvapconfig/src/ap.rs:195:36
      |
  192 |                     && a1.info.is_some()
      |                        ----------------- the check is happening here
  ...
  195 |                     let i1 = match a1.info.as_ref().unwrap() {
      |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
      |
      = help: try using `match`
      = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
      = note: `#[warn(clippy::unnecessary_unwrap)]` on by default

  warning: called `unwrap` on `a2.info` after checking its variant with `is_some`
     --> pvapconfig/src/ap.rs:199:36
      |
  193 |                     && a2.info.is_some()
      |                        ----------------- the check is happening here
  ...
  199 |                     let i2 = match a2.info.as_ref().unwrap() {
      |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
      |
      = help: try using `match`
      = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap

Suggested-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Reviewed-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Marc Hartmayer
f0a148fd0b rust: Fix cargo clippy false positives
Add allow attributes for intentional patterns in pvverify and pvsecret.

Command line used to get the findings:

  $ clippy --all-features -- --cap-lints=warn
  ...
  help: remove this field
    --> pvverify/src/cli.rs:22:5
     |
  22 |     version: (),
     |     ^^^^^^^^^^^
     = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
     = note: `#[warn(clippy::manual_non_exhaustive)]` on by default
  help: use the `#[non_exhaustive]` attribute instead
     |
  16 + #[non_exhaustive]
  17 | pub struct CliOptions {
     |

  warning: struct `AddSecretOptComb` is never constructed
     --> pvsecret/src/cli.rs:313:12
      |
  313 | pub struct AddSecretOptComb<'a> {
      |            ^^^^^^^^^^^^^^^^
      |
      = note: `-W dead-code` implied by `-W unused`
      = help: to override `-W unused` add `#[expect(dead_code)]` or `#[allow(dead_code)]`

  warning: struct `ListSecretOptComb` is never constructed
     --> pvsecret/src/cli.rs:355:12
      |
  355 | pub struct ListSecretOptComb<'a> {
      |            ^^^^^^^^^^^^^^^^^

  warning: struct `RetrSecretOptionsComb` is never constructed
     --> pvsecret/src/cli.rs:490:12
      |
  490 | pub struct RetrSecretOptionsComb<'a> {
      |            ^^^^^^^^^^^^^^^^^^^^^

Assisted-by: IBM Bob:1.0.4
Reviewed-by: Timo Keller <tkeller@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Marc Hartmayer
390e48869e pvebc: Fix cargo doc warning
$ cargo doc
...
warning: unclosed HTML tag `u8`
  --> pvebc/src/ebc_utils.rs:25:43
   |
25 | /// Read all data from a reader into a Vec<u8>
   |                                           ^^^^
   |
   = note: `#[warn(rustdoc::invalid_html_tags)]` on by default
help: try marking as source code
   |
25 | /// Read all data from a reader into a `Vec<u8>`
   |                                        +       +

Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-25 14:14:43 +02:00
Finn Callies
3635a19462 pvebc: Log services to journal+console
Change StandardOutput=console to StandardOutput=journal+console and
StandardError=console to StandardError=journal+console respectively for
the pvebc dracut module systemd services.

Fixes: https://github.com/ibm-s390-linux/s390-tools/issues/205
Fixes: 3d679f61f ("pvebc: Disable unit logging to /boot")
Reviewed-by: Holger Dengler <dengler@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
584e785f5c zkey: Adjust the error message when calculating the KVP fails
The 'paes' and 'phmac' kernel ciphers and the respective kernel modules
are no longer used for calculating the key verification pattern. Instead,
the 'pkey' kernel module and its sub modules is used now.

In case the 'pkey' kernel module is not available, an appropriate error
message is already printed by open_pkey_device() when opening the device.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
7fffdcfe8c zkey: Remove the use of AF_ALG for calculating key verification patterns
Instead of using AF_ALG to calculate key verification patterns, transform
the key blob into a protected key and calculate the key verification
patterns with CAPCF calls.

The 'zkey-cryptsetup convert' command also calculates key verification
patterns from clear keys. Support this, too.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
44d6020774 zkey: Pass /dev/pkey file descriptor through to all functions
To be able to calculate the key verification pattern of a key blob without
the use of AF_ALG, the file descriptor of the /dev/pkey device needs to
be passed through to all functions that might need to calculate the key
verification pattern.

Also make sure that all commands that might need to calculate the key
verification pattern have 'need_pkey_device = 1' so that the device is
opened.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
30ce11b8da zkey: Add helper function to transform a key blob into a protected key
To be able to calculate the key verification pattern of a key blob without
the use of AF_ALG, the key blob needs to be transformed into a protected
key via the PKEY_KBLOB2PROTK3 ioctl.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
c7ab70df2c zkey: Fix possible out of bounds access with malformed property files
When reading a properties file with a line containing a binary zero as first
character, an array access at index -1 is attempted, because strlen considers
this as an empty string (len = 0). The following array access 'line[len - 1]'
accesses the array at index -1.

Fix this by checking the line length and skip empty lines.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
c953486dec zkey-cryptsetup: Fix handling of 'hmac(sha512)' integrity algorithm
Fix a copy & paste error on handling the 'hmac(sha512)' integrity algorithm
with the 'convert' command.

Fixes: 97136585da ("zkey-cryptsetup: Support PHMAC integrity with convert command")
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:03 +02:00
Ingo Franzki
610cc581da zkey: Fix file size checking in read_secure_key()
Key files read by read_secure_key() may not be just a key blob, but it
might also contain an additional integrity key blob.

Correct the upper bounds checking and allow up to 3 key blobs to be read.
This allows for an AES-XTS key blob consisting of 2 key blobs plus an
integrity key blob concatenated to the encryption key blob.

Fixes: 771c794580 ("zkey-cryptsetup: Support PHMAC integrity with setkey command")
Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Ingo Franzki
604c34e66b libkmipclient: Fix 'assignment discards 'const' qualifier' warnings
Fix compile warnings like

   warning: assignment discards 'const' qualifier from pointer target type
                                                   [-Wdiscarded-qualifiers]

by declaring the variables as const.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Ingo Franzki
e2cd2e78c5 libekmfweb: Fix 'assignment discards 'const' qualifier' warnings
Fix compile warnings like

   warning: assignment discards 'const' qualifier from pointer target type
                                                   [-Wdiscarded-qualifiers]

by declaring the variables as const.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Ingo Franzki
d04d331673 zkey/kmip: Fix 'assignment discards 'const' qualifier' warnings
Fix compile warnings like

   warning: assignment discards 'const' qualifier from pointer target type
                                                   [-Wdiscarded-qualifiers]

by declaring the variables as const or cast appropriately.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Ingo Franzki
27f20495a7 zkey: Fix 'assignment discards 'const' qualifier' warnings
Fix compile warnings like

   warning: assignment discards 'const' qualifier from pointer target type
                                                   [-Wdiscarded-qualifiers]

by declaring the variables as const or cast appropriately.

Reviewed-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Stephen Gallagher
679ab77b96 Use EVP_PKEY_id() for OpenSSL 1.1 detection
The previous function, EVP_PKEY_meth_remove(), was deprecated in OpenSSL
3.0 and fully removed in OpenSSL 4.0. As a result, it fails detection of
OpenSSL 1.1+ if OpenSSL 4 is present.

Closes: https://github.com/ibm-s390-linux/s390-tools/pull/204
[seiden@linux.ibm.com: Add link to GH PR]
Signed-off-by: Stephen Gallagher <sgallagh@redhat.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Dan Horák
8bb20be528 ebc: implement --version option for pvics
For consistency reasons implement the --version option handling for the new
pvics tool.

Closes: https://github.com/ibm-s390-linux/s390-tools/pull/203
[seiden@linux.ibm.com: Add link to GH PR]
Signed-off-by: Dan Horák <dan@danny.cz>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Jan Höppner
d04ea8603d rust: Upgrade MSRV and dependencies
Bump MSRV to 1.85.0 and upgrade all dependencies using 'cargo upgrade'
from cargo-edit. The bump to 1.85.0 is requiered to get at least openssl
version 0.10.79 including fixes for a couple security issues.

Link: https://crates.io/crates/cargo-edit
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Steffen Eiden
51d2c92067 rust: Remove non_ascii_idents lint
This lint also covers dependencies that generate code i.e. derives.
Those dependencies might introduce non-ascii chars deliberately as e.g.
zerocopy 0.8.32+ does.

Remove it to avoid any confusing warnings regarding non-ascii chars.

Link: https://github.com/rust-lang/rust/issues/151025
[hoeppner@linux.ibm.com: Add link to reported issue]
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Marc Hartmayer
7e3b8b8c16 pvinfo: Remove comments after formatting the code
Remove them as they are pointless.

Suggested-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Marc Hartmayer
f8fb9ce32a rust: Run rustfmt with some experimental options
+ Sort and group the imports
+ Normalize and format comments (100 characters width)

Command used:

$ cargo +nightly fmt --

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Marc Hartmayer
3d5f75d6c3 rustfmt.toml: Enable additional formatting options
Enable more settings, including some that are currently unstable. Prefer
options that are also used by the Rust project itself [1].

[1] 64a965e901/rustfmt.toml (L5-L6)

Acked-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-06-22 16:43:02 +02:00
Jan Höppner
ddc8248b21 Prepare for next release
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-05-22 16:34:46 +02:00
298 changed files with 9367 additions and 4036 deletions

View File

@@ -1,13 +1,12 @@
edition = "2021"
newline_style = "Unix"
# Unstable options that help catching some mistakes in formatting and that we may want to enable
# when they become stable.
#
# They are kept here since they are useful to run from time to time.
#comment_width = 100
#format_code_in_doc_comments = true
#group_imports = "StdExternalCrate"
#normalize_comments = true
# Unstable options that help catching some mistakes in formatting
group_imports = "StdExternalCrate"
imports_granularity = "Module"
use_field_init_shorthand = true
comment_width = 100
format_code_in_doc_comments = true
normalize_comments = true
wrap_comments = true
#reorder_impl_items = true
#wrap_comments = true

View File

@@ -77,7 +77,6 @@ List of all individuals having contributed content to s390-tools
- Jean-Baptiste Joret
- Jens Remus
- Jochen Roehrig
- Joern Siglen
- Juergen Christ
- Julian Wiedmann
- Jörn Siglen
@@ -135,6 +134,7 @@ List of all individuals having contributed content to s390-tools
- Steffen Eiden
- Steffen Maier
- Steffen Thoss
- Stephen Gallagher
- Sumanth Korikkar
- Susanne Wintenberger
- Sven Schnelle
@@ -148,6 +148,7 @@ List of all individuals having contributed content to s390-tools
- Thomas Spatzier
- Thomas Weber
- Thorsten Winkler
- Timo Keller
- Tobias Huschle
- Tuan Hoang
- Ursula Braun

View File

@@ -1,6 +1,56 @@
Release history for s390-tools (MIT version)
--------------------------------------------
* __v2.43.1 (2026-07-22)__
For Linux kernel version: 7.1
Changes of existing tools:
- dasdfmt: Check disk type before prompting for blocksize
- iucvterm/ts-shell: Introduce config parameter to specify iucvconn binary
- iucvterm/ts-shell: Introduce pager config to replace env variable
- pv: Add root CA organization pinning to certificate verification
- zdump/ngdump: Use OpenSSL's SHA256 to compute digest of dump image
Bug Fixes:
- 95sel-ebc: Add udev-settle dependency
- 95sel-ebc: Add umount to boot service
- 95sel-ebc: Harden boot mount service
- fdasd: Fix memory leak in yes_no() function
- iucvterm/iucvtty: Ensure PTY and server fd's are closed at exec
- iucvterm/iucvtty: Prevent connection stalls when receiving TERM env
- iucvterm/iucvtty: Validate TERM environment name
- libkmipclient: Protect from symlink-following attacks
- libkmipclient: Various bugfixes
- libseckey: Protect from symlink-following attacks
- libseckey: Various bugfixes
- mon_procd: Fix possible static buffer overflow
- osasnmpd: Fix SNMP non-compliance
- zdev: Harden against invalid udev, import, firmware, or hypervisor data
- zipl-editenv: Check in-bootmap environment block validity
- zipl/boot: Check in-bootmap environment block syntax
- zipl: Check keyword duplications in BLS entries
- zipl: Fix bugs in mirror support
- zkey: Protect from symlink-following attacks
* __v2.43.0 (2026-06-25)__
For Linux kernel version: 7.1
s390-tools: Set Rust MSRV to 1.85.0
Changes of existing tools:
- dbginfo.sh: Add IBM appliance specific files
- lshwc: Show explicitly selected unnamed counters with --hide
- pvattest: Add firmware check version 2
- zipl: Introduce verbosity levels of zipl session (--debug)
- zkey: Remove the use of AF_ALG for calculating key verification patterns
Bug Fixes:
- ebc: implement --version option for pvics
- pvebc: Log services to journal+console
- pvics: Fix virt-resize permission error
* __v2.42.1 (2026-05-22)__
For Linux kernel version: 7.0

View File

@@ -713,7 +713,7 @@ static int readdir_entry(struct fst_entry *fst, off_t addr)
*/
static inline int strip_right(const char *str, int size)
{
while (str[size - 1] == 0x20)
while (size > 0 && str[size - 1] == 0x20)
size--;
return size;
}

View File

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

View File

@@ -406,6 +406,38 @@ static void output_times(struct time_formats date)
}
}
struct ctr_filter_match {
bool selected;
bool selected_by_id;
};
static struct ctr_filter_match ctr_match(const char *name, const char *list,
size_t id)
{
struct ctr_filter_match match = { .selected = !list };
char *copy, *token;
char id_str[32];
if (!list)
return match;
snprintf(id_str, sizeof(id_str), "%zu", id);
copy = util_strdup(list);
for (token = strtok(copy, ","); token; token = strtok(NULL, ",")) {
if (name && strcmp(token, name) == 0) {
match.selected = true;
break;
}
if (strcmp(token, id_str) == 0) {
match.selected = true;
match.selected_by_id = true;
break;
}
}
free(copy);
return match;
}
static void prepare_counter(size_t id, unsigned long value)
{
if (output_format == FMT_CSV) {
@@ -438,9 +470,14 @@ static void output_per_cpu(struct time_formats date)
util_fmt_obj_start(FMT_LIST, "counters");
}
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
struct ctr_filter_match match;
if (!ctrname[i].hitcnt)
continue;
if (hideundef && !ctrname[i].name)
match = ctr_match(ctrname[i].name, ctrlist, i);
if (!match.selected)
continue;
if (hideundef && !ctrname[i].name && !match.selected_by_id)
continue;
prepare_counter(i, ctrname[i].ccv[h]);
}
@@ -462,9 +499,14 @@ static void output_total(struct time_formats date)
util_fmt_obj_start(FMT_LIST, "counters");
}
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
struct ctr_filter_match match;
if (!ctrname[i].hitcnt)
continue;
if (hideundef && !ctrname[i].name)
match = ctr_match(ctrname[i].name, ctrlist, i);
if (!match.selected)
continue;
if (hideundef && !ctrname[i].name && !match.selected_by_id)
continue;
prepare_counter(i, ctrname[i].total);
ctrname[i].total = 0;

View File

@@ -1566,6 +1566,8 @@ int main(int argc, char *argv[])
g.ese = dasd_sys_ese(g.dev_node);
eval_format_mode();
check_disk();
/* Either let the user specify the blksize or get it from the kernel */
if (!g.blksize_specified) {
if (!(mode == FULL ||
@@ -1587,8 +1589,6 @@ int main(int argc, char *argv[])
error("VOLSER not found on device %s", g.dev_path);
}
check_disk();
if (check_param(str, ERR_LENGTH, &format_params) < 0)
error("%s", str);

View File

@@ -495,6 +495,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
fprintf(stderr,
"Error: Device name was truncated\n");
free(path);
free(readbuf);
return -1;
}
@@ -504,6 +505,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
closedir(directory);
free(path);
free(readbuf);
return 0;
}

View File

@@ -387,20 +387,28 @@ static int yes_no(char *question_str)
ssize_t bytes_read;
char *answer;
size_t size;
int rc;
size = 0;
answer = NULL;
while (1) {
printf("%s (y/n): ", question_str);
bytes_read = getline(&answer, &size, stdin);
if (bytes_read < 0)
return -1;
if (answer[0] == 'y')
return 0;
if (answer[0] == 'n')
return 1;
if (bytes_read < 0) {
rc = -1;
break;
}
if (answer[0] == 'y') {
rc = 0;
break;
}
if (answer[0] == 'n') {
rc = 1;
break;
}
}
free(answer);
return rc;
}
static char *fdasd_partition_type(char *dsname)

View File

@@ -514,7 +514,7 @@ static int hmcdrv_path_copy(struct hmcdrv_fuse_file *fp, char *dest)
char *src = HMCDRV_FUSE_PATH(fp);
int len = 0;
while ((len < (HMCDRV_FUSE_MAXPATH - 1)) &&
while ((len < (HMCDRV_FUSE_MAXPATH - 2)) &&
(*src != '\0')) {
*dest = *src;

View File

@@ -102,6 +102,9 @@
_x > _y ? _x : _y; \
})
#ifdef __SIZEOF_INT128__
typedef unsigned __int128 u128 __aligned(16);
#endif
typedef unsigned long long u64;
typedef signed long long s64;
typedef unsigned int u32;

View File

@@ -14,14 +14,19 @@ use strict;
use warnings;
use File::Basename;
use Getopt::Long qw(:config no_ignore_case);
use Scalar::Util qw(tainted);
use Term::ReadLine;
use POSIX;
$ENV{'PERL5LIB'} = "";
$ENV{'PERL5OPT'} = "";
$ENV{'PERLLIB'} = "";
$ENV{'PERL_USE_UNSAFE_INC'} = "";
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
$ENV{'PERL_RL'} = " o=0"; # use best avail. readline
$ENV{'PATH'} = "/bin:/sbin:/usr/bin:/usr/sbin";
$ENV{'LESSSECURE'} = 1; # let less run in "secure" mode
$ENV{'PAGER'} = $ENV{'PAGER'} || "/usr/bin/less";
my $ts_shell = fileparse($0, qr/\.[^.]+/);
$SIG{__WARN__} = sub { print STDERR "$ts_shell: $_[0]"; };
$SIG{__DIE__} = sub { print STDERR "$ts_shell: $_[0]"; exit 255; };
@@ -36,6 +41,7 @@ my %config = (
'auditdir' => "@var_path@/log/ts-shell",
'iucvconn' => "@iucvconn_path@/iucvconn",
'prompt' => getpwuid($>) . '@'."$ts_shell> ",
'pager' => "/usr/bin/less",
# runtime options
'rl' => undef, # terminal readline (rl)
'user' => getpwuid($>), # user name
@@ -65,6 +71,7 @@ sub cmd_list(\%);
sub rl_cmd_completion($$$);
sub list_regex_match($);
sub updateConfiguration(\%$);
sub checkConfiguration(\%);
sub loadAuthorization(\%);
sub readFile($$);
sub log_debug($);
@@ -103,6 +110,7 @@ sub main()
"$config{conffile} failed: $!";
exit 3;
}
checkConfiguration(%config);
# load list of systems the ts-shell is allowed to use; and
# hash the systems names in uppercase
@@ -185,11 +193,50 @@ sub updateConfiguration(\%$)
{
my ($cfg, $line) = @_;
my ($option, $value) = split /\s*=\s*/, $line;
$cfg->{sysfile} = $value if $option =~ /^ts-systems$/;
$cfg->{authfile} = $value if $option =~ /^ts-authorization$/;
$cfg->{auditdir} = $value if $option =~ /^transcript-directory$/;
$cfg->{auditfile} = $value if $option =~ /^transcript-systems$/;
$cfg->{pager} = $value if $option =~ /^pager$/;
$cfg->{iucvconn} = $value if $option =~ /^iucvconn$/;
}
# checkConfiguration() - Validate and untaint configuration values
#
# Called once after readFile() has finished populating %cfg from
# ts-shell.conf. Validates each security-sensitive path and untaints
# it via a regex capture so Perl taint mode permits later filesystem use.
#
# %cfg: Hash reference to terminal server configuration
#
sub checkConfiguration(\%)
{
my $cfg = shift;
# Validate pager to untaint for later use
unless ($cfg->{pager} =~ m#^(/[\w./-]+)$#) {
log_error "Invalid pager configuration";
exit 6;
}
$cfg->{pager} = $1;
# Validate iucvconn path to untaint for later use
unless ($cfg->{iucvconn} =~ m#^(/[\w./-]+)$#) {
log_error "Invalid iucvconn configuration";
exit 7;
}
$cfg->{iucvconn} = $1;
# Validate transcript-directory to untaint for later use
unless ($cfg->{auditdir} =~ m#^(/[\w./-]+)$#) {
log_error "Invalid transcript-directory configuration";
exit 8;
}
$cfg->{auditdir} = $1;
# Untaint user name (tainted by getpwuid) for use in filesystem paths
($cfg->{user}) = ($cfg->{user} =~ /^([\w.-]+)$/);
}
# loadAuthorization() - Load system authorizations from file
@@ -205,7 +252,7 @@ sub loadAuthorization(\%)
{
my $cfg = shift();
return 0 unless open(AUTH, "<$cfg->{authfile}");
return 0 unless open(AUTH, "<", $cfg->{authfile});
AUTH_ENT: while (<AUTH>) {
chomp;
@@ -217,13 +264,13 @@ sub loadAuthorization(\%)
my ($key, $val) = split /\s*=\s*/;
# read authorization configuration for user and its groups
if ($key =~ /^$cfg->{user}$/) {
if ($key =~ /^\Q$cfg->{user}\E$/) {
$authorized = 1;
log_debug "Found user: $key";
} elsif ($key =~ /^@(\S+)$/) {
my $group = $1;
$authorized = 1 if grep {/^${group}$/} @{$cfg->{groups}};
$authorized = 1 if grep {/^\Q${group}\E$/} @{$cfg->{groups}};
log_debug "Found group: $key" if $authorized;
}
@@ -303,7 +350,7 @@ sub readFile($$)
{
my ($file, $sub) = @_;
return 0 unless open(CONF, "<$file");
return 0 unless open(CONF, "<", "$file");
while (<CONF>) {
chomp;
next if /^#/; # ignore comments
@@ -392,7 +439,15 @@ sub get_auditlog_file($$$)
}
}
$$filepath = "$cfg->{auditdir}/$cfg->{user}/" . uc $guest . "_";
# validate and sanitize guest directory
$guest = uc $guest;
unless ($guest =~ /^([A-Z0-9@#\$_-]{1,8})$/) {
log_error "Creating session file path failed for guest: $guest";
return 0; # return error
}
# construct the file path
$$filepath = "$cfg->{auditdir}/$cfg->{user}/" . $guest . "_";
$$filepath .= strftime '%F-%H%M%S', localtime;
log_debug "Session transcript file: $$filepath";
@@ -416,7 +471,7 @@ sub cmd_connect($\%)
}
my ($guest, $srv) = split /\s+/, $params;
$service = $srv if $srv && $srv =~ /\w{1,8}/;
$service = $srv if $srv && $srv =~ /^\w{1,8}$/;
# check authorization:
# The auth_func contract is to return a code, that must be one of:
@@ -482,7 +537,7 @@ sub cmd_service($\%)
return;
}
if ($new =~ /\w{1,8}/) {
if ($new =~ /^\w{1,8}$/) {
$cfg->{service} = $new;
# push new service for cmd completion
unless (grep /$new/, @{$cfg->{services}}) {
@@ -545,7 +600,7 @@ sub pager($)
{
my $eval = shift();
unless (open(PAGER, "|$ENV{'PAGER'}")) {
unless (open(PAGER, '|-', "$config{'pager'}")) {
eval &$eval;
return;
}

View File

@@ -1,9 +1,8 @@
.\" SPDX-License-Identifier: MIT
.\"
.\" ts-shell.1
.\"
.\"
.\" Copyright IBM Corp. 2008, 2017
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\" Copyright IBM Corp.
.\" ----------------------------------------------------------------------
.TH "ts-shell" "1" "March 2009" "s390-tools" "Terminal Server over IUCV"
.
@@ -269,6 +268,10 @@ session transcripts are saved.
See section "Configure terminal session transcripts" for details.
.
.TP
.BR pager " = " \fI/usr/bin/less\fP
Specifies the path to a pager program to paginate output of ts-shell commands.
.
.RE
.
.
@@ -542,14 +545,6 @@ Directory for saving terminal session transcripts.
.
.SH "ENVIRONMENT"
.TP
.B PAGER
The \fBPAGER\fP environment variable designates a program used as pager for the
\fBlist\fP command of the terminal server shell.
If \fBPAGER\fP is not set or empty,
.BR less (1)
is used.
.
.TP
.B LESSSECURE
\fBts-shell\fP sets this variable to run
.BR less (1)

View File

@@ -2,6 +2,12 @@
#
# See ts-shell(1) manual page for file format syntax.
# Generic settings
# ----------------
# pager
# binary to paginate output (default: less)
#pager = /usr/bin/less
# System and authorization settings
# ---------------------------------

View File

@@ -21,6 +21,9 @@
/* Message buffer: message header + 4096 bytes of data */
#define MSG_BUFFER_SIZE (MSG_DATA_OFFSET + (4096))
/* Message data sizes */
#define MAX_TERM_SIZE 256
/* Error macros */
#define print_error(s) program_error(PRG_COMPONENT, (s))
#define iucvtty_error(m) \
@@ -55,13 +58,14 @@ extern int iucvtty_read_data(int, struct iucvtty_msg *, size_t);
extern int iucvtty_read_msg(int, struct iucvtty_msg *, size_t, size_t *);
extern int iucvtty_write_msg(int, struct iucvtty_msg *);
extern void iucvtty_skip_msg_residual(int, size_t *);
extern void iucvtty_skip_msg_chunk(int, size_t *);
extern ssize_t __write(int, const void*, size_t);
extern int strmatch(const char *, const char *);
extern int is_regex_valid(const char *);
extern int is_client_allowed(const char *, const struct iucvterm_cfg *);
extern int is_term_valid(const char *term, size_t len);
extern void userid_cpy(char [9], const char [8]);
extern void iucv_msg_error(const char *, uint32_t);

View File

@@ -86,7 +86,7 @@ static inline struct iucvtty_msg *msg_alloc(uint8_t type, uint16_t size)
{
struct iucvtty_msg *m;
m = malloc(size + MSG_DATA_OFFSET);
m = calloc(1, size + MSG_DATA_OFFSET);
if (m != NULL) {
m->version = MSG_VERSION;
m->type = type;

View File

@@ -8,6 +8,8 @@
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <regex.h>
#include <stdio.h>
@@ -36,7 +38,7 @@
* @len: Buffer length
*
* Write @len number of bytes from the buffer @buf to the file
* descriptor @fd. The routines handles EINTR and partially writes.
* descriptor @fd. The routine handles EINTR and partial writes.
* Returns the error code from the underlying write(2) syscall.
*/
ssize_t __write(int fd, const void *buf, size_t len)
@@ -55,6 +57,33 @@ ssize_t __write(int fd, const void *buf, size_t len)
return written;
}
/**
* __read() - Read data
* @fd: File descriptor
* @buf: Pointer to data buffer
* @len: Buffer size
*
* Read up to @len number of bytes from file descriptor @fd and stores them
* in the buffer to which @buf points.
* The routine handles EINTR and partial reads and returns the error code
* from the underlying read(2) syscall.
*/
ssize_t __read(int fd, void *buf, size_t len)
{
ssize_t rc;
size_t count = 0;
while (count < len) {
rc = read(fd, buf + count, len - count);
if (rc == -1 && errno == EINTR)
continue;
if (rc <= 0)
return rc;
count += rc;
}
return count;
}
#ifdef __DEBUG__
static void __dump_msg(int fd, const struct iucvtty_msg *m, char dir)
{
@@ -116,14 +145,16 @@ int iucvtty_tx_termenv(int dest, char *dflt)
len = 0;
if (term != NULL)
len = 1 + strlen(term);
len = MIN(1 + strlen(term), (size_t)MAX_TERM_SIZE);
/* Note: The server console tool waits for terminal environment
* information: the message is sent even if it is empty */
msg = msg_alloc(MSG_TYPE_TERMENV, len);
if (msg == NULL)
return -1;
msg_cpy_from(msg, term, len);
msg->datalen = len;
if (msg->datalen)
snprintf((char *)msg->data, msg->datalen, "%s", term);
rc = iucvtty_write_msg(dest, msg);
msg_free(msg);
@@ -146,12 +177,14 @@ int iucvtty_rx_termenv(int fd, void *buf, size_t len)
return -1;
skip = 0;
rc = iucvtty_read_msg(fd, msg, msg_size(msg), &skip);
iucvtty_skip_msg_residual(fd, &skip);
iucvtty_skip_msg_chunk(fd, &skip);
if (!rc) {
if (msg->datalen == 0)
if (msg->datalen == 0) {
memset(buf, 0, MIN(1u, len));
else
} else {
msg_cpy_to(msg, buf, len);
memset(buf + MIN(msg->datalen, len - 1), 0, 1);
}
}
msg_free(msg);
return rc;
@@ -262,93 +295,175 @@ int iucvtty_copy_data(int dest, struct iucvtty_msg *msg)
}
/**
* iucvtty_skip_msg_residual() - Skip (receive & forget) count number of bytes
* iucvtty_skip_msg_chunk() - Skip (receive & forget) count number of bytes
* @fd: File descriptor
* @residual: Residual of an iucv tty message received by iucvtty_read_msg()
* @chunk: Remaining chunk from a previous iucvtty_read_msg() call
*
* See iucvtty_read_msg() for an explanation when to use this routine.
* Note: The @residual parameter shall not be NULL.
* Note: The @chunk parameter must not be NULL.
*/
void iucvtty_skip_msg_residual(int fd, size_t *residual)
void iucvtty_skip_msg_chunk(int fd, size_t *chunk)
{
char b;
size_t i;
int r;
char buf[256];
if (*residual <= 0)
if (*chunk <= 0)
return;
for (i = 0; i < *residual; i++)
if (read(fd, &b, 1) <= 0)
while (*chunk) {
r = __read(fd, buf, MIN(sizeof(buf), *chunk));
if (r <= 0)
break;
*residual = 0;
*chunk -= r;
}
*chunk = 0;
}
/**
* iucvtty_read_msg_chunk() - Read IUCV message chunk
* @fd: File descriptor to read from
* @msg: Pointer to IUCV message buffer
* @msglen: IUCV message buffer size (including message header)
* @chunk: Size of chunk data to read
*
* Stores new message data and calculates next chunk size if not all data
* could be read. Returns zero on success, non-zero otherwise.
*/
static int iucvtty_read_msg_chunk(int fd, struct iucvtty_msg *msg,
size_t msglen, size_t *chunk)
{
ssize_t r;
size_t datalen;
/* Calculate message data length to read */
datalen = MIN(msglen - MSG_DATA_OFFSET, *chunk);
for (;;) {
r = read(fd, msg->data, datalen);
if (r == -1 && errno == EINTR)
continue;
if (r <= 0)
return -1;
break;
}
/* Update message and re-calculate next chunk */
msg->datalen = r;
*chunk -= r;
return 0;
}
/**
* validate_msg() - Perform sanity checks on a received message
* @msg: IUCV message buffer
*
* Returns zero if the message is valid; otherwise non-zero
*/
static int validate_msg(struct iucvtty_msg *msg)
{
switch (msg->type) {
case MSG_TYPE_DATA:
/* The datalen ranges from 0 to its maximum of 0xffff
* which is the maximum of the type definition of uint16_t.
*
* Consider the datalen value as valid.
*/
break;
case MSG_TYPE_ERROR:
if (msg->datalen != sizeof(uint32_t))
return 1;
break;
case MSG_TYPE_TERMENV:
if (msg->datalen > MAX_TERM_SIZE)
return 1;
break;
case MSG_TYPE_TERMIOS: /* ignored */
break;
case MSG_TYPE_WINSIZE:
if (msg->datalen != sizeof(struct winsize))
return 1;
break;
default:
/* Invalid message type */
return 1;
}
return 0;
}
/**
* iucvtty_read_msg() - Read/Receive an IUCV message
* @fd: File descriptor to read from
* @msg: Pointer to IUCV message buffer
* @len: IUCV message data len
* @residual: Status to be used by next call
* @msglen: IUCV message buffer size (MUST BE > MSG_DATA_OFFSET)
* @chunk: Size of remaining data; must be passed on next call
*
* The function reads up to @len bytes from file descriptor @fd.
* If the received message is larger than @len bytes, the @residual value
* is set to the number of bytes remaining.
* The function shall then be re-called to create a new message and receive
* the next chunk of size @residual; or the remaining characters must be
* skipped using the iucvtty_skip_msg() routine.
* Note: The @len parameter shall be greater than MSG_DATA_OFFSET.
* The @residual parameter shall not be NULL.
* The function reads up to @len bytes from file descriptor @fd. If the
* received message is larger than @len bytes, the @chunk value is set to
* the number of remaining bytes. The function shall then be re-called to
* create a new message and receive the outstanding data. Alternatively,
* call iucvtty_skip_msg_chunk() to discard remaining data.
*
* NOTE: The @len parameter must be greater than MSG_DATA_OFFSET!
* The @chunk parameter must not be NULL.
*/
int iucvtty_read_msg(int fd, struct iucvtty_msg *msg,
size_t len, size_t *residual)
size_t msglen, size_t *chunk)
{
int rc;
ssize_t r; /* number of bytes read from fd */
if (*residual)
len = MIN(len - MSG_DATA_OFFSET, *residual);
/* Ensure message buffer size can hold data and can be processed */
assert(msglen > MSG_DATA_OFFSET);
assert(chunk != NULL);
while (1) {
if (*residual) {
r = read(fd, msg->data, len);
if (r > 0)
msg->datalen = r;
} else
r = read(fd, msg, len);
/* Read pending message data */
if (*chunk)
return iucvtty_read_msg_chunk(fd, msg, msglen, chunk);
if (r == -1 && errno == EINTR)
continue;
if (r <= 0) {
rc = -1;
goto out_read_error;
}
break; /* exit loop for a successful read */
/* Read message header of new message */
r = __read(fd, msg, MSG_DATA_OFFSET);
if (r <= 0) {
rc = -1;
goto out_read_error;
}
#ifdef __DEBUG__
if (!*residual)
__dump_msg(fd, msg, 'R');
__dump_msg(fd, msg, 'R');
#endif
/* (re)calculate next chunk */
if (*residual)
*residual -= msg->datalen;
else
if (msg->datalen > (r - MSG_DATA_OFFSET)) {
/* calculate pending msg data and update datalen */
*residual = msg->datalen - (r - MSG_DATA_OFFSET);
msg->datalen = r - MSG_DATA_OFFSET;
}
/* check for a sane message */
/* Check message header */
if (msg->version != MSG_VERSION) {
fprintf(stderr, _("%s: %s\n"),
PRG_COMPONENT, _("The version of the received data "
"message is not supported\n"));
"message is not supported"));
fprintf(stderr, "MSG: msg->version=%u msg->type=%u msg->datalen=%u\n",
msg->version, msg->type, msg->datalen);
rc = -2;
goto out_read_error;
}
rc = 0;
/* Check for an empty message */
if (!msg->datalen)
return 0;
/* Check message type and data length */
if (validate_msg(msg)) {
fprintf(stderr, _("%s: %s\n"),
PRG_COMPONENT, _("The received message is invalid"));
fprintf(stderr, "MSG: msg->version=%u msg->type=%u msg->datalen=%u\n",
msg->version, msg->type, msg->datalen);
rc = -3;
goto out_read_error;
}
/* Process the new message as a one chunk */
*chunk = msg->datalen;
msg->datalen = 0;
rc = iucvtty_read_msg_chunk(fd, msg, msglen, chunk);
out_read_error:
return rc;
}
@@ -501,6 +616,31 @@ int is_client_allowed(const char *client, const struct iucvterm_cfg *cfg)
return strmatch(client, cfg->client_re);
}
/**
* is_term_valid() - Validate TERM environment value
* @term: Terminal environment name to validate
* @len: Maximum number of characters to validate
*/
int is_term_valid(const char *term, size_t len)
{
const char *c;
if (term == NULL || *term == '\0')
return 0;
for (c = term; *c != '\0'; c++) {
if ((size_t)(c - term) >= len)
return 0;
if (!isalnum((unsigned char)*c) &&
*c != '.' &&
*c != '_' &&
*c != '-')
return 0;
}
return 1;
}
/**
* userid_cpy() - Copy z/VM user ID and skip trailing spaces.
* @dest: Destination buffer

View File

@@ -17,6 +17,7 @@
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <syslog.h>
@@ -32,7 +33,6 @@
#define SYSLOG_IDENT "iucvtty"
#define PRG_COMPONENT SYSLOG_IDENT
#define TERM_BUFSIZE 256
#define TERM_DEFAULT "linux"
@@ -63,12 +63,46 @@ static int exec_login_prog(char *cmd[])
return rc;
}
/**
* receive_term_env() - receive terminal environment with timeout
* @term: Terminal environment variable
* @len: Maximum characters for the terminal environment variable
*/
static void receive_term_env(int client, char *term, size_t len)
{
struct timeval tv = { .tv_sec = 0, .tv_usec = 0};
/* Set a temporary timeout of 10s to time out a stalling
* client connection. Use SO_RCVTIMEO to set the timeout.
* Using those hard coded values directly is safe as
* SO_RCVTIMEO is not being used in other parts of iucvtty.
*/
tv.tv_sec = 10;
setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
memset(term, 0, len);
if (iucvtty_rx_termenv(client, term, len))
snprintf(term, len, "%s", TERM_DEFAULT);
tv.tv_sec = 0;
setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if (!is_term_valid(term, len)) {
print_error("Ignoring received TERM env due to invalid character(s)");
snprintf(term, len, "%s", TERM_DEFAULT);
}
}
/**
* iucvtty_worker() - Handle an incoming client connection
* @client: Client file descriptor
* @master: PTY master file descriptor
* @slave: PTY slave file descriptor
* @cfg: IUCV TTY configuration structure.
*
* Unlike the HVC IUCV terminal device driver, iucvtty expects to
* receive the TERM environment first. This is necessary to correctly
* set up the login program.
*/
static int iucvtty_worker(int client, int master, int slave,
const struct iucvterm_cfg *cfg)
@@ -78,15 +112,14 @@ static int iucvtty_worker(int client, int master, int slave,
pid_t child;
fd_set set;
size_t chunk;
char term_env[TERM_BUFSIZE];
char term_env[MAX_TERM_SIZE];
/* flush pending terminal data */
tcflush(master, TCIOFLUSH);
/* read terminal parameters from client */
if (iucvtty_rx_termenv(client, term_env, TERM_BUFSIZE))
sprintf(term_env, TERM_DEFAULT);
/* read and validate terminal parameters from client */
receive_term_env(client, term_env, sizeof(term_env));
/* start login program */
child = fork();
@@ -113,6 +146,9 @@ static int iucvtty_worker(int client, int master, int slave,
exit(3); /* we only reach here if exec has failed */
}
/* Close slave fd to properly handle SIGHUP for the child process */
close(slave);
/* setup buffers */
msg = malloc(MSG_BUFFER_SIZE);
if (msg == NULL) {
@@ -226,8 +262,13 @@ int main(int argc, char *argv[])
}
/* set close-on-exec for file descriptors */
fcntl(master, F_SETFD, FD_CLOEXEC);
fcntl(server, F_SETFD, FD_CLOEXEC);
if (fcntl(master, F_SETFD, FD_CLOEXEC) ||
fcntl(server, F_SETFD, FD_CLOEXEC)) {
print_error("Setting file controls failed");
close(server);
rc = 1;
goto exit_on_error;
}
/* syslog */
openlog(SYSLOG_IDENT, LOG_PID, LOG_AUTHPRIV);
@@ -240,6 +281,7 @@ int main(int argc, char *argv[])
client = accept(server, (struct sockaddr *) &caddr, &len);
if (client == -1) {
print_error("An incoming connection could not be accepted");
close(server);
rc = 2;
goto exit_on_error;
}
@@ -256,10 +298,14 @@ int main(int argc, char *argv[])
} else { /* client is allowed to connect */
syslog(LOG_INFO, "Accepted client connection from %s",
client_host);
/* set close-on-exec for client socket */
fcntl(client, F_SETFD, FD_CLOEXEC);
/* close server socket */
close(server);
/* set close-on-exec for client socket */
if (fcntl(client, F_SETFD, FD_CLOEXEC)) {
print_error("Setting file controls failed");
rc = 4;
goto exit_on_error;
}
/* setup signal handler to notify shutdown signal */
sigemptyset(&sigact.sa_mask);

View File

@@ -13,7 +13,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termio.h>
#include <unistd.h>
#include "iucvterm/functions.h"
@@ -114,7 +113,7 @@ static int __testReadWriteMessage_nochunks(int sv[2])
if (iucvtty_read_msg(sv[1], msg[1], 6 + MSG_DATA_OFFSET, &residual))
return 2;
assert(0 == memcmp(msg[1]->data, data, msg[1]->datalen));
iucvtty_skip_msg_residual(sv[1], &residual);
iucvtty_skip_msg_chunk(sv[1], &residual);
assert(0 == residual);
/*printf("datalen=%u data='%s'\n",
msg[1]->datalen, msg[1]->data);*/
@@ -144,7 +143,7 @@ static int __testReadWriteMessage(int sv[2])
/* read msg */
if (iucvtty_read_msg(sv[1], msg[1], BUF_SIZE, &chunk))
return 2;
iucvtty_skip_msg_residual(sv[1], &chunk);
iucvtty_skip_msg_chunk(sv[1], &chunk);
/* compare msg */
if (__msgcmp(msg[0], msg[1]))

View File

@@ -43,7 +43,7 @@ detect-openssl-version.dep:
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
echo "#endif" >> $(TMPFILE)
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
echo " EVP_PKEY_id(NULL);" >> $(TMPFILE)
echo "}" >> $(TMPFILE)
mv $(TMPFILE) $@

View File

@@ -233,7 +233,7 @@ static int _ekmf_extract_pubkey(const char *cert, const char *pub_key_pem,
goto out;
}
fp = fopen(pub_key_pem, "w");
fp = fopen_nofollow(pub_key_pem, "w");
if (fp == NULL) {
rc = -errno;
pr_verbose(verbose, "File '%s': %s", pub_key_pem,
@@ -458,7 +458,7 @@ retry:
out);
if (server_cert_pem != NULL) {
fp = fopen(server_cert_pem, "w");
fp = fopen_nofollow(server_cert_pem, "w");
if (fp == NULL) {
rc = -errno;
pr_verbose(verbose, "File '%s': %s", server_cert_pem,
@@ -495,7 +495,7 @@ retry:
*/
if (ci->num_of_certs > 1 && ca_bundle_pem != NULL &&
do_verify == 0) {
fp = fopen(ca_bundle_pem, "w");
fp = fopen_nofollow(ca_bundle_pem, "w");
if (fp == NULL) {
rc = -errno;
pr_verbose(verbose, "File '%s': %s",
@@ -1415,7 +1415,7 @@ int ekmf_login(const struct ekmf_config *config, CURL **curl_handle,
while (*tok == ' ')
tok++;
fp = fopen(config->login_token, "w");
fp = fopen_nofollow(config->login_token, "w");
if (fp == NULL) {
rc = -errno;
pr_verbose(verbose, "Failed to open file %s: '%s'",
@@ -1739,7 +1739,7 @@ int ekmf_get_settings(const struct ekmf_config *config, CURL **curl_handle,
if (xts_key2_template != NULL) {
*xts_key2_template = _ekmf_find_setting(response_obj,
SETTING_ID_XTS_KEY2_TEMPLATE, verbose);
if (*identity_template == NULL) {
if (*xts_key2_template == NULL) {
if (error_msg != NULL) {
if (asprintf(error_msg, "The EKMF Web setting "
"'XTS Key Template Name (Key 2)' "
@@ -1882,8 +1882,9 @@ int ekmf_check_feature(const struct ekmf_config *config, CURL **curl_handle,
pr_verbose(verbose, "Feature '%s' is not installed",
FEATURE_ID_PERVASIVE_ENCRYPTION);
rc = -ENOTSUP;
if (asprintf(error_msg, "EKMF Web feature "
"'Pervasive Encryption' is not installed.")) {
if (error_msg != NULL &&
asprintf(error_msg, "EKMF Web feature "
"'Pervasive Encryption' is not installed.") < 0) {
pr_verbose(verbose, "asprintf failed");
rc = -ENOMEM;
}
@@ -1925,7 +1926,7 @@ static int _ekmf_build_party_info(const char *key_uuid, const char *timestamp,
if (*party_info_length < (size_t)EVP_MD_size(md)) {
pr_verbose(verbose, "Party info buffer is too small");
return -ERANGE;
rc = -ERANGE;
goto out;
}
@@ -2219,6 +2220,11 @@ static int _ekmf_import_key(unsigned char *req_sess_key,
unsigned char *party_info = NULL;
int rc;
if (resp_party_info_length > SIZE_MAX - req_party_info_length) {
rc = -EINVAL;
goto out;
}
party_info_length = req_party_info_length + resp_party_info_length;
party_info = malloc(party_info_length);
if (party_info == NULL) {
@@ -4547,8 +4553,9 @@ out:
*/
static char *_ekmf_base64_encode(const unsigned char *data, size_t data_size)
{
int outlen, len;
size_t outlen;
char *out;
int len;
outlen = (data_size / 3) * 4;
if (data_size % 3 > 0)
@@ -4559,7 +4566,7 @@ static char *_ekmf_base64_encode(const unsigned char *data, size_t data_size)
return NULL;
len = EVP_EncodeBlock((unsigned char *)out, data, data_size);
if (len != outlen) {
if (len < 0 || (size_t)len != outlen) {
free(out);
return NULL;
}
@@ -4596,7 +4603,7 @@ static int _ekmf_build_key_material(const unsigned char *certificate,
"JSON object", verbose, out);
payload = _ekmf_base64_encode(certificate, certificate_size);
JSON_CHECK_ERROR(*keymat_obj == NULL, rc, -EIO,
JSON_CHECK_ERROR(payload == NULL, rc, -EIO,
"Failed to base64 encode the certificate",
verbose, out);

View File

@@ -9,9 +9,11 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
@@ -104,7 +106,7 @@ int decode_base64url(unsigned char *output, size_t *outlen,
len = EVP_DecodeBlock((unsigned char *)padded_output,
(unsigned char *)padded_input, padded_inlen);
if (len != (int)padded_inlen * 3 / 4) {
if ((size_t)len != padded_inlen * 3 / 4) {
rc = -EIO;
goto out;
}
@@ -229,8 +231,9 @@ int parse_json_web_token(const char *token, json_object **header_obj,
size_t *signature_len)
{
json_object *hdr = NULL, *pld = NULL, *b64_obj = NULL;
char *ch, *header, *payload, *json = NULL;
size_t header_len, payload_len, json_len;
const char *ch, *header, *payload;
char *json = NULL;
bool b64 = true;
int rc = 0;
@@ -254,7 +257,7 @@ int parse_json_web_token(const char *token, json_object **header_obj,
* and the following parts (if any) are used for JWS Signature, or JWE
* Encryption (not considered here).
*/
header = (char *)token;
header = token;
ch = strchr(token, '.');
if (ch == NULL) {
@@ -363,9 +366,9 @@ out:
*payload_obj = pld;
else
json_object_put(pld);
if (signature != NULL && rc != 0) {
free(signature);
signature = NULL;
if (signature != NULL && *signature != NULL && rc != 0) {
free(*signature);
*signature = NULL;
*signature_len = 0;
}
if (json != NULL)
@@ -653,7 +656,7 @@ int verify_json_web_signature(const char *jws, const unsigned char *payload,
BIGNUM *bn_s = NULL;
const char *alg;
size_t sig_len;
char *ch;
const char *ch;
if (jws == NULL || pkey == NULL)
return -EINVAL;
@@ -1034,7 +1037,7 @@ int clone_tag_def_list(const struct ekmf_tag_def_list *src,
if (src->tag_defs[i].description != NULL) {
dest->tag_defs[i].description =
strdup(src->tag_defs[i].description);
if (dest->tag_defs[i].description != NULL) {
if (dest->tag_defs[i].description == NULL) {
rc = -ENOMEM;
goto out;
}
@@ -1930,7 +1933,7 @@ int write_key_blob(const char *filename, unsigned char *key_blob,
if (filename == NULL || key_blob == NULL || key_blob_len == 0)
return -EINVAL;
fp = fopen(filename, "w");
fp = fopen_nofollow(filename, "w");
if (fp == NULL)
return -errno;
@@ -2051,7 +2054,7 @@ int write_x509_certificate(const char *pem_filename, X509 *cert)
if (pem_filename == NULL || cert == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -2085,7 +2088,7 @@ int write_x509_request(const char *pem_filename, X509_REQ *req, bool new_hdr)
if (pem_filename == NULL || req == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -2153,7 +2156,7 @@ int write_public_key(const char *pem_filename, EVP_PKEY *pkey)
if (pem_filename == NULL || pkey == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -2605,3 +2608,33 @@ int json_object_object_add_ex(struct json_object *obj, const char *const key,
return 0;
}
#endif
FILE *fopen_nofollow(const char *path, const char *mode)
{
int flags = O_NOFOLLOW;
int fd;
FILE *fp;
/* Determine flags based on mode */
if (mode[0] == 'r')
flags |= (mode[1] == '+') ? O_RDWR : O_RDONLY;
else if (mode[0] == 'w')
flags |= O_CREAT | O_TRUNC |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else if (mode[0] == 'a')
flags |= O_CREAT | O_APPEND |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else
return NULL;
fd = open(path, flags, 0600);
if (fd < 0)
return NULL;
fp = fdopen(fd, mode);
if (fp == NULL) {
close(fd);
return NULL;
}
return fp;
}

View File

@@ -124,4 +124,6 @@ int json_object_object_add_ex(struct json_object *obj, const char *const key,
const unsigned int opts);
#endif
FILE *fopen_nofollow(const char *path, const char *mode);
#endif

View File

@@ -46,7 +46,7 @@ detect-openssl-version.dep:
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
echo "#endif" >> $(TMPFILE)
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
echo " EVP_PKEY_id(NULL);" >> $(TMPFILE)
echo "}" >> $(TMPFILE)
mv $(TMPFILE) $@

View File

@@ -735,7 +735,8 @@ int kmip_connection_https_perform(struct kmip_connection *conn,
switch (conn->config.encoding) {
case KMIP_ENCODING_TTLV:
rc = kmip_decode_ttlv(write_cb.ttlv.resp_mem_bio, NULL,
response, debug);
response, KMIP_DECODE_MAX_NESTING_LEVEL,
debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_ttlv failed");
goto out;
@@ -750,7 +751,7 @@ int kmip_connection_https_perform(struct kmip_connection *conn,
}
rc = kmip_decode_json(write_cb.json.resp_obj, NULL, response,
debug);
KMIP_DECODE_MAX_NESTING_LEVEL, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_json failed");
goto out;
@@ -768,7 +769,8 @@ int kmip_connection_https_perform(struct kmip_connection *conn,
rc = kmip_decode_xml(xmlDocGetRootElement(
write_cb.xml.ctx->myDoc),
NULL, response, debug);
NULL, response,
KMIP_DECODE_MAX_NESTING_LEVEL, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_xml failed");
goto out;

View File

@@ -27,12 +27,16 @@
* @param parent the parent node or NULL if no parent exists.
* @param node On return: the decoded node. The newly allocated
* node has a reference count of 1.
* @param max_nesting_level the maximum nesting levels of structures within the
* KMIP node. If the nesting level is reached, E2BIG
* is returned.
* @param debug if true, debug messages are printed
*
* @returns 0 in case of success, or a negative errno value
*/
int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
struct kmip_node **node, bool debug)
struct kmip_node **node, size_t max_nesting_level,
bool debug)
{
json_object *tag_obj, *type_obj, *value_obj, *name_obj;
enum kmip_tag tag, v1_attr_tag = 0;
@@ -42,6 +46,9 @@ int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
int rc, num, i;
int64_t int64;
if (max_nesting_level == 0)
return -E2BIG;
if (obj == NULL || node == NULL)
return -EINVAL;
@@ -82,7 +89,7 @@ int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
rc = -EBADMSG;
goto out;
}
n->name = strdup(json_object_get_string(tag_obj));
n->name = strdup(json_object_get_string(name_obj));
}
type_obj = json_object_object_get(obj, KMIP_JSON_TYPE);
@@ -138,7 +145,7 @@ int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
for (i = 0; i < num; i++) {
rc = kmip_decode_json(
json_object_array_get_idx(value_obj, i),
n, &e, debug);
n, &e, max_nesting_level - 1, debug);
if (rc != 0) {
kmip_debug(debug, "Failed to parse "
"array element %d", i);
@@ -614,6 +621,10 @@ int kmip_encode_json(const struct kmip_node *node, json_object **obj,
case KMIP_TYPE_DATE_TIME:
tm = gmtime((time_t *)&node->date_time_value);
if (tm == NULL) {
rc = -EINVAL;
goto out;
}
strftime(outstr, sizeof(outstr), KMIP_ISO8601_TIMESTAMP_UTC,
tm);
memb_obj = json_object_new_string(outstr);

View File

@@ -426,8 +426,14 @@ int kmip_get_key_value(const struct kmip_node *node,
/* Must be a KMIP v1.x attribute then */
kmip_node_free(attr);
if (num_attrs != NULL)
*num_attrs = kmip_node_get_structure_element_count(node) - 1;
if (num_attrs != NULL) {
*num_attrs = kmip_node_get_structure_element_count(node);
if (*num_attrs == 0) {
rc = -EBADMSG;
goto error;
}
(*num_attrs)--;
}
if (v2_attr == NULL)
return 0;

View File

@@ -258,7 +258,7 @@ int kmip_node_add_structure_elements(struct kmip_node *node,
*
* @param node the KMIP node
*
* @returns the number of elements, or -1 if the node is not of type structure
* @returns the number of elements, or 0 if the node is not of type structure
*/
unsigned int kmip_node_get_structure_element_count(const struct kmip_node *node)
{
@@ -266,10 +266,10 @@ unsigned int kmip_node_get_structure_element_count(const struct kmip_node *node)
unsigned int i;
if (node == NULL)
return -1;
return 0;
if (node->type != KMIP_TYPE_STRUCTURE)
return -1;
return 0;
element = node->structure_value;
for (i = 0; element != NULL; i++)
@@ -319,7 +319,7 @@ struct kmip_node *kmip_node_get_structure_element_by_index(
* @param node the KMIP node
* @param tag the tag to find
*
* @returns the number of elements, or -1 if the node is not of type structure
* @returns the number of elements, or 0 if the node is not of type structure
*/
unsigned int kmip_node_get_structure_element_by_tag_count(
const struct kmip_node *node,
@@ -329,10 +329,10 @@ unsigned int kmip_node_get_structure_element_by_tag_count(
unsigned int i;
if (node == NULL)
return -1;
return 0;
if (node->type != KMIP_TYPE_STRUCTURE)
return -1;
return 0;
element = node->structure_value;
for (i = 0; element != NULL; element = element->next) {
@@ -897,7 +897,7 @@ struct kmip_node *kmip_node_clone(const struct kmip_node *node)
case KMIP_TYPE_TEXT_STRING:
if (node->text_value != NULL) {
clone->text_value = strdup(node->text_value);
if (node->text_value == NULL)
if (clone->text_value == NULL)
goto error;
clone->length = strlen(clone->text_value);
}
@@ -1370,7 +1370,8 @@ int kmip_connection_get_server_cert(const char *server,
struct kmip_conn_config config = { 0 };
struct kmip_connection *conn = NULL;
int rc, numcerts, i, port_found = 0;
char *hostname = NULL, *tok, *tok2;
const char *tok, *tok2;
char *hostname = NULL;
STACK_OF(X509) *chain;
bool do_verify = true;
FILE *fp = NULL;
@@ -1488,7 +1489,7 @@ retry:
}
if (i == 0 && server_cert_pem != NULL) {
fp = fopen(server_cert_pem, "w");
fp = fopen_nofollow(server_cert_pem, "w");
if (fp == NULL) {
rc = -errno;
kmip_debug(debug, "Failed to open %s for write",
@@ -1506,7 +1507,7 @@ retry:
fp = NULL;
if (server_pubkey_pem != NULL) {
fp = fopen(server_pubkey_pem, "w");
fp = fopen_nofollow(server_pubkey_pem, "w");
if (fp == NULL) {
rc = -errno;
kmip_debug(debug, "Failed to open %s "
@@ -1532,7 +1533,7 @@ retry:
if (i > 0 && cert_chain_pem != NULL) {
if (fp == NULL)
fp = fopen(cert_chain_pem, "w");
fp = fopen_nofollow(cert_chain_pem, "w");
if (fp == NULL) {
rc = -errno;
kmip_debug(debug, "Failed to open %s for write",

View File

@@ -94,19 +94,23 @@ int kmip_connection_https_perform(struct kmip_connection *connection,
bool debug);
void kmip_connection_https_term(struct kmip_connection *connection);
/* KIMP decoding and encoding internal functions */
/* KIMP decoding and encoding internal functions and definitions */
#define KMIP_DECODE_MAX_NESTING_LEVEL 32
int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
bool debug);
size_t max_nesting_level, bool debug);
int kmip_encode_ttlv(struct kmip_node *node, BIO *bio, size_t *size,
bool debug);
int kmip_decode_json(const json_object *obj, struct kmip_node *parent,
struct kmip_node **node, bool debug);
struct kmip_node **node, size_t max_nesting_level,
bool debug);
int kmip_encode_json(const struct kmip_node *node, json_object **obj,
bool debug);
int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
struct kmip_node **node, bool debug);
struct kmip_node **node, size_t max_nesting_level,
bool debug);
int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug);
#endif

View File

@@ -686,14 +686,20 @@ int kmip_get_get_attribute_list_response_payload(const struct kmip_node *node,
if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD)
return -EBADMSG;
if (kmip_node_get_type(node) != KMIP_TYPE_STRUCTURE)
return -EBADMSG;
if (unique_id != NULL)
*unique_id = kmip_node_get_structure_element_by_tag(node,
KMIP_TAG_UNIQUE_IDENTIFIER, 0);
if (num_attr_refs != NULL)
if (num_attr_refs != NULL) {
*num_attr_refs =
kmip_node_get_structure_element_count(node) - 1;
kmip_node_get_structure_element_count(node);
if (*num_attr_refs == 0)
return -EBADMSG;
(*num_attr_refs)--;
}
if (attr_ref == NULL)
return 0;
@@ -766,6 +772,8 @@ int kmip_get_get_attributes_response_payload(const struct kmip_node *node,
if (kmip_node_get_tag(node) != KMIP_TAG_RESPONSE_PAYLOAD)
return -EBADMSG;
if (kmip_node_get_type(node) != KMIP_TYPE_STRUCTURE)
return -EBADMSG;
if (unique_id != NULL)
*unique_id = kmip_node_get_structure_element_by_tag(node,
@@ -798,8 +806,12 @@ int kmip_get_get_attributes_response_payload(const struct kmip_node *node,
/* Must be a KMIP v1.x attribute then */
kmip_node_free(attr);
if (num_attrs != NULL)
*num_attrs = kmip_node_get_structure_element_count(node) - 1;
if (num_attrs != NULL) {
*num_attrs = kmip_node_get_structure_element_count(node);
if (*num_attrs == 0)
return -EBADMSG;
(*num_attrs)--;
}
if (v2_attr == NULL)
return 0;

View File

@@ -493,7 +493,8 @@ int kmip_connection_tls_perform(struct kmip_connection *conn,
kmip_debug(debug, "%lu bytes sent", size);
/* receive the response */
rc = kmip_decode_ttlv(conn->plain_tls.bio, NULL, response, debug);
rc = kmip_decode_ttlv(conn->plain_tls.bio, NULL, response,
KMIP_DECODE_MAX_NESTING_LEVEL, debug);
if (rc != 0 || *response == NULL) {
kmip_debug(debug, "kmip_decode_ttlv failed");
goto out;

View File

@@ -28,12 +28,15 @@
* as many bytes as needed.
* @param node On return: the decoded node. The newly allocated
* node has a reference count of 1.
* @param max_nesting_level the maximum nesting levels of structures within the
* KMIP node. If the nesting level is reached, E2BIG
* is returned.
* @param debug if true, debug messages are printed
*
* @returns 0 in case of success, or a negative errno value
*/
int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
bool debug)
size_t max_nesting_level, bool debug)
{
unsigned char padding[KMIP_TTLV_BLOCK_LENGTH];
unsigned char ttlv[KMIP_TTLV_HEADER_LENGTH];
@@ -44,6 +47,9 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
uint64_t int64;
int rc;
if (max_nesting_level == 0)
return -E2BIG;
if (bio == NULL || node == NULL)
return -EINVAL;
@@ -73,18 +79,18 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
n->ref_count = 1;
/* Tag: 3-byte binary unsigned integer, transmitted big endian */
n->tag |= (uint32_t)(ttlv[0] << 16);
n->tag |= (uint32_t)(ttlv[1] << 8);
n->tag |= (uint32_t)(ttlv[2]);
n->tag |= (uint32_t)ttlv[0] << 16;
n->tag |= (uint32_t)ttlv[1] << 8;
n->tag |= (uint32_t)ttlv[2];
/* Type: 1 byte containing a coded value that indicates the data type */
n->type = ttlv[3];
/* Length: 32-bit binary integer, transmitted big-endian */
n->length |= (uint32_t)(ttlv[4] << 24);
n->length |= (uint32_t)(ttlv[5] << 16);
n->length |= (uint32_t)(ttlv[6] << 8);
n->length |= (uint32_t)(ttlv[7]);
n->length |= (uint32_t)ttlv[4] << 24;
n->length |= (uint32_t)ttlv[5] << 16;
n->length |= (uint32_t)ttlv[6] << 8;
n->length |= (uint32_t)ttlv[7];
kmip_debug(debug, "tag: 0x%x type: 0x%x, length: %u", n->tag, n->type,
n->length);
@@ -98,6 +104,11 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
case KMIP_TYPE_TEXT_STRING:
case KMIP_TYPE_BYTE_STRING:
value_len = n->length;
if (value_len > INT_MAX) {
rc = -EMSGSIZE;
goto out;
}
value = calloc(1, value_len + 1);
if (value == NULL) {
kmip_debug(debug, "calloc failed");
@@ -160,14 +171,22 @@ int kmip_decode_ttlv(BIO *bio, size_t *size, struct kmip_node **node,
rc = -EIO;
goto out;
}
if (size != NULL)
if (size != NULL) {
if (*size < pad_len) {
rc = -EMSGSIZE;
goto out;
}
*size -= pad_len;
}
}
switch (n->type) {
case KMIP_TYPE_STRUCTURE:
while (value_len > 0) {
rc = kmip_decode_ttlv(bio, &value_len, &e, debug);
rc = kmip_decode_ttlv(bio, &value_len, &e,
max_nesting_level - 1, debug);
if (rc != 0) {
kmip_debug(debug, "kmip_decode_ttlv failed: "
"rc: %d", rc);
@@ -263,7 +282,7 @@ out:
static int kmip_node_get_length(struct kmip_node *node, size_t *length)
{
struct kmip_node *element;
size_t len;
size_t len, prev_len;
int rc;
if (node == NULL || length == NULL)
@@ -278,10 +297,13 @@ static int kmip_node_get_length(struct kmip_node *node, size_t *length)
if (rc != 0)
return rc;
prev_len = *length;
*length += KMIP_TTLV_HEADER_LENGTH + len;
if ((len % KMIP_TTLV_BLOCK_LENGTH) != 0)
*length += KMIP_TTLV_BLOCK_LENGTH -
(len % KMIP_TTLV_BLOCK_LENGTH);
if (*length < prev_len)
return -EOVERFLOW;
element = element->next;
}

View File

@@ -12,9 +12,11 @@
#include <errno.h>
#include <err.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "utils.h"
#include "names.h"
@@ -72,6 +74,8 @@ int kmip_parse_decimal_uint(const char *str, uint64_t *val)
if (str == NULL)
return -EINVAL;
if (str[0] == '-')
return -EBADMSG;
errno = 0;
v = strtoull(str, &endptr, 10);
@@ -164,13 +168,16 @@ int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
* Format a hex string from the byte array specified in val. The caller must
* free the returned str.
*/
int kmip_format_hex(const unsigned char *val, uint32_t length, bool prefix,
int kmip_format_hex(const unsigned char *val, size_t length, bool prefix,
char **str)
{
uint32_t str_len, i;
size_t str_len, i;
char tmp[4];
char *ret;
if (length > (SIZE_MAX - ((prefix ? 2 : 0) + 1)) / 2)
return -EINVAL;
str_len = length * 2 + (prefix ? 2 : 0) + 1;
ret = calloc(1, str_len);
if (ret == NULL)
@@ -220,14 +227,18 @@ int kmip_parse_bignum(const char *str, bool has_prefix, BIGNUM **bn)
int kmip_format_bignum(const BIGNUM *bn, bool prefix, char **str)
{
unsigned char *buf;
uint32_t len;
uint32_t len, prev_len;
int rc;
len = kmip_encode_bignum_length(bn);
/* BIG INTEGERS must be a multiple of 8 bytes long */
if ((len % KMIP_BIG_INTEGER_BLOCK_LENGTH) != 0)
if ((len % KMIP_BIG_INTEGER_BLOCK_LENGTH) != 0) {
prev_len = len;
len += KMIP_BIG_INTEGER_BLOCK_LENGTH -
(len % KMIP_BIG_INTEGER_BLOCK_LENGTH);
if (len < prev_len)
return -EOVERFLOW;
}
buf = malloc(len);
if (buf == NULL)
@@ -402,10 +413,10 @@ int kmip_parse_mask(enum kmip_tag tag, const char *str, char separator,
return rc;
}
static int kmip_append_string(char **str, int *str_len, char separator,
static int kmip_append_string(char **str, size_t *str_len, char separator,
const char *append)
{
int new_len;
size_t new_len;
char *tmp;
if (str == NULL || str_len == NULL)
@@ -422,6 +433,9 @@ static int kmip_append_string(char **str, int *str_len, char separator,
if (append != NULL)
new_len += strlen(append);
if (new_len < *str_len)
return -EOVERFLOW;
tmp = realloc(*str, new_len);
if (tmp == NULL)
return -ENOMEM;
@@ -446,8 +460,9 @@ int kmip_format_mask(enum kmip_tag tag, int32_t value, char separator,
char **str)
{
const struct kmip_enum *info;
int rc = 0, i, s_len = 0;
char *s = NULL, *tmp;
size_t s_len = 0;
int rc = 0, i;
info = kmip_enum_info_by_tag(tag);
if (info == NULL || value == 0)
@@ -726,3 +741,32 @@ enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent)
return 0;
}
FILE *fopen_nofollow(const char *path, const char *mode)
{
int flags = O_NOFOLLOW;
int fd;
FILE *fp;
/* Determine flags based on mode */
if (mode[0] == 'r')
flags |= (mode[1] == '+') ? O_RDWR : O_RDONLY;
else if (mode[0] == 'w')
flags |= O_CREAT | O_TRUNC |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else if (mode[0] == 'a')
flags |= O_CREAT | O_APPEND |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else
return NULL;
fd = open(path, flags, 0600);
if (fd < 0)
return NULL;
fp = fdopen(fd, mode);
if (fp == NULL) {
close(fd);
return NULL;
}
return fp;
}

View File

@@ -38,7 +38,7 @@ int kmip_parse_decimal_uint(const char *str, uint64_t *val);
int kmip_parse_hex_int(const char *str, int64_t *val);
int kmip_parse_hex(const char *str, bool has_prefix, unsigned char **val,
uint32_t *length);
int kmip_format_hex(const unsigned char *val, uint32_t length, bool prefix,
int kmip_format_hex(const unsigned char *val, size_t length, bool prefix,
char **str);
int kmip_parse_bignum(const char *str, bool has_prefix, BIGNUM **bn);
@@ -58,4 +58,6 @@ void kmip_node_dump(struct kmip_node *node, bool debug);
enum kmip_tag kmip_find_v1_attribute_name_tag(struct kmip_node *parent);
FILE *fopen_nofollow(const char *path, const char *mode);
#endif

View File

@@ -28,12 +28,16 @@
* @param parent the parent node or NULL if no parent exists.
* @param node On return: the decoded node.The newly allocated
* node has a reference count of 1.
* @param max_nesting_level the maximum nesting levels of structures within the
* KMIP node. If the nesting level is reached, E2BIG
* is returned.
* @param debug if true, debug messages are printed
*
* @returns 0 in case of success, or a negative errno value
*/
int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
struct kmip_node **node, bool debug)
struct kmip_node **node, size_t max_nesting_level,
bool debug)
{
char *tag_attr = NULL, *name_attr = NULL, *type_attr = NULL;
enum kmip_tag tag, v1_attr_tag = 0;
@@ -44,6 +48,9 @@ int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
int64_t int64;
int rc = 0, i;
if (max_nesting_level == 0)
return -E2BIG;
if (xml == NULL || node == NULL)
return -EINVAL;
@@ -122,7 +129,8 @@ int kmip_decode_xml(const xmlNode *xml, struct kmip_node *parent,
if (child->type != XML_ELEMENT_NODE)
continue;
rc = kmip_decode_xml(child, n, &e, debug);
rc = kmip_decode_xml(child, n, &e,
max_nesting_level - 1, debug);
if (rc != 0) {
kmip_debug(debug, "Failed to parse child "
"element %d", i);
@@ -452,6 +460,10 @@ int kmip_encode_xml(const struct kmip_node *node, xmlNode **xml, bool debug)
case KMIP_TYPE_DATE_TIME:
tm = gmtime((time_t *)&node->date_time_value);
if (tm == NULL) {
rc = -EINVAL;
goto out;
}
strftime(tmp_str, sizeof(tmp_str), KMIP_ISO8601_TIMESTAMP_UTC,
tm);
attr = xmlSetProp(ret_xml, (xmlChar *)KMIP_XML_VALUE,

View File

@@ -28,7 +28,7 @@ detect-openssl-version.dep:
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
echo "#endif" >> $(TMPFILE)
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
echo " EVP_PKEY_id(NULL);" >> $(TMPFILE)
echo "}" >> $(TMPFILE)
mv $(TMPFILE) $@

View File

@@ -258,7 +258,9 @@ static int sk_cca_get_library_functions(const struct sk_ext_cca_lib *cca_lib,
*
* @param cca_lib the CCA library structure
* @param curve_nid the nid specifying the curve.
* @param key_token a buffer to store the generated key token
* @param key_token a buffer to store the generated key token. If NULL,
* the required buffer size is returned in
* key_token_length (size query).
* @param key_token_length On entry: the size of the buffer
* On return: the size of the key token
* @param debug if true, debug messages are printed
@@ -286,7 +288,7 @@ int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib,
long param1 = 0;
int rc;
if (cca_lib == NULL || key_token == NULL || key_token_length == NULL)
if (cca_lib == NULL || key_token_length == NULL)
return -EINVAL;
if (key_token == NULL) {
@@ -384,7 +386,9 @@ int SK_CCA_generate_ec_key_pair(const struct sk_ext_cca_lib *cca_lib,
* 3, 5, 17, 257, or 65537. Specify zero to choose the
* exponent by random (only possible for modulus_bits
* up to 2048).
* @param key_token a buffer to store the generated key token
* @param key_token a buffer to store the generated key token. If NULL,
* the required buffer size is returned in
* key_token_length (size query).
* @param key_token_length On entry: the size of the buffer
* On return: the size of the key token
* @param debug if true, debug messages are printed
@@ -412,7 +416,7 @@ int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib,
long param1 = 0;
int rc;
if (cca_lib == NULL || key_token == NULL || key_token_length == NULL)
if (cca_lib == NULL || key_token_length == NULL)
return -EINVAL;
if (key_token == NULL) {
@@ -459,7 +463,7 @@ int SK_CCA_generate_rsa_key_pair(const struct sk_ext_cca_lib *cca_lib,
case 257:
key_value_structure.public_exp_length = 2;
key_value_structure.public_exponent[0] = 0x01;
key_value_structure.public_exponent[0] = 0x01;
key_value_structure.public_exponent[1] = 0x01;
break;
case 65537:
key_value_structure.public_exp_length = 3;
@@ -560,6 +564,11 @@ static const void *sk_cca_get_pka_section(const unsigned char *key_token,
section_hdr = (struct cca_section_header *)&key_token[ofs];
while (section_hdr->section_identifier != section_id) {
if (section_hdr->section_length <
sizeof(struct cca_section_header)) {
sk_debug(debug, "ERROR: invalid section length");
return NULL;
}
ofs += section_hdr->section_length;
if (ofs >= token_hdr->token_length) {
sk_debug(debug, "ERROR: section %u not found",
@@ -1385,7 +1394,7 @@ static int sk_cca_get_public_from_rsa_key(const unsigned char *key_token,
return -EINVAL;
}
if (rsa_pub_section->section_header.section_length <
sizeof(struct cca_ec_pub_key_section)) {
sizeof(struct cca_rsa_pub_key_section)) {
sk_debug(debug, "ERROR: invalid RSA public key section length");
return -EINVAL;
}

View File

@@ -195,7 +195,7 @@ static int sk_ep11_generate_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
CK_RV rv;
int rc;
if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL)
if (ep11_lib == NULL || key_token_length == NULL)
return -EINVAL;
if (key_token == NULL) {
@@ -262,7 +262,9 @@ static int sk_ep11_generate_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
*
* @param ep11_lib the Ep11 library structure
* @param curve_nid the nid specifying the curve.
* @param key_token a buffer to store the generated key token
* @param key_token a buffer to store the generated key token. If NULL,
* the required buffer size is returned in
* key_token_length (size query).
* @param key_token_length On entry: the size of the buffer
* On return: the size of the key token
* @param debug if true, debug messages are printed
@@ -292,7 +294,7 @@ int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
const struct sk_ec_curve_info *curve;
int rc;
if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL)
if (ep11_lib == NULL || key_token_length == NULL)
return -EINVAL;
sk_debug(debug, "curve_nid: %d", curve_nid);
@@ -331,7 +333,9 @@ int SK_EP11_generate_ec_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
* 3, 5, 17, 257, or 65537. Specify zero to choose the
* exponent by random.
* @param x9_31 if true, generate a X9.31 RSA key
* @param key_token a buffer to store the generated key token
* @param key_token a buffer to store the generated key token. If NULL,
* the required buffer size is returned in
* key_token_length (size query).
* @param key_token_length On entry: the size of the buffer
* On return: the size of the key token
* @param debug if true, debug messages are printed
@@ -365,7 +369,7 @@ int SK_EP11_generate_rsa_key_pair(const struct sk_ext_ep11_lib *ep11_lib,
CK_ULONG priv_tmpl_num = sizeof(priv_tmpl) / sizeof(CK_ATTRIBUTE);
int rc;
if (ep11_lib == NULL || key_token == NULL || key_token_length == NULL)
if (ep11_lib == NULL || key_token_length == NULL)
return -EINVAL;
sk_debug(debug, "modulus_bits: %lu pub_exp: %u x9_31: %d",
@@ -413,6 +417,8 @@ static unsigned char sk_ep11_parse_der_tag(const unsigned char *data,
if (num > sizeof(size_t))
return 0;
*value_len = data[2];
if (data_len < 2 + num)
return 0;
for (i = 1; i < num; i++) {
*value_len <<= 8;
*value_len |= data[2 + i];
@@ -498,6 +504,8 @@ static int sk_ep11_parse_spki(const unsigned char *spki, size_t spki_len,
pub_key, pub_key_len);
if (tag != 0x03) /* BITSTRING */
return -EINVAL;
if (*pub_key_len == 0)
return -EINVAL;
/* skip unsused-bits byte */
(*pub_key)++;
@@ -555,7 +563,7 @@ int SK_EP11_get_key_type(const unsigned char *key_token,
return -EINVAL;
spki_size = key_token_length - hdr->len;
if (spki_size <= 0)
if (spki_size == 0)
return -EINVAL;
rc = sk_ep11_parse_spki(key_token + hdr->len, spki_size,
@@ -702,7 +710,7 @@ static int sk_ep11_rsa_sign(const unsigned char *key_token,
case RSA_X931_PADDING:
mech.mechanism = CKM_RSA_X9_31;
if ((hdr->flags && PKEY_TYPE_EP11_FLAG_X9_31) == 0) {
if ((hdr->flags & PKEY_TYPE_EP11_FLAG_X9_31) == 0) {
sk_debug(debug, "ERROR: no RSA X9.31 key");
return -EINVAL;
}
@@ -1375,7 +1383,7 @@ int SK_EP11_get_public_from_secure_key(const unsigned char *key_token,
return -EINVAL;
spki_size = key_token_length - hdr->len;
if (spki_size <= 0)
if (spki_size == 0)
return -EINVAL;
rc = sk_ep11_parse_spki(key_token + hdr->len, spki_size,

View File

@@ -698,6 +698,7 @@ static int sk_pkey_meth_setup_pkey(EVP_PKEY *pkey,
struct sk_pkey_data *data;
EC_KEY *ec;
RSA *rsa;
int rc;
if (pkey == NULL || secure_key == NULL || secure_key_size == 0 ||
funcs == NULL)
@@ -733,12 +734,14 @@ static int sk_pkey_meth_setup_pkey(EVP_PKEY *pkey,
ec = EVP_PKEY_get0_EC_KEY(pkey);
if (ec == NULL) {
sk_debug(debug, "EVP_PKEY_get0_EC_KEY failed");
return -EIO;
rc = -EIO;
goto error;
}
if (!EC_KEY_set_ex_data(ec, sk_pkey_data_ec_index, data)) {
sk_debug(debug, "EC_KEY_set_ex_data failed");
return -EIO;
rc = -EIO;
goto error;
}
break;
case EVP_PKEY_RSA:
@@ -746,17 +749,29 @@ static int sk_pkey_meth_setup_pkey(EVP_PKEY *pkey,
rsa = EVP_PKEY_get0_RSA(pkey);
if (rsa == NULL) {
sk_debug(debug, "EVP_PKEY_get0_RSA failed");
return -EIO;
rc = -EIO;
goto error;
}
if (!RSA_set_ex_data(rsa, sk_pkey_data_rsa_index, data)) {
sk_debug(debug, "RSA_set_ex_data failed");
return -EIO;
rc = -EIO;
goto error;
}
break;
default:
rc = -EIO;
goto error;
}
return 0;
error:
if (data != NULL) {
OPENSSL_free(data->key_blob);
OPENSSL_free(data);
}
return rc;
}
/**
@@ -796,13 +811,13 @@ int SK_OPENSSL_init(bool debug)
return -EIO;
}
sk_pkey_meth_default_method_rsa = EVP_PKEY_meth_find(EVP_PKEY_RSA);
if (sk_pkey_meth_default_method_ec == NULL) {
if (sk_pkey_meth_default_method_rsa == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA) failed");
return -EIO;
}
sk_pkey_meth_default_method_rsa_pss =
EVP_PKEY_meth_find(EVP_PKEY_RSA_PSS);
if (sk_pkey_meth_default_method_ec == NULL) {
if (sk_pkey_meth_default_method_rsa_pss == NULL) {
sk_debug(debug, "ERROR: EVP_PKEY_meth_find(RSA-PSS) failed");
return -EIO;
}

View File

@@ -10,10 +10,12 @@
#include <string.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
@@ -811,6 +813,35 @@ out:
return rc;
}
static inline FILE *fopen_nofollow(const char *path, const char *mode)
{
int flags = O_NOFOLLOW;
int fd;
FILE *fp;
/* Determine flags based on mode */
if (mode[0] == 'r')
flags |= (mode[1] == '+') ? O_RDWR : O_RDONLY;
else if (mode[0] == 'w')
flags |= O_CREAT | O_TRUNC |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else if (mode[0] == 'a')
flags |= O_CREAT | O_APPEND |
((mode[1] == '+') ? O_RDWR : O_WRONLY);
else
return NULL;
fd = open(path, flags, 0600);
if (fd < 0)
return NULL;
fp = fdopen(fd, mode);
if (fp == NULL) {
close(fd);
return NULL;
}
return fp;
}
/**
* Reads a X.509 certificate from the specified PEM file.
@@ -863,7 +894,7 @@ int SK_UTIL_write_x509_certificate(const char *pem_filename, X509 *cert)
if (pem_filename == NULL || cert == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -898,7 +929,7 @@ int SK_UTIL_write_x509_request(const char *pem_filename, X509_REQ *req,
if (pem_filename == NULL || req == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;
@@ -982,7 +1013,7 @@ int SK_UTIL_write_key_blob(const char *filename, unsigned char *key_blob,
if (filename == NULL || key_blob == NULL || key_blob_len == 0)
return -EINVAL;
fp = fopen(filename, "w");
fp = fopen_nofollow(filename, "w");
if (fp == NULL)
return -errno;
@@ -1047,7 +1078,7 @@ int SK_UTIL_write_public_key(const char *pem_filename, EVP_PKEY *pkey)
if (pem_filename == NULL || pkey == NULL)
return -EINVAL;
fp = fopen(pem_filename, "w");
fp = fopen_nofollow(pem_filename, "w");
if (fp == NULL)
return -errno;

View File

@@ -742,6 +742,8 @@ static int read_cmdline(struct task_t *task)
buf[i] = ' ';
}
name_lens.cmdline_len = num;
if (name_lens.cmdline_len > MAX_CMD_LEN)
name_lens.cmdline_len = MAX_CMD_LEN;
cmdlnlenp = mon_record + sizeof(struct monwrite_hdr);
cmdlnlenp += sizeof(struct procd_hdr);
cmdlnlenp += sizeof(struct task_t);

View File

@@ -44,7 +44,20 @@
#define MAX_GET_DATA 4094 /* maximum GET response data length */
#define GET_AREA_LEN MAX_GET_DATA + 512 /* size for GET command area length */
#define TIME_BUF_SIZE 128 /* buffer size for date and time string */
#define MAX_OID_STR_LEN MAX_OID_LEN * 5 /* max OID string size */
/*
* Define maximum SNMP OID string size.
* An OID can have up to 128 sub-ids and each sub-id can have up to 10
* digits. When converted to string, this means 1 character for the
* period separator and 10 characters for the digits themselves. At
* the end of OID string, we need one more character for the null
* terminator. The unsigned long data type, which can have up to 20
* digits is used to represent a sub-id. This means each sub-id
* string now needs 20+1 characters instead of 10+1 in-order to avoid
* compiler warnings.
*/
#define MAX_OID_STR_LEN (MAX_OID_LEN * 21) + 1
/* definitions for 2.6 qeth */
#define QETH_SYSFILE "/sys/bus/ccwgroup/drivers/qeth/notifier_register"
#define SIOC_QETH_ADP_SET_SNMP_CONTROL (SIOCDEVPRIVATE + 5)

View File

@@ -110,7 +110,7 @@ int str_to_oid_conv ( char* uc_oid, oid* ul_oid )
*********************************************************************/
int oid_to_str_conv (oid* ul_oid, size_t length, char* uc_oid )
{
#define MAX_CHARS 50 /* size of buffer */
#define MAX_CHARS 22 /* size of buffer */
int i;
short valid = TRUE;
char buffer[MAX_CHARS]; /* buffer used for conversion */
@@ -125,9 +125,9 @@ int oid_to_str_conv (oid* ul_oid, size_t length, char* uc_oid )
{
/* convert and append OID digit to return string */
if (i == 0)
sprintf( buffer, "%lu", ul_oid[i] );
snprintf( buffer, sizeof(buffer), "%lu", ul_oid[i] );
else
sprintf( buffer, ".%lu", ul_oid[i] );
snprintf( buffer, sizeof(buffer), ".%lu", ul_oid[i] );
strcat( uc_oid, buffer );
} /* end for */

587
rust/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,12 +17,11 @@ resolver = "2"
[workspace.package]
edition = "2021"
license = "MIT"
rust-version = "1.75.0"
rust-version = "1.85.0"
[workspace.lints.rust]
missing_docs = { level = "deny", priority = 1 }
missing_debug_implementations = "warn"
non_ascii_idents = "warn"
nonstandard-style = "warn"
trivial_numeric_casts = "warn"
unstable_features = "warn"

View File

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

View File

@@ -7,11 +7,11 @@ mod msa;
mod query;
mod stfle;
use anyhow::bail;
use anyhow::Result as anyhowRes;
use clap::Parser;
use std::io::ErrorKind;
use std::result::Result::Ok;
use anyhow::{bail, Result as anyhowRes};
use clap::Parser;
use utils::print_version;
use crate::cli::{Cli, Format};

View File

@@ -2,11 +2,13 @@
//
// Copyright IBM Corp. 2024
use crate::cli::Cli;
use core::fmt::{Display, Formatter, Result};
use serde::{Serialize, Serializer};
use utils::HexSlice;
use crate::cli::Cli;
/// Number of total function codes (0 to 127)
pub const NUMBER_FUNC_CODES: usize = 128;

View File

@@ -3,15 +3,13 @@
// Copyright IBM Corp. 2024
use std::fs::File;
use std::io::Error;
use std::io::Read;
use std::io::{Error, Read};
use std::ops::Index;
use std::result::Result;
use zerocopy::FromBytes;
use crate::msa::InstructionKind;
use crate::msa::QueryAuthInfo;
use crate::msa::{InstructionKind, QueryAuthInfo};
/// Path to sysfs in which the query and qai informations are fetched from
const SYSFS_PATH: &str = "/sys/devices/system/cpu/cpacf/";

View File

@@ -15,17 +15,18 @@ workspace = true
[dependencies]
byteorder = "1.5"
curl = "0.4.47"
curl = "0.4.49"
enum_dispatch = "0.3.13"
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"
log = { version = "0.4.29", features = ["std", "release_max_level_debug"] }
openssl = "0.10.80"
openssl-sys = "0.9.116"
serde = { version = "1.0.228", features = ["derive"] }
thiserror = "2.0.18"
zerocopy = { version="0.8", features = ["derive"] }
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" }
[dev-dependencies]
serde_test = "1.0.177"
testing_logger = "0.1.1"

View File

@@ -1,13 +1,12 @@
#![allow(missing_docs)]
use pv::request::EcPubKeyCoord;
use pv_core::misc::encode_hex;
use s390_pv as pv;
use std::env::args;
use pv::misc::{read_certs, read_file};
use pv::request::EcPubKeyCoord;
use pv::{Error, Result};
use pv_core::misc::encode_hex;
use s390_pv as pv;
fn main() -> Result<()> {
let hkd = args().nth(1).expect("Expect one Host-key document");

View File

@@ -2,10 +2,9 @@
//
// Copyright IBM Corp. 2023, 2024
use std::{
io::{Read, Seek, SeekFrom::Current},
mem::size_of,
};
use std::io::SeekFrom::Current;
use std::io::{Read, Seek};
use std::mem::size_of;
use log::{debug, warn};
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
@@ -13,6 +12,13 @@ use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64
// (SE) boot request control block aka SE header
use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE};
/// Version of the Secure Execution header
#[derive(Debug, PartialEq)]
pub enum SeHdrVersion {
/// Secure Execution header v1
One = 0x100,
}
/// Struct containing all SE-header tags.
///
/// Contains:
@@ -206,7 +212,7 @@ impl BootHdrTags {
///
/// This function will return an error if the header could not be found in
/// `img` or is invalid.
pub fn from_se_image<R>(img: &mut R) -> Result<Self>
pub fn from_se_image<R>(img: &mut R) -> Result<(Self, SeHdrVersion)>
where
R: Read + Seek,
{
@@ -233,10 +239,13 @@ impl BootHdrTags {
};
// Some sanity checks
if hdr_head.version.get() != 0x100 {
debug!("Unsupported hdr-version: {:0>4x}", hdr_head.version.get());
return Err(Error::InvBootHdr);
}
let hdr_version = match hdr_head.version.get() {
0x100 => SeHdrVersion::One,
_ => {
debug!("Unsupported hdr-version: {:0>4x}", hdr_head.version.get());
return Err(Error::InvBootHdr);
}
};
// go to the Boot header tag
img.seek(Current(
@@ -249,12 +258,15 @@ impl BootHdrTags {
let mut tag = [0u8; BootHdrHead::TAG_SIZE];
img.read_exact(tag.as_mut_slice())?;
Ok(Self {
pld: hdr_head.pld,
ald: hdr_head.ald,
tld: hdr_head.tld,
tag,
})
Ok((
Self {
pld: hdr_head.pld,
ald: hdr_head.ald,
tld: hdr_head.tld,
tag,
},
hdr_version,
))
}
}
@@ -320,7 +332,7 @@ mod tests {
fn from_se_image_hdr() {
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap();
assert_eq!(hdr_tags, EXP_HDR);
assert_eq!(hdr_tags, (EXP_HDR, SeHdrVersion::One));
}
#[test]
@@ -356,18 +368,19 @@ mod tests {
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
img[0x12000..0x12280].copy_from_slice(bin_hdr);
let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(img)).unwrap();
assert_eq!(hdr_tags, EXP_HDR);
assert_eq!(hdr_tags, (EXP_HDR, SeHdrVersion::One));
}
#[test]
fn tags_convert_u8() {
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap();
let ser: &[u8] = hdr_tags.as_ref();
let ser: &[u8] = hdr_tags.0.as_ref();
let mut ser = ser.to_vec();
let der: BootHdrTags = ser.clone().try_into().unwrap();
assert_eq!(hdr_tags, der);
assert_eq!(hdr_tags.0, der);
assert_eq!(hdr_tags.1, SeHdrVersion::One);
ser.pop();
let der: Result<BootHdrTags> = ser.clone().try_into();
@@ -380,7 +393,7 @@ mod tests {
}
#[test]
fn se_img_metadata() {
fn se_img_metadata_v1() {
let metadata = SeImgMetaData::new_v1(0x14000, 0x16000);
let data = [
83, 101, 73, 109, 103, 76, 110, 120, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 0, 0, 0, 0,

View File

@@ -2,25 +2,28 @@
//
// Copyright IBM Corp. 2023, 2024
use std::{convert::TryInto, fmt::Display, ops::Range};
use std::convert::TryInto;
use std::fmt::Display;
use std::ops::Range;
use enum_dispatch::enum_dispatch;
use openssl::{
derive::Deriver,
ec::{EcGroup, EcKey},
hash::{DigestBytes, MessageDigest},
md::MdRef,
nid::Nid,
pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public},
pkey_ctx::{HkdfMode, PkeyCtx},
rand::rand_bytes,
rsa::Padding,
sign::{Signer, Verifier},
symm::{decrypt_aead as openssl_decrypt_aead, encrypt_aead as openssl_encrypt_aead, Cipher},
use openssl::derive::Deriver;
use openssl::ec::{EcGroup, EcKey};
use openssl::hash::{DigestBytes, MessageDigest};
use openssl::md::MdRef;
use openssl::nid::Nid;
use openssl::pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public};
use openssl::pkey_ctx::{HkdfMode, PkeyCtx};
use openssl::rand::rand_bytes;
use openssl::rsa::Padding;
use openssl::sign::{Signer, Verifier};
use openssl::symm::{
decrypt_aead as openssl_decrypt_aead, encrypt_aead as openssl_encrypt_aead, Cipher,
};
use pv_core::request::Confidential;
use crate::{error::Result, Error};
use crate::error::Result;
use crate::Error;
/// An AES256-GCM key that will purge itself out of the memory when going out of scope
pub type Aes256GcmKey = Confidential<[u8; SymKeyType::AES_256_GCM_KEY_LEN]>;
@@ -159,6 +162,12 @@ impl SymKey {
}
}
impl Display for SymKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SymKey({:?})", self.key_type())
}
}
/// Performs an hkdf according to RFC 5869.
/// See [`OpenSSL HKDF`]()
///
@@ -539,8 +548,166 @@ pub(crate) fn verify_signature<T: HasPublic>(
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::thread;
use super::*;
use crate::{get_test_asset, test_utils::*, PvCoreError};
use crate::test_utils::*;
use crate::{get_test_asset, PvCoreError};
/// Test that deterministic RNG contexts are thread-local and don't interfere.
///
/// Per OpenSSL documentation (RAND_get0_primary(3)):
/// "The public and private DRBG are thread-local instances, which are used by
/// RAND_bytes() and RAND_priv_bytes(), respectively."
///
/// Reference: <https://docs.openssl.org/3.1/man3/RAND_get0_primary/>
///
/// Note: RAND_set0_public() and RAND_set0_private() require OpenSSL >= 3.1.
#[test]
fn test_deterministic_rng_thread_isolation() {
use std::sync::Barrier;
use openssl::rand::rand_bytes;
// Barriers to synchronize: thread1 installs → thread2 installs → both generate → both
// complete
let barrier_after_t1_install = Arc::new(Barrier::new(2));
let barrier_after_t2_install = Arc::new(Barrier::new(2));
let barrier_after_rand_bytes = Arc::new(Barrier::new(2));
let barrier1_clone = Arc::clone(&barrier_after_t1_install);
let barrier2_clone = Arc::clone(&barrier_after_t2_install);
let barrier3_clone = Arc::clone(&barrier_after_rand_bytes);
// Thread 1: Install deterministic RNG with specific entropy
let thread1 = thread::spawn(move || {
let entropy = [0x42u8; 4096];
let nonce = [0x24u8; 48];
// Install thread-local deterministic RNG
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
// Signal thread2 that we've installed our RNG
barrier1_clone.wait();
// Wait for thread2 to install its RNG
barrier2_clone.wait();
// Now generate bytes while thread2 also has its RNG installed
let mut buf = [0u8; 32];
rand_bytes(&mut buf).unwrap();
// Wait for thread2 to also complete rand_bytes
barrier3_clone.wait();
buf
});
// Thread 2: Install different deterministic RNG after thread1
let thread2 = thread::spawn(move || {
// Wait for thread1 to install its RNG first
barrier_after_t1_install.wait();
// Now install our own thread-local deterministic RNG with different entropy
let entropy = [0xAAu8; 4096];
let nonce = [0x55u8; 48];
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
// Signal thread1 that we've installed our RNG
barrier_after_t2_install.wait();
// Generate bytes with our different entropy (concurrently with thread1)
let mut buf = [0u8; 32];
rand_bytes(&mut buf).unwrap();
// Wait for thread1 to also complete rand_bytes
barrier_after_rand_bytes.wait();
buf
});
let t1_buf = thread1.join().unwrap();
let t2_buf = thread2.join().unwrap();
// Expected deterministic values for thread 1 (entropy=0x42, nonce=0x24)
let expected_t1: [u8; 32] = [
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
];
// Expected deterministic values for thread 2 (entropy=0xAA, nonce=0x55)
let expected_t2: [u8; 32] = [
170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170,
170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170,
];
// Verify each thread produced its expected deterministic output
assert_eq!(
t1_buf, expected_t1,
"Thread 1 should produce deterministic output"
);
assert_eq!(
t2_buf, expected_t2,
"Thread 2 should produce deterministic output"
);
// Also verify they are different (proves thread-local isolation)
assert_ne!(
t1_buf, t2_buf,
"Different thread-local entropy should produce different output"
);
}
/// Test that the original RNG is properly restored after DeterministicTestRandGuard is dropped
#[test]
fn test_deterministic_rng_restoration() {
use openssl::rand::rand_bytes;
// Generate random bytes with system RNG before installing deterministic RNG
let mut before_buf = [0u8; 32];
rand_bytes(&mut before_buf).unwrap();
let deterministic_buf = {
let entropy = [0x42u8; 4096];
let nonce = [0x24u8; 48];
// Install deterministic RNG
let _rng = DeterministicTestRandGuard::install(&entropy, &nonce).unwrap();
// Generate deterministic bytes
let mut buf = [0u8; 32];
rand_bytes(&mut buf).unwrap();
// Expected deterministic output
let expected: [u8; 32] = [
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
];
assert_eq!(buf, expected, "Should produce deterministic output");
buf
// _rng is dropped here, should restore original RNG
};
// Generate random bytes again with restored system RNG
let mut after_buf = [0u8; 32];
rand_bytes(&mut after_buf).unwrap();
// The system RNG should produce different random values each time
// (extremely unlikely to match the deterministic output)
assert_ne!(
after_buf, deterministic_buf,
"After restoration, system RNG should produce different random values"
);
// Also verify that before and after are different (system RNG produces random values)
// Note: This could theoretically fail with probability 1/2^256, but that's negligible
assert_ne!(
before_buf, after_buf,
"System RNG should produce different random values on each call"
);
}
#[test]
fn sign_ec() {

View File

@@ -94,7 +94,7 @@ pub enum Error {
BinArcbInvVersion(u32),
#[error(
"The attestation request encrypted sice is to0 small {0}. Request probably tampered with."
"The attestation request encrypted sice is too small {0}. Request probably tampered with."
)]
BinArcbSeaSmall(u32),
@@ -173,8 +173,16 @@ pub enum HkdVerifyErrorType {
IssuerMismatch,
#[error("No CRL distribution points found")]
NoCrlDP,
#[error("CRL distribution point uses unsupported protocol (only HTTP/HTTPS allowed)")]
InvalidCrlProtocol,
#[error("The IBM Z signing key could not be verified. Error occurred at level {1}")]
IbmSignInvalid(#[source] openssl::x509::X509VerifyResult, u32),
#[error("Too many redirections during CRL download")]
TooManyRedirectionsCrlDownload,
#[error("CRL download exceeds maximum file size of {} MiB", .0 / (1024 * 1024))]
CrlDownloadTooLarge(u64),
#[error("CRL download failed")]
CrlDownloadFailed,
}
macro_rules! bail_hkd_verify {

View File

@@ -63,13 +63,11 @@ pub mod uv {
pub mod attest {
pub use pv_core::attest::*;
pub use crate::uvattest::{
additional::AdditionalData,
arcb::{
AttestationAuthenticated, AttestationFlags, AttestationRequest, AttestationVersion,
},
attest::{AttestationItems, AttestationMeasurement},
pub use crate::uvattest::additional::AdditionalData;
pub use crate::uvattest::arcb::{
AttestationAuthenticated, AttestationFlags, AttestationRequest, AttestationVersion,
};
pub use crate::uvattest::attest::{AttestationItems, AttestationMeasurement};
}
/// Definitions and functions to write objects in PEM format
@@ -91,27 +89,22 @@ pub use crate::error::HkdVerifyErrorType;
/// Functionalities to build UV requests
pub mod request {
pub use crate::{
brcb::{seek_se_hdr_start, BootHdrTags, SeImgMetaData},
crypto::{
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array,
AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey,
SymKeyType, SHA_512_HASH_LEN,
},
req::{EcPubKeyCoord, Encrypt, Keyslot, ReqEncrCtx, Request},
verify::{CertVerifier, HkdVerifier, NoVerifyHkd},
pub use crate::brcb::{seek_se_hdr_start, BootHdrTags, SeImgMetaData};
pub use crate::crypto::{
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array,
AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey, SymKeyType,
SHA_512_HASH_LEN,
};
pub use crate::req::{EcPubKeyCoord, Encrypt, HostKey, Keyslot, ReqEncrCtx, Request};
pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd};
/// Reexports some useful OpenSSL symbols
pub mod openssl {
pub use openssl::{
error::ErrorStack,
hash::DigestBytes,
nid::Nid,
pkey,
sha::{Sha256, Sha512},
x509,
};
pub use openssl::error::ErrorStack;
pub use openssl::hash::DigestBytes;
pub use openssl::nid::Nid;
pub use openssl::sha::{Sha256, Sha512};
pub use openssl::{pkey, x509};
// rust-OpenSSL does not define these NIDs
#[allow(missing_docs)]
pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
@@ -120,7 +113,6 @@ pub mod request {
}
pub use pv_core::request::*;
pub use pv_core::PolicyReference;
}
@@ -128,11 +120,9 @@ pub mod request {
pub mod secret {
pub use pv_core::secret::*;
pub use crate::uvsecret::{
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
ext_secret::ExtSecret,
guest_secret::GuestSecret,
retr_secret::{IbmProtectedKey, RetrievedSecret},
user_data::verify_asrcb_and_get_user_data,
};
pub use crate::uvsecret::asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion};
pub use crate::uvsecret::ext_secret::ExtSecret;
pub use crate::uvsecret::guest_secret::GuestSecret;
pub use crate::uvsecret::retr_secret::{IbmProtectedKey, RetrievedSecret};
pub use crate::uvsecret::user_data::verify_asrcb_and_get_user_data;
}

View File

@@ -2,11 +2,11 @@
//
// Copyright IBM Corp. 2023
use std::ffi::c_int;
use std::fmt;
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
use openssl::x509::{X509CrlRef, X509Ref};
use std::ffi::c_int;
mod ffi {
extern "C" {
@@ -99,9 +99,8 @@ impl AkidExtension for X509CrlRef {
#[cfg(test)]
mod test {
use crate::test_utils::load_gen_cert;
use super::*;
use crate::test_utils::load_gen_cert;
#[test]
fn akid() {

View File

@@ -3,10 +3,12 @@
// Copyright IBM Corp. 2024
use core::slice;
use std::ffi::c_int;
use std::marker::PhantomData;
use std::ptr;
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);

View File

@@ -1,16 +1,13 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
pub use crate::openssl_extensions::stackable_crl::*;
use foreign_types::{ForeignType, ForeignTypeRef};
use openssl::{
error::ErrorStack,
stack::{Stack, StackRef},
x509::{
store::{X509StoreBuilderRef, X509StoreRef},
X509CrlRef, X509NameRef, X509Ref, X509StoreContextRef, X509,
},
};
use openssl::error::ErrorStack;
use openssl::stack::{Stack, StackRef};
use openssl::x509::store::{X509StoreBuilderRef, X509StoreRef};
use openssl::x509::{X509CrlRef, X509NameRef, X509Ref, X509StoreContextRef, X509};
pub use crate::openssl_extensions::stackable_crl::*;
pub fn opt_to_ptr<T: ForeignTypeRef>(o: Option<&T>) -> *mut T::CType {
match o {

View File

@@ -0,0 +1,595 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
#![cfg_attr(not(test), expect(unused))]
use std::ffi::CStr;
use std::ptr::NonNull;
use foreign_types::ForeignType;
use openssl::error::ErrorStack;
use openssl::pkey::{KeyType, PKey, Private};
use openssl::pkey_ctx::PkeyCtx;
// automatically generated by rust-bindgen 0.69.5
mod ffi {
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct evp_pkey_ctx_st {
_unused: [u8; 0],
}
pub type EvpPkeyCtx = evp_pkey_ctx_st;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ossl_param_st {
pub key: *const ::std::os::raw::c_char,
pub data_type: ::std::os::raw::c_uint,
pub data: *mut ::std::os::raw::c_void,
pub data_size: usize,
pub return_size: usize,
}
pub type OsslParam = ossl_param_st;
extern "C" {
pub fn EVP_PKEY_CTX_new_from_name(
libctx: *mut ::std::os::raw::c_void,
name: *const ::std::os::raw::c_char,
propquery: *const ::std::os::raw::c_char,
) -> *mut EvpPkeyCtx;
}
extern "C" {
pub fn EVP_PKEY_keygen_init(ctx: *mut EvpPkeyCtx) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn EVP_PKEY_generate(
ctx: *mut EvpPkeyCtx,
ppkey: *mut *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn EVP_PKEY_CTX_free(ctx: *mut EvpPkeyCtx);
}
extern "C" {
pub fn EVP_PKEY_encapsulate_init(
ctx: *mut EvpPkeyCtx,
params: *const OsslParam,
) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn EVP_PKEY_encapsulate(
ctx: *mut EvpPkeyCtx,
wrappedkey: *mut ::std::os::raw::c_uchar,
wrappedkeylen: *mut usize,
genkey: *mut ::std::os::raw::c_uchar,
genkeylen: *mut usize,
) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn EVP_PKEY_decapsulate_init(
ctx: *mut EvpPkeyCtx,
params: *const OsslParam,
) -> ::std::os::raw::c_int;
}
extern "C" {
pub fn EVP_PKEY_decapsulate(
ctx: *mut EvpPkeyCtx,
unwrapped: *mut ::std::os::raw::c_uchar,
unwrappedlen: *mut usize,
wrapped: *const ::std::os::raw::c_uchar,
wrappedlen: usize,
) -> ::std::os::raw::c_int;
}
}
const ML_KEM_512_NAME: &CStr = c"ML-KEM-512";
const ML_KEM_768_NAME: &CStr = c"ML-KEM-768";
const ML_KEM_1024_NAME: &CStr = c"ML-KEM-1024";
fn ml_kem_name(key_type: KeyType) -> Result<&'static CStr, ErrorStack> {
if key_type == KeyType::ML_KEM_512 {
Ok(ML_KEM_512_NAME)
} else if key_type == KeyType::ML_KEM_768 {
Ok(ML_KEM_768_NAME)
} else if key_type == KeyType::ML_KEM_1024 {
Ok(ML_KEM_1024_NAME)
} else {
Err(ErrorStack::get())
}
}
/// Encapsulate a key
pub trait PkeyEncapsulateContext {
/// Initialize the encapsulation operation.
///
/// # Errors
/// Returns an error if the OpenSSL operation fails.
fn encapsulate_init(&mut self) -> Result<(), ErrorStack>;
/// Perform the encapsulation operation.
///
/// # Parameters
/// - `wrappedkey`: Optional buffer to receive the wrapped key.
/// - `genkey`: Optional buffer to receive the generated key.
///
/// # Returns
/// A tuple of `(wrappedkey_len, genkey_len)` on success.
///
/// # Errors
/// Returns an error if the OpenSSL operation fails.
fn encapsulate(
&mut self,
wrappedkey: Option<&mut [u8]>,
genkey: Option<&mut [u8]>,
) -> Result<(usize, usize), ErrorStack>;
/// Convenience method to encapsulate into vectors.
///
/// # Parameters
/// - `wrappedkey`: Buffer to receive the wrapped key (ciphertext).
/// - `genkey`: Buffer to receive the generated key (shared secret).
///
/// # Returns
/// A tuple of `(wrappedkey_len, genkey_len)` on success.
///
/// # Errors
/// Returns an error if the OpenSSL operation fails.
fn encapsulate_to_vec(
&mut self,
wrappedkey: &mut Vec<u8>,
genkey: &mut Vec<u8>,
) -> Result<(usize, usize), ErrorStack> {
let wrappedkey_base = wrappedkey.len();
let genkey_base = genkey.len();
// Query the required output buffer sizes.
let (wrappedkey_len, genkey_len) = self.encapsulate(None, None)?;
wrappedkey.resize(wrappedkey_base + wrappedkey_len, 0);
genkey.resize(genkey_base + genkey_len, 0);
let (wrappedkey_len, genkey_len) = self.encapsulate(
Some(&mut wrappedkey[wrappedkey_base..]),
Some(&mut genkey[genkey_base..]),
)?;
wrappedkey.truncate(wrappedkey_base + wrappedkey_len);
genkey.truncate(genkey_base + genkey_len);
Ok((wrappedkey_len, genkey_len))
}
}
impl<T> PkeyEncapsulateContext for PkeyCtx<T> {
#[inline]
fn encapsulate_init(&mut self) -> Result<(), ErrorStack> {
// SAFETY: self.as_ptr() returns a valid EVP_PKEY_CTX pointer. Parameter-based
// configuration is intentionally unsupported here, so a null params pointer is passed.
let ret = unsafe {
ffi::EVP_PKEY_encapsulate_init(self.as_ptr() as *mut ffi::EvpPkeyCtx, std::ptr::null())
};
if ret == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
}
fn encapsulate(
&mut self,
wrappedkey: Option<&mut [u8]>,
genkey: Option<&mut [u8]>,
) -> Result<(usize, usize), ErrorStack> {
let mut wrappedkey_len = wrappedkey.as_ref().map_or(0, |buf| buf.len());
let mut genkey_len = genkey.as_ref().map_or(0, |buf| buf.len());
let wrappedkey_ptr = wrappedkey
.map(|buf| buf.as_mut_ptr())
.unwrap_or(std::ptr::null_mut());
let genkey_ptr = genkey
.map(|buf| buf.as_mut_ptr())
.unwrap_or(std::ptr::null_mut());
// SAFETY: All pointers are either valid mutable buffers or null.
let ret = unsafe {
ffi::EVP_PKEY_encapsulate(
self.as_ptr() as *mut ffi::EvpPkeyCtx,
wrappedkey_ptr,
&mut wrappedkey_len,
genkey_ptr,
&mut genkey_len,
)
};
if ret == 1 {
Ok((wrappedkey_len, genkey_len))
} else {
Err(ErrorStack::get())
}
}
}
/// Decapsulate a key
pub trait PkeyDecapsulateContext {
/// Initialize the decapsulation operation.
///
/// # Errors
/// Returns an error if the OpenSSL operation fails.
fn decapsulate_init(&mut self) -> Result<(), ErrorStack>;
/// Perform the decapsulation operation.
///
/// # Parameters
/// - `wrapped`: The wrapped key (ciphertext) to decapsulate.
/// - `unwrapped`: Optional buffer to receive the unwrapped shared secret.
///
/// # Returns
/// The length of the unwrapped shared secret on success.
///
/// # Errors
/// Returns an error if the OpenSSL operation fails.
fn decapsulate(
&mut self,
wrapped: &[u8],
unwrapped: Option<&mut [u8]>,
) -> Result<usize, ErrorStack>;
/// Convenience method to decapsulate into a vector.
///
/// # Parameters
/// - `wrapped`: The wrapped key (ciphertext) to decapsulate.
/// - `unwrapped`: Buffer to receive the unwrapped shared secret.
///
/// # Returns
/// The length of the unwrapped shared secret on success.
///
/// # Errors
/// Returns an error if the OpenSSL operation fails.
fn decapsulate_to_vec(
&mut self,
wrapped: &[u8],
unwrapped: &mut Vec<u8>,
) -> Result<usize, ErrorStack> {
let unwrapped_base = unwrapped.len();
// Query the required output buffer size.
let unwrapped_len = self.decapsulate(wrapped, None)?;
unwrapped.resize(unwrapped_base + unwrapped_len, 0);
let unwrapped_len = self.decapsulate(wrapped, Some(&mut unwrapped[unwrapped_base..]))?;
unwrapped.truncate(unwrapped_base + unwrapped_len);
Ok(unwrapped_len)
}
}
impl<T> PkeyDecapsulateContext for PkeyCtx<T> {
#[inline]
fn decapsulate_init(&mut self) -> Result<(), ErrorStack> {
// SAFETY: self.as_ptr() returns a valid EVP_PKEY_CTX pointer. Parameter-based
// configuration is intentionally unsupported here, so a null params pointer is passed.
let ret = unsafe {
ffi::EVP_PKEY_decapsulate_init(self.as_ptr() as *mut ffi::EvpPkeyCtx, std::ptr::null())
};
if ret == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
}
fn decapsulate(
&mut self,
wrapped: &[u8],
unwrapped: Option<&mut [u8]>,
) -> Result<usize, ErrorStack> {
let mut unwrapped_len = unwrapped.as_ref().map_or(0, |buf| buf.len());
let unwrapped_ptr = unwrapped
.map(|buf| buf.as_mut_ptr())
.unwrap_or(std::ptr::null_mut());
// SAFETY: All pointers are either valid mutable buffers or null, and wrapped is a valid
// slice.
let ret = unsafe {
ffi::EVP_PKEY_decapsulate(
self.as_ptr() as *mut ffi::EvpPkeyCtx,
unwrapped_ptr,
&mut unwrapped_len,
wrapped.as_ptr(),
wrapped.len(),
)
};
if ret == 1 {
Ok(unwrapped_len)
} else {
Err(ErrorStack::get())
}
}
}
/// ML-KEM key generation context.
///
/// The inner pointer is guaranteed to be non-null for the lifetime of the context.
pub struct MlKemKeyCtx {
ctx: NonNull<ffi::EvpPkeyCtx>,
}
impl MlKemKeyCtx {
/// Create a new ML-KEM key generation context.
///
/// # Parameters
/// - `key_type`: The ML-KEM key type (`KeyType::ML_KEM_512`, `KeyType::ML_KEM_768`, or
/// `KeyType::ML_KEM_1024`).
///
/// # Errors
/// Returns an error if the context cannot be created or the key type is unsupported.
pub fn new(key_type: KeyType) -> Result<Self, ErrorStack> {
let name = ml_kem_name(key_type)?;
// SAFETY: name is a valid null-terminated C string.
let ctx = unsafe {
ffi::EVP_PKEY_CTX_new_from_name(std::ptr::null_mut(), name.as_ptr(), std::ptr::null())
};
let Some(ctx) = NonNull::new(ctx) else {
return Err(ErrorStack::get());
};
// SAFETY: ctx is a valid non-null pointer.
let ret = unsafe { ffi::EVP_PKEY_keygen_init(ctx.as_ptr()) };
if ret != 1 {
// SAFETY: ctx is a valid non-null pointer.
unsafe { ffi::EVP_PKEY_CTX_free(ctx.as_ptr()) };
return Err(ErrorStack::get());
}
Ok(Self { ctx })
}
/// Generate an ML-KEM keypair.
///
/// # Returns
/// A `PKey` containing both the private and public key.
///
/// # Errors
/// Returns an error if key generation fails.
pub fn generate(&mut self) -> Result<PKey<Private>, ErrorStack> {
let mut pkey: *mut std::os::raw::c_void = std::ptr::null_mut();
// SAFETY: self.ctx is valid, and pkey is a valid mutable pointer.
let ret = unsafe { ffi::EVP_PKEY_generate(self.ctx.as_ptr(), &mut pkey) };
if ret != 1 || pkey.is_null() {
return Err(ErrorStack::get());
}
// SAFETY: pkey is a valid EVP_PKEY pointer created by OpenSSL.
unsafe { Ok(PKey::from_ptr(pkey as *mut openssl_sys::EVP_PKEY)) }
}
}
impl Drop for MlKemKeyCtx {
fn drop(&mut self) {
// SAFETY: self.ctx is guaranteed to be non-null and owned by this context.
unsafe {
ffi::EVP_PKEY_CTX_free(self.ctx.as_ptr());
}
}
}
/// Generate an ML-KEM keypair.
///
/// # Parameters
/// - `key_type`: The ML-KEM key type (`KeyType::ML_KEM_512`, `KeyType::ML_KEM_768`, or
/// `KeyType::ML_KEM_1024`).
///
/// # Returns
/// A `PKey` containing both the private and public key.
///
/// # Errors
/// Returns an error if key generation fails.
pub fn generate_ml_kem(key_type: KeyType) -> Result<PKey<Private>, ErrorStack> {
let mut ctx = MlKemKeyCtx::new(key_type)?;
ctx.generate()
}
#[cfg(test)]
mod tests {
use super::*;
fn roundtrip_ml_kem(key_type: KeyType, expected_ciphertext_len: usize) {
let keypair = generate_ml_kem(key_type).expect("Failed to generate ML-KEM keypair");
// Extract public key for encapsulation
let public_key_der = keypair
.public_key_to_der()
.expect("Failed to export public key");
let public_key =
PKey::public_key_from_der(&public_key_der).expect("Failed to import public key");
// Encapsulate: generate ciphertext and shared secret using public key
let mut ctx_enc =
PkeyCtx::new(&public_key).expect("Failed to create encapsulation context");
ctx_enc
.encapsulate_init()
.expect("Failed to initialize encapsulation");
let mut ciphertext = Vec::new();
let mut shared_secret_enc = Vec::new();
ctx_enc
.encapsulate_to_vec(&mut ciphertext, &mut shared_secret_enc)
.expect("Failed to encapsulate");
assert!(!ciphertext.is_empty(), "Ciphertext should not be empty");
assert!(
!shared_secret_enc.is_empty(),
"Shared secret should not be empty"
);
// Decapsulate: recover shared secret from ciphertext using private key
let mut ctx_dec = PkeyCtx::new(&keypair).expect("Failed to create decapsulation context");
ctx_dec
.decapsulate_init()
.expect("Failed to initialize decapsulation");
let mut shared_secret_dec = Vec::new();
ctx_dec
.decapsulate_to_vec(&ciphertext, &mut shared_secret_dec)
.expect("Failed to decapsulate");
// Verify that the shared secrets match
assert_eq!(
shared_secret_enc, shared_secret_dec,
"Shared secrets from encapsulation and decapsulation should match"
);
assert_eq!(ciphertext.len(), expected_ciphertext_len);
assert_eq!(shared_secret_enc.len(), 32);
}
#[test]
fn test_encapsulate_decapsulate_roundtrip_ml_kem_512() {
roundtrip_ml_kem(KeyType::ML_KEM_512, 768);
}
#[test]
fn test_encapsulate_decapsulate_roundtrip_ml_kem_768() {
roundtrip_ml_kem(KeyType::ML_KEM_768, 1088);
}
#[test]
fn test_encapsulate_decapsulate_roundtrip_ml_kem_1024() {
roundtrip_ml_kem(KeyType::ML_KEM_1024, 1568);
}
#[test]
fn test_ml_kem_key_ctx_new_accepts_supported_key_types() {
MlKemKeyCtx::new(KeyType::ML_KEM_512).expect("ML-KEM-512 context creation must succeed");
MlKemKeyCtx::new(KeyType::ML_KEM_768).expect("ML-KEM-768 context creation must succeed");
MlKemKeyCtx::new(KeyType::ML_KEM_1024).expect("ML-KEM-1024 context creation must succeed");
}
#[test]
fn test_ml_kem_key_ctx_new_rejects_unsupported_key_type() {
assert!(
MlKemKeyCtx::new(KeyType::RSA).is_err(),
"Unsupported key type must fail"
);
}
#[test]
fn test_ml_kem_key_ctx_generate_produces_ml_kem_keypair() {
let mut ctx = MlKemKeyCtx::new(KeyType::ML_KEM_768)
.expect("ML-KEM-768 context creation must succeed");
let keypair = ctx
.generate()
.expect("ML-KEM-768 key generation must succeed");
let public_key_der = keypair
.public_key_to_der()
.expect("Generated keypair must export a public key");
let public_key =
PKey::public_key_from_der(&public_key_der).expect("Generated public key must reimport");
let mut ctx_enc =
PkeyCtx::new(&public_key).expect("Failed to create encapsulation context");
ctx_enc
.encapsulate_init()
.expect("Failed to initialize encapsulation");
let mut ciphertext = Vec::new();
let mut shared_secret = Vec::new();
ctx_enc
.encapsulate_to_vec(&mut ciphertext, &mut shared_secret)
.expect("Generated keypair must support encapsulation");
assert_eq!(ciphertext.len(), 1088);
assert_eq!(shared_secret.len(), 32);
}
#[test]
fn test_generate_ml_kem_rejects_unsupported_key_type() {
generate_ml_kem(KeyType::RSA).expect_err("Unsupported key type must fail");
}
#[test]
fn test_encapsulate_requires_init() {
let keypair =
generate_ml_kem(KeyType::ML_KEM_512).expect("Failed to generate ML-KEM-512 keypair");
let public_key_der = keypair
.public_key_to_der()
.expect("Failed to export public key");
let public_key =
PKey::public_key_from_der(&public_key_der).expect("Failed to import public key");
let mut ctx_enc =
PkeyCtx::new(&public_key).expect("Failed to create encapsulation context");
let err = ctx_enc
.encapsulate_to_vec(&mut Vec::new(), &mut Vec::new())
.expect_err("Encapsulation without init must fail");
assert!(
!err.errors().is_empty(),
"OpenSSL should report an error when encapsulate_init was not called"
);
}
#[test]
fn test_decapsulate_requires_init() {
let keypair =
generate_ml_kem(KeyType::ML_KEM_512).expect("Failed to generate ML-KEM-512 keypair");
let mut ctx_dec = PkeyCtx::new(&keypair).expect("Failed to create decapsulation context");
let err = ctx_dec
.decapsulate_to_vec(&[0_u8; 768], &mut Vec::new())
.expect_err("Decapsulation without init must fail");
assert!(
!err.errors().is_empty(),
"OpenSSL should report an error when decapsulate_init was not called"
);
}
#[test]
fn test_decapsulate_rejects_undersized_output_buffer() {
let keypair =
generate_ml_kem(KeyType::ML_KEM_768).expect("Failed to generate ML-KEM-768 keypair");
let public_key_der = keypair
.public_key_to_der()
.expect("Failed to export public key");
let public_key =
PKey::public_key_from_der(&public_key_der).expect("Failed to import public key");
let mut ctx_enc =
PkeyCtx::new(&public_key).expect("Failed to create encapsulation context");
ctx_enc
.encapsulate_init()
.expect("Failed to initialize encapsulation");
let mut ciphertext = Vec::new();
let mut shared_secret = Vec::new();
ctx_enc
.encapsulate_to_vec(&mut ciphertext, &mut shared_secret)
.expect("Failed to encapsulate");
let mut ctx_dec = PkeyCtx::new(&keypair).expect("Failed to create decapsulation context");
ctx_dec
.decapsulate_init()
.expect("Failed to initialize decapsulation");
let mut undersized = [0_u8; 31];
let err = ctx_dec
.decapsulate(&ciphertext, Some(&mut undersized))
.expect_err("Undersized output buffer must fail");
assert!(
!err.errors().is_empty(),
"OpenSSL should report an error for an undersized output buffer"
);
}
}

View File

@@ -8,8 +8,11 @@
mod akid;
mod bio;
mod crl;
mod ml_kem;
mod stackable_crl;
pub use akid::*;
pub use bio::*;
pub use crl::*;
#[expect(unused_imports)]
pub use ml_kem::*;

View File

@@ -2,15 +2,15 @@
//
// Copyright IBM Corp. 2023
use crate::openssl_extensions::bio::BioMemSlice;
use foreign_types::{ForeignType, ForeignTypeRef};
use openssl::{
error::ErrorStack,
stack::Stackable,
x509::{X509Crl, X509CrlRef},
};
use std::ptr;
use foreign_types::{ForeignType, ForeignTypeRef};
use openssl::error::ErrorStack;
use openssl::stack::Stackable;
use openssl::x509::{X509Crl, X509CrlRef};
use crate::openssl_extensions::bio::BioMemSlice;
#[derive(Debug)]
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);

View File

@@ -2,18 +2,19 @@
//
// Copyright IBM Corp. 2024
use crate::Result;
use crate::{openssl_extensions::BioMem, Error};
use std::ffi::{c_char, CString};
use std::fmt::Display;
use openssl::error::ErrorStack;
use pv_core::request::Confidential;
use std::{
ffi::{c_char, CString},
fmt::Display,
};
use crate::openssl_extensions::BioMem;
use crate::{Error, Result};
mod ffi {
use openssl_sys::BIO;
use std::ffi::{c_char, c_int, c_long, c_uchar};
use openssl_sys::BIO;
extern "C" {
pub fn PEM_write_bio(
bio: *mut BIO,
@@ -141,8 +142,8 @@ impl Pem {
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
// 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

View File

@@ -1,692 +0,0 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use std::mem::size_of;
use openssl::{
bn::{BigNum, BigNumContext},
ec::{EcGroup, EcGroupRef, EcKey, EcPointRef},
error::ErrorStack,
hash::{DigestBytes, MessageDigest},
nid::Nid,
pkey::{PKey, PKeyRef, Private, Public},
};
use pv_core::request::{RequestMagic, RequestVersion};
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
use crate::{
assert_size,
crypto::{
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, hash, random_array,
AeadEncryptionResult, SymKey, SymKeyType,
},
misc::to_u32,
request::Confidential,
Error, Result,
};
/// Encrypt a _secret_ using self and a given private key.
pub trait Encrypt {
/// Encrypts `secret` using `self` and `priv_key` the encryption.
///
/// # Returns
/// the encrypted data.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not encrypt the secret.
fn encrypt(&self, secret: &[u8], priv_key: &PKeyRef<Private>) -> Result<Vec<u8>> {
let mut res = Vec::with_capacity(80);
self.encrypt_to(secret, priv_key, &mut res)?;
Ok(res)
}
/// Encrypts `secret` using `self` and `priv_key` the encryption.
/// Appends the encrypted data to `to`
///
/// # Returns
/// The encrypted data.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not encrypt the secret.
fn encrypt_to(
&self,
secret: &[u8],
priv_key: &PKeyRef<Private>,
to: &mut Vec<u8>,
) -> Result<()>;
}
/// Types of Authenticated Data
#[allow(missing_debug_implementations)]
pub enum Aad<'a> {
/// Authenticated Keyslot
Ks(&'a Keyslot),
/// Unchanged authenticated data
Plain(&'a [u8]),
/// Authenticated data that has to be encrypted in beforehand
Encr(&'a dyn Encrypt),
}
/// IBM Z Host key-slot
///
/// Layout in binary format:
/// ```none
/// _______________________________________________________________
/// | Public Host Key Hash (32) |
/// | Wrapped(=Encrypted) Request Protection Key(32) |
/// | Key Slot Tag (16) |
/// |_____________________________________________________________|
/// ```
#[derive(Debug, Clone)]
pub struct Keyslot(PKey<Public>);
impl Keyslot {
/// Size of a host-key hash
pub const PHKH_SIZE: u32 = 0x20;
/// Creates a new Keyslot from the provided public key
pub fn new(hostkey: PKey<Public>) -> Self {
Self(hostkey)
}
}
impl Encrypt for Keyslot {
/// Encrypts the given request protection key `prot_key`.
///
/// The AES256 encryption key is derived from `self` as public key, and `priv_key` as private
/// key.
///
/// # Returns
/// The encrypted Keyslot.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not encrypt the secret.
fn encrypt_to(
&self,
prot_key: &[u8],
priv_key: &PKeyRef<Private>,
to: &mut Vec<u8>,
) -> Result<()> {
let derived_key = derive_aes256_gcm_key(priv_key, &self.0)?;
let mut wrpk_and_kst =
encrypt_aead(&derived_key.into(), &[0; 12], &[], prot_key)?.into_buf();
let phk: EcPubKeyCoord = self.0.as_ref().try_into()?;
to.reserve(80);
to.extend_from_slice(&hash(MessageDigest::sha256(), phk.as_ref())?);
to.append(&mut wrpk_and_kst);
Ok(())
}
}
/// Context used to manage the encryption of requests.
/// Intended to be used by [`Request`] implementations
#[derive(Debug)]
pub struct ReqEncrCtx {
iv: [u8; 12],
priv_key: PKey<Private>,
prot_key: SymKey,
}
impl ReqEncrCtx {
/// Create a new encryption context that uses AES256.
///
/// * `iv` - Initialization vector for the request encryption
/// * `priv_key` - Private key to wrap [`Keyslot`]
/// * `prot_key` - Symmetric key for request encryption. Part of [`Keyslot`]
///
/// If an argument is set to `None` a ranom is generated
///
/// # Errors
///
/// This function will return an error if OpenSSL could not generate a random value.
pub fn new_aes_256<I, P, S>(iv: I, priv_key: P, prot_key: S) -> Result<Self>
where
I: Into<Option<[u8; 12]>>,
P: Into<Option<PKey<Private>>>,
S: Into<Option<SymKey>>,
{
let iv = iv.into().unwrap_or(random_array()?);
let priv_key = priv_key.into().unwrap_or(gen_ec_key(Nid::SECP521R1)?);
let prot_key = prot_key
.into()
.unwrap_or(SymKey::random(SymKeyType::Aes256Gcm)?);
Ok(Self {
iv,
priv_key,
prot_key,
})
}
/// Create a new encryption context with random input values.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not generate a random value.
pub fn random(ket_tp: SymKeyType) -> Result<Self> {
match ket_tp {
SymKeyType::Aes256Gcm => Self::new_aes_256(None, None, None),
SymKeyType::Aes256Xts => Err(Error::NoAeadKey),
}
}
/// Build the authenticated data for a request.
/// # Returns
/// ```none
/// _______________________________________________________________
/// | MAGIC (8) Version Number (4) Size (4)|
/// | IV (12) Reserved (4)|
/// | Reserved (7) Num keyslots (1) Reserved(4) Encr Size (4)|
/// | --------------------------------------------------- |
/// | Request type dependent AAD data |
/// |-------------------------------------------------------------|
/// ```
pub fn build_aad<O>(
&self,
version: RequestVersion,
aad: &Vec<Aad>,
encr_size: usize,
magic: O,
) -> Result<Vec<u8>>
where
O: Into<Option<RequestMagic>>,
{
self.build_aad_impl(version, aad, encr_size, magic.into())
}
/// Concrete implementation for [`ReqEncrCtx::build_aad`].
fn build_aad_impl(
&self,
version: RequestVersion,
aad: &Vec<Aad>,
encr_size: usize,
magic: Option<RequestMagic>,
) -> Result<Vec<u8>> {
let nks = aad.iter().filter(|a| matches!(a, Aad::Ks(_))).count();
let nks: u8 = match nks {
0 => Err(Error::NoHostkey),
n if n > u8::MAX as usize => Err(Error::ManyHostkeys),
n => Ok(n as u8),
}?;
let mut auth_data: Vec<u8> = Vec::with_capacity(2048);
// reserve space for the request header
auth_data.resize(size_of::<RequestHdr>(), 0);
for a in aad {
match a {
Aad::Plain(p) => auth_data.extend_from_slice(p),
Aad::Ks(ks) => {
ks.encrypt_to(self.prot_key.value(), &self.priv_key, &mut auth_data)?
}
Aad::Encr(e) => {
e.encrypt_to(self.prot_key.value(), &self.priv_key, &mut auth_data)?
}
}
}
let rql = to_u32(auth_data.len() + encr_size + 16).ok_or_else(|| {
pv_core::Error::Specification("Configured request size to large".to_string())
})?;
let sea = to_u32(encr_size)
.ok_or_else(|| pv_core::Error::Specification("Encrypted size to large".to_string()))?;
let req_hdr = RequestHdr::new(version, rql, self.iv, nks, sea, magic);
// copy request header to the start of the request
auth_data[..size_of::<RequestHdr>()].copy_from_slice(req_hdr.as_bytes());
Ok(auth_data)
}
/// Get the public coordinates from the private key (Customer private key)
/// # Errors
///
/// This function will return an error if the public key could not be extracted by OpenSSL.
/// Very unlikely.
pub fn key_coords(&self) -> Result<EcPubKeyCoord> {
self.priv_key.as_ref().try_into().map_err(Error::Crypto)
}
/// Encrypt confidential Data with this encryption context and provide a GCM tag.
///
/// * `aad` - additional authentic data
/// * `conf` - data to be encrypted
///
/// # Returns
/// [`Vec<u8>`] with the following content:
/// 1. `aad`
/// 2. `encr(conf)`
/// 3. `aes gcm tag`
///
/// # Errors
///
/// This function will return an error if the data could not be encrypted by OpenSSL.
pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result<AeadEncryptionResult> {
encrypt_aead(&self.prot_key, &self.iv, aad, conf)
}
/// Returns a reference to the request protection key of this [`ReqEncrCtx`].
pub fn prot_key(&self) -> &SymKey {
&self.prot_key
}
}
/// Public key components of an [`openssl::ec::EcKey`] key.
#[repr(C)]
#[derive(Debug, Clone)]
pub struct EcPubKeyCoord([u8; 160]);
impl AsRef<[u8]> for EcPubKeyCoord {
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
const ECDH_PUB_KEY_COORD_POINT_SIZE: usize = 0x50;
impl EcPubKeyCoord {
/// Returns the SHA256 hash of the [`EcPubKeyCoord`].
///
/// If [`EcPubKeyCoord`] was built from a host-key, this value is the public host-key hash.
pub fn sha256(&self) -> Result<DigestBytes> {
hash(MessageDigest::sha256(), self.as_ref())
}
/// Construct a [`EcPubKeyCoord`]
///
/// # Safety
/// This function is marked unsafe, because data not representing two EC points violates the
/// invariant of this struct.
pub unsafe fn from_data(data: [u8; 160]) -> Self {
EcPubKeyCoord(data)
}
}
/// Get the pub ECDH coordinates in the format the Ultravisor expects it:
/// The two coordinates are padded to 80 bytes each.
fn get_pub_ecdh_points(pkey: &EcPointRef, grp: &EcGroupRef) -> Result<[u8; 160], ErrorStack> {
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
let mut bn_ctx = BigNumContext::new()?;
pkey.affine_coordinates(grp, &mut x, &mut y, &mut bn_ctx)?;
let mut coord: Vec<u8> = x.to_vec_padded(ECDH_PUB_KEY_COORD_POINT_SIZE as i32)?;
coord.append(&mut y.to_vec_padded(ECDH_PUB_KEY_COORD_POINT_SIZE as i32)?);
Ok(coord.try_into().unwrap())
}
impl TryFrom<EcPubKeyCoord> for PKey<Public> {
type Error = ErrorStack;
fn try_from(value: EcPubKeyCoord) -> Result<Self, Self::Error> {
let ecdh = value.as_ref();
let grp = EcGroup::from_curve_name(Nid::SECP521R1)?;
let x = BigNum::from_slice(&ecdh[..ECDH_PUB_KEY_COORD_POINT_SIZE])?;
let y = BigNum::from_slice(&ecdh[ECDH_PUB_KEY_COORD_POINT_SIZE..])?;
let ec_key = EcKey::from_public_key_affine_coordinates(&grp, &x, &y)?;
Self::from_ec_key(ec_key)
}
}
macro_rules! ecdh_from {
($type: ty) => {
impl TryFrom<&PKeyRef<$type>> for EcPubKeyCoord {
type Error = ErrorStack;
fn try_from(key: &PKeyRef<$type>) -> Result<Self, Self::Error> {
let k = key.ec_key()?;
k.check_key()?;
let grp = k.group();
let pub_key = k.public_key();
let coord = get_pub_ecdh_points(pub_key, grp)?;
Ok(Self(coord))
}
}
impl TryFrom<PKey<$type>> for EcPubKeyCoord {
type Error = ErrorStack;
fn try_from(key: PKey<$type>) -> Result<Self, Self::Error> {
let key_ref = key.as_ref();
key_ref.try_into()
}
}
};
}
ecdh_from!(Private);
ecdh_from!(Public);
/// Representation of the shared parts of the request header.
/// Used by [`ReqEncrCtx`]
#[repr(C)]
#[derive(Debug, Copy, Clone, IntoBytes, FromBytes, Immutable)]
struct RequestHdr {
magic: [u8; 8],
rqvn: U32<BigEndian>,
rql: U32<BigEndian>,
iv: [u8; 12],
reserved1c: [u8; 4],
reserved20: [u8; 7],
nks: u8,
reserved28: u32,
sea: U32<BigEndian>,
}
assert_size!(RequestHdr, 48);
impl RequestHdr {
fn new(rqvn: u32, rql: u32, iv: [u8; 12], nks: u8, sea: u32, magic: Option<[u8; 8]>) -> Self {
Self {
magic: magic.unwrap_or_default(),
rqvn: rqvn.into(),
rql: rql.into(),
iv,
reserved1c: [0; 4],
reserved20: [0; 7],
nks,
reserved28: 0,
sea: sea.into(),
}
}
}
/// A trait representing a request for the Ultravisor.
///
/// All requests share a few things:
/// * All requests need to be encrypted on a trusted machine
/// * All requests have at least one Hostkeyslot
///
/// The encryption setup is handled by [`ReqEncrCtx`]. Implementers need to pass the data to the
/// `ReqEncrCtx` when implementing `encrypt`. A hostkey should be represented by [`Keyslot`] during
/// encryption.
///
/// An UV request consists of an authenticated area (AAD), an encrypted area (Encr) and a 16 byte
/// tag. The AAD contains a general header and Request type defined data (including Keyslots). It
/// is encrypted with an Request protection key (symmetric). This key is encrypted with a
/// (generated) private key and the public key of the host system (Host key)
/// ```none
/// _______________________________________________________________
/// | MAGIC (8) Version Number (4) Size (4)|
/// | IV (12) Reserved (4)|
/// | Reserved (7) Num keyslots (1) Reserved(4) Encr Size (4)|
/// | --------------------------------------------------- |
/// | Request type dependent AAD data |
/// | ---------------------------------------------------- |
/// | Encrypted (request type dependent) data |
/// | ---------------------------------------------------- |
/// | AES GCM Tag (16) |
/// |_____________________________________________________________|
/// ```
pub trait Request {
/// Encrypt the request into its binary format
///
/// # Errors
///
/// This function will return an error if the encryption fails, the request does not have at
/// least a hostkey, or other implementation dependent contracts are not met.
fn encrypt(&self, ctx: &ReqEncrCtx) -> Result<Vec<u8>>;
/// Add a host-key to this request
///
/// Must be called at least once, otherwise {`Request::encrypt`} will fail
fn add_hostkey(&mut self, hostkey: PKey<Public>);
}
/// A struct to represent some parts of a binary/encrypted request.
#[derive(Debug)]
#[allow(clippy::len_without_is_empty)]
pub(crate) struct BinReqValues<'a> {
iv: &'a [u8],
aad: &'a [u8],
req_dep_aad: &'a [u8],
encr: &'a [u8],
tag: &'a [u8],
version: u32,
len: usize,
}
impl<'a> BinReqValues<'a> {
pub(crate) const TAG_LEN: usize = SymKeyType::AES_256_GCM_TAG_LEN;
/// Get the locations from this request.
///
/// Does minimal sanity test, just tests to prevent panics.
/// `req` may be larger than the actual request.
pub(crate) fn get(req: &'a [u8]) -> Result<Self> {
let (hdr, _) = RequestHdr::read_from_prefix(req).map_err(|_| Error::BinRequestSmall)?;
let rql = hdr.rql.get() as usize;
let sea = hdr.sea.get() as usize;
if rql < req.len() || sea + Self::TAG_LEN > rql {
return Err(Error::BinRequestSmall);
}
let aad_size = rql - sea - Self::TAG_LEN;
if aad_size < size_of::<RequestHdr>() {
return Err(Error::BinRequestSmall);
}
let iv = &req[0x10..0x1c];
let aad = &req[..aad_size];
let req_dep_aad = &req[size_of::<RequestHdr>()..aad_size];
let encr = &req[aad_size..(aad_size + sea)];
let tag = &req[rql - Self::TAG_LEN..];
Ok(Self {
iv,
aad,
req_dep_aad,
encr,
tag,
version: hdr.rqvn.get(),
len: rql,
})
}
/// Returns the version of this [`BinReqValues`].
pub(crate) fn version(&self) -> u32 {
self.version
}
/// Returns the length of this [`BinReqValues`].
pub(crate) fn len(&self) -> usize {
self.len
}
/// Returns the size of the encrypted area
pub(crate) fn sea(&self) -> u32 {
self.encr.len() as u32
}
/// Decrypts the encrypted area with the provided key
pub(crate) fn decrypt(&self, key: &SymKey) -> Result<Confidential<Vec<u8>>> {
let result = decrypt_aead(key, self.iv, self.aad, self.encr, self.tag)?;
Ok(result.into_plain())
}
/// Returns a reference to the request dependent authenticated area of this [`BinReqValues`]
/// already interpreted.
///
/// If target struct is larger than the request depended-AAD None is returned. See
/// [`FromBytes::ref_from_prefix`]
pub(crate) fn req_dep_aad<T>(&self) -> Option<&T>
where
T: FromBytes + Sized + Immutable + KnownLayout,
{
T::ref_from_prefix(self.req_dep_aad).map(|s| s.0).ok()
}
/// Returns a reference to the tag of this [`BinReqValues`].
pub(crate) fn tag(&self) -> &[u8] {
self.tag
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{get_test_asset, request::SymKey, test_utils::*};
static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes();
#[test]
fn encr_build_aad() {
let (cust_key, host_key) = get_test_keys();
let ks = Keyslot::new(host_key);
let ctx = ReqEncrCtx::new_aes_256(
Some([0x11; 12]),
Some(cust_key),
Some(SymKey::Aes256([0x17; 32].into())),
)
.unwrap();
let v = [0x55; 8];
let aad = Aad::Plain(&v);
let aad = ctx
.build_aad(0x200, &vec![aad, Aad::Ks(&ks)], 16, Some(TEST_MAGIC))
.unwrap();
let mut aad_exp = vec![
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // progr
0, 0, 2, 0, // vers
0, 0, 0, 168, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
1, // nks
0, 0, 0, 0, // res
0, 0, 0, 16, // sea
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // aad
];
aad_exp.extend_from_slice(get_test_asset!("exp/keyslot.bin"));
assert_eq!(&aad, &aad_exp);
}
#[test]
fn encr_build_aad_nks_no() {
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let aad = Vec::<Aad>::new();
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::NoHostkey)));
}
#[test]
fn encr_build_aad_nks_many() {
let (_, host_key) = get_test_keys();
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let ks: Vec<Keyslot> = (0..257).map(|_| Keyslot::new(host_key.clone())).collect();
let mut aad = Vec::<Aad>::new();
ks.iter().for_each(|ks| aad.push(Aad::Ks(ks)));
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::ManyHostkeys)));
}
#[test]
fn encr_build_aad_nks() {
let (_, host_key) = get_test_keys();
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let ks = [
Keyslot::new(host_key.clone()),
Keyslot::new(host_key.clone()),
Keyslot::new(host_key),
];
let mut aad = Vec::<Aad>::new();
ks.iter().for_each(|ks| aad.push(Aad::Ks(ks)));
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)).unwrap();
assert_eq!(aad.get(39).unwrap(), &3u8);
}
#[test]
fn req_hdr() {
let hdr = RequestHdr::new(0x200, 22, [0x11; 12], 15, 44, None);
let hdr_bin = hdr.as_bytes();
let hdr_bin_exp = [
0u8, 0, 0, 0, 0, 0, 0, 0, // magic
0, 0, 2, 0, // vers
0, 0, 0, 22, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
15, // nks
0, 0, 0, 0, // res
0, 0, 0, 44, // sea
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
#[test]
fn req_hdr2() {
let mut hdr = RequestHdr::new(0x200, 0x1234, [0x11; 12], 15, 44, Some(TEST_MAGIC));
let hdr_bin = hdr.as_mut_bytes();
let hdr_bin_exp = [
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // magic
0, 0, 2, 0, // vers
0, 0, 0x12, 0x34, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
15, // nks
0, 0, 0, 0, // res
0, 0, 0, 44, // sea
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
#[test]
fn keyslot() {
let (cust_key, host_key) = get_test_keys();
let exp_keyslot = get_test_asset!("exp/keyslot.bin").to_vec();
let keyslot = Keyslot::new(host_key);
let encr_ks = keyslot.encrypt(&[0x17u8; 32], &cust_key).unwrap();
assert_eq!(exp_keyslot, encr_ks);
let encr_ks = keyslot.encrypt(&[0x16u8; 32], &cust_key).unwrap();
assert_ne!(exp_keyslot, encr_ks);
}
#[test]
fn ec_pub_ec_coord_from() {
let (cust_key, _) = get_test_keys();
let pub_key = get_test_asset!("keys/public_cust.bin");
assert_eq!(pub_key.len(), 160);
let ec_coord: EcPubKeyCoord = cust_key.as_ref().try_into().unwrap();
assert_eq!(ec_coord.as_ref(), pub_key);
}
#[test]
fn ec_pub_ec_coord_hash() {
let exp = [
0x5e, 0xe9, 0x05, 0xa9, 0xbe, 0x70, 0x36, 0x68, 0x15, 0xa4, 0x56, 0x41, 0xaf, 0xae,
0x00, 0x97, 0x3b, 0x1f, 0x45, 0x29, 0x2f, 0x43, 0xbc, 0xd7, 0x63, 0x8e, 0xe2, 0xa7,
0x3f, 0xd7, 0xc4, 0x5e,
];
let (cust_key, _) = get_test_keys();
let ec_coord: EcPubKeyCoord = cust_key.as_ref().try_into().unwrap();
let hash = ec_coord.sha256().unwrap();
assert_eq!(hash.as_ref(), &exp);
}
#[test]
fn conversion_ecdh_and_vice_versa() {
let (_, cust_pub) = get_test_keys();
let phk: EcPubKeyCoord = cust_pub.clone().try_into().unwrap();
assert_eq!(
phk.as_ref(),
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 118, 136, 28, 216, 75, 139, 109, 231,
18, 60, 126, 144, 14, 223, 120, 231, 247, 182, 132, 153, 145, 70, 177, 38, 59, 168,
184, 108, 132, 71, 240, 138, 182, 212, 105, 194, 177, 40, 237, 158, 28, 53, 1, 88,
5, 172, 211, 211, 2, 51, 211, 145, 34, 247, 226, 248, 170, 28, 43, 20, 123, 120,
131, 180, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 69, 86, 194, 92,
249, 47, 41, 206, 102, 189, 68, 17, 77, 107, 123, 60, 120, 225, 58, 63, 144, 189,
185, 0, 64, 246, 135, 110, 82, 98, 247, 120, 166, 26, 147, 125, 27, 52, 128, 46,
178, 87, 227, 78, 6, 114, 221, 95, 42, 52, 122, 221, 170, 40, 32, 53, 9, 42, 112,
195, 92, 46, 121, 115
]
);
let cust_pub_back: PKey<Public> = phk.try_into().unwrap();
assert!(cust_pub.public_eq(&cust_pub_back));
}
}

257
rust/pv/src/req/context.rs Normal file
View File

@@ -0,0 +1,257 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! Request encryption context for managing UV request encryption.
use std::mem::size_of;
use openssl::nid::Nid;
use openssl::pkey::{PKey, Private};
use pv_core::request::{RequestMagic, RequestVersion};
use zerocopy::IntoBytes;
use super::{Aad, EcPubKeyCoord, Encrypt, RequestHdr};
use crate::crypto::{
encrypt_aead, gen_ec_key, random_array, AeadEncryptionResult, SymKey, SymKeyType,
};
use crate::misc::to_u32;
use crate::{Error, Result};
/// Context used to manage the encryption of requests.
/// Intended to be used by [`Request`](super::Request) implementations
#[derive(Debug)]
pub struct ReqEncrCtx {
iv: [u8; 12],
priv_key: PKey<Private>,
prot_key: SymKey,
}
impl ReqEncrCtx {
/// Create a new encryption context that uses AES256.
///
/// * `iv` - Initialization vector for the request encryption
/// * `priv_key` - Private key to wrap [`Keyslot`](super::Keyslot)
/// * `prot_key` - Symmetric key for request encryption. Part of [`Keyslot`](super::Keyslot)
///
/// If an argument is set to `None` a random is generated
///
/// # Errors
///
/// This function will return an error if OpenSSL could not generate a random value.
pub fn new_aes_256<I, P, S>(iv: I, priv_key: P, prot_key: S) -> Result<Self>
where
I: Into<Option<[u8; 12]>>,
P: Into<Option<PKey<Private>>>,
S: Into<Option<SymKey>>,
{
let iv = iv.into().unwrap_or(random_array()?);
let priv_key = priv_key.into().unwrap_or(gen_ec_key(Nid::SECP521R1)?);
let prot_key = prot_key
.into()
.unwrap_or(SymKey::random(SymKeyType::Aes256Gcm)?);
Ok(Self {
iv,
priv_key,
prot_key,
})
}
/// Create a new encryption context with random input values.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not generate a random value.
pub fn random(key_tp: SymKeyType) -> Result<Self> {
match key_tp {
SymKeyType::Aes256Gcm => Self::new_aes_256(None, None, None),
SymKeyType::Aes256Xts => Err(Error::NoAeadKey),
}
}
/// Build the authenticated data for a request.
/// # Returns
/// ```none
/// _______________________________________________________________
/// | MAGIC (8) Version Number (4) Size (4)|
/// | IV (12) Reserved (4)|
/// | Reserved (7) Num keyslots (1) Reserved(4) Encr Size (4)|
/// | --------------------------------------------------- |
/// | Request type dependent AAD data |
/// |-------------------------------------------------------------|
/// ```
pub fn build_aad<O>(
&self,
version: RequestVersion,
aad: &Vec<Aad>,
encr_size: usize,
magic: O,
) -> Result<Vec<u8>>
where
O: Into<Option<RequestMagic>>,
{
self.build_aad_impl(version, aad, encr_size, magic.into())
}
/// Concrete implementation for [`ReqEncrCtx::build_aad`].
fn build_aad_impl(
&self,
version: RequestVersion,
aad: &Vec<Aad>,
encr_size: usize,
magic: Option<RequestMagic>,
) -> Result<Vec<u8>> {
let nks = aad.iter().filter(|a| matches!(a, Aad::Ks(_))).count();
let nks: u8 = match nks {
0 => Err(Error::NoHostkey),
n if n > u8::MAX as usize => Err(Error::ManyHostkeys),
n => Ok(n as u8),
}?;
let mut auth_data: Vec<u8> = Vec::with_capacity(2048);
// reserve space for the request header
auth_data.resize(size_of::<RequestHdr>(), 0);
for a in aad {
match a {
Aad::Plain(p) => auth_data.extend_from_slice(p),
Aad::Ks(ks) => {
ks.encrypt_to(self.prot_key.value(), &self.priv_key, &mut auth_data)?
}
Aad::Encr(e) => {
e.encrypt_to(self.prot_key.value(), &self.priv_key, &mut auth_data)?
}
}
}
let rql = to_u32(auth_data.len() + encr_size + 16).ok_or_else(|| {
pv_core::Error::Specification("Configured request size to large".to_string())
})?;
let sea = to_u32(encr_size)
.ok_or_else(|| pv_core::Error::Specification("Encrypted size to large".to_string()))?;
let req_hdr = RequestHdr::new(version, rql, self.iv, nks, sea, magic);
// copy request header to the start of the request
auth_data[..size_of::<RequestHdr>()].copy_from_slice(req_hdr.as_bytes());
Ok(auth_data)
}
/// Get the public coordinates from the private key (Customer private key)
/// # Errors
///
/// This function will return an error if the public key could not be extracted by OpenSSL.
/// Very unlikely.
pub fn key_coords(&self) -> Result<EcPubKeyCoord> {
self.priv_key.as_ref().try_into().map_err(Error::Crypto)
}
/// Encrypt confidential Data with this encryption context and provide a GCM tag.
///
/// * `aad` - additional authentic data
/// * `conf` - data to be encrypted
///
/// # Returns
/// [`Vec<u8>`] with the following content:
/// 1. `aad`
/// 2. `encr(conf)`
/// 3. `aes gcm tag`
///
/// # Errors
///
/// This function will return an error if the data could not be encrypted by OpenSSL.
pub(crate) fn encrypt_aead(&self, aad: &[u8], conf: &[u8]) -> Result<AeadEncryptionResult> {
encrypt_aead(&self.prot_key, &self.iv, aad, conf)
}
/// Returns a reference to the request protection key of this [`ReqEncrCtx`].
pub fn prot_key(&self) -> &SymKey {
&self.prot_key
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::get_test_asset;
use crate::req::hostkey::HostKey;
use crate::req::keyslot::Keyslot;
use crate::request::SymKey;
use crate::test_utils::*;
static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes();
#[test]
fn encr_build_aad() {
let (cust_key, host_key) = get_test_keys();
let ks = Keyslot::new(HostKey::V1(host_key));
let ctx = ReqEncrCtx::new_aes_256(
Some([0x11; 12]),
Some(cust_key),
Some(SymKey::Aes256([0x17; 32].into())),
)
.unwrap();
let v = [0x55; 8];
let aad = Aad::Plain(&v);
let aad = ctx
.build_aad(0x200, &vec![aad, Aad::Ks(&ks)], 16, Some(TEST_MAGIC))
.unwrap();
let mut aad_exp = vec![
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // progr
0, 0, 2, 0, // vers
0, 0, 0, 168, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
1, // nks
0, 0, 0, 0, // res
0, 0, 0, 16, // sea
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // aad
];
aad_exp.extend_from_slice(get_test_asset!("exp/keyslot.bin"));
assert_eq!(&aad, &aad_exp);
}
#[test]
fn encr_build_aad_nks_no() {
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let aad = Vec::<Aad>::new();
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::NoHostkey)));
}
#[test]
fn encr_build_aad_nks_many() {
let (_, host_key) = get_test_keys();
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let ks: Vec<Keyslot> = (0..257)
.map(|_| Keyslot::new(HostKey::V1(host_key.clone())))
.collect();
let mut aad = Vec::<Aad>::new();
ks.iter().for_each(|ks| aad.push(Aad::Ks(ks)));
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::ManyHostkeys)));
}
#[test]
fn encr_build_aad_nks() {
let (_, host_key) = get_test_keys();
let host_key = HostKey::V1(host_key);
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let ks = [
Keyslot::new(host_key.clone()),
Keyslot::new(host_key.clone()),
Keyslot::new(host_key),
];
let mut aad = Vec::<Aad>::new();
ks.iter().for_each(|ks| aad.push(Aad::Ks(ks)));
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)).unwrap();
assert_eq!(aad.get(39).unwrap(), &3u8);
}
}

154
rust/pv/src/req/ec_coord.rs Normal file
View File

@@ -0,0 +1,154 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! EC public key coordinate utilities and conversions.
use openssl::bn::{BigNum, BigNumContext};
use openssl::ec::{EcGroup, EcGroupRef, EcKey, EcPointRef};
use openssl::error::ErrorStack;
use openssl::hash::{DigestBytes, MessageDigest};
use openssl::nid::Nid;
use openssl::pkey::{PKey, PKeyRef, Private, Public};
use crate::crypto::hash;
use crate::Result;
/// Public key components of an [`openssl::ec::EcKey`] key.
#[repr(C)]
#[derive(Debug, Clone)]
pub struct EcPubKeyCoord([u8; 160]);
impl AsRef<[u8]> for EcPubKeyCoord {
fn as_ref(&self) -> &[u8] {
self.0.as_slice()
}
}
const ECDH_PUB_KEY_COORD_POINT_SIZE: usize = 0x50;
impl EcPubKeyCoord {
/// Returns the SHA256 hash of the [`EcPubKeyCoord`].
///
/// If [`EcPubKeyCoord`] was built from a host-key, this value is the public host-key hash.
pub fn sha256(&self) -> Result<DigestBytes> {
hash(MessageDigest::sha256(), self.as_ref())
}
/// Construct a [`EcPubKeyCoord`]
///
/// # Safety
/// This function is marked unsafe, because data not representing two EC points violates the
/// invariant of this struct.
pub unsafe fn from_data(data: [u8; 160]) -> Self {
EcPubKeyCoord(data)
}
}
/// Get the pub ECDH coordinates in the format the Ultravisor expects it:
/// The two coordinates are padded to 80 bytes each.
fn get_pub_ecdh_points(pkey: &EcPointRef, grp: &EcGroupRef) -> Result<[u8; 160], ErrorStack> {
let mut x = BigNum::new()?;
let mut y = BigNum::new()?;
let mut bn_ctx = BigNumContext::new()?;
pkey.affine_coordinates(grp, &mut x, &mut y, &mut bn_ctx)?;
let mut coord: Vec<u8> = x.to_vec_padded(ECDH_PUB_KEY_COORD_POINT_SIZE as i32)?;
coord.append(&mut y.to_vec_padded(ECDH_PUB_KEY_COORD_POINT_SIZE as i32)?);
Ok(coord.try_into().unwrap())
}
impl TryFrom<EcPubKeyCoord> for PKey<Public> {
type Error = ErrorStack;
fn try_from(value: EcPubKeyCoord) -> Result<Self, Self::Error> {
let ecdh = value.as_ref();
let grp = EcGroup::from_curve_name(Nid::SECP521R1)?;
let x = BigNum::from_slice(&ecdh[..ECDH_PUB_KEY_COORD_POINT_SIZE])?;
let y = BigNum::from_slice(&ecdh[ECDH_PUB_KEY_COORD_POINT_SIZE..])?;
let ec_key = EcKey::from_public_key_affine_coordinates(&grp, &x, &y)?;
Self::from_ec_key(ec_key)
}
}
macro_rules! ecdh_from {
($type: ty) => {
impl TryFrom<&PKeyRef<$type>> for EcPubKeyCoord {
type Error = ErrorStack;
fn try_from(key: &PKeyRef<$type>) -> Result<Self, Self::Error> {
let k = key.ec_key()?;
k.check_key()?;
let grp = k.group();
let pub_key = k.public_key();
let coord = get_pub_ecdh_points(pub_key, grp)?;
Ok(Self(coord))
}
}
impl TryFrom<PKey<$type>> for EcPubKeyCoord {
type Error = ErrorStack;
fn try_from(key: PKey<$type>) -> Result<Self, Self::Error> {
let key_ref = key.as_ref();
key_ref.try_into()
}
}
};
}
ecdh_from!(Private);
ecdh_from!(Public);
#[cfg(test)]
mod tests {
use super::*;
use crate::get_test_asset;
use crate::test_utils::*;
#[test]
fn ec_pub_ec_coord_from() {
let (cust_key, _) = get_test_keys();
let pub_key = get_test_asset!("keys/public_cust.bin");
assert_eq!(pub_key.len(), 160);
let ec_coord: EcPubKeyCoord = cust_key.as_ref().try_into().unwrap();
assert_eq!(ec_coord.as_ref(), pub_key);
}
#[test]
fn ec_pub_ec_coord_hash() {
let exp = [
0x5e, 0xe9, 0x05, 0xa9, 0xbe, 0x70, 0x36, 0x68, 0x15, 0xa4, 0x56, 0x41, 0xaf, 0xae,
0x00, 0x97, 0x3b, 0x1f, 0x45, 0x29, 0x2f, 0x43, 0xbc, 0xd7, 0x63, 0x8e, 0xe2, 0xa7,
0x3f, 0xd7, 0xc4, 0x5e,
];
let (cust_key, _) = get_test_keys();
let ec_coord: EcPubKeyCoord = cust_key.as_ref().try_into().unwrap();
let hash = ec_coord.sha256().unwrap();
assert_eq!(hash.as_ref(), &exp);
}
#[test]
fn conversion_ecdh_and_vice_versa() {
let (_, cust_pub) = get_test_keys();
let phk: EcPubKeyCoord = cust_pub.clone().try_into().unwrap();
assert_eq!(
phk.as_ref(),
&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 118, 136, 28, 216, 75, 139, 109, 231,
18, 60, 126, 144, 14, 223, 120, 231, 247, 182, 132, 153, 145, 70, 177, 38, 59, 168,
184, 108, 132, 71, 240, 138, 182, 212, 105, 194, 177, 40, 237, 158, 28, 53, 1, 88,
5, 172, 211, 211, 2, 51, 211, 145, 34, 247, 226, 248, 170, 28, 43, 20, 123, 120,
131, 180, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 195, 69, 86, 194, 92,
249, 47, 41, 206, 102, 189, 68, 17, 77, 107, 123, 60, 120, 225, 58, 63, 144, 189,
185, 0, 64, 246, 135, 110, 82, 98, 247, 120, 166, 26, 147, 125, 27, 52, 128, 46,
178, 87, 227, 78, 6, 114, 221, 95, 42, 52, 122, 221, 170, 40, 32, 53, 9, 42, 112,
195, 92, 46, 121, 115
]
);
let cust_pub_back: PKey<Public> = phk.try_into().unwrap();
assert!(cust_pub.public_eq(&cust_pub_back));
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! Encryption trait and authenticated data types for UV requests.
use openssl::pkey::{PKeyRef, Private};
use crate::req::Keyslot;
use crate::Result;
/// Encrypt a _secret_ using self and a given private key.
pub trait Encrypt {
/// Encrypts `secret` using `self` and `priv_key` the encryption.
///
/// # Returns
/// the encrypted data.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not encrypt the secret.
fn encrypt(&self, secret: &[u8], priv_key: &PKeyRef<Private>) -> Result<Vec<u8>> {
let mut res = Vec::with_capacity(80);
self.encrypt_to(secret, priv_key, &mut res)?;
Ok(res)
}
/// Encrypts `secret` using `self` and `priv_key` the encryption.
/// Appends the encrypted data to `to`
///
/// # Returns
/// The encrypted data.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not encrypt the secret.
fn encrypt_to(
&self,
secret: &[u8],
priv_key: &PKeyRef<Private>,
to: &mut Vec<u8>,
) -> Result<()>;
}
/// Types of Authenticated Data
#[allow(missing_debug_implementations)]
pub enum Aad<'a> {
/// Authenticated Keyslot
Ks(&'a Keyslot),
/// Unchanged authenticated data
Plain(&'a [u8]),
/// Authenticated data that has to be encrypted in beforehand
Encr(&'a dyn Encrypt),
}

91
rust/pv/src/req/header.rs Normal file
View File

@@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! Request header structure for UV requests.
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, U32};
use crate::assert_size;
/// Representation of the shared parts of the request header.
/// Used by [`ReqEncrCtx`](super::ReqEncrCtx)
#[repr(C)]
#[derive(Debug, Copy, Clone, IntoBytes, FromBytes, Immutable)]
pub struct RequestHdr {
magic: [u8; 8],
pub(crate) rqvn: U32<BigEndian>,
pub(crate) rql: U32<BigEndian>,
iv: [u8; 12],
reserved1c: [u8; 4],
reserved20: [u8; 7],
nks: u8,
reserved28: u32,
pub(crate) sea: U32<BigEndian>,
}
assert_size!(RequestHdr, 48);
impl RequestHdr {
pub(crate) fn new(
rqvn: u32,
rql: u32,
iv: [u8; 12],
nks: u8,
sea: u32,
magic: Option<[u8; 8]>,
) -> Self {
Self {
magic: magic.unwrap_or_default(),
rqvn: rqvn.into(),
rql: rql.into(),
iv,
reserved1c: [0; 4],
reserved20: [0; 7],
nks,
reserved28: 0,
sea: sea.into(),
}
}
}
#[cfg(test)]
mod tests {
use zerocopy::IntoBytes;
use super::*;
static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes();
#[test]
fn req_hdr() {
let hdr = RequestHdr::new(0x200, 22, [0x11; 12], 15, 44, None);
let hdr_bin = hdr.as_bytes();
let hdr_bin_exp = [
0u8, 0, 0, 0, 0, 0, 0, 0, // magic
0, 0, 2, 0, // vers
0, 0, 0, 22, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
15, // nks
0, 0, 0, 0, // res
0, 0, 0, 44, // sea
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
#[test]
fn req_hdr2() {
let mut hdr = RequestHdr::new(0x200, 0x1234, [0x11; 12], 15, 44, Some(TEST_MAGIC));
let hdr_bin = hdr.as_mut_bytes();
let hdr_bin_exp = [
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // magic
0, 0, 2, 0, // vers
0, 0, 0x12, 0x34, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
15, // nks
0, 0, 0, 0, // res
0, 0, 0, 44, // sea
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
}

View File

@@ -0,0 +1,30 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! Host key types for UV requests
use openssl::pkey::{PKey, Public};
/// Versioned host keys container
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum HostKey {
/// ECDH public key
V1(PKey<Public>),
}
impl HostKey {
/// Return the ECDH public key
pub fn ec_key(&self) -> &PKey<Public> {
match self {
HostKey::V1(ec_key) => ec_key,
}
}
}
impl AsRef<HostKey> for HostKey {
fn as_ref(&self) -> &HostKey {
self
}
}

150
rust/pv/src/req/keyslot.rs Normal file
View File

@@ -0,0 +1,150 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! IBM Z Host key-slot implementations.
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, PKeyRef, Private, Public};
use super::ec_coord::EcPubKeyCoord;
use super::encrypt::Encrypt;
use crate::crypto::{derive_aes256_gcm_key, encrypt_aead, hash};
use crate::request::HostKey;
use crate::Result;
/// IBM Z Host key-slot
///
/// Layout in binary format:
/// ```none
/// _______________________________________________________________
/// | Public Host Key Hash (32) |
/// | Wrapped(=Encrypted) Request Protection Key(32) |
/// | Key Slot Tag (16) |
/// |_____________________________________________________________|
/// ```
#[derive(Debug, Clone)]
pub struct KeyslotV1(PKey<Public>);
impl KeyslotV1 {
/// Size of a host-key hash
pub const PHKH_SIZE: u32 = 0x20;
/// Size of complete V1 keyslot in bytes
pub const SIZE: usize = 80;
/// Creates a new Keyslot from the provided public key
pub fn new(hostkey: PKey<Public>) -> Self {
Self(hostkey)
}
}
impl Encrypt for KeyslotV1 {
/// Encrypts the given request protection key `prot_key`.
///
/// The AES256 encryption key is derived from `self` as public key, and `priv_key` as private
/// key.
///
/// # Returns
/// The encrypted Keyslot.
///
/// # Errors
///
/// This function will return an error if OpenSSL could not encrypt the secret.
fn encrypt_to(
&self,
prot_key: &[u8],
priv_key: &PKeyRef<Private>,
to: &mut Vec<u8>,
) -> Result<()> {
let derived_key = derive_aes256_gcm_key(priv_key, &self.0)?;
let mut wrpk_and_kst =
encrypt_aead(&derived_key.into(), &[0; 12], &[], prot_key)?.into_buf();
assert_eq!(wrpk_and_kst.len(), 48);
let phk: EcPubKeyCoord = self.0.as_ref().try_into()?;
to.reserve(80);
let hash = hash(MessageDigest::sha256(), phk.as_ref())?;
assert_eq!(hash.len(), 32);
to.extend_from_slice(&hash);
to.append(&mut wrpk_and_kst);
Ok(())
}
}
/// Versioned keyslot container
#[non_exhaustive]
#[derive(Debug, Clone)]
pub enum Keyslot {
/// V1 key-slots with ECDH keys
V1(KeyslotV1),
}
impl Keyslot {
/// Return a keyslot with the same key-type as the given host-key
pub fn new(hostkey: HostKey) -> Self {
match hostkey {
HostKey::V1(key) => Keyslot::V1(KeyslotV1::new(key)),
}
}
/// Return the public host key hash size for the given version of the key-slot in bytes
pub fn phkh_size(&self) -> u32 {
match self {
Keyslot::V1(_) => KeyslotV1::PHKH_SIZE,
}
}
/// Return the size of the key-slot in bytes
pub fn size(&self) -> usize {
match self {
Keyslot::V1(_) => KeyslotV1::SIZE,
}
}
/// Return whether the key-slot uses hybrid keys
pub fn is_hybrid(&self) -> bool {
match self {
Keyslot::V1(_) => false,
}
}
}
impl Encrypt for Keyslot {
fn encrypt_to(
&self,
secret: &[u8],
priv_key: &PKeyRef<Private>,
to: &mut Vec<u8>,
) -> Result<()> {
match self {
Keyslot::V1(ks) => ks.encrypt_to(secret, priv_key, to),
}
}
}
impl From<PKey<Public>> for Keyslot {
fn from(key: PKey<Public>) -> Self {
Keyslot::V1(KeyslotV1::new(key))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::get_test_asset;
use crate::test_utils::*;
#[test]
fn keyslot() {
let (cust_key, host_key) = get_test_keys();
let exp_keyslot = get_test_asset!("exp/keyslot.bin").to_vec();
let keyslot = KeyslotV1(host_key);
let encr_ks = keyslot.encrypt(&[0x17u8; 32], &cust_key).unwrap();
assert_eq!(exp_keyslot, encr_ks);
let encr_ks = keyslot.encrypt(&[0x16u8; 32], &cust_key).unwrap();
assert_ne!(exp_keyslot, encr_ks);
}
}

25
rust/pv/src/req/mod.rs Normal file
View File

@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
//! Request encryption and key management for IBM Z Ultravisor.
//!
//! This module provides functionality for creating and encrypting requests to the
//! IBM Z Ultravisor, including host key management, keyslot encryption, and request
//! context handling.
mod context;
mod ec_coord;
mod encrypt;
mod header;
mod hostkey;
mod keyslot;
mod request;
// Re-export public types
pub use context::ReqEncrCtx;
pub use ec_coord::EcPubKeyCoord;
pub use encrypt::{Aad, Encrypt};
pub use header::RequestHdr;
pub use hostkey::HostKey;
pub use keyslot::{Keyslot, KeyslotV1};
pub use request::{BinReqValues, Request};

279
rust/pv/src/req/request.rs Normal file
View File

@@ -0,0 +1,279 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
use std::mem::size_of;
use zerocopy::{FromBytes, Immutable, KnownLayout};
use super::HostKey;
use crate::crypto::{decrypt_aead, SymKey, SymKeyType};
use crate::req::context::ReqEncrCtx;
use crate::req::header::RequestHdr;
use crate::request::Confidential;
use crate::{Error, Result};
/// A trait representing a request for the Ultravisor.
///
/// All requests share a few things:
/// * All requests need to be encrypted on a trusted machine
/// * All requests have at least one Hostkeyslot
///
/// The encryption setup is handled by [`ReqEncrCtx`]. Implementers need to pass the data to the
/// `ReqEncrCtx` when implementing `encrypt`. A hostkey should be represented by
/// [`Keyslot`](super::Keyslot) during encryption.
///
/// An UV request consists of an authenticated area (AAD), an encrypted area (Encr) and a 16 byte
/// tag. The AAD contains a general header and Request type defined data (including Keyslots). It
/// is encrypted with an Request protection key (symmetric). This key is encrypted with a
/// (generated) private key and the public key of the host system (Host key)
/// ```none
/// _______________________________________________________________
/// | MAGIC (8) Version Number (4) Size (4)|
/// | IV (12) Reserved (4)|
/// | Reserved (7) Num keyslots (1) Reserved(4) Encr Size (4)|
/// | --------------------------------------------------- |
/// | Request type dependent AAD data |
/// | ---------------------------------------------------- |
/// | Encrypted (request type dependent) data |
/// | ---------------------------------------------------- |
/// | AES GCM Tag (16) |
/// |_____________________________________________________________|
/// ```
pub trait Request {
/// Encrypt the request into its binary format
///
/// # Errors
///
/// This function will return an error if the encryption fails, the request does not have at
/// least a hostkey, or other implementation dependent contracts are not met.
fn encrypt(&self, ctx: &ReqEncrCtx) -> Result<Vec<u8>>;
/// Add a host-key to this request
///
/// Must be called at least once, otherwise {`Request::encrypt`} will fail
fn add_hostkey(&mut self, hostkey: HostKey);
}
/// A struct to represent some parts of a binary/encrypted request.
#[derive(Debug)]
#[allow(clippy::len_without_is_empty)]
pub struct BinReqValues<'a> {
iv: &'a [u8],
aad: &'a [u8],
req_dep_aad: &'a [u8],
encr: &'a [u8],
tag: &'a [u8],
version: u32,
len: usize,
}
impl<'a> BinReqValues<'a> {
pub(crate) const TAG_LEN: usize = SymKeyType::AES_256_GCM_TAG_LEN;
/// Get the locations from this request.
///
/// Does minimal sanity test, just tests to prevent panics.
/// `req` may be larger than the actual request.
pub(crate) fn get(req: &'a [u8]) -> Result<Self> {
let (hdr, _) = RequestHdr::read_from_prefix(req).map_err(|_| Error::BinRequestSmall)?;
let rql = hdr.rql.get() as usize;
let sea = hdr.sea.get() as usize;
if req.len() < rql || sea + Self::TAG_LEN > rql {
return Err(Error::BinRequestSmall);
}
let aad_size = rql - sea - Self::TAG_LEN;
if aad_size < size_of::<RequestHdr>() {
return Err(Error::BinRequestSmall);
}
let iv = &req[0x10..0x1c];
let aad = &req[..aad_size];
let req_dep_aad = &req[size_of::<RequestHdr>()..aad_size];
let encr = &req[aad_size..(aad_size + sea)];
let tag = &req[rql - Self::TAG_LEN..];
Ok(Self {
iv,
aad,
req_dep_aad,
encr,
tag,
version: hdr.rqvn.get(),
len: rql,
})
}
/// Returns the version of this [`BinReqValues`].
pub(crate) fn version(&self) -> u32 {
self.version
}
/// Returns the length of this [`BinReqValues`].
pub(crate) fn len(&self) -> usize {
self.len
}
/// Returns the size of the encrypted area
pub(crate) fn sea(&self) -> u32 {
self.encr.len() as u32
}
/// Decrypts the encrypted area with the provided key
pub(crate) fn decrypt(&self, key: &SymKey) -> Result<Confidential<Vec<u8>>> {
let result = decrypt_aead(key, self.iv, self.aad, self.encr, self.tag)?;
Ok(result.into_plain())
}
/// Returns a reference to the request dependent authenticated area of this [`BinReqValues`]
/// already interpreted.
///
/// If target struct is larger than the request depended-AAD None is returned. See
/// [`FromBytes::ref_from_prefix`]
pub(crate) fn req_dep_aad<T>(&self) -> Option<&T>
where
T: FromBytes + Sized + Immutable + KnownLayout,
{
T::ref_from_prefix(self.req_dep_aad).map(|s| s.0).ok()
}
/// Returns a reference to the tag of this [`BinReqValues`].
pub(crate) fn tag(&self) -> &[u8] {
self.tag
}
}
#[cfg(test)]
mod tests {
use zerocopy::IntoBytes;
use super::*;
use crate::get_test_asset;
use crate::req::header::RequestHdr;
use crate::req::{Aad, HostKey, Keyslot, ReqEncrCtx};
use crate::request::SymKey;
use crate::test_utils::*;
static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes();
#[test]
fn encr_build_aad() {
let (cust_key, host_key) = get_test_keys();
let ks = Keyslot::new(HostKey::V1(host_key));
let ctx = ReqEncrCtx::new_aes_256(
Some([0x11; 12]),
Some(cust_key),
Some(SymKey::Aes256([0x17; 32].into())),
)
.unwrap();
let v = [0x55; 8];
let aad = Aad::Plain(&v);
let aad = ctx
.build_aad(0x200, &vec![aad, Aad::Ks(&ks)], 16, Some(TEST_MAGIC))
.unwrap();
let mut aad_exp = vec![
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // progr
0, 0, 2, 0, // vers
0, 0, 0, 168, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
1, // nks
0, 0, 0, 0, // res
0, 0, 0, 16, // sea
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // aad
];
aad_exp.extend_from_slice(get_test_asset!("exp/keyslot.bin"));
assert_eq!(&aad, &aad_exp);
}
#[test]
fn encr_build_aad_nks_no() {
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let aad = Vec::<Aad>::new();
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::NoHostkey)));
}
#[test]
fn encr_build_aad_nks_many() {
let (_, host_key) = get_test_keys();
let host_key = HostKey::V1(host_key);
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let ks: Vec<Keyslot> = (0..257).map(|_| Keyslot::new(host_key.clone())).collect();
let mut aad = Vec::<Aad>::new();
ks.iter().for_each(|ks| aad.push(Aad::Ks(ks)));
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC));
assert!(matches!(aad, Err(Error::ManyHostkeys)));
}
#[test]
fn encr_build_aad_nks() {
let (_, host_key) = get_test_keys();
let host_key = HostKey::V1(host_key);
let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap();
let ks = [
Keyslot::new(host_key.clone()),
Keyslot::new(host_key.clone()),
Keyslot::new(host_key),
];
let mut aad = Vec::<Aad>::new();
ks.iter().for_each(|ks| aad.push(Aad::Ks(ks)));
let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)).unwrap();
assert_eq!(aad.get(39).unwrap(), &3u8);
}
#[test]
fn req_hdr() {
let hdr = RequestHdr::new(0x200, 22, [0x11; 12], 15, 44, None);
let hdr_bin = hdr.as_bytes();
let hdr_bin_exp = [
0u8, 0, 0, 0, 0, 0, 0, 0, // magic
0, 0, 2, 0, // vers
0, 0, 0, 22, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
15, // nks
0, 0, 0, 0, // res
0, 0, 0, 44, // sea
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
#[test]
fn req_hdr2() {
let mut hdr = RequestHdr::new(0x200, 0x1234, [0x11; 12], 15, 44, Some(TEST_MAGIC));
let hdr_bin = hdr.as_mut_bytes();
let hdr_bin_exp = [
0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // magic
0, 0, 2, 0, // vers
0, 0, 0x12, 0x34, // size
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res
15, // nks
0, 0, 0, 0, // res
0, 0, 0, 44, // sea
];
assert_eq!(hdr_bin, &hdr_bin_exp);
}
#[test]
fn bin_req_values_buffer_too_small() {
// Create a valid header with request length set to 200 bytes
let hdr = RequestHdr::new(0x200, 200, [0x11; 12], 1, 32, Some(TEST_MAGIC));
let hdr_bin = hdr.as_bytes();
// Create a buffer that's smaller than the declared request length (48
// bytes)
let small_buffer = Vec::from(hdr_bin);
let result = BinReqValues::get(&small_buffer);
assert!(matches!(result, Err(Error::BinRequestSmall)));
}
}

View File

@@ -1,22 +1,21 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
// Copyright IBM Corp.
// DO NOT USE ANY OF THESE ITEMS IN PRODUCTION CODE
// USED FOR INTERNAL UNIT AND FVT TESTING ONLY!!!
use std::{
fs,
path::{Path, PathBuf},
};
use std::ffi::c_void;
use std::fs;
use std::mem::{size_of, ManuallyDrop};
use std::path::{Path, PathBuf};
use std::ptr::NonNull;
use openssl::{
bn::BigNum,
ec::{EcGroup, EcKey},
error::ErrorStack,
nid::Nid,
pkey::{PKey, Private, Public},
x509::{X509Crl, X509},
};
use openssl::bn::BigNum;
use openssl::ec::{EcGroup, EcKey};
use openssl::error::ErrorStack;
use openssl::nid::Nid;
use openssl::pkey::{PKey, Private, Public};
use openssl::x509::{X509Crl, X509};
/// TEST ONLY! Loads the specified asset into the binary at compile time.
///
@@ -122,3 +121,379 @@ fn get_keypair(pub_coords: &[u8], priv_num: &[u8]) -> Result<PKey<Private>, Erro
key.check_key()?;
PKey::from_ec_key(key)
}
// To regenerate these bindings, run:
// bindgen bindgen_wrapper.h -o bindgen_output.rs \
// --allowlist-function "RAND_get0_public" \
// --allowlist-function "RAND_get0_private" --allowlist-function "EVP_RAND_fetch" \
// --allowlist-function "EVP_RAND_free" --allowlist-function "EVP_RAND_CTX_new" \
// --allowlist-function "EVP_RAND_CTX_free" --allowlist-function "EVP_RAND_CTX_up_ref" \
// --allowlist-function "EVP_RAND_instantiate" --allowlist-function "RAND_set0_public" \
// --allowlist-function "RAND_set0_private" --allowlist-type "OSSL_PARAM"
// where bindgen_wrapper.h contains:
// #include <openssl/provider.h>
// #include <openssl/rand.h>
// #include <openssl/evp.h>
// #include <openssl/params.h>
mod ffi {
use std::ffi::{c_char, c_int, c_uchar, c_uint, c_void};
#[repr(C)]
pub struct OsslParam {
pub key: *const c_char,
pub data_type: c_uint,
pub data: *mut c_void,
pub data_size: usize,
pub return_size: usize,
}
pub enum OsslLibCtx {}
pub enum OsslProvider {}
pub enum EvpRand {}
pub enum EvpRandCtx {}
unsafe extern "C" {
pub fn RAND_get0_public(ctx: *mut OsslLibCtx) -> *mut EvpRandCtx;
pub fn RAND_get0_private(ctx: *mut OsslLibCtx) -> *mut EvpRandCtx;
pub fn EVP_RAND_fetch(
libctx: *mut OsslLibCtx,
algorithm: *const c_char,
properties: *const c_char,
) -> *mut EvpRand;
pub fn EVP_RAND_free(rand: *mut EvpRand);
pub fn EVP_RAND_CTX_new(rand: *mut EvpRand, parent: *mut EvpRandCtx) -> *mut EvpRandCtx;
pub fn EVP_RAND_CTX_free(ctx: *mut EvpRandCtx);
pub fn EVP_RAND_CTX_up_ref(ctx: *mut EvpRandCtx) -> c_int;
pub fn EVP_RAND_instantiate(
ctx: *mut EvpRandCtx,
strength: c_uint,
prediction_resistance: c_int,
pstr: *const c_uchar,
pstr_len: usize,
params: *const OsslParam,
) -> c_int;
pub fn RAND_set0_public(ctx: *mut OsslLibCtx, rand: *mut EvpRandCtx) -> c_int;
pub fn RAND_set0_private(ctx: *mut OsslLibCtx, rand: *mut EvpRandCtx) -> c_int;
}
}
// Constants for OSSL_PARAM construction
const OSSL_PARAM_OCTET_STRING: u32 = 5;
const OSSL_PARAM_UNSIGNED_INTEGER: u32 = 2;
const OSSL_PARAM_END: u32 = 0;
fn ossl_param_end() -> ffi::OsslParam {
ffi::OsslParam {
key: std::ptr::null(),
data_type: OSSL_PARAM_END,
data: std::ptr::null_mut(),
data_size: 0,
return_size: 0,
}
}
fn ossl_param_octet_string(name: &'static [u8], data: &mut [u8]) -> ffi::OsslParam {
// SAFETY: Constructing OSSL_PARAM for octet string.
// - name is a static null-terminated C string, valid for 'static
// - data is a valid mutable slice, pointer remains valid during param usage
// - Pointer casts are safe as they preserve alignment and validity
ffi::OsslParam {
key: name.as_ptr().cast(),
data_type: OSSL_PARAM_OCTET_STRING,
data: data.as_mut_ptr().cast(),
data_size: data.len(),
return_size: data.len(),
}
}
fn ossl_param_uint(name: &'static [u8], value: &mut u32) -> ffi::OsslParam {
// SAFETY: Constructing OSSL_PARAM for unsigned integer.
// - name is a static null-terminated C string, valid for 'static
// - value is a valid mutable reference, pointer remains valid during param usage
// - Pointer cast to c_void is safe as it preserves alignment and validity
ffi::OsslParam {
key: name.as_ptr().cast(),
data_type: OSSL_PARAM_UNSIGNED_INTEGER,
data: (value as *mut u32).cast::<c_void>(),
data_size: size_of::<u32>(),
return_size: size_of::<u32>(),
}
}
#[derive(Debug)]
struct FetchedRand(NonNull<ffi::EvpRand>);
impl FetchedRand {
const TEST_RAND_NAME: &'static [u8] = b"TEST-RAND\0";
fn fetch_test_rand() -> Result<Self, ErrorStack> {
// SAFETY: Calling OpenSSL C API with valid parameters.
// - null_mut() is valid for optional OSSL_LIB_CTX parameter
// - Self::TEST_RAND_NAME is a valid null-terminated C string
// - null() is valid for optional properties parameter
// - Returns null on error, which we handle via NonNull::new
let rand = unsafe {
ffi::EVP_RAND_fetch(
std::ptr::null_mut(),
Self::TEST_RAND_NAME.as_ptr().cast(),
std::ptr::null(),
)
};
NonNull::new(rand).map(Self).ok_or_else(ErrorStack::get)
}
fn as_ptr(&self) -> *mut ffi::EvpRand {
self.0.as_ptr()
}
}
impl Drop for FetchedRand {
fn drop(&mut self) {
// SAFETY: self.0 is a valid non-null EVP_RAND pointer that we own.
// This is the only place we call free, preventing double-free.
unsafe {
ffi::EVP_RAND_free(self.0.as_ptr());
}
}
}
#[derive(Debug)]
struct RandCtx(NonNull<ffi::EvpRandCtx>);
impl RandCtx {
fn new(rand: &FetchedRand) -> Result<Self, ErrorStack> {
// SAFETY: Calling OpenSSL C API with valid parameters.
// - rand.as_ptr() is a valid non-null EVP_RAND pointer
// - null_mut() is valid for optional parent parameter
// - Returns null on error, which we handle via NonNull::new
let ctx = unsafe { ffi::EVP_RAND_CTX_new(rand.as_ptr(), std::ptr::null_mut()) };
NonNull::new(ctx).map(Self).ok_or_else(ErrorStack::get)
}
fn up_ref(ptr: *mut ffi::EvpRandCtx) -> Result<Self, ErrorStack> {
let ptr = NonNull::new(ptr).ok_or_else(ErrorStack::get)?;
// SAFETY: ptr is a valid non-null EVP_RAND_CTX pointer.
// EVP_RAND_CTX_up_ref increments the reference count.
// Returns 1 on success, 0 on failure.
let rc = unsafe { ffi::EVP_RAND_CTX_up_ref(ptr.as_ptr()) };
if rc == 1 {
Ok(Self(ptr))
} else {
Err(ErrorStack::get())
}
}
fn current_public() -> Result<Option<Self>, ErrorStack> {
// SAFETY: RAND_get0_public returns a borrowed pointer (no ownership transfer).
// Returns null if no public RNG is set, which we handle.
let ptr = unsafe { ffi::RAND_get0_public(std::ptr::null_mut()) };
if ptr.is_null() {
Ok(None)
} else {
Self::up_ref(ptr).map(Some)
}
}
fn current_private() -> Result<Option<Self>, ErrorStack> {
// SAFETY: RAND_get0_private returns a borrowed pointer (no ownership transfer).
// Returns null if no private RNG is set, which we handle.
let ptr = unsafe { ffi::RAND_get0_private(std::ptr::null_mut()) };
if ptr.is_null() {
Ok(None)
} else {
Self::up_ref(ptr).map(Some)
}
}
fn as_ptr(&self) -> *mut ffi::EvpRandCtx {
self.0.as_ptr()
}
fn instantiate_test_rand(&self, entropy: &[u8], nonce: &[u8]) -> Result<(), ErrorStack> {
// See https://docs.openssl.org/3.1/man7/EVP_RAND-TEST-RAND/#description
// for the available parameters.
const TEST_ENTROPY_PARAM: &[u8] = b"test_entropy\0";
const TEST_NONCE_PARAM: &[u8] = b"test_nonce\0";
const STRENGTH_PARAM: &[u8] = b"strength\0";
let mut entropy = entropy.to_vec();
let mut nonce = nonce.to_vec();
let mut strength = 256u32;
let params = [
ossl_param_uint(STRENGTH_PARAM, &mut strength),
ossl_param_octet_string(TEST_ENTROPY_PARAM, &mut entropy),
ossl_param_octet_string(TEST_NONCE_PARAM, &mut nonce),
ossl_param_end(),
];
// SAFETY: Calling OpenSSL C API with valid parameters.
// - self.as_ptr() is a valid non-null EVP_RAND_CTX pointer
// - strength is a valid u32 value
// - prediction_resistance=0 is valid
// - pstr=null and pstr_len=0 indicate no personalization string
// - params points to a valid array of OSSL_PARAM with proper terminator
// - All mutable references in params remain valid for the call duration
let rc = unsafe {
ffi::EVP_RAND_instantiate(
self.as_ptr(),
strength,
0,
std::ptr::null(),
0,
params.as_ptr(),
)
};
if rc == 1 {
Ok(())
} else {
Err(ErrorStack::get())
}
}
fn install_as_public(self) -> Result<InstalledRandCtx, ErrorStack> {
// SAFETY: Calling OpenSSL C API to transfer ownership.
// - self.as_ptr() is a valid non-null EVP_RAND_CTX pointer
// - RAND_set0_public takes ownership of the context on success (rc==1)
// - We wrap in ManuallyDrop to prevent double-free since OpenSSL now owns it
let rc = unsafe { ffi::RAND_set0_public(std::ptr::null_mut(), self.as_ptr()) };
if rc == 1 {
Ok(InstalledRandCtx(ManuallyDrop::new(self)))
} else {
Err(ErrorStack::get())
}
}
fn install_as_private(self) -> Result<InstalledRandCtx, ErrorStack> {
// SAFETY: Calling OpenSSL C API to transfer ownership.
// - self.as_ptr() is a valid non-null EVP_RAND_CTX pointer
// - RAND_set0_private takes ownership of the context on success (rc==1)
// - We wrap in ManuallyDrop to prevent double-free since OpenSSL now owns it
let rc = unsafe { ffi::RAND_set0_private(std::ptr::null_mut(), self.as_ptr()) };
if rc == 1 {
Ok(InstalledRandCtx(ManuallyDrop::new(self)))
} else {
Err(ErrorStack::get())
}
}
}
impl Drop for RandCtx {
fn drop(&mut self) {
// SAFETY: self.0 is a valid non-null EVP_RAND_CTX pointer that we own.
// This is only called when ownership was NOT transferred to OpenSSL.
// InstalledRandCtx uses ManuallyDrop to prevent this from running after transfer.
unsafe {
ffi::EVP_RAND_CTX_free(self.0.as_ptr());
}
}
}
#[derive(Debug)]
struct InstalledRandCtx(ManuallyDrop<RandCtx>);
impl Drop for InstalledRandCtx {
fn drop(&mut self) {
// SAFETY: Ownership of the EVP_RAND_CTX was transferred to OpenSSL
// via RAND_set0_public/private, so we must not call EVP_RAND_CTX_free.
// ManuallyDrop prevents RandCtx::drop from running automatically.
}
}
#[derive(Debug)]
struct PreviousRandCtx(Option<RandCtx>);
impl PreviousRandCtx {
fn capture_public() -> Result<Self, ErrorStack> {
RandCtx::current_public().map(Self)
}
fn capture_private() -> Result<Self, ErrorStack> {
RandCtx::current_private().map(Self)
}
fn restore_public(&mut self) {
let Some(ctx) = self.0.take() else {
return;
};
// SAFETY: Restoring previously captured RNG context.
// - ctx.as_ptr() is a valid non-null EVP_RAND_CTX pointer
// - RAND_set0_public takes ownership of the context
// - We forget ctx to prevent double-free since OpenSSL now owns it
// - Ignoring return value as restoration is best-effort during cleanup
let _ = unsafe { ffi::RAND_set0_public(std::ptr::null_mut(), ctx.as_ptr()) };
std::mem::forget(ctx);
}
fn restore_private(&mut self) {
let Some(ctx) = self.0.take() else {
return;
};
// SAFETY: Restoring previously captured RNG context.
// - ctx.as_ptr() is a valid non-null EVP_RAND_CTX pointer
// - RAND_set0_private takes ownership of the context
// - We forget ctx to prevent double-free since OpenSSL now owns it
// - Ignoring return value as restoration is best-effort during cleanup
let _ = unsafe { ffi::RAND_set0_private(std::ptr::null_mut(), ctx.as_ptr()) };
std::mem::forget(ctx);
}
}
#[derive(Debug)]
pub struct DeterministicTestRandGuard {
previous_public: PreviousRandCtx,
previous_private: PreviousRandCtx,
_public: InstalledRandCtx,
_private: InstalledRandCtx,
}
impl DeterministicTestRandGuard {
/// Install OpenSSL >= 3 TEST-RAND as the thread-local public/private RNG for deterministic
/// tests.
///
/// The supplied entropy is consumed across generate calls. The nonce is replayed for each
/// nonce request. Per OpenSSL documentation, the public and private DRBG instances are
/// thread-local, so each thread can safely install its own deterministic RNG without
/// affecting other threads.
///
/// # Thread Safety
///
/// From OpenSSL documentation (RAND_get0_primary(3)):
/// > "The public and private DRBG are thread-local instances, which are used by
/// > RAND_bytes() and RAND_priv_bytes(), respectively."
///
/// Reference: <https://docs.openssl.org/3.1/man3/RAND_get0_primary/>
///
/// **Note:** RAND_set0_public() and RAND_set0_private() require OpenSSL >= 3.1.
///
/// # Errors
///
/// Returns an OpenSSL error if the TEST-RAND provider cannot be configured.
pub fn install(entropy: &[u8], nonce: &[u8]) -> Result<Self, ErrorStack> {
let previous_public = PreviousRandCtx::capture_public()?;
let previous_private = PreviousRandCtx::capture_private()?;
let rand = FetchedRand::fetch_test_rand()?;
let public = RandCtx::new(&rand)?;
public.instantiate_test_rand(entropy, nonce)?;
let public = public.install_as_public()?;
let private = RandCtx::new(&rand)?;
private.instantiate_test_rand(entropy, nonce)?;
let private = private.install_as_private()?;
Ok(Self {
previous_public,
previous_private,
_public: public,
_private: private,
})
}
}
impl Drop for DeterministicTestRandGuard {
fn drop(&mut self) {
self.previous_private.restore_private();
self.previous_public.restore_public();
}
}

View File

@@ -1,11 +1,10 @@
// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp. 2023
use openssl::error::ErrorStack;
use openssl::x509::{X509Crl, X509};
use crate::{Error, Result};
use openssl::{
error::ErrorStack,
x509::{X509Crl, X509},
};
/// Read all CRLs from the buffer and parse them into a vector.
///

View File

@@ -2,18 +2,17 @@
//
// Copyright IBM Corp. 2024
use serde::Serialize;
use std::fmt::Display;
use crate::req::Keyslot;
use crate::static_assert;
use crate::{Error, Result};
use serde::Serialize;
use super::arcb::AttestationFlags;
use crate::req::KeyslotV1;
use crate::{static_assert, Error, Result};
/// Hash for additional-data stuff used for parsing [`AdditionalData`]
pub(super) const PHKH_SIZE: u32 = 0x20;
static_assert!(Keyslot::PHKH_SIZE == PHKH_SIZE);
static_assert!(KeyslotV1::PHKH_SIZE == PHKH_SIZE);
pub(super) const SECRET_STORE_HASH_SIZE: u32 = 0x40;
pub(super) const FW_STATE_SIZE: u32 = 0x140;

View File

@@ -2,27 +2,19 @@
//
// Copyright IBM Corp. 2024
use openssl::pkey::{PKey, Public};
use std::mem::size_of;
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
use crate::{
assert_size,
attest::{AttestationMagic, AttestationMeasAlg},
crypto::random_array,
misc::Flags,
req::{Aad, BinReqValues, Keyslot, ReqEncrCtx},
request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize},
static_assert,
uv::UvFlags,
Error, Result,
};
use super::{
additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE},
AttNonce,
};
use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE};
use super::AttNonce;
use crate::attest::{AttestationMagic, AttestationMeasAlg};
use crate::crypto::random_array;
use crate::misc::Flags;
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize};
use crate::uv::UvFlags;
use crate::{assert_size, static_assert, Error, Result};
#[cfg(doc)]
use crate::{
request::SymKeyType,
@@ -59,7 +51,7 @@ use crate::{
///
/// ```rust,no_run
/// # use s390_pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion};
/// # use s390_pv::request::{SymKeyType, Request, ReqEncrCtx};
/// # use s390_pv::request::{SymKeyType, Request, ReqEncrCtx, HostKey};
/// # fn main() -> s390_pv::Result<()> {
/// let att_version = AttestationVersion::One;
/// let meas_alg = AttestationMeasAlg::HmacSha512;
@@ -68,11 +60,11 @@ use crate::{
/// let hkd = s390_pv::misc::read_certs(&std::fs::read("host-key-document.crt")?)?;
/// // IBM issued HKD certificates typically have one X509
/// let hkd = hkd.first().unwrap().public_key()?;
/// arcb.add_hostkey(hkd);
/// arcb.add_hostkey(HostKey::V1(hkd));
/// // you can add multiple hostkeys
/// // arcb.add_hostkey(another_hkd);
/// // encrypt it
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256)?;
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?;
/// let arcb = arcb.encrypt(&ctx)?;
/// # Ok(())
/// # }
@@ -243,7 +235,7 @@ impl Request for AttestationRequest {
ctx.encrypt_aead(&aad, conf).map(|res| res.into_buf())
}
fn add_hostkey(&mut self, hostkey: PKey<Public>) {
fn add_hostkey(&mut self, hostkey: HostKey) {
self.keyslots.push(Keyslot::new(hostkey))
}
}
@@ -419,8 +411,9 @@ impl Zeroize for ReqConfData {
#[cfg(test)]
mod test {
use super::*;
use crate::{get_test_asset, request::SymKey, test_utils::get_test_keys};
use crate::get_test_asset;
use crate::request::SymKey;
use crate::test_utils::get_test_keys;
const ARPK: [u8; 32] = [0x17; 32];
const NONCE: [u8; 16] = [0xab; 16];
@@ -450,7 +443,7 @@ mod test {
arcb.conf.value_mut().nonce = NONCE;
arcb.conf.value_mut().meas_key = MEAS;
arcb.add_hostkey(host_key);
arcb.add_hostkey(HostKey::V1(host_key));
arcb.encrypt(&ctx).unwrap()
}

View File

@@ -2,20 +2,21 @@
//
// Copyright IBM Corp. 2024
use super::AttNonce;
use crate::{
attest::AttestationMeasAlg, brcb::BootHdrTags, crypto::calculate_hmac, request::Confidential,
uv::ConfigUid, Result,
};
use openssl::{
hash::MessageDigest,
pkey::{PKeyRef, Private},
};
use std::mem::size_of;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKeyRef, Private};
use zerocopy::{BigEndian, IntoBytes, U16, U32};
use super::AttNonce;
use crate::attest::AttestationMeasAlg;
#[cfg(doc)]
use crate::attest::AttestationRequest;
use crate::brcb::BootHdrTags;
use crate::crypto::calculate_hmac;
use crate::request::Confidential;
use crate::uv::ConfigUid;
use crate::Result;
/// Holds the data to be measured.
///
@@ -151,9 +152,10 @@ impl From<Vec<u8>> for AttestationMeasurement {
#[cfg(test)]
mod test {
use super::*;
use openssl::pkey::PKey;
use super::*;
const M_KEY: [u8; 64] = [0x41; 64];
const BOOT_HDR_TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]);
const CUID: [u8; 16] = [5; 16];

View File

@@ -2,24 +2,23 @@
//
// Copyright IBM Corp. 2023
use super::{guest_secret::ListableSecretHdr, user_data::UserData};
use crate::{
assert_size,
crypto::{hkdf_rfc_5869, AeadEncryptionResult},
misc::Flags,
req::{Aad, BinReqValues, Keyslot, ReqEncrCtx},
request::{BootHdrTags, Confidential, Request},
secret::{ExtSecret, GuestSecret},
uv::{ConfigUid, UvFlags},
Result,
};
use openssl::{
md::Md,
pkey::{PKey, Private, Public},
};
use pv_core::{request::RequestVersion, secret::AddSecretMagic, uv::SecretId};
use openssl::md::Md;
use openssl::pkey::{PKey, Private};
use pv_core::request::RequestVersion;
use pv_core::secret::AddSecretMagic;
use pv_core::uv::SecretId;
use zerocopy::{Immutable, IntoBytes};
use super::guest_secret::ListableSecretHdr;
use super::user_data::UserData;
use crate::crypto::{hkdf_rfc_5869, AeadEncryptionResult};
use crate::misc::Flags;
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
use crate::request::{BootHdrTags, Confidential, Request};
use crate::secret::{ExtSecret, GuestSecret};
use crate::uv::{ConfigUid, UvFlags};
use crate::{assert_size, Result};
/// Authenticated data w/o user data
#[repr(C)]
#[derive(Debug, Clone, Copy, IntoBytes, Immutable)]
@@ -310,7 +309,7 @@ impl Request for AddSecretRequest {
}
}
fn add_hostkey(&mut self, hostkey: PKey<Public>) {
fn add_hostkey(&mut self, hostkey: HostKey) {
self.keyslots.push(Keyslot::new(hostkey))
}
}

View File

@@ -2,31 +2,25 @@
//
// Copyright IBM Corp. 2023
#[allow(unused_imports)] // used for more convenient docstring
use super::asrcb::AddSecretRequest;
use crate::{
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 openssl::{
hash::MessageDigest,
nid::Nid,
pkey::{Id, PKey, PKeyRef, Private},
};
use std::fmt::Display;
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use openssl::pkey::{Id, PKey, PKeyRef, Private};
use pv_core::static_assert;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use zerocopy::{BigEndian, KnownLayout};
use zerocopy::{FromBytes, Immutable, IntoBytes, U16, U32};
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U16, U32};
#[allow(unused_imports)] // used for more convenient docstring
use super::asrcb::AddSecretRequest;
use crate::crypto::{hash, random_array, SymKeyType};
use crate::request::openssl::{NID_ED25519, NID_ED448};
use crate::request::Confidential;
use crate::uv::{
AesSizes, AesXtsSizes, EcCurves, HmacShaSizes, ListableSecretType, RetrievableSecret,
RetrieveCmd, SecretId,
};
use crate::{assert_size, Error, Result};
const ASSOC_SECRET_SIZE: usize = 32;
const CCK_SIZE: usize = 32;
@@ -476,13 +470,13 @@ impl UpdateCckHdr {
#[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};
use super::RetrievableSecret::*;
use super::{HmacShaSizes as HmacSizes, *};
#[test]
fn association() {
let secret_value = [0x11; 32];

View File

@@ -2,15 +2,15 @@
//
// Copyright IBM Corp. 2024
use crate::{crypto::SymKeyType, pem::Pem, uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD, Result};
use log::warn;
use pv_core::{
request::Confidential,
uv::{ListableSecretType, RetrievableSecret, RetrieveCmd},
};
use zerocopy::BigEndian;
use zerocopy::{FromBytes, U16};
use pv_core::request::Confidential;
use pv_core::uv::{ListableSecretType, RetrievableSecret, RetrieveCmd};
use zerocopy::{BigEndian, FromBytes, U16};
use crate::crypto::SymKeyType;
use crate::pem::Pem;
use crate::uvsecret::guest_secret::MAX_SIZE_PLAIN_PAYLOAD;
use crate::Result;
/// An IBM Protected Key
///
@@ -72,8 +72,8 @@ impl From<RetrieveCmd> for RetrievedSecret {
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
// 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()
.0
@@ -81,8 +81,8 @@ impl From<RetrieveCmd> for RetrievedSecret {
// Test if the plain text secret has a size:
// 1. len <= 8190
// 2. first two bytes are max 15 less than buffer-size+2 i.e. smaller than the
// block length
// 2. first two bytes are max 15 less than buffer-size+2 i.e. smaller than the block
// length
// 3. bytes after len + 2 are zero
match len <= MAX_SIZE_PLAIN_PAYLOAD
&& key.value().len() - (len + 2) < SymKeyType::AES_256_GCM_BLOCK_LEN
@@ -148,9 +148,10 @@ impl RetrievedSecret {
#[cfg(test)]
mod test {
use super::*;
use pv_core::uv::*;
use super::*;
fn mk_retr(secret: &[u8]) -> RetrievedSecret {
let entry = SecretEntry::new(
0,

View File

@@ -2,21 +2,17 @@
//
// Copyright IBM Corp. 2024
use crate::assert_size;
use crate::{
crypto::{sign_msg, verify_signature},
req::BinReqValues,
request::{
openssl::pkey::{HasParams, HasPublic, Id, PKey, PKeyRef, Private, Public},
RequestMagic,
},
secret::{AddSecretMagic, AddSecretRequest, AddSecretVersion, UserDataType},
Error, Result,
};
use openssl::hash::MessageDigest;
use openssl::nid::Nid;
use zerocopy::{BigEndian, FromBytes, IntoBytes, KnownLayout, U16};
use crate::crypto::{sign_msg, verify_signature};
use crate::req::BinReqValues;
use crate::request::openssl::pkey::{HasParams, HasPublic, Id, PKey, PKeyRef, Private, Public};
use crate::request::RequestMagic;
use crate::secret::{AddSecretMagic, AddSecretRequest, AddSecretVersion, UserDataType};
use crate::{assert_size, Error, Result};
/// User data.
///
/// User defined data can be:
@@ -380,7 +376,8 @@ impl From<VerifiedUserData> for Vec<u8> {
#[cfg(test)]
mod test {
use super::*;
use crate::{get_test_asset, test_utils::get_test_keys};
use crate::get_test_asset;
use crate::test_utils::get_test_keys;
#[test]
fn sign_null() {

View File

@@ -2,22 +2,18 @@
//
// Copyright IBM Corp. 2023
use crate::openssl_extensions::{StackableX509Crl, X509StoreContextExtension, X509StoreExtension};
use core::slice;
use std::path::Path;
use helper::{download_first_crl_from_x509, StoreSetupMode};
use log::{debug, trace};
use openssl::error::ErrorStack;
use openssl::stack::Stack;
use openssl::x509::store::X509Store;
use openssl::x509::{CrlStatus, X509NameRef, X509Ref, X509StoreContext, X509StoreContextRef, X509};
use std::path::Path;
#[cfg(not(test))]
use helper::download_first_crl_from_x509;
#[cfg(test)]
use test::download_first_crl_from_x509;
use crate::error::bail_hkd_verify;
use crate::misc::{read_certs, read_file};
use crate::openssl_extensions::{StackableX509Crl, X509StoreContextExtension, X509StoreExtension};
use crate::Result;
mod helper;
@@ -167,7 +163,8 @@ impl CertVerifier {
/// * `cert_paths` - Paths to certificates for the chain of trust
/// * `crl_paths` - Paths to certificate revocation lists for the chain of trust
/// * `root_ca_path` - Path to the root of trust
/// * `offline` - if set to true the verification process will not try to download CRLs from the internet.
/// * `offline` - if set to true the verification process will not try to download CRLs from the
/// internet.
///
/// # Errors
///
@@ -183,26 +180,51 @@ impl CertVerifier {
Q: AsRef<Path>,
R: AsRef<Path>,
{
let mut store = helper::store_setup(root_ca_path, crl_paths, cert_paths)?;
let mut untr_certs = Vec::with_capacity(cert_paths.len());
for path in cert_paths {
let mut crt = read_certs(&read_file(path, "certificate")?)?;
if !offline {
for c in &crt {
if let Some(crl) = download_first_crl_from_x509(c)? {
crl.iter().try_for_each(|c| store.add_crl(c))?;
}
}
}
untr_certs.append(&mut crt);
}
let (ibm_z_sign_key, chain) = helper::extract_ibm_sign_key(untr_certs.clone())?;
let root_ca_verification = match root_ca_path {
Some(_) => helper::RootCaVerification::SkipPinning,
None => helper::RootCaVerification::RootCaOrganizationPinning("DigiCert"),
};
// remove the IBM signing certificate from chain.
// We have to verify them separately as they are not marked as intermediate certs
let (ibm_z_sign_key, chain) = helper::extract_ibm_sign_key(untr_certs)?;
// Two-round verification:
//
// Round 1: Verify chain without CRL checks before downloading files
// from URLs from (yet) untrusted certificates.
let store_builder = helper::store_setup(
root_ca_path.as_ref(),
crl_paths,
cert_paths,
StoreSetupMode::WithoutCrlCheck,
)?;
helper::verify_chain(
&store_builder.build(),
&chain,
&[&ibm_z_sign_key],
&root_ca_verification,
)?;
let store = store.build();
helper::verify_chain(&store, &chain, slice::from_ref(&ibm_z_sign_key))?;
// Round 2: Download CRLs and verify again, but this time with CRL checks
let mut store_builder = helper::store_setup(
root_ca_path,
crl_paths,
cert_paths,
StoreSetupMode::WithCrlCheck,
)?;
if !offline {
for cert in &untr_certs {
if let Some(crls) = download_first_crl_from_x509(cert)? {
crls.iter().try_for_each(|c| store_builder.add_crl(c))?;
}
}
}
let store = store_builder.build();
helper::verify_chain(&store, &chain, &[&ibm_z_sign_key], &root_ca_verification)?;
Ok(Self {
store,

File diff suppressed because it is too large Load Diff

View File

@@ -4,32 +4,14 @@
#![cfg(test)]
use super::{helper, helper::*, *};
use crate::{utils::read_crls, Error, HkdVerifyErrorType::*};
use openssl::{stack::Stack, x509::X509Crl};
use std::path::Path;
use openssl::stack::Stack;
use super::helper::*;
use super::{helper, *};
use crate::test_utils::*;
// Mock function
pub fn download_first_crl_from_x509(cert: &X509Ref) -> Result<Option<Vec<X509Crl>>> {
fn mock_download<P: AsRef<Path>>(path: P) -> Result<Vec<X509Crl>> {
read_crls(std::fs::read(path)?)
}
for dist_point in x509_dist_points(cert) {
{
let path = get_cert_asset_path(&dist_point);
let crls = if let Ok(buf) = mock_download(&path) {
buf
} else {
continue;
};
return Ok(Some(crls));
}
}
Ok(None)
}
use crate::verify::helper::StoreSetupMode;
use crate::Error;
use crate::HkdVerifyErrorType::*;
#[test]
fn store_setup() {
@@ -37,7 +19,12 @@ fn store_setup() {
let inter_path = get_cert_asset_path("inter.crt");
let crls: [String; 0] = [];
let store = helper::store_setup(None::<String>, &crls, &[&ibm_path, &inter_path]);
let store = helper::store_setup(
None::<String>,
&crls,
&[&ibm_path, &inter_path],
StoreSetupMode::WithCrlCheck,
);
assert!(store.is_ok());
}
@@ -60,20 +47,51 @@ fn verify_chain_offline() {
let root_crt = get_cert_asset_path("root_ca.chained.crt");
let certs: [String; 0] = [];
let store = helper::store_setup(Some(&root_crt), &[&inter_crl], &certs)
.unwrap()
.build();
let store = helper::store_setup(
Some(&root_crt),
&[&inter_crl],
&certs,
StoreSetupMode::WithCrlCheck,
)
.unwrap()
.build();
let mut sk = Stack::<X509>::new().unwrap();
sk.push(inter_crt).unwrap();
assert!(verify_chain(&store, &sk, &[ibm_crt]).is_ok());
assert!(verify_chain(
&store,
&sk,
&[&ibm_crt],
&RootCaVerification::RootCaOrganizationPinning(
"International Business Machines Corporationn"
)
)
.is_err());
assert!(verify_chain(
&store,
&sk,
&[&ibm_crt],
&RootCaVerification::RootCaOrganizationPinning("International")
)
.is_err());
assert!(verify_chain(
&store,
&sk,
&[&ibm_crt],
&RootCaVerification::RootCaOrganizationPinning(
"International Business Machines Corporation"
)
)
.is_ok());
assert!(verify_chain(&store, &sk, &[&ibm_crt], &RootCaVerification::SkipPinning).is_ok());
}
#[test]
fn dist_points() {
let crt = load_gen_cert("ibm.crt");
let res = x509_dist_points(&crt);
let exp = vec!["inter_ca.crl"];
let exp = vec!["http://inter_ca.crl"];
assert_eq!(res, exp);
}

View File

@@ -3,25 +3,18 @@
// Copyright IBM Corp. 2023
#![allow(missing_docs)]
use openssl::{
ec::{EcGroup, EcKey},
nid::Nid,
pkey::Private,
};
use s390_pv::{
get_test_asset,
request::{
openssl::pkey::{PKey, Public},
BootHdrTags, ReqEncrCtx, Request, SymKey,
},
secret::{
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion,
ExtSecret, GuestSecret,
},
test_utils::get_test_keys,
uv::ConfigUid,
Result,
use openssl::ec::{EcGroup, EcKey};
use openssl::nid::Nid;
use openssl::pkey::Private;
use s390_pv::request::openssl::pkey::{PKey, Public};
use s390_pv::request::{BootHdrTags, HostKey, ReqEncrCtx, Request, SymKey};
use s390_pv::secret::{
verify_asrcb_and_get_user_data, AddSecretFlags, AddSecretRequest, AddSecretVersion, ExtSecret,
GuestSecret,
};
use s390_pv::test_utils::get_test_keys;
use s390_pv::uv::ConfigUid;
use s390_pv::{get_test_asset, Result};
const TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]);
const CUID: ConfigUid = [0x42u8; 16];
@@ -33,7 +26,7 @@ fn create_asrcb(
ext_secret: Option<ExtSecret>,
flags: AddSecretFlags,
cuid: Option<ConfigUid>,
hkd: PKey<Public>,
hkd: HostKey,
ctx: &ReqEncrCtx,
) -> Result<Vec<u8>> {
let mut asrcb = AddSecretRequest::new(AddSecretVersion::One, guest_secret, TAGS, flags);
@@ -74,7 +67,14 @@ where
true => Some(CUID),
false => None,
};
create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx)
create_asrcb(
guest_secret,
ext_secret.into(),
flags,
cuid,
HostKey::V1(host_key),
&ctx,
)
}
fn association() -> GuestSecret {
@@ -98,7 +98,7 @@ fn create_signed_asrcb(skey: PKey<Private>, user_data: Vec<u8>) -> Vec<u8> {
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
asrcb.add_hostkey(host_key);
asrcb.add_hostkey(HostKey::V1(host_key));
asrcb.set_user_data(user_data, Some(skey)).unwrap();
asrcb.encrypt(&ctx).unwrap()
}
@@ -110,7 +110,7 @@ fn null_none_default_ncuid_one_user_unsgn() {
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
asrcb.add_hostkey(host_key);
asrcb.add_hostkey(HostKey::V1(host_key));
asrcb.set_user_data(user_data_orig.clone(), None).unwrap();
let asrcb = asrcb.encrypt(&ctx).unwrap();
@@ -243,7 +243,7 @@ fn null_none_default_cuid_seven() {
let (hkd, ctx) = get_crypto();
let mut asrcb =
AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag());
(0..7).for_each(|_| asrcb.add_hostkey(hkd.clone()));
(0..7).for_each(|_| asrcb.add_hostkey(HostKey::V1(hkd.clone())));
asrcb.set_cuid(CUID);
let asrcb = asrcb.encrypt(&ctx).unwrap();

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE/TCCAuWgAwIBAgIUWT/F3gP9fOTTq3yOMVskcXM8vOAwDQYJKoZIhvcNAQEN
MIIFBDCCAuygAwIBAgIUD2bTT6yk21U+ojAQnf3iw5w2EEowDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy
MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3
NDhaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAAT1zpBNemPCT0tEx2S9qnlPxyBgxKl6BSX
ghqf3MfOqQKIUdKEbxP0nr2QtIR/MwvBp4YhjxZ9AZtzVtXbQrULTAGlKK4qcf1I
W1rYZ5S0b4dmvh2HbIv9wZLWKaZ/ovnvAJk949WDCbBzC3Dy1E3zCaYPefLHHNve
dWX8RtFUhnTqm6NxMG8wHQYDVR0fBBYwFDASoBCgDoYMZmFrZV9pYm0uY3J0MA4G
A1UdDwEB/wQEAwIDCDAfBgNVHSMEGDAWgBSQCOxEmGwiupA2ER7srMBydGO1aTAd
BgNVHQ4EFgQUUl5SPIEseFNeneyuOidsw06L7gQwDQYJKoZIhvcNAQENBQADggIB
AFAg/hFlaBT+WNlUmVJlAd6FYr6vvRjJiKxcNBZ5wElzxA5OjuGX6pLYiNkzXSy2
N/4nQ3a8fr04IB9Uhx5ncMzSKVkG+4mbN3xmR7f6zZuFWV/T9Aom3LIbQ5KYR2wW
EvX8b2xbvd74rKAgavq/iuFRn8skQJGgQk9J2YEApOW9wkoFQRgziuu55Cw5GT+f
w9rKgAFTN33ZwfWs86ELJlDOY0aX5373WGccuEKm8y+l2UVLlly125eezz84RSh7
5j0VUTjK4ZqpSD8yiPxN+vocV+nY52cgWxrVf7g7wlPunxXWX4rxR0z/mEVDDBAC
y7cAxsa4HtmJexuZbjEh8TEgDZXDA24BuFXqtkwqnUpv8KghyKb0KgTQecP9rRhL
b2iHdQrALnddMzJzp7Tn+jsR2A2G0lLLsIuIRFR/1eAxecgTMoxXV8N0SJHn6emH
Uwif1Qr5JVw4UOFSLW3MT3f70hY5hMzrxowOapcYhyR0vbhGFrF1YI8YmAuk2m4P
Dfd5Za5cnfVJiAdNBnNPtV/5wiViX1VJGCrIxgDN6B2VE0VQLp2gKwpsSmhqN4CA
qHzAi3Yj3F8vQ2mWQeuABWLAJrIoxXpVYQvTf4uij0ARZRbajQXx+LZnAUsCi5eZ
LaYrTVn8NzHlGQocUWljG06GaLY8GNqKPQrZD/+6hIYK
EAYHKoZIzj0CAQYFK4EEACMDgYYABAGXMdZTHQ6O1boDC7ncHiILfFqHBRkOaEPs
NTntrxSdyWj7CCAa/VkUKVxq3CnyXqbUv8V/fNncU/ibw0o1m/T3SABExiXKfuKl
1rUm99FfFeZO2uuIcg0pzbGf+opPttIyR+zp1LLbF3eDo0PEovBLT1J1s6iUIbx8
hyfMwUG5JmxdiKN4MHYwJAYDVR0fBB0wGzAZoBegFYYTaHR0cDovL2Zha2VfaWJt
LmNydDAOBgNVHQ8BAf8EBAMCAwgwHwYDVR0jBBgwFoAUVpyIVzghY6i5zPVTHvRO
xxIW8SswHQYDVR0OBBYEFGWsrMjwzRKXSrSBTPdYPTSU6aHnMA0GCSqGSIb3DQEB
DQUAA4ICAQAZ9mWLrrIFk65OJIk6UuX3wwRo824+rsU0TSATzTqRNVb4Ie0KUhMK
V3JbLG16PKk/1k/U6t9h+hZp9O8e3netj5rpxUxQVdMXbUdF4FSxNr1MrY0tWynf
U121bdsDLAgaZlBtl+DRgltH/ApDNnyVetAT7LpOwdmssnPhFytD5//kE4+s54Fy
GsB+aKgvZDHjFgIUkcTYvuB3eTK3J3jrEv8QJYB/Pp3233yMnsSbrE3XLRafJtqQ
77Vi7U8/i9aC9XpDPjTC76sH4nPpby0M85+RnDe+wuV98c7oAd+HcBvxlb80dIbu
jhzXXC6i9teR9PqjJo32T+Pn+gmZlQf1ddXzrYxFa/4mVnVw6IjQfs8NsZRVios7
7vxxm4sLWIJ5s+2gp+jEsV0I+KY/54KLBpJ4XkPRgVhkZRgKieeaFhdknyKOPIPP
dlCbt4e4ivGnbtwO5BnLxq/ek+DuHAsSp+dE4z7MtOKsL5HYgKEqTOV0H1UdjXH9
ezAUwrrAaMc5cSk5ksnA/LOmGd4fZNhD9r6m/Dwtk3PY6DrESEIh/IXYEcurdNtu
vj/Lhscv3AT5DZ7IRbBpHKtNPGezFvcLM18rK+pqHJyoFg77PhKJEUDqRbpbq+VM
vFWV/aTqd6rNPpkVwlI1p3I3BculIlmKPaUc9qhsAlQXHWnBAzvSSg==
-----END CERTIFICATE-----

View File

@@ -1,8 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAXR6FCIgd+fjhO/WJ
KwLTP01mBDtYkWbVE71jshjLLpZHunRpsYKbQKCwaDmMYLRrQnTxPgZH1PVKzguI
7M7n9zyhgYkDgYYABAAT1zpBNemPCT0tEx2S9qnlPxyBgxKl6BSXghqf3MfOqQKI
UdKEbxP0nr2QtIR/MwvBp4YhjxZ9AZtzVtXbQrULTAGlKK4qcf1IW1rYZ5S0b4dm
vh2HbIv9wZLWKaZ/ovnvAJk949WDCbBzC3Dy1E3zCaYPefLHHNvedWX8RtFUhnTq
mw==
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBoV15DZCYEBy2dx5Q
7PjcNBni/J3FXBka2MZdy402KKrrW6mZI554n4IlpNeyL9Tb4bMp4glYdDsoHLp/
jbZ4K4ChgYkDgYYABAGXMdZTHQ6O1boDC7ncHiILfFqHBRkOaEPsNTntrxSdyWj7
CCAa/VkUKVxq3CnyXqbUv8V/fNncU/ibw0o1m/T3SABExiXKfuKl1rUm99FfFeZO
2uuIcg0pzbGf+opPttIyR+zp1LLbF3eDo0PEovBLT1J1s6iUIbx8hyfMwUG5Jmxd
iA==
-----END PRIVATE KEY-----

View File

@@ -3,18 +3,18 @@ MIIDVTCCAT0CAQEwDQYJKoZIhvcNAQENBQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYD
VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u
MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv
cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEVMBMGA1UEBwwMUG91Z2hrZWVwc2ll
MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI0MDMx
MTE1NTIzOVoYDzIzODgxMjIzMTU1MjM5WjAVMBMCAgIrFw0yNDAzMjAxNTUyMzla
oCMwITAfBgNVHSMEGDAWgBSQCOxEmGwiupA2ER7srMBydGO1aTANBgkqhkiG9w0B
AQ0FAAOCAgEAsRE3oW/VAx2JPESuWDZVbKIX9n26BZUZ2mdmUocRmn7KQ6CAi0Ac
L5YRUvYt7kmGo6BSA6rUa0TMoMBtFzfIJ6HQZzEoA/LOkIKMfNHsFFzpIJxghPYJ
PfPkXoLpAOBcLDrYWr1bJ3mkvrd6Tuyx02wJEhVmTcF8W/18AFRPuEVg/u3vJmeQ
yMdwQZG42kEslvhCTO688vozYeX+dXO1/AXamzYQZyEWk2cBQ28DIc5eg39Tq77N
89xqWNK/FWKkF4USn9psiBJQpKEjq+P1jTgdpuN3IGv618hlxS455eGyJrbiTxjD
osWGShI+ZVznnqw98aX558hnNWHic+5JVvdJAwNaMMuNNkApFdz328z9dPtcUAZT
7mNcKAuY1NcqdxKTPwkGWfmZm+WndGwKLwpshBhF/ImiAb/UiDrLh0jGbQ/FDE3j
DtWs4k8eXIibtzGhgjfewdXO66jw5Z55FajcOPgja3uw2g2KEhJ+/VP1YtCvCpkm
NX0liKgKLzUGDCbzyrH7QD16T633ebLbak3CtNMMlxZRQO8DQDbDsEKybCG62f/w
OmM+QmL7lRFcXDbtVNxBSQKaDBDlNaEGEjM8phz166g+UMD7M6xJjmwJx/G/QvUd
98YQfMEIt8IlrIbIfHXnAlj4SJwWNfOw+SN0dCqD5CDEYzYVtZgrNXY=
MScwJQYDVQQLDB5JQk0gWiBIb3N0IEtleSBTaWduaW5nIFNlcnZpY2UXDTI2MDYy
MjEwNDc0OFoYDzIzOTEwNDA1MTA0NzQ4WjAVMBMCAgIrFw0yNjA3MDExMDQ3NDha
oCMwITAfBgNVHSMEGDAWgBRWnIhXOCFjqLnM9VMe9E7HEhbxKzANBgkqhkiG9w0B
AQ0FAAOCAgEAQIp0vuCpPifq82vs30SfcTn0ppIeM2s7TSpGL8AVC0+56x8RXl+O
1QUw4gOWtlO4m7legzGbCu1Wh3gXFb+6nUhWhT0egtAneoNDXRSHGXdo0nWAoH3J
XUN/PaipGvDIEZF/UnehYoRrLG307CgJBQkCErFVvI6XJxqGtc6wgsQM2a5A72zV
jlPEAbCsj5k+y7XzJ+vFVA531IHjdtYrEVMh2T0dN821n/yCwwecuMP4semlqYx0
xJNm2y4whvyQnDpg4YIfLw5LUc9otvztLYW8h3Si34TO0ApJ6TpEdBdFx23jm/jJ
VbfM+g2wFoqsW621BiHn9S1KYD0ENSsbaG6BVRm/CTbPNYw6AdgZS1ggZ+a30NCn
ktg6wxVmXK0I3fzgTcOnaspr/rxkBjDi16sfob9yUuaBuTBOgMO0suq7BEFIl97Q
tpkVVz9mb53Ixt/ZuF6QpB4DqOyksS8YehBJc9nOGMknOxcS1zYrWJzVJyyrKrlB
3VHQ4aYE0WsmP+dpX/QE+gj9/Mp6cvTywVSjW/d9zGOyrSvS/ViU1qsMWrr2iy23
tIOaWRVJBi0TX8S8om4ACgi/qwYGTAcH32Hde0nKy07Bm7JJjGUtJbfLzkM/2rjW
qvmPlmmPWJxMyVYoY9+mamuDd2jRtJyoULrE5hCIupFKVV7Di8RQJJc=
-----END X509 CRL-----

View File

@@ -1,38 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIGrTCCBJWgAwIBAgIUMCRfNPXX7mCzJea1P3mCUDqZdB0wDQYJKoZIhvcNAQEL
MIIGtDCCBJygAwIBAgIUJvPxAmw0HNfQtDlcHDQQXvh5AegwDQYJKoZIhvcNAQEL
BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0
MDMyMTE0NTIzOVoYDzIzODgxMjIzMTQ1MjM5WjCBzDELMAkGA1UEBhMCVVMxNDAy
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI2
MDcwMjA4NDc0OFoYDzIzOTEwNDA1MDg0NzQ4WjCBzDELMAkGA1UEBhMCVVMxNDAy
BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp
b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y
cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMRUwEwYDVQQHDAxQb3VnaGtlZXBz
aWUxJzAlBgNVBAsMHklCTSBaIEhvc3QgS2V5IFNpZ25pbmcgU2VydmljZTCCAiIw
DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALIvcLAKt2DM5rTc1nOhyVT2EbNy
CA1Sy+l1mSloBRVqjyk464mvlqq9p/CDO1e6tWiFhdEFChuTYzzDOxHazLjPc2Y5
U+r0QYYY8KK6jLQDj4crChPMJoJ0HZbmlWe/3uMl7lmrVMyHgqqOftQ52etBCGDn
d/RV7U4OC+MJ01ePYUcABxPh3APA+DDV5eZ2b1k74JzLvXa+SGA82MPDQYc70Waf
CcsfCw04Adnc968CEUbNQYxdfXZfL0Uvi44bohuaB5b07KY4cCetdMaBIuowgVsa
d3Zo3pI/vaj8nlAIVmYNbG4aVmItG2q+3K8Zt11A1WXMCfVBfzg4aHAmEPJzVD+s
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
-----END CERTIFICATE-----

View File

@@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
vpiaRjhZtk5MLPJs9mTHq1STrzL9
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
Lt5nZ3XPsdhCctZyYGqQjq5YsoGNU1yr
-----END PRIVATE KEY-----

View File

@@ -3,18 +3,18 @@ MIIDRjCCAS4CAQEwDQYJKoZIhvcNAQENBQAwgb0xCzAJBgNVBAYTAlVTMTQwMgYD
VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u
MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv
cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMR4wHAYD
VQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUgQ0EXDTI0MDMxMTE1NTIzOFoYDzIzODgx
MjIzMTU1MjM4WjAVMBMCAgG8Fw0yNDAzMjAxNTUyMzhaoCMwITAfBgNVHSMEGDAW
gBSMe9JFoed6PvsUVY3z4MU6KhnKCjANBgkqhkiG9w0BAQ0FAAOCAgEATjy3crOb
zDvF2CiBxmDMvOK8E/fhQ1BSUOB2qc/OiqK6zb1Q2MFvni945+Q+TwbHJqWBfU2z
QHuhOQHUQQNnIvMPXgjJuT32eF2Y7J4weaQOHNjrt1dwE6TPC1DLNUzibbjKr5x1
/scmCiXWYxjeLvS7aTACNaD1UmUxyroK6h4oc968ofdPURK54sAiddY+VBA4KW4B
WlAjxGSErZk0DIg5SXakSQbTkz8+pLnrqqtmvwQXk92lyfrwsSiZhHlcfBrJ6+mp
1Mho9H5r97mO/LRRV+2CRShqTElxVORbW3Q2ku6RGJHEUB7AssIeKBtEma8yfUff
S2bX0P82ETUCT3CPZK29QJ8eaxxH+PaRnyiT/W8vWuo+BRw8XqQ2AsM22vVCPP61
mg6VNsPJkJ8UaUF/S41aGMS21HNGk4Ik0iv0wiuu3q2rcFwGuuWB/gRUfSuPfFSO
K5sEf47dBPB5kMZCSEkX1RvHPWAYwd6g8GgVdlQEt/IIX4ChoASES5JNhDFar7tg
6h72VhfTtwEkGqg/z8vI1zP1Qzl2bZ773B/k/TAJ3j6N1JXiHUKioL1hAnA9glrj
3hnZQEQSewPf8opPe/l7UEQNXGlamYic5WRsm8xbN1KL6HARLqxkiC7K8SS6U99s
T7L2jh4egE5kyM4lLOFYfEugj4s6Soq6XuU=
VQQLDBVJQk0gWiBJbnRlcm1lZGlhdGUgQ0EXDTI2MDYyMjEwNDc0N1oYDzIzOTEw
NDA1MTA0NzQ3WjAVMBMCAgG8Fw0yNjA3MDExMDQ3NDdaoCMwITAfBgNVHSMEGDAW
gBRBgPq0WBDRF/Zb2D4wpASJVmNL5TANBgkqhkiG9w0BAQ0FAAOCAgEAhEMl0/XK
3bLp1PMsjB4QrgfAqm8DV5I2WXxrHc0anooAlEqIPEGea5FO4UkL0oxz3Ffxo09O
Blh9xwAZOOfvjp1mkRucpd2QEdznOlIikL9NXWNMM7Bm9Lljla8pdBrIZQSrBAoi
WC3StSccD88Evoua87YH98MyO2md4zeDfDdICzZTrYqWWRhbghiJ/vPQwFY/SM+4
uGp47egoxt8lTMNpPbqBV9+ptZvarvICIQmlRTU4ncWKVb2LmNYJ7zJXeJqEqtsX
Ti/fIhnZd26IHXfmEp2VNJOp9ODZATiSCZ9od90y/Dhbi2ZmR3hFTU/VrKgzeqzb
Qbi+dLKvaVf4HGWVxOUuSY9279W3aLkCbP5iL9qGtjE6hL0XncCDR0ifADTN7zye
1o/OcZr7VT4V6a4dBm/5kNJh3RB8fcLGgrn1Rfa+2GSniFOCMjZCjdMlGHq4WmPb
yvOLLF2AD8P7PwLxMJ28Elbg9TN7OiV19ALB5ZgEFoBwan4zIWtkekw+pafEqQma
cDGx197fUf39bYWxGSpALsP3pptNrTsNmUNdhJuLxO2kq5jFQMXRq+yl5VG7JTms
b/VhB86fOt16hHZQto3gKvd0wXvjCRGiYvOJLjbDQG3e8ghTCGU+40CwWhdC0q0A
5k1z1ruYUj1mI0+TMGkz5MbyJ3dmgh3JTmQ=
-----END X509 CRL-----

View File

@@ -1,37 +1,38 @@
-----BEGIN CERTIFICATE-----
MIIGjDCCBHSgAwIBAgIUKp3mjstxJ9gXt3S7lpKjri2dEUwwDQYJKoZIhvcNAQEL
MIIGkzCCBHugAwIBAgIUd+Tu7LLVqsmeGQbJ5IAjvORmZzAwDQYJKoZIhvcNAQEL
BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0
MDMyMTE0NTIzOFoYDzIzODgxMjIzMTQ1MjM4WjCBvTELMAkGA1UEBhMCVVMxNDAy
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI2
MDcwMjA4NDc0N1oYDzIzOTEwNDA1MDg0NzQ3WjCBvTELMAkGA1UEBhMCVVMxNDAy
BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp
b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y
cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxHjAc
BgNVBAsMFUlCTSBaIEludGVybWVkaWF0ZSBDQTCCAiIwDQYJKoZIhvcNAQEBBQAD
ggIPADCCAgoCggIBAK+kNJHAUL4dcMnjElivyuzkO2UwTUlJcKQCBmWRHYQjRbP5
akJx8SZ+wI0Spo00hG4sV2BxA83J+Yrked53DEzlR+RxojA16vXzlUO8c9KH2dJQ
E17PgZnu7/hECmQcT69ZJAOh78ILEXRYk+2ixSTaZRTtchJdzfXIQ0633O2Mi/7z
z5idQmQeXbitC3QIZeNzwitl2FLXPIw6MUnktOKNjQSNxMr9AU8q1cfOoICJQs0J
wYVpby0dv1z0f2N21JJywaeAImHa2h58sSX6uqwOXtzdwcPlr2+iJ09YwHM8uO0D
rtPx3bLHfkvbpMd5cCKjSeFozP1nVnlKJUmAZ8UXk4MAFKCtuLv2/InA8MkclZmX
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
ZLaX284IyQ==
-----END CERTIFICATE-----

View File

@@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
M9Wd2js5nPPh0AN3Px1643vFYcAQ
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
xToCDsY72xtSlqKcRx1Jyo5xXSPvd68=
-----END PRIVATE KEY-----

View File

@@ -3,18 +3,18 @@ MIIDPjCCASYCAQEwDQYJKoZIhvcNAQENBQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYD
VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u
MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv
cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMRYwFAYD
VQQLDA1JQk0gWiBSb290IENBFw0yNDAzMTExNTUyMzhaGA8yMzg4MTIyMzE1NTIz
OFowFTATAgIBTRcNMjQwMzIwMTU1MjM4WqAjMCEwHwYDVR0jBBgwFoAULCM6sLGi
l2kMgXqL+7vHO6vCBHcwDQYJKoZIhvcNAQENBQADggIBAJB5ERfMQEZ5Pdc3A8+d
gDiY53VEdA0Zd8MDv+j+Mgu1qn7IW2rpE313yVHieclZ65ReEtb825St8UBJtjiZ
9Vd4lK9FUQKKCblCYNrEbu9cvqNeHDGxldQPUkEoz+z5kielcEwD6RUsK5fSYe3Y
6E3jc9mMHw7xWCSiolvNr5y6AUymfu22v41qgfaB0yNe5uz6vRgRoiy+OTP3Z7oc
ls7o73F9O4k6QYdba8us4v0TSwZAAPO16TsHhmyHcBe3w23UIusTU/c+6A++hGdU
TNUW3OZl+hv+4BcnClrVBDfWiExOMNzIhH9hzlXQ2qNPNqz2ymlH8Wgpf1TBYJ8m
xe8EuGGrDFuewa45kA8uxuHCiSLCsBowDEXXkswaSF4E4yzYZqcNDcLOedK7vG9G
+zXYHOpLsZMyfgauxjuWtwR/ma+ub85CHy+eUC2waI+Mk0Uk0Lr5y0Jdp8ztxakp
UlVHYNU/Q+kwHfmKztkOsedomOf1/8IvzE930ZB8rv0G8ok2HXYvA3+r3lcDSG/V
7+yq3HcJezw0XCDtc5mv8dWI6pUv8siWFKok+hlkZpeSXgtqRhF2rzI8yz+lk5Cr
5yCWroPI0T44o2RzjfGZAViRH0nnYF1GoBORSZORppubcjmiTy46OFG/fiWKGKV1
sngAoQ4TCDQzId0zwGLtUCHu
VQQLDA1JQk0gWiBSb290IENBFw0yNjA2MjIxMDQ3NDZaGA8yMzkxMDQwNTEwNDc0
NlowFTATAgIBTRcNMjYwNzAxMTA0NzQ2WqAjMCEwHwYDVR0jBBgwFoAUDz/nhP/+
DIPql2WnuHiBw1W1pCowDQYJKoZIhvcNAQENBQADggIBAMZqebVQ5uXrSOjIbzH8
RJCo9JjUH3XQJaKQrZW8QWpZKzY2rpLU1Ag6C8jM8qHAEPpkaLbwcpZUOV3g4AEw
K5ezIe8VVJK7aHH96XYoIuYVXmohk0M39IEf14sIB5BkCX8aDC8dFekyZf23Uv/T
zwjl7RDc4MAaeV4zKGZ0TBAOT9w1Rfek/U4jwQfvuOvY1Mw5wn7SJrrfE4waaPwK
Nklpd6VsxvMgmLKV7BU3rEwndkbtkAWyWJw2F1L6p93ZNUBILDFc5o2f5qnLp6Yj
qvEZmWUWO7+Y0qNdsOjljmX0XfuWhLuWB2zq6LceR1hr52XABvJAFIDuFnelfgeh
c1rXpZThZSNwNhTVcZr4eLIJ/uR3UtIdl5uTkWY8Gsm3jaC9IoPADlSN+fOMV9eR
1u0q2XBj0DafadDjeAevxM9xw8S/ToklqgYPRQSA3nq38EVE8c7Klkv9x/Ar2U3M
5GGeDIphX93dzi7j1VAWw0fuylY8Bg69NJ9+889K+shDzIL3xBbXEC8RhmiHFi3H
We2O2dtrVb62gUd/WgSTxu3OG3mqWPwPU94zK38zPyndDF41YGp70ur7wHkFPcfJ
0WopCMqdEWBrUc8ZPZ1EBw9cAd44i+hVQB4x7qzNCmHJWi2942kpGv1vJ+IfUr9/
0MtaUK+nInHcSPuyxhdlb6BE
-----END X509 CRL-----

View File

@@ -1,37 +1,37 @@
-----BEGIN CERTIFICATE-----
MIIGXzCCBEegAwIBAgIUC2OH79tloDOfMBeyDBA84epTMKYwDQYJKoZIhvcNAQEL
MIIGXzCCBEegAwIBAgIULbCWZUfOIiRXCiGx8jr2mhqjQrswDQYJKoZIhvcNAQEL
BQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI0
MDMyMTE0NTIzOFoYDzIzODgxMjIzMTQ1MjM4WjCBtTELMAkGA1UEBhMCVVMxNDAy
azEPMA0GA1UEBwwGQXJtb25rMRYwFAYDVQQLDA1JQk0gWiBSb290IENBMCAXDTI2
MDcwMjA4NDc0NloYDzIzOTEwNDA1MDg0NzQ2WjCBtTELMAkGA1UEBhMCVVMxNDAy
BgNVBAoMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29ycG9yYXRp
b24xNDAyBgNVBAMMK0ludGVybmF0aW9uYWwgQnVzaW5lc3MgTWFjaGluZXMgQ29y
cG9yYXRpb24xETAPBgNVBAgMCE5ldyBZb3JrMQ8wDQYDVQQHDAZBcm1vbmsxFjAU
BgNVBAsMDUlCTSBaIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
AoICAQDK+jIoIZVYyEq7ZrxfKv/TQQWCgI5C8BewYkB+/rt8NY0fn/aAZbvJdnDX
cQCLpCeETDi7C2hGujnZQN68otr2GpJVQ/kgKnM8yU/p9yBSUan9Zo/195a9YytF
6Ys/Yc0HApoPtB3t85NHNjAvhzdcBKWMb+/2FiJ61gdnrO/zORnxp/BejxTMyB6r
0mhhVX4wwdl5vfkX/qS3TL0rajEz0V+SwneDiIomoVJCnco40Km1M20qU5L7EpNH
IMEP00hcj40zO0jJ8cGDNoKgTHsZPovYfFmWITVOFvaPcKaloUU67dYEg0fv3ypX
aYHRszcd794VOWQ85r/mlFSSELwoUrVH7G0t6wnzOawW2kk7ZYndbo+Z1ZFb1Ul+
iVwUAHzx/ylmOORJNM4JGiYyC5+1481MLeB+37+V3TJaBe4IoKqVo/OCBsaPt7x1
rMpZLw4gXj3A0Hh8gi0z5HDkKolMSawuU8dpFeI0GN+4hJN9DNQh0OTGLEfzeo+1
lNzsF+jGT9B0tApWhBq/QTyHXvJREVx32hPd/1X6bVbd9mik0bexg6bVpZVNG1sn
7GsVO2wq5OWKe3UtSh8wJcjGdW38fPAB46oIBQ7x0HGdp/KFiBTWxrexLThMFvYL
wPtWedj8ntgOBoH5MJgL1XkVYP+1EQYaBJNfswvF4CrvPRCtCwIDAQABo2MwYTAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQsIzqw
saKXaQyBeov7u8c7q8IEdzAdBgNVHQ4EFgQULCM6sLGil2kMgXqL+7vHO6vCBHcw
DQYJKoZIhvcNAQELBQADggIBALZNLMgm7RqIKY++Blc2qeIDrwlx1U/PPN/PsZsN
cQR48Bq+vQ4cV9hOcZC3jfRbkkDho8jxVTXdEOcpDyU4zNSLG1+b1FNRvqjq2L+d
ow190dYqvfgbdNFo8V6kLdRFe5aCLhHTWGLTbl0vvkc99h1Lt9ZXr6LzhFwbXSGr
Q/qJoLZFVBwJZmfZY3VmL661sBcTmzDDGJxg00fg+1jCIk2Ot3dn+7jQ8g6HYMQv
8GrqGKcFia8fXcTTmcH+Swr8rps1MYypjkpX/zVHsFuENdqZNkqU+OLTaifkrBuO
6evTTjEOcDzMFs5Uipjvq7XOBg5rz9BKRXrjDMdI4CmUzMR272lToNw7lHqqNDKi
nxAzPjPWVwXQTu2LFY3NwubWkPAVsd6FsHKzQHlq97N4sj+vp6vctaEUgFS4pUlL
tbjw6AtQA72z4uIpcAzE2ctTj831QeQSZGKBUpeNPjuK/NjytXQm8MvW7LI2OgaI
H5B9+KE9KjqijpNiiOoidYVwFRrZniIQii06qXwQUgz2gGIzYPER9+3PijrtOY+t
K/vS6NohUYb16PhbbvzFktSGBtvp1kUF4fB2NJh0z6OiZXVEHlCYEDTv78RS36iU
PXijR+rALi20P4Sg6picuvmt2epfSMQ4ynNxPhbSG7wMp+Zp5tOrZcmg4jN0Yt56
tACH
AoICAQDgdpk9Tu3dKO1O2orgftU2GkgARccHONa/NqnTcqaw5vbgWU8CiC99ejvC
z5ER0R3xldjU3uTh2UXgQCY2Qf51lsYNx7CgjFVzTaoJ9FhD96mEIYtMWPMoF1SR
mxxeGQCJHfMtB3ammatkXKV8ia9GlGQFtQU6Mh0CmNZelrvs7zXvYRMfW4oQX1zb
9s4n1E+gxErTRw+PlK2LmOPO7AAMrrGn2sbxaoqJijMWOysekYZaNfaKkOOzyuXn
fa8bcG6qAQWmQX56aUYTiwPNtyovFvUancgDg4bP5hD4hEsl2NRSQZQggt663NWK
ALQBbL6qeJDI+WlAf83TbDVV8wGPuwGKE79+ES+TnT0GhhanEJ2J2swm3IcEVuya
pEay0NAfoEfuzgyYYerekeAvqHbvRIxFjR1Hh5QvkGjmmr376E4CkeyXaqjFSl6o
XamS0gSVfvnT/jYm9SGVFafA38ZGx2iEOMtybwJM+frp5F6DqwRG/dKgJ0W4nW/N
E2iB2JJhVR9iaR973d/4G5vL5WVFUGv3b6VyZZAPftSzPoqPqGEaKGaHprECPUDQ
fR1wdLYUuQfqgVnj1sKWT8YWNNc+11idVU/hPF3Yu9dSPVaEdYIQiQ0NNqnhneO/
IVsOz4xBbh++frq8H16k0xqkOJuC6mCZaU4YxWy8SZ7jukyCywIDAQABo2MwYTAP
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQPP+eE
//4Mg+qXZae4eIHDVbWkKjAdBgNVHQ4EFgQUDz/nhP/+DIPql2WnuHiBw1W1pCow
DQYJKoZIhvcNAQELBQADggIBADxbNKDguPbfyhUOmrXcOPdzNWGvr51Cm0OZLOod
nUcP9rkmEZYMZkmAGYCMkiK/HCd22iOXyHW+OYvq7zdLlaWuMfE6wxwWZ8bK3NQC
3w0HPyAOcxknvGnHLnmi4+sKz+ZwVn816EgYv2iytVdfjclVK0Zvoa6COc0C085e
kWe0hWFuqbVRzmtqoQauwxU9+rfY21/GtiZbkOiAo6VP9qqfrq3hDBoF/WT2gGVT
GPuz6j+m2D2ai2s1rWLJfLfcSIySZ67tMmg3lL/8FrxLVmZ15XjuX8Y1M4XhjdV3
G+9XSeuvyqWBrN1l99U3BvAVozqI0rEx7PqH8dpirwuYWRrJM7bZbuOvdVAT3wpE
ypfXOkjVmTCkvJH2wrupywiVIu7vANJ6poV4zo+f6GJvM8iv2z8S3NmM7vt7NFFX
1Q6HFmlAgAW5SRQ49ClKZAftW5fiOCwkpWsRY6REeKz/bF4anm0vVZpPutM1BYRx
yw4xZihWcfxipzeX+HRuxYZIpuQaUnTKWl9QgCxKzUwNitSL/bp1uI/H+TgRR29w
6ns+M9ZufO7oiuAPBAjw1TTQ8f4C+8qxJIxqKZxqk9TkmDK2+hLkbdBx1hcIBJHY
ClfQLQOBEL1l3nZd2wnZRkdaVPKj235CB05PTeAlrmYHGmhAKhmBpV2eY0J8EFf5
STAx
-----END CERTIFICATE-----

View File

@@ -1,52 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
2lXPecP5/ilaLLUhs7SijqHzcKiNL80=
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
DFiJLuJwaQVArPexRziXnDrbb0bQkBe2
-----END PRIVATE KEY-----

View File

@@ -3,18 +3,18 @@ MIIDPjCCASYCAQEwDQYJKoZIhvcNAQENBQAwgbUxCzAJBgNVBAYTAlVTMTQwMgYD
VQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9u
MTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFsIEJ1c2luZXNzIE1hY2hpbmVzIENvcnBv
cmF0aW9uMREwDwYDVQQIDAhOZXcgWW9yazEPMA0GA1UEBwwGQXJtb25rMRYwFAYD
VQQLDA1JQk0gWiBSb290IENBFw0yNDAzMTExNTUyMzhaGA8yMzg4MTIyMzE1NTIz
OFowFTATAgIBTRcNMjQwMzIwMTU1MjM4WqAjMCEwHwYDVR0jBBgwFoAU1p7a8iaQ
sGamSdR9585yWIW36CowDQYJKoZIhvcNAQENBQADggIBAMcBN8PztJELdCo6fQvw
C9ZU41AmBZsMdIoacgacupt94gHf8SAO1Ric46j3KERpA0mWKAx6RH1s3OX2qN/C
YSrVPIZ076Cn9EMVdK9w9hqt6SYHoot9CwPutF6BBG5O2uN6eAsEM21B5Tr6xTZ/
SUj3fO3+QsePDL84BRfU5vtZcGj1BkULopWwibG82ayEUlbY3J8OPjK5YKuHtxJd
lI4yfkmrEPwHEm8bwI6lhaIxrtTjoVgoolw+FqtVdVdU6O254AUKwxN88bHoA/sk
BOr13AoHXiKEBaEJhocTLJRfvUZVRYGll43ELsy60XEH51W27/uULnC92vBkp4Q1
5hPwSD0RHKfMwsLKqoJaWX1CxfdttapCfriehMXJlv46GPmq6cLIEbbHzJKsyJhN
4mW8Uiwn1aM5EhMauSLuuwNp8QRo4rmQzUstxwn4O/3HzTcjVWbn0XvBXa2S5HtD
2kyWwN/qck1N9aXYk/sf2A2py2ECDuXc/Kvh1kDi6ZSaKA0VSS1FX62HvdqZKQb5
ZmyoQUpg8yv0hholaZd9Jye0pwww/K8CFddsyUMRYDSj0+qiw4pgx6KK3ElKnHeH
uq+iLxDLWTqrWQw6k0I92B9gSlNKR3Z6Zq2UTxo6hB7IYgZv5nxx3BRxnTm6aH6a
pIoxgnFnbNnyUIRAkOCHbFqy
VQQLDA1JQk0gWiBSb290IENBFw0yNjA2MjIxMDQ3NDZaGA8yMzkxMDQwNTEwNDc0
NlowFTATAgIBTRcNMjYwNzAxMTA0NzQ2WqAjMCEwHwYDVR0jBBgwFoAUIlsT6zAF
eYbpsFsGeGMQDoofNl4wDQYJKoZIhvcNAQENBQADggIBACnVIJGdrqX0b2vUQ9Oc
DTfN6KMlmt5mKD9c7c24OPOVo7y1lGEGcanGsZ8/0mZ7G5dIqWU0yxXnMD1U3cbm
nKGHkor/gyvP6hdozFUWIc5EVebDJvnIfj4L0kEVFE+wUO3iqtNnbQjvBo90iq9W
j+TTD+QQAMGKQyi3AwEL6nVrOZiUwjaJCdaV41zXxn7hiyfN89T4xM/f1TnRZ+xw
5ELqGN77PBWaEWf3XyLIbxt/2xJL03zmV8cEHmdPG37i/p82mcYxqkwRlU6iCJeQ
CQ39GNQd9avGm5+cUchAmdZf+tQLVcKsKqjZ7Cq0yihxKoYwOngeOZswF3NueGIP
jXi3jnA8f0vr1uDilleYkIs5At9IAMq2oA8czKelcIIqQLiTaj03m6u0ehb4VWmA
T9LWR6RtuBS0d2Lx8cYapzU0tnnU9mOemixljqLqnURmhfI2yRuh9f2FuzD2n5Pv
H4WHpc2a2Uoz4ub+v93j1ZScDCb+xTmOJ6HfJ2V6A8FYIaD21X1n8n4yc+WeV4Ta
aBqoWI3TihzwJGvc67Zjfdkt7KhUZ6heJZHRrPVKWbDIVL7NVFnx53ANkOZ18pWt
IzJb2xWQlP3u9e1JX4yqVw75woZxQKE3v60F0UGofamRB375lGDYrsufD5Sz1XOl
Cwl/78PBFBOkcvgVCubeR9cU
-----END X509 CRL-----

View File

@@ -12,7 +12,7 @@ from cryptography.x509.oid import NameOID
ONE_DAY = datetime.timedelta(1, 0, 0)
def createEcKeyPair(curve=ec.SECP521R1):
def createEcKeyPair(curve=ec.SECP521R1()):
return ec.generate_private_key(curve=curve, backend=default_backend())
@@ -72,8 +72,8 @@ def createCert(
pub_key=None,
):
sha = hashes.SHA256
not_before = not_before or datetime.datetime.utcnow()
not_after = not_after or datetime.datetime.utcnow() + datetime.timedelta(
not_before = not_before or datetime.datetime.now(datetime.UTC)
not_after = not_after or datetime.datetime.now(datetime.UTC) + datetime.timedelta(
days=365 * 365
)
crl_dp = None
@@ -216,7 +216,7 @@ def getPrivKey(path, create_priv_key):
if __name__ == "__main__":
MOCKUP_CRL_DIST = ""
MOCKUP_CRL_DIST = "http://"
# create root CA
root_ca_subject = x509.Name(

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE+DCCAuCgAwIBAgIUBSLhuGTvxPbggG70ISL2R6DDGZcwDQYJKoZIhvcNAQEN
MIIE/zCCAuegAwIBAgIURQZKxfTuf0H43V0uJcBaFGTxwfMwDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy
MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3
NDhaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJF
hzrwY+wQacmJjWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvL
J6lKJuCog5RfMsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qH
PAD96UEDM8JanKNsMGowGAYDVR0fBBEwDzANoAugCYYHaWJtLmNybDAOBgNVHQ8B
Af8EBAMCAwgwHwYDVR0jBBgwFoAUw4weXbTWAZisD86gZSugZ6V1FNkwHQYDVR0O
BBYEFL4m5UxVbUdEl9yg4sjiWKO/EAxyMA0GCSqGSIb3DQEBDQUAA4ICAQBwaPG5
Mg3iKtxR6ncteH+YmtMHW4/wB/341pTKFsKBYsMNWuCC5AKbNrshCNbFbctqhLrB
LmEpmza3/Pk6izO4AozHNl0tRec/HnQr2gonfI48HBDRiV2f40x0gJG9gGCiJy7o
6iKZDYUnjfnhXVC67RwLMEiIKbeOAWQ9hHqegUjYdaaIlhyiHLMuWMceidvG76nN
2eyJUNEouT4+UvquD2oqSitB3ZLhWRqPOQn57ME1b0QYF240PN8r21YtzPmSI+s/
ej04EcQZrlJId6GtU7YwD1767hVw84v/QjPbMqnYQbxX8n3IvOf541rQ0UdjBFc9
UhbnSn32IGFrRlL1y3MPBF6hLPcpW4P0QrUijc6gZ+x6SNFho8n+dk53F7RvMi1l
SLgJl7x8pUeqBn5QKMcYYsZG39oZmQj4xHjAABx2hRWayDscvROiQpvLHRtLVmk7
+hq4Q/jalc2cNHZSwLX6Tv5P+8waTnXg8YNEHeAAgcw1lD+uw5HgusjGD4USE7Hq
Q6EDGzC+Ny3u2+35XWbNaKWVthtKAIcZ9B4LjdJXeQFGcOMr6yV5rKfOFQwXo8bS
rNy57tiva8KM2weSfRil4f146Rsb3TJzUdlkaN+NVIY0YImiC+rR0qa6Iv6JCio1
F2lu8m/aRHQQF5J5fD7ge6v7F2D6K3qT9tTlrw==
EAYHKoZIzj0CAQYFK4EEACMDgYYABADmnrO8fwhFRmvTAeT91LiGJatTUNRhTr+S
Pf9glpJGFemv1amiV89qBYij6IuYJDhSYAQNllegWfntvzDbCOhDvAH0SY9Ivhd7
7nA20M0Z9FuUNaFBRAkUB8VhVPC+lMM7i/0QA3FCWQEP1V28oXSyIOO7SF77GAb8
U8dobSvrGtb7dKNzMHEwHwYDVR0fBBgwFjAUoBKgEIYOaHR0cDovL2libS5jcmww
DgYDVR0PAQH/BAQDAgMIMB8GA1UdIwQYMBaAFP4r9a6YMGLiFEhK51N4O3+mmIbt
MB0GA1UdDgQWBBTLpumJYUrj5XZpdF8A3HUH8Iz3yjANBgkqhkiG9w0BAQ0FAAOC
AgEAPoZhxpmjEf2mv+Gqwt0z0yG/J3/R2UVO5tHw5L/WWgr8zKWtdVPca9fIFFiT
bRam+m59hDVZ1nIg0SGmxEfsdJmxfk2h3cI9IJDF3CfELKmfUaVISL1zykdSBzIG
J4PO3WmUo3W/F41OzaRiSqY4I+8gOXwZmX4bTAFFNYQ9KI9JEJbY5ZM7oKd9Ts57
lPDWgKf9luN3Teuw+8tj+3G3lzfZ8hO7RThNLi2qr8t0VPecps7ZwNLR+AXLs0F8
Olx3IIhe7REHZnMqTaVJVlHPUi4REpoI7A3jT6S8AnNsEVjxsIbmJtwJEwwlVMU2
E3e046RhNUqyHY5ahBPrgBUVadO0/b8QDW+vLAwbm5yr6E//h7Ygit8grA2l2LtS
vuMEOnsLec1vTcDaeTtDpDJePP6naAc/ZXM+N1riebOYleKlsCylSAEZsoHT0unu
jRCSTtr5YlWHIP7cQbn9YS8gmvMFU+M+0iNsKqs85o3hbcqchRm8DcLGiDD+FC4k
2soTE2K3OaVsfYvfxUcA8vvGOeO13gZplpdL0iw+r1aMm7jhiljf6buF8ZwvsAVC
o1N7uHuOr7Z3hONSbw4GWHFkWy82SIlbM3xNL3geDl7TX3VZFR4OTeIXSskuVOSg
tLwBY1/RM2ZkDnraxi0XDfNJZRiFK4WVZz/tJSGgrF0YT4M=
-----END CERTIFICATE-----

View File

@@ -1,8 +1,8 @@
-----BEGIN PRIVATE KEY-----
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIA8rPNC6rxZR+GxSxb
qWRreFGnWRQGd22nHWKAvQmrA5GlXTtWQMoL8il9Jb1OnrQPPo620nQpzA1GXo4U
BUqgYoShgYkDgYYABAHGO0MnpQa6Q2IxgqV7AGwd3OwBnYOJjYJFhzrwY+wQacmJ
jWeNyHahBCxu4bM8vDr70SF5vZFrWpcWpc9JTY5AagFCFDqIfSvLJ6lKJuCog5Rf
MsWJpG2j/MnK7MxG+Ph0R+ItmLFbWFxCV5YOT43olhwYZr/pd9qHPAD96UEDM8Ja
nA==
MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIA0FE54XL97lOXcJa0
hM+jtHL3zaUQ1up5GOlzcU/4YJ1zGvyXuhOPoC0+tO+Tm6FyVpr5kuYvnix+ixRP
Kbq0q6ihgYkDgYYABADmnrO8fwhFRmvTAeT91LiGJatTUNRhTr+SPf9glpJGFemv
1amiV89qBYij6IuYJDhSYAQNllegWfntvzDbCOhDvAH0SY9Ivhd77nA20M0Z9FuU
NaFBRAkUB8VhVPC+lMM7i/0QA3FCWQEP1V28oXSyIOO7SF77GAb8U8dobSvrGtb7
dA==
-----END PRIVATE KEY-----

View File

@@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE+DCCAuCgAwIBAgIUC3KzCH9KUb8ZaY6J/97gqebLoIYwDQYJKoZIhvcNAQEN
MIIE/zCCAuegAwIBAgIUZ2LzT6AgSHsm2Y0wV0byf4dkroswDQYJKoZIhvcNAQEN
BQAwgcwxCzAJBgNVBAYTAlVTMTQwMgYDVQQKDCtJbnRlcm5hdGlvbmFsIEJ1c2lu
ZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMTQwMgYDVQQDDCtJbnRlcm5hdGlvbmFs
IEJ1c2luZXNzIE1hY2hpbmVzIENvcnBvcmF0aW9uMREwDwYDVQQIDAhOZXcgWW9y
azEVMBMGA1UEBwwMUG91Z2hrZWVwc2llMScwJQYDVQQLDB5JQk0gWiBIb3N0IEtl
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjQwMzIxMTQ1MjM5WhgPMjM4ODEyMjMxNDUy
MzlaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
eSBTaWduaW5nIFNlcnZpY2UwIBcNMjYwNzAyMDg0NzQ4WhgPMjM5MTA0MDUwODQ3
NDhaMIG2MQswCQYDVQQGEwJVUzE0MDIGA1UECgwrSW50ZXJuYXRpb25hbCBCdXNp
bmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrSW50ZXJuYXRpb25h
bCBCdXNpbmVzcyBNYWNoaW5lcyBDb3Jwb3JhdGlvbjERMA8GA1UECAwITmV3IFlv
cmsxDzANBgNVBAcMBkFybW9uazEXMBUGA1UECwwOSUJNIFogSG9zdCBLZXkwgZsw
EAYHKoZIzj0CAQYFK4EEACMDgYYABAE3KDrdCdVeBV34NicA8AoP7hItcwxgXHOQ
F+V02N6b5AR7w3YDHE/JSzSoZZIYiqdy8SmeD5GtwHLV8tLQ8xnadgHT0BesGS02
w0H5foGzzZOHZW3AfbdH4MpssR4Bf1jqL0jw6eV+oiMStDuZ44zri7PFjvVByt6M
oeSvh5qAXCQG/6NsMGowGAYDVR0fBBEwDzANoAugCYYHaWJtLmNybDAOBgNVHQ8B
Af8EBAMCAwgwHwYDVR0jBBgwFoAUw4weXbTWAZisD86gZSugZ6V1FNkwHQYDVR0O
BBYEFL8FyjCu1iRcjwx4pzfo+VIQ5NdbMA0GCSqGSIb3DQEBDQUAA4ICAQASxSXu
RxNw/kSwqedNq9jOTHb5FATNykBIVSuXS9BB2qkjcTVDXNnlsSBrPL/CumRp/TFD
5VsB2rLhESmUrpghQrODeFvyFE52yVhxvcNCyjz7yIQZvc4qofMQMsg3o0rSqp2s
lu1PUbcrL2aCG1yxB3isObVqiWaiRdnPxL8aX3Qt6BszlwWUgaFoaH0uZxlgVGKV
C+dXrn5WkNRVd2ouHSLQE6fIUYIf/TrV+AKu804IEoFRIvMUCqQRUHsj5toKhfDb
6tl/Xd+EiPCYbnhR2J01I08yxMExvYXfXapY7JJjlWTHKFKaxLoqv++NZRM1bW6s
uyLWP735Qb+0AmhZ6TfeJM7H77LpQK0WCylaNVJWWjXt9UsnNdirCbp/jpKF8bnG
2PkjBHKruvCakqw1bDq8eDv9In1Ki+Um4gp7OfjYvcN8zxvGQofgj++UaCy982iX
WSq14iUyrRDVu8zWghL/F1lUx7ab8UV+OrmZuCALVHZ76YVdwmJXGYll1OBbJbgL
5xze6p77vKzbgNyABWmR6TlHq/nFDhj9kKirpQaI7WHyOtsGpc7sqd0tT+CeOhNf
l3xXyFPb6N58aSC2cY0W0Nq6X/mWIgMqHY9lzYLmoLBFFZlIjqWzwyVcajmjrtaK
rlfs0e9f9DvVV8bMTXFMUBlWrmYDrROKpYLqhg==
EAYHKoZIzj0CAQYFK4EEACMDgYYABAE69tLPv1D4DIFTgQo+P57ZABDoxe6x5Rjy
+NYiRi/Nfir11CxVGQDaYDfUFsb4peQJz6haxqXfXmm4MWlIbdWa1gHMf3GQl/xt
IGGbJURxnBIR3rj2pqd0DqbONj8blnQTCWLSHsLsgyI6WWK4RHd9ZtER0UQDxoQc
c8Htsuh1+SKo6qNzMHEwHwYDVR0fBBgwFjAUoBKgEIYOaHR0cDovL2libS5jcmww
DgYDVR0PAQH/BAQDAgMIMB8GA1UdIwQYMBaAFP4r9a6YMGLiFEhK51N4O3+mmIbt
MB0GA1UdDgQWBBTjTQZrKFQ5geqc4+9zl31SO5w4DTANBgkqhkiG9w0BAQ0FAAOC
AgEAFvvK0+jphYgn93vZDB06EG7wH4EsEe6dDmG8E2N+SCbjWigTYElemKOl5ION
i3G1Wluc2SEN2gHX4MRnHQNE7bxUAX8IhbKNW0+xcQX3BOT/mPbWsrrHkvkBTzO0
wQPIUVtGjRe1igL4b/Pv29fWsphOjHm1ZyqIr9V5Qk0hhOWsQ8i4zspTks0OGSml
OPIY4iB/DyvUWcwsI5ZhdyK1l0ZKKS8zxWWKT4BgRtR4WGFUArkb+7fWYpIl91CE
xE+TVTIhjuTyajZG1/wPtzI3TrQPjB5LKv5OvqroLMamuMeQG+CydFEOUb7z05Zk
f4PU6ryvB2hPAwnzqKtf+TNWimiuqPM06pEZRSpUKcoXzJiNIweICkoOllQDrx4m
g5pvjVT23FeDuEQWLiB9XMFmx46Thv1AGkP+gKbaLIjkxrfWEomEoY6/6VDeFjFl
FxEYSvnazQ5K5tBQ2rA46OvpdpbIifI+x1aaP45snKQ/wvoeRfXySV9B5S/ESzrM
xVgAY++/lteSzvUJaC+ZVKkvjIIznxNHnHG8GrFKPFZqoUS10zGKiYve9yXQITK2
n32heLzy+SM4Qcso43f+3n0R0KrdjOn3peXakfNEFaGkJQvMdeYQpZkALDHXpbGi
TA4C4qSTGP89TrEpGWpuAKuMHP5Vqf7y/EDr8Y4xP1TPRFo=
-----END CERTIFICATE-----

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