mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f85c8ebba0 | ||
|
|
e4e455630b | ||
|
|
d82beef937 | ||
|
|
c3e61e5e0f | ||
|
|
f04e46baf0 | ||
|
|
e93bd7e61c | ||
|
|
edf40976f0 | ||
|
|
e557f41c6e | ||
|
|
68fe816da9 | ||
|
|
367ca6e16b | ||
|
|
6c67f05ca7 | ||
|
|
4887e80995 | ||
|
|
74a338edfc | ||
|
|
8878ba4fd2 | ||
|
|
e53f5ccfea | ||
|
|
6e7eb62ea1 | ||
|
|
2bb61f96c0 | ||
|
|
a902fd9afe | ||
|
|
dc42460d82 | ||
|
|
6689e25865 | ||
|
|
20f6791fb4 | ||
|
|
f0a148fd0b | ||
|
|
390e48869e | ||
|
|
3635a19462 | ||
|
|
584e785f5c | ||
|
|
7fffdcfe8c | ||
|
|
44d6020774 | ||
|
|
30ce11b8da | ||
|
|
c7ab70df2c | ||
|
|
c953486dec | ||
|
|
610cc581da | ||
|
|
604c34e66b | ||
|
|
e2cd2e78c5 | ||
|
|
d04d331673 | ||
|
|
27f20495a7 | ||
|
|
679ab77b96 | ||
|
|
8bb20be528 | ||
|
|
d04ea8603d | ||
|
|
51d2c92067 | ||
|
|
7e3b8b8c16 | ||
|
|
f8fb9ce32a | ||
|
|
3d5f75d6c3 | ||
|
|
ddc8248b21 | ||
|
|
77083b1cbb | ||
|
|
21c2a04347 | ||
|
|
8f2d77c9d3 | ||
|
|
b6a4d7a6c1 | ||
|
|
f223069f0b | ||
|
|
19a5af8da9 | ||
|
|
03b73ab3f9 | ||
|
|
080a6678fb | ||
|
|
1d6f7d0bec | ||
|
|
a50d0485c9 | ||
|
|
d2a6a771a5 | ||
|
|
7568a0790f | ||
|
|
ca0ee966b8 | ||
|
|
36a7b2e6eb | ||
|
|
851f63eb03 | ||
|
|
3d679f61fc | ||
|
|
9237c5b675 | ||
|
|
d1ab6be082 | ||
|
|
d6c2bac99f | ||
|
|
997343f841 | ||
|
|
6a767408b3 | ||
|
|
3aa5c38714 | ||
|
|
c04a0919f6 | ||
|
|
c803cb925e | ||
|
|
1741ecff96 | ||
|
|
20de1fce2a | ||
|
|
a5af5bcf70 | ||
|
|
7730f2489f | ||
|
|
6dbc5646f9 | ||
|
|
f70991ab1e | ||
|
|
e9ee658492 | ||
|
|
35d5f41232 | ||
|
|
260a0a2428 | ||
|
|
b444e71ee3 | ||
|
|
31d576a595 | ||
|
|
68309ccb7f | ||
|
|
755ea88d5d | ||
|
|
af99efaab2 | ||
|
|
3b55ca085e | ||
|
|
9fdfd1a6dc | ||
|
|
c459ec08c6 | ||
|
|
eb63434f44 | ||
|
|
0eae712cc2 | ||
|
|
e7e9f137c1 | ||
|
|
364cb9d869 | ||
|
|
a64e9cab67 | ||
|
|
74cb6ee40e | ||
|
|
8c99c3878e | ||
|
|
f302f5734b | ||
|
|
3485192791 | ||
|
|
715da84030 | ||
|
|
6e53be736e | ||
|
|
88bf638487 | ||
|
|
182892da12 | ||
|
|
f9e07c3916 | ||
|
|
3aaf3c067e | ||
|
|
801e404160 | ||
|
|
280b8509d1 | ||
|
|
0f56416d82 | ||
|
|
c88e0276c1 | ||
|
|
5b7f08624b | ||
|
|
b94d5e8f87 | ||
|
|
a4f171d4ba | ||
|
|
93f8d093ce | ||
|
|
7e68d7f61a | ||
|
|
7755d35995 | ||
|
|
211431abc2 | ||
|
|
0ae29148f0 | ||
|
|
43cf7873be | ||
|
|
d8cea21e1e | ||
|
|
7093a70b51 | ||
|
|
06984cd9ee | ||
|
|
3a05ab769d | ||
|
|
bcbc69c77f | ||
|
|
b385b8cc38 | ||
|
|
2b282bdacd | ||
|
|
bd2610d275 | ||
|
|
5cf2cefa94 | ||
|
|
984e008127 | ||
|
|
fcfee1aa84 | ||
|
|
b401ad0da0 | ||
|
|
50f909db8f | ||
|
|
d0046257b6 | ||
|
|
daad3bf0e7 | ||
|
|
6537f711a6 | ||
|
|
7c75df9e96 | ||
|
|
376ddfbd22 | ||
|
|
1afa6efb26 | ||
|
|
69c89416b0 | ||
|
|
81e0d02d90 | ||
|
|
d41d968792 | ||
|
|
c81ca8f01b | ||
|
|
73ab25f419 | ||
|
|
6fc1ed1a67 | ||
|
|
1c3547d205 | ||
|
|
b6bb87f377 | ||
|
|
bd0bb9dd5c | ||
|
|
075f7f7186 | ||
|
|
5af1e8cc69 | ||
|
|
145c21a9ca | ||
|
|
1bd8ee4b79 | ||
|
|
4f0dfae97e | ||
|
|
3d6bb988c2 | ||
|
|
8f83002e37 | ||
|
|
d92f419836 | ||
|
|
8ff8b40182 |
52
.gitignore
vendored
52
.gitignore
vendored
@@ -25,7 +25,7 @@ compile_commands.json
|
|||||||
#
|
#
|
||||||
**/.detect-openssl.dep.c
|
**/.detect-openssl.dep.c
|
||||||
*.debug
|
*.debug
|
||||||
*/autocompletion_generator_host
|
**/autocompletion_generator_host
|
||||||
ap_tools/ap-check
|
ap_tools/ap-check
|
||||||
cmsfs-fuse/cmsfs-fuse
|
cmsfs-fuse/cmsfs-fuse
|
||||||
cpacfstats/cpacfstats
|
cpacfstats/cpacfstats
|
||||||
@@ -50,12 +50,22 @@ dasdfmt/dasdfmt
|
|||||||
dasdfmt/_dasdfmt
|
dasdfmt/_dasdfmt
|
||||||
dasdfmt/dasdfmt.bash
|
dasdfmt/dasdfmt.bash
|
||||||
dasdinfo/dasdinfo
|
dasdinfo/dasdinfo
|
||||||
|
dasdinfo/_dasdinfo
|
||||||
|
dasdinfo/dasdinfo.bash
|
||||||
dasdview/dasdview
|
dasdview/dasdview
|
||||||
|
dasdview/_dasdview
|
||||||
|
dasdview/dasdview.bash
|
||||||
dump2tar/src/dump2tar
|
dump2tar/src/dump2tar
|
||||||
|
dump2tar/src/_dump2tar
|
||||||
|
dump2tar/src/dump2tar.bash
|
||||||
fdasd/fdasd
|
fdasd/fdasd
|
||||||
|
fdasd/_fdasd
|
||||||
|
fdasd/fdasd.bash
|
||||||
hmcdrvfs/hmcdrvfs
|
hmcdrvfs/hmcdrvfs
|
||||||
hsavmcore/check-dep-fuse
|
hsavmcore/check-dep-fuse
|
||||||
hsavmcore/hsavmcore
|
hsavmcore/hsavmcore
|
||||||
|
hsavmcore/_hsavmcore
|
||||||
|
hsavmcore/hsavmcore.bash
|
||||||
hyptop/hyptop
|
hyptop/hyptop
|
||||||
ip_watcher/xcec-bridge
|
ip_watcher/xcec-bridge
|
||||||
ipl_tools/chreipl
|
ipl_tools/chreipl
|
||||||
@@ -88,34 +98,58 @@ libutil/*_example
|
|||||||
libvmcp/vmcp_example
|
libvmcp/vmcp_example
|
||||||
libzds/libzds.a
|
libzds/libzds.a
|
||||||
lsstp/lsstp
|
lsstp/lsstp
|
||||||
|
lsstp/_lsstp
|
||||||
|
lsstp/lsstp.bash
|
||||||
mon_tools/mon_fsstatd
|
mon_tools/mon_fsstatd
|
||||||
mon_tools/mon_procd
|
mon_tools/mon_procd
|
||||||
opticsmon/opticsmon
|
opticsmon/opticsmon
|
||||||
|
opticsmon/_opticsmon
|
||||||
|
opticsmon/opticsmon.bash
|
||||||
osasnmpd/osasnmpd
|
osasnmpd/osasnmpd
|
||||||
qetharp/qetharp
|
qetharp/qetharp
|
||||||
qethqoat/qethqoat
|
qethqoat/qethqoat
|
||||||
systemd/cpacfstatsd.service
|
systemd/cpacfstatsd.service
|
||||||
systemd/iucvtty-login@.service
|
systemd/iucvtty-login@.service
|
||||||
systemd/ttyrun-getty@.service
|
systemd/ttyrun-getty@.service
|
||||||
tape390/tape390_crypt
|
|
||||||
tape390/tape390_display
|
|
||||||
tunedasd/src/tunedasd
|
tunedasd/src/tunedasd
|
||||||
|
tunedasd/src/_tunedasd
|
||||||
|
tunedasd/src/tunedasd.bash
|
||||||
vmcp/vmcp
|
vmcp/vmcp
|
||||||
|
vmcp/_vmcp
|
||||||
|
vmcp/vmcp.bash
|
||||||
vmur/vmur
|
vmur/vmur
|
||||||
zconf/chp/chchp
|
zconf/chp/chchp
|
||||||
|
zconf/chp/_chchp
|
||||||
|
zconf/chp/chchp.bash
|
||||||
zconf/chp/lschp
|
zconf/chp/lschp
|
||||||
|
zconf/chp/_lschp
|
||||||
|
zconf/chp/lschp.bash
|
||||||
zconf/chp/chpstat/chpstat
|
zconf/chp/chpstat/chpstat
|
||||||
|
zconf/chp/chpstat/_chpstat
|
||||||
|
zconf/chp/chpstat/chpstat.bash
|
||||||
zconf/css/lscss
|
zconf/css/lscss
|
||||||
|
zconf/css/_lscss
|
||||||
|
zconf/css/lscss.bash
|
||||||
zconf/qeth/lsqeth
|
zconf/qeth/lsqeth
|
||||||
|
zconf/qeth/_lsqeth
|
||||||
|
zconf/qeth/lsqeth.bash
|
||||||
zconf/scm/lsscm
|
zconf/scm/lsscm
|
||||||
|
zconf/scm/_lsscm
|
||||||
|
zconf/scm/lsscm.bash
|
||||||
zconf/zcrypt/chzcrypt
|
zconf/zcrypt/chzcrypt
|
||||||
|
zconf/zcrypt/_chzcrypt
|
||||||
|
zconf/zcrypt/chzcrypt.bash
|
||||||
zconf/zcrypt/lszcrypt
|
zconf/zcrypt/lszcrypt
|
||||||
|
zconf/zcrypt/_lszcrypt
|
||||||
|
zconf/zcrypt/lszcrypt.bash
|
||||||
zconf/zcrypt/zcryptctl
|
zconf/zcrypt/zcryptctl
|
||||||
zconf/zcrypt/zcryptstats
|
zconf/zcrypt/zcryptstats
|
||||||
|
zconf/zcrypt/_zcryptstats
|
||||||
|
zconf/zcrypt/zcryptstats.bash
|
||||||
zdev/src/chzdev
|
zdev/src/chzdev
|
||||||
zdev/src/chzdev_usage.c
|
zdev/src/chzdev_usage.h
|
||||||
zdev/src/lszdev
|
zdev/src/lszdev
|
||||||
zdev/src/lszdev_usage.c
|
zdev/src/lszdev_usage.h
|
||||||
zdev/src/zdev_id
|
zdev/src/zdev_id
|
||||||
zdsfs/zdsfs
|
zdsfs/zdsfs
|
||||||
zdump/.check_dep_fuse
|
zdump/.check_dep_fuse
|
||||||
@@ -139,6 +173,8 @@ zipl/src/chreipl_helper.device-mapper
|
|||||||
zipl/src/chreipl_helper.md
|
zipl/src/chreipl_helper.md
|
||||||
zipl/src/zipl
|
zipl/src/zipl
|
||||||
zipl/src/zipl-editenv
|
zipl/src/zipl-editenv
|
||||||
|
zipl/src/_zipl-editenv
|
||||||
|
zipl/src/zipl-editenv.bash
|
||||||
zipl/src/zipl_helper.device-mapper
|
zipl/src/zipl_helper.device-mapper
|
||||||
zipl/src/zipl_helper.md
|
zipl/src/zipl_helper.md
|
||||||
zkey/check-dep-zkey
|
zkey/check-dep-zkey
|
||||||
@@ -151,5 +187,11 @@ zkey/kmip/zkey-kmip.so
|
|||||||
zkey/zkey
|
zkey/zkey
|
||||||
zkey/zkey-cryptsetup
|
zkey/zkey-cryptsetup
|
||||||
zmemtopo/zmemtopo
|
zmemtopo/zmemtopo
|
||||||
|
zmemtopo/_zmemtopo
|
||||||
|
zmemtopo/zmemtopo.bash
|
||||||
zpcictl/zpcictl
|
zpcictl/zpcictl
|
||||||
|
zpcictl/_zpcictl
|
||||||
|
zpcictl/zpcictl.bash
|
||||||
zpwr/zpwr
|
zpwr/zpwr
|
||||||
|
zpwr/_zpwr
|
||||||
|
zpwr/zpwr.bash
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
newline_style = "Unix"
|
newline_style = "Unix"
|
||||||
|
|
||||||
# Unstable options that help catching some mistakes in formatting and that we may want to enable
|
# Unstable options that help catching some mistakes in formatting
|
||||||
# when they become stable.
|
group_imports = "StdExternalCrate"
|
||||||
#
|
imports_granularity = "Module"
|
||||||
# They are kept here since they are useful to run from time to time.
|
use_field_init_shorthand = true
|
||||||
#comment_width = 100
|
comment_width = 100
|
||||||
#format_code_in_doc_comments = true
|
format_code_in_doc_comments = true
|
||||||
#group_imports = "StdExternalCrate"
|
normalize_comments = true
|
||||||
#normalize_comments = true
|
wrap_comments = true
|
||||||
#reorder_impl_items = true
|
#reorder_impl_items = true
|
||||||
#wrap_comments = true
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Bjoern Walk
|
- Bjoern Walk
|
||||||
- Brian C. Lane
|
- Brian C. Lane
|
||||||
- Carsten Otte
|
- Carsten Otte
|
||||||
|
- Chinmaya Kajagar
|
||||||
- Christian Borntraeger
|
- Christian Borntraeger
|
||||||
- Christian Ehrhardt
|
- Christian Ehrhardt
|
||||||
- Christof Schmitt
|
- Christof Schmitt
|
||||||
@@ -76,7 +77,6 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Jean-Baptiste Joret
|
- Jean-Baptiste Joret
|
||||||
- Jens Remus
|
- Jens Remus
|
||||||
- Jochen Roehrig
|
- Jochen Roehrig
|
||||||
- Joern Siglen
|
|
||||||
- Juergen Christ
|
- Juergen Christ
|
||||||
- Julian Wiedmann
|
- Julian Wiedmann
|
||||||
- Jörn Siglen
|
- Jörn Siglen
|
||||||
@@ -134,6 +134,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Steffen Eiden
|
- Steffen Eiden
|
||||||
- Steffen Maier
|
- Steffen Maier
|
||||||
- Steffen Thoss
|
- Steffen Thoss
|
||||||
|
- Stephen Gallagher
|
||||||
- Sumanth Korikkar
|
- Sumanth Korikkar
|
||||||
- Susanne Wintenberger
|
- Susanne Wintenberger
|
||||||
- Sven Schnelle
|
- Sven Schnelle
|
||||||
@@ -147,6 +148,7 @@ List of all individuals having contributed content to s390-tools
|
|||||||
- Thomas Spatzier
|
- Thomas Spatzier
|
||||||
- Thomas Weber
|
- Thomas Weber
|
||||||
- Thorsten Winkler
|
- Thorsten Winkler
|
||||||
|
- Timo Keller
|
||||||
- Tobias Huschle
|
- Tobias Huschle
|
||||||
- Tuan Hoang
|
- Tuan Hoang
|
||||||
- Ursula Braun
|
- Ursula Braun
|
||||||
|
|||||||
67
CHANGELOG.md
67
CHANGELOG.md
@@ -1,6 +1,73 @@
|
|||||||
Release history for s390-tools (MIT version)
|
Release history for s390-tools (MIT version)
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
* __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
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- cpumf/pai: Improve -m XXX argument verification
|
||||||
|
- pvattest: Add -i -o option variant for check
|
||||||
|
- pvattest: Show perform -i & -o option in help
|
||||||
|
- pvebc: Disable unit logging to /boot
|
||||||
|
- pvsecret: Add -i -o option variants
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- cpumf/pai: Remove unnecessary const parameter definition
|
||||||
|
- pv: Fix error description
|
||||||
|
- pvebc: Fix dependency for non EBC guests
|
||||||
|
- pvebc: Fix kernel module dependencies
|
||||||
|
- zipl: Don't modify job->data.dump and job->data.mvdump sequentially
|
||||||
|
|
||||||
|
* __v2.42.0 (2026-04-30)__
|
||||||
|
|
||||||
|
For Linux kernel version: 7.0
|
||||||
|
|
||||||
|
Add new tools / libraries:
|
||||||
|
- Enable zsh and bash autocompletion for various tools
|
||||||
|
- pvebc: Resolve ASR integrity structure for EBC
|
||||||
|
- pvics: Generate SEL guests from base images
|
||||||
|
|
||||||
|
Remove:
|
||||||
|
- tape390_display and tape390_crypt removed due to long gone hardware support
|
||||||
|
- znetcontrolunits: Remove znetcontrolunits library
|
||||||
|
|
||||||
|
Changes of existing tools:
|
||||||
|
- cpumf/pai: Install SIGINT/SIGTERM handler for graceful termination
|
||||||
|
- dbginfo.sh: Add command zmemtopo
|
||||||
|
- libutil/util_fmt: Add support for JSON Lines text format
|
||||||
|
- lstape: Remove 3480 and 3590 tape support
|
||||||
|
- lsznet: Remove support for lcs device type
|
||||||
|
- pvsecret: Add support for ASR integrity structure for EBC
|
||||||
|
- zfcpdbf: Print plogi and prli within open port response as payload
|
||||||
|
- zfcpdbf: Trace all fsf status read buffer fields under HBA
|
||||||
|
- zipl/boot: Add secure boot option to the dump programm
|
||||||
|
- zkey, libekmfweb, libseckey, libkmipclient: Adjust for OpenSSL v4.0.0 API
|
||||||
|
changes and deprecations
|
||||||
|
|
||||||
|
Bug Fixes:
|
||||||
|
- hyptop/opts: Fix long command line option abbreviations
|
||||||
|
- libutil/util_autocomp: Fix default file completion
|
||||||
|
- zipl/boot: Fix stage3 secure boot trailer placement
|
||||||
|
|
||||||
* __v2.41.0 (2026-02-16)__
|
* __v2.41.0 (2026-02-16)__
|
||||||
|
|
||||||
For Linux kernel version: 6.19
|
For Linux kernel version: 6.19
|
||||||
|
|||||||
2
Makefile
2
Makefile
@@ -11,7 +11,7 @@ BASELIB_DIRS = libutil libseckey
|
|||||||
LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \
|
LIB_DIRS = libvtoc libzds libdasd libccw libvmcp libekmfweb \
|
||||||
libkmipclient libcpumf libap libpv libzpci
|
libkmipclient libcpumf libap libpv libzpci
|
||||||
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
TOOL_DIRS = zipl zdump fdasd dasdfmt dasdview tunedasd \
|
||||||
tape390 osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
osasnmpd qetharp ip_watcher qethconf scripts zconf \
|
||||||
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
vmcp man mon_tools dasdinfo vmur cpuplugd ipl_tools \
|
||||||
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
ziomon iucvterm hyptop cmsfs-fuse qethqoat zfcpdump zdsfs cpumf \
|
||||||
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
systemd hmcdrvfs cpacfstats zdev dump2tar zkey netboot etc zpcictl \
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -30,6 +30,8 @@ Package contents
|
|||||||
Manage secrets for IBM Secure Execution guests
|
Manage secrets for IBM Secure Execution guests
|
||||||
- pvimg:
|
- pvimg:
|
||||||
Create and inspect IBM Secure Execution images
|
Create and inspect IBM Secure Execution images
|
||||||
|
- pvebc:
|
||||||
|
Verify a secret structure for IBM Secure Execution for Linux.
|
||||||
|
|
||||||
* dasdfmt:
|
* dasdfmt:
|
||||||
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
Low-level format ECKD DASDs with the classical Linux disk layout or the new
|
||||||
@@ -50,6 +52,10 @@ Package contents
|
|||||||
Create an IBM Secure Execution (protected virtualization) image. The
|
Create an IBM Secure Execution (protected virtualization) image. The
|
||||||
genprotimg command is a symbolic link to the `pvimg create` command.
|
genprotimg command is a symbolic link to the `pvimg create` command.
|
||||||
|
|
||||||
|
* pvics:
|
||||||
|
Convert a qcow2 image to a qcow2 image ready to boot as IBM Secure Execution
|
||||||
|
for Linux guest.
|
||||||
|
|
||||||
* udev rules:
|
* udev rules:
|
||||||
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
- 59-dasd.rules: rules for unique DASD device nodes created in /dev/disk/.
|
||||||
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
- 57-osasnmpd.rules: udev rules for osasnmpd.
|
||||||
@@ -79,13 +85,6 @@ Package contents
|
|||||||
* qetharp:
|
* qetharp:
|
||||||
Read and flush the ARP cache on OSA Express network cards.
|
Read and flush the ARP cache on OSA Express network cards.
|
||||||
|
|
||||||
* tape390_display:
|
|
||||||
Display information on the message display facility of a s390 tape
|
|
||||||
device.
|
|
||||||
|
|
||||||
* tape390_crypt:
|
|
||||||
Control and query crypto settings for 3592 tape devices.
|
|
||||||
|
|
||||||
* osasnmpd:
|
* osasnmpd:
|
||||||
NET-SNMP subagent implementing MIBs provided by OSA-Express
|
NET-SNMP subagent implementing MIBs provided by OSA-Express
|
||||||
features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet.
|
features Fast Ethernet, Gigabit Ethernet, 10 Gigabit Ethernet.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ endif
|
|||||||
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
# The variable "DISTRELEASE" should be overwritten in rpm spec files with:
|
||||||
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
# "make DISTRELEASE=%{release}" and "make install DISTRELEASE=%{release}"
|
||||||
VERSION := 2
|
VERSION := 2
|
||||||
RELEASE := 41
|
RELEASE := 43
|
||||||
PATCHLEVEL := 0
|
PATCHLEVEL := 0
|
||||||
DISTRELEASE := build-$(shell date +%Y%m%d)
|
DISTRELEASE := build-$(shell date +%Y%m%d)
|
||||||
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
S390_TOOLS_RELEASE := $(VERSION).$(RELEASE).$(PATCHLEVEL)-$(DISTRELEASE)
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
/*
|
/*
|
||||||
* Autocompletion generation - for cpumf family of tools
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lib/util_autocomp.h"
|
#include "lib/util_autocomp.h"
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* Command line utilities - for chcpumf
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CHCPUMF_CLI_H
|
#ifndef CHCPUMF_CLI_H
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* Command line utilities - for lscpumf
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LSCPUMF_CLI_H
|
#ifndef LSCPUMF_CLI_H
|
||||||
|
|||||||
@@ -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)
|
static void prepare_counter(size_t id, unsigned long value)
|
||||||
{
|
{
|
||||||
if (output_format == FMT_CSV) {
|
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");
|
util_fmt_obj_start(FMT_LIST, "counters");
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
struct ctr_filter_match match;
|
||||||
|
|
||||||
if (!ctrname[i].hitcnt)
|
if (!ctrname[i].hitcnt)
|
||||||
continue;
|
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;
|
continue;
|
||||||
prepare_counter(i, ctrname[i].ccv[h]);
|
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");
|
util_fmt_obj_start(FMT_LIST, "counters");
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
for (size_t i = 0; i < ARRAY_SIZE(ctrname); ++i) {
|
||||||
|
struct ctr_filter_match match;
|
||||||
|
|
||||||
if (!ctrname[i].hitcnt)
|
if (!ctrname[i].hitcnt)
|
||||||
continue;
|
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;
|
continue;
|
||||||
prepare_counter(i, ctrname[i].total);
|
prepare_counter(i, ctrname[i].total);
|
||||||
ctrname[i].total = 0;
|
ctrname[i].total = 0;
|
||||||
@@ -700,7 +742,7 @@ static int do_it(char *s)
|
|||||||
|
|
||||||
if (output_format == FMT_CSV)
|
if (output_format == FMT_CSV)
|
||||||
flags |= FMT_NOMETA;
|
flags |= FMT_NOMETA;
|
||||||
if (output_format == FMT_JSON || output_format == FMT_JSONSEQ)
|
if (util_fmt_is_json(output_format))
|
||||||
flags |= FMT_HANDLEINT;
|
flags |= FMT_HANDLEINT;
|
||||||
if (quote_all)
|
if (quote_all)
|
||||||
flags |= FMT_QUOTEALL;
|
flags |= FMT_QUOTEALL;
|
||||||
@@ -708,7 +750,7 @@ static int do_it(char *s)
|
|||||||
mk_labels();
|
mk_labels();
|
||||||
util_fmt_init(stdout, output_format, flags, 1);
|
util_fmt_init(stdout, output_format, flags, 1);
|
||||||
util_fmt_obj_start(FMT_DEFAULT, "lshwc");
|
util_fmt_obj_start(FMT_DEFAULT, "lshwc");
|
||||||
if (output_format == FMT_JSON || output_format == FMT_JSONSEQ) {
|
if (util_fmt_is_json(output_format)) {
|
||||||
util_fmt_obj_start(FMT_ROW, "cpumcf info");
|
util_fmt_obj_start(FMT_ROW, "cpumcf info");
|
||||||
util_fmt_pair(FMT_PERSIST, "counter first", "%d", cfvn);
|
util_fmt_pair(FMT_PERSIST, "counter first", "%d", cfvn);
|
||||||
util_fmt_pair(FMT_PERSIST, "counter second", "%d", csvn);
|
util_fmt_pair(FMT_PERSIST, "counter second", "%d", csvn);
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* Command line utilities - for lshwc
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LSHWC_CLI_H
|
#ifndef LSHWC_CLI_H
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* Command line utilities - for lspai
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LSPAI_CLI_H
|
#ifndef LSPAI_CLI_H
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ Apply quoting to every output element, regardless of content or format.
|
|||||||
.TP
|
.TP
|
||||||
.BR \-f ", " \-\-format \fI\ FORMAT\fP
|
.BR \-f ", " \-\-format \fI\ FORMAT\fP
|
||||||
Retrieve output in one of the following formats:
|
Retrieve output in one of the following formats:
|
||||||
JSON, CSV, JSON-SEQ or PAIRS.
|
JSON, CSV, JSON-SEQ, JSONL or PAIRS.
|
||||||
If no format is specified, the output defaults to CSV.
|
If no format is specified, the output defaults to CSV.
|
||||||
.
|
.
|
||||||
.SS JSON Output Structure
|
.SS JSON Output Structure
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ Default sort order is PAI counter name.
|
|||||||
.TP
|
.TP
|
||||||
.BI \-\-format "\ FORMAT"
|
.BI \-\-format "\ FORMAT"
|
||||||
Retrieve output in one of the following formats:
|
Retrieve output in one of the following formats:
|
||||||
JSON, csv, json-seq or pairs.
|
JSON, csv, json-seq, jsonl or pairs.
|
||||||
If no format is specified,
|
If no format is specified,
|
||||||
the output defaults to a human-readable format.
|
the output defaults to a human-readable format.
|
||||||
.
|
.
|
||||||
|
|||||||
41
cpumf/pai.c
41
cpumf/pai.c
@@ -14,6 +14,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -47,6 +48,8 @@
|
|||||||
#define S390_EVT_PAI_CRYPTO 0x1000
|
#define S390_EVT_PAI_CRYPTO 0x1000
|
||||||
#define S390_EVT_PAI_NNPA 0x1800
|
#define S390_EVT_PAI_NNPA 0x1800
|
||||||
|
|
||||||
|
/* SIGINT or SIGTERM signal received */
|
||||||
|
static volatile unsigned int sigterm;
|
||||||
/* Default values for select() timeout: 1 second */
|
/* Default values for select() timeout: 1 second */
|
||||||
static unsigned long read_interval = 1000;
|
static unsigned long read_interval = 1000;
|
||||||
/* Size of mapped perf event ring buffer in 4KB pages.
|
/* Size of mapped perf event ring buffer in 4KB pages.
|
||||||
@@ -350,6 +353,10 @@ static int collect(unsigned long cnt)
|
|||||||
if (FD_ISSET(i, &r_fds))
|
if (FD_ISSET(i, &r_fds))
|
||||||
readmap(i);
|
readmap(i);
|
||||||
}
|
}
|
||||||
|
} else if (errno == EINTR && sigterm) {
|
||||||
|
/* Interrupt by signal SIGINT/SIGTERM, one more iteration */
|
||||||
|
cnt = 2;
|
||||||
|
rc = 0;
|
||||||
}
|
}
|
||||||
} while (rc != -1 && --cnt > 0);
|
} while (rc != -1 && --cnt > 0);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -887,7 +894,7 @@ static int parse_event_attr(char *cp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Parse CPU list and event specifications */
|
/* Parse CPU list and event specifications */
|
||||||
static void parse_cpulist(int enr, const char *parm)
|
static void parse_cpulist(int enr, char *parm)
|
||||||
{
|
{
|
||||||
unsigned int evt_attr = 0;
|
unsigned int evt_attr = 0;
|
||||||
cpu_set_t cmdlist, result;
|
cpu_set_t cmdlist, result;
|
||||||
@@ -941,33 +948,27 @@ static const struct util_prg prg = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static void record_cpus_crypto(const char *cp)
|
static void record_cpus_crypto(char *cp)
|
||||||
{
|
{
|
||||||
if (!libcpumf_have_pai_crypto())
|
if (!libcpumf_have_pai_crypto())
|
||||||
errx(EXIT_FAILURE, "No support for PAI crypto counters");
|
errx(EXIT_FAILURE, "No support for PAI crypto counters");
|
||||||
parse_cpulist(S390_EVT_PAI_CRYPTO, cp);
|
parse_cpulist(S390_EVT_PAI_CRYPTO, cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void record_cpus_nnpa(const char *cp)
|
static void record_cpus_nnpa(char *cp)
|
||||||
{
|
{
|
||||||
if (!libcpumf_have_pai_nnpa())
|
if (!libcpumf_have_pai_nnpa())
|
||||||
errx(EXIT_FAILURE, "No support for PAI nnpa counters");
|
errx(EXIT_FAILURE, "No support for PAI nnpa counters");
|
||||||
parse_cpulist(S390_EVT_PAI_NNPA, cp);
|
parse_cpulist(S390_EVT_PAI_NNPA, cp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mapsize must be power of 2 and larger than 4. Count bits in n and
|
/* Mapsize must be power of 2 and larger than 4. Return true in this case.
|
||||||
* return 0 if input is invalid and has a bit count larger than one.
|
|
||||||
*/
|
*/
|
||||||
static unsigned long check_mapsize(unsigned long n)
|
static bool check_mapsize(unsigned long n)
|
||||||
{
|
{
|
||||||
int bit, cnt = 0;
|
|
||||||
|
|
||||||
if (n < 4)
|
if (n < 4)
|
||||||
return 0;
|
return 0;
|
||||||
for (bit = 0; bit < __BITS_PER_LONG; ++bit)
|
return (n & (n - 1)) == 0;
|
||||||
if (n & (1 << bit))
|
|
||||||
++cnt;
|
|
||||||
return cnt == 1 ? n : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setprio(const char *prio)
|
static void setprio(const char *prio)
|
||||||
@@ -983,6 +984,12 @@ static void setprio(const char *prio)
|
|||||||
err(EXIT_FAILURE, "Could not set realtime priority");
|
err(EXIT_FAILURE, "Could not set realtime priority");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sig_handler(int no)
|
||||||
|
{
|
||||||
|
if (no == SIGINT || no == SIGTERM)
|
||||||
|
sigterm = 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
bool crypto_record = false, report = false;
|
bool crypto_record = false, report = false;
|
||||||
@@ -1027,11 +1034,11 @@ int main(int argc, char **argv)
|
|||||||
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
errno = 0;
|
|
||||||
mapsize = strtoul(optarg, &slash, 0);
|
mapsize = strtoul(optarg, &slash, 0);
|
||||||
mapsize = check_mapsize(mapsize);
|
if (!mapsize || *slash)
|
||||||
if (errno || !mapsize || *slash)
|
|
||||||
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
errx(EXIT_FAILURE, "Invalid argument for -%c", ch);
|
||||||
|
if (!check_mapsize(mapsize))
|
||||||
|
errx(EXIT_FAILURE, "No power of 2 number for -%c", ch);
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
record_cpus_nnpa(optarg);
|
record_cpus_nnpa(optarg);
|
||||||
@@ -1070,6 +1077,10 @@ int main(int argc, char **argv)
|
|||||||
errx(EXIT_FAILURE, "Invalid argument for runtime");
|
errx(EXIT_FAILURE, "Invalid argument for runtime");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signal(SIGTERM, sig_handler) == SIG_ERR ||
|
||||||
|
signal(SIGINT, sig_handler) == SIG_ERR)
|
||||||
|
errx(EXIT_FAILURE, "Failed to set signal handler");
|
||||||
|
|
||||||
ev_install(group);
|
ev_install(group);
|
||||||
ev_enable();
|
ev_enable();
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* Command line utilities - for pai
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PAI_CLI_H
|
#ifndef PAI_CLI_H
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
/*
|
/*
|
||||||
* Autocompletion generation - for dasdfmt tool
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "lib/util_autocomp.h"
|
#include "lib/util_autocomp.h"
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* Command line utilities - for dasdfmt
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright IBM Corp. 2025
|
* Copyright IBM Corp.
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DASDFMT_CLI_H
|
#ifndef DASDFMT_CLI_H
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _dasdinfo
|
||||||
|
bash-completions = dasdinfo.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
libs = $(rootdir)/libutil/libutil.a \
|
libs = $(rootdir)/libutil/libutil.a \
|
||||||
$(rootdir)/libdasd/libdasd.a
|
$(rootdir)/libdasd/libdasd.a
|
||||||
|
|
||||||
|
|||||||
16
dasdinfo/autocompletion_generator_host.c
Normal file
16
dasdinfo/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "dasdinfo_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "dasdinfo");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
#include "lib/util_path.h"
|
#include "lib/util_path.h"
|
||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
|
#include "dasdinfo_cli.h"
|
||||||
|
|
||||||
#define RD_BUFFER_SIZE 80
|
#define RD_BUFFER_SIZE 80
|
||||||
#define TEMP_DEV_MAX_RETRIES 1000
|
#define TEMP_DEV_MAX_RETRIES 1000
|
||||||
|
|
||||||
@@ -50,49 +52,6 @@ static const struct util_prg prg = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("DEVICE"),
|
|
||||||
{
|
|
||||||
.option = { "block", required_argument, NULL, 'b' },
|
|
||||||
.argument = "BLOCKDEV",
|
|
||||||
.desc = "Block device name, e.g. dasdb",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "devnode", required_argument, NULL, 'd' },
|
|
||||||
.argument = "DEVNODE",
|
|
||||||
.desc = "Device node, e.g. /dev/dasda",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "busid", required_argument, NULL, 'i' },
|
|
||||||
.argument = "BUSID",
|
|
||||||
.desc = "Bus ID, e.g. 0.0.e910",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "label", no_argument, NULL, 'l' },
|
|
||||||
.desc = "Print DASD volume label (volser)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "uid", no_argument, NULL, 'u' },
|
|
||||||
.desc = "Print DASD uid (without z/VM minidisk token)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "extended-uid", no_argument, NULL, 'x' },
|
|
||||||
.desc = "Print DASD uid (including z/VM minidisk token)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "all", no_argument, NULL, 'a' },
|
|
||||||
.desc = "Same as -u -x -l",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "export", no_argument, NULL, 'e' },
|
|
||||||
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
/* needed because ftw can not pass arbitrary arguments */
|
/* needed because ftw can not pass arbitrary arguments */
|
||||||
static char *searchbusid;
|
static char *searchbusid;
|
||||||
static char *busiddir;
|
static char *busiddir;
|
||||||
@@ -101,9 +60,9 @@ struct volume_label {
|
|||||||
char volkey[4];
|
char volkey[4];
|
||||||
char vollbl[4];
|
char vollbl[4];
|
||||||
char volid[6];
|
char volid[6];
|
||||||
} __attribute__ ((packed));
|
} __packed;
|
||||||
|
|
||||||
static char EBCtoASC[256] = {
|
static char ebc_to_asc[256] = {
|
||||||
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
||||||
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
||||||
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
||||||
@@ -175,7 +134,7 @@ static char *dinfo_ebcdic_dec(char *source, char *target, int l)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < l; i++)
|
for (i = 0; i < l; i++)
|
||||||
target[i] = EBCtoASC[(unsigned char)(source[i])];
|
target[i] = ebc_to_asc[(unsigned char)(source[i])];
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
@@ -261,7 +220,7 @@ static void *dinfo_malloc(size_t size)
|
|||||||
void *result;
|
void *result;
|
||||||
|
|
||||||
result = malloc(size);
|
result = malloc(size);
|
||||||
if (result == NULL)
|
if (!result)
|
||||||
warnx("Could not allocate %lu bytes of memory", size);
|
warnx("Could not allocate %lu bytes of memory", size);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -274,7 +233,7 @@ static char *dinfo_make_path(char *dirname, char *filename)
|
|||||||
|
|
||||||
len = strlen(dirname) + strlen(filename) + 2;
|
len = strlen(dirname) + strlen(filename) + 2;
|
||||||
result = (char *)dinfo_malloc(len);
|
result = (char *)dinfo_malloc(len);
|
||||||
if (result == NULL)
|
if (!result)
|
||||||
return NULL;
|
return NULL;
|
||||||
sprintf(result, "%s/%s", dirname, filename);
|
sprintf(result, "%s/%s", dirname, filename);
|
||||||
return result;
|
return result;
|
||||||
@@ -296,12 +255,12 @@ static int dinfo_create_devnode(dev_t dev, char **devno)
|
|||||||
|
|
||||||
/* Try several locations for the temporary device node. */
|
/* Try several locations for the temporary device node. */
|
||||||
for (path = 0; path < ARRAY_SIZE(pathname); path++) {
|
for (path = 0; path < ARRAY_SIZE(pathname); path++) {
|
||||||
if (pathname[path] == NULL)
|
if (!pathname[path])
|
||||||
continue;
|
continue;
|
||||||
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
for (retry = 0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
||||||
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
|
snprintf(filename, sizeof(filename), "dasdinfo%04d", retry);
|
||||||
result = dinfo_make_path(pathname[path], filename);
|
result = dinfo_make_path(pathname[path], filename);
|
||||||
if (result == NULL)
|
if (!result)
|
||||||
return -1;
|
return -1;
|
||||||
rc = mknod(result, mode, dev);
|
rc = mknod(result, mode, dev);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
@@ -342,7 +301,7 @@ static int dinfo_extract_dev(dev_t *dev, char *str)
|
|||||||
memset(tmp, 0, RD_BUFFER_SIZE);
|
memset(tmp, 0, RD_BUFFER_SIZE);
|
||||||
util_strlcpy(tmp, str, RD_BUFFER_SIZE);
|
util_strlcpy(tmp, str, RD_BUFFER_SIZE);
|
||||||
p = strchr(tmp, ':');
|
p = strchr(tmp, ':');
|
||||||
if (p == NULL) {
|
if (!p) {
|
||||||
warnx("Error: unable to extract major/minor");
|
warnx("Error: unable to extract major/minor");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -401,14 +360,14 @@ dinfo_is_busiddir(const char *fpath, const struct stat *UNUSED(sb),
|
|||||||
return -1;
|
return -1;
|
||||||
linkdir = util_readlink(tempdir);
|
linkdir = util_readlink(tempdir);
|
||||||
free(tempdir);
|
free(tempdir);
|
||||||
if (strstr(linkdir, "dasd") == NULL) {
|
if (!strstr(linkdir, "dasd")) {
|
||||||
free(linkdir);
|
free(linkdir);
|
||||||
return FTW_CONTINUE;
|
return FTW_CONTINUE;
|
||||||
}
|
}
|
||||||
free(linkdir);
|
free(linkdir);
|
||||||
free(busiddir);
|
free(busiddir);
|
||||||
busiddir = strdup(fpath);
|
busiddir = strdup(fpath);
|
||||||
if (busiddir == NULL)
|
if (!busiddir)
|
||||||
return -1;
|
return -1;
|
||||||
return FTW_STOP;
|
return FTW_STOP;
|
||||||
}
|
}
|
||||||
@@ -421,7 +380,7 @@ dinfo_find_entry(const char *dir, const char *searchstring,
|
|||||||
struct dirent *dir_entry = NULL;
|
struct dirent *dir_entry = NULL;
|
||||||
|
|
||||||
directory = opendir(dir);
|
directory = opendir(dir);
|
||||||
if (directory == NULL)
|
if (!directory)
|
||||||
return -1;
|
return -1;
|
||||||
while ((dir_entry = readdir(directory)) != NULL) {
|
while ((dir_entry = readdir(directory)) != NULL) {
|
||||||
/* compare if the found entry has exactly the same name and type
|
/* compare if the found entry has exactly the same name and type
|
||||||
@@ -431,7 +390,7 @@ dinfo_find_entry(const char *dir, const char *searchstring,
|
|||||||
strlen(searchstring)) == 0) &&
|
strlen(searchstring)) == 0) &&
|
||||||
(dir_entry->d_type & type)) {
|
(dir_entry->d_type & type)) {
|
||||||
*result = strdup(dir_entry->d_name);
|
*result = strdup(dir_entry->d_name);
|
||||||
if (*result == NULL)
|
if (!*result)
|
||||||
goto out;
|
goto out;
|
||||||
closedir(directory);
|
closedir(directory);
|
||||||
return 0; /* found */
|
return 0; /* found */
|
||||||
@@ -477,7 +436,7 @@ dinfo_get_blockdev_from_busid(char *busid, char **blkdev)
|
|||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
goto out2;
|
goto out2;
|
||||||
*blkdev = strdup(strchr(result, ':') + 1);
|
*blkdev = strdup(strchr(result, ':') + 1);
|
||||||
if (*blkdev == NULL)
|
if (!*blkdev)
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,7 +469,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
|||||||
|
|
||||||
path = util_path_sysfs("block/");
|
path = util_path_sysfs("block/");
|
||||||
directory = opendir(path);
|
directory = opendir(path);
|
||||||
if (directory == NULL) {
|
if (!directory) {
|
||||||
warnx("Error: could not open directory %s", path);
|
warnx("Error: could not open directory %s", path);
|
||||||
free(path);
|
free(path);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -536,6 +495,7 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
|||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Error: Device name was truncated\n");
|
"Error: Device name was truncated\n");
|
||||||
free(path);
|
free(path);
|
||||||
|
free(readbuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,13 +505,12 @@ static int dinfo_get_uid_from_devnode(char **uidfile, char *devnode)
|
|||||||
|
|
||||||
closedir(directory);
|
closedir(directory);
|
||||||
free(path);
|
free(path);
|
||||||
|
free(readbuf);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct utsname uname_buf;
|
|
||||||
int version, release;
|
|
||||||
char *uidfile = NULL;
|
char *uidfile = NULL;
|
||||||
char *device = NULL;
|
char *device = NULL;
|
||||||
char *readbuf = NULL;
|
char *readbuf = NULL;
|
||||||
@@ -611,35 +570,19 @@ int main(int argc, char *argv[])
|
|||||||
util_prg_print_version();
|
util_prg_print_version();
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Try 'dasdinfo --help' for more "
|
fprintf(stderr, "Try 'dasdinfo --help' for more information.\n");
|
||||||
"information.\n");
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uname(&uname_buf);
|
if (!busid && !blockdev && !devnode)
|
||||||
sscanf(uname_buf.release, "%d.%d", &version, &release);
|
errx(EXIT_FAILURE, "Error: please specify a device using either -b, -i or -d");
|
||||||
if (strcmp(uname_buf.sysname, "Linux") ||
|
|
||||||
version < 2 || (version == 2 && release < 6)) {
|
|
||||||
warnx("%s %d.%d is not supported", uname_buf.sysname,
|
|
||||||
version, release);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!busid && !blockdev && !devnode) {
|
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode))
|
||||||
warnx("Error: please specify a device using either -b, -i or -d");
|
errx(EXIT_FAILURE, "Error: please specify device only once, either -b, -i or -d");
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((busid && blockdev) || (busid && devnode) || (blockdev && devnode)) {
|
if (!print_uid && !print_extended_uid && !print_vlabel)
|
||||||
warnx("Error: please specify device only once, either -b, -i or -d");
|
errx(EXIT_FAILURE, "Error: no action specified (e.g. -u)");
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!print_uid && !print_extended_uid && !print_vlabel) {
|
|
||||||
warnx("Error: no action specified (e.g. -u)");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
readbuf = dinfo_malloc(RD_BUFFER_SIZE);
|
||||||
if (!readbuf)
|
if (!readbuf)
|
||||||
|
|||||||
55
dasdinfo/dasdinfo_cli.h
Normal file
55
dasdinfo/dasdinfo_cli.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DASDINFO_CLI_H
|
||||||
|
#define DASDINFO_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("DEVICE"),
|
||||||
|
{
|
||||||
|
.option = { "block", required_argument, NULL, 'b' },
|
||||||
|
.argument = "BLOCKDEV",
|
||||||
|
.desc = "Block device name, e.g. dasdb",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "devnode", required_argument, NULL, 'd' },
|
||||||
|
.argument = "DEVNODE",
|
||||||
|
.desc = "Device node, e.g. /dev/dasda",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "busid", required_argument, NULL, 'i' },
|
||||||
|
.argument = "BUSID",
|
||||||
|
.desc = "Bus ID, e.g. 0.0.e910",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "label", no_argument, NULL, 'l' },
|
||||||
|
.desc = "Print DASD volume label (volser)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "uid", no_argument, NULL, 'u' },
|
||||||
|
.desc = "Print DASD uid (without z/VM minidisk token)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "extended-uid", no_argument, NULL, 'x' },
|
||||||
|
.desc = "Print DASD uid (including z/VM minidisk token)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "all", no_argument, NULL, 'a' },
|
||||||
|
.desc = "Same as -u -x -l",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "export", no_argument, NULL, 'e' },
|
||||||
|
.desc = "Export ID_BUS, ID_TYPE, ID_SERIAL for use in udev",
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _dasdview
|
||||||
|
bash-completions = dasdview.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
libs = $(rootdir)/libdasd/libdasd.a \
|
libs = $(rootdir)/libdasd/libdasd.a \
|
||||||
$(rootdir)/libzds/libzds.a \
|
$(rootdir)/libzds/libzds.a \
|
||||||
$(rootdir)/libvtoc/libvtoc.a \
|
$(rootdir)/libvtoc/libvtoc.a \
|
||||||
|
|||||||
16
dasdview/autocompletion_generator_host.c
Normal file
16
dasdview/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "dasdview_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "dasdview");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#include "dasdview.h"
|
#include "dasdview.h"
|
||||||
|
#include "dasdview_cli.h"
|
||||||
|
|
||||||
/* Characters per line */
|
/* Characters per line */
|
||||||
#define DASDVIEW_CPL 16
|
#define DASDVIEW_CPL 16
|
||||||
@@ -56,61 +57,6 @@ static const struct util_prg prg = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("DUMP OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { NULL, no_argument, NULL, '1' },
|
|
||||||
.desc = "Show DASD content in short Hex/EBCDIC/ASCII format",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { NULL, no_argument, NULL, '2' },
|
|
||||||
.desc = "Show DASD content in detailed Hex/EBCDIC/ASCII format",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOLONG,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "begin", required_argument, NULL, 'b' },
|
|
||||||
.argument = "BEGIN",
|
|
||||||
.desc = "Specify start of dump in kilobytes (suffix k), "
|
|
||||||
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "size", required_argument, NULL, 's' },
|
|
||||||
.argument = "SIZE",
|
|
||||||
.desc = "Specify size of dump in kilobytes (suffix k), "
|
|
||||||
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("MISC"),
|
|
||||||
{
|
|
||||||
.option = { "characteristic", no_argument, NULL, 'c' },
|
|
||||||
.desc = "Print the characteristics of a device",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "info", no_argument, NULL, 'i' },
|
|
||||||
.desc = "Print general DASD information and geometry",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "volser", no_argument, NULL, 'j' },
|
|
||||||
.desc = "Print the volume serial number",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "label", no_argument, NULL, 'l' },
|
|
||||||
.desc = "Print information about the volume label",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "vtoc", required_argument, NULL, 't' },
|
|
||||||
.argument = "SPEC",
|
|
||||||
.desc = "Print the table of content (VTOC)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "extended", no_argument, NULL, 'x' },
|
|
||||||
.desc = "Print extended DASD information",
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate and print an error message based on the formatted
|
* Generate and print an error message based on the formatted
|
||||||
* text string FMT and a variable amount of extra arguments.
|
* text string FMT and a variable amount of extra arguments.
|
||||||
|
|||||||
67
dasdview/dasdview_cli.h
Normal file
67
dasdview/dasdview_cli.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DASDVIEW_CLI_H
|
||||||
|
#define DASDVIEW_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("DUMP OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { NULL, no_argument, NULL, '1' },
|
||||||
|
.desc = "Show DASD content in short Hex/EBCDIC/ASCII format",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { NULL, no_argument, NULL, '2' },
|
||||||
|
.desc = "Show DASD content in detailed Hex/EBCDIC/ASCII format",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOLONG,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "begin", required_argument, NULL, 'b' },
|
||||||
|
.argument = "BEGIN",
|
||||||
|
.desc = "Specify start of dump in kilobytes (suffix k), "
|
||||||
|
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "size", required_argument, NULL, 's' },
|
||||||
|
.argument = "SIZE",
|
||||||
|
.desc = "Specify size of dump in kilobytes (suffix k), "
|
||||||
|
"megabytes (m), blocks (b), tracks (t), or cylinders (c)",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("MISC"),
|
||||||
|
{
|
||||||
|
.option = { "characteristic", no_argument, NULL, 'c' },
|
||||||
|
.desc = "Print the characteristics of a device",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "info", no_argument, NULL, 'i' },
|
||||||
|
.desc = "Print general DASD information and geometry",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "volser", no_argument, NULL, 'j' },
|
||||||
|
.desc = "Print the volume serial number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "label", no_argument, NULL, 'l' },
|
||||||
|
.desc = "Print information about the volume label",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "vtoc", required_argument, NULL, 't' },
|
||||||
|
.argument = "SPEC",
|
||||||
|
.desc = "Print the table of content (VTOC)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "extended", no_argument, NULL, 'x' },
|
||||||
|
.desc = "Print extended DASD information",
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
136
dump2tar/include/dump2tar_cli.h
Normal file
136
dump2tar/include/dump2tar_cli.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef DUMP2TAR_CLI_H
|
||||||
|
#define DUMP2TAR_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
#define OPT_NOSHORT_BASE 256
|
||||||
|
|
||||||
|
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
|
||||||
|
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
|
||||||
|
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
|
||||||
|
|
||||||
|
/* Definition of command line options */
|
||||||
|
static struct util_opt dump2tar_opts[] = {
|
||||||
|
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "output-file", required_argument, NULL, 'o' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Write archive to FILE (default: standard output)",
|
||||||
|
},
|
||||||
|
#ifdef HAVE_ZLIB
|
||||||
|
{
|
||||||
|
.option = { "gzip", no_argument, NULL, 'z' },
|
||||||
|
.desc = "Write a gzip compressed archive",
|
||||||
|
},
|
||||||
|
#endif /* HAVE_ZLIB */
|
||||||
|
{
|
||||||
|
.option = { "max-size", required_argument, NULL, 'm' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Stop adding files when archive size exceeds N bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "timeout", required_argument, NULL, 't' },
|
||||||
|
.argument = "SEC",
|
||||||
|
.desc = "Stop adding files after SEC seconds",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "no-eof", no_argument, NULL, 131 },
|
||||||
|
.desc = "Do not write an end-of-file marker",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "add-cmd-status", no_argument, NULL, 132 },
|
||||||
|
.desc = "Add status of commands as separate file",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "append", no_argument, NULL, 133 },
|
||||||
|
.desc = "Append output to end of file",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
|
||||||
|
UTIL_OPT_SECTION("INPUT OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "files-from", required_argument, NULL, 'F' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Read filenames from FILE (- for standard input)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
|
||||||
|
.desc = "Continue after read errors",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "buffer-size", required_argument, NULL, 'b' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Read data in chunks of N byte (default: 16384)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "file-timeout", required_argument, NULL, 'T' },
|
||||||
|
.desc = "Stop reading file after SEC seconds",
|
||||||
|
.argument = "SEC",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "file-max-size", required_argument, NULL, 'M' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Stop reading file after N bytes",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "jobs", required_argument, NULL, 'j' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Read N files in parallel (default: 1)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
|
||||||
|
.argument = "N",
|
||||||
|
.desc = "Read N files per CPU in parallel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "exclude", required_argument, NULL, 'x' },
|
||||||
|
.argument = "PATTERN",
|
||||||
|
.desc = "Don't add files matching PATTERN",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "exclude-from", required_argument, NULL, 'X' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Don't add files matching patterns in FILE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "exclude-type", required_argument, NULL,
|
||||||
|
OPT_EXCLUDETYPE },
|
||||||
|
.argument = "TYPE",
|
||||||
|
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
|
||||||
|
.desc = "Add link targets instead of links",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "no-recursion", no_argument, NULL,
|
||||||
|
OPT_NORECURSION },
|
||||||
|
.desc = "Don't add files from sub-directories",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
|
||||||
|
UTIL_OPT_SECTION("MISC OPTIONS"),
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
{
|
||||||
|
.option = { "verbose", no_argument, NULL, 'V' },
|
||||||
|
.desc = "Print additional informational output",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "quiet", no_argument, NULL, 'q' },
|
||||||
|
.desc = "Suppress printing of informational output",
|
||||||
|
},
|
||||||
|
UTIL_OPT_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
# Common definitions
|
# Common definitions
|
||||||
include ../../common.mak
|
include ../../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _dump2tar
|
||||||
|
bash-completions = dump2tar.bash
|
||||||
|
|
||||||
|
include ../../common_autocomp.mak
|
||||||
|
|
||||||
ALL_CPPFLAGS += -I../include -Wno-unused-parameter
|
ALL_CPPFLAGS += -I../include -Wno-unused-parameter
|
||||||
LDLIBS += -lpthread -lrt
|
LDLIBS += -lpthread -lrt
|
||||||
ifneq ($(HAVE_ZLIB),0)
|
ifneq ($(HAVE_ZLIB),0)
|
||||||
|
|||||||
16
dump2tar/src/autocompletion_generator_host.c
Normal file
16
dump2tar/src/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "../include/dump2tar_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(dump2tar_opts, "dump2tar");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "lib/util_prg.h"
|
#include "lib/util_prg.h"
|
||||||
|
|
||||||
#include "dump.h"
|
#include "dump.h"
|
||||||
|
#include "dump2tar_cli.h"
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "idcache.h"
|
#include "idcache.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
@@ -28,12 +29,6 @@
|
|||||||
|
|
||||||
#define MIN_BUFFER_SIZE 4096
|
#define MIN_BUFFER_SIZE 4096
|
||||||
|
|
||||||
#define OPT_NOSHORT_BASE 256
|
|
||||||
|
|
||||||
#define OPT_DEREFERENCE (OPT_NOSHORT_BASE + 0)
|
|
||||||
#define OPT_NORECURSION (OPT_NOSHORT_BASE + 1)
|
|
||||||
#define OPT_EXCLUDETYPE (OPT_NOSHORT_BASE + 2)
|
|
||||||
|
|
||||||
/* Program description */
|
/* Program description */
|
||||||
static const struct util_prg dump2tar_prg = {
|
static const struct util_prg dump2tar_prg = {
|
||||||
.desc = "Use dump2tar to create a tar archive from the contents "
|
.desc = "Use dump2tar to create a tar archive from the contents "
|
||||||
@@ -52,124 +47,6 @@ static const struct util_prg dump2tar_prg = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Definition of command line options */
|
|
||||||
static struct util_opt dump2tar_opts[] = {
|
|
||||||
UTIL_OPT_SECTION("OUTPUT OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "output-file", required_argument, NULL, 'o' },
|
|
||||||
.argument = "FILE",
|
|
||||||
.desc = "Write archive to FILE (default: standard output)",
|
|
||||||
},
|
|
||||||
#ifdef HAVE_ZLIB
|
|
||||||
{
|
|
||||||
.option = { "gzip", no_argument, NULL, 'z' },
|
|
||||||
.desc = "Write a gzip compressed archive",
|
|
||||||
},
|
|
||||||
#endif /* HAVE_ZLIB */
|
|
||||||
{
|
|
||||||
.option = { "max-size", required_argument, NULL, 'm' },
|
|
||||||
.argument = "N",
|
|
||||||
.desc = "Stop adding files when archive size exceeds N bytes",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "timeout", required_argument, NULL, 't' },
|
|
||||||
.argument = "SEC",
|
|
||||||
.desc = "Stop adding files after SEC seconds",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "no-eof", no_argument, NULL, 131 },
|
|
||||||
.desc = "Do not write an end-of-file marker",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "add-cmd-status", no_argument, NULL, 132 },
|
|
||||||
.desc = "Add status of commands as separate file",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "append", no_argument, NULL, 133 },
|
|
||||||
.desc = "Append output to end of file",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
|
|
||||||
UTIL_OPT_SECTION("INPUT OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "files-from", required_argument, NULL, 'F' },
|
|
||||||
.argument = "FILE",
|
|
||||||
.desc = "Read filenames from FILE (- for standard input)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "ignore-failed-read", no_argument, NULL, 'i' },
|
|
||||||
.desc = "Continue after read errors",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "buffer-size", required_argument, NULL, 'b' },
|
|
||||||
.argument = "N",
|
|
||||||
.desc = "Read data in chunks of N byte (default: 16384)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "file-timeout", required_argument, NULL, 'T' },
|
|
||||||
.desc = "Stop reading file after SEC seconds",
|
|
||||||
.argument = "SEC",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "file-max-size", required_argument, NULL, 'M' },
|
|
||||||
.argument = "N",
|
|
||||||
.desc = "Stop reading file after N bytes",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "jobs", required_argument, NULL, 'j' },
|
|
||||||
.argument = "N",
|
|
||||||
.desc = "Read N files in parallel (default: 1)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "jobs-per-cpu", required_argument, NULL, 'J' },
|
|
||||||
.argument = "N",
|
|
||||||
.desc = "Read N files per CPU in parallel",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "exclude", required_argument, NULL, 'x' },
|
|
||||||
.argument = "PATTERN",
|
|
||||||
.desc = "Don't add files matching PATTERN",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "exclude-from", required_argument, NULL, 'X' },
|
|
||||||
.argument = "FILE",
|
|
||||||
.desc = "Don't add files matching patterns in FILE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "exclude-type", required_argument, NULL,
|
|
||||||
OPT_EXCLUDETYPE },
|
|
||||||
.argument = "TYPE",
|
|
||||||
.desc = "Don't add files of specified TYPE (one of: fdcbpls)",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "dereference", no_argument, NULL, OPT_DEREFERENCE },
|
|
||||||
.desc = "Add link targets instead of links",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "no-recursion", no_argument, NULL,
|
|
||||||
OPT_NORECURSION },
|
|
||||||
.desc = "Don't add files from sub-directories",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
|
|
||||||
UTIL_OPT_SECTION("MISC OPTIONS"),
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
{
|
|
||||||
.option = { "verbose", no_argument, NULL, 'V' },
|
|
||||||
.desc = "Print additional informational output",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "quiet", no_argument, NULL, 'q' },
|
|
||||||
.desc = "Suppress printing of informational output",
|
|
||||||
},
|
|
||||||
UTIL_OPT_END,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Split buffer size specification in @arg into two numbers to be stored in
|
/* Split buffer size specification in @arg into two numbers to be stored in
|
||||||
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
|
* @from_ptr and @to_ptr. Return %EXIT_OK on success. */
|
||||||
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
|
static int parse_buffer_size(char *arg, size_t *from_ptr, size_t *to_ptr)
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _fdasd
|
||||||
|
bash-completions = fdasd.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
libs = $(rootdir)/libvtoc/libvtoc.a \
|
libs = $(rootdir)/libvtoc/libvtoc.a \
|
||||||
$(rootdir)/libzds/libzds.a \
|
$(rootdir)/libzds/libzds.a \
|
||||||
$(rootdir)/libdasd/libdasd.a \
|
$(rootdir)/libdasd/libdasd.a \
|
||||||
|
|||||||
16
fdasd/autocompletion_generator_host.c
Normal file
16
fdasd/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "fdasd_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "fdasd");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "lib/zt_common.h"
|
#include "lib/zt_common.h"
|
||||||
|
|
||||||
#include "fdasd.h"
|
#include "fdasd.h"
|
||||||
|
#include "fdasd_cli.h"
|
||||||
|
|
||||||
/* global variables */
|
/* global variables */
|
||||||
static struct hd_geometry geo;
|
static struct hd_geometry geo;
|
||||||
@@ -138,58 +139,6 @@ static const struct util_prg prg = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("NON-INTERACTIVE MODE"),
|
|
||||||
{
|
|
||||||
.option = { "auto", no_argument, NULL, 'a' },
|
|
||||||
.desc = "Create a single partition spanning the entire disk",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "config", required_argument, NULL, 'c' },
|
|
||||||
.argument = "FILE",
|
|
||||||
.desc = "Create partitions(s) based on content of FILE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "keep_volser", no_argument, NULL, 'k' },
|
|
||||||
.desc = "Do not change the current volume serial",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "label", required_argument, NULL, 'l' },
|
|
||||||
.argument = "VOLSER",
|
|
||||||
.desc = "Set the volume serial to VOLSER",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("MISC"),
|
|
||||||
{
|
|
||||||
.option = { "check_host_count", no_argument, NULL, 'C' },
|
|
||||||
.desc = "Check if device is in use by other hosts",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "force", optional_argument, NULL, 'f' },
|
|
||||||
.argument = "TYPE,SIZE",
|
|
||||||
.desc = "Force fdasd to work on non DASD devices with assumed "
|
|
||||||
"TYPE (3390, 3380, or 9345) and blocksize SIZE",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "volser", no_argument, NULL, 'i' },
|
|
||||||
.desc = "Print volume serial",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "table", no_argument, NULL, 'p' },
|
|
||||||
.desc = "Print partition table",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "verbose", no_argument, NULL, 'r' },
|
|
||||||
.desc = "Provide more verbose output",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "silent", no_argument, NULL, 's' },
|
|
||||||
.desc = "Suppress messages",
|
|
||||||
},
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static int getpos(fdasd_anchor_t *anc, int dsn)
|
static int getpos(fdasd_anchor_t *anc, int dsn)
|
||||||
{
|
{
|
||||||
return anc->partno[dsn];
|
return anc->partno[dsn];
|
||||||
|
|||||||
64
fdasd/fdasd_cli.h
Normal file
64
fdasd/fdasd_cli.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FDASD_CLI_H
|
||||||
|
#define FDASD_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("NON-INTERACTIVE MODE"),
|
||||||
|
{
|
||||||
|
.option = { "auto", no_argument, NULL, 'a' },
|
||||||
|
.desc = "Create a single partition spanning the entire disk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "config", required_argument, NULL, 'c' },
|
||||||
|
.argument = "FILE",
|
||||||
|
.desc = "Create partitions(s) based on content of FILE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "keep_volser", no_argument, NULL, 'k' },
|
||||||
|
.desc = "Do not change the current volume serial",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "label", required_argument, NULL, 'l' },
|
||||||
|
.argument = "VOLSER",
|
||||||
|
.desc = "Set the volume serial to VOLSER",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("MISC"),
|
||||||
|
{
|
||||||
|
.option = { "check_host_count", no_argument, NULL, 'C' },
|
||||||
|
.desc = "Check if device is in use by other hosts",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "force", optional_argument, NULL, 'f' },
|
||||||
|
.argument = "TYPE,SIZE",
|
||||||
|
.desc = "Force fdasd to work on non DASD devices with assumed "
|
||||||
|
"TYPE (3390, 3380, or 9345) and blocksize SIZE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "volser", no_argument, NULL, 'i' },
|
||||||
|
.desc = "Print volume serial",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "table", no_argument, NULL, 'p' },
|
||||||
|
.desc = "Print partition table",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "verbose", no_argument, NULL, 'r' },
|
||||||
|
.desc = "Provide more verbose output",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "silent", no_argument, NULL, 's' },
|
||||||
|
.desc = "Suppress messages",
|
||||||
|
},
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -7,6 +7,11 @@
|
|||||||
|
|
||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _hsavmcore
|
||||||
|
bash-completions = hsavmcore.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
ALL_CPPFLAGS += -D_FILE_OFFSET_BITS=64
|
||||||
|
|
||||||
ifeq (${HAVE_FUSE},0)
|
ifeq (${HAVE_FUSE},0)
|
||||||
@@ -41,7 +46,7 @@ endif
|
|||||||
ALL_CFLAGS += $(FUSE_CFLAGS) $(SYSTEMD_CFLAGS)
|
ALL_CFLAGS += $(FUSE_CFLAGS) $(SYSTEMD_CFLAGS)
|
||||||
LDLIBS += $(FUSE_LDLIBS) $(SYSTEMD_LDLIBS) -lpthread
|
LDLIBS += $(FUSE_LDLIBS) $(SYSTEMD_LDLIBS) -lpthread
|
||||||
|
|
||||||
sources := $(wildcard *.c)
|
sources := $(filter-out %_host.c, $(wildcard *.c))
|
||||||
objects := $(patsubst %.c,%.o,$(sources))
|
objects := $(patsubst %.c,%.o,$(sources))
|
||||||
|
|
||||||
libs = $(rootdir)/libutil/libutil.a
|
libs = $(rootdir)/libutil/libutil.a
|
||||||
|
|||||||
16
hsavmcore/autocompletion_generator_host.c
Normal file
16
hsavmcore/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "hsavmcore_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "hsavmcore");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "lib/util_log.h"
|
#include "lib/util_log.h"
|
||||||
|
|
||||||
#include "cmdline_options.h"
|
#include "cmdline_options.h"
|
||||||
|
#include "hsavmcore_cli.h"
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "hsavmcore is designed to make the dump process with kdump more "
|
.desc = "hsavmcore is designed to make the dump process with kdump more "
|
||||||
@@ -32,91 +33,6 @@ static const struct util_prg prg = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("CONFIGURATION"),
|
|
||||||
{
|
|
||||||
.option = { "config", required_argument, NULL, 'c' },
|
|
||||||
.argument = "CONFIGFILE",
|
|
||||||
.desc = "Path to the configuration file.\n"
|
|
||||||
"Default: no configuration file is used",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "vmcore", required_argument, NULL, 'C' },
|
|
||||||
.argument = "VMCOREFILE",
|
|
||||||
.desc = "Path to the vmcore file.\n"
|
|
||||||
"Default: " PROC_VMCORE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "hsa", required_argument, NULL, 'H' },
|
|
||||||
.argument = "ZCOREHSAFILE",
|
|
||||||
.desc = "Path to the zcore HSA file.\n"
|
|
||||||
"Default: " ZCORE_HSA,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "workdir", required_argument, NULL, 'W' },
|
|
||||||
.argument = "WORKDIR",
|
|
||||||
.desc = "Path to the work directory where temporary files can be "
|
|
||||||
"stored.\nDefault: " WORKDIR,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "bmvmcore", required_argument, NULL, 'B' },
|
|
||||||
.argument = "VMCOREFILE",
|
|
||||||
.desc = "Path to the target of the bind mount for the vmcore "
|
|
||||||
"replacement.\nDefault: " PROC_VMCORE,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "swap", required_argument, NULL, 'S' },
|
|
||||||
.argument = "PATH",
|
|
||||||
.desc = "Path to a swap device or file. The specified swap "
|
|
||||||
"device or file must exist and have the proper swap "
|
|
||||||
"format.\nDefault: no swap device or file is activated",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "hsasize", required_argument, NULL, 'T' },
|
|
||||||
.argument = "HSASIZE",
|
|
||||||
.desc = "HSA size in bytes.\n"
|
|
||||||
"Default: -1 (read from the zcore HSA file)",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "dbgfsmnt", no_argument, NULL, 'D' },
|
|
||||||
.desc = "Mount the debug file system.\n"
|
|
||||||
"Default: the debug file system is not mounted",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "hsamem", no_argument, NULL, 'F' },
|
|
||||||
.desc = "Cache the HSA memory in regular memory.\n"
|
|
||||||
"Default: the HSA memory is cached as a file within "
|
|
||||||
"WORKDIR",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "norelhsa", no_argument, NULL, 'R' },
|
|
||||||
.desc = "Do NOT release the HSA memory after caching.\n"
|
|
||||||
"Default: the HSA memory is released",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "nobindmnt", no_argument, NULL, 'N' },
|
|
||||||
.desc = "Do NOT replace the system's vmcore.\n"
|
|
||||||
"Default: the system's vmcore is replaced",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("LOGGING"),
|
|
||||||
{
|
|
||||||
.option = { "verbose", no_argument, NULL, 'V' },
|
|
||||||
.desc = "Print verbose messages to stdout. Repeat this option "
|
|
||||||
"for increased verbosity from just error messages to "
|
|
||||||
"also include warning, information, debug, and trace "
|
|
||||||
"messages. This option is intended for debugging",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "fusedbg", no_argument, NULL, 'G' },
|
|
||||||
.desc = "Enable FUSE debugging.\n"
|
|
||||||
"Default: FUSE debugging is disabled",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
void parse_cmdline_options(int argc, char *argv[], struct config *config)
|
void parse_cmdline_options(int argc, char *argv[], struct config *config)
|
||||||
{
|
{
|
||||||
int opt, ret;
|
int opt, ret;
|
||||||
|
|||||||
99
hsavmcore/hsavmcore_cli.h
Normal file
99
hsavmcore/hsavmcore_cli.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HSAVMCORE_CLI_H
|
||||||
|
#define HSAVMCORE_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("CONFIGURATION"),
|
||||||
|
{
|
||||||
|
.option = { "config", required_argument, NULL, 'c' },
|
||||||
|
.argument = "CONFIGFILE",
|
||||||
|
.desc = "Path to the configuration file.\n"
|
||||||
|
"Default: no configuration file is used",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "vmcore", required_argument, NULL, 'C' },
|
||||||
|
.argument = "VMCOREFILE",
|
||||||
|
.desc = "Path to the vmcore file.\n"
|
||||||
|
"Default: " PROC_VMCORE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hsa", required_argument, NULL, 'H' },
|
||||||
|
.argument = "ZCOREHSAFILE",
|
||||||
|
.desc = "Path to the zcore HSA file.\n"
|
||||||
|
"Default: " ZCORE_HSA,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "workdir", required_argument, NULL, 'W' },
|
||||||
|
.argument = "WORKDIR",
|
||||||
|
.desc = "Path to the work directory where temporary files can be "
|
||||||
|
"stored.\nDefault: " WORKDIR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "bmvmcore", required_argument, NULL, 'B' },
|
||||||
|
.argument = "VMCOREFILE",
|
||||||
|
.desc = "Path to the target of the bind mount for the vmcore "
|
||||||
|
"replacement.\nDefault: " PROC_VMCORE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "swap", required_argument, NULL, 'S' },
|
||||||
|
.argument = "PATH",
|
||||||
|
.desc = "Path to a swap device or file. The specified swap "
|
||||||
|
"device or file must exist and have the proper swap "
|
||||||
|
"format.\nDefault: no swap device or file is activated",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hsasize", required_argument, NULL, 'T' },
|
||||||
|
.argument = "HSASIZE",
|
||||||
|
.desc = "HSA size in bytes.\n"
|
||||||
|
"Default: -1 (read from the zcore HSA file)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "dbgfsmnt", no_argument, NULL, 'D' },
|
||||||
|
.desc = "Mount the debug file system.\n"
|
||||||
|
"Default: the debug file system is not mounted",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "hsamem", no_argument, NULL, 'F' },
|
||||||
|
.desc = "Cache the HSA memory in regular memory.\n"
|
||||||
|
"Default: the HSA memory is cached as a file within "
|
||||||
|
"WORKDIR",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "norelhsa", no_argument, NULL, 'R' },
|
||||||
|
.desc = "Do NOT release the HSA memory after caching.\n"
|
||||||
|
"Default: the HSA memory is released",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "nobindmnt", no_argument, NULL, 'N' },
|
||||||
|
.desc = "Do NOT replace the system's vmcore.\n"
|
||||||
|
"Default: the system's vmcore is replaced",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("LOGGING"),
|
||||||
|
{
|
||||||
|
.option = { "verbose", no_argument, NULL, 'V' },
|
||||||
|
.desc = "Print verbose messages to stdout. Repeat this option "
|
||||||
|
"for increased verbosity from just error messages to "
|
||||||
|
"also include warning, information, debug, and trace "
|
||||||
|
"messages. This option is intended for debugging",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "fusedbg", no_argument, NULL, 'G' },
|
||||||
|
.desc = "Enable FUSE debugging.\n"
|
||||||
|
"Default: FUSE debugging is disabled",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -96,6 +96,17 @@ with an ASCII Record Separator character (0x1e) and suffixed with an ASCII Line
|
|||||||
Feed character (0x0a) in accordance with RFC7464.
|
Feed character (0x0a) in accordance with RFC7464.
|
||||||
.BR
|
.BR
|
||||||
|
|
||||||
|
See section "OUTPUT FORMAT" for more details.
|
||||||
|
.BR
|
||||||
|
.PP
|
||||||
|
.IP \(bu 3
|
||||||
|
.B jsonl:
|
||||||
|
Line-delimited JSON data structures
|
||||||
|
|
||||||
|
Data for each iteration is formatted as a separate JSON data structure
|
||||||
|
separated by ASCII Line Feed character (0x0a, LF).
|
||||||
|
.BR
|
||||||
|
|
||||||
See section "OUTPUT FORMAT" for more details.
|
See section "OUTPUT FORMAT" for more details.
|
||||||
.BR
|
.BR
|
||||||
.PP
|
.PP
|
||||||
@@ -458,6 +469,14 @@ Subsequent objects each represent performance data for one iteration
|
|||||||
.br
|
.br
|
||||||
.PP
|
.PP
|
||||||
|
|
||||||
|
.SS jsonl
|
||||||
|
|
||||||
|
The jsonl output format is a data streaming variation of the JSON output format
|
||||||
|
described above with the same properties as the json\-seq output with the
|
||||||
|
difference that JSON data is separated only by an ASCII Line Feed character
|
||||||
|
(0x0a, LF).
|
||||||
|
|
||||||
|
|
||||||
.SH EXAMPLES
|
.SH EXAMPLES
|
||||||
To start hyptop with the "sys_list" window in interactive mode, enter:
|
To start hyptop with the "sys_list" window in interactive mode, enter:
|
||||||
.br
|
.br
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ static void l_fmt_init(void)
|
|||||||
flags |= FMT_QUOTEALL;
|
flags |= FMT_QUOTEALL;
|
||||||
if (g.o.format == FMT_CSV || g.o.format_all)
|
if (g.o.format == FMT_CSV || g.o.format_all)
|
||||||
flags |= FMT_KEEPINVAL;
|
flags |= FMT_KEEPINVAL;
|
||||||
if (g.o.format == FMT_JSON || g.o.format == FMT_JSONSEQ)
|
if (util_fmt_is_json(g.o.format))
|
||||||
flags |= FMT_HANDLEINT;
|
flags |= FMT_HANDLEINT;
|
||||||
util_fmt_init(stdout, g.o.format, flags, 1);
|
util_fmt_init(stdout, g.o.format, flags, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,14 +54,6 @@ static char HELP_TEXT[] =
|
|||||||
#define OPT_FORMAT 256 /* --format */
|
#define OPT_FORMAT 256 /* --format */
|
||||||
#define OPT_FORMAT_ALL 261 /* --all*/
|
#define OPT_FORMAT_ALL 261 /* --all*/
|
||||||
|
|
||||||
/*
|
|
||||||
* Options with underscore to keep compatibility
|
|
||||||
*/
|
|
||||||
#define OPT_BATCH_MODE 257 /* --batch_mode */
|
|
||||||
#define OPT_SORT_FIELD 258 /* --sort | --sort_field */
|
|
||||||
#define OPT_CPU_TYPES 259 /* --cpu_types */
|
|
||||||
#define OPT_SMT_FACTOR 260 /* --smt_factor */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize default settings
|
* Initialize default settings
|
||||||
*/
|
*/
|
||||||
@@ -224,7 +216,7 @@ static void l_fields_set(char *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the "--sort_field" option
|
* Set the "--sort" option
|
||||||
*/
|
*/
|
||||||
static void l_sort_field_set(char *str)
|
static void l_sort_field_set(char *str)
|
||||||
{
|
{
|
||||||
@@ -362,19 +354,18 @@ void opts_parse(int argc, char *argv[])
|
|||||||
{ "version", no_argument, NULL, 'v'},
|
{ "version", no_argument, NULL, 'v'},
|
||||||
{ "help", no_argument, NULL, 'h'},
|
{ "help", no_argument, NULL, 'h'},
|
||||||
{ "batch-mode", no_argument, NULL, 'b'},
|
{ "batch-mode", no_argument, NULL, 'b'},
|
||||||
{ "batch_mode", no_argument, NULL, OPT_BATCH_MODE},
|
{ "batch_mode", no_argument, NULL, 'b'},
|
||||||
{ "all", no_argument, NULL, OPT_FORMAT_ALL },
|
{ "all", no_argument, NULL, OPT_FORMAT_ALL },
|
||||||
{ "delay", required_argument, NULL, 'd'},
|
{ "delay", required_argument, NULL, 'd'},
|
||||||
{ "smt-factor", required_argument, NULL, 'm'},
|
{ "smt-factor", required_argument, NULL, 'm'},
|
||||||
{ "smt_factor", required_argument, NULL, OPT_SMT_FACTOR},
|
{ "smt_factor", required_argument, NULL, 'm'},
|
||||||
{ "window", required_argument, NULL, 'w'},
|
{ "window", required_argument, NULL, 'w'},
|
||||||
{ "sys", required_argument, NULL, 's'},
|
{ "sys", required_argument, NULL, 's'},
|
||||||
{ "iterations", required_argument, NULL, 'n'},
|
{ "iterations", required_argument, NULL, 'n'},
|
||||||
{ "fields", required_argument, NULL, 'f'},
|
{ "fields", required_argument, NULL, 'f'},
|
||||||
{ "sort-field", required_argument, NULL, 'S'},
|
{ "sort", required_argument, NULL, 'S'},
|
||||||
{ "sort_field", required_argument, NULL, OPT_SORT_FIELD},
|
|
||||||
{ "cpu-types", required_argument, NULL, 't'},
|
{ "cpu-types", required_argument, NULL, 't'},
|
||||||
{ "cpu_types", required_argument, NULL, OPT_CPU_TYPES},
|
{ "cpu_types", required_argument, NULL, 't'},
|
||||||
{ "format", required_argument, NULL, OPT_FORMAT },
|
{ "format", required_argument, NULL, OPT_FORMAT },
|
||||||
{ NULL, 0, NULL, 0 }
|
{ NULL, 0, NULL, 0 }
|
||||||
};
|
};
|
||||||
@@ -393,14 +384,12 @@ void opts_parse(int argc, char *argv[])
|
|||||||
case 'h':
|
case 'h':
|
||||||
l_usage();
|
l_usage();
|
||||||
hyptop_exit(0);
|
hyptop_exit(0);
|
||||||
case OPT_BATCH_MODE:
|
|
||||||
case 'b':
|
case 'b':
|
||||||
l_batch_mode_set();
|
l_batch_mode_set();
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
l_delay_set(optarg);
|
l_delay_set(optarg);
|
||||||
break;
|
break;
|
||||||
case OPT_SMT_FACTOR:
|
|
||||||
case 'm':
|
case 'm':
|
||||||
l_factor_set(optarg);
|
l_factor_set(optarg);
|
||||||
break;
|
break;
|
||||||
@@ -413,14 +402,12 @@ void opts_parse(int argc, char *argv[])
|
|||||||
case 'n':
|
case 'n':
|
||||||
l_iterations_set(optarg);
|
l_iterations_set(optarg);
|
||||||
break;
|
break;
|
||||||
case OPT_CPU_TYPES:
|
|
||||||
case 't':
|
case 't':
|
||||||
l_cpu_types_set(optarg);
|
l_cpu_types_set(optarg);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
l_fields_set(optarg);
|
l_fields_set(optarg);
|
||||||
break;
|
break;
|
||||||
case OPT_SORT_FIELD:
|
|
||||||
case 'S':
|
case 'S':
|
||||||
l_sort_field_set(optarg);
|
l_sort_field_set(optarg);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1037,7 +1037,7 @@ void table_fmt_start(void)
|
|||||||
{
|
{
|
||||||
if (!g.o.format_specified)
|
if (!g.o.format_specified)
|
||||||
return;
|
return;
|
||||||
if (g.o.format != FMT_JSONSEQ)
|
if (!util_fmt_is_json_stream(g.o.format))
|
||||||
util_fmt_obj_start(FMT_LIST, "hyptop");
|
util_fmt_obj_start(FMT_LIST, "hyptop");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,7 +1045,7 @@ void table_fmt_end(void)
|
|||||||
{
|
{
|
||||||
if (!g.o.format_specified)
|
if (!g.o.format_specified)
|
||||||
return;
|
return;
|
||||||
if (g.o.format != FMT_JSONSEQ)
|
if (!util_fmt_is_json_stream(g.o.format))
|
||||||
util_fmt_obj_end(); /* hyptop[] */
|
util_fmt_obj_end(); /* hyptop[] */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: MIT */
|
|
||||||
/*
|
/*
|
||||||
* autocomp - command line autocompletion
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
|
||||||
* Generating autocompletion scripts for bash and zsh
|
|
||||||
* based on util_opt struct
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2025
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef LIB_UTIL_AUTOCOMP_H
|
#ifndef LIB_UTIL_AUTOCOMP_H
|
||||||
|
|||||||
@@ -45,12 +45,13 @@
|
|||||||
#define FMT_DEFAULT 0
|
#define FMT_DEFAULT 0
|
||||||
|
|
||||||
/* Names of supported output format types. */
|
/* Names of supported output format types. */
|
||||||
#define FMT_TYPE_NAMES "json json-seq pairs csv"
|
#define FMT_TYPE_NAMES "json json-seq jsonl pairs csv"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enum util_fmt_t - Output format types.
|
* enum util_fmt_t - Output format types.
|
||||||
* @FMT_JSON: JavaScript Object Notation output data structure
|
* @FMT_JSON: JavaScript Object Notation output data structure
|
||||||
* @FMT_JSONSEQ: Sequence of JSON data structures according to RFC7464
|
* @FMT_JSONSEQ: Sequence of JSON data structures according to RFC7464
|
||||||
|
* @FMT_JSONL: Line-delimited JSON
|
||||||
* @FMT_PAIRS: Textual key=value pairs
|
* @FMT_PAIRS: Textual key=value pairs
|
||||||
* @FMT_CSV: Comma-separated-values output
|
* @FMT_CSV: Comma-separated-values output
|
||||||
*
|
*
|
||||||
@@ -59,6 +60,7 @@
|
|||||||
enum util_fmt_t {
|
enum util_fmt_t {
|
||||||
FMT_JSON,
|
FMT_JSON,
|
||||||
FMT_JSONSEQ,
|
FMT_JSONSEQ,
|
||||||
|
FMT_JSONL,
|
||||||
FMT_PAIRS,
|
FMT_PAIRS,
|
||||||
FMT_CSV,
|
FMT_CSV,
|
||||||
};
|
};
|
||||||
@@ -232,4 +234,23 @@ void util_fmt_obj_end(void);
|
|||||||
*/
|
*/
|
||||||
void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...);
|
void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* util_fmt_is_json() - Determine whether format is JSON.
|
||||||
|
* @type: Format type identifier.
|
||||||
|
*
|
||||||
|
* Return: %true if type is JSON, %false otherwise.
|
||||||
|
*/
|
||||||
|
bool util_fmt_is_json(enum util_fmt_t type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* util_fmt_is_json_stream() - Determine whether format is JSON stream.
|
||||||
|
* @type: Format type identifier.
|
||||||
|
*
|
||||||
|
* Determine whether a given format @type represents a JSON streaming format
|
||||||
|
* such as json-seq (@FMT_JSONSEQ) or jsonl (@FMT_JSONL).
|
||||||
|
*
|
||||||
|
* Return: %true if type is either @FMT_JSONSEQ or @FMT_JSONL, %false otherwise.
|
||||||
|
*/
|
||||||
|
bool util_fmt_is_json_stream(enum util_fmt_t type);
|
||||||
|
|
||||||
#endif /* LIB_UTIL_FMT_H */
|
#endif /* LIB_UTIL_FMT_H */
|
||||||
|
|||||||
@@ -102,6 +102,9 @@
|
|||||||
_x > _y ? _x : _y; \
|
_x > _y ? _x : _y; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#ifdef __SIZEOF_INT128__
|
||||||
|
typedef unsigned __int128 u128 __aligned(16);
|
||||||
|
#endif
|
||||||
typedef unsigned long long u64;
|
typedef unsigned long long u64;
|
||||||
typedef signed long long s64;
|
typedef signed long long s64;
|
||||||
typedef unsigned int u32;
|
typedef unsigned int u32;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ detect-openssl-version.dep:
|
|||||||
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
||||||
echo "#endif" >> $(TMPFILE)
|
echo "#endif" >> $(TMPFILE)
|
||||||
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
||||||
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
|
echo " EVP_PKEY_id(NULL);" >> $(TMPFILE)
|
||||||
echo "}" >> $(TMPFILE)
|
echo "}" >> $(TMPFILE)
|
||||||
mv $(TMPFILE) $@
|
mv $(TMPFILE) $@
|
||||||
|
|
||||||
|
|||||||
@@ -229,8 +229,9 @@ int parse_json_web_token(const char *token, json_object **header_obj,
|
|||||||
size_t *signature_len)
|
size_t *signature_len)
|
||||||
{
|
{
|
||||||
json_object *hdr = NULL, *pld = NULL, *b64_obj = NULL;
|
json_object *hdr = NULL, *pld = NULL, *b64_obj = NULL;
|
||||||
char *ch, *header, *payload, *json = NULL;
|
|
||||||
size_t header_len, payload_len, json_len;
|
size_t header_len, payload_len, json_len;
|
||||||
|
const char *ch, *header, *payload;
|
||||||
|
char *json = NULL;
|
||||||
bool b64 = true;
|
bool b64 = true;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
@@ -254,7 +255,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
|
* and the following parts (if any) are used for JWS Signature, or JWE
|
||||||
* Encryption (not considered here).
|
* Encryption (not considered here).
|
||||||
*/
|
*/
|
||||||
header = (char *)token;
|
header = token;
|
||||||
|
|
||||||
ch = strchr(token, '.');
|
ch = strchr(token, '.');
|
||||||
if (ch == NULL) {
|
if (ch == NULL) {
|
||||||
@@ -653,7 +654,7 @@ int verify_json_web_signature(const char *jws, const unsigned char *payload,
|
|||||||
BIGNUM *bn_s = NULL;
|
BIGNUM *bn_s = NULL;
|
||||||
const char *alg;
|
const char *alg;
|
||||||
size_t sig_len;
|
size_t sig_len;
|
||||||
char *ch;
|
const char *ch;
|
||||||
|
|
||||||
if (jws == NULL || pkey == NULL)
|
if (jws == NULL || pkey == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -2172,7 +2173,7 @@ int write_public_key(const char *pem_filename, EVP_PKEY *pkey)
|
|||||||
*/
|
*/
|
||||||
static bool is_duplicate_name_entry(X509_NAME *name, X509_NAME_ENTRY *entry)
|
static bool is_duplicate_name_entry(X509_NAME *name, X509_NAME_ENTRY *entry)
|
||||||
{
|
{
|
||||||
X509_NAME_ENTRY *ne;
|
const X509_NAME_ENTRY *ne;
|
||||||
int count, i;
|
int count, i;
|
||||||
|
|
||||||
count = X509_NAME_entry_count(name);
|
count = X509_NAME_entry_count(name);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ detect-openssl-version.dep:
|
|||||||
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
||||||
echo "#endif" >> $(TMPFILE)
|
echo "#endif" >> $(TMPFILE)
|
||||||
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
||||||
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
|
echo " EVP_PKEY_id(NULL);" >> $(TMPFILE)
|
||||||
echo "}" >> $(TMPFILE)
|
echo "}" >> $(TMPFILE)
|
||||||
mv $(TMPFILE) $@
|
mv $(TMPFILE) $@
|
||||||
|
|
||||||
|
|||||||
@@ -1370,7 +1370,8 @@ int kmip_connection_get_server_cert(const char *server,
|
|||||||
struct kmip_conn_config config = { 0 };
|
struct kmip_conn_config config = { 0 };
|
||||||
struct kmip_connection *conn = NULL;
|
struct kmip_connection *conn = NULL;
|
||||||
int rc, numcerts, i, port_found = 0;
|
int rc, numcerts, i, port_found = 0;
|
||||||
char *hostname = NULL, *tok, *tok2;
|
const char *tok, *tok2;
|
||||||
|
char *hostname = NULL;
|
||||||
STACK_OF(X509) *chain;
|
STACK_OF(X509) *chain;
|
||||||
bool do_verify = true;
|
bool do_verify = true;
|
||||||
FILE *fp = NULL;
|
FILE *fp = NULL;
|
||||||
|
|||||||
@@ -378,8 +378,14 @@ int kmip_connection_tls_init(struct kmip_connection *conn, bool debug)
|
|||||||
if (conn->config.tls_verify_host) {
|
if (conn->config.tls_verify_host) {
|
||||||
SSL_set_hostflags(conn->plain_tls.ssl,
|
SSL_set_hostflags(conn->plain_tls.ssl,
|
||||||
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||||
|
#if OPENSSL_VERSION_PREREQ(4, 0)
|
||||||
|
if (SSL_set1_ipaddr(conn->plain_tls.ssl, hostname) != 1 &&
|
||||||
|
SSL_set1_dnsname(conn->plain_tls.ssl, hostname) != 1) {
|
||||||
|
kmip_debug(debug, "SSL_set1_ipaddr/dnsname failed");
|
||||||
|
#else
|
||||||
if (SSL_set1_host(conn->plain_tls.ssl, hostname) != 1) {
|
if (SSL_set1_host(conn->plain_tls.ssl, hostname) != 1) {
|
||||||
kmip_debug(debug, "SSL_set1_host failed");
|
kmip_debug(debug, "SSL_set1_host failed");
|
||||||
|
#endif
|
||||||
if (debug)
|
if (debug)
|
||||||
ERR_print_errors_fp(stderr);
|
ERR_print_errors_fp(stderr);
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ detect-openssl-version.dep:
|
|||||||
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
echo " #error openssl version 1.1 is required" >> $(TMPFILE)
|
||||||
echo "#endif" >> $(TMPFILE)
|
echo "#endif" >> $(TMPFILE)
|
||||||
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
echo "static void __attribute__((unused)) test(void) {" >> $(TMPFILE)
|
||||||
echo " EVP_PKEY_meth_remove(NULL);" >> $(TMPFILE)
|
echo " EVP_PKEY_id(NULL);" >> $(TMPFILE)
|
||||||
echo "}" >> $(TMPFILE)
|
echo "}" >> $(TMPFILE)
|
||||||
mv $(TMPFILE) $@
|
mv $(TMPFILE) $@
|
||||||
|
|
||||||
|
|||||||
@@ -395,7 +395,7 @@ const struct sk_digest_info *SK_UTIL_get_digest_info(int digest_nid)
|
|||||||
static bool SK_UTILS_is_duplicate_name_entry(const X509_NAME *name,
|
static bool SK_UTILS_is_duplicate_name_entry(const X509_NAME *name,
|
||||||
const X509_NAME_ENTRY *entry)
|
const X509_NAME_ENTRY *entry)
|
||||||
{
|
{
|
||||||
X509_NAME_ENTRY *ne;
|
const X509_NAME_ENTRY *ne;
|
||||||
int count, i;
|
int count, i;
|
||||||
|
|
||||||
count = X509_NAME_entry_count(name);
|
count = X509_NAME_entry_count(name);
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
/*
|
/*
|
||||||
* autocomp - command line autocompletion
|
* SPDX-License-Identifier: MIT
|
||||||
*
|
|
||||||
* Generating autocompletion scripts for bash and zsh
|
|
||||||
* based on util_opt struct
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2025
|
|
||||||
*
|
|
||||||
* s390-tools is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the MIT license. See LICENSE for details.
|
|
||||||
*
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -22,18 +14,19 @@
|
|||||||
#include "lib/util_autocomp.h"
|
#include "lib/util_autocomp.h"
|
||||||
#include "lib/util_opt.h"
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
static const char *bash_script_part1 = "() {\n\n\
|
static const char *bash_script_part1 = "() {\n\
|
||||||
\tlocal current_word previous_word options_array\n\n\
|
\tlocal current_word options_array\n\
|
||||||
\tCOMPREPLY=()\n\n\
|
\tCOMPREPLY=()\n\n\
|
||||||
\tcurrent_word=\"${COMP_WORDS[COMP_CWORD]}\"\n\n\
|
\tcurrent_word=\"${COMP_WORDS[COMP_CWORD]}\"\n\
|
||||||
\tprevious_word=\"${COMP_WORDS[COMP_CWORD-1]}\"\n\n\
|
|
||||||
\toptions_array=\"";
|
\toptions_array=\"";
|
||||||
|
|
||||||
static const char *bash_script_part2 = "\tif [[ ${current_word} == -* || ${COMP_CWORD} -eq 1 ]] ; then\n\n\
|
static const char *bash_script_part2 = "\tif [[ ${current_word} == -* ]] ; then\n\
|
||||||
\t\tCOMPREPLY=( $(compgen -W \"${options_array}\" -- ${current_word} ) )\n\n\
|
\t\tmapfile -t \"COMPREPLY\" < <(compgen -W \"${options_array}\" -- \"$current_word\")\n\
|
||||||
\t\treturn 0\n\n\
|
\telse\n\
|
||||||
\tfi\n\n\
|
\t\tcompopt -o bashdefault -o default\n\
|
||||||
}\n\n\
|
\tfi\n\
|
||||||
|
\treturn 0\n\
|
||||||
|
}\n\
|
||||||
complete -F ";
|
complete -F ";
|
||||||
|
|
||||||
static char *format_name(const char *fmt, char *tool_name)
|
static char *format_name(const char *fmt, char *tool_name)
|
||||||
@@ -63,7 +56,7 @@ static int init_scriptfile(char *file_path)
|
|||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
fd = open(file_path, O_CREAT | O_WRONLY, 0644);
|
fd = open(file_path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
return fd;
|
return fd;
|
||||||
@@ -86,7 +79,7 @@ static int start_bash_scriptfile(int fd, char *func_name)
|
|||||||
|
|
||||||
static int start_zsh_scriptfile(int fd, char *func_name, char *tool_name)
|
static int start_zsh_scriptfile(int fd, char *func_name, char *tool_name)
|
||||||
{
|
{
|
||||||
const char *part3 = " {\n\n\t_arguments -C \\\n";
|
const char *part3 = " {\n\n\t_arguments -C -A \"*\" \\\n";
|
||||||
const char *part2 = "\n\nfunction ";
|
const char *part2 = "\n\nfunction ";
|
||||||
const char *part1 = "#compdef ";
|
const char *part1 = "#compdef ";
|
||||||
int len, ret = 0;
|
int len, ret = 0;
|
||||||
@@ -127,6 +120,7 @@ static int write_bash_command_options(struct util_opt *opt_vec, int fd)
|
|||||||
|
|
||||||
static int write_zsh_command_options(struct util_opt *opt_vec, int fd)
|
static int write_zsh_command_options(struct util_opt *opt_vec, int fd)
|
||||||
{
|
{
|
||||||
|
const char *end = "\t\t\"*:files:_files\"\n}\n";
|
||||||
const char *name, *desc;
|
const char *name, *desc;
|
||||||
char *str;
|
char *str;
|
||||||
int len;
|
int len;
|
||||||
@@ -145,8 +139,14 @@ static int write_zsh_command_options(struct util_opt *opt_vec, int fd)
|
|||||||
free(str);
|
free(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (write(fd, "\n}\n", 3) != 3)
|
len = asprintf(&str, "%s", end);
|
||||||
|
if (len == -1)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
if (write(fd, str, len) != len) {
|
||||||
|
free(str);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
free(str);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,35 +169,6 @@ static int finish_bash_scriptfile(char *tool_name, int fd, char *func_name)
|
|||||||
* Adds tab completion in bash for a command.
|
* Adds tab completion in bash for a command.
|
||||||
* Works by generating an autocompletion
|
* Works by generating an autocompletion
|
||||||
* script file at '/usr/share/bash-completion/completions'.
|
* script file at '/usr/share/bash-completion/completions'.
|
||||||
*
|
|
||||||
* The full script will be as follows, supposing the tool name is
|
|
||||||
* 'example' and it only has the options '--help' and
|
|
||||||
* '--version':
|
|
||||||
*
|
|
||||||
* _example() {
|
|
||||||
*
|
|
||||||
* local current_word previous_word options_array
|
|
||||||
*
|
|
||||||
* COMPREPLY=()
|
|
||||||
*
|
|
||||||
* current_word="${COMP_WORDS[COMP_CWORD]}"
|
|
||||||
*
|
|
||||||
* previous_word="${COMP_WORDS[COMP_CWORD-1]}"
|
|
||||||
*
|
|
||||||
* options_array="--version --help"
|
|
||||||
*
|
|
||||||
* if [[ ${current_word} == -* || ${COMP_CWORD} -eq 1 ]] ; then
|
|
||||||
*
|
|
||||||
* COMPREPLY=( $(compgen -W "${options_array}" -- ${current_word} ) )
|
|
||||||
*
|
|
||||||
* return 0
|
|
||||||
*
|
|
||||||
* fi
|
|
||||||
*
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* complete -F _example example
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
static void generate_bash_autocomp(struct util_opt *opt_vec, char *tool_name)
|
static void generate_bash_autocomp(struct util_opt *opt_vec, char *tool_name)
|
||||||
{
|
{
|
||||||
@@ -244,22 +215,6 @@ end:
|
|||||||
* Adds tab completion in zsh for a command.
|
* Adds tab completion in zsh for a command.
|
||||||
* Works by generating an autocompletion
|
* Works by generating an autocompletion
|
||||||
* script file at '/usr/share/zsh/site-functions'.
|
* script file at '/usr/share/zsh/site-functions'.
|
||||||
*
|
|
||||||
* The full script will be as follows, supposing the tool name is
|
|
||||||
* 'example' and it only has the options '--help', -h and
|
|
||||||
* '--version' (the descriptions, as well as the flags are
|
|
||||||
* taken from a util_opt struct):
|
|
||||||
*
|
|
||||||
* #compdef example_completion
|
|
||||||
*
|
|
||||||
* function _example_completion {
|
|
||||||
*
|
|
||||||
* _arguments -C \
|
|
||||||
* "-h[Show help information]" \
|
|
||||||
* "--help[Show help but long format]" \
|
|
||||||
* "--version[Show version]"
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
static void generate_zsh_autocomp(struct util_opt *opt_vec, char *tool_name)
|
static void generate_zsh_autocomp(struct util_opt *opt_vec, char *tool_name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ static const struct {
|
|||||||
} formats[] = {
|
} formats[] = {
|
||||||
{ "json", FMT_JSON },
|
{ "json", FMT_JSON },
|
||||||
{ "json-seq", FMT_JSONSEQ },
|
{ "json-seq", FMT_JSONSEQ },
|
||||||
|
{ "jsonl", FMT_JSONL },
|
||||||
{ "pairs", FMT_PAIRS },
|
{ "pairs", FMT_PAIRS },
|
||||||
{ "csv", FMT_CSV },
|
{ "csv", FMT_CSV },
|
||||||
};
|
};
|
||||||
@@ -111,6 +112,29 @@ bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool util_fmt_is_json(enum util_fmt_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case FMT_JSON:
|
||||||
|
case FMT_JSONSEQ:
|
||||||
|
case FMT_JSONL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool util_fmt_is_json_stream(enum util_fmt_t type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case FMT_JSONSEQ:
|
||||||
|
case FMT_JSONL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void safe_write(const char *str)
|
static void safe_write(const char *str)
|
||||||
{
|
{
|
||||||
size_t done, todo;
|
size_t done, todo;
|
||||||
@@ -129,7 +153,7 @@ static void _indent(unsigned int off, bool safe)
|
|||||||
{
|
{
|
||||||
unsigned int num, i;
|
unsigned int num, i;
|
||||||
|
|
||||||
if (f.type == FMT_JSONSEQ)
|
if (util_fmt_is_json_stream(f.type))
|
||||||
return;
|
return;
|
||||||
num = f.ind_base + off;
|
num = f.ind_base + off;
|
||||||
if (f.type == FMT_JSON && f.lvl > 0)
|
if (f.type == FMT_JSON && f.lvl > 0)
|
||||||
@@ -408,8 +432,8 @@ static void emit_meta_object(void)
|
|||||||
util_fmt_pair(quoted, "time", "%s", date);
|
util_fmt_pair(quoted, "time", "%s", date);
|
||||||
_util_fmt_obj_end();
|
_util_fmt_obj_end();
|
||||||
|
|
||||||
if (f.type == FMT_JSONSEQ) {
|
if (util_fmt_is_json_stream(f.type)) {
|
||||||
/* Tool meta-data is a separate object for JSONSEQ. */
|
/* Tool meta-data is a separate object for JSON streams. */
|
||||||
util_fmt_obj_end();
|
util_fmt_obj_end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -478,7 +502,7 @@ void util_fmt_obj_end(void)
|
|||||||
{
|
{
|
||||||
_util_fmt_obj_end();
|
_util_fmt_obj_end();
|
||||||
|
|
||||||
if (f.lvl == 1 && f.meta_done && f.type != FMT_JSONSEQ) {
|
if (f.lvl == 1 && f.meta_done && !util_fmt_is_json_stream(f.type)) {
|
||||||
/* Emit closure for top-level meta-container object. */
|
/* Emit closure for top-level meta-container object. */
|
||||||
util_fmt_obj_end();
|
util_fmt_obj_end();
|
||||||
}
|
}
|
||||||
@@ -737,7 +761,7 @@ void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
|
|||||||
f.do_warn = (flags & FMT_WARN);
|
f.do_warn = (flags & FMT_WARN);
|
||||||
f.handle_int = (flags & FMT_HANDLEINT);
|
f.handle_int = (flags & FMT_HANDLEINT);
|
||||||
f.api_level = api_level;
|
f.api_level = api_level;
|
||||||
if (type == FMT_JSONSEQ)
|
if (util_fmt_is_json_stream(type))
|
||||||
f.nl = "";
|
f.nl = "";
|
||||||
else
|
else
|
||||||
f.nl = "\n";
|
f.nl = "\n";
|
||||||
@@ -750,6 +774,7 @@ void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
|
|||||||
break;
|
break;
|
||||||
case FMT_JSON:
|
case FMT_JSON:
|
||||||
case FMT_JSONSEQ:
|
case FMT_JSONSEQ:
|
||||||
|
case FMT_JSONL:
|
||||||
f.obj_start = &json_obj_start;
|
f.obj_start = &json_obj_start;
|
||||||
f.obj_end = &json_obj_end;
|
f.obj_end = &json_obj_end;
|
||||||
f.map = &json_map;
|
f.map = &json_map;
|
||||||
|
|||||||
@@ -193,6 +193,9 @@ int main(int UNUSED(argc), char *UNUSED(argv[]))
|
|||||||
announce("JSON formatted as sequence");
|
announce("JSON formatted as sequence");
|
||||||
simple_example(FMT_JSONSEQ, FMT_DEFAULT);
|
simple_example(FMT_JSONSEQ, FMT_DEFAULT);
|
||||||
|
|
||||||
|
announce("JSON Lines format");
|
||||||
|
simple_example(FMT_JSONL, FMT_DEFAULT);
|
||||||
|
|
||||||
announce("Pairs output");
|
announce("Pairs output");
|
||||||
simple_example(FMT_PAIRS, FMT_KEEPINVAL);
|
simple_example(FMT_PAIRS, FMT_KEEPINVAL);
|
||||||
|
|
||||||
@@ -232,6 +235,9 @@ int main(int UNUSED(argc), char *UNUSED(argv[]))
|
|||||||
announce("JSON sequence output with meta-data");
|
announce("JSON sequence output with meta-data");
|
||||||
meta_example(FMT_JSONSEQ);
|
meta_example(FMT_JSONSEQ);
|
||||||
|
|
||||||
|
announce("JSON Lines output with meta-data");
|
||||||
|
meta_example(FMT_JSONL);
|
||||||
|
|
||||||
announce("Pairs output with meta-data");
|
announce("Pairs output with meta-data");
|
||||||
meta_example(FMT_PAIRS);
|
meta_example(FMT_PAIRS);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
include ../common.mak
|
include ../common.mak
|
||||||
|
|
||||||
|
zsh-completions = _lsstp
|
||||||
|
bash-completions = lsstp.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
libs = $(rootdir)/libutil/libutil.a
|
libs = $(rootdir)/libutil/libutil.a
|
||||||
all: lsstp
|
all: lsstp
|
||||||
|
|
||||||
|
|||||||
16
lsstp/autocompletion_generator_host.c
Normal file
16
lsstp/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "lsstp_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "lsstp");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@
|
|||||||
#include "lib/util_prg.h"
|
#include "lib/util_prg.h"
|
||||||
#include "lib/util_path.h"
|
#include "lib/util_path.h"
|
||||||
|
|
||||||
|
#include "lsstp_cli.h"
|
||||||
|
|
||||||
static const struct util_prg prg = {
|
static const struct util_prg prg = {
|
||||||
.desc = "Display STP system information",
|
.desc = "Display STP system information",
|
||||||
.args = "",
|
.args = "",
|
||||||
@@ -34,12 +36,6 @@ static const struct util_prg prg = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
struct stp_parms {
|
struct stp_parms {
|
||||||
uint64_t ctn_id;
|
uint64_t ctn_id;
|
||||||
unsigned int online;
|
unsigned int online;
|
||||||
|
|||||||
18
lsstp/lsstp_cli.h
Normal file
18
lsstp/lsstp_cli.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LSSTP_CLI_H
|
||||||
|
#define LSSTP_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -2,6 +2,11 @@ include ../common.mak
|
|||||||
|
|
||||||
TESTS := tests/
|
TESTS := tests/
|
||||||
|
|
||||||
|
zsh-completions = _opticsmon
|
||||||
|
bash-completions = opticsmon.bash
|
||||||
|
|
||||||
|
include ../common_autocomp.mak
|
||||||
|
|
||||||
libs =$(rootdir)/libzpci/libzpci.a $(rootdir)/libutil/libutil.a
|
libs =$(rootdir)/libzpci/libzpci.a $(rootdir)/libutil/libutil.a
|
||||||
|
|
||||||
ifneq (${HAVE_OPENSSL},0)
|
ifneq (${HAVE_OPENSSL},0)
|
||||||
|
|||||||
16
opticsmon/autocompletion_generator_host.c
Normal file
16
opticsmon/autocompletion_generator_host.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lib/util_autocomp.h"
|
||||||
|
|
||||||
|
#include "opticsmon_cli.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
generate_autocomp(opt_vec, "opticsmon");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -45,7 +45,7 @@ opticsmon - Monitor optical modules for directly attached PCI based NICs
|
|||||||
Use
|
Use
|
||||||
.B opticsmon
|
.B opticsmon
|
||||||
to monitor the health of the optical modules of directly attached PCI based
|
to monitor the health of the optical modules of directly attached PCI based
|
||||||
NICs. When executed without the \fB--daemon\fR option it will collect optical
|
NICs. When executed without the \fB--monitor\fR option it will collect optical
|
||||||
module data from all available PCI network interface physical functions and
|
module data from all available PCI network interface physical functions and
|
||||||
print a summary in JSON format. Add the \fB--send-report\fR option to report
|
print a summary in JSON format. Add the \fB--send-report\fR option to report
|
||||||
this data to the support element.
|
this data to the support element.
|
||||||
@@ -55,7 +55,7 @@ this data to the support element.
|
|||||||
.
|
.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.SS Operation Options
|
.SS Operation Options
|
||||||
.OD daemon "d"
|
.OD monitor "m"
|
||||||
Run continuously and report on link state changes and periodically
|
Run continuously and report on link state changes and periodically
|
||||||
.PP
|
.PP
|
||||||
.
|
.
|
||||||
|
|||||||
@@ -27,10 +27,11 @@
|
|||||||
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
#include "optics_info.h"
|
|
||||||
#include "optics_sclp.h"
|
|
||||||
#include "ethtool.h"
|
#include "ethtool.h"
|
||||||
#include "link_mon.h"
|
#include "link_mon.h"
|
||||||
|
#include "optics_info.h"
|
||||||
|
#include "optics_sclp.h"
|
||||||
|
#include "opticsmon_cli.h"
|
||||||
|
|
||||||
#define API_LEVEL 1
|
#define API_LEVEL 1
|
||||||
|
|
||||||
@@ -61,44 +62,6 @@ static const struct util_prg prg = {
|
|||||||
UTIL_PRG_COPYRIGHT_END }
|
UTIL_PRG_COPYRIGHT_END }
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OPT_DUMP 128
|
|
||||||
|
|
||||||
static struct util_opt opt_vec[] = {
|
|
||||||
UTIL_OPT_SECTION("OPERATION OPTIONS"),
|
|
||||||
{
|
|
||||||
.option = { "monitor", no_argument, NULL, 'm' },
|
|
||||||
.desc = "Run continuously and report on link state changes "
|
|
||||||
"collecting optics health data when a change is detected",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "send-report", no_argument, NULL, 'r' },
|
|
||||||
.desc = "Report the optics health data to the Support Element",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "quiet", no_argument, NULL, 'q' },
|
|
||||||
.desc = "Be quiet and don't print optics health summary",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.option = { "module-info", no_argument, NULL, OPT_DUMP },
|
|
||||||
.desc = "Include a base64 encoded binary dump of the module's "
|
|
||||||
"SFF-8636/8472/8024 standard data for each netdev. "
|
|
||||||
"This matches \"ethtool --module-info <netdev> raw on\"",
|
|
||||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
|
|
||||||
{
|
|
||||||
.option = { "interval", required_argument, NULL, 'i' },
|
|
||||||
.argument = "seconds",
|
|
||||||
.desc = "Interval in seconds at which to collect monitoring data "
|
|
||||||
"in the absence of link state changes. A value larger than "
|
|
||||||
"24 hours (86400 seconds) is clamped down to 24 hours.",
|
|
||||||
},
|
|
||||||
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
|
||||||
UTIL_OPT_HELP,
|
|
||||||
UTIL_OPT_VERSION,
|
|
||||||
UTIL_OPT_END
|
|
||||||
};
|
|
||||||
|
|
||||||
static void parse_cmdline(int argc, char *argv[], struct options *opts)
|
static void parse_cmdline(int argc, char *argv[], struct options *opts)
|
||||||
{
|
{
|
||||||
uint32_t seconds;
|
uint32_t seconds;
|
||||||
|
|||||||
50
opticsmon/opticsmon_cli.h
Normal file
50
opticsmon/opticsmon_cli.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*
|
||||||
|
* Copyright IBM Corp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef OPTICSMON_CLI_H
|
||||||
|
#define OPTICSMON_CLI_H
|
||||||
|
|
||||||
|
#include "lib/util_opt.h"
|
||||||
|
|
||||||
|
#define OPT_DUMP 128
|
||||||
|
|
||||||
|
static struct util_opt opt_vec[] = {
|
||||||
|
UTIL_OPT_SECTION("OPERATION OPTIONS"),
|
||||||
|
{
|
||||||
|
.option = { "monitor", no_argument, NULL, 'm' },
|
||||||
|
.desc = "Run continuously and report on link state changes "
|
||||||
|
"collecting optics health data when a change is detected",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "send-report", no_argument, NULL, 'r' },
|
||||||
|
.desc = "Report the optics health data to the Support Element",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "quiet", no_argument, NULL, 'q' },
|
||||||
|
.desc = "Be quiet and don't print optics health summary",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.option = { "module-info", no_argument, NULL, OPT_DUMP },
|
||||||
|
.desc = "Include a base64 encoded binary dump of the module's "
|
||||||
|
"SFF-8636/8472/8024 standard data for each netdev. "
|
||||||
|
"This matches 'ethtool --module-info <netdev> raw on'",
|
||||||
|
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("OPTIONS WITH ARGUMENTS"),
|
||||||
|
{
|
||||||
|
.option = { "interval", required_argument, NULL, 'i' },
|
||||||
|
.argument = "seconds",
|
||||||
|
.desc = "Interval in seconds at which to collect monitoring data "
|
||||||
|
"in the absence of link state changes. A value larger than "
|
||||||
|
"24 hours (86400 seconds) is clamped down to 24 hours.",
|
||||||
|
},
|
||||||
|
UTIL_OPT_SECTION("GENERAL OPTIONS"),
|
||||||
|
UTIL_OPT_HELP,
|
||||||
|
UTIL_OPT_VERSION,
|
||||||
|
UTIL_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
678
rust/Cargo.lock
generated
678
rust/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ members = [
|
|||||||
"pv_core",
|
"pv_core",
|
||||||
"pvapconfig",
|
"pvapconfig",
|
||||||
"pvattest",
|
"pvattest",
|
||||||
|
"pvebc",
|
||||||
"pvimg",
|
"pvimg",
|
||||||
"pvinfo",
|
"pvinfo",
|
||||||
"pvsecret",
|
"pvsecret",
|
||||||
@@ -16,12 +17,11 @@ resolver = "2"
|
|||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
rust-version = "1.75.0"
|
rust-version = "1.85.0"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
missing_docs = { level = "deny", priority = 1 }
|
missing_docs = { level = "deny", priority = 1 }
|
||||||
missing_debug_implementations = "warn"
|
missing_debug_implementations = "warn"
|
||||||
non_ascii_idents = "warn"
|
|
||||||
nonstandard-style = "warn"
|
nonstandard-style = "warn"
|
||||||
trivial_numeric_casts = "warn"
|
trivial_numeric_casts = "warn"
|
||||||
unstable_features = "warn"
|
unstable_features = "warn"
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ CARGO_TARGETS :=
|
|||||||
PV_TARGETS :=
|
PV_TARGETS :=
|
||||||
CARGO_TEST_TARGETS :=
|
CARGO_TEST_TARGETS :=
|
||||||
|
|
||||||
|
SEL_EBC_MODDIR := 95sel-ebc
|
||||||
|
|
||||||
ifneq (${HAVE_CARGO},0)
|
ifneq (${HAVE_CARGO},0)
|
||||||
CARGO_TARGETS :=
|
CARGO_TARGETS := pvebc
|
||||||
|
|
||||||
BUILD_TARGETS = $(CARGO_TARGETS)
|
BUILD_TARGETS = $(CARGO_TARGETS)
|
||||||
INSTALL_TARGETS := install-rust-tools install-man install-shell-completions
|
INSTALL_TARGETS := install-rust-tools install-man install-shell-completions
|
||||||
@@ -29,6 +31,7 @@ ifneq (${HAVE_LIBCURL},0)
|
|||||||
|
|
||||||
ifeq ($(HOST_ARCH),s390x)
|
ifeq ($(HOST_ARCH),s390x)
|
||||||
PV_TARGETS += pvapconfig pvinfo
|
PV_TARGETS += pvapconfig pvinfo
|
||||||
|
INSTALL_TARGETS += install-ebc-dracut-module
|
||||||
else
|
else
|
||||||
BUILD_TARGETS += skip-pvapconfig skip-pvinfo
|
BUILD_TARGETS += skip-pvapconfig skip-pvinfo
|
||||||
endif #HOSTARCH
|
endif #HOSTARCH
|
||||||
@@ -104,6 +107,25 @@ endif # CARGO
|
|||||||
|
|
||||||
rust-test: $(CARGO_TEST_TARGETS)
|
rust-test: $(CARGO_TEST_TARGETS)
|
||||||
|
|
||||||
|
install-ebc-dracut-module:
|
||||||
|
ifneq ($(HAVE_DRACUT),0)
|
||||||
|
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/
|
||||||
|
$(INSTALL) -m 755 -d $(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
|
||||||
|
$(INSTALL) -m 755 pvebc/$(SEL_EBC_MODDIR)/module-setup.sh \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/override-crypttab.sh \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/pvebc-wrapper.sh \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/boot-mount.sh \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-modules.conf \
|
||||||
|
$(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
|
||||||
|
$(INSTALL) -m 644 pvebc/$(SEL_EBC_MODDIR)/sel-ebc-boot-mount.service \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-override-crypttab.service \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-paes-enforce.service \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/sel-ebc-pvebc.service \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/sel-ebc.crypttab \
|
||||||
|
pvebc/$(SEL_EBC_MODDIR)/sel-ebc.target \
|
||||||
|
$(DESTDIR)$(DRACUTMODDIR)/$(SEL_EBC_MODDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
install-rust-tools: $(BUILD_TARGETS)
|
install-rust-tools: $(BUILD_TARGETS)
|
||||||
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
|
||||||
$(foreach target,$(CARGO_TARGETS),\
|
$(foreach target,$(CARGO_TARGETS),\
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ license.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.95"
|
anyhow = "1.0.102"
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.6", features = ["derive"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
utils = { path = "../utils" }
|
utils = { path = "../utils" }
|
||||||
zerocopy = { version="0.8", features = ["derive"] }
|
zerocopy = { version="0.8", features = ["derive"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.2"
|
cc = "1.2"
|
||||||
clap = { version ="4.5", features = ["derive"]}
|
clap = { version ="4.6", features = ["derive"]}
|
||||||
lazy_static = "1.5"
|
lazy_static = "1.5"
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ mod msa;
|
|||||||
mod query;
|
mod query;
|
||||||
mod stfle;
|
mod stfle;
|
||||||
|
|
||||||
use anyhow::bail;
|
|
||||||
use anyhow::Result as anyhowRes;
|
|
||||||
use clap::Parser;
|
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::result::Result::Ok;
|
use std::result::Result::Ok;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result as anyhowRes};
|
||||||
|
use clap::Parser;
|
||||||
use utils::print_version;
|
use utils::print_version;
|
||||||
|
|
||||||
use crate::cli::{Cli, Format};
|
use crate::cli::{Cli, Format};
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use crate::cli::Cli;
|
|
||||||
use core::fmt::{Display, Formatter, Result};
|
use core::fmt::{Display, Formatter, Result};
|
||||||
|
|
||||||
use serde::{Serialize, Serializer};
|
use serde::{Serialize, Serializer};
|
||||||
use utils::HexSlice;
|
use utils::HexSlice;
|
||||||
|
|
||||||
|
use crate::cli::Cli;
|
||||||
|
|
||||||
/// Number of total function codes (0 to 127)
|
/// Number of total function codes (0 to 127)
|
||||||
pub const NUMBER_FUNC_CODES: usize = 128;
|
pub const NUMBER_FUNC_CODES: usize = 128;
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,13 @@
|
|||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Error;
|
use std::io::{Error, Read};
|
||||||
use std::io::Read;
|
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use zerocopy::FromBytes;
|
use zerocopy::FromBytes;
|
||||||
|
|
||||||
use crate::msa::InstructionKind;
|
use crate::msa::{InstructionKind, QueryAuthInfo};
|
||||||
use crate::msa::QueryAuthInfo;
|
|
||||||
|
|
||||||
/// Path to sysfs in which the query and qai informations are fetched from
|
/// Path to sysfs in which the query and qai informations are fetched from
|
||||||
const SYSFS_PATH: &str = "/sys/devices/system/cpu/cpacf/";
|
const SYSFS_PATH: &str = "/sys/devices/system/cpu/cpacf/";
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
curl = "0.4.47"
|
curl = "0.4.49"
|
||||||
enum_dispatch = "0.3.13"
|
enum_dispatch = "0.3.13"
|
||||||
foreign-types = "0.3.2"
|
foreign-types = "0.3.2"
|
||||||
log = { version = "0.4.25", features = ["std", "release_max_level_debug"] }
|
log = { version = "0.4.29", features = ["std", "release_max_level_debug"] }
|
||||||
openssl = "0.10.70"
|
openssl = "0.10.80"
|
||||||
openssl-sys = "0.9.105"
|
openssl-sys = "0.9.116"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
thiserror = "2.0.11"
|
thiserror = "2.0.18"
|
||||||
zerocopy = { version="0.8", features = ["derive"] }
|
zerocopy = { version="0.8", features = ["derive"] }
|
||||||
|
|
||||||
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" }
|
pv_core = { path = "../pv_core", package = "s390_pv_core", version = "0.12.0" }
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use pv::request::EcPubKeyCoord;
|
|
||||||
use pv_core::misc::encode_hex;
|
|
||||||
use s390_pv as pv;
|
|
||||||
|
|
||||||
use std::env::args;
|
use std::env::args;
|
||||||
|
|
||||||
use pv::misc::{read_certs, read_file};
|
use pv::misc::{read_certs, read_file};
|
||||||
|
use pv::request::EcPubKeyCoord;
|
||||||
use pv::{Error, Result};
|
use pv::{Error, Result};
|
||||||
|
use pv_core::misc::encode_hex;
|
||||||
|
use s390_pv as pv;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let hkd = args().nth(1).expect("Expect one Host-key document");
|
let hkd = args().nth(1).expect("Expect one Host-key document");
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023, 2024
|
// Copyright IBM Corp. 2023, 2024
|
||||||
|
|
||||||
use std::{
|
use std::io::SeekFrom::Current;
|
||||||
io::{Read, Seek, SeekFrom::Current},
|
use std::io::{Read, Seek};
|
||||||
mem::size_of,
|
use std::mem::size_of;
|
||||||
};
|
|
||||||
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32, U64};
|
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
|
// (SE) boot request control block aka SE header
|
||||||
use crate::{assert_size, request::MagicValue, static_assert, Error, Result, PAGESIZE};
|
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.
|
/// Struct containing all SE-header tags.
|
||||||
///
|
///
|
||||||
/// Contains:
|
/// Contains:
|
||||||
@@ -206,7 +212,7 @@ impl BootHdrTags {
|
|||||||
///
|
///
|
||||||
/// This function will return an error if the header could not be found in
|
/// This function will return an error if the header could not be found in
|
||||||
/// `img` or is invalid.
|
/// `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
|
where
|
||||||
R: Read + Seek,
|
R: Read + Seek,
|
||||||
{
|
{
|
||||||
@@ -233,10 +239,13 @@ impl BootHdrTags {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Some sanity checks
|
// Some sanity checks
|
||||||
if hdr_head.version.get() != 0x100 {
|
let hdr_version = match hdr_head.version.get() {
|
||||||
debug!("Unsupported hdr-version: {:0>4x}", hdr_head.version.get());
|
0x100 => SeHdrVersion::One,
|
||||||
return Err(Error::InvBootHdr);
|
_ => {
|
||||||
}
|
debug!("Unsupported hdr-version: {:0>4x}", hdr_head.version.get());
|
||||||
|
return Err(Error::InvBootHdr);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// go to the Boot header tag
|
// go to the Boot header tag
|
||||||
img.seek(Current(
|
img.seek(Current(
|
||||||
@@ -249,12 +258,15 @@ impl BootHdrTags {
|
|||||||
let mut tag = [0u8; BootHdrHead::TAG_SIZE];
|
let mut tag = [0u8; BootHdrHead::TAG_SIZE];
|
||||||
img.read_exact(tag.as_mut_slice())?;
|
img.read_exact(tag.as_mut_slice())?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok((
|
||||||
pld: hdr_head.pld,
|
Self {
|
||||||
ald: hdr_head.ald,
|
pld: hdr_head.pld,
|
||||||
tld: hdr_head.tld,
|
ald: hdr_head.ald,
|
||||||
tag,
|
tld: hdr_head.tld,
|
||||||
})
|
tag,
|
||||||
|
},
|
||||||
|
hdr_version,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +332,7 @@ mod tests {
|
|||||||
fn from_se_image_hdr() {
|
fn from_se_image_hdr() {
|
||||||
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
|
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
|
||||||
let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap();
|
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]
|
#[test]
|
||||||
@@ -356,18 +368,19 @@ mod tests {
|
|||||||
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
|
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
|
||||||
img[0x12000..0x12280].copy_from_slice(bin_hdr);
|
img[0x12000..0x12280].copy_from_slice(bin_hdr);
|
||||||
let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(img)).unwrap();
|
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]
|
#[test]
|
||||||
fn tags_convert_u8() {
|
fn tags_convert_u8() {
|
||||||
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
|
let bin_hdr = get_test_asset!("exp/secure_guest.hdr");
|
||||||
let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap();
|
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 mut ser = ser.to_vec();
|
||||||
|
|
||||||
let der: BootHdrTags = ser.clone().try_into().unwrap();
|
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();
|
ser.pop();
|
||||||
let der: Result<BootHdrTags> = ser.clone().try_into();
|
let der: Result<BootHdrTags> = ser.clone().try_into();
|
||||||
@@ -380,7 +393,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn se_img_metadata() {
|
fn se_img_metadata_v1() {
|
||||||
let metadata = SeImgMetaData::new_v1(0x14000, 0x16000);
|
let metadata = SeImgMetaData::new_v1(0x14000, 0x16000);
|
||||||
let data = [
|
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,
|
83, 101, 73, 109, 103, 76, 110, 120, 0, 0, 0, 0, 0, 1, 64, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||||
|
|||||||
@@ -2,25 +2,28 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023, 2024
|
// 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 enum_dispatch::enum_dispatch;
|
||||||
use openssl::{
|
use openssl::derive::Deriver;
|
||||||
derive::Deriver,
|
use openssl::ec::{EcGroup, EcKey};
|
||||||
ec::{EcGroup, EcKey},
|
use openssl::hash::{DigestBytes, MessageDigest};
|
||||||
hash::{DigestBytes, MessageDigest},
|
use openssl::md::MdRef;
|
||||||
md::MdRef,
|
use openssl::nid::Nid;
|
||||||
nid::Nid,
|
use openssl::pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public};
|
||||||
pkey::{HasPublic, Id, PKey, PKeyRef, Private, Public},
|
use openssl::pkey_ctx::{HkdfMode, PkeyCtx};
|
||||||
pkey_ctx::{HkdfMode, PkeyCtx},
|
use openssl::rand::rand_bytes;
|
||||||
rand::rand_bytes,
|
use openssl::rsa::Padding;
|
||||||
rsa::Padding,
|
use openssl::sign::{Signer, Verifier};
|
||||||
sign::{Signer, Verifier},
|
use openssl::symm::{
|
||||||
symm::{decrypt_aead as openssl_decrypt_aead, encrypt_aead as openssl_encrypt_aead, Cipher},
|
decrypt_aead as openssl_decrypt_aead, encrypt_aead as openssl_encrypt_aead, Cipher,
|
||||||
};
|
};
|
||||||
use pv_core::request::Confidential;
|
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
|
/// 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]>;
|
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.
|
/// Performs an hkdf according to RFC 5869.
|
||||||
/// See [`OpenSSL HKDF`]()
|
/// See [`OpenSSL HKDF`]()
|
||||||
///
|
///
|
||||||
@@ -540,7 +549,8 @@ pub(crate) fn verify_signature<T: HasPublic>(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{get_test_asset, test_utils::*, PvCoreError};
|
use crate::test_utils::*;
|
||||||
|
use crate::{get_test_asset, PvCoreError};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sign_ec() {
|
fn sign_ec() {
|
||||||
|
|||||||
@@ -90,11 +90,11 @@ pub enum Error {
|
|||||||
#[error("Input does not contain an attestation request")]
|
#[error("Input does not contain an attestation request")]
|
||||||
NoArcb,
|
NoArcb,
|
||||||
|
|
||||||
#[error("The attestation request has an unknown version (.0)")]
|
#[error("The attestation request has an unknown version {0}")]
|
||||||
BinArcbInvVersion(u32),
|
BinArcbInvVersion(u32),
|
||||||
|
|
||||||
#[error(
|
#[error(
|
||||||
"The attestation request encrypted sice is to0 small (.0). Request probably tampered with."
|
"The attestation request encrypted sice is to0 small {0}. Request probably tampered with."
|
||||||
)]
|
)]
|
||||||
BinArcbSeaSmall(u32),
|
BinArcbSeaSmall(u32),
|
||||||
|
|
||||||
|
|||||||
@@ -63,13 +63,11 @@ pub mod uv {
|
|||||||
pub mod attest {
|
pub mod attest {
|
||||||
pub use pv_core::attest::*;
|
pub use pv_core::attest::*;
|
||||||
|
|
||||||
pub use crate::uvattest::{
|
pub use crate::uvattest::additional::AdditionalData;
|
||||||
additional::AdditionalData,
|
pub use crate::uvattest::arcb::{
|
||||||
arcb::{
|
AttestationAuthenticated, AttestationFlags, AttestationRequest, AttestationVersion,
|
||||||
AttestationAuthenticated, AttestationFlags, AttestationRequest, AttestationVersion,
|
|
||||||
},
|
|
||||||
attest::{AttestationItems, AttestationMeasurement},
|
|
||||||
};
|
};
|
||||||
|
pub use crate::uvattest::attest::{AttestationItems, AttestationMeasurement};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Definitions and functions to write objects in PEM format
|
/// Definitions and functions to write objects in PEM format
|
||||||
@@ -91,20 +89,22 @@ pub use crate::error::HkdVerifyErrorType;
|
|||||||
|
|
||||||
/// Functionalities to build UV requests
|
/// Functionalities to build UV requests
|
||||||
pub mod request {
|
pub mod request {
|
||||||
pub use crate::{
|
pub use crate::brcb::{seek_se_hdr_start, BootHdrTags, SeImgMetaData};
|
||||||
brcb::{seek_se_hdr_start, BootHdrTags, SeImgMetaData},
|
pub use crate::crypto::{
|
||||||
crypto::{
|
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array,
|
||||||
decrypt_aead, derive_aes256_gcm_key, encrypt_aead, gen_ec_key, random_array,
|
AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey, SymKeyType,
|
||||||
AeadDecryptionResult, AeadEncryptionResult, Aes256GcmKey, Aes256XtsKey, SymKey,
|
SHA_512_HASH_LEN,
|
||||||
SymKeyType, SHA_512_HASH_LEN,
|
|
||||||
},
|
|
||||||
req::{EcPubKeyCoord, Encrypt, Keyslot, ReqEncrCtx, Request},
|
|
||||||
verify::{CertVerifier, HkdVerifier, NoVerifyHkd},
|
|
||||||
};
|
};
|
||||||
|
pub use crate::req::{EcPubKeyCoord, Encrypt, HostKey, Keyslot, ReqEncrCtx, Request};
|
||||||
|
pub use crate::verify::{CertVerifier, HkdVerifier, NoVerifyHkd};
|
||||||
|
|
||||||
/// Reexports some useful OpenSSL symbols
|
/// Reexports some useful OpenSSL symbols
|
||||||
pub mod openssl {
|
pub mod openssl {
|
||||||
pub use openssl::{error::ErrorStack, hash::DigestBytes, nid::Nid, pkey, 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
|
// rust-OpenSSL does not define these NIDs
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
|
pub const NID_ED25519: Nid = Nid::from_raw(openssl_sys::NID_ED25519);
|
||||||
@@ -113,17 +113,16 @@ pub mod request {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use pv_core::request::*;
|
pub use pv_core::request::*;
|
||||||
|
pub use pv_core::PolicyReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Functionalities for creating add-secret requests
|
/// Functionalities for creating add-secret requests
|
||||||
pub mod secret {
|
pub mod secret {
|
||||||
pub use pv_core::secret::*;
|
pub use pv_core::secret::*;
|
||||||
|
|
||||||
pub use crate::uvsecret::{
|
pub use crate::uvsecret::asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion};
|
||||||
asrcb::{AddSecretFlags, AddSecretRequest, AddSecretVersion},
|
pub use crate::uvsecret::ext_secret::ExtSecret;
|
||||||
ext_secret::ExtSecret,
|
pub use crate::uvsecret::guest_secret::GuestSecret;
|
||||||
guest_secret::GuestSecret,
|
pub use crate::uvsecret::retr_secret::{IbmProtectedKey, RetrievedSecret};
|
||||||
retr_secret::{IbmProtectedKey, RetrievedSecret},
|
pub use crate::uvsecret::user_data::verify_asrcb_and_get_user_data;
|
||||||
user_data::verify_asrcb_and_get_user_data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
|
use std::ffi::c_int;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
|
use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
|
||||||
use openssl::x509::{X509CrlRef, X509Ref};
|
use openssl::x509::{X509CrlRef, X509Ref};
|
||||||
use std::ffi::c_int;
|
|
||||||
|
|
||||||
mod ffi {
|
mod ffi {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -99,9 +99,8 @@ impl AkidExtension for X509CrlRef {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::test_utils::load_gen_cert;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::test_utils::load_gen_cert;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn akid() {
|
fn akid() {
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use core::slice;
|
use core::slice;
|
||||||
|
use std::ffi::c_int;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
use openssl::error::ErrorStack;
|
use openssl::error::ErrorStack;
|
||||||
use openssl_sys::BIO_new_mem_buf;
|
use openssl_sys::BIO_new_mem_buf;
|
||||||
use std::ffi::c_int;
|
|
||||||
use std::{marker::PhantomData, ptr};
|
|
||||||
|
|
||||||
pub struct BioMem(*mut openssl_sys::BIO);
|
pub struct BioMem(*mut openssl_sys::BIO);
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023
|
// Copyright IBM Corp. 2023
|
||||||
pub use crate::openssl_extensions::stackable_crl::*;
|
|
||||||
use foreign_types::{ForeignType, ForeignTypeRef};
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
use openssl::{
|
use openssl::error::ErrorStack;
|
||||||
error::ErrorStack,
|
use openssl::stack::{Stack, StackRef};
|
||||||
stack::{Stack, StackRef},
|
use openssl::x509::store::{X509StoreBuilderRef, X509StoreRef};
|
||||||
x509::{
|
use openssl::x509::{X509CrlRef, X509NameRef, X509Ref, X509StoreContextRef, X509};
|
||||||
store::{X509StoreBuilderRef, X509StoreRef},
|
|
||||||
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 {
|
pub fn opt_to_ptr<T: ForeignTypeRef>(o: Option<&T>) -> *mut T::CType {
|
||||||
match o {
|
match o {
|
||||||
|
|||||||
595
rust/pv/src/openssl_extensions/ml_kem.rs
Normal file
595
rust/pv/src/openssl_extensions/ml_kem.rs
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
//
|
||||||
|
// Copyright IBM Corp.
|
||||||
|
|
||||||
|
#![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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,11 @@
|
|||||||
mod akid;
|
mod akid;
|
||||||
mod bio;
|
mod bio;
|
||||||
mod crl;
|
mod crl;
|
||||||
|
mod ml_kem;
|
||||||
mod stackable_crl;
|
mod stackable_crl;
|
||||||
|
|
||||||
pub use akid::*;
|
pub use akid::*;
|
||||||
pub use bio::*;
|
pub use bio::*;
|
||||||
pub use crl::*;
|
pub use crl::*;
|
||||||
|
#[expect(unused_imports)]
|
||||||
|
pub use ml_kem::*;
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023
|
// 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 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)]
|
#[derive(Debug)]
|
||||||
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
|
pub struct StackableX509Crl(*mut openssl_sys::X509_CRL);
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,19 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use crate::Result;
|
use std::ffi::{c_char, CString};
|
||||||
use crate::{openssl_extensions::BioMem, Error};
|
use std::fmt::Display;
|
||||||
|
|
||||||
use openssl::error::ErrorStack;
|
use openssl::error::ErrorStack;
|
||||||
use pv_core::request::Confidential;
|
use pv_core::request::Confidential;
|
||||||
use std::{
|
|
||||||
ffi::{c_char, CString},
|
use crate::openssl_extensions::BioMem;
|
||||||
fmt::Display,
|
use crate::{Error, Result};
|
||||||
};
|
|
||||||
|
|
||||||
mod ffi {
|
mod ffi {
|
||||||
use openssl_sys::BIO;
|
|
||||||
use std::ffi::{c_char, c_int, c_long, c_uchar};
|
use std::ffi::{c_char, c_int, c_long, c_uchar};
|
||||||
|
|
||||||
|
use openssl_sys::BIO;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn PEM_write_bio(
|
pub fn PEM_write_bio(
|
||||||
bio: *mut BIO,
|
bio: *mut BIO,
|
||||||
@@ -141,8 +142,8 @@ impl Pem {
|
|||||||
|
|
||||||
let inner_pem = InnerPem::new(name, header, data.as_ref())?;
|
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
|
// Create the PEM format eagerly so that to_string/display cannot fail because of ASCII or
|
||||||
// Both error should be very unlikely
|
// 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
|
// OpenSSL should be able to create PEM if there is enough memory and produce a non-null
|
||||||
// terminated ASCII-string
|
// terminated ASCII-string
|
||||||
// Unwrap succeeds it's all ASCII
|
// Unwrap succeeds it's all ASCII
|
||||||
|
|||||||
@@ -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
257
rust/pv/src/req/context.rs
Normal 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
154
rust/pv/src/req/ec_coord.rs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
54
rust/pv/src/req/encrypt.rs
Normal file
54
rust/pv/src/req/encrypt.rs
Normal 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
91
rust/pv/src/req/header.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
rust/pv/src/req/hostkey.rs
Normal file
30
rust/pv/src/req/hostkey.rs
Normal 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
150
rust/pv/src/req/keyslot.rs
Normal 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
25
rust/pv/src/req/mod.rs
Normal 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};
|
||||||
265
rust/pv/src/req/request.rs
Normal file
265
rust/pv/src/req/request.rs
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
// 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 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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,19 +4,15 @@
|
|||||||
|
|
||||||
// DO NOT USE ANY OF THESE ITEMS IN PRODUCTION CODE
|
// DO NOT USE ANY OF THESE ITEMS IN PRODUCTION CODE
|
||||||
// USED FOR INTERNAL UNIT AND FVT TESTING ONLY!!!
|
// USED FOR INTERNAL UNIT AND FVT TESTING ONLY!!!
|
||||||
use std::{
|
use std::fs;
|
||||||
fs,
|
use std::path::{Path, PathBuf};
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use openssl::{
|
use openssl::bn::BigNum;
|
||||||
bn::BigNum,
|
use openssl::ec::{EcGroup, EcKey};
|
||||||
ec::{EcGroup, EcKey},
|
use openssl::error::ErrorStack;
|
||||||
error::ErrorStack,
|
use openssl::nid::Nid;
|
||||||
nid::Nid,
|
use openssl::pkey::{PKey, Private, Public};
|
||||||
pkey::{PKey, Private, Public},
|
use openssl::x509::{X509Crl, X509};
|
||||||
x509::{X509Crl, X509},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TEST ONLY! Loads the specified asset into the binary at compile time.
|
/// TEST ONLY! Loads the specified asset into the binary at compile time.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023
|
// Copyright IBM Corp. 2023
|
||||||
|
use openssl::error::ErrorStack;
|
||||||
|
use openssl::x509::{X509Crl, X509};
|
||||||
|
|
||||||
use crate::{Error, Result};
|
use crate::{Error, Result};
|
||||||
use openssl::{
|
|
||||||
error::ErrorStack,
|
|
||||||
x509::{X509Crl, X509},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Read all CRLs from the buffer and parse them into a vector.
|
/// Read all CRLs from the buffer and parse them into a vector.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -2,18 +2,17 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::req::Keyslot;
|
use serde::Serialize;
|
||||||
use crate::static_assert;
|
|
||||||
use crate::{Error, Result};
|
|
||||||
|
|
||||||
use super::arcb::AttestationFlags;
|
use super::arcb::AttestationFlags;
|
||||||
|
use crate::req::KeyslotV1;
|
||||||
|
use crate::{static_assert, Error, Result};
|
||||||
|
|
||||||
/// Hash for additional-data stuff used for parsing [`AdditionalData`]
|
/// Hash for additional-data stuff used for parsing [`AdditionalData`]
|
||||||
pub(super) const PHKH_SIZE: u32 = 0x20;
|
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 SECRET_STORE_HASH_SIZE: u32 = 0x40;
|
||||||
pub(super) const FW_STATE_SIZE: u32 = 0x140;
|
pub(super) const FW_STATE_SIZE: u32 = 0x140;
|
||||||
|
|
||||||
|
|||||||
@@ -2,27 +2,19 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// Copyright IBM Corp. 2024
|
||||||
|
|
||||||
use openssl::pkey::{PKey, Public};
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
|
use zerocopy::{BigEndian, FromBytes, Immutable, IntoBytes, KnownLayout, U32};
|
||||||
|
|
||||||
use crate::{
|
use super::additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE};
|
||||||
assert_size,
|
use super::AttNonce;
|
||||||
attest::{AttestationMagic, AttestationMeasAlg},
|
use crate::attest::{AttestationMagic, AttestationMeasAlg};
|
||||||
crypto::random_array,
|
use crate::crypto::random_array;
|
||||||
misc::Flags,
|
use crate::misc::Flags;
|
||||||
req::{Aad, BinReqValues, Keyslot, ReqEncrCtx},
|
use crate::req::{Aad, BinReqValues, HostKey, Keyslot, ReqEncrCtx};
|
||||||
request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize},
|
use crate::request::{Confidential, MagicValue, Request, RequestVersion, SymKey, Zeroize};
|
||||||
static_assert,
|
use crate::uv::UvFlags;
|
||||||
uv::UvFlags,
|
use crate::{assert_size, static_assert, Error, Result};
|
||||||
Error, Result,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
additional::{FW_STATE_SIZE, PHKH_SIZE, SECRET_STORE_HASH_SIZE},
|
|
||||||
AttNonce,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use crate::{
|
use crate::{
|
||||||
request::SymKeyType,
|
request::SymKeyType,
|
||||||
@@ -59,7 +51,7 @@ use crate::{
|
|||||||
///
|
///
|
||||||
/// ```rust,no_run
|
/// ```rust,no_run
|
||||||
/// # use s390_pv::attest::{AttestationFlags, AttestationMeasAlg, AttestationRequest, AttestationVersion};
|
/// # 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<()> {
|
/// # fn main() -> s390_pv::Result<()> {
|
||||||
/// let att_version = AttestationVersion::One;
|
/// let att_version = AttestationVersion::One;
|
||||||
/// let meas_alg = AttestationMeasAlg::HmacSha512;
|
/// 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")?)?;
|
/// let hkd = s390_pv::misc::read_certs(&std::fs::read("host-key-document.crt")?)?;
|
||||||
/// // IBM issued HKD certificates typically have one X509
|
/// // IBM issued HKD certificates typically have one X509
|
||||||
/// let hkd = hkd.first().unwrap().public_key()?;
|
/// let hkd = hkd.first().unwrap().public_key()?;
|
||||||
/// arcb.add_hostkey(hkd);
|
/// arcb.add_hostkey(HostKey::V1(hkd));
|
||||||
/// // you can add multiple hostkeys
|
/// // you can add multiple hostkeys
|
||||||
/// // arcb.add_hostkey(another_hkd);
|
/// // arcb.add_hostkey(another_hkd);
|
||||||
/// // encrypt it
|
/// // encrypt it
|
||||||
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256)?;
|
/// let ctx = ReqEncrCtx::random(SymKeyType::Aes256Gcm)?;
|
||||||
/// let arcb = arcb.encrypt(&ctx)?;
|
/// let arcb = arcb.encrypt(&ctx)?;
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
@@ -243,7 +235,7 @@ impl Request for AttestationRequest {
|
|||||||
ctx.encrypt_aead(&aad, conf).map(|res| res.into_buf())
|
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))
|
self.keyslots.push(Keyslot::new(hostkey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,8 +411,9 @@ impl Zeroize for ReqConfData {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::get_test_asset;
|
||||||
use crate::{get_test_asset, request::SymKey, test_utils::get_test_keys};
|
use crate::request::SymKey;
|
||||||
|
use crate::test_utils::get_test_keys;
|
||||||
|
|
||||||
const ARPK: [u8; 32] = [0x17; 32];
|
const ARPK: [u8; 32] = [0x17; 32];
|
||||||
const NONCE: [u8; 16] = [0xab; 16];
|
const NONCE: [u8; 16] = [0xab; 16];
|
||||||
@@ -450,7 +443,7 @@ mod test {
|
|||||||
arcb.conf.value_mut().nonce = NONCE;
|
arcb.conf.value_mut().nonce = NONCE;
|
||||||
arcb.conf.value_mut().meas_key = MEAS;
|
arcb.conf.value_mut().meas_key = MEAS;
|
||||||
|
|
||||||
arcb.add_hostkey(host_key);
|
arcb.add_hostkey(HostKey::V1(host_key));
|
||||||
arcb.encrypt(&ctx).unwrap()
|
arcb.encrypt(&ctx).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,21 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2024
|
// 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 std::mem::size_of;
|
||||||
|
|
||||||
|
use openssl::hash::MessageDigest;
|
||||||
|
use openssl::pkey::{PKeyRef, Private};
|
||||||
use zerocopy::{BigEndian, IntoBytes, U16, U32};
|
use zerocopy::{BigEndian, IntoBytes, U16, U32};
|
||||||
|
|
||||||
|
use super::AttNonce;
|
||||||
|
use crate::attest::AttestationMeasAlg;
|
||||||
#[cfg(doc)]
|
#[cfg(doc)]
|
||||||
use crate::attest::AttestationRequest;
|
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.
|
/// Holds the data to be measured.
|
||||||
///
|
///
|
||||||
@@ -151,9 +152,10 @@ impl From<Vec<u8>> for AttestationMeasurement {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
|
||||||
use openssl::pkey::PKey;
|
use openssl::pkey::PKey;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
const M_KEY: [u8; 64] = [0x41; 64];
|
const M_KEY: [u8; 64] = [0x41; 64];
|
||||||
const BOOT_HDR_TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]);
|
const BOOT_HDR_TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]);
|
||||||
const CUID: [u8; 16] = [5; 16];
|
const CUID: [u8; 16] = [5; 16];
|
||||||
|
|||||||
@@ -2,24 +2,23 @@
|
|||||||
//
|
//
|
||||||
// Copyright IBM Corp. 2023
|
// Copyright IBM Corp. 2023
|
||||||
|
|
||||||
use super::{guest_secret::ListableSecretHdr, user_data::UserData};
|
use openssl::md::Md;
|
||||||
use crate::{
|
use openssl::pkey::{PKey, Private};
|
||||||
assert_size,
|
use pv_core::request::RequestVersion;
|
||||||
crypto::{hkdf_rfc_5869, AeadEncryptionResult},
|
use pv_core::secret::AddSecretMagic;
|
||||||
misc::Flags,
|
use pv_core::uv::SecretId;
|
||||||
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 zerocopy::{Immutable, IntoBytes};
|
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
|
/// Authenticated data w/o user data
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, IntoBytes, Immutable)]
|
#[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))
|
self.keyslots.push(Keyslot::new(hostkey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user